Skip to content

Commit

Permalink
Update interest rate strategies + move terminology from collateral to…
Browse files Browse the repository at this point in the history
… 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.
  • Loading branch information
bpierre authored Feb 11, 2025
1 parent a1ba607 commit 0b938f7
Show file tree
Hide file tree
Showing 52 changed files with 1,470 additions and 1,449 deletions.
22 changes: 11 additions & 11 deletions frontend/app/src/comps/About/About.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,38 +33,38 @@ const ENV_EXCLUDE: Set<keyof typeof env> = new Set([
// - contracts: main contracts (CONTRACT_*)
// - branches: branches contracts (in COLLATERAL_CONTRACTS)
function getEnvGroups() {
const config = { ...env };
const envConfig = { ...env };

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

for (const [key, value] of Object.entries(env) as Entries<typeof env>) {
if (key.startsWith("CONTRACT_")) {
contracts[key.replace("CONTRACT_", "")] = String(value);
delete config[key];
delete envConfig[key];
continue;
}
}

const branches: {
collIndex: number;
branchId: number;
symbol: string;
contracts: [string, string][];
}[] = [];

for (const { collIndex, symbol, contracts } of config.COLLATERAL_CONTRACTS) {
for (const { branchId, symbol, contracts } of envConfig.BRANCHES) {
branches.push({
collIndex,
branchId,
symbol,
contracts: Object
.entries(contracts)
.map(([name, address]) => [name, address]),
});
}

delete config["COLLATERAL_CONTRACTS" as keyof typeof config];
delete envConfig["COLLATERAL_CONTRACTS" as keyof typeof envConfig];

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

return { config: configFinal, contracts, branches };
return { config: envConfigFinal, contracts, branches };
}

const AboutContext = createContext<{
Expand Down Expand Up @@ -279,9 +279,9 @@ export function About({ children }: { children: ReactNode }) {
}
entries={envGroups.contracts}
/>
{envGroups.branches.map(({ collIndex, symbol, contracts }) => (
{envGroups.branches.map(({ branchId, symbol, contracts }) => (
<AboutTable
key={collIndex}
key={branchId}
title={`Branch contracts: ${symbol}`}
entries={Object.fromEntries(contracts)}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { CollIndex, PositionEarn } from "@/src/types";
import type { BranchId, PositionEarn } from "@/src/types";
import type { ReactNode } from "react";

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

export function EarnPositionSummary({
collIndex,
branchId,
prevEarnPosition,
earnPosition,
linkToScreen,
title,
txPreviewMode,
}: {
collIndex: CollIndex;
branchId: BranchId;
prevEarnPosition?: PositionEarn | null;
earnPosition: PositionEarn | null;
linkToScreen?: boolean;
title?: ReactNode;
txPreviewMode?: boolean;
}) {
const collToken = getCollToken(collIndex);
const earnPool = useEarnPool(collIndex);
const collToken = getCollToken(branchId);
const earnPool = useEarnPool(branchId);

const { totalDeposited: totalPoolDeposit } = earnPool.data;

Expand Down
189 changes: 189 additions & 0 deletions frontend/app/src/comps/InterestRateField/DelegateBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
import type { Delegate } from "@/src/types";

import { Amount } from "@/src/comps/Amount/Amount";
import { fmtnum, formatRedemptionRisk } from "@/src/formatting";
import { getRedemptionRisk } from "@/src/liquity-math";
import { riskLevelToStatusMode } from "@/src/uikit-utils";
import { css } from "@/styled-system/css";
import { Button, IconCopy, StatusDot, TextButton } from "@liquity2/uikit";
import { MiniChart } from "./MiniChart";
import { ShadowBox } from "./ShadowBox";

export function DelegateBox({
delegate,
onSelect,
selectLabel = "Select",
}: {
delegate: Delegate;
onSelect: (delegate: Delegate) => void;
selectLabel: string;
}) {
const delegationRisk = getRedemptionRisk(delegate.interestRate);
return (
<ShadowBox key={delegate.id}>
<section
key={delegate.name}
className={css({
display: "flex",
flexDirection: "column",
alignItems: "center",
padding: "8px 16px",
})}
>
<div
className={css({
display: "flex",
flexDirection: "column",
width: "100%",
paddingBottom: 12,
borderBottom: "1px solid token(colors.borderSoft)",
})}
>
<div
className={css({
display: "flex",
justifyContent: "space-between",
alignItems: "center",
width: "100%",
fontSize: 20,
fontWeight: 500,
userSelect: "none",
})}
>
<h1 title={`${delegate.name} (${delegate.address})`}>
{delegate.name}
</h1>
<div
className={css({
display: "flex",
gap: 6,
alignItems: "center",
})}
>
<MiniChart />
{fmtnum(delegate.interestRate, "pct1z")}%
</div>
</div>
<div
className={css({
display: "flex",
justifyContent: "space-between",
width: "100%",
fontSize: 14,
color: "content",
})}
>
<div
className={css({
display: "flex",
gap: 8,
alignItems: "center",
})}
>
<Amount
value={delegate.boldAmount}
format="compact"
suffix=" BOLD"
/>
</div>
<div
className={css({
display: "flex",
gap: 8,
alignItems: "center",
})}
>
<StatusDot mode={riskLevelToStatusMode(delegationRisk)} />
{formatRedemptionRisk(delegationRisk)}
</div>
</div>
</div>
<div
className={css({
display: "flex",
flexDirection: "column",
width: "100%",
paddingTop: 12,
fontSize: 14,
paddingBottom: 12,
borderBottom: "1px solid token(colors.borderSoft)",
})}
>
<div
className={css({
display: "flex",
justifyContent: "space-between",
width: "100%",
fontSize: 14,
color: "content",
})}
>
<div>Interest rate range</div>
<div>
{fmtnum(delegate.interestRateChange[0], "pct2")}
<span>-</span>
{fmtnum(delegate.interestRateChange[1], "pct2")}%
</div>
</div>
{delegate.fee && (
<div
className={css({
display: "flex",
justifyContent: "space-between",
width: "100%",
fontSize: 14,
color: "content",
})}
>
<div>
Fees <abbr title="per annum">p.a.</abbr>
</div>
<div title={`${fmtnum(delegate.fee, "pctfull")}%`}>
{fmtnum(delegate.fee, { digits: 4, scale: 100 })}%
</div>
</div>
)}
</div>
<div
className={css({
display: "flex",
justifyContent: "space-between",
width: "100%",
paddingTop: 16,
paddingBottom: 8,
fontSize: 14,
})}
>
<div
className={css({
display: "flex",
gap: 8,
})}
>
<TextButton
label={
<>
Copy address
<IconCopy size={16} />
</>
}
className={css({
fontSize: 14,
})}
/>
</div>
<div>
<Button
label={selectLabel}
mode="primary"
size="small"
onClick={() => {
onSelect(delegate);
}}
/>
</div>
</div>
</section>
</ShadowBox>
);
}
Loading

0 comments on commit 0b938f7

Please sign in to comment.