@liquid-af/sdk 0.9.1 → 0.10.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liquid-af/sdk",
3
- "version": "0.9.1",
3
+ "version": "0.10.0",
4
4
  "description": "TypeScript SDK for the Liquid DeFi protocol on Solana",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -79,16 +79,16 @@
79
79
  "src"
80
80
  ],
81
81
  "scripts": {
82
- "build": "yarn copy-idl && tsc",
82
+ "build": "bun run copy-idl && tsc",
83
83
  "copy-idl": "cp ../../target/idl/*.json src/idl/ && cp ../../target/types/*.ts src/idl/",
84
84
  "clean": "rm -rf dist",
85
- "prebuild": "yarn clean",
86
- "prepack": "yarn build",
87
- "prepublishOnly": "yarn test:build",
85
+ "prebuild": "bun run clean",
86
+ "prepack": "bun run build",
87
+ "prepublishOnly": "bun run test:build",
88
88
  "test:build": "tsc --noEmit",
89
- "version:patch": "yarn version --patch --no-git-tag-version",
90
- "version:minor": "yarn version --minor --no-git-tag-version",
91
- "version:major": "yarn version --major --no-git-tag-version",
89
+ "version:patch": "bun run version --patch --no-git-tag-version",
90
+ "version:minor": "bun run version --minor --no-git-tag-version",
91
+ "version:major": "bun run version --major --no-git-tag-version",
92
92
  "docs": "typedoc",
93
93
  "publish:test": "npm publish --dry-run",
94
94
  "publish:prod": "npm publish"
@@ -40,9 +40,5 @@ export type {
40
40
  LiquidEventName,
41
41
  } from "./types.js";
42
42
 
43
- export {
44
- parseTransactionEvents,
45
- addEventListener,
46
- waitForEvent,
47
- } from "./parser.js";
43
+ export { parseTransactionEvents, waitForEvent } from "./parser.js";
48
44
  export type { ParsedEvent } from "./parser.js";
@@ -1,4 +1,9 @@
1
- import { BorshCoder, type Program } from "@coral-xyz/anchor";
1
+ import { BorshCoder, type Idl, type Program, utils } from "@coral-xyz/anchor";
2
+ import {
3
+ type TransactionResponse,
4
+ type VersionedTransactionResponse,
5
+ PublicKey
6
+ } from "@solana/web3.js";
2
7
  import type { LiquidEvents } from "../idl/liquid_events.js";
3
8
  import type { LiquidEventMap, LiquidEventName } from "./types.js";
4
9
 
@@ -8,59 +13,111 @@ export interface ParsedEvent<T = unknown> {
8
13
  data: T;
9
14
  }
10
15
 
11
- const PROGRAM_DATA = "Program data: ";
12
- const PROGRAM_LOG = "Program log: ";
13
- const INVOKE_RE = /^Program ([1-9A-HJ-NP-Za-km-z]+) invoke \[(\d+)\]$/;
14
- const EXIT_RE = /^Program ([1-9A-HJ-NP-Za-km-z]+) (success|failed)/;
16
+ /**
17
+ * Maps IDL instruction names (snake_case) to LiquidEventMap keys (camelCase).
18
+ * Anchor's BorshCoder.instruction.decode() returns camelCase names, so this
19
+ * maps those decoded names to our event type keys.
20
+ */
21
+ const INSTRUCTION_TO_EVENT: Record<string, LiquidEventName> = {
22
+ logTokenCreated: "tokenCreated",
23
+ logTrade: "tradeEvent",
24
+ logMigrationCompleted: "migrationCompleted",
25
+ logCurveCompleted: "curveCompleted",
26
+ logCurveBuyback: "curveBuybackExecuted",
27
+ logReferralWithdrawn: "referralRewardsWithdrawn",
28
+ logCurvePaused: "curveProtocolPaused",
29
+ logCurveUnpaused: "curveProtocolUnpaused",
30
+ logPoolCreated: "poolCreated",
31
+ logSwap: "swapEvent",
32
+ logLpChange: "lpChangeEvent",
33
+ logAmmBuyback: "ammBuybackExecuted",
34
+ logAmmPaused: "ammProtocolPaused",
35
+ logAmmUnpaused: "ammProtocolUnpaused",
36
+ logFeesDistributed: "feesDistributed",
37
+ logFeesClaimed: "feesClaimed",
38
+ logFeeConfigRevoked: "feeConfigRevoked",
39
+ logFeeConfigUpdated: "feeConfigUpdated",
40
+ logFeeConfigAuthorityOverridden: "feeConfigAuthorityOverridden",
41
+ logDealCreated: "dealCreated",
42
+ logDealUpdated: "dealUpdated",
43
+ logDealClosed: "dealClosed",
44
+ logDealRedeemed: "dealRedeemed",
45
+ logSnapshotTaken: "snapshotTaken",
46
+ logUserUpdated: "userUpdatedByAdmin",
47
+ logReferrerSet: "referrerSet",
48
+ logCashbackMode: "cashbackModeActivated",
49
+ logCashbackEarned: "cashbackEarned",
50
+ logCashbackSpent: "cashbackSpent",
51
+ };
15
52
 
16
53
  /**
17
- * Parses events from transaction logs for the liquid-events program.
18
- * Unlike Anchor's built-in EventParser, this correctly identifies events
19
- * from programs invoked via CPI (not just top-level invocations).
54
+ * Parses events from a transaction by inspecting inner instructions
55
+ * (CPI calls to the liquid-events program) instead of transaction logs.
20
56
  *
21
- * @param logs - Transaction log messages
57
+ * This approach is immune to log bleaching attacks since event data is
58
+ * encoded in the instruction data of CPI calls, not in transaction logs.
59
+ *
60
+ * @param transaction - Full transaction response (from getTransaction)
22
61
  * @param program - The liquid-events program instance
23
62
  * @returns Array of parsed events
24
63
  */
25
64
  export const parseTransactionEvents = (
26
- logs: string[],
65
+ transaction:
66
+ | TransactionResponse
67
+ | VersionedTransactionResponse
68
+ | null
69
+ | undefined,
27
70
  program: Program<LiquidEvents>,
28
71
  ): ParsedEvent[] => {
29
- return parseCpiLogs(
30
- logs,
31
- program.programId.toString(),
32
- new BorshCoder(program.idl),
33
- );
34
- };
72
+ if (!transaction?.meta?.innerInstructions) return [];
35
73
 
36
- /**
37
- * Registers an event listener on the liquid-events program.
38
- * Returns the listener ID (use program.removeEventListener to unsubscribe).
39
- *
40
- * @param program - The liquid-events program instance
41
- * @param eventName - Name of the event to listen for
42
- * @param callback - Handler called when event is received
43
- * @returns Listener ID for removal
44
- */
45
- export const addEventListener = <N extends LiquidEventName>(
46
- program: Program<LiquidEvents>,
47
- eventName: N,
48
- callback: (event: LiquidEventMap[N]) => void,
49
- ): number => {
50
- // Cast needed: Anchor's IdlEvents types differ structurally from our
51
- // hand-written interfaces (e.g. enum variant wrapping), but the runtime
52
- // data is identical.
53
- return program.addEventListener(eventName, (data: unknown) => {
54
- callback(data as LiquidEventMap[N]);
55
- });
74
+ const coder = new BorshCoder(program.idl as Idl);
75
+ const programId = program.programId;
76
+ const events: ParsedEvent[] = [];
77
+
78
+ // Get account keys from the transaction message
79
+ const message = transaction.transaction.message;
80
+ const accountKeys: PublicKey[] =
81
+ "staticAccountKeys" in message
82
+ ? (message.staticAccountKeys as PublicKey[])
83
+ : (message as unknown as { accountKeys: PublicKey[] }).accountKeys;
84
+
85
+ for (const innerIxGroup of transaction.meta.innerInstructions) {
86
+ for (const innerIx of innerIxGroup.instructions) {
87
+ // Check if this inner instruction targets the events program
88
+ const ixProgramKey = accountKeys[innerIx.programIdIndex];
89
+ if (!ixProgramKey || !programId.equals(new PublicKey(ixProgramKey)))
90
+ continue;
91
+
92
+ // Decode the instruction data (base58-encoded in RPC responses)
93
+ const rawData = utils.bytes.bs58.decode(innerIx.data);
94
+ if (rawData.length < 8) continue;
95
+
96
+ // Use Anchor's instruction coder to decode
97
+ const decoded = coder.instruction.decode(Buffer.from(rawData));
98
+ if (!decoded) continue;
99
+
100
+ // Map instruction name to event name
101
+ const eventName = INSTRUCTION_TO_EVENT[decoded.name];
102
+ if (!eventName) continue;
103
+
104
+ // The instruction has a single "data" argument containing the event struct
105
+ events.push({
106
+ name: eventName,
107
+ data: (decoded.data as Record<string, unknown>).data,
108
+ });
109
+ }
110
+ }
111
+
112
+ return events;
56
113
  };
57
114
 
58
115
  /**
59
- * Subscribes to logs via WebSocket, fires a trigger, and polls until the
60
- * event arrives or the timeout is exceeded.
116
+ * Subscribes to logs to capture the transaction signature, fires a trigger,
117
+ * then fetches the full transaction to parse events from inner instructions.
61
118
  *
62
- * Uses CPI-aware log parsing so events emitted by programs that are only
63
- * invoked via CPI (e.g., the centralized events program) are correctly detected.
119
+ * Immune to log bleaching: event data is decoded from CPI instruction data,
120
+ * not from transaction logs.
64
121
  *
65
122
  * @param program - The liquid-events program instance
66
123
  * @param eventName - Name of the event to wait for
@@ -69,95 +126,68 @@ export const addEventListener = <N extends LiquidEventName>(
69
126
  * @returns Object containing the trigger result and the captured event data
70
127
  * @throws If the event is not received within the timeout period
71
128
  */
72
- export async function waitForEvent<N extends LiquidEventName, TResult = string>(
129
+ export async function waitForEvent<
130
+ N extends LiquidEventName,
131
+ TResult = string,
132
+ >(
73
133
  program: Program<LiquidEvents>,
74
134
  eventName: N,
75
135
  trigger: () => Promise<TResult>,
76
136
  timeoutMs = 5000,
77
137
  ): Promise<{ result: TResult; event: LiquidEventMap[N] }> {
78
- let eventData: LiquidEventMap[N] | null = null;
79
- const programId = program.programId.toString();
80
- const coder = new BorshCoder(program.idl);
138
+ const connection = program.provider.connection;
139
+ let capturedSignature: string | null = null;
81
140
 
82
- // Subscribe to logs mentioning this program (includes CPI invocations)
83
- const subscriptionId = program.provider.connection.onLogs(
141
+ // Subscribe to logs to capture the transaction signature
142
+ // (invoke/exit logs are always present even without emit!)
143
+ const subscriptionId = connection.onLogs(
84
144
  program.programId,
85
- (logs) => {
86
- if (logs.err) return;
87
- const events = parseCpiLogs(logs.logs, programId, coder);
88
- for (const event of events) {
89
- if (event.name === eventName) {
90
- eventData = event.data as LiquidEventMap[N];
91
- }
92
- }
145
+ (logInfo) => {
146
+ if (logInfo.err) return;
147
+ capturedSignature = logInfo.signature;
93
148
  },
94
149
  );
95
150
 
96
151
  try {
97
152
  const result = await trigger();
153
+
154
+ // If the trigger returned a string, use it as the signature directly
155
+ if (typeof result === "string") {
156
+ capturedSignature = result;
157
+ }
158
+
159
+ // Wait for the signature to be captured
98
160
  const start = Date.now();
99
- while (!eventData && Date.now() - start < timeoutMs) {
161
+ while (!capturedSignature && Date.now() - start < timeoutMs) {
100
162
  await new Promise((r) => setTimeout(r, 200));
101
163
  }
102
- if (!eventData) {
164
+ if (!capturedSignature) {
103
165
  throw new Error(
104
- `Event '${eventName}' not received within ${timeoutMs}ms`,
166
+ `No transaction signature captured within ${timeoutMs}ms`,
105
167
  );
106
168
  }
107
- return { result, event: eventData };
108
- } finally {
109
- await program.provider.connection.removeOnLogsListener(subscriptionId);
110
- }
111
- }
112
-
113
- /**
114
- * Parses events from transaction logs, properly handling CPI contexts.
115
- *
116
- * Anchor's built-in EventParser pushes a generic "cpi" string onto its
117
- * execution stack for nested invocations, so it cannot identify events
118
- * emitted by a CPI-only program. This parser extracts the actual program
119
- * ID from each `invoke` log line, enabling correct attribution of events
120
- * to programs at any CPI depth.
121
- */
122
- function parseCpiLogs(
123
- logs: string[],
124
- programId: string,
125
- coder: BorshCoder,
126
- ): ParsedEvent[] {
127
- const events: ParsedEvent[] = [];
128
- const stack: string[] = [];
129
169
 
130
- for (const log of logs) {
131
- if (!log.startsWith("Program ")) continue;
132
-
133
- const invokeMatch = INVOKE_RE.exec(log);
134
- if (invokeMatch) {
135
- stack.push(invokeMatch[1]);
136
- continue;
170
+ // Fetch the full transaction to get inner instructions
171
+ const tx = await connection.getTransaction(capturedSignature, {
172
+ commitment: "confirmed",
173
+ maxSupportedTransactionVersion: 0,
174
+ });
175
+ if (!tx) {
176
+ throw new Error(`Transaction not found: ${capturedSignature}`);
137
177
  }
138
178
 
139
- const exitMatch = EXIT_RE.exec(log);
140
- if (exitMatch) {
141
- stack.pop();
142
- continue;
179
+ // Parse events from inner instructions
180
+ const events = parseTransactionEvents(tx, program);
181
+ const event = events.find((e) => e.name === eventName);
182
+ if (!event) {
183
+ throw new Error(
184
+ `Event '${eventName}' not found in transaction ${capturedSignature}. ` +
185
+ `Found events: [${events.map((e) => e.name).join(", ")}]`,
186
+ );
143
187
  }
144
188
 
145
- // Only try to decode events when inside the target program's context
146
- if (stack.length > 0 && stack[stack.length - 1] === programId) {
147
- let logStr: string | null = null;
148
- if (log.startsWith(PROGRAM_DATA)) {
149
- logStr = log.slice(PROGRAM_DATA.length);
150
- } else if (log.startsWith(PROGRAM_LOG)) {
151
- logStr = log.slice(PROGRAM_LOG.length);
152
- }
153
- if (logStr) {
154
- const event = coder.events.decode(logStr);
155
- if (event) {
156
- events.push({ name: event.name, data: event.data });
157
- }
158
- }
159
- }
189
+ return { result, event: event.data as LiquidEventMap[N] };
190
+ } finally {
191
+ await connection.removeOnLogsListener(subscriptionId);
160
192
  }
161
-
162
- return events;
163
193
  }
@@ -1097,6 +1097,13 @@
1097
1097
  "Liquid events program for centralized event emission."
1098
1098
  ],
1099
1099
  "address": "EventsQeXA43nLKR69DhHwNsJ6aM512AweMCgG3wG8zG"
1100
+ },
1101
+ {
1102
+ "name": "instructions",
1103
+ "docs": [
1104
+ "Instructions sysvar for CPI caller detection."
1105
+ ],
1106
+ "address": "Sysvar1nstructions1111111111111111111111111"
1100
1107
  }
1101
1108
  ],
1102
1109
  "args": [
@@ -2108,6 +2115,13 @@
2108
2115
  "Liquid events program for centralized event emission."
2109
2116
  ],
2110
2117
  "address": "EventsQeXA43nLKR69DhHwNsJ6aM512AweMCgG3wG8zG"
2118
+ },
2119
+ {
2120
+ "name": "instructions",
2121
+ "docs": [
2122
+ "Instructions sysvar for CPI caller detection."
2123
+ ],
2124
+ "address": "Sysvar1nstructions1111111111111111111111111"
2111
2125
  }
2112
2126
  ],
2113
2127
  "args": [
@@ -3027,6 +3041,13 @@
3027
3041
  "Liquid events program for centralized event emission."
3028
3042
  ],
3029
3043
  "address": "EventsQeXA43nLKR69DhHwNsJ6aM512AweMCgG3wG8zG"
3044
+ },
3045
+ {
3046
+ "name": "instructions",
3047
+ "docs": [
3048
+ "Instructions sysvar for CPI caller detection."
3049
+ ],
3050
+ "address": "Sysvar1nstructions1111111111111111111111111"
3030
3051
  }
3031
3052
  ],
3032
3053
  "args": [
@@ -4038,6 +4059,13 @@
4038
4059
  "Liquid events program for centralized event emission."
4039
4060
  ],
4040
4061
  "address": "EventsQeXA43nLKR69DhHwNsJ6aM512AweMCgG3wG8zG"
4062
+ },
4063
+ {
4064
+ "name": "instructions",
4065
+ "docs": [
4066
+ "Instructions sysvar for CPI caller detection."
4067
+ ],
4068
+ "address": "Sysvar1nstructions1111111111111111111111111"
4041
4069
  }
