zhereh-frontend

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit 324ac2cddc9f47552560eae172cf1ff7c17b1379
parent 4c0ddde83d652c0fa578976c0292b5a66c3b894b
Author: William Muli <willi.wambu@gmail.com>
Date:   Sun, 18 Jun 2023 12:41:35 +0300

Added balance and token vote details

Diffstat:
Msrc/client.ts | 10+++++-----
Msrc/components/Nav.svelte | 100++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Msrc/routes/+layout.svelte | 118++++++++++++++++++++++++++++---------------------------------------------------
Msrc/shared/types.ts | 9++++++++-
Msrc/store.ts | 19++++++++++++++++---
Asrc/utils/amout.ts | 0
6 files changed, 159 insertions(+), 97 deletions(-)

diff --git a/src/client.ts b/src/client.ts @@ -1,12 +1,12 @@ import { createPublicClient, createWalletClient, custom, http } from 'viem' -import { localhost } from '@wagmi/chains' +import type { Chain } from '@wagmi/chains' -export const walletClient = () => createWalletClient({ - chain: localhost, +export const walletClient = (chain: Chain) => createWalletClient({ + chain, transport: custom(window.ethereum) }) -export const publicClient = () => createPublicClient({ - chain: localhost, +export const publicClient = (chain: Chain) => createPublicClient({ + chain , transport: http() }) \ No newline at end of file diff --git a/src/components/Nav.svelte b/src/components/Nav.svelte @@ -1,16 +1,58 @@ <script lang="ts"> import { onMount } from "svelte" - import { connectionDetails, configuredChain } from "../store"; - import { walletClient } from "../client" + import { PUBLIC_ERC20_CONTRACT_ADDRESS } from "$env/static/public" + import { connectionDetails, configuredChain, voteToken, votingConfig } from "../store"; + import { publicClient, walletClient } from "../client" import { NavOptions } from "../options/nav-options"; import { reduceAddress } from "../utils/reduce-address" - import type { RequestAddressesReturnType } from "viem" - + import { formatEther, formatUnits, getContract, type RequestAddressesReturnType } from "viem" + import { erc20TokenAbi } from "../shared/erc20-token-abi" + let networkModalOpen: boolean = false + let tokenModalOpen: boolean = false + let balance: bigint + + $: ({ name, symbol, decimals, balance: erc20Balance } = $voteToken) + $: ({ isConnected, userAddress } = $connectionDetails) + $: ({ contractAddress, erc20ContractAddress } = $votingConfig) + + $: { + if($configuredChain && userAddress) { + publicClient($configuredChain) + .getBalance({ + address: userAddress + }) + .then(bal => balance = bal) + .catch(err => console.error(err)) + + const contract = getContract({ + address: PUBLIC_ERC20_CONTRACT_ADDRESS as `0x${string}`, + publicClient: publicClient($configuredChain), + walletClient: walletClient($configuredChain), + abi: erc20TokenAbi + }) + + contract.read.balanceOf([userAddress] as [`0x${string}`]) + .then(balance => voteToken.update(current => ({...current, balance }))) + .catch(err => console.error(err)) + + contract.read.name() + .then(name => voteToken.update(current => ({...current, name }))) + .catch(err => console.error(err)) + + contract.read.symbol() + .then(symbol => voteToken.update(current => ({...current, symbol }))) + .catch(err => console.error(err)) + + contract.read.decimals() + .then(decimals => voteToken.update(current => ({...current, decimals }))) + .catch(err => console.error(err)) + } + } const connectWallet = async () => { if (window.ethereum) { - const accounts = await walletClient().requestAddresses() + const accounts = await walletClient($configuredChain).requestAddresses() if (accounts.length > 0) { updateConnectionDetails(true, accounts) @@ -24,7 +66,7 @@ onMount(async () => { if (window.ethereum) { - const accounts = await walletClient().getAddresses() + const accounts = await walletClient($configuredChain).getAddresses() if (accounts.length > 0) { updateConnectionDetails(true, accounts) @@ -73,24 +115,36 @@ {/each} </div> <div> - <button class="btn btn-primary btn-md normal-case text-white" on:click={() => networkModalOpen = true}>Network Details</button> + <button class="btn btn-secondary text-white normal-case" on:click={() => tokenModalOpen = true}> + {name} Bal: {erc20Balance && decimals && `${formatUnits(erc20Balance, decimals)} ${symbol}`} + <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6"> + <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5" /> + </svg> + </button> + <button + class="btn btn-primary btn-md normal-case text-white" + on:click={() => networkModalOpen = true}> + Network Details + <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6"> + <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5" /> + </svg> + </button> <button class="btn btn-md btn-primary text-white" on:click={connectWallet} > - {$connectionDetails.isConnected ? reduceAddress($connectionDetails.userAddress) : 'Connect wallet'} + {isConnected && userAddress ? reduceAddress(userAddress) : 'Connect wallet'} </button > </div> </div> - <!-- You can open the modal using ID.showModal() method --> <dialog id="networkDetails" class="modal" open={networkModalOpen}> <div class="modal-box "> <button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2" on:click={() => networkModalOpen = false}>✕</button> <h3 class="font-semibold text-lg">Network Details</h3> <div class="divider my-1"></div> - <div class="flex flex-row items-start gap-5 "> + <div class="flex flex-row items-start gap-5"> <div class=""> <p class="py-2 font-normal">Network Name</p> <p class="py-2 font-normal">RPC Endpoint</p> @@ -107,10 +161,34 @@ <p class="py-2 font-light">{$configuredChain.nativeCurrency.name}</p> <p class="py-2 font-light">{$configuredChain.nativeCurrency.symbol}</p> <p class="py-2 font-light">{$configuredChain.nativeCurrency.decimals}</p> - </div> </div> </div> </dialog> + + <dialog id="networkDetails" class="modal" open={tokenModalOpen}> + <div class="modal-box w-11/12"> + <button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2" on:click={() => tokenModalOpen = false}>✕</button> + <h3 class="font-semibold text-lg">Token Vote Details</h3> + <div class="divider my-1"></div> + <ul> + <li class="font-normal"> + Current Network: <span class="font-light">{$configuredChain.name}</span> + </li> + <li class="font-normal"> + Your current balance: <span class="font-light">{balance ? formatEther(balance) : ''} {$configuredChain.nativeCurrency.symbol}</span> + </li> + <li class="font-normal">Vote Contract Address: <span class="font-light">{contractAddress}</span></li> + <li class="font-normal"> + ERC20 Contract Address <span class="font-light">{erc20ContractAddress}</span> + </li> + <li class="font-normal">{name} Balance: + <span class="font-light"> + {erc20Balance && decimals && `${formatUnits(erc20Balance, decimals)} ${symbol}`} + </span> + </li> + </ul> + </div> + </dialog> </nav> diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte @@ -1,73 +1,47 @@ <script lang="ts"> - import { onMount } from 'svelte' - import { formatEther, getContract, parseEther, type RequestAddressesReturnType, stringToHex } from 'viem' + import { getContract, parseEther, stringToHex } from 'viem' import { walletClient, publicClient } from '../client' import Nav from '../components/Nav.svelte' import '../app.css' import { PUBLIC_ERC20_CONTRACT_ADDRESS, PUBLIC_VOTE_CONTRACT_ADDRESS } from '$env/static/public' - import { erc20TokenAbi, gitableAbi } from '../shared/erc20-token-abi' + import { erc20TokenAbi } from '../shared/erc20-token-abi' import { voteContractAbi } from '../shared/token-vote-abi' - import { configuredChain, votingConfig } from '../store' - - let userAddress: `0x${string}` | undefined - let balance: bigint | undefined - let isConnected: boolean = false - // $configuredChain - - // console.log($configuredChain) - configuredChain.subscribe(value => console.log(value)) - votingConfig.subscribe(value => console.log(value)) - - const setup = async (accounts: RequestAddressesReturnType) => { - userAddress = accounts[0] - try { - balance = await publicClient().getBalance({ - address: userAddress - }) - isConnected = true - - - } catch (error) { - console.error(error) - } - } - - const connectWallet = async () => { - if (window.ethereum) { - const accounts = await walletClient().requestAddresses() - - if (accounts.length > 0) { - await setup(accounts) - } - } - } + import { configuredChain, connectionDetails, voteToken } from '../store' const sendTransaction = async () => { - if (!userAddress || !window.ethereum) return - await walletClient().sendTransaction({ - account: userAddress, + if (!$connectionDetails.userAddress || !window.ethereum) return + await walletClient($configuredChain).sendTransaction({ + account: $connectionDetails.userAddress, to: '0x0000000000000000000000000000000000000000', value: parseEther('0.000001') }) } - const checkBalance = async () => { - if (!userAddress) return - - const ethBalance = await publicClient().getBalance({ - address: userAddress as `0x${string}` - }) - - console.log({ ethBalance }) - - const contract = getContract({ - address: PUBLIC_ERC20_CONTRACT_ADDRESS as `0x${string}`, - publicClient: publicClient(), - walletClient: walletClient(), - abi: erc20TokenAbi - }) - const result = await contract.read.balanceOf([userAddress] as [`0x${string}`]) - console.log(result) + $: { + if ($connectionDetails.userAddress && configuredChain) { + const contract = getContract({ + address: PUBLIC_ERC20_CONTRACT_ADDRESS as `0x${string}`, + publicClient: publicClient($configuredChain), + walletClient: walletClient($configuredChain), + abi: erc20TokenAbi + }) + + contract.read.balanceOf([$connectionDetails.userAddress] as [`0x${string}`]) + .then(balance => voteToken.update(current => ({...current, balance }))) + .catch(err => console.log(err)) + + contract.read.name() + .then(name => voteToken.update(current => ({...current, name }))) + .catch(err => console.error(err)) + + contract.read.symbol() + .then(symbol => voteToken.update(current => ({...current, symbol }))) + .catch(err => console.error(err)) + + contract.read.decimals() + .then(decimals => voteToken.update(current => ({...current, decimals }))) + .catch(err => console.error(err)) + } } const createProposal = async () => { @@ -75,22 +49,22 @@ const blockWait = 100 const targetVote = 500000 - const { request } = await publicClient().simulateContract({ + const { request } = await publicClient($configuredChain).simulateContract({ address: PUBLIC_VOTE_CONTRACT_ADDRESS as `0x${string}`, abi: voteContractAbi, functionName: 'propose', args: [description, BigInt(blockWait), targetVote], - account: userAddress + account: $connectionDetails.userAddress }) - const result = await walletClient().writeContract(request) + const result = await walletClient($configuredChain).writeContract(request) console.log(result) } const getCurrentProposal = async () => { const contract = getContract({ address: PUBLIC_ERC20_CONTRACT_ADDRESS as `0x${string}`, - publicClient: publicClient(), - walletClient: walletClient(), + publicClient: publicClient($configuredChain), + walletClient: walletClient($configuredChain), abi: voteContractAbi }) const result = await contract.read.getProposal([BigInt(1)]) @@ -98,21 +72,11 @@ } </script> -<Nav on:click={connectWallet} /> -{#if isConnected} - <p class="text-xl text-green-600"> - Successfully connected with account: <strong>{userAddress}</strong> - </p> - <button class="btn btn-sm" on:click={sendTransaction}>Send transaction</button> - <button class="btn bt-sm" on:click={checkBalance}>Check balance</button> - <button class="btn" on:click={createProposal}>Create proposal</button> - <button class="btn" on:click={getCurrentProposal}>Get current proposal</button> - <ul> - <li>Current Network: {walletClient().chain.name}</li> - <li>Your current balance: {balance && formatEther(balance)} eth</li> - </ul> -{:else} - <button class="btn" on:click={connectWallet}>Connect with Wallet </button> +<Nav /> +{#if $connectionDetails.isConnected} + <button class="btn mt-1" on:click={sendTransaction}>Send transaction</button> + <button class="btn mt-1" on:click={createProposal}>Create proposal</button> + <button class="btn mt-1" on:click={getCurrentProposal}>Get current proposal</button> {/if} <slot /> diff --git a/src/shared/types.ts b/src/shared/types.ts @@ -5,5 +5,12 @@ export interface VotingConfig { export interface ConnectionDetails { isConnected: boolean - userAddress: string + userAddress: `0x${string}` +} + +export interface VoteToken { + balance: bigint + name: string + symbol: string + decimals: number } \ No newline at end of file diff --git a/src/store.ts b/src/store.ts @@ -11,7 +11,7 @@ import { PUBLIC_VOTE_CONTRACT_ADDRESS, PUBLIC_ERC20_CONTRACT_ADDRESS } from '$env/static/public' -import type { ConnectionDetails, VotingConfig } from './shared/types' +import type { VoteToken, ConnectionDetails, VotingConfig } from './shared/types' import { LOCAL_STORAGE_KEYS } from './shared/localstorage-keys' const createChain = () => { @@ -40,7 +40,6 @@ const createChain = () => { if (browser) { localValue = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEYS.CHAIN_CONFIG) || '{}') } - console.log({localValue}) const { subscribe, update } = writable<Chain>(localValue || initialChain) @@ -76,7 +75,20 @@ export const createVotingConfig = () => { export const createConnectionDetails = () => { const { subscribe, update } = writable<ConnectionDetails>({ isConnected: false, - userAddress: '' + userAddress: '' as `0x${string}` + }) + + return { + subscribe, + update + } +} +export const createVoteToken = () => { + const { subscribe, update } = writable<VoteToken>({ + name: '', + symbol: '', + decimals: 0, + balance: BigInt(0) }) return { @@ -88,3 +100,4 @@ export const createConnectionDetails = () => { export const configuredChain = createChain() export const votingConfig = createVotingConfig() export const connectionDetails = createConnectionDetails() +export const voteToken = createVoteToken() diff --git a/src/utils/amout.ts b/src/utils/amout.ts