From 9f4ccb778aafaa8bb49bc5f92c94915709c47e36 Mon Sep 17 00:00:00 2001 From: dcota <32775237+DaigaroCota@users.noreply.github.com> Date: Fri, 1 Mar 2024 09:36:54 -0800 Subject: [PATCH] refactor: landing wording constants and function to know allowance --- packages/nextjs/components/index/hero.tsx | 129 ++++++------------ .../components/index/protocolNumbers.tsx | 36 +---- .../components/xoc-dapp/abis/xocabis.ts | 118 ++++++++++++++++ packages/nextjs/utils/constants.ts | 59 ++++++++ .../scaffold-eth/fetchPriceFromUniswap.ts | 32 +++++ 5 files changed, 258 insertions(+), 116 deletions(-) create mode 100644 packages/nextjs/utils/constants.ts diff --git a/packages/nextjs/components/index/hero.tsx b/packages/nextjs/components/index/hero.tsx index a1647fc..17ef5a4 100644 --- a/packages/nextjs/components/index/hero.tsx +++ b/packages/nextjs/components/index/hero.tsx @@ -1,105 +1,86 @@ import React, { useEffect, useState } from "react"; import Image from "next/image"; import heroImg from "../../public/hero-1.png"; -import { houseOfReserveABI, xocolatlABI } from "../xoc-dapp/abis/xocabis"; +import { erc20ABI, houseOfReserveABI } from "../xoc-dapp/abis/xocabis"; import Container from "./container"; import MXNFetch from "./mxnFetch"; import ProtocolNumbers from "./protocolNumbers"; import XOCMinted from "./xocMinted"; -import { parseEther } from "viem"; +import { formatEther, parseEther } from "viem"; import { useAccount } from "wagmi"; import { useContractRead, useContractWrite } from "wagmi"; import { swapRouterABI } from "~~/components/index/abis/uniabis"; - -enum FEE_BIPS { - ONE = 100, - FIVE = 500, - THIRTY = 3000, - HUNDRED = 10000, -} - -/** - * @param path array of token addresses - * @param fees array from FEE_BIPS enum - * @returns hexbytes string `encodePacked` per solidity - */ -export function encodePath(path: string[], fees: FEE_BIPS[]) { - if (path.length != fees.length + 1) { - throw new Error("path/fee lengths do not match"); - } - const hexStringFees = fees.map(fee => toUint24HexPadded(fee)); - let encoded = "0x"; - for (let i = 0; i < fees.length; i++) { - encoded += String(path[i]).slice(2); - encoded += hexStringFees[i]; - } - // encode the path token - encoded += path[path.length - 1].slice(2); - return encoded.toLowerCase(); -} - -function toUint24HexPadded(num: number) { - const hex = num.toString(16); - return hex.padStart(6, "0"); -} +import { ADDR_LIB, XOC_ADDRESS } from "~~/utils/constants"; +import { FEE_BIPS, encodePath } from "~~/utils/scaffold-eth"; const Hero = () => { const account = useAccount(); const [expectedAmountIn, setExpectedAmountIn] = useState(0n); const { data: latestPriceData }: { data: bigint | undefined } = useContractRead({ - address: "0xd411BE9A105Ea7701FabBe58C2834b7033EBC203", // House of Reserve (WETH) + address: ADDR_LIB.polygon.weth.houseOfReserve, // House of Reserve (WETH) abi: houseOfReserveABI, functionName: "getLatestPrice", watch: true, }); - const path = encodePath( - [ - "0xa411c9Aa00E020e4f88Bc19996d29c5B7ADB4ACf", - "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", - "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", - ], + const { data: accountAllowance }: { data: bigint | undefined } = useContractRead({ + address: ADDR_LIB.polygon.weth.address, + abi: erc20ABI, + functionName: "allowance", + args: [account.address, ADDR_LIB.polygon.uniswapSwapRouter], + }); + + const xocWethPath = encodePath( + [XOC_ADDRESS, ADDR_LIB.polygon.usdc.address, ADDR_LIB.polygon.weth.address], [FEE_BIPS.FIVE, FEE_BIPS.FIVE], ); - const amountOut = parseEther("100"); + const ONE_HUNDRED_XOC = parseEther("100"); useEffect(() => { if (latestPriceData) { const scaledLatestPrice = latestPriceData * 10000000000n; const slippage = parseEther("0.005"); const scaleValue = parseEther("1") + slippage; - setExpectedAmountIn((amountOut * scaleValue) / scaledLatestPrice); + setExpectedAmountIn((ONE_HUNDRED_XOC * scaleValue) / scaledLatestPrice); } }, [latestPriceData]); const { write: approve } = useContractWrite({ - address: "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", - abi: xocolatlABI, + address: ADDR_LIB.polygon.weth.address, + abi: erc20ABI, functionName: "approve", - args: ["0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45", parseEther("0.002")], + args: [ADDR_LIB.polygon.uniswapSwapRouter, expectedAmountIn], }); const { write: executeTrade, isError } = useContractWrite({ - address: "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45", + address: ADDR_LIB.polygon.uniswapSwapRouter, abi: swapRouterABI, functionName: "exactOutput", - args: [{ path: path, recipient: account.address, amountOut: amountOut, amountInMaximum: expectedAmountIn }], + args: [ + { path: xocWethPath, recipient: account.address, amountOut: ONE_HUNDRED_XOC, amountInMaximum: expectedAmountIn }, + ], }); + // TO REMOVE + console.log("accountAllowance", formatEther(accountAllowance ? accountAllowance : 0n)); + console.log("expectedAmountIn", formatEther(expectedAmountIn)); + console.log("Is allowance greater than expectedAmountIn", accountAllowance && accountAllowance >= expectedAmountIn); + console.log("If above is true, then you hide the approve button and show the executeTrade button"); + return ( <>

- Bienvenidos -
A Scaffold-XOC + Welcome +
to Scaffold-XOC

- ¡Nos complace tenerte aquí! + ¡A decentralized app for Mexico's #1 decentralized stablecoin!

- Scaffold-XOC es un proyecto de código abierto diseñado con{" "} + Scaffold-XOC is an opensource project and this app interface was built using{" "} { > Scaffold-Eth-2. {" "} - Esto significa que puedes clonar el código y empezar a utilizarlo fácilmente. Incluye funciones conectadas - a los contratos de $XOC y el protocolo para acuñar y quemar el stablecoin. Además, cuenta con una interfaz - de usuario intuitiva para que puedas empezar a interactuar de forma sencilla. + This app can be run locally in your machine or easily forked to be customized by yourself. It is connected + connected to the $XOC protocol and it allows minting and burning of the stablecoin through the protocol's + House of Reserves contracts.

- ¡No importa tu nivel de experiencia! Ya seas un experto en tecnología blockchain o estés dando tus - primeros pasos, Scaffold-XOC está aquí para ayudarte a comprender y utilizar $XOC de manera fácil y - accesible. ¡Explora, aprende y únete a la revolución de las finanzas descentralizadas con Scaffold-XOC! + Your experience level doesn't matter! Whether you are a DeFi expert or taking your first steps, + Scaffold-XOC is here to help you understand and use $XOC in an easy and accessible way. Explore, learn and + join the decentralized finance revolution with Scaffold-XOC!

@@ -128,11 +109,11 @@ const Hero = () => {

BUY $XOC

-

- Since this is the 1st version, you need to click approve to approve 0.02 wETH that will be used to - buy XOC on Uniswap. -

-

Token In: ~0.002 Wrapped Ether

+

Buy 100 XOC on Uniswap.

+

+ Token In: {expectedAmountIn ? parseFloat(formatEther(expectedAmountIn)).toFixed(5) : "reading"}{" "} + Wrapped Ether +

Token Out: 100 XOC

- - - GitHub - - - Clonar el Repositorio -
@@ -234,7 +195,7 @@ const Hero = () => {

- Mas de 1 millon de mexicanos ya usan $XOC en sus empresas + $XOC is the most liquid stablecoin for the MXN currency.

diff --git a/packages/nextjs/components/index/protocolNumbers.tsx b/packages/nextjs/components/index/protocolNumbers.tsx index da67a56..e1b7173 100644 --- a/packages/nextjs/components/index/protocolNumbers.tsx +++ b/packages/nextjs/components/index/protocolNumbers.tsx @@ -2,45 +2,17 @@ import React, { useEffect, useState } from "react"; import { quoterABI } from "./abis/uniabis"; import { formatEther } from "viem"; import { useContractRead } from "wagmi"; - -enum FEE_BIPS { - ONE = 100, - FIVE = 500, - THIRTY = 3000, - HUNDRED = 10000, -} - -export function encodePath(path: string[], fees: FEE_BIPS[]) { - if (path.length != fees.length + 1) { - throw new Error("path/fee lengths do not match"); - } - const hexStringFees = fees.map(fee => toUint24HexPadded(fee)); - let encoded = "0x"; - for (let i = 0; i < fees.length; i++) { - encoded += String(path[i]).slice(2); - encoded += hexStringFees[i]; - } - encoded += path[path.length - 1].slice(2); - return encoded.toLowerCase(); -} - -function toUint24HexPadded(num: number) { - const hex = num.toString(16); - return hex.padStart(6, "0"); -} +import { ADDR_LIB, XOC_ADDRESS } from "~~/utils/constants"; +import { FEE_BIPS, encodePath } from "~~/utils/scaffold-eth"; const ProtocolNumbers = () => { const path = encodePath( - [ - "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", - "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", - "0xa411c9Aa00E020e4f88Bc19996d29c5B7ADB4ACf", - ], + [ADDR_LIB.polygon.weth.address, ADDR_LIB.polygon.usdc.address, XOC_ADDRESS], [FEE_BIPS.FIVE, FEE_BIPS.FIVE], ); const { data: quotedAmountOut } = useContractRead({ - address: "0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6", + address: ADDR_LIB.polygon.uniswapQuoter, abi: quoterABI, functionName: "quoteExactOutput", args: [path, BigInt(1e18).toString()], diff --git a/packages/nextjs/components/xoc-dapp/abis/xocabis.ts b/packages/nextjs/components/xoc-dapp/abis/xocabis.ts index aae342e..8ce5dec 100644 --- a/packages/nextjs/components/xoc-dapp/abis/xocabis.ts +++ b/packages/nextjs/components/xoc-dapp/abis/xocabis.ts @@ -459,6 +459,124 @@ export const xocolatlABI = [ }, ]; +export const erc20ABI = [ + { + constant: true, + inputs: [], + name: "name", + outputs: [{ name: "", type: "string" }], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: false, + inputs: [ + { name: "guy", type: "address" }, + { name: "wad", type: "uint256" }, + ], + name: "approve", + outputs: [{ name: "", type: "bool" }], + payable: false, + stateMutability: "nonpayable", + type: "function", + }, + { + constant: true, + inputs: [], + name: "totalSupply", + outputs: [{ name: "", type: "uint256" }], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: false, + inputs: [ + { name: "src", type: "address" }, + { name: "dst", type: "address" }, + { name: "wad", type: "uint256" }, + ], + name: "transferFrom", + outputs: [{ name: "", type: "bool" }], + payable: false, + stateMutability: "nonpayable", + type: "function", + }, + { + constant: true, + inputs: [], + name: "decimals", + outputs: [{ name: "", type: "uint8" }], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: true, + inputs: [{ name: "", type: "address" }], + name: "balanceOf", + outputs: [{ name: "", type: "uint256" }], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: true, + inputs: [], + name: "symbol", + outputs: [{ name: "", type: "string" }], + payable: false, + stateMutability: "view", + type: "function", + }, + { + constant: false, + inputs: [ + { name: "dst", type: "address" }, + { name: "wad", type: "uint256" }, + ], + name: "transfer", + outputs: [{ name: "", type: "bool" }], + payable: false, + stateMutability: "nonpayable", + type: "function", + }, + { + constant: true, + inputs: [ + { name: "", type: "address" }, + { name: "", type: "address" }, + ], + name: "allowance", + outputs: [{ name: "", type: "uint256" }], + payable: false, + stateMutability: "view", + type: "function", + }, + { payable: true, stateMutability: "payable", type: "fallback" }, + { + anonymous: false, + inputs: [ + { indexed: true, name: "src", type: "address" }, + { indexed: true, name: "guy", type: "address" }, + { indexed: false, name: "wad", type: "uint256" }, + ], + name: "Approval", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, name: "src", type: "address" }, + { indexed: true, name: "dst", type: "address" }, + { indexed: false, name: "wad", type: "uint256" }, + ], + name: "Transfer", + type: "event", + }, +]; + export const houseOfReserveABI = [ { inputs: [], name: "OracleHouse_invalidInput", type: "error" }, { inputs: [], name: "OracleHouse_noValue", type: "error" }, diff --git a/packages/nextjs/utils/constants.ts b/packages/nextjs/utils/constants.ts new file mode 100644 index 0000000..a155d80 --- /dev/null +++ b/packages/nextjs/utils/constants.ts @@ -0,0 +1,59 @@ +export const XOC_ADDRESS = "0xa411c9Aa00E020e4f88Bc19996d29c5B7ADB4ACf"; + +export const ADDR_LIB = { + mainnet: { + weth: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + }, + arbitrum: { + weth: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", + }, + optimism: { + weth: "0x4200000000000000000000000000000000000006", + }, + binance: { + weth: "0x2170Ed0880ac9A755fd29B2688956BD959F933F8", + wbnb: "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", + }, + polygon: { + houseOfCoin: "0x7ed1aCD46dE3a4E63f2D3b0f4fB5532e113a520B", + uniswapSwapRouter: "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45", + uniswapQuoter: "0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6", + wsteth: "0x03b54A6e9a984069379fae1a4fC4dBAE93B3bCCD", + weth: { + address: "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", + houseOfReserve: "0xd411BE9A105Ea7701FabBe58C2834b7033EBC203", + }, + wmatic: "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270", + maticx: "0xfa68FB4628DFF1028CFEc22b4162FCcd0d45efb6", + wbtc: "0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6", + usdce: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", + usdc: { + address: "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", + }, + }, + polygonzkevm: { + weth: "0x4F9A0e7FD2Bf6067db6994CF12E4495Df938E6e9", + }, + linea: { + weth: "0xe5D7C2a44FfDDf6b295A15c148167daaAf5Cf34f", + }, + base: { + weth: "0x4200000000000000000000000000000000000006", + }, + gnosis: { + weth: "0x6A023CCd1ff6F2045C3309768eAd9E68F978f6e1", + }, + goerli: { + weth: "0xCCB14936C2E000ED8393A571D15A2672537838Ad", + wbtc: "0x45AC379F019E48ca5dAC02E54F406F99F5088099", + }, + sepolia: { + weth: "0xD0dF82dE051244f04BfF3A8bB1f62E1cD39eED92", + wbtc: "0xf864F011C5A97fD8Da79baEd78ba77b47112935a", + }, + mumbai: { + wmatic: "0xf237dE5664D3c2D2545684E76fef02A3A58A364c", + weth: "0xD087ff96281dcf722AEa82aCA57E8545EA9e6C96", + wbtc: "0x97e8dE167322a3bCA28E8A49BC46F6Ce128FEC68", + }, +}; diff --git a/packages/nextjs/utils/scaffold-eth/fetchPriceFromUniswap.ts b/packages/nextjs/utils/scaffold-eth/fetchPriceFromUniswap.ts index 5a94ebd..da8849d 100644 --- a/packages/nextjs/utils/scaffold-eth/fetchPriceFromUniswap.ts +++ b/packages/nextjs/utils/scaffold-eth/fetchPriceFromUniswap.ts @@ -16,6 +16,38 @@ const ABI = parseAbi([ "function token1() external view returns (address)", ]); +export enum FEE_BIPS { + ONE = 100, + FIVE = 500, + THIRTY = 3000, + HUNDRED = 10000, +} + +function toUint24HexPadded(num: number) { + const hex = num.toString(16); + return hex.padStart(6, "0"); +} + +/** + * @param path array of token addresses + * @param fees array from FEE_BIPS enum + * @returns hexbytes string `encodePacked` per solidity + */ +export function encodePath(path: string[], fees: FEE_BIPS[]) { + if (path.length != fees.length + 1) { + throw new Error("path/fee lengths do not match"); + } + const hexStringFees = fees.map(fee => toUint24HexPadded(fee)); + let encoded = "0x"; + for (let i = 0; i < fees.length; i++) { + encoded += String(path[i]).slice(2); + encoded += hexStringFees[i]; + } + // encode the path token + encoded += path[path.length - 1].slice(2); + return encoded.toLowerCase(); +} + export const fetchPriceFromUniswap = async (targetNetwork: ChainWithAttributes): Promise => { if ( targetNetwork.nativeCurrency.symbol !== "ETH" &&