-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add gas reports simulations (#95)
* feat: compound borrow gas profiling Signed-off-by: Reinis Martinsons <reinis@umaproject.org> * fix Signed-off-by: Reinis Martinsons <reinis@umaproject.org> * fix Signed-off-by: Reinis Martinsons <reinis@umaproject.org> * feat: add compound liquidiation report Signed-off-by: Pablo Maldonado <pablo@umaproject.org> * feat: add aave v2 liquidation gas report Signed-off-by: Pablo Maldonado <pablo@umaproject.org> * feat: aave v3 liquidation Signed-off-by: Pablo Maldonado <pablo@umaproject.org> * refactor: format Signed-off-by: Pablo Maldonado <pablo@umaproject.org> * feat: aave v3 borrow Signed-off-by: Pablo Maldonado <pablo@umaproject.org> --------- Signed-off-by: Reinis Martinsons <reinis@umaproject.org> Signed-off-by: Pablo Maldonado <pablo@umaproject.org> Co-authored-by: Reinis Martinsons <reinis@umaproject.org>
- Loading branch information
1 parent
d580565
commit 9404cf7
Showing
8 changed files
with
980 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
import { providers, utils } from "ethers"; | ||
import { | ||
createTenderlyFork, | ||
deleteTenderlyFork, | ||
findForkByDescription, | ||
getTenderlyFork, | ||
setForkSimulationDescription, | ||
shareTenderlyFork, | ||
} from "../TenderlyHelpers/TenderlyFork"; | ||
import { | ||
TenderlySimulationResult, | ||
simulateTenderlyTx, | ||
} from "../TenderlyHelpers/TenderlySimulation"; | ||
// Have to import TestedOevShare manually since it is not unique. | ||
import { TestedOevShare__factory } from "../../contract-types/factories/AaveV2.Liquidation.sol/TestedOevShare__factory"; | ||
|
||
// Common constants. | ||
const blockNumber = 18426914; | ||
const chainId = 1; | ||
|
||
// Used ABIs. | ||
const aaveOracleAbi = [ | ||
"function setAssetSources(address[] memory assets, address[] memory sources)", | ||
]; | ||
|
||
const borrowCall = async ( | ||
forkTimestamp: number, | ||
forkId: string, | ||
rootId?: string // If not provided, the simulation starts off the fork initial state. | ||
): Promise<TenderlySimulationResult> => { | ||
// Borrow tx | ||
// https://etherscan.io/tx/0x99751e7c2114bfc74d7effb6114a7d752c7573396a5d6456ee7b56497132d474 | ||
let simulation: TenderlySimulationResult; | ||
simulation = await simulateTenderlyTx({ | ||
chainId, | ||
from: "0x2a111934d990668e705c85da0e976db06281ef0a", | ||
to: "0x7d2768de32b0b80b7a3454c06bdac94a69ddc7a9", | ||
value: "0", | ||
input: | ||
"0xa415bcad000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000000000000000000000000000000000000077359400000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a111934d990668e705c85da0e976db06281ef0a", | ||
timestampOverride: forkTimestamp, | ||
fork: { id: forkId, root: rootId }, | ||
description: "borrowCall", | ||
}); | ||
|
||
return simulation; | ||
}; | ||
|
||
const regularAaveV2Borrow = async (): Promise<number> => { | ||
// Create and share new fork (delete the old one if it exists). | ||
const alias = "Regular AAVE V2 Borrow"; | ||
const description = "Generated: " + utils.keccak256(utils.toUtf8Bytes(alias)); | ||
const existingFork = await findForkByDescription(description); | ||
if (existingFork) await deleteTenderlyFork(existingFork.id); | ||
const fork = await createTenderlyFork({ | ||
chainId, | ||
alias, | ||
description, | ||
blockNumber, | ||
txIndex: 125, | ||
}); | ||
const forkUrl = await shareTenderlyFork(fork.id); | ||
|
||
// Get provider, accounts and start time of the fork. | ||
const provider = new providers.StaticJsonRpcProvider(fork.rpcUrl); | ||
const forkTimestamp = (await provider.getBlock(blockNumber)).timestamp; | ||
|
||
// Open user position. | ||
const simulation = await borrowCall(forkTimestamp, fork.id); // Start off the initial fork state. | ||
|
||
console.log("Simulated " + alias + " in " + simulation.resultUrl.url); | ||
console.log(" Consumed gas: " + simulation.gasUsed); | ||
console.log(" Fork URL: " + forkUrl + "\n"); | ||
|
||
return simulation.gasUsed; | ||
}; | ||
|
||
const oevShareAaveV2Borrow = async (): Promise<number> => { | ||
// Create and share new fork (delete the old one if it exists). | ||
const alias = "OEVShare AAVE V2 Borrow"; | ||
const description = | ||
"Genereated: " + utils.keccak256(utils.toUtf8Bytes(alias)); | ||
const existingFork = await findForkByDescription(description); | ||
if (existingFork) await deleteTenderlyFork(existingFork.id); | ||
let fork = await createTenderlyFork({ | ||
chainId, | ||
alias, | ||
description, | ||
blockNumber, | ||
txIndex: 125, | ||
}); | ||
const forkUrl = await shareTenderlyFork(fork.id); | ||
|
||
// Get provider, accounts and start time of the fork. | ||
const provider = new providers.StaticJsonRpcProvider(fork.rpcUrl); | ||
const [ownerAddress, userAddress, unlockerAddress] = | ||
await provider.listAccounts(); // These should have 100 ETH balance. | ||
const ownerSigner = provider.getSigner(ownerAddress); | ||
const forkTimestamp = (await provider.getBlock(blockNumber)).timestamp; | ||
|
||
// Deploy OevShare. | ||
const testedOevShareFactory = new TestedOevShare__factory(ownerSigner); | ||
const testedOevShare = await testedOevShareFactory.deploy( | ||
"0xEe9F2375b4bdF6387aa8265dD4FB8F16512A1d46", | ||
18 | ||
); | ||
await testedOevShare.deployTransaction.wait(); | ||
fork = await getTenderlyFork(fork.id); // Refresh to get head id since we submitted tx through RPC. | ||
if (!fork.headId) throw new Error("Fork head id not found."); | ||
await setForkSimulationDescription(fork.id, fork.headId, "Deploy OevShare"); | ||
|
||
// Enable unlocker on TestedOevShare. | ||
const setUnlockerInput = testedOevShareFactory.interface.encodeFunctionData( | ||
"setUnlocker", | ||
[unlockerAddress, true] | ||
); | ||
let simulation = await simulateTenderlyTx({ | ||
chainId, | ||
from: ownerAddress, | ||
to: testedOevShare.address, | ||
input: setUnlockerInput, | ||
timestampOverride: forkTimestamp, | ||
fork: { id: fork.id, root: fork.headId }, | ||
description: "Enable unlocker on OEVShare", | ||
}); | ||
|
||
// setOevShareAsAaveSource | ||
const aaveOracleInterface = new utils.Interface(aaveOracleAbi); | ||
const aaveOracleCallData = aaveOracleInterface.encodeFunctionData( | ||
"setAssetSources", | ||
[["0xdac17f958d2ee523a2206206994597c13d831ec7"], [testedOevShare.address]] | ||
); | ||
|
||
simulation = await simulateTenderlyTx({ | ||
chainId, | ||
from: "0xEE56e2B3D491590B5b31738cC34d5232F378a8D5", //aaveOracle owner | ||
to: "0xA50ba011c48153De246E5192C8f9258A2ba79Ca9", //aaveOracle | ||
input: aaveOracleCallData, | ||
timestampOverride: forkTimestamp, | ||
fork: { id: fork.id, root: simulation.id }, | ||
description: "Change OEVShare as Aave source", | ||
}); | ||
|
||
// Unlock latest value. | ||
const unlockLatestValueInput = | ||
testedOevShareFactory.interface.encodeFunctionData("unlockLatestValue"); | ||
simulation = await simulateTenderlyTx({ | ||
chainId, | ||
from: unlockerAddress, | ||
to: testedOevShare.address, | ||
input: unlockLatestValueInput, | ||
timestampOverride: forkTimestamp, | ||
fork: { id: fork.id, root: simulation.id }, | ||
description: "Unlock latest value on OEVShare", | ||
}); | ||
|
||
// Open user position. | ||
simulation = await borrowCall(forkTimestamp, fork.id, simulation.id); | ||
|
||
console.log("Simulated " + alias + " in " + simulation.resultUrl.url); | ||
console.log(" Consumed gas: " + simulation.gasUsed); | ||
console.log(" Fork URL: " + forkUrl + "\n"); | ||
|
||
return simulation.gasUsed; | ||
}; | ||
|
||
export const aaveV2Borrow = async () => { | ||
console.log("AAVE V2 Borrow gas comparison with unlock:\n"); | ||
|
||
const regularAaveV2BorrowGas = await regularAaveV2Borrow(); | ||
const oevShareAaveV2BorrowGas = await oevShareAaveV2Borrow(); | ||
const gasDiff = oevShareAaveV2BorrowGas - regularAaveV2BorrowGas; | ||
|
||
console.log("Gas difference: " + gasDiff); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
import { Contract, providers, utils } from "ethers"; | ||
import { | ||
createTenderlyFork, | ||
deleteTenderlyFork, | ||
findForkByDescription, | ||
getTenderlyFork, | ||
setForkSimulationDescription, | ||
shareTenderlyFork, | ||
} from "../TenderlyHelpers/TenderlyFork"; | ||
import { | ||
simulateTenderlyTx, | ||
TenderlySimulationResult, | ||
} from "../TenderlyHelpers/TenderlySimulation"; | ||
import { UniswapAnchoredViewDestinationAdapter__factory } from "../../contract-types"; | ||
// Have to import TestedOevShare manually since it is not unique. | ||
import { TestedOevShare__factory } from "../../contract-types/factories/AaveV2.Liquidation.sol/TestedOevShare__factory"; | ||
|
||
// Common constants. | ||
const blockNumber = 17937311; | ||
const chainId = 1; | ||
|
||
// Used ABIs. | ||
const aaveV2LendingPoolAbi = [ | ||
"function liquidationCall(address collateralAsset, address debtAsset, address user, uint256 debtToCover, bool receiveAToken)", | ||
"function getReserveData(address asset) returns (uint256 totalLiquidity, uint256 availableLiquidity, uint256 totalBorrowsStable, uint256 totalBorrowsVariable, uint256 liquidityRate, uint256 variableBorrowRate, uint256 stableBorrowRate, uint256 averageStableBorrowRate, uint256 utilizationRate, uint256 liquidityIndex, uint256 variableBorrowIndex, address aTokenAddress, uint40 lastUpdateTimestamp)", | ||
"function getUserAccountData(address user) returns (uint256 totalCollateralETH, uint256 totalDebtETH, uint256 availableBorrowsETH, uint256 currentLiquidationThreshold, uint256 ltv, uint256 healthFactor)", | ||
]; | ||
|
||
const aaveOracleAbi = [ | ||
"function setAssetSources(address[] memory assets, address[] memory sources)", | ||
]; | ||
|
||
const liquidationCall = async ( | ||
forkTimestamp: number, | ||
forkId: string, | ||
rootId?: string // If not provided, the simulation starts off the fork initial state. | ||
): Promise<TenderlySimulationResult> => { | ||
// Post collateral. | ||
let simulation: TenderlySimulationResult; | ||
const lendingPoolInterface = new utils.Interface(aaveV2LendingPoolAbi); | ||
const liquidationCallData = lendingPoolInterface.encodeFunctionData( | ||
"liquidationCall", | ||
[ | ||
"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", | ||
"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", | ||
"0x6e3aa85db95bba36276a37ed93b12b7ab0782afb", | ||
"148912478614", | ||
true, | ||
] | ||
); | ||
simulation = await simulateTenderlyTx({ | ||
chainId, | ||
from: "0x796d37daf7cdc455e023be793d0daa6240707069", | ||
to: "0x7d2768de32b0b80b7a3454c06bdac94a69ddc7a9", | ||
value: "0", | ||
input: liquidationCallData, | ||
timestampOverride: forkTimestamp, | ||
fork: { id: forkId, root: rootId }, | ||
description: "liquidationCall", | ||
}); | ||
|
||
return simulation; | ||
}; | ||
|
||
const regularAaveV2Liquidation = async (): Promise<number> => { | ||
// Create and share new fork (delete the old one if it exists). | ||
const alias = "Regular AaveV2 Liquidation"; | ||
const description = "Generated: " + utils.keccak256(utils.toUtf8Bytes(alias)); | ||
const existingFork = await findForkByDescription(description); | ||
if (existingFork) await deleteTenderlyFork(existingFork.id); | ||
const fork = await createTenderlyFork({ | ||
chainId, | ||
alias, | ||
description, | ||
blockNumber, | ||
txIndex:1, | ||
}); | ||
const forkUrl = await shareTenderlyFork(fork.id); | ||
|
||
// Get provider, accounts and start time of the fork. | ||
const provider = new providers.StaticJsonRpcProvider(fork.rpcUrl); | ||
const forkTimestamp = (await provider.getBlock(blockNumber)).timestamp; | ||
|
||
// Open user position. | ||
const simulation = await liquidationCall(forkTimestamp, fork.id); // Start off the initial fork state. | ||
|
||
console.log("Simulated " + alias + " in " + simulation.resultUrl.url); | ||
console.log(" Consumed gas: " + simulation.gasUsed); | ||
console.log(" Fork URL: " + forkUrl + "\n"); | ||
|
||
return simulation.gasUsed; | ||
}; | ||
|
||
const oevShareAaveV2Liquidation = async (): Promise<number> => { | ||
// Create and share new fork (delete the old one if it exists). | ||
const alias = "OEVShare AAVE V2 Liquidation"; | ||
const description = | ||
"Genereated: " + utils.keccak256(utils.toUtf8Bytes(alias)); | ||
const existingFork = await findForkByDescription(description); | ||
if (existingFork) await deleteTenderlyFork(existingFork.id); | ||
let fork = await createTenderlyFork({ | ||
chainId, | ||
alias, | ||
description, | ||
blockNumber, | ||
txIndex:1, | ||
}); | ||
const forkUrl = await shareTenderlyFork(fork.id); | ||
|
||
// Get provider, accounts and start time of the fork. | ||
const provider = new providers.StaticJsonRpcProvider(fork.rpcUrl); | ||
const [ownerAddress, userAddress, unlockerAddress] = | ||
await provider.listAccounts(); // These should have 100 ETH balance. | ||
const ownerSigner = provider.getSigner(ownerAddress); | ||
const forkTimestamp = (await provider.getBlock(blockNumber)).timestamp; | ||
|
||
// Deploy OevShare. | ||
const testedOevShareFactory = new TestedOevShare__factory(ownerSigner); | ||
const testedOevShare = await testedOevShareFactory.deploy( | ||
"0x8e0b7e6062272B5eF4524250bFFF8e5Bd3497757", | ||
18 | ||
); | ||
await testedOevShare.deployTransaction.wait(); | ||
fork = await getTenderlyFork(fork.id); // Refresh to get head id since we submitted tx through RPC. | ||
if (!fork.headId) throw new Error("Fork head id not found."); | ||
await setForkSimulationDescription(fork.id, fork.headId, "Deploy OevShare"); | ||
|
||
// Enable unlocker on TestedOevShare. | ||
const setUnlockerInput = testedOevShareFactory.interface.encodeFunctionData( | ||
"setUnlocker", | ||
[unlockerAddress, true] | ||
); | ||
let simulation = await simulateTenderlyTx({ | ||
chainId, | ||
from: ownerAddress, | ||
to: testedOevShare.address, | ||
input: setUnlockerInput, | ||
timestampOverride: forkTimestamp, | ||
fork: { id: fork.id, root: fork.headId }, | ||
description: "Enable unlocker on OEVShare", | ||
}); | ||
|
||
// setOevShareAsAaveSource | ||
const aaveOracleInterface = new utils.Interface(aaveOracleAbi); | ||
const aaveOracleCallData = aaveOracleInterface.encodeFunctionData( | ||
"setAssetSources", | ||
[["0x57Ab1ec28D129707052df4dF418D58a2D46d5f51"], [testedOevShare.address]] | ||
); | ||
|
||
simulation = await simulateTenderlyTx({ | ||
chainId, | ||
from: "0xEE56e2B3D491590B5b31738cC34d5232F378a8D5", //aaveOracle owner | ||
to: "0xA50ba011c48153De246E5192C8f9258A2ba79Ca9", //aaveOracle | ||
input: aaveOracleCallData, | ||
timestampOverride: forkTimestamp, | ||
fork: { id: fork.id, root: simulation.id }, | ||
description: "Change OEVShare as Aave source", | ||
}); | ||
|
||
// Unlock latest value. | ||
const unlockLatestValueInput = | ||
testedOevShareFactory.interface.encodeFunctionData("unlockLatestValue"); | ||
simulation = await simulateTenderlyTx({ | ||
chainId, | ||
from: unlockerAddress, | ||
to: testedOevShare.address, | ||
input: unlockLatestValueInput, | ||
timestampOverride: forkTimestamp, | ||
fork: { id: fork.id, root: simulation.id }, | ||
description: "Unlock latest value on OEVShare", | ||
}); | ||
|
||
// Open user position. | ||
simulation = await liquidationCall(forkTimestamp, fork.id, simulation.id); | ||
|
||
console.log("Simulated " + alias + " in " + simulation.resultUrl.url); | ||
console.log(" Consumed gas: " + simulation.gasUsed); | ||
console.log(" Fork URL: " + forkUrl + "\n"); | ||
|
||
return simulation.gasUsed; | ||
}; | ||
|
||
export const aaveV2Liquidation = async () => { | ||
console.log("AAVE V2 Liquidation gas comparison with unlock:\n"); | ||
|
||
const regularAaveV2LiquidationGas = await regularAaveV2Liquidation(); | ||
const oevShareAaveV2LiquidationGas = await oevShareAaveV2Liquidation(); | ||
const gasDiff = oevShareAaveV2LiquidationGas - regularAaveV2LiquidationGas; | ||
|
||
console.log("Gas difference: " + gasDiff); | ||
}; |
Oops, something went wrong.