Skip to content

Instantly share code, notes, and snippets.

@crypto-perry
Created August 9, 2019 18:00
Show Gist options
  • Save crypto-perry/90f75d847542462d78ffe7de0eee30cd to your computer and use it in GitHub Desktop.
Save crypto-perry/90f75d847542462d78ffe7de0eee30cd to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.5.1+commit.c8a2cb62.js&optimize=true&gist=
pragma solidity >=0.5.0;
interface IERC20 {
function transfer(address to, uint256 value) external returns (bool);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
function totalSupply() external view returns (uint256);
function balanceOf(address who) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
contract ERC20OptionTrade {
enum TradeState {None, Sell, Buy, Matched, Closed}
struct Trade {
address payable buyer;
address payable seller;
string symbol;
uint256 payment;
uint256 deposit;
uint256 amountOfTokens;
uint256 expiration;
TradeState state;
}
event OpenTrade(uint256 tradeId, address buyer, address seller, string symbol, uint256 payment, uint256 deposit, uint256 amount, uint256 expiration, TradeState state);
event MatchTrade(uint256 tradeId, address buyer, address seller);
event CloseTrade(uint256 tradeId, address buyer, address seller, bool expired);
address owner;
uint256 feesGathered = 0;
uint256 public tradeCounter = 0;
mapping (uint256 => Trade) public trades;
mapping (bytes32 => IERC20) private tokens;
modifier onlyOwner {
require(msg.sender == owner);
_;
}
constructor() public {
owner = msg.sender;
}
function() external payable {}
function convert(string memory key) private pure returns (bytes32 ret) {
require(bytes(key).length <= 32);
assembly {
ret := mload(add(key, 32))
}
}
function setTokenProduct(string memory symbol, address token) public onlyOwner {
tokens[convert(symbol)] = IERC20(token);
}
function getTokenInfo(string memory symbol) public view returns (IERC20) {
return tokens[convert(symbol)];
}
function openTrade(string memory side, string memory symbol, uint256 amountOfTokens, uint256 priceOfOneToken, uint256 depositPercentage, uint256 expireAfterHours, address payable other) public payable {
require(tokens[convert(symbol)] != IERC20(0x0));
uint256 payment = amountOfTokens * priceOfOneToken;
uint256 depositRequired = depositPercentage * payment / 100;
uint256 fee = computeFee(payment);
if (convert(side) == convert("WTB")) {
require(msg.value >= payment + fee);
if (msg.value > payment + fee) {
msg.sender.transfer(msg.value - payment - fee);
}
tradeCounter++;
trades[tradeCounter] = Trade(msg.sender, other, symbol, payment, depositRequired, amountOfTokens, now + expireAfterHours * 1 hours, TradeState.Buy);
emit OpenTrade(tradeCounter, msg.sender, other, symbol, payment, depositRequired, amountOfTokens, now + expireAfterHours * 1 hours, TradeState.Buy);
} else if (convert(side) == convert("WTS")) {
require(msg.value >= depositRequired + fee);
if (msg.value > depositRequired + fee) {
msg.sender.transfer(msg.value - depositRequired - fee);
}
tradeCounter++;
trades[tradeCounter] = Trade(other, msg.sender, symbol, payment, depositRequired, amountOfTokens, now + expireAfterHours * 1 hours, TradeState.Sell);
emit OpenTrade(tradeCounter, other, msg.sender, symbol, payment, depositRequired, amountOfTokens, now + expireAfterHours * 1 hours, TradeState.Sell);
} else {
revert();
}
}
function cancelOpenTrade(uint256 tradeId) public {
if (trades[tradeId].state == TradeState.Sell) {
require(trades[tradeId].seller == msg.sender);
msg.sender.transfer(trades[tradeId].deposit + computeFee(trades[tradeId].payment));
} else if (trades[tradeId].state == TradeState.Buy) {
require(trades[tradeId].buyer == msg.sender);
msg.sender.transfer(trades[tradeId].payment + computeFee(trades[tradeId].payment));
} else {
revert();
}
trades[tradeId].state = TradeState.Closed;
emit CloseTrade(tradeId, trades[tradeId].buyer, trades[tradeId].seller, false);
}
function matchTrade(uint256 tradeId, string memory side, string memory symbol, uint256 payment, uint256 deposit, uint256 amount, uint256 expiration) public payable {
require(trades[tradeId].state == TradeState.Sell || trades[tradeId].state == TradeState.Buy);
require(trades[tradeId].payment == payment);
require(convert(trades[tradeId].symbol) == convert(symbol));
require(trades[tradeId].deposit == deposit);
require(trades[tradeId].amountOfTokens == amount);
require(trades[tradeId].expiration == expiration);
if (trades[tradeId].buyer == address(0x0)) {
trades[tradeId].buyer = msg.sender;
} else if (trades[tradeId].seller == address(0x0)) {
trades[tradeId].seller = msg.sender;
}
uint256 deposit = trades[tradeId].deposit;
uint256 payment = trades[tradeId].payment;
deposit += computeFee(payment);
payment += computeFee(payment);
if (msg.sender == trades[tradeId].seller) {
require(msg.value >= deposit);
if (msg.value > deposit) {
msg.sender.transfer(msg.value - deposit);
}
} else if (msg.sender == trades[tradeId].buyer) {
require(msg.value >= payment);
if (msg.value > payment) {
msg.sender.transfer(msg.value - payment);
}
} else {
revert("You cannot interact with this trade!");
}
trades[tradeId].state = TradeState.Matched;
emit MatchTrade(tradeId, trades[tradeId].buyer, trades[tradeId].seller);
}
function completeTrade(uint256 tradeId) public {
require(tokens[convert(trades[tradeId].symbol)] != IERC20(0x4));
require(trades[tradeId].state == TradeState.Matched);
trades[tradeId].state = TradeState.Closed;
require(tokens[convert(trades[tradeId].symbol)].transferFrom(trades[tradeId].seller, trades[tradeId].buyer, trades[tradeId].amountOfTokens));
trades[tradeId].seller.transfer(trades[tradeId].payment + trades[tradeId].deposit);
feesGathered += 2 * computeFee(trades[tradeId].payment);
emit CloseTrade(tradeId, trades[tradeId].buyer, trades[tradeId].seller, false);
}
function claimDeposit(uint256 tradeId) public {
require(trades[tradeId].state == TradeState.Matched);
require(trades[tradeId].buyer == msg.sender && trades[tradeId].expiration < now);
trades[tradeId].state = TradeState.Closed;
trades[tradeId].buyer.transfer(trades[tradeId].payment + trades[tradeId].deposit);
feesGathered += 2 * computeFee(trades[tradeId].payment);
emit CloseTrade(tradeId, trades[tradeId].buyer, trades[tradeId].seller, true);
}
function withdrawFees(uint256 amount) public onlyOwner {
require(feesGathered >= amount);
feesGathered -= amount;
msg.sender.transfer(amount);
}
function computeFee(uint256 value) private pure returns (uint256) {
uint256 fee = value * 5;
require(fee / 5 == value); // Check overflow
return fee / 1000; // This is the fee we take on each side (0.5% * payment)
}
}
pragma solidity >=0.5.0;
contract ERC20Basic {
string public constant name = "ERC20BasicTest";
string public constant symbol = "TEST";
uint8 public constant decimals = 18;
event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
event Transfer(address indexed from, address indexed to, uint tokens);
mapping(address => uint256) balances;
mapping(address => mapping (address => uint256)) allowed;
uint256 totalSupply_;
using SafeMath for uint256;
constructor(uint256 total) public {
totalSupply_ = total;
balances[msg.sender] = totalSupply_;
}
function totalSupply() public view returns (uint256) {
return totalSupply_;
}
function balanceOf(address tokenOwner) public view returns (uint) {
return balances[tokenOwner];
}
function transfer(address receiver, uint numTokens) public returns (bool) {
require(numTokens <= balances[msg.sender]);
balances[msg.sender] = balances[msg.sender].sub(numTokens);
balances[receiver] = balances[receiver].add(numTokens);
emit Transfer(msg.sender, receiver, numTokens);
return true;
}
function approve(address delegate, uint numTokens) public returns (bool) {
allowed[msg.sender][delegate] = numTokens;
emit Approval(msg.sender, delegate, numTokens);
return true;
}
function allowance(address owner, address delegate) public view returns (uint) {
return allowed[owner][delegate];
}
function transferFrom(address owner, address buyer, uint numTokens) public returns (bool) {
require(numTokens <= balances[owner]);
require(numTokens <= allowed[owner][msg.sender]);
balances[owner] = balances[owner].sub(numTokens);
allowed[owner][msg.sender] = allowed[owner][msg.sender].sub(numTokens);
balances[buyer] = balances[buyer].add(numTokens);
emit Transfer(owner, buyer, numTokens);
return true;
}
}
library SafeMath {
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment