@0block.io/sdk 0.4.1 → 0.5.1
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/dist/fetch.d.ts +13 -1
- package/dist/fetch.js +25 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/zeroblock.d.ts +31 -0
- package/dist/zeroblock.js +131 -0
- package/package.json +1 -1
package/dist/fetch.d.ts
CHANGED
|
@@ -19,7 +19,19 @@ export declare function resolveTokenProgram(fetcher: AccountFetcher, mint: Addre
|
|
|
19
19
|
export declare function fetchPumpfunGlobalFeeRecipient(fetcher: AccountFetcher, globalAccount: web3.PublicKey): Promise<web3.PublicKey>;
|
|
20
20
|
/** Non-zero entries of Global.buyback_fee_recipients ([Pubkey; 8]). */
|
|
21
21
|
export declare function fetchPumpfunBuybackFeeRecipients(fetcher: AccountFetcher, globalAccount: web3.PublicKey): Promise<web3.PublicKey[]>;
|
|
22
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* BondingCurve.creator (offset: discriminator + 5×u64 + bool), with tradeability
|
|
24
|
+
* preflight. Throws a PRECISE error instead of letting an untradeable market
|
|
25
|
+
* fail on-chain (where the user would still pay the tx fee + tip):
|
|
26
|
+
*
|
|
27
|
+
* - `complete == true` → the token GRADUATED off the bonding curve (liquidity
|
|
28
|
+
* moved to an AMM — PumpSwap/Raydium); the pump.fun program rejects both
|
|
29
|
+
* buys and sells on a complete curve.
|
|
30
|
+
* - 49-byte legacy layout (pre-creator-fee, curve untouched since the mid-2025
|
|
31
|
+
* pump.fun upgrade) → needs the program's extend_account before any modern
|
|
32
|
+
* swap can even deserialize it. In practice these are dead tokens: anything
|
|
33
|
+
* actively traded has long been extended.
|
|
34
|
+
*/
|
|
23
35
|
export declare function fetchPumpfunBondingCurveCreator(fetcher: AccountFetcher, bondingCurve: web3.PublicKey): Promise<web3.PublicKey>;
|
|
24
36
|
/**
|
|
25
37
|
* Resolve everything a pump.fun swap needs for one mint. Run this server-side
|
package/dist/fetch.js
CHANGED
|
@@ -55,17 +55,38 @@ export async function fetchPumpfunBuybackFeeRecipients(fetcher, globalAccount) {
|
|
|
55
55
|
}
|
|
56
56
|
return recipients;
|
|
57
57
|
}
|
|
58
|
-
/**
|
|
58
|
+
/**
|
|
59
|
+
* BondingCurve.creator (offset: discriminator + 5×u64 + bool), with tradeability
|
|
60
|
+
* preflight. Throws a PRECISE error instead of letting an untradeable market
|
|
61
|
+
* fail on-chain (where the user would still pay the tx fee + tip):
|
|
62
|
+
*
|
|
63
|
+
* - `complete == true` → the token GRADUATED off the bonding curve (liquidity
|
|
64
|
+
* moved to an AMM — PumpSwap/Raydium); the pump.fun program rejects both
|
|
65
|
+
* buys and sells on a complete curve.
|
|
66
|
+
* - 49-byte legacy layout (pre-creator-fee, curve untouched since the mid-2025
|
|
67
|
+
* pump.fun upgrade) → needs the program's extend_account before any modern
|
|
68
|
+
* swap can even deserialize it. In practice these are dead tokens: anything
|
|
69
|
+
* actively traded has long been extended.
|
|
70
|
+
*/
|
|
59
71
|
export async function fetchPumpfunBondingCurveCreator(fetcher, bondingCurve) {
|
|
60
72
|
const info = await fetcher.getAccountInfo(bondingCurve);
|
|
61
73
|
if (!info) {
|
|
62
74
|
throw new Error(`pump.fun bonding curve not found: ${bondingCurve.toBase58()}`);
|
|
63
75
|
}
|
|
64
|
-
const
|
|
65
|
-
if (info.data.length <
|
|
76
|
+
const legacyLen = 8 + 5 * 8 + 1; // ..= complete flag (pre-creator layout)
|
|
77
|
+
if (info.data.length < legacyLen) {
|
|
66
78
|
throw new Error("pump.fun bonding curve data too short");
|
|
67
79
|
}
|
|
68
|
-
|
|
80
|
+
const complete = info.data[legacyLen - 1] === 1;
|
|
81
|
+
if (complete) {
|
|
82
|
+
throw new Error("pump.fun bonding curve is complete — the token graduated to an AMM " +
|
|
83
|
+
"(PumpSwap/Raydium); it cannot be traded on the bonding curve");
|
|
84
|
+
}
|
|
85
|
+
if (info.data.length < legacyLen + 32) {
|
|
86
|
+
throw new Error("legacy pump.fun bonding curve (pre-creator layout, never extended) — " +
|
|
87
|
+
"the account needs pump.fun extend_account before modern swaps can run");
|
|
88
|
+
}
|
|
89
|
+
return new web3.PublicKey(info.data.slice(legacyLen, legacyLen + 32));
|
|
69
90
|
}
|
|
70
91
|
/**
|
|
71
92
|
* Resolve everything a pump.fun swap needs for one mint. Run this server-side
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export * from "./types.js";
|
|
|
3
3
|
export { createContext, type CreateContextParams, type TenantInit, type ZeroBlockContext, } from "./context.js";
|
|
4
4
|
export { buildSendTransactionRequest, createLandingClient, withLandingTip, type LandingClient, type LandingServiceConfig, type SendTransactionOptions, } from "./landing.js";
|
|
5
5
|
export { fetchRecentBlockhash, type FetchBlockhashOptions, type RecentBlockhash, } from "./blockhash.js";
|
|
6
|
+
export { zeroblockFetcher, fetchTokenBalanceViaZeroblock, fetchLookupTableViaZeroblock, type ZeroblockReadOptions, type ZeroblockTokenBalance, } from "./zeroblock.js";
|
|
6
7
|
export { toPk, deriveAta, deriveOrderMarker, deriveWrapperConfig, deriveRouterGlobalConfig, deriveRouterTenantConfig, derivePumpfunGlobal, derivePumpfunBondingCurve, derivePumpfunBondingCurveV2, derivePumpfunEventAuthority, derivePumpfunCreatorVault, derivePumpfunGlobalVolumeAccumulator, derivePumpfunUserVolumeAccumulator, derivePumpfunFeeConfig, } from "./pda.js";
|
|
7
8
|
export { type AccountFetcher, connectionFetcher, resolveTokenProgram, resolvePumpfunMarketContext, fetchPumpfunGlobalFeeRecipient, fetchPumpfunBondingCurveCreator, fetchPumpfunBuybackFeeRecipients, } from "./fetch.js";
|
|
8
9
|
export { buildPumpfunRemainingAccounts, type PumpfunRemainingAccountsParams, } from "./venues/pumpfun.js";
|
package/dist/index.js
CHANGED
|
@@ -16,6 +16,7 @@ export * from "./types.js";
|
|
|
16
16
|
export { createContext, } from "./context.js";
|
|
17
17
|
export { buildSendTransactionRequest, createLandingClient, withLandingTip, } from "./landing.js";
|
|
18
18
|
export { fetchRecentBlockhash, } from "./blockhash.js";
|
|
19
|
+
export { zeroblockFetcher, fetchTokenBalanceViaZeroblock, fetchLookupTableViaZeroblock, } from "./zeroblock.js";
|
|
19
20
|
export { toPk, deriveAta, deriveOrderMarker, deriveWrapperConfig, deriveRouterGlobalConfig, deriveRouterTenantConfig, derivePumpfunGlobal, derivePumpfunBondingCurve, derivePumpfunBondingCurveV2, derivePumpfunEventAuthority, derivePumpfunCreatorVault, derivePumpfunGlobalVolumeAccumulator, derivePumpfunUserVolumeAccumulator, derivePumpfunFeeConfig, } from "./pda.js";
|
|
20
21
|
export { connectionFetcher, resolveTokenProgram, resolvePumpfunMarketContext, fetchPumpfunGlobalFeeRecipient, fetchPumpfunBondingCurveCreator, fetchPumpfunBuybackFeeRecipients, } from "./fetch.js";
|
|
21
22
|
export { buildPumpfunRemainingAccounts, } from "./venues/pumpfun.js";
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { AccountFetcher } from "./fetch.js";
|
|
2
|
+
import type { Address, LookupTableData } from "./types.js";
|
|
3
|
+
export interface ZeroblockReadOptions {
|
|
4
|
+
/** Injectable fetch (tests / non-standard runtimes). Defaults to global fetch. */
|
|
5
|
+
fetchImpl?: typeof fetch;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* An {@link AccountFetcher} backed by 0Block's read gateway instead of a
|
|
9
|
+
* web3.js Connection. Coalesces every getAccountInfo issued in the same
|
|
10
|
+
* microtask into ONE getMultipleAccounts round-trip (the market resolver reads
|
|
11
|
+
* ~4 accounts concurrently → a single request), and dedupes repeated addresses.
|
|
12
|
+
*/
|
|
13
|
+
export declare function zeroblockFetcher(apiBaseUrl: string, opts?: ZeroblockReadOptions): AccountFetcher;
|
|
14
|
+
export interface ZeroblockTokenBalance {
|
|
15
|
+
/** Raw base-unit balance (summed across the owner's token accounts), integer string. */
|
|
16
|
+
amountBaseUnits: string;
|
|
17
|
+
decimals: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The SPL token balance of `owner` for `mint`, via 0Block's
|
|
21
|
+
* getTokenAccountsByOwner proxy. Returns a ZERO balance (not an error) when the
|
|
22
|
+
* owner holds no token account for the mint — a legitimate "nothing to sell"
|
|
23
|
+
* state. Sums across multiple token accounts (rare multi-ATA case).
|
|
24
|
+
*/
|
|
25
|
+
export declare function fetchTokenBalanceViaZeroblock(apiBaseUrl: string, owner: Address, mint: Address, opts?: ZeroblockReadOptions): Promise<ZeroblockTokenBalance>;
|
|
26
|
+
/**
|
|
27
|
+
* Fetch and decode an Address Lookup Table via 0Block's accounts proxy —
|
|
28
|
+
* returns the table key + its stored addresses (base58), ready to pass as a
|
|
29
|
+
* {@link LookupTableData}. Returns null when the table account does not exist.
|
|
30
|
+
*/
|
|
31
|
+
export declare function fetchLookupTableViaZeroblock(apiBaseUrl: string, tableAddress: Address, opts?: ZeroblockReadOptions): Promise<LookupTableData | null>;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// 0Block READ GATEWAY client. Lets a browser build swap transactions with NO
|
|
2
|
+
// Solana RPC of its own: 0Block proxies the handful of read methods a swap
|
|
3
|
+
// needs (getMultipleAccounts, getTokenAccountsByOwner) to its OWN upstream RPC
|
|
4
|
+
// node and returns the standard JSON-RPC `result` verbatim. This module speaks
|
|
5
|
+
// that gateway and adapts it to the SDK's AccountFetcher seam, so the pump.fun
|
|
6
|
+
// market resolver, ALT decode, and balance reads all work unchanged — just
|
|
7
|
+
// sourced from 0Block instead of web3.js.
|
|
8
|
+
//
|
|
9
|
+
// Endpoints (all public, no API key — the data is public on-chain):
|
|
10
|
+
// POST {base}/api/v1/accounts { addresses:[<b58>...] } -> RPC getMultipleAccounts result
|
|
11
|
+
// GET {base}/api/v1/token-accounts?owner=<b58>&mint=<b58> -> RPC getTokenAccountsByOwner result
|
|
12
|
+
import * as web3 from "@solana/web3.js";
|
|
13
|
+
import { toPk } from "./pda.js";
|
|
14
|
+
/** Strip trailing slashes so `${base}/api/v1/...` is always well-formed. */
|
|
15
|
+
function normalizeBase(apiBaseUrl) {
|
|
16
|
+
return apiBaseUrl.replace(/\/+$/, "");
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Fetch raw account infos for `addresses` (order-preserving) via 0Block's
|
|
20
|
+
* getMultipleAccounts proxy. Missing accounts come back as null.
|
|
21
|
+
*/
|
|
22
|
+
async function fetchAccountsRaw(base, addresses, fetchImpl) {
|
|
23
|
+
const res = await fetchImpl(`${base}/api/v1/accounts`, {
|
|
24
|
+
method: "POST",
|
|
25
|
+
headers: { "Content-Type": "application/json" },
|
|
26
|
+
body: JSON.stringify({ addresses }),
|
|
27
|
+
});
|
|
28
|
+
if (!res.ok) {
|
|
29
|
+
throw new Error(`0Block accounts unavailable (HTTP ${res.status})`);
|
|
30
|
+
}
|
|
31
|
+
const body = (await res.json());
|
|
32
|
+
if (body.error) {
|
|
33
|
+
throw new Error(`0Block accounts error: ${body.error.message ?? "unknown"}`);
|
|
34
|
+
}
|
|
35
|
+
const value = body.value ?? [];
|
|
36
|
+
return addresses.map((_, i) => {
|
|
37
|
+
const acct = value[i];
|
|
38
|
+
if (!acct || acct.data === undefined)
|
|
39
|
+
return null;
|
|
40
|
+
const b64 = Array.isArray(acct.data) ? acct.data[0] : acct.data;
|
|
41
|
+
return {
|
|
42
|
+
data: new Uint8Array(Buffer.from(b64, "base64")),
|
|
43
|
+
owner: new web3.PublicKey(acct.owner ?? web3.PublicKey.default.toBase58()),
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* An {@link AccountFetcher} backed by 0Block's read gateway instead of a
|
|
49
|
+
* web3.js Connection. Coalesces every getAccountInfo issued in the same
|
|
50
|
+
* microtask into ONE getMultipleAccounts round-trip (the market resolver reads
|
|
51
|
+
* ~4 accounts concurrently → a single request), and dedupes repeated addresses.
|
|
52
|
+
*/
|
|
53
|
+
export function zeroblockFetcher(apiBaseUrl, opts) {
|
|
54
|
+
const base = normalizeBase(apiBaseUrl);
|
|
55
|
+
const fetchImpl = opts?.fetchImpl ?? fetch;
|
|
56
|
+
let queue = [];
|
|
57
|
+
let scheduled = false;
|
|
58
|
+
const flush = async () => {
|
|
59
|
+
const batch = queue;
|
|
60
|
+
queue = [];
|
|
61
|
+
scheduled = false;
|
|
62
|
+
const unique = [...new Set(batch.map((p) => p.address.toBase58()))];
|
|
63
|
+
try {
|
|
64
|
+
const infos = await fetchAccountsRaw(base, unique, fetchImpl);
|
|
65
|
+
const byAddr = new Map(unique.map((a, i) => [a, infos[i]]));
|
|
66
|
+
for (const p of batch)
|
|
67
|
+
p.resolve(byAddr.get(p.address.toBase58()) ?? null);
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
for (const p of batch)
|
|
71
|
+
p.reject(err);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
return {
|
|
75
|
+
getAccountInfo(address) {
|
|
76
|
+
return new Promise((resolve, reject) => {
|
|
77
|
+
queue.push({ address, resolve, reject });
|
|
78
|
+
if (!scheduled) {
|
|
79
|
+
scheduled = true;
|
|
80
|
+
queueMicrotask(() => void flush());
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* The SPL token balance of `owner` for `mint`, via 0Block's
|
|
88
|
+
* getTokenAccountsByOwner proxy. Returns a ZERO balance (not an error) when the
|
|
89
|
+
* owner holds no token account for the mint — a legitimate "nothing to sell"
|
|
90
|
+
* state. Sums across multiple token accounts (rare multi-ATA case).
|
|
91
|
+
*/
|
|
92
|
+
export async function fetchTokenBalanceViaZeroblock(apiBaseUrl, owner, mint, opts) {
|
|
93
|
+
const base = normalizeBase(apiBaseUrl);
|
|
94
|
+
const fetchImpl = opts?.fetchImpl ?? fetch;
|
|
95
|
+
const ownerB58 = toPk(owner).toBase58();
|
|
96
|
+
const mintB58 = toPk(mint).toBase58();
|
|
97
|
+
const res = await fetchImpl(`${base}/api/v1/token-accounts?owner=${ownerB58}&mint=${mintB58}`);
|
|
98
|
+
if (!res.ok) {
|
|
99
|
+
throw new Error(`0Block token balance unavailable (HTTP ${res.status})`);
|
|
100
|
+
}
|
|
101
|
+
const body = (await res.json());
|
|
102
|
+
if (body.error) {
|
|
103
|
+
throw new Error(`0Block token balance error: ${body.error.message ?? "unknown"}`);
|
|
104
|
+
}
|
|
105
|
+
let total = 0n;
|
|
106
|
+
let decimals = 0;
|
|
107
|
+
for (const acct of body.value ?? []) {
|
|
108
|
+
const tokenAmount = acct.account?.data?.parsed?.info?.tokenAmount;
|
|
109
|
+
if (!tokenAmount || typeof tokenAmount.amount !== "string")
|
|
110
|
+
continue;
|
|
111
|
+
total += BigInt(tokenAmount.amount);
|
|
112
|
+
if (typeof tokenAmount.decimals === "number")
|
|
113
|
+
decimals = tokenAmount.decimals;
|
|
114
|
+
}
|
|
115
|
+
return { amountBaseUnits: total.toString(), decimals };
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Fetch and decode an Address Lookup Table via 0Block's accounts proxy —
|
|
119
|
+
* returns the table key + its stored addresses (base58), ready to pass as a
|
|
120
|
+
* {@link LookupTableData}. Returns null when the table account does not exist.
|
|
121
|
+
*/
|
|
122
|
+
export async function fetchLookupTableViaZeroblock(apiBaseUrl, tableAddress, opts) {
|
|
123
|
+
const base = normalizeBase(apiBaseUrl);
|
|
124
|
+
const fetchImpl = opts?.fetchImpl ?? fetch;
|
|
125
|
+
const keyB58 = toPk(tableAddress).toBase58();
|
|
126
|
+
const [info] = await fetchAccountsRaw(base, [keyB58], fetchImpl);
|
|
127
|
+
if (!info)
|
|
128
|
+
return null;
|
|
129
|
+
const state = web3.AddressLookupTableAccount.deserialize(info.data);
|
|
130
|
+
return { key: keyB58, addresses: state.addresses.map((a) => a.toBase58()) };
|
|
131
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@0block.io/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Client-side transaction builder for the 0Block DEX router. Builds unsigned v0 swap transactions entirely offline \u2014 tenant-wrapped or direct \u2014 with shared address lookup tables, gateway tips, and on-chain fee-event decoding for accounting.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"solana",
|