diff --git a/package.json b/package.json index f56f26b..9ea450c 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,10 @@ }, "packageManager": "yarn@3.2.3", "dependencies": { + "@nextui-org/modal": "^2.0.28", + "@uniswap/sdk-core": "^4.0.10", + "@uniswap/smart-order-router": "^3.21.1", + "@uniswap/v3-sdk": "^3.10.2", "@uniswap/widgets": "^2.59.0", "vercel": "^33.4.1" } diff --git a/packages/nextjs/components/index/abis/uniabis.ts b/packages/nextjs/components/index/abis/uniabis.ts new file mode 100644 index 0000000..92968d7 --- /dev/null +++ b/packages/nextjs/components/index/abis/uniabis.ts @@ -0,0 +1,538 @@ +export const quoterABI = [ + { + inputs: [ + { internalType: "address", name: "_factory", type: "address" }, + { internalType: "address", name: "_WETH9", type: "address" }, + ], + stateMutability: "nonpayable", + type: "constructor", + }, + { + inputs: [], + name: "WETH9", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "factory", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "bytes", name: "path", type: "bytes" }, + { internalType: "uint256", name: "amountIn", type: "uint256" }, + ], + name: "quoteExactInput", + outputs: [{ internalType: "uint256", name: "amountOut", type: "uint256" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "tokenIn", type: "address" }, + { internalType: "address", name: "tokenOut", type: "address" }, + { internalType: "uint24", name: "fee", type: "uint24" }, + { internalType: "uint256", name: "amountIn", type: "uint256" }, + { internalType: "uint160", name: "sqrtPriceLimitX96", type: "uint160" }, + ], + name: "quoteExactInputSingle", + outputs: [{ internalType: "uint256", name: "amountOut", type: "uint256" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes", name: "path", type: "bytes" }, + { internalType: "uint256", name: "amountOut", type: "uint256" }, + ], + name: "quoteExactOutput", + outputs: [{ internalType: "uint256", name: "amountIn", type: "uint256" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "tokenIn", type: "address" }, + { internalType: "address", name: "tokenOut", type: "address" }, + { internalType: "uint24", name: "fee", type: "uint24" }, + { internalType: "uint256", name: "amountOut", type: "uint256" }, + { internalType: "uint160", name: "sqrtPriceLimitX96", type: "uint160" }, + ], + name: "quoteExactOutputSingle", + outputs: [{ internalType: "uint256", name: "amountIn", type: "uint256" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "int256", name: "amount0Delta", type: "int256" }, + { internalType: "int256", name: "amount1Delta", type: "int256" }, + { internalType: "bytes", name: "path", type: "bytes" }, + ], + name: "uniswapV3SwapCallback", + outputs: [], + stateMutability: "view", + type: "function", + }, +]; + +export const swapRouterABI = [ + { + inputs: [ + { internalType: "address", name: "_factoryV2", type: "address" }, + { internalType: "address", name: "factoryV3", type: "address" }, + { internalType: "address", name: "_positionManager", type: "address" }, + { internalType: "address", name: "_WETH9", type: "address" }, + ], + stateMutability: "nonpayable", + type: "constructor", + }, + { + inputs: [], + name: "WETH9", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "token", type: "address" }], + name: "approveMax", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "token", type: "address" }], + name: "approveMaxMinusOne", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "token", type: "address" }], + name: "approveZeroThenMax", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "token", type: "address" }], + name: "approveZeroThenMaxMinusOne", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [{ internalType: "bytes", name: "data", type: "bytes" }], + name: "callPositionManager", + outputs: [{ internalType: "bytes", name: "result", type: "bytes" }], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes[]", name: "paths", type: "bytes[]" }, + { internalType: "uint128[]", name: "amounts", type: "uint128[]" }, + { internalType: "uint24", name: "maximumTickDivergence", type: "uint24" }, + { internalType: "uint32", name: "secondsAgo", type: "uint32" }, + ], + name: "checkOracleSlippage", + outputs: [], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "bytes", name: "path", type: "bytes" }, + { internalType: "uint24", name: "maximumTickDivergence", type: "uint24" }, + { internalType: "uint32", name: "secondsAgo", type: "uint32" }, + ], + name: "checkOracleSlippage", + outputs: [], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + components: [ + { internalType: "bytes", name: "path", type: "bytes" }, + { internalType: "address", name: "recipient", type: "address" }, + { internalType: "uint256", name: "amountIn", type: "uint256" }, + { internalType: "uint256", name: "amountOutMinimum", type: "uint256" }, + ], + internalType: "struct IV3SwapRouter.ExactInputParams", + name: "params", + type: "tuple", + }, + ], + name: "exactInput", + outputs: [{ internalType: "uint256", name: "amountOut", type: "uint256" }], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + components: [ + { internalType: "address", name: "tokenIn", type: "address" }, + { internalType: "address", name: "tokenOut", type: "address" }, + { internalType: "uint24", name: "fee", type: "uint24" }, + { internalType: "address", name: "recipient", type: "address" }, + { internalType: "uint256", name: "amountIn", type: "uint256" }, + { internalType: "uint256", name: "amountOutMinimum", type: "uint256" }, + { internalType: "uint160", name: "sqrtPriceLimitX96", type: "uint160" }, + ], + internalType: "struct IV3SwapRouter.ExactInputSingleParams", + name: "params", + type: "tuple", + }, + ], + name: "exactInputSingle", + outputs: [{ internalType: "uint256", name: "amountOut", type: "uint256" }], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + components: [ + { internalType: "bytes", name: "path", type: "bytes" }, + { internalType: "address", name: "recipient", type: "address" }, + { internalType: "uint256", name: "amountOut", type: "uint256" }, + { internalType: "uint256", name: "amountInMaximum", type: "uint256" }, + ], + internalType: "struct IV3SwapRouter.ExactOutputParams", + name: "params", + type: "tuple", + }, + ], + name: "exactOutput", + outputs: [{ internalType: "uint256", name: "amountIn", type: "uint256" }], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + components: [ + { internalType: "address", name: "tokenIn", type: "address" }, + { internalType: "address", name: "tokenOut", type: "address" }, + { internalType: "uint24", name: "fee", type: "uint24" }, + { internalType: "address", name: "recipient", type: "address" }, + { internalType: "uint256", name: "amountOut", type: "uint256" }, + { internalType: "uint256", name: "amountInMaximum", type: "uint256" }, + { internalType: "uint160", name: "sqrtPriceLimitX96", type: "uint160" }, + ], + internalType: "struct IV3SwapRouter.ExactOutputSingleParams", + name: "params", + type: "tuple", + }, + ], + name: "exactOutputSingle", + outputs: [{ internalType: "uint256", name: "amountIn", type: "uint256" }], + stateMutability: "payable", + type: "function", + }, + { + inputs: [], + name: "factory", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "factoryV2", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "token", type: "address" }, + { internalType: "uint256", name: "amount", type: "uint256" }, + ], + name: "getApprovalType", + outputs: [{ internalType: "enum IApproveAndCall.ApprovalType", name: "", type: "uint8" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + components: [ + { internalType: "address", name: "token0", type: "address" }, + { internalType: "address", name: "token1", type: "address" }, + { internalType: "uint256", name: "tokenId", type: "uint256" }, + { internalType: "uint256", name: "amount0Min", type: "uint256" }, + { internalType: "uint256", name: "amount1Min", type: "uint256" }, + ], + internalType: "struct IApproveAndCall.IncreaseLiquidityParams", + name: "params", + type: "tuple", + }, + ], + name: "increaseLiquidity", + outputs: [{ internalType: "bytes", name: "result", type: "bytes" }], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + components: [ + { internalType: "address", name: "token0", type: "address" }, + { internalType: "address", name: "token1", type: "address" }, + { internalType: "uint24", name: "fee", type: "uint24" }, + { internalType: "int24", name: "tickLower", type: "int24" }, + { internalType: "int24", name: "tickUpper", type: "int24" }, + { internalType: "uint256", name: "amount0Min", type: "uint256" }, + { internalType: "uint256", name: "amount1Min", type: "uint256" }, + { internalType: "address", name: "recipient", type: "address" }, + ], + internalType: "struct IApproveAndCall.MintParams", + name: "params", + type: "tuple", + }, + ], + name: "mint", + outputs: [{ internalType: "bytes", name: "result", type: "bytes" }], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "previousBlockhash", type: "bytes32" }, + { internalType: "bytes[]", name: "data", type: "bytes[]" }, + ], + name: "multicall", + outputs: [{ internalType: "bytes[]", name: "", type: "bytes[]" }], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "deadline", type: "uint256" }, + { internalType: "bytes[]", name: "data", type: "bytes[]" }, + ], + name: "multicall", + outputs: [{ internalType: "bytes[]", name: "", type: "bytes[]" }], + stateMutability: "payable", + type: "function", + }, + { + inputs: [{ internalType: "bytes[]", name: "data", type: "bytes[]" }], + name: "multicall", + outputs: [{ internalType: "bytes[]", name: "results", type: "bytes[]" }], + stateMutability: "payable", + type: "function", + }, + { + inputs: [], + name: "positionManager", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "token", type: "address" }, + { internalType: "uint256", name: "value", type: "uint256" }, + ], + name: "pull", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { inputs: [], name: "refundETH", outputs: [], stateMutability: "payable", type: "function" }, + { + inputs: [ + { internalType: "address", name: "token", type: "address" }, + { internalType: "uint256", name: "value", type: "uint256" }, + { internalType: "uint256", name: "deadline", type: "uint256" }, + { internalType: "uint8", name: "v", type: "uint8" }, + { internalType: "bytes32", name: "r", type: "bytes32" }, + { internalType: "bytes32", name: "s", type: "bytes32" }, + ], + name: "selfPermit", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "token", type: "address" }, + { internalType: "uint256", name: "nonce", type: "uint256" }, + { internalType: "uint256", name: "expiry", type: "uint256" }, + { internalType: "uint8", name: "v", type: "uint8" }, + { internalType: "bytes32", name: "r", type: "bytes32" }, + { internalType: "bytes32", name: "s", type: "bytes32" }, + ], + name: "selfPermitAllowed", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "token", type: "address" }, + { internalType: "uint256", name: "nonce", type: "uint256" }, + { internalType: "uint256", name: "expiry", type: "uint256" }, + { internalType: "uint8", name: "v", type: "uint8" }, + { internalType: "bytes32", name: "r", type: "bytes32" }, + { internalType: "bytes32", name: "s", type: "bytes32" }, + ], + name: "selfPermitAllowedIfNecessary", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "token", type: "address" }, + { internalType: "uint256", name: "value", type: "uint256" }, + { internalType: "uint256", name: "deadline", type: "uint256" }, + { internalType: "uint8", name: "v", type: "uint8" }, + { internalType: "bytes32", name: "r", type: "bytes32" }, + { internalType: "bytes32", name: "s", type: "bytes32" }, + ], + name: "selfPermitIfNecessary", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "amountIn", type: "uint256" }, + { internalType: "uint256", name: "amountOutMin", type: "uint256" }, + { internalType: "address[]", name: "path", type: "address[]" }, + { internalType: "address", name: "to", type: "address" }, + ], + name: "swapExactTokensForTokens", + outputs: [{ internalType: "uint256", name: "amountOut", type: "uint256" }], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "amountOut", type: "uint256" }, + { internalType: "uint256", name: "amountInMax", type: "uint256" }, + { internalType: "address[]", name: "path", type: "address[]" }, + { internalType: "address", name: "to", type: "address" }, + ], + name: "swapTokensForExactTokens", + outputs: [{ internalType: "uint256", name: "amountIn", type: "uint256" }], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "token", type: "address" }, + { internalType: "uint256", name: "amountMinimum", type: "uint256" }, + { internalType: "address", name: "recipient", type: "address" }, + ], + name: "sweepToken", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "token", type: "address" }, + { internalType: "uint256", name: "amountMinimum", type: "uint256" }, + ], + name: "sweepToken", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "token", type: "address" }, + { internalType: "uint256", name: "amountMinimum", type: "uint256" }, + { internalType: "uint256", name: "feeBips", type: "uint256" }, + { internalType: "address", name: "feeRecipient", type: "address" }, + ], + name: "sweepTokenWithFee", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "token", type: "address" }, + { internalType: "uint256", name: "amountMinimum", type: "uint256" }, + { internalType: "address", name: "recipient", type: "address" }, + { internalType: "uint256", name: "feeBips", type: "uint256" }, + { internalType: "address", name: "feeRecipient", type: "address" }, + ], + name: "sweepTokenWithFee", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { internalType: "int256", name: "amount0Delta", type: "int256" }, + { internalType: "int256", name: "amount1Delta", type: "int256" }, + { internalType: "bytes", name: "_data", type: "bytes" }, + ], + name: "uniswapV3SwapCallback", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "amountMinimum", type: "uint256" }, + { internalType: "address", name: "recipient", type: "address" }, + ], + name: "unwrapWETH9", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "amountMinimum", type: "uint256" }], + name: "unwrapWETH9", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "amountMinimum", type: "uint256" }, + { internalType: "address", name: "recipient", type: "address" }, + { internalType: "uint256", name: "feeBips", type: "uint256" }, + { internalType: "address", name: "feeRecipient", type: "address" }, + ], + name: "unwrapWETH9WithFee", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "amountMinimum", type: "uint256" }, + { internalType: "uint256", name: "feeBips", type: "uint256" }, + { internalType: "address", name: "feeRecipient", type: "address" }, + ], + name: "unwrapWETH9WithFee", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "value", type: "uint256" }], + name: "wrapETH", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { stateMutability: "payable", type: "receive" }, +]; diff --git a/packages/nextjs/components/index/benefits.js b/packages/nextjs/components/index/benefits.js index 209f71b..bb7e498 100644 --- a/packages/nextjs/components/index/benefits.js +++ b/packages/nextjs/components/index/benefits.js @@ -13,7 +13,7 @@ const Benefits = props => {
Benefits {
-

Ready to try-out this template?

+

Listo para unirte a la aventura?

- Don't let your visitors see a poor landing. + No dejes pasar la oportunidad de aportar!

@@ -18,7 +18,7 @@ const Cta = () => { rel="noopener noreferrer" // Fix: Added 'noreferrer' attribute className="inline-block py-3 mx-auto text-lg font-medium text-center text-indigo-600 bg-white rounded-md px-7 lg:px-10 lg:py-5 " > - Download for Free + Mintea tu NFT de membresía
diff --git a/packages/nextjs/components/index/data.js b/packages/nextjs/components/index/data.js index 8d553e6..df64287 100644 --- a/packages/nextjs/components/index/data.js +++ b/packages/nextjs/components/index/data.js @@ -11,46 +11,46 @@ import benefitOneImg from "../../public/benefit-one.png"; import benefitTwoImg from "../../public/benefit-two.png"; const benefitOne = { - title: "Highlight your benefits", - desc: "You can use this space to highlight your first benefit or a feature of your product. It can also contain an image or Illustration like in the example along with some bullet points.", + title: "El primer peso MXN decentralizado", + desc: "Por medio de contratos inteligentes, $XOC se convierte en una peso MXN digital sin intermediarios de por medio, lo que significa que no está controlada por ningún gobierno o banco central. Esto permite a los usuarios enviar y recibir dinero de forma rápida y segura, sin importar dónde se encuentren.", image: benefitOneImg, bullets: [ { - title: "Understand your customers", - desc: "Then explain the first point breifly in one or two lines.", + title: "Tu moneda es codigo en el internet", + desc: "Es la siguiente evolución del dinero en linea.", icon: , }, { - title: "Improve acquisition", - desc: "Here you can add the next benefit point.", + title: "Transacciones rápidas y seguras", + desc: "Estamos en las redes de Polygon, Gnosis Chain y Optimism.", icon: , }, { - title: "Drive customer retention", - desc: "This will be your last bullet point in this section.", + title: "Sin intermediarios de por medio", + desc: "No está controlada por ningún gobierno o banco central.", icon: , }, ], }; const benefitTwo = { - title: "Offer more benefits here", - desc: "You can use this same layout with a flip image to highlight your rest of the benefits of your product. It can also contain an image or Illustration as above section along with some bullet points.", + title: "Construida por La DAO", + desc: "Todo surge desde una comunidad de personas que buscan construir un sistema financiero abierto y descentralizado. La DAO es una organización autónoma descentralizada que se encarga de gestionar todo el trabajo requerido para construir en DeFi.", image: benefitTwoImg, bullets: [ { - title: "Mobile Responsive Template", - desc: "Nextly is designed as a mobile first responsive template.", + title: "Construimos usando herramientas Web3", + desc: "OpenZeppelin, Scaffold-Eth-2, DaoHaus, Safe y muchas mas...", icon: , }, { - title: "Powered by Next.js & TailwindCSS", - desc: "This template is powered by latest technologies and tools.", + title: "Una nueva democracia financiera", + desc: "Todo el trabajo esta direccionado a construir una nueva forma mas justa de hacer finanzas.", icon: , }, { - title: "Dark & Light Mode", - desc: "Nextly comes with a zero-config light & dark mode. ", + title: "Encuentra un mentor, o se el que nunca tuviste", + desc: "Nos encanta onboardear mas y mas gente dispuesta a contribuir.", icon: , }, ], diff --git a/packages/nextjs/components/index/hero.js b/packages/nextjs/components/index/hero.tsx similarity index 85% rename from packages/nextjs/components/index/hero.js rename to packages/nextjs/components/index/hero.tsx index 904b552..dada92c 100644 --- a/packages/nextjs/components/index/hero.js +++ b/packages/nextjs/components/index/hero.tsx @@ -1,11 +1,64 @@ import Image from "next/image"; -import heroImg from "../../public/hero.png"; +import heroImg from "../../public/hero-1.png"; import Container from "./container"; +import { useAccount } from "wagmi"; +import { 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"); +} const Hero = () => { + const path = encodePath( + [ + "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", + "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", + "0xa411c9Aa00E020e4f88Bc19996d29c5B7ADB4ACf", + ], + [FEE_BIPS.FIVE, FEE_BIPS.FIVE], + ); + + const address = useAccount(); + + const { write: executeTrade } = useContractWrite({ + address: "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45", + abi: swapRouterABI, + functionName: "exactOutput", + args: [path, address, 100000000000000000000, 1000000000000000000, 0], + }); + return ( <> - +

@@ -31,16 +84,29 @@ const Hero = () => {

- (document.getElementById("my_modal_1") as HTMLDialogElement)?.showModal()} > - Buy 100 $XOC - + Buy $XOC + + +
+

BUY $XOC

+

This is a Modal where you can eventually see a route and execute a swap upon.

+

Token In: Wrapped Ether

+

Token Out: XOC

+ +
+
+ {/* if there is a button in form, it will close the modal */} + +
+
+
+
{ GitHub - Fork our contracts + Clonar el Repositorio

@@ -66,8 +132,8 @@ const Hero = () => {
Hero Illustration {
- Trusted by 2000+ customers worldwide +

+ Mas de 1 millon de mexicanos ya usan $XOC en sus empresas +

diff --git a/packages/nextjs/components/index/popupWidget.js b/packages/nextjs/components/index/popupWidget.js deleted file mode 100644 index ebbe967..0000000 --- a/packages/nextjs/components/index/popupWidget.js +++ /dev/null @@ -1,109 +0,0 @@ -import React, { useState } from "react"; -import { useForm, useWatch } from "react-hook-form"; - -const PopupWidget = () => { - const { - register, - handleSubmit, - reset, - control, - formState: { errors, isSubmitSuccessful, isSubmitting }, - } = useForm({ - mode: "onTouched", - }); - const [isSuccess, setIsSuccess] = useState(false); - const [Message, setMessage] = useState(""); - - const userName = useWatch({ control, name: "name", defaultValue: "Someone" }); - - const onSubmit = async (data, e) => { - console.log(data); - await fetch("https://api.web3forms.com/submit", { - method: "POST", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - body: JSON.stringify(data, null, 2), - }) - .then(async response => { - let json = await response.json(); - if (json.success) { - setIsSuccess(true); - setMessage(json.message); - e.target.reset(); - reset(); - } else { - setIsSuccess(false); - setMessage(json.message); - } - }) - .catch(error => { - setIsSuccess(false); - setMessage("Client Error. Please check the console.log for more info"); - console.log(error); - }); - }; - - return ( -
-
- -
-
-

How can we help?

-

We usually respond in a few hours

-
-
- {!isSubmitSuccessful && ( -
- {/* ... (rest of the form) */} -
- )} - - {/* ... (success and error messages) */} -
-
-
-
- ); -}; - -export default PopupWidget; \ No newline at end of file diff --git a/packages/nextjs/components/index/protocolNumbers.tsx b/packages/nextjs/components/index/protocolNumbers.tsx new file mode 100644 index 0000000..8426bf4 --- /dev/null +++ b/packages/nextjs/components/index/protocolNumbers.tsx @@ -0,0 +1,74 @@ +import React 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, +} + +/** + * @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"); +} + +const ProtocolNumbers = () => { + const path = encodePath( + [ + "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", + "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", + "0xa411c9Aa00E020e4f88Bc19996d29c5B7ADB4ACf", + ], + [FEE_BIPS.FIVE, FEE_BIPS.FIVE], + ); + + const { data: quotedAmountOut } = useContractRead({ + address: "0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6", + abi: quoterABI, + functionName: "quoteExactInput", + args: [path, BigInt(1e18).toString()], + }); + + return ( + <> +
+
+
Precio de 1 Ether
+
+ {quotedAmountOut + ? new Intl.NumberFormat("es-MX", { style: "currency", currency: "MXN" }).format( + parseFloat(formatEther(BigInt(quotedAmountOut.toString()))), + ) + : "MXN0.00"} +
+
En $XOC Mexicanos
+
+
+ + ); +}; + +export default ProtocolNumbers; diff --git a/packages/nextjs/components/xoc-dapp/Dashboard.tsx b/packages/nextjs/components/xoc-dapp/Dashboard.tsx index 555b517..81df5a6 100644 --- a/packages/nextjs/components/xoc-dapp/Dashboard.tsx +++ b/packages/nextjs/components/xoc-dapp/Dashboard.tsx @@ -3,6 +3,7 @@ import { useState } from "react"; import AmountInput from "./AmountInput"; import CollateralInfo from "./CollateralInfo"; import PillNavigation from "./PillNavigation"; +import Swap from "./Swap"; import { houseOfCoinABI, houseOfReserveABI } from "./abis/xocabis"; import { parseEther } from "viem"; import { useContractWrite } from "wagmi"; @@ -108,6 +109,9 @@ const Dashboard: React.FC = () => { isLoading={withdrawLoading} /> )} +
+ {selectedTab === "exchange" && } +
{(depositError || depositLoading) &&
Hello!!!
} {mintingError &&
Hello!!!
} {mintingLoading && ( diff --git a/packages/nextjs/components/xoc-dapp/Swap/SwapButton.tsx b/packages/nextjs/components/xoc-dapp/Swap/SwapButton.tsx new file mode 100644 index 0000000..519b1f3 --- /dev/null +++ b/packages/nextjs/components/xoc-dapp/Swap/SwapButton.tsx @@ -0,0 +1,65 @@ +import React from "react"; +import { GetBalances } from "./SwapInputBox"; +import { NUMBER_REGEX } from "./utils"; +import type { actionType } from "./utils"; +import { useConnectModal } from "@rainbow-me/rainbowkit"; +import { scroll } from "viem/chains"; +import { useAccount, useNetwork, useSwitchNetwork } from "wagmi"; +import { ArrowsRightLeftIcon } from "@heroicons/react/24/outline"; + +type SwapButtonProps = { + action: actionType; + swap: () => void; + isLoading: boolean; + value: string; +}; + +const SwapButton: React.FC = ({ action, swap, isLoading, value }) => { + const { isConnected, isConnecting } = useAccount(); + const { chain: connectedChain } = useNetwork(); + const { switchNetwork } = useSwitchNetwork(); + const { openConnectModal } = useConnectModal(); + const { data: Balances } = GetBalances({ token: "All" }); + const inSufficientFunds = + action === "Wrap" ? Balances.ethBalance < Number(value) : Balances.wethBalance < Number(value); + + return ( + <> + {isConnected && connectedChain ? ( + + ) : connectedChain && connectedChain?.id !== scroll.id ? ( + + ) : ( + + )} + + ); +}; + +export default SwapButton; diff --git a/packages/nextjs/components/xoc-dapp/Swap/SwapFee.tsx b/packages/nextjs/components/xoc-dapp/Swap/SwapFee.tsx new file mode 100644 index 0000000..b3b4781 --- /dev/null +++ b/packages/nextjs/components/xoc-dapp/Swap/SwapFee.tsx @@ -0,0 +1,20 @@ +import useIsMounted from "../../../hooks/scaffold-eth/isMounted"; +import { useFeeData } from "wagmi"; + +function SwapFee() { + const isMouted = useIsMounted(); + //fix hydration error + const { data, isError, isLoading } = useFeeData({ + watch: true, + }); + + if (isMouted && isLoading) return ~$0.00; + if (isMouted && isError) return
Error fetching fee data
; + // const gasPrice = Number(data?.formatted.gasPrice); + if (isMouted && data) + return ( + ~${Number(data?.formatted.gasPrice).toFixed(3)} + ); +} + +export default SwapFee; diff --git a/packages/nextjs/components/xoc-dapp/Swap/SwapInputBox.tsx b/packages/nextjs/components/xoc-dapp/Swap/SwapInputBox.tsx new file mode 100644 index 0000000..b0acb28 --- /dev/null +++ b/packages/nextjs/components/xoc-dapp/Swap/SwapInputBox.tsx @@ -0,0 +1,86 @@ +import React from "react"; +import Image from "next/image"; +import { InputBase } from "../../scaffold-eth/Input/InputBase"; +import { NUMBER_REGEX, stripWeth } from "./utils"; +import type { tokenType } from "./utils"; +import { useAccount } from "wagmi"; +import { useAccountBalance, useScaffoldContractRead } from "~~/hooks/scaffold-eth"; +import useIsMounted from "~~/hooks/scaffold-eth/isMounted"; + +interface Props { + isInput?: boolean; + token: tokenType; + value: string; + setValue: (value: string) => void; +} + +const SwapInputBox: React.FC = ({ isInput, token, value, setValue }) => { + const ticker = token === "Ether" ? "ETH" : "WETH"; + + const { jsx: Balance } = GetBalances({ token }); + + return ( +
+

{token}

+
+
+ {`${ticker} + {ticker} +
+ setValue(value)} + error={Boolean(value) && !NUMBER_REGEX.test(value)} + disabled={!isInput} + /> +
+
+ Balance: {Balance} +
+
+ ); +}; + +export default SwapInputBox; + +export function GetBalances({ token }: { token: tokenType }) { + const { address } = useAccount(); + const isMounted = useIsMounted(); + const account = isMounted && address ? address : ""; + const { balance, isError, isLoading } = useAccountBalance(account); + const { + data, + isError: isErrorWeth, + isLoading: isLoadingWeth, + } = useScaffoldContractRead({ contractName: "WETH9", functionName: "balanceOf", args: [account] }); + + let jsxToRender = 0.00; + let balanceData = { ethBalance: 0, wethBalance: 0 }; + + if (token === "All") + balanceData = { + ethBalance: Number(balance?.toFixed(4) as string), + wethBalance: stripWeth(data), + }; + else if (token === "Wrapped Ether") { + if (isLoadingWeth) jsxToRender = ; + if (isErrorWeth) jsxToRender = Error; + jsxToRender = {data !== undefined ? stripWeth(data) : "0"}; + } else { + if (isLoading) jsxToRender = ; + if (isError) jsxToRender = Error; + jsxToRender = {balance ? balance.toFixed(4) : "0"}; + } + return { jsx: jsxToRender, data: balanceData }; +} diff --git a/packages/nextjs/components/xoc-dapp/Swap/index.tsx b/packages/nextjs/components/xoc-dapp/Swap/index.tsx new file mode 100644 index 0000000..3323c97 --- /dev/null +++ b/packages/nextjs/components/xoc-dapp/Swap/index.tsx @@ -0,0 +1,52 @@ +import { useState } from "react"; +import Arrow from "../../../public/Arrow"; +import GasStation from "../../../public/GasStation"; +import SwapButton from "./SwapButton"; +import SwapFee from "./SwapFee"; +import SwapInputBox from "./SwapInputBox"; +import type { actionType } from "./utils"; +import { NUMBER_REGEX } from "./utils"; +import { parseEther } from "viem"; +import { useScaffoldContractWrite } from "~~/hooks/scaffold-eth"; + +const Swap = () => { + const [action, setAction] = useState("Wrap"); + const [value, setValue] = useState(""); + + // Blockcalls + const { writeAsync: depositETH, isLoading: isWrapping } = useScaffoldContractWrite({ + contractName: "WETH9", + functionName: "deposit", + value: NUMBER_REGEX.test(value) ? parseEther(value) : undefined, + }); + const { writeAsync: withdrawETH, isLoading: isUnwrapping } = useScaffoldContractWrite({ + contractName: "WETH9", + functionName: "withdraw", + args: [NUMBER_REGEX.test(value) ? parseEther(value) : undefined], + }); + + return ( +
+

{action}

+ +
+ setAction(action === "Wrap" ? "Unwrap" : "Wrap")} + className="p-2 rounded-lg bg-secondary w-[35px] h-[35px] cursor-pointer border-[#243c5a] border-[3px] m-auto absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 transition-all duration-300 hover:rotate-180" + /> +
+ +
+ +
+ +
+ ); +}; + +export default Swap; diff --git a/packages/nextjs/components/xoc-dapp/Swap/utils.ts b/packages/nextjs/components/xoc-dapp/Swap/utils.ts new file mode 100644 index 0000000..9599788 --- /dev/null +++ b/packages/nextjs/components/xoc-dapp/Swap/utils.ts @@ -0,0 +1,15 @@ +//TYPES +export type tokenType = "Ether" | "Wrapped Ether" | "All"; +export type actionType = "Wrap" | "Unwrap"; + +// UTILS + +export const NUMBER_REGEX = /^\.?\d+\.?\d*$/; +export const stripWeth = (token: bigint | undefined) => { + let weth = 0; + if (token !== undefined) { + const _weth = Number(token) / 1e18; + _weth === 0 ? (weth = 0) : (weth = _weth).toFixed(4); + } + return weth; +}; diff --git a/packages/nextjs/hooks/scaffold-eth/isMounted.tsx b/packages/nextjs/hooks/scaffold-eth/isMounted.tsx new file mode 100644 index 0000000..d9e3c80 --- /dev/null +++ b/packages/nextjs/hooks/scaffold-eth/isMounted.tsx @@ -0,0 +1,13 @@ +import { useEffect, useRef } from "react"; + +function useIsMounted() { + const isMountedRef = useRef(false); + + useEffect(() => { + isMountedRef.current = true; + }, []); + + return isMountedRef.current; +} + +export default useIsMounted; diff --git a/packages/nextjs/pages/index.tsx b/packages/nextjs/pages/index.tsx index 56593cc..bdf8f05 100644 --- a/packages/nextjs/pages/index.tsx +++ b/packages/nextjs/pages/index.tsx @@ -1,38 +1,86 @@ -/* import Link from "next/link"; */ +import Image from "next/image"; +import Link from "next/link"; import { benefitOne, benefitTwo } from "../components/index/data"; import type { NextPage } from "next"; - -/* import { BanknotesIcon, ChatBubbleLeftRightIcon, PhotoIcon } from "@heroicons/react/24/outline"; */ +import { MagnifyingGlassIcon, SparklesIcon } from "@heroicons/react/24/outline"; import { MetaHeader } from "~~/components/MetaHeader"; -import Video from "~~/components/index/Video"; import Benefits from "~~/components/index/benefits"; import Cta from "~~/components/index/cta"; import Faq from "~~/components/index/faq"; import Hero from "~~/components/index/hero"; +import ProtocolNumbers from "~~/components/index/protocolNumbers"; import SectionTitle from "~~/components/index/sectionTitle"; import Testimonials from "~~/components/index/testimonials"; - -/* import PopUpWidget from "~~/components/index/popupWidget"; */ +import grow from "~~/public/grow.png"; const Home: NextPage = () => { return ( <> - - Nextly is a free landing page & marketing website template for startups and indie projects. Its built with - Next.js & TailwindCSS. And its completely open-source. +
+
+ Hero Illustration +
+
+
+
+
+
+ +
+
+

+ Fuente:{" "} + + Uniswap V3{" "} + +

+
+
+
+ +

+ Experiment with{" "} + + Example UI + {" "} + to build your own UI. +

+
+
+ +

+ Explore your local transactions with the{" "} + + Block Explorer + {" "} + tab. +

+
+
+
+ + El uso de $XOC es la unica forma de integrarte a la comunidad emergente Web3 que estar creciendo en México y el + mundo. Aquí te dejamos algunos beneficios de usar $XOC. - - This section is to highlight a promo or demo video of your product. Analysts says a landing page with video has - 3% more conversion rate. So, don't forget to add one. Just like this. + + Aprende como funciona la magia de $XOC y como puedes empezar a usarlo. Aquí te dejamos un video para que te des + una idea de como funciona el protocolo y donde queda mas trabajo por hacer. -