Skip to content

Commit

Permalink
[tests] fix: fixup GraphAdapter and Tenderizer tests
Browse files Browse the repository at this point in the history
  • Loading branch information
kyriediculous committed Dec 11, 2023
1 parent 860699b commit f93412e
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 40 deletions.
2 changes: 0 additions & 2 deletions src/adapters/GraphAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ contract GraphAdapter is Adapter {
uint256 lastEpochUnlockedAt;
mapping(uint256 => Epoch) epochs;
mapping(uint256 => Unlock) unlocks;
uint256 tokensPerShare;
}

function _loadStorage() internal pure returns (Storage storage $) {
Expand Down Expand Up @@ -212,7 +211,6 @@ contract GraphAdapter is Adapter {
// calculate shares to undelegate from The Graph
IGraphStaking.DelegationPool memory delPool = GRAPH_STAKING.delegationPools(validator);
uint256 undelegationShares = currentEpochAmount * delPool.shares / delPool.tokens;

// account for possible rounding error
undelegationShares = del.shares < undelegationShares ? del.shares : undelegationShares;

Expand Down
74 changes: 43 additions & 31 deletions test/adapters/GraphAdapter.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,18 @@

pragma solidity >=0.8.19;

import { Test, stdError } from "forge-std/Test.sol";
import { GraphAdapter } from "core/adapters/GraphAdapter.sol";
import { Test } from "forge-std/Test.sol";
import { GraphAdapter, GRAPH_STAKING, GRAPH_EPOCHS, GRT } from "core/adapters/GraphAdapter.sol";
import { IERC20 } from "core/interfaces/IERC20.sol";
import { IGraphStaking, IGraphEpochManager } from "core/adapters/interfaces/IGraph.sol";
import { TestHelpers } from "test/helpers/Helpers.sol";

// solhint-disable func-name-mixedcase

contract GraphAdapterTest is Test, GraphAdapter, TestHelpers {
address private staking = 0xF55041E37E12cD407ad00CE2910B8269B01263b9;
address private epochs = 0x03541c5cd35953CD447261122F93A5E7b812D697;
address private token = 0xc944E90C64B2c07662A292be6244BDf05Cda44a7;
address private staking = address(GRAPH_STAKING);
address private epochs = address(GRAPH_EPOCHS);
address private token = address(GRT);

address private validator = vm.addr(1);

Expand All @@ -34,14 +34,19 @@ contract GraphAdapterTest is Test, GraphAdapter, TestHelpers {
uint256 private MAX_UINT_SQRT = sqrt(MAX_UINT - 1);

function setUp() public {
vm.etch(staking, bytes("code"));
vm.mockCall(staking, abi.encodeCall(IGraphStaking.delegationTaxPercentage, ()), abi.encode(DELEGATION_TAX));
vm.mockCall(staking, abi.encodeCall(IGraphStaking.thawingPeriod, ()), abi.encode(THAWING_PERIOD));
}

function testFuzz_PreviewDeposit(uint256 amount) public {
amount = bound(amount, 0, MAX_UINT / DELEGATION_TAX);
vm.expectCall(staking, abi.encodeCall(IGraphStaking.delegationTaxPercentage, ()));
assertEq(this.previewDeposit(validator, amount), amount - amount * DELEGATION_TAX / MAX_PPM);
amount = bound(amount, 1, 10e32);

vm.mockCall(staking, abi.encodeCall(IGraphStaking.delegationPools, (validator)), abi.encode(0, 0, 0, 0, 1 ether, 1 ether));
uint256 am = amount - amount * DELEGATION_TAX / MAX_PPM;
uint256 shares = am * 1 ether / 1 ether;
uint256 ev = shares * (1 ether + am) / (1 ether + shares);
assertEq(this.previewDeposit(validator, amount), ev);
}

function testFuzz_UnlockMaturity(uint256 lastEpochUnlockedAt, uint256 userEpoch) public {
Expand Down Expand Up @@ -82,13 +87,18 @@ contract GraphAdapterTest is Test, GraphAdapter, TestHelpers {
assertEq(this.previewWithdraw(unlockID), unlockShares * epochAmount / epochTotalShares);
}

function test_Stake() public {
uint256 amount = 1 ether;
function testFuzz_Stake(uint256 amount) public {
amount = bound(amount, 1, 10e32);
vm.mockCall(token, abi.encodeCall(IERC20.approve, (staking, amount)), abi.encode(true));
vm.mockCall(staking, abi.encodeCall(IGraphStaking.delegate, (validator, amount)), abi.encode(amount));
uint256 am = amount - amount * DELEGATION_TAX / MAX_PPM;
uint256 shares = am * 1 ether / 1 ether;
uint256 ev = shares * (1 ether + am) / (1 ether + shares);
vm.mockCall(staking, abi.encodeCall(IGraphStaking.delegate, (validator, amount)), abi.encode(shares));
vm.mockCall(staking, abi.encodeCall(IGraphStaking.delegationPools, (validator)), abi.encode(0, 0, 0, 0, 1 ether, 1 ether));
vm.expectCall(token, abi.encodeCall(IERC20.approve, (staking, amount)));
vm.expectCall(staking, abi.encodeCall(IGraphStaking.delegate, (validator, amount)));
this.stake(validator, amount);
uint256 staked = this.stake(validator, amount);
assertEq(staked, ev, "invalid staked amount");
}

function test_Stake_RevertIfApproveFails() public {
Expand Down Expand Up @@ -134,40 +144,42 @@ contract GraphAdapterTest is Test, GraphAdapter, TestHelpers {
assertEq($.currentEpoch, epoch, "invalid epoch");
}

function testFuzz_Unstake(uint256 currentEpochAmount, uint256 stakedAmount, uint256 stakedShares) public {
uint256 amount = 1 ether;
currentEpochAmount = bound(currentEpochAmount, amount, MAX_UINT_SQRT - amount);
stakedAmount = bound(stakedAmount, 1, MAX_UINT_SQRT);
stakedShares = bound(stakedShares, stakedAmount, MAX_UINT_SQRT);
function testFuzz_Unstake(uint256 amount, uint256 stakedAmount, uint256 stakedShares) public {
stakedAmount = bound(stakedAmount, 1 ether, 10e32);
amount = bound(amount, 1, stakedAmount);
stakedShares = bound(stakedShares, 1 ether, 10e32);
uint256 epoch = 1;
uint256 lastUnlockID = 1;

Storage storage $ = _loadStorage();
$.currentEpoch = epoch;
$.epochs[epoch].amount = currentEpochAmount;
$.epochs[epoch].totalShares = currentEpochAmount;
$.lastUnlockID = lastUnlockID;
$.tokensPerShare = stakedAmount * 1 ether / stakedShares;
$.tokensPerShare = $.tokensPerShare == 0 ? 1 ether : $.tokensPerShare;

// No unlock processing (del.tokensLockedUntil = 0)
// and currentEpoch.amount != 0 since we undelegate `amount`
// => undelegate currentEpoch.amount
// For this test epoch shares are 1:1 to epoch amounts

vm.mockCall(
staking, abi.encodeCall(IGraphStaking.getDelegation, (validator, address(this))), abi.encode(stakedShares, 0, 0)
staking, abi.encodeCall(IGraphStaking.delegationPools, (validator)), abi.encode(0, 0, 0, 0, stakedAmount, stakedShares)
);

vm.mockCall(
staking, abi.encodeCall(IGraphStaking.getDelegation, (validator, address(this))), abi.encode(stakedShares, 0, 0)
);
vm.mockCall(epochs, abi.encodeCall(IGraphEpochManager.currentEpoch, ()), abi.encode(epoch));

uint256 expShares = (currentEpochAmount + amount) * 1 ether / $.tokensPerShare;
expShares = expShares > stakedShares ? stakedShares : expShares;
vm.mockCall(staking, abi.encodeCall(IGraphStaking.undelegate, (validator, expShares)), abi.encode(expShares));

vm.expectCall(staking, abi.encodeCall(IGraphStaking.undelegate, (validator, expShares)));
uint256 expUndelegateShares = amount * stakedShares / stakedAmount;
vm.mockCall(
staking, abi.encodeCall(IGraphStaking.undelegate, (validator, expUndelegateShares)), abi.encode(expUndelegateShares)
);
vm.expectCall(staking, abi.encodeCall(IGraphStaking.undelegate, (validator, expUndelegateShares)));
this.unstake(validator, amount);
assertEq($.currentEpoch, epoch + 1, "invalid epoch");
assertEq($.lastEpochUnlockedAt, block.number, "invalid lastEpochUnlockedAt");
assertEq($.unlocks[lastUnlockID + 1].shares, amount, "invalid unlock shares");
assertEq($.unlocks[lastUnlockID + 1].epoch, epoch, "invalid unlock epoch");
assertEq($.epochs[epoch].amount, currentEpochAmount + amount, "invalid epoch amount");
assertEq($.epochs[epoch].totalShares, currentEpochAmount + amount, "invalid epoch shares");
assertEq($.epochs[epoch].amount, amount, "invalid epoch amount");
assertEq($.epochs[epoch].totalShares, amount, "invalid epoch shares");
}

function test_Withdraw_LastTwoEpochsEmpty() public {
Expand All @@ -186,6 +198,7 @@ contract GraphAdapterTest is Test, GraphAdapter, TestHelpers {
function test_Withdraw_PreviousEpochEmpty() public {
uint256 amount = 1 ether;

vm.mockCall(staking, abi.encodeCall(IGraphStaking.delegationPools, (validator)), abi.encode(0, 0, 0, 0, amount, amount));
// should call `undelegate` but not `withdrawDelegation`
vm.mockCall(staking, abi.encodeCall(IGraphStaking.getDelegation, (validator, address(this))), abi.encode(amount, 0, 0));

Expand All @@ -196,7 +209,6 @@ contract GraphAdapterTest is Test, GraphAdapter, TestHelpers {
$.unlocks[0].shares = amount;
$.epochs[0].totalShares = amount;
$.epochs[0].amount = amount;
$.tokensPerShare = 1 ether;

uint256 epoch = 2;
$.currentEpoch = epoch;
Expand Down
18 changes: 11 additions & 7 deletions test/tenderizer/Tenderizer.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ contract TenderizerSetup is Test, TestHelpers {
string internal symbol = "FOO";

function setUp() public {
vm.etch(registry, bytes("code"));
vm.etch(adapter, bytes("code"));
vm.etch(asset, bytes("code"));
vm.etch(staking, bytes("code"));
vm.etch(unlocks, bytes("code"));
// Setup global mock responses
vm.mockCall(registry, abi.encodeCall(Registry.adapter, (asset)), abi.encode(adapter));
vm.mockCall(registry, abi.encodeCall(Registry.fee, (asset)), abi.encode(0.05 ether));
Expand Down Expand Up @@ -146,17 +151,15 @@ contract TenderizerTest is TenderizerSetup, TenderizerEvents {
amountOut = bound(amountOut, 1, MAX_UINT_SQRT);

vm.mockCall(adapter, abi.encodeCall(Adapter.rebase, (validator, 0)), abi.encode(0));
vm.mockCall(adapter, abi.encodeCall(Adapter.previewDeposit, (validator, amountIn)), abi.encode(amountOut));
vm.mockCall(asset, abi.encodeCall(IERC20.transferFrom, (account1, address(tenderizer), amountIn)), abi.encode(true));

vm.mockCall(adapter, abi.encodeCall(Adapter.stake, (validator, amountIn)), abi.encode(amountOut));
vm.expectCall(adapter, abi.encodeCall(Adapter.rebase, (validator, 0)));
vm.expectCall(asset, abi.encodeCall(IERC20.transferFrom, (account1, address(tenderizer), amountIn)));
vm.expectCall(adapter, abi.encodeCall(Adapter.previewDeposit, (validator, amountIn)));
vm.expectCall(adapter, abi.encodeCall(Adapter.stake, (validator, amountIn)));
vm.prank(account1);

vm.expectEmit(true, true, true, true);
emit Deposit(account1, account2, amountIn, amountOut);

vm.prank(account1);
uint256 actualAssets = tenderizer.deposit(account2, amountIn);

assertEq(actualAssets, amountOut, "invalid return value");
Expand All @@ -178,7 +181,7 @@ contract TenderizerTest is TenderizerSetup, TenderizerEvents {

function test_Deposit_RevertIfZeroAmount() public {
vm.mockCall(adapter, abi.encodeCall(Adapter.rebase, (validator, 0)), abi.encode(0));
vm.mockCall(adapter, abi.encodeCall(Adapter.previewDeposit, (validator, 0)), abi.encode(0));
vm.mockCall(adapter, abi.encodeCall(Adapter.stake, (validator, 0)), abi.encode(0));
vm.expectRevert(TToken.ZeroAmount.selector);
tenderizer.deposit(account1, 0);
}
Expand Down Expand Up @@ -326,7 +329,6 @@ contract TenderizerTest is TenderizerSetup, TenderizerEvents {
_deposit(account2, deposit2, deposit1);

vm.mockCall(adapter, abi.encodeCall(Adapter.rebase, (validator, totalDeposit)), abi.encode(newStake));

vm.mockCall(registry, abi.encodeCall(Registry.fee, (asset)), abi.encode(feeRate));

uint256 cappedFeeRate = feeRate > MAX_FEE ? MAX_FEE : feeRate;
Expand Down Expand Up @@ -391,6 +393,8 @@ contract TenderizerTest is TenderizerSetup, TenderizerEvents {
function _deposit(address account, uint256 amount, uint256 totalPreviousDeposits) internal {
vm.mockCall(adapter, abi.encodeCall(Adapter.previewDeposit, (validator, amount)), abi.encode(amount));
vm.mockCall(adapter, abi.encodeCall(Adapter.rebase, (validator, totalPreviousDeposits)), abi.encode(totalPreviousDeposits));
vm.mockCall(adapter, abi.encodeCall(Adapter.stake, (validator, amount)), abi.encode(amount));

vm.prank(account);
tenderizer.deposit(account, amount);
}
Expand Down

0 comments on commit f93412e

Please sign in to comment.