Skip to content

Commit

Permalink
Hardhat tasks (#2234)
Browse files Browse the repository at this point in the history
* Added bulk Hardhat tasks exitValidators and removeValidators

* Added base support for vault hardhat tasks

* Added manuallyFixAccounting hardhat task (#2229)

Added simulation Hardhat tasks deployForceEtherSender and forceSend

* manuallyFixAccounting now supports using scaled up amounts
Added mine Hardhat task

* Added snapAero Hardhat task

* Added strategy's tick positions

* More data to snapAero HH task

* Prettier

* Added missing blockTag params

* review comments

* prettier
  • Loading branch information
naddison36 authored Sep 16, 2024
1 parent 8163e3a commit a9676f4
Show file tree
Hide file tree
Showing 9 changed files with 503 additions and 13 deletions.
30 changes: 30 additions & 0 deletions contracts/contracts/interfaces/aerodrome/ICLPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,34 @@ interface ICLPool {
/// @dev This value has no relationship to the total liquidity across all ticks
/// @dev This value includes staked liquidity
function liquidity() external view returns (uint128);

/// @notice Look up information about a specific tick in the pool
/// @param tick The tick to look up
/// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or
/// tick upper,
/// liquidityNet how much liquidity changes when the pool price crosses the tick,
/// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0,
/// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1,
/// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick
/// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from
/// the current tick,
/// secondsOutside the seconds spent on the other side of the tick from the current tick,
/// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise
/// equal to false.
/// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0.
/// In addition, these values are only relative and must be used only in comparison to previous snapshots for
/// a specific position.
function ticks(int24 tick)
external
view
returns (
uint128 liquidityGross,
int128 liquidityNet,
uint256 feeGrowthOutside0X128,
uint256 feeGrowthOutside1X128,
int56 tickCumulativeOutside,
uint160 secondsPerLiquidityOutsideX128,
uint32 secondsOutside,
bool initialized
);
}
19 changes: 19 additions & 0 deletions contracts/contracts/mocks/ForceEtherSender.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract ForceEtherSender {
// Constructor to optionally receive Ether upon deployment
constructor() payable {}

// Function to allow the contract to receive Ether
receive() external payable {}

// Function to self-destruct and force-send Ether to an address
function forceSend(address payable recipient) external {
// Requires that the contract has a balance greater than 0
require(address(this).balance > 0, "No Ether to send");

// selfdestruct sends all Ether held by the contract to the recipient
selfdestruct(recipient);
}
}
167 changes: 167 additions & 0 deletions contracts/tasks/aero.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
const { formatUnits } = require("ethers").utils;
const { BigNumber } = require("ethers");
const { getBlock } = require("./block");
const { resolveAsset, resolveContract } = require("../utils/resolvers");
const { getSigner } = require("../utils/signers");
const { base } = require("../utils/addresses");

const snapAero = async ({ block }) => {
const signer = await getSigner();
const blockTag = await getBlock(block);

const weth = await resolveAsset("WETH");
const vault = await resolveContract("OETHBaseVaultProxy", "IVault");
const oeth = await resolveContract("OETHBaseProxy", "OETHBase");
const pool = await resolveContract(base.aerodromeOETHbWETHClPool, "ICLPool");
const aeroStrat = await resolveContract(
`AerodromeAMOStrategyProxy`,
"AerodromeAMOStrategy"
);
const sugarHelper = await resolveContract(base.sugarHelper, "ISugarHelper");

const Q96 = BigNumber.from(2).pow(96);
const sqrtRatioX96TickLower = await aeroStrat
.connect(signer)
.sqrtRatioX96TickLower({ blockTag });
const sqrtRatioX96TickHigher = await aeroStrat
.connect(signer)
.sqrtRatioX96TickHigher({ blockTag });
const lowerTick = await aeroStrat.connect(signer).lowerTick({ blockTag });

const { tick, sqrtPriceX96 } = await pool.connect(signer).slot0({ blockTag });
const { liquidityGross } = await pool
.connect(signer)
.ticks(lowerTick, { blockTag });
const { amount0: tickWethBalance, amount1: tickOethBalance } =
await sugarHelper
.connect(signer)
.getAmountsForLiquidity(
sqrtPriceX96,
sqrtRatioX96TickLower,
sqrtRatioX96TickHigher,
liquidityGross,
{ blockTag }
);

// Pool balances
const poolPrice = sqrtPriceX96
.mul(sqrtPriceX96)
.mul(10000000000)
.div(Q96)
.div(Q96);
const poolWethBalance = await weth
.connect(signer)
.balanceOf(base.aerodromeOETHbWETHClPool, { blockTag });
const poolOethBalance = await oeth
.connect(signer)
.balanceOf(base.aerodromeOETHbWETHClPool, { blockTag });
const poolTotal = poolWethBalance.add(poolOethBalance);
const poolWethPercentage = poolWethBalance.mul(10000).div(poolTotal);
const poolOethPercentage = poolOethBalance.mul(10000).div(poolTotal);

// Tick balances
const tickTotal = tickWethBalance.add(tickOethBalance);
const tickWethPercentage = tickWethBalance.mul(10000).div(tickTotal);
const tickOethPercentage = tickOethBalance.mul(10000).div(tickTotal);
const tickTotalPercentage = tickTotal.mul(10000).div(poolTotal);

// Strategy's tick position
const {
_amountWeth: tickStratWethBalance,
_amountOethb: tickStratOethBalance,
} = await aeroStrat.getPositionPrincipal();
const tickStratTotal = tickStratWethBalance.add(tickStratOethBalance);
const tickStratWethPercentage = tickStratWethBalance
.mul(10000)
.div(tickStratTotal);
const tickStratOethPercentage = tickStratOethBalance
.mul(10000)
.div(tickStratTotal);
const tickStratTotalOfTickPercentage = tickStratTotal
.mul(10000)
.div(tickTotal);
const tickStratTotalOfPoolPercentage = tickStratTotal
.mul(10000)
.div(poolTotal);

const checkBalance = await aeroStrat
.connect(signer)
.checkBalance(weth.address, { blockTag });

const vaultWethBalance = await weth
.connect(signer)
.balanceOf(vault.address, { blockTag });

// Pool balances
console.log(
`Pool price : ${formatUnits(poolPrice, 10)} OETHb/WETH, ${tick} tick`
);
console.log(
`Pool WETH : ${formatUnits(poolWethBalance)} (${formatUnits(
poolWethPercentage,
2
)}%), ${poolWethBalance} wei (includes unclaimed WETH)`
);
console.log(
`Pool OETH : ${formatUnits(poolOethBalance)} (${formatUnits(
poolOethPercentage,
2
)}%), ${poolOethBalance} wei`
);
console.log(`Pool total : ${formatUnits(poolTotal)}`);

// Tick balances
console.log(
`\nTick WETH : ${formatUnits(tickWethBalance)} (${formatUnits(
tickWethPercentage,
2
)}%), ${tickWethBalance} wei`
);
console.log(
`Tick OETH : ${formatUnits(tickOethBalance)} (${formatUnits(
tickOethPercentage,
2
)}%), ${tickOethBalance} wei`
);
console.log(
`Tick total : ${formatUnits(tickStratTotal)} ${formatUnits(
tickTotalPercentage,
2
)}% of pool`
);

// Strategy's tick position
console.log(
`\nTick strat WETH : ${formatUnits(tickStratWethBalance)} (${formatUnits(
tickStratWethPercentage,
2
)}%), ${poolWethBalance} wei`
);
console.log(
`Tick strat OETH : ${formatUnits(tickStratOethBalance)} (${formatUnits(
tickStratOethPercentage,
2
)}%), ${poolOethBalance} wei`
);
console.log(
`Tick strat total : ${formatUnits(tickStratTotal)} ${formatUnits(
tickStratTotalOfTickPercentage,
2
)}% of tick, ${formatUnits(tickStratTotalOfPoolPercentage, 2)}% of pool`
);

console.log(
`\nStrategy balance : ${formatUnits(
checkBalance
)} ether, ${checkBalance} wei`
);
console.log(
`Vault WETH : ${formatUnits(
vaultWethBalance
)}, ${vaultWethBalance} wei`
);
};

module.exports = {
snapAero,
};
8 changes: 8 additions & 0 deletions contracts/tasks/block.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const { mine } = require("@nomicfoundation/hardhat-network-helpers");

const log = require("../utils/logger")("task:block");

async function getBlock(block) {
Expand Down Expand Up @@ -26,7 +28,13 @@ async function getDiffBlocks(taskArguments) {
};
}

async function advanceBlocks(blocks) {
log(`Advancing ${blocks} blocks`);
await mine(blocks);
}

module.exports = {
advanceBlocks,
getBlock,
getDiffBlocks,
};
43 changes: 43 additions & 0 deletions contracts/tasks/simulation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const { formatUnits } = require("ethers").utils;
const { getSigner } = require("../utils/signers");
const { logTxDetails } = require("../utils/txLogger");

const log = require("../utils/logger")("task:simulation");

const deployForceEtherSender = async () => {
const signer = await getSigner();

log(`About to deploy the ForceEtherSender contract`);

// Get the contract factory
const ForceEtherSenderFactory = await ethers.getContractFactory(
"ForceEtherSender",
signer
);

// Deploy the contract with an initial message
const forceEtherSender = await ForceEtherSenderFactory.deploy();

// Wait for the contract to be deployed
await forceEtherSender.deployed();

log("ForceEtherSender contract deployed to:", forceEtherSender.address);
};

const forceSend = async ({ sender, recipient }) => {
const signer = await getSigner();
const balance = await ethers.provider.getBalance(sender);
log(`About to forceSend ${formatUnits(balance)} ETH to ${recipient}`);

const forceEtherSender = await ethers.getContractAt(
"ForceEtherSender",
sender
);
const tx = await forceEtherSender.connect(signer).forceSend(recipient);
await logTxDetails(tx, "forceSend");
};

module.exports = {
deployForceEtherSender,
forceSend,
};
Loading

0 comments on commit a9676f4

Please sign in to comment.