sdk/js: attempt jest tests
Change-Id: I139153994604f0048f690b011048daec4d760a55
This commit is contained in:
parent
71e6fa7f6f
commit
c824a99636
|
@ -1,5 +1,17 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.0.9
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
Integration tests
|
||||||
|
|
||||||
|
NodeJS target wasm
|
||||||
|
|
||||||
|
Ability to update attestations on EVM chains & Terra.
|
||||||
|
|
||||||
|
nativeToHexString utility function for converting native addresses into VAA hex format.
|
||||||
|
|
||||||
## 0.0.8
|
## 0.0.8
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"transform": {
|
||||||
|
"^.+\\.(t|j)sx?$": "ts-jest"
|
||||||
|
},
|
||||||
|
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
|
||||||
|
"moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node"]
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@certusone/wormhole-sdk",
|
"name": "@certusone/wormhole-sdk",
|
||||||
"version": "0.0.8",
|
"version": "0.0.9",
|
||||||
"description": "SDK for interacting with Wormhole",
|
"description": "SDK for interacting with Wormhole",
|
||||||
"homepage": "https://wormholenetwork.com",
|
"homepage": "https://wormholenetwork.com",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
|
@ -15,7 +15,7 @@
|
||||||
"build-deps": "npm run build-abis && npm run build-contracts",
|
"build-deps": "npm run build-abis && npm run build-contracts",
|
||||||
"build-lib": "tsc && node scripts/copyEthersTypes.js && node scripts/copyWasm.js",
|
"build-lib": "tsc && node scripts/copyEthersTypes.js && node scripts/copyWasm.js",
|
||||||
"build-all": "npm run build-deps && npm run build-lib",
|
"build-all": "npm run build-deps && npm run build-lib",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "jest --config jestconfig.json --verbose",
|
||||||
"build": "npm run build-all",
|
"build": "npm run build-all",
|
||||||
"format": "echo \"disabled: prettier --write \"src/**/*.ts\"\"",
|
"format": "echo \"disabled: prettier --write \"src/**/*.ts\"\"",
|
||||||
"lint": "tslint -p tsconfig.json",
|
"lint": "tslint -p tsconfig.json",
|
||||||
|
@ -38,14 +38,18 @@
|
||||||
"author": "certusone",
|
"author": "certusone",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@improbable-eng/grpc-web-node-http-transport": "^0.15.0",
|
||||||
"@openzeppelin/contracts": "^4.2.0",
|
"@openzeppelin/contracts": "^4.2.0",
|
||||||
"@typechain/ethers-v5": "^7.0.1",
|
"@typechain/ethers-v5": "^7.0.1",
|
||||||
|
"@types/jest": "^27.0.2",
|
||||||
"@types/long": "^4.0.1",
|
"@types/long": "^4.0.1",
|
||||||
"@types/node": "^16.6.1",
|
"@types/node": "^16.6.1",
|
||||||
"@types/react": "^17.0.19",
|
"@types/react": "^17.0.19",
|
||||||
"copy-dir": "^1.3.0",
|
"copy-dir": "^1.3.0",
|
||||||
"ethers": "^5.4.4",
|
"ethers": "^5.4.4",
|
||||||
|
"jest": "^27.3.1",
|
||||||
"prettier": "^2.3.2",
|
"prettier": "^2.3.2",
|
||||||
|
"ts-jest": "^27.0.7",
|
||||||
"tslint": "^6.1.3",
|
"tslint": "^6.1.3",
|
||||||
"tslint-config-prettier": "^1.18.0",
|
"tslint-config-prettier": "^1.18.0",
|
||||||
"typescript": "^4.3.5"
|
"typescript": "^4.3.5"
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { PublicKey } from "@solana/web3.js";
|
import { PublicKey } from "@solana/web3.js";
|
||||||
|
import { importCoreWasm } from "../solana/wasm";
|
||||||
|
|
||||||
export async function getClaimAddressSolana(
|
export async function getClaimAddressSolana(
|
||||||
programAddress: string,
|
programAddress: string,
|
||||||
signedVAA: Uint8Array
|
signedVAA: Uint8Array
|
||||||
) {
|
) {
|
||||||
const { claim_address } = await import("../solana/core/bridge");
|
const { claim_address } = await importCoreWasm();
|
||||||
return new PublicKey(claim_address(programAddress, signedVAA));
|
return new PublicKey(claim_address(programAddress, signedVAA));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { PublicKey } from "@solana/web3.js";
|
import { PublicKey } from "@solana/web3.js";
|
||||||
import { bech32 } from "bech32";
|
import { bech32 } from "bech32";
|
||||||
import { arrayify, BytesLike, Hexable, zeroPad } from "ethers/lib/utils";
|
import { arrayify, BytesLike, Hexable, zeroPad } from "ethers/lib/utils";
|
||||||
|
import { importTokenWasm } from "../solana/wasm";
|
||||||
|
|
||||||
export function getEmitterAddressEth(
|
export function getEmitterAddressEth(
|
||||||
contractAddress: number | BytesLike | Hexable
|
contractAddress: number | BytesLike | Hexable
|
||||||
|
@ -9,7 +10,7 @@ export function getEmitterAddressEth(
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getEmitterAddressSolana(programAddress: string) {
|
export async function getEmitterAddressSolana(programAddress: string) {
|
||||||
const { emitter_address } = await import("../solana/token/token_bridge");
|
const { emitter_address } = await importTokenWasm();
|
||||||
return Buffer.from(
|
return Buffer.from(
|
||||||
zeroPad(new PublicKey(emitter_address(programAddress)).toBytes(), 32)
|
zeroPad(new PublicKey(emitter_address(programAddress)).toBytes(), 32)
|
||||||
).toString("hex");
|
).toString("hex");
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Token, TOKEN_PROGRAM_ID, u64 } from "@solana/spl-token";
|
import { Token, TOKEN_PROGRAM_ID, u64 } from "@solana/spl-token";
|
||||||
import { Connection, PublicKey, Transaction } from "@solana/web3.js";
|
import { Connection, PublicKey, Transaction } from "@solana/web3.js";
|
||||||
import { ixFromRust } from "../solana";
|
import { ixFromRust } from "../solana";
|
||||||
|
import { importMigrationWasm } from "../solana/wasm";
|
||||||
|
|
||||||
export default async function addLiquidity(
|
export default async function addLiquidity(
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
|
@ -12,9 +13,7 @@ export default async function addLiquidity(
|
||||||
lp_share_token_account: string,
|
lp_share_token_account: string,
|
||||||
amount: BigInt
|
amount: BigInt
|
||||||
) {
|
) {
|
||||||
const { authority_address, add_liquidity } = await import(
|
const { authority_address, add_liquidity } = await importMigrationWasm();
|
||||||
"../solana/migration/wormhole_migration"
|
|
||||||
);
|
|
||||||
const approvalIx = Token.createApproveInstruction(
|
const approvalIx = Token.createApproveInstruction(
|
||||||
TOKEN_PROGRAM_ID,
|
TOKEN_PROGRAM_ID,
|
||||||
new PublicKey(liquidity_token_account),
|
new PublicKey(liquidity_token_account),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
import { importMigrationWasm } from "../solana/wasm";
|
||||||
|
|
||||||
export default async function authorityAddress(program_id: string) {
|
export default async function authorityAddress(program_id: string) {
|
||||||
const { authority_address } = await import(
|
const { authority_address } = await importMigrationWasm();
|
||||||
"../solana/migration/wormhole_migration"
|
|
||||||
);
|
|
||||||
return authority_address(program_id);
|
return authority_address(program_id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Token, TOKEN_PROGRAM_ID, u64 } from "@solana/spl-token";
|
import { Token, TOKEN_PROGRAM_ID, u64 } from "@solana/spl-token";
|
||||||
import { Connection, PublicKey, Transaction } from "@solana/web3.js";
|
import { Connection, PublicKey, Transaction } from "@solana/web3.js";
|
||||||
import { ixFromRust } from "../solana";
|
import { ixFromRust } from "../solana";
|
||||||
|
import { importMigrationWasm } from "../solana/wasm";
|
||||||
|
|
||||||
export default async function claimShares(
|
export default async function claimShares(
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
|
@ -12,9 +13,7 @@ export default async function claimShares(
|
||||||
lp_share_token_account: string,
|
lp_share_token_account: string,
|
||||||
amount: BigInt
|
amount: BigInt
|
||||||
) {
|
) {
|
||||||
const { authority_address, claim_shares } = await import(
|
const { authority_address, claim_shares } = await importMigrationWasm();
|
||||||
"../solana/migration/wormhole_migration"
|
|
||||||
);
|
|
||||||
const approvalIx = Token.createApproveInstruction(
|
const approvalIx = Token.createApproveInstruction(
|
||||||
TOKEN_PROGRAM_ID,
|
TOKEN_PROGRAM_ID,
|
||||||
new PublicKey(lp_share_token_account),
|
new PublicKey(lp_share_token_account),
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { Connection, PublicKey, Transaction } from "@solana/web3.js";
|
import { Connection, PublicKey, Transaction } from "@solana/web3.js";
|
||||||
import { ixFromRust } from "../solana";
|
import { ixFromRust } from "../solana";
|
||||||
|
import { importMigrationWasm } from "../solana/wasm";
|
||||||
|
|
||||||
export default async function createPool(
|
export default async function createPool(
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
|
@ -9,9 +10,7 @@ export default async function createPool(
|
||||||
from_mint: string,
|
from_mint: string,
|
||||||
to_mint: string
|
to_mint: string
|
||||||
) {
|
) {
|
||||||
const { create_pool } = await import(
|
const { create_pool } = await importMigrationWasm();
|
||||||
"../solana/migration/wormhole_migration"
|
|
||||||
);
|
|
||||||
const ix = ixFromRust(create_pool(program_id, payer, from_mint, to_mint));
|
const ix = ixFromRust(create_pool(program_id, payer, from_mint, to_mint));
|
||||||
const transaction = new Transaction().add(ix);
|
const transaction = new Transaction().add(ix);
|
||||||
const { blockhash } = await connection.getRecentBlockhash();
|
const { blockhash } = await connection.getRecentBlockhash();
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
|
import { importMigrationWasm } from "../solana/wasm";
|
||||||
|
|
||||||
export default async function fromCustodyAddress(
|
export default async function fromCustodyAddress(
|
||||||
program_id: string,
|
program_id: string,
|
||||||
pool: string
|
pool: string
|
||||||
) {
|
) {
|
||||||
const { from_custody_address } = await import(
|
const { from_custody_address } = await importMigrationWasm();
|
||||||
"../solana/migration/wormhole_migration"
|
|
||||||
);
|
|
||||||
return from_custody_address(program_id, pool);
|
return from_custody_address(program_id, pool);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Token, TOKEN_PROGRAM_ID, u64 } from "@solana/spl-token";
|
import { Token, TOKEN_PROGRAM_ID, u64 } from "@solana/spl-token";
|
||||||
import { Connection, PublicKey, Transaction } from "@solana/web3.js";
|
import { Connection, PublicKey, Transaction } from "@solana/web3.js";
|
||||||
import { ixFromRust } from "../solana";
|
import { ixFromRust } from "../solana";
|
||||||
|
import { importMigrationWasm } from "../solana/wasm";
|
||||||
|
|
||||||
export default async function migrateTokens(
|
export default async function migrateTokens(
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
|
@ -12,9 +13,7 @@ export default async function migrateTokens(
|
||||||
output_token_account: string,
|
output_token_account: string,
|
||||||
amount: BigInt
|
amount: BigInt
|
||||||
) {
|
) {
|
||||||
const { authority_address, migrate_tokens } = await import(
|
const { authority_address, migrate_tokens } = await importMigrationWasm();
|
||||||
"../solana/migration/wormhole_migration"
|
|
||||||
);
|
|
||||||
const approvalIx = Token.createApproveInstruction(
|
const approvalIx = Token.createApproveInstruction(
|
||||||
TOKEN_PROGRAM_ID,
|
TOKEN_PROGRAM_ID,
|
||||||
new PublicKey(input_token_account),
|
new PublicKey(input_token_account),
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
import { importMigrationWasm } from "../solana/wasm";
|
||||||
|
|
||||||
export default async function parsePool(data: Uint8Array) {
|
export default async function parsePool(data: Uint8Array) {
|
||||||
const { parse_pool } = await import("../solana/migration/wormhole_migration");
|
const { parse_pool } = await importMigrationWasm();
|
||||||
return parse_pool(data);
|
return parse_pool(data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
|
import { importMigrationWasm } from "../solana/wasm";
|
||||||
|
|
||||||
export default async function poolAddress(
|
export default async function poolAddress(
|
||||||
program_id: string,
|
program_id: string,
|
||||||
from_mint: string,
|
from_mint: string,
|
||||||
to_mint: string
|
to_mint: string
|
||||||
) {
|
) {
|
||||||
const { pool_address } = await import(
|
const { pool_address } = await importMigrationWasm();
|
||||||
"../solana/migration/wormhole_migration"
|
|
||||||
);
|
|
||||||
return pool_address(program_id, from_mint, to_mint);
|
return pool_address(program_id, from_mint, to_mint);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Token, TOKEN_PROGRAM_ID, u64 } from "@solana/spl-token";
|
import { Token, TOKEN_PROGRAM_ID, u64 } from "@solana/spl-token";
|
||||||
import { Connection, PublicKey, Transaction } from "@solana/web3.js";
|
import { Connection, PublicKey, Transaction } from "@solana/web3.js";
|
||||||
import { ixFromRust } from "../solana";
|
import { ixFromRust } from "../solana";
|
||||||
|
import { importMigrationWasm } from "../solana/wasm";
|
||||||
|
|
||||||
export default async function removeLiquidity(
|
export default async function removeLiquidity(
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
|
@ -12,9 +13,7 @@ export default async function removeLiquidity(
|
||||||
lp_share_token_account: string,
|
lp_share_token_account: string,
|
||||||
amount: BigInt
|
amount: BigInt
|
||||||
) {
|
) {
|
||||||
const { authority_address, remove_liquidity } = await import(
|
const { authority_address, remove_liquidity } = await importMigrationWasm();
|
||||||
"../solana/migration/wormhole_migration"
|
|
||||||
);
|
|
||||||
const approvalIx = Token.createApproveInstruction(
|
const approvalIx = Token.createApproveInstruction(
|
||||||
TOKEN_PROGRAM_ID,
|
TOKEN_PROGRAM_ID,
|
||||||
new PublicKey(lp_share_token_account),
|
new PublicKey(lp_share_token_account),
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
|
import { importMigrationWasm } from "../solana/wasm";
|
||||||
|
|
||||||
export default async function shareMintAddress(
|
export default async function shareMintAddress(
|
||||||
program_id: string,
|
program_id: string,
|
||||||
pool: string
|
pool: string
|
||||||
) {
|
) {
|
||||||
const { share_mint_address } = await import(
|
const { share_mint_address } = await importMigrationWasm();
|
||||||
"../solana/migration/wormhole_migration"
|
|
||||||
);
|
|
||||||
return share_mint_address(program_id, pool);
|
return share_mint_address(program_id, pool);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
|
import { importMigrationWasm } from "../solana/wasm";
|
||||||
|
|
||||||
export default async function toCustodyAddress(
|
export default async function toCustodyAddress(
|
||||||
program_id: string,
|
program_id: string,
|
||||||
pool: string
|
pool: string
|
||||||
) {
|
) {
|
||||||
const { to_custody_address } = await import(
|
const { to_custody_address } = await importMigrationWasm();
|
||||||
"../solana/migration/wormhole_migration"
|
|
||||||
);
|
|
||||||
return to_custody_address(program_id, pool);
|
return to_custody_address(program_id, pool);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { PublicKey } from "@solana/web3.js";
|
||||||
import { ethers } from "ethers";
|
import { ethers } from "ethers";
|
||||||
import { CHAIN_ID_SOLANA } from "..";
|
import { CHAIN_ID_SOLANA } from "..";
|
||||||
import { NFTBridge__factory } from "../ethers-contracts";
|
import { NFTBridge__factory } from "../ethers-contracts";
|
||||||
|
import { importNftWasm } from "../solana/wasm";
|
||||||
import { ChainId } from "../utils";
|
import { ChainId } from "../utils";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -47,7 +48,7 @@ export async function getForeignAssetSol(
|
||||||
originAsset: Uint8Array,
|
originAsset: Uint8Array,
|
||||||
tokenId: Uint8Array
|
tokenId: Uint8Array
|
||||||
) {
|
) {
|
||||||
const { wrapped_address } = await import("../solana/nft/nft_bridge");
|
const { wrapped_address } = await importNftWasm();
|
||||||
const wrappedAddress = wrapped_address(
|
const wrappedAddress = wrapped_address(
|
||||||
tokenBridgeAddress,
|
tokenBridgeAddress,
|
||||||
originAsset,
|
originAsset,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Connection, PublicKey } from "@solana/web3.js";
|
import { Connection, PublicKey } from "@solana/web3.js";
|
||||||
import { ethers } from "ethers";
|
import { ethers } from "ethers";
|
||||||
import { Bridge__factory } from "../ethers-contracts";
|
import { Bridge__factory } from "../ethers-contracts";
|
||||||
|
import { importNftWasm } from "../solana/wasm";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether or not an asset address on Ethereum is a wormhole wrapped asset
|
* Returns whether or not an asset address on Ethereum is a wormhole wrapped asset
|
||||||
|
@ -32,7 +33,7 @@ export async function getIsWrappedAssetSol(
|
||||||
mintAddress: string
|
mintAddress: string
|
||||||
) {
|
) {
|
||||||
if (!mintAddress) return false;
|
if (!mintAddress) return false;
|
||||||
const { wrapped_meta_address } = await import("../solana/nft/nft_bridge");
|
const { wrapped_meta_address } = await importNftWasm();
|
||||||
const wrappedMetaAddress = wrapped_meta_address(
|
const wrappedMetaAddress = wrapped_meta_address(
|
||||||
tokenBridgeAddress,
|
tokenBridgeAddress,
|
||||||
new PublicKey(mintAddress).toBytes()
|
new PublicKey(mintAddress).toBytes()
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { Connection, PublicKey } from "@solana/web3.js";
|
||||||
import { BigNumber, ethers } from "ethers";
|
import { BigNumber, ethers } from "ethers";
|
||||||
import { arrayify, zeroPad } from "ethers/lib/utils";
|
import { arrayify, zeroPad } from "ethers/lib/utils";
|
||||||
import { TokenImplementation__factory } from "../ethers-contracts";
|
import { TokenImplementation__factory } from "../ethers-contracts";
|
||||||
|
import { importNftWasm } from "../solana/wasm";
|
||||||
import { ChainId, CHAIN_ID_SOLANA } from "../utils";
|
import { ChainId, CHAIN_ID_SOLANA } from "../utils";
|
||||||
import { getIsWrappedAssetEth } from "./getIsWrappedAsset";
|
import { getIsWrappedAssetEth } from "./getIsWrappedAsset";
|
||||||
|
|
||||||
|
@ -70,9 +71,7 @@ export async function getOriginalAssetSol(
|
||||||
): Promise<WormholeWrappedNFTInfo> {
|
): Promise<WormholeWrappedNFTInfo> {
|
||||||
if (mintAddress) {
|
if (mintAddress) {
|
||||||
// TODO: share some of this with getIsWrappedAssetSol, like a getWrappedMetaAccountAddress or something
|
// TODO: share some of this with getIsWrappedAssetSol, like a getWrappedMetaAccountAddress or something
|
||||||
const { parse_wrapped_meta, wrapped_meta_address } = await import(
|
const { parse_wrapped_meta, wrapped_meta_address } = await importNftWasm();
|
||||||
"../solana/nft/nft_bridge"
|
|
||||||
);
|
|
||||||
const wrappedMetaAddress = wrapped_meta_address(
|
const wrappedMetaAddress = wrapped_meta_address(
|
||||||
tokenBridgeAddress,
|
tokenBridgeAddress,
|
||||||
new PublicKey(mintAddress).toBytes()
|
new PublicKey(mintAddress).toBytes()
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { ethers } from "ethers";
|
||||||
import { CHAIN_ID_SOLANA } from "..";
|
import { CHAIN_ID_SOLANA } from "..";
|
||||||
import { Bridge__factory } from "../ethers-contracts";
|
import { Bridge__factory } from "../ethers-contracts";
|
||||||
import { ixFromRust } from "../solana";
|
import { ixFromRust } from "../solana";
|
||||||
|
import { importCoreWasm, importNftWasm } from "../solana/wasm";
|
||||||
|
|
||||||
export async function redeemOnEth(
|
export async function redeemOnEth(
|
||||||
tokenBridgeAddress: string,
|
tokenBridgeAddress: string,
|
||||||
|
@ -16,7 +17,7 @@ export async function redeemOnEth(
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function isNFTVAASolanaNative(signedVAA: Uint8Array) {
|
export async function isNFTVAASolanaNative(signedVAA: Uint8Array) {
|
||||||
const { parse_vaa } = await import("../solana/core/bridge");
|
const { parse_vaa } = await importCoreWasm();
|
||||||
const parsedVAA = parse_vaa(signedVAA);
|
const parsedVAA = parse_vaa(signedVAA);
|
||||||
const isSolanaNative =
|
const isSolanaNative =
|
||||||
Buffer.from(new Uint8Array(parsedVAA.payload)).readUInt16BE(33) ===
|
Buffer.from(new Uint8Array(parsedVAA.payload)).readUInt16BE(33) ===
|
||||||
|
@ -33,7 +34,7 @@ export async function redeemOnSolana(
|
||||||
) {
|
) {
|
||||||
const isSolanaNative = await isNFTVAASolanaNative(signedVAA);
|
const isSolanaNative = await isNFTVAASolanaNative(signedVAA);
|
||||||
const { complete_transfer_wrapped_ix, complete_transfer_native_ix } =
|
const { complete_transfer_wrapped_ix, complete_transfer_native_ix } =
|
||||||
await import("../solana/nft/nft_bridge");
|
await importNftWasm();
|
||||||
const ixs = [];
|
const ixs = [];
|
||||||
if (isSolanaNative) {
|
if (isSolanaNative) {
|
||||||
ixs.push(
|
ixs.push(
|
||||||
|
@ -74,9 +75,7 @@ export async function createMetaOnSolana(
|
||||||
payerAddress: string,
|
payerAddress: string,
|
||||||
signedVAA: Uint8Array
|
signedVAA: Uint8Array
|
||||||
) {
|
) {
|
||||||
const { complete_transfer_wrapped_meta_ix } = await import(
|
const { complete_transfer_wrapped_meta_ix } = await importNftWasm();
|
||||||
"../solana/nft/nft_bridge"
|
|
||||||
);
|
|
||||||
const ix = ixFromRust(
|
const ix = ixFromRust(
|
||||||
complete_transfer_wrapped_meta_ix(
|
complete_transfer_wrapped_meta_ix(
|
||||||
tokenBridgeAddress,
|
tokenBridgeAddress,
|
||||||
|
|
|
@ -1,106 +1,109 @@
|
||||||
import {Token, TOKEN_PROGRAM_ID} from "@solana/spl-token";
|
import { Token, TOKEN_PROGRAM_ID } from "@solana/spl-token";
|
||||||
import {Connection, Keypair, PublicKey, Transaction} from "@solana/web3.js";
|
import { Connection, Keypair, PublicKey, Transaction } from "@solana/web3.js";
|
||||||
import {ethers} from "ethers";
|
import { ethers } from "ethers";
|
||||||
import {
|
import {
|
||||||
NFTBridge__factory,
|
NFTBridge__factory,
|
||||||
NFTImplementation__factory,
|
NFTImplementation__factory,
|
||||||
} from "../ethers-contracts";
|
} from "../ethers-contracts";
|
||||||
import {getBridgeFeeIx, ixFromRust} from "../solana";
|
import { getBridgeFeeIx, ixFromRust } from "../solana";
|
||||||
import {ChainId, CHAIN_ID_SOLANA, createNonce} from "../utils";
|
import { importNftWasm } from "../solana/wasm";
|
||||||
|
import { ChainId, CHAIN_ID_SOLANA, createNonce } from "../utils";
|
||||||
|
|
||||||
export async function transferFromEth(
|
export async function transferFromEth(
|
||||||
tokenBridgeAddress: string,
|
tokenBridgeAddress: string,
|
||||||
signer: ethers.Signer,
|
signer: ethers.Signer,
|
||||||
tokenAddress: string,
|
tokenAddress: string,
|
||||||
tokenID: ethers.BigNumberish,
|
tokenID: ethers.BigNumberish,
|
||||||
recipientChain: ChainId,
|
recipientChain: ChainId,
|
||||||
recipientAddress: Uint8Array
|
recipientAddress: Uint8Array
|
||||||
) {
|
) {
|
||||||
//TODO: should we check if token attestation exists on the target chain
|
//TODO: should we check if token attestation exists on the target chain
|
||||||
const token = NFTImplementation__factory.connect(tokenAddress, signer);
|
const token = NFTImplementation__factory.connect(tokenAddress, signer);
|
||||||
await (await token.approve(tokenBridgeAddress, tokenID)).wait();
|
await (await token.approve(tokenBridgeAddress, tokenID)).wait();
|
||||||
const bridge = NFTBridge__factory.connect(tokenBridgeAddress, signer);
|
const bridge = NFTBridge__factory.connect(tokenBridgeAddress, signer);
|
||||||
const v = await bridge.transferNFT(
|
const v = await bridge.transferNFT(
|
||||||
tokenAddress,
|
tokenAddress,
|
||||||
tokenID,
|
tokenID,
|
||||||
recipientChain,
|
recipientChain,
|
||||||
recipientAddress,
|
recipientAddress,
|
||||||
createNonce()
|
createNonce()
|
||||||
);
|
);
|
||||||
const receipt = await v.wait();
|
const receipt = await v.wait();
|
||||||
return receipt;
|
return receipt;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function transferFromSolana(
|
export async function transferFromSolana(
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
bridgeAddress: string,
|
bridgeAddress: string,
|
||||||
tokenBridgeAddress: string,
|
tokenBridgeAddress: string,
|
||||||
payerAddress: string,
|
payerAddress: string,
|
||||||
fromAddress: string,
|
fromAddress: string,
|
||||||
mintAddress: string,
|
mintAddress: string,
|
||||||
targetAddress: Uint8Array,
|
targetAddress: Uint8Array,
|
||||||
targetChain: ChainId,
|
targetChain: ChainId,
|
||||||
originAddress?: Uint8Array,
|
originAddress?: Uint8Array,
|
||||||
originChain?: ChainId,
|
originChain?: ChainId,
|
||||||
originTokenId?: Uint8Array
|
originTokenId?: Uint8Array
|
||||||
) {
|
) {
|
||||||
const nonce = createNonce().readUInt32LE(0);
|
const nonce = createNonce().readUInt32LE(0);
|
||||||
const transferIx = await getBridgeFeeIx(
|
const transferIx = await getBridgeFeeIx(
|
||||||
connection,
|
connection,
|
||||||
bridgeAddress,
|
bridgeAddress,
|
||||||
payerAddress
|
payerAddress
|
||||||
|
);
|
||||||
|
const {
|
||||||
|
transfer_native_ix,
|
||||||
|
transfer_wrapped_ix,
|
||||||
|
approval_authority_address,
|
||||||
|
} = await importNftWasm();
|
||||||
|
const approvalIx = Token.createApproveInstruction(
|
||||||
|
TOKEN_PROGRAM_ID,
|
||||||
|
new PublicKey(fromAddress),
|
||||||
|
new PublicKey(approval_authority_address(tokenBridgeAddress)),
|
||||||
|
new PublicKey(payerAddress),
|
||||||
|
[],
|
||||||
|
Number(1)
|
||||||
|
);
|
||||||
|
let messageKey = Keypair.generate();
|
||||||
|
const isSolanaNative =
|
||||||
|
originChain === undefined || originChain === CHAIN_ID_SOLANA;
|
||||||
|
if (!isSolanaNative && !originAddress && !originTokenId) {
|
||||||
|
throw new Error(
|
||||||
|
"originAddress and originTokenId are required when specifying originChain"
|
||||||
);
|
);
|
||||||
const {
|
}
|
||||||
transfer_native_ix,
|
const ix = ixFromRust(
|
||||||
transfer_wrapped_ix,
|
isSolanaNative
|
||||||
approval_authority_address,
|
? transfer_native_ix(
|
||||||
} = await import("../solana/nft/nft_bridge");
|
tokenBridgeAddress,
|
||||||
const approvalIx = Token.createApproveInstruction(
|
bridgeAddress,
|
||||||
TOKEN_PROGRAM_ID,
|
payerAddress,
|
||||||
new PublicKey(fromAddress),
|
messageKey.publicKey.toString(),
|
||||||
new PublicKey(approval_authority_address(tokenBridgeAddress)),
|
fromAddress,
|
||||||
new PublicKey(payerAddress),
|
mintAddress,
|
||||||
[],
|
nonce,
|
||||||
Number(1)
|
targetAddress,
|
||||||
);
|
targetChain
|
||||||
let messageKey = Keypair.generate();
|
)
|
||||||
const isSolanaNative =
|
: transfer_wrapped_ix(
|
||||||
originChain === undefined || originChain === CHAIN_ID_SOLANA;
|
tokenBridgeAddress,
|
||||||
if (!isSolanaNative && !originAddress && !originTokenId) {
|
bridgeAddress,
|
||||||
throw new Error("originAddress and originTokenId are required when specifying originChain");
|
payerAddress,
|
||||||
}
|
messageKey.publicKey.toString(),
|
||||||
const ix = ixFromRust(
|
fromAddress,
|
||||||
isSolanaNative
|
payerAddress,
|
||||||
? transfer_native_ix(
|
originChain as number, // checked by isSolanaNative
|
||||||
tokenBridgeAddress,
|
originAddress as Uint8Array, // checked by throw
|
||||||
bridgeAddress,
|
originTokenId as Uint8Array, // checked by throw
|
||||||
payerAddress,
|
nonce,
|
||||||
messageKey.publicKey.toString(),
|
targetAddress,
|
||||||
fromAddress,
|
targetChain
|
||||||
mintAddress,
|
)
|
||||||
nonce,
|
);
|
||||||
targetAddress,
|
const transaction = new Transaction().add(transferIx, approvalIx, ix);
|
||||||
targetChain
|
const { blockhash } = await connection.getRecentBlockhash();
|
||||||
)
|
transaction.recentBlockhash = blockhash;
|
||||||
: transfer_wrapped_ix(
|
transaction.feePayer = new PublicKey(payerAddress);
|
||||||
tokenBridgeAddress,
|
transaction.partialSign(messageKey);
|
||||||
bridgeAddress,
|
return transaction;
|
||||||
payerAddress,
|
|
||||||
messageKey.publicKey.toString(),
|
|
||||||
fromAddress,
|
|
||||||
payerAddress,
|
|
||||||
originChain as number, // checked by isSolanaNative
|
|
||||||
originAddress as Uint8Array, // checked by throw
|
|
||||||
originTokenId as Uint8Array, // checked by throw
|
|
||||||
nonce,
|
|
||||||
targetAddress,
|
|
||||||
targetChain
|
|
||||||
)
|
|
||||||
);
|
|
||||||
const transaction = new Transaction().add(transferIx, approvalIx, ix);
|
|
||||||
const {blockhash} = await connection.getRecentBlockhash();
|
|
||||||
transaction.recentBlockhash = blockhash;
|
|
||||||
transaction.feePayer = new PublicKey(payerAddress);
|
|
||||||
transaction.partialSign(messageKey);
|
|
||||||
return transaction;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { ChainId, getSignedVAA } from "..";
|
||||||
|
|
||||||
|
export default async function getSignedVAAWithRetry(
|
||||||
|
hosts: string[],
|
||||||
|
emitterChain: ChainId,
|
||||||
|
emitterAddress: string,
|
||||||
|
sequence: string,
|
||||||
|
extraGrpcOpts = {},
|
||||||
|
retryTimeout = 1000,
|
||||||
|
retryAttempts?: number
|
||||||
|
) {
|
||||||
|
let currentWormholeRpcHost = -1;
|
||||||
|
const getNextRpcHost = () => ++currentWormholeRpcHost % hosts.length;
|
||||||
|
let result;
|
||||||
|
let attempts = 0;
|
||||||
|
while (!result) {
|
||||||
|
attempts++;
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, retryTimeout));
|
||||||
|
try {
|
||||||
|
result = await getSignedVAA(
|
||||||
|
hosts[getNextRpcHost()],
|
||||||
|
emitterChain,
|
||||||
|
emitterAddress,
|
||||||
|
sequence,
|
||||||
|
extraGrpcOpts
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
if (retryAttempts !== undefined && attempts > retryAttempts) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
|
@ -1,11 +1,12 @@
|
||||||
import { Connection, PublicKey, SystemProgram } from "@solana/web3.js";
|
import { Connection, PublicKey, SystemProgram } from "@solana/web3.js";
|
||||||
|
import { importCoreWasm } from "./wasm";
|
||||||
|
|
||||||
export async function getBridgeFeeIx(
|
export async function getBridgeFeeIx(
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
bridgeAddress: string,
|
bridgeAddress: string,
|
||||||
payerAddress: string
|
payerAddress: string
|
||||||
) {
|
) {
|
||||||
const bridge = await import("./core/bridge");
|
const bridge = await importCoreWasm();
|
||||||
const feeAccount = await bridge.fee_collector_address(bridgeAddress);
|
const feeAccount = await bridge.fee_collector_address(bridgeAddress);
|
||||||
const bridgeStatePK = new PublicKey(bridge.state_address(bridgeAddress));
|
const bridgeStatePK = new PublicKey(bridge.state_address(bridgeAddress));
|
||||||
const bridgeStateAccountInfo = await connection.getAccountInfo(bridgeStatePK);
|
const bridgeStateAccountInfo = await connection.getAccountInfo(bridgeStatePK);
|
||||||
|
|
|
@ -6,11 +6,12 @@ import {
|
||||||
TransactionInstruction,
|
TransactionInstruction,
|
||||||
} from "@solana/web3.js";
|
} from "@solana/web3.js";
|
||||||
import { ixFromRust } from "./rust";
|
import { ixFromRust } from "./rust";
|
||||||
|
import { importCoreWasm } from "./wasm";
|
||||||
|
|
||||||
// is there a better pattern for this?
|
// is there a better pattern for this?
|
||||||
export async function postVaa(
|
export async function postVaa(
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
signTransaction: (transaction: Transaction) => any,
|
signTransaction: (transaction: Transaction) => Promise<Transaction>,
|
||||||
bridge_id: string,
|
bridge_id: string,
|
||||||
payer: string,
|
payer: string,
|
||||||
vaa: Buffer
|
vaa: Buffer
|
||||||
|
@ -20,7 +21,7 @@ export async function postVaa(
|
||||||
parse_guardian_set,
|
parse_guardian_set,
|
||||||
verify_signatures_ix,
|
verify_signatures_ix,
|
||||||
post_vaa_ix,
|
post_vaa_ix,
|
||||||
} = await import("./core/bridge");
|
} = await importCoreWasm();
|
||||||
let bridge_state = await getBridgeState(connection, bridge_id);
|
let bridge_state = await getBridgeState(connection, bridge_id);
|
||||||
let guardian_addr = new PublicKey(
|
let guardian_addr = new PublicKey(
|
||||||
guardian_set_address(bridge_id, bridge_state.guardian_set_index)
|
guardian_set_address(bridge_id, bridge_state.guardian_set_index)
|
||||||
|
@ -74,7 +75,7 @@ async function getBridgeState(
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
bridge_id: string
|
bridge_id: string
|
||||||
): Promise<BridgeState> {
|
): Promise<BridgeState> {
|
||||||
const { parse_state, state_address } = await import("./core/bridge");
|
const { parse_state, state_address } = await importCoreWasm();
|
||||||
let bridge_state = new PublicKey(state_address(bridge_id));
|
let bridge_state = new PublicKey(state_address(bridge_id));
|
||||||
let acc = await connection.getAccountInfo(bridge_state);
|
let acc = await connection.getAccountInfo(bridge_state);
|
||||||
if (acc?.data === undefined) {
|
if (acc?.data === undefined) {
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
const coreWasms = {
|
||||||
|
bundler: async () => await import("./core/bridge"),
|
||||||
|
node: async () => await import("./core-node/bridge"),
|
||||||
|
};
|
||||||
|
const migrationWasms = {
|
||||||
|
bundler: async () => await import("./migration/wormhole_migration"),
|
||||||
|
node: async () => await import("./migration-node/wormhole_migration"),
|
||||||
|
};
|
||||||
|
const nftWasms = {
|
||||||
|
bundler: async () => await import("./nft/nft_bridge"),
|
||||||
|
node: async () => await import("./nft-node/nft_bridge"),
|
||||||
|
};
|
||||||
|
const tokenWasms = {
|
||||||
|
bundler: async () => await import("./token/token_bridge"),
|
||||||
|
node: async () => await import("./token-node/token_bridge"),
|
||||||
|
};
|
||||||
|
let importDefaultCoreWasm = coreWasms.bundler;
|
||||||
|
let importDefaultMigrationWasm = migrationWasms.bundler;
|
||||||
|
let importDefaultNftWasm = nftWasms.bundler;
|
||||||
|
let importDefaultTokenWasm = tokenWasms.bundler;
|
||||||
|
export function setDefaultWasm(type: "bundler" | "node") {
|
||||||
|
importDefaultCoreWasm = coreWasms[type];
|
||||||
|
importDefaultMigrationWasm = migrationWasms[type];
|
||||||
|
importDefaultNftWasm = nftWasms[type];
|
||||||
|
importDefaultTokenWasm = tokenWasms[type];
|
||||||
|
}
|
||||||
|
export async function importCoreWasm() {
|
||||||
|
return await importDefaultCoreWasm();
|
||||||
|
}
|
||||||
|
export async function importMigrationWasm() {
|
||||||
|
return await importDefaultMigrationWasm();
|
||||||
|
}
|
||||||
|
export async function importNftWasm() {
|
||||||
|
return await importDefaultNftWasm();
|
||||||
|
}
|
||||||
|
export async function importTokenWasm() {
|
||||||
|
return await importDefaultTokenWasm();
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { describe, expect, it } from "@jest/globals";
|
||||||
|
import { Connection, PublicKey } from "@solana/web3.js";
|
||||||
|
|
||||||
|
// see devnet.md
|
||||||
|
export const ETH_NODE_URL = "ws://localhost:8545";
|
||||||
|
export const ETH_PRIVATE_KEY =
|
||||||
|
"0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d";
|
||||||
|
export const ETH_CORE_BRIDGE_ADDRESS =
|
||||||
|
"0xC89Ce4735882C9F0f0FE26686c53074E09B0D550";
|
||||||
|
export const ETH_TOKEN_BRIDGE_ADDRESS =
|
||||||
|
"0x0290FB167208Af455bB137780163b7B7a9a10C16";
|
||||||
|
export const SOLANA_HOST = "http://localhost:8899";
|
||||||
|
export const SOLANA_PRIVATE_KEY = new Uint8Array([
|
||||||
|
14, 173, 153, 4, 176, 224, 201, 111, 32, 237, 183, 185, 159, 247, 22, 161, 89,
|
||||||
|
84, 215, 209, 212, 137, 10, 92, 157, 49, 29, 192, 101, 164, 152, 70, 87, 65,
|
||||||
|
8, 174, 214, 157, 175, 126, 98, 90, 54, 24, 100, 177, 247, 77, 19, 112, 47,
|
||||||
|
44, 165, 109, 233, 102, 14, 86, 109, 29, 134, 145, 132, 141,
|
||||||
|
]);
|
||||||
|
export const SOLANA_CORE_BRIDGE_ADDRESS =
|
||||||
|
"Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o";
|
||||||
|
export const SOLANA_TOKEN_BRIDGE_ADDRESS =
|
||||||
|
"B6RHG3mfcckmrYN1UhmJzyS1XX3fZKbkeUcpJe9Sy3FE";
|
||||||
|
export const TEST_ERC20 = "0x2D8BE6BF0baA74e0A907016679CaE9190e80dD0A";
|
||||||
|
export const TEST_SOLANA_TOKEN = "2WDq7wSs9zYrpx2kbHDA4RUTRch2CCTP6ZWaH4GNfnQQ";
|
||||||
|
export const WORMHOLE_RPC_HOSTS = ["http://localhost:7071"];
|
||||||
|
|
||||||
|
describe("consts should exist", () => {
|
||||||
|
it("has Solana test token", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
const connection = new Connection(SOLANA_HOST, "confirmed");
|
||||||
|
return expect(
|
||||||
|
connection.getAccountInfo(new PublicKey(TEST_SOLANA_TOKEN))
|
||||||
|
).resolves.toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,393 @@
|
||||||
|
import { parseUnits } from "@ethersproject/units";
|
||||||
|
import { NodeHttpTransport } from "@improbable-eng/grpc-web-node-http-transport";
|
||||||
|
import { describe, jest, test } from "@jest/globals";
|
||||||
|
import {
|
||||||
|
ASSOCIATED_TOKEN_PROGRAM_ID,
|
||||||
|
Token,
|
||||||
|
TOKEN_PROGRAM_ID,
|
||||||
|
} from "@solana/spl-token";
|
||||||
|
import { Connection, Keypair, PublicKey, Transaction } from "@solana/web3.js";
|
||||||
|
import { ethers } from "ethers";
|
||||||
|
import {
|
||||||
|
approveEth,
|
||||||
|
attestFromEth,
|
||||||
|
attestFromSolana,
|
||||||
|
CHAIN_ID_ETH,
|
||||||
|
CHAIN_ID_SOLANA,
|
||||||
|
createWrappedOnEth,
|
||||||
|
createWrappedOnSolana,
|
||||||
|
getEmitterAddressEth,
|
||||||
|
getEmitterAddressSolana,
|
||||||
|
getForeignAssetSolana,
|
||||||
|
hexToUint8Array,
|
||||||
|
nativeToHexString,
|
||||||
|
parseSequenceFromLogEth,
|
||||||
|
parseSequenceFromLogSolana,
|
||||||
|
postVaaSolana,
|
||||||
|
redeemOnEth,
|
||||||
|
redeemOnSolana,
|
||||||
|
transferFromEth,
|
||||||
|
transferFromSolana,
|
||||||
|
} from "../..";
|
||||||
|
import getSignedVAAWithRetry from "../../rpc/getSignedVAAWithRetry";
|
||||||
|
import { setDefaultWasm } from "../../solana/wasm";
|
||||||
|
import {
|
||||||
|
ETH_CORE_BRIDGE_ADDRESS,
|
||||||
|
ETH_NODE_URL,
|
||||||
|
ETH_PRIVATE_KEY,
|
||||||
|
ETH_TOKEN_BRIDGE_ADDRESS,
|
||||||
|
SOLANA_CORE_BRIDGE_ADDRESS,
|
||||||
|
SOLANA_HOST,
|
||||||
|
SOLANA_PRIVATE_KEY,
|
||||||
|
SOLANA_TOKEN_BRIDGE_ADDRESS,
|
||||||
|
TEST_ERC20,
|
||||||
|
TEST_SOLANA_TOKEN,
|
||||||
|
WORMHOLE_RPC_HOSTS,
|
||||||
|
} from "./consts";
|
||||||
|
|
||||||
|
setDefaultWasm("node");
|
||||||
|
|
||||||
|
jest.setTimeout(60000);
|
||||||
|
|
||||||
|
// TODO: setup keypair and provider/signer before, destroy provider after
|
||||||
|
// TODO: make the repeatable (can't attest an already attested token)
|
||||||
|
// TODO: add Terra
|
||||||
|
|
||||||
|
describe("Integration Tests", () => {
|
||||||
|
describe("Ethereum to Solana", () => {
|
||||||
|
test("Attest Ethereum ERC-20 to Solana", (done) => {
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
// create a signer for Eth
|
||||||
|
const provider = new ethers.providers.WebSocketProvider(ETH_NODE_URL);
|
||||||
|
const signer = new ethers.Wallet(ETH_PRIVATE_KEY, provider);
|
||||||
|
// attest the test token
|
||||||
|
const receipt = await attestFromEth(
|
||||||
|
ETH_TOKEN_BRIDGE_ADDRESS,
|
||||||
|
signer,
|
||||||
|
TEST_ERC20
|
||||||
|
);
|
||||||
|
// get the sequence from the logs (needed to fetch the vaa)
|
||||||
|
const sequence = parseSequenceFromLogEth(
|
||||||
|
receipt,
|
||||||
|
ETH_CORE_BRIDGE_ADDRESS
|
||||||
|
);
|
||||||
|
const emitterAddress = getEmitterAddressEth(ETH_TOKEN_BRIDGE_ADDRESS);
|
||||||
|
// poll until the guardian(s) witness and sign the vaa
|
||||||
|
const { vaaBytes: signedVAA } = await getSignedVAAWithRetry(
|
||||||
|
WORMHOLE_RPC_HOSTS,
|
||||||
|
CHAIN_ID_ETH,
|
||||||
|
emitterAddress,
|
||||||
|
sequence,
|
||||||
|
{
|
||||||
|
transport: NodeHttpTransport(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
// create a keypair for Solana
|
||||||
|
const keypair = Keypair.fromSecretKey(SOLANA_PRIVATE_KEY);
|
||||||
|
const payerAddress = keypair.publicKey.toString();
|
||||||
|
// post vaa to Solana
|
||||||
|
const connection = new Connection(SOLANA_HOST, "confirmed");
|
||||||
|
await postVaaSolana(
|
||||||
|
connection,
|
||||||
|
async (transaction) => {
|
||||||
|
transaction.partialSign(keypair);
|
||||||
|
return transaction;
|
||||||
|
},
|
||||||
|
SOLANA_CORE_BRIDGE_ADDRESS,
|
||||||
|
payerAddress,
|
||||||
|
Buffer.from(signedVAA)
|
||||||
|
);
|
||||||
|
// create wormhole wrapped token (mint and metadata) on solana
|
||||||
|
const transaction = await createWrappedOnSolana(
|
||||||
|
connection,
|
||||||
|
SOLANA_CORE_BRIDGE_ADDRESS,
|
||||||
|
SOLANA_TOKEN_BRIDGE_ADDRESS,
|
||||||
|
payerAddress,
|
||||||
|
signedVAA
|
||||||
|
);
|
||||||
|
// sign, send, and confirm transaction
|
||||||
|
try {
|
||||||
|
transaction.partialSign(keypair);
|
||||||
|
const txid = await connection.sendRawTransaction(
|
||||||
|
transaction.serialize()
|
||||||
|
);
|
||||||
|
await connection.confirmTransaction(txid);
|
||||||
|
} catch (e) {
|
||||||
|
// this could fail because the token is already attested (in an unclean env)
|
||||||
|
}
|
||||||
|
provider.destroy();
|
||||||
|
done();
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
done(
|
||||||
|
"An error occurred while trying to attest from Ethereum to Solana"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
});
|
||||||
|
// TODO: it is attested
|
||||||
|
test("Send Ethereum ERC-20 to Solana", (done) => {
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
// create a keypair for Solana
|
||||||
|
const connection = new Connection(SOLANA_HOST, "confirmed");
|
||||||
|
const keypair = Keypair.fromSecretKey(SOLANA_PRIVATE_KEY);
|
||||||
|
const payerAddress = keypair.publicKey.toString();
|
||||||
|
// determine destination address - an associated token account
|
||||||
|
const solanaMintKey = new PublicKey(
|
||||||
|
(await getForeignAssetSolana(
|
||||||
|
connection,
|
||||||
|
SOLANA_TOKEN_BRIDGE_ADDRESS,
|
||||||
|
CHAIN_ID_ETH,
|
||||||
|
hexToUint8Array(nativeToHexString(TEST_ERC20, CHAIN_ID_ETH) || "")
|
||||||
|
)) || ""
|
||||||
|
);
|
||||||
|
const recipient = await Token.getAssociatedTokenAddress(
|
||||||
|
ASSOCIATED_TOKEN_PROGRAM_ID,
|
||||||
|
TOKEN_PROGRAM_ID,
|
||||||
|
solanaMintKey,
|
||||||
|
keypair.publicKey
|
||||||
|
);
|
||||||
|
// create the associated token account if it doesn't exist
|
||||||
|
const associatedAddressInfo = await connection.getAccountInfo(
|
||||||
|
recipient
|
||||||
|
);
|
||||||
|
if (!associatedAddressInfo) {
|
||||||
|
const transaction = new Transaction().add(
|
||||||
|
await Token.createAssociatedTokenAccountInstruction(
|
||||||
|
ASSOCIATED_TOKEN_PROGRAM_ID,
|
||||||
|
TOKEN_PROGRAM_ID,
|
||||||
|
solanaMintKey,
|
||||||
|
recipient,
|
||||||
|
keypair.publicKey, // owner
|
||||||
|
keypair.publicKey // payer
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const { blockhash } = await connection.getRecentBlockhash();
|
||||||
|
transaction.recentBlockhash = blockhash;
|
||||||
|
transaction.feePayer = keypair.publicKey;
|
||||||
|
// sign, send, and confirm transaction
|
||||||
|
transaction.partialSign(keypair);
|
||||||
|
const txid = await connection.sendRawTransaction(
|
||||||
|
transaction.serialize()
|
||||||
|
);
|
||||||
|
await connection.confirmTransaction(txid);
|
||||||
|
}
|
||||||
|
// create a signer for Eth
|
||||||
|
const provider = new ethers.providers.WebSocketProvider(ETH_NODE_URL);
|
||||||
|
const signer = new ethers.Wallet(ETH_PRIVATE_KEY, provider);
|
||||||
|
const amount = parseUnits("1", 18);
|
||||||
|
// approve the bridge to spend tokens
|
||||||
|
await approveEth(
|
||||||
|
ETH_TOKEN_BRIDGE_ADDRESS,
|
||||||
|
TEST_ERC20,
|
||||||
|
signer,
|
||||||
|
amount
|
||||||
|
);
|
||||||
|
// transfer tokens
|
||||||
|
const receipt = await transferFromEth(
|
||||||
|
ETH_TOKEN_BRIDGE_ADDRESS,
|
||||||
|
signer,
|
||||||
|
TEST_ERC20,
|
||||||
|
amount,
|
||||||
|
CHAIN_ID_SOLANA,
|
||||||
|
hexToUint8Array(
|
||||||
|
nativeToHexString(recipient.toString(), CHAIN_ID_SOLANA) || ""
|
||||||
|
)
|
||||||
|
);
|
||||||
|
// get the sequence from the logs (needed to fetch the vaa)
|
||||||
|
const sequence = parseSequenceFromLogEth(
|
||||||
|
receipt,
|
||||||
|
ETH_CORE_BRIDGE_ADDRESS
|
||||||
|
);
|
||||||
|
const emitterAddress = getEmitterAddressEth(ETH_TOKEN_BRIDGE_ADDRESS);
|
||||||
|
// poll until the guardian(s) witness and sign the vaa
|
||||||
|
const { vaaBytes: signedVAA } = await getSignedVAAWithRetry(
|
||||||
|
WORMHOLE_RPC_HOSTS,
|
||||||
|
CHAIN_ID_ETH,
|
||||||
|
emitterAddress,
|
||||||
|
sequence,
|
||||||
|
{
|
||||||
|
transport: NodeHttpTransport(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
// post vaa to Solana
|
||||||
|
await postVaaSolana(
|
||||||
|
connection,
|
||||||
|
async (transaction) => {
|
||||||
|
transaction.partialSign(keypair);
|
||||||
|
return transaction;
|
||||||
|
},
|
||||||
|
SOLANA_CORE_BRIDGE_ADDRESS,
|
||||||
|
payerAddress,
|
||||||
|
Buffer.from(signedVAA)
|
||||||
|
);
|
||||||
|
// redeem tokens on solana
|
||||||
|
const transaction = await redeemOnSolana(
|
||||||
|
connection,
|
||||||
|
SOLANA_CORE_BRIDGE_ADDRESS,
|
||||||
|
SOLANA_TOKEN_BRIDGE_ADDRESS,
|
||||||
|
payerAddress,
|
||||||
|
signedVAA
|
||||||
|
);
|
||||||
|
// sign, send, and confirm transaction
|
||||||
|
transaction.partialSign(keypair);
|
||||||
|
const txid = await connection.sendRawTransaction(
|
||||||
|
transaction.serialize()
|
||||||
|
);
|
||||||
|
await connection.confirmTransaction(txid);
|
||||||
|
provider.destroy();
|
||||||
|
done();
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
done(
|
||||||
|
"An error occurred while trying to send from Ethereum to Solana"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
});
|
||||||
|
// TODO: it has increased balance
|
||||||
|
});
|
||||||
|
describe("Solana to Ethereum", () => {
|
||||||
|
test("Attest Solana SPL to Ethereum", (done) => {
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
// create a keypair for Solana
|
||||||
|
const keypair = Keypair.fromSecretKey(SOLANA_PRIVATE_KEY);
|
||||||
|
const payerAddress = keypair.publicKey.toString();
|
||||||
|
// attest the test token
|
||||||
|
const connection = new Connection(SOLANA_HOST, "confirmed");
|
||||||
|
const transaction = await attestFromSolana(
|
||||||
|
connection,
|
||||||
|
SOLANA_CORE_BRIDGE_ADDRESS,
|
||||||
|
SOLANA_TOKEN_BRIDGE_ADDRESS,
|
||||||
|
payerAddress,
|
||||||
|
TEST_SOLANA_TOKEN
|
||||||
|
);
|
||||||
|
// sign, send, and confirm transaction
|
||||||
|
transaction.partialSign(keypair);
|
||||||
|
const txid = await connection.sendRawTransaction(
|
||||||
|
transaction.serialize()
|
||||||
|
);
|
||||||
|
await connection.confirmTransaction(txid);
|
||||||
|
const info = await connection.getTransaction(txid);
|
||||||
|
if (!info) {
|
||||||
|
throw new Error(
|
||||||
|
"An error occurred while fetching the transaction info"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// get the sequence from the logs (needed to fetch the vaa)
|
||||||
|
const sequence = parseSequenceFromLogSolana(info);
|
||||||
|
const emitterAddress = await getEmitterAddressSolana(
|
||||||
|
SOLANA_TOKEN_BRIDGE_ADDRESS
|
||||||
|
);
|
||||||
|
// poll until the guardian(s) witness and sign the vaa
|
||||||
|
const { vaaBytes: signedVAA } = await getSignedVAAWithRetry(
|
||||||
|
WORMHOLE_RPC_HOSTS,
|
||||||
|
CHAIN_ID_SOLANA,
|
||||||
|
emitterAddress,
|
||||||
|
sequence,
|
||||||
|
{
|
||||||
|
transport: NodeHttpTransport(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
// create a signer for Eth
|
||||||
|
const provider = new ethers.providers.WebSocketProvider(ETH_NODE_URL);
|
||||||
|
const signer = new ethers.Wallet(ETH_PRIVATE_KEY, provider);
|
||||||
|
try {
|
||||||
|
await createWrappedOnEth(
|
||||||
|
ETH_TOKEN_BRIDGE_ADDRESS,
|
||||||
|
signer,
|
||||||
|
signedVAA
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
// this could fail because the token is already attested (in an unclean env)
|
||||||
|
}
|
||||||
|
provider.destroy();
|
||||||
|
done();
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
done(
|
||||||
|
"An error occurred while trying to attest from Solana to Ethereum"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
});
|
||||||
|
// TODO: it is attested
|
||||||
|
test("Send Solana SPL to Ethereum", (done) => {
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
// create a signer for Eth
|
||||||
|
const provider = new ethers.providers.WebSocketProvider(ETH_NODE_URL);
|
||||||
|
const signer = new ethers.Wallet(ETH_PRIVATE_KEY, provider);
|
||||||
|
const targetAddress = await signer.getAddress();
|
||||||
|
// create a keypair for Solana
|
||||||
|
const keypair = Keypair.fromSecretKey(SOLANA_PRIVATE_KEY);
|
||||||
|
const payerAddress = keypair.publicKey.toString();
|
||||||
|
// find the associated token account
|
||||||
|
const fromAddress = (
|
||||||
|
await Token.getAssociatedTokenAddress(
|
||||||
|
ASSOCIATED_TOKEN_PROGRAM_ID,
|
||||||
|
TOKEN_PROGRAM_ID,
|
||||||
|
new PublicKey(TEST_SOLANA_TOKEN),
|
||||||
|
keypair.publicKey
|
||||||
|
)
|
||||||
|
).toString();
|
||||||
|
// transfer the test token
|
||||||
|
const connection = new Connection(SOLANA_HOST, "confirmed");
|
||||||
|
const amount = parseUnits("1", 9).toBigInt();
|
||||||
|
const transaction = await transferFromSolana(
|
||||||
|
connection,
|
||||||
|
SOLANA_CORE_BRIDGE_ADDRESS,
|
||||||
|
SOLANA_TOKEN_BRIDGE_ADDRESS,
|
||||||
|
payerAddress,
|
||||||
|
fromAddress,
|
||||||
|
TEST_SOLANA_TOKEN,
|
||||||
|
amount,
|
||||||
|
hexToUint8Array(
|
||||||
|
nativeToHexString(targetAddress, CHAIN_ID_ETH) || ""
|
||||||
|
),
|
||||||
|
CHAIN_ID_ETH
|
||||||
|
);
|
||||||
|
// sign, send, and confirm transaction
|
||||||
|
transaction.partialSign(keypair);
|
||||||
|
const txid = await connection.sendRawTransaction(
|
||||||
|
transaction.serialize()
|
||||||
|
);
|
||||||
|
await connection.confirmTransaction(txid);
|
||||||
|
const info = await connection.getTransaction(txid);
|
||||||
|
if (!info) {
|
||||||
|
throw new Error(
|
||||||
|
"An error occurred while fetching the transaction info"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// get the sequence from the logs (needed to fetch the vaa)
|
||||||
|
const sequence = parseSequenceFromLogSolana(info);
|
||||||
|
const emitterAddress = await getEmitterAddressSolana(
|
||||||
|
SOLANA_TOKEN_BRIDGE_ADDRESS
|
||||||
|
);
|
||||||
|
// poll until the guardian(s) witness and sign the vaa
|
||||||
|
const { vaaBytes: signedVAA } = await getSignedVAAWithRetry(
|
||||||
|
WORMHOLE_RPC_HOSTS,
|
||||||
|
CHAIN_ID_SOLANA,
|
||||||
|
emitterAddress,
|
||||||
|
sequence,
|
||||||
|
{
|
||||||
|
transport: NodeHttpTransport(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
await redeemOnEth(ETH_TOKEN_BRIDGE_ADDRESS, signer, signedVAA);
|
||||||
|
provider.destroy();
|
||||||
|
done();
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
done(
|
||||||
|
"An error occurred while trying to attest from Solana to Ethereum"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
});
|
||||||
|
// TODO: it has increased balance
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,11 +1,11 @@
|
||||||
import { Connection, Keypair, PublicKey, Transaction } from "@solana/web3.js";
|
import { Connection, Keypair, PublicKey, Transaction } from "@solana/web3.js";
|
||||||
|
import { MsgExecuteContract } from "@terra-money/terra.js";
|
||||||
import { ethers } from "ethers";
|
import { ethers } from "ethers";
|
||||||
|
import { isNativeDenom } from "..";
|
||||||
import { Bridge__factory } from "../ethers-contracts";
|
import { Bridge__factory } from "../ethers-contracts";
|
||||||
import { getBridgeFeeIx, ixFromRust } from "../solana";
|
import { getBridgeFeeIx, ixFromRust } from "../solana";
|
||||||
|
import { importTokenWasm } from "../solana/wasm";
|
||||||
import { createNonce } from "../utils/createNonce";
|
import { createNonce } from "../utils/createNonce";
|
||||||
import { ConnectedWallet as TerraConnectedWallet } from "@terra-money/wallet-provider";
|
|
||||||
import { MsgExecuteContract } from "@terra-money/terra.js";
|
|
||||||
import { isNativeDenom } from "..";
|
|
||||||
|
|
||||||
export async function attestFromEth(
|
export async function attestFromEth(
|
||||||
tokenBridgeAddress: string,
|
tokenBridgeAddress: string,
|
||||||
|
@ -54,7 +54,7 @@ export async function attestFromSolana(
|
||||||
bridgeAddress,
|
bridgeAddress,
|
||||||
payerAddress
|
payerAddress
|
||||||
);
|
);
|
||||||
const { attest_ix } = await import("../solana/token/token_bridge");
|
const { attest_ix } = await importTokenWasm();
|
||||||
const messageKey = Keypair.generate();
|
const messageKey = Keypair.generate();
|
||||||
const ix = ixFromRust(
|
const ix = ixFromRust(
|
||||||
attest_ix(
|
attest_ix(
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { Connection, PublicKey, Transaction } from "@solana/web3.js";
|
import { Connection, PublicKey, Transaction } from "@solana/web3.js";
|
||||||
import { ethers } from "ethers";
|
|
||||||
import { Bridge__factory } from "../ethers-contracts";
|
|
||||||
import { ConnectedWallet as TerraConnectedWallet } from "@terra-money/wallet-provider";
|
|
||||||
import { MsgExecuteContract } from "@terra-money/terra.js";
|
import { MsgExecuteContract } from "@terra-money/terra.js";
|
||||||
import { ixFromRust } from "../solana";
|
import { ethers } from "ethers";
|
||||||
import { fromUint8Array } from "js-base64";
|
import { fromUint8Array } from "js-base64";
|
||||||
|
import { Bridge__factory } from "../ethers-contracts";
|
||||||
|
import { ixFromRust } from "../solana";
|
||||||
|
import { importTokenWasm } from "../solana/wasm";
|
||||||
|
|
||||||
export async function createWrappedOnEth(
|
export async function createWrappedOnEth(
|
||||||
tokenBridgeAddress: string,
|
tokenBridgeAddress: string,
|
||||||
|
@ -36,7 +36,7 @@ export async function createWrappedOnSolana(
|
||||||
payerAddress: string,
|
payerAddress: string,
|
||||||
signedVAA: Uint8Array
|
signedVAA: Uint8Array
|
||||||
) {
|
) {
|
||||||
const { create_wrapped_ix } = await import("../solana/token/token_bridge");
|
const { create_wrapped_ix } = await importTokenWasm();
|
||||||
const ix = ixFromRust(
|
const ix = ixFromRust(
|
||||||
create_wrapped_ix(
|
create_wrapped_ix(
|
||||||
tokenBridgeAddress,
|
tokenBridgeAddress,
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { Bridge__factory } from "../ethers-contracts";
|
||||||
import { ChainId } from "../utils";
|
import { ChainId } from "../utils";
|
||||||
import { LCDClient } from "@terra-money/terra.js";
|
import { LCDClient } from "@terra-money/terra.js";
|
||||||
import { fromUint8Array } from "js-base64";
|
import { fromUint8Array } from "js-base64";
|
||||||
|
import { importTokenWasm } from "../solana/wasm";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a foreign asset address on Ethereum for a provided native chain and asset address, AddressZero if it does not exist
|
* Returns a foreign asset address on Ethereum for a provided native chain and asset address, AddressZero if it does not exist
|
||||||
|
@ -63,7 +64,7 @@ export async function getForeignAssetSolana(
|
||||||
originChain: ChainId,
|
originChain: ChainId,
|
||||||
originAsset: Uint8Array
|
originAsset: Uint8Array
|
||||||
) {
|
) {
|
||||||
const { wrapped_address } = await import("../solana/token/token_bridge");
|
const { wrapped_address } = await importTokenWasm();
|
||||||
const wrappedAddress = wrapped_address(
|
const wrappedAddress = wrapped_address(
|
||||||
tokenBridgeAddress,
|
tokenBridgeAddress,
|
||||||
originAsset,
|
originAsset,
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { Connection, PublicKey } from "@solana/web3.js";
|
||||||
import { ethers } from "ethers";
|
import { ethers } from "ethers";
|
||||||
import { Bridge__factory } from "../ethers-contracts";
|
import { Bridge__factory } from "../ethers-contracts";
|
||||||
import { ConnectedWallet as TerraConnectedWallet } from "@terra-money/wallet-provider";
|
import { ConnectedWallet as TerraConnectedWallet } from "@terra-money/wallet-provider";
|
||||||
|
import { importTokenWasm } from "../solana/wasm";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether or not an asset address on Ethereum is a wormhole wrapped asset
|
* Returns whether or not an asset address on Ethereum is a wormhole wrapped asset
|
||||||
|
@ -41,7 +42,7 @@ export async function getIsWrappedAssetSol(
|
||||||
mintAddress: string
|
mintAddress: string
|
||||||
) {
|
) {
|
||||||
if (!mintAddress) return false;
|
if (!mintAddress) return false;
|
||||||
const { wrapped_meta_address } = await import("../solana/token/token_bridge");
|
const { wrapped_meta_address } = await importTokenWasm();
|
||||||
const wrappedMetaAddress = wrapped_meta_address(
|
const wrappedMetaAddress = wrapped_meta_address(
|
||||||
tokenBridgeAddress,
|
tokenBridgeAddress,
|
||||||
new PublicKey(mintAddress).toBytes()
|
new PublicKey(mintAddress).toBytes()
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { LCDClient } from "@terra-money/terra.js";
|
||||||
import { ethers } from "ethers";
|
import { ethers } from "ethers";
|
||||||
import { arrayify, zeroPad } from "ethers/lib/utils";
|
import { arrayify, zeroPad } from "ethers/lib/utils";
|
||||||
import { TokenImplementation__factory } from "../ethers-contracts";
|
import { TokenImplementation__factory } from "../ethers-contracts";
|
||||||
|
import { importTokenWasm } from "../solana/wasm";
|
||||||
import { buildNativeId, canonicalAddress, isNativeDenom } from "../terra";
|
import { buildNativeId, canonicalAddress, isNativeDenom } from "../terra";
|
||||||
import { ChainId, CHAIN_ID_SOLANA, CHAIN_ID_TERRA } from "../utils";
|
import { ChainId, CHAIN_ID_SOLANA, CHAIN_ID_TERRA } from "../utils";
|
||||||
import { getIsWrappedAssetEth } from "./getIsWrappedAsset";
|
import { getIsWrappedAssetEth } from "./getIsWrappedAsset";
|
||||||
|
@ -101,9 +102,8 @@ export async function getOriginalAssetSol(
|
||||||
): Promise<WormholeWrappedInfo> {
|
): Promise<WormholeWrappedInfo> {
|
||||||
if (mintAddress) {
|
if (mintAddress) {
|
||||||
// TODO: share some of this with getIsWrappedAssetSol, like a getWrappedMetaAccountAddress or something
|
// TODO: share some of this with getIsWrappedAssetSol, like a getWrappedMetaAccountAddress or something
|
||||||
const { parse_wrapped_meta, wrapped_meta_address } = await import(
|
const { parse_wrapped_meta, wrapped_meta_address } =
|
||||||
"../solana/token/token_bridge"
|
await importTokenWasm();
|
||||||
);
|
|
||||||
const wrappedMetaAddress = wrapped_meta_address(
|
const wrappedMetaAddress = wrapped_meta_address(
|
||||||
tokenBridgeAddress,
|
tokenBridgeAddress,
|
||||||
new PublicKey(mintAddress).toBytes()
|
new PublicKey(mintAddress).toBytes()
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { ethers } from "ethers";
|
||||||
import { fromUint8Array } from "js-base64";
|
import { fromUint8Array } from "js-base64";
|
||||||
import { Bridge__factory } from "../ethers-contracts";
|
import { Bridge__factory } from "../ethers-contracts";
|
||||||
import { ixFromRust } from "../solana";
|
import { ixFromRust } from "../solana";
|
||||||
|
import { importCoreWasm, importTokenWasm } from "../solana/wasm";
|
||||||
import {
|
import {
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
WSOL_ADDRESS,
|
WSOL_ADDRESS,
|
||||||
|
@ -61,10 +62,8 @@ export async function redeemAndUnwrapOnSolana(
|
||||||
payerAddress: string,
|
payerAddress: string,
|
||||||
signedVAA: Uint8Array
|
signedVAA: Uint8Array
|
||||||
) {
|
) {
|
||||||
const { parse_vaa } = await import("../solana/core/bridge");
|
const { parse_vaa } = await importCoreWasm();
|
||||||
const { complete_transfer_native_ix } = await import(
|
const { complete_transfer_native_ix } = await importTokenWasm();
|
||||||
"../solana/token/token_bridge"
|
|
||||||
);
|
|
||||||
const parsedVAA = parse_vaa(signedVAA);
|
const parsedVAA = parse_vaa(signedVAA);
|
||||||
const parsedPayload = parseTransferPayload(
|
const parsedPayload = parseTransferPayload(
|
||||||
Buffer.from(new Uint8Array(parsedVAA.payload))
|
Buffer.from(new Uint8Array(parsedVAA.payload))
|
||||||
|
@ -151,13 +150,13 @@ export async function redeemOnSolana(
|
||||||
payerAddress: string,
|
payerAddress: string,
|
||||||
signedVAA: Uint8Array
|
signedVAA: Uint8Array
|
||||||
) {
|
) {
|
||||||
const { parse_vaa } = await import("../solana/core/bridge");
|
const { parse_vaa } = await importCoreWasm();
|
||||||
const parsedVAA = parse_vaa(signedVAA);
|
const parsedVAA = parse_vaa(signedVAA);
|
||||||
const isSolanaNative =
|
const isSolanaNative =
|
||||||
Buffer.from(new Uint8Array(parsedVAA.payload)).readUInt16BE(65) ===
|
Buffer.from(new Uint8Array(parsedVAA.payload)).readUInt16BE(65) ===
|
||||||
CHAIN_ID_SOLANA;
|
CHAIN_ID_SOLANA;
|
||||||
const { complete_transfer_wrapped_ix, complete_transfer_native_ix } =
|
const { complete_transfer_wrapped_ix, complete_transfer_native_ix } =
|
||||||
await import("../solana/token/token_bridge");
|
await importTokenWasm();
|
||||||
const ixs = [];
|
const ixs = [];
|
||||||
if (isSolanaNative) {
|
if (isSolanaNative) {
|
||||||
ixs.push(
|
ixs.push(
|
||||||
|
|
|
@ -14,6 +14,7 @@ import {
|
||||||
TokenImplementation__factory,
|
TokenImplementation__factory,
|
||||||
} from "../ethers-contracts";
|
} from "../ethers-contracts";
|
||||||
import { getBridgeFeeIx, ixFromRust } from "../solana";
|
import { getBridgeFeeIx, ixFromRust } from "../solana";
|
||||||
|
import { importTokenWasm } from "../solana/wasm";
|
||||||
import { ChainId, CHAIN_ID_SOLANA, createNonce, WSOL_ADDRESS } from "../utils";
|
import { ChainId, CHAIN_ID_SOLANA, createNonce, WSOL_ADDRESS } from "../utils";
|
||||||
|
|
||||||
export async function getAllowanceEth(
|
export async function getAllowanceEth(
|
||||||
|
@ -202,9 +203,8 @@ export async function transferNativeSol(
|
||||||
);
|
);
|
||||||
|
|
||||||
//Normal approve & transfer instructions, except that the wSOL is sent from the ancillary account.
|
//Normal approve & transfer instructions, except that the wSOL is sent from the ancillary account.
|
||||||
const { transfer_native_ix, approval_authority_address } = await import(
|
const { transfer_native_ix, approval_authority_address } =
|
||||||
"../solana/token/token_bridge"
|
await importTokenWasm();
|
||||||
);
|
|
||||||
const nonce = createNonce().readUInt32LE(0);
|
const nonce = createNonce().readUInt32LE(0);
|
||||||
const fee = BigInt(0); // for now, this won't do anything, we may add later
|
const fee = BigInt(0); // for now, this won't do anything, we may add later
|
||||||
const transferIx = await getBridgeFeeIx(
|
const transferIx = await getBridgeFeeIx(
|
||||||
|
@ -286,7 +286,7 @@ export async function transferFromSolana(
|
||||||
transfer_native_ix,
|
transfer_native_ix,
|
||||||
transfer_wrapped_ix,
|
transfer_wrapped_ix,
|
||||||
approval_authority_address,
|
approval_authority_address,
|
||||||
} = await import("../solana/token/token_bridge");
|
} = await importTokenWasm();
|
||||||
const approvalIx = Token.createApproveInstruction(
|
const approvalIx = Token.createApproveInstruction(
|
||||||
TOKEN_PROGRAM_ID,
|
TOKEN_PROGRAM_ID,
|
||||||
new PublicKey(fromAddress),
|
new PublicKey(fromAddress),
|
||||||
|
|
|
@ -42,6 +42,10 @@ RUN --mount=type=cache,target=/root/.cache \
|
||||||
--mount=type=cache,target=migration/target \
|
--mount=type=cache,target=migration/target \
|
||||||
cd migration && /usr/local/cargo/bin/wasm-pack build --target bundler -d bundler -- --features wasm
|
cd migration && /usr/local/cargo/bin/wasm-pack build --target bundler -d bundler -- --features wasm
|
||||||
|
|
||||||
|
RUN --mount=type=cache,target=/root/.cache \
|
||||||
|
--mount=type=cache,target=migration/target \
|
||||||
|
cd migration && /usr/local/cargo/bin/wasm-pack build --target nodejs -d nodejs -- --features wasm
|
||||||
|
|
||||||
# Compile NFT Bridge
|
# Compile NFT Bridge
|
||||||
RUN --mount=type=cache,target=/root/.cache \
|
RUN --mount=type=cache,target=/root/.cache \
|
||||||
--mount=type=cache,target=modules/nft_bridge/target \
|
--mount=type=cache,target=modules/nft_bridge/target \
|
||||||
|
@ -73,6 +77,10 @@ COPY --from=build /usr/src/bridge/bridge/program/bundler explorer/wasm/core
|
||||||
COPY --from=build /usr/src/bridge/modules/token_bridge/program/bundler explorer/wasm/token
|
COPY --from=build /usr/src/bridge/modules/token_bridge/program/bundler explorer/wasm/token
|
||||||
COPY --from=build /usr/src/bridge/modules/nft_bridge/program/bundler explorer/wasm/nft
|
COPY --from=build /usr/src/bridge/modules/nft_bridge/program/bundler explorer/wasm/nft
|
||||||
|
|
||||||
|
COPY --from=build /usr/src/bridge/bridge/program/nodejs sdk/js/src/solana/core-node
|
||||||
|
COPY --from=build /usr/src/bridge/modules/token_bridge/program/nodejs sdk/js/src/solana/token-node
|
||||||
|
COPY --from=build /usr/src/bridge/migration/nodejs sdk/js/src/solana/migration-node
|
||||||
|
COPY --from=build /usr/src/bridge/modules/nft_bridge/program/nodejs sdk/js/src/solana/nft-node
|
||||||
COPY --from=build /usr/src/bridge/bridge/program/nodejs clients/solana/pkg
|
COPY --from=build /usr/src/bridge/bridge/program/nodejs clients/solana/pkg
|
||||||
COPY --from=build /usr/src/bridge/bridge/program/nodejs clients/token_bridge/pkg/core
|
COPY --from=build /usr/src/bridge/bridge/program/nodejs clients/token_bridge/pkg/core
|
||||||
COPY --from=build /usr/src/bridge/bridge/program/nodejs clients/nft_bridge/pkg/core
|
COPY --from=build /usr/src/bridge/bridge/program/nodejs clients/nft_bridge/pkg/core
|
||||||
|
|
Loading…
Reference in New Issue