@elisym/sdk 0.7.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 +5 -2
- package/dist/agent-store.cjs.map +1 -1
- package/dist/agent-store.d.cts +9 -9
- package/dist/agent-store.d.ts +9 -9
- package/dist/agent-store.js +5 -3
- 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 +421 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +151 -9
- package/dist/index.d.ts +151 -9
- package/dist/index.js +409 -26
- 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,6 +1,7 @@
|
|
|
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');
|
|
@@ -118,13 +119,13 @@ function decodeConfig(encodedAccount) {
|
|
|
118
119
|
getConfigDecoder()
|
|
119
120
|
);
|
|
120
121
|
}
|
|
121
|
-
async function fetchConfig(rpc,
|
|
122
|
-
const maybeAccount = await fetchMaybeConfig(rpc,
|
|
122
|
+
async function fetchConfig(rpc, address3, config) {
|
|
123
|
+
const maybeAccount = await fetchMaybeConfig(rpc, address3, config);
|
|
123
124
|
kit.assertAccountExists(maybeAccount);
|
|
124
125
|
return maybeAccount;
|
|
125
126
|
}
|
|
126
|
-
async function fetchMaybeConfig(rpc,
|
|
127
|
-
const maybeAccount = await kit.fetchEncodedAccount(rpc,
|
|
127
|
+
async function fetchMaybeConfig(rpc, address3, config) {
|
|
128
|
+
const maybeAccount = await kit.fetchEncodedAccount(rpc, address3, config);
|
|
128
129
|
return decodeConfig(maybeAccount);
|
|
129
130
|
}
|
|
130
131
|
if (process.env.NODE_ENV !== "production") ;
|
|
@@ -175,6 +176,99 @@ async function getProtocolConfig(rpc, programId, options) {
|
|
|
175
176
|
);
|
|
176
177
|
}
|
|
177
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
|
+
}
|
|
178
272
|
var BPS_DENOMINATOR = 1e4;
|
|
179
273
|
function assertLamports(value, field) {
|
|
180
274
|
if (!Number.isInteger(value) || value < 0) {
|
|
@@ -284,6 +378,12 @@ var SOLANA_ADDRESS_LENGTH_RE = /^.{32,44}$/;
|
|
|
284
378
|
var lamportsSchema = zod.z.number().int().positive().max(MAX_SAFE_LAMPORTS, `amount must be <= ${MAX_SAFE_LAMPORTS}`);
|
|
285
379
|
var feeAmountSchema = zod.z.number().int().nonnegative().max(MAX_SAFE_LAMPORTS, `fee_amount must be <= ${MAX_SAFE_LAMPORTS}`);
|
|
286
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
|
+
});
|
|
287
387
|
var PaymentRequestSchema = zod.z.object({
|
|
288
388
|
recipient: solanaAddressSchema,
|
|
289
389
|
amount: lamportsSchema,
|
|
@@ -292,7 +392,8 @@ var PaymentRequestSchema = zod.z.object({
|
|
|
292
392
|
fee_address: solanaAddressSchema.optional(),
|
|
293
393
|
fee_amount: feeAmountSchema.optional(),
|
|
294
394
|
created_at: zod.z.number().int().positive(),
|
|
295
|
-
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()
|
|
296
397
|
});
|
|
297
398
|
function parsePaymentRequest(input, options) {
|
|
298
399
|
let parsed;
|
|
@@ -374,6 +475,12 @@ var SolanaPaymentStrategy = class {
|
|
|
374
475
|
assertExpirySecs(expirySecs);
|
|
375
476
|
const feeAmount = calculateProtocolFee(amount, config.feeBps);
|
|
376
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;
|
|
377
484
|
return {
|
|
378
485
|
recipient: recipientAddress,
|
|
379
486
|
amount,
|
|
@@ -381,7 +488,8 @@ var SolanaPaymentStrategy = class {
|
|
|
381
488
|
fee_address: config.treasury,
|
|
382
489
|
fee_amount: feeAmount,
|
|
383
490
|
created_at: Math.floor(Date.now() / 1e3),
|
|
384
|
-
expiry_secs: expirySecs
|
|
491
|
+
expiry_secs: expirySecs,
|
|
492
|
+
...assetRef ? { asset: assetRef } : {}
|
|
385
493
|
};
|
|
386
494
|
}
|
|
387
495
|
validatePaymentRequest(requestJson, config, expectedRecipient, options) {
|
|
@@ -399,6 +507,16 @@ var SolanaPaymentStrategy = class {
|
|
|
399
507
|
return { code: "invalid_amount", message: parsed.error.message };
|
|
400
508
|
}
|
|
401
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
|
+
}
|
|
402
520
|
if (!isValidSolanaAddress(data.recipient)) {
|
|
403
521
|
return {
|
|
404
522
|
code: "invalid_recipient_address",
|
|
@@ -487,7 +605,7 @@ var SolanaPaymentStrategy = class {
|
|
|
487
605
|
if (!Number.isInteger(computeUnitLimit) || computeUnitLimit <= 0) {
|
|
488
606
|
throw new Error(`Invalid computeUnitLimit: ${computeUnitLimit}. Must be a positive integer.`);
|
|
489
607
|
}
|
|
490
|
-
const paymentInstructions = buildPaymentInstructions(paymentRequest, payerSigner);
|
|
608
|
+
const paymentInstructions = await buildPaymentInstructions(paymentRequest, payerSigner);
|
|
491
609
|
const priorityFeeMicroLamports = options?.priorityFeeMicroLamports ?? await estimatePriorityFeeMicroLamports(rpc, {
|
|
492
610
|
percentile: options?.priorityFeePercentile ?? DEFAULT_PRIORITY_FEE_PERCENTILE
|
|
493
611
|
});
|
|
@@ -549,6 +667,16 @@ var SolanaPaymentStrategy = class {
|
|
|
549
667
|
error: `Fee amount (${feeAmount}) exceeds or equals total amount (${paymentRequest.amount})`
|
|
550
668
|
};
|
|
551
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;
|
|
552
680
|
if (options?.txSignature) {
|
|
553
681
|
return this._verifyBySignature(
|
|
554
682
|
rpc,
|
|
@@ -558,6 +686,7 @@ var SolanaPaymentStrategy = class {
|
|
|
558
686
|
treasury,
|
|
559
687
|
expectedNet,
|
|
560
688
|
feeAmount,
|
|
689
|
+
mint,
|
|
561
690
|
options?.retries ?? DEFAULTS.VERIFY_RETRIES,
|
|
562
691
|
options?.intervalMs ?? DEFAULTS.VERIFY_INTERVAL_MS
|
|
563
692
|
);
|
|
@@ -569,11 +698,12 @@ var SolanaPaymentStrategy = class {
|
|
|
569
698
|
treasury,
|
|
570
699
|
expectedNet,
|
|
571
700
|
feeAmount,
|
|
701
|
+
mint,
|
|
572
702
|
options?.retries ?? DEFAULTS.VERIFY_BY_REF_RETRIES,
|
|
573
703
|
options?.intervalMs ?? DEFAULTS.VERIFY_BY_REF_INTERVAL_MS
|
|
574
704
|
);
|
|
575
705
|
}
|
|
576
|
-
async _verifyBySignature(rpc, txSignature, referenceKey, recipientAddress, treasuryAddress, expectedNet, expectedFee, retries, intervalMs) {
|
|
706
|
+
async _verifyBySignature(rpc, txSignature, referenceKey, recipientAddress, treasuryAddress, expectedNet, expectedFee, mint, retries, intervalMs) {
|
|
577
707
|
let lastError;
|
|
578
708
|
for (let attempt = 0; attempt < retries; attempt++) {
|
|
579
709
|
try {
|
|
@@ -592,15 +722,18 @@ var SolanaPaymentStrategy = class {
|
|
|
592
722
|
error: tx?.meta?.err ? "Transaction failed on-chain" : "Transaction not found"
|
|
593
723
|
};
|
|
594
724
|
}
|
|
595
|
-
const verdict =
|
|
725
|
+
const verdict = checkTxDiff({
|
|
596
726
|
accountKeys: tx.transaction.message.accountKeys,
|
|
597
727
|
preBalances: tx.meta.preBalances,
|
|
598
728
|
postBalances: tx.meta.postBalances,
|
|
729
|
+
preTokenBalances: tx.meta.preTokenBalances,
|
|
730
|
+
postTokenBalances: tx.meta.postTokenBalances,
|
|
599
731
|
referenceKey,
|
|
600
732
|
recipientAddress,
|
|
601
733
|
treasuryAddress,
|
|
602
734
|
expectedNet,
|
|
603
|
-
expectedFee
|
|
735
|
+
expectedFee,
|
|
736
|
+
mint
|
|
604
737
|
});
|
|
605
738
|
if (verdict.ok) {
|
|
606
739
|
return { verified: true, txSignature };
|
|
@@ -618,7 +751,7 @@ var SolanaPaymentStrategy = class {
|
|
|
618
751
|
error: `Verification failed after ${retries} retries: ${lastError instanceof Error ? lastError.message : "unknown error"}`
|
|
619
752
|
};
|
|
620
753
|
}
|
|
621
|
-
async _verifyByReference(rpc, referenceKey, recipientAddress, treasuryAddress, expectedNet, expectedFee, retries, intervalMs) {
|
|
754
|
+
async _verifyByReference(rpc, referenceKey, recipientAddress, treasuryAddress, expectedNet, expectedFee, mint, retries, intervalMs) {
|
|
622
755
|
let lastError;
|
|
623
756
|
const reference = kit.address(referenceKey);
|
|
624
757
|
for (let attempt = 0; attempt < retries; attempt++) {
|
|
@@ -642,15 +775,18 @@ var SolanaPaymentStrategy = class {
|
|
|
642
775
|
if (!tx?.meta || tx.meta.err) {
|
|
643
776
|
continue;
|
|
644
777
|
}
|
|
645
|
-
const verdict =
|
|
778
|
+
const verdict = checkTxDiff({
|
|
646
779
|
accountKeys: tx.transaction.message.accountKeys,
|
|
647
780
|
preBalances: tx.meta.preBalances,
|
|
648
781
|
postBalances: tx.meta.postBalances,
|
|
782
|
+
preTokenBalances: tx.meta.preTokenBalances,
|
|
783
|
+
postTokenBalances: tx.meta.postTokenBalances,
|
|
649
784
|
referenceKey,
|
|
650
785
|
recipientAddress,
|
|
651
786
|
treasuryAddress,
|
|
652
787
|
expectedNet,
|
|
653
|
-
expectedFee
|
|
788
|
+
expectedFee,
|
|
789
|
+
mint
|
|
654
790
|
});
|
|
655
791
|
if (verdict.ok) {
|
|
656
792
|
return { verified: true, txSignature: sig };
|
|
@@ -670,7 +806,7 @@ var SolanaPaymentStrategy = class {
|
|
|
670
806
|
};
|
|
671
807
|
}
|
|
672
808
|
};
|
|
673
|
-
function
|
|
809
|
+
function checkTxDiff(input) {
|
|
674
810
|
const balanceCount = input.preBalances.length;
|
|
675
811
|
const keyToIdx = /* @__PURE__ */ new Map();
|
|
676
812
|
for (let i = 0; i < Math.min(input.accountKeys.length, balanceCount); i++) {
|
|
@@ -682,6 +818,9 @@ function checkBalanceDiff(input) {
|
|
|
682
818
|
if (!keyToIdx.has(input.referenceKey)) {
|
|
683
819
|
return { ok: false, reason: "Reference key not found in transaction - possible replay" };
|
|
684
820
|
}
|
|
821
|
+
if (input.mint) {
|
|
822
|
+
return checkTokenBalanceDiff(input);
|
|
823
|
+
}
|
|
685
824
|
const recipientIdx = keyToIdx.get(input.recipientAddress);
|
|
686
825
|
if (recipientIdx === void 0) {
|
|
687
826
|
return { ok: false, reason: "Recipient not found in transaction" };
|
|
@@ -714,6 +853,47 @@ function checkBalanceDiff(input) {
|
|
|
714
853
|
}
|
|
715
854
|
return { ok: true };
|
|
716
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
|
+
}
|
|
717
897
|
function bigIntDelta(post, pre) {
|
|
718
898
|
const postValue = post === void 0 ? 0n : BigInt(post);
|
|
719
899
|
const preValue = pre === void 0 ? 0n : BigInt(pre);
|
|
@@ -722,7 +902,7 @@ function bigIntDelta(post, pre) {
|
|
|
722
902
|
function waitMs(ms) {
|
|
723
903
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
724
904
|
}
|
|
725
|
-
function buildPaymentInstructions(paymentRequest, payerSigner) {
|
|
905
|
+
async function buildPaymentInstructions(paymentRequest, payerSigner) {
|
|
726
906
|
const recipient = kit.address(paymentRequest.recipient);
|
|
727
907
|
const reference = kit.address(paymentRequest.reference);
|
|
728
908
|
const feeAmount = paymentRequest.fee_amount ?? 0;
|
|
@@ -732,22 +912,98 @@ function buildPaymentInstructions(paymentRequest, payerSigner) {
|
|
|
732
912
|
`Fee amount (${feeAmount}) exceeds or equals total amount (${paymentRequest.amount}). Cannot create transaction with non-positive provider amount.`
|
|
733
913
|
);
|
|
734
914
|
}
|
|
735
|
-
const
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
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
|
|
739
992
|
});
|
|
740
993
|
const providerTransferIxWithReference = {
|
|
741
994
|
...providerTransferIx,
|
|
742
995
|
accounts: [...providerTransferIx.accounts, { address: reference, role: kit.AccountRole.READONLY }]
|
|
743
996
|
};
|
|
744
|
-
|
|
745
|
-
if (paymentRequest.fee_address && feeAmount > 0) {
|
|
997
|
+
instructions.push(providerTransferIxWithReference);
|
|
998
|
+
if (treasuryAta && paymentRequest.fee_address && feeAmount > 0) {
|
|
746
999
|
instructions.push(
|
|
747
|
-
|
|
748
|
-
source:
|
|
749
|
-
|
|
750
|
-
|
|
1000
|
+
token.getTransferCheckedInstruction({
|
|
1001
|
+
source: payerAta,
|
|
1002
|
+
mint,
|
|
1003
|
+
destination: treasuryAta,
|
|
1004
|
+
authority: payerSigner,
|
|
1005
|
+
amount: BigInt(feeAmount),
|
|
1006
|
+
decimals: asset.decimals
|
|
751
1007
|
})
|
|
752
1008
|
);
|
|
753
1009
|
}
|
|
@@ -2356,6 +2612,133 @@ var ElisymClient = class {
|
|
|
2356
2612
|
this.pool.close();
|
|
2357
2613
|
}
|
|
2358
2614
|
};
|
|
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);
|
|
2656
|
+
}
|
|
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
|
+
};
|
|
2671
|
+
}
|
|
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;
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
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) {
|
|
2692
|
+
try {
|
|
2693
|
+
const res = await rpc.getAccountInfo(acct, { encoding: "base64" }).send();
|
|
2694
|
+
if (!res || !res.value) {
|
|
2695
|
+
missing++;
|
|
2696
|
+
}
|
|
2697
|
+
} catch {
|
|
2698
|
+
missing++;
|
|
2699
|
+
}
|
|
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")}`;
|
|
2732
|
+
}
|
|
2733
|
+
var SessionSpendLimitEntrySchema = zod.z.object({
|
|
2734
|
+
chain: zod.z.enum(["solana"]),
|
|
2735
|
+
token: zod.z.string().min(1).max(16).regex(/^[a-z0-9]+$/, "token must be lowercase alphanumeric"),
|
|
2736
|
+
mint: zod.z.string().min(1).max(64).optional(),
|
|
2737
|
+
amount: zod.z.number().positive().finite()
|
|
2738
|
+
}).strict();
|
|
2739
|
+
var GlobalConfigSchema = zod.z.object({
|
|
2740
|
+
session_spend_limits: zod.z.array(SessionSpendLimitEntrySchema).max(16).optional()
|
|
2741
|
+
}).strict();
|
|
2359
2742
|
function formatSol(lamports) {
|
|
2360
2743
|
const sol = new Decimal2__default.default(lamports).div(LAMPORTS_PER_SOL);
|
|
2361
2744
|
if (sol.gte(1e6)) {
|
|
@@ -2554,6 +2937,7 @@ exports.DEFAULT_REDACT_PATHS = DEFAULT_REDACT_PATHS;
|
|
|
2554
2937
|
exports.DiscoveryService = DiscoveryService;
|
|
2555
2938
|
exports.ElisymClient = ElisymClient;
|
|
2556
2939
|
exports.ElisymIdentity = ElisymIdentity;
|
|
2940
|
+
exports.GlobalConfigSchema = GlobalConfigSchema;
|
|
2557
2941
|
exports.INPUT_REDACT_PATHS = INPUT_REDACT_PATHS;
|
|
2558
2942
|
exports.KIND_APP_HANDLER = KIND_APP_HANDLER;
|
|
2559
2943
|
exports.KIND_JOB_FEEDBACK = KIND_JOB_FEEDBACK;
|
|
@@ -2563,10 +2947,12 @@ exports.KIND_JOB_RESULT = KIND_JOB_RESULT;
|
|
|
2563
2947
|
exports.KIND_JOB_RESULT_BASE = KIND_JOB_RESULT_BASE;
|
|
2564
2948
|
exports.KIND_PING = KIND_PING;
|
|
2565
2949
|
exports.KIND_PONG = KIND_PONG;
|
|
2950
|
+
exports.KNOWN_ASSETS = KNOWN_ASSETS;
|
|
2566
2951
|
exports.LAMPORTS_PER_SOL = LAMPORTS_PER_SOL;
|
|
2567
2952
|
exports.LIMITS = LIMITS;
|
|
2568
2953
|
exports.MarketplaceService = MarketplaceService;
|
|
2569
2954
|
exports.MediaService = MediaService;
|
|
2955
|
+
exports.NATIVE_SOL = NATIVE_SOL;
|
|
2570
2956
|
exports.NostrPool = NostrPool;
|
|
2571
2957
|
exports.PROTOCOL_FEE_BPS = PROTOCOL_FEE_BPS;
|
|
2572
2958
|
exports.PROTOCOL_PROGRAM_ID_DEVNET = PROTOCOL_PROGRAM_ID_DEVNET;
|
|
@@ -2575,9 +2961,13 @@ exports.PaymentRequestSchema = PaymentRequestSchema;
|
|
|
2575
2961
|
exports.PingService = PingService;
|
|
2576
2962
|
exports.RELAYS = RELAYS;
|
|
2577
2963
|
exports.SECRET_REDACT_PATHS = SECRET_REDACT_PATHS;
|
|
2964
|
+
exports.SessionSpendLimitEntrySchema = SessionSpendLimitEntrySchema;
|
|
2578
2965
|
exports.SolanaPaymentStrategy = SolanaPaymentStrategy;
|
|
2966
|
+
exports.USDC_SOLANA_DEVNET = USDC_SOLANA_DEVNET;
|
|
2579
2967
|
exports.assertExpiry = assertExpiry;
|
|
2580
2968
|
exports.assertLamports = assertLamports;
|
|
2969
|
+
exports.assetByKey = assetByKey;
|
|
2970
|
+
exports.assetKey = assetKey;
|
|
2581
2971
|
exports.buildPaymentInstructions = buildPaymentInstructions;
|
|
2582
2972
|
exports.calculateProtocolFee = calculateProtocolFee;
|
|
2583
2973
|
exports.clearPriorityFeeCache = clearPriorityFeeCache;
|
|
@@ -2585,6 +2975,9 @@ exports.clearProtocolConfigCache = clearProtocolConfigCache;
|
|
|
2585
2975
|
exports.createPaymentRequestWithOnchainConfig = createPaymentRequestWithOnchainConfig;
|
|
2586
2976
|
exports.createSlidingWindowLimiter = createSlidingWindowLimiter;
|
|
2587
2977
|
exports.estimatePriorityFeeMicroLamports = estimatePriorityFeeMicroLamports;
|
|
2978
|
+
exports.estimateSolFeeLamports = estimateSolFeeLamports;
|
|
2979
|
+
exports.formatAssetAmount = formatAssetAmount;
|
|
2980
|
+
exports.formatFeeBreakdown = formatFeeBreakdown;
|
|
2588
2981
|
exports.formatSol = formatSol;
|
|
2589
2982
|
exports.getProtocolConfig = getProtocolConfig;
|
|
2590
2983
|
exports.getProtocolProgramId = getProtocolProgramId;
|
|
@@ -2593,8 +2986,11 @@ exports.jobResultKind = jobResultKind;
|
|
|
2593
2986
|
exports.makeCensor = makeCensor;
|
|
2594
2987
|
exports.nip44Decrypt = nip44Decrypt;
|
|
2595
2988
|
exports.nip44Encrypt = nip44Encrypt;
|
|
2989
|
+
exports.parseAssetAmount = parseAssetAmount;
|
|
2596
2990
|
exports.parsePaymentRequest = parsePaymentRequest;
|
|
2597
2991
|
exports.pickPercentileFee = pickPercentileFee;
|
|
2992
|
+
exports.resolveAssetFromPaymentRequest = resolveAssetFromPaymentRequest;
|
|
2993
|
+
exports.resolveKnownAsset = resolveKnownAsset;
|
|
2598
2994
|
exports.timeAgo = timeAgo;
|
|
2599
2995
|
exports.toDTag = toDTag;
|
|
2600
2996
|
exports.truncateKey = truncateKey;
|