@0block.io/sdk 0.2.0 → 0.4.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.
@@ -0,0 +1,16 @@
1
+ export interface FetchBlockhashOptions {
2
+ /** Injectable fetch (tests / non-standard runtimes). Defaults to global fetch. */
3
+ fetchImpl?: typeof fetch;
4
+ }
5
+ export interface RecentBlockhash {
6
+ /** The finalized blockhash to use as a transaction's recentBlockhash. */
7
+ blockhash: string;
8
+ /** Last block height at which this blockhash is still valid (0 if absent). */
9
+ lastValidBlockHeight: number;
10
+ }
11
+ /**
12
+ * Fetch the recent finalized blockhash from 0Block's cached public endpoint.
13
+ * `apiBaseUrl` is the 0Block API base (e.g. "https://<0block-host>"); the
14
+ * "/api/v1/blockhash" path is appended.
15
+ */
16
+ export declare function fetchRecentBlockhash(apiBaseUrl: string, opts?: FetchBlockhashOptions): Promise<RecentBlockhash>;
@@ -0,0 +1,24 @@
1
+ // 0Block public API: recent blockhash. Lets clients build transactions without
2
+ // their own Solana read RPC — 0Block polls and caches getLatestBlockhash on the
3
+ // server and serves it at GET {apiBaseUrl}/api/v1/blockhash (public, no key).
4
+ /**
5
+ * Fetch the recent finalized blockhash from 0Block's cached public endpoint.
6
+ * `apiBaseUrl` is the 0Block API base (e.g. "https://<0block-host>"); the
7
+ * "/api/v1/blockhash" path is appended.
8
+ */
9
+ export async function fetchRecentBlockhash(apiBaseUrl, opts) {
10
+ const fetchImpl = opts?.fetchImpl ?? fetch;
11
+ const base = apiBaseUrl.replace(/\/+$/, "");
12
+ const res = await fetchImpl(`${base}/api/v1/blockhash`);
13
+ if (!res.ok) {
14
+ throw new Error(`0Block blockhash unavailable (HTTP ${res.status})`);
15
+ }
16
+ const body = (await res.json());
17
+ if (!body.blockhash) {
18
+ throw new Error("0Block blockhash response missing blockhash");
19
+ }
20
+ return {
21
+ blockhash: body.blockhash,
22
+ lastValidBlockHeight: body.lastValidBlockHeight ?? 0,
23
+ };
24
+ }
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ export * from "./constants.js";
2
2
  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
+ export { fetchRecentBlockhash, type FetchBlockhashOptions, type RecentBlockhash, } from "./blockhash.js";
5
6
  export { toPk, deriveAta, deriveOrderMarker, deriveWrapperConfig, deriveRouterGlobalConfig, deriveRouterTenantConfig, derivePumpfunGlobal, derivePumpfunBondingCurve, derivePumpfunBondingCurveV2, derivePumpfunEventAuthority, derivePumpfunCreatorVault, derivePumpfunGlobalVolumeAccumulator, derivePumpfunUserVolumeAccumulator, derivePumpfunFeeConfig, } from "./pda.js";
6
7
  export { type AccountFetcher, connectionFetcher, resolveTokenProgram, resolvePumpfunMarketContext, fetchPumpfunGlobalFeeRecipient, fetchPumpfunBondingCurveCreator, fetchPumpfunBuybackFeeRecipients, } from "./fetch.js";
7
8
  export { buildPumpfunRemainingAccounts, type PumpfunRemainingAccountsParams, } from "./venues/pumpfun.js";
