diff --git a/bridge_ui/package-lock.json b/bridge_ui/package-lock.json
index 8ecc63f8..207078b3 100644
--- a/bridge_ui/package-lock.json
+++ b/bridge_ui/package-lock.json
@@ -20,6 +20,7 @@
"@typechain/ethers-v5": "^7.0.1",
"bridge": "file:rust_modules\\core",
"ethers": "^5.4.1",
+ "prettier": "^2.3.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-redux": "^7.2.4",
@@ -29467,7 +29468,6 @@
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz",
"integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==",
- "peer": true,
"bin": {
"prettier": "bin-prettier.js"
},
@@ -35735,7 +35735,6 @@
"resolved": "https://registry.npmjs.org/wasm-dce/-/wasm-dce-1.0.2.tgz",
"integrity": "sha512-Fq1+nu43ybsjSnBquLrW/cULmKs61qbv9k8ep13QUe0nABBezMoNAA+j6QY66MW0/eoDVDp1rjXDqQ2VKyS/Xg==",
"dev": true,
- "peer": true,
"dependencies": {
"@babel/core": "^7.0.0-beta.39",
"@babel/traverse": "^7.0.0-beta.39",
@@ -35749,7 +35748,6 @@
"resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.47.tgz",
"integrity": "sha512-+rq2cr4GDhtToEzKFD6KZZMDBXhjFAr9JjPw9pAppZACeEWqNM294j+NdBzkSHYXwzzBmVjZ3nEVJlOhbR2gOQ==",
"dev": true,
- "peer": true,
"bin": {
"babylon": "bin/babylon.js"
},
@@ -36585,7 +36583,6 @@
"resolved": "https://registry.npmjs.org/webassembly-floating-point-hex-parser/-/webassembly-floating-point-hex-parser-0.1.2.tgz",
"integrity": "sha512-TUf1H++8U10+stJbFydnvrpG5Sznz5Rilez/oZlV5zI0C/e4cSxd8rALAJ8VpTvjVWxLmL3SVSJUK6Ap9AoiNg==",
"dev": true,
- "peer": true,
"engines": {
"node": "*"
}
@@ -36595,7 +36592,6 @@
"resolved": "https://registry.npmjs.org/webassembly-interpreter/-/webassembly-interpreter-0.0.30.tgz",
"integrity": "sha512-+Jdy2piEvz9T5j751mOE8+rBO12p+nNW6Fg4kJZ+zP1oUfsm+151sbAbM8AFxWTURmWCGP+r8Lxwfv3pzN1bCQ==",
"dev": true,
- "peer": true,
"dependencies": {
"@babel/code-frame": "^7.0.0-beta.36",
"long": "^3.2.0",
@@ -36615,7 +36611,6 @@
"resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz",
"integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=",
"dev": true,
- "peer": true,
"engines": {
"node": ">=0.6"
}
@@ -46630,6 +46625,7 @@
"dev": true,
"optional": true,
"requires": {
+ "bitcore-lib": "^8.25.10",
"unorm": "^1.4.1"
}
},
@@ -62340,8 +62336,7 @@
"prettier": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz",
- "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==",
- "peer": true
+ "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ=="
},
"pretty-bytes": {
"version": "5.6.0",
@@ -67424,7 +67419,6 @@
"resolved": "https://registry.npmjs.org/wasm-dce/-/wasm-dce-1.0.2.tgz",
"integrity": "sha512-Fq1+nu43ybsjSnBquLrW/cULmKs61qbv9k8ep13QUe0nABBezMoNAA+j6QY66MW0/eoDVDp1rjXDqQ2VKyS/Xg==",
"dev": true,
- "peer": true,
"requires": {
"@babel/core": "^7.0.0-beta.39",
"@babel/traverse": "^7.0.0-beta.39",
@@ -67437,8 +67431,7 @@
"version": "7.0.0-beta.47",
"resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.47.tgz",
"integrity": "sha512-+rq2cr4GDhtToEzKFD6KZZMDBXhjFAr9JjPw9pAppZACeEWqNM294j+NdBzkSHYXwzzBmVjZ3nEVJlOhbR2gOQ==",
- "dev": true,
- "peer": true
+ "dev": true
}
}
},
@@ -67448,7 +67441,8 @@
"integrity": "sha512-R4s75XH+o8qM+WaRrAU9S2rbAMDzob18/S3V8R9ZoFpZkPWLAohWWlzWAp1ybeTkOuuku/X1zJtxiV0pBYxZww==",
"dev": true,
"requires": {
- "loader-utils": "^1.1.0"
+ "loader-utils": "^1.1.0",
+ "wasm-dce": "^1.0.0"
},
"dependencies": {
"json5": {
@@ -68170,15 +68164,13 @@
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/webassembly-floating-point-hex-parser/-/webassembly-floating-point-hex-parser-0.1.2.tgz",
"integrity": "sha512-TUf1H++8U10+stJbFydnvrpG5Sznz5Rilez/oZlV5zI0C/e4cSxd8rALAJ8VpTvjVWxLmL3SVSJUK6Ap9AoiNg==",
- "dev": true,
- "peer": true
+ "dev": true
},
"webassembly-interpreter": {
"version": "0.0.30",
"resolved": "https://registry.npmjs.org/webassembly-interpreter/-/webassembly-interpreter-0.0.30.tgz",
"integrity": "sha512-+Jdy2piEvz9T5j751mOE8+rBO12p+nNW6Fg4kJZ+zP1oUfsm+151sbAbM8AFxWTURmWCGP+r8Lxwfv3pzN1bCQ==",
"dev": true,
- "peer": true,
"requires": {
"@babel/code-frame": "^7.0.0-beta.36",
"long": "^3.2.0",
@@ -68189,8 +68181,7 @@
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz",
"integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=",
- "dev": true,
- "peer": true
+ "dev": true
}
}
},
diff --git a/bridge_ui/package.json b/bridge_ui/package.json
index f7757224..030a3689 100644
--- a/bridge_ui/package.json
+++ b/bridge_ui/package.json
@@ -53,6 +53,7 @@
"@truffle/hdwallet-provider": "^1.4.1",
"copy-dir": "^1.3.0",
"truffle": "^5.4.1",
- "wasm-loader": "^1.3.0"
+ "wasm-loader": "^1.3.0",
+ "prettier": "^2.3.2"
}
}
diff --git a/bridge_ui/src/components/Attest/Create.tsx b/bridge_ui/src/components/Attest/Create.tsx
index c595577a..466084eb 100644
--- a/bridge_ui/src/components/Attest/Create.tsx
+++ b/bridge_ui/src/components/Attest/Create.tsx
@@ -1,70 +1,83 @@
-import { Button, CircularProgress, makeStyles } from "@material-ui/core";
-import { useCallback } from "react";
-import { useDispatch, useSelector } from "react-redux";
-import { useSolanaWallet } from "../../contexts/SolanaWalletContext";
-import useAttestSignedVAA from "../../hooks/useAttestSignedVAA";
-import { setIsCreating } from "../../store/attestSlice";
-import {
- selectAttestIsCreating,
- selectAttestTargetChain,
-} from "../../store/selectors";
-import { CHAIN_ID_SOLANA } from "../../utils/consts";
-import createWrappedOn, {
- createWrappedOnSolana,
-} from "../../utils/createWrappedOn";
-
-const useStyles = makeStyles((theme) => ({
- transferButton: {
- marginTop: theme.spacing(2),
- textTransform: "none",
- width: "100%",
- },
-}));
-
-function Create() {
- const dispatch = useDispatch();
- const classes = useStyles();
- const targetChain = useSelector(selectAttestTargetChain);
- const { wallet } = useSolanaWallet();
- const solPK = wallet?.publicKey;
- const signedVAA = useAttestSignedVAA();
- const isCreating = useSelector(selectAttestIsCreating);
- const handleCreateClick = useCallback(() => {
- if (
- targetChain === CHAIN_ID_SOLANA &&
- createWrappedOn[targetChain] === createWrappedOnSolana &&
- signedVAA
- ) {
- dispatch(setIsCreating(true));
- createWrappedOnSolana(wallet, solPK?.toString(), signedVAA);
- }
- }, [dispatch, targetChain, wallet, solPK, signedVAA]);
- return (
-
-
- {isCreating ? (
-
- ) : null}
-
- );
-}
-
-export default Create;
+import { Button, CircularProgress, makeStyles } from "@material-ui/core";
+import { useCallback } from "react";
+import { useDispatch, useSelector } from "react-redux";
+import { useSolanaWallet } from "../../contexts/SolanaWalletContext";
+import useAttestSignedVAA from "../../hooks/useAttestSignedVAA";
+import { setIsCreating } from "../../store/attestSlice";
+import {
+ selectAttestIsCreating,
+ selectAttestTargetChain,
+} from "../../store/selectors";
+import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "../../utils/consts";
+import createWrappedOn, {
+ createWrappedOnEth,
+ createWrappedOnSolana,
+} from "../../utils/createWrappedOn";
+import { useEthereumProvider } from "../../contexts/EthereumProviderContext";
+
+const useStyles = makeStyles((theme) => ({
+ transferButton: {
+ marginTop: theme.spacing(2),
+ textTransform: "none",
+ width: "100%",
+ },
+}));
+
+function Create() {
+ const dispatch = useDispatch();
+ const classes = useStyles();
+ const targetChain = useSelector(selectAttestTargetChain);
+ const { wallet } = useSolanaWallet();
+ const solPK = wallet?.publicKey;
+ const signedVAA = useAttestSignedVAA();
+ const isCreating = useSelector(selectAttestIsCreating);
+ const { provider, signer } = useEthereumProvider();
+ const handleCreateClick = useCallback(() => {
+ if (
+ targetChain === CHAIN_ID_SOLANA &&
+ createWrappedOn[targetChain] === createWrappedOnSolana &&
+ signedVAA
+ ) {
+ dispatch(setIsCreating(true));
+ createWrappedOnSolana(wallet, solPK?.toString(), signedVAA);
+ }
+ if (
+ targetChain === CHAIN_ID_ETH &&
+ createWrappedOn[targetChain] === createWrappedOnEth &&
+ signedVAA
+ ) {
+ (async () => {
+ dispatch(setIsCreating(true));
+ createWrappedOnEth(provider, signer, signedVAA);
+ })();
+ }
+ }, [dispatch, targetChain, wallet, solPK, signedVAA]);
+ return (
+
+
+ {isCreating ? (
+
+ ) : null}
+
+ );
+}
+
+export default Create;
diff --git a/bridge_ui/src/components/Attest/Send.tsx b/bridge_ui/src/components/Attest/Send.tsx
index 4b9186db..6a475562 100644
--- a/bridge_ui/src/components/Attest/Send.tsx
+++ b/bridge_ui/src/components/Attest/Send.tsx
@@ -1,127 +1,127 @@
-import { Button, CircularProgress, makeStyles } from "@material-ui/core";
-import { useCallback } from "react";
-import { useDispatch, useSelector } from "react-redux";
-import { useEthereumProvider } from "../../contexts/EthereumProviderContext";
-import { useSolanaWallet } from "../../contexts/SolanaWalletContext";
-import useWrappedAsset from "../../hooks/useWrappedAsset";
-import { setIsSending, setSignedVAAHex } from "../../store/attestSlice";
-import {
- selectAttestIsSendComplete,
- selectAttestIsSending,
- selectAttestIsTargetComplete,
- selectAttestSourceAsset,
- selectAttestSourceChain,
- selectAttestTargetChain,
-} from "../../store/selectors";
-import { uint8ArrayToHex } from "../../utils/array";
-import attestFrom, {
- attestFromEth,
- attestFromSolana,
-} from "../../utils/attestFrom";
-import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "../../utils/consts";
-
-const useStyles = makeStyles((theme) => ({
- transferButton: {
- marginTop: theme.spacing(2),
- textTransform: "none",
- width: "100%",
- },
-}));
-
-// TODO: move attest to its own workflow
-
-function Send() {
- const classes = useStyles();
- const dispatch = useDispatch();
- const sourceChain = useSelector(selectAttestSourceChain);
- const sourceAsset = useSelector(selectAttestSourceAsset);
- const targetChain = useSelector(selectAttestTargetChain);
- const isTargetComplete = useSelector(selectAttestIsTargetComplete);
- const isSending = useSelector(selectAttestIsSending);
- const isSendComplete = useSelector(selectAttestIsSendComplete);
- const { provider, signer } = useEthereumProvider();
- const { wallet } = useSolanaWallet();
- const solPK = wallet?.publicKey;
- const {
- isLoading: isCheckingWrapped,
- // isWrapped,
- wrappedAsset,
- } = useWrappedAsset(targetChain, sourceChain, sourceAsset, provider);
- // TODO: check this and send to separate flow
- const isWrapped = true;
- console.log(isCheckingWrapped, isWrapped, wrappedAsset);
- // TODO: dynamically get "to" wallet
- const handleAttestClick = useCallback(() => {
- // TODO: more generic way of calling these
- if (attestFrom[sourceChain]) {
- if (
- sourceChain === CHAIN_ID_ETH &&
- attestFrom[sourceChain] === attestFromEth
- ) {
- //TODO: just for testing, this should eventually use the store to communicate between steps
- (async () => {
- dispatch(setIsSending(true));
- try {
- const vaaBytes = await attestFromEth(provider, signer, sourceAsset);
- console.log("bytes in attest", vaaBytes);
- vaaBytes && dispatch(setSignedVAAHex(uint8ArrayToHex(vaaBytes)));
- } catch (e) {
- console.error(e);
- dispatch(setIsSending(false));
- }
- })();
- }
- if (
- sourceChain === CHAIN_ID_SOLANA &&
- attestFrom[sourceChain] === attestFromSolana
- ) {
- //TODO: just for testing, this should eventually use the store to communicate between steps
- (async () => {
- dispatch(setIsSending(true));
- try {
- const vaaBytes = await attestFromSolana(
- wallet,
- solPK?.toString(),
- sourceAsset
- );
- console.log("bytes in attest", vaaBytes);
- vaaBytes && dispatch(setSignedVAAHex(uint8ArrayToHex(vaaBytes)));
- } catch (e) {
- console.error(e);
- dispatch(setIsSending(false));
- }
- })();
- }
- }
- }, [dispatch, sourceChain, provider, signer, wallet, solPK, sourceAsset]);
- return (
- <>
-
-
- {isSending ? (
-
- ) : null}
-
- >
- );
-}
-
-export default Send;
+import { Button, CircularProgress, makeStyles } from "@material-ui/core";
+import { useCallback } from "react";
+import { useDispatch, useSelector } from "react-redux";
+import { useEthereumProvider } from "../../contexts/EthereumProviderContext";
+import { useSolanaWallet } from "../../contexts/SolanaWalletContext";
+import useWrappedAsset from "../../hooks/useWrappedAsset";
+import { setIsSending, setSignedVAAHex } from "../../store/attestSlice";
+import {
+ selectAttestIsSendComplete,
+ selectAttestIsSending,
+ selectAttestIsTargetComplete,
+ selectAttestSourceAsset,
+ selectAttestSourceChain,
+ selectAttestTargetChain,
+} from "../../store/selectors";
+import { uint8ArrayToHex } from "../../utils/array";
+import attestFrom, {
+ attestFromEth,
+ attestFromSolana,
+} from "../../utils/attestFrom";
+import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "../../utils/consts";
+
+const useStyles = makeStyles((theme) => ({
+ transferButton: {
+ marginTop: theme.spacing(2),
+ textTransform: "none",
+ width: "100%",
+ },
+}));
+
+// TODO: move attest to its own workflow
+
+function Send() {
+ const classes = useStyles();
+ const dispatch = useDispatch();
+ const sourceChain = useSelector(selectAttestSourceChain);
+ const sourceAsset = useSelector(selectAttestSourceAsset);
+ const targetChain = useSelector(selectAttestTargetChain);
+ const isTargetComplete = useSelector(selectAttestIsTargetComplete);
+ const isSending = useSelector(selectAttestIsSending);
+ const isSendComplete = useSelector(selectAttestIsSendComplete);
+ const { provider, signer } = useEthereumProvider();
+ const { wallet } = useSolanaWallet();
+ const solPK = wallet?.publicKey;
+ const {
+ isLoading: isCheckingWrapped,
+ // isWrapped,
+ wrappedAsset,
+ } = useWrappedAsset(targetChain, sourceChain, sourceAsset, provider);
+ // TODO: check this and send to separate flow
+ const isWrapped = true;
+ console.log(isCheckingWrapped, isWrapped, wrappedAsset);
+ // TODO: dynamically get "to" wallet
+ const handleAttestClick = useCallback(() => {
+ // TODO: more generic way of calling these
+ if (attestFrom[sourceChain]) {
+ if (
+ sourceChain === CHAIN_ID_ETH &&
+ attestFrom[sourceChain] === attestFromEth
+ ) {
+ //TODO: just for testing, this should eventually use the store to communicate between steps
+ (async () => {
+ dispatch(setIsSending(true));
+ try {
+ const vaaBytes = await attestFromEth(provider, signer, sourceAsset);
+ console.log("bytes in attest", vaaBytes);
+ vaaBytes && dispatch(setSignedVAAHex(uint8ArrayToHex(vaaBytes)));
+ } catch (e) {
+ console.error(e);
+ dispatch(setIsSending(false));
+ }
+ })();
+ }
+ if (
+ sourceChain === CHAIN_ID_SOLANA &&
+ attestFrom[sourceChain] === attestFromSolana
+ ) {
+ //TODO: just for testing, this should eventually use the store to communicate between steps
+ (async () => {
+ dispatch(setIsSending(true));
+ try {
+ const vaaBytes = await attestFromSolana(
+ wallet,
+ solPK?.toString(),
+ sourceAsset
+ );
+ console.log("bytes in attest", vaaBytes);
+ vaaBytes && dispatch(setSignedVAAHex(uint8ArrayToHex(vaaBytes)));
+ } catch (e) {
+ console.error(e);
+ dispatch(setIsSending(false));
+ }
+ })();
+ }
+ }
+ }, [dispatch, sourceChain, provider, signer, wallet, solPK, sourceAsset]);
+ return (
+ <>
+
+
+ {isSending ? (
+
+ ) : null}
+
+ >
+ );
+}
+
+export default Send;
diff --git a/bridge_ui/src/components/Attest/Source.tsx b/bridge_ui/src/components/Attest/Source.tsx
index a3a22452..4630ac83 100644
--- a/bridge_ui/src/components/Attest/Source.tsx
+++ b/bridge_ui/src/components/Attest/Source.tsx
@@ -1,85 +1,85 @@
-import { Button, makeStyles, MenuItem, TextField } from "@material-ui/core";
-import { useCallback } from "react";
-import { useDispatch, useSelector } from "react-redux";
-import {
- selectAttestIsSourceComplete,
- selectAttestShouldLockFields,
- selectAttestSourceAsset,
- selectAttestSourceChain,
-} from "../../store/selectors";
-import {
- incrementStep,
- setSourceAsset,
- setSourceChain,
-} from "../../store/attestSlice";
-import { CHAINS } from "../../utils/consts";
-import KeyAndBalance from "../KeyAndBalance";
-
-const useStyles = makeStyles((theme) => ({
- transferField: {
- marginTop: theme.spacing(5),
- },
-}));
-
-function Source() {
- const classes = useStyles();
- const dispatch = useDispatch();
- const sourceChain = useSelector(selectAttestSourceChain);
- const sourceAsset = useSelector(selectAttestSourceAsset);
- const isSourceComplete = useSelector(selectAttestIsSourceComplete);
- const shouldLockFields = useSelector(selectAttestShouldLockFields);
- const handleSourceChange = useCallback(
- (event) => {
- dispatch(setSourceChain(event.target.value));
- },
- [dispatch]
- );
- const handleAssetChange = useCallback(
- (event) => {
- dispatch(setSourceAsset(event.target.value));
- },
- [dispatch]
- );
- const handleNextClick = useCallback(
- (event) => {
- dispatch(incrementStep());
- },
- [dispatch]
- );
- return (
- <>
-
- {CHAINS.map(({ id, name }) => (
-
- ))}
-
-
-
-
- >
- );
-}
-
-export default Source;
+import { Button, makeStyles, MenuItem, TextField } from "@material-ui/core";
+import { useCallback } from "react";
+import { useDispatch, useSelector } from "react-redux";
+import {
+ selectAttestIsSourceComplete,
+ selectAttestShouldLockFields,
+ selectAttestSourceAsset,
+ selectAttestSourceChain,
+} from "../../store/selectors";
+import {
+ incrementStep,
+ setSourceAsset,
+ setSourceChain,
+} from "../../store/attestSlice";
+import { CHAINS } from "../../utils/consts";
+import KeyAndBalance from "../KeyAndBalance";
+
+const useStyles = makeStyles((theme) => ({
+ transferField: {
+ marginTop: theme.spacing(5),
+ },
+}));
+
+function Source() {
+ const classes = useStyles();
+ const dispatch = useDispatch();
+ const sourceChain = useSelector(selectAttestSourceChain);
+ const sourceAsset = useSelector(selectAttestSourceAsset);
+ const isSourceComplete = useSelector(selectAttestIsSourceComplete);
+ const shouldLockFields = useSelector(selectAttestShouldLockFields);
+ const handleSourceChange = useCallback(
+ (event) => {
+ dispatch(setSourceChain(event.target.value));
+ },
+ [dispatch]
+ );
+ const handleAssetChange = useCallback(
+ (event) => {
+ dispatch(setSourceAsset(event.target.value));
+ },
+ [dispatch]
+ );
+ const handleNextClick = useCallback(
+ (event) => {
+ dispatch(incrementStep());
+ },
+ [dispatch]
+ );
+ return (
+ <>
+
+ {CHAINS.map(({ id, name }) => (
+
+ ))}
+
+
+
+
+ >
+ );
+}
+
+export default Source;
diff --git a/bridge_ui/src/components/Attest/Target.tsx b/bridge_ui/src/components/Attest/Target.tsx
index 9bda1e38..5eeb7ed6 100644
--- a/bridge_ui/src/components/Attest/Target.tsx
+++ b/bridge_ui/src/components/Attest/Target.tsx
@@ -1,65 +1,65 @@
-import { Button, MenuItem, TextField } from "@material-ui/core";
-import { useCallback, useMemo } from "react";
-import { useDispatch, useSelector } from "react-redux";
-import {
- selectAttestIsTargetComplete,
- selectAttestShouldLockFields,
- selectAttestSourceChain,
- selectAttestTargetChain,
-} from "../../store/selectors";
-import { incrementStep, setTargetChain } from "../../store/attestSlice";
-import { CHAINS } from "../../utils/consts";
-import KeyAndBalance from "../KeyAndBalance";
-
-function Target() {
- const dispatch = useDispatch();
- const sourceChain = useSelector(selectAttestSourceChain);
- const chains = useMemo(
- () => CHAINS.filter((c) => c.id !== sourceChain),
- [sourceChain]
- );
- const targetChain = useSelector(selectAttestTargetChain);
- const isTargetComplete = useSelector(selectAttestIsTargetComplete);
- const shouldLockFields = useSelector(selectAttestShouldLockFields);
- const handleTargetChange = useCallback(
- (event) => {
- dispatch(setTargetChain(event.target.value));
- },
- [dispatch]
- );
- const handleNextClick = useCallback(
- (event) => {
- dispatch(incrementStep());
- },
- [dispatch]
- );
- return (
- <>
-
- {chains.map(({ id, name }) => (
-
- ))}
-
- {/* TODO: determine "to" token address */}
-
-
- >
- );
-}
-
-export default Target;
+import { Button, MenuItem, TextField } from "@material-ui/core";
+import { useCallback, useMemo } from "react";
+import { useDispatch, useSelector } from "react-redux";
+import {
+ selectAttestIsTargetComplete,
+ selectAttestShouldLockFields,
+ selectAttestSourceChain,
+ selectAttestTargetChain,
+} from "../../store/selectors";
+import { incrementStep, setTargetChain } from "../../store/attestSlice";
+import { CHAINS } from "../../utils/consts";
+import KeyAndBalance from "../KeyAndBalance";
+
+function Target() {
+ const dispatch = useDispatch();
+ const sourceChain = useSelector(selectAttestSourceChain);
+ const chains = useMemo(
+ () => CHAINS.filter((c) => c.id !== sourceChain),
+ [sourceChain]
+ );
+ const targetChain = useSelector(selectAttestTargetChain);
+ const isTargetComplete = useSelector(selectAttestIsTargetComplete);
+ const shouldLockFields = useSelector(selectAttestShouldLockFields);
+ const handleTargetChange = useCallback(
+ (event) => {
+ dispatch(setTargetChain(event.target.value));
+ },
+ [dispatch]
+ );
+ const handleNextClick = useCallback(
+ (event) => {
+ dispatch(incrementStep());
+ },
+ [dispatch]
+ );
+ return (
+ <>
+
+ {chains.map(({ id, name }) => (
+
+ ))}
+
+ {/* TODO: determine "to" token address */}
+
+
+ >
+ );
+}
+
+export default Target;
diff --git a/bridge_ui/src/components/Attest/index.tsx b/bridge_ui/src/components/Attest/index.tsx
index 34444623..34e0d63b 100644
--- a/bridge_ui/src/components/Attest/index.tsx
+++ b/bridge_ui/src/components/Attest/index.tsx
@@ -1,70 +1,70 @@
-import {
- Container,
- Step,
- StepButton,
- StepContent,
- Stepper,
-} from "@material-ui/core";
-import { useDispatch, useSelector } from "react-redux";
-import useGetBalanceEffect from "../../hooks/useGetBalanceEffect";
-import {
- selectAttestActiveStep,
- selectAttestSignedVAAHex,
-} from "../../store/selectors";
-import { setStep } from "../../store/attestSlice";
-import Create from "./Create";
-import Send from "./Send";
-import Source from "./Source";
-import Target from "./Target";
-
-// TODO: ensure that both wallets are connected to the same known network
-
-function Attest() {
- useGetBalanceEffect();
- const dispatch = useDispatch();
- const activeStep = useSelector(selectAttestActiveStep);
- const signedVAAHex = useSelector(selectAttestSignedVAAHex);
- return (
-
-
-
- dispatch(setStep(0))}>
- Select a source
-
-
-
-
-
-
- dispatch(setStep(1))}>
- Select a target
-
-
-
-
-
-
- dispatch(setStep(2))}>
- Send attestation
-
-
-
-
-
-
- dispatch(setStep(3))}
- disabled={!signedVAAHex}
- >
- Create wrapper
-
-
-
-
-
-
-
- );
-}
-
-export default Attest;
+import {
+ Container,
+ Step,
+ StepButton,
+ StepContent,
+ Stepper,
+} from "@material-ui/core";
+import { useDispatch, useSelector } from "react-redux";
+import useGetBalanceEffect from "../../hooks/useGetBalanceEffect";
+import {
+ selectAttestActiveStep,
+ selectAttestSignedVAAHex,
+} from "../../store/selectors";
+import { setStep } from "../../store/attestSlice";
+import Create from "./Create";
+import Send from "./Send";
+import Source from "./Source";
+import Target from "./Target";
+
+// TODO: ensure that both wallets are connected to the same known network
+
+function Attest() {
+ useGetBalanceEffect();
+ const dispatch = useDispatch();
+ const activeStep = useSelector(selectAttestActiveStep);
+ const signedVAAHex = useSelector(selectAttestSignedVAAHex);
+ return (
+
+
+
+ dispatch(setStep(0))}>
+ Select a source
+
+
+
+
+
+
+ dispatch(setStep(1))}>
+ Select a target
+
+
+
+
+
+
+ dispatch(setStep(2))}>
+ Send attestation
+
+
+
+
+
+
+ dispatch(setStep(3))}
+ disabled={!signedVAAHex}
+ >
+ Create wrapper
+
+
+
+
+
+
+
+ );
+}
+
+export default Attest;
diff --git a/bridge_ui/src/components/EthereumSignerKey.tsx b/bridge_ui/src/components/EthereumSignerKey.tsx
index 09d8a462..f77c8006 100644
--- a/bridge_ui/src/components/EthereumSignerKey.tsx
+++ b/bridge_ui/src/components/EthereumSignerKey.tsx
@@ -1,25 +1,25 @@
-import { Typography } from "@material-ui/core";
-import { useEthereumProvider } from "../contexts/EthereumProviderContext";
-import ToggleConnectedButton from "./ToggleConnectedButton";
-
-const EthereumSignerKey = () => {
- const { connect, disconnect, signerAddress, providerError } =
- useEthereumProvider();
- return (
- <>
-
- {providerError ? (
-
- {providerError}
-
- ) : null}
- >
- );
-};
-
-export default EthereumSignerKey;
+import { Typography } from "@material-ui/core";
+import { useEthereumProvider } from "../contexts/EthereumProviderContext";
+import ToggleConnectedButton from "./ToggleConnectedButton";
+
+const EthereumSignerKey = () => {
+ const { connect, disconnect, signerAddress, providerError } =
+ useEthereumProvider();
+ return (
+ <>
+
+ {providerError ? (
+
+ {providerError}
+
+ ) : null}
+ >
+ );
+};
+
+export default EthereumSignerKey;
diff --git a/bridge_ui/src/components/KeyAndBalance.tsx b/bridge_ui/src/components/KeyAndBalance.tsx
index 7835e5f0..8d07beb0 100644
--- a/bridge_ui/src/components/KeyAndBalance.tsx
+++ b/bridge_ui/src/components/KeyAndBalance.tsx
@@ -1,32 +1,32 @@
-import { Typography } from "@material-ui/core";
-import { ChainId, CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "../utils/consts";
-import EthereumSignerKey from "./EthereumSignerKey";
-import SolanaWalletKey from "./SolanaWalletKey";
-
-function KeyAndBalance({
- chainId,
- balance,
-}: {
- chainId: ChainId;
- balance?: string;
-}) {
- if (chainId === CHAIN_ID_ETH) {
- return (
- <>
-
- {balance}
- >
- );
- }
- if (chainId === CHAIN_ID_SOLANA) {
- return (
- <>
-
- {balance}
- >
- );
- }
- return null;
-}
-
-export default KeyAndBalance;
+import { Typography } from "@material-ui/core";
+import { ChainId, CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "../utils/consts";
+import EthereumSignerKey from "./EthereumSignerKey";
+import SolanaWalletKey from "./SolanaWalletKey";
+
+function KeyAndBalance({
+ chainId,
+ balance,
+}: {
+ chainId: ChainId;
+ balance?: string;
+}) {
+ if (chainId === CHAIN_ID_ETH) {
+ return (
+ <>
+
+ {balance}
+ >
+ );
+ }
+ if (chainId === CHAIN_ID_SOLANA) {
+ return (
+ <>
+
+ {balance}
+ >
+ );
+ }
+ return null;
+}
+
+export default KeyAndBalance;
diff --git a/bridge_ui/src/components/SolanaWalletKey.tsx b/bridge_ui/src/components/SolanaWalletKey.tsx
index 10ec46fa..6056720f 100644
--- a/bridge_ui/src/components/SolanaWalletKey.tsx
+++ b/bridge_ui/src/components/SolanaWalletKey.tsx
@@ -1,17 +1,17 @@
-import { useSolanaWallet } from "../contexts/SolanaWalletContext";
-import ToggleConnectedButton from "./ToggleConnectedButton";
-
-const SolanaWalletKey = () => {
- const { connect, disconnect, connected, wallet } = useSolanaWallet();
- const pk = wallet?.publicKey?.toString() || "";
- return (
-
- );
-};
-
-export default SolanaWalletKey;
+import { useSolanaWallet } from "../contexts/SolanaWalletContext";
+import ToggleConnectedButton from "./ToggleConnectedButton";
+
+const SolanaWalletKey = () => {
+ const { connect, disconnect, connected, wallet } = useSolanaWallet();
+ const pk = wallet?.publicKey?.toString() || "";
+ return (
+
+ );
+};
+
+export default SolanaWalletKey;
diff --git a/bridge_ui/src/components/ToggleConnectedButton.tsx b/bridge_ui/src/components/ToggleConnectedButton.tsx
index 3cfae25b..529ab278 100644
--- a/bridge_ui/src/components/ToggleConnectedButton.tsx
+++ b/bridge_ui/src/components/ToggleConnectedButton.tsx
@@ -1,51 +1,51 @@
-import { Button, makeStyles, Tooltip } from "@material-ui/core";
-
-const useStyles = makeStyles((theme) => ({
- button: {
- display: "block",
- margin: `${theme.spacing(1)}px auto`,
- width: "100%",
- maxWidth: 400,
- },
-}));
-
-const ToggleConnectedButton = ({
- connect,
- disconnect,
- connected,
- pk,
-}: {
- connect(): any;
- disconnect(): any;
- connected: boolean;
- pk: string;
-}) => {
- const classes = useStyles();
- const is0x = pk.startsWith("0x");
- return connected ? (
-
-
-
- ) : (
-
- );
-};
-
-export default ToggleConnectedButton;
+import { Button, makeStyles, Tooltip } from "@material-ui/core";
+
+const useStyles = makeStyles((theme) => ({
+ button: {
+ display: "block",
+ margin: `${theme.spacing(1)}px auto`,
+ width: "100%",
+ maxWidth: 400,
+ },
+}));
+
+const ToggleConnectedButton = ({
+ connect,
+ disconnect,
+ connected,
+ pk,
+}: {
+ connect(): any;
+ disconnect(): any;
+ connected: boolean;
+ pk: string;
+}) => {
+ const classes = useStyles();
+ const is0x = pk.startsWith("0x");
+ return connected ? (
+
+
+
+ ) : (
+
+ );
+};
+
+export default ToggleConnectedButton;
diff --git a/bridge_ui/src/components/Transfer/Redeem.tsx b/bridge_ui/src/components/Transfer/Redeem.tsx
index bac87ec6..be408504 100644
--- a/bridge_ui/src/components/Transfer/Redeem.tsx
+++ b/bridge_ui/src/components/Transfer/Redeem.tsx
@@ -1,67 +1,78 @@
-import { Button, CircularProgress, makeStyles } from "@material-ui/core";
-import { useCallback } from "react";
-import { useDispatch, useSelector } from "react-redux";
-import { useEthereumProvider } from "../../contexts/EthereumProviderContext";
-import useTransferSignedVAA from "../../hooks/useTransferSignedVAA";
-import {
- selectTransferIsRedeeming,
- selectTransferTargetChain,
-} from "../../store/selectors";
-import { setIsRedeeming } from "../../store/transferSlice";
-import { CHAIN_ID_ETH } from "../../utils/consts";
-import redeemOn, { redeemOnEth } from "../../utils/redeemOn";
-
-const useStyles = makeStyles((theme) => ({
- transferButton: {
- marginTop: theme.spacing(2),
- textTransform: "none",
- width: "100%",
- },
-}));
-
-function Redeem() {
- const dispatch = useDispatch();
- const classes = useStyles();
- const targetChain = useSelector(selectTransferTargetChain);
- const { provider, signer } = useEthereumProvider();
- const signedVAA = useTransferSignedVAA();
- const isRedeeming = useSelector(selectTransferIsRedeeming);
- const handleRedeemClick = useCallback(() => {
- if (
- targetChain === CHAIN_ID_ETH &&
- redeemOn[targetChain] === redeemOnEth &&
- signedVAA
- ) {
- dispatch(setIsRedeeming(true));
- redeemOnEth(provider, signer, signedVAA);
- }
- }, [dispatch, targetChain, provider, signer, signedVAA]);
- return (
-
-
- {isRedeeming ? (
-
- ) : null}
-
- );
-}
-
-export default Redeem;
+import { Button, CircularProgress, makeStyles } from "@material-ui/core";
+import { useCallback } from "react";
+import { useDispatch, useSelector } from "react-redux";
+import { useEthereumProvider } from "../../contexts/EthereumProviderContext";
+import useTransferSignedVAA from "../../hooks/useTransferSignedVAA";
+import {
+ selectTransferIsRedeeming,
+ selectTransferTargetChain,
+} from "../../store/selectors";
+import { setIsRedeeming } from "../../store/transferSlice";
+import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "../../utils/consts";
+import redeemOn, { redeemOnEth, redeemOnSolana } from "../../utils/redeemOn";
+import { useSolanaWallet } from "../../contexts/SolanaWalletContext";
+
+const useStyles = makeStyles((theme) => ({
+ transferButton: {
+ marginTop: theme.spacing(2),
+ textTransform: "none",
+ width: "100%",
+ },
+}));
+
+function Redeem() {
+ const dispatch = useDispatch();
+ const classes = useStyles();
+ const targetChain = useSelector(selectTransferTargetChain);
+ const { wallet } = useSolanaWallet();
+ const solPK = wallet?.publicKey;
+ const { provider, signer } = useEthereumProvider();
+ const signedVAA = useTransferSignedVAA();
+ const isRedeeming = useSelector(selectTransferIsRedeeming);
+ const handleRedeemClick = useCallback(() => {
+ if (
+ targetChain === CHAIN_ID_ETH &&
+ redeemOn[targetChain] === redeemOnEth &&
+ signedVAA
+ ) {
+ dispatch(setIsRedeeming(true));
+ redeemOnEth(provider, signer, signedVAA);
+ }
+ if (
+ targetChain === CHAIN_ID_SOLANA &&
+ redeemOn[targetChain] === redeemOnSolana &&
+ signedVAA
+ ) {
+ dispatch(setIsRedeeming(true));
+ redeemOnSolana(wallet, solPK?.toString(), signedVAA);
+ }
+ }, [dispatch, targetChain, provider, signer, signedVAA]);
+ return (
+
+
+ {isRedeeming ? (
+
+ ) : null}
+
+ );
+}
+
+export default Redeem;
diff --git a/bridge_ui/src/components/Transfer/Send.tsx b/bridge_ui/src/components/Transfer/Send.tsx
index bc1fb1b9..a88e4221 100644
--- a/bridge_ui/src/components/Transfer/Send.tsx
+++ b/bridge_ui/src/components/Transfer/Send.tsx
@@ -1,164 +1,164 @@
-import { Button, CircularProgress, makeStyles } from "@material-ui/core";
-import { useCallback } from "react";
-import { useDispatch, useSelector } from "react-redux";
-import { useEthereumProvider } from "../../contexts/EthereumProviderContext";
-import { useSolanaWallet } from "../../contexts/SolanaWalletContext";
-import useWrappedAsset from "../../hooks/useWrappedAsset";
-import {
- selectTransferAmount,
- selectTransferIsSendComplete,
- selectTransferIsSending,
- selectTransferIsTargetComplete,
- selectTransferSourceAsset,
- selectTransferSourceChain,
- selectTransferSourceParsedTokenAccount,
- selectTransferTargetChain,
-} from "../../store/selectors";
-import { setIsSending, setSignedVAAHex } from "../../store/transferSlice";
-import { uint8ArrayToHex } from "../../utils/array";
-import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "../../utils/consts";
-import transferFrom, {
- transferFromEth,
- transferFromSolana,
-} from "../../utils/transferFrom";
-
-const useStyles = makeStyles((theme) => ({
- transferButton: {
- marginTop: theme.spacing(2),
- textTransform: "none",
- width: "100%",
- },
-}));
-
-// TODO: move attest to its own workflow
-
-function Send() {
- const classes = useStyles();
- const dispatch = useDispatch();
- const sourceChain = useSelector(selectTransferSourceChain);
- const sourceAsset = useSelector(selectTransferSourceAsset);
- const amount = useSelector(selectTransferAmount);
- const targetChain = useSelector(selectTransferTargetChain);
- const isTargetComplete = useSelector(selectTransferIsTargetComplete);
- const isSending = useSelector(selectTransferIsSending);
- const isSendComplete = useSelector(selectTransferIsSendComplete);
- const { provider, signer, signerAddress } = useEthereumProvider();
- const { wallet } = useSolanaWallet();
- const solPK = wallet?.publicKey;
- const sourceParsedTokenAccount = useSelector(
- selectTransferSourceParsedTokenAccount
- );
- const tokenPK = sourceParsedTokenAccount?.publicKey;
- const decimals = sourceParsedTokenAccount?.decimals;
- const {
- isLoading: isCheckingWrapped,
- // isWrapped,
- wrappedAsset,
- } = useWrappedAsset(targetChain, sourceChain, sourceAsset, provider);
- // TODO: check this and send to separate flow
- const isWrapped = true;
- console.log(isCheckingWrapped, isWrapped, wrappedAsset);
- // TODO: dynamically get "to" wallet
- const handleTransferClick = useCallback(() => {
- // TODO: we should separate state for transaction vs fetching vaa
- // TODO: more generic way of calling these
- if (transferFrom[sourceChain]) {
- if (
- sourceChain === CHAIN_ID_ETH &&
- transferFrom[sourceChain] === transferFromEth &&
- decimals
- ) {
- //TODO: just for testing, this should eventually use the store to communicate between steps
- (async () => {
- dispatch(setIsSending(true));
- try {
- const vaaBytes = await transferFromEth(
- provider,
- signer,
- sourceAsset,
- decimals,
- amount,
- targetChain,
- solPK?.toBytes()
- );
- console.log("bytes in transfer", vaaBytes);
- vaaBytes && dispatch(setSignedVAAHex(uint8ArrayToHex(vaaBytes)));
- } catch (e) {
- console.error(e);
- dispatch(setIsSending(false));
- }
- })();
- }
- if (
- sourceChain === CHAIN_ID_SOLANA &&
- transferFrom[sourceChain] === transferFromSolana &&
- decimals
- ) {
- //TODO: just for testing, this should eventually use the store to communicate between steps
- (async () => {
- dispatch(setIsSending(true));
- try {
- const vaaBytes = await transferFromSolana(
- wallet,
- solPK?.toString(),
- tokenPK,
- sourceAsset,
- amount,
- decimals,
- signerAddress,
- targetChain
- );
- console.log("bytes in transfer", vaaBytes);
- vaaBytes && dispatch(setSignedVAAHex(uint8ArrayToHex(vaaBytes)));
- } catch (e) {
- console.error(e);
- dispatch(setIsSending(false));
- }
- })();
- }
- }
- }, [
- dispatch,
- sourceChain,
- provider,
- signer,
- signerAddress,
- wallet,
- solPK,
- tokenPK,
- sourceAsset,
- amount,
- decimals,
- targetChain,
- ]);
- return (
- <>
-
-
- {isSending ? (
-
- ) : null}
-
- >
- );
-}
-
-export default Send;
+import { Button, CircularProgress, makeStyles } from "@material-ui/core";
+import { useCallback } from "react";
+import { useDispatch, useSelector } from "react-redux";
+import { useEthereumProvider } from "../../contexts/EthereumProviderContext";
+import { useSolanaWallet } from "../../contexts/SolanaWalletContext";
+import useWrappedAsset from "../../hooks/useWrappedAsset";
+import {
+ selectTransferAmount,
+ selectTransferIsSendComplete,
+ selectTransferIsSending,
+ selectTransferIsTargetComplete,
+ selectTransferSourceAsset,
+ selectTransferSourceChain,
+ selectTransferSourceParsedTokenAccount,
+ selectTransferTargetChain,
+} from "../../store/selectors";
+import { setIsSending, setSignedVAAHex } from "../../store/transferSlice";
+import { uint8ArrayToHex } from "../../utils/array";
+import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "../../utils/consts";
+import transferFrom, {
+ transferFromEth,
+ transferFromSolana,
+} from "../../utils/transferFrom";
+
+const useStyles = makeStyles((theme) => ({
+ transferButton: {
+ marginTop: theme.spacing(2),
+ textTransform: "none",
+ width: "100%",
+ },
+}));
+
+// TODO: move attest to its own workflow
+
+function Send() {
+ const classes = useStyles();
+ const dispatch = useDispatch();
+ const sourceChain = useSelector(selectTransferSourceChain);
+ const sourceAsset = useSelector(selectTransferSourceAsset);
+ const amount = useSelector(selectTransferAmount);
+ const targetChain = useSelector(selectTransferTargetChain);
+ const isTargetComplete = useSelector(selectTransferIsTargetComplete);
+ const isSending = useSelector(selectTransferIsSending);
+ const isSendComplete = useSelector(selectTransferIsSendComplete);
+ const { provider, signer, signerAddress } = useEthereumProvider();
+ const { wallet } = useSolanaWallet();
+ const solPK = wallet?.publicKey;
+ const sourceParsedTokenAccount = useSelector(
+ selectTransferSourceParsedTokenAccount
+ );
+ const tokenPK = sourceParsedTokenAccount?.publicKey;
+ const decimals = sourceParsedTokenAccount?.decimals;
+ const {
+ isLoading: isCheckingWrapped,
+ // isWrapped,
+ wrappedAsset,
+ } = useWrappedAsset(targetChain, sourceChain, sourceAsset, provider);
+ // TODO: check this and send to separate flow
+ const isWrapped = true;
+ console.log(isCheckingWrapped, isWrapped, wrappedAsset);
+ // TODO: dynamically get "to" wallet
+ const handleTransferClick = useCallback(() => {
+ // TODO: we should separate state for transaction vs fetching vaa
+ // TODO: more generic way of calling these
+ if (transferFrom[sourceChain]) {
+ if (
+ sourceChain === CHAIN_ID_ETH &&
+ transferFrom[sourceChain] === transferFromEth &&
+ decimals
+ ) {
+ //TODO: just for testing, this should eventually use the store to communicate between steps
+ (async () => {
+ dispatch(setIsSending(true));
+ try {
+ const vaaBytes = await transferFromEth(
+ provider,
+ signer,
+ sourceAsset,
+ decimals,
+ amount,
+ targetChain,
+ solPK?.toBytes()
+ );
+ console.log("bytes in transfer", vaaBytes);
+ vaaBytes && dispatch(setSignedVAAHex(uint8ArrayToHex(vaaBytes)));
+ } catch (e) {
+ console.error(e);
+ dispatch(setIsSending(false));
+ }
+ })();
+ }
+ if (
+ sourceChain === CHAIN_ID_SOLANA &&
+ transferFrom[sourceChain] === transferFromSolana &&
+ decimals
+ ) {
+ //TODO: just for testing, this should eventually use the store to communicate between steps
+ (async () => {
+ dispatch(setIsSending(true));
+ try {
+ const vaaBytes = await transferFromSolana(
+ wallet,
+ solPK?.toString(),
+ tokenPK,
+ sourceAsset,
+ amount,
+ decimals,
+ signerAddress,
+ targetChain
+ );
+ console.log("bytes in transfer", vaaBytes);
+ vaaBytes && dispatch(setSignedVAAHex(uint8ArrayToHex(vaaBytes)));
+ } catch (e) {
+ console.error(e);
+ dispatch(setIsSending(false));
+ }
+ })();
+ }
+ }
+ }, [
+ dispatch,
+ sourceChain,
+ provider,
+ signer,
+ signerAddress,
+ wallet,
+ solPK,
+ tokenPK,
+ sourceAsset,
+ amount,
+ decimals,
+ targetChain,
+ ]);
+ return (
+ <>
+
+
+ {isSending ? (
+
+ ) : null}
+
+ >
+ );
+}
+
+export default Send;
diff --git a/bridge_ui/src/components/Transfer/Source.tsx b/bridge_ui/src/components/Transfer/Source.tsx
index f9fa8f2c..09544706 100644
--- a/bridge_ui/src/components/Transfer/Source.tsx
+++ b/bridge_ui/src/components/Transfer/Source.tsx
@@ -1,105 +1,105 @@
-import { Button, makeStyles, MenuItem, TextField } from "@material-ui/core";
-import { useCallback } from "react";
-import { useDispatch, useSelector } from "react-redux";
-import {
- selectTransferAmount,
- selectTransferIsSourceComplete,
- selectTransferShouldLockFields,
- selectTransferSourceAsset,
- selectTransferSourceBalanceString,
- selectTransferSourceChain,
-} from "../../store/selectors";
-import {
- incrementStep,
- setAmount,
- setSourceAsset,
- setSourceChain,
-} from "../../store/transferSlice";
-import { CHAINS } from "../../utils/consts";
-import KeyAndBalance from "../KeyAndBalance";
-
-const useStyles = makeStyles((theme) => ({
- transferField: {
- marginTop: theme.spacing(5),
- },
-}));
-
-function Source() {
- const classes = useStyles();
- const dispatch = useDispatch();
- const sourceChain = useSelector(selectTransferSourceChain);
- const sourceAsset = useSelector(selectTransferSourceAsset);
- const uiAmountString = useSelector(selectTransferSourceBalanceString);
- const amount = useSelector(selectTransferAmount);
- const isSourceComplete = useSelector(selectTransferIsSourceComplete);
- const shouldLockFields = useSelector(selectTransferShouldLockFields);
- const handleSourceChange = useCallback(
- (event) => {
- dispatch(setSourceChain(event.target.value));
- },
- [dispatch]
- );
- const handleAssetChange = useCallback(
- (event) => {
- dispatch(setSourceAsset(event.target.value));
- },
- [dispatch]
- );
- const handleAmountChange = useCallback(
- (event) => {
- dispatch(setAmount(event.target.value));
- },
- [dispatch]
- );
- const handleNextClick = useCallback(
- (event) => {
- dispatch(incrementStep());
- },
- [dispatch]
- );
- return (
- <>
-
- {CHAINS.map(({ id, name }) => (
-
- ))}
-
-
-
-
-
- >
- );
-}
-
-export default Source;
+import { Button, makeStyles, MenuItem, TextField } from "@material-ui/core";
+import { useCallback } from "react";
+import { useDispatch, useSelector } from "react-redux";
+import {
+ selectTransferAmount,
+ selectTransferIsSourceComplete,
+ selectTransferShouldLockFields,
+ selectTransferSourceAsset,
+ selectTransferSourceBalanceString,
+ selectTransferSourceChain,
+} from "../../store/selectors";
+import {
+ incrementStep,
+ setAmount,
+ setSourceAsset,
+ setSourceChain,
+} from "../../store/transferSlice";
+import { CHAINS } from "../../utils/consts";
+import KeyAndBalance from "../KeyAndBalance";
+
+const useStyles = makeStyles((theme) => ({
+ transferField: {
+ marginTop: theme.spacing(5),
+ },
+}));
+
+function Source() {
+ const classes = useStyles();
+ const dispatch = useDispatch();
+ const sourceChain = useSelector(selectTransferSourceChain);
+ const sourceAsset = useSelector(selectTransferSourceAsset);
+ const uiAmountString = useSelector(selectTransferSourceBalanceString);
+ const amount = useSelector(selectTransferAmount);
+ const isSourceComplete = useSelector(selectTransferIsSourceComplete);
+ const shouldLockFields = useSelector(selectTransferShouldLockFields);
+ const handleSourceChange = useCallback(
+ (event) => {
+ dispatch(setSourceChain(event.target.value));
+ },
+ [dispatch]
+ );
+ const handleAssetChange = useCallback(
+ (event) => {
+ dispatch(setSourceAsset(event.target.value));
+ },
+ [dispatch]
+ );
+ const handleAmountChange = useCallback(
+ (event) => {
+ dispatch(setAmount(event.target.value));
+ },
+ [dispatch]
+ );
+ const handleNextClick = useCallback(
+ (event) => {
+ dispatch(incrementStep());
+ },
+ [dispatch]
+ );
+ return (
+ <>
+
+ {CHAINS.map(({ id, name }) => (
+
+ ))}
+
+
+
+
+
+ >
+ );
+}
+
+export default Source;
diff --git a/bridge_ui/src/components/Transfer/Target.tsx b/bridge_ui/src/components/Transfer/Target.tsx
index 6ee21aff..e01642b8 100644
--- a/bridge_ui/src/components/Transfer/Target.tsx
+++ b/bridge_ui/src/components/Transfer/Target.tsx
@@ -1,65 +1,65 @@
-import { Button, MenuItem, TextField } from "@material-ui/core";
-import { useCallback, useMemo } from "react";
-import { useDispatch, useSelector } from "react-redux";
-import {
- selectTransferIsTargetComplete,
- selectTransferShouldLockFields,
- selectTransferSourceChain,
- selectTransferTargetChain,
-} from "../../store/selectors";
-import { incrementStep, setTargetChain } from "../../store/transferSlice";
-import { CHAINS } from "../../utils/consts";
-import KeyAndBalance from "../KeyAndBalance";
-
-function Target() {
- const dispatch = useDispatch();
- const sourceChain = useSelector(selectTransferSourceChain);
- const chains = useMemo(
- () => CHAINS.filter((c) => c.id !== sourceChain),
- [sourceChain]
- );
- const targetChain = useSelector(selectTransferTargetChain);
- const isTargetComplete = useSelector(selectTransferIsTargetComplete);
- const shouldLockFields = useSelector(selectTransferShouldLockFields);
- const handleTargetChange = useCallback(
- (event) => {
- dispatch(setTargetChain(event.target.value));
- },
- [dispatch]
- );
- const handleNextClick = useCallback(
- (event) => {
- dispatch(incrementStep());
- },
- [dispatch]
- );
- return (
- <>
-
- {chains.map(({ id, name }) => (
-
- ))}
-
- {/* TODO: determine "to" token address */}
-
-
- >
- );
-}
-
-export default Target;
+import { Button, MenuItem, TextField } from "@material-ui/core";
+import { useCallback, useMemo } from "react";
+import { useDispatch, useSelector } from "react-redux";
+import {
+ selectTransferIsTargetComplete,
+ selectTransferShouldLockFields,
+ selectTransferSourceChain,
+ selectTransferTargetChain,
+} from "../../store/selectors";
+import { incrementStep, setTargetChain } from "../../store/transferSlice";
+import { CHAINS } from "../../utils/consts";
+import KeyAndBalance from "../KeyAndBalance";
+
+function Target() {
+ const dispatch = useDispatch();
+ const sourceChain = useSelector(selectTransferSourceChain);
+ const chains = useMemo(
+ () => CHAINS.filter((c) => c.id !== sourceChain),
+ [sourceChain]
+ );
+ const targetChain = useSelector(selectTransferTargetChain);
+ const isTargetComplete = useSelector(selectTransferIsTargetComplete);
+ const shouldLockFields = useSelector(selectTransferShouldLockFields);
+ const handleTargetChange = useCallback(
+ (event) => {
+ dispatch(setTargetChain(event.target.value));
+ },
+ [dispatch]
+ );
+ const handleNextClick = useCallback(
+ (event) => {
+ dispatch(incrementStep());
+ },
+ [dispatch]
+ );
+ return (
+ <>
+
+ {chains.map(({ id, name }) => (
+
+ ))}
+
+ {/* TODO: determine "to" token address */}
+
+
+ >
+ );
+}
+
+export default Target;
diff --git a/bridge_ui/src/components/Transfer/index.tsx b/bridge_ui/src/components/Transfer/index.tsx
index 3a9a04d8..b64401e5 100644
--- a/bridge_ui/src/components/Transfer/index.tsx
+++ b/bridge_ui/src/components/Transfer/index.tsx
@@ -1,73 +1,73 @@
-import {
- Container,
- Step,
- StepButton,
- StepContent,
- Stepper,
-} from "@material-ui/core";
-import { useDispatch, useSelector } from "react-redux";
-import useGetBalanceEffect from "../../hooks/useGetBalanceEffect";
-import {
- selectTransferActiveStep,
- selectTransferSignedVAAHex,
-} from "../../store/selectors";
-import { setStep } from "../../store/transferSlice";
-import Redeem from "./Redeem";
-import Send from "./Send";
-import Source from "./Source";
-import Target from "./Target";
-
-// TODO: ensure that both wallets are connected to the same known network
-// TODO: loaders and such, navigation block?
-// TODO: refresh displayed token amount after transfer somehow, could be resolved by having different components appear
-// TODO: warn if amount exceeds balance
-
-function Transfer() {
- useGetBalanceEffect();
- const dispatch = useDispatch();
- const activeStep = useSelector(selectTransferActiveStep);
- const signedVAAHex = useSelector(selectTransferSignedVAAHex);
- return (
-
-
-
- dispatch(setStep(0))}>
- Select a source
-
-
-
-
-
-
- dispatch(setStep(1))}>
- Select a target
-
-
-
-
-
-
- dispatch(setStep(2))}>
- Send tokens
-
-
-
-
-
-
- dispatch(setStep(3))}
- disabled={!signedVAAHex}
- >
- Redeem tokens
-
-
-
-
-
-
-
- );
-}
-
-export default Transfer;
+import {
+ Container,
+ Step,
+ StepButton,
+ StepContent,
+ Stepper,
+} from "@material-ui/core";
+import { useDispatch, useSelector } from "react-redux";
+import useGetBalanceEffect from "../../hooks/useGetBalanceEffect";
+import {
+ selectTransferActiveStep,
+ selectTransferSignedVAAHex,
+} from "../../store/selectors";
+import { setStep } from "../../store/transferSlice";
+import Redeem from "./Redeem";
+import Send from "./Send";
+import Source from "./Source";
+import Target from "./Target";
+
+// TODO: ensure that both wallets are connected to the same known network
+// TODO: loaders and such, navigation block?
+// TODO: refresh displayed token amount after transfer somehow, could be resolved by having different components appear
+// TODO: warn if amount exceeds balance
+
+function Transfer() {
+ useGetBalanceEffect();
+ const dispatch = useDispatch();
+ const activeStep = useSelector(selectTransferActiveStep);
+ const signedVAAHex = useSelector(selectTransferSignedVAAHex);
+ return (
+
+
+
+ dispatch(setStep(0))}>
+ Select a source
+
+
+
+
+
+
+ dispatch(setStep(1))}>
+ Select a target
+
+
+
+
+
+
+ dispatch(setStep(2))}>
+ Send tokens
+
+
+
+
+
+
+ dispatch(setStep(3))}
+ disabled={!signedVAAHex}
+ >
+ Redeem tokens
+
+
+
+
+
+
+
+ );
+}
+
+export default Transfer;
diff --git a/bridge_ui/src/hooks/useAttestSignedVAA.ts b/bridge_ui/src/hooks/useAttestSignedVAA.ts
index c563c26f..d4c5bd66 100644
--- a/bridge_ui/src/hooks/useAttestSignedVAA.ts
+++ b/bridge_ui/src/hooks/useAttestSignedVAA.ts
@@ -1,13 +1,13 @@
-import { useMemo } from "react";
-import { useSelector } from "react-redux";
-import { selectAttestSignedVAAHex } from "../store/selectors";
-import { hexToUint8Array } from "../utils/array";
-
-export default function useAttestSignedVAA() {
- const signedVAAHex = useSelector(selectAttestSignedVAAHex);
- const signedVAA = useMemo(
- () => (signedVAAHex ? hexToUint8Array(signedVAAHex) : undefined),
- [signedVAAHex]
- );
- return signedVAA;
-}
+import { useMemo } from "react";
+import { useSelector } from "react-redux";
+import { selectAttestSignedVAAHex } from "../store/selectors";
+import { hexToUint8Array } from "../utils/array";
+
+export default function useAttestSignedVAA() {
+ const signedVAAHex = useSelector(selectAttestSignedVAAHex);
+ const signedVAA = useMemo(
+ () => (signedVAAHex ? hexToUint8Array(signedVAAHex) : undefined),
+ [signedVAAHex]
+ );
+ return signedVAA;
+}
diff --git a/bridge_ui/src/hooks/useGetBalanceEffect.ts b/bridge_ui/src/hooks/useGetBalanceEffect.ts
index 68998cd2..9e07f478 100644
--- a/bridge_ui/src/hooks/useGetBalanceEffect.ts
+++ b/bridge_ui/src/hooks/useGetBalanceEffect.ts
@@ -1,115 +1,115 @@
-import { Connection, PublicKey } from "@solana/web3.js";
-import { formatUnits } from "ethers/lib/utils";
-import { useEffect } from "react";
-import { useDispatch, useSelector } from "react-redux";
-import { useEthereumProvider } from "../contexts/EthereumProviderContext";
-import { useSolanaWallet } from "../contexts/SolanaWalletContext";
-import { TokenImplementation__factory } from "../ethers-contracts";
-import {
- selectTransferSourceAsset,
- selectTransferSourceChain,
-} from "../store/selectors";
-import { setSourceParsedTokenAccount } from "../store/transferSlice";
-import { CHAIN_ID_ETH, CHAIN_ID_SOLANA, SOLANA_HOST } from "../utils/consts";
-
-function createParsedTokenAccount(
- publicKey: PublicKey | undefined,
- amount: string,
- decimals: number,
- uiAmount: number,
- uiAmountString: string
-) {
- return {
- publicKey: publicKey?.toString(),
- amount,
- decimals,
- uiAmount,
- uiAmountString,
- };
-}
-
-function useGetBalanceEffect() {
- const dispatch = useDispatch();
- const sourceChain = useSelector(selectTransferSourceChain);
- const sourceAsset = useSelector(selectTransferSourceAsset);
- const { wallet } = useSolanaWallet();
- const solPK = wallet?.publicKey;
- const { provider, signerAddress } = useEthereumProvider();
- useEffect(() => {
- // TODO: loading state
- dispatch(setSourceParsedTokenAccount(undefined));
- if (!sourceAsset) {
- return;
- }
- let cancelled = false;
- if (sourceChain === CHAIN_ID_SOLANA && solPK) {
- let mint;
- try {
- mint = new PublicKey(sourceAsset);
- } catch (e) {
- return;
- }
- const connection = new Connection(SOLANA_HOST, "finalized");
- connection
- .getParsedTokenAccountsByOwner(solPK, { mint })
- .then(({ value }) => {
- if (!cancelled) {
- if (value.length) {
- dispatch(
- setSourceParsedTokenAccount(
- createParsedTokenAccount(
- value[0].pubkey,
- value[0].account.data.parsed?.info?.tokenAmount?.amount,
- value[0].account.data.parsed?.info?.tokenAmount?.decimals,
- value[0].account.data.parsed?.info?.tokenAmount?.uiAmount,
- value[0].account.data.parsed?.info?.tokenAmount
- ?.uiAmountString
- )
- )
- );
- } else {
- // TODO: error state
- }
- }
- })
- .catch(() => {
- if (!cancelled) {
- // TODO: error state
- }
- });
- }
- if (sourceChain === CHAIN_ID_ETH && provider && signerAddress) {
- const token = TokenImplementation__factory.connect(sourceAsset, provider);
- token
- .decimals()
- .then((decimals) => {
- token.balanceOf(signerAddress).then((n) => {
- if (!cancelled) {
- dispatch(
- setSourceParsedTokenAccount(
- // TODO: verify accuracy
- createParsedTokenAccount(
- undefined,
- n.toString(),
- decimals,
- Number(formatUnits(n, decimals)),
- formatUnits(n, decimals)
- )
- )
- );
- }
- });
- })
- .catch(() => {
- if (!cancelled) {
- // TODO: error state
- }
- });
- }
- return () => {
- cancelled = true;
- };
- }, [dispatch, sourceChain, sourceAsset, solPK, provider, signerAddress]);
-}
-
-export default useGetBalanceEffect;
+import { Connection, PublicKey } from "@solana/web3.js";
+import { formatUnits } from "ethers/lib/utils";
+import { useEffect } from "react";
+import { useDispatch, useSelector } from "react-redux";
+import { useEthereumProvider } from "../contexts/EthereumProviderContext";
+import { useSolanaWallet } from "../contexts/SolanaWalletContext";
+import { TokenImplementation__factory } from "../ethers-contracts";
+import {
+ selectTransferSourceAsset,
+ selectTransferSourceChain,
+} from "../store/selectors";
+import { setSourceParsedTokenAccount } from "../store/transferSlice";
+import { CHAIN_ID_ETH, CHAIN_ID_SOLANA, SOLANA_HOST } from "../utils/consts";
+
+function createParsedTokenAccount(
+ publicKey: PublicKey | undefined,
+ amount: string,
+ decimals: number,
+ uiAmount: number,
+ uiAmountString: string
+) {
+ return {
+ publicKey: publicKey?.toString(),
+ amount,
+ decimals,
+ uiAmount,
+ uiAmountString,
+ };
+}
+
+function useGetBalanceEffect() {
+ const dispatch = useDispatch();
+ const sourceChain = useSelector(selectTransferSourceChain);
+ const sourceAsset = useSelector(selectTransferSourceAsset);
+ const { wallet } = useSolanaWallet();
+ const solPK = wallet?.publicKey;
+ const { provider, signerAddress } = useEthereumProvider();
+ useEffect(() => {
+ // TODO: loading state
+ dispatch(setSourceParsedTokenAccount(undefined));
+ if (!sourceAsset) {
+ return;
+ }
+ let cancelled = false;
+ if (sourceChain === CHAIN_ID_SOLANA && solPK) {
+ let mint;
+ try {
+ mint = new PublicKey(sourceAsset);
+ } catch (e) {
+ return;
+ }
+ const connection = new Connection(SOLANA_HOST, "finalized");
+ connection
+ .getParsedTokenAccountsByOwner(solPK, { mint })
+ .then(({ value }) => {
+ if (!cancelled) {
+ if (value.length) {
+ dispatch(
+ setSourceParsedTokenAccount(
+ createParsedTokenAccount(
+ value[0].pubkey,
+ value[0].account.data.parsed?.info?.tokenAmount?.amount,
+ value[0].account.data.parsed?.info?.tokenAmount?.decimals,
+ value[0].account.data.parsed?.info?.tokenAmount?.uiAmount,
+ value[0].account.data.parsed?.info?.tokenAmount
+ ?.uiAmountString
+ )
+ )
+ );
+ } else {
+ // TODO: error state
+ }
+ }
+ })
+ .catch(() => {
+ if (!cancelled) {
+ // TODO: error state
+ }
+ });
+ }
+ if (sourceChain === CHAIN_ID_ETH && provider && signerAddress) {
+ const token = TokenImplementation__factory.connect(sourceAsset, provider);
+ token
+ .decimals()
+ .then((decimals) => {
+ token.balanceOf(signerAddress).then((n) => {
+ if (!cancelled) {
+ dispatch(
+ setSourceParsedTokenAccount(
+ // TODO: verify accuracy
+ createParsedTokenAccount(
+ undefined,
+ n.toString(),
+ decimals,
+ Number(formatUnits(n, decimals)),
+ formatUnits(n, decimals)
+ )
+ )
+ );
+ }
+ });
+ })
+ .catch(() => {
+ if (!cancelled) {
+ // TODO: error state
+ }
+ });
+ }
+ return () => {
+ cancelled = true;
+ };
+ }, [dispatch, sourceChain, sourceAsset, solPK, provider, signerAddress]);
+}
+
+export default useGetBalanceEffect;
diff --git a/bridge_ui/src/hooks/useTransferSignedVAA.ts b/bridge_ui/src/hooks/useTransferSignedVAA.ts
index 316ea859..d13b1f76 100644
--- a/bridge_ui/src/hooks/useTransferSignedVAA.ts
+++ b/bridge_ui/src/hooks/useTransferSignedVAA.ts
@@ -1,13 +1,13 @@
-import { useMemo } from "react";
-import { useSelector } from "react-redux";
-import { selectTransferSignedVAAHex } from "../store/selectors";
-import { hexToUint8Array } from "../utils/array";
-
-export default function useTransferSignedVAA() {
- const signedVAAHex = useSelector(selectTransferSignedVAAHex);
- const signedVAA = useMemo(
- () => (signedVAAHex ? hexToUint8Array(signedVAAHex) : undefined),
- [signedVAAHex]
- );
- return signedVAA;
-}
+import { useMemo } from "react";
+import { useSelector } from "react-redux";
+import { selectTransferSignedVAAHex } from "../store/selectors";
+import { hexToUint8Array } from "../utils/array";
+
+export default function useTransferSignedVAA() {
+ const signedVAAHex = useSelector(selectTransferSignedVAAHex);
+ const signedVAA = useMemo(
+ () => (signedVAAHex ? hexToUint8Array(signedVAAHex) : undefined),
+ [signedVAAHex]
+ );
+ return signedVAA;
+}
diff --git a/bridge_ui/src/hooks/useWrappedAsset.ts b/bridge_ui/src/hooks/useWrappedAsset.ts
index 8538a825..b5a1475a 100644
--- a/bridge_ui/src/hooks/useWrappedAsset.ts
+++ b/bridge_ui/src/hooks/useWrappedAsset.ts
@@ -1,74 +1,74 @@
-import { ethers } from "ethers";
-import { useEffect, useState } from "react";
-import { ChainId, CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "../utils/consts";
-import {
- getAttestedAssetEth,
- getAttestedAssetSol,
-} from "../utils/getAttestedAsset";
-export interface WrappedAssetState {
- isLoading: boolean;
- isWrapped: boolean;
- wrappedAsset: string | null;
-}
-
-function useWrappedAsset(
- checkChain: ChainId,
- originChain: ChainId,
- originAsset: string,
- provider: ethers.providers.Web3Provider | undefined
-) {
- const [state, setState] = useState({
- isLoading: false,
- isWrapped: false,
- wrappedAsset: null,
- });
- useEffect(() => {
- let cancelled = false;
- (async () => {
- if (checkChain === CHAIN_ID_ETH && provider) {
- setState({ isLoading: true, isWrapped: false, wrappedAsset: null });
- const asset = await getAttestedAssetEth(
- provider,
- originChain,
- originAsset
- );
- if (!cancelled) {
- setState({
- isLoading: false,
- isWrapped: !!asset && asset !== ethers.constants.AddressZero,
- wrappedAsset: asset,
- });
- }
- } else if (checkChain === CHAIN_ID_SOLANA) {
- setState({ isLoading: true, isWrapped: false, wrappedAsset: null });
- try {
- const asset = await getAttestedAssetSol(originChain, originAsset);
- if (!cancelled) {
- setState({
- isLoading: false,
- isWrapped: !!asset,
- wrappedAsset: asset,
- });
- }
- } catch (e) {
- if (!cancelled) {
- // TODO: warning for this
- setState({
- isLoading: false,
- isWrapped: false,
- wrappedAsset: null,
- });
- }
- }
- } else {
- setState({ isLoading: false, isWrapped: false, wrappedAsset: null });
- }
- })();
- return () => {
- cancelled = true;
- };
- }, [checkChain, originChain, originAsset, provider]);
- return state;
-}
-
-export default useWrappedAsset;
+import { ethers } from "ethers";
+import { useEffect, useState } from "react";
+import { ChainId, CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "../utils/consts";
+import {
+ getAttestedAssetEth,
+ getAttestedAssetSol,
+} from "../utils/getAttestedAsset";
+export interface WrappedAssetState {
+ isLoading: boolean;
+ isWrapped: boolean;
+ wrappedAsset: string | null;
+}
+
+function useWrappedAsset(
+ checkChain: ChainId,
+ originChain: ChainId,
+ originAsset: string,
+ provider: ethers.providers.Web3Provider | undefined
+) {
+ const [state, setState] = useState({
+ isLoading: false,
+ isWrapped: false,
+ wrappedAsset: null,
+ });
+ useEffect(() => {
+ let cancelled = false;
+ (async () => {
+ if (checkChain === CHAIN_ID_ETH && provider) {
+ setState({ isLoading: true, isWrapped: false, wrappedAsset: null });
+ const asset = await getAttestedAssetEth(
+ provider,
+ originChain,
+ originAsset
+ );
+ if (!cancelled) {
+ setState({
+ isLoading: false,
+ isWrapped: !!asset && asset !== ethers.constants.AddressZero,
+ wrappedAsset: asset,
+ });
+ }
+ } else if (checkChain === CHAIN_ID_SOLANA) {
+ setState({ isLoading: true, isWrapped: false, wrappedAsset: null });
+ try {
+ const asset = await getAttestedAssetSol(originChain, originAsset);
+ if (!cancelled) {
+ setState({
+ isLoading: false,
+ isWrapped: !!asset,
+ wrappedAsset: asset,
+ });
+ }
+ } catch (e) {
+ if (!cancelled) {
+ // TODO: warning for this
+ setState({
+ isLoading: false,
+ isWrapped: false,
+ wrappedAsset: null,
+ });
+ }
+ }
+ } else {
+ setState({ isLoading: false, isWrapped: false, wrappedAsset: null });
+ }
+ })();
+ return () => {
+ cancelled = true;
+ };
+ }, [checkChain, originChain, originAsset, provider]);
+ return state;
+}
+
+export default useWrappedAsset;
diff --git a/bridge_ui/src/muiTheme.js b/bridge_ui/src/muiTheme.js
index ad39cdaf..4733bc1a 100644
--- a/bridge_ui/src/muiTheme.js
+++ b/bridge_ui/src/muiTheme.js
@@ -1,31 +1,31 @@
-import { createTheme, responsiveFontSizes } from "@material-ui/core";
-
-export const theme = responsiveFontSizes(
- createTheme({
- palette: {
- type: "dark",
- background: {
- default: "#010114",
- paper: "#010114",
- },
- divider: "#4e4e54",
- primary: {
- main: "rgba(0, 116, 255, 0.8)", // #0074FF
- },
- secondary: {
- main: "rgb(0,239,216,0.8)", // #00EFD8
- },
- error: {
- main: "#FD3503",
- },
- },
- overrides: {
- MuiButton: {
- root: {
- borderRadius: 0,
- textTransform: "none",
- },
- },
- },
- })
-);
+import { createTheme, responsiveFontSizes } from "@material-ui/core";
+
+export const theme = responsiveFontSizes(
+ createTheme({
+ palette: {
+ type: "dark",
+ background: {
+ default: "#010114",
+ paper: "#010114",
+ },
+ divider: "#4e4e54",
+ primary: {
+ main: "rgba(0, 116, 255, 0.8)", // #0074FF
+ },
+ secondary: {
+ main: "rgb(0,239,216,0.8)", // #00EFD8
+ },
+ error: {
+ main: "#FD3503",
+ },
+ },
+ overrides: {
+ MuiButton: {
+ root: {
+ borderRadius: 0,
+ textTransform: "none",
+ },
+ },
+ },
+ })
+);
diff --git a/bridge_ui/src/react-app-env.d.ts b/bridge_ui/src/react-app-env.d.ts
index ece12df6..6431bc5f 100644
--- a/bridge_ui/src/react-app-env.d.ts
+++ b/bridge_ui/src/react-app-env.d.ts
@@ -1 +1 @@
-///
+///
diff --git a/bridge_ui/src/sdk/index.ts b/bridge_ui/src/sdk/index.ts
index 5bf3a802..3403c77b 100644
--- a/bridge_ui/src/sdk/index.ts
+++ b/bridge_ui/src/sdk/index.ts
@@ -1,58 +1,58 @@
-import {
- AccountMeta,
- PublicKey,
- TransactionInstruction,
-} from "@solana/web3.js";
-import {
- GrpcWebImpl,
- PublicrpcClientImpl,
-} from "../proto/publicrpc/v1/publicrpc";
-import { ChainId } from "../utils/consts";
-
-// begin from clients\solana\main.ts
-export function ixFromRust(data: any): TransactionInstruction {
- let keys: Array = data.accounts.map(accountMetaFromRust);
- return new TransactionInstruction({
- programId: new PublicKey(data.program_id),
- data: Buffer.from(data.data),
- keys: keys,
- });
-}
-
-function accountMetaFromRust(meta: any): AccountMeta {
- return {
- pubkey: new PublicKey(meta.pubkey),
- isSigner: meta.is_signer,
- isWritable: meta.is_writable,
- };
-}
-// end from clients\solana\main.ts
-
-export async function getSignedVAA(
- emitterChain: ChainId,
- emitterAddress: string,
- sequence: string
-) {
- const rpc = new GrpcWebImpl("http://localhost:8080", {});
- const api = new PublicrpcClientImpl(rpc);
- // TODO: potential infinite loop, support cancellation?
- let result;
- while (!result) {
- console.log("wait 1 second");
- await new Promise((resolve) => setTimeout(resolve, 1000));
- console.log("check for signed vaa", emitterChain, emitterAddress, sequence);
- try {
- result = await api.GetSignedVAA({
- messageId: {
- emitterChain,
- emitterAddress,
- sequence,
- },
- });
- console.log(result);
- } catch (e) {
- console.log(e);
- }
- }
- return result;
-}
+import {
+ AccountMeta,
+ PublicKey,
+ TransactionInstruction,
+} from "@solana/web3.js";
+import {
+ GrpcWebImpl,
+ PublicrpcClientImpl,
+} from "../proto/publicrpc/v1/publicrpc";
+import { ChainId } from "../utils/consts";
+
+// begin from clients\solana\main.ts
+export function ixFromRust(data: any): TransactionInstruction {
+ let keys: Array = data.accounts.map(accountMetaFromRust);
+ return new TransactionInstruction({
+ programId: new PublicKey(data.program_id),
+ data: Buffer.from(data.data),
+ keys: keys,
+ });
+}
+
+function accountMetaFromRust(meta: any): AccountMeta {
+ return {
+ pubkey: new PublicKey(meta.pubkey),
+ isSigner: meta.is_signer,
+ isWritable: meta.is_writable,
+ };
+}
+// end from clients\solana\main.ts
+
+export async function getSignedVAA(
+ emitterChain: ChainId,
+ emitterAddress: string,
+ sequence: string
+) {
+ const rpc = new GrpcWebImpl("http://localhost:8080", {});
+ const api = new PublicrpcClientImpl(rpc);
+ // TODO: potential infinite loop, support cancellation?
+ let result;
+ while (!result) {
+ console.log("wait 1 second");
+ await new Promise((resolve) => setTimeout(resolve, 1000));
+ console.log("check for signed vaa", emitterChain, emitterAddress, sequence);
+ try {
+ result = await api.GetSignedVAA({
+ messageId: {
+ emitterChain,
+ emitterAddress,
+ sequence,
+ },
+ });
+ console.log(result);
+ } catch (e) {
+ console.log(e);
+ }
+ }
+ return result;
+}
diff --git a/bridge_ui/src/store/attestSlice.ts b/bridge_ui/src/store/attestSlice.ts
index c7642d4a..6d7588b4 100644
--- a/bridge_ui/src/store/attestSlice.ts
+++ b/bridge_ui/src/store/attestSlice.ts
@@ -1,105 +1,105 @@
-import { createSlice, PayloadAction } from "@reduxjs/toolkit";
-import {
- ChainId,
- CHAIN_ID_ETH,
- CHAIN_ID_SOLANA,
- ETH_TEST_TOKEN_ADDRESS,
- SOL_TEST_TOKEN_ADDRESS,
-} from "../utils/consts";
-
-const LAST_STEP = 3;
-
-type Steps = 0 | 1 | 2 | 3;
-
-export interface AttestState {
- activeStep: Steps;
- sourceChain: ChainId;
- sourceAsset: string;
- targetChain: ChainId;
- signedVAAHex: string | undefined;
- isSending: boolean;
- isCreating: boolean;
-}
-
-const initialState: AttestState = {
- activeStep: 0,
- sourceChain: CHAIN_ID_SOLANA,
- sourceAsset: SOL_TEST_TOKEN_ADDRESS,
- targetChain: CHAIN_ID_ETH,
- signedVAAHex: undefined,
- isSending: false,
- isCreating: false,
-};
-
-export const attestSlice = createSlice({
- name: "attest",
- initialState,
- reducers: {
- incrementStep: (state) => {
- if (state.activeStep < LAST_STEP) state.activeStep++;
- },
- decrementStep: (state) => {
- if (state.activeStep > 0) state.activeStep--;
- },
- setStep: (state, action: PayloadAction) => {
- state.activeStep = action.payload;
- },
- setSourceChain: (state, action: PayloadAction) => {
- const prevSourceChain = state.sourceChain;
- state.sourceChain = action.payload;
- // TODO: remove or check env - for testing purposes
- if (action.payload === CHAIN_ID_ETH) {
- state.sourceAsset = ETH_TEST_TOKEN_ADDRESS;
- }
- if (action.payload === CHAIN_ID_SOLANA) {
- state.sourceAsset = SOL_TEST_TOKEN_ADDRESS;
- }
- if (state.targetChain === action.payload) {
- state.targetChain = prevSourceChain;
- }
- },
- setSourceAsset: (state, action: PayloadAction) => {
- state.sourceAsset = action.payload;
- },
- setTargetChain: (state, action: PayloadAction) => {
- const prevTargetChain = state.targetChain;
- state.targetChain = action.payload;
- if (state.sourceChain === action.payload) {
- state.sourceChain = prevTargetChain;
- state.activeStep = 0;
- // TODO: remove or check env - for testing purposes
- if (state.targetChain === CHAIN_ID_ETH) {
- state.sourceAsset = ETH_TEST_TOKEN_ADDRESS;
- }
- if (state.targetChain === CHAIN_ID_SOLANA) {
- state.sourceAsset = SOL_TEST_TOKEN_ADDRESS;
- }
- }
- },
- setSignedVAAHex: (state, action: PayloadAction) => {
- state.signedVAAHex = action.payload;
- state.isSending = false;
- state.activeStep = 3;
- },
- setIsSending: (state, action: PayloadAction) => {
- state.isSending = action.payload;
- },
- setIsCreating: (state, action: PayloadAction) => {
- state.isCreating = action.payload;
- },
- },
-});
-
-export const {
- incrementStep,
- decrementStep,
- setStep,
- setSourceChain,
- setSourceAsset,
- setTargetChain,
- setSignedVAAHex,
- setIsSending,
- setIsCreating,
-} = attestSlice.actions;
-
-export default attestSlice.reducer;
+import { createSlice, PayloadAction } from "@reduxjs/toolkit";
+import {
+ ChainId,
+ CHAIN_ID_ETH,
+ CHAIN_ID_SOLANA,
+ ETH_TEST_TOKEN_ADDRESS,
+ SOL_TEST_TOKEN_ADDRESS,
+} from "../utils/consts";
+
+const LAST_STEP = 3;
+
+type Steps = 0 | 1 | 2 | 3;
+
+export interface AttestState {
+ activeStep: Steps;
+ sourceChain: ChainId;
+ sourceAsset: string;
+ targetChain: ChainId;
+ signedVAAHex: string | undefined;
+ isSending: boolean;
+ isCreating: boolean;
+}
+
+const initialState: AttestState = {
+ activeStep: 0,
+ sourceChain: CHAIN_ID_SOLANA,
+ sourceAsset: SOL_TEST_TOKEN_ADDRESS,
+ targetChain: CHAIN_ID_ETH,
+ signedVAAHex: undefined,
+ isSending: false,
+ isCreating: false,
+};
+
+export const attestSlice = createSlice({
+ name: "attest",
+ initialState,
+ reducers: {
+ incrementStep: (state) => {
+ if (state.activeStep < LAST_STEP) state.activeStep++;
+ },
+ decrementStep: (state) => {
+ if (state.activeStep > 0) state.activeStep--;
+ },
+ setStep: (state, action: PayloadAction) => {
+ state.activeStep = action.payload;
+ },
+ setSourceChain: (state, action: PayloadAction) => {
+ const prevSourceChain = state.sourceChain;
+ state.sourceChain = action.payload;
+ // TODO: remove or check env - for testing purposes
+ if (action.payload === CHAIN_ID_ETH) {
+ state.sourceAsset = ETH_TEST_TOKEN_ADDRESS;
+ }
+ if (action.payload === CHAIN_ID_SOLANA) {
+ state.sourceAsset = SOL_TEST_TOKEN_ADDRESS;
+ }
+ if (state.targetChain === action.payload) {
+ state.targetChain = prevSourceChain;
+ }
+ },
+ setSourceAsset: (state, action: PayloadAction) => {
+ state.sourceAsset = action.payload;
+ },
+ setTargetChain: (state, action: PayloadAction) => {
+ const prevTargetChain = state.targetChain;
+ state.targetChain = action.payload;
+ if (state.sourceChain === action.payload) {
+ state.sourceChain = prevTargetChain;
+ state.activeStep = 0;
+ // TODO: remove or check env - for testing purposes
+ if (state.targetChain === CHAIN_ID_ETH) {
+ state.sourceAsset = ETH_TEST_TOKEN_ADDRESS;
+ }
+ if (state.targetChain === CHAIN_ID_SOLANA) {
+ state.sourceAsset = SOL_TEST_TOKEN_ADDRESS;
+ }
+ }
+ },
+ setSignedVAAHex: (state, action: PayloadAction) => {
+ state.signedVAAHex = action.payload;
+ state.isSending = false;
+ state.activeStep = 3;
+ },
+ setIsSending: (state, action: PayloadAction) => {
+ state.isSending = action.payload;
+ },
+ setIsCreating: (state, action: PayloadAction) => {
+ state.isCreating = action.payload;
+ },
+ },
+});
+
+export const {
+ incrementStep,
+ decrementStep,
+ setStep,
+ setSourceChain,
+ setSourceAsset,
+ setTargetChain,
+ setSignedVAAHex,
+ setIsSending,
+ setIsCreating,
+} = attestSlice.actions;
+
+export default attestSlice.reducer;
diff --git a/bridge_ui/src/store/index.ts b/bridge_ui/src/store/index.ts
index ebd851f5..d5ff4ac0 100644
--- a/bridge_ui/src/store/index.ts
+++ b/bridge_ui/src/store/index.ts
@@ -1,15 +1,15 @@
-import { configureStore } from "@reduxjs/toolkit";
-import attestReducer from "./attestSlice";
-import transferReducer from "./transferSlice";
-
-export const store = configureStore({
- reducer: {
- attest: attestReducer,
- transfer: transferReducer,
- },
-});
-
-// Infer the `RootState` and `AppDispatch` types from the store itself
-export type RootState = ReturnType;
-// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
-export type AppDispatch = typeof store.dispatch;
+import { configureStore } from "@reduxjs/toolkit";
+import attestReducer from "./attestSlice";
+import transferReducer from "./transferSlice";
+
+export const store = configureStore({
+ reducer: {
+ attest: attestReducer,
+ transfer: transferReducer,
+ },
+});
+
+// Infer the `RootState` and `AppDispatch` types from the store itself
+export type RootState = ReturnType;
+// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
+export type AppDispatch = typeof store.dispatch;
diff --git a/bridge_ui/src/store/selectors.ts b/bridge_ui/src/store/selectors.ts
index fa4813a1..8bc9b3ec 100644
--- a/bridge_ui/src/store/selectors.ts
+++ b/bridge_ui/src/store/selectors.ts
@@ -1,86 +1,86 @@
-import { parseUnits } from "ethers/lib/utils";
-import { RootState } from ".";
-import { CHAIN_ID_SOLANA } from "../utils/consts";
-
-/*
- * Attest
- */
-
-export const selectAttestActiveStep = (state: RootState) =>
- state.attest.activeStep;
-export const selectAttestSourceChain = (state: RootState) =>
- state.attest.sourceChain;
-export const selectAttestSourceAsset = (state: RootState) =>
- state.attest.sourceAsset;
-export const selectAttestTargetChain = (state: RootState) =>
- state.attest.targetChain;
-export const selectAttestSignedVAAHex = (state: RootState) =>
- state.attest.signedVAAHex;
-export const selectAttestIsSending = (state: RootState) =>
- state.attest.isSending;
-export const selectAttestIsCreating = (state: RootState) =>
- state.attest.isCreating;
-
-// safety checks
-// TODO: could make this return a string with a user informative message
-export const selectAttestIsSourceComplete = (state: RootState) =>
- !!state.attest.sourceChain && !!state.attest.sourceAsset;
-// TODO: check wrapped asset exists or is native attest
-export const selectAttestIsTargetComplete = (state: RootState) =>
- selectAttestIsSourceComplete(state) && !!state.attest.targetChain;
-export const selectAttestIsSendComplete = (state: RootState) =>
- !!selectAttestSignedVAAHex(state);
-export const selectAttestShouldLockFields = (state: RootState) =>
- selectAttestIsSending(state) || selectAttestIsSendComplete(state);
-
-/*
- * Transfer
- */
-
-export const selectTransferActiveStep = (state: RootState) =>
- state.transfer.activeStep;
-export const selectTransferSourceChain = (state: RootState) =>
- state.transfer.sourceChain;
-export const selectTransferSourceAsset = (state: RootState) =>
- state.transfer.sourceAsset;
-export const selectTransferSourceParsedTokenAccount = (state: RootState) =>
- state.transfer.sourceParsedTokenAccount;
-export const selectTransferSourceBalanceString = (state: RootState) =>
- state.transfer.sourceParsedTokenAccount?.uiAmountString || "";
-export const selectTransferAmount = (state: RootState) => state.transfer.amount;
-export const selectTransferTargetChain = (state: RootState) =>
- state.transfer.targetChain;
-export const selectTransferSignedVAAHex = (state: RootState) =>
- state.transfer.signedVAAHex;
-export const selectTransferIsSending = (state: RootState) =>
- state.transfer.isSending;
-export const selectTransferIsRedeeming = (state: RootState) =>
- state.transfer.isRedeeming;
-
-// safety checks
-// TODO: could make this return a string with a user informative message
-export const selectTransferIsSourceComplete = (state: RootState) =>
- !!state.transfer.sourceChain &&
- !!state.transfer.sourceAsset &&
- !!state.transfer.sourceParsedTokenAccount &&
- !!state.transfer.amount &&
- (state.transfer.sourceChain !== CHAIN_ID_SOLANA ||
- !!state.transfer.sourceParsedTokenAccount.publicKey) &&
- !!state.transfer.sourceParsedTokenAccount.uiAmountString &&
- // TODO: make safe with too many decimals
- parseUnits(
- state.transfer.amount,
- state.transfer.sourceParsedTokenAccount.decimals
- ).lte(
- parseUnits(
- state.transfer.sourceParsedTokenAccount.uiAmountString,
- state.transfer.sourceParsedTokenAccount.decimals
- )
- );
-// TODO: check wrapped asset exists or is native transfer
-export const selectTransferIsTargetComplete = (state: RootState) =>
- selectTransferIsSourceComplete(state) && !!state.transfer.targetChain;
-export const selectTransferIsSendComplete = (state: RootState) =>
- !!selectTransferSignedVAAHex(state);
-export const selectTransferShouldLockFields = (state: RootState) =>
- selectTransferIsSending(state) || selectTransferIsSendComplete(state);
+import { parseUnits } from "ethers/lib/utils";
+import { RootState } from ".";
+import { CHAIN_ID_SOLANA } from "../utils/consts";
+
+/*
+ * Attest
+ */
+
+export const selectAttestActiveStep = (state: RootState) =>
+ state.attest.activeStep;
+export const selectAttestSourceChain = (state: RootState) =>
+ state.attest.sourceChain;
+export const selectAttestSourceAsset = (state: RootState) =>
+ state.attest.sourceAsset;
+export const selectAttestTargetChain = (state: RootState) =>
+ state.attest.targetChain;
+export const selectAttestSignedVAAHex = (state: RootState) =>
+ state.attest.signedVAAHex;
+export const selectAttestIsSending = (state: RootState) =>
+ state.attest.isSending;
+export const selectAttestIsCreating = (state: RootState) =>
+ state.attest.isCreating;
+
+// safety checks
+// TODO: could make this return a string with a user informative message
+export const selectAttestIsSourceComplete = (state: RootState) =>
+ !!state.attest.sourceChain && !!state.attest.sourceAsset;
+// TODO: check wrapped asset exists or is native attest
+export const selectAttestIsTargetComplete = (state: RootState) =>
+ selectAttestIsSourceComplete(state) && !!state.attest.targetChain;
+export const selectAttestIsSendComplete = (state: RootState) =>
+ !!selectAttestSignedVAAHex(state);
+export const selectAttestShouldLockFields = (state: RootState) =>
+ selectAttestIsSending(state) || selectAttestIsSendComplete(state);
+
+/*
+ * Transfer
+ */
+
+export const selectTransferActiveStep = (state: RootState) =>
+ state.transfer.activeStep;
+export const selectTransferSourceChain = (state: RootState) =>
+ state.transfer.sourceChain;
+export const selectTransferSourceAsset = (state: RootState) =>
+ state.transfer.sourceAsset;
+export const selectTransferSourceParsedTokenAccount = (state: RootState) =>
+ state.transfer.sourceParsedTokenAccount;
+export const selectTransferSourceBalanceString = (state: RootState) =>
+ state.transfer.sourceParsedTokenAccount?.uiAmountString || "";
+export const selectTransferAmount = (state: RootState) => state.transfer.amount;
+export const selectTransferTargetChain = (state: RootState) =>
+ state.transfer.targetChain;
+export const selectTransferSignedVAAHex = (state: RootState) =>
+ state.transfer.signedVAAHex;
+export const selectTransferIsSending = (state: RootState) =>
+ state.transfer.isSending;
+export const selectTransferIsRedeeming = (state: RootState) =>
+ state.transfer.isRedeeming;
+
+// safety checks
+// TODO: could make this return a string with a user informative message
+export const selectTransferIsSourceComplete = (state: RootState) =>
+ !!state.transfer.sourceChain &&
+ !!state.transfer.sourceAsset &&
+ !!state.transfer.sourceParsedTokenAccount &&
+ !!state.transfer.amount &&
+ (state.transfer.sourceChain !== CHAIN_ID_SOLANA ||
+ !!state.transfer.sourceParsedTokenAccount.publicKey) &&
+ !!state.transfer.sourceParsedTokenAccount.uiAmountString &&
+ // TODO: make safe with too many decimals
+ parseUnits(
+ state.transfer.amount,
+ state.transfer.sourceParsedTokenAccount.decimals
+ ).lte(
+ parseUnits(
+ state.transfer.sourceParsedTokenAccount.uiAmountString,
+ state.transfer.sourceParsedTokenAccount.decimals
+ )
+ );
+// TODO: check wrapped asset exists or is native transfer
+export const selectTransferIsTargetComplete = (state: RootState) =>
+ selectTransferIsSourceComplete(state) && !!state.transfer.targetChain;
+export const selectTransferIsSendComplete = (state: RootState) =>
+ !!selectTransferSignedVAAHex(state);
+export const selectTransferShouldLockFields = (state: RootState) =>
+ selectTransferIsSending(state) || selectTransferIsSendComplete(state);
diff --git a/bridge_ui/src/store/transferSlice.ts b/bridge_ui/src/store/transferSlice.ts
index 8462466f..5e3b0126 100644
--- a/bridge_ui/src/store/transferSlice.ts
+++ b/bridge_ui/src/store/transferSlice.ts
@@ -1,128 +1,128 @@
-import { createSlice, PayloadAction } from "@reduxjs/toolkit";
-import {
- ChainId,
- CHAIN_ID_ETH,
- CHAIN_ID_SOLANA,
- ETH_TEST_TOKEN_ADDRESS,
- SOL_TEST_TOKEN_ADDRESS,
-} from "../utils/consts";
-
-const LAST_STEP = 3;
-
-type Steps = 0 | 1 | 2 | 3;
-
-export interface ParsedTokenAccount {
- publicKey: string | undefined;
- amount: string;
- decimals: number;
- uiAmount: number;
- uiAmountString: string;
-}
-
-export interface TransferState {
- activeStep: Steps;
- sourceChain: ChainId;
- sourceAsset: string;
- sourceParsedTokenAccount: ParsedTokenAccount | undefined;
- amount: string;
- targetChain: ChainId;
- signedVAAHex: string | undefined;
- isSending: boolean;
- isRedeeming: boolean;
-}
-
-const initialState: TransferState = {
- activeStep: 0,
- sourceChain: CHAIN_ID_SOLANA,
- sourceAsset: SOL_TEST_TOKEN_ADDRESS,
- sourceParsedTokenAccount: undefined,
- amount: "",
- targetChain: CHAIN_ID_ETH,
- signedVAAHex: undefined,
- isSending: false,
- isRedeeming: false,
-};
-
-export const transferSlice = createSlice({
- name: "transfer",
- initialState,
- reducers: {
- incrementStep: (state) => {
- if (state.activeStep < LAST_STEP) state.activeStep++;
- },
- decrementStep: (state) => {
- if (state.activeStep > 0) state.activeStep--;
- },
- setStep: (state, action: PayloadAction) => {
- state.activeStep = action.payload;
- },
- setSourceChain: (state, action: PayloadAction) => {
- const prevSourceChain = state.sourceChain;
- state.sourceChain = action.payload;
- // TODO: remove or check env - for testing purposes
- if (action.payload === CHAIN_ID_ETH) {
- state.sourceAsset = ETH_TEST_TOKEN_ADDRESS;
- }
- if (action.payload === CHAIN_ID_SOLANA) {
- state.sourceAsset = SOL_TEST_TOKEN_ADDRESS;
- }
- if (state.targetChain === action.payload) {
- state.targetChain = prevSourceChain;
- }
- },
- setSourceAsset: (state, action: PayloadAction) => {
- state.sourceAsset = action.payload;
- },
- setSourceParsedTokenAccount: (
- state,
- action: PayloadAction
- ) => {
- state.sourceParsedTokenAccount = action.payload;
- },
- setAmount: (state, action: PayloadAction) => {
- state.amount = action.payload;
- },
- setTargetChain: (state, action: PayloadAction) => {
- const prevTargetChain = state.targetChain;
- state.targetChain = action.payload;
- if (state.sourceChain === action.payload) {
- state.sourceChain = prevTargetChain;
- state.activeStep = 0;
- // TODO: remove or check env - for testing purposes
- if (state.targetChain === CHAIN_ID_ETH) {
- state.sourceAsset = ETH_TEST_TOKEN_ADDRESS;
- }
- if (state.targetChain === CHAIN_ID_SOLANA) {
- state.sourceAsset = SOL_TEST_TOKEN_ADDRESS;
- }
- }
- },
- setSignedVAAHex: (state, action: PayloadAction) => {
- state.signedVAAHex = action.payload;
- state.isSending = false;
- state.activeStep = 3;
- },
- setIsSending: (state, action: PayloadAction) => {
- state.isSending = action.payload;
- },
- setIsRedeeming: (state, action: PayloadAction) => {
- state.isRedeeming = action.payload;
- },
- },
-});
-
-export const {
- incrementStep,
- decrementStep,
- setStep,
- setSourceChain,
- setSourceAsset,
- setSourceParsedTokenAccount,
- setAmount,
- setTargetChain,
- setSignedVAAHex,
- setIsSending,
- setIsRedeeming,
-} = transferSlice.actions;
-
-export default transferSlice.reducer;
+import { createSlice, PayloadAction } from "@reduxjs/toolkit";
+import {
+ ChainId,
+ CHAIN_ID_ETH,
+ CHAIN_ID_SOLANA,
+ ETH_TEST_TOKEN_ADDRESS,
+ SOL_TEST_TOKEN_ADDRESS,
+} from "../utils/consts";
+
+const LAST_STEP = 3;
+
+type Steps = 0 | 1 | 2 | 3;
+
+export interface ParsedTokenAccount {
+ publicKey: string | undefined;
+ amount: string;
+ decimals: number;
+ uiAmount: number;
+ uiAmountString: string;
+}
+
+export interface TransferState {
+ activeStep: Steps;
+ sourceChain: ChainId;
+ sourceAsset: string;
+ sourceParsedTokenAccount: ParsedTokenAccount | undefined;
+ amount: string;
+ targetChain: ChainId;
+ signedVAAHex: string | undefined;
+ isSending: boolean;
+ isRedeeming: boolean;
+}
+
+const initialState: TransferState = {
+ activeStep: 0,
+ sourceChain: CHAIN_ID_SOLANA,
+ sourceAsset: SOL_TEST_TOKEN_ADDRESS,
+ sourceParsedTokenAccount: undefined,
+ amount: "",
+ targetChain: CHAIN_ID_ETH,
+ signedVAAHex: undefined,
+ isSending: false,
+ isRedeeming: false,
+};
+
+export const transferSlice = createSlice({
+ name: "transfer",
+ initialState,
+ reducers: {
+ incrementStep: (state) => {
+ if (state.activeStep < LAST_STEP) state.activeStep++;
+ },
+ decrementStep: (state) => {
+ if (state.activeStep > 0) state.activeStep--;
+ },
+ setStep: (state, action: PayloadAction) => {
+ state.activeStep = action.payload;
+ },
+ setSourceChain: (state, action: PayloadAction) => {
+ const prevSourceChain = state.sourceChain;
+ state.sourceChain = action.payload;
+ // TODO: remove or check env - for testing purposes
+ if (action.payload === CHAIN_ID_ETH) {
+ state.sourceAsset = ETH_TEST_TOKEN_ADDRESS;
+ }
+ if (action.payload === CHAIN_ID_SOLANA) {
+ state.sourceAsset = SOL_TEST_TOKEN_ADDRESS;
+ }
+ if (state.targetChain === action.payload) {
+ state.targetChain = prevSourceChain;
+ }
+ },
+ setSourceAsset: (state, action: PayloadAction) => {
+ state.sourceAsset = action.payload;
+ },
+ setSourceParsedTokenAccount: (
+ state,
+ action: PayloadAction
+ ) => {
+ state.sourceParsedTokenAccount = action.payload;
+ },
+ setAmount: (state, action: PayloadAction) => {
+ state.amount = action.payload;
+ },
+ setTargetChain: (state, action: PayloadAction) => {
+ const prevTargetChain = state.targetChain;
+ state.targetChain = action.payload;
+ if (state.sourceChain === action.payload) {
+ state.sourceChain = prevTargetChain;
+ state.activeStep = 0;
+ // TODO: remove or check env - for testing purposes
+ if (state.targetChain === CHAIN_ID_ETH) {
+ state.sourceAsset = ETH_TEST_TOKEN_ADDRESS;
+ }
+ if (state.targetChain === CHAIN_ID_SOLANA) {
+ state.sourceAsset = SOL_TEST_TOKEN_ADDRESS;
+ }
+ }
+ },
+ setSignedVAAHex: (state, action: PayloadAction) => {
+ state.signedVAAHex = action.payload;
+ state.isSending = false;
+ state.activeStep = 3;
+ },
+ setIsSending: (state, action: PayloadAction) => {
+ state.isSending = action.payload;
+ },
+ setIsRedeeming: (state, action: PayloadAction) => {
+ state.isRedeeming = action.payload;
+ },
+ },
+});
+
+export const {
+ incrementStep,
+ decrementStep,
+ setStep,
+ setSourceChain,
+ setSourceAsset,
+ setSourceParsedTokenAccount,
+ setAmount,
+ setTargetChain,
+ setSignedVAAHex,
+ setIsSending,
+ setIsRedeeming,
+} = transferSlice.actions;
+
+export default transferSlice.reducer;
diff --git a/bridge_ui/src/utils/array.ts b/bridge_ui/src/utils/array.ts
index 8acaf69b..b311a396 100644
--- a/bridge_ui/src/utils/array.ts
+++ b/bridge_ui/src/utils/array.ts
@@ -1,4 +1,4 @@
-export const uint8ArrayToHex = (a: Uint8Array) =>
- Buffer.from(a).toString("hex");
-export const hexToUint8Array = (h: string) =>
- new Uint8Array(Buffer.from(h, "hex"));
+export const uint8ArrayToHex = (a: Uint8Array) =>
+ Buffer.from(a).toString("hex");
+export const hexToUint8Array = (h: string) =>
+ new Uint8Array(Buffer.from(h, "hex"));
diff --git a/bridge_ui/src/utils/attestFrom.ts b/bridge_ui/src/utils/attestFrom.ts
index 2e5bd38d..261832a0 100644
--- a/bridge_ui/src/utils/attestFrom.ts
+++ b/bridge_ui/src/utils/attestFrom.ts
@@ -1,157 +1,157 @@
-import Wallet from "@project-serum/sol-wallet-adapter";
-import {
- Connection,
- Keypair,
- PublicKey,
- SystemProgram,
- Transaction,
-} from "@solana/web3.js";
-import { ethers } from "ethers";
-import { arrayify, zeroPad } from "ethers/lib/utils";
-import { Bridge__factory, Implementation__factory } from "../ethers-contracts";
-import { getSignedVAA, ixFromRust } from "../sdk";
-import {
- CHAIN_ID_ETH,
- CHAIN_ID_SOLANA,
- ETH_BRIDGE_ADDRESS,
- ETH_TOKEN_BRIDGE_ADDRESS,
- SOLANA_HOST,
- SOL_BRIDGE_ADDRESS,
- SOL_TOKEN_BRIDGE_ADDRESS,
-} from "./consts";
-
-// TODO: allow for / handle cancellation?
-// TODO: overall better input checking and error handling
-export async function attestFromEth(
- provider: ethers.providers.Web3Provider | undefined,
- signer: ethers.Signer | undefined,
- tokenAddress: string
-) {
- if (!provider || !signer) return;
- //TODO: more catches
- const signerAddress = await signer.getAddress();
- console.log("Signer:", signerAddress);
- console.log("Token:", tokenAddress);
- const nonceConst = Math.random() * 100000;
- const nonceBuffer = Buffer.alloc(4);
- nonceBuffer.writeUInt32LE(nonceConst, 0);
- console.log("Initiating attestation");
- console.log("Nonce:", nonceBuffer);
- const bridge = Bridge__factory.connect(ETH_TOKEN_BRIDGE_ADDRESS, signer);
- const v = await bridge.attestToken(tokenAddress, nonceBuffer);
- const receipt = await v.wait();
- // TODO: log parsing should be part of a utility
- // TODO: dangerous!(?)
- const bridgeLog = receipt.logs.filter((l) => {
- console.log(l.address, ETH_BRIDGE_ADDRESS);
- return l.address === ETH_BRIDGE_ADDRESS;
- })[0];
- const {
- args: { sequence },
- } = Implementation__factory.createInterface().parseLog(bridgeLog);
- console.log("SEQ:", sequence);
- const emitterAddress = Buffer.from(
- zeroPad(arrayify(ETH_TOKEN_BRIDGE_ADDRESS), 32)
- ).toString("hex");
- const { vaaBytes } = await getSignedVAA(
- CHAIN_ID_ETH,
- emitterAddress,
- sequence
- );
- console.log("SIGNED VAA:", vaaBytes);
- return vaaBytes;
-}
-
-// TODO: need to check transfer native vs transfer wrapped
-// TODO: switch out targetProvider for generic address (this likely involves getting these in their respective contexts)
-export async function attestFromSolana(
- wallet: Wallet | undefined,
- payerAddress: string | undefined, //TODO: we may not need this since we have wallet
- mintAddress: string
-) {
- if (!wallet || !wallet.publicKey || !payerAddress) return;
- const nonceConst = Math.random() * 100000;
- const nonceBuffer = Buffer.alloc(4);
- nonceBuffer.writeUInt32LE(nonceConst, 0);
- const nonce = nonceBuffer.readUInt32LE(0);
- console.log("program:", SOL_TOKEN_BRIDGE_ADDRESS);
- console.log("bridge:", SOL_BRIDGE_ADDRESS);
- console.log("payer:", payerAddress);
- console.log("token:", mintAddress);
- console.log("nonce:", nonce);
- const bridge = await import("bridge");
- const feeAccount = await bridge.fee_collector_address(SOL_BRIDGE_ADDRESS);
- const bridgeStatePK = new PublicKey(bridge.state_address(SOL_BRIDGE_ADDRESS));
- // TODO: share connection in context?
- const connection = new Connection(SOLANA_HOST, "confirmed");
- const bridgeStateAccountInfo = await connection.getAccountInfo(bridgeStatePK);
- if (bridgeStateAccountInfo?.data === undefined) {
- throw new Error("bridge state not found");
- }
- const bridgeState = bridge.parse_state(
- new Uint8Array(bridgeStateAccountInfo?.data)
- );
- const transferIx = SystemProgram.transfer({
- fromPubkey: new PublicKey(payerAddress),
- toPubkey: new PublicKey(feeAccount),
- lamports: bridgeState.config.fee,
- });
- // TODO: pass in connection
- // Add transfer instruction to transaction
- const { attest_ix, emitter_address } = await import("token-bridge");
- const messageKey = Keypair.generate();
- const ix = ixFromRust(
- attest_ix(
- SOL_TOKEN_BRIDGE_ADDRESS,
- SOL_BRIDGE_ADDRESS,
- payerAddress,
- messageKey.publicKey.toString(),
- mintAddress,
- nonce
- )
- );
- const transaction = new Transaction().add(transferIx, ix);
- const { blockhash } = await connection.getRecentBlockhash();
- transaction.recentBlockhash = blockhash;
- transaction.feePayer = new PublicKey(payerAddress);
- transaction.partialSign(messageKey);
- // Sign transaction, broadcast, and confirm
- const signed = await wallet.signTransaction(transaction);
- console.log("SIGNED", signed);
- const txid = await connection.sendRawTransaction(signed.serialize());
- console.log("SENT", txid);
- const conf = await connection.confirmTransaction(txid);
- console.log("CONFIRMED", conf);
- const info = await connection.getTransaction(txid);
- console.log("INFO", info);
- // TODO: log parsing should be part of a utility
- // TODO: better parsing, safer
- const SEQ_LOG = "Program log: Sequence: ";
- const sequence = info?.meta?.logMessages
- ?.filter((msg) => msg.startsWith(SEQ_LOG))[0]
- .replace(SEQ_LOG, "");
- if (!sequence) {
- throw new Error("sequence not found");
- }
- console.log("SEQ", sequence);
- const emitterAddress = Buffer.from(
- zeroPad(
- new PublicKey(emitter_address(SOL_TOKEN_BRIDGE_ADDRESS)).toBytes(),
- 32
- )
- ).toString("hex");
- const { vaaBytes } = await getSignedVAA(
- CHAIN_ID_SOLANA,
- emitterAddress,
- sequence
- );
- console.log("SIGNED VAA:", vaaBytes);
- return vaaBytes;
-}
-
-const attestFrom = {
- [CHAIN_ID_ETH]: attestFromEth,
- [CHAIN_ID_SOLANA]: attestFromSolana,
-};
-
-export default attestFrom;
+import Wallet from "@project-serum/sol-wallet-adapter";
+import {
+ Connection,
+ Keypair,
+ PublicKey,
+ SystemProgram,
+ Transaction,
+} from "@solana/web3.js";
+import { ethers } from "ethers";
+import { arrayify, zeroPad } from "ethers/lib/utils";
+import { Bridge__factory, Implementation__factory } from "../ethers-contracts";
+import { getSignedVAA, ixFromRust } from "../sdk";
+import {
+ CHAIN_ID_ETH,
+ CHAIN_ID_SOLANA,
+ ETH_BRIDGE_ADDRESS,
+ ETH_TOKEN_BRIDGE_ADDRESS,
+ SOLANA_HOST,
+ SOL_BRIDGE_ADDRESS,
+ SOL_TOKEN_BRIDGE_ADDRESS,
+} from "./consts";
+
+// TODO: allow for / handle cancellation?
+// TODO: overall better input checking and error handling
+export async function attestFromEth(
+ provider: ethers.providers.Web3Provider | undefined,
+ signer: ethers.Signer | undefined,
+ tokenAddress: string
+) {
+ if (!provider || !signer) return;
+ //TODO: more catches
+ const signerAddress = await signer.getAddress();
+ console.log("Signer:", signerAddress);
+ console.log("Token:", tokenAddress);
+ const nonceConst = Math.random() * 100000;
+ const nonceBuffer = Buffer.alloc(4);
+ nonceBuffer.writeUInt32LE(nonceConst, 0);
+ console.log("Initiating attestation");
+ console.log("Nonce:", nonceBuffer);
+ const bridge = Bridge__factory.connect(ETH_TOKEN_BRIDGE_ADDRESS, signer);
+ const v = await bridge.attestToken(tokenAddress, nonceBuffer);
+ const receipt = await v.wait();
+ // TODO: log parsing should be part of a utility
+ // TODO: dangerous!(?)
+ const bridgeLog = receipt.logs.filter((l) => {
+ console.log(l.address, ETH_BRIDGE_ADDRESS);
+ return l.address === ETH_BRIDGE_ADDRESS;
+ })[0];
+ const {
+ args: { sequence },
+ } = Implementation__factory.createInterface().parseLog(bridgeLog);
+ console.log("SEQ:", sequence);
+ const emitterAddress = Buffer.from(
+ zeroPad(arrayify(ETH_TOKEN_BRIDGE_ADDRESS), 32)
+ ).toString("hex");
+ const { vaaBytes } = await getSignedVAA(
+ CHAIN_ID_ETH,
+ emitterAddress,
+ sequence
+ );
+ console.log("SIGNED VAA:", vaaBytes);
+ return vaaBytes;
+}
+
+// TODO: need to check transfer native vs transfer wrapped
+// TODO: switch out targetProvider for generic address (this likely involves getting these in their respective contexts)
+export async function attestFromSolana(
+ wallet: Wallet | undefined,
+ payerAddress: string | undefined, //TODO: we may not need this since we have wallet
+ mintAddress: string
+) {
+ if (!wallet || !wallet.publicKey || !payerAddress) return;
+ const nonceConst = Math.random() * 100000;
+ const nonceBuffer = Buffer.alloc(4);
+ nonceBuffer.writeUInt32LE(nonceConst, 0);
+ const nonce = nonceBuffer.readUInt32LE(0);
+ console.log("program:", SOL_TOKEN_BRIDGE_ADDRESS);
+ console.log("bridge:", SOL_BRIDGE_ADDRESS);
+ console.log("payer:", payerAddress);
+ console.log("token:", mintAddress);
+ console.log("nonce:", nonce);
+ const bridge = await import("bridge");
+ const feeAccount = await bridge.fee_collector_address(SOL_BRIDGE_ADDRESS);
+ const bridgeStatePK = new PublicKey(bridge.state_address(SOL_BRIDGE_ADDRESS));
+ // TODO: share connection in context?
+ const connection = new Connection(SOLANA_HOST, "confirmed");
+ const bridgeStateAccountInfo = await connection.getAccountInfo(bridgeStatePK);
+ if (bridgeStateAccountInfo?.data === undefined) {
+ throw new Error("bridge state not found");
+ }
+ const bridgeState = bridge.parse_state(
+ new Uint8Array(bridgeStateAccountInfo?.data)
+ );
+ const transferIx = SystemProgram.transfer({
+ fromPubkey: new PublicKey(payerAddress),
+ toPubkey: new PublicKey(feeAccount),
+ lamports: bridgeState.config.fee,
+ });
+ // TODO: pass in connection
+ // Add transfer instruction to transaction
+ const { attest_ix, emitter_address } = await import("token-bridge");
+ const messageKey = Keypair.generate();
+ const ix = ixFromRust(
+ attest_ix(
+ SOL_TOKEN_BRIDGE_ADDRESS,
+ SOL_BRIDGE_ADDRESS,
+ payerAddress,
+ messageKey.publicKey.toString(),
+ mintAddress,
+ nonce
+ )
+ );
+ const transaction = new Transaction().add(transferIx, ix);
+ const { blockhash } = await connection.getRecentBlockhash();
+ transaction.recentBlockhash = blockhash;
+ transaction.feePayer = new PublicKey(payerAddress);
+ transaction.partialSign(messageKey);
+ // Sign transaction, broadcast, and confirm
+ const signed = await wallet.signTransaction(transaction);
+ console.log("SIGNED", signed);
+ const txid = await connection.sendRawTransaction(signed.serialize());
+ console.log("SENT", txid);
+ const conf = await connection.confirmTransaction(txid);
+ console.log("CONFIRMED", conf);
+ const info = await connection.getTransaction(txid);
+ console.log("INFO", info);
+ // TODO: log parsing should be part of a utility
+ // TODO: better parsing, safer
+ const SEQ_LOG = "Program log: Sequence: ";
+ const sequence = info?.meta?.logMessages
+ ?.filter((msg) => msg.startsWith(SEQ_LOG))[0]
+ .replace(SEQ_LOG, "");
+ if (!sequence) {
+ throw new Error("sequence not found");
+ }
+ console.log("SEQ", sequence);
+ const emitterAddress = Buffer.from(
+ zeroPad(
+ new PublicKey(emitter_address(SOL_TOKEN_BRIDGE_ADDRESS)).toBytes(),
+ 32
+ )
+ ).toString("hex");
+ const { vaaBytes } = await getSignedVAA(
+ CHAIN_ID_SOLANA,
+ emitterAddress,
+ sequence.toString()
+ );
+ console.log("SIGNED VAA:", vaaBytes);
+ return vaaBytes;
+}
+
+const attestFrom = {
+ [CHAIN_ID_ETH]: attestFromEth,
+ [CHAIN_ID_SOLANA]: attestFromSolana,
+};
+
+export default attestFrom;
diff --git a/bridge_ui/src/utils/consts.ts b/bridge_ui/src/utils/consts.ts
index 8050b0f5..0158343c 100644
--- a/bridge_ui/src/utils/consts.ts
+++ b/bridge_ui/src/utils/consts.ts
@@ -1,49 +1,49 @@
-import { getAddress } from "ethers/lib/utils";
-
-export type ChainId = 1 | 2 | 3 | 4;
-export const CHAIN_ID_SOLANA: ChainId = 1;
-export const CHAIN_ID_ETH: ChainId = 2;
-export const CHAIN_ID_TERRA: ChainId = 3;
-export const CHAIN_ID_BSC: ChainId = 4;
-export interface ChainInfo {
- id: ChainId;
- name: string;
-}
-export const CHAINS = [
- {
- id: CHAIN_ID_BSC,
- name: "Binance Smart Chain",
- },
- {
- id: CHAIN_ID_ETH,
- name: "Ethereum",
- },
- {
- id: CHAIN_ID_SOLANA,
- name: "Solana",
- },
- {
- id: CHAIN_ID_TERRA,
- name: "Terra",
- },
-];
-export type ChainsById = { [key in ChainId]: ChainInfo };
-export const CHAINS_BY_ID: ChainsById = CHAINS.reduce((obj, chain) => {
- obj[chain.id] = chain;
- return obj;
-}, {} as ChainsById);
-export const SOLANA_HOST = "http://localhost:8899";
-export const ETH_TEST_TOKEN_ADDRESS = getAddress(
- "0x0290FB167208Af455bB137780163b7B7a9a10C16"
-);
-export const ETH_BRIDGE_ADDRESS = getAddress(
- "0x254dffcd3277c0b1660f6d42efbb754edababc2b"
-);
-export const ETH_TOKEN_BRIDGE_ADDRESS = getAddress(
- "0xe982e462b094850f12af94d21d470e21be9d0e9c"
-);
-export const SOL_TEST_TOKEN_ADDRESS =
- "2WDq7wSs9zYrpx2kbHDA4RUTRch2CCTP6ZWaH4GNfnQQ";
-export const SOL_BRIDGE_ADDRESS = "Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o";
-export const SOL_TOKEN_BRIDGE_ADDRESS =
- "B6RHG3mfcckmrYN1UhmJzyS1XX3fZKbkeUcpJe9Sy3FE";
+import { getAddress } from "ethers/lib/utils";
+
+export type ChainId = 1 | 2 | 3 | 4;
+export const CHAIN_ID_SOLANA: ChainId = 1;
+export const CHAIN_ID_ETH: ChainId = 2;
+export const CHAIN_ID_TERRA: ChainId = 3;
+export const CHAIN_ID_BSC: ChainId = 4;
+export interface ChainInfo {
+ id: ChainId;
+ name: string;
+}
+export const CHAINS = [
+ {
+ id: CHAIN_ID_BSC,
+ name: "Binance Smart Chain",
+ },
+ {
+ id: CHAIN_ID_ETH,
+ name: "Ethereum",
+ },
+ {
+ id: CHAIN_ID_SOLANA,
+ name: "Solana",
+ },
+ {
+ id: CHAIN_ID_TERRA,
+ name: "Terra",
+ },
+];
+export type ChainsById = { [key in ChainId]: ChainInfo };
+export const CHAINS_BY_ID: ChainsById = CHAINS.reduce((obj, chain) => {
+ obj[chain.id] = chain;
+ return obj;
+}, {} as ChainsById);
+export const SOLANA_HOST = "http://localhost:8899";
+export const ETH_TEST_TOKEN_ADDRESS = getAddress(
+ "0x0290FB167208Af455bB137780163b7B7a9a10C16"
+);
+export const ETH_BRIDGE_ADDRESS = getAddress(
+ "0x254dffcd3277c0b1660f6d42efbb754edababc2b"
+);
+export const ETH_TOKEN_BRIDGE_ADDRESS = getAddress(
+ "0xe982e462b094850f12af94d21d470e21be9d0e9c"
+);
+export const SOL_TEST_TOKEN_ADDRESS =
+ "2WDq7wSs9zYrpx2kbHDA4RUTRch2CCTP6ZWaH4GNfnQQ";
+export const SOL_BRIDGE_ADDRESS = "Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o";
+export const SOL_TOKEN_BRIDGE_ADDRESS =
+ "B6RHG3mfcckmrYN1UhmJzyS1XX3fZKbkeUcpJe9Sy3FE";
diff --git a/bridge_ui/src/utils/createWrappedOn.ts b/bridge_ui/src/utils/createWrappedOn.ts
index 695b0c03..1faadf8a 100644
--- a/bridge_ui/src/utils/createWrappedOn.ts
+++ b/bridge_ui/src/utils/createWrappedOn.ts
@@ -1,52 +1,80 @@
-import Wallet from "@project-serum/sol-wallet-adapter";
-import { Connection, PublicKey, Transaction } from "@solana/web3.js";
-import { ixFromRust } from "../sdk";
-import {
- CHAIN_ID_SOLANA,
- SOLANA_HOST,
- SOL_BRIDGE_ADDRESS,
- SOL_TOKEN_BRIDGE_ADDRESS,
-} from "./consts";
-
-export async function createWrappedOnSolana(
- wallet: Wallet | undefined,
- payerAddress: string | undefined, //TODO: we may not need this since we have wallet
- signedVAA: Uint8Array
-) {
- if (!wallet || !wallet.publicKey || !payerAddress) return;
- console.log("creating wrapped");
- console.log("PROGRAM:", SOL_TOKEN_BRIDGE_ADDRESS);
- console.log("BRIDGE:", SOL_BRIDGE_ADDRESS);
- console.log("PAYER:", payerAddress);
- console.log("VAA:", signedVAA);
- // TODO: share connection in context?
- const connection = new Connection(SOLANA_HOST, "confirmed");
- const { create_wrapped_ix } = await import("token-bridge");
- const ix = ixFromRust(
- create_wrapped_ix(
- SOL_TOKEN_BRIDGE_ADDRESS,
- SOL_BRIDGE_ADDRESS,
- payerAddress,
- signedVAA
- )
- );
- const transaction = new Transaction().add(ix);
- const { blockhash } = await connection.getRecentBlockhash();
- transaction.recentBlockhash = blockhash;
- transaction.feePayer = new PublicKey(payerAddress);
- // Sign transaction, broadcast, and confirm
- const signed = await wallet.signTransaction(transaction);
- console.log("SIGNED", signed);
- const txid = await connection.sendRawTransaction(signed.serialize());
- console.log("SENT", txid);
- const conf = await connection.confirmTransaction(txid);
- console.log("CONFIRMED", conf);
- const info = await connection.getTransaction(txid);
- console.log("INFO", info);
-}
-
-const createWrappedOn = {
- [CHAIN_ID_SOLANA]: createWrappedOnSolana,
-};
-
-export default createWrappedOn;
+import Wallet from "@project-serum/sol-wallet-adapter";
+import { Connection, PublicKey, Transaction } from "@solana/web3.js";
+import { ixFromRust } from "../sdk";
+import {
+ CHAIN_ID_SOLANA,
+ SOLANA_HOST,
+ SOL_BRIDGE_ADDRESS,
+ SOL_TOKEN_BRIDGE_ADDRESS,
+ ETH_TOKEN_BRIDGE_ADDRESS,
+ CHAIN_ID_ETH,
+} from "./consts";
+import { ethers } from "ethers";
+import { Bridge__factory } from "../ethers-contracts";
+import { postVaa } from "./postVaa";
+
+export async function createWrappedOnEth(
+ provider: ethers.providers.Web3Provider | undefined,
+ signer: ethers.Signer | undefined,
+ signedVAA: Uint8Array
+) {
+ console.log(provider, signer, signedVAA);
+ if (!provider || !signer) return;
+ console.log("creating wrapped");
+ const bridge = Bridge__factory.connect(ETH_TOKEN_BRIDGE_ADDRESS, signer);
+ const v = await bridge.createWrapped(signedVAA);
+ const receipt = await v.wait();
+ console.log(receipt);
+}
+
+export async function createWrappedOnSolana(
+ wallet: Wallet | undefined,
+ payerAddress: string | undefined, //TODO: we may not need this since we have wallet
+ signedVAA: Uint8Array
+) {
+ if (!wallet || !wallet.publicKey || !payerAddress) return;
+ console.log("creating wrapped");
+ console.log("PROGRAM:", SOL_TOKEN_BRIDGE_ADDRESS);
+ console.log("BRIDGE:", SOL_BRIDGE_ADDRESS);
+ console.log("PAYER:", payerAddress);
+ console.log("VAA:", signedVAA);
+ // TODO: share connection in context?
+ const connection = new Connection(SOLANA_HOST, "confirmed");
+ const { create_wrapped_ix } = await import("token-bridge");
+
+ await postVaa(
+ connection,
+ wallet,
+ SOL_BRIDGE_ADDRESS,
+ payerAddress,
+ Buffer.from(signedVAA)
+ );
+ const ix = ixFromRust(
+ create_wrapped_ix(
+ SOL_TOKEN_BRIDGE_ADDRESS,
+ SOL_BRIDGE_ADDRESS,
+ payerAddress,
+ signedVAA
+ )
+ );
+ const transaction = new Transaction().add(ix);
+ const { blockhash } = await connection.getRecentBlockhash();
+ transaction.recentBlockhash = blockhash;
+ transaction.feePayer = new PublicKey(payerAddress);
+ // Sign transaction, broadcast, and confirm
+ const signed = await wallet.signTransaction(transaction);
+ console.log("SIGNED", signed);
+ const txid = await connection.sendRawTransaction(signed.serialize());
+ console.log("SENT", txid);
+ const conf = await connection.confirmTransaction(txid);
+ console.log("CONFIRMED", conf);
+ const info = await connection.getTransaction(txid);
+ console.log("INFO", info);
+}
+
+const createWrappedOn = {
+ [CHAIN_ID_SOLANA]: createWrappedOnSolana,
+ [CHAIN_ID_ETH]: createWrappedOnEth,
+};
+
+export default createWrappedOn;
diff --git a/bridge_ui/src/utils/getAttestedAsset.ts b/bridge_ui/src/utils/getAttestedAsset.ts
index 4cb96d52..e8196dad 100644
--- a/bridge_ui/src/utils/getAttestedAsset.ts
+++ b/bridge_ui/src/utils/getAttestedAsset.ts
@@ -1,60 +1,60 @@
-import { Connection, PublicKey } from "@solana/web3.js";
-import { ethers } from "ethers";
-import { arrayify, isHexString, zeroPad } from "ethers/lib/utils";
-import { Bridge__factory } from "../ethers-contracts";
-import {
- ChainId,
- CHAIN_ID_SOLANA,
- ETH_TOKEN_BRIDGE_ADDRESS,
- SOLANA_HOST,
- SOL_TOKEN_BRIDGE_ADDRESS,
-} from "./consts";
-
-export async function getAttestedAssetEth(
- provider: ethers.providers.Web3Provider,
- originChain: ChainId,
- originAsset: string
-) {
- const tokenBridge = Bridge__factory.connect(
- ETH_TOKEN_BRIDGE_ADDRESS,
- provider
- );
- try {
- // TODO: address conversion may be more complex than this
- const originAssetBytes = zeroPad(
- originChain === CHAIN_ID_SOLANA
- ? new PublicKey(originAsset).toBytes()
- : arrayify(originAsset),
- 32
- );
- return await tokenBridge.wrappedAsset(originChain, originAssetBytes);
- } catch (e) {
- return ethers.constants.AddressZero;
- }
-}
-
-export async function getAttestedAssetSol(
- originChain: ChainId,
- originAsset: string
-) {
- if (!isHexString(originAsset)) return null;
- const { wrapped_address } = await import("token-bridge");
- // TODO: address conversion may be more complex than this
- const originAssetBytes = zeroPad(
- arrayify(originAsset, { hexPad: "left" }),
- 32
- );
- const wrappedAddress = wrapped_address(
- SOL_TOKEN_BRIDGE_ADDRESS,
- originAssetBytes,
- originChain
- );
- const wrappedAddressPK = new PublicKey(wrappedAddress);
- // TODO: share connection in context?
- const connection = new Connection(SOLANA_HOST, "confirmed");
- const wrappedAssetAccountInfo = await connection.getAccountInfo(
- wrappedAddressPK
- );
- console.log("WAAI", wrappedAssetAccountInfo);
- return wrappedAssetAccountInfo ? wrappedAddressPK.toString() : null;
-}
+import { Connection, PublicKey } from "@solana/web3.js";
+import { ethers } from "ethers";
+import { arrayify, isHexString, zeroPad } from "ethers/lib/utils";
+import { Bridge__factory } from "../ethers-contracts";
+import {
+ ChainId,
+ CHAIN_ID_SOLANA,
+ ETH_TOKEN_BRIDGE_ADDRESS,
+ SOLANA_HOST,
+ SOL_TOKEN_BRIDGE_ADDRESS,
+} from "./consts";
+
+export async function getAttestedAssetEth(
+ provider: ethers.providers.Web3Provider,
+ originChain: ChainId,
+ originAsset: string
+) {
+ const tokenBridge = Bridge__factory.connect(
+ ETH_TOKEN_BRIDGE_ADDRESS,
+ provider
+ );
+ try {
+ // TODO: address conversion may be more complex than this
+ const originAssetBytes = zeroPad(
+ originChain === CHAIN_ID_SOLANA
+ ? new PublicKey(originAsset).toBytes()
+ : arrayify(originAsset),
+ 32
+ );
+ return await tokenBridge.wrappedAsset(originChain, originAssetBytes);
+ } catch (e) {
+ return ethers.constants.AddressZero;
+ }
+}
+
+export async function getAttestedAssetSol(
+ originChain: ChainId,
+ originAsset: string
+) {
+ if (!isHexString(originAsset)) return null;
+ const { wrapped_address } = await import("token-bridge");
+ // TODO: address conversion may be more complex than this
+ const originAssetBytes = zeroPad(
+ arrayify(originAsset, { hexPad: "left" }),
+ 32
+ );
+ const wrappedAddress = wrapped_address(
+ SOL_TOKEN_BRIDGE_ADDRESS,
+ originAssetBytes,
+ originChain
+ );
+ const wrappedAddressPK = new PublicKey(wrappedAddress);
+ // TODO: share connection in context?
+ const connection = new Connection(SOLANA_HOST, "confirmed");
+ const wrappedAssetAccountInfo = await connection.getAccountInfo(
+ wrappedAddressPK
+ );
+ console.log("WAAI", wrappedAssetAccountInfo);
+ return wrappedAssetAccountInfo ? wrappedAddressPK.toString() : null;
+}
diff --git a/bridge_ui/src/utils/postVaa.ts b/bridge_ui/src/utils/postVaa.ts
new file mode 100644
index 00000000..aec17d49
--- /dev/null
+++ b/bridge_ui/src/utils/postVaa.ts
@@ -0,0 +1,111 @@
+import {
+ Connection,
+ Keypair,
+ PublicKey,
+ Transaction,
+ TransactionInstruction,
+} from "@solana/web3.js";
+import Wallet from "@project-serum/sol-wallet-adapter";
+import { ixFromRust } from "../sdk";
+
+export async function postVaa(
+ connection: Connection,
+ wallet: Wallet,
+ bridge_id: string,
+ payer: string,
+ vaa: Buffer
+) {
+ const {
+ guardian_set_address,
+ parse_guardian_set,
+ verify_signatures_ix,
+ post_vaa_ix,
+ } = await import("bridge");
+ let bridge_state = await getBridgeState(connection, bridge_id);
+ let guardian_addr = new PublicKey(
+ guardian_set_address(bridge_id, bridge_state.guardianSetIndex)
+ );
+ let acc = await connection.getAccountInfo(guardian_addr);
+ if (acc?.data === undefined) {
+ return;
+ }
+ let guardian_data = parse_guardian_set(new Uint8Array(acc?.data));
+
+ let signature_set = Keypair.generate();
+ let txs = verify_signatures_ix(
+ bridge_id,
+ payer,
+ bridge_state.guardianSetIndex,
+ guardian_data,
+ signature_set.publicKey.toString(),
+ vaa
+ );
+ // Add transfer instruction to transaction
+ for (let tx of txs) {
+ let ixs: Array = tx.map((v: any) => {
+ return ixFromRust(v);
+ });
+ let transaction = new Transaction().add(ixs[0], ixs[1]);
+ const { blockhash } = await connection.getRecentBlockhash();
+ transaction.recentBlockhash = blockhash;
+ transaction.feePayer = new PublicKey(payer);
+ transaction.partialSign(signature_set);
+
+ // Sign transaction, broadcast, and confirm
+ const signed = await wallet.signTransaction(transaction);
+ console.log("SIGNED", signed);
+ const txid = await connection.sendRawTransaction(signed.serialize());
+ console.log("SENT", txid);
+ const conf = await connection.confirmTransaction(txid);
+ console.log("CONFIRMED", conf);
+ }
+
+ let ix = ixFromRust(
+ post_vaa_ix(bridge_id, payer, signature_set.publicKey.toString(), vaa)
+ );
+ let transaction = new Transaction().add(ix);
+ const { blockhash } = await connection.getRecentBlockhash();
+ transaction.recentBlockhash = blockhash;
+ transaction.feePayer = new PublicKey(payer);
+
+ const signed = await wallet.signTransaction(transaction);
+ console.log("SIGNED", signed);
+ const txid = await connection.sendRawTransaction(signed.serialize());
+ console.log("SENT", txid);
+ const conf = await connection.confirmTransaction(txid);
+ console.log("CONFIRMED", conf);
+}
+
+async function getBridgeState(
+ connection: Connection,
+ bridge_id: string
+): Promise {
+ const { parse_state, state_address } = await import("bridge");
+ let bridge_state = new PublicKey(state_address(bridge_id));
+ let acc = await connection.getAccountInfo(bridge_state);
+ if (acc?.data === undefined) {
+ throw new Error("bridge state not found");
+ }
+ return parse_state(new Uint8Array(acc?.data));
+}
+
+interface BridgeState {
+ // The current guardian set index, used to decide which signature sets to accept.
+ guardianSetIndex: number;
+
+ // Lamports in the collection account
+ lastLamports: number;
+
+ // Bridge configuration, which is set once upon initialization.
+ config: BridgeConfig;
+}
+
+interface BridgeConfig {
+ // Period for how long a guardian set is valid after it has been replaced by a new one. This
+ // guarantees that VAAs issued by that set can still be submitted for a certain period. In
+ // this period we still trust the old guardian set.
+ guardianSetExpirationTime: number;
+
+ // Amount of lamports that needs to be paid to the protocol to post a message
+ fee: number;
+}
diff --git a/bridge_ui/src/utils/redeemOn.ts b/bridge_ui/src/utils/redeemOn.ts
index 91e4fd62..8a10f988 100644
--- a/bridge_ui/src/utils/redeemOn.ts
+++ b/bridge_ui/src/utils/redeemOn.ts
@@ -1,23 +1,81 @@
-import { ethers } from "ethers";
-import { Bridge__factory } from "../ethers-contracts";
-import { CHAIN_ID_ETH, ETH_TOKEN_BRIDGE_ADDRESS } from "./consts";
-
-export async function redeemOnEth(
- provider: ethers.providers.Web3Provider | undefined,
- signer: ethers.Signer | undefined,
- signedVAA: Uint8Array
-) {
- console.log(provider, signer, signedVAA);
- if (!provider || !signer) return;
- console.log("completing transfer");
- const bridge = Bridge__factory.connect(ETH_TOKEN_BRIDGE_ADDRESS, signer);
- const v = await bridge.completeTransfer(signedVAA);
- const receipt = await v.wait();
- console.log(receipt);
-}
-
-const redeemOn = {
- [CHAIN_ID_ETH]: redeemOnEth,
-};
-
-export default redeemOn;
+import { ethers } from "ethers";
+import { Bridge__factory } from "../ethers-contracts";
+import {
+ CHAIN_ID_ETH,
+ CHAIN_ID_SOLANA,
+ ETH_TOKEN_BRIDGE_ADDRESS,
+ SOL_BRIDGE_ADDRESS,
+ SOL_TOKEN_BRIDGE_ADDRESS,
+ SOLANA_HOST,
+} from "./consts";
+import Wallet from "@project-serum/sol-wallet-adapter";
+import { Connection, PublicKey, Transaction } from "@solana/web3.js";
+import { postVaa } from "./postVaa";
+import { ixFromRust } from "../sdk";
+
+export async function redeemOnEth(
+ provider: ethers.providers.Web3Provider | undefined,
+ signer: ethers.Signer | undefined,
+ signedVAA: Uint8Array
+) {
+ console.log(provider, signer, signedVAA);
+ if (!provider || !signer) return;
+ console.log("completing transfer");
+ const bridge = Bridge__factory.connect(ETH_TOKEN_BRIDGE_ADDRESS, signer);
+ const v = await bridge.completeTransfer(signedVAA);
+ const receipt = await v.wait();
+ console.log(receipt);
+}
+
+export async function redeemOnSolana(
+ wallet: Wallet | undefined,
+ payerAddress: string | undefined, //TODO: we may not need this since we have wallet
+ signedVAA: Uint8Array
+) {
+ if (!wallet || !wallet.publicKey || !payerAddress) return;
+ console.log("completing transfer");
+ console.log("PROGRAM:", SOL_TOKEN_BRIDGE_ADDRESS);
+ console.log("BRIDGE:", SOL_BRIDGE_ADDRESS);
+ console.log("PAYER:", payerAddress);
+ console.log("VAA:", signedVAA);
+ // TODO: share connection in context?
+ const connection = new Connection(SOLANA_HOST, "confirmed");
+ const { complete_transfer_wrapped_ix } = await import("token-bridge");
+
+ await postVaa(
+ connection,
+ wallet,
+ SOL_BRIDGE_ADDRESS,
+ payerAddress,
+ Buffer.from(signedVAA)
+ );
+ console.log(Buffer.from(signedVAA).toString("hex"));
+ const ix = ixFromRust(
+ complete_transfer_wrapped_ix(
+ SOL_TOKEN_BRIDGE_ADDRESS,
+ SOL_BRIDGE_ADDRESS,
+ payerAddress,
+ signedVAA
+ )
+ );
+ const transaction = new Transaction().add(ix);
+ const { blockhash } = await connection.getRecentBlockhash();
+ transaction.recentBlockhash = blockhash;
+ transaction.feePayer = new PublicKey(payerAddress);
+ // Sign transaction, broadcast, and confirm
+ const signed = await wallet.signTransaction(transaction);
+ console.log("SIGNED", signed);
+ const txid = await connection.sendRawTransaction(signed.serialize());
+ console.log("SENT", txid);
+ const conf = await connection.confirmTransaction(txid);
+ console.log("CONFIRMED", conf);
+ const info = await connection.getTransaction(txid);
+ console.log("INFO", info);
+}
+
+const redeemOn = {
+ [CHAIN_ID_ETH]: redeemOnEth,
+ [CHAIN_ID_SOLANA]: redeemOnSolana,
+};
+
+export default redeemOn;
diff --git a/bridge_ui/src/utils/transferFrom.ts b/bridge_ui/src/utils/transferFrom.ts
index 8d4e08d6..a34073da 100644
--- a/bridge_ui/src/utils/transferFrom.ts
+++ b/bridge_ui/src/utils/transferFrom.ts
@@ -1,227 +1,228 @@
-import Wallet from "@project-serum/sol-wallet-adapter";
-import { Token, TOKEN_PROGRAM_ID } from "@solana/spl-token";
-import {
- Connection, Keypair,
- PublicKey,
- SystemProgram,
- Transaction,
-} from "@solana/web3.js";
-import { ethers } from "ethers";
-import { arrayify, formatUnits, parseUnits, zeroPad } from "ethers/lib/utils";
-import {
- Bridge__factory,
- Implementation__factory,
- TokenImplementation__factory,
-} from "../ethers-contracts";
-import { getSignedVAA, ixFromRust } from "../sdk";
-import {
- ChainId,
- CHAIN_ID_ETH,
- CHAIN_ID_SOLANA,
- ETH_BRIDGE_ADDRESS,
- ETH_TOKEN_BRIDGE_ADDRESS,
- SOLANA_HOST,
- SOL_BRIDGE_ADDRESS,
- SOL_TOKEN_BRIDGE_ADDRESS,
-} from "./consts";
-
-// TODO: allow for / handle cancellation?
-// TODO: overall better input checking and error handling
-export async function transferFromEth(
- provider: ethers.providers.Web3Provider | undefined,
- signer: ethers.Signer | undefined,
- tokenAddress: string,
- decimals: number,
- amount: string,
- recipientChain: ChainId,
- recipientAddress: Uint8Array | undefined
-) {
- if (!provider || !signer || !recipientAddress) return;
- //TODO: check if token attestation exists on the target chain
- //TODO: don't hardcode, fetch decimals / share them with balance, how do we determine recipient chain?
- //TODO: more catches
- const amountParsed = parseUnits(amount, decimals);
- const signerAddress = await signer.getAddress();
- console.log("Signer:", signerAddress);
- console.log("Token:", tokenAddress);
- const token = TokenImplementation__factory.connect(tokenAddress, signer);
- const allowance = await token.allowance(
- signerAddress,
- ETH_TOKEN_BRIDGE_ADDRESS
- );
- console.log("Allowance", allowance.toString()); //TODO: should we check that this is zero and warn if it isn't?
- const transaction = await token.approve(
- ETH_TOKEN_BRIDGE_ADDRESS,
- amountParsed
- );
- console.log(transaction);
- const fee = 0; // for now, this won't do anything, we may add later
- const nonceConst = Math.random() * 100000;
- const nonceBuffer = Buffer.alloc(4);
- nonceBuffer.writeUInt32LE(nonceConst, 0);
- console.log("Initiating transfer");
- console.log("Amount:", formatUnits(amountParsed, decimals));
- console.log("To chain:", recipientChain);
- console.log("To address:", recipientAddress);
- console.log("Fees:", fee);
- console.log("Nonce:", nonceBuffer);
- const bridge = Bridge__factory.connect(ETH_TOKEN_BRIDGE_ADDRESS, signer);
- const v = await bridge.transferTokens(
- tokenAddress,
- amountParsed,
- recipientChain,
- recipientAddress,
- fee,
- nonceBuffer
- );
- const receipt = await v.wait();
- // TODO: log parsing should be part of a utility
- // TODO: dangerous!(?)
- const bridgeLog = receipt.logs.filter((l) => {
- console.log(l.address, ETH_BRIDGE_ADDRESS);
- return l.address === ETH_BRIDGE_ADDRESS;
- })[0];
- const {
- args: { sender, sequence },
- } = Implementation__factory.createInterface().parseLog(bridgeLog);
- console.log(sender, sequence);
- const emitterAddress = Buffer.from(
- zeroPad(arrayify(ETH_TOKEN_BRIDGE_ADDRESS), 32)
- ).toString("hex");
- const { vaaBytes } = await getSignedVAA(
- CHAIN_ID_ETH,
- emitterAddress,
- sequence
- );
- console.log("SIGNED VAA:", vaaBytes);
- return vaaBytes;
-}
-
-// TODO: need to check transfer native vs transfer wrapped
-// TODO: switch out targetProvider for generic address (this likely involves getting these in their respective contexts)
-export async function transferFromSolana(
- wallet: Wallet | undefined,
- payerAddress: string | undefined, //TODO: we may not need this since we have wallet
- fromAddress: string | undefined,
- mintAddress: string,
- amount: string,
- decimals: number,
- targetAddressStr: string | undefined,
- targetChain: ChainId
-) {
- if (
- !wallet ||
- !wallet.publicKey ||
- !payerAddress ||
- !fromAddress ||
- !targetAddressStr
- )
- return;
- const targetAddress = zeroPad(arrayify(targetAddressStr), 32);
- const nonceConst = Math.random() * 100000;
- const nonceBuffer = Buffer.alloc(4);
- nonceBuffer.writeUInt32LE(nonceConst, 0);
- const nonce = nonceBuffer.readUInt32LE(0);
- const amountParsed = parseUnits(amount, decimals).toBigInt();
- const fee = BigInt(0); // for now, this won't do anything, we may add later
- console.log("program:", SOL_TOKEN_BRIDGE_ADDRESS);
- console.log("bridge:", SOL_BRIDGE_ADDRESS);
- console.log("payer:", payerAddress);
- console.log("from:", fromAddress);
- console.log("token:", mintAddress);
- console.log("nonce:", nonce);
- console.log("amount:", amountParsed);
- console.log("fee:", fee);
- console.log("target:", targetAddressStr, targetAddress);
- console.log("chain:", targetChain);
- const bridge = await import("bridge");
- const feeAccount = await bridge.fee_collector_address(SOL_BRIDGE_ADDRESS);
- const bridgeStatePK = new PublicKey(bridge.state_address(SOL_BRIDGE_ADDRESS));
- // TODO: share connection in context?
- const connection = new Connection(SOLANA_HOST, "confirmed");
- const bridgeStateAccountInfo = await connection.getAccountInfo(bridgeStatePK);
- if (bridgeStateAccountInfo?.data === undefined) {
- throw new Error("bridge state not found");
- }
- const bridgeState = bridge.parse_state(
- new Uint8Array(bridgeStateAccountInfo?.data)
- );
- const transferIx = SystemProgram.transfer({
- fromPubkey: new PublicKey(payerAddress),
- toPubkey: new PublicKey(feeAccount),
- lamports: bridgeState.config.fee,
- });
- // TODO: pass in connection
- // Add transfer instruction to transaction
- const { transfer_native_ix, approval_authority_address, emitter_address } =
- await import("token-bridge");
- const approvalIx = Token.createApproveInstruction(
- TOKEN_PROGRAM_ID,
- new PublicKey(fromAddress),
- new PublicKey(approval_authority_address(SOL_TOKEN_BRIDGE_ADDRESS)),
- new PublicKey(payerAddress),
- [],
- Number(amountParsed)
- );
-
- let messageKey = Keypair.generate();
- const ix = ixFromRust(
- transfer_native_ix(
- SOL_TOKEN_BRIDGE_ADDRESS,
- SOL_BRIDGE_ADDRESS,
- payerAddress,
- messageKey.publicKey.toString(),
- fromAddress,
- mintAddress,
- nonce,
- amountParsed,
- fee,
- targetAddress,
- targetChain
- )
- );
- const transaction = new Transaction().add(transferIx, approvalIx, ix);
- const { blockhash } = await connection.getRecentBlockhash();
- transaction.recentBlockhash = blockhash;
- transaction.feePayer = new PublicKey(payerAddress);
- transaction.partialSign(messageKey);
- // Sign transaction, broadcast, and confirm
- const signed = await wallet.signTransaction(transaction);
- console.log("SIGNED", signed);
- const txid = await connection.sendRawTransaction(signed.serialize());
- console.log("SENT", txid);
- const conf = await connection.confirmTransaction(txid);
- console.log("CONFIRMED", conf);
- const info = await connection.getTransaction(txid);
- console.log("INFO", info);
- // TODO: log parsing should be part of a utility
- // TODO: better parsing, safer
- const SEQ_LOG = "Program log: Sequence: ";
- const sequence = info?.meta?.logMessages
- ?.filter((msg) => msg.startsWith(SEQ_LOG))[0]
- .replace(SEQ_LOG, "");
- if (!sequence) {
- throw new Error("sequence not found");
- }
- console.log("SEQ", sequence);
- const emitterAddress = Buffer.from(
- zeroPad(
- new PublicKey(emitter_address(SOL_TOKEN_BRIDGE_ADDRESS)).toBytes(),
- 32
- )
- ).toString("hex");
- const { vaaBytes } = await getSignedVAA(
- CHAIN_ID_SOLANA,
- emitterAddress,
- sequence
- );
- console.log("SIGNED VAA:", vaaBytes);
- return vaaBytes;
-}
-
-const transferFrom = {
- [CHAIN_ID_ETH]: transferFromEth,
- [CHAIN_ID_SOLANA]: transferFromSolana,
-};
-
-export default transferFrom;
+import Wallet from "@project-serum/sol-wallet-adapter";
+import { Token, TOKEN_PROGRAM_ID } from "@solana/spl-token";
+import {
+ Connection,
+ Keypair,
+ PublicKey,
+ SystemProgram,
+ Transaction,
+} from "@solana/web3.js";
+import { ethers } from "ethers";
+import { arrayify, formatUnits, parseUnits, zeroPad } from "ethers/lib/utils";
+import {
+ Bridge__factory,
+ Implementation__factory,
+ TokenImplementation__factory,
+} from "../ethers-contracts";
+import { getSignedVAA, ixFromRust } from "../sdk";
+import {
+ ChainId,
+ CHAIN_ID_ETH,
+ CHAIN_ID_SOLANA,
+ ETH_BRIDGE_ADDRESS,
+ ETH_TOKEN_BRIDGE_ADDRESS,
+ SOLANA_HOST,
+ SOL_BRIDGE_ADDRESS,
+ SOL_TOKEN_BRIDGE_ADDRESS,
+} from "./consts";
+
+// TODO: allow for / handle cancellation?
+// TODO: overall better input checking and error handling
+export async function transferFromEth(
+ provider: ethers.providers.Web3Provider | undefined,
+ signer: ethers.Signer | undefined,
+ tokenAddress: string,
+ decimals: number,
+ amount: string,
+ recipientChain: ChainId,
+ recipientAddress: Uint8Array | undefined
+) {
+ if (!provider || !signer || !recipientAddress) return;
+ //TODO: check if token attestation exists on the target chain
+ //TODO: don't hardcode, fetch decimals / share them with balance, how do we determine recipient chain?
+ //TODO: more catches
+ const amountParsed = parseUnits(amount, decimals);
+ const signerAddress = await signer.getAddress();
+ console.log("Signer:", signerAddress);
+ console.log("Token:", tokenAddress);
+ const token = TokenImplementation__factory.connect(tokenAddress, signer);
+ const allowance = await token.allowance(
+ signerAddress,
+ ETH_TOKEN_BRIDGE_ADDRESS
+ );
+ console.log("Allowance", allowance.toString()); //TODO: should we check that this is zero and warn if it isn't?
+ const transaction = await token.approve(
+ ETH_TOKEN_BRIDGE_ADDRESS,
+ amountParsed
+ );
+ console.log(transaction);
+ const fee = 0; // for now, this won't do anything, we may add later
+ const nonceConst = Math.random() * 100000;
+ const nonceBuffer = Buffer.alloc(4);
+ nonceBuffer.writeUInt32LE(nonceConst, 0);
+ console.log("Initiating transfer");
+ console.log("Amount:", formatUnits(amountParsed, decimals));
+ console.log("To chain:", recipientChain);
+ console.log("To address:", recipientAddress);
+ console.log("Fees:", fee);
+ console.log("Nonce:", nonceBuffer);
+ const bridge = Bridge__factory.connect(ETH_TOKEN_BRIDGE_ADDRESS, signer);
+ const v = await bridge.transferTokens(
+ tokenAddress,
+ amountParsed,
+ recipientChain,
+ recipientAddress,
+ fee,
+ nonceBuffer
+ );
+ const receipt = await v.wait();
+ // TODO: log parsing should be part of a utility
+ // TODO: dangerous!(?)
+ const bridgeLog = receipt.logs.filter((l) => {
+ console.log(l.address, ETH_BRIDGE_ADDRESS);
+ return l.address === ETH_BRIDGE_ADDRESS;
+ })[0];
+ const {
+ args: { sender, sequence },
+ } = Implementation__factory.createInterface().parseLog(bridgeLog);
+ console.log(sender, sequence);
+ const emitterAddress = Buffer.from(
+ zeroPad(arrayify(ETH_TOKEN_BRIDGE_ADDRESS), 32)
+ ).toString("hex");
+ const { vaaBytes } = await getSignedVAA(
+ CHAIN_ID_ETH,
+ emitterAddress,
+ sequence.toString()
+ );
+ console.log("SIGNED VAA:", vaaBytes);
+ return vaaBytes;
+}
+
+// TODO: need to check transfer native vs transfer wrapped
+// TODO: switch out targetProvider for generic address (this likely involves getting these in their respective contexts)
+export async function transferFromSolana(
+ wallet: Wallet | undefined,
+ payerAddress: string | undefined, //TODO: we may not need this since we have wallet
+ fromAddress: string | undefined,
+ mintAddress: string,
+ amount: string,
+ decimals: number,
+ targetAddressStr: string | undefined,
+ targetChain: ChainId
+) {
+ if (
+ !wallet ||
+ !wallet.publicKey ||
+ !payerAddress ||
+ !fromAddress ||
+ !targetAddressStr
+ )
+ return;
+ const targetAddress = zeroPad(arrayify(targetAddressStr), 32);
+ const nonceConst = Math.random() * 100000;
+ const nonceBuffer = Buffer.alloc(4);
+ nonceBuffer.writeUInt32LE(nonceConst, 0);
+ const nonce = nonceBuffer.readUInt32LE(0);
+ const amountParsed = parseUnits(amount, decimals).toBigInt();
+ const fee = BigInt(0); // for now, this won't do anything, we may add later
+ console.log("program:", SOL_TOKEN_BRIDGE_ADDRESS);
+ console.log("bridge:", SOL_BRIDGE_ADDRESS);
+ console.log("payer:", payerAddress);
+ console.log("from:", fromAddress);
+ console.log("token:", mintAddress);
+ console.log("nonce:", nonce);
+ console.log("amount:", amountParsed);
+ console.log("fee:", fee);
+ console.log("target:", targetAddressStr, targetAddress);
+ console.log("chain:", targetChain);
+ const bridge = await import("bridge");
+ const feeAccount = await bridge.fee_collector_address(SOL_BRIDGE_ADDRESS);
+ const bridgeStatePK = new PublicKey(bridge.state_address(SOL_BRIDGE_ADDRESS));
+ // TODO: share connection in context?
+ const connection = new Connection(SOLANA_HOST, "confirmed");
+ const bridgeStateAccountInfo = await connection.getAccountInfo(bridgeStatePK);
+ if (bridgeStateAccountInfo?.data === undefined) {
+ throw new Error("bridge state not found");
+ }
+ const bridgeState = bridge.parse_state(
+ new Uint8Array(bridgeStateAccountInfo?.data)
+ );
+ const transferIx = SystemProgram.transfer({
+ fromPubkey: new PublicKey(payerAddress),
+ toPubkey: new PublicKey(feeAccount),
+ lamports: bridgeState.config.fee,
+ });
+ // TODO: pass in connection
+ // Add transfer instruction to transaction
+ const { transfer_native_ix, approval_authority_address, emitter_address } =
+ await import("token-bridge");
+ const approvalIx = Token.createApproveInstruction(
+ TOKEN_PROGRAM_ID,
+ new PublicKey(fromAddress),
+ new PublicKey(approval_authority_address(SOL_TOKEN_BRIDGE_ADDRESS)),
+ new PublicKey(payerAddress),
+ [],
+ Number(amountParsed)
+ );
+
+ let messageKey = Keypair.generate();
+ const ix = ixFromRust(
+ transfer_native_ix(
+ SOL_TOKEN_BRIDGE_ADDRESS,
+ SOL_BRIDGE_ADDRESS,
+ payerAddress,
+ messageKey.publicKey.toString(),
+ fromAddress,
+ mintAddress,
+ nonce,
+ amountParsed,
+ fee,
+ targetAddress,
+ targetChain
+ )
+ );
+ const transaction = new Transaction().add(transferIx, approvalIx, ix);
+ const { blockhash } = await connection.getRecentBlockhash();
+ transaction.recentBlockhash = blockhash;
+ transaction.feePayer = new PublicKey(payerAddress);
+ transaction.partialSign(messageKey);
+ // Sign transaction, broadcast, and confirm
+ const signed = await wallet.signTransaction(transaction);
+ console.log("SIGNED", signed);
+ const txid = await connection.sendRawTransaction(signed.serialize());
+ console.log("SENT", txid);
+ const conf = await connection.confirmTransaction(txid);
+ console.log("CONFIRMED", conf);
+ const info = await connection.getTransaction(txid);
+ console.log("INFO", info);
+ // TODO: log parsing should be part of a utility
+ // TODO: better parsing, safer
+ const SEQ_LOG = "Program log: Sequence: ";
+ const sequence = info?.meta?.logMessages
+ ?.filter((msg) => msg.startsWith(SEQ_LOG))[0]
+ .replace(SEQ_LOG, "");
+ if (!sequence) {
+ throw new Error("sequence not found");
+ }
+ console.log("SEQ", sequence);
+ const emitterAddress = Buffer.from(
+ zeroPad(
+ new PublicKey(emitter_address(SOL_TOKEN_BRIDGE_ADDRESS)).toBytes(),
+ 32
+ )
+ ).toString("hex");
+ const { vaaBytes } = await getSignedVAA(
+ CHAIN_ID_SOLANA,
+ emitterAddress,
+ sequence
+ );
+ console.log("SIGNED VAA:", vaaBytes);
+ return vaaBytes;
+}
+
+const transferFrom = {
+ [CHAIN_ID_ETH]: transferFromEth,
+ [CHAIN_ID_SOLANA]: transferFromSolana,
+};
+
+export default transferFrom;