Skip to content

Commit 274168a

Browse files
committed
feat: use merkle lib
1 parent ba97274 commit 274168a

6 files changed

Lines changed: 148 additions & 58 deletions

File tree

features/adjustment/mint/form/submit-button/submit-button.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export const SubmitButton = () => {
3939
const disabled = isSubmitting && !isValid;
4040

4141
return (
42-
<OracleReportButton>
42+
<OracleReportButton action="minting">
4343
<MultiplePermissionedSubmitButton
4444
dashboardRoles={roles}
4545
type="submit"

features/report/components/oracle-report-button.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { useFormControllerRetry } from 'shared/hook-form/form-controller/use-for
1515

1616
export type OracleReportButtonProps = React.PropsWithChildren<{
1717
ensureFreshReport?: boolean;
18+
action?: string;
1819
}>;
1920

2021
export type ModalState = {
@@ -24,6 +25,7 @@ export type ModalState = {
2425

2526
export const OracleReportButton = ({
2627
ensureFreshReport = true,
28+
action,
2729
children,
2830
}: OracleReportButtonProps) => {
2931
const [submitStep, setSubmitStep] = useState<ModalState>(() => ({
@@ -58,7 +60,7 @@ export const OracleReportButton = ({
5860
mutate();
5961
}}
6062
>
61-
Apply Oracle Report
63+
Apply Oracle Report {action ? `to unlock ${action}` : ''}
6264
</Button>
6365
) : (
6466
children

features/report/ipfs/index.ts

Lines changed: 60 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,34 @@
11
import jsonBigint from 'json-bigint';
2-
import { Address, getAddress, Hex } from 'viem';
2+
import { Address, Hex } from 'viem';
33

44
import { getApiURL } from 'config';
55
import { CID_TO_GATEWAY } from './ipfs-gateways';
6+
import { StandardMerkleTree } from '@openzeppelin/merkle-tree';
67

7-
export const fetchIPFS = async <TResult>(cid: string): Promise<TResult> => {
8+
type IPFSReport = {
9+
blockNumber: bigint;
10+
format: 'standard-v1';
11+
refSlot: bigint;
12+
timestamp: bigint;
13+
leafEncoding: ['address', 'uint256', 'uint256', 'uint256', 'uint256'];
14+
leafIndexToData: {
15+
0: 'vault_address';
16+
1: 'total_value_wei';
17+
2: 'in_out_delta';
18+
3: 'fee';
19+
4: 'liability_shares';
20+
};
21+
merkleTreeRoot: Hex;
22+
prevTreeCID: string;
23+
proofsCID: string;
24+
tree: string[];
25+
values: {
26+
treeIndex: bigint;
27+
value: [Address, bigint, bigint, bigint, bigint];
28+
}[];
29+
};
30+
31+
const fetchIPFS = async <TResult>(cid: string): Promise<TResult> => {
832
for (const gateway of CID_TO_GATEWAY) {
933
const url = gateway(cid);
1034
try {
@@ -27,15 +51,39 @@ export const fetchIPFS = async <TResult>(cid: string): Promise<TResult> => {
2751
throw new Error('Could not fetch report from IPFS');
2852
};
2953

30-
export const extractVaultProof = (
31-
vault: Address,
32-
proofsData: IPFSReportProofs,
33-
) => {
34-
const vaultEntry = proofsData.proofs[getAddress(vault)];
35-
if (!vaultEntry) {
36-
throw new Error(`[extractVaultProof] Vault ${vault} not found in report`);
54+
const extractProofFromIPFS = async (cid: string, vault: Address) => {
55+
const IPFSReportData = await fetchIPFS<IPFSReport>(cid);
56+
57+
const merkleTree = StandardMerkleTree.load({
58+
...IPFSReportData,
59+
values: IPFSReportData.values.map(({ treeIndex, value }) => {
60+
return {
61+
value,
62+
treeIndex: Number(treeIndex),
63+
};
64+
}),
65+
});
66+
67+
const vaultIndex = IPFSReportData.values.findIndex(
68+
({ value }) => value[0].toLowerCase() === vault.toLowerCase(),
69+
);
70+
71+
if (vaultIndex < 0) {
72+
throw new Error(
73+
`[extractProofFromIPFS] Vault ${vault} not found in report`,
74+
);
3775
}
38-
return vaultEntry;
76+
77+
const vaultEntry = IPFSReportData.values[vaultIndex];
78+
79+
return {
80+
vault: vaultEntry.value[0],
81+
totalValueWei: vaultEntry.value[1],
82+
inOutDelta: vaultEntry.value[2],
83+
fee: vaultEntry.value[3],
84+
liabilityShares: vaultEntry.value[4],
85+
proof: merkleTree.getProof(vaultIndex),
86+
};
3987
};
4088

4189
export const fetchReportMerkle = async (
@@ -47,49 +95,7 @@ export const fetchReportMerkle = async (
4795
if (url) {
4896
throw new Error('not implemented');
4997
}
50-
// fallback to IPFS
51-
const IPFSReportData = await fetchIPFS<IPFSReport>(cid);
52-
const IPFSReportsProofs = await fetchIPFS<IPFSReportProofs>(
53-
IPFSReportData.proofsCID,
54-
);
55-
return extractVaultProof(vault, IPFSReportsProofs);
56-
};
5798

58-
type IPFSReport = {
59-
blockNumber: bigint;
60-
format: 'standard-v1';
61-
refSlot: bigint;
62-
timestamp: bigint;
63-
leafEncoding: ['address', 'uint256', 'uint256', 'uint256', 'uint256'];
64-
leafIndexToData: {
65-
0: 'vault_address';
66-
1: 'total_value_wei';
67-
2: 'in_out_delta';
68-
3: 'fee';
69-
4: 'liability_shares';
70-
};
71-
merkleTreeRoot: Hex;
72-
prevTreeCID: string;
73-
proofsCID: string;
74-
tree: string[];
75-
values: {
76-
treeIndex: bigint;
77-
value: [Address, bigint, bigint, bigint, bigint];
78-
}[];
79-
};
80-
81-
type IPFSReportProofs = {
82-
merkleTreeRoot: Hex;
83-
refSlot: bigint;
84-
proofs: {
85-
[key: string]: {
86-
id: bigint;
87-
totalValueWei: bigint;
88-
inOutDelta: bigint;
89-
fee: bigint;
90-
liabilityShares: bigint;
91-
leaf: Hex;
92-
proof: Hex[];
93-
};
94-
};
99+
// fallback
100+
return await extractProofFromIPFS(cid, vault);
95101
};

features/supply/withdraw/form/submit-button/submit-button.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export const SubmitButton = () => {
1010
const disabled = isSubmitting || !isValid || !isDirty;
1111

1212
return (
13-
<OracleReportButton>
13+
<OracleReportButton action="withdrawal">
1414
<PermissionedSubmitButton
1515
type="submit"
1616
dashboardRole="withdrawer"

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"@lidofinance/next-pages": "^0.51.0",
3333
"@lidofinance/rpc": "^0.51.0",
3434
"@lidofinance/satanizer": "^0.51.0",
35+
"@openzeppelin/merkle-tree": "^1.0.8",
3536
"@reef-knot/types": "^4.1.0",
3637
"@tanstack/react-query": "^5.51.21",
3738
"copy-to-clipboard": "^3.3.1",

yarn.lock

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2611,6 +2611,14 @@
26112611
"@lodestar/params" "^1.29.0"
26122612
ethereum-cryptography "^2.0.0"
26132613

2614+
"@metamask/abi-utils@^2.0.4":
2615+
version "2.0.4"
2616+
resolved "https://registry.yarnpkg.com/@metamask/abi-utils/-/abi-utils-2.0.4.tgz#20908c1d910f7a17a89fdf5778a5c59d5cb8b8be"
2617+
integrity sha512-StnIgUB75x7a7AgUhiaUZDpCsqGp7VkNnZh2XivXkJ6mPkE83U8ARGQj5MbRis7VJY8BC5V1AbB1fjdh0hupPQ==
2618+
dependencies:
2619+
"@metamask/superstruct" "^3.1.0"
2620+
"@metamask/utils" "^9.0.0"
2621+
26142622
"@metamask/eth-json-rpc-provider@^1.0.0":
26152623
version "1.0.1"
26162624
resolved "https://registry.yarnpkg.com/@metamask/eth-json-rpc-provider/-/eth-json-rpc-provider-1.0.1.tgz#3fd5316c767847f4ca107518b611b15396a5a32c"
@@ -2747,6 +2755,11 @@
27472755
resolved "https://registry.yarnpkg.com/@metamask/superstruct/-/superstruct-3.0.0.tgz#0200d0a627522904a7e0fd751dcc6fb863cefacb"
27482756
integrity sha512-TOm+Lt/lCJk9j/3QT2LucrPewRmqI7/GKT+blK2IIOAkBMS+9TmeNjd2Y+TlfpSSYstaYsGZyz1XwpiTCg6RLA==
27492757

2758+
"@metamask/superstruct@^3.1.0":
2759+
version "3.2.1"
2760+
resolved "https://registry.yarnpkg.com/@metamask/superstruct/-/superstruct-3.2.1.tgz#fca933017c5b78529f8f525560cef32c57e889d2"
2761+
integrity sha512-fLgJnDOXFmuVlB38rUN5SmU7hAFQcCjrg3Vrxz67KTY7YHFnSNEKvX4avmEBdOI0yTCxZjwMCFEqsC8k2+Wd3g==
2762+
27502763
"@metamask/utils@^5.0.1":
27512764
version "5.0.2"
27522765
resolved "https://registry.yarnpkg.com/@metamask/utils/-/utils-5.0.2.tgz#140ba5061d90d9dac0280c19cab101bc18c8857c"
@@ -2773,6 +2786,21 @@
27732786
semver "^7.5.4"
27742787
uuid "^9.0.1"
27752788

2789+
"@metamask/utils@^9.0.0":
2790+
version "9.3.0"
2791+
resolved "https://registry.yarnpkg.com/@metamask/utils/-/utils-9.3.0.tgz#4726bd7f5d6a43ea8425b6d663ab9207f617c2d1"
2792+
integrity sha512-w8CVbdkDrVXFJbfBSlDfafDR6BAkpDmv1bC1UJVCoVny5tW2RKAdn9i68Xf7asYT4TnUhl/hN4zfUiKQq9II4g==
2793+
dependencies:
2794+
"@ethereumjs/tx" "^4.2.0"
2795+
"@metamask/superstruct" "^3.1.0"
2796+
"@noble/hashes" "^1.3.1"
2797+
"@scure/base" "^1.1.3"
2798+
"@types/debug" "^4.1.7"
2799+
debug "^4.3.4"
2800+
pony-cause "^2.1.10"
2801+
semver "^7.5.4"
2802+
uuid "^9.0.1"
2803+
27762804
"@motionone/animation@^10.15.1", "@motionone/animation@^10.17.0":
27772805
version "10.17.0"
27782806
resolved "https://registry.yarnpkg.com/@motionone/animation/-/animation-10.17.0.tgz#7633c6f684b5fee2b61c405881b8c24662c68fca"
@@ -2964,6 +2992,11 @@
29642992
resolved "https://registry.yarnpkg.com/@noble/ciphers/-/ciphers-1.2.1.tgz#3812b72c057a28b44ff0ad4aff5ca846e5b9cdc9"
29652993
integrity sha512-rONPWMC7PeExE077uLE4oqWrZ1IvAfz3oH9LibVAcVCopJiA9R62uavnbEzdkVmJYI6M6Zgkbeb07+tWjlq2XA==
29662994

2995+
"@noble/ciphers@1.3.0":
2996+
version "1.3.0"
2997+
resolved "https://registry.yarnpkg.com/@noble/ciphers/-/ciphers-1.3.0.tgz#f64b8ff886c240e644e5573c097f86e5b43676dc"
2998+
integrity sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==
2999+
29673000
"@noble/ciphers@^1.0.0":
29683001
version "1.0.0"
29693002
resolved "https://registry.yarnpkg.com/@noble/ciphers/-/ciphers-1.0.0.tgz#34758a1cbfcd4126880f83e6b1cdeb88785b7970"
@@ -2997,6 +3030,13 @@
29973030
dependencies:
29983031
"@noble/hashes" "1.7.2"
29993032

3033+
"@noble/curves@1.9.0", "@noble/curves@~1.9.0":
3034+
version "1.9.0"
3035+
resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.9.0.tgz#13e0ca8be4a0ce66c113693a94514e5599f40cfc"
3036+
integrity sha512-7YDlXiNMdO1YZeH6t/kvopHHbIZzlxrCV9WLqCY6QhcXOoXiNCMDqJIglZ9Yjx5+w7Dz30TITFrlTjnRg7sKEg==
3037+
dependencies:
3038+
"@noble/hashes" "1.8.0"
3039+
30003040
"@noble/curves@^1.6.0":
30013041
version "1.6.0"
30023042
resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.6.0.tgz#be5296ebcd5a1730fccea4786d420f87abfeb40b"
@@ -3041,7 +3081,7 @@
30413081
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.7.2.tgz#d53c65a21658fb02f3303e7ee3ba89d6754c64b4"
30423082
integrity sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ==
30433083

3044-
"@noble/hashes@^1.3.0":
3084+
"@noble/hashes@1.8.0", "@noble/hashes@^1.3.0", "@noble/hashes@~1.8.0":
30453085
version "1.8.0"
30463086
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.8.0.tgz#cee43d801fcef9644b11b8194857695acd5f815a"
30473087
integrity sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==
@@ -3067,6 +3107,14 @@
30673107
"@nodelib/fs.scandir" "2.1.5"
30683108
fastq "^1.6.0"
30693109

3110+
"@openzeppelin/merkle-tree@^1.0.8":
3111+
version "1.0.8"
3112+
resolved "https://registry.yarnpkg.com/@openzeppelin/merkle-tree/-/merkle-tree-1.0.8.tgz#31ea9cdc09de37315c32650a9772db1352dcdcd8"
3113+
integrity sha512-E2c9/Y3vjZXwVvPZKqCKUn7upnvam1P1ZhowJyZVQSkzZm5WhumtaRr+wkUXrZVfkIc7Gfrl7xzabElqDL09ow==
3114+
dependencies:
3115+
"@metamask/abi-utils" "^2.0.4"
3116+
ethereum-cryptography "^3.0.0"
3117+
30703118
"@parcel/watcher-android-arm64@2.4.0":
30713119
version "2.4.0"
30723120
resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.0.tgz#9c93763794153e4f76920994a423b6ea3257059d"
@@ -3413,6 +3461,11 @@
34133461
resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.2.4.tgz#002eb571a35d69bdb4c214d0995dff76a8dcd2a9"
34143462
integrity sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ==
34153463

3464+
"@scure/base@~1.2.5":
3465+
version "1.2.5"
3466+
resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.2.5.tgz#f9d1b232425b367d0dcb81c96611dcc651d58671"
3467+
integrity sha512-9rE6EOVeIQzt5TSu4v+K523F8u6DhBsoZWPGKlnCshhlDhy0kJzUX4V+tr2dWmzF1GdekvThABoEQBGBQI7xZw==
3468+
34163469
"@scure/bip32@1.4.0":
34173470
version "1.4.0"
34183471
resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.4.0.tgz#4e1f1e196abedcef395b33b9674a042524e20d67"
@@ -3431,6 +3484,15 @@
34313484
"@noble/hashes" "~1.7.1"
34323485
"@scure/base" "~1.2.2"
34333486

3487+
"@scure/bip32@1.7.0":
3488+
version "1.7.0"
3489+
resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.7.0.tgz#b8683bab172369f988f1589640e53c4606984219"
3490+
integrity sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==
3491+
dependencies:
3492+
"@noble/curves" "~1.9.0"
3493+
"@noble/hashes" "~1.8.0"
3494+
"@scure/base" "~1.2.5"
3495+
34343496
"@scure/bip32@^1.5.0":
34353497
version "1.6.0"
34363498
resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.6.0.tgz#6dbc6b4af7c9101b351f41231a879d8da47e0891"
@@ -3456,6 +3518,14 @@
34563518
"@noble/hashes" "~1.7.1"
34573519
"@scure/base" "~1.2.4"
34583520

3521+
"@scure/bip39@1.6.0":
3522+
version "1.6.0"
3523+
resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.6.0.tgz#475970ace440d7be87a6086cbee77cb8f1a684f9"
3524+
integrity sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==
3525+
dependencies:
3526+
"@noble/hashes" "~1.8.0"
3527+
"@scure/base" "~1.2.5"
3528+
34593529
"@scure/bip39@^1.4.0":
34603530
version "1.5.0"
34613531
resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.5.0.tgz#c8f9533dbd787641b047984356531d84485f19be"
@@ -6470,6 +6540,17 @@ ethereum-cryptography@^2.0.0:
64706540
"@scure/bip32" "1.4.0"
64716541
"@scure/bip39" "1.3.0"
64726542

6543+
ethereum-cryptography@^3.0.0:
6544+
version "3.2.0"
6545+
resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-3.2.0.tgz#42a04b57834bf536e552b50a70b9ee5057c71dc6"
6546+
integrity sha512-Urr5YVsalH+Jo0sYkTkv1MyI9bLYZwW8BENZCeE1QYaTHETEYx0Nv/SVsWkSqpYrzweg6d8KMY1wTjH/1m/BIg==
6547+
dependencies:
6548+
"@noble/ciphers" "1.3.0"
6549+
"@noble/curves" "1.9.0"
6550+
"@noble/hashes" "1.8.0"
6551+
"@scure/bip32" "1.7.0"
6552+
"@scure/bip39" "1.6.0"
6553+
64736554
ethers@5.7.2:
64746555
version "5.7.2"
64756556
resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e"

0 commit comments

Comments
 (0)