文件 1 的 8:Address.sol
pragma solidity 0.5.9;
library Address {
function isContract(address account) internal view returns (bool) {
uint256 size;
assembly { size := extcodesize(account) }
return size > 0;
}
function toPayable(address account) internal pure returns (address payable) {
return address(uint160(account));
}
}
文件 2 的 8:ERC20Interface.sol
pragma solidity 0.5.9;
interface ERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
文件 3 的 8:KyberNetworkProxyInterface.sol
pragma solidity 0.5.9;
import "./ERC20Interface.sol";
interface KyberNetworkProxyInterface {
function tradeWithHint(ERC20 src, uint srcAmount, ERC20 dest, address payable destAddress, uint maxDestAmount,
uint minConversionRate, address walletId, bytes calldata hint) external payable returns(uint);
function maxGasPrice() external view returns(uint);
function getUserCapInWei(address user) external view returns(uint);
function getUserCapInTokenWei(address user, ERC20 token) external view returns(uint);
function enabled() external view returns(bool);
function info(bytes32 id) external view returns(uint);
function getExpectedRate(ERC20 src, ERC20 dest, uint srcQty) external view
returns (uint expectedRate, uint slippageRate);
}
文件 4 的 8:KyberSwapLimitOrder.sol
pragma solidity 0.5.9;
import "./KyberNetworkProxyInterface.sol";
import "./SafeERC20.sol";
import "./SafeMath.sol";
import "./Withdrawable.sol";
contract KyberSwapLimitOrder is Withdrawable {
mapping(address => mapping(uint256 => uint256)) public nonces;
bool public tradeEnabled;
KyberNetworkProxyInterface public kyberNetworkProxy;
uint256 public constant MAX_DEST_AMOUNT = 2 ** 256 - 1;
uint256 public constant PRECISION = 10**4;
using SafeMath for uint256;
using SafeERC20 for ERC20;
constructor(
address _admin,
KyberNetworkProxyInterface _kyberNetworkProxy
)
public
Withdrawable(_admin) {
require(_admin != address(0));
require(address(_kyberNetworkProxy) != address(0));
kyberNetworkProxy = _kyberNetworkProxy;
}
event TradeEnabled(bool tradeEnabled);
function enableTrade() external onlyAdmin {
tradeEnabled = true;
emit TradeEnabled(tradeEnabled);
}
function disableTrade() external onlyAdmin {
tradeEnabled = false;
emit TradeEnabled(tradeEnabled);
}
function listToken(ERC20 token)
external
onlyAdmin
{
require(address(token) != address(0));
token.safeApprove(address(kyberNetworkProxy), MAX_DEST_AMOUNT);
}
struct VerifyParams {
address user;
uint8 v;
uint256 concatenatedTokenAddresses;
uint256 nonce;
bytes32 hashedParams;
bytes32 r;
bytes32 s;
}
struct TradeInput {
ERC20 srcToken;
uint256 srcQty;
ERC20 destToken;
address payable destAddress;
uint256 minConversionRate;
uint256 feeInPrecision;
}
event LimitOrderExecute(address indexed user, uint256 nonce, address indexed srcToken,
uint256 actualSrcQty, uint256 destAmount, address indexed destToken,
address destAddress, uint256 feeInSrcTokenWei);
function executeLimitOrder(
address user,
uint256 nonce,
ERC20 srcToken,
uint256 srcQty,
ERC20 destToken,
address payable destAddress,
uint256 minConversionRate,
uint256 feeInPrecision,
uint8 v,
bytes32 r,
bytes32 s
)
onlyOperator
external
{
require(tradeEnabled);
VerifyParams memory verifyParams;
verifyParams.user = user;
verifyParams.concatenatedTokenAddresses = concatTokenAddresses(address(srcToken), address(destToken));
verifyParams.nonce = nonce;
verifyParams.hashedParams = keccak256(abi.encodePacked(
user, nonce, srcToken, srcQty, destToken, destAddress, minConversionRate, feeInPrecision));
verifyParams.v = v;
verifyParams.r = r;
verifyParams.s = s;
require(verifyTradeParams(verifyParams));
TradeInput memory tradeInput;
tradeInput.srcToken = srcToken;
tradeInput.srcQty = srcQty;
tradeInput.destToken = destToken;
tradeInput.destAddress = destAddress;
tradeInput.minConversionRate = minConversionRate;
tradeInput.feeInPrecision = feeInPrecision;
trade(tradeInput, verifyParams);
}
event OldOrdersInvalidated(address user, uint256 concatenatedTokenAddresses, uint256 nonce);
function invalidateOldOrders(uint256 concatenatedTokenAddresses, uint256 nonce) external {
require(validAddressInNonce(nonce));
require(isValidNonce(msg.sender, concatenatedTokenAddresses, nonce));
updateNonce(msg.sender, concatenatedTokenAddresses, nonce);
emit OldOrdersInvalidated(msg.sender, concatenatedTokenAddresses, nonce);
}
function concatTokenAddresses(address srcToken, address destToken) public pure returns (uint256) {
return ((uint256(srcToken) >> 32) << 128) + (uint256(destToken) >> 32);
}
function validAddressInNonce(uint256 nonce) public view returns (bool) {
return (nonce >> 128) == (uint256(address(this)) >> 32);
}
function isValidNonce(address user, uint256 concatenatedTokenAddresses, uint256 nonce) public view returns (bool) {
return nonce > nonces[user][concatenatedTokenAddresses];
}
function verifySignature(bytes32 hash, uint8 v, bytes32 r, bytes32 s, address user) public pure returns (bool) {
bytes memory prefix = "\x19Ethereum Signed Message:\n32";
bytes32 prefixedHash = keccak256(abi.encodePacked(prefix, hash));
return ecrecover(prefixedHash, v, r, s) == user;
}
function deductFee(uint256 srcQty, uint256 feeInPrecision) public pure returns
(uint256 actualSrcQty, uint256 feeInSrcTokenWei) {
require(feeInPrecision <= 100 * PRECISION);
feeInSrcTokenWei = srcQty.mul(feeInPrecision).div(100 * PRECISION);
actualSrcQty = srcQty.sub(feeInSrcTokenWei);
}
event NonceUpdated(address user, uint256 concatenatedTokenAddresses, uint256 nonce);
function updateNonce(address user, uint256 concatenatedTokenAddresses, uint256 nonce) internal {
nonces[user][concatenatedTokenAddresses] = nonce;
emit NonceUpdated(user, concatenatedTokenAddresses, nonce);
}
function verifyTradeParams(VerifyParams memory verifyParams) internal view returns (bool) {
require(validAddressInNonce(verifyParams.nonce));
require(isValidNonce(verifyParams.user, verifyParams.concatenatedTokenAddresses, verifyParams.nonce));
require(verifySignature(
verifyParams.hashedParams,
verifyParams.v,
verifyParams.r,
verifyParams.s,
verifyParams.user
));
return true;
}
function trade(TradeInput memory tradeInput, VerifyParams memory verifyParams) internal {
tradeInput.srcToken.safeTransferFrom(verifyParams.user, address(this), tradeInput.srcQty);
uint256 actualSrcQty;
uint256 feeInSrcTokenWei;
(actualSrcQty, feeInSrcTokenWei) = deductFee(tradeInput.srcQty, tradeInput.feeInPrecision);
updateNonce(verifyParams.user, verifyParams.concatenatedTokenAddresses, verifyParams.nonce);
uint256 destAmount = kyberNetworkProxy.tradeWithHint(
tradeInput.srcToken,
actualSrcQty,
tradeInput.destToken,
tradeInput.destAddress,
MAX_DEST_AMOUNT,
tradeInput.minConversionRate,
address(this),
"PERM"
);
emit LimitOrderExecute(
verifyParams.user,
verifyParams.nonce,
address(tradeInput.srcToken),
actualSrcQty,
destAmount,
address(tradeInput.destToken),
tradeInput.destAddress,
feeInSrcTokenWei
);
}
}
文件 5 的 8:PermissionGroups.sol
pragma solidity 0.5.9;
contract PermissionGroups {
address public admin;
address public pendingAdmin;
mapping(address=>bool) public operators;
mapping(address=>bool) public alerters;
constructor(address _admin) public {
admin = _admin;
}
modifier onlyAdmin() {
require(msg.sender == admin);
_;
}
modifier onlyOperator() {
require(operators[msg.sender]);
_;
}
modifier onlyAlerter() {
require(alerters[msg.sender]);
_;
}
event TransferAdminPending(address pendingAdmin);
function transferAdmin(address newAdmin) public onlyAdmin {
require(newAdmin != address(0));
emit TransferAdminPending(pendingAdmin);
pendingAdmin = newAdmin;
}
function transferAdminQuickly(address newAdmin) public onlyAdmin {
require(newAdmin != address(0));
emit TransferAdminPending(newAdmin);
emit AdminClaimed(newAdmin, admin);
admin = newAdmin;
}
event AdminClaimed( address newAdmin, address previousAdmin);
function claimAdmin() public {
require(pendingAdmin == msg.sender);
emit AdminClaimed(pendingAdmin, admin);
admin = pendingAdmin;
pendingAdmin = address(0);
}
event AlerterAdded (address newAlerter, bool isAdd);
function addAlerter(address newAlerter) public onlyAdmin {
require(!alerters[newAlerter]);
alerters[newAlerter] = true;
emit AlerterAdded(newAlerter, true);
}
function removeAlerter (address alerter) public onlyAdmin {
require(alerters[alerter]);
alerters[alerter] = false;
emit AlerterAdded(alerter, false);
}
event OperatorAdded(address newOperator, bool isAdd);
function addOperator(address newOperator) public onlyAdmin {
require(!operators[newOperator]);
operators[newOperator] = true;
emit OperatorAdded(newOperator, true);
}
function removeOperator (address operator) public onlyAdmin {
require(operators[operator]);
operators[operator] = false;
emit OperatorAdded(operator, false);
}
}
文件 6 的 8:SafeERC20.sol
pragma solidity 0.5.9;
import "./ERC20Interface.sol";
import "./SafeMath.sol";
import "./Address.sol";
library SafeERC20 {
using SafeMath for uint256;
using Address for address;
function safeTransfer(ERC20 token, address to, uint256 value) internal {
callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(ERC20 token, address from, address to, uint256 value) internal {
callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(ERC20 token, address spender, uint256 value) internal {
require((value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(ERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).add(value);
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(ERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value);
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function callOptionalReturn(ERC20 token, bytes memory data) private {
require(address(token).isContract(), "SafeERC20: call to non-contract");
(bool success, bytes memory returndata) = address(token).call(data);
require(success, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
文件 7 的 8:SafeMath.sol
pragma solidity 0.5.9;
library SafeMath {
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
uint256 c = a - b;
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, "SafeMath: multiplication overflow");
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: division by zero");
uint256 c = a / b;
return c;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0, "SafeMath: modulo by zero");
return a % b;
}
}
文件 8 的 8:Withdrawable.sol
pragma solidity 0.5.9;
import "./PermissionGroups.sol";
import "./SafeERC20.sol";
contract Withdrawable is PermissionGroups {
using SafeERC20 for ERC20;
constructor(address _admin) public PermissionGroups (_admin) {}
event TokenWithdraw(ERC20 token, uint amount, address sendTo);
function withdrawToken(ERC20 token, uint amount, address sendTo) external onlyAdmin {
token.safeTransfer(sendTo, amount);
emit TokenWithdraw(token, amount, sendTo);
}
event EtherWithdraw(uint amount, address sendTo);
function withdrawEther(uint amount, address payable sendTo) external onlyAdmin {
sendTo.transfer(amount);
emit EtherWithdraw(amount, sendTo);
}
}
{
"compilationTarget": {
"KyberSwapLimitOrder.sol": "KyberSwapLimitOrder"
},
"evmVersion": "petersburg",
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"constant":false,"inputs":[],"name":"enableTrade","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"alerter","type":"address"}],"name":"removeAlerter","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"operators","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"}],"name":"listToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"pendingAdmin","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"user","type":"address"},{"name":"concatenatedTokenAddresses","type":"uint256"},{"name":"nonce","type":"uint256"}],"name":"isValidNonce","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"srcToken","type":"address"},{"name":"destToken","type":"address"}],"name":"concatTokenAddresses","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"name":"nonce","type":"uint256"}],"name":"validAddressInNonce","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"amount","type":"uint256"},{"name":"sendTo","type":"address"}],"name":"withdrawToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newAlerter","type":"address"}],"name":"addAlerter","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"uint256"}],"name":"nonces","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"disableTrade","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newAdmin","type":"address"}],"name":"transferAdmin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"claimAdmin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newAdmin","type":"address"}],"name":"transferAdminQuickly","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"srcQty","type":"uint256"},{"name":"feeInPrecision","type":"uint256"}],"name":"deductFee","outputs":[{"name":"actualSrcQty","type":"uint256"},{"name":"feeInSrcTokenWei","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"name":"newOperator","type":"address"}],"name":"addOperator","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"hash","type":"bytes32"},{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"},{"name":"user","type":"address"}],"name":"verifySignature","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"name":"user","type":"address"},{"name":"nonce","type":"uint256"},{"name":"srcToken","type":"address"},{"name":"srcQty","type":"uint256"},{"name":"destToken","type":"address"},{"name":"destAddress","type":"address"},{"name":"minConversionRate","type":"uint256"},{"name":"feeInPrecision","type":"uint256"},{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"}],"name":"executeLimitOrder","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"PRECISION","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"operator","type":"address"}],"name":"removeOperator","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"MAX_DEST_AMOUNT","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"amount","type":"uint256"},{"name":"sendTo","type":"address"}],"name":"withdrawEther","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"alerters","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tradeEnabled","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"kyberNetworkProxy","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"concatenatedTokenAddresses","type":"uint256"},{"name":"nonce","type":"uint256"}],"name":"invalidateOldOrders","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"admin","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_admin","type":"address"},{"name":"_kyberNetworkProxy","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"tradeEnabled","type":"bool"}],"name":"TradeEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"user","type":"address"},{"indexed":false,"name":"nonce","type":"uint256"},{"indexed":true,"name":"srcToken","type":"address"},{"indexed":false,"name":"actualSrcQty","type":"uint256"},{"indexed":false,"name":"destAmount","type":"uint256"},{"indexed":true,"name":"destToken","type":"address"},{"indexed":false,"name":"destAddress","type":"address"},{"indexed":false,"name":"feeInSrcTokenWei","type":"uint256"}],"name":"LimitOrderExecute","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"user","type":"address"},{"indexed":false,"name":"concatenatedTokenAddresses","type":"uint256"},{"indexed":false,"name":"nonce","type":"uint256"}],"name":"OldOrdersInvalidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"user","type":"address"},{"indexed":false,"name":"concatenatedTokenAddresses","type":"uint256"},{"indexed":false,"name":"nonce","type":"uint256"}],"name":"NonceUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"token","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"sendTo","type":"address"}],"name":"TokenWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"sendTo","type":"address"}],"name":"EtherWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"pendingAdmin","type":"address"}],"name":"TransferAdminPending","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"newAdmin","type":"address"},{"indexed":false,"name":"previousAdmin","type":"address"}],"name":"AdminClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"newAlerter","type":"address"},{"indexed":false,"name":"isAdd","type":"bool"}],"name":"AlerterAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"newOperator","type":"address"},{"indexed":false,"name":"isAdd","type":"bool"}],"name":"OperatorAdded","type":"event"}]