Skip to content

Instantly share code, notes, and snippets.

@crypto-perry
Created October 8, 2019 02:17
Show Gist options
  • Save crypto-perry/47ee740fdcca118e834324efe27570d1 to your computer and use it in GitHub Desktop.
Save crypto-perry/47ee740fdcca118e834324efe27570d1 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=true&gist=
pragma solidity >=0.5.0;
import "./ierc20.sol";
contract ERC20Controlled is IERC20 {
string public constant name = "ERC20ControlledToken";
string public constant symbol = "CTRL";
// The owner can set butlers and bots.
address private owner;
modifier onlyOwner {
require(owner == msg.sender);
_;
}
// Accounts allowed to use this proxy token to spend tokens.
mapping(address => bool) private butlers;
modifier onlyButler {
require(butlers[msg.sender]);
_;
}
// Accounts allowed to record trades on the other blockchain.
mapping(address => bool) private bots;
modifier onlyBot {
require(bots[msg.sender]);
_;
}
// Balances of users.
mapping(address => uint256) private balances;
// Event to be emited when the bots need to complete a trade to some user.
event Transfer(address indexed from, address indexed to, uint tokens);
// Event to be emited whenever a user tries to link his other blockchain address to his ethereum address.
event AddressMatch(address ethAddress, bytes tokenAddress, bytes proof);
constructor() public {
owner = msg.sender;
}
function matchAddress(bytes memory addr, bytes memory proof) public {
emit AddressMatch(msg.sender, addr, proof);
}
function addButler(address butler) public onlyOwner {
butlers[butler] = true;
}
function removeButler(address butler) public onlyOwner {
butlers[butler] = false;
}
function addBot(address bot) public onlyOwner {
bots[bot] = true;
}
function removeBot(address bot) public onlyOwner {
bots[bot] = false;
}
function approve(address spender, uint numTokens) public onlyBot returns (bool) {
uint256 res = balances[spender] + numTokens;
assert(res >= numTokens); // Protect against overflow.
balances[spender] = res;
return true;
}
function revokeApproval(address spender, uint numTokens) public onlyBot {
if (balances[spender] >= numTokens) {
balances[spender] -= (numTokens);
} else {
balances[spender] = 0;
}
}
function totalSupply() external view returns (uint256) {
return 0;
}
function balanceOf(address who) external view returns (uint256) {
return balances[who];
}
function allowance(address who, address spender) external view returns (uint256) {
return butlers[spender] ? balances[who] : 0;
}
function transfer(address to, uint numTokens) public onlyButler returns (bool) {
emit Transfer(address(this), to, numTokens);
return true;
}
function transferFrom(address from, address to, uint numTokens) public onlyButler returns (bool) {
require(numTokens <= balances[from]);
balances[from] -= numTokens;
if (!butlers[to]) {
emit Transfer(address(this), to, numTokens);
}
return true;
}
}
pragma solidity >=0.5.0;
import "./ierc20.sol";
import './safemathlib.sol';
contract ERC20OpenBuy {
using SafeMath for uint256;
event UpdateBuy(address maker, address token, uint256 weiPerToken, uint256 expiration, uint256 weiRemaining);
address public owner;
uint256 public feesGathered;
mapping (uint256 => uint256) public buyOrders;
modifier onlyOwner {
require(msg.sender == owner);
_;
}
constructor() public {
owner = msg.sender;
}
function addToBuyOffer(address token, uint256 weiPerToken, uint256 expiration, uint256 tokenAmount) public payable {
require(weiPerToken % 1000 == 0, "Price must be multiple of 1000!"); // We require this to guarantee fees will add up.
uint256 tradeId = uint256(keccak256(abi.encodePacked(msg.sender, token, weiPerToken, expiration)));
uint256 weiPayment = tokenAmount.mul(weiPerToken);
uint256 fee = computeFee(weiPayment);
require(msg.value == weiPayment.add(fee), "Not enough ether sent!"); // Add fee and check eth sent
buyOrders[tradeId] = buyOrders[tradeId].add(weiPayment); // Update trade state
emit UpdateBuy(msg.sender, token, weiPerToken, expiration, buyOrders[tradeId]); // Log open BUY
}
function partialFillBuy(address token, uint256 weiPerToken, uint256 expiration, uint256 tokenAmount, address payable other) public {
require(expiration > now, "Trade expired!");
uint256 tradeId = uint256(keccak256(abi.encodePacked(other, token, weiPerToken, expiration)));
uint256 weiPayment = tokenAmount.mul(weiPerToken);
require(buyOrders[tradeId] >= weiPayment, "Not enough ether in trade!"); // Make sure there are enough tokens
buyOrders[tradeId] = buyOrders[tradeId] - weiPayment;
require(IERC20(token).transferFrom(msg.sender, other, tokenAmount), "Token transfer failed!"); // Take tokens from seller and give to buyer
uint256 fee = computeFee(weiPayment);
msg.sender.transfer(weiPayment - fee);
feesGathered = feesGathered.add(fee).add(fee);
emit UpdateBuy(other, token, weiPerToken, expiration, buyOrders[tradeId]); // Log open BUY
}
function cancelBuyOffer(address token, uint256 weiPerToken, uint256 expiration) public {
uint256 tradeId = uint256(keccak256(abi.encodePacked(msg.sender, token, weiPerToken, expiration)));
uint256 ethAmount = buyOrders[tradeId];
buyOrders[tradeId] = 0;
msg.sender.transfer(ethAmount); // Refund eth to buyer
emit UpdateBuy(msg.sender, token, weiPerToken, expiration, 0); // Log open BUY
}
function _withdrawFees() public onlyOwner {
uint256 amount = feesGathered;
feesGathered = 0;
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() external payable {
revert();
}
}
contract ERC20OpenSell {
using SafeMath for uint256;
event UpdateSell(address maker, address token, uint256 weiPerToken, uint256 expiration, uint256 tokensRemaining);
address public owner;
address public allowedSpender;
uint256 public feesGathered;
mapping (uint256 => uint256) public sellOrders;
modifier onlyOwner {
require(msg.sender == owner);
_;
}
constructor() public {
owner = msg.sender;
}
function addToSellOffer(address token, uint256 weiPerToken, uint256 expiration, uint256 tokenAmount) public {
uint256 tradeId = uint256(keccak256(abi.encodePacked(msg.sender, token, weiPerToken, expiration)));
require(IERC20(token).transferFrom(msg.sender, address(this), tokenAmount)); // Take tokens from seller
sellOrders[tradeId] = sellOrders[tradeId].add(tokenAmount); // Update state
emit UpdateSell(msg.sender, token, weiPerToken, expiration, sellOrders[tradeId]); // Log open SELL
}
function partialFillSell(address token, uint256 weiPerToken, uint256 expiration, address payable other) public payable {
uint256 tradeId = uint256(keccak256(abi.encodePacked(other, token, weiPerToken, expiration)));
uint256 ethPayment = msg.value.mul(1000) / 1005; // Without fee
uint256 tokenAmount = ethPayment / weiPerToken;
require(sellOrders[tradeId] >= tokenAmount); // Make sure there are enough tokens to pay
sellOrders[tradeId] = sellOrders[tradeId] - tokenAmount; // Update state
require(IERC20(token).transfer(msg.sender, tokenAmount)); // Give tokens to buyer
uint256 fee = computeFee(ethPayment);
other.transfer(ethPayment - fee); // Pay the seller
feesGathered = feesGathered.add(fee).add(fee);
emit UpdateSell(other, token, weiPerToken, expiration, sellOrders[tradeId]); // Log open BUY
}
function cancelSellOffer(address token, uint256 weiPerToken, uint256 expiration) public {
uint256 tradeId = uint256(keccak256(abi.encodePacked(msg.sender, token, weiPerToken, expiration)));
uint256 tokenAmount = sellOrders[tradeId];
sellOrders[tradeId] = 0;
IERC20(token).transfer(msg.sender, tokenAmount); // Refund seller
emit UpdateSell(msg.sender, token, weiPerToken, expiration, 0); // Log open SELL
}
function _withdrawFees() public onlyOwner {
uint256 amount = feesGathered;
feesGathered = 0;
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() external payable {
revert();
}
}
pragma solidity >=0.5.0;
import "./ierc20.sol";
import './safemathlib.sol';
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;
import "./ierc20.sol";
library SafeMath {
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
contract ERC20Test is IERC20 {
using SafeMath for uint256;
string public constant name = "ERC20TestToken";
string public constant symbol = "TEST";
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;
}
}
pragma solidity >=0.5.0;
import './ierc20.sol';
import './safemathlib.sol';
contract OTCButlerERC20Trade {
using SafeMath for uint256;
uint256 constant NonePaid = 0;
uint256 constant BuyPaid = 1;
uint256 constant SellPaid = 2;
event UpdateTrade(address indexed buyer, address indexed seller, address token, uint256 tokenAmount, uint256 weiAmount, uint256 expiration, uint256 tradeState);
event UpdateBuy(address maker, address token, uint256 weiPerToken, uint256 expiration, uint256 tokensWanted);
event UpdateSell(address maker, address token, uint256 weiPerToken, uint256 expiration, uint256 tokensOffered);
uint256 public feesGathered;
event ECFAnnouncement(uint256 newEarlyCancellationFee, uint256 enforcementDate);
uint256 public ecf = 1000; // Early cancellation fee in wei (ECF)
uint256 public announcedecf; // This is the announced early cancellation fee (AECF), which can be enforced only after 1 day from announcement!
uint256 public changeEcfWindowStart; // Time after which the ECF can be set to AECF. Can be set at most 3 hours after this time.
function announceNewECF(uint256 newECF) public {
require(msg.sender == owner, "Only owner is allowed to call this!");
announcedecf = newECF;
changeEcfWindowStart = now + 1 days;
emit ECFAnnouncement(newECF, changeEcfWindowStart);
}
function enforceAnnouncedECF() public {
require(changeEcfWindowStart < now && now < changeEcfWindowStart + 3 hours, "Not within allowed window!");
ecf = announcedecf;
changeEcfWindowStart = 0;
}
mapping(uint256 => uint256) public trades;
address payable public owner;
constructor() public {
owner = msg.sender;
}
// Function to create/supplement a buy offer.
// The msg.sender pays ether and requests an amount of tokens at a certain price.
function addToBuyOffer(address token, uint256 weiPerToken, uint256 expiration, uint256 tokenAmount) public payable {
require(weiPerToken > 0 && weiPerToken % 1000 == 0, "Price must be multiple of 1000!"); // We require this to guarantee fees will never give remainder.
uint256 tradeId = uint256(keccak256(abi.encodePacked(msg.sender, address(0), token, weiPerToken, expiration)));
uint256 weiPayment = tokenAmount.mul(weiPerToken);
uint256 fee = computeFee(weiPayment);
require(msg.value == weiPayment.add(fee), "Not enough ether sent!"); // Add fee and check eth sent
trades[tradeId] = trades[tradeId].add(tokenAmount); // Update trade state
emit UpdateBuy(msg.sender, token, weiPerToken, expiration, trades[tradeId]); // Log active BUY
}
// Function to (partially) fill a buy offer.
// The msg.sender will give an amount of tokens at the specified price. He must have allowed our contract to spend on his behalf.
function partialFillBuy(address token, uint256 weiPerToken, uint256 expiration, uint256 tokenAmount, address payable other) public {
require(expiration > now, "Trade expired!");
uint256 tradeId = uint256(keccak256(abi.encodePacked(other, address(0), token, weiPerToken, expiration)));
require(trades[tradeId] >= tokenAmount, "Not enough tokens in offer!");
trades[tradeId] -= tokenAmount;
require(IERC20(token).transferFrom(msg.sender, other, tokenAmount), "Token transfer failed!"); // Take tokens from seller and give to buyer
uint256 weiPayment = tokenAmount.mul(weiPerToken);
uint256 fee = computeFee(weiPayment);
msg.sender.transfer(weiPayment - fee);
feesGathered = feesGathered.add(fee).add(fee);
emit UpdateBuy(other, token, weiPerToken, expiration, trades[tradeId]); // Log active/completed BUY
}
// Function to cancel a buy offer by its maker. Only a trade maker can close a trade. He will pay a fee for early cancellation!
function cancelBuyOffer(address token, uint256 weiPerToken, uint256 expiration) public {
uint256 tradeId = uint256(keccak256(abi.encodePacked(msg.sender, address(0), token, weiPerToken, expiration)));
require(trades[tradeId] > 0, "Trade doesn't exist!");
uint256 weiPayment = trades[tradeId].mul(weiPerToken);
weiPayment = weiPayment.add(computeFee(weiPayment));
trades[tradeId] = 0;
if (expiration > now) { // If trade did not expire yet
if (weiPayment > ecf) { // If enough eth to cover the early cancellation fee
msg.sender.transfer(weiPayment - ecf); // Refund buyer
feesGathered = feesGathered.add(ecf); // but take early cancellation fee
} else { // If not enough ether
feesGathered = feesGathered.add(weiPayment); // Take max possible fee and don't refund
}
} else { // If trade expired
msg.sender.transfer(weiPayment); // Refund buyer without taking fee.
}
emit UpdateBuy(msg.sender, token, weiPerToken, expiration, 0); // Log canceled BUY
}
// Function to create/supplement a sell offer.
// The msg.sender will give an amount of tokens at the specified price. He must have allowed our contract to spend on his behalf.
function addToSellOffer(address token, uint256 weiPerToken, uint256 expiration, uint256 tokenAmount) public {
require(weiPerToken > 0 && weiPerToken % 1000 == 0, "Price must be multiple of 1000!"); // We require this to guarantee fees will never give remainder.
uint256 tradeId = uint256(keccak256(abi.encodePacked(address(0), msg.sender, token, weiPerToken, expiration)));
require(IERC20(token).transferFrom(msg.sender, address(this), tokenAmount), "Token transfer failed!"); // Take tokens from seller and keep in escrow
trades[tradeId] = trades[tradeId].add(tokenAmount); // Update state
emit UpdateSell(msg.sender, token, weiPerToken, expiration, trades[tradeId]); // Log open SELL
}
// Function to (partially) fill a sell offer.
// The msg.sender pays ether in exchange for an amount of tokens at the specified price.
function partialFillSell(address token, uint256 weiPerToken, uint256 expiration, uint256 tokenAmount, address payable other) public payable {
uint256 tradeId = uint256(keccak256(abi.encodePacked(address(0), other, token, weiPerToken, expiration)));
require(trades[tradeId] >= tokenAmount, "Not enough tokens in offer!");
uint256 weiPayment = tokenAmount.mul(weiPerToken);
uint256 fee = computeFee(weiPayment);
require(msg.value == weiPayment.add(fee), "Not enough ether sent!"); // Add fee and check eth sent
trades[tradeId] = trades[tradeId] - tokenAmount; // Update state
require(IERC20(token).transfer(msg.sender, tokenAmount), "Token transfer failed!"); // Give tokens to buyer
other.transfer(weiPayment - fee); // Pay the seller
feesGathered = feesGathered.add(fee).add(fee);
emit UpdateSell(other, token, weiPerToken, expiration, trades[tradeId]); // Log open BUY
}
// Function to cancel a sell offer by its maker. Only a trade maker can close a trade. He will pay a fee for early cancellation!
function cancelSellOffer(address token, uint256 weiPerToken, uint256 expiration) public payable {
uint256 tradeId = uint256(keccak256(abi.encodePacked(address(0), msg.sender, token, weiPerToken, expiration)));
require(trades[tradeId] > 0, "Trade doesn't exist!");
uint256 tokenAmount = trades[tradeId];
if (expiration > now) { // If trade did not expire yet
require(msg.value == ecf, "Early cancellation fee not paid!"); // Take a fee for early cancellation
feesGathered = feesGathered.add(ecf);
}
trades[tradeId] = 0;
IERC20(token).transfer(msg.sender, tokenAmount); // Refund seller
emit UpdateSell(msg.sender, token, weiPerToken, expiration, 0); // Log open SELL
}
// Function to create a direct trade. User must specify all details exactly or he will open a trade instead of closing one.
function directTrade(bool wantToBuy, address payable other, address token, uint256 tokenAmount, uint256 weiAmount, uint256 expiration) public payable {
uint256 tradeId = wantToBuy
? uint256(keccak256(abi.encodePacked(msg.sender, other, token, tokenAmount, weiAmount, expiration)))
: uint256(keccak256(abi.encodePacked(other, msg.sender, token, tokenAmount, weiAmount, expiration)));
uint256 state = trades[tradeId];
if (state == NonePaid) { // If the trade doesn't exist
require(weiAmount > 0 && weiAmount % 1000 == 0, "Price must be multiple of 1000!"); // We require this to guarantee fees will never give remainder.
if (wantToBuy) { // If trying to buy
require(msg.value == weiAmount.add(computeFee(weiAmount)), "Not enough ether sent!"); // Take eth and fee from buyer
trades[tradeId] = BuyPaid; // Update state
emit UpdateTrade(msg.sender, other, token, tokenAmount, weiAmount, expiration, BuyPaid); // Log open BUY
} else { // If trying to sell
require(IERC20(token).transferFrom(msg.sender, address(this), tokenAmount), "Token transfer failed!"); // Take tokens from seller
trades[tradeId] = SellPaid; // Update state
emit UpdateTrade(other, msg.sender, token, tokenAmount, weiAmount, expiration, SellPaid); // Log open SELL
}
} else if (wantToBuy && state == SellPaid) { // If buyer closes the trade
require(expiration > now, "Trade expired!");
uint256 fee = computeFee(weiAmount);
require(msg.value == weiAmount.add(fee), "Not enough ether sent!"); // Take eth and fee from buyer
trades[tradeId] = NonePaid; // Close trade first to protect against reentrancy
require(IERC20(token).transfer(msg.sender, tokenAmount), "Token transfer failed!"); // Send tokens to buyer (from this contract as seller already paid us)
other.transfer(weiAmount - fee); // Send eth - fee to seller
feesGathered = feesGathered.add(fee).add(fee);
emit UpdateTrade(msg.sender, other, token, tokenAmount, weiAmount, expiration, NonePaid); // Log closed trade
} else if (!wantToBuy && state == BuyPaid) { // If seller closes the trade
require(expiration > now, "Trade expired!");
trades[tradeId] = NonePaid; // Close trade first to protect against reentrancy
require(IERC20(token).transferFrom(msg.sender, other, tokenAmount), "Token transfer failed!"); // Send tokens to buyer (directly from seller)
uint256 fee = computeFee(weiAmount);
msg.sender.transfer(weiAmount - fee); // Send eth - fee to seller
feesGathered = feesGathered.add(fee).add(fee);
emit UpdateTrade(other, msg.sender, token, tokenAmount, weiAmount, expiration, NonePaid); // Log closed trade
} else {
revert();
}
}
// Function that allows the trade maker to cancel a direct trade. We will take a fee for cancellations before the expiry time!
function cancelDirectTrade(bool wantToBuy, address payable other, address token, uint256 tokenAmount, uint256 weiAmount, uint256 expiration) public payable {
uint256 tradeId = wantToBuy
? uint256(keccak256(abi.encodePacked(msg.sender, other, token, tokenAmount, weiAmount, expiration)))
: uint256(keccak256(abi.encodePacked(other, msg.sender, token, tokenAmount, weiAmount, expiration)));
uint256 state = trades[tradeId];
if (wantToBuy && state == BuyPaid) { // If buyer cancels the trade
trades[tradeId] = NonePaid; // Close trade first to protect against reentrancy
uint256 weiPayment = weiAmount.add(computeFee(weiAmount));
if (expiration > now) { // If trade did not expire yet
if (weiPayment > ecf) { // If enough eth to cover the early cancellation fee
msg.sender.transfer(weiPayment - ecf); // Refund buyer
feesGathered = feesGathered.add(ecf); // but take early cancellation fee
} else { // If not enough ether
feesGathered = feesGathered.add(weiPayment); // Take max possible fee and don't refund
}
} else { // If trade expired
msg.sender.transfer(weiPayment); // Refund buyer without taking fee.
}
emit UpdateTrade(msg.sender, other, token, tokenAmount, weiAmount, expiration, NonePaid); // Log closed trade
} else if (!wantToBuy && state == SellPaid) { // If seller cancels the trade
if (expiration > now) { // If trade did not expire yet
require(msg.value == ecf, "Not enough eth to cover fee!"); // Take a fee for early cancellation
feesGathered = feesGathered.add(ecf);
}
trades[tradeId] = NonePaid; // Close trade first to protect against reentrancy
require(IERC20(token).transfer(msg.sender, tokenAmount), "Token transfer failed!"); // Refund tokens to seller
emit UpdateTrade(other, msg.sender, token, tokenAmount, weiAmount, expiration, NonePaid); // Log closed trade
} else {
revert("Cannot interact with trade!");
}
}
function _withdrawFees() public {
uint256 amount = feesGathered;
feesGathered = 0;
owner.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() external payable {
revert();
}
event SelfDestructAnnouncement(uint256 selfDestructTime);
uint256 public selfDestructAfter = 2**256 - 1;
function announceSelfDestruct() public {
require(owner == msg.sender);
selfDestructAfter = now + 30 days;
emit SelfDestructAnnouncement(selfDestructAfter);
}
function selfDestruct() public {
require(selfDestructAfter < now);
selfdestruct(owner);
}
}
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);
}
pragma solidity >=0.5.0;
library SafeMath {
function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
c = a + b;
require(c >= a);
return c;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
if (a == 0) {
return 0;
}
c = a * b;
require(c / a == b);
return c;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment