-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add avalon strategy test cases
- Loading branch information
Showing
5 changed files
with
318 additions
and
1 deletion.
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
119 changes: 119 additions & 0 deletions
119
test/gateway/e2e-strategy-tests/AvalonLendingStrategyForked.sol
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,119 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.13; | ||
|
||
import {stdStorage, StdStorage, Test, console} from "forge-std/Test.sol"; | ||
|
||
using stdStorage for StdStorage; | ||
|
||
import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol"; | ||
import {IAvalonIPool, AvalonLendingStrategy} from "../../../src/gateway/strategy/AvalonStrategy.sol"; | ||
import {StrategySlippageArgs} from "../../../src/gateway/CommonStructs.sol"; | ||
import {Constants} from "./Constants.sol"; | ||
|
||
// Command to run this test with Foundry: | ||
// BOB_PROD_PUBLIC_RPC_URL=https://rpc.gobob.xyz/ forge test --match-contract AvalonTBTCLendingStrategyForked -vv | ||
|
||
contract AvalonTBTCLendingStrategyForked is Test { | ||
// Instantiate TBTC token using its address from Constants | ||
IERC20 token = IERC20(Constants.TBTC_ADDRESS); | ||
|
||
function setUp() public { | ||
// Set up the test environment by creating a fork of the BOB_PROD_PUBLIC_RPC_URL | ||
// Fixing the block number ensures reproducible test results | ||
vm.createSelectFork(vm.envString("BOB_PROD_PUBLIC_RPC_URL"), 6077077); | ||
|
||
// Transfer 100 TBTC tokens to DUMMY_SENDER | ||
vm.prank(0xa79a356B01ef805B3089b4FE67447b96c7e6DD4C); | ||
token.transfer(Constants.DUMMY_SENDER, 100 ether); | ||
vm.stopPrank(); | ||
} | ||
|
||
function testAvalonTBTCStrategy() public { | ||
// Instantiate the Avalon TBTC token and pool contracts | ||
IERC20 avalonTBTCToken = IERC20(0x5E007Ed35c7d89f5889eb6FD0cdCAa38059560ef); | ||
IAvalonIPool pool = IAvalonIPool(0x35B3F1BFe7cbE1e95A3DC2Ad054eB6f0D4c879b6); | ||
|
||
// Deploy a new AvalonLendingStrategy contract | ||
AvalonLendingStrategy strategy = new AvalonLendingStrategy(avalonTBTCToken, pool); | ||
|
||
// DUMMY_SENDER approves the strategy contract to spend 1 TBTC on their behalf | ||
vm.prank(Constants.DUMMY_SENDER); | ||
token.approve(address(strategy), 1 ether); | ||
vm.stopPrank(); | ||
|
||
// DUMMY_SENDER sends 1 TBTC to the strategy with slippage arguments | ||
vm.prank(Constants.DUMMY_SENDER); | ||
strategy.handleGatewayMessageWithSlippageArgs( | ||
token, | ||
1 ether, // Amount: 1 TBTC | ||
Constants.DUMMY_RECEIVER, | ||
StrategySlippageArgs(0) // No slippage allowed | ||
); | ||
vm.stopPrank(); | ||
|
||
// Assert that DUMMY_RECEIVER's token balance is still 0 (funds are in the pool) | ||
assertEq(token.balanceOf(Constants.DUMMY_RECEIVER), 0 ether); | ||
|
||
// DUMMY_RECEIVER withdraws Received TBTC from the pool | ||
vm.prank(Constants.DUMMY_RECEIVER); | ||
pool.withdraw(address(token), 1 ether, Constants.DUMMY_RECEIVER); | ||
vm.stopPrank(); | ||
|
||
// Assert that DUMMY_RECEIVER now has 1 TBTC in their balance | ||
assertEq(token.balanceOf(Constants.DUMMY_RECEIVER), 1 ether); | ||
} | ||
} | ||
|
||
// Command to run this test with Foundry: | ||
// BOB_PROD_PUBLIC_RPC_URL=https://rpc.gobob.xyz/ forge test --match-contract AvalonWBTCLendingStrategyForked -vv | ||
|
||
contract AvalonWBTCLendingStrategyForked is Test { | ||
// Instantiate TBTC token using its address from Constants | ||
IERC20 token = IERC20(Constants.WBTC_ADDRESS); | ||
|
||
function setUp() public { | ||
// Set up the test environment by creating a fork of the BOB_PROD_PUBLIC_RPC_URL | ||
// Fixing the block number ensures reproducible test results | ||
vm.createSelectFork(vm.envString("BOB_PROD_PUBLIC_RPC_URL"), 6216882); | ||
|
||
// Transfer 100 WBTC tokens to DUMMY_SENDER | ||
vm.prank(0x5A8E9774d67fe846C6F4311c073e2AC34b33646F); | ||
token.transfer(Constants.DUMMY_SENDER, 100 * 1e8); | ||
vm.stopPrank(); | ||
} | ||
|
||
function testAvalonWBTCStrategy() public { | ||
// Instantiate the Avalon WBTC token and pool contracts | ||
IERC20 avalonWBTCToken = IERC20(0xd6890176e8d912142AC489e8B5D8D93F8dE74D60); | ||
IAvalonIPool pool = IAvalonIPool(0x35B3F1BFe7cbE1e95A3DC2Ad054eB6f0D4c879b6); | ||
|
||
// Deploy a new AvalonLendingStrategy contract | ||
AvalonLendingStrategy strategy = new AvalonLendingStrategy(avalonWBTCToken, pool); | ||
|
||
// DUMMY_SENDER approves the strategy contract to spend 1 WBTC on their behalf | ||
vm.prank(Constants.DUMMY_SENDER); | ||
token.approve(address(strategy), 1 * 1e8); | ||
vm.stopPrank(); | ||
|
||
// DUMMY_SENDER sends 1 WBTC to the strategy with slippage arguments | ||
vm.prank(Constants.DUMMY_SENDER); | ||
strategy.handleGatewayMessageWithSlippageArgs( | ||
token, | ||
1 * 1e8, // Amount: 1 WBTC | ||
Constants.DUMMY_RECEIVER, | ||
StrategySlippageArgs(0) // No slippage allowed | ||
); | ||
vm.stopPrank(); | ||
|
||
// Assert that DUMMY_RECEIVER's token balance is still 0 (funds are in the pool) | ||
assertEq(token.balanceOf(Constants.DUMMY_RECEIVER), 0); | ||
|
||
// DUMMY_RECEIVER withdraws Received WBTC from the pool | ||
vm.prank(Constants.DUMMY_RECEIVER); | ||
pool.withdraw(address(token), 1 * 1e8, Constants.DUMMY_RECEIVER); | ||
vm.stopPrank(); | ||
|
||
// Assert that DUMMY_RECEIVER now has 1 WBTC in their balance | ||
assertEq(token.balanceOf(Constants.DUMMY_RECEIVER), 1 * 1e8); | ||
} | ||
} |
64 changes: 64 additions & 0 deletions
64
test/gateway/e2e-strategy-tests/AvalonLstStrategyForked.sol
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,64 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.0; | ||
|
||
import {Test, console} from "forge-std/Test.sol"; | ||
import { | ||
IAvalonIPool, AvalonLendingStrategy, AvalonLstStrategy | ||
} from "../../../src/gateway/strategy/AvalonStrategy.sol"; | ||
import {StrategySlippageArgs} from "../../../src/gateway/CommonStructs.sol"; | ||
import {Constants} from "./Constants.sol"; | ||
import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol"; | ||
import {SolvLSTStrategy, ISolvBTCRouter} from "../../../src/gateway/strategy/SolvStrategy.sol"; | ||
|
||
// Command to run this test with Foundry: | ||
// BOB_PROD_PUBLIC_RPC_URL=https://rpc.gobob.xyz/ forge test --match-contract AvalonWBTCLstStrategyForked -vv | ||
|
||
contract AvalonWBTCLstStrategyForked is Test { | ||
// Instantiate WBTC token using its address from Constants | ||
IERC20 token = IERC20(Constants.WBTC_ADDRESS); | ||
|
||
function setUp() public { | ||
// Set up the test environment by creating a fork of the BOB_PROD_PUBLIC_RPC_URL | ||
// Fixing the block number ensures reproducible test results | ||
vm.createSelectFork(vm.envString("BOB_PROD_PUBLIC_RPC_URL"), 6216882); | ||
|
||
// Transfer 100 WBTC tokens to DUMMY_SENDER | ||
vm.prank(0x5A8E9774d67fe846C6F4311c073e2AC34b33646F); | ||
token.transfer(Constants.DUMMY_SENDER, 100 * 1e8); | ||
vm.stopPrank(); | ||
} | ||
|
||
function testWbtcLstStrategy() public { | ||
IERC20 solvBTC = IERC20(0x541FD749419CA806a8bc7da8ac23D346f2dF8B77); | ||
IERC20 solvBTCBBN = IERC20(0xCC0966D8418d412c599A6421b760a847eB169A8c); | ||
SolvLSTStrategy solvLSTStrategy = new SolvLSTStrategy( | ||
ISolvBTCRouter(0x49b072158564Db36304518FFa37B1cFc13916A90), | ||
ISolvBTCRouter(0xbA46FcC16B464D9787314167bDD9f1Ce28405bA1), | ||
0x5664520240a46b4b3e9655c20cc3f9e08496a9b746a478e476ae3e04d6c8fc31, | ||
0x6899a7e13b655fa367208cb27c6eaa2410370d1565dc1f5f11853a1e8cbef033, | ||
solvBTC, | ||
solvBTCBBN | ||
); | ||
|
||
IERC20 avalonSolvBtcBBNToken = IERC20(0x2E6500A7Add9a788753a897e4e3477f651c612eb); | ||
IAvalonIPool pool = IAvalonIPool(0x35B3F1BFe7cbE1e95A3DC2Ad054eB6f0D4c879b6); | ||
AvalonLendingStrategy avalonLendingStrategy = new AvalonLendingStrategy(avalonSolvBtcBBNToken, pool); | ||
|
||
AvalonLstStrategy avalonLstStrategy = new AvalonLstStrategy(solvLSTStrategy, avalonLendingStrategy); | ||
|
||
// DUMMY_SENDER approves the strategy contract to spend 1 WBTC on their behalf | ||
vm.prank(Constants.DUMMY_SENDER); | ||
token.approve(address(avalonLstStrategy), 1 * 1e8); | ||
vm.stopPrank(); | ||
|
||
assertEq(avalonSolvBtcBBNToken.balanceOf(address(Constants.DUMMY_RECEIVER)), 0); | ||
|
||
vm.prank(Constants.DUMMY_SENDER); | ||
avalonLstStrategy.handleGatewayMessageWithSlippageArgs( | ||
token, 1 * 1e8, Constants.DUMMY_RECEIVER, StrategySlippageArgs(0) | ||
); | ||
vm.stopPrank(); | ||
|
||
assertEq(avalonSolvBtcBBNToken.balanceOf(address(Constants.DUMMY_RECEIVER)), 1 ether); | ||
} | ||
} |
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,13 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.13; | ||
|
||
/** | ||
* @title Commonly used constants. | ||
*/ | ||
library Constants { | ||
// Used for providing a uniform interface for functions that deal with both ERC20 and native tokens. | ||
address constant WBTC_ADDRESS = 0x03C7054BCB39f7b2e5B2c7AcB37583e32D70Cfa3; | ||
address constant TBTC_ADDRESS = 0xBBa2eF945D523C4e2608C9E1214C2Cc64D4fc2e2; | ||
address constant DUMMY_SENDER = 0x999999cf1046e68e36E1aA2E0E07105eDDD1f08E; | ||
address constant DUMMY_RECEIVER = 0xa2adc38f06704Cd2e633e8656DbF8B3224E840b8; | ||
} |
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,121 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.13; | ||
|
||
import {stdStorage, StdStorage, Test, console} from "forge-std/Test.sol"; | ||
|
||
using stdStorage for StdStorage; | ||
|
||
import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol"; | ||
import {ERC20} from "openzeppelin-contracts/token/ERC20/ERC20.sol"; | ||
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; | ||
|
||
import {IAvalonIPool, AvalonLendingStrategy} from "../../../src/gateway/strategy/AvalonStrategy.sol"; | ||
import {StrategySlippageArgs} from "../../../src/gateway/CommonStructs.sol"; | ||
|
||
contract DummyPoolImplementation is IAvalonIPool { | ||
ArbitaryErc20 avalonToken; | ||
bool private doSupply; | ||
|
||
// Constructor with a flag to determine supply behavior | ||
constructor(ArbitaryErc20 _avalonToken, bool _doSupply) { | ||
doSupply = _doSupply; | ||
avalonToken = _avalonToken; | ||
} | ||
|
||
// Supply function behavior changes based on the flag | ||
function supply(address, /* asset */ uint256 amount, address onBehalfOf, uint16 /* referralCode */ ) | ||
external | ||
override | ||
{ | ||
if (doSupply) { | ||
// Supply logic for DummyPoolImplementation2: transfers tokens | ||
avalonToken.transfer(onBehalfOf, amount); | ||
} | ||
// If doSupply is false, no supply action is taken (DummyPoolImplementation behavior) | ||
} | ||
|
||
// Withdraw function (unchanged in both cases) | ||
function withdraw(address, uint256, address /* to */ ) external pure override returns (uint256) { | ||
return 0; | ||
} | ||
} | ||
|
||
contract ArbitaryErc20 is ERC20, Ownable { | ||
constructor(string memory name_, string memory symbol_) ERC20(name_, symbol_) {} | ||
|
||
// Mint function accessible only to the owner | ||
function sudoMint(address to, uint256 amount) public onlyOwner { | ||
_mint(to, amount); // Provided by the OpenZeppelin ERC20 contract | ||
} | ||
} | ||
|
||
// forge test --match-contract AvalonLendingStrategyTest -vv | ||
contract AvalonLendingStrategyTest is Test { | ||
event TokenOutput(address tokenReceived, uint256 amountOut); | ||
|
||
ArbitaryErc20 lendingToken; | ||
ArbitaryErc20 avalonToken; | ||
|
||
function setUp() public { | ||
lendingToken = new ArbitaryErc20("Lending Token", "Lending Token"); | ||
avalonToken = new ArbitaryErc20("Avalon Token", "Avalon Token"); | ||
lendingToken.sudoMint(address(this), 100 ether); // Mint 100 tokens to this contract | ||
} | ||
|
||
function testLendingStrategyForValidAmount() public { | ||
IAvalonIPool dummyPool = new DummyPoolImplementation(avalonToken, true); | ||
avalonToken.sudoMint(address(dummyPool), 100 ether); // Mint 100 tokens to this contract | ||
|
||
AvalonLendingStrategy avalonStrategy = new AvalonLendingStrategy(avalonToken, dummyPool); | ||
|
||
// Approve ionicStrategy to spend 100 tBTC tokens on behalf of this contract | ||
lendingToken.increaseAllowance(address(avalonStrategy), 1 ether); | ||
|
||
vm.expectEmit(); | ||
emit TokenOutput(address(avalonToken), 1 ether); | ||
avalonStrategy.handleGatewayMessageWithSlippageArgs( | ||
lendingToken, 1 ether, vm.addr(1), StrategySlippageArgs(1 ether) | ||
); | ||
|
||
assertEq(avalonToken.balanceOf(vm.addr(1)), 1 ether); | ||
assertEq(lendingToken.balanceOf(address(this)), 99 ether); | ||
} | ||
|
||
function testWhenInsufficientSupplyProvided() public { | ||
IAvalonIPool dummyPool = new DummyPoolImplementation(avalonToken, false); | ||
AvalonLendingStrategy avalonStrategy = new AvalonLendingStrategy(avalonToken, dummyPool); | ||
|
||
// Approve ionicStrategy to spend 100 tBTC tokens on behalf of this contract | ||
lendingToken.increaseAllowance(address(avalonStrategy), 100); | ||
|
||
vm.expectRevert("Insufficient supply provided"); | ||
avalonStrategy.handleGatewayMessageWithSlippageArgs(lendingToken, 100, vm.addr(1), StrategySlippageArgs(0)); | ||
} | ||
|
||
function testWhenInsufficientOutputAmount() public { | ||
IAvalonIPool dummyPool = new DummyPoolImplementation(avalonToken, true); | ||
avalonToken.sudoMint(address(dummyPool), 100 ether); // Mint 100 tokens to this contract | ||
|
||
AvalonLendingStrategy avalonStrategy = new AvalonLendingStrategy(avalonToken, dummyPool); | ||
|
||
// Approve ionicStrategy to spend 100 tBTC tokens on behalf of this contract | ||
lendingToken.increaseAllowance(address(avalonStrategy), 100); | ||
|
||
vm.expectRevert("Insufficient output amount"); | ||
avalonStrategy.handleGatewayMessageWithSlippageArgs( | ||
lendingToken, 100, vm.addr(1), StrategySlippageArgs(100 + 1) | ||
); | ||
} | ||
} | ||
|
||
// forge test --match-contract AvalonLstStrategyTest -vv | ||
contract AvalonLstStrategyTest is Test { | ||
event TokenOutput(address tokenReceived, uint256 amountOut); | ||
|
||
ArbitaryErc20 lendingToken; | ||
ArbitaryErc20 avalonToken; | ||
|
||
function setUp() public { | ||
//ToDo: When solv lst stragey unit test completed | ||
} | ||
} |