2021-09-22 12:07:21 -07:00
|
|
|
import {
|
2022-10-26 07:28:46 -07:00
|
|
|
AccountLayout,
|
|
|
|
createCloseAccountInstruction,
|
|
|
|
createInitializeAccountInstruction,
|
|
|
|
createTransferInstruction,
|
|
|
|
getMinimumBalanceForRentExemptMint,
|
|
|
|
getMint,
|
|
|
|
NATIVE_MINT,
|
|
|
|
TOKEN_PROGRAM_ID,
|
|
|
|
} from "@solana/spl-token";
|
|
|
|
import {
|
|
|
|
Commitment,
|
2021-09-22 12:07:21 -07:00
|
|
|
Connection,
|
|
|
|
Keypair,
|
|
|
|
PublicKey,
|
2022-10-26 07:28:46 -07:00
|
|
|
PublicKeyInitData,
|
2021-09-22 12:07:21 -07:00
|
|
|
SystemProgram,
|
|
|
|
Transaction,
|
|
|
|
} from "@solana/web3.js";
|
2021-09-09 21:22:11 -07:00
|
|
|
import { MsgExecuteContract } from "@terra-money/terra.js";
|
2022-04-29 12:57:41 -07:00
|
|
|
import { Algodv2 } from "algosdk";
|
2022-03-02 12:11:00 -08:00
|
|
|
import { ethers, Overrides } from "ethers";
|
2021-09-09 21:22:11 -07:00
|
|
|
import { fromUint8Array } from "js-base64";
|
2022-10-26 07:28:46 -07:00
|
|
|
import BN from "bn.js";
|
|
|
|
import {
|
|
|
|
TransactionSignerPair,
|
|
|
|
_parseVAAAlgorand,
|
|
|
|
_submitVAAAlgorand,
|
|
|
|
} from "../algorand";
|
2021-08-17 18:19:15 -07:00
|
|
|
import { Bridge__factory } from "../ethers-contracts";
|
2021-09-22 12:07:21 -07:00
|
|
|
import {
|
2022-08-04 08:53:08 -07:00
|
|
|
CHAIN_ID_NEAR,
|
2021-09-22 12:07:21 -07:00
|
|
|
CHAIN_ID_SOLANA,
|
2022-08-04 08:53:08 -07:00
|
|
|
ChainId,
|
|
|
|
MAX_VAA_DECIMALS,
|
2022-06-29 10:09:29 -07:00
|
|
|
uint8ArrayToHex,
|
2022-09-28 06:53:15 -07:00
|
|
|
callFunctionNear,
|
2022-10-26 07:57:08 -07:00
|
|
|
hashLookup,
|
2021-09-22 12:07:21 -07:00
|
|
|
} from "../utils";
|
2022-06-29 10:09:29 -07:00
|
|
|
import { MsgExecuteContract as MsgExecuteContractInjective } from "@injectivelabs/sdk-ts";
|
2022-10-26 07:28:46 -07:00
|
|
|
import {
|
|
|
|
createCompleteTransferNativeInstruction,
|
|
|
|
createCompleteTransferWrappedInstruction,
|
|
|
|
} from "../solana/tokenBridge";
|
|
|
|
import { SignedVaa, parseTokenTransferVaa } from "../vaa";
|
|
|
|
import { getForeignAssetNear } from "./getForeignAsset";
|
2022-09-28 06:53:15 -07:00
|
|
|
import { FunctionCallOptions } from "near-api-js/lib/account";
|
|
|
|
import { Provider } from "near-api-js/lib/providers";
|
2022-10-12 08:30:32 -07:00
|
|
|
import { MsgExecuteContract as XplaMsgExecuteContract } from "@xpla/xpla.js";
|
2022-10-24 15:12:02 -07:00
|
|
|
import { AptosClient, Types } from "aptos";
|
2022-10-26 07:57:08 -07:00
|
|
|
import { completeTransferAndRegister } from "../aptos";
|
2021-08-17 18:19:15 -07:00
|
|
|
|
|
|
|
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> } = {}
|
2021-08-17 18:19:15 -07:00
|
|
|
) {
|
|
|
|
const bridge = Bridge__factory.connect(tokenBridgeAddress, signer);
|
2022-03-02 12:11:00 -08:00
|
|
|
const v = await bridge.completeTransfer(signedVAA, overrides);
|
2021-08-17 18:19:15 -07:00
|
|
|
const receipt = await v.wait();
|
|
|
|
return receipt;
|
|
|
|
}
|
|
|
|
|
2021-09-18 17:41:17 -07:00
|
|
|
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> } = {}
|
2021-09-18 17:41:17 -07:00
|
|
|
) {
|
|
|
|
const bridge = Bridge__factory.connect(tokenBridgeAddress, signer);
|
2022-03-02 12:11:00 -08:00
|
|
|
const v = await bridge.completeTransferAndUnwrapETH(signedVAA, overrides);
|
2021-09-18 17:41:17 -07:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2022-06-29 10:09:29 -07:00
|
|
|
/**
|
|
|
|
* Submits the supplied VAA to Injective
|
|
|
|
* @param tokenBridgeAddress Address of Inj token bridge contract
|
|
|
|
* @param walletAddress Address of wallet in inj format
|
|
|
|
* @param signedVAA VAA with the attestation message
|
|
|
|
* @returns Message to be broadcast
|
|
|
|
*/
|
|
|
|
export async function submitVAAOnInjective(
|
|
|
|
tokenBridgeAddress: string,
|
|
|
|
walletAddress: string,
|
|
|
|
signedVAA: Uint8Array
|
|
|
|
): Promise<MsgExecuteContractInjective> {
|
|
|
|
return MsgExecuteContractInjective.fromJSON({
|
|
|
|
contractAddress: tokenBridgeAddress,
|
|
|
|
sender: walletAddress,
|
|
|
|
msg: {
|
|
|
|
data: fromUint8Array(signedVAA),
|
|
|
|
},
|
|
|
|
action: "submit_vaa",
|
|
|
|
});
|
|
|
|
}
|
|
|
|
export const redeemOnInjective = submitVAAOnInjective;
|
|
|
|
|
2022-10-12 08:30:32 -07:00
|
|
|
export function redeemOnXpla(
|
|
|
|
tokenBridgeAddress: string,
|
|
|
|
walletAddress: string,
|
|
|
|
signedVAA: Uint8Array
|
|
|
|
): XplaMsgExecuteContract {
|
|
|
|
return new XplaMsgExecuteContract(walletAddress, tokenBridgeAddress, {
|
|
|
|
submit_vaa: {
|
|
|
|
data: fromUint8Array(signedVAA),
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-09-22 12:07:21 -07:00
|
|
|
export async function redeemAndUnwrapOnSolana(
|
|
|
|
connection: Connection,
|
2022-10-26 07:28:46 -07:00
|
|
|
bridgeAddress: PublicKeyInitData,
|
|
|
|
tokenBridgeAddress: PublicKeyInitData,
|
|
|
|
payerAddress: PublicKeyInitData,
|
|
|
|
signedVaa: SignedVaa,
|
|
|
|
commitment?: Commitment
|
2021-09-22 12:07:21 -07:00
|
|
|
) {
|
2022-10-26 07:28:46 -07:00
|
|
|
const parsed = parseTokenTransferVaa(signedVaa);
|
|
|
|
const targetPublicKey = new PublicKey(parsed.to);
|
|
|
|
const targetAmount = await getMint(connection, NATIVE_MINT, commitment).then(
|
|
|
|
(info) =>
|
|
|
|
parsed.amount * BigInt(Math.pow(10, info.decimals - MAX_VAA_DECIMALS))
|
2021-09-22 12:07:21 -07:00
|
|
|
);
|
2022-10-26 07:28:46 -07:00
|
|
|
const rentBalance = await getMinimumBalanceForRentExemptMint(connection);
|
|
|
|
if (Buffer.compare(parsed.tokenAddress, NATIVE_MINT.toBuffer()) != 0) {
|
|
|
|
return Promise.reject("tokenAddress != NATIVE_MINT");
|
2021-09-22 12:07:21 -07:00
|
|
|
}
|
|
|
|
const payerPublicKey = new PublicKey(payerAddress);
|
|
|
|
const ancillaryKeypair = Keypair.generate();
|
|
|
|
|
2022-10-26 07:28:46 -07:00
|
|
|
const completeTransferIx = createCompleteTransferNativeInstruction(
|
|
|
|
tokenBridgeAddress,
|
|
|
|
bridgeAddress,
|
|
|
|
payerPublicKey,
|
|
|
|
signedVaa
|
2021-09-22 12:07:21 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
//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
|
2022-10-26 07:28:46 -07:00
|
|
|
const initAccountIx = await createInitializeAccountInstruction(
|
2021-09-22 12:07:21 -07:00
|
|
|
ancillaryKeypair.publicKey,
|
2022-10-26 07:28:46 -07:00
|
|
|
NATIVE_MINT,
|
2021-09-22 12:07:21 -07:00
|
|
|
payerPublicKey
|
|
|
|
);
|
|
|
|
|
|
|
|
//Send in the amount of wSOL which we want converted to SOL
|
2022-10-26 07:28:46 -07:00
|
|
|
const balanceTransferIx = createTransferInstruction(
|
2021-09-22 12:07:21 -07:00
|
|
|
targetPublicKey,
|
|
|
|
ancillaryKeypair.publicKey,
|
|
|
|
payerPublicKey,
|
2022-10-26 07:28:46 -07:00
|
|
|
targetAmount.valueOf()
|
2021-09-22 12:07:21 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
//Close the ancillary account for cleanup. Payer address receives any remaining funds
|
2022-10-26 07:28:46 -07:00
|
|
|
const closeAccountIx = createCloseAccountInstruction(
|
2021-09-22 12:07:21 -07:00
|
|
|
ancillaryKeypair.publicKey, //account to close
|
|
|
|
payerPublicKey, //Remaining funds destination
|
2022-10-26 07:28:46 -07:00
|
|
|
payerPublicKey //authority
|
2021-09-22 12:07:21 -07:00
|
|
|
);
|
|
|
|
|
2022-10-26 07:28:46 -07:00
|
|
|
const { blockhash } = await connection.getLatestBlockhash(commitment);
|
2021-09-22 12:07:21 -07:00
|
|
|
const transaction = new Transaction();
|
|
|
|
transaction.recentBlockhash = blockhash;
|
2022-10-26 07:28:46 -07:00
|
|
|
transaction.feePayer = payerPublicKey;
|
|
|
|
transaction.add(
|
|
|
|
completeTransferIx,
|
|
|
|
createAncillaryAccountIx,
|
|
|
|
initAccountIx,
|
|
|
|
balanceTransferIx,
|
|
|
|
closeAccountIx
|
|
|
|
);
|
2021-09-22 12:07:21 -07:00
|
|
|
transaction.partialSign(ancillaryKeypair);
|
|
|
|
return transaction;
|
|
|
|
}
|
|
|
|
|
2021-08-17 18:19:15 -07:00
|
|
|
export async function redeemOnSolana(
|
|
|
|
connection: Connection,
|
2022-10-26 07:28:46 -07:00
|
|
|
bridgeAddress: PublicKeyInitData,
|
|
|
|
tokenBridgeAddress: PublicKeyInitData,
|
|
|
|
payerAddress: PublicKeyInitData,
|
|
|
|
signedVaa: SignedVaa,
|
|
|
|
feeRecipientAddress?: PublicKeyInitData,
|
|
|
|
commitment?: Commitment
|
2021-08-17 18:19:15 -07:00
|
|
|
) {
|
2022-10-26 07:28:46 -07:00
|
|
|
const parsed = parseTokenTransferVaa(signedVaa);
|
|
|
|
const createCompleteTransferInstruction =
|
|
|
|
parsed.tokenChain == CHAIN_ID_SOLANA
|
|
|
|
? createCompleteTransferNativeInstruction
|
|
|
|
: createCompleteTransferWrappedInstruction;
|
|
|
|
const transaction = new Transaction().add(
|
|
|
|
createCompleteTransferInstruction(
|
|
|
|
tokenBridgeAddress,
|
|
|
|
bridgeAddress,
|
|
|
|
payerAddress,
|
|
|
|
parsed,
|
|
|
|
feeRecipientAddress
|
|
|
|
)
|
|
|
|
);
|
|
|
|
const { blockhash } = await connection.getLatestBlockhash(commitment);
|
2021-08-17 18:19:15 -07:00
|
|
|
transaction.recentBlockhash = blockhash;
|
|
|
|
transaction.feePayer = new PublicKey(payerAddress);
|
|
|
|
return transaction;
|
|
|
|
}
|
2022-04-29 12:57:41 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* This basically just submits the VAA to Algorand
|
|
|
|
* @param client AlgodV2 client
|
|
|
|
* @param tokenBridgeId Token bridge ID
|
|
|
|
* @param bridgeId Core bridge ID
|
|
|
|
* @param vaa The VAA to be redeemed
|
|
|
|
* @param acct Sending account
|
|
|
|
* @returns Transaction ID(s)
|
|
|
|
*/
|
|
|
|
export async function redeemOnAlgorand(
|
|
|
|
client: Algodv2,
|
|
|
|
tokenBridgeId: bigint,
|
|
|
|
bridgeId: bigint,
|
|
|
|
vaa: Uint8Array,
|
|
|
|
senderAddr: string
|
|
|
|
): Promise<TransactionSignerPair[]> {
|
|
|
|
return await _submitVAAAlgorand(
|
|
|
|
client,
|
|
|
|
tokenBridgeId,
|
|
|
|
bridgeId,
|
|
|
|
vaa,
|
|
|
|
senderAddr
|
|
|
|
);
|
|
|
|
}
|
2022-08-04 08:53:08 -07:00
|
|
|
|
|
|
|
export async function redeemOnNear(
|
2022-09-28 06:53:15 -07:00
|
|
|
provider: Provider,
|
|
|
|
account: string,
|
2022-08-04 08:53:08 -07:00
|
|
|
tokenBridge: string,
|
|
|
|
vaa: Uint8Array
|
2022-09-28 06:53:15 -07:00
|
|
|
): Promise<FunctionCallOptions[]> {
|
|
|
|
const options: FunctionCallOptions[] = [];
|
|
|
|
const p = _parseVAAAlgorand(vaa);
|
2022-08-04 08:53:08 -07:00
|
|
|
|
|
|
|
if (p.ToChain !== CHAIN_ID_NEAR) {
|
|
|
|
throw new Error("Not destined for NEAR");
|
|
|
|
}
|
|
|
|
|
2022-09-28 06:53:15 -07:00
|
|
|
const { found, value: receiver } = await hashLookup(
|
|
|
|
provider,
|
|
|
|
tokenBridge,
|
|
|
|
uint8ArrayToHex(p.ToAddress as Uint8Array)
|
|
|
|
);
|
2022-08-04 08:53:08 -07:00
|
|
|
|
2022-09-28 06:53:15 -07:00
|
|
|
if (!found) {
|
2022-08-04 08:53:08 -07:00
|
|
|
throw new Error(
|
|
|
|
"Unregistered receiver (receiving account is not registered)"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-09-28 06:53:15 -07:00
|
|
|
const token = await getForeignAssetNear(
|
|
|
|
provider,
|
2022-08-04 08:53:08 -07:00
|
|
|
tokenBridge,
|
|
|
|
p.FromChain as ChainId,
|
|
|
|
p.Contract as string
|
|
|
|
);
|
|
|
|
|
|
|
|
if (
|
|
|
|
(p.Contract as string) !==
|
|
|
|
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
|
|
) {
|
2022-09-28 06:53:15 -07:00
|
|
|
if (token === "" || token === null) {
|
|
|
|
throw new Error("Unregistered token (has it been attested?)");
|
|
|
|
}
|
|
|
|
|
|
|
|
const bal = await callFunctionNear(
|
|
|
|
provider,
|
|
|
|
token as string,
|
|
|
|
"storage_balance_of",
|
|
|
|
{
|
|
|
|
account_id: receiver,
|
|
|
|
}
|
|
|
|
);
|
2022-08-04 08:53:08 -07:00
|
|
|
|
|
|
|
if (bal === null) {
|
2022-09-28 06:53:15 -07:00
|
|
|
options.push({
|
|
|
|
contractId: token as string,
|
|
|
|
methodName: "storage_deposit",
|
|
|
|
args: { account_id: receiver, registration_only: true },
|
|
|
|
gas: new BN("100000000000000"),
|
|
|
|
attachedDeposit: new BN("2000000000000000000000"), // 0.002 NEAR
|
|
|
|
});
|
2022-08-04 08:53:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (
|
|
|
|
p.Fee !== undefined &&
|
|
|
|
Buffer.compare(
|
|
|
|
p.Fee,
|
|
|
|
Buffer.from(
|
|
|
|
"0000000000000000000000000000000000000000000000000000000000000000",
|
|
|
|
"hex"
|
|
|
|
)
|
|
|
|
) !== 0
|
|
|
|
) {
|
2022-09-28 06:53:15 -07:00
|
|
|
const bal = await callFunctionNear(
|
|
|
|
provider,
|
2022-08-04 08:53:08 -07:00
|
|
|
token as string,
|
|
|
|
"storage_balance_of",
|
|
|
|
{
|
2022-09-28 06:53:15 -07:00
|
|
|
account_id: account,
|
2022-08-04 08:53:08 -07:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
if (bal === null) {
|
2022-09-28 06:53:15 -07:00
|
|
|
options.push({
|
|
|
|
contractId: token as string,
|
|
|
|
methodName: "storage_deposit",
|
|
|
|
args: { account_id: account, registration_only: true },
|
|
|
|
gas: new BN("100000000000000"),
|
|
|
|
attachedDeposit: new BN("2000000000000000000000"), // 0.002 NEAR
|
|
|
|
});
|
2022-08-04 08:53:08 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-28 06:53:15 -07:00
|
|
|
options.push({
|
2022-08-04 08:53:08 -07:00
|
|
|
contractId: tokenBridge,
|
|
|
|
methodName: "submit_vaa",
|
|
|
|
args: {
|
|
|
|
vaa: uint8ArrayToHex(vaa),
|
|
|
|
},
|
|
|
|
attachedDeposit: new BN("100000000000000000000000"),
|
|
|
|
gas: new BN("150000000000000"),
|
|
|
|
});
|
|
|
|
|
2022-09-28 06:53:15 -07:00
|
|
|
options.push({
|
2022-08-04 08:53:08 -07:00
|
|
|
contractId: tokenBridge,
|
|
|
|
methodName: "submit_vaa",
|
|
|
|
args: {
|
|
|
|
vaa: uint8ArrayToHex(vaa),
|
|
|
|
},
|
|
|
|
attachedDeposit: new BN("100000000000000000000000"),
|
|
|
|
gas: new BN("150000000000000"),
|
|
|
|
});
|
|
|
|
|
2022-09-28 06:53:15 -07:00
|
|
|
return options;
|
2022-08-04 08:53:08 -07:00
|
|
|
}
|
2022-10-24 15:12:02 -07:00
|
|
|
|
2022-10-24 22:15:21 -07:00
|
|
|
/**
|
|
|
|
* Register the token specified in the given VAA in the transfer recipient's account if necessary
|
|
|
|
* and complete the transfer.
|
|
|
|
* @param client Client used to transfer data to/from Aptos node
|
|
|
|
* @param tokenBridgeAddress Address of token bridge
|
|
|
|
* @param transferVAA Bytes of transfer VAA
|
|
|
|
* @returns Transaction payload
|
|
|
|
*/
|
2022-10-24 15:12:02 -07:00
|
|
|
export function redeemOnAptos(
|
|
|
|
client: AptosClient,
|
|
|
|
tokenBridgeAddress: string,
|
|
|
|
transferVAA: Uint8Array
|
|
|
|
): Promise<Types.EntryFunctionPayload> {
|
2022-10-26 07:57:08 -07:00
|
|
|
return completeTransferAndRegister(client, tokenBridgeAddress, transferVAA);
|
2022-10-24 15:12:02 -07:00
|
|
|
}
|