@subly_fi/pay 0.5.0 → 0.6.1
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 +8 -4
- package/dist/cli.js +4 -2
- package/dist/deposit.js +49 -8
- package/dist/pay.js +30 -9
- package/dist/setup-link.js +1502 -0
- package/dist/setup-status.js +27 -0
- package/dist/withdraw.js +32 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,10 +19,14 @@ Ships one `pay` dispatcher bin with subcommands, all runnable with `npx` (no clo
|
|
|
19
19
|
threshold, deposits, and (when the mandate opts in) withdrawals return an
|
|
20
20
|
`approveUrl` to paste into chat; retry with the `approvalId` once the
|
|
21
21
|
human approved.
|
|
22
|
-
- `pay fetch <url
|
|
23
|
-
OpenClaw skill)
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
- `pay fetch <url> [maxAmountRawUsdc] [apr_<approvalId>]` — one-shot: pay for
|
|
23
|
+
a URL, print the receipt (used by the OpenClaw skill); retry with the
|
|
24
|
+
`apr_...` id after an `approval_required` refusal
|
|
25
|
+
- `pay deposit <amountRawUsdc> [apr_...]` / `pay withdraw <amountRawUsdc>
|
|
26
|
+
[apr_...]` — vault deposit / withdraw with the same owner-approval flow
|
|
27
|
+
- `pay setup-link [--initial-deposit <raw>] [--approval-threshold <raw>] ...`
|
|
28
|
+
/ `pay setup-status <sessionId>` — owner onboarding for CLI/skill harnesses
|
|
29
|
+
(same flow as the MCP setup tools)
|
|
26
30
|
|
|
27
31
|
## Wallet
|
|
28
32
|
|
package/dist/cli.js
CHANGED
|
@@ -9,14 +9,16 @@ const TARGETS = {
|
|
|
9
9
|
mcp: "mcp-server.js",
|
|
10
10
|
fetch: "pay.js",
|
|
11
11
|
deposit: "deposit.js",
|
|
12
|
-
withdraw: "withdraw.js"
|
|
12
|
+
withdraw: "withdraw.js",
|
|
13
|
+
"setup-link": "setup-link.js",
|
|
14
|
+
"setup-status": "setup-status.js"
|
|
13
15
|
};
|
|
14
16
|
|
|
15
17
|
const [sub, ...rest] = process.argv.slice(2);
|
|
16
18
|
const target = sub === undefined ? undefined : TARGETS[sub];
|
|
17
19
|
if (target === undefined) {
|
|
18
20
|
console.error(
|
|
19
|
-
"usage: subly <mcp|fetch <url>|deposit <amountRawUsdc>|withdraw <amountRawUsdc>>"
|
|
21
|
+
"usage: subly <mcp|fetch <url>|deposit <amountRawUsdc>|withdraw <amountRawUsdc>|setup-link|setup-status <sessionId>>"
|
|
20
22
|
);
|
|
21
23
|
process.exit(1);
|
|
22
24
|
}
|
package/dist/deposit.js
CHANGED
|
@@ -1115,26 +1115,26 @@ var VaultFlowClient = class {
|
|
|
1115
1115
|
* up and used automatically before surfacing deposit_approval_required.
|
|
1116
1116
|
*/
|
|
1117
1117
|
async deposit(input) {
|
|
1118
|
-
let
|
|
1118
|
+
let approvalId2 = input.approvalId;
|
|
1119
1119
|
let prepared;
|
|
1120
1120
|
try {
|
|
1121
1121
|
prepared = await this.postJson("prepare", "/v1/deposits/prepare", {
|
|
1122
1122
|
wallet: this.signer.walletAddress,
|
|
1123
1123
|
amountRawUsdc: input.amountRawUsdc.toString(),
|
|
1124
|
-
...
|
|
1124
|
+
...approvalId2 === void 0 ? {} : { approvalId: approvalId2 }
|
|
1125
1125
|
});
|
|
1126
1126
|
} catch (error) {
|
|
1127
|
-
if (!(error instanceof VaultFlowClientError) || error.code !== "deposit_approval_required" ||
|
|
1127
|
+
if (!(error instanceof VaultFlowClientError) || error.code !== "deposit_approval_required" || approvalId2 !== void 0) {
|
|
1128
1128
|
throw error;
|
|
1129
1129
|
}
|
|
1130
|
-
|
|
1131
|
-
if (
|
|
1130
|
+
approvalId2 = await this.findApprovedDepositApproval(input.amountRawUsdc);
|
|
1131
|
+
if (approvalId2 === void 0) {
|
|
1132
1132
|
throw error;
|
|
1133
1133
|
}
|
|
1134
1134
|
prepared = await this.postJson("prepare", "/v1/deposits/prepare", {
|
|
1135
1135
|
wallet: this.signer.walletAddress,
|
|
1136
1136
|
amountRawUsdc: input.amountRawUsdc.toString(),
|
|
1137
|
-
approvalId
|
|
1137
|
+
approvalId: approvalId2
|
|
1138
1138
|
});
|
|
1139
1139
|
}
|
|
1140
1140
|
const signed = await this.signer.signDeposit({
|
|
@@ -1480,9 +1480,13 @@ var relayerBaseUrl = process.env.SUBLY_RELAYER_URL ?? process.env.SUBLY_FACILITA
|
|
|
1480
1480
|
var amountRawUsdc = process.argv[2];
|
|
1481
1481
|
if (amountRawUsdc === void 0 || !/^[1-9]\d*$/.test(amountRawUsdc)) {
|
|
1482
1482
|
fail(
|
|
1483
|
-
"Usage:
|
|
1483
|
+
"Usage: pay deposit <amountRawUsdc> [apr_<approvalId>] (positive integer, e.g. 60000000 = 60 USDC)"
|
|
1484
1484
|
);
|
|
1485
1485
|
}
|
|
1486
|
+
var approvalId = process.argv[3];
|
|
1487
|
+
if (approvalId !== void 0 && !/^apr_[0-9a-f]+$/i.test(approvalId)) {
|
|
1488
|
+
fail(`unrecognized argument: ${approvalId} (expected apr_<approvalId>)`);
|
|
1489
|
+
}
|
|
1486
1490
|
var keyPairSigner = await loadKeyPairSigner({
|
|
1487
1491
|
base58Secret: process.env.SUBLY_DEMO_AGENT_KEYPAIR,
|
|
1488
1492
|
jsonFilePath: process.env.SUBLY_DEMO_AGENT_KEYPAIR_PATH,
|
|
@@ -1508,9 +1512,46 @@ console.log(
|
|
|
1508
1512
|
);
|
|
1509
1513
|
var submitted;
|
|
1510
1514
|
try {
|
|
1511
|
-
submitted = await vaultFlows.deposit({
|
|
1515
|
+
submitted = await vaultFlows.deposit({
|
|
1516
|
+
amountRawUsdc: BigInt(amountRawUsdc),
|
|
1517
|
+
...approvalId === void 0 ? {} : { approvalId }
|
|
1518
|
+
});
|
|
1512
1519
|
} catch (error) {
|
|
1513
1520
|
if (error instanceof VaultFlowClientError) {
|
|
1521
|
+
if (error.code === "deposit_approval_required") {
|
|
1522
|
+
const details = error.errorDetails ?? {};
|
|
1523
|
+
process.stdout.write(
|
|
1524
|
+
`${JSON.stringify(
|
|
1525
|
+
{
|
|
1526
|
+
ok: false,
|
|
1527
|
+
approvalRequired: true,
|
|
1528
|
+
approvalId: details.approvalId ?? null,
|
|
1529
|
+
approveUrl: details.approveUrl ?? null,
|
|
1530
|
+
expiresAtMs: details.expiresAtMs ?? null,
|
|
1531
|
+
message: `This deposit needs the owner's approval. Paste approveUrl to the user; once they approve (Face ID / wallet sign), retry: pay deposit ${amountRawUsdc} ${details.approvalId ?? "<approvalId>"}`
|
|
1532
|
+
},
|
|
1533
|
+
null,
|
|
1534
|
+
2
|
|
1535
|
+
)}
|
|
1536
|
+
`
|
|
1537
|
+
);
|
|
1538
|
+
process.exit(1);
|
|
1539
|
+
}
|
|
1540
|
+
if (error.code === "mandate_required_for_deposit") {
|
|
1541
|
+
process.stdout.write(
|
|
1542
|
+
`${JSON.stringify(
|
|
1543
|
+
{
|
|
1544
|
+
ok: false,
|
|
1545
|
+
setupRequired: true,
|
|
1546
|
+
message: `Deposits require a registered owner. Create a setup link (pay setup-link --initial-deposit ${amountRawUsdc}), paste the setupUrl to the user, wait for their Face ID, then deposit again \u2014 the first deposit is pre-approved.`
|
|
1547
|
+
},
|
|
1548
|
+
null,
|
|
1549
|
+
2
|
|
1550
|
+
)}
|
|
1551
|
+
`
|
|
1552
|
+
);
|
|
1553
|
+
process.exit(1);
|
|
1554
|
+
}
|
|
1514
1555
|
fail(`[deposit] ${error.step} failed: ${error.message}`);
|
|
1515
1556
|
}
|
|
1516
1557
|
throw error;
|
package/dist/pay.js
CHANGED
|
@@ -1120,26 +1120,26 @@ var VaultFlowClient = class {
|
|
|
1120
1120
|
* up and used automatically before surfacing deposit_approval_required.
|
|
1121
1121
|
*/
|
|
1122
1122
|
async deposit(input) {
|
|
1123
|
-
let
|
|
1123
|
+
let approvalId2 = input.approvalId;
|
|
1124
1124
|
let prepared;
|
|
1125
1125
|
try {
|
|
1126
1126
|
prepared = await this.postJson("prepare", "/v1/deposits/prepare", {
|
|
1127
1127
|
wallet: this.signer.walletAddress,
|
|
1128
1128
|
amountRawUsdc: input.amountRawUsdc.toString(),
|
|
1129
|
-
...
|
|
1129
|
+
...approvalId2 === void 0 ? {} : { approvalId: approvalId2 }
|
|
1130
1130
|
});
|
|
1131
1131
|
} catch (error) {
|
|
1132
|
-
if (!(error instanceof VaultFlowClientError) || error.code !== "deposit_approval_required" ||
|
|
1132
|
+
if (!(error instanceof VaultFlowClientError) || error.code !== "deposit_approval_required" || approvalId2 !== void 0) {
|
|
1133
1133
|
throw error;
|
|
1134
1134
|
}
|
|
1135
|
-
|
|
1136
|
-
if (
|
|
1135
|
+
approvalId2 = await this.findApprovedDepositApproval(input.amountRawUsdc);
|
|
1136
|
+
if (approvalId2 === void 0) {
|
|
1137
1137
|
throw error;
|
|
1138
1138
|
}
|
|
1139
1139
|
prepared = await this.postJson("prepare", "/v1/deposits/prepare", {
|
|
1140
1140
|
wallet: this.signer.walletAddress,
|
|
1141
1141
|
amountRawUsdc: input.amountRawUsdc.toString(),
|
|
1142
|
-
approvalId
|
|
1142
|
+
approvalId: approvalId2
|
|
1143
1143
|
});
|
|
1144
1144
|
}
|
|
1145
1145
|
const signed = await this.signer.signDeposit({
|
|
@@ -2251,9 +2251,20 @@ function fail(message) {
|
|
|
2251
2251
|
}
|
|
2252
2252
|
var url = process.argv[2];
|
|
2253
2253
|
if (url === void 0 || !/^https?:\/\//.test(url)) {
|
|
2254
|
-
fail("Usage: pay fetch <url> [maxAmountRawUsdc]");
|
|
2254
|
+
fail("Usage: pay fetch <url> [maxAmountRawUsdc] [apr_<approvalId>]");
|
|
2255
|
+
}
|
|
2256
|
+
var maxAmountArg;
|
|
2257
|
+
var approvalId;
|
|
2258
|
+
for (const arg of process.argv.slice(3)) {
|
|
2259
|
+
if (/^apr_[0-9a-f]+$/i.test(arg)) {
|
|
2260
|
+
approvalId = arg;
|
|
2261
|
+
} else if (/^\d+$/.test(arg)) {
|
|
2262
|
+
maxAmountArg = arg;
|
|
2263
|
+
} else {
|
|
2264
|
+
fail(`unrecognized argument: ${arg}
|
|
2265
|
+
Usage: pay fetch <url> [maxAmountRawUsdc] [apr_<approvalId>]`);
|
|
2266
|
+
}
|
|
2255
2267
|
}
|
|
2256
|
-
var maxAmountArg = process.argv[3];
|
|
2257
2268
|
var relayerBaseUrl = process.env.SUBLY_RELAYER_URL ?? process.env.SUBLY_FACILITATOR_URL ?? "https://api.demo.sublyfi.com";
|
|
2258
2269
|
var rpcUrl = process.env.SOLANA_RPC_URL ?? "https://api.mainnet-beta.solana.com";
|
|
2259
2270
|
var defaultMaxAmountRawUsdc = process.env.SUBLY_MCP_MAX_AMOUNT_RAW_USDC === void 0 ? 10000n : BigInt(process.env.SUBLY_MCP_MAX_AMOUNT_RAW_USDC);
|
|
@@ -2294,6 +2305,7 @@ try {
|
|
|
2294
2305
|
...method === void 0 ? {} : { method },
|
|
2295
2306
|
...body === void 0 ? {} : { body, headers: { "content-type": "application/json" } },
|
|
2296
2307
|
...maxAmountArg === void 0 ? {} : { maxAmountRawUsdc: BigInt(maxAmountArg) },
|
|
2308
|
+
...approvalId === void 0 ? {} : { approvalId },
|
|
2297
2309
|
...process.env.SUBLY_PAY_FORCE_NEW_PAYMENT === "1" ? { forceNewPayment: true } : {}
|
|
2298
2310
|
});
|
|
2299
2311
|
process.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
@@ -2303,9 +2315,18 @@ try {
|
|
|
2303
2315
|
}
|
|
2304
2316
|
} catch (error) {
|
|
2305
2317
|
if (error instanceof StandardX402PayError) {
|
|
2318
|
+
const refusalApprovalId = error.reason === "approval_required" ? error.detail?.approvalId : void 0;
|
|
2306
2319
|
process.stdout.write(
|
|
2307
2320
|
`${JSON.stringify(
|
|
2308
|
-
{
|
|
2321
|
+
{
|
|
2322
|
+
paid: false,
|
|
2323
|
+
reason: error.reason,
|
|
2324
|
+
message: error.message,
|
|
2325
|
+
detail: error.detail ?? null,
|
|
2326
|
+
...refusalApprovalId === void 0 ? {} : {
|
|
2327
|
+
retry: `pay fetch "${url}"${maxAmountArg === void 0 ? "" : ` ${maxAmountArg}`} ${refusalApprovalId}`
|
|
2328
|
+
}
|
|
2329
|
+
},
|
|
2309
2330
|
null,
|
|
2310
2331
|
2
|
|
2311
2332
|
)}
|