Skip to content

Instantly share code, notes, and snippets.

@ethereumdegen
Last active March 26, 2023 17:04
Show Gist options
  • Save ethereumdegen/9ac006a8ba6070b6fd685de0d7270432 to your computer and use it in GitHub Desktop.
Save ethereumdegen/9ac006a8ba6070b6fd685de0d7270432 to your computer and use it in GitHub Desktop.
Etherdelta ERC20 Exchange
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface Token {
function transferFrom(address from, address to, uint256 value) external returns (bool);
function transfer(address to, uint256 value) external returns (bool);
}
contract EtherDelta {
address public admin;
mapping (address => mapping (address => uint256)) public tokens;
mapping (address => mapping (bytes32 => bool)) public orders;
mapping (address => mapping (bytes32 => uint256)) public orderFills;
event Order(address indexed tokenGet, uint256 amountGet, address indexed tokenGive, uint256 amountGive, uint256 expires, uint256 nonce, address indexed user);
event Cancel(address indexed tokenGet, uint256 amountGet, address indexed tokenGive, uint256 amountGive, uint256 expires, uint256 nonce, address indexed user, uint8 v, bytes32 r, bytes32 s);
event Trade(address indexed tokenGet, uint256 amountGet, address indexed tokenGive, uint256 amountGive, address get, address give);
event Deposit(address indexed token, address indexed user, uint256 amount, uint256 balance);
event Withdraw(address indexed token, address indexed user, uint256 amount, uint256 balance);
constructor(address admin_) {
admin = admin_;
}
receive() external payable {
revert();
}
function changeAdmin(address admin_) external {
require(msg.sender == admin, "not admin");
admin = admin_;
}
function deposit() external payable {
tokens[address(0)][msg.sender] += msg.value;
emit Deposit(address(0), msg.sender, msg.value, tokens[address(0)][msg.sender]);
}
function withdraw(uint256 amount) external {
require(tokens[address(0)][msg.sender] >= amount, "insufficient balance");
tokens[address(0)][msg.sender] -= amount;
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "ETH transfer failed");
emit Withdraw(address(0), msg.sender, amount, tokens[address(0)][msg.sender]);
}
function depositToken(address token, uint256 amount) external {
require(token != address(0), "invalid token");
require(Token(token).transferFrom(msg.sender, address(this), amount), "token transfer failed");
tokens[token][msg.sender] += amount;
emit Deposit(token, msg.sender, amount, tokens[token][msg.sender]);
}
function withdrawToken(address token, uint256 amount) external {
require(token != address(0), "invalid token");
require(tokens[token][msg.sender] >= amount, "insufficient balance");
tokens[token][msg.sender] -= amount;
require(Token(token).transfer(msg.sender, amount), "token transfer failed");
emit Withdraw(token, msg.sender, amount, tokens[token][msg.sender]);
}
function balanceOf(address token, address user) external view returns (uint256) {
return tokens[token][user];
}
function order(address tokenGet, uint256 amountGet, address tokenGive, uint256 amountGive, uint256 expires, uint256 nonce) external {
bytes32 hash = keccak256(abi.encodePacked(address(this), tokenGet, amountGet, tokenGive, amountGive, expires, nonce));
orders[msg.sender][hash] = true;
emit Order(tokenGet, amountGet, tokenGive, amountGive, expires, nonce, msg.sender);
}
function trade(address tokenGet, uint256 amountGet, address tokenGive, uint256 amountGive, uint256 expires, uint256 nonce, address user, uint8 v, bytes32 r, bytes32 s, uint256 amount) external {
bytes32 hash = keccak256(abi.encodePacked(address(this), tokenGet, amountGet, tokenGive, amountGive, expires, nonce));
require((orders[user][hash] || ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)), v, r, s) == user) && block.number <= expires && (orderFills[user][hash] + amount) <= amountGet, "invalid trade");
tradeBalances(tokenGet, amountGet, tokenGive, amountGive, user, amount);
orderFills[user][hash] = (orderFills[user][hash] + amount);
emit Trade(tokenGet, amount, tokenGive, amountGive * amount / amountGet, user, msg.sender);
}
function tradeBalances(address tokenGet, uint256 amountGet, address tokenGive, uint256 amountGive, address user, uint256 amount) private {
tokens[tokenGet][msg.sender] = (tokens[tokenGet][msg.sender]- amount);
tokens[tokenGet][user] = (tokens[tokenGet][user] + amount);
tokens[tokenGive][user] = (tokens[tokenGive][user] - ( (amountGive * amount) / amountGet));
tokens[tokenGive][msg.sender] = (tokens[tokenGive][msg.sender] + ((amountGive * amount) / amountGet));
}
function testTrade(address tokenGet, uint256 amountGet, address tokenGive, uint256 amountGive, uint256 expires, uint256 nonce, address user, uint8 v, bytes32 r, bytes32 s, uint256 amount, address sender) external view returns (bool) {
return tokens[tokenGet][sender] >= amount && availableVolume(tokenGet, amountGet, tokenGive, amountGive, expires, nonce, user, v, r, s) >= amount;
}
function availableVolume(address tokenGet, uint256 amountGet, address tokenGive, uint256 amountGive, uint256 expires, uint256 nonce, address user, uint8 v, bytes32 r, bytes32 s) public view returns (uint256) {
bytes32 hash = keccak256(abi.encodePacked(address(this), tokenGet, amountGet, tokenGive, amountGive, expires, nonce));
if (!(orders[user][hash] || ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)), v, r, s) == user) || block.number > expires) {
return 0;
}
uint256 available1 = (amountGet - orderFills[user][hash]);
uint256 available2 = (tokens[tokenGive][user] * amountGet) / amountGive;
return available1 < available2 ? available1 : available2;
}
function amountFilled(address tokenGet, uint256 amountGet, address tokenGive, uint256 amountGive, uint256 expires, uint256 nonce, address user, uint8 v, bytes32 r, bytes32 s) external view returns (uint256) {
bytes32 hash = keccak256(abi.encodePacked(address(this), tokenGet, amountGet, tokenGive, amountGive, expires, nonce));
return orderFills[user][hash];
}
function cancelOrder(address tokenGet, uint256 amountGet, address tokenGive, uint256 amountGive, uint256 expires, uint256 nonce, uint8 v, bytes32 r, bytes32 s) external {
bytes32 hash = keccak256(abi.encodePacked(address(this), tokenGet, amountGet, tokenGive, amountGive, expires, nonce));
require(orders[msg.sender][hash] || ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)), v, r, s) == msg.sender, "not authorized");
orderFills[msg.sender][hash] = amountGet;
emit Cancel(tokenGet, amountGet, tokenGive, amountGive, expires, nonce, msg.sender, v, r, s);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment