bridge_ui: solana balances, begin wasm integration

Change-Id: Ifde4ec688ff678f46c1606e519fc36c0ece68fd4
This commit is contained in:
Evan Gray 2021-08-01 23:21:28 -04:00 committed by Hendrik Hofstadt
parent 591e68b5ee
commit b1a237db99
10 changed files with 554 additions and 53 deletions

28
bridge_ui/craco.config.js Normal file
View File

@ -0,0 +1,28 @@
const { addBeforeLoader, loaderByName } = require("@craco/craco");
module.exports = {
webpack: {
configure: (webpackConfig) => {
const wasmExtensionRegExp = /\.wasm$/;
webpackConfig.resolve.extensions.push(".wasm");
webpackConfig.module.rules.forEach((rule) => {
(rule.oneOf || []).forEach((oneOf) => {
if (oneOf.loader && oneOf.loader.indexOf("file-loader") >= 0) {
oneOf.exclude.push(wasmExtensionRegExp);
}
});
});
const wasmLoader = {
test: /\.wasm$/,
include: /node_modules\/(bridge|token-bridge)/,
loaders: ["wasm-loader"],
};
addBeforeLoader(webpackConfig, loaderByName("file-loader"), wasmLoader);
return webpackConfig;
},
},
};

View File

@ -5,7 +5,9 @@
"requires": true,
"packages": {
"": {
"name": "test_ui",
"version": "0.1.0",
"hasInstallScript": true,
"dependencies": {
"@material-ui/core": "^4.12.2",
"@metamask/detect-provider": "^1.2.0",
@ -13,16 +15,20 @@
"@solana/wallet-base": "^0.0.1",
"@solana/web3.js": "^1.22.0",
"@typechain/ethers-v5": "^7.0.1",
"bridge": "file:rust_modules\\core",
"ethers": "^5.4.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "4.0.3"
"react-scripts": "4.0.3",
"token-bridge": "file:rust_modules\\token"
},
"devDependencies": {
"@craco/craco": "^6.2.0",
"@openzeppelin/contracts": "^4.2.0",
"@truffle/hdwallet-provider": "^1.4.1",
"copy-dir": "^1.3.0",
"truffle": "^5.4.1"
"truffle": "^5.4.1",
"wasm-loader": "^1.3.0"
}
},
"node_modules/@apollo/client": {
@ -2005,6 +2011,27 @@
"ieee754": "^1.1.13"
}
},
"node_modules/@craco/craco": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/@craco/craco/-/craco-6.2.0.tgz",
"integrity": "sha512-kLc4GSdgR9D5JiZmSxtzbvBKcUFSJqMXImRjjYf5pacwiyAs3XfQwai7T+pExfLQNUnytgkL8jRFUJeYrkVr7g==",
"dev": true,
"dependencies": {
"cross-spawn": "^7.0.0",
"lodash": "^4.17.15",
"semver": "^7.3.2",
"webpack-merge": "^4.2.2"
},
"bin": {
"craco": "bin/craco.js"
},
"engines": {
"node": ">=6"
},
"peerDependencies": {
"react-scripts": "^4.0.0"
}
},
"node_modules/@csstools/convert-colors": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz",
@ -10222,6 +10249,10 @@
"node": ">=8"
}
},
"node_modules/bridge": {
"resolved": "rust_modules/core",
"link": true
},
"node_modules/brorand": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
@ -34482,6 +34513,10 @@
"node": ">=0.6"
}
},
"node_modules/token-bridge": {
"resolved": "rust_modules/token",
"link": true
},
"node_modules/tough-cookie": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz",
@ -35551,6 +35586,72 @@
"makeerror": "1.0.x"
}
},
"node_modules/wasm-dce": {
"version": "1.0.2",
"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",
"@babel/types": "^7.0.0-beta.39",
"babylon": "^7.0.0-beta.39",
"webassembly-interpreter": "0.0.30"
}
},
"node_modules/wasm-dce/node_modules/babylon": {
"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,
"bin": {
"babylon": "bin/babylon.js"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/wasm-loader": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/wasm-loader/-/wasm-loader-1.3.0.tgz",
"integrity": "sha512-R4s75XH+o8qM+WaRrAU9S2rbAMDzob18/S3V8R9ZoFpZkPWLAohWWlzWAp1ybeTkOuuku/X1zJtxiV0pBYxZww==",
"dev": true,
"dependencies": {
"loader-utils": "^1.1.0",
"wasm-dce": "^1.0.0"
},
"peerDependencies": {
"wasm-dce": "1.x"
}
},
"node_modules/wasm-loader/node_modules/json5": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
"dev": true,
"dependencies": {
"minimist": "^1.2.0"
},
"bin": {
"json5": "lib/cli.js"
}
},
"node_modules/wasm-loader/node_modules/loader-utils": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
"integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
"dev": true,
"dependencies": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^1.0.1"
},
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/watchpack": {
"version": "1.7.5",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz",
@ -36335,6 +36436,46 @@
"xhr-request-promise": "^0.1.2"
}
},
"node_modules/webassembly-floating-point-hex-parser": {
"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,
"engines": {
"node": "*"
}
},
"node_modules/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,
"dependencies": {
"@babel/code-frame": "^7.0.0-beta.36",
"long": "^3.2.0",
"webassembly-floating-point-hex-parser": "0.1.2"
},
"bin": {
"wasm": "lib/bin/repl.js",
"wasm2wast": "lib/bin/wasm2wast.js",
"wasmast": "lib/bin/wasmast.js",
"wasmdump": "lib/bin/wasmdump.js",
"wasmrun": "lib/bin/wasmrun.js",
"wastast": "lib/bin/wastast.js"
}
},
"node_modules/webassembly-interpreter/node_modules/long": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz",
"integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=",
"dev": true,
"peer": true,
"engines": {
"node": ">=0.6"
}
},
"node_modules/webidl-conversions": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz",
@ -37076,6 +37217,15 @@
"node": ">= 4.0.0"
}
},
"node_modules/webpack-merge": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz",
"integrity": "sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==",
"dev": true,
"dependencies": {
"lodash": "^4.17.15"
}
},
"node_modules/webpack-sources": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz",
@ -38362,6 +38512,14 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true,
"optional": true
},
"rust_modules/core": {
"name": "bridge",
"version": "0.1.0"
},
"rust_modules/token": {
"name": "token-bridge",
"version": "0.1.0"
}
},
"dependencies": {
@ -39743,6 +39901,18 @@
}
}
},
"@craco/craco": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/@craco/craco/-/craco-6.2.0.tgz",
"integrity": "sha512-kLc4GSdgR9D5JiZmSxtzbvBKcUFSJqMXImRjjYf5pacwiyAs3XfQwai7T+pExfLQNUnytgkL8jRFUJeYrkVr7g==",
"dev": true,
"requires": {
"cross-spawn": "^7.0.0",
"lodash": "^4.17.15",
"semver": "^7.3.2",
"webpack-merge": "^4.2.2"
}
},
"@csstools/convert-colors": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz",
@ -46437,6 +46607,9 @@
"fill-range": "^7.0.1"
}
},
"bridge": {
"version": "file:rust_modules/core"
},
"brorand": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
@ -66128,6 +66301,9 @@
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
},
"token-bridge": {
"version": "file:rust_modules/token"
},
"tough-cookie": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz",
@ -66999,6 +67175,60 @@
"makeerror": "1.0.x"
}
},
"wasm-dce": {
"version": "1.0.2",
"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",
"@babel/types": "^7.0.0-beta.39",
"babylon": "^7.0.0-beta.39",
"webassembly-interpreter": "0.0.30"
},
"dependencies": {
"babylon": {
"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
}
}
},
"wasm-loader": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/wasm-loader/-/wasm-loader-1.3.0.tgz",
"integrity": "sha512-R4s75XH+o8qM+WaRrAU9S2rbAMDzob18/S3V8R9ZoFpZkPWLAohWWlzWAp1ybeTkOuuku/X1zJtxiV0pBYxZww==",
"dev": true,
"requires": {
"loader-utils": "^1.1.0"
},
"dependencies": {
"json5": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
"dev": true,
"requires": {
"minimist": "^1.2.0"
}
},
"loader-utils": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
"integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
"dev": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^1.0.1"
}
}
}
},
"watchpack": {
"version": "1.7.5",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz",
@ -67692,6 +67922,34 @@
}
}
},
"webassembly-floating-point-hex-parser": {
"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
},
"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",
"webassembly-floating-point-hex-parser": "0.1.2"
},
"dependencies": {
"long": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz",
"integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=",
"dev": true,
"peer": true
}
}
},
"webidl-conversions": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz",
@ -68494,6 +68752,15 @@
}
}
},
"webpack-merge": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz",
"integrity": "sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==",
"dev": true,
"requires": {
"lodash": "^4.17.15"
}
},
"webpack-sources": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz",

