First edits made like in the video.
This commit is contained in:
@@ -1,10 +0,0 @@
|
||||
import { render } from "@testing-library/react";
|
||||
import React from "react";
|
||||
|
||||
import App from "./App";
|
||||
|
||||
test("renders learn react link", () => {
|
||||
const { getByText } = render(<App />);
|
||||
const linkElement = getByText(/learn react/i);
|
||||
expect(linkElement).toBeInTheDocument();
|
||||
});
|
||||
1
packages/react-app/src/assets/chevron-down.svg
Normal file
1
packages/react-app/src/assets/chevron-down.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="12" height="7" viewBox="0 0 12 7" fill="none" xmlns="http://www.w3.org/2000/svg" class="sc-3zewi2-11 kHQTSW"><path d="M0.97168 1L6.20532 6L11.439 1" stroke="#AEAEAE"></path></svg>
|
||||
|
After Width: | Height: | Size: 191 B |
|
Before Width: | Height: | Size: 211 KiB After Width: | Height: | Size: 211 KiB |
5
packages/react-app/src/assets/index.js
vendored
Normal file
5
packages/react-app/src/assets/index.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
import chevronDown from "./chevron-down.svg";
|
||||
import ethereumLogo from "./ethereumLogo.png";
|
||||
import uniswapLogo from "./uniswapLogo.png";
|
||||
|
||||
export { chevronDown, ethereumLogo, uniswapLogo };
|
||||
BIN
packages/react-app/src/assets/uniswapLogo.png
Normal file
BIN
packages/react-app/src/assets/uniswapLogo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
55
packages/react-app/src/components/index.js
vendored
55
packages/react-app/src/components/index.js
vendored
@@ -1,55 +0,0 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
export const Body = styled.div`
|
||||
align-items: center;
|
||||
color: white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: calc(10px + 2vmin);
|
||||
justify-content: center;
|
||||
margin-top: 40px;
|
||||
`;
|
||||
|
||||
export const Button = styled.button`
|
||||
background-color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
color: #282c34;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
margin: 0px 20px;
|
||||
padding: 12px 24px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
`;
|
||||
|
||||
export const Container = styled.div`
|
||||
background-color: #282c34;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: calc(100vh);
|
||||
`;
|
||||
|
||||
export const Header = styled.header`
|
||||
align-items: center;
|
||||
background-color: #282c34;
|
||||
color: white;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
min-height: 70px;
|
||||
`;
|
||||
|
||||
export const Image = styled.img`
|
||||
height: 40vmin;
|
||||
margin-bottom: 16px;
|
||||
pointer-events: none;
|
||||
`;
|
||||
|
||||
export const Link = styled.a.attrs({
|
||||
target: "_blank",
|
||||
rel: "noopener noreferrer",
|
||||
})`
|
||||
color: #61dafb;
|
||||
margin-top: 8px;
|
||||
`;
|
||||
10
packages/react-app/src/config.js
vendored
Normal file
10
packages/react-app/src/config.js
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Goerli } from "@usedapp/core";
|
||||
|
||||
export const ROUTER_ADDRESS = "[YOUR ADDRESS HERE]";
|
||||
|
||||
export const DAPP_CONFIG = {
|
||||
readOnlyChainId: Goerli.chainId,
|
||||
readOnlyUrls: {
|
||||
[Goerli.chainId]: "[YOUR URL HERE]",
|
||||
},
|
||||
};
|
||||
15
packages/react-app/src/graphql/subgraph.js
vendored
15
packages/react-app/src/graphql/subgraph.js
vendored
@@ -1,15 +0,0 @@
|
||||
import { gql } from "@apollo/client";
|
||||
|
||||
// See more example queries on https://thegraph.com/explorer/subgraph/paulrberg/create-eth-app
|
||||
const GET_TRANSFERS = gql`
|
||||
{
|
||||
transfers(first: 10) {
|
||||
id
|
||||
from
|
||||
to
|
||||
value
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default GET_TRANSFERS;
|
||||
@@ -1,11 +1,59 @@
|
||||
body {
|
||||
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@100;200;300;400;500;600;700;800;900&display=swap");
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
/* Chrome, Safari, Edge, Opera */
|
||||
input::-webkit-outer-spin-button,
|
||||
input::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans",
|
||||
"Droid Sans", "Helvetica Neue", sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||
/* Firefox */
|
||||
input[type="number"] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
||||
.gradient_btn {
|
||||
background: linear-gradient(
|
||||
103.91deg,
|
||||
#9b51e0 21.01%,
|
||||
rgba(48, 129, 237, 0.8) 100%
|
||||
);
|
||||
}
|
||||
|
||||
.pink_gradient {
|
||||
position: absolute;
|
||||
top: 0%;
|
||||
left: 0%;
|
||||
width: 123px;
|
||||
height: 123px;
|
||||
border-radius: 50%;
|
||||
|
||||
background: #fb37ff;
|
||||
filter: blur(200px);
|
||||
}
|
||||
|
||||
.blue_gradient {
|
||||
position: absolute;
|
||||
bottom: 0%;
|
||||
right: 0%;
|
||||
width: 123px;
|
||||
height: 123px;
|
||||
border-radius: 50%;
|
||||
|
||||
background: #18b2de;
|
||||
filter: blur(200px);
|
||||
}
|
||||
|
||||
.gradient-border {
|
||||
background: linear-gradient(
|
||||
168.82deg,
|
||||
#fb37ff 1.7%,
|
||||
rgba(155, 111, 238, 0) 27.12%,
|
||||
rgba(123, 127, 234, 0) 61.28%,
|
||||
#1bb2de 99.52%
|
||||
);
|
||||
}
|
||||
34
packages/react-app/src/index.js
vendored
34
packages/react-app/src/index.js
vendored
@@ -1,37 +1,17 @@
|
||||
import "./index.css";
|
||||
|
||||
import { ApolloClient, ApolloProvider, InMemoryCache } from "@apollo/client";
|
||||
import { DAppProvider, Mainnet } from "@usedapp/core";
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import { DAppProvider } from "@usedapp/core";
|
||||
|
||||
import App from "./App";
|
||||
import { DAPP_CONFIG } from "./config";
|
||||
|
||||
// IMPORTANT, PLEASE READ
|
||||
// To avoid disruptions in your app, change this to your own Infura project id.
|
||||
// https://infura.io/register
|
||||
const INFURA_PROJECT_ID = "529670718fd74cd2a041466303daecd7";
|
||||
const config = {
|
||||
readOnlyChainId: Mainnet.chainId,
|
||||
readOnlyUrls: {
|
||||
[Mainnet.chainId]: "https://mainnet.infura.io/v3/" + INFURA_PROJECT_ID,
|
||||
},
|
||||
}
|
||||
|
||||
// You should replace this url with your own and put it into a .env file
|
||||
// See all subgraphs: https://thegraph.com/explorer/
|
||||
const client = new ApolloClient({
|
||||
cache: new InMemoryCache(),
|
||||
uri: "https://api.thegraph.com/subgraphs/name/paulrberg/create-eth-app",
|
||||
});
|
||||
import "./index.css";
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<DAppProvider config={config}>
|
||||
<ApolloProvider client={client}>
|
||||
<App />
|
||||
</ApolloProvider>
|
||||
<DAppProvider config={DAPP_CONFIG}>
|
||||
<App />
|
||||
</DAppProvider>
|
||||
</React.StrictMode>,
|
||||
document.getElementById("root"),
|
||||
);
|
||||
document.getElementById("root")
|
||||
);
|
||||
5
packages/react-app/src/setupTests.js
vendored
5
packages/react-app/src/setupTests.js
vendored
@@ -1,5 +0,0 @@
|
||||
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
||||
// allows you to do things like:
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import "@testing-library/jest-dom/extend-expect";
|
||||
50
packages/react-app/src/styles/index.js
vendored
Normal file
50
packages/react-app/src/styles/index.js
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
const styles = {
|
||||
// App.js
|
||||
container: "flex justify-center min-h-screen sm:px-16 px-6 bg-site-black",
|
||||
innerContainer:
|
||||
"flex justify-between items-center flex-col max-w-[1280px] w-full",
|
||||
header: "flex flex-row justify-between items-center w-full sm:py-10 py-6",
|
||||
exchangeContainer:
|
||||
"flex-1 flex justify-start items-center flex-col w-full mt-10",
|
||||
headTitle: "text-white font-poppins font-black text-5xl tracking-wide",
|
||||
subTitle: "text-dim-white font-poppins font-medium mt-3 text-base",
|
||||
exchangeBoxWrapper: "mt-10 w-full flex justify-center",
|
||||
exchangeBox:
|
||||
"relative md:max-w-[700px] md:min-w-[500px] min-w-full max-w-full gradient-border p-[2px] rounded-3xl",
|
||||
exchange:
|
||||
"w-full min-h-[400px] bg-site-black backdrop-blur-[4px] rounded-3xl shadow-card flex p-10",
|
||||
|
||||
// AmountIn & AmountOut
|
||||
amountContainer:
|
||||
"flex justify-between items-center flex-row w-full min-w-full bg-site-dim border-[1px] border-transparent hover:border-site-dim2 min-h-[96px] sm:p-8 p-4 rounded-[20px]",
|
||||
amountInput:
|
||||
"w-full flex-1 bg-transparent outline-none font-poppins font-black text-2xl text-white",
|
||||
currencyButton:
|
||||
"flex flex-row items-center bg-site-dim2 py-2 px-4 rounded-xl font-poppins font-bold text-white",
|
||||
currencyList:
|
||||
"absolute z-10 right-0 bg-site-black border-[1px] border-site-dim2 w-full mt-2 rounded-lg min-w-[170px] overflow-hidden",
|
||||
currencyListItem:
|
||||
"font-poppins font-medium text-base text-white hover:text-dim-white px-5 py-3 hover:bg-site-dim2 cursor-pointer",
|
||||
|
||||
// Exchange
|
||||
actionButton:
|
||||
"border-none outline-none px-6 py-2 font-poppins font-bold text-lg rounded-2xl leading-[24px] transition-all min-h-[56px]",
|
||||
message: "font-poppins font-lg text-white font-bold mt-7",
|
||||
|
||||
// WalletButton
|
||||
walletButton:
|
||||
"bg-site-pink border-none outline-none px-6 py-2 font-poppins font-bold text-lg text-white rounded-3xl leading-[24px] hover:bg-pink-600 transition-all",
|
||||
|
||||
// loader
|
||||
loader: "flex justify-center items-center flex-col w-full min-h-full",
|
||||
loaderImg: "w-56 h-56 object-contain",
|
||||
loaderText:
|
||||
"font-poppins font-normal text-dim-white text-lg text-center mt-10",
|
||||
|
||||
// balance
|
||||
balance: "w-full text-left mt-2 ml-2",
|
||||
balanceText: "font-poppins font-normal text-dim-white",
|
||||
balanceBold: "font-semibold text-white",
|
||||
};
|
||||
|
||||
export default styles;
|
||||
110
packages/react-app/src/utils/helpers.js
vendored
Normal file
110
packages/react-app/src/utils/helpers.js
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
|
||||
import { Contract } from "@ethersproject/contracts";
|
||||
import { abis } from "@my-app/contracts";
|
||||
import { useCall } from "@usedapp/core";
|
||||
import { parseUnits } from "ethers/lib/utils";
|
||||
import { useEffect } from "react";
|
||||
|
||||
import { ROUTER_ADDRESS } from "../config";
|
||||
|
||||
export const getAvailableTokens = (pools) =>
|
||||
pools.reduce((prev, curr) => {
|
||||
prev[curr.token0Address] = curr.token0Name;
|
||||
prev[curr.token1Address] = curr.token1Name;
|
||||
return prev;
|
||||
}, {});
|
||||
|
||||
export const getCounterpartTokens = (pools, fromToken) => pools
|
||||
.filter((cur) => cur.token0Address === fromToken || cur.token1Address)
|
||||
.reduce((prev, curr) => {
|
||||
if (curr.token0Address === fromToken) {
|
||||
prev[curr.token1Address] = curr.token1Name;
|
||||
} else if (curr.token1Address === fromToken) {
|
||||
prev[curr.token0Address] = curr.token0Name;
|
||||
}
|
||||
return prev;
|
||||
}, {});
|
||||
|
||||
|
||||
export const findPoolByTokens = (pools, fromToken, toToken) => {
|
||||
if (!Array.isArray(pools) || !fromToken || !toToken) return undefined;
|
||||
|
||||
return pools.find((cur) =>
|
||||
(cur.token0Address === fromToken && cur.token1Address === toToken) ||
|
||||
(cur.token1Address === fromToken && cur.token0Address === toToken)
|
||||
);
|
||||
};
|
||||
|
||||
export const isOperationPending = (operationState) =>
|
||||
operationState.status === "PendingSignature" || operationState.status === "Mining";
|
||||
export const isOperationFailed = (operationState) =>
|
||||
operationState.status === "Fail" || operationState.status === "Exception";
|
||||
export const isOperationSucceeded = (operationState) =>
|
||||
operationState.status === "Success";
|
||||
|
||||
export const getFailureMessage = (swapApproveState, swapExecuteState) => {
|
||||
if (isOperationPending(swapApproveState) || isOperationPending(swapExecuteState)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (isOperationFailed(swapApproveState)) {
|
||||
return "Approval failed - " + swapApproveState.errorMessage;
|
||||
}
|
||||
|
||||
if (isOperationFailed(swapExecuteState)) {
|
||||
return "Swap failed - " + swapExecuteState.errorMessage;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const getSuccessMessage = (swapApproveState, swapExecuteState) => {
|
||||
if (isOperationPending(swapExecuteState) ||isOperationPending(swapApproveState)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (isOperationSucceeded(swapExecuteState)) {
|
||||
return "Swap executed successfully";
|
||||
}
|
||||
|
||||
if (isOperationSucceeded(swapApproveState)) {
|
||||
return "Approval successful";
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const useAmountsOut = (pairAddress, amountIn, fromToken, toToken) => {
|
||||
const isValidAmountIn = amountIn.gt(parseUnits("0"));
|
||||
const areParamsValid = !!(pairAddress && isValidAmountIn && fromToken && toToken);
|
||||
|
||||
const { error, value } =
|
||||
useCall(
|
||||
areParamsValid && {
|
||||
contract: new Contract(ROUTER_ADDRESS, abis.router02),
|
||||
method: "getAmountsOut",
|
||||
args: [amountIn, [fromToken, toToken]],
|
||||
}
|
||||
) ?? {};
|
||||
return error ? parseUnits("0") : value?.amounts[1];
|
||||
}
|
||||
|
||||
export const useOnClickOutside = (ref, handler) => {
|
||||
useEffect(() => {
|
||||
const listener = (event) => {
|
||||
// Do nothing if clicking ref's element or descendent elements
|
||||
if (!ref.current || ref.current.contains(event.target)) {
|
||||
return;
|
||||
}
|
||||
handler(event);
|
||||
};
|
||||
|
||||
document.addEventListener("mousedown", listener);
|
||||
document.addEventListener("touchstart", listener);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("mousedown", listener);
|
||||
document.removeEventListener("touchstart", listener);
|
||||
};
|
||||
}, [ref, handler]);
|
||||
}
|
||||
10
packages/react-app/src/utils/index.js
vendored
Normal file
10
packages/react-app/src/utils/index.js
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
export {
|
||||
getAvailableTokens,
|
||||
getCounterpartTokens,
|
||||
findPoolByTokens,
|
||||
isOperationPending,
|
||||
getFailureMessage,
|
||||
getSuccessMessage,
|
||||
useAmountsOut,
|
||||
useOnClickOutside
|
||||
} from './helpers';
|
||||
Reference in New Issue
Block a user