@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.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getTransferSolInstruction } from '@solana-program/system';
|
|
2
|
+
import { findAssociatedTokenPda, TOKEN_PROGRAM_ADDRESS, getCreateAssociatedTokenIdempotentInstruction, ASSOCIATED_TOKEN_PROGRAM_ADDRESS, getTransferCheckedInstruction } from '@solana-program/token';
|
|
2
3
|
import { pipe, createTransactionMessage, setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash, setTransactionMessageComputeUnitLimit, setTransactionMessageComputeUnitPrice, appendTransactionMessageInstructions, signTransactionMessageWithSigners, address, AccountRole, getProgramDerivedAddress, assertAccountExists, isAddress, getAddressDecoder, fetchEncodedAccount, decodeAccount, getStructDecoder, fixDecoderSize, getBytesDecoder, getU8Decoder, getOptionDecoder, getU16Decoder, getBooleanDecoder, getI64Decoder } from '@solana/kit';
|
|
3
4
|
import Decimal2 from 'decimal.js-light';
|
|
4
5
|
import { z } from 'zod';
|
|
@@ -93,13 +94,13 @@ function decodeConfig(encodedAccount) {
|
|
|
93
94
|
getConfigDecoder()
|
|
94
95
|
);
|
|
95
96
|
}
|
|
96
|
-
async function fetchConfig(rpc,
|
|
97
|
-
const maybeAccount = await fetchMaybeConfig(rpc,
|
|
97
|
+
async function fetchConfig(rpc, address3, config) {
|
|
98
|
+
const maybeAccount = await fetchMaybeConfig(rpc, address3, config);
|
|
98
99
|
assertAccountExists(maybeAccount);
|
|
99
100
|
return maybeAccount;
|
|
100
101
|
}
|
|
101
|
-
async function fetchMaybeConfig(rpc,
|
|
102
|
-
const maybeAccount = await fetchEncodedAccount(rpc,
|
|
102
|
+
async function fetchMaybeConfig(rpc, address3, config) {
|
|
103
|
+
const maybeAccount = await fetchEncodedAccount(rpc, address3, config);
|
|
103
104
|
return decodeConfig(maybeAccount);
|
|
104
105
|
}
|
|
105
106
|
if (process.env.NODE_ENV !== "production") ;
|
|
@@ -150,6 +151,99 @@ async function getProtocolConfig(rpc, programId, options) {
|
|
|
150
151
|
);
|
|
151
152
|
}
|
|
152
153
|
}
|
|
154
|
+
|
|
155
|
+
// src/payment/assets.ts
|
|
156
|
+
var NATIVE_SOL = {
|
|
157
|
+
chain: "solana",
|
|
158
|
+
token: "sol",
|
|
159
|
+
decimals: 9,
|
|
160
|
+
symbol: "SOL"
|
|
161
|
+
};
|
|
162
|
+
var USDC_SOLANA_DEVNET = {
|
|
163
|
+
chain: "solana",
|
|
164
|
+
token: "usdc",
|
|
165
|
+
mint: "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
|
|
166
|
+
decimals: 6,
|
|
167
|
+
symbol: "USDC"
|
|
168
|
+
};
|
|
169
|
+
var KNOWN_ASSETS = [NATIVE_SOL, USDC_SOLANA_DEVNET];
|
|
170
|
+
function assetKey(a) {
|
|
171
|
+
return a.mint ? `${a.chain}:${a.token}:${a.mint}` : `${a.chain}:${a.token}`;
|
|
172
|
+
}
|
|
173
|
+
function resolveKnownAsset(chain, token, mint) {
|
|
174
|
+
const key = mint ? `${chain}:${token}:${mint}` : `${chain}:${token}`;
|
|
175
|
+
return KNOWN_ASSETS.find((asset) => assetKey(asset) === key);
|
|
176
|
+
}
|
|
177
|
+
function assetByKey(key) {
|
|
178
|
+
return KNOWN_ASSETS.find((asset) => assetKey(asset) === key);
|
|
179
|
+
}
|
|
180
|
+
function resolveAssetFromPaymentRequest(request) {
|
|
181
|
+
if (!request.asset) {
|
|
182
|
+
return NATIVE_SOL;
|
|
183
|
+
}
|
|
184
|
+
const found = resolveKnownAsset(request.asset.chain, request.asset.token, request.asset.mint);
|
|
185
|
+
if (!found) {
|
|
186
|
+
const display = request.asset.mint ? `${request.asset.chain}:${request.asset.token}:${request.asset.mint}` : `${request.asset.chain}:${request.asset.token}`;
|
|
187
|
+
throw new Error(
|
|
188
|
+
`Unknown asset in payment request: ${display}. Known assets: ${KNOWN_ASSETS.map(assetKey).join(", ")}`
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
return found;
|
|
192
|
+
}
|
|
193
|
+
var DECIMAL_RE = /^(\d+\.\d*|\d*\.\d+|\d+)$/;
|
|
194
|
+
function parseAssetAmount(asset, human) {
|
|
195
|
+
const trimmed = human.trim();
|
|
196
|
+
if (!trimmed) {
|
|
197
|
+
throw new Error(`${asset.symbol} amount is empty`);
|
|
198
|
+
}
|
|
199
|
+
if (trimmed.startsWith("-")) {
|
|
200
|
+
throw new Error(`${asset.symbol} amount cannot be negative`);
|
|
201
|
+
}
|
|
202
|
+
if (!DECIMAL_RE.test(trimmed)) {
|
|
203
|
+
throw new Error(
|
|
204
|
+
`${asset.symbol} amount must be a non-negative decimal (e.g. "0.5", "1"); got "${human}"`
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
const dotPos = trimmed.indexOf(".");
|
|
208
|
+
let wholePart;
|
|
209
|
+
if (dotPos === -1) {
|
|
210
|
+
wholePart = trimmed;
|
|
211
|
+
} else if (dotPos === 0) {
|
|
212
|
+
wholePart = "0";
|
|
213
|
+
} else {
|
|
214
|
+
wholePart = trimmed.slice(0, dotPos);
|
|
215
|
+
}
|
|
216
|
+
const fracPart = dotPos === -1 ? "" : trimmed.slice(dotPos + 1);
|
|
217
|
+
if (fracPart.length > asset.decimals) {
|
|
218
|
+
throw new Error(
|
|
219
|
+
`${asset.symbol} amount has too many decimals (max ${asset.decimals}); got "${human}"`
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
const unit = 10n ** BigInt(asset.decimals);
|
|
223
|
+
const whole = BigInt(wholePart);
|
|
224
|
+
const frac = fracPart ? BigInt(fracPart.padEnd(asset.decimals, "0")) : 0n;
|
|
225
|
+
const raw = whole * unit + frac;
|
|
226
|
+
if (raw === 0n) {
|
|
227
|
+
throw new Error(`${asset.symbol} amount must be positive; got "${human}"`);
|
|
228
|
+
}
|
|
229
|
+
if (raw > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
230
|
+
throw new Error(
|
|
231
|
+
`${asset.symbol} amount exceeds safe range (max ${Number.MAX_SAFE_INTEGER} subunits)`
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
return raw;
|
|
235
|
+
}
|
|
236
|
+
function formatAssetAmount(asset, raw) {
|
|
237
|
+
const sign = raw < 0n ? "-" : "";
|
|
238
|
+
const abs = raw < 0n ? -raw : raw;
|
|
239
|
+
const unit = 10n ** BigInt(asset.decimals);
|
|
240
|
+
const whole = abs / unit;
|
|
241
|
+
const frac = abs % unit;
|
|
242
|
+
if (asset.decimals === 0) {
|
|
243
|
+
return `${sign}${whole} ${asset.symbol}`;
|
|
244
|
+
}
|
|
245
|
+
return `${sign}${whole}.${frac.toString().padStart(asset.decimals, "0")} ${asset.symbol}`;
|
|
246
|
+
}
|
|
153
247
|
var BPS_DENOMINATOR = 1e4;
|
|
154
248
|
function assertLamports(value, field) {
|
|
155
249
|
if (!Number.isInteger(value) || value < 0) {
|
|
@@ -259,6 +353,12 @@ var SOLANA_ADDRESS_LENGTH_RE = /^.{32,44}$/;
|
|
|
259
353
|
var lamportsSchema = z.number().int().positive().max(MAX_SAFE_LAMPORTS, `amount must be <= ${MAX_SAFE_LAMPORTS}`);
|
|
260
354
|
var feeAmountSchema = z.number().int().nonnegative().max(MAX_SAFE_LAMPORTS, `fee_amount must be <= ${MAX_SAFE_LAMPORTS}`);
|
|
261
355
|
var solanaAddressSchema = z.string().regex(BASE58_RE, "must be base58").regex(SOLANA_ADDRESS_LENGTH_RE, "must be 32-44 base58 chars");
|
|
356
|
+
var paymentAssetRefSchema = z.object({
|
|
357
|
+
chain: z.string().min(1),
|
|
358
|
+
token: z.string().min(1),
|
|
359
|
+
mint: solanaAddressSchema.optional(),
|
|
360
|
+
decimals: z.number().int().min(0).max(18)
|
|
361
|
+
});
|
|
262
362
|
var PaymentRequestSchema = z.object({
|
|
263
363
|
recipient: solanaAddressSchema,
|
|
264
364
|
amount: lamportsSchema,
|
|
@@ -267,7 +367,8 @@ var PaymentRequestSchema = z.object({
|
|
|
267
367
|
fee_address: solanaAddressSchema.optional(),
|
|
268
368
|
fee_amount: feeAmountSchema.optional(),
|
|
269
369
|
created_at: z.number().int().positive(),
|
|
270
|
-
expiry_secs: z.number().int().positive().max(MAX_EXPIRY_SECS_SCHEMA, `expiry_secs must be <= ${MAX_EXPIRY_SECS_SCHEMA}`)
|
|
370
|
+
expiry_secs: z.number().int().positive().max(MAX_EXPIRY_SECS_SCHEMA, `expiry_secs must be <= ${MAX_EXPIRY_SECS_SCHEMA}`),
|
|
371
|
+
asset: paymentAssetRefSchema.optional()
|
|
271
372
|
});
|
|
272
373
|
function parsePaymentRequest(input, options) {
|
|
273
374
|
let parsed;
|
|
@@ -349,6 +450,12 @@ var SolanaPaymentStrategy = class {
|
|
|
349
450
|
assertExpirySecs(expirySecs);
|
|
350
451
|
const feeAmount = calculateProtocolFee(amount, config.feeBps);
|
|
351
452
|
const reference = generateReference();
|
|
453
|
+
const assetRef = options?.asset && options.asset !== NATIVE_SOL ? {
|
|
454
|
+
chain: options.asset.chain,
|
|
455
|
+
token: options.asset.token,
|
|
456
|
+
mint: options.asset.mint,
|
|
457
|
+
decimals: options.asset.decimals
|
|
458
|
+
} : void 0;
|
|
352
459
|
return {
|
|
353
460
|
recipient: recipientAddress,
|
|
354
461
|
amount,
|
|
@@ -356,7 +463,8 @@ var SolanaPaymentStrategy = class {
|
|
|
356
463
|
fee_address: config.treasury,
|
|
357
464
|
fee_amount: feeAmount,
|
|
358
465
|
created_at: Math.floor(Date.now() / 1e3),
|
|
359
|
-
expiry_secs: expirySecs
|
|
466
|
+
expiry_secs: expirySecs,
|
|
467
|
+
...assetRef ? { asset: assetRef } : {}
|
|
360
468
|
};
|
|
361
469
|
}
|
|
362
470
|
validatePaymentRequest(requestJson, config, expectedRecipient, options) {
|
|
@@ -374,6 +482,16 @@ var SolanaPaymentStrategy = class {
|
|
|
374
482
|
return { code: "invalid_amount", message: parsed.error.message };
|
|
375
483
|
}
|
|
376
484
|
const data = parsed.data;
|
|
485
|
+
if (data.asset) {
|
|
486
|
+
try {
|
|
487
|
+
resolveAssetFromPaymentRequest(data);
|
|
488
|
+
} catch (error) {
|
|
489
|
+
return {
|
|
490
|
+
code: "invalid_asset",
|
|
491
|
+
message: error instanceof Error ? error.message : String(error)
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
}
|
|
377
495
|
if (!isValidSolanaAddress(data.recipient)) {
|
|
378
496
|
return {
|
|
379
497
|
code: "invalid_recipient_address",
|
|
@@ -462,7 +580,7 @@ var SolanaPaymentStrategy = class {
|
|
|
462
580
|
if (!Number.isInteger(computeUnitLimit) || computeUnitLimit <= 0) {
|
|
463
581
|
throw new Error(`Invalid computeUnitLimit: ${computeUnitLimit}. Must be a positive integer.`);
|
|
464
582
|
}
|
|
465
|
-
const paymentInstructions = buildPaymentInstructions(paymentRequest, payerSigner);
|
|
583
|
+
const paymentInstructions = await buildPaymentInstructions(paymentRequest, payerSigner);
|
|
466
584
|
const priorityFeeMicroLamports = options?.priorityFeeMicroLamports ?? await estimatePriorityFeeMicroLamports(rpc, {
|
|
467
585
|
percentile: options?.priorityFeePercentile ?? DEFAULT_PRIORITY_FEE_PERCENTILE
|
|
468
586
|
});
|
|
@@ -524,6 +642,16 @@ var SolanaPaymentStrategy = class {
|
|
|
524
642
|
error: `Fee amount (${feeAmount}) exceeds or equals total amount (${paymentRequest.amount})`
|
|
525
643
|
};
|
|
526
644
|
}
|
|
645
|
+
let asset;
|
|
646
|
+
try {
|
|
647
|
+
asset = resolveAssetFromPaymentRequest(paymentRequest);
|
|
648
|
+
} catch (error) {
|
|
649
|
+
return {
|
|
650
|
+
verified: false,
|
|
651
|
+
error: error instanceof Error ? error.message : String(error)
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
const mint = asset.mint;
|
|
527
655
|
if (options?.txSignature) {
|
|
528
656
|
return this._verifyBySignature(
|
|
529
657
|
rpc,
|
|
@@ -533,6 +661,7 @@ var SolanaPaymentStrategy = class {
|
|
|
533
661
|
treasury,
|
|
534
662
|
expectedNet,
|
|
535
663
|
feeAmount,
|
|
664
|
+
mint,
|
|
536
665
|
options?.retries ?? DEFAULTS.VERIFY_RETRIES,
|
|
537
666
|
options?.intervalMs ?? DEFAULTS.VERIFY_INTERVAL_MS
|
|
538
667
|
);
|
|
@@ -544,11 +673,12 @@ var SolanaPaymentStrategy = class {
|
|
|
544
673
|
treasury,
|
|
545
674
|
expectedNet,
|
|
546
675
|
feeAmount,
|
|
676
|
+
mint,
|
|
547
677
|
options?.retries ?? DEFAULTS.VERIFY_BY_REF_RETRIES,
|
|
548
678
|
options?.intervalMs ?? DEFAULTS.VERIFY_BY_REF_INTERVAL_MS
|
|
549
679
|
);
|
|
550
680
|
}
|
|
551
|
-
async _verifyBySignature(rpc, txSignature, referenceKey, recipientAddress, treasuryAddress, expectedNet, expectedFee, retries, intervalMs) {
|
|
681
|
+
async _verifyBySignature(rpc, txSignature, referenceKey, recipientAddress, treasuryAddress, expectedNet, expectedFee, mint, retries, intervalMs) {
|
|
552
682
|
let lastError;
|
|
553
683
|
for (let attempt = 0; attempt < retries; attempt++) {
|
|
554
684
|
try {
|
|
@@ -567,15 +697,18 @@ var SolanaPaymentStrategy = class {
|
|
|
567
697
|
error: tx?.meta?.err ? "Transaction failed on-chain" : "Transaction not found"
|
|
568
698
|
};
|
|
569
699
|
}
|
|
570
|
-
const verdict =
|
|
700
|
+
const verdict = checkTxDiff({
|
|
571
701
|
accountKeys: tx.transaction.message.accountKeys,
|
|
572
702
|
preBalances: tx.meta.preBalances,
|
|
573
703
|
postBalances: tx.meta.postBalances,
|
|
704
|
+
preTokenBalances: tx.meta.preTokenBalances,
|
|
705
|
+
postTokenBalances: tx.meta.postTokenBalances,
|
|
574
706
|
referenceKey,
|
|
575
707
|
recipientAddress,
|
|
576
708
|
treasuryAddress,
|
|
577
709
|
expectedNet,
|
|
578
|
-
expectedFee
|
|
710
|
+
expectedFee,
|
|
711
|
+
mint
|
|
579
712
|
});
|
|
580
713
|
if (verdict.ok) {
|
|
581
714
|
return { verified: true, txSignature };
|
|
@@ -593,7 +726,7 @@ var SolanaPaymentStrategy = class {
|
|
|
593
726
|
error: `Verification failed after ${retries} retries: ${lastError instanceof Error ? lastError.message : "unknown error"}`
|
|
594
727
|
};
|
|
595
728
|
}
|
|
596
|
-
async _verifyByReference(rpc, referenceKey, recipientAddress, treasuryAddress, expectedNet, expectedFee, retries, intervalMs) {
|
|
729
|
+
async _verifyByReference(rpc, referenceKey, recipientAddress, treasuryAddress, expectedNet, expectedFee, mint, retries, intervalMs) {
|
|
597
730
|
let lastError;
|
|
598
731
|
const reference = address(referenceKey);
|
|
599
732
|
for (let attempt = 0; attempt < retries; attempt++) {
|
|
@@ -617,15 +750,18 @@ var SolanaPaymentStrategy = class {
|
|
|
617
750
|
if (!tx?.meta || tx.meta.err) {
|
|
618
751
|
continue;
|
|
619
752
|
}
|
|
620
|
-
const verdict =
|
|
753
|
+
const verdict = checkTxDiff({
|
|
621
754
|
accountKeys: tx.transaction.message.accountKeys,
|
|
622
755
|
preBalances: tx.meta.preBalances,
|
|
623
756
|
postBalances: tx.meta.postBalances,
|
|
757
|
+
preTokenBalances: tx.meta.preTokenBalances,
|
|
758
|
+
postTokenBalances: tx.meta.postTokenBalances,
|
|
624
759
|
referenceKey,
|
|
625
760
|
recipientAddress,
|
|
626
761
|
treasuryAddress,
|
|
627
762
|
expectedNet,
|
|
628
|
-
expectedFee
|
|
763
|
+
expectedFee,
|
|
764
|
+
mint
|
|
629
765
|
});
|
|
630
766
|
if (verdict.ok) {
|
|
631
767
|
return { verified: true, txSignature: sig };
|
|
@@ -645,7 +781,7 @@ var SolanaPaymentStrategy = class {
|
|
|
645
781
|
};
|
|
646
782
|
}
|
|
647
783
|
};
|
|
648
|
-
function
|
|
784
|
+
function checkTxDiff(input) {
|
|
649
785
|
const balanceCount = input.preBalances.length;
|
|
650
786
|
const keyToIdx = /* @__PURE__ */ new Map();
|
|
651
787
|
for (let i = 0; i < Math.min(input.accountKeys.length, balanceCount); i++) {
|
|
@@ -657,6 +793,9 @@ function checkBalanceDiff(input) {
|
|
|
657
793
|
if (!keyToIdx.has(input.referenceKey)) {
|
|
658
794
|
return { ok: false, reason: "Reference key not found in transaction - possible replay" };
|
|
659
795
|
}
|
|
796
|
+
if (input.mint) {
|
|
797
|
+
return checkTokenBalanceDiff(input);
|
|
798
|
+
}
|
|
660
799
|
const recipientIdx = keyToIdx.get(input.recipientAddress);
|
|
661
800
|
if (recipientIdx === void 0) {
|
|
662
801
|
return { ok: false, reason: "Recipient not found in transaction" };
|
|
@@ -689,6 +828,47 @@ function checkBalanceDiff(input) {
|
|
|
689
828
|
}
|
|
690
829
|
return { ok: true };
|
|
691
830
|
}
|
|
831
|
+
function checkTokenBalanceDiff(input) {
|
|
832
|
+
const mint = input.mint;
|
|
833
|
+
if (!mint) {
|
|
834
|
+
return { ok: false, reason: "Expected mint for SPL verification, got none" };
|
|
835
|
+
}
|
|
836
|
+
const pre = input.preTokenBalances ?? [];
|
|
837
|
+
const post = input.postTokenBalances ?? [];
|
|
838
|
+
const tokenDelta = (ownerAddress) => {
|
|
839
|
+
const preEntry = pre.find((entry) => entry.owner === ownerAddress && entry.mint === mint);
|
|
840
|
+
const postEntry = post.find((entry) => entry.owner === ownerAddress && entry.mint === mint);
|
|
841
|
+
if (!postEntry) {
|
|
842
|
+
return -1n;
|
|
843
|
+
}
|
|
844
|
+
const preAmount = preEntry ? BigInt(preEntry.uiTokenAmount.amount) : 0n;
|
|
845
|
+
const postAmount = BigInt(postEntry.uiTokenAmount.amount);
|
|
846
|
+
return postAmount - preAmount;
|
|
847
|
+
};
|
|
848
|
+
const recipientDelta = tokenDelta(input.recipientAddress);
|
|
849
|
+
if (recipientDelta === -1n) {
|
|
850
|
+
return { ok: false, reason: "Recipient token account not found in transaction" };
|
|
851
|
+
}
|
|
852
|
+
if (recipientDelta < BigInt(input.expectedNet)) {
|
|
853
|
+
return {
|
|
854
|
+
ok: false,
|
|
855
|
+
reason: `Recipient received ${recipientDelta.toString()} tokens, expected >= ${input.expectedNet}`
|
|
856
|
+
};
|
|
857
|
+
}
|
|
858
|
+
if (input.expectedFee > 0) {
|
|
859
|
+
const treasuryDelta = tokenDelta(input.treasuryAddress);
|
|
860
|
+
if (treasuryDelta === -1n) {
|
|
861
|
+
return { ok: false, reason: "Treasury token account not found in transaction" };
|
|
862
|
+
}
|
|
863
|
+
if (treasuryDelta < BigInt(input.expectedFee)) {
|
|
864
|
+
return {
|
|
865
|
+
ok: false,
|
|
866
|
+
reason: `Treasury received ${treasuryDelta.toString()} tokens, expected >= ${input.expectedFee}`
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
return { ok: true };
|
|
871
|
+
}
|
|
692
872
|
function bigIntDelta(post, pre) {
|
|
693
873
|
const postValue = post === void 0 ? 0n : BigInt(post);
|
|
694
874
|
const preValue = pre === void 0 ? 0n : BigInt(pre);
|
|
@@ -697,7 +877,7 @@ function bigIntDelta(post, pre) {
|
|
|
697
877
|
function waitMs(ms) {
|
|
698
878
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
699
879
|
}
|
|
700
|
-
function buildPaymentInstructions(paymentRequest, payerSigner) {
|
|
880
|
+
async function buildPaymentInstructions(paymentRequest, payerSigner) {
|
|
701
881
|
const recipient = address(paymentRequest.recipient);
|
|
702
882
|
const reference = address(paymentRequest.reference);
|
|
703
883
|
const feeAmount = paymentRequest.fee_amount ?? 0;
|
|
@@ -707,22 +887,98 @@ function buildPaymentInstructions(paymentRequest, payerSigner) {
|
|
|
707
887
|
`Fee amount (${feeAmount}) exceeds or equals total amount (${paymentRequest.amount}). Cannot create transaction with non-positive provider amount.`
|
|
708
888
|
);
|
|
709
889
|
}
|
|
710
|
-
const
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
890
|
+
const asset = resolveAssetFromPaymentRequest(paymentRequest);
|
|
891
|
+
if (!asset.mint) {
|
|
892
|
+
const providerTransferIx2 = getTransferSolInstruction({
|
|
893
|
+
source: payerSigner,
|
|
894
|
+
destination: recipient,
|
|
895
|
+
amount: BigInt(providerAmount)
|
|
896
|
+
});
|
|
897
|
+
const providerTransferIxWithReference2 = {
|
|
898
|
+
...providerTransferIx2,
|
|
899
|
+
accounts: [
|
|
900
|
+
...providerTransferIx2.accounts,
|
|
901
|
+
{ address: reference, role: AccountRole.READONLY }
|
|
902
|
+
]
|
|
903
|
+
};
|
|
904
|
+
const instructions2 = [providerTransferIxWithReference2];
|
|
905
|
+
if (paymentRequest.fee_address && feeAmount > 0) {
|
|
906
|
+
instructions2.push(
|
|
907
|
+
getTransferSolInstruction({
|
|
908
|
+
source: payerSigner,
|
|
909
|
+
destination: address(paymentRequest.fee_address),
|
|
910
|
+
amount: BigInt(feeAmount)
|
|
911
|
+
})
|
|
912
|
+
);
|
|
913
|
+
}
|
|
914
|
+
return instructions2;
|
|
915
|
+
}
|
|
916
|
+
const mint = address(asset.mint);
|
|
917
|
+
const payerAddress = payerSigner.address;
|
|
918
|
+
const [payerAta] = await findAssociatedTokenPda({
|
|
919
|
+
owner: payerAddress,
|
|
920
|
+
tokenProgram: TOKEN_PROGRAM_ADDRESS,
|
|
921
|
+
mint
|
|
922
|
+
});
|
|
923
|
+
const [recipientAta] = await findAssociatedTokenPda({
|
|
924
|
+
owner: recipient,
|
|
925
|
+
tokenProgram: TOKEN_PROGRAM_ADDRESS,
|
|
926
|
+
mint
|
|
927
|
+
});
|
|
928
|
+
const instructions = [];
|
|
929
|
+
instructions.push(
|
|
930
|
+
getCreateAssociatedTokenIdempotentInstruction(
|
|
931
|
+
{
|
|
932
|
+
payer: payerSigner,
|
|
933
|
+
ata: recipientAta,
|
|
934
|
+
owner: recipient,
|
|
935
|
+
mint
|
|
936
|
+
},
|
|
937
|
+
{ programAddress: ASSOCIATED_TOKEN_PROGRAM_ADDRESS }
|
|
938
|
+
)
|
|
939
|
+
);
|
|
940
|
+
let treasuryAta;
|
|
941
|
+
if (paymentRequest.fee_address && feeAmount > 0) {
|
|
942
|
+
const treasuryOwner = address(paymentRequest.fee_address);
|
|
943
|
+
[treasuryAta] = await findAssociatedTokenPda({
|
|
944
|
+
owner: treasuryOwner,
|
|
945
|
+
tokenProgram: TOKEN_PROGRAM_ADDRESS,
|
|
946
|
+
mint
|
|
947
|
+
});
|
|
948
|
+
instructions.push(
|
|
949
|
+
getCreateAssociatedTokenIdempotentInstruction(
|
|
950
|
+
{
|
|
951
|
+
payer: payerSigner,
|
|
952
|
+
ata: treasuryAta,
|
|
953
|
+
owner: treasuryOwner,
|
|
954
|
+
mint
|
|
955
|
+
},
|
|
956
|
+
{ programAddress: ASSOCIATED_TOKEN_PROGRAM_ADDRESS }
|
|
957
|
+
)
|
|
958
|
+
);
|
|
959
|
+
}
|
|
960
|
+
const providerTransferIx = getTransferCheckedInstruction({
|
|
961
|
+
source: payerAta,
|
|
962
|
+
mint,
|
|
963
|
+
destination: recipientAta,
|
|
964
|
+
authority: payerSigner,
|
|
965
|
+
amount: BigInt(providerAmount),
|
|
966
|
+
decimals: asset.decimals
|
|
714
967
|
});
|
|
715
968
|
const providerTransferIxWithReference = {
|
|
716
969
|
...providerTransferIx,
|
|
717
970
|
accounts: [...providerTransferIx.accounts, { address: reference, role: AccountRole.READONLY }]
|
|
718
971
|
};
|
|
719
|
-
|
|
720
|
-
if (paymentRequest.fee_address && feeAmount > 0) {
|
|
972
|
+
instructions.push(providerTransferIxWithReference);
|
|
973
|
+
if (treasuryAta && paymentRequest.fee_address && feeAmount > 0) {
|
|
721
974
|
instructions.push(
|
|
722
|
-
|
|
723
|
-
source:
|
|
724
|
-
|
|
725
|
-
|
|
975
|
+
getTransferCheckedInstruction({
|
|
976
|
+
source: payerAta,
|
|
977
|
+
mint,
|
|
978
|
+
destination: treasuryAta,
|
|
979
|
+
authority: payerSigner,
|
|
980
|
+
amount: BigInt(feeAmount),
|
|
981
|
+
decimals: asset.decimals
|
|
726
982
|
})
|
|
727
983
|
);
|
|
728
984
|
}
|
|
@@ -2331,6 +2587,133 @@ var ElisymClient = class {
|
|
|
2331
2587
|
this.pool.close();
|
|
2332
2588
|
}
|
|
2333
2589
|
};
|
|
2590
|
+
var DEFAULT_COMPUTE_UNIT_LIMIT2 = 2e5;
|
|
2591
|
+
var DEFAULT_PRIORITY_FEE_PERCENTILE2 = 75;
|
|
2592
|
+
var BASE_FEE_LAMPORTS_PER_SIGNATURE = 5000n;
|
|
2593
|
+
var FALLBACK_ATA_RENT_LAMPORTS = 2039280n;
|
|
2594
|
+
var SPL_TOKEN_ACCOUNT_SIZE = 165;
|
|
2595
|
+
async function estimateSolFeeLamports(rpc, paymentRequest, _payerAddress, options) {
|
|
2596
|
+
const numSignatures = options?.numSignatures ?? 1;
|
|
2597
|
+
const computeUnitLimit = options?.computeUnitLimit ?? DEFAULT_COMPUTE_UNIT_LIMIT2;
|
|
2598
|
+
const priorityFeeMicroLamports = options?.priorityFeeMicroLamports ?? await estimatePriorityFeeMicroLamports(rpc, {
|
|
2599
|
+
percentile: options?.priorityFeePercentile ?? DEFAULT_PRIORITY_FEE_PERCENTILE2
|
|
2600
|
+
});
|
|
2601
|
+
const baseFeeLamports = BASE_FEE_LAMPORTS_PER_SIGNATURE * BigInt(numSignatures);
|
|
2602
|
+
const priorityFeeLamports = ceilDiv(
|
|
2603
|
+
priorityFeeMicroLamports * BigInt(computeUnitLimit),
|
|
2604
|
+
1000000n
|
|
2605
|
+
);
|
|
2606
|
+
const asset = resolveAssetFromPaymentRequest(paymentRequest);
|
|
2607
|
+
let rentLamports = 0n;
|
|
2608
|
+
let rentPerAtaLamports = 0n;
|
|
2609
|
+
let missingAtaCount = 0;
|
|
2610
|
+
if (asset.mint) {
|
|
2611
|
+
rentPerAtaLamports = await fetchAtaRent(rpc);
|
|
2612
|
+
const mint = address(asset.mint);
|
|
2613
|
+
const ataAccountsToCheck = [];
|
|
2614
|
+
const [recipientAta] = await findAssociatedTokenPda({
|
|
2615
|
+
owner: address(paymentRequest.recipient),
|
|
2616
|
+
tokenProgram: TOKEN_PROGRAM_ADDRESS,
|
|
2617
|
+
mint
|
|
2618
|
+
});
|
|
2619
|
+
ataAccountsToCheck.push(recipientAta);
|
|
2620
|
+
const feeAmount = paymentRequest.fee_amount ?? 0;
|
|
2621
|
+
if (paymentRequest.fee_address && feeAmount > 0) {
|
|
2622
|
+
const [treasuryAta] = await findAssociatedTokenPda({
|
|
2623
|
+
owner: address(paymentRequest.fee_address),
|
|
2624
|
+
tokenProgram: TOKEN_PROGRAM_ADDRESS,
|
|
2625
|
+
mint
|
|
2626
|
+
});
|
|
2627
|
+
ataAccountsToCheck.push(treasuryAta);
|
|
2628
|
+
}
|
|
2629
|
+
missingAtaCount = await countMissingAccounts(rpc, ataAccountsToCheck);
|
|
2630
|
+
rentLamports = rentPerAtaLamports * BigInt(missingAtaCount);
|
|
2631
|
+
}
|
|
2632
|
+
const totalLamports = baseFeeLamports + priorityFeeLamports + rentLamports;
|
|
2633
|
+
return {
|
|
2634
|
+
baseFeeLamports,
|
|
2635
|
+
priorityFeeLamports,
|
|
2636
|
+
rentLamports,
|
|
2637
|
+
totalLamports,
|
|
2638
|
+
breakdown: {
|
|
2639
|
+
numSignatures,
|
|
2640
|
+
priorityFeeMicroLamports,
|
|
2641
|
+
computeUnitLimit,
|
|
2642
|
+
rentPerAtaLamports,
|
|
2643
|
+
missingAtaCount
|
|
2644
|
+
}
|
|
2645
|
+
};
|
|
2646
|
+
}
|
|
2647
|
+
async function fetchAtaRent(rpc) {
|
|
2648
|
+
try {
|
|
2649
|
+
const lamports = await rpc.getMinimumBalanceForRentExemption(BigInt(SPL_TOKEN_ACCOUNT_SIZE)).send();
|
|
2650
|
+
if (typeof lamports === "bigint") {
|
|
2651
|
+
return lamports;
|
|
2652
|
+
}
|
|
2653
|
+
if (typeof lamports === "number" && Number.isFinite(lamports) && lamports > 0) {
|
|
2654
|
+
return BigInt(lamports);
|
|
2655
|
+
}
|
|
2656
|
+
return FALLBACK_ATA_RENT_LAMPORTS;
|
|
2657
|
+
} catch {
|
|
2658
|
+
return FALLBACK_ATA_RENT_LAMPORTS;
|
|
2659
|
+
}
|
|
2660
|
+
}
|
|
2661
|
+
async function countMissingAccounts(rpc, accounts) {
|
|
2662
|
+
if (accounts.length === 0) {
|
|
2663
|
+
return 0;
|
|
2664
|
+
}
|
|
2665
|
+
let missing = 0;
|
|
2666
|
+
for (const acct of accounts) {
|
|
2667
|
+
try {
|
|
2668
|
+
const res = await rpc.getAccountInfo(acct, { encoding: "base64" }).send();
|
|
2669
|
+
if (!res || !res.value) {
|
|
2670
|
+
missing++;
|
|
2671
|
+
}
|
|
2672
|
+
} catch {
|
|
2673
|
+
missing++;
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
return missing;
|
|
2677
|
+
}
|
|
2678
|
+
function ceilDiv(num, denom) {
|
|
2679
|
+
if (denom === 0n) {
|
|
2680
|
+
throw new Error("division by zero in ceilDiv");
|
|
2681
|
+
}
|
|
2682
|
+
const q = num / denom;
|
|
2683
|
+
const r = num % denom;
|
|
2684
|
+
return r === 0n ? q : q + 1n;
|
|
2685
|
+
}
|
|
2686
|
+
function formatFeeBreakdown(estimate) {
|
|
2687
|
+
const line = (label, lamports) => {
|
|
2688
|
+
const label16 = label.padEnd(14);
|
|
2689
|
+
return ` ${label16}${lamports.toString()} lamports (${lamportsToSol(lamports)} SOL)`;
|
|
2690
|
+
};
|
|
2691
|
+
const lines = [
|
|
2692
|
+
"Estimated SOL cost for this transaction:",
|
|
2693
|
+
line("Base fee:", estimate.baseFeeLamports),
|
|
2694
|
+
line("Priority fee:", estimate.priorityFeeLamports)
|
|
2695
|
+
];
|
|
2696
|
+
if (estimate.rentLamports > 0n) {
|
|
2697
|
+
lines.push(line("ATA rent:", estimate.rentLamports));
|
|
2698
|
+
}
|
|
2699
|
+
lines.push(line("Total:", estimate.totalLamports));
|
|
2700
|
+
return lines.join("\n");
|
|
2701
|
+
}
|
|
2702
|
+
function lamportsToSol(lamports) {
|
|
2703
|
+
const LAMPORTS_PER_SOL2 = 1000000000n;
|
|
2704
|
+
const whole = lamports / LAMPORTS_PER_SOL2;
|
|
2705
|
+
const frac = lamports % LAMPORTS_PER_SOL2;
|
|
2706
|
+
return `${whole}.${frac.toString().padStart(9, "0")}`;
|
|
2707
|
+
}
|
|
2708
|
+
var SessionSpendLimitEntrySchema = z.object({
|
|
2709
|
+
chain: z.enum(["solana"]),
|
|
2710
|
+
token: z.string().min(1).max(16).regex(/^[a-z0-9]+$/, "token must be lowercase alphanumeric"),
|
|
2711
|
+
mint: z.string().min(1).max(64).optional(),
|
|
2712
|
+
amount: z.number().positive().finite()
|
|
2713
|
+
}).strict();
|
|
2714
|
+
var GlobalConfigSchema = z.object({
|
|
2715
|
+
session_spend_limits: z.array(SessionSpendLimitEntrySchema).max(16).optional()
|
|
2716
|
+
}).strict();
|
|
2334
2717
|
function formatSol(lamports) {
|
|
2335
2718
|
const sol = new Decimal2(lamports).div(LAMPORTS_PER_SOL);
|
|
2336
2719
|
if (sol.gte(1e6)) {
|
|
@@ -2522,6 +2905,6 @@ function makeCensor() {
|
|
|
2522
2905
|
};
|
|
2523
2906
|
}
|
|
2524
2907
|
|
|
2525
|
-
export { BoundedSet, DEFAULTS, DEFAULT_KIND_OFFSET, DEFAULT_REDACT_PATHS, DiscoveryService, ElisymClient, ElisymIdentity, INPUT_REDACT_PATHS, KIND_APP_HANDLER, KIND_JOB_FEEDBACK, KIND_JOB_REQUEST, KIND_JOB_REQUEST_BASE, KIND_JOB_RESULT, KIND_JOB_RESULT_BASE, KIND_PING, KIND_PONG, LAMPORTS_PER_SOL, LIMITS, MarketplaceService, MediaService, NostrPool, PROTOCOL_FEE_BPS, PROTOCOL_PROGRAM_ID_DEVNET, PROTOCOL_TREASURY, PaymentRequestSchema, PingService, RELAYS, SECRET_REDACT_PATHS, SolanaPaymentStrategy, assertExpiry, assertLamports, buildPaymentInstructions, calculateProtocolFee, clearPriorityFeeCache, clearProtocolConfigCache, createPaymentRequestWithOnchainConfig, createSlidingWindowLimiter, estimatePriorityFeeMicroLamports, formatSol, getProtocolConfig, getProtocolProgramId, jobRequestKind, jobResultKind, makeCensor, nip44Decrypt, nip44Encrypt, parsePaymentRequest, pickPercentileFee, timeAgo, toDTag, truncateKey, validateAgentName, validateExpiry };
|
|
2908
|
+
export { BoundedSet, DEFAULTS, DEFAULT_KIND_OFFSET, DEFAULT_REDACT_PATHS, DiscoveryService, ElisymClient, ElisymIdentity, GlobalConfigSchema, INPUT_REDACT_PATHS, KIND_APP_HANDLER, KIND_JOB_FEEDBACK, KIND_JOB_REQUEST, KIND_JOB_REQUEST_BASE, KIND_JOB_RESULT, KIND_JOB_RESULT_BASE, KIND_PING, KIND_PONG, KNOWN_ASSETS, LAMPORTS_PER_SOL, LIMITS, MarketplaceService, MediaService, NATIVE_SOL, NostrPool, PROTOCOL_FEE_BPS, PROTOCOL_PROGRAM_ID_DEVNET, PROTOCOL_TREASURY, PaymentRequestSchema, PingService, RELAYS, SECRET_REDACT_PATHS, SessionSpendLimitEntrySchema, SolanaPaymentStrategy, USDC_SOLANA_DEVNET, assertExpiry, assertLamports, assetByKey, assetKey, buildPaymentInstructions, calculateProtocolFee, clearPriorityFeeCache, clearProtocolConfigCache, createPaymentRequestWithOnchainConfig, createSlidingWindowLimiter, estimatePriorityFeeMicroLamports, estimateSolFeeLamports, formatAssetAmount, formatFeeBreakdown, formatSol, getProtocolConfig, getProtocolProgramId, jobRequestKind, jobResultKind, makeCensor, nip44Decrypt, nip44Encrypt, parseAssetAmount, parsePaymentRequest, pickPercentileFee, resolveAssetFromPaymentRequest, resolveKnownAsset, timeAgo, toDTag, truncateKey, validateAgentName, validateExpiry };
|
|
2526
2909
|
//# sourceMappingURL=index.js.map
|
|
2527
2910
|
//# sourceMappingURL=index.js.map
|