View File

@ -9,16 +9,18 @@
"@solana/wallet-base": "^0.0.1",
"@solana/web3.js": "^1.22.0",
"@typechain/ethers-v5": "^7.0.1",
"bridge": "file:rust_modules\\core",
"ethers": "^5.4.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "4.0.3"
"react-scripts": "4.0.3",
"token-bridge": "file:rust_modules\\token"
},
"scripts": {
"postinstall": "npm run build-contracts",
"start": "react-scripts start",
"build": "npm run build-contracts && react-scripts build",
"test": "npm run build-contracts && react-scripts test",
"start": "craco start",
"build": "npm run build-contracts && craco build",
"test": "npm run build-contracts && craco test",
"eject": "react-scripts eject",
"build-contracts": "npm run build --prefix ../ethereum && node scripts/copyContracts.js && typechain --target=ethers-v5 --out-dir=src/ethers-contracts contracts/*.json"
},
@ -41,9 +43,11 @@
]
},
"devDependencies": {
"@craco/craco": "^6.2.0",
"@openzeppelin/contracts": "^4.2.0",
"@truffle/hdwallet-provider": "^1.4.1",
"copy-dir": "^1.3.0",
"truffle": "^5.4.1"
"truffle": "^5.4.1",
"wasm-loader": "^1.3.0"
}
}

