@subly_fi/pay 0.6.0 → 0.6.2

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/dist/deposit.js CHANGED
@@ -1,9 +1,132 @@
1
+ // ../../src/api/wallet-auth.ts
2
+ import { createHash } from "node:crypto";
3
+ import bs58 from "bs58";
4
+ import nacl from "tweetnacl";
5
+ var WALLET_AUTH_WALLET_HEADER = "x-subly-wallet";
6
+ var WALLET_AUTH_SIGNED_AT_HEADER = "x-subly-signed-at";
7
+ var WALLET_AUTH_SIGNATURE_HEADER = "x-subly-signature";
8
+ function sha256Hex(data) {
9
+ return createHash("sha256").update(data, "utf8").digest("hex");
10
+ }
11
+ function walletAuthMessage(params) {
12
+ return new TextEncoder().encode(
13
+ `subly-api:${params.method.toUpperCase()}:${params.path}:${sha256Hex(
14
+ params.rawBody
15
+ )}:${params.signedAtMs}`
16
+ );
17
+ }
18
+
19
+ // ../../src/client/wallet-auth-headers.ts
20
+ async function walletAuthHeaders(params) {
21
+ const signedAtMs = String(Date.now());
22
+ const message = walletAuthMessage({
23
+ method: params.method,
24
+ path: (() => {
25
+ const url = new URL(params.url);
26
+ return url.pathname + url.search;
27
+ })(),
28
+ rawBody: params.body ?? "",
29
+ signedAtMs
30
+ });
31
+ return {
32
+ [WALLET_AUTH_WALLET_HEADER]: params.signer.walletAddress,
33
+ [WALLET_AUTH_SIGNED_AT_HEADER]: signedAtMs,
34
+ [WALLET_AUTH_SIGNATURE_HEADER]: await params.signer.signApiMessage(message)
35
+ };
36
+ }
37
+
38
+ // ../../src/client/onboarding.ts
39
+ var SELF_SERVE_POLICY_ID = "self-serve";
40
+ var OnboardingError = class extends Error {
41
+ constructor(step, message, detail = null) {
42
+ super(message);
43
+ this.step = step;
44
+ this.detail = detail;
45
+ this.name = "OnboardingError";
46
+ }
47
+ step;
48
+ detail;
49
+ };
50
+ async function ensureWalletOnboarded(params) {
51
+ const fetchImpl = params.fetchImpl ?? fetch;
52
+ const baseUrl = params.relayerBaseUrl.replace(/\/$/, "");
53
+ const post = async (step, path, body) => {
54
+ const url = `${baseUrl}${path}`;
55
+ const serialized = JSON.stringify(body);
56
+ const response = await fetchImpl(url, {
57
+ method: "POST",
58
+ headers: {
59
+ ...await walletAuthHeaders({
60
+ signer: params.signer,
61
+ method: "POST",
62
+ url,
63
+ body: serialized
64
+ }),
65
+ "content-type": "application/json"
66
+ },
67
+ body: serialized
68
+ });
69
+ if (response.status !== 200) {
70
+ let detail = null;
71
+ try {
72
+ detail = await response.json();
73
+ } catch {
74
+ detail = null;
75
+ }
76
+ throw new OnboardingError(
77
+ step,
78
+ `wallet onboarding ${step} failed with ${response.status}`,
79
+ detail
80
+ );
81
+ }
82
+ };
83
+ const wallet = params.signer.walletAddress;
84
+ await post("register", "/v1/wallets/agent", {
85
+ wallet,
86
+ signingPolicyId: SELF_SERVE_POLICY_ID,
87
+ signingMode: "non_interactive",
88
+ signerValidationMode: params.signer.validationMode,
89
+ signerProvider: params.signer.provider ?? "local-keypair",
90
+ activateForPayments: true
91
+ });
92
+ await post("sync", `/v1/wallets/${wallet}/sync`, { source: "chain" });
93
+ }
94
+
95
+ // ../../src/client/signer-env.ts
96
+ import { createKeyPairSignerFromBytes as createKeyPairSignerFromBytes2 } from "@solana/kit";
97
+
98
+ // ../../src/solana/keys.ts
99
+ import { readFileSync } from "node:fs";
100
+ import bs582 from "bs58";
101
+ import {
102
+ createKeyPairSignerFromBytes
103
+ } from "@solana/kit";
104
+ function loadSecretKeyBytes(params) {
105
+ const { base58Secret, jsonFilePath, label } = params;
106
+ if (base58Secret !== void 0 && base58Secret.length > 0) {
107
+ const bytes = bs582.decode(base58Secret);
108
+ if (bytes.length !== 64) {
109
+ throw new Error(`${label} base58 secret must decode to 64 bytes`);
110
+ }
111
+ return bytes;
112
+ }
113
+ if (jsonFilePath !== void 0 && jsonFilePath.length > 0) {
114
+ const raw = JSON.parse(readFileSync(jsonFilePath, "utf8"));
115
+ if (!Array.isArray(raw) || raw.length !== 64) {
116
+ throw new Error(`${label} keypair file must be a 64-byte JSON array`);
117
+ }
118
+ return Uint8Array.from(raw);
119
+ }
120
+ throw new Error(`${label} keypair is not configured`);
121
+ }
122
+
1
123
  // ../../src/client/agent-wallet-signer.ts
2
124
  import { signBytes } from "@solana/kit";
3
- import bs584 from "bs58";
125
+ import bs587 from "bs58";
126
+ import nacl3 from "tweetnacl";
4
127
 
5
128
  // ../../src/solana/tx.ts
