Skip to content

Commit 840cc12

Browse files
perf/refactor: optimize lending liquidations, interest caching, UI SDK, and tests (#770)
1 parent fd0293b commit 840cc12

19 files changed

Lines changed: 706 additions & 19 deletions
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
name: Contract Integration Tests
2+
3+
on:
4+
pull_request:
5+
branches: [main]
6+
paths:
7+
- 'stellar-lend/contracts/**'
8+
- 'stellar-lend/packages/test-framework/**'
9+
- 'stellar-lend/benchmarks/**'
10+
- '.github/workflows/contract-integration-tests.yml'
11+
workflow_dispatch: {}
12+
13+
env:
14+
CARGO_TERM_COLOR: always
15+
16+
jobs:
17+
integration:
18+
name: Integration, Fuzz Smoke, Gas Diff
19+
runs-on: ubuntu-latest
20+
steps:
21+
- uses: actions/checkout@v4
22+
23+
- uses: dtolnay/rust-toolchain@stable
24+
with:
25+
components: rustfmt, clippy
26+
27+
- uses: actions/cache@v4
28+
with:
29+
path: |
30+
~/.cargo/registry
31+
~/.cargo/git
32+
stellar-lend/target
33+
key: ${{ runner.os }}-contracts-integration-${{ hashFiles('stellar-lend/Cargo.lock') }}
34+
restore-keys: |
35+
${{ runner.os }}-contracts-integration-
36+
37+
- name: Check formatting
38+
working-directory: stellar-lend
39+
run: cargo fmt --all -- --check
40+
41+
- name: Run integration framework tests
42+
working-directory: stellar-lend
43+
run: cargo test --package test-framework --verbose
44+
45+
- name: Run lending tests
46+
working-directory: stellar-lend
47+
run: cargo test --package stellarlend-lending --verbose
48+
49+
- name: Run flash-loan liquidation integration tests
50+
working-directory: stellar-lend
51+
run: cargo test --package flash-loan-liquidation-tests --verbose
52+
53+
- name: Run gas benchmarks with regression detection
54+
working-directory: stellar-lend
55+
run: cargo run --release --bin run_benchmarks -- --compare benchmarks/baseline.json --output benchmark-results.json
56+
57+
- name: Upload gas diff report
58+
if: always()
59+
uses: actions/upload-artifact@v4
60+
with:
61+
name: contract-gas-diff
62+
path: |
63+
stellar-lend/benchmark-results.json
64+
stellar-lend/benchmark-results.md

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
"workspaces": [
77
"api",
88
"oracle",
9-
"packages/sdk"
9+
"packages/sdk",
10+
"packages/ui-lending"
1011
],
1112
"scripts": {
1213
"prepare": "husky || true",
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import type { StorybookConfig } from '@storybook/react-vite';
2+
3+
const config: StorybookConfig = {
4+
stories: ['../stories/**/*.stories.@(ts|tsx)'],
5+
addons: ['@storybook/addon-essentials', '@storybook/addon-interactions', '@storybook/addon-themes'],
6+
framework: {
7+
name: '@storybook/react-vite',
8+
options: {},
9+
},
10+
docs: {
11+
autodocs: 'tag',
12+
},
13+
};
14+
15+
export default config;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import type { Preview } from '@storybook/react';
2+
3+
const preview: Preview = {
4+
parameters: {
5+
controls: {
6+
matchers: {
7+
color: /(background|color)$/i,
8+
date: /Date$/i,
9+
},
10+
},
11+
},
12+
};
13+
14+
export default preview;

packages/ui-lending/package.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"scripts": {
1616
"build": "tsup",
1717
"test": "jest",
18+
"typecheck": "tsc --noEmit",
1819
"storybook": "storybook dev -p 6006",
1920
"build-storybook": "storybook build"
2021
},
@@ -23,8 +24,15 @@
2324
"react-dom": ">=18.0.0"
2425
},
2526
"devDependencies": {
27+
"@storybook/addon-essentials": "^8.4.7",
28+
"@storybook/addon-interactions": "^8.4.7",
29+
"@storybook/addon-themes": "^8.4.7",
30+
"@storybook/react": "^8.4.7",
31+
"@storybook/react-vite": "^8.4.7",
2632
"@types/react": "^18.0.0",
2733
"@types/react-dom": "^18.0.0",
34+
"storybook": "^8.4.7",
35+
"tsup": "^8.3.5",
2836
"typescript": "^5.0.0"
2937
},
3038
"keywords": ["stellar", "lending", "defi", "react", "components"],
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import type { Meta, StoryObj } from '@storybook/react';
2+
import React from 'react';
3+
import { HealthMeter } from '../src/components/HealthMeter';
4+
import { createTheme } from '../src/utils/theme';
5+
6+
const meta: Meta<typeof HealthMeter> = {
7+
title: 'Lending/HealthMeter',
8+
component: HealthMeter,
9+
parameters: { layout: 'centered' },
10+
tags: ['autodocs'],
11+
};
12+
13+
export default meta;
14+
type Story = StoryObj<typeof HealthMeter>;
15+
16+
const safeHealth = {
17+
value: 2.42,
18+
status: 'safe' as const,
19+
collateralValue: 24_200,
20+
borrowedValue: 8_000,
21+
liquidationThreshold: 0.8,
22+
};
23+
24+
export const Safe: Story = {
25+
args: { health: safeHealth },
26+
};
27+
28+
export const Liquidatable: Story = {
29+
args: {
30+
health: {
31+
...safeHealth,
32+
value: 0.94,
33+
status: 'liquidatable',
34+
borrowedValue: 20_600,
35+
},
36+
},
37+
};
38+
39+
export const Compact: Story = {
40+
args: { health: safeHealth, showDetails: false },
41+
};
42+
43+
export const DarkMode: Story = {
44+
args: { health: safeHealth, theme: createTheme('dark') },
45+
parameters: { backgrounds: { default: 'dark' } },
46+
};
47+
48+
export const Loading: Story = {
49+
args: { health: safeHealth, isLoading: true },
50+
};
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import type { Meta, StoryObj } from '@storybook/react';
2+
import React from 'react';
3+
import { LiquidationRiskGauge } from '../src/components/LiquidationRiskGauge';
4+
import { createTheme } from '../src/utils/theme';
5+
6+
const meta: Meta<typeof LiquidationRiskGauge> = {
7+
title: 'Lending/LiquidationRiskGauge',
8+
component: LiquidationRiskGauge,
9+
parameters: { layout: 'centered' },
10+
tags: ['autodocs'],
11+
};
12+
13+
export default meta;
14+
type Story = StoryObj<typeof LiquidationRiskGauge>;
15+
16+
const risk = {
17+
healthFactor: 1.18,
18+
liquidationPrice: 0.82,
19+
currentPrice: 1,
20+
safetyBuffer: 0.18,
21+
riskLevel: 'high' as const,
22+
};
23+
24+
export const HighRisk: Story = {
25+
args: { risk },
26+
};
27+
28+
export const Critical: Story = {
29+
args: {
30+
risk: {
31+
...risk,
32+
healthFactor: 0.96,
33+
safetyBuffer: 0.02,
34+
riskLevel: 'critical',
35+
},
36+
},
37+
};
38+
39+
export const DarkMode: Story = {
40+
args: { risk, theme: createTheme('dark') },
41+
parameters: { backgrounds: { default: 'dark' } },
42+
};
43+
44+
export const Loading: Story = {
45+
args: { risk, isLoading: true },
46+
};
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import type { Meta, StoryObj } from '@storybook/react';
2+
import React from 'react';
3+
import { PoolCard } from '../src/components/PoolCard';
4+
import { createTheme } from '../src/utils/theme';
5+
6+
const meta: Meta<typeof PoolCard> = {
7+
title: 'Lending/PoolCard',
8+
component: PoolCard,
9+
parameters: { layout: 'centered' },
10+
tags: ['autodocs'],
11+
};
12+
13+
export default meta;
14+
type Story = StoryObj<typeof PoolCard>;
15+
16+
const pool = {
17+
id: 'xlm-usdc',
18+
asset: 'USDC Market',
19+
symbol: 'USDC',
20+
totalSupply: 4_200_000,
21+
totalBorrow: 2_730_000,
22+
supplyApy: 4.18,
23+
borrowApy: 6.92,
24+
utilization: 0.65,
25+
collateralFactor: 0.8,
26+
liquidationThreshold: 0.85,
27+
price: 1,
28+
isActive: true,
29+
};
30+
31+
export const Active: Story = {
32+
args: { pool },
33+
};
34+
35+
export const WithActions: Story = {
36+
args: {
37+
pool,
38+
onSupply: (selected) => alert(`Supply ${selected.symbol}`),
39+
onBorrow: (selected) => alert(`Borrow ${selected.symbol}`),
40+
},
41+
};
42+
43+
export const Inactive: Story = {
44+
args: { pool: { ...pool, isActive: false } },
45+
};
46+
47+
export const DarkMode: Story = {
48+
args: { pool, theme: createTheme('dark') },
49+
parameters: { backgrounds: { default: 'dark' } },
50+
};
51+
52+
export const Loading: Story = {
53+
args: { pool, isLoading: true },
54+
};
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import type { Meta, StoryObj } from '@storybook/react';
2+
import React from 'react';
3+
import { RateChart } from '../src/components/RateChart';
4+
import { createTheme } from '../src/utils/theme';
5+
6+
const meta: Meta<typeof RateChart> = {
7+
title: 'Lending/RateChart',
8+
component: RateChart,
9+
parameters: { layout: 'centered' },
10+
tags: ['autodocs'],
11+
};
12+
13+
export default meta;
14+
type Story = StoryObj<typeof RateChart>;
15+
16+
const data = Array.from({ length: 12 }, (_, i) => ({
17+
timestamp: Date.now() - (11 - i) * 86_400_000,
18+
supplyApy: 3.2 + i * 0.09,
19+
borrowApy: 5.8 + i * 0.14,
20+
utilization: 0.51 + i * 0.015,
21+
}));
22+
23+
export const SupplyAndBorrow: Story = {
24+
args: { data },
25+
};
26+
27+
export const BorrowOnly: Story = {
28+
args: { data, showSupply: false },
29+
};
30+
31+
export const Empty: Story = {
32+
args: { data: [], isEmpty: true },
33+
};
34+
35+
export const DarkMode: Story = {
36+
args: { data, theme: createTheme('dark') },
37+
parameters: { backgrounds: { default: 'dark' } },
38+
};
39+
40+
export const Loading: Story = {
41+
args: { data, isLoading: true },
42+
};

packages/ui-lending/tsup.config.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { defineConfig } from 'tsup';
2+
3+
export default defineConfig({
4+
entry: ['src/index.ts'],
5+
format: ['cjs', 'esm'],
6+
dts: true,
7+
sourcemap: true,
8+
clean: true,
9+
external: ['react', 'react-dom'],
10+
});

0 commit comments

Comments
 (0)