Skip to content

Commit

Permalink
adds a few AludelV3 solidity tests
Browse files Browse the repository at this point in the history
I'm only testing funding features, specially how the reward schedules
works because I want to see how the reward schedule optimizations would
look like.
  • Loading branch information
itirabasso committed Dec 19, 2022
1 parent 5e14145 commit 852483e
Showing 1 changed file with 308 additions and 0 deletions.
308 changes: 308 additions & 0 deletions src/test/AludelV3.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,308 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// solhint-disable func-name-mixedcase
pragma solidity ^0.8.6;

import {DSTest} from "ds-test/src/test.sol";
import {ERC20} from "solmate/src/tokens/ERC20.sol";
import {Vm} from "forge-std/src/Vm.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

import {AludelFactory} from "../contracts/AludelFactory.sol";
import {IAludelV3} from "../contracts/aludel/IAludelV3.sol";
import {IAludel} from "../contracts/aludel/IAludel.sol";
import {AludelV3} from "../contracts/aludel/AludelV3.sol";
import {AludelV3Lib} from "../contracts/aludel/AludelV3Lib.sol";

import {RewardPoolFactory} from "alchemist/contracts/aludel/RewardPoolFactory.sol";
import {PowerSwitchFactory} from "../contracts/powerSwitch/PowerSwitchFactory.sol";

import {MockERC20} from "../contracts/mocks/MockERC20.sol";
import {Spy} from "./Spy.sol";

import {Crucible, IUniversalVault} from "alchemist/contracts/crucible/Crucible.sol";
import {CrucibleFactory} from "alchemist/contracts/crucible/CrucibleFactory.sol";

import {User} from "./User.sol";
import {Utils} from "./Utils.sol";
import {UserFactory} from "./UserFactory.sol";

import "forge-std/src/console2.sol";