6
- import bs58 from "bs58";
129
+ import bs583 from "bs58";
7
130
  import {
8
131
  appendTransactionMessageInstructions,
9
132
  compileTransaction,
@@ -18,9 +141,9 @@ import {
18
141
  } from "@solana/kit";
19
142
 
20
143
  // ../../src/lib/hash.ts
21
- import { createHash } from "node:crypto";
144
+ import { createHash as createHash2 } from "node:crypto";
22
145
  function sha256TaggedHex(data) {
23
- return `sha256-${createHash("sha256").update(data).digest("hex")}`;
146
+ return `sha256-${createHash2("sha256").update(data).digest("hex")}`;
24
147
  }
25
148
  function stableStringify(value) {
26
149
  if (value === null) {
@@ -46,6 +169,24 @@ function hashStableJson(value) {
46
169
  function decodeSerializedTransaction(serializedBase64) {
47
170
  return getTransactionDecoder().decode(Buffer.from(serializedBase64, "base64"));
48
171
  }
172
+ function attachExternalSignatureToTransaction(params) {
173
+ if (!(params.signer in params.transaction.signatures)) {
174
+ throw new Error(
175
+ `transaction does not expect a signature from ${params.signer}`
176
+ );
177
+ }
178
+ const transaction = Object.freeze({
179
+ ...params.transaction,
180
+ signatures: Object.freeze({
181
+ ...params.transaction.signatures,
182
+ [params.signer]: params.signature
183
+ })
184
+ });
185
+ return {
186
+ serializedBase64: getBase64EncodedWireTransaction(transaction),
187
+ transaction
188
+ };
189
+ }
49
190
  async function addSignaturesToSerializedTransaction(params) {
50
191
  const decoded = decodeSerializedTransaction(params.serializedBase64);
51
192
  const signed = await partiallySignTransaction(params.signers, decoded);
@@ -59,11 +200,148 @@ function signatureBase58ForSigner(transaction, signer2) {
59
200
  if (signature === null || signature === void 0) {
60
201
  return null;
61
202
  }
62
- return bs58.encode(signature);
203
+ return bs583.encode(signature);
204
+ }
205
+
206
+ // ../../src/client/remote-signer-transport.ts
207
+ import bs584 from "bs58";
208
+ import nacl2 from "tweetnacl";
209
+ var RemoteSigningError = class extends Error {
210
+ constructor(provider, message, detail = null) {
211
+ super(`[${provider}] ${message}`);
212
+ this.provider = provider;
213
+ this.detail = detail;
214
+ this.name = "RemoteSigningError";
215
+ }
216
+ provider;
217
+ detail;
218
+ };
219
+ function ed25519PublicKeyBytes(provider, walletAddress) {
220
+ let bytes;
221
+ try {
222
+ bytes = bs584.decode(walletAddress);
223
+ } catch {
224
+ throw new RemoteSigningError(
225
+ provider,
226
+ `wallet address ${walletAddress} is not base58`
227
+ );
228
+ }
229
+ if (bytes.length !== 32) {
230
+ throw new RemoteSigningError(
231
+ provider,
232
+ `wallet address ${walletAddress} is not a 32-byte ed25519 key`
233
+ );
234
+ }
235
+ return bytes;
236
+ }
237
+ function verifiedEd25519Signature(params) {
238
+ const publicKey = ed25519PublicKeyBytes(params.provider, params.walletAddress);
239
+ const encoded = params.encodedSignature.trim();
240
+ for (const candidate of decodeSignatureCandidates(encoded)) {
241
+ if (nacl2.sign.detached.verify(params.message, candidate, publicKey)) {
242
+ return candidate;
243
+ }
244
+ }
245
+ throw new RemoteSigningError(
246
+ params.provider,
247
+ `signature did not verify for wallet ${params.walletAddress}`
248
+ );
249
+ }
250
+ function decodeSignatureCandidates(encoded) {
251
+ const candidates = [];
252
+ const hex = encoded.startsWith("0x") ? encoded.slice(2) : encoded;
253
+ if (/^[0-9a-fA-F]{128}$/.test(hex)) {
254
+ candidates.push(Uint8Array.from(Buffer.from(hex, "hex")));
255
+ }
256
+ try {
257
+ const fromBase58 = bs584.decode(encoded);
258
+ if (fromBase58.length === 64) {
259
+ candidates.push(fromBase58);
260
+ }
261
+ } catch {
262
+ }
263
+ if (/^[A-Za-z0-9+/=_-]+$/.test(encoded)) {
264
+ const fromBase64 = Uint8Array.from(
265
+ Buffer.from(encoded.replace(/-/g, "+").replace(/_/g, "/"), "base64")
266
+ );
267
+ if (fromBase64.length === 64) {
268
+ candidates.push(fromBase64);
269
+ }
270
+ }
271
+ return candidates;
272
+ }
273
+ async function requestVerifiedTransactionSignature(params) {
274
+ const { transport } = params;
275
+ const signedBase64 = await transport.signTransaction(
276
+ params.serializedTransactionBase64
277
+ );
278
+ let returned;
279
+ try {
280
+ returned = decodeSerializedTransaction(signedBase64);
281
+ } catch (error) {
282
+ throw new RemoteSigningError(
283
+ transport.provider,
284
+ "provider returned an undecodable signed transaction",
285
+ error
286
+ );
287
+ }
288
+ const signature = returned.signatures[transport.walletAddress] ?? null;
289
+ if (signature === null) {
290
+ throw new RemoteSigningError(
291
+ transport.provider,
292
+ `signed transaction is missing the signature for ${transport.walletAddress}`
293
+ );
294
+ }
295
+ const publicKey = params.publicKey ?? ed25519PublicKeyBytes(transport.provider, transport.walletAddress);
296
+ if (!nacl2.sign.detached.verify(params.messageBytes, signature, publicKey)) {
297
+ throw new RemoteSigningError(
298
+ transport.provider,
299
+ "returned signature does not verify over the requested transaction"
300
+ );
301
+ }
302
+ return signature;
303
+ }
304
+ async function externallySignedAgentTransaction(params) {
305
+ const original = decodeSerializedTransaction(params.serializedTransaction);
306
+ const signature = await requestVerifiedTransactionSignature({
307
+ transport: params.transport,
308
+ serializedTransactionBase64: params.serializedTransaction,
309
+ messageBytes: original.messageBytes
310
+ });
311
+ const attached = attachExternalSignatureToTransaction({
312
+ transaction: original,
313
+ signer: params.transport.walletAddress,
314
+ signature
315
+ });
316
+ return {
317
+ serializedTransaction: attached.serializedBase64,
318
+ agentSignature: bs584.encode(signature)
319
+ };
320
+ }
321
+ async function providerJsonRequest(params) {
322
+ const response = await params.fetchImpl(`${params.baseUrl}${params.path}`, {
323
+ method: params.method,
324
+ headers: { ...params.headers, "content-type": "application/json" },
325
+ ...params.body === void 0 ? {} : { body: JSON.stringify(params.body) }
326
+ });
327
+ let json = null;
328
+ try {
329
+ json = await response.json();
330
+ } catch {
331
+ json = null;
332
+ }
333
+ if (!response.ok) {
334
+ throw new RemoteSigningError(
335
+ params.provider,
336
+ `${params.method} ${params.path} failed with ${response.status}`,
337
+ json
338
+ );
339
+ }
340
+ return json;
63
341
  }
64
342
 
65
343
  // ../../src/client/transaction-intent-validator.ts
66
- import bs583 from "bs58";
344
+ import bs586 from "bs58";
67
345
  import { getCompiledTransactionMessageDecoder } from "@solana/kit";
68
346
 
69
347
  // ../../src/config/constants.ts
@@ -71,14 +349,30 @@ var PAYMENT_SCHEME = "subly-yield-exact";
71
349
  var SOLANA_MAINNET_NETWORK = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
72
350
  var SPL_TOKEN_PROGRAM_ID = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
73
351
  var ASSOCIATED_TOKEN_PROGRAM_ID = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL";
352
+ var envOr = (name, fallback) => {
353
+ const value = process.env[name]?.trim();
354
+ return value ? value : fallback;
355
+ };
74
356
  var SUBLY_VAULT = {
75
357
  name: "Subly USDC Payment Vault Alpha",
76
- address: "5kfkpQZ6AkQgizHVThqkxD4J3db2i7pE3mHdPNRbx7jr",
358
+ address: envOr(
359
+ "SUBLY_VAULT_ADDRESS",
360
+ "5kfkpQZ6AkQgizHVThqkxD4J3db2i7pE3mHdPNRbx7jr"
361
+ ),
77
362
  programId: "KvauGMspG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd",
78
- usdcMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
79
- shareMint: "7hGX49So539MU9Rrah8nBNVYXswWVwEJvgWNYeBDYq3a",
363
+ usdcMint: envOr(
364
+ "SUBLY_VAULT_USDC_MINT",
365
+ "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
366
+ ),
367
+ shareMint: envOr(
368
+ "SUBLY_VAULT_SHARE_MINT",
369
+ "7hGX49So539MU9Rrah8nBNVYXswWVwEJvgWNYeBDYq3a"
370
+ ),
80
371
  lookupTable: "7UbXhDnpK7WVnwsfivzQRENoqKqAULQ5s19gS1xJrQEo",
81
- farm: "E2Ct77LowkDAH1T9ubwPpb84pU2GSGrUdgH3KeTTpLX"
372
+ farm: envOr(
373
+ "SUBLY_VAULT_FARM",
374
+ "E2Ct77LowkDAH1T9ubwPpb84pU2GSGrUdgH3KeTTpLX"
375
+ )
82
376
  };
83
377
  var USDC_DECIMALS = 6;
84
378
 
@@ -98,8 +392,8 @@ function computeRequestBindingHash(fields) {
98
392
  }
99
393
 
100
394
  // ../../src/lib/associated-token-account.ts
101
- import { createHash as createHash2 } from "node:crypto";
102
- import bs582 from "bs58";
395
+ import { createHash as createHash3 } from "node:crypto";
396
+ import bs585 from "bs58";
103
397
  var PDA_MARKER = Buffer.from("ProgramDerivedAddress", "utf8");
104
398
  var ED25519_P = (1n << 255n) - 19n;
105
399
  var ED25519_D = mod(-121665n * modPow(121666n, ED25519_P - 2n, ED25519_P), ED25519_P);
@@ -120,13 +414,13 @@ function deriveAssociatedTokenAddress(params) {
120
414
  associatedTokenProgramId
121
415
  );
122
416
  if (address2 !== null) {
123
- return bs582.encode(address2);
417
+ return bs585.encode(address2);
124
418
  }
125
419
  }
126
420
  throw new Error("Unable to derive associated token account address");
127
421
  }
128
422
  function createProgramAddress(seeds, programId) {
129
- const hash = createHash2("sha256");
423
+ const hash = createHash3("sha256");
130
424
  for (const seed of seeds) {
131
425
  hash.update(seed);
132
426
  }
@@ -136,7 +430,7 @@ function createProgramAddress(seeds, programId) {
136
430
  return isEd25519Point(digest) ? null : new Uint8Array(digest);
137
431
  }
138
432
  function decodePublicKey(value, fieldName) {
139
- const decoded = bs582.decode(value);
433
+ const decoded = bs585.decode(value);
140
434
  if (decoded.length !== 32) {
141
435
  throw new Error(`${fieldName} must be a 32-byte public key`);
142
436
  }
@@ -337,6 +631,9 @@ function validatePaymentIntentTransaction(params) {
337
631
  if (intent.shareMint !== SUBLY_VAULT.shareMint) {
338
632
  reject("share_mint_mismatch", "Unsupported share mint");
339
633
  }
634
+ if (intent.farm !== SUBLY_VAULT.farm) {
635
+ reject("farm_mismatch", "Unsupported Kamino farm");
636
+ }
340
637
  if (intent.asset !== SUBLY_VAULT.usdcMint) {
341
638
  reject("asset_mismatch", "Only USDC payments are supported");
342
639
  }
@@ -405,7 +702,7 @@ function validatePaymentIntentTransaction(params) {
405
702
  expectComputeBudgetPair(ixs, policy);
406
703
  expectCreateTemporaryAccount(ixs, intent, policy);
407
704
  expectInitializeTemporaryAccount(ixs, intent);
408
- consumeFarmInstructions(ixs, intent.wallet);
705
+ consumeFarmInstructions(ixs, intent);
409
706
  expectKvaultWithdraw(ixs, {
410
707
  wallet: intent.wallet,
411
708
  vault: intent.vault,
@@ -548,13 +845,27 @@ function validateWithdrawalIntentTransaction(params) {
548
845
  reject("fee_payer_mismatch", "Transaction fee payer is not the Subly sponsor");
549
846
  }
550
847
  let sawWithdraw = false;
848
+ let farmUserState = null;
849
+ let farmInstructionCount = 0;
551
850
  for (const ix of decoded.instructions) {
552
851
  switch (ix.programAddress) {
553
852
  case COMPUTE_BUDGET_PROGRAM_ID:
554
853
  validateComputeBudgetInstruction(ix, policy);
555
854
  break;
556
855
  case MEMO_PROGRAM_ID:
856
+ break;
557
857
  case KAMINO_FARMS_PROGRAM_ID:
858
+ farmInstructionCount += 1;
859
+ if (farmInstructionCount === 1) {
860
+ farmUserState = validateFarmUnstakeInstruction(ix, intent);
861
+ } else if (farmInstructionCount === 2) {
862
+ validateFarmWithdrawInstruction(ix, intent, farmUserState);
863
+ } else {
864
+ reject(
865
+ "farm_instruction_mismatch",
866
+ "Withdrawal may contain only one farm unstake and one farm withdrawal"
867
+ );
868
+ }
558
869
  break;
559
870
  case ASSOCIATED_TOKEN_PROGRAM_ID2:
560
871
  expectAtaCreateForOwner(ix, intent.wallet);
@@ -575,6 +886,12 @@ function validateWithdrawalIntentTransaction(params) {
575
886
  break;
576
887
  }
577
888
  case KVAULT_PROGRAM_ID: {
889
+ if (sawWithdraw) {
890
+ reject(
891
+ "withdraw_mismatch",
892
+ "Withdrawal may contain only one KVault withdraw instruction"
893
+ );
894
+ }
578
895
  validateKvaultWithdrawInstruction(ix, {
579
896
  wallet: intent.wallet,
580
897
  vault: intent.vault,
@@ -597,6 +914,12 @@ function validateWithdrawalIntentTransaction(params) {
597
914
  if (!sawWithdraw) {
598
915
  reject("missing_instruction", "Withdrawal transaction has no KVault withdraw");
599
916
  }
917
+ if (farmInstructionCount === 1) {
918
+ reject(
919
+ "farm_instruction_mismatch",
920
+ "A farm unstake must be followed by a farm withdrawal"
921
+ );
922
+ }
600
923
  }
601
924
  function assertVaultIntentTargets(intent) {
602
925
  if (intent.vault !== SUBLY_VAULT.address) {
@@ -605,6 +928,9 @@ function assertVaultIntentTargets(intent) {
605
928
  if (intent.shareMint !== SUBLY_VAULT.shareMint) {
606
929
  reject("share_mint_mismatch", "Unsupported share mint");
607
930
  }
931
+ if (intent.farm !== SUBLY_VAULT.farm) {
932
+ reject("farm_mismatch", "Unsupported Kamino farm");
933
+ }
608
934
  if (intent.asset !== SUBLY_VAULT.usdcMint) {
609
935
  reject("asset_mismatch", "Only USDC is supported");
610
936
  }
@@ -677,7 +1003,7 @@ function expectCreateTemporaryAccount(ixs, intent, policy) {
677
1003
  }
678
1004
  const lamports = readU64LE(ix.data, 4);
679
1005
  const space = readU64LE(ix.data, 12);
680
- const owner = bs583.encode(ix.data.subarray(20, 52));
1006
+ const owner = bs586.encode(ix.data.subarray(20, 52));
681
1007
  if (space !== 165n) {
682
1008
  reject("temp_account_mismatch", "Temporary account space must be 165 bytes");
683
1009
  }
@@ -699,7 +1025,7 @@ function expectInitializeTemporaryAccount(ixs, intent) {
699
1025
  if (ix === void 0 || ix.programAddress !== SPL_TOKEN_PROGRAM_ID || ix.data[0] !== 18) {
700
1026
  reject("temp_account_mismatch", "Expected InitializeAccount3 instruction");
701
1027
  }
702
- const owner = bs583.encode(ix.data.subarray(1, 33));
1028
+ const owner = bs586.encode(ix.data.subarray(1, 33));
703
1029
  if (owner !== intent.wallet) {
704
1030
  reject(
705
1031
  "temp_account_mismatch",
@@ -713,15 +1039,90 @@ function expectInitializeTemporaryAccount(ixs, intent) {
713
1039
  reject("temp_account_mismatch", "Temporary account mint must be USDC");
714
1040
  }
715
1041
  }
716
- function consumeFarmInstructions(ixs, wallet) {
717
- while (ixs[0] !== void 0 && ixs[0].programAddress === KAMINO_FARMS_PROGRAM_ID) {
718
- const ix = ixs.shift();
719
- if (!ix.accounts.includes(wallet)) {
720
- reject(
721
- "farm_instruction_mismatch",
722
- "Farm unstake instruction does not reference the agent wallet"
723
- );
724
- }
1042
+ function consumeFarmInstructions(ixs, intent) {
1043
+ if (ixs[0]?.programAddress !== KAMINO_FARMS_PROGRAM_ID) {
1044
+ return;
1045
+ }
1046
+ const unstake = ixs.shift();
1047
+ const userState = validateFarmUnstakeInstruction(unstake, intent);
1048
+ if (ixs[0]?.programAddress !== KAMINO_FARMS_PROGRAM_ID) {
1049
+ reject(
1050
+ "farm_instruction_mismatch",
1051
+ "A farm unstake must be followed by a farm withdrawal"
1052
+ );
1053
+ }
1054
+ const withdraw = ixs.shift();
1055
+ validateFarmWithdrawInstruction(withdraw, intent, userState);
1056
+ if (ixs[0]?.programAddress === KAMINO_FARMS_PROGRAM_ID) {
1057
+ reject(
1058
+ "farm_instruction_mismatch",
1059
+ "Payment may contain only one farm unstake and one farm withdrawal"
1060
+ );
1061
+ }
1062
+ }
1063
+ var KAMINO_FARMS_UNSTAKE_DISCRIMINATOR = Uint8Array.from([
1064
+ 90,
1065
+ 95,
1066
+ 107,
1067
+ 42,
1068
+ 205,
1069
+ 124,
1070
+ 50,
1071
+ 225
1072
+ ]);
1073
+ var KAMINO_FARMS_WITHDRAW_UNSTAKED_DISCRIMINATOR = Uint8Array.from([
1074
+ 36,
1075
+ 102,
1076
+ 187,
1077
+ 49,
1078
+ 220,
1079
+ 36,
1080
+ 132,
1081
+ 67
1082
+ ]);
1083
+ function validateFarmUnstakeInstruction(ix, intent) {
1084
+ if (ix.programAddress !== KAMINO_FARMS_PROGRAM_ID || !bytesStartWith(ix.data, KAMINO_FARMS_UNSTAKE_DISCRIMINATOR) || ix.data.length !== 24 || readU128LE(ix.data, 8) <= 0n) {
1085
+ reject(
1086
+ "farm_instruction_mismatch",
1087
+ "Expected a non-zero Kamino farm unstake instruction"
1088
+ );
1089
+ }
1090
+ if (ix.accounts.length !== 4) {
1091
+ reject(
1092
+ "farm_instruction_mismatch",
1093
+ "Farm unstake account list is not canonical"
1094
+ );
1095
+ }
1096
+ if (ix.accounts[0] !== intent.wallet) {
1097
+ reject("farm_instruction_mismatch", "Farm unstake owner must be the agent wallet");
1098
+ }
1099
+ if (ix.accounts[2] !== intent.farm) {
1100
+ reject("farm_instruction_mismatch", "Farm unstake target is not the approved farm");
1101
+ }
1102
+ return ix.accounts[1];
1103
+ }
1104
+ function validateFarmWithdrawInstruction(ix, intent, expectedUserState) {
1105
+ if (ix.programAddress !== KAMINO_FARMS_PROGRAM_ID || !bytesStartWith(ix.data, KAMINO_FARMS_WITHDRAW_UNSTAKED_DISCRIMINATOR) || ix.data.length !== 8) {
1106
+ reject(
1107
+ "farm_instruction_mismatch",
1108
+ "Expected a canonical Kamino farm withdrawal instruction"
1109
+ );
1110
+ }
1111
+ if (ix.accounts.length !== 7) {
1112
+ reject(
1113
+ "farm_instruction_mismatch",
1114
+ "Farm withdrawal account list is not canonical"
1115
+ );
1116
+ }
1117
+ const expectedSharesAta = deriveAssociatedTokenAddress({
1118
+ owner: intent.wallet,
1119
+ mint: intent.shareMint
1120
+ });
1121
+ if (ix.accounts[0] !== intent.wallet || ix.accounts[1] !== expectedUserState || ix.accounts[2] !== intent.farm || ix.accounts[3] !== expectedSharesAta || ix.accounts[6] !== SPL_TOKEN_PROGRAM_ID) {
1122
+ reject(
1123
+ "farm_instruction_mismatch",
1124
+ "Farm withdrawal must return the approved vault shares to the agent wallet"
1125
+ );
725
1126
  }
726
1127
  }
727
1128
  function expectKvaultWithdraw(ixs, expectation) {
@@ -856,6 +1257,16 @@ function readU32LE(data, offset) {
856
1257
  }
857
1258
  return Buffer.from(data.subarray(offset, offset + 4)).readUInt32LE(0);
858
1259
  }
1260
+ function readU128LE(data, offset) {
1261
+ if (data.length < offset + 16) {
1262
+ reject("invalid_transaction_encoding", "Instruction data too short for u128");
1263
+ }
1264
+ let value = 0n;
1265
+ for (let index = 0; index < 16; index += 1) {
1266
+ value |= BigInt(data[offset + index]) << BigInt(index * 8);
1267
+ }
1268
+ return value;
1269
+ }
859
1270
  function readShortVec(bytes, startOffset) {
860
1271
  let value = 0;
861
1272
  let shift = 0;
@@ -876,55 +1287,55 @@ function readShortVec(bytes, startOffset) {
876
1287
  }
877
1288
 
878
1289
  // ../../src/client/agent-wallet-signer.ts
879
- var LocalKeypairAgentWalletSigner = class {
1290
+ var IntentValidatingAgentWalletSigner = class {
880
1291
  validationMode = "structured_intent_transaction";
881
- keyPairSigner;
882
1292
  validationPolicy;
883
- constructor(keyPairSigner2, validationPolicy) {
884
- this.keyPairSigner = keyPairSigner2;
1293
+ constructor(validationPolicy) {
885
1294
  this.validationPolicy = validationPolicy;
886
1295
  }
887
- get walletAddress() {
888
- return this.keyPairSigner.address;
889
- }
890
1296
  async signPayment(params) {
891
1297
  this.assertIntentWallet(params.intent.wallet);
892
- validatePaymentIntentTransaction({
893
- ...params,
894
- ...this.validationPolicy === void 0 ? {} : { policy: this.validationPolicy }
895
- });
1298
+ validatePaymentIntentTransaction({ ...params, ...this.policySpread() });
896
1299
  return this.sign(params.serializedTransaction);
897
1300
  }
898
1301
  async signDeposit(params) {
899
1302
  this.assertIntentWallet(params.intent.wallet);
900
- validateDepositIntentTransaction({
901
- ...params,
902
- ...this.validationPolicy === void 0 ? {} : { policy: this.validationPolicy }
903
- });
1303
+ validateDepositIntentTransaction({ ...params, ...this.policySpread() });
904
1304
  return this.sign(params.serializedTransaction);
905
1305
  }
906
1306
  async signWithdrawal(params) {
907
1307
  this.assertIntentWallet(params.intent.wallet);
908
- validateWithdrawalIntentTransaction({
909
- ...params,
910
- ...this.validationPolicy === void 0 ? {} : { policy: this.validationPolicy }
911
- });
1308
+ validateWithdrawalIntentTransaction({ ...params, ...this.policySpread() });
912
1309
  return this.sign(params.serializedTransaction);
913
1310
  }
1311
+ policySpread() {
1312
+ return this.validationPolicy === void 0 ? {} : { policy: this.validationPolicy };
1313
+ }
914
1314
  assertIntentWallet(wallet) {
915
- if (wallet !== this.keyPairSigner.address) {
1315
+ if (wallet !== this.walletAddress) {
916
1316
  throw new IntentValidationError(
917
1317
  "wallet_mismatch",
918
1318
  "Intent wallet does not match this signer's wallet"
919
1319
  );
920
1320
  }
921
1321
  }
1322
+ };
1323
+ var LocalKeypairAgentWalletSigner = class extends IntentValidatingAgentWalletSigner {
1324
+ provider = "local-keypair";
1325
+ keyPairSigner;
1326
+ constructor(keyPairSigner, validationPolicy) {
1327
+ super(validationPolicy);
1328
+ this.keyPairSigner = keyPairSigner;
1329
+ }
1330
+ get walletAddress() {
1331
+ return this.keyPairSigner.address;
1332
+ }
922
1333
  async signApiMessage(message) {
923
1334
  const signature = await signBytes(
924
1335
  this.keyPairSigner.keyPair.privateKey,
925
1336
  message
926
1337
  );
927
- return bs584.encode(signature);
1338
+ return bs587.encode(signature);
928
1339
  }
929
1340
  async sign(serializedTransaction) {
930
1341
  const { serializedBase64, transaction } = await addSignaturesToSerializedTransaction({
@@ -944,96 +1355,378 @@ var LocalKeypairAgentWalletSigner = class {
944
1355
  return { serializedTransaction: serializedBase64, agentSignature };
945
1356
  }
946
1357
  };
1358
+ var RemoteAgentWalletSigner = class extends IntentValidatingAgentWalletSigner {
1359
+ transport;
1360
+ publicKey;
1361
+ constructor(transport, validationPolicy) {
1362
+ super(validationPolicy);
1363
+ this.transport = transport;
1364
+ this.publicKey = ed25519PublicKeyBytes(
1365
+ transport.provider,
1366
+ transport.walletAddress
1367
+ );
1368
+ }
1369
+ get walletAddress() {
1370
+ return this.transport.walletAddress;
1371
+ }
1372
+ get provider() {
1373
+ return this.transport.provider;
1374
+ }
1375
+ async signApiMessage(message) {
1376
+ const signature = await this.transport.signMessage(message);
1377
+ if (!nacl3.sign.detached.verify(message, signature, this.publicKey)) {
1378
+ throw new RemoteSigningError(
1379
+ this.transport.provider,
1380
+ "message signature did not verify for the agent wallet"
1381
+ );
1382
+ }
1383
+ return bs587.encode(signature);
1384
+ }
1385
+ sign(serializedTransaction) {
1386
+ return externallySignedAgentTransaction({
1387
+ transport: this.transport,
1388
+ serializedTransaction
1389
+ });
1390
+ }
1391
+ };
947
1392
 
948
- // ../../src/api/wallet-auth.ts
949
- import { createHash as createHash3 } from "node:crypto";
950
- import bs585 from "bs58";
951
- import nacl from "tweetnacl";
952
- var WALLET_AUTH_WALLET_HEADER = "x-subly-wallet";
953
- var WALLET_AUTH_SIGNED_AT_HEADER = "x-subly-signed-at";
954
- var WALLET_AUTH_SIGNATURE_HEADER = "x-subly-signature";
955
- function sha256Hex(data) {
956
- return createHash3("sha256").update(data, "utf8").digest("hex");
957
- }
958
- function walletAuthMessage(params) {
959
- return new TextEncoder().encode(
960
- `subly-api:${params.method.toUpperCase()}:${params.path}:${sha256Hex(
961
- params.rawBody
962
- )}:${params.signedAtMs}`
963
- );
964
- }
965
-
966
- // ../../src/client/wallet-auth-headers.ts
967
- async function walletAuthHeaders(params) {
968
- const signedAtMs = String(Date.now());
969
- const message = walletAuthMessage({
970
- method: params.method,
971
- path: new URL(params.url).pathname,
972
- rawBody: params.body ?? "",
973
- signedAtMs
974
- });
1393
+ // ../../src/client/signer-transports/circle.ts
1394
+ import {
1395
+ constants,
1396
+ createPublicKey,
1397
+ publicEncrypt
1398
+ } from "node:crypto";
1399
+ var PROVIDER = "circle";
1400
+ var DEFAULT_BASE_URL = "https://api.circle.com";
1401
+ async function createCircleSignerTransport(config) {
1402
+ if (!/^[0-9a-fA-F]{64}$/.test(config.entitySecret)) {
1403
+ throw new RemoteSigningError(
1404
+ PROVIDER,
1405
+ "entity secret must be 32 bytes of hex (64 hex chars)"
1406
+ );
1407
+ }
1408
+ const baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
1409
+ const fetchImpl = config.fetchImpl ?? fetch;
1410
+ const request = async (method, path, body) => {
1411
+ const json = await providerJsonRequest({
1412
+ provider: PROVIDER,
1413
+ fetchImpl,
1414
+ baseUrl,
1415
+ path,
1416
+ method,
1417
+ headers: { authorization: `Bearer ${config.apiKey}` },
1418
+ body
1419
+ });
1420
+ const data = json?.data;
1421
+ if (data === void 0) {
1422
+ throw new RemoteSigningError(
1423
+ PROVIDER,
1424
+ `${method} ${path} returned no data envelope`,
1425
+ json
1426
+ );
1427
+ }
1428
+ return data;
1429
+ };
1430
+ const walletData = await request("GET", `/v1/w3s/wallets/${config.walletId}`);
1431
+ const wallet = walletData.wallet;
1432
+ if (wallet?.address === void 0) {
1433
+ throw new RemoteSigningError(
1434
+ PROVIDER,
1435
+ `wallet ${config.walletId} has no address`,
1436
+ walletData
1437
+ );
1438
+ }
1439
+ if (wallet.blockchain !== "SOL") {
1440
+ throw new RemoteSigningError(
1441
+ PROVIDER,
1442
+ `wallet ${config.walletId} is on ${String(
1443
+ wallet.blockchain
1444
+ )}, expected SOL (Solana mainnet)`
1445
+ );
1446
+ }
1447
+ const walletAddress = wallet.address;
1448
+ let entityPublicKey = null;
1449
+ const entitySecretCiphertext = async () => {
1450
+ if (entityPublicKey === null) {
1451
+ const data = await request("GET", "/v1/w3s/config/entity/publicKey");
1452
+ const publicKey = data.publicKey;
1453
+ if (typeof publicKey !== "string") {
1454
+ throw new RemoteSigningError(
1455
+ PROVIDER,
1456
+ "entity public key response has no publicKey",
1457
+ data
1458
+ );
1459
+ }
1460
+ entityPublicKey = createPublicKey(publicKey);
1461
+ }
1462
+ return publicEncrypt(
1463
+ {
1464
+ key: entityPublicKey,
1465
+ padding: constants.RSA_PKCS1_OAEP_PADDING,
1466
+ oaepHash: "sha256"
1467
+ },
1468
+ Buffer.from(config.entitySecret, "hex")
1469
+ ).toString("base64");
1470
+ };
975
1471
  return {
976
- [WALLET_AUTH_WALLET_HEADER]: params.signer.walletAddress,
977
- [WALLET_AUTH_SIGNED_AT_HEADER]: signedAtMs,
978
- [WALLET_AUTH_SIGNATURE_HEADER]: await params.signer.signApiMessage(message)
1472
+ provider: PROVIDER,
1473
+ walletAddress,
1474
+ async signMessage(message) {
1475
+ const data = await request("POST", "/v1/w3s/developer/sign/message", {
1476
+ walletId: config.walletId,
1477
+ message: `0x${Buffer.from(message).toString("hex")}`,
1478
+ encodedByHex: true,
1479
+ entitySecretCiphertext: await entitySecretCiphertext()
1480
+ });
1481
+ const signature = data.signature;
1482
+ if (typeof signature !== "string") {
1483
+ throw new RemoteSigningError(
1484
+ PROVIDER,
1485
+ "sign/message returned no signature",
1486
+ data
1487
+ );
1488
+ }
1489
+ return verifiedEd25519Signature({
1490
+ provider: PROVIDER,
1491
+ encodedSignature: signature,
1492
+ message,
1493
+ walletAddress
1494
+ });
1495
+ },
1496
+ async signTransaction(serializedTransactionBase64) {
1497
+ const data = await request(
1498
+ "POST",
1499
+ "/v1/w3s/developer/sign/transaction",
1500
+ {
1501
+ walletId: config.walletId,
1502
+ rawTransaction: serializedTransactionBase64,
1503
+ entitySecretCiphertext: await entitySecretCiphertext()
1504
+ }
1505
+ );
1506
+ const signedTransaction = data.signedTransaction;
1507
+ if (typeof signedTransaction !== "string") {
1508
+ throw new RemoteSigningError(
1509
+ PROVIDER,
1510
+ "sign/transaction returned no signedTransaction",
1511
+ data
1512
+ );
1513
+ }
1514
+ return signedTransaction;
1515
+ }
979
1516
  };
980
1517
  }
981
1518
 
982
- // ../../src/client/onboarding.ts
983
- var SELF_SERVE_POLICY_ID = "self-serve";
984
- var OnboardingError = class extends Error {
985
- constructor(step, message, detail = null) {
986
- super(message);
987
- this.step = step;
988
- this.detail = detail;
989
- this.name = "OnboardingError";
1519
+ // ../../src/client/signer-transports/privy.ts
1520
+ import { createPrivateKey, createSign } from "node:crypto";
1521
+ var PROVIDER2 = "privy";
1522
+ var DEFAULT_BASE_URL2 = "https://api.privy.io";
1523
+ function canonicalJson(value) {
1524
+ if (Array.isArray(value)) {
1525
+ return `[${value.map(canonicalJson).join(",")}]`;
990
1526
  }
991
- step;
992
- detail;
993
- };
994
- async function ensureWalletOnboarded(params) {
995
- const fetchImpl = params.fetchImpl ?? fetch;
996
- const baseUrl = params.relayerBaseUrl.replace(/\/$/, "");
997
- const post = async (step, path, body) => {
998
- const url = `${baseUrl}${path}`;
999
- const serialized = JSON.stringify(body);
1000
- const response = await fetchImpl(url, {
1001
- method: "POST",
1002
- headers: {
1003
- ...await walletAuthHeaders({
1004
- signer: params.signer,
1005
- method: "POST",
1006
- url,
1007
- body: serialized
1008
- }),
1009
- "content-type": "application/json"
1010
- },
1011
- body: serialized
1527
+ if (value !== null && typeof value === "object") {
1528
+ const entries = Object.entries(value).filter(([, v]) => v !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
1529
+ return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`).join(",")}}`;
1530
+ }
1531
+ return JSON.stringify(value);
1532
+ }
1533
+ function parseAuthorizationKey(base64Pkcs8) {
1534
+ const stripped = base64Pkcs8.replace(/^wallet-auth:/, "").trim();
1535
+ try {
1536
+ return createPrivateKey({
1537
+ key: Buffer.from(stripped, "base64"),
1538
+ format: "der",
1539
+ type: "pkcs8"
1012
1540
  });
1013
- if (response.status !== 200) {
1014
- let detail = null;
1015
- try {
1016
- detail = await response.json();
1017
- } catch {
1018
- detail = null;
1541
+ } catch (error) {
1542
+ throw new RemoteSigningError(
1543
+ PROVIDER2,
1544
+ "authorization key is not a base64 PKCS#8 P-256 private key",
1545
+ error
1546
+ );
1547
+ }
1548
+ }
1549
+ function authorizationSignature(params) {
1550
+ const payload = {
1551
+ version: 1,
1552
+ method: params.method,
1553
+ url: params.url,
1554
+ body: params.body,
1555
+ headers: { "privy-app-id": params.appId }
1556
+ };
1557
+ const signer2 = createSign("sha256");
1558
+ signer2.update(canonicalJson(payload));
1559
+ return signer2.sign(params.key).toString("base64");
1560
+ }
1561
+ async function createPrivySignerTransport(config) {
1562
+ const baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL2).replace(/\/$/, "");
1563
+ const fetchImpl = config.fetchImpl ?? fetch;
1564
+ const authorizationKey = config.authorizationPrivateKey === void 0 ? null : parseAuthorizationKey(config.authorizationPrivateKey);
1565
+ const baseHeaders = {
1566
+ authorization: `Basic ${Buffer.from(
1567
+ `${config.appId}:${config.appSecret}`
1568
+ ).toString("base64")}`,
1569
+ "privy-app-id": config.appId
1570
+ };
1571
+ const request = async (method, path, body) => {
1572
+ const headers = authorizationKey !== null && method !== "GET" && body !== void 0 ? {
1573
+ ...baseHeaders,
1574
+ "privy-authorization-signature": authorizationSignature({
1575
+ key: authorizationKey,
1576
+ appId: config.appId,
1577
+ method,
1578
+ url: `${baseUrl}${path}`,
1579
+ body
1580
+ })
1581
+ } : baseHeaders;
1582
+ const json = await providerJsonRequest({
1583
+ provider: PROVIDER2,
1584
+ fetchImpl,
1585
+ baseUrl,
1586
+ path,
1587
+ method,
1588
+ headers,
1589
+ body
1590
+ });
1591
+ if (json === null || typeof json !== "object") {
1592
+ throw new RemoteSigningError(
1593
+ PROVIDER2,
1594
+ `${method} ${path} returned a non-JSON body`
1595
+ );
1596
+ }
1597
+ return json;
1598
+ };
1599
+ const rpc2 = async (body) => {
1600
+ const response = await request(
1601
+ "POST",
1602
+ `/v1/wallets/${config.walletId}/rpc`,
1603
+ body
1604
+ );
1605
+ const data = response.data;
1606
+ if (data === null || typeof data !== "object") {
1607
+ throw new RemoteSigningError(PROVIDER2, "rpc returned no data", response);
1608
+ }
1609
+ return data;
1610
+ };
1611
+ const wallet = await request("GET", `/v1/wallets/${config.walletId}`);
1612
+ const walletAddress = wallet.address;
1613
+ if (typeof walletAddress !== "string") {
1614
+ throw new RemoteSigningError(
1615
+ PROVIDER2,
1616
+ `wallet ${config.walletId} has no address`,
1617
+ wallet
1618
+ );
1619
+ }
1620
+ if (wallet.chain_type !== "solana") {
1621
+ throw new RemoteSigningError(
1622
+ PROVIDER2,
1623
+ `wallet ${config.walletId} is ${String(
1624
+ wallet.chain_type
1625
+ )}, expected solana`
1626
+ );
1627
+ }
1628
+ return {
1629
+ provider: PROVIDER2,
1630
+ walletAddress,
1631
+ async signMessage(message) {
1632
+ const data = await rpc2({
1633
+ chain_type: "solana",
1634
+ method: "signMessage",
1635
+ params: {
1636
+ message: Buffer.from(message).toString("base64"),
1637
+ encoding: "base64"
1638
+ }
1639
+ });
1640
+ const signature = data.signature;
1641
+ if (typeof signature !== "string") {
1642
+ throw new RemoteSigningError(
1643
+ PROVIDER2,
1644
+ "signMessage returned no signature",
1645
+ data
1646
+ );
1019
1647
  }
1020
- throw new OnboardingError(
1021
- step,
1022
- `wallet onboarding ${step} failed with ${response.status}`,
1023
- detail
1648
+ return verifiedEd25519Signature({
1649
+ provider: PROVIDER2,
1650
+ encodedSignature: signature,
1651
+ message,
1652
+ walletAddress
1653
+ });
1654
+ },
1655
+ async signTransaction(serializedTransactionBase64) {
1656
+ const data = await rpc2({
1657
+ chain_type: "solana",
1658
+ method: "signTransaction",
1659
+ params: {
1660
+ transaction: serializedTransactionBase64,
1661
+ encoding: "base64"
1662
+ }
1663
+ });
1664
+ const signedTransaction = data.signed_transaction;
1665
+ if (typeof signedTransaction !== "string") {
1666
+ throw new RemoteSigningError(
1667
+ PROVIDER2,
1668
+ "signTransaction returned no signed_transaction",
1669
+ data
1670
+ );
1671
+ }
1672
+ return signedTransaction;
1673
+ }
1674
+ };
1675
+ }
1676
+
1677
+ // ../../src/client/signer-env.ts
1678
+ async function agentWalletSignerFromEnv(env = process.env) {
1679
+ const nonEmpty = (value) => {
1680
+ const trimmed = value?.trim();
1681
+ return trimmed === void 0 || trimmed === "" ? void 0 : trimmed;
1682
+ };
1683
+ const provider = nonEmpty(env.SUBLY_SIGNER_PROVIDER)?.toLowerCase() ?? "local";
1684
+ const pickVar = (name) => nonEmpty(env[`SUBLY_${name}`]) ?? nonEmpty(env[name]);
1685
+ const requireVar = (name) => {
1686
+ const value = pickVar(name);
1687
+ if (value === void 0) {
1688
+ throw new Error(
1689
+ `${name} (or SUBLY_${name}) is required for SUBLY_SIGNER_PROVIDER=${provider}`
1024
1690
  );
1025
1691
  }
1692
+ return value;
1026
1693
  };
1027
- const wallet = params.signer.walletAddress;
1028
- await post("register", "/v1/wallets/agent", {
1029
- wallet,
1030
- signingPolicyId: SELF_SERVE_POLICY_ID,
1031
- signingMode: "non_interactive",
1032
- signerValidationMode: params.signer.validationMode,
1033
- signerProvider: "local-keypair",
1034
- activateForPayments: true
1035
- });
1036
- await post("sync", `/v1/wallets/${wallet}/sync`, { source: "chain" });
1694
+ if (provider === "local") {
1695
+ const localSecretKey = loadSecretKeyBytes({
1696
+ base58Secret: env.SUBLY_DEMO_AGENT_KEYPAIR,
1697
+ jsonFilePath: env.SUBLY_DEMO_AGENT_KEYPAIR_PATH,
1698
+ label: "SUBLY_DEMO_AGENT_KEYPAIR"
1699
+ });
1700
+ return {
1701
+ provider,
1702
+ signer: new LocalKeypairAgentWalletSigner(
1703
+ await createKeyPairSignerFromBytes2(localSecretKey)
1704
+ ),
1705
+ localSecretKey
1706
+ };
1707
+ }
1708
+ if (provider === "circle") {
1709
+ const transport = await createCircleSignerTransport({
1710
+ apiKey: requireVar("CIRCLE_API_KEY"),
1711
+ entitySecret: requireVar("CIRCLE_ENTITY_SECRET"),
1712
+ walletId: requireVar("CIRCLE_WALLET_ID"),
1713
+ baseUrl: pickVar("CIRCLE_BASE_URL")
1714
+ });
1715
+ return { provider, signer: new RemoteAgentWalletSigner(transport), transport };
1716
+ }
1717
+ if (provider === "privy") {
1718
+ const transport = await createPrivySignerTransport({
1719
+ appId: requireVar("PRIVY_APP_ID"),
1720
+ appSecret: requireVar("PRIVY_APP_SECRET"),
1721
+ walletId: requireVar("PRIVY_WALLET_ID"),
1722
+ authorizationPrivateKey: pickVar("PRIVY_AUTHORIZATION_KEY"),
1723
+ baseUrl: pickVar("PRIVY_BASE_URL")
1724
+ });
1725
+ return { provider, signer: new RemoteAgentWalletSigner(transport), transport };
1726
+ }
1727
+ throw new Error(
1728
+ `unknown SUBLY_SIGNER_PROVIDER "${provider}" (expected local, circle, or privy)`
1729
+ );
1037
1730
  }
1038
1731
 
1039
1732
  // ../../src/client/lookup-tables.ts
@@ -1432,31 +2125,6 @@ function parseRelayerError(text) {
1432
2125
  }
1433
2126
  }
1434
2127
 
1435
- // ../../src/solana/keys.ts
1436
- import { readFileSync } from "node:fs";
1437
- import bs586 from "bs58";
1438
- import {
1439
- createKeyPairSignerFromBytes
1440
- } from "@solana/kit";
1441
- async function loadKeyPairSigner(params) {
1442
- const { base58Secret, jsonFilePath, label } = params;
1443
- if (base58Secret !== void 0 && base58Secret.length > 0) {
1444
- const bytes = bs586.decode(base58Secret);
1445
- if (bytes.length !== 64) {
1446
- throw new Error(`${label} base58 secret must decode to 64 bytes`);
1447
- }
1448
- return createKeyPairSignerFromBytes(bytes);
1449
- }
1450
- if (jsonFilePath !== void 0 && jsonFilePath.length > 0) {
1451
- const raw = JSON.parse(readFileSync(jsonFilePath, "utf8"));
1452
- if (!Array.isArray(raw) || raw.length !== 64) {
1453
- throw new Error(`${label} keypair file must be a 64-byte JSON array`);
1454
- }
1455
- return createKeyPairSignerFromBytes(Uint8Array.from(raw));
1456
- }
1457
- throw new Error(`${label} keypair is not configured`);
1458
- }
1459
-
1460
2128
  // ../../src/solana/rpc.ts
1461
2129
  import { createSolanaRpc } from "@solana/kit";
1462
2130
  function createRpc(url) {
@@ -1487,12 +2155,7 @@ var approvalId = process.argv[3];
1487
2155
  if (approvalId !== void 0 && !/^apr_[0-9a-f]+$/i.test(approvalId)) {
1488
2156
  fail(`unrecognized argument: ${approvalId} (expected apr_<approvalId>)`);
1489
2157
  }
1490
- var keyPairSigner = await loadKeyPairSigner({
1491
- base58Secret: process.env.SUBLY_DEMO_AGENT_KEYPAIR,
1492
- jsonFilePath: process.env.SUBLY_DEMO_AGENT_KEYPAIR_PATH,
1493
- label: "SUBLY_DEMO_AGENT_KEYPAIR"
1494
- });
1495
- var signer = new LocalKeypairAgentWalletSigner(keyPairSigner);
2158
+ var { signer } = await agentWalletSignerFromEnv();
1496
2159
  var rpc = createRpc(
1497
2160
  process.env.SOLANA_RPC_URL ?? "https://api.mainnet-beta.solana.com"
1498
2161
  );