Skip to content

Commit 9cbff54

Browse files
authored
Merge pull request #261 from lidofinance/vaults
Vaults
2 parents 5be5b07 + f1a47e7 commit 9cbff54

Some content is hidden

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

55 files changed

+16958
-239
lines changed

.editorconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ root = true
44
end_of_line = lf
55
insert_final_newline = true
66

7-
[*.{js,json,yml}]
7+
[*.{ts,js,json,yml}]
88
charset = utf-8
99
indent_style = space
1010
indent_size = 2

packages/sdk/.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,7 @@
22
TEST_PRIVATE_KEY=0xeeeeeee
33
TEST_CHAIN_ID=560048
44
TEST_RPC_URL=
5+
TEST_L2_RPC_URL=
6+
TEST_L2_CHAIN_ID=
57
TEST_SKIP_SPENDING_TESTS=false
68
TEST_SUBGRAPH_URL=

packages/sdk/jest.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const jestConfig: JestConfigWithTsJest = {
2121
],
2222
},
2323
maxWorkers: 1,
24+
setupFiles: ['<rootDir>/tests/mocks/multiformats.mock.cjs'],
2425
globalSetup: '<rootDir>/tests/global-setup.cjs',
2526
globalTeardown: '<rootDir>/tests/global-teardown.cjs',
2627
testTimeout: 60_000,

