Skip to content

Commit 8ee82a8

Browse files
authored
feat: svr <> regular adapters comparative report (#207)
1 parent ccb3189 commit 8ee82a8

File tree

8 files changed

+321
-0
lines changed

8 files changed

+321
-0
lines changed

src/cli.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {addCommand as addFork} from './commands/fork';
77
import {addCommand as addGovernance} from './commands/governance';
88
import {addCommand as addIpfsCommand} from './commands/ipfsUpload';
99
import {addCommand as addCapoCommand} from './commands/capo';
10+
import {addCommand as addCapoSvrCommand} from './commands/capoSvr';
1011

1112
const program = new Command();
1213

@@ -32,5 +33,6 @@ addDiffSnapshots(program);
3233
addFork(program);
3334
addIpfsCommand(program);
3435
addCapoCommand(program);
36+
addCapoSvrCommand(program);
3537

3638
program.parse();

src/commands/capoSvr.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import fs from 'node:fs';
2+
import type {Command} from '@commander-js/extra-typings';
3+
import {generateSvrCapoReport} from '../reports/capo-svr-comparative-report';
4+
import {readJsonFile, readJsonString} from '../utils/json';
5+
6+
export function addCommand(program: Command) {
7+
program
8+
.command('capo-svr-report')
9+
.description('generate a capo svr comparative report')
10+
.argument('<source>')
11+
.option('-o, --out <string>', 'output path')
12+
.option('--stringMode', 'expects input to be a string, not paths')
13+
.action(async (_source, options) => {
14+
const source = options.stringMode ? readJsonString(_source) : readJsonFile(_source);
15+
const content = await generateSvrCapoReport(source);
16+
if (options.out) {
17+
fs.writeFileSync(options.out, content);
18+
} else {
19+
console.log(content);
20+
}
21+
});
22+
}

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@ export * from './govv3/generateProposalReport';
77
export * from './utils/tenderlyClient';
88
export * from './utils/logger';
99
export * from './govv3/utils/checkAddress';
10+
export * from './reports/capo-report';
11+
export * from './reports/capo-svr-comparative-report';
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2+
3+
exports[`capo svr report > should generate a well formatted capo svr report 1`] = `
4+
"# Capo Comparative Report
5+
6+
# Capped wstETH / stETH(ETH) / USD
7+
8+
- Difference between adapters using SVR and regular Chainlink Feeds
9+
10+
| SVR Adapter | Regular Adapter | Diff | Date |
11+
| --- | --- | --- | --- |
12+
| 1880.1097014 | 1880.16617657 | 0.00% | 12 Apr 2025 |
13+
| 1940.99317901 | 1934.84763392 | 0.32% | 13 Apr 2025 |
14+
| 1951.25160577 | 1948.99310701 | 0.12% | 14 Apr 2025 |
15+
| 1968.85656094 | 1968.56171868 | 0.01% | 15 Apr 2025 |
16+
| 1880.44218254 | 1880.88400462 | 0.02% | 16 Apr 2025 |
17+
| 1927.14919798 | 1921.9665194 | 0.27% | 17 Apr 2025 |
18+
| 1901.98709042 | 1901.24372975 | 0.04% | 18 Apr 2025 |
19+
| 1922.66605311 | 1922.94047495 | 0.01% | 19 Apr 2025 |
20+
| 1908.33882018 | 1906.22505555 | 0.11% | 20 Apr 2025 |
21+
| 1973.33083291 | 1970.15250316 | 0.16% | 21 Apr 2025 |
22+
| 1948.43062491 | 1949.37422474 | 0.05% | 22 Apr 2025 |
23+
| 2160.35296377 | 2171.82424304 | 0.53% | 23 Apr 2025 |
24+
| 2093.83167086 | 2093.91812992 | 0.00% | 24 Apr 2025 |
25+
| 2148.42289872 | 2146.33325394 | 0.10% | 25 Apr 2025 |
26+
| 2146.93062115 | 2146.64704136 | 0.01% | 26 Apr 2025 |
27+
| 2167.39047482 | 2161.98612144 | 0.25% | 27 Apr 2025 |
28+
| 2169.22825978 | 2170.50143613 | 0.06% | 28 Apr 2025 |
29+
| 2183.31686871 | 2183.95464045 | 0.03% | 29 Apr 2025 |
30+
| 2106.14087107 | 2100.66019434 | 0.26% | 30 Apr 2025 |
31+
| 2209.6315434 | 2213.95366061 | 0.20% | 01 May 2025 |
32+
| 2215.33523206 | 2216.96923478 | 0.07% | 02 May 2025 |
33+
| 2207.45309063 | 2207.8434084 | 0.02% | 03 May 2025 |
34+
| 2196.18873716 | 2196.94841401 | 0.03% | 04 May 2025 |
35+
| 2173.76096755 | 2173.72491546 | 0.00% | 05 May 2025 |
36+
"
37+
`;
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import {formatUnits} from 'viem';
2+
import {CapoSvrSnapshot} from './snapshot-types';
3+
4+
type Comparative = {
5+
regularAdapterPrice: string;
6+
svrAdapterPrice: string;
7+
diff: string;
8+
date: string;
9+
};
10+
11+
export async function generateSvrCapoReport(snapshot: CapoSvrSnapshot) {
12+
let comparative: Comparative[] = [];
13+
14+
for (const key in snapshot.comparative) {
15+
const price = snapshot.comparative[key];
16+
17+
const svrAdapterPrice = formatUnits(BigInt(price.svrAdapterPrice), snapshot.decimals);
18+
const regularAdapterPrice = formatUnits(BigInt(price.regularAdapterPrice), snapshot.decimals);
19+
const difference =
20+
Number(svrAdapterPrice) > Number(regularAdapterPrice)
21+
? Number(svrAdapterPrice) - Number(regularAdapterPrice)
22+
: Number(regularAdapterPrice) - Number(svrAdapterPrice);
23+
24+
const diff = (
25+
(100 * difference) /
26+
((Number(svrAdapterPrice) + Number(regularAdapterPrice)) / 2)
27+
).toFixed(2);
28+
29+
const formattedDate = formatTimestamp(price.timestamp);
30+
31+
comparative.push({
32+
svrAdapterPrice,
33+
regularAdapterPrice,
34+
diff,
35+
date: formattedDate,
36+
});
37+
}
38+
39+
comparative = comparative.slice(snapshot.minSnapshotDelay);
40+
41+
// generate md report
42+
let content = '';
43+
content += `# Capo Comparative Report\n\n`;
44+
content += `# ${snapshot.sourceName} \n\n`;
45+
content += `- Difference between adapters using SVR and regular Chainlink Feeds\n\n`;
46+
content += `| SVR Adapter | Regular Adapter | Diff | Date |\n`;
47+
content += `| --- | --- | --- | --- |\n`;
48+
comparative.forEach((price) => {
49+
content += `| ${price.svrAdapterPrice} | ${price.regularAdapterPrice} | ${price.diff}% | ${price.date} |\n`;
50+
});
51+
return content;
52+
}
53+
54+
function formatTimestamp(timestampInSec: number) {
55+
// Create a new Date object from the timestamp in seconds
56+
const date = new Date(timestampInSec * 1000);
57+
58+
// Use the Intl.DateTimeFormat API to format the date
59+
return new Intl.DateTimeFormat('en-GB', {
60+
year: 'numeric',
61+
month: 'short',
62+
day: '2-digit',
63+
timeZone: 'GMT',
64+
}).format(date);
65+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import {describe, expect, it} from 'vitest';
2+
import {readJsonFile} from '../utils/json';
3+
import {generateSvrCapoReport} from './capo-svr-comparative-report';
4+
5+
describe('capo svr report', () => {
6+
it(
7+
'should generate a well formatted capo svr report',
8+
async () => {
9+
const snapshot = readJsonFile('/src/reports/mocks/capoSvr.json');
10+
const content = await generateSvrCapoReport(snapshot);
11+
console.log(content);
12+
expect(content).toMatchSnapshot();
13+
},
14+
{timeout: 30000},
15+
);
16+
});

src/reports/mocks/capoSvr.json

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
{
2+
"comparative": {
3+
"22199900": {
4+
"regularAdapterPrice": 217666375221,
5+
"svrAdapterPrice": 217554419401,
6+
"timestamp": 1743822131
7+
},
8+
"22207200": {
9+
"regularAdapterPrice": 216795440985,
10+
"svrAdapterPrice": 216644202291,
11+
"timestamp": 1743910247
12+
},
13+
"22214500": {
14+
"regularAdapterPrice": 184904466888,
15+
"svrAdapterPrice": 184331232930,
16+
"timestamp": 1743998399
17+
},
18+
"22221800": {
19+
"regularAdapterPrice": 191382628592,
20+
"svrAdapterPrice": 190799419233,
21+
"timestamp": 1744086611
22+
},
23+
"22229100": {
24+
"regularAdapterPrice": 172703894879,
25+
"svrAdapterPrice": 172730277631,
26+
"timestamp": 1744174619
27+
},
28+
"22236400": {
29+
"regularAdapterPrice": 194477533416,
30+
"svrAdapterPrice": 194601135762,
31+
"timestamp": 1744262495
32+
},
33+
"22243700": {
34+
"regularAdapterPrice": 185570748959,
35+
"svrAdapterPrice": 185112817156,
36+
"timestamp": 1744350443
37+
},
38+
"22251000": {
39+
"regularAdapterPrice": 188016617657,
40+
"svrAdapterPrice": 188010970140,
41+
"timestamp": 1744438283
42+
},
43+
"22258300": {
44+
"regularAdapterPrice": 193484763392,
45+
"svrAdapterPrice": 194099317901,
46+
"timestamp": 1744526231
47+
},
48+
"22265600": {
49+
"regularAdapterPrice": 194899310701,
50+
"svrAdapterPrice": 195125160577,
51+
"timestamp": 1744614059
52+
},
53+
"22272900": {
54+
"regularAdapterPrice": 196856171868,
55+
"svrAdapterPrice": 196885656094,
56+
"timestamp": 1744702151
57+
},
58+
"22280200": {
59+
"regularAdapterPrice": 188088400462,
60+
"svrAdapterPrice": 188044218254,
61+
"timestamp": 1744790159
62+
},
63+
"22287500": {
64+
"regularAdapterPrice": 192196651940,
65+
"svrAdapterPrice": 192714919798,
66+
"timestamp": 1744878263
67+
},
68+
"22294800": {
69+
"regularAdapterPrice": 190124372975,
70+
"svrAdapterPrice": 190198709042,
71+
"timestamp": 1744966187
72+
},
73+
"22302100": {
74+
"regularAdapterPrice": 192294047495,
75+
"svrAdapterPrice": 192266605311,
76+
"timestamp": 1745054243
77+
},
78+
"22309400": {
79+
"regularAdapterPrice": 190622505555,
80+
"svrAdapterPrice": 190833882018,
81+
"timestamp": 1745142263
82+
},
83+
"22316700": {
84+
"regularAdapterPrice": 197015250316,
85+
"svrAdapterPrice": 197333083291,
86+
"timestamp": 1745230343
87+
},
88+
"22324000": {
89+
"regularAdapterPrice": 194937422474,
90+
"svrAdapterPrice": 194843062491,
91+
"timestamp": 1745318339
92+
},
93+
"22331300": {
94+
"regularAdapterPrice": 217182424304,
95+
"svrAdapterPrice": 216035296377,
96+
"timestamp": 1745406467
97+
},
98+
"22338600": {
99+
"regularAdapterPrice": 209391812992,
100+
"svrAdapterPrice": 209383167086,
101+
"timestamp": 1745494463
102+
},
103+
"22345900": {
104+
"regularAdapterPrice": 214633325394,
105+
"svrAdapterPrice": 214842289872,
106+
"timestamp": 1745582423
107+
},
108+
"22353200": {
109+
"regularAdapterPrice": 214664704136,
110+
"svrAdapterPrice": 214693062115,
111+
"timestamp": 1745670383
112+
},
113+
"22360500": {
114+
"regularAdapterPrice": 216198612144,
115+
"svrAdapterPrice": 216739047482,
116+
"timestamp": 1745758211
117+
},
118+
"22367800": {
119+
"regularAdapterPrice": 217050143613,
120+
"svrAdapterPrice": 216922825978,
121+
"timestamp": 1745846207
122+
},
123+
"22375100": {
124+
"regularAdapterPrice": 218395464045,
125+
"svrAdapterPrice": 218331686871,
126+
"timestamp": 1745934335
127+
},
128+
"22382400": {
129+
"regularAdapterPrice": 210066019434,
130+
"svrAdapterPrice": 210614087107,
131+
"timestamp": 1746022607
132+
},
133+
"22389700": {
134+
"regularAdapterPrice": 221395366061,
135+
"svrAdapterPrice": 220963154340,
136+
"timestamp": 1746110987
137+
},
138+
"22397000": {
139+
"regularAdapterPrice": 221696923478,
140+
"svrAdapterPrice": 221533523206,
141+
"timestamp": 1746199583
142+
},
143+
"22404300": {
144+
"regularAdapterPrice": 220784340840,
145+
"svrAdapterPrice": 220745309063,
146+
"timestamp": 1746287867
147+
},
148+
"22411600": {
149+
"regularAdapterPrice": 219694841401,
150+
"svrAdapterPrice": 219618873716,
151+
"timestamp": 1746376319
152+
},
153+
"22418900": {
154+
"regularAdapterPrice": 217372491546,
155+
"svrAdapterPrice": 217376096755,
156+
"timestamp": 1746464591
157+
}
158+
},
159+
"decimals": 8,
160+
"minSnapshotDelay": 7,
161+
"sourceName": "Capped wstETH / stETH(ETH) / USD"
162+
}

src/reports/snapshot-types.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,3 +165,18 @@ export const capoSnapshotSchema = z.object({
165165
});
166166

167167
export type CapoSnapshot = z.infer<typeof capoSnapshotSchema>;
168+
169+
export const capoSvrPriceSchema = z.object({
170+
regularAdapterPrice: z.number(),
171+
svrAdapterPrice: z.number(),
172+
timestamp: z.number(),
173+
});
174+
175+
export const capoSvrSnapshotSchema = z.object({
176+
sourceName: z.string(),
177+
decimals: z.number(),
178+
minSnapshotDelay: z.number(),
179+
comparative: z.record(capoSvrPriceSchema),
180+
});
181+
182+
export type CapoSvrSnapshot = z.infer<typeof capoSvrSnapshotSchema>;

0 commit comments

Comments
 (0)