bridge_ui: separate attest workflow
Change-Id: If6270d7ce0deb02a48b63b81ba2ef688c5f4af29
This commit is contained in:
parent
f53d180753
commit
1da690aa42
|
@ -1,4 +1,5 @@
|
||||||
import { AppBar, makeStyles, Toolbar } from "@material-ui/core";
|
import { AppBar, makeStyles, Toolbar } from "@material-ui/core";
|
||||||
|
import Attest from "./components/Attest";
|
||||||
import Transfer from "./components/Transfer";
|
import Transfer from "./components/Transfer";
|
||||||
import wormholeLogo from "./icons/wormhole.svg";
|
import wormholeLogo from "./icons/wormhole.svg";
|
||||||
|
|
||||||
|
@ -40,6 +41,7 @@ function App() {
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
<div className={classes.content}>
|
<div className={classes.content}>
|
||||||
|
<Attest />
|
||||||
<Transfer />
|
<Transfer />
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
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 (
|
||||||
|
<div style={{ position: "relative" }}>
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
variant="contained"
|
||||||
|
className={classes.transferButton}
|
||||||
|
disabled={isCreating}
|
||||||
|
onClick={handleCreateClick}
|
||||||
|
>
|
||||||
|
Create
|
||||||
|
</Button>
|
||||||
|
{isCreating ? (
|
||||||
|
<CircularProgress
|
||||||
|
size={24}
|
||||||
|
color="inherit"
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
bottom: 0,
|
||||||
|
left: "50%",
|
||||||
|
marginLeft: -12,
|
||||||
|
marginBottom: 6,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Create;
|
|
@ -0,0 +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 (
|
||||||
|
<>
|
||||||
|
<div style={{ position: "relative" }}>
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
variant="contained"
|
||||||
|
className={classes.transferButton}
|
||||||
|
onClick={handleAttestClick}
|
||||||
|
disabled={!isTargetComplete || isSending || isSendComplete}
|
||||||
|
>
|
||||||
|
Attest
|
||||||
|
</Button>
|
||||||
|
{isSending ? (
|
||||||
|
<CircularProgress
|
||||||
|
size={24}
|
||||||
|
color="inherit"
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
bottom: 0,
|
||||||
|
left: "50%",
|
||||||
|
marginLeft: -12,
|
||||||
|
marginBottom: 6,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Send;
|
|
@ -0,0 +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 (
|
||||||
|
<>
|
||||||
|
<TextField
|
||||||
|
select
|
||||||
|
fullWidth
|
||||||
|
value={sourceChain}
|
||||||
|
onChange={handleSourceChange}
|
||||||
|
disabled={shouldLockFields}
|
||||||
|
>
|
||||||
|
{CHAINS.map(({ id, name }) => (
|
||||||
|
<MenuItem key={id} value={id}>
|
||||||
|
{name}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</TextField>
|
||||||
|
<KeyAndBalance chainId={sourceChain} />
|
||||||
|
<TextField
|
||||||
|
placeholder="Asset"
|
||||||
|
fullWidth
|
||||||
|
className={classes.transferField}
|
||||||
|
value={sourceAsset}
|
||||||
|
onChange={handleAssetChange}
|
||||||
|
disabled={shouldLockFields}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
disabled={!isSourceComplete}
|
||||||
|
onClick={handleNextClick}
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
Next
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Source;
|
|
@ -0,0 +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 (
|
||||||
|
<>
|
||||||
|
<TextField
|
||||||
|
select
|
||||||
|
fullWidth
|
||||||
|
value={targetChain}
|
||||||
|
onChange={handleTargetChange}
|
||||||
|
disabled={shouldLockFields}
|
||||||
|
>
|
||||||
|
{chains.map(({ id, name }) => (
|
||||||
|
<MenuItem key={id} value={id}>
|
||||||
|
{name}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</TextField>
|
||||||
|
{/* TODO: determine "to" token address */}
|
||||||
|
<KeyAndBalance chainId={targetChain} />
|
||||||
|
<Button
|
||||||
|
disabled={!isTargetComplete}
|
||||||
|
onClick={handleNextClick}
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
Next
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Target;
|
|
@ -0,0 +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 (
|
||||||
|
<Container maxWidth="md">
|
||||||
|
<Stepper activeStep={activeStep} orientation="vertical">
|
||||||
|
<Step>
|
||||||
|
<StepButton onClick={() => dispatch(setStep(0))}>
|
||||||
|
Select a source
|
||||||
|
</StepButton>
|
||||||
|
<StepContent>
|
||||||
|
<Source />
|
||||||
|
</StepContent>
|
||||||
|
</Step>
|
||||||
|
<Step>
|
||||||
|
<StepButton onClick={() => dispatch(setStep(1))}>
|
||||||
|
Select a target
|
||||||
|
</StepButton>
|
||||||
|
<StepContent>
|
||||||
|
<Target />
|
||||||
|
</StepContent>
|
||||||
|
</Step>
|
||||||
|
<Step>
|
||||||
|
<StepButton onClick={() => dispatch(setStep(2))}>
|
||||||
|
Send attestation
|
||||||
|
</StepButton>
|
||||||
|
<StepContent>
|
||||||
|
<Send />
|
||||||
|
</StepContent>
|
||||||
|
</Step>
|
||||||
|
<Step>
|
||||||
|
<StepButton
|
||||||
|
onClick={() => dispatch(setStep(3))}
|
||||||
|
disabled={!signedVAAHex}
|
||||||
|
>
|
||||||
|
Create wrapper
|
||||||
|
</StepButton>
|
||||||
|
<StepContent>
|
||||||
|
<Create />
|
||||||
|
</StepContent>
|
||||||
|
</Step>
|
||||||
|
</Stepper>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Attest;
|
|
@ -3,7 +3,10 @@ import { useCallback } from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { useEthereumProvider } from "../../contexts/EthereumProviderContext";
|
import { useEthereumProvider } from "../../contexts/EthereumProviderContext";
|
||||||
import useTransferSignedVAA from "../../hooks/useTransferSignedVAA";
|
import useTransferSignedVAA from "../../hooks/useTransferSignedVAA";
|
||||||
import { selectIsRedeeming, selectTargetChain } from "../../store/selectors";
|
import {
|
||||||
|
selectTransferIsRedeeming,
|
||||||
|
selectTransferTargetChain,
|
||||||
|
} from "../../store/selectors";
|
||||||
import { setIsRedeeming } from "../../store/transferSlice";
|
import { setIsRedeeming } from "../../store/transferSlice";
|
||||||
import { CHAIN_ID_ETH } from "../../utils/consts";
|
import { CHAIN_ID_ETH } from "../../utils/consts";
|
||||||
import redeemOn, { redeemOnEth } from "../../utils/redeemOn";
|
import redeemOn, { redeemOnEth } from "../../utils/redeemOn";
|
||||||
|
@ -19,10 +22,10 @@ const useStyles = makeStyles((theme) => ({
|
||||||
function Redeem() {
|
function Redeem() {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const targetChain = useSelector(selectTargetChain);
|
const targetChain = useSelector(selectTransferTargetChain);
|
||||||
const { provider, signer } = useEthereumProvider();
|
const { provider, signer } = useEthereumProvider();
|
||||||
const signedVAA = useTransferSignedVAA();
|
const signedVAA = useTransferSignedVAA();
|
||||||
const isRedeeming = useSelector(selectIsRedeeming);
|
const isRedeeming = useSelector(selectTransferIsRedeeming);
|
||||||
const handleRedeemClick = useCallback(() => {
|
const handleRedeemClick = useCallback(() => {
|
||||||
if (
|
if (
|
||||||
targetChain === CHAIN_ID_ETH &&
|
targetChain === CHAIN_ID_ETH &&
|
||||||
|
|
|
@ -5,14 +5,14 @@ import { useEthereumProvider } from "../../contexts/EthereumProviderContext";
|
||||||
import { useSolanaWallet } from "../../contexts/SolanaWalletContext";
|
import { useSolanaWallet } from "../../contexts/SolanaWalletContext";
|
||||||
import useWrappedAsset from "../../hooks/useWrappedAsset";
|
import useWrappedAsset from "../../hooks/useWrappedAsset";
|
||||||
import {
|
import {
|
||||||
selectAmount,
|
selectTransferAmount,
|
||||||
selectIsSendComplete,
|
selectTransferIsSendComplete,
|
||||||
selectIsSending,
|
selectTransferIsSending,
|
||||||
selectIsTargetComplete,
|
selectTransferIsTargetComplete,
|
||||||
selectSourceAsset,
|
selectTransferSourceAsset,
|
||||||
selectSourceChain,
|
selectTransferSourceChain,
|
||||||
selectSourceParsedTokenAccount,
|
selectTransferSourceParsedTokenAccount,
|
||||||
selectTargetChain,
|
selectTransferTargetChain,
|
||||||
} from "../../store/selectors";
|
} from "../../store/selectors";
|
||||||
import { setIsSending, setSignedVAAHex } from "../../store/transferSlice";
|
import { setIsSending, setSignedVAAHex } from "../../store/transferSlice";
|
||||||
import { uint8ArrayToHex } from "../../utils/array";
|
import { uint8ArrayToHex } from "../../utils/array";
|
||||||
|
@ -35,17 +35,19 @@ const useStyles = makeStyles((theme) => ({
|
||||||
function Send() {
|
function Send() {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const sourceChain = useSelector(selectSourceChain);
|
const sourceChain = useSelector(selectTransferSourceChain);
|
||||||
const sourceAsset = useSelector(selectSourceAsset);
|
const sourceAsset = useSelector(selectTransferSourceAsset);
|
||||||
const amount = useSelector(selectAmount);
|
const amount = useSelector(selectTransferAmount);
|
||||||
const targetChain = useSelector(selectTargetChain);
|
const targetChain = useSelector(selectTransferTargetChain);
|
||||||
const isTargetComplete = useSelector(selectIsTargetComplete);
|
const isTargetComplete = useSelector(selectTransferIsTargetComplete);
|
||||||
const isSending = useSelector(selectIsSending);
|
const isSending = useSelector(selectTransferIsSending);
|
||||||
const isSendComplete = useSelector(selectIsSendComplete);
|
const isSendComplete = useSelector(selectTransferIsSendComplete);
|
||||||
const { provider, signer, signerAddress } = useEthereumProvider();
|
const { provider, signer, signerAddress } = useEthereumProvider();
|
||||||
const { wallet } = useSolanaWallet();
|
const { wallet } = useSolanaWallet();
|
||||||
const solPK = wallet?.publicKey;
|
const solPK = wallet?.publicKey;
|
||||||
const sourceParsedTokenAccount = useSelector(selectSourceParsedTokenAccount);
|
const sourceParsedTokenAccount = useSelector(
|
||||||
|
selectTransferSourceParsedTokenAccount
|
||||||
|
);
|
||||||
const tokenPK = sourceParsedTokenAccount?.publicKey;
|
const tokenPK = sourceParsedTokenAccount?.publicKey;
|
||||||
const decimals = sourceParsedTokenAccount?.decimals;
|
const decimals = sourceParsedTokenAccount?.decimals;
|
||||||
const {
|
const {
|
||||||
|
|
|
@ -2,12 +2,12 @@ import { Button, makeStyles, MenuItem, TextField } from "@material-ui/core";
|
||||||
import { useCallback } from "react";
|
import { useCallback } from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import {
|
import {
|
||||||
selectAmount,
|
selectTransferAmount,
|
||||||
selectIsSourceComplete,
|
selectTransferIsSourceComplete,
|
||||||
selectShouldLockFields,
|
selectTransferShouldLockFields,
|
||||||
selectSourceAsset,
|
selectTransferSourceAsset,
|
||||||
selectSourceBalanceString,
|
selectTransferSourceBalanceString,
|
||||||
selectSourceChain,
|
selectTransferSourceChain,
|
||||||
} from "../../store/selectors";
|
} from "../../store/selectors";
|
||||||
import {
|
import {
|
||||||
incrementStep,
|
incrementStep,
|
||||||
|
@ -27,12 +27,12 @@ const useStyles = makeStyles((theme) => ({
|
||||||
function Source() {
|
function Source() {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const sourceChain = useSelector(selectSourceChain);
|
const sourceChain = useSelector(selectTransferSourceChain);
|
||||||
const sourceAsset = useSelector(selectSourceAsset);
|
const sourceAsset = useSelector(selectTransferSourceAsset);
|
||||||
const uiAmountString = useSelector(selectSourceBalanceString);
|
const uiAmountString = useSelector(selectTransferSourceBalanceString);
|
||||||
const amount = useSelector(selectAmount);
|
const amount = useSelector(selectTransferAmount);
|
||||||
const isSourceComplete = useSelector(selectIsSourceComplete);
|
const isSourceComplete = useSelector(selectTransferIsSourceComplete);
|
||||||
const shouldLockFields = useSelector(selectShouldLockFields);
|
const shouldLockFields = useSelector(selectTransferShouldLockFields);
|
||||||
const handleSourceChange = useCallback(
|
const handleSourceChange = useCallback(
|
||||||
(event) => {
|
(event) => {
|
||||||
dispatch(setSourceChain(event.target.value));
|
dispatch(setSourceChain(event.target.value));
|
||||||
|
|
|
@ -2,10 +2,10 @@ import { Button, MenuItem, TextField } from "@material-ui/core";
|
||||||
import { useCallback, useMemo } from "react";
|
import { useCallback, useMemo } from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import {
|
import {
|
||||||
selectIsTargetComplete,
|
selectTransferIsTargetComplete,
|
||||||
selectShouldLockFields,
|
selectTransferShouldLockFields,
|
||||||
selectSourceChain,
|
selectTransferSourceChain,
|
||||||
selectTargetChain,
|
selectTransferTargetChain,
|
||||||
} from "../../store/selectors";
|
} from "../../store/selectors";
|
||||||
import { incrementStep, setTargetChain } from "../../store/transferSlice";
|
import { incrementStep, setTargetChain } from "../../store/transferSlice";
|
||||||
import { CHAINS } from "../../utils/consts";
|
import { CHAINS } from "../../utils/consts";
|
||||||
|
@ -13,14 +13,14 @@ import KeyAndBalance from "../KeyAndBalance";
|
||||||
|
|
||||||
function Target() {
|
function Target() {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const sourceChain = useSelector(selectSourceChain);
|
const sourceChain = useSelector(selectTransferSourceChain);
|
||||||
const chains = useMemo(
|
const chains = useMemo(
|
||||||
() => CHAINS.filter((c) => c.id !== sourceChain),
|
() => CHAINS.filter((c) => c.id !== sourceChain),
|
||||||
[sourceChain]
|
[sourceChain]
|
||||||
);
|
);
|
||||||
const targetChain = useSelector(selectTargetChain);
|
const targetChain = useSelector(selectTransferTargetChain);
|
||||||
const isTargetComplete = useSelector(selectIsTargetComplete);
|
const isTargetComplete = useSelector(selectTransferIsTargetComplete);
|
||||||
const shouldLockFields = useSelector(selectShouldLockFields);
|
const shouldLockFields = useSelector(selectTransferShouldLockFields);
|
||||||
const handleTargetChange = useCallback(
|
const handleTargetChange = useCallback(
|
||||||
(event) => {
|
(event) => {
|
||||||
dispatch(setTargetChain(event.target.value));
|
dispatch(setTargetChain(event.target.value));
|
||||||
|
|
|
@ -7,7 +7,10 @@ import {
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import useGetBalanceEffect from "../../hooks/useGetBalanceEffect";
|
import useGetBalanceEffect from "../../hooks/useGetBalanceEffect";
|
||||||
import { selectActiveStep, selectSignedVAAHex } from "../../store/selectors";
|
import {
|
||||||
|
selectTransferActiveStep,
|
||||||
|
selectTransferSignedVAAHex,
|
||||||
|
} from "../../store/selectors";
|
||||||
import { setStep } from "../../store/transferSlice";
|
import { setStep } from "../../store/transferSlice";
|
||||||
import Redeem from "./Redeem";
|
import Redeem from "./Redeem";
|
||||||
import Send from "./Send";
|
import Send from "./Send";
|
||||||
|
@ -22,8 +25,8 @@ import Target from "./Target";
|
||||||
function Transfer() {
|
function Transfer() {
|
||||||
useGetBalanceEffect();
|
useGetBalanceEffect();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const activeStep = useSelector(selectActiveStep);
|
const activeStep = useSelector(selectTransferActiveStep);
|
||||||
const signedVAAHex = useSelector(selectSignedVAAHex);
|
const signedVAAHex = useSelector(selectTransferSignedVAAHex);
|
||||||
return (
|
return (
|
||||||
<Container maxWidth="md">
|
<Container maxWidth="md">
|
||||||
<Stepper activeStep={activeStep} orientation="vertical">
|
<Stepper activeStep={activeStep} orientation="vertical">
|
||||||
|
|
|
@ -0,0 +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;
|
||||||
|
}
|
|
@ -5,7 +5,10 @@ import { useDispatch, useSelector } from "react-redux";
|
||||||
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
|
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
|
||||||
import { useSolanaWallet } from "../contexts/SolanaWalletContext";
|
import { useSolanaWallet } from "../contexts/SolanaWalletContext";
|
||||||
import { TokenImplementation__factory } from "../ethers-contracts";
|
import { TokenImplementation__factory } from "../ethers-contracts";
|
||||||
import { selectSourceAsset, selectSourceChain } from "../store/selectors";
|
import {
|
||||||
|
selectTransferSourceAsset,
|
||||||
|
selectTransferSourceChain,
|
||||||
|
} from "../store/selectors";
|
||||||
import { setSourceParsedTokenAccount } from "../store/transferSlice";
|
import { setSourceParsedTokenAccount } from "../store/transferSlice";
|
||||||
import { CHAIN_ID_ETH, CHAIN_ID_SOLANA, SOLANA_HOST } from "../utils/consts";
|
import { CHAIN_ID_ETH, CHAIN_ID_SOLANA, SOLANA_HOST } from "../utils/consts";
|
||||||
|
|
||||||
|
@ -27,8 +30,8 @@ function createParsedTokenAccount(
|
||||||
|
|
||||||
function useGetBalanceEffect() {
|
function useGetBalanceEffect() {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const sourceChain = useSelector(selectSourceChain);
|
const sourceChain = useSelector(selectTransferSourceChain);
|
||||||
const sourceAsset = useSelector(selectSourceAsset);
|
const sourceAsset = useSelector(selectTransferSourceAsset);
|
||||||
const { wallet } = useSolanaWallet();
|
const { wallet } = useSolanaWallet();
|
||||||
const solPK = wallet?.publicKey;
|
const solPK = wallet?.publicKey;
|
||||||
const { provider, signerAddress } = useEthereumProvider();
|
const { provider, signerAddress } = useEthereumProvider();
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import { selectSignedVAAHex } from "../store/selectors";
|
import { selectTransferSignedVAAHex } from "../store/selectors";
|
||||||
import { hexToUint8Array } from "../utils/array";
|
import { hexToUint8Array } from "../utils/array";
|
||||||
|
|
||||||
export default function useTransferSignedVAA() {
|
export default function useTransferSignedVAA() {
|
||||||
const signedVAAHex = useSelector(selectSignedVAAHex);
|
const signedVAAHex = useSelector(selectTransferSignedVAAHex);
|
||||||
const signedVAA = useMemo(
|
const signedVAA = useMemo(
|
||||||
() => (signedVAAHex ? hexToUint8Array(signedVAAHex) : undefined),
|
() => (signedVAAHex ? hexToUint8Array(signedVAAHex) : undefined),
|
||||||
[signedVAAHex]
|
[signedVAAHex]
|
||||||
|
|
|
@ -0,0 +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<Steps>) => {
|
||||||
|
state.activeStep = action.payload;
|
||||||
|
},
|
||||||
|
setSourceChain: (state, action: PayloadAction<ChainId>) => {
|
||||||
|
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<string>) => {
|
||||||
|
state.sourceAsset = action.payload;
|
||||||
|
},
|
||||||
|
setTargetChain: (state, action: PayloadAction<ChainId>) => {
|
||||||
|
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<string>) => {
|
||||||
|
state.signedVAAHex = action.payload;
|
||||||
|
state.isSending = false;
|
||||||
|
state.activeStep = 3;
|
||||||
|
},
|
||||||
|
setIsSending: (state, action: PayloadAction<boolean>) => {
|
||||||
|
state.isSending = action.payload;
|
||||||
|
},
|
||||||
|
setIsCreating: (state, action: PayloadAction<boolean>) => {
|
||||||
|
state.isCreating = action.payload;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const {
|
||||||
|
incrementStep,
|
||||||
|
decrementStep,
|
||||||
|
setStep,
|
||||||
|
setSourceChain,
|
||||||
|
setSourceAsset,
|
||||||
|
setTargetChain,
|
||||||
|
setSignedVAAHex,
|
||||||
|
setIsSending,
|
||||||
|
setIsCreating,
|
||||||
|
} = attestSlice.actions;
|
||||||
|
|
||||||
|
export default attestSlice.reducer;
|
|
@ -1,8 +1,10 @@
|
||||||
import { configureStore } from "@reduxjs/toolkit";
|
import { configureStore } from "@reduxjs/toolkit";
|
||||||
|
import attestReducer from "./attestSlice";
|
||||||
import transferReducer from "./transferSlice";
|
import transferReducer from "./transferSlice";
|
||||||
|
|
||||||
export const store = configureStore({
|
export const store = configureStore({
|
||||||
reducer: {
|
reducer: {
|
||||||
|
attest: attestReducer,
|
||||||
transfer: transferReducer,
|
transfer: transferReducer,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,27 +2,64 @@ import { parseUnits } from "ethers/lib/utils";
|
||||||
import { RootState } from ".";
|
import { RootState } from ".";
|
||||||
import { CHAIN_ID_SOLANA } from "../utils/consts";
|
import { CHAIN_ID_SOLANA } from "../utils/consts";
|
||||||
|
|
||||||
export const selectActiveStep = (state: RootState) => state.transfer.activeStep;
|
/*
|
||||||
export const selectSourceChain = (state: RootState) =>
|
* 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;
|
state.transfer.sourceChain;
|
||||||
export const selectSourceAsset = (state: RootState) =>
|
export const selectTransferSourceAsset = (state: RootState) =>
|
||||||
state.transfer.sourceAsset;
|
state.transfer.sourceAsset;
|
||||||
export const selectSourceParsedTokenAccount = (state: RootState) =>
|
export const selectTransferSourceParsedTokenAccount = (state: RootState) =>
|
||||||
state.transfer.sourceParsedTokenAccount;
|
state.transfer.sourceParsedTokenAccount;
|
||||||
export const selectSourceBalanceString = (state: RootState) =>
|
export const selectTransferSourceBalanceString = (state: RootState) =>
|
||||||
state.transfer.sourceParsedTokenAccount?.uiAmountString || "";
|
state.transfer.sourceParsedTokenAccount?.uiAmountString || "";
|
||||||
export const selectAmount = (state: RootState) => state.transfer.amount;
|
export const selectTransferAmount = (state: RootState) => state.transfer.amount;
|
||||||
export const selectTargetChain = (state: RootState) =>
|
export const selectTransferTargetChain = (state: RootState) =>
|
||||||
state.transfer.targetChain;
|
state.transfer.targetChain;
|
||||||
export const selectSignedVAAHex = (state: RootState) =>
|
export const selectTransferSignedVAAHex = (state: RootState) =>
|
||||||
state.transfer.signedVAAHex;
|
state.transfer.signedVAAHex;
|
||||||
export const selectIsSending = (state: RootState) => state.transfer.isSending;
|
export const selectTransferIsSending = (state: RootState) =>
|
||||||
export const selectIsRedeeming = (state: RootState) =>
|
state.transfer.isSending;
|
||||||
|
export const selectTransferIsRedeeming = (state: RootState) =>
|
||||||
state.transfer.isRedeeming;
|
state.transfer.isRedeeming;
|
||||||
|
|
||||||
// safety checks
|
// safety checks
|
||||||
// TODO: could make this return a string with a user informative message
|
// TODO: could make this return a string with a user informative message
|
||||||
export const selectIsSourceComplete = (state: RootState) =>
|
export const selectTransferIsSourceComplete = (state: RootState) =>
|
||||||
!!state.transfer.sourceChain &&
|
!!state.transfer.sourceChain &&
|
||||||
!!state.transfer.sourceAsset &&
|
!!state.transfer.sourceAsset &&
|
||||||
!!state.transfer.sourceParsedTokenAccount &&
|
!!state.transfer.sourceParsedTokenAccount &&
|
||||||
|
@ -41,9 +78,9 @@ export const selectIsSourceComplete = (state: RootState) =>
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
// TODO: check wrapped asset exists or is native transfer
|
// TODO: check wrapped asset exists or is native transfer
|
||||||
export const selectIsTargetComplete = (state: RootState) =>
|
export const selectTransferIsTargetComplete = (state: RootState) =>
|
||||||
selectIsSourceComplete(state) && !!state.transfer.targetChain;
|
selectTransferIsSourceComplete(state) && !!state.transfer.targetChain;
|
||||||
export const selectIsSendComplete = (state: RootState) =>
|
export const selectTransferIsSendComplete = (state: RootState) =>
|
||||||
!!selectSignedVAAHex(state);
|
!!selectTransferSignedVAAHex(state);
|
||||||
export const selectShouldLockFields = (state: RootState) =>
|
export const selectTransferShouldLockFields = (state: RootState) =>
|
||||||
selectIsSending(state) || selectIsSendComplete(state);
|
selectTransferIsSending(state) || selectTransferIsSendComplete(state);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import Wallet from "@project-serum/sol-wallet-adapter";
|
import Wallet from "@project-serum/sol-wallet-adapter";
|
||||||
import {
|
import {
|
||||||
Connection, Keypair,
|
Connection,
|
||||||
|
Keypair,
|
||||||
PublicKey,
|
PublicKey,
|
||||||
SystemProgram,
|
SystemProgram,
|
||||||
Transaction,
|
Transaction,
|
||||||
|
@ -66,8 +67,7 @@ export async function attestFromEth(
|
||||||
export async function attestFromSolana(
|
export async function attestFromSolana(
|
||||||
wallet: Wallet | undefined,
|
wallet: Wallet | undefined,
|
||||||
payerAddress: string | undefined, //TODO: we may not need this since we have wallet
|
payerAddress: string | undefined, //TODO: we may not need this since we have wallet
|
||||||
mintAddress: string,
|
mintAddress: string
|
||||||
decimals: number
|
|
||||||
) {
|
) {
|
||||||
if (!wallet || !wallet.publicKey || !payerAddress) return;
|
if (!wallet || !wallet.publicKey || !payerAddress) return;
|
||||||
const nonceConst = Math.random() * 100000;
|
const nonceConst = Math.random() * 100000;
|
||||||
|
@ -106,8 +106,8 @@ export async function attestFromSolana(
|
||||||
SOL_BRIDGE_ADDRESS,
|
SOL_BRIDGE_ADDRESS,
|
||||||
payerAddress,
|
payerAddress,
|
||||||
messageKey.publicKey.toString(),
|
messageKey.publicKey.toString(),
|
||||||
mintAddress, // TODO: mint_metadata: what address is this supposed to be?
|
mintAddress,
|
||||||
nonce,
|
nonce
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
const transaction = new Transaction().add(transferIx, ix);
|
const transaction = new Transaction().add(transferIx, ix);
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
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;
|
Loading…
Reference in New Issue