├── .vscode └── settings.json ├── assets ├── eth.png ├── dexterity.png └── ethCurrency.png ├── public ├── bg.png ├── favicon.ico ├── dexterity-bg.png ├── vercel.svg └── bg-dex.svg ├── prettier.config.js ├── styles └── globals.css ├── postcss.config.js ├── lib ├── constants.js ├── sanityClient.js └── Transactions.json ├── tailwind.config.js ├── next-env.d.ts ├── pages ├── _app.js ├── api │ └── hello.ts └── index.js ├── tsconfig.json ├── .gitignore ├── components ├── TransactionLoader.js ├── TransactionHistory.js ├── Main.js └── Header.js ├── package.json ├── context └── TransactionContext.js └── README.md /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "trace.server": "off" 3 | } -------------------------------------------------------------------------------- /assets/eth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vdutts7/dexterity/HEAD/assets/eth.png -------------------------------------------------------------------------------- /public/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vdutts7/dexterity/HEAD/public/bg.png -------------------------------------------------------------------------------- /assets/dexterity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vdutts7/dexterity/HEAD/assets/dexterity.png -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | singleQuote: true, 3 | semi: false, 4 | } 5 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vdutts7/dexterity/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /styles/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /assets/ethCurrency.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vdutts7/dexterity/HEAD/assets/ethCurrency.png -------------------------------------------------------------------------------- /public/dexterity-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vdutts7/dexterity/HEAD/public/dexterity-bg.png -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /lib/constants.js: -------------------------------------------------------------------------------- 1 | import abi from './Transactions.json' 2 | 3 | export const contractABI = abi.abi 4 | export const contractAddress = '0x6AC1102758E5EaF9131134d5d5558dc4c425d6Cd' 5 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: [ 3 | './pages/**/*.{js,ts,jsx,tsx}', 4 | './components/**/*.{js,ts,jsx,tsx}', 5 | ], 6 | theme: { 7 | extend: {}, 8 | }, 9 | plugins: [], 10 | } 11 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /pages/_app.js: -------------------------------------------------------------------------------- 1 | import '../styles/globals.css' 2 | import { TransactionProvider } from '../context/TransactionContext' 3 | 4 | function MyApp({ Component, pageProps }) { 5 | return ( 6 | 7 | 8 | 9 | ) 10 | } 11 | 12 | export default MyApp -------------------------------------------------------------------------------- /pages/api/hello.ts: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | import type { NextApiRequest, NextApiResponse } from 'next' 3 | 4 | type Data = { 5 | name: string 6 | } 7 | 8 | export default function handler( 9 | req: NextApiRequest, 10 | res: NextApiResponse 11 | ) { 12 | res.status(200).json({ name: 'John Doe' }) 13 | } 14 | -------------------------------------------------------------------------------- /lib/sanityClient.js: -------------------------------------------------------------------------------- 1 | import sanityClient from '@sanity/client' 2 | 3 | export const client = sanityClient({ 4 | projectId: 'o4vohdlc', 5 | dataset: 'production', 6 | apiVersion: 'v1', 7 | token: 8 | 'skr99odBxLl4VzrrjUUCOnPrCKSnz0LCJhK5nILCuGfLfYmyTEHvLAn41dkuQQs2vHHiqv230ZVDOsFizi94NcG8daOwl9eI8f8gaghIpZRev1wGyr0XcEHW745Z0visirLlOz2iYJ1BNXBrVxKxYBTQhrNJjttEXydNZWmGODnjNPbFHjgy', 9 | useCdn: false, 10 | ignoreBrowserTokenWarning: true, 11 | }) -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true 17 | }, 18 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 19 | "exclude": ["node_modules"] 20 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | .env 33 | 34 | # vercel 35 | .vercel 36 | 37 | # typescript 38 | *.tsbuildinfo 39 | -------------------------------------------------------------------------------- /components/TransactionLoader.js: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react' 2 | import { MoonLoader } from 'react-spinners' 3 | 4 | const style = { 5 | wrapper: `text-white h-96 w-72 flex flex-col justify-center items-center`, 6 | title: `font-semibold text-xl mb-12`, 7 | } 8 | 9 | const cssOverride = css` 10 | display: block; 11 | margin: 0 auto; 12 | border-color: white; 13 | ` 14 | 15 | const TransactionLoader = () => { 16 | return ( 17 |
18 |
Transaction in progress...
19 | 20 |
21 | ) 22 | } 23 | 24 | export default TransactionLoader -------------------------------------------------------------------------------- /pages/index.js: -------------------------------------------------------------------------------- 1 | import Head from 'next/head' 2 | import Header from '../components/Header' 3 | import Main from '../components/Main' 4 | import TransactionHistory from '../components/TransactionHistory' 5 | const style = { 6 | wrapper: `h-screen max-h-screen h-min-screen w-screen bg-cover bg-center text-white select-none flex flex-col justify-between`, 7 | } 8 | 9 | export default function Home() { 10 | return( 11 |
15 | 16 | {/* Add page title and meta tags here */} 17 | 18 |
19 |
20 | 21 |
22 | ) 23 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "next dev", 5 | "build": "next build", 6 | "start": "next start" 7 | }, 8 | "dependencies": { 9 | "@emotion/react": "^11.8.2", 10 | "@sanity/client": "^3.2.0", 11 | "ethers": "^5.6.1", 12 | "next": "^12.1.0", 13 | "react": "^17.0.2", 14 | "react-dom": "^17.0.2", 15 | "react-icons": "^4.3.1", 16 | "react-modal": "^3.14.4", 17 | "react-spinners": "^0.11.0" 18 | }, 19 | "devDependencies": { 20 | "@types/node": "17.0.4", 21 | "@types/react": "17.0.38", 22 | "autoprefixer": "^10.4.0", 23 | "postcss": "^8.4.5", 24 | "prettier": "^2.5.1", 25 | "prettier-plugin-tailwindcss": "^0.1.1", 26 | "tailwindcss": "^3.0.23", 27 | "typescript": "4.5.4" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /components/TransactionHistory.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react' 2 | import { client } from '../lib/sanityClient' 3 | import { useContext } from 'react' 4 | import { TransactionContext } from '../context/TransactionContext' 5 | import Image from 'next/image' 6 | import ethLogo from '../assets/ethCurrency.png' 7 | import { FiArrowUpRight } from 'react-icons/fi' 8 | 9 | const style = { 10 | wrapper: `h-full text-white select-none h-full w-screen flex-1 pt-14 flex items-end justify-end pb-12 overflow-scroll px-8`, 11 | txHistoryItem: `bg-[#191a1e] rounded-lg px-4 py-2 my-2 flex items-center justify-end`, 12 | txDetails: `flex items-center`, 13 | toAddress: `text-[#f48706] mx-2`, 14 | txTimestamp: `mx-2`, 15 | etherscanLink: `flex items-center text-[#2172e5]`, 16 | } 17 | 18 | const TransactionHistory = () => { 19 | const { isLoading, currentAccount } = useContext(TransactionContext) 20 | const [transactionHistory, setTransactionHistory] = useState([]) 21 | 22 | useEffect(() => { 23 | ;(async () => { 24 | if (!isLoading && currentAccount) { 25 | const query = ` 26 | *[_type=="users" && _id == "${currentAccount}"] { 27 | "transactionList": transactions[]->{amount, toAddress, timestamp, txHash}|order(timestamp desc)[0..4] 28 | } 29 | ` 30 | 31 | const clientRes = await client.fetch(query) 32 | 33 | setTransactionHistory(clientRes[0].transactionList) 34 | } 35 | })() 36 | }, [isLoading, currentAccount]) 37 | 38 | return ( 39 |
40 |
41 | {transactionHistory && 42 | transactionHistory?.map((transaction, index) => ( 43 |
44 |
45 | eth 46 | {transaction.amount} Ξ sent to{' '} 47 | 48 | {transaction.toAddress.substring(0, 6)}... 49 | 50 |
{' '} 51 | on{' '} 52 |
53 | {new Date(transaction.timestamp).toLocaleString('en-US', { 54 | timeZone: 'PST', 55 | hour12: true, 56 | timeStyle: 'short', 57 | dateStyle: 'long', 58 | })} 59 |
60 | 71 |
72 | ))} 73 |
74 |
75 | ) 76 | } 77 | 78 | export default TransactionHistory -------------------------------------------------------------------------------- /components/Main.js: -------------------------------------------------------------------------------- 1 | import Image from 'next/image' 2 | import { RiSettings3Fill } from 'react-icons/ri' 3 | import { AiOutlineDown } from 'react-icons/ai' 4 | import ethLogo from '../assets/eth.png' 5 | import { useContext } from 'react' 6 | import { TransactionContext } from '../context/TransactionContext' 7 | import Modal from 'react-modal' 8 | import { useRouter } from 'next/router' 9 | import TransactionLoader from './TransactionLoader' 10 | 11 | Modal.setAppElement('#__next') 12 | 13 | const style = { 14 | wrapper: `w-screen flex items-center justify-center mt-14`, 15 | content: `bg-[rgba(25, 27, 31, 0.8)] w-[40rem] rounded-2xl p-4 backdrop-filter backdrop-blur-md`, 16 | formHeader: `px-2 flex items-center justify-between font-semibold text-xl`, 17 | transferPropContainer: 'bg-[rgba(209, 209, 209, 0.8)] my-3 rounded-2xl p-6 text-3xl border hover:border-[#d1d1d1] flex justify-between shadow-md transition-all duration-300', 18 | transferPropInput: `bg-transparent placeholder:text-[#d1d1d1] text-black outline-none mb-6 w-full text-2xl`, 19 | currencySelector: `flex w-1/4`, 20 | currencySelectorContent: `w-full h-min flex justify-between items-center bg-[#2D2F36] hover:bg-[#41444F] rounded-2xl text-xl font-medium cursor-pointer p-2 mt-[-0.2rem]`, 21 | currencySelectorIcon: `flex items-center`, 22 | currencySelectorTicker: `mx-2`, 23 | currencySelectorArrow: `text-lg`, 24 | confirmButton: `bg-black my-2 rounded-2xl py-6 px-8 text-xl font-semibold flex items-center justify-center cursor-pointer hover:border-[#234169]`, 25 | } 26 | 27 | const customStyles = { 28 | content: { 29 | top: '50%', 30 | left: '50%', 31 | right: 'auto', 32 | bottom: 'auto', 33 | transform: 'translate(-50%, -50%)', 34 | backgroundColor: '#ffffff', 35 | padding: 0, 36 | border: 'none', 37 | }, 38 | overlay: { 39 | backgroundColor: '#ffffff', 40 | }, 41 | } 42 | 43 | const Main = () => { 44 | const { formData, handleChange, sendTransaction } = 45 | useContext(TransactionContext) 46 | const router = useRouter() 47 | 48 | const handleSubmit = async (e) => { 49 | const { addressTo, amount } = formData 50 | e.preventDefault() 51 | 52 | if (!addressTo || !amount) return 53 | 54 | sendTransaction() 55 | } 56 | 57 | return ( 58 |
59 |
60 |
61 |
Swap
62 |
63 | 64 |
65 |
66 |
67 | handleChange(e, 'amount')} 73 | /> 74 |
75 |
76 |
77 | eth logo 78 |
79 |
ETH
80 | 81 |
82 |
83 |
84 |
85 | handleChange(e, 'addressTo')} 90 | /> 91 |
92 |
93 |
handleSubmit(e)} className={style.confirmButton}> 94 | Confirm 95 |
96 |
97 | 98 | 99 | 100 | 101 |
102 | ) 103 | } 104 | 105 | export default Main -------------------------------------------------------------------------------- /components/Header.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useContext } from 'react' 2 | import Image from 'next/image' 3 | import { FiArrowUpRight } from 'react-icons/fi' 4 | import { AiOutlineDown } from 'react-icons/ai' 5 | import ethLogo from '../assets/eth.png' 6 | import dexterityLogo from '../assets/dexterity.png' 7 | import { TransactionContext } from '../context/TransactionContext' 8 | 9 | const style = { 10 | wrapper: `p-4 w-screen flex justify-between items-center`, 11 | headerLogo: `flex w-1/4 items-center justify-start`, 12 | nav: `flex-1 flex justify-center items-center`, 13 | navItemsContainer: `flex bg-[#191B1F] rounded-3xl`, 14 | navItem: `px-4 py-2 m-1 flex items-center text-lg font-semibold text-[0.9rem] cursor-pointer rounded-3xl`, 15 | activeNavItem: `bg-[#20242A] 16 | background: '-webkit-linear-gradient(180deg, #F687B3, #A78BFA, #6B46C1)', 17 | '-webkit-background-clip': 'text', 18 | '-webkit-text-fill-color': 'transparent' 19 | `, 20 | buttonsContainer: `flex w-1/4 justify-end items-center`, 21 | button: `flex items-center bg-[#191B1F] rounded-2xl mx-2 text-[0.9rem] font-semibold cursor-pointer`, 22 | buttonPadding: `p-2`, 23 | buttonTextContainer: `h-8 flex items-center`, 24 | buttonIconContainer: `flex items-center justify-center w-8 h-8`, 25 | buttonAccent: `bg-[#000000] border border-[#163256] hover:border-[#234169] h-full rounded-2xl flex items-center justify-center text-[#4F90EA]`, 26 | } 27 | 28 | const Header = () => { 29 | const [selectedNav, setSelectedNav] = useState('swap') 30 | const {connectWallet, currentAccount} = useContext(TransactionContext) 31 | const [userName, setUserName] = useState() 32 | 33 | useEffect(() => { 34 | if (!currentAccount) return 35 | setUserName(`${currentAccount.slice(0, 7)}...${currentAccount.slice(35)}`,) 36 | }, [currentAccount]) 37 | 38 | return ( 39 |
40 |
41 |
42 | dexterity 43 |

Dexterity

46 |
47 |
48 |
49 | 50 |
51 |
52 |
53 |
setSelectedNav('swap')} 55 | className={`${style.navItem} ${ 56 | selectedNav === 'swap' && style.activeNavItem 57 | }`} 58 | > 59 | Swap 60 |
61 |
setSelectedNav('pool')} 63 | className={`${style.navItem} ${ 64 | selectedNav === 'pool' && style.activeNavItem 65 | }`} 66 | > 67 | Pool (coming soon) 68 |
69 | 74 |
75 | Charts 76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 | eth logo 84 |
85 |

