Implement Add Liquidity (#77)
* CSS fixes * Add Liquidity UI and Validation * Finish Add Liquidity
This commit is contained in:
parent
a4e0d11cef
commit
509ddaeaa0
@ -2,25 +2,26 @@
|
|||||||
|
|
||||||
.currency-input-panel {
|
.currency-input-panel {
|
||||||
@extend %col-nowrap;
|
@extend %col-nowrap;
|
||||||
|
box-shadow: 0 4px 8px 0 rgba($royal-blue, 0.1);
|
||||||
|
position: relative;
|
||||||
|
border-radius: 1.25rem;
|
||||||
|
z-index: 200;
|
||||||
|
|
||||||
&__container {
|
&__container {
|
||||||
position: relative;
|
|
||||||
z-index: 200;
|
|
||||||
border-radius: 1.25rem;
|
border-radius: 1.25rem;
|
||||||
border: 0.5px solid $mercury-gray;
|
box-shadow: 0 0 0 .5px $mercury-gray;
|
||||||
background-color: $white;
|
background-color: $white;
|
||||||
box-shadow: 0px 4px 4px 2px rgba($royal-blue, 0.05);
|
|
||||||
|
|
||||||
&--error {
|
&--error {
|
||||||
border: 0.5px solid $salmon-red;
|
box-shadow: 0 0 0 .5px $salmon-red;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus-within {
|
&:focus-within {
|
||||||
border-color: $royal-blue;
|
box-shadow: 0 0 .5px .5px $malibu-blue;
|
||||||
}
|
}
|
||||||
|
|
||||||
&--error:focus-within {
|
&--error:focus-within {
|
||||||
border-color: $salmon-red;
|
box-shadow: 0 0 .5px .5px $salmon-red;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,6 +94,12 @@
|
|||||||
background-image: url(../../assets/images/dropdown.svg);
|
background-image: url(../../assets/images/dropdown.svg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&--disabled {
|
||||||
|
.currency-input-panel__dropdown-icon {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__sub-currency-select {
|
&__sub-currency-select {
|
||||||
|
@ -45,10 +45,13 @@ class CurrencyInputPanel extends Component {
|
|||||||
selectedTokens: PropTypes.array.isRequired,
|
selectedTokens: PropTypes.array.isRequired,
|
||||||
errorMessage: PropTypes.string,
|
errorMessage: PropTypes.string,
|
||||||
selectedTokenAddress: PropTypes.string,
|
selectedTokenAddress: PropTypes.string,
|
||||||
|
disableTokenSelect: PropTypes.bool,
|
||||||
|
filteredTokens: PropTypes.arrayOf(PropTypes.string),
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
selectedTokens: [],
|
selectedTokens: [],
|
||||||
|
filteredTokens: [],
|
||||||
onCurrencySelected() {},
|
onCurrencySelected() {},
|
||||||
onValueChange() {},
|
onValueChange() {},
|
||||||
selectedTokenAddress: '',
|
selectedTokenAddress: '',
|
||||||
@ -64,19 +67,20 @@ class CurrencyInputPanel extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
createTokenList = () => {
|
createTokenList = () => {
|
||||||
|
const { filteredTokens } = this.props;
|
||||||
let tokens = this.props.tokenAddresses.addresses;
|
let tokens = this.props.tokenAddresses.addresses;
|
||||||
let tokenList = [ { value: 'ETH', label: 'ETH', address: 'ETH' } ];
|
let tokenList = [ { value: 'ETH', label: 'ETH', address: 'ETH' } ];
|
||||||
|
|
||||||
for (let i = 0; i < tokens.length; i++) {
|
for (let i = 0; i < tokens.length; i++) {
|
||||||
let entry = { value: '', label: '' };
|
let entry = { value: '', label: '' };
|
||||||
entry.value = tokens[i][0];
|
entry.value = tokens[i][0];
|
||||||
entry.label = tokens[i][0];
|
entry.label = tokens[i][0];
|
||||||
entry.address = tokens[i][1];
|
entry.address = tokens[i][1];
|
||||||
tokenList.push(entry);
|
tokenList.push(entry);
|
||||||
TOKEN_ADDRESS_TO_LABEL[tokens[i][1]] = tokens[i][0];
|
TOKEN_ADDRESS_TO_LABEL[tokens[i][1]] = tokens[i][0];
|
||||||
}
|
}
|
||||||
|
|
||||||
return tokenList;
|
return tokenList.filter(({ address }) => !filteredTokens.includes(address));
|
||||||
};
|
};
|
||||||
|
|
||||||
onTokenSelect = (address) => {
|
onTokenSelect = (address) => {
|
||||||
@ -122,7 +126,12 @@ class CurrencyInputPanel extends Component {
|
|||||||
renderTokenList() {
|
renderTokenList() {
|
||||||
const tokens = this.createTokenList();
|
const tokens = this.createTokenList();
|
||||||
const { searchQuery } = this.state;
|
const { searchQuery } = this.state;
|
||||||
const { selectedTokens } = this.props;
|
const { selectedTokens, disableTokenSelect } = this.props;
|
||||||
|
|
||||||
|
if (disableTokenSelect) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let results;
|
let results;
|
||||||
|
|
||||||
if (!searchQuery) {
|
if (!searchQuery) {
|
||||||
@ -196,6 +205,7 @@ class CurrencyInputPanel extends Component {
|
|||||||
value,
|
value,
|
||||||
onValueChange,
|
onValueChange,
|
||||||
selectedTokenAddress,
|
selectedTokenAddress,
|
||||||
|
disableTokenSelect,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -232,8 +242,13 @@ class CurrencyInputPanel extends Component {
|
|||||||
<button
|
<button
|
||||||
className={classnames("currency-input-panel__currency-select", {
|
className={classnames("currency-input-panel__currency-select", {
|
||||||
'currency-input-panel__currency-select--selected': selectedTokenAddress,
|
'currency-input-panel__currency-select--selected': selectedTokenAddress,
|
||||||
|
'currency-input-panel__currency-select--disabled': disableTokenSelect,
|
||||||
})}
|
})}
|
||||||
onClick={() => this.setState({ isShowingModal: true })}
|
onClick={() => {
|
||||||
|
if (!disableTokenSelect) {
|
||||||
|
this.setState({ isShowingModal: true });
|
||||||
|
}
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
selectedTokenAddress
|
selectedTokenAddress
|
||||||
|
@ -6,14 +6,14 @@
|
|||||||
height: 3rem;
|
height: 3rem;
|
||||||
background-color: $concrete-gray;
|
background-color: $concrete-gray;
|
||||||
border-radius: 3rem;
|
border-radius: 3rem;
|
||||||
border: 1px solid $mercury-gray;
|
box-shadow: 0 0 0 .5px darken($concrete-gray, 5);
|
||||||
|
|
||||||
.tab:first-child {
|
.tab:first-child {
|
||||||
margin-left: -1px;
|
//margin-left: -1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab:last-child {
|
.tab:last-child {
|
||||||
margin-right: -1px;
|
//margin-right: -1px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,7 +24,6 @@
|
|||||||
height: 3rem;
|
height: 3rem;
|
||||||
flex: 1 0 auto;
|
flex: 1 0 auto;
|
||||||
border-radius: 3rem;
|
border-radius: 3rem;
|
||||||
border: 1px solid transparent;
|
|
||||||
transition: 300ms ease-in-out;
|
transition: 300ms ease-in-out;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
@ -36,7 +35,7 @@
|
|||||||
&--selected {
|
&--selected {
|
||||||
background-color: $white;
|
background-color: $white;
|
||||||
border-radius: 3rem;
|
border-radius: 3rem;
|
||||||
border: 1px solid $mercury-gray;
|
box-shadow: 0 0 .5px .5px $mercury-gray;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
|
||||||
span {
|
span {
|
||||||
|
@ -17,6 +17,7 @@ export const ADD_CONTRACT = 'web3connect/addContract';
|
|||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
web3: null,
|
web3: null,
|
||||||
|
initialized: false,
|
||||||
account: '',
|
account: '',
|
||||||
balances: {
|
balances: {
|
||||||
ethereum: {},
|
ethereum: {},
|
||||||
@ -39,7 +40,7 @@ export const selectors = () => (dispatch, getState) => {
|
|||||||
return {
|
return {
|
||||||
getBalance: address => {
|
getBalance: address => {
|
||||||
const balance = state.balances.ethereum[address];
|
const balance = state.balances.ethereum[address];
|
||||||
console.log({balance})
|
|
||||||
if (!balance) {
|
if (!balance) {
|
||||||
dispatch(watchBalance({ balanceOf: address }));
|
dispatch(watchBalance({ balanceOf: address }));
|
||||||
return Balance(0, 'ETH');
|
return Balance(0, 'ETH');
|
||||||
@ -98,7 +99,6 @@ export const initialize = () => (dispatch, getState) => {
|
|||||||
|
|
||||||
if (typeof window.web3 !== 'undefined') {
|
if (typeof window.web3 !== 'undefined') {
|
||||||
const web3 = new Web3(window.web3.currentProvider);
|
const web3 = new Web3(window.web3.currentProvider);
|
||||||
await window.ethereum.enable();
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: INITIALIZE,
|
type: INITIALIZE,
|
||||||
payload: web3,
|
payload: web3,
|
||||||
@ -108,28 +108,38 @@ export const initialize = () => (dispatch, getState) => {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
export const watchBalance = ({ balanceOf, tokenAddress }) => {
|
export const watchBalance = ({ balanceOf, tokenAddress }) => (dispatch, getState) => {
|
||||||
if (!balanceOf) {
|
if (!balanceOf) {
|
||||||
return { type: '' };
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { web3connect } = getState();
|
||||||
|
const { watched } = web3connect;
|
||||||
|
|
||||||
if (!tokenAddress) {
|
if (!tokenAddress) {
|
||||||
return {
|
if (watched.balances.ethereum.includes(balanceOf)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dispatch({
|
||||||
type: WATCH_ETH_BALANCE,
|
type: WATCH_ETH_BALANCE,
|
||||||
payload: balanceOf,
|
payload: balanceOf,
|
||||||
};
|
});
|
||||||
} else if (tokenAddress) {
|
} else if (tokenAddress) {
|
||||||
return {
|
if (watched.balances[tokenAddress] && watched.balances[tokenAddress].includes(balanceOf)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dispatch({
|
||||||
type: WATCH_TOKEN_BALANCE,
|
type: WATCH_TOKEN_BALANCE,
|
||||||
payload: {
|
payload: {
|
||||||
tokenAddress,
|
tokenAddress,
|
||||||
balanceOf,
|
balanceOf,
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export const sync = () => async (dispatch, getState) => {
|
export const sync = () => async (dispatch, getState) => {
|
||||||
|
const { getBalance, getTokenBalance } = dispatch(selectors());
|
||||||
const web3 = await dispatch(initialize());
|
const web3 = await dispatch(initialize());
|
||||||
const {
|
const {
|
||||||
account,
|
account,
|
||||||
@ -142,12 +152,17 @@ export const sync = () => async (dispatch, getState) => {
|
|||||||
if (account !== accounts[0]) {
|
if (account !== accounts[0]) {
|
||||||
dispatch({ type: UPDATE_ACCOUNT, payload: accounts[0] });
|
dispatch({ type: UPDATE_ACCOUNT, payload: accounts[0] });
|
||||||
dispatch(watchBalance({ balanceOf: accounts[0] }));
|
dispatch(watchBalance({ balanceOf: accounts[0] }));
|
||||||
// dispatch(watchBalance({ balanceOf: accounts[0], tokenAddress: '0xDA5B056Cfb861282B4b59d29c9B395bcC238D29B' }));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync Ethereum Balances
|
// Sync Ethereum Balances
|
||||||
watched.balances.ethereum.forEach(async address => {
|
watched.balances.ethereum.forEach(async address => {
|
||||||
const balance = await web3.eth.getBalance(address);
|
const balance = await web3.eth.getBalance(address);
|
||||||
|
const { value } = getBalance(address);
|
||||||
|
|
||||||
|
|
||||||
|
if (value.isEqualTo(BN(balance))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
dispatch({
|
dispatch({
|
||||||
type: UPDATE_ETH_BALANCE,
|
type: UPDATE_ETH_BALANCE,
|
||||||
payload: {
|
payload: {
|
||||||
@ -178,9 +193,16 @@ export const sync = () => async (dispatch, getState) => {
|
|||||||
|
|
||||||
const watchlist = watched.balances[tokenAddress] || [];
|
const watchlist = watched.balances[tokenAddress] || [];
|
||||||
watchlist.forEach(async address => {
|
watchlist.forEach(async address => {
|
||||||
|
const tokenBalance = getTokenBalance(tokenAddress, address);
|
||||||
const balance = await contract.methods.balanceOf(address).call();
|
const balance = await contract.methods.balanceOf(address).call();
|
||||||
const decimals = await contract.methods.decimals().call();
|
const decimals = tokenBalance.decimals || await contract.methods.decimals().call();
|
||||||
const symbol = await contract.methods.symbol().call();
|
const symbol = tokenBalance.label || await contract.methods.symbol().call();
|
||||||
|
|
||||||
|
if (tokenBalance.value.isEqualTo(BN(balance))) {
|
||||||
|
console.log('block');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: UPDATE_TOKEN_BALANCE,
|
type: UPDATE_TOKEN_BALANCE,
|
||||||
payload: {
|
payload: {
|
||||||
@ -200,7 +222,6 @@ export const startWatching = () => async (dispatch, getState) => {
|
|||||||
: 5000;
|
: 5000;
|
||||||
|
|
||||||
dispatch(sync());
|
dispatch(sync());
|
||||||
|
|
||||||
setTimeout(() => dispatch(startWatching()), timeout);
|
setTimeout(() => dispatch(startWatching()), timeout);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ window.addEventListener('load', function() {
|
|||||||
<DrizzleProvider options={{
|
<DrizzleProvider options={{
|
||||||
contracts: [],
|
contracts: [],
|
||||||
events: [],
|
events: [],
|
||||||
polls: { accounts: 3000, blocks: 3000 },
|
polls: { accounts: 60000, blocks: 60000 },
|
||||||
}} store={store}>
|
}} store={store}>
|
||||||
<App />
|
<App />
|
||||||
</DrizzleProvider>
|
</DrizzleProvider>
|
||||||
|
@ -8,7 +8,9 @@ import { selectors, sync } from '../../ducks/web3connect';
|
|||||||
import ArrowDown from '../../assets/images/arrow-down-blue.svg';
|
import ArrowDown from '../../assets/images/arrow-down-blue.svg';
|
||||||
import ModeSelector from './ModeSelector';
|
import ModeSelector from './ModeSelector';
|
||||||
import {BigNumber as BN} from 'bignumber.js';
|
import {BigNumber as BN} from 'bignumber.js';
|
||||||
|
import EXCHANGE_ABI from '../../abi/exchange';
|
||||||
import "./pool.scss";
|
import "./pool.scss";
|
||||||
|
import promisify from "../../helpers/web3-promisfy";
|
||||||
|
|
||||||
const INPUT = 0;
|
const INPUT = 0;
|
||||||
const OUTPUT = 1;
|
const OUTPUT = 1;
|
||||||
@ -18,6 +20,7 @@ class AddLiquidity extends Component {
|
|||||||
isConnected: PropTypes.bool.isRequired,
|
isConnected: PropTypes.bool.isRequired,
|
||||||
account: PropTypes.string.isRequired,
|
account: PropTypes.string.isRequired,
|
||||||
selectors: PropTypes.func.isRequired,
|
selectors: PropTypes.func.isRequired,
|
||||||
|
balances: PropTypes.object.isRequired,
|
||||||
exchangeAddresses: PropTypes.shape({
|
exchangeAddresses: PropTypes.shape({
|
||||||
fromToken: PropTypes.object.isRequired,
|
fromToken: PropTypes.object.isRequired,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
@ -26,11 +29,27 @@ class AddLiquidity extends Component {
|
|||||||
state = {
|
state = {
|
||||||
inputValue: '',
|
inputValue: '',
|
||||||
outputValue: '',
|
outputValue: '',
|
||||||
inputCurrency: '',
|
inputCurrency: 'ETH',
|
||||||
outputCurrency: '',
|
outputCurrency: '',
|
||||||
lastEditedField: '',
|
lastEditedField: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
|
const { isConnected, account, exchangeAddresses, balances, web3 } = this.props;
|
||||||
|
const { inputValue, outputValue, inputCurrency, outputCurrency, lastEditedField } = this.state;
|
||||||
|
|
||||||
|
return isConnected !== nextProps.isConnected ||
|
||||||
|
account !== nextProps.account ||
|
||||||
|
exchangeAddresses !== nextProps.exchangeAddresses ||
|
||||||
|
web3 !== nextProps.web3 ||
|
||||||
|
balances !== nextProps.balances ||
|
||||||
|
inputValue !== nextState.inputValue ||
|
||||||
|
outputValue !== nextState.outputValue ||
|
||||||
|
inputCurrency !== nextState.inputCurrency ||
|
||||||
|
outputCurrency !== nextState.outputCurrency ||
|
||||||
|
lastEditedField !== nextState.lastEditedField;
|
||||||
|
}
|
||||||
|
|
||||||
getBalance(currency) {
|
getBalance(currency) {
|
||||||
const { selectors, account } = this.props;
|
const { selectors, account } = this.props;
|
||||||
|
|
||||||
@ -47,6 +66,34 @@ class AddLiquidity extends Component {
|
|||||||
return `Balance: ${value.dividedBy(10 ** decimals).toFixed(4)}`;
|
return `Balance: ${value.dividedBy(10 ** decimals).toFixed(4)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onAddLiquidity = async () => {
|
||||||
|
const { account, web3, exchangeAddresses: { fromToken }, selectors } = this.props;
|
||||||
|
const { inputValue, outputValue, outputCurrency } = this.state;
|
||||||
|
const exchange = new web3.eth.Contract(EXCHANGE_ABI, fromToken[outputCurrency]);
|
||||||
|
|
||||||
|
const ethAmount = BN(inputValue).multipliedBy(10 ** 18);
|
||||||
|
const { decimals } = selectors().getTokenBalance(outputCurrency, fromToken[outputCurrency]);
|
||||||
|
const tokenAmount = BN(outputValue).multipliedBy(10 ** decimals);
|
||||||
|
const { value: ethReserve } = selectors().getBalance(fromToken[outputCurrency]);
|
||||||
|
const totalLiquidity = await exchange.methods.totalSupply().call();
|
||||||
|
const liquidityMinted = BN(totalLiquidity).multipliedBy(ethAmount.dividedBy(ethReserve));
|
||||||
|
const blockNumber = await promisify(web3, 'getBlockNumber');
|
||||||
|
const block = await promisify(web3, 'getBlock', blockNumber);
|
||||||
|
const deadline = block.timestamp + 300;
|
||||||
|
const MAX_LIQUIDITY_SLIPPAGE = 0.025;
|
||||||
|
const minLiquidity = liquidityMinted.multipliedBy(1 - MAX_LIQUIDITY_SLIPPAGE);
|
||||||
|
const maxTokens = tokenAmount.multipliedBy(1 + MAX_LIQUIDITY_SLIPPAGE);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const tx = await exchange.methods.addLiquidity(minLiquidity.toFixed(0), maxTokens.toFixed(0), deadline).send({
|
||||||
|
from: account,
|
||||||
|
value: ethAmount.toFixed(0)
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
onInputChange = value => {
|
onInputChange = value => {
|
||||||
const { inputCurrency, outputCurrency } = this.state;
|
const { inputCurrency, outputCurrency } = this.state;
|
||||||
const exchangeRate = this.getExchangeRate();
|
const exchangeRate = this.getExchangeRate();
|
||||||
@ -103,6 +150,39 @@ class AddLiquidity extends Component {
|
|||||||
return tokenValue.dividedBy(ethValue);
|
return tokenValue.dividedBy(ethValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validate() {
|
||||||
|
const { selectors, account } = this.props;
|
||||||
|
const {
|
||||||
|
inputValue, outputValue,
|
||||||
|
inputCurrency, outputCurrency,
|
||||||
|
} = this.state;
|
||||||
|
|
||||||
|
let inputError;
|
||||||
|
let outputError;
|
||||||
|
let isValid = true;
|
||||||
|
|
||||||
|
if (!inputValue || !outputValue || !inputCurrency || !outputCurrency) {
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { value: ethValue } = selectors().getBalance(account);
|
||||||
|
const { value: tokenValue, decimals } = selectors().getTokenBalance(outputCurrency, account);
|
||||||
|
|
||||||
|
if (ethValue.isLessThan(BN(inputValue * 10 ** 18))) {
|
||||||
|
inputError = 'Insufficient Balance';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokenValue.isLessThan(BN(outputValue * 10 ** decimals))) {
|
||||||
|
outputError = 'Insufficient Balance';
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
inputError,
|
||||||
|
outputError,
|
||||||
|
isValid: isValid && !inputError && !outputError,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
renderInfo() {
|
renderInfo() {
|
||||||
const { selectors, exchangeAddresses: { fromToken } } = this.props;
|
const { selectors, exchangeAddresses: { fromToken } } = this.props;
|
||||||
const { inputCurrency, outputCurrency } = this.state;
|
const { inputCurrency, outputCurrency } = this.state;
|
||||||
@ -136,11 +216,71 @@ class AddLiquidity extends Component {
|
|||||||
<div className="pool__summary-panel">
|
<div className="pool__summary-panel">
|
||||||
<div className="pool__exchange-rate-wrapper">
|
<div className="pool__exchange-rate-wrapper">
|
||||||
<span className="pool__exchange-rate">Exchange Rate</span>
|
<span className="pool__exchange-rate">Exchange Rate</span>
|
||||||
<span>{`1 ETH = ${tokenValue.dividedBy(ethValue).toFixed(4)} BAT`}</span>
|
<span>{`1 ETH = ${tokenValue.dividedBy(ethValue).toFixed(4)} ${label}`}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="pool__exchange-rate-wrapper">
|
<div className="pool__exchange-rate-wrapper">
|
||||||
<span className="swap__exchange-rate">Current Pool Size</span>
|
<span className="swap__exchange-rate">Current Pool Size</span>
|
||||||
<span>{` ${ethValue.dividedBy(10 ** 18).toFixed(2)} ${eth} / ${tokenValue.dividedBy(10 ** decimals).toFixed(2)} ${label}`}</span>
|
<span>{` ${ethValue.dividedBy(10 ** 18).toFixed(2)} ${eth} + ${tokenValue.dividedBy(10 ** decimals).toFixed(2)} ${label}`}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSummary() {
|
||||||
|
const { selectors, exchangeAddresses: { fromToken } } = this.props;
|
||||||
|
const {
|
||||||
|
inputValue,
|
||||||
|
outputValue,
|
||||||
|
inputCurrency,
|
||||||
|
outputCurrency,
|
||||||
|
} = this.state;
|
||||||
|
|
||||||
|
if (!inputCurrency || !outputCurrency) {
|
||||||
|
return (
|
||||||
|
<div className="swap__summary-wrapper">
|
||||||
|
<div>Select a token to continue.</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputCurrency === outputCurrency) {
|
||||||
|
return (
|
||||||
|
<div className="swap__summary-wrapper">
|
||||||
|
<div>Must be different token.</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (![inputCurrency, outputCurrency].includes('ETH')) {
|
||||||
|
return (
|
||||||
|
<div className="swap__summary-wrapper">
|
||||||
|
<div>One of the input must be ETH.</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const { value, decimals, label } = selectors().getTokenBalance(outputCurrency, fromToken[outputCurrency]);
|
||||||
|
|
||||||
|
if (!inputValue || !outputValue) {
|
||||||
|
return (
|
||||||
|
<div className="swap__summary-wrapper">
|
||||||
|
<div>{`Enter a ${inputCurrency} or ${label} value to continue.`}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const SLIPPAGE = 0.025;
|
||||||
|
const minOutput = BN(outputValue).multipliedBy(1 - SLIPPAGE);
|
||||||
|
const maxOutput = BN(outputValue).multipliedBy(1 + SLIPPAGE);
|
||||||
|
const tokenReserve = value.dividedBy(10 ** decimals);
|
||||||
|
const minPercentage = minOutput.dividedBy(minOutput.plus(tokenReserve)).multipliedBy(100);
|
||||||
|
const maxPercentage = maxOutput.dividedBy(maxOutput.plus(tokenReserve)).multipliedBy(100);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="swap__summary-wrapper">
|
||||||
|
<div>You are adding between {b(`${minOutput.toFixed(2)} - ${maxOutput.toFixed(2)} ${label}`)} + {b(`${BN(inputValue).toFixed(2)} ETH`)} into the liquidity pool.</div>
|
||||||
|
<div className="pool__last-summary-text">
|
||||||
|
You will receive between {b(`${minPercentage.toFixed(2)}%`)} and {b(`${maxPercentage.toFixed(2)}%`)} of the {`${label}/ETH`} pool tokens.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@ -159,19 +299,23 @@ class AddLiquidity extends Component {
|
|||||||
lastEditedField,
|
lastEditedField,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
|
const { inputError, outputError, isValid } = this.validate();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classnames('swap__content', { 'swap--inactive': !isConnected })}>
|
<div
|
||||||
|
className={classnames('swap__content', {
|
||||||
|
'swap--inactive': !isConnected,
|
||||||
|
})}
|
||||||
|
>
|
||||||
<ModeSelector />
|
<ModeSelector />
|
||||||
<CurrencyInputPanel
|
<CurrencyInputPanel
|
||||||
title="Deposit"
|
title="Deposit"
|
||||||
description={lastEditedField === OUTPUT ? '(estimated)' : ''}
|
|
||||||
extraText={this.getBalance(inputCurrency)}
|
extraText={this.getBalance(inputCurrency)}
|
||||||
onCurrencySelected={currency => {
|
|
||||||
this.setState({ inputCurrency: currency });
|
|
||||||
this.props.sync();
|
|
||||||
}}
|
|
||||||
onValueChange={this.onInputChange}
|
onValueChange={this.onInputChange}
|
||||||
|
selectedTokenAddress="ETH"
|
||||||
value={inputValue}
|
value={inputValue}
|
||||||
|
errorMessage={inputError}
|
||||||
|
disableTokenSelect
|
||||||
/>
|
/>
|
||||||
<OversizedPanel>
|
<OversizedPanel>
|
||||||
<div className="swap__down-arrow-background">
|
<div className="swap__down-arrow-background">
|
||||||
@ -180,32 +324,32 @@ class AddLiquidity extends Component {
|
|||||||
</OversizedPanel>
|
</OversizedPanel>
|
||||||
<CurrencyInputPanel
|
<CurrencyInputPanel
|
||||||
title="Deposit"
|
title="Deposit"
|
||||||
description={lastEditedField === INPUT ? '(estimated)' : ''}
|
description="(estimated)"
|
||||||
extraText={this.getBalance(outputCurrency)}
|
extraText={this.getBalance(outputCurrency)}
|
||||||
|
selectedTokenAddress={outputCurrency}
|
||||||
onCurrencySelected={currency => {
|
onCurrencySelected={currency => {
|
||||||
this.setState({ outputCurrency: currency });
|
this.setState({ outputCurrency: currency });
|
||||||
this.props.sync();
|
this.props.sync();
|
||||||
}}
|
}}
|
||||||
onValueChange={this.onOutputChange}
|
onValueChange={this.onOutputChange}
|
||||||
value={outputValue}
|
value={outputValue}
|
||||||
|
errorMessage={outputError}
|
||||||
|
filteredTokens={[ 'ETH' ]}
|
||||||
/>
|
/>
|
||||||
<OversizedPanel hideBottom>
|
<OversizedPanel hideBottom>
|
||||||
{ this.renderInfo() }
|
{ this.renderInfo() }
|
||||||
</OversizedPanel>
|
</OversizedPanel>
|
||||||
<div className="swap__summary-wrapper">
|
{ this.renderSummary() }
|
||||||
<div>You are adding between {b`212000.00 - 216000.00 BAT`} + {b`166.683543 ETH`} into the liquidity pool.</div>
|
|
||||||
<div className="pool__last-summary-text">You will receive between {b`66%`} and {b`67%`} of the BAT/ETH pool tokens.</div>
|
|
||||||
</div>
|
|
||||||
<div className="pool__cta-container">
|
<div className="pool__cta-container">
|
||||||
<button
|
<button
|
||||||
className={classnames('pool__cta-btn', {
|
className={classnames('pool__cta-btn', {
|
||||||
'swap--inactive': !this.props.isConnected,
|
'swap--inactive': !this.props.isConnected,
|
||||||
'pool__cta-btn--inactive': !this.props.isValid,
|
'pool__cta-btn--inactive': !isValid,
|
||||||
})}
|
})}
|
||||||
disabled={!this.props.isValid}
|
disabled={!isValid}
|
||||||
onClick={this.onSwap}
|
onClick={this.onAddLiquidity}
|
||||||
>
|
>
|
||||||
Swap
|
Add Liquidity
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -219,6 +363,7 @@ export default drizzleConnect(
|
|||||||
isConnected: Boolean(state.web3connect.account),
|
isConnected: Boolean(state.web3connect.account),
|
||||||
account: state.web3connect.account,
|
account: state.web3connect.account,
|
||||||
balances: state.web3connect.balances,
|
balances: state.web3connect.balances,
|
||||||
|
web3: state.web3connect.web3,
|
||||||
exchangeAddresses: state.addresses.exchangeAddresses,
|
exchangeAddresses: state.addresses.exchangeAddresses,
|
||||||
}),
|
}),
|
||||||
dispatch => ({
|
dispatch => ({
|
||||||
|
@ -11,6 +11,7 @@ $mine-shaft-gray: #2B2B2B;
|
|||||||
|
|
||||||
// Blue
|
// Blue
|
||||||
$zumthor-blue: #EBF4FF;
|
$zumthor-blue: #EBF4FF;
|
||||||
|
$malibu-blue: #5CA2FF;
|
||||||
$royal-blue: #2F80ED;
|
$royal-blue: #2F80ED;
|
||||||
|
|
||||||
// Purple
|
// Purple
|
||||||
|
Loading…
Reference in New Issue
Block a user