@gearbox-protocol/sdk 14.12.0-next.29 → 14.12.0-next.30
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/preview/preview/CreditAccountState.js +154 -0
- package/dist/cjs/preview/preview/buildDelayedPreview.js +111 -0
- package/dist/cjs/preview/preview/detectDelayedOperation.js +71 -0
- package/dist/cjs/preview/preview/errors.js +17 -0
- package/dist/cjs/preview/preview/index.js +10 -4
- package/dist/cjs/preview/preview/previewAdjustCreditAccount.js +16 -39
- package/dist/cjs/preview/preview/previewCloseOrRepayCreditAccount.js +11 -37
- package/dist/cjs/preview/preview/previewOpenCreditAccount.js +13 -8
- package/dist/cjs/preview/preview/previewOperation.js +46 -5
- package/dist/cjs/preview/preview/{applyInnerOperations.js → replayInnerOperations.js} +24 -35
- package/dist/cjs/preview/preview/{applyQuotaChanges.js → replayMulticall.js} +14 -20
- package/dist/cjs/preview/preview/types.js +5 -2
- package/dist/esm/preview/preview/CreditAccountState.js +133 -0
- package/dist/esm/preview/preview/buildDelayedPreview.js +90 -0
- package/dist/esm/preview/preview/detectDelayedOperation.js +51 -0
- package/dist/esm/preview/preview/errors.js +16 -0
- package/dist/esm/preview/preview/index.js +5 -2
- package/dist/esm/preview/preview/previewAdjustCreditAccount.js +19 -48
- package/dist/esm/preview/preview/previewCloseOrRepayCreditAccount.js +14 -44
- package/dist/esm/preview/preview/previewOpenCreditAccount.js +15 -10
- package/dist/esm/preview/preview/previewOperation.js +52 -6
- package/dist/esm/preview/preview/{applyInnerOperations.js → replayInnerOperations.js} +20 -34
- package/dist/esm/preview/preview/replayMulticall.js +16 -0
- package/dist/esm/preview/preview/types.js +3 -1
- package/dist/types/preview/preview/CreditAccountState.d.ts +83 -0
- package/dist/types/preview/preview/buildDelayedPreview.d.ts +41 -0
- package/dist/types/preview/preview/detectDelayedOperation.d.ts +29 -0
- package/dist/types/preview/preview/errors.d.ts +15 -0
- package/dist/types/preview/preview/index.d.ts +5 -2
- package/dist/types/preview/preview/previewAdjustCreditAccount.d.ts +3 -3
- package/dist/types/preview/preview/previewCloseOrRepayCreditAccount.d.ts +2 -2
- package/dist/types/preview/preview/replayInnerOperations.d.ts +44 -0
- package/dist/types/preview/preview/replayMulticall.d.ts +38 -0
- package/dist/types/preview/preview/types.d.ts +58 -3
- package/dist/types/preview/types.d.ts +17 -6
- package/package.json +1 -1
- package/dist/esm/preview/preview/applyQuotaChanges.js +0 -19
- package/dist/types/preview/preview/applyInnerOperations.d.ts +0 -59
- package/dist/types/preview/preview/applyQuotaChanges.d.ts +0 -24
|
@@ -22,12 +22,15 @@ __export(previewOperation_exports, {
|
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(previewOperation_exports);
|
|
24
24
|
var import_parse = require("../parse/index.js");
|
|
25
|
+
var import_buildDelayedPreview = require("./buildDelayedPreview.js");
|
|
25
26
|
var import_detectCloseOrRepay = require("./detectCloseOrRepay.js");
|
|
27
|
+
var import_detectDelayedOperation = require("./detectDelayedOperation.js");
|
|
26
28
|
var import_errors = require("./errors.js");
|
|
27
29
|
var import_previewAdjustCreditAccount = require("./previewAdjustCreditAccount.js");
|
|
28
30
|
var import_previewCloseOrRepayCreditAccount = require("./previewCloseOrRepayCreditAccount.js");
|
|
29
31
|
var import_previewOpenCreditAccount = require("./previewOpenCreditAccount.js");
|
|
30
32
|
var import_previewPoolOperation = require("./previewPoolOperation.js");
|
|
33
|
+
var import_replayMulticall = require("./replayMulticall.js");
|
|
31
34
|
async function previewOperation(input, options) {
|
|
32
35
|
const operation = (0, import_parse.parseOperationCalldata)(input);
|
|
33
36
|
if ((0, import_parse.isPoolOperation)(operation)) {
|
|
@@ -37,16 +40,54 @@ async function previewOperation(input, options) {
|
|
|
37
40
|
return (0, import_previewOpenCreditAccount.previewOpenCreditAccount)(input, operation);
|
|
38
41
|
}
|
|
39
42
|
if (operation.operation === "CloseCreditAccount") {
|
|
40
|
-
|
|
43
|
+
const resolved = await resolveCreditAccount(input, operation, options);
|
|
44
|
+
return (0, import_previewCloseOrRepayCreditAccount.previewCloseOrRepayCreditAccount)(input, operation, true, resolved);
|
|
41
45
|
}
|
|
42
46
|
if (operation.operation === "MultiCall" || operation.operation === "BotMulticall" || operation.operation === "RWAMulticall") {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
return (0, import_previewAdjustCreditAccount.previewAdjustCreditAccount)(input, operation, options);
|
|
47
|
+
const resolved = await resolveCreditAccount(input, operation, options);
|
|
48
|
+
return previewMulticallOperation(input, operation, resolved);
|
|
47
49
|
}
|
|
48
50
|
throw new import_errors.UnsupportedOperationError(operation.operation);
|
|
49
51
|
}
|
|
52
|
+
async function resolveCreditAccount(input, operation, options) {
|
|
53
|
+
let creditAccount = options?.creditAccount;
|
|
54
|
+
if (!creditAccount) {
|
|
55
|
+
creditAccount = await input.sdk.accounts.getCreditAccountData(
|
|
56
|
+
operation.creditAccount,
|
|
57
|
+
options?.blockNumber
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
if (!creditAccount) {
|
|
61
|
+
throw new Error(`credit account ${operation.creditAccount} not found`);
|
|
62
|
+
}
|
|
63
|
+
return { ...options, creditAccount };
|
|
64
|
+
}
|
|
65
|
+
async function previewMulticallOperation(input, operation, options) {
|
|
66
|
+
const { sdk } = input;
|
|
67
|
+
const instantPreview = (0, import_detectCloseOrRepay.isCloseOrRepay)(operation.multicall) ? await (0, import_previewCloseOrRepayCreditAccount.previewCloseOrRepayCreditAccount)(input, operation, false, options) : await (0, import_previewAdjustCreditAccount.previewAdjustCreditAccount)(input, operation, options);
|
|
68
|
+
const delayed = (0, import_detectDelayedOperation.detectDelayedOperation)(sdk, operation.multicall);
|
|
69
|
+
if (!delayed) {
|
|
70
|
+
return instantPreview;
|
|
71
|
+
}
|
|
72
|
+
const { before, after } = await (0, import_replayMulticall.replayMulticall)(sdk, operation, options);
|
|
73
|
+
const market = sdk.marketRegister.findByCreditManager(
|
|
74
|
+
operation.creditManager
|
|
75
|
+
);
|
|
76
|
+
const convert = (token, to, amount) => market.priceOracle.convert(token, to, amount);
|
|
77
|
+
return {
|
|
78
|
+
operation: "DelayedCreditAccountOperation",
|
|
79
|
+
creditAccount: operation.creditAccount,
|
|
80
|
+
creditManager: operation.creditManager,
|
|
81
|
+
intent: delayed.intent,
|
|
82
|
+
instantPreview,
|
|
83
|
+
delayedPreview: (0, import_buildDelayedPreview.buildDelayedPreview)(
|
|
84
|
+
after.account,
|
|
85
|
+
before,
|
|
86
|
+
delayed,
|
|
87
|
+
convert
|
|
88
|
+
)
|
|
89
|
+
};
|
|
90
|
+
}
|
|
50
91
|
// Annotate the CommonJS export names for ESM import in node:
|
|
51
92
|
0 && (module.exports = {
|
|
52
93
|
previewOperation
|
|
@@ -16,27 +16,24 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
16
16
|
return to;
|
|
17
17
|
};
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
var
|
|
20
|
-
__export(
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
var replayInnerOperations_exports = {};
|
|
20
|
+
__export(replayInnerOperations_exports, {
|
|
21
|
+
makeReplayState: () => makeReplayState,
|
|
22
|
+
replayInnerOperations: () => replayInnerOperations
|
|
23
23
|
});
|
|
24
|
-
module.exports = __toCommonJS(
|
|
24
|
+
module.exports = __toCommonJS(replayInnerOperations_exports);
|
|
25
25
|
var import_adapters = require("../../plugins/adapters/index.js");
|
|
26
26
|
var import_sdk = require("../../sdk/index.js");
|
|
27
27
|
var import_applyRWAWrapUnwrap = require("./applyRWAWrapUnwrap.js");
|
|
28
28
|
var import_types = require("./types.js");
|
|
29
|
-
function
|
|
29
|
+
function makeReplayState(account) {
|
|
30
30
|
return {
|
|
31
|
-
|
|
32
|
-
debt: 0n,
|
|
33
|
-
totalDebt: 0n,
|
|
31
|
+
account,
|
|
34
32
|
collateralAdded: new import_sdk.AssetsMap(),
|
|
35
|
-
collateralWithdrawn: new import_sdk.AssetsMap()
|
|
36
|
-
quotaChanges: []
|
|
33
|
+
collateralWithdrawn: new import_sdk.AssetsMap()
|
|
37
34
|
};
|
|
38
35
|
}
|
|
39
|
-
async function
|
|
36
|
+
async function replayInnerOperations(sdk, multicall, state) {
|
|
40
37
|
let inBracket = false;
|
|
41
38
|
let error;
|
|
42
39
|
for (const op of multicall) {
|
|
@@ -49,13 +46,13 @@ async function applyInnerOperations(sdk, multicall, state) {
|
|
|
49
46
|
applyWithdrawCollateral(state, op);
|
|
50
47
|
break;
|
|
51
48
|
case "IncreaseBorrowedAmount":
|
|
52
|
-
|
|
49
|
+
state.account.increaseDebt(op.amount);
|
|
53
50
|
break;
|
|
54
51
|
case "DecreaseBorrowedAmount":
|
|
55
|
-
|
|
52
|
+
state.account.repay(op.amount);
|
|
56
53
|
break;
|
|
57
54
|
case "UpdateQuota":
|
|
58
|
-
state.
|
|
55
|
+
state.account.updateQuota(op.token, op.change);
|
|
59
56
|
break;
|
|
60
57
|
case "StoreExpectedBalances":
|
|
61
58
|
if (inBracket) {
|
|
@@ -66,7 +63,7 @@ async function applyInnerOperations(sdk, multicall, state) {
|
|
|
66
63
|
}
|
|
67
64
|
inBracket = true;
|
|
68
65
|
for (const { token, balance } of op.deltas) {
|
|
69
|
-
state.balances.inc(token, balance);
|
|
66
|
+
state.account.balances.inc(token, balance);
|
|
70
67
|
}
|
|
71
68
|
break;
|
|
72
69
|
case "CompareBalances":
|
|
@@ -79,7 +76,12 @@ async function applyInnerOperations(sdk, multicall, state) {
|
|
|
79
76
|
inBracket = false;
|
|
80
77
|
break;
|
|
81
78
|
case "Execute":
|
|
82
|
-
opError = await applyExecute(
|
|
79
|
+
opError = await applyExecute(
|
|
80
|
+
sdk,
|
|
81
|
+
op,
|
|
82
|
+
inBracket,
|
|
83
|
+
state.account.balances
|
|
84
|
+
);
|
|
83
85
|
break;
|
|
84
86
|
}
|
|
85
87
|
error ??= opError;
|
|
@@ -94,27 +96,14 @@ async function applyInnerOperations(sdk, multicall, state) {
|
|
|
94
96
|
}
|
|
95
97
|
function applyAddCollateral(state, op) {
|
|
96
98
|
state.collateralAdded.inc(op.token, op.amount);
|
|
97
|
-
state.balances.inc(op.token, op.amount);
|
|
99
|
+
state.account.balances.inc(op.token, op.amount);
|
|
98
100
|
}
|
|
99
101
|
function applyWithdrawCollateral(state, op) {
|
|
100
|
-
const running = state.balances.getOrZero(op.token);
|
|
102
|
+
const running = state.account.balances.getOrZero(op.token);
|
|
101
103
|
const amount = op.amount === import_sdk.MAX_UINT256 ? running > 0n ? running : 0n : op.amount;
|
|
102
|
-
state.balances.dec(op.token, amount);
|
|
104
|
+
state.account.balances.dec(op.token, amount);
|
|
103
105
|
state.collateralWithdrawn.inc(op.token, amount);
|
|
104
106
|
}
|
|
105
|
-
function applyIncreaseDebt(state, op) {
|
|
106
|
-
state.debt += op.amount;
|
|
107
|
-
state.totalDebt += op.amount;
|
|
108
|
-
state.balances.inc(op.token, op.amount);
|
|
109
|
-
}
|
|
110
|
-
function applyDecreaseDebt(state, op) {
|
|
111
|
-
const repaid = op.amount > state.totalDebt ? state.totalDebt : op.amount;
|
|
112
|
-
state.balances.dec(op.token, repaid);
|
|
113
|
-
state.totalDebt -= repaid;
|
|
114
|
-
if (state.debt > state.totalDebt) {
|
|
115
|
-
state.debt = state.totalDebt;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
107
|
async function applyExecute(sdk, op, inBracket, balances) {
|
|
119
108
|
const adapter = sdk.getContract(op.adapter);
|
|
120
109
|
if (!inBracket) {
|
|
@@ -159,6 +148,6 @@ function isRWAShare(sdk, adapter) {
|
|
|
159
148
|
}
|
|
160
149
|
// Annotate the CommonJS export names for ESM import in node:
|
|
161
150
|
0 && (module.exports = {
|
|
162
|
-
|
|
163
|
-
|
|
151
|
+
makeReplayState,
|
|
152
|
+
replayInnerOperations
|
|
164
153
|
});
|
|
@@ -16,28 +16,22 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
16
16
|
return to;
|
|
17
17
|
};
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
var
|
|
20
|
-
__export(
|
|
21
|
-
|
|
19
|
+
var replayMulticall_exports = {};
|
|
20
|
+
__export(replayMulticall_exports, {
|
|
21
|
+
replayMulticall: () => replayMulticall
|
|
22
22
|
});
|
|
23
|
-
module.exports = __toCommonJS(
|
|
24
|
-
var
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
return {
|
|
36
|
-
quotas: final.toAssets(0n),
|
|
37
|
-
quotasChange: final.difference(initialQuotas).toAssets()
|
|
38
|
-
};
|
|
23
|
+
module.exports = __toCommonJS(replayMulticall_exports);
|
|
24
|
+
var import_CreditAccountState = require("./CreditAccountState.js");
|
|
25
|
+
var import_replayInnerOperations = require("./replayInnerOperations.js");
|
|
26
|
+
async function replayMulticall(sdk, operation, options) {
|
|
27
|
+
const before = import_CreditAccountState.CreditAccountState.fromCreditAccountData(
|
|
28
|
+
options.creditAccount
|
|
29
|
+
);
|
|
30
|
+
const after = (0, import_replayInnerOperations.makeReplayState)(before.clone());
|
|
31
|
+
const error = await (0, import_replayInnerOperations.replayInnerOperations)(sdk, operation.multicall, after);
|
|
32
|
+
return { before, after, error };
|
|
39
33
|
}
|
|
40
34
|
// Annotate the CommonJS export names for ESM import in node:
|
|
41
35
|
0 && (module.exports = {
|
|
42
|
-
|
|
36
|
+
replayMulticall
|
|
43
37
|
});
|
|
@@ -24,7 +24,8 @@ __export(types_exports, {
|
|
|
24
24
|
ERROR_NON_ADAPTER_CALL_IN_BRACKET: () => ERROR_NON_ADAPTER_CALL_IN_BRACKET,
|
|
25
25
|
ERROR_UNPREVIEWABLE_ADAPTER_CALL: () => ERROR_UNPREVIEWABLE_ADAPTER_CALL,
|
|
26
26
|
ERROR_UNPREVIEWABLE_RWA_WRAP_UNWRAP: () => ERROR_UNPREVIEWABLE_RWA_WRAP_UNWRAP,
|
|
27
|
-
ERROR_UNPRICEABLE_TOKEN: () => ERROR_UNPRICEABLE_TOKEN
|
|
27
|
+
ERROR_UNPRICEABLE_TOKEN: () => ERROR_UNPRICEABLE_TOKEN,
|
|
28
|
+
PREVIEW_DUST: () => PREVIEW_DUST
|
|
28
29
|
});
|
|
29
30
|
module.exports = __toCommonJS(types_exports);
|
|
30
31
|
const ERROR_MALFORMED_BRACKET = 1001;
|
|
@@ -34,6 +35,7 @@ const ERROR_UNPREVIEWABLE_ADAPTER_CALL = 1004;
|
|
|
34
35
|
const ERROR_UNPREVIEWABLE_RWA_WRAP_UNWRAP = 1005;
|
|
35
36
|
const ERROR_INVALID_TRANSACTION_VALUE = 1006;
|
|
36
37
|
const ERROR_UNPRICEABLE_TOKEN = 2001;
|
|
38
|
+
const PREVIEW_DUST = 10n;
|
|
37
39
|
// Annotate the CommonJS export names for ESM import in node:
|
|
38
40
|
0 && (module.exports = {
|
|
39
41
|
ERROR_ADAPTER_CALL_OUTSIDE_BRACKET,
|
|
@@ -42,5 +44,6 @@ const ERROR_UNPRICEABLE_TOKEN = 2001;
|
|
|
42
44
|
ERROR_NON_ADAPTER_CALL_IN_BRACKET,
|
|
43
45
|
ERROR_UNPREVIEWABLE_ADAPTER_CALL,
|
|
44
46
|
ERROR_UNPREVIEWABLE_RWA_WRAP_UNWRAP,
|
|
45
|
-
ERROR_UNPRICEABLE_TOKEN
|
|
47
|
+
ERROR_UNPRICEABLE_TOKEN,
|
|
48
|
+
PREVIEW_DUST
|
|
46
49
|
});
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { zeroAddress } from "viem";
|
|
2
|
+
import {
|
|
3
|
+
AssetsMap,
|
|
4
|
+
MIN_INT96
|
|
5
|
+
} from "../../sdk/index.js";
|
|
6
|
+
class CreditAccountState {
|
|
7
|
+
creditAccount;
|
|
8
|
+
creditManager;
|
|
9
|
+
/**
|
|
10
|
+
* Credit manager underlying
|
|
11
|
+
*/
|
|
12
|
+
underlying;
|
|
13
|
+
/**
|
|
14
|
+
* Per-token balances
|
|
15
|
+
*/
|
|
16
|
+
balances;
|
|
17
|
+
/**
|
|
18
|
+
* Per-token quotas
|
|
19
|
+
*/
|
|
20
|
+
quotas;
|
|
21
|
+
/**
|
|
22
|
+
* Debt principal
|
|
23
|
+
*/
|
|
24
|
+
debt;
|
|
25
|
+
/**
|
|
26
|
+
* Total debt: principal + accrued interest + accrued fees.
|
|
27
|
+
* `decreaseDebt` amounts operate on total debt (interest and fees are
|
|
28
|
+
* repaid before principal), so principal alone is not enough to resolve
|
|
29
|
+
* either the underlying balance decrement or the final principal.
|
|
30
|
+
*/
|
|
31
|
+
totalDebt;
|
|
32
|
+
constructor(props) {
|
|
33
|
+
this.creditAccount = props.creditAccount;
|
|
34
|
+
this.creditManager = props.creditManager;
|
|
35
|
+
this.underlying = props.underlying;
|
|
36
|
+
this.balances = props.balances ?? new AssetsMap();
|
|
37
|
+
this.quotas = props.quotas ?? new AssetsMap();
|
|
38
|
+
this.debt = props.debt ?? 0n;
|
|
39
|
+
this.totalDebt = props.totalDebt ?? 0n;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The account-opening seed: everything zero/empty. The account doesn't
|
|
43
|
+
* exist yet at preview time, so `creditAccount` is the zero address;
|
|
44
|
+
* opening previews take identity from the parsed operation instead.
|
|
45
|
+
*/
|
|
46
|
+
static beforeOpen(creditManager, underlying) {
|
|
47
|
+
return new CreditAccountState({
|
|
48
|
+
creditAccount: zeroAddress,
|
|
49
|
+
creditManager,
|
|
50
|
+
underlying
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Pre-state of an existing account from its on-chain data, with dust
|
|
55
|
+
* balances and quotas (≤ 1 wei) filtered out.
|
|
56
|
+
*/
|
|
57
|
+
static fromCreditAccountData(ca) {
|
|
58
|
+
const balances = new AssetsMap();
|
|
59
|
+
const quotas = new AssetsMap();
|
|
60
|
+
for (const t of ca.tokens) {
|
|
61
|
+
if (t.balance > 1n) {
|
|
62
|
+
balances.upsert(t.token, t.balance);
|
|
63
|
+
}
|
|
64
|
+
if (t.quota > 1n) {
|
|
65
|
+
quotas.upsert(t.token, t.quota);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return new CreditAccountState({
|
|
69
|
+
creditAccount: ca.creditAccount,
|
|
70
|
+
creditManager: ca.creditManager,
|
|
71
|
+
underlying: ca.underlying,
|
|
72
|
+
balances,
|
|
73
|
+
quotas,
|
|
74
|
+
debt: ca.debt,
|
|
75
|
+
totalDebt: ca.debt + ca.accruedInterest + ca.accruedFees
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
clone() {
|
|
79
|
+
return new CreditAccountState({
|
|
80
|
+
creditAccount: this.creditAccount,
|
|
81
|
+
creditManager: this.creditManager,
|
|
82
|
+
underlying: this.underlying,
|
|
83
|
+
balances: this.balances.clone(),
|
|
84
|
+
quotas: this.quotas.clone(),
|
|
85
|
+
debt: this.debt,
|
|
86
|
+
totalDebt: this.totalDebt
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Borrows `amount` of underlying: debt, total debt and the underlying
|
|
91
|
+
* balance all increase by it.
|
|
92
|
+
*/
|
|
93
|
+
increaseDebt(amount) {
|
|
94
|
+
this.debt += amount;
|
|
95
|
+
this.totalDebt += amount;
|
|
96
|
+
this.balances.inc(this.underlying, amount);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Repays up to `amount` of underlying against the debt, clamped to the
|
|
100
|
+
* total debt (the facade never repays more than is owed). Interest and
|
|
101
|
+
* fees are repaid before principal (CreditLogic.calcDecrease), so
|
|
102
|
+
* principal only decreases once total debt drops below it. The underlying
|
|
103
|
+
* balance decreases by the repaid amount.
|
|
104
|
+
*
|
|
105
|
+
* @returns The actually repaid (post-clamp) amount.
|
|
106
|
+
*/
|
|
107
|
+
repay(amount) {
|
|
108
|
+
const repaid = amount > this.totalDebt ? this.totalDebt : amount;
|
|
109
|
+
this.balances.dec(this.underlying, repaid);
|
|
110
|
+
this.totalDebt -= repaid;
|
|
111
|
+
if (this.debt > this.totalDebt) {
|
|
112
|
+
this.debt = this.totalDebt;
|
|
113
|
+
}
|
|
114
|
+
return repaid;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Applies a relative `updateQuota` change to the token's quota:
|
|
118
|
+
* `MIN_INT96` is the facade "disable quota" sentinel and zeroes it, other
|
|
119
|
+
* changes are added with the result clamped at zero (quotas cannot go
|
|
120
|
+
* negative on-chain).
|
|
121
|
+
*/
|
|
122
|
+
updateQuota(token, change) {
|
|
123
|
+
if (change === MIN_INT96) {
|
|
124
|
+
this.quotas.upsert(token, 0n);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const next = this.quotas.getOrZero(token) + change;
|
|
128
|
+
this.quotas.upsert(token, next > 0n ? next : 0n);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
export {
|
|
132
|
+
CreditAccountState
|
|
133
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { isAddressEqual } from "viem";
|
|
2
|
+
import { AssetsMap } from "../../sdk/index.js";
|
|
3
|
+
import {
|
|
4
|
+
ERROR_UNPRICEABLE_TOKEN,
|
|
5
|
+
PREVIEW_DUST
|
|
6
|
+
} from "./types.js";
|
|
7
|
+
function buildDelayedPreview(state, before, detected, convert) {
|
|
8
|
+
const { request, intent } = detected;
|
|
9
|
+
const post = state.clone();
|
|
10
|
+
let error;
|
|
11
|
+
const safeConvert = (token, to, amount) => {
|
|
12
|
+
try {
|
|
13
|
+
return convert(token, to, amount);
|
|
14
|
+
} catch {
|
|
15
|
+
error ??= {
|
|
16
|
+
code: ERROR_UNPRICEABLE_TOKEN,
|
|
17
|
+
message: `cannot price token ${token}`
|
|
18
|
+
};
|
|
19
|
+
return 0n;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
const claimed = post.balances.getOrZero(request.phantomToken);
|
|
23
|
+
post.balances.upsert(request.phantomToken, 0n);
|
|
24
|
+
post.quotas.upsert(request.phantomToken, 0n);
|
|
25
|
+
post.balances.inc(request.claimToken, claimed);
|
|
26
|
+
if (intent?.type === "CLOSE_ACCOUNT") {
|
|
27
|
+
const totalValue2 = post.balances.sum(
|
|
28
|
+
(token, balance) => balance > 0n ? safeConvert(token, post.underlying, balance) : 0n
|
|
29
|
+
);
|
|
30
|
+
const receivedAmount = totalValue2 > post.totalDebt ? totalValue2 - post.totalDebt : 0n;
|
|
31
|
+
const preview2 = {
|
|
32
|
+
operation: "CloseCreditAccount",
|
|
33
|
+
permanent: true,
|
|
34
|
+
creditManager: post.creditManager,
|
|
35
|
+
creditAccount: post.creditAccount,
|
|
36
|
+
receivedAmount,
|
|
37
|
+
error
|
|
38
|
+
};
|
|
39
|
+
return preview2;
|
|
40
|
+
}
|
|
41
|
+
const collateralWithdrawn = new AssetsMap();
|
|
42
|
+
const repayFromClaim = (amount) => {
|
|
43
|
+
const available = post.balances.getOrZero(request.claimToken);
|
|
44
|
+
const spent = amount > available ? available : amount;
|
|
45
|
+
if (spent <= 0n) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const received = safeConvert(request.claimToken, post.underlying, spent);
|
|
49
|
+
post.balances.dec(request.claimToken, spent);
|
|
50
|
+
post.balances.inc(post.underlying, received);
|
|
51
|
+
post.repay(received);
|
|
52
|
+
};
|
|
53
|
+
if (intent?.type === "DECREASE_LEVERAGE") {
|
|
54
|
+
repayFromClaim(claimed);
|
|
55
|
+
} else if (intent?.type === "WITHDRAW_COLLATERAL") {
|
|
56
|
+
const running = post.balances.getOrZero(intent.withdrawToken);
|
|
57
|
+
const withdrawn = intent.withdrawAmount > running ? running : intent.withdrawAmount;
|
|
58
|
+
if (withdrawn > 0n) {
|
|
59
|
+
post.balances.dec(intent.withdrawToken, withdrawn);
|
|
60
|
+
collateralWithdrawn.upsert(intent.withdrawToken, withdrawn);
|
|
61
|
+
}
|
|
62
|
+
const remainingClaim = isAddressEqual(intent.withdrawToken, request.claimToken) && claimed > withdrawn ? claimed - withdrawn : claimed;
|
|
63
|
+
repayFromClaim(remainingClaim);
|
|
64
|
+
}
|
|
65
|
+
const totalValue = post.balances.sum(
|
|
66
|
+
(token, balance) => balance > PREVIEW_DUST ? safeConvert(token, post.underlying, balance) : 0n
|
|
67
|
+
);
|
|
68
|
+
const preview = {
|
|
69
|
+
operation: "AdjustCreditAccount",
|
|
70
|
+
creditManager: post.creditManager,
|
|
71
|
+
creditAccount: post.creditAccount,
|
|
72
|
+
// the resume flow adds nothing from the wallet
|
|
73
|
+
collateralAdded: [],
|
|
74
|
+
collateralWithdrawn: collateralWithdrawn.toAssets(),
|
|
75
|
+
totalValue,
|
|
76
|
+
debt: post.debt,
|
|
77
|
+
// relative to the pre-transaction state: where the account will end up
|
|
78
|
+
// compared to now, once the withdrawal is claimed and the intent resumed
|
|
79
|
+
debtChange: post.debt - before.debt,
|
|
80
|
+
quotas: post.quotas.toAssets(0n),
|
|
81
|
+
quotasChange: post.quotas.difference(before.quotas).toAssets(),
|
|
82
|
+
assets: post.balances.toAssets(PREVIEW_DUST),
|
|
83
|
+
assetsChange: post.balances.difference(before.balances).toAssets(PREVIEW_DUST),
|
|
84
|
+
error
|
|
85
|
+
};
|
|
86
|
+
return preview;
|
|
87
|
+
}
|
|
88
|
+
export {
|
|
89
|
+
buildDelayedPreview
|
|
90
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { isAddressEqual } from "viem";
|
|
2
|
+
import {
|
|
3
|
+
AbstractAdapterContract
|
|
4
|
+
} from "../../plugins/adapters/index.js";
|
|
5
|
+
import {
|
|
6
|
+
decodeDelayedIntent
|
|
7
|
+
} from "../../sdk/index.js";
|
|
8
|
+
import { InvalidDelayedIntentError } from "./errors.js";
|
|
9
|
+
function detectDelayedOperation(sdk, multicall) {
|
|
10
|
+
let bracketDeltas = [];
|
|
11
|
+
for (const op of multicall) {
|
|
12
|
+
if (op.operation === "StoreExpectedBalances") {
|
|
13
|
+
bracketDeltas = op.deltas;
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
if (op.operation === "CompareBalances") {
|
|
17
|
+
bracketDeltas = [];
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
if (op.operation !== "Execute") {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
const adapter = sdk.getContract(op.adapter);
|
|
24
|
+
if (!(adapter instanceof AbstractAdapterContract)) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
const request = adapter.parseDelayedWithdrawalRequest(op.calldata);
|
|
28
|
+
if (!request) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const mintsPhantom = bracketDeltas.some(
|
|
32
|
+
({ token, balance }) => balance > 0n && isAddressEqual(token, request.phantomToken)
|
|
33
|
+
);
|
|
34
|
+
if (!mintsPhantom) {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
let intent;
|
|
38
|
+
if (request.extraData && request.extraData !== "0x") {
|
|
39
|
+
try {
|
|
40
|
+
intent = decodeDelayedIntent(request.extraData);
|
|
41
|
+
} catch (e) {
|
|
42
|
+
throw new InvalidDelayedIntentError(op.adapter, request.extraData, e);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return { request, intent };
|
|
46
|
+
}
|
|
47
|
+
return void 0;
|
|
48
|
+
}
|
|
49
|
+
export {
|
|
50
|
+
detectDelayedOperation
|
|
51
|
+
};
|
|
@@ -7,6 +7,22 @@ class UnsupportedOperationError extends Error {
|
|
|
7
7
|
this.operation = operation;
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
|
+
class InvalidDelayedIntentError extends Error {
|
|
11
|
+
/** Adapter the withdrawal request was addressed to. */
|
|
12
|
+
adapter;
|
|
13
|
+
/** Raw `extraData` that failed to decode. */
|
|
14
|
+
extraData;
|
|
15
|
+
constructor(adapter, extraData, cause) {
|
|
16
|
+
super(
|
|
17
|
+
`cannot decode delayed intent from extraData ${extraData} of withdrawal request to adapter ${adapter}`,
|
|
18
|
+
{ cause }
|
|
19
|
+
);
|
|
20
|
+
this.name = "InvalidDelayedIntentError";
|
|
21
|
+
this.adapter = adapter;
|
|
22
|
+
this.extraData = extraData;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
10
25
|
export {
|
|
26
|
+
InvalidDelayedIntentError,
|
|
11
27
|
UnsupportedOperationError
|
|
12
28
|
};
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
export * from "./
|
|
2
|
-
export * from "./
|
|
1
|
+
export * from "./buildDelayedPreview.js";
|
|
2
|
+
export * from "./CreditAccountState.js";
|
|
3
3
|
export * from "./detectCloseOrRepay.js";
|
|
4
|
+
export * from "./detectDelayedOperation.js";
|
|
4
5
|
export * from "./errors.js";
|
|
5
6
|
export * from "./previewAdjustCreditAccount.js";
|
|
6
7
|
export * from "./previewCloseOrRepayCreditAccount.js";
|
|
7
8
|
export * from "./previewOperation.js";
|
|
9
|
+
export * from "./replayInnerOperations.js";
|
|
10
|
+
export * from "./replayMulticall.js";
|
|
8
11
|
export * from "./types.js";
|
|
@@ -1,15 +1,8 @@
|
|
|
1
|
+
import { AP_WETH_TOKEN, NO_VERSION } from "../../sdk/index.js";
|
|
2
|
+
import { replayMulticall } from "./replayMulticall.js";
|
|
1
3
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
NO_VERSION
|
|
5
|
-
} from "../../sdk/index.js";
|
|
6
|
-
import {
|
|
7
|
-
applyInnerOperations,
|
|
8
|
-
makeInnerOperationsState
|
|
9
|
-
} from "./applyInnerOperations.js";
|
|
10
|
-
import { applyQuotaChanges } from "./applyQuotaChanges.js";
|
|
11
|
-
import {
|
|
12
|
-
ERROR_UNPRICEABLE_TOKEN
|
|
4
|
+
ERROR_UNPRICEABLE_TOKEN,
|
|
5
|
+
PREVIEW_DUST
|
|
13
6
|
} from "./types.js";
|
|
14
7
|
import { unwrapNativeCollateral } from "./unwrapNativeCollateral.js";
|
|
15
8
|
async function previewAdjustCreditAccount(input, operation, options) {
|
|
@@ -17,43 +10,21 @@ async function previewAdjustCreditAccount(input, operation, options) {
|
|
|
17
10
|
const market = sdk.marketRegister.findByCreditManager(
|
|
18
11
|
operation.creditManager
|
|
19
12
|
);
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (!ca) {
|
|
28
|
-
throw new Error(`credit account ${operation.creditAccount} not found`);
|
|
29
|
-
}
|
|
30
|
-
const initialBalances = new AssetsMap();
|
|
31
|
-
const initialQuotas = new AssetsMap();
|
|
32
|
-
for (const t of ca.tokens) {
|
|
33
|
-
if (t.balance > 1n) {
|
|
34
|
-
initialBalances.upsert(t.token, t.balance);
|
|
35
|
-
}
|
|
36
|
-
if (t.quota > 1n) {
|
|
37
|
-
initialQuotas.upsert(t.token, t.quota);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
const state = makeInnerOperationsState();
|
|
41
|
-
state.balances = initialBalances.clone();
|
|
42
|
-
state.debt = ca.debt;
|
|
43
|
-
state.totalDebt = ca.debt + ca.accruedInterest + ca.accruedFees;
|
|
44
|
-
let error = await applyInnerOperations(sdk, operation.multicall, state);
|
|
13
|
+
const {
|
|
14
|
+
before,
|
|
15
|
+
after,
|
|
16
|
+
error: replayError
|
|
17
|
+
} = await replayMulticall(sdk, operation, options);
|
|
18
|
+
const account = after.account;
|
|
19
|
+
let error = replayError;
|
|
45
20
|
const { assets: collateralAdded, error: unwrapError } = unwrapNativeCollateral(
|
|
46
|
-
|
|
21
|
+
after.collateralAdded.toAssets(),
|
|
47
22
|
value,
|
|
48
23
|
sdk.addressProvider.getAddress(AP_WETH_TOKEN, NO_VERSION)
|
|
49
24
|
);
|
|
50
25
|
error ??= unwrapError;
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
state.quotaChanges
|
|
54
|
-
);
|
|
55
|
-
const assets = state.balances.toAssets(1n);
|
|
56
|
-
const assetsChange = state.balances.difference(initialBalances).toAssets(1n);
|
|
26
|
+
const assets = account.balances.toAssets(PREVIEW_DUST);
|
|
27
|
+
const assetsChange = account.balances.difference(before.balances).toAssets(PREVIEW_DUST);
|
|
57
28
|
const totalValue = assets.reduce((acc, { token, balance }) => {
|
|
58
29
|
try {
|
|
59
30
|
return acc + market.priceOracle.convert(token, market.underlying, balance);
|
|
@@ -70,12 +41,12 @@ async function previewAdjustCreditAccount(input, operation, options) {
|
|
|
70
41
|
creditManager: operation.creditManager,
|
|
71
42
|
creditAccount: operation.creditAccount,
|
|
72
43
|
collateralAdded,
|
|
73
|
-
collateralWithdrawn:
|
|
44
|
+
collateralWithdrawn: after.collateralWithdrawn.toAssets(),
|
|
74
45
|
totalValue,
|
|
75
|
-
debt:
|
|
76
|
-
debtChange:
|
|
77
|
-
quotas,
|
|
78
|
-
quotasChange,
|
|
46
|
+
debt: account.debt,
|
|
47
|
+
debtChange: account.debt - before.debt,
|
|
48
|
+
quotas: account.quotas.toAssets(0n),
|
|
49
|
+
quotasChange: account.quotas.difference(before.quotas).toAssets(),
|
|
79
50
|
assets,
|
|
80
51
|
assetsChange,
|
|
81
52
|
error
|