packages/sdk/package.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@
8181
"default": "./dist/cjs/dual-governance/index.js",
8282
"types": "./dist/types/dual-governance/index.d.ts"
8383
},
84+
"./stvault": {
85+
"import": "./dist/esm/stvault/index.js",
86+
"default": "./dist/cjs/stvault/index.js",
87+
"types": "./dist/types/stvault/index.d.ts"
88+
},
8489
"./package.json": "./package.json"
8590
},
8691
"typesVersions": {
@@ -120,6 +125,9 @@
120125
],
121126
"dual-governance": [
122127
"./dist/types/dual-governance/index.d.ts"
128+
],
129+
"stvault": [
130+
"./dist/types/stvault/index.d.ts"
123131
]
124132
}
125133
},
@@ -160,8 +168,12 @@
160168
},
161169
"dependencies": {
162170
"@ethersproject/bytes": "^5.7.0",
171+
"@openzeppelin/merkle-tree": "^1.0.8",
172+
"blockstore-core": "^5.0.4",
163173
"graphql": "^16.8.1",
164-
"graphql-request": "^6.1.0"
174+
"graphql-request": "^6.1.0",
175+
"ipfs-unixfs-importer": "^15.4.0",
176+
"multiformats": "^13.4.0"
165177
},
166178
"peerDependencies": {
167179
"viem": "^2.45.0"

packages/sdk/src/common/constants.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@ export enum LIDO_CONTRACT_NAMES {
7878
accountingOracle = 'accountingOracle',
7979
depositSecurityModule = 'depositSecurityModule',
8080
elRewardsVault = 'elRewardsVault',
81-
legacyOracle = 'legacyOracle',
8281
lido = 'lido',
8382
oracleReportSanityChecker = 'oracleReportSanityChecker',
8483
postTokenRebaseReceiver = 'postTokenRebaseReceiver',
@@ -112,15 +111,22 @@ export const DUAL_GOVERNANCE_CONTRACT_ADDRESSES: {
112111

113112
export const WSTETH_REFERRAL_STAKER: { [key in CHAINS]?: Address } = {
114113
[CHAINS.Mainnet]: '0xa88f0329C2c4ce51ba3fc619BBf44efE7120Dd0d',
115-
[CHAINS.Hoodi]: '0xf886BcC68b240316103fE8A12453Ce7831c2e835',
114+
[CHAINS.Hoodi]: '0xf886BcC68b240316103fE8A12453Ce7831c2e835',
116115
[CHAINS.Sepolia]: '0x9E90338495FfD691bDDC680e47D94b60cF66dDad',
117-
}
116+
};
118117

119118
export enum LIDO_L2_CONTRACT_NAMES {
120119
wsteth = 'wsteth',
121120
steth = 'steth',
122121
}
123122

123+
export const VAULT_VIEWER_CONTRACT_ADDRESSES: {
124+
[key in CHAINS]?: Address;
125+
} = {
126+
[CHAINS.Hoodi]: '0xEAfD3F8DC0ABA14B81344ea0B869DdF9F7e18221',
127+
[CHAINS.Mainnet]: '0x57dAb476c3E37a2410076C9568d796e429fd8418',
128+
} as const;
129+
124130
export const LIDO_L2_CONTRACT_ADDRESSES: {
125131
[key in CHAINS]?: { [key2 in LIDO_L2_CONTRACT_NAMES]?: Address };
126132
} = {

packages/sdk/src/common/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export * from './constants.js';
22
export * from './utils/sdk-error.js';
3+
export * from './utils/get-encodable-contract.js';
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { describe, expect, test } from '@jest/globals';
2+
import { bigIntCeilDiv } from '../bigint-math.js';
3+
4+
describe('bigIntCeilDiv', () => {
5+
test('returns exact quotient when divisible', () => {
6+
expect(bigIntCeilDiv(10n, 2n)).toBe(5n);
7+
});
8+
9+
test('rounds up when there is a remainder', () => {
10+
expect(bigIntCeilDiv(11n, 2n)).toBe(6n);
11+
expect(bigIntCeilDiv(15n, 4n)).toBe(4n);
12+
});
13+
14+
test('handles zero numerator', () => {
15+
expect(bigIntCeilDiv(0n, 3n)).toBe(0n);
16+
});
17+
18+
test('throws when dividing by zero', () => {
19+
expect(() => bigIntCeilDiv(1n, 0n)).toThrow('DIVISION_BY_ZERO');
20+
});
21+
});
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
export const bigIntMax = (...args: bigint[]) =>
2+
args.reduce((a, b) => (a > b ? a : b));
3+
export const bigIntMin = (...args: bigint[]) =>
4+
args.reduce((a, b) => (a < b ? a : b));
5+
6+
export const bigIntAbs = (value: bigint): bigint =>
7+
value < 0n ? -value : value;
8+
9+
export const bigIntSign = (value: bigint): 1n | -1n => {
10+
return value >= 0n ? 1n : -1n;
11+
};
12+
13+
export const bigIntCeilDiv = (
14+
numerator: bigint,
15+
denominator: bigint,
16+
): bigint => {
17+
if (denominator === 0n) {
18+
throw new Error('DIVISION_BY_ZERO');
19+
}
20+
21+
const quotient = numerator / denominator;
22+
const remainder = numerator % denominator;
23+
24+
return remainder === 0n ? quotient : quotient + 1n;
25+
};
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { encodeFunctionData, type Abi, type Address, type Hex } from 'viem';
2+
3+
// copy from view internals
4+
const getFunctionParameters = (values: unknown[]) => {
5+
const hasArgs = values.length > 0 && Array.isArray(values[0]);
6+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
7+
const args = (hasArgs ? values[0]! : []) as unknown[];
8+
const options = ((hasArgs ? values[1] : values[0]) ?? {}) as any;
9+
return { args, options };
10+
};
11+
12+
type ContractType = {
13+
address: Address;
14+
abi: Abi;
15+
read?: {
16+
[functionName: string]: (...args: any[]) => Promise<any>;
17+
};
18+
simulate?: {
19+
[functionName: string]: (...args: any[]) => Promise<any>;
20+
};
21+
};
22+
23+
export type EncodableContract<
24+
TContract extends ContractType,
25+
TSimulate extends
26+
TContract['simulate'] = TContract['simulate'] extends undefined
27+
? never
28+
: TContract['simulate'],
29+
TRead extends TContract['read'] = TContract['read'] extends undefined
30+
? never
31+
: TContract['read'],
32+
TMethods extends TSimulate & TRead = TSimulate & TRead,
33+
TFunctionName extends keyof TMethods = keyof TMethods,
34+
> = TContract & {
35+
prepare: {
36+
[K in TFunctionName]: (
37+
...args: Parameters<
38+
TMethods[K] extends (...args: any[]) => any ? TMethods[K] : never
39+
>
40+
) => {
41+
address: TContract['address'];
42+
abi: TContract['abi'];
43+
functionName: K;
44+
args: Parameters<
45+
TMethods[K] extends (...args: any[]) => any ? TMethods[K] : never
46+
>[0] extends readonly unknown[]
47+
? Parameters<
48+
TMethods[K] extends (...args: any[]) => any ? TMethods[K] : never
49+
>[0]
50+
: undefined;
51+
};
52+
};
53+
encode: {
54+
[K in TFunctionName]: (
55+
...args: Parameters<
56+
TMethods[K] extends (...args: any[]) => any ? TMethods[K] : never
57+
>
58+
) => {
59+
to: TContract['address'];
60+
data: Hex;
61+
value?: bigint;
62+
};
63+
};
64+
};
65+
66+
export const getEncodableContract = <TContract extends ContractType>(
67+
contract: TContract,
68+
) => {
69+
(contract as any).prepare = new Proxy(
70+
{},
71+
{
72+
get(_, functionName: string) {
73+
return (...parameters: unknown[]) => {
74+
const { args } = getFunctionParameters(parameters);
75+
return {
76+
address: contract.address,
77+
abi: contract.abi,
78+
functionName,
79+
args,
80+
};
81+
};
82+
},
83+
},
84+
);
85+
(contract as any).encode = new Proxy(
86+
{},
87+
{
88+
get(_, functionName: string) {
89+
return (...parameters: unknown[]) => {
90+
const { args, options } = getFunctionParameters(parameters);
91+
return {
92+
to: contract.address,
93+
data: encodeFunctionData({
94+
abi: contract.abi,
95+
functionName,
96+
args,
97+
}),
98+
value: options.value,
99+
};
100+
};
101+
},
102+
},
103+
);
104+
return contract as EncodableContract<TContract>;
105+
};
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* Converts snake_case string to camelCase
3+
* @param str - The snake_case string to convert
4+
* @returns The camelCase version of the string
5+
*/
6+
export const snakeToCamel = (str: string): string => {
7+
return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
8+
};

0 commit comments

Comments
 (0)