Merge branch 'v2-prototype' of https://github.com/0xProject/0x-monorepo into feature/website/portal-onboarding-polish

This commit is contained in:
fragosti 2018-06-29 13:27:19 -07:00
commit e693629bbd
61 changed files with 1801 additions and 5512 deletions

View File

@ -53,9 +53,9 @@ jobs:
- restore_cache:
keys:
- repo-{{ .Environment.CIRCLE_SHA1 }}
# HACK(albrow): we need to sleep 15 seconds to ensure the devnet is
# HACK(albrow): we need to sleep 10 seconds to ensure the devnet is
# initialized
- run: sleep 15 && TEST_PROVIDER=geth yarn wsrun test contracts
- run: sleep 10 && TEST_PROVIDER=geth yarn wsrun test contracts
test-rest:
docker:
- image: circleci/node:9

View File

@ -39,7 +39,7 @@
"devDependencies": {
"async-child-process": "^1.1.1",
"coveralls": "^3.0.0",
"ganache-cli": "^6.1.0",
"ganache-cli": "6.1.3",
"lcov-result-merger": "^3.0.0",
"lerna": "^2.5.1",
"npm-run-all": "^4.1.2",

View File

@ -60,6 +60,13 @@ contract MixinTransactions is
{
bytes32 schemaHash = EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH;
bytes32 dataHash = keccak256(data);
// Assembly for more efficiently computing:
// keccak256(abi.encode(
// EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH,
// salt,
// signerAddress,
// keccak256(data)
// ));
assembly {
let memPtr := mload(64)
mstore(memPtr, schemaHash)

View File

@ -98,6 +98,21 @@ contract LibOrder is
bytes32 schemaHash = EIP712_ORDER_SCHEMA_HASH;
bytes32 makerAssetDataHash = keccak256(order.makerAssetData);
bytes32 takerAssetDataHash = keccak256(order.takerAssetData);
// Assembly for more efficiently computing:
// keccak256(abi.encode(
// order.makerAddress,
// order.takerAddress,
// order.feeRecipientAddress,
// order.senderAddress,
// order.makerAssetAmount,
// order.takerAssetAmount,
// order.makerFee,
// order.takerFee,
// order.expirationTimeSeconds,
// order.salt,
// keccak256(order.makerAssetData),
// keccak256(order.takerAssetData)
// ));
assembly {
// Backup
let temp1 := mload(sub(order, 32))

View File

@ -47,6 +47,7 @@
"dependencies": {
"@0xproject/subproviders": "^0.10.4",
"@0xproject/types": "^0.8.1",
"@0xproject/utils": "^0.7.1",
"ethereum-types": "^0.0.1",
"@0xproject/typescript-typings": "^0.4.1",
"@0xproject/web3-wrapper": "^0.7.1",

View File

@ -1,3 +1,4 @@
import { logUtils } from '@0xproject/utils';
import { uniqueVersionIds, Web3Wrapper } from '@0xproject/web3-wrapper';
import { includes } from 'lodash';
@ -6,9 +7,17 @@ enum NodeType {
Ganache = 'GANACHE',
}
// HACK(albrow): 🐉 We have to do this so that debug.setHead works correctly.
// (Geth does not seem to like debug.setHead(0), so by sending some transactions
// we increase the current block number beyond 0). Additionally, some tests seem
// to break when there are fewer than 3 blocks in the chain. (We have no idea
// why, but it was consistently reproducible).
const MINIMUM_BLOCKS = 3;
export class BlockchainLifecycle {
private _web3Wrapper: Web3Wrapper;
private _snapshotIdsStack: number[];
private _addresses: string[] = [];
constructor(web3Wrapper: Web3Wrapper) {
this._web3Wrapper = web3Wrapper;
this._snapshotIdsStack = [];
@ -21,7 +30,13 @@ export class BlockchainLifecycle {
this._snapshotIdsStack.push(snapshotId);
break;
case NodeType.Geth:
const blockNumber = await this._web3Wrapper.getBlockNumberAsync();
let blockNumber = await this._web3Wrapper.getBlockNumberAsync();
if (blockNumber < MINIMUM_BLOCKS) {
// If the minimum block number is not met, force Geth to
// mine some blocks by sending some dummy transactions.
await this._mineMinimumBlocksAsync();
blockNumber = await this._web3Wrapper.getBlockNumberAsync();
}
this._snapshotIdsStack.push(blockNumber);
break;
default:
@ -56,4 +71,25 @@ export class BlockchainLifecycle {
throw new Error(`Unknown client version: ${version}`);
}
}
private async _mineMinimumBlocksAsync(): Promise<void> {
logUtils.warn('WARNING: minimum block number for tests not met. Mining additional blocks...');
if (this._addresses.length === 0) {
this._addresses = await this._web3Wrapper.getAvailableAddressesAsync();
if (this._addresses.length === 0) {
throw new Error('No accounts found');
}
}
while ((await this._web3Wrapper.getBlockNumberAsync()) < MINIMUM_BLOCKS) {
logUtils.warn('Mining block...');
await this._web3Wrapper.awaitTransactionMinedAsync(
await this._web3Wrapper.sendTransactionAsync({
from: this._addresses[0],
to: this._addresses[0],
value: '0',
}),
0,
);
}
logUtils.warn('Done mining the minimum number of blocks.');
}
}

View File

@ -3,7 +3,7 @@ set -e
# Create log directory for Geth
mkdir -p /var/log
# Start Geth in background and redirect output to log file
# Start Geth and direct output to stdout
/geth \
--verbosity 5 \
--datadir node0/ \
@ -22,23 +22,4 @@ mkdir -p /var/log
--mine \
--etherbase '0xe8816898d851d5b61b7f950627d04d794c07ca37' \
--unlock '0xe8816898d851d5b61b7f950627d04d794c07ca37,0x5409ed021d9299bf6814279a6a1411a7e866a631,0x6ecbe1db9ef729cbe972c83fb886247691fb6beb,0xe36ea790bc9d7ab70c55260c66d52b1eca985f84,0xe834ec434daba538cd1b9fe1582052b880bd7e63,0x78dc5d2d739606d31509c31d654056a45185ecb6,0xa8dda8d7f5310e4a9e24f8eba77e091ac264f872,0x06cef8e666768cc40cc78cf93d9611019ddcb628,0x4404ac8bd8f9618d27ad2f1485aa1b2cfd82482d,0x7457d5e02197480db681d3fdf256c7aca21bdc12,0x91c987bf62d25945db517bdaa840a6c661374402' \
--password=node0/password.txt \
> /var/log/geth &
# Wait for Geth to unlock the first account
sleep 10
# Send some transactions.
# HACK(albrow): 🐉 We have to do this so that debug.setHead works correctly.
# (Geth does not seem to like debug.setHead(0), so by sending some transactions
# we increase the current block number beyond 0). Additionally, some tests seem
# to break when there are fewer than 3 blocks in the chain. (We have no idea
# why, but it was consistently reproducible).
/geth --datadir node0/ attach --exec 'eth.sendTransaction({"from": "0x5409ED021D9299bf6814279A6A1411A7e866A631", "to": "0x84bd1cfa409cb0bb9b23b8b1a33515b4ac00a0af", "value": "0x1"})'
sleep 3
/geth --datadir node0/ attach --exec 'eth.sendTransaction({"from": "0x5409ED021D9299bf6814279A6A1411A7e866A631", "to": "0x84bd1cfa409cb0bb9b23b8b1a33515b4ac00a0af", "value": "0x1"})'
sleep 3
/geth --datadir node0/ attach --exec 'eth.sendTransaction({"from": "0x5409ED021D9299bf6814279A6A1411A7e866A631", "to": "0x84bd1cfa409cb0bb9b23b8b1a33515b4ac00a0af", "value": "0x1"})'
# Use tail to re-attach to the log file and actually see the output.
tail -f /var/log/geth
--password=node0/password.txt

View File

@ -745,4 +745,4 @@
}
},
"networks": {}
}
}

View File

@ -1,106 +0,0 @@
{
"schemaVersion": "2.0.0",
"contractName": "DummyERC721Receiver",
"compilerOutput": {
"abi": [
{
"constant": false,
"inputs": [
{
"name": "_from",
"type": "address"
},
{
"name": "_tokenId",
"type": "uint256"
},
{
"name": "_data",
"type": "bytes"
}
],
"name": "onERC721Received",
"outputs": [
{
"name": "",
"type": "bytes4"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"name": "from",
"type": "address"
},
{
"indexed": false,
"name": "tokenId",
"type": "uint256"
},
{
"indexed": false,
"name": "data",
"type": "bytes"
}
],
"name": "TokenReceived",
"type": "event"
}
],
"evm": {
"bytecode": {
"linkReferences": {},
"object": "0x608060405234801561001057600080fd5b5061021c806100206000396000f3006080604052600436106100405763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663f0b9e5ba8114610045575b600080fd5b34801561005157600080fd5b50604080516020600460443581810135601f81018490048402850184019095528484526100bb94823573ffffffffffffffffffffffffffffffffffffffff169460248035953695946064949201919081908401838280828437509497506100f09650505050505050565b604080517fffffffff000000000000000000000000000000000000000000000000000000009092168252519081900360200190f35b60007fefe605af9663e0f15f1dd40dad79f119df71e8d2affb3f6857cb9707c6c4b3ea848484604051808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200183815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561018b578181015183820152602001610173565b50505050905090810190601f1680156101b85780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a1507ff0b9e5ba0000000000000000000000000000000000000000000000000000000093925050505600a165627a7a72305820b4f9087d0d09769494b2e03d6db34350ff32a048f73c050c3eda5fa9b2644dae0029",
"opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0x10 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x21C DUP1 PUSH2 0x20 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH2 0x40 JUMPI PUSH4 0xFFFFFFFF PUSH29 0x100000000000000000000000000000000000000000000000000000000 PUSH1 0x0 CALLDATALOAD DIV AND PUSH4 0xF0B9E5BA DUP2 EQ PUSH2 0x45 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x51 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 PUSH1 0x4 PUSH1 0x44 CALLDATALOAD DUP2 DUP2 ADD CALLDATALOAD PUSH1 0x1F DUP2 ADD DUP5 SWAP1 DIV DUP5 MUL DUP6 ADD DUP5 ADD SWAP1 SWAP6 MSTORE DUP5 DUP5 MSTORE PUSH2 0xBB SWAP5 DUP3 CALLDATALOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP5 PUSH1 0x24 DUP1 CALLDATALOAD SWAP6 CALLDATASIZE SWAP6 SWAP5 PUSH1 0x64 SWAP5 SWAP3 ADD SWAP2 SWAP1 DUP2 SWAP1 DUP5 ADD DUP4 DUP3 DUP1 DUP3 DUP5 CALLDATACOPY POP SWAP5 SWAP8 POP PUSH2 0xF0 SWAP7 POP POP POP POP POP POP POP JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 SWAP1 SWAP3 AND DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST PUSH1 0x0 PUSH32 0xEFE605AF9663E0F15F1DD40DAD79F119DF71E8D2AFFB3F6857CB9707C6C4B3EA DUP5 DUP5 DUP5 PUSH1 0x40 MLOAD DUP1 DUP5 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 MSTORE PUSH1 0x20 ADD DUP4 DUP2 MSTORE PUSH1 0x20 ADD DUP1 PUSH1 0x20 ADD DUP3 DUP2 SUB DUP3 MSTORE DUP4 DUP2 DUP2 MLOAD DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP DUP1 MLOAD SWAP1 PUSH1 0x20 ADD SWAP1 DUP1 DUP4 DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x18B JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH2 0x173 JUMP JUMPDEST POP POP POP POP SWAP1 POP SWAP1 DUP2 ADD SWAP1 PUSH1 0x1F AND DUP1 ISZERO PUSH2 0x1B8 JUMPI DUP1 DUP3 SUB DUP1 MLOAD PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB NOT AND DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP JUMPDEST POP SWAP5 POP POP POP POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG1 POP PUSH32 0xF0B9E5BA00000000000000000000000000000000000000000000000000000000 SWAP4 SWAP3 POP POP POP JUMP STOP LOG1 PUSH6 0x627A7A723058 KECCAK256 0xb4 0xf9 ADDMOD PUSH30 0xD09769494B2E03D6DB34350FF32A048F73C050C3EDA5FA9B2644DAE0029 ",
"sourceMap": "1186:1067:0:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;1186:1067:0;;;;;;;"
},
"deployedBytecode": {
"linkReferences": {},
"object": "0x6080604052600436106100405763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663f0b9e5ba8114610045575b600080fd5b34801561005157600080fd5b50604080516020600460443581810135601f81018490048402850184019095528484526100bb94823573ffffffffffffffffffffffffffffffffffffffff169460248035953695946064949201919081908401838280828437509497506100f09650505050505050565b604080517fffffffff000000000000000000000000000000000000000000000000000000009092168252519081900360200190f35b60007fefe605af9663e0f15f1dd40dad79f119df71e8d2affb3f6857cb9707c6c4b3ea848484604051808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200183815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561018b578181015183820152602001610173565b50505050905090810190601f1680156101b85780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a1507ff0b9e5ba0000000000000000000000000000000000000000000000000000000093925050505600a165627a7a72305820b4f9087d0d09769494b2e03d6db34350ff32a048f73c050c3eda5fa9b2644dae0029",
"opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH2 0x40 JUMPI PUSH4 0xFFFFFFFF PUSH29 0x100000000000000000000000000000000000000000000000000000000 PUSH1 0x0 CALLDATALOAD DIV AND PUSH4 0xF0B9E5BA DUP2 EQ PUSH2 0x45 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x51 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 PUSH1 0x4 PUSH1 0x44 CALLDATALOAD DUP2 DUP2 ADD CALLDATALOAD PUSH1 0x1F DUP2 ADD DUP5 SWAP1 DIV DUP5 MUL DUP6 ADD DUP5 ADD SWAP1 SWAP6 MSTORE DUP5 DUP5 MSTORE PUSH2 0xBB SWAP5 DUP3 CALLDATALOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP5 PUSH1 0x24 DUP1 CALLDATALOAD SWAP6 CALLDATASIZE SWAP6 SWAP5 PUSH1 0x64 SWAP5 SWAP3 ADD SWAP2 SWAP1 DUP2 SWAP1 DUP5 ADD DUP4 DUP3 DUP1 DUP3 DUP5 CALLDATACOPY POP SWAP5 SWAP8 POP PUSH2 0xF0 SWAP7 POP POP POP POP POP POP POP JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 SWAP1 SWAP3 AND DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST PUSH1 0x0 PUSH32 0xEFE605AF9663E0F15F1DD40DAD79F119DF71E8D2AFFB3F6857CB9707C6C4B3EA DUP5 DUP5 DUP5 PUSH1 0x40 MLOAD DUP1 DUP5 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 MSTORE PUSH1 0x20 ADD DUP4 DUP2 MSTORE PUSH1 0x20 ADD DUP1 PUSH1 0x20 ADD DUP3 DUP2 SUB DUP3 MSTORE DUP4 DUP2 DUP2 MLOAD DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP DUP1 MLOAD SWAP1 PUSH1 0x20 ADD SWAP1 DUP1 DUP4 DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x18B JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH2 0x173 JUMP JUMPDEST POP POP POP POP SWAP1 POP SWAP1 DUP2 ADD SWAP1 PUSH1 0x1F AND DUP1 ISZERO PUSH2 0x1B8 JUMPI DUP1 DUP3 SUB DUP1 MLOAD PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB NOT AND DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP JUMPDEST POP SWAP5 POP POP POP POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG1 POP PUSH32 0xF0B9E5BA00000000000000000000000000000000000000000000000000000000 SWAP4 SWAP3 POP POP POP JUMP STOP LOG1 PUSH6 0x627A7A723058 KECCAK256 0xb4 0xf9 ADDMOD PUSH30 0xD09769494B2E03D6DB34350FF32A048F73C050C3EDA5FA9B2644DAE0029 ",
"sourceMap": "1186:1067:0:-;;;;;;;;;;;;;;;;;;;;;;;2014:237;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;2014:237:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;2014:237:0;;-1:-1:-1;2014:237:0;;-1:-1:-1;;;;;;;2014:237:0;;;;;;;;;;;;;;;;;;;;;2148:6;2175:37;2189:5;2196:8;2206:5;2175:37;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;2175:37:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;2229:15:0;2014:237;;;;;:::o"
}
}
},
"sources": {
"current/test/DummyERC721Receiver/DummyERC721Receiver.sol": {
"id": 0
},
"current/tokens/ERC721Token/IERC721Receiver.sol": {
"id": 1
}
},
"sourceCodes": {
"current/test/DummyERC721Receiver/DummyERC721Receiver.sol": "/*\nThe MIT License (MIT)\n\nCopyright (c) 2016 Smart Contract Solutions, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be included\nin all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\nOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n*/\n\npragma solidity ^0.4.24;\n\nimport \"../../tokens/ERC721Token/IERC721Receiver.sol\";\n\ncontract DummyERC721Receiver is\n IERC721Receiver\n{\n\n event TokenReceived(\n address from,\n uint256 tokenId,\n bytes data\n );\n\n /**\n * @notice Handle the receipt of an NFT\n * @dev The ERC721 smart contract calls this function on the recipient\n * after a `safetransfer`. This function MAY throw to revert and reject the\n * transfer. This function MUST use 50,000 gas or less. Return of other\n * than the magic value MUST result in the transaction being reverted.\n * Note: the contract address is always the message sender.\n * @param _from The sending address\n * @param _tokenId The NFT identifier which is being transfered\n * @param _data Additional data with no specified format\n * @return `bytes4(keccak256(\"onERC721Received(address,uint256,bytes)\"))`\n */\n function onERC721Received(\n address _from,\n uint256 _tokenId,\n bytes _data\n )\n public\n returns (bytes4)\n {\n emit TokenReceived(_from, _tokenId, _data);\n return ERC721_RECEIVED;\n }\n}\n",
"current/tokens/ERC721Token/IERC721Receiver.sol": "/*\nThe MIT License (MIT)\n\nCopyright (c) 2016 Smart Contract Solutions, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be included\nin all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\nOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n*/\n\npragma solidity ^0.4.24;\n\n/**\n * @title ERC721 token receiver interface\n * @dev Interface for any contract that wants to support safeTransfers\n * rom ERC721 asset contracts.\n * Modified from https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/token/ERC721/ERC721Receiver.sol\n */\ncontract IERC721Receiver {\n /**\n * @dev Magic value to be returned upon successful reception of an NFT\n * Equals to `bytes4(keccak256(\"onERC721Received(address,uint256,bytes)\"))`,\n * which can be also obtained as `ERC721Receiver(0).onERC721Received.selector`\n */\n bytes4 constant ERC721_RECEIVED = 0xf0b9e5ba;\n\n /**\n * @notice Handle the receipt of an NFT\n * @dev The ERC721 smart contract calls this function on the recipient\n * after a `safetransfer`. This function MAY throw to revert and reject the\n * transfer. This function MUST use 50,000 gas or less. Return of other\n * than the magic value MUST result in the transaction being reverted.\n * Note: the contract address is always the message sender.\n * @param _from The sending address\n * @param _tokenId The NFT identifier which is being transfered\n * @param _data Additional data with no specified format\n * @return `bytes4(keccak256(\"onERC721Received(address,uint256,bytes)\"))`\n */\n function onERC721Received(\n address _from,\n uint256 _tokenId,\n bytes _data)\n public\n returns (bytes4);\n}\n"
},
"sourceTreeHashHex": "0x78d041d511725e5b22accff013b3ca3c813fc2804b0af3a247e6932ffd1c23ae",
"compiler": {
"name": "solc",
"version": "soljson-v0.4.24+commit.e67f0147.js",
"settings": {
"optimizer": {
"enabled": true,
"runs": 1000000
},
"outputSelection": {
"*": {
"*": [
"abi",
"evm.bytecode.object",
"evm.bytecode.sourceMap",
"evm.deployedBytecode.object",
"evm.deployedBytecode.sourceMap"
]
}
}
}
},
"networks": {}
}

View File

@ -278,4 +278,4 @@
}
},
"networks": {}
}
}

