mppx 0.10.0 → 0.11.0
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 +19 -0
- package/dist/cli/cli.js +134 -134
- package/dist/cli/plugins/index.js +12 -12
- package/dist/client/Mppx.d.ts +25 -12
- package/dist/client/Mppx.js +18 -7
- package/dist/client/internal/protocols/X402.js +0 -2
- package/dist/internal/version.d.ts +2 -2
- package/dist/internal/version.js +1 -1
- package/dist/stripe/server/Methods.js +11 -3
- package/dist/stripe/server/internal/analytics.d.ts +1 -0
- package/dist/stripe/server/internal/analytics.js +2 -2
- package/dist/stripe/server/internal/record-payment.js +2 -2
- package/dist/tempo/Methods.d.ts +5 -3
- package/dist/tempo/Methods.js +6 -5
- package/dist/tempo/client/Charge.d.ts +6 -4
- package/dist/tempo/client/Charge.js +61 -28
- package/dist/tempo/client/Methods.d.ts +5 -3
- package/dist/tempo/index.d.ts +2 -1
- package/dist/tempo/index.js +2 -1
- package/dist/tempo/internal/defaults.d.ts +11 -0
- package/dist/tempo/internal/defaults.js +11 -0
- package/dist/tempo/internal/machine-token-charge.d.ts +56 -0
- package/dist/tempo/internal/machine-token-charge.js +108 -0
- package/dist/tempo/server/Charge.d.ts +3 -0
- package/dist/tempo/server/Charge.js +68 -36
- package/dist/tempo/server/Methods.d.ts +9 -4
- package/dist/tempo/server/Methods.js +5 -2
- package/dist/tempo/server/internal/html.gen.d.ts +1 -1
- package/dist/tempo/server/internal/html.gen.js +1 -1
- package/package.json +1 -1
- package/dist/tempo/Tokens.d.ts +0 -13
- package/dist/tempo/Tokens.js +0 -13
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type * as Hex from 'ox/Hex';
|
|
2
|
+
import { type Address, type Client } from 'viem';
|
|
3
|
+
type Call = {
|
|
4
|
+
data?: Hex.Hex | undefined;
|
|
5
|
+
to?: Address | undefined;
|
|
6
|
+
value?: bigint | undefined;
|
|
7
|
+
};
|
|
8
|
+
type RouteCall = {
|
|
9
|
+
data: Hex.Hex;
|
|
10
|
+
to: Address;
|
|
11
|
+
};
|
|
12
|
+
type Route = {
|
|
13
|
+
calls: readonly [RouteCall, RouteCall];
|
|
14
|
+
settlementSender: Address;
|
|
15
|
+
transfers: readonly [{
|
|
16
|
+
amount: bigint;
|
|
17
|
+
memo: Hex.Hex;
|
|
18
|
+
recipient: Address;
|
|
19
|
+
}];
|
|
20
|
+
};
|
|
21
|
+
type Transfer = {
|
|
22
|
+
amount: bigint | string;
|
|
23
|
+
memo?: string | undefined;
|
|
24
|
+
recipient: Address;
|
|
25
|
+
};
|
|
26
|
+
/** Returns whether a first-party machine-token charge route is deployed on a chain. */
|
|
27
|
+
export declare function isSupported(chainId: number | undefined): boolean;
|
|
28
|
+
/** Returns whether a currency is the machine token for a supported chain. */
|
|
29
|
+
export declare function isMachineToken(parameters: {
|
|
30
|
+
chainId: number | undefined;
|
|
31
|
+
currency: Address;
|
|
32
|
+
}): boolean;
|
|
33
|
+
/** Resolves the canonical first-party settlement route for a compatible charge. */
|
|
34
|
+
export declare function getRoute(parameters: {
|
|
35
|
+
chainId: number | undefined;
|
|
36
|
+
currency: Address;
|
|
37
|
+
transfers: readonly Transfer[];
|
|
38
|
+
}): Route | undefined;
|
|
39
|
+
/** Builds and simulates the first-party settlement route. */
|
|
40
|
+
export declare function findRoute(client: Client, parameters: {
|
|
41
|
+
account: Address;
|
|
42
|
+
chainId: number;
|
|
43
|
+
currency: Address;
|
|
44
|
+
transfers: readonly Transfer[];
|
|
45
|
+
}): Promise<Route | undefined>;
|
|
46
|
+
/** Matches an exact first-party settlement route. */
|
|
47
|
+
export declare function matchRoute(parameters: {
|
|
48
|
+
calls: readonly Call[];
|
|
49
|
+
chainId: number | undefined;
|
|
50
|
+
currency: Address;
|
|
51
|
+
transfers: readonly Transfer[];
|
|
52
|
+
}): Route | undefined;
|
|
53
|
+
/** Returns the trusted sender that settles the configured charge route on a chain. */
|
|
54
|
+
export declare function getSettlementSender(chainId: number | undefined): Address | undefined;
|
|
55
|
+
export {};
|
|
56
|
+
//# sourceMappingURL=machine-token-charge.d.ts.map
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { decodeFunctionData, encodeFunctionData, isAddress, isAddressEqual, parseAbi, } from 'viem';
|
|
2
|
+
import { call, readContract } from 'viem/actions';
|
|
3
|
+
import { Abis, Actions } from 'viem/tempo';
|
|
4
|
+
import * as defaults from './defaults.js';
|
|
5
|
+
const machineTokenSwapperAbi = parseAbi([
|
|
6
|
+
'function swapTo(address inputToken,uint256 amount,address targetToken,address recipient,bytes32 memo)',
|
|
7
|
+
]);
|
|
8
|
+
function getDeployment(chainId) {
|
|
9
|
+
if (chainId === undefined)
|
|
10
|
+
return undefined;
|
|
11
|
+
return defaults.machineToken[chainId];
|
|
12
|
+
}
|
|
13
|
+
/** Returns whether a first-party machine-token charge route is deployed on a chain. */
|
|
14
|
+
export function isSupported(chainId) {
|
|
15
|
+
return !!getDeployment(chainId);
|
|
16
|
+
}
|
|
17
|
+
/** Returns whether a currency is the machine token for a supported chain. */
|
|
18
|
+
export function isMachineToken(parameters) {
|
|
19
|
+
const deployment = getDeployment(parameters.chainId);
|
|
20
|
+
return (!!deployment &&
|
|
21
|
+
isAddress(parameters.currency, { strict: false }) &&
|
|
22
|
+
isAddressEqual(parameters.currency, deployment.token));
|
|
23
|
+
}
|
|
24
|
+
/** Resolves the canonical first-party settlement route for a compatible charge. */
|
|
25
|
+
export function getRoute(parameters) {
|
|
26
|
+
const deployment = getDeployment(parameters.chainId);
|
|
27
|
+
const transfer = parameters.transfers.length === 1 ? parameters.transfers[0] : undefined;
|
|
28
|
+
if (!deployment || !transfer?.memo)
|
|
29
|
+
return undefined;
|
|
30
|
+
const amount = BigInt(transfer.amount);
|
|
31
|
+
const memo = transfer.memo;
|
|
32
|
+
return {
|
|
33
|
+
calls: [
|
|
34
|
+
{
|
|
35
|
+
data: encodeFunctionData({
|
|
36
|
+
abi: Abis.tip20,
|
|
37
|
+
functionName: 'approve',
|
|
38
|
+
args: [deployment.swap, amount],
|
|
39
|
+
}),
|
|
40
|
+
to: deployment.token,
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
data: encodeFunctionData({
|
|
44
|
+
abi: machineTokenSwapperAbi,
|
|
45
|
+
functionName: 'swapTo',
|
|
46
|
+
args: [deployment.token, amount, parameters.currency, transfer.recipient, memo],
|
|
47
|
+
}),
|
|
48
|
+
to: deployment.swap,
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
settlementSender: deployment.swap,
|
|
52
|
+
transfers: [{ amount, memo, recipient: transfer.recipient }],
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/** Builds and simulates the first-party settlement route. */
|
|
56
|
+
export async function findRoute(client, parameters) {
|
|
57
|
+
const route = getRoute(parameters);
|
|
58
|
+
if (!route)
|
|
59
|
+
return undefined;
|
|
60
|
+
try {
|
|
61
|
+
const balance = await readContract(client, Actions.token.getBalance.call(client, {
|
|
62
|
+
account: parameters.account,
|
|
63
|
+
token: route.calls[0].to,
|
|
64
|
+
}));
|
|
65
|
+
if (balance < route.transfers[0].amount)
|
|
66
|
+
return undefined;
|
|
67
|
+
await call(client, { account: parameters.account, calls: route.calls });
|
|
68
|
+
return route;
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/** Matches an exact first-party settlement route. */
|
|
75
|
+
export function matchRoute(parameters) {
|
|
76
|
+
const transfer = parameters.transfers.length === 1 ? parameters.transfers[0] : undefined;
|
|
77
|
+
const swap = parameters.calls[1];
|
|
78
|
+
if (!transfer || parameters.calls.length !== 2 || !swap?.data)
|
|
79
|
+
return undefined;
|
|
80
|
+
try {
|
|
81
|
+
const decoded = decodeFunctionData({ abi: machineTokenSwapperAbi, data: swap.data });
|
|
82
|
+
const route = getRoute({
|
|
83
|
+
...parameters,
|
|
84
|
+
transfers: [{ ...transfer, memo: transfer.memo ?? decoded.args[4] }],
|
|
85
|
+
});
|
|
86
|
+
if (!route)
|
|
87
|
+
return undefined;
|
|
88
|
+
if (parameters.calls.some((call, index) => {
|
|
89
|
+
const expected = route.calls[index];
|
|
90
|
+
return (!call.data ||
|
|
91
|
+
!call.to ||
|
|
92
|
+
!expected ||
|
|
93
|
+
(call.value ?? 0n) !== 0n ||
|
|
94
|
+
!isAddressEqual(call.to, expected.to) ||
|
|
95
|
+
call.data.toLowerCase() !== expected.data.toLowerCase());
|
|
96
|
+
}))
|
|
97
|
+
return undefined;
|
|
98
|
+
return route;
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/** Returns the trusted sender that settles the configured charge route on a chain. */
|
|
105
|
+
export function getSettlementSender(chainId) {
|
|
106
|
+
return getDeployment(chainId)?.swap;
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=machine-token-charge.js.map
|
|
@@ -45,6 +45,8 @@ export declare namespace charge {
|
|
|
45
45
|
} | undefined;
|
|
46
46
|
};
|
|
47
47
|
type Parameters = {
|
|
48
|
+
/** Enables first-party machine-token funding through the canonical swapper. */
|
|
49
|
+
machineTokenEnabled?: boolean | undefined;
|
|
48
50
|
/** Render payment page when Accept header is text/html (e.g. in browsers) */
|
|
49
51
|
html?: boolean | Html.Config | undefined;
|
|
50
52
|
/**
|
|
@@ -99,6 +101,7 @@ export declare namespace charge {
|
|
|
99
101
|
* relay broadcasts pull credentials, while it recognizes a push
|
|
100
102
|
* credential as already broadcast and returns its receipt without sending
|
|
101
103
|
* it again.
|
|
104
|
+
*
|
|
102
105
|
*/
|
|
103
106
|
relay?: RelayOptions | undefined;
|
|
104
107
|
/**
|
|
@@ -15,6 +15,7 @@ import * as Charge_internal from '../internal/charge.js';
|
|
|
15
15
|
import * as defaults from '../internal/defaults.js';
|
|
16
16
|
import * as FeePayer from '../internal/fee-payer.js';
|
|
17
17
|
import { resolveFeeToken } from '../internal/fee-token.js';
|
|
18
|
+
import * as MachineTokenCharge from '../internal/machine-token-charge.js';
|
|
18
19
|
import * as Proof from '../internal/proof.js';
|
|
19
20
|
import * as Selectors from '../internal/selectors.js';
|
|
20
21
|
import * as Methods from '../Methods.js';
|
|
@@ -32,7 +33,7 @@ import * as SponsorBudget from './SponsorBudget.js';
|
|
|
32
33
|
* ```
|
|
33
34
|
*/
|
|
34
35
|
export function charge(parameters = {}) {
|
|
35
|
-
const { amount, currency = defaults.resolveCurrency(parameters), decimals = defaults.decimals, description, externalId, feeToken: configuredFeeToken, feePayerPolicy, html,
|
|
36
|
+
const { machineTokenEnabled, amount, currency = defaults.resolveCurrency(parameters), decimals = defaults.decimals, description, externalId, feeToken: configuredFeeToken, feePayerPolicy, html, relay, sponsorBudget, splits, supportedModes, validateSender, waitForConfirmation = true, } = parameters;
|
|
36
37
|
const storeKeyPrefix = parameters.storeKeyPrefix ?? '';
|
|
37
38
|
const rawStore = (parameters.store ?? Store.memory());
|
|
38
39
|
const store = Store.from(rawStore, { keyPrefix: storeKeyPrefix });
|
|
@@ -83,36 +84,40 @@ export function charge(parameters = {}) {
|
|
|
83
84
|
const supportedModes = methodDetails?.supportedModes;
|
|
84
85
|
const currency = resolvedRequest.currency;
|
|
85
86
|
const recipient = resolvedRequest.recipient;
|
|
86
|
-
const
|
|
87
|
+
const machineTokenEnabled = methodDetails?.machineTokenEnabled === true;
|
|
88
|
+
const settlementSender = machineTokenEnabled
|
|
89
|
+
? MachineTokenCharge.getSettlementSender(chainId)
|
|
90
|
+
: undefined;
|
|
87
91
|
const isZeroAmount = BigInt(amount) === 0n;
|
|
88
92
|
Expires.assert(challenge.expires, challenge.id);
|
|
89
93
|
if (isZeroAmount && payload.type !== 'proof')
|
|
90
94
|
throw new MismatchError('Zero-amount challenges require a proof credential.', {});
|
|
91
95
|
return {
|
|
92
96
|
amount,
|
|
97
|
+
machineTokenEnabled,
|
|
93
98
|
chainId,
|
|
94
99
|
challenge,
|
|
95
100
|
client: await getClient({ chainId }),
|
|
96
101
|
currency,
|
|
97
102
|
isZeroAmount,
|
|
98
|
-
memo,
|
|
99
103
|
methodDetails,
|
|
100
104
|
payload,
|
|
101
105
|
recipient,
|
|
102
106
|
requestAllowsFeePayer: request.feePayer !== false,
|
|
103
107
|
resolvedRequest,
|
|
108
|
+
settlementSenders: settlementSender ? [settlementSender] : [],
|
|
104
109
|
supportedModes,
|
|
105
110
|
};
|
|
106
111
|
}
|
|
107
112
|
async function validateHashCredential(credential, payload, context) {
|
|
108
|
-
const { amount, chainId, challenge, client, currency,
|
|
113
|
+
const { amount, chainId, challenge, client, currency, methodDetails, recipient, settlementSenders, supportedModes, } = context;
|
|
109
114
|
if (supportedModes && !supportedModes.includes('push'))
|
|
110
115
|
throw new MismatchError('Hash credentials are not supported for this challenge.', {});
|
|
111
116
|
const source = parseHashCredentialSource({
|
|
112
117
|
chainId: chainId ?? client.chain?.id,
|
|
113
118
|
source: credential.source,
|
|
114
119
|
});
|
|
115
|
-
const transfers = getExpectedTransfers({ amount,
|
|
120
|
+
const transfers = getExpectedTransfers({ amount, methodDetails, recipient });
|
|
116
121
|
const receipt = await getTransactionReceipt(client, {
|
|
117
122
|
hash: payload.hash,
|
|
118
123
|
});
|
|
@@ -120,15 +125,15 @@ export function charge(parameters = {}) {
|
|
|
120
125
|
const matchedLogs = await assertTransferLogs(receipt, {
|
|
121
126
|
currency,
|
|
122
127
|
sender,
|
|
128
|
+
settlementSenders,
|
|
123
129
|
source,
|
|
124
130
|
transfers,
|
|
125
131
|
validateSender,
|
|
126
132
|
});
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
});
|
|
133
|
+
assertChallengeBoundMemo(matchedLogs, {
|
|
134
|
+
challengeId: challenge.id,
|
|
135
|
+
realm: challenge.realm,
|
|
136
|
+
});
|
|
132
137
|
return { receipt: toReceipt(receipt), sender, transfers };
|
|
133
138
|
}
|
|
134
139
|
async function validateProofCredential(credential, payload, context) {
|
|
@@ -169,7 +174,7 @@ export function charge(parameters = {}) {
|
|
|
169
174
|
return { sender: source.address };
|
|
170
175
|
}
|
|
171
176
|
async function validateTransactionCredential(credential, payload, request, context) {
|
|
172
|
-
const { amount, chainId, challenge, client, currency,
|
|
177
|
+
const { machineTokenEnabled, amount, chainId, challenge, client, currency, methodDetails, recipient, requestAllowsFeePayer, supportedModes, } = context;
|
|
173
178
|
if (supportedModes && !supportedModes.includes('pull'))
|
|
174
179
|
throw new MismatchError('Transaction credentials are not supported for this challenge.', {});
|
|
175
180
|
const serializedTransaction = payload.signature;
|
|
@@ -181,25 +186,40 @@ export function charge(parameters = {}) {
|
|
|
181
186
|
const isFeePayerTx = methodDetails?.feePayer === true &&
|
|
182
187
|
requestAllowsFeePayer &&
|
|
183
188
|
!!(Account.is(request.feePayer) ? request.feePayer : feePayer || remoteFeePayer);
|
|
184
|
-
const transfers = getExpectedTransfers({ amount,
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
189
|
+
const transfers = getExpectedTransfers({ amount, methodDetails, recipient });
|
|
190
|
+
const machineTokenRoute = machineTokenEnabled
|
|
191
|
+
? MachineTokenCharge.matchRoute({
|
|
192
|
+
calls: transaction.calls ?? [],
|
|
193
|
+
chainId: chainId ?? client.chain?.id,
|
|
194
|
+
currency,
|
|
195
|
+
transfers,
|
|
196
|
+
})
|
|
197
|
+
: undefined;
|
|
198
|
+
const matchedCalls = machineTokenRoute?.transfers ??
|
|
199
|
+
assertTransferCalls(transaction.calls ?? [], {
|
|
200
|
+
currency,
|
|
201
|
+
exactCount: isFeePayerTx,
|
|
202
|
+
transfers,
|
|
194
203
|
});
|
|
204
|
+
assertChallengeBoundCallMemo(matchedCalls, {
|
|
205
|
+
challengeId: challenge.id,
|
|
206
|
+
realm: challenge.realm,
|
|
207
|
+
});
|
|
195
208
|
if (isFeePayerTx) {
|
|
196
|
-
|
|
209
|
+
if (!machineTokenRoute)
|
|
210
|
+
FeePayer.validateCalls(transaction.calls, { amount, currency, recipient }, { currency, expectedTransfers: transfers });
|
|
197
211
|
FeePayer.assertAllowedFeeToken(transaction, FeePayer.defaultAllowedFeeTokens(chainId));
|
|
198
212
|
}
|
|
199
213
|
else {
|
|
200
214
|
await viem_call(client, FeePayer.simulationTransaction(transaction, { feePayer: false }));
|
|
201
215
|
}
|
|
202
|
-
return {
|
|
216
|
+
return {
|
|
217
|
+
isFeePayerTx,
|
|
218
|
+
serializedTransaction,
|
|
219
|
+
settlementSenders: machineTokenRoute ? [machineTokenRoute.settlementSender] : [],
|
|
220
|
+
transaction,
|
|
221
|
+
transfers,
|
|
222
|
+
};
|
|
203
223
|
}
|
|
204
224
|
async function validateCredential(credential, request, context) {
|
|
205
225
|
switch (context.payload.type) {
|
|
@@ -217,7 +237,7 @@ export function charge(parameters = {}) {
|
|
|
217
237
|
return { details: { mode: 'proof', sender }, type: 'proof' };
|
|
218
238
|
}
|
|
219
239
|
case 'transaction': {
|
|
220
|
-
const { isFeePayerTx, serializedTransaction, transaction, transfers } = await validateTransactionCredential(credential, context.payload, request, context);
|
|
240
|
+
const { isFeePayerTx, serializedTransaction, settlementSenders, transaction, transfers } = await validateTransactionCredential(credential, context.payload, request, context);
|
|
221
241
|
return {
|
|
222
242
|
details: {
|
|
223
243
|
mode: 'pull',
|
|
@@ -227,6 +247,7 @@ export function charge(parameters = {}) {
|
|
|
227
247
|
},
|
|
228
248
|
isFeePayerTx,
|
|
229
249
|
serializedTransaction,
|
|
250
|
+
settlementSenders,
|
|
230
251
|
transaction,
|
|
231
252
|
transfers,
|
|
232
253
|
type: 'transaction',
|
|
@@ -238,12 +259,12 @@ export function charge(parameters = {}) {
|
|
|
238
259
|
canOffer: parameters.canOffer,
|
|
239
260
|
onPaymentSuccess: parameters.onPaymentSuccess,
|
|
240
261
|
defaults: {
|
|
262
|
+
machineTokenEnabled,
|
|
241
263
|
amount,
|
|
242
264
|
currency,
|
|
243
265
|
decimals,
|
|
244
266
|
description,
|
|
245
267
|
externalId,
|
|
246
|
-
memo,
|
|
247
268
|
recipient,
|
|
248
269
|
splits,
|
|
249
270
|
supportedModes,
|
|
@@ -298,6 +319,13 @@ export function charge(parameters = {}) {
|
|
|
298
319
|
})();
|
|
299
320
|
if (client.chain?.id !== chainId)
|
|
300
321
|
throw new Error(`Client not configured with chainId ${chainId}.`);
|
|
322
|
+
if (MachineTokenCharge.isMachineToken({
|
|
323
|
+
chainId,
|
|
324
|
+
currency: request.currency,
|
|
325
|
+
}))
|
|
326
|
+
throw new Error('MACH cannot be advertised as a charge currency. Advertise the settlement currency with `machineTokenEnabled: true`.');
|
|
327
|
+
if (request.machineTokenEnabled && !MachineTokenCharge.isSupported(chainId))
|
|
328
|
+
throw new Error(`Machine tokens are not supported on chainId ${chainId}.`);
|
|
301
329
|
const resolvedFeePayer = (() => {
|
|
302
330
|
if (request.feePayer === false)
|
|
303
331
|
return credential ? false : undefined;
|
|
@@ -308,9 +336,9 @@ export function charge(parameters = {}) {
|
|
|
308
336
|
})();
|
|
309
337
|
return {
|
|
310
338
|
...request,
|
|
339
|
+
machineTokenEnabled: request.machineTokenEnabled || undefined,
|
|
311
340
|
chainId,
|
|
312
341
|
feePayer: resolvedFeePayer,
|
|
313
|
-
memo: request.memo || undefined,
|
|
314
342
|
};
|
|
315
343
|
},
|
|
316
344
|
async validate({ credential, request }) {
|
|
@@ -328,7 +356,7 @@ export function charge(parameters = {}) {
|
|
|
328
356
|
},
|
|
329
357
|
async broadcast({ credential, request }) {
|
|
330
358
|
const context = await resolveCredentialContext({ credential, request });
|
|
331
|
-
const { amount, chainId, challenge, client, currency,
|
|
359
|
+
const { amount, chainId, challenge, client, currency, methodDetails, recipient, requestAllowsFeePayer, } = context;
|
|
332
360
|
const validated = await validateCredential(credential, request, context);
|
|
333
361
|
const feePayerAccount = methodDetails?.feePayer === true && requestAllowsFeePayer
|
|
334
362
|
? Account.is(request.feePayer)
|
|
@@ -362,7 +390,7 @@ export function charge(parameters = {}) {
|
|
|
362
390
|
};
|
|
363
391
|
}
|
|
364
392
|
case 'transaction': {
|
|
365
|
-
const { isFeePayerTx, serializedTransaction, transaction, transfers } = validated;
|
|
393
|
+
const { isFeePayerTx, serializedTransaction, settlementSenders, transaction, transfers } = validated;
|
|
366
394
|
// Pre-broadcast dedup: catch exact byte-for-byte replays early.
|
|
367
395
|
const hash = keccak256(serializedTransaction);
|
|
368
396
|
if (!(await tryClaimHash(store, hash, replayExpires))) {
|
|
@@ -495,13 +523,13 @@ export function charge(parameters = {}) {
|
|
|
495
523
|
const matchedLogs = await assertTransferLogs(receipt, {
|
|
496
524
|
currency,
|
|
497
525
|
sender: transaction.from,
|
|
526
|
+
settlementSenders,
|
|
498
527
|
transfers,
|
|
499
528
|
});
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
});
|
|
529
|
+
assertChallengeBoundMemo(matchedLogs, {
|
|
530
|
+
challengeId: challenge.id,
|
|
531
|
+
realm: challenge.realm,
|
|
532
|
+
});
|
|
505
533
|
if (receipt.transactionHash.toLowerCase() !== finalHash.toLowerCase())
|
|
506
534
|
throw new VerificationFailedError({
|
|
507
535
|
reason: 'Broadcast transaction hash does not match the signed transaction',
|
|
@@ -550,16 +578,16 @@ function chargeBinding(request) {
|
|
|
550
578
|
// Exhaustively destructure so new charge request fields require an explicit binding decision.
|
|
551
579
|
const { amount, currency, description, externalId, methodDetails, recipient, ...requestRest } = request;
|
|
552
580
|
requestRest;
|
|
553
|
-
const { chainId, feePayer,
|
|
581
|
+
const { chainId, feePayer, machineTokenEnabled, splits, supportedModes, ...methodDetailsRest } = methodDetails ?? {};
|
|
554
582
|
methodDetailsRest;
|
|
555
583
|
void feePayer;
|
|
556
584
|
return {
|
|
585
|
+
machineTokenEnabled,
|
|
557
586
|
amount,
|
|
558
587
|
chainId,
|
|
559
588
|
currency,
|
|
560
589
|
description,
|
|
561
590
|
externalId,
|
|
562
|
-
memo,
|
|
563
591
|
recipient,
|
|
564
592
|
splits,
|
|
565
593
|
supportedModes,
|
|
@@ -569,7 +597,6 @@ function getExpectedTransfers(parameters) {
|
|
|
569
597
|
return Charge_internal.getTransfers({
|
|
570
598
|
amount: parameters.amount,
|
|
571
599
|
methodDetails: {
|
|
572
|
-
memo: parameters.memo,
|
|
573
600
|
splits: parameters.methodDetails?.splits,
|
|
574
601
|
},
|
|
575
602
|
recipient: parameters.recipient,
|
|
@@ -695,7 +722,9 @@ async function assertTransferLogs(receipt, parameters) {
|
|
|
695
722
|
if (!(await isValidTransferSender({
|
|
696
723
|
expectedSender: parameters.sender,
|
|
697
724
|
sender: log.args.from,
|
|
725
|
+
settlementSenders: parameters.settlementSenders,
|
|
698
726
|
source: parameters.source,
|
|
727
|
+
transactionSender: receipt.from,
|
|
699
728
|
validateSender: parameters.validateSender,
|
|
700
729
|
})))
|
|
701
730
|
continue;
|
|
@@ -770,6 +799,9 @@ function isSameTransferLog(a, b) {
|
|
|
770
799
|
async function isValidTransferSender(parameters) {
|
|
771
800
|
if (TempoAddress.isEqual(parameters.sender, parameters.expectedSender))
|
|
772
801
|
return true;
|
|
802
|
+
if (TempoAddress.isEqual(parameters.transactionSender, parameters.expectedSender) &&
|
|
803
|
+
parameters.settlementSenders?.some((sender) => TempoAddress.isEqual(parameters.sender, sender)))
|
|
804
|
+
return true;
|
|
773
805
|
if (!parameters.validateSender)
|
|
774
806
|
return false;
|
|
775
807
|
return parameters.validateSender({
|
|
@@ -37,6 +37,7 @@ export declare function tempo<const parameters extends tempo.Parameters>(paramet
|
|
|
37
37
|
}, import("zod/v4/core").$strip>], "type">;
|
|
38
38
|
};
|
|
39
39
|
request: import("zod/mini").ZodMiniPipe<import("zod/mini").ZodMiniObject<{
|
|
40
|
+
machineTokenEnabled: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniBoolean<boolean>>;
|
|
40
41
|
amount: import("zod/mini").ZodMiniString<string>;
|
|
41
42
|
chainId: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniNumber<number>>;
|
|
42
43
|
currency: import("zod/mini").ZodMiniString<string>;
|
|
@@ -44,7 +45,7 @@ export declare function tempo<const parameters extends tempo.Parameters>(paramet
|
|
|
44
45
|
description: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
45
46
|
externalId: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
46
47
|
feePayer: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniPipe<import("zod/mini").ZodMiniUnion<readonly [import("zod/mini").ZodMiniBoolean<boolean>, import("zod/mini").ZodMiniCustom<import("viem").Account, import("viem").Account>]>, import("zod/mini").ZodMiniTransform<boolean, boolean | import("viem").Account>>>;
|
|
47
|
-
memo: import("zod/mini").ZodMiniOptional<import("zod/mini").
|
|
48
|
+
memo: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniNever>;
|
|
48
49
|
recipient: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
49
50
|
splits: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniArray<import("zod/mini").ZodMiniObject<{
|
|
50
51
|
amount: import("zod/mini").ZodMiniString<string>;
|
|
@@ -62,9 +63,9 @@ export declare function tempo<const parameters extends tempo.Parameters>(paramet
|
|
|
62
63
|
recipient?: string | undefined;
|
|
63
64
|
amount: string;
|
|
64
65
|
methodDetails?: {
|
|
66
|
+
machineTokenEnabled?: boolean | undefined;
|
|
65
67
|
chainId?: number | undefined;
|
|
66
68
|
feePayer?: boolean | undefined;
|
|
67
|
-
memo?: string | undefined;
|
|
68
69
|
splits?: {
|
|
69
70
|
memo?: string | undefined;
|
|
70
71
|
recipient: `0x${string}`;
|
|
@@ -73,6 +74,7 @@ export declare function tempo<const parameters extends tempo.Parameters>(paramet
|
|
|
73
74
|
supportedModes?: ("pull" | "push")[] | undefined;
|
|
74
75
|
} | undefined;
|
|
75
76
|
}, {
|
|
77
|
+
machineTokenEnabled?: boolean | undefined;
|
|
76
78
|
amount: string;
|
|
77
79
|
chainId?: number | undefined;
|
|
78
80
|
currency: string;
|
|
@@ -80,7 +82,7 @@ export declare function tempo<const parameters extends tempo.Parameters>(paramet
|
|
|
80
82
|
description?: string | undefined;
|
|
81
83
|
externalId?: string | undefined;
|
|
82
84
|
feePayer?: boolean | undefined;
|
|
83
|
-
memo?:
|
|
85
|
+
memo?: undefined;
|
|
84
86
|
recipient?: string | undefined;
|
|
85
87
|
splits?: {
|
|
86
88
|
amount: string;
|
|
@@ -181,7 +183,10 @@ export declare function tempo<const parameters extends tempo.Parameters>(paramet
|
|
|
181
183
|
};
|
|
182
184
|
}, session_.DeriveDefaults<parameters>, parameters["sse"] extends false | undefined ? undefined : import("./internal/transport.js").Sse, session_.Extensions, undefined>];
|
|
183
185
|
export declare namespace tempo {
|
|
184
|
-
type Parameters = charge_.Parameters & session_.Parameters
|
|
186
|
+
type Parameters = Omit<charge_.Parameters, 'machineTokenEnabled'> & session_.Parameters & {
|
|
187
|
+
/** Enables MACH funding for compatible Tempo methods. Currently applies to `charge`. */
|
|
188
|
+
machineTokenEnabled?: boolean | undefined;
|
|
189
|
+
};
|
|
185
190
|
/** Tempo API relay configuration for server-side charges. */
|
|
186
191
|
type RelayOptions = charge_.RelayOptions;
|
|
187
192
|
/** Stable failure codes returned by Tempo API's MPP relay. */
|
|
@@ -14,8 +14,11 @@ function createChargeMethod(parameters) {
|
|
|
14
14
|
return tempo.charge(parameters);
|
|
15
15
|
}
|
|
16
16
|
function createSessionMethod(parameters) {
|
|
17
|
-
//
|
|
18
|
-
|
|
17
|
+
// Machine-token funding is configured at the Tempo family level, but only
|
|
18
|
+
// charge implements it today. Keep it out of session configuration until
|
|
19
|
+
// session routing has its own complete lifecycle support.
|
|
20
|
+
const { machineTokenEnabled: _machineTokenEnabled, ...sessionParameters } = parameters ?? {};
|
|
21
|
+
return sessionServer(sessionParameters);
|
|
19
22
|
}
|
|
20
23
|
/**
|
|
21
24
|
* Creates the common Tempo `charge` and `session` methods from shared parameters.
|