contract Administrable {
using SafeMath for uint256;
mapping (address => bool) private admins;
uint256 private _nAdmin;
uint256 private _nLimit;
event Activated(address indexed admin);
event Deactivated(address indexed admin);
/**
* @dev The Administrable constructor sets the original `admin` of the contract to the sender
* account. The initial limit amount of admin is 2.
*/
constructor() internal {
_setAdminLimit(2);
_activateAdmin(msg.sender);
}
function isAdmin() public view returns(bool) {
return admins[msg.sender];
}
/**
* @dev Throws if called by non-admin.
*/
modifier onlyAdmin() {
require(isAdmin(), "sender not admin");
_;
}
function activateAdmin(address admin) external onlyAdmin {
_activateAdmin(admin);
}
function deactivateAdmin(address admin) external onlyAdmin {
_safeDeactivateAdmin(admin);
}
function setAdminLimit(uint256 n) external onlyAdmin {
_setAdminLimit(n);
}
function _setAdminLimit(uint256 n) internal {
require(_nLimit != n, "same limit");
_nLimit = n;
}
/**
* @notice The Amount of admin should be bounded by _nLimit.
*/
function _activateAdmin(address admin) internal {
require(admin != address(0), "invalid address");
require(_nAdmin < _nLimit, "too many admins existed");
require(!admins[admin], "already admin");
admins[admin] = true;
_nAdmin = _nAdmin.add(1);
emit Activated(admin);
}
/**
* @notice At least one admin should exists.
*/
function _safeDeactivateAdmin(address admin) internal {
require(_nAdmin > 1, "admin should > 1");
_deactivateAdmin(admin);
}
function _deactivateAdmin(address admin) internal {
require(admins[admin], "not admin");
admins[admin] = false;
_nAdmin = _nAdmin.sub(1);
emit Deactivated(admin);
}
}
library ErrorHandler {
function errorHandler(bytes memory ret) internal pure {
if (ret.length > 0) {
byte ec = abi.decode(ret, (byte));
if (ec != 0x00)
revert(byteToHexString(ec));
}
}
function byteToHexString(byte data) internal pure returns (string memory ret) {
bytes memory ec = bytes("0x00");
byte dataL = data & 0x0f;
byte dataH = data >> 4;
if (dataL < 0x0a)
ec[3] = byte(uint8(ec[3]) + uint8(dataL));
else
ec[3] = byte(uint8(ec[3]) + uint8(dataL) + 0x27);
if (dataH < 0x0a)
ec[2] = byte(uint8(ec[2]) + uint8(dataH));
else
ec[2] = byte(uint8(ec[2]) + uint8(dataH) + 0x27);
return string(ec);
}
}
library SafeMath {
/**
* @dev Multiplies two unsigned integers, reverts on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b);
return c;
}
/**
* @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a);
uint256 c = a - b;
return c;
}
/**
* @dev Adds two unsigned integers, reverts on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a);
return c;
}
/**
* @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
* reverts when dividing by zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0);
return a % b;
}
}
contract Ownable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
constructor () internal {
_owner = msg.sender;
emit OwnershipTransferred(address(0), _owner);
}
/**
* @return the address of the owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner());
_;
}
/**
* @return true if `msg.sender` is the owner of the contract.
*/
function isOwner() public view returns (bool) {
return msg.sender == _owner;
}
/**
* @dev Allows the current owner to relinquish control of the contract.
* It will not be possible to call the functions with the `onlyOwner`
* modifier anymore.
* @notice Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
_transferOwnership(newOwner);
}
/**
* @dev Transfers control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0));
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
library Address {
/**
* Returns whether the target address is a contract
* @dev This function will return false if invoked during the constructor of a contract,
* as the code is not actually created until after the constructor finishes.
* @param account address of the account to check
* @return whether the target address is a contract
*/
function isContract(address account) internal view returns (bool) {
uint256 size;
// XXX Currently there is no better way to check if there is a contract in an address
// than to check the size of the code at that address.
// See https://ethereum.stackexchange.com/a/14016/36603
// for more details about how this works.
// TODO Check this again before the Serenity release, because all addresses will be
// contracts then.
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 0;
}
}
contract Proxy is Ownable {
using Address for address;
// keccak256 hash of "dinngo.proxy.implementation"
bytes32 private constant IMPLEMENTATION_SLOT =
0x3b2ff02c0f36dba7cc1b20a669e540b974575f04ef71846d482983efb03bebb4;
event Upgraded(address indexed implementation);
constructor(address implementation) internal {
assert(IMPLEMENTATION_SLOT == keccak256("dinngo.proxy.implementation"));
_setImplementation(implementation);
}
/**
* @notice Upgrade the implementation contract. Can only be triggered
* by the owner. Emits the Upgraded event.
* @param implementation The new implementation address.
*/
function upgrade(address implementation) external onlyOwner {
_setImplementation(implementation);
emit Upgraded(implementation);
}
/**
* @dev Set the implementation address in the storage slot.
* @param implementation The new implementation address.
*/
function _setImplementation(address implementation) internal {
require(implementation.isContract(),
"Implementation address should be a contract address"
);
bytes32 slot = IMPLEMENTATION_SLOT;
assembly {
sstore(slot, implementation)
}
}
/**
* @dev Returns the current implementation address.
*/
function _implementation() internal view returns (address implementation) {
bytes32 slot = IMPLEMENTATION_SLOT;
assembly {
implementation := sload(slot)
}
}
}
contract TimelockUpgradableProxy is Proxy {
// keccak256 hash of "dinngo.proxy.registration"
bytes32 private constant REGISTRATION_SLOT =
0x90215db359d12011b32ff0c897114c39e26956599904ee846adb0dd49f782e97;
// keccak256 hash of "dinngo.proxy.time"
bytes32 private constant TIME_SLOT =
0xe89d1a29650bdc8a918bc762afb8ef07e10f6180e461c3fc305f9f142e5591e6;
uint256 private constant UPGRADE_TIME = 14 days;
event UpgradeAnnounced(address indexed implementation, uint256 time);
constructor() internal {
assert(REGISTRATION_SLOT == keccak256("dinngo.proxy.registration"));
assert(TIME_SLOT == keccak256("dinngo.proxy.time"));
}
/**
* @notice Register the implementation address as the candidate contract
* to be upgraded. Emits the UpgradeAnnounced event.
* @param implementation The implementation contract address to be registered.
*/
function register(address implementation) external onlyOwner {
_registerImplementation(implementation);
emit UpgradeAnnounced(implementation, _time());
}
/**
* @dev Overload the function in contract Proxy.
* @notice Upgrade the implementation contract.
* @param implementation The new implementation contract.
*/
function upgrade(address implementation) external {
require(implementation == _registration());
upgradeAnnounced();
}
/**
* @notice Upgrade the implementation contract to the announced address.
* Emits the Upgraded event.
*/
function upgradeAnnounced() public onlyOwner {
require(now >= _time());
_setImplementation(_registration());
emit Upgraded(_registration());
}
/**
* @dev Register the imeplemtation address to the registation slot. Record the
* valid time by adding the UPGRADE_TIME to the registration time to the time slot.
* @param implementation The implemetation address to be registered.
*/
function _registerImplementation(address implementation) internal {
require(implementation.isContract(),
"Implementation address should be a contract address"
);
uint256 time = now + UPGRADE_TIME;
bytes32 implSlot = REGISTRATION_SLOT;
bytes32 timeSlot = TIME_SLOT;
assembly {
sstore(implSlot, implementation)
sstore(timeSlot, time)
}
}
/**
* @dev Return the valid time of registered implementation address.
*/
function _time() internal view returns (uint256 time) {
bytes32 slot = TIME_SLOT;
assembly {
time := sload(slot)
}
}
/**
* @dev Return the registered implementation address.
*/
function _registration() internal view returns (address implementation) {
bytes32 slot = REGISTRATION_SLOT;
assembly {
implementation := sload(slot)
}
}
}
contract DinngoProxy is Ownable, Administrable, TimelockUpgradableProxy {
using ErrorHandler for bytes;
uint256 public processTime;
mapping (address => mapping (address => uint256)) public balances;
mapping (bytes32 => uint256) public orderFills;
mapping (uint256 => address payable) public userID_Address;
mapping (uint256 => address) public tokenID_Address;
mapping (address => uint256) public userRanks;
mapping (address => uint256) public tokenRanks;
mapping (address => uint256) public lockTimes;
/**
* @dev User ID 0 is the management wallet.
* Token ID 0 is ETH (address 0). Token ID 1 is DGO.
* @param dinngoWallet The main address of dinngo
* @param dinngoToken The contract address of DGO
*/
constructor(
address payable dinngoWallet,
address dinngoToken,
address impl
) Proxy(impl) public {
processTime = 90 days;
userID_Address[0] = dinngoWallet;
userRanks[dinngoWallet] = 255;
tokenID_Address[0] = address(0);
tokenID_Address[1] = dinngoToken;
}
/**
* @dev All ether directly sent to contract will be refunded
*/
function() external payable {
revert();
}
/**
* @notice Add the address to the user list. Event AddUser will be emitted
* after execution.
* @dev Record the user list to map the user address to a specific user ID, in
* order to compact the data size when transferring user address information
* @param id The user id to be assigned
* @param user The user address to be added
*/
function addUser(uint256 id, address user) external onlyAdmin {
(bool ok,) = _implementation().delegatecall(
abi.encodeWithSignature("addUser(uint256,address)", id, user)
);
require(ok);
}
/**
* @notice Remove the address from the user list.
* @dev The user rank is set to 0 to remove the user.
* @param user The user address to be added
*/
function removeUser(address user) external onlyAdmin {
(bool ok,) = _implementation().delegatecall(
abi.encodeWithSignature("removeUser(address)", user)
);
require(ok);
}
/**
* @notice Update the rank of user. Can only be called by owner.
* @param user The user address
* @param rank The rank to be assigned
*/
function updateUserRank(address user, uint256 rank) external onlyAdmin {
(bool ok,) = _implementation().delegatecall(
abi.encodeWithSignature("updateUserRank(address,uint256)",user, rank)
);
require(ok);
}
/**
* @notice Add the token to the token list. Event AddToken will be emitted
* after execution.
* @dev Record the token list to map the token contract address to a specific
* token ID, in order to compact the data size when transferring token contract
* address information
* @param id The token id to be assigned
* @param token The token contract address to be added
*/
function addToken(uint256 id, address token) external onlyOwner {
(bool ok,) = _implementation().delegatecall(
abi.encodeWithSignature("addToken(uint256,address)", id, token)
);
require(ok);
}
/**
* @notice Remove the token to the token list.
* @dev The token rank is set to 0 to remove the token.
* @param token The token contract address to be removed.
*/
function removeToken(address token) external onlyOwner {
(bool ok,) = _implementation().delegatecall(
abi.encodeWithSignature("removeToken(address)", token)
);
require(ok);
}
/**
* @notice Update the rank of token. Can only be called by owner.
* @param token The token contract address.
* @param rank The rank to be assigned.
*/
function updateTokenRank(address token, uint256 rank) external onlyOwner {
(bool ok,) = _implementation().delegatecall(
abi.encodeWithSignature("updateTokenRank(address,uint256)", token, rank)
);
require(ok);
}
function activateAdmin(address admin) external onlyOwner {
_activateAdmin(admin);
}
function deactivateAdmin(address admin) external onlyOwner {
_safeDeactivateAdmin(admin);
}
/**
* @notice Force-deactivate allows owner to deactivate admin even there will be
* no admin left. Should only be executed under emergency situation.
*/
function forceDeactivateAdmin(address admin) external onlyOwner {
_deactivateAdmin(admin);
}
function setAdminLimit(uint256 n) external onlyOwner {
_setAdminLimit(n);
}
/**
* @notice The deposit function for ether. The ether that is sent with the function
* call will be deposited. The first time user will be added to the user list.
* Event Deposit will be emitted after execution.
*/
function deposit() external payable {
(bool ok,) = _implementation().delegatecall(abi.encodeWithSignature("deposit()"));
require(ok);
}
/**
* @notice The deposit function for tokens. The first time user will be added to
* the user list. Event Deposit will be emitted after execution.
* @param token Address of the token contract to be deposited
* @param amount Amount of the token to be depositied
*/
function depositToken(address token, uint256 amount) external {
(bool ok,) = _implementation().delegatecall(
abi.encodeWithSignature("depositToken(address,uint256)", token, amount)
);
require(ok);
}
/**
* @notice The withdraw function for ether. Event Withdraw will be emitted
* after execution. User needs to be locked before calling withdraw.
* @param amount The amount to be withdrawn.
*/
function withdraw(uint256 amount) external {
(bool ok,) = _implementation().delegatecall(
abi.encodeWithSignature("withdraw(uint256)", amount)
);
require(ok);
}
/**
* @notice The withdraw function for tokens. Event Withdraw will be emitted
* after execution. User needs to be locked before calling withdraw.
* @param token The token contract address to be withdrawn.
* @param amount The token amount to be withdrawn.
*/
function withdrawToken(address token, uint256 amount) external {
(bool ok,) = _implementation().delegatecall(
abi.encodeWithSignature("withdrawToken(address,uint256)", token, amount)
);
require(ok);
}
/**
* @notice The withdraw function that can only be triggered by owner.
* Event Withdraw will be emitted after execution.
* @param withdrawal The serialized withdrawal data
*/
function withdrawByAdmin(bytes calldata withdrawal) external onlyAdmin {
(bool ok, bytes memory ret) = _implementation().delegatecall(
abi.encodeWithSignature("withdrawByAdmin(bytes)", withdrawal)
);
require(ok);
ret.errorHandler();
}
/**
* @notice The settle function for orders. First order is taker order and the followings
* are maker orders.
* @param orders The serialized orders.
*/
function settle(bytes calldata orders) external onlyAdmin {
(bool ok, bytes memory ret) = _implementation().delegatecall(
abi.encodeWithSignature("settle(bytes)", orders)
);
require(ok);
ret.errorHandler();
}
/**
* @notice The migrate function that can only be triggered by admin.
* @param migration The serialized migration data
*/
function migrateByAdmin(bytes calldata migration) external onlyAdmin {
(bool ok, bytes memory ret) = _implementation().delegatecall(
abi.encodeWithSignature("migrateByAdmin(bytes)", migration)
);
require(ok);
ret.errorHandler();
}
/**
* @notice Announce lock of the sender
*/
function lock() external {
(bool ok,) = _implementation().delegatecall(abi.encodeWithSignature("lock()"));
require(ok);
}
/**
* @notice Unlock the sender
*/
function unlock() external {
(bool ok,) = _implementation().delegatecall(abi.encodeWithSignature("unlock()"));
require(ok);
}
/**
* @notice Change the processing time of locking the user address
*/
function changeProcessTime(uint256 time) external onlyOwner {
(bool ok,) = _implementation().delegatecall(
abi.encodeWithSignature("changeProcessTime(uint256)", time)
);
require(ok);
}
}
{
"compilationTarget": {
"DinngoProxy.sol": "DinngoProxy"
},
"evmVersion": "petersburg",
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"constant":false,"inputs":[],"name":"upgradeAnnounced","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"implementation","type":"address"}],"name":"upgrade","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"withdrawal","type":"bytes"}],"name":"withdrawByAdmin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"admin","type":"address"}],"name":"activateAdmin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"rank","type":"uint256"}],"name":"updateTokenRank","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"amount","type":"uint256"}],"name":"depositToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"implementation","type":"address"}],"name":"register","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"tokenRanks","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"userRanks","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"lockTimes","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"id","type":"uint256"},{"name":"token","type":"address"}],"name":"addToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"id","type":"uint256"},{"name":"user","type":"address"}],"name":"addUser","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"}],"name":"removeToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"userID_Address","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"admin","type":"address"}],"name":"forceDeactivateAdmin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"renounceOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isOwner","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"n","type":"uint256"}],"name":"setAdminLimit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"user","type":"address"}],"name":"removeUser","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"amount","type":"uint256"}],"name":"withdrawToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"unlock","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"migration","type":"bytes"}],"name":"migrateByAdmin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"user","type":"address"},{"name":"rank","type":"uint256"}],"name":"updateUserRank","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"isAdmin","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"balances","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"orders","type":"bytes"}],"name":"settle","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"deposit","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"processTime","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"admin","type":"address"}],"name":"deactivateAdmin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"tokenID_Address","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"time","type":"uint256"}],"name":"changeProcessTime","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"orderFills","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"lock","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"dinngoWallet","type":"address"},{"name":"dinngoToken","type":"address"},{"name":"impl","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"implementation","type":"address"},{"indexed":false,"name":"time","type":"uint256"}],"name":"UpgradeAnnounced","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"admin","type":"address"}],"name":"Activated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"admin","type":"address"}],"name":"Deactivated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}]