Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improving the Buy Xoc UI #10

Merged
merged 1 commit into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 45 additions & 84 deletions packages/nextjs/components/index/hero.tsx
Original file line number Diff line number Diff line change
@@ -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<bigint>(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 (
<>
<Container className="flex flex-wrap">
<div className="flex items-center w-full lg:w-1/2">
<div className="max-w-2xl m-8 text-justify">
<h1 className="text-4xl font-bold leading-snug tracking-tight text-gray-800 lg:text-4xl lg:leading-tight xl:text-6xl xl:leading-tight dark:text-inherit">
Bienvenidos
<br />A Scaffold-XOC
Welcome
<br /> to Scaffold-XOC
</h1>
<h2 className="text-2xl font-semibold leading-normal text-gray-500 lg:text-2xl xl:text-xl dark:text-inherit">
¡Nos complace tenerte aquí!
¡A decentralized app for Mexico's #1 decentralized stablecoin!
</h2>
<p className="py-5 text-xl leading-normal text-gray-500 lg:text-xl dark:text-inherit">
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{" "}
<a
href="https://scaffoldeth.io/"
target="_blank"
Expand All @@ -108,14 +89,14 @@ const Hero = () => {
>
Scaffold-Eth-2.
</a>{" "}
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.
</p>
<p className="py-5 text-xl leading-normal text-gray-500 lg:text-base dark:text-inherit">
¡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!
</p>

<div className="flex flex-col items-start space-y-3 sm:space-x-4 sm:space-y-0 sm:items-center sm:flex-row">
Expand All @@ -128,11 +109,11 @@ const Hero = () => {
<dialog id="my_modal_1" className="modal">
<div className="modal-box">
<h3 className="font-bold text-lg">BUY $XOC</h3>
<p className="py-4">
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.
</p>
<h3>Token In: ~0.002 Wrapped Ether</h3>
<p className="py-4">Buy 100 XOC on Uniswap.</p>
<h3>
Token In: {expectedAmountIn ? parseFloat(formatEther(expectedAmountIn)).toFixed(5) : "reading"}{" "}
Wrapped Ether
</h3>
<h3>Token Out: 100 XOC</h3>
<div className=" mt-12">
<button className="btn mr-5" onClick={() => approve()}>
Expand All @@ -151,26 +132,6 @@ const Hero = () => {
</div>
</div>
</dialog>
<a
href="https://github.com/iafhurtado/scaffold-xoc"
target="_blank"
rel="noreferrer noopener"
className="flex items-center space-x-2 text-gray-500 dark:text-gray-400"
>
<svg
role="img"
width="24"
height="24"
className="w-5 h-5"
viewBox="0 0 24 24"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
>
<title>GitHub</title>
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12" />
</svg>
<span> Clonar el Repositorio</span>
</a>
</div>
</div>
</div>
Expand Down Expand Up @@ -234,7 +195,7 @@ const Hero = () => {
<div className="flex flex-col justify-center mt-24 mb-14">
<div className="text-xl text-center text-inherit dark:text-inherit">
<h2>
Mas de <span className=" text-green-500">1 millon de mexicanos</span> ya usan $XOC en sus empresas
$XOC is the <span className=" text-green-500">most liquid stablecoin</span> for the MXN currency.
</h2>
</div>

Expand Down
36 changes: 4 additions & 32 deletions packages/nextjs/components/index/protocolNumbers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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()],
Expand Down
118 changes: 118 additions & 0 deletions packages/nextjs/components/xoc-dapp/abis/xocabis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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" },
Expand Down
Loading
Loading