@gearbox-protocol/sdk 14.11.3 → 14.12.0-next.1
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/cjs/common-utils/utils/index.js +2 -0
- package/dist/cjs/{history/trace-utils.js → common-utils/utils/trace.js} +62 -5
- package/dist/cjs/dev/index.js +3 -1
- package/dist/cjs/dev/verifyTestnet.js +52 -0
- package/dist/cjs/history/assembleOperations.js +6 -6
- package/dist/cjs/history/classifyMulticallOperations.js +31 -25
- package/dist/cjs/history/index.js +0 -2
- package/dist/cjs/history/parseCreditAccountTransaction.js +5 -6
- package/dist/cjs/history/toLegacyOperation.js +5 -1
- package/dist/cjs/plugins/adapters/contracts/AbstractAdapter.js +25 -37
- package/dist/cjs/plugins/adapters/contracts/AccountMigratorAdapterContract.js +9 -0
- package/dist/cjs/{history/internal-types.js → plugins/adapters/transfers.js} +2 -2
- package/dist/cjs/preview/index.js +28 -0
- package/dist/cjs/preview/package.json +1 -0
- package/dist/cjs/preview/parse/classifyInnerOperations.js +92 -0
- package/dist/cjs/preview/parse/errors.js +47 -0
- package/dist/cjs/preview/parse/extractExpectedBalanceChanges.js +48 -0
- package/dist/cjs/preview/parse/index.js +34 -0
- package/dist/cjs/preview/parse/parseFacadeOperationCalldata.js +115 -0
- package/dist/cjs/preview/parse/parseOperationCalldata.js +46 -0
- package/dist/cjs/preview/parse/parsePoolOperationCalldata.js +77 -0
- package/dist/cjs/{history/inner-operations.js → preview/parse/types-adapters.js} +2 -2
- package/dist/cjs/preview/parse/types-facades.js +16 -0
- package/dist/cjs/preview/parse/types-pools.js +16 -0
- package/dist/cjs/preview/parse/types.js +37 -0
- package/dist/cjs/preview/prerequisites/AllowancePrerequisite.js +78 -0
- package/dist/cjs/preview/prerequisites/BalancePrerequisite.js +77 -0
- package/dist/cjs/preview/prerequisites/Prerequisite.js +85 -0
- package/dist/cjs/preview/prerequisites/buildPrerequisites.js +187 -0
- package/dist/cjs/preview/prerequisites/index.js +34 -0
- package/dist/cjs/preview/prerequisites/prepareAction.js +48 -0
- package/dist/cjs/preview/prerequisites/runPrerequisites.js +71 -0
- package/dist/cjs/preview/prerequisites/types.js +16 -0
- package/dist/cjs/preview/simulate/constants.js +28 -0
- package/dist/cjs/preview/simulate/errors.js +98 -0
- package/dist/cjs/preview/simulate/extractERC20Transfers.js +47 -0
- package/dist/cjs/preview/simulate/holders.js +45 -0
- package/dist/cjs/preview/simulate/index.js +40 -0
- package/dist/cjs/preview/simulate/simulateFacadeOperation.js +30 -0
- package/dist/cjs/preview/simulate/simulateOperation.js +37 -0
- package/dist/cjs/preview/simulate/simulatePoolOpMulticall.js +155 -0
- package/dist/cjs/preview/simulate/simulatePoolOpV1.js +106 -0
- package/dist/cjs/preview/simulate/simulatePoolOperation.js +62 -0
- package/dist/cjs/preview/simulate/types.js +16 -0
- package/dist/cjs/{history → preview/trace}/errors.js +0 -26
- package/dist/cjs/preview/trace/extractAdapterCallTraces.js +58 -0
- package/dist/cjs/{history → preview/trace}/extractTransfers.js +9 -12
- package/dist/cjs/{history → preview/trace}/findFacadeCalls.js +3 -3
- package/dist/cjs/preview/trace/index.js +30 -0
- package/dist/cjs/preview/trace/types.js +16 -0
- package/dist/cjs/sdk/accounts/CreditAccountsServiceV310.js +154 -0
- package/dist/esm/common-utils/utils/index.js +1 -0
- package/dist/esm/common-utils/utils/trace.js +93 -0
- package/dist/esm/dev/index.js +1 -0
- package/dist/esm/dev/verifyTestnet.js +27 -0
- package/dist/esm/history/assembleOperations.js +8 -6
- package/dist/esm/history/classifyMulticallOperations.js +29 -23
- package/dist/esm/history/index.js +0 -1
- package/dist/esm/history/parseCreditAccountTransaction.js +3 -4
- package/dist/esm/history/toLegacyOperation.js +5 -1
- package/dist/esm/plugins/adapters/contracts/AbstractAdapter.js +28 -38
- package/dist/esm/plugins/adapters/contracts/AccountMigratorAdapterContract.js +9 -0
- package/dist/esm/preview/index.js +4 -0
- package/dist/esm/preview/package.json +1 -0
- package/dist/esm/preview/parse/classifyInnerOperations.js +68 -0
- package/dist/esm/preview/parse/errors.js +22 -0
- package/dist/esm/preview/parse/extractExpectedBalanceChanges.js +24 -0
- package/dist/esm/preview/parse/index.js +7 -0
- package/dist/esm/preview/parse/parseFacadeOperationCalldata.js +91 -0
- package/dist/esm/preview/parse/parseOperationCalldata.js +25 -0
- package/dist/esm/preview/parse/parsePoolOperationCalldata.js +53 -0
- package/dist/esm/preview/parse/types-facades.js +0 -0
- package/dist/esm/preview/parse/types-pools.js +0 -0
- package/dist/esm/preview/parse/types.js +9 -0
- package/dist/esm/preview/prerequisites/AllowancePrerequisite.js +57 -0
- package/dist/esm/preview/prerequisites/BalancePrerequisite.js +56 -0
- package/dist/esm/preview/prerequisites/Prerequisite.js +63 -0
- package/dist/esm/preview/prerequisites/buildPrerequisites.js +163 -0
- package/dist/esm/preview/prerequisites/index.js +7 -0
- package/dist/esm/preview/prerequisites/prepareAction.js +23 -0
- package/dist/esm/preview/prerequisites/runPrerequisites.js +47 -0
- package/dist/esm/preview/prerequisites/types.js +0 -0
- package/dist/esm/preview/simulate/constants.js +4 -0
- package/dist/esm/preview/simulate/errors.js +75 -0
- package/dist/esm/preview/simulate/extractERC20Transfers.js +23 -0
- package/dist/esm/preview/simulate/holders.js +21 -0
- package/dist/esm/preview/simulate/index.js +12 -0
- package/dist/esm/preview/simulate/simulateFacadeOperation.js +6 -0
- package/dist/esm/preview/simulate/simulateOperation.js +13 -0
- package/dist/esm/preview/simulate/simulatePoolOpMulticall.js +130 -0
- package/dist/esm/preview/simulate/simulatePoolOpV1.js +82 -0
- package/dist/esm/preview/simulate/simulatePoolOperation.js +41 -0
- package/dist/esm/preview/simulate/types.js +0 -0
- package/dist/esm/{history → preview/trace}/errors.js +0 -24
- package/dist/esm/preview/trace/extractAdapterCallTraces.js +40 -0
- package/dist/esm/{history → preview/trace}/extractTransfers.js +9 -12
- package/dist/esm/{history → preview/trace}/findFacadeCalls.js +4 -2
- package/dist/esm/preview/trace/index.js +5 -0
- package/dist/esm/preview/trace/types.js +0 -0
- package/dist/esm/sdk/accounts/CreditAccountsServiceV310.js +154 -0
- package/dist/types/common-utils/utils/index.d.ts +1 -0
- package/dist/types/common-utils/utils/trace.d.ts +73 -0
- package/dist/types/dev/index.d.ts +1 -0
- package/dist/types/dev/verifyTestnet.d.ts +24 -0
- package/dist/types/history/assembleOperations.d.ts +11 -6
- package/dist/types/history/classifyMulticallOperations.d.ts +21 -9
- package/dist/types/history/index.d.ts +0 -1
- package/dist/types/history/mapOperations.d.ts +7 -9
- package/dist/types/history/types.d.ts +22 -71
- package/dist/types/plugins/adapters/contracts/AbstractAdapter.d.ts +16 -17
- package/dist/types/plugins/adapters/contracts/AccountMigratorAdapterContract.d.ts +10 -1
- package/dist/types/plugins/adapters/contracts/ConvexV1BaseRewardPoolAdapterContract.d.ts +1 -1
- package/dist/types/plugins/adapters/contracts/ConvexV1BoosterAdapterContract.d.ts +1 -1
- package/dist/types/plugins/adapters/contracts/Curve2AssetsAdapterContract.d.ts +1 -1
- package/dist/types/plugins/adapters/contracts/Curve3AssetsAdapterContract.d.ts +1 -1
- package/dist/types/plugins/adapters/contracts/Curve4AssetsAdapterContract.d.ts +1 -1
- package/dist/types/plugins/adapters/contracts/CurveV1AdapterDeposit.d.ts +1 -1
- package/dist/types/plugins/adapters/contracts/CurveV1AdapterStETHContract.d.ts +1 -1
- package/dist/types/plugins/adapters/contracts/CurveV1StableNGAdapterContract.d.ts +1 -1
- package/dist/types/plugins/adapters/contracts/DaiUsdsAdapterContract.d.ts +1 -1
- package/dist/types/plugins/adapters/contracts/ERC4626AdapterContract.d.ts +1 -1
- package/dist/types/plugins/adapters/contracts/ERC4626ReferralAdapterContract.d.ts +1 -1
- package/dist/types/plugins/adapters/contracts/LidoV1AdapterContract.d.ts +1 -1
- package/dist/types/plugins/adapters/contracts/MellowDVVAdapterContract.d.ts +1 -1
- package/dist/types/plugins/adapters/contracts/MellowERC4626VaultAdapterContract.d.ts +1 -1
- package/dist/types/plugins/adapters/contracts/UniswapV2AdapterContract.d.ts +1 -1
- package/dist/types/plugins/adapters/contracts/UniswapV4AdapterContract.d.ts +1 -1
- package/dist/types/plugins/adapters/contracts/WstETHV1AdapterContract.d.ts +1 -1
- package/dist/types/plugins/adapters/transferHelpers.d.ts +1 -1
- package/dist/types/plugins/adapters/transfers.d.ts +17 -0
- package/dist/types/plugins/adapters/types.d.ts +8 -46
- package/dist/types/preview/index.d.ts +4 -0
- package/dist/types/preview/parse/classifyInnerOperations.d.ts +21 -0
- package/dist/types/preview/parse/errors.d.ts +17 -0
- package/dist/types/preview/parse/extractExpectedBalanceChanges.d.ts +22 -0
- package/dist/types/preview/parse/index.d.ts +7 -0
- package/dist/types/preview/parse/parseFacadeOperationCalldata.d.ts +19 -0
- package/dist/types/preview/parse/parseOperationCalldata.d.ts +23 -0
- package/dist/types/preview/parse/parsePoolOperationCalldata.d.ts +17 -0
- package/dist/types/preview/parse/types-adapters.d.ts +69 -0
- package/dist/types/preview/parse/types-facades.d.ts +158 -0
- package/dist/types/preview/parse/types-pools.d.ts +55 -0
- package/dist/types/preview/parse/types.d.ts +44 -0
- package/dist/types/preview/prerequisites/AllowancePrerequisite.d.ts +24 -0
- package/dist/types/preview/prerequisites/BalancePrerequisite.d.ts +23 -0
- package/dist/types/preview/prerequisites/Prerequisite.d.ts +60 -0
- package/dist/types/preview/prerequisites/buildPrerequisites.d.ts +16 -0
- package/dist/types/preview/prerequisites/index.d.ts +7 -0
- package/dist/types/preview/prerequisites/prepareAction.d.ts +52 -0
- package/dist/types/preview/prerequisites/runPrerequisites.d.ts +12 -0
- package/dist/types/preview/prerequisites/types.d.ts +78 -0
- package/dist/types/preview/simulate/constants.d.ts +6 -0
- package/dist/types/preview/simulate/errors.d.ts +51 -0
- package/dist/types/preview/simulate/extractERC20Transfers.d.ts +11 -0
- package/dist/types/preview/simulate/holders.d.ts +7 -0
- package/dist/types/preview/simulate/index.d.ts +9 -0
- package/dist/types/preview/simulate/simulateFacadeOperation.d.ts +25 -0
- package/dist/types/preview/simulate/simulateOperation.d.ts +20 -0
- package/dist/types/preview/simulate/simulatePoolOpMulticall.d.ts +28 -0
- package/dist/types/preview/simulate/simulatePoolOpV1.d.ts +14 -0
- package/dist/types/preview/simulate/simulatePoolOperation.d.ts +8 -0
- package/dist/types/preview/simulate/types.d.ts +79 -0
- package/dist/types/{history → preview/trace}/errors.d.ts +0 -10
- package/dist/types/preview/trace/extractAdapterCallTraces.d.ts +23 -0
- package/dist/types/{history → preview/trace}/extractTransfers.d.ts +14 -5
- package/dist/types/{history → preview/trace}/findFacadeCalls.d.ts +3 -2
- package/dist/types/preview/trace/index.d.ts +5 -0
- package/dist/types/preview/trace/types.d.ts +21 -0
- package/dist/types/sdk/accounts/CreditAccountsServiceV310.d.ts +21 -1
- package/dist/types/sdk/accounts/types.d.ts +82 -0
- package/package.json +6 -1
- package/dist/cjs/history/extractProtocolCalls.js +0 -53
- package/dist/esm/history/extractProtocolCalls.js +0 -32
- package/dist/esm/history/trace-utils.js +0 -36
- package/dist/types/history/extractProtocolCalls.d.ts +0 -8
- package/dist/types/history/inner-operations.d.ts +0 -57
- package/dist/types/history/internal-types.d.ts +0 -47
- package/dist/types/history/trace-utils.d.ts +0 -12
- /package/dist/esm/{history/inner-operations.js → plugins/adapters/transfers.js} +0 -0
- /package/dist/esm/{history/internal-types.js → preview/parse/types-adapters.js} +0 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
async function verifyPrerequisites(prereqs, ctx) {
|
|
2
|
+
if (prereqs.length === 0) {
|
|
3
|
+
return [];
|
|
4
|
+
}
|
|
5
|
+
const ranges = [];
|
|
6
|
+
const calls = [];
|
|
7
|
+
for (const prereq of prereqs) {
|
|
8
|
+
const start = calls.length;
|
|
9
|
+
calls.push(...prereq.calls(ctx));
|
|
10
|
+
ranges.push({ start, end: calls.length });
|
|
11
|
+
}
|
|
12
|
+
let results;
|
|
13
|
+
try {
|
|
14
|
+
results = await ctx.sdk.client.multicall({
|
|
15
|
+
allowFailure: true,
|
|
16
|
+
contracts: calls,
|
|
17
|
+
// `undefined` lets viem read at `latest`; `blockNumber` is only set for
|
|
18
|
+
// testnet forks pinned to a specific block.
|
|
19
|
+
blockNumber: ctx.blockNumber
|
|
20
|
+
});
|
|
21
|
+
} catch (cause) {
|
|
22
|
+
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
23
|
+
return prereqs.map(
|
|
24
|
+
(prereq, i) => (
|
|
25
|
+
// Each prereq pairs its own kind with its detail, so the widened
|
|
26
|
+
// `resolve` return is safe to narrow back to the discriminated union.
|
|
27
|
+
prereq.resolve(
|
|
28
|
+
failureSlice(ranges[i].end - ranges[i].start, error)
|
|
29
|
+
)
|
|
30
|
+
)
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
return prereqs.map(
|
|
34
|
+
(prereq, i) => prereq.resolve(
|
|
35
|
+
results.slice(ranges[i].start, ranges[i].end)
|
|
36
|
+
)
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
function failureSlice(n, error) {
|
|
40
|
+
return Array.from({ length: n }, () => ({
|
|
41
|
+
status: "failure",
|
|
42
|
+
error
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
export {
|
|
46
|
+
verifyPrerequisites
|
|
47
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BaseError,
|
|
3
|
+
ContractFunctionRevertedError,
|
|
4
|
+
decodeErrorResult
|
|
5
|
+
} from "viem";
|
|
6
|
+
import { errorAbis } from "../../abi/errors.js";
|
|
7
|
+
class PreviewSimulationError extends BaseError {
|
|
8
|
+
name = "PreviewSimulationError";
|
|
9
|
+
/** Per-flow decoded failures behind this error. */
|
|
10
|
+
failures;
|
|
11
|
+
constructor(failures) {
|
|
12
|
+
const shortMessage = failures.length <= 1 ? failures[0]?.detail.reason ?? "simulation failed" : "all simulation flows failed";
|
|
13
|
+
const cause = failures.find((f) => f.detail.cause instanceof Error)?.detail.cause;
|
|
14
|
+
super(shortMessage, {
|
|
15
|
+
metaMessages: failures.map((f) => `[${f.source}] ${f.detail.reason}`),
|
|
16
|
+
cause
|
|
17
|
+
});
|
|
18
|
+
this.failures = failures;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function asPreviewSimulationError(reason, source) {
|
|
22
|
+
if (reason instanceof PreviewSimulationError) {
|
|
23
|
+
return reason;
|
|
24
|
+
}
|
|
25
|
+
const error = reason instanceof Error ? reason : new Error(String(reason));
|
|
26
|
+
return new PreviewSimulationError([
|
|
27
|
+
{ source, detail: decodeSimulationError({ error }) }
|
|
28
|
+
]);
|
|
29
|
+
}
|
|
30
|
+
function combinePreviewSimulationErrors(...errors) {
|
|
31
|
+
return new PreviewSimulationError(
|
|
32
|
+
errors.filter((e) => e).flatMap((e) => e?.failures ?? [])
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
function decodeSimulationError(revert) {
|
|
36
|
+
const { error, data } = revert;
|
|
37
|
+
if (data && data !== "0x") {
|
|
38
|
+
try {
|
|
39
|
+
const decoded = decodeErrorResult({ abi: errorAbis, data });
|
|
40
|
+
return {
|
|
41
|
+
reason: formatDecodedError(decoded.errorName, decoded.args),
|
|
42
|
+
cause: error
|
|
43
|
+
};
|
|
44
|
+
} catch {
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (error instanceof BaseError) {
|
|
48
|
+
const reverted = error.walk(
|
|
49
|
+
(e) => e instanceof ContractFunctionRevertedError
|
|
50
|
+
);
|
|
51
|
+
if (reverted instanceof ContractFunctionRevertedError) {
|
|
52
|
+
return {
|
|
53
|
+
reason: reverted.data?.errorName ?? reverted.reason ?? reverted.shortMessage ?? "reverted",
|
|
54
|
+
cause: error
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return { reason: error.shortMessage ?? error.name, cause: error };
|
|
58
|
+
}
|
|
59
|
+
if (error instanceof Error) {
|
|
60
|
+
return { reason: error.message, cause: error };
|
|
61
|
+
}
|
|
62
|
+
return { reason: "reverted", cause: error };
|
|
63
|
+
}
|
|
64
|
+
function formatDecodedError(errorName, args) {
|
|
65
|
+
if (!args || args.length === 0) {
|
|
66
|
+
return errorName;
|
|
67
|
+
}
|
|
68
|
+
return `${errorName}(${args.map((arg) => String(arg)).join(", ")})`;
|
|
69
|
+
}
|
|
70
|
+
export {
|
|
71
|
+
PreviewSimulationError,
|
|
72
|
+
asPreviewSimulationError,
|
|
73
|
+
combinePreviewSimulationErrors,
|
|
74
|
+
decodeSimulationError
|
|
75
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { isAddressEqual, parseEventLogs } from "viem";
|
|
2
|
+
import { ierc20Abi } from "../../abi/iERC20.js";
|
|
3
|
+
function extractERC20Transfers(logs, watch) {
|
|
4
|
+
const parsed = parseEventLogs({
|
|
5
|
+
abi: ierc20Abi,
|
|
6
|
+
eventName: "Transfer",
|
|
7
|
+
logs
|
|
8
|
+
});
|
|
9
|
+
const transfers = [];
|
|
10
|
+
for (const log of parsed) {
|
|
11
|
+
const { from, to, value } = log.args;
|
|
12
|
+
const involved = watch.some(
|
|
13
|
+
(address) => isAddressEqual(from, address) || isAddressEqual(to, address)
|
|
14
|
+
);
|
|
15
|
+
if (involved) {
|
|
16
|
+
transfers.push({ token: log.address, amount: value, from, to });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return transfers;
|
|
20
|
+
}
|
|
21
|
+
export {
|
|
22
|
+
extractERC20Transfers
|
|
23
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { AddressSet } from "../../sdk/index.js";
|
|
2
|
+
function sourceHolder(operation, wallet) {
|
|
3
|
+
switch (operation.operation) {
|
|
4
|
+
case "Deposit":
|
|
5
|
+
case "Mint":
|
|
6
|
+
return wallet;
|
|
7
|
+
case "Withdraw":
|
|
8
|
+
case "Redeem":
|
|
9
|
+
return operation.owner;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function watchedHolders(operation, wallet) {
|
|
13
|
+
const holders = new AddressSet([
|
|
14
|
+
sourceHolder(operation, wallet),
|
|
15
|
+
operation.receiver
|
|
16
|
+
]);
|
|
17
|
+
return holders.asArray();
|
|
18
|
+
}
|
|
19
|
+
export {
|
|
20
|
+
watchedHolders
|
|
21
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ETH_SIMULATE_V1_NETWORKS } from "./constants.js";
|
|
2
|
+
import { PreviewSimulationError } from "./errors.js";
|
|
3
|
+
import { simulateFacadeOperation } from "./simulateFacadeOperation.js";
|
|
4
|
+
import { simulateOperation } from "./simulateOperation.js";
|
|
5
|
+
import { simulatePoolOperation } from "./simulatePoolOperation.js";
|
|
6
|
+
export {
|
|
7
|
+
ETH_SIMULATE_V1_NETWORKS,
|
|
8
|
+
PreviewSimulationError,
|
|
9
|
+
simulateFacadeOperation,
|
|
10
|
+
simulateOperation,
|
|
11
|
+
simulatePoolOperation
|
|
12
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { isPoolOperation } from "../parse/index.js";
|
|
2
|
+
import { simulateFacadeOperation } from "./simulateFacadeOperation.js";
|
|
3
|
+
import { simulatePoolOperation } from "./simulatePoolOperation.js";
|
|
4
|
+
async function simulateOperation(input, options) {
|
|
5
|
+
const { operation } = input;
|
|
6
|
+
if (isPoolOperation(operation)) {
|
|
7
|
+
return simulatePoolOperation({ ...input, operation }, options);
|
|
8
|
+
}
|
|
9
|
+
return simulateFacadeOperation({ ...input, operation }, options);
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
simulateOperation
|
|
13
|
+
};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { iPoolV310Abi } from "../../abi/310/generated.js";
|
|
2
|
+
import { ierc20Abi } from "../../abi/iERC20.js";
|
|
3
|
+
import { AddressMap } from "../../sdk/index.js";
|
|
4
|
+
import { decodeSimulationError, PreviewSimulationError } from "./errors.js";
|
|
5
|
+
import { watchedHolders } from "./holders.js";
|
|
6
|
+
function previewRead(operation) {
|
|
7
|
+
switch (operation.operation) {
|
|
8
|
+
case "Deposit":
|
|
9
|
+
return { functionName: "previewDeposit", amount: operation.assets };
|
|
10
|
+
case "Mint":
|
|
11
|
+
return { functionName: "previewMint", amount: operation.shares };
|
|
12
|
+
case "Withdraw":
|
|
13
|
+
return { functionName: "previewWithdraw", amount: operation.assets };
|
|
14
|
+
case "Redeem":
|
|
15
|
+
return { functionName: "previewRedeem", amount: operation.shares };
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
async function simulatePoolOpMulticall(input, options = {}) {
|
|
19
|
+
const { sdk, operation, wallet } = input;
|
|
20
|
+
const { blockNumber } = options;
|
|
21
|
+
const { underlying, pool } = operation;
|
|
22
|
+
const holders = watchedHolders(operation, wallet);
|
|
23
|
+
const tokens = [underlying, pool];
|
|
24
|
+
const balanceCalls = holders.flatMap(
|
|
25
|
+
(holder) => tokens.map((token) => ({ holder, token }))
|
|
26
|
+
);
|
|
27
|
+
const balanceContracts = balanceCalls.map(
|
|
28
|
+
({ holder, token }) => ({
|
|
29
|
+
address: token,
|
|
30
|
+
abi: ierc20Abi,
|
|
31
|
+
functionName: "balanceOf",
|
|
32
|
+
args: [holder]
|
|
33
|
+
})
|
|
34
|
+
);
|
|
35
|
+
const { functionName, amount } = previewRead(operation);
|
|
36
|
+
const previewContract = {
|
|
37
|
+
address: pool,
|
|
38
|
+
abi: iPoolV310Abi,
|
|
39
|
+
functionName,
|
|
40
|
+
args: [amount]
|
|
41
|
+
};
|
|
42
|
+
let results;
|
|
43
|
+
try {
|
|
44
|
+
results = await sdk.client.multicall({
|
|
45
|
+
allowFailure: false,
|
|
46
|
+
// `undefined` lets viem read at `latest`; `blockNumber` is only set for
|
|
47
|
+
// testnet forks pinned to a specific block.
|
|
48
|
+
blockNumber,
|
|
49
|
+
contracts: [...balanceContracts, previewContract]
|
|
50
|
+
});
|
|
51
|
+
} catch (cause) {
|
|
52
|
+
throw new PreviewSimulationError([
|
|
53
|
+
{
|
|
54
|
+
source: "multicall",
|
|
55
|
+
detail: decodeSimulationError({
|
|
56
|
+
error: cause instanceof Error ? cause : new Error(String(cause))
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
]);
|
|
60
|
+
}
|
|
61
|
+
const previewAmount = results[balanceContracts.length];
|
|
62
|
+
const balances = new AddressMap();
|
|
63
|
+
for (const [i, { holder, token }] of balanceCalls.entries()) {
|
|
64
|
+
const tokenBalances = balances.get(holder) ?? new AddressMap();
|
|
65
|
+
tokenBalances.upsert(token, results[i]);
|
|
66
|
+
balances.upsert(holder, tokenBalances);
|
|
67
|
+
}
|
|
68
|
+
const before = (token, holder) => balances.get(holder)?.get(token) ?? 0n;
|
|
69
|
+
return {
|
|
70
|
+
balanceChanges: computePoolOpBalanceChanges(
|
|
71
|
+
operation,
|
|
72
|
+
wallet,
|
|
73
|
+
previewAmount,
|
|
74
|
+
before
|
|
75
|
+
),
|
|
76
|
+
transfers: void 0
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function balanceLegs(operation, wallet, previewAmount) {
|
|
80
|
+
const { underlying, pool, receiver } = operation;
|
|
81
|
+
switch (operation.operation) {
|
|
82
|
+
case "Deposit":
|
|
83
|
+
return [
|
|
84
|
+
{ address: wallet, token: underlying, delta: -operation.assets },
|
|
85
|
+
{ address: receiver, token: pool, delta: previewAmount }
|
|
86
|
+
];
|
|
87
|
+
case "Mint":
|
|
88
|
+
return [
|
|
89
|
+
{ address: wallet, token: underlying, delta: -previewAmount },
|
|
90
|
+
{ address: receiver, token: pool, delta: operation.shares }
|
|
91
|
+
];
|
|
92
|
+
case "Withdraw":
|
|
93
|
+
return [
|
|
94
|
+
{ address: operation.owner, token: pool, delta: -previewAmount },
|
|
95
|
+
{ address: receiver, token: underlying, delta: operation.assets }
|
|
96
|
+
];
|
|
97
|
+
case "Redeem":
|
|
98
|
+
return [
|
|
99
|
+
{ address: operation.owner, token: pool, delta: -operation.shares },
|
|
100
|
+
{ address: receiver, token: underlying, delta: previewAmount }
|
|
101
|
+
];
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function computePoolOpBalanceChanges(operation, wallet, previewAmount, before) {
|
|
105
|
+
const byAddress = new AddressMap();
|
|
106
|
+
for (const { address, token, delta } of balanceLegs(
|
|
107
|
+
operation,
|
|
108
|
+
wallet,
|
|
109
|
+
previewAmount
|
|
110
|
+
)) {
|
|
111
|
+
const beforeBalance = before(token, address);
|
|
112
|
+
const change = {
|
|
113
|
+
token,
|
|
114
|
+
before: beforeBalance,
|
|
115
|
+
after: beforeBalance + delta,
|
|
116
|
+
delta
|
|
117
|
+
};
|
|
118
|
+
const existing = byAddress.get(address);
|
|
119
|
+
if (existing) {
|
|
120
|
+
existing.changes.push(change);
|
|
121
|
+
} else {
|
|
122
|
+
byAddress.upsert(address, { address, changes: [change] });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return byAddress.values();
|
|
126
|
+
}
|
|
127
|
+
export {
|
|
128
|
+
computePoolOpBalanceChanges,
|
|
129
|
+
simulatePoolOpMulticall
|
|
130
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { simulateCalls } from "viem/actions";
|
|
2
|
+
import { ierc20Abi } from "../../abi/iERC20.js";
|
|
3
|
+
import { decodeSimulationError, PreviewSimulationError } from "./errors.js";
|
|
4
|
+
import { extractERC20Transfers } from "./extractERC20Transfers.js";
|
|
5
|
+
import { watchedHolders } from "./holders.js";
|
|
6
|
+
async function simulatePoolOpV1(input, options = {}) {
|
|
7
|
+
const { sdk, operation, to, calldata, wallet } = input;
|
|
8
|
+
const { blockNumber } = options;
|
|
9
|
+
const { underlying, pool } = operation;
|
|
10
|
+
const holders = watchedHolders(operation, wallet);
|
|
11
|
+
const tokens = [underlying, pool];
|
|
12
|
+
const balanceOf = (token, holder) => ({
|
|
13
|
+
to: token,
|
|
14
|
+
abi: ierc20Abi,
|
|
15
|
+
functionName: "balanceOf",
|
|
16
|
+
args: [holder]
|
|
17
|
+
});
|
|
18
|
+
const balanceCalls = holders.flatMap(
|
|
19
|
+
(holder) => tokens.map((token) => balanceOf(token, holder))
|
|
20
|
+
);
|
|
21
|
+
let results;
|
|
22
|
+
try {
|
|
23
|
+
({ results } = await simulateCalls(sdk.client, {
|
|
24
|
+
account: wallet,
|
|
25
|
+
// `undefined` lets viem simulate at `latest`; `blockNumber` is only set
|
|
26
|
+
// for testnet forks pinned to a specific block.
|
|
27
|
+
blockNumber,
|
|
28
|
+
calls: [...balanceCalls, { to, data: calldata }, ...balanceCalls]
|
|
29
|
+
}));
|
|
30
|
+
} catch (cause) {
|
|
31
|
+
throw new PreviewSimulationError([
|
|
32
|
+
{
|
|
33
|
+
source: "eth_simulateV1",
|
|
34
|
+
detail: decodeSimulationError({
|
|
35
|
+
error: cause instanceof Error ? cause : new Error(String(cause))
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
]);
|
|
39
|
+
}
|
|
40
|
+
const sim = results;
|
|
41
|
+
const txIndex = balanceCalls.length;
|
|
42
|
+
const afterOffset = txIndex + 1;
|
|
43
|
+
const txResult = sim[txIndex];
|
|
44
|
+
if (!txResult || txResult.status === "failure") {
|
|
45
|
+
throw new PreviewSimulationError([
|
|
46
|
+
{
|
|
47
|
+
source: "eth_simulateV1",
|
|
48
|
+
detail: decodeSimulationError({
|
|
49
|
+
error: txResult?.error,
|
|
50
|
+
data: txResult?.data
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
]);
|
|
54
|
+
}
|
|
55
|
+
const balanceChanges = [];
|
|
56
|
+
for (const [holderIndex, address] of holders.entries()) {
|
|
57
|
+
const changes = [];
|
|
58
|
+
for (const [tokenIndex, token] of tokens.entries()) {
|
|
59
|
+
const slot = holderIndex * tokens.length + tokenIndex;
|
|
60
|
+
const before = readBalance(sim[slot]);
|
|
61
|
+
const after = readBalance(sim[afterOffset + slot]);
|
|
62
|
+
const delta = after - before;
|
|
63
|
+
const magnitude = delta >= 0n ? delta : -delta;
|
|
64
|
+
if (magnitude > 1n) {
|
|
65
|
+
changes.push({ token, before, after, delta });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (changes.length > 0) {
|
|
69
|
+
balanceChanges.push({ address, changes });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
transfers: extractERC20Transfers(txResult.logs ?? [], holders),
|
|
74
|
+
balanceChanges
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function readBalance(result) {
|
|
78
|
+
return result?.status === "success" ? result.result : 0n;
|
|
79
|
+
}
|
|
80
|
+
export {
|
|
81
|
+
simulatePoolOpV1
|
|
82
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { ETH_SIMULATE_V1_NETWORKS } from "./constants.js";
|
|
2
|
+
import {
|
|
3
|
+
asPreviewSimulationError,
|
|
4
|
+
combinePreviewSimulationErrors
|
|
5
|
+
} from "./errors.js";
|
|
6
|
+
import { simulatePoolOpMulticall } from "./simulatePoolOpMulticall.js";
|
|
7
|
+
import { simulatePoolOpV1 } from "./simulatePoolOpV1.js";
|
|
8
|
+
async function simulatePoolOperation(input, options = {}) {
|
|
9
|
+
const { sdk } = input;
|
|
10
|
+
const { logger } = options;
|
|
11
|
+
const useSimulateV1 = options.useSimulateV1 ?? ETH_SIMULATE_V1_NETWORKS.has(sdk.networkType);
|
|
12
|
+
logger?.debug(
|
|
13
|
+
{ wallet: input.wallet, to: input.to },
|
|
14
|
+
"simulating pool operation"
|
|
15
|
+
);
|
|
16
|
+
const [v1, multicall] = await Promise.allSettled([
|
|
17
|
+
useSimulateV1 ? simulatePoolOpV1(input, options) : void 0,
|
|
18
|
+
simulatePoolOpMulticall(input, options)
|
|
19
|
+
]);
|
|
20
|
+
if (v1.status === "fulfilled" && v1.value) {
|
|
21
|
+
return { status: "success", ...v1.value };
|
|
22
|
+
}
|
|
23
|
+
if (multicall.status === "fulfilled") {
|
|
24
|
+
if (v1.status === "rejected") {
|
|
25
|
+
logger?.debug(
|
|
26
|
+
asPreviewSimulationError(v1.reason, "eth_simulateV1"),
|
|
27
|
+
"eth_simulateV1 flow failed; falling back to multicall result"
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
return { status: "success", ...multicall.value };
|
|
31
|
+
}
|
|
32
|
+
const error = combinePreviewSimulationErrors(
|
|
33
|
+
v1.status === "rejected" ? asPreviewSimulationError(v1.reason, "eth_simulateV1") : void 0,
|
|
34
|
+
asPreviewSimulationError(multicall.reason, "multicall")
|
|
35
|
+
);
|
|
36
|
+
logger?.error(error, "pool operation simulation failed");
|
|
37
|
+
return { status: "failure", error };
|
|
38
|
+
}
|
|
39
|
+
export {
|
|
40
|
+
simulatePoolOperation
|
|
41
|
+
};
|
|
File without changes
|
|
@@ -22,28 +22,6 @@ class WithdrawCollateralAlignmentError extends Error {
|
|
|
22
22
|
this.name = "WithdrawCollateralAlignmentError";
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
|
-
class ProtocolCallNotFoundError extends Error {
|
|
26
|
-
targetContract;
|
|
27
|
-
executeIndex;
|
|
28
|
-
constructor(targetContract, executeIndex) {
|
|
29
|
-
super(
|
|
30
|
-
`protocol call to ${targetContract} not found in trace for Execute #${executeIndex}`
|
|
31
|
-
);
|
|
32
|
-
this.name = "ProtocolCallNotFoundError";
|
|
33
|
-
this.targetContract = targetContract;
|
|
34
|
-
this.executeIndex = executeIndex;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
class AdapterTraceAlignmentError extends Error {
|
|
38
|
-
constructor(expected, actual) {
|
|
39
|
-
super(`found ${actual} adapter traces for ${expected} execute events`);
|
|
40
|
-
this.expected = expected;
|
|
41
|
-
this.actual = actual;
|
|
42
|
-
this.name = "AdapterTracesAlignmentError";
|
|
43
|
-
}
|
|
44
|
-
expected;
|
|
45
|
-
actual;
|
|
46
|
-
}
|
|
47
25
|
class UnexpectedFacadeEventOrderError extends Error {
|
|
48
26
|
constructor(e) {
|
|
49
27
|
super(
|
|
@@ -63,8 +41,6 @@ class UnknownFacadeCallError extends Error {
|
|
|
63
41
|
}
|
|
64
42
|
}
|
|
65
43
|
export {
|
|
66
|
-
AdapterTraceAlignmentError,
|
|
67
|
-
ProtocolCallNotFoundError,
|
|
68
44
|
TransferAlignmentError,
|
|
69
45
|
UnexpectedFacadeEventOrderError,
|
|
70
46
|
UnknownAdapterError,
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import {
|
|
2
|
+
decodeAbiParameters,
|
|
3
|
+
isAddressEqual,
|
|
4
|
+
toFunctionSelector
|
|
5
|
+
} from "viem";
|
|
6
|
+
import {
|
|
7
|
+
resolveProtocolCall
|
|
8
|
+
} from "../../common-utils/utils/trace.js";
|
|
9
|
+
const WITHDRAW_PHANTOM_TOKEN_SELECTOR = toFunctionSelector(
|
|
10
|
+
"withdrawPhantomToken(address,uint256)"
|
|
11
|
+
);
|
|
12
|
+
const GET_PHANTOM_TOKEN_INFO_SELECTOR = toFunctionSelector(
|
|
13
|
+
"getPhantomTokenInfo()"
|
|
14
|
+
);
|
|
15
|
+
function isSynthesizedPhantomWithdrawal(siblings, index) {
|
|
16
|
+
const node = siblings[index];
|
|
17
|
+
if (node.type !== "CALL" || node.input.slice(0, 10).toLowerCase() !== WITHDRAW_PHANTOM_TOKEN_SELECTOR) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
const [token] = decodeAbiParameters(
|
|
21
|
+
[{ type: "address" }, { type: "uint256" }],
|
|
22
|
+
`0x${node.input.slice(10)}`
|
|
23
|
+
);
|
|
24
|
+
for (let i = 0; i < index; i++) {
|
|
25
|
+
const sib = siblings[i];
|
|
26
|
+
if (sib.type === "STATICCALL" && sib.input.slice(0, 10).toLowerCase() === GET_PHANTOM_TOKEN_INFO_SELECTOR && isAddressEqual(sib.to, token)) {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
function extractAdapterCallTraces(facadeTrace) {
|
|
33
|
+
const subtraces = facadeTrace.calls ?? [];
|
|
34
|
+
return subtraces.filter(
|
|
35
|
+
(sub, index) => resolveProtocolCall(sub) !== void 0 && !isSynthesizedPhantomWithdrawal(subtraces, index)
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
export {
|
|
39
|
+
extractAdapterCallTraces
|
|
40
|
+
};
|
|
@@ -3,9 +3,9 @@ import {
|
|
|
3
3
|
isAddressEqual,
|
|
4
4
|
parseEventLogs
|
|
5
5
|
} from "viem";
|
|
6
|
-
import { iCreditFacadeV310Abi } from "
|
|
7
|
-
import { ierc20Abi } from "
|
|
8
|
-
import { AddressMap } from "
|
|
6
|
+
import { iCreditFacadeV310Abi } from "../../abi/310/generated.js";
|
|
7
|
+
import { ierc20Abi } from "../../abi/iERC20.js";
|
|
8
|
+
import { AddressMap } from "../../sdk/index.js";
|
|
9
9
|
import { UnexpectedFacadeEventOrderError } from "./errors.js";
|
|
10
10
|
function extractTransfers(logs, creditAccount, pool, creditFacade) {
|
|
11
11
|
const ranges = buildOperationRanges(logs, creditFacade, creditAccount);
|
|
@@ -14,7 +14,7 @@ function extractTransfers(logs, creditAccount, pool, creditFacade) {
|
|
|
14
14
|
return !!e && (e.eventName === "LiquidateCreditAccount" || e.eventName === "PartiallyLiquidateCreditAccount") && isAddressEqual(e.args.creditAccount, creditAccount);
|
|
15
15
|
});
|
|
16
16
|
let currentEntries = [];
|
|
17
|
-
const
|
|
17
|
+
const executeTransfers = [];
|
|
18
18
|
const directTransfers = [];
|
|
19
19
|
const phantomTokens = new AddressMap();
|
|
20
20
|
const withdrawCollateralEvents = [];
|
|
@@ -23,20 +23,17 @@ function extractTransfers(logs, creditAccount, pool, creditFacade) {
|
|
|
23
23
|
const facadeEvent = tryDecodeFacadeEvent(log, creditFacade);
|
|
24
24
|
if (facadeEvent) {
|
|
25
25
|
if (isExecute(facadeEvent, creditAccount)) {
|
|
26
|
-
|
|
27
|
-
transfers: currentEntries,
|
|
28
|
-
targetContract: facadeEvent.args.targetContract
|
|
29
|
-
});
|
|
26
|
+
executeTransfers.push(currentEntries);
|
|
30
27
|
} else if (isLiquidation(facadeEvent, creditAccount)) {
|
|
31
28
|
liquidationRemainingFunds = facadeEvent.args.remainingFunds;
|
|
32
29
|
} else if (isWithdrawPhantomToken(facadeEvent, creditAccount)) {
|
|
33
|
-
const
|
|
34
|
-
if (!
|
|
30
|
+
const phantomTransfers = executeTransfers.pop();
|
|
31
|
+
if (!phantomTransfers) {
|
|
35
32
|
throw new Error(
|
|
36
33
|
`WithdrawPhantomToken without preceding Execute at logIndex ${facadeEvent.logIndex}`
|
|
37
34
|
);
|
|
38
35
|
}
|
|
39
|
-
const rawDeposit =
|
|
36
|
+
const rawDeposit = phantomTransfers.find(
|
|
40
37
|
(t) => isAddressEqual(t.to, creditAccount)
|
|
41
38
|
);
|
|
42
39
|
if (!rawDeposit) {
|
|
@@ -80,7 +77,7 @@ function extractTransfers(logs, creditAccount, pool, creditFacade) {
|
|
|
80
77
|
}
|
|
81
78
|
}
|
|
82
79
|
return {
|
|
83
|
-
|
|
80
|
+
executeTransfers,
|
|
84
81
|
directTransfers,
|
|
85
82
|
liquidationRemainingFunds,
|
|
86
83
|
phantomTokens,
|
|
@@ -2,9 +2,11 @@ import {
|
|
|
2
2
|
decodeFunctionResult,
|
|
3
3
|
isAddressEqual
|
|
4
4
|
} from "viem";
|
|
5
|
-
import { iCreditFacadeV310Abi } from "
|
|
5
|
+
import { iCreditFacadeV310Abi } from "../../abi/310/generated.js";
|
|
6
|
+
import {
|
|
7
|
+
collectTraces
|
|
8
|
+
} from "../../common-utils/utils/trace.js";
|
|
6
9
|
import { UnknownFacadeCallError } from "./errors.js";
|
|
7
|
-
import { collectTraces } from "./trace-utils.js";
|
|
8
10
|
const FACADE_CALL_TYPES = {
|
|
9
11
|
multicall: "MultiCall",
|
|
10
12
|
botMulticall: "BotMulticall",
|
|
File without changes
|