-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* forge install: Euphrates * add EuphratesRouter * add tests
- Loading branch information
1 parent
bb1b628
commit 6ce848d
Showing
8 changed files
with
250 additions
and
0 deletions.
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
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
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,20 @@ | ||
import { ethers, run } from 'hardhat'; | ||
|
||
async function main() { | ||
const Factory = await ethers.getContractFactory('EuphratesFactory'); | ||
const factory = await Factory.deploy(); | ||
await factory.deployed(); | ||
|
||
console.log(`euphrates factory address: ${factory.address}`); | ||
console.log('remember to publish it!'); | ||
|
||
await run('verify:verify', { | ||
address: factory.address, | ||
constructorArguments: [], | ||
}); | ||
} | ||
|
||
main().catch((error) => { | ||
console.error(error); | ||
process.exitCode = 1; | ||
}); |
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,62 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.17; | ||
|
||
import { ERC20 } from "solmate/tokens/ERC20.sol"; | ||
import { FeeRegistry } from "./FeeRegistry.sol"; | ||
import { EuphratesRouter, EuphratesInstructions } from "./EuphratesRouter.sol"; | ||
|
||
contract EuphratesFactory { | ||
function deployEuphratesRouter(FeeRegistry fees, EuphratesInstructions memory inst, address euphratesAddress) | ||
public | ||
returns (EuphratesRouter) | ||
{ | ||
// no need to use salt as we want to keep the router address the same for the same fees &instructions | ||
bytes32 salt; | ||
|
||
EuphratesRouter router; | ||
try new EuphratesRouter{salt: salt}(fees, inst, euphratesAddress) returns (EuphratesRouter router_) { | ||
router = router_; | ||
} catch { | ||
router = EuphratesRouter( | ||
address( | ||
uint160( | ||
uint256( | ||
keccak256( | ||
abi.encodePacked( | ||
bytes1(0xff), | ||
address(this), | ||
salt, | ||
keccak256( | ||
abi.encodePacked(type(EuphratesRouter).creationCode, abi.encode(fees, inst)) | ||
) | ||
) | ||
) | ||
) | ||
) | ||
) | ||
); | ||
} | ||
|
||
return router; | ||
} | ||
|
||
function deployEuphratesRouterAndRoute( | ||
FeeRegistry fees, | ||
EuphratesInstructions memory inst, | ||
address euphratesAddress, | ||
ERC20 token | ||
) public { | ||
EuphratesRouter router = deployEuphratesRouter(fees, inst, euphratesAddress); | ||
router.route(token, msg.sender); | ||
} | ||
|
||
function deployEuphratesRouterAndRouteNoFee( | ||
FeeRegistry fees, | ||
EuphratesInstructions memory inst, | ||
address euphratesAddress, | ||
ERC20 token | ||
) public { | ||
EuphratesRouter router = deployEuphratesRouter(fees, inst, euphratesAddress); | ||
router.routeNoFee(token); | ||
} | ||
} |
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,43 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.17; | ||
|
||
import { ERC20 } from "solmate/tokens/ERC20.sol"; | ||
import { SafeTransferLib } from "solmate/utils/SafeTransferLib.sol"; | ||
import { IStakingTo } from "euphrates/IStaking.sol"; | ||
import { BaseRouter } from "./BaseRouter.sol"; | ||
import { FeeRegistry } from "./FeeRegistry.sol"; | ||
|
||
struct EuphratesInstructions { | ||
uint256 poolId; | ||
address recipient; | ||
} | ||
|
||
contract EuphratesRouter is BaseRouter { | ||
using SafeTransferLib for ERC20; | ||
|
||
address private _euphratesAddress; | ||
EuphratesInstructions private _instructions; | ||
|
||
constructor(FeeRegistry fees, EuphratesInstructions memory instructions, address euphratesAddress) | ||
BaseRouter(fees) | ||
{ | ||
_instructions = instructions; | ||
_euphratesAddress = euphratesAddress; | ||
} | ||
|
||
function routeImpl(ERC20 token) internal override { | ||
if (address(token) == address(IStakingTo(_euphratesAddress).shareTypes(_instructions.poolId))) { | ||
bool approved = token.approve(_euphratesAddress, token.balanceOf(address(this))); | ||
require(approved, "EuphratesRouter: approve failed"); | ||
|
||
// This may fail due to the configurations of Euphrates. | ||
// That means user is doing something wrong and will revert. | ||
IStakingTo(_euphratesAddress).stakeTo( | ||
_instructions.poolId, token.balanceOf(address(this)), _instructions.recipient | ||
); | ||
} else { | ||
// received token is not share token, transfer it to recipient to avoid it stuck in this contract | ||
token.safeTransfer(_instructions.recipient, token.balanceOf(address(this))); | ||
} | ||
} | ||
} |
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,94 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.13; | ||
|
||
import "forge-std/Test.sol"; | ||
|
||
import "../src/EuphratesRouter.sol"; | ||
import "../src/FeeRegistry.sol"; | ||
import "./MockToken.sol"; | ||
import "./MockEuphrates.sol"; | ||
|
||
contract EuphratesRouterTest is Test { | ||
FeeRegistry public fees; | ||
MockToken public token1; | ||
MockToken public token2; | ||
MockToken public token3; | ||
MockEuphrates public euphrates; | ||
address public alice = address(0x01010101010101010101); | ||
address public bob = address(0x02020202020202020202); | ||
address public charlie = address(0x03030303030303030303); | ||
|
||
function setUp() public { | ||
token1 = new MockToken("Token1", "TK1"); | ||
token2 = new MockToken("Token2", "TK2"); | ||
token3 = new MockToken("token3", "TK3"); | ||
|
||
Fee[] memory feeArray = new Fee[](2); | ||
feeArray[0] = Fee(address(token1), 1 ether); | ||
feeArray[1] = Fee(address(token2), 2 ether); | ||
|
||
fees = new FeeRegistry(feeArray); | ||
|
||
euphrates = new MockEuphrates(); | ||
//vm.etch(EUPHRATES, address(euphrates).code); | ||
euphrates.addPool(IERC20(address(token1))); | ||
euphrates.addPool(IERC20(address(token2))); | ||
} | ||
|
||
function testRouteWithFee() public { | ||
EuphratesInstructions memory inst = EuphratesInstructions(0, alice); | ||
EuphratesRouter router = new EuphratesRouter(fees, inst, address(euphrates)); | ||
|
||
token1.transfer(address(router), 5 ether); | ||
|
||
vm.prank(bob); | ||
router.route(token1, bob); | ||
|
||
assertEq(token1.balanceOf(address(router)), 0); | ||
assertEq(euphrates.shares(0, alice), 4 ether); // (amount - fee) | ||
assertEq(token1.balanceOf(bob), 1 ether); // fee | ||
} | ||
|
||
function testRouteWithFeeWithOtherRecipient() public { | ||
EuphratesInstructions memory inst = EuphratesInstructions(0, alice); | ||
EuphratesRouter router = new EuphratesRouter(fees, inst, address(euphrates)); | ||
|
||
token1.transfer(address(router), 5 ether); | ||
|
||
vm.prank(bob); | ||
router.route(token1, charlie); | ||
|
||
assertEq(token1.balanceOf(address(router)), 0); | ||
assertEq(euphrates.shares(0, alice), 4 ether); // (amount - fee) | ||
assertEq(token1.balanceOf(charlie), 1 ether); // fee | ||
} | ||
|
||
function testRouteWithoutFee() public { | ||
EuphratesInstructions memory inst = EuphratesInstructions(0, alice); | ||
EuphratesRouter router = new EuphratesRouter(fees, inst, address(euphrates)); | ||
|
||
token1.transfer(address(router), 5 ether); | ||
|
||
vm.prank(bob); | ||
router.routeNoFee(token1); | ||
|
||
assertEq(token1.balanceOf(address(router)), 0); | ||
assertEq(euphrates.shares(0, alice), 5 ether); | ||
assertEq(token1.balanceOf(bob), 0); | ||
} | ||
|
||
function testRouteForNotMatchedToken() public { | ||
EuphratesInstructions memory inst = EuphratesInstructions(0, alice); | ||
EuphratesRouter router = new EuphratesRouter(fees, inst, address(euphrates)); | ||
|
||
token2.transfer(address(router), 5 ether); | ||
|
||
vm.prank(bob); | ||
router.route(token2, bob); | ||
|
||
assertEq(token2.balanceOf(address(router)), 0); | ||
assertEq(euphrates.shares(0, alice), 0); | ||
assertEq(token2.balanceOf(alice), 3 ether); // (amount - fee) | ||
assertEq(token2.balanceOf(bob), 2 ether); // fee | ||
} | ||
} |
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,26 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.17; | ||
|
||
import "euphrates/IStaking.sol"; | ||
import "euphrates/Staking.sol"; | ||
import "./MockToken.sol"; | ||
|
||
contract MockEuphrates is Staking, IStakingTo { | ||
using SafeMath for uint256; | ||
using SafeERC20 for IERC20; | ||
|
||
function stakeTo(uint256 poolId, uint256 amount, address receiver) public override returns (bool) { | ||
require(amount > 0, "cannot stake 0"); | ||
IERC20 shareType = IERC20(address(shareTypes(poolId))); | ||
require(address(shareType) != address(0), "invalid pool"); | ||
|
||
_totalShares[poolId] = _totalShares[poolId].add(amount); | ||
_shares[poolId][receiver] = _shares[poolId][receiver].add(amount); | ||
|
||
shareType.safeTransferFrom(msg.sender, address(this), amount); | ||
|
||
emit Stake(receiver, poolId, amount); | ||
|
||
return true; | ||
} | ||
} |