View File

@ -278,4 +278,4 @@
}
},
"networks": {}
}
}

File diff suppressed because one or more lines are too long

View File

@ -2079,4 +2079,4 @@
}
},
"networks": {}
}
}

File diff suppressed because one or more lines are too long

View File

@ -1,89 +0,0 @@
{
"schemaVersion": "2.0.0",
"contractName": "IAssetData",
"compilerOutput": {
"abi": [
{
"constant": true,
"inputs": [
{
"name": "tokenContract",
"type": "address"
},
{
"name": "tokenId",
"type": "uint256"
},
{
"name": "receiverData",
"type": "bytes"
}
],
"name": "ERC721Token",
"outputs": [],
"payable": false,
"stateMutability": "pure",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "tokenContract",
"type": "address"
}
],
"name": "ERC20Token",
"outputs": [],
"payable": false,
"stateMutability": "pure",
"type": "function"
}
],
"evm": {
"bytecode": {
"linkReferences": {},
"object": "0x",
"opcodes": "",
"sourceMap": ""
},
"deployedBytecode": {
"linkReferences": {},
"object": "0x",
"opcodes": "",
"sourceMap": ""
}
}
},
"sources": {
"current/protocol/AssetProxy/interfaces/IAssetData.sol": {
"id": 0
}
},
"sourceCodes": {
"current/protocol/AssetProxy/interfaces/IAssetData.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity ^0.4.23;\n\n// @dev Interface of the asset proxy's assetData.\n// The asset proxies take an ABI encoded `bytes assetData` as argument.\n// This argument is ABI encoded as one of the methods of this interface.\ninterface IAssetData {\n \n function ERC20Token(\n address tokenContract)\n external pure;\n \n function ERC721Token(\n address tokenContract,\n uint256 tokenId,\n bytes receiverData)\n external pure;\n \n}\n"
},
"sourceTreeHashHex": "0x7fc145f6e88b00700970b3937ba5a88e398a0e66e2abb14f0349209f4d4e49f1",
"compiler": {
"name": "solc",
"version": "soljson-v0.4.24+commit.e67f0147.js",
"settings": {
"optimizer": {
"enabled": true,
"runs": 1000000
},
"outputSelection": {
"*": {
"*": [
"abi",
"evm.bytecode.object",
"evm.bytecode.sourceMap",
"evm.deployedBytecode.object",
"evm.deployedBytecode.sourceMap"
]
}
}
}
},
"networks": {}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,95 +0,0 @@
{
"schemaVersion": "2.0.0",
"contractName": "TestValidator",
"compilerOutput": {
"abi": [
{
"constant": true,
"inputs": [
{
"name": "hash",
"type": "bytes32"
},
{
"name": "signerAddress",
"type": "address"
},
{
"name": "signature",
"type": "bytes"
}
],
"name": "isValidSignature",
"outputs": [
{
"name": "isValid",
"type": "bool"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"name": "validSigner",
"type": "address"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "constructor"
}
],
"evm": {
"bytecode": {
"linkReferences": {},
"object": "0x608060405234801561001057600080fd5b50604051602080610134833981016040525160008054600160a060020a03909216600160a060020a031990921691909117905560e3806100516000396000f300608060405260043610603e5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416639363470281146043575b600080fd5b348015604e57600080fd5b50607e60048035906024803573ffffffffffffffffffffffffffffffffffffffff16916044359182019101356092565b604080519115158252519081900360200190f35b505060005473ffffffffffffffffffffffffffffffffffffffff9081169116149190505600a165627a7a72305820d56af815348fc993337c155cb13340d950e982ab65a940dff32a1d25f78ca9bb0029",
"opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0x10 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x40 MLOAD PUSH1 0x20 DUP1 PUSH2 0x134 DUP4 CODECOPY DUP2 ADD PUSH1 0x40 MSTORE MLOAD PUSH1 0x0 DUP1 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB SWAP1 SWAP3 AND PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB NOT SWAP1 SWAP3 AND SWAP2 SWAP1 SWAP2 OR SWAP1 SSTORE PUSH1 0xE3 DUP1 PUSH2 0x51 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH1 0x3E JUMPI PUSH4 0xFFFFFFFF PUSH29 0x100000000000000000000000000000000000000000000000000000000 PUSH1 0x0 CALLDATALOAD DIV AND PUSH4 0x93634702 DUP2 EQ PUSH1 0x43 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH1 0x4E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x7E PUSH1 0x4 DUP1 CALLDATALOAD SWAP1 PUSH1 0x24 DUP1 CALLDATALOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP2 PUSH1 0x44 CALLDATALOAD SWAP2 DUP3 ADD SWAP2 ADD CALLDATALOAD PUSH1 0x92 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 ISZERO ISZERO DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST POP POP PUSH1 0x0 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 DUP2 AND SWAP2 AND EQ SWAP2 SWAP1 POP JUMP STOP LOG1 PUSH6 0x627A7A723058 KECCAK256 0xd5 PUSH11 0xF815348FC993337C155CB1 CALLER BLOCKHASH 0xd9 POP 0xe9 DUP3 0xab PUSH6 0xA940DFF32A1D 0x25 0xf7 DUP13 0xa9 0xbb STOP 0x29 ",
"sourceMap": "667:864:1:-;;;916:84;8:9:-1;5:2;;;30:1;27;20:12;5:2;916:84:1;;;;;;;;;;;;;967:12;:26;;-1:-1:-1;;;;;967:26:1;;;-1:-1:-1;;;;;;967:26:1;;;;;;;;;667:864;;;;;;"
},
"deployedBytecode": {
"linkReferences": {},
"object": "0x608060405260043610603e5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416639363470281146043575b600080fd5b348015604e57600080fd5b50607e60048035906024803573ffffffffffffffffffffffffffffffffffffffff16916044359182019101356092565b604080519115158252519081900360200190f35b505060005473ffffffffffffffffffffffffffffffffffffffff9081169116149190505600a165627a7a72305820d56af815348fc993337c155cb13340d950e982ab65a940dff32a1d25f78ca9bb0029",
"opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH1 0x3E JUMPI PUSH4 0xFFFFFFFF PUSH29 0x100000000000000000000000000000000000000000000000000000000 PUSH1 0x0 CALLDATALOAD DIV AND PUSH4 0x93634702 DUP2 EQ PUSH1 0x43 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH1 0x4E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x7E PUSH1 0x4 DUP1 CALLDATALOAD SWAP1 PUSH1 0x24 DUP1 CALLDATALOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP2 PUSH1 0x44 CALLDATALOAD SWAP2 DUP3 ADD SWAP2 ADD CALLDATALOAD PUSH1 0x92 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 ISZERO ISZERO DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST POP POP PUSH1 0x0 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 DUP2 AND SWAP2 AND EQ SWAP2 SWAP1 POP JUMP STOP LOG1 PUSH6 0x627A7A723058 KECCAK256 0xd5 PUSH11 0xF815348FC993337C155CB1 CALLER BLOCKHASH 0xd9 POP 0xe9 DUP3 0xab PUSH6 0xA940DFF32A1D 0x25 0xf7 DUP13 0xa9 0xbb STOP 0x29 ",
"sourceMap": "667:864:1:-;;;;;;;;;;;;;;;;;;;;;;;1299:230;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1299:230:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;1456:12:1;1509;;;;;1492:29;;;;1299:230;-1:-1:-1;1299:230:1:o"
}
}
},
"sources": {
"current/protocol/Exchange/interfaces/IValidator.sol": {
"id": 0
},
"current/test/TestValidator/TestValidator.sol": {
"id": 1
}
},
"sourceCodes": {
"current/protocol/Exchange/interfaces/IValidator.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity ^0.4.23;\n\ncontract IValidator {\n\n /// @dev Verifies that a signature is valid.\n /// @param hash Message hash that is signed.\n /// @param signerAddress Address that should have signed the given hash.\n /// @param signature Proof of signing.\n /// @return Validity of order signature.\n function isValidSignature(\n bytes32 hash,\n address signerAddress,\n bytes signature\n )\n external\n view\n returns (bool isValid);\n}\n",
"current/test/TestValidator/TestValidator.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity ^0.4.24;\n\nimport \"../../protocol/Exchange/interfaces/IValidator.sol\";\n\ncontract TestValidator is \n IValidator\n{\n\n // The single valid signer for this wallet.\n address VALID_SIGNER;\n\n /// @dev constructs a new `TestValidator` with a single valid signer.\n /// @param validSigner The sole, valid signer.\n constructor (address validSigner) public {\n VALID_SIGNER = validSigner;\n }\n\n /// @dev Verifies that a signature is valid. `signer` must match `VALID_SIGNER`.\n /// @param hash Message hash that is signed.\n /// @param signerAddress Address that should have signed the given hash.\n /// @param signature Proof of signing.\n /// @return Validity of signature.\n function isValidSignature(\n bytes32 hash,\n address signerAddress,\n bytes signature\n )\n external\n view\n returns (bool isValid)\n {\n return (signerAddress == VALID_SIGNER);\n }\n}\n"
},
"sourceTreeHashHex": "0x46c9bcd1eaeb97a053a035d7aa8867b98dd8dc7a9faf39d5e2fa41e911c728ee",
"compiler": {
"name": "solc",
"version": "soljson-v0.4.24+commit.e67f0147.js",
"settings": {
"optimizer": {
"enabled": true,
"runs": 1000000
},
"outputSelection": {
"*": {
"*": [
"abi",
"evm.bytecode.object",
"evm.bytecode.sourceMap",
"evm.deployedBytecode.object",
"evm.deployedBytecode.sourceMap"
]
}
}
}
},
"networks": {}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -327,4 +327,4 @@
}
},
"networks": {}
}
}

