diff --git a/packages/omgx/wallet-frontend/src/actions/networkAction.js b/packages/omgx/wallet-frontend/src/actions/networkAction.js index 06b5d63f663ff..57c38df38bc08 100644 --- a/packages/omgx/wallet-frontend/src/actions/networkAction.js +++ b/packages/omgx/wallet-frontend/src/actions/networkAction.js @@ -60,7 +60,15 @@ export function depositL2LP(token, value) { ) } -//DEPOSIT ETH +//SWAP RELATED - Depositing into the L2LP triggers the swap-exit - variant of depositL2LP +//that handles Exit All +export function fastExitAll(token) { + return createAction('EXIT/CREATE', () => + networkService.fastExitAll(token) + ) +} + +//CLASSICAL DEPOSIT ETH export function depositETHL2(value) { return createAction('DEPOSIT/CREATE', () => { return networkService.depositETHL2(value) diff --git a/packages/omgx/wallet-frontend/src/components/input/Input.js b/packages/omgx/wallet-frontend/src/components/input/Input.js index 01968593e72fb..dfeba3713052e 100644 --- a/packages/omgx/wallet-frontend/src/components/input/Input.js +++ b/packages/omgx/wallet-frontend/src/components/input/Input.js @@ -41,6 +41,9 @@ function Input({ variant, newStyle = false, allowUseAll = false, + allowExitAll = false, + onExitAll, + loading, }) { async function handlePaste() { @@ -104,7 +107,6 @@ function Input({ Max Amount: {Number(maxValue).toFixed(3)} - {allowUseAll && ( )} + {allowExitAll && ( + + + + )} )} {paste && ( diff --git a/packages/omgx/wallet-frontend/src/components/input/Input.styles.js b/packages/omgx/wallet-frontend/src/components/input/Input.styles.js index 6a333d02a1247..6f6cc74d5117c 100644 --- a/packages/omgx/wallet-frontend/src/components/input/Input.styles.js +++ b/packages/omgx/wallet-frontend/src/components/input/Input.styles.js @@ -58,6 +58,7 @@ export const InputWrapper = styled(Box)(({ theme }) => ({ export const ActionsWrapper = styled(Box)` display: flex; flex-direction: column; - align-items: flex-end; + align-items: center; flex: 3; + margin-left: 10px; `; diff --git a/packages/omgx/wallet-frontend/src/containers/account/Account.js b/packages/omgx/wallet-frontend/src/containers/account/Account.js index 57eed0d7fa14a..9e3bd32d4a568 100644 --- a/packages/omgx/wallet-frontend/src/containers/account/Account.js +++ b/packages/omgx/wallet-frontend/src/containers/account/Account.js @@ -231,7 +231,7 @@ function Account () { You are using Mainnet.
WARNING: the mainnet smart contracts are not fully audited and funds may be at risk.
- Please exercise caution when using Mainnet. + Please be cautious when using Mainnet.
} @@ -249,7 +249,7 @@ function Account () { variant="body2" component="p" > - Note: Balance altering transaction in progress - please be patient + Transaction in progress - please be patient diff --git a/packages/omgx/wallet-frontend/src/containers/modals/exit/steps/DoExitStepFast.js b/packages/omgx/wallet-frontend/src/containers/modals/exit/steps/DoExitStepFast.js index 2aea023210c22..e18d5252e4704 100644 --- a/packages/omgx/wallet-frontend/src/containers/modals/exit/steps/DoExitStepFast.js +++ b/packages/omgx/wallet-frontend/src/containers/modals/exit/steps/DoExitStepFast.js @@ -17,7 +17,7 @@ import React, { useState, useEffect } from 'react' import { useDispatch, useSelector } from 'react-redux' -import { depositL2LP } from 'actions/networkAction' +import { depositL2LP, fastExitAll } from 'actions/networkAction' import { openAlert } from 'actions/uiAction' import { selectLoading } from 'selectors/loadingSelector' @@ -42,11 +42,14 @@ function DoExitStepFast({ handleClose, token }) { const dispatch = useDispatch() const [ value, setValue ] = useState('') - const [ value_Wei_String, setValue_Wei_String ] = useState('0') //support for Use Max + const [ value_Wei_String, setValue_Wei_String ] = useState('0') - const [LPBalance, setLPBalance] = useState(0) - const [feeRate, setFeeRate] = useState(0) - const [validValue, setValidValue] = useState(false) + const [ LPBalance, setLPBalance ] = useState(0) + const [ feeRate, setFeeRate ] = useState(0) + const [ l1gas, setl1gas ] = useState(0) + const [ l2FeeBalance, setL2FeeBalance ] = useState(0) + + const [ validValue, setValidValue ] = useState(false) const loading = useSelector(selectLoading(['EXIT/CREATE'])) @@ -57,15 +60,10 @@ function DoExitStepFast({ handleClose, token }) { function setAmount(value) { - //console.log("setAmount") - const tooSmall = new BN(value).lte(new BN(0.0)) const tooBig = new BN(value).gt(new BN(maxValue)) - //console.log("tooSmall",tooSmall) - //console.log("tooBig",tooBig) - - if (tooSmall || tooBig) { + if (tooSmall || tooBig || (Number(l1gas) > Number(l2FeeBalance))) { setValidValue(false) } else { setValidValue(true) @@ -101,6 +99,28 @@ function DoExitStepFast({ handleClose, token }) { } + async function doExitAll() { + + console.log("Amount to exit:", token.balance.toString()) + + let res = await dispatch( + fastExitAll( + token.address + ) + ) + + if (res) { + dispatch( + openAlert( + `${token.symbol} was bridged. You will receive + ${receivableAmount(value)} ${token.symbol} minus gas fees (if bridging ETH) on L1.` + ) + ) + handleClose() + } + + } + useEffect(() => { if (typeof(token) !== 'undefined') { networkService.L1LPBalance(token.addressL1).then((res) => { @@ -109,12 +129,19 @@ function DoExitStepFast({ handleClose, token }) { networkService.getTotalFeeRate().then((feeRate) => { setFeeRate(feeRate) }) + networkService.getFastExitCost(token.address).then((fee) => { + setl1gas(fee) + }) + networkService.getL2FeeBalance().then((ETHbalance) => { + setL2FeeBalance(ETHbalance) + }) } // to clean up state and fix the // error in console for max state update. return ()=>{ setLPBalance(0) setFeeRate(0) + setl1gas(0) } }, [ token ]) @@ -134,9 +161,27 @@ function DoExitStepFast({ handleClose, token }) { let buttonLabel = 'Cancel' if( loading ) buttonLabel = 'Close' + let ETHstring = '' + + if(l1gas && Number(l1gas) > 0) { + if(Number(l1gas) > Number(l2FeeBalance)){ + ETHstring = `The estimated gas fee for this transaction (approval + bridge) is ${Number(l1gas).toFixed(4)} ETH. + WARNING: your L2 ETH balance of ${Number(l2FeeBalance).toFixed(4)} is too small to cover this transaction. + THE TRANSACTION WILL FAIL.` + } else if (Number(l1gas) > Number(l2FeeBalance) * 0.9) { + ETHstring = `The estimated gas fee for this transaction (approval + bridge) is ${Number(l1gas).toFixed(4)} ETH. + CAUTION: your L2 ETH balance of ${Number(l2FeeBalance).toFixed(4)} is very close to the estimated cost. + This transaction might fail. It would be safer to have slightly more ETH in your L2 wallet to cover gas fees.` + } else { + ETHstring = `The estimated gas fee for this transaction (approval + bridge) is ${Number(l1gas).toFixed(4)} ETH. + Your L2 ETH balance of ${Number(l2FeeBalance).toFixed(4)} is sufficent to cover this transaction.` + } + } + return ( <> + Fast Bridge to L1 @@ -152,15 +197,13 @@ function DoExitStepFast({ handleClose, token }) { setAmount(i.target.value) setValue_Wei_String(toWei_String(i.target.value, token.decimals)) }} - onUseMax={(i)=>{//they want to use the maximum - setAmount(maxValue) //so the input value updates for the user - setValue_Wei_String(token.balance.toString()) - }} - allowUseAll={true} unit={token.symbol} maxValue={maxValue} newStyle variant="standard" + loading={loading} + onExitAll={doExitAll} + allowExitAll={true} /> {validValue && token && ( @@ -175,6 +218,18 @@ function DoExitStepFast({ handleClose, token }) { )} + {Number(l1gas) > 0 && Number(l1gas) > Number(l2FeeBalance) && ( + + {ETHstring} + + )} + + {Number(l1gas) > 0 && Number(l1gas) <= Number(l2FeeBalance) && ( + + {ETHstring} + + )} + {Number(LPBalance) < Number(value) && ( The liquidity pool balance (of {LPBalance}) is too low to cover your bridge - please @@ -189,27 +244,29 @@ function DoExitStepFast({ handleClose, token }) { )} + + - - + + ) diff --git a/packages/omgx/wallet-frontend/src/services/networkService.js b/packages/omgx/wallet-frontend/src/services/networkService.js index 25468ebfda6ce..8c42ce8bbd7fd 100644 --- a/packages/omgx/wallet-frontend/src/services/networkService.js +++ b/packages/omgx/wallet-frontend/src/services/networkService.js @@ -1203,6 +1203,19 @@ class NetworkService { }) } + async getL2FeeBalance() { + try { + const layer2Balance = await this.L2Provider.getBalance(this.account) + return utils.formatEther(layer2Balance) + } catch (error) { + throw new WebWalletError({ + originalError: error, + reportToSentry: false, + reportToUi: false, + }) + } + } + async getBalances() { try { @@ -1394,7 +1407,8 @@ class NetworkService { this.L2LPAddress ) - let depositAmount_BN = new BN(value_Wei_String) + //let depositAmount_BN = new BN(value_Wei_String) + let depositAmount_BN = BigNumber.from(value_Wei_String) if (depositAmount_BN.gt(allowance_BN)) { const approveStatus = await L2ERC20Contract.approve( @@ -2052,6 +2066,141 @@ class NetworkService { return balance.toString() } + async getFastExitCost(currencyAddress) { + + const L2ERC20Contract = new ethers.Contract( + currencyAddress, + L2ERC20Json.abi, + this.provider.getSigner() + ) + + const tx = await L2ERC20Contract.populateTransaction.approve( + this.L2LPAddress, + utils.parseEther('1.0') + ) + + const approvalGas_BN = await this.L2Provider.estimateGas(tx) + const approvalCost_BN = approvalGas_BN.mul('15000000') + console.log("Approve (ETH):", utils.formatEther(approvalCost_BN)) + + const tx2 = await this.L2LPContract.populateTransaction.clientDepositL2( + utils.parseEther('0'), //needs to be zero here otherwise will fail due to allowance + currencyAddress + ) + + const despositGas_BN = await this.L2Provider.estimateGas(tx2) + const despositCost_BN = despositGas_BN.mul('15000000') + console.log("Deposit gas cost (ETH)", utils.formatEther(despositCost_BN)) + + //returns total cost in ETH + return utils.formatEther(despositCost_BN.add(approvalCost_BN)) + } + + /**************************************************************/ + /***** SWAP OFF from BOBA by depositing funds to the L2LP *****/ + /**************************************************************/ + async fastExitAll(currencyAddress) { + + updateSignatureStatus_exitLP(false) + + let approvalGas_BN = BigNumber.from("0") + let approvalCost_BN = BigNumber.from("0") + + console.log("Address:",currencyAddress) + + const L2ERC20Contract = new ethers.Contract( + currencyAddress, + L2ERC20Json.abi, + this.provider.getSigner() + ) + + let balance_BN = await L2ERC20Contract.balanceOf( + this.account + ) + console.log("Initial Balance:", utils.formatEther(balance_BN)) + + let allowance_BN = await L2ERC20Contract.allowance( + this.account, + this.L2LPAddress + ) + console.log("Allowance:",utils.formatEther(allowance_BN)) + + if (balance_BN.gt(allowance_BN)) { + + //Estimate gas + const tx = await L2ERC20Contract.populateTransaction.approve( + this.L2LPAddress, + balance_BN + ) + + approvalGas_BN = await this.L2Provider.estimateGas(tx) + approvalCost_BN = approvalGas_BN.mul('15000000') + console.log("Cost to Approve:", utils.formatEther(approvalCost_BN)) + + const approveStatus = await L2ERC20Contract.approve( + this.L2LPAddress, + balance_BN + ) + await approveStatus.wait() + + if (!approveStatus) + return false + + } else { + //console.log("Balance:",balance_BN) + console.log("Allowance already suitable:", utils.formatEther(allowance_BN)) + } + + + if(currencyAddress === this.L2_ETH_Address) { + //if fee token, need to debit allowance cost if any + balance_BN = balance_BN.sub(approvalCost_BN) + console.log("ETH: Balance after approval", utils.formatEther(balance_BN)) + } + + const tx2 = await this.L2LPContract.populateTransaction.clientDepositL2( + balance_BN, + currencyAddress + ) + //console.log("tx2",tx2) + + const despositGas_BN = await this.L2Provider.estimateGas(tx2) + const despositCost_BN = despositGas_BN.mul('15000000') + console.log("Deposit gas cost (ETH)", utils.formatEther(despositCost_BN)) + + let amount_BN = balance_BN + + if(currencyAddress === this.L2_ETH_Address) { + //if fee token, need to consider cost to exit + amount_BN = balance_BN.sub(despositCost_BN) + } + console.log("Amount to exit:", utils.formatEther(amount_BN)) + + const depositTX = await this.L2LPContract.clientDepositL2( + amount_BN.toString(), + currencyAddress + ) + + //at this point the tx has been submitted, and we are waiting... + await depositTX.wait() + + //closes the modal + updateSignatureStatus_exitLP(true) + + // Waiting for the response from L1 + const [L2ToL1msgHash] = await this.fastWatcher.getMessageHashesFromL2Tx( + depositTX.hash + ) + console.log(' got L2->L1 message hash', L2ToL1msgHash) + + const L1Receipt = await this.fastWatcher.getL1TransactionReceipt( + L2ToL1msgHash + ) + console.log(' completed Deposit! L1 tx hash:', L1Receipt.transactionHash) + + return L1Receipt + } + /**************************************************************/ /***** SWAP OFF from BOBA by depositing funds to the L2LP *****/ /**************************************************************/ @@ -2070,7 +2219,8 @@ class NetworkService { this.L2LPAddress ) - let depositAmount_BN = new BN(value_Wei_String) + //let depositAmount_BN = new BN(value_Wei_String) + let depositAmount_BN = BigNumber.from(value_Wei_String) if (depositAmount_BN.gt(allowance_BN)) { const approveStatus = await L2ERC20Contract.approve(