@parallel-protocol/cli 0.2.11 → 0.3.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/README.md +6 -0
- package/dist/index.js +181 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -339,12 +339,17 @@ parallel pay prepare \
|
|
|
339
339
|
|
|
340
340
|
# Force which token the agent pays with
|
|
341
341
|
parallel pay prepare --amount <n> --recipient <address> --chain <chain> --pay-with <usdp|usdc|susdp>
|
|
342
|
+
|
|
343
|
+
# Metered payment (scheme upto): sign --amount as a cap, the merchant charges its actual cost
|
|
344
|
+
parallel pay prepare --amount <cap> --recipient <address> --chain <chain> --scheme upto --valid-for 600 --wallet
|
|
342
345
|
```
|
|
343
346
|
|
|
344
347
|
**`--token`** controls what the **merchant receives** (default: `usdp`). Accepted values: `usdp` · `usdc` · `susdp` · any backing collateral symbol (`susds`, `sfrxusd`, …) · a token contract address.
|
|
345
348
|
|
|
346
349
|
**`--pay-with`** overrides which token the **agent pays with** (default: auto-detected from wallet balance). Accepted values: `usdp` · `usdc` · `susdp`.
|
|
347
350
|
|
|
351
|
+
**`--scheme`** selects the payment scheme (default: `exact`). With `upto`, `--amount` is a **cap**: the authorization is signed for the cap towards the `UptoEscrow` contract, the merchant settles the amount it actually charges and the escrow refunds the rest to the agent in the same transaction (Route U). Metered payments are USDp only — `--token` and `--pay-with` other than `usdp` are refused. **`--valid-for <seconds>`** (upto only, 1–3600, default 300) sets how long the authorization stays valid; pass the `maxTimeoutSeconds` the merchant announced in its 402. Available on chains where the escrow is deployed (see `parallel pay capabilities`).
|
|
352
|
+
|
|
348
353
|
**Auto-route selection** (when `--wallet` is provided):
|
|
349
354
|
|
|
350
355
|
| `--token` (merchant receives) | Agent wallet | Route | Method |
|
|
@@ -359,6 +364,7 @@ parallel pay prepare --amount <n> --recipient <address> --chain <chain> --pay-wi
|
|
|
359
364
|
| `susdp` | sUSDp | J | `transferWithAuthorization` |
|
|
360
365
|
| collateral (e.g. `susds`) | USDp | D | `swapExactOutputWithAuthorization` |
|
|
361
366
|
| collateral (e.g. `susds`) | sUSDp | K | `redeemWithAuthorization` → collateral |
|
|
367
|
+
| `usdp` with `--scheme upto` | USDp ≥ cap | U | `uptoWithAuthorization` (cap signed, actual charged, rest refunded) |
|
|
362
368
|
|
|
363
369
|
Without `--wallet`, the payload is unsigned and `tokenIn` defaults to `usdp`.
|
|
364
370
|
|
package/dist/index.js
CHANGED
|
@@ -1635,7 +1635,7 @@ function suppressLogs() {
|
|
|
1635
1635
|
|
|
1636
1636
|
// package.json
|
|
1637
1637
|
var package_default = {
|
|
1638
|
-
version: "0.
|
|
1638
|
+
version: "0.3.0"};
|
|
1639
1639
|
function findEnvFile(dir) {
|
|
1640
1640
|
const candidate = join(dir, ".env");
|
|
1641
1641
|
if (existsSync(candidate)) return candidate;
|
|
@@ -1730,7 +1730,8 @@ var CHAINS_STATIC = {
|
|
|
1730
1730
|
susdp: "0x472eD57b376fE400259FB28e5C46eB53f0E3e7E7",
|
|
1731
1731
|
bridgeableUsdp: "0x4Dde0e308CFB60515218C6ad2DF1134Fc48531FC",
|
|
1732
1732
|
flashParallel: "0x08417cdb7F52a5021bB4eb6E0deAf3f295c3f182",
|
|
1733
|
-
harvester: "0xCa43eCFCDFBA1fED003649e946Ae6091646B410a"
|
|
1733
|
+
harvester: "0xCa43eCFCDFBA1fED003649e946Ae6091646B410a",
|
|
1734
|
+
uptoEscrow: "0xcCA03ed95Ab203a8566a8abfFDF03AEE4030B974"
|
|
1734
1735
|
}
|
|
1735
1736
|
},
|
|
1736
1737
|
arbitrum: {
|
|
@@ -1994,6 +1995,9 @@ var PARALLELIZER_CHAINS = CHAIN_NAMES.filter(
|
|
|
1994
1995
|
var SAVINGS_CHAINS = CHAIN_NAMES.filter(
|
|
1995
1996
|
(c) => !!CHAINS_STATIC[c].contracts.susdp
|
|
1996
1997
|
);
|
|
1998
|
+
CHAIN_NAMES.filter(
|
|
1999
|
+
(c) => !!CHAINS_STATIC[c].contracts.uptoEscrow
|
|
2000
|
+
);
|
|
1997
2001
|
function isParallelizerChain(chainName) {
|
|
1998
2002
|
return !!CHAINS_STATIC[chainName]?.contracts.parallelizer;
|
|
1999
2003
|
}
|
|
@@ -2006,6 +2010,9 @@ function getParallelizerAddress(chainName) {
|
|
|
2006
2010
|
function getSUSDpAddress(chainName) {
|
|
2007
2011
|
return CHAINS_STATIC[chainName]?.contracts.susdp;
|
|
2008
2012
|
}
|
|
2013
|
+
function getUptoEscrowAddress(chainName) {
|
|
2014
|
+
return CHAINS_STATIC[chainName]?.contracts.uptoEscrow;
|
|
2015
|
+
}
|
|
2009
2016
|
var USDC_CHAIN_ADDRESSES = {
|
|
2010
2017
|
ethereum: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
2011
2018
|
base: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
@@ -5239,6 +5246,35 @@ var SWAP_EXACT_INPUT_TYPEHASH = keccak256(
|
|
|
5239
5246
|
"SwapExactInputWithAuthorization(address from,address tokenIn,address tokenOut,uint256 amountIn,uint256 amountOutMin,address to,uint256 deadline,bytes32 userSalt)"
|
|
5240
5247
|
)
|
|
5241
5248
|
);
|
|
5249
|
+
var UPTO_WITH_AUTHORIZATION_TYPEHASH = keccak256(
|
|
5250
|
+
toHex(
|
|
5251
|
+
"UptoWithAuthorization(address from,address token,uint256 maxAmount,address payTo,uint256 validBefore,bytes32 userSalt)"
|
|
5252
|
+
)
|
|
5253
|
+
);
|
|
5254
|
+
function deriveUptoNonce(intent) {
|
|
5255
|
+
return keccak256(
|
|
5256
|
+
encodeAbiParameters(
|
|
5257
|
+
[
|
|
5258
|
+
{ type: "bytes32" },
|
|
5259
|
+
{ type: "address" },
|
|
5260
|
+
{ type: "address" },
|
|
5261
|
+
{ type: "uint256" },
|
|
5262
|
+
{ type: "address" },
|
|
5263
|
+
{ type: "uint256" },
|
|
5264
|
+
{ type: "bytes32" }
|
|
5265
|
+
],
|
|
5266
|
+
[
|
|
5267
|
+
UPTO_WITH_AUTHORIZATION_TYPEHASH,
|
|
5268
|
+
intent.from,
|
|
5269
|
+
intent.token,
|
|
5270
|
+
intent.maxAmount,
|
|
5271
|
+
intent.payTo,
|
|
5272
|
+
intent.validBefore,
|
|
5273
|
+
intent.userSalt
|
|
5274
|
+
]
|
|
5275
|
+
)
|
|
5276
|
+
);
|
|
5277
|
+
}
|
|
5242
5278
|
var REDEEM_WITH_AUTHORIZATION_TYPES = {
|
|
5243
5279
|
RedeemWithAuthorization: [
|
|
5244
5280
|
{ name: "vault", type: "address" },
|
|
@@ -5297,10 +5333,15 @@ var hexBytes = (bytes) => z.string().refine(
|
|
|
5297
5333
|
var Bytes32Schema = hexBytes(32);
|
|
5298
5334
|
var SignatureSchema = hexBytes(65);
|
|
5299
5335
|
z.object({
|
|
5336
|
+
// Absent means "exact" — the historical wire predates this field.
|
|
5337
|
+
scheme: z.enum(["exact", "upto"]).optional(),
|
|
5300
5338
|
maxAmountRequired: z.string(),
|
|
5301
5339
|
asset: AddressSchema,
|
|
5302
5340
|
payTo: AddressSchema,
|
|
5303
|
-
network: z.string()
|
|
5341
|
+
network: z.string(),
|
|
5342
|
+
// Settlement window (seconds) the merchant grants; drives the payer's
|
|
5343
|
+
// `validBefore`. Absent → the payer's default window applies.
|
|
5344
|
+
maxTimeoutSeconds: z.number().int().positive().optional()
|
|
5304
5345
|
});
|
|
5305
5346
|
var AuthorizationSchema = z.object({
|
|
5306
5347
|
from: AddressSchema,
|
|
@@ -5324,6 +5365,9 @@ var DepositAuthorizationSchema = z.object({
|
|
|
5324
5365
|
validBefore: z.string(),
|
|
5325
5366
|
nonce: Bytes32Schema
|
|
5326
5367
|
});
|
|
5368
|
+
var UptoParamsSchema = z.object({
|
|
5369
|
+
payTo: AddressSchema
|
|
5370
|
+
});
|
|
5327
5371
|
var SwapParamsSchema = z.object({
|
|
5328
5372
|
tokenOut: AddressSchema,
|
|
5329
5373
|
// swapExactOutput (Routes B/D/I/K): exact collateral out + max USDp in
|
|
@@ -5335,7 +5379,7 @@ var SwapParamsSchema = z.object({
|
|
|
5335
5379
|
recipient: AddressSchema
|
|
5336
5380
|
});
|
|
5337
5381
|
z.object({
|
|
5338
|
-
scheme: z.
|
|
5382
|
+
scheme: z.enum(["exact", "upto"]),
|
|
5339
5383
|
network: z.string(),
|
|
5340
5384
|
method: z.enum([
|
|
5341
5385
|
"transferWithAuthorization",
|
|
@@ -5344,7 +5388,11 @@ z.object({
|
|
|
5344
5388
|
"depositWithAuthorization",
|
|
5345
5389
|
"redeemWithAuthorization",
|
|
5346
5390
|
// Route L: agent holds USDp + sUSDp, neither sufficient alone.
|
|
5347
|
-
"partialRedeemWithAuthorization"
|
|
5391
|
+
"partialRedeemWithAuthorization",
|
|
5392
|
+
// Scheme upto: ReceiveWithAuthorization(MAX) signed to the UptoEscrow;
|
|
5393
|
+
// the facilitator settles the merchant-decided actual ≤ MAX and the
|
|
5394
|
+
// escrow refunds the remainder atomically.
|
|
5395
|
+
"uptoWithAuthorization"
|
|
5348
5396
|
]),
|
|
5349
5397
|
// Disambiguates routes that share a method but operate on different tokens
|
|
5350
5398
|
// (e.g. transferWithAuthorization: USDp → A, USDC → F, sUSDp → J). Optional —
|
|
@@ -5363,7 +5411,8 @@ z.object({
|
|
|
5363
5411
|
// Route G: DepositWithAuthorization signature on the sUSDp domain.
|
|
5364
5412
|
depositSignature: SignatureSchema.optional()
|
|
5365
5413
|
}),
|
|
5366
|
-
swapParams: SwapParamsSchema.optional()
|
|
5414
|
+
swapParams: SwapParamsSchema.optional(),
|
|
5415
|
+
uptoParams: UptoParamsSchema.optional()
|
|
5367
5416
|
});
|
|
5368
5417
|
var AMOUNT_RE = /^\d{1,30}(\.\d{1,18})?$/;
|
|
5369
5418
|
var isPlainPositiveDecimal = (v) => AMOUNT_RE.test(v) && parseFloat(v) > 0;
|
|
@@ -8578,9 +8627,9 @@ var PREVIEW_MINT_ABI = [
|
|
|
8578
8627
|
outputs: [{ type: "uint256" }]
|
|
8579
8628
|
}
|
|
8580
8629
|
];
|
|
8581
|
-
function buildTiming() {
|
|
8630
|
+
function buildTiming(validitySeconds = 300n) {
|
|
8582
8631
|
const now = BigInt(Math.floor(Date.now() / 1e3));
|
|
8583
|
-
return { validAfter: now - 60n, validBefore: now +
|
|
8632
|
+
return { validAfter: now - 60n, validBefore: now + validitySeconds };
|
|
8584
8633
|
}
|
|
8585
8634
|
function slippageUp(amount) {
|
|
8586
8635
|
return amount + amount * SLIPPAGE_BPS / 10000n;
|
|
@@ -8788,6 +8837,77 @@ async function prepareRouteA(chain, senderAddress, amount, recipient) {
|
|
|
8788
8837
|
gasEstimate: 65e3
|
|
8789
8838
|
};
|
|
8790
8839
|
}
|
|
8840
|
+
var UPTO_MAX_VALIDITY_SECONDS = 3600;
|
|
8841
|
+
async function prepareRouteUpto(chain, senderAddress, maxAmount, recipient, validitySeconds = 300) {
|
|
8842
|
+
const usdpAddress = getUSDpAddress(chain);
|
|
8843
|
+
if (!usdpAddress) throw new Error(`USDp not deployed on ${chain}`);
|
|
8844
|
+
const escrow = getUptoEscrowAddress(chain);
|
|
8845
|
+
if (!escrow) {
|
|
8846
|
+
throw new Error(
|
|
8847
|
+
`[PAYMENT_METHOD_NOT_SUPPORTED] Metered payments (upto) are not available on ${chain}`
|
|
8848
|
+
);
|
|
8849
|
+
}
|
|
8850
|
+
const maxAmountWei = parseUnits(maxAmount, 18);
|
|
8851
|
+
const window2 = BigInt(
|
|
8852
|
+
Math.min(
|
|
8853
|
+
Math.max(1, Math.trunc(validitySeconds)),
|
|
8854
|
+
UPTO_MAX_VALIDITY_SECONDS
|
|
8855
|
+
)
|
|
8856
|
+
);
|
|
8857
|
+
const { validAfter, validBefore } = buildTiming(window2);
|
|
8858
|
+
const userSalt = generateNonce();
|
|
8859
|
+
const derived = deriveUptoNonce({
|
|
8860
|
+
from: senderAddress,
|
|
8861
|
+
token: usdpAddress,
|
|
8862
|
+
maxAmount: maxAmountWei,
|
|
8863
|
+
payTo: recipient,
|
|
8864
|
+
validBefore,
|
|
8865
|
+
userSalt
|
|
8866
|
+
});
|
|
8867
|
+
const authMsg = {
|
|
8868
|
+
from: senderAddress,
|
|
8869
|
+
to: escrow,
|
|
8870
|
+
value: maxAmountWei,
|
|
8871
|
+
validAfter,
|
|
8872
|
+
validBefore,
|
|
8873
|
+
nonce: derived
|
|
8874
|
+
};
|
|
8875
|
+
const domain = await readTokenDomain(chain, usdpAddress);
|
|
8876
|
+
const typedData = buildReceiveAuthorizationTypedData(domain, authMsg);
|
|
8877
|
+
const messageHash = hashTypedData({
|
|
8878
|
+
...typedData,
|
|
8879
|
+
message: typedData.message
|
|
8880
|
+
});
|
|
8881
|
+
return {
|
|
8882
|
+
route: "U",
|
|
8883
|
+
steps: [
|
|
8884
|
+
{
|
|
8885
|
+
label: "signature",
|
|
8886
|
+
description: "ReceiveWithAuthorization on USDp domain to the UptoEscrow (derived nonce binds payTo and cap)",
|
|
8887
|
+
messageHash,
|
|
8888
|
+
typedData
|
|
8889
|
+
}
|
|
8890
|
+
],
|
|
8891
|
+
paymentPayload: {
|
|
8892
|
+
scheme: "upto",
|
|
8893
|
+
network: chain,
|
|
8894
|
+
method: "uptoWithAuthorization",
|
|
8895
|
+
payload: {
|
|
8896
|
+
authorization: {
|
|
8897
|
+
from: senderAddress,
|
|
8898
|
+
to: escrow,
|
|
8899
|
+
value: maxAmountWei.toString(),
|
|
8900
|
+
validAfter: validAfter.toString(),
|
|
8901
|
+
validBefore: validBefore.toString(),
|
|
8902
|
+
nonce: userSalt
|
|
8903
|
+
}
|
|
8904
|
+
},
|
|
8905
|
+
uptoParams: { payTo: recipient }
|
|
8906
|
+
},
|
|
8907
|
+
typedData,
|
|
8908
|
+
gasEstimate: 13e4
|
|
8909
|
+
};
|
|
8910
|
+
}
|
|
8791
8911
|
async function prepareRouteB(chain, senderAddress, amount, recipient, targetToken) {
|
|
8792
8912
|
if (!isParallelizerChain(chain)) {
|
|
8793
8913
|
throw new Error(
|
|
@@ -9714,6 +9834,22 @@ async function preparePayment(params) {
|
|
|
9714
9834
|
const sender = params.senderAddress;
|
|
9715
9835
|
const recv = params.recipient;
|
|
9716
9836
|
const tokenIn = params.tokenIn ?? "usdp";
|
|
9837
|
+
if (params.scheme === "upto") {
|
|
9838
|
+
const usdp = getUSDpAddress(chain)?.toLowerCase();
|
|
9839
|
+
const wantsOtherToken = params.targetToken !== void 0 && params.targetToken.toLowerCase() !== usdp;
|
|
9840
|
+
if (tokenIn !== "usdp" || wantsOtherToken) {
|
|
9841
|
+
throw new Error(
|
|
9842
|
+
"[PAYMENT_METHOD_NOT_SUPPORTED] Metered payments (upto) are USDp \u2192 USDp only"
|
|
9843
|
+
);
|
|
9844
|
+
}
|
|
9845
|
+
return prepareRouteUpto(
|
|
9846
|
+
chain,
|
|
9847
|
+
sender,
|
|
9848
|
+
amount,
|
|
9849
|
+
recv,
|
|
9850
|
+
params.validitySeconds
|
|
9851
|
+
);
|
|
9852
|
+
}
|
|
9717
9853
|
const usdpAddress = getUSDpAddress(chain);
|
|
9718
9854
|
const target = params.targetToken ?? usdpAddress;
|
|
9719
9855
|
const wantsUSDp = !target || !!usdpAddress && target.toLowerCase() === usdpAddress.toLowerCase();
|
|
@@ -10007,6 +10143,13 @@ function prepareCmd() {
|
|
|
10007
10143
|
).option(
|
|
10008
10144
|
"--pay-with <usdp|usdc|susdp>",
|
|
10009
10145
|
"force the input token (overrides balance-based auto-selection)"
|
|
10146
|
+
).option(
|
|
10147
|
+
"--scheme <exact|upto>",
|
|
10148
|
+
"payment scheme: 'upto' signs --amount as a cap settled through the UptoEscrow (USDp only)",
|
|
10149
|
+
"exact"
|
|
10150
|
+
).option(
|
|
10151
|
+
"--valid-for <seconds>",
|
|
10152
|
+
"upto only: settlement window announced by the merchant (max 3600)"
|
|
10010
10153
|
).option(
|
|
10011
10154
|
"-w, --wallet [path|env]",
|
|
10012
10155
|
"keystore path or 'env' (PARALLEL_PRIVATE_KEY) \u2014 signs and encodes the payload"
|
|
@@ -10029,6 +10172,26 @@ function prepareCmd() {
|
|
|
10029
10172
|
`Invalid --pay-with value "${opts.payWith}". Must be one of: usdp, usdc, susdp`
|
|
10030
10173
|
);
|
|
10031
10174
|
}
|
|
10175
|
+
if (opts.scheme !== "exact" && opts.scheme !== "upto") {
|
|
10176
|
+
throw new Error(
|
|
10177
|
+
`Invalid --scheme value "${opts.scheme}". Must be one of: exact, upto`
|
|
10178
|
+
);
|
|
10179
|
+
}
|
|
10180
|
+
const isUpto = opts.scheme === "upto";
|
|
10181
|
+
if (isUpto && opts.token.toLowerCase() !== "usdp") {
|
|
10182
|
+
throw new Error(
|
|
10183
|
+
"--scheme upto pays USDp merchants only: use --token usdp"
|
|
10184
|
+
);
|
|
10185
|
+
}
|
|
10186
|
+
if (isUpto && opts.payWith && opts.payWith !== "usdp") {
|
|
10187
|
+
throw new Error(
|
|
10188
|
+
"--scheme upto is paid in USDp: use --pay-with usdp or omit it"
|
|
10189
|
+
);
|
|
10190
|
+
}
|
|
10191
|
+
const validitySeconds = opts.validFor !== void 0 ? assertValidInteger(opts.validFor, "--valid-for", 1, 3600) : void 0;
|
|
10192
|
+
if (!isUpto && validitySeconds !== void 0) {
|
|
10193
|
+
throw new Error("--valid-for only applies to --scheme upto");
|
|
10194
|
+
}
|
|
10032
10195
|
const account = await loadWallet(walletFlag);
|
|
10033
10196
|
const senderAddress = account?.address;
|
|
10034
10197
|
if (!senderAddress && walletFlag) {
|
|
@@ -10064,6 +10227,7 @@ function prepareCmd() {
|
|
|
10064
10227
|
tokenIn = hasRouteB ? "usdp" : "usdc";
|
|
10065
10228
|
}
|
|
10066
10229
|
}
|
|
10230
|
+
if (isUpto) tokenIn = "usdp";
|
|
10067
10231
|
if (senderAddress) {
|
|
10068
10232
|
if (tokenIn === "usdp") {
|
|
10069
10233
|
await assertSufficientBalance(
|
|
@@ -10097,7 +10261,9 @@ function prepareCmd() {
|
|
|
10097
10261
|
amount: opts.amount,
|
|
10098
10262
|
recipient: opts.recipient,
|
|
10099
10263
|
tokenIn,
|
|
10100
|
-
targetToken
|
|
10264
|
+
targetToken,
|
|
10265
|
+
scheme: opts.scheme,
|
|
10266
|
+
validitySeconds
|
|
10101
10267
|
});
|
|
10102
10268
|
if (account) {
|
|
10103
10269
|
const { signedPayload, encoded } = await signPayload(
|
|
@@ -10117,8 +10283,12 @@ function prepareCmd() {
|
|
|
10117
10283
|
[
|
|
10118
10284
|
["Route", result.route],
|
|
10119
10285
|
["Method", result.paymentPayload.method],
|
|
10286
|
+
["Scheme", opts.scheme],
|
|
10120
10287
|
["Chain", chain],
|
|
10121
|
-
[
|
|
10288
|
+
[
|
|
10289
|
+
isUpto ? "Max Amount" : "Amount",
|
|
10290
|
+
`${opts.amount} ${tokenLabel2(opts.token)}`
|
|
10291
|
+
],
|
|
10122
10292
|
["Recipient", opts.recipient],
|
|
10123
10293
|
["Signatures", result.steps.map((s) => s.label).join(", ")],
|
|
10124
10294
|
["Gas Estimate", result.gasEstimate.toLocaleString()]
|
|
@@ -15347,7 +15517,7 @@ function swapCommand() {
|
|
|
15347
15517
|
|
|
15348
15518
|
// ../mcp-server/package.json
|
|
15349
15519
|
var package_default2 = {
|
|
15350
|
-
version: "1.
|
|
15520
|
+
version: "1.3.0"};
|
|
15351
15521
|
|
|
15352
15522
|
// src/commands/version.ts
|
|
15353
15523
|
function versionCommand() {
|