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
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,43 @@ import {IAerodromePool} from "../../../../interfaces/periphery/dex/IAerodromePoo
import {IAerodromeRouter} from "../../../../interfaces/periphery/dex/IAerodromeRouter.sol";
import {ZapAerodromeBase} from "./ZapAerodromeBase.sol";

import {IERC20Metadata} from "@openzeppelin/contracts/interfaces/IERC20Metadata.sol";
import {IERC20, SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

contract ZapAerodromePoolUSDC is ZapAerodromeBase {
constructor(address _staking, address _bridge, address _router) ZapAerodromeBase(_staking, _bridge, _router) {
using SafeERC20 for IERC20;
using SafeERC20 for IERC20Metadata;

address public odos;

constructor(
address _staking,
address _bridge,
address _router,
address _odos
) ZapAerodromeBase(_staking, _bridge, _router) {
// nothing
odos = _odos;
}

/**
* @notice Zaps collateral into ZAI LP tokens
* @dev This function is used when the user only has collateral tokens.
* @param collateralAmount The amount of collateral to zap
* @param minLpAmount The minimum amount of LP tokens to stake
* @notice Wrapper function for directly zapping with USDC collateral
* @param collateralAmount The amount of USDC to deposit into the LP
* @param minLpAmount The minimum LP tokens to receive after zapping
*/
function zapIntoLP(uint256 collateralAmount, uint256 minLpAmount) external {
collateral.transferFrom(msg.sender, me, collateralAmount);
function zapIntoLP(uint256 collateralAmount, uint256 minLpAmount) public {
collateral.safeTransferFrom(msg.sender, me, collateralAmount);
_zapIntoLP(collateralAmount, minLpAmount);
}

uint256 price = collateral.balanceOf(address(pool)) * 1e30 / zai.balanceOf(address(pool));
/**
* @notice Internal function to zap collateral into ZAI LP tokens
* @dev Decides which zap function to use based on pool price stability
* @param collateralAmount The amount of USDC available for LP zapping
* @param minLpAmount The minimum LP tokens to receive after zapping
*/
function _zapIntoLP(uint256 collateralAmount, uint256 minLpAmount) internal {
uint256 price = (collateral.balanceOf(address(pool)) * 1e30) / zai.balanceOf(address(pool));
if (price < 90 * 1e16) {
// < 0.99
_zapDepegged(collateralAmount, minLpAmount);
Expand All @@ -40,6 +62,33 @@ contract ZapAerodromePoolUSDC is ZapAerodromeBase {
}
}

/**
* @notice Zaps collateral into ZAI LP tokens with any token by using Odos
* @param swapAsset The asset to swap into USDC using Odos
* @param swapAmount The amount of `swapAsset` to swap
* @param minLpAmount The minimum LP tokens to receive after zapping
* @param odosCallData Encoded Odos swap data
*/
function zapIntoLPWithOdos(
IERC20 swapAsset,
uint256 swapAmount,
uint256 minLpAmount,
bytes memory odosCallData
) external payable {
if (address(swapAsset) != address(0)) {
// Transfer swapAsset from user
swapAsset.safeTransferFrom(msg.sender, me, swapAmount);
// Approve Odos to spend swapAsset and perform the swap
swapAsset.approve(odos, swapAmount);
}

(bool success,) = odos.call{value: msg.value}(odosCallData);
require(success, "Odos swap failed");

// Now we have USDC in contract, call internal zap function
_zapIntoLP(collateral.balanceOf(me), minLpAmount);
}

function _zapDepegged(uint256 collateralAmount, uint256 minLpAmount) internal {
IAerodromeRouter.Route memory route =
IAerodromeRouter.Route({from: address(collateral), to: address(zai), stable: true, factory: factory});
Expand All @@ -49,7 +98,7 @@ contract ZapAerodromePoolUSDC is ZapAerodromeBase {

router.swapExactTokensForTokens(
collateralAmount / 2, // uint256 amountIn,
collateralAmount / 2 * 1e12, // uint256 amountOutMin,
(collateralAmount / 2) * 1e12, // uint256 amountOutMin,
routes, // Route[] calldata routes,
me, // address to,
block.timestamp // uint256 deadline
Expand Down
7 changes: 5 additions & 2 deletions deploy/base/zap-aerodrome-usdc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ async function main(hre: HardhatRuntimeEnvironment) {
assert(hre.network.name === "base", "Wrong network");
const { deployments } = hre;

const ODOS_ROUTER_BASE = "0x19cEeAd7105607Cd444F5ad10dd51356436095a1"

const args = [
(await deployments.get("StakingLPRewards-sUSDZUSDC")).address,
(await deployments.get("L2DepositCollateralL0")).address,
(await deployments.get("AerodromeRouter")).address,
ODOS_ROUTER_BASE
];

const zapD = await deployContract(
Expand All @@ -29,8 +32,8 @@ async function main(hre: HardhatRuntimeEnvironment) {
).address
);

await waitForTx(await usdc.approve(zap.target, MaxUint256));
await waitForTx(await zap.zapIntoLP(1e6, 0));
// await waitForTx(await usdc.approve(zap.target, MaxUint256));
// await waitForTx(await zap.zapIntoLP(1e6, 0));
}

main.tags = ["ZapAerodromePoolUSDC"];
Expand Down
243 changes: 184 additions & 59 deletions deployments/base/ZapAerodromePoolUSDC.json

Large diffs are not rendered by default.

105 changes: 105 additions & 0 deletions scripts/governance/deploy-governance-base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { ZeroAddress } from "ethers";
import { ethers, network, run } from "hardhat";

async function main() {
const SAFE = "0x7427E82f5abCbcA2a45cAfE6e65cBC1FADf9ad9D";
const MAHA_BASE = "0x554bba833518793056CF105E66aBEA330672c0dE";
const WETH_BASE = "0x4200000000000000000000000000000000000006";
const REWARD_DURATION = 86400 * 7; // 7 Days

const TransparentProxy = await ethers.getContractFactory(
"TransparentUpgradeableProxy"
);
const LockerToken = await ethers.getContractFactory("LockerToken");
const OmnichainStakingToken = await ethers.getContractFactory(
"OmnichainStakingToken"
);

// Deploy the implementations
const omnichainStakingTokenImpl = await OmnichainStakingToken.deploy();
const lockerTokenImpl = await LockerToken.deploy();

await omnichainStakingTokenImpl.waitForDeployment();
await lockerTokenImpl.waitForDeployment();

console.log(
`Omnichain Staking Implementation: ${await omnichainStakingTokenImpl.getAddress()}`
);
console.log(
`Locker Token Implementation: ${await lockerTokenImpl.getAddress()}`
);

// Deploy proxies
const omnichainStakingTokenProxy = await TransparentProxy.deploy(
await omnichainStakingTokenImpl.getAddress(),
SAFE,
"0x"
);
const lockerTokenProxy = await TransparentProxy.deploy(
await lockerTokenImpl.getAddress(),
SAFE,
"0x"
);

await omnichainStakingTokenProxy.waitForDeployment();
await lockerTokenProxy.waitForDeployment();

console.log(
`Omnichain Staking Proxy: ${await omnichainStakingTokenProxy.getAddress()}`
);
console.log(`Locker Token Proxy: ${await lockerTokenProxy.getAddress()}`);

// Interacting through proxies
const omnichainStakingToken = await ethers.getContractAt(
"OmnichainStakingToken",
await omnichainStakingTokenProxy.getAddress()
);

const lockerToken = await ethers.getContractAt(
"LockerToken",
await lockerTokenProxy.getAddress()
);

// Initialize the contracts
await lockerToken.init(
MAHA_BASE,
await omnichainStakingToken.getAddress(),
ZeroAddress
);

await omnichainStakingToken.init(
await lockerToken.getAddress(),
WETH_BASE,
MAHA_BASE,
REWARD_DURATION,
SAFE,
ZeroAddress
);

if (network.name !== "hardhat") {
// Verify the implementations
await run("verify:verify", { address: await lockerTokenImpl.getAddress() });
await run("verify:verify", {
address: await omnichainStakingTokenImpl.getAddress(),
});

// Verify the proxies
await run("verify:verify", {
address: await lockerTokenProxy.getAddress(),
constructorArguments: [await lockerTokenImpl.getAddress(), SAFE, "0x"],
});
await run("verify:verify", {
address: await omnichainStakingTokenProxy.getAddress(),
constructorArguments: [
await omnichainStakingTokenImpl.getAddress(),
SAFE,
"0x",
],
});
}
}

main().catch((err) => {
console.error(err);
process.exitCode = 1;
});
59 changes: 59 additions & 0 deletions test/foundry/USDCZapForkTest.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity 0.8.21;

import {ZapAerodromePoolUSDC} from "../../contracts/periphery/zaps/implementations/base/ZapAerodromePoolUSDC.sol";
import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
import {Test} from "forge-std/Test.sol";

contract USDCZapForkTest is Test {
ZapAerodromePoolUSDC public zapOdos;
uint256 public baseMainnetForkId;
string public BASE_RPC_URL = vm.envString("BASE_RPC_URL");
address staking = 0x1097dFe9539350cb466dF9CA89A5e61195A520B0;
address bridge = 0xA07cf1c081F46524A133c1B6E8eE0B5f96A51255;
address odos = 0x19cEeAd7105607Cd444F5ad10dd51356436095a1;
address router = 0xcF77a3Ba9A5CA399B7c97c74d54e5b1Beb874E43;

function setUp() public {
baseMainnetForkId = vm.createFork(BASE_RPC_URL);
vm.selectFork(baseMainnetForkId);
zapOdos = new ZapAerodromePoolUSDC(staking, bridge, router, address(odos));
}

function testInitValues() external view {
assertEq(address(zapOdos.staking()), 0x1097dFe9539350cb466dF9CA89A5e61195A520B0);
assertEq(address(zapOdos.zai()), 0x0A27E060C0406f8Ab7B64e3BEE036a37e5a62853);
assertEq(address(zapOdos.router()), 0xcF77a3Ba9A5CA399B7c97c74d54e5b1Beb874E43);
assertEq(zapOdos.odos(), 0x19cEeAd7105607Cd444F5ad10dd51356436095a1);
}

function testZapIntoLPOdosETH() external {
vm.startPrank(0x1A9CE4fC65b2267bb32d692E17dE54Ff996747D8);
IERC20 swapAsset = IERC20(address(0));
vm.deal(0x1A9CE4fC65b2267bb32d692E17dE54Ff996747D8, 5 ether);
uint256 swapAmount = 0.0001 ether;
uint256 minLpAmount = 0;
bytes memory odosCall =
hex"83bd37f900000004065af3107a40000304c57b00c49b00017882570840A97A490a37bd8Db9e1aE39165bfBd6000000015615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f000000000301020300040101020a0001010201ff00000000000000000000000000000000001db0d0cb84914d09a92ba11d122bab732ac35fe04200000000000000000000000000000000000006000000000000000000000000000000000000000000000000";
zapOdos.zapIntoLPWithOdos{value: swapAmount}(swapAsset, swapAmount, minLpAmount, odosCall);
vm.stopPrank();
}

function testZapIntoODOSToken() external {
address caller = 0x1A9CE4fC65b2267bb32d692E17dE54Ff996747D8;
address ZRO = 0x6985884C4392D348587B19cb9eAAf157F13271cd;
address ZRO_WHALE = 0xF977814e90dA44bFA03b6295A0616a897441aceC;
vm.startPrank(ZRO_WHALE);
IERC20(ZRO).transfer(caller, 200 ether);
vm.stopPrank();
vm.startPrank(caller);
IERC20(ZRO).approve(address(zapOdos), 100 ether);
IERC20 swapAsset = IERC20(ZRO); // ZRO
uint256 swapAmount = 100 ether;
uint256 minLpAmount = 0;
bytes memory odosCall =
hex"83bd37f900016985884c4392d348587b19cb9eaaf157f13271cd000409056bc75e2d631000000414fa581400c49b00017882570840A97A490a37bd8Db9e1aE39165bfBd6000000015615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f000000000903030a013d2d88e4340100010102007fffffdd0160ee122c0a0100030200000a0100040200037b5bea170000020a0200050601040a010107080000260101090601ff000000000000000000000000000000000000000000000000000000000000deac3451b21038b89476ea60c8bb21bdfe97995e6985884c4392d348587b19cb9eaaf157f13271cd899cd88db60c1484ceebbf9b0a91a9a6415d485bcaeedd8f1acf55f2df259afc090d519069f72a2bbf371ea62f6464d092f715f6cd359bd22e24ff514200000000000000000000000000000000000006b94b22332abf5f89877a14cc88f2abc48c34b3dfcbb7c0000ab88b473b1f5afd9ef808440eed33bf4cfd5ba4b8e0475d9a3cfa863e0e18ccf9d3eb25000000000000000000000000";
zapOdos.zapIntoLPWithOdos(swapAsset, swapAmount, minLpAmount, odosCall);
vm.stopPrank();
}
}
8 changes: 6 additions & 2 deletions test/foundry/periphery/zap/ZapAerodromePoolUSDCTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,13 @@ contract ZapAerodromePoolUSDCTest is BaseZaiTest {
IERC20 _staking = IERC20(0x1097dFe9539350cb466dF9CA89A5e61195A520B0);
address _restaking = 0xA07cf1c081F46524A133c1B6E8eE0B5f96A51255;
address _router = 0xcF77a3Ba9A5CA399B7c97c74d54e5b1Beb874E43;
address odos = 0x19cEeAd7105607Cd444F5ad10dd51356436095a1;

ZapAerodromePoolUSDC _zap = new ZapAerodromePoolUSDC(
address(_staking), // lp staking pool
_restaking, // bridge
_router
_router,
odos
);

vm.startPrank(user);
Expand Down Expand Up @@ -74,11 +76,13 @@ contract ZapAerodromePoolUSDCTest is BaseZaiTest {
IERC20 _staking = IERC20(0x1097dFe9539350cb466dF9CA89A5e61195A520B0);
address _restaking = 0xA07cf1c081F46524A133c1B6E8eE0B5f96A51255;
address _router = 0xcF77a3Ba9A5CA399B7c97c74d54e5b1Beb874E43;
address odos = 0x19cEeAd7105607Cd444F5ad10dd51356436095a1;

ZapAerodromePoolUSDC _zap = new ZapAerodromePoolUSDC(
address(_staking), // lp staking pool
_restaking, // bridge
_router
_router,
odos
);

vm.startPrank(user);
Expand Down