@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.
- package/LICENSE +21 -0
- package/README.md +283 -0
- package/dist/clients/AdminClient.d.ts +194 -0
- package/dist/clients/AdminClient.d.ts.map +1 -0
- package/dist/clients/AdminClient.js +482 -0
- package/dist/clients/AdminClient.js.map +1 -0
- package/dist/clients/CofferBackendClient.d.ts +877 -0
- package/dist/clients/CofferBackendClient.d.ts.map +1 -0
- package/dist/clients/CofferBackendClient.js +536 -0
- package/dist/clients/CofferBackendClient.js.map +1 -0
- package/dist/clients/CubeBackendClient.d.ts +877 -0
- package/dist/clients/CubeBackendClient.d.ts.map +1 -0
- package/dist/clients/CubeBackendClient.js +536 -0
- package/dist/clients/CubeBackendClient.js.map +1 -0
- package/dist/clients/CubicPoolClient.d.ts +120 -0
- package/dist/clients/CubicPoolClient.d.ts.map +1 -0
- package/dist/clients/CubicPoolClient.js +687 -0
- package/dist/clients/CubicPoolClient.js.map +1 -0
- package/dist/clients/PoolFactoryClient.d.ts +53 -0
- package/dist/clients/PoolFactoryClient.d.ts.map +1 -0
- package/dist/clients/PoolFactoryClient.js +91 -0
- package/dist/clients/PoolFactoryClient.js.map +1 -0
- package/dist/clients/RpcClient.d.ts +49 -0
- package/dist/clients/RpcClient.d.ts.map +1 -0
- package/dist/clients/RpcClient.js +135 -0
- package/dist/clients/RpcClient.js.map +1 -0
- package/dist/clients/SingleTokenDepositClient.d.ts +67 -0
- package/dist/clients/SingleTokenDepositClient.d.ts.map +1 -0
- package/dist/clients/SingleTokenDepositClient.js +105 -0
- package/dist/clients/SingleTokenDepositClient.js.map +1 -0
- package/dist/clients/contract-instructions.d.ts +24 -0
- package/dist/clients/contract-instructions.d.ts.map +1 -0
- package/dist/clients/contract-instructions.js +130 -0
- package/dist/clients/contract-instructions.js.map +1 -0
- package/dist/clients/index.d.ts +10 -0
- package/dist/clients/index.d.ts.map +1 -0
- package/dist/clients/index.js +26 -0
- package/dist/clients/index.js.map +1 -0
- package/dist/clients/quote-math.d.ts +15 -0
- package/dist/clients/quote-math.d.ts.map +1 -0
- package/dist/clients/quote-math.js +98 -0
- package/dist/clients/quote-math.js.map +1 -0
- package/dist/clients/tx-builders.d.ts +231 -0
- package/dist/clients/tx-builders.d.ts.map +1 -0
- package/dist/clients/tx-builders.js +861 -0
- package/dist/clients/tx-builders.js.map +1 -0
- package/dist/clients/versioned.d.ts +34 -0
- package/dist/clients/versioned.d.ts.map +1 -0
- package/dist/clients/versioned.js +55 -0
- package/dist/clients/versioned.js.map +1 -0
- package/dist/config/index.d.ts +65 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +75 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/networks.d.ts +12 -0
- package/dist/config/networks.d.ts.map +1 -0
- package/dist/config/networks.js +51 -0
- package/dist/config/networks.js.map +1 -0
- package/dist/config/tokens.d.ts +19 -0
- package/dist/config/tokens.d.ts.map +1 -0
- package/dist/config/tokens.js +43 -0
- package/dist/config/tokens.js.map +1 -0
- package/dist/idl/cubic_pool.json +4203 -0
- package/dist/idl/index.d.ts +1769 -0
- package/dist/idl/index.d.ts.map +1 -0
- package/dist/idl/index.js +18 -0
- package/dist/idl/index.js.map +1 -0
- package/dist/idl/protocol_admin.json +2615 -0
- package/dist/idl/single_token_liquidity.json +1121 -0
- package/dist/index.d.ts +75 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +206 -0
- package/dist/index.js.map +1 -0
- package/dist/math/cubicMath.d.ts +40 -0
- package/dist/math/cubicMath.d.ts.map +1 -0
- package/dist/math/cubicMath.js +82 -0
- package/dist/math/cubicMath.js.map +1 -0
- package/dist/math/fixedPoint.d.ts +19 -0
- package/dist/math/fixedPoint.d.ts.map +1 -0
- package/dist/math/fixedPoint.js +86 -0
- package/dist/math/fixedPoint.js.map +1 -0
- package/dist/math/index.d.ts +9 -0
- package/dist/math/index.d.ts.map +1 -0
- package/dist/math/index.js +30 -0
- package/dist/math/index.js.map +1 -0
- package/dist/math/logExp.d.ts +6 -0
- package/dist/math/logExp.d.ts.map +1 -0
- package/dist/math/logExp.js +68 -0
- package/dist/math/logExp.js.map +1 -0
- package/dist/math/lpLoss.d.ts +44 -0
- package/dist/math/lpLoss.d.ts.map +1 -0
- package/dist/math/lpLoss.js +103 -0
- package/dist/math/lpLoss.js.map +1 -0
- package/dist/math/maxSelloff.d.ts +164 -0
- package/dist/math/maxSelloff.d.ts.map +1 -0
- package/dist/math/maxSelloff.js +397 -0
- package/dist/math/maxSelloff.js.map +1 -0
- package/dist/math/singleToken.d.ts +54 -0
- package/dist/math/singleToken.d.ts.map +1 -0
- package/dist/math/singleToken.js +188 -0
- package/dist/math/singleToken.js.map +1 -0
- package/dist/math/slippage.d.ts +13 -0
- package/dist/math/slippage.d.ts.map +1 -0
- package/dist/math/slippage.js +44 -0
- package/dist/math/slippage.js.map +1 -0
- package/dist/math/surgeFee.d.ts +24 -0
- package/dist/math/surgeFee.d.ts.map +1 -0
- package/dist/math/surgeFee.js +85 -0
- package/dist/math/surgeFee.js.map +1 -0
- package/dist/math/weightedMath.d.ts +20 -0
- package/dist/math/weightedMath.d.ts.map +1 -0
- package/dist/math/weightedMath.js +66 -0
- package/dist/math/weightedMath.js.map +1 -0
- package/dist/parsers/borsh.d.ts +28 -0
- package/dist/parsers/borsh.d.ts.map +1 -0
- package/dist/parsers/borsh.js +104 -0
- package/dist/parsers/borsh.js.map +1 -0
- package/dist/parsers/contracts.d.ts +26 -0
- package/dist/parsers/contracts.d.ts.map +1 -0
- package/dist/parsers/contracts.js +62 -0
- package/dist/parsers/contracts.js.map +1 -0
- package/dist/parsers/events.d.ts +3 -0
- package/dist/parsers/events.d.ts.map +1 -0
- package/dist/parsers/events.js +281 -0
- package/dist/parsers/events.js.map +1 -0
- package/dist/parsers/index.d.ts +6 -0
- package/dist/parsers/index.d.ts.map +1 -0
- package/dist/parsers/index.js +22 -0
- package/dist/parsers/index.js.map +1 -0
- package/dist/parsers/mintAccount.d.ts +24 -0
- package/dist/parsers/mintAccount.d.ts.map +1 -0
- package/dist/parsers/mintAccount.js +27 -0
- package/dist/parsers/mintAccount.js.map +1 -0
- package/dist/parsers/poolAccount.d.ts +141 -0
- package/dist/parsers/poolAccount.d.ts.map +1 -0
- package/dist/parsers/poolAccount.js +224 -0
- package/dist/parsers/poolAccount.js.map +1 -0
- package/dist/types/contracts.d.ts +1340 -0
- package/dist/types/contracts.d.ts.map +1 -0
- package/dist/types/contracts.js +3 -0
- package/dist/types/contracts.js.map +1 -0
- package/dist/types/events.d.ts +172 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/events.js +3 -0
- package/dist/types/events.js.map +1 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +22 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/pool.d.ts +149 -0
- package/dist/types/pool.d.ts.map +1 -0
- package/dist/types/pool.js +3 -0
- package/dist/types/pool.js.map +1 -0
- package/dist/types/result.d.ts +24 -0
- package/dist/types/result.d.ts.map +1 -0
- package/dist/types/result.js +11 -0
- package/dist/types/result.js.map +1 -0
- package/dist/types/tx.d.ts +261 -0
- package/dist/types/tx.d.ts.map +1 -0
- package/dist/types/tx.js +3 -0
- package/dist/types/tx.js.map +1 -0
- package/dist/utils/errors.d.ts +44 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +192 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/extensions.d.ts +98 -0
- package/dist/utils/extensions.d.ts.map +1 -0
- package/dist/utils/extensions.js +187 -0
- package/dist/utils/extensions.js.map +1 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +21 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/pda.d.ts +8 -0
- package/dist/utils/pda.d.ts.map +1 -0
- package/dist/utils/pda.js +27 -0
- package/dist/utils/pda.js.map +1 -0
- package/dist/utils/retry.d.ts +22 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +62 -0
- package/dist/utils/retry.js.map +1 -0
- package/package.json +54 -0
- package/src/clients/AdminClient.ts +608 -0
- package/src/clients/CofferBackendClient.ts +1413 -0
- package/src/clients/CubicPoolClient.ts +719 -0
- package/src/clients/PoolFactoryClient.ts +110 -0
- package/src/clients/RpcClient.ts +195 -0
- package/src/clients/SingleTokenDepositClient.ts +143 -0
- package/src/clients/contract-instructions.ts +115 -0
- package/src/clients/index.ts +9 -0
- package/src/clients/quote-math.ts +81 -0
- package/src/clients/tx-builders.ts +1096 -0
- package/src/clients/versioned.ts +80 -0
- package/src/config/index.ts +111 -0
- package/src/config/networks.ts +71 -0
- package/src/config/tokens.ts +52 -0
- package/src/idl/cubic_pool.json +4203 -0
- package/src/idl/index.ts +13 -0
- package/src/idl/protocol_admin.json +2615 -0
- package/src/idl/single_token_liquidity.json +1121 -0
- package/src/index.ts +262 -0
- package/src/math/cubicMath.ts +92 -0
- package/src/math/fixedPoint.ts +71 -0
- package/src/math/index.ts +13 -0
- package/src/math/logExp.ts +61 -0
- package/src/math/lpLoss.ts +160 -0
- package/src/math/maxSelloff.ts +564 -0
- package/src/math/singleToken.ts +251 -0
- package/src/math/slippage.ts +38 -0
- package/src/math/surgeFee.ts +101 -0
- package/src/math/weightedMath.ts +64 -0
- package/src/parsers/borsh.ts +106 -0
- package/src/parsers/contracts.ts +64 -0
- package/src/parsers/events.ts +275 -0
- package/src/parsers/index.ts +5 -0
- package/src/parsers/mintAccount.ts +44 -0
- package/src/parsers/poolAccount.ts +372 -0
- package/src/types/contracts.ts +238 -0
- package/src/types/events.ts +198 -0
- package/src/types/index.ts +5 -0
- package/src/types/pool.ts +147 -0
- package/src/types/result.ts +51 -0
- package/src/types/tx.ts +278 -0
- package/src/utils/errors.ts +210 -0
- package/src/utils/extensions.ts +196 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/pda.ts +58 -0
- 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
|
+
}
|
package/src/utils/pda.ts
ADDED
|
@@ -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
|
+
}
|