@coffer_so/sdk 1.0.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.
Files changed (228) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +283 -0
  3. package/dist/clients/AdminClient.d.ts +194 -0
  4. package/dist/clients/AdminClient.d.ts.map +1 -0
  5. package/dist/clients/AdminClient.js +482 -0
  6. package/dist/clients/AdminClient.js.map +1 -0
  7. package/dist/clients/CofferBackendClient.d.ts +877 -0
  8. package/dist/clients/CofferBackendClient.d.ts.map +1 -0
  9. package/dist/clients/CofferBackendClient.js +536 -0
  10. package/dist/clients/CofferBackendClient.js.map +1 -0
  11. package/dist/clients/CubeBackendClient.d.ts +877 -0
  12. package/dist/clients/CubeBackendClient.d.ts.map +1 -0
  13. package/dist/clients/CubeBackendClient.js +536 -0
  14. package/dist/clients/CubeBackendClient.js.map +1 -0
  15. package/dist/clients/CubicPoolClient.d.ts +120 -0
  16. package/dist/clients/CubicPoolClient.d.ts.map +1 -0
  17. package/dist/clients/CubicPoolClient.js +687 -0
  18. package/dist/clients/CubicPoolClient.js.map +1 -0
  19. package/dist/clients/PoolFactoryClient.d.ts +53 -0
  20. package/dist/clients/PoolFactoryClient.d.ts.map +1 -0
  21. package/dist/clients/PoolFactoryClient.js +91 -0
  22. package/dist/clients/PoolFactoryClient.js.map +1 -0
  23. package/dist/clients/RpcClient.d.ts +49 -0
  24. package/dist/clients/RpcClient.d.ts.map +1 -0
  25. package/dist/clients/RpcClient.js +135 -0
  26. package/dist/clients/RpcClient.js.map +1 -0
  27. package/dist/clients/SingleTokenDepositClient.d.ts +67 -0
  28. package/dist/clients/SingleTokenDepositClient.d.ts.map +1 -0
  29. package/dist/clients/SingleTokenDepositClient.js +105 -0
  30. package/dist/clients/SingleTokenDepositClient.js.map +1 -0
  31. package/dist/clients/contract-instructions.d.ts +24 -0
  32. package/dist/clients/contract-instructions.d.ts.map +1 -0
  33. package/dist/clients/contract-instructions.js +130 -0
  34. package/dist/clients/contract-instructions.js.map +1 -0
  35. package/dist/clients/index.d.ts +10 -0
  36. package/dist/clients/index.d.ts.map +1 -0
  37. package/dist/clients/index.js +26 -0
  38. package/dist/clients/index.js.map +1 -0
  39. package/dist/clients/quote-math.d.ts +15 -0
  40. package/dist/clients/quote-math.d.ts.map +1 -0
  41. package/dist/clients/quote-math.js +98 -0
  42. package/dist/clients/quote-math.js.map +1 -0
  43. package/dist/clients/tx-builders.d.ts +231 -0
  44. package/dist/clients/tx-builders.d.ts.map +1 -0
  45. package/dist/clients/tx-builders.js +861 -0
  46. package/dist/clients/tx-builders.js.map +1 -0
  47. package/dist/clients/versioned.d.ts +34 -0
  48. package/dist/clients/versioned.d.ts.map +1 -0
  49. package/dist/clients/versioned.js +55 -0
  50. package/dist/clients/versioned.js.map +1 -0
  51. package/dist/config/index.d.ts +65 -0
  52. package/dist/config/index.d.ts.map +1 -0
  53. package/dist/config/index.js +75 -0
  54. package/dist/config/index.js.map +1 -0
  55. package/dist/config/networks.d.ts +12 -0
  56. package/dist/config/networks.d.ts.map +1 -0
  57. package/dist/config/networks.js +51 -0
  58. package/dist/config/networks.js.map +1 -0
  59. package/dist/config/tokens.d.ts +19 -0
  60. package/dist/config/tokens.d.ts.map +1 -0
  61. package/dist/config/tokens.js +43 -0
  62. package/dist/config/tokens.js.map +1 -0
  63. package/dist/idl/cubic_pool.json +4203 -0
  64. package/dist/idl/index.d.ts +1769 -0
  65. package/dist/idl/index.d.ts.map +1 -0
  66. package/dist/idl/index.js +18 -0
  67. package/dist/idl/index.js.map +1 -0
  68. package/dist/idl/protocol_admin.json +2615 -0
  69. package/dist/idl/single_token_liquidity.json +1121 -0
  70. package/dist/index.d.ts +75 -0
  71. package/dist/index.d.ts.map +1 -0
  72. package/dist/index.js +206 -0
  73. package/dist/index.js.map +1 -0
  74. package/dist/math/cubicMath.d.ts +40 -0
  75. package/dist/math/cubicMath.d.ts.map +1 -0
  76. package/dist/math/cubicMath.js +82 -0
  77. package/dist/math/cubicMath.js.map +1 -0
  78. package/dist/math/fixedPoint.d.ts +19 -0
  79. package/dist/math/fixedPoint.d.ts.map +1 -0
  80. package/dist/math/fixedPoint.js +86 -0
  81. package/dist/math/fixedPoint.js.map +1 -0
  82. package/dist/math/index.d.ts +9 -0
  83. package/dist/math/index.d.ts.map +1 -0
  84. package/dist/math/index.js +30 -0
  85. package/dist/math/index.js.map +1 -0
  86. package/dist/math/logExp.d.ts +6 -0
  87. package/dist/math/logExp.d.ts.map +1 -0
  88. package/dist/math/logExp.js +68 -0
  89. package/dist/math/logExp.js.map +1 -0
  90. package/dist/math/lpLoss.d.ts +44 -0
  91. package/dist/math/lpLoss.d.ts.map +1 -0
  92. package/dist/math/lpLoss.js +103 -0
  93. package/dist/math/lpLoss.js.map +1 -0
  94. package/dist/math/maxSelloff.d.ts +164 -0
  95. package/dist/math/maxSelloff.d.ts.map +1 -0
  96. package/dist/math/maxSelloff.js +397 -0
  97. package/dist/math/maxSelloff.js.map +1 -0
  98. package/dist/math/singleToken.d.ts +54 -0
  99. package/dist/math/singleToken.d.ts.map +1 -0
  100. package/dist/math/singleToken.js +188 -0
  101. package/dist/math/singleToken.js.map +1 -0
  102. package/dist/math/slippage.d.ts +13 -0
  103. package/dist/math/slippage.d.ts.map +1 -0
  104. package/dist/math/slippage.js +44 -0
  105. package/dist/math/slippage.js.map +1 -0
  106. package/dist/math/surgeFee.d.ts +24 -0
  107. package/dist/math/surgeFee.d.ts.map +1 -0
  108. package/dist/math/surgeFee.js +85 -0
  109. package/dist/math/surgeFee.js.map +1 -0
  110. package/dist/math/weightedMath.d.ts +20 -0
  111. package/dist/math/weightedMath.d.ts.map +1 -0
  112. package/dist/math/weightedMath.js +66 -0
  113. package/dist/math/weightedMath.js.map +1 -0
  114. package/dist/parsers/borsh.d.ts +28 -0
  115. package/dist/parsers/borsh.d.ts.map +1 -0
  116. package/dist/parsers/borsh.js +104 -0
  117. package/dist/parsers/borsh.js.map +1 -0
  118. package/dist/parsers/contracts.d.ts +26 -0
  119. package/dist/parsers/contracts.d.ts.map +1 -0
  120. package/dist/parsers/contracts.js +62 -0
  121. package/dist/parsers/contracts.js.map +1 -0
  122. package/dist/parsers/events.d.ts +3 -0
  123. package/dist/parsers/events.d.ts.map +1 -0
  124. package/dist/parsers/events.js +281 -0
  125. package/dist/parsers/events.js.map +1 -0
  126. package/dist/parsers/index.d.ts +6 -0
  127. package/dist/parsers/index.d.ts.map +1 -0
  128. package/dist/parsers/index.js +22 -0
  129. package/dist/parsers/index.js.map +1 -0
  130. package/dist/parsers/mintAccount.d.ts +24 -0
  131. package/dist/parsers/mintAccount.d.ts.map +1 -0
  132. package/dist/parsers/mintAccount.js +27 -0
  133. package/dist/parsers/mintAccount.js.map +1 -0
  134. package/dist/parsers/poolAccount.d.ts +141 -0
  135. package/dist/parsers/poolAccount.d.ts.map +1 -0
  136. package/dist/parsers/poolAccount.js +224 -0
  137. package/dist/parsers/poolAccount.js.map +1 -0
  138. package/dist/types/contracts.d.ts +1340 -0
  139. package/dist/types/contracts.d.ts.map +1 -0
  140. package/dist/types/contracts.js +3 -0
  141. package/dist/types/contracts.js.map +1 -0
  142. package/dist/types/events.d.ts +172 -0
  143. package/dist/types/events.d.ts.map +1 -0
  144. package/dist/types/events.js +3 -0
  145. package/dist/types/events.js.map +1 -0
  146. package/dist/types/index.d.ts +6 -0
  147. package/dist/types/index.d.ts.map +1 -0
  148. package/dist/types/index.js +22 -0
  149. package/dist/types/index.js.map +1 -0
  150. package/dist/types/pool.d.ts +149 -0
  151. package/dist/types/pool.d.ts.map +1 -0
  152. package/dist/types/pool.js +3 -0
  153. package/dist/types/pool.js.map +1 -0
  154. package/dist/types/result.d.ts +24 -0
  155. package/dist/types/result.d.ts.map +1 -0
  156. package/dist/types/result.js +11 -0
  157. package/dist/types/result.js.map +1 -0
  158. package/dist/types/tx.d.ts +261 -0
  159. package/dist/types/tx.d.ts.map +1 -0
  160. package/dist/types/tx.js +3 -0
  161. package/dist/types/tx.js.map +1 -0
  162. package/dist/utils/errors.d.ts +44 -0
  163. package/dist/utils/errors.d.ts.map +1 -0
  164. package/dist/utils/errors.js +192 -0
  165. package/dist/utils/errors.js.map +1 -0
  166. package/dist/utils/extensions.d.ts +98 -0
  167. package/dist/utils/extensions.d.ts.map +1 -0
  168. package/dist/utils/extensions.js +187 -0
  169. package/dist/utils/extensions.js.map +1 -0
  170. package/dist/utils/index.d.ts +5 -0
  171. package/dist/utils/index.d.ts.map +1 -0
  172. package/dist/utils/index.js +21 -0
  173. package/dist/utils/index.js.map +1 -0
  174. package/dist/utils/pda.d.ts +8 -0
  175. package/dist/utils/pda.d.ts.map +1 -0
  176. package/dist/utils/pda.js +27 -0
  177. package/dist/utils/pda.js.map +1 -0
  178. package/dist/utils/retry.d.ts +22 -0
  179. package/dist/utils/retry.d.ts.map +1 -0
  180. package/dist/utils/retry.js +62 -0
  181. package/dist/utils/retry.js.map +1 -0
  182. package/package.json +54 -0
  183. package/src/clients/AdminClient.ts +608 -0
  184. package/src/clients/CofferBackendClient.ts +1413 -0
  185. package/src/clients/CubicPoolClient.ts +719 -0
  186. package/src/clients/PoolFactoryClient.ts +110 -0
  187. package/src/clients/RpcClient.ts +195 -0
  188. package/src/clients/SingleTokenDepositClient.ts +143 -0
  189. package/src/clients/contract-instructions.ts +115 -0
  190. package/src/clients/index.ts +9 -0
  191. package/src/clients/quote-math.ts +81 -0
  192. package/src/clients/tx-builders.ts +1096 -0
  193. package/src/clients/versioned.ts +80 -0
  194. package/src/config/index.ts +111 -0
  195. package/src/config/networks.ts +71 -0
  196. package/src/config/tokens.ts +52 -0
  197. package/src/idl/cubic_pool.json +4203 -0
  198. package/src/idl/index.ts +13 -0
  199. package/src/idl/protocol_admin.json +2615 -0
  200. package/src/idl/single_token_liquidity.json +1121 -0
  201. package/src/index.ts +262 -0
  202. package/src/math/cubicMath.ts +92 -0
  203. package/src/math/fixedPoint.ts +71 -0
  204. package/src/math/index.ts +13 -0
  205. package/src/math/logExp.ts +61 -0
  206. package/src/math/lpLoss.ts +160 -0
  207. package/src/math/maxSelloff.ts +564 -0
  208. package/src/math/singleToken.ts +251 -0
  209. package/src/math/slippage.ts +38 -0
  210. package/src/math/surgeFee.ts +101 -0
  211. package/src/math/weightedMath.ts +64 -0
  212. package/src/parsers/borsh.ts +106 -0
  213. package/src/parsers/contracts.ts +64 -0
  214. package/src/parsers/events.ts +275 -0
  215. package/src/parsers/index.ts +5 -0
  216. package/src/parsers/mintAccount.ts +44 -0
  217. package/src/parsers/poolAccount.ts +372 -0
  218. package/src/types/contracts.ts +238 -0
  219. package/src/types/events.ts +198 -0
  220. package/src/types/index.ts +5 -0
  221. package/src/types/pool.ts +147 -0
  222. package/src/types/result.ts +51 -0
  223. package/src/types/tx.ts +278 -0
  224. package/src/utils/errors.ts +210 -0
  225. package/src/utils/extensions.ts +196 -0
  226. package/src/utils/index.ts +4 -0
  227. package/src/utils/pda.ts +58 -0
  228. package/src/utils/retry.ts +85 -0
