@augustdigital/sdk 9.3.0 → 9.4.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/evm/getters.js +40 -0
- package/lib/adapters/evm/index.d.ts +106 -2
- package/lib/adapters/evm/index.js +125 -0
- package/lib/adapters/solana/index.d.ts +12 -5
- package/lib/adapters/solana/index.js +12 -4
- package/lib/adapters/solana/pricing.d.ts +173 -0
- package/lib/adapters/solana/pricing.js +223 -0
- package/lib/adapters/solana/types.d.ts +45 -0
- package/lib/adapters/solana/vault.actions.d.ts +33 -8
- package/lib/adapters/solana/vault.actions.js +185 -28
- package/lib/adapters/stellar/actions.d.ts +2 -2
- package/lib/adapters/stellar/actions.js +2 -2
- package/lib/adapters/stellar/getters.d.ts +1 -1
- package/lib/adapters/stellar/getters.js +2 -2
- package/lib/adapters/stellar/index.d.ts +1 -1
- package/lib/adapters/stellar/index.js +1 -1
- package/lib/adapters/stellar/types.d.ts +1 -2
- package/lib/core/analytics/method-taxonomy.js +7 -0
- package/lib/core/analytics/version.d.ts +1 -1
- package/lib/core/analytics/version.js +1 -1
- package/lib/core/base.class.d.ts +2 -2
- package/lib/core/base.class.js +2 -2
- package/lib/core/constants/core.d.ts +17 -3
- package/lib/core/constants/core.js +17 -3
- package/lib/core/constants/vaults.js +4 -0
- package/lib/core/fetcher.js +112 -45
- package/lib/core/helpers/concurrency.d.ts +37 -0
- package/lib/core/helpers/concurrency.js +58 -0
- package/lib/core/helpers/core.d.ts +9 -0
- package/lib/core/helpers/core.js +47 -0
- package/lib/core/helpers/vaults.d.ts +1 -1
- package/lib/core/helpers/vaults.js +1 -1
- package/lib/core/helpers/web3.js +14 -1
- package/lib/core/logger/curator-alert.d.ts +2 -2
- package/lib/core/logger/curator-alert.js +2 -2
- package/lib/evm/methods/crossChainVaultRegistry.d.ts +2 -3
- package/lib/evm/methods/crossChainVaultRegistry.js +2 -3
- package/lib/evm/types/crossChain.d.ts +1 -1
- package/lib/main.d.ts +7 -5
- package/lib/main.js +7 -5
- package/lib/modules/vaults/getters.d.ts +10 -3
- package/lib/modules/vaults/getters.js +205 -71
- package/lib/modules/vaults/main.d.ts +4 -4
- package/lib/modules/vaults/main.js +7 -4
- package/lib/modules/vaults/read.actions.d.ts +181 -0
- package/lib/modules/vaults/read.actions.js +331 -36
- package/lib/modules/vaults/types.d.ts +13 -0
- package/lib/modules/vaults/utils.js +5 -1
- package/lib/modules/vaults/write.actions.d.ts +74 -6
- package/lib/modules/vaults/write.actions.js +133 -23
- package/lib/sdk.d.ts +9827 -9342
- package/lib/services/subgraph/schema.d.ts +1 -1
- package/lib/services/subgraph/schema.js +1 -1
- package/lib/types/pools.d.ts +1 -1
- package/lib/types/vaults.d.ts +4 -4
- package/package.json +1 -1
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.describeSolanaError = describeSolanaError;
|
|
4
|
+
exports.slippageHint = slippageHint;
|
|
4
5
|
exports.handleSolanaDeposit = handleSolanaDeposit;
|
|
5
6
|
exports.handleSolanaRedeem = handleSolanaRedeem;
|
|
6
7
|
const anchor_1 = require("@coral-xyz/anchor");
|
|
7
8
|
const web3_js_1 = require("@solana/web3.js");
|
|
8
9
|
const spl_token_1 = require("@solana/spl-token");
|
|
9
10
|
const constants_1 = require("./constants");
|
|
11
|
+
const pricing_1 = require("./pricing");
|
|
10
12
|
const utils_1 = require("./utils");
|
|
11
13
|
const core_1 = require("../../core");
|
|
12
14
|
/**
|
|
@@ -49,6 +51,113 @@ function describeSolanaError(e) {
|
|
|
49
51
|
? str
|
|
50
52
|
: REASONLESS_SOLANA_ERROR;
|
|
51
53
|
}
|
|
54
|
+
/** `SlippageExceeded` in `august_vault`'s error enum (ordinal 18 + Anchor's 6000 offset). */
|
|
55
|
+
const SLIPPAGE_EXCEEDED_CODE = 6018;
|
|
56
|
+
/**
|
|
57
|
+
* A sentence to append when the program rejected the instruction for
|
|
58
|
+
* slippage, so the caller learns what to do rather than just what happened.
|
|
59
|
+
* Empty for every other error.
|
|
60
|
+
*
|
|
61
|
+
* Not merely a restatement of the program's reason string. Both checked
|
|
62
|
+
* instructions share error 6018, whose on-chain message is share-worded —
|
|
63
|
+
* "Received fewer shares than the caller's minimum" — so on a *redeem*, where
|
|
64
|
+
* the floor is denominated in assets, the program describes the wrong
|
|
65
|
+
* quantity. This sentence is what makes that case readable. The numeric code
|
|
66
|
+
* `describeSolanaError` renders alongside it is kept deliberately: it is the
|
|
67
|
+
* handle support and telemetry group on, and it survives a reword of the
|
|
68
|
+
* program's message.
|
|
69
|
+
*/
|
|
70
|
+
function slippageHint(e) {
|
|
71
|
+
const anchor = e;
|
|
72
|
+
const code = anchor?.error?.errorCode?.number ?? anchor?.code;
|
|
73
|
+
const name = anchor?.error?.errorCode?.code;
|
|
74
|
+
return code === SLIPPAGE_EXCEEDED_CODE || name === 'SlippageExceeded'
|
|
75
|
+
? ' The share price moved past your slippage tolerance between the quote and execution; retry, or pass a higher `slippageBps`.'
|
|
76
|
+
: '';
|
|
77
|
+
}
|
|
78
|
+
/** Largest value Anchor's `u64` encoder can represent. */
|
|
79
|
+
const U64_MAX = 18446744073709551615n;
|
|
80
|
+
/**
|
|
81
|
+
* Validate a caller-supplied slippage floor before it reaches Anchor's `u64`
|
|
82
|
+
* encoder.
|
|
83
|
+
*
|
|
84
|
+
* Anchor serializes a `BN` to `u64` via `toArrayLike`, which writes the
|
|
85
|
+
* magnitude and drops the sign — a `-1n` floor would reach the program as a
|
|
86
|
+
* floor of `1`, inverting the caller's intent instead of failing. Values above
|
|
87
|
+
* `u64` fare no better: they surface as an untyped "byte array longer than
|
|
88
|
+
* desired length" from inside the encoder, naming nothing the caller passed.
|
|
89
|
+
* Both are caught here, at the public-API boundary.
|
|
90
|
+
*
|
|
91
|
+
* @param value - The caller's `minSharesOut` / `minAssetsOut`, in raw base units.
|
|
92
|
+
* @param label - Option name, quoted in the error.
|
|
93
|
+
* @returns `value`, once known to fit an unsigned 64-bit integer.
|
|
94
|
+
* @throws {@link AugustValidationError} when it is not a `bigint` in `0 .. 2^64-1`.
|
|
95
|
+
*/
|
|
96
|
+
function validateExplicitFloor(value, label) {
|
|
97
|
+
if (typeof value !== 'bigint' || value < 0n || value > U64_MAX) {
|
|
98
|
+
throw new core_1.AugustValidationError('INVALID_INPUT', `${label} must be a bigint between 0 and ${U64_MAX} raw base units, got ${String(value)}.`);
|
|
99
|
+
}
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The vault state and share-mint supply as of a **single slot**.
|
|
104
|
+
*
|
|
105
|
+
* Both figures price the quote, so they have to come from one snapshot. Read
|
|
106
|
+
* independently — even concurrently, as two RPC requests — a deposit or
|
|
107
|
+
* redemption landing in between combines a supply and an asset total that
|
|
108
|
+
* never coexisted, and the quote is wrong in whichever direction the
|
|
109
|
+
* interleaving fell. Seeing a 10% deposit's new assets against the old supply
|
|
110
|
+
* quotes ~10% high, and the floor then rejects a redemption whose share price
|
|
111
|
+
* never moved; the opposite ordering quotes low and silently weakens the
|
|
112
|
+
* protection. `getMultipleAccountsInfo` is served from one slot, so the pair
|
|
113
|
+
* is always coherent — and it costs one round trip instead of two.
|
|
114
|
+
*
|
|
115
|
+
* @param program - Anchor program, used only for its account coder; the
|
|
116
|
+
* provider it was built with is irrelevant, so no read-only program is needed.
|
|
117
|
+
* @param vaultStatePda - Vault state account to decode.
|
|
118
|
+
* @param shareMintAddr - Share mint whose supply and decimals are read.
|
|
119
|
+
* @returns The decoded `VaultState`, the share mint's raw `supply`, and its
|
|
120
|
+
* `decimals` — all from the same slot.
|
|
121
|
+
* @throws {@link AugustSDKError} when either account is absent, or is not the
|
|
122
|
+
* account type it should be.
|
|
123
|
+
*/
|
|
124
|
+
async function readPricingSnapshot({ connection, program, vaultStatePda, shareMintAddr, }) {
|
|
125
|
+
const [vaultStateInfo, shareMintInfo] = await connection.getMultipleAccountsInfo([vaultStatePda, shareMintAddr]);
|
|
126
|
+
if (!vaultStateInfo) {
|
|
127
|
+
throw new core_1.AugustSDKError('UNKNOWN', `Vault state account ${vaultStatePda.toBase58()} does not exist; it cannot be priced.`);
|
|
128
|
+
}
|
|
129
|
+
if (!shareMintInfo) {
|
|
130
|
+
throw new core_1.AugustSDKError('UNKNOWN', `Share mint ${shareMintAddr.toBase58()} does not exist; it cannot be priced.`);
|
|
131
|
+
}
|
|
132
|
+
// `vaultState`, not the IDL's own `VaultState`: `Program` runs the IDL
|
|
133
|
+
// through `convertIdlToCamelCase`, so its coder only answers to the
|
|
134
|
+
// camelCase name and the IDL spelling throws `Account not found`. (A coder
|
|
135
|
+
// built directly from the raw IDL behaves the opposite way — which is why
|
|
136
|
+
// this has to be checked against a real `Program`, as its test does.)
|
|
137
|
+
//
|
|
138
|
+
// Decoded by name rather than `decodeAny`, so a `vaultAddress` pointing at
|
|
139
|
+
// some other account of this program fails here instead of being decoded as
|
|
140
|
+
// whatever it happens to be.
|
|
141
|
+
let vaultStateData;
|
|
142
|
+
try {
|
|
143
|
+
vaultStateData = program.coder.accounts.decode('vaultState', vaultStateInfo.data);
|
|
144
|
+
}
|
|
145
|
+
catch (e) {
|
|
146
|
+
throw new core_1.AugustSDKError('UNKNOWN', `Account ${vaultStatePda.toBase58()} is not a vault state account for this program.`, { cause: e });
|
|
147
|
+
}
|
|
148
|
+
let mint;
|
|
149
|
+
try {
|
|
150
|
+
mint = (0, spl_token_1.unpackMint)(shareMintAddr, shareMintInfo, spl_token_1.TOKEN_PROGRAM_ID);
|
|
151
|
+
}
|
|
152
|
+
catch (e) {
|
|
153
|
+
throw new core_1.AugustSDKError('UNKNOWN', `Account ${shareMintAddr.toBase58()} is not an SPL Token mint.`, { cause: e });
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
vaultStateData,
|
|
157
|
+
supply: mint.supply,
|
|
158
|
+
shareDecimals: mint.decimals,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
52
161
|
/**
|
|
53
162
|
* Pick which token account to hand the vault program for a given mint, and
|
|
54
163
|
* describe how to create it when the user has none.
|
|
@@ -65,7 +174,7 @@ function describeSolanaError(e) {
|
|
|
65
174
|
* `.preInstructions()` it is atomic with the transfer, so there is no window
|
|
66
175
|
* in which the vault instruction can reference an account that has not
|
|
67
176
|
* landed. Sent as its own transaction — as this module used to — it raced
|
|
68
|
-
* its own confirmation. Observed in production (
|
|
177
|
+
* its own confirmation. Observed in production (2026-08-11):
|
|
69
178
|
* a deposit failed with `Simulation failed … Logs: []` while the share-ATA
|
|
70
179
|
* create was still in flight, then succeeded 17s later on retry. The empty
|
|
71
180
|
* log array means the transaction never reached program execution, so there
|
|
@@ -152,11 +261,13 @@ async function resolveUserTokenAccountPair({ connection, owner, mints, }) {
|
|
|
152
261
|
* non-refundable while the account stays open — on top of the network fee.
|
|
153
262
|
* Callers should ensure the wallet holds enough SOL for both before
|
|
154
263
|
* prompting the user to sign.
|
|
155
|
-
* @remarks
|
|
156
|
-
*
|
|
157
|
-
* that
|
|
264
|
+
* @remarks Sends `deposit_checked`, not `deposit`. Unless `options.minSharesOut`
|
|
265
|
+
* is given, the vault state and share mint are read to quote the shares this
|
|
266
|
+
* deposit should mint, and that quote lowered by `options.slippageBps`
|
|
267
|
+
* (default {@link DEFAULT_SLIPPAGE_BPS}) is passed as the floor; the program
|
|
268
|
+
* reverts with `SlippageExceeded` if execution would mint fewer.
|
|
158
269
|
*/
|
|
159
|
-
async function handleSolanaDeposit({ provider, connection, network = constants_1.fallbackNetwork, vaultProgramId, vaultAddress, depositAmount, publicKey, idl, }) {
|
|
270
|
+
async function handleSolanaDeposit({ provider, connection, network = constants_1.fallbackNetwork, vaultProgramId, vaultAddress, depositAmount, publicKey, idl, options = {}, }) {
|
|
160
271
|
try {
|
|
161
272
|
if (!publicKey)
|
|
162
273
|
throw new core_1.AugustValidationError('INVALID_INPUT', 'handleSolanaDeposit: wallet not connected');
|
|
@@ -167,6 +278,9 @@ async function handleSolanaDeposit({ provider, connection, network = constants_1
|
|
|
167
278
|
throw new core_1.AugustValidationError('INVALID_INPUT', 'handleSolanaDeposit: depositAmount is required and must be > 0');
|
|
168
279
|
if (!vaultProgramId)
|
|
169
280
|
throw new core_1.AugustValidationError('INVALID_INPUT', 'handleSolanaDeposit: vaultProgramId is required');
|
|
281
|
+
// Before any RPC: an unusable floor should cost the caller nothing.
|
|
282
|
+
if (options.minSharesOut !== undefined)
|
|
283
|
+
validateExplicitFloor(options.minSharesOut, 'minSharesOut');
|
|
170
284
|
const { depositMint, shareMint, vaultVersion } = await utils_1.SolanaUtils.getVaultMints({
|
|
171
285
|
network,
|
|
172
286
|
connection,
|
|
@@ -279,9 +393,33 @@ async function handleSolanaDeposit({ provider, connection, network = constants_1
|
|
|
279
393
|
core_1.Logger.log.info('handleSolanaDeposit', 'Share mint', shareMintAddr.toBase58());
|
|
280
394
|
core_1.Logger.log.info('handleSolanaDeposit', 'Deposit mint', _depositMint?.toBase58());
|
|
281
395
|
core_1.Logger.log.info('handleSolanaDeposit', 'Signer', _publicKey.toBase58());
|
|
282
|
-
|
|
396
|
+
let minSharesOut;
|
|
397
|
+
if (options.minSharesOut !== undefined) {
|
|
398
|
+
minSharesOut = options.minSharesOut;
|
|
399
|
+
}
|
|
400
|
+
else {
|
|
401
|
+
// One read the unguarded `deposit` never needed: the vault state (assets
|
|
402
|
+
// and offset) and the share mint (supply) in a single snapshot, since
|
|
403
|
+
// pricing against a mismatched pair is worse than not quoting at all —
|
|
404
|
+
// see `readPricingSnapshot`. A caller already holding a quote can pass
|
|
405
|
+
// `minSharesOut` and skip it.
|
|
406
|
+
const { vaultStateData, supply } = await readPricingSnapshot({
|
|
407
|
+
connection,
|
|
408
|
+
program,
|
|
409
|
+
vaultStatePda,
|
|
410
|
+
shareMintAddr,
|
|
411
|
+
});
|
|
412
|
+
minSharesOut = (0, pricing_1.quoteDepositFloor)({
|
|
413
|
+
supply,
|
|
414
|
+
totalAssets: (0, pricing_1.totalAssetsOf)(vaultStateData),
|
|
415
|
+
amount: BigInt(depositAmountRaw.toString()),
|
|
416
|
+
offset: (0, pricing_1.resolveShareOffset)(vaultStateData.shareOffset),
|
|
417
|
+
slippageBps: options.slippageBps ?? pricing_1.DEFAULT_SLIPPAGE_BPS,
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
core_1.Logger.log.info('handleSolanaDeposit', 'Min shares out (raw)', minSharesOut.toString());
|
|
283
421
|
const tx = await program.methods
|
|
284
|
-
.
|
|
422
|
+
.depositChecked(depositAmountRaw, new anchor_1.BN(minSharesOut.toString()))
|
|
285
423
|
.accounts({
|
|
286
424
|
vaultState: vaultStatePda,
|
|
287
425
|
vaultTokenAta: vaultTokenAtaPda,
|
|
@@ -320,7 +458,7 @@ async function handleSolanaDeposit({ provider, connection, network = constants_1
|
|
|
320
458
|
vaultAddress: vaultAddress ? String(vaultAddress) : undefined,
|
|
321
459
|
depositAmount: depositAmountForLog,
|
|
322
460
|
});
|
|
323
|
-
throw new core_1.AugustSDKError('UNKNOWN', `Solana deposit failed: ${describeSolanaError(e)}`, {
|
|
461
|
+
throw new core_1.AugustSDKError('UNKNOWN', `Solana deposit failed: ${describeSolanaError(e)}${slippageHint(e)}`, {
|
|
324
462
|
cause: e,
|
|
325
463
|
context: {
|
|
326
464
|
vaultProgramId: String(vaultProgramId),
|
|
@@ -334,8 +472,12 @@ async function handleSolanaDeposit({ provider, connection, network = constants_1
|
|
|
334
472
|
* Redeem vault shares back into the underlying mint on a Solana August vault.
|
|
335
473
|
*
|
|
336
474
|
* @param redeemShares `bigint` is treated as raw share-token units;
|
|
337
|
-
* `number` is treated as a UI amount.
|
|
338
|
-
*
|
|
475
|
+
* `number` is treated as a UI amount.
|
|
476
|
+
* @remarks Sends `redeem_checked`, not `redeem`. Unless `options.minAssetsOut`
|
|
477
|
+
* is given, the payout is quoted from the vault state and share supply the
|
|
478
|
+
* handler already reads, **net of the withdrawal fee**, and that quote
|
|
479
|
+
* lowered by `options.slippageBps` (default {@link DEFAULT_SLIPPAGE_BPS}) is
|
|
480
|
+
* passed as the floor; the program reverts with `SlippageExceeded` below it.
|
|
339
481
|
* @throws AugustValidationError on missing wallet/programId or invalid amount,
|
|
340
482
|
* or when the wallet holds fewer shares than `redeemShares`.
|
|
341
483
|
* @throws AugustSDKError when a candidate token account's balance cannot be
|
|
@@ -347,7 +489,7 @@ async function handleSolanaDeposit({ provider, connection, network = constants_1
|
|
|
347
489
|
* non-refundable, on top of the network fee. Callers should ensure the
|
|
348
490
|
* wallet holds enough SOL for all of it before prompting the user to sign.
|
|
349
491
|
*/
|
|
350
|
-
async function handleSolanaRedeem({ provider, connection, vaultProgramId, vaultAddress, publicKey, redeemShares, idl, }) {
|
|
492
|
+
async function handleSolanaRedeem({ provider, connection, vaultProgramId, vaultAddress, publicKey, redeemShares, idl, options = {}, }) {
|
|
351
493
|
try {
|
|
352
494
|
if (!publicKey)
|
|
353
495
|
throw new core_1.AugustValidationError('INVALID_INPUT', 'handleSolanaRedeem: wallet not connected');
|
|
@@ -358,6 +500,9 @@ async function handleSolanaRedeem({ provider, connection, vaultProgramId, vaultA
|
|
|
358
500
|
throw new core_1.AugustValidationError('INVALID_INPUT', 'handleSolanaRedeem: redeemShares is required and must be > 0');
|
|
359
501
|
if (!vaultProgramId)
|
|
360
502
|
throw new core_1.AugustValidationError('INVALID_INPUT', 'handleSolanaRedeem: vaultProgramId is required');
|
|
503
|
+
// Before any RPC: an unusable floor should cost the caller nothing.
|
|
504
|
+
if (options.minAssetsOut !== undefined)
|
|
505
|
+
validateExplicitFloor(options.minAssetsOut, 'minAssetsOut');
|
|
361
506
|
const { depositMint, shareMint, vaultVersion } = await utils_1.SolanaUtils.getVaultMints({
|
|
362
507
|
network: provider.connection.rpcEndpoint.includes('devnet')
|
|
363
508
|
? 'devnet'
|
|
@@ -385,13 +530,16 @@ async function handleSolanaRedeem({ provider, connection, vaultProgramId, vaultA
|
|
|
385
530
|
// Use on-chain share mint from vault state instead of deriving a PDA,
|
|
386
531
|
// since PDA derivation assumes one share mint per program (single-vault only).
|
|
387
532
|
const shareMintAddr = new web3_js_1.PublicKey(shareMint);
|
|
388
|
-
//
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
|
|
533
|
+
// Share-mint decimals and supply plus the vault state, from one slot. This
|
|
534
|
+
// replaces a `getParsedAccountInfo` here and a `vaultState.fetch` further
|
|
535
|
+
// down — one round trip rather than two, and supply and assets can no
|
|
536
|
+
// longer be read either side of someone else's deposit.
|
|
537
|
+
const { vaultStateData, supply: shareSupply, shareDecimals, } = await readPricingSnapshot({
|
|
538
|
+
connection,
|
|
539
|
+
program,
|
|
540
|
+
vaultStatePda,
|
|
541
|
+
shareMintAddr,
|
|
542
|
+
});
|
|
395
543
|
let redeemSharesRaw;
|
|
396
544
|
if (typeof redeemShares === 'bigint') {
|
|
397
545
|
redeemSharesRaw = new anchor_1.BN(redeemShares.toString());
|
|
@@ -447,14 +595,22 @@ async function handleSolanaRedeem({ provider, connection, vaultProgramId, vaultA
|
|
|
447
595
|
'so it cannot receive the redemption proceeds.');
|
|
448
596
|
}
|
|
449
597
|
const senderTokenAccount = payoutAccount.address;
|
|
450
|
-
//
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
598
|
+
// No extra reads here: the share mint (supply) and vault state (assets,
|
|
599
|
+
// offset, fee, fee recipient) came from the snapshot above.
|
|
600
|
+
let minAssetsOut;
|
|
601
|
+
if (options.minAssetsOut !== undefined) {
|
|
602
|
+
minAssetsOut = options.minAssetsOut;
|
|
603
|
+
}
|
|
604
|
+
else {
|
|
605
|
+
minAssetsOut = (0, pricing_1.quoteRedeemFloor)({
|
|
606
|
+
supply: shareSupply,
|
|
607
|
+
totalAssets: (0, pricing_1.totalAssetsOf)(vaultStateData),
|
|
608
|
+
shares: BigInt(redeemSharesRaw.toString()),
|
|
609
|
+
offset: (0, pricing_1.resolveShareOffset)(vaultStateData.shareOffset),
|
|
610
|
+
feePpm: (0, pricing_1.toBigInt)(vaultStateData.withdrawalFee, 'withdrawalFee'),
|
|
611
|
+
slippageBps: options.slippageBps ?? pricing_1.DEFAULT_SLIPPAGE_BPS,
|
|
612
|
+
});
|
|
613
|
+
}
|
|
458
614
|
// Get the fee recipient's token account
|
|
459
615
|
const feeRecipientTokenAccount = await (0, spl_token_1.getAssociatedTokenAddress)(_depositMint, vaultStateData.feeRecipient);
|
|
460
616
|
// Every account this handler might have to create is created in the same
|
|
@@ -483,8 +639,9 @@ async function handleSolanaRedeem({ provider, connection, vaultProgramId, vaultA
|
|
|
483
639
|
core_1.Logger.log.info('handleSolanaRedeem', 'Share mint:', shareMintAddr.toBase58());
|
|
484
640
|
core_1.Logger.log.info('handleSolanaRedeem', 'Deposit mint:', _depositMint?.toBase58());
|
|
485
641
|
core_1.Logger.log.info('handleSolanaRedeem', 'Signer:', _publicKey.toBase58());
|
|
642
|
+
core_1.Logger.log.info('handleSolanaRedeem', 'Min assets out, net of fee (raw):', minAssetsOut.toString());
|
|
486
643
|
const tx = await program.methods
|
|
487
|
-
.
|
|
644
|
+
.redeemChecked(redeemSharesRaw, new anchor_1.BN(minAssetsOut.toString()))
|
|
488
645
|
.accounts({
|
|
489
646
|
vaultState: vaultStatePda,
|
|
490
647
|
vaultDepositAta: vaultTokenAtaPda,
|
|
@@ -516,7 +673,7 @@ async function handleSolanaRedeem({ provider, connection, vaultProgramId, vaultA
|
|
|
516
673
|
vaultAddress: vaultAddress ? String(vaultAddress) : undefined,
|
|
517
674
|
redeemShares: redeemSharesForLog,
|
|
518
675
|
});
|
|
519
|
-
throw new core_1.AugustSDKError('UNKNOWN', `Solana redeem failed: ${describeSolanaError(e)}`, {
|
|
676
|
+
throw new core_1.AugustSDKError('UNKNOWN', `Solana redeem failed: ${describeSolanaError(e)}${slippageHint(e)}`, {
|
|
520
677
|
cause: e,
|
|
521
678
|
context: {
|
|
522
679
|
vaultProgramId: String(vaultProgramId),
|
|
@@ -25,8 +25,8 @@ export declare function handleStellarDeposit(params: IStellarDepositParams): Pro
|
|
|
25
25
|
* Side effect on failure: when the *vault* cannot serve the redemption — it
|
|
26
26
|
* rejected the call, or its ledger state needs restoring — the failure is
|
|
27
27
|
* reported to that vault's curator. Stellar vaults are instant-redeem only, so
|
|
28
|
-
* this is the curator's only signal that their depositors cannot get out
|
|
29
|
-
*
|
|
28
|
+
* this is the curator's only signal that their depositors cannot get out.
|
|
29
|
+
* Fire-and-forget and deduped; see
|
|
30
30
|
* {@link reportRedeemFailure} and {@link REPORTED_BUILD_STAGES}.
|
|
31
31
|
*
|
|
32
32
|
* Note this includes a caller-caused rejection such as redeeming more shares
|
|
@@ -81,8 +81,8 @@ async function handleStellarDeposit(params) {
|
|
|
81
81
|
* Side effect on failure: when the *vault* cannot serve the redemption — it
|
|
82
82
|
* rejected the call, or its ledger state needs restoring — the failure is
|
|
83
83
|
* reported to that vault's curator. Stellar vaults are instant-redeem only, so
|
|
84
|
-
* this is the curator's only signal that their depositors cannot get out
|
|
85
|
-
*
|
|
84
|
+
* this is the curator's only signal that their depositors cannot get out.
|
|
85
|
+
* Fire-and-forget and deduped; see
|
|
86
86
|
* {@link reportRedeemFailure} and {@link REPORTED_BUILD_STAGES}.
|
|
87
87
|
*
|
|
88
88
|
* Note this includes a caller-caused rejection such as redeeming more shares
|
|
@@ -32,7 +32,7 @@ export declare const getStellarVault: (tokenizedVault: ITokenizedVault, options:
|
|
|
32
32
|
* (e.g. 13). A consumer that sizes a redeem trusting a silent fallback 7 would
|
|
33
33
|
* under-redeem by `10^offset`. Callers that settle a redeem from this position
|
|
34
34
|
* MUST refuse (surface an error) when `decimalsFromFallback` is `true` rather
|
|
35
|
-
* than trusting `decimals`.
|
|
35
|
+
* than trusting `decimals`.
|
|
36
36
|
*
|
|
37
37
|
* @param vaultAddress - Stellar vault contract ID (C… address)
|
|
38
38
|
* @param walletAddress - User's Stellar account (G… address); validated upfront
|
|
@@ -22,7 +22,7 @@ const soroban_1 = require("./soroban");
|
|
|
22
22
|
* Returns `{ decimals, fromFallback }`. When the `decimals()` retval is missing
|
|
23
23
|
* or unparseable, `decimals` is STELLAR_FALLBACK_DECIMALS (7) and `fromFallback`
|
|
24
24
|
* is `true` — the signal callers need to avoid trusting a fabricated scale (a
|
|
25
|
-
* silent 7 under-redeems ERC4626 offset vaults
|
|
25
|
+
* silent 7 under-redeems ERC4626 offset vaults). A successful
|
|
26
26
|
* parse returns the on-chain value with `fromFallback: false`.
|
|
27
27
|
*/
|
|
28
28
|
function parseDecimals(scVal, context, vaultAddress) {
|
|
@@ -172,7 +172,7 @@ exports.getStellarVault = getStellarVault;
|
|
|
172
172
|
* (e.g. 13). A consumer that sizes a redeem trusting a silent fallback 7 would
|
|
173
173
|
* under-redeem by `10^offset`. Callers that settle a redeem from this position
|
|
174
174
|
* MUST refuse (surface an error) when `decimalsFromFallback` is `true` rather
|
|
175
|
-
* than trusting `decimals`.
|
|
175
|
+
* than trusting `decimals`.
|
|
176
176
|
*
|
|
177
177
|
* @param vaultAddress - Stellar vault contract ID (C… address)
|
|
178
178
|
* @param walletAddress - User's Stellar account (G… address); validated upfront
|
|
@@ -112,7 +112,7 @@ export declare class StellarAdapter {
|
|
|
112
112
|
* @returns The position `{ shares, decimals, decimalsFromFallback }`, or null
|
|
113
113
|
* if the balance read fails. When `decimalsFromFallback` is `true` the
|
|
114
114
|
* `decimals()` read failed and `decimals` is the fallback (7) — callers sizing
|
|
115
|
-
* a redeem MUST refuse rather than trust it
|
|
115
|
+
* a redeem MUST refuse rather than trust it.
|
|
116
116
|
*/
|
|
117
117
|
getUserPosition(vaultAddress: string, walletAddress: string): Promise<IStellarUserPosition | null>;
|
|
118
118
|
/**
|
|
@@ -167,7 +167,7 @@ class StellarAdapter {
|
|
|
167
167
|
* @returns The position `{ shares, decimals, decimalsFromFallback }`, or null
|
|
168
168
|
* if the balance read fails. When `decimalsFromFallback` is `true` the
|
|
169
169
|
* `decimals()` read failed and `decimals` is the fallback (7) — callers sizing
|
|
170
|
-
* a redeem MUST refuse rather than trust it
|
|
170
|
+
* a redeem MUST refuse rather than trust it.
|
|
171
171
|
*/
|
|
172
172
|
async getUserPosition(vaultAddress, walletAddress) {
|
|
173
173
|
return StellarGetters.getStellarUserPosition(vaultAddress, walletAddress, this._network, this._sorobanRpcUrl);
|
|
@@ -37,8 +37,7 @@ export interface IStellarRedeemParams {
|
|
|
37
37
|
* (7) and `decimalsFromFallback` is set to `true`. Callers that size a redeem
|
|
38
38
|
* from this position MUST check `decimalsFromFallback` and refuse to settle
|
|
39
39
|
* when it is `true` — trusting a fabricated 7 against an ERC4626 offset vault
|
|
40
|
-
* (share decimals = asset + offset) under-redeems by `10^offset`.
|
|
41
|
-
* AUGUST-6381.
|
|
40
|
+
* (share decimals = asset + offset) under-redeems by `10^offset`.
|
|
42
41
|
*/
|
|
43
42
|
export interface IStellarUserPosition {
|
|
44
43
|
/** Raw share balance in smallest unit, as a non-negative integer string */
|
|
@@ -64,6 +64,9 @@ exports.METHOD_CATEGORIES = {
|
|
|
64
64
|
getVaultWithdrawals: 'read.redemption',
|
|
65
65
|
getWithdrawalRequestsWithStatus: 'read.redemption',
|
|
66
66
|
previewRedemption: 'read.redemption',
|
|
67
|
+
getBurnableAmountByReceiver: 'read.redemption',
|
|
68
|
+
getRequirementByDate: 'read.redemption',
|
|
69
|
+
getScheduledTransactionsByDate: 'read.redemption',
|
|
67
70
|
getPrice: 'read.price',
|
|
68
71
|
getUserPoints: 'read.points',
|
|
69
72
|
fetchPointsLeaderboard: 'read.points',
|
|
@@ -111,6 +114,9 @@ exports.METHOD_CATEGORIES = {
|
|
|
111
114
|
allowance: 'read.evm',
|
|
112
115
|
balanceOf: 'read.evm',
|
|
113
116
|
maxDeposit: 'read.vault',
|
|
117
|
+
getIdleLiquidity: 'read.vault',
|
|
118
|
+
lagDuration: 'read.vault',
|
|
119
|
+
getWithdrawalEpoch: 'read.vault',
|
|
114
120
|
vaultDeposit: 'write.deposit',
|
|
115
121
|
depositNative: 'write.deposit',
|
|
116
122
|
swapAndDeposit: 'write.deposit',
|
|
@@ -118,6 +124,7 @@ exports.METHOD_CATEGORIES = {
|
|
|
118
124
|
depositNativeViaSwapRouter: 'write.deposit',
|
|
119
125
|
swapRouterDeposit: 'write.deposit',
|
|
120
126
|
vaultRedeem: 'write.redeem',
|
|
127
|
+
claimWithdrawal: 'write.redeem',
|
|
121
128
|
vaultRequestRedeem: 'write.redeem',
|
|
122
129
|
rwaRedeemAsset: 'write.redeem',
|
|
123
130
|
vaultApprove: 'write.approve',
|
package/lib/core/base.class.d.ts
CHANGED
|
@@ -148,8 +148,8 @@ export declare class AugustBase {
|
|
|
148
148
|
*/
|
|
149
149
|
constructor({ appName, providers, keys, monitoring, analytics, versionCheck, timeoutMs, publicApiBaseUrl, attribution, }: IAugustBase);
|
|
150
150
|
/**
|
|
151
|
-
* Verify API
|
|
152
|
-
*
|
|
151
|
+
* Verify the configured August API key and mark this instance as authorized.
|
|
152
|
+
* @throws Error when `keys.august` is missing or rejected by the API.
|
|
153
153
|
*/
|
|
154
154
|
init(): Promise<void>;
|
|
155
155
|
/**
|
package/lib/core/base.class.js
CHANGED
|
@@ -87,8 +87,8 @@ class AugustBase {
|
|
|
87
87
|
});
|
|
88
88
|
}
|
|
89
89
|
/**
|
|
90
|
-
* Verify API
|
|
91
|
-
*
|
|
90
|
+
* Verify the configured August API key and mark this instance as authorized.
|
|
91
|
+
* @throws Error when `keys.august` is missing or rejected by the API.
|
|
92
92
|
*/
|
|
93
93
|
async init() {
|
|
94
94
|
const [augustCheck] = await Promise.all([
|
|
@@ -3,10 +3,24 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { IAddress, VaultAddress } from '../../types';
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* Default per-request timeout, in milliseconds, for every August HTTP fetcher
|
|
7
|
+
* (`fetchAugustPublic`, `fetchAugustWithKey`, `fetchAugustWithBearer`).
|
|
8
|
+
*
|
|
9
|
+
* Previously 90s. That was longer than any realistic caller was willing to
|
|
10
|
+
* wait: server-rendered integrations run inside serverless functions whose own
|
|
11
|
+
* execution budget is well under 90s, so the request had already been abandoned
|
|
12
|
+
* upstream by the time the abort fired — the timeout only ever garbage-collected
|
|
13
|
+
* a dead request, while the socket stayed open holding backend capacity and
|
|
14
|
+
* amplifying the stall for every concurrent caller.
|
|
15
|
+
*
|
|
16
|
+
* 15s is above the p99 of a healthy August API response and below every common
|
|
17
|
+
* upstream budget (Vercel/Lambda defaults, browser patience, upstream gateway
|
|
18
|
+
* timeouts), so a stalled request now fails fast and frees its connection.
|
|
19
|
+
*
|
|
20
|
+
* Callers that genuinely need longer override per-request via
|
|
21
|
+
* `IFetchAugustOptions.timeoutMs`, or process-wide via `setSdkRequestTimeout`.
|
|
8
22
|
*/
|
|
9
|
-
export declare const REQUEST_TIMEOUT_MS =
|
|
23
|
+
export declare const REQUEST_TIMEOUT_MS = 15000;
|
|
10
24
|
export declare const PRICE_SERVER_URL = "https://prices.augustdigital.io";
|
|
11
25
|
export declare const WEBSERVER_URL: {
|
|
12
26
|
production: string;
|
|
@@ -33,10 +33,24 @@ function subaccountSegment(subaccount) {
|
|
|
33
33
|
: encodeURIComponent(subaccount);
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
36
|
+
* Default per-request timeout, in milliseconds, for every August HTTP fetcher
|
|
37
|
+
* (`fetchAugustPublic`, `fetchAugustWithKey`, `fetchAugustWithBearer`).
|
|
38
|
+
*
|
|
39
|
+
* Previously 90s. That was longer than any realistic caller was willing to
|
|
40
|
+
* wait: server-rendered integrations run inside serverless functions whose own
|
|
41
|
+
* execution budget is well under 90s, so the request had already been abandoned
|
|
42
|
+
* upstream by the time the abort fired — the timeout only ever garbage-collected
|
|
43
|
+
* a dead request, while the socket stayed open holding backend capacity and
|
|
44
|
+
* amplifying the stall for every concurrent caller.
|
|
45
|
+
*
|
|
46
|
+
* 15s is above the p99 of a healthy August API response and below every common
|
|
47
|
+
* upstream budget (Vercel/Lambda defaults, browser patience, upstream gateway
|
|
48
|
+
* timeouts), so a stalled request now fails fast and frees its connection.
|
|
49
|
+
*
|
|
50
|
+
* Callers that genuinely need longer override per-request via
|
|
51
|
+
* `IFetchAugustOptions.timeoutMs`, or process-wide via `setSdkRequestTimeout`.
|
|
38
52
|
*/
|
|
39
|
-
exports.REQUEST_TIMEOUT_MS =
|
|
53
|
+
exports.REQUEST_TIMEOUT_MS = 15000;
|
|
40
54
|
exports.PRICE_SERVER_URL = 'https://prices.augustdigital.io';
|
|
41
55
|
exports.WEBSERVER_URL = {
|
|
42
56
|
production: 'https://backend.fractalprotocol.org',
|
|
@@ -103,6 +103,7 @@ exports.VAULT_FUNCTIONS_V1 = [
|
|
|
103
103
|
'withdrawalFee',
|
|
104
104
|
'lagDuration',
|
|
105
105
|
'withdrawalsPaused',
|
|
106
|
+
'depositsPaused',
|
|
106
107
|
'maxDepositAmount',
|
|
107
108
|
];
|
|
108
109
|
exports.VAULT_FUNCTIONS_V2 = [
|
|
@@ -113,7 +114,10 @@ exports.VAULT_FUNCTIONS_V2 = [
|
|
|
113
114
|
'maxDepositAmount',
|
|
114
115
|
'lagDuration',
|
|
115
116
|
'withdrawalsPaused',
|
|
117
|
+
'depositsPaused',
|
|
116
118
|
'depositCap',
|
|
119
|
+
// MUST stay last: `getEvmVaultV2` reads the receipt token positionally as
|
|
120
|
+
// the final entry of this list.
|
|
117
121
|
'lpTokenAddress',
|
|
118
122
|
];
|
|
119
123
|
exports.VAULT_FUNCTIONS_V2_RECEIPT = [
|