mppx 0.3.9 → 0.3.11
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/README.md +3 -3
- package/dist/Challenge.d.ts.map +1 -1
- package/dist/Challenge.js +2 -0
- package/dist/Challenge.js.map +1 -1
- package/dist/Errors.d.ts +0 -2
- package/dist/Errors.d.ts.map +1 -1
- package/dist/Errors.js +1 -3
- package/dist/Errors.js.map +1 -1
- package/dist/internal/constantTimeEqual.d.ts.map +1 -1
- package/dist/internal/constantTimeEqual.js +4 -6
- package/dist/internal/constantTimeEqual.js.map +1 -1
- package/dist/internal/env.d.ts +2 -2
- package/dist/internal/env.d.ts.map +1 -1
- package/dist/internal/env.js +1 -2
- package/dist/internal/env.js.map +1 -1
- package/dist/middlewares/internal/mppx.d.ts.map +1 -1
- package/dist/middlewares/internal/mppx.js +6 -2
- package/dist/middlewares/internal/mppx.js.map +1 -1
- package/dist/server/Mppx.d.ts +13 -3
- package/dist/server/Mppx.d.ts.map +1 -1
- package/dist/server/Mppx.js +46 -3
- package/dist/server/Mppx.js.map +1 -1
- package/dist/tempo/internal/simulate.d.ts +21 -0
- package/dist/tempo/internal/simulate.d.ts.map +1 -0
- package/dist/tempo/internal/simulate.js +31 -0
- package/dist/tempo/internal/simulate.js.map +1 -0
- package/dist/tempo/server/Charge.d.ts +12 -0
- package/dist/tempo/server/Charge.d.ts.map +1 -1
- package/dist/tempo/server/Charge.js +28 -6
- package/dist/tempo/server/Charge.js.map +1 -1
- package/dist/tempo/server/Session.d.ts +14 -0
- package/dist/tempo/server/Session.d.ts.map +1 -1
- package/dist/tempo/server/Session.js +59 -40
- package/dist/tempo/server/Session.js.map +1 -1
- package/dist/tempo/session/Chain.d.ts +3 -0
- package/dist/tempo/session/Chain.d.ts.map +1 -1
- package/dist/tempo/session/Chain.js +27 -6
- package/dist/tempo/session/Chain.js.map +1 -1
- package/package.json +1 -1
- package/src/Challenge.ts +2 -0
- package/src/Errors.test.ts +43 -18
- package/src/Errors.ts +1 -4
- package/src/client/Mppx.test.ts +1 -0
- package/src/internal/constantTimeEqual.ts +5 -4
- package/src/internal/env.test.ts +2 -2
- package/src/internal/env.ts +4 -5
- package/src/middlewares/express.test.ts +5 -0
- package/src/middlewares/hono.test.ts +5 -0
- package/src/middlewares/internal/mppx.ts +5 -2
- package/src/middlewares/nextjs.test.ts +5 -0
- package/src/proxy/Proxy.test.ts +3 -0
- package/src/proxy/services/openai.test.ts +3 -0
- package/src/server/Mppx.test.ts +93 -2
- package/src/server/Mppx.ts +81 -6
- package/src/tempo/internal/simulate.ts +49 -0
- package/src/tempo/server/Charge.test.ts +62 -0
- package/src/tempo/server/Charge.ts +44 -6
- package/src/tempo/server/Session.test.ts +49 -0
- package/src/tempo/server/Session.ts +76 -34
- package/src/tempo/session/Chain.test.ts +36 -0
- package/src/tempo/session/Chain.ts +38 -2
|
@@ -1101,6 +1101,55 @@ describe('session', () => {
|
|
|
1101
1101
|
expect(result).toBeUndefined()
|
|
1102
1102
|
})
|
|
1103
1103
|
|
|
1104
|
+
test('returns undefined for open POST with content-length > 0 (content request)', () => {
|
|
1105
|
+
const server = createServer()
|
|
1106
|
+
const result = server.respond!({
|
|
1107
|
+
credential: {
|
|
1108
|
+
challenge: makeChallenge({
|
|
1109
|
+
channelId: '0x0000000000000000000000000000000000000000000000000000000000000001' as Hex,
|
|
1110
|
+
}),
|
|
1111
|
+
payload: { action: 'open' },
|
|
1112
|
+
},
|
|
1113
|
+
input: new Request('http://localhost', {
|
|
1114
|
+
method: 'POST',
|
|
1115
|
+
headers: { 'content-length': '42' },
|
|
1116
|
+
}),
|
|
1117
|
+
} as any)
|
|
1118
|
+
expect(result).toBeUndefined()
|
|
1119
|
+
})
|
|
1120
|
+
|
|
1121
|
+
test('returns undefined for open POST with transfer-encoding header (content request)', () => {
|
|
1122
|
+
const server = createServer()
|
|
1123
|
+
const result = server.respond!({
|
|
1124
|
+
credential: {
|
|
1125
|
+
challenge: makeChallenge({
|
|
1126
|
+
channelId: '0x0000000000000000000000000000000000000000000000000000000000000001' as Hex,
|
|
1127
|
+
}),
|
|
1128
|
+
payload: { action: 'open' },
|
|
1129
|
+
},
|
|
1130
|
+
input: new Request('http://localhost', {
|
|
1131
|
+
method: 'POST',
|
|
1132
|
+
headers: { 'transfer-encoding': 'chunked' },
|
|
1133
|
+
}),
|
|
1134
|
+
} as any)
|
|
1135
|
+
expect(result).toBeUndefined()
|
|
1136
|
+
})
|
|
1137
|
+
|
|
1138
|
+
test('returns 204 for GET with topUp action', () => {
|
|
1139
|
+
const server = createServer()
|
|
1140
|
+
const result = server.respond!({
|
|
1141
|
+
credential: {
|
|
1142
|
+
challenge: makeChallenge({
|
|
1143
|
+
channelId: '0x0000000000000000000000000000000000000000000000000000000000000001' as Hex,
|
|
1144
|
+
}),
|
|
1145
|
+
payload: { action: 'topUp' },
|
|
1146
|
+
},
|
|
1147
|
+
input: new Request('http://localhost', { method: 'GET' }),
|
|
1148
|
+
} as any)
|
|
1149
|
+
expect(result).toBeInstanceOf(Response)
|
|
1150
|
+
expect((result as Response).status).toBe(204)
|
|
1151
|
+
})
|
|
1152
|
+
|
|
1104
1153
|
test('returns undefined for voucher POST with content-length > 0 (content request)', () => {
|
|
1105
1154
|
const server = createServer()
|
|
1106
1155
|
const result = server.respond!({
|
|
@@ -85,13 +85,17 @@ export function session<const parameters extends session.Parameters>(p?: paramet
|
|
|
85
85
|
const parameters = p as parameters
|
|
86
86
|
const {
|
|
87
87
|
amount,
|
|
88
|
+
channelStateTtl = 60_000,
|
|
88
89
|
currency = defaults.resolveCurrency(parameters),
|
|
89
90
|
decimals = defaults.decimals,
|
|
90
91
|
store: rawStore = Store.memory(),
|
|
91
92
|
suggestedDeposit,
|
|
92
93
|
unitType,
|
|
94
|
+
waitForConfirmation = true,
|
|
93
95
|
} = parameters
|
|
94
96
|
|
|
97
|
+
const lastOnChainVerified = new Map<Hex, number>()
|
|
98
|
+
|
|
95
99
|
const store = ChannelStore.fromStore(rawStore)
|
|
96
100
|
|
|
97
101
|
const getClient = Client.getResolver({
|
|
@@ -187,7 +191,9 @@ export function session<const parameters extends session.Parameters>(p?: paramet
|
|
|
187
191
|
payload,
|
|
188
192
|
methodDetails,
|
|
189
193
|
resolvedFeePayer,
|
|
194
|
+
waitForConfirmation,
|
|
190
195
|
)
|
|
196
|
+
lastOnChainVerified.set(payload.channelId, Date.now())
|
|
191
197
|
break
|
|
192
198
|
|
|
193
199
|
case 'topUp':
|
|
@@ -199,6 +205,7 @@ export function session<const parameters extends session.Parameters>(p?: paramet
|
|
|
199
205
|
methodDetails,
|
|
200
206
|
resolvedFeePayer,
|
|
201
207
|
)
|
|
208
|
+
lastOnChainVerified.set(payload.channelId, Date.now())
|
|
202
209
|
break
|
|
203
210
|
|
|
204
211
|
case 'voucher':
|
|
@@ -209,6 +216,8 @@ export function session<const parameters extends session.Parameters>(p?: paramet
|
|
|
209
216
|
challenge,
|
|
210
217
|
payload,
|
|
211
218
|
methodDetails,
|
|
219
|
+
channelStateTtl,
|
|
220
|
+
lastOnChainVerified,
|
|
212
221
|
)
|
|
213
222
|
break
|
|
214
223
|
|
|
@@ -238,34 +247,30 @@ export function session<const parameters extends session.Parameters>(p?: paramet
|
|
|
238
247
|
// invoking the user's route handler. When it returns undefined, the
|
|
239
248
|
// user's handler runs normally and serves content.
|
|
240
249
|
//
|
|
241
|
-
//
|
|
242
|
-
// return 204 regardless of request method.
|
|
250
|
+
// close and topUp are always gated (204) — they are pure management.
|
|
243
251
|
//
|
|
244
|
-
//
|
|
245
|
-
//
|
|
246
|
-
//
|
|
247
|
-
//
|
|
248
|
-
//
|
|
249
|
-
//
|
|
250
|
-
// single GET retry) receive content as expected.
|
|
252
|
+
// open and voucher are gated only for bodyless POSTs (management
|
|
253
|
+
// updates). POSTs with a body are content requests — the client's
|
|
254
|
+
// original request piggybacked on the credential — so they fall
|
|
255
|
+
// through to serve content. GETs always fall through so auto-mode
|
|
256
|
+
// clients (whose fetch wrapper bundles open+voucher into a single
|
|
257
|
+
// GET retry) receive content as expected.
|
|
251
258
|
respond({ credential, input }) {
|
|
252
259
|
const { payload } = credential as Credential.Credential<SessionCredentialPayload>
|
|
253
260
|
|
|
254
261
|
if (payload.action === 'close') return new Response(null, { status: 204 })
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
if (
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
if (input.headers.has('transfer-encoding')) return undefined
|
|
268
|
-
return new Response(null, { status: 204 })
|
|
262
|
+
if (payload.action === 'topUp') return new Response(null, { status: 204 })
|
|
263
|
+
|
|
264
|
+
// open and voucher: gate only bodyless POSTs (management updates).
|
|
265
|
+
// POSTs with a body are content requests — fall through so the
|
|
266
|
+
// upstream response is returned to the client.
|
|
267
|
+
if (input.method === 'POST') {
|
|
268
|
+
const contentLength = input.headers.get('content-length')
|
|
269
|
+
if (contentLength !== null && contentLength !== '0') return undefined
|
|
270
|
+
if (input.headers.has('transfer-encoding')) return undefined
|
|
271
|
+
return new Response(null, { status: 204 })
|
|
272
|
+
}
|
|
273
|
+
return undefined
|
|
269
274
|
},
|
|
270
275
|
})
|
|
271
276
|
}
|
|
@@ -277,8 +282,22 @@ export declare namespace session {
|
|
|
277
282
|
>
|
|
278
283
|
|
|
279
284
|
type Parameters = {
|
|
285
|
+
/** 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 60_000 */
|
|
286
|
+
channelStateTtl?: number | undefined
|
|
280
287
|
/** Minimum voucher delta to accept (numeric string, default: "0"). */
|
|
281
288
|
minVoucherDelta?: string | undefined
|
|
289
|
+
/**
|
|
290
|
+
* Whether to wait for the open transaction to confirm on-chain before
|
|
291
|
+
* responding. @default true
|
|
292
|
+
*
|
|
293
|
+
* When `false`, the transaction is simulated via `eth_estimateGas` and
|
|
294
|
+
* broadcast without waiting for inclusion. The receipt will optimistically
|
|
295
|
+
* report `status: 'success'` based on simulation alone — if the
|
|
296
|
+
* transaction reverts on-chain after broadcast (e.g. due to a state
|
|
297
|
+
* change between simulation and inclusion), the receipt will not reflect
|
|
298
|
+
* the failure.
|
|
299
|
+
*/
|
|
300
|
+
waitForConfirmation?: boolean | undefined
|
|
282
301
|
/** Store backend for channel state. */
|
|
283
302
|
store?: Store.Store | undefined
|
|
284
303
|
/**
|
|
@@ -492,7 +511,12 @@ async function verifyAndAcceptVoucher(parameters: {
|
|
|
492
511
|
}
|
|
493
512
|
|
|
494
513
|
/**
|
|
495
|
-
* Handle 'open' action -
|
|
514
|
+
* Handle 'open' action - verify voucher, create channel, and broadcast.
|
|
515
|
+
*
|
|
516
|
+
* When `waitForConfirmation` is true (default), the open transaction is
|
|
517
|
+
* broadcast and confirmed on-chain before responding. When false, the
|
|
518
|
+
* transaction is validated and simulated via `eth_estimateGas`; the receipt
|
|
519
|
+
* is returned immediately and the broadcast runs in the background.
|
|
496
520
|
*/
|
|
497
521
|
async function handleOpen(
|
|
498
522
|
store: ChannelStore.ChannelStore,
|
|
@@ -501,6 +525,7 @@ async function handleOpen(
|
|
|
501
525
|
payload: SessionCredentialPayload & { action: 'open' },
|
|
502
526
|
methodDetails: SessionMethodDetails,
|
|
503
527
|
feePayer: viem_Account | undefined,
|
|
528
|
+
waitForConfirmation: boolean,
|
|
504
529
|
): Promise<SessionReceipt> {
|
|
505
530
|
const voucher = parseVoucherFromPayload(
|
|
506
531
|
payload.channelId,
|
|
@@ -520,6 +545,7 @@ async function handleOpen(
|
|
|
520
545
|
recipient,
|
|
521
546
|
currency,
|
|
522
547
|
feePayer,
|
|
548
|
+
waitForConfirmation,
|
|
523
549
|
})
|
|
524
550
|
|
|
525
551
|
validateOnChainChannel(onChain, recipient, currency, amount)
|
|
@@ -631,6 +657,7 @@ async function handleTopUp(
|
|
|
631
657
|
serializedTransaction: payload.transaction,
|
|
632
658
|
escrowContract: methodDetails.escrowContract,
|
|
633
659
|
channelId: payload.channelId,
|
|
660
|
+
currency: challenge.request.currency as Address,
|
|
634
661
|
declaredDeposit,
|
|
635
662
|
previousDeposit: channel.deposit,
|
|
636
663
|
feePayer,
|
|
@@ -655,11 +682,13 @@ async function handleTopUp(
|
|
|
655
682
|
*/
|
|
656
683
|
async function handleVoucher(
|
|
657
684
|
store: ChannelStore.ChannelStore,
|
|
658
|
-
|
|
685
|
+
client: viem_Client,
|
|
659
686
|
minVoucherDelta: bigint,
|
|
660
687
|
challenge: Challenge.Challenge,
|
|
661
688
|
payload: SessionCredentialPayload & { action: 'voucher' },
|
|
662
689
|
methodDetails: SessionMethodDetails,
|
|
690
|
+
channelStateTtl: number,
|
|
691
|
+
lastOnChainVerified: Map<Hex, number>,
|
|
663
692
|
): Promise<SessionReceipt> {
|
|
664
693
|
const channel = await store.getChannel(payload.channelId)
|
|
665
694
|
if (!channel) {
|
|
@@ -681,15 +710,28 @@ async function handleVoucher(
|
|
|
681
710
|
// same session can safely use the cached deposit/signer values.
|
|
682
711
|
// This avoids an RPC round-trip per voucher, which is critical for
|
|
683
712
|
// high-frequency SSE streaming where vouchers arrive per-token.
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
713
|
+
//
|
|
714
|
+
// To guard against the payer initiating a forced close while vouchers
|
|
715
|
+
// are still being accepted, re-query on-chain state when the cache
|
|
716
|
+
// exceeds the configured staleness TTL.
|
|
717
|
+
const lastVerified = lastOnChainVerified.get(payload.channelId) ?? 0
|
|
718
|
+
const isStale = Date.now() - lastVerified > channelStateTtl
|
|
719
|
+
|
|
720
|
+
let cachedOnChain: OnChainChannel
|
|
721
|
+
if (isStale) {
|
|
722
|
+
cachedOnChain = await getOnChainChannel(client, methodDetails.escrowContract, payload.channelId)
|
|
723
|
+
lastOnChainVerified.set(payload.channelId, Date.now())
|
|
724
|
+
} else {
|
|
725
|
+
cachedOnChain = {
|
|
726
|
+
payer: channel.payer,
|
|
727
|
+
payee: channel.payee,
|
|
728
|
+
token: channel.token,
|
|
729
|
+
deposit: channel.deposit,
|
|
730
|
+
settled: channel.settledOnChain,
|
|
731
|
+
finalized: channel.finalized,
|
|
732
|
+
authorizedSigner: channel.authorizedSigner,
|
|
733
|
+
closeRequestedAt: 0n,
|
|
734
|
+
}
|
|
693
735
|
}
|
|
694
736
|
|
|
695
737
|
return verifyAndAcceptVoucher({
|
|
@@ -347,6 +347,38 @@ describe('on-chain', () => {
|
|
|
347
347
|
expect(result.txHash).toBeUndefined()
|
|
348
348
|
expect(result.onChain.deposit).toBe(deposit)
|
|
349
349
|
})
|
|
350
|
+
|
|
351
|
+
test('waitForConfirmation: false returns derived on-chain state', async () => {
|
|
352
|
+
const salt = nextSalt()
|
|
353
|
+
const deposit = 10_000_000n
|
|
354
|
+
|
|
355
|
+
const { channelId, serializedTransaction } = await signOpenChannel({
|
|
356
|
+
escrow: escrowContract,
|
|
357
|
+
payer,
|
|
358
|
+
payee: recipient,
|
|
359
|
+
token: currency,
|
|
360
|
+
deposit,
|
|
361
|
+
salt,
|
|
362
|
+
})
|
|
363
|
+
|
|
364
|
+
const result = await broadcastOpenTransaction({
|
|
365
|
+
client,
|
|
366
|
+
serializedTransaction,
|
|
367
|
+
escrowContract,
|
|
368
|
+
channelId,
|
|
369
|
+
recipient,
|
|
370
|
+
currency,
|
|
371
|
+
waitForConfirmation: false,
|
|
372
|
+
})
|
|
373
|
+
|
|
374
|
+
expect(result.txHash).toBeDefined()
|
|
375
|
+
expect(result.onChain.payer.toLowerCase()).toBe(payer.address.toLowerCase())
|
|
376
|
+
expect(result.onChain.payee.toLowerCase()).toBe(recipient.toLowerCase())
|
|
377
|
+
expect(result.onChain.token.toLowerCase()).toBe(currency.toLowerCase())
|
|
378
|
+
expect(result.onChain.deposit).toBe(deposit)
|
|
379
|
+
expect(result.onChain.settled).toBe(0n)
|
|
380
|
+
expect(result.onChain.finalized).toBe(false)
|
|
381
|
+
})
|
|
350
382
|
})
|
|
351
383
|
|
|
352
384
|
describe('broadcastTopUpTransaction', () => {
|
|
@@ -381,6 +413,7 @@ describe('on-chain', () => {
|
|
|
381
413
|
serializedTransaction,
|
|
382
414
|
escrowContract,
|
|
383
415
|
channelId: wrongChannelId,
|
|
416
|
+
currency: asset,
|
|
384
417
|
declaredDeposit: topUpAmount,
|
|
385
418
|
previousDeposit: deposit,
|
|
386
419
|
}),
|
|
@@ -415,6 +448,7 @@ describe('on-chain', () => {
|
|
|
415
448
|
serializedTransaction,
|
|
416
449
|
escrowContract,
|
|
417
450
|
channelId,
|
|
451
|
+
currency: asset,
|
|
418
452
|
declaredDeposit: 9_999_999n,
|
|
419
453
|
previousDeposit: deposit,
|
|
420
454
|
}),
|
|
@@ -448,6 +482,7 @@ describe('on-chain', () => {
|
|
|
448
482
|
serializedTransaction,
|
|
449
483
|
escrowContract,
|
|
450
484
|
channelId,
|
|
485
|
+
currency: asset,
|
|
451
486
|
declaredDeposit: topUpAmount,
|
|
452
487
|
previousDeposit: deposit,
|
|
453
488
|
})
|
|
@@ -502,6 +537,7 @@ describe('on-chain', () => {
|
|
|
502
537
|
serializedTransaction: tampered,
|
|
503
538
|
escrowContract,
|
|
504
539
|
channelId,
|
|
540
|
+
currency: asset,
|
|
505
541
|
declaredDeposit: topUpAmount,
|
|
506
542
|
previousDeposit: deposit,
|
|
507
543
|
feePayer: accounts[0],
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
decodeFunctionData,
|
|
6
6
|
encodeFunctionData,
|
|
7
7
|
getAbiItem,
|
|
8
|
+
getAddress,
|
|
8
9
|
type Hex,
|
|
9
10
|
isAddressEqual,
|
|
10
11
|
type ReadContractReturnType,
|
|
@@ -13,6 +14,7 @@ import {
|
|
|
13
14
|
import {
|
|
14
15
|
prepareTransactionRequest,
|
|
15
16
|
readContract,
|
|
17
|
+
sendRawTransaction,
|
|
16
18
|
sendRawTransactionSync,
|
|
17
19
|
signTransaction,
|
|
18
20
|
writeContract,
|
|
@@ -20,6 +22,7 @@ import {
|
|
|
20
22
|
import { Transaction } from 'viem/tempo'
|
|
21
23
|
import { BadRequestError, ChannelClosedError, VerificationFailedError } from '../../Errors.js'
|
|
22
24
|
import * as defaults from '../internal/defaults.js'
|
|
25
|
+
import { simulateTransaction } from '../internal/simulate.js'
|
|
23
26
|
import { escrowAbi } from './escrow.abi.js'
|
|
24
27
|
import type { SignedVoucher } from './Types.js'
|
|
25
28
|
|
|
@@ -205,6 +208,8 @@ export async function broadcastOpenTransaction(parameters: {
|
|
|
205
208
|
recipient: Address
|
|
206
209
|
currency: Address
|
|
207
210
|
feePayer?: Account | undefined
|
|
211
|
+
/** When false, simulates instead of waiting for confirmation and returns derived on-chain state. @default true */
|
|
212
|
+
waitForConfirmation?: boolean | undefined
|
|
208
213
|
}): Promise<BroadcastResult> {
|
|
209
214
|
const {
|
|
210
215
|
client,
|
|
@@ -214,6 +219,7 @@ export async function broadcastOpenTransaction(parameters: {
|
|
|
214
219
|
recipient,
|
|
215
220
|
currency,
|
|
216
221
|
feePayer,
|
|
222
|
+
waitForConfirmation = true,
|
|
217
223
|
} = parameters
|
|
218
224
|
|
|
219
225
|
const transaction = Transaction.deserialize(
|
|
@@ -252,7 +258,13 @@ export async function broadcastOpenTransaction(parameters: {
|
|
|
252
258
|
}
|
|
253
259
|
|
|
254
260
|
const { args: openArgs } = decodeFunctionData({ abi: escrowAbi, data: openCall.data! })
|
|
255
|
-
const [payee, token] = openArgs as readonly [
|
|
261
|
+
const [payee, token, deposit, , authorizedSigner] = openArgs as readonly [
|
|
262
|
+
Address,
|
|
263
|
+
Address,
|
|
264
|
+
bigint,
|
|
265
|
+
Hex,
|
|
266
|
+
Address,
|
|
267
|
+
]
|
|
256
268
|
|
|
257
269
|
if (!isAddressEqual(payee, recipient)) {
|
|
258
270
|
throw new VerificationFailedError({
|
|
@@ -276,6 +288,28 @@ export async function broadcastOpenTransaction(parameters: {
|
|
|
276
288
|
return serializedTransaction
|
|
277
289
|
})()
|
|
278
290
|
|
|
291
|
+
if (!waitForConfirmation) {
|
|
292
|
+
const from = getAddress(transaction.from as Address)
|
|
293
|
+
await simulateTransaction(client, { ...transaction, from, calls })
|
|
294
|
+
const txHash = await sendRawTransaction(client, {
|
|
295
|
+
serializedTransaction: serializedTransaction_final as Transaction.TransactionSerializedTempo,
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
return {
|
|
299
|
+
txHash,
|
|
300
|
+
onChain: {
|
|
301
|
+
payer: from,
|
|
302
|
+
payee,
|
|
303
|
+
token,
|
|
304
|
+
authorizedSigner,
|
|
305
|
+
deposit,
|
|
306
|
+
settled: 0n,
|
|
307
|
+
closeRequestedAt: 0n,
|
|
308
|
+
finalized: false,
|
|
309
|
+
} as OnChainChannel,
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
279
313
|
let txHash: Hex | undefined
|
|
280
314
|
try {
|
|
281
315
|
const receipt = await sendRawTransactionSync(client, {
|
|
@@ -307,6 +341,7 @@ export async function broadcastTopUpTransaction(parameters: {
|
|
|
307
341
|
serializedTransaction: Hex
|
|
308
342
|
escrowContract: Address
|
|
309
343
|
channelId: Hex
|
|
344
|
+
currency: Address
|
|
310
345
|
declaredDeposit: bigint
|
|
311
346
|
previousDeposit: bigint
|
|
312
347
|
feePayer?: Account | undefined
|
|
@@ -316,6 +351,7 @@ export async function broadcastTopUpTransaction(parameters: {
|
|
|
316
351
|
serializedTransaction,
|
|
317
352
|
escrowContract,
|
|
318
353
|
channelId,
|
|
354
|
+
currency,
|
|
319
355
|
declaredDeposit,
|
|
320
356
|
previousDeposit,
|
|
321
357
|
feePayer,
|
|
@@ -347,7 +383,7 @@ export async function broadcastTopUpTransaction(parameters: {
|
|
|
347
383
|
const selector = call.data.slice(0, 10)
|
|
348
384
|
const isEscrowTopUp =
|
|
349
385
|
isAddressEqual(call.to, escrowContract) && selector === escrowTopUpSelector
|
|
350
|
-
const isTokenApprove = selector === erc20ApproveSelector
|
|
386
|
+
const isTokenApprove = isAddressEqual(call.to, currency) && selector === erc20ApproveSelector
|
|
351
387
|
if (!isEscrowTopUp && !isTokenApprove) {
|
|
352
388
|
throw new BadRequestError({
|
|
353
389
|
reason: 'fee-sponsored topUp transaction contains an unauthorized call',
|