diff --git a/.env.example b/.env.example index 33bc1b4..24aea0a 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,6 @@ VITE_REOWN_PROJECT_ID=YOUR_PROJECT_ID +VITE_ALCHEMY_API_KEY=YOUR_ALCHEMY_API_KEY VITE_ETHEREUM_MAINNET_RPC=YOUR_MAINNET_RPC_URL -VITE_ETHREUM_SEPOLIA_RPC=YOUR_SEPOLIA_RPC_URL \ No newline at end of file +VITE_ETHEREUM_SEPOLIA_RPC=YOUR_SEPOLIA_RPC_URL +VITE_IPFS_UPLOAD_URL=YOUR_IPFS_UPLOAD_URL +VITE_IPFS_GATEWAY_URL=YOUR_IPFS_GATEWAY_URL \ No newline at end of file diff --git a/.gitignore b/.gitignore index 4abe73e..af20c3c 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,6 @@ dist-ssr # Environment variables .env + +# Yarn +.yarn/install-state.gz \ No newline at end of file diff --git a/.yarn/install-state.gz b/.yarn/install-state.gz deleted file mode 100644 index e7a62aa..0000000 Binary files a/.yarn/install-state.gz and /dev/null differ diff --git a/README.md b/README.md index 5de58a6..f7e61bf 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,22 @@ [![Netlify Status](https://api.netlify.com/api/v1/badges/4cb4168d-2078-400e-a159-4e52e44175ea/deploy-status)](https://app.netlify.com/projects/kleros-escrow-v1/deploys) -Local development: +For local development: + +1. Install dependencies: + +```bash +yarn install +``` + +2. Generate hooks using `wagmi/cli`: + +```bash +yarn generate +``` + +3. Run: ```bash -yarn install && yarn dev +yarn dev ``` diff --git a/netlify.toml b/netlify.toml new file mode 100644 index 0000000..b87b8d3 --- /dev/null +++ b/netlify.toml @@ -0,0 +1,4 @@ +[[redirects]] + from = "/*" + to = "/index.html" + status = 200 diff --git a/package.json b/package.json index fb806ef..e62a190 100644 --- a/package.json +++ b/package.json @@ -7,14 +7,17 @@ "dev": "vite", "build": "tsc -b && vite build", "lint": "eslint .", - "preview": "vite preview" + "preview": "vite preview", + "generate": "wagmi generate" }, "dependencies": { - "@kleros/ui-components-library": "^3.3.4", + "@kleros/ui-components-library": "^3.6.0", "@reown/appkit": "^1.7.6", "@reown/appkit-adapter-wagmi": "^1.7.6", "@tanstack/react-query": "^5.76.1", + "alchemy-sdk": "^3.6.1", "react": "^18.2.55", + "react-countdown": "^2.3.6", "react-dom": "^18.2.55", "react-router": "^7.6.0", "styled-components": "^6.1.18", @@ -26,6 +29,7 @@ "@types/react": "^18.2.55", "@types/react-dom": "^18.2.18", "@vitejs/plugin-react": "^4.4.1", + "@wagmi/cli": "^2.3.1", "eslint": "^9.25.0", "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.19", diff --git a/src/App.tsx b/src/App.tsx index 29450c4..300d1b4 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,7 +1,9 @@ import { BrowserRouter, Routes, Route } from "react-router"; import { Providers } from "./providers"; import Layout from "layout/Layout"; -import Home from "components/Home/Home"; +import Home from "pages/Home/Home"; +import Transaction from "pages/Transaction/Transaction"; +import New from "pages/New/New"; function App() { return ( @@ -10,6 +12,11 @@ function App() { }> } /> + } + /> + } /> diff --git a/src/assets/aave.png b/src/assets/aave.png new file mode 100644 index 0000000..39c9bc9 Binary files /dev/null and b/src/assets/aave.png differ diff --git a/src/assets/arb.png b/src/assets/arb.png new file mode 100644 index 0000000..c106161 Binary files /dev/null and b/src/assets/arb.png differ diff --git a/src/assets/close.svg b/src/assets/close.svg new file mode 100644 index 0000000..78cbdbb --- /dev/null +++ b/src/assets/close.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/crypto-transaction.png b/src/assets/crypto-transaction.png new file mode 100644 index 0000000..0a0d0c3 Binary files /dev/null and b/src/assets/crypto-transaction.png differ diff --git a/src/assets/dai.png b/src/assets/dai.png new file mode 100644 index 0000000..614a3cb Binary files /dev/null and b/src/assets/dai.png differ diff --git a/src/assets/doc.svg b/src/assets/doc.svg new file mode 100644 index 0000000..ba8258a --- /dev/null +++ b/src/assets/doc.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/escrow-type.png b/src/assets/escrow-type.png new file mode 100644 index 0000000..7135831 Binary files /dev/null and b/src/assets/escrow-type.png differ diff --git a/src/assets/eth.png b/src/assets/eth.png new file mode 100644 index 0000000..5d2d214 Binary files /dev/null and b/src/assets/eth.png differ diff --git a/src/assets/etherscan.svg b/src/assets/etherscan.svg new file mode 100644 index 0000000..37ec3d2 --- /dev/null +++ b/src/assets/etherscan.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/assets/general-service.png b/src/assets/general-service.png new file mode 100644 index 0000000..e9394a6 Binary files /dev/null and b/src/assets/general-service.png differ diff --git a/src/assets/gno.png b/src/assets/gno.png new file mode 100644 index 0000000..26685a7 Binary files /dev/null and b/src/assets/gno.png differ diff --git a/src/assets/info-circle-outline.svg b/src/assets/info-circle-outline.svg new file mode 100644 index 0000000..49f1463 --- /dev/null +++ b/src/assets/info-circle-outline.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/link.png b/src/assets/link.png new file mode 100644 index 0000000..efdccaf Binary files /dev/null and b/src/assets/link.png differ diff --git a/src/assets/pnk.png b/src/assets/pnk.png new file mode 100644 index 0000000..737556d Binary files /dev/null and b/src/assets/pnk.png differ diff --git a/src/assets/pol.png b/src/assets/pol.png new file mode 100644 index 0000000..d24c53b Binary files /dev/null and b/src/assets/pol.png differ diff --git a/src/assets/uni.png b/src/assets/uni.png new file mode 100644 index 0000000..5f9de5a Binary files /dev/null and b/src/assets/uni.png differ diff --git a/src/assets/unknowntoken.png b/src/assets/unknowntoken.png new file mode 100644 index 0000000..c1b3faa Binary files /dev/null and b/src/assets/unknowntoken.png differ diff --git a/src/assets/usdc.png b/src/assets/usdc.png new file mode 100644 index 0000000..980ba8d Binary files /dev/null and b/src/assets/usdc.png differ diff --git a/src/assets/usdt.png b/src/assets/usdt.png new file mode 100644 index 0000000..7204850 Binary files /dev/null and b/src/assets/usdt.png differ diff --git a/src/assets/warning-circle-outline.svg b/src/assets/warning-circle-outline.svg new file mode 100644 index 0000000..8aad069 --- /dev/null +++ b/src/assets/warning-circle-outline.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/weth.png b/src/assets/weth.png new file mode 100644 index 0000000..cd5acf0 Binary files /dev/null and b/src/assets/weth.png differ diff --git a/src/components/common/IconButton.ts b/src/components/Common/Buttons/IconButton.ts similarity index 50% rename from src/components/common/IconButton.ts rename to src/components/Common/Buttons/IconButton.ts index efa8d6b..9d96ca0 100644 --- a/src/components/common/IconButton.ts +++ b/src/components/Common/Buttons/IconButton.ts @@ -1,16 +1,21 @@ import styled from "styled-components"; import { Button } from "@kleros/ui-components-library"; -export const IconButton = styled(Button)` +export const IconButton = styled(Button)<{ customSVGColor?: boolean }>` background: transparent; border: none; padding: 0; width: 32px; height: 32px; - path { - /* dark mode secondary text color */ - fill: #becce5; - } + + ${({ customSVGColor = true }) => + customSVGColor && + ` + path { + /* dark mode secondary text color */ + fill: #becce5; + } + `} &:hover { opacity: 0.8; diff --git a/src/components/Common/Containers/DefaultPageContainer.ts b/src/components/Common/Containers/DefaultPageContainer.ts new file mode 100644 index 0000000..6ed982d --- /dev/null +++ b/src/components/Common/Containers/DefaultPageContainer.ts @@ -0,0 +1,19 @@ +import styled from "styled-components"; + +export const DefaultPageContainer = styled.div` + display: flex; + flex-direction: column; + height: 100%; + gap: 16px; + align-items: center; + overflow-y: auto; + padding: 64px; + + @media (max-width: ${({ theme }) => theme.breakpoints.md}) { + padding: 32px; + } + + @media (max-width: ${({ theme }) => theme.breakpoints.sm}) { + padding: 16px; + } +`; diff --git a/src/components/Common/Display/AddressLinkAndCopy.tsx b/src/components/Common/Display/AddressLinkAndCopy.tsx new file mode 100644 index 0000000..f6e97e9 --- /dev/null +++ b/src/components/Common/Display/AddressLinkAndCopy.tsx @@ -0,0 +1,40 @@ +import { Copiable } from "@kleros/ui-components-library"; +import { IconButton } from "../Buttons/IconButton"; +import EtherscanIcon from "assets/etherscan.svg?react"; +import styled from "styled-components"; +import { addressToShortString } from "utils/common"; + +interface Props { + address: string; +} + +const Container = styled.div` + display: flex; + margin-left: -0.5rem; +`; + +/* + * NOTE: If in dev and using the Sepolia network, the link will still point to mainnet. This is fine because prod is only available on mainnet, so no need for any logic for dynamic linking. + * Additionally, for existing transactions, there is no easy way to know the chain they are on, because there's no guarantee that that information will be in the metaevidence. + */ +export default function AddressLinkAndCopy({ address }: Props) { + return ( + + + } + text="" + customSVGColor={false} + /> + + +

