diff --git a/examples/erc20-react-example/package.json b/examples/erc20-react-example/package.json index ce66b0082..d77af441c 100644 --- a/examples/erc20-react-example/package.json +++ b/examples/erc20-react-example/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { - "@buildwithsygma/sygma-sdk-core": "link:../packages/sdk", + "@buildwithsygma/sygma-sdk-core": "*", "@buildwithsygma/sygma-contracts": "1.0.0", "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.1.1", @@ -21,9 +21,7 @@ "react-scripts": "5.0.1", "ts-node": "^10.7.0", "ethers": "5.6.2", - "typescript": "^4.6.3", - "web-vitals": "^2.1.4", - "web3": "^1.7.3" + "typescript": "^4.6.3" }, "scripts": { "start": "PORT=3001 BROWSER=none react-app-rewired start", @@ -59,6 +57,7 @@ "react-app-rewired": "^2.2.1", "stream-browserify": "^3.0.0", "stream-http": "^3.2.0", - "url": "^0.11.0" + "url": "^0.11.0", + "@metamask/providers": "^10.2.1" } } diff --git a/examples/erc20-react-example/src/App.tsx b/examples/erc20-react-example/src/App.tsx index 2a65b1d50..baaae04e3 100644 --- a/examples/erc20-react-example/src/App.tsx +++ b/examples/erc20-react-example/src/App.tsx @@ -1,20 +1,13 @@ -import React, { - CSSProperties, - SetStateAction, - useEffect, - useState, -} from "react"; -import { BigNumber, utils, constants } from "ethers"; +import React, { CSSProperties, useEffect, useState } from "react"; +import { BigNumber, utils, constants, Event, providers } from "ethers"; + import { useForm } from "react-hook-form"; import { Sygma, - BridgeData, SygmaBridgeSetupList, BridgeEvents, - FeeDataResult } from "@buildwithsygma/sygma-sdk-core"; - const bridgeSetupList: SygmaBridgeSetupList = [ { domainId: "1", @@ -22,13 +15,13 @@ const bridgeSetupList: SygmaBridgeSetupList = [ name: "Local EVM 1", decimals: 18, bridgeAddress: "0x6CdE2Cd82a4F8B74693Ff5e194c19CA08c2d1c68", - erc20HandlerAddress: "0x1ED1d77911944622FCcDDEad8A731fd77E94173e", - erc721HandlerAddress: "0x481f97f9C82a971B3844a422936a4d3c4082bF84", + erc20HandlerAddress: "0x02091EefF969b33A5CE8A729DaE325879bf76f90", + erc721HandlerAddress: "0xC2D334e2f27A9dB2Ed8C4561De86C1A00EBf6760", rpcUrl: "http://localhost:8545", tokens: [ { type: "erc20", - address: "0x1CcB4231f2ff299E1E049De76F0a1D2B415C563A", + address: "0x78E5b9cEC9aEA29071f070C8cC561F692B3511A6", name: "ERC20LRTST", symbol: "ETHIcon", imageUri: "ETHIcon", @@ -37,7 +30,7 @@ const bridgeSetupList: SygmaBridgeSetupList = [ "0x0000000000000000000000000000000000000000000000000000000000000300", feeSettings: { type: "basic", - address: "0x78E5b9cEC9aEA29071f070C8cC561F692B3511A6", + address: "0x8dA96a8C2b2d3e5ae7e668d0C94393aa8D5D3B94", }, }, ], @@ -48,13 +41,13 @@ const bridgeSetupList: SygmaBridgeSetupList = [ name: "Local EVM 2", decimals: 18, bridgeAddress: "0x6CdE2Cd82a4F8B74693Ff5e194c19CA08c2d1c68", - erc20HandlerAddress: "0x1ED1d77911944622FCcDDEad8A731fd77E94173e", + erc20HandlerAddress: "0x02091EefF969b33A5CE8A729DaE325879bf76f90", erc721HandlerAddress: "0x481f97f9C82a971B3844a422936a4d3c4082bF84", rpcUrl: "http://localhost:8547", tokens: [ { type: "erc20", - address: "0x1CcB4231f2ff299E1E049De76F0a1D2B415C563A", + address: "0x78E5b9cEC9aEA29071f070C8cC561F692B3511A6", name: "ERC20LRTST", symbol: "ETHIcon", imageUri: "ETHIcon", @@ -63,20 +56,13 @@ const bridgeSetupList: SygmaBridgeSetupList = [ "0x0000000000000000000000000000000000000000000000000000000000000300", feeSettings: { type: "basic", - address: "0x78E5b9cEC9aEA29071f070C8cC561F692B3511A6", + address: "0x8dA96a8C2b2d3e5ae7e668d0C94393aa8D5D3B94", }, }, ], }, ]; -const feeOracleSetup = { - feeOracleBaseUrl: "http://localhost:8091", - feeOracleHandlerAddress: "0xa9ddD97e1762920679f3C20ec779D79a81903c0B", -}; - -const notEve = "0xF4314cb9046bECe6AA54bb9533155434d0c76909"; - type LocalData = { balance: BigNumber; address: string; @@ -87,26 +73,26 @@ type LocalData = { type SygmaData = { chain1: BridgeEvents; chain2: BridgeEvents }; -const proposalExecutionEventsLogs = async ( - originDomainId: any, - despositNonce: any, - dataHash: any, - tx: any -) => { +const proposalExecutionEventsLogs = ( + originDomainId: number, + despositNonce: BigNumber, + dataHash: string, + tx: Event +): void => { console.warn("Proposal execution event!"); console.log({ originDomainId, despositNonce, dataHash, tx }); console.warn("Transfer complete!"); }; const depositEventLogs = ( - destinationDomainId: any, - resourceId: any, - depositNonce: any, - user: any, - data: any, + destinationDomainId: number, + resourceId: string, + depositNonce: BigNumber, + user: string, + data: string, handleResponse: any, - tx: any -) => { + tx: Event +): void => { console.log( `bride deposit event deposit nonce: ${depositNonce.toString()} to contract with ResourceId: ${resourceId}` ); @@ -114,29 +100,23 @@ const depositEventLogs = ( console.info("Deposit in transit!"); }; -function App() { - const [data, setData] = useState( +function App(): JSX.Element { + const [data, setData] = useState(undefined); + + const [sygmaInstance, setSygmaInstance] = useState( + undefined + ); + const [homeDepositNonce, setHomeDepositNonce] = useState( + undefined + ); + const [accountData, setAccountData] = useState( undefined ); - - const [sygmaInstance, setSygmaInstance] = useState(undefined); - const [homeDepositNonce, setHomeDepositNonce] = useState(undefined) - const [accountData, setAccountData] = useState(undefined); const [metaIsConnected, setMetaIsConnected] = useState(false); const [isReady, setIsReady] = useState(false); - const [logicConnected, setLogicConnected] = useState( - false - ); - const [customFee, setCustomFee] = useState(); + const [logicConnected, setLogicConnected] = useState(false); - - const { - register, - handleSubmit, - watch, - setValue, - formState: { errors }, - } = useForm({ + const { register, handleSubmit, watch, setValue } = useForm({ defaultValues: { amount: "1", address: "0x4CEEf6139f00F9F4535Ad19640Ff7A0137708485", @@ -150,25 +130,20 @@ function App() { useEffect(() => { const setup = { bridgeSetupList }; const sygma = new Sygma(setup); - console.log("🚀 ~ file: App.tsx ~ line 153 ~ useEffect ~ sygma", sygma) setSygmaInstance(sygma); }, []); - useEffect(() => { - if (sygmaInstance) { + if (sygmaInstance && sygmaInstance.bridgeSetup?.chain2 && logicConnected) { sygmaInstance.setDestination(watchTo); } - }, [watchTo, sygmaInstance]); - + }, [watchTo, sygmaInstance, logicConnected]); useEffect(() => { if (sygmaInstance && homeDepositNonce) { sygmaInstance.removeHomeChainDepositEventListener(); - sygmaInstance.createHomeChainDepositEventListener( - depositEventLogs - ); + void sygmaInstance.createHomeChainDepositEventListener(depositEventLogs); sygmaInstance.removeDestinationProposalExecutionEventListener(); sygmaInstance.destinationProposalExecutionEventListener( @@ -178,26 +153,24 @@ function App() { } }, [sygmaInstance, homeDepositNonce]); - const getAccountData = async (sygma: Sygma) => { + const getAccountData = async (sygma: Sygma): Promise => { try { const balance = (await sygma.getSignerBalance("chain1")) ?? BigNumber.from("0"); const address = await sygma.getSignerAddress("chain1"); const gasPrice = await sygma.getSignerGasPrice("chain1"); - const { balanceOfTokens, tokenName } = await sygma.getTokenInfo( - "chain1" - ); - console.log("signer balance", utils.formatEther(balance!)); + const { balanceOfTokens, tokenName } = await sygma.getTokenInfo("chain1"); + console.log("signer balance", utils.formatEther(balance)); console.log("signer address", address); - console.log("gas price", utils.formatEther(gasPrice!)); + console.log("gas price", utils.formatEther(gasPrice ?? 0)); console.log("balance of tokens", utils.formatUnits(balanceOfTokens, 18)); - setValue("address", address!) + setValue("address", address ?? ""); setAccountData({ - balance: balance!, - address: address!, - gasPrice: gasPrice!, - balanceOfTokens: balanceOfTokens!, - tokenName: tokenName!, + balance: balance, + address: address ?? "", + gasPrice: gasPrice ?? BigNumber.from("0"), + balanceOfTokens: balanceOfTokens, + tokenName: tokenName, }); setIsReady(true); } catch (e) { @@ -208,7 +181,7 @@ function App() { useEffect(() => { if (window.ethereum !== undefined) { - window.ethereum._metamask.isUnlocked().then((d: any) => { + void window.ethereum._metamask.isUnlocked().then((d: boolean) => { console.log("is metamask unlocked?", d); setMetaIsConnected(d); }); @@ -217,76 +190,94 @@ function App() { useEffect(() => { if (data !== undefined && sygmaInstance !== undefined) { - getAccountData(sygmaInstance); + void getAccountData(sygmaInstance); } }, [data, logicConnected]); useEffect(() => { - if (metaIsConnected && sygmaInstance !== undefined) { - handleConnect(); - getAccountData(sygmaInstance! as Sygma); - setValue("from", sygmaInstance.bridgeSetup?.chain1.domainId!) - setValue("to", sygmaInstance.bridgeSetup?.chain2.domainId!) + if ( + metaIsConnected && + sygmaInstance !== undefined && + sygmaInstance.bridgeSetup && + isReady + ) { + void handleConnect(); + void getAccountData(sygmaInstance); + setValue("from", sygmaInstance.bridgeSetup?.chain1.domainId); + setValue("to", sygmaInstance.bridgeSetup?.chain2.domainId); } - }, [metaIsConnected]); - - const submit = async (values: any) => { - const { amount, address, from, to } = values; - - const basicFeeData = await (sygmaInstance as Sygma).fetchBasicFeeData( - { + }, [metaIsConnected, sygmaInstance, isReady]); + + const submit = async (values: { + amount: string; + address: string; + from: string; + to: string; + }): Promise => { + const { amount, address } = values; + if (sygmaInstance) { + const basicFeeData = await sygmaInstance.fetchBasicFeeData({ amount: amount, recipientAddress: address, - } - ); - if (!(basicFeeData instanceof Error)) { - console.log( - "🚀 ~ file: App.tsx ~ line 244 ~ submit ~ feeOracleData", - basicFeeData - ); - if ( - window.confirm( - `Current fee: ${basicFeeData.calculatedRate} ${basicFeeData.erc20TokenAddress === constants.AddressZero ? 'ETH' : basicFeeData.erc20TokenAddress}\n\nTotal(amount+fee): ${ - parseFloat(amount) + parseFloat(basicFeeData.calculatedRate) - } tokens\n\nDo you really want to proceed?` - ) - ) { - const approveTx = await (sygmaInstance as Sygma).approve({ - amountOrIdForApproval: "1", - }); + }); + if (!(basicFeeData instanceof Error)) { console.log( - "🚀 ~ file: App.tsx ~ line 259 ~ submit ~ approveTx", - approveTx + "🚀 ~ file: App.tsx ~ line 244 ~ submit ~ feeOracleData", + basicFeeData ); - const depositTx = await (sygmaInstance as Sygma).deposit({ - amount, - recipientAddress: address, - feeData: basicFeeData, - }); - const depositEvent = await sygmaInstance!.getDepositEventFromReceipt(depositTx!) - const { depositNonce } = depositEvent.args; - setHomeDepositNonce(depositNonce.toNumber()); - console.log("result of transfer", depositTx); + if ( + window.confirm( + `Current fee: ${basicFeeData.calculatedRate} ${ + basicFeeData.erc20TokenAddress === constants.AddressZero + ? "ETH" + : basicFeeData.erc20TokenAddress + }\n\nTotal(amount+fee): ${ + parseFloat(amount) + parseFloat(basicFeeData.calculatedRate) + } tokens\n\nDo you really want to proceed?` + ) + ) { + const approveTx = await sygmaInstance.approve({ + amountOrIdForApproval: "1", + }); + console.log( + "🚀 ~ file: App.tsx ~ line 259 ~ submit ~ approveTx", + approveTx + ); + const depositTx = await sygmaInstance.deposit({ + amount, + recipientAddress: address, + feeData: basicFeeData, + }); + if (depositTx) { + const depositEvent = await sygmaInstance.getDepositEventFromReceipt( + depositTx + ); + const { depositNonce } = depositEvent.args; + setHomeDepositNonce(depositNonce.toNumber()); + console.log("result of transfer", depositTx); + } + } } } }; - const handleConnect = () => { + const handleConnect = (): Promise | undefined => { // IF META IS NOT SIGNIN, TRIGGER POP OF THE WINDOW FOR THE EXTENSION if (!metaIsConnected) { return window.ethereum .request({ method: "eth_requestAccounts" }) - .then((r: any) => { - console.log("request to unlock metamask", r); - const [addr] = r; + .then((result) => { + console.log("request to unlock metamask", result); + + const [addr] = result as string[]; setMetaIsConnected(true); - setAccountData({ - ...(accountData as LocalData), - address: addr, - }); + if (accountData) { + accountData.address = addr; + setAccountData(accountData); + } }) - .catch((error: any) => { - if (error.code === 4001) { + .catch((error: { code?: number }) => { + if (error.code && error.code === 4001) { // EIP-1193 userRejectedRequest error console.log("Please connect to MetaMask."); } else { @@ -294,9 +285,8 @@ function App() { } }); } else if (metaIsConnected) { - - const data = (sygmaInstance as Sygma).initializeConnectionFromWeb3Provider( - window.ethereum + const data = sygmaInstance?.initializeConnectionFromWeb3Provider( + window.ethereum as unknown as providers.ExternalProvider ); //@ts-ignore-line @@ -355,27 +345,16 @@ function App() { >

Account data

- Address: {(accountData as LocalData).address} + Address: {accountData.address}
- ETH:{" "} - - {utils.formatEther((accountData as LocalData).balance)} - + ETH: {utils.formatEther(accountData.balance)}
- Gas price:{" "} - - {utils.formatEther((accountData as LocalData).gasPrice)} - + Gas price: {utils.formatEther(accountData.gasPrice)}
Balance of tokens:{" "} - - {utils.formatUnits( - (accountData as LocalData).balanceOfTokens, - 18 - )} - {" "} - of {(accountData as LocalData).tokenName} tokens + {utils.formatUnits(accountData.balanceOfTokens, 18)} of{" "} + {accountData.tokenName} tokens

@@ -385,27 +364,33 @@ function App() { <>
void handleSubmit(submit)(...args)} style={{ display: "flex", flexDirection: "column", }} > - + - + - + - + - {tokenList && tokenList.map((tokenId) => ( - - ))} - + {tokenList && + tokenList.map((tokenId) => ( + + ))}