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

Feature: donate to MeToken balanceLocked #106

Merged
merged 7 commits into from
Jan 31, 2022
Merged
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
18 changes: 18 additions & 0 deletions contracts/Foundry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,24 @@ contract Foundry is IFoundry, Ownable, Initializable {
);
}

function donate(address _meToken, uint256 _assetsDeposited)
external
override
{
Details.MeToken memory meToken_ = meTokenRegistry.getDetails(_meToken);
Details.Hub memory hub_ = hub.getDetails(meToken_.hubId);
require(meToken_.migration == address(0), "meToken resubscribing");

IVault vault = IVault(hub_.vault);
address asset = hub_.asset;

vault.handleDeposit(msg.sender, asset, _assetsDeposited, 0);
pegahcarter marked this conversation as resolved.
Show resolved Hide resolved

meTokenRegistry.updateBalanceLocked(true, _meToken, _assetsDeposited);

emit Donate(_meToken, asset, msg.sender, _assetsDeposited);
}

// NOTE: for now this does not include fees
function _calculateMeTokensMinted(
address _meToken,
Expand Down
17 changes: 17 additions & 0 deletions contracts/interfaces/IFoundry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@ interface IFoundry {
uint256 _assetsReturned
);

/// @notice Event of donating to meToken owner
/// @param _meToken address of meToken burned
/// @param _asset address of asset returned
/// @param _donor address donating the asset
/// @param _assetsDeposited amount of assets to c
event Donate(
address _meToken,
address _asset,
address _donor,
uint256 _assetsDeposited
);

/// @notice Mint a meToken by depositing the underlying asset
/// @param _meToken address of meToken to mint
/// @param _assetsDeposited amount of assets to deposit
Expand All @@ -55,4 +67,9 @@ interface IFoundry {
uint256 _meTokensBurned,
address _recipient
) external;

/// @notice Donate a meToken's underlying asset to its owner
/// @param _meToken address of meToken to burn
/// @param _assetsDeposited amount of asset to donate
function donate(address _meToken, uint256 _assetsDeposited) external;
}
6 changes: 6 additions & 0 deletions contracts/migrations/UniswapSingleTransferMigration.sol
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ contract UniswapSingleTransferMigration is ReentrancyGuard, Vault, IMigration {
{
require(msg.sender == address(meTokenRegistry), "!meTokenRegistry");

Details.MeToken memory meToken_ = meTokenRegistry.getDetails(_meToken);
Details.Hub memory hub_ = hub.getDetails(meToken_.hubId);
Details.Hub memory targetHub_ = hub.getDetails(meToken_.targetHubId);

require(hub_.asset != targetHub_.asset, "same asset");
pegahcarter marked this conversation as resolved.
Show resolved Hide resolved

(uint256 soonest, uint24 fee) = abi.decode(
_encodedArgs,
(uint256, uint24)
Expand Down
246 changes: 240 additions & 6 deletions test/contracts/Foundry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ import { MeToken } from "../../artifacts/types/MeToken";
import { expect } from "chai";
import { UniswapSingleTransferMigration } from "../../artifacts/types/UniswapSingleTransferMigration";
import { hubSetup } from "../utils/hubSetup";
import { ICurve } from "../../artifacts/types";
import { ICurve, SameAssetTransferMigration } from "../../artifacts/types";

const setup = async () => {
describe("Foundry.sol", () => {
let DAI: string;
let DAIWhale: string;
let daiHolder: Signer;
let WETH: string;
let dai: ERC20;
let weth: ERC20;
let account0: SignerWithAddress;
let account1: SignerWithAddress;
let account2: SignerWithAddress;
Expand All @@ -46,6 +46,8 @@ const setup = async () => {
let singleAssetVault: SingleAssetVault;
let migrationRegistry: MigrationRegistry;
let curveRegistry: CurveRegistry;
let encodedCurveDetails: string;
let encodedVaultArgs: string;

const hubId = 1;
const name = "Carl meToken";
Expand All @@ -60,6 +62,7 @@ const setup = async () => {
const tokenDeposited = ethers.utils.parseEther(
tokenDepositedInETH.toString()
);
const fee = 3000;

// TODO: pass in curve arguments to function
// TODO: then loop over array of set of curve arguments
Expand All @@ -72,13 +75,13 @@ const setup = async () => {
// weight at 50% linear curve
// const reserveWeight = BigNumber.from(MAX_WEIGHT).div(2).toString();
before(async () => {
({ DAI, DAIWhale } = await getNamedAccounts());
const encodedVaultArgs = ethers.utils.defaultAbiCoder.encode(
({ DAI, WETH } = await getNamedAccounts());
encodedVaultArgs = ethers.utils.defaultAbiCoder.encode(
["address"],
[DAI]
);
// TODO: pass in name of curve to deploy, encodedCurveDetails to general func
const encodedCurveDetails = ethers.utils.defaultAbiCoder.encode(
encodedCurveDetails = ethers.utils.defaultAbiCoder.encode(
["uint256", "uint32"],
[baseY, reserveWeight]
);
Expand Down Expand Up @@ -109,9 +112,13 @@ const setup = async () => {

// Prefund owner/buyer w/ DAI
dai = token;
weth = await getContractAt<ERC20>("ERC20", WETH);
await dai
.connect(tokenHolder)
.transfer(account0.address, amount1.mul(10));
await weth
.connect(tokenHolder)
.transfer(account0.address, amount1.mul(10));
await dai
.connect(tokenHolder)
.transfer(account1.address, amount1.mul(10));
Expand All @@ -120,6 +127,7 @@ const setup = async () => {
.transfer(account2.address, amount1.mul(10));
const max = ethers.constants.MaxUint256;
await dai.connect(account0).approve(singleAssetVault.address, max);
await weth.connect(account0).approve(singleAssetVault.address, max);
await dai.connect(account1).approve(singleAssetVault.address, max);
await dai.connect(account2).approve(singleAssetVault.address, max);
await dai.connect(account1).approve(meTokenRegistry.address, max);
Expand Down Expand Up @@ -1072,6 +1080,232 @@ const setup = async () => {
await meToken.balanceOf(account2.address)
);
});
after(async () => {
const oldDetails = await hub.getDetails(hubId);
await mineBlock(oldDetails.endTime.toNumber() + 2);
const block = await ethers.provider.getBlock("latest");
expect(oldDetails.endTime).to.be.lt(block.timestamp);

await hub.finishUpdate(hubId);
const newDetails = await hub.getDetails(hubId);
expect(newDetails.updating).to.be.equal(false);
});
});
describe("donate with same asset migration", () => {
let migration: SameAssetTransferMigration;
before(async () => {
await hub.register(
account0.address,
DAI,
singleAssetVault.address,
_curve.address,
refundRatio,
encodedCurveDetails,
encodedVaultArgs
);
migration = await deploy<SameAssetTransferMigration>(
"SameAssetTransferMigration",
undefined,
account0.address,
foundry.address,
hub.address,
meTokenRegistry.address,
migrationRegistry.address
);

await migrationRegistry.approve(
singleAssetVault.address,
singleAssetVault.address,
migration.address
);

const encodedMigrationArgs = "0x";

await meTokenRegistry
.connect(account2)
.initResubscribe(
meToken.address,
2,
migration.address,
encodedMigrationArgs
);
expect(
(await meTokenRegistry.getDetails(meToken.address)).migration
).to.equal(migration.address);
});
it("should revert when meToken is resubscribing", async () => {
await expect(foundry.donate(meToken.address, 10)).to.be.revertedWith(
"meToken resubscribing"
);
});
it("should be able to donate", async () => {
const meTokenRegistryDetails = await meTokenRegistry.getDetails(
meToken.address
);
await mineBlock(meTokenRegistryDetails.endTime.toNumber() + 2);
const block = await ethers.provider.getBlock("latest");
expect(meTokenRegistryDetails.endTime).to.be.lt(block.timestamp);
await meTokenRegistry.finishResubscribe(meToken.address);

const oldVaultBalance = await dai.balanceOf(singleAssetVault.address);
const oldAccountBalance = await dai.balanceOf(account0.address);
const oldMeTokenDetails = await meTokenRegistry.getDetails(
meToken.address
);
const oldAccruedFee = await singleAssetVault.accruedFees(dai.address);

const assetsDeposited = 10;
const tx = await foundry.donate(meToken.address, assetsDeposited);

await expect(tx)
.to.emit(foundry, "Donate")
.withArgs(
meToken.address,
dai.address,
account0.address,
assetsDeposited
)
.to.emit(singleAssetVault, "HandleDeposit")
.withArgs(account0.address, dai.address, assetsDeposited, 0)
.to.emit(meTokenRegistry, "UpdateBalanceLocked")
.withArgs(true, meToken.address, assetsDeposited);

const newMeTokenDetails = await meTokenRegistry.getDetails(
meToken.address
);
const newVaultBalance = await dai.balanceOf(singleAssetVault.address);
const newAccountBalance = await dai.balanceOf(account0.address);
const newAccruedFee = await singleAssetVault.accruedFees(dai.address);

expect(oldMeTokenDetails.balanceLocked.add(assetsDeposited)).to.equal(
newMeTokenDetails.balanceLocked
);
expect(oldMeTokenDetails.balancePooled).to.equal(
newMeTokenDetails.balancePooled
);
expect(oldVaultBalance.add(assetsDeposited)).to.equal(newVaultBalance);
expect(oldAccountBalance.sub(assetsDeposited)).to.equal(
newAccountBalance
);
expect(oldAccruedFee).to.equal(newAccruedFee);
});
});

describe("donate with same UniswapSingleTransfer migration", () => {
let migration: UniswapSingleTransferMigration;
before(async () => {
await hub.register(
account0.address,
WETH,
singleAssetVault.address,
_curve.address,
refundRatio,
encodedCurveDetails,
encodedVaultArgs
);
migration = await deploy<UniswapSingleTransferMigration>(
"UniswapSingleTransferMigration",
undefined,
account0.address,
foundry.address,
hub.address,
meTokenRegistry.address,
migrationRegistry.address
);

await migrationRegistry.approve(
singleAssetVault.address,
singleAssetVault.address,
migration.address
);

let block = await ethers.provider.getBlock("latest");
const earliestSwapTime = block.timestamp + 600 * 60; // 10h in future
const encodedMigrationArgs = ethers.utils.defaultAbiCoder.encode(
["uint256", "uint24"],
[earliestSwapTime, fee]
);

await meTokenRegistry
.connect(account2)
.initResubscribe(
meToken.address,
3,
migration.address,
encodedMigrationArgs
);
expect(
(await meTokenRegistry.getDetails(meToken.address)).migration
).to.equal(migration.address);
const migrationDetails = await migration.getDetails(meToken.address);
await mineBlock(migrationDetails.soonest.toNumber() + 2);

block = await ethers.provider.getBlock("latest");
expect(migrationDetails.soonest).to.be.lt(block.timestamp);
});
it("should revert when meToken is resubscribing", async () => {
await expect(foundry.donate(meToken.address, 10)).to.be.revertedWith(
"meToken resubscribing"
);
});
it("should be able to donate", async () => {
await meTokenRegistry.finishResubscribe(meToken.address);

const oldDAIVaultBalance = await dai.balanceOf(
singleAssetVault.address
);
const oldWETHVaultBalance = await weth.balanceOf(
singleAssetVault.address
);
const oldAccountBalance = await weth.balanceOf(account0.address);
const oldMeTokenDetails = await meTokenRegistry.getDetails(
meToken.address
);
const oldAccruedFee = await singleAssetVault.accruedFees(weth.address);

const assetsDeposited = 10;
const tx = await foundry.donate(meToken.address, assetsDeposited);

await expect(tx)
.to.emit(foundry, "Donate")
.withArgs(
meToken.address,
weth.address,
account0.address,
assetsDeposited
)
.to.emit(singleAssetVault, "HandleDeposit")
.withArgs(account0.address, weth.address, assetsDeposited, 0)
.to.emit(meTokenRegistry, "UpdateBalanceLocked")
.withArgs(true, meToken.address, assetsDeposited);

const newMeTokenDetails = await meTokenRegistry.getDetails(
meToken.address
);
const newDAIVaultBalance = await dai.balanceOf(
singleAssetVault.address
);
const newWETHVaultBalance = await weth.balanceOf(
singleAssetVault.address
);
const newAccountBalance = await weth.balanceOf(account0.address);
const newAccruedFee = await singleAssetVault.accruedFees(weth.address);

expect(oldMeTokenDetails.balanceLocked.add(assetsDeposited)).to.equal(
newMeTokenDetails.balanceLocked
);
expect(oldMeTokenDetails.balancePooled).to.equal(
newMeTokenDetails.balancePooled
);
expect(oldDAIVaultBalance).to.equal(newDAIVaultBalance);
expect(oldWETHVaultBalance.add(assetsDeposited)).to.equal(
newWETHVaultBalance
);
expect(oldAccountBalance.sub(assetsDeposited)).to.equal(
newAccountBalance
);
expect(oldAccruedFee).to.equal(newAccruedFee);
});
});
});
};
Expand Down
6 changes: 3 additions & 3 deletions test/integration/MeTokenRegistry/ResubscribeRefundRatio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,11 @@ const setup = async () => {
// Pre-load owner and buyer w/ DAI
await dai
.connect(tokenHolder)
.transfer(account2.address, ethers.utils.parseEther("500"));
.transfer(account2.address, ethers.utils.parseEther("250"));

await weth
.connect(tokenHolder)
.transfer(account2.address, ethers.utils.parseEther("500"));
.transfer(account2.address, ethers.utils.parseEther("250"));

// Create meToken and subscribe to Hub1
await meTokenRegistry
Expand Down Expand Up @@ -155,7 +155,7 @@ const setup = async () => {
migration.address,
encodedMigrationArgs
);
tokenDepositedInETH = 100;
tokenDepositedInETH = 50;
tokenDeposited = ethers.utils.parseEther(tokenDepositedInETH.toString());
await dai
.connect(account2)
Expand Down