@@ -0,0 +1,196 @@
1
+ import BN from "bn.js";
2
+ import { PoolInfo, PoolTokenInfo } from "../types/pool";
3
+
4
+ /**
5
+ * Token-2022 mint-extension policy.
6
+ *
7
+ * The cubic-pool program moves tokens with the plain SPL `Transfer`
8
+ * instruction (no `transfer_checked`, no fee accounting). Token-2022 refuses
9
+ * a plain `Transfer` for mints that carry a transfer fee, transfer hook or
10
+ * pausable extension,
11
+ * and a `NonTransferable` mint cannot move at all — so any pool operation
12
+ * that touches such a token reverts inside the token program
13
+ * (`MintRequiredForTransfer`, error 0x1f), after the user has already paid
14
+ * for the transaction.
15
+ *
16
+ * The SDK therefore inspects every pool token's mint on `sync()` and refuses
17
+ * to quote or build a transaction that would move a token with an
18
+ * unsupported extension. The refusal is a normal `SdkResult` error with code
19
+ * `unsupported_token_extension` (or a thrown `Error` from the low-level
20
+ * `build*Ix` helpers).
21
+ */
22
+
23
+ /** Token-2022 `ExtensionType` discriminants (u16, little-endian on the wire). */
24
+ export enum MintExtension {
25
+ Uninitialized = 0,
26
+ TransferFeeConfig = 1,
27
+ TransferFeeAmount = 2,
28
+ MintCloseAuthority = 3,
29
+ ConfidentialTransferMint = 4,
30
+ ConfidentialTransferAccount = 5,
31
+ DefaultAccountState = 6,
32
+ ImmutableOwner = 7,
33
+ MemoTransfer = 8,
34
+ NonTransferable = 9,
35
+ InterestBearingConfig = 10,
36
+ CpiGuard = 11,
37
+ PermanentDelegate = 12,
38
+ NonTransferableAccount = 13,
39
+ TransferHook = 14,
40
+ TransferHookAccount = 15,
41
+ ConfidentialTransferFeeConfig = 16,
42
+ ConfidentialTransferFeeAmount = 17,
43
+ MetadataPointer = 18,
44
+ TokenMetadata = 19,
45
+ GroupPointer = 20,
46
+ TokenGroup = 21,
47
+ GroupMemberPointer = 22,
48
+ TokenGroupMember = 23,
49
+ ConfidentialMintBurn = 24,
50
+ ScaledUiAmountConfig = 25,
51
+ PausableConfig = 26,
52
+ PausableAccount = 27,
53
+ PermissionedBurn = 28,
54
+ }
55
+
56
+ /** Highest extension discriminant accepted by the deployed cubic-pool program. */
57
+ export const MAX_KNOWN_MINT_EXTENSION = MintExtension.PausableAccount;
58
+
59
+ /**
60
+ * Extensions the AMM can never move, whatever the pool's `banned_extensions`
61
+ * policy says. This is a transfer-capability check, not the mint-creation
62
+ * policy: even an inert TransferHook needs a mint account for plain Transfer.
63
+ */
64
+ export const HARD_UNSUPPORTED_MINT_EXTENSIONS: ReadonlyArray<MintExtension> = [
65
+ MintExtension.TransferFeeConfig, // Token-2022 rejects plain Transfer (needs transfer_checked_with_fee)
66
+ MintExtension.TransferHook, // Token-2022 rejects plain Transfer (needs transfer_checked + hook accounts)
67
+ MintExtension.NonTransferable, // cannot move at all
68
+ MintExtension.ConfidentialTransferFeeConfig, // implies a transfer fee
69
+ MintExtension.PausableConfig, // Token-2022 needs the mint to check the pause state
70
+ ];
71
+
72
+ /**
73
+ * `DEFAULT_BANNED_EXTENSIONS` from `cubic-pool/constants.rs`: the bitmap a
74
+ * fresh `CubicPoolConfig` starts with. Exposed so consumers can render the
75
+ * policy; the per-pool effective bitmap is `PoolInfo.bannedExtensions`.
76
+ */
77
+ export const DEFAULT_BANNED_EXTENSIONS: bigint =
78
+ (1n << BigInt(MintExtension.TransferFeeConfig)) |
79
+ (1n << BigInt(MintExtension.MintCloseAuthority)) |
80
+ (1n << BigInt(MintExtension.InterestBearingConfig)) |
81
+ (1n << BigInt(MintExtension.PermanentDelegate)) |
82
+ (1n << BigInt(MintExtension.TransferHook)) |
83
+ (1n << BigInt(MintExtension.ScaledUiAmountConfig)) |
84
+ (1n << BigInt(MintExtension.PausableConfig));
85
+
86
+ /** Human-readable name for an extension discriminant. */
87
+ export function mintExtensionName(type: number): string {
88
+ return MintExtension[type] ?? `Unknown(${type})`;
89
+ }
90
+
91
+ const MINT_BASE_LEN = 82;
92
+ const ACCOUNT_TYPE_OFFSET = 165; // base(82) + padding to token-account size(165)
93
+ const ACCOUNT_TYPE_MINT = 1;
94
+
95
+ /**
96
+ * Parse the TLV extension list of a Token-2022 mint. Returns `[]` for a
97
+ * classic SPL Token mint (82 bytes) or a Token-2022 mint with no
98
+ * extensions. Rejects uninitialized mints, invalid mint layouts and malformed
99
+ * TLV data. Pool policy and transfer capability are classified separately.
100
+ */
101
+ export function parseMintExtensions(data: Buffer | Uint8Array): number[] {
102
+ const buf = Buffer.isBuffer(data) ? data : Buffer.from(data);
103
+ if (buf.length !== MINT_BASE_LEN && buf.length <= ACCOUNT_TYPE_OFFSET) {
104
+ throw new Error(`parseMintExtensions: invalid mint length ${buf.length}`);
105
+ }
106
+ if (buf[45] !== 1) {
107
+ throw new Error("parseMintExtensions: mint is not initialized");
108
+ }
109
+ if (buf.length === MINT_BASE_LEN) return [];
110
+ if (buf[ACCOUNT_TYPE_OFFSET] !== ACCOUNT_TYPE_MINT) {
111
+ throw new Error(
112
+ `parseMintExtensions: account type byte is ${buf[ACCOUNT_TYPE_OFFSET]}, expected ${ACCOUNT_TYPE_MINT} (Mint)`
113
+ );
114
+ }
115
+ const out: number[] = [];
116
+ let off = ACCOUNT_TYPE_OFFSET + 1;
117
+ while (off + 4 <= buf.length) {
118
+ const type = buf.readUInt16LE(off);
119
+ const len = buf.readUInt16LE(off + 2);
120
+ if (type === MintExtension.Uninitialized) {
121
+ if (buf.subarray(off).some((byte) => byte !== 0)) {
122
+ throw new Error("parseMintExtensions: nonzero data in uninitialized TLV tail");
123
+ }
124
+ return out;
125
+ }
126
+ off += 4;
127
+ if (off + len > buf.length) {
128
+ throw new Error(`parseMintExtensions: extension ${type} claims ${len} bytes past end of account`);
129
+ }
130
+ if (out.includes(type)) {
131
+ throw new Error(`parseMintExtensions: duplicate extension ${type}`);
132
+ }
133
+ out.push(type);
134
+ off += len;
135
+ }
136
+ if (buf.subarray(off).some((byte) => byte !== 0)) {
137
+ throw new Error("parseMintExtensions: incomplete TLV header");
138
+ }
139
+ return out;
140
+ }
141
+
142
+ /**
143
+ * Which of `extensions` the AMM cannot transfer: everything in
144
+ * {@link HARD_UNSUPPORTED_MINT_EXTENSIONS}, anything the program does not
145
+ * know (> {@link MAX_KNOWN_MINT_EXTENSION}; the program rejects unknown
146
+ * types at creation and the SDK cannot assume their transfer semantics).
147
+ *
148
+ * `banned_extensions` is a snapshot of creation policy, not a runtime transfer
149
+ * restriction. The optional second argument is retained for compatibility
150
+ * but does not change this result. Use `bannedMintExtensions` for diagnostics.
151
+ */
152
+ export function unsupportedMintExtensions(extensions: number[], _bannedExtensions?: BN | bigint): number[] {
153
+ return extensions.filter(
154
+ (type) => HARD_UNSUPPORTED_MINT_EXTENSIONS.includes(type) || type > MAX_KNOWN_MINT_EXTENSION
155
+ );
156
+ }
157
+
158
+ /** Extensions covered by a creation-policy bitmap; does not imply transfer failure. */
159
+ export function bannedMintExtensions(extensions: number[], bannedExtensions?: BN | bigint): number[] {
160
+ const banned =
161
+ bannedExtensions === undefined
162
+ ? 0n
163
+ : typeof bannedExtensions === "bigint"
164
+ ? bannedExtensions
165
+ : BigInt(bannedExtensions.toString());
166
+ if (banned < 0n || banned > 0xffff_ffff_ffff_ffffn) {
167
+ throw new Error("bannedMintExtensions: bitmap must fit u64");
168
+ }
169
+ return extensions.filter((type) => type >= 0 && type < 64 && ((banned >> BigInt(type)) & 1n) === 1n);
170
+ }
171
+
172
+ /** One-line description of why a token is refused, for error messages. */
173
+ export function describeUnsupportedToken(t: Pick<PoolTokenInfo, "index" | "mint" | "unsupportedExtensions" | "metadata">): string {
174
+ const sym = t.metadata?.symbol ?? t.mint.toBase58();
175
+ return `token[${t.index}] ${sym} has unsupported Token-2022 extension(s): ${(t.unsupportedExtensions ?? [])
176
+ .map(mintExtensionName)
177
+ .join(", ")}`;
178
+ }
179
+
180
+ /**
181
+ * Throw if any of the given token indices (or every token when `indices` is
182
+ * `"all"`) carries an unsupported extension. Used by the low-level
183
+ * instruction builders so that direct `build*Ix` callers get the same
184
+ * protection as `CubicPoolClient` users.
185
+ */
186
+ export function assertTokensSupported(pool: PoolInfo, indices: number[] | "all", op: string): void {
187
+ const idx = indices === "all" ? pool.tokens.map((t) => t.index) : indices;
188
+ const bad = idx
189
+ .map((i) => pool.tokens[i])
190
+ .filter((t): t is PoolTokenInfo => !!t && (t.unsupportedExtensions?.length ?? 0) > 0);
191
+ if (bad.length === 0) return;
192
+ throw new Error(
193
+ `${op}: unsupported Token-2022 extension for this program's transfer instructions. ` +
194
+ bad.map(describeUnsupportedToken).join("; ")
195
+ );
196
+ }
@@ -0,0 +1,4 @@
1
+ export * from "./errors";
2
+ export * from "./retry";
3
+ export * from "./pda";
4
+ export * from "./extensions";
@@ -0,0 +1,58 @@
1
+ import { PublicKey } from "@solana/web3.js";
2
+ import BN from "bn.js";
3
+ import { ASSOCIATED_TOKEN_PROGRAM_ID } from "@solana/spl-token";
4
+ import {
5
+ BPT_MINT_SEED,
6
+ CUBIC_POOL_SEED,
7
+ STLD_HELPER_SEED,
8
+ TREASURY_SEED,
9
+ } from "../config";
10
+
11
+ export function derivePoolPda(
12
+ programId: PublicKey,
13
+ config: PublicKey,
14
+ poolId: BN
15
+ ): [PublicKey, number] {
16
+ return PublicKey.findProgramAddressSync(
17
+ [CUBIC_POOL_SEED, config.toBuffer(), poolId.toArrayLike(Buffer, "le", 8)],
18
+ programId
19
+ );
20
+ }
21
+
22
+ export function deriveBptMint(
23
+ cubicPoolProgramId: PublicKey,
24
+ pool: PublicKey
25
+ ): [PublicKey, number] {
26
+ return PublicKey.findProgramAddressSync(
27
+ [BPT_MINT_SEED, pool.toBuffer()],
28
+ cubicPoolProgramId
29
+ );
30
+ }
31
+
32
+ export function deriveHelperPda(
33
+ stldProgramId: PublicKey,
34
+ pool: PublicKey
35
+ ): [PublicKey, number] {
36
+ return PublicKey.findProgramAddressSync(
37
+ [STLD_HELPER_SEED, pool.toBuffer()],
38
+ stldProgramId
39
+ );
40
+ }
41
+
42
+ export function deriveTreasuryPda(
43
+ protocolFeesProgramId: PublicKey
44
+ ): [PublicKey, number] {
45
+ return PublicKey.findProgramAddressSync([TREASURY_SEED], protocolFeesProgramId);
46
+ }
47
+
48
+ export function deriveAta(
49
+ owner: PublicKey,
50
+ mint: PublicKey,
51
+ tokenProgram: PublicKey
52
+ ): PublicKey {
53
+ const [ata] = PublicKey.findProgramAddressSync(
54
+ [owner.toBuffer(), tokenProgram.toBuffer(), mint.toBuffer()],
55
+ ASSOCIATED_TOKEN_PROGRAM_ID
56
+ );
57
+ return ata;
58
+ }
@@ -0,0 +1,85 @@
1
+ import { SdkResult, err, ok } from "../types/result";
2
+ import { toSdkError } from "./errors";
3
+
4
+ export interface RetryOptions {
5
+ /** Number of attempts total (1 = no retry). */
6
+ attempts?: number;
7
+ /** Per-attempt timeout (ms). */
8
+ timeoutMs?: number;
9
+ /** Backoff schedule (ms) between attempts. */
10
+ backoffMs?: number[];
11
+ /** Additional predicate: return `false` to stop retrying early. */
12
+ shouldRetry?: (error: unknown) => boolean;
13
+ }
14
+
15
+ const DEFAULT_BACKOFF_MS = [200, 500, 1500];
16
+
17
+ /**
18
+ * Retry-wrapped helper. Returns a SdkResult<T>. Never throws. Use for any
19
+ * I/O: RPC calls, backend fetches, CPI simulations.
20
+ *
21
+ * ```ts
22
+ * const res = await safeCall(() => connection.getAccountInfo(pk), { attempts: 3 });
23
+ * if (!res.ok) showToast(res.error.humanMessage);
24
+ * ```
25
+ */
26
+ export async function safeCall<T>(
27
+ operation: () => Promise<T>,
28
+ opts: RetryOptions = {}
29
+ ): Promise<SdkResult<T>> {
30
+ const attempts = opts.attempts ?? 3;
31
+ const backoff = opts.backoffMs ?? DEFAULT_BACKOFF_MS;
32
+ const timeoutMs = opts.timeoutMs ?? 15_000;
33
+ const shouldRetry = opts.shouldRetry ?? defaultShouldRetry;
34
+
35
+ let lastError: unknown;
36
+ for (let attempt = 0; attempt < attempts; attempt++) {
37
+ try {
38
+ const raced = await withTimeout(operation(), timeoutMs);
39
+ return ok(raced);
40
+ } catch (e) {
41
+ lastError = e;
42
+ if (!shouldRetry(e)) break;
43
+ if (attempt < attempts - 1) {
44
+ const delayIdx = Math.min(attempt, backoff.length - 1);
45
+ await sleep(backoff[delayIdx]);
46
+ }
47
+ }
48
+ }
49
+ return err(
50
+ toSdkError(lastError).code,
51
+ toSdkError(lastError).humanMessage,
52
+ lastError
53
+ );
54
+ }
55
+
56
+ function defaultShouldRetry(e: unknown): boolean {
57
+ const err = toSdkError(e);
58
+ // Retry on transient network / rate-limit errors, stop on structural ones.
59
+ return (
60
+ err.code === "rpc_timeout" ||
61
+ err.code === "rpc_unavailable" ||
62
+ err.code === "rpc_rate_limited" ||
63
+ err.code === "backend_unavailable"
64
+ );
65
+ }
66
+
67
+ function sleep(ms: number): Promise<void> {
68
+ return new Promise((r) => setTimeout(r, ms));
69
+ }
70
+
71
+ function withTimeout<T>(p: Promise<T>, ms: number): Promise<T> {
72
+ return new Promise((resolve, reject) => {
73
+ const h = setTimeout(() => reject(new Error(`timed out after ${ms}ms`)), ms);
74
+ p.then(
75
+ (v) => {
76
+ clearTimeout(h);
77
+ resolve(v);
78
+ },
79
+ (e) => {
80
+ clearTimeout(h);
81
+ reject(e);
82
+ }
83
+ );
84
+ });
85
+ }