@@ -9,7 +10,7 @@ export { buildPumpfunSwapInstruction, decodeSwapData, randomOrderId, resolveFeeM
9
10
  export { buildTipInstruction, maskTipAccountInTables } from "./tip.js";
10
11
  export { buildComputeBudgetInstructions } from "./computeBudget.js";
11
12
  export { createAtaIdempotentInstruction, syncNativeInstruction, closeAccountInstruction, wrapSolInstructions, unwrapWsolInstruction, } from "./wsol.js";
12
- export { buildPumpfunSwapTransaction, serializeWithSignatures, toLookupTableAccount, } from "./transaction.js";
13
+ export { buildPumpfunSwapTransaction, serializeWithSignatures, buildSubmitTemplate, toLookupTableAccount, type SubmitTemplate, } from "./transaction.js";
13
14
  export { buildLookupTableAddresses, buildInitLookupTableInstructions, type InitLookupTableResult, } from "./alt.js";
14
15
  export { applyPreset, slippagePctToBps, solToLamports, type PresetSwapInputs, type PresetSwapOutputs, } from "./presets.js";
15
16
  export { decodeSwapResultEvents } from "./events.js";
package/dist/index.js CHANGED
@@ -15,6 +15,7 @@ export * from "./constants.js";
15
15
  export * from "./types.js";
16
16
  export { createContext, } from "./context.js";
17
17
  export { buildSendTransactionRequest, createLandingClient, withLandingTip, } from "./landing.js";
18
+ export { fetchRecentBlockhash, } from "./blockhash.js";
18
19
  export { toPk, deriveAta, deriveOrderMarker, deriveWrapperConfig, deriveRouterGlobalConfig, deriveRouterTenantConfig, derivePumpfunGlobal, derivePumpfunBondingCurve, derivePumpfunBondingCurveV2, derivePumpfunEventAuthority, derivePumpfunCreatorVault, derivePumpfunGlobalVolumeAccumulator, derivePumpfunUserVolumeAccumulator, derivePumpfunFeeConfig, } from "./pda.js";
19
20
  export { connectionFetcher, resolveTokenProgram, resolvePumpfunMarketContext, fetchPumpfunGlobalFeeRecipient, fetchPumpfunBondingCurveCreator, fetchPumpfunBuybackFeeRecipients, } from "./fetch.js";
20
21
  export { buildPumpfunRemainingAccounts, } from "./venues/pumpfun.js";
@@ -22,7 +23,7 @@ export { buildPumpfunSwapInstruction, decodeSwapData, randomOrderId, resolveFeeM
22
23
  export { buildTipInstruction, maskTipAccountInTables } from "./tip.js";
23
24
  export { buildComputeBudgetInstructions } from "./computeBudget.js";
24
25
  export { createAtaIdempotentInstruction, syncNativeInstruction, closeAccountInstruction, wrapSolInstructions, unwrapWsolInstruction, } from "./wsol.js";
25
- export { buildPumpfunSwapTransaction, serializeWithSignatures, toLookupTableAccount, } from "./transaction.js";
26
+ export { buildPumpfunSwapTransaction, serializeWithSignatures, buildSubmitTemplate, toLookupTableAccount, } from "./transaction.js";
26
27
  export { buildLookupTableAddresses, buildInitLookupTableInstructions, } from "./alt.js";
27
28
  export { applyPreset, slippagePctToBps, solToLamports, } from "./presets.js";
28
29
  export { decodeSwapResultEvents } from "./events.js";
@@ -17,3 +17,34 @@ export declare function buildPumpfunSwapTransaction(params: BuildPumpfunSwapPara
17
17
  * requiredSigners order and return the wire-ready bytes.
18
18
  */
19
19
  export declare function serializeWithSignatures(result: BuildSwapResult, signatures: Uint8Array[]): Uint8Array;
20
+ /**
21
+ * A submit template for a signer-and-submitter that is CHAIN-AGNOSTIC (e.g.
22
+ * sigcore): it signs `payload` and splices the raw signature into
23
+ * `wireTemplate` at `signatureOffset`, then broadcasts the result — with no
24
+ * knowledge of Solana's transaction format.
25
+ *
26
+ * The layout works because a Solana v0 transaction is
27
+ * `[compact-u16 sig count][sig0..sigN-1][message]`, and the bytes that get
28
+ * signed are exactly the `[message]` tail. `wireTemplate` is that full
29
+ * structure with the signature region zeroed; splicing the 64-byte signature
30
+ * over the zeros yields bytes identical to `serializeWithSignatures`.
31
+ *
32
+ * Single-signer only (the RAX flow): exactly one signature at a fixed offset.
33
+ */
34
+ export interface SubmitTemplate {
35
+ /** The bytes the signer must sign (the transaction message). Hex-encoded. */
36
+ payloadHex: string;
37
+ /** Full wire transaction with the signature zeroed. Base64-encoded. */
38
+ wireTemplateBase64: string;
39
+ /** Byte offset in the template where the 64-byte signature is spliced in. */
40
+ signatureOffset: number;
41
+ /** Signature length in bytes (64 for ed25519). */
42
+ signatureLength: number;
43
+ /** The single required signer's base58 address (the user). */
44
+ signer: string;
45
+ }
46
+ /**
47
+ * Emit a chain-agnostic {@link SubmitTemplate} from a built single-signer
48
+ * transaction. The offset is derived from the wire layout, not assumed.
49
+ */
50
+ export declare function buildSubmitTemplate(result: BuildSwapResult): SubmitTemplate;
@@ -137,3 +137,55 @@ export function serializeWithSignatures(result, signatures) {
137
137
  }
138
138
  return result.transaction.serialize();
139
139
  }
140
+ /** compact-u16 (shortvec) encoded length — how Solana prefixes the sig array. */
141
+ function compactU16Length(n) {
142
+ if (n < 0x80)
143
+ return 1;
144
+ if (n < 0x4000)
145
+ return 2;
146
+ return 3;
147
+ }
148
+ /**
149
+ * Emit a chain-agnostic {@link SubmitTemplate} from a built single-signer
150
+ * transaction. The offset is derived from the wire layout, not assumed.
151
+ */
152
+ export function buildSubmitTemplate(result) {
153
+ const header = result.transaction.message.header;
154
+ if (header.numRequiredSignatures !== 1) {
155
+ throw new Error(`submit template supports a single signer; got ${header.numRequiredSignatures}`);
156
+ }
157
+ const SIG_LEN = 64;
158
+ // Zeroed-signature serialization IS the template (web3.js fills missing
159
+ // signatures with 64 zero bytes), so no manual zeroing is needed.
160
+ const wireTemplate = result.transaction.serialize();
161
+ const signatureOffset = compactU16Length(1); // count prefix precedes sig 0
162
+ // Invariant: the payload (message) is the tail after [count][signature].
163
+ const messageStart = signatureOffset + SIG_LEN;
164
+ const templateTail = wireTemplate.slice(messageStart);
165
+ if (templateTail.length !== result.messageBytes.length ||
166
+ !templateTail.every((b, i) => b === result.messageBytes[i])) {
167
+ throw new Error("submit template layout mismatch — message tail differs");
168
+ }
169
+ return {
170
+ payloadHex: bytesToHex(result.messageBytes),
171
+ wireTemplateBase64: toBase64(wireTemplate),
172
+ signatureOffset,
173
+ signatureLength: SIG_LEN,
174
+ signer: result.requiredSigners[0].toBase58(),
175
+ };
176
+ }
177
+ function bytesToHex(bytes) {
178
+ let hex = "";
179
+ for (const b of bytes)
180
+ hex += b.toString(16).padStart(2, "0");
181
+ return hex;
182
+ }
183
+ function toBase64(bytes) {
184
+ // Buffer in Node; btoa fallback in the browser.
185
+ if (typeof Buffer !== "undefined")
186
+ return Buffer.from(bytes).toString("base64");
187
+ let binary = "";
188
+ for (const b of bytes)
189
+ binary += String.fromCharCode(b);
190
+ return btoa(binary);
191
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@0block.io/sdk",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
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",