Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hardhat tasks #2234

Merged
merged 11 commits into from
Sep 16, 2024
28 changes: 28 additions & 0 deletions contracts/contracts/interfaces/aerodrome/ICLPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,32 @@ 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);
}
}
162 changes: 162 additions & 0 deletions contracts/tasks/aero.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
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");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could, but I'll leave it from addresses.js

const aeroStrat = await resolveContract(
`AerodromeAMOStrategyProxy`,
"AerodromeAMOStrategy"
);
const sugarHelper = await resolveContract(base.sugarHelper, "ISugarHelper");

const Q96 = BigNumber.from(2).pow(96);
const sqrtRatioX96TickLower = BigNumber.from("79224201403219477170569942574"); // -1 tick
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These could be read from the strategy contract. Would make it easier to use the same script for different strategies:
https://github.com/OriginProtocol/origin-dollar/blob/master/contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol#L87

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea. I'll add

const sqrtRatioX96TickHigher = BigNumber.from(
"79228162514264337593543950336"
); // 0 tick

const { tick, sqrtPriceX96 } = await pool.connect(signer).slot0({ blockTag });
const { liquidityGross } = await pool.connect(signer).ticks(-1, { blockTag });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the -1 tick is true for this strategy, might be a different thing for other chain strategies. Available as a constant here: https://github.com/OriginProtocol/origin-dollar/blob/master/contracts/contracts/strategies/aerodrome/AerodromeAMOStrategy.sol#L67

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea. I'll change

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good to know this isn't WETH liquidity in the pool, rather WETH balance/tokens of the pool.
WETH balance = WETH liquidity + unclaimed WETH
Unclaimed WETH can originate from decreasing liquidity in the pool and not yet collecting it. (the decrease + collect are separate calls).
In most cases though the WETH on the pool will probably be the pool's liquidity.
Also I think the WETH swap fees remain on the pool (since those are claimed by the veAreo voters)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add the pool balances so we can see when its different to the actual WETH balance

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought there was a getBalances function but there isn't. It needs to be calculated from the sqrtPriceX96 and liquidity. I'll leave for now but can add if you thinks its useful information

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it is pretty annoying that there isn't a simple way to get that data off of the contract

.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(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these are fine, maybe there should be just additional notice, that these balances include unclaimed WETH/OETH from swap fees and liquidity decreases.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a comment

`Pool WETH : ${formatUnits(poolWethBalance)} (${formatUnits(
poolWethPercentage,
2
)}%), ${poolWethBalance} wei`
);
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
Loading