{addressToShortString(address)}

+
+
+ ); +} diff --git a/src/components/Common/Dividers/DefaultDivider.ts b/src/components/Common/Dividers/DefaultDivider.ts new file mode 100644 index 0000000..8d2c8bf --- /dev/null +++ b/src/components/Common/Dividers/DefaultDivider.ts @@ -0,0 +1,5 @@ +import styled from "styled-components"; + +export const DefaultDivider = styled.hr` + color: ${({ theme }) => theme.colors.stroke}; +`; diff --git a/src/components/Common/Form/StyledDisplaySmall.ts b/src/components/Common/Form/StyledDisplaySmall.ts new file mode 100644 index 0000000..02d1618 --- /dev/null +++ b/src/components/Common/Form/StyledDisplaySmall.ts @@ -0,0 +1,20 @@ +import styled from "styled-components"; +import { DisplaySmall } from "@kleros/ui-components-library"; + +export const StyledDisplaySmall = styled(DisplaySmall)` + overflow: hidden; + white-space: nowrap; + height: fit-content; + + label { + font-weight: bold; + } + + div { + margin-top: 0; + } + + h2 { + font-weight: normal; + } +`; diff --git a/src/components/Common/Form/StyledForm.ts b/src/components/Common/Form/StyledForm.ts new file mode 100644 index 0000000..17868f1 --- /dev/null +++ b/src/components/Common/Form/StyledForm.ts @@ -0,0 +1,22 @@ +import { Form } from "@kleros/ui-components-library"; +import styled, { css } from "styled-components"; + +export const StyledForm = styled(Form)` + display: flex; + flex-direction: column; + align-items: center; + width: fit-content; + max-width: 80%; + gap: 16px; +`; + +export const ButtonContainer = styled.div` + display: flex; + gap: 16px; +`; + +export const mobileResponsive = css` + @media (max-width: ${({ theme }) => theme.breakpoints.sm}) { + width: 250px; + } +`; diff --git a/src/components/Common/Modal/StyledModal.ts b/src/components/Common/Modal/StyledModal.ts new file mode 100644 index 0000000..9f5bdfe --- /dev/null +++ b/src/components/Common/Modal/StyledModal.ts @@ -0,0 +1,21 @@ +import { Modal } from "@kleros/ui-components-library"; +import styled from "styled-components"; +import { mobileResponsive } from "../Form/StyledForm"; + +export const StyledModal = styled(Modal)<{ width?: string }>` + width: ${({ width }) => width ?? "500px"}; + height: 100%; + max-height: 500px; + display: flex; + flex-direction: column; + gap: 1rem; + padding: 1rem; + border-radius: ${({ theme }) => theme.radius.boxDefault}; + overflow-y: auto; + + @media (max-width: ${({ theme }) => theme.breakpoints.md}) { + width: 500px; + } + + ${mobileResponsive} +`; diff --git a/src/components/Common/Skeleton/BaseSkeleton.ts b/src/components/Common/Skeleton/BaseSkeleton.ts new file mode 100644 index 0000000..fafd25b --- /dev/null +++ b/src/components/Common/Skeleton/BaseSkeleton.ts @@ -0,0 +1,6 @@ +import styled from "styled-components"; + +export const BaseSkeleton = styled.div` + animation: ${({ theme }) => theme.animations.loading}; + background-color: ${({ theme }) => theme.colors.tintPurple}; +`; diff --git a/src/components/CreateTransactionWizard/CreateTransactionWizard.tsx b/src/components/CreateTransactionWizard/CreateTransactionWizard.tsx new file mode 100644 index 0000000..9db1c67 --- /dev/null +++ b/src/components/CreateTransactionWizard/CreateTransactionWizard.tsx @@ -0,0 +1,62 @@ +import { Steps } from "@kleros/ui-components-library"; +import { useState } from "react"; +import styled from "styled-components"; +import Template from "./Template/Template"; +import Details from "./Details/Details"; +import Terms from "./Terms/Terms"; +import Preview from "./Preview/Preview"; + +const STEPS = [ + { title: "Escrow type" }, + { title: "Details" }, + { title: "Terms" }, + { title: "Preview" }, +]; + +const Container = styled.div` + position: relative; + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + + @media (max-height: ${({ theme }) => theme.breakpoints.md}) { + height: unset; + } +`; + +const StyledSteps = styled(Steps)` + position: absolute; + left: 4%; + top: 50%; + transform: translateY(-50%); + height: 200px; + + @media (max-width: ${({ theme }) => theme.breakpoints.lg}) { + display: none; + } +`; + +export default function CreateTransactionWizard() { + const [currentStep, setCurrentStep] = useState(0); + + const next = () => { + setCurrentStep(currentStep + 1); + }; + + const back = () => { + setCurrentStep(currentStep - 1); + }; + + return ( + + + + {currentStep === 0 &&