@gauntlet-xyz/sdk 2.3.0 → 2.5.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.
Files changed (86) hide show
  1. package/README.md +52 -0
  2. package/dist/api/client.d.ts +22 -1
  3. package/dist/api/client.d.ts.map +1 -1
  4. package/dist/api/client.js +11 -1
  5. package/dist/api/client.js.map +1 -1
  6. package/dist/api/generated.d.ts +51 -7
  7. package/dist/api/generated.d.ts.map +1 -1
  8. package/dist/api/index.d.ts +1 -1
  9. package/dist/api/index.d.ts.map +1 -1
  10. package/dist/api/index.js.map +1 -1
  11. package/dist/errors.d.ts +11 -0
  12. package/dist/errors.d.ts.map +1 -1
  13. package/dist/errors.js +21 -2
  14. package/dist/errors.js.map +1 -1
  15. package/dist/evm/abis/priceAndFeeCalculatorV2.d.ts +44 -0
  16. package/dist/evm/abis/priceAndFeeCalculatorV2.d.ts.map +1 -1
  17. package/dist/evm/abis/priceAndFeeCalculatorV2.js +22 -0
  18. package/dist/evm/abis/priceAndFeeCalculatorV2.js.map +1 -1
  19. package/dist/evm/abis/provisionerV2.d.ts +56 -0
  20. package/dist/evm/abis/provisionerV2.d.ts.map +1 -1
  21. package/dist/evm/abis/provisionerV2.js +33 -0
  22. package/dist/evm/abis/provisionerV2.js.map +1 -1
  23. package/dist/evm/adapters/aera.d.ts.map +1 -1
  24. package/dist/evm/adapters/aera.js +41 -10
  25. package/dist/evm/adapters/aera.js.map +1 -1
  26. package/dist/evm/adapters/types.d.ts +2 -0
  27. package/dist/evm/adapters/types.d.ts.map +1 -1
  28. package/dist/evm/aeraContracts/priceAndFeeCalculator.d.ts +10 -5
  29. package/dist/evm/aeraContracts/priceAndFeeCalculator.d.ts.map +1 -1
  30. package/dist/evm/aeraContracts/priceAndFeeCalculator.js +39 -5
  31. package/dist/evm/aeraContracts/priceAndFeeCalculator.js.map +1 -1
  32. package/dist/evm/aeraContracts/readOptions.d.ts +8 -0
  33. package/dist/evm/aeraContracts/readOptions.d.ts.map +1 -0
  34. package/dist/evm/aeraContracts/readOptions.js +4 -0
  35. package/dist/evm/aeraContracts/readOptions.js.map +1 -0
  36. package/dist/evm/aeraContracts/v2.d.ts +373 -2
  37. package/dist/evm/aeraContracts/v2.d.ts.map +1 -1
  38. package/dist/evm/aeraContracts/v2.js +84 -28
  39. package/dist/evm/aeraContracts/v2.js.map +1 -1
  40. package/dist/evm/aeraContracts.d.ts +9 -3
  41. package/dist/evm/aeraContracts.d.ts.map +1 -1
  42. package/dist/evm/aeraContracts.js +69 -8
  43. package/dist/evm/aeraContracts.js.map +1 -1
  44. package/dist/evm/deposit.d.ts.map +1 -1
  45. package/dist/evm/deposit.js +1 -1
  46. package/dist/evm/deposit.js.map +1 -1
  47. package/dist/evm/index.d.ts +8 -0
  48. package/dist/evm/index.d.ts.map +1 -1
  49. package/dist/evm/index.js +3 -0
  50. package/dist/evm/index.js.map +1 -1
  51. package/dist/evm/tokenModes.d.ts +22 -0
  52. package/dist/evm/tokenModes.d.ts.map +1 -0
  53. package/dist/evm/tokenModes.js +34 -0
  54. package/dist/evm/tokenModes.js.map +1 -0
  55. package/dist/evm/withdraw.d.ts +8 -0
  56. package/dist/evm/withdraw.d.ts.map +1 -1
  57. package/dist/evm/withdraw.js +85 -5
  58. package/dist/evm/withdraw.js.map +1 -1
  59. package/dist/evm/withdrawQuote.d.ts +127 -0
  60. package/dist/evm/withdrawQuote.d.ts.map +1 -0
  61. package/dist/evm/withdrawQuote.js +164 -0
  62. package/dist/evm/withdrawQuote.js.map +1 -0
  63. package/dist/index.d.ts +7 -1
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +3 -1
  66. package/dist/index.js.map +1 -1
  67. package/manifest/vaults.json +112 -154
  68. package/package.json +1 -1
  69. package/src/api/client.ts +35 -2
  70. package/src/api/generated.ts +51 -7
  71. package/src/api/index.ts +5 -0
  72. package/src/errors.ts +32 -2
  73. package/src/evm/abis/priceAndFeeCalculatorV2.ts +22 -0
  74. package/src/evm/abis/provisionerV2.ts +33 -0
  75. package/src/evm/adapters/aera.ts +50 -23
  76. package/src/evm/adapters/types.ts +2 -0
  77. package/src/evm/aeraContracts/priceAndFeeCalculator.ts +72 -5
  78. package/src/evm/aeraContracts/readOptions.ts +3 -0
  79. package/src/evm/aeraContracts/v2.ts +158 -34
  80. package/src/evm/aeraContracts.ts +81 -8
  81. package/src/evm/deposit.ts +2 -1
  82. package/src/evm/index.ts +32 -0
  83. package/src/evm/tokenModes.ts +54 -0
  84. package/src/evm/withdraw.ts +131 -7
  85. package/src/evm/withdrawQuote.ts +360 -0
  86. package/src/index.ts +16 -0
