nara-sdk 1.0.77 → 1.0.79
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 +8 -3
- package/index.ts +3 -1
- package/package.json +3 -2
- package/src/bridge.ts +27 -11
- package/src/constants.ts +12 -8
- package/src/tx_parser.ts +71 -19
package/README.md
CHANGED
|
@@ -127,12 +127,17 @@ registerBridgeToken('USDT', {
|
|
|
127
127
|
### Fee configuration
|
|
128
128
|
|
|
129
129
|
Default fee: **0.5%** (50 bps), deducted from the bridged amount on the source chain.
|
|
130
|
+
Fee recipients are chain-specific (one per source chain).
|
|
130
131
|
|
|
131
132
|
```ts
|
|
132
|
-
import { setBridgeFeeRecipient } from 'nara-sdk';
|
|
133
|
+
import { setBridgeFeeRecipient, getBridgeFeeRecipient } from 'nara-sdk';
|
|
133
134
|
|
|
134
|
-
// Override fee recipient at runtime
|
|
135
|
-
setBridgeFeeRecipient('
|
|
135
|
+
// Override fee recipient at runtime (per chain)
|
|
136
|
+
setBridgeFeeRecipient('solana', 'SolanaFeeRecipientPubkey...');
|
|
137
|
+
setBridgeFeeRecipient('nara', 'NaraFeeRecipientPubkey...');
|
|
138
|
+
|
|
139
|
+
// Read current recipient
|
|
140
|
+
const recipient = getBridgeFeeRecipient('solana'); // PublicKey
|
|
136
141
|
|
|
137
142
|
// Or per-call
|
|
138
143
|
await bridgeTransfer(conn, wallet, {
|
package/index.ts
CHANGED
|
@@ -16,7 +16,8 @@ export {
|
|
|
16
16
|
DEFAULT_AGENT_REGISTRY_PROGRAM_ID,
|
|
17
17
|
DEFAULT_ALT_ADDRESS,
|
|
18
18
|
DEFAULT_BRIDGE_FEE_BPS,
|
|
19
|
-
|
|
19
|
+
DEFAULT_BRIDGE_FEE_RECIPIENT_SOLANA,
|
|
20
|
+
DEFAULT_BRIDGE_FEE_RECIPIENT_NARA,
|
|
20
21
|
BRIDGE_FEE_BPS_DENOMINATOR,
|
|
21
22
|
} from "./src/constants";
|
|
22
23
|
|
|
@@ -222,6 +223,7 @@ export {
|
|
|
222
223
|
// Export transaction parser
|
|
223
224
|
export {
|
|
224
225
|
parseTxFromHash,
|
|
226
|
+
parseTxsFromHashes,
|
|
225
227
|
parseTxResponse,
|
|
226
228
|
formatParsedTx,
|
|
227
229
|
type ParsedInstruction,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nara-sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.79",
|
|
4
4
|
"description": "SDK for the Nara chain (Solana-compatible)",
|
|
5
5
|
"module": "index.ts",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"sdk"
|
|
19
19
|
],
|
|
20
20
|
"scripts": {
|
|
21
|
-
"test": "tsx test/read_api.test.ts"
|
|
21
|
+
"test": "tsx test/read_api.test.ts",
|
|
22
|
+
"test:parser": "tsx test/tx_parser.test.ts"
|
|
22
23
|
},
|
|
23
24
|
"author": "",
|
|
24
25
|
"license": "MIT",
|
package/src/bridge.ts
CHANGED
|
@@ -27,7 +27,8 @@ import {
|
|
|
27
27
|
import {
|
|
28
28
|
BRIDGE_FEE_BPS_DENOMINATOR,
|
|
29
29
|
DEFAULT_BRIDGE_FEE_BPS,
|
|
30
|
-
|
|
30
|
+
DEFAULT_BRIDGE_FEE_RECIPIENT_SOLANA,
|
|
31
|
+
DEFAULT_BRIDGE_FEE_RECIPIENT_NARA,
|
|
31
32
|
} from "./constants";
|
|
32
33
|
import { sendTx } from "./tx";
|
|
33
34
|
|
|
@@ -185,23 +186,38 @@ function getToken(symbol: string): BridgeTokenConfig {
|
|
|
185
186
|
return t;
|
|
186
187
|
}
|
|
187
188
|
|
|
188
|
-
// ─── Fee recipient (runtime override)
|
|
189
|
+
// ─── Fee recipient (runtime override, per chain) ──────────────────
|
|
189
190
|
|
|
190
|
-
|
|
191
|
+
const _feeRecipientOverrides: Record<BridgeChain, PublicKey | null> = {
|
|
192
|
+
solana: null,
|
|
193
|
+
nara: null,
|
|
194
|
+
};
|
|
191
195
|
|
|
192
|
-
/**
|
|
193
|
-
|
|
196
|
+
/**
|
|
197
|
+
* Override the bridge fee recipient for a specific source chain at runtime.
|
|
198
|
+
* Pass `null` as recipient to clear the override and fall back to the default.
|
|
199
|
+
*/
|
|
200
|
+
export function setBridgeFeeRecipient(
|
|
201
|
+
chain: BridgeChain,
|
|
202
|
+
recipient: PublicKey | string | null
|
|
203
|
+
): void {
|
|
194
204
|
if (recipient === null) {
|
|
195
|
-
|
|
205
|
+
_feeRecipientOverrides[chain] = null;
|
|
196
206
|
return;
|
|
197
207
|
}
|
|
198
|
-
|
|
208
|
+
_feeRecipientOverrides[chain] =
|
|
199
209
|
typeof recipient === "string" ? new PublicKey(recipient) : recipient;
|
|
200
210
|
}
|
|
201
211
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
212
|
+
/** Get the current fee recipient for a source chain (override or default). */
|
|
213
|
+
export function getBridgeFeeRecipient(chain: BridgeChain): PublicKey {
|
|
214
|
+
const override = _feeRecipientOverrides[chain];
|
|
215
|
+
if (override) return override;
|
|
216
|
+
const defaultAddr =
|
|
217
|
+
chain === "solana"
|
|
218
|
+
? DEFAULT_BRIDGE_FEE_RECIPIENT_SOLANA
|
|
219
|
+
: DEFAULT_BRIDGE_FEE_RECIPIENT_NARA;
|
|
220
|
+
return new PublicKey(defaultAddr);
|
|
205
221
|
}
|
|
206
222
|
|
|
207
223
|
// ─── PDA derivation ───────────────────────────────────────────────
|
|
@@ -515,7 +531,7 @@ export function makeBridgeIxs(params: BridgeTransferParams): BridgeIxsResult {
|
|
|
515
531
|
throw new Error("bridge amount after fee is zero — increase amount or lower feeBps");
|
|
516
532
|
}
|
|
517
533
|
|
|
518
|
-
const recipientForFee = feeRecipient ?? getBridgeFeeRecipient();
|
|
534
|
+
const recipientForFee = feeRecipient ?? getBridgeFeeRecipient(fromChain);
|
|
519
535
|
const feeIxs = makeBridgeFeeIxs({
|
|
520
536
|
token,
|
|
521
537
|
fromChain,
|
package/src/constants.ts
CHANGED
|
@@ -48,8 +48,8 @@ export const DEFAULT_ALT_ADDRESS = process.env.ALT_ADDRESS || "3uw7RatGTB4hdHnuV
|
|
|
48
48
|
|
|
49
49
|
/**
|
|
50
50
|
* Bridge fee in basis points (1 bps = 0.01%). 50 = 0.5%.
|
|
51
|
-
* Deducted from the bridged amount and transferred to
|
|
52
|
-
* in the same transaction.
|
|
51
|
+
* Deducted from the bridged amount and transferred to the chain-specific
|
|
52
|
+
* fee recipient in the same transaction.
|
|
53
53
|
*/
|
|
54
54
|
export const DEFAULT_BRIDGE_FEE_BPS = 50;
|
|
55
55
|
|
|
@@ -57,11 +57,15 @@ export const DEFAULT_BRIDGE_FEE_BPS = 50;
|
|
|
57
57
|
export const BRIDGE_FEE_BPS_DENOMINATOR = 10000;
|
|
58
58
|
|
|
59
59
|
/**
|
|
60
|
-
* Default fee recipient pubkey
|
|
61
|
-
*
|
|
62
|
-
* Override at runtime via setBridgeFeeRecipient() in src/bridge.ts.
|
|
63
|
-
*
|
|
64
|
-
* NOTE: replace with actual fee recipient before mainnet usage.
|
|
60
|
+
* Default fee recipient pubkey on Solana (when bridging FROM Solana).
|
|
61
|
+
* Override at runtime via setBridgeFeeRecipient("solana", ...).
|
|
65
62
|
*/
|
|
66
|
-
export const
|
|
63
|
+
export const DEFAULT_BRIDGE_FEE_RECIPIENT_SOLANA =
|
|
64
|
+
"HaPQTvGJBunoWA3AyyWRL9etVEbQWsXVoj3fHpBprLy5";
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Default fee recipient pubkey on Nara (when bridging FROM Nara).
|
|
68
|
+
* Override at runtime via setBridgeFeeRecipient("nara", ...).
|
|
69
|
+
*/
|
|
70
|
+
export const DEFAULT_BRIDGE_FEE_RECIPIENT_NARA =
|
|
67
71
|
"FERLFwBpCyoEuvFP68eP6Fv4FCVocnNyyFUCYwpfmjqn";
|
package/src/tx_parser.ts
CHANGED
|
@@ -43,7 +43,10 @@ import { BRIDGE_TOKENS } from "./bridge";
|
|
|
43
43
|
// ─── Types ────────────────────────────────────────────────────────
|
|
44
44
|
|
|
45
45
|
export interface ParsedInstruction {
|
|
46
|
-
/**
|
|
46
|
+
/**
|
|
47
|
+
* Instruction index. For top-level ixs this is the 0-based position in the tx.
|
|
48
|
+
* For inner ixs this is the 0-based position within the parent's CPI list.
|
|
49
|
+
*/
|
|
47
50
|
index: number;
|
|
48
51
|
/** Human-readable program name */
|
|
49
52
|
programName: string;
|
|
@@ -57,6 +60,11 @@ export interface ParsedInstruction {
|
|
|
57
60
|
accounts: string[];
|
|
58
61
|
/** Raw instruction data (base64) */
|
|
59
62
|
rawData: string;
|
|
63
|
+
/**
|
|
64
|
+
* Inner instructions (CPI calls) made by this instruction.
|
|
65
|
+
* Only populated on top-level ixs; empty/undefined for ixs that made no CPI calls.
|
|
66
|
+
*/
|
|
67
|
+
innerInstructions?: ParsedInstruction[];
|
|
60
68
|
}
|
|
61
69
|
|
|
62
70
|
export interface ParsedTransaction {
|
|
@@ -72,7 +80,7 @@ export interface ParsedTransaction {
|
|
|
72
80
|
error: string | null;
|
|
73
81
|
/** Fee paid in lamports */
|
|
74
82
|
fee: number;
|
|
75
|
-
/**
|
|
83
|
+
/** Top-level instructions. Inner (CPI) ixs are nested under each one via `innerInstructions`. */
|
|
76
84
|
instructions: ParsedInstruction[];
|
|
77
85
|
/** Log messages */
|
|
78
86
|
logs: string[];
|
|
@@ -635,6 +643,39 @@ export async function parseTxFromHash(
|
|
|
635
643
|
return parseTxResponse(tx);
|
|
636
644
|
}
|
|
637
645
|
|
|
646
|
+
/**
|
|
647
|
+
* Fetch and parse multiple transactions in a single batch RPC request.
|
|
648
|
+
*
|
|
649
|
+
* Uses `connection.getTransactions(sigs)` which sends one JSON-RPC batch
|
|
650
|
+
* under the hood (via `_rpcBatchRequest`), so N signatures → 1 HTTP call.
|
|
651
|
+
*
|
|
652
|
+
* Returns a result array aligned with the input: each entry is either a
|
|
653
|
+
* `ParsedTransaction` or `null` if the RPC could not find that signature.
|
|
654
|
+
* This preserves index mapping so the caller can pair results with inputs.
|
|
655
|
+
*
|
|
656
|
+
* @example
|
|
657
|
+
* ```ts
|
|
658
|
+
* const results = await parseTxsFromHashes(connection, [sig1, sig2, sig3]);
|
|
659
|
+
* results.forEach((r, i) => {
|
|
660
|
+
* if (!r) console.log(`${sigs[i]} not found`);
|
|
661
|
+
* else console.log(formatParsedTx(r));
|
|
662
|
+
* });
|
|
663
|
+
* ```
|
|
664
|
+
*/
|
|
665
|
+
export async function parseTxsFromHashes(
|
|
666
|
+
connection: Connection,
|
|
667
|
+
signatures: string[]
|
|
668
|
+
): Promise<(ParsedTransaction | null)[]> {
|
|
669
|
+
if (signatures.length === 0) return [];
|
|
670
|
+
|
|
671
|
+
const txs = await connection.getTransactions(signatures, {
|
|
672
|
+
maxSupportedTransactionVersion: 0,
|
|
673
|
+
commitment: "confirmed",
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
return txs.map((tx) => (tx ? parseTxResponse(tx) : null));
|
|
677
|
+
}
|
|
678
|
+
|
|
638
679
|
/**
|
|
639
680
|
* Parse a VersionedTransactionResponse (from getTransaction) synchronously.
|
|
640
681
|
*
|
|
@@ -663,25 +704,25 @@ export function parseTxResponse(tx: VersionedTransactionResponse): ParsedTransac
|
|
|
663
704
|
decodeInstruction(ix, i, accountKeys)
|
|
664
705
|
);
|
|
665
706
|
|
|
666
|
-
//
|
|
707
|
+
// Attach inner instructions (CPI calls) under their parent ix.
|
|
708
|
+
// RPC response: meta.innerInstructions is [{ index, instructions: [...] }, ...]
|
|
709
|
+
// where `index` points to the top-level ix that made the CPI calls.
|
|
667
710
|
if (tx.meta?.innerInstructions) {
|
|
668
711
|
for (const inner of tx.meta.innerInstructions) {
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
712
|
+
const parent = instructions[inner.index];
|
|
713
|
+
if (!parent) continue;
|
|
714
|
+
parent.innerInstructions = inner.instructions.map((innerIx, j) => {
|
|
715
|
+
const dataBuf: Buffer =
|
|
716
|
+
typeof innerIx.data === "string"
|
|
717
|
+
? Buffer.from(decodeBase58(innerIx.data))
|
|
718
|
+
: Buffer.from(innerIx.data);
|
|
676
719
|
const innerCompiled: MessageCompiledInstruction = {
|
|
677
720
|
programIdIndex: innerIx.programIdIndex,
|
|
678
721
|
accountKeyIndexes: innerIx.accounts,
|
|
679
722
|
data: dataBuf,
|
|
680
723
|
};
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
);
|
|
684
|
-
}
|
|
724
|
+
return decodeInstruction(innerCompiled, j, accountKeys);
|
|
725
|
+
});
|
|
685
726
|
}
|
|
686
727
|
}
|
|
687
728
|
|
|
@@ -739,12 +780,23 @@ export function formatParsedTx(parsed: ParsedTransaction): string {
|
|
|
739
780
|
lines.push("");
|
|
740
781
|
|
|
741
782
|
for (const ix of parsed.instructions) {
|
|
742
|
-
|
|
743
|
-
.map(([k, v]) => `${k}=${v}`)
|
|
744
|
-
.join(", ");
|
|
745
|
-
lines.push(` #${ix.index} [${ix.programName}] ${ix.type}`);
|
|
746
|
-
if (infoStr) lines.push(` ${infoStr}`);
|
|
783
|
+
formatIxLines(ix, 0, lines);
|
|
747
784
|
}
|
|
748
785
|
|
|
749
786
|
return lines.join("\n");
|
|
750
787
|
}
|
|
788
|
+
|
|
789
|
+
function formatIxLines(ix: ParsedInstruction, depth: number, lines: string[]): void {
|
|
790
|
+
const indent = " ".repeat(depth + 1);
|
|
791
|
+
const prefix = depth === 0 ? `#${ix.index}` : `↳ ${ix.index}`;
|
|
792
|
+
const infoStr = Object.entries(ix.info)
|
|
793
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
794
|
+
.join(", ");
|
|
795
|
+
lines.push(`${indent}${prefix} [${ix.programName}] ${ix.type}`);
|
|
796
|
+
if (infoStr) lines.push(`${indent} ${infoStr}`);
|
|
797
|
+
if (ix.innerInstructions && ix.innerInstructions.length > 0) {
|
|
798
|
+
for (const inner of ix.innerInstructions) {
|
|
799
|
+
formatIxLines(inner, depth + 1, lines);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
}
|