4042
4070
  ],
4043
4071
  "args": [
@@ -9244,6 +9272,13 @@
9244
9272
  "Liquid events program for centralized event emission."
9245
9273
  ],
9246
9274
  "address": "EventsQeXA43nLKR69DhHwNsJ6aM512AweMCgG3wG8zG"
9275
+ },
9276
+ {
9277
+ "name": "instructions",
9278
+ "docs": [
9279
+ "Instructions sysvar for CPI caller detection."
9280
+ ],
9281
+ "address": "Sysvar1nstructions1111111111111111111111111"
9247
9282
  }
9248
9283
  ],
9249
9284
  "args": [
@@ -10255,6 +10290,13 @@
10255
10290
  "Liquid events program for centralized event emission."
10256
10291
  ],
10257
10292
  "address": "EventsQeXA43nLKR69DhHwNsJ6aM512AweMCgG3wG8zG"
10293
+ },
10294
+ {
10295
+ "name": "instructions",
10296
+ "docs": [
10297
+ "Instructions sysvar for CPI caller detection."
10298
+ ],
10299
+ "address": "Sysvar1nstructions1111111111111111111111111"
10258
10300
  }
10259
10301
  ],
10260
10302
  "args": [
@@ -11174,6 +11216,13 @@
11174
11216
  "Liquid events program for centralized event emission."
11175
11217
  ],
11176
11218
  "address": "EventsQeXA43nLKR69DhHwNsJ6aM512AweMCgG3wG8zG"
11219
+ },
11220
+ {
11221
+ "name": "instructions",
11222
+ "docs": [
11223
+ "Instructions sysvar for CPI caller detection."
11224
+ ],
11225
+ "address": "Sysvar1nstructions1111111111111111111111111"
11177
11226
  }
11178
11227
  ],
11179
11228
  "args": [
@@ -12185,6 +12234,13 @@
12185
12234
  "Liquid events program for centralized event emission."
12186
12235
  ],
12187
12236
  "address": "EventsQeXA43nLKR69DhHwNsJ6aM512AweMCgG3wG8zG"
12237
+ },
12238
+ {
12239
+ "name": "instructions",
12240
+ "docs": [
12241
+ "Instructions sysvar for CPI caller detection."
12242
+ ],
12243
+ "address": "Sysvar1nstructions1111111111111111111111111"
12188
12244
  }
12189
12245
  ],
12190
12246
  "args": [
@@ -13135,6 +13191,11 @@
13135
13191
  "code": 6045,
13136
13192
  "name": "BuybackVaultNotDrained",
13137
13193
  "msg": "Buyback vault must be drained before migration"
13194
+ },
13195
+ {
13196
+ "code": 6046,
13197
+ "name": "CallerBlacklisted",
13198
+ "msg": "CPI caller is blacklisted"
13138
13199
  }
13139
13200
  ],
13140
13201
  "types": [
package/src/idl/liquid.ts CHANGED
@@ -1103,6 +1103,13 @@ export type Liquid = {
1103
1103
  "Liquid events program for centralized event emission."
1104
1104
  ],
1105
1105
  "address": "EventsQeXA43nLKR69DhHwNsJ6aM512AweMCgG3wG8zG"
1106
+ },
1107
+ {
1108
+ "name": "instructions",
1109
+ "docs": [
1110
+ "Instructions sysvar for CPI caller detection."
1111
+ ],
1112
+ "address": "Sysvar1nstructions1111111111111111111111111"
1106
1113
  }
1107
1114
  ],
1108
1115
  "args": [
@@ -2114,6 +2121,13 @@ export type Liquid = {
2114
2121
  "Liquid events program for centralized event emission."
2115
2122
  ],
2116
2123
  "address": "EventsQeXA43nLKR69DhHwNsJ6aM512AweMCgG3wG8zG"
2124
+ },
2125
+ {
2126
+ "name": "instructions",
2127
+ "docs": [
2128
+ "Instructions sysvar for CPI caller detection."
2129
+ ],
2130
+ "address": "Sysvar1nstructions1111111111111111111111111"
2117
2131
  }
2118
2132
  ],