@@ -13,8 +13,9 @@ import {
13
13
  convertUnitsToTokenIfActive,
14
14
  } from './priceAndFeeCalculator';
15
15
  import { MAX_BPS } from '../../constants';
16
- import { StalePriceError } from '../../errors';
16
+ import { StalePriceError, UnsupportedFeatureError } from '../../errors';
17
17
  import type { ContractVersion } from '../types';
18
+ import { readAtBlock } from './readOptions';
18
19
 
19
20
  export type PriceAndFeeCalculatorContract<T extends Client> = GetContractReturnType<
20
21
  typeof priceAndFeeCalculatorV2Abi,
@@ -33,16 +34,44 @@ export function getPriceAndFeeCalculator<T extends Client>(
33
34
  });
34
35
  }
35
36
 
36
- function getProvisioner<T extends Client>(client: T, address: Address) {
37
- return getContract({
38
- address,
39
- abi: provisionerV2Abi,
40
- client,
41
- });
37
+ type SyncRedeemReadOptions = {
38
+ blockNumber?: bigint;
39
+ blockTimestamp?: bigint;
40
+ };
41
+
42
+ async function resolveReadContext(
43
+ client: PublicClient,
44
+ options: SyncRedeemReadOptions = {}
45
+ ): Promise<Required<SyncRedeemReadOptions>> {
46
+ if (options.blockNumber !== undefined && options.blockTimestamp !== undefined) {
47
+ return { blockNumber: options.blockNumber, blockTimestamp: options.blockTimestamp };
48
+ }
49
+
50
+ const block =
51
+ options.blockNumber === undefined ? await client.getBlock() : await client.getBlock(options);
52
+ if (block.number === null) {
53
+ throw new UnsupportedFeatureError('Aera: sync redeem reads require a numbered block');
54
+ }
55
+
56
+ return {
57
+ blockNumber: options.blockNumber ?? block.number,
58
+ blockTimestamp: options.blockTimestamp ?? BigInt(block.timestamp),
59
+ };
42
60
  }
43
61
 
44
- async function getTokenDetails(client: PublicClient, provisioner: Address, token: Address) {
45
- return getProvisioner(client, provisioner).read.tokensDetails([token]);
62
+ async function getTokenDetails(
63
+ client: PublicClient,
64
+ provisioner: Address,
65
+ token: Address,
66
+ options: SyncRedeemReadOptions = {}
67
+ ) {
68
+ return client.readContract({
69
+ address: provisioner,
70
+ abi: provisionerV2Abi,
71
+ functionName: 'tokensDetails',
72
+ args: [token],
73
+ ...readAtBlock(options),
74
+ });
46
75
  }
47
76
 
48
77
  async function getDepositUnitsOut({
@@ -54,6 +83,7 @@ async function getDepositUnitsOut({
54
83
  token,
55
84
  tokensIn,
56
85
  multiplierIndex,
86
+ options,
57
87
  }: {
58
88
  client: PublicClient;
59
89
  provisioner: Address;
@@ -63,8 +93,9 @@ async function getDepositUnitsOut({
63
93
  token: Address;
64
94
  tokensIn: bigint;
65
95
  multiplierIndex: 4 | 6;
96
+ options?: SyncRedeemReadOptions;
66
97
  }): Promise<bigint> {
67
- const tokenDetails = await getTokenDetails(client, provisioner, token);
98
+ const tokenDetails = await getTokenDetails(client, provisioner, token, options);
68
99
  const multiplier = BigInt(tokenDetails[multiplierIndex]);
69
100
  const adjustedTokensIn = (tokensIn * multiplier) / MAX_BPS;
70
101
 
@@ -75,7 +106,8 @@ async function getDepositUnitsOut({
75
106
  vault,
76
107
  token,
77
108
  adjustedTokensIn,
78
- Rounding.Floor
109
+ Rounding.Floor,
110
+ options
79
111
  );
80
112
  }
81
113
 
@@ -88,6 +120,7 @@ async function getRedeemTokenOut({
88
120
  token,
89
121
  unitsIn,
90
122
  multiplierIndex,
123
+ options,
91
124
  }: {
92
125
  client: PublicClient;
93
126
  provisioner: Address;
@@ -97,9 +130,10 @@ async function getRedeemTokenOut({
97
130
  token: Address;
98
131
  unitsIn: bigint;
99
132
  multiplierIndex: 5 | 7;
133
+ options?: SyncRedeemReadOptions;
100
134
  }): Promise<bigint> {
101
135
  const [tokenDetails, tokensOut] = await Promise.all([
102
- getTokenDetails(client, provisioner, token),
136
+ getTokenDetails(client, provisioner, token, options),
103
137
  convertUnitsToTokenIfActive(
104
138
  client,
105
139
  feeCalculator,
@@ -107,7 +141,8 @@ async function getRedeemTokenOut({
107
141
  vault,
108
142
  token,
109
143
  unitsIn,
110
- Rounding.Floor
144
+ Rounding.Floor,
145
+ options
111
146
  ),
112
147
  ]);
113
148
  const multiplier = BigInt(tokenDetails[multiplierIndex]);
@@ -124,6 +159,7 @@ async function getWithdrawUnitsIn({
124
159
  token,
125
160
  tokensOut,
126
161
  multiplierIndex,
162
+ options,
127
163
  }: {
128
164
  client: PublicClient;
129
165
  provisioner: Address;
@@ -133,8 +169,9 @@ async function getWithdrawUnitsIn({
133
169
  token: Address;
134
170
  tokensOut: bigint;
135
171
  multiplierIndex: 5 | 7;
172
+ options?: SyncRedeemReadOptions;
136
173
  }): Promise<bigint> {
137
- const tokenDetails = await getTokenDetails(client, provisioner, token);
174
+ const tokenDetails = await getTokenDetails(client, provisioner, token, options);
138
175
  const multiplier = BigInt(tokenDetails[multiplierIndex]);
139
176
  const preMultiplierTokens = (tokensOut * MAX_BPS + multiplier - 1n) / multiplier;
140
177
 
@@ -145,17 +182,37 @@ async function getWithdrawUnitsIn({
145
182
  vault,
146
183
  token,
147
184
  preMultiplierTokens,
148
- Rounding.Ceil
185
+ Rounding.Ceil,
186
+ options
149
187
  );
150
188
  }
151
189
 
152
- async function getSyncRedeemMultiplier(
190
+ /** Breakdown of the effective sync redeem multiplier, in basis points. */
191
+ export interface SyncRedeemRate {
192
+ /** Base sync redeem multiplier (tokensDetails.syncRedeemMultiplier). */
193
+ baseMultiplierBps: bigint;
194
+ /** Dynamic premium charged for the current oracle price age. */
195
+ dynamicPremiumBps: bigint;
196
+ /** Multiplier actually applied to size the redemption (base - dynamic premium). */
197
+ effectiveMultiplierBps: bigint;
198
+ }
199
+
200
+ /**
201
+ * Reads the live sync redeem rate, replicating the provisioner's effective-multiplier
202
+ * derivation (base multiplier minus a price-age-proportional dynamic premium).
203
+ *
204
+ * @throws {StalePriceError} If the oracle price is older than the configured max age,
205
+ * in which case the on-chain sync redeem would revert.
206
+ */
207
+ export async function getSyncRedeemRate(
153
208
  client: PublicClient,
154
209
  provisioner: Address,
155
210
  vault: Address,
156
211
  token: Address,
157
- feeCalculator: Address
158
- ): Promise<bigint> {
212
+ feeCalculator: Address,
213
+ options: SyncRedeemReadOptions = {}
214
+ ): Promise<SyncRedeemRate> {
215
+ const readContext = await resolveReadContext(client, options);
159
216
  const [tokenDetails, syncRedeemDetails, anchorTimestamp] = await client.multicall({
160
217
  contracts: [
161
218
  {
@@ -177,12 +234,12 @@ async function getSyncRedeemMultiplier(
177
234
  },
178
235
  ],
179
236
  allowFailure: false,
237
+ blockNumber: readContext.blockNumber,
180
238
  });
181
- const block = await client.getBlock();
182
- const syncRedeemMultiplier = BigInt(tokenDetails[7]);
239
+ const baseMultiplierBps = BigInt(tokenDetails[7]);
183
240
  const maxPriceAge = BigInt(syncRedeemDetails[0]);
184
241
  const maxDynamicPremiumBps = BigInt(syncRedeemDetails[2]);
185
- const blockTimestamp = BigInt(block.timestamp);
242
+ const blockTimestamp = readContext.blockTimestamp;
186
243
  const priceTimestamp = BigInt(anchorTimestamp);
187
244
 
188
245
  if (priceTimestamp + maxPriceAge < blockTimestamp) {
@@ -198,7 +255,23 @@ async function getSyncRedeemMultiplier(
198
255
  ? 0n
199
256
  : ((blockTimestamp - priceTimestamp) * maxDynamicPremiumBps + maxPriceAge - 1n) / maxPriceAge;
200
257
 
201
- return syncRedeemMultiplier - dynamicPremiumBps;
258
+ return {
259
+ baseMultiplierBps,
260
+ dynamicPremiumBps,
261
+ effectiveMultiplierBps: baseMultiplierBps - dynamicPremiumBps,
262
+ };
263
+ }
264
+
265
+ async function getSyncRedeemMultiplier(
266
+ client: PublicClient,
267
+ provisioner: Address,
268
+ vault: Address,
269
+ token: Address,
270
+ feeCalculator: Address,
271
+ options: SyncRedeemReadOptions = {}
272
+ ): Promise<bigint> {
273
+ return (await getSyncRedeemRate(client, provisioner, vault, token, feeCalculator, options))
274
+ .effectiveMultiplierBps;
202
275
  }
203
276
 
204
277
  export async function getSyncRedeemTokenOut(
@@ -208,8 +281,12 @@ export async function getSyncRedeemTokenOut(
208
281
  feeCalculatorVersion: ContractVersion,
209
282
  vault: Address,
210
283
  token: Address,
211
- unitsIn: bigint
284
+ unitsIn: bigint,
285
+ effectiveMultiplierBps?: bigint,
286
+ options: SyncRedeemReadOptions = {}
212
287
  ): Promise<bigint> {
288
+ const readContext =
289
+ effectiveMultiplierBps === undefined ? await resolveReadContext(client, options) : options;
213
290
  const [tokenAmount, multiplier] = await Promise.all([
214
291
  convertUnitsToTokenIfActive(
215
292
  client,
@@ -218,9 +295,12 @@ export async function getSyncRedeemTokenOut(
218
295
  vault,
219
296
  token,
220
297
  unitsIn,
221
- Rounding.Floor
298
+ Rounding.Floor,
299
+ readContext
222
300
  ),
223
- getSyncRedeemMultiplier(client, provisioner, vault, token, feeCalculator),
301
+ effectiveMultiplierBps !== undefined
302
+ ? Promise.resolve(effectiveMultiplierBps)
303
+ : getSyncRedeemMultiplier(client, provisioner, vault, token, feeCalculator, readContext),
224
304
  ]);
225
305
 
226
306
  return (tokenAmount * multiplier) / MAX_BPS;
@@ -317,15 +397,15 @@ export async function getSyncWithdrawUnitsIn(
317
397
  feeCalculatorVersion: ContractVersion,
318
398
  vault: Address,
319
399
  token: Address,
320
- tokensOut: bigint
400
+ tokensOut: bigint,
401
+ effectiveMultiplierBps?: bigint,
402
+ options: SyncRedeemReadOptions = {}
321
403
  ): Promise<bigint> {
322
- const multiplier = await getSyncRedeemMultiplier(
323
- client,
324
- provisioner,
325
- vault,
326
- token,
327
- feeCalculator
328
- );
404
+ const readContext =
405
+ effectiveMultiplierBps === undefined ? await resolveReadContext(client, options) : options;
406
+ const multiplier =
407
+ effectiveMultiplierBps ??
408
+ (await getSyncRedeemMultiplier(client, provisioner, vault, token, feeCalculator, readContext));
329
409
  const prePremiumTokens = (tokensOut * MAX_BPS + multiplier - 1n) / multiplier;
330
410
 
331
411
  return convertTokenToUnitsIfActive(
@@ -335,10 +415,54 @@ export async function getSyncWithdrawUnitsIn(
335
415
  vault,
336
416
  token,
337
417
  prePremiumTokens,
338
- Rounding.Ceil
418
+ Rounding.Ceil,
419
+ readContext
339
420
  );
340
421
  }
341
422
 
423
+ /** Current sync redeem epoch accounting, in numeraire (all values uint256). */
424
+ export interface SyncRedeemEpochState {
425
+ epochTimestamp: bigint;
426
+ epochStartTvlNumeraire: bigint;
427
+ epochRedeemedNumeraire: bigint;
428
+ epochCapNumeraire: bigint;
429
+ }
430
+
431
+ export async function getSyncRedeemEpochState(
432
+ client: PublicClient,
433
+ provisioner: Address,
434
+ options: SyncRedeemReadOptions = {}
435
+ ): Promise<SyncRedeemEpochState> {
436
+ const [epochTimestamp, epochStartTvlNumeraire, epochRedeemedNumeraire, epochCapNumeraire] =
437
+ await client.readContract({
438
+ address: provisioner,
439
+ abi: provisionerV2Abi,
440
+ functionName: 'getSyncRedeemEpochState',
441
+ ...readAtBlock(options),
442
+ });
443
+
444
+ return { epochTimestamp, epochStartTvlNumeraire, epochRedeemedNumeraire, epochCapNumeraire };
445
+ }
446
+
447
+ /**
448
+ * Raw expiry timestamp for the user's latest sync-deposit lock (0 if none was recorded).
449
+ * The user is locked while this value is greater than or equal to the relevant block timestamp.
450
+ */
451
+ export async function getUserUnitsRefundableUntil(
452
+ client: PublicClient,
453
+ provisioner: Address,
454
+ user: Address,
455
+ options: SyncRedeemReadOptions = {}
456
+ ): Promise<bigint> {
457
+ return client.readContract({
458
+ address: provisioner,
459
+ abi: provisionerV2Abi,
460
+ functionName: 'userUnitsRefundableUntil',
461
+ args: [user],
462
+ ...readAtBlock(options),
463
+ });
464
+ }
465
+
342
466
  export function depositTxRequest(
343
467
  provisioner: Address,
344
468
  token: Address,
@@ -15,6 +15,7 @@ import { provisionerAbi } from './abis/provisioner';
15
15
  import { provisionerV2Abi } from './abis/provisionerV2';
16
16
  import { MAX_BPS } from '../constants';
17
17
  import { ContractVersion, type EvmVaultDeployment } from './types';
18
+ import { readAtBlock } from './aeraContracts/readOptions';
18
19
 
19
20
  const contractVersionCache = new Map<string, ContractVersion>();
20
21
 
@@ -28,6 +29,19 @@ const contractVersionAbi = [
28
29
  },
29
30
  ] as const;
30
31
 
32
+ const solvingGateAbi = [
33
+ {
34
+ type: 'function',
35
+ inputs: [
36
+ { name: 'provisioner', internalType: 'address', type: 'address' },
37
+ { name: 'token', internalType: 'address', type: 'address' },
38
+ ],
39
+ name: 'paused',
40
+ outputs: [{ name: '', internalType: 'bool', type: 'bool' }],
41
+ stateMutability: 'view',
42
+ },
43
+ ] as const;
44
+
31
45
  export interface AeraRuntimeContracts {
32
46
  provisioner: {
33
47
  address: Address;
@@ -46,6 +60,19 @@ export interface AeraTokenModeSupport {
46
60
  syncRedeem: boolean;
47
61
  }
48
62
 
63
+ export function requireAeraSyncRedeemRuntime(runtime: AeraRuntimeContracts): void {
64
+ if (runtime.provisioner.version !== ContractVersion.V2) {
65
+ throw new UnsupportedFeatureError('Aera: sync redeem requires a V2 provisioner');
66
+ }
67
+ if (runtime.feeCalculator.version !== ContractVersion.V2) {
68
+ throw new UnsupportedFeatureError('Aera: sync redeem requires a V2 price and fee calculator');
69
+ }
70
+ }
71
+
72
+ export type AeraReadOptions = {
73
+ blockNumber?: bigint;
74
+ };
75
+
49
76
  export function applySlippageDown(amount: bigint, slippageBps: number): bigint {
50
77
  return (amount * (MAX_BPS - BigInt(slippageBps))) / MAX_BPS;
51
78
  }
@@ -97,7 +124,8 @@ export function getMultiDepositorVault<T extends Client>(
97
124
 
98
125
  export async function resolveContractVersion(
99
126
  client: PublicClient,
100
- address: Address
127
+ address: Address,
128
+ options: AeraReadOptions = {}
101
129
  ): Promise<ContractVersion> {
102
130
  const cacheKey = contractVersionCacheKey(client, address);
103
131
  const cached = contractVersionCache.get(cacheKey);
@@ -108,6 +136,7 @@ export async function resolveContractVersion(
108
136
  address,
109
137
  abi: contractVersionAbi,
110
138
  functionName: 'version',
139
+ ...readAtBlock(options),
111
140
  });
112
141
  const version = versionString.startsWith('2.') ? ContractVersion.V2 : ContractVersion.V1;
113
142
  contractVersionCache.set(cacheKey, version);
@@ -123,7 +152,8 @@ export async function resolveContractVersion(
123
152
 
124
153
  export async function resolveAeraRuntimeContracts(
125
154
  client: PublicClient,
126
- vault: EvmVaultDeployment
155
+ vault: EvmVaultDeployment,
156
+ options: AeraReadOptions = {}
127
157
  ): Promise<AeraRuntimeContracts> {
128
158
  if (vault.vaultType !== 'multi-depositor') {
129
159
  throw new UnimplementedFeatureError('Aera: single depositor vaults');
@@ -134,11 +164,13 @@ export async function resolveAeraRuntimeContracts(
134
164
  address: vault.vaultAddress,
135
165
  abi: multiDepositorVaultAbi,
136
166
  functionName: 'provisioner',
167
+ ...readAtBlock(options),
137
168
  }),
138
169
  client.readContract({
139
170
  address: vault.vaultAddress,
140
171
  abi: multiDepositorVaultAbi,
141
172
  functionName: 'feeCalculator',
173
+ ...readAtBlock(options),
142
174
  }),
143
175
  ]);
144
176
 
@@ -150,8 +182,8 @@ export async function resolveAeraRuntimeContracts(
150
182
  }
151
183
 
152
184
  const [provisionerVersion, feeCalculatorVersion] = await Promise.all([
153
- resolveContractVersion(client, provisionerAddress),
154
- resolveContractVersion(client, feeCalculatorAddress),
185
+ resolveContractVersion(client, provisionerAddress, options),
186
+ resolveContractVersion(client, feeCalculatorAddress, options),
155
187
  ]);
156
188
 
157
189
  return {
@@ -169,21 +201,61 @@ export async function resolveAeraRuntimeContracts(
169
201
  export async function resolveAeraTokenModeSupport(
170
202
  client: PublicClient,
171
203
  runtime: AeraRuntimeContracts,
172
- token: Address
204
+ token: Address,
205
+ options: AeraReadOptions & { includeSyncModes?: boolean } = {}
173
206
  ): Promise<AeraTokenModeSupport> {
174
207
  if (runtime.provisioner.version === ContractVersion.V2) {
175
- const tokenDetails = await client.readContract({
208
+ const tokenDetailsPromise = client.readContract({
176
209
  address: runtime.provisioner.address,
177
210
  abi: provisionerV2Abi,
178
211
  functionName: 'tokensDetails',
179
212
  args: [token],
213
+ ...readAtBlock(options),
180
214
  });
215
+ if (options.includeSyncModes === false) {
216
+ const tokenDetails = await tokenDetailsPromise;
217
+ return {
218
+ asyncDeposit: Boolean(tokenDetails[0]),
219
+ asyncRedeem: Boolean(tokenDetails[1]),
220
+ syncDeposit: false,
221
+ syncRedeem: false,
222
+ };
223
+ }
224
+
225
+ const [tokenDetails, solvingGateEnabled, solvingGate] = await Promise.all([
226
+ tokenDetailsPromise,
227
+ client.readContract({
228
+ address: runtime.provisioner.address,
229
+ abi: provisionerV2Abi,
230
+ functionName: 'SOLVING_GATE_ENABLED',
231
+ ...readAtBlock(options),
232
+ }),
233
+ client.readContract({
234
+ address: runtime.provisioner.address,
235
+ abi: provisionerV2Abi,
236
+ functionName: 'solvingGate',
237
+ ...readAtBlock(options),
238
+ }),
239
+ ]);
240
+ const solvingPaused =
241
+ solvingGateEnabled && solvingGate !== zeroAddress
242
+ ? await client.readContract({
243
+ address: solvingGate,
244
+ abi: solvingGateAbi,
245
+ functionName: 'paused',
246
+ args: [runtime.provisioner.address, token],
247
+ ...readAtBlock(options),
248
+ })
249
+ : false;
181
250
 
182
251
  return {
183
252
  asyncDeposit: Boolean(tokenDetails[0]),
184
253
  asyncRedeem: Boolean(tokenDetails[1]),
185
- syncDeposit: Boolean(tokenDetails[2]),
186
- syncRedeem: Boolean(tokenDetails[3]),
254
+ syncDeposit: Boolean(tokenDetails[2]) && !solvingPaused,
255
+ syncRedeem:
256
+ Boolean(tokenDetails[3]) &&
257
+ !solvingPaused &&
258
+ runtime.feeCalculator.version === ContractVersion.V2,
187
259
  };
188
260
  }
189
261
 
@@ -192,6 +264,7 @@ export async function resolveAeraTokenModeSupport(
192
264
  abi: provisionerAbi,
193
265
  functionName: 'tokensDetails',
194
266
  args: [token],
267
+ ...readAtBlock(options),
195
268
  });
196
269
 
197
270
  return {
@@ -113,7 +113,8 @@ export async function getDepositTx(
113
113
  const tokenModeSupport = await resolveAeraTokenModeSupport(
114
114
  publicClient,
115
115
  aeraRuntime,
116
- token.address
116
+ token.address,
117
+ { includeSyncModes: requestedDepositMode !== 'async' }
117
118
  );
118
119
  modifiedDepositMode = resolveOperationMode(params.vaultId, requestedDepositMode, {
119
120
  async: tokenModeSupport.asyncDeposit,
package/src/evm/index.ts CHANGED
@@ -1,10 +1,30 @@
1
1
  export { getDepositTx } from './deposit';
2
2
  export { getDepositReceiverApprovalTx } from './depositReceiverApproval';
3
3
  export { getWithdrawTx } from './withdraw';
4
+ export { getSyncWithdrawQuote } from './withdrawQuote';
5
+ export { getAeraTokenModeSupport } from './tokenModes';
4
6
  export { getUserCurrentBalance } from './userCurrentBalance';
5
7
  export { getVaults, VaultId } from './vaults';
6
8
  export { ContractVersion } from './types';
7
9
  export { resolveAeraRuntimeContracts, resolveContractVersion } from './aeraContracts';
10
+ export {
11
+ GauntletSDKError,
12
+ AccountRequiredError,
13
+ AccountMismatchError,
14
+ ChainMismatchError,
15
+ InvalidSlippageBPSError,
16
+ InvalidSolverTipError,
17
+ InvalidSyncWithdrawBoundError,
18
+ InvalidWithdrawParamsError,
19
+ RpcNotConfiguredError,
20
+ StalePriceError,
21
+ UnsupportedAssetError,
22
+ UnsupportedDepositModeError,
23
+ UnsupportedFeatureError,
24
+ UnsupportedProtocolError,
25
+ UnimplementedFeatureError,
26
+ VaultNotFoundError,
27
+ } from '../errors';
8
28
  export {
9
29
  Rounding,
10
30
  convertTokenToUnits,
@@ -19,7 +39,18 @@ export {
19
39
  export type { EvmDepositParams } from './deposit';
20
40
  export type { EvmDepositReceiverApprovalParams } from './depositReceiverApproval';
21
41
  export type { EvmWithdrawParams } from './withdraw';
42
+ export type {
43
+ SyncWithdrawQuote,
44
+ SyncWithdrawQuoteParams,
45
+ SyncWithdrawCapacity,
46
+ SyncWithdrawQuoteContext,
47
+ SyncWithdrawQuoteRequest,
48
+ SyncWithdrawQuoteBounds,
49
+ } from './withdrawQuote';
50
+ export type { AeraTokenModeSupportParams } from './tokenModes';
51
+ export type { SyncRedeemRate } from './aeraContracts/v2';
22
52
  export type { EvmTxStep } from './adapters/types';
53
+ export type { PreparedTx } from '../attribution';
23
54
  export type {
24
55
  VaultInfo,
25
56
  VaultDeployment,
@@ -31,3 +62,4 @@ export type { VaultFilter } from './vaults';
31
62
  export type { UserCurrentBalanceParams, UserCurrentBalance } from './userCurrentBalance';
32
63
  export type { AeraRuntimeContracts, AeraTokenModeSupport } from './aeraContracts';
33
64
  export type { NormalizedVaultPriceState } from './aeraContracts/priceAndFeeCalculator';
65
+ export type { SyncWithdrawBound } from '../errors';
@@ -0,0 +1,54 @@
1
+ import type { GauntletClient } from '../client';
2
+ import { UnsupportedAssetError, UnsupportedFeatureError, VaultNotFoundError } from '../errors';
3
+ import { resolveVault } from './vaults';
4
+ import {
5
+ resolveAeraRuntimeContracts,
6
+ resolveAeraTokenModeSupport,
7
+ type AeraTokenModeSupport,
8
+ } from './aeraContracts';
9
+
10
+ export interface AeraTokenModeSupportParams {
11
+ vaultId: string;
12
+ /** EVM chain ID. Defaults to the vault's primary chain (Base for current multichain vaults). */
13
+ chainId?: number;
14
+ /** Required for multiasset vaults. */
15
+ assetSymbol?: string;
16
+ }
17
+
18
+ /**
19
+ * Reads which deposit/redeem modes (sync vs async) are currently enabled on-chain for a vault token.
20
+ *
21
+ * Use this to decide which entry points to offer a user (e.g. whether an instant withdraw is
22
+ * available) before any amount is entered. Mode availability includes the provisioner's token
23
+ * settings and live runtime gates. A paused V2 solving gate disables both sync modes.
24
+ *
25
+ * @throws {VaultNotFoundError} If the vault ID is not found.
26
+ * @throws {UnsupportedFeatureError} If the vault is not an Aera deployment.
27
+ * @throws {UnsupportedAssetError} If the asset symbol is not accepted by the vault.
28
+ */
29
+ export async function getAeraTokenModeSupport(
30
+ client: GauntletClient,
31
+ params: AeraTokenModeSupportParams
32
+ ): Promise<AeraTokenModeSupport> {
33
+ const resolved = await resolveVault(client, params.vaultId, params.chainId);
34
+ if (!resolved) throw new VaultNotFoundError(params.vaultId);
35
+
36
+ const { vault, protocol } = resolved;
37
+ if (protocol !== 'aera') {
38
+ throw new UnsupportedFeatureError('Token mode support is only available for Aera vaults');
39
+ }
40
+
41
+ const chainId = params.chainId ?? vault.chainId;
42
+ const publicClient = client.getPublicClient(chainId);
43
+
44
+ const token =
45
+ vault.supplyToken.length > 1
46
+ ? vault.supplyToken.find((tInfo) => tInfo.symbol === params.assetSymbol)
47
+ : vault.supplyToken[0];
48
+ if (token === undefined) {
49
+ throw new UnsupportedAssetError(params.assetSymbol ?? 'unknown', params.vaultId);
50
+ }
51
+
52
+ const runtime = await resolveAeraRuntimeContracts(publicClient, vault);
53
+ return resolveAeraTokenModeSupport(publicClient, runtime, token.address);
54
+ }