-
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.
This is WIP code to check how dispatching to vaults could work
- Loading branch information
Showing
9 changed files
with
288 additions
and
6 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,34 @@ | ||
// SPDX-License-Identifier: GPL-3.0-only | ||
pragma solidity ^0.8.21; | ||
|
||
import "@openzeppelin/contracts/access/Ownable.sol"; | ||
import "@openzeppelin/contracts/interfaces/IERC4626.sol"; | ||
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; | ||
|
||
import "./Router.sol"; | ||
|
||
contract Dispatcher is Router, Ownable { | ||
using SafeERC20 for IERC20; | ||
|
||
Acre acre; | ||
|
||
constructor(Acre _acre) Ownable(msg.sender) { | ||
acre = _acre; | ||
} | ||
|
||
function assetsHolder() public virtual override returns (address){ | ||
return address(acre); | ||
} | ||
|
||
function sharesHolder() public virtual override returns (address){ | ||
return address(this); | ||
} | ||
|
||
function migrateShares(IERC4626[] calldata _vaults) public onlyOwner { | ||
address newDispatcher = address(acre.dispatcher()); | ||
|
||
for (uint i=0; i<_vaults.length; i++) { | ||
_vaults[i].transfer(newDispatcher, _vaults[i].balanceOf(address(this))); | ||
} | ||
} | ||
} |
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,67 @@ | ||
// SPDX-License-Identifier: GPL-3.0-only | ||
pragma solidity ^0.8.21; | ||
|
||
import "@openzeppelin/contracts/interfaces/IERC20.sol"; | ||
import "@openzeppelin/contracts/interfaces/IERC4626.sol"; | ||
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; | ||
import "./Acre.sol"; | ||
|
||
|
||
// TODO: Consider deploying ERC4626RouterBase from the ERC4626 Alliance. | ||
// TODO: Think about adding reentrancy guard | ||
// TODO: Add ACL | ||
|
||
abstract contract Router { | ||
using SafeERC20 for IERC20; | ||
|
||
/// @notice thrown when amount of assets received is below the min set by caller | ||
error MinAmountError(); | ||
|
||
/// @notice thrown when amount of shares received is below the min set by caller | ||
error MinSharesError(); | ||
|
||
/// @notice thrown when amount of assets received is above the max set by caller | ||
error MaxAmountError(); | ||
|
||
/// @notice thrown when amount of shares received is above the max set by caller | ||
error MaxSharesError(); | ||
|
||
|
||
function assetsHolder() public virtual returns (address); | ||
function sharesHolder() public virtual returns (address); | ||
|
||
function deposit( | ||
IERC4626 vault, | ||
uint256 amount, | ||
uint256 minSharesOut | ||
) public returns (uint256 sharesOut) { | ||
IERC20(vault.asset()).safeTransferFrom(assetsHolder(), address(this), amount); | ||
|
||
IERC20(vault.asset()).approve(address(vault), amount); | ||
|
||
if ((sharesOut = vault.deposit(amount, sharesHolder())) < minSharesOut) { | ||
revert MinSharesError(); | ||
} | ||
} | ||
|
||
|
||
function withdraw( | ||
IERC4626 vault, | ||
uint256 amount, | ||
uint256 maxSharesOut | ||
) public returns (uint256 sharesOut) { | ||
if ((sharesOut = vault.withdraw(amount, assetsHolder(), sharesHolder())) > maxSharesOut) { | ||
revert MaxSharesError(); | ||
} | ||
} | ||
|
||
function redeem( | ||
IERC4626 vault, | ||
uint256 shares, | ||
uint256 minAmountOut | ||
) public returns (uint256 amountOut) { | ||
if ((amountOut = vault.redeem(shares, assetsHolder(), sharesHolder())) < minAmountOut) { | ||
revert MinAmountError(); | ||
} | ||
} | ||
} |
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,12 @@ | ||
// SPDX-License-Identifier: GPL-3.0-only | ||
pragma solidity ^0.8.21; | ||
|
||
import "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; | ||
|
||
contract TestERC4626 is ERC4626 { | ||
constructor( | ||
IERC20 asset, | ||
string memory tokenName, | ||
string memory tokenSymbol | ||
) ERC4626(asset) ERC20(tokenName, tokenSymbol) {} | ||
} |
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,24 @@ | ||
import type { HardhatRuntimeEnvironment } from "hardhat/types" | ||
import type { DeployFunction } from "hardhat-deploy/types" | ||
|
||
const func: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { | ||
const { getNamedAccounts, deployments } = hre | ||
const { deployer } = await getNamedAccounts() | ||
|
||
const acre = await deployments.get("Acre") | ||
|
||
await deployments.deploy("Dispatcher", { | ||
from: deployer, | ||
args: [acre.address], | ||
log: true, | ||
waitConfirmations: 1, | ||
}) | ||
|
||
// TODO: Add Etherscan verification | ||
// TODO: Add Tenderly verification | ||
} | ||
|
||
export default func | ||
|
||
func.tags = ["Dispatcher"] | ||
func.dependencies = ["Acre"] |
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,21 @@ | ||
import type { HardhatRuntimeEnvironment } from "hardhat/types" | ||
import type { DeployFunction } from "hardhat-deploy/types" | ||
|
||
const func: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { | ||
const { getNamedAccounts, deployments } = hre | ||
const { deployer, governance } = await getNamedAccounts() | ||
const { log } = deployments | ||
|
||
log(`transferring ownership of AcreRouter contract to ${governance}`) | ||
|
||
await deployments.execute( | ||
"Dispatcher", | ||
{ from: deployer, log: true, waitConfirmations: 1 }, | ||
"transferOwnership", | ||
governance, | ||
) | ||
} | ||
|
||
export default func | ||
|
||
func.tags = ["TransferOwnershipDispatcher"] |
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,109 @@ | ||
import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers" | ||
import { expect } from "chai" | ||
|
||
import type { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers" | ||
|
||
import { ethers } from "hardhat" | ||
import { deployment } from "./helpers/context" | ||
import { getNamedSigner, getUnnamedSigner } from "./helpers/signer" | ||
|
||
import { to1e18 } from "./utils" | ||
|
||
import type { Acre, Dispatcher, TestERC4626, TestERC20 } from "../typechain" | ||
|
||
async function fixture() { | ||
const { tbtc, acre, dispatcher } = await deployment() | ||
|
||
const { governance } = await getNamedSigner() | ||
|
||
const [staker1] = await getUnnamedSigner() | ||
|
||
await acre | ||
.connect(governance) | ||
.upgradeDispatcher(await dispatcher.getAddress()) | ||
|
||
const vault: TestERC4626 = await ethers.deployContract("TestERC4626", [ | ||
await tbtc.getAddress(), | ||
"Test Vault Token", | ||
"vToken", | ||
]) | ||
await vault.waitForDeployment() | ||
|
||
return { acre, tbtc, dispatcher, vault, staker1 } | ||
} | ||
|
||
describe("Dispatcher", () => { | ||
const staker1Amount = to1e18(1000) | ||
|
||
let acre: Acre | ||
let tbtc: TestERC20 | ||
let dispatcher: Dispatcher | ||
let vault: TestERC4626 | ||
|
||
let staker1: HardhatEthersSigner | ||
|
||
before(async () => { | ||
;({ acre, tbtc, dispatcher, vault, staker1 } = await loadFixture(fixture)) | ||
}) | ||
|
||
it("test deposit and withdraw", async () => { | ||
// Mint tBTC for staker. | ||
await tbtc.mint(staker1.address, staker1Amount) | ||
|
||
// Stake tBTC in Acre. | ||
await tbtc.approve(await acre.getAddress(), staker1Amount) | ||
await acre.connect(staker1).deposit(staker1Amount, staker1.address) | ||
|
||
expect(await tbtc.balanceOf(await acre.getAddress())).to.be.equal( | ||
staker1Amount, | ||
) | ||
|
||
const vaultDepositAmount = to1e18(500) | ||
const expectedSharesDeposit = vaultDepositAmount | ||
|
||
await dispatcher.deposit( | ||
await vault.getAddress(), | ||
vaultDepositAmount, | ||
expectedSharesDeposit, | ||
) | ||
|
||
expect(await tbtc.balanceOf(await acre.getAddress())).to.be.equal( | ||
staker1Amount - vaultDepositAmount, | ||
) | ||
expect(await tbtc.balanceOf(await dispatcher.getAddress())).to.be.equal(0) | ||
expect(await tbtc.balanceOf(await vault.getAddress())).to.be.equal( | ||
vaultDepositAmount, | ||
) | ||
|
||
expect(await vault.balanceOf(await acre.getAddress())).to.be.equal(0) | ||
expect(await vault.balanceOf(await dispatcher.getAddress())).to.be.equal( | ||
expectedSharesDeposit, | ||
) | ||
|
||
// // Simulate Vault generating yield. | ||
// const yieldAmount = to1e18(200) | ||
// await tbtc.mint(await vault.getAddress(), yieldAmount) | ||
|
||
// Partial withdrawal. | ||
const amountToWithdraw1 = to1e18(300) | ||
const expectedSharesWithdraw = to1e18(300) | ||
await dispatcher.withdraw( | ||
await vault.getAddress(), | ||
amountToWithdraw1, | ||
expectedSharesWithdraw, | ||
) | ||
|
||
expect(await vault.balanceOf(await acre.getAddress())).to.be.equal(0) | ||
expect(await vault.balanceOf(await dispatcher.getAddress())).to.be.equal( | ||
expectedSharesDeposit - expectedSharesWithdraw, | ||
) | ||
|
||
expect(await tbtc.balanceOf(await acre.getAddress())).to.be.equal( | ||
staker1Amount - vaultDepositAmount + amountToWithdraw1, | ||
) | ||
expect(await tbtc.balanceOf(await dispatcher.getAddress())).to.be.equal(0) | ||
expect(await tbtc.balanceOf(await vault.getAddress())).to.be.equal( | ||
vaultDepositAmount - amountToWithdraw1, | ||
) | ||
}) | ||
}) |
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