View File

@ -0,0 +1,50 @@
import { Typography } from "@material-ui/core";
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
import { useSolanaWallet } from "../contexts/SolanaWalletContext";
import useEthereumBalance from "../hooks/useEthereumBalance";
import useSolanaBalance from "../hooks/useSolanaBalance";
import { ChainId, CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "../utils/consts";
import EthereumSignerKey from "./EthereumSignerKey";
import SolanaWalletKey from "./SolanaWalletKey";
function KeyAndBalance({
chainId,
tokenAddress,
}: {
chainId: ChainId;
tokenAddress?: string;
}) {
// TODO: more generic way to get balance
const provider = useEthereumProvider();
const ethBalance = useEthereumBalance(
tokenAddress,
provider,
chainId === CHAIN_ID_ETH
);
const { wallet: solWallet } = useSolanaWallet();
const solPK = solWallet?.publicKey;
const solBalance = useSolanaBalance(
tokenAddress,
solPK,
chainId === CHAIN_ID_SOLANA
);
if (chainId === CHAIN_ID_ETH) {
return (
<>
<EthereumSignerKey />
<Typography>{ethBalance}</Typography>
</>
);
}
if (chainId === CHAIN_ID_SOLANA) {
return (
<>
<SolanaWalletKey />
<Typography>{solBalance}</Typography>
</>
);
}
return null;
}
export default KeyAndBalance;

View File

@ -10,16 +10,21 @@ import { useCallback, useState } from "react";
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
import { useSolanaWallet } from "../contexts/SolanaWalletContext";
import useEthereumBalance from "../hooks/useEthereumBalance";
import useSolanaBalance from "../hooks/useSolanaBalance";
import {
ChainId,
CHAINS,
CHAINS_BY_ID,
CHAIN_ID_ETH,
CHAIN_ID_SOLANA,
ETH_TEST_TOKEN_ADDRESS,
SOL_TEST_TOKEN_ADDRESS,
} from "../utils/consts";
import transferFrom from "../utils/transferFrom";
import EthereumSignerKey from "./EthereumSignerKey";
import SolanaWalletKey from "./SolanaWalletKey";
import transferFrom, {
transferFromEth,
transferFromSolana,
} from "../utils/transferFrom";
import KeyAndBalance from "./KeyAndBalance";
const useStyles = makeStyles((theme) => ({
transferBox: {
@ -57,6 +62,13 @@ function Transfer() {
const handleFromChange = useCallback(
(event) => {
setFromChain(event.target.value);
// TODO: remove or check env - for testing purposes
if (event.target.value === CHAIN_ID_ETH) {
setAssetAddress(ETH_TEST_TOKEN_ADDRESS);
}
if (event.target.value === CHAIN_ID_SOLANA) {
setAssetAddress(SOL_TEST_TOKEN_ADDRESS);
}
if (toChain === event.target.value) {
setToChain(fromChain);
}
@ -68,6 +80,13 @@ function Transfer() {
setToChain(event.target.value);
if (fromChain === event.target.value) {
setFromChain(toChain);
// TODO: remove or check env - for testing purposes
if (toChain === CHAIN_ID_ETH) {
setAssetAddress(ETH_TEST_TOKEN_ADDRESS);
}
if (toChain === CHAIN_ID_SOLANA) {
setAssetAddress(SOL_TEST_TOKEN_ADDRESS);
}
}
},
[fromChain, toChain]
@ -79,22 +98,56 @@ function Transfer() {
setAmount(event.target.value);
}, []);
const provider = useEthereumProvider();
const ethBalance = useEthereumBalance(assetAddress, provider);
const { wallet } = useSolanaWallet();
const solPK = wallet?.publicKey?.toBytes();
const solPK = wallet?.publicKey;
// TODO: dynamically get "to" wallet
const handleClick = useCallback(() => {
// TODO: more generic way of calling these
if (transferFrom[fromChain]) {
transferFrom[fromChain](provider, assetAddress, amount, toChain, solPK);
if (
fromChain === CHAIN_ID_ETH &&
transferFrom[fromChain] === transferFromEth
) {
transferFromEth(
provider,
assetAddress,
amount,
toChain,
solPK?.toBytes()
);
}
if (
fromChain === CHAIN_ID_SOLANA &&
transferFrom[fromChain] === transferFromSolana
) {
transferFromSolana(
solPK?.toString(),
assetAddress,
amount,
provider,
toChain
);
}
}
}, [fromChain, provider, solPK, assetAddress, amount, toChain]);
// update this as we develop, just setting expectations with the button state
const ethBalance = useEthereumBalance(
assetAddress,
provider,
fromChain === CHAIN_ID_ETH
);
const solBalance = useSolanaBalance(
assetAddress,
solPK,
fromChain === CHAIN_ID_SOLANA
);
const balance = Number(ethBalance) || Number(solBalance);
const isTransferImplemented = !!transferFrom[fromChain];
const isProviderConnected = !!provider;
const isRecipientAvailable = !!solPK;
const isAddressDefined = !!assetAddress;
const isAmountPositive = Number(amount) > 0; // TODO: this needs per-chain, bn parsing
const isBalanceAtLeastAmount = Number(ethBalance) >= Number(amount); // TODO: ditto
const isBalanceAtLeastAmount = balance >= Number(amount); // TODO: ditto
const canAttemptTransfer =
isTransferImplemented &&
isProviderConnected &&
@ -106,7 +159,7 @@ function Transfer() {
<div className={classes.transferBox}>
<Grid container>
<Grid item xs={4}>
<Typography>To</Typography>
<Typography>From</Typography>
<TextField
select
fullWidth
@ -119,14 +172,13 @@ function Transfer() {
</MenuItem>
))}
</TextField>
<EthereumSignerKey />
<Typography>{ethBalance}</Typography>
<KeyAndBalance chainId={fromChain} tokenAddress={assetAddress} />
</Grid>
<Grid item xs={4} className={classes.arrow}>
&rarr;
</Grid>
<Grid item xs={4}>
<Typography>From</Typography>
<Typography>To</Typography>
<TextField select fullWidth value={toChain} onChange={handleToChange}>
{CHAINS.map(({ id, name }) => (
<MenuItem key={id} value={id}>
@ -134,7 +186,8 @@ function Transfer() {
</MenuItem>
))}
</TextField>
<SolanaWalletKey />
{/* TODO: determine "to" token address */}
<KeyAndBalance chainId={toChain} />
</Grid>
</Grid>
<TextField
@ -164,7 +217,7 @@ function Transfer() {
{canAttemptTransfer ? null : (
<Typography variant="body2" color="error">
{!isTransferImplemented
? `Transfer is not yet implemented for ${CHAINS[fromChain]}`
? `Transfer is not yet implemented for ${CHAINS_BY_ID[fromChain].name}`
: !isProviderConnected
? "The source wallet is not connected"
: !isRecipientAvailable
@ -175,8 +228,6 @@ function Transfer() {
? "The amount must be positive"
: !isBalanceAtLeastAmount
? "The amount may not be greater than the balance"
: !isBalanceAtLeastAmount
? "The amount may not be greater than the balance"
: ""}
</Typography>
)}

View File

@ -3,35 +3,46 @@ import { formatUnits } from "ethers/lib/utils";
import { useEffect, useState } from "react";
import { TokenImplementation__factory } from "../ethers-contracts";
function useEthereumBalance(address: string, provider?: ethers.providers.Web3Provider) {
function useEthereumBalance(
address: string | undefined,
provider: ethers.providers.Web3Provider | undefined,
shouldCalculate?: boolean
) {
//TODO: should this check allowance too or subtract allowance?
const [balance, setBalance] = useState<string>('')
useEffect(()=>{
if (!address || !provider) {
setBalance('')
return
const [balance, setBalance] = useState<string>("");
useEffect(() => {
if (!address || !provider || !shouldCalculate) {
setBalance("");
return;
}
let cancelled = false
let cancelled = false;
const token = TokenImplementation__factory.connect(address, provider);
token.decimals().then((decimals) => {
console.log(decimals);
provider
?.getSigner()
.getAddress()
.then((pk) => {
console.log(pk)
token.balanceOf(pk).then((n) => {
if (!cancelled) {
setBalance(formatUnits(n,decimals))
}
});
token
.decimals()
.then((decimals) => {
console.log(decimals);
provider
?.getSigner()
.getAddress()
.then((pk) => {
console.log(pk);
token.balanceOf(pk).then((n) => {
if (!cancelled) {
setBalance(formatUnits(n, decimals));
}
});
});
})
.catch(() => {
if (!cancelled) {
setBalance("");
}
});
});
return () => {
cancelled = true
}
},[address, provider])
return balance
cancelled = true;
};
}, [address, provider, shouldCalculate]);
return balance;
}
export default useEthereumBalance
export default useEthereumBalance;

View File

@ -0,0 +1,47 @@
import { Connection, PublicKey } from "@solana/web3.js";
import { useEffect, useState } from "react";
import { SOLANA_HOST } from "../utils/consts";
function useSolanaBalance(
tokenAddress: string | undefined,
ownerAddress: PublicKey | null | undefined,
shouldCalculate?: boolean
) {
//TODO: should connection happen in a context?
const [balance, setBalance] = useState<string>("");
useEffect(() => {
if (!tokenAddress || !ownerAddress || !shouldCalculate) {
setBalance("");
return;
}
let cancelled = false;
const connection = new Connection(SOLANA_HOST);
connection
.getParsedTokenAccountsByOwner(ownerAddress, {
mint: new PublicKey(tokenAddress),
})
.then(({ value }) => {
if (!cancelled) {
if (value.length) {
console.log(value[0].account.data.parsed);
setBalance(
value[0].account.data.parsed?.info?.tokenAmount?.uiAmountString
);
} else {
setBalance("0");
}
}
})
.catch(() => {
if (!cancelled) {
setBalance("");
}
});
return () => {
cancelled = true;
};
}, [tokenAddress, ownerAddress, shouldCalculate]);
return balance;
}
export default useSolanaBalance;

View File

@ -25,6 +25,8 @@ export const CHAINS = [
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 = "0x0290FB167208Af455bB137780163b7B7a9a10C16"
export const ETH_TOKEN_BRIDGE_ADDRESS = "0xe982e462b094850f12af94d21d470e21be9d0e9c"

View File

@ -1,12 +1,12 @@
import { ethers } from "ethers";
import { formatUnits, parseUnits } from "ethers/lib/utils";
import { arrayify, formatUnits, parseUnits } from "ethers/lib/utils";
import { Bridge__factory, TokenImplementation__factory } from "../ethers-contracts";
import { ChainId, CHAIN_ID_ETH, ETH_TOKEN_BRIDGE_ADDRESS } from "./consts";
import { ChainId, CHAIN_ID_ETH, CHAIN_ID_SOLANA, ETH_TOKEN_BRIDGE_ADDRESS, SOL_TOKEN_BRIDGE_ADDRESS } from "./consts";
// TODO: this should probably be extended from the context somehow so that the signatures match
// TODO: allow for / handle cancellation?
// TODO: overall better input checking and error handling
function transferFromEth(provider: ethers.providers.Web3Provider | undefined, tokenAddress: string, amount: string, recipientChain: ChainId, recipientAddress: Uint8Array | undefined) {
export function transferFromEth(provider: ethers.providers.Web3Provider | undefined, tokenAddress: string, amount: string, recipientChain: ChainId, recipientAddress: Uint8Array | undefined) {
if (!provider || !recipientAddress) return;
const signer = provider.getSigner();
if (!signer) return;
@ -59,8 +59,41 @@ function transferFromEth(provider: ethers.providers.Web3Provider | undefined, to
});
}
// 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 function transferFromSolana(fromAddress: string | undefined, tokenAddress: string, amount: string, targetProvider: ethers.providers.Web3Provider | undefined, targetChain: ChainId) {
if (!fromAddress || !targetProvider) return;
const targetSigner = targetProvider.getSigner();
if (!targetSigner) return;
targetSigner.getAddress().then(targetAddressStr => {
const targetAddress = arrayify(targetAddressStr)
const nonceConst = Math.random() * 100000;
const nonceBuffer = Buffer.alloc(4);
nonceBuffer.writeUInt32LE(nonceConst, 0);
const nonce = nonceBuffer.readUInt32LE(0)
// TODO: check decimals
// should we avoid BigInt?
const amountParsed = BigInt(amount)
const fee = BigInt(0) // for now, this won't do anything, we may add later
console.log('bridge:',SOL_TOKEN_BRIDGE_ADDRESS)
console.log('from:',fromAddress)
console.log('token:',tokenAddress)
console.log('nonce:',nonce)
console.log('amount:',amountParsed)
console.log('fee:',fee)
console.log('target:',targetAddressStr,targetAddress)
console.log('chain:',targetChain)
// TODO: program_id vs bridge_id?
import("token-bridge").then(({transfer_native_ix})=>{
const ix = transfer_native_ix(SOL_TOKEN_BRIDGE_ADDRESS,SOL_TOKEN_BRIDGE_ADDRESS,fromAddress,fromAddress,tokenAddress,nonce,amountParsed,fee,targetAddress,targetChain)
console.log(ix)
})
})
}
const transferFrom = {
[CHAIN_ID_ETH]: transferFromEth
[CHAIN_ID_ETH]: transferFromEth,
[CHAIN_ID_SOLANA]: transferFromSolana
}
export default transferFrom

View File

@ -10,12 +10,20 @@ set -euo pipefail
-v $(pwd)/../bridge_ui/rust_modules/core:/usr/src/bridge/bridge/program/pkg \
-e EMITTER_ADDRESS=11111111111111111111111111111115 \
localhost/certusone/wormhole-wasmpack:latest \
/usr/local/cargo/bin/wasm-pack build --target bundler -- --features wasm
docker run --rm -it --workdir /usr/src/bridge/bridge/program \
-v $(pwd)/../clients/solana/pkg:/usr/src/bridge/bridge/program/pkg \
-e EMITTER_ADDRESS=11111111111111111111111111111115 \
localhost/certusone/wormhole-wasmpack:latest \
/usr/local/cargo/bin/wasm-pack build --target nodejs -- --features wasm
cp $(pwd)/../bridge_ui/rust_modules/core/. $(pwd)/../clients/solana/pkg/ -R
docker run --rm -it --workdir /usr/src/bridge/modules/token_bridge/program \
-v $(pwd)/../bridge_ui/rust_modules/token:/usr/src/bridge/modules/token_bridge/program/pkg \
-e EMITTER_ADDRESS=11111111111111111111111111111115 \
localhost/certusone/wormhole-wasmpack:latest \
/usr/local/cargo/bin/wasm-pack build --target bundler -- --features wasm
docker run --rm -it --workdir /usr/src/bridge/modules/token_bridge/program \
-v $(pwd)/../clients/token_bridge/pkg:/usr/src/bridge/modules/token_bridge/program/pkg \
-e EMITTER_ADDRESS=11111111111111111111111111111115 \
localhost/certusone/wormhole-wasmpack:latest \
/usr/local/cargo/bin/wasm-pack build --target nodejs -- --features wasm
cp $(pwd)/../bridge_ui/rust_modules/. $(pwd)/../clients/token_bridge/pkg -R
)