2119
2133
  "args": [
@@ -3033,6 +3047,13 @@ export type Liquid = {
3033
3047
  "Liquid events program for centralized event emission."
3034
3048
  ],
3035
3049
  "address": "EventsQeXA43nLKR69DhHwNsJ6aM512AweMCgG3wG8zG"
3050
+ },
3051
+ {
3052
+ "name": "instructions",
3053
+ "docs": [
3054
+ "Instructions sysvar for CPI caller detection."
3055
+ ],
3056
+ "address": "Sysvar1nstructions1111111111111111111111111"
3036
3057
  }
3037
3058
  ],
3038
3059
  "args": [
@@ -4044,6 +4065,13 @@ export type Liquid = {
4044
4065
  "Liquid events program for centralized event emission."
4045
4066
  ],
4046
4067
  "address": "EventsQeXA43nLKR69DhHwNsJ6aM512AweMCgG3wG8zG"
4068
+ },
4069
+ {
4070
+ "name": "instructions",
4071
+ "docs": [
4072
+ "Instructions sysvar for CPI caller detection."
4073
+ ],
4074
+ "address": "Sysvar1nstructions1111111111111111111111111"
4047
4075
  }
4048
4076
  ],
4049
4077
  "args": [
@@ -9250,6 +9278,13 @@ export type Liquid = {
9250
9278
  "Liquid events program for centralized event emission."
9251
9279
  ],
9252
9280
  "address": "EventsQeXA43nLKR69DhHwNsJ6aM512AweMCgG3wG8zG"
9281
+ },
9282
+ {
9283
+ "name": "instructions",
9284
+ "docs": [
9285
+ "Instructions sysvar for CPI caller detection."
9286
+ ],
9287
+ "address": "Sysvar1nstructions1111111111111111111111111"
9253
9288
  }
