Skip to content

Commit 4bc2b13

Browse files
committed
feat(benchmarks): add redact
1 parent 6e42632 commit 4bc2b13

8 files changed

Lines changed: 434 additions & 8 deletions

File tree

gas-benchmarks/.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ ETH_RPC_URL=https://mainnet.gateway.tenderly.co
22

33
SCROLL_RPC_URL=https://scroll.gateway.tenderly.co
44

5+
SEPOLIA_RPC_URL=https://sepolia.gateway.tenderly.co
6+
57
MONERO_FAIL_NODES_API_URL=https://monero.fail/nodes.json
68

79
MIN_SAMPLES=10

gas-benchmarks/src/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Intmax } from "./intmax/index.js";
44
import { Monero } from "./monero/index.js";
55
import { PrivacyPools } from "./privacy-pools/index.js";
66
import { Railgun } from "./railgun/index.js";
7+
import { Redact } from "./redact/index.js";
78
import { TornadoCash } from "./tornado-cash/index.js";
89
import { db } from "./utils/db.js";
910

@@ -14,6 +15,7 @@ const intmax = new Intmax();
1415
const monero = new Monero();
1516
const hinkal = new Hinkal();
1617
const fluidkey = new Fluidkey();
18+
const redact = new Redact();
1719

1820
await db.read();
1921