contract AludelV3Test is DSTest {

AludelFactory private factory;
Vm private vm;
AludelV3 private aludel;
Spy private spyTemplate;

User private user;
User private anotherUser;
User private admin;
User private recipient;

Crucible private crucibleA;
Crucible private crucibleB;
Crucible private crucibleC;

MockERC20 private stakingToken;
MockERC20 private rewardToken;
RewardPoolFactory private rewardPoolFactory;
PowerSwitchFactory private powerSwitchFactory;

IAludelV3.RewardScaling private rewardScaling;

CrucibleFactory private crucibleFactory;
Crucible private crucible;

address[] private bonusTokens;

AludelV3 private template;

uint16 private bps;
address private constant CRUCIBLE_FACTORY = address(0xcc1b13);
uint64 private constant START_TIME = 10000 seconds;
uint64 private constant SCHEDULE_DURATION = 1 minutes;

uint256 public constant BASE_SHARES_PER_WEI = 1000000;
uint256 public constant MILLION = 1e6;


uint256 public constant STAKE_AMOUNT = 60 ether;
uint256 public constant REWARD_AMOUNT = 600 ether;

AludelV3.AludelInitializationParams private defaultInitParams;
Utils.LaunchParams private defaultLaunchParams;

function setUp() public {

vm = Utils.vm();

UserFactory userFactory = new UserFactory();
user = userFactory.createUser("user", 0);
anotherUser = userFactory.createUser("anotherUser", 1);
admin = userFactory.createUser("admin", 2);
recipient = userFactory.createUser("recipient", 3);

Crucible crucibleTemplate = new Crucible();
crucibleTemplate.initializeLock();
crucibleFactory = new CrucibleFactory(address(crucibleTemplate));

crucible = Utils.createCrucible(user, crucibleFactory);

// feeBps set 0 to make calculations easier to comprehend
bps = 0;
factory = new AludelFactory(recipient.addr(), bps);

template = new AludelV3();
template.initializeLock();

rewardPoolFactory = new RewardPoolFactory();
powerSwitchFactory = new PowerSwitchFactory();
stakingToken = new MockERC20("", "STK");
rewardToken = new MockERC20("", "RWD");

rewardScaling = IAludelV3.RewardScaling({
floor: 1 ether,
ceiling: 1 ether,
time: 1 days
});

bonusTokens = new address[](2);
bonusTokens[0] = address(new MockERC20("", "BonusToken A"));
bonusTokens[1] = address(new MockERC20("", "BonusToken B"));

factory.addTemplate(address(template), "aludel v3", false);

defaultInitParams = AludelV3.AludelInitializationParams({
rewardPoolFactory: address(rewardPoolFactory),
powerSwitchFactory: address(powerSwitchFactory),
stakingToken: address(stakingToken),
rewardToken: address(rewardToken),
rewardScaling: rewardScaling
});

defaultLaunchParams = Utils.LaunchParams({
template: address(template),
name: "name",
stakingTokenUrl: "https://staking.token",
startTime: START_TIME,
vaultFactory: address(crucibleFactory),
bonusTokens: new address[](0),
owner: admin.addr(),
initParams: abi.encode(defaultInitParams)
});

aludel = AludelV3(Utils.launchProgram(factory, defaultLaunchParams));
vm.warp(START_TIME);
}

function test_calculate_new_shares_no_previous_shares(
uint128 sharesOutstanding,
uint128 remainingRewards,
uint128 newRewards
) public {

vm.assume(sharesOutstanding == 0);
assertEq(
AludelV3Lib.calculateNewShares(sharesOutstanding, remainingRewards, newRewards),
newRewards * BASE_SHARES_PER_WEI
);

}

function test_calculate_new_shares_with_previous_shares(
uint128 sharesOutstanding,
uint128 remainingRewards,
uint128 newRewards
) public {

vm.assume(sharesOutstanding > 0);
vm.assume(remainingRewards > 0);
// todo : do we need this?
// vm.assume(sharesOutstanding < remainingRewards * BASE_SHARES_PER_WEI);
assertEq(
AludelV3Lib.calculateNewShares(sharesOutstanding, remainingRewards, newRewards),
uint256(sharesOutstanding) * uint256(newRewards) / uint256(remainingRewards)
);
}

function test_funding() public {

AludelV3.AludelData memory data = aludel.getAludelData();

Utils.fundMockToken(admin.addr(), rewardToken, REWARD_AMOUNT * 5);

vm.startPrank(admin.addr());
rewardToken.approve(address(aludel), REWARD_AMOUNT * 5);

// schedule 0, default schedule (reward amount, schedule duration)
aludel.fund(REWARD_AMOUNT, SCHEDULE_DURATION);

data = aludel.getAludelData();
assertEq(data.rewardSharesOutstanding, REWARD_AMOUNT * BASE_SHARES_PER_WEI);

vm.warp(block.timestamp + SCHEDULE_DURATION);

// schedule 0 shares are now fully unlocked
assertEq(
aludel.calculateSharesLocked(data.rewardSchedules, block.timestamp),
0
);

// check: if shares are not redeemed shares should be scaled linearly
uint256 base_shares = data.rewardSharesOutstanding;

// schedule 1, idem schedule 0, default schedule
aludel.fund(REWARD_AMOUNT, SCHEDULE_DURATION);

data = aludel.getAludelData();
// shares are linearly scaled
assertEq(data.rewardSharesOutstanding, base_shares * 2);
//
assertEq(
aludel.calculateUnlockedRewards(
data.rewardSchedules,
REWARD_AMOUNT,
data.rewardSharesOutstanding,
block.timestamp + SCHEDULE_DURATION
),
REWARD_AMOUNT
);
//
assertEq(
aludel.calculateSharesLocked(
data.rewardSchedules, block.timestamp
),
base_shares
);

// schedule 2
aludel.fund(REWARD_AMOUNT, SCHEDULE_DURATION);

data = aludel.getAludelData();
// schedules 0, 1 and 2 shares.
assertEq(data.rewardSharesOutstanding, base_shares * 3);
assertEq(
aludel.calculateUnlockedRewards(
data.rewardSchedules,
REWARD_AMOUNT * 2,
data.rewardSharesOutstanding,
block.timestamp + SCHEDULE_DURATION
),
REWARD_AMOUNT * 2
);
assertEq(
aludel.calculateSharesLocked(
data.rewardSchedules, block.timestamp
),
base_shares * 2
);

data = aludel.getAludelData();

assertEq(data.rewardSharesOutstanding, REWARD_AMOUNT * BASE_SHARES_PER_WEI * 3);
assertEq(
aludel.calculateUnlockedRewards(
data.rewardSchedules,
REWARD_AMOUNT * 3,
data.rewardSharesOutstanding,
block.timestamp + SCHEDULE_DURATION
),
REWARD_AMOUNT * 3
);

vm.warp(block.timestamp + SCHEDULE_DURATION);

data = aludel.getAludelData();

assertEq(data.rewardSharesOutstanding, base_shares * 3);

assertEq(
aludel.calculateUnlockedRewards(
data.rewardSchedules,
REWARD_AMOUNT * 3,
data.rewardSharesOutstanding,
block.timestamp + SCHEDULE_DURATION
),
REWARD_AMOUNT * 3
);
// shares locked now
assertEq(
aludel.calculateSharesLocked(
data.rewardSchedules, block.timestamp
),
0
);

// schedule 3, default schedule
aludel.fund(REWARD_AMOUNT, SCHEDULE_DURATION);

// schedule 4 takes two periods to fully unlock rewards
aludel.fund(REWARD_AMOUNT, SCHEDULE_DURATION * 2);

vm.warp(block.timestamp + SCHEDULE_DURATION);

data = aludel.getAludelData();
assertEq(data.rewardSharesOutstanding, base_shares * 5);
// previous four periods amounts and half of the fifth.
assertEq(
aludel.calculateUnlockedRewards(
data.rewardSchedules,
REWARD_AMOUNT * 5,
data.rewardSharesOutstanding,
block.timestamp
),
REWARD_AMOUNT * 4 + REWARD_AMOUNT / 2
);
// fifth schedule shares are half-locked
assertEq(
aludel.calculateSharesLocked(
data.rewardSchedules, block.timestamp
),
base_shares / 2
);
}



}

0 comments on commit 852483e

Please sign in to comment.