@0block.io/sdk 0.1.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 +172 -0
- package/dist/alt.d.ts +34 -0
- package/dist/alt.js +83 -0
- package/dist/computeBudget.d.ts +8 -0
- package/dist/computeBudget.js +25 -0
- package/dist/constants.d.ts +45 -0
- package/dist/constants.js +54 -0
- package/dist/context.d.ts +47 -0
- package/dist/context.js +27 -0
- package/dist/events.d.ts +9 -0
- package/dist/events.js +106 -0
- package/dist/fetch.d.ts +34 -0
- package/dist/fetch.js +104 -0
- package/dist/idl/router.d.ts +652 -0
- package/dist/idl/router.js +966 -0
- package/dist/idl/routerEvents.d.ts +557 -0
- package/dist/idl/routerEvents.js +802 -0
- package/dist/idl/tenantWrapper.d.ts +419 -0
- package/dist/idl/tenantWrapper.js +740 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +27 -0
- package/dist/pda.d.ts +21 -0
- package/dist/pda.js +53 -0
- package/dist/presets.d.ts +23 -0
- package/dist/presets.js +36 -0
- package/dist/swap.d.ts +46 -0
- package/dist/swap.js +230 -0
- package/dist/tip.d.ts +15 -0
- package/dist/tip.js +37 -0
- package/dist/transaction.d.ts +19 -0
- package/dist/transaction.js +106 -0
- package/dist/types.d.ts +124 -0
- package/dist/types.js +1 -0
- package/dist/venues/pumpfun.d.ts +32 -0
- package/dist/venues/pumpfun.js +56 -0
- package/dist/wsol.d.ts +30 -0
- package/dist/wsol.js +69 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# @0block.io/sdk
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@0block.io/sdk)
|
|
4
|
+
[](https://www.npmjs.com/package/@0block.io/sdk)
|
|
5
|
+
|
|
6
|
+
**Client-side transaction builder for the 0Block DEX router.** Builds unsigned
|
|
7
|
+
v0 swap transactions entirely offline — tenant-wrapped or direct — with shared
|
|
8
|
+
address lookup tables, gateway tips, and on-chain fee-event decoding for
|
|
9
|
+
accounting.
|
|
10
|
+
|
|
11
|
+
- **Zero network calls at trade time.** Market context, lookup tables, and a
|
|
12
|
+
blockhash are resolved once server-side and streamed; the client assembles
|
|
13
|
+
the transaction synchronously.
|
|
14
|
+
- **One signature.** The end user is the only required signer, in both modes.
|
|
15
|
+
- **Fees are on-chain, not inputs.** Tenant and platform commissions are read
|
|
16
|
+
and enforced by the programs; the SDK never accepts a fee parameter.
|
|
17
|
+
- **External-signer native.** Returns the exact message bytes to sign, for
|
|
18
|
+
wallets, signing services, or HSMs — no `Keypair` ever touches the SDK.
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
npm install @0block.io/sdk
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Requires Node ≥ 20 server-side; browser builds work with any modern bundler.
|
|
27
|
+
|
|
28
|
+
## Modes
|
|
29
|
+
|
|
30
|
+
The mode is fixed at initialization:
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import { createContext } from "@0block.io/sdk";
|
|
34
|
+
|
|
35
|
+
// Tenant mode — swaps route through your tenant wrapper program.
|
|
36
|
+
// Your tenant fee (set on-chain) and the 0Block fee apply.
|
|
37
|
+
const ctx = createContext({
|
|
38
|
+
tenant: {
|
|
39
|
+
wrapperProgramId: "<your tenant wrapper program id>",
|
|
40
|
+
tenantFeeVault: "<your tenant fee vault>",
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Direct mode — swaps invoke the 0Block router directly.
|
|
45
|
+
// Only the 0Block platform fee applies.
|
|
46
|
+
const ctx = createContext();
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Quick start
|
|
50
|
+
|
|
51
|
+
### 1. Backend — resolve market context once, stream it
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { connectionFetcher, resolvePumpfunMarketContext } from "@0block.io/sdk";
|
|
55
|
+
|
|
56
|
+
const fetcher = connectionFetcher(connection);
|
|
57
|
+
const market = await resolvePumpfunMarketContext(fetcher, { mint });
|
|
58
|
+
// `market` is plain strings — push it to clients over your existing
|
|
59
|
+
// stream together with quotes and a FINALIZED blockhash.
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 2. Backend ops — shared lookup table, once per venue set
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
import {
|
|
66
|
+
buildLookupTableAddresses,
|
|
67
|
+
buildInitLookupTableInstructions,
|
|
68
|
+
} from "@0block.io/sdk";
|
|
69
|
+
|
|
70
|
+
const addresses = buildLookupTableAddresses({ context: ctx, market });
|
|
71
|
+
const { tableAddress, transactions } = buildInitLookupTableInstructions({
|
|
72
|
+
authority: opsKey,
|
|
73
|
+
recentSlot,
|
|
74
|
+
addresses,
|
|
75
|
+
});
|
|
76
|
+
// Send each batch as one transaction, in order. Wait ~1 slot before first use.
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 3. Client — preset → unsigned transaction → your signer
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import { applyPreset, buildPumpfunSwapTransaction } from "@0block.io/sdk";
|
|
83
|
+
|
|
84
|
+
const { minReturn, tip, computeBudget } = applyPreset(
|
|
85
|
+
{ slippagePct: 20, prioritySol: 0.001, bribeSol: 0.001 },
|
|
86
|
+
{ expectedOut: quote.expectedOut, tipAccount: streamed.tipAccount }
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const built = buildPumpfunSwapTransaction({
|
|
90
|
+
context: ctx,
|
|
91
|
+
market,
|
|
92
|
+
user: wallet.address,
|
|
93
|
+
side: "buy",
|
|
94
|
+
amountIn: 10_000_000n, // 0.01 SOL
|
|
95
|
+
minReturn,
|
|
96
|
+
recentBlockhash: streamed.finalizedBlockhash,
|
|
97
|
+
lookupTables: streamed.lookupTables,
|
|
98
|
+
tip,
|
|
99
|
+
computeBudget,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// built.messageBytes → sign with the user's key (exactly one signature)
|
|
103
|
+
// built.orderId → correlate the fill via SwapResultEvent
|
|
104
|
+
// built.estimatedSize → wire size, pre-checked against the 1232-byte limit
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Attach the signature and get wire bytes with `serializeWithSignatures(built, [sig])`,
|
|
108
|
+
then submit through the 0Block gateway.
|
|
109
|
+
|
|
110
|
+
### 4. Indexer — fee accounting from confirmed transactions
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
import { decodeSwapResultEvents } from "@0block.io/sdk";
|
|
114
|
+
|
|
115
|
+
const events = decodeSwapResultEvents(tx.meta.logMessages);
|
|
116
|
+
// [{ orderId, status, commissions: { feeMint, totalFeeAmount,
|
|
117
|
+
// items: [{ kind: "platform" | "tenant", bps, amount, tenantProgram }] } }]
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## API overview
|
|
121
|
+
|
|
122
|
+
| Runs | Function | Purpose |
|
|
123
|
+
|---|---|---|
|
|
124
|
+
| anywhere | `createContext` | Resolve tenant vs direct mode from init params |
|
|
125
|
+
| backend | `resolvePumpfunMarketContext` | Chain-derived accounts for a mint, as streamable strings |
|
|
126
|
+
| backend | `buildLookupTableAddresses` / `buildInitLookupTableInstructions` | Create the shared static-accounts table |
|
|
127
|
+
| client | `applyPreset` | Slippage % / priority SOL / bribe SOL → build params |
|
|
128
|
+
| client | `buildPumpfunSwapTransaction` | Complete unsigned v0 transaction (offline) |
|
|
129
|
+
| client | `serializeWithSignatures` | Attach external signatures → wire bytes |
|
|
130
|
+
| indexer | `decodeSwapResultEvents` | `SwapResultEvent` → normalized commission breakdown |
|
|
131
|
+
|
|
132
|
+
Lower-level building blocks (`buildPumpfunSwapInstruction`, PDA derivations,
|
|
133
|
+
wSOL wrap/unwrap helpers, tip and compute-budget builders, vendored IDLs) are
|
|
134
|
+
exported as well.
|
|
135
|
+
|
|
136
|
+
## Operational notes
|
|
137
|
+
|
|
138
|
+
- **Tenant mode prerequisites.** Your wrapper must be the build in which the
|
|
139
|
+
`wrapper_config` PDA signs the router CPI, and the router's
|
|
140
|
+
`TenantConfig.tenant_authority` must equal that PDA
|
|
141
|
+
(`deriveWrapperConfig(wrapperProgramId)`). Otherwise swaps fail
|
|
142
|
+
`UnauthorizedTenant`.
|
|
143
|
+
- **Direct mode prerequisite.** The router requires *a* `TenantConfig` account
|
|
144
|
+
in the `tenant_cfg` slot even on direct calls (ignored for fees). The router
|
|
145
|
+
operator provisions a standing "direct" config; override its key with
|
|
146
|
+
`directTenantConfigKey` if needed.
|
|
147
|
+
- **Tips must stay static message keys.** The gateway verifies the tip transfer
|
|
148
|
+
on raw bytes without resolving lookup tables. The builder masks tip accounts
|
|
149
|
+
out of table copies defensively — never add tip accounts to an ALT.
|
|
150
|
+
- **Use a FINALIZED blockhash.** The gateway forwards to a different staked
|
|
151
|
+
node; a `confirmed` hash may be unknown there.
|
|
152
|
+
- **Native SOL handling.** Buys wrap SOL → wSOL in-transaction (idempotent ATA
|
|
153
|
+
+ transfer + SyncNative); sells close the wSOL ATA back to SOL. Opt out with
|
|
154
|
+
`wrapSol` / `unwrapWsol` if your users manage wSOL themselves.
|
|
155
|
+
- **Venue coverage.** v1 ships the pump.fun bonding-curve venue (buy/sell,
|
|
156
|
+
single hop). Additional venues follow the same shape — one
|
|
157
|
+
remaining-accounts builder per venue.
|
|
158
|
+
|
|
159
|
+
## Development
|
|
160
|
+
|
|
161
|
+
```sh
|
|
162
|
+
npm install
|
|
163
|
+
npm test # compiles, then runs the offline smoke test (no network, no keys)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
The vendored IDLs under `src/idl/` are generated from the on-chain programs'
|
|
167
|
+
Anchor builds. Regenerate them after any program change — account ordering and
|
|
168
|
+
discriminators must match on-chain reality.
|
|
169
|
+
|
|
170
|
+
## License
|
|
171
|
+
|
|
172
|
+
UNLICENSED — all rights reserved. Contact 0block.io for usage terms.
|
package/dist/alt.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as web3 from "@solana/web3.js";
|
|
2
|
+
import type { ZeroBlockContext } from "./context.js";
|
|
3
|
+
import type { PumpfunMarketContext } from "./types.js";
|
|
4
|
+
/**
|
|
5
|
+
* The static account set for pump.fun swaps under a context — everything that
|
|
6
|
+
* is identical across users and trades. Load these into a shared address
|
|
7
|
+
* lookup table once (ops), and every user's v0 transaction references it.
|
|
8
|
+
*
|
|
9
|
+
* Never add gateway tip accounts: the gateway requires the tip destination as
|
|
10
|
+
* a static message key (the build path masks it defensively either way).
|
|
11
|
+
*/
|
|
12
|
+
export declare function buildLookupTableAddresses(params: {
|
|
13
|
+
context: ZeroBlockContext;
|
|
14
|
+
/** Any market context for the venue — only its static venue fields are used. */
|
|
15
|
+
market: Pick<PumpfunMarketContext, "pumpfunProgram" | "pumpfunFeeProgram" | "global" | "eventAuthority" | "globalVolumeAccumulator" | "feeConfig" | "buybackFeeRecipients">;
|
|
16
|
+
extra?: (string | web3.PublicKey)[];
|
|
17
|
+
}): web3.PublicKey[];
|
|
18
|
+
export interface InitLookupTableResult {
|
|
19
|
+
tableAddress: web3.PublicKey;
|
|
20
|
+
/**
|
|
21
|
+
* Instruction batches: send each inner array as one transaction, in order
|
|
22
|
+
* (create+first extend, then further extends of ≤20 addresses). Wait ~1
|
|
23
|
+
* slot after the last extend before first use.
|
|
24
|
+
*/
|
|
25
|
+
transactions: web3.TransactionInstruction[][];
|
|
26
|
+
}
|
|
27
|
+
/** Build the create/extend instruction batches for a shared lookup table (ops path). */
|
|
28
|
+
export declare function buildInitLookupTableInstructions(params: {
|
|
29
|
+
authority: web3.PublicKey;
|
|
30
|
+
payer?: web3.PublicKey;
|
|
31
|
+
/** A recent (finalized) slot, required by createLookupTable. */
|
|
32
|
+
recentSlot: number;
|
|
33
|
+
addresses: web3.PublicKey[];
|
|
34
|
+
}): InitLookupTableResult;
|
package/dist/alt.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import * as web3 from "@solana/web3.js";
|
|
2
|
+
import { ASSOCIATED_TOKEN_PROGRAM_ID, INSTRUCTIONS_SYSVAR_ID, TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID, WSOL_MINT, } from "./constants.js";
|
|
3
|
+
import { deriveAta, deriveRouterGlobalConfig, deriveRouterTenantConfig, deriveWrapperConfig, toPk, } from "./pda.js";
|
|
4
|
+
/**
|
|
5
|
+
* The static account set for pump.fun swaps under a context — everything that
|
|
6
|
+
* is identical across users and trades. Load these into a shared address
|
|
7
|
+
* lookup table once (ops), and every user's v0 transaction references it.
|
|
8
|
+
*
|
|
9
|
+
* Never add gateway tip accounts: the gateway requires the tip destination as
|
|
10
|
+
* a static message key (the build path masks it defensively either way).
|
|
11
|
+
*/
|
|
12
|
+
export function buildLookupTableAddresses(params) {
|
|
13
|
+
const { context } = params;
|
|
14
|
+
const routerProgramId = context.routerProgramId;
|
|
15
|
+
const zeroblockFeeVault = context.zeroblockFeeVault;
|
|
16
|
+
const tenantAddresses = context.mode === "tenant"
|
|
17
|
+
? [
|
|
18
|
+
context.wrapperProgramId,
|
|
19
|
+
deriveWrapperConfig(context.wrapperProgramId),
|
|
20
|
+
deriveRouterTenantConfig(routerProgramId, context.wrapperProgramId),
|
|
21
|
+
context.tenantFeeVault,
|
|
22
|
+
deriveAta(context.tenantFeeVault, WSOL_MINT, TOKEN_PROGRAM_ID),
|
|
23
|
+
]
|
|
24
|
+
: [deriveRouterTenantConfig(routerProgramId, context.directTenantConfigKey)];
|
|
25
|
+
const addresses = [
|
|
26
|
+
routerProgramId,
|
|
27
|
+
deriveRouterGlobalConfig(routerProgramId),
|
|
28
|
+
...tenantAddresses,
|
|
29
|
+
zeroblockFeeVault,
|
|
30
|
+
deriveAta(zeroblockFeeVault, WSOL_MINT, TOKEN_PROGRAM_ID),
|
|
31
|
+
TOKEN_PROGRAM_ID,
|
|
32
|
+
TOKEN_2022_PROGRAM_ID,
|
|
33
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
34
|
+
web3.SystemProgram.programId,
|
|
35
|
+
INSTRUCTIONS_SYSVAR_ID,
|
|
36
|
+
WSOL_MINT,
|
|
37
|
+
toPk(params.market.pumpfunProgram),
|
|
38
|
+
toPk(params.market.global),
|
|
39
|
+
toPk(params.market.eventAuthority),
|
|
40
|
+
toPk(params.market.globalVolumeAccumulator),
|
|
41
|
+
toPk(params.market.feeConfig),
|
|
42
|
+
toPk(params.market.pumpfunFeeProgram),
|
|
43
|
+
...params.market.buybackFeeRecipients.map((r) => toPk(r)),
|
|
44
|
+
...(params.extra ?? []).map((a) => toPk(a)),
|
|
45
|
+
];
|
|
46
|
+
const seen = new Set();
|
|
47
|
+
return addresses.filter((a) => {
|
|
48
|
+
const k = a.toBase58();
|
|
49
|
+
if (seen.has(k))
|
|
50
|
+
return false;
|
|
51
|
+
seen.add(k);
|
|
52
|
+
return true;
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/** Build the create/extend instruction batches for a shared lookup table (ops path). */
|
|
56
|
+
export function buildInitLookupTableInstructions(params) {
|
|
57
|
+
const payer = params.payer ?? params.authority;
|
|
58
|
+
const CHUNK = 20;
|
|
59
|
+
const [createIx, tableAddress] = web3.AddressLookupTableProgram.createLookupTable({
|
|
60
|
+
authority: params.authority,
|
|
61
|
+
payer,
|
|
62
|
+
recentSlot: params.recentSlot,
|
|
63
|
+
});
|
|
64
|
+
const transactions = [];
|
|
65
|
+
const first = web3.AddressLookupTableProgram.extendLookupTable({
|
|
66
|
+
payer,
|
|
67
|
+
authority: params.authority,
|
|
68
|
+
lookupTable: tableAddress,
|
|
69
|
+
addresses: params.addresses.slice(0, CHUNK),
|
|
70
|
+
});
|
|
71
|
+
transactions.push([createIx, first]);
|
|
72
|
+
for (let i = CHUNK; i < params.addresses.length; i += CHUNK) {
|
|
73
|
+
transactions.push([
|
|
74
|
+
web3.AddressLookupTableProgram.extendLookupTable({
|
|
75
|
+
payer,
|
|
76
|
+
authority: params.authority,
|
|
77
|
+
lookupTable: tableAddress,
|
|
78
|
+
addresses: params.addresses.slice(i, i + CHUNK),
|
|
79
|
+
}),
|
|
80
|
+
]);
|
|
81
|
+
}
|
|
82
|
+
return { tableAddress, transactions };
|
|
83
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as web3 from "@solana/web3.js";
|
|
2
|
+
import type { ComputeBudgetParams } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Convert a UI-level "priority fee in SOL" (the preset's Priority field) into
|
|
5
|
+
* compute-budget instructions: a CU limit plus a per-CU price such that the
|
|
6
|
+
* total priority fee equals approximately `prioritySol`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function buildComputeBudgetInstructions(params: ComputeBudgetParams): web3.TransactionInstruction[];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as web3 from "@solana/web3.js";
|
|
2
|
+
import { DEFAULT_COMPUTE_UNIT_LIMIT } from "./constants.js";
|
|
3
|
+
/**
|
|
4
|
+
* Convert a UI-level "priority fee in SOL" (the preset's Priority field) into
|
|
5
|
+
* compute-budget instructions: a CU limit plus a per-CU price such that the
|
|
6
|
+
* total priority fee equals approximately `prioritySol`.
|
|
7
|
+
*/
|
|
8
|
+
export function buildComputeBudgetInstructions(params) {
|
|
9
|
+
const units = params.computeUnitLimit ?? DEFAULT_COMPUTE_UNIT_LIMIT;
|
|
10
|
+
if (!Number.isInteger(units) || units <= 0 || units > 1_400_000) {
|
|
11
|
+
throw new Error("computeUnitLimit must be an integer in (0, 1_400_000]");
|
|
12
|
+
}
|
|
13
|
+
if (!(params.prioritySol >= 0)) {
|
|
14
|
+
throw new Error("prioritySol must be >= 0");
|
|
15
|
+
}
|
|
16
|
+
const ixs = [
|
|
17
|
+
web3.ComputeBudgetProgram.setComputeUnitLimit({ units }),
|
|
18
|
+
];
|
|
19
|
+
// total lamports = units * microLamports / 1e6 => micro-lamports per CU:
|
|
20
|
+
const microLamports = BigInt(Math.round((params.prioritySol * 1e9 * 1e6) / units));
|
|
21
|
+
if (microLamports > 0n) {
|
|
22
|
+
ixs.push(web3.ComputeBudgetProgram.setComputeUnitPrice({ microLamports }));
|
|
23
|
+
}
|
|
24
|
+
return ixs;
|
|
25
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as web3 from "@solana/web3.js";
|
|
2
|
+
/** Solana wire-format ceiling for a serialized transaction (packet MTU). */
|
|
3
|
+
export declare const MAX_TX_SIZE = 1232;
|
|
4
|
+
/** 0Block DEX router program (the default when no tenant is configured). */
|
|
5
|
+
export declare const ROUTER_PROGRAM_ID: web3.PublicKey;
|
|
6
|
+
/** 0Block platform fee authority (owner of 0Block fee token accounts). */
|
|
7
|
+
export declare const ZEROBLOCK_FEE_VAULT: web3.PublicKey;
|
|
8
|
+
export declare const TOKEN_PROGRAM_ID: web3.PublicKey;
|
|
9
|
+
export declare const TOKEN_2022_PROGRAM_ID: web3.PublicKey;
|
|
10
|
+
export declare const ASSOCIATED_TOKEN_PROGRAM_ID: web3.PublicKey;
|
|
11
|
+
export declare const WSOL_MINT: web3.PublicKey;
|
|
12
|
+
export declare const USDC_MINT: web3.PublicKey;
|
|
13
|
+
export declare const USD1_MINT: web3.PublicKey;
|
|
14
|
+
export declare const INSTRUCTIONS_SYSVAR_ID: web3.PublicKey;
|
|
15
|
+
/**
|
|
16
|
+
* Mints the router accepts as the fee mint. Fee base = amount_in when the
|
|
17
|
+
* SOURCE mint is allowlisted, else amount_out (see router config.rs fee math).
|
|
18
|
+
*/
|
|
19
|
+
export declare const FEE_MINT_ALLOWLIST: readonly string[];
|
|
20
|
+
export declare const PUMPFUN_PROGRAM_ID: web3.PublicKey;
|
|
21
|
+
export declare const PUMPFUN_FEE_PROGRAM_ID: web3.PublicKey;
|
|
22
|
+
export declare const SEED_WRAPPER_CONFIG: Uint8Array<ArrayBuffer>;
|
|
23
|
+
export declare const SEED_ROUTER_GLOBAL_CONFIG: Uint8Array<ArrayBuffer>;
|
|
24
|
+
export declare const SEED_ROUTER_TENANT: Uint8Array<ArrayBuffer>;
|
|
25
|
+
export declare const SEED_PUMPFUN_GLOBAL: Uint8Array<ArrayBuffer>;
|
|
26
|
+
export declare const SEED_PUMPFUN_BONDING_CURVE: Uint8Array<ArrayBuffer>;
|
|
27
|
+
export declare const SEED_PUMPFUN_BONDING_CURVE_V2: Uint8Array<ArrayBuffer>;
|
|
28
|
+
export declare const SEED_PUMPFUN_EVENT_AUTHORITY: Uint8Array<ArrayBuffer>;
|
|
29
|
+
export declare const SEED_PUMPFUN_CREATOR_VAULT: Uint8Array<ArrayBuffer>;
|
|
30
|
+
export declare const SEED_PUMPFUN_GLOBAL_VOLUME: Uint8Array<ArrayBuffer>;
|
|
31
|
+
export declare const SEED_PUMPFUN_USER_VOLUME: Uint8Array<ArrayBuffer>;
|
|
32
|
+
export declare const SEED_PUMPFUN_FEE_CONFIG: Uint8Array<ArrayBuffer>;
|
|
33
|
+
/**
|
|
34
|
+
* Byte offset of Global.buyback_fee_recipients ([Pubkey; 8]) inside the
|
|
35
|
+
* pump.fun Global account data (8-byte discriminator + preceding fields).
|
|
36
|
+
*/
|
|
37
|
+
export declare const PUMPFUN_GLOBAL_BUYBACK_RECIPIENTS_OFFSET = 741;
|
|
38
|
+
/** Default gateway tip when the caller does not specify one (0.001 SOL). */
|
|
39
|
+
export declare const DEFAULT_TIP_LAMPORTS = 1000000n;
|
|
40
|
+
/**
|
|
41
|
+
* Default compute-unit limit for a single-hop wrapper swap. Router swaps CPI
|
|
42
|
+
* through the wrapper into the venue program, so they are heavier than a
|
|
43
|
+
* direct venue call.
|
|
44
|
+
*/
|
|
45
|
+
export declare const DEFAULT_COMPUTE_UNIT_LIMIT = 400000;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import * as web3 from "@solana/web3.js";
|
|
2
|
+
/** Solana wire-format ceiling for a serialized transaction (packet MTU). */
|
|
3
|
+
export const MAX_TX_SIZE = 1232;
|
|
4
|
+
/** 0Block DEX router program (the default when no tenant is configured). */
|
|
5
|
+
export const ROUTER_PROGRAM_ID = new web3.PublicKey("zeroRxfemoQCz6JT7RyShjuuRCN7VZof1WSekfXhmEn");
|
|
6
|
+
/** 0Block platform fee authority (owner of 0Block fee token accounts). */
|
|
7
|
+
export const ZEROBLOCK_FEE_VAULT = new web3.PublicKey("zerobvXpiuDCSrpmw1dJNbfAXPLp8hFYeQ62u4XRQZf");
|
|
8
|
+
export const TOKEN_PROGRAM_ID = new web3.PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
|
|
9
|
+
export const TOKEN_2022_PROGRAM_ID = new web3.PublicKey("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb");
|
|
10
|
+
export const ASSOCIATED_TOKEN_PROGRAM_ID = new web3.PublicKey("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL");
|
|
11
|
+
export const WSOL_MINT = new web3.PublicKey("So11111111111111111111111111111111111111112");
|
|
12
|
+
export const USDC_MINT = new web3.PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
|
|
13
|
+
export const USD1_MINT = new web3.PublicKey("USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB");
|
|
14
|
+
export const INSTRUCTIONS_SYSVAR_ID = new web3.PublicKey("Sysvar1nstructions1111111111111111111111111");
|
|
15
|
+
/**
|
|
16
|
+
* Mints the router accepts as the fee mint. Fee base = amount_in when the
|
|
17
|
+
* SOURCE mint is allowlisted, else amount_out (see router config.rs fee math).
|
|
18
|
+
*/
|
|
19
|
+
export const FEE_MINT_ALLOWLIST = [
|
|
20
|
+
web3.SystemProgram.programId.toBase58(),
|
|
21
|
+
WSOL_MINT.toBase58(),
|
|
22
|
+
USDC_MINT.toBase58(),
|
|
23
|
+
USD1_MINT.toBase58(),
|
|
24
|
+
];
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// pump.fun (the v1 venue)
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
export const PUMPFUN_PROGRAM_ID = new web3.PublicKey("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P");
|
|
29
|
+
export const PUMPFUN_FEE_PROGRAM_ID = new web3.PublicKey("pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ");
|
|
30
|
+
const te = new TextEncoder();
|
|
31
|
+
export const SEED_WRAPPER_CONFIG = te.encode("tenant_wrapper_config");
|
|
32
|
+
export const SEED_ROUTER_GLOBAL_CONFIG = te.encode("config");
|
|
33
|
+
export const SEED_ROUTER_TENANT = te.encode("tenant");
|
|
34
|
+
export const SEED_PUMPFUN_GLOBAL = te.encode("global");
|
|
35
|
+
export const SEED_PUMPFUN_BONDING_CURVE = te.encode("bonding-curve");
|
|
36
|
+
export const SEED_PUMPFUN_BONDING_CURVE_V2 = te.encode("bonding-curve-v2");
|
|
37
|
+
export const SEED_PUMPFUN_EVENT_AUTHORITY = te.encode("__event_authority");
|
|
38
|
+
export const SEED_PUMPFUN_CREATOR_VAULT = te.encode("creator-vault");
|
|
39
|
+
export const SEED_PUMPFUN_GLOBAL_VOLUME = te.encode("global_volume_accumulator");
|
|
40
|
+
export const SEED_PUMPFUN_USER_VOLUME = te.encode("user_volume_accumulator");
|
|
41
|
+
export const SEED_PUMPFUN_FEE_CONFIG = te.encode("fee_config");
|
|
42
|
+
/**
|
|
43
|
+
* Byte offset of Global.buyback_fee_recipients ([Pubkey; 8]) inside the
|
|
44
|
+
* pump.fun Global account data (8-byte discriminator + preceding fields).
|
|
45
|
+
*/
|
|
46
|
+
export const PUMPFUN_GLOBAL_BUYBACK_RECIPIENTS_OFFSET = 741;
|
|
47
|
+
/** Default gateway tip when the caller does not specify one (0.001 SOL). */
|
|
48
|
+
export const DEFAULT_TIP_LAMPORTS = 1000000n;
|
|
49
|
+
/**
|
|
50
|
+
* Default compute-unit limit for a single-hop wrapper swap. Router swaps CPI
|
|
51
|
+
* through the wrapper into the venue program, so they are heavier than a
|
|
52
|
+
* direct venue call.
|
|
53
|
+
*/
|
|
54
|
+
export const DEFAULT_COMPUTE_UNIT_LIMIT = 400_000;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as web3 from "@solana/web3.js";
|
|
2
|
+
import type { Address } from "./types.js";
|
|
3
|
+
/** Tenant initialization: the tenant's wrapper program + fee vault. */
|
|
4
|
+
export interface TenantInit {
|
|
5
|
+
/** The tenant's deployed wrapper program. */
|
|
6
|
+
wrapperProgramId: Address;
|
|
7
|
+
/** Tenant fee authority (owns the tenant fee token accounts). */
|
|
8
|
+
tenantFeeVault: Address;
|
|
9
|
+
}
|
|
10
|
+
export interface CreateContextParams {
|
|
11
|
+
/**
|
|
12
|
+
* Provide to route swaps through the tenant's wrapper program (tenant fees
|
|
13
|
+
* apply). Omit to invoke the 0Block router directly with 0Block fee
|
|
14
|
+
* accounts only.
|
|
15
|
+
*/
|
|
16
|
+
tenant?: TenantInit;
|
|
17
|
+
/** Router program id. Defaults to the mainnet router. */
|
|
18
|
+
routerProgramId?: Address;
|
|
19
|
+
/** 0Block platform fee authority. Defaults to the mainnet vault. */
|
|
20
|
+
zeroblockFeeVault?: Address;
|
|
21
|
+
/**
|
|
22
|
+
* Direct mode only: the wrapper key whose router TenantConfig PDA is passed
|
|
23
|
+
* as `tenant_cfg` (the account must exist; the router ignores it for fee
|
|
24
|
+
* purposes on direct calls). Defaults to the default pubkey — the router
|
|
25
|
+
* operator initializes this "direct" TenantConfig once.
|
|
26
|
+
*/
|
|
27
|
+
directTenantConfigKey?: Address;
|
|
28
|
+
}
|
|
29
|
+
/** Resolved build context: tenant-wrapped or direct-to-router. */
|
|
30
|
+
export type ZeroBlockContext = {
|
|
31
|
+
mode: "tenant";
|
|
32
|
+
routerProgramId: web3.PublicKey;
|
|
33
|
+
zeroblockFeeVault: web3.PublicKey;
|
|
34
|
+
wrapperProgramId: web3.PublicKey;
|
|
35
|
+
tenantFeeVault: web3.PublicKey;
|
|
36
|
+
} | {
|
|
37
|
+
mode: "direct";
|
|
38
|
+
routerProgramId: web3.PublicKey;
|
|
39
|
+
zeroblockFeeVault: web3.PublicKey;
|
|
40
|
+
directTenantConfigKey: web3.PublicKey;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Resolve how transactions are built. With a `tenant`, swaps go through the
|
|
44
|
+
* tenant's wrapper program (tenant + 0Block fees); without one, they invoke
|
|
45
|
+
* the 0Block router directly (0Block fee only).
|
|
46
|
+
*/
|
|
47
|
+
export declare function createContext(params?: CreateContextParams): ZeroBlockContext;
|
package/dist/context.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as web3 from "@solana/web3.js";
|
|
2
|
+
import { ROUTER_PROGRAM_ID, ZEROBLOCK_FEE_VAULT } from "./constants.js";
|
|
3
|
+
import { toPk } from "./pda.js";
|
|
4
|
+
/**
|
|
5
|
+
* Resolve how transactions are built. With a `tenant`, swaps go through the
|
|
6
|
+
* tenant's wrapper program (tenant + 0Block fees); without one, they invoke
|
|
7
|
+
* the 0Block router directly (0Block fee only).
|
|
8
|
+
*/
|
|
9
|
+
export function createContext(params = {}) {
|
|
10
|
+
const routerProgramId = toPk(params.routerProgramId ?? ROUTER_PROGRAM_ID);
|
|
11
|
+
const zeroblockFeeVault = toPk(params.zeroblockFeeVault ?? ZEROBLOCK_FEE_VAULT);
|
|
12
|
+
if (params.tenant) {
|
|
13
|
+
return {
|
|
14
|
+
mode: "tenant",
|
|
15
|
+
routerProgramId,
|
|
16
|
+
zeroblockFeeVault,
|
|
17
|
+
wrapperProgramId: toPk(params.tenant.wrapperProgramId),
|
|
18
|
+
tenantFeeVault: toPk(params.tenant.tenantFeeVault),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
mode: "direct",
|
|
23
|
+
routerProgramId,
|
|
24
|
+
zeroblockFeeVault,
|
|
25
|
+
directTenantConfigKey: toPk(params.directTenantConfigKey ?? web3.PublicKey.default),
|
|
26
|
+
};
|
|
27
|
+
}
|
package/dist/events.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DecodedSwapResult } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Decode every router SwapResultEvent found in a transaction's log messages
|
|
4
|
+
* (tx.meta.logMessages). This is the accounting hook: on success the event
|
|
5
|
+
* carries the full CommissionBreakdown — fee mint, base, total, and one item
|
|
6
|
+
* per recipient (platform vs tenant, with the tenant wrapper program id) —
|
|
7
|
+
* plus the order_id that correlates back to the submitted order.
|
|
8
|
+
*/
|
|
9
|
+
export declare function decodeSwapResultEvents(logMessages: readonly string[]): DecodedSwapResult[];
|
package/dist/events.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import * as web3 from "@solana/web3.js";
|
|
2
|
+
// Anchor ships CJS-only; a default import + destructure works everywhere
|
|
3
|
+
// (bundlers and raw Node ESM), named imports do not.
|
|
4
|
+
import anchor from "@coral-xyz/anchor";
|
|
5
|
+
const { BN, BorshEventCoder } = anchor;
|
|
6
|
+
import { ROUTER_IDL } from "./idl/router.js";
|
|
7
|
+
// Event layouts derive from the vendored (trimmed) router IDL.
|
|
8
|
+
const eventCoder = new BorshEventCoder(ROUTER_IDL);
|
|
9
|
+
const PROGRAM_DATA_PREFIX = "Program data: ";
|
|
10
|
+
function toBigint(v) {
|
|
11
|
+
if (typeof v === "bigint")
|
|
12
|
+
return v;
|
|
13
|
+
if (BN.isBN(v))
|
|
14
|
+
return BigInt(v.toString());
|
|
15
|
+
return BigInt(String(v));
|
|
16
|
+
}
|
|
17
|
+
function toBase58(v) {
|
|
18
|
+
return v instanceof web3.PublicKey ? v.toBase58() : new web3.PublicKey(String(v)).toBase58();
|
|
19
|
+
}
|
|
20
|
+
/** Case-insensitive enum-variant lookup ({Success:{…}} vs {success:{…}}). */
|
|
21
|
+
function variant(obj, name) {
|
|
22
|
+
for (const key of Object.keys(obj)) {
|
|
23
|
+
if (key.toLowerCase() === name.toLowerCase())
|
|
24
|
+
return obj[key];
|
|
25
|
+
}
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Anchor decodes tuple enum variants as { "0": value } (numeric string keys);
|
|
30
|
+
* named-field variants decode as plain objects. Normalize to the inner value.
|
|
31
|
+
*/
|
|
32
|
+
function unwrapTuple(value) {
|
|
33
|
+
if (Array.isArray(value))
|
|
34
|
+
return value[0];
|
|
35
|
+
const obj = value;
|
|
36
|
+
if (obj && typeof obj === "object" && "0" in obj && Object.keys(obj).length === 1) {
|
|
37
|
+
return obj["0"];
|
|
38
|
+
}
|
|
39
|
+
return obj;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Decode every router SwapResultEvent found in a transaction's log messages
|
|
43
|
+
* (tx.meta.logMessages). This is the accounting hook: on success the event
|
|
44
|
+
* carries the full CommissionBreakdown — fee mint, base, total, and one item
|
|
45
|
+
* per recipient (platform vs tenant, with the tenant wrapper program id) —
|
|
46
|
+
* plus the order_id that correlates back to the submitted order.
|
|
47
|
+
*/
|
|
48
|
+
export function decodeSwapResultEvents(logMessages) {
|
|
49
|
+
const results = [];
|
|
50
|
+
for (const line of logMessages) {
|
|
51
|
+
const idx = line.indexOf(PROGRAM_DATA_PREFIX);
|
|
52
|
+
if (idx === -1)
|
|
53
|
+
continue;
|
|
54
|
+
const base64 = line.slice(idx + PROGRAM_DATA_PREFIX.length).trim();
|
|
55
|
+
let decoded = null;
|
|
56
|
+
try {
|
|
57
|
+
decoded = eventCoder.decode(base64);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
continue; // some other program's data log
|
|
61
|
+
}
|
|
62
|
+
if (!decoded || decoded.name !== "SwapResultEvent")
|
|
63
|
+
continue;
|
|
64
|
+
const data = decoded.data;
|
|
65
|
+
const orderId = toBigint(data.order_id ?? data.orderId);
|
|
66
|
+
const resultData = (data.data ?? {});
|
|
67
|
+
const successRaw = variant(resultData, "Success");
|
|
68
|
+
if (successRaw) {
|
|
69
|
+
const success = unwrapTuple(successRaw);
|
|
70
|
+
const commissions = success.commissions;
|
|
71
|
+
const feeSource = variant((commissions.fee_source ?? commissions.feeSource ?? {}), "Input")
|
|
72
|
+
? "input"
|
|
73
|
+
: "output";
|
|
74
|
+
const items = (commissions.items ?? []).map((item) => ({
|
|
75
|
+
kind: (variant((item.kind ?? {}), "Platform")
|
|
76
|
+
? "platform"
|
|
77
|
+
: "tenant"),
|
|
78
|
+
bps: Number(item.bps),
|
|
79
|
+
amount: toBigint(item.amount),
|
|
80
|
+
tenantProgram: toBase58(item.tenant_program ?? item.tenantProgram),
|
|
81
|
+
}));
|
|
82
|
+
results.push({
|
|
83
|
+
orderId,
|
|
84
|
+
status: "success",
|
|
85
|
+
commissions: {
|
|
86
|
+
feeMint: toBase58(commissions.fee_mint ?? commissions.feeMint),
|
|
87
|
+
feeSource,
|
|
88
|
+
feeBaseAmount: toBigint(commissions.fee_base_amount ?? commissions.feeBaseAmount),
|
|
89
|
+
totalFeeAmount: toBigint(commissions.total_fee_amount ?? commissions.totalFeeAmount),
|
|
90
|
+
items,
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const failureRaw = variant(resultData, "Failure");
|
|
96
|
+
if (failureRaw) {
|
|
97
|
+
const f = unwrapTuple(failureRaw);
|
|
98
|
+
results.push({
|
|
99
|
+
orderId,
|
|
100
|
+
status: "failure",
|
|
101
|
+
failure: { code: toBigint(f.code), message: String(f.message) },
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return results;
|
|
106
|
+
}
|
package/dist/fetch.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as web3 from "@solana/web3.js";
|
|
2
|
+
import type { Address, PumpfunMarketContext } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* The one seam the resolver needs to read chain state. Implement it with a
|
|
5
|
+
* web3.js Connection (see connectionFetcher) or any RPC client. The builder
|
|
6
|
+
* itself never fetches — resolve once server-side, stream the context out.
|
|
7
|
+
*/
|
|
8
|
+
export interface AccountFetcher {
|
|
9
|
+
getAccountInfo(address: web3.PublicKey): Promise<{
|
|
10
|
+
data: Uint8Array;
|
|
11
|
+
owner: web3.PublicKey;
|
|
12
|
+
} | null>;
|
|
13
|
+
}
|
|
14
|
+
/** AccountFetcher backed by a @solana/web3.js Connection. */
|
|
15
|
+
export declare function connectionFetcher(connection: web3.Connection): AccountFetcher;
|
|
16
|
+
/** Which token program owns a mint (classic SPL vs Token-2022). */
|
|
17
|
+
export declare function resolveTokenProgram(fetcher: AccountFetcher, mint: Address): Promise<web3.PublicKey>;
|
|
18
|
+
/** Global.fee_recipient (offset: discriminator + initialized flag + authority). */
|
|
19
|
+
export declare function fetchPumpfunGlobalFeeRecipient(fetcher: AccountFetcher, globalAccount: web3.PublicKey): Promise<web3.PublicKey>;
|
|
20
|
+
/** Non-zero entries of Global.buyback_fee_recipients ([Pubkey; 8]). */
|
|
21
|
+
export declare function fetchPumpfunBuybackFeeRecipients(fetcher: AccountFetcher, globalAccount: web3.PublicKey): Promise<web3.PublicKey[]>;
|
|
22
|
+
/** BondingCurve.creator (offset: discriminator + 5×u64 + bool). */
|
|
23
|
+
export declare function fetchPumpfunBondingCurveCreator(fetcher: AccountFetcher, bondingCurve: web3.PublicKey): Promise<web3.PublicKey>;
|
|
24
|
+
/**
|
|
25
|
+
* Resolve everything a pump.fun swap needs for one mint. Run this server-side
|
|
26
|
+
* next to an RPC (the tenant's backend) and stream the result to clients — every field
|
|
27
|
+
* is a plain base58 string. Only the per-user volume accumulator is left for
|
|
28
|
+
* build time (it depends on the user key).
|
|
29
|
+
*/
|
|
30
|
+
export declare function resolvePumpfunMarketContext(fetcher: AccountFetcher, params: {
|
|
31
|
+
mint: Address;
|
|
32
|
+
pumpfunProgram?: Address;
|
|
33
|
+
pumpfunFeeProgram?: Address;
|
|
34
|
+
}): Promise<PumpfunMarketContext>;
|