From d60c35737da2a5078eb46c973c290225f9095d0f Mon Sep 17 00:00:00 2001 From: wangdong Date: Sun, 29 Apr 2018 18:41:10 -0400 Subject: [PATCH] rename --- contracts/BrokerRegistry.sol | 113 ++++- contracts/BrokerRegistryImpl.sol | 147 ------- .../{ClearingHouseImpl.sol => Exchange.sol} | 54 +-- ...Interceptor.sol => IBrokerInterceptor.sol} | 4 +- contracts/IBrokerRegistry.sol | 72 +++ .../{ClearingHouse.sol => IExchange.sol} | 4 +- ...rInterceptor.sol => IOrderInterceptor.sol} | 4 +- ...ngInterceptor.sol => IRingInterceptor.sol} | 4 +- contracts/ITokenFactory.sol | 51 +++ contracts/ITokenRegistry.sol | 90 ++++ contracts/ITokenTransferDelegate.sol | 138 ++++++ contracts/ITransferableMultsig.sol | 45 ++ contracts/TokenFactory.sol | 79 +++- contracts/TokenFactoryImpl.sol | 90 ---- contracts/TokenRegistry.sol | 149 ++++++- contracts/TokenRegistryImpl.sol | 201 --------- contracts/TokenTransferDelegate.sol | 374 +++++++++++++--- contracts/TokenTransferDelegateImpl.sol | 412 ------------------ contracts/TransferableMultsig.sol | 135 +++++- contracts/TransferableMultsigImpl.sol | 168 ------- 20 files changed, 1167 insertions(+), 1167 deletions(-) delete mode 100644 contracts/BrokerRegistryImpl.sol rename contracts/{ClearingHouseImpl.sol => Exchange.sol} (95%) rename contracts/{BrokerInterceptor.sol => IBrokerInterceptor.sol} (95%) create mode 100644 contracts/IBrokerRegistry.sol rename contracts/{ClearingHouse.sol => IExchange.sol} (98%) rename contracts/{OrderInterceptor.sol => IOrderInterceptor.sol} (92%) rename contracts/{RingInterceptor.sol => IRingInterceptor.sol} (93%) create mode 100644 contracts/ITokenFactory.sol create mode 100644 contracts/ITokenRegistry.sol create mode 100644 contracts/ITokenTransferDelegate.sol create mode 100644 contracts/ITransferableMultsig.sol delete mode 100644 contracts/TokenFactoryImpl.sol delete mode 100644 contracts/TokenRegistryImpl.sol delete mode 100644 contracts/TokenTransferDelegateImpl.sol delete mode 100644 contracts/TransferableMultsigImpl.sol diff --git a/contracts/BrokerRegistry.sol b/contracts/BrokerRegistry.sol index 3b49150d..9a04fc4f 100644 --- a/contracts/BrokerRegistry.sol +++ b/contracts/BrokerRegistry.sol @@ -18,24 +18,22 @@ pragma solidity 0.4.23; pragma experimental "v0.5.0"; pragma experimental "ABIEncoderV2"; +import "./IBrokerRegistry.sol"; -/// @title Trade Broker -/// @dev A broker is an account that can submit order on behalf of other -/// accounts. When register a broker, the owner can also specify a -/// pre-deployed BrokerInterceptor to manage the allowance of the -/// specific broker. + +/// @title An Implementation of IBrokerRegistry. /// @author Daniel Wang - . -contract BrokerRegistry { - event BrokerRegistered( - address owner, - address broker, - address interceptor - ); +contract BrokerRegistry is IBrokerRegistry { + struct Broker { + uint pos; // 0 mens unregistered; if > 0, pos - 1 is the + // token's position in `addresses`. + address owner; + address addr; + address interceptor; + } - event BrokerUnregistered( - address owner, - address broker - ); + mapping(address => Broker[]) public brokerageMap; + mapping(address => mapping(address => Broker)) public brokerMap; function getBroker( address owner, @@ -46,7 +44,12 @@ contract BrokerRegistry { returns( bool registered, address interceptor - ); + ) + { + Broker storage b = brokerMap[owner][broker]; + registered = (b.addr == broker); + interceptor = b.interceptor; + } function getBrokers( uint start, @@ -57,16 +60,88 @@ contract BrokerRegistry { returns ( address[] brokers, address[] interceptors - ); + ) + { + Broker[] storage _brokers = brokerageMap[msg.sender]; + uint num = _brokers.length; + + if (start >= num) { + return; + } + + uint end = start + count; + if (end > num) { + end = num; + } + + if (start == num) { + return; + } + + brokers = new address[](end - start); + interceptors = new address[](end - start); + for (uint i = start; i < end; i++) { + brokers[i - start] = _brokers[i].addr; + interceptors[i - start] = _brokers[i].interceptor; + } + } function registerBroker( address broker, address interceptor // 0x0 allowed ) - external; + external + { + require(0x0 != broker,"bad broker"); + require( + 0 == brokerMap[msg.sender][broker].pos, + "broker already exists" + ); + + Broker[] storage brokers = brokerageMap[msg.sender]; + Broker memory b = Broker( + brokers.length + 1, + msg.sender, + broker, + interceptor + ); + + brokers.push(b); + brokerMap[msg.sender][broker] = b; + + emit BrokerRegistered( + msg.sender, + broker, + interceptor + ); + } function unregisterBroker( address broker ) - external; + external + { + require(0x0 != broker, "bad broker"); + require( + brokerMap[msg.sender][broker].addr == broker, + "broker not found" + ); + + Broker storage b = brokerMap[msg.sender][broker]; + delete brokerMap[msg.sender][broker]; + + Broker[] storage brokers = brokerageMap[msg.sender]; + Broker storage lastBroker = brokers[brokers.length - 1]; + + if (lastBroker.addr != broker) { + // Swap with the last token and update the pos + lastBroker.pos = b.pos; + brokers[b.pos - 1] = lastBroker; + brokerMap[lastBroker.owner][lastBroker.addr] = lastBroker; + } + + brokers.length--; + + emit BrokerUnregistered(msg.sender, broker); + } } diff --git a/contracts/BrokerRegistryImpl.sol b/contracts/BrokerRegistryImpl.sol deleted file mode 100644 index d2865d46..00000000 --- a/contracts/BrokerRegistryImpl.sol +++ /dev/null @@ -1,147 +0,0 @@ -/* - - Copyright 2017 Loopring Project Ltd (Loopring Foundation). - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -pragma solidity 0.4.23; -pragma experimental "v0.5.0"; -pragma experimental "ABIEncoderV2"; - -import "./BrokerRegistry.sol"; - - -/// @title An Implementation of BrokerRegistry. -/// @author Daniel Wang - . -contract BrokerRegistryImpl is BrokerRegistry { - struct Broker { - uint pos; // 0 mens unregistered; if > 0, pos - 1 is the - // token's position in `addresses`. - address owner; - address addr; - address interceptor; - } - - mapping(address => Broker[]) public brokerageMap; - mapping(address => mapping(address => Broker)) public brokerMap; - - function getBroker( - address owner, - address broker - ) - external - view - returns( - bool registered, - address interceptor - ) - { - Broker storage b = brokerMap[owner][broker]; - registered = (b.addr == broker); - interceptor = b.interceptor; - } - - function getBrokers( - uint start, - uint count - ) - public - view - returns ( - address[] brokers, - address[] interceptors - ) - { - Broker[] storage _brokers = brokerageMap[msg.sender]; - uint num = _brokers.length; - - if (start >= num) { - return; - } - - uint end = start + count; - if (end > num) { - end = num; - } - - if (start == num) { - return; - } - - brokers = new address[](end - start); - interceptors = new address[](end - start); - for (uint i = start; i < end; i++) { - brokers[i - start] = _brokers[i].addr; - interceptors[i - start] = _brokers[i].interceptor; - } - } - - function registerBroker( - address broker, - address interceptor // 0x0 allowed - ) - external - { - require(0x0 != broker,"bad broker"); - require( - 0 == brokerMap[msg.sender][broker].pos, - "broker already exists" - ); - - Broker[] storage brokers = brokerageMap[msg.sender]; - Broker memory b = Broker( - brokers.length + 1, - msg.sender, - broker, - interceptor - ); - - brokers.push(b); - brokerMap[msg.sender][broker] = b; - - emit BrokerRegistered( - msg.sender, - broker, - interceptor - ); - } - - function unregisterBroker( - address broker - ) - external - { - require(0x0 != broker, "bad broker"); - require( - brokerMap[msg.sender][broker].addr == broker, - "broker not found" - ); - - Broker storage b = brokerMap[msg.sender][broker]; - delete brokerMap[msg.sender][broker]; - - Broker[] storage brokers = brokerageMap[msg.sender]; - Broker storage lastBroker = brokers[brokers.length - 1]; - - if (lastBroker.addr != broker) { - // Swap with the last token and update the pos - lastBroker.pos = b.pos; - brokers[b.pos - 1] = lastBroker; - brokerMap[lastBroker.owner][lastBroker.addr] = lastBroker; - } - - brokers.length--; - - emit BrokerUnregistered(msg.sender, broker); - } -} diff --git a/contracts/ClearingHouseImpl.sol b/contracts/Exchange.sol similarity index 95% rename from contracts/ClearingHouseImpl.sol rename to contracts/Exchange.sol index 93b9cfa9..1b6be0b3 100644 --- a/contracts/ClearingHouseImpl.sol +++ b/contracts/Exchange.sol @@ -22,14 +22,14 @@ import "./lib/AddressUtil.sol"; import "./lib/ERC20.sol"; import "./lib/MathUint.sol"; import "./lib/MultihashUtil.sol"; -import "./BrokerRegistry.sol"; -import "./BrokerInterceptor.sol"; -import "./ClearingHouse.sol"; -import "./TokenRegistry.sol"; -import "./TokenTransferDelegate.sol"; +import "./IBrokerRegistry.sol"; +import "./IBrokerInterceptor.sol"; +import "./IExchange.sol"; +import "./ITokenRegistry.sol"; +import "./ITokenTransferDelegate.sol"; -/// @title An Implementation of Clearn House. +/// @title An Implementation of IExchange. /// @author Daniel Wang - , /// @author Kongliang Zhong - /// @@ -39,7 +39,7 @@ import "./TokenTransferDelegate.sol"; /// https://github.com/BenjaminPrice /// https://github.com/jonasshen /// https://github.com/Hephyrius -contract ClearingHouseImpl is ClearingHouse { +contract Exchange is IExchange { using AddressUtil for address; using MathUint for uint; @@ -83,7 +83,7 @@ contract ClearingHouseImpl is ClearingHouse { bool optAllOrNone; bool marginSplitAsFee; bytes32 orderHash; - address trackerAddr; + address brokerInterceptor; uint rateS; uint rateB; uint fillAmountS; @@ -105,8 +105,8 @@ contract ClearingHouseImpl is ClearingHouse { uint8 feeSelections; uint64 ringIndex; uint ringSize; // computed - TokenTransferDelegate delegate; - BrokerRegistry brokerRegistry; + ITokenTransferDelegate delegate; + IBrokerRegistry brokerRegistry; Order[] orders; bytes32 ringHash; // computed } @@ -194,7 +194,7 @@ contract ClearingHouseImpl is ClearingHouse { ); if (order.signer != order.owner) { - BrokerRegistry brokerRegistry = BrokerRegistry(brokerRegistryAddress); + IBrokerRegistry brokerRegistry = IBrokerRegistry(brokerRegistryAddress); bool registered; address tracker; (registered, tracker) = brokerRegistry.getBroker( @@ -208,7 +208,7 @@ contract ClearingHouseImpl is ClearingHouse { if (order.optAllOrNone) { cancelAmount = order.optCapByAmountB ? order.amountB : order.amountS; } - TokenTransferDelegate delegate = TokenTransferDelegate(delegateAddress); + ITokenTransferDelegate delegate = ITokenTransferDelegate(delegateAddress); delegate.addCancelled(orderHash, cancelAmount); delegate.addCancelledOrFilled(orderHash, cancelAmount); @@ -229,7 +229,7 @@ contract ClearingHouseImpl is ClearingHouse { uint t = (cutoff == 0 || cutoff >= block.timestamp) ? block.timestamp : cutoff; bytes20 tokenPair = bytes20(token1) ^ bytes20(token2); - TokenTransferDelegate delegate = TokenTransferDelegate(delegateAddress); + ITokenTransferDelegate delegate = ITokenTransferDelegate(delegateAddress); require( delegate.tradingPairCutoffs(msg.sender, tokenPair) < t, @@ -251,7 +251,7 @@ contract ClearingHouseImpl is ClearingHouse { external { uint t = (cutoff == 0 || cutoff >= block.timestamp) ? block.timestamp : cutoff; - TokenTransferDelegate delegate = TokenTransferDelegate(delegateAddress); + ITokenTransferDelegate delegate = ITokenTransferDelegate(delegateAddress); require( delegate.cutoffs(msg.sender) < t, @@ -283,8 +283,8 @@ contract ClearingHouseImpl is ClearingHouse { feeSelections, ringIndex, addressesList.length, - TokenTransferDelegate(delegateAddress), - BrokerRegistry(brokerRegistryAddress), + ITokenTransferDelegate(delegateAddress), + IBrokerRegistry(brokerRegistryAddress), new Order[](addressesList.length), 0x0 // ringHash ); @@ -414,9 +414,9 @@ contract ClearingHouseImpl is ClearingHouse { ); if (order.signer != order.owner) { - BrokerRegistry brokerRegistry = BrokerRegistry(brokerRegistryAddress); + IBrokerRegistry brokerRegistry = IBrokerRegistry(brokerRegistryAddress); bool authenticated; - (authenticated, order.trackerAddr) = brokerRegistry.getBroker( + (authenticated, order.brokerInterceptor) = brokerRegistry.getBroker( order.owner, order.signer ); @@ -492,7 +492,7 @@ contract ClearingHouseImpl is ClearingHouse { // Test all token addresses at once require( - TokenRegistry(tokenRegistryAddress).areAllTokensRegistered(tokens), + ITokenRegistry(tokenRegistryAddress).areAllTokensRegistered(tokens), "token not registered" ); } @@ -590,7 +590,7 @@ contract ClearingHouseImpl is ClearingHouse { order.tokenS, order.owner, order.signer, - order.trackerAddr + order.brokerInterceptor ); // This check is more strict than it needs to be, in case the @@ -673,7 +673,7 @@ contract ClearingHouseImpl is ClearingHouse { lrcTokenAddress, order.owner, order.signer, - order.trackerAddr + order.brokerInterceptor ); // If the order is selling LRC, we need to calculate how much LRC @@ -782,7 +782,7 @@ contract ClearingHouseImpl is ClearingHouse { // Store owner and tokenS of every order batch[p++] = bytes32(order.owner); batch[p++] = bytes32(order.signer); - batch[p++] = bytes32(order.trackerAddr); + batch[p++] = bytes32(order.brokerInterceptor); batch[p++] = bytes32(order.tokenS); // Store all amounts @@ -886,11 +886,11 @@ contract ClearingHouseImpl is ClearingHouse { /// @return Amount of ERC20 token that can be spent by this contract. function getSpendable( - TokenTransferDelegate delegate, + ITokenTransferDelegate delegate, address tokenAddress, address tokenOwner, address broker, - address trackerAddr + address brokerInterceptor ) private view @@ -912,8 +912,8 @@ contract ClearingHouseImpl is ClearingHouse { } } - if (trackerAddr != 0x0) { - amount = BrokerInterceptor(trackerAddr).getAllowance( + if (brokerInterceptor != 0x0) { + amount = IBrokerInterceptor(brokerInterceptor).getAllowance( tokenOwner, broker, tokenAddress @@ -975,7 +975,7 @@ contract ClearingHouseImpl is ClearingHouse { returns (uint) { bytes20 tokenPair = bytes20(token1) ^ bytes20(token2); - TokenTransferDelegate delegate = TokenTransferDelegate(delegateAddress); + ITokenTransferDelegate delegate = ITokenTransferDelegate(delegateAddress); return delegate.tradingPairCutoffs(orderOwner, tokenPair); } } diff --git a/contracts/BrokerInterceptor.sol b/contracts/IBrokerInterceptor.sol similarity index 95% rename from contracts/BrokerInterceptor.sol rename to contracts/IBrokerInterceptor.sol index 59646a0f..7ad00698 100644 --- a/contracts/BrokerInterceptor.sol +++ b/contracts/IBrokerInterceptor.sol @@ -19,8 +19,8 @@ pragma experimental "v0.5.0"; pragma experimental "ABIEncoderV2"; -/// @title BrokerInterceptor -contract BrokerInterceptor { +/// @title IBrokerInterceptor +contract IBrokerInterceptor { /// @dev Returns the max amount the broker can buy or sell. function getAllowance( address owner, diff --git a/contracts/IBrokerRegistry.sol b/contracts/IBrokerRegistry.sol new file mode 100644 index 00000000..3a64505b --- /dev/null +++ b/contracts/IBrokerRegistry.sol @@ -0,0 +1,72 @@ +/* + + Copyright 2017 Loopring Project Ltd (Loopring Foundation). + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +pragma solidity 0.4.23; +pragma experimental "v0.5.0"; +pragma experimental "ABIEncoderV2"; + + +/// @title IBrokerRegistry +/// @dev A broker is an account that can submit order on behalf of other +/// accounts. When register a broker, the owner can also specify a +/// pre-deployed BrokerInterceptor to manage the allowance of the +/// specific broker. +/// @author Daniel Wang - . +contract IBrokerRegistry { + event BrokerRegistered( + address owner, + address broker, + address interceptor + ); + + event BrokerUnregistered( + address owner, + address broker + ); + + function getBroker( + address owner, + address broker + ) + external + view + returns( + bool registered, + address interceptor + ); + + function getBrokers( + uint start, + uint count + ) + public + view + returns ( + address[] brokers, + address[] interceptors + ); + + function registerBroker( + address broker, + address interceptor // 0x0 allowed + ) + external; + + function unregisterBroker( + address broker + ) + external; +} diff --git a/contracts/ClearingHouse.sol b/contracts/IExchange.sol similarity index 98% rename from contracts/ClearingHouse.sol rename to contracts/IExchange.sol index af48eb86..04dbd68b 100644 --- a/contracts/ClearingHouse.sol +++ b/contracts/IExchange.sol @@ -19,10 +19,10 @@ pragma experimental "v0.5.0"; pragma experimental "ABIEncoderV2"; -/// @title Clearing House +/// @title IExchange /// @author Daniel Wang - /// @author Kongliang Zhong - -contract ClearingHouse { +contract IExchange { uint8 public constant MARGIN_SPLIT_PERCENTAGE_BASE = 100; uint8 public constant OPTION_MASK_CAP_BY_AMOUNTB = 0x01; diff --git a/contracts/OrderInterceptor.sol b/contracts/IOrderInterceptor.sol similarity index 92% rename from contracts/OrderInterceptor.sol rename to contracts/IOrderInterceptor.sol index 26d805ad..a72f5dd4 100644 --- a/contracts/OrderInterceptor.sol +++ b/contracts/IOrderInterceptor.sol @@ -19,8 +19,8 @@ pragma experimental "v0.5.0"; pragma experimental "ABIEncoderV2"; -/// @title Order Interceptor +/// @title IOrderInterceptor /// @author Daniel Wang - . -contract OrderInterceptor { +contract IOrderInterceptor { } diff --git a/contracts/RingInterceptor.sol b/contracts/IRingInterceptor.sol similarity index 93% rename from contracts/RingInterceptor.sol rename to contracts/IRingInterceptor.sol index 93fba2e6..1f584860 100644 --- a/contracts/RingInterceptor.sol +++ b/contracts/IRingInterceptor.sol @@ -19,8 +19,8 @@ pragma experimental "v0.5.0"; pragma experimental "ABIEncoderV2"; -/// @title Ring Interceptor +/// @title IRingInterceptor /// @author Daniel Wang - . -contract RingInterceptor { +contract IRingInterceptor { } diff --git a/contracts/ITokenFactory.sol b/contracts/ITokenFactory.sol new file mode 100644 index 00000000..7996611c --- /dev/null +++ b/contracts/ITokenFactory.sol @@ -0,0 +1,51 @@ +/* + + Copyright 2017 Loopring Project Ltd (Loopring Foundation). + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +pragma solidity 0.4.23; +pragma experimental "v0.5.0"; +pragma experimental "ABIEncoderV2"; + + +/// @title ITokenFactory +/// @dev This contract deploys ERC20 token contract and registered the contract +/// so the token can be traded with Loopring Protocol. +/// @author Kongliang Zhong - , +/// @author Daniel Wang - . +contract ITokenFactory { + event TokenCreated( + address indexed addr, + string name, + string symbol, + uint8 decimals, + uint totalSupply, + address firstHolder + ); + + /// @dev Deploy an ERC20 token contract, register it with TokenRegistry, + /// and returns the new token's address. + /// @param name The name of the token + /// @param symbol The symbol of the token. + /// @param decimals The decimals of the token. + /// @param totalSupply The total supply of the token. + function createToken( + string name, + string symbol, + uint8 decimals, + uint totalSupply + ) + external + returns (address addr); +} diff --git a/contracts/ITokenRegistry.sol b/contracts/ITokenRegistry.sol new file mode 100644 index 00000000..11cc2ad1 --- /dev/null +++ b/contracts/ITokenRegistry.sol @@ -0,0 +1,90 @@ +/* + + Copyright 2017 Loopring Project Ltd (Loopring Foundation). + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +pragma solidity 0.4.23; +pragma experimental "v0.5.0"; +pragma experimental "ABIEncoderV2"; + + +/// @title ITokenRegistry +/// @dev This contract maintains a list of tokens the Protocol supports. +/// @author Kongliang Zhong - , +/// @author Daniel Wang - . +contract ITokenRegistry { + event TokenRegistered( + address indexed addr, + string symbol + ); + + event TokenUnregistered( + address indexed addr, + string symbol + ); + + function registerToken( + address addr, + string symbol + ) + external; + + function registerMintedToken( + address addr, + string symbol + ) + external; + + function unregisterToken( + address addr, + string symbol + ) + external; + + function areAllTokensRegistered( + address[] addressList + ) + external + view + returns (bool); + + function getAddressBySymbol( + string symbol + ) + external + view + returns (address); + + function isTokenRegisteredBySymbol( + string symbol + ) + public + view + returns (bool); + + function isTokenRegistered( + address addr + ) + public + view + returns (bool); + + function getTokens( + uint start, + uint count + ) + public + view + returns (address[] addressList); +} diff --git a/contracts/ITokenTransferDelegate.sol b/contracts/ITokenTransferDelegate.sol new file mode 100644 index 00000000..46aa535c --- /dev/null +++ b/contracts/ITokenTransferDelegate.sol @@ -0,0 +1,138 @@ +/* + + Copyright 2017 Loopring Project Ltd (Loopring Foundation). + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +pragma solidity 0.4.23; +pragma experimental "v0.5.0"; +pragma experimental "ABIEncoderV2"; + + +/// @title ITokenTransferDelegate +/// @dev Acts as a middle man to transfer ERC20 tokens on behalf of different +/// versions of Loopring protocol to avoid ERC20 re-authorization. +/// @author Daniel Wang - . +contract ITokenTransferDelegate { + event AddressAuthorized( + address indexed addr, + uint32 number + ); + + event AddressDeauthorized( + address indexed addr, + uint32 number + ); + + // The following map is used to keep trace of order fill and cancellation + // history. + mapping (bytes32 => uint) public cancelledOrFilled; + + // This map is used to keep trace of order's cancellation history. + mapping (bytes32 => uint) public cancelled; + + // A map from address to its cutoff timestamp. + mapping (address => uint) public cutoffs; + + // A map from address to its trading-pair cutoff timestamp. + mapping (address => mapping (bytes20 => uint)) public tradingPairCutoffs; + + /// @dev Add a Loopring protocol address. + /// @param addr A loopring protocol address. + function authorizeAddress( + address addr + ) + external; + + /// @dev Remove a Loopring protocol address. + /// @param addr A loopring protocol address. + function deauthorizeAddress( + address addr + ) + external; + + function getLatestAuthorizedAddresses( + uint max + ) + external + view + returns (address[] addresses); + + /// @dev Invoke ERC20 transferFrom method. + /// @param token Address of token to transfer. + /// @param from Address to transfer token from. + /// @param to Address to transfer token to. + /// @param value Amount of token to transfer. + function transferToken( + address token, + address from, + address to, + uint value + ) + external; + + function batchUpdateHistoryAndTransferTokens( + address lrcTokenAddress, + address minerFeeRecipient, + bytes32[] historyBatch, + bytes32[] transferBatch + ) + external; + + function isAddressAuthorized( + address addr + ) + public + view + returns (bool); + + function addCancelled( + bytes32 orderHash, + uint cancelAmount + ) + external; + + function addCancelledOrFilled( + bytes32 orderHash, + uint cancelOrFillAmount + ) + external; + + function setCutoffs( + uint cutoff + ) + external; + + function setTradingPairCutoffs( + bytes20 tokenPair, + uint cutoff + ) + external; + + function checkCutoffsBatch( + address[] owners, + bytes20[] tradingPairs, + uint[] validSince + ) + external + view; + + function suspend() + external; + + function resume() + external; + + function kill() + external; +} diff --git a/contracts/ITransferableMultsig.sol b/contracts/ITransferableMultsig.sol new file mode 100644 index 00000000..a0818751 --- /dev/null +++ b/contracts/ITransferableMultsig.sol @@ -0,0 +1,45 @@ +/* + + Copyright 2017 Loopring Project Ltd (Loopring Foundation). + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +pragma solidity 0.4.23; +pragma experimental "v0.5.0"; +pragma experimental "ABIEncoderV2"; + + +/// @title ITransferableMultsig +/// @author Daniel Wang - . +contract ITransferableMultsig { + // Note that address recovered from signatures must be strictly increasing. + function execute( + uint8[] sigV, + bytes32[] sigR, + bytes32[] sigS, + address destination, + uint value, + bytes data + ) + external; + + // Note that address recovered from signatures must be strictly increasing. + function transferOwnership( + uint8[] sigV, + bytes32[] sigR, + bytes32[] sigS, + uint _threshold, + address[] _owners + ) + external; +} diff --git a/contracts/TokenFactory.sol b/contracts/TokenFactory.sol index 86ba3a8d..fadff1c1 100644 --- a/contracts/TokenFactory.sol +++ b/contracts/TokenFactory.sol @@ -18,28 +18,40 @@ pragma solidity 0.4.23; pragma experimental "v0.5.0"; pragma experimental "ABIEncoderV2"; +import "./lib/AddressUtil.sol"; +import "./lib/ERC20Token.sol"; +import "./lib/StringUtil.sol"; +import "./ITokenFactory.sol"; +import "./ITokenRegistry.sol"; -/// @title ERC20 Token Mint -/// @dev This contract deploys ERC20 token contract and registered the contract -/// so the token can be traded with Loopring Protocol. + +/// @title An Implementation of ITokenFactory. /// @author Kongliang Zhong - , /// @author Daniel Wang - . -contract TokenFactory { - event TokenCreated( - address indexed addr, - string name, - string symbol, - uint8 decimals, - uint totalSupply, - address firstHolder - ); - - /// @dev Deploy an ERC20 token contract, register it with TokenRegistry, - /// and returns the new token's address. - /// @param name The name of the token - /// @param symbol The symbol of the token. - /// @param decimals The decimals of the token. - /// @param totalSupply The total supply of the token. +contract TokenFactoryImpl is ITokenFactory { + using AddressUtil for address; + using StringUtil for string; + + mapping(bytes10 => address) public tokens; + address public tokenRegistry; + + /// @dev Disable default function. + function () + payable + external + { + revert(); + } + + constructor( + address _tokenRegistry + ) + public + { + require(tokenRegistry == 0x0 && _tokenRegistry.isContract()); + tokenRegistry = _tokenRegistry; + } + function createToken( string name, string symbol, @@ -47,5 +59,32 @@ contract TokenFactory { uint totalSupply ) external - returns (address addr); + returns (address addr) + { + require(symbol.checkStringLength(3, 10), "bad symbol size"); + + bytes10 symbolBytes = symbol.stringToBytes10(); + require(tokens[symbolBytes] == 0x0, "invalid symbol"); + + ERC20Token token = new ERC20Token( + name, + symbol, + decimals, + totalSupply, + tx.origin + ); + + addr = address(token); + ITokenRegistry(tokenRegistry).registerMintedToken(addr, symbol); + tokens[symbolBytes] = addr; + + emit TokenCreated( + addr, + name, + symbol, + decimals, + totalSupply, + tx.origin + ); + } } diff --git a/contracts/TokenFactoryImpl.sol b/contracts/TokenFactoryImpl.sol deleted file mode 100644 index db6dae88..00000000 --- a/contracts/TokenFactoryImpl.sol +++ /dev/null @@ -1,90 +0,0 @@ -/* - - Copyright 2017 Loopring Project Ltd (Loopring Foundation). - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -pragma solidity 0.4.23; -pragma experimental "v0.5.0"; -pragma experimental "ABIEncoderV2"; - -import "./lib/AddressUtil.sol"; -import "./lib/ERC20Token.sol"; -import "./lib/StringUtil.sol"; -import "./TokenFactory.sol"; -import "./TokenRegistry.sol"; - - -/// @title An Implementation of TokenFactory. -/// @author Kongliang Zhong - , -/// @author Daniel Wang - . -contract TokenFactoryImpl is TokenFactory { - using AddressUtil for address; - using StringUtil for string; - - mapping(bytes10 => address) public tokens; - address public tokenRegistry; - - /// @dev Disable default function. - function () - payable - external - { - revert(); - } - - constructor( - address _tokenRegistry - ) - public - { - require(tokenRegistry == 0x0 && _tokenRegistry.isContract()); - tokenRegistry = _tokenRegistry; - } - - function createToken( - string name, - string symbol, - uint8 decimals, - uint totalSupply - ) - external - returns (address addr) - { - require(symbol.checkStringLength(3, 10), "bad symbol size"); - - bytes10 symbolBytes = symbol.stringToBytes10(); - require(tokens[symbolBytes] == 0x0, "invalid symbol"); - - ERC20Token token = new ERC20Token( - name, - symbol, - decimals, - totalSupply, - tx.origin - ); - - addr = address(token); - TokenRegistry(tokenRegistry).registerMintedToken(addr, symbol); - tokens[symbolBytes] = addr; - - emit TokenCreated( - addr, - name, - symbol, - decimals, - totalSupply, - tx.origin - ); - } -} diff --git a/contracts/TokenRegistry.sol b/contracts/TokenRegistry.sol index 3a1727c1..0be2e7f1 100644 --- a/contracts/TokenRegistry.sol +++ b/contracts/TokenRegistry.sol @@ -18,67 +18,128 @@ pragma solidity 0.4.23; pragma experimental "v0.5.0"; pragma experimental "ABIEncoderV2"; +import "./lib/AddressUtil.sol"; +import "./lib/Claimable.sol"; +import "./ITokenRegistry.sol"; -/// @title Token Register Contract -/// @dev This contract maintains a list of tokens the Protocol supports. + +/// @title An Implementation of TokenRegistry. /// @author Kongliang Zhong - , /// @author Daniel Wang - . -contract TokenRegistry { - event TokenRegistered( - address indexed addr, - string symbol - ); +contract TokenRegistry is ITokenRegistry, Claimable { + using AddressUtil for address; + + address[] public addresses; + mapping (address => TokenInfo) addressMap; + mapping (string => address) symbolMap; + + struct TokenInfo { + uint pos; // 0 mens unregistered; if > 0, pos - 1 is the + // token's position in `addresses`. + string symbol; // Symbol of the token + } - event TokenUnregistered( - address indexed addr, - string symbol - ); + /// @dev Disable default function. + function () + payable + external + { + revert(); + } function registerToken( address addr, string symbol ) - external; + external + onlyOwner + { + registerTokenInternal(addr, symbol); + } function registerMintedToken( address addr, string symbol ) - external; + external + { + registerTokenInternal(addr, symbol); + } function unregisterToken( address addr, string symbol ) - external; + external + onlyOwner + { + require(addr != 0x0,"bad address"); + require(symbolMap[symbol] == addr, "token not found"); + delete symbolMap[symbol]; + + uint pos = addressMap[addr].pos; + require(pos != 0); + delete addressMap[addr]; + + // We will replace the token we need to unregister with the last token + // Only the pos of the last token will need to be updated + address lastToken = addresses[addresses.length - 1]; + + // Don't do anything if the last token is the one we want to delete + if (addr != lastToken) { + // Swap with the last token and update the pos + addresses[pos - 1] = lastToken; + addressMap[lastToken].pos = pos; + } + addresses.length--; + + emit TokenUnregistered(addr, symbol); + } function areAllTokensRegistered( address[] addressList ) external view - returns (bool); + returns (bool) + { + for (uint i = 0; i < addressList.length; i++) { + if (addressMap[addressList[i]].pos == 0) { + return false; + } + } + return true; + } function getAddressBySymbol( string symbol ) external view - returns (address); + returns (address) + { + return symbolMap[symbol]; + } function isTokenRegisteredBySymbol( string symbol ) public view - returns (bool); + returns (bool) + { + return symbolMap[symbol] != 0x0; + } function isTokenRegistered( address addr ) public view - returns (bool); + returns (bool) + { + return addressMap[addr].pos != 0; + } function getTokens( uint start, @@ -86,5 +147,55 @@ contract TokenRegistry { ) public view - returns (address[] addressList); + returns (address[] addressList) + { + uint num = addresses.length; + + if (start >= num) { + return; + } + + uint end = start + count; + if (end > num) { + end = num; + } + + if (start == num) { + return; + } + + addressList = new address[](end - start); + for (uint i = start; i < end; i++) { + addressList[i - start] = addresses[i]; + } + } + + // address[] public addresses; + // mapping (address => TokenInfo) addressMap; + // mapping (string => address) symbolMap; + + // struct TokenInfo { + // uint pos; // 0 mens unregistered; if > 0, pos - 1 is the + // // token's position in `addresses`. + // string symbol; // Symbol of the token + // } + + + function registerTokenInternal( + address addr, + string symbol + ) + internal + { + require(0x0 != addr, "bad address"); + require(bytes(symbol).length > 0, "empty symbol"); + require(0x0 == symbolMap[symbol], "symbol registered"); + require(0 == addressMap[addr].pos, "address registered"); + + addresses.push(addr); + symbolMap[symbol] = addr; + addressMap[addr] = TokenInfo(addresses.length, symbol); + + emit TokenRegistered(addr, symbol); + } } diff --git a/contracts/TokenRegistryImpl.sol b/contracts/TokenRegistryImpl.sol deleted file mode 100644 index 4f1b0e6a..00000000 --- a/contracts/TokenRegistryImpl.sol +++ /dev/null @@ -1,201 +0,0 @@ -/* - - Copyright 2017 Loopring Project Ltd (Loopring Foundation). - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -pragma solidity 0.4.23; -pragma experimental "v0.5.0"; -pragma experimental "ABIEncoderV2"; - -import "./lib/AddressUtil.sol"; -import "./lib/Claimable.sol"; -import "./TokenRegistry.sol"; - - -/// @title An Implementation of TokenRegistry. -/// @author Kongliang Zhong - , -/// @author Daniel Wang - . -contract TokenRegistryImpl is TokenRegistry, Claimable { - using AddressUtil for address; - - address[] public addresses; - mapping (address => TokenInfo) addressMap; - mapping (string => address) symbolMap; - - struct TokenInfo { - uint pos; // 0 mens unregistered; if > 0, pos - 1 is the - // token's position in `addresses`. - string symbol; // Symbol of the token - } - - /// @dev Disable default function. - function () - payable - external - { - revert(); - } - - function registerToken( - address addr, - string symbol - ) - external - onlyOwner - { - registerTokenInternal(addr, symbol); - } - - function registerMintedToken( - address addr, - string symbol - ) - external - { - registerTokenInternal(addr, symbol); - } - - function unregisterToken( - address addr, - string symbol - ) - external - onlyOwner - { - require(addr != 0x0,"bad address"); - require(symbolMap[symbol] == addr, "token not found"); - delete symbolMap[symbol]; - - uint pos = addressMap[addr].pos; - require(pos != 0); - delete addressMap[addr]; - - // We will replace the token we need to unregister with the last token - // Only the pos of the last token will need to be updated - address lastToken = addresses[addresses.length - 1]; - - // Don't do anything if the last token is the one we want to delete - if (addr != lastToken) { - // Swap with the last token and update the pos - addresses[pos - 1] = lastToken; - addressMap[lastToken].pos = pos; - } - addresses.length--; - - emit TokenUnregistered(addr, symbol); - } - - function areAllTokensRegistered( - address[] addressList - ) - external - view - returns (bool) - { - for (uint i = 0; i < addressList.length; i++) { - if (addressMap[addressList[i]].pos == 0) { - return false; - } - } - return true; - } - - function getAddressBySymbol( - string symbol - ) - external - view - returns (address) - { - return symbolMap[symbol]; - } - - function isTokenRegisteredBySymbol( - string symbol - ) - public - view - returns (bool) - { - return symbolMap[symbol] != 0x0; - } - - function isTokenRegistered( - address addr - ) - public - view - returns (bool) - { - return addressMap[addr].pos != 0; - } - - function getTokens( - uint start, - uint count - ) - public - view - returns (address[] addressList) - { - uint num = addresses.length; - - if (start >= num) { - return; - } - - uint end = start + count; - if (end > num) { - end = num; - } - - if (start == num) { - return; - } - - addressList = new address[](end - start); - for (uint i = start; i < end; i++) { - addressList[i - start] = addresses[i]; - } - } - - // address[] public addresses; - // mapping (address => TokenInfo) addressMap; - // mapping (string => address) symbolMap; - - // struct TokenInfo { - // uint pos; // 0 mens unregistered; if > 0, pos - 1 is the - // // token's position in `addresses`. - // string symbol; // Symbol of the token - // } - - - function registerTokenInternal( - address addr, - string symbol - ) - internal - { - require(0x0 != addr, "bad address"); - require(bytes(symbol).length > 0, "empty symbol"); - require(0x0 == symbolMap[symbol], "symbol registered"); - require(0 == addressMap[addr].pos, "address registered"); - - addresses.push(addr); - symbolMap[symbol] = addr; - addressMap[addr] = TokenInfo(addresses.length, symbol); - - emit TokenRegistered(addr, symbol); - } -} diff --git a/contracts/TokenTransferDelegate.sol b/contracts/TokenTransferDelegate.sol index df9b262b..db4da789 100644 --- a/contracts/TokenTransferDelegate.sol +++ b/contracts/TokenTransferDelegate.sol @@ -18,106 +18,353 @@ pragma solidity 0.4.23; pragma experimental "v0.5.0"; pragma experimental "ABIEncoderV2"; +import "./lib/Claimable.sol"; +import "./lib/ERC20.sol"; +import "./lib/MathUint.sol"; +import "./IBrokerInterceptor.sol"; +import "./ITokenTransferDelegate.sol"; -/// @title TokenTransferDelegate -/// @dev Acts as a middle man to transfer ERC20 tokens on behalf of different -/// versions of Loopring protocol to avoid ERC20 re-authorization. + +/// @title An Implementation of ITokenTransferDelegate. /// @author Daniel Wang - . -contract TokenTransferDelegate { - event AddressAuthorized( - address indexed addr, - uint32 number - ); +/// @author Kongliang Zhong - . +contract TokenTransferDelegate is ITokenTransferDelegate, Claimable { + using MathUint for uint; + + uint8 public walletSplitPercentage = 0; + + constructor( + uint8 _walletSplitPercentage + ) + public + { + require(_walletSplitPercentage >= 0 && _walletSplitPercentage <= 100); + walletSplitPercentage = _walletSplitPercentage; + } + + bool public suspended = false; - event AddressDeauthorized( - address indexed addr, - uint32 number - ); + struct AddressInfo { + address previous; + uint32 index; + bool authorized; + } - // The following map is used to keep trace of order fill and cancellation - // history. - mapping (bytes32 => uint) public cancelledOrFilled; + mapping(address => AddressInfo) public addressInfos; + address public latestAddress; - // This map is used to keep trace of order's cancellation history. - mapping (bytes32 => uint) public cancelled; + modifier onlyAuthorized() + { + require(addressInfos[msg.sender].authorized, "unauthorized"); + _; + } - // A map from address to its cutoff timestamp. - mapping (address => uint) public cutoffs; + modifier notSuspended() + { + require(!suspended); + _; + } - // A map from address to its trading-pair cutoff timestamp. - mapping (address => mapping (bytes20 => uint)) public tradingPairCutoffs; + modifier isSuspended() + { + require(suspended); + _; + } + + /// @dev Disable default function. + function () + payable + external + { + revert(); + } - /// @dev Add a Loopring protocol address. - /// @param addr A loopring protocol address. function authorizeAddress( address addr ) - external; + onlyOwner + external + { + AddressInfo storage addrInfo = addressInfos[addr]; + + if (addrInfo.index != 0) { // existing + if (addrInfo.authorized == false) { // re-authorize + addrInfo.authorized = true; + emit AddressAuthorized(addr, addrInfo.index); + } + } else { + address prev = latestAddress; + if (prev == 0x0) { + addrInfo.index = 1; + addrInfo.authorized = true; + } else { + addrInfo.previous = prev; + addrInfo.index = addressInfos[prev].index + 1; + + } + addrInfo.authorized = true; + latestAddress = addr; + emit AddressAuthorized(addr, addrInfo.index); + } + } - /// @dev Remove a Loopring protocol address. - /// @param addr A loopring protocol address. function deauthorizeAddress( address addr ) - external; + onlyOwner + external + { + uint32 index = addressInfos[addr].index; + if (index != 0) { + addressInfos[addr].authorized = false; + emit AddressDeauthorized(addr, index); + } + } function getLatestAuthorizedAddresses( uint max ) external view - returns (address[] addresses); + returns (address[] addresses) + { + addresses = new address[](max); + address addr = latestAddress; + AddressInfo memory addrInfo; + uint count = 0; + + while (addr != 0x0 && count < max) { + addrInfo = addressInfos[addr]; + if (addrInfo.index == 0) { + break; + } + addresses[count++] = addr; + addr = addrInfo.previous; + } + } - /// @dev Invoke ERC20 transferFrom method. - /// @param token Address of token to transfer. - /// @param from Address to transfer token from. - /// @param to Address to transfer token to. - /// @param value Amount of token to transfer. function transferToken( address token, address from, address to, uint value ) - external; + onlyAuthorized + notSuspended + external + { + if (value > 0 && from != to && to != 0x0) { + require( + ERC20(token).transferFrom(from, to, value), + "token transfer failure" + ); + } + } function batchUpdateHistoryAndTransferTokens( - address lrcTokenAddress, - address minerFeeRecipient, + address lrcAddr, + address miner, bytes32[] historyBatch, - bytes32[] transferBatch + bytes32[] batch ) - external; + onlyAuthorized + notSuspended + external + { + // require(batch.length % 9 == 0); + // require(historyBatch.length % 2 == 0); + // require(batch.length / 9 == historyBatch.length / 2); + uint i; + for (i = 0; i < historyBatch.length / 2; i += 2) { + cancelledOrFilled[historyBatch[i]] = + cancelledOrFilled[historyBatch[i]].add(uint(historyBatch[i + 1])); + } + + address prevOwner = address(batch[batch.length - 9]); + + for (i = 0; i < batch.length; i += 9) { + address owner = address(batch[i]); + address signer = address(batch[i + 1]); + address brokerInterceptor = address(batch[i + 2]); + + // Pay token to previous order, or to miner as previous order's + // margin split or/and this order's margin split. + address token = address(batch[i + 3]); + uint amount; + + // Here batch[i + 4] has been checked not to be 0. + if (owner != prevOwner) { + amount = uint(batch[i + 4]); + if (amount > 0) { + require( + ERC20(token).transferFrom( + owner, + prevOwner, + amount + ), + "token transfer failure" + ); + } + + if (brokerInterceptor != 0x0) { + require( + IBrokerInterceptor(brokerInterceptor).onTokenSpent( + owner, + signer, + token, + amount + ), + "brokerInterceptor update failure" + ); + } + } + + // Miner pays LRx fee to order owner + amount = uint(batch[i + 6]); + if (amount != 0 && miner != owner) { + require( + ERC20(lrcAddr).transferFrom( + miner, + owner, + amount + ), + "token transfer failure" + ); + } + + // Split margin-split income between miner and wallet + splitPayFee( + token, + owner, + miner, + signer, + brokerInterceptor, + address(batch[i + 8]), + uint(batch[i + 5]) + ); + + // Split LRC fee income between miner and wallet + splitPayFee( + lrcAddr, + owner, + miner, + signer, + brokerInterceptor, + address(batch[i + 8]), + uint(batch[i + 7]) + ); + + prevOwner = owner; + } + } function isAddressAuthorized( address addr ) public view - returns (bool); + returns (bool) + { + return addressInfos[addr].authorized; + } + + function splitPayFee( + address token, + address owner, + address miner, + address broker, + address brokerInterceptor, + address wallet, + uint fee + ) + internal + { + if (fee == 0) { + return; + } + + uint walletFee = (wallet == 0x0) ? 0 : fee.mul(walletSplitPercentage) / 100; + uint minerFee = fee.sub(walletFee); + + if (walletFee > 0 && wallet != owner) { + require( + ERC20(token).transferFrom( + owner, + wallet, + walletFee + ), + "token transfer failure" + ); + } + + if (minerFee > 0 && miner != 0x0 && miner != owner) { + require( + ERC20(token).transferFrom( + owner, + miner, + minerFee + ), + "token transfer failure" + ); + } + + if (broker != 0x0) { + require( + IBrokerInterceptor(brokerInterceptor).onTokenSpent( + owner, + broker, + token, + fee + ), + "token transfer failure" + ); + } + } function addCancelled( bytes32 orderHash, - uint cancelAmount + uint cancelAmount ) - external; + onlyAuthorized + notSuspended + external + { + cancelled[orderHash] = cancelled[orderHash].add(cancelAmount); + } function addCancelledOrFilled( bytes32 orderHash, - uint cancelOrFillAmount + uint cancelOrFillAmount ) - external; + onlyAuthorized + notSuspended + external + { + cancelledOrFilled[orderHash] = + cancelledOrFilled[orderHash].add(cancelOrFillAmount); + } + function setCutoffs( uint cutoff ) - external; + onlyAuthorized + notSuspended + external + { + cutoffs[tx.origin] = cutoff; + } function setTradingPairCutoffs( bytes20 tokenPair, - uint cutoff + uint cutoff ) - external; + onlyAuthorized + notSuspended + external + { + tradingPairCutoffs[tx.origin][tokenPair] = cutoff; + } function checkCutoffsBatch( address[] owners, @@ -125,14 +372,41 @@ contract TokenTransferDelegate { uint[] validSince ) external - view; + view + { + uint len = owners.length; + require(len == tradingPairs.length); + require(len == validSince.length); + + for(uint i = 0; i < len; i++) { + require(validSince[i] > tradingPairCutoffs[owners[i]][tradingPairs[i]]); // order trading pair is cut off + require(validSince[i] > cutoffs[owners[i]]); // order is cut off + } + } function suspend() - external; + onlyOwner + notSuspended + external + { + suspended = true; + } function resume() - external; + onlyOwner + isSuspended + external + { + suspended = false; + } + /// owner must suspend delegate first before invoke kill method. function kill() - external; + onlyOwner + isSuspended + external + { + owner = 0x0; + emit OwnershipTransferred(owner, 0x0); + } } diff --git a/contracts/TokenTransferDelegateImpl.sol b/contracts/TokenTransferDelegateImpl.sol deleted file mode 100644 index 091e384d..00000000 --- a/contracts/TokenTransferDelegateImpl.sol +++ /dev/null @@ -1,412 +0,0 @@ -/* - - Copyright 2017 Loopring Project Ltd (Loopring Foundation). - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -pragma solidity 0.4.23; -pragma experimental "v0.5.0"; -pragma experimental "ABIEncoderV2"; - -import "./lib/Claimable.sol"; -import "./lib/ERC20.sol"; -import "./lib/MathUint.sol"; -import "./BrokerInterceptor.sol"; -import "./TokenTransferDelegate.sol"; - - -/// @title An Implementation of TokenTransferDelegate. -/// @author Daniel Wang - . -/// @author Kongliang Zhong - . -contract TokenTransferDelegateImpl is TokenTransferDelegate, Claimable { - using MathUint for uint; - - uint8 public walletSplitPercentage = 0; - - constructor( - uint8 _walletSplitPercentage - ) - public - { - require(_walletSplitPercentage >= 0 && _walletSplitPercentage <= 100); - walletSplitPercentage = _walletSplitPercentage; - } - - bool public suspended = false; - - struct AddressInfo { - address previous; - uint32 index; - bool authorized; - } - - mapping(address => AddressInfo) public addressInfos; - address public latestAddress; - - modifier onlyAuthorized() - { - require(addressInfos[msg.sender].authorized, "unauthorized"); - _; - } - - modifier notSuspended() - { - require(!suspended); - _; - } - - modifier isSuspended() - { - require(suspended); - _; - } - - /// @dev Disable default function. - function () - payable - external - { - revert(); - } - - function authorizeAddress( - address addr - ) - onlyOwner - external - { - AddressInfo storage addrInfo = addressInfos[addr]; - - if (addrInfo.index != 0) { // existing - if (addrInfo.authorized == false) { // re-authorize - addrInfo.authorized = true; - emit AddressAuthorized(addr, addrInfo.index); - } - } else { - address prev = latestAddress; - if (prev == 0x0) { - addrInfo.index = 1; - addrInfo.authorized = true; - } else { - addrInfo.previous = prev; - addrInfo.index = addressInfos[prev].index + 1; - - } - addrInfo.authorized = true; - latestAddress = addr; - emit AddressAuthorized(addr, addrInfo.index); - } - } - - function deauthorizeAddress( - address addr - ) - onlyOwner - external - { - uint32 index = addressInfos[addr].index; - if (index != 0) { - addressInfos[addr].authorized = false; - emit AddressDeauthorized(addr, index); - } - } - - function getLatestAuthorizedAddresses( - uint max - ) - external - view - returns (address[] addresses) - { - addresses = new address[](max); - address addr = latestAddress; - AddressInfo memory addrInfo; - uint count = 0; - - while (addr != 0x0 && count < max) { - addrInfo = addressInfos[addr]; - if (addrInfo.index == 0) { - break; - } - addresses[count++] = addr; - addr = addrInfo.previous; - } - } - - function transferToken( - address token, - address from, - address to, - uint value - ) - onlyAuthorized - notSuspended - external - { - if (value > 0 && from != to && to != 0x0) { - require( - ERC20(token).transferFrom(from, to, value), - "token transfer failure" - ); - } - } - - function batchUpdateHistoryAndTransferTokens( - address lrcAddr, - address miner, - bytes32[] historyBatch, - bytes32[] batch - ) - onlyAuthorized - notSuspended - external - { - // require(batch.length % 9 == 0); - // require(historyBatch.length % 2 == 0); - // require(batch.length / 9 == historyBatch.length / 2); - uint i; - for (i = 0; i < historyBatch.length / 2; i += 2) { - cancelledOrFilled[historyBatch[i]] = - cancelledOrFilled[historyBatch[i]].add(uint(historyBatch[i + 1])); - } - - address prevOwner = address(batch[batch.length - 9]); - - for (i = 0; i < batch.length; i += 9) { - address owner = address(batch[i]); - address signer = address(batch[i + 1]); - address tracker = address(batch[i + 2]); - - // Pay token to previous order, or to miner as previous order's - // margin split or/and this order's margin split. - address token = address(batch[i + 3]); - uint amount; - - // Here batch[i + 4] has been checked not to be 0. - if (owner != prevOwner) { - amount = uint(batch[i + 4]); - if (amount > 0) { - require( - ERC20(token).transferFrom( - owner, - prevOwner, - amount - ), - "token transfer failure" - ); - } - - if (tracker != 0x0) { - require( - BrokerInterceptor(tracker).onTokenSpent( - owner, - signer, - token, - amount - ), - "tracker update failure" - ); - } - } - - // Miner pays LRx fee to order owner - amount = uint(batch[i + 6]); - if (amount != 0 && miner != owner) { - require( - ERC20(lrcAddr).transferFrom( - miner, - owner, - amount - ), - "token transfer failure" - ); - } - - // Split margin-split income between miner and wallet - splitPayFee( - token, - owner, - miner, - signer, - tracker, - address(batch[i + 8]), - uint(batch[i + 5]) - ); - - // Split LRC fee income between miner and wallet - splitPayFee( - lrcAddr, - owner, - miner, - signer, - tracker, - address(batch[i + 8]), - uint(batch[i + 7]) - ); - - prevOwner = owner; - } - } - - function isAddressAuthorized( - address addr - ) - public - view - returns (bool) - { - return addressInfos[addr].authorized; - } - - function splitPayFee( - address token, - address owner, - address miner, - address broker, - address tracker, - address wallet, - uint fee - ) - internal - { - if (fee == 0) { - return; - } - - uint walletFee = (wallet == 0x0) ? 0 : fee.mul(walletSplitPercentage) / 100; - uint minerFee = fee.sub(walletFee); - - if (walletFee > 0 && wallet != owner) { - require( - ERC20(token).transferFrom( - owner, - wallet, - walletFee - ), - "token transfer failure" - ); - } - - if (minerFee > 0 && miner != 0x0 && miner != owner) { - require( - ERC20(token).transferFrom( - owner, - miner, - minerFee - ), - "token transfer failure" - ); - } - - if (broker != 0x0) { - require( - BrokerInterceptor(tracker).onTokenSpent( - owner, - broker, - token, - fee - ), - "token transfer failure" - ); - } - } - - function addCancelled( - bytes32 orderHash, - uint cancelAmount - ) - onlyAuthorized - notSuspended - external - { - cancelled[orderHash] = cancelled[orderHash].add(cancelAmount); - } - - function addCancelledOrFilled( - bytes32 orderHash, - uint cancelOrFillAmount - ) - onlyAuthorized - notSuspended - external - { - cancelledOrFilled[orderHash] = - cancelledOrFilled[orderHash].add(cancelOrFillAmount); - } - - - function setCutoffs( - uint cutoff - ) - onlyAuthorized - notSuspended - external - { - cutoffs[tx.origin] = cutoff; - } - - function setTradingPairCutoffs( - bytes20 tokenPair, - uint cutoff - ) - onlyAuthorized - notSuspended - external - { - tradingPairCutoffs[tx.origin][tokenPair] = cutoff; - } - - function checkCutoffsBatch( - address[] owners, - bytes20[] tradingPairs, - uint[] validSince - ) - external - view - { - uint len = owners.length; - require(len == tradingPairs.length); - require(len == validSince.length); - - for(uint i = 0; i < len; i++) { - require(validSince[i] > tradingPairCutoffs[owners[i]][tradingPairs[i]]); // order trading pair is cut off - require(validSince[i] > cutoffs[owners[i]]); // order is cut off - } - } - - function suspend() - onlyOwner - notSuspended - external - { - suspended = true; - } - - function resume() - onlyOwner - isSuspended - external - { - suspended = false; - } - - /// owner must suspend delegate first before invoke kill method. - function kill() - onlyOwner - isSuspended - external - { - owner = 0x0; - emit OwnershipTransferred(owner, 0x0); - } -} diff --git a/contracts/TransferableMultsig.sol b/contracts/TransferableMultsig.sol index a049ddb0..7ef0542b 100644 --- a/contracts/TransferableMultsig.sol +++ b/contracts/TransferableMultsig.sol @@ -18,11 +18,34 @@ pragma solidity 0.4.23; pragma experimental "v0.5.0"; pragma experimental "ABIEncoderV2"; +import "./ITransferableMultsig.sol"; -/// @title Transferable Multisignature Contract + +/// @title An Implementation of ITransferableMultsig /// @author Daniel Wang - . -contract TransferableMultsig { - // Note that address recovered from signatures must be strictly increasing. +contract TransferableMultsig is ITransferableMultsig { + + uint public nonce; // (only) mutable state + uint public threshold; // immutable state + mapping (address => bool) ownerMap; // immutable state + address[] public owners; // immutable state + + constructor( + uint _threshold, + address[] _owners + ) + public + { + updateOwners(_threshold, _owners); + } + + // default function does nothing. + function () + payable + external + { + } + function execute( uint8[] sigV, bytes32[] sigR, @@ -31,9 +54,33 @@ contract TransferableMultsig { uint value, bytes data ) - external; + external + { + // Follows ERC191 signature scheme: + // https://github.com/ethereum/EIPs/issues/191 + bytes32 txHash = keccak256( + byte(0x19), + byte(0), + this, + nonce++, + destination, + value, + data + ); + + verifySignatures( + sigV, + sigR, + sigS, + txHash + ); + + require( + destination.call.value(value)(data), + "execution error" + ); + } - // Note that address recovered from signatures must be strictly increasing. function transferOwnership( uint8[] sigV, bytes32[] sigR, @@ -41,5 +88,81 @@ contract TransferableMultsig { uint _threshold, address[] _owners ) - external; + external + { + // Follows ERC191 signature scheme: + // https://github.com/ethereum/EIPs/issues/191 + bytes32 txHash = keccak256( + byte(0x19), + byte(0), + this, + nonce++, + _threshold, + _owners + ); + + verifySignatures( + sigV, + sigR, + sigS, + txHash + ); + updateOwners(_threshold, _owners); + } + + function verifySignatures( + uint8[] sigV, + bytes32[] sigR, + bytes32[] sigS, + bytes32 txHash + ) + view + internal + { + uint _threshold = threshold; + require(_threshold == sigR.length); + require(_threshold == sigS.length); + require(_threshold == sigV.length); + + address lastAddr = 0x0; // cannot have 0x0 as an owner + for (uint i = 0; i < threshold; i++) { + address recovered = ecrecover( + txHash, + sigV[i], + sigR[i], + sigS[i] + ); + + require(recovered > lastAddr && ownerMap[recovered]); + lastAddr = recovered; + } + } + + function updateOwners( + uint _threshold, + address[] _owners + ) + internal + { + require(_owners.length <= 10); + require(_threshold <= _owners.length); + require(_threshold != 0); + + // remove all current owners from ownerMap. + address[] memory currentOwners = owners; + for (uint i = 0; i < currentOwners.length; i++) { + ownerMap[currentOwners[i]] = false; + } + + address lastAddr = 0x0; + for (uint i = 0; i < _owners.length; i++) { + address owner = _owners[i]; + require(owner > lastAddr); + + ownerMap[owner] = true; + lastAddr = owner; + } + owners = _owners; + threshold = _threshold; + } } diff --git a/contracts/TransferableMultsigImpl.sol b/contracts/TransferableMultsigImpl.sol deleted file mode 100644 index 477cbf3f..00000000 --- a/contracts/TransferableMultsigImpl.sol +++ /dev/null @@ -1,168 +0,0 @@ -/* - - Copyright 2017 Loopring Project Ltd (Loopring Foundation). - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -pragma solidity 0.4.23; -pragma experimental "v0.5.0"; -pragma experimental "ABIEncoderV2"; - -import "./TransferableMultsig.sol"; - - -/// @title An Implementation of TransferableMultsig。 -/// @author Daniel Wang - . -contract TransferableMultsigImpl is TransferableMultsig { - - uint public nonce; // (only) mutable state - uint public threshold; // immutable state - mapping (address => bool) ownerMap; // immutable state - address[] public owners; // immutable state - - constructor( - uint _threshold, - address[] _owners - ) - public - { - updateOwners(_threshold, _owners); - } - - // default function does nothing. - function () - payable - external - { - } - - function execute( - uint8[] sigV, - bytes32[] sigR, - bytes32[] sigS, - address destination, - uint value, - bytes data - ) - external - { - // Follows ERC191 signature scheme: - // https://github.com/ethereum/EIPs/issues/191 - bytes32 txHash = keccak256( - byte(0x19), - byte(0), - this, - nonce++, - destination, - value, - data - ); - - verifySignatures( - sigV, - sigR, - sigS, - txHash - ); - - require( - destination.call.value(value)(data), - "execution error" - ); - } - - function transferOwnership( - uint8[] sigV, - bytes32[] sigR, - bytes32[] sigS, - uint _threshold, - address[] _owners - ) - external - { - // Follows ERC191 signature scheme: - // https://github.com/ethereum/EIPs/issues/191 - bytes32 txHash = keccak256( - byte(0x19), - byte(0), - this, - nonce++, - _threshold, - _owners - ); - - verifySignatures( - sigV, - sigR, - sigS, - txHash - ); - updateOwners(_threshold, _owners); - } - - function verifySignatures( - uint8[] sigV, - bytes32[] sigR, - bytes32[] sigS, - bytes32 txHash - ) - view - internal - { - uint _threshold = threshold; - require(_threshold == sigR.length); - require(_threshold == sigS.length); - require(_threshold == sigV.length); - - address lastAddr = 0x0; // cannot have 0x0 as an owner - for (uint i = 0; i < threshold; i++) { - address recovered = ecrecover( - txHash, - sigV[i], - sigR[i], - sigS[i] - ); - - require(recovered > lastAddr && ownerMap[recovered]); - lastAddr = recovered; - } - } - - function updateOwners( - uint _threshold, - address[] _owners - ) - internal - { - require(_owners.length <= 10); - require(_threshold <= _owners.length); - require(_threshold != 0); - - // remove all current owners from ownerMap. - address[] memory currentOwners = owners; - for (uint i = 0; i < currentOwners.length; i++) { - ownerMap[currentOwners[i]] = false; - } - - address lastAddr = 0x0; - for (uint i = 0; i < _owners.length; i++) { - address owner = _owners[i]; - require(owner > lastAddr); - - ownerMap[owner] = true; - lastAddr = owner; - } - owners = _owners; - threshold = _threshold; - } -}