bridge_ui: eth wrapped assets and getSignedVAA
Change-Id: I1beaeefb7863c0543e180ed2e15e91c645b89299
This commit is contained in:
parent
3aecf65f4d
commit
5187120fa0
|
@ -9,6 +9,7 @@
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@improbable-eng/grpc-web": "^0.13.0",
|
||||||
"@material-ui/core": "^4.12.2",
|
"@material-ui/core": "^4.12.2",
|
||||||
"@metamask/detect-provider": "^1.2.0",
|
"@metamask/detect-provider": "^1.2.0",
|
||||||
"@project-serum/sol-wallet-adapter": "^0.2.5",
|
"@project-serum/sol-wallet-adapter": "^0.2.5",
|
||||||
|
@ -4011,8 +4012,6 @@
|
||||||
"version": "0.13.0",
|
"version": "0.13.0",
|
||||||
"resolved": "https://registry.npmjs.org/@improbable-eng/grpc-web/-/grpc-web-0.13.0.tgz",
|
"resolved": "https://registry.npmjs.org/@improbable-eng/grpc-web/-/grpc-web-0.13.0.tgz",
|
||||||
"integrity": "sha512-vaxxT+Qwb7GPqDQrBV4vAAfH0HywgOLw6xGIKXd9Q8hcV63CQhmS3p4+pZ9/wVvt4Ph3ZDK9fdC983b9aGMUFg==",
|
"integrity": "sha512-vaxxT+Qwb7GPqDQrBV4vAAfH0HywgOLw6xGIKXd9Q8hcV63CQhmS3p4+pZ9/wVvt4Ph3ZDK9fdC983b9aGMUFg==",
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"browser-headers": "^0.4.0"
|
"browser-headers": "^0.4.0"
|
||||||
},
|
},
|
||||||
|
@ -10309,9 +10308,7 @@
|
||||||
"node_modules/browser-headers": {
|
"node_modules/browser-headers": {
|
||||||
"version": "0.4.1",
|
"version": "0.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/browser-headers/-/browser-headers-0.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/browser-headers/-/browser-headers-0.4.1.tgz",
|
||||||
"integrity": "sha512-CA9hsySZVo9371qEHjHZtYxV2cFtVj5Wj/ZHi8ooEsrtm4vOnl9Y9HmyYWk9q+05d7K3rdoAE0j3MVEFVvtQtg==",
|
"integrity": "sha512-CA9hsySZVo9371qEHjHZtYxV2cFtVj5Wj/ZHi8ooEsrtm4vOnl9Y9HmyYWk9q+05d7K3rdoAE0j3MVEFVvtQtg=="
|
||||||
"dev": true,
|
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"node_modules/browser-process-hrtime": {
|
"node_modules/browser-process-hrtime": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
@ -17417,9 +17414,7 @@
|
||||||
"node_modules/google-protobuf": {
|
"node_modules/google-protobuf": {
|
||||||
"version": "3.17.3",
|
"version": "3.17.3",
|
||||||
"resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.17.3.tgz",
|
"resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.17.3.tgz",
|
||||||
"integrity": "sha512-OVPzcSWIAJ+d5yiHyeaLrdufQtrvaBrF4JQg+z8ynTkbO3uFcujqXszTumqg1cGsAsjkWnI+M5B1xZ19yR4Wyg==",
|
"integrity": "sha512-OVPzcSWIAJ+d5yiHyeaLrdufQtrvaBrF4JQg+z8ynTkbO3uFcujqXszTumqg1cGsAsjkWnI+M5B1xZ19yR4Wyg=="
|
||||||
"dev": true,
|
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"node_modules/got": {
|
"node_modules/got": {
|
||||||
"version": "9.6.0",
|
"version": "9.6.0",
|
||||||
|
@ -41493,8 +41488,6 @@
|
||||||
"version": "0.13.0",
|
"version": "0.13.0",
|
||||||
"resolved": "https://registry.npmjs.org/@improbable-eng/grpc-web/-/grpc-web-0.13.0.tgz",
|
"resolved": "https://registry.npmjs.org/@improbable-eng/grpc-web/-/grpc-web-0.13.0.tgz",
|
||||||
"integrity": "sha512-vaxxT+Qwb7GPqDQrBV4vAAfH0HywgOLw6xGIKXd9Q8hcV63CQhmS3p4+pZ9/wVvt4Ph3ZDK9fdC983b9aGMUFg==",
|
"integrity": "sha512-vaxxT+Qwb7GPqDQrBV4vAAfH0HywgOLw6xGIKXd9Q8hcV63CQhmS3p4+pZ9/wVvt4Ph3ZDK9fdC983b9aGMUFg==",
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"browser-headers": "^0.4.0"
|
"browser-headers": "^0.4.0"
|
||||||
}
|
}
|
||||||
|
@ -46695,9 +46688,7 @@
|
||||||
"browser-headers": {
|
"browser-headers": {
|
||||||
"version": "0.4.1",
|
"version": "0.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/browser-headers/-/browser-headers-0.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/browser-headers/-/browser-headers-0.4.1.tgz",
|
||||||
"integrity": "sha512-CA9hsySZVo9371qEHjHZtYxV2cFtVj5Wj/ZHi8ooEsrtm4vOnl9Y9HmyYWk9q+05d7K3rdoAE0j3MVEFVvtQtg==",
|
"integrity": "sha512-CA9hsySZVo9371qEHjHZtYxV2cFtVj5Wj/ZHi8ooEsrtm4vOnl9Y9HmyYWk9q+05d7K3rdoAE0j3MVEFVvtQtg=="
|
||||||
"dev": true,
|
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"browser-process-hrtime": {
|
"browser-process-hrtime": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
@ -52508,9 +52499,7 @@
|
||||||
"google-protobuf": {
|
"google-protobuf": {
|
||||||
"version": "3.17.3",
|
"version": "3.17.3",
|
||||||
"resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.17.3.tgz",
|
"resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.17.3.tgz",
|
||||||
"integrity": "sha512-OVPzcSWIAJ+d5yiHyeaLrdufQtrvaBrF4JQg+z8ynTkbO3uFcujqXszTumqg1cGsAsjkWnI+M5B1xZ19yR4Wyg==",
|
"integrity": "sha512-OVPzcSWIAJ+d5yiHyeaLrdufQtrvaBrF4JQg+z8ynTkbO3uFcujqXszTumqg1cGsAsjkWnI+M5B1xZ19yR4Wyg=="
|
||||||
"dev": true,
|
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"got": {
|
"got": {
|
||||||
"version": "9.6.0",
|
"version": "9.6.0",
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@improbable-eng/grpc-web": "^0.13.0",
|
||||||
"@material-ui/core": "^4.12.2",
|
"@material-ui/core": "^4.12.2",
|
||||||
"@metamask/detect-provider": "^1.2.0",
|
"@metamask/detect-provider": "^1.2.0",
|
||||||
"@project-serum/sol-wallet-adapter": "^0.2.5",
|
"@project-serum/sol-wallet-adapter": "^0.2.5",
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
|
CircularProgress,
|
||||||
Grid,
|
Grid,
|
||||||
makeStyles,
|
makeStyles,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
|
@ -11,6 +12,7 @@ import { useEthereumProvider } from "../contexts/EthereumProviderContext";
|
||||||
import { useSolanaWallet } from "../contexts/SolanaWalletContext";
|
import { useSolanaWallet } from "../contexts/SolanaWalletContext";
|
||||||
import useEthereumBalance from "../hooks/useEthereumBalance";
|
import useEthereumBalance from "../hooks/useEthereumBalance";
|
||||||
import useSolanaBalance from "../hooks/useSolanaBalance";
|
import useSolanaBalance from "../hooks/useSolanaBalance";
|
||||||
|
import useWrappedAsset from "../hooks/useWrappedAsset";
|
||||||
import {
|
import {
|
||||||
ChainId,
|
ChainId,
|
||||||
CHAINS,
|
CHAINS,
|
||||||
|
@ -105,8 +107,18 @@ function Transfer() {
|
||||||
decimals: solDecimals,
|
decimals: solDecimals,
|
||||||
uiAmount: solBalance,
|
uiAmount: solBalance,
|
||||||
} = useSolanaBalance(assetAddress, solPK, fromChain === CHAIN_ID_SOLANA);
|
} = useSolanaBalance(assetAddress, solPK, fromChain === CHAIN_ID_SOLANA);
|
||||||
|
const { isLoading: isCheckingWrapped, wrappedAsset } = useWrappedAsset(
|
||||||
|
toChain,
|
||||||
|
fromChain,
|
||||||
|
assetAddress,
|
||||||
|
provider
|
||||||
|
);
|
||||||
|
console.log(isCheckingWrapped, wrappedAsset);
|
||||||
|
// TODO: make a helper function for this
|
||||||
|
const isWrapped = true;
|
||||||
|
//wrappedAsset && wrappedAsset !== ethers.constants.AddressZero;
|
||||||
// TODO: dynamically get "to" wallet
|
// TODO: dynamically get "to" wallet
|
||||||
const handleClick = useCallback(() => {
|
const handleTransferClick = useCallback(() => {
|
||||||
// TODO: more generic way of calling these
|
// TODO: more generic way of calling these
|
||||||
if (transferFrom[fromChain]) {
|
if (transferFrom[fromChain]) {
|
||||||
if (
|
if (
|
||||||
|
@ -210,39 +222,77 @@ function Transfer() {
|
||||||
value={assetAddress}
|
value={assetAddress}
|
||||||
onChange={handleAssetChange}
|
onChange={handleAssetChange}
|
||||||
/>
|
/>
|
||||||
<TextField
|
{isWrapped ? (
|
||||||
placeholder="Amount"
|
<>
|
||||||
type="number"
|
<TextField
|
||||||
fullWidth
|
placeholder="Amount"
|
||||||
className={classes.transferField}
|
type="number"
|
||||||
value={amount}
|
fullWidth
|
||||||
onChange={handleAmountChange}
|
className={classes.transferField}
|
||||||
/>
|
value={amount}
|
||||||
<Button
|
onChange={handleAmountChange}
|
||||||
color="primary"
|
/>
|
||||||
variant="contained"
|
<Button
|
||||||
className={classes.transferButton}
|
color="primary"
|
||||||
onClick={handleClick}
|
variant="contained"
|
||||||
disabled={!canAttemptTransfer}
|
className={classes.transferButton}
|
||||||
>
|
onClick={handleTransferClick}
|
||||||
Transfer
|
disabled={!canAttemptTransfer}
|
||||||
</Button>
|
>
|
||||||
{canAttemptTransfer ? null : (
|
Transfer
|
||||||
<Typography variant="body2" color="error">
|
</Button>
|
||||||
{!isTransferImplemented
|
{canAttemptTransfer ? null : (
|
||||||
? `Transfer is not yet implemented for ${CHAINS_BY_ID[fromChain].name}`
|
<Typography variant="body2" color="error">
|
||||||
: !isProviderConnected
|
{!isTransferImplemented
|
||||||
? "The source wallet is not connected"
|
? `Transfer is not yet implemented for ${CHAINS_BY_ID[fromChain].name}`
|
||||||
: !isRecipientAvailable
|
: !isProviderConnected
|
||||||
? "The receiving wallet is not connected"
|
? "The source wallet is not connected"
|
||||||
: !isAddressDefined
|
: !isRecipientAvailable
|
||||||
? "Please provide an asset address"
|
? "The receiving wallet is not connected"
|
||||||
: !isAmountPositive
|
: !isAddressDefined
|
||||||
? "The amount must be positive"
|
? "Please provide an asset address"
|
||||||
: !isBalanceAtLeastAmount
|
: !isAmountPositive
|
||||||
? "The amount may not be greater than the balance"
|
? "The amount must be positive"
|
||||||
: ""}
|
: !isBalanceAtLeastAmount
|
||||||
</Typography>
|
? "The amount may not be greater than the balance"
|
||||||
|
: ""}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<div style={{ position: "relative" }}>
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
variant="contained"
|
||||||
|
disabled={isCheckingWrapped}
|
||||||
|
className={classes.transferButton}
|
||||||
|
>
|
||||||
|
Attest
|
||||||
|
</Button>
|
||||||
|
{isCheckingWrapped ? (
|
||||||
|
<CircularProgress
|
||||||
|
size={24}
|
||||||
|
color="inherit"
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
bottom: 0,
|
||||||
|
left: "50%",
|
||||||
|
marginLeft: -12,
|
||||||
|
marginBottom: 6,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
{isCheckingWrapped ? null : (
|
||||||
|
<Typography variant="body2">
|
||||||
|
<br />
|
||||||
|
This token does not exist on {CHAINS_BY_ID[toChain].name}. Someone
|
||||||
|
must attest the the token to the target chain before it can be
|
||||||
|
transferred.
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -20,12 +20,10 @@ function useEthereumBalance(
|
||||||
token
|
token
|
||||||
.decimals()
|
.decimals()
|
||||||
.then((decimals) => {
|
.then((decimals) => {
|
||||||
console.log(decimals);
|
|
||||||
provider
|
provider
|
||||||
?.getSigner()
|
?.getSigner()
|
||||||
.getAddress()
|
.getAddress()
|
||||||
.then((pk) => {
|
.then((pk) => {
|
||||||
console.log(pk);
|
|
||||||
token.balanceOf(pk).then((n) => {
|
token.balanceOf(pk).then((n) => {
|
||||||
if (!cancelled) {
|
if (!cancelled) {
|
||||||
setBalance(formatUnits(n, decimals));
|
setBalance(formatUnits(n, decimals));
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { ethers } from "ethers";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { ChainId, CHAIN_ID_ETH } from "../utils/consts";
|
||||||
|
import { wrappedAssetEth } from "../utils/wrappedAsset";
|
||||||
|
|
||||||
|
export interface WrappedAssetState {
|
||||||
|
isLoading: boolean;
|
||||||
|
wrappedAsset: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function useWrappedAsset(
|
||||||
|
checkChain: ChainId,
|
||||||
|
originChain: ChainId,
|
||||||
|
originAsset: string,
|
||||||
|
provider: ethers.providers.Web3Provider | undefined
|
||||||
|
) {
|
||||||
|
const [state, setState] = useState<WrappedAssetState>({
|
||||||
|
isLoading: false,
|
||||||
|
wrappedAsset: null,
|
||||||
|
});
|
||||||
|
useEffect(() => {
|
||||||
|
let cancelled = false;
|
||||||
|
(async () => {
|
||||||
|
if (provider && checkChain === CHAIN_ID_ETH) {
|
||||||
|
setState({ isLoading: true, wrappedAsset: null });
|
||||||
|
const asset = await wrappedAssetEth(provider, originChain, originAsset);
|
||||||
|
if (!cancelled) {
|
||||||
|
setState({ isLoading: false, wrappedAsset: asset });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setState({ isLoading: false, wrappedAsset: null });
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
return () => {
|
||||||
|
cancelled = true;
|
||||||
|
};
|
||||||
|
}, [checkChain, originChain, originAsset, provider]);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useWrappedAsset;
|
|
@ -8,7 +8,7 @@ export const theme = responsiveFontSizes(
|
||||||
default: "#010114",
|
default: "#010114",
|
||||||
paper: "#010114",
|
paper: "#010114",
|
||||||
},
|
},
|
||||||
divider: "#FFFFFF",
|
divider: "#4e4e54",
|
||||||
primary: {
|
primary: {
|
||||||
main: "#0074FF",
|
main: "#0074FF",
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
import {
|
||||||
|
GrpcWebImpl,
|
||||||
|
PublicrpcClientImpl,
|
||||||
|
} from "../proto/publicrpc/v1/publicrpc";
|
||||||
|
import { ChainId } from "../utils/consts";
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { getAddress } from "ethers/lib/utils";
|
||||||
|
|
||||||
export type ChainId = 1 | 2 | 3 | 4;
|
export type ChainId = 1 | 2 | 3 | 4;
|
||||||
export const CHAIN_ID_SOLANA: ChainId = 1;
|
export const CHAIN_ID_SOLANA: ChainId = 1;
|
||||||
export const CHAIN_ID_ETH: ChainId = 2;
|
export const CHAIN_ID_ETH: ChainId = 2;
|
||||||
|
@ -31,10 +33,15 @@ export const CHAINS_BY_ID: ChainsById = CHAINS.reduce((obj, chain) => {
|
||||||
return obj;
|
return obj;
|
||||||
}, {} as ChainsById);
|
}, {} as ChainsById);
|
||||||
export const SOLANA_HOST = "http://localhost:8899";
|
export const SOLANA_HOST = "http://localhost:8899";
|
||||||
export const ETH_TEST_TOKEN_ADDRESS =
|
export const ETH_TEST_TOKEN_ADDRESS = getAddress(
|
||||||
"0x0290FB167208Af455bB137780163b7B7a9a10C16";
|
"0x0290FB167208Af455bB137780163b7B7a9a10C16"
|
||||||
export const ETH_TOKEN_BRIDGE_ADDRESS =
|
);
|
||||||
"0xe982e462b094850f12af94d21d470e21be9d0e9c";
|
export const ETH_BRIDGE_ADDRESS = getAddress(
|
||||||
|
"0x254dffcd3277c0b1660f6d42efbb754edababc2b"
|
||||||
|
);
|
||||||
|
export const ETH_TOKEN_BRIDGE_ADDRESS = getAddress(
|
||||||
|
"0xe982e462b094850f12af94d21d470e21be9d0e9c"
|
||||||
|
);
|
||||||
export const SOL_TEST_TOKEN_ADDRESS =
|
export const SOL_TEST_TOKEN_ADDRESS =
|
||||||
"2WDq7wSs9zYrpx2kbHDA4RUTRch2CCTP6ZWaH4GNfnQQ";
|
"2WDq7wSs9zYrpx2kbHDA4RUTRch2CCTP6ZWaH4GNfnQQ";
|
||||||
export const SOL_BRIDGE_ADDRESS = "Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o";
|
export const SOL_BRIDGE_ADDRESS = "Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o";
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import Wallet from "@project-serum/sol-wallet-adapter";
|
import Wallet from "@project-serum/sol-wallet-adapter";
|
||||||
|
import { Token, TOKEN_PROGRAM_ID } from "@solana/spl-token";
|
||||||
import {
|
import {
|
||||||
AccountMeta,
|
AccountMeta,
|
||||||
Connection,
|
Connection,
|
||||||
|
@ -7,17 +8,19 @@ import {
|
||||||
Transaction,
|
Transaction,
|
||||||
TransactionInstruction,
|
TransactionInstruction,
|
||||||
} from "@solana/web3.js";
|
} from "@solana/web3.js";
|
||||||
import { Token, TOKEN_PROGRAM_ID } from "@solana/spl-token";
|
|
||||||
import { ethers } from "ethers";
|
import { ethers } from "ethers";
|
||||||
import { arrayify, formatUnits, parseUnits, zeroPad } from "ethers/lib/utils";
|
import { arrayify, formatUnits, parseUnits, zeroPad } from "ethers/lib/utils";
|
||||||
import {
|
import {
|
||||||
Bridge__factory,
|
Bridge__factory,
|
||||||
|
Implementation__factory,
|
||||||
TokenImplementation__factory,
|
TokenImplementation__factory,
|
||||||
} from "../ethers-contracts";
|
} from "../ethers-contracts";
|
||||||
|
import { getSignedVAA } from "../sdk";
|
||||||
import {
|
import {
|
||||||
ChainId,
|
ChainId,
|
||||||
CHAIN_ID_ETH,
|
CHAIN_ID_ETH,
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
|
ETH_BRIDGE_ADDRESS,
|
||||||
ETH_TOKEN_BRIDGE_ADDRESS,
|
ETH_TOKEN_BRIDGE_ADDRESS,
|
||||||
SOLANA_HOST,
|
SOLANA_HOST,
|
||||||
SOL_BRIDGE_ADDRESS,
|
SOL_BRIDGE_ADDRESS,
|
||||||
|
@ -41,46 +44,60 @@ export function transferFromEth(
|
||||||
//TODO: don't hardcode, fetch decimals / share them with balance, how do we determine recipient chain?
|
//TODO: don't hardcode, fetch decimals / share them with balance, how do we determine recipient chain?
|
||||||
//TODO: more catches
|
//TODO: more catches
|
||||||
const amountParsed = parseUnits(amount, 18);
|
const amountParsed = parseUnits(amount, 18);
|
||||||
signer.getAddress().then((signerAddress) => {
|
(async () => {
|
||||||
|
const signerAddress = await signer.getAddress();
|
||||||
console.log("Signer:", signerAddress);
|
console.log("Signer:", signerAddress);
|
||||||
console.log("Token:", tokenAddress);
|
console.log("Token:", tokenAddress);
|
||||||
const token = TokenImplementation__factory.connect(tokenAddress, signer);
|
const token = TokenImplementation__factory.connect(tokenAddress, signer);
|
||||||
token
|
const allowance = await token.allowance(
|
||||||
.allowance(signerAddress, ETH_TOKEN_BRIDGE_ADDRESS)
|
signerAddress,
|
||||||
.then((allowance) => {
|
ETH_TOKEN_BRIDGE_ADDRESS
|
||||||
console.log("Allowance", allowance.toString()); //TODO: should we check that this is zero and warn if it isn't?
|
);
|
||||||
token
|
console.log("Allowance", allowance.toString()); //TODO: should we check that this is zero and warn if it isn't?
|
||||||
.approve(ETH_TOKEN_BRIDGE_ADDRESS, amountParsed)
|
const transaction = await token.approve(
|
||||||
.then((transaction) => {
|
ETH_TOKEN_BRIDGE_ADDRESS,
|
||||||
console.log(transaction);
|
amountParsed
|
||||||
const fee = 0; // for now, this won't do anything, we may add later
|
);
|
||||||
const nonceConst = Math.random() * 100000;
|
console.log(transaction);
|
||||||
const nonceBuffer = Buffer.alloc(4);
|
const fee = 0; // for now, this won't do anything, we may add later
|
||||||
nonceBuffer.writeUInt32LE(nonceConst, 0);
|
const nonceConst = Math.random() * 100000;
|
||||||
console.log("Initiating transfer");
|
const nonceBuffer = Buffer.alloc(4);
|
||||||
console.log("Amount:", formatUnits(amountParsed, 18));
|
nonceBuffer.writeUInt32LE(nonceConst, 0);
|
||||||
console.log("To chain:", recipientChain);
|
console.log("Initiating transfer");
|
||||||
console.log("To address:", recipientAddress);
|
console.log("Amount:", formatUnits(amountParsed, 18));
|
||||||
console.log("Fees:", fee);
|
console.log("To chain:", recipientChain);
|
||||||
console.log("Nonce:", nonceBuffer);
|
console.log("To address:", recipientAddress);
|
||||||
const bridge = Bridge__factory.connect(
|
console.log("Fees:", fee);
|
||||||
ETH_TOKEN_BRIDGE_ADDRESS,
|
console.log("Nonce:", nonceBuffer);
|
||||||
signer
|
const bridge = Bridge__factory.connect(ETH_TOKEN_BRIDGE_ADDRESS, signer);
|
||||||
);
|
const v = await bridge.transferTokens(
|
||||||
bridge
|
tokenAddress,
|
||||||
.transferTokens(
|
amountParsed,
|
||||||
tokenAddress,
|
recipientChain,
|
||||||
amountParsed,
|
recipientAddress,
|
||||||
recipientChain,
|
fee,
|
||||||
recipientAddress,
|
nonceBuffer
|
||||||
fee,
|
);
|
||||||
nonceBuffer
|
const receipt = await v.wait();
|
||||||
)
|
// TODO: dangerous!(?)
|
||||||
.then((v) => console.log("Success:", v))
|
const bridgeLog = receipt.logs.filter((l) => {
|
||||||
.catch((r) => console.error(r)); //TODO: integrate toast messages
|
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);
|
||||||
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: should we share this with client? ooh, should client use the SDK ;)
|
// TODO: should we share this with client? ooh, should client use the SDK ;)
|
||||||
|
@ -201,8 +218,8 @@ export function transferFromSolana(
|
||||||
console.log("SIGNED", signed);
|
console.log("SIGNED", signed);
|
||||||
const txid = await connection.sendRawTransaction(signed.serialize());
|
const txid = await connection.sendRawTransaction(signed.serialize());
|
||||||
console.log("SENT", txid);
|
console.log("SENT", txid);
|
||||||
await connection.confirmTransaction(txid);
|
const conf = await connection.confirmTransaction(txid);
|
||||||
console.log("CONFIRMED");
|
console.log("CONFIRMED", conf);
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { PublicKey } from "@solana/web3.js";
|
||||||
|
import { ethers } from "ethers";
|
||||||
|
import { arrayify, zeroPad } from "ethers/lib/utils";
|
||||||
|
import { Bridge__factory } from "../ethers-contracts";
|
||||||
|
import { ChainId, CHAIN_ID_SOLANA, ETH_TOKEN_BRIDGE_ADDRESS } from "./consts";
|
||||||
|
|
||||||
|
export function wrappedAssetEth(
|
||||||
|
provider: ethers.providers.Web3Provider,
|
||||||
|
originChain: ChainId,
|
||||||
|
originAsset: string
|
||||||
|
) {
|
||||||
|
const tokenBridge = Bridge__factory.connect(
|
||||||
|
ETH_TOKEN_BRIDGE_ADDRESS,
|
||||||
|
provider
|
||||||
|
);
|
||||||
|
// TODO: address conversion may be more complex than this
|
||||||
|
const originAssetBytes = zeroPad(
|
||||||
|
originChain === CHAIN_ID_SOLANA
|
||||||
|
? new PublicKey(originAsset).toBytes()
|
||||||
|
: arrayify(originAsset),
|
||||||
|
32
|
||||||
|
);
|
||||||
|
return tokenBridge.wrappedAsset(originChain, originAssetBytes);
|
||||||
|
}
|
|
@ -17,6 +17,7 @@
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
|
"downlevelIteration": true,
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"jsx": "react-jsx"
|
"jsx": "react-jsx"
|
||||||
},
|
},
|
||||||
|
|
|
@ -19,4 +19,4 @@ plugins:
|
||||||
- env=browser
|
- env=browser
|
||||||
- forceLong=string
|
- forceLong=string
|
||||||
- outputClientImpl=grpc-web
|
- outputClientImpl=grpc-web
|
||||||
- explorer/src/proto
|
- bridge_ui/src/proto
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
"": {
|
"": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"ts-proto": "^1.81.1"
|
"ts-proto": "^1.82.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@protobufjs/aspromise": {
|
"node_modules/@protobufjs/aspromise": {
|
||||||
|
@ -175,9 +175,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ts-proto": {
|
"node_modules/ts-proto": {
|
||||||
"version": "1.82.0",
|
"version": "1.82.3",
|
||||||
"resolved": "https://registry.npmjs.org/ts-proto/-/ts-proto-1.82.0.tgz",
|
"resolved": "https://registry.npmjs.org/ts-proto/-/ts-proto-1.82.3.tgz",
|
||||||
"integrity": "sha512-vo4QN4QhR0D4/+C/pSbRIVSV6U7dooNcuyW3SL9DvhKRQA4lnAbF5QBs77ge3JRi+aSZJm8MlzTNk7+e++fvvQ==",
|
"integrity": "sha512-ODveOXK2imsgTiqkBcJu9mIOklmCTSzs7Xu+mT8Xljwh3Wenhax7bhty+x2eO4J7AfNkikXH0Xs7K3lk3UT8VA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/object-hash": "^1.3.0",
|
"@types/object-hash": "^1.3.0",
|
||||||
|
@ -354,9 +354,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ts-proto": {
|
"ts-proto": {
|
||||||
"version": "1.82.0",
|
"version": "1.82.3",
|
||||||
"resolved": "https://registry.npmjs.org/ts-proto/-/ts-proto-1.82.0.tgz",
|
"resolved": "https://registry.npmjs.org/ts-proto/-/ts-proto-1.82.3.tgz",
|
||||||
"integrity": "sha512-vo4QN4QhR0D4/+C/pSbRIVSV6U7dooNcuyW3SL9DvhKRQA4lnAbF5QBs77ge3JRi+aSZJm8MlzTNk7+e++fvvQ==",
|
"integrity": "sha512-ODveOXK2imsgTiqkBcJu9mIOklmCTSzs7Xu+mT8Xljwh3Wenhax7bhty+x2eO4J7AfNkikXH0Xs7K3lk3UT8VA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/object-hash": "^1.3.0",
|
"@types/object-hash": "^1.3.0",
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "tooling for building web code from protobufs",
|
"description": "tooling for building web code from protobufs",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"ts-proto": "^1.81.1"
|
"ts-proto": "^1.82.3"
|
||||||
},
|
}
|
||||||
"dependencies": {}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue