sdk/js: begin

Change-Id: Ia0c676c051deae20449a15b4556160a8d6e24386
This commit is contained in:
Evan Gray 2021-08-15 18:17:11 -04:00
parent f04c9e5d45
commit 9f8083566b
36 changed files with 2447 additions and 101 deletions

View File

@ -9,6 +9,7 @@
"version": "0.1.0",
"hasInstallScript": true,
"dependencies": {
"@certusone/wormhole-sdk": "file:..\\sdk\\js",
"@improbable-eng/grpc-web": "^0.13.0",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.11.2",
@ -39,6 +40,23 @@
"wasm-loader": "^1.3.0"
}
},
"../sdk/js": {
"version": "0.0.1",
"license": "Apache-2.0",
"dependencies": {
"@improbable-eng/grpc-web": "^0.14.0",
"@solana/web3.js": "^1.24.0",
"protobufjs": "^6.11.2",
"rxjs": "^7.3.0"
},
"devDependencies": {
"@types/long": "^4.0.1",
"prettier": "^2.3.2",
"tslint": "^6.1.3",
"tslint-config-prettier": "^1.18.0",
"typescript": "^4.3.5"
}
},
"node_modules/@apollo/client": {
"version": "3.3.21",
"resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.3.21.tgz",
@ -1968,6 +1986,10 @@
"resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
"integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw=="
},
"node_modules/@certusone/wormhole-sdk": {
"resolved": "../sdk/js",
"link": true
},
"node_modules/@cnakazawa/watch": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz",
@ -40127,6 +40149,20 @@
"resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
"integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw=="
},
"@certusone/wormhole-sdk": {
"version": "file:../sdk/js",
"requires": {
"@improbable-eng/grpc-web": "^0.14.0",
"@solana/web3.js": "^1.24.0",
"@types/long": "^4.0.1",
"prettier": "^2.3.2",
"protobufjs": "^6.11.2",
"rxjs": "^7.3.0",
"tslint": "^6.1.3",
"tslint-config-prettier": "^1.18.0",
"typescript": "^4.3.5"
}
},
"@cnakazawa/watch": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz",

View File

@ -3,6 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@certusone/wormhole-sdk": "file:..\\sdk\\js",
"@improbable-eng/grpc-web": "^0.13.0",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.11.2",

View File

@ -1 +0,0 @@
proto

View File