9254
9289
  ],
9255
9290
  "args": [
@@ -10261,6 +10296,13 @@ export type Liquid = {
10261
10296
  "Liquid events program for centralized event emission."
10262
10297
  ],
10263
10298
  "address": "EventsQeXA43nLKR69DhHwNsJ6aM512AweMCgG3wG8zG"
10299
+ },
10300
+ {
10301
+ "name": "instructions",
10302
+ "docs": [
10303
+ "Instructions sysvar for CPI caller detection."
10304
+ ],
10305
+ "address": "Sysvar1nstructions1111111111111111111111111"
10264
10306
  }
10265
10307
  ],
10266
10308
  "args": [
@@ -11180,6 +11222,13 @@ export type Liquid = {
11180
11222
  "Liquid events program for centralized event emission."
11181
11223
  ],
11182
11224
  "address": "EventsQeXA43nLKR69DhHwNsJ6aM512AweMCgG3wG8zG"
11225
+ },
11226
+ {
11227
+ "name": "instructions",
11228
+ "docs": [
11229
+ "Instructions sysvar for CPI caller detection."
11230
+ ],
11231
+ "address": "Sysvar1nstructions1111111111111111111111111"
11183
11232
  }
11184
11233
  ],
11185
11234
  "args": [
@@ -12191,6 +12240,13 @@ export type Liquid = {
12191
12240
  "Liquid events program for centralized event emission."
12192
12241
  ],
12193
12242
  "address": "EventsQeXA43nLKR69DhHwNsJ6aM512AweMCgG3wG8zG"
12243
+ },
12244
+ {
12245
+ "name": "instructions",
12246
+ "docs": [
12247
+ "Instructions sysvar for CPI caller detection."
12248
+ ],
12249
+ "address": "Sysvar1nstructions1111111111111111111111111"
12194
12250
  }
12195
12251
  ],
12196
12252
  "args": [
@@ -13141,6 +13197,11 @@ export type Liquid = {
13141
13197
  "code": 6045,
13142
13198
  "name": "buybackVaultNotDrained",
13143
13199
  "msg": "Buyback vault must be drained before migration"
13200
+ },
13201
+ {
13202
+ "code": 6046,
13203
+ "name": "callerBlacklisted",
13204
+ "msg": "CPI caller is blacklisted"
13144
13205
  }
13145
13206
  ],
13146
13207
  "types": [