@@ -26,8 +26,9 @@ const taskToPromise = (t) => new Promise((resolve, reject) => t.fork(reject, res
2626const REFRESH_INTERVAL = 15000
2727const TOKEN_ALLOWANCE_POLL_INTERVAL = 5000
2828const provider = ethers . providers . getDefaultProvider ( `https://api.blockchain.info/eth/nodes/rpc` )
29+ const ENTER_DETAILS = 'ENTER_DETAILS'
2930const COMPLETE_SWAP = 'COMPLETE_SWAP'
30- const NATIVE_CURRENCY = 'ETH'
31+ const NATIVE_TOKEN = 'ETH'
3132
3233export default ( { api, coreSagas, networks } : { api : APIType ; coreSagas : any ; networks : any } ) => {
3334 const { waitForUserData } = profileSagas ( {
@@ -43,19 +44,19 @@ export default ({ api, coreSagas, networks }: { api: APIType; coreSagas: any; ne
4344 const state = yield select ( )
4445 const nonCustodialCoinAccounts = yield * select ( ( ) =>
4546 selectors . coins . getCoinAccounts ( state , {
46- coins : [ NATIVE_CURRENCY ] ,
47+ coins : [ NATIVE_TOKEN ] ,
4748 nonCustodialAccounts : true
4849 } )
4950 )
5051
51- if ( ! nonCustodialCoinAccounts [ NATIVE_CURRENCY ] ?. length ) {
52+ if ( ! nonCustodialCoinAccounts [ NATIVE_TOKEN ] ?. length ) {
5253 yield put ( actions . core . data . eth . fetchData ( ) )
5354 yield put ( actions . core . data . eth . fetchErc20Data ( ) )
5455 }
5556 const walletCurrency = yield select ( selectors . core . settings . getCurrency )
5657 const coins = yield select ( selectors . core . data . coins . getCoins )
5758 const erc20Coins : CoinType [ ] = yield select ( selectors . core . data . coins . getErc20Coins )
58- const tokens = [ NATIVE_CURRENCY , ...erc20Coins ]
59+ const tokens = [ NATIVE_TOKEN , ...erc20Coins ]
5960 . map ( ( coin ) => {
6061 const { name, precision, symbol, type } = coins [ coin ] . coinfig
6162
@@ -86,7 +87,7 @@ export default ({ api, coreSagas, networks }: { api: APIType; coreSagas: any; ne
8687 symbol
8788 }
8889
89- if ( coin === 'ETH' ) {
90+ if ( coin === NATIVE_TOKEN ) {
9091 return {
9192 ...tokenObj ,
9293 address : '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee'
@@ -188,7 +189,7 @@ export default ({ api, coreSagas, networks }: { api: APIType; coreSagas: any; ne
188189 }
189190
190191 const nonCustodialCoinAccounts = selectors . coins . getCoinAccounts ( yield * select ( ) , {
191- coins : [ baseToken ] ,
192+ coins : [ baseToken , NATIVE_TOKEN ] ,
192193 nonCustodialAccounts : true
193194 } )
194195
@@ -199,8 +200,6 @@ export default ({ api, coreSagas, networks }: { api: APIType; coreSagas: any; ne
199200 throw Error ( 'No user wallet address' )
200201 }
201202
202- // const quoteResponse = quoteMock
203-
204203 const quoteResponse = yield * call ( api . getDexSwapQuote , {
205204 fromCurrency : {
206205 address : baseTokenInfo . address ,
@@ -239,9 +238,23 @@ export default ({ api, coreSagas, networks }: { api: APIType; coreSagas: any; ne
239238
240239 // We have a list of quotes but it's valid only for cross chains transactions that we currently don't have
241240 // Also we consider to return to the FE only one quote in that case
242- const { quote, quoteTtl } = quoteResponse
241+ const { quote, quoteTtl, transaction } = quoteResponse
243242
244243 if ( quote ) {
244+ const nonEthCustodialbalance = nonCustodialCoinAccounts [ NATIVE_TOKEN ] [ 0 ] . balance
245+ const { gasLimit, gasPrice } = transaction
246+ const gasLimitBn = ethers . BigNumber . from ( gasLimit )
247+ const gasPriceBn = ethers . BigNumber . from ( gasPrice )
248+ const gasFee = gasLimitBn . mul ( gasPriceBn )
249+
250+ if ( gasFee . gt ( nonEthCustodialbalance ) ) {
251+ // eslint-disable-next-line no-throw-literal
252+ throw {
253+ message : 'Not enough ETH to cover gas.' ,
254+ title : 'Insufficient ETH'
255+ }
256+ }
257+
245258 yield * put (
246259 actions . form . change (
247260 DEX_SWAP_FORM ,
@@ -287,24 +300,31 @@ export default ({ api, coreSagas, networks }: { api: APIType; coreSagas: any; ne
287300 // exit whenever the counterTokenAmount changes, to avoid infinitely calling fetchSwapQuote
288301 if ( field === 'counterTokenAmount' || field === 'step' ) return
289302
303+ // reset error if user changes token
304+ if ( field === 'baseToken' ) {
305+ const error = yield select ( selectors . components . dex . getSwapQuote )
306+ if ( error ) yield put ( A . clearCurrentSwapQuote ( ) )
307+ }
308+
290309 // exit if incorrect form changed or the form values were modified by a saga (avoid infinite loop)
291310 if ( form !== DEX_SWAP_FORM || action [ '@@redux-saga/SAGA_ACTION' ] === true ) return
292311 const formValues = selectors . form . getFormValues ( DEX_SWAP_FORM ) ( yield * select ( ) ) as DexSwapForm
293- const { baseToken, baseTokenAmount, counterToken, counterTokenAmount } = formValues
312+ const { baseToken, baseTokenAmount, counterToken } = formValues
294313
295314 // if one of the values is 0 set another one to 0 and clear a quote
296315 if ( field === 'baseTokenAmount' && getValidSwapAmount ( baseTokenAmount ) === 0 ) {
297316 yield * put ( actions . form . change ( DEX_SWAP_FORM , 'counterTokenAmount' , '' ) )
298317 yield * put ( A . clearCurrentSwapQuote ( ) )
299- return
318+ return yield put ( A . stopPollSwapQuote ( ) )
300319 }
301320
302321 if (
303- ( field === 'baseTokenAmount' && baseToken ) ||
304- ( field === 'baseToken' && getValidSwapAmount ( baseTokenAmount ) !== 0 && baseToken )
322+ ( field === 'baseTokenAmount' ||
323+ ( field === 'baseToken' && getValidSwapAmount ( baseTokenAmount ) !== 0 ) ||
324+ field === 'counterToken' ) &&
325+ baseToken
305326 ) {
306327 const token = selectors . components . dex . getTokenInfo ( yield * select ( ) , baseToken )
307-
308328 if ( ! token ) return
309329
310330 const { balance } = token
@@ -321,7 +341,8 @@ export default ({ api, coreSagas, networks }: { api: APIType; coreSagas: any; ne
321341 title : 'Insufficient Balance'
322342 } )
323343 )
324- return
344+
345+ return yield put ( A . stopPollSwapQuote ( ) )
325346 }
326347 }
327348
@@ -355,7 +376,7 @@ export default ({ api, coreSagas, networks }: { api: APIType; coreSagas: any; ne
355376 const response = yield call ( api . getDexTokenAllowance , {
356377 addressOwner : nonCustodialAddress ,
357378 currency : tokenAddress ,
358- network : 'ETH' ,
379+ network : NATIVE_TOKEN ,
359380 spender : 'ZEROX_EXCHANGE'
360381 } )
361382 const isTokenAllowed = response ?. result . allowance !== '0'
@@ -394,7 +415,7 @@ export default ({ api, coreSagas, networks }: { api: APIType; coreSagas: any; ne
394415 const response = yield call ( api . getDexTokenAllowance , {
395416 addressOwner : nonCustodialAddress ,
396417 currency : tokenAddress ,
397- network : 'ETH' ,
418+ network : NATIVE_TOKEN ,
398419 spender : 'ZEROX_EXCHANGE'
399420 } )
400421 const isTokenAllowed = response ?. result . allowance !== '0'
@@ -446,7 +467,7 @@ export default ({ api, coreSagas, networks }: { api: APIType; coreSagas: any; ne
446467 spender : 'ZEROX_EXCHANGE' ,
447468 type : 'TOKEN_APPROVAL'
448469 } ,
449- network : 'ETH'
470+ network : NATIVE_TOKEN
450471 } as BuildDexTxParams
451472
452473 while ( true ) {
@@ -464,7 +485,17 @@ export default ({ api, coreSagas, networks }: { api: APIType; coreSagas: any; ne
464485 yield delay ( REFRESH_INTERVAL )
465486 }
466487 } catch ( e ) {
467- yield put ( A . pollTokenAllowanceTxFailure ( e ) )
488+ if ( e ?. error === 'Unable to fetch gas estimate' ) {
489+ yield put (
490+ A . fetchSwapQuoteFailure ( {
491+ message : 'Not enough ETH to cover gas.' ,
492+ title : 'Insufficient ETH'
493+ } )
494+ )
495+ yield put ( actions . modals . closeAllModals ( ) )
496+ } else {
497+ yield put ( A . pollTokenAllowanceTxFailure ( e ) )
498+ }
468499 yield put ( A . stopPollTokenAllowanceTx ( ) )
469500 }
470501 }
@@ -514,7 +545,7 @@ export default ({ api, coreSagas, networks }: { api: APIType; coreSagas: any; ne
514545 } ,
515546 type : 'SWAP'
516547 } ,
517- network : 'ETH'
548+ network : NATIVE_TOKEN
518549 } as BuildDexTxParams
519550
520551 // build dex tx by call api
@@ -526,10 +557,21 @@ export default ({ api, coreSagas, networks }: { api: APIType; coreSagas: any; ne
526557 // send tx
527558 const tx = yield call ( ( ) => taskToPromise ( Task . of ( provider . sendTransaction ( signedTx ) ) ) )
528559 yield put ( A . sendSwapQuoteSuccess ( { tx : tx . hash } ) )
560+ yield put ( actions . form . change ( DEX_SWAP_FORM , 'step' , COMPLETE_SWAP ) )
529561 } catch ( e ) {
530- yield put ( A . sendSwapQuoteFailure ( e ) )
562+ if ( e . error === 'Insufficient funds for transaction fees' ) {
563+ yield put (
564+ A . fetchSwapQuoteFailure ( {
565+ message : 'Not enough ETH to cover gas.' ,
566+ title : 'Insufficient ETH'
567+ } )
568+ )
569+ yield put ( actions . form . change ( DEX_SWAP_FORM , 'step' , ENTER_DETAILS ) )
570+ } else {
571+ yield put ( A . sendSwapQuoteFailure ( e ) )
572+ yield put ( actions . form . change ( DEX_SWAP_FORM , 'step' , COMPLETE_SWAP ) )
573+ }
531574 }
532- yield put ( actions . form . change ( DEX_SWAP_FORM , 'step' , COMPLETE_SWAP ) )
533575 }
534576
535577 return {
0 commit comments