mppx 0.13.4 → 0.13.5
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 +21 -0
- package/dist/Errors.d.ts +63 -2
- package/dist/Errors.js +52 -2
- package/dist/Method.d.ts +10 -0
- package/dist/cli/cli.js +133 -133
- package/dist/cli/plugins/index.js +12 -12
- package/dist/client/Mppx.d.ts +2 -0
- package/dist/client/Mppx.js +8 -10
- package/dist/client/internal/Fetch.js +107 -8
- package/dist/client/internal/MethodResponse.d.ts +6 -0
- package/dist/client/internal/MethodResponse.js +16 -0
- package/dist/client/internal/protocols/Mcp.js +36 -5
- package/dist/evm/server/Charge.js +3 -1
- package/dist/internal/version.d.ts +2 -2
- package/dist/internal/version.js +1 -1
- package/dist/internal/warnOnce.d.ts +3 -0
- package/dist/internal/warnOnce.js +9 -0
- package/dist/server/Mppx.d.ts +5 -0
- package/dist/server/Mppx.js +14 -13
- package/dist/server/Transport.js +10 -0
- package/dist/tempo/Attribution.d.ts +24 -9
- package/dist/tempo/Attribution.js +25 -5
- package/dist/tempo/client/Charge.d.ts +116 -10
- package/dist/tempo/client/Charge.js +116 -22
- package/dist/tempo/client/Methods.d.ts +88 -9
- package/dist/tempo/index.d.ts +1 -0
- package/dist/tempo/index.js +1 -0
- package/dist/tempo/internal/fee-payer.js +4 -0
- package/dist/tempo/server/Charge.d.ts +11 -0
- package/dist/tempo/server/Charge.js +160 -29
- package/dist/tempo/server/Methods.js +16 -4
- package/dist/tempo/server/Subscription.js +2 -1
- package/dist/tempo/server/internal/fallback-store.d.ts +9 -0
- package/dist/tempo/server/internal/fallback-store.js +13 -0
- package/dist/tempo/server/internal/html.gen.js +1 -1
- package/dist/tempo/session/precompile/Chain.d.ts +2 -0
- package/dist/tempo/session/precompile/Chain.js +3 -1
- package/dist/tempo/session/server/Session.js +10 -11
- package/dist/tempo/session/server/Settlement.js +11 -3
- package/package.json +1 -1
|
@@ -155,6 +155,8 @@ export type ReadbackRetryOptions = {
|
|
|
155
155
|
retries?: number | undefined;
|
|
156
156
|
/** Delay between attempts, in milliseconds. @default 250 */
|
|
157
157
|
delayMs?: number | undefined;
|
|
158
|
+
/** Whether a failed attempt may be retried. @default every failure */
|
|
159
|
+
shouldRetry?: ((error: unknown) => boolean) | undefined;
|
|
158
160
|
};
|
|
159
161
|
/**
|
|
160
162
|
* Retry an on-chain readback that is pinned to the block containing a just-sent
|
|
@@ -169,7 +169,7 @@ export async function getChannelStatesBatch(client, channelIds, escrow = tip20Ch
|
|
|
169
169
|
* verification even though the open transaction succeeded.
|
|
170
170
|
*/
|
|
171
171
|
export async function readbackWithRetry(read, options = {}) {
|
|
172
|
-
const { retries = 5, delayMs = 250 } = options;
|
|
172
|
+
const { retries = 5, delayMs = 250, shouldRetry = () => true } = options;
|
|
173
173
|
let lastError;
|
|
174
174
|
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
175
175
|
try {
|
|
@@ -177,6 +177,8 @@ export async function readbackWithRetry(read, options = {}) {
|
|
|
177
177
|
}
|
|
178
178
|
catch (error) {
|
|
179
179
|
lastError = error;
|
|
180
|
+
if (!shouldRetry(error))
|
|
181
|
+
break;
|
|
180
182
|
if (attempt < retries)
|
|
181
183
|
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
182
184
|
}
|
|
@@ -13,12 +13,12 @@ import * as Errors from '../../../Errors.js';
|
|
|
13
13
|
import * as Expires from '../../../Expires.js';
|
|
14
14
|
import * as Method from '../../../Method.js';
|
|
15
15
|
import * as ChallengeMeta from '../../../server/internal/challengeMeta.js';
|
|
16
|
-
import * as Store from '../../../Store.js';
|
|
17
16
|
import * as Client from '../../../viem/Client.js';
|
|
18
17
|
import * as Account from '../../internal/account.js';
|
|
19
18
|
import * as defaults from '../../internal/defaults.js';
|
|
20
19
|
import * as Methods from '../../Methods.js';
|
|
21
20
|
import * as ChargeServer from '../../server/Charge.js';
|
|
21
|
+
import { fallbackStore } from '../../server/internal/fallback-store.js';
|
|
22
22
|
import * as Transport from '../../server/internal/transport.js';
|
|
23
23
|
import { tip20ChannelEscrow } from '../precompile/Protocol.js';
|
|
24
24
|
import { deserializeSnapshot as deserializeSessionSnapshot, serializeSnapshot as serializeSessionSnapshot, } from '../Snapshot.js';
|
|
@@ -66,17 +66,14 @@ function createBootstrapChallenge(parameters) {
|
|
|
66
66
|
});
|
|
67
67
|
}
|
|
68
68
|
function respondBootstrapChallenge(challenge, error) {
|
|
69
|
-
const headers = new Headers({
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
const headers = new Headers({ 'Cache-Control': 'no-store' });
|
|
70
|
+
// A 403 is final: omit the challenge, as `Transport.http()` does.
|
|
71
|
+
if (error?.status !== 403)
|
|
72
|
+
headers.set(Constants.Headers.wwwAuthenticate, Challenge.serialize(challenge));
|
|
73
73
|
if (!error)
|
|
74
74
|
return new Response(null, { status: 402, headers });
|
|
75
75
|
headers.set('Content-Type', 'application/problem+json');
|
|
76
|
-
return new Response(JSON.stringify(error.toProblemDetails(challenge.id)), {
|
|
77
|
-
status: error.status,
|
|
78
|
-
headers,
|
|
79
|
-
});
|
|
76
|
+
return new Response(JSON.stringify(error.toProblemDetails(error.status === 403 ? undefined : challenge.id)), { status: error.status, headers });
|
|
80
77
|
}
|
|
81
78
|
function assertBootstrapChallengeMatches(expected, actual) {
|
|
82
79
|
if (actual.method !== Constants.Methods.tempo || actual.intent !== Constants.Intents.charge)
|
|
@@ -164,7 +161,9 @@ async function handleBootstrapPreflight(parameters) {
|
|
|
164
161
|
});
|
|
165
162
|
}
|
|
166
163
|
catch (error) {
|
|
167
|
-
return respondBootstrapChallenge(challenge, error instanceof Errors.PaymentError
|
|
164
|
+
return respondBootstrapChallenge(challenge, error instanceof Errors.PaymentError
|
|
165
|
+
? error
|
|
166
|
+
: new Errors.InternalPaymentError({ cause: error }));
|
|
168
167
|
}
|
|
169
168
|
const channelId = await resolveSessionChannelId({
|
|
170
169
|
capturedRequest: parameters.capturedRequest,
|
|
@@ -195,7 +194,7 @@ async function handleBootstrapPreflight(parameters) {
|
|
|
195
194
|
/** Creates a server-side TIP20EscrowChannel precompile session payment method. */
|
|
196
195
|
export function session(p) {
|
|
197
196
|
const parameters = p;
|
|
198
|
-
const { amount, channelStateTtl = 5_000, currency = defaults.resolveCurrency(parameters), decimals = defaults.decimals, operator, store: rawStore =
|
|
197
|
+
const { amount, channelStateTtl = 5_000, currency = defaults.resolveCurrency(parameters), decimals = defaults.decimals, operator, store: rawStore = fallbackStore('tempo.session'), suggestedDeposit, unitType, } = parameters;
|
|
199
198
|
const settlementSchedule = resolveSettlementSchedule(parameters.settlementSchedule, decimals);
|
|
200
199
|
const onSessionSettlement = parameters.onSessionSettlement;
|
|
201
200
|
const onSessionSettlementFailure = parameters.onSessionSettlementFailure;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseError as viem_BaseError, HttpRequestError, InternalRpcError, isAddress, isAddressEqual, LimitExceededRpcError, parseUnits, ResourceUnavailableRpcError, RpcRequestError, SocketClosedError, TimeoutError, WaitForTransactionReceiptTimeoutError, WebSocketRequestError, zeroAddress, } from 'viem';
|
|
1
|
+
import { BaseError as viem_BaseError, ExecutionRevertedError, HttpRequestError, InternalRpcError, isAddress, isAddressEqual, LimitExceededRpcError, parseUnits, ResourceUnavailableRpcError, RpcRequestError, SocketClosedError, TimeoutError, WaitForTransactionReceiptTimeoutError, WebSocketRequestError, zeroAddress, } from 'viem';
|
|
2
2
|
import { BadRequestError, ChannelClosedError, ChannelNotFoundError, InsufficientBalanceError, VerificationFailedError, } from '../../../Errors.js';
|
|
3
3
|
import { isSessionContentRequest } from '../../server/internal/request-body.js';
|
|
4
4
|
import * as Chain from '../precompile/Chain.js';
|
|
@@ -307,7 +307,10 @@ export async function settle(store_, client, channelId_, options) {
|
|
|
307
307
|
sender: account?.address,
|
|
308
308
|
});
|
|
309
309
|
const amount = uint96(channel.highestVoucher.cumulativeAmount);
|
|
310
|
-
const
|
|
310
|
+
const { signature } = channel.highestVoucher;
|
|
311
|
+
// A replica behind the open or top-up that admitted this voucher rejects the settle while
|
|
312
|
+
// preparing it (e.g. `ChannelNotFound`); nothing was broadcast, so retry until it catches up.
|
|
313
|
+
const txHash = await Chain.readbackWithRetry(() => Chain.settleOnChain(client, channel.descriptor, amount, signature, escrow, account
|
|
311
314
|
? {
|
|
312
315
|
account,
|
|
313
316
|
...(options?.feePayer ? { feePayer: options.feePayer } : {}),
|
|
@@ -315,7 +318,7 @@ export async function settle(store_, client, channelId_, options) {
|
|
|
315
318
|
...(options?.feeToken ? { feeToken: options.feeToken } : {}),
|
|
316
319
|
candidateFeeTokens: options?.candidateFeeTokens ?? [channel.token],
|
|
317
320
|
}
|
|
318
|
-
: undefined);
|
|
321
|
+
: undefined), { shouldRetry: isExecutionRevert });
|
|
319
322
|
const receipt = await Chain.waitForSuccessfulReceipt(client, txHash);
|
|
320
323
|
const settled = readSettledReceiptFields(Chain.getChannelEvent(receipt, 'Settled', channelId));
|
|
321
324
|
const { deltaPaid, newSettled } = settled;
|
|
@@ -389,6 +392,11 @@ export function ignoreRetryableSettlementFailure(error) {
|
|
|
389
392
|
return undefined;
|
|
390
393
|
throw error;
|
|
391
394
|
}
|
|
395
|
+
/** Whether a settlement failed because the node's execution reverted it. */
|
|
396
|
+
function isExecutionRevert(error) {
|
|
397
|
+
return (error instanceof viem_BaseError &&
|
|
398
|
+
Boolean(error.walk((cause) => cause instanceof ExecutionRevertedError)));
|
|
399
|
+
}
|
|
392
400
|
function isRetryableSettlementFailure(error) {
|
|
393
401
|
if (error instanceof SettlementLeaseError)
|
|
394
402
|
return true;
|