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
42 changes: 13 additions & 29 deletions contracts/allocators/old-allocators/FraxSharesAllocator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,7 @@ contract FraxSharesAllocator is Initializable, OwnableUpgradeable {
__Context_init_unchained();
__Ownable_init_unchained();

require(_treasury != address(0), "zero treasury address");
treasury = ITreasury(_treasury);
treasury = ITreasury(address(0));

require(_fxs != address(0), "zero FXS address");
fxs = IERC20(_fxs);
Expand All @@ -121,20 +120,21 @@ contract FraxSharesAllocator is Initializable, OwnableUpgradeable {
/* ======== POLICY FUNCTIONS ======== */

/**
* @notice harvest FXS rewards, will relock all veFXS for the maximum amount of time (4 years)
* @notice harvest FXS rewards
*/
function harvest() external {
uint256 amount = veFXSYieldDistributorV4.getYield();

if (amount > 0) {
totalAmountDeployed = totalAmountDeployed.add(amount);

fxs.safeApprove(address(veFXS), amount);
veFXS.increase_amount(amount);
if (_canExtendLock()) {
lockEnd = block.timestamp + MAX_TIME;
veFXS.increase_unlock_time(block.timestamp + MAX_TIME);
}
fxs.transfer(owner(), amount);

// Do not extend lock
// fxs.safeApprove(address(veFXS), amount);
// veFXS.increase_amount(amount);
// if (_canExtendLock()) {
// lockEnd = block.timestamp + MAX_TIME;
// veFXS.increase_unlock_time(block.timestamp + MAX_TIME);
// }
}
}

Expand All @@ -143,27 +143,11 @@ contract FraxSharesAllocator is Initializable, OwnableUpgradeable {
* @param _amount uint
*/
function deposit(uint256 _amount) external onlyOwner {
treasury.manage(address(fxs), _amount);

uint256 prevAmount = totalAmountDeployed;
totalAmountDeployed = totalAmountDeployed.add(_amount);

fxs.safeApprove(address(veFXS), _amount);
if (prevAmount == 0) {
lockEnd = block.timestamp + MAX_TIME;
veFXS.create_lock(_amount, lockEnd);
} else {
veFXS.increase_amount(_amount);
if (_canExtendLock()) {
lockEnd = block.timestamp + MAX_TIME;
veFXS.increase_unlock_time(block.timestamp + MAX_TIME);
}
}
// No-op, this is for withdrawal to a seller only
}

function setTreasury(address _treasury) external onlyOwner {
require(_treasury != address(0), "zero treasury address");
treasury = ITreasury(_treasury);
// No-op, treasury is un-needed in sale of allocator
}

/* ======== VIEW FUNCTIONS ======== */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ contract FraxSharesAllocatorVoting is FraxSharesAllocator {
}

function withdrawToken(address token, uint256 amount) external onlyOwner {
IERC20(token).transfer(address(treasury), amount);
IERC20(token).transfer(msg.sender, amount);
}
}
175 changes: 175 additions & 0 deletions test/allocators/FraxSharesAllocatorVotingV2.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
import chai, { expect } from "chai";
import { ethers, upgrades, config } from "hardhat";
import { BigNumber } from "ethers";
import {
ITreasury,
IveFXS,
FraxSharesAllocatorVoting,
OlympusTreasury,
ERC20,
FraxSharesAllocatorVoting__factory
} from "../../types";
import { coins } from "../utils/coins";
import { olympus } from "../utils/olympus";
import { helpers } from "../utils/helpers";
import { vefxsAbi, wlContractAbi } from "../utils/fxsAllocatorAbis";
import { proxyAdminAbi, transparentUpgradeableProxyAbi } from "../utils/abi";

const ZERO_ADDRESS = ethers.utils.getAddress("0x0000000000000000000000000000000000000000");
const MAX_TIME = 4 * 365 * 86400 + 1;

describe("FraxSharesAllocatorVotingV2", () => {
describe("Integration Tests", () => {
// signers
let owner: SignerWithAddress;
let other: SignerWithAddress;
let newOwner: SignerWithAddress;
let fxsWallet: SignerWithAddress;
let wlOwner: SignerWithAddress;

// contracts
let treasury: OlympusTreasury;
let factory: FraxSharesAllocatorVoting__factory;
let proxyAdmin: any;
let proxy: any;
let allocator: FraxSharesAllocatorVoting;
let proxiedAllocator: any;

// tokens
let fxs: ERC20;
let veFXS: any;

// network
let url: string = config.networks.hardhat.forking!.url;

// variables
let snapshotId: number = 0;

before(async () => {
await helpers.pinBlock(17935468, url);

[other, newOwner] = await ethers.getSigners();

owner = await helpers.impersonate("0x245cc372C84B3645Bf0Ffe6538620B04a217988B");
fxsWallet = await helpers.impersonate("0xF977814e90dA44bFA03b6295A0616a897441aceC");
wlOwner = await helpers.impersonate("0xb1748c79709f4ba2dd82834b8c82d4a505003f27");

fxs = await helpers.getCoin(coins.fxs);
veFXS = await ethers.getContractAt(vefxsAbi, "0xc8418aF6358FFddA74e09Ca9CC3Fe03Ca6aDC5b0");

treasury = (await ethers.getContractAt(
"OlympusTreasury",
olympus.treasury
)) as OlympusTreasury;

factory = (await ethers.getContractFactory("FraxSharesAllocatorVoting")) as FraxSharesAllocatorVoting__factory;
factory = factory.connect(owner);
proxyAdmin = (await ethers.getContractAt(
proxyAdminAbi,
"0xC8D6043061Bc0A13587E92d762386F4EC29Deb8F"
)) as any;
proxy = (await ethers.getContractAt(
transparentUpgradeableProxyAbi,
"0xde7b85f52577B113181921A7aa8Fc0C22e309475"
)) as any;
allocator = (await factory.deploy()) as FraxSharesAllocatorVoting;
allocator = allocator.connect(owner);
allocator.initialize(
treasury.address,
veFXS.address,
fxs.address,
"0xc6764e58b36e26b08Fd1d2AeD4538c02171fA872"
);

proxiedAllocator = await allocator.attach(proxy.address);
proxiedAllocator = proxiedAllocator.connect(owner);
});

beforeEach(async () => {
snapshotId = await helpers.snapshot();
});

afterEach(async () => {
await helpers.revert(snapshotId);
});

describe("upgrade", () => {
it("should upgrade", async () => {
await proxyAdmin.connect(owner).upgrade(proxy.address, allocator.address);
expect(await proxyAdmin.getProxyImplementation(proxy.address)).to.eq(allocator.address);
});
});

describe("deposit", () => {
before(async () => {
await proxyAdmin.connect(owner).upgrade(proxy.address, allocator.address);
});

it("should do nothing on deposit", async () => {
const fxsTreasuryBalanceBefore = await fxs.balanceOf(treasury.address);
const fxsAllocatorBalanceBefore = await fxs.balanceOf(proxiedAllocator.address);

proxiedAllocator.deposit(1);

const fxsTreasuryBalanceAfter = await fxs.balanceOf(treasury.address);
const fxsAllocatorBalanceAfter = await fxs.balanceOf(proxiedAllocator.address);

expect(fxsTreasuryBalanceAfter).to.eq(fxsTreasuryBalanceBefore);
expect(fxsAllocatorBalanceAfter).to.eq(fxsAllocatorBalanceBefore);
});
});

describe("setTreasury", () => {
before(async () => {
await proxyAdmin.connect(owner).upgrade(proxy.address, allocator.address);
});

it("should do nothing on setTreasury", async () => {
const treasuryBefore = await proxiedAllocator.treasury();
proxiedAllocator.setTreasury(ZERO_ADDRESS);
const treasuryAfter = await proxiedAllocator.treasury();

expect(treasuryBefore).to.eq(treasuryAfter);
});
});

describe("withdrawToken", () => {
before(async () => {
await proxyAdmin.connect(owner).upgrade(proxy.address, allocator.address);
});

it("should send tokens to owner on withdraw", async () => {
// transfer fxs in
await fxs.connect(fxsWallet).transfer(proxy.address, 1000);

const fxsBalanceBefore = await fxs.balanceOf(owner.address);
const fxsAllocatorBalanceBefore = await fxs.balanceOf(proxy.address);

await proxiedAllocator.withdrawToken(fxs.address, 1000);

const fxsBalanceAfter = await fxs.balanceOf(owner.address);
const fxsAllocatorBalanceAfter = await fxs.balanceOf(proxy.address);

expect(fxsBalanceAfter).to.eq(fxsBalanceBefore.add(1000));
expect(fxsAllocatorBalanceAfter).to.eq(fxsAllocatorBalanceBefore.sub(1000));
});
});

describe("harvest", () => {
before(async () => {
await proxyAdmin.connect(owner).upgrade(proxy.address, allocator.address);
});

it("should get yield", async () => {
const fxsBalanceBefore = await fxs.balanceOf(owner.address);

await proxiedAllocator.harvest();

const fxsBalanceAfter = await fxs.balanceOf(owner.address);

expect(fxsBalanceAfter).to.be.gt(fxsBalanceBefore);
});
});
});
});
4 changes: 4 additions & 0 deletions test/utils/abi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -535,3 +535,7 @@ export const dsrAbi = [
type: "function",
},
];

export const transparentUpgradeableProxyAbi = [{"inputs":[{"internalType":"address","name":"_logic","type":"address"},{"internalType":"address","name":"admin_","type":"address"},{"internalType":"bytes","name":"_data","type":"bytes"}],"stateMutability":"payable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"beacon","type":"address"}],"name":"BeaconUpgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"admin_","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newAdmin","type":"address"}],"name":"changeAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"implementation_","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"}],"name":"upgradeTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"},{"stateMutability":"payable","type":"receive"}];

export const proxyAdminAbi = [{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"contract TransparentUpgradeableProxy","name":"proxy","type":"address"},{"internalType":"address","name":"newAdmin","type":"address"}],"name":"changeProxyAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract TransparentUpgradeableProxy","name":"proxy","type":"address"}],"name":"getProxyAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract TransparentUpgradeableProxy","name":"proxy","type":"address"}],"name":"getProxyImplementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract TransparentUpgradeableProxy","name":"proxy","type":"address"},{"internalType":"address","name":"implementation","type":"address"}],"name":"upgrade","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract TransparentUpgradeableProxy","name":"proxy","type":"address"},{"internalType":"address","name":"implementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeAndCall","outputs":[],"stateMutability":"payable","type":"function"}];