aboutsummaryrefslogtreecommitdiffstats
path: root/packages/contracts/src/1.0.0
diff options
context:
space:
mode:
authorLeonid Logvinov <logvinov.leon@gmail.com>2018-07-05 21:04:01 +0800
committerLeonid Logvinov <logvinov.leon@gmail.com>2018-07-05 21:04:01 +0800
commit1ee2d6ed54b6159d1e8952692f4ddba0ebd65012 (patch)
tree1cebf47acf4fff2352a709035c141ec7ffdb8353 /packages/contracts/src/1.0.0
parent1eba78e20ac468d3b4d6ebe8fd91eb5277577e0a (diff)
parent5176d929fa6d3c6ce414448ea2441bd450f04e3c (diff)
downloaddexon-sol-tools-1ee2d6ed54b6159d1e8952692f4ddba0ebd65012.tar.gz
dexon-sol-tools-1ee2d6ed54b6159d1e8952692f4ddba0ebd65012.tar.zst
dexon-sol-tools-1ee2d6ed54b6159d1e8952692f4ddba0ebd65012.zip
Merge branch 'v2-prototype' into v2-contract-wrappers-WIP
Diffstat (limited to 'packages/contracts/src/1.0.0')
-rw-r--r--packages/contracts/src/1.0.0/Arbitrage/Arbitrage.sol114
-rw-r--r--packages/contracts/src/1.0.0/ERC20Token/ERC20Token_v1.sol44
-rw-r--r--packages/contracts/src/1.0.0/EtherDelta/AccountLevels.sol11
-rw-r--r--packages/contracts/src/1.0.0/EtherDelta/EtherDelta.sol168
-rw-r--r--packages/contracts/src/1.0.0/Exchange/Exchange_v1.sol602
-rw-r--r--packages/contracts/src/1.0.0/Exchange/IExchange_v1.sol226
-rw-r--r--packages/contracts/src/1.0.0/MultiSigWalletWithTImeLockExceptRemoveAuthorizedAddress/MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress.sol82
-rw-r--r--packages/contracts/src/1.0.0/Ownable/IOwnable_v1.sol18
-rw-r--r--packages/contracts/src/1.0.0/Ownable/Ownable_v1.sol27
-rw-r--r--packages/contracts/src/1.0.0/SafeMath/SafeMath_v1.sol73
-rw-r--r--packages/contracts/src/1.0.0/Token/Token_v1.sol39
-rw-r--r--packages/contracts/src/1.0.0/TokenRegistry/ITokenRegistery.sol195
-rw-r--r--packages/contracts/src/1.0.0/TokenRegistry/TokenRegistry.sol308
-rw-r--r--packages/contracts/src/1.0.0/TokenTransferProxy/TokenTransferProxy_v1.sol115
-rw-r--r--packages/contracts/src/1.0.0/UnlimitedAllowanceToken/UnlimitedAllowanceToken_v1.sol52
15 files changed, 2074 insertions, 0 deletions
diff --git a/packages/contracts/src/1.0.0/Arbitrage/Arbitrage.sol b/packages/contracts/src/1.0.0/Arbitrage/Arbitrage.sol
new file mode 100644
index 000000000..5054afc2f
--- /dev/null
+++ b/packages/contracts/src/1.0.0/Arbitrage/Arbitrage.sol
@@ -0,0 +1,114 @@
+pragma solidity ^0.4.19;
+
+import { IExchange_v1 as Exchange } from "../Exchange/IExchange_v1.sol";
+import { EtherDelta } from "../EtherDelta/EtherDelta.sol";
+import { Ownable_v1 as Ownable } from "../Ownable/Ownable_v1.sol";
+import { IToken_v1 as Token } from "../Token/IToken_v1.sol";
+
+/// @title Arbitrage - Facilitates atomic arbitrage of ERC20 tokens between EtherDelta and 0x Exchange contract.
+/// @author Leonid Logvinov - <leo@0xProject.com>
+contract Arbitrage is Ownable {
+
+ Exchange exchange;
+ EtherDelta etherDelta;
+ address proxyAddress;
+
+ uint256 constant MAX_UINT = 2**256 - 1;
+
+ function Arbitrage(address _exchangeAddress, address _etherDeltaAddress, address _proxyAddress) {
+ exchange = Exchange(_exchangeAddress);
+ etherDelta = EtherDelta(_etherDeltaAddress);
+ proxyAddress = _proxyAddress;
+ }
+
+ /*
+ * Makes token tradeable by setting an allowance for etherDelta and 0x proxy contract.
+ * Also sets an allowance for the owner of the contracts therefore allowing to withdraw tokens.
+ */
+ function setAllowances(address tokenAddress) external onlyOwner {
+ Token token = Token(tokenAddress);
+ token.approve(address(etherDelta), MAX_UINT);
+ token.approve(proxyAddress, MAX_UINT);
+ token.approve(owner, MAX_UINT);
+ }
+
+ /*
+ * Because of the limits on the number of local variables in Solidity we need to compress parameters while loosing
+ * readability. Scheme of the parameter layout:
+ *
+ * addresses
+ * 0..4 orderAddresses
+ * 5 user
+ *
+ * values
+ * 0..5 orderValues
+ * 6 fillTakerTokenAmount
+ * 7 amountGet
+ * 8 amountGive
+ * 9 expires
+ * 10 nonce
+ * 11 amount
+
+ * signature
+ * exchange then etherDelta
+ */
+ function makeAtomicTrade(
+ address[6] addresses, uint[12] values,
+ uint8[2] v, bytes32[2] r, bytes32[2] s
+ ) external onlyOwner {
+ makeExchangeTrade(addresses, values, v, r, s);
+ makeEtherDeltaTrade(addresses, values, v, r, s);
+ }
+
+ function makeEtherDeltaTrade(
+ address[6] addresses, uint[12] values,
+ uint8[2] v, bytes32[2] r, bytes32[2] s
+ ) internal {
+ uint amount = values[11];
+ etherDelta.depositToken(
+ addresses[2], // tokenGet === makerToken
+ values[7] // amountGet
+ );
+ etherDelta.trade(
+ addresses[2], // tokenGet === makerToken
+ values[7], // amountGet
+ addresses[3], // tokenGive === takerToken
+ values[8], // amountGive
+ values[9], // expires
+ values[10], // nonce
+ addresses[5], // user
+ v[1],
+ r[1],
+ s[1],
+ amount
+ );
+ etherDelta.withdrawToken(
+ addresses[3], // tokenGive === tokenToken
+ values[8] // amountGive
+ );
+ }
+
+ function makeExchangeTrade(
+ address[6] addresses, uint[12] values,
+ uint8[2] v, bytes32[2] r, bytes32[2] s
+ ) internal {
+ address[5] memory orderAddresses = [
+ addresses[0], // maker
+ addresses[1], // taker
+ addresses[2], // makerToken
+ addresses[3], // takerToken
+ addresses[4] // feeRecepient
+ ];
+ uint[6] memory orderValues = [
+ values[0], // makerTokenAmount
+ values[1], // takerTokenAmount
+ values[2], // makerFee
+ values[3], // takerFee
+ values[4], // expirationTimestampInSec
+ values[5] // salt
+ ];
+ uint fillTakerTokenAmount = values[6]; // fillTakerTokenAmount
+ // Execute Exchange trade. It either succeeds in full or fails and reverts all the changes.
+ exchange.fillOrKillOrder(orderAddresses, orderValues, fillTakerTokenAmount, v[0], r[0], s[0]);
+ }
+}
diff --git a/packages/contracts/src/1.0.0/ERC20Token/ERC20Token_v1.sol b/packages/contracts/src/1.0.0/ERC20Token/ERC20Token_v1.sol
new file mode 100644
index 000000000..e05ee2d5e
--- /dev/null
+++ b/packages/contracts/src/1.0.0/ERC20Token/ERC20Token_v1.sol
@@ -0,0 +1,44 @@
+pragma solidity ^0.4.11;
+
+import { Token_v1 as Token } from "../Token/Token_v1.sol";
+
+contract ERC20Token_v1 is Token {
+
+ function transfer(address _to, uint _value) returns (bool) {
+ //Default assumes totalSupply can't be over max (2^256 - 1).
+ if (balances[msg.sender] >= _value && balances[_to] + _value >= balances[_to]) {
+ balances[msg.sender] -= _value;
+ balances[_to] += _value;
+ Transfer(msg.sender, _to, _value);
+ return true;
+ } else { return false; }
+ }
+
+ function transferFrom(address _from, address _to, uint _value) returns (bool) {
+ if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value >= balances[_to]) {
+ balances[_to] += _value;
+ balances[_from] -= _value;
+ allowed[_from][msg.sender] -= _value;
+ Transfer(_from, _to, _value);
+ return true;
+ } else { return false; }
+ }
+
+ function balanceOf(address _owner) constant returns (uint) {
+ return balances[_owner];
+ }
+
+ function approve(address _spender, uint _value) returns (bool) {
+ allowed[msg.sender][_spender] = _value;
+ Approval(msg.sender, _spender, _value);
+ return true;
+ }
+
+ function allowance(address _owner, address _spender) constant returns (uint) {
+ return allowed[_owner][_spender];
+ }
+
+ mapping (address => uint) balances;
+ mapping (address => mapping (address => uint)) allowed;
+ uint public totalSupply;
+}
diff --git a/packages/contracts/src/1.0.0/EtherDelta/AccountLevels.sol b/packages/contracts/src/1.0.0/EtherDelta/AccountLevels.sol
new file mode 100644
index 000000000..8d7a930d3
--- /dev/null
+++ b/packages/contracts/src/1.0.0/EtherDelta/AccountLevels.sol
@@ -0,0 +1,11 @@
+pragma solidity ^0.4.19;
+
+contract AccountLevels {
+ //given a user, returns an account level
+ //0 = regular user (pays take fee and make fee)
+ //1 = market maker silver (pays take fee, no make fee, gets rebate)
+ //2 = market maker gold (pays take fee, no make fee, gets entire counterparty's take fee as rebate)
+ function accountLevel(address user) constant returns(uint) {
+ return 0;
+ }
+}
diff --git a/packages/contracts/src/1.0.0/EtherDelta/EtherDelta.sol b/packages/contracts/src/1.0.0/EtherDelta/EtherDelta.sol
new file mode 100644
index 000000000..fe599ca0a
--- /dev/null
+++ b/packages/contracts/src/1.0.0/EtherDelta/EtherDelta.sol
@@ -0,0 +1,168 @@
+pragma solidity ^0.4.19;
+
+import { SafeMath } from "../SafeMath/SafeMath_v1.sol";
+import { AccountLevels } from "./AccountLevels.sol";
+import { Token } from "../Token/Token_v1.sol";
+
+contract EtherDelta is SafeMath {
+ address public admin; //the admin address
+ address public feeAccount; //the account that will receive fees
+ address public accountLevelsAddr; //the address of the AccountLevels contract
+ uint public feeMake; //percentage times (1 ether)
+ uint public feeTake; //percentage times (1 ether)
+ uint public feeRebate; //percentage times (1 ether)
+ mapping (address => mapping (address => uint)) public tokens; //mapping of token addresses to mapping of account balances (token=0 means Ether)
+ mapping (address => mapping (bytes32 => bool)) public orders; //mapping of user accounts to mapping of order hashes to booleans (true = submitted by user, equivalent to offchain signature)
+ mapping (address => mapping (bytes32 => uint)) public orderFills; //mapping of user accounts to mapping of order hashes to uints (amount of order that has been filled)
+
+ event Order(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, address user);
+ event Cancel(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, address user, uint8 v, bytes32 r, bytes32 s);
+ event Trade(address tokenGet, uint amountGet, address tokenGive, uint amountGive, address get, address give);
+ event Deposit(address token, address user, uint amount, uint balance);
+ event Withdraw(address token, address user, uint amount, uint balance);
+
+ function EtherDelta(address admin_, address feeAccount_, address accountLevelsAddr_, uint feeMake_, uint feeTake_, uint feeRebate_) {
+ admin = admin_;
+ feeAccount = feeAccount_;
+ accountLevelsAddr = accountLevelsAddr_;
+ feeMake = feeMake_;
+ feeTake = feeTake_;
+ feeRebate = feeRebate_;
+ }
+
+ function() {
+ throw;
+ }
+
+ function changeAdmin(address admin_) {
+ if (msg.sender != admin) throw;
+ admin = admin_;
+ }
+
+ function changeAccountLevelsAddr(address accountLevelsAddr_) {
+ if (msg.sender != admin) throw;
+ accountLevelsAddr = accountLevelsAddr_;
+ }
+
+ function changeFeeAccount(address feeAccount_) {
+ if (msg.sender != admin) throw;
+ feeAccount = feeAccount_;
+ }
+
+ function changeFeeMake(uint feeMake_) {
+ if (msg.sender != admin) throw;
+ if (feeMake_ > feeMake) throw;
+ feeMake = feeMake_;
+ }
+
+ function changeFeeTake(uint feeTake_) {
+ if (msg.sender != admin) throw;
+ if (feeTake_ > feeTake || feeTake_ < feeRebate) throw;
+ feeTake = feeTake_;
+ }
+
+ function changeFeeRebate(uint feeRebate_) {
+ if (msg.sender != admin) throw;
+ if (feeRebate_ < feeRebate || feeRebate_ > feeTake) throw;
+ feeRebate = feeRebate_;
+ }
+
+ function deposit() payable {
+ tokens[0][msg.sender] = safeAdd(tokens[0][msg.sender], msg.value);
+ Deposit(0, msg.sender, msg.value, tokens[0][msg.sender]);
+ }
+
+ function withdraw(uint amount) {
+ if (tokens[0][msg.sender] < amount) throw;
+ tokens[0][msg.sender] = safeSub(tokens[0][msg.sender], amount);
+ if (!msg.sender.call.value(amount)()) throw;
+ Withdraw(0, msg.sender, amount, tokens[0][msg.sender]);
+ }
+
+ function depositToken(address token, uint amount) {
+ //remember to call Token(address).approve(this, amount) or this contract will not be able to do the transfer on your behalf.
+ if (token==0) throw;
+ if (!Token(token).transferFrom(msg.sender, this, amount)) throw;
+ tokens[token][msg.sender] = safeAdd(tokens[token][msg.sender], amount);
+ Deposit(token, msg.sender, amount, tokens[token][msg.sender]);
+ }
+
+ function withdrawToken(address token, uint amount) {
+ if (token==0) throw;
+ if (tokens[token][msg.sender] < amount) throw;
+ tokens[token][msg.sender] = safeSub(tokens[token][msg.sender], amount);
+ if (!Token(token).transfer(msg.sender, amount)) throw;
+ Withdraw(token, msg.sender, amount, tokens[token][msg.sender]);
+ }
+
+ function balanceOf(address token, address user) constant returns (uint) {
+ return tokens[token][user];
+ }
+
+ function order(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce) {
+ bytes32 hash = sha256(this, tokenGet, amountGet, tokenGive, amountGive, expires, nonce);
+ orders[msg.sender][hash] = true;
+ Order(tokenGet, amountGet, tokenGive, amountGive, expires, nonce, msg.sender);
+ }
+
+ function trade(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, address user, uint8 v, bytes32 r, bytes32 s, uint amount) {
+ //amount is in amountGet terms
+ bytes32 hash = sha256(this, tokenGet, amountGet, tokenGive, amountGive, expires, nonce);
+ if (!(
+ (orders[user][hash] || ecrecover(sha3("\x19Ethereum Signed Message:\n32", hash),v,r,s) == user) &&
+ block.number <= expires &&
+ safeAdd(orderFills[user][hash], amount) <= amountGet
+ )) throw;
+ tradeBalances(tokenGet, amountGet, tokenGive, amountGive, user, amount);
+ orderFills[user][hash] = safeAdd(orderFills[user][hash], amount);
+ Trade(tokenGet, amount, tokenGive, amountGive * amount / amountGet, user, msg.sender);
+ }
+
+ function tradeBalances(address tokenGet, uint amountGet, address tokenGive, uint amountGive, address user, uint amount) private {
+ uint feeMakeXfer = safeMul(amount, feeMake) / (1 ether);
+ uint feeTakeXfer = safeMul(amount, feeTake) / (1 ether);
+ uint feeRebateXfer = 0;
+ if (accountLevelsAddr != 0x0) {
+ uint accountLevel = AccountLevels(accountLevelsAddr).accountLevel(user);
+ if (accountLevel==1) feeRebateXfer = safeMul(amount, feeRebate) / (1 ether);
+ if (accountLevel==2) feeRebateXfer = feeTakeXfer;
+ }
+ tokens[tokenGet][msg.sender] = safeSub(tokens[tokenGet][msg.sender], safeAdd(amount, feeTakeXfer));
+ tokens[tokenGet][user] = safeAdd(tokens[tokenGet][user], safeSub(safeAdd(amount, feeRebateXfer), feeMakeXfer));
+ tokens[tokenGet][feeAccount] = safeAdd(tokens[tokenGet][feeAccount], safeSub(safeAdd(feeMakeXfer, feeTakeXfer), feeRebateXfer));
+ tokens[tokenGive][user] = safeSub(tokens[tokenGive][user], safeMul(amountGive, amount) / amountGet);
+ tokens[tokenGive][msg.sender] = safeAdd(tokens[tokenGive][msg.sender], safeMul(amountGive, amount) / amountGet);
+ }
+
+ function testTrade(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, address user, uint8 v, bytes32 r, bytes32 s, uint amount, address sender) constant returns(bool) {
+ if (!(
+ tokens[tokenGet][sender] >= amount &&
+ availableVolume(tokenGet, amountGet, tokenGive, amountGive, expires, nonce, user, v, r, s) >= amount
+ )) return false;
+ return true;
+ }
+
+ function availableVolume(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, address user, uint8 v, bytes32 r, bytes32 s) constant returns(uint) {
+ bytes32 hash = sha256(this, tokenGet, amountGet, tokenGive, amountGive, expires, nonce);
+ if (!(
+ (orders[user][hash] || ecrecover(sha3("\x19Ethereum Signed Message:\n32", hash),v,r,s) == user) &&
+ block.number <= expires
+ )) return 0;
+ uint available1 = safeSub(amountGet, orderFills[user][hash]);
+ uint available2 = safeMul(tokens[tokenGive][user], amountGet) / amountGive;
+ if (available1<available2) return available1;
+ return available2;
+ }
+
+ function amountFilled(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, address user, uint8 v, bytes32 r, bytes32 s) constant returns(uint) {
+ bytes32 hash = sha256(this, tokenGet, amountGet, tokenGive, amountGive, expires, nonce);
+ return orderFills[user][hash];
+ }
+
+ function cancelOrder(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, uint8 v, bytes32 r, bytes32 s) {
+ bytes32 hash = sha256(this, tokenGet, amountGet, tokenGive, amountGive, expires, nonce);
+ if (!(orders[msg.sender][hash] || ecrecover(sha3("\x19Ethereum Signed Message:\n32", hash),v,r,s) == msg.sender)) throw;
+ orderFills[msg.sender][hash] = amountGet;
+ Cancel(tokenGet, amountGet, tokenGive, amountGive, expires, nonce, msg.sender, v, r, s);
+ }
+}
diff --git a/packages/contracts/src/1.0.0/Exchange/Exchange_v1.sol b/packages/contracts/src/1.0.0/Exchange/Exchange_v1.sol
new file mode 100644
index 000000000..3f8e7368d
--- /dev/null
+++ b/packages/contracts/src/1.0.0/Exchange/Exchange_v1.sol
@@ -0,0 +1,602 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ 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.14;
+
+import { TokenTransferProxy_v1 as TokenTransferProxy } from "../TokenTransferProxy/TokenTransferProxy_v1.sol";
+import { Token_v1 as Token } from "../Token/Token_v1.sol";
+import { SafeMath_v1 as SafeMath } from "../SafeMath/SafeMath_v1.sol";
+
+/// @title Exchange - Facilitates exchange of ERC20 tokens.
+/// @author Amir Bandeali - <amir@0xProject.com>, Will Warren - <will@0xProject.com>
+contract Exchange_v1 is SafeMath {
+
+ // Error Codes
+ enum Errors {
+ ORDER_EXPIRED, // Order has already expired
+ ORDER_FULLY_FILLED_OR_CANCELLED, // Order has already been fully filled or cancelled
+ ROUNDING_ERROR_TOO_LARGE, // Rounding error too large
+ INSUFFICIENT_BALANCE_OR_ALLOWANCE // Insufficient balance or allowance for token transfer
+ }
+
+ string constant public VERSION = "1.0.0";
+ uint16 constant public EXTERNAL_QUERY_GAS_LIMIT = 4999; // Changes to state require at least 5000 gas
+
+ address public ZRX_TOKEN_CONTRACT;
+ address public TOKEN_TRANSFER_PROXY_CONTRACT;
+
+ // Mappings of orderHash => amounts of takerTokenAmount filled or cancelled.
+ mapping (bytes32 => uint) public filled;
+ mapping (bytes32 => uint) public cancelled;
+
+ event LogFill(
+ address indexed maker,
+ address taker,
+ address indexed feeRecipient,
+ address makerToken,
+ address takerToken,
+ uint filledMakerTokenAmount,
+ uint filledTakerTokenAmount,
+ uint paidMakerFee,
+ uint paidTakerFee,
+ bytes32 indexed tokens, // keccak256(makerToken, takerToken), allows subscribing to a token pair
+ bytes32 orderHash
+ );
+
+ event LogCancel(
+ address indexed maker,
+ address indexed feeRecipient,
+ address makerToken,
+ address takerToken,
+ uint cancelledMakerTokenAmount,
+ uint cancelledTakerTokenAmount,
+ bytes32 indexed tokens,
+ bytes32 orderHash
+ );
+
+ event LogError(uint8 indexed errorId, bytes32 indexed orderHash);
+
+ struct Order {
+ address maker;
+ address taker;
+ address makerToken;
+ address takerToken;
+ address feeRecipient;
+ uint makerTokenAmount;
+ uint takerTokenAmount;
+ uint makerFee;
+ uint takerFee;
+ uint expirationTimestampInSec;
+ bytes32 orderHash;
+ }
+
+ function Exchange_v1(address _zrxToken, address _tokenTransferProxy) {
+ ZRX_TOKEN_CONTRACT = _zrxToken;
+ TOKEN_TRANSFER_PROXY_CONTRACT = _tokenTransferProxy;
+ }
+
+ /*
+ * Core exchange functions
+ */
+
+ /// @dev Fills the input order.
+ /// @param orderAddresses Array of order's maker, taker, makerToken, takerToken, and feeRecipient.
+ /// @param orderValues Array of order's makerTokenAmount, takerTokenAmount, makerFee, takerFee, expirationTimestampInSec, and salt.
+ /// @param fillTakerTokenAmount Desired amount of takerToken to fill.
+ /// @param shouldThrowOnInsufficientBalanceOrAllowance Test if transfer will fail before attempting.
+ /// @param v ECDSA signature parameter v.
+ /// @param r ECDSA signature parameters r.
+ /// @param s ECDSA signature parameters s.
+ /// @return Total amount of takerToken filled in trade.
+ function fillOrder(
+ address[5] orderAddresses,
+ uint[6] orderValues,
+ uint fillTakerTokenAmount,
+ bool shouldThrowOnInsufficientBalanceOrAllowance,
+ uint8 v,
+ bytes32 r,
+ bytes32 s)
+ public
+ returns (uint filledTakerTokenAmount)
+ {
+ Order memory order = Order({
+ maker: orderAddresses[0],
+ taker: orderAddresses[1],
+ makerToken: orderAddresses[2],
+ takerToken: orderAddresses[3],
+ feeRecipient: orderAddresses[4],
+ makerTokenAmount: orderValues[0],
+ takerTokenAmount: orderValues[1],
+ makerFee: orderValues[2],
+ takerFee: orderValues[3],
+ expirationTimestampInSec: orderValues[4],
+ orderHash: getOrderHash(orderAddresses, orderValues)
+ });
+
+ require(order.taker == address(0) || order.taker == msg.sender);
+ require(order.makerTokenAmount > 0 && order.takerTokenAmount > 0 && fillTakerTokenAmount > 0);
+ require(isValidSignature(
+ order.maker,
+ order.orderHash,
+ v,
+ r,
+ s
+ ));
+
+ if (block.timestamp >= order.expirationTimestampInSec) {
+ LogError(uint8(Errors.ORDER_EXPIRED), order.orderHash);
+ return 0;
+ }
+
+ uint remainingTakerTokenAmount = safeSub(order.takerTokenAmount, getUnavailableTakerTokenAmount(order.orderHash));
+ filledTakerTokenAmount = min256(fillTakerTokenAmount, remainingTakerTokenAmount);
+ if (filledTakerTokenAmount == 0) {
+ LogError(uint8(Errors.ORDER_FULLY_FILLED_OR_CANCELLED), order.orderHash);
+ return 0;
+ }
+
+ if (isRoundingError(filledTakerTokenAmount, order.takerTokenAmount, order.makerTokenAmount)) {
+ LogError(uint8(Errors.ROUNDING_ERROR_TOO_LARGE), order.orderHash);
+ return 0;
+ }
+
+ if (!shouldThrowOnInsufficientBalanceOrAllowance && !isTransferable(order, filledTakerTokenAmount)) {
+ LogError(uint8(Errors.INSUFFICIENT_BALANCE_OR_ALLOWANCE), order.orderHash);
+ return 0;
+ }
+
+ uint filledMakerTokenAmount = getPartialAmount(filledTakerTokenAmount, order.takerTokenAmount, order.makerTokenAmount);
+ uint paidMakerFee;
+ uint paidTakerFee;
+ filled[order.orderHash] = safeAdd(filled[order.orderHash], filledTakerTokenAmount);
+ require(transferViaTokenTransferProxy(
+ order.makerToken,
+ order.maker,
+ msg.sender,
+ filledMakerTokenAmount
+ ));
+ require(transferViaTokenTransferProxy(
+ order.takerToken,
+ msg.sender,
+ order.maker,
+ filledTakerTokenAmount
+ ));
+ if (order.feeRecipient != address(0)) {
+ if (order.makerFee > 0) {
+ paidMakerFee = getPartialAmount(filledTakerTokenAmount, order.takerTokenAmount, order.makerFee);
+ require(transferViaTokenTransferProxy(
+ ZRX_TOKEN_CONTRACT,
+ order.maker,
+ order.feeRecipient,
+ paidMakerFee
+ ));
+ }
+ if (order.takerFee > 0) {
+ paidTakerFee = getPartialAmount(filledTakerTokenAmount, order.takerTokenAmount, order.takerFee);
+ require(transferViaTokenTransferProxy(
+ ZRX_TOKEN_CONTRACT,
+ msg.sender,
+ order.feeRecipient,
+ paidTakerFee
+ ));
+ }
+ }
+
+ LogFill(
+ order.maker,
+ msg.sender,
+ order.feeRecipient,
+ order.makerToken,
+ order.takerToken,
+ filledMakerTokenAmount,
+ filledTakerTokenAmount,
+ paidMakerFee,
+ paidTakerFee,
+ keccak256(order.makerToken, order.takerToken),
+ order.orderHash
+ );
+ return filledTakerTokenAmount;
+ }
+
+ /// @dev Cancels the input order.
+ /// @param orderAddresses Array of order's maker, taker, makerToken, takerToken, and feeRecipient.
+ /// @param orderValues Array of order's makerTokenAmount, takerTokenAmount, makerFee, takerFee, expirationTimestampInSec, and salt.
+ /// @param cancelTakerTokenAmount Desired amount of takerToken to cancel in order.
+ /// @return Amount of takerToken cancelled.
+ function cancelOrder(
+ address[5] orderAddresses,
+ uint[6] orderValues,
+ uint cancelTakerTokenAmount)
+ public
+ returns (uint)
+ {
+ Order memory order = Order({
+ maker: orderAddresses[0],
+ taker: orderAddresses[1],
+ makerToken: orderAddresses[2],
+ takerToken: orderAddresses[3],
+ feeRecipient: orderAddresses[4],
+ makerTokenAmount: orderValues[0],
+ takerTokenAmount: orderValues[1],
+ makerFee: orderValues[2],
+ takerFee: orderValues[3],
+ expirationTimestampInSec: orderValues[4],
+ orderHash: getOrderHash(orderAddresses, orderValues)
+ });
+
+ require(order.maker == msg.sender);
+ require(order.makerTokenAmount > 0 && order.takerTokenAmount > 0 && cancelTakerTokenAmount > 0);
+
+ if (block.timestamp >= order.expirationTimestampInSec) {
+ LogError(uint8(Errors.ORDER_EXPIRED), order.orderHash);
+ return 0;
+ }
+
+ uint remainingTakerTokenAmount = safeSub(order.takerTokenAmount, getUnavailableTakerTokenAmount(order.orderHash));
+ uint cancelledTakerTokenAmount = min256(cancelTakerTokenAmount, remainingTakerTokenAmount);
+ if (cancelledTakerTokenAmount == 0) {
+ LogError(uint8(Errors.ORDER_FULLY_FILLED_OR_CANCELLED), order.orderHash);
+ return 0;
+ }
+
+ cancelled[order.orderHash] = safeAdd(cancelled[order.orderHash], cancelledTakerTokenAmount);
+
+ LogCancel(
+ order.maker,
+ order.feeRecipient,
+ order.makerToken,
+ order.takerToken,
+ getPartialAmount(cancelledTakerTokenAmount, order.takerTokenAmount, order.makerTokenAmount),
+ cancelledTakerTokenAmount,
+ keccak256(order.makerToken, order.takerToken),
+ order.orderHash
+ );
+ return cancelledTakerTokenAmount;
+ }
+
+ /*
+ * Wrapper functions
+ */
+
+ /// @dev Fills an order with specified parameters and ECDSA signature, throws if specified amount not filled entirely.
+ /// @param orderAddresses Array of order's maker, taker, makerToken, takerToken, and feeRecipient.
+ /// @param orderValues Array of order's makerTokenAmount, takerTokenAmount, makerFee, takerFee, expirationTimestampInSec, and salt.
+ /// @param fillTakerTokenAmount Desired amount of takerToken to fill.
+ /// @param v ECDSA signature parameter v.
+ /// @param r ECDSA signature parameters r.
+ /// @param s ECDSA signature parameters s.
+ function fillOrKillOrder(
+ address[5] orderAddresses,
+ uint[6] orderValues,
+ uint fillTakerTokenAmount,
+ uint8 v,
+ bytes32 r,
+ bytes32 s)
+ public
+ {
+ require(fillOrder(
+ orderAddresses,
+ orderValues,
+ fillTakerTokenAmount,
+ false,
+ v,
+ r,
+ s
+ ) == fillTakerTokenAmount);
+ }
+
+ /// @dev Synchronously executes multiple fill orders in a single transaction.
+ /// @param orderAddresses Array of address arrays containing individual order addresses.
+ /// @param orderValues Array of uint arrays containing individual order values.
+ /// @param fillTakerTokenAmounts Array of desired amounts of takerToken to fill in orders.
+ /// @param shouldThrowOnInsufficientBalanceOrAllowance Test if transfers will fail before attempting.
+ /// @param v Array ECDSA signature v parameters.
+ /// @param r Array of ECDSA signature r parameters.
+ /// @param s Array of ECDSA signature s parameters.
+ function batchFillOrders(
+ address[5][] orderAddresses,
+ uint[6][] orderValues,
+ uint[] fillTakerTokenAmounts,
+ bool shouldThrowOnInsufficientBalanceOrAllowance,
+ uint8[] v,
+ bytes32[] r,
+ bytes32[] s)
+ public
+ {
+ for (uint i = 0; i < orderAddresses.length; i++) {
+ fillOrder(
+ orderAddresses[i],
+ orderValues[i],
+ fillTakerTokenAmounts[i],
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ v[i],
+ r[i],
+ s[i]
+ );
+ }
+ }
+
+ /// @dev Synchronously executes multiple fillOrKill orders in a single transaction.
+ /// @param orderAddresses Array of address arrays containing individual order addresses.
+ /// @param orderValues Array of uint arrays containing individual order values.
+ /// @param fillTakerTokenAmounts Array of desired amounts of takerToken to fill in orders.
+ /// @param v Array ECDSA signature v parameters.
+ /// @param r Array of ECDSA signature r parameters.
+ /// @param s Array of ECDSA signature s parameters.
+ function batchFillOrKillOrders(
+ address[5][] orderAddresses,
+ uint[6][] orderValues,
+ uint[] fillTakerTokenAmounts,
+ uint8[] v,
+ bytes32[] r,
+ bytes32[] s)
+ public
+ {
+ for (uint i = 0; i < orderAddresses.length; i++) {
+ fillOrKillOrder(
+ orderAddresses[i],
+ orderValues[i],
+ fillTakerTokenAmounts[i],
+ v[i],
+ r[i],
+ s[i]
+ );
+ }
+ }
+
+ /// @dev Synchronously executes multiple fill orders in a single transaction until total fillTakerTokenAmount filled.
+ /// @param orderAddresses Array of address arrays containing individual order addresses.
+ /// @param orderValues Array of uint arrays containing individual order values.
+ /// @param fillTakerTokenAmount Desired total amount of takerToken to fill in orders.
+ /// @param shouldThrowOnInsufficientBalanceOrAllowance Test if transfers will fail before attempting.
+ /// @param v Array ECDSA signature v parameters.
+ /// @param r Array of ECDSA signature r parameters.
+ /// @param s Array of ECDSA signature s parameters.
+ /// @return Total amount of fillTakerTokenAmount filled in orders.
+ function fillOrdersUpTo(
+ address[5][] orderAddresses,
+ uint[6][] orderValues,
+ uint fillTakerTokenAmount,
+ bool shouldThrowOnInsufficientBalanceOrAllowance,
+ uint8[] v,
+ bytes32[] r,
+ bytes32[] s)
+ public
+ returns (uint)
+ {
+ uint filledTakerTokenAmount = 0;
+ for (uint i = 0; i < orderAddresses.length; i++) {
+ require(orderAddresses[i][3] == orderAddresses[0][3]); // takerToken must be the same for each order
+ filledTakerTokenAmount = safeAdd(filledTakerTokenAmount, fillOrder(
+ orderAddresses[i],
+ orderValues[i],
+ safeSub(fillTakerTokenAmount, filledTakerTokenAmount),
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ v[i],
+ r[i],
+ s[i]
+ ));
+ if (filledTakerTokenAmount == fillTakerTokenAmount) break;
+ }
+ return filledTakerTokenAmount;
+ }
+
+ /// @dev Synchronously cancels multiple orders in a single transaction.
+ /// @param orderAddresses Array of address arrays containing individual order addresses.
+ /// @param orderValues Array of uint arrays containing individual order values.
+ /// @param cancelTakerTokenAmounts Array of desired amounts of takerToken to cancel in orders.
+ function batchCancelOrders(
+ address[5][] orderAddresses,
+ uint[6][] orderValues,
+ uint[] cancelTakerTokenAmounts)
+ public
+ {
+ for (uint i = 0; i < orderAddresses.length; i++) {
+ cancelOrder(
+ orderAddresses[i],
+ orderValues[i],
+ cancelTakerTokenAmounts[i]
+ );
+ }
+ }
+
+ /*
+ * Constant public functions
+ */
+
+ /// @dev Calculates Keccak-256 hash of order with specified parameters.
+ /// @param orderAddresses Array of order's maker, taker, makerToken, takerToken, and feeRecipient.
+ /// @param orderValues Array of order's makerTokenAmount, takerTokenAmount, makerFee, takerFee, expirationTimestampInSec, and salt.
+ /// @return Keccak-256 hash of order.
+ function getOrderHash(address[5] orderAddresses, uint[6] orderValues)
+ public
+ constant
+ returns (bytes32)
+ {
+ return keccak256(
+ address(this),
+ orderAddresses[0], // maker
+ orderAddresses[1], // taker
+ orderAddresses[2], // makerToken
+ orderAddresses[3], // takerToken
+ orderAddresses[4], // feeRecipient
+ orderValues[0], // makerTokenAmount
+ orderValues[1], // takerTokenAmount
+ orderValues[2], // makerFee
+ orderValues[3], // takerFee
+ orderValues[4], // expirationTimestampInSec
+ orderValues[5] // salt
+ );
+ }
+
+ /// @dev Verifies that an order signature is valid.
+ /// @param signer address of signer.
+ /// @param hash Signed Keccak-256 hash.
+ /// @param v ECDSA signature parameter v.
+ /// @param r ECDSA signature parameters r.
+ /// @param s ECDSA signature parameters s.
+ /// @return Validity of order signature.
+ function isValidSignature(
+ address signer,
+ bytes32 hash,
+ uint8 v,
+ bytes32 r,
+ bytes32 s)
+ public
+ constant
+ returns (bool)
+ {
+ return signer == ecrecover(
+ keccak256("\x19Ethereum Signed Message:\n32", hash),
+ v,
+ r,
+ s
+ );
+ }
+
+ /// @dev Checks if rounding error > 0.1%.
+ /// @param numerator Numerator.
+ /// @param denominator Denominator.
+ /// @param target Value to multiply with numerator/denominator.
+ /// @return Rounding error is present.
+ function isRoundingError(uint numerator, uint denominator, uint target)
+ public
+ constant
+ returns (bool)
+ {
+ uint remainder = mulmod(target, numerator, denominator);
+ if (remainder == 0) return false; // No rounding error.
+
+ uint errPercentageTimes1000000 = safeDiv(
+ safeMul(remainder, 1000000),
+ safeMul(numerator, target)
+ );
+ return errPercentageTimes1000000 > 1000;
+ }
+
+ /// @dev Calculates partial value given a numerator and denominator.
+ /// @param numerator Numerator.
+ /// @param denominator Denominator.
+ /// @param target Value to calculate partial of.
+ /// @return Partial value of target.
+ function getPartialAmount(uint numerator, uint denominator, uint target)
+ public
+ constant
+ returns (uint)
+ {
+ return safeDiv(safeMul(numerator, target), denominator);
+ }
+
+ /// @dev Calculates the sum of values already filled and cancelled for a given order.
+ /// @param orderHash The Keccak-256 hash of the given order.
+ /// @return Sum of values already filled and cancelled.
+ function getUnavailableTakerTokenAmount(bytes32 orderHash)
+ public
+ constant
+ returns (uint)
+ {
+ return safeAdd(filled[orderHash], cancelled[orderHash]);
+ }
+
+
+ /*
+ * Internal functions
+ */
+
+ /// @dev Transfers a token using TokenTransferProxy transferFrom function.
+ /// @param token Address of token to transferFrom.
+ /// @param from Address transfering token.
+ /// @param to Address receiving token.
+ /// @param value Amount of token to transfer.
+ /// @return Success of token transfer.
+ function transferViaTokenTransferProxy(
+ address token,
+ address from,
+ address to,
+ uint value)
+ internal
+ returns (bool)
+ {
+ return TokenTransferProxy(TOKEN_TRANSFER_PROXY_CONTRACT).transferFrom(token, from, to, value);
+ }
+
+ /// @dev Checks if any order transfers will fail.
+ /// @param order Order struct of params that will be checked.
+ /// @param fillTakerTokenAmount Desired amount of takerToken to fill.
+ /// @return Predicted result of transfers.
+ function isTransferable(Order order, uint fillTakerTokenAmount)
+ internal
+ constant // The called token contracts may attempt to change state, but will not be able to due to gas limits on getBalance and getAllowance.
+ returns (bool)
+ {
+ address taker = msg.sender;
+ uint fillMakerTokenAmount = getPartialAmount(fillTakerTokenAmount, order.takerTokenAmount, order.makerTokenAmount);
+
+ if (order.feeRecipient != address(0)) {
+ bool isMakerTokenZRX = order.makerToken == ZRX_TOKEN_CONTRACT;
+ bool isTakerTokenZRX = order.takerToken == ZRX_TOKEN_CONTRACT;
+ uint paidMakerFee = getPartialAmount(fillTakerTokenAmount, order.takerTokenAmount, order.makerFee);
+ uint paidTakerFee = getPartialAmount(fillTakerTokenAmount, order.takerTokenAmount, order.takerFee);
+ uint requiredMakerZRX = isMakerTokenZRX ? safeAdd(fillMakerTokenAmount, paidMakerFee) : paidMakerFee;
+ uint requiredTakerZRX = isTakerTokenZRX ? safeAdd(fillTakerTokenAmount, paidTakerFee) : paidTakerFee;
+
+ if ( getBalance(ZRX_TOKEN_CONTRACT, order.maker) < requiredMakerZRX
+ || getAllowance(ZRX_TOKEN_CONTRACT, order.maker) < requiredMakerZRX
+ || getBalance(ZRX_TOKEN_CONTRACT, taker) < requiredTakerZRX
+ || getAllowance(ZRX_TOKEN_CONTRACT, taker) < requiredTakerZRX
+ ) return false;
+
+ if (!isMakerTokenZRX && ( getBalance(order.makerToken, order.maker) < fillMakerTokenAmount // Don't double check makerToken if ZRX
+ || getAllowance(order.makerToken, order.maker) < fillMakerTokenAmount)
+ ) return false;
+ if (!isTakerTokenZRX && ( getBalance(order.takerToken, taker) < fillTakerTokenAmount // Don't double check takerToken if ZRX
+ || getAllowance(order.takerToken, taker) < fillTakerTokenAmount)
+ ) return false;
+ } else if ( getBalance(order.makerToken, order.maker) < fillMakerTokenAmount
+ || getAllowance(order.makerToken, order.maker) < fillMakerTokenAmount
+ || getBalance(order.takerToken, taker) < fillTakerTokenAmount
+ || getAllowance(order.takerToken, taker) < fillTakerTokenAmount
+ ) return false;
+
+ return true;
+ }
+
+ /// @dev Get token balance of an address.
+ /// @param token Address of token.
+ /// @param owner Address of owner.
+ /// @return Token balance of owner.
+ function getBalance(address token, address owner)
+ internal
+ constant // The called token contract may attempt to change state, but will not be able to due to an added gas limit.
+ returns (uint)
+ {
+ return Token(token).balanceOf.gas(EXTERNAL_QUERY_GAS_LIMIT)(owner); // Limit gas to prevent reentrancy
+ }
+
+ /// @dev Get allowance of token given to TokenTransferProxy by an address.
+ /// @param token Address of token.
+ /// @param owner Address of owner.
+ /// @return Allowance of token given to TokenTransferProxy by owner.
+ function getAllowance(address token, address owner)
+ internal
+ constant // The called token contract may attempt to change state, but will not be able to due to an added gas limit.
+ returns (uint)
+ {
+ return Token(token).allowance.gas(EXTERNAL_QUERY_GAS_LIMIT)(owner, TOKEN_TRANSFER_PROXY_CONTRACT); // Limit gas to prevent reentrancy
+ }
+}
diff --git a/packages/contracts/src/1.0.0/Exchange/IExchange_v1.sol b/packages/contracts/src/1.0.0/Exchange/IExchange_v1.sol
new file mode 100644
index 000000000..ec4bce7b0
--- /dev/null
+++ b/packages/contracts/src/1.0.0/Exchange/IExchange_v1.sol
@@ -0,0 +1,226 @@
+pragma solidity ^0.4.19;
+
+contract IExchange_v1 {
+
+ // Error Codes
+ enum Errors {
+ ORDER_EXPIRED, // Order has already expired
+ ORDER_FULLY_FILLED_OR_CANCELLED, // Order has already been fully filled or cancelled
+ ROUNDING_ERROR_TOO_LARGE, // Rounding error too large
+ INSUFFICIENT_BALANCE_OR_ALLOWANCE // Insufficient balance or allowance for token transfer
+ }
+
+ event LogError(uint8 indexed errorId, bytes32 indexed orderHash);
+
+ event LogFill(
+ address indexed maker,
+ address taker,
+ address indexed feeRecipient,
+ address makerToken,
+ address takerToken,
+ uint filledMakerTokenAmount,
+ uint filledTakerTokenAmount,
+ uint paidMakerFee,
+ uint paidTakerFee,
+ bytes32 indexed tokens, // keccak256(makerToken, takerToken), allows subscribing to a token pair
+ bytes32 orderHash
+ );
+
+ event LogCancel(
+ address indexed maker,
+ address indexed feeRecipient,
+ address makerToken,
+ address takerToken,
+ uint cancelledMakerTokenAmount,
+ uint cancelledTakerTokenAmount,
+ bytes32 indexed tokens,
+ bytes32 orderHash
+ );
+
+ function ZRX_TOKEN_CONTRACT()
+ public view
+ returns (address);
+
+ function TOKEN_TRANSFER_PROXY_CONTRACT()
+ public view
+ returns (address);
+
+ function EXTERNAL_QUERY_GAS_LIMIT()
+ public view
+ returns (uint16);
+
+ function VERSION()
+ public view
+ returns (string);
+
+ function filled(bytes32)
+ public view
+ returns (uint256);
+
+ function cancelled(bytes32)
+ public view
+ returns (uint256);
+
+ /// @dev Calculates the sum of values already filled and cancelled for a given order.
+ /// @param orderHash The Keccak-256 hash of the given order.
+ /// @return Sum of values already filled and cancelled.
+ function getUnavailableTakerTokenAmount(bytes32 orderHash)
+ public constant
+ returns (uint);
+
+ /// @dev Calculates partial value given a numerator and denominator.
+ /// @param numerator Numerator.
+ /// @param denominator Denominator.
+ /// @param target Value to calculate partial of.
+ /// @return Partial value of target.
+ function getPartialAmount(uint numerator, uint denominator, uint target)
+ public constant
+ returns (uint);
+
+ /// @dev Checks if rounding error > 0.1%.
+ /// @param numerator Numerator.
+ /// @param denominator Denominator.
+ /// @param target Value to multiply with numerator/denominator.
+ /// @return Rounding error is present.
+ function isRoundingError(uint numerator, uint denominator, uint target)
+ public constant
+ returns (bool);
+
+ /// @dev Calculates Keccak-256 hash of order with specified parameters.
+ /// @param orderAddresses Array of order's maker, taker, makerToken, takerToken, and feeRecipient.
+ /// @param orderValues Array of order's makerTokenAmount, takerTokenAmount, makerFee, takerFee, expirationTimestampInSec, and salt.
+ /// @return Keccak-256 hash of order.
+ function getOrderHash(address[5] orderAddresses, uint[6] orderValues)
+ public
+ constant
+ returns (bytes32);
+
+ /// @dev Verifies that an order signature is valid.
+ /// @param signer address of signer.
+ /// @param hash Signed Keccak-256 hash.
+ /// @param v ECDSA signature parameter v.
+ /// @param r ECDSA signature parameters r.
+ /// @param s ECDSA signature parameters s.
+ /// @return Validity of order signature.
+ function isValidSignature(
+ address signer,
+ bytes32 hash,
+ uint8 v,
+ bytes32 r,
+ bytes32 s)
+ public constant
+ returns (bool);
+
+ /// @dev Fills the input order.
+ /// @param orderAddresses Array of order's maker, taker, makerToken, takerToken, and feeRecipient.
+ /// @param orderValues Array of order's makerTokenAmount, takerTokenAmount, makerFee, takerFee, expirationTimestampInSec, and salt.
+ /// @param fillTakerTokenAmount Desired amount of takerToken to fill.
+ /// @param shouldThrowOnInsufficientBalanceOrAllowance Test if transfer will fail before attempting.
+ /// @param v ECDSA signature parameter v.
+ /// @param r ECDSA signature parameters r.
+ /// @param s ECDSA signature parameters s.
+ /// @return Total amount of takerToken filled in trade.
+ function fillOrder(
+ address[5] orderAddresses,
+ uint[6] orderValues,
+ uint fillTakerTokenAmount,
+ bool shouldThrowOnInsufficientBalanceOrAllowance,
+ uint8 v,
+ bytes32 r,
+ bytes32 s)
+ public
+ returns (uint filledTakerTokenAmount);
+
+ /// @dev Cancels the input order.
+ /// @param orderAddresses Array of order's maker, taker, makerToken, takerToken, and feeRecipient.
+ /// @param orderValues Array of order's makerTokenAmount, takerTokenAmount, makerFee, takerFee, expirationTimestampInSec, and salt.
+ /// @param cancelTakerTokenAmount Desired amount of takerToken to cancel in order.
+ /// @return Amount of takerToken cancelled.
+ function cancelOrder(
+ address[5] orderAddresses,
+ uint[6] orderValues,
+ uint cancelTakerTokenAmount)
+ public
+ returns (uint);
+
+
+ /// @dev Fills an order with specified parameters and ECDSA signature, throws if specified amount not filled entirely.
+ /// @param orderAddresses Array of order's maker, taker, makerToken, takerToken, and feeRecipient.
+ /// @param orderValues Array of order's makerTokenAmount, takerTokenAmount, makerFee, takerFee, expirationTimestampInSec, and salt.
+ /// @param fillTakerTokenAmount Desired amount of takerToken to fill.
+ /// @param v ECDSA signature parameter v.
+ /// @param r ECDSA signature parameters r.
+ /// @param s ECDSA signature parameters s.
+ function fillOrKillOrder(
+ address[5] orderAddresses,
+ uint[6] orderValues,
+ uint fillTakerTokenAmount,
+ uint8 v,
+ bytes32 r,
+ bytes32 s)
+ public;
+
+ /// @dev Synchronously executes multiple fill orders in a single transaction.
+ /// @param orderAddresses Array of address arrays containing individual order addresses.
+ /// @param orderValues Array of uint arrays containing individual order values.
+ /// @param fillTakerTokenAmounts Array of desired amounts of takerToken to fill in orders.
+ /// @param shouldThrowOnInsufficientBalanceOrAllowance Test if transfers will fail before attempting.
+ /// @param v Array ECDSA signature v parameters.
+ /// @param r Array of ECDSA signature r parameters.
+ /// @param s Array of ECDSA signature s parameters.
+ function batchFillOrders(
+ address[5][] orderAddresses,
+ uint[6][] orderValues,
+ uint[] fillTakerTokenAmounts,
+ bool shouldThrowOnInsufficientBalanceOrAllowance,
+ uint8[] v,
+ bytes32[] r,
+ bytes32[] s)
+ public;
+
+ /// @dev Synchronously executes multiple fillOrKill orders in a single transaction.
+ /// @param orderAddresses Array of address arrays containing individual order addresses.
+ /// @param orderValues Array of uint arrays containing individual order values.
+ /// @param fillTakerTokenAmounts Array of desired amounts of takerToken to fill in orders.
+ /// @param v Array ECDSA signature v parameters.
+ /// @param r Array of ECDSA signature r parameters.
+ /// @param s Array of ECDSA signature s parameters.
+ function batchFillOrKillOrders(
+ address[5][] orderAddresses,
+ uint[6][] orderValues,
+ uint[] fillTakerTokenAmounts,
+ uint8[] v,
+ bytes32[] r,
+ bytes32[] s)
+ public;
+
+ /// @dev Synchronously executes multiple fill orders in a single transaction until total fillTakerTokenAmount filled.
+ /// @param orderAddresses Array of address arrays containing individual order addresses.
+ /// @param orderValues Array of uint arrays containing individual order values.
+ /// @param fillTakerTokenAmount Desired total amount of takerToken to fill in orders.
+ /// @param shouldThrowOnInsufficientBalanceOrAllowance Test if transfers will fail before attempting.
+ /// @param v Array ECDSA signature v parameters.
+ /// @param r Array of ECDSA signature r parameters.
+ /// @param s Array of ECDSA signature s parameters.
+ /// @return Total amount of fillTakerTokenAmount filled in orders.
+ function fillOrdersUpTo(
+ address[5][] orderAddresses,
+ uint[6][] orderValues,
+ uint fillTakerTokenAmount,
+ bool shouldThrowOnInsufficientBalanceOrAllowance,
+ uint8[] v,
+ bytes32[] r,
+ bytes32[] s)
+ public
+ returns (uint);
+
+ /// @dev Synchronously cancels multiple orders in a single transaction.
+ /// @param orderAddresses Array of address arrays containing individual order addresses.
+ /// @param orderValues Array of uint arrays containing individual order values.
+ /// @param cancelTakerTokenAmounts Array of desired amounts of takerToken to cancel in orders.
+ function batchCancelOrders(
+ address[5][] orderAddresses,
+ uint[6][] orderValues,
+ uint[] cancelTakerTokenAmounts)
+ public;
+}
diff --git a/packages/contracts/src/1.0.0/MultiSigWalletWithTImeLockExceptRemoveAuthorizedAddress/MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress.sol b/packages/contracts/src/1.0.0/MultiSigWalletWithTImeLockExceptRemoveAuthorizedAddress/MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress.sol
new file mode 100644
index 000000000..241e02d4a
--- /dev/null
+++ b/packages/contracts/src/1.0.0/MultiSigWalletWithTImeLockExceptRemoveAuthorizedAddress/MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress.sol
@@ -0,0 +1,82 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ 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.10;
+
+import "../../current/multisig/MultiSigWalletWithTimeLock.sol";
+
+contract MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress is MultiSigWalletWithTimeLock {
+
+ address public TOKEN_TRANSFER_PROXY_CONTRACT;
+
+ modifier validRemoveAuthorizedAddressTx(uint transactionId) {
+ Transaction storage tx = transactions[transactionId];
+ require(tx.destination == TOKEN_TRANSFER_PROXY_CONTRACT);
+ require(isFunctionRemoveAuthorizedAddress(tx.data));
+ _;
+ }
+
+ /// @dev Contract constructor sets initial owners, required number of confirmations, time lock, and tokenTransferProxy address.
+ /// @param _owners List of initial owners.
+ /// @param _required Number of required confirmations.
+ /// @param _secondsTimeLocked Duration needed after a transaction is confirmed and before it becomes executable, in seconds.
+ /// @param _tokenTransferProxy Address of TokenTransferProxy contract.
+ function MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress(
+ address[] _owners,
+ uint _required,
+ uint _secondsTimeLocked,
+ address _tokenTransferProxy)
+ public
+ MultiSigWalletWithTimeLock(_owners, _required, _secondsTimeLocked)
+ {
+ TOKEN_TRANSFER_PROXY_CONTRACT = _tokenTransferProxy;
+ }
+
+ /// @dev Allows execution of removeAuthorizedAddress without time lock.
+ /// @param transactionId Transaction ID.
+ function executeRemoveAuthorizedAddress(uint transactionId)
+ public
+ notExecuted(transactionId)
+ fullyConfirmed(transactionId)
+ validRemoveAuthorizedAddressTx(transactionId)
+ {
+ Transaction storage tx = transactions[transactionId];
+ tx.executed = true;
+ if (tx.destination.call.value(tx.value)(tx.data))
+ Execution(transactionId);
+ else {
+ ExecutionFailure(transactionId);
+ tx.executed = false;
+ }
+ }
+
+ /// @dev Compares first 4 bytes of byte array to removeAuthorizedAddress function signature.
+ /// @param data Transaction data.
+ /// @return Successful if data is a call to removeAuthorizedAddress.
+ function isFunctionRemoveAuthorizedAddress(bytes data)
+ public
+ constant
+ returns (bool)
+ {
+ bytes4 removeAuthorizedAddressSignature = bytes4(sha3("removeAuthorizedAddress(address)"));
+ for (uint i = 0; i < 4; i++) {
+ require(data[i] == removeAuthorizedAddressSignature[i]);
+ }
+ return true;
+ }
+} \ No newline at end of file
diff --git a/packages/contracts/src/1.0.0/Ownable/IOwnable_v1.sol b/packages/contracts/src/1.0.0/Ownable/IOwnable_v1.sol
new file mode 100644
index 000000000..7e22d544d
--- /dev/null
+++ b/packages/contracts/src/1.0.0/Ownable/IOwnable_v1.sol
@@ -0,0 +1,18 @@
+pragma solidity ^0.4.19;
+
+/*
+ * Ownable
+ *
+ * Base contract with an owner.
+ * Provides onlyOwner modifier, which prevents function from running if it is called by anyone other than the owner.
+ */
+
+contract IOwnable_v1 {
+
+ function owner()
+ public view
+ returns (address);
+
+ function transferOwnership(address newOwner)
+ public;
+}
diff --git a/packages/contracts/src/1.0.0/Ownable/Ownable_v1.sol b/packages/contracts/src/1.0.0/Ownable/Ownable_v1.sol
new file mode 100644
index 000000000..c87438fa4
--- /dev/null
+++ b/packages/contracts/src/1.0.0/Ownable/Ownable_v1.sol
@@ -0,0 +1,27 @@
+pragma solidity ^0.4.11;
+
+/*
+ * Ownable
+ *
+ * Base contract with an owner.
+ * Provides onlyOwner modifier, which prevents function from running if it is called by anyone other than the owner.
+ */
+
+contract Ownable_v1 {
+ address public owner;
+
+ function Ownable_v1() {
+ owner = msg.sender;
+ }
+
+ modifier onlyOwner() {
+ require(msg.sender == owner);
+ _;
+ }
+
+ function transferOwnership(address newOwner) onlyOwner {
+ if (newOwner != address(0)) {
+ owner = newOwner;
+ }
+ }
+}
diff --git a/packages/contracts/src/1.0.0/SafeMath/SafeMath_v1.sol b/packages/contracts/src/1.0.0/SafeMath/SafeMath_v1.sol
new file mode 100644
index 000000000..341d611ec
--- /dev/null
+++ b/packages/contracts/src/1.0.0/SafeMath/SafeMath_v1.sol
@@ -0,0 +1,73 @@
+pragma solidity ^0.4.11;
+
+contract SafeMath_v1 {
+ function safeMul(uint a, uint b)
+ internal
+ constant
+ returns (uint256)
+ {
+ uint c = a * b;
+ assert(a == 0 || c / a == b);
+ return c;
+ }
+
+ function safeDiv(uint a, uint b)
+ internal
+ constant
+ returns (uint256)
+ {
+ uint c = a / b;
+ return c;
+ }
+
+ function safeSub(uint a, uint b)
+ internal
+ constant
+ returns (uint256)
+ {
+ assert(b <= a);
+ return a - b;
+ }
+
+ function safeAdd(uint a, uint b)
+ internal
+ constant
+ returns (uint256)
+ {
+ uint c = a + b;
+ assert(c >= a);
+ return c;
+ }
+
+ function max64(uint64 a, uint64 b)
+ internal
+ constant
+ returns (uint64)
+ {
+ return a >= b ? a : b;
+ }
+
+ function min64(uint64 a, uint64 b)
+ internal
+ constant
+ returns (uint64)
+ {
+ return a < b ? a : b;
+ }
+
+ function max256(uint256 a, uint256 b)
+ internal
+ constant
+ returns (uint256)
+ {
+ return a >= b ? a : b;
+ }
+
+ function min256(uint256 a, uint256 b)
+ internal
+ constant
+ returns (uint256)
+ {
+ return a < b ? a : b;
+ }
+}
diff --git a/packages/contracts/src/1.0.0/Token/Token_v1.sol b/packages/contracts/src/1.0.0/Token/Token_v1.sol
new file mode 100644
index 000000000..de619fb7e
--- /dev/null
+++ b/packages/contracts/src/1.0.0/Token/Token_v1.sol
@@ -0,0 +1,39 @@
+pragma solidity ^0.4.11;
+
+contract Token_v1 {
+
+ /// @return total amount of tokens
+ function totalSupply() constant returns (uint supply) {}
+
+ /// @param _owner The address from which the balance will be retrieved
+ /// @return The balance
+ function balanceOf(address _owner) constant returns (uint balance) {}
+
+ /// @notice send `_value` token to `_to` from `msg.sender`
+ /// @param _to The address of the recipient
+ /// @param _value The amount of token to be transferred
+ /// @return Whether the transfer was successful or not
+ function transfer(address _to, uint _value) returns (bool success) {}
+
+ /// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`
+ /// @param _from The address of the sender
+ /// @param _to The address of the recipient
+ /// @param _value The amount of token to be transferred
+ /// @return Whether the transfer was successful or not
+ function transferFrom(address _from, address _to, uint _value) returns (bool success) {}
+
+ /// @notice `msg.sender` approves `_addr` to spend `_value` tokens
+ /// @param _spender The address of the account able to transfer the tokens
+ /// @param _value The amount of wei to be approved for transfer
+ /// @return Whether the approval was successful or not
+ function approve(address _spender, uint _value) returns (bool success) {}
+
+ /// @param _owner The address of the account owning tokens
+ /// @param _spender The address of the account able to transfer the tokens
+ /// @return Amount of remaining tokens allowed to spent
+ function allowance(address _owner, address _spender) constant returns (uint remaining) {}
+
+ event Transfer(address indexed _from, address indexed _to, uint _value);
+ event Approval(address indexed _owner, address indexed _spender, uint _value);
+}
+
diff --git a/packages/contracts/src/1.0.0/TokenRegistry/ITokenRegistery.sol b/packages/contracts/src/1.0.0/TokenRegistry/ITokenRegistery.sol
new file mode 100644
index 000000000..b8bdaf3b9
--- /dev/null
+++ b/packages/contracts/src/1.0.0/TokenRegistry/ITokenRegistery.sol
@@ -0,0 +1,195 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ 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.21;
+
+import { IOwnable_v1 as IOwnable } from "../Ownable/IOwnable_v1.sol";
+
+/// @title Token Registry - Stores metadata associated with ERC20 tokens. See ERC22 https://github.com/ethereum/EIPs/issues/22
+/// @author Amir Bandeali - <amir@0xProject.com>, Will Warren - <will@0xProject.com>
+contract ITokenRegistery is IOwnable {
+
+ event LogAddToken(
+ address indexed token,
+ string name,
+ string symbol,
+ uint8 decimals,
+ bytes ipfsHash,
+ bytes swarmHash
+ );
+
+ event LogRemoveToken(
+ address indexed token,
+ string name,
+ string symbol,
+ uint8 decimals,
+ bytes ipfsHash,
+ bytes swarmHash
+ );
+
+ event LogTokenNameChange(
+ address indexed token,
+ string oldName,
+ string newName
+ );
+
+ event LogTokenSymbolChange(
+ address indexed token,
+ string oldSymbol,
+ string newSymbol
+ );
+
+ event LogTokenIpfsHashChange(
+ address indexed token,
+ bytes oldIpfsHash,
+ bytes newIpfsHash
+ );
+
+ event LogTokenSwarmHashChange(
+ address indexed token,
+ bytes oldSwarmHash,
+ bytes newSwarmHash
+ );
+
+ function tokens(address tokenAddress)
+ public view
+ returns (
+ address token,
+ string name,
+ string symbol,
+ uint8 decimals,
+ bytes ipfsHash,
+ bytes swarmHash
+ );
+
+ function tokenAddresses(uint256 index)
+ public view
+ returns (address);
+
+
+ /// @dev Allows owner to add a new token to the registry.
+ /// @param _token Address of new token.
+ /// @param _name Name of new token.
+ /// @param _symbol Symbol for new token.
+ /// @param _decimals Number of decimals, divisibility of new token.
+ /// @param _ipfsHash IPFS hash of token icon.
+ /// @param _swarmHash Swarm hash of token icon.
+ function addToken(
+ address _token,
+ string _name,
+ string _symbol,
+ uint8 _decimals,
+ bytes _ipfsHash,
+ bytes _swarmHash)
+ public;
+
+ /// @dev Allows owner to remove an existing token from the registry.
+ /// @param _token Address of existing token.
+ function removeToken(address _token, uint _index)
+ public;
+
+ /// @dev Allows owner to modify an existing token's name.
+ /// @param _token Address of existing token.
+ /// @param _name New name.
+ function setTokenName(address _token, string _name)
+ public;
+
+ /// @dev Allows owner to modify an existing token's symbol.
+ /// @param _token Address of existing token.
+ /// @param _symbol New symbol.
+ function setTokenSymbol(address _token, string _symbol)
+ public;
+
+ /// @dev Allows owner to modify an existing token's IPFS hash.
+ /// @param _token Address of existing token.
+ /// @param _ipfsHash New IPFS hash.
+ function setTokenIpfsHash(address _token, bytes _ipfsHash)
+ public;
+
+ /// @dev Allows owner to modify an existing token's Swarm hash.
+ /// @param _token Address of existing token.
+ /// @param _swarmHash New Swarm hash.
+ function setTokenSwarmHash(address _token, bytes _swarmHash)
+ public;
+
+ /*
+ * Web3 call functions
+ */
+
+ /// @dev Provides a registered token's address when given the token symbol.
+ /// @param _symbol Symbol of registered token.
+ /// @return Token's address.
+ function getTokenAddressBySymbol(string _symbol)
+ public constant
+ returns (address);
+
+ /// @dev Provides a registered token's address when given the token name.
+ /// @param _name Name of registered token.
+ /// @return Token's address.
+ function getTokenAddressByName(string _name)
+ public constant
+ returns (address);
+
+ /// @dev Provides a registered token's metadata, looked up by address.
+ /// @param _token Address of registered token.
+ /// @return Token metadata.
+ function getTokenMetaData(address _token)
+ public constant
+ returns (
+ address, //tokenAddress
+ string, //name
+ string, //symbol
+ uint8, //decimals
+ bytes, //ipfsHash
+ bytes //swarmHash
+ );
+
+ /// @dev Provides a registered token's metadata, looked up by name.
+ /// @param _name Name of registered token.
+ /// @return Token metadata.
+ function getTokenByName(string _name)
+ public constant
+ returns (
+ address, //tokenAddress
+ string, //name
+ string, //symbol
+ uint8, //decimals
+ bytes, //ipfsHash
+ bytes //swarmHash
+ );
+
+ /// @dev Provides a registered token's metadata, looked up by symbol.
+ /// @param _symbol Symbol of registered token.
+ /// @return Token metadata.
+ function getTokenBySymbol(string _symbol)
+ public constant
+ returns (
+ address, //tokenAddress
+ string, //name
+ string, //symbol
+ uint8, //decimals
+ bytes, //ipfsHash
+ bytes //swarmHash
+ );
+
+ /// @dev Returns an array containing all token addresses.
+ /// @return Array of token addresses.
+ function getTokenAddresses()
+ public constant
+ returns (address[]);
+}
diff --git a/packages/contracts/src/1.0.0/TokenRegistry/TokenRegistry.sol b/packages/contracts/src/1.0.0/TokenRegistry/TokenRegistry.sol
new file mode 100644
index 000000000..7417a10a3
--- /dev/null
+++ b/packages/contracts/src/1.0.0/TokenRegistry/TokenRegistry.sol
@@ -0,0 +1,308 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ 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.11;
+
+import { Ownable_v1 as Ownable } from "../Ownable/Ownable_v1.sol";
+
+/// @title Token Registry - Stores metadata associated with ERC20 tokens. See ERC22 https://github.com/ethereum/EIPs/issues/22
+/// @author Amir Bandeali - <amir@0xProject.com>, Will Warren - <will@0xProject.com>
+contract TokenRegistry is Ownable {
+
+ event LogAddToken(
+ address indexed token,
+ string name,
+ string symbol,
+ uint8 decimals,
+ bytes ipfsHash,
+ bytes swarmHash
+ );
+
+ event LogRemoveToken(
+ address indexed token,
+ string name,
+ string symbol,
+ uint8 decimals,
+ bytes ipfsHash,
+ bytes swarmHash
+ );
+
+ event LogTokenNameChange(address indexed token, string oldName, string newName);
+ event LogTokenSymbolChange(address indexed token, string oldSymbol, string newSymbol);
+ event LogTokenIpfsHashChange(address indexed token, bytes oldIpfsHash, bytes newIpfsHash);
+ event LogTokenSwarmHashChange(address indexed token, bytes oldSwarmHash, bytes newSwarmHash);
+
+ mapping (address => TokenMetadata) public tokens;
+ mapping (string => address) tokenBySymbol;
+ mapping (string => address) tokenByName;
+
+ address[] public tokenAddresses;
+
+ struct TokenMetadata {
+ address token;
+ string name;
+ string symbol;
+ uint8 decimals;
+ bytes ipfsHash;
+ bytes swarmHash;
+ }
+
+ modifier tokenExists(address _token) {
+ require(tokens[_token].token != address(0));
+ _;
+ }
+
+ modifier tokenDoesNotExist(address _token) {
+ require(tokens[_token].token == address(0));
+ _;
+ }
+
+ modifier nameDoesNotExist(string _name) {
+ require(tokenByName[_name] == address(0));
+ _;
+ }
+
+ modifier symbolDoesNotExist(string _symbol) {
+ require(tokenBySymbol[_symbol] == address(0));
+ _;
+ }
+
+ modifier addressNotNull(address _address) {
+ require(_address != address(0));
+ _;
+ }
+
+
+ /// @dev Allows owner to add a new token to the registry.
+ /// @param _token Address of new token.
+ /// @param _name Name of new token.
+ /// @param _symbol Symbol for new token.
+ /// @param _decimals Number of decimals, divisibility of new token.
+ /// @param _ipfsHash IPFS hash of token icon.
+ /// @param _swarmHash Swarm hash of token icon.
+ function addToken(
+ address _token,
+ string _name,
+ string _symbol,
+ uint8 _decimals,
+ bytes _ipfsHash,
+ bytes _swarmHash)
+ public
+ onlyOwner
+ tokenDoesNotExist(_token)
+ addressNotNull(_token)
+ symbolDoesNotExist(_symbol)
+ nameDoesNotExist(_name)
+ {
+ tokens[_token] = TokenMetadata({
+ token: _token,
+ name: _name,
+ symbol: _symbol,
+ decimals: _decimals,
+ ipfsHash: _ipfsHash,
+ swarmHash: _swarmHash
+ });
+ tokenAddresses.push(_token);
+ tokenBySymbol[_symbol] = _token;
+ tokenByName[_name] = _token;
+ LogAddToken(
+ _token,
+ _name,
+ _symbol,
+ _decimals,
+ _ipfsHash,
+ _swarmHash
+ );
+ }
+
+ /// @dev Allows owner to remove an existing token from the registry.
+ /// @param _token Address of existing token.
+ function removeToken(address _token, uint _index)
+ public
+ onlyOwner
+ tokenExists(_token)
+ {
+ require(tokenAddresses[_index] == _token);
+
+ tokenAddresses[_index] = tokenAddresses[tokenAddresses.length - 1];
+ tokenAddresses.length -= 1;
+
+ TokenMetadata storage token = tokens[_token];
+ LogRemoveToken(
+ token.token,
+ token.name,
+ token.symbol,
+ token.decimals,
+ token.ipfsHash,
+ token.swarmHash
+ );
+ delete tokenBySymbol[token.symbol];
+ delete tokenByName[token.name];
+ delete tokens[_token];
+ }
+
+ /// @dev Allows owner to modify an existing token's name.
+ /// @param _token Address of existing token.
+ /// @param _name New name.
+ function setTokenName(address _token, string _name)
+ public
+ onlyOwner
+ tokenExists(_token)
+ nameDoesNotExist(_name)
+ {
+ TokenMetadata storage token = tokens[_token];
+ LogTokenNameChange(_token, token.name, _name);
+ delete tokenByName[token.name];
+ tokenByName[_name] = _token;
+ token.name = _name;
+ }
+
+ /// @dev Allows owner to modify an existing token's symbol.
+ /// @param _token Address of existing token.
+ /// @param _symbol New symbol.
+ function setTokenSymbol(address _token, string _symbol)
+ public
+ onlyOwner
+ tokenExists(_token)
+ symbolDoesNotExist(_symbol)
+ {
+ TokenMetadata storage token = tokens[_token];
+ LogTokenSymbolChange(_token, token.symbol, _symbol);
+ delete tokenBySymbol[token.symbol];
+ tokenBySymbol[_symbol] = _token;
+ token.symbol = _symbol;
+ }
+
+ /// @dev Allows owner to modify an existing token's IPFS hash.
+ /// @param _token Address of existing token.
+ /// @param _ipfsHash New IPFS hash.
+ function setTokenIpfsHash(address _token, bytes _ipfsHash)
+ public
+ onlyOwner
+ tokenExists(_token)
+ {
+ TokenMetadata storage token = tokens[_token];
+ LogTokenIpfsHashChange(_token, token.ipfsHash, _ipfsHash);
+ token.ipfsHash = _ipfsHash;
+ }
+
+ /// @dev Allows owner to modify an existing token's Swarm hash.
+ /// @param _token Address of existing token.
+ /// @param _swarmHash New Swarm hash.
+ function setTokenSwarmHash(address _token, bytes _swarmHash)
+ public
+ onlyOwner
+ tokenExists(_token)
+ {
+ TokenMetadata storage token = tokens[_token];
+ LogTokenSwarmHashChange(_token, token.swarmHash, _swarmHash);
+ token.swarmHash = _swarmHash;
+ }
+
+ /*
+ * Web3 call functions
+ */
+
+ /// @dev Provides a registered token's address when given the token symbol.
+ /// @param _symbol Symbol of registered token.
+ /// @return Token's address.
+ function getTokenAddressBySymbol(string _symbol) constant returns (address) {
+ return tokenBySymbol[_symbol];
+ }
+
+ /// @dev Provides a registered token's address when given the token name.
+ /// @param _name Name of registered token.
+ /// @return Token's address.
+ function getTokenAddressByName(string _name) constant returns (address) {
+ return tokenByName[_name];
+ }
+
+ /// @dev Provides a registered token's metadata, looked up by address.
+ /// @param _token Address of registered token.
+ /// @return Token metadata.
+ function getTokenMetaData(address _token)
+ public
+ constant
+ returns (
+ address, //tokenAddress
+ string, //name
+ string, //symbol
+ uint8, //decimals
+ bytes, //ipfsHash
+ bytes //swarmHash
+ )
+ {
+ TokenMetadata memory token = tokens[_token];
+ return (
+ token.token,
+ token.name,
+ token.symbol,
+ token.decimals,
+ token.ipfsHash,
+ token.swarmHash
+ );
+ }
+
+ /// @dev Provides a registered token's metadata, looked up by name.
+ /// @param _name Name of registered token.
+ /// @return Token metadata.
+ function getTokenByName(string _name)
+ public
+ constant
+ returns (
+ address, //tokenAddress
+ string, //name
+ string, //symbol
+ uint8, //decimals
+ bytes, //ipfsHash
+ bytes //swarmHash
+ )
+ {
+ address _token = tokenByName[_name];
+ return getTokenMetaData(_token);
+ }
+
+ /// @dev Provides a registered token's metadata, looked up by symbol.
+ /// @param _symbol Symbol of registered token.
+ /// @return Token metadata.
+ function getTokenBySymbol(string _symbol)
+ public
+ constant
+ returns (
+ address, //tokenAddress
+ string, //name
+ string, //symbol
+ uint8, //decimals
+ bytes, //ipfsHash
+ bytes //swarmHash
+ )
+ {
+ address _token = tokenBySymbol[_symbol];
+ return getTokenMetaData(_token);
+ }
+
+ /// @dev Returns an array containing all token addresses.
+ /// @return Array of token addresses.
+ function getTokenAddresses()
+ public
+ constant
+ returns (address[])
+ {
+ return tokenAddresses;
+ }
+}
diff --git a/packages/contracts/src/1.0.0/TokenTransferProxy/TokenTransferProxy_v1.sol b/packages/contracts/src/1.0.0/TokenTransferProxy/TokenTransferProxy_v1.sol
new file mode 100644
index 000000000..e3659d8ba
--- /dev/null
+++ b/packages/contracts/src/1.0.0/TokenTransferProxy/TokenTransferProxy_v1.sol
@@ -0,0 +1,115 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ 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.11;
+
+import { Token_v1 as Token } from "../Token/Token_v1.sol";
+import { Ownable_v1 as Ownable } from "../Ownable/Ownable_v1.sol";
+
+/// @title TokenTransferProxy - Transfers tokens on behalf of contracts that have been approved via decentralized governance.
+/// @author Amir Bandeali - <amir@0xProject.com>, Will Warren - <will@0xProject.com>
+contract TokenTransferProxy_v1 is Ownable {
+
+ /// @dev Only authorized addresses can invoke functions with this modifier.
+ modifier onlyAuthorized {
+ require(authorized[msg.sender]);
+ _;
+ }
+
+ modifier targetAuthorized(address target) {
+ require(authorized[target]);
+ _;
+ }
+
+ modifier targetNotAuthorized(address target) {
+ require(!authorized[target]);
+ _;
+ }
+
+ mapping (address => bool) public authorized;
+ address[] public authorities;
+
+ event LogAuthorizedAddressAdded(address indexed target, address indexed caller);
+ event LogAuthorizedAddressRemoved(address indexed target, address indexed caller);
+
+ /*
+ * Public functions
+ */
+
+ /// @dev Authorizes an address.
+ /// @param target Address to authorize.
+ function addAuthorizedAddress(address target)
+ public
+ onlyOwner
+ targetNotAuthorized(target)
+ {
+ authorized[target] = true;
+ authorities.push(target);
+ LogAuthorizedAddressAdded(target, msg.sender);
+ }
+
+ /// @dev Removes authorizion of an address.
+ /// @param target Address to remove authorization from.
+ function removeAuthorizedAddress(address target)
+ public
+ onlyOwner
+ targetAuthorized(target)
+ {
+ delete authorized[target];
+ for (uint i = 0; i < authorities.length; i++) {
+ if (authorities[i] == target) {
+ authorities[i] = authorities[authorities.length - 1];
+ authorities.length -= 1;
+ break;
+ }
+ }
+ LogAuthorizedAddressRemoved(target, msg.sender);
+ }
+
+ /// @dev Calls into ERC20 Token contract, invoking transferFrom.
+ /// @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.
+ /// @return Success of transfer.
+ function transferFrom(
+ address token,
+ address from,
+ address to,
+ uint value)
+ public
+ onlyAuthorized
+ returns (bool)
+ {
+ return Token(token).transferFrom(from, to, value);
+ }
+
+ /*
+ * Public constant functions
+ */
+
+ /// @dev Gets all authorized addresses.
+ /// @return Array of authorized addresses.
+ function getAuthorizedAddresses()
+ public
+ constant
+ returns (address[])
+ {
+ return authorities;
+ }
+}
diff --git a/packages/contracts/src/1.0.0/UnlimitedAllowanceToken/UnlimitedAllowanceToken_v1.sol b/packages/contracts/src/1.0.0/UnlimitedAllowanceToken/UnlimitedAllowanceToken_v1.sol
new file mode 100644
index 000000000..46379c43d
--- /dev/null
+++ b/packages/contracts/src/1.0.0/UnlimitedAllowanceToken/UnlimitedAllowanceToken_v1.sol
@@ -0,0 +1,52 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ 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.11;
+
+import { ERC20Token_v1 as ERC20Token } from "../ERC20Token/ERC20Token_v1.sol";
+
+contract UnlimitedAllowanceToken_v1 is ERC20Token {
+
+ uint constant MAX_UINT = 2**256 - 1;
+
+ /// @dev ERC20 transferFrom, modified such that an allowance of MAX_UINT represents an unlimited allowance.
+ /// @param _from Address to transfer from.
+ /// @param _to Address to transfer to.
+ /// @param _value Amount to transfer.
+ /// @return Success of transfer.
+ function transferFrom(address _from, address _to, uint _value)
+ public
+ returns (bool)
+ {
+ uint allowance = allowed[_from][msg.sender];
+ if (balances[_from] >= _value
+ && allowance >= _value
+ && balances[_to] + _value >= balances[_to]
+ ) {
+ balances[_to] += _value;
+ balances[_from] -= _value;
+ if (allowance < MAX_UINT) {
+ allowed[_from][msg.sender] -= _value;
+ }
+ Transfer(_from, _to, _value);
+ return true;
+ } else {
+ return false;
+ }
+ }
+}