@augustdigital/sdk 8.20.1 → 8.21.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/lib/adapters/solana/index.d.ts +42 -8
- package/lib/adapters/solana/index.js +39 -2
- package/lib/adapters/solana/utils.d.ts +54 -3
- package/lib/adapters/solana/utils.js +101 -18
- package/lib/adapters/solana/vault.actions.d.ts +37 -7
- package/lib/adapters/solana/vault.actions.js +231 -81
- package/lib/core/analytics/version.d.ts +1 -1
- package/lib/core/analytics/version.js +1 -1
- package/lib/main.js +7 -1
- package/lib/sdk.d.ts +92 -13
- package/lib/types/web3.d.ts +15 -0
- package/package.json +1 -1
|
@@ -7,7 +7,7 @@ import * as SolanaConstants from './constants';
|
|
|
7
7
|
import * as SolanaGetters from './getters';
|
|
8
8
|
import * as SolanaActions from './vault.actions';
|
|
9
9
|
import type { AnchorProvider, web3 } from '@coral-xyz/anchor';
|
|
10
|
-
import { Connection, PublicKey, type Transaction } from '@solana/web3.js';
|
|
10
|
+
import { type Commitment, Connection, PublicKey, type Transaction } from '@solana/web3.js';
|
|
11
11
|
import type { ISolanaNetwork, ISolanaRpcEndpoint } from './types';
|
|
12
12
|
import type { SendTransactionOptions } from '@solana/wallet-adapter-base';
|
|
13
13
|
export declare const Solana: {
|
|
@@ -61,11 +61,11 @@ export declare const Solana: {
|
|
|
61
61
|
fetchUserTokenBalance: ({ connection, publicKey, depositMint, }: {
|
|
62
62
|
publicKey: PublicKey | string;
|
|
63
63
|
depositMint?: PublicKey | string;
|
|
64
|
-
} & import("./types").ISolanaConnectionOptions) => Promise<
|
|
64
|
+
} & import("./types").ISolanaConnectionOptions) => Promise<string>;
|
|
65
65
|
fetchUserShareBalance: ({ connection, publicKey, shareMint, }: {
|
|
66
66
|
publicKey: PublicKey | string;
|
|
67
67
|
shareMint?: PublicKey | string;
|
|
68
|
-
} & import("./types").ISolanaConnectionOptions) => Promise<
|
|
68
|
+
} & import("./types").ISolanaConnectionOptions) => Promise<number>;
|
|
69
69
|
fetchUserShareBalanceRaw: ({ connection, publicKey, shareMint, }: {
|
|
70
70
|
publicKey: PublicKey | string;
|
|
71
71
|
shareMint?: PublicKey | string;
|
|
@@ -119,7 +119,23 @@ declare class SolanaAdapter {
|
|
|
119
119
|
private _network;
|
|
120
120
|
private _connection;
|
|
121
121
|
private _provider;
|
|
122
|
-
|
|
122
|
+
/**
|
|
123
|
+
* @param commitment - Commitment for every read this adapter makes *and* for
|
|
124
|
+
* the confirmation of every write, so the two always describe the same
|
|
125
|
+
* chain state — the vault handlers take it from this same `Connection`.
|
|
126
|
+
*
|
|
127
|
+
* Defaults to `'finalized'`, which is what this adapter has always used in
|
|
128
|
+
* practice: it previously passed no commitment at all, so both reads and
|
|
129
|
+
* confirmations fell through to the RPC's own `'finalized'` default. The
|
|
130
|
+
* default is stated explicitly now rather than inherited, but it is
|
|
131
|
+
* unchanged.
|
|
132
|
+
*
|
|
133
|
+
* Pass `'confirmed'` for a markedly faster round trip — seconds rather
|
|
134
|
+
* than tens of seconds — accepting that the state you act on can still, in
|
|
135
|
+
* principle, be rolled back. Also settable as the `commitment` field of
|
|
136
|
+
* the `solana` config passed to `AugustSDK`.
|
|
137
|
+
*/
|
|
138
|
+
constructor(endpoint: ISolanaRpcEndpoint, network?: ISolanaNetwork, commitment?: Commitment);
|
|
123
139
|
get endpoint(): `https://${string}`;
|
|
124
140
|
get network(): ISolanaNetwork;
|
|
125
141
|
get connection(): web3.Connection;
|
|
@@ -171,8 +187,8 @@ declare class SolanaAdapter {
|
|
|
171
187
|
image: string;
|
|
172
188
|
}>;
|
|
173
189
|
getTokenSymbol(mintAddress: string | PublicKey): Promise<string>;
|
|
174
|
-
fetchUserTokenBalance(publicKey: PublicKey | string, depositMint: PublicKey | string): Promise<
|
|
175
|
-
fetchUserShareBalance(publicKey: PublicKey | string, shareMint: PublicKey | string): Promise<
|
|
190
|
+
fetchUserTokenBalance(publicKey: PublicKey | string, depositMint: PublicKey | string): Promise<string>;
|
|
191
|
+
fetchUserShareBalance(publicKey: PublicKey | string, shareMint: PublicKey | string): Promise<number>;
|
|
176
192
|
/**
|
|
177
193
|
* BigInt-safe variant of {@link fetchUserShareBalance}. Returns the raw u64
|
|
178
194
|
* `amount` (as a base-units string) plus the mint's `decimals` (or `null`
|
|
@@ -212,13 +228,31 @@ declare class SolanaAdapter {
|
|
|
212
228
|
/**
|
|
213
229
|
* Deposit funds into a Solana August vault.
|
|
214
230
|
* @param depositAmount `bigint` (raw on-chain units) or `number` (UI amount).
|
|
231
|
+
* @param sendTransaction - **Ignored; scheduled for removal (AUGUST-7221).**
|
|
232
|
+
* Share-account creation is now prepended to the deposit instruction, so
|
|
233
|
+
* the SDK sends no second transaction and never invokes this callback —
|
|
234
|
+
* signing goes through the provider's wallet.
|
|
235
|
+
*
|
|
236
|
+
* **Pass `undefined` here; do not delete the argument.** It sits *before*
|
|
237
|
+
* `vaultAddress`, so removing it shifts your vault address into this slot
|
|
238
|
+
* and leaves `vaultAddress` undefined — which silently falls back to the
|
|
239
|
+
* legacy single-vault PDA derivation and targets a different vault.
|
|
215
240
|
*/
|
|
216
|
-
vaultDeposit(vaultProgramId: PublicKey | string, idl: any, publicKey: PublicKey | string, depositAmount: number | bigint, sendTransaction
|
|
241
|
+
vaultDeposit(vaultProgramId: PublicKey | string, idl: any, publicKey: PublicKey | string, depositAmount: number | bigint, sendTransaction?: (transaction: Transaction | web3.VersionedTransaction, connection: Connection, options?: SendTransactionOptions) => Promise<web3.TransactionSignature>, vaultAddress?: PublicKey | string): Promise<any>;
|
|
217
242
|
/**
|
|
218
243
|
* Redeem vault shares from a Solana August vault.
|
|
219
244
|
* @param redeemShares `bigint` (raw share units) or `number` (UI amount).
|
|
245
|
+
* @param sendTransaction - **Ignored; scheduled for removal (AUGUST-7221).**
|
|
246
|
+
* Payout- and fee-recipient-account creation is now prepended to the redeem
|
|
247
|
+
* instruction, so the SDK sends no second transaction and never invokes
|
|
248
|
+
* this callback — signing goes through the provider's wallet.
|
|
249
|
+
*
|
|
250
|
+
* **Pass `undefined` here; do not delete the argument.** It sits *before*
|
|
251
|
+
* `vaultAddress`, so removing it shifts your vault address into this slot
|
|
252
|
+
* and leaves `vaultAddress` undefined — which silently falls back to the
|
|
253
|
+
* legacy single-vault PDA derivation and targets a different vault.
|
|
220
254
|
*/
|
|
221
|
-
vaultRedeem(vaultProgramId: PublicKey | string, idl: any, publicKey: PublicKey | string, redeemShares: number | bigint, sendTransaction
|
|
255
|
+
vaultRedeem(vaultProgramId: PublicKey | string, idl: any, publicKey: PublicKey | string, redeemShares: number | bigint, sendTransaction?: (transaction: Transaction | web3.VersionedTransaction, connection: Connection, options?: SendTransactionOptions) => Promise<web3.TransactionSignature>, vaultAddress?: PublicKey | string): Promise<any>;
|
|
222
256
|
/**
|
|
223
257
|
* Canonical program id for a program type on this adapter's network.
|
|
224
258
|
*
|
|
@@ -68,10 +68,29 @@ class SolanaAdapter {
|
|
|
68
68
|
_network;
|
|
69
69
|
_connection;
|
|
70
70
|
_provider;
|
|
71
|
-
|
|
71
|
+
/**
|
|
72
|
+
* @param commitment - Commitment for every read this adapter makes *and* for
|
|
73
|
+
* the confirmation of every write, so the two always describe the same
|
|
74
|
+
* chain state — the vault handlers take it from this same `Connection`.
|
|
75
|
+
*
|
|
76
|
+
* Defaults to `'finalized'`, which is what this adapter has always used in
|
|
77
|
+
* practice: it previously passed no commitment at all, so both reads and
|
|
78
|
+
* confirmations fell through to the RPC's own `'finalized'` default. The
|
|
79
|
+
* default is stated explicitly now rather than inherited, but it is
|
|
80
|
+
* unchanged.
|
|
81
|
+
*
|
|
82
|
+
* Pass `'confirmed'` for a markedly faster round trip — seconds rather
|
|
83
|
+
* than tens of seconds — accepting that the state you act on can still, in
|
|
84
|
+
* principle, be rolled back. Also settable as the `commitment` field of
|
|
85
|
+
* the `solana` config passed to `AugustSDK`.
|
|
86
|
+
*/
|
|
87
|
+
constructor(endpoint, network = utils_1.SolanaUtils.fallbackNetwork, commitment = 'finalized') {
|
|
72
88
|
this._endpoint = endpoint;
|
|
73
89
|
this._network = network;
|
|
74
|
-
|
|
90
|
+
// Explicit rather than inherited: left unset, web3.js omits the parameter
|
|
91
|
+
// and the RPC applies its own default, which is the same 'finalized' —
|
|
92
|
+
// but then nothing ties it to the commitment writes confirm at.
|
|
93
|
+
const connection = new web3_js_1.Connection(endpoint, commitment);
|
|
75
94
|
this._connection = connection;
|
|
76
95
|
this._provider = utils_1.SolanaUtils.getReadOnlyProvider({
|
|
77
96
|
network: this._network,
|
|
@@ -238,6 +257,15 @@ class SolanaAdapter {
|
|
|
238
257
|
/**
|
|
239
258
|
* Deposit funds into a Solana August vault.
|
|
240
259
|
* @param depositAmount `bigint` (raw on-chain units) or `number` (UI amount).
|
|
260
|
+
* @param sendTransaction - **Ignored; scheduled for removal (AUGUST-7221).**
|
|
261
|
+
* Share-account creation is now prepended to the deposit instruction, so
|
|
262
|
+
* the SDK sends no second transaction and never invokes this callback —
|
|
263
|
+
* signing goes through the provider's wallet.
|
|
264
|
+
*
|
|
265
|
+
* **Pass `undefined` here; do not delete the argument.** It sits *before*
|
|
266
|
+
* `vaultAddress`, so removing it shifts your vault address into this slot
|
|
267
|
+
* and leaves `vaultAddress` undefined — which silently falls back to the
|
|
268
|
+
* legacy single-vault PDA derivation and targets a different vault.
|
|
241
269
|
*/
|
|
242
270
|
async vaultDeposit(vaultProgramId, idl, publicKey, depositAmount, sendTransaction, vaultAddress) {
|
|
243
271
|
return await (0, vault_actions_1.handleSolanaDeposit)({
|
|
@@ -255,6 +283,15 @@ class SolanaAdapter {
|
|
|
255
283
|
/**
|
|
256
284
|
* Redeem vault shares from a Solana August vault.
|
|
257
285
|
* @param redeemShares `bigint` (raw share units) or `number` (UI amount).
|
|
286
|
+
* @param sendTransaction - **Ignored; scheduled for removal (AUGUST-7221).**
|
|
287
|
+
* Payout- and fee-recipient-account creation is now prepended to the redeem
|
|
288
|
+
* instruction, so the SDK sends no second transaction and never invokes
|
|
289
|
+
* this callback — signing goes through the provider's wallet.
|
|
290
|
+
*
|
|
291
|
+
* **Pass `undefined` here; do not delete the argument.** It sits *before*
|
|
292
|
+
* `vaultAddress`, so removing it shifts your vault address into this slot
|
|
293
|
+
* and leaves `vaultAddress` undefined — which silently falls back to the
|
|
294
|
+
* legacy single-vault PDA derivation and targets a different vault.
|
|
258
295
|
*/
|
|
259
296
|
async vaultRedeem(vaultProgramId, idl, publicKey, redeemShares, sendTransaction, vaultAddress) {
|
|
260
297
|
return await (0, vault_actions_1.handleSolanaRedeem)({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AnchorProvider, BN, Program, type web3 } from '@coral-xyz/anchor';
|
|
2
|
-
import { PublicKey, type Transaction } from '@solana/web3.js';
|
|
2
|
+
import { PublicKey, type ParsedAccountData, type Transaction } from '@solana/web3.js';
|
|
3
3
|
import type { ISolanaConnectionOptions, ISolanaVaultState } from './types';
|
|
4
4
|
declare function getExplorerLink({ signature, type, network, }: {
|
|
5
5
|
signature: string;
|
|
@@ -58,14 +58,65 @@ declare function getToken({ mintAddress, endpoint, connection, }: {
|
|
|
58
58
|
declare function getTokenSymbol({ endpoint, mintAddress, }: {
|
|
59
59
|
mintAddress: string | PublicKey;
|
|
60
60
|
} & ISolanaConnectionOptions): Promise<string | null>;
|
|
61
|
+
/**
|
|
62
|
+
* A wallet's token account of record for one mint: the account holding the
|
|
63
|
+
* largest balance.
|
|
64
|
+
*
|
|
65
|
+
* **This is the single selection rule for the whole Solana adapter.** Reads
|
|
66
|
+
* (balances shown in a UI) and writes (the account a transfer actually names)
|
|
67
|
+
* must agree on which account represents the user, or the two describe
|
|
68
|
+
* different money: a balance read from account A while a deposit mints into
|
|
69
|
+
* account B looks, to the user, like a successful transaction that never
|
|
70
|
+
* arrived. Every reader in this file and both vault handlers call this, so the
|
|
71
|
+
* rule cannot drift.
|
|
72
|
+
*
|
|
73
|
+
* Largest-balance rather than "the first account the RPC returned":
|
|
74
|
+
* `getParsedTokenAccountsByOwner` guarantees no ordering, so taking `[0]` can
|
|
75
|
+
* select an empty leftover while the user's funds sit in a sibling account —
|
|
76
|
+
* which fails on-chain as an insufficient-funds revert *after* they sign.
|
|
77
|
+
*
|
|
78
|
+
* **Spendable accounts win over frozen ones**, regardless of balance: a token
|
|
79
|
+
* issuer can freeze an account, and neither a transfer out of nor a transfer
|
|
80
|
+
* into a frozen account can succeed. Picking the richest account outright would
|
|
81
|
+
* hand a frozen one to the program and revert with SPL `0x11` after the user
|
|
82
|
+
* signs, while a perfectly usable sibling sat next to it. When *every* account
|
|
83
|
+
* is frozen the richest frozen one is still returned, flagged — a reader should
|
|
84
|
+
* show that balance, because the user does own it, and a writer should refuse
|
|
85
|
+
* before asking for a signature.
|
|
86
|
+
*
|
|
87
|
+
* Internal to the adapter: exported for `vault.actions.ts`, deliberately not
|
|
88
|
+
* re-exported on `SolanaUtils`. It takes a raw RPC response shape, which is not
|
|
89
|
+
* a contract worth freezing on the public surface.
|
|
90
|
+
*
|
|
91
|
+
* @param accounts - The `.value` array from `getParsedTokenAccountsByOwner`.
|
|
92
|
+
* @returns The selected account with its raw balance, decimals, the RPC's own
|
|
93
|
+
* `uiAmount`, and whether it is frozen — or `undefined` when the wallet owns
|
|
94
|
+
* no account for the mint.
|
|
95
|
+
* @throws AugustSDKError when an account's balance cannot be read. An
|
|
96
|
+
* unreadable balance is *unknown*, not zero — scoring it zero would silently
|
|
97
|
+
* demote a funded account below a dust one and reinstate the `[0]` behaviour
|
|
98
|
+
* this function exists to prevent.
|
|
99
|
+
*/
|
|
100
|
+
export declare function selectRichestTokenAccount(accounts: {
|
|
101
|
+
pubkey: PublicKey;
|
|
102
|
+
account: {
|
|
103
|
+
data: ParsedAccountData | Buffer;
|
|
104
|
+
};
|
|
105
|
+
}[]): {
|
|
106
|
+
pubkey: PublicKey;
|
|
107
|
+
amount: bigint;
|
|
108
|
+
decimals: number | null;
|
|
109
|
+
uiAmount: number | null;
|
|
110
|
+
frozen: boolean;
|
|
111
|
+
} | undefined;
|
|
61
112
|
declare function fetchUserTokenBalance({ connection, publicKey, depositMint, }: {
|
|
62
113
|
publicKey: PublicKey | string;
|
|
63
114
|
depositMint?: PublicKey | string;
|
|
64
|
-
} & ISolanaConnectionOptions): Promise<
|
|
115
|
+
} & ISolanaConnectionOptions): Promise<string>;
|
|
65
116
|
declare function fetchUserShareBalance({ connection, publicKey, shareMint, }: {
|
|
66
117
|
publicKey: PublicKey | string;
|
|
67
118
|
shareMint?: PublicKey | string;
|
|
68
|
-
} & ISolanaConnectionOptions): Promise<
|
|
119
|
+
} & ISolanaConnectionOptions): Promise<number>;
|
|
69
120
|
/**
|
|
70
121
|
* Same on-chain lookup as `fetchUserShareBalance`, but returns the raw u64
|
|
71
122
|
* `amount` string and the mint's `decimals` — or `null` when the scale can't
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SolanaUtils = exports.isSolanaAddress = void 0;
|
|
4
|
+
exports.selectRichestTokenAccount = selectRichestTokenAccount;
|
|
4
5
|
exports.uiAmountToRawBn = uiAmountToRawBn;
|
|
5
6
|
const anchor_1 = require("@coral-xyz/anchor");
|
|
6
7
|
const web3_js_1 = require("@solana/web3.js");
|
|
@@ -103,7 +104,11 @@ function getProvider({ connection, publicKey, signTransaction, }) {
|
|
|
103
104
|
const signedTransactions = await Promise.all(txs.map(async (tx) => signTransaction(tx)));
|
|
104
105
|
return signedTransactions;
|
|
105
106
|
},
|
|
106
|
-
},
|
|
107
|
+
},
|
|
108
|
+
// Inherited from the connection, not hard-coded: a caller who configured
|
|
109
|
+
// the adapter for 'finalized' must not have writes issued through
|
|
110
|
+
// `getProgram(...).methods…rpc()` confirm at something weaker.
|
|
111
|
+
{ commitment: connection.commitment ?? 'finalized' });
|
|
107
112
|
}
|
|
108
113
|
// Read-only provider for data fetching without wallet connection
|
|
109
114
|
function getReadOnlyProvider({ connection }) {
|
|
@@ -117,8 +122,10 @@ function getReadOnlyProvider({ connection }) {
|
|
|
117
122
|
throw new core_1.AugustValidationError('INVALID_INPUT', 'Cannot sign transactions with read-only provider');
|
|
118
123
|
},
|
|
119
124
|
};
|
|
125
|
+
// See `getProvider` — the connection is the single source of truth for
|
|
126
|
+
// commitment, so reads through this provider match reads made directly.
|
|
120
127
|
return new anchor_1.AnchorProvider(connection, dummyWallet, {
|
|
121
|
-
commitment: '
|
|
128
|
+
commitment: connection.commitment ?? 'finalized',
|
|
122
129
|
});
|
|
123
130
|
}
|
|
124
131
|
// ============================================================================
|
|
@@ -440,6 +447,87 @@ async function getTokenSymbol({ endpoint, mintAddress, }) {
|
|
|
440
447
|
return null;
|
|
441
448
|
}
|
|
442
449
|
}
|
|
450
|
+
/**
|
|
451
|
+
* A wallet's token account of record for one mint: the account holding the
|
|
452
|
+
* largest balance.
|
|
453
|
+
*
|
|
454
|
+
* **This is the single selection rule for the whole Solana adapter.** Reads
|
|
455
|
+
* (balances shown in a UI) and writes (the account a transfer actually names)
|
|
456
|
+
* must agree on which account represents the user, or the two describe
|
|
457
|
+
* different money: a balance read from account A while a deposit mints into
|
|
458
|
+
* account B looks, to the user, like a successful transaction that never
|
|
459
|
+
* arrived. Every reader in this file and both vault handlers call this, so the
|
|
460
|
+
* rule cannot drift.
|
|
461
|
+
*
|
|
462
|
+
* Largest-balance rather than "the first account the RPC returned":
|
|
463
|
+
* `getParsedTokenAccountsByOwner` guarantees no ordering, so taking `[0]` can
|
|
464
|
+
* select an empty leftover while the user's funds sit in a sibling account —
|
|
465
|
+
* which fails on-chain as an insufficient-funds revert *after* they sign.
|
|
466
|
+
*
|
|
467
|
+
* **Spendable accounts win over frozen ones**, regardless of balance: a token
|
|
468
|
+
* issuer can freeze an account, and neither a transfer out of nor a transfer
|
|
469
|
+
* into a frozen account can succeed. Picking the richest account outright would
|
|
470
|
+
* hand a frozen one to the program and revert with SPL `0x11` after the user
|
|
471
|
+
* signs, while a perfectly usable sibling sat next to it. When *every* account
|
|
472
|
+
* is frozen the richest frozen one is still returned, flagged — a reader should
|
|
473
|
+
* show that balance, because the user does own it, and a writer should refuse
|
|
474
|
+
* before asking for a signature.
|
|
475
|
+
*
|
|
476
|
+
* Internal to the adapter: exported for `vault.actions.ts`, deliberately not
|
|
477
|
+
* re-exported on `SolanaUtils`. It takes a raw RPC response shape, which is not
|
|
478
|
+
* a contract worth freezing on the public surface.
|
|
479
|
+
*
|
|
480
|
+
* @param accounts - The `.value` array from `getParsedTokenAccountsByOwner`.
|
|
481
|
+
* @returns The selected account with its raw balance, decimals, the RPC's own
|
|
482
|
+
* `uiAmount`, and whether it is frozen — or `undefined` when the wallet owns
|
|
483
|
+
* no account for the mint.
|
|
484
|
+
* @throws AugustSDKError when an account's balance cannot be read. An
|
|
485
|
+
* unreadable balance is *unknown*, not zero — scoring it zero would silently
|
|
486
|
+
* demote a funded account below a dust one and reinstate the `[0]` behaviour
|
|
487
|
+
* this function exists to prevent.
|
|
488
|
+
*/
|
|
489
|
+
function selectRichestTokenAccount(accounts) {
|
|
490
|
+
const parsed = accounts.map((entry) => {
|
|
491
|
+
const info = entry.account.data?.parsed?.info;
|
|
492
|
+
const tokenAmount = info?.tokenAmount;
|
|
493
|
+
const raw = tokenAmount?.amount;
|
|
494
|
+
// A u64 string is the only shape the RPC promises here. Anything else
|
|
495
|
+
// (a base64 `data` the node could not jsonParse, a reshaping proxy) means
|
|
496
|
+
// we do not know this balance.
|
|
497
|
+
if (typeof raw !== 'string' || !/^\d+$/.test(raw)) {
|
|
498
|
+
throw new core_1.AugustSDKError('UNKNOWN', `Could not read the balance of token account ${entry.pubkey.toBase58()}. ` +
|
|
499
|
+
'The RPC returned an unrecognized response; please retry or switch RPC.', {
|
|
500
|
+
context: {
|
|
501
|
+
account: entry.pubkey.toBase58(),
|
|
502
|
+
rawAmount: String(raw),
|
|
503
|
+
},
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
return {
|
|
507
|
+
pubkey: entry.pubkey,
|
|
508
|
+
amount: BigInt(raw),
|
|
509
|
+
decimals: typeof tokenAmount?.decimals === 'number' ? tokenAmount.decimals : null,
|
|
510
|
+
// Passed through from the RPC rather than recomputed from `amount`, so
|
|
511
|
+
// the lossy float these legacy readers return is not made lossier.
|
|
512
|
+
uiAmount: typeof tokenAmount?.uiAmount === 'number' ? tokenAmount.uiAmount : null,
|
|
513
|
+
frozen: info?.state === 'frozen',
|
|
514
|
+
};
|
|
515
|
+
});
|
|
516
|
+
const richest = (candidates) => candidates.reduce((best, candidate) => {
|
|
517
|
+
if (!best)
|
|
518
|
+
return candidate;
|
|
519
|
+
if (candidate.amount !== best.amount)
|
|
520
|
+
return candidate.amount > best.amount ? candidate : best;
|
|
521
|
+
// Equal balances: break the tie on the address. Falling back to "whichever
|
|
522
|
+
// the RPC listed first" would leave exactly the ordering dependence this
|
|
523
|
+
// function exists to remove — harmless while the balances match, but it
|
|
524
|
+
// means two calls can name different accounts for the same wallet.
|
|
525
|
+
return candidate.pubkey.toBase58() < best.pubkey.toBase58()
|
|
526
|
+
? candidate
|
|
527
|
+
: best;
|
|
528
|
+
}, undefined);
|
|
529
|
+
return richest(parsed.filter((a) => !a.frozen)) ?? richest(parsed);
|
|
530
|
+
}
|
|
443
531
|
async function fetchUserTokenBalance({ connection, publicKey, depositMint, }) {
|
|
444
532
|
if (!publicKey || !depositMint)
|
|
445
533
|
return '0';
|
|
@@ -449,12 +537,9 @@ async function fetchUserTokenBalance({ connection, publicKey, depositMint, }) {
|
|
|
449
537
|
const tokenAccounts = await connection.getParsedTokenAccountsByOwner(_publicKey, {
|
|
450
538
|
mint: _depositMint,
|
|
451
539
|
});
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
return balance?.toString() || '0';
|
|
456
|
-
}
|
|
457
|
-
return '0';
|
|
540
|
+
// Same rule the write path uses — see `selectRichestTokenAccount`.
|
|
541
|
+
const richest = selectRichestTokenAccount(tokenAccounts.value);
|
|
542
|
+
return richest?.uiAmount?.toString() || '0';
|
|
458
543
|
}
|
|
459
544
|
catch (e) {
|
|
460
545
|
const error = e;
|
|
@@ -473,12 +558,9 @@ async function fetchUserShareBalance({ connection, publicKey, shareMint, }) {
|
|
|
473
558
|
const shareAccounts = await connection.getParsedTokenAccountsByOwner(_publicKey, {
|
|
474
559
|
mint: _shareMint,
|
|
475
560
|
});
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
return balance ?? 0;
|
|
480
|
-
}
|
|
481
|
-
return 0;
|
|
561
|
+
// Same rule the write path uses — see `selectRichestTokenAccount`.
|
|
562
|
+
const richest = selectRichestTokenAccount(shareAccounts.value);
|
|
563
|
+
return richest?.uiAmount ?? 0;
|
|
482
564
|
}
|
|
483
565
|
catch (e) {
|
|
484
566
|
const error = e;
|
|
@@ -519,12 +601,13 @@ async function fetchUserShareBalanceRaw({ connection, publicKey, shareMint, }) {
|
|
|
519
601
|
const shareAccounts = await connection.getParsedTokenAccountsByOwner(_publicKey, {
|
|
520
602
|
mint: _shareMint,
|
|
521
603
|
});
|
|
522
|
-
|
|
523
|
-
|
|
604
|
+
// Same rule the write path uses — see `selectRichestTokenAccount`.
|
|
605
|
+
const richest = selectRichestTokenAccount(shareAccounts?.value ?? []);
|
|
606
|
+
if (!richest)
|
|
524
607
|
return { amount: '0', decimals: null };
|
|
525
608
|
return {
|
|
526
|
-
amount:
|
|
527
|
-
decimals:
|
|
609
|
+
amount: richest.amount.toString(),
|
|
610
|
+
decimals: richest.decimals,
|
|
528
611
|
};
|
|
529
612
|
}
|
|
530
613
|
catch (e) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type web3 } from '@coral-xyz/anchor';
|
|
2
|
-
import { type Connection, PublicKey, Transaction } from '@solana/web3.js';
|
|
2
|
+
import { type Connection, PublicKey, type Transaction } from '@solana/web3.js';
|
|
3
3
|
import type { ISolanaConnectionOptions } from './types';
|
|
4
4
|
import type { SendTransactionOptions } from '@solana/wallet-adapter-base';
|
|
5
5
|
/**
|
|
@@ -26,18 +26,33 @@ export declare function describeSolanaError(e: unknown): string;
|
|
|
26
26
|
* @param depositAmount `bigint` is treated as raw on-chain units (preferred
|
|
27
27
|
* — no JS-float round-trip). `number` is treated as a UI amount and
|
|
28
28
|
* scaled by the deposit mint's decimals via {@link uiAmountToRawBn}.
|
|
29
|
-
* @throws AugustValidationError on missing wallet/programId or invalid amount
|
|
29
|
+
* @throws AugustValidationError on missing wallet/programId or invalid amount,
|
|
30
|
+
* or when the wallet's deposit-mint balance is below `depositAmount`.
|
|
31
|
+
* @throws AugustSDKError when a candidate token account's balance cannot be
|
|
32
|
+
* read, or wrapping any downstream failure (`cause` preserved).
|
|
33
|
+
* @remarks **Side effects.** Submits exactly one transaction, signed by the
|
|
34
|
+
* provider's wallet. When the wallet has no share account, that transaction
|
|
35
|
+
* also creates one, and `publicKey` pays its rent — roughly 0.00204 SOL,
|
|
36
|
+
* non-refundable while the account stays open — on top of the network fee.
|
|
37
|
+
* Callers should ensure the wallet holds enough SOL for both before
|
|
38
|
+
* prompting the user to sign.
|
|
30
39
|
* @remarks The on-chain `deposit` instruction has no `min_shares_out`
|
|
31
40
|
* parameter; slippage cannot be enforced on-chain until the program ships
|
|
32
41
|
* that argument.
|
|
33
42
|
*/
|
|
34
|
-
export declare function handleSolanaDeposit({ provider, connection, network, vaultProgramId, vaultAddress, depositAmount, publicKey,
|
|
43
|
+
export declare function handleSolanaDeposit({ provider, connection, network, vaultProgramId, vaultAddress, depositAmount, publicKey, idl, }: {
|
|
35
44
|
vaultProgramId: PublicKey | string;
|
|
36
45
|
vaultAddress?: PublicKey | string;
|
|
37
46
|
idl: any;
|
|
38
47
|
publicKey: PublicKey | string;
|
|
39
48
|
depositAmount: number | bigint;
|
|
40
|
-
|
|
49
|
+
/**
|
|
50
|
+
* @deprecated Accepted and ignored. Share-account creation is now prepended
|
|
51
|
+
* to the deposit instruction, so there is no longer a second transaction to
|
|
52
|
+
* send. Kept so existing callers keep compiling; will be removed in the next
|
|
53
|
+
* major.
|
|
54
|
+
*/
|
|
55
|
+
sendTransaction?: (transaction: Transaction | web3.VersionedTransaction, connection: Connection, options?: SendTransactionOptions) => Promise<web3.TransactionSignature>;
|
|
41
56
|
} & ISolanaConnectionOptions): Promise<any>;
|
|
42
57
|
/**
|
|
43
58
|
* Redeem vault shares back into the underlying mint on a Solana August vault.
|
|
@@ -45,13 +60,28 @@ export declare function handleSolanaDeposit({ provider, connection, network, vau
|
|
|
45
60
|
* @param redeemShares `bigint` is treated as raw share-token units;
|
|
46
61
|
* `number` is treated as a UI amount. See {@link handleSolanaDeposit} for
|
|
47
62
|
* the same caveat about the missing on-chain slippage guard.
|
|
48
|
-
* @throws AugustValidationError on missing wallet/programId or invalid amount
|
|
63
|
+
* @throws AugustValidationError on missing wallet/programId or invalid amount,
|
|
64
|
+
* or when the wallet holds fewer shares than `redeemShares`.
|
|
65
|
+
* @throws AugustSDKError when a candidate token account's balance cannot be
|
|
66
|
+
* read, or wrapping any downstream failure (`cause` preserved).
|
|
67
|
+
* @remarks **Side effects.** Submits exactly one transaction, signed by the
|
|
68
|
+
* provider's wallet. That transaction also creates the payout account when
|
|
69
|
+
* the wallet has none, and the vault's fee-recipient account when *it* has
|
|
70
|
+
* none — `publicKey` pays rent for both, roughly 0.00204 SOL each and
|
|
71
|
+
* non-refundable, on top of the network fee. Callers should ensure the
|
|
72
|
+
* wallet holds enough SOL for all of it before prompting the user to sign.
|
|
49
73
|
*/
|
|
50
|
-
export declare function handleSolanaRedeem({ provider, connection, vaultProgramId, vaultAddress, publicKey, redeemShares,
|
|
74
|
+
export declare function handleSolanaRedeem({ provider, connection, vaultProgramId, vaultAddress, publicKey, redeemShares, idl, }: {
|
|
51
75
|
idl: any;
|
|
52
76
|
vaultProgramId: PublicKey | string;
|
|
53
77
|
vaultAddress?: PublicKey | string;
|
|
54
78
|
publicKey: PublicKey | string;
|
|
55
79
|
redeemShares: number | bigint;
|
|
56
|
-
|
|
80
|
+
/**
|
|
81
|
+
* @deprecated Accepted and ignored. Payout- and fee-recipient-account
|
|
82
|
+
* creation is now prepended to the redeem instruction, so there is no longer
|
|
83
|
+
* a second transaction to send. Kept so existing callers keep compiling;
|
|
84
|
+
* will be removed in the next major.
|
|
85
|
+
*/
|
|
86
|
+
sendTransaction?: (transaction: Transaction | web3.VersionedTransaction, connection: Connection, options?: SendTransactionOptions) => Promise<web3.TransactionSignature>;
|
|
57
87
|
} & ISolanaConnectionOptions): Promise<any>;
|
|
@@ -49,18 +49,114 @@ function describeSolanaError(e) {
|
|
|
49
49
|
? str
|
|
50
50
|
: REASONLESS_SOLANA_ERROR;
|
|
51
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Pick which token account to hand the vault program for a given mint, and
|
|
54
|
+
* describe how to create it when the user has none.
|
|
55
|
+
*
|
|
56
|
+
* The vault program constrains these accounts by `token::mint` +
|
|
57
|
+
* `token::authority` (not `associated_token::…`), so any account the signer
|
|
58
|
+
* owns qualifies. We therefore keep an existing account when there is one —
|
|
59
|
+
* that is where the user's balance already lives — and fall back to the
|
|
60
|
+
* associated token account otherwise.
|
|
61
|
+
*
|
|
62
|
+
* Two deliberate choices:
|
|
63
|
+
*
|
|
64
|
+
* - **The create is returned, not sent.** Prepended to the deposit/redeem via
|
|
65
|
+
* `.preInstructions()` it is atomic with the transfer, so there is no window
|
|
66
|
+
* in which the vault instruction can reference an account that has not
|
|
67
|
+
* landed. Sent as its own transaction — as this module used to — it raced
|
|
68
|
+
* its own confirmation. Observed in production (AUGUST-7221, 2026-08-11):
|
|
69
|
+
* a deposit failed with `Simulation failed … Logs: []` while the share-ATA
|
|
70
|
+
* create was still in flight, then succeeded 17s later on retry. The empty
|
|
71
|
+
* log array means the transaction never reached program execution, so there
|
|
72
|
+
* was nothing on-chain to debug from.
|
|
73
|
+
* - **Idempotent.** `createAssociatedTokenAccountIdempotentInstruction` is a
|
|
74
|
+
* no-op when the account already exists, so a concurrent create — another
|
|
75
|
+
* tab, a wallet's auto-provisioning — cannot turn into an
|
|
76
|
+
* `SystemError::AccountAlreadyInUse` ("already in use") failure between our
|
|
77
|
+
* read and our submit.
|
|
78
|
+
*
|
|
79
|
+
* Which account wins when several exist is decided by
|
|
80
|
+
* {@link selectRichestTokenAccount} — the same rule every balance *read* in
|
|
81
|
+
* this adapter uses, so the account a UI reports and the account a transfer
|
|
82
|
+
* names are never different accounts.
|
|
83
|
+
*
|
|
84
|
+
* Classic SPL Token only: the ATA is derived under `TOKEN_PROGRAM_ID`, matching
|
|
85
|
+
* the `tokenProgram` both handlers pass, even though the program's
|
|
86
|
+
* `TokenInterface` would also accept Token-2022.
|
|
87
|
+
*
|
|
88
|
+
* @param owner - Wallet that owns the account and pays rent for any creation.
|
|
89
|
+
* @throws AugustSDKError when a candidate account's balance cannot be read
|
|
90
|
+
* (propagated from the shared selector), and `TokenOwnerOffCurveError` when
|
|
91
|
+
* `owner` is off-curve and an ATA has to be derived.
|
|
92
|
+
*/
|
|
93
|
+
async function resolveUserTokenAccount({ connection, owner, mint, }) {
|
|
94
|
+
const existing = await connection.getParsedTokenAccountsByOwner(owner, {
|
|
95
|
+
mint,
|
|
96
|
+
});
|
|
97
|
+
const richest = (0, utils_1.selectRichestTokenAccount)(existing.value);
|
|
98
|
+
if (richest)
|
|
99
|
+
return {
|
|
100
|
+
address: richest.pubkey,
|
|
101
|
+
balance: richest.amount,
|
|
102
|
+
frozen: richest.frozen,
|
|
103
|
+
};
|
|
104
|
+
const ata = await (0, spl_token_1.getAssociatedTokenAddress)(mint, owner);
|
|
105
|
+
return {
|
|
106
|
+
address: ata,
|
|
107
|
+
balance: BigInt(0),
|
|
108
|
+
frozen: false,
|
|
109
|
+
createIx: (0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(owner, // payer
|
|
110
|
+
ata, // associated token account
|
|
111
|
+
owner, // owner
|
|
112
|
+
mint),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Resolve the two token accounts a vault instruction needs, concurrently.
|
|
117
|
+
*
|
|
118
|
+
* The lookups are independent, so running them in sequence doubles the
|
|
119
|
+
* round-trip latency on the hot path for no benefit — both results are needed
|
|
120
|
+
* before anything is submitted either way.
|
|
121
|
+
*
|
|
122
|
+
* Rejections are surfaced in argument order rather than in whichever order the
|
|
123
|
+
* RPC happened to fail, so the error a user sees does not depend on network
|
|
124
|
+
* timing. `Promise.all` would reject with whichever call failed *first*, which
|
|
125
|
+
* on a malformed RPC response (both lookups throwing) would name a different
|
|
126
|
+
* account run to run.
|
|
127
|
+
*
|
|
128
|
+
* @param mints - `[first, second]`; the returned tuple matches this order.
|
|
129
|
+
*/
|
|
130
|
+
async function resolveUserTokenAccountPair({ connection, owner, mints, }) {
|
|
131
|
+
const settled = await Promise.allSettled(mints.map((mint) => resolveUserTokenAccount({ connection, owner, mint })));
|
|
132
|
+
const resolved = settled.map((outcome) => {
|
|
133
|
+
if (outcome.status === 'rejected')
|
|
134
|
+
throw outcome.reason;
|
|
135
|
+
return outcome.value;
|
|
136
|
+
});
|
|
137
|
+
return [resolved[0], resolved[1]];
|
|
138
|
+
}
|
|
52
139
|
/**
|
|
53
140
|
* Deposit funds into a Solana August vault and mint share tokens.
|
|
54
141
|
*
|
|
55
142
|
* @param depositAmount `bigint` is treated as raw on-chain units (preferred
|
|
56
143
|
* — no JS-float round-trip). `number` is treated as a UI amount and
|
|
57
144
|
* scaled by the deposit mint's decimals via {@link uiAmountToRawBn}.
|
|
58
|
-
* @throws AugustValidationError on missing wallet/programId or invalid amount
|
|
145
|
+
* @throws AugustValidationError on missing wallet/programId or invalid amount,
|
|
146
|
+
* or when the wallet's deposit-mint balance is below `depositAmount`.
|
|
147
|
+
* @throws AugustSDKError when a candidate token account's balance cannot be
|
|
148
|
+
* read, or wrapping any downstream failure (`cause` preserved).
|
|
149
|
+
* @remarks **Side effects.** Submits exactly one transaction, signed by the
|
|
150
|
+
* provider's wallet. When the wallet has no share account, that transaction
|
|
151
|
+
* also creates one, and `publicKey` pays its rent — roughly 0.00204 SOL,
|
|
152
|
+
* non-refundable while the account stays open — on top of the network fee.
|
|
153
|
+
* Callers should ensure the wallet holds enough SOL for both before
|
|
154
|
+
* prompting the user to sign.
|
|
59
155
|
* @remarks The on-chain `deposit` instruction has no `min_shares_out`
|
|
60
156
|
* parameter; slippage cannot be enforced on-chain until the program ships
|
|
61
157
|
* that argument.
|
|
62
158
|
*/
|
|
63
|
-
async function handleSolanaDeposit({ provider, connection, network = constants_1.fallbackNetwork, vaultProgramId, vaultAddress, depositAmount, publicKey,
|
|
159
|
+
async function handleSolanaDeposit({ provider, connection, network = constants_1.fallbackNetwork, vaultProgramId, vaultAddress, depositAmount, publicKey, idl, }) {
|
|
64
160
|
try {
|
|
65
161
|
if (!publicKey)
|
|
66
162
|
throw new core_1.AugustValidationError('INVALID_INPUT', 'handleSolanaDeposit: wallet not connected');
|
|
@@ -119,50 +215,70 @@ async function handleSolanaDeposit({ provider, connection, network = constants_1
|
|
|
119
215
|
}
|
|
120
216
|
depositAmountRaw = (0, utils_1.uiAmountToRawBn)(depositAmount, decimals);
|
|
121
217
|
}
|
|
122
|
-
//
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
218
|
+
// Re-check *after* scaling. The guard above tests the caller's number, but
|
|
219
|
+
// a positive UI amount below one raw unit (1e-7 into a 6-decimal mint)
|
|
220
|
+
// truncates to zero here. Zero would then pass the balance gate below
|
|
221
|
+
// (`0n < 0n` is false) and submit a no-op transfer — from an account that
|
|
222
|
+
// may not even exist, since a zero balance is also what an absent account
|
|
223
|
+
// reports.
|
|
224
|
+
if (depositAmountRaw.lten(0)) {
|
|
225
|
+
throw new core_1.AugustValidationError('INVALID_INPUT', `Deposit amount is too small: ${depositAmount} is below one raw unit ` +
|
|
226
|
+
`of a ${decimals}-decimal mint.`);
|
|
227
|
+
}
|
|
228
|
+
// Match whatever this connection *reads* at. A `Connection` built without
|
|
229
|
+
// a commitment omits the parameter, so its reads take the RPC's default —
|
|
230
|
+
// `finalized`. Confirming writes at `confirmed` there would put reads
|
|
231
|
+
// behind writes again, which is the mismatch this whole change removes.
|
|
232
|
+
// `SolanaAdapter` always sets one explicitly, so this fallback only
|
|
233
|
+
// applies to callers reaching these handlers directly via `Solana.actions`.
|
|
234
|
+
const txCommitment = connection.commitment ?? 'finalized';
|
|
235
|
+
// `depositAccount` funds the transfer; `shareAccount` receives the minted
|
|
236
|
+
// shares. Independent lookups, so they run concurrently — but the guards
|
|
237
|
+
// below stay in a fixed order so the first problem a user hits is always
|
|
238
|
+
// reported the same way.
|
|
239
|
+
const [depositAccount, shareAccount] = await resolveUserTokenAccountPair({
|
|
240
|
+
connection,
|
|
241
|
+
owner: _publicKey,
|
|
242
|
+
mints: [_depositMint, shareMintAddr],
|
|
129
243
|
});
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
244
|
+
// Gate on the balance, not on whether an account exists: an empty leftover
|
|
245
|
+
// account is a real state, and letting it through only trades this error
|
|
246
|
+
// for an insufficient-funds revert the user pays a signature to discover.
|
|
247
|
+
// The balance is already in hand from the lookup, so this costs no extra
|
|
248
|
+
// RPC.
|
|
249
|
+
if (depositAccount.frozen) {
|
|
250
|
+
throw new core_1.AugustValidationError('INVALID_INPUT', 'Your token account for the deposit mint is frozen by the token issuer, ' +
|
|
251
|
+
'so the deposit cannot be transferred out of it.');
|
|
133
252
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
// Create share account if it doesn't exist
|
|
138
|
-
senderShareAccount = await (0, spl_token_1.getAssociatedTokenAddress)(shareMintAddr, _publicKey);
|
|
139
|
-
core_1.Logger.log.info('handleSolanaDeposit', 'Sender share account', senderShareAccount.toBase58());
|
|
140
|
-
// Create the associated token account
|
|
141
|
-
const createAtaIx = (0, spl_token_1.createAssociatedTokenAccountInstruction)(_publicKey, // payer
|
|
142
|
-
senderShareAccount, // associated token account
|
|
143
|
-
_publicKey, // owner
|
|
144
|
-
shareMintAddr);
|
|
145
|
-
core_1.Logger.log.info('handleSolanaDeposit', 'Create ATA Ix', createAtaIx.programId.toBase58());
|
|
146
|
-
const transaction = new web3_js_1.Transaction().add(createAtaIx);
|
|
147
|
-
const signature = await sendTransaction(transaction, connection);
|
|
148
|
-
core_1.Logger.log.info('handleSolanaDeposit', 'Created share account:', signature.toString());
|
|
253
|
+
if (depositAccount.balance < BigInt(depositAmountRaw.toString())) {
|
|
254
|
+
throw new core_1.AugustValidationError('INVALID_INPUT', `Insufficient balance to deposit: this wallet holds ${depositAccount.balance} ` +
|
|
255
|
+
`raw units of the deposit mint, and the deposit requires ${depositAmountRaw.toString()}.`);
|
|
149
256
|
}
|
|
150
|
-
|
|
151
|
-
|
|
257
|
+
const senderTokenAccount = depositAccount.address;
|
|
258
|
+
// A missing share account is routine — every first-time depositor starts
|
|
259
|
+
// here, as does anyone whose account was closed after redeeming their full
|
|
260
|
+
// balance — so it is created inline rather than rejecting the deposit.
|
|
261
|
+
// Minting into a *frozen* one fails just as transferring out of one does,
|
|
262
|
+
// so this destination needs the same guard as the funding account above.
|
|
263
|
+
if (shareAccount.frozen) {
|
|
264
|
+
throw new core_1.AugustValidationError('INVALID_INPUT', 'Your share account is frozen by the token issuer, so it cannot ' +
|
|
265
|
+
'receive the shares this deposit would mint.');
|
|
152
266
|
}
|
|
153
|
-
const
|
|
154
|
-
|
|
267
|
+
const senderShareAccount = shareAccount.address;
|
|
268
|
+
const preInstructions = shareAccount.createIx
|
|
269
|
+
? [shareAccount.createIx]
|
|
270
|
+
: [];
|
|
271
|
+
core_1.Logger.log.info('handleSolanaDeposit', 'Sender token account', senderTokenAccount.toBase58());
|
|
272
|
+
core_1.Logger.log.info('handleSolanaDeposit', shareAccount.createIx
|
|
273
|
+
? 'Sender share account (created in this tx)'
|
|
274
|
+
: 'Sender share account (existing)', senderShareAccount.toBase58());
|
|
155
275
|
core_1.Logger.log.info('handleSolanaDeposit', '\n\nDeposit TX Params:\n');
|
|
156
276
|
core_1.Logger.log.info('handleSolanaDeposit', 'Program', depositAmountRaw);
|
|
157
277
|
core_1.Logger.log.info('handleSolanaDeposit', 'Vault state PDA', vaultStatePda.toBase58());
|
|
158
278
|
core_1.Logger.log.info('handleSolanaDeposit', 'Vault token ATA PDA', vaultTokenAtaPda.toBase58());
|
|
159
|
-
core_1.Logger.log.info('handleSolanaDeposit', 'Sender token account', senderTokenAccount?.toBase58());
|
|
160
|
-
core_1.Logger.log.info('handleSolanaDeposit', 'Sender share account', senderShareAccount?.toBase58());
|
|
161
279
|
core_1.Logger.log.info('handleSolanaDeposit', 'Share mint', shareMintAddr.toBase58());
|
|
162
280
|
core_1.Logger.log.info('handleSolanaDeposit', 'Deposit mint', _depositMint?.toBase58());
|
|
163
281
|
core_1.Logger.log.info('handleSolanaDeposit', 'Signer', _publicKey.toBase58());
|
|
164
|
-
// Get a fresh blockhash to avoid "Blockhash not found" errors
|
|
165
|
-
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('confirmed');
|
|
166
282
|
// TODO(slippage): pass `min_shares_out` here when the program ships it.
|
|
167
283
|
const tx = await program.methods
|
|
168
284
|
.deposit(depositAmountRaw)
|
|
@@ -172,21 +288,25 @@ async function handleSolanaDeposit({ provider, connection, network = constants_1
|
|
|
172
288
|
senderTokenAccount,
|
|
173
289
|
senderShareAccount,
|
|
174
290
|
shareMint: shareMintAddr,
|
|
175
|
-
depositMint:
|
|
176
|
-
signer:
|
|
291
|
+
depositMint: _depositMint,
|
|
292
|
+
signer: _publicKey,
|
|
177
293
|
tokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
|
|
178
294
|
})
|
|
295
|
+
.preInstructions(preInstructions)
|
|
296
|
+
// `.rpc()` builds, signs, submits and confirms in one step. The extra
|
|
297
|
+
// `getLatestBlockhash` + `confirmTransaction` this module used to run
|
|
298
|
+
// afterwards re-confirmed an already-confirmed signature against a
|
|
299
|
+
// blockhash the transaction never carried.
|
|
300
|
+
//
|
|
301
|
+
// Commitment is taken from the same `Connection` every read in this
|
|
302
|
+
// adapter uses, so a caller who asks for 'finalized' gets it on both
|
|
303
|
+
// sides rather than finalized reads racing confirmed writes.
|
|
179
304
|
.rpc({
|
|
180
305
|
skipPreflight: false,
|
|
181
|
-
preflightCommitment:
|
|
306
|
+
preflightCommitment: txCommitment,
|
|
307
|
+
commitment: txCommitment,
|
|
182
308
|
});
|
|
183
309
|
core_1.Logger.log.info('handleSolanaDeposit', 'Deposit successful:', tx);
|
|
184
|
-
// Wait for confirmation with the blockhash we used
|
|
185
|
-
await connection.confirmTransaction({
|
|
186
|
-
signature: tx,
|
|
187
|
-
blockhash,
|
|
188
|
-
lastValidBlockHeight,
|
|
189
|
-
}, 'confirmed');
|
|
190
310
|
return tx;
|
|
191
311
|
}
|
|
192
312
|
catch (e) {
|
|
@@ -216,9 +336,18 @@ async function handleSolanaDeposit({ provider, connection, network = constants_1
|
|
|
216
336
|
* @param redeemShares `bigint` is treated as raw share-token units;
|
|
217
337
|
* `number` is treated as a UI amount. See {@link handleSolanaDeposit} for
|
|
218
338
|
* the same caveat about the missing on-chain slippage guard.
|
|
219
|
-
* @throws AugustValidationError on missing wallet/programId or invalid amount
|
|
339
|
+
* @throws AugustValidationError on missing wallet/programId or invalid amount,
|
|
340
|
+
* or when the wallet holds fewer shares than `redeemShares`.
|
|
341
|
+
* @throws AugustSDKError when a candidate token account's balance cannot be
|
|
342
|
+
* read, or wrapping any downstream failure (`cause` preserved).
|
|
343
|
+
* @remarks **Side effects.** Submits exactly one transaction, signed by the
|
|
344
|
+
* provider's wallet. That transaction also creates the payout account when
|
|
345
|
+
* the wallet has none, and the vault's fee-recipient account when *it* has
|
|
346
|
+
* none — `publicKey` pays rent for both, roughly 0.00204 SOL each and
|
|
347
|
+
* non-refundable, on top of the network fee. Callers should ensure the
|
|
348
|
+
* wallet holds enough SOL for all of it before prompting the user to sign.
|
|
220
349
|
*/
|
|
221
|
-
async function handleSolanaRedeem({ provider, connection, vaultProgramId, vaultAddress, publicKey, redeemShares,
|
|
350
|
+
async function handleSolanaRedeem({ provider, connection, vaultProgramId, vaultAddress, publicKey, redeemShares, idl, }) {
|
|
222
351
|
try {
|
|
223
352
|
if (!publicKey)
|
|
224
353
|
throw new core_1.AugustValidationError('INVALID_INPUT', 'handleSolanaRedeem: wallet not connected');
|
|
@@ -276,21 +405,48 @@ async function handleSolanaRedeem({ provider, connection, vaultProgramId, vaultA
|
|
|
276
405
|
}
|
|
277
406
|
redeemSharesRaw = (0, utils_1.uiAmountToRawBn)(redeemShares, shareDecimals);
|
|
278
407
|
}
|
|
279
|
-
//
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
408
|
+
// See `handleSolanaDeposit` — a positive UI amount can truncate to zero,
|
|
409
|
+
// which would pass the share-balance gate below and burn nothing.
|
|
410
|
+
if (redeemSharesRaw.lten(0)) {
|
|
411
|
+
throw new core_1.AugustValidationError('INVALID_INPUT', `Redemption amount is too small: ${redeemShares} is below one raw unit ` +
|
|
412
|
+
`of a ${shareDecimals}-decimal share mint.`);
|
|
413
|
+
}
|
|
414
|
+
// Match whatever this connection *reads* at. A `Connection` built without
|
|
415
|
+
// a commitment omits the parameter, so its reads take the RPC's default —
|
|
416
|
+
// `finalized`. Confirming writes at `confirmed` there would put reads
|
|
417
|
+
// behind writes again, which is the mismatch this whole change removes.
|
|
418
|
+
// `SolanaAdapter` always sets one explicitly, so this fallback only
|
|
419
|
+
// applies to callers reaching these handlers directly via `Solana.actions`.
|
|
420
|
+
const txCommitment = connection.commitment ?? 'finalized';
|
|
421
|
+
// `shareAccount` is burned from; `payoutAccount` receives the underlying.
|
|
422
|
+
// Independent lookups, so they run concurrently — guards stay ordered.
|
|
423
|
+
const [shareAccount, payoutAccount] = await resolveUserTokenAccountPair({
|
|
424
|
+
connection,
|
|
425
|
+
owner: _publicKey,
|
|
426
|
+
mints: [shareMintAddr, _depositMint],
|
|
285
427
|
});
|
|
286
|
-
|
|
287
|
-
|
|
428
|
+
// Gated on balance for the same reason as the deposit's funding account.
|
|
429
|
+
if (shareAccount.frozen) {
|
|
430
|
+
throw new core_1.AugustValidationError('INVALID_INPUT', 'Your share account is frozen by the token issuer, so these shares ' +
|
|
431
|
+
'cannot be redeemed.');
|
|
432
|
+
}
|
|
433
|
+
if (shareAccount.balance < BigInt(redeemSharesRaw.toString())) {
|
|
434
|
+
throw new core_1.AugustValidationError('INVALID_INPUT', `Insufficient shares to redeem: this wallet holds ${shareAccount.balance} ` +
|
|
435
|
+
`raw share units, and the redemption requires ${redeemSharesRaw.toString()}.`);
|
|
288
436
|
}
|
|
289
|
-
|
|
290
|
-
|
|
437
|
+
const senderShareAccount = shareAccount.address;
|
|
438
|
+
// Redeeming is precisely how a holder first receives the deposit mint, so
|
|
439
|
+
// requiring the payout account to pre-exist (as this module used to) left
|
|
440
|
+
// shares unredeemable through this SDK for everyone who acquired them any
|
|
441
|
+
// way other than depositing from this same wallet — a transfer, an
|
|
442
|
+
// airdrop, a market buy — as well as any depositor who later closed their
|
|
443
|
+
// deposit-mint account. A frozen account cannot receive either, so the
|
|
444
|
+
// payout would revert.
|
|
445
|
+
if (payoutAccount.frozen) {
|
|
446
|
+
throw new core_1.AugustValidationError('INVALID_INPUT', 'Your token account for the deposit mint is frozen by the token issuer, ' +
|
|
447
|
+
'so it cannot receive the redemption proceeds.');
|
|
291
448
|
}
|
|
292
|
-
const senderTokenAccount =
|
|
293
|
-
const senderShareAccount = userShareAccounts?.value[0]?.pubkey;
|
|
449
|
+
const senderTokenAccount = payoutAccount.address;
|
|
294
450
|
// Get the fee recipient from vault state
|
|
295
451
|
const readOnlyProvider = utils_1.SolanaUtils.getReadOnlyProvider({ connection });
|
|
296
452
|
const readOnlyProgram = utils_1.SolanaUtils.getProgram({
|
|
@@ -301,22 +457,20 @@ async function handleSolanaRedeem({ provider, connection, vaultProgramId, vaultA
|
|
|
301
457
|
const vaultStateData = await readOnlyProgram.account.vaultState.fetch(vaultStatePda);
|
|
302
458
|
// Get the fee recipient's token account
|
|
303
459
|
const feeRecipientTokenAccount = await (0, spl_token_1.getAssociatedTokenAddress)(_depositMint, vaultStateData.feeRecipient);
|
|
304
|
-
//
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
460
|
+
// Every account this handler might have to create is created in the same
|
|
461
|
+
// transaction that uses it. The fee recipient's ATA is created
|
|
462
|
+
// unconditionally: the idempotent instruction is a no-op when the account
|
|
463
|
+
// already exists (costing the user nothing), which buys us one fewer RPC
|
|
464
|
+
// round trip and removes the read-then-create window entirely. The user
|
|
465
|
+
// pays the rent when it is genuinely absent, as before.
|
|
466
|
+
const preInstructions = [
|
|
467
|
+
(0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(_publicKey, // payer (user pays for the account creation)
|
|
308
468
|
feeRecipientTokenAccount, // associated token account
|
|
309
469
|
vaultStateData.feeRecipient, // owner (the actual fee recipient)
|
|
310
|
-
_depositMint)
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
// Wait a moment for the account to be created
|
|
315
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
316
|
-
}
|
|
317
|
-
else {
|
|
318
|
-
core_1.Logger.log.info('handleSolanaRedeem', 'Fee recipient token account already exists');
|
|
319
|
-
}
|
|
470
|
+
_depositMint),
|
|
471
|
+
];
|
|
472
|
+
if (payoutAccount.createIx)
|
|
473
|
+
preInstructions.push(payoutAccount.createIx);
|
|
320
474
|
core_1.Logger.log.info('handleSolanaRedeem', '\n\nRedeem TX Params:\n');
|
|
321
475
|
core_1.Logger.log.info('handleSolanaRedeem', 'Redeem shares (UI):', redeemShares);
|
|
322
476
|
core_1.Logger.log.info('handleSolanaRedeem', 'Redeem shares (raw):', redeemSharesRaw);
|
|
@@ -329,8 +483,6 @@ async function handleSolanaRedeem({ provider, connection, vaultProgramId, vaultA
|
|
|
329
483
|
core_1.Logger.log.info('handleSolanaRedeem', 'Share mint:', shareMintAddr.toBase58());
|
|
330
484
|
core_1.Logger.log.info('handleSolanaRedeem', 'Deposit mint:', _depositMint?.toBase58());
|
|
331
485
|
core_1.Logger.log.info('handleSolanaRedeem', 'Signer:', _publicKey.toBase58());
|
|
332
|
-
// Get a fresh blockhash to avoid "Blockhash not found" errors
|
|
333
|
-
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('confirmed');
|
|
334
486
|
const tx = await program.methods
|
|
335
487
|
.redeem(redeemSharesRaw)
|
|
336
488
|
.accounts({
|
|
@@ -344,17 +496,15 @@ async function handleSolanaRedeem({ provider, connection, vaultProgramId, vaultA
|
|
|
344
496
|
signer: _publicKey,
|
|
345
497
|
tokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
|
|
346
498
|
})
|
|
499
|
+
.preInstructions(preInstructions)
|
|
500
|
+
// See `handleSolanaDeposit` for why `.rpc()` owns the confirmation and
|
|
501
|
+
// why the commitment comes from the connection.
|
|
347
502
|
.rpc({
|
|
348
503
|
skipPreflight: false,
|
|
349
|
-
preflightCommitment:
|
|
504
|
+
preflightCommitment: txCommitment,
|
|
505
|
+
commitment: txCommitment,
|
|
350
506
|
});
|
|
351
507
|
core_1.Logger.log.info('handleSolanaRedeem', 'Redeem successful:', tx);
|
|
352
|
-
// Wait for confirmation with the blockhash we used
|
|
353
|
-
await connection.confirmTransaction({
|
|
354
|
-
signature: tx,
|
|
355
|
-
blockhash,
|
|
356
|
-
lastValidBlockHeight,
|
|
357
|
-
}, 'confirmed');
|
|
358
508
|
return tx;
|
|
359
509
|
}
|
|
360
510
|
catch (e) {
|
package/lib/main.js
CHANGED
|
@@ -90,21 +90,27 @@ class AugustSDK extends core_1.AugustBase {
|
|
|
90
90
|
return {
|
|
91
91
|
rpcUrl: baseConfig.solana.rpcUrl,
|
|
92
92
|
network: baseConfig.solana.network,
|
|
93
|
+
commitment: baseConfig.solana.commitment,
|
|
93
94
|
};
|
|
94
95
|
}
|
|
95
96
|
const legacyRpcUrl = baseConfig.providers?.[core_1.SPECIAL_CHAINS.solana.chainId];
|
|
96
97
|
if (legacyRpcUrl) {
|
|
98
|
+
// No commitment here: `ISolanaConfig` requires `rpcUrl` + `network`, so
|
|
99
|
+
// a commitment-only `solana` object alongside this legacy path is not
|
|
100
|
+
// expressible in TypeScript. Set `solana: { rpcUrl, network,
|
|
101
|
+
// commitment }` to configure it.
|
|
97
102
|
return {
|
|
98
103
|
rpcUrl: legacyRpcUrl,
|
|
99
104
|
network: legacyRpcUrl.includes('devnet')
|
|
100
105
|
? 'devnet'
|
|
101
106
|
: 'mainnet-beta',
|
|
107
|
+
commitment: undefined,
|
|
102
108
|
};
|
|
103
109
|
}
|
|
104
110
|
return null;
|
|
105
111
|
})();
|
|
106
112
|
if (solanaConfig) {
|
|
107
|
-
this.solana = new solana_1.default(solanaConfig.rpcUrl, solanaConfig.network);
|
|
113
|
+
this.solana = new solana_1.default(solanaConfig.rpcUrl, solanaConfig.network, solanaConfig.commitment);
|
|
108
114
|
(0, analytics_1.instrumentClass)(this.solana, () => core_1.SPECIAL_CHAINS.solana.chainId);
|
|
109
115
|
}
|
|
110
116
|
this.sui = new sui_1.default();
|
package/lib/sdk.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { Abi } from 'abitype';
|
|
|
2
2
|
import type { AbiParametersToPrimitiveTypes } from 'abitype';
|
|
3
3
|
import type { AnchorProvider } from '@coral-xyz/anchor';
|
|
4
4
|
import type { BaseContract } from 'ethers';
|
|
5
|
+
import { Commitment } from '@solana/web3.js';
|
|
5
6
|
import { Connection } from '@solana/web3.js';
|
|
6
7
|
import type { ContractTransaction } from 'ethers';
|
|
7
8
|
import type { ContractTransactionResponse } from 'ethers';
|
|
@@ -19436,18 +19437,33 @@ declare type AsArray<T> = T extends readonly unknown[] ? T : never;
|
|
|
19436
19437
|
* @param depositAmount `bigint` is treated as raw on-chain units (preferred
|
|
19437
19438
|
* — no JS-float round-trip). `number` is treated as a UI amount and
|
|
19438
19439
|
* scaled by the deposit mint's decimals via {@link uiAmountToRawBn}.
|
|
19439
|
-
* @throws AugustValidationError on missing wallet/programId or invalid amount
|
|
19440
|
+
* @throws AugustValidationError on missing wallet/programId or invalid amount,
|
|
19441
|
+
* or when the wallet's deposit-mint balance is below `depositAmount`.
|
|
19442
|
+
* @throws AugustSDKError when a candidate token account's balance cannot be
|
|
19443
|
+
* read, or wrapping any downstream failure (`cause` preserved).
|
|
19444
|
+
* @remarks **Side effects.** Submits exactly one transaction, signed by the
|
|
19445
|
+
* provider's wallet. When the wallet has no share account, that transaction
|
|
19446
|
+
* also creates one, and `publicKey` pays its rent — roughly 0.00204 SOL,
|
|
19447
|
+
* non-refundable while the account stays open — on top of the network fee.
|
|
19448
|
+
* Callers should ensure the wallet holds enough SOL for both before
|
|
19449
|
+
* prompting the user to sign.
|
|
19440
19450
|
* @remarks The on-chain `deposit` instruction has no `min_shares_out`
|
|
19441
19451
|
* parameter; slippage cannot be enforced on-chain until the program ships
|
|
19442
19452
|
* that argument.
|
|
19443
19453
|
*/
|
|
19444
|
-
declare function handleSolanaDeposit({ provider, connection, network, vaultProgramId, vaultAddress, depositAmount, publicKey,
|
|
19454
|
+
declare function handleSolanaDeposit({ provider, connection, network, vaultProgramId, vaultAddress, depositAmount, publicKey, idl, }: {
|
|
19445
19455
|
vaultProgramId: PublicKey | string;
|
|
19446
19456
|
vaultAddress?: PublicKey | string;
|
|
19447
19457
|
idl: any;
|
|
19448
19458
|
publicKey: PublicKey | string;
|
|
19449
19459
|
depositAmount: number | bigint;
|
|
19450
|
-
|
|
19460
|
+
/**
|
|
19461
|
+
* @deprecated Accepted and ignored. Share-account creation is now prepended
|
|
19462
|
+
* to the deposit instruction, so there is no longer a second transaction to
|
|
19463
|
+
* send. Kept so existing callers keep compiling; will be removed in the next
|
|
19464
|
+
* major.
|
|
19465
|
+
*/
|
|
19466
|
+
sendTransaction?: (transaction: Transaction | web3.VersionedTransaction, connection: Connection, options?: SendTransactionOptions) => Promise<web3.TransactionSignature>;
|
|
19451
19467
|
} & ISolanaConnectionOptions): Promise<any>;
|
|
19452
19468
|
|
|
19453
19469
|
/**
|
|
@@ -19456,15 +19472,30 @@ declare type AsArray<T> = T extends readonly unknown[] ? T : never;
|
|
|
19456
19472
|
* @param redeemShares `bigint` is treated as raw share-token units;
|
|
19457
19473
|
* `number` is treated as a UI amount. See {@link handleSolanaDeposit} for
|
|
19458
19474
|
* the same caveat about the missing on-chain slippage guard.
|
|
19459
|
-
* @throws AugustValidationError on missing wallet/programId or invalid amount
|
|
19475
|
+
* @throws AugustValidationError on missing wallet/programId or invalid amount,
|
|
19476
|
+
* or when the wallet holds fewer shares than `redeemShares`.
|
|
19477
|
+
* @throws AugustSDKError when a candidate token account's balance cannot be
|
|
19478
|
+
* read, or wrapping any downstream failure (`cause` preserved).
|
|
19479
|
+
* @remarks **Side effects.** Submits exactly one transaction, signed by the
|
|
19480
|
+
* provider's wallet. That transaction also creates the payout account when
|
|
19481
|
+
* the wallet has none, and the vault's fee-recipient account when *it* has
|
|
19482
|
+
* none — `publicKey` pays rent for both, roughly 0.00204 SOL each and
|
|
19483
|
+
* non-refundable, on top of the network fee. Callers should ensure the
|
|
19484
|
+
* wallet holds enough SOL for all of it before prompting the user to sign.
|
|
19460
19485
|
*/
|
|
19461
|
-
declare function handleSolanaRedeem({ provider, connection, vaultProgramId, vaultAddress, publicKey, redeemShares,
|
|
19486
|
+
declare function handleSolanaRedeem({ provider, connection, vaultProgramId, vaultAddress, publicKey, redeemShares, idl, }: {
|
|
19462
19487
|
idl: any;
|
|
19463
19488
|
vaultProgramId: PublicKey | string;
|
|
19464
19489
|
vaultAddress?: PublicKey | string;
|
|
19465
19490
|
publicKey: PublicKey | string;
|
|
19466
19491
|
redeemShares: number | bigint;
|
|
19467
|
-
|
|
19492
|
+
/**
|
|
19493
|
+
* @deprecated Accepted and ignored. Payout- and fee-recipient-account
|
|
19494
|
+
* creation is now prepended to the redeem instruction, so there is no longer
|
|
19495
|
+
* a second transaction to send. Kept so existing callers keep compiling;
|
|
19496
|
+
* will be removed in the next major.
|
|
19497
|
+
*/
|
|
19498
|
+
sendTransaction?: (transaction: Transaction | web3.VersionedTransaction, connection: Connection, options?: SendTransactionOptions) => Promise<web3.TransactionSignature>;
|
|
19468
19499
|
} & ISolanaConnectionOptions): Promise<any>;
|
|
19469
19500
|
|
|
19470
19501
|
/**
|
|
@@ -21229,6 +21260,20 @@ declare type AsArray<T> = T extends readonly unknown[] ? T : never;
|
|
|
21229
21260
|
export declare interface ISolanaConfig {
|
|
21230
21261
|
rpcUrl: string;
|
|
21231
21262
|
network: ISolanaNetwork;
|
|
21263
|
+
/**
|
|
21264
|
+
* Commitment for every Solana read *and* for the confirmation of every
|
|
21265
|
+
* Solana write, so the two always describe the same chain state.
|
|
21266
|
+
*
|
|
21267
|
+
* Defaults to `'finalized'` — unchanged from the commitment the SDK has
|
|
21268
|
+
* always inherited from the RPC. Pass `'confirmed'` for a markedly faster
|
|
21269
|
+
* round trip (seconds rather than tens of seconds), accepting that the state
|
|
21270
|
+
* you act on can still, in principle, be rolled back.
|
|
21271
|
+
*
|
|
21272
|
+
* Only read from this object. Configuring Solana through the legacy
|
|
21273
|
+
* `providers` map instead leaves the commitment at its default, since this
|
|
21274
|
+
* type requires `rpcUrl` and `network` alongside it.
|
|
21275
|
+
*/
|
|
21276
|
+
commitment?: Commitment;
|
|
21232
21277
|
}
|
|
21233
21278
|
|
|
21234
21279
|
declare interface ISolanaConnectionOptions {
|
|
@@ -25100,11 +25145,11 @@ declare type AsArray<T> = T extends readonly unknown[] ? T : never;
|
|
|
25100
25145
|
fetchUserTokenBalance: ({ connection, publicKey, depositMint, }: {
|
|
25101
25146
|
publicKey: PublicKey | string;
|
|
25102
25147
|
depositMint?: PublicKey | string;
|
|
25103
|
-
} & ISolanaConnectionOptions) => Promise<
|
|
25148
|
+
} & ISolanaConnectionOptions) => Promise<string>;
|
|
25104
25149
|
fetchUserShareBalance: ({ connection, publicKey, shareMint, }: {
|
|
25105
25150
|
publicKey: PublicKey | string;
|
|
25106
25151
|
shareMint?: PublicKey | string;
|
|
25107
|
-
} & ISolanaConnectionOptions) => Promise<
|
|
25152
|
+
} & ISolanaConnectionOptions) => Promise<number>;
|
|
25108
25153
|
fetchUserShareBalanceRaw: ({ connection, publicKey, shareMint, }: {
|
|
25109
25154
|
publicKey: PublicKey | string;
|
|
25110
25155
|
shareMint?: PublicKey | string;
|
|
@@ -25167,7 +25212,23 @@ declare type AsArray<T> = T extends readonly unknown[] ? T : never;
|
|
|
25167
25212
|
private _network;
|
|
25168
25213
|
private _connection;
|
|
25169
25214
|
private _provider;
|
|
25170
|
-
|
|
25215
|
+
/**
|
|
25216
|
+
* @param commitment - Commitment for every read this adapter makes *and* for
|
|
25217
|
+
* the confirmation of every write, so the two always describe the same
|
|
25218
|
+
* chain state — the vault handlers take it from this same `Connection`.
|
|
25219
|
+
*
|
|
25220
|
+
* Defaults to `'finalized'`, which is what this adapter has always used in
|
|
25221
|
+
* practice: it previously passed no commitment at all, so both reads and
|
|
25222
|
+
* confirmations fell through to the RPC's own `'finalized'` default. The
|
|
25223
|
+
* default is stated explicitly now rather than inherited, but it is
|
|
25224
|
+
* unchanged.
|
|
25225
|
+
*
|
|
25226
|
+
* Pass `'confirmed'` for a markedly faster round trip — seconds rather
|
|
25227
|
+
* than tens of seconds — accepting that the state you act on can still, in
|
|
25228
|
+
* principle, be rolled back. Also settable as the `commitment` field of
|
|
25229
|
+
* the `solana` config passed to `AugustSDK`.
|
|
25230
|
+
*/
|
|
25231
|
+
constructor(endpoint: ISolanaRpcEndpoint, network?: ISolanaNetwork_2, commitment?: Commitment);
|
|
25171
25232
|
get endpoint(): `https://${string}`;
|
|
25172
25233
|
get network(): ISolanaNetwork_2;
|
|
25173
25234
|
get connection(): web3.Connection;
|
|
@@ -25219,8 +25280,8 @@ declare type AsArray<T> = T extends readonly unknown[] ? T : never;
|
|
|
25219
25280
|
image: string;
|
|
25220
25281
|
}>;
|
|
25221
25282
|
getTokenSymbol(mintAddress: string | PublicKey): Promise<string>;
|
|
25222
|
-
fetchUserTokenBalance(publicKey: PublicKey | string, depositMint: PublicKey | string): Promise<
|
|
25223
|
-
fetchUserShareBalance(publicKey: PublicKey | string, shareMint: PublicKey | string): Promise<
|
|
25283
|
+
fetchUserTokenBalance(publicKey: PublicKey | string, depositMint: PublicKey | string): Promise<string>;
|
|
25284
|
+
fetchUserShareBalance(publicKey: PublicKey | string, shareMint: PublicKey | string): Promise<number>;
|
|
25224
25285
|
/**
|
|
25225
25286
|
* BigInt-safe variant of {@link fetchUserShareBalance}. Returns the raw u64
|
|
25226
25287
|
* `amount` (as a base-units string) plus the mint's `decimals` (or `null`
|
|
@@ -25260,13 +25321,31 @@ declare type AsArray<T> = T extends readonly unknown[] ? T : never;
|
|
|
25260
25321
|
/**
|
|
25261
25322
|
* Deposit funds into a Solana August vault.
|
|
25262
25323
|
* @param depositAmount `bigint` (raw on-chain units) or `number` (UI amount).
|
|
25324
|
+
* @param sendTransaction - **Ignored; scheduled for removal (AUGUST-7221).**
|
|
25325
|
+
* Share-account creation is now prepended to the deposit instruction, so
|
|
25326
|
+
* the SDK sends no second transaction and never invokes this callback —
|
|
25327
|
+
* signing goes through the provider's wallet.
|
|
25328
|
+
*
|
|
25329
|
+
* **Pass `undefined` here; do not delete the argument.** It sits *before*
|
|
25330
|
+
* `vaultAddress`, so removing it shifts your vault address into this slot
|
|
25331
|
+
* and leaves `vaultAddress` undefined — which silently falls back to the
|
|
25332
|
+
* legacy single-vault PDA derivation and targets a different vault.
|
|
25263
25333
|
*/
|
|
25264
|
-
vaultDeposit(vaultProgramId: PublicKey | string, idl: any, publicKey: PublicKey | string, depositAmount: number | bigint, sendTransaction
|
|
25334
|
+
vaultDeposit(vaultProgramId: PublicKey | string, idl: any, publicKey: PublicKey | string, depositAmount: number | bigint, sendTransaction?: (transaction: Transaction | web3.VersionedTransaction, connection: Connection, options?: SendTransactionOptions) => Promise<web3.TransactionSignature>, vaultAddress?: PublicKey | string): Promise<any>;
|
|
25265
25335
|
/**
|
|
25266
25336
|
* Redeem vault shares from a Solana August vault.
|
|
25267
25337
|
* @param redeemShares `bigint` (raw share units) or `number` (UI amount).
|
|
25338
|
+
* @param sendTransaction - **Ignored; scheduled for removal (AUGUST-7221).**
|
|
25339
|
+
* Payout- and fee-recipient-account creation is now prepended to the redeem
|
|
25340
|
+
* instruction, so the SDK sends no second transaction and never invokes
|
|
25341
|
+
* this callback — signing goes through the provider's wallet.
|
|
25342
|
+
*
|
|
25343
|
+
* **Pass `undefined` here; do not delete the argument.** It sits *before*
|
|
25344
|
+
* `vaultAddress`, so removing it shifts your vault address into this slot
|
|
25345
|
+
* and leaves `vaultAddress` undefined — which silently falls back to the
|
|
25346
|
+
* legacy single-vault PDA derivation and targets a different vault.
|
|
25268
25347
|
*/
|
|
25269
|
-
vaultRedeem(vaultProgramId: PublicKey | string, idl: any, publicKey: PublicKey | string, redeemShares: number | bigint, sendTransaction
|
|
25348
|
+
vaultRedeem(vaultProgramId: PublicKey | string, idl: any, publicKey: PublicKey | string, redeemShares: number | bigint, sendTransaction?: (transaction: Transaction | web3.VersionedTransaction, connection: Connection, options?: SendTransactionOptions) => Promise<web3.TransactionSignature>, vaultAddress?: PublicKey | string): Promise<any>;
|
|
25270
25349
|
/**
|
|
25271
25350
|
* Canonical program id for a program type on this adapter's network.
|
|
25272
25351
|
*
|
package/lib/types/web3.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Commitment } from '@solana/web3.js';
|
|
1
2
|
import type { Signer, Provider } from 'ethers';
|
|
2
3
|
export type IAddress = `0x${string}`;
|
|
3
4
|
/**
|
|
@@ -33,6 +34,20 @@ export type IStellarNetwork = 'mainnet' | 'testnet';
|
|
|
33
34
|
export interface ISolanaConfig {
|
|
34
35
|
rpcUrl: string;
|
|
35
36
|
network: ISolanaNetwork;
|
|
37
|
+
/**
|
|
38
|
+
* Commitment for every Solana read *and* for the confirmation of every
|
|
39
|
+
* Solana write, so the two always describe the same chain state.
|
|
40
|
+
*
|
|
41
|
+
* Defaults to `'finalized'` — unchanged from the commitment the SDK has
|
|
42
|
+
* always inherited from the RPC. Pass `'confirmed'` for a markedly faster
|
|
43
|
+
* round trip (seconds rather than tens of seconds), accepting that the state
|
|
44
|
+
* you act on can still, in principle, be rolled back.
|
|
45
|
+
*
|
|
46
|
+
* Only read from this object. Configuring Solana through the legacy
|
|
47
|
+
* `providers` map instead leaves the commitment at its default, since this
|
|
48
|
+
* type requires `rpcUrl` and `network` alongside it.
|
|
49
|
+
*/
|
|
50
|
+
commitment?: Commitment;
|
|
36
51
|
}
|
|
37
52
|
/**
|
|
38
53
|
* Stellar-specific configuration for `new AugustSDK({ stellar })`. All
|