mppx 0.6.12 → 0.6.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 +14 -0
- package/dist/cli/plugins/tempo.d.ts.map +1 -1
- package/dist/cli/plugins/tempo.js +24 -1
- package/dist/cli/plugins/tempo.js.map +1 -1
- package/dist/middlewares/express.d.ts.map +1 -1
- package/dist/middlewares/express.js +22 -0
- package/dist/middlewares/express.js.map +1 -1
- package/dist/tempo/client/SessionManager.d.ts.map +1 -1
- package/dist/tempo/client/SessionManager.js +26 -1
- package/dist/tempo/client/SessionManager.js.map +1 -1
- package/dist/tempo/internal/fee-payer.d.ts +11 -1
- package/dist/tempo/internal/fee-payer.d.ts.map +1 -1
- package/dist/tempo/internal/fee-payer.js +71 -6
- package/dist/tempo/internal/fee-payer.js.map +1 -1
- package/dist/tempo/server/Charge.d.ts.map +1 -1
- package/dist/tempo/server/Charge.js +53 -10
- package/dist/tempo/server/Charge.js.map +1 -1
- package/dist/tempo/server/Session.d.ts.map +1 -1
- package/dist/tempo/server/Session.js +80 -29
- 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/server/internal/request-body.d.ts +1 -1
- package/dist/tempo/server/internal/request-body.d.ts.map +1 -1
- package/dist/tempo/server/internal/request-body.js +3 -0
- package/dist/tempo/server/internal/request-body.js.map +1 -1
- package/dist/tempo/server/internal/transport.d.ts +7 -0
- package/dist/tempo/server/internal/transport.d.ts.map +1 -1
- package/dist/tempo/server/internal/transport.js +16 -0
- package/dist/tempo/server/internal/transport.js.map +1 -1
- package/dist/tempo/session/Chain.d.ts +1 -0
- package/dist/tempo/session/Chain.d.ts.map +1 -1
- package/dist/tempo/session/Chain.js +28 -11
- package/dist/tempo/session/Chain.js.map +1 -1
- package/dist/tempo/session/ChannelStore.d.ts.map +1 -1
- package/dist/tempo/session/ChannelStore.js +6 -0
- package/dist/tempo/session/ChannelStore.js.map +1 -1
- package/dist/tempo/session/Sse.d.ts +1 -0
- package/dist/tempo/session/Sse.d.ts.map +1 -1
- package/dist/tempo/session/Sse.js +34 -12
- package/dist/tempo/session/Sse.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/plugins/tempo.ts +28 -1
- package/src/middlewares/express.test.ts +27 -0
- package/src/middlewares/express.ts +24 -0
- package/src/tempo/client/SessionManager.ts +26 -1
- package/src/tempo/internal/fee-payer.test.ts +139 -0
- package/src/tempo/internal/fee-payer.ts +85 -6
- package/src/tempo/server/Charge.test.ts +119 -0
- package/src/tempo/server/Charge.ts +70 -10
- package/src/tempo/server/Session.test.ts +327 -0
- package/src/tempo/server/Session.ts +91 -39
- package/src/tempo/server/internal/html.gen.ts +1 -1
- package/src/tempo/server/internal/request-body.test.ts +26 -0
- package/src/tempo/server/internal/request-body.ts +4 -1
- package/src/tempo/server/internal/transport.test.ts +28 -2
- package/src/tempo/server/internal/transport.ts +23 -0
- package/src/tempo/session/Chain.test.ts +140 -1
- package/src/tempo/session/Chain.ts +34 -10
- package/src/tempo/session/ChannelStore.test.ts +21 -0
- package/src/tempo/session/ChannelStore.ts +6 -0
- package/src/tempo/session/Sse.test.ts +52 -0
- package/src/tempo/session/Sse.ts +22 -2
|
@@ -168,9 +168,10 @@ export function session<const parameters extends session.Parameters>(
|
|
|
168
168
|
|
|
169
169
|
// Extract feePayer.
|
|
170
170
|
const resolvedFeePayer = (() => {
|
|
171
|
+
if (request.feePayer === false) return credential ? false : undefined
|
|
171
172
|
const account = typeof request.feePayer === 'object' ? request.feePayer : feePayer
|
|
172
|
-
const requested =
|
|
173
|
-
if (credential) return account
|
|
173
|
+
const requested = account ?? feePayer ?? feePayerUrl
|
|
174
|
+
if (credential) return account ?? (feePayerUrl ? true : undefined)
|
|
174
175
|
if (requested) return true
|
|
175
176
|
return undefined
|
|
176
177
|
})()
|
|
@@ -196,7 +197,17 @@ export function session<const parameters extends session.Parameters>(
|
|
|
196
197
|
const methodDetails = resolvedRequest.methodDetails as SessionMethodDetails
|
|
197
198
|
const client = await getClient({ chainId: methodDetails.chainId })
|
|
198
199
|
|
|
199
|
-
const
|
|
200
|
+
const requestAllowsFeePayer =
|
|
201
|
+
request.feePayer !== false &&
|
|
202
|
+
(request.feePayer === undefined ||
|
|
203
|
+
request.feePayer === true ||
|
|
204
|
+
typeof request.feePayer === 'object')
|
|
205
|
+
const resolvedFeePayer =
|
|
206
|
+
methodDetails.feePayer === true && requestAllowsFeePayer
|
|
207
|
+
? typeof request.feePayer === 'object'
|
|
208
|
+
? request.feePayer
|
|
209
|
+
: feePayer
|
|
210
|
+
: undefined
|
|
200
211
|
const minVoucherDelta = parseUnits(parameters.minVoucherDelta ?? '0', decimals)
|
|
201
212
|
const effectiveMinVoucherDelta = methodDetails.minVoucherDelta
|
|
202
213
|
? BigInt(methodDetails.minVoucherDelta)
|
|
@@ -268,7 +279,6 @@ export function session<const parameters extends session.Parameters>(
|
|
|
268
279
|
// This keeps equal-voucher replays bounded by the voucher's remaining
|
|
269
280
|
// balance instead of serving repeated responses for free.
|
|
270
281
|
if (
|
|
271
|
-
!parameters.sse &&
|
|
272
282
|
envelope &&
|
|
273
283
|
isSessionContentRequest(envelope.capturedRequest) &&
|
|
274
284
|
(payload.action === 'open' || payload.action === 'voucher')
|
|
@@ -283,6 +293,7 @@ export function session<const parameters extends session.Parameters>(
|
|
|
283
293
|
spent: charged.spent.toString(),
|
|
284
294
|
units: charged.units,
|
|
285
295
|
}
|
|
296
|
+
if (parameters.sse) sessionReceipt = Transport.markPrepaidSessionTick(sessionReceipt)
|
|
286
297
|
}
|
|
287
298
|
|
|
288
299
|
return sessionReceipt
|
|
@@ -454,6 +465,9 @@ export async function charge(
|
|
|
454
465
|
throw new ChannelClosedError({ reason: 'channel not found' })
|
|
455
466
|
}
|
|
456
467
|
if (!result.ok) {
|
|
468
|
+
if (result.channel.finalized) throw new ChannelClosedError({ reason: 'channel is finalized' })
|
|
469
|
+
if (result.channel.closeRequestedAt !== 0n)
|
|
470
|
+
throw new ChannelClosedError({ reason: 'channel has a pending close request' })
|
|
457
471
|
const available = result.channel.highestVoucherAmount - result.channel.spent
|
|
458
472
|
throw new InsufficientBalanceError({
|
|
459
473
|
reason: `requested ${amount}, available ${available}`,
|
|
@@ -622,6 +636,38 @@ async function handleOpen(
|
|
|
622
636
|
const currency = challenge.request.currency as Address
|
|
623
637
|
const amount = challenge.request.amount ? BigInt(challenge.request.amount as string) : undefined
|
|
624
638
|
|
|
639
|
+
const validateOpenVoucher = async (onChain: OnChainChannel) => {
|
|
640
|
+
validateOnChainChannel(onChain, recipient, currency, amount)
|
|
641
|
+
|
|
642
|
+
const authorizedSigner =
|
|
643
|
+
onChain.authorizedSigner === '0x0000000000000000000000000000000000000000'
|
|
644
|
+
? onChain.payer
|
|
645
|
+
: onChain.authorizedSigner
|
|
646
|
+
|
|
647
|
+
if (voucher.cumulativeAmount > onChain.deposit) {
|
|
648
|
+
throw new AmountExceedsDepositError({ reason: 'voucher amount exceeds on-chain deposit' })
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
if (voucher.cumulativeAmount <= onChain.settled) {
|
|
652
|
+
throw new VerificationFailedError({
|
|
653
|
+
reason: 'voucher cumulativeAmount is below on-chain settled amount',
|
|
654
|
+
})
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
const isValid = await verifyVoucher(
|
|
658
|
+
methodDetails.escrowContract,
|
|
659
|
+
methodDetails.chainId,
|
|
660
|
+
voucher,
|
|
661
|
+
authorizedSigner,
|
|
662
|
+
)
|
|
663
|
+
|
|
664
|
+
if (!isValid) {
|
|
665
|
+
throw new InvalidSignatureError({ reason: 'invalid voucher signature' })
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
return authorizedSigner
|
|
669
|
+
}
|
|
670
|
+
|
|
625
671
|
const { onChain, txHash } = await broadcastOpenTransaction({
|
|
626
672
|
client,
|
|
627
673
|
serializedTransaction: payload.transaction,
|
|
@@ -632,36 +678,13 @@ async function handleOpen(
|
|
|
632
678
|
challengeExpires: challenge.expires,
|
|
633
679
|
feePayerPolicy,
|
|
634
680
|
feePayer,
|
|
681
|
+
beforeBroadcast: async (pendingOnChain) => {
|
|
682
|
+
await validateOpenVoucher(pendingOnChain)
|
|
683
|
+
},
|
|
635
684
|
waitForConfirmation,
|
|
636
685
|
})
|
|
637
686
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
const authorizedSigner =
|
|
641
|
-
onChain.authorizedSigner === '0x0000000000000000000000000000000000000000'
|
|
642
|
-
? onChain.payer
|
|
643
|
-
: onChain.authorizedSigner
|
|
644
|
-
|
|
645
|
-
if (voucher.cumulativeAmount > onChain.deposit) {
|
|
646
|
-
throw new AmountExceedsDepositError({ reason: 'voucher amount exceeds on-chain deposit' })
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
if (voucher.cumulativeAmount <= onChain.settled) {
|
|
650
|
-
throw new VerificationFailedError({
|
|
651
|
-
reason: 'voucher cumulativeAmount is below on-chain settled amount',
|
|
652
|
-
})
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
const isValid = await verifyVoucher(
|
|
656
|
-
methodDetails.escrowContract,
|
|
657
|
-
methodDetails.chainId,
|
|
658
|
-
voucher,
|
|
659
|
-
authorizedSigner,
|
|
660
|
-
)
|
|
661
|
-
|
|
662
|
-
if (!isValid) {
|
|
663
|
-
throw new InvalidSignatureError({ reason: 'invalid voucher signature' })
|
|
664
|
-
}
|
|
687
|
+
const authorizedSigner = await validateOpenVoucher(onChain)
|
|
665
688
|
|
|
666
689
|
const updated = await store.updateChannel(channelId, (existing) => {
|
|
667
690
|
if (existing) {
|
|
@@ -914,16 +937,45 @@ async function handleClose(
|
|
|
914
937
|
throw new InvalidSignatureError({ reason: 'invalid voucher signature' })
|
|
915
938
|
}
|
|
916
939
|
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
940
|
+
const pendingCloseStartedAt = BigInt(Math.floor(Date.now() / 1000) || 1)
|
|
941
|
+
const previousCloseRequestedAt = channel.closeRequestedAt
|
|
942
|
+
let pendingCloseMarked = false
|
|
943
|
+
await store.updateChannel(channelId, (current) => {
|
|
944
|
+
if (!current) return null
|
|
945
|
+
if (current.finalized) throw new ChannelClosedError({ reason: 'channel is already finalized' })
|
|
946
|
+
if (current.closeRequestedAt !== 0n)
|
|
947
|
+
throw new ChannelClosedError({ reason: 'channel has a pending close request' })
|
|
948
|
+
if (voucher.cumulativeAmount < current.spent) {
|
|
949
|
+
throw new VerificationFailedError({
|
|
950
|
+
reason: `close voucher amount must be >= ${current.spent} (spent)`,
|
|
951
|
+
})
|
|
952
|
+
}
|
|
953
|
+
pendingCloseMarked = true
|
|
954
|
+
return { ...current, closeRequestedAt: pendingCloseStartedAt }
|
|
922
955
|
})
|
|
923
956
|
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
957
|
+
let txHash: Hex | undefined
|
|
958
|
+
try {
|
|
959
|
+
assertSettlementSender({
|
|
960
|
+
operation: 'close',
|
|
961
|
+
channelId: payload.channelId,
|
|
962
|
+
payee: onChain.payee,
|
|
963
|
+
sender: account?.address ?? client.account?.address,
|
|
964
|
+
})
|
|
965
|
+
|
|
966
|
+
txHash = await closeOnChain(client, methodDetails.escrowContract, voucher, {
|
|
967
|
+
...(feePayer && account ? { feePayer, account } : { account }),
|
|
968
|
+
})
|
|
969
|
+
} catch (error) {
|
|
970
|
+
if (pendingCloseMarked) {
|
|
971
|
+
await store.updateChannel(channelId, (current) =>
|
|
972
|
+
current && current.closeRequestedAt === pendingCloseStartedAt
|
|
973
|
+
? { ...current, closeRequestedAt: previousCloseRequestedAt }
|
|
974
|
+
: current,
|
|
975
|
+
)
|
|
976
|
+
}
|
|
977
|
+
throw error
|
|
978
|
+
}
|
|
927
979
|
|
|
928
980
|
const updated = await store.updateChannel(channelId, (current) => {
|
|
929
981
|
if (!current) return null
|