@ -1,6 +1,8 @@
import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
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 useAttestSignedVAA from "../../hooks/useAttestSignedVAA";
import { setIsCreating } from "../../store/attestSlice";
@ -8,12 +10,10 @@ import {
selectAttestIsCreating,
selectAttestTargetChain,
} from "../../store/selectors";
import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "../../utils/consts";
import createWrappedOn, {
createWrappedOnEth,
createWrappedOnSolana,
} from "../../utils/createWrappedOn";
import { useEthereumProvider } from "../../contexts/EthereumProviderContext";
const useStyles = makeStyles((theme) => ({
transferButton: {

View File

@ -1,3 +1,4 @@
import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
import { Button, CircularProgress, makeStyles } from "@material-ui/core";
import { useCallback } from "react";
import { useDispatch, useSelector } from "react-redux";
@ -16,7 +17,6 @@ import attestFrom, {
attestFromEth,
attestFromSolana,
} from "../../utils/attestFrom";
import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "../../utils/consts";
const useStyles = makeStyles((theme) => ({
transferButton: {

View File

@ -1,5 +1,9 @@
import {
ChainId,
CHAIN_ID_ETH,
CHAIN_ID_SOLANA,
} from "@certusone/wormhole-sdk";
import { Typography } from "@material-ui/core";
import { ChainId, CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "../utils/consts";
import EthereumSignerKey from "./EthereumSignerKey";
import SolanaWalletKey from "./SolanaWalletKey";

View File

@ -1,3 +1,4 @@
import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
import { Button, CircularProgress, makeStyles } from "@material-ui/core";
import { useCallback } from "react";
import { useDispatch, useSelector } from "react-redux";
@ -12,7 +13,6 @@ import {
selectTransferTargetChain,
} from "../../store/selectors";
import { setIsRedeeming } from "../../store/transferSlice";
import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "../../utils/consts";
import redeemOn, { redeemOnEth, redeemOnSolana } from "../../utils/redeemOn";
const useStyles = makeStyles((theme) => ({

View File

@ -1,3 +1,4 @@
import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
import { Button, CircularProgress, makeStyles } from "@material-ui/core";
import {
ASSOCIATED_TOKEN_PROGRAM_ID,
@ -26,7 +27,6 @@ import {
} from "../../store/selectors";
import { setIsSending, setSignedVAAHex } from "../../store/transferSlice";
import { uint8ArrayToHex } from "../../utils/array";
import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "../../utils/consts";
import transferFrom, {
transferFromEth,
transferFromSolana,

View File

@ -1,3 +1,4 @@
import { CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
import { Button, makeStyles, MenuItem, TextField } from "@material-ui/core";
import { PublicKey } from "@solana/web3.js";
import { useCallback, useMemo } from "react";
@ -13,7 +14,7 @@ import {
} from "../../store/selectors";
import { incrementStep, setTargetChain } from "../../store/transferSlice";
import { hexToUint8Array } from "../../utils/array";
import { CHAINS, CHAIN_ID_SOLANA } from "../../utils/consts";
import { CHAINS } from "../../utils/consts";
import KeyAndBalance from "../KeyAndBalance";
const useStyles = makeStyles((theme) => ({

View File

@ -1,3 +1,4 @@
import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
@ -6,7 +7,6 @@ import {
selectTransferSourceChain,
} from "../store/selectors";
import { setSourceWormholeWrappedInfo } from "../store/transferSlice";
import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "../utils/consts";
import {
getOriginalAssetEth,
getOriginalAssetSol,

View File

@ -1,3 +1,4 @@
import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
@ -10,7 +11,6 @@ import {
selectTransferTargetChain,
} from "../store/selectors";
import { setTargetAsset } from "../store/transferSlice";
import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "../utils/consts";
import {
getForeignAssetEth,
getForeignAssetSol,

View File

@ -1,3 +1,4 @@
import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
import { Connection, PublicKey } from "@solana/web3.js";
import { formatUnits } from "ethers/lib/utils";
import { useEffect } from "react";
@ -16,7 +17,7 @@ import {
setTargetParsedTokenAccount,
} from "../store/transferSlice";
import { hexToUint8Array } from "../utils/array";
import { CHAIN_ID_ETH, CHAIN_ID_SOLANA, SOLANA_HOST } from "../utils/consts";
import { SOLANA_HOST } from "../utils/consts";
function createParsedTokenAccount(
publicKey: PublicKey | undefined,

View File

@ -1,58 +0,0 @@
import {
AccountMeta,
PublicKey,
TransactionInstruction,
} from "@solana/web3.js";
import {
GrpcWebImpl,
PublicrpcClientImpl,
} from "../proto/publicrpc/v1/publicrpc";
import { ChainId } from "../utils/consts";
// begin from clients\solana\main.ts
export function ixFromRust(data: any): TransactionInstruction {
let keys: Array<AccountMeta> = data.accounts.map(accountMetaFromRust);
return new TransactionInstruction({
programId: new PublicKey(data.program_id),
data: Buffer.from(data.data),
keys: keys,
});
}
function accountMetaFromRust(meta: any): AccountMeta {
return {
pubkey: new PublicKey(meta.pubkey),
isSigner: meta.is_signer,
isWritable: meta.is_writable,
};
}
// end from clients\solana\main.ts
export async function getSignedVAA(
emitterChain: ChainId,
emitterAddress: string,
sequence: string
) {
const rpc = new GrpcWebImpl("http://localhost:8080", {});
const api = new PublicrpcClientImpl(rpc);
// TODO: potential infinite loop, support cancellation?
let result;
while (!result) {
console.log("wait 1 second");
await new Promise((resolve) => setTimeout(resolve, 1000));
console.log("check for signed vaa", emitterChain, emitterAddress, sequence);
try {
result = await api.GetSignedVAA({
messageId: {
emitterChain,
emitterAddress,
sequence,
},
});
console.log(result);
} catch (e) {
console.log(e);
}
}
return result;
}

View File

@ -1,8 +1,10 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import {
ChainId,
CHAIN_ID_ETH,
CHAIN_ID_SOLANA,
} from "@certusone/wormhole-sdk";
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import {
ETH_TEST_TOKEN_ADDRESS,
SOL_TEST_TOKEN_ADDRESS,
} from "../utils/consts";

View File

@ -1,7 +1,7 @@
import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
import { ethers } from "ethers";
import { parseUnits } from "ethers/lib/utils";
import { RootState } from ".";
import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "../utils/consts";
/*
* Attest

View File

@ -1,8 +1,10 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import {
ChainId,
CHAIN_ID_ETH,
CHAIN_ID_SOLANA,
} from "@certusone/wormhole-sdk";
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import {
ETH_TEST_TOKEN_ADDRESS,
SOL_TEST_TOKEN_ADDRESS,
} from "../utils/consts";

View File

@ -1,3 +1,5 @@
import { getSignedVAA, ixFromRust } from "@certusone/wormhole-sdk";
import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
import Wallet from "@project-serum/sol-wallet-adapter";
import {
Connection,
@ -9,10 +11,7 @@ import {
import { ethers } from "ethers";
import { arrayify, zeroPad } from "ethers/lib/utils";
import { Bridge__factory, Implementation__factory } from "../ethers-contracts";
import { getSignedVAA, ixFromRust } from "../sdk";
import {
CHAIN_ID_ETH,
CHAIN_ID_SOLANA,
ETH_BRIDGE_ADDRESS,
ETH_TOKEN_BRIDGE_ADDRESS,
SOLANA_HOST,

View File

@ -1,10 +1,12 @@
import {
ChainId,
CHAIN_ID_BSC,
CHAIN_ID_ETH,
CHAIN_ID_SOLANA,
CHAIN_ID_TERRA,
} from "@certusone/wormhole-sdk";
import { getAddress } from "ethers/lib/utils";
export type ChainId = 1 | 2 | 3 | 4;
export const CHAIN_ID_SOLANA: ChainId = 1;
export const CHAIN_ID_ETH: ChainId = 2;
export const CHAIN_ID_TERRA: ChainId = 3;
export const CHAIN_ID_BSC: ChainId = 4;
export interface ChainInfo {
id: ChainId;
name: string;

View File

@ -1,17 +1,16 @@
import Wallet from "@project-serum/sol-wallet-adapter";
import { Connection, PublicKey, Transaction } from "@solana/web3.js";
import { ixFromRust } from "../sdk";
import {
CHAIN_ID_SOLANA,
SOLANA_HOST,
SOL_BRIDGE_ADDRESS,
SOL_TOKEN_BRIDGE_ADDRESS,
ETH_TOKEN_BRIDGE_ADDRESS,
CHAIN_ID_ETH,
} from "./consts";
import { ethers } from "ethers";
import { Bridge__factory } from "../ethers-contracts";
import { postVaa } from "./postVaa";
import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
import { ixFromRust } from "@certusone/wormhole-sdk";
export async function createWrappedOnEth(
provider: ethers.providers.Web3Provider | undefined,

View File

@ -1,10 +1,9 @@
import { ChainId, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
import { Connection, PublicKey } from "@solana/web3.js";
import { ethers } from "ethers";
import { arrayify, isHexString, zeroPad } from "ethers/lib/utils";
import { Bridge__factory } from "../ethers-contracts";
import {
ChainId,
CHAIN_ID_SOLANA,
ETH_TOKEN_BRIDGE_ADDRESS,
SOLANA_HOST,
SOL_TOKEN_BRIDGE_ADDRESS,

View File

@ -1,15 +1,14 @@
import {
ChainId,
CHAIN_ID_ETH,
CHAIN_ID_SOLANA,
} from "@certusone/wormhole-sdk";
import { Connection, PublicKey } from "@solana/web3.js";
import { ethers } from "ethers";
import { arrayify } from "ethers/lib/utils";
import { TokenImplementation__factory } from "../ethers-contracts";
import { uint8ArrayToHex } from "./array";
import {
ChainId,
CHAIN_ID_ETH,
CHAIN_ID_SOLANA,
SOLANA_HOST,
SOL_TOKEN_BRIDGE_ADDRESS,
} from "./consts";
import { SOLANA_HOST, SOL_TOKEN_BRIDGE_ADDRESS } from "./consts";
import { getIsWrappedAssetEth } from "./getIsWrappedAsset";
export interface WormholeWrappedInfo {

View File

@ -6,7 +6,7 @@ import {
TransactionInstruction,
} from "@solana/web3.js";
import Wallet from "@project-serum/sol-wallet-adapter";
import { ixFromRust } from "../sdk";
import { ixFromRust } from "@certusone/wormhole-sdk";
export async function postVaa(
connection: Connection,

View File

@ -1,8 +1,6 @@
import { ethers } from "ethers";
import { Bridge__factory } from "../ethers-contracts";
import {
CHAIN_ID_ETH,
CHAIN_ID_SOLANA,
ETH_TOKEN_BRIDGE_ADDRESS,
SOL_BRIDGE_ADDRESS,
SOL_TOKEN_BRIDGE_ADDRESS,
@ -11,12 +9,13 @@ import {
import Wallet from "@project-serum/sol-wallet-adapter";
import { Connection, PublicKey, Transaction } from "@solana/web3.js";
import { postVaa } from "./postVaa";
import { ixFromRust } from "../sdk";
import {
ASSOCIATED_TOKEN_PROGRAM_ID,
Token,
TOKEN_PROGRAM_ID,
} from "@solana/spl-token";
import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
import { ixFromRust } from "@certusone/wormhole-sdk";
export async function redeemOnEth(
provider: ethers.providers.Web3Provider | undefined,

View File

@ -1,3 +1,9 @@
import { getSignedVAA, ixFromRust } from "@certusone/wormhole-sdk";
import {
ChainId,
CHAIN_ID_ETH,
CHAIN_ID_SOLANA,
} from "@certusone/wormhole-sdk";
import Wallet from "@project-serum/sol-wallet-adapter";
import { Token, TOKEN_PROGRAM_ID } from "@solana/spl-token";
import {
@ -14,12 +20,8 @@ import {
Implementation__factory,
TokenImplementation__factory,
} from "../ethers-contracts";
import { getSignedVAA, ixFromRust } from "../sdk";
import { hexToUint8Array } from "./array";
import {
ChainId,
CHAIN_ID_ETH,
CHAIN_ID_SOLANA,
ETH_BRIDGE_ADDRESS,
ETH_TOKEN_BRIDGE_ADDRESS,
SOLANA_HOST,

View File

@ -11,7 +11,7 @@ plugins:
- outputClientImpl=grpc-web
- explorer/src/proto
- name: tsproto
out: bridge_ui/src/proto
out: sdk/js/src/proto
path: tools/node_modules/.bin/protoc-gen-ts_proto
opt:
- paths=source_relative

33
sdk/js/.gitignore vendored Normal file
View File

@ -0,0 +1,33 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# ethereum contracts
/contracts
/src/ethers-contracts
# tsproto output
/src/proto
# build
/lib

2193
sdk/js/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

47
sdk/js/package.json Normal file
View File

@ -0,0 +1,47 @@
{
"name": "@certusone/wormhole-sdk",
"version": "0.0.1",
"description": "SDK for interacting with Wormhole",
"homepage": "https://wormholenetwork.com",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"files": [
"lib/**/*"
],
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "tsc",
"format": "prettier --write \"src/**/*.ts\"",
"lint": "tslint -p tsconfig.json",
"prepare": "npm run build",
"prepublishOnly": "npm test && npm run lint",
"preversion": "npm run lint",
"version": "npm run format && git add -A src",
"postversion": "git push && git push --tags"
},
"keywords": [
"wormhole",
"bridge",
"token",
"sdk",
"solana",
"ethereum",
"terra",
"bsc"
],
"author": "certusone",
"license": "Apache-2.0",
"devDependencies": {
"@types/long": "^4.0.1",
"prettier": "^2.3.2",
"tslint": "^6.1.3",
"tslint-config-prettier": "^1.18.0",
"typescript": "^4.3.5"
},
"dependencies": {
"@improbable-eng/grpc-web": "^0.14.0",
"@solana/web3.js": "^1.24.0",
"protobufjs": "^6.11.2",
"rxjs": "^7.3.0"
}
}

2
sdk/js/src/index.ts Normal file
View File

@ -0,0 +1,2 @@
export * from "./rpc";
export * from "./utils";

View File

@ -0,0 +1,31 @@
import { ChainId } from "../utils/consts";
import {
GrpcWebImpl,
PublicrpcClientImpl,
} from "../proto/publicrpc/v1/publicrpc";
export async function getSignedVAA(
emitterChain: ChainId,
emitterAddress: string,
sequence: string
) {
const rpc = new GrpcWebImpl("http://localhost:8080", {}); // TODO: make this a parameter
const api = new PublicrpcClientImpl(rpc);
// TODO: move this loop outside sdk
let result;
while (!result) {
await new Promise((resolve) => setTimeout(resolve, 1000));
try {
result = await api.GetSignedVAA({
messageId: {
emitterChain,
emitterAddress,
sequence,
},
});
} catch (e) {
// TODO: instead of try/catch, simply return api.GetSignedVAA
}
}
return result;
}

1
sdk/js/src/rpc/index.ts Normal file
View File

@ -0,0 +1 @@
export * from "./getSignedVAA";

View File

@ -0,0 +1,5 @@
export type ChainId = 1 | 2 | 3 | 4;
export const CHAIN_ID_SOLANA: ChainId = 1;
export const CHAIN_ID_ETH: ChainId = 2;
export const CHAIN_ID_TERRA: ChainId = 3;
export const CHAIN_ID_BSC: ChainId = 4;

View File

@ -0,0 +1,2 @@
export * from "./consts";
export * from "./rust";

23
sdk/js/src/utils/rust.ts Normal file
View File

@ -0,0 +1,23 @@
import {
AccountMeta,
PublicKey,
TransactionInstruction,
} from "@solana/web3.js";
// begin from clients\solana\main.ts
export function ixFromRust(data: any): TransactionInstruction {
const keys: AccountMeta[] = data.accounts.map(accountMetaFromRust);
return new TransactionInstruction({
programId: new PublicKey(data.program_id),
data: Buffer.from(data.data),
keys,
});
}
function accountMetaFromRust(meta: any): AccountMeta {
return {
pubkey: new PublicKey(meta.pubkey),
isSigner: meta.is_signer,
isWritable: meta.is_writable,
};
}
// end from clients\solana\main.ts

13
sdk/js/tsconfig.json Normal file
View File

@ -0,0 +1,13 @@
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"declaration": true,
"outDir": "./lib",
"strict": true,
"esModuleInterop": true,
"downlevelIteration": true,
},
"include": ["src"],
"exclude": ["node_modules", "**/__tests__/*"]
}

9
sdk/js/tslint.json Normal file
View File

@ -0,0 +1,9 @@
{
"extends": ["tslint:recommended", "tslint-config-prettier"],
"linterOptions": {
"exclude": [
"src/proto/**"
]
}
}