@gauntlet-xyz/sdk 1.0.5 → 2.0.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/dist/constants.d.ts +16 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +28 -0
- package/dist/constants.js.map +1 -0
- package/dist/errors.d.ts +18 -3
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +20 -2
- package/dist/errors.js.map +1 -1
- package/dist/evm/abis/priceAndFeeCalculatorV2.d.ts +233 -0
- package/dist/evm/abis/priceAndFeeCalculatorV2.d.ts.map +1 -0
- package/dist/evm/abis/priceAndFeeCalculatorV2.js +119 -0
- package/dist/evm/abis/priceAndFeeCalculatorV2.js.map +1 -0
- package/dist/evm/abis/provisionerV2.d.ts +460 -0
- package/dist/evm/abis/provisionerV2.d.ts.map +1 -0
- package/dist/evm/abis/provisionerV2.js +203 -0
- package/dist/evm/abis/provisionerV2.js.map +1 -0
- package/dist/evm/adapters/aera.d.ts +3 -3
- package/dist/evm/adapters/aera.d.ts.map +1 -1
- package/dist/evm/adapters/aera.js +179 -103
- package/dist/evm/adapters/aera.js.map +1 -1
- package/dist/evm/adapters/types.d.ts +12 -1
- package/dist/evm/adapters/types.d.ts.map +1 -1
- package/dist/evm/aeraContracts/priceAndFeeCalculator.d.ts +41 -0
- package/dist/evm/aeraContracts/priceAndFeeCalculator.d.ts.map +1 -0
- package/dist/evm/aeraContracts/priceAndFeeCalculator.js +140 -0
- package/dist/evm/aeraContracts/priceAndFeeCalculator.js.map +1 -0
- package/dist/evm/aeraContracts/v1.d.ts +2317 -0
- package/dist/evm/aeraContracts/v1.d.ts.map +1 -0
- package/dist/evm/aeraContracts/v1.js +59 -0
- package/dist/evm/aeraContracts/v1.js.map +1 -0
- package/dist/evm/aeraContracts/v2.d.ts +2802 -0
- package/dist/evm/aeraContracts/v2.d.ts.map +1 -0
- package/dist/evm/aeraContracts/v2.js +217 -0
- package/dist/evm/aeraContracts/v2.js.map +1 -0
- package/dist/evm/aeraContracts.d.ts +23 -2316
- package/dist/evm/aeraContracts.d.ts.map +1 -1
- package/dist/evm/aeraContracts.js +117 -24
- package/dist/evm/aeraContracts.js.map +1 -1
- package/dist/evm/deposit.d.ts +5 -1
- package/dist/evm/deposit.d.ts.map +1 -1
- package/dist/evm/deposit.js +28 -14
- package/dist/evm/deposit.js.map +1 -1
- package/dist/evm/depositReceiverApproval.d.ts +17 -0
- package/dist/evm/depositReceiverApproval.d.ts.map +1 -0
- package/dist/evm/depositReceiverApproval.js +32 -0
- package/dist/evm/depositReceiverApproval.js.map +1 -0
- package/dist/evm/index.d.ts +8 -1
- package/dist/evm/index.d.ts.map +1 -1
- package/dist/evm/index.js +4 -0
- package/dist/evm/index.js.map +1 -1
- package/dist/evm/operationMode.d.ts +9 -0
- package/dist/evm/operationMode.d.ts.map +1 -0
- package/dist/evm/operationMode.js +41 -0
- package/dist/evm/operationMode.js.map +1 -0
- package/dist/evm/types.d.ts +7 -3
- package/dist/evm/types.d.ts.map +1 -1
- package/dist/evm/types.js +4 -1
- package/dist/evm/types.js.map +1 -1
- package/dist/evm/userCurrentBalance.d.ts.map +1 -1
- package/dist/evm/userCurrentBalance.js +68 -58
- package/dist/evm/userCurrentBalance.js.map +1 -1
- package/dist/evm/withdraw.d.ts +5 -1
- package/dist/evm/withdraw.d.ts.map +1 -1
- package/dist/evm/withdraw.js +25 -13
- package/dist/evm/withdraw.js.map +1 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/manifest/vaults.json +31 -139
- package/package.json +1 -1
- package/src/constants.ts +32 -0
- package/src/errors.ts +44 -5
- package/src/evm/abis/priceAndFeeCalculatorV2.ts +118 -0
- package/src/evm/abis/provisionerV2.ts +202 -0
- package/src/evm/adapters/aera.ts +419 -137
- package/src/evm/adapters/types.ts +19 -1
- package/src/evm/aeraContracts/priceAndFeeCalculator.ts +223 -0
- package/src/evm/aeraContracts/v1.ts +157 -0
- package/src/evm/aeraContracts/v2.ts +466 -0
- package/src/evm/aeraContracts.ts +173 -52
- package/src/evm/deposit.ts +48 -16
- package/src/evm/depositReceiverApproval.ts +51 -0
- package/src/evm/index.ts +23 -1
- package/src/evm/operationMode.ts +60 -0
- package/src/evm/types.ts +9 -3
- package/src/evm/userCurrentBalance.ts +80 -55
- package/src/evm/withdraw.ts +42 -15
- package/src/index.ts +19 -0
package/src/evm/deposit.ts
CHANGED
|
@@ -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.
|
|
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) ||
|
|
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 {
|
|
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,15 +6,21 @@ 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[];
|
|
22
|
+
/** Number of days before an async request deadline expires. Defaults to 3. */
|
|
23
|
+
expirationDays?: number;
|
|
18
24
|
}
|
|
19
25
|
|
|
20
26
|
/** Union of all supported chain deployment types. Narrow on `chain` to access chain-specific fields. */
|
|
@@ -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,
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
};
|
|
83
|
-
|
|
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 [
|
|
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
|
|
104
|
-
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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()
|
|
150
|
-
const redeemLogs = redeemPages.flat()
|
|
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:
|
|
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:
|
|
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
|
|
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.
|
|
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) {
|
package/src/evm/withdraw.ts
CHANGED
|
@@ -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.
|
|
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) ||
|
|
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,6 @@
|
|
|
1
1
|
export { GauntletClient, AttributionMode } from './client';
|
|
2
2
|
export type { GauntletClientConfig } from './client';
|
|
3
|
+
export { ContractVersion } from './evm/types';
|
|
3
4
|
|
|
4
5
|
export type {
|
|
5
6
|
VaultInfo,
|
|
@@ -11,15 +12,30 @@ export type {
|
|
|
11
12
|
export type { VaultFilter } from './evm/vaults';
|
|
12
13
|
|
|
13
14
|
export { getDepositTx } from './evm/deposit';
|
|
15
|
+
export { getDepositReceiverApprovalTx } from './evm/depositReceiverApproval';
|
|
14
16
|
export { getWithdrawTx } from './evm/withdraw';
|
|
15
17
|
export { getUserCurrentBalance } from './evm/userCurrentBalance';
|
|
16
18
|
export { getVaults, VaultId } from './evm/vaults';
|
|
19
|
+
export { resolveAeraRuntimeContracts, resolveContractVersion } from './evm/aeraContracts';
|
|
20
|
+
export {
|
|
21
|
+
Rounding,
|
|
22
|
+
convertTokenToUnits,
|
|
23
|
+
convertTokenToUnitsIfActive,
|
|
24
|
+
convertUnitsToToken,
|
|
25
|
+
convertUnitsToTokenIfActive,
|
|
26
|
+
getAnchorTimestamp,
|
|
27
|
+
getVaultState,
|
|
28
|
+
isVaultPaused,
|
|
29
|
+
} from './evm/aeraContracts/priceAndFeeCalculator';
|
|
17
30
|
|
|
18
31
|
export type { EvmDepositParams } from './evm/deposit';
|
|
32
|
+
export type { EvmDepositReceiverApprovalParams } from './evm/depositReceiverApproval';
|
|
19
33
|
export type { EvmWithdrawParams } from './evm/withdraw';
|
|
20
34
|
export type { EvmTxStep } from './evm/adapters/types';
|
|
21
35
|
export type { UserCurrentBalanceParams, UserCurrentBalance } from './evm/userCurrentBalance';
|
|
22
36
|
export type { PreparedTx } from './attribution';
|
|
37
|
+
export type { AeraRuntimeContracts, AeraTokenModeSupport } from './evm/aeraContracts';
|
|
38
|
+
export type { NormalizedVaultPriceState } from './evm/aeraContracts/priceAndFeeCalculator';
|
|
23
39
|
|
|
24
40
|
export {
|
|
25
41
|
GauntletSDKError,
|
|
@@ -30,7 +46,10 @@ export {
|
|
|
30
46
|
RpcNotConfiguredError,
|
|
31
47
|
AccountRequiredError,
|
|
32
48
|
UnsupportedProtocolError,
|
|
49
|
+
UnsupportedFeatureError,
|
|
33
50
|
InvalidWithdrawParamsError,
|
|
34
51
|
UnimplementedFeatureError,
|
|
35
52
|
UnitConversionError,
|
|
53
|
+
StalePriceError,
|
|
54
|
+
InvalidSolverTipError,
|
|
36
55
|
} from './errors';
|