diff --git a/packages/react-app/src/components/Exchange.js b/packages/react-app/src/components/Exchange.js index bc514e3..548218d 100644 --- a/packages/react-app/src/components/Exchange.js +++ b/packages/react-app/src/components/Exchange.js @@ -7,103 +7,161 @@ import { parseUnits } from "ethers/lib/utils"; import { getAvailableTokens, getCounterpartTokens, findPoolByTokens, isOperationPending, getFailureMessage, getSuccessMessage } from '../utils'; import { ROUTER_ADDRESS } from "../config"; -import AmountIn from "./"; -import AmountOut from "./"; -import Balance from "./"; +import AmountIn from "./AmountIn"; +import AmountOut from "./AmountOut"; +import Balance from "./Balance"; import styles from "../styles"; const Exchange = ({ pools }) => { - const { account } = useEthers(); - const [fromValue, setFromValue] = useState("0"); - const [fromToken, setFromToken] = useState(pools[0].token0Address); - const [toToken, setToToken] = useState(""); - const [resetState, setResetState] = useState(false) + const { account } = useEthers(); + const [fromValue, setFromValue] = useState("0"); + const [fromToken, setFromToken] = useState(pools[0].token0Address); // initialFromToken + const [toToken, setToToken] = useState(""); + const [resetState, setResetState] = useState(false) - const fromValueBigNumber = parseUnits(fromValue || "0"); - const availableTokens = getAvailableTokens(pools); //What tokens can we swap from? - const counterpartTokens = getCounterpartTokens(pools, fromToken); //What tokens can we swap to? - const pairAddress = findPoolByTokens(pools, fromToken, toToken)?.address ?? ""; //find a pair address ot that liquidity pair + const fromValueBigNumber = parseUnits(fromValue || "0"); // converse the string to bigNumber + const availableTokens = getAvailableTokens(pools); + const counterpartTokens = getCounterpartTokens(pools, fromToken); + const pairAddress = findPoolByTokens(pools, fromToken, toToken)?.address ?? ""; - //Get contract addresses - const routerContract = new Contract(ROUTER_ADDRESS, abis.router02); - const fromTokenContract = new Contract(fromToken, ERC20.abi); - const fromTokenBalance = useTokenBalance(fromToken, account); //Know balance of "from Token" - const toTokenBalance = useTokenBalance(toToken, account); //Know balance of "to Token" - const tokenAllowance = useTokenAllowance(fromToken, account, ROUTER_ADDRESS) || parseUnits("0"); - const approvedNeeded = fromValueBigNumber.gt(tokenAllowance); //We need to approve to make the swap - const formValueIsGreaterThan0 = fromValueBigNumber.gt(parseUnits("0")); //has to be greater than 0 - const hasEnoughBalance = fromValueBigNumber.lte(fromTokenBalance ?? parseUnits("0")); //lte = lower than or equal to + const routerContract = new Contract(ROUTER_ADDRESS, abis.router02); + const fromTokenContract = new Contract(fromToken, ERC20.abi); + const fromTokenBalance = useTokenBalance(fromToken, account); + const toTokenBalance = useTokenBalance(toToken, account); + const tokenAllowance = useTokenAllowance(fromToken, account, ROUTER_ADDRESS) || parseUnits("0"); + const approvedNeeded = fromValueBigNumber.gt(tokenAllowance); + const formValueIsGreaterThan0 = fromValueBigNumber.gt(parseUnits("0")); + const hasEnoughBalance = fromValueBigNumber.lte(fromTokenBalance ?? parseUnits("0")); - const isApproving = isOperationPending(swapApproveState); - const isSwapping = isOperationPending(swapExecuteState); + // approve initiating a contract call (similar to use state) -> gives the state and the sender... + const { state: swapApproveState, send: swapApproveSend } = + useContractFunction(fromTokenContract, "approve", { + transactionName: "onApproveRequested", + gasLimitBufferPercentage: 10, + }); + // swap initiating a contract call (similar to use state) -> gives the state and the sender... + const { state: swapExecuteState, send: swapExecuteSend } = + useContractFunction(routerContract, "swapExactTokensForTokens", { + transactionName: "swapExactTokensForTokens", + gasLimitBufferPercentage: 10, + }); - const successMessage = getSuccessMessage(swapApproveState, swapExecuteState); - const failureMessage = getFailureMessage(swapApproveState, swapExecuteState); + const isApproving = isOperationPending(swapApproveState); + const isSwapping = isOperationPending(swapExecuteState); + const canApprove = !isApproving && approvedNeeded; + const canSwap = !approvedNeeded && !isSwapping && formValueIsGreaterThan0 && hasEnoughBalance; - return ( -
-
- - -
- -
- - -
- - {approvedNeeded && !isSwapping ? ( - - ) : ( - - )} - - {failureMessage && !resetState ? ( -

{failureMessage}

- ) : successMessage ? ( -

{successMessage}

- ) : ( - "" - )} -
- ); -} + const successMessage = getSuccessMessage(swapApproveState, swapExecuteState); + const failureMessage = getFailureMessage(swapApproveState, swapExecuteState); -export default Exchange; \ No newline at end of file + const onApproveRequested = () => { + swapApproveSend(ROUTER_ADDRESS, ethers.constants.MaxUint256); + }; + + // https://docs.uniswap.org/protocol/V2/reference/smart-contracts/router-02#swapexacttokensfortokens + const onSwapRequested = () => { + swapExecuteSend( + fromValueBigNumber, + 0, + [fromToken, toToken], + account, + Math.floor(Date.now() / 1000) + 60 * 20 + ).then((_) => { + setFromValue("0"); + }); + }; + + const onFromValueChange = (value) => { + const trimmedValue = value.trim(); + + try { + trimmedValue && parseUnits(value); + setFromValue(value); + } catch (e) {} + }; + + const onFromTokenChange = (value) => { + setFromToken(value); + }; + + const onToTokenChange = (value) => { + setToToken(value); + }; + + useEffect(() => { + if(failureMessage || successMessage) { + setTimeout(() => { + setResetState(true) + setFromValue("0") + setToToken("") + }, 5000) + } + }, [failureMessage, successMessage]) + + return ( +
+
+ + +
+ +
+ + +
+ + {approvedNeeded && !isSwapping ? ( + + ) : ( + + )} + + {failureMessage && !resetState ? ( +

{failureMessage}

+ ) : successMessage ? ( +

{successMessage}

+ ) : ( + "" + )} +
+ ); +}; + +export default Exchange; diff --git a/packages/react-app/src/components/Loader.js b/packages/react-app/src/components/Loader.js index a41289d..b195bc5 100644 --- a/packages/react-app/src/components/Loader.js +++ b/packages/react-app/src/components/Loader.js @@ -6,7 +6,11 @@ import {ethereumLogo2 } from "../assets"; const Loader = ({ title }) => { return (
- Ethereum Logo + Ethereum Logo

{title}