File diff suppressed because one or more lines are too long

View File

@ -283,4 +283,4 @@
}
},
"networks": {}
}
}

View File

@ -1,6 +1,10 @@
{
"contractsDir": "../contracts/src/contracts",
"compilerSettings": {
"optimizer": {
"enabled": true,
"runs": 1000000
},
"outputSelection": {
"*": {
"*": [

View File

@ -24,7 +24,7 @@
"generate_contract_wrappers:v2":
"abi-gen --abis ${npm_package_config_abis_v2} --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output src/v2/contract_wrappers --backend ethers",
"compile:v1": "sol-compiler --artifacts-dir artifacts/1.0.0 --contracts Exchange_v1,DummyERC20Token,ZRXToken,WETH9,TokenTransferProxy_v1,MultiSigWallet,MultiSigWalletWithTimeLock,MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress,TokenRegistry",
"compile:v2": "sol-compiler --artifacts-dir artifacts/2.0.0 --contracts ERC20Token,DummyERC20Token,ERC721Token,DummyERC721Token,ERC20Proxy,ERC721Proxy,Exchange,MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress,ZRXToken,WETH9,IWallet,IValidator"
"compile:v2": "sol-compiler --artifacts-dir artifacts/2.0.0 --contracts AssetProxyOwner,ERC20Token,DummyERC20Token,ERC721Token,DummyERC721Token,ERC20Proxy,ERC721Proxy,Exchange,MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress,ZRXToken,WETH9,IWallet,IValidator"
},
"config": {
"abis": {
@ -50,6 +50,7 @@
},
"dependencies": {
"@0xproject/base-contract": "^0.3.4",
"@0xproject/order-utils": "^1.0.0",
"@0xproject/sol-compiler": "^0.5.2",
"@0xproject/typescript-typings": "^0.4.1",
"@0xproject/utils": "^0.7.1",

View File

@ -1,3 +1,4 @@
import { assetProxyUtils, constants } from '@0xproject/order-utils';
import { BigNumber } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import { Provider, TxData } from 'ethereum-types';
@ -51,7 +52,7 @@ export const runV2MigrationsAsync = async (provider: Provider, artifactsDir: str
artifacts.Exchange,
provider,
txDefaults,
zrxToken.address,
assetProxyUtils.encodeERC20AssetData(zrxToken.address),
);
artifactsWriter.saveArtifact(exchange);
@ -74,16 +75,37 @@ export const runV2MigrationsAsync = async (provider: Provider, artifactsDir: str
);
artifactsWriter.saveArtifact(assetProxyOwner);
await web3Wrapper.awaitTransactionSuccessAsync(
await erc20proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { from: owner }),
await erc20proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, {
from: owner,
}),
);
await web3Wrapper.awaitTransactionSuccessAsync(
await erc20proxy.transferOwnership.sendTransactionAsync(assetProxyOwner.address, { from: owner }),
await erc20proxy.transferOwnership.sendTransactionAsync(assetProxyOwner.address, {
from: owner,
}),
);
await web3Wrapper.awaitTransactionSuccessAsync(
await erc721proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { from: owner }),
await erc721proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, {
from: owner,
}),
);
await web3Wrapper.awaitTransactionSuccessAsync(
await erc721proxy.transferOwnership.sendTransactionAsync(assetProxyOwner.address, { from: owner }),
await erc721proxy.transferOwnership.sendTransactionAsync(assetProxyOwner.address, {
from: owner,
}),
);
// Register the Asset Proxies to the Exchange
// HACK: These are exposed in the types package but migrations currently uses an older version
// but we can pull the asset data id from the proxies
const erc20ProxyId = await erc20proxy.getProxyId.callAsync();
const erc721ProxyId = await erc721proxy.getProxyId.callAsync();
const oldAddress = constants.NULL_ADDRESS;
await web3Wrapper.awaitTransactionSuccessAsync(
await exchange.registerAssetProxy.sendTransactionAsync(erc20ProxyId, erc20proxy.address, oldAddress),
);
await web3Wrapper.awaitTransactionSuccessAsync(
await exchange.registerAssetProxy.sendTransactionAsync(erc721ProxyId, erc721proxy.address, oldAddress),
);
// Dummy ERC20 tokens

View File

@ -52,7 +52,6 @@
"homepage": "https://github.com/0xProject/0x-monorepo/packages/order-utils/README.md",
"devDependencies": {
"@0xproject/dev-utils": "^0.4.4",
"@0xproject/migrations": "^0.0.8",
"@0xproject/monorepo-scripts": "^0.2.1",
"@0xproject/tslint-config": "^0.4.20",
"@types/ethereumjs-abi": "^0.6.0",

View File

@ -15,7 +15,7 @@ export class GanacheSubprovider extends Subprovider {
* Instantiates a GanacheSubprovider
* @param opts The desired opts with which to instantiate the Ganache provider
*/
constructor(opts: any) {
constructor(opts: Ganache.GanacheOpts) {
super();
this._ganacheProvider = Ganache.provider(opts);
}

View File

@ -7,7 +7,9 @@ declare module 'ganache-core' {
};
port?: number;
network_id?: number;
networkId?: number;
mnemonic?: string;
gasLimit?: number;
}
// tslint:disable-next-line:completed-docs
export function provider(opts: GanacheOpts): Provider;

View File

@ -48,10 +48,10 @@ const styles: Styles = {
width: 25,
},
offTrackStyle: {
backgroundColor: colors.allowanceToggleOffTrack,
backgroundColor: colors.grey300,
},
onTrackStyle: {
backgroundColor: colors.allowanceToggleOnTrack,
backgroundColor: colors.mediumBlue,
},
};

View File

@ -53,8 +53,14 @@ class PlainPortalOnboardingFlow extends React.Component<PortalOnboardingFlowProp
public componentWillUnmount(): void {
this._unlisten();
}
public componentDidUpdate(): void {
public componentDidUpdate(prevProps: PortalOnboardingFlowProps): void {
this._adjustStepIfShould();
if (!prevProps.isRunning && this.props.isRunning) {
// On mobile, make sure the wallet is completely visible.
if (this.props.screenWidth === ScreenWidths.Sm) {
document.querySelector('.wallet').scrollIntoView();
}
}
}
public render(): React.ReactNode {
return (

View File

@ -44,12 +44,13 @@ export const DrawerMenu = (props: DrawerMenuProps) => {
iconName: 'zmdi-portable-wifi',
};
const menuItemEntries = _.concat(relayerItemEntry, defaultMenuItemEntries);
const displayMessage = utils.getReadableAccountState(
const accountState = utils.getAccountState(
props.blockchainIsLoaded && !_.isUndefined(props.blockchain),
props.providerType,
props.injectedProviderName,
props.userAddress,
);
const displayMessage = utils.getReadableAccountState(accountState, props.userAddress);
return (
<div style={styles.root}>
<Header userAddress={props.userAddress} displayMessage={displayMessage} />

View File

@ -1,10 +1,10 @@
import { colors, constants as sharedConstants } from '@0xproject/react-shared';
import { BigNumber } from '@0xproject/utils';
import * as _ from 'lodash';
import ActionAccountBalanceWallet from 'material-ui/svg-icons/action/account-balance-wallet';
import Help from 'material-ui/svg-icons/action/help';
import * as React from 'react';
import * as DocumentTitle from 'react-document-title';
import { Route, RouteComponentProps, Switch } from 'react-router-dom';
import { Link, Route, RouteComponentProps, Switch } from 'react-router-dom';
import { Blockchain } from 'ts/blockchain';
import { BlockchainErrDialog } from 'ts/components/dialogs/blockchain_err_dialog';
@ -24,7 +24,6 @@ import { TopBar, TopBarDisplayType } from 'ts/components/top_bar/top_bar';
import { TradeHistory } from 'ts/components/trade_history/trade_history';
import { Container } from 'ts/components/ui/container';
import { FlashMessage } from 'ts/components/ui/flash_message';
import { Island } from 'ts/components/ui/island';
import { Text } from 'ts/components/ui/text';
import { Wallet } from 'ts/components/wallet/wallet';
import { GenerateOrderForm } from 'ts/containers/generate_order_form';
@ -319,15 +318,14 @@ export class Portal extends React.Component<PortalProps, PortalState> {
);
}
private _renderWallet(): React.ReactNode {
const startOnboarding = this._renderStartOnboarding();
const isMobile = utils.isMobile(this.props.screenWidth);
// We need room to scroll down for mobile onboarding
const marginBottom = isMobile ? '200px' : '15px';
return (
<div>
<Container>
{isMobile && <Container marginBottom="15px">{startOnboarding}</Container>}
<Container marginBottom={marginBottom}>
<Container className="flex flex-column items-center">
{isMobile && <Container marginBottom="20px">{this._renderStartOnboarding()}</Container>}
<Container marginBottom={marginBottom} width="100%">
<Wallet
style={
!isMobile && this.props.isPortalOnboardingShowing
@ -355,7 +353,7 @@ export class Portal extends React.Component<PortalProps, PortalState> {
refetchTokenStateAsync={this._refetchTokenStateAsync.bind(this)}
/>
</Container>
{!isMobile && <Container marginTop="15px">{startOnboarding}</Container>}
{!isMobile && <Container marginTop="8px">{this._renderStartOnboarding()}</Container>}
</Container>
<PortalOnboardingFlow
blockchain={this._blockchain}
@ -366,26 +364,27 @@ export class Portal extends React.Component<PortalProps, PortalState> {
);
}
private _renderStartOnboarding(): React.ReactNode {
return (
<Island>
<Container
marginTop="30px"
marginBottom="30px"
marginLeft="30px"
marginRight="30px"
className="flex justify-around items-center"
>
<ActionAccountBalanceWallet style={{ width: '30px', height: '30px' }} color={colors.orange} />
<Text
fontColor={colors.grey}
fontSize="16px"
center={true}
onClick={this._startOnboarding.bind(this)}
>
const isMobile = utils.isMobile(this.props.screenWidth);
const shouldStartOnboarding = !isMobile || this.props.location.pathname === `${WebsitePaths.Portal}/account`;
const startOnboarding = (
<Container className="flex items-center center">
<Help style={{ width: '20px', height: '20px' }} color={colors.mediumBlue} />
<Container marginLeft="8px">
<Text fontColor={colors.mediumBlue} fontSize="16px" onClick={this._startOnboarding.bind(this)}>
Learn how to set up your account
</Text>
</Container>
</Island>
</Container>
);
return !shouldStartOnboarding ? (
<Link
to={{ pathname: `${WebsitePaths.Portal}/account`, state: { startOnboarding: true } }}
style={{ textDecoration: 'none' }}
>
{startOnboarding}
</Link>
) : (
startOnboarding
);
}
@ -393,10 +392,6 @@ export class Portal extends React.Component<PortalProps, PortalState> {
const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId];
analytics.logEvent('Portal', 'Onboarding Started - Manual', networkName, this.props.portalOnboardingStep);
this.props.dispatcher.updatePortalOnboardingShowing(true);
// On mobile, make sure the wallet is completely visible.
if (this.props.screenWidth === ScreenWidths.Sm) {
document.querySelector('.wallet').scrollIntoView();
}
}
private _renderWalletSection(): React.ReactNode {
return <Section header={<TextHeader labelText="Your Account" />} body={this._renderWallet()} />;
@ -535,11 +530,15 @@ export class Portal extends React.Component<PortalProps, PortalState> {
);
}
private _renderRelayerIndexSection(): React.ReactNode {
return <Section header={<TextHeader labelText="0x Relayers" />} body={this._renderRelayerIndex()} />;
}
private _renderRelayerIndex(): React.ReactNode {
const isMobile = utils.isMobile(this.props.screenWidth);
return (
<Section
header={<TextHeader labelText="0x Relayers" />}
body={<RelayerIndex networkId={this.props.networkId} screenWidth={this.props.screenWidth} />}
/>
<Container className="flex flex-column items-center">
{isMobile && <Container marginBottom="20px">{this._renderStartOnboarding()}</Container>}
<RelayerIndex networkId={this.props.networkId} screenWidth={this.props.screenWidth} />
</Container>
);
}
private _renderNotFoundMessage(): React.ReactNode {

View File

@ -6,9 +6,9 @@ export interface SectionProps {
}
export const Section = (props: SectionProps) => {
return (
<div className="flex flex-column" style={{ height: '100%' }}>
<div className="flex flex-column">
{props.header}
<div className="flex-auto">{props.body}</div>
{props.body}
</div>
);
};

View File

@ -2,19 +2,21 @@ import { Styles } from '@0xproject/react-shared';
import * as _ from 'lodash';
import CircularProgress from 'material-ui/CircularProgress';
import RaisedButton from 'material-ui/RaisedButton';
import ActionAccountBalanceWallet from 'material-ui/svg-icons/action/account-balance-wallet';
import Lock from 'material-ui/svg-icons/action/lock';
import * as React from 'react';
import { Blockchain } from 'ts/blockchain';
import { ProviderPicker } from 'ts/components/top_bar/provider_picker';
import { AccountConnection } from 'ts/components/ui/account_connection';
import { Container } from 'ts/components/ui/container';
import { DropDown } from 'ts/components/ui/drop_down';
import { Identicon } from 'ts/components/ui/identicon';
import { Image } from 'ts/components/ui/image';
import { Island } from 'ts/components/ui/island';
import { Text } from 'ts/components/ui/text';
import { Dispatcher } from 'ts/redux/dispatcher';
import { colors } from 'ts/style/colors';
import { ProviderType } from 'ts/types';
import { AccountState, ProviderType } from 'ts/types';
import { constants } from 'ts/utils/constants';
import { utils } from 'ts/utils/utils';
@ -46,37 +48,13 @@ export class ProviderDisplay extends React.Component<ProviderDisplayProps, Provi
this.props.providerType,
this.props.injectedProviderName,
);
const displayMessage = utils.getReadableAccountState(
this._isBlockchainReady(),
this.props.providerType,
this.props.injectedProviderName,
this.props.userAddress,
);
// If the "injected" provider is our fallback public node, then we want to
// show the "connect a wallet" message instead of the providerName
const injectedProviderName = isExternallyInjectedProvider
? this.props.injectedProviderName
: 'Connect a wallet';
const providerTitle =
this.props.providerType === ProviderType.Injected ? injectedProviderName : 'Ledger Nano S';
const isProviderMetamask = providerTitle === constants.PROVIDER_NAME_METAMASK;
const hoverActiveNode = (
<Island className="flex items-center p1" style={styles.root}>
<div>
{this._isBlockchainReady() ? (
<Identicon address={this.props.userAddress} diameter={ROOT_HEIGHT} />
) : (
<CircularProgress size={ROOT_HEIGHT} thickness={2} />
)}
</div>
<Island className="flex items-center py1 px2" style={styles.root}>
{this._renderIcon()}
<Container marginLeft="12px" marginRight="12px">
<Text fontSize="14px" fontColor={colors.darkGrey}>
{displayMessage}
</Text>
{this._renderDisplayMessage()}
</Container>
{isProviderMetamask && (
<Image src="/images/metamask_icon.png" height={ROOT_HEIGHT} width={ROOT_HEIGHT} />
)}
{this._renderInjectedProvider()}
</Island>
);
const hasLedgerProvider = this.props.providerType === ProviderType.Ledger;
@ -168,7 +146,69 @@ export class ProviderDisplay extends React.Component<ProviderDisplayProps, Provi
);
}
}
private _renderIcon(): React.ReactNode {
const accountState = this._getAccountState();
switch (accountState) {
case AccountState.Ready:
return <Identicon address={this.props.userAddress} diameter={ROOT_HEIGHT} />;
case AccountState.Loading:
return <CircularProgress size={ROOT_HEIGHT} thickness={2} />;
case AccountState.Locked:
return <Lock color={colors.black} />;
case AccountState.Disconnected:
return <ActionAccountBalanceWallet color={colors.mediumBlue} />;
default:
return null;
}
}
private _renderDisplayMessage(): React.ReactNode {
const accountState = this._getAccountState();
const displayMessage = utils.getReadableAccountState(accountState, this.props.userAddress);
const fontColor = this._getDisplayMessageFontColor();
return (
<Text fontSize="16px" fontColor={fontColor} fontWeight={500}>
{displayMessage}
</Text>
);
}
private _getDisplayMessageFontColor(): string {
const accountState = this._getAccountState();
switch (accountState) {
case AccountState.Loading:
return colors.darkGrey;
case AccountState.Ready:
case AccountState.Locked:
case AccountState.Disconnected:
default:
return colors.black;
}
}
private _renderInjectedProvider(): React.ReactNode {
const accountState = this._getAccountState();
switch (accountState) {
case AccountState.Ready:
case AccountState.Locked:
return (
<AccountConnection
accountState={accountState}
injectedProviderName={this.props.injectedProviderName}
/>
);
case AccountState.Disconnected:
case AccountState.Loading:
default:
return null;
}
}
private _isBlockchainReady(): boolean {
return this.props.blockchainIsLoaded && !_.isUndefined(this.props.blockchain);
}
private _getAccountState(): AccountState {
return utils.getAccountState(
this._isBlockchainReady(),
this.props.providerType,
this.props.injectedProviderName,
this.props.userAddress,
);
}
}

View File

@ -0,0 +1,40 @@
import * as React from 'react';
import { Circle } from 'ts/components/ui/circle';
import { Container } from 'ts/components/ui/container';
import { Text } from 'ts/components/ui/text';
import { colors } from 'ts/style/colors';
import { AccountState } from 'ts/types';
export interface AccountConnectionProps {
accountState: AccountState;
injectedProviderName: string;
}
export const AccountConnection: React.StatelessComponent<AccountConnectionProps> = ({
accountState,
injectedProviderName,
}) => {
return (
<Container className="flex items-center">
<Circle diameter={6} fillColor={getInjectedProviderColor(accountState)} />
<Container marginLeft="6px">
<Text fontSize="12px" lineHeight="14px" fontColor={colors.darkGrey}>
{injectedProviderName}
</Text>
</Container>
</Container>
);
};
const getInjectedProviderColor = (accountState: AccountState) => {
switch (accountState) {
case AccountState.Ready:
return colors.limeGreen;
case AccountState.Locked:
case AccountState.Loading:
case AccountState.Disconnected:
default:
return colors.red;
}
};

View File

@ -0,0 +1,16 @@
import * as React from 'react';
export interface CircleProps {
className?: string;
diameter: number;
fillColor: string;
}
export const Circle: React.StatelessComponent<CircleProps> = ({ className, diameter, fillColor }) => {
const radius = diameter / 2;
return (
<svg className={className} height={diameter} width={diameter}>
<circle cx={radius} cy={radius} r={radius} fill={fillColor} />
</svg>
);
};

View File

@ -14,7 +14,9 @@ export interface ContainerProps {
backgroundColor?: string;
borderRadius?: StringOrNum;
maxWidth?: StringOrNum;
maxHeight?: StringOrNum;
width?: StringOrNum;
height?: StringOrNum;
minHeight?: StringOrNum;
isHidden?: boolean;
className?: string;

View File

@ -2,6 +2,7 @@ import blockies = require('blockies');
import * as _ from 'lodash';
import * as React from 'react';
import { Circle } from 'ts/components/ui/circle';
import { Image } from 'ts/components/ui/image';
import { colors } from 'ts/style/colors';
@ -20,7 +21,6 @@ export class Identicon extends React.Component<IdenticonProps, IdenticonState> {
public render(): React.ReactNode {
const address = this.props.address;
const diameter = this.props.diameter;
const radius = diameter / 2;
return (
<div
className="circle relative transitionFix"
@ -40,9 +40,7 @@ export class Identicon extends React.Component<IdenticonProps, IdenticonState> {
width={diameter}
/>
) : (
<svg height={diameter} width={diameter}>
<circle cx={radius} cy={radius} r={radius} fill={colors.grey200} />
</svg>
<Circle diameter={diameter} fillColor={colors.grey200} />
)}
</div>
);

View File

@ -15,6 +15,7 @@ export interface TextProps {
minHeight?: string;
center?: boolean;
fontWeight?: number | string;
textDecorationLine?: string;
onClick?: () => void;
}
@ -28,6 +29,7 @@ export const Text = styled(PlainText)`
font-family: ${props => props.fontFamily};
font-weight: ${props => props.fontWeight};
font-size: ${props => props.fontSize};
text-decoration-line: ${props => props.textDecorationLine};
${props => (props.lineHeight ? `line-height: ${props.lineHeight}` : '')};
${props => (props.center ? 'text-align: center' : '')};
color: ${props => props.fontColor};
@ -45,6 +47,7 @@ Text.defaultProps = {
fontColor: colors.black,
fontSize: '15px',
lineHeight: '1.5em',
textDecorationLine: 'none',
Tag: 'div',
};

View File

@ -0,0 +1,146 @@
import * as _ from 'lodash';
import * as React from 'react';
import { Blockchain } from 'ts/blockchain';
import { Container } from 'ts/components/ui/container';
import { Image } from 'ts/components/ui/image';
import { Island } from 'ts/components/ui/island';
import { Text } from 'ts/components/ui/text';
import { Dispatcher } from 'ts/redux/dispatcher';
import { colors } from 'ts/style/colors';
import { styled } from 'ts/style/theme';
import { AccountState, BrowserType, ProviderType } from 'ts/types';
import { constants } from 'ts/utils/constants';
import { utils } from 'ts/utils/utils';
const METAMASK_IMG_SRC = '/images/metamask_icon.png';
export interface BodyOverlayProps {
dispatcher: Dispatcher;
userAddress: string;
injectedProviderName: string;
providerType: ProviderType;
onToggleLedgerDialog: () => void;
blockchain?: Blockchain;
blockchainIsLoaded: boolean;
}
interface BodyOverlayState {}
export class BodyOverlay extends React.Component<BodyOverlayProps, BodyOverlayState> {
public render(): React.ReactNode {
const accountState = this._getAccountState();
switch (accountState) {
case AccountState.Locked:
return <LockedOverlay onUseDifferentWalletClicked={this.props.onToggleLedgerDialog} />;
case AccountState.Disconnected:
return <DisconnectedOverlay onUseDifferentWalletClicked={this.props.onToggleLedgerDialog} />;
case AccountState.Ready:
case AccountState.Loading:
default:
return null;
}
}
private _isBlockchainReady(): boolean {
return this.props.blockchainIsLoaded && !_.isUndefined(this.props.blockchain);
}
private _getAccountState(): AccountState {
return utils.getAccountState(
this._isBlockchainReady(),
this.props.providerType,
this.props.injectedProviderName,
this.props.userAddress,
);
}
}
interface LockedOverlayProps {
className?: string;
onUseDifferentWalletClicked?: () => void;
}
const PlainLockedOverlay: React.StatelessComponent<LockedOverlayProps> = ({
className,
onUseDifferentWalletClicked,
}) => (
<div className={className}>
<Container
className="flex flex-column items-center"
marginBottom="24px"
marginTop="24px"
marginLeft="48px"
marginRight="48px"
>
<Image src={METAMASK_IMG_SRC} height="70px" />
<Container marginTop="12px">
<Text fontColor={colors.metaMaskOrange} fontSize="16px" fontWeight="bold">
Please Unlock MetaMask
</Text>
</Container>
<UseDifferentWallet fontColor={colors.darkGrey} onClick={onUseDifferentWalletClicked} />
</Container>
</div>
);
const LockedOverlay = styled(PlainLockedOverlay)`
background: ${colors.metaMaskTransparentOrange};
border: 1px solid ${colors.metaMaskOrange};
border-radius: 10px;
`;
interface DisconnectedOverlayProps {
onUseDifferentWalletClicked?: () => void;
}
const DisconnectedOverlay = (props: DisconnectedOverlayProps) => {
return (
<div className="flex flex-column items-center">
<GetMetaMask />
<UseDifferentWallet fontColor={colors.mediumBlue} onClick={props.onUseDifferentWalletClicked} />
</div>
);
};
interface UseDifferentWallet {
fontColor: string;
onClick?: () => void;
}
const UseDifferentWallet = (props: UseDifferentWallet) => {
return (
<Container marginTop="12px">
<Text fontColor={props.fontColor} fontSize="16px" textDecorationLine="underline" onClick={props.onClick}>
Use a different wallet
</Text>
</Container>
);
};
const GetMetaMask = () => {
const browserType = utils.getBrowserType();
let extensionLink;
switch (browserType) {
case BrowserType.Chrome:
extensionLink = constants.URL_METAMASK_CHROME_STORE;
break;
case BrowserType.Firefox:
extensionLink = constants.URL_METAMASK_FIREFOX_STORE;
break;
case BrowserType.Opera:
extensionLink = constants.URL_METAMASK_OPERA_STORE;
break;
default:
extensionLink = constants.URL_METAMASK_HOMEPAGE;
}
return (
<a href={extensionLink} target="_blank" style={{ textDecoration: 'none' }}>
<Island
className="flex items-center py1 px2"
style={{ height: 28, borderRadius: 28, backgroundColor: colors.mediumBlue }}
>
<Image src={METAMASK_IMG_SRC} width="28px" />
<Container marginLeft="8px" marginRight="12px">
<Text fontColor={colors.white} fontSize="16px" fontWeight={500}>
Get MetaMask Wallet
</Text>
</Container>
</Island>
</a>
);
};

View File

@ -0,0 +1,41 @@
import * as React from 'react';
import { Circle } from 'ts/components/ui/circle';
import { Container } from 'ts/components/ui/container';
import { Text } from 'ts/components/ui/text';
import { PlaceHolder } from 'ts/components/wallet/placeholder';
import { StandardIconRow } from 'ts/components/wallet/standard_icon_row';
import { colors } from 'ts/style/colors';
export interface NullTokenRowProps {
iconDimension: number;
fillColor: string;
}
export const NullTokenRow: React.StatelessComponent<NullTokenRowProps> = ({ iconDimension, fillColor }) => {
const icon = <Circle diameter={iconDimension} fillColor={fillColor} />;
const main = (
<div className="flex flex-column">
<PlaceHolder hideChildren={true} fillColor={fillColor}>
<Text fontSize="16px" fontWeight="bold" lineHeight="1em">
0.00 XXX
</Text>
</PlaceHolder>
<Container marginTop="3px">
<PlaceHolder hideChildren={true} fillColor={fillColor}>
<Text fontSize="14px" fontColor={colors.darkGrey} lineHeight="1em">
$0.00
</Text>
</PlaceHolder>
</Container>
</div>
);
const accessory = (
<Container marginRight="12px">
<PlaceHolder hideChildren={true} fillColor={fillColor}>
<Container width="20px" height="14px" />
</PlaceHolder>
</Container>
);
return <StandardIconRow icon={icon} main={main} accessory={accessory} />;
};

View File

@ -0,0 +1,25 @@
import * as React from 'react';
import { styled } from 'ts/style/theme';
export interface PlaceHolderProps {
className?: string;
hideChildren: React.ReactNode;
fillColor: string;
}
const PlainPlaceHolder: React.StatelessComponent<PlaceHolderProps> = ({ className, hideChildren, children }) => {
const childrenVisibility = hideChildren ? 'hidden' : 'visible';
const childrenStyle: React.CSSProperties = { visibility: childrenVisibility };
return (
<div className={className}>
<div style={childrenStyle}>{children}</div>
</div>
);
};
export const PlaceHolder = styled(PlainPlaceHolder)`
background-color: ${props => (props.hideChildren ? props.fillColor : 'transparent')};
display: inline-block;
border-radius: 2px;
`;

View File

@ -0,0 +1,44 @@
import * as React from 'react';
import { colors } from 'ts/style/colors';
import { styled } from 'ts/style/theme';
export interface StandardIconRowProps {
className?: string;
icon: React.ReactNode;
main: React.ReactNode;
accessory?: React.ReactNode;
minHeight?: string;
borderBottomColor?: string;
borderBottomStyle?: string;
borderWidth?: string;
backgroundColor?: string;
}
const PlainStandardIconRow: React.StatelessComponent<StandardIconRowProps> = ({ className, icon, main, accessory }) => {
return (
<div className={`flex items-center ${className}`}>
<div className="flex items-center px2">{icon}</div>
<div className="flex-none pr2">{main}</div>
<div className="flex-auto" />
<div>{accessory}</div>
</div>
);
};
export const StandardIconRow = styled(PlainStandardIconRow)`
min-height: ${props => props.minHeight};
border-bottom-color: ${props => props.borderBottomColor};
border-bottom-style: ${props => props.borderBottomStyle};
border-width: ${props => props.borderWidth};
background-color: ${props => props.backgroundColor};
`;
StandardIconRow.defaultProps = {
minHeight: '85px',
borderBottomColor: colors.walletBorder,
borderBottomStyle: 'solid',
borderWidth: '1px',
backgroundColor: colors.walletDefaultItemBackground,
};
StandardIconRow.displayName = 'StandardIconRow';

View File

@ -1,36 +1,31 @@
import {
constants as sharedConstants,
EtherscanLinkSuffixes,
Styles,
utils as sharedUtils,
} from '@0xproject/react-shared';
import { constants as sharedConstants, EtherscanLinkSuffixes, utils as sharedUtils } from '@0xproject/react-shared';
import { BigNumber, errorUtils } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as _ from 'lodash';
import CircularProgress from 'material-ui/CircularProgress';
import FloatingActionButton from 'material-ui/FloatingActionButton';
import { ListItem } from 'material-ui/List';
import ActionAccountBalanceWallet from 'material-ui/svg-icons/action/account-balance-wallet';
import ContentAdd from 'material-ui/svg-icons/content/add';
import ContentRemove from 'material-ui/svg-icons/content/remove';
import * as React from 'react';
import { Link } from 'react-router-dom';
import firstBy = require('thenby');
import { Blockchain } from 'ts/blockchain';
import { AccountConnection } from 'ts/components/ui/account_connection';
import { Container } from 'ts/components/ui/container';
import { IconButton } from 'ts/components/ui/icon_button';
import { Identicon } from 'ts/components/ui/identicon';
import { Island } from 'ts/components/ui/island';
import { Text } from 'ts/components/ui/text';
import { TokenIcon } from 'ts/components/ui/token_icon';
import { WalletDisconnectedItem } from 'ts/components/wallet/wallet_disconnected_item';
import { BodyOverlay } from 'ts/components/wallet/body_overlay';
import { NullTokenRow } from 'ts/components/wallet/null_token_row';
import { PlaceHolder } from 'ts/components/wallet/placeholder';
import { StandardIconRow } from 'ts/components/wallet/standard_icon_row';
import { WrapEtherItem } from 'ts/components/wallet/wrap_ether_item';
import { AllowanceToggle } from 'ts/containers/inputs/allowance_toggle';
import { Dispatcher } from 'ts/redux/dispatcher';
import { colors } from 'ts/style/colors';
import { styled } from 'ts/style/theme';
import {
AccountState,
BlockchainErrs,
ProviderType,
ScreenWidths,
@ -44,7 +39,6 @@ import {
import { analytics } from 'ts/utils/analytics';
import { constants } from 'ts/utils/constants';
import { utils } from 'ts/utils/utils';
import { styles as walletItemStyles } from 'ts/utils/wallet_item_styles';
export interface WalletProps {
userAddress: string;
@ -84,66 +78,16 @@ interface AccessoryItemConfig {
allowanceToggleConfig?: AllowanceToggleConfig;
}
const styles: Styles = {
root: {
width: '100%',
},
footerItemInnerDiv: {
paddingLeft: 24,
borderTopColor: colors.walletBorder,
borderTopStyle: 'solid',
borderWidth: 1,
},
borderedItem: {
borderBottomColor: colors.walletBorder,
borderBottomStyle: 'solid',
borderWidth: 1,
},
tokenItem: {
backgroundColor: colors.walletDefaultItemBackground,
minHeight: 85,
},
amountLabel: {
fontWeight: 'bold',
color: colors.black,
},
valueLabel: {
color: colors.grey,
fontSize: 14,
},
paddedItem: {
paddingTop: 8,
paddingBottom: 8,
},
bodyInnerDiv: {
overflow: 'auto',
WebkitOverflowScrolling: 'touch',
},
manageYourWalletText: {
color: colors.mediumBlue,
fontWeight: 'bold',
},
loadingBody: {
height: 381,
},
};
const ETHER_ICON_PATH = '/images/ether.png';
const ICON_DIMENSION = 28;
const BODY_ITEM_KEY = 'BODY';
const HEADER_ITEM_KEY = 'HEADER';
const FOOTER_ITEM_KEY = 'FOOTER';
const DISCONNECTED_ITEM_KEY = 'DISCONNECTED';
const ETHER_ITEM_KEY = 'ETHER';
const USD_DECIMAL_PLACES = 2;
const NO_ALLOWANCE_TOGGLE_SPACE_WIDTH = 56;
const ACCOUNT_PATH = `${WebsitePaths.Portal}/account`;
const ActionButton = styled(FloatingActionButton)`
button {
position: static !important;
}
`;
const PLACEHOLDER_COLOR = colors.grey300;
const LOADING_ROWS_COUNT = 6;
export class Wallet extends React.Component<WalletProps, WalletState> {
public static defaultProps = {
@ -171,72 +115,103 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
}
}
public render(): React.ReactNode {
const isBlockchainLoaded = this.props.blockchainIsLoaded && this.props.blockchainErr === BlockchainErrs.NoError;
return (
<Island className="flex flex-column wallet" style={{ ...styles.root, ...this.props.style }}>
{isBlockchainLoaded ? this._renderLoadedRows() : this._renderLoadingRows()}
<Island className="flex flex-column wallet" style={this.props.style}>
{this._isBlockchainReady() ? this._renderLoadedRows() : this._renderLoadingRows()}
</Island>
);
}
private _renderLoadingRows(): React.ReactNode {
return _.concat(this._renderLoadingHeaderRows(), this._renderLoadingBodyRows());
}
private _renderLoadingHeaderRows(): React.ReactElement<{}> {
return this._renderPlainHeaderRow('Loading...');
}
private _renderLoadingBodyRows(): React.ReactElement<{}> {
const bodyStyle = this._getBodyStyle();
const loadingRowsRange = _.range(LOADING_ROWS_COUNT);
return (
<div key={BODY_ITEM_KEY} className="flex flex-column" style={bodyStyle}>
{_.map(loadingRowsRange, index => {
return <NullTokenRow key={index} iconDimension={ICON_DIMENSION} fillColor={PLACEHOLDER_COLOR} />;
})}
<Container
className="flex items-center"
position="absolute"
width="100%"
height="100%"
maxHeight={bodyStyle.maxHeight}
>
<div className="mx-auto">
<BodyOverlay
dispatcher={this.props.dispatcher}
userAddress={this.props.userAddress}
injectedProviderName={this.props.injectedProviderName}
providerType={this.props.providerType}
onToggleLedgerDialog={this.props.onToggleLedgerDialog}
blockchain={this.props.blockchain}
blockchainIsLoaded={this.props.blockchainIsLoaded}
/>
</div>
</Container>
</div>
);
}
private _renderLoadedRows(): React.ReactNode {
const isAddressAvailable = !_.isEmpty(this.props.userAddress);
return isAddressAvailable
? _.concat(this._renderConnectedHeaderRows(), this._renderBody(), this._renderFooterRows())
: _.concat(this._renderDisconnectedHeaderRows(), this._renderDisconnectedRows());
}
private _renderLoadingRows(): React.ReactNode {
return _.concat(this._renderDisconnectedHeaderRows(), this._renderLoadingBodyRows());
}
private _renderLoadingBodyRows(): React.ReactElement<{}> {
return (
<div key={BODY_ITEM_KEY} className="flex items-center" style={styles.loadingBody}>
<div className="mx-auto">
<CircularProgress size={40} thickness={5} />
</div>
</div>
);
? _.concat(this._renderConnectedHeaderRows(), this._renderBody())
: _.concat(this._renderDisconnectedHeaderRows(), this._renderLoadingBodyRows());
}
private _renderDisconnectedHeaderRows(): React.ReactElement<{}> {
const primaryText = 'wallet';
const isExternallyInjectedProvider = utils.isExternallyInjected(
this.props.providerType,
this.props.injectedProviderName,
);
const text = isExternallyInjectedProvider ? 'Please unlock MetaMask...' : 'Please connect a wallet...';
return this._renderPlainHeaderRow(text);
}
private _renderPlainHeaderRow(text: string): React.ReactElement<{}> {
return (
<StandardIconRow
key={HEADER_ITEM_KEY}
icon={<ActionAccountBalanceWallet color={colors.mediumBlue} />}
main={primaryText.toUpperCase()}
style={styles.borderedItem}
/>
);
}
private _renderDisconnectedRows(): React.ReactElement<{}> {
return (
<WalletDisconnectedItem
key={DISCONNECTED_ITEM_KEY}
providerType={this.props.providerType}
injectedProviderName={this.props.injectedProviderName}
onToggleLedgerDialog={this.props.onToggleLedgerDialog}
icon={<ActionAccountBalanceWallet color={colors.grey} />}
main={
<Text fontSize="16px" fontColor={colors.grey}>
{text}
</Text>
// https://github.com/palantir/tslint-react/issues/140
// tslint:disable-next-line:jsx-curly-spacing
}
minHeight="60px"
backgroundColor={colors.white}
/>
);
}
private _renderConnectedHeaderRows(): React.ReactElement<{}> {
const userAddress = this.props.userAddress;
const primaryText = utils.getAddressBeginAndEnd(userAddress);
const accountState = this._getAccountState();
const main = (
<div className="flex flex-column">
<Text fontSize="16px" lineHeight="19px" fontWeight={500}>
{utils.getAddressBeginAndEnd(userAddress)}
</Text>
<AccountConnection accountState={accountState} injectedProviderName={this.props.injectedProviderName} />
</div>
);
return (
<Link key={HEADER_ITEM_KEY} to={ACCOUNT_PATH} style={{ textDecoration: 'none' }}>
<StandardIconRow
icon={<Identicon address={userAddress} diameter={ICON_DIMENSION} />}
main={primaryText}
style={styles.borderedItem}
main={main}
minHeight="60px"
backgroundColor={colors.white}
/>
</Link>
);
}
private _renderBody(): React.ReactElement<{}> {
const bodyStyle: React.CSSProperties = {
...styles.bodyInnerDiv,
overflow: this.state.isHoveringSidebar ? 'auto' : 'hidden',
// TODO: make this completely responsive
maxHeight: this.props.screenWidth !== ScreenWidths.Sm ? 475 : undefined,
};
const bodyStyle = this._getBodyStyle();
return (
<div
style={bodyStyle}
@ -249,6 +224,17 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
</div>
);
}
private _getBodyStyle(): React.CSSProperties {
return {
overflow: 'auto',
WebkitOverflowScrolling: 'touch',
position: 'relative',
overflowY: this.state.isHoveringSidebar ? 'scroll' : 'hidden',
marginRight: this.state.isHoveringSidebar ? 0 : 4,
// TODO: make this completely responsive
maxHeight: this.props.screenWidth !== ScreenWidths.Sm ? 475 : undefined,
};
}
private _onSidebarHover(_event: React.FormEvent<HTMLInputElement>): void {
this.setState({
isHoveringSidebar: true,
@ -259,51 +245,6 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
isHoveringSidebar: false,
});
}
private _renderFooterRows(): React.ReactElement<{}> {
return (
<div key={FOOTER_ITEM_KEY}>
<ListItem
primaryText={
<div className="flex">
<ActionButton mini={true} zDepth={0} onClick={this.props.onAddToken}>
<ContentAdd />
</ActionButton>
<ActionButton mini={true} zDepth={0} className="px1" onClick={this.props.onRemoveToken}>
<ContentRemove />
</ActionButton>
<div
style={{
paddingLeft: 10,
position: 'relative',
top: '50%',
transform: 'translateY(33%)',
}}
>
add/remove tokens
</div>
</div>
}
disabled={true}
innerDivStyle={styles.footerItemInnerDiv}
style={styles.borderedItem}
/>
{this.props.location.pathname !== ACCOUNT_PATH && (
<Link to={ACCOUNT_PATH} style={{ textDecoration: 'none' }}>
<ListItem
primaryText={
<div className="flex right" style={styles.manageYourWalletText}>
manage your wallet
</div>
// https://github.com/palantir/tslint-react/issues/140
// tslint:disable-next-line:jsx-curly-spacing
}
style={{ ...styles.paddedItem, ...styles.borderedItem }}
/>
</Link>
)}
</div>
);
}
private _renderEthRows(): React.ReactNode {
const icon = <img style={{ width: ICON_DIMENSION, height: ICON_DIMENSION }} src={ETHER_ICON_PATH} />;
const primaryText = this._renderAmount(
@ -325,7 +266,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
wrappedEtherDirection: Side.Deposit,
};
const key = ETHER_ITEM_KEY;
return this._renderBalanceRow(key, icon, primaryText, secondaryText, accessoryItemConfig, false);
return this._renderBalanceRow(key, icon, primaryText, secondaryText, accessoryItemConfig);
}
private _renderTokenRows(): React.ReactNode {
const trackedTokens = this.props.trackedTokens;
@ -336,7 +277,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
);
return _.map(trackedTokensStartingWithEtherToken, this._renderTokenRow.bind(this));
}
private _renderTokenRow(token: Token, index: number): React.ReactNode {
private _renderTokenRow(token: Token): React.ReactNode {
const tokenState = this.props.trackedTokenStateByAddress[token.address];
if (_.isUndefined(tokenState)) {
return null;
@ -364,8 +305,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
},
};
const key = token.address;
const isLastRow = index === this.props.trackedTokens.length - 1;
return this._renderBalanceRow(key, icon, primaryText, secondaryText, accessoryItemConfig, isLastRow);
return this._renderBalanceRow(key, icon, primaryText, secondaryText, accessoryItemConfig);
}
private _renderBalanceRow(
key: string,
@ -373,20 +313,12 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
primaryText: React.ReactNode,
secondaryText: React.ReactNode,
accessoryItemConfig: AccessoryItemConfig,
isLastRow: boolean,
className?: string,
): React.ReactNode {
const shouldShowWrapEtherItem =
!_.isUndefined(this.state.wrappedEtherDirection) &&
this.state.wrappedEtherDirection === accessoryItemConfig.wrappedEtherDirection &&
!_.isUndefined(this.props.userEtherBalanceInWei);
let additionalStyle;
if (shouldShowWrapEtherItem) {
additionalStyle = walletItemStyles.focusedItem;
} else if (!isLastRow) {
additionalStyle = styles.borderedItem;
}
const style = { ...styles.tokenItem, ...additionalStyle };
const etherToken = this._getEthToken();
return (
<div id={key} key={key} className={`flex flex-column ${className || ''}`}>
@ -399,7 +331,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
</div>
}
accessory={this._renderAccessoryItems(accessoryItemConfig)}
style={style}
backgroundColor={shouldShowWrapEtherItem ? colors.walletFocusedItemBackground : undefined}
/>
{shouldShowWrapEtherItem && (
<WrapEtherItem
@ -458,13 +390,19 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
): React.ReactNode {
if (isLoading) {
return (
<PlaceHolder hideChildren={isLoading}>
<div style={styles.amountLabel}>0.00 XXX</div>
<PlaceHolder hideChildren={isLoading} fillColor={PLACEHOLDER_COLOR}>
<Text fontSize="16px" fontWeight="bold" lineHeight="1em">
0.00 XXX
</Text>
</PlaceHolder>
);
} else {
const result = utils.getFormattedAmount(amount, decimals, symbol);
return <div style={styles.amountLabel}>{result}</div>;
return (
<Text fontSize="16px" fontWeight="bold" lineHeight="1em">
{result}
</Text>
);
}
}
private _renderValue(
@ -487,8 +425,10 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
result = '$0.00';
}
return (
<PlaceHolder hideChildren={isLoading}>
<div style={styles.valueLabel}>{result}</div>
<PlaceHolder hideChildren={isLoading} fillColor={PLACEHOLDER_COLOR}>
<Text fontSize="14px" fontColor={colors.darkGrey} lineHeight="1em">
{result}
</Text>
</PlaceHolder>
);
}
@ -541,41 +481,17 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
private _getEthToken(): Token {
return utils.getEthToken(this.props.tokenByAddress);
}
private _isBlockchainReady(): boolean {
return this.props.blockchainIsLoaded && !_.isUndefined(this.props.blockchain);
}
private _getAccountState(): AccountState {
return utils.getAccountState(
this._isBlockchainReady(),
this.props.providerType,
this.props.injectedProviderName,
this.props.userAddress,
);
}
}
interface StandardIconRowProps {
icon: React.ReactNode;
main: React.ReactNode;
accessory?: React.ReactNode;
style?: React.CSSProperties;
}
const StandardIconRow = (props: StandardIconRowProps) => {
return (
<div className="flex items-center" style={props.style}>
<div className="p2">{props.icon}</div>
<div className="flex-none pr2 pt2 pb2">{props.main}</div>
<div className="flex-auto" />
<div>{props.accessory}</div>
</div>
);
};
interface PlaceHolderProps {
hideChildren: React.ReactNode;
children?: React.ReactNode;
}
const PlaceHolder = (props: PlaceHolderProps) => {
const rootBackgroundColor = props.hideChildren ? colors.lightGrey : 'transparent';
const rootStyle: React.CSSProperties = {
backgroundColor: rootBackgroundColor,
display: 'inline-block',
borderRadius: 2,
};
const childrenVisibility = props.hideChildren ? 'hidden' : 'visible';
const childrenStyle: React.CSSProperties = { visibility: childrenVisibility };
return (
<div style={rootStyle}>
<div style={childrenStyle}>{props.children}</div>
</div>
);
};
// tslint:disable:max-file-line-count

View File

@ -1,100 +0,0 @@
import { Styles } from '@0xproject/react-shared';
import FlatButton from 'material-ui/FlatButton';
import * as React from 'react';
import { colors } from 'ts/style/colors';
import { BrowserType, ProviderType } from 'ts/types';
import { constants } from 'ts/utils/constants';
import { utils } from 'ts/utils/utils';
export interface WalletDisconnectedItemProps {
providerType: ProviderType;
injectedProviderName: string;
onToggleLedgerDialog: () => void;
}
const styles: Styles = {
button: {
border: colors.walletBorder,
borderStyle: 'solid',
borderWidth: 1,
height: 80,
},
hrefAdjustment: {
paddingTop: 20, // HACK: For some reason when we set the href prop of a FlatButton material-ui reduces the top padding
},
otherWalletText: {
fontSize: 14,
color: colors.grey500,
textDecoration: 'underline',
},
};
const ITEM_HEIGHT = 381;
const METAMASK_ICON_WIDTH = 35;
const LEDGER_ICON_WIDTH = 30;
const BUTTON_BOTTOM_PADDING = 80;
export const WalletDisconnectedItem: React.StatelessComponent<WalletDisconnectedItemProps> = (
props: WalletDisconnectedItemProps,
) => {
const isExternallyInjectedProvider = utils.isExternallyInjected(props.providerType, props.injectedProviderName);
return (
<div className="flex flex-center">
<div className="mx-auto">
<div className="table" style={{ height: ITEM_HEIGHT }}>
<div className="table-cell align-middle">
<ProviderButton isExternallyInjectedProvider={isExternallyInjectedProvider} />
<div className="flex flex-center py2" style={{ paddingBottom: BUTTON_BOTTOM_PADDING }}>
<div className="mx-auto">
<div onClick={props.onToggleLedgerDialog} style={{ cursor: 'pointer' }}>
<img src="/images/ledger_icon.png" style={{ width: LEDGER_ICON_WIDTH }} />
<span className="px1" style={styles.otherWalletText}>
user other wallet
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
interface ProviderButtonProps {
isExternallyInjectedProvider: boolean;
}
const ProviderButton: React.StatelessComponent<ProviderButtonProps> = (props: ProviderButtonProps) => {
const browserType = utils.getBrowserType();
let extensionLink;
if (!props.isExternallyInjectedProvider) {
switch (browserType) {
case BrowserType.Chrome:
extensionLink = constants.URL_METAMASK_CHROME_STORE;
break;
case BrowserType.Firefox:
extensionLink = constants.URL_METAMASK_FIREFOX_STORE;
break;
case BrowserType.Opera:
extensionLink = constants.URL_METAMASK_OPERA_STORE;
break;
default:
extensionLink = constants.URL_METAMASK_HOMEPAGE;
}
}
return (
<FlatButton
label={props.isExternallyInjectedProvider ? 'Please unlock account' : 'Get Metamask Wallet Extension'}
labelStyle={{ color: colors.black }}
labelPosition="after"
primary={true}
icon={<img src="/images/metamask_icon.png" width={METAMASK_ICON_WIDTH.toString()} />}
style={props.isExternallyInjectedProvider ? styles.button : { ...styles.button, ...styles.hrefAdjustment }}
href={extensionLink}
target={props.isExternallyInjectedProvider ? undefined : '_blank'}
disabled={props.isExternallyInjectedProvider}
/>
);
};

View File

@ -15,7 +15,6 @@ import { analytics } from 'ts/utils/analytics';
import { constants } from 'ts/utils/constants';
import { errorReporter } from 'ts/utils/error_reporter';
import { utils } from 'ts/utils/utils';
import { styles as walletItemStyles } from 'ts/utils/wallet_item_styles';
export interface WrapEtherItemProps {
userAddress: string;
@ -95,7 +94,7 @@ export class WrapEtherItem extends React.Component<WrapEtherItemProps, WrapEther
const topLabelText = isWrappingEth ? 'Convert ETH into WETH 1:1' : 'Convert WETH into ETH 1:1';
return (
<div className="flex" style={walletItemStyles.focusedItem}>
<div className="flex" style={{ backgroundColor: colors.walletFocusedItemBackground }}>
<div>{this._renderIsEthConversionHappeningSpinner()} </div>
<div className="flex flex-column">
<div style={styles.topLabel}>{topLabelText}</div>

View File

@ -1,14 +1,14 @@
import * as React from 'react';
import { Circle } from 'ts/components/ui/circle';
export interface ListItemProps {
bulletColor?: string;
}
export const ListItem: React.StatelessComponent<ListItemProps> = ({ bulletColor, children }) => {
return (
<div className="flex items-center">
<svg className="flex-none lg-px2 md-px2 sm-pl2" height="26" width="26">
<circle cx="13" cy="13" r="13" fill={bulletColor || 'transparent'} />
</svg>
<Circle className="flex-none lg-px2 md-px2 sm-pl2" diameter={26} fillColor={bulletColor || 'transparent'} />
<div className="flex-auto px2">{children}</div>
</div>
);

View File

@ -6,13 +6,13 @@ const appColors = {
walletDefaultItemBackground: '#fbfbfc',
walletFocusedItemBackground: '#f0f1f4',
allowanceToggleShadow: 'rgba(0, 0, 0, 0)',
allowanceToggleOffTrack: '#adadad',
allowanceToggleOnTrack: sharedColors.mediumBlue,
wrapEtherConfirmationButton: sharedColors.mediumBlue,
drawerMenuBackground: '#4a4a4a',
menuItemDefaultSelectedBackground: '#424242',
jobsPageBackground: sharedColors.grey50,
jobsPageOpenPositionRow: sharedColors.grey100,
metaMaskOrange: '#f68c24',
metaMaskTransparentOrange: 'rgba(255, 248, 242, 0.8)',
};
export const colors = {

View File

@ -564,4 +564,11 @@ export enum BrowserType {
Opera = 'Opera',
Other = 'Other',
}
export enum AccountState {
Disconnected = 'Disconnected',
Ready = 'Ready',
Loading = 'Loading',
Locked = 'Locked',
}
// tslint:disable:max-file-line-count

View File

@ -26,7 +26,7 @@ export const constants = {
NETWORK_ID_TESTRPC: 50,
NULL_ADDRESS: '0x0000000000000000000000000000000000000000',
PROVIDER_NAME_LEDGER: 'Ledger',
PROVIDER_NAME_METAMASK: 'Metamask',
PROVIDER_NAME_METAMASK: 'MetaMask',
PROVIDER_NAME_PARITY_SIGNER: 'Parity Signer',
PROVIDER_NAME_MIST: 'Mist',
PROVIDER_NAME_GENERIC: 'Injected Web3',

View File

@ -9,6 +9,7 @@ import deepEqual = require('deep-equal');
import * as _ from 'lodash';
import * as moment from 'moment';
import {
AccountState,
BlockchainCallErrs,
BrowserType,
Environments,
@ -192,23 +193,37 @@ export const utils = {
const truncatedAddress = `${address.substring(0, 6)}...${address.substr(-4)}`; // 0x3d5a...b287
return truncatedAddress;
},
getReadableAccountState(
getReadableAccountState(accountState: AccountState, userAddress: string): string {
switch (accountState) {
case AccountState.Loading:
return 'Loading...';
case AccountState.Ready:
return utils.getAddressBeginAndEnd(userAddress);
case AccountState.Locked:
return 'Please Unlock';
case AccountState.Disconnected:
return 'Connect a Wallet';
default:
return '';
}
},
getAccountState(
isBlockchainReady: boolean,
providerType: ProviderType,
injectedProviderName: string,
userAddress?: string,
): string {
): AccountState {
const isAddressAvailable = !_.isUndefined(userAddress) && !_.isEmpty(userAddress);
const isExternallyInjectedProvider = utils.isExternallyInjected(providerType, injectedProviderName);
if (!isBlockchainReady) {
return 'Loading account';
return AccountState.Loading;
} else if (isAddressAvailable) {
return utils.getAddressBeginAndEnd(userAddress);
return AccountState.Ready;
// tslint:disable-next-line: prefer-conditional-expression
} else if (isExternallyInjectedProvider) {
return 'Account locked';
return AccountState.Locked;
} else {
return 'No wallet detected';
return AccountState.Disconnected;
}
},
hasUniqueNameAndSymbol(tokens: Token[], token: Token): boolean {

View File

@ -1,9 +0,0 @@
import { Styles } from '@0xproject/react-shared';
import { colors } from 'ts/style/colors';
export const styles: Styles = {
focusedItem: {
backgroundColor: colors.walletFocusedItemBackground,
},
};

View File

@ -4887,9 +4887,9 @@ functional-red-black-tree@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
ganache-cli@^6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/ganache-cli/-/ganache-cli-6.1.0.tgz#486c846497204b644166b5f0f74c9b41d02bdc25"
ganache-cli@6.1.3:
version "6.1.3"
resolved "https://registry.yarnpkg.com/ganache-cli/-/ganache-cli-6.1.3.tgz#40323e56c878b5c7275830cb8a4fa75a32f6d468"
dependencies:
source-map-support "^0.5.3"
webpack-cli "^2.0.9"