@mento-protocol/config 0.1.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/aggregators.json +120 -0
- package/chain-metadata.json +22 -0
- package/deployment-namespaces.json +6 -0
- package/dist/aggregators.d.ts +30 -0
- package/dist/aggregators.d.ts.map +1 -0
- package/dist/aggregators.js +88 -0
- package/dist/chains.d.ts +7 -0
- package/dist/chains.d.ts.map +1 -0
- package/dist/chains.js +29 -0
- package/dist/deployment-namespaces.d.ts +7 -0
- package/dist/deployment-namespaces.d.ts.map +1 -0
- package/dist/deployment-namespaces.js +12 -0
- package/dist/erc20-abi.d.ts +25 -0
- package/dist/erc20-abi.d.ts.map +1 -0
- package/dist/erc20-abi.js +32 -0
- package/dist/format.d.ts +3 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +12 -0
- package/dist/fx-calendar.d.ts +22 -0
- package/dist/fx-calendar.d.ts.map +1 -0
- package/dist/fx-calendar.js +27 -0
- package/dist/oracle-reporters.d.ts +13 -0
- package/dist/oracle-reporters.d.ts.map +1 -0
- package/dist/oracle-reporters.js +65 -0
- package/dist/protocol-fee.d.ts +2 -0
- package/dist/protocol-fee.d.ts.map +1 -0
- package/dist/protocol-fee.js +13 -0
- package/dist/rebalance-abi.d.ts +182 -0
- package/dist/rebalance-abi.d.ts.map +1 -0
- package/dist/rebalance-abi.js +188 -0
- package/dist/rebalance-enrichment.d.ts +137 -0
- package/dist/rebalance-enrichment.d.ts.map +1 -0
- package/dist/rebalance-enrichment.js +96 -0
- package/dist/thresholds.d.ts +25 -0
- package/dist/thresholds.d.ts.map +1 -0
- package/dist/thresholds.js +33 -0
- package/dist/tokens.d.ts +15 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +107 -0
- package/dist/units.d.ts +16 -0
- package/dist/units.d.ts.map +1 -0
- package/dist/units.js +25 -0
- package/fx-calendar.json +7 -0
- package/oracle-reporters.json +123 -0
- package/package.json +109 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for the FPMM liquidity-strategy ABI subset and
|
|
3
|
+
* Solidity-error → human-readable message map used by the rebalance probe.
|
|
4
|
+
*
|
|
5
|
+
* Two consumers today:
|
|
6
|
+
* - `ui-dashboard/src/lib/rebalance-check.ts` (pool-detail tooltip + KPI strip)
|
|
7
|
+
* - `metrics-bridge/src/rebalance-check.ts` (Slack alert annotation)
|
|
8
|
+
*
|
|
9
|
+
* Drift between the two would mean the dashboard explains a revert that the
|
|
10
|
+
* Slack alert reports as `unknown` (or vice versa). Keeping the data here
|
|
11
|
+
* forces both probes to see exactly the same vocabulary.
|
|
12
|
+
*
|
|
13
|
+
* Implementation notes:
|
|
14
|
+
* - `STRATEGY_ABI_SOURCES` is a plain string[] of human-readable ABI fragments.
|
|
15
|
+
* Consumers feed it into viem's `parseAbi(...)` to materialise the typed
|
|
16
|
+
* ABI — that keeps the viem dependency out of `shared-config/`. The
|
|
17
|
+
* `as const` annotation preserves literal types so `parseAbi` can produce
|
|
18
|
+
* a strongly-typed ABI on the consumer side.
|
|
19
|
+
* - `ERROR_MESSAGES` is a flat lookup map. The `decodeBlockedRevert` glue
|
|
20
|
+
* stays in each consumer because it carries probe-specific concerns
|
|
21
|
+
* (the dashboard adds enrichment fetches for `CDPLS_*_BALANCE_TOO_LOW` /
|
|
22
|
+
* `RLS_RESERVE_OUT_OF_COLLATERAL`; the bridge collapses healthy no-ops
|
|
23
|
+
* to `ok` and routes the unbounded payload to the diagnostic log channel
|
|
24
|
+
* for cardinality + Slack-injection-safety reasons).
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* Strategy ABI as a const-string array — feed this into viem's `parseAbi(...)`
|
|
28
|
+
* on the consumer side. The list covers every error code emitted by the
|
|
29
|
+
* FPMM rebalance pipeline plus the detection-only getters
|
|
30
|
+
* (`getCDPConfig`/`reserve`/`getPools`) needed to identify the strategy
|
|
31
|
+
* type before simulating.
|
|
32
|
+
*
|
|
33
|
+
* Drift across the two consumers (dashboard tooltip + Slack alert annotation)
|
|
34
|
+
* would mean a revert decoded by one probe falls through to `unknown` on
|
|
35
|
+
* the other. Add new errors / detection getters here, never inline.
|
|
36
|
+
*/
|
|
37
|
+
export declare const STRATEGY_ABI_SOURCES: readonly ["function rebalance(address pool) external", "function determineAction(address pool) external view returns ((address pool, uint256 reserveNumerator, uint256 reserveDenominator, uint256 oraclePriceNumerator, uint256 oraclePriceDenominator, bool reservePriceAboveOraclePrice, uint16 rebalanceThreshold, address token0, address token1, uint8 decimals0, uint8 decimals1, bool isToken0Debt, uint64 liquiditySourceIncentiveExpansion, uint64 protocolIncentiveExpansion, uint64 liquiditySourceIncentiveContraction, uint64 protocolIncentiveContraction) ctx, (uint8 direction, uint256 amount0Out, uint256 amount1Out, uint256 amountOwedToPool) action)", "function poolConfigs(address pool) external view returns (bool isToken0Debt, uint32 lastRebalance, uint32 rebalanceCooldown, address protocolFeeRecipient, uint64 liquiditySourceIncentiveExpansion, uint64 protocolIncentiveExpansion, uint64 liquiditySourceIncentiveContraction, uint64 protocolIncentiveContraction)", "function getCDPConfig(address pool) external view returns ((address stabilityPool, address collateralRegistry, uint256 stabilityPoolPercentage, uint256 maxIterations))", "function reserve() external view returns (address)", "function getPools() external view returns (address[])", "error LS_BAD_INCENTIVE()", "error LS_CAN_ONLY_REBALANCE_ONCE(address pool)", "error LS_COOLDOWN_ACTIVE()", "error LS_DEBT_TOKEN_NOT_IN_POOL()", "error LS_HOOK_NOT_CALLED()", "error LS_INCENTIVE_TOO_HIGH()", "error LS_INVALID_DECIMAL()", "error LS_INVALID_OWNER()", "error LS_INVALID_PRICES()", "error LS_INVALID_SENDER()", "error LS_INVALID_THRESHOLD()", "error LS_POOL_ALREADY_EXISTS()", "error LS_POOL_MUST_BE_SET()", "error LS_POOL_NOT_FOUND()", "error LS_POOL_NOT_REBALANCEABLE()", "error LS_PROTOCOL_FEE_RECIPIENT_REQUIRED()", "error LS_STRATEGY_EXECUTION_FAILED()", "error LS_ZERO_DECIMAL()", "error CDPLS_COLLATERAL_REGISTRY_IS_ZERO()", "error CDPLS_INVALID_STABILITY_POOL_PERCENTAGE()", "error CDPLS_OUT_OF_FUNDS_FOR_REDEMPTION_SUBSIDY()", "error CDPLS_REDEMPTION_FEE_TOO_LARGE()", "error CDPLS_REDEMPTION_SHORTFALL_TOO_LARGE(uint256 shortfall)", "error CDPLS_STABILITY_POOL_BALANCE_TOO_LOW()", "error CDPLS_STABILITY_POOL_IS_ZERO()", "error RLS_COLLATERAL_TO_POOL_FAILED()", "error RLS_INVALID_RESERVE()", "error RLS_RESERVE_OUT_OF_COLLATERAL()", "error RLS_TOKEN_IN_NOT_SUPPORTED()", "error RLS_TOKEN_OUT_NOT_SUPPORTED()", "error OLS_OUT_OF_COLLATERAL()", "error OLS_OUT_OF_DEBT()", "error NotLiquidityStrategy()", "error PriceDifferenceTooSmall()", "error PriceDifferenceNotImproved()", "error PriceDifferenceMovedInWrongDirection()", "error PriceDifferenceMovedTooFarFromThresholds()", "error RebalanceDirectionInvalid()", "error RebalanceThresholdTooHigh()", "error RebalanceIncentiveTooHigh()", "error ReferenceRateNotSet()", "error ReserveValueDecreased()", "error ReservesEmpty()"];
|
|
38
|
+
/**
|
|
39
|
+
* Solidity error name → human-readable explanation. Slack alert annotations
|
|
40
|
+
* and dashboard tooltips share the same vocabulary so operators see the
|
|
41
|
+
* same wording across surfaces.
|
|
42
|
+
*
|
|
43
|
+
* Bounded enum (~30 codes). Both consumers' `decodeBlockedRevert` glue
|
|
44
|
+
* MUST keep `reason_message` inside this map (built-in `Error(string)` /
|
|
45
|
+
* `Panic(uint256)` reverts collapse to fixed strings — see
|
|
46
|
+
* `metrics-bridge/src/rebalance-check.ts`) so the Prometheus label
|
|
47
|
+
* cardinality stays bounded and a non-canonical strategy can't inject
|
|
48
|
+
* Slack mrkdwn through the alert body.
|
|
49
|
+
*
|
|
50
|
+
* INVARIANT — entries MUST be bare phrases (no trailing punctuation): the
|
|
51
|
+
* Slack alert template at `alerts/rules/main.tf` appends a "." when
|
|
52
|
+
* rendering ("Rebalance Blocked: <reason_message>."). Trailing "." / "!" /
|
|
53
|
+
* "—" here would render as double punctuation. The dashboard tooltip
|
|
54
|
+
* (`ui-dashboard/src/components/pool-header/rebalance-status-value.tsx`)
|
|
55
|
+
* concatenates with " — " so it's also happiest with bare phrases.
|
|
56
|
+
*/
|
|
57
|
+
export declare const ERROR_MESSAGES: {
|
|
58
|
+
readonly CDPLS_STABILITY_POOL_BALANCE_TOO_LOW: "Stability pool has insufficient liquidity";
|
|
59
|
+
readonly CDPLS_STABILITY_POOL_IS_ZERO: "No stability pool configured for this strategy";
|
|
60
|
+
readonly CDPLS_COLLATERAL_REGISTRY_IS_ZERO: "Collateral registry not configured";
|
|
61
|
+
readonly CDPLS_OUT_OF_FUNDS_FOR_REDEMPTION_SUBSIDY: "Insufficient funds to cover redemption subsidy";
|
|
62
|
+
readonly CDPLS_REDEMPTION_FEE_TOO_LARGE: "Redemption fee exceeds tolerance";
|
|
63
|
+
readonly CDPLS_REDEMPTION_SHORTFALL_TOO_LARGE: "Redemption shortfall exceeds tolerance";
|
|
64
|
+
readonly CDPLS_INVALID_STABILITY_POOL_PERCENTAGE: "Invalid stability pool percentage configuration";
|
|
65
|
+
readonly RLS_RESERVE_OUT_OF_COLLATERAL: "Reserve has insufficient collateral";
|
|
66
|
+
readonly RLS_INVALID_RESERVE: "Reserve contract not configured";
|
|
67
|
+
readonly RLS_COLLATERAL_TO_POOL_FAILED: "Collateral transfer to pool failed";
|
|
68
|
+
readonly RLS_TOKEN_IN_NOT_SUPPORTED: "Collateral token not supported by reserve";
|
|
69
|
+
readonly RLS_TOKEN_OUT_NOT_SUPPORTED: "Debt token not supported by reserve";
|
|
70
|
+
readonly OLS_OUT_OF_COLLATERAL: "Strategy has no collateral liquidity available";
|
|
71
|
+
readonly OLS_OUT_OF_DEBT: "Strategy has no debt liquidity available";
|
|
72
|
+
readonly LS_COOLDOWN_ACTIVE: "Rebalance cooldown is active — retry shortly";
|
|
73
|
+
readonly LS_POOL_NOT_REBALANCEABLE: "Pool deviation is below the rebalance threshold";
|
|
74
|
+
readonly LS_INVALID_PRICES: "Oracle price data is invalid or stale";
|
|
75
|
+
readonly LS_CAN_ONLY_REBALANCE_ONCE: "Pool was already rebalanced this block";
|
|
76
|
+
readonly LS_POOL_NOT_FOUND: "Pool is not registered with this strategy";
|
|
77
|
+
readonly LS_STRATEGY_EXECUTION_FAILED: "Strategy execution failed internally";
|
|
78
|
+
readonly LS_HOOK_NOT_CALLED: "Pool hook was not invoked — possible contract misconfiguration";
|
|
79
|
+
readonly LS_BAD_INCENTIVE: "Incentive configuration is invalid";
|
|
80
|
+
readonly LS_INCENTIVE_TOO_HIGH: "Rebalance incentive exceeds allowed maximum";
|
|
81
|
+
readonly LS_DEBT_TOKEN_NOT_IN_POOL: "Debt token not found in pool pair";
|
|
82
|
+
readonly LS_INVALID_DECIMAL: "Token decimal configuration is invalid";
|
|
83
|
+
readonly LS_INVALID_OWNER: "Unauthorized — only owner can call this";
|
|
84
|
+
readonly LS_INVALID_SENDER: "Unauthorized caller";
|
|
85
|
+
readonly LS_INVALID_THRESHOLD: "Rebalance threshold is out of valid range";
|
|
86
|
+
readonly LS_POOL_ALREADY_EXISTS: "Pool is already registered";
|
|
87
|
+
readonly LS_POOL_MUST_BE_SET: "Pool address cannot be zero";
|
|
88
|
+
readonly LS_PROTOCOL_FEE_RECIPIENT_REQUIRED: "Protocol fee recipient must be set";
|
|
89
|
+
readonly LS_ZERO_DECIMAL: "Token has zero decimals";
|
|
90
|
+
readonly NotLiquidityStrategy: "Caller is not a registered liquidity strategy";
|
|
91
|
+
readonly PriceDifferenceTooSmall: "Pool deviation is below the rebalance threshold";
|
|
92
|
+
readonly PriceDifferenceNotImproved: "Rebalance did not improve the price deviation";
|
|
93
|
+
readonly PriceDifferenceMovedInWrongDirection: "Rebalance moved the price in the wrong direction";
|
|
94
|
+
readonly PriceDifferenceMovedTooFarFromThresholds: "Rebalance moved the price too far past the threshold";
|
|
95
|
+
readonly RebalanceDirectionInvalid: "Invalid rebalance direction";
|
|
96
|
+
readonly RebalanceThresholdTooHigh: "Rebalance threshold exceeds allowed maximum";
|
|
97
|
+
readonly RebalanceIncentiveTooHigh: "Rebalance incentive exceeds allowed maximum";
|
|
98
|
+
readonly ReferenceRateNotSet: "Oracle reference rate is not configured";
|
|
99
|
+
readonly ReserveValueDecreased: "Pool reserve value decreased after rebalance";
|
|
100
|
+
readonly ReservesEmpty: "Pool reserves are empty";
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* Canonical Solidity-error names — the keys of `ERROR_MESSAGES` exposed as a
|
|
104
|
+
* string-literal union. Use `ReasonCode` whenever you compare against a code
|
|
105
|
+
* (e.g. `code === REASON_CODES.RLS_RESERVE_OUT_OF_COLLATERAL`); a typo on a
|
|
106
|
+
* string literal silently disables the comparison, which would in turn skip
|
|
107
|
+
* enrichment fetches and ship a degraded alert annotation. The `as const`
|
|
108
|
+
* map + `satisfies` annotation force compile-time validation.
|
|
109
|
+
*/
|
|
110
|
+
export type ReasonCode = keyof typeof ERROR_MESSAGES;
|
|
111
|
+
/**
|
|
112
|
+
* Built-in Solidity revert kinds — `Error(string)` and `Panic(uint256)` —
|
|
113
|
+
* plus a catch-all `unknown` for unrecognised payloads. Both consumers
|
|
114
|
+
* (`metrics-bridge` probe + `ui-dashboard` probe) collapse these to a fixed
|
|
115
|
+
* `reasonMessage` to keep the Prometheus label space bounded and the Slack
|
|
116
|
+
* alert body free of strategy-supplied mrkdwn.
|
|
117
|
+
*
|
|
118
|
+
* `SyntheticReasonCode | ReasonCode` is the full set of values that can land
|
|
119
|
+
* on a `RebalanceProbeBlocked.reasonCode` — the discriminated union the
|
|
120
|
+
* dashboard tooltip + Slack annotation render against.
|
|
121
|
+
*/
|
|
122
|
+
export type SyntheticReasonCode = "Error" | "Panic" | "unknown";
|
|
123
|
+
/**
|
|
124
|
+
* Const-object mirror of `ReasonCode` so consumers can write
|
|
125
|
+
* `REASON_CODES.RLS_RESERVE_OUT_OF_COLLATERAL` instead of a bare string
|
|
126
|
+
* literal. Catches typos at the call site (the bare-string form would
|
|
127
|
+
* silently disable enrichment if mistyped).
|
|
128
|
+
*/
|
|
129
|
+
export declare const REASON_CODES: Readonly<{
|
|
130
|
+
CDPLS_STABILITY_POOL_BALANCE_TOO_LOW: "CDPLS_STABILITY_POOL_BALANCE_TOO_LOW";
|
|
131
|
+
CDPLS_STABILITY_POOL_IS_ZERO: "CDPLS_STABILITY_POOL_IS_ZERO";
|
|
132
|
+
CDPLS_COLLATERAL_REGISTRY_IS_ZERO: "CDPLS_COLLATERAL_REGISTRY_IS_ZERO";
|
|
133
|
+
CDPLS_OUT_OF_FUNDS_FOR_REDEMPTION_SUBSIDY: "CDPLS_OUT_OF_FUNDS_FOR_REDEMPTION_SUBSIDY";
|
|
134
|
+
CDPLS_REDEMPTION_FEE_TOO_LARGE: "CDPLS_REDEMPTION_FEE_TOO_LARGE";
|
|
135
|
+
CDPLS_REDEMPTION_SHORTFALL_TOO_LARGE: "CDPLS_REDEMPTION_SHORTFALL_TOO_LARGE";
|
|
136
|
+
CDPLS_INVALID_STABILITY_POOL_PERCENTAGE: "CDPLS_INVALID_STABILITY_POOL_PERCENTAGE";
|
|
137
|
+
RLS_RESERVE_OUT_OF_COLLATERAL: "RLS_RESERVE_OUT_OF_COLLATERAL";
|
|
138
|
+
RLS_INVALID_RESERVE: "RLS_INVALID_RESERVE";
|
|
139
|
+
RLS_COLLATERAL_TO_POOL_FAILED: "RLS_COLLATERAL_TO_POOL_FAILED";
|
|
140
|
+
RLS_TOKEN_IN_NOT_SUPPORTED: "RLS_TOKEN_IN_NOT_SUPPORTED";
|
|
141
|
+
RLS_TOKEN_OUT_NOT_SUPPORTED: "RLS_TOKEN_OUT_NOT_SUPPORTED";
|
|
142
|
+
OLS_OUT_OF_COLLATERAL: "OLS_OUT_OF_COLLATERAL";
|
|
143
|
+
OLS_OUT_OF_DEBT: "OLS_OUT_OF_DEBT";
|
|
144
|
+
LS_COOLDOWN_ACTIVE: "LS_COOLDOWN_ACTIVE";
|
|
145
|
+
LS_POOL_NOT_REBALANCEABLE: "LS_POOL_NOT_REBALANCEABLE";
|
|
146
|
+
LS_INVALID_PRICES: "LS_INVALID_PRICES";
|
|
147
|
+
LS_CAN_ONLY_REBALANCE_ONCE: "LS_CAN_ONLY_REBALANCE_ONCE";
|
|
148
|
+
LS_POOL_NOT_FOUND: "LS_POOL_NOT_FOUND";
|
|
149
|
+
LS_STRATEGY_EXECUTION_FAILED: "LS_STRATEGY_EXECUTION_FAILED";
|
|
150
|
+
LS_HOOK_NOT_CALLED: "LS_HOOK_NOT_CALLED";
|
|
151
|
+
LS_BAD_INCENTIVE: "LS_BAD_INCENTIVE";
|
|
152
|
+
LS_INCENTIVE_TOO_HIGH: "LS_INCENTIVE_TOO_HIGH";
|
|
153
|
+
LS_DEBT_TOKEN_NOT_IN_POOL: "LS_DEBT_TOKEN_NOT_IN_POOL";
|
|
154
|
+
LS_INVALID_DECIMAL: "LS_INVALID_DECIMAL";
|
|
155
|
+
LS_INVALID_OWNER: "LS_INVALID_OWNER";
|
|
156
|
+
LS_INVALID_SENDER: "LS_INVALID_SENDER";
|
|
157
|
+
LS_INVALID_THRESHOLD: "LS_INVALID_THRESHOLD";
|
|
158
|
+
LS_POOL_ALREADY_EXISTS: "LS_POOL_ALREADY_EXISTS";
|
|
159
|
+
LS_POOL_MUST_BE_SET: "LS_POOL_MUST_BE_SET";
|
|
160
|
+
LS_PROTOCOL_FEE_RECIPIENT_REQUIRED: "LS_PROTOCOL_FEE_RECIPIENT_REQUIRED";
|
|
161
|
+
LS_ZERO_DECIMAL: "LS_ZERO_DECIMAL";
|
|
162
|
+
NotLiquidityStrategy: "NotLiquidityStrategy";
|
|
163
|
+
PriceDifferenceTooSmall: "PriceDifferenceTooSmall";
|
|
164
|
+
PriceDifferenceNotImproved: "PriceDifferenceNotImproved";
|
|
165
|
+
PriceDifferenceMovedInWrongDirection: "PriceDifferenceMovedInWrongDirection";
|
|
166
|
+
PriceDifferenceMovedTooFarFromThresholds: "PriceDifferenceMovedTooFarFromThresholds";
|
|
167
|
+
RebalanceDirectionInvalid: "RebalanceDirectionInvalid";
|
|
168
|
+
RebalanceThresholdTooHigh: "RebalanceThresholdTooHigh";
|
|
169
|
+
RebalanceIncentiveTooHigh: "RebalanceIncentiveTooHigh";
|
|
170
|
+
ReferenceRateNotSet: "ReferenceRateNotSet";
|
|
171
|
+
ReserveValueDecreased: "ReserveValueDecreased";
|
|
172
|
+
ReservesEmpty: "ReservesEmpty";
|
|
173
|
+
}>;
|
|
174
|
+
/**
|
|
175
|
+
* Revert codes where the strategy refuses to rebalance BECAUSE the pool is
|
|
176
|
+
* healthy — not because anything is wrong. Both consumers collapse these
|
|
177
|
+
* to a passive "no action needed" state so no red "blocked" line appears
|
|
178
|
+
* in the UI / alert annotation when the rebalancer is just waiting for
|
|
179
|
+
* the pool to drift further past threshold.
|
|
180
|
+
*/
|
|
181
|
+
export declare const HEALTHY_NO_OP_ERRORS: ReadonlySet<string>;
|
|
182
|
+
//# sourceMappingURL=rebalance-abi.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rebalance-abi.d.ts","sourceRoot":"","sources":["../src/rebalance-abi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,oBAAoB,myFAgEvB,CAAC;AAEX;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyDgB,CAAC;AAE5C;;;;;;;GAOG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,cAAc,CAAC;AAErD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,OAAO,GAAG,SAAS,CAAC;AAEhE;;;;;GAKG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAIxB,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,EAAE,WAAW,CAAC,MAAM,CAGnD,CAAC"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for the FPMM liquidity-strategy ABI subset and
|
|
3
|
+
* Solidity-error → human-readable message map used by the rebalance probe.
|
|
4
|
+
*
|
|
5
|
+
* Two consumers today:
|
|
6
|
+
* - `ui-dashboard/src/lib/rebalance-check.ts` (pool-detail tooltip + KPI strip)
|
|
7
|
+
* - `metrics-bridge/src/rebalance-check.ts` (Slack alert annotation)
|
|
8
|
+
*
|
|
9
|
+
* Drift between the two would mean the dashboard explains a revert that the
|
|
10
|
+
* Slack alert reports as `unknown` (or vice versa). Keeping the data here
|
|
11
|
+
* forces both probes to see exactly the same vocabulary.
|
|
12
|
+
*
|
|
13
|
+
* Implementation notes:
|
|
14
|
+
* - `STRATEGY_ABI_SOURCES` is a plain string[] of human-readable ABI fragments.
|
|
15
|
+
* Consumers feed it into viem's `parseAbi(...)` to materialise the typed
|
|
16
|
+
* ABI — that keeps the viem dependency out of `shared-config/`. The
|
|
17
|
+
* `as const` annotation preserves literal types so `parseAbi` can produce
|
|
18
|
+
* a strongly-typed ABI on the consumer side.
|
|
19
|
+
* - `ERROR_MESSAGES` is a flat lookup map. The `decodeBlockedRevert` glue
|
|
20
|
+
* stays in each consumer because it carries probe-specific concerns
|
|
21
|
+
* (the dashboard adds enrichment fetches for `CDPLS_*_BALANCE_TOO_LOW` /
|
|
22
|
+
* `RLS_RESERVE_OUT_OF_COLLATERAL`; the bridge collapses healthy no-ops
|
|
23
|
+
* to `ok` and routes the unbounded payload to the diagnostic log channel
|
|
24
|
+
* for cardinality + Slack-injection-safety reasons).
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* Strategy ABI as a const-string array — feed this into viem's `parseAbi(...)`
|
|
28
|
+
* on the consumer side. The list covers every error code emitted by the
|
|
29
|
+
* FPMM rebalance pipeline plus the detection-only getters
|
|
30
|
+
* (`getCDPConfig`/`reserve`/`getPools`) needed to identify the strategy
|
|
31
|
+
* type before simulating.
|
|
32
|
+
*
|
|
33
|
+
* Drift across the two consumers (dashboard tooltip + Slack alert annotation)
|
|
34
|
+
* would mean a revert decoded by one probe falls through to `unknown` on
|
|
35
|
+
* the other. Add new errors / detection getters here, never inline.
|
|
36
|
+
*/
|
|
37
|
+
export const STRATEGY_ABI_SOURCES = [
|
|
38
|
+
// Shared
|
|
39
|
+
"function rebalance(address pool) external",
|
|
40
|
+
// OLS-specific probe: view-only, handles the zero-sender path explicitly.
|
|
41
|
+
// CDP/Reserve `rebalance` simulates fine from address(0) (tokens move
|
|
42
|
+
// strategy → pool), but OLS routes ERC20 transfers from `msg.sender`,
|
|
43
|
+
// so the address(0) probe always reverts inside ERC20 — meaningless.
|
|
44
|
+
"function determineAction(address pool) external view returns ((address pool, uint256 reserveNumerator, uint256 reserveDenominator, uint256 oraclePriceNumerator, uint256 oraclePriceDenominator, bool reservePriceAboveOraclePrice, uint16 rebalanceThreshold, address token0, address token1, uint8 decimals0, uint8 decimals1, bool isToken0Debt, uint64 liquiditySourceIncentiveExpansion, uint64 protocolIncentiveExpansion, uint64 liquiditySourceIncentiveContraction, uint64 protocolIncentiveContraction) ctx, (uint8 direction, uint256 amount0Out, uint256 amount1Out, uint256 amountOwedToPool) action)",
|
|
45
|
+
// Used by the dashboard's reserve-enrichment path (find the debt token).
|
|
46
|
+
// Bridge ignores it — dropping enrichment keeps the alert annotation
|
|
47
|
+
// bounded — but keeping it in the canonical ABI is harmless.
|
|
48
|
+
"function poolConfigs(address pool) external view returns (bool isToken0Debt, uint32 lastRebalance, uint32 rebalanceCooldown, address protocolFeeRecipient, uint64 liquiditySourceIncentiveExpansion, uint64 protocolIncentiveExpansion, uint64 liquiditySourceIncentiveContraction, uint64 protocolIncentiveContraction)",
|
|
49
|
+
// Strategy-type detection probes (zero-arg getters, cheap).
|
|
50
|
+
"function getCDPConfig(address pool) external view returns ((address stabilityPool, address collateralRegistry, uint256 stabilityPoolPercentage, uint256 maxIterations))",
|
|
51
|
+
"function reserve() external view returns (address)",
|
|
52
|
+
"function getPools() external view returns (address[])",
|
|
53
|
+
// Shared LS_*
|
|
54
|
+
"error LS_BAD_INCENTIVE()",
|
|
55
|
+
"error LS_CAN_ONLY_REBALANCE_ONCE(address pool)",
|
|
56
|
+
"error LS_COOLDOWN_ACTIVE()",
|
|
57
|
+
"error LS_DEBT_TOKEN_NOT_IN_POOL()",
|
|
58
|
+
"error LS_HOOK_NOT_CALLED()",
|
|
59
|
+
"error LS_INCENTIVE_TOO_HIGH()",
|
|
60
|
+
"error LS_INVALID_DECIMAL()",
|
|
61
|
+
"error LS_INVALID_OWNER()",
|
|
62
|
+
"error LS_INVALID_PRICES()",
|
|
63
|
+
"error LS_INVALID_SENDER()",
|
|
64
|
+
"error LS_INVALID_THRESHOLD()",
|
|
65
|
+
"error LS_POOL_ALREADY_EXISTS()",
|
|
66
|
+
"error LS_POOL_MUST_BE_SET()",
|
|
67
|
+
"error LS_POOL_NOT_FOUND()",
|
|
68
|
+
"error LS_POOL_NOT_REBALANCEABLE()",
|
|
69
|
+
"error LS_PROTOCOL_FEE_RECIPIENT_REQUIRED()",
|
|
70
|
+
"error LS_STRATEGY_EXECUTION_FAILED()",
|
|
71
|
+
"error LS_ZERO_DECIMAL()",
|
|
72
|
+
// CDP strategy
|
|
73
|
+
"error CDPLS_COLLATERAL_REGISTRY_IS_ZERO()",
|
|
74
|
+
"error CDPLS_INVALID_STABILITY_POOL_PERCENTAGE()",
|
|
75
|
+
"error CDPLS_OUT_OF_FUNDS_FOR_REDEMPTION_SUBSIDY()",
|
|
76
|
+
"error CDPLS_REDEMPTION_FEE_TOO_LARGE()",
|
|
77
|
+
"error CDPLS_REDEMPTION_SHORTFALL_TOO_LARGE(uint256 shortfall)",
|
|
78
|
+
"error CDPLS_STABILITY_POOL_BALANCE_TOO_LOW()",
|
|
79
|
+
"error CDPLS_STABILITY_POOL_IS_ZERO()",
|
|
80
|
+
// Reserve strategy
|
|
81
|
+
"error RLS_COLLATERAL_TO_POOL_FAILED()",
|
|
82
|
+
"error RLS_INVALID_RESERVE()",
|
|
83
|
+
"error RLS_RESERVE_OUT_OF_COLLATERAL()",
|
|
84
|
+
"error RLS_TOKEN_IN_NOT_SUPPORTED()",
|
|
85
|
+
"error RLS_TOKEN_OUT_NOT_SUPPORTED()",
|
|
86
|
+
// OLS strategy
|
|
87
|
+
"error OLS_OUT_OF_COLLATERAL()",
|
|
88
|
+
"error OLS_OUT_OF_DEBT()",
|
|
89
|
+
// FPMM pool-side errors (can bubble through strategy.rebalance → pool.rebalance)
|
|
90
|
+
"error NotLiquidityStrategy()",
|
|
91
|
+
"error PriceDifferenceTooSmall()",
|
|
92
|
+
"error PriceDifferenceNotImproved()",
|
|
93
|
+
"error PriceDifferenceMovedInWrongDirection()",
|
|
94
|
+
"error PriceDifferenceMovedTooFarFromThresholds()",
|
|
95
|
+
"error RebalanceDirectionInvalid()",
|
|
96
|
+
"error RebalanceThresholdTooHigh()",
|
|
97
|
+
"error RebalanceIncentiveTooHigh()",
|
|
98
|
+
"error ReferenceRateNotSet()",
|
|
99
|
+
"error ReserveValueDecreased()",
|
|
100
|
+
"error ReservesEmpty()",
|
|
101
|
+
];
|
|
102
|
+
/**
|
|
103
|
+
* Solidity error name → human-readable explanation. Slack alert annotations
|
|
104
|
+
* and dashboard tooltips share the same vocabulary so operators see the
|
|
105
|
+
* same wording across surfaces.
|
|
106
|
+
*
|
|
107
|
+
* Bounded enum (~30 codes). Both consumers' `decodeBlockedRevert` glue
|
|
108
|
+
* MUST keep `reason_message` inside this map (built-in `Error(string)` /
|
|
109
|
+
* `Panic(uint256)` reverts collapse to fixed strings — see
|
|
110
|
+
* `metrics-bridge/src/rebalance-check.ts`) so the Prometheus label
|
|
111
|
+
* cardinality stays bounded and a non-canonical strategy can't inject
|
|
112
|
+
* Slack mrkdwn through the alert body.
|
|
113
|
+
*
|
|
114
|
+
* INVARIANT — entries MUST be bare phrases (no trailing punctuation): the
|
|
115
|
+
* Slack alert template at `alerts/rules/main.tf` appends a "." when
|
|
116
|
+
* rendering ("Rebalance Blocked: <reason_message>."). Trailing "." / "!" /
|
|
117
|
+
* "—" here would render as double punctuation. The dashboard tooltip
|
|
118
|
+
* (`ui-dashboard/src/components/pool-header/rebalance-status-value.tsx`)
|
|
119
|
+
* concatenates with " — " so it's also happiest with bare phrases.
|
|
120
|
+
*/
|
|
121
|
+
export const ERROR_MESSAGES = {
|
|
122
|
+
// CDP strategy
|
|
123
|
+
CDPLS_STABILITY_POOL_BALANCE_TOO_LOW: "Stability pool has insufficient liquidity",
|
|
124
|
+
CDPLS_STABILITY_POOL_IS_ZERO: "No stability pool configured for this strategy",
|
|
125
|
+
CDPLS_COLLATERAL_REGISTRY_IS_ZERO: "Collateral registry not configured",
|
|
126
|
+
CDPLS_OUT_OF_FUNDS_FOR_REDEMPTION_SUBSIDY: "Insufficient funds to cover redemption subsidy",
|
|
127
|
+
CDPLS_REDEMPTION_FEE_TOO_LARGE: "Redemption fee exceeds tolerance",
|
|
128
|
+
CDPLS_REDEMPTION_SHORTFALL_TOO_LARGE: "Redemption shortfall exceeds tolerance",
|
|
129
|
+
CDPLS_INVALID_STABILITY_POOL_PERCENTAGE: "Invalid stability pool percentage configuration",
|
|
130
|
+
// Reserve strategy
|
|
131
|
+
RLS_RESERVE_OUT_OF_COLLATERAL: "Reserve has insufficient collateral",
|
|
132
|
+
RLS_INVALID_RESERVE: "Reserve contract not configured",
|
|
133
|
+
RLS_COLLATERAL_TO_POOL_FAILED: "Collateral transfer to pool failed",
|
|
134
|
+
RLS_TOKEN_IN_NOT_SUPPORTED: "Collateral token not supported by reserve",
|
|
135
|
+
RLS_TOKEN_OUT_NOT_SUPPORTED: "Debt token not supported by reserve",
|
|
136
|
+
// Open liquidity strategy
|
|
137
|
+
OLS_OUT_OF_COLLATERAL: "Strategy has no collateral liquidity available",
|
|
138
|
+
OLS_OUT_OF_DEBT: "Strategy has no debt liquidity available",
|
|
139
|
+
// Shared
|
|
140
|
+
LS_COOLDOWN_ACTIVE: "Rebalance cooldown is active — retry shortly",
|
|
141
|
+
LS_POOL_NOT_REBALANCEABLE: "Pool deviation is below the rebalance threshold",
|
|
142
|
+
LS_INVALID_PRICES: "Oracle price data is invalid or stale",
|
|
143
|
+
LS_CAN_ONLY_REBALANCE_ONCE: "Pool was already rebalanced this block",
|
|
144
|
+
LS_POOL_NOT_FOUND: "Pool is not registered with this strategy",
|
|
145
|
+
LS_STRATEGY_EXECUTION_FAILED: "Strategy execution failed internally",
|
|
146
|
+
LS_HOOK_NOT_CALLED: "Pool hook was not invoked — possible contract misconfiguration",
|
|
147
|
+
LS_BAD_INCENTIVE: "Incentive configuration is invalid",
|
|
148
|
+
LS_INCENTIVE_TOO_HIGH: "Rebalance incentive exceeds allowed maximum",
|
|
149
|
+
LS_DEBT_TOKEN_NOT_IN_POOL: "Debt token not found in pool pair",
|
|
150
|
+
LS_INVALID_DECIMAL: "Token decimal configuration is invalid",
|
|
151
|
+
LS_INVALID_OWNER: "Unauthorized — only owner can call this",
|
|
152
|
+
LS_INVALID_SENDER: "Unauthorized caller",
|
|
153
|
+
LS_INVALID_THRESHOLD: "Rebalance threshold is out of valid range",
|
|
154
|
+
LS_POOL_ALREADY_EXISTS: "Pool is already registered",
|
|
155
|
+
LS_POOL_MUST_BE_SET: "Pool address cannot be zero",
|
|
156
|
+
LS_PROTOCOL_FEE_RECIPIENT_REQUIRED: "Protocol fee recipient must be set",
|
|
157
|
+
LS_ZERO_DECIMAL: "Token has zero decimals",
|
|
158
|
+
// FPMM pool-side errors
|
|
159
|
+
NotLiquidityStrategy: "Caller is not a registered liquidity strategy",
|
|
160
|
+
PriceDifferenceTooSmall: "Pool deviation is below the rebalance threshold",
|
|
161
|
+
PriceDifferenceNotImproved: "Rebalance did not improve the price deviation",
|
|
162
|
+
PriceDifferenceMovedInWrongDirection: "Rebalance moved the price in the wrong direction",
|
|
163
|
+
PriceDifferenceMovedTooFarFromThresholds: "Rebalance moved the price too far past the threshold",
|
|
164
|
+
RebalanceDirectionInvalid: "Invalid rebalance direction",
|
|
165
|
+
RebalanceThresholdTooHigh: "Rebalance threshold exceeds allowed maximum",
|
|
166
|
+
RebalanceIncentiveTooHigh: "Rebalance incentive exceeds allowed maximum",
|
|
167
|
+
ReferenceRateNotSet: "Oracle reference rate is not configured",
|
|
168
|
+
ReserveValueDecreased: "Pool reserve value decreased after rebalance",
|
|
169
|
+
ReservesEmpty: "Pool reserves are empty",
|
|
170
|
+
};
|
|
171
|
+
/**
|
|
172
|
+
* Const-object mirror of `ReasonCode` so consumers can write
|
|
173
|
+
* `REASON_CODES.RLS_RESERVE_OUT_OF_COLLATERAL` instead of a bare string
|
|
174
|
+
* literal. Catches typos at the call site (the bare-string form would
|
|
175
|
+
* silently disable enrichment if mistyped).
|
|
176
|
+
*/
|
|
177
|
+
export const REASON_CODES = Object.freeze(Object.fromEntries(Object.keys(ERROR_MESSAGES).map((k) => [k, k])));
|
|
178
|
+
/**
|
|
179
|
+
* Revert codes where the strategy refuses to rebalance BECAUSE the pool is
|
|
180
|
+
* healthy — not because anything is wrong. Both consumers collapse these
|
|
181
|
+
* to a passive "no action needed" state so no red "blocked" line appears
|
|
182
|
+
* in the UI / alert annotation when the rebalancer is just waiting for
|
|
183
|
+
* the pool to drift further past threshold.
|
|
184
|
+
*/
|
|
185
|
+
export const HEALTHY_NO_OP_ERRORS = new Set([
|
|
186
|
+
"LS_POOL_NOT_REBALANCEABLE",
|
|
187
|
+
"PriceDifferenceTooSmall",
|
|
188
|
+
]);
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reserve-strategy enrichment helper — shared by the dashboard's pool-detail
|
|
3
|
+
* tooltip and the metrics-bridge Slack-alert probe. Both consumers walk the
|
|
4
|
+
* same chain (`reserve()` → debt-leg lookup → `balanceOf` + `decimals` on
|
|
5
|
+
* collateral); the only consumer-specific concerns are:
|
|
6
|
+
*
|
|
7
|
+
* - WHERE the debt-leg flag comes from. Bridge reads `determineAction(pool)`
|
|
8
|
+
* to also recover `amountOwedToPool` (the strategy's required transfer to
|
|
9
|
+
* close the breach); dashboard reads `poolConfigs(pool)` and skips the
|
|
10
|
+
* needed-amount lookup. Encoded by `mode: "needed" | "balance-only"`.
|
|
11
|
+
* - WHAT the symbol resolver looks like. Bridge calls
|
|
12
|
+
* `tokenSymbol(chainId, addr)` from the canonical manifest; dashboard
|
|
13
|
+
* reads `symbol()` on-chain. Encoded by an injected `resolveSymbol`
|
|
14
|
+
* callback.
|
|
15
|
+
* - HOW decimals get resolved. Bridge memoizes via the canonical manifest
|
|
16
|
+
* plus a process-lifetime cache; dashboard reads `decimals()` on-chain.
|
|
17
|
+
* Encoded by an injected `resolveDecimals` callback.
|
|
18
|
+
*
|
|
19
|
+
* shared-config stays viem-free: the consumer passes an opaque
|
|
20
|
+
* `EnrichmentRpc` shape (just the `readContract` calls we need) so the
|
|
21
|
+
* function compiles without depending on viem's `PublicClient`.
|
|
22
|
+
*/
|
|
23
|
+
import { ERC20_ABI_SOURCES, POOL_PAIR_ABI_SOURCES } from "./erc20-abi.js";
|
|
24
|
+
import { STRATEGY_ABI_SOURCES } from "./rebalance-abi.js";
|
|
25
|
+
/**
|
|
26
|
+
* Re-export the source ABI strings so consumers don't have to thread two
|
|
27
|
+
* imports together to call `parseAbi(...)` on the same fragments. Keeping
|
|
28
|
+
* the strings (not parsed ABIs) keeps shared-config viem-free.
|
|
29
|
+
*/
|
|
30
|
+
export { ERC20_ABI_SOURCES, POOL_PAIR_ABI_SOURCES, STRATEGY_ABI_SOURCES };
|
|
31
|
+
/**
|
|
32
|
+
* Minimal RPC surface the enrichment fetch needs. The four dispatchers
|
|
33
|
+
* mirror viem's `client.readContract` shape (per-callsite ABI / function
|
|
34
|
+
* name / args / return type) so consumers can adapt their viem client with
|
|
35
|
+
* a single thin wrapper. See `metrics-bridge/src/rebalance-check.ts` and
|
|
36
|
+
* `ui-dashboard/src/lib/rebalance-check.ts` for the call shape.
|
|
37
|
+
*
|
|
38
|
+
* Each dispatcher accepts an optional `signal: AbortSignal`. When present
|
|
39
|
+
* and aborted, the dispatcher MUST reject promptly so the caller's await
|
|
40
|
+
* unblocks (the underlying viem `client.readContract` doesn't natively
|
|
41
|
+
* accept a per-call signal in viem 2.47.0, so consumers wrap each call in
|
|
42
|
+
* a `Promise.race`-style abort guard — see metrics-bridge's `abortable`
|
|
43
|
+
* helper). This lets the metrics-bridge probe runner cancel an outlived
|
|
44
|
+
* cycle's enrichment fetches without leaking RPC calls.
|
|
45
|
+
*/
|
|
46
|
+
export interface EnrichmentRpc {
|
|
47
|
+
/** Read `determineAction(pool)` — used in `mode: "needed"`. */
|
|
48
|
+
readDetermineAction(args: {
|
|
49
|
+
strategy: `0x${string}`;
|
|
50
|
+
pool: `0x${string}`;
|
|
51
|
+
signal?: AbortSignal;
|
|
52
|
+
}): Promise<{
|
|
53
|
+
isToken0Debt: boolean;
|
|
54
|
+
amountOwedToPool: bigint;
|
|
55
|
+
}>;
|
|
56
|
+
/** Read `poolConfigs(pool)` — used in `mode: "balance-only"`. */
|
|
57
|
+
readPoolConfigs(args: {
|
|
58
|
+
strategy: `0x${string}`;
|
|
59
|
+
pool: `0x${string}`;
|
|
60
|
+
signal?: AbortSignal;
|
|
61
|
+
}): Promise<{
|
|
62
|
+
isToken0Debt: boolean;
|
|
63
|
+
}>;
|
|
64
|
+
/** Read pool's `token0()` and `token1()` in parallel. */
|
|
65
|
+
readPoolTokens(pool: `0x${string}`, signal?: AbortSignal): Promise<{
|
|
66
|
+
token0: `0x${string}`;
|
|
67
|
+
token1: `0x${string}`;
|
|
68
|
+
}>;
|
|
69
|
+
/** Read `balanceOf(holder)` against an ERC20. */
|
|
70
|
+
readBalanceOf(args: {
|
|
71
|
+
token: `0x${string}`;
|
|
72
|
+
holder: `0x${string}`;
|
|
73
|
+
signal?: AbortSignal;
|
|
74
|
+
}): Promise<bigint>;
|
|
75
|
+
}
|
|
76
|
+
export interface EnrichmentOptions {
|
|
77
|
+
chainId: number;
|
|
78
|
+
/**
|
|
79
|
+
* Reserve address — bridge threads this in from the upstream detection
|
|
80
|
+
* call (Reserve detection itself reads `reserve()`, so it's free), saving
|
|
81
|
+
* one RPC per probe. Dashboard reads it on-demand and passes the resolved
|
|
82
|
+
* value here.
|
|
83
|
+
*/
|
|
84
|
+
reserveAddr: `0x${string}`;
|
|
85
|
+
/**
|
|
86
|
+
* `"needed"` reads `determineAction(pool)` (returns `amountOwedToPool`
|
|
87
|
+
* alongside `isToken0Debt`). `"balance-only"` reads `poolConfigs(pool)`
|
|
88
|
+
* and skips the needed-amount lookup.
|
|
89
|
+
*/
|
|
90
|
+
mode: "needed" | "balance-only";
|
|
91
|
+
/**
|
|
92
|
+
* Symbol resolver — receives `(chainId, address)`, returns the canonical
|
|
93
|
+
* symbol or `null`. Bridge passes `tokenSymbol` from the contracts
|
|
94
|
+
* manifest; dashboard passes a wrapper around its on-chain `symbol()`
|
|
95
|
+
* read.
|
|
96
|
+
*/
|
|
97
|
+
resolveSymbol(chainId: number, address: `0x${string}`): Promise<string | null> | string | null;
|
|
98
|
+
/**
|
|
99
|
+
* Decimals resolver — receives `(chainId, address)`, returns the
|
|
100
|
+
* decimals. Both consumers prefer the canonical manifest first (zero
|
|
101
|
+
* RPC) and fall back to an on-chain read; the difference is in the
|
|
102
|
+
* caching strategy. Throw on unrecoverable RPC failure.
|
|
103
|
+
*
|
|
104
|
+
* The optional `signal` is forwarded by the bridge probe so a timed-out
|
|
105
|
+
* cycle can short-circuit on-chain decimal lookups too — dashboards
|
|
106
|
+
* that don't aim a signal at this resolver simply omit the argument.
|
|
107
|
+
*/
|
|
108
|
+
resolveDecimals(chainId: number, address: `0x${string}`, signal?: AbortSignal): Promise<number>;
|
|
109
|
+
/**
|
|
110
|
+
* Optional abort signal threaded through every RPC call this enrichment
|
|
111
|
+
* fetch issues. When the signal aborts, all in-flight reads MUST reject
|
|
112
|
+
* promptly so the caller can stop holding stale state. The metrics-bridge
|
|
113
|
+
* probe sets this to its per-probe AbortController; the dashboard tooltip
|
|
114
|
+
* leaves it undefined (no cancellation requirement on user-initiated
|
|
115
|
+
* tooltip fetches).
|
|
116
|
+
*/
|
|
117
|
+
signal?: AbortSignal;
|
|
118
|
+
}
|
|
119
|
+
export interface EnrichmentResult {
|
|
120
|
+
collateralToken: `0x${string}`;
|
|
121
|
+
/** Reserve's current ERC20 balance of the collateral, in human units. */
|
|
122
|
+
balance: number;
|
|
123
|
+
/** Strategy's required transfer to close the breach, in human units. Absent in `balance-only` mode. */
|
|
124
|
+
needed?: number;
|
|
125
|
+
/** Resolved collateral token symbol — `null` when the manifest lookup misses. */
|
|
126
|
+
tokenSymbol: string | null;
|
|
127
|
+
/** Decimals of the collateral token. */
|
|
128
|
+
decimals: number;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Fetch the reserve-collateral state needed to annotate a
|
|
132
|
+
* `RLS_RESERVE_OUT_OF_COLLATERAL` revert with on-chain context. Returns
|
|
133
|
+
* `null` on any RPC failure so consumers can fall back to the generic
|
|
134
|
+
* reason message without crashing the breach alert.
|
|
135
|
+
*/
|
|
136
|
+
export declare function fetchReserveEnrichment(rpc: EnrichmentRpc, strategy: `0x${string}`, pool: `0x${string}`, options: EnrichmentOptions): Promise<EnrichmentResult | null>;
|
|
137
|
+
//# sourceMappingURL=rebalance-enrichment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rebalance-enrichment.d.ts","sourceRoot":"","sources":["../src/rebalance-enrichment.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAC1E,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAG1D;;;;GAIG;AACH,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,CAAC;AAE1E;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,aAAa;IAC5B,+DAA+D;IAC/D,mBAAmB,CAAC,IAAI,EAAE;QACxB,QAAQ,EAAE,KAAK,MAAM,EAAE,CAAC;QACxB,IAAI,EAAE,KAAK,MAAM,EAAE,CAAC;QACpB,MAAM,CAAC,EAAE,WAAW,CAAC;KACtB,GAAG,OAAO,CAAC;QAAE,YAAY,EAAE,OAAO,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjE,iEAAiE;IACjE,eAAe,CAAC,IAAI,EAAE;QACpB,QAAQ,EAAE,KAAK,MAAM,EAAE,CAAC;QACxB,IAAI,EAAE,KAAK,MAAM,EAAE,CAAC;QACpB,MAAM,CAAC,EAAE,WAAW,CAAC;KACtB,GAAG,OAAO,CAAC;QAAE,YAAY,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACvC,yDAAyD;IACzD,cAAc,CACZ,IAAI,EAAE,KAAK,MAAM,EAAE,EACnB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC;QAAE,MAAM,EAAE,KAAK,MAAM,EAAE,CAAC;QAAC,MAAM,EAAE,KAAK,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IAC7D,iDAAiD;IACjD,aAAa,CAAC,IAAI,EAAE;QAClB,KAAK,EAAE,KAAK,MAAM,EAAE,CAAC;QACrB,MAAM,EAAE,KAAK,MAAM,EAAE,CAAC;QACtB,MAAM,CAAC,EAAE,WAAW,CAAC;KACtB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,WAAW,EAAE,KAAK,MAAM,EAAE,CAAC;IAC3B;;;;OAIG;IACH,IAAI,EAAE,QAAQ,GAAG,cAAc,CAAC;IAChC;;;;;OAKG;IACH,aAAa,CACX,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,KAAK,MAAM,EAAE,GACrB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,MAAM,GAAG,IAAI,CAAC;IAC1C;;;;;;;;;OASG;IACH,eAAe,CACb,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,KAAK,MAAM,EAAE,EACtB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,MAAM,CAAC,CAAC;IACnB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,eAAe,EAAE,KAAK,MAAM,EAAE,CAAC;IAC/B,yEAAyE;IACzE,OAAO,EAAE,MAAM,CAAC;IAChB,uGAAuG;IACvG,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,aAAa,EAClB,QAAQ,EAAE,KAAK,MAAM,EAAE,EACvB,IAAI,EAAE,KAAK,MAAM,EAAE,EACnB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAgElC"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reserve-strategy enrichment helper — shared by the dashboard's pool-detail
|
|
3
|
+
* tooltip and the metrics-bridge Slack-alert probe. Both consumers walk the
|
|
4
|
+
* same chain (`reserve()` → debt-leg lookup → `balanceOf` + `decimals` on
|
|
5
|
+
* collateral); the only consumer-specific concerns are:
|
|
6
|
+
*
|
|
7
|
+
* - WHERE the debt-leg flag comes from. Bridge reads `determineAction(pool)`
|
|
8
|
+
* to also recover `amountOwedToPool` (the strategy's required transfer to
|
|
9
|
+
* close the breach); dashboard reads `poolConfigs(pool)` and skips the
|
|
10
|
+
* needed-amount lookup. Encoded by `mode: "needed" | "balance-only"`.
|
|
11
|
+
* - WHAT the symbol resolver looks like. Bridge calls
|
|
12
|
+
* `tokenSymbol(chainId, addr)` from the canonical manifest; dashboard
|
|
13
|
+
* reads `symbol()` on-chain. Encoded by an injected `resolveSymbol`
|
|
14
|
+
* callback.
|
|
15
|
+
* - HOW decimals get resolved. Bridge memoizes via the canonical manifest
|
|
16
|
+
* plus a process-lifetime cache; dashboard reads `decimals()` on-chain.
|
|
17
|
+
* Encoded by an injected `resolveDecimals` callback.
|
|
18
|
+
*
|
|
19
|
+
* shared-config stays viem-free: the consumer passes an opaque
|
|
20
|
+
* `EnrichmentRpc` shape (just the `readContract` calls we need) so the
|
|
21
|
+
* function compiles without depending on viem's `PublicClient`.
|
|
22
|
+
*/
|
|
23
|
+
import { ERC20_ABI_SOURCES, POOL_PAIR_ABI_SOURCES } from "./erc20-abi.js";
|
|
24
|
+
import { STRATEGY_ABI_SOURCES } from "./rebalance-abi.js";
|
|
25
|
+
import { toHumanUnits } from "./units.js";
|
|
26
|
+
/**
|
|
27
|
+
* Re-export the source ABI strings so consumers don't have to thread two
|
|
28
|
+
* imports together to call `parseAbi(...)` on the same fragments. Keeping
|
|
29
|
+
* the strings (not parsed ABIs) keeps shared-config viem-free.
|
|
30
|
+
*/
|
|
31
|
+
export { ERC20_ABI_SOURCES, POOL_PAIR_ABI_SOURCES, STRATEGY_ABI_SOURCES };
|
|
32
|
+
/**
|
|
33
|
+
* Fetch the reserve-collateral state needed to annotate a
|
|
34
|
+
* `RLS_RESERVE_OUT_OF_COLLATERAL` revert with on-chain context. Returns
|
|
35
|
+
* `null` on any RPC failure so consumers can fall back to the generic
|
|
36
|
+
* reason message without crashing the breach alert.
|
|
37
|
+
*/
|
|
38
|
+
export async function fetchReserveEnrichment(rpc, strategy, pool, options) {
|
|
39
|
+
try {
|
|
40
|
+
// 1. Determine which leg is debt + (in needed-mode) the required
|
|
41
|
+
// transfer amount. Issued in parallel with the pool's token0/token1
|
|
42
|
+
// reads since neither dependency-graphs into the other. Strategy
|
|
43
|
+
// debt-leg read goes first so consumers with sequential / order-
|
|
44
|
+
// sensitive mocks (vitest `mockResolvedValueOnce` chains) can stay
|
|
45
|
+
// deterministic without overhauling their fixtures.
|
|
46
|
+
const { signal } = options;
|
|
47
|
+
const [debtInfo, { token0, token1 }] = await Promise.all([
|
|
48
|
+
options.mode === "needed"
|
|
49
|
+
? rpc.readDetermineAction({ strategy, pool, signal })
|
|
50
|
+
: rpc.readPoolConfigs({ strategy, pool, signal }),
|
|
51
|
+
rpc.readPoolTokens(pool, signal),
|
|
52
|
+
]);
|
|
53
|
+
// 2. Collateral is the non-debt leg.
|
|
54
|
+
const collateralToken = debtInfo.isToken0Debt ? token1 : token0;
|
|
55
|
+
// 3. balanceOf + symbol + decimals on the collateral token. All three
|
|
56
|
+
// reads are independent of each other given `collateralToken`, so
|
|
57
|
+
// we issue them in parallel. Decimals + symbol may resolve from the
|
|
58
|
+
// consumer's manifest / cache (zero RPC) or fall back to an on-chain
|
|
59
|
+
// read; balanceOf is always fresh.
|
|
60
|
+
const [balance, tokenSymbol, decimals] = await Promise.all([
|
|
61
|
+
rpc.readBalanceOf({
|
|
62
|
+
token: collateralToken,
|
|
63
|
+
holder: options.reserveAddr,
|
|
64
|
+
signal,
|
|
65
|
+
}),
|
|
66
|
+
options.resolveSymbol(options.chainId, collateralToken),
|
|
67
|
+
options.resolveDecimals(options.chainId, collateralToken, signal),
|
|
68
|
+
]);
|
|
69
|
+
const result = {
|
|
70
|
+
collateralToken,
|
|
71
|
+
balance: toHumanUnits(balance, decimals),
|
|
72
|
+
tokenSymbol,
|
|
73
|
+
decimals,
|
|
74
|
+
};
|
|
75
|
+
if (options.mode === "needed" &&
|
|
76
|
+
"amountOwedToPool" in debtInfo &&
|
|
77
|
+
typeof debtInfo.amountOwedToPool === "bigint") {
|
|
78
|
+
result.needed = toHumanUnits(debtInfo.amountOwedToPool, decimals);
|
|
79
|
+
}
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
// Re-throw aborts so the caller's wall-clock timeout actually short-
|
|
84
|
+
// circuits — otherwise an AbortError mid-enrichment gets swallowed and
|
|
85
|
+
// the probe returns the un-enriched blocked result instead of a
|
|
86
|
+
// transport_error, defeating the abort guarantee for any pool whose
|
|
87
|
+
// signal happens to fire during reserve enrichment.
|
|
88
|
+
if (err &&
|
|
89
|
+
typeof err === "object" &&
|
|
90
|
+
(err.name === "AbortError" ||
|
|
91
|
+
err.code === "ABORT_ERR")) {
|
|
92
|
+
throw err;
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OK/WARN boundary. A pool is considered "within tolerance" while
|
|
3
|
+
* `priceDifference / rebalanceThreshold ≤ DEVIATION_TOLERANCE_RATIO`.
|
|
4
|
+
* Strict `>` flips the pool to WARN (or above).
|
|
5
|
+
*
|
|
6
|
+
* Mirrors `DEVIATION_TOLERANCE_NUM / DEVIATION_TOLERANCE_DEN` in
|
|
7
|
+
* `indexer-envio/src/pool/health.ts`; `test/deviationThresholdSharedConfigSync.test.ts`
|
|
8
|
+
* enforces numeric parity and `test/healthStatusParity.test.ts` enforces
|
|
9
|
+
* behavioral parity with the dashboard's `computeHealthStatus`.
|
|
10
|
+
*/
|
|
11
|
+
export declare const DEVIATION_TOLERANCE_RATIO = 1.01;
|
|
12
|
+
/**
|
|
13
|
+
* WARN/CRITICAL magnitude boundary. A pool past `DEVIATION_TOLERANCE_RATIO`
|
|
14
|
+
* stays WARN until `priceDifference / rebalanceThreshold > DEVIATION_CRITICAL_RATIO`
|
|
15
|
+
* AND the breach has outlived `DEVIATION_BREACH_GRACE_SECONDS`. Below this
|
|
16
|
+
* magnitude, duration alone never escalates a breach to CRITICAL.
|
|
17
|
+
*
|
|
18
|
+
* Mirrors `DEVIATION_CRITICAL_NUM / DEVIATION_CRITICAL_DEN` in
|
|
19
|
+
* `indexer-envio/src/pool/health.ts`; `test/deviationThresholdSharedConfigSync.test.ts`
|
|
20
|
+
* enforces numeric parity and `test/healthStatusParity.test.ts` enforces
|
|
21
|
+
* behavioral parity. Also gates the metrics-bridge rebalance-reason probe so
|
|
22
|
+
* the annotation only attaches to alerts that can actually fire.
|
|
23
|
+
*/
|
|
24
|
+
export declare const DEVIATION_CRITICAL_RATIO = 1.05;
|
|
25
|
+
//# sourceMappingURL=thresholds.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thresholds.d.ts","sourceRoot":"","sources":["../src/thresholds.ts"],"names":[],"mappings":"AAUA;;;;;;;;;GASG;AACH,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,wBAAwB,OAAO,CAAC"}
|