@@ -27,6 +29,7 @@ const [
2729
moneroMetrics,
2830
hinkalMetrics,
2931
fluidkeyMetrics,
32+
redactMetrics,
3033
] = await Promise.all([
3134
railgun.benchmark(),
3235
tornadoCash.benchmark(),
@@ -35,6 +38,7 @@ const [
3538
monero.benchmark(),
3639
hinkal.benchmark(),
3740
fluidkey.benchmark(),
41+
redact.benchmark(),
3842
]);
3943

4044
await db.update((data) => {
@@ -85,6 +89,12 @@ await db.update((data) => {
8589
transfer_eth: fluidkeyMetrics.transferEth,
8690
transfer_erc20: fluidkeyMetrics.transferErc20,
8791
};
92+
93+
// eslint-disable-next-line no-param-reassign
94+
data[`${redact.name}_${redact.version}`] = {
95+
encrypt_eth: redactMetrics.encryptEth,
96+
decrypt_eth: redactMetrics.decryptEth,
97+
};
8898
});
8999

90100
const end = Date.now();
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
import { parseAbiItem, type Address } from "viem";
2+
3+
import type { ProtocolConfig } from "../utils/types.js";
4+
5+
/**
6+
* FHERC20 Confidential Ether eETH:
7+
*/
8+
const CONFIDENTIAL_ETHER_PROXY: Address = "0xC132c8a82A24Fe1e491082932e3db4F70Ce95c93";
9+
10+
/**
11+
* ConfidentialETH.shieldNative function (inherited from FHERC20NativeWrapperUpgradeable.sol):
12+
*
13+
* Emits:
14+
* TaskCreated() - Assigned task to encrypt (emitted by Task Manager of Fhenix coprocessor)
15+
* TaskCreated() - Assigned task to encrypt (emitted by Task Manager of Fhenix coprocessor)
16+
* TaskCreated() - Assigned task to encrypt (emitted by Task Manager of Fhenix coprocessor)
17+
* TaskCreated() - Assigned task to encrypt (emitted by Task Manager of Fhenix coprocessor)
18+
* TaskCreated() - Assigned task to encrypt (emitted by Task Manager of Fhenix coprocessor)
19+
* TaskCreated() - Assigned task to encrypt (emitted by Task Manager of Fhenix coprocessor)
20+
* TaskCreated() - Assigned task to encrypt (emitted by Task Manager of Fhenix coprocessor)
21+
* Transfer() - Transfer to simulate a ERC20 transfer with dummy values (emitted by the confidential Eth contract)
22+
* ConfidentialTransfer() - Encrypted token transfer announcement (emitted by the confidential Eth contract)
23+
* ShieldedNative() - Shielded native ETH action (emitted by the confidential Eth contract)
24+
*/
25+
const ENCRYPT_ETH_EVENTS = [
26+
parseAbiItem(
27+
"event TaskCreated(uint256 indexed taskId, string descriptionHash, uint256 maxBudget, uint256 commitDeadline, uint256 revealDeadline)",
28+
),
29+
parseAbiItem(
30+
"event TaskCreated(uint256 indexed taskId, string descriptionHash, uint256 maxBudget, uint256 commitDeadline, uint256 revealDeadline)",
31+
),
32+
parseAbiItem(
33+
"event TaskCreated(uint256 indexed taskId, string descriptionHash, uint256 maxBudget, uint256 commitDeadline, uint256 revealDeadline)",
34+
),
35+
parseAbiItem(
36+
"event TaskCreated(uint256 indexed taskId, string descriptionHash, uint256 maxBudget, uint256 commitDeadline, uint256 revealDeadline)",
37+
),
38+
parseAbiItem(
39+
"event TaskCreated(uint256 indexed taskId, string descriptionHash, uint256 maxBudget, uint256 commitDeadline, uint256 revealDeadline)",
40+
),
41+
parseAbiItem(
42+
"event TaskCreated(uint256 indexed taskId, string descriptionHash, uint256 maxBudget, uint256 commitDeadline, uint256 revealDeadline)",
43+
),
44+
parseAbiItem(
45+
"event TaskCreated(uint256 indexed taskId, string descriptionHash, uint256 maxBudget, uint256 commitDeadline, uint256 revealDeadline)",
46+
),
47+
parseAbiItem("event Transfer(address indexed from, address indexed to, uint256 value)"),
48+
parseAbiItem("event ConfidentialTransfer(address indexed from, address indexed to, bytes32 indexed amount)"),
49+
parseAbiItem("event ShieldedNative(address indexed from, address indexed to, uint256 value)"),
50+
] as const;
51+
52+
/**
53+
* ConfidentialETH.decrypt function:
54+
*
55+
* Emits:
56+
* TaskCreated() - Assigned task to decrypt (emitted by Task Manager of Fhenix coprocessor)
57+
* TaskCreated() - Assigned task to decrypt (emitted by Task Manager of Fhenix coprocessor)
58+
* TaskCreated() - Assigned task to decrypt (emitted by Task Manager of Fhenix coprocessor)
59+
* TaskCreated() - Assigned task to decrypt (emitted by Task Manager of Fhenix coprocessor)
60+
* TaskCreated() - Assigned task to decrypt (emitted by Task Manager of Fhenix coprocessor)
61+
* TaskCreated() - Assigned task to decrypt (emitted by Task Manager of Fhenix coprocessor)
62+
* TaskCreated() - Assigned task to decrypt (emitted by Task Manager of Fhenix coprocessor)
63+
* Transfer() - Transfer to simulate a ERC20 transfer with dummy values (emitted by the confidential Eth contract)
64+
* ConfidentialTransfer() - Encrypted token transfer announcement (emitted by the confidential Eth contract)
65+
* Unshielded() - Unshielded native ETH action (emitted by the confidential Eth contract)
66+
*/
67+
const DECRYPT_ETH_EVENTS = [
68+
parseAbiItem(
69+
"event TaskCreated(uint256 indexed taskId, string descriptionHash, uint256 maxBudget, uint256 commitDeadline, uint256 revealDeadline)",
70+
),
71+
parseAbiItem(
72+
"event TaskCreated(uint256 indexed taskId, string descriptionHash, uint256 maxBudget, uint256 commitDeadline, uint256 revealDeadline)",
73+
),
74+
parseAbiItem(
75+
"event TaskCreated(uint256 indexed taskId, string descriptionHash, uint256 maxBudget, uint256 commitDeadline, uint256 revealDeadline)",
76+
),
77+
parseAbiItem(
78+
"event TaskCreated(uint256 indexed taskId, string descriptionHash, uint256 maxBudget, uint256 commitDeadline, uint256 revealDeadline)",
79+
),
80+
parseAbiItem(
81+
"event TaskCreated(uint256 indexed taskId, string descriptionHash, uint256 maxBudget, uint256 commitDeadline, uint256 revealDeadline)",
82+
),
83+
parseAbiItem(
84+
"event TaskCreated(uint256 indexed taskId, string descriptionHash, uint256 maxBudget, uint256 commitDeadline, uint256 revealDeadline)",
85+
),
86+
parseAbiItem(
87+
"event TaskCreated(uint256 indexed taskId, string descriptionHash, uint256 maxBudget, uint256 commitDeadline, uint256 revealDeadline)",
88+
),
89+
parseAbiItem("event Transfer(address indexed from, address indexed to, uint256 value)"),
90+
parseAbiItem("event ConfidentialTransfer(address indexed from, address indexed to, bytes32 indexed amount)"),
91+
parseAbiItem("event Unshielded(address indexed to, bytes32 indexed amount)"),
92+
] as const;
93+
94+
/**
95+
* ConfidentialETH.claimAllDecrypted function:
96+
*
97+
* Emits:
98+
* ClaimedUnshielded() - Claimed unshielded ETH transfer notification (emitted by the confidential wrapper contract)
99+
*
100+
*/
101+
const CLAIM_DECRYPTED_ETH_EVENTS = [
102+
parseAbiItem(
103+
"event ClaimedUnshielded(address indexed to, bytes32 indexed unshieldRequestId, bytes32 indexed unshieldAmount, uint64 unshieldAmountCleartext)",
104+
),
105+
] as const;
106+
107+
/**
108+
* ConfidentialETH -> FHERC20NativeWrapperUpgradeable -> FHERC20Upgradeable.confidentialTransfer function:
109+
*
110+
* Emits:
111+
* TaskCreated() - Assigned task to decrypt (emitted by Task Manager of Fhenix coprocessor)
112+
* TaskCreated() - Assigned task to decrypt (emitted by Task Manager of Fhenix coprocessor)
113+
* TaskCreated() - Assigned task to decrypt (emitted by Task Manager of Fhenix coprocessor)
114+
* TaskCreated() - Assigned task to decrypt (emitted by Task Manager of Fhenix coprocessor)
115+
* TaskCreated() - Assigned task to decrypt (emitted by Task Manager of Fhenix coprocessor)
116+
* TaskCreated() - Assigned task to decrypt (emitted by Task Manager of Fhenix coprocessor)
117+
* Transfer() - Transfer to simulate a ERC20 transfer with dummy values (emitted by the confidential Eth contract)
118+
* ConfidentialTransfer() - Encrypted token transfer announcement (emitted by the confidential Eth contract)
119+
*
120+
* It can be used by confidential ETH or confidential ERC20 tokens because both are FHERC20 confidential tokens.
121+
*/
122+
const ENCRYPTED_TOKEN_TRANSFER_EVENTS = [
123+
parseAbiItem(
124+
"event TaskCreated(uint256 indexed taskId, string descriptionHash, uint256 maxBudget, uint256 commitDeadline, uint256 revealDeadline)",
125+
),
126+
parseAbiItem(
127+
"event TaskCreated(uint256 indexed taskId, string descriptionHash, uint256 maxBudget, uint256 commitDeadline, uint256 revealDeadline)",
128+
),
129+
parseAbiItem(
130+
"event TaskCreated(uint256 indexed taskId, string descriptionHash, uint256 maxBudget, uint256 commitDeadline, uint256 revealDeadline)",
131+
),
132+
parseAbiItem(
133+
"event TaskCreated(uint256 indexed taskId, string descriptionHash, uint256 maxBudget, uint256 commitDeadline, uint256 revealDeadline)",
134+
),
135+
parseAbiItem(
136+
"event TaskCreated(uint256 indexed taskId, string descriptionHash, uint256 maxBudget, uint256 commitDeadline, uint256 revealDeadline)",
137+
),
138+
parseAbiItem(
139+
"event TaskCreated(uint256 indexed taskId, string descriptionHash, uint256 maxBudget, uint256 commitDeadline, uint256 revealDeadline)",
140+
),
141+
parseAbiItem("event Transfer(address indexed from, address indexed to, uint256 value)"),
142+
parseAbiItem("event ConfidentialTransfer(address indexed from, address indexed to, bytes32 indexed amount)"),
143+
] as const;
144+
145+
/** Redact configuration */
146+
const REDACT_CONFIG = {
147+
name: "redact",
148+
version: "1.0.0",
149+
contracts: [
150+
{
151+
address: CONFIDENTIAL_ETHER_PROXY,
152+
sourceUrl:
153+
"https://github.com/FhenixProtocol/redact/blob/new-version-deployment/packages/hardhat/contracts/ConfidentialETH.sol",
154+
},
155+
],
156+
operations: [
157+
{
158+
functionSourceUrl:
159+
"https://github.com/FhenixProtocol/fhenix-confidential-contracts/blob/0823e57e320c1c72e0caee7faeebc4d3a0710373/contracts/FHERC20/extensions/FHERC20NativeWrapperUpgradeable.sol#L91",
160+
exampleTxUrl:
161+
"https://sepolia.etherscan.io/tx/0xf1758a8d42ef208d4e20b00445a9c6b75aa8ae21c7fa6c5dcbdde78b1efa1ac1",
162+
events: ENCRYPT_ETH_EVENTS,
163+
},
164+
{
165+
functionSourceUrl:
166+
"https://github.com/FhenixProtocol/fhenix-confidential-contracts/blob/0823e57e320c1c72e0caee7faeebc4d3a0710373/contracts/FHERC20/extensions/FHERC20NativeWrapperUpgradeable.sol#L112",
167+
exampleTxUrl:
168+
"https://sepolia.etherscan.io/tx/0xd0ce16c80d911ac3d5a9262f79372cff3b87291253e0163ced5c582a3b87e0cf",
169+
events: DECRYPT_ETH_EVENTS,
170+
},
171+
{
172+
functionSourceUrl:
173+
"https://github.com/FhenixProtocol/fhenix-confidential-contracts/blob/0823e57e320c1c72e0caee7faeebc4d3a0710373/contracts/FHERC20/extensions/FHERC20NativeWrapperUpgradeable.sol#L135",
174+
exampleTxUrl:
175+
"https://sepolia.etherscan.io/tx/0x94b0cd3b3e8c7f53d7b9db497973ffd1b14b2dea3a6ef89f6b5561d775c4b649",
176+
events: CLAIM_DECRYPTED_ETH_EVENTS,
177+
},
178+
{
179+
functionSourceUrl:
180+
"https://github.com/FhenixProtocol/fhenix-confidential-contracts/blob/0823e57e320c1c72e0caee7faeebc4d3a0710373/contracts/FHERC20/FHERC20Upgradeable.sol#L216",
181+
exampleTxUrl:
182+
"https://sepolia.etherscan.io/tx/0x9e2cc152f3c63cca0c02fcda4e2636776d00a304410b62ffcd110a9f4a664bcb",
183+
events: ENCRYPTED_TOKEN_TRANSFER_EVENTS,
184+
},
185+
],
186+
} satisfies ProtocolConfig;
187+
188+
export {
189+
CONFIDENTIAL_ETHER_PROXY,
190+
ENCRYPT_ETH_EVENTS,
191+
DECRYPT_ETH_EVENTS,
192+
CLAIM_DECRYPTED_ETH_EVENTS,
193+
ENCRYPTED_TOKEN_TRANSFER_EVENTS,
194+
REDACT_CONFIG,
195+
};

gas-benchmarks/src/redact/index.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import { sepolia } from "viem/chains";
2+
3+
import type { FeeMetrics } from "../utils/types.js";
4+
5+
import { BLOCK_WINDOW_ETHEREUM_3_DAYS, MIN_SAMPLES } from "../utils/constants.js";
6+
import { getValidTransactions } from "../utils/rpc.js";
7+
import { getAverageMetrics } from "../utils/utils.js";
8+
9+
import {
10+
CLAIM_DECRYPTED_ETH_EVENTS,
11+
CONFIDENTIAL_ETHER_PROXY,
12+
DECRYPT_ETH_EVENTS,
13+
ENCRYPT_ETH_EVENTS,
14+
ENCRYPTED_TOKEN_TRANSFER_EVENTS,
15+
REDACT_CONFIG,
16+
} from "./constants.js";
17+
18+
export class Redact {
19+
readonly name = REDACT_CONFIG.name;
20+
21+
readonly version = REDACT_CONFIG.version;
22+
23+
async benchmark(): Promise<Record<string, FeeMetrics>> {
24+
const [encryptEth, decryptEth] = await Promise.all([this.benchmarkEncryptETH(), this.benchmarkDecryptETH()]);
25+
26+
return { encryptEth, decryptEth };
27+
}
28+
29+
async benchmarkEncryptETH(): Promise<FeeMetrics> {
30+
const receipts = await getValidTransactions({
31+
contractAddress: CONFIDENTIAL_ETHER_PROXY,
32+
events: ENCRYPT_ETH_EVENTS,
33+
chain: sepolia,
34+
blockWindow: BLOCK_WINDOW_ETHEREUM_3_DAYS, // there are a lot of encrypted transfers so use a small block window to rate limit
35+
});
36+
37+
if (receipts.length < MIN_SAMPLES) {
38+
throw new Error(`${this.name} encrypt ETH: receipts (${receipts.length}) < MIN_SAMPLES (${MIN_SAMPLES})`);
39+
}
40+
41+
return getAverageMetrics(receipts);
42+
}
43+
44+
async benchmarkDecryptETH(): Promise<FeeMetrics> {
45+
const [decryptReceipts, claimReceipts] = await Promise.all([
46+
getValidTransactions({
47+
contractAddress: CONFIDENTIAL_ETHER_PROXY,
48+
events: DECRYPT_ETH_EVENTS,
49+
chain: sepolia,
50+
blockWindow: BLOCK_WINDOW_ETHEREUM_3_DAYS, // there are a lot of encrypted transfers so use a small block window to rate limit
51+
}),
52+
getValidTransactions({
53+
contractAddress: CONFIDENTIAL_ETHER_PROXY,
54+
events: CLAIM_DECRYPTED_ETH_EVENTS,
55+
chain: sepolia,
56+
blockWindow: BLOCK_WINDOW_ETHEREUM_3_DAYS, // there are a lot of encrypted transfers so use a small block window to rate limit
57+
}),
58+
]);
59+
60+
if (decryptReceipts.length < MIN_SAMPLES) {
61+
throw new Error(`${this.name} decrypt ETH: receipts (${decryptReceipts.length}) < MIN_SAMPLES (${MIN_SAMPLES})`);
62+
}
63+
64+
if (claimReceipts.length < MIN_SAMPLES) {
65+
throw new Error(
66+
`${this.name} claim decrypted ETH: receipts (${claimReceipts.length}) < MIN_SAMPLES (${MIN_SAMPLES})`,
67+
);
68+
}
69+
70+
const decryptMetrics = getAverageMetrics(decryptReceipts);
71+
const claimMetrics = getAverageMetrics(claimReceipts);
72+
73+
// Sum decrypt and claim metrics to get the final metrics for the end-user to fully exit (decrypt + claim)
74+
const metrics: FeeMetrics = {
75+
averageGasUsed:
76+
decryptMetrics.averageGasUsed === "no-data" || claimMetrics.averageGasUsed === "no-data"
77+
? "no-data"
78+
: Number(decryptMetrics.averageGasUsed) + Number(claimMetrics.averageGasUsed),
79+
averageGasPrice:
80+
decryptMetrics.averageGasPrice === "no-data" || claimMetrics.averageGasPrice === "no-data"
81+
? "no-data"
82+
: Number(decryptMetrics.averageGasPrice) + Number(claimMetrics.averageGasPrice),
83+
averageTxFee:
84+
decryptMetrics.averageTxFee === "no-data" || claimMetrics.averageTxFee === "no-data"
85+
? "no-data"
86+
: Number(decryptMetrics.averageTxFee) + Number(claimMetrics.averageTxFee),
87+
};
88+
89+
return metrics;
90+
}
91+
92+
// TODO: not enough txs at the moment (April 17th 2026). We can activate it later
93+
async benchmarkEncryptedTokenTransfer(): Promise<FeeMetrics> {
94+
const receipts = await getValidTransactions({
95+
contractAddress: CONFIDENTIAL_ETHER_PROXY,
96+
events: ENCRYPTED_TOKEN_TRANSFER_EVENTS,
97+
chain: sepolia,
98+
blockWindow: BLOCK_WINDOW_ETHEREUM_3_DAYS,
99+
});
100+
101+
if (receipts.length < MIN_SAMPLES) {
102+
throw new Error(
103+
`${this.name} encrypted token transfer: receipts (${receipts.length}) < MIN_SAMPLES (${MIN_SAMPLES})`,
104+
);
105+
}
106+
107+
return getAverageMetrics(receipts);
108+
}
109+
}

0 commit comments

Comments
 (0)