Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
I
interface
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
LuckySwap
interface
Commits
5987095e
Unverified
Commit
5987095e
authored
Oct 26, 2018
by
Kenny Tran
Committed by
GitHub
Oct 26, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #84 from kennyt/refactor-send
Refactor send to use web3connect
parents
a28d0f0b
09e70021
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
515 additions
and
354 deletions
+515
-354
index.js
src/pages/Send/index.js
+515
-354
No files found.
src/pages/Send/index.js
View file @
5987095e
import
React
,
{
Component
}
from
'
react
'
;
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
{
selectors
}
from
'
../../ducks/web3connect
'
;
import
Header
from
'
../../components/Header
'
;
import
CurrencyInputPanel
from
'
../../components/CurrencyInputPanel
'
;
import
AddressInputPanel
from
'
../../components/AddressInputPanel
'
;
import
CurrencyInputPanel
from
'
../../components/CurrencyInputPanel
'
;
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
EXCHANGE_ABI
from
'
../../abi/exchange
'
;
import
"
./send.scss
"
;
import
promisify
from
"
../../helpers/web3-promisfy
"
;
const
INPUT
=
0
;
const
OUTPUT
=
1
;
class
Send
extends
Component
{
static
propTypes
=
{
// Injected by React Router Dom
push
:
PropTypes
.
func
.
isRequired
,
pathname
:
PropTypes
.
string
.
isRequired
,
currentAddress
:
PropTypes
.
string
,
account
:
PropTypes
.
string
,
isConnected
:
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
,
};
static
contextTypes
=
{
drizzle
:
PropTypes
.
object
,
selectors
:
PropTypes
.
func
.
isRequired
,
web3
:
PropTypes
.
object
.
isRequired
,
};
state
=
{
exchangeRate
:
BN
(
0
),
approvalTxId
:
null
,
sendTxId
:
null
,
inputValue
:
''
,
outputValue
:
''
,
inputCurrency
:
''
,
outputCurrency
:
''
,
inputAmountB
:
''
,
lastEditedField
:
''
,
recipient
:
''
,
};
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
)}
`
);
}
});
return
true
;
}
componentWillUnmount
()
{
this
.
resetSend
();
reset
()
{
this
.
setState
({
inputValue
:
''
,
outputValue
:
''
,
inputCurrency
:
''
,
outputCurrency
:
''
,
inputAmountB
:
''
,
lastEditedField
:
''
,
recipient
:
''
,
});
}
resetSend
()
{
this
.
props
.
resetSend
();
this
.
setState
({
approvalTxId
:
null
,
sendTxId
:
null
});
componentWillReceiveProps
()
{
this
.
recalcForm
();
}
getSendStatus
()
{
const
{
drizzle
}
=
this
.
context
;
validate
()
{
const
{
selectors
,
account
}
=
this
.
props
;
const
{
inputValue
,
outputValue
,
inputCurrency
,
outputCurrency
,
recipient
,
}
=
this
.
state
;
return
getTxStatus
({
drizzleCtx
:
drizzle
,
txId
:
this
.
state
.
sendTxId
,
});
}
let
inputError
=
''
;
let
outputError
=
''
;
let
isValid
=
true
;
getTokenLabel
(
address
)
{
if
(
address
===
'
ETH
'
)
{
return
'
ETH
'
;
if
(
!
inputValue
||
!
outputValue
||
!
inputCurrency
||
!
outputCurrency
||
!
recipient
)
{
isValid
=
false
;
}
const
{
initialized
,
contracts
,
}
=
this
.
props
;
const
{
drizzle
}
=
this
.
context
;
const
{
web3
}
=
drizzle
;
const
{
value
:
inputBalance
,
decimals
:
inputDecimals
}
=
selectors
().
getBalance
(
account
,
inputCurrency
);
if
(
!
initialized
||
!
web3
||
!
address
)
{
return
'
'
;
if
(
inputBalance
.
isLessThan
(
BN
(
inputValue
*
10
**
inputDecimals
))
)
{
inputError
=
'
Insufficient Balance
'
;
}
const
symbolKey
=
drizzle
.
contracts
[
address
].
methods
.
symbol
.
cacheCall
();
const
token
=
contracts
[
address
];
const
symbol
=
token
.
symbol
[
symbolKey
];
if
(
!
symbol
)
{
return
''
;
if
(
inputValue
===
'
N/A
'
)
{
inputError
=
'
Not a valid input value
'
;
}
return
symbol
.
value
;
return
{
inputError
,
outputError
,
isValid
:
isValid
&&
!
inputError
&&
!
outputError
,
};
}
getBalance
(
currency
)
{
const
{
selectors
,
account
}
=
this
.
props
;
recalcForm
(
)
{
const
{
inputCurrency
,
outputCurrency
}
=
this
.
state
;
if
(
!
c
urrency
)
{
return
''
;
if
(
!
inputCurrency
||
!
outputC
urrency
)
{
return
;
}
if
(
currency
===
'
ETH
'
)
{
const
{
value
,
decimals
}
=
selectors
().
getBalance
(
account
);
return
`Balance:
${
value
.
dividedBy
(
10
**
decimals
).
toFixed
(
4
)}
`
;
if
(
inputCurrency
===
outputCurrency
)
{
this
.
setState
({
inputValue
:
''
,
outputValue
:
''
,
});
return
;
}
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
'
,
''
);
if
(
inputCurrency
!==
'
ETH
'
&&
outputCurrency
!==
'
ETH
'
)
{
this
.
recalcTokenTokenForm
();
return
;
}
this
.
props
.
updateField
(
'
lastEditedField
'
,
'
input
'
);
}
updateOutput
(
amount
)
{
this
.
props
.
updateField
(
'
output
'
,
amount
);
if
(
!
amount
)
{
this
.
props
.
updateField
(
'
input
'
,
''
);
}
this
.
props
.
updateField
(
'
lastEditedField
'
,
'
output
'
);
this
.
recalcEthTokenForm
();
}
async
getExchangeRate
(
props
)
{
recalcTokenTokenForm
=
()
=>
{
const
{
input
,
output
,
exchangeAddresses
:
{
fromToken
},
selectors
,
}
=
this
.
props
;
const
{
inputValue
:
oldInputValue
,
outputValue
:
oldOutputValue
,
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
,
})
;
}
exchangeRate
:
oldExchangeRate
,
inputAmountB
:
oldInputAmountB
,
}
=
this
.
state
;
const
exchangeAddressA
=
fromToken
[
inputCurrency
];
const
exchangeAddressB
=
fromToken
[
outputCurrency
];
const
{
value
:
inputReserveA
,
decimals
:
inputDecimalsA
}
=
selectors
().
getBalance
(
exchangeAddressA
,
inputCurrency
);
const
{
value
:
outputReserveA
}
=
selectors
().
getBalance
(
exchangeAddressA
,
'
ETH
'
);
const
{
value
:
inputReserveB
}
=
selectors
().
getBalance
(
exchangeAddressB
,
'
ETH
'
);
const
{
value
:
outputReserveB
,
decimals
:
outputDecimalsB
}
=
selectors
().
getBalance
(
exchangeAddressB
,
outputCurrency
);
if
(
lastEditedField
===
INPUT
)
{
if
(
!
oldInputValue
)
{
return
this
.
setState
({
outputValue
:
''
,
exchangeRate
:
BN
(
0
),
});
}
const
inputAmountA
=
BN
(
oldInputValue
).
multipliedBy
(
10
**
inputDecimalsA
);
const
outputAmountA
=
calculateEtherTokenOutput
({
inputAmount
:
inputAmountA
,
inputReserve
:
inputReserveA
,
outputReserve
:
outputReserveA
,
});
// Redundant Variable for readability of the formala
// OutputAmount from the first send becomes InputAmount of the second send
const
inputAmountB
=
outputAmountA
;
const
outputAmountB
=
calculateEtherTokenOutput
({
inputAmount
:
inputAmountB
,
inputReserve
:
inputReserveB
,
outputReserve
:
outputReserveB
,
});
getIsUnapproved
()
{
const
{
input
,
inputCurrency
,
account
,
contracts
,
exchangeAddresses
}
=
this
.
props
;
const
{
drizzle
}
=
this
.
context
;
const
exchangeRate
=
outputAmountB
.
dividedBy
(
inputAmountA
);
const
outputValue
=
outputAmountB
.
dividedBy
(
BN
(
10
**
outputDecimalsB
)).
toFixed
(
7
);
return
isExchangeUnapproved
({
value
:
input
,
currency
:
inputCurrency
,
drizzleCtx
:
drizzle
,
contractStore
:
contracts
,
account
,
exchangeAddresses
,
});
}
const
appendState
=
{};
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
,
if
(
!
exchangeRate
.
isEqualTo
(
BN
(
oldExchangeRate
)))
{
appendState
.
exchangeRate
=
exchangeRate
;
}
if
(
outputValue
!==
oldOutputValue
)
{
appendState
.
outputValue
=
outputValue
;
}
this
.
setState
(
appendState
);
}
if
(
lastEditedField
===
OUTPUT
)
{
if
(
!
oldOutputValue
)
{
return
this
.
setState
({
inputValue
:
''
,
exchangeRate
:
BN
(
0
),
});
}
const
outputAmountB
=
BN
(
oldOutputValue
).
multipliedBy
(
10
**
outputDecimalsB
);
const
inputAmountB
=
calculateEtherTokenInput
({
outputAmount
:
outputAmountB
,
inputReserve
:
inputReserveB
,
outputReserve
:
outputReserveB
,
});
// Redundant Variable for readability of the formala
// InputAmount from the first send becomes OutputAmount of the second send
const
outputAmountA
=
inputAmountB
;
const
inputAmountA
=
calculateEtherTokenInput
({
outputAmount
:
outputAmountA
,
inputReserve
:
inputReserveA
,
outputReserve
:
outputReserveA
,
});
this
.
setState
({
approvalTxId
})
const
exchangeRate
=
outputAmountB
.
dividedBy
(
inputAmountA
);
const
inputValue
=
inputAmountA
.
isNegative
()
?
'
N/A
'
:
inputAmountA
.
dividedBy
(
BN
(
10
**
inputDecimalsA
)).
toFixed
(
7
);
const
appendState
=
{};
if
(
!
exchangeRate
.
isEqualTo
(
BN
(
oldExchangeRate
)))
{
appendState
.
exchangeRate
=
exchangeRate
;
}
if
(
inputValue
!==
oldInputValue
)
{
appendState
.
inputValue
=
inputValue
;
}
if
(
!
inputAmountB
.
isEqualTo
(
BN
(
oldInputAmountB
)))
{
appendState
.
inputAmountB
=
inputAmountB
;
}
this
.
setState
(
appendState
);
}
}
getApprovalStatus
()
{
const
{
drizzle
}
=
this
.
context
;
};
return
getTxStatus
(
{
drizzleCtx
:
drizzle
,
txId
:
this
.
state
.
approvalTxId
,
});
}
recalcEthTokenForm
=
()
=>
{
const
{
exchangeAddresses
:
{
fromToken
}
,
selectors
,
}
=
this
.
props
;
onSend
=
async
()
=>
{
const
{
input
,
output
,
input
Value
:
oldInputValue
,
output
Value
:
oldOutputValue
,
inputCurrency
,
outputCurrency
,
recipient
,
exchangeAddresses
,
lastEditedField
,
account
,
contracts
,
selectors
,
}
=
this
.
props
;
exchangeRate
:
oldExchangeRate
,
}
=
this
.
state
;
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
,
});
const
tokenAddress
=
[
inputCurrency
,
outputCurrency
].
filter
(
currency
=>
currency
!==
'
ETH
'
)[
0
];
const
exchangeAddress
=
fromToken
[
tokenAddress
];
if
(
!
exchangeAddress
)
{
return
;
}
if
(
lastEditedField
===
'
output
'
)
{
sendTxId
=
await
sendOutput
({
drizzleCtx
:
drizzle
,
contractStore
:
contracts
,
input
,
output
,
inputCurrency
,
outputCurrency
,
recipient
,
exchangeAddresses
,
account
,
inputDecimals
,
outputDecimals
,
});
const
{
value
:
inputReserve
,
decimals
:
inputDecimals
}
=
selectors
().
getBalance
(
exchangeAddress
,
inputCurrency
);
const
{
value
:
outputReserve
,
decimals
:
outputDecimals
}
=
selectors
().
getBalance
(
exchangeAddress
,
outputCurrency
);
if
(
lastEditedField
===
INPUT
)
{
if
(
!
oldInputValue
)
{
return
this
.
setState
({
outputValue
:
''
,
exchangeRate
:
BN
(
0
),
});
}
const
inputAmount
=
BN
(
oldInputValue
).
multipliedBy
(
10
**
inputDecimals
);
const
outputAmount
=
calculateEtherTokenOutput
({
inputAmount
,
inputReserve
,
outputReserve
});
const
exchangeRate
=
outputAmount
.
dividedBy
(
inputAmount
);
const
outputValue
=
outputAmount
.
dividedBy
(
BN
(
10
**
outputDecimals
)).
toFixed
(
7
);
const
appendState
=
{};
if
(
!
exchangeRate
.
isEqualTo
(
BN
(
oldExchangeRate
)))
{
appendState
.
exchangeRate
=
exchangeRate
;
}
if
(
outputValue
!==
oldOutputValue
)
{
appendState
.
outputValue
=
outputValue
;
}
this
.
setState
(
appendState
);
}
else
if
(
lastEditedField
===
OUTPUT
)
{
if
(
!
oldOutputValue
)
{
return
this
.
setState
({
inputValue
:
''
,
exchangeRate
:
BN
(
0
),
});
}
const
outputAmount
=
BN
(
oldOutputValue
).
multipliedBy
(
10
**
outputDecimals
);
const
inputAmount
=
calculateEtherTokenInput
({
outputAmount
,
inputReserve
,
outputReserve
});
const
exchangeRate
=
outputAmount
.
dividedBy
(
inputAmount
);
const
inputValue
=
inputAmount
.
isNegative
()
?
'
N/A
'
:
inputAmount
.
dividedBy
(
BN
(
10
**
inputDecimals
)).
toFixed
(
7
);
const
appendState
=
{};
if
(
!
exchangeRate
.
isEqualTo
(
BN
(
oldExchangeRate
)))
{
appendState
.
exchangeRate
=
exchangeRate
;
}
if
(
inputValue
!==
oldInputValue
)
{
appendState
.
inputValue
=
inputValue
;
}
this
.
setState
(
appendState
);
}
};
this
.
setState
({
sendTxId
});
updateInput
=
amount
=>
{
this
.
setState
({
inputValue
:
amount
,
lastEditedField
:
INPUT
,
},
this
.
recalcForm
);
};
handleSubButtonClick
=
()
=>
{
if
(
this
.
getIsUnapproved
()
&&
this
.
getApprovalStatus
()
!==
'
pending
'
)
{
this
.
approveExchange
();
}
}
updateOutput
=
amount
=>
{
this
.
setState
({
outputValue
:
amount
,
lastEditedField
:
OUTPUT
,
},
this
.
recalcForm
);
};
validate
()
{
onSend
=
async
()
=>
{
const
{
selectors
,
exchangeAddresses
:
{
fromToken
}
,
account
,
input
,
output
,
inputCurrency
,
outputCurrency
web3
,
selectors
,
}
=
this
.
props
;
let
inputError
;
let
isValid
=
true
;
if
(
!
input
||
!
output
||
!
inputCurrency
||
!
outputCurrency
)
{
isValid
=
false
;
const
{
inputValue
,
outputValue
,
inputCurrency
,
outputCurrency
,
inputAmountB
,
lastEditedField
,
recipient
,
}
=
this
.
state
;
const
ALLOWED_SLIPPAGE
=
0.025
;
const
TOKEN_ALLOWED_SLIPPAGE
=
0.04
;
const
type
=
getSendType
(
inputCurrency
,
outputCurrency
);
const
{
decimals
:
inputDecimals
}
=
selectors
().
getBalance
(
account
,
inputCurrency
);
const
{
decimals
:
outputDecimals
}
=
selectors
().
getBalance
(
account
,
outputCurrency
);
const
blockNumber
=
await
promisify
(
web3
,
'
getBlockNumber
'
);
const
block
=
await
promisify
(
web3
,
'
getBlock
'
,
blockNumber
);
const
deadline
=
block
.
timestamp
+
300
;
if
(
lastEditedField
===
INPUT
)
{
// send input
switch
(
type
)
{
case
'
ETH_TO_TOKEN
'
:
new
web3
.
eth
.
Contract
(
EXCHANGE_ABI
,
fromToken
[
outputCurrency
])
.
methods
.
ethToTokenTransferInput
(
BN
(
outputValue
).
multipliedBy
(
10
**
outputDecimals
).
multipliedBy
(
1
-
ALLOWED_SLIPPAGE
).
toFixed
(
0
),
deadline
,
recipient
,
)
.
send
({
from
:
account
,
value
:
BN
(
inputValue
).
multipliedBy
(
10
**
18
).
toFixed
(
0
),
},
err
=>
!
err
&&
this
.
reset
());
break
;
case
'
TOKEN_TO_ETH
'
:
new
web3
.
eth
.
Contract
(
EXCHANGE_ABI
,
fromToken
[
inputCurrency
])
.
methods
.
tokenToEthTransferInput
(
BN
(
inputValue
).
multipliedBy
(
10
**
inputDecimals
).
toFixed
(
0
),
BN
(
outputValue
).
multipliedBy
(
10
**
outputDecimals
).
multipliedBy
(
1
-
ALLOWED_SLIPPAGE
).
toFixed
(
0
),
deadline
,
recipient
,
)
.
send
({
from
:
account
,
},
err
=>
!
err
&&
this
.
reset
());
break
;
case
'
TOKEN_TO_TOKEN
'
:
new
web3
.
eth
.
Contract
(
EXCHANGE_ABI
,
fromToken
[
inputCurrency
])
.
methods
.
tokenToTokenTransferInput
(
BN
(
inputValue
).
multipliedBy
(
10
**
inputDecimals
).
toFixed
(
0
),
BN
(
outputValue
).
multipliedBy
(
10
**
outputDecimals
).
multipliedBy
(
1
-
TOKEN_ALLOWED_SLIPPAGE
).
toFixed
(
0
),
'
1
'
,
deadline
,
recipient
,
outputCurrency
,
)
.
send
({
from
:
account
,
},
err
=>
!
err
&&
this
.
reset
());
break
;
default
:
break
;
}
}
const
{
value
:
inputBalance
,
decimals
:
inputDecimals
}
=
inputCurrency
===
'
ETH
'
?
selectors
().
getBalance
(
account
)
:
selectors
().
getTokenBalance
(
inputCurrency
,
account
);
if
(
inputBalance
.
isLessThan
(
BN
(
input
*
10
**
inputDecimals
)))
{
inputError
=
'
Insufficient Balance
'
;
if
(
lastEditedField
===
OUTPUT
)
{
// send output
switch
(
type
)
{
case
'
ETH_TO_TOKEN
'
:
new
web3
.
eth
.
Contract
(
EXCHANGE_ABI
,
fromToken
[
outputCurrency
])
.
methods
.
ethToTokenTransferOutput
(
BN
(
outputValue
).
multipliedBy
(
10
**
outputDecimals
).
toFixed
(
0
),
deadline
,
recipient
,
)
.
send
({
from
:
account
,
value
:
BN
(
inputValue
).
multipliedBy
(
10
**
inputDecimals
).
multipliedBy
(
1
+
ALLOWED_SLIPPAGE
).
toFixed
(
0
),
},
err
=>
!
err
&&
this
.
reset
());
break
;
case
'
TOKEN_TO_ETH
'
:
new
web3
.
eth
.
Contract
(
EXCHANGE_ABI
,
fromToken
[
inputCurrency
])
.
methods
.
tokenToEthTransferOutput
(
BN
(
outputValue
).
multipliedBy
(
10
**
outputDecimals
).
toFixed
(
0
),
BN
(
inputValue
).
multipliedBy
(
10
**
inputDecimals
).
multipliedBy
(
1
+
ALLOWED_SLIPPAGE
).
toFixed
(
0
),
deadline
,
recipient
,
)
.
send
({
from
:
account
,
},
err
=>
!
err
&&
this
.
reset
());
break
;
case
'
TOKEN_TO_TOKEN
'
:
if
(
!
inputAmountB
)
{
return
;
}
console
.
log
(
BN
(
outputValue
).
multipliedBy
(
10
**
outputDecimals
).
toFixed
(
0
),
BN
(
inputValue
).
multipliedBy
(
10
**
inputDecimals
).
multipliedBy
(
1
+
TOKEN_ALLOWED_SLIPPAGE
).
toFixed
(
0
),
inputAmountB
.
multipliedBy
(
1.2
).
toFixed
(
0
),
deadline
,
outputCurrency
,
)
new
web3
.
eth
.
Contract
(
EXCHANGE_ABI
,
fromToken
[
inputCurrency
])
.
methods
.
tokenToTokenTransferOutput
(
BN
(
outputValue
).
multipliedBy
(
10
**
outputDecimals
).
toFixed
(
0
),
BN
(
inputValue
).
multipliedBy
(
10
**
inputDecimals
).
multipliedBy
(
1
+
TOKEN_ALLOWED_SLIPPAGE
).
toFixed
(
0
),
inputAmountB
.
multipliedBy
(
1.2
).
toFixed
(
0
),
deadline
,
recipient
,
outputCurrency
,
).
send
({
from
:
account
,
},
err
=>
!
err
&&
this
.
reset
());
break
;
default
:
break
;
}
}
};
return
{
inputError
,
outputError
:
''
,
isValid
:
isValid
&&
!
inputError
,
};
}
renderSummary
(
inputError
,
outputError
,
inputLabel
,
outputLabel
)
{
renderSummary
()
{
const
{
selectors
,
input
,
output
,
inputValue
,
inputCurrency
,
inputError
,
outputValue
,
outputCurrency
,
outputError
,
recipient
,
}
=
this
.
props
;
const
{
exchangeRate
}
=
this
.
state
;
}
=
this
.
state
;
const
{
selectors
,
account
}
=
this
.
props
;
const
{
label
:
inputLabel
}
=
selectors
().
getBalance
(
account
,
inputCurrency
);
const
{
label
:
outputLabel
}
=
selectors
().
getBalance
(
account
,
outputCurrency
);
let
nextStepMessage
;
if
(
inputError
||
outputError
)
{
...
...
@@ -362,8 +469,8 @@ class Send extends Component {
nextStepMessage
=
'
Select a token to continue.
'
;
}
else
if
(
inputCurrency
===
outputCurrency
)
{
nextStepMessage
=
'
Must be different token.
'
;
}
else
if
(
!
input
||
!
output
)
{
const
missingCurrencyValue
=
!
input
?
inputCurrency
:
outputCurrency
;
}
else
if
(
!
input
Value
||
!
outputValue
)
{
const
missingCurrencyValue
=
!
input
Value
?
inputLabel
:
outputLabel
;
nextStepMessage
=
`Enter a
${
missingCurrencyValue
}
value to continue.`
;
}
else
if
(
!
recipient
)
{
nextStepMessage
=
'
Enter a wallet address to send to.
'
;
...
...
@@ -378,34 +485,69 @@ class Send extends Component {
}
const
SLIPPAGE
=
0.025
;
const
exchangedOutput
=
BN
(
input
).
multipliedBy
(
BN
(
exchangeRate
));
const
minOutput
=
exchangedOutput
.
multipliedBy
(
1
-
SLIPPAGE
).
toFixed
(
8
);
const
maxOutput
=
exchangedOutput
.
multipliedBy
(
1
+
SLIPPAGE
).
toFixed
(
8
);
const
minOutput
=
BN
(
outputValue
).
multipliedBy
(
1
-
SLIPPAGE
).
toFixed
(
2
);
const
maxOutput
=
BN
(
outputValue
).
multipliedBy
(
1
+
SLIPPAGE
).
toFixed
(
2
);
return
(
<
div
className
=
"
swap__summary-wrapper
"
>
<
div
>
You
are
selling
{
b
(
`
${
input
}
${
inputLabel
}
`
)}
You
are
selling
{
b
(
`
${
input
Value
}
${
inputLabel
}
`
)}
<
/div
>
<
div
className
=
"
send__last-summary-text
"
>
<
span
className
=
"
swap__highlight-text
"
>
{
recipient
.
slice
(
0
,
6
)}
<
/span
>
will
receive
between
{
b
(
`
${
minOutput
}
${
outputLabel
}
`
)}
and
{
b
(
`
${
maxOutput
}
${
outputLabel
}
`
)}
<
span
className
=
"
swap__highlight-text
"
>
{
recipient
.
slice
(
0
,
6
)}
<
/span> will receive between {b
(
`${minOutput} ${outputLabel}`
)
} and {b
(
`${maxOutput} ${outputLabel}`
)
}
<
/div
>
<
/div
>
)
}
renderExchangeRate
()
{
const
{
account
,
selectors
}
=
this
.
props
;
const
{
exchangeRate
,
inputCurrency
,
outputCurrency
}
=
this
.
state
;
const
{
label
:
inputLabel
}
=
selectors
().
getBalance
(
account
,
inputCurrency
);
const
{
label
:
outputLabel
}
=
selectors
().
getBalance
(
account
,
outputCurrency
);
if
(
!
exchangeRate
||
exchangeRate
.
isNaN
()
||
!
inputCurrency
||
!
outputCurrency
)
{
return
(
<
OversizedPanel
hideBottom
>
<
div
className
=
"
swap__exchange-rate-wrapper
"
>
<
span
className
=
"
swap__exchange-rate
"
>
Exchange
Rate
<
/span
>
<
span
>
-
<
/span
>
<
/div
>
<
/OversizedPanel
>
);
}
return
(
<
OversizedPanel
hideBottom
>
<
div
className
=
"
swap__exchange-rate-wrapper
"
>
<
span
className
=
"
swap__exchange-rate
"
>
Exchange
Rate
<
/span
>
<
span
>
{
`1
${
inputLabel
}
=
${
exchangeRate
.
toFixed
(
7
)}
${
outputLabel
}
`
}
<
/span
>
<
/div
>
<
/OversizedPanel
>
);
}
render
()
{
const
{
lastEditedField
,
inputCurrency
,
outputCurrency
,
input
,
output
,
recipient
}
=
this
.
props
;
const
{
exchangeRate
}
=
this
.
state
;
const
inputLabel
=
this
.
getTokenLabel
(
inputCurrency
);
const
outputLabel
=
this
.
getTokenLabel
(
outputCurrency
);
const
{
selectors
,
account
}
=
this
.
props
;
const
{
lastEditedField
,
inputCurrency
,
outputCurrency
,
inputValue
,
outputValue
,
recipient
,
}
=
this
.
state
;
const
estimatedText
=
'
(estimated)
'
;
const
{
value
:
inputBalance
,
decimals
:
inputDecimals
}
=
selectors
().
getBalance
(
account
,
inputCurrency
);
const
{
value
:
outputBalance
,
decimals
:
outputDecimals
}
=
selectors
().
getBalance
(
account
,
outputCurrency
);
const
{
inputError
,
outputError
,
isValid
}
=
this
.
validate
();
return
(
<
div
className
=
"
s
end
"
>
<
div
className
=
"
s
wap
"
>
<
Header
/>
<
div
className
=
{
classnames
(
'
swap__content
'
,
{
...
...
@@ -414,17 +556,17 @@ class Send extends Component {
>
<
CurrencyInputPanel
title
=
"
Input
"
description
=
{
lastEditedField
===
'
output
'
?
estimatedText
:
''
}
onCurrencySelected
=
{(
d
)
=>
{
this
.
props
.
updateField
(
'
inputCurrency
'
,
d
)
this
.
props
.
sync
();
}}
onValueChange
=
{
d
=>
this
.
updateInput
(
d
)}
description
=
{
lastEditedField
===
OUTPUT
?
estimatedText
:
''
}
extraText
=
{
inputCurrency
?
`Balance:
${
inputBalance
.
dividedBy
(
BN
(
10
**
inputDecimals
)).
toFixed
(
4
)}
`
:
''
}
onCurrencySelected
=
{
inputCurrency
=>
this
.
setState
({
inputCurrency
},
this
.
recalcForm
)}
onValueChange
=
{
this
.
updateInput
}
selectedTokens
=
{[
inputCurrency
,
outputCurrency
]}
errorMessage
=
{
inputError
}
value
=
{
input
}
selectedTokenAddress
=
{
inputCurrency
}
extraText
=
{
this
.
getBalance
(
inputCurrency
)}
value
=
{
inputValue
}
errorMessage
=
{
inputError
}
/
>
<
OversizedPanel
>
<
div
className
=
"
swap__down-arrow-background
"
>
...
...
@@ -433,17 +575,17 @@ class Send extends Component {
<
/OversizedPanel
>
<
CurrencyInputPanel
title
=
"
Output
"
description
=
{
lastEditedField
===
'
input
'
?
estimatedText
:
''
}
onCurrencySelected
=
{(
d
)
=>
{
this
.
props
.
updateField
(
'
outputCurrency
'
,
d
)
this
.
props
.
sync
();
}}
onValueChange
=
{
d
=>
this
.
updateOutput
(
d
)}
description
=
{
lastEditedField
===
INPUT
?
estimatedText
:
''
}
extraText
=
{
outputCurrency
?
`Balance:
${
outputBalance
.
dividedBy
(
BN
(
10
**
outputDecimals
)).
toFixed
(
4
)}
`
:
''
}
onCurrencySelected
=
{
outputCurrency
=>
this
.
setState
({
outputCurrency
},
this
.
recalcForm
)}
onValueChange
=
{
this
.
updateOutput
}
selectedTokens
=
{[
inputCurrency
,
outputCurrency
]}
errorMessage
=
{
outputError
}
value
=
{
output
}
value
=
{
outputValue
}
selectedTokenAddress
=
{
outputCurrency
}
e
xtraText
=
{
this
.
getBalance
(
outputCurrency
)
}
e
rrorMessage
=
{
outputError
}
disableUnlock
/>
<
OversizedPanel
>
...
...
@@ -453,71 +595,90 @@ class Send extends Component {
<
/OversizedPanel
>
<
AddressInputPanel
value
=
{
recipient
}
onChange
=
{
address
=>
this
.
props
.
updateField
(
'
recipient
'
,
address
)}
onChange
=
{
address
=>
this
.
setState
({
recipient
:
address
}
)}
/
>
<
OversizedPanel
hideBottom
>
<
div
className
=
"
swap__exchange-rate-wrapper
"
>
<
span
className
=
"
swap__exchange-rate
"
>
Exchange
Rate
<
/span
>
<
span
>
{
exchangeRate
?
`1
${
inputLabel
}
=
${
exchangeRate
.
toFixed
(
7
)}
${
outputLabel
}
`
:
'
-
'
}
<
/span
>
<
/div
>
<
/OversizedPanel
>
{
this
.
renderSummary
(
inputError
,
outputError
,
inputLabel
,
outputLabel
)
}
{
this
.
renderExchangeRate
()
}
{
this
.
renderSummary
()
}
<
/div
>
<
button
className
=
{
classnames
(
'
swap__cta-btn
'
,
{
'
swap--inactive
'
:
!
this
.
props
.
isConnected
,
'
swap__cta-btn--inactive
'
:
!
isValid
,
})}
disabled
=
{
!
isValid
}
onClick
=
{
this
.
onSend
}
>
Send
<
/button
>
<
/div
>
);
}
}
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
,
}),
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
()),
})
),
export
default
drizzleConnect
(
Send
,
state
=>
({
balances
:
state
.
web3connect
.
balances
,
isConnected
:
!!
state
.
web3connect
.
account
,
account
:
state
.
web3connect
.
account
,
web3
:
state
.
web3connect
.
web3
,
exchangeAddresses
:
state
.
addresses
.
exchangeAddresses
,
}),
dispatch
=>
({
selectors
:
()
=>
dispatch
(
selectors
()),
}),
);
function
b
(
text
)
{
return
<
span
className
=
"
swap__highlight-text
"
>
{
text
}
<
/span
>
const
b
=
text
=>
<
span
className
=
"
swap__highlight-text
"
>
{
text
}
<
/span>
;
function
calculateEtherTokenOutput
({
inputAmount
:
rawInput
,
inputReserve
:
rawReserveIn
,
outputReserve
:
rawReserveOut
})
{
const
inputAmount
=
BN
(
rawInput
);
const
inputReserve
=
BN
(
rawReserveIn
);
const
outputReserve
=
BN
(
rawReserveOut
);
if
(
inputAmount
.
isLessThan
(
BN
(
10
**
9
)))
{
console
.
warn
(
`inputAmount is only
${
inputAmount
.
toFixed
(
0
)}
. Did you forget to multiply by 10 ** decimals?`
);
}
const
numerator
=
inputAmount
.
multipliedBy
(
outputReserve
).
multipliedBy
(
997
);
const
denominator
=
inputReserve
.
multipliedBy
(
1000
).
plus
(
inputAmount
.
multipliedBy
(
997
));
return
numerator
.
dividedBy
(
denominator
);
}
function
calculateEtherTokenInput
({
outputAmount
:
rawOutput
,
inputReserve
:
rawReserveIn
,
outputReserve
:
rawReserveOut
})
{
const
outputAmount
=
BN
(
rawOutput
);
const
inputReserve
=
BN
(
rawReserveIn
);
const
outputReserve
=
BN
(
rawReserveOut
);
if
(
outputAmount
.
isLessThan
(
BN
(
10
**
9
)))
{
console
.
warn
(
`inputAmount is only
${
outputAmount
.
toFixed
(
0
)}
. Did you forget to multiply by 10 ** decimals?`
);
}
const
numerator
=
outputAmount
.
multipliedBy
(
inputReserve
).
multipliedBy
(
1000
);
const
denominator
=
outputReserve
.
minus
(
outputAmount
).
multipliedBy
(
997
);
return
numerator
.
dividedBy
(
denominator
.
plus
(
1
));
}
function
getSendType
(
inputCurrency
,
outputCurrency
)
{
if
(
!
inputCurrency
||
!
outputCurrency
)
{
return
;
}
if
(
inputCurrency
===
outputCurrency
)
{
return
;
}
if
(
inputCurrency
!==
'
ETH
'
&&
outputCurrency
!==
'
ETH
'
)
{
return
'
TOKEN_TO_TOKEN
'
}
if
(
inputCurrency
===
'
ETH
'
)
{
return
'
ETH_TO_TOKEN
'
;
}
if
(
outputCurrency
===
'
ETH
'
)
{
return
'
TOKEN_TO_ETH
'
;
}
return
;
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment