Last active
March 26, 2023 17:04
-
-
Save ethereumdegen/9ac006a8ba6070b6fd685de0d7270432 to your computer and use it in GitHub Desktop.
Etherdelta ERC20 Exchange
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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