@elisym/sdk 0.8.0 → 0.9.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 +31 -10
- package/dist/agent-store.cjs +1 -2
- package/dist/agent-store.cjs.map +1 -1
- package/dist/agent-store.d.cts +14 -16
- package/dist/agent-store.d.ts +14 -16
- package/dist/agent-store.js +1 -2
- package/dist/agent-store.js.map +1 -1
- package/dist/assets-CMf-v55Z.d.cts +58 -0
- package/dist/assets-CMf-v55Z.d.ts +58 -0
- package/dist/global-schema-CddHP2nk.d.cts +62 -0
- package/dist/global-schema-CddHP2nk.d.ts +62 -0
- package/dist/index.cjs +394 -137
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +151 -118
- package/dist/index.d.ts +151 -118
- package/dist/index.js +391 -135
- package/dist/index.js.map +1 -1
- package/dist/node.cjs +60 -0
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +21 -1
- package/dist/node.d.ts +21 -1
- package/dist/node.js +55 -1
- package/dist/node.js.map +1 -1
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.d.cts +14 -0
- package/dist/runtime.d.ts +14 -0
- package/dist/runtime.js.map +1 -1
- package/dist/skills.cjs +123 -10
- package/dist/skills.cjs.map +1 -1
- package/dist/skills.d.cts +17 -4
- package/dist/skills.d.ts +17 -4
- package/dist/skills.js +123 -10
- package/dist/skills.js.map +1 -1
- package/package.json +3 -1
package/dist/index.cjs
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var system = require('@solana-program/system');
|
|
4
|
+
var token = require('@solana-program/token');
|
|
4
5
|
var kit = require('@solana/kit');
|
|
5
6
|
var Decimal2 = require('decimal.js-light');
|
|
6
7
|
var zod = require('zod');
|
|
7
8
|
var nostrTools = require('nostr-tools');
|
|
8
9
|
var nip44 = require('nostr-tools/nip44');
|
|
9
|
-
var promises = require('fs/promises');
|
|
10
|
-
var YAML2 = require('yaml');
|
|
11
|
-
var crypto$1 = require('crypto');
|
|
12
|
-
var path = require('path');
|
|
13
10
|
|
|
14
11
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
15
12
|
|
|
@@ -33,7 +30,6 @@ function _interopNamespace(e) {
|
|
|
33
30
|
|
|
34
31
|
var Decimal2__default = /*#__PURE__*/_interopDefault(Decimal2);
|
|
35
32
|
var nip44__namespace = /*#__PURE__*/_interopNamespace(nip44);
|
|
36
|
-
var YAML2__default = /*#__PURE__*/_interopDefault(YAML2);
|
|
37
33
|
|
|
38
34
|
// src/constants.ts
|
|
39
35
|
var RELAYS = [
|
|
@@ -123,13 +119,13 @@ function decodeConfig(encodedAccount) {
|
|
|
123
119
|
getConfigDecoder()
|
|
124
120
|
);
|
|
125
121
|
}
|
|
126
|
-
async function fetchConfig(rpc,
|
|
127
|
-
const maybeAccount = await fetchMaybeConfig(rpc,
|
|
122
|
+
async function fetchConfig(rpc, address3, config) {
|
|
123
|
+
const maybeAccount = await fetchMaybeConfig(rpc, address3, config);
|
|
128
124
|
kit.assertAccountExists(maybeAccount);
|
|
129
125
|
return maybeAccount;
|
|
130
126
|
}
|
|
131
|
-
async function fetchMaybeConfig(rpc,
|
|
132
|
-
const maybeAccount = await kit.fetchEncodedAccount(rpc,
|
|
127
|
+
async function fetchMaybeConfig(rpc, address3, config) {
|
|
128
|
+
const maybeAccount = await kit.fetchEncodedAccount(rpc, address3, config);
|
|
133
129
|
return decodeConfig(maybeAccount);
|
|
134
130
|
}
|
|
135
131
|
if (process.env.NODE_ENV !== "production") ;
|
|
@@ -180,6 +176,99 @@ async function getProtocolConfig(rpc, programId, options) {
|
|
|
180
176
|
);
|
|
181
177
|
}
|
|
182
178
|
}
|
|
179
|
+
|
|
180
|
+
// src/payment/assets.ts
|
|
181
|
+
var NATIVE_SOL = {
|
|
182
|
+
chain: "solana",
|
|
183
|
+
token: "sol",
|
|
184
|
+
decimals: 9,
|
|
185
|
+
symbol: "SOL"
|
|
186
|
+
};
|
|
187
|
+
var USDC_SOLANA_DEVNET = {
|
|
188
|
+
chain: "solana",
|
|
189
|
+
token: "usdc",
|
|
190
|
+
mint: "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
|
|
191
|
+
decimals: 6,
|
|
192
|
+
symbol: "USDC"
|
|
193
|
+
};
|
|
194
|
+
var KNOWN_ASSETS = [NATIVE_SOL, USDC_SOLANA_DEVNET];
|
|
195
|
+
function assetKey(a) {
|
|
196
|
+
return a.mint ? `${a.chain}:${a.token}:${a.mint}` : `${a.chain}:${a.token}`;
|
|
197
|
+
}
|
|
198
|
+
function resolveKnownAsset(chain, token, mint) {
|
|
199
|
+
const key = mint ? `${chain}:${token}:${mint}` : `${chain}:${token}`;
|
|
200
|
+
return KNOWN_ASSETS.find((asset) => assetKey(asset) === key);
|
|
201
|
+
}
|
|
202
|
+
function assetByKey(key) {
|
|
203
|
+
return KNOWN_ASSETS.find((asset) => assetKey(asset) === key);
|
|
204
|
+
}
|
|
205
|
+
function resolveAssetFromPaymentRequest(request) {
|
|
206
|
+
if (!request.asset) {
|
|
207
|
+
return NATIVE_SOL;
|
|
208
|
+
}
|
|
209
|
+
const found = resolveKnownAsset(request.asset.chain, request.asset.token, request.asset.mint);
|
|
210
|
+
if (!found) {
|
|
211
|
+
const display = request.asset.mint ? `${request.asset.chain}:${request.asset.token}:${request.asset.mint}` : `${request.asset.chain}:${request.asset.token}`;
|
|
212
|
+
throw new Error(
|
|
213
|
+
`Unknown asset in payment request: ${display}. Known assets: ${KNOWN_ASSETS.map(assetKey).join(", ")}`
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
return found;
|
|
217
|
+
}
|
|
218
|
+
var DECIMAL_RE = /^(\d+\.\d*|\d*\.\d+|\d+)$/;
|
|
219
|
+
function parseAssetAmount(asset, human) {
|
|
220
|
+
const trimmed = human.trim();
|
|
221
|
+
if (!trimmed) {
|
|
222
|
+
throw new Error(`${asset.symbol} amount is empty`);
|
|
223
|
+
}
|
|
224
|
+
if (trimmed.startsWith("-")) {
|
|
225
|
+
throw new Error(`${asset.symbol} amount cannot be negative`);
|
|
226
|
+
}
|
|
227
|
+
if (!DECIMAL_RE.test(trimmed)) {
|
|
228
|
+
throw new Error(
|
|
229
|
+
`${asset.symbol} amount must be a non-negative decimal (e.g. "0.5", "1"); got "${human}"`
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
const dotPos = trimmed.indexOf(".");
|
|
233
|
+
let wholePart;
|
|
234
|
+
if (dotPos === -1) {
|
|
235
|
+
wholePart = trimmed;
|
|
236
|
+
} else if (dotPos === 0) {
|
|
237
|
+
wholePart = "0";
|
|
238
|
+
} else {
|
|
239
|
+
wholePart = trimmed.slice(0, dotPos);
|
|
240
|
+
}
|
|
241
|
+
const fracPart = dotPos === -1 ? "" : trimmed.slice(dotPos + 1);
|
|
242
|
+
if (fracPart.length > asset.decimals) {
|
|
243
|
+
throw new Error(
|
|
244
|
+
`${asset.symbol} amount has too many decimals (max ${asset.decimals}); got "${human}"`
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
const unit = 10n ** BigInt(asset.decimals);
|
|
248
|
+
const whole = BigInt(wholePart);
|
|
249
|
+
const frac = fracPart ? BigInt(fracPart.padEnd(asset.decimals, "0")) : 0n;
|
|
250
|
+
const raw = whole * unit + frac;
|
|
251
|
+
if (raw === 0n) {
|
|
252
|
+
throw new Error(`${asset.symbol} amount must be positive; got "${human}"`);
|
|
253
|
+
}
|
|
254
|
+
if (raw > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
255
|
+
throw new Error(
|
|
256
|
+
`${asset.symbol} amount exceeds safe range (max ${Number.MAX_SAFE_INTEGER} subunits)`
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
return raw;
|
|
260
|
+
}
|
|
261
|
+
function formatAssetAmount(asset, raw) {
|
|
262
|
+
const sign = raw < 0n ? "-" : "";
|
|
263
|
+
const abs = raw < 0n ? -raw : raw;
|
|
264
|
+
const unit = 10n ** BigInt(asset.decimals);
|
|
265
|
+
const whole = abs / unit;
|
|
266
|
+
const frac = abs % unit;
|
|
267
|
+
if (asset.decimals === 0) {
|
|
268
|
+
return `${sign}${whole} ${asset.symbol}`;
|
|
269
|
+
}
|
|
270
|
+
return `${sign}${whole}.${frac.toString().padStart(asset.decimals, "0")} ${asset.symbol}`;
|
|
271
|
+
}
|
|
183
272
|
var BPS_DENOMINATOR = 1e4;
|
|
184
273
|
function assertLamports(value, field) {
|
|
185
274
|
if (!Number.isInteger(value) || value < 0) {
|
|
@@ -289,6 +378,12 @@ var SOLANA_ADDRESS_LENGTH_RE = /^.{32,44}$/;
|
|
|
289
378
|
var lamportsSchema = zod.z.number().int().positive().max(MAX_SAFE_LAMPORTS, `amount must be <= ${MAX_SAFE_LAMPORTS}`);
|
|
290
379
|
var feeAmountSchema = zod.z.number().int().nonnegative().max(MAX_SAFE_LAMPORTS, `fee_amount must be <= ${MAX_SAFE_LAMPORTS}`);
|
|
291
380
|
var solanaAddressSchema = zod.z.string().regex(BASE58_RE, "must be base58").regex(SOLANA_ADDRESS_LENGTH_RE, "must be 32-44 base58 chars");
|
|
381
|
+
var paymentAssetRefSchema = zod.z.object({
|
|
382
|
+
chain: zod.z.string().min(1),
|
|
383
|
+
token: zod.z.string().min(1),
|
|
384
|
+
mint: solanaAddressSchema.optional(),
|
|
385
|
+
decimals: zod.z.number().int().min(0).max(18)
|
|
386
|
+
});
|
|
292
387
|
var PaymentRequestSchema = zod.z.object({
|
|
293
388
|
recipient: solanaAddressSchema,
|
|
294
389
|
amount: lamportsSchema,
|
|
@@ -297,7 +392,8 @@ var PaymentRequestSchema = zod.z.object({
|
|
|
297
392
|
fee_address: solanaAddressSchema.optional(),
|
|
298
393
|
fee_amount: feeAmountSchema.optional(),
|
|
299
394
|
created_at: zod.z.number().int().positive(),
|
|
300
|
-
expiry_secs: zod.z.number().int().positive().max(MAX_EXPIRY_SECS_SCHEMA, `expiry_secs must be <= ${MAX_EXPIRY_SECS_SCHEMA}`)
|
|
395
|
+
expiry_secs: zod.z.number().int().positive().max(MAX_EXPIRY_SECS_SCHEMA, `expiry_secs must be <= ${MAX_EXPIRY_SECS_SCHEMA}`),
|
|
396
|
+
asset: paymentAssetRefSchema.optional()
|
|
301
397
|
});
|
|
302
398
|
function parsePaymentRequest(input, options) {
|
|
303
399
|
let parsed;
|
|
@@ -379,6 +475,12 @@ var SolanaPaymentStrategy = class {
|
|
|
379
475
|
assertExpirySecs(expirySecs);
|
|
380
476
|
const feeAmount = calculateProtocolFee(amount, config.feeBps);
|
|
381
477
|
const reference = generateReference();
|
|
478
|
+
const assetRef = options?.asset && options.asset !== NATIVE_SOL ? {
|
|
479
|
+
chain: options.asset.chain,
|
|
480
|
+
token: options.asset.token,
|
|
481
|
+
mint: options.asset.mint,
|
|
482
|
+
decimals: options.asset.decimals
|
|
483
|
+
} : void 0;
|
|
382
484
|
return {
|
|
383
485
|
recipient: recipientAddress,
|
|
384
486
|
amount,
|
|
@@ -386,7 +488,8 @@ var SolanaPaymentStrategy = class {
|
|
|
386
488
|
fee_address: config.treasury,
|
|
387
489
|
fee_amount: feeAmount,
|
|
388
490
|
created_at: Math.floor(Date.now() / 1e3),
|
|
389
|
-
expiry_secs: expirySecs
|
|
491
|
+
expiry_secs: expirySecs,
|
|
492
|
+
...assetRef ? { asset: assetRef } : {}
|
|
390
493
|
};
|
|
391
494
|
}
|
|
392
495
|
validatePaymentRequest(requestJson, config, expectedRecipient, options) {
|
|
@@ -404,6 +507,16 @@ var SolanaPaymentStrategy = class {
|
|
|
404
507
|
return { code: "invalid_amount", message: parsed.error.message };
|
|
405
508
|
}
|
|
406
509
|
const data = parsed.data;
|
|
510
|
+
if (data.asset) {
|
|
511
|
+
try {
|
|
512
|
+
resolveAssetFromPaymentRequest(data);
|
|
513
|
+
} catch (error) {
|
|
514
|
+
return {
|
|
515
|
+
code: "invalid_asset",
|
|
516
|
+
message: error instanceof Error ? error.message : String(error)
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
}
|
|
407
520
|
if (!isValidSolanaAddress(data.recipient)) {
|
|
408
521
|
return {
|
|
409
522
|
code: "invalid_recipient_address",
|
|
@@ -492,7 +605,7 @@ var SolanaPaymentStrategy = class {
|
|
|
492
605
|
if (!Number.isInteger(computeUnitLimit) || computeUnitLimit <= 0) {
|
|
493
606
|
throw new Error(`Invalid computeUnitLimit: ${computeUnitLimit}. Must be a positive integer.`);
|
|
494
607
|
}
|
|
495
|
-
const paymentInstructions = buildPaymentInstructions(paymentRequest, payerSigner);
|
|
608
|
+
const paymentInstructions = await buildPaymentInstructions(paymentRequest, payerSigner);
|
|
496
609
|
const priorityFeeMicroLamports = options?.priorityFeeMicroLamports ?? await estimatePriorityFeeMicroLamports(rpc, {
|
|
497
610
|
percentile: options?.priorityFeePercentile ?? DEFAULT_PRIORITY_FEE_PERCENTILE
|
|
498
611
|
});
|
|
@@ -554,6 +667,16 @@ var SolanaPaymentStrategy = class {
|
|
|
554
667
|
error: `Fee amount (${feeAmount}) exceeds or equals total amount (${paymentRequest.amount})`
|
|
555
668
|
};
|
|
556
669
|
}
|
|
670
|
+
let asset;
|
|
671
|
+
try {
|
|
672
|
+
asset = resolveAssetFromPaymentRequest(paymentRequest);
|
|
673
|
+
} catch (error) {
|
|
674
|
+
return {
|
|
675
|
+
verified: false,
|
|
676
|
+
error: error instanceof Error ? error.message : String(error)
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
const mint = asset.mint;
|
|
557
680
|
if (options?.txSignature) {
|
|
558
681
|
return this._verifyBySignature(
|
|
559
682
|
rpc,
|
|
@@ -563,6 +686,7 @@ var SolanaPaymentStrategy = class {
|
|
|
563
686
|
treasury,
|
|
564
687
|
expectedNet,
|
|
565
688
|
feeAmount,
|
|
689
|
+
mint,
|
|
566
690
|
options?.retries ?? DEFAULTS.VERIFY_RETRIES,
|
|
567
691
|
options?.intervalMs ?? DEFAULTS.VERIFY_INTERVAL_MS
|
|
568
692
|
);
|
|
@@ -574,11 +698,12 @@ var SolanaPaymentStrategy = class {
|
|
|
574
698
|
treasury,
|
|
575
699
|
expectedNet,
|
|
576
700
|
feeAmount,
|
|
701
|
+
mint,
|
|
577
702
|
options?.retries ?? DEFAULTS.VERIFY_BY_REF_RETRIES,
|
|
578
703
|
options?.intervalMs ?? DEFAULTS.VERIFY_BY_REF_INTERVAL_MS
|
|
579
704
|
);
|
|
580
705
|
}
|
|
581
|
-
async _verifyBySignature(rpc, txSignature, referenceKey, recipientAddress, treasuryAddress, expectedNet, expectedFee, retries, intervalMs) {
|
|
706
|
+
async _verifyBySignature(rpc, txSignature, referenceKey, recipientAddress, treasuryAddress, expectedNet, expectedFee, mint, retries, intervalMs) {
|
|
582
707
|
let lastError;
|
|
583
708
|
for (let attempt = 0; attempt < retries; attempt++) {
|
|
584
709
|
try {
|
|
@@ -597,15 +722,18 @@ var SolanaPaymentStrategy = class {
|
|
|
597
722
|
error: tx?.meta?.err ? "Transaction failed on-chain" : "Transaction not found"
|
|
598
723
|
};
|
|
599
724
|
}
|
|
600
|
-
const verdict =
|
|
725
|
+
const verdict = checkTxDiff({
|
|
601
726
|
accountKeys: tx.transaction.message.accountKeys,
|
|
602
727
|
preBalances: tx.meta.preBalances,
|
|
603
728
|
postBalances: tx.meta.postBalances,
|
|
729
|
+
preTokenBalances: tx.meta.preTokenBalances,
|
|
730
|
+
postTokenBalances: tx.meta.postTokenBalances,
|
|
604
731
|
referenceKey,
|
|
605
732
|
recipientAddress,
|
|
606
733
|
treasuryAddress,
|
|
607
734
|
expectedNet,
|
|
608
|
-
expectedFee
|
|
735
|
+
expectedFee,
|
|
736
|
+
mint
|
|
609
737
|
});
|
|
610
738
|
if (verdict.ok) {
|
|
611
739
|
return { verified: true, txSignature };
|
|
@@ -623,7 +751,7 @@ var SolanaPaymentStrategy = class {
|
|
|
623
751
|
error: `Verification failed after ${retries} retries: ${lastError instanceof Error ? lastError.message : "unknown error"}`
|
|
624
752
|
};
|
|
625
753
|
}
|
|
626
|
-
async _verifyByReference(rpc, referenceKey, recipientAddress, treasuryAddress, expectedNet, expectedFee, retries, intervalMs) {
|
|
754
|
+
async _verifyByReference(rpc, referenceKey, recipientAddress, treasuryAddress, expectedNet, expectedFee, mint, retries, intervalMs) {
|
|
627
755
|
let lastError;
|
|
628
756
|
const reference = kit.address(referenceKey);
|
|
629
757
|
for (let attempt = 0; attempt < retries; attempt++) {
|
|
@@ -647,15 +775,18 @@ var SolanaPaymentStrategy = class {
|
|
|
647
775
|
if (!tx?.meta || tx.meta.err) {
|
|
648
776
|
continue;
|
|
649
777
|
}
|
|
650
|
-
const verdict =
|
|
778
|
+
const verdict = checkTxDiff({
|
|
651
779
|
accountKeys: tx.transaction.message.accountKeys,
|
|
652
780
|
preBalances: tx.meta.preBalances,
|
|
653
781
|
postBalances: tx.meta.postBalances,
|
|
782
|
+
preTokenBalances: tx.meta.preTokenBalances,
|
|
783
|
+
postTokenBalances: tx.meta.postTokenBalances,
|
|
654
784
|
referenceKey,
|
|
655
785
|
recipientAddress,
|
|
656
786
|
treasuryAddress,
|
|
657
787
|
expectedNet,
|
|
658
|
-
expectedFee
|
|
788
|
+
expectedFee,
|
|
789
|
+
mint
|
|
659
790
|
});
|
|
660
791
|
if (verdict.ok) {
|
|
661
792
|
return { verified: true, txSignature: sig };
|
|
@@ -675,7 +806,7 @@ var SolanaPaymentStrategy = class {
|
|
|
675
806
|
};
|
|
676
807
|
}
|
|
677
808
|
};
|
|
678
|
-
function
|
|
809
|
+
function checkTxDiff(input) {
|
|
679
810
|
const balanceCount = input.preBalances.length;
|
|
680
811
|
const keyToIdx = /* @__PURE__ */ new Map();
|
|
681
812
|
for (let i = 0; i < Math.min(input.accountKeys.length, balanceCount); i++) {
|
|
@@ -687,6 +818,9 @@ function checkBalanceDiff(input) {
|
|
|
687
818
|
if (!keyToIdx.has(input.referenceKey)) {
|
|
688
819
|
return { ok: false, reason: "Reference key not found in transaction - possible replay" };
|
|
689
820
|
}
|
|
821
|
+
if (input.mint) {
|
|
822
|
+
return checkTokenBalanceDiff(input);
|
|
823
|
+
}
|
|
690
824
|
const recipientIdx = keyToIdx.get(input.recipientAddress);
|
|
691
825
|
if (recipientIdx === void 0) {
|
|
692
826
|
return { ok: false, reason: "Recipient not found in transaction" };
|
|
@@ -719,6 +853,47 @@ function checkBalanceDiff(input) {
|
|
|
719
853
|
}
|
|
720
854
|
return { ok: true };
|
|
721
855
|
}
|
|
856
|
+
function checkTokenBalanceDiff(input) {
|
|
857
|
+
const mint = input.mint;
|
|
858
|
+
if (!mint) {
|
|
859
|
+
return { ok: false, reason: "Expected mint for SPL verification, got none" };
|
|
860
|
+
}
|
|
861
|
+
const pre = input.preTokenBalances ?? [];
|
|
862
|
+
const post = input.postTokenBalances ?? [];
|
|
863
|
+
const tokenDelta = (ownerAddress) => {
|
|
864
|
+
const preEntry = pre.find((entry) => entry.owner === ownerAddress && entry.mint === mint);
|
|
865
|
+
const postEntry = post.find((entry) => entry.owner === ownerAddress && entry.mint === mint);
|
|
866
|
+
if (!postEntry) {
|
|
867
|
+
return -1n;
|
|
868
|
+
}
|
|
869
|
+
const preAmount = preEntry ? BigInt(preEntry.uiTokenAmount.amount) : 0n;
|
|
870
|
+
const postAmount = BigInt(postEntry.uiTokenAmount.amount);
|
|
871
|
+
return postAmount - preAmount;
|
|
872
|
+
};
|
|
873
|
+
const recipientDelta = tokenDelta(input.recipientAddress);
|
|
874
|
+
if (recipientDelta === -1n) {
|
|
875
|
+
return { ok: false, reason: "Recipient token account not found in transaction" };
|
|
876
|
+
}
|
|
877
|
+
if (recipientDelta < BigInt(input.expectedNet)) {
|
|
878
|
+
return {
|
|
879
|
+
ok: false,
|
|
880
|
+
reason: `Recipient received ${recipientDelta.toString()} tokens, expected >= ${input.expectedNet}`
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
if (input.expectedFee > 0) {
|
|
884
|
+
const treasuryDelta = tokenDelta(input.treasuryAddress);
|
|
885
|
+
if (treasuryDelta === -1n) {
|
|
886
|
+
return { ok: false, reason: "Treasury token account not found in transaction" };
|
|
887
|
+
}
|
|
888
|
+
if (treasuryDelta < BigInt(input.expectedFee)) {
|
|
889
|
+
return {
|
|
890
|
+
ok: false,
|
|
891
|
+
reason: `Treasury received ${treasuryDelta.toString()} tokens, expected >= ${input.expectedFee}`
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
return { ok: true };
|
|
896
|
+
}
|
|
722
897
|
function bigIntDelta(post, pre) {
|
|
723
898
|
const postValue = post === void 0 ? 0n : BigInt(post);
|
|
724
899
|
const preValue = pre === void 0 ? 0n : BigInt(pre);
|
|
@@ -727,7 +902,7 @@ function bigIntDelta(post, pre) {
|
|
|
727
902
|
function waitMs(ms) {
|
|
728
903
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
729
904
|
}
|
|
730
|
-
function buildPaymentInstructions(paymentRequest, payerSigner) {
|
|
905
|
+
async function buildPaymentInstructions(paymentRequest, payerSigner) {
|
|
731
906
|
const recipient = kit.address(paymentRequest.recipient);
|
|
732
907
|
const reference = kit.address(paymentRequest.reference);
|
|
733
908
|
const feeAmount = paymentRequest.fee_amount ?? 0;
|
|
@@ -737,22 +912,98 @@ function buildPaymentInstructions(paymentRequest, payerSigner) {
|
|
|
737
912
|
`Fee amount (${feeAmount}) exceeds or equals total amount (${paymentRequest.amount}). Cannot create transaction with non-positive provider amount.`
|
|
738
913
|
);
|
|
739
914
|
}
|
|
740
|
-
const
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
915
|
+
const asset = resolveAssetFromPaymentRequest(paymentRequest);
|
|
916
|
+
if (!asset.mint) {
|
|
917
|
+
const providerTransferIx2 = system.getTransferSolInstruction({
|
|
918
|
+
source: payerSigner,
|
|
919
|
+
destination: recipient,
|
|
920
|
+
amount: BigInt(providerAmount)
|
|
921
|
+
});
|
|
922
|
+
const providerTransferIxWithReference2 = {
|
|
923
|
+
...providerTransferIx2,
|
|
924
|
+
accounts: [
|
|
925
|
+
...providerTransferIx2.accounts,
|
|
926
|
+
{ address: reference, role: kit.AccountRole.READONLY }
|
|
927
|
+
]
|
|
928
|
+
};
|
|
929
|
+
const instructions2 = [providerTransferIxWithReference2];
|
|
930
|
+
if (paymentRequest.fee_address && feeAmount > 0) {
|
|
931
|
+
instructions2.push(
|
|
932
|
+
system.getTransferSolInstruction({
|
|
933
|
+
source: payerSigner,
|
|
934
|
+
destination: kit.address(paymentRequest.fee_address),
|
|
935
|
+
amount: BigInt(feeAmount)
|
|
936
|
+
})
|
|
937
|
+
);
|
|
938
|
+
}
|
|
939
|
+
return instructions2;
|
|
940
|
+
}
|
|
941
|
+
const mint = kit.address(asset.mint);
|
|
942
|
+
const payerAddress = payerSigner.address;
|
|
943
|
+
const [payerAta] = await token.findAssociatedTokenPda({
|
|
944
|
+
owner: payerAddress,
|
|
945
|
+
tokenProgram: token.TOKEN_PROGRAM_ADDRESS,
|
|
946
|
+
mint
|
|
947
|
+
});
|
|
948
|
+
const [recipientAta] = await token.findAssociatedTokenPda({
|
|
949
|
+
owner: recipient,
|
|
950
|
+
tokenProgram: token.TOKEN_PROGRAM_ADDRESS,
|
|
951
|
+
mint
|
|
952
|
+
});
|
|
953
|
+
const instructions = [];
|
|
954
|
+
instructions.push(
|
|
955
|
+
token.getCreateAssociatedTokenIdempotentInstruction(
|
|
956
|
+
{
|
|
957
|
+
payer: payerSigner,
|
|
958
|
+
ata: recipientAta,
|
|
959
|
+
owner: recipient,
|
|
960
|
+
mint
|
|
961
|
+
},
|
|
962
|
+
{ programAddress: token.ASSOCIATED_TOKEN_PROGRAM_ADDRESS }
|
|
963
|
+
)
|
|
964
|
+
);
|
|
965
|
+
let treasuryAta;
|
|
966
|
+
if (paymentRequest.fee_address && feeAmount > 0) {
|
|
967
|
+
const treasuryOwner = kit.address(paymentRequest.fee_address);
|
|
968
|
+
[treasuryAta] = await token.findAssociatedTokenPda({
|
|
969
|
+
owner: treasuryOwner,
|
|
970
|
+
tokenProgram: token.TOKEN_PROGRAM_ADDRESS,
|
|
971
|
+
mint
|
|
972
|
+
});
|
|
973
|
+
instructions.push(
|
|
974
|
+
token.getCreateAssociatedTokenIdempotentInstruction(
|
|
975
|
+
{
|
|
976
|
+
payer: payerSigner,
|
|
977
|
+
ata: treasuryAta,
|
|
978
|
+
owner: treasuryOwner,
|
|
979
|
+
mint
|
|
980
|
+
},
|
|
981
|
+
{ programAddress: token.ASSOCIATED_TOKEN_PROGRAM_ADDRESS }
|
|
982
|
+
)
|
|
983
|
+
);
|
|
984
|
+
}
|
|
985
|
+
const providerTransferIx = token.getTransferCheckedInstruction({
|
|
986
|
+
source: payerAta,
|
|
987
|
+
mint,
|
|
988
|
+
destination: recipientAta,
|
|
989
|
+
authority: payerSigner,
|
|
990
|
+
amount: BigInt(providerAmount),
|
|
991
|
+
decimals: asset.decimals
|
|
744
992
|
});
|
|
745
993
|
const providerTransferIxWithReference = {
|
|
746
994
|
...providerTransferIx,
|
|
747
995
|
accounts: [...providerTransferIx.accounts, { address: reference, role: kit.AccountRole.READONLY }]
|
|
748
996
|
};
|
|
749
|
-
|
|
750
|
-
if (paymentRequest.fee_address && feeAmount > 0) {
|
|
997
|
+
instructions.push(providerTransferIxWithReference);
|
|
998
|
+
if (treasuryAta && paymentRequest.fee_address && feeAmount > 0) {
|
|
751
999
|
instructions.push(
|
|
752
|
-
|
|
753
|
-
source:
|
|
754
|
-
|
|
755
|
-
|
|
1000
|
+
token.getTransferCheckedInstruction({
|
|
1001
|
+
source: payerAta,
|
|
1002
|
+
mint,
|
|
1003
|
+
destination: treasuryAta,
|
|
1004
|
+
authority: payerSigner,
|
|
1005
|
+
amount: BigInt(feeAmount),
|
|
1006
|
+
decimals: asset.decimals
|
|
756
1007
|
})
|
|
757
1008
|
);
|
|
758
1009
|
}
|
|
@@ -2361,96 +2612,124 @@ var ElisymClient = class {
|
|
|
2361
2612
|
this.pool.close();
|
|
2362
2613
|
}
|
|
2363
2614
|
};
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
var
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
)
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
}
|
|
2406
|
-
const fracPart = dotPos === -1 ? "" : trimmed.slice(dotPos + 1);
|
|
2407
|
-
if (fracPart.length > asset.decimals) {
|
|
2408
|
-
throw new Error(
|
|
2409
|
-
`${asset.symbol} amount has too many decimals (max ${asset.decimals}); got "${human}"`
|
|
2410
|
-
);
|
|
2411
|
-
}
|
|
2412
|
-
const unit = 10n ** BigInt(asset.decimals);
|
|
2413
|
-
const whole = BigInt(wholePart);
|
|
2414
|
-
const frac = fracPart ? BigInt(fracPart.padEnd(asset.decimals, "0")) : 0n;
|
|
2415
|
-
const raw = whole * unit + frac;
|
|
2416
|
-
if (raw === 0n) {
|
|
2417
|
-
throw new Error(`${asset.symbol} amount must be positive; got "${human}"`);
|
|
2418
|
-
}
|
|
2419
|
-
if (raw > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
2420
|
-
throw new Error(
|
|
2421
|
-
`${asset.symbol} amount exceeds safe range (max ${Number.MAX_SAFE_INTEGER} subunits)`
|
|
2422
|
-
);
|
|
2615
|
+
var DEFAULT_COMPUTE_UNIT_LIMIT2 = 2e5;
|
|
2616
|
+
var DEFAULT_PRIORITY_FEE_PERCENTILE2 = 75;
|
|
2617
|
+
var BASE_FEE_LAMPORTS_PER_SIGNATURE = 5000n;
|
|
2618
|
+
var FALLBACK_ATA_RENT_LAMPORTS = 2039280n;
|
|
2619
|
+
var SPL_TOKEN_ACCOUNT_SIZE = 165;
|
|
2620
|
+
async function estimateSolFeeLamports(rpc, paymentRequest, _payerAddress, options) {
|
|
2621
|
+
const numSignatures = options?.numSignatures ?? 1;
|
|
2622
|
+
const computeUnitLimit = options?.computeUnitLimit ?? DEFAULT_COMPUTE_UNIT_LIMIT2;
|
|
2623
|
+
const priorityFeeMicroLamports = options?.priorityFeeMicroLamports ?? await estimatePriorityFeeMicroLamports(rpc, {
|
|
2624
|
+
percentile: options?.priorityFeePercentile ?? DEFAULT_PRIORITY_FEE_PERCENTILE2
|
|
2625
|
+
});
|
|
2626
|
+
const baseFeeLamports = BASE_FEE_LAMPORTS_PER_SIGNATURE * BigInt(numSignatures);
|
|
2627
|
+
const priorityFeeLamports = ceilDiv(
|
|
2628
|
+
priorityFeeMicroLamports * BigInt(computeUnitLimit),
|
|
2629
|
+
1000000n
|
|
2630
|
+
);
|
|
2631
|
+
const asset = resolveAssetFromPaymentRequest(paymentRequest);
|
|
2632
|
+
let rentLamports = 0n;
|
|
2633
|
+
let rentPerAtaLamports = 0n;
|
|
2634
|
+
let missingAtaCount = 0;
|
|
2635
|
+
if (asset.mint) {
|
|
2636
|
+
rentPerAtaLamports = await fetchAtaRent(rpc);
|
|
2637
|
+
const mint = kit.address(asset.mint);
|
|
2638
|
+
const ataAccountsToCheck = [];
|
|
2639
|
+
const [recipientAta] = await token.findAssociatedTokenPda({
|
|
2640
|
+
owner: kit.address(paymentRequest.recipient),
|
|
2641
|
+
tokenProgram: token.TOKEN_PROGRAM_ADDRESS,
|
|
2642
|
+
mint
|
|
2643
|
+
});
|
|
2644
|
+
ataAccountsToCheck.push(recipientAta);
|
|
2645
|
+
const feeAmount = paymentRequest.fee_amount ?? 0;
|
|
2646
|
+
if (paymentRequest.fee_address && feeAmount > 0) {
|
|
2647
|
+
const [treasuryAta] = await token.findAssociatedTokenPda({
|
|
2648
|
+
owner: kit.address(paymentRequest.fee_address),
|
|
2649
|
+
tokenProgram: token.TOKEN_PROGRAM_ADDRESS,
|
|
2650
|
+
mint
|
|
2651
|
+
});
|
|
2652
|
+
ataAccountsToCheck.push(treasuryAta);
|
|
2653
|
+
}
|
|
2654
|
+
missingAtaCount = await countMissingAccounts(rpc, ataAccountsToCheck);
|
|
2655
|
+
rentLamports = rentPerAtaLamports * BigInt(missingAtaCount);
|
|
2423
2656
|
}
|
|
2424
|
-
|
|
2657
|
+
const totalLamports = baseFeeLamports + priorityFeeLamports + rentLamports;
|
|
2658
|
+
return {
|
|
2659
|
+
baseFeeLamports,
|
|
2660
|
+
priorityFeeLamports,
|
|
2661
|
+
rentLamports,
|
|
2662
|
+
totalLamports,
|
|
2663
|
+
breakdown: {
|
|
2664
|
+
numSignatures,
|
|
2665
|
+
priorityFeeMicroLamports,
|
|
2666
|
+
computeUnitLimit,
|
|
2667
|
+
rentPerAtaLamports,
|
|
2668
|
+
missingAtaCount
|
|
2669
|
+
}
|
|
2670
|
+
};
|
|
2425
2671
|
}
|
|
2426
|
-
function
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2672
|
+
async function fetchAtaRent(rpc) {
|
|
2673
|
+
try {
|
|
2674
|
+
const lamports = await rpc.getMinimumBalanceForRentExemption(BigInt(SPL_TOKEN_ACCOUNT_SIZE)).send();
|
|
2675
|
+
if (typeof lamports === "bigint") {
|
|
2676
|
+
return lamports;
|
|
2677
|
+
}
|
|
2678
|
+
if (typeof lamports === "number" && Number.isFinite(lamports) && lamports > 0) {
|
|
2679
|
+
return BigInt(lamports);
|
|
2680
|
+
}
|
|
2681
|
+
return FALLBACK_ATA_RENT_LAMPORTS;
|
|
2682
|
+
} catch {
|
|
2683
|
+
return FALLBACK_ATA_RENT_LAMPORTS;
|
|
2434
2684
|
}
|
|
2435
|
-
return `${sign}${whole}.${frac.toString().padStart(asset.decimals, "0")} ${asset.symbol}`;
|
|
2436
2685
|
}
|
|
2437
|
-
async function
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
} catch (e) {
|
|
2686
|
+
async function countMissingAccounts(rpc, accounts) {
|
|
2687
|
+
if (accounts.length === 0) {
|
|
2688
|
+
return 0;
|
|
2689
|
+
}
|
|
2690
|
+
let missing = 0;
|
|
2691
|
+
for (const acct of accounts) {
|
|
2444
2692
|
try {
|
|
2445
|
-
const
|
|
2446
|
-
|
|
2693
|
+
const res = await rpc.getAccountInfo(acct, { encoding: "base64" }).send();
|
|
2694
|
+
if (!res || !res.value) {
|
|
2695
|
+
missing++;
|
|
2696
|
+
}
|
|
2447
2697
|
} catch {
|
|
2698
|
+
missing++;
|
|
2448
2699
|
}
|
|
2449
|
-
throw e;
|
|
2450
2700
|
}
|
|
2701
|
+
return missing;
|
|
2702
|
+
}
|
|
2703
|
+
function ceilDiv(num, denom) {
|
|
2704
|
+
if (denom === 0n) {
|
|
2705
|
+
throw new Error("division by zero in ceilDiv");
|
|
2706
|
+
}
|
|
2707
|
+
const q = num / denom;
|
|
2708
|
+
const r = num % denom;
|
|
2709
|
+
return r === 0n ? q : q + 1n;
|
|
2710
|
+
}
|
|
2711
|
+
function formatFeeBreakdown(estimate) {
|
|
2712
|
+
const line = (label, lamports) => {
|
|
2713
|
+
const label16 = label.padEnd(14);
|
|
2714
|
+
return ` ${label16}${lamports.toString()} lamports (${lamportsToSol(lamports)} SOL)`;
|
|
2715
|
+
};
|
|
2716
|
+
const lines = [
|
|
2717
|
+
"Estimated SOL cost for this transaction:",
|
|
2718
|
+
line("Base fee:", estimate.baseFeeLamports),
|
|
2719
|
+
line("Priority fee:", estimate.priorityFeeLamports)
|
|
2720
|
+
];
|
|
2721
|
+
if (estimate.rentLamports > 0n) {
|
|
2722
|
+
lines.push(line("ATA rent:", estimate.rentLamports));
|
|
2723
|
+
}
|
|
2724
|
+
lines.push(line("Total:", estimate.totalLamports));
|
|
2725
|
+
return lines.join("\n");
|
|
2726
|
+
}
|
|
2727
|
+
function lamportsToSol(lamports) {
|
|
2728
|
+
const LAMPORTS_PER_SOL2 = 1000000000n;
|
|
2729
|
+
const whole = lamports / LAMPORTS_PER_SOL2;
|
|
2730
|
+
const frac = lamports % LAMPORTS_PER_SOL2;
|
|
2731
|
+
return `${whole}.${frac.toString().padStart(9, "0")}`;
|
|
2451
2732
|
}
|
|
2452
|
-
|
|
2453
|
-
// src/config/global.ts
|
|
2454
2733
|
var SessionSpendLimitEntrySchema = zod.z.object({
|
|
2455
2734
|
chain: zod.z.enum(["solana"]),
|
|
2456
2735
|
token: zod.z.string().min(1).max(16).regex(/^[a-z0-9]+$/, "token must be lowercase alphanumeric"),
|
|
@@ -2460,30 +2739,6 @@ var SessionSpendLimitEntrySchema = zod.z.object({
|
|
|
2460
2739
|
var GlobalConfigSchema = zod.z.object({
|
|
2461
2740
|
session_spend_limits: zod.z.array(SessionSpendLimitEntrySchema).max(16).optional()
|
|
2462
2741
|
}).strict();
|
|
2463
|
-
function isEnoent(e) {
|
|
2464
|
-
return typeof e === "object" && e !== null && "code" in e && e.code === "ENOENT";
|
|
2465
|
-
}
|
|
2466
|
-
async function loadGlobalConfig(path) {
|
|
2467
|
-
let raw;
|
|
2468
|
-
try {
|
|
2469
|
-
raw = await promises.readFile(path, "utf-8");
|
|
2470
|
-
} catch (e) {
|
|
2471
|
-
if (isEnoent(e)) {
|
|
2472
|
-
return {};
|
|
2473
|
-
}
|
|
2474
|
-
throw e;
|
|
2475
|
-
}
|
|
2476
|
-
if (raw.trim() === "") {
|
|
2477
|
-
return {};
|
|
2478
|
-
}
|
|
2479
|
-
const parsed = YAML2__default.default.parse(raw);
|
|
2480
|
-
return GlobalConfigSchema.parse(parsed ?? {});
|
|
2481
|
-
}
|
|
2482
|
-
async function writeGlobalConfig(path, config) {
|
|
2483
|
-
const validated = GlobalConfigSchema.parse(config);
|
|
2484
|
-
const body = YAML2__default.default.stringify(validated);
|
|
2485
|
-
await writeFileAtomic(path, body, 420);
|
|
2486
|
-
}
|
|
2487
2742
|
function formatSol(lamports) {
|
|
2488
2743
|
const sol = new Decimal2__default.default(lamports).div(LAMPORTS_PER_SOL);
|
|
2489
2744
|
if (sol.gte(1e6)) {
|
|
@@ -2708,6 +2963,7 @@ exports.RELAYS = RELAYS;
|
|
|
2708
2963
|
exports.SECRET_REDACT_PATHS = SECRET_REDACT_PATHS;
|
|
2709
2964
|
exports.SessionSpendLimitEntrySchema = SessionSpendLimitEntrySchema;
|
|
2710
2965
|
exports.SolanaPaymentStrategy = SolanaPaymentStrategy;
|
|
2966
|
+
exports.USDC_SOLANA_DEVNET = USDC_SOLANA_DEVNET;
|
|
2711
2967
|
exports.assertExpiry = assertExpiry;
|
|
2712
2968
|
exports.assertLamports = assertLamports;
|
|
2713
2969
|
exports.assetByKey = assetByKey;
|
|
@@ -2719,25 +2975,26 @@ exports.clearProtocolConfigCache = clearProtocolConfigCache;
|
|
|
2719
2975
|
exports.createPaymentRequestWithOnchainConfig = createPaymentRequestWithOnchainConfig;
|
|
2720
2976
|
exports.createSlidingWindowLimiter = createSlidingWindowLimiter;
|
|
2721
2977
|
exports.estimatePriorityFeeMicroLamports = estimatePriorityFeeMicroLamports;
|
|
2978
|
+
exports.estimateSolFeeLamports = estimateSolFeeLamports;
|
|
2722
2979
|
exports.formatAssetAmount = formatAssetAmount;
|
|
2980
|
+
exports.formatFeeBreakdown = formatFeeBreakdown;
|
|
2723
2981
|
exports.formatSol = formatSol;
|
|
2724
2982
|
exports.getProtocolConfig = getProtocolConfig;
|
|
2725
2983
|
exports.getProtocolProgramId = getProtocolProgramId;
|
|
2726
2984
|
exports.jobRequestKind = jobRequestKind;
|
|
2727
2985
|
exports.jobResultKind = jobResultKind;
|
|
2728
|
-
exports.loadGlobalConfig = loadGlobalConfig;
|
|
2729
2986
|
exports.makeCensor = makeCensor;
|
|
2730
2987
|
exports.nip44Decrypt = nip44Decrypt;
|
|
2731
2988
|
exports.nip44Encrypt = nip44Encrypt;
|
|
2732
2989
|
exports.parseAssetAmount = parseAssetAmount;
|
|
2733
2990
|
exports.parsePaymentRequest = parsePaymentRequest;
|
|
2734
2991
|
exports.pickPercentileFee = pickPercentileFee;
|
|
2992
|
+
exports.resolveAssetFromPaymentRequest = resolveAssetFromPaymentRequest;
|
|
2735
2993
|
exports.resolveKnownAsset = resolveKnownAsset;
|
|
2736
2994
|
exports.timeAgo = timeAgo;
|
|
2737
2995
|
exports.toDTag = toDTag;
|
|
2738
2996
|
exports.truncateKey = truncateKey;
|
|
2739
2997
|
exports.validateAgentName = validateAgentName;
|
|
2740
2998
|
exports.validateExpiry = validateExpiry;
|
|
2741
|
-
exports.writeGlobalConfig = writeGlobalConfig;
|
|
2742
2999
|
//# sourceMappingURL=index.cjs.map
|
|
2743
3000
|
//# sourceMappingURL=index.cjs.map
|