@gauntlet-xyz/sdk 1.0.6 → 2.3.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 (145) hide show
  1. package/README.md +82 -1
  2. package/dist/api/activity.d.ts +70 -0
  3. package/dist/api/activity.d.ts.map +1 -0
  4. package/dist/api/activity.js +154 -0
  5. package/dist/api/activity.js.map +1 -0
  6. package/dist/api/caip.d.ts +29 -0
  7. package/dist/api/caip.d.ts.map +1 -0
  8. package/dist/api/caip.js +38 -0
  9. package/dist/api/caip.js.map +1 -0
  10. package/dist/api/client.d.ts +114 -0
  11. package/dist/api/client.d.ts.map +1 -0
  12. package/dist/api/client.js +109 -0
  13. package/dist/api/client.js.map +1 -0
  14. package/dist/api/decimal.d.ts +21 -0
  15. package/dist/api/decimal.d.ts.map +1 -0
  16. package/dist/api/decimal.js +44 -0
  17. package/dist/api/decimal.js.map +1 -0
  18. package/dist/api/errors.d.ts +32 -0
  19. package/dist/api/errors.d.ts.map +1 -0
  20. package/dist/api/errors.js +41 -0
  21. package/dist/api/errors.js.map +1 -0
  22. package/dist/api/generated.d.ts +1461 -0
  23. package/dist/api/generated.d.ts.map +1 -0
  24. package/dist/api/generated.js +6 -0
  25. package/dist/api/generated.js.map +1 -0
  26. package/dist/api/http.d.ts +16 -0
  27. package/dist/api/http.d.ts.map +1 -0
  28. package/dist/api/http.js +39 -0
  29. package/dist/api/http.js.map +1 -0
  30. package/dist/api/index.d.ts +14 -0
  31. package/dist/api/index.d.ts.map +1 -0
  32. package/dist/api/index.js +8 -0
  33. package/dist/api/index.js.map +1 -0
  34. package/dist/api/positionHistory.d.ts +44 -0
  35. package/dist/api/positionHistory.d.ts.map +1 -0
  36. package/dist/api/positionHistory.js +126 -0
  37. package/dist/api/positionHistory.js.map +1 -0
  38. package/dist/client.d.ts +6 -0
  39. package/dist/client.d.ts.map +1 -1
  40. package/dist/client.js +2 -0
  41. package/dist/client.js.map +1 -1
  42. package/dist/constants.d.ts +16 -0
  43. package/dist/constants.d.ts.map +1 -0
  44. package/dist/constants.js +28 -0
  45. package/dist/constants.js.map +1 -0
  46. package/dist/errors.d.ts +18 -3
  47. package/dist/errors.d.ts.map +1 -1
  48. package/dist/errors.js +20 -2
  49. package/dist/errors.js.map +1 -1
  50. package/dist/evm/abis/priceAndFeeCalculatorV2.d.ts +233 -0
  51. package/dist/evm/abis/priceAndFeeCalculatorV2.d.ts.map +1 -0
  52. package/dist/evm/abis/priceAndFeeCalculatorV2.js +119 -0
  53. package/dist/evm/abis/priceAndFeeCalculatorV2.js.map +1 -0
  54. package/dist/evm/abis/provisionerV2.d.ts +684 -0
  55. package/dist/evm/abis/provisionerV2.d.ts.map +1 -0
  56. package/dist/evm/abis/provisionerV2.js +262 -0
  57. package/dist/evm/abis/provisionerV2.js.map +1 -0
  58. package/dist/evm/adapters/aera.d.ts +3 -3
  59. package/dist/evm/adapters/aera.d.ts.map +1 -1
  60. package/dist/evm/adapters/aera.js +179 -103
  61. package/dist/evm/adapters/aera.js.map +1 -1
  62. package/dist/evm/adapters/types.d.ts +12 -1
  63. package/dist/evm/adapters/types.d.ts.map +1 -1
  64. package/dist/evm/aeraContracts/priceAndFeeCalculator.d.ts +41 -0
  65. package/dist/evm/aeraContracts/priceAndFeeCalculator.d.ts.map +1 -0
  66. package/dist/evm/aeraContracts/priceAndFeeCalculator.js +140 -0
  67. package/dist/evm/aeraContracts/priceAndFeeCalculator.js.map +1 -0
  68. package/dist/evm/aeraContracts/v1.d.ts +2317 -0
  69. package/dist/evm/aeraContracts/v1.d.ts.map +1 -0
  70. package/dist/evm/aeraContracts/v1.js +59 -0
  71. package/dist/evm/aeraContracts/v1.js.map +1 -0
  72. package/dist/evm/aeraContracts/v2.d.ts +4146 -0
  73. package/dist/evm/aeraContracts/v2.d.ts.map +1 -0
  74. package/dist/evm/aeraContracts/v2.js +217 -0
  75. package/dist/evm/aeraContracts/v2.js.map +1 -0
  76. package/dist/evm/aeraContracts.d.ts +23 -2316
  77. package/dist/evm/aeraContracts.d.ts.map +1 -1
  78. package/dist/evm/aeraContracts.js +117 -24
  79. package/dist/evm/aeraContracts.js.map +1 -1
  80. package/dist/evm/deposit.d.ts +5 -1
  81. package/dist/evm/deposit.d.ts.map +1 -1
  82. package/dist/evm/deposit.js +28 -14
  83. package/dist/evm/deposit.js.map +1 -1
  84. package/dist/evm/depositReceiverApproval.d.ts +17 -0
  85. package/dist/evm/depositReceiverApproval.d.ts.map +1 -0
  86. package/dist/evm/depositReceiverApproval.js +32 -0
  87. package/dist/evm/depositReceiverApproval.js.map +1 -0
  88. package/dist/evm/index.d.ts +8 -1
  89. package/dist/evm/index.d.ts.map +1 -1
  90. package/dist/evm/index.js +4 -0
  91. package/dist/evm/index.js.map +1 -1
  92. package/dist/evm/operationMode.d.ts +9 -0
  93. package/dist/evm/operationMode.d.ts.map +1 -0
  94. package/dist/evm/operationMode.js +41 -0
  95. package/dist/evm/operationMode.js.map +1 -0
  96. package/dist/evm/types.d.ts +5 -3
  97. package/dist/evm/types.d.ts.map +1 -1
  98. package/dist/evm/types.js +4 -1
  99. package/dist/evm/types.js.map +1 -1
  100. package/dist/evm/userCurrentBalance.d.ts.map +1 -1
  101. package/dist/evm/userCurrentBalance.js +68 -58
  102. package/dist/evm/userCurrentBalance.js.map +1 -1
  103. package/dist/evm/withdraw.d.ts +5 -1
  104. package/dist/evm/withdraw.d.ts.map +1 -1
  105. package/dist/evm/withdraw.js +25 -13
  106. package/dist/evm/withdraw.js.map +1 -1
  107. package/dist/index.d.ts +9 -1
  108. package/dist/index.d.ts.map +1 -1
  109. package/dist/index.js +6 -1
  110. package/dist/index.js.map +1 -1
  111. package/dist/privy/index.d.ts +52 -0
  112. package/dist/privy/index.d.ts.map +1 -0
  113. package/dist/privy/index.js +51 -0
  114. package/dist/privy/index.js.map +1 -0
  115. package/manifest/vaults.json +316 -132
  116. package/package.json +2 -2
  117. package/src/api/activity.ts +219 -0
  118. package/src/api/caip.ts +61 -0
  119. package/src/api/client.ts +201 -0
  120. package/src/api/decimal.ts +49 -0
  121. package/src/api/errors.ts +64 -0
  122. package/src/api/generated.ts +1461 -0
  123. package/src/api/http.ts +62 -0
  124. package/src/api/index.ts +65 -0
  125. package/src/api/positionHistory.ts +167 -0
  126. package/src/client.ts +7 -1
  127. package/src/constants.ts +32 -0
  128. package/src/errors.ts +44 -5
  129. package/src/evm/abis/priceAndFeeCalculatorV2.ts +118 -0
  130. package/src/evm/abis/provisionerV2.ts +261 -0
  131. package/src/evm/adapters/aera.ts +419 -137
  132. package/src/evm/adapters/types.ts +19 -1
  133. package/src/evm/aeraContracts/priceAndFeeCalculator.ts +223 -0
  134. package/src/evm/aeraContracts/v1.ts +157 -0
  135. package/src/evm/aeraContracts/v2.ts +466 -0
  136. package/src/evm/aeraContracts.ts +173 -52
  137. package/src/evm/deposit.ts +48 -16
  138. package/src/evm/depositReceiverApproval.ts +51 -0
  139. package/src/evm/index.ts +23 -1
  140. package/src/evm/operationMode.ts +60 -0
  141. package/src/evm/types.ts +7 -3
  142. package/src/evm/userCurrentBalance.ts +80 -55
  143. package/src/evm/withdraw.ts +42 -15
  144. package/src/index.ts +21 -0
  145. package/src/privy/index.ts +95 -0
