Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 55 additions & 12 deletions gas-benchmarks/src/fluidkey/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { mainnet } from "viem/chains";

import type { FeeMetrics } from "../utils/types.js";
import type { IAggregatedMetrics } from "./types.js";
import type { TransactionReceipt } from "viem";

import { BLOCK_WINDOW_ETHEREUM_5_WEEKS, BLOCK_WINDOW_ETHEREUM_10_MINUTES, MIN_SAMPLES } from "../utils/constants.js";
import { getValidEthTransfers, getValidTransactions } from "../utils/rpc.js";
Expand All @@ -20,18 +21,27 @@ export class Fluidkey {

readonly version = FLUIDKEY_CONFIG.version;

async benchmark(): Promise<Record<string, FeeMetrics>> {
const [shieldEth, shieldErc20, transferEth, transferErc20] = await Promise.all([
async benchmark(): Promise<IAggregatedMetrics> {
const [shieldEthReceipts, shieldErc20Receipts, transferEthReceipts, transferErc20Receipts] = await Promise.all([
this.benchmarkShieldETH(),
this.benchmarkShieldERC20(),
this.benchmarkTransferETH(),
this.benchmarkTransferERC20(),
]);

return { shieldEth, shieldErc20, transferEth, transferErc20 };
return {
shieldEth: getAverageMetrics(shieldEthReceipts),
shieldErc20: getAverageMetrics(shieldErc20Receipts),
transferEth: getAverageMetrics(transferEthReceipts),
transferErc20: getAverageMetrics(transferErc20Receipts),
anonymitySetSize: {
eth: shieldEthReceipts.length - transferEthReceipts.length,
...this.benchmarkAnonymitySetSizeERC20(shieldErc20Receipts, transferErc20Receipts),
},
};
}

async benchmarkShieldETH(): Promise<FeeMetrics> {
private async benchmarkShieldETH(): Promise<TransactionReceipt[]> {
const receipts = await getValidEthTransfers({
chain: mainnet,
blockWindow: BLOCK_WINDOW_ETHEREUM_10_MINUTES, // TODO: fetch native ETH txs using block window
Expand All @@ -41,10 +51,10 @@ export class Fluidkey {
throw new Error(`${this.name} shield ETH: receipts (${receipts.length}) < MIN_SAMPLES (${MIN_SAMPLES})`);
}

return getAverageMetrics(receipts);
return receipts;
}

async benchmarkShieldERC20(): Promise<FeeMetrics> {
private async benchmarkShieldERC20(): Promise<TransactionReceipt[]> {
const receipts = await getValidTransactions({
contractAddress: USDC_ERC20_TOKEN_ADDRESS,
events: SHIELD_ERC20_EVENTS,
Expand All @@ -56,10 +66,10 @@ export class Fluidkey {
throw new Error(`${this.name} shield ERC20: receipts (${receipts.length}) < MIN_SAMPLES (${MIN_SAMPLES})`);
}

return getAverageMetrics(receipts);
return receipts;
}

async benchmarkTransferETH(): Promise<FeeMetrics> {
private async benchmarkTransferETH(): Promise<TransactionReceipt[]> {
const receipts = await getValidTransactions({
contractAddress: FLUIDKEY_RELAYER_CONTRACT,
events: TRANSFER_ETH_EVENTS,
Expand All @@ -71,10 +81,10 @@ export class Fluidkey {
throw new Error(`${this.name} transfer ETH: receipts (${receipts.length}) < MIN_SAMPLES (${MIN_SAMPLES})`);
}

return getAverageMetrics(receipts);
return receipts;
}

async benchmarkTransferERC20(): Promise<FeeMetrics> {
private async benchmarkTransferERC20(): Promise<TransactionReceipt[]> {
const receipts = await getValidTransactions({
contractAddress: FLUIDKEY_RELAYER_CONTRACT,
events: TRANSFER_ERC20_EVENTS,
Expand All @@ -85,6 +95,39 @@ export class Fluidkey {
throw new Error(`${this.name} transfer ERC20: receipts (${receipts.length}) < MIN_SAMPLES (${MIN_SAMPLES})`);
}

return getAverageMetrics(receipts);
return receipts;
}

private benchmarkAnonymitySetSizeERC20(
shieldReceipts: TransactionReceipt[],
transferReceipts: TransactionReceipt[],
): Record<string, bigint | number> {
const shieldMap = shieldReceipts.reduce((map, receipt) => {
const address = receipt.to;

if (address) {
const count = map.get(address) ?? 0;
map.set(address, count + 1);
}

return map;
}, new Map<string, number>());

const unshieldMap = transferReceipts.reduce((map, receipt) => {
const address = receipt.to;

if (address) {
const count = map.get(address) ?? 0;
map.set(address, count + 1);
}

return map;
}, new Map<string, number>());

return [...shieldMap.entries()].reduce<Record<string, bigint | number>>((acc, [address, count]) => {
acc[address] = count - (unshieldMap.get(address) ?? 0);

return acc;
}, {});
}
}
31 changes: 31 additions & 0 deletions gas-benchmarks/src/fluidkey/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { FeeMetrics } from "../utils/types.js";

/**
* Interface representing the aggregated metrics
*/
export interface IAggregatedMetrics {
/**
* The average fee metrics for the shield ETH operation, including average gas used, average gas price, and average transaction fee.
*/
shieldEth: FeeMetrics;

/**
* The average fee metrics for the shield ERC20 operation, including average gas used, average gas price, and average transaction fee.
*/
shieldErc20: FeeMetrics;

/**
* The average fee metrics for the transfer ETH operation, including average gas used, average gas price, and average transaction fee.
*/
transferEth: FeeMetrics;

/**
* The average fee metrics for the transfer ERC20 operation, including average gas used, average gas price, and average transaction fee.
*/
transferErc20: FeeMetrics;

/**
* The size of the anonymity set by native and ERC20 tokens, which represents the number of transactions that are indistinguishable from each other in terms of privacy.
*/
anonymitySetSize: Record<string, bigint | number>;
}
78 changes: 64 additions & 14 deletions gas-benchmarks/src/hinkal/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { mainnet } from "viem/chains";

import type { FeeMetrics } from "../utils/types.js";
import type { IAggregatedMetrics } from "./types.js";
import type { TransactionReceipt } from "viem";

import { BLOCK_WINDOW_ETHEREUM_5_WEEKS, MIN_SAMPLES } from "../utils/constants.js";
import { getValidTransactions } from "../utils/rpc.js";
Expand All @@ -21,19 +22,35 @@ export class Hinkal {

readonly version = HINKAL_CONFIG.version;

async benchmark(): Promise<Record<string, FeeMetrics>> {
const [shieldEth, unshieldEth, internalTransfer, shieldErc20, unshieldErc20] = await Promise.all([
async benchmark(): Promise<IAggregatedMetrics> {
const [
shieldEthReceipts,
unshieldEthReceipts,
internalTransferReceipts,
shieldErc20Receipts,
unshieldErc20Receipts,
] = await Promise.all([
this.benchmarkShieldETH(),
this.benchmarkUnshieldETH(),
this.benchmarkInternalTransfer(),
this.benchmarkShieldERC20(),
this.benchmarkUnshieldERC20(),
]);

return { shieldEth, unshieldEth, internalTransfer, shieldErc20, unshieldErc20 };
return {
shieldEth: getAverageMetrics(shieldEthReceipts),
unshieldEth: getAverageMetrics(unshieldEthReceipts),
internalTransfer: getAverageMetrics(internalTransferReceipts),
shieldErc20: getAverageMetrics(shieldErc20Receipts),
unshieldErc20: getAverageMetrics(unshieldErc20Receipts),
anonymitySetSize: {
eth: shieldEthReceipts.length - unshieldEthReceipts.length,
...this.benchmarkAnonymitySetSizeERC20(shieldErc20Receipts, unshieldErc20Receipts),
},
};
}

async benchmarkShieldETH(): Promise<FeeMetrics> {
private async benchmarkShieldETH(): Promise<TransactionReceipt[]> {
const receipts = await getValidTransactions({
contractAddress: HINKAL_POOL,
events: SHIELD_ETH_EVENTS,
Expand All @@ -45,10 +62,10 @@ export class Hinkal {
throw new Error(`${this.name} shield ETH: receipts (${receipts.length}) < MIN_SAMPLES (${MIN_SAMPLES})`);
}

return getAverageMetrics(receipts);
return receipts;
}

async benchmarkUnshieldETH(): Promise<FeeMetrics> {
private async benchmarkUnshieldETH(): Promise<TransactionReceipt[]> {
const receipts = await getValidTransactions({
contractAddress: HINKAL_POOL,
events: UNSHIELD_ETH_EVENTS,
Expand All @@ -60,10 +77,10 @@ export class Hinkal {
throw new Error(`${this.name} unshield ETH: receipts (${receipts.length}) < MIN_SAMPLES (${MIN_SAMPLES})`);
}

return getAverageMetrics(receipts);
return receipts;
}

async benchmarkInternalTransfer(): Promise<FeeMetrics> {
private async benchmarkInternalTransfer(): Promise<TransactionReceipt[]> {
const receipts = await getValidTransactions({
contractAddress: HINKAL_POOL,
events: INTERNAL_TRANSFER_EVENTS,
Expand All @@ -75,10 +92,10 @@ export class Hinkal {
throw new Error(`${this.name} internal transfer: receipts (${receipts.length}) < MIN_SAMPLES (${MIN_SAMPLES})`);
}

return getAverageMetrics(receipts);
return receipts;
}

async benchmarkShieldERC20(): Promise<FeeMetrics> {
private async benchmarkShieldERC20(): Promise<TransactionReceipt[]> {
const receipts = await getValidTransactions({
contractAddress: HINKAL_POOL,
events: SHIELD_ERC20_EVENTS,
Expand All @@ -90,10 +107,10 @@ export class Hinkal {
throw new Error(`${this.name} shield ERC20: receipts (${receipts.length}) < MIN_SAMPLES (${MIN_SAMPLES})`);
}

return getAverageMetrics(receipts);
return receipts;
}

async benchmarkUnshieldERC20(): Promise<FeeMetrics> {
private async benchmarkUnshieldERC20(): Promise<TransactionReceipt[]> {
const receipts = await getValidTransactions({
contractAddress: HINKAL_POOL,
events: UNSHIELD_ERC20_EVENTS,
Expand All @@ -105,6 +122,39 @@ export class Hinkal {
throw new Error(`${this.name} unshield ERC20: receipts (${receipts.length}) < MIN_SAMPLES (${MIN_SAMPLES})`);
}

return getAverageMetrics(receipts);
return receipts;
}

private benchmarkAnonymitySetSizeERC20(
shieldReceipts: TransactionReceipt[],
unshieldReceipts: TransactionReceipt[],
): Record<string, bigint | number> {
const shieldMap = shieldReceipts.reduce((map, receipt) => {
const address = receipt.to;

if (address) {
const count = map.get(address) ?? 0;
map.set(address, count + 1);
}

return map;
}, new Map<string, number>());

const unshieldMap = unshieldReceipts.reduce((map, receipt) => {
const address = receipt.to;

if (address) {
const count = map.get(address) ?? 0;
map.set(address, count + 1);
}

return map;
}, new Map<string, number>());

return [...shieldMap.entries()].reduce<Record<string, bigint | number>>((acc, [address, count]) => {
acc[address] = count - (unshieldMap.get(address) ?? 0);

return acc;
}, {});
}
}
36 changes: 36 additions & 0 deletions gas-benchmarks/src/hinkal/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import type { FeeMetrics } from "../utils/types.js";

/**
* Interface representing the aggregated metrics
*/
export interface IAggregatedMetrics {
/**
* The average fee metrics for the shield ERC20 operation, including average gas used, average gas price, and average transaction fee.
*/
shieldErc20: FeeMetrics;

/**
* The average fee metrics for the unshield ERC20 operation, including average gas used, average gas price, and average transaction fee.
*/
unshieldErc20: FeeMetrics;

/**
* The average fee metrics for the internal transfers operation, including average gas used, average gas price, and average transaction fee.
*/
internalTransfer: FeeMetrics;

/**
* The average fee metrics for the shield ETH operation, including average gas used, average gas price, and average transaction fee.
*/
shieldEth: FeeMetrics;

/**
* The average fee metrics for the unshield ETH operation, including average gas used, average gas price, and average transaction fee.
*/
unshieldEth: FeeMetrics;

/**
* The size of the anonymity set by native and ERC20 tokens, which represents the number of transactions that are indistinguishable from each other in terms of privacy.
*/
anonymitySetSize: Record<string, bigint | number>;
}
7 changes: 7 additions & 0 deletions gas-benchmarks/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,29 +43,34 @@ await db.update((data) => {
shield_erc20: railgunMetrics.shieldErc20,
unshield_erc20: railgunMetrics.unshieldErc20,
transfer_erc20: railgunMetrics.transferErc20,
anonymity_set_size: railgunMetrics.anonymitySetSize,
};

// eslint-disable-next-line no-param-reassign
data[`${tornadoCash.name}_${tornadoCash.version}`] = {
shield_eth: tornadoCashMetrics.shieldEth,
unshield_eth: tornadoCashMetrics.unshieldEth,
anonymity_set_size: tornadoCashMetrics.anonymitySetSize,
};

// eslint-disable-next-line no-param-reassign
data[`${privacyPools.name}_${privacyPools.version}`] = {
shield_eth: privacyPoolsMetrics.shieldEth,
unshield_eth: privacyPoolsMetrics.unshieldEth,
anonymity_set_size: privacyPoolsMetrics.anonymitySetSize,
};

// eslint-disable-next-line no-param-reassign
data[`${intmax.name}_${intmax.version}`] = {
deposit_eth: intmaxMetrics.depositEth,
withdraw_eth: intmaxMetrics.withdrawEth,
anonymity_set_size: intmaxMetrics.anonymitySetSize,
};

// eslint-disable-next-line no-param-reassign
data[`${monero.name}_${monero.version}`] = {
transfer: moneroMetrics.transfer,
anonymity_set_size: moneroMetrics.anonymitySetSize,
};

// eslint-disable-next-line no-param-reassign
Expand All @@ -76,6 +81,7 @@ await db.update((data) => {
shield_erc20: hinkalMetrics.shieldErc20,
unshield_erc20: hinkalMetrics.unshieldErc20,
transfer_erc20: hinkalMetrics.internalTransfer,
anonymity_set_size: hinkalMetrics.anonymitySetSize,
};

// eslint-disable-next-line no-param-reassign
Expand All @@ -84,6 +90,7 @@ await db.update((data) => {
shield_erc20: fluidkeyMetrics.shieldErc20,
transfer_eth: fluidkeyMetrics.transferEth,
transfer_erc20: fluidkeyMetrics.transferErc20,
anonymity_set_size: fluidkeyMetrics.anonymitySetSize,
};
});

Expand Down
Loading