diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index ba08709..a455d06 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -8,14 +8,19 @@ import { Symfoni } from "./hardhat/SymfoniContext"; import { Greeter } from './components/Greeter'; import { Bridge } from './components/Bridge'; import { getAndSetProvider } from './utils/network'; +import { ERC20 } from './types/ERC20'; import Header from './components/Header'; +import { getISntEthereum } from './utils/contracts'; +import { goerliProvider } from './utils/providers' const { useState, useEffect } = React; function App() { const classes: any = useStyles(); const [provider, setProvider] = useState(); + const [ethereumProvider, setEthereumProvider] = useState(); const [account, setAccount] = useState(''); + const [sntEthereum, setSntEthereum] = useState(); useEffect(() => { if (!provider) getAndSetProvider(setProvider); @@ -24,12 +29,19 @@ function App() { }) }) + useEffect(() => { + if (provider) { + const snt: ERC20 = getISntEthereum(goerliProvider) + setSntEthereum(snt); + } + }, [provider]) + useEffect(() => { if (window.ethereum && window.ethereum.selectedAddress) setAccount(window.ethereum.selectedAddress); window.ethereum.on('accountsChanged', function (accounts: Array) { setAccount(accounts[0]); }); - }, [window.ethereum.selectedAddress]) + }) return ( @@ -39,8 +51,13 @@ function App() { account={account} provider={provider} enableEthereum={undefined} + sntEthereum={sntEthereum} + /> + - diff --git a/frontend/src/components/Bridge.tsx b/frontend/src/components/Bridge.tsx index 561a461..adc530c 100644 --- a/frontend/src/components/Bridge.tsx +++ b/frontend/src/components/Bridge.tsx @@ -1,20 +1,32 @@ import React, { useContext, useEffect, useState } from 'react'; import { BridgeContext, SymfoniBridge } from "./../hardhat/SymfoniContext"; -import { Wallet, providers, Contract, VoidSigner } from "ethers"; +import { Wallet, providers, Contract, VoidSigner, BigNumberish } from "ethers"; +import Typography from '@material-ui/core/Typography'; import { Bridge as IBridge } from "../types/Bridge" -import { SNT_ADDRESS } from "../../constants/goerliAddress"; +import { SNT_ADDRESS } from "../constants/goerliAddress"; import { Formik, FormikProps } from 'formik'; import StatusTextField from './base/TextField'; import useStyles from '../styles/bridge'; import StatusButton from './base/Button'; - +import { ETHEREUM_CHAIN_ID, AVA_CHAIN_ID } from '../constants/networks'; +import { createResourceID, createERCDepositData, toWei } from '../utils/helpers'; +import { Provider } from '@ethersproject/providers'; +import { getISntEthereum } from '../utils/contracts'; +import { ERC20 } from "../types/ERC20"; +import { fromWei } from "../utils/helpers" +import { getSetBalance } from "../utils/contracts"; type IBridgeInfo = { amount: string, + account: string, } const wallet = Wallet.createRandom(); -interface Props { } +interface Props { + account: string, + provider: Provider | undefined, + sntEthereum: ERC20 | undefined +} const FUJI_BRIDGE = '0xE57Eb49689bCAE4dE61D326F7E79Bd14aB527f0f'; const GOERLI_BRIDGE = '0xD0E461b1Dc56503fC72565FA964C28E274146D44'; const fujiProvider = new providers.JsonRpcProvider("https://api.avax-test.network/ext/bc/C/rpc"); @@ -24,13 +36,14 @@ const fujiVoidSigner = new VoidSigner(wallet.address, fujiProvider); const goerliVoidsigner = new VoidSigner(wallet.address, goerliProvider); -export const Bridge: React.FC = () => { +export const Bridge: React.FC = ({ account, provider, sntEthereum }) => { const classes: any = useStyles() const bridge: SymfoniBridge = useContext(BridgeContext); const [message, setMessage] = useState(""); const [inputGreeting, setInputGreeting] = useState(""); const [goerliBridge, setGoerliBridge] = useState(); const [fujiBridge, setFujiBridge] = useState(); + const [sntEthereumBalance, setSntEthereumBalance] = useState() const { fieldWidth } = classes; useEffect(() => { const doAsync = async () => { @@ -52,6 +65,10 @@ export const Bridge: React.FC = () => { doAsync(); }, [bridge]) + useEffect(() => { + getSetBalance(sntEthereum, account, setSntEthereumBalance); + }, [account]) + const handleSetGreeting = async (e: React.MouseEvent) => { e.preventDefault() if (!bridge.instance) throw Error("Bridge instance not ready") @@ -67,8 +84,18 @@ export const Bridge: React.FC = () => { { + const { amount, account } = values; + const resourceId = createResourceID(SNT_ADDRESS, ETHEREUM_CHAIN_ID); + const encodedData = createERCDepositData(toWei(amount), account); + const tx = await bridge.instance?.deposit( + AVA_CHAIN_ID, + resourceId, + encodedData + ); }} - onSubmit={async (values) => {}} > {({ values, @@ -80,15 +107,27 @@ export const Bridge: React.FC = () => { }: FormikProps) => { return (
+ {!!sntEthereumBalance && + SNT Ethereum balance: {fromWei(sntEthereumBalance)} + } + = () => { )} } -
+ ) } diff --git a/frontend/src/components/Header.tsx b/frontend/src/components/Header.tsx index a910583..cb2d880 100644 --- a/frontend/src/components/Header.tsx +++ b/frontend/src/components/Header.tsx @@ -5,7 +5,7 @@ import { EnableEthereum } from '../localTypes' import useStyles from '../styles/header' import { getNetwork } from '../utils/network' import { Provider } from '@ethersproject/providers' - +import { ERC20 } from "../types/ERC20"; const formatAccount = (account: string): string => { const start = account.slice(0,6) @@ -16,10 +16,12 @@ const formatAccount = (account: string): string => { type HeaderProps = { account: string, enableEthereum: EnableEthereum | undefined, - provider: Provider | undefined + provider: Provider | undefined, + sntEthereum: ERC20 | undefined } -function Header({account, enableEthereum, provider}: HeaderProps) { + +function Header({account, enableEthereum, provider, sntEthereum}: HeaderProps) { const classes: any = useStyles() const [network, sNetwork] = useState() diff --git a/frontend/constants/goerliAddress.ts b/frontend/src/constants/goerliAddress.ts similarity index 100% rename from frontend/constants/goerliAddress.ts rename to frontend/src/constants/goerliAddress.ts diff --git a/frontend/src/constants/networks.ts b/frontend/src/constants/networks.ts new file mode 100644 index 0000000..f53da00 --- /dev/null +++ b/frontend/src/constants/networks.ts @@ -0,0 +1,2 @@ +export const ETHEREUM_CHAIN_ID: number = 1; +export const AVA_CHAIN_ID: number = 2; diff --git a/frontend/src/styles/bridge.js b/frontend/src/styles/bridge.js index 228db58..2c3c5e8 100644 --- a/frontend/src/styles/bridge.js +++ b/frontend/src/styles/bridge.js @@ -6,7 +6,7 @@ const useStyles = makeStyles(theme => ({ gridTemplateColumns: 'repeat(48, [col] 1fr)', gridColumn: '3 / 45', [theme.breakpoints.up('md')]: { - gridTemplateRows: '4rem 4rem auto auto 6rem', + gridTemplateRows: '3rem 6rem 6rem 5rem', gridColumn: '8 / 42', } }, @@ -14,6 +14,11 @@ const useStyles = makeStyles(theme => ({ cursor: 'pointer', color: '#4360DF' }, + balanceText: { + color: '#025ea2', + gridColumn: '3 / 49', + fontSize: '1.5rem' + }, title: { gridColumn: '3 / 49' }, diff --git a/frontend/src/utils/contracts.ts b/frontend/src/utils/contracts.ts new file mode 100644 index 0000000..9fcef06 --- /dev/null +++ b/frontend/src/utils/contracts.ts @@ -0,0 +1,14 @@ +import { Wallet, providers, Contract, VoidSigner } from "ethers"; +import { SNT_ADDRESS } from "../constants/goerliAddress"; +import { ERC20 } from "../types/ERC20"; +import { ERC20__factory } from "../types/factories/ERC20__factory" +import { Provider } from '@ethersproject/providers' + +export const getISntEthereum = (provider: Provider) => { + return ERC20__factory.connect(SNT_ADDRESS, provider); +} + +export const getSetBalance = async (token: ERC20|undefined, account: string, setState: Function) => { + const balance = await token?.balanceOf(account); + setState(balance); +} diff --git a/frontend/src/utils/helpers.ts b/frontend/src/utils/helpers.ts new file mode 100644 index 0000000..9821e1b --- /dev/null +++ b/frontend/src/utils/helpers.ts @@ -0,0 +1,48 @@ +import { Address } from "cluster"; +import { BigNumberish, BytesLike, utils } from "ethers"; +const { + hexZeroPad, + hexlify, + formatBytes32String, + keccak256, + formatEther +} = utils; + +export const fromWei = (x: BigNumberish) => Number(formatEther(x)); +const AbiCoder = new utils.AbiCoder; +export const toHex = (covertThis: any, padding: any) => { + return hexZeroPad(hexlify(covertThis), padding); +}; + +export const createResourceID = (contractAddress: string, chainID: number) => { + const str = chainID.toString() + contractAddress + return formatBytes32String(str.slice(0, 31)); +}; + +export const abiEncode = (valueTypes: any[], values: any[]) => { + return AbiCoder.encode(valueTypes, values) +}; + +export const generateDepositMetaData = (amount: number, recipientAddress: string) => { + console.log({recipientAddress}) + const addressLength = 20; + return abiEncode( + ['uint256', 'uint256', 'bytes'], + [1, addressLength, recipientAddress] + ); +} + +export const toWei = (x: string) => utils.parseEther(x); + +export const createERCDepositData = ( + tokenAmountOrID: BigNumberish, + recipientAddress: string +): string => { + const lenRecipientAddress = 20; + return '0x' + + toHex(tokenAmountOrID, 32).substr(2) + // Token amount or ID to deposit (32 bytes) + toHex(lenRecipientAddress, 32).substr(2) + // len(recipientAddress) (32 bytes) + recipientAddress.substr(2); // recipientAddress (?? bytes) +}; + +export const generateDepositDataHash = (handler: string, depositData: string): BytesLike => keccak256(handler + depositData.substr(2)); diff --git a/frontend/src/utils/providers.ts b/frontend/src/utils/providers.ts new file mode 100644 index 0000000..95b4a7e --- /dev/null +++ b/frontend/src/utils/providers.ts @@ -0,0 +1,8 @@ +import { Wallet, providers, Contract, VoidSigner } from "ethers"; + +const wallet = Wallet.createRandom(); +export const goerliProvider = new providers.InfuraProvider("goerli"); +export const fujiProvider = new providers.JsonRpcProvider("https://api.avax-test.network/ext/bc/C/rpc"); +const fujiSigner = new Wallet(wallet.privateKey, fujiProvider); +export const fujiVoidSigner = new VoidSigner(wallet.address, fujiProvider); +export const goerliVoidsigner = new VoidSigner(wallet.address, goerliProvider); diff --git a/utils/helpers.ts b/utils/helpers.ts index 2c3a2ec..d421b6e 100644 --- a/utils/helpers.ts +++ b/utils/helpers.ts @@ -22,7 +22,6 @@ export const abiEncode = (valueTypes: any[], values: any[]) => { }; export const generateDepositMetaData = (amount: number, recipientAddress: string) => { - console.log({recipientAddress}) const addressLength = 20; return abiEncode( ['uint256', 'uint256', 'bytes'],