@picon-finance/dlmm-sdk 1.16.1 → 1.16.3

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 CHANGED
@@ -5,9 +5,22 @@ TypeScript SDK for the [Picon DLMM](https://github.com/picon-finance) protocol o
5
5
  ## Install
6
6
 
7
7
  ```bash
8
+ bun add @picon-finance/dlmm-sdk
9
+ yarn add @picon-finance/dlmm-sdk
8
10
  npm install @picon-finance/dlmm-sdk
9
11
  ```
10
12
 
13
+ ## Environments
14
+
15
+ The package targets ESM (`"type": "module"`), but its ESM resolution has limitations —
16
+ supported consumption splits into two scenarios:
17
+
18
+ - **Server-side (Node scripts, tests, tooling): use [Bun](https://bun.sh).**
19
+ - **Client-side / anything bundler-fronted (webpack, Vite, Rollup, ...): works as-is.** Webpack
20
+ needs `resolve.fullySpecified: false` on `.m?js$`; most other bundlers are lenient by default.
21
+
22
+ **Not currently supported: bare `node` with no Bun and no bundler in front of it.**
23
+
11
24
  ## Usage
12
25
 
13
26
  The SDK is built around stateful classes (`DlmmProgram`, `Pool`, `Position`, `BinArray`) that cache on-chain state to avoid redundant RPC round trips. Every method returns unsigned `Instruction`s (never signs or sends) — build, sign, and send the transaction yourself with `@solana/kit`.
@@ -188,14 +201,6 @@ for (const claimIxs of claimTxs) {
188
201
  }
189
202
  ```
190
203
 
191
- ### Admin
192
-
193
- `Pool.claimProtocolFee(authority)` drains `pool.data.protocolFeeX`/`protocolFeeY` to the admin's associated token accounts — `authority` must match `AdminConfig.authority`. `Pool.setProtocolShare(authority, protocolShare)` and `Pool.setDynamicFeeConfig(authority, config)` are similarly admin-gated single instructions:
194
-
195
- ```ts
196
- const claimProtocolFeeIxs = await pool.claimProtocolFee(adminSigner);
197
- ```
198
-
199
204
  ### Price math
200
205
 
201
206
  `getBaseBinPrice(binStep)`, `getBinPrice(baseBinPrice, binId)`, and `getBinPriceFromStep(binStep, binId)` are direct `bigint` ports of the on-chain Q64.64 price math (`bin_math.rs`'s `base_bin_price`/`bin_price`/`bin_price_from_step`) — useful for computing a bin's price without an RPC round trip, e.g. when picking `lowerBinId`/`upperBinId` for a new position before it exists on-chain. `getBinPrice`/`getBinPriceFromStep` return `undefined` (mirroring the Rust `Option`) if `binId` is outside the representable range for that `binStep`:
@@ -2,7 +2,7 @@
2
2
  "address": "pDLMMk3KW3CUSxE1Wgjhx3jSmm2spe6Ry7Mq2HT26rC",
3
3
  "metadata": {
4
4
  "name": "picon_dlmm",
5
- "version": "1.1.0",
5
+ "version": "1.1.1",
6
6
  "spec": "0.1.0",
7
7
  "description": "Created with Anchor"
8
8
  },
@@ -1 +1 @@
1
- {"version":3,"file":"assert.d.ts","sourceRoot":"","sources":["../../src/utils/assert.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,UAAU,GAChB,IAAI,CAMN;AAED,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,EAC3B,IAAI,EAAE;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,GACzB,IAAI,CAMN"}
1
+ {"version":3,"file":"assert.d.ts","sourceRoot":"","sources":["../../src/utils/assert.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,UAAU,GAChB,IAAI,CAMN;AAED,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,EAC3B,IAAI,EAAE;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,GACzB,IAAI,CAMN"}
@@ -1,4 +1,5 @@
1
1
  export declare function getBaseBinPrice(binStep: number): bigint;
2
2
  export declare function getBinPrice(baseBinPrice: bigint, binId: number): bigint | undefined;
3
3
  export declare function getBinPriceFromStep(binStep: number, binId: number): bigint | undefined;
4
+ export declare function getBinIdFromPrice(binStep: number, price: bigint): number | undefined;
4
5
  //# sourceMappingURL=price.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"price.d.ts","sourceRoot":"","sources":["../../src/utils/price.ts"],"names":[],"mappings":"AAGA,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGvD;AAED,wBAAgB,WAAW,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAmBnF;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEtF"}
1
+ {"version":3,"file":"price.d.ts","sourceRoot":"","sources":["../../src/utils/price.ts"],"names":[],"mappings":"AAIA,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGvD;AAED,wBAAgB,WAAW,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAmBnF;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEtF;AA8BD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAsCpF"}
@@ -1,4 +1,5 @@
1
1
  import { MAX_BASIS_POINTS } from "./constants";
2
+ import { isUndefined } from "./type_guards";
2
3
  import { expBySquaring, MAX, MAX_EXPONENTIAL, ONE, SCALE_OFFSET } from "./u64x64";
3
4
  export function getBaseBinPrice(binStep) {
4
5
  const priceStep = (BigInt(binStep) << SCALE_OFFSET) / BigInt(MAX_BASIS_POINTS);
@@ -16,8 +17,8 @@ export function getBinPrice(baseBinPrice, binId) {
16
17
  const absBinId = Math.abs(binId);
17
18
  if (absBinId >= MAX_EXPONENTIAL)
18
19
  return;
19
- const invertedBase = MAX / baseBinPrice;
20
- const price = expBySquaring(invertedBase, absBinId);
20
+ const invertedBaseBinPrice = MAX / baseBinPrice;
21
+ const price = expBySquaring(invertedBaseBinPrice, absBinId);
21
22
  if (price === 0n)
22
23
  return;
23
24
  return invert ? MAX / price : price;
@@ -25,3 +26,64 @@ export function getBinPrice(baseBinPrice, binId) {
25
26
  export function getBinPriceFromStep(binStep, binId) {
26
27
  return getBinPrice(getBaseBinPrice(binStep), binId);
27
28
  }
29
+ const MAX_BIN_ID_BY_BIN_STEP = new Map([
30
+ [1, 443636],
31
+ [2, 221829],
32
+ [4, 110925],
33
+ [5, 88745],
34
+ [8, 55473],
35
+ [10, 44383],
36
+ [15, 29596],
37
+ [16, 27748],
38
+ [20, 22202],
39
+ [25, 17759],
40
+ [30, 14809],
41
+ [50, 8893],
42
+ [75, 5937],
43
+ [80, 5567],
44
+ [100, 4456],
45
+ [125, 3571],
46
+ [150, 2979],
47
+ [160, 2794],
48
+ [200, 2239],
49
+ [250, 1796],
50
+ [300, 1500],
51
+ [400, 1130],
52
+ ]);
53
+ const MIN_PRICE = 1n;
54
+ const MAX_PRICE = MAX;
55
+ export function getBinIdFromPrice(binStep, price) {
56
+ if (price <= 0n)
57
+ return;
58
+ const maxBinId = MAX_BIN_ID_BY_BIN_STEP.get(binStep);
59
+ if (isUndefined(maxBinId)) {
60
+ throw new Error(`getBinIdFromPrice: unsupported binStep (${binStep})`);
61
+ }
62
+ if (price <= MIN_PRICE)
63
+ return -maxBinId;
64
+ if (price >= MAX_PRICE)
65
+ return maxBinId;
66
+ const baseBinPrice = getBaseBinPrice(binStep);
67
+ let lowerBinId = -maxBinId;
68
+ let upperBinId = maxBinId;
69
+ let currentPrice = MAX_PRICE;
70
+ let previousPrice;
71
+ while (lowerBinId < upperBinId) {
72
+ const middleBinId = lowerBinId + Math.floor((upperBinId - lowerBinId) / 2);
73
+ const middlePrice = getBinPrice(baseBinPrice, middleBinId);
74
+ if (middlePrice < price) {
75
+ lowerBinId = middleBinId + 1;
76
+ previousPrice = middlePrice;
77
+ }
78
+ else {
79
+ upperBinId = middleBinId;
80
+ currentPrice = middlePrice;
81
+ }
82
+ }
83
+ if (isUndefined(previousPrice))
84
+ return lowerBinId;
85
+ // previousPrice < price <= currentPrice always holds here, so both distances are non-negative.
86
+ const previousPriceDistance = price - previousPrice;
87
+ const currentPriceDistance = currentPrice - price;
88
+ return previousPriceDistance < currentPriceDistance ? lowerBinId - 1 : lowerBinId;
89
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@picon-finance/dlmm-sdk",
3
- "version": "1.16.1",
3
+ "version": "1.16.3",
4
4
  "description": "Typescript SDK for interacting with the Picon DLMM protocol",
5
5
  "author": "Picon Finance",
6
6
  "license": "MIT",
@@ -23,7 +23,8 @@
23
23
  },
24
24
  "files": [
25
25
  "./dist/",
26
- "./src/"
26
+ "./src/",
27
+ "!**/*.test.ts"
27
28
  ],
28
29
  "scripts": {
29
30
  "clean": "rm -rf ./dist/",
@@ -36,9 +37,10 @@
36
37
  "build-wasm-bindings-web": "wasm-bindgen ../target/wasm32-unknown-unknown/release/picon_dlmm_quote.wasm --out-dir ./src/quote/wasm-web --target web --typescript",
37
38
  "build-wasm": "bun run build-wasm-crate && bun run clean-wasm && bun run build-wasm-bindings-node && bun run build-wasm-bindings-web",
38
39
  "copy-wasm-binaries": "mkdir -p ./dist/quote/wasm-node ./dist/quote/wasm-web && cp ./src/quote/wasm-node/*.wasm ./src/quote/wasm-node/*.js ./src/quote/wasm-node/*.d.ts ./dist/quote/wasm-node/ && cp ./src/quote/wasm-web/*.wasm ./src/quote/wasm-web/*.js ./src/quote/wasm-web/*.d.ts ./dist/quote/wasm-web/",
39
- "compile": "bun run clean && npx tsc && bun run copy-wasm-binaries",
40
+ "compile": "bun run clean && npx tsc -p tsconfig.build.json && bun run copy-wasm-binaries",
40
41
  "build": "bun run codama-generate-js && bun run build-wasm && bun run compile",
41
- "prepublishOnly": "bun run build"
42
+ "prepublishOnly": "bun run build",
43
+ "test": "bun test"
42
44
  },
43
45
  "dependencies": {
44
46
  "@solana-program/system": "^0.13.0",
@@ -30,12 +30,12 @@ import {
30
30
  assertPositionBelongsToPool,
31
31
  } from "../utils/assert";
32
32
  import { isUndefined } from "../utils/type_guards";
33
- import {
33
+ import {
34
34
  chunk,
35
35
  fetchInChunks,
36
36
  } from "../utils/chunk";
37
37
  import { resolveAdminConfigAddress } from "./admin_config";
38
- import {
38
+ import {
39
39
  DEFAULT_POSITIONS_PER_CLAIM,
40
40
  MAX_ACCOUNTS_PER_RPC_BATCH,
41
41
  } from "../utils/constants";
@@ -2,7 +2,7 @@
2
2
  "address": "pDLMMk3KW3CUSxE1Wgjhx3jSmm2spe6Ry7Mq2HT26rC",
3
3
  "metadata": {
4
4
  "name": "picon_dlmm",
5
- "version": "1.1.0",
5
+ "version": "1.1.1",
6
6
  "spec": "0.1.0",
7
7
  "description": "Created with Anchor"
8
8
  },
@@ -1,5 +1,4 @@
1
1
  import type { Address } from "@solana/kit";
2
-
3
2
  import type { BinIdRange } from "./bin_location";
4
3
 
5
4
  export function assertRangeWithinRange(
@@ -1,4 +1,5 @@
1
1
  import { MAX_BASIS_POINTS } from "./constants";
2
+ import { isUndefined } from "./type_guards";
2
3
  import { expBySquaring, MAX, MAX_EXPONENTIAL, ONE, SCALE_OFFSET } from "./u64x64";
3
4
 
4
5
  export function getBaseBinPrice(binStep: number): bigint {
@@ -19,8 +20,8 @@ export function getBinPrice(baseBinPrice: bigint, binId: number): bigint | undef
19
20
 
20
21
  if (absBinId >= MAX_EXPONENTIAL) return;
21
22
 
22
- const invertedBase = MAX / baseBinPrice;
23
- const price = expBySquaring(invertedBase, absBinId);
23
+ const invertedBaseBinPrice = MAX / baseBinPrice;
24
+ const price = expBySquaring(invertedBaseBinPrice, absBinId);
24
25
 
25
26
  if (price === 0n) return;
26
27
 
@@ -30,3 +31,71 @@ export function getBinPrice(baseBinPrice: bigint, binId: number): bigint | undef
30
31
  export function getBinPriceFromStep(binStep: number, binId: number): bigint | undefined {
31
32
  return getBinPrice(getBaseBinPrice(binStep), binId);
32
33
  }
34
+
35
+ const MAX_BIN_ID_BY_BIN_STEP = new Map<number, number>([
36
+ [1, 443636],
37
+ [2, 221829],
38
+ [4, 110925],
39
+ [5, 88745],
40
+ [8, 55473],
41
+ [10, 44383],
42
+ [15, 29596],
43
+ [16, 27748],
44
+ [20, 22202],
45
+ [25, 17759],
46
+ [30, 14809],
47
+ [50, 8893],
48
+ [75, 5937],
49
+ [80, 5567],
50
+ [100, 4456],
51
+ [125, 3571],
52
+ [150, 2979],
53
+ [160, 2794],
54
+ [200, 2239],
55
+ [250, 1796],
56
+ [300, 1500],
57
+ [400, 1130],
58
+ ]);
59
+
60
+ const MIN_PRICE = 1n;
61
+ const MAX_PRICE = MAX;
62
+
63
+ export function getBinIdFromPrice(binStep: number, price: bigint): number | undefined {
64
+ if (price <= 0n) return;
65
+
66
+ const maxBinId = MAX_BIN_ID_BY_BIN_STEP.get(binStep);
67
+ if (isUndefined(maxBinId)) {
68
+ throw new Error(`getBinIdFromPrice: unsupported binStep (${binStep})`);
69
+ }
70
+
71
+ if (price <= MIN_PRICE) return -maxBinId;
72
+ if (price >= MAX_PRICE) return maxBinId;
73
+
74
+ const baseBinPrice = getBaseBinPrice(binStep);
75
+
76
+ let lowerBinId = -maxBinId;
77
+ let upperBinId = maxBinId;
78
+
79
+ let currentPrice = MAX_PRICE;
80
+ let previousPrice: bigint | undefined;
81
+
82
+ while (lowerBinId < upperBinId) {
83
+ const middleBinId = lowerBinId + Math.floor((upperBinId - lowerBinId) / 2);
84
+ const middlePrice = getBinPrice(baseBinPrice, middleBinId)!;
85
+
86
+ if (middlePrice < price) {
87
+ lowerBinId = middleBinId + 1;
88
+ previousPrice = middlePrice;
89
+ } else {
90
+ upperBinId = middleBinId;
91
+ currentPrice = middlePrice;
92
+ }
93
+ }
94
+
95
+ if (isUndefined(previousPrice)) return lowerBinId;
96
+
97
+ // previousPrice < price <= currentPrice always holds here, so both distances are non-negative.
98
+ const previousPriceDistance = price - previousPrice;
99
+ const currentPriceDistance = currentPrice - price;
100
+ return previousPriceDistance < currentPriceDistance ? lowerBinId - 1 : lowerBinId;
101
+ }