add multichain asset tracking

This commit is contained in:
Barry Gitarts 2021-02-04 12:17:35 -05:00
parent 8893e767bf
commit 07912aef80
10 changed files with 149 additions and 15 deletions

View File

@ -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<Provider>();
const [ethereumProvider, setEthereumProvider] = useState<Provider>();
const [account, setAccount] = useState<string>('');
const [sntEthereum, setSntEthereum] = useState<ERC20>();
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<string>) {
setAccount(accounts[0]);
});
}, [window.ethereum.selectedAddress])
})
return (
<ThemeProvider theme={theme}>
@ -39,8 +51,13 @@ function App() {
account={account}
provider={provider}
enableEthereum={undefined}
sntEthereum={sntEthereum}
/>
<Bridge
account={account}
provider={provider}
sntEthereum={sntEthereum}
/>
<Bridge></Bridge>
</div>
</Symfoni>
</ThemeProvider>

View File

@ -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<Props> = () => {
export const Bridge: React.FC<Props> = ({ account, provider, sntEthereum }) => {
const classes: any = useStyles()
const bridge: SymfoniBridge = useContext(BridgeContext);
const [message, setMessage] = useState("");
const [inputGreeting, setInputGreeting] = useState("");
const [goerliBridge, setGoerliBridge] = useState<IBridge>();
const [fujiBridge, setFujiBridge] = useState<IBridge>();
const [sntEthereumBalance, setSntEthereumBalance] = useState<BigNumberish>()
const { fieldWidth } = classes;
useEffect(() => {
const doAsync = async () => {
@ -52,6 +65,10 @@ export const Bridge: React.FC<Props> = () => {
doAsync();
}, [bridge])
useEffect(() => {
getSetBalance(sntEthereum, account, setSntEthereumBalance);
}, [account])
const handleSetGreeting = async (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
e.preventDefault()
if (!bridge.instance) throw Error("Bridge instance not ready")
@ -67,8 +84,18 @@ export const Bridge: React.FC<Props> = () => {
<Formik
initialValues={{
amount: '',
account
}}
onSubmit={async (values) => {
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<Props> = () => {
}: FormikProps<IBridgeInfo>) => {
return (
<form className={classes.root} onSubmit={handleSubmit}>
{!!sntEthereumBalance && <Typography className={classes.balanceText}>
SNT Ethereum balance: {fromWei(sntEthereumBalance)}
</Typography>}
<StatusTextField
className={fieldWidth}
name="amount"
label="Enter amount of SNT to send across bridge"
idFor="Title"
idFor="amount"
onChange={handleChange}
onBlur={handleBlur}
value={values.amount}
/>
<StatusTextField
className={fieldWidth}
name="account"
label="account receiving across bridge"
idFor="account"
onChange={handleChange}
onBlur={handleBlur}
value={values.account}
/>
<StatusButton
className={fieldWidth}
buttonText="Submit"
@ -97,7 +136,7 @@ export const Bridge: React.FC<Props> = () => {
</form>
)}
}
</Formik>
</Formik>
)
}

View File

@ -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<string>()

View File

@ -0,0 +1,2 @@
export const ETHEREUM_CHAIN_ID: number = 1;
export const AVA_CHAIN_ID: number = 2;

View File

@ -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'
},

View File

@ -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);
}

View File

@ -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));

View File

@ -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);

View File

@ -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'],