diff --git a/src/components/Features/Transfer/Transfer.js b/src/components/Features/Transfer/Transfer.js index 27891217..e9347ec7 100644 --- a/src/components/Features/Transfer/Transfer.js +++ b/src/components/Features/Transfer/Transfer.js @@ -1,8 +1,21 @@ import {NetworkType} from '@starkware-industries/commons-js-enums'; -import {afterDecimal, evaluate, isNegative, isZero} from '@starkware-industries/commons-js-utils'; +import {useFetchData} from '@starkware-industries/commons-js-hooks'; +import { + afterDecimal, + createHttpClient, + evaluate, + isNegative, + isZero, + parseFromDecimals, + promiseHandler +} from '@starkware-industries/commons-js-utils'; import PropTypes from 'prop-types'; import React, {Fragment, useEffect, useState} from 'react'; +import { + SPACESHARD_MAINNET_RELAYER_URL_MAINNET, + SPACESHARD_TESTNET_RELAYER_URL_GOERLI +} from '../../../config/constants'; import { useConstants, useTransferToL1, @@ -20,6 +33,8 @@ import { useBridgeIsFull, useTransfer } from '../../../providers/TransferProvider'; +import {useL1Wallet} from '../../../providers/WalletsProvider'; +import {getTimestamp} from '../../../utils/timestamp'; import { Loading, LoadingSize, @@ -32,6 +47,7 @@ import { Link, LoginWalletButton } from '../../UI'; +import {OneTxWithdrawal} from '../../UI/OneTxWithdrawal/OneTxWithdrawal'; import styles from './Transfer.module.scss'; export const Transfer = ({onNetworkSwap}) => { @@ -57,6 +73,32 @@ export const Transfer = ({onNetworkSwap}) => { const getL1Token = useL1Token(); const getL2Token = useL2Token(); const {isLoggedIn} = useLogin(); + const [floorTimestamp, setFloorTimestamp] = useState(getTimestamp()); + const [useOneTxWithdrawal, setUseOneTxWithdrawal] = useState(false); + const {chainId} = useL1Wallet(); + + console.log(chainId); + + const fetchGasCostData = useFetchData(async () => { + if (!isL1 && selectedToken) { + const httpClient = createHttpClient({ + baseURL: + chainId === 1 + ? SPACESHARD_MAINNET_RELAYER_URL_MAINNET + : SPACESHARD_TESTNET_RELAYER_URL_GOERLI + }); + + const {bridgeAddress} = selectedToken; + const timestamp = Math.floor(new Date().getTime() / 1000); + // await sleep(3); + const [result] = await promiseHandler(httpClient.get(`/${bridgeAddress}/${timestamp}`)); + console.log(result); + if (result) { + return result.data.result; + } + return null; + } + }, [selectedToken, isL1, floorTimestamp]); useEffect(() => { if (!selectedToken) { @@ -78,6 +120,16 @@ export const Transfer = ({onNetworkSwap}) => { } }, [amount, selectedToken, isL1, bridgeIsFull]); + useEffect(() => { + const interval = setInterval(() => { + const ftimestamp = getTimestamp(); + if (floorTimestamp < ftimestamp) { + setFloorTimestamp(ftimestamp); + } + }, 30 * 1000); + return () => clearInterval(interval); + }, []); + const validateAmount = () => { let errorMsg = ''; const {decimals, balance, maxDeposit, symbol} = selectedToken; @@ -115,7 +167,14 @@ export const Transfer = ({onNetworkSwap}) => { setAmount(event.target.value); }; - const onTransferClick = async () => (isL1 ? transferToL2(amount) : transferToL1(amount)); + const onTransferClick = async () => + isL1 + ? transferToL2(amount) + : transferToL1( + amount, + useOneTxWithdrawal ? fetchGasCostData.data.relayerAddress : null, + fetchGasCostData.data.gasCost + ); const onRefreshTokenBalanceClick = () => { updateTokenBalance(selectedToken.symbol); @@ -178,6 +237,13 @@ export const Transfer = ({onNetworkSwap}) => { {isL1 ? renderL1Network() : renderL2Network()} {isL1 ? renderL2Network() : renderL1Network()} + {!isL1 && fetchGasCostData.data && ( + + )} {isLoggedIn ? ( { + return ( +
+ { +

+ One transaction withdrawal gas cost {gasCost} ETH{' '} + setUseOneTxWithdrawal(!useOneTxWithdrawal)} + /> +

+ } +
+ ); +}; + +OneTxWithdrawal.propTypes = { + gasCost: PropTypes.string, + isloading: PropTypes.bool, + isL1: PropTypes.bool, + isTokenSupported: PropTypes.bool, + useOneTxWithdrawal: PropTypes.bool, + setUseOneTxWithdrawal: PropTypes.func +}; diff --git a/src/components/UI/OneTxWithdrawal/OneTxWithdrawal.module.scss b/src/components/UI/OneTxWithdrawal/OneTxWithdrawal.module.scss new file mode 100644 index 00000000..3d0f75ba --- /dev/null +++ b/src/components/UI/OneTxWithdrawal/OneTxWithdrawal.module.scss @@ -0,0 +1,17 @@ +@import '../../../index'; + +.description { + height: 100%; + margin: 8px 0; + padding: 16px 12px; + color: $--color-white; + text-decoration: none; + font-size: 12px; + line-height: 18px; +} + +.oneTxWithdrawal{ + display: flex; + justify-content: space-between; + margin-right: 8px; +} \ No newline at end of file diff --git a/src/config/constants.js b/src/config/constants.js index a5488e04..aae1cb92 100644 --- a/src/config/constants.js +++ b/src/config/constants.js @@ -28,3 +28,6 @@ export const APP_URL_MAINNET = 'https://starkgate.starknet.io'; export const STARKGATE_ALPHA_LIMITATIONS_URL = `${STARKGATE_DOCS_URL}/#starkgate_alpha_limitations`; export const STARKGATE_COMPLIANCE_MAIL_ADDRESS = 'compliance@starkware.co'; export const STARKNET_ECOSYSTEM_URL = 'https://starknet-ecosystem.com'; +export const SPACESHARD_MAINNET_RELAYER_URL_MAINNET = 'https://starkgate.spaceshard.io/v1/gas-cost'; +export const SPACESHARD_TESTNET_RELAYER_URL_GOERLI = + 'https://starkgate-testnet.spaceshard.io/v1/gas-cost'; diff --git a/src/hooks/useBridgeContractAPI.js b/src/hooks/useBridgeContractAPI.js index db334625..8e40664d 100644 --- a/src/hooks/useBridgeContractAPI.js +++ b/src/hooks/useBridgeContractAPI.js @@ -14,6 +14,7 @@ import {useL1Token} from '../providers/TokensProvider'; import {useSelectedToken} from '../providers/TransferProvider'; import {useL1Wallet} from '../providers/WalletsProvider'; import {isDai} from '../utils'; +import {GasFeeTokenAddress} from '../utils/oneTxWithdrawal'; import { useL1TokenBridgeContract, useL2TokenBridgeContract, @@ -112,7 +113,7 @@ export const useBridgeContractAPI = () => { ); const initiateWithdraw = useCallback( - async ({recipient, amount}) => { + async ({recipient, amount, relayerAddress, gasCost}) => { const {bridgeAddress, tokenAddress, decimals, symbol} = selectedToken; const bridge = getL2BridgeContract(bridgeAddress); const token = getL2TokenContract(tokenAddress); @@ -138,6 +139,18 @@ export const useBridgeContractAPI = () => { } } ]; + + if (relayerAddress) { + console.log("Pay the relayer", Number(gasCost), parseFromDecimals(gasCost, 18)) + transactions.push({ + contract: getL2TokenContract(GasFeeTokenAddress), + method: 'transfer', + args: { + user: parseToFelt(relayerAddress), + amount: parseToUint256(parseFromDecimals(gasCost, 18)) + } + }); + } return sendTransactionL2(transactions); }, [selectedToken, getL2BridgeContract, getL2TokenContract] diff --git a/src/hooks/useTransferToL1.js b/src/hooks/useTransferToL1.js index 733ed5ac..679b5832 100644 --- a/src/hooks/useTransferToL1.js +++ b/src/hooks/useTransferToL1.js @@ -31,7 +31,7 @@ export const useTransferToL1 = () => { const {addTransfer} = useTransfersLog(); return useCallback( - async amount => { + async (amount, relayerAddress, gasCost) => { const {name, symbol} = selectedToken; const sendInitiateWithdraw = () => { @@ -43,7 +43,9 @@ export const useTransferToL1 = () => { }); return initiateWithdraw({ recipient: accountL1, - amount + amount, + relayerAddress, + gasCost }); }; diff --git a/src/utils/oneTxWithdrawal.js b/src/utils/oneTxWithdrawal.js new file mode 100644 index 00000000..44f34615 --- /dev/null +++ b/src/utils/oneTxWithdrawal.js @@ -0,0 +1,2 @@ +export const GasFeeTokenAddress = + '0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7'; diff --git a/src/utils/timestamp.js b/src/utils/timestamp.js new file mode 100644 index 00000000..5e47af88 --- /dev/null +++ b/src/utils/timestamp.js @@ -0,0 +1,4 @@ +export const getTimestamp = () => { + const timestamp = Math.floor(new Date().getTime() / 1000); + return timestamp - (timestamp % 900); +};