Skip to content

Commit 0b938f7

Browse files
authored
Update interest rate strategies + move terminology from collateral to branch (#809)
fixes #804 Delegate Strategies - In the env vars, add an auto delegate per branch. - Enable the IC strategies. - Fetch the min & max interest rates from the contracts. - Split delegation modal components in separate files. - Start migrating some subgraph-hooks functions into `liquity-utils.ts`. Collateral => Branch - `collIndex` is now `branchId`. - A new type, `Branch`, has been added. It replaces what used to be called a collateral (which now only corresponds to the branch collateral, when used). Branches objects can now contain additional data rather than contract addresses only. - Branch.strategies corresponds to the interest rate delegate strategies. - `COLL_*_DELEGATE_AUTO` is now `COLL_*_IC_STRATEGIES` and allows to define multiple strategies. - `getBranch()` now throws if the branch doesn’t exist, so it can be called without checking the returned value + throwing an error if null. - `getCollToken()` follows the same errors behavior. - `getCollateralContract()` has been renamed `getBranchContract()` and follows the same errors behavior. - `getCollateralContracts()` has been renamed `getBranchContracts()` and follows the same errors behavior.
1 parent a1ba607 commit 0b938f7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+1470
-1449
lines changed

frontend/app/src/comps/About/About.tsx

+11-11
Original file line numberDiff line numberDiff line change
@@ -33,38 +33,38 @@ const ENV_EXCLUDE: Set<keyof typeof env> = new Set([
3333
// - contracts: main contracts (CONTRACT_*)
3434
// - branches: branches contracts (in COLLATERAL_CONTRACTS)
3535
function getEnvGroups() {
36-
const config = { ...env };
36+
const envConfig = { ...env };
3737

3838
const contracts: Record<string, string> = {};
3939

4040
for (const [key, value] of Object.entries(env) as Entries<typeof env>) {
4141
if (key.startsWith("CONTRACT_")) {
4242
contracts[key.replace("CONTRACT_", "")] = String(value);
43-
delete config[key];
43+
delete envConfig[key];
4444
continue;
4545
}
4646
}
4747

4848
const branches: {
49-
collIndex: number;
49+
branchId: number;
5050
symbol: string;
5151
contracts: [string, string][];
5252
}[] = [];
5353

54-
for (const { collIndex, symbol, contracts } of config.COLLATERAL_CONTRACTS) {
54+
for (const { branchId, symbol, contracts } of envConfig.BRANCHES) {
5555
branches.push({
56-
collIndex,
56+
branchId,
5757
symbol,
5858
contracts: Object
5959
.entries(contracts)
6060
.map(([name, address]) => [name, address]),
6161
});
6262
}
6363

64-
delete config["COLLATERAL_CONTRACTS" as keyof typeof config];
64+
delete envConfig["COLLATERAL_CONTRACTS" as keyof typeof envConfig];
6565

66-
const configFinal = Object.fromEntries(
67-
Object.entries(config)
66+
const envConfigFinal = Object.fromEntries(
67+
Object.entries(envConfig)
6868
.filter(([key]) => !ENV_EXCLUDE.has(key as keyof typeof env))
6969
.map(([key, value]) => {
7070
if (key === "CHAIN_BLOCK_EXPLORER") {
@@ -79,7 +79,7 @@ function getEnvGroups() {
7979
}),
8080
);
8181

82-
return { config: configFinal, contracts, branches };
82+
return { config: envConfigFinal, contracts, branches };
8383
}
8484

8585
const AboutContext = createContext<{
@@ -279,9 +279,9 @@ export function About({ children }: { children: ReactNode }) {
279279
}
280280
entries={envGroups.contracts}
281281
/>
282-
{envGroups.branches.map(({ collIndex, symbol, contracts }) => (
282+
{envGroups.branches.map(({ branchId, symbol, contracts }) => (
283283
<AboutTable
284-
key={collIndex}
284+
key={branchId}
285285
title={`Branch contracts: ${symbol}`}
286286
entries={Object.fromEntries(contracts)}
287287
/>

frontend/app/src/comps/EarnPositionSummary/EarnPositionSummary.tsx

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { CollIndex, PositionEarn } from "@/src/types";
1+
import type { BranchId, PositionEarn } from "@/src/types";
22
import type { ReactNode } from "react";
33

44
import { Amount } from "@/src/comps/Amount/Amount";
@@ -11,22 +11,22 @@ import * as dn from "dnum";
1111
import Link from "next/link";
1212

1313
export function EarnPositionSummary({
14-
collIndex,
14+
branchId,
1515
prevEarnPosition,
1616
earnPosition,
1717
linkToScreen,
1818
title,
1919
txPreviewMode,
2020
}: {
21-
collIndex: CollIndex;
21+
branchId: BranchId;
2222
prevEarnPosition?: PositionEarn | null;
2323
earnPosition: PositionEarn | null;
2424
linkToScreen?: boolean;
2525
title?: ReactNode;
2626
txPreviewMode?: boolean;
2727
}) {
28-
const collToken = getCollToken(collIndex);
29-
const earnPool = useEarnPool(collIndex);
28+
const collToken = getCollToken(branchId);
29+
const earnPool = useEarnPool(branchId);
3030

3131
const { totalDeposited: totalPoolDeposit } = earnPool.data;
3232

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
import type { Delegate } from "@/src/types";
2+
3+
import { Amount } from "@/src/comps/Amount/Amount";
4+
import { fmtnum, formatRedemptionRisk } from "@/src/formatting";
5+
import { getRedemptionRisk } from "@/src/liquity-math";
6+
import { riskLevelToStatusMode } from "@/src/uikit-utils";
7+
import { css } from "@/styled-system/css";
8+
import { Button, IconCopy, StatusDot, TextButton } from "@liquity2/uikit";
9+
import { MiniChart } from "./MiniChart";
10+
import { ShadowBox } from "./ShadowBox";
11+
12+
export function DelegateBox({
13+
delegate,
14+
onSelect,
15+
selectLabel = "Select",
16+
}: {
17+
delegate: Delegate;
18+
onSelect: (delegate: Delegate) => void;
19+
selectLabel: string;
20+
}) {
21+
const delegationRisk = getRedemptionRisk(delegate.interestRate);
22+
return (
23+
<ShadowBox key={delegate.id}>
24+
<section
25+
key={delegate.name}
26+
className={css({
27+
display: "flex",
28+
flexDirection: "column",
29+
alignItems: "center",
30+
padding: "8px 16px",
31+
})}
32+
>
33+
<div
34+
className={css({
35+
display: "flex",
36+
flexDirection: "column",
37+
width: "100%",
38+
paddingBottom: 12,
39+
borderBottom: "1px solid token(colors.borderSoft)",
40+
})}
41+
>
42+
<div
43+
className={css({
44+
display: "flex",
45+
justifyContent: "space-between",
46+
alignItems: "center",
47+
width: "100%",
48+
fontSize: 20,
49+
fontWeight: 500,
50+
userSelect: "none",
51+
})}
52+
>
53+
<h1 title={`${delegate.name} (${delegate.address})`}>
54+
{delegate.name}
55+
</h1>
56+
<div
57+
className={css({
58+
display: "flex",
59+
gap: 6,
60+
alignItems: "center",
61+
})}
62+
>
63+
<MiniChart />
64+
{fmtnum(delegate.interestRate, "pct1z")}%
65+
</div>
66+
</div>
67+
<div
68+
className={css({
69+
display: "flex",
70+
justifyContent: "space-between",
71+
width: "100%",
72+
fontSize: 14,
73+
color: "content",
74+
})}
75+
>
76+
<div
77+
className={css({
78+
display: "flex",
79+
gap: 8,
80+
alignItems: "center",
81+
})}
82+
>
83+
<Amount
84+
value={delegate.boldAmount}
85+
format="compact"
86+
suffix=" BOLD"
87+
/>
88+
</div>
89+
<div
90+
className={css({
91+
display: "flex",
92+
gap: 8,
93+
alignItems: "center",
94+
})}
95+
>
96+
<StatusDot mode={riskLevelToStatusMode(delegationRisk)} />
97+
{formatRedemptionRisk(delegationRisk)}
98+
</div>
99+
</div>
100+
</div>
101+
<div
102+
className={css({
103+
display: "flex",
104+
flexDirection: "column",
105+
width: "100%",
106+
paddingTop: 12,
107+
fontSize: 14,
108+
paddingBottom: 12,
109+
borderBottom: "1px solid token(colors.borderSoft)",
110+
})}
111+
>
112+
<div
113+
className={css({
114+
display: "flex",
115+
justifyContent: "space-between",
116+
width: "100%",
117+
fontSize: 14,
118+
color: "content",
119+
})}
120+
>
121+
<div>Interest rate range</div>
122+
<div>
123+
{fmtnum(delegate.interestRateChange[0], "pct2")}
124+
<span>-</span>
125+
{fmtnum(delegate.interestRateChange[1], "pct2")}%
126+
</div>
127+
</div>
128+
{delegate.fee && (
129+
<div
130+
className={css({
131+
display: "flex",
132+
justifyContent: "space-between",
133+
width: "100%",
134+
fontSize: 14,
135+
color: "content",
136+
})}
137+
>
138+
<div>
139+
Fees <abbr title="per annum">p.a.</abbr>
140+
</div>
141+
<div title={`${fmtnum(delegate.fee, "pctfull")}%`}>
142+
{fmtnum(delegate.fee, { digits: 4, scale: 100 })}%
143+
</div>
144+
</div>
145+
)}
146+
</div>
147+
<div
148+
className={css({
149+
display: "flex",
150+
justifyContent: "space-between",
151+
width: "100%",
152+
paddingTop: 16,
153+
paddingBottom: 8,
154+
fontSize: 14,
155+
})}
156+
>
157+
<div
158+
className={css({
159+
display: "flex",
160+
gap: 8,
161+
})}
162+
>
163+
<TextButton
164+
label={
165+
<>
166+
Copy address
167+
<IconCopy size={16} />
168+
</>
169+
}
170+
className={css({
171+
fontSize: 14,
172+
})}
173+
/>
174+
</div>
175+
<div>
176+
<Button
177+
label={selectLabel}
178+
mode="primary"
179+
size="small"
180+
onClick={() => {
181+
onSelect(delegate);
182+
}}
183+
/>
184+
</div>
185+
</div>
186+
</section>
187+
</ShadowBox>
188+
);
189+
}

0 commit comments

Comments
 (0)