Skip to content
This repository was archived by the owner on Jan 10, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion script/DeployLocal.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ contract DeployLocalScript is Script {

// Link
store.link(address(trade), address(pool), address(usdc), address(clp));
trade.link(address(chainlink), address(pool), address(store));
trade.link(address(chainlink), address(pool), address(store), 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
pool.link(address(trade), address(store));
console.log("Contracts linked");

Expand Down
145 changes: 132 additions & 13 deletions src/Trade.sol
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import "./Chainlink.sol";
import "./Store.sol";
import "./Pool.sol";
import "./interfaces/ITrade.sol";
import "./interfaces/IUniswapV2Router02.sol";
import "./interfaces/IUniswapV2Factory.sol";

contract Trade is ITrade {

using SafeERC20 for IERC20;

uint256 public constant UNIT = 10**18;
uint256 public constant BPS_DIVIDER = 10000;

Expand All @@ -16,17 +22,19 @@ contract Trade is ITrade {
Chainlink public chainlink;
Pool public pool;
Store public store;
IUniswapV2Router02 public router;

// Methods

constructor() {
gov = msg.sender;
}

function link(address _chainlink, address _pool, address _store) external onlyGov {
function link(address _chainlink, address _pool, address _store, address _router) external onlyGov {
chainlink = Chainlink(_chainlink);
pool = Pool(_pool);
store = Store(_store);
router = IUniswapV2Router02(_router);
}

function deposit(uint256 amount) external {
Expand All @@ -35,7 +43,116 @@ contract Trade is ITrade {
store.incrementBalance(msg.sender, amount);
emit Deposit(msg.sender, amount);
}


function depositThroughUniswap(
uint256 amountIn,
address[] memory path
)
external
payable
{
require(path.length > 1, "!path");
require(amountIn > 0, "!amountIn");

require(path[path.length - 1] == store.currency(), "!currency");

address inputCurrency = path[0];
if (msg.value > 0) {
require(amountIn == msg.value, "!msg.value");
} else {
IERC20(inputCurrency).safeTransferFrom(msg.sender, address(this), amountIn);
IERC20(inputCurrency).approve(address(router), amountIn);
}

uint256[] memory amountsOut;
if (msg.value > 0) {
amountsOut = router.swapExactETHForTokens{
value: amountIn
}(
0,
path,
address(store),
block.timestamp
);
} else {
amountsOut = router.swapExactTokensForTokens(
amountIn,
0,
path,
address(store),
block.timestamp
);
}

uint256 amount = amountsOut[amountsOut.length - 1];
store.incrementBalance(msg.sender, amount);
emit Deposit(msg.sender, amount);
}

function addLiquidityThroughUniswap(
address tokenA,
address tokenB,
uint256 amountA,
uint256 amountB
)
external
payable
{
IUniswapV2Factory factor = IUniswapV2Factory(router.factory());
require(factor.getPair(tokenA, tokenB) == store.currency(), "!currency");
require(amountA > 0 && amountB > 0, "!amount");

if (msg.value > 0) {
require(amountA == msg.value, "!msg.value");
require(tokenA == router.WETH(), "!tokenA");
} else {
IERC20(tokenA).safeTransferFrom(msg.sender, address(this), amountA);
IERC20(tokenA).approve(address(router), amountA);
}
IERC20(tokenB).safeTransferFrom(msg.sender, address(this), amountB);
IERC20(tokenB).approve(address(router), amountB);

uint256 amountAUsed;
uint256 amountBUsed;
uint256 liquidity;
if (msg.value > 0) {
(amountBUsed, amountAUsed, liquidity) = router.addLiquidityETH{
value: amountA
}(
tokenB,
amountB,
0,
0,
address(store),
block.timestamp
);
if (amountA > amountAUsed) {
payable(msg.sender).transfer(amountA - amountAUsed);
}
} else {
(amountAUsed, amountBUsed, liquidity) = router.addLiquidity(
tokenA,
tokenB,
amountA,
amountB,
0,
0,
address(store),
block.timestamp
);
if (amountA > amountAUsed) {
IERC20(tokenB).transfer(msg.sender, amountA - amountAUsed);
}
}

if (amountB > amountBUsed) {
IERC20(tokenB).transfer(msg.sender, amountB - amountBUsed);
}

store.incrementBalance(msg.sender, liquidity);
emit Deposit(msg.sender, liquidity);
}

function withdraw(uint256 amount) external {
require(amount > 0, "!amount");
address user = msg.sender;
Expand All @@ -54,9 +171,20 @@ contract Trade is ITrade {
}

function submitOrder(Store.Order memory params, uint256 tpPrice, uint256 slPrice) external {

address user = msg.sender;


// check equity
int256 upl = getUpl(user);
uint256 balance = store.getBalance(user);
int256 equity = int256(balance) + upl;
uint256 lockedMargin = store.getLockedMargin(user);

if (int256(lockedMargin + params.margin) > equity) {
uint256 oldMargin = params.margin;
params.margin = uint256(equity - int256(lockedMargin));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, but here size must also be adjusted so leverage stays consistent. @0xlock

params.size = params.size * params.margin / oldMargin;
}

Store.Market memory market = store.getMarket(params.market);
require(market.maxLeverage > 0, "!market");
require(market.minSize <= params.size, "!min-size");
Expand All @@ -70,17 +198,8 @@ contract Trade is ITrade {
require(leverage <= market.maxLeverage * UNIT, "!max-leverage");

store.lockMargin(user, params.margin);

}

// check equity
int256 upl = getUpl(user);
uint256 balance = store.getBalance(user);
int256 equity = int256(balance) + upl;
uint256 lockedMargin = store.getLockedMargin(user);

require(int256(lockedMargin) <= equity, "!equity");

// fee
uint256 fee = market.fee * params.size / BPS_DIVIDER;
store.decrementBalance(user, fee);
Expand Down
17 changes: 17 additions & 0 deletions src/interfaces/IUniswapV2Factory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
pragma solidity >=0.5.0;

interface IUniswapV2Factory {
event PairCreated(address indexed token0, address indexed token1, address pair, uint);

function feeTo() external view returns (address);
function feeToSetter() external view returns (address);

function getPair(address tokenA, address tokenB) external view returns (address pair);
function allPairs(uint) external view returns (address pair);
function allPairsLength() external view returns (uint);

function createPair(address tokenA, address tokenB) external returns (address pair);

function setFeeTo(address) external;
function setFeeToSetter(address) external;
}
95 changes: 95 additions & 0 deletions src/interfaces/IUniswapV2Router01.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
pragma solidity >=0.6.2;

interface IUniswapV2Router01 {
function factory() external pure returns (address);
function WETH() external pure returns (address);

function addLiquidity(
address tokenA,
address tokenB,
uint amountADesired,
uint amountBDesired,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) external returns (uint amountA, uint amountB, uint liquidity);
function addLiquidityETH(
address token,
uint amountTokenDesired,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external payable returns (uint amountToken, uint amountETH, uint liquidity);
function removeLiquidity(
address tokenA,
address tokenB,
uint liquidity,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) external returns (uint amountA, uint amountB);
function removeLiquidityETH(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external returns (uint amountToken, uint amountETH);
function removeLiquidityWithPermit(
address tokenA,
address tokenB,
uint liquidity,
uint amountAMin,
uint amountBMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s
) external returns (uint amountA, uint amountB);
function removeLiquidityETHWithPermit(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s
) external returns (uint amountToken, uint amountETH);
function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function swapTokensForExactTokens(
uint amountOut,
uint amountInMax,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
external
payable
returns (uint[] memory amounts);
function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
external
returns (uint[] memory amounts);
function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
external
returns (uint[] memory amounts);
function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
external
payable
returns (uint[] memory amounts);

function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
}
44 changes: 44 additions & 0 deletions src/interfaces/IUniswapV2Router02.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
pragma solidity >=0.6.2;

import './IUniswapV2Router01.sol';

interface IUniswapV2Router02 is IUniswapV2Router01 {
function removeLiquidityETHSupportingFeeOnTransferTokens(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external returns (uint amountETH);
function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s
) external returns (uint amountETH);

function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external;
function swapExactETHForTokensSupportingFeeOnTransferTokens(
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external payable;
function swapExactTokensForETHSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external;
}