Connect send page with exchange utils (#75)
* Connect send page with exchange utils * Add exchangeRate and fix txId
This commit is contained in:
parent
b1a5a6c867
commit
a4e0d11cef
@ -9,6 +9,13 @@ class AddressInputPanel extends Component {
|
||||
title: PropTypes.string,
|
||||
description: PropTypes.string,
|
||||
extraText: PropTypes.string,
|
||||
onChange: PropTypes.func,
|
||||
value: PropTypes.string,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
onChange() {},
|
||||
value: '',
|
||||
};
|
||||
|
||||
render() {
|
||||
@ -16,6 +23,8 @@ class AddressInputPanel extends Component {
|
||||
title,
|
||||
description,
|
||||
extraText,
|
||||
onChange,
|
||||
value
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
@ -31,6 +40,8 @@ class AddressInputPanel extends Component {
|
||||
type="text"
|
||||
className="address-input-panel__input"
|
||||
placeholder="0x1234..."
|
||||
onChange={e => onChange(e.target.value)}
|
||||
value={value}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -37,7 +37,7 @@ class CurrencyInputPanel extends Component {
|
||||
onCurrencySelected: PropTypes.func,
|
||||
onValueChange: PropTypes.func,
|
||||
tokenAddresses: PropTypes.shape({
|
||||
address: PropTypes.array.isRequired,
|
||||
addresses: PropTypes.array.isRequired,
|
||||
}).isRequired,
|
||||
exchangeAddresses: PropTypes.shape({
|
||||
fromToken: PropTypes.object.isRequired,
|
||||
|
@ -4,6 +4,7 @@ import addresses from './addresses';
|
||||
import exchangeContracts from './exchange-contract';
|
||||
import tokenContracts from './token-contract';
|
||||
import exchange from './exchange';
|
||||
import send from './send';
|
||||
import swap from './swap';
|
||||
import web3connect from './web3connect';
|
||||
|
||||
@ -12,6 +13,7 @@ export default combineReducers({
|
||||
exchangeContracts,
|
||||
tokenContracts,
|
||||
exchange,
|
||||
send,
|
||||
swap,
|
||||
web3connect,
|
||||
...drizzleReducers,
|
||||
|
88
src/ducks/send.js
Normal file
88
src/ducks/send.js
Normal file
@ -0,0 +1,88 @@
|
||||
const UPDATE_FIELD = 'app/send/updateField';
|
||||
const ADD_ERROR = 'app/send/addError';
|
||||
const REMOVE_ERROR = 'app/send/removeError';
|
||||
const RESET_SEND = 'app/send/resetSend';
|
||||
|
||||
const getInitialState = () => {
|
||||
return {
|
||||
input: '',
|
||||
output: '',
|
||||
inputCurrency: '',
|
||||
outputCurrency: '',
|
||||
recipient: '',
|
||||
lastEditedField: '',
|
||||
inputErrors: [],
|
||||
outputErrors: [],
|
||||
};
|
||||
};
|
||||
|
||||
export const isValidSend = (state) => {
|
||||
const { send } = state;
|
||||
|
||||
return send.outputCurrency !== '' &&
|
||||
send.inputCurrency !== '' &&
|
||||
send.input !== '' &&
|
||||
send.output !== '' &&
|
||||
send.recipient !== '' &&
|
||||
send.inputErrors.length === 0 &&
|
||||
send.outputErrors.length === 0;
|
||||
};
|
||||
|
||||
export const updateField = ({ name, value }) => ({
|
||||
type: UPDATE_FIELD,
|
||||
payload: { name, value },
|
||||
});
|
||||
|
||||
export const addError = ({ name, value }) => ({
|
||||
type: ADD_ERROR,
|
||||
payload: { name, value },
|
||||
});
|
||||
|
||||
export const removeError = ({ name, value }) => ({
|
||||
type: REMOVE_ERROR,
|
||||
payload: { name, value },
|
||||
});
|
||||
|
||||
export const resetSend = () => ({
|
||||
type: RESET_SEND,
|
||||
});
|
||||
|
||||
function reduceAddError(state, payload) {
|
||||
const { name, value } = payload;
|
||||
let nextErrors = state[name];
|
||||
if (nextErrors.indexOf(value) === -1) {
|
||||
nextErrors = [...nextErrors, value];
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
[name]: nextErrors,
|
||||
};
|
||||
}
|
||||
|
||||
function reduceRemoveError(state, payload) {
|
||||
const { name, value } = payload;
|
||||
|
||||
return {
|
||||
...state,
|
||||
[name]: state[name].filter(error => error !== value),
|
||||
};
|
||||
}
|
||||
|
||||
export default function sendReducer(state = getInitialState(), { type, payload }) {
|
||||
switch (type) {
|
||||
case UPDATE_FIELD:
|
||||
return {
|
||||
...state,
|
||||
[payload.name]: payload.value,
|
||||
};
|
||||
case ADD_ERROR:
|
||||
return reduceAddError(state, payload);
|
||||
case REMOVE_ERROR:
|
||||
return reduceRemoveError(state, payload);
|
||||
case RESET_SEND:
|
||||
return getInitialState();
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
@ -273,7 +273,7 @@ const ETH_TO_ERC20 = {
|
||||
value: maxInput.toFixed(0),
|
||||
});
|
||||
},
|
||||
sendInput: async ({drizzleCtx, contractStore, input, output, account, inputCurrency, outputCurrency, exchangeAddresses, recipient}) => {
|
||||
sendInput: async ({drizzleCtx, contractStore, input, output, outputDecimals, account, inputCurrency, outputCurrency, exchangeAddresses, recipient}) => {
|
||||
if (!validEthToErc20(inputCurrency, outputCurrency)) {
|
||||
return;
|
||||
}
|
||||
@ -287,7 +287,6 @@ const ETH_TO_ERC20 = {
|
||||
|
||||
const deadline = await getDeadline(drizzleCtx, 300);
|
||||
const ALLOWED_SLIPPAGE = BN(0.025);
|
||||
const outputDecimals = await getDecimals({ address: outputCurrency, contractStore, drizzleCtx });
|
||||
const minOutput = BN(output).multipliedBy(10 ** outputDecimals).multipliedBy(BN(1).minus(ALLOWED_SLIPPAGE));
|
||||
|
||||
return exchange.methods.ethToTokenTransferInput.cacheSend(
|
||||
@ -300,7 +299,7 @@ const ETH_TO_ERC20 = {
|
||||
}
|
||||
);
|
||||
},
|
||||
sendOutput: async ({drizzleCtx, contractStore, input, output, account, inputCurrency, outputCurrency, exchangeAddresses, recipient}) => {
|
||||
sendOutput: async ({drizzleCtx, contractStore, input, output, outputDecimals, account, inputCurrency, outputCurrency, exchangeAddresses, recipient}) => {
|
||||
if (!validEthToErc20(inputCurrency, outputCurrency)) {
|
||||
return;
|
||||
}
|
||||
@ -314,7 +313,6 @@ const ETH_TO_ERC20 = {
|
||||
|
||||
const deadline = await getDeadline(drizzleCtx, 300);
|
||||
const ALLOWED_SLIPPAGE = BN(0.025);
|
||||
const outputDecimals = await getDecimals({ address: outputCurrency, contractStore, drizzleCtx });
|
||||
const outputAmount = BN(output).multipliedBy(BN(10 ** outputDecimals));
|
||||
const maxInput = BN(input).multipliedBy(10 ** 18).multipliedBy(BN(1).plus(ALLOWED_SLIPPAGE));
|
||||
return exchange.methods.ethToTokenTransferOutput.cacheSend(
|
||||
@ -481,7 +479,7 @@ const ERC20_TO_ETH = {
|
||||
{ from: account },
|
||||
);
|
||||
},
|
||||
sendInput: async ({drizzleCtx, contractStore, input, output, account, inputCurrency, outputCurrency, exchangeAddresses, recipient}) => {
|
||||
sendInput: async ({drizzleCtx, contractStore, input, output, inputDecimals, account, inputCurrency, outputCurrency, exchangeAddresses, recipient}) => {
|
||||
if (!validErc20ToEth(inputCurrency, outputCurrency)) {
|
||||
return;
|
||||
}
|
||||
@ -495,7 +493,6 @@ const ERC20_TO_ETH = {
|
||||
|
||||
const deadline = await getDeadline(drizzleCtx, 300);
|
||||
const ALLOWED_SLIPPAGE = BN(0.025);
|
||||
const inputDecimals = await getDecimals({ address: inputCurrency, contractStore, drizzleCtx });
|
||||
const minOutput = BN(output).multipliedBy(10 ** 18).multipliedBy(BN(1).minus(ALLOWED_SLIPPAGE));
|
||||
const inputAmount = BN(input).multipliedBy(10 ** inputDecimals);
|
||||
|
||||
@ -507,7 +504,7 @@ const ERC20_TO_ETH = {
|
||||
{ from: account, value: '0x0' },
|
||||
);
|
||||
},
|
||||
sendOutput: async ({drizzleCtx, contractStore, input, output, account, inputCurrency, outputCurrency, exchangeAddresses, recipient}) => {
|
||||
sendOutput: async ({drizzleCtx, contractStore, input, output, inputDecimals, account, inputCurrency, outputCurrency, exchangeAddresses, recipient}) => {
|
||||
if (!validErc20ToEth(inputCurrency, outputCurrency)) {
|
||||
return;
|
||||
}
|
||||
@ -521,7 +518,6 @@ const ERC20_TO_ETH = {
|
||||
|
||||
const deadline = await getDeadline(drizzleCtx, 300);
|
||||
const ALLOWED_SLIPPAGE = BN(0.025);
|
||||
const inputDecimals = await getDecimals({ address: inputCurrency, contractStore, drizzleCtx });
|
||||
const maxInput = BN(input).multipliedBy(10 ** inputDecimals).multipliedBy(BN(1).plus(ALLOWED_SLIPPAGE));
|
||||
const outputAmount = BN(output).multipliedBy(10 ** 18);
|
||||
|
||||
@ -676,7 +672,7 @@ const ERC20_TO_ERC20 = {
|
||||
{ from: account },
|
||||
);
|
||||
},
|
||||
sendInput: async ({drizzleCtx, contractStore, input, output, account, inputCurrency, outputCurrency, exchangeAddresses, recipient}) => {
|
||||
sendInput: async ({drizzleCtx, contractStore, input, output, inputDecimals, outputDecimals, account, inputCurrency, outputCurrency, exchangeAddresses, recipient}) => {
|
||||
if (!validErc20ToErc20(inputCurrency, outputCurrency)) {
|
||||
return;
|
||||
}
|
||||
@ -688,8 +684,6 @@ const ERC20_TO_ERC20 = {
|
||||
}
|
||||
const deadline = await getDeadline(drizzleCtx, 300);
|
||||
const ALLOWED_SLIPPAGE = BN(0.04);
|
||||
const inputDecimals = await getDecimals({ address: inputCurrency, contractStore, drizzleCtx });
|
||||
const outputDecimals = await getDecimals({ address: outputCurrency, contractStore, drizzleCtx });
|
||||
const inputAmount = BN(input).multipliedBy(BN(10 ** inputDecimals));
|
||||
const outputAmount = BN(input).multipliedBy(BN(10 ** outputDecimals));
|
||||
|
||||
@ -719,6 +713,8 @@ const ERC20_TO_ERC20 = {
|
||||
outputCurrency,
|
||||
exchangeAddresses,
|
||||
recipient,
|
||||
inputDecimals,
|
||||
outputDecimals,
|
||||
} = opts;
|
||||
const exchangeRateA = await ETH_TO_ERC20.calculateInput({ ...opts, inputCurrency: 'ETH' });
|
||||
if (!exchangeRateA) {
|
||||
@ -738,8 +734,6 @@ const ERC20_TO_ERC20 = {
|
||||
|
||||
const deadline = await getDeadline(drizzleCtx, 300);
|
||||
const ALLOWED_SLIPPAGE = BN(0.04);
|
||||
const inputDecimals = await getDecimals({ address: inputCurrency, contractStore, drizzleCtx });
|
||||
const outputDecimals = await getDecimals({ address: outputCurrency, contractStore, drizzleCtx });
|
||||
const inputAmount = BN(input).multipliedBy(BN(10 ** inputDecimals));
|
||||
const outputAmount = BN(output).multipliedBy(BN(10 ** outputDecimals));
|
||||
const inputAmountB = BN(output).dividedBy(exchangeRateA).multipliedBy(BN(10 ** 18));
|
||||
|
@ -3,11 +3,32 @@ import { drizzleConnect } from 'drizzle-react';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
|
||||
import { isValidSend, updateField, addError, removeError, resetSend } from '../../ducks/send';
|
||||
import { selectors, sync } from '../../ducks/web3connect';
|
||||
import {BigNumber as BN} from "bignumber.js";
|
||||
import deepEqual from 'deep-equal';
|
||||
|
||||
import Header from '../../components/Header';
|
||||
import CurrencyInputPanel from '../../components/CurrencyInputPanel';
|
||||
import AddressInputPanel from '../../components/AddressInputPanel';
|
||||
import OversizedPanel from '../../components/OversizedPanel';
|
||||
import ArrowDown from '../../assets/images/arrow-down-blue.svg';
|
||||
import Pending from '../../assets/images/pending.svg';
|
||||
|
||||
import {
|
||||
calculateExchangeRateFromInput,
|
||||
calculateExchangeRateFromOutput,
|
||||
sendInput,
|
||||
sendOutput,
|
||||
} from '../../helpers/exchange-utils';
|
||||
import {
|
||||
isExchangeUnapproved,
|
||||
approveExchange,
|
||||
} from '../../helpers/approval-utils';
|
||||
import {
|
||||
getTxStatus
|
||||
} from '../../helpers/contract-utils';
|
||||
|
||||
import "./send.scss";
|
||||
|
||||
@ -18,9 +39,300 @@ class Send extends Component {
|
||||
pathname: PropTypes.string.isRequired,
|
||||
currentAddress: PropTypes.string,
|
||||
isConnected: PropTypes.bool.isRequired,
|
||||
isValid: PropTypes.bool.isRequired,
|
||||
updateField: PropTypes.func.isRequired,
|
||||
input: PropTypes.string,
|
||||
output: PropTypes.string,
|
||||
inputCurrency: PropTypes.string,
|
||||
outputCurrency: PropTypes.string,
|
||||
recipient: PropTypes.string,
|
||||
lastEditedField: PropTypes.string,
|
||||
inputErrors: PropTypes.arrayOf(PropTypes.string),
|
||||
outputErrors: PropTypes.arrayOf(PropTypes.string),
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
drizzle: PropTypes.object,
|
||||
};
|
||||
|
||||
state = {
|
||||
exchangeRate: BN(0),
|
||||
approvalTxId: null,
|
||||
sendTxId: null,
|
||||
};
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
return !deepEqual(nextProps, this.props) ||
|
||||
!deepEqual(nextState, this.state);
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if (this.getSendStatus() === 'pending') {
|
||||
this.resetSend();
|
||||
}
|
||||
|
||||
this.getExchangeRate(this.props)
|
||||
.then(exchangeRate => {
|
||||
if (this.state.exchangeRate !== exchangeRate) {
|
||||
this.setState({ exchangeRate });
|
||||
}
|
||||
|
||||
if (!exchangeRate) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.props.lastEditedField === 'input') {
|
||||
this.props.updateField('output', `${BN(this.props.input).multipliedBy(exchangeRate).toFixed(7)}`);
|
||||
} else if (this.props.lastEditedField === 'output') {
|
||||
this.props.updateField('input', `${BN(this.props.output).multipliedBy(BN(1).dividedBy(exchangeRate)).toFixed(7)}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.resetSend();
|
||||
}
|
||||
|
||||
resetSend() {
|
||||
this.props.resetSend();
|
||||
this.setState({approvalTxId: null, sendTxId: null});
|
||||
}
|
||||
|
||||
getSendStatus() {
|
||||
const { drizzle } = this.context;
|
||||
|
||||
return getTxStatus({
|
||||
drizzleCtx: drizzle,
|
||||
txId: this.state.sendTxId,
|
||||
});
|
||||
}
|
||||
|
||||
getTokenLabel(address) {
|
||||
if (address === 'ETH') {
|
||||
return 'ETH';
|
||||
}
|
||||
|
||||
const {
|
||||
initialized,
|
||||
contracts,
|
||||
} = this.props;
|
||||
const { drizzle } = this.context;
|
||||
const { web3 } = drizzle;
|
||||
|
||||
if (!initialized || !web3 || !address) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const symbolKey = drizzle.contracts[address].methods.symbol.cacheCall();
|
||||
const token = contracts[address];
|
||||
const symbol = token.symbol[symbolKey];
|
||||
|
||||
if (!symbol) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return symbol.value;
|
||||
}
|
||||
|
||||
getBalance(currency) {
|
||||
const { selectors, account } = this.props;
|
||||
|
||||
if (!currency) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (currency === 'ETH') {
|
||||
const { value, decimals } = selectors().getBalance(account);
|
||||
return `Balance: ${value.dividedBy(10 ** decimals).toFixed(4)}`;
|
||||
}
|
||||
|
||||
const { value, decimals } = selectors().getTokenBalance(currency, account);
|
||||
return `Balance: ${value.dividedBy(10 ** decimals).toFixed(4)}`;
|
||||
}
|
||||
|
||||
updateInput(amount) {
|
||||
this.props.updateField('input', amount);
|
||||
if (!amount) {
|
||||
this.props.updateField('output', '');
|
||||
}
|
||||
this.props.updateField('lastEditedField', 'input');
|
||||
}
|
||||
|
||||
updateOutput(amount) {
|
||||
this.props.updateField('output', amount);
|
||||
if (!amount) {
|
||||
this.props.updateField('input', '');
|
||||
}
|
||||
this.props.updateField('lastEditedField', 'output');
|
||||
}
|
||||
|
||||
async getExchangeRate(props) {
|
||||
const {
|
||||
input,
|
||||
output,
|
||||
inputCurrency,
|
||||
outputCurrency,
|
||||
exchangeAddresses,
|
||||
lastEditedField,
|
||||
contracts,
|
||||
} = props;
|
||||
|
||||
const { drizzle } = this.context;
|
||||
|
||||
return lastEditedField === 'input'
|
||||
? await calculateExchangeRateFromInput({
|
||||
drizzleCtx: drizzle,
|
||||
contractStore: contracts,
|
||||
input,
|
||||
output,
|
||||
inputCurrency,
|
||||
outputCurrency,
|
||||
exchangeAddresses,
|
||||
})
|
||||
: await calculateExchangeRateFromOutput({
|
||||
drizzleCtx: drizzle,
|
||||
contractStore: contracts,
|
||||
input,
|
||||
output,
|
||||
inputCurrency,
|
||||
outputCurrency,
|
||||
exchangeAddresses,
|
||||
}) ;
|
||||
}
|
||||
|
||||
getIsUnapproved() {
|
||||
const {
|
||||
input,
|
||||
inputCurrency,
|
||||
account,
|
||||
contracts,
|
||||
exchangeAddresses
|
||||
} = this.props;
|
||||
const { drizzle } = this.context;
|
||||
|
||||
return isExchangeUnapproved({
|
||||
value: input,
|
||||
currency: inputCurrency,
|
||||
drizzleCtx: drizzle,
|
||||
contractStore: contracts,
|
||||
account,
|
||||
exchangeAddresses,
|
||||
});
|
||||
}
|
||||
|
||||
approveExchange = async () => {
|
||||
const {
|
||||
inputCurrency,
|
||||
exchangeAddresses,
|
||||
account,
|
||||
contracts,
|
||||
} = this.props;
|
||||
const { drizzle } = this.context;
|
||||
|
||||
if (this.getIsUnapproved()) {
|
||||
const approvalTxId = await approveExchange({
|
||||
currency: inputCurrency,
|
||||
drizzleCtx: drizzle,
|
||||
contractStore: contracts,
|
||||
account,
|
||||
exchangeAddresses,
|
||||
});
|
||||
|
||||
this.setState({ approvalTxId })
|
||||
}
|
||||
}
|
||||
|
||||
getApprovalStatus() {
|
||||
const { drizzle } = this.context;
|
||||
|
||||
return getTxStatus({
|
||||
drizzleCtx: drizzle,
|
||||
txId: this.state.approvalTxId,
|
||||
});
|
||||
}
|
||||
|
||||
onSend = async () => {
|
||||
const {
|
||||
input,
|
||||
output,
|
||||
inputCurrency,
|
||||
outputCurrency,
|
||||
recipient,
|
||||
exchangeAddresses,
|
||||
lastEditedField,
|
||||
account,
|
||||
contracts,
|
||||
selectors,
|
||||
} = this.props;
|
||||
|
||||
const { drizzle } = this.context;
|
||||
const { decimals: inputDecimals } = inputCurrency === 'ETH' ?
|
||||
selectors().getBalance(account)
|
||||
: selectors().getTokenBalance(inputCurrency, account);
|
||||
const { decimals: outputDecimals } = outputCurrency === 'ETH' ?
|
||||
selectors().getBalance(account)
|
||||
: selectors().getTokenBalance(outputCurrency, account);
|
||||
let sendTxId;
|
||||
|
||||
if (lastEditedField === 'input') {
|
||||
sendTxId = await sendInput({
|
||||
drizzleCtx: drizzle,
|
||||
contractStore: contracts,
|
||||
input,
|
||||
output,
|
||||
inputCurrency,
|
||||
outputCurrency,
|
||||
recipient,
|
||||
exchangeAddresses,
|
||||
account,
|
||||
inputDecimals,
|
||||
outputDecimals,
|
||||
});
|
||||
}
|
||||
|
||||
if (lastEditedField === 'output') {
|
||||
sendTxId = await sendOutput({
|
||||
drizzleCtx: drizzle,
|
||||
contractStore: contracts,
|
||||
input,
|
||||
output,
|
||||
inputCurrency,
|
||||
outputCurrency,
|
||||
recipient,
|
||||
exchangeAddresses,
|
||||
account,
|
||||
inputDecimals,
|
||||
outputDecimals,
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({ sendTxId });
|
||||
};
|
||||
|
||||
handleSubButtonClick = () => {
|
||||
if (this.getIsUnapproved() && this.getApprovalStatus() !== 'pending') {
|
||||
this.approveExchange();
|
||||
}
|
||||
}
|
||||
|
||||
renderSubButtonText() {
|
||||
if (this.getApprovalStatus() === 'pending') {
|
||||
return [
|
||||
(<img key="pending" className="swap__sub-icon" src={Pending} />),
|
||||
(<span key="text" className="swap__sub-text">Pending</span>)
|
||||
];
|
||||
} else {
|
||||
return '🔒 Unlock'
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lastEditedField, inputCurrency, outputCurrency, input, output, recipient, isValid, outputErrors, inputErrors } = this.props;
|
||||
const { exchangeRate } = this.state;
|
||||
const inputLabel = this.getTokenLabel(inputCurrency);
|
||||
const outputLabel = this.getTokenLabel(outputCurrency);
|
||||
const estimatedText = '(estimated)';
|
||||
// 0xc41c71CAeA8ccc9AE19c6d8a66c6870C6E9c3632
|
||||
return (
|
||||
<div className="send">
|
||||
<Header />
|
||||
@ -31,7 +343,19 @@ class Send extends Component {
|
||||
>
|
||||
<CurrencyInputPanel
|
||||
title="Input"
|
||||
extraText="Balance: 0.03141"
|
||||
description={lastEditedField === 'output' ? estimatedText : ''}
|
||||
onCurrencySelected={(d) => {
|
||||
this.props.updateField('inputCurrency', d)
|
||||
this.props.sync();
|
||||
}}
|
||||
onValueChange={d => this.updateInput(d)}
|
||||
selectedTokens={[inputCurrency, outputCurrency]}
|
||||
addError={error => this.props.addError('inputErrors', error)}
|
||||
removeError={error => this.props.removeError('inputErrors', error)}
|
||||
errors={inputErrors}
|
||||
value={input}
|
||||
selectedTokenAddress={inputCurrency}
|
||||
extraText={this.getBalance(inputCurrency)}
|
||||
/>
|
||||
<OversizedPanel>
|
||||
<div className="swap__down-arrow-background">
|
||||
@ -40,30 +364,56 @@ class Send extends Component {
|
||||
</OversizedPanel>
|
||||
<CurrencyInputPanel
|
||||
title="Output"
|
||||
description="(estimated)"
|
||||
extraText="Balance: 0.0"
|
||||
description={lastEditedField === 'input' ? estimatedText : ''}
|
||||
onCurrencySelected={(d) => {
|
||||
this.props.updateField('outputCurrency', d)
|
||||
this.props.sync();
|
||||
}}
|
||||
onValueChange={d => this.updateOutput(d)}
|
||||
selectedTokens={[inputCurrency, outputCurrency]}
|
||||
addError={error => this.props.addError('outputErrors', error)}
|
||||
removeError={error => this.props.removeError('outputErrors', error)}
|
||||
errors={outputErrors}
|
||||
value={output}
|
||||
selectedTokenAddress={outputCurrency}
|
||||
extraText={this.getBalance(outputCurrency)}
|
||||
/>
|
||||
<OversizedPanel>
|
||||
<div className="swap__down-arrow-background">
|
||||
<img className="swap__down-arrow" src={ArrowDown} />
|
||||
</div>
|
||||
</OversizedPanel>
|
||||
<AddressInputPanel />
|
||||
<AddressInputPanel
|
||||
value={recipient}
|
||||
onChange={address => this.props.updateField('recipient', address)}
|
||||
/>
|
||||
<OversizedPanel hideBottom>
|
||||
<div className="swap__exchange-rate-wrapper">
|
||||
<span className="swap__exchange-rate">Exchange Rate</span>
|
||||
<span>1 ETH = 1283.878 BAT</span>
|
||||
<span>
|
||||
{exchangeRate ? `1 ${inputLabel} = ${exchangeRate.toFixed(7)} ${outputLabel}` : ' - '}
|
||||
</span>
|
||||
</div>
|
||||
</OversizedPanel>
|
||||
<div className="swap__summary-wrapper">
|
||||
<div>You are selling <span className="swap__highlight-text">0.01 ETH</span></div>
|
||||
<div>You will receive between <span className="swap__highlight-text">12.80</span> and <span className="swap__highlight-text">12.83 BAT</span></div>
|
||||
</div>
|
||||
{
|
||||
inputLabel && input
|
||||
? (
|
||||
<div className="swap__summary-wrapper">
|
||||
<div>You are selling <span className="swap__highlight-text">{`${input} ${inputLabel}`}</span></div>
|
||||
<div>You will receive between <span className="swap__highlight-text">12.80</span> and <span
|
||||
className="swap__highlight-text">12.83 BAT</span></div>
|
||||
</div>
|
||||
)
|
||||
: null
|
||||
}
|
||||
</div>
|
||||
<button
|
||||
className={classnames('swap__cta-btn', {
|
||||
'swap--inactive': !this.props.isConnected,
|
||||
'swap__cta-btn--inactive': !this.props.isValid,
|
||||
})}
|
||||
disabled={!this.props.isValid}
|
||||
onClick={this.onSend}
|
||||
>
|
||||
Send
|
||||
</button>
|
||||
@ -77,10 +427,38 @@ export default withRouter(
|
||||
drizzleConnect(
|
||||
Send,
|
||||
(state, ownProps) => ({
|
||||
// React Router
|
||||
push: ownProps.history.push,
|
||||
pathname: ownProps.location.pathname,
|
||||
|
||||
// From Drizzle
|
||||
initialized: state.drizzleStatus.initialized,
|
||||
balance: state.accountBalances[state.accounts[0]] || null,
|
||||
account: state.accounts[0],
|
||||
contracts: state.contracts,
|
||||
currentAddress: state.accounts[0],
|
||||
isConnected: !!(state.drizzleStatus.initialized && state.accounts[0]),
|
||||
|
||||
// Redux Store
|
||||
balances: state.web3connect.balances,
|
||||
input: state.send.input,
|
||||
output: state.send.output,
|
||||
inputCurrency: state.send.inputCurrency,
|
||||
outputCurrency: state.send.outputCurrency,
|
||||
recipient: state.send.recipient,
|
||||
lastEditedField: state.send.lastEditedField,
|
||||
exchangeAddresses: state.addresses.exchangeAddresses,
|
||||
isValid: isValidSend(state),
|
||||
inputErrors: state.send.inputErrors,
|
||||
outputErrors: state.send.outputErrors,
|
||||
}),
|
||||
dispatch => ({
|
||||
updateField: (name, value) => dispatch(updateField({ name, value })),
|
||||
addError: (name, value) => dispatch(addError({ name, value })),
|
||||
removeError: (name, value) => dispatch(removeError({ name, value })),
|
||||
resetSend: () => dispatch(resetSend()),
|
||||
selectors: () => dispatch(selectors()),
|
||||
sync: () => dispatch(sync()),
|
||||
})
|
||||
),
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user