@piprail/sdk 1.20.0 → 1.21.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.
@@ -1,364 +0,0 @@
1
- import {
2
- ConfirmationTimeoutError,
3
- UnknownTokenError,
4
- WrongFamilyError,
5
- nativeCost,
6
- rejectForeignToken,
7
- toInsufficientFundsError
8
- } from "./chunk-ILPABTI2.js";
9
-
10
- // src/drivers/solana/index.ts
11
- import { Connection, PublicKey as PublicKey2 } from "@solana/web3.js";
12
- import {
13
- getAccount,
14
- getAssociatedTokenAddressSync as getAssociatedTokenAddressSync2,
15
- TokenAccountNotFoundError
16
- } from "@solana/spl-token";
17
-
18
- // src/drivers/solana/chains.ts
19
- var SOL_DECIMALS = 9;
20
- var SOLANA_MAINNET = {
21
- caip2: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
22
- defaultRpc: "https://api.mainnet-beta.solana.com",
23
- tokens: {
24
- USDC: {
25
- mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
26
- decimals: 6,
27
- symbol: "USDC"
28
- },
29
- USDT: {
30
- mint: "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB",
31
- decimals: 6,
32
- symbol: "USDT"
33
- }
34
- }
35
- };
36
-
37
- // src/drivers/solana/pay.ts
38
- import {
39
- PublicKey,
40
- SystemProgram,
41
- Transaction
42
- } from "@solana/web3.js";
43
- import {
44
- createAssociatedTokenAccountIdempotentInstruction,
45
- createTransferCheckedInstruction,
46
- getAssociatedTokenAddressSync
47
- } from "@solana/spl-token";
48
- async function paySolana(params) {
49
- const { connection, keypair, accept } = params;
50
- const payTo = new PublicKey(accept.payTo);
51
- const amount = BigInt(accept.amount);
52
- const tx = new Transaction();
53
- if (accept.asset === "native") {
54
- tx.add(
55
- SystemProgram.transfer({
56
- fromPubkey: keypair.publicKey,
57
- toPubkey: payTo,
58
- lamports: amount
59
- })
60
- );
61
- } else {
62
- const mint = new PublicKey(accept.asset);
63
- const source = getAssociatedTokenAddressSync(mint, keypair.publicKey);
64
- const dest = getAssociatedTokenAddressSync(mint, payTo);
65
- tx.add(
66
- createAssociatedTokenAccountIdempotentInstruction(
67
- keypair.publicKey,
68
- // payer for any rent
69
- dest,
70
- payTo,
71
- mint
72
- )
73
- );
74
- tx.add(
75
- createTransferCheckedInstruction(
76
- source,
77
- mint,
78
- dest,
79
- keypair.publicKey,
80
- amount,
81
- accept.extra.decimals
82
- )
83
- );
84
- }
85
- const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
86
- tx.recentBlockhash = blockhash;
87
- tx.lastValidBlockHeight = lastValidBlockHeight;
88
- tx.feePayer = keypair.publicKey;
89
- tx.sign(keypair);
90
- const signature = await connection.sendRawTransaction(tx.serialize(), {
91
- maxRetries: 5
92
- });
93
- await connection.confirmTransaction(
94
- { signature, blockhash, lastValidBlockHeight },
95
- "confirmed"
96
- );
97
- return signature;
98
- }
99
-
100
- // src/drivers/solana/verify.ts
101
- async function verifySolana(params) {
102
- const { connection, signature, accept } = params;
103
- const required = BigInt(accept.amount);
104
- let tx;
105
- try {
106
- tx = await connection.getParsedTransaction(signature, {
107
- commitment: "confirmed",
108
- maxSupportedTransactionVersion: 0
109
- });
110
- } catch {
111
- return notFound(signature);
112
- }
113
- if (!tx) return notFound(signature);
114
- const meta = tx.meta;
115
- if (!meta) {
116
- return { ok: false, error: "no_meta", detail: `No metadata for ${signature}.` };
117
- }
118
- if (meta.err !== null) {
119
- return {
120
- ok: false,
121
- error: "tx_reverted",
122
- detail: `Transaction ${signature} failed on-chain.`
123
- };
124
- }
125
- if (typeof tx.blockTime !== "number") {
126
- return {
127
- ok: false,
128
- error: "payment_expired",
129
- detail: `Cannot determine the age of ${signature} (no blockTime).`
130
- };
131
- }
132
- const ageSeconds = Math.floor(Date.now() / 1e3) - tx.blockTime;
133
- if (ageSeconds > accept.maxTimeoutSeconds) {
134
- return {
135
- ok: false,
136
- error: "payment_expired",
137
- detail: `Payment is ${ageSeconds}s old; max allowed is ${accept.maxTimeoutSeconds}s.`
138
- };
139
- }
140
- const accountKeys = tx.transaction.message.accountKeys.map(
141
- (k) => k.pubkey.toBase58()
142
- );
143
- const payer = accountKeys[0] ?? "";
144
- if (accept.asset === "native") {
145
- const idx = accountKeys.indexOf(accept.payTo);
146
- if (idx < 0) {
147
- return {
148
- ok: false,
149
- error: "wrong_recipient",
150
- detail: `Native payment in ${signature} did not credit ${accept.payTo}.`
151
- };
152
- }
153
- const delta = BigInt(meta.postBalances[idx] ?? 0) - BigInt(meta.preBalances[idx] ?? 0);
154
- if (delta < required) {
155
- return {
156
- ok: false,
157
- error: "amount_too_low",
158
- detail: `Credited ${delta} lamports, required ${required}.`
159
- };
160
- }
161
- } else {
162
- const mint = accept.asset;
163
- const pre = meta.preTokenBalances ?? [];
164
- const post = meta.postTokenBalances ?? [];
165
- let delta = 0n;
166
- for (const p of post) {
167
- if (p.mint !== mint || p.owner !== accept.payTo) continue;
168
- const before = pre.find((x) => x.accountIndex === p.accountIndex);
169
- delta += BigInt(p.uiTokenAmount.amount) - BigInt(before?.uiTokenAmount.amount ?? "0");
170
- }
171
- if (delta < required) {
172
- return {
173
- ok: false,
174
- error: "transfer_not_found",
175
- detail: `No SPL transfer of >= ${required} (mint ${mint}) to ${accept.payTo} in ${signature}.`
176
- };
177
- }
178
- }
179
- return {
180
- ok: true,
181
- receipt: {
182
- scheme: "onchain-proof",
183
- success: true,
184
- network: accept.network,
185
- transaction: signature,
186
- asset: accept.asset,
187
- amount: accept.amount,
188
- payer,
189
- payTo: accept.payTo,
190
- verifiedAt: (/* @__PURE__ */ new Date()).toISOString()
191
- }
192
- };
193
- }
194
- function notFound(signature) {
195
- return {
196
- ok: false,
197
- error: "tx_not_found",
198
- detail: `Signature ${signature} not found or not yet confirmed.`
199
- };
200
- }
201
-
202
- // src/drivers/solana/wallet.ts
203
- import { Keypair as Keypair2 } from "@solana/web3.js";
204
- import bs58 from "bs58";
205
- function toKeypair(wallet, network) {
206
- if (typeof wallet !== "object" || wallet === null) {
207
- throw new WrongFamilyError(
208
- `chain ${network} is Solana; wallet must be { secretKey } or { signer }.`
209
- );
210
- }
211
- if ("privateKey" in wallet || "walletClient" in wallet) {
212
- throw new WrongFamilyError(
213
- `chain ${network} is Solana; an EVM wallet can't be used \u2014 pass { secretKey } or { signer }.`
214
- );
215
- }
216
- if ("signer" in wallet) {
217
- return wallet.signer;
218
- }
219
- if ("secretKey" in wallet) {
220
- const sk = wallet.secretKey;
221
- const bytes = typeof sk === "string" ? bs58.decode(sk) : sk;
222
- return Keypair2.fromSecretKey(bytes);
223
- }
224
- throw new WrongFamilyError(
225
- `chain ${network} is Solana; wallet must be { secretKey } or { signer }.`
226
- );
227
- }
228
-
229
- // src/drivers/solana/index.ts
230
- var solanaDriver = {
231
- family: "solana",
232
- resolve(opts) {
233
- if (opts.chain !== "solana") return null;
234
- const rpcUrl = opts.rpcUrl ?? SOLANA_MAINNET.defaultRpc;
235
- return makeSolanaNetwork(SOLANA_MAINNET, rpcUrl);
236
- }
237
- };
238
- function makeSolanaNetwork(preset, rpcUrl) {
239
- const connection = new Connection(rpcUrl, "confirmed");
240
- const network = preset.caip2;
241
- return {
242
- family: "solana",
243
- network,
244
- supports: (n) => n === network,
245
- resolveToken(token) {
246
- if (token === "native") {
247
- return { asset: "native", decimals: SOL_DECIMALS, symbol: "SOL" };
248
- }
249
- if (typeof token === "string") {
250
- const info = preset.tokens[token.toUpperCase()];
251
- if (!info) {
252
- const known = Object.keys(preset.tokens).join(", ") || "(none built in)";
253
- throw new UnknownTokenError(
254
- `token "${token}" isn't built in for Solana (known: ${known}). Pass { mint, decimals } instead, or use 'native'.`
255
- );
256
- }
257
- return { asset: info.mint, decimals: info.decimals, symbol: info.symbol };
258
- }
259
- rejectForeignToken(token, "solana", network);
260
- if (!("mint" in token)) {
261
- throw new WrongFamilyError(
262
- `chain ${network} is Solana; a custom token must be { mint, decimals }.`
263
- );
264
- }
265
- return {
266
- asset: token.mint,
267
- decimals: token.decimals,
268
- ...token.symbol ? { symbol: token.symbol } : {}
269
- };
270
- },
271
- describeAsset(asset) {
272
- if (asset === "native") return { symbol: "SOL", decimals: SOL_DECIMALS };
273
- for (const info of Object.values(preset.tokens)) {
274
- if (info.mint === asset) return { symbol: info.symbol, decimals: info.decimals };
275
- }
276
- return null;
277
- },
278
- assertValidPayTo(payTo) {
279
- if (payTo.startsWith("0x")) {
280
- throw new WrongFamilyError(
281
- `chain ${network} is Solana, but payTo "${payTo}" looks like an EVM address.`
282
- );
283
- }
284
- try {
285
- new PublicKey2(payTo);
286
- } catch {
287
- throw new WrongFamilyError(
288
- `chain ${network} is Solana, but payTo "${payTo}" is not a base58 address.`
289
- );
290
- }
291
- },
292
- bindWallet(wallet) {
293
- return { _native: toKeypair(wallet, network) };
294
- },
295
- async send(wallet, accept) {
296
- try {
297
- return await paySolana({ connection, keypair: wallet._native, accept });
298
- } catch (err) {
299
- throw toInsufficientFundsError(err) ?? err;
300
- }
301
- },
302
- async confirm(ref) {
303
- let info;
304
- try {
305
- const { value } = await connection.getSignatureStatuses([ref], {
306
- searchTransactionHistory: true
307
- });
308
- info = value[0];
309
- } catch (err) {
310
- throw new ConfirmationTimeoutError(
311
- `Solana payment ${ref} could not be confirmed (RPC read failed).`,
312
- { cause: err }
313
- );
314
- }
315
- if (!info || info.err || info.confirmationStatus !== "confirmed" && info.confirmationStatus !== "finalized") {
316
- throw new ConfirmationTimeoutError(`Solana payment ${ref} did not confirm in time.`);
317
- }
318
- return { height: String(info.slot) };
319
- },
320
- async estimateCost(accept) {
321
- const base = 5000n;
322
- if (accept.asset === "native") {
323
- return nativeCost({
324
- symbol: "SOL",
325
- decimals: SOL_DECIMALS,
326
- fee: base,
327
- basis: "heuristic",
328
- detail: "1 signature (5000 lamports)"
329
- });
330
- }
331
- const ataRent = 2039280n;
332
- return nativeCost({
333
- symbol: "SOL",
334
- decimals: SOL_DECIMALS,
335
- fee: base + ataRent,
336
- basis: "heuristic",
337
- detail: "1 signature + recipient token-account rent (~0.00204 SOL, if not already created)"
338
- });
339
- },
340
- async balanceOf(wallet, asset) {
341
- const owner = wallet._native.publicKey;
342
- const native = await connection.getBalance(owner).then((n) => BigInt(n)).catch(() => null);
343
- if (asset === "native") return { token: native, native };
344
- let token;
345
- try {
346
- const ata = getAssociatedTokenAddressSync2(new PublicKey2(asset), owner);
347
- token = (await getAccount(connection, ata, "confirmed")).amount;
348
- } catch (e) {
349
- token = e instanceof TokenAccountNotFoundError ? 0n : null;
350
- }
351
- return { token, native };
352
- },
353
- // No receive prerequisite — the payer's tx idempotently creates the recipient's ATA (pay.ts).
354
- async recipientReady() {
355
- return { ready: "n/a" };
356
- },
357
- async verify(ref, accept) {
358
- return verifySolana({ connection, signature: ref, accept });
359
- }
360
- };
361
- }
362
- export {
363
- solanaDriver
364
- };