wormhole/sdk/js/src/token_bridge/redeem.ts

192 lines
5.6 KiB
TypeScript
Raw Normal View History

import { AccountLayout, Token, TOKEN_PROGRAM_ID, u64 } from "@solana/spl-token";
import {
Connection,
Keypair,
PublicKey,
SystemProgram,
Transaction,
} from "@solana/web3.js";
import { MsgExecuteContract } from "@terra-money/terra.js";
2022-03-02 12:11:00 -08:00
import { ethers, Overrides } from "ethers";
import { fromUint8Array } from "js-base64";
import { Bridge__factory } from "../ethers-contracts";
import { ixFromRust } from "../solana";
import { importCoreWasm, importTokenWasm } from "../solana/wasm";
import {
CHAIN_ID_SOLANA,
WSOL_ADDRESS,
WSOL_DECIMALS,
MAX_VAA_DECIMALS,
} from "../utils";
import { hexToNativeString } from "../utils/array";
import { parseTransferPayload } from "../utils/parseVaa";
export async function redeemOnEth(
tokenBridgeAddress: string,
signer: ethers.Signer,
2022-03-02 12:11:00 -08:00
signedVAA: Uint8Array,
overrides: Overrides & { from?: string | Promise<string> } = {}
) {
const bridge = Bridge__factory.connect(tokenBridgeAddress, signer);
2022-03-02 12:11:00 -08:00
const v = await bridge.completeTransfer(signedVAA, overrides);
const receipt = await v.wait();
return receipt;
}
export async function redeemOnEthNative(
tokenBridgeAddress: string,
signer: ethers.Signer,
2022-03-02 12:11:00 -08:00
signedVAA: Uint8Array,
overrides: Overrides & { from?: string | Promise<string> } = {}
) {
const bridge = Bridge__factory.connect(tokenBridgeAddress, signer);
2022-03-02 12:11:00 -08:00
const v = await bridge.completeTransferAndUnwrapETH(signedVAA, overrides);
const receipt = await v.wait();
return receipt;
}
export async function redeemOnTerra(
tokenBridgeAddress: string,
walletAddress: string,
signedVAA: Uint8Array
) {
return new MsgExecuteContract(walletAddress, tokenBridgeAddress, {
submit_vaa: {
data: fromUint8Array(signedVAA),
},
});
}
export async function redeemAndUnwrapOnSolana(
connection: Connection,
bridgeAddress: string,
tokenBridgeAddress: string,
payerAddress: string,
signedVAA: Uint8Array
) {
const { parse_vaa } = await importCoreWasm();
const { complete_transfer_native_ix } = await importTokenWasm();
const parsedVAA = parse_vaa(signedVAA);
const parsedPayload = parseTransferPayload(
Buffer.from(new Uint8Array(parsedVAA.payload))
);
const targetAddress = hexToNativeString(
parsedPayload.targetAddress,
CHAIN_ID_SOLANA
);
if (!targetAddress) {
throw new Error("Failed to read the target address.");
}
const targetPublicKey = new PublicKey(targetAddress);
const targetAmount =
parsedPayload.amount *
BigInt(WSOL_DECIMALS - MAX_VAA_DECIMALS) *
BigInt(10);
const rentBalance = await Token.getMinBalanceRentForExemptAccount(connection);
const mintPublicKey = new PublicKey(WSOL_ADDRESS);
const payerPublicKey = new PublicKey(payerAddress);
const ancillaryKeypair = Keypair.generate();
const completeTransferIx = ixFromRust(
complete_transfer_native_ix(
tokenBridgeAddress,
bridgeAddress,
payerAddress,
signedVAA
)
);
//This will create a temporary account where the wSOL will be moved
const createAncillaryAccountIx = SystemProgram.createAccount({
fromPubkey: payerPublicKey,
newAccountPubkey: ancillaryKeypair.publicKey,
lamports: rentBalance, //spl token accounts need rent exemption
space: AccountLayout.span,
programId: TOKEN_PROGRAM_ID,
});
//Initialize the account as a WSOL account, with the original payerAddress as owner
const initAccountIx = await Token.createInitAccountInstruction(
TOKEN_PROGRAM_ID,
mintPublicKey,
ancillaryKeypair.publicKey,
payerPublicKey
);
//Send in the amount of wSOL which we want converted to SOL
const balanceTransferIx = Token.createTransferInstruction(
TOKEN_PROGRAM_ID,
targetPublicKey,
ancillaryKeypair.publicKey,
payerPublicKey,
[],
new u64(targetAmount.toString(16), 16)
);
//Close the ancillary account for cleanup. Payer address receives any remaining funds
const closeAccountIx = Token.createCloseAccountInstruction(
TOKEN_PROGRAM_ID,
ancillaryKeypair.publicKey, //account to close
payerPublicKey, //Remaining funds destination
payerPublicKey, //authority
[]
);
const { blockhash } = await connection.getRecentBlockhash();
const transaction = new Transaction();
transaction.recentBlockhash = blockhash;
transaction.feePayer = new PublicKey(payerAddress);
transaction.add(completeTransferIx);
transaction.add(createAncillaryAccountIx);
transaction.add(initAccountIx);
transaction.add(balanceTransferIx);
transaction.add(closeAccountIx);
transaction.partialSign(ancillaryKeypair);
return transaction;
}
export async function redeemOnSolana(
connection: Connection,
bridgeAddress: string,
tokenBridgeAddress: string,
payerAddress: string,
signedVAA: Uint8Array
) {
const { parse_vaa } = await importCoreWasm();
const parsedVAA = parse_vaa(signedVAA);
const isSolanaNative =
Buffer.from(new Uint8Array(parsedVAA.payload)).readUInt16BE(65) ===
CHAIN_ID_SOLANA;
const { complete_transfer_wrapped_ix, complete_transfer_native_ix } =
await importTokenWasm();
const ixs = [];
if (isSolanaNative) {
ixs.push(
ixFromRust(
complete_transfer_native_ix(
tokenBridgeAddress,
bridgeAddress,
payerAddress,
signedVAA
)
)
);
} else {
ixs.push(
ixFromRust(
complete_transfer_wrapped_ix(
tokenBridgeAddress,
bridgeAddress,
payerAddress,
signedVAA
)
)
);
}
const transaction = new Transaction().add(...ixs);
const { blockhash } = await connection.getRecentBlockhash();
transaction.recentBlockhash = blockhash;
transaction.feePayer = new PublicKey(payerAddress);
return transaction;
}