@exponent-labs/exponent-sdk 0.9.0 → 0.9.2
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/build/client/vaults/index.d.ts +2 -0
- package/build/client/vaults/index.js +2 -0
- package/build/client/vaults/index.js.map +1 -1
- package/build/client/vaults/types/index.d.ts +2 -0
- package/build/client/vaults/types/index.js +2 -0
- package/build/client/vaults/types/index.js.map +1 -1
- package/build/client/vaults/types/kaminoFarmEntry.d.ts +15 -0
- package/build/client/vaults/types/kaminoFarmEntry.js +17 -0
- package/build/client/vaults/types/kaminoFarmEntry.js.map +1 -0
- package/build/client/vaults/types/kaminoObligationEntry.d.ts +21 -4
- package/build/client/vaults/types/kaminoObligationEntry.js +2 -1
- package/build/client/vaults/types/kaminoObligationEntry.js.map +1 -1
- package/build/client/vaults/types/positionUpdate.d.ts +9 -0
- package/build/client/vaults/types/positionUpdate.js +23 -0
- package/build/client/vaults/types/positionUpdate.js.map +1 -1
- package/build/client/vaults/types/proposalAction.js +0 -3
- package/build/client/vaults/types/proposalAction.js.map +1 -1
- package/build/client/vaults/types/reserveFarmMapping.d.ts +19 -0
- package/build/client/vaults/types/reserveFarmMapping.js +18 -0
- package/build/client/vaults/types/reserveFarmMapping.js.map +1 -0
- package/build/client/vaults/types/strategyPosition.d.ts +5 -0
- package/build/client/vaults/types/strategyPosition.js +5 -0
- package/build/client/vaults/types/strategyPosition.js.map +1 -1
- package/build/exponentVaults/aumCalculator.d.ts +25 -4
- package/build/exponentVaults/aumCalculator.js +236 -15
- package/build/exponentVaults/aumCalculator.js.map +1 -1
- package/build/exponentVaults/fetcher.d.ts +52 -0
- package/build/exponentVaults/fetcher.js +199 -0
- package/build/exponentVaults/fetcher.js.map +1 -0
- package/build/exponentVaults/index.d.ts +10 -9
- package/build/exponentVaults/index.js +26 -8
- package/build/exponentVaults/index.js.map +1 -1
- package/build/exponentVaults/kamino-farms.d.ts +144 -0
- package/build/exponentVaults/kamino-farms.js +396 -0
- package/build/exponentVaults/kamino-farms.js.map +1 -0
- package/build/exponentVaults/loopscale/client.d.ts +240 -0
- package/build/exponentVaults/loopscale/client.js +590 -0
- package/build/exponentVaults/loopscale/client.js.map +1 -0
- package/build/exponentVaults/loopscale/client.test.d.ts +1 -0
- package/build/exponentVaults/loopscale/client.test.js +183 -0
- package/build/exponentVaults/loopscale/client.test.js.map +1 -0
- package/build/exponentVaults/loopscale/helpers.d.ts +29 -0
- package/build/exponentVaults/loopscale/helpers.js +119 -0
- package/build/exponentVaults/loopscale/helpers.js.map +1 -0
- package/build/exponentVaults/loopscale/index.d.ts +3 -0
- package/build/exponentVaults/loopscale/index.js +12 -0
- package/build/exponentVaults/loopscale/index.js.map +1 -0
- package/build/exponentVaults/loopscale/prepared-transactions.d.ts +13 -0
- package/build/exponentVaults/loopscale/prepared-transactions.js +271 -0
- package/build/exponentVaults/loopscale/prepared-transactions.js.map +1 -0
- package/build/exponentVaults/loopscale/prepared-transactions.test.d.ts +1 -0
- package/build/exponentVaults/loopscale/prepared-transactions.test.js +400 -0
- package/build/exponentVaults/loopscale/prepared-transactions.test.js.map +1 -0
- package/build/exponentVaults/loopscale/prepared-types.d.ts +62 -0
- package/build/exponentVaults/loopscale/prepared-types.js +3 -0
- package/build/exponentVaults/loopscale/prepared-types.js.map +1 -0
- package/build/exponentVaults/loopscale/response-plan.d.ts +69 -0
- package/build/exponentVaults/loopscale/response-plan.js +141 -0
- package/build/exponentVaults/loopscale/response-plan.js.map +1 -0
- package/build/exponentVaults/loopscale/response-plan.test.d.ts +1 -0
- package/build/exponentVaults/loopscale/response-plan.test.js +139 -0
- package/build/exponentVaults/loopscale/response-plan.test.js.map +1 -0
- package/build/exponentVaults/loopscale/send-plan.d.ts +75 -0
- package/build/exponentVaults/loopscale/send-plan.js +235 -0
- package/build/exponentVaults/loopscale/send-plan.js.map +1 -0
- package/build/exponentVaults/loopscale/types.d.ts +443 -0
- package/build/exponentVaults/loopscale/types.js +3 -0
- package/build/exponentVaults/loopscale/types.js.map +1 -0
- package/build/exponentVaults/loopscale-client.d.ts +113 -524
- package/build/exponentVaults/loopscale-client.js +296 -539
- package/build/exponentVaults/loopscale-client.js.map +1 -1
- package/build/exponentVaults/loopscale-client.test.d.ts +1 -0
- package/build/exponentVaults/loopscale-client.test.js +162 -0
- package/build/exponentVaults/loopscale-client.test.js.map +1 -0
- package/build/exponentVaults/loopscale-client.types.d.ts +425 -0
- package/build/exponentVaults/loopscale-client.types.js +3 -0
- package/build/exponentVaults/loopscale-client.types.js.map +1 -0
- package/build/exponentVaults/loopscale-execution.d.ts +125 -0
- package/build/exponentVaults/loopscale-execution.js +341 -0
- package/build/exponentVaults/loopscale-execution.js.map +1 -0
- package/build/exponentVaults/loopscale-execution.test.d.ts +1 -0
- package/build/exponentVaults/loopscale-execution.test.js +139 -0
- package/build/exponentVaults/loopscale-execution.test.js.map +1 -0
- package/build/exponentVaults/loopscale-vault.d.ts +115 -0
- package/build/exponentVaults/loopscale-vault.js +275 -0
- package/build/exponentVaults/loopscale-vault.js.map +1 -0
- package/build/exponentVaults/loopscale-vault.test.d.ts +1 -0
- package/build/exponentVaults/loopscale-vault.test.js +102 -0
- package/build/exponentVaults/loopscale-vault.test.js.map +1 -0
- package/build/exponentVaults/policyBuilders.d.ts +62 -0
- package/build/exponentVaults/policyBuilders.js +119 -2
- package/build/exponentVaults/policyBuilders.js.map +1 -1
- package/build/exponentVaults/pricePathResolver.d.ts +45 -0
- package/build/exponentVaults/pricePathResolver.js +198 -0
- package/build/exponentVaults/pricePathResolver.js.map +1 -0
- package/build/exponentVaults/pricePathResolver.test.d.ts +1 -0
- package/build/exponentVaults/pricePathResolver.test.js +369 -0
- package/build/exponentVaults/pricePathResolver.test.js.map +1 -0
- package/build/exponentVaults/syncTransaction.js +4 -1
- package/build/exponentVaults/syncTransaction.js.map +1 -1
- package/build/exponentVaults/titan-quote.js +170 -36
- package/build/exponentVaults/titan-quote.js.map +1 -1
- package/build/exponentVaults/vault-instruction-types.d.ts +363 -0
- package/build/exponentVaults/vault-instruction-types.js +128 -0
- package/build/exponentVaults/vault-instruction-types.js.map +1 -0
- package/build/exponentVaults/vault-interaction.d.ts +203 -343
- package/build/exponentVaults/vault-interaction.js +1894 -426
- package/build/exponentVaults/vault-interaction.js.map +1 -1
- package/build/exponentVaults/vault-interaction.kamino-vault.test.d.ts +1 -0
- package/build/exponentVaults/vault-interaction.kamino-vault.test.js +143 -0
- package/build/exponentVaults/vault-interaction.kamino-vault.test.js.map +1 -0
- package/build/exponentVaults/vault.d.ts +51 -2
- package/build/exponentVaults/vault.js +324 -48
- package/build/exponentVaults/vault.js.map +1 -1
- package/build/exponentVaults/vaultTransactionBuilder.d.ts +100 -134
- package/build/exponentVaults/vaultTransactionBuilder.js +383 -285
- package/build/exponentVaults/vaultTransactionBuilder.js.map +1 -1
- package/build/exponentVaults/vaultTransactionBuilder.test.d.ts +1 -0
- package/build/exponentVaults/vaultTransactionBuilder.test.js +297 -0
- package/build/exponentVaults/vaultTransactionBuilder.test.js.map +1 -0
- package/build/marketThree.d.ts +6 -2
- package/build/marketThree.js +10 -8
- package/build/marketThree.js.map +1 -1
- package/package.json +34 -32
- package/src/client/vaults/index.ts +2 -0
- package/src/client/vaults/types/index.ts +2 -0
- package/src/client/vaults/types/kaminoFarmEntry.ts +32 -0
- package/src/client/vaults/types/kaminoObligationEntry.ts +6 -3
- package/src/client/vaults/types/positionUpdate.ts +62 -0
- package/src/client/vaults/types/proposalAction.ts +0 -3
- package/src/client/vaults/types/reserveFarmMapping.ts +35 -0
- package/src/client/vaults/types/strategyPosition.ts +18 -1
- package/src/exponentVaults/aumCalculator.ts +353 -16
- package/src/exponentVaults/fetcher.ts +257 -0
- package/src/exponentVaults/index.ts +65 -40
- package/src/exponentVaults/kamino-farms.ts +538 -0
- package/src/exponentVaults/loopscale/client.ts +808 -0
- package/src/exponentVaults/loopscale/helpers.ts +172 -0
- package/src/exponentVaults/loopscale/index.ts +57 -0
- package/src/exponentVaults/loopscale/prepared-transactions.ts +435 -0
- package/src/exponentVaults/loopscale/prepared-types.ts +73 -0
- package/src/exponentVaults/loopscale/types.ts +466 -0
- package/src/exponentVaults/policyBuilders.ts +170 -0
- package/src/exponentVaults/pricePathResolver.test.ts +466 -0
- package/src/exponentVaults/pricePathResolver.ts +273 -0
- package/src/exponentVaults/syncTransaction.ts +6 -1
- package/src/exponentVaults/titan-quote.ts +231 -45
- package/src/exponentVaults/vault-instruction-types.ts +493 -0
- package/src/exponentVaults/vault-interaction.kamino-vault.test.ts +149 -0
- package/src/exponentVaults/vault-interaction.ts +2818 -799
- package/src/exponentVaults/vault.ts +474 -63
- package/src/exponentVaults/vaultTransactionBuilder.test.ts +349 -0
- package/src/exponentVaults/vaultTransactionBuilder.ts +581 -433
- package/src/marketThree.ts +14 -6
- package/src/exponentVaults/loopscale-client.ts +0 -1373
|
@@ -1,33 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.VaultTransactionBuilder = void 0;
|
|
4
4
|
const web3_js_1 = require("@solana/web3.js");
|
|
5
|
-
const
|
|
5
|
+
const loopscale_1 = require("./loopscale");
|
|
6
6
|
const vault_interaction_1 = require("./vault-interaction");
|
|
7
7
|
const syncTransaction_1 = require("./syncTransaction");
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
// ============================================================================
|
|
11
|
-
const JITO_TIP_ADDRESSES = [
|
|
12
|
-
new web3_js_1.PublicKey("96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5"),
|
|
13
|
-
new web3_js_1.PublicKey("HFqU5x63VTqvQss8hp11i4wVV8bD44PvwucfZ2bU7gRe"),
|
|
14
|
-
new web3_js_1.PublicKey("Cw8CFyM9FkoMi7K7Crf6HNQqf4uEMzpKw6QNghXLvLkY"),
|
|
15
|
-
new web3_js_1.PublicKey("ADaUMid9yfUytqMBgopwjb2DTLSokTSzL1zt6iGPaS49"),
|
|
16
|
-
new web3_js_1.PublicKey("DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh"),
|
|
17
|
-
new web3_js_1.PublicKey("ADuUkR4vqLUMWXxW9gh6D6L8pMSawimctcNZ5pGwDcEt"),
|
|
18
|
-
new web3_js_1.PublicKey("DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL"),
|
|
19
|
-
new web3_js_1.PublicKey("3AVi9Tg9Uo68tJfuvoKvqKNWKkC5wPdSSdeBnizKZ6jT"),
|
|
20
|
-
];
|
|
21
|
-
const JITO_REGIONAL_URLS = [
|
|
22
|
-
"https://amsterdam.mainnet.block-engine.jito.wtf",
|
|
23
|
-
"https://frankfurt.mainnet.block-engine.jito.wtf",
|
|
24
|
-
"https://ny.mainnet.block-engine.jito.wtf",
|
|
25
|
-
"https://tokyo.mainnet.block-engine.jito.wtf",
|
|
26
|
-
];
|
|
27
|
-
const JITO_MAINNET_URL = "https://mainnet.block-engine.jito.wtf";
|
|
28
|
-
const JITO_BUNDLES_PATH = "/api/v1/bundles";
|
|
29
|
-
const JITO_MAX_RETRIES = 10;
|
|
30
|
-
const JITO_RETRY_DELAY_MS = 1000;
|
|
8
|
+
const environment_1 = require("../environment");
|
|
9
|
+
const SCOPE_PROGRAM_ID = new web3_js_1.PublicKey("HFn8GnPADiny6XqUoWE8uRPPxb29ikn4yTuPa9MF2fWJ");
|
|
31
10
|
// ============================================================================
|
|
32
11
|
// VaultTransactionBuilder
|
|
33
12
|
// ============================================================================
|
|
@@ -35,42 +14,44 @@ const JITO_RETRY_DELAY_MS = 1000;
|
|
|
35
14
|
* Orchestrates strategy vault transaction construction.
|
|
36
15
|
*
|
|
37
16
|
* Replaces the per-recipe boilerplate of manually assembling compute budgets,
|
|
38
|
-
*
|
|
39
|
-
*
|
|
17
|
+
* sync transaction wrapping, ALT gathering, setup transaction splitting, and
|
|
18
|
+
* forward-looking Kamino/CLMM/yield position tracking.
|
|
40
19
|
*
|
|
41
|
-
* **Lifecycle:** create → add actions → build →
|
|
20
|
+
* **Lifecycle:** create → add actions → build → send()
|
|
42
21
|
*
|
|
43
22
|
* @example Single action
|
|
44
23
|
* ```ts
|
|
45
24
|
* const result = await VaultTransactionBuilder
|
|
46
|
-
* .create({ vault, connection, signer: manager.publicKey
|
|
25
|
+
* .create({ vault, connection, signer: manager.publicKey })
|
|
47
26
|
* .addActions([clmmAction.buyPt({ market, ptOutMin, syInMax })])
|
|
48
27
|
* .build()
|
|
49
28
|
*
|
|
50
|
-
* await result.
|
|
29
|
+
* await result.send({ signers: [manager] })
|
|
51
30
|
* ```
|
|
52
31
|
*
|
|
53
32
|
* @example Multi-step — each addActions() becomes its own sync transaction
|
|
54
33
|
* ```ts
|
|
55
34
|
* const result = await VaultTransactionBuilder
|
|
56
|
-
* .create({ vault, connection, signer: manager.publicKey
|
|
35
|
+
* .create({ vault, connection, signer: manager.publicKey })
|
|
57
36
|
* .addActions([kaminoAction.deposit("MAIN", "USDC", amount)]) // sync tx 1
|
|
58
37
|
* .addActions([kaminoAction.borrow("MAIN", "SOL", amount)]) // sync tx 2
|
|
59
38
|
* .build()
|
|
60
39
|
*
|
|
61
|
-
* await result.
|
|
40
|
+
* await result.send({ signers: [manager] })
|
|
62
41
|
* ```
|
|
63
42
|
*
|
|
64
43
|
* @example Mixed Kamino + Loopscale — co-signing handled automatically
|
|
65
44
|
* ```ts
|
|
45
|
+
* const loopscaleDeposit = await loopscale.depositStrategy({ ... })
|
|
46
|
+
*
|
|
66
47
|
* const result = await VaultTransactionBuilder
|
|
67
|
-
* .create({ vault, connection, signer: manager.publicKey
|
|
48
|
+
* .create({ vault, connection, signer: manager.publicKey })
|
|
68
49
|
* .addActions([kaminoAction.deposit(...)]) // signed normally
|
|
69
|
-
* .
|
|
50
|
+
* .addLoopscaleResponse(loopscaleDeposit) // co-signed automatically
|
|
70
51
|
* .build()
|
|
71
52
|
*
|
|
72
|
-
* //
|
|
73
|
-
* await result.
|
|
53
|
+
* // send() auto-creates a LoopscaleClient for Loopscale co-signing
|
|
54
|
+
* await result.send({ signers: [manager] })
|
|
74
55
|
* ```
|
|
75
56
|
*/
|
|
76
57
|
class VaultTransactionBuilder {
|
|
@@ -83,7 +64,7 @@ class VaultTransactionBuilder {
|
|
|
83
64
|
/**
|
|
84
65
|
* Create a new builder instance.
|
|
85
66
|
*
|
|
86
|
-
* @param config - Builder configuration including the vault, connection,
|
|
67
|
+
* @param config - Builder configuration including the vault, connection, and signer.
|
|
87
68
|
* @returns A new {@link VaultTransactionBuilder} ready for action chaining.
|
|
88
69
|
*/
|
|
89
70
|
static create(config) {
|
|
@@ -115,70 +96,143 @@ class VaultTransactionBuilder {
|
|
|
115
96
|
addActions(instructions, options) {
|
|
116
97
|
this.stepCounter++;
|
|
117
98
|
const label = options?.label ?? `step-${this.stepCounter}`;
|
|
118
|
-
this.steps.push({ label, instructions: [...instructions],
|
|
99
|
+
this.steps.push({ kind: "instructions", label, instructions: [...instructions], options: options ?? {} });
|
|
119
100
|
return this;
|
|
120
101
|
}
|
|
121
102
|
/**
|
|
122
|
-
* Add Loopscale
|
|
103
|
+
* Add a raw Loopscale response as a new step.
|
|
123
104
|
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
105
|
+
* The builder will flatten the response into manager-facing transactions,
|
|
106
|
+
* preserving Loopscale-signed segments while keeping local setup/top-level
|
|
107
|
+
* transactions outside the Loopscale signing domain.
|
|
127
108
|
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
109
|
+
* @param response - Raw Loopscale response returned by {@link LoopscaleClient}.
|
|
110
|
+
* Only `label` is supported for Loopscale steps. Compute overrides are
|
|
111
|
+
* preserved on the raw Loopscale response and are not applied by the
|
|
112
|
+
* builder.
|
|
132
113
|
*
|
|
133
|
-
* @param
|
|
134
|
-
* @param options - Optional per-step compute budget overrides and label.
|
|
114
|
+
* @param options - Optional step label.
|
|
135
115
|
* @returns `this` for chaining.
|
|
136
116
|
*/
|
|
137
|
-
|
|
117
|
+
addLoopscaleResponse(response, options) {
|
|
138
118
|
this.stepCounter++;
|
|
139
119
|
const label = options?.label ?? `loopscale-step-${this.stepCounter}`;
|
|
140
|
-
this.steps.push({
|
|
120
|
+
this.steps.push({ kind: "loopscale-response", label, response, options: options ?? {} });
|
|
141
121
|
return this;
|
|
142
122
|
}
|
|
143
123
|
/**
|
|
144
124
|
* Build TransactionSets from the configured steps.
|
|
145
125
|
*
|
|
146
126
|
* Produces an ordered array of TransactionSets:
|
|
147
|
-
* 1. **
|
|
148
|
-
* 2. **
|
|
149
|
-
*
|
|
127
|
+
* 1. **Setup transactions** — token account creation / builder-managed setup (only if needed)
|
|
128
|
+
* 2. **Sync transactions** — one per step, wrapped in Squads sync transaction
|
|
129
|
+
*
|
|
130
|
+
* Same-transaction `UpdatePrice` instructions are injected directly into any
|
|
131
|
+
* setup or sync set that can hit AUM recalculation.
|
|
150
132
|
*
|
|
151
|
-
*
|
|
152
|
-
* Use `result.sendJitoBundle()` for atomic same-slot execution.
|
|
133
|
+
* Use `result.send()` for sequential execution.
|
|
153
134
|
*
|
|
154
|
-
* @returns A {@link VaultTransactionBuildResult} with TransactionSets and a
|
|
135
|
+
* @returns A {@link VaultTransactionBuildResult} with TransactionSets and a send method.
|
|
155
136
|
* @throws If no steps have been configured (call addActions first).
|
|
156
137
|
* @throws If any assembled transaction exceeds the 1232-byte Solana packet limit.
|
|
157
138
|
*/
|
|
158
139
|
async build() {
|
|
159
140
|
if (this.steps.length === 0) {
|
|
160
|
-
throw new Error("No steps configured. Call addActions() or
|
|
141
|
+
throw new Error("No steps configured. Call addActions() or addLoopscaleResponse() before build().");
|
|
161
142
|
}
|
|
162
|
-
const { vault, connection, signer,
|
|
143
|
+
const { vault, connection, signer, pricesAccount } = this.config;
|
|
144
|
+
const vaultPda = this.config.vaultPda ?? vault.state.squadsVault;
|
|
145
|
+
const extraLookupTableAddresses = this.config.lookupTableAddresses ?? [];
|
|
146
|
+
const autoManagePositions = this.config.autoManagePositions ?? true;
|
|
163
147
|
const squadsProgram = this.config.squadsProgram ?? syncTransaction_1.SQUADS_PROGRAM_ID;
|
|
164
148
|
const defaultCU = this.config.computeUnitLimit ?? 1_400_000;
|
|
165
149
|
const defaultHeap = this.config.heapFrameBytes ?? 256 * 1024;
|
|
166
|
-
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
150
|
+
const transactionSets = [];
|
|
151
|
+
const allAltAddresses = new Set([
|
|
152
|
+
...(vault.state.addressLookupTable ? [vault.state.addressLookupTable.toBase58()] : []),
|
|
153
|
+
...extraLookupTableAddresses.map((address) => address.toBase58()),
|
|
154
|
+
]);
|
|
155
|
+
// Share a single setup context across all steps so that tracked accounts,
|
|
156
|
+
// positions, and orderbooks carry over — preventing duplicate setup txs.
|
|
157
|
+
const sharedSetupContext = (0, vault_interaction_1.createStrategySetupContext)({
|
|
158
|
+
connection,
|
|
159
|
+
env: environment_1.LOCAL_ENV,
|
|
160
|
+
owner: vaultPda,
|
|
161
|
+
signer,
|
|
162
|
+
vaultAddress: vault.selfAddress,
|
|
163
|
+
vaultPda,
|
|
164
|
+
accountIndex: 0,
|
|
165
|
+
squadsProgram,
|
|
166
|
+
autoManagePositions,
|
|
167
|
+
pricesAccount,
|
|
168
|
+
});
|
|
169
|
+
let loopscaleClient;
|
|
180
170
|
for (const step of this.steps) {
|
|
181
|
-
|
|
171
|
+
if (step.kind === "loopscale-response") {
|
|
172
|
+
loopscaleClient ??= createBuilderLoopscaleClient({
|
|
173
|
+
connection,
|
|
174
|
+
vaultPda,
|
|
175
|
+
loopscale: this.config.loopscale,
|
|
176
|
+
});
|
|
177
|
+
const preparedTransactions = await loopscaleClient.prepareVaultTransactions({
|
|
178
|
+
response: step.response,
|
|
179
|
+
context: {
|
|
180
|
+
connection,
|
|
181
|
+
signer,
|
|
182
|
+
vaultPda,
|
|
183
|
+
vaultAddress: vault.selfAddress,
|
|
184
|
+
squadsProgram,
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
const stepPriceRefreshIxs = await (0, vault_interaction_1.buildSetupStatePriceRefreshInstructions)(sharedSetupContext);
|
|
188
|
+
for (const [preparedIndex, preparedTransaction] of preparedTransactions.entries()) {
|
|
189
|
+
const txLabel = preparedTransactions.length === 1
|
|
190
|
+
? step.label
|
|
191
|
+
: `${step.label}-${preparedIndex + 1}`;
|
|
192
|
+
if (preparedTransaction.setupInstructions.length > 0) {
|
|
193
|
+
const setupAltAddresses = mergeAddressLookupTableAddresses(extraLookupTableAddresses, vault.state.addressLookupTable ? [vault.state.addressLookupTable] : []);
|
|
194
|
+
const setupSet = {
|
|
195
|
+
label: `${txLabel}-setup`,
|
|
196
|
+
instructions: [
|
|
197
|
+
web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: defaultCU }),
|
|
198
|
+
...preparedTransaction.setupInstructions,
|
|
199
|
+
],
|
|
200
|
+
signers: [],
|
|
201
|
+
addressLookupTableAddresses: setupAltAddresses,
|
|
202
|
+
coSignWithLoopscale: false,
|
|
203
|
+
};
|
|
204
|
+
if (touchesManagedVaultPrograms(setupSet.instructions, vault.programId, squadsProgram)) {
|
|
205
|
+
setupSet.instructions = insertAfterLeadingComputeBudgetAndScopeInstructions(setupSet.instructions, stepPriceRefreshIxs);
|
|
206
|
+
}
|
|
207
|
+
transactionSets.push(setupSet);
|
|
208
|
+
for (const alt of setupSet.addressLookupTableAddresses) {
|
|
209
|
+
allAltAddresses.add(alt.toBase58());
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
const shouldInjectLocalLoopscalePriceRefresh = (!preparedTransaction.requiresLoopscaleCoSign
|
|
213
|
+
&& touchesManagedVaultPrograms(preparedTransaction.instructions, vault.programId, squadsProgram));
|
|
214
|
+
const loopscaleSet = {
|
|
215
|
+
label: txLabel,
|
|
216
|
+
instructions: preparedTransaction.instructions,
|
|
217
|
+
signers: preparedTransaction.signers,
|
|
218
|
+
addressLookupTableAddresses: shouldInjectLocalLoopscalePriceRefresh
|
|
219
|
+
? mergeAddressLookupTableAddresses(preparedTransaction.addressLookupTableAddresses, extraLookupTableAddresses, vault.state.addressLookupTable ? [vault.state.addressLookupTable] : [])
|
|
220
|
+
: mergeAddressLookupTableAddresses(preparedTransaction.addressLookupTableAddresses, extraLookupTableAddresses),
|
|
221
|
+
coSignWithLoopscale: preparedTransaction.requiresLoopscaleCoSign,
|
|
222
|
+
};
|
|
223
|
+
if (shouldInjectLocalLoopscalePriceRefresh) {
|
|
224
|
+
loopscaleSet.instructions = insertAfterLeadingComputeBudgetAndScopeInstructions(loopscaleSet.instructions, stepPriceRefreshIxs);
|
|
225
|
+
}
|
|
226
|
+
transactionSets.push(loopscaleSet);
|
|
227
|
+
for (const alt of loopscaleSet.addressLookupTableAddresses) {
|
|
228
|
+
allAltAddresses.add(alt.toBase58());
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
const stepCU = step.options.computeUnitLimit ?? defaultCU;
|
|
234
|
+
const stepHeap = step.options.heapFrameBytes ?? defaultHeap;
|
|
235
|
+
const syncResults = await (0, vault_interaction_1.createVaultSyncTransactions)({
|
|
182
236
|
instructions: step.instructions,
|
|
183
237
|
owner: vaultPda,
|
|
184
238
|
connection,
|
|
@@ -186,171 +240,172 @@ class VaultTransactionBuilder {
|
|
|
186
240
|
signer,
|
|
187
241
|
vaultAddress: vault.selfAddress,
|
|
188
242
|
squadsProgram,
|
|
243
|
+
autoManagePositions,
|
|
244
|
+
setupContext: sharedSetupContext,
|
|
189
245
|
});
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
246
|
+
const stepPriceRefreshIxs = await (0, vault_interaction_1.buildSetupStatePriceRefreshInstructions)(sharedSetupContext);
|
|
247
|
+
for (const [syncIndex, syncResult] of syncResults.entries()) {
|
|
248
|
+
const txLabel = syncResults.length === 1
|
|
249
|
+
? step.label
|
|
250
|
+
: `${step.label}-${syncIndex + 1}`;
|
|
251
|
+
if (syncResult.setupInstructions.length > 0) {
|
|
252
|
+
const setupSet = {
|
|
253
|
+
label: `${txLabel}-setup`,
|
|
254
|
+
instructions: [
|
|
255
|
+
web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: defaultCU }),
|
|
256
|
+
...syncResult.setupInstructions,
|
|
257
|
+
],
|
|
258
|
+
signers: [],
|
|
259
|
+
addressLookupTableAddresses: mergeAddressLookupTableAddresses(syncResult.addressLookupTableAddresses, extraLookupTableAddresses, vault.state.addressLookupTable ? [vault.state.addressLookupTable] : []),
|
|
260
|
+
coSignWithLoopscale: false,
|
|
261
|
+
};
|
|
262
|
+
if (shouldInjectPriceRefresh(setupSet, vault.programId, squadsProgram)) {
|
|
263
|
+
setupSet.instructions = insertAfterLeadingComputeBudgetAndScopeInstructions(setupSet.instructions, stepPriceRefreshIxs);
|
|
264
|
+
}
|
|
265
|
+
transactionSets.push(setupSet);
|
|
266
|
+
for (const alt of setupSet.addressLookupTableAddresses) {
|
|
267
|
+
allAltAddresses.add(alt.toBase58());
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
const syncSet = {
|
|
271
|
+
label: txLabel,
|
|
193
272
|
instructions: [
|
|
194
|
-
|
|
195
|
-
...syncResult.
|
|
273
|
+
...buildComputeBudgetIxs(stepCU, stepHeap, step.options.priorityFee),
|
|
274
|
+
...syncResult.preInstructions,
|
|
275
|
+
syncResult.instruction,
|
|
276
|
+
...syncResult.postInstructions,
|
|
196
277
|
],
|
|
197
|
-
signers:
|
|
198
|
-
addressLookupTableAddresses: [],
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
syncResult.instruction,
|
|
209
|
-
...syncResult.postInstructions,
|
|
210
|
-
],
|
|
211
|
-
signers: syncResult.signers,
|
|
212
|
-
addressLookupTableAddresses: [
|
|
213
|
-
...syncResult.addressLookupTableAddresses,
|
|
214
|
-
...(vault.state.addressLookupTable ? [vault.state.addressLookupTable] : []),
|
|
215
|
-
],
|
|
216
|
-
};
|
|
217
|
-
transactionSets.push(syncSet);
|
|
218
|
-
for (const alt of syncSet.addressLookupTableAddresses) {
|
|
219
|
-
allAltAddresses.add(alt.toBase58());
|
|
278
|
+
signers: syncResult.signers,
|
|
279
|
+
addressLookupTableAddresses: mergeAddressLookupTableAddresses(syncResult.addressLookupTableAddresses, extraLookupTableAddresses, vault.state.addressLookupTable ? [vault.state.addressLookupTable] : []),
|
|
280
|
+
coSignWithLoopscale: false,
|
|
281
|
+
};
|
|
282
|
+
if (shouldInjectPriceRefresh(syncSet, vault.programId, squadsProgram)) {
|
|
283
|
+
syncSet.instructions = insertAfterLeadingComputeBudgetAndScopeInstructions(syncSet.instructions, stepPriceRefreshIxs);
|
|
284
|
+
}
|
|
285
|
+
transactionSets.push(syncSet);
|
|
286
|
+
for (const alt of syncSet.addressLookupTableAddresses) {
|
|
287
|
+
allAltAddresses.add(alt.toBase58());
|
|
288
|
+
}
|
|
220
289
|
}
|
|
221
290
|
}
|
|
222
291
|
const lookupTableAddresses = [...allAltAddresses].map((a) => new web3_js_1.PublicKey(a));
|
|
223
|
-
//
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
recentBlockhash: "1".repeat(44),
|
|
231
|
-
instructions: txSet.instructions,
|
|
232
|
-
}).compileToV0Message(txAltAccounts);
|
|
233
|
-
const testTx = new web3_js_1.VersionedTransaction(testMsg);
|
|
234
|
-
const serialized = testTx.serialize();
|
|
235
|
-
if (serialized.length > 1232) {
|
|
236
|
-
throw new Error(`Transaction "${txSet.label}" is oversized (${serialized.length} bytes, limit 1232). ` +
|
|
237
|
-
`This usually means too many actions were packed into a single addActions() call. ` +
|
|
238
|
-
`Split them into separate addActions() calls so each gets its own sync transaction.`);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
catch (e) {
|
|
242
|
-
if (e instanceof Error && e.message.includes("oversized"))
|
|
243
|
-
throw e;
|
|
292
|
+
// Resolve the vault's persistent ALT to determine which account keys it
|
|
293
|
+
// already covers. Any keys NOT covered will need an ephemeral ALT.
|
|
294
|
+
const persistentAltAccounts = await resolveAltAccounts(connection, lookupTableAddresses);
|
|
295
|
+
const persistentAltKeys = new Set();
|
|
296
|
+
for (const alt of persistentAltAccounts) {
|
|
297
|
+
for (const addr of alt.state.addresses) {
|
|
298
|
+
persistentAltKeys.add(addr.toBase58());
|
|
244
299
|
}
|
|
245
300
|
}
|
|
301
|
+
// Collect every unique account key referenced by any transaction set.
|
|
302
|
+
const allUniqueKeys = collectUniqueAccountKeys(transactionSets, signer);
|
|
303
|
+
const ephemeralAltEntries = allUniqueKeys.filter((k) => !persistentAltKeys.has(k.toBase58()));
|
|
304
|
+
// Validate transaction sizes. When an ephemeral ALT will be created,
|
|
305
|
+
// skip validation here — send() will validate after ALT activation.
|
|
306
|
+
if (ephemeralAltEntries.length === 0) {
|
|
307
|
+
validateTransactionSizes(transactionSets, persistentAltAccounts, signer);
|
|
308
|
+
}
|
|
246
309
|
const labels = transactionSets.map((ts) => ts.label);
|
|
247
|
-
// Capture state for
|
|
310
|
+
// Capture state for send() closure
|
|
248
311
|
const closedConnection = connection;
|
|
249
312
|
const closedSigner = signer;
|
|
313
|
+
const closedVaultPda = vaultPda;
|
|
250
314
|
const closedLookupTableAddresses = lookupTableAddresses;
|
|
251
|
-
const
|
|
315
|
+
const closedLoopscaleConfig = this.config.loopscale;
|
|
252
316
|
return {
|
|
253
317
|
transactionSets,
|
|
254
318
|
lookupTableAddresses,
|
|
255
319
|
labels,
|
|
256
|
-
async
|
|
320
|
+
async send(options) {
|
|
321
|
+
const commitment = options.commitment ?? "confirmed";
|
|
322
|
+
const payer = options.signers[0];
|
|
323
|
+
// ── Ephemeral ALT creation ───────────────────────────────────────
|
|
324
|
+
let ephemeralAlt = null;
|
|
325
|
+
let ephemeralAltAccount;
|
|
326
|
+
if (ephemeralAltEntries.length > 0) {
|
|
327
|
+
const slot = await closedConnection.getSlot("processed");
|
|
328
|
+
const recentSlot = Math.max(slot - 1, 0);
|
|
329
|
+
const [createIx, altAddress] = web3_js_1.AddressLookupTableProgram.createLookupTable({
|
|
330
|
+
authority: payer.publicKey,
|
|
331
|
+
payer: payer.publicKey,
|
|
332
|
+
recentSlot,
|
|
333
|
+
});
|
|
334
|
+
// Chunk extend instructions — each can hold ~20 addresses safely
|
|
335
|
+
const extendChunks = chunkArray(ephemeralAltEntries, 20);
|
|
336
|
+
const extendIxs = extendChunks.map((chunk) => web3_js_1.AddressLookupTableProgram.extendLookupTable({
|
|
337
|
+
lookupTable: altAddress,
|
|
338
|
+
authority: payer.publicKey,
|
|
339
|
+
payer: payer.publicKey,
|
|
340
|
+
addresses: chunk,
|
|
341
|
+
}));
|
|
342
|
+
// Send create + first extend in one transaction
|
|
343
|
+
const altSetupIxs = [createIx, extendIxs[0]];
|
|
344
|
+
await sendAndConfirmTransaction(closedConnection, altSetupIxs, [payer], commitment);
|
|
345
|
+
// Send remaining extend chunks if any
|
|
346
|
+
for (let i = 1; i < extendIxs.length; i++) {
|
|
347
|
+
await sendAndConfirmTransaction(closedConnection, [extendIxs[i]], [payer], commitment);
|
|
348
|
+
}
|
|
349
|
+
// Wait for ALT entries to become active (usable in the slot AFTER extension)
|
|
350
|
+
await waitForSlotAdvance(closedConnection, slot, "processed");
|
|
351
|
+
// Resolve the ephemeral ALT account for use in transactions
|
|
352
|
+
const resolved = await closedConnection.getAddressLookupTable(altAddress);
|
|
353
|
+
if (!resolved.value) {
|
|
354
|
+
throw new Error(`Ephemeral ALT ${altAddress.toBase58()} not found after creation`);
|
|
355
|
+
}
|
|
356
|
+
ephemeralAltAccount = resolved.value;
|
|
357
|
+
ephemeralAlt = { address: altAddress, authority: payer.publicKey };
|
|
358
|
+
}
|
|
359
|
+
// ── Resolve all ALT accounts (persistent + ephemeral) ────────────
|
|
257
360
|
const resolvedAltAccounts = await resolveAltAccounts(closedConnection, closedLookupTableAddresses);
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
361
|
+
if (ephemeralAltAccount) {
|
|
362
|
+
resolvedAltAccounts.push(ephemeralAltAccount);
|
|
363
|
+
}
|
|
364
|
+
// If we skipped build-time validation, validate now with the full ALT set
|
|
365
|
+
if (ephemeralAltEntries.length > 0) {
|
|
366
|
+
validateTransactionSizes(transactionSets, resolvedAltAccounts, closedSigner, ephemeralAltAccount);
|
|
367
|
+
}
|
|
368
|
+
// ── Auto-create LoopscaleClient if any step needs co-signing ─────
|
|
369
|
+
const hasLoopscale = transactionSets.some((txSet) => txSet.coSignWithLoopscale);
|
|
261
370
|
let loopscaleClient;
|
|
262
371
|
if (hasLoopscale) {
|
|
263
|
-
loopscaleClient =
|
|
372
|
+
loopscaleClient = createBuilderLoopscaleClient({
|
|
264
373
|
connection: closedConnection,
|
|
265
|
-
|
|
374
|
+
vaultPda: closedVaultPda,
|
|
375
|
+
loopscale: closedLoopscaleConfig,
|
|
266
376
|
});
|
|
267
377
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
}
|
|
276
|
-
const txAltAccounts = resolvedAltAccounts.filter((alt) => txSet.addressLookupTableAddresses.some((addr) => addr.equals(alt.key)));
|
|
378
|
+
// ── Send transactions ────────────────────────────────────────────
|
|
379
|
+
const signatures = [];
|
|
380
|
+
for (const txSet of transactionSets) {
|
|
381
|
+
const { blockhash, lastValidBlockHeight } = await closedConnection.getLatestBlockhash();
|
|
382
|
+
// Include the ephemeral ALT for every transaction set
|
|
383
|
+
const txAltAccounts = resolvedAltAccounts.filter((alt) => txSet.addressLookupTableAddresses.some((addr) => addr.equals(alt.key))
|
|
384
|
+
|| (ephemeralAltAccount && alt.key.equals(ephemeralAltAccount.key)));
|
|
277
385
|
const msg = new web3_js_1.TransactionMessage({
|
|
278
|
-
payerKey:
|
|
386
|
+
payerKey: payer.publicKey,
|
|
279
387
|
recentBlockhash: blockhash,
|
|
280
|
-
instructions,
|
|
388
|
+
instructions: txSet.instructions,
|
|
281
389
|
}).compileToV0Message(txAltAccounts);
|
|
282
390
|
let tx = new web3_js_1.VersionedTransaction(msg);
|
|
283
|
-
|
|
284
|
-
const step = closedSteps.find((s) => s.label === txSet.label);
|
|
285
|
-
if (step?.isLoopscale && loopscaleClient) {
|
|
391
|
+
if (txSet.coSignWithLoopscale && loopscaleClient) {
|
|
286
392
|
tx.sign([...options.signers, ...txSet.signers]);
|
|
287
393
|
tx = await loopscaleClient.coSign(tx);
|
|
288
394
|
}
|
|
289
395
|
else {
|
|
290
396
|
tx.sign([...options.signers, ...txSet.signers]);
|
|
291
397
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
if (options.customSend) {
|
|
296
|
-
const sigs = await options.customSend(allTxs, labels);
|
|
297
|
-
return { signatures: sigs, bundleId: "" };
|
|
398
|
+
const sig = await closedConnection.sendTransaction(tx);
|
|
399
|
+
await closedConnection.confirmTransaction({ signature: sig, blockhash, lastValidBlockHeight }, commitment);
|
|
400
|
+
signatures.push(sig);
|
|
298
401
|
}
|
|
299
|
-
|
|
300
|
-
const result = await sendJitoBundleRpc(serialized, options.jitoAuthKey);
|
|
301
|
-
const commitment = options.commitment ?? "confirmed";
|
|
302
|
-
if (result.signatures.length > 0) {
|
|
303
|
-
await closedConnection.confirmTransaction({ signature: result.signatures[0], blockhash, lastValidBlockHeight }, commitment).catch(() => { });
|
|
304
|
-
}
|
|
305
|
-
return result;
|
|
402
|
+
return { signatures, ephemeralAlt };
|
|
306
403
|
},
|
|
307
404
|
};
|
|
308
405
|
}
|
|
309
406
|
}
|
|
310
407
|
exports.VaultTransactionBuilder = VaultTransactionBuilder;
|
|
311
408
|
// ============================================================================
|
|
312
|
-
// bundleForJito — standalone utility
|
|
313
|
-
// ============================================================================
|
|
314
|
-
/**
|
|
315
|
-
* Package TransactionSets into Jito-compatible unsigned VersionedTransactions.
|
|
316
|
-
*
|
|
317
|
-
* All transactions share the same blockhash and a Jito tip instruction is appended
|
|
318
|
-
* to the last transaction. The returned transactions are **unsigned** — the caller
|
|
319
|
-
* is responsible for signing (useful for web app flows where a wallet adapter handles signing).
|
|
320
|
-
*
|
|
321
|
-
* For a fully managed sign-and-send flow, use {@link VaultTransactionBuildResult.sendJitoBundle} instead.
|
|
322
|
-
*
|
|
323
|
-
* @param transactionSets - Ordered TransactionSets from `builder.build()`.
|
|
324
|
-
* @param params.connection - Solana RPC connection for ALT resolution and blockhash fetching.
|
|
325
|
-
* @param params.payer - Public key of the fee payer (used in the V0 message header).
|
|
326
|
-
* @param params.tipLamports - Jito tip amount in lamports, appended to the last transaction.
|
|
327
|
-
* @param params.lookupTableAddresses - All ALT addresses from the build result.
|
|
328
|
-
* @returns Unsigned VersionedTransactions ready for wallet signing.
|
|
329
|
-
*/
|
|
330
|
-
async function bundleForJito(transactionSets, params) {
|
|
331
|
-
const { connection, payer, tipLamports, lookupTableAddresses } = params;
|
|
332
|
-
const altAccounts = await resolveAltAccounts(connection, lookupTableAddresses);
|
|
333
|
-
const { blockhash } = await connection.getLatestBlockhash();
|
|
334
|
-
const transactions = [];
|
|
335
|
-
for (let i = 0; i < transactionSets.length; i++) {
|
|
336
|
-
const txSet = transactionSets[i];
|
|
337
|
-
const isLast = i === transactionSets.length - 1;
|
|
338
|
-
const instructions = [...txSet.instructions];
|
|
339
|
-
if (isLast && tipLamports > 0) {
|
|
340
|
-
instructions.push(createJitoTipIx(tipLamports, payer));
|
|
341
|
-
}
|
|
342
|
-
const txAltAccounts = altAccounts.filter((alt) => txSet.addressLookupTableAddresses.some((addr) => addr.equals(alt.key)));
|
|
343
|
-
const messageV0 = new web3_js_1.TransactionMessage({
|
|
344
|
-
payerKey: payer,
|
|
345
|
-
recentBlockhash: blockhash,
|
|
346
|
-
instructions,
|
|
347
|
-
}).compileToV0Message(txAltAccounts);
|
|
348
|
-
transactions.push(new web3_js_1.VersionedTransaction(messageV0));
|
|
349
|
-
}
|
|
350
|
-
return transactions;
|
|
351
|
-
}
|
|
352
|
-
exports.bundleForJito = bundleForJito;
|
|
353
|
-
// ============================================================================
|
|
354
409
|
// Internal helpers
|
|
355
410
|
// ============================================================================
|
|
356
411
|
/** Build compute budget instructions: CU limit + heap frame + optional priority fee. */
|
|
@@ -364,6 +419,56 @@ function buildComputeBudgetIxs(computeUnitLimit, heapFrameBytes, priorityFee) {
|
|
|
364
419
|
}
|
|
365
420
|
return ixs;
|
|
366
421
|
}
|
|
422
|
+
function insertAfterLeadingComputeBudgetAndScopeInstructions(instructions, extraInstructions) {
|
|
423
|
+
if (extraInstructions.length === 0) {
|
|
424
|
+
return instructions;
|
|
425
|
+
}
|
|
426
|
+
let insertIndex = 0;
|
|
427
|
+
while (insertIndex < instructions.length
|
|
428
|
+
&& instructions[insertIndex]?.programId.equals(web3_js_1.ComputeBudgetProgram.programId)) {
|
|
429
|
+
insertIndex += 1;
|
|
430
|
+
}
|
|
431
|
+
while (insertIndex < instructions.length
|
|
432
|
+
&& instructions[insertIndex]?.programId.equals(SCOPE_PROGRAM_ID)) {
|
|
433
|
+
insertIndex += 1;
|
|
434
|
+
}
|
|
435
|
+
return [
|
|
436
|
+
...instructions.slice(0, insertIndex),
|
|
437
|
+
...extraInstructions,
|
|
438
|
+
...instructions.slice(insertIndex),
|
|
439
|
+
];
|
|
440
|
+
}
|
|
441
|
+
function shouldInjectPriceRefresh(txSet, programId, squadsProgram) {
|
|
442
|
+
if (!txSet.label.endsWith("-setup")) {
|
|
443
|
+
return true;
|
|
444
|
+
}
|
|
445
|
+
return txSet.instructions.some((instruction) => instruction.programId.equals(programId) || instruction.programId.equals(squadsProgram));
|
|
446
|
+
}
|
|
447
|
+
function touchesManagedVaultPrograms(instructions, programId, squadsProgram) {
|
|
448
|
+
return instructions.some((instruction) => instruction.programId.equals(programId) || instruction.programId.equals(squadsProgram));
|
|
449
|
+
}
|
|
450
|
+
function mergeAddressLookupTableAddresses(...addressGroups) {
|
|
451
|
+
const seen = new Set();
|
|
452
|
+
const merged = [];
|
|
453
|
+
for (const group of addressGroups) {
|
|
454
|
+
for (const address of group) {
|
|
455
|
+
const key = address.toBase58();
|
|
456
|
+
if (seen.has(key))
|
|
457
|
+
continue;
|
|
458
|
+
seen.add(key);
|
|
459
|
+
merged.push(address);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
return merged;
|
|
463
|
+
}
|
|
464
|
+
function createBuilderLoopscaleClient(params) {
|
|
465
|
+
return new loopscale_1.LoopscaleClient({
|
|
466
|
+
connection: params.connection,
|
|
467
|
+
userWallet: params.vaultPda,
|
|
468
|
+
baseUrl: params.loopscale?.baseUrl,
|
|
469
|
+
debug: params.loopscale?.debug,
|
|
470
|
+
});
|
|
471
|
+
}
|
|
367
472
|
/** Fetch and resolve AddressLookupTableAccounts from on-chain, filtering nulls. */
|
|
368
473
|
async function resolveAltAccounts(connection, addresses) {
|
|
369
474
|
const results = await Promise.all(addresses.map((addr) => connection.getAddressLookupTable(addr)));
|
|
@@ -371,100 +476,93 @@ async function resolveAltAccounts(connection, addresses) {
|
|
|
371
476
|
.map((r) => r.value)
|
|
372
477
|
.filter((v) => v !== null);
|
|
373
478
|
}
|
|
374
|
-
/**
|
|
375
|
-
function
|
|
376
|
-
const
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
* Send a bundle of serialized transactions to Jito block engine endpoints.
|
|
385
|
-
* Fans out to regional endpoints (best-effort) and retries the mainnet endpoint.
|
|
386
|
-
*/
|
|
387
|
-
async function sendJitoBundleRpc(serializedTransactions, authKey) {
|
|
388
|
-
if (serializedTransactions.length > 5) {
|
|
389
|
-
throw new Error("Jito bundles support at most 5 transactions");
|
|
390
|
-
}
|
|
391
|
-
const signatures = serializedTransactions.map((tx) => {
|
|
392
|
-
const vtx = web3_js_1.VersionedTransaction.deserialize(tx);
|
|
393
|
-
return Buffer.from(vtx.signatures[0]).toString("base64");
|
|
394
|
-
});
|
|
395
|
-
const requestBody = {
|
|
396
|
-
jsonrpc: "2.0",
|
|
397
|
-
id: 1,
|
|
398
|
-
method: "sendBundle",
|
|
399
|
-
params: [serializedTransactions.map((tx) => tx.toString("base64")), { encoding: "base64" }],
|
|
479
|
+
/** Collect all unique account keys referenced across all transaction sets. */
|
|
480
|
+
function collectUniqueAccountKeys(transactionSets, signer) {
|
|
481
|
+
const seen = new Set();
|
|
482
|
+
const keys = [];
|
|
483
|
+
const add = (pubkey) => {
|
|
484
|
+
const str = pubkey.toBase58();
|
|
485
|
+
if (!seen.has(str)) {
|
|
486
|
+
seen.add(str);
|
|
487
|
+
keys.push(pubkey);
|
|
488
|
+
}
|
|
400
489
|
};
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
Promise.allSettled(regionalPromises);
|
|
408
|
-
// Retry mainnet endpoint
|
|
409
|
-
for (let attempt = 0; attempt < JITO_MAX_RETRIES; attempt++) {
|
|
410
|
-
try {
|
|
411
|
-
const response = await fetch(`${JITO_MAINNET_URL}${JITO_BUNDLES_PATH}`, {
|
|
412
|
-
method: "POST",
|
|
413
|
-
headers,
|
|
414
|
-
body: JSON.stringify(requestBody),
|
|
415
|
-
});
|
|
416
|
-
const data = (await response.json().catch(() => undefined));
|
|
417
|
-
if (response.ok && !data?.error && typeof data?.result === "string") {
|
|
418
|
-
return { bundleId: data.result, signatures };
|
|
419
|
-
}
|
|
420
|
-
if (response.status === 400) {
|
|
421
|
-
return { bundleId: "", signatures };
|
|
490
|
+
add(signer);
|
|
491
|
+
for (const txSet of transactionSets) {
|
|
492
|
+
for (const ix of txSet.instructions) {
|
|
493
|
+
add(ix.programId);
|
|
494
|
+
for (const key of ix.keys) {
|
|
495
|
+
add(key.pubkey);
|
|
422
496
|
}
|
|
423
497
|
}
|
|
424
|
-
catch { }
|
|
425
|
-
await new Promise((r) => setTimeout(r, JITO_RETRY_DELAY_MS));
|
|
426
498
|
}
|
|
427
|
-
return
|
|
499
|
+
return keys;
|
|
428
500
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
const
|
|
444
|
-
if (
|
|
445
|
-
|
|
446
|
-
|
|
501
|
+
/** Validate that every transaction set can serialize within the 1232-byte limit. */
|
|
502
|
+
function validateTransactionSizes(transactionSets, altAccounts, signer, extraAlt) {
|
|
503
|
+
for (const txSet of transactionSets) {
|
|
504
|
+
const txAltAccounts = altAccounts.filter((alt) => txSet.addressLookupTableAddresses.some((addr) => addr.equals(alt.key)));
|
|
505
|
+
if (extraAlt && !txAltAccounts.some((a) => a.key.equals(extraAlt.key))) {
|
|
506
|
+
txAltAccounts.push(extraAlt);
|
|
507
|
+
}
|
|
508
|
+
try {
|
|
509
|
+
const testMsg = new web3_js_1.TransactionMessage({
|
|
510
|
+
payerKey: signer,
|
|
511
|
+
recentBlockhash: web3_js_1.PublicKey.default.toBase58(),
|
|
512
|
+
instructions: txSet.instructions,
|
|
513
|
+
}).compileToV0Message(txAltAccounts);
|
|
514
|
+
const testTx = new web3_js_1.VersionedTransaction(testMsg);
|
|
515
|
+
const serialized = testTx.serialize();
|
|
516
|
+
if (serialized.length > 1232) {
|
|
517
|
+
throw new Error(`Transaction "${txSet.label}" is oversized (${serialized.length} bytes, limit 1232). ` +
|
|
518
|
+
`This usually means too many actions were packed into a single addActions() call. ` +
|
|
519
|
+
`Split them into separate addActions() calls so each gets its own sync transaction.`);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
catch (e) {
|
|
523
|
+
if (e instanceof Error) {
|
|
524
|
+
const ixCount = txSet.instructions.length;
|
|
525
|
+
const altCount = txAltAccounts.length;
|
|
526
|
+
const accountCount = txSet.instructions.reduce((sum, ix) => sum + ix.keys.length, 0);
|
|
527
|
+
throw new Error(`Transaction "${txSet.label}" failed to serialize: ${e.message}. ` +
|
|
528
|
+
`Transaction has ${ixCount} instructions, ${accountCount} account keys, ` +
|
|
529
|
+
`and ${altCount} address lookup table(s). ` +
|
|
530
|
+
`Try splitting actions into separate addActions() calls.`);
|
|
447
531
|
}
|
|
532
|
+
throw e;
|
|
448
533
|
}
|
|
449
534
|
}
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
535
|
+
}
|
|
536
|
+
/** Split an array into chunks of the given size. */
|
|
537
|
+
function chunkArray(arr, size) {
|
|
538
|
+
const chunks = [];
|
|
539
|
+
for (let i = 0; i < arr.length; i += size) {
|
|
540
|
+
chunks.push(arr.slice(i, i + size));
|
|
541
|
+
}
|
|
542
|
+
return chunks;
|
|
543
|
+
}
|
|
544
|
+
/** Build, sign, send, and confirm a simple transaction. */
|
|
545
|
+
async function sendAndConfirmTransaction(connection, instructions, signers, commitment) {
|
|
546
|
+
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash(commitment);
|
|
547
|
+
const msg = new web3_js_1.TransactionMessage({
|
|
548
|
+
payerKey: signers[0].publicKey,
|
|
549
|
+
recentBlockhash: blockhash,
|
|
550
|
+
instructions,
|
|
551
|
+
}).compileToV0Message();
|
|
552
|
+
const tx = new web3_js_1.VersionedTransaction(msg);
|
|
553
|
+
tx.sign(signers);
|
|
554
|
+
const sig = await connection.sendTransaction(tx);
|
|
555
|
+
await connection.confirmTransaction({ signature: sig, blockhash, lastValidBlockHeight }, commitment);
|
|
556
|
+
return sig;
|
|
557
|
+
}
|
|
558
|
+
/** Poll until the current slot advances past the target slot. */
|
|
559
|
+
async function waitForSlotAdvance(connection, targetSlot, commitment = "processed") {
|
|
560
|
+
for (let i = 0; i < 60; i++) {
|
|
561
|
+
const currentSlot = await connection.getSlot(commitment);
|
|
562
|
+
if (currentSlot > targetSlot)
|
|
563
|
+
return;
|
|
564
|
+
await new Promise((r) => setTimeout(r, 400));
|
|
467
565
|
}
|
|
468
|
-
|
|
566
|
+
throw new Error("Timed out waiting for slot to advance after ephemeral ALT creation");
|
|
469
567
|
}
|
|
470
568
|
//# sourceMappingURL=vaultTransactionBuilder.js.map
|