Ethereum

86 |
87 | 88 |
89 |
90 | 91 | {currentAccount ? ( 92 |
93 |
{userName}
94 |
95 | ) : ( 96 |
connectWallet()} 98 | className={`${style.button} `} 99 | > 100 |
107 | Connect Wallet 108 |
109 |
110 | )} 111 | 112 |
113 |
114 | ) 115 | } 116 | 117 | export default Header 118 | -------------------------------------------------------------------------------- /context/TransactionContext.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import { contractABI, contractAddress } from '../lib/constants' 3 | import { ethers } from 'ethers' 4 | import { client } from '../lib/sanityClient' 5 | import { useRouter } from 'next/router' 6 | 7 | export const TransactionContext = React.createContext() 8 | 9 | let eth 10 | 11 | if (typeof window !== 'undefined') { 12 | eth = window.ethereum 13 | } 14 | 15 | const getEthereumContract = () => { 16 | const provider = new ethers.providers.Web3Provider(ethereum) 17 | const signer = provider.getSigner() 18 | const transactionContract = new ethers.Contract( 19 | contractAddress, 20 | contractABI, 21 | signer, 22 | ) 23 | 24 | return transactionContract 25 | } 26 | 27 | export const TransactionProvider = ({ children }) => { 28 | const [currentAccount, setCurrentAccount] = useState() 29 | const [isLoading, setIsLoading] = useState(false) 30 | const router = useRouter() 31 | const [formData, setFormData] = useState({ 32 | addressTo: '', 33 | amount: '', 34 | }) 35 | 36 | useEffect(() => { 37 | if (isLoading) { 38 | router.push(`/?loading=${currentAccount}`) 39 | } else { 40 | router.push(`/`) 41 | } 42 | }, [isLoading]) 43 | 44 | useEffect(() => { 45 | if (!currentAccount) return 46 | ;(async () => { 47 | const userDoc = { 48 | _type: 'users', 49 | _id: currentAccount, 50 | userName: 'Unnamed', 51 | address: currentAccount, 52 | } 53 | 54 | await client.createIfNotExists(userDoc) 55 | })() 56 | }, [currentAccount]) 57 | 58 | const handleChange = (e, name) => { 59 | setFormData(prevState => ({ ...prevState, [name]: e.target.value })) 60 | } 61 | 62 | const checkIfWalletIsConnected = async (metamask = eth) => { 63 | try { 64 | if (!metamask) return alert('Please install metamask ') 65 | 66 | const accounts = await metamask.request({ method: 'eth_accounts' }) 67 | 68 | if (accounts.length) { 69 | setCurrentAccount(accounts[0]) 70 | } 71 | } catch (error) { 72 | console.error(error) 73 | throw new Error('No ethereum object.') 74 | } 75 | } 76 | 77 | const connectWallet = async (metamask = eth) => { 78 | try { 79 | if (!metamask) return alert('Please install metamask ') 80 | 81 | const accounts = await metamask.request({ method: 'eth_requestAccounts' }) 82 | 83 | setCurrentAccount(accounts[0]) 84 | } catch (error) { 85 | console.error(error) 86 | throw new Error('No ethereum object.') 87 | } 88 | } 89 | 90 | const saveTransaction = async ( 91 | txHash, 92 | amount, 93 | fromAddress = currentAccount, 94 | toAddress, 95 | ) => { 96 | const txDoc = { 97 | _type: 'transactions', 98 | _id: txHash, 99 | fromAddress: fromAddress, 100 | toAddress: toAddress, 101 | timestamp: new Date(Date.now()).toISOString(), 102 | txHash: txHash, 103 | amount: parseFloat(amount), 104 | } 105 | 106 | await client.createIfNotExists(txDoc) 107 | 108 | await client 109 | .patch(currentAccount) 110 | .setIfMissing({ transactions: [] }) 111 | .insert('after', 'transactions[-1]', [ 112 | { 113 | _key: txHash, 114 | _ref: txHash, 115 | _type: 'reference', 116 | }, 117 | ]) 118 | .commit() 119 | 120 | return 121 | } 122 | 123 | const sendTransaction = async ( 124 | metamask = eth, 125 | connectedAccount = currentAccount, 126 | ) => { 127 | try { 128 | if (!metamask) return alert('Please install metamask ') 129 | const { addressTo, amount } = formData 130 | const transactionContract = getEthereumContract() 131 | 132 | const parsedAmount = ethers.utils.parseEther(amount) 133 | 134 | await metamask.request({ 135 | method: 'eth_sendTransaction', 136 | params: [ 137 | { 138 | from: connectedAccount, 139 | to: addressTo, 140 | gas: '0x7EF40', // 520000 Gwei 141 | value: parsedAmount._hex, 142 | }, 143 | ], 144 | }) 145 | 146 | const transactionHash = await transactionContract.publishTransaction( 147 | addressTo, 148 | parsedAmount, 149 | `Transferring ETH ${parsedAmount} to ${addressTo}`, 150 | 'TRANSFER', 151 | ) 152 | 153 | setIsLoading(true) 154 | 155 | await transactionHash.wait() 156 | 157 | await saveTransaction( 158 | transactionHash.hash, 159 | amount, 160 | connectedAccount, 161 | addressTo, 162 | ) 163 | 164 | setIsLoading(false) 165 | } catch (error) { 166 | console.log(error) 167 | } 168 | } 169 | 170 | useEffect(() => { 171 | checkIfWalletIsConnected() 172 | }, []) 173 | 174 | return ( 175 | 186 | {children} 187 | 188 | ) 189 | } -------------------------------------------------------------------------------- /lib/Transactions.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-artifact-1", 3 | "contractName": "Transactions", 4 | "sourceName": "contracts/Transactions.sol", 5 | "abi": [ 6 | { 7 | "anonymous": false, 8 | "inputs": [ 9 | { 10 | "indexed": false, 11 | "internalType": "address", 12 | "name": "sender", 13 | "type": "address" 14 | }, 15 | { 16 | "indexed": false, 17 | "internalType": "address", 18 | "name": "receiver", 19 | "type": "address" 20 | }, 21 | { 22 | "indexed": false, 23 | "internalType": "uint256", 24 | "name": "amount", 25 | "type": "uint256" 26 | }, 27 | { 28 | "indexed": false, 29 | "internalType": "string", 30 | "name": "message", 31 | "type": "string" 32 | }, 33 | { 34 | "indexed": false, 35 | "internalType": "uint256", 36 | "name": "timestamp", 37 | "type": "uint256" 38 | }, 39 | { 40 | "indexed": false, 41 | "internalType": "string", 42 | "name": "keyword", 43 | "type": "string" 44 | } 45 | ], 46 | "name": "Transfer", 47 | "type": "event" 48 | }, 49 | { 50 | "inputs": [ 51 | { 52 | "internalType": "address payable", 53 | "name": "receiver", 54 | "type": "address" 55 | }, 56 | { 57 | "internalType": "uint256", 58 | "name": "amount", 59 | "type": "uint256" 60 | }, 61 | { 62 | "internalType": "string", 63 | "name": "message", 64 | "type": "string" 65 | }, 66 | { 67 | "internalType": "string", 68 | "name": "keyword", 69 | "type": "string" 70 | } 71 | ], 72 | "name": "publishTransaction", 73 | "outputs": [], 74 | "stateMutability": "nonpayable", 75 | "type": "function" 76 | } 77 | ], 78 | "bytecode": "0x608060405234801561001057600080fd5b5061049a806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80637d0ee35714610030575b600080fd5b61004a60048036038101906100459190610125565b61004c565b005b7f416cfa4330a4565f45c2fd2dd4826a83a37443aba2ce6f79477c7355afac35fa3385858542866040516100859695949392919061021e565b60405180910390a150505050565b60006100a66100a1846102b2565b61028d565b9050828152602081018484840111156100be57600080fd5b6100c9848285610383565b509392505050565b6000813590506100e081610436565b92915050565b600082601f8301126100f757600080fd5b8135610107848260208601610093565b91505092915050565b60008135905061011f8161044d565b92915050565b6000806000806080858703121561013b57600080fd5b6000610149878288016100d1565b945050602061015a87828801610110565b935050604085013567ffffffffffffffff81111561017757600080fd5b610183878288016100e6565b925050606085013567ffffffffffffffff8111156101a057600080fd5b6101ac878288016100e6565b91505092959194509250565b6101c18161034d565b82525050565b6101d0816102ff565b82525050565b60006101e1826102e3565b6101eb81856102ee565b93506101fb818560208601610392565b61020481610425565b840191505092915050565b61021881610343565b82525050565b600060c08201905061023360008301896101c7565b61024060208301886101b8565b61024d604083018761020f565b818103606083015261025f81866101d6565b905061026e608083018561020f565b81810360a083015261028081846101d6565b9050979650505050505050565b60006102976102a8565b90506102a382826103c5565b919050565b6000604051905090565b600067ffffffffffffffff8211156102cd576102cc6103f6565b5b6102d682610425565b9050602081019050919050565b600081519050919050565b600082825260208201905092915050565b600061030a82610323565b9050919050565b600061031c82610323565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006103588261035f565b9050919050565b600061036a82610371565b9050919050565b600061037c82610323565b9050919050565b82818337600083830152505050565b60005b838110156103b0578082015181840152602081019050610395565b838111156103bf576000848401525b50505050565b6103ce82610425565b810181811067ffffffffffffffff821117156103ed576103ec6103f6565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000601f19601f8301169050919050565b61043f81610311565b811461044a57600080fd5b50565b61045681610343565b811461046157600080fd5b5056fea2646970667358221220262f1bdbd23a69610b873261bf29e10ff8042b7bbccc0694b3068bfe8eed76e064736f6c63430008040033", 79 | "deployedBytecode": "0x608060405234801561001057600080fd5b506004361061002b5760003560e01c80637d0ee35714610030575b600080fd5b61004a60048036038101906100459190610125565b61004c565b005b7f416cfa4330a4565f45c2fd2dd4826a83a37443aba2ce6f79477c7355afac35fa3385858542866040516100859695949392919061021e565b60405180910390a150505050565b60006100a66100a1846102b2565b61028d565b9050828152602081018484840111156100be57600080fd5b6100c9848285610383565b509392505050565b6000813590506100e081610436565b92915050565b600082601f8301126100f757600080fd5b8135610107848260208601610093565b91505092915050565b60008135905061011f8161044d565b92915050565b6000806000806080858703121561013b57600080fd5b6000610149878288016100d1565b945050602061015a87828801610110565b935050604085013567ffffffffffffffff81111561017757600080fd5b610183878288016100e6565b925050606085013567ffffffffffffffff8111156101a057600080fd5b6101ac878288016100e6565b91505092959194509250565b6101c18161034d565b82525050565b6101d0816102ff565b82525050565b60006101e1826102e3565b6101eb81856102ee565b93506101fb818560208601610392565b61020481610425565b840191505092915050565b61021881610343565b82525050565b600060c08201905061023360008301896101c7565b61024060208301886101b8565b61024d604083018761020f565b818103606083015261025f81866101d6565b905061026e608083018561020f565b81810360a083015261028081846101d6565b9050979650505050505050565b60006102976102a8565b90506102a382826103c5565b919050565b6000604051905090565b600067ffffffffffffffff8211156102cd576102cc6103f6565b5b6102d682610425565b9050602081019050919050565b600081519050919050565b600082825260208201905092915050565b600061030a82610323565b9050919050565b600061031c82610323565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006103588261035f565b9050919050565b600061036a82610371565b9050919050565b600061037c82610323565b9050919050565b82818337600083830152505050565b60005b838110156103b0578082015181840152602081019050610395565b838111156103bf576000848401525b50505050565b6103ce82610425565b810181811067ffffffffffffffff821117156103ed576103ec6103f6565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000601f19601f8301169050919050565b61043f81610311565b811461044a57600080fd5b50565b61045681610343565b811461046157600080fd5b5056fea2646970667358221220262f1bdbd23a69610b873261bf29e10ff8042b7bbccc0694b3068bfe8eed76e064736f6c63430008040033", 80 | "linkReferences": {}, 81 | "deployedLinkReferences": {} 82 | } 83 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | Logo 3 |

4 | Dexterity 5 |

6 |

7 | Decentralized Cryptocurrency Exchange with MetaMask Authentication ⛓️🌎🦊 8 |
9 |

10 | 11 | [![Website][website]][website-url] 12 | [![Github][github]][github-url] 13 | 14 |
15 | 16 |
17 | 18 | ## Table of Contents 19 | 20 |
    21 | 📝 About 22 |
      23 |
    24 | 💻 How to build
    25 | 🚀 Next steps
    26 | 🔧 Tools used 27 |
      28 |
    29 | 👤 Contact 30 |
31 | 32 |
33 | 34 | ## 📝About 35 | 36 | Building towards a Decentralized Exchange (DEX) Web 3.0 dApp: 37 | 38 | - Solidity, Next.js, and Sanity.io. 39 | - Deployed to Ethereum mainnet via Alchemy endpoint 40 | - Integrated a full MetaMask wallet authentication, providing secure access to the platform 🔥 41 | 42 | Useful tool to view live crypto prices as well as trade/swap between currencies without going through a centralized platform (like Coinbase, Robinhood, or everyone's favorite-- FTX). 43 | 44 | This was a learning project so it's broken in some areas. It's also not meant to be a revolutionary work of art, go to [Uniswap](https://uniswap.org/) for that. It was merely practice for me as a developer to get my hands dirty with Web3 Javascript packages when I first began learning Solidity from YouTube. It was also a big learning gap initially for me as I was not familiar with authentication from the server-side on the browser, which was made even more complicated due to the nature of cryptographic hashes and private/public keys. 45 | 46 |
47 | 48 | ## 💻How to Build 49 | 50 | 1. **Smart Contract Development via Solidity, Hardhat:** 51 | 52 | - Developed Solidity smart contracts in `smart_contract/` to power the DEX 53 | - Main contract, `Transactions.sol`, handles transaction execution. 54 | - Used Solidity v0.8.4 for up-to-date standards. 55 | - Hardhat- used to streamline contract deployment and testing. 56 | - Automated contract deployment via `deploy.js`. 57 | 58 | 2. **Ethereum Interaction via Web3.js, Ether.js:** 59 | 60 | - Web3.js and Ether.js in `client/` for Ethereum interaction. 61 | - Web3.js connects app to Ethereum node for data reading and transactions. 62 | - Ether.js simplifies contract interaction and data querying. 63 | - `lib/Transactions.json` communicates with deployed contract. 64 | 65 | 3. **🔥 Wallet Authentication 🔥 via MetaMask:** 66 | 67 | - Integrated MetaMask in `client/` for secure user authentication. 68 | - Users access the platform with an Ethereum wallet 69 | - MetaMask does its magic to enables smooth and secure transactions. 70 | - Hardest part of project, very satisfying 71 | 72 | 4. **Content Management System (CMS) via Sanity.io:** 73 | 74 | - `studio/`- CMS via Sanity.io 75 | 76 | 5. **ETH Mainnet Interaction via Alchemy:** 77 | 78 | - Deployed "to the blockchain" i.e. the Ethereum mainnet (via Alchemy). 79 | - `Ethereum-Mainnet` badge in README signifies the connection. 80 | 81 | 6. **Styling and UI:** 82 | - Next.js frontend + Tailwind CSS for rapid UI development. 83 | 84 |

85 | 86 | ## 🚀Next Steps 87 | 88 | ### Contributions and Open-Source License 89 | 90 | - Open-source project for learning and exploration. 91 | - Encouraging community contributions! Considering social tokens backed by proof-of-work mechanism 92 | 93 | ### Cash App Integration? 94 | 95 | - Cash App integration to speed up fiat to crypto onboarding 96 | - Cash App already boasts large userbase of fiat defectors as many of those users lost trust / never gained the trust of the Big Banks, whch also repeteadly fail to adapt to mobile and web technologies 97 | - CEO Jack Dorsey has been pushing the boundaries of the fiat regulation box for years now, mainly through its grey status as a bank / fintech app / securities territory via its Bitcoin wallet integration and cashback-automatic-invest feature which allows cashback users receive to automically route to their BTC wallet. 98 | 99 | ### Proof-of-Work? 100 | 101 | - Adding social tokens to user wallets as work is done on the Dexterity network, allowing open source work to be incentivized and leads to a better platform for everyone 102 | - Somewhere along these winding routes a DAO will spawn, still learning about that 103 | 104 | ## 🔧Tools Used 105 | 106 | ### Smart Contract Development 107 | 108 | [![Solidity][solidity]][solidity-url] 109 | [![Hardhat][hardhat]][hardhat-url] 110 | 111 | ### Ethereum Interaction 112 | 113 | [![Web3.js][web3.js]][web3.js-url] 114 | [![Ether.js][ether.js]][ether.js-url] 115 | [![Ethereum-Mainnet][ethereum-mainnet]][ethereum-mainnet-url] 116 | 117 | ### Wallet Authentication 118 | 119 | [![Metamask][metamask]][metamask-url] 120 | 121 | ### Blockchain Data Provider 122 | 123 | [![Alchemy][alchemy]][alchemy-url] 124 | 125 | ### Content Management System (CMS) 126 | 127 | [![Sanity.io][sanity.io]][sanity.io-url] 128 | 129 | ### Frontend and UI 130 | 131 | [![Next][next]][next-url] 132 | [![TailwindCSS][tailwindcss]][tailwindcss-url] 133 | [![Vercel][vercel]][vercel-url] 134 | 135 | ## 👤Contact 136 | 137 | [![Email][email]][email-url] 138 | [![Twitter][twitter]][twitter-url] 139 | 140 | 141 | 142 | 143 | [alchemy]: https://img.shields.io/badge/Alchemy-0072FF?style=for-the-badge&logo=alchemy&logoColor=white&color=0072FF 144 | [alchemy-url]: https://alchemyapi.io/ 145 | [solidity]: https://img.shields.io/badge/Solidity-FFCA28?style=for-the-badge&logo=solidity&logoColor=white&color=2a2477 146 | [solidity-url]: https://soliditylang.org/ 147 | [hardhat]: https://img.shields.io/badge/Hardhat-FFCA28?style=for-the-badge&logo=hardhat&logoColor=black&color=F0FF00 148 | [hardhat-url]: https://hardhat.org/ 149 | [web3.js]: https://img.shields.io/badge/Web3.js-FFCA28?style=for-the-badge&logo=ethereum&logoColor=white&color=437eb4 150 | [web3.js-url]: https://web3js.readthedocs.io/en/v1.7.3/ 151 | [ether.js]: https://img.shields.io/badge/Ether.js-FFCA28?style=for-the-badge&logo=ethereum&logoColor=white&color=28359a 152 | [ether.js-url]: https://docs.ethers.org/v5/ 153 | [metamask]: https://img.shields.io/badge/🦊_Metamask-000000?style=for-the-badge&logo=metamask&logoColor=white&color=e68a3c 154 | [metamask-url]: https://metamask.io/ 155 | [sanity.io]: https://img.shields.io/badge/Sanity.io-E26D5F?style=for-the-badge&logo=sanitydotio&logoColor=white&color=E26D5F&labelColor=000000 156 | [sanity.io-url]: https://www.sanity.io/ 157 | [ethereum-mainnet]: https://img.shields.io/badge/Ethereum_Mainnet-3C3C3D?style=for-the-badge&logo=ethereum&logoColor=white&color=3C3C3D 158 | [ethereum-mainnet-url]: https://ethereum.org/en/ 159 | [next]: https://img.shields.io/badge/next.js-000000?style=for-the-badge&logo=nextdotjs&logoColor=white 160 | [next-url]: https://nextjs.org/ 161 | [tailwindcss]: https://img.shields.io/badge/Tailwind_CSS-38B2AC?style=for-the-badge&logo=tailwind-css&logoColor=skyblue&color=0A192F 162 | [tailwindcss-url]: https://tailwindcss.com/ 163 | [vercel]: https://img.shields.io/badge/Vercel-FFFFFF?style=for-the-badge&logo=Vercel&logoColor=white&color=black 164 | [vercel-url]: https://Vercel.com/ 165 | [website]: https://img.shields.io/badge/🔗Website-7f18ff?style=for-the-badge 166 | [website-url]: https://d3xterity.vercel.app/ 167 | [github]: https://img.shields.io/badge/💻Github-000000?style=for-the-badge 168 | [github-url]: https://github.com/vdutts7/cs186-ai-chat 169 | [email]: https://img.shields.io/badge/me@vdutts7.com-FFCA28?style=for-the-badge&logo=Gmail&logoColor=00bbff&color=black 170 | [email-url]: # 171 | [twitter]: https://img.shields.io/badge/Twitter-FFCA28?style=for-the-badge&logo=Twitter&logoColor=00bbff&color=black 172 | [twitter-url]: https://twitter.com/vdutts7/ 173 | -------------------------------------------------------------------------------- /public/bg-dex.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | --------------------------------------------------------------------------------