diff --git a/src/js/actions/SendFormActions.js b/src/js/actions/SendFormActions.js index 2d1eb15f..55830c8c 100644 --- a/src/js/actions/SendFormActions.js +++ b/src/js/actions/SendFormActions.js @@ -285,24 +285,27 @@ export const validation = (): ThunkAction => { let decimalRegExp: RegExp; if (state.token !== accountState.network) { - const token: any = getState().tokens.find(t => t.ethAddress === account.address && t.symbol === state.token); + const token = getState().tokens.find(t => t.ethAddress === account.address && t.symbol === state.token); + if (token) { + if (parseInt(token.decimals) > 0) { + //decimalRegExp = new RegExp('^(0|0\\.([0-9]{0,' + token.decimals + '})?|[1-9]+\\.?([0-9]{0,' + token.decimals + '})?|\\.[0-9]{1,' + token.decimals + '})$'); + decimalRegExp = new RegExp('^(0|0\\.([0-9]{0,' + token.decimals + '})?|[1-9]+\\.?([0-9]{0,' + token.decimals + '})?|\\.[0-9]{1,' + token.decimals + '})$'); + } else { + // decimalRegExp = new RegExp('^(0|0\\.?|[1-9]+\\.?)$'); + decimalRegExp = new RegExp('^[0-9]+$'); + } + + if (!state.amount.match(decimalRegExp)) { + errors.amount = `Maximum ${ token.decimals} decimals allowed`; + } else if (new BigNumber(state.total).greaterThan(account.balance)) { + errors.amount = `Not enough ${ state.coinSymbol.toUpperCase() } to cover transaction fee`; + } else if (new BigNumber(state.amount).greaterThan(token.balance)) { + errors.amount = 'Not enough funds'; + } else if (new BigNumber(state.amount).lessThanOrEqualTo('0')) { + errors.amount = 'Amount is too low'; + } + } - if (parseInt(token.decimals) > 0) { - decimalRegExp = new RegExp('^(0|0\\.([0-9]{0,' + token.decimals + '})?|[1-9]+\\.?([0-9]{0,' + token.decimals + '})?|\\.[0-9]{1,' + token.decimals + '})$'); - } else { - // decimalRegExp = new RegExp('^(0|0\\.?|[1-9]+\\.?)$'); - decimalRegExp = new RegExp('^[0-9]+$'); - } - - if (!state.amount.match(decimalRegExp)) { - errors.amount = `Maximum ${ token.decimals} decimals allowed`; - } else if (new BigNumber(state.total).greaterThan(account.balance)) { - errors.amount = `Not enough ${ state.coinSymbol.toUpperCase() } to cover transaction fee`; - } else if (new BigNumber(state.amount).greaterThan(token.balance)) { - errors.amount = 'Not enough funds'; - } else if (new BigNumber(state.amount).lessThanOrEqualTo('0')) { - errors.amount = 'Amount is too low'; - } } else { decimalRegExp = new RegExp('^(0|0\\.([0-9]{0,18})?|[1-9]+\\.?([0-9]{0,18})?|\\.[0-9]{0,18})$'); if (!state.amount.match(decimalRegExp)) { diff --git a/src/js/actions/TokenActions.js b/src/js/actions/TokenActions.js index f895aff7..27b14996 100644 --- a/src/js/actions/TokenActions.js +++ b/src/js/actions/TokenActions.js @@ -75,6 +75,7 @@ export const add = (token: NetworkToken, account: Account): AsyncAction => { payload: { loaded: false, deviceState: account.deviceState, + network: account.network, name: token.name, symbol: token.symbol, address: token.address, diff --git a/src/js/components/wallet/send/PendingTransactions.js b/src/js/components/wallet/send/PendingTransactions.js index 4d9e5bf5..ef4e3057 100644 --- a/src/js/components/wallet/send/PendingTransactions.js +++ b/src/js/components/wallet/send/PendingTransactions.js @@ -22,7 +22,7 @@ const PendingTransactions = (props: Props) => { if (pending.length < 1) return null; - const tokens = props.tokens.filter(t => t.ethAddress === account.address); + const tokens = props.tokens.filter(t => t.ethAddress === account.address && t.network === account.network); const bgColor = new ColorHash({lightness: 0.7}); const textColor = new ColorHash(); @@ -33,13 +33,15 @@ const PendingTransactions = (props: Props) => { if (tx.token !== tx.network) { const token = tokens.find(t => t.symbol === tx.token); - iconColor = { - color: textColor.hex(token.name), - background: bgColor.hex(token.name), - borderColor: bgColor.hex(token.name) + if (token) { + iconColor = { + color: textColor.hex(token.name), + background: bgColor.hex(token.name), + borderColor: bgColor.hex(token.name) + } + symbol = token.symbol.toUpperCase(); + name = token.name; } - symbol = token.symbol.toUpperCase(); - name = token.name; } else { iconColor = { color: textColor.hex(tx.network), diff --git a/src/js/components/wallet/send/SendForm.js b/src/js/components/wallet/send/SendForm.js index 882762d6..624058af 100644 --- a/src/js/components/wallet/send/SendForm.js +++ b/src/js/components/wallet/send/SendForm.js @@ -40,7 +40,6 @@ const _render = (props: Props, state: AccountState): React$Element => { coinSymbol, token, feeLevels, - fee, selectedFeeLevel, gasPriceNeedsUpdate, total, @@ -61,8 +60,7 @@ const _render = (props: Props, state: AccountState): React$Element => { onSend, } = props.sendFormActions; - const { config } = props.localStorage; - const selectedCoin = config.coins.find(c => c.network === network); + const selectedCoin = props.abstractAccount.coin; const fiatRate = props.fiat.find(f => f.network === network); const tokens = addressTokens.map(t => { diff --git a/src/js/components/wallet/send/index.js b/src/js/components/wallet/send/index.js index 8d2d1b60..e2245286 100644 --- a/src/js/components/wallet/send/index.js +++ b/src/js/components/wallet/send/index.js @@ -1,7 +1,7 @@ /* @flow */ 'use strict'; -import React, { Component, PropTypes } from 'react'; +import * as React from 'react'; import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; @@ -11,15 +11,17 @@ import SendForm from './SendForm'; import type { MapStateToProps, MapDispatchToProps } from 'react-redux'; import type { State, Dispatch } from '../../../flowtype'; -import type { StateProps as BaseStateProps, DispatchProps as BaseDispatchProps, Props as BaseProps} from '../account/AbstractAccount'; +import type { StateProps as BaseStateProps, DispatchProps as BaseDispatchProps } from '../account/AbstractAccount'; type OwnProps = { } -type StateProps = StateProps & { +type StateProps = BaseStateProps & { tokens: $ElementType, pending: $ElementType, + sendForm: $ElementType, fiat: $ElementType, localStorage: $ElementType, + children?: React.Node; } type DispatchProps = BaseDispatchProps & { @@ -29,7 +31,7 @@ type DispatchProps = BaseDispatchProps & { sendFormActions: typeof SendFormActions } -export type Props = BaseProps & StateProps & DispatchProps; +export type Props = StateProps & DispatchProps; const mapStateToProps: MapStateToProps = (state: State, own: OwnProps): StateProps => { return { diff --git a/src/js/components/wallet/summary/Summary.js b/src/js/components/wallet/summary/Summary.js index dd5ff977..77eb76c2 100644 --- a/src/js/components/wallet/summary/Summary.js +++ b/src/js/components/wallet/summary/Summary.js @@ -36,7 +36,7 @@ const _render = (props: Props, state: AccountState): React$Element => { if (!device || !account) return
; const abstractAccount = props.abstractAccount; - const tokens = props.tokens.filter(t => t.ethAddress === account.address); + const tokens = props.tokens.filter(t => t.ethAddress === account.address && t.network === account.network); return ( diff --git a/src/js/reducers/LocalStorageReducer.js b/src/js/reducers/LocalStorageReducer.js index 761a1a14..e7e6b133 100644 --- a/src/js/reducers/LocalStorageReducer.js +++ b/src/js/reducers/LocalStorageReducer.js @@ -59,6 +59,11 @@ export type Config = { fiatValueTickers: Array; } +export type CustomBackend = { + name: string; + url: string; +} + export type State = { initialized: boolean; @@ -66,6 +71,7 @@ export type State = { config: Config; ERC20Abi: Array; tokens: TokensCollection; + customBackend: Array; } const initialState: State = { @@ -77,6 +83,13 @@ const initialState: State = { }, ERC20Abi: [], tokens: {}, + customBackend: [ + { + name: "Custom1", + slug: "custom1", + url: "http://google.com" + } + ] }; export default function localStorage(state: State = initialState, action: Action): State { diff --git a/src/js/reducers/TokensReducer.js b/src/js/reducers/TokensReducer.js index b6262199..165e9358 100644 --- a/src/js/reducers/TokensReducer.js +++ b/src/js/reducers/TokensReducer.js @@ -9,6 +9,7 @@ import type { Action, TrezorDevice } from '../flowtype'; export type Token = { loaded: boolean; +deviceState: string; + +network: string; +name: string; +symbol: string; +address: string; diff --git a/src/js/services/LocalStorageService.js b/src/js/services/LocalStorageService.js index 37905f38..4823980d 100644 --- a/src/js/services/LocalStorageService.js +++ b/src/js/services/LocalStorageService.js @@ -44,7 +44,7 @@ const findAccounts = (devices: Array, accounts: Array): A const findTokens = (accounts: Array, tokens: Array): Array => { return accounts.reduce((arr, account) => { - return arr.concat(tokens.filter(a => a.ethAddress === account.address)); + return arr.concat(tokens.filter(token => token.ethAddress === account.address && token.network === account.network)); }, []); } diff --git a/src/solidity/grzegorz-token.js b/src/solidity/grzegorz-token.js new file mode 100644 index 00000000..095fe96d --- /dev/null +++ b/src/solidity/grzegorz-token.js @@ -0,0 +1,137 @@ +pragma solidity ^0.4.4; + +contract Token { + + /// @return total amount of tokens + function totalSupply() constant returns (uint256 supply) {} + + /// @param _owner The address from which the balance will be retrieved + /// @return The balance + function balanceOf(address _owner) constant returns (uint256 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, uint256 _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, uint256 _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, uint256 _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 (uint256 remaining) {} + + event Transfer(address indexed _from, address indexed _to, uint256 _value); + event Approval(address indexed _owner, address indexed _spender, uint256 _value); + +} + + + +contract StandardToken is Token { + + function transfer(address _to, uint256 _value) returns (bool success) { + //Default assumes totalSupply can't be over max (2^256 - 1). + //If your token leaves out totalSupply and can issue more tokens as time goes on, you need to check if it doesn't wrap. + //Replace the if with this one instead. + //if (balances[msg.sender] >= _value && balances[_to] + _value > balances[_to]) { + if (balances[msg.sender] >= _value && _value > 0) { + balances[msg.sender] -= _value; + balances[_to] += _value; + Transfer(msg.sender, _to, _value); + return true; + } else { return false; } + } + + function transferFrom(address _from, address _to, uint256 _value) returns (bool success) { + //same as above. Replace this line with the following if you want to protect against wrapping uints. + //if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value > balances[_to]) { + if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && _value > 0) { + 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 (uint256 balance) { + return balances[_owner]; + } + + function approve(address _spender, uint256 _value) returns (bool success) { + allowed[msg.sender][_spender] = _value; + Approval(msg.sender, _spender, _value); + return true; + } + + function allowance(address _owner, address _spender) constant returns (uint256 remaining) { + return allowed[_owner][_spender]; + } + + mapping (address => uint256) balances; + mapping (address => mapping (address => uint256)) allowed; + uint256 public totalSupply; +} + + +//name this contract whatever you'd like +contract GrzegorzBrzeczyszczykiewicz is StandardToken { + + function () { + //if ether is sent to this address, send it back. + throw; + } + + /* Public variables of the token */ + + /* + NOTE: + The following variables are OPTIONAL vanities. One does not have to include them. + They allow one to customise the token contract & in no way influences the core functionality. + Some wallets/interfaces might not even bother to look at this information. + */ + string public name; //fancy name: eg Simon Bucks + uint8 public decimals; //How many decimals to show. ie. There could 1000 base units with 3 decimals. Meaning 0.980 SBX = 980 base units. It's like comparing 1 wei to 1 ether. + string public symbol; //An identifier: eg SBX + string public version = 'H1.0'; //human 0.1 standard. Just an arbitrary versioning scheme. + +// +// CHANGE THESE VALUES FOR YOUR TOKEN +// + +//make sure this function name matches the contract name above. So if you're token is called TutorialToken, make sure the //contract name above is also TutorialToken instead of ERC20Token + + function GrzegorzBrzeczyszczykiewicz( + ) { + balances[msg.sender] = 1000000; // Give the creator all initial tokens (100000 for example) + totalSupply = 1000000; // Update total supply (100000 for example) + name = "Grzegorz Brzęczyszczykiewicz"; // Set the name for display purposes + decimals = 3; // Amount of decimals for display purposes + symbol = "GRZBRZ"; // Set the symbol for display purposes + } + + /* Approves and then calls the receiving contract */ + function approveAndCall(address _spender, uint256 _value, bytes _extraData) returns (bool success) { + allowed[msg.sender][_spender] = _value; + Approval(msg.sender, _spender, _value); + + //call the receiveApproval function on the contract you want to be notified. This crafts the function signature manually so one doesn't have to include a contract in here just for this. + //receiveApproval(address _from, uint256 _value, address _tokenContract, bytes _extraData) + //it is assumed that when does this that the call *should* succeed, otherwise one would use vanilla approve instead. + if(!_spender.call(bytes4(bytes32(sha3("receiveApproval(address,uint256,address,bytes)"))), msg.sender, _value, this, _extraData)) { throw; } + return true; + } +} \ No newline at end of file