2021-09-22 12:07:21 -07:00
|
|
|
import { AccountLayout, Token, TOKEN_PROGRAM_ID, u64 } from "@solana/spl-token";
|
|
|
|
import {
|
|
|
|
Connection,
|
|
|
|
Keypair,
|
|
|
|
PublicKey,
|
|
|
|
SystemProgram,
|
|
|
|
Transaction,
|
|
|
|
} from "@solana/web3.js";
|
2021-09-09 21:22:11 -07:00
|
|
|
import { MsgExecuteContract } from "@terra-money/terra.js";
|
2021-08-17 18:19:15 -07:00
|
|
|
import { ethers } from "ethers";
|
2021-09-09 21:22:11 -07:00
|
|
|
import { fromUint8Array } from "js-base64";
|
2021-08-17 18:19:15 -07:00
|
|
|
import { Bridge__factory } from "../ethers-contracts";
|
|
|
|
import { ixFromRust } from "../solana";
|
2021-09-22 12:07:21 -07:00
|
|
|
import {
|
|
|
|
CHAIN_ID_SOLANA,
|
|
|
|
WSOL_ADDRESS,
|
|
|
|
WSOL_DECIMALS,
|
|
|
|
MAX_VAA_DECIMALS,
|
|
|
|
} from "../utils";
|
|
|
|
import { hexToNativeString } from "../utils/array";
|
|
|
|
import { parseTransferPayload } from "../utils/parseVaa";
|
2021-08-17 18:19:15 -07:00
|
|
|
|
|
|
|
export async function redeemOnEth(
|
|
|
|
tokenBridgeAddress: string,
|
|
|
|
signer: ethers.Signer,
|
|
|
|
signedVAA: Uint8Array
|
|
|
|
) {
|
|
|
|
const bridge = Bridge__factory.connect(tokenBridgeAddress, signer);
|
|
|
|
const v = await bridge.completeTransfer(signedVAA);
|
|
|
|
const receipt = await v.wait();
|
|
|
|
return receipt;
|
|
|
|
}
|
|
|
|
|
2021-09-18 17:41:17 -07:00
|
|
|
export async function redeemOnEthNative(
|
|
|
|
tokenBridgeAddress: string,
|
|
|
|
signer: ethers.Signer,
|
|
|
|
signedVAA: Uint8Array
|
|
|
|
) {
|
|
|
|
const bridge = Bridge__factory.connect(tokenBridgeAddress, signer);
|
|
|
|
const v = await bridge.completeTransferAndUnwrapETH(signedVAA);
|
|
|
|
const receipt = await v.wait();
|
|
|
|
return receipt;
|
|
|
|
}
|
|
|
|
|
2021-08-30 03:32:31 -07:00
|
|
|
export async function redeemOnTerra(
|
|
|
|
tokenBridgeAddress: string,
|
|
|
|
walletAddress: string,
|
|
|
|
signedVAA: Uint8Array
|
|
|
|
) {
|
2021-10-22 09:33:05 -07:00
|
|
|
return new MsgExecuteContract(walletAddress, tokenBridgeAddress, {
|
|
|
|
submit_vaa: {
|
|
|
|
data: fromUint8Array(signedVAA),
|
2021-08-30 03:32:31 -07:00
|
|
|
},
|
2021-10-22 09:33:05 -07:00
|
|
|
});
|
2021-08-30 03:32:31 -07:00
|
|
|
}
|
|
|
|
|
2021-09-22 12:07:21 -07:00
|
|
|
export async function redeemAndUnwrapOnSolana(
|
|
|
|
connection: Connection,
|
|
|
|
bridgeAddress: string,
|
|
|
|
tokenBridgeAddress: string,
|
|
|
|
payerAddress: string,
|
|
|
|
signedVAA: Uint8Array
|
|
|
|
) {
|
|
|
|
const { parse_vaa } = await import("../solana/core/bridge");
|
|
|
|
const { complete_transfer_native_ix } = await import(
|
|
|
|
"../solana/token/token_bridge"
|
|
|
|
);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-08-17 18:19:15 -07:00
|
|
|
export async function redeemOnSolana(
|
|
|
|
connection: Connection,
|
|
|
|
bridgeAddress: string,
|
|
|
|
tokenBridgeAddress: string,
|
|
|
|
payerAddress: string,
|
2021-08-29 14:03:36 -07:00
|
|
|
signedVAA: Uint8Array
|
2021-08-17 18:19:15 -07:00
|
|
|
) {
|
2021-08-29 14:03:36 -07:00
|
|
|
const { parse_vaa } = await import("../solana/core/bridge");
|
|
|
|
const parsedVAA = parse_vaa(signedVAA);
|
|
|
|
const isSolanaNative =
|
|
|
|
Buffer.from(new Uint8Array(parsedVAA.payload)).readUInt16BE(65) ===
|
|
|
|
CHAIN_ID_SOLANA;
|
2021-08-17 18:19:15 -07:00
|
|
|
const { complete_transfer_wrapped_ix, complete_transfer_native_ix } =
|
|
|
|
await import("../solana/token/token_bridge");
|
|
|
|
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;
|
|
|
|
}
|