@@ -6,11 +6,22 @@ import { encodeTransactionWithAttribution, PreparedTx } from '../attribution';
6
6
  import {
7
7
  AccountRequiredError,
8
8
  VaultNotFoundError,
9
- UnsupportedDepositModeError,
10
9
  UnsupportedAssetError,
11
10
  InvalidSlippageBPSError,
12
11
  } from '../errors';
12
+ import {
13
+ resolveAeraRuntimeContracts,
14
+ resolveAeraTokenModeSupport,
15
+ type AeraRuntimeContracts,
16
+ } from './aeraContracts';
13
17
  import { resolveVault } from './vaults';
18
+ import { DEFAULT_BPS, MAX_BPS } from '../constants';
19
+ import {
20
+ parseOperationMode,
21
+ resolveOperationMode,
22
+ resolveSyncOnlyOperationMode,
23
+ type OperationMode,
24
+ } from './operationMode';
14
25
 
15
26
  export interface EvmDepositParams {
16
27
  vaultId: string;
@@ -19,12 +30,16 @@ export interface EvmDepositParams {
19
30
  chainId?: number;
20
31
  // Required for multiasset vaults, not utilized yet
21
32
  assetSymbol?: string;
22
- /** Request async (queued) deposit. Only valid for vaults with depositMode 'async' or 'both'. */
33
+ /** Request async (queued) or sync deposit. Availability is read from live vault configuration. */
23
34
  depositMode?: string;
24
35
  // So a developer is able to specify a separate receiver than the tx sender
25
36
  receiver?: Address;
26
37
  /** Slippage tolerance in basis points (e.g. 100 = 1%). Defaults to 100. */
27
38
  slippageBps?: number;
39
+ /** Solver tip passed to async Aera provisioner requests. Defaults to 0. */
40
+ solverTip?: bigint;
41
+ /** Maximum price age passed to async Aera provisioner requests. Defaults to 10 days. */
42
+ maxPriceAge?: bigint;
28
43
  }
29
44
 
30
45
  /**
@@ -61,7 +76,9 @@ export async function getDepositTx(
61
76
  ): Promise<PreparedTx[]> {
62
77
  if (
63
78
  params.slippageBps !== undefined &&
64
- (!Number.isInteger(params.slippageBps) || params.slippageBps < 0 || params.slippageBps > 10000)
79
+ (!Number.isInteger(params.slippageBps) ||
80
+ params.slippageBps < 0 ||
81
+ params.slippageBps > Number(MAX_BPS))
65
82
  ) {
66
83
  throw new InvalidSlippageBPSError(params.slippageBps);
67
84
  }
@@ -76,18 +93,6 @@ export async function getDepositTx(
76
93
 
77
94
  const { vault, protocol } = resolved;
78
95
 
79
- if (params.depositMode === 'sync' && vault.depositMode === 'async') {
80
- throw new UnsupportedDepositModeError(params.vaultId, 'sync', vault.depositMode);
81
- }
82
- if (params.depositMode === 'async' && vault.depositMode === 'sync') {
83
- throw new UnsupportedDepositModeError(params.vaultId, 'async', vault.depositMode);
84
- }
85
- let modifiedDepositMode = params.depositMode;
86
- if (params.depositMode === undefined) {
87
- modifiedDepositMode = vault.depositMode === 'both' ? 'async' : vault.depositMode;
88
- }
89
-
90
- const spender = vault.provisionerAddress ?? vault.vaultAddress;
91
96
  const adapter = getAdapter(protocol);
92
97
  const publicClient = client.getPublicClient(chainId);
93
98
  const token =
@@ -99,6 +104,30 @@ export async function getDepositTx(
99
104
  throw new UnsupportedAssetError(params.assetSymbol ?? 'unknown', params.vaultId);
100
105
  }
101
106
 
107
+ const requestedDepositMode = parseOperationMode(params.vaultId, params.depositMode);
108
+ let modifiedDepositMode: OperationMode;
109
+ let aeraRuntime: AeraRuntimeContracts | undefined;
110
+
111
+ if (protocol === 'aera') {
112
+ aeraRuntime = await resolveAeraRuntimeContracts(publicClient, vault);
113
+ const tokenModeSupport = await resolveAeraTokenModeSupport(
114
+ publicClient,
115
+ aeraRuntime,
116
+ token.address
117
+ );
118
+ modifiedDepositMode = resolveOperationMode(params.vaultId, requestedDepositMode, {
119
+ async: tokenModeSupport.asyncDeposit,
120
+ sync: tokenModeSupport.syncDeposit,
121
+ });
122
+ } else {
123
+ modifiedDepositMode = resolveSyncOnlyOperationMode(params.vaultId, requestedDepositMode);
124
+ }
125
+
126
+ const spender =
127
+ protocol === 'aera' && modifiedDepositMode === 'async'
128
+ ? aeraRuntime!.provisioner.address
129
+ : vault.vaultAddress;
130
+
102
131
  // NOTE: this could be moved to the buildDeposit level
103
132
  const { sufficient } = await adapter.checkAllowance({
104
133
  publicClient,
@@ -129,7 +158,10 @@ export async function getDepositTx(
129
158
  async: modifiedDepositMode === 'async',
130
159
  asset: token,
131
160
  publicClient,
132
- slippageBps: params.slippageBps,
161
+ slippageBps: params.slippageBps ?? DEFAULT_BPS,
162
+ solverTip: params.solverTip,
163
+ maxPriceAge: params.maxPriceAge,
164
+ aeraRuntime,
133
165
  });
134
166
  steps.push(...depositSteps);
135
167
 
@@ -0,0 +1,51 @@
1
+ import type { Address } from 'viem';
2
+ import type { GauntletClient } from '../client';
3
+ import type { PreparedTx } from '../attribution';
4
+ import { encodeTransactionWithAttribution } from '../attribution';
5
+ import { AccountRequiredError, UnsupportedFeatureError, VaultNotFoundError } from '../errors';
6
+ import { resolveAeraRuntimeContracts } from './aeraContracts';
7
+ import * as provisionerV2 from './aeraContracts/v2';
8
+ import { ContractVersion } from './types';
9
+ import { resolveVault } from './vaults';
10
+
11
+ export interface EvmDepositReceiverApprovalParams {
12
+ vaultId: string;
13
+ depositor: Address;
14
+ approved?: boolean;
15
+ chainId?: number;
16
+ }
17
+
18
+ /**
19
+ * Builds the V2 receiver-side approval transaction required before a depositor
20
+ * can make sync deposits to a separate receiver.
21
+ *
22
+ * The wallet account on the client is the receiver and must sign this transaction.
23
+ */
24
+ export async function getDepositReceiverApprovalTx(
25
+ client: GauntletClient,
26
+ params: EvmDepositReceiverApprovalParams
27
+ ): Promise<PreparedTx> {
28
+ const resolved = await resolveVault(client, params.vaultId, params.chainId);
29
+ if (!resolved) throw new VaultNotFoundError(params.vaultId);
30
+
31
+ const chainId = params.chainId ?? resolved.vault.chainId;
32
+ const receiver = client.wallet?.account?.address;
33
+ if (!receiver) throw new AccountRequiredError();
34
+
35
+ const { vault } = resolved;
36
+ const publicClient = client.getPublicClient(chainId);
37
+ const runtime = await resolveAeraRuntimeContracts(publicClient, vault);
38
+ if (runtime.provisioner.version !== ContractVersion.V2) {
39
+ throw new UnsupportedFeatureError('Aera: deposit receiver approval on V1');
40
+ }
41
+
42
+ return encodeTransactionWithAttribution(client, {
43
+ type: 'setDepositReceiverApproval',
44
+ ...provisionerV2.setDepositReceiverApprovalTxRequest(
45
+ runtime.provisioner.address,
46
+ params.depositor,
47
+ params.approved ?? true,
48
+ receiver
49
+ ),
50
+ });
51
+ }
package/src/evm/index.ts CHANGED
@@ -1,11 +1,33 @@
1
1
  export { getDepositTx } from './deposit';
2
+ export { getDepositReceiverApprovalTx } from './depositReceiverApproval';
2
3
  export { getWithdrawTx } from './withdraw';
3
4
  export { getUserCurrentBalance } from './userCurrentBalance';
4
5
  export { getVaults, VaultId } from './vaults';
6
+ export { ContractVersion } from './types';
7
+ export { resolveAeraRuntimeContracts, resolveContractVersion } from './aeraContracts';
8
+ export {
9
+ Rounding,
10
+ convertTokenToUnits,
11
+ convertTokenToUnitsIfActive,
12
+ convertUnitsToToken,
13
+ convertUnitsToTokenIfActive,
14
+ getAnchorTimestamp,
15
+ getVaultState,
16
+ isVaultPaused,
17
+ } from './aeraContracts/priceAndFeeCalculator';
5
18
 
6
19
  export type { EvmDepositParams } from './deposit';
20
+ export type { EvmDepositReceiverApprovalParams } from './depositReceiverApproval';
7
21
  export type { EvmWithdrawParams } from './withdraw';
8
22
  export type { EvmTxStep } from './adapters/types';
9
- export type { VaultInfo, VaultDeployment, TokenInfo, VaultManifest } from './types';
23
+ export type {
24
+ VaultInfo,
25
+ VaultDeployment,
26
+ EvmVaultDeployment,
27
+ TokenInfo,
28
+ VaultManifest,
29
+ } from './types';
10
30
  export type { VaultFilter } from './vaults';
11
31
  export type { UserCurrentBalanceParams, UserCurrentBalance } from './userCurrentBalance';
32
+ export type { AeraRuntimeContracts, AeraTokenModeSupport } from './aeraContracts';
33
+ export type { NormalizedVaultPriceState } from './aeraContracts/priceAndFeeCalculator';
@@ -0,0 +1,60 @@
1
+ import { UnsupportedDepositModeError } from '../errors';
2
+
3
+ export type OperationMode = 'sync' | 'async';
4
+
5
+ export interface OperationModeSupport {
6
+ sync: boolean;
7
+ async: boolean;
8
+ }
9
+
10
+ function formatAvailableModes(support: OperationModeSupport): string {
11
+ if (support.async && support.sync) return 'both';
12
+ if (support.async) return 'async';
13
+ if (support.sync) return 'sync';
14
+ return 'none';
15
+ }
16
+
17
+ export function parseOperationMode(
18
+ vaultId: string,
19
+ requested: string | undefined
20
+ ): OperationMode | undefined {
21
+ if (requested === undefined || requested === 'sync' || requested === 'async') {
22
+ return requested;
23
+ }
24
+
25
+ throw new UnsupportedDepositModeError(vaultId, requested, 'sync, async');
26
+ }
27
+
28
+ export function resolveOperationMode(
29
+ vaultId: string,
30
+ requested: OperationMode | undefined,
31
+ support: OperationModeSupport
32
+ ): OperationMode {
33
+ const available = formatAvailableModes(support);
34
+
35
+ if (requested === 'sync') {
36
+ if (!support.sync) throw new UnsupportedDepositModeError(vaultId, 'sync', available);
37
+ return 'sync';
38
+ }
39
+
40
+ if (requested === 'async') {
41
+ if (!support.async) throw new UnsupportedDepositModeError(vaultId, 'async', available);
42
+ return 'async';
43
+ }
44
+
45
+ if (support.async) return 'async';
46
+ if (support.sync) return 'sync';
47
+
48
+ throw new UnsupportedDepositModeError(vaultId, 'async', available);
49
+ }
50
+
51
+ export function resolveSyncOnlyOperationMode(
52
+ vaultId: string,
53
+ requested: OperationMode | undefined
54
+ ): OperationMode {
55
+ if (requested === 'async') {
56
+ throw new UnsupportedDepositModeError(vaultId, 'async', 'sync');
57
+ }
58
+
59
+ return 'sync';
60
+ }
package/src/evm/types.ts CHANGED
@@ -6,14 +6,18 @@ export interface TokenInfo {
6
6
  decimals: number;
7
7
  }
8
8
 
9
+ export const ContractVersion = {
10
+ V1: 'v1',
11
+ V2: 'v2',
12
+ } as const;
13
+
14
+ export type ContractVersion = (typeof ContractVersion)[keyof typeof ContractVersion];
15
+
9
16
  export interface EvmVaultDeployment {
10
17
  chain: 'evm';
11
18
  chainId: number;
12
19
  vaultAddress: Address;
13
- /** Present for Aera multi-depositor vaults; absent for single-depositor and Morpho */
14
- provisionerAddress?: Address;
15
20
  vaultType: 'single-depositor' | 'multi-depositor';
16
- depositMode: 'sync' | 'async' | 'both';
17
21
  supplyToken: TokenInfo[];
18
22
  /** Number of days before an async request deadline expires. Defaults to 3. */
19
23
  expirationDays?: number;
@@ -2,10 +2,14 @@ import { parseAbi, type Address } from 'viem';
2
2
  import type { GauntletClient } from '../client';
3
3
  import type { EvmVaultDeployment } from './types';
4
4
  import { ChainMismatchError, UnsupportedProtocolError, VaultNotFoundError } from '../errors';
5
- import { getMultiDepositorVault, getPriceAndFeeCalculator } from './aeraContracts';
5
+ import { getMultiDepositorVault, resolveAeraRuntimeContracts } from './aeraContracts';
6
+ import { convertUnitsToToken } from './aeraContracts/priceAndFeeCalculator';
7
+ import { provisionerV2Abi } from './abis/provisionerV2';
8
+ import { ContractVersion } from './types';
9
+ import { BLOCKS_3_DAYS, CHAIN_NAMES, DEFAULT_BLOCKS_3_DAYS, LOG_PAGE_SIZE } from '../constants';
6
10
 
7
11
  // Inline as-const ABI items give viem full type inference for args and return types.
8
- const DEPOSIT_REQUESTED_ABI = [
12
+ const DEPOSIT_REQUESTED_V1_ABI = [
9
13
  {
10
14
  type: 'event',
11
15
  name: 'DepositRequested',
@@ -23,7 +27,7 @@ const DEPOSIT_REQUESTED_ABI = [
23
27
  },
24
28
  ] as const;
25
29
 
26
- const REDEEM_REQUESTED_ABI = [
30
+ const REDEEM_REQUESTED_V1_ABI = [
27
31
  {
28
32
  type: 'event',
29
33
  name: 'RedeemRequested',
@@ -41,8 +45,7 @@ const REDEEM_REQUESTED_ABI = [
41
45
  },
42
46
  ] as const;
43
47
 
44
- // ABI for provisioner state queries
45
- const provisionerStateAbi = parseAbi([
48
+ const provisionerV1StateAbi = parseAbi([
46
49
  'function asyncDepositHashes(bytes32 asyncDepositHash) view returns (bool exists)',
47
50
  'function asyncRedeemHashes(bytes32 asyncRedeemHash) view returns (bool exists)',
48
51
  ]);
@@ -66,24 +69,22 @@ export interface UserCurrentBalance {
66
69
  pendingWithdraw: bigint;
67
70
  }
68
71
 
69
- const CHAIN_NAMES: Record<number, string> = {
70
- 1: 'ethereum',
71
- 8453: 'base',
72
- 42161: 'arbitrum',
73
- 10: 'optimism',
74
- };
72
+ // V2 legacy wrapper calls can emit both receiver-aware and legacy events for one request.
73
+ // Keep one row per request hash before state checks and summing pending amounts.
74
+ function dedupeByRequestHash<TLog extends { args: { [key: string]: unknown } }>(
75
+ logs: TLog[],
76
+ hashKey: 'depositRequestHash' | 'redeemRequestHash'
77
+ ): TLog[] {
78
+ const seen = new Set<unknown>();
75
79
 
76
- // Approximate 3-day block lookback per chain based on avg block time
77
- const BLOCKS_3_DAYS: Record<number, bigint> = {
78
- 1: 21_600n,
79
- 8453: 129_600n,
80
- 42161: 1_036_800n,
81
- 10: 129_600n,
82
- };
83
- const DEFAULT_BLOCKS_3_DAYS = 129_600n;
84
-
85
- // Many RPC providers cap eth_getLogs to 10,000 blocks per request.
86
- const LOG_PAGE_SIZE = 9_999n;
80
+ return logs.filter((log) => {
81
+ const hash = log.args[hashKey];
82
+ // Drop malformed events and duplicates so one pending request cannot be counted twice.
83
+ if (hash == null || seen.has(hash)) return false;
84
+ seen.add(hash);
85
+ return true;
86
+ });
87
+ }
87
88
 
88
89
  async function queryDeploymentBalance(
89
90
  client: GauntletClient,
@@ -92,20 +93,23 @@ async function queryDeploymentBalance(
92
93
  ): Promise<UserCurrentBalance> {
93
94
  const publicClient = client.getPublicClient(deployment.chainId);
94
95
  const token = deployment.supplyToken[0];
95
- const provisionerAddress = deployment.provisionerAddress!;
96
96
 
97
97
  const vaultContract = getMultiDepositorVault(publicClient, deployment.vaultAddress);
98
- const [units, feeCalculatorAddress] = await Promise.all([
98
+ const [runtime, units] = await Promise.all([
99
+ resolveAeraRuntimeContracts(publicClient, deployment),
99
100
  vaultContract.read.balanceOf([address]),
100
- vaultContract.read.feeCalculator(),
101
101
  ]);
102
+ const provisionerAddress = runtime.provisioner.address;
103
+ const isV2 = runtime.provisioner.version === ContractVersion.V2;
102
104
 
103
- const feeCalc = getPriceAndFeeCalculator(publicClient, feeCalculatorAddress);
104
- const balance = await feeCalc.read.convertUnitsToToken([
105
+ const balance = await convertUnitsToToken(
106
+ publicClient,
107
+ runtime.feeCalculator.address,
108
+ runtime.feeCalculator.version,
105
109
  deployment.vaultAddress,
106
110
  token.address,
107
- units,
108
- ]);
111
+ units
112
+ );
109
113
 
110
114
  // Look back ~3 days for pending async requests
111
115
  const currentBlock = await publicClient.getBlockNumber();
@@ -122,46 +126,64 @@ async function queryDeploymentBalance(
122
126
  const [depositPages, redeemPages] = await Promise.all([
123
127
  Promise.all(
124
128
  chunks.map(([from, to]) =>
125
- publicClient.getContractEvents({
126
- address: provisionerAddress,
127
- abi: DEPOSIT_REQUESTED_ABI,
128
- eventName: 'DepositRequested',
129
- args: { user: address },
130
- fromBlock: from,
131
- toBlock: to,
132
- })
129
+ isV2
130
+ ? publicClient.getContractEvents({
131
+ address: provisionerAddress,
132
+ abi: provisionerV2Abi,
133
+ eventName: 'DepositRequested',
134
+ args: { receiver: address },
135
+ fromBlock: from,
136
+ toBlock: to,
137
+ })
138
+ : publicClient.getContractEvents({
139
+ address: provisionerAddress,
140
+ abi: DEPOSIT_REQUESTED_V1_ABI,
141
+ eventName: 'DepositRequested',
142
+ args: { user: address },
143
+ fromBlock: from,
144
+ toBlock: to,
145
+ })
133
146
  )
134
147
  ),
135
148
  Promise.all(
136
149
  chunks.map(([from, to]) =>
137
- publicClient.getContractEvents({
138
- address: provisionerAddress,
139
- abi: REDEEM_REQUESTED_ABI,
140
- eventName: 'RedeemRequested',
141
- args: { user: address },
142
- fromBlock: from,
143
- toBlock: to,
144
- })
150
+ isV2
151
+ ? publicClient.getContractEvents({
152
+ address: provisionerAddress,
153
+ abi: provisionerV2Abi,
154
+ eventName: 'RedeemRequested',
155
+ args: { user: address },
156
+ fromBlock: from,
157
+ toBlock: to,
158
+ })
159
+ : publicClient.getContractEvents({
160
+ address: provisionerAddress,
161
+ abi: REDEEM_REQUESTED_V1_ABI,
162
+ eventName: 'RedeemRequested',
163
+ args: { user: address },
164
+ fromBlock: from,
165
+ toBlock: to,
166
+ })
145
167
  )
146
168
  ),
147
169
  ]);
148
170
 
149
- const depositLogs = depositPages.flat().filter((log) => log.args.depositRequestHash != null);
150
- const redeemLogs = redeemPages.flat().filter((log) => log.args.redeemRequestHash != null);
171
+ const depositLogs = dedupeByRequestHash(depositPages.flat(), 'depositRequestHash');
172
+ const redeemLogs = dedupeByRequestHash(redeemPages.flat(), 'redeemRequestHash');
151
173
 
152
174
  // Check which requests are still pending on-chain via a single multicall
153
175
  const pendingChecks = await publicClient.multicall({
154
176
  contracts: [
155
177
  ...depositLogs.map((log) => ({
156
178
  address: provisionerAddress,
157
- abi: provisionerStateAbi,
158
- functionName: 'asyncDepositHashes' as const,
179
+ abi: isV2 ? provisionerV2Abi : provisionerV1StateAbi,
180
+ functionName: isV2 ? ('asyncRequestHashes' as const) : ('asyncDepositHashes' as const),
159
181
  args: [log.args.depositRequestHash!] as const,
160
182
  })),
161
183
  ...redeemLogs.map((log) => ({
162
184
  address: provisionerAddress,
163
- abi: provisionerStateAbi,
164
- functionName: 'asyncRedeemHashes' as const,
185
+ abi: isV2 ? provisionerV2Abi : provisionerV1StateAbi,
186
+ functionName: isV2 ? ('asyncRequestHashes' as const) : ('asyncRedeemHashes' as const),
165
187
  args: [log.args.redeemRequestHash!] as const,
166
188
  })),
167
189
  ],
@@ -187,11 +209,14 @@ async function queryDeploymentBalance(
187
209
 
188
210
  const pendingWithdraw =
189
211
  pendingWithdrawUnits > 0n
190
- ? await feeCalc.read.convertUnitsToToken([
212
+ ? await convertUnitsToToken(
213
+ publicClient,
214
+ runtime.feeCalculator.address,
215
+ runtime.feeCalculator.version,
191
216
  deployment.vaultAddress,
192
217
  token.address,
193
- pendingWithdrawUnits,
194
- ])
218
+ pendingWithdrawUnits
219
+ )
195
220
  : 0n;
196
221
 
197
222
  return {
@@ -243,7 +268,7 @@ export async function getUserCurrentBalance(
243
268
  if (!vaultInfo) throw new VaultNotFoundError(params.vaultId);
244
269
 
245
270
  const evmDeployments = vaultInfo.deployments.filter(
246
- (d): d is EvmVaultDeployment => d.chain === 'evm' && d.provisionerAddress !== undefined
271
+ (d): d is EvmVaultDeployment => d.chain === 'evm' && d.vaultType === 'multi-depositor'
247
272
  );
248
273
 
249
274
  if (vaultInfo.protocol !== 'aera' || evmDeployments.length === 0) {
@@ -7,10 +7,21 @@ import {
7
7
  AccountRequiredError,
8
8
  VaultNotFoundError,
9
9
  UnsupportedAssetError,
10
- UnsupportedDepositModeError,
11
10
  InvalidSlippageBPSError,
12
11
  } from '../errors';
12
+ import {
13
+ resolveAeraRuntimeContracts,
14
+ resolveAeraTokenModeSupport,
15
+ type AeraRuntimeContracts,
16
+ } from './aeraContracts';
13
17
  import { resolveVault } from './vaults';
18
+ import { DEFAULT_BPS, MAX_BPS } from '../constants';
19
+ import {
20
+ parseOperationMode,
21
+ resolveOperationMode,
22
+ resolveSyncOnlyOperationMode,
23
+ type OperationMode,
24
+ } from './operationMode';
14
25
 
15
26
  export type EvmWithdrawParams = {
16
27
  vaultId: string;
@@ -18,12 +29,16 @@ export type EvmWithdrawParams = {
18
29
  chainId?: number;
19
30
  // Required for multiasset vaults, not utilized yet
20
31
  assetSymbol?: string;
21
- /** Request async (queued) withdraw. Only valid for vaults with depositMode 'async' or 'both'. */
32
+ /** Request async (queued) or sync withdraw. Availability is read from live vault configuration. */
22
33
  depositMode?: string;
23
34
  // So a developer is able to specify a separate receiver than the tx sender
24
35
  receiver?: Address;
25
36
  /** Slippage tolerance in basis points (e.g. 100 = 1%). Defaults to 100. */
26
37
  slippageBps?: number;
38
+ /** Solver tip passed to async Aera provisioner requests. Defaults to 0. */
39
+ solverTip?: bigint;
40
+ /** Maximum price age passed to async Aera provisioner requests. Defaults to 10 days. */
41
+ maxPriceAge?: bigint;
27
42
  } & (
28
43
  | { shares: bigint; amount?: never; entireAmount?: never }
29
44
  | { amount: bigint; shares?: never; entireAmount?: never }
@@ -70,7 +85,9 @@ export async function getWithdrawTx(
70
85
  ): Promise<PreparedTx[]> {
71
86
  if (
72
87
  params.slippageBps !== undefined &&
73
- (!Number.isInteger(params.slippageBps) || params.slippageBps < 0 || params.slippageBps > 10000)
88
+ (!Number.isInteger(params.slippageBps) ||
89
+ params.slippageBps < 0 ||
90
+ params.slippageBps > Number(MAX_BPS))
74
91
  ) {
75
92
  throw new InvalidSlippageBPSError(params.slippageBps);
76
93
  }
@@ -85,17 +102,6 @@ export async function getWithdrawTx(
85
102
 
86
103
  const { vault, protocol } = resolved;
87
104
 
88
- if (params.depositMode === 'sync' && vault.depositMode === 'async') {
89
- throw new UnsupportedDepositModeError(params.vaultId, 'sync', vault.depositMode);
90
- }
91
- if (params.depositMode === 'async' && vault.depositMode === 'sync') {
92
- throw new UnsupportedDepositModeError(params.vaultId, 'async', vault.depositMode);
93
- }
94
- let modifiedDepositMode = params.depositMode;
95
- if (params.depositMode === undefined) {
96
- modifiedDepositMode = vault.depositMode === 'both' ? 'async' : vault.depositMode;
97
- }
98
-
99
105
  const token =
100
106
  vault.supplyToken.length > 1
101
107
  ? vault.supplyToken.find((tInfo) => tInfo.symbol === params.assetSymbol)
@@ -107,6 +113,24 @@ export async function getWithdrawTx(
107
113
 
108
114
  const adapter = getAdapter(protocol);
109
115
  const publicClient = client.getPublicClient(chainId);
116
+ const requestedWithdrawMode = parseOperationMode(params.vaultId, params.depositMode);
117
+ let modifiedDepositMode: OperationMode;
118
+ let aeraRuntime: AeraRuntimeContracts | undefined;
119
+
120
+ if (protocol === 'aera') {
121
+ aeraRuntime = await resolveAeraRuntimeContracts(publicClient, vault);
122
+ const tokenModeSupport = await resolveAeraTokenModeSupport(
123
+ publicClient,
124
+ aeraRuntime,
125
+ token.address
126
+ );
127
+ modifiedDepositMode = resolveOperationMode(params.vaultId, requestedWithdrawMode, {
128
+ async: tokenModeSupport.asyncRedeem,
129
+ sync: tokenModeSupport.syncRedeem,
130
+ });
131
+ } else {
132
+ modifiedDepositMode = resolveSyncOnlyOperationMode(params.vaultId, requestedWithdrawMode);
133
+ }
110
134
 
111
135
  const withdrawParams = {
112
136
  vault,
@@ -115,7 +139,10 @@ export async function getWithdrawTx(
115
139
  async: modifiedDepositMode === 'async',
116
140
  asset: token,
117
141
  publicClient,
118
- slippageBps: params.slippageBps,
142
+ slippageBps: params.slippageBps ?? DEFAULT_BPS,
143
+ solverTip: params.solverTip,
144
+ maxPriceAge: params.maxPriceAge,
145
+ aeraRuntime,
119
146
  ...('shares' in params && params.shares != null ? { shares: params.shares } : {}),
120
147
  ...('amount' in params && params.amount != null ? { amount: params.amount } : {}),
121
148
  ...('entireAmount' in params && params.entireAmount ? { entireAmount: true as const } : {}),
package/src/index.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  export { GauntletClient, AttributionMode } from './client';
2
2
  export type { GauntletClientConfig } from './client';
3
+ export { ContractVersion } from './evm/types';
4
+
5
+ export * from './api';
3
6
 
4
7
  export type {
5
8
  VaultInfo,
@@ -11,15 +14,30 @@ export type {
11
14
  export type { VaultFilter } from './evm/vaults';
12
15
 
13
16
  export { getDepositTx } from './evm/deposit';
17
+ export { getDepositReceiverApprovalTx } from './evm/depositReceiverApproval';
14
18
  export { getWithdrawTx } from './evm/withdraw';
15
19
  export { getUserCurrentBalance } from './evm/userCurrentBalance';
16
20
  export { getVaults, VaultId } from './evm/vaults';
21
+ export { resolveAeraRuntimeContracts, resolveContractVersion } from './evm/aeraContracts';
22
+ export {
23
+ Rounding,
24
+ convertTokenToUnits,
25
+ convertTokenToUnitsIfActive,
26
+ convertUnitsToToken,
27
+ convertUnitsToTokenIfActive,
28
+ getAnchorTimestamp,
29
+ getVaultState,
30
+ isVaultPaused,
31
+ } from './evm/aeraContracts/priceAndFeeCalculator';
17
32
 
18
33
  export type { EvmDepositParams } from './evm/deposit';
34
+ export type { EvmDepositReceiverApprovalParams } from './evm/depositReceiverApproval';
19
35
  export type { EvmWithdrawParams } from './evm/withdraw';
20
36
  export type { EvmTxStep } from './evm/adapters/types';
21
37
  export type { UserCurrentBalanceParams, UserCurrentBalance } from './evm/userCurrentBalance';
22
38
  export type { PreparedTx } from './attribution';
39
+ export type { AeraRuntimeContracts, AeraTokenModeSupport } from './evm/aeraContracts';
40
+ export type { NormalizedVaultPriceState } from './evm/aeraContracts/priceAndFeeCalculator';
23
41
 
24
42
  export {
25
43
  GauntletSDKError,
@@ -30,7 +48,10 @@ export {
30
48
  RpcNotConfiguredError,
31
49
  AccountRequiredError,
32
50
  UnsupportedProtocolError,
51
+ UnsupportedFeatureError,
33
52
  InvalidWithdrawParamsError,
34
53
  UnimplementedFeatureError,
35
54
  UnitConversionError,
55
+ StalePriceError,
56
+ InvalidSolverTipError,
36
57
  } from './errors';