Skip to content

Instantly share code, notes, and snippets.

@crypto-perry
Created September 15, 2019 09:02
Show Gist options
  • Save crypto-perry/a1c67079730f31a0081c271c6853ae60 to your computer and use it in GitHub Desktop.
Save crypto-perry/a1c67079730f31a0081c271c6853ae60 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.11+commit.c082d0b4.js&optimize=false&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);
}
library SafeMath {
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a);
return c;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b);
return c;
}
}
contract ERC20OptionTrade {
using SafeMath for uint256;
enum TradeState {None, SellPaid, BuyPaid, Canceled, Matched, Closed, Expired}
struct Trade {
address payable buyer;
address payable seller;
string symbol;
uint256 payment;
uint256 amountOfTokens;
uint256 deposit;
uint256 expiration;
TradeState state;
}
event OpenTrade(uint256 tradeId, address indexed buyer, address indexed seller, string symbol, uint256 pricePerToken, uint256 amountOfTokens, uint256 depositPercentage, uint256 expiration, TradeState state);
event UpdateTrade(uint256 tradeId, address indexed buyer, address indexed seller, TradeState state);
address private owner;
uint256 public feesGathered;
mapping (uint256 => Trade) public trades;
mapping (bytes32 => IERC20) private tokens;
modifier onlyOwner {
require(msg.sender == owner);
_;
}
constructor() public {
owner = msg.sender;
}
function() external payable {
revert();
}
function A_trade(bool wantToBuy, string memory symbol, uint256 amountOfTokens, uint256 pricePerToken, uint256 depositPercentage, uint256 expiration, address payable other) public payable {
require(tokens[convert(symbol)] != IERC20(0x0));
require(pricePerToken >= 1000); // min price so that divisions with 1000 never give remainder
Trade memory t;
(t.symbol, t.payment, t.amountOfTokens, t.deposit, t.expiration)
= (symbol, pricePerToken.mul(amountOfTokens), amountOfTokens, pricePerToken.mul(amountOfTokens).mul(depositPercentage) / 100, expiration);
uint256 paymentRequired;
(t.buyer, t.seller, t.state, paymentRequired) = wantToBuy
? (msg.sender, other, TradeState.BuyPaid, t.payment.add(computeFee(t.payment)))
: (other, msg.sender, TradeState.SellPaid, t.deposit.add(computeFee(t.payment)));
require(msg.value >= paymentRequired);
uint256 tradeId = uint256(keccak256(abi.encodePacked(t.buyer, t.seller, t.symbol, t.amountOfTokens, pricePerToken, depositPercentage, t.expiration)));
Trade storage existingTrade = trades[tradeId];
if (existingTrade.state == TradeState.None) {
emit OpenTrade(tradeId, t.buyer, t.seller, t.symbol, pricePerToken, t.amountOfTokens, depositPercentage, t.expiration, t.state);
trades[tradeId] = t;
} else if (t.state == TradeState.BuyPaid && existingTrade.state == TradeState.SellPaid
|| t.state == TradeState.SellPaid && existingTrade.state == TradeState.BuyPaid) {
existingTrade.state = TradeState.Matched;
emit UpdateTrade(tradeId, t.buyer, t.seller, existingTrade.state);
} else {
revert();
}
msg.sender.transfer(msg.value - paymentRequired);
}
function B_matchTrade(uint256 tradeId) public payable {
Trade storage t = trades[tradeId];
uint256 paymentRequired;
if(t.state == TradeState.SellPaid) {
if (t.buyer == address(0x0)) {
t.buyer = msg.sender;
} else {
require(msg.sender == t.buyer);
}
paymentRequired = t.payment.add(computeFee(t.payment));
} else if(t.state == TradeState.BuyPaid) {
if (t.seller == address(0x0)) {
t.seller = msg.sender;
} else {
require(msg.sender == t.seller);
}
paymentRequired = t.deposit.add(computeFee(t.payment));
} else {
revert();
}
require(msg.value >= paymentRequired);
t.state = TradeState.Matched;
emit UpdateTrade(tradeId, t.buyer, t.seller, t.state);
msg.sender.transfer(msg.value - paymentRequired);
}
function B_cancelOpenTrade(uint256 tradeId) public {
Trade storage t = trades[tradeId];
require(t.state == TradeState.SellPaid || t.state == TradeState.BuyPaid);
require(msg.sender == t.seller || msg.sender == t.buyer);
address payable actor; uint256 refund;
(actor, refund) = (t.state == TradeState.SellPaid)
? (t.seller, t.deposit.add(computeFee(t.payment)))
: (t.buyer, t.payment.add(computeFee(t.payment)));
t.state = TradeState.Canceled;
emit UpdateTrade(tradeId, t.buyer, t.seller, t.state);
actor.transfer(refund);
}
function C_completeTrade(uint256 tradeId) public {
Trade storage t = trades[tradeId];
require(t.state == TradeState.Matched);
t.state = TradeState.Closed;
feesGathered += computeFee(t.payment).mul(2);
require(tokens[convert(t.symbol)].transferFrom(t.seller, t.buyer, t.amountOfTokens));
t.seller.transfer(t.payment + t.deposit);
emit UpdateTrade(tradeId, t.buyer, t.seller, t.state);
}
function C_claimExpiredTrade(uint256 tradeId) public {
Trade storage t = trades[tradeId];
require(t.state == TradeState.Matched && msg.sender == t.buyer && t.expiration < now);
t.state = TradeState.Expired;
feesGathered += computeFee(t.payment).mul(2);
t.buyer.transfer(t.payment + t.deposit);
emit UpdateTrade(tradeId, t.buyer, t.seller, t.state);
}
function _withdrawFees(uint256 amount) public onlyOwner {
require(feesGathered >= amount);
feesGathered -= amount;
msg.sender.transfer(amount);
}
function computeFee(uint256 value) private pure returns (uint256) {
return value.mul(5) / 1000; // This is the fee we take on each side (0.5%)
}
function convert(string memory key) private pure returns (bytes32 ret) {
require(bytes(key).length <= 32);
assembly {
ret := mload(add(key, 32))
}
}
function getExpirationAfter(uint256 amountOfHours) public view returns (uint256) {
return now.add(amountOfHours.mul(1 hours));
}
function tradeInfo(bool wantToBuy, string memory symbol, uint256 amountOfTokens,
uint256 priceOfOneToken, uint256 depositPercentage, uint256 expiration, address payable other) public view
returns (uint256 _tradeId, uint256 _buySideTotal, uint256 _sellSideTotal, TradeState _state) {
_buySideTotal = amountOfTokens.mul(priceOfOneToken);
_sellSideTotal = depositPercentage.mul(_buySideTotal) / 100;
_sellSideTotal = _sellSideTotal.add(computeFee(_buySideTotal));
_buySideTotal = _buySideTotal.add(computeFee(_buySideTotal));
address payable buyer; address payable seller; (buyer, seller) = wantToBuy ? (msg.sender, other) : (other, msg.sender);
uint256 tradeId = uint256(keccak256(abi.encodePacked(buyer, seller, symbol, amountOfTokens, priceOfOneToken, depositPercentage, expiration)));
return (tradeId, _buySideTotal, _sellSideTotal, trades[tradeId].state);
}
function _setTokenAddress(string memory symbol, address token) public onlyOwner {
tokens[convert(symbol)] = IERC20(token);
}
function getTokenAddress(string memory symbol) public view returns (IERC20) {
return tokens[convert(symbol)];
}
}
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 ERC20Swap {
enum TradeState {None, SellPaid, BuyPaid, Closed, Expired}
struct Trade {
address payable buyer;
address payable seller;
address token;
uint256 ethAmount;
uint256 tokenAmount;
uint256 expiration;
TradeState state;
}
event OpenTrade(address indexed buyer, address indexed seller, uint256 tradeId, address token, uint256 ethAmount, uint256 tokenAmount, uint256 expiration, TradeState state);
event UpdateTrade(address indexed buyer, address indexed seller, uint256 tradeId, TradeState state);
address public owner;
uint256 public feesGathered;
mapping (uint256 => Trade) public trades;
modifier onlyOwner {
require(msg.sender == owner);
_;
}
constructor() public {
owner = msg.sender;
}
function A_trade(bool wantToBuy, address token, uint256 tokenAmount, uint256 ethAmount, uint256 expiration, address payable other) public payable {
if (wantToBuy) {
uint256 tradeId = uint256(keccak256(abi.encodePacked(msg.sender, other, token, ethAmount, tokenAmount, expiration)));
if (trades[tradeId].state == TradeState.None) {
require(msg.value == ethAmount + computeFee(ethAmount));
Trade memory t;
(t.token, t.ethAmount, t.tokenAmount, t.expiration) = (token, ethAmount, tokenAmount, expiration);
(t.buyer, t.seller, t.state) = (msg.sender, other, TradeState.BuyPaid);
emit OpenTrade(msg.sender, other, tradeId, token, ethAmount, tokenAmount, expiration, TradeState.BuyPaid);
trades[tradeId] = t;
} else {
revert();
}
} else {
uint256 tradeId = uint256(keccak256(abi.encodePacked(other, msg.sender, token, ethAmount, tokenAmount, expiration)));
if (trades[tradeId].state == TradeState.None) {
require(IERC20(token).transferFrom(msg.sender, address(this), tokenAmount));
Trade memory t;
(t.token, t.ethAmount, t.tokenAmount, t.expiration) = (token, ethAmount, tokenAmount, expiration);
(t.buyer, t.seller, t.state) = (other, msg.sender, TradeState.SellPaid);
emit OpenTrade(other, msg.sender, tradeId, token, ethAmount, tokenAmount, expiration, TradeState.SellPaid);
trades[tradeId] = t;
} else {
revert();
}
}
}
function B_matchTrade(uint256 tradeId) public payable {
Trade storage t = trades[tradeId];
require(t.expiration > now);
if(t.state == TradeState.SellPaid) { // If SellPaid
require(msg.sender == t.buyer); // Only buyer can match
require(msg.value == t.ethAmount + computeFee(t.ethAmount)); // Buyer must pay eth and fee
t.state = TradeState.Closed; // Close trade
require(IERC20(t.token).transfer(t.buyer, t.tokenAmount)); // Transfer tokens from this contract to the buyer
} else if(t.state == TradeState.BuyPaid) { // If BuyPaid
require(msg.sender == t.seller); // Only Seller can match
t.state = TradeState.Closed; // Close trade
require(IERC20(t.token).transferFrom(t.seller, t.buyer, t.tokenAmount)); // Transfer tokens from seller to buyer
} else {
revert();
}
t.seller.transfer(t.ethAmount - computeFee(t.ethAmount)); // Pay seller (take fee on his side as well)
emit UpdateTrade(t.buyer, t.seller, tradeId, t.state); // Log the update
feesGathered += 2 * computeFee(t.ethAmount);
}
function B_cancelTrade(uint256 tradeId) public {
Trade storage t = trades[tradeId];
if(t.state == TradeState.SellPaid) { // If SellPaid
require(msg.sender == t.seller); // Only seller can cancel
t.state = TradeState.Expired; // Expire trade
IERC20(t.token).transfer(msg.sender, t.tokenAmount); // Refund seller with his tokens
} else if(t.state == TradeState.BuyPaid) { // If BuyPaid
require(msg.sender == t.buyer); // Only buyer can cancel
t.state = TradeState.Expired; // Expire trade
msg.sender.transfer(t.ethAmount + computeFee(t.ethAmount)); // Refund buyer with his eth
} else {
revert();
}
emit UpdateTrade(t.buyer, t.seller, tradeId, t.state); // Log the update
}
function _withdrawFees() public onlyOwner {
uint256 amount = feesGathered;
feesGathered = 0;
msg.sender.transfer(amount);
}
function computeFee(uint256 value) private pure returns (uint256) {
return value * 5 / 1000; // This is the fee we take on each side (0.5%)
}
function getExpirationAfter(uint256 amountOfHours) public view returns (uint256) {
return now + (amountOfHours * (1 hours));
}
function tradeInfo(bool wantToBuy, address token, uint256 tokenAmount,
uint256 ethAmount, uint256 expiration, address payable other) public view
returns (uint256 _tradeId, uint256 _buySideTotal, TradeState _state) {
_buySideTotal = ethAmount + computeFee(ethAmount);
address payable buyer; address payable seller; (buyer, seller) = wantToBuy ? (msg.sender, other) : (other, msg.sender);
_tradeId = uint256(keccak256(abi.encodePacked(buyer, seller, token, ethAmount, tokenAmount, expiration)));
return (_tradeId, _buySideTotal, trades[_tradeId].state);
}
function() external payable {
revert();
}
}
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 ERC20Dummy is IERC20 {
string public constant name = "ERC20DummyToken";
string public constant symbol = "DUMMY_TOKEN";
constructor() public {}
function totalSupply() public view returns (uint256) {
return 0;
}
function balanceOf(address tokenOwner) public view returns (uint) {
return 0;
}
function transfer(address receiver, uint numTokens) public returns (bool) {
revert();
}
function approve(address delegate, uint numTokens) public returns (bool) {
revert();
}
function allowance(address owner, address delegate) public view returns (uint) {
return 0;
}
function transferFrom(address owner, address buyer, uint numTokens) public returns (bool) {
revert();
}
}
contract ERC20Test is IERC20 {
using SafeMath for uint256;
string public constant name = "ERC20TestToken";
string public constant symbol = "TEST_TOKEN";
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_;
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] -= 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] -= numTokens;
allowed[owner][msg.sender] -= numTokens;
balances[buyer] = balances[buyer].add(numTokens);
emit Transfer(owner, buyer, numTokens);
return true;
}
}
library SafeMath {
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