@subly_fi/pay 0.2.0 → 0.4.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 +11 -4
- package/dist/deposit.js +262 -71
- package/dist/mcp-server.js +1076 -411
- package/dist/pay.js +861 -543
- package/dist/withdraw.js +295 -102
- package/package.json +9 -7
package/README.md
CHANGED
|
@@ -7,7 +7,10 @@ locally with your own Solana key; Subly never holds it.
|
|
|
7
7
|
|
|
8
8
|
Ships one `pay` dispatcher bin with subcommands, all runnable with `npx` (no clone):
|
|
9
9
|
|
|
10
|
-
- `pay mcp` — an MCP server (Claude Code, Cursor, any MCP client)
|
|
10
|
+
- `pay mcp` — an MCP server (Claude Code, Cursor, any MCP client) exposing
|
|
11
|
+
the full lifecycle as tools: `deposit_to_subly_vault`,
|
|
12
|
+
`get_subly_yield_budget`, `fetch_with_subly_payment`,
|
|
13
|
+
`withdraw_from_subly_vault`
|
|
11
14
|
- `pay fetch <url>` — one-shot: pay for a URL, print the receipt (used by the
|
|
12
15
|
OpenClaw skill)
|
|
13
16
|
- `pay deposit <amountRawUsdc>` / `pay withdraw <amountRawUsdc>` — vault
|
|
@@ -45,10 +48,14 @@ npx -y @subly_fi/pay fetch https://seller.example.com/api/premium
|
|
|
45
48
|
| Var | Required | Default |
|
|
46
49
|
|---|---|---|
|
|
47
50
|
| `SUBLY_DEMO_AGENT_KEYPAIR_PATH` | yes (or `SUBLY_DEMO_AGENT_KEYPAIR` base58) | — |
|
|
48
|
-
| `
|
|
51
|
+
| `SUBLY_RELAYER_URL` | no | `https://api.demo.sublyfi.com` |
|
|
49
52
|
| `SOLANA_RPC_URL` | no | public mainnet RPC |
|
|
50
53
|
| `SUBLY_MCP_MAX_AMOUNT_RAW_USDC` | no | `10000` (0.01 USDC) per-payment cap |
|
|
51
54
|
|
|
52
55
|
Requests authenticate with a signature from your wallet key — there is no API
|
|
53
|
-
token.
|
|
54
|
-
|
|
56
|
+
token. `SUBLY_FACILITATOR_URL` is still accepted as a legacy fallback for
|
|
57
|
+
`SUBLY_RELAYER_URL`. Spending is bounded twice: the client cap, and the
|
|
58
|
+
relayer's server-side guard that refuses to realize anything beyond the
|
|
59
|
+
spendable yield — the deposited principal is never touched by a payment.
|
|
60
|
+
(A plain `pay withdraw` is the exit path and may of course move principal
|
|
61
|
+
back to your wallet.)
|
package/dist/deposit.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
// src/client/agent-wallet-signer.ts
|
|
1
|
+
// ../../src/client/agent-wallet-signer.ts
|
|
2
2
|
import { signBytes } from "@solana/kit";
|
|
3
3
|
import bs584 from "bs58";
|
|
4
4
|
|
|
5
|
-
// src/solana/tx.ts
|
|
5
|
+
// ../../src/solana/tx.ts
|
|
6
6
|
import bs58 from "bs58";
|
|
7
7
|
import {
|
|
8
8
|
appendTransactionMessageInstructions,
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
setTransactionMessageLifetimeUsingBlockhash
|
|
18
18
|
} from "@solana/kit";
|
|
19
19
|
|
|
20
|
-
// src/lib/hash.ts
|
|
20
|
+
// ../../src/lib/hash.ts
|
|
21
21
|
import { createHash } from "node:crypto";
|
|
22
22
|
function sha256TaggedHex(data) {
|
|
23
23
|
return `sha256-${createHash("sha256").update(data).digest("hex")}`;
|
|
@@ -42,16 +42,16 @@ function hashStableJson(value) {
|
|
|
42
42
|
return sha256TaggedHex(stableStringify(value));
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
// src/solana/tx.ts
|
|
45
|
+
// ../../src/solana/tx.ts
|
|
46
46
|
function decodeSerializedTransaction(serializedBase64) {
|
|
47
47
|
return getTransactionDecoder().decode(Buffer.from(serializedBase64, "base64"));
|
|
48
48
|
}
|
|
49
49
|
async function addSignaturesToSerializedTransaction(params) {
|
|
50
50
|
const decoded = decodeSerializedTransaction(params.serializedBase64);
|
|
51
|
-
const
|
|
51
|
+
const signed = await partiallySignTransaction(params.signers, decoded);
|
|
52
52
|
return {
|
|
53
|
-
serializedBase64: getBase64EncodedWireTransaction(
|
|
54
|
-
transaction:
|
|
53
|
+
serializedBase64: getBase64EncodedWireTransaction(signed),
|
|
54
|
+
transaction: signed
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
57
|
function signatureBase58ForSigner(transaction, signer2) {
|
|
@@ -62,11 +62,11 @@ function signatureBase58ForSigner(transaction, signer2) {
|
|
|
62
62
|
return bs58.encode(signature);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
// src/client/transaction-intent-validator.ts
|
|
65
|
+
// ../../src/client/transaction-intent-validator.ts
|
|
66
66
|
import bs583 from "bs58";
|
|
67
67
|
import { getCompiledTransactionMessageDecoder } from "@solana/kit";
|
|
68
68
|
|
|
69
|
-
// src/config/constants.ts
|
|
69
|
+
// ../../src/config/constants.ts
|
|
70
70
|
var PAYMENT_SCHEME = "subly-yield-exact";
|
|
71
71
|
var SOLANA_MAINNET_NETWORK = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
|
|
72
72
|
var SPL_TOKEN_PROGRAM_ID = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
|
|
@@ -82,7 +82,7 @@ var SUBLY_VAULT = {
|
|
|
82
82
|
};
|
|
83
83
|
var USDC_DECIMALS = 6;
|
|
84
84
|
|
|
85
|
-
// src/domain/request-binding.ts
|
|
85
|
+
// ../../src/domain/request-binding.ts
|
|
86
86
|
function computeRequestBindingHash(fields) {
|
|
87
87
|
return hashStableJson({
|
|
88
88
|
sellerRequestId: fields.sellerRequestId,
|
|
@@ -97,7 +97,7 @@ function computeRequestBindingHash(fields) {
|
|
|
97
97
|
});
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
// src/lib/associated-token-account.ts
|
|
100
|
+
// ../../src/lib/associated-token-account.ts
|
|
101
101
|
import { createHash as createHash2 } from "node:crypto";
|
|
102
102
|
import bs582 from "bs58";
|
|
103
103
|
var PDA_MARKER = Buffer.from("ProgramDerivedAddress", "utf8");
|
|
@@ -189,7 +189,7 @@ function modPow(base, exponent, modulus) {
|
|
|
189
189
|
return result;
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
-
// src/client/transaction-intent-validator.ts
|
|
192
|
+
// ../../src/client/transaction-intent-validator.ts
|
|
193
193
|
var COMPUTE_BUDGET_PROGRAM_ID = "ComputeBudget111111111111111111111111111111";
|
|
194
194
|
var SYSTEM_PROGRAM_ID = "11111111111111111111111111111111";
|
|
195
195
|
var ASSOCIATED_TOKEN_PROGRAM_ID2 = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL";
|
|
@@ -875,7 +875,7 @@ function readShortVec(bytes, startOffset) {
|
|
|
875
875
|
return null;
|
|
876
876
|
}
|
|
877
877
|
|
|
878
|
-
// src/client/agent-wallet-signer.ts
|
|
878
|
+
// ../../src/client/agent-wallet-signer.ts
|
|
879
879
|
var LocalKeypairAgentWalletSigner = class {
|
|
880
880
|
validationMode = "structured_intent_transaction";
|
|
881
881
|
keyPairSigner;
|
|
@@ -945,7 +945,7 @@ var LocalKeypairAgentWalletSigner = class {
|
|
|
945
945
|
}
|
|
946
946
|
};
|
|
947
947
|
|
|
948
|
-
// src/api/wallet-auth.ts
|
|
948
|
+
// ../../src/api/wallet-auth.ts
|
|
949
949
|
import { createHash as createHash3 } from "node:crypto";
|
|
950
950
|
import bs585 from "bs58";
|
|
951
951
|
import nacl from "tweetnacl";
|
|
@@ -963,7 +963,7 @@ function walletAuthMessage(params) {
|
|
|
963
963
|
);
|
|
964
964
|
}
|
|
965
965
|
|
|
966
|
-
// src/client/wallet-auth-headers.ts
|
|
966
|
+
// ../../src/client/wallet-auth-headers.ts
|
|
967
967
|
async function walletAuthHeaders(params) {
|
|
968
968
|
const signedAtMs = String(Date.now());
|
|
969
969
|
const message = walletAuthMessage({
|
|
@@ -979,7 +979,7 @@ async function walletAuthHeaders(params) {
|
|
|
979
979
|
};
|
|
980
980
|
}
|
|
981
981
|
|
|
982
|
-
// src/client/onboarding.ts
|
|
982
|
+
// ../../src/client/onboarding.ts
|
|
983
983
|
var SELF_SERVE_POLICY_ID = "self-serve";
|
|
984
984
|
var OnboardingError = class extends Error {
|
|
985
985
|
constructor(step, message, detail = null) {
|
|
@@ -1036,7 +1036,7 @@ async function ensureWalletOnboarded(params) {
|
|
|
1036
1036
|
await post("sync", `/v1/wallets/${wallet}/sync`, { source: "chain" });
|
|
1037
1037
|
}
|
|
1038
1038
|
|
|
1039
|
-
// src/client/lookup-tables.ts
|
|
1039
|
+
// ../../src/client/lookup-tables.ts
|
|
1040
1040
|
import { fetchAllMaybeAddressLookupTable } from "@solana-program/address-lookup-table";
|
|
1041
1041
|
import { address, getCompiledTransactionMessageDecoder as getCompiledTransactionMessageDecoder2 } from "@solana/kit";
|
|
1042
1042
|
function lookupTableAddressesForTransaction(serializedTransaction) {
|
|
@@ -1076,7 +1076,223 @@ async function fetchLookupTablesForTransaction(rpc2, serializedTransaction) {
|
|
|
1076
1076
|
return result;
|
|
1077
1077
|
}
|
|
1078
1078
|
|
|
1079
|
-
// src/
|
|
1079
|
+
// ../../src/client/vault-flows.ts
|
|
1080
|
+
var VaultFlowClientError = class extends Error {
|
|
1081
|
+
constructor(step, message, detail = null) {
|
|
1082
|
+
super(message);
|
|
1083
|
+
this.step = step;
|
|
1084
|
+
this.detail = detail;
|
|
1085
|
+
this.name = "VaultFlowClientError";
|
|
1086
|
+
}
|
|
1087
|
+
step;
|
|
1088
|
+
detail;
|
|
1089
|
+
};
|
|
1090
|
+
var VaultFlowClient = class {
|
|
1091
|
+
baseUrl;
|
|
1092
|
+
signer;
|
|
1093
|
+
fetchImpl;
|
|
1094
|
+
lookupTablesFor;
|
|
1095
|
+
pollTimeoutMs;
|
|
1096
|
+
pollIntervalMs;
|
|
1097
|
+
constructor(config) {
|
|
1098
|
+
this.baseUrl = config.facilitatorBaseUrl.replace(/\/$/, "");
|
|
1099
|
+
this.signer = config.signer;
|
|
1100
|
+
this.fetchImpl = config.fetchImpl ?? fetch;
|
|
1101
|
+
this.lookupTablesFor = config.lookupTablesFor ?? ((serializedTransaction) => fetchLookupTablesForTransaction(config.rpc, serializedTransaction));
|
|
1102
|
+
this.pollTimeoutMs = config.pollTimeoutMs ?? 9e4;
|
|
1103
|
+
this.pollIntervalMs = config.pollIntervalMs ?? 2500;
|
|
1104
|
+
}
|
|
1105
|
+
/** Moves USDC from the agent wallet into the vault (fee sponsored). */
|
|
1106
|
+
async deposit(input) {
|
|
1107
|
+
const prepared = await this.postJson("prepare", "/v1/deposits/prepare", {
|
|
1108
|
+
wallet: this.signer.walletAddress,
|
|
1109
|
+
amountRawUsdc: input.amountRawUsdc.toString()
|
|
1110
|
+
});
|
|
1111
|
+
const signed = await this.signer.signDeposit({
|
|
1112
|
+
intent: prepared.signingIntent,
|
|
1113
|
+
serializedTransaction: prepared.serializedTransaction,
|
|
1114
|
+
lookupTables: await this.lookupTablesFor(prepared.serializedTransaction)
|
|
1115
|
+
});
|
|
1116
|
+
let outcome = await this.postJson("submit", "/v1/deposits/submit", {
|
|
1117
|
+
depositId: prepared.depositId,
|
|
1118
|
+
serializedTransaction: signed.serializedTransaction,
|
|
1119
|
+
agentSignature: signed.agentSignature
|
|
1120
|
+
});
|
|
1121
|
+
if (outcome.status === "submitted") {
|
|
1122
|
+
outcome = await this.pollUntilTerminal(
|
|
1123
|
+
`/v1/deposits/${prepared.depositId}`,
|
|
1124
|
+
outcome
|
|
1125
|
+
);
|
|
1126
|
+
}
|
|
1127
|
+
return {
|
|
1128
|
+
depositId: prepared.depositId,
|
|
1129
|
+
status: outcome.status,
|
|
1130
|
+
txSignature: outcome.txSignature ?? null,
|
|
1131
|
+
actualDepositRawUsdc: outcome.actualDepositRawUsdc ?? null,
|
|
1132
|
+
sharesMintedRaw: outcome.sharesMintedRaw ?? null,
|
|
1133
|
+
errorCode: outcome.errorCode ?? null
|
|
1134
|
+
};
|
|
1135
|
+
}
|
|
1136
|
+
/**
|
|
1137
|
+
* Moves USDC from the vault back to the agent wallet's USDC ATA (fee
|
|
1138
|
+
* sponsored). A plain withdrawal is the exit path and MAY spend principal;
|
|
1139
|
+
* with purpose "yield_realize" the relayer refuses anything beyond the
|
|
1140
|
+
* spendable yield (the payment path, via RelayerYieldRealizer).
|
|
1141
|
+
*/
|
|
1142
|
+
async withdraw(input) {
|
|
1143
|
+
const prepared = await this.postJson(
|
|
1144
|
+
"prepare",
|
|
1145
|
+
"/v1/withdrawals/prepare",
|
|
1146
|
+
{
|
|
1147
|
+
wallet: this.signer.walletAddress,
|
|
1148
|
+
amountRawUsdc: input.amountRawUsdc.toString(),
|
|
1149
|
+
...input.purpose === void 0 ? {} : { purpose: input.purpose }
|
|
1150
|
+
}
|
|
1151
|
+
);
|
|
1152
|
+
const signed = await this.signer.signWithdrawal({
|
|
1153
|
+
intent: prepared.signingIntent,
|
|
1154
|
+
serializedTransaction: prepared.serializedTransaction,
|
|
1155
|
+
lookupTables: await this.lookupTablesFor(prepared.serializedTransaction)
|
|
1156
|
+
});
|
|
1157
|
+
let outcome = await this.postJson("submit", "/v1/withdrawals/submit", {
|
|
1158
|
+
withdrawalId: prepared.withdrawalId,
|
|
1159
|
+
serializedTransaction: signed.serializedTransaction,
|
|
1160
|
+
agentSignature: signed.agentSignature
|
|
1161
|
+
});
|
|
1162
|
+
if (outcome.status === "submitted") {
|
|
1163
|
+
outcome = await this.pollUntilTerminal(
|
|
1164
|
+
`/v1/withdrawals/${prepared.withdrawalId}`,
|
|
1165
|
+
outcome
|
|
1166
|
+
);
|
|
1167
|
+
}
|
|
1168
|
+
return {
|
|
1169
|
+
withdrawalId: prepared.withdrawalId,
|
|
1170
|
+
status: outcome.status,
|
|
1171
|
+
txSignature: outcome.txSignature ?? null,
|
|
1172
|
+
destinationUsdcAta: prepared.destinationUsdcAta,
|
|
1173
|
+
actualWithdrawRawUsdc: outcome.actualWithdrawRawUsdc ?? null,
|
|
1174
|
+
actualSharesBurnedRaw: outcome.actualSharesBurnedRaw ?? null,
|
|
1175
|
+
errorCode: outcome.errorCode ?? null
|
|
1176
|
+
};
|
|
1177
|
+
}
|
|
1178
|
+
/**
|
|
1179
|
+
* Reads the yield budget. Syncs the relayer's ledger from chain first (so
|
|
1180
|
+
* yield accrued since the last sync shows up); the sync is best-effort and
|
|
1181
|
+
* on failure the last-synced view is returned.
|
|
1182
|
+
*/
|
|
1183
|
+
async getBudget(options = {}) {
|
|
1184
|
+
if (options.refreshFromChain !== false) {
|
|
1185
|
+
try {
|
|
1186
|
+
await this.postJson(
|
|
1187
|
+
"sync",
|
|
1188
|
+
`/v1/wallets/${this.signer.walletAddress}/sync`,
|
|
1189
|
+
{ source: "chain" }
|
|
1190
|
+
);
|
|
1191
|
+
} catch {
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
const url = `${this.baseUrl}/v1/wallets/${this.signer.walletAddress}/budget`;
|
|
1195
|
+
const response = await this.fetchImpl(url, {
|
|
1196
|
+
headers: await walletAuthHeaders({
|
|
1197
|
+
signer: this.signer,
|
|
1198
|
+
method: "GET",
|
|
1199
|
+
url
|
|
1200
|
+
})
|
|
1201
|
+
});
|
|
1202
|
+
const text = await response.text();
|
|
1203
|
+
if (response.status !== 200) {
|
|
1204
|
+
throw new VaultFlowClientError(
|
|
1205
|
+
"budget",
|
|
1206
|
+
`budget endpoint returned ${response.status}: ${text}`
|
|
1207
|
+
);
|
|
1208
|
+
}
|
|
1209
|
+
let parsed;
|
|
1210
|
+
try {
|
|
1211
|
+
parsed = JSON.parse(text);
|
|
1212
|
+
} catch {
|
|
1213
|
+
throw new VaultFlowClientError(
|
|
1214
|
+
"budget",
|
|
1215
|
+
"budget endpoint returned 200 with a non-JSON body",
|
|
1216
|
+
text
|
|
1217
|
+
);
|
|
1218
|
+
}
|
|
1219
|
+
const body = parsed;
|
|
1220
|
+
return {
|
|
1221
|
+
wallet: this.signer.walletAddress,
|
|
1222
|
+
principalBasisRawUsdc: body.position?.principalBasisRawUsdc ?? "0",
|
|
1223
|
+
positionValueRawUsdc: body.budget?.positionValueRawUsdc ?? "0",
|
|
1224
|
+
grossYieldRawUsdc: body.budget?.grossYieldRawUsdc ?? "0",
|
|
1225
|
+
spendableYieldRawUsdc: body.budget?.spendableYieldRawUsdc ?? "0"
|
|
1226
|
+
};
|
|
1227
|
+
}
|
|
1228
|
+
/**
|
|
1229
|
+
* Polls the reconciling GET endpoint until the intent leaves "submitted"
|
|
1230
|
+
* (each read looks the tx up on-chain) or the timeout elapses.
|
|
1231
|
+
*/
|
|
1232
|
+
async pollUntilTerminal(path, last) {
|
|
1233
|
+
const deadline = Date.now() + this.pollTimeoutMs;
|
|
1234
|
+
let latest = last;
|
|
1235
|
+
while (Date.now() < deadline) {
|
|
1236
|
+
await new Promise((resolve) => setTimeout(resolve, this.pollIntervalMs));
|
|
1237
|
+
const url = `${this.baseUrl}${path}`;
|
|
1238
|
+
const response = await this.fetchImpl(url, {
|
|
1239
|
+
headers: await walletAuthHeaders({
|
|
1240
|
+
signer: this.signer,
|
|
1241
|
+
method: "GET",
|
|
1242
|
+
url
|
|
1243
|
+
})
|
|
1244
|
+
});
|
|
1245
|
+
if (response.status !== 200) {
|
|
1246
|
+
continue;
|
|
1247
|
+
}
|
|
1248
|
+
try {
|
|
1249
|
+
latest = await response.json();
|
|
1250
|
+
} catch {
|
|
1251
|
+
continue;
|
|
1252
|
+
}
|
|
1253
|
+
if (latest.status !== "submitted") {
|
|
1254
|
+
return latest;
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
return latest;
|
|
1258
|
+
}
|
|
1259
|
+
async postJson(step, path, body) {
|
|
1260
|
+
const url = `${this.baseUrl}${path}`;
|
|
1261
|
+
const serialized = JSON.stringify(body);
|
|
1262
|
+
const response = await this.fetchImpl(url, {
|
|
1263
|
+
method: "POST",
|
|
1264
|
+
headers: {
|
|
1265
|
+
...await walletAuthHeaders({
|
|
1266
|
+
signer: this.signer,
|
|
1267
|
+
method: "POST",
|
|
1268
|
+
url,
|
|
1269
|
+
body: serialized
|
|
1270
|
+
}),
|
|
1271
|
+
"content-type": "application/json"
|
|
1272
|
+
},
|
|
1273
|
+
body: serialized
|
|
1274
|
+
});
|
|
1275
|
+
const text = await response.text();
|
|
1276
|
+
if (response.status !== 200) {
|
|
1277
|
+
throw new VaultFlowClientError(
|
|
1278
|
+
step,
|
|
1279
|
+
`${path} failed with ${response.status}: ${text}`,
|
|
1280
|
+
text
|
|
1281
|
+
);
|
|
1282
|
+
}
|
|
1283
|
+
try {
|
|
1284
|
+
return JSON.parse(text);
|
|
1285
|
+
} catch {
|
|
1286
|
+
throw new VaultFlowClientError(
|
|
1287
|
+
step,
|
|
1288
|
+
`${path} returned 200 with a non-JSON body`,
|
|
1289
|
+
text
|
|
1290
|
+
);
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
};
|
|
1294
|
+
|
|
1295
|
+
// ../../src/solana/keys.ts
|
|
1080
1296
|
import { readFileSync } from "node:fs";
|
|
1081
1297
|
import bs586 from "bs58";
|
|
1082
1298
|
import {
|
|
@@ -1101,13 +1317,13 @@ async function loadKeyPairSigner(params) {
|
|
|
1101
1317
|
throw new Error(`${label} keypair is not configured`);
|
|
1102
1318
|
}
|
|
1103
1319
|
|
|
1104
|
-
// src/solana/rpc.ts
|
|
1320
|
+
// ../../src/solana/rpc.ts
|
|
1105
1321
|
import { createSolanaRpc } from "@solana/kit";
|
|
1106
1322
|
function createRpc(url) {
|
|
1107
1323
|
return createSolanaRpc(url);
|
|
1108
1324
|
}
|
|
1109
1325
|
|
|
1110
|
-
// demo/shared.ts
|
|
1326
|
+
// ../../demo/shared.ts
|
|
1111
1327
|
function fail(message) {
|
|
1112
1328
|
console.error(message);
|
|
1113
1329
|
process.exit(1);
|
|
@@ -1119,8 +1335,8 @@ function formatRawUsdc(raw) {
|
|
|
1119
1335
|
return `${whole}.${fraction}`;
|
|
1120
1336
|
}
|
|
1121
1337
|
|
|
1122
|
-
// demo/deposit.ts
|
|
1123
|
-
var
|
|
1338
|
+
// ../../demo/deposit.ts
|
|
1339
|
+
var relayerBaseUrl = process.env.SUBLY_RELAYER_URL ?? process.env.SUBLY_FACILITATOR_URL ?? "https://api.demo.sublyfi.com";
|
|
1124
1340
|
var amountRawUsdc = process.argv[2];
|
|
1125
1341
|
if (amountRawUsdc === void 0 || !/^[1-9]\d*$/.test(amountRawUsdc)) {
|
|
1126
1342
|
fail(
|
|
@@ -1136,69 +1352,44 @@ var signer = new LocalKeypairAgentWalletSigner(keyPairSigner);
|
|
|
1136
1352
|
var rpc = createRpc(
|
|
1137
1353
|
process.env.SOLANA_RPC_URL ?? "https://api.mainnet-beta.solana.com"
|
|
1138
1354
|
);
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
headers: {
|
|
1145
|
-
...await walletAuthHeaders({
|
|
1146
|
-
signer,
|
|
1147
|
-
method: "POST",
|
|
1148
|
-
url,
|
|
1149
|
-
body: serialized
|
|
1150
|
-
}),
|
|
1151
|
-
"content-type": "application/json"
|
|
1152
|
-
},
|
|
1153
|
-
body: serialized
|
|
1154
|
-
});
|
|
1155
|
-
const text = await response.text();
|
|
1156
|
-
if (response.status !== 200) {
|
|
1157
|
-
fail(`[deposit] ${path} failed with ${response.status}: ${text}`);
|
|
1158
|
-
}
|
|
1159
|
-
return JSON.parse(text);
|
|
1160
|
-
}
|
|
1355
|
+
var vaultFlows = new VaultFlowClient({
|
|
1356
|
+
facilitatorBaseUrl: relayerBaseUrl,
|
|
1357
|
+
signer,
|
|
1358
|
+
rpc
|
|
1359
|
+
});
|
|
1161
1360
|
console.log(`[deposit] agent wallet: ${signer.walletAddress}`);
|
|
1162
|
-
console.log(`[deposit]
|
|
1361
|
+
console.log(`[deposit] relayer: ${relayerBaseUrl}`);
|
|
1163
1362
|
console.log("\n[deposit] step 0: ensure the wallet is registered (self-serve)");
|
|
1164
|
-
await ensureWalletOnboarded({ facilitatorBaseUrl, signer });
|
|
1363
|
+
await ensureWalletOnboarded({ facilitatorBaseUrl: relayerBaseUrl, signer });
|
|
1165
1364
|
console.log("[deposit] wallet registered and synced");
|
|
1166
1365
|
console.log(
|
|
1167
1366
|
`
|
|
1168
|
-
[deposit] step 1:
|
|
1367
|
+
[deposit] step 1: deposit ${formatRawUsdc(amountRawUsdc)} USDC (prepare -> validate intent + sign locally -> sponsor submits)`
|
|
1169
1368
|
);
|
|
1170
|
-
var
|
|
1171
|
-
|
|
1172
|
-
amountRawUsdc
|
|
1173
|
-
})
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
serializedTransaction: prepared.serializedTransaction,
|
|
1181
|
-
lookupTables: await fetchLookupTablesForTransaction(
|
|
1182
|
-
rpc,
|
|
1183
|
-
prepared.serializedTransaction
|
|
1184
|
-
)
|
|
1185
|
-
});
|
|
1186
|
-
console.log("[deposit] agent signature attached");
|
|
1187
|
-
console.log("\n[deposit] step 3: submit (sponsor co-signs and broadcasts)");
|
|
1188
|
-
var submitted = await postJson("/v1/deposits/submit", {
|
|
1189
|
-
depositId: prepared.depositId,
|
|
1190
|
-
serializedTransaction: signed.serializedTransaction,
|
|
1191
|
-
agentSignature: signed.agentSignature
|
|
1192
|
-
});
|
|
1369
|
+
var submitted;
|
|
1370
|
+
try {
|
|
1371
|
+
submitted = await vaultFlows.deposit({ amountRawUsdc: BigInt(amountRawUsdc) });
|
|
1372
|
+
} catch (error) {
|
|
1373
|
+
if (error instanceof VaultFlowClientError) {
|
|
1374
|
+
fail(`[deposit] ${error.step} failed: ${error.message}`);
|
|
1375
|
+
}
|
|
1376
|
+
throw error;
|
|
1377
|
+
}
|
|
1378
|
+
console.log(`[deposit] depositId: ${submitted.depositId}`);
|
|
1193
1379
|
console.log(`[deposit] status: ${submitted.status}`);
|
|
1194
1380
|
if (submitted.txSignature !== null) {
|
|
1195
1381
|
console.log(
|
|
1196
1382
|
`[deposit] transaction: https://solscan.io/tx/${submitted.txSignature}`
|
|
1197
1383
|
);
|
|
1198
1384
|
}
|
|
1385
|
+
if (submitted.status === "submitted") {
|
|
1386
|
+
fail(
|
|
1387
|
+
`[deposit] broadcast but not yet confirmed \u2014 it may still land. Do NOT resubmit; check GET /v1/deposits/${submitted.depositId} (or the tx link above) first`
|
|
1388
|
+
);
|
|
1389
|
+
}
|
|
1199
1390
|
if (submitted.status !== "confirmed") {
|
|
1200
1391
|
fail(
|
|
1201
|
-
`[deposit] not confirmed (errorCode=${submitted.errorCode}); check GET /v1/deposits/${
|
|
1392
|
+
`[deposit] not confirmed (errorCode=${submitted.errorCode}); check GET /v1/deposits/${submitted.depositId} and the relayer logs`
|
|
1202
1393
|
);
|
|
1203
1394
|
}
|
|
1204
1395
|
console.log(
|