2021-09-22 12:07:21 -07:00
|
|
|
import { AccountLayout, Token, TOKEN_PROGRAM_ID, u64 } from "@solana/spl-token";
|
|
|
|
import {
|
|
|
|
Connection,
|
|
|
|
Keypair,
|
|
|
|
PublicKey,
|
|
|
|
SystemProgram,
|
2022-04-29 12:57:41 -07:00
|
|
|
Transaction as SolanaTransaction,
|
2021-09-22 12:07:21 -07:00
|
|
|
} from "@solana/web3.js";
|
2021-08-30 18:07:09 -07:00
|
|
|
import { MsgExecuteContract } from "@terra-money/terra.js";
|
2022-06-29 10:09:29 -07:00
|
|
|
import { MsgExecuteContract as MsgExecuteContractInjective } from "@injectivelabs/sdk-ts";
|
2022-04-29 12:57:41 -07:00
|
|
|
import {
|
|
|
|
Algodv2,
|
|
|
|
bigIntToBytes,
|
|
|
|
getApplicationAddress,
|
|
|
|
makeApplicationCallTxnFromObject,
|
|
|
|
makeAssetTransferTxnWithSuggestedParamsFromObject,
|
|
|
|
makePaymentTxnWithSuggestedParamsFromObject,
|
|
|
|
OnApplicationComplete,
|
|
|
|
SuggestedParams,
|
|
|
|
Transaction as AlgorandTransaction,
|
|
|
|
} from "algosdk";
|
2022-06-29 10:09:29 -07:00
|
|
|
import { ethers, Overrides, PayableOverrides } from "ethers";
|
2022-09-28 06:53:15 -07:00
|
|
|
import { getIsWrappedAssetNear, isNativeDenom } from "..";
|
2022-04-29 12:57:41 -07:00
|
|
|
import {
|
|
|
|
assetOptinCheck,
|
|
|
|
getMessageFee,
|
|
|
|
optin,
|
|
|
|
TransactionSignerPair,
|
|
|
|
} from "../algorand";
|
|
|
|
import { getEmitterAddressAlgorand } from "../bridge";
|
2021-08-17 18:19:15 -07:00
|
|
|
import {
|
|
|
|
Bridge__factory,
|
|
|
|
TokenImplementation__factory,
|
|
|
|
} from "../ethers-contracts";
|
|
|
|
import { getBridgeFeeIx, ixFromRust } from "../solana";
|
2021-11-05 12:18:12 -07:00
|
|
|
import { importTokenWasm } from "../solana/wasm";
|
2022-04-29 12:57:41 -07:00
|
|
|
import {
|
|
|
|
ChainId,
|
2022-05-05 09:35:11 -07:00
|
|
|
ChainName,
|
2022-08-04 08:53:08 -07:00
|
|
|
WSOL_ADDRESS,
|
2022-05-05 09:35:11 -07:00
|
|
|
coalesceChainId,
|
2022-04-29 12:57:41 -07:00
|
|
|
createNonce,
|
|
|
|
hexToUint8Array,
|
|
|
|
textToUint8Array,
|
2022-08-04 08:53:08 -07:00
|
|
|
uint8ArrayToHex,
|
2022-06-29 10:09:29 -07:00
|
|
|
CHAIN_ID_SOLANA,
|
2022-09-28 06:53:15 -07:00
|
|
|
callFunctionNear,
|
2022-04-29 12:57:41 -07:00
|
|
|
} from "../utils";
|
|
|
|
import { safeBigIntToNumber } from "../utils/bigint";
|
2022-06-29 10:09:29 -07:00
|
|
|
import { isNativeDenomInjective } from "../cosmwasm";
|
2022-08-04 08:53:08 -07:00
|
|
|
const BN = require("bn.js");
|
2022-09-28 06:53:15 -07:00
|
|
|
import { FunctionCallOptions } from "near-api-js/lib/account";
|
|
|
|
import { Provider } from "near-api-js/lib/providers";
|
2021-08-17 18:19:15 -07:00
|
|
|
|
2021-09-13 21:45:26 -07:00
|
|
|
export async function getAllowanceEth(
|
|
|
|
tokenBridgeAddress: string,
|
|
|
|
tokenAddress: string,
|
|
|
|
signer: ethers.Signer
|
|
|
|
) {
|
|
|
|
const token = TokenImplementation__factory.connect(tokenAddress, signer);
|
|
|
|
const signerAddress = await signer.getAddress();
|
|
|
|
const allowance = await token.allowance(signerAddress, tokenBridgeAddress);
|
|
|
|
|
|
|
|
return allowance;
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function approveEth(
|
|
|
|
tokenBridgeAddress: string,
|
|
|
|
tokenAddress: string,
|
|
|
|
signer: ethers.Signer,
|
2022-03-02 12:11:00 -08:00
|
|
|
amount: ethers.BigNumberish,
|
|
|
|
overrides: Overrides & { from?: string | Promise<string> } = {}
|
2021-09-13 21:45:26 -07:00
|
|
|
) {
|
|
|
|
const token = TokenImplementation__factory.connect(tokenAddress, signer);
|
2022-03-02 12:11:00 -08:00
|
|
|
return await (
|
|
|
|
await token.approve(tokenBridgeAddress, amount, overrides)
|
|
|
|
).wait();
|
2021-09-13 21:45:26 -07:00
|
|
|
}
|
|
|
|
|
2021-08-17 18:19:15 -07:00
|
|
|
export async function transferFromEth(
|
|
|
|
tokenBridgeAddress: string,
|
|
|
|
signer: ethers.Signer,
|
|
|
|
tokenAddress: string,
|
|
|
|
amount: ethers.BigNumberish,
|
2022-05-05 09:35:11 -07:00
|
|
|
recipientChain: ChainId | ChainName,
|
2022-03-03 07:00:18 -08:00
|
|
|
recipientAddress: Uint8Array,
|
2022-03-02 12:11:00 -08:00
|
|
|
relayerFee: ethers.BigNumberish = 0,
|
2022-05-23 09:10:35 -07:00
|
|
|
overrides: PayableOverrides & { from?: string | Promise<string> } = {},
|
|
|
|
payload: Uint8Array | null = null
|
2021-08-17 18:19:15 -07:00
|
|
|
) {
|
2022-05-05 09:35:11 -07:00
|
|
|
const recipientChainId = coalesceChainId(recipientChain);
|
2021-08-17 18:19:15 -07:00
|
|
|
const bridge = Bridge__factory.connect(tokenBridgeAddress, signer);
|
2022-05-23 09:10:35 -07:00
|
|
|
const v =
|
|
|
|
payload === null
|
|
|
|
? await bridge.transferTokens(
|
|
|
|
tokenAddress,
|
|
|
|
amount,
|
|
|
|
recipientChainId,
|
|
|
|
recipientAddress,
|
|
|
|
relayerFee,
|
|
|
|
createNonce(),
|
|
|
|
overrides
|
|
|
|
)
|
|
|
|
: await bridge.transferTokensWithPayload(
|
|
|
|
tokenAddress,
|
|
|
|
amount,
|
|
|
|
recipientChainId,
|
|
|
|
recipientAddress,
|
|
|
|
createNonce(),
|
|
|
|
payload,
|
|
|
|
overrides
|
|
|
|
);
|
2022-05-20 15:40:17 -07:00
|
|
|
const receipt = await v.wait();
|
|
|
|
return receipt;
|
|
|
|
}
|
|
|
|
|
2021-09-18 17:41:17 -07:00
|
|
|
export async function transferFromEthNative(
|
|
|
|
tokenBridgeAddress: string,
|
|
|
|
signer: ethers.Signer,
|
|
|
|
amount: ethers.BigNumberish,
|
2022-05-05 09:35:11 -07:00
|
|
|
recipientChain: ChainId | ChainId,
|
2022-03-03 07:00:18 -08:00
|
|
|
recipientAddress: Uint8Array,
|
2022-03-02 12:11:00 -08:00
|
|
|
relayerFee: ethers.BigNumberish = 0,
|
2022-05-23 09:10:35 -07:00
|
|
|
overrides: PayableOverrides & { from?: string | Promise<string> } = {},
|
|
|
|
payload: Uint8Array | null = null
|
2022-05-20 15:40:17 -07:00
|
|
|
) {
|
|
|
|
const recipientChainId = coalesceChainId(recipientChain);
|
|
|
|
const bridge = Bridge__factory.connect(tokenBridgeAddress, signer);
|
2022-05-23 09:10:35 -07:00
|
|
|
const v =
|
|
|
|
payload === null
|
|
|
|
? await bridge.wrapAndTransferETH(
|
|
|
|
recipientChainId,
|
|
|
|
recipientAddress,
|
|
|
|
relayerFee,
|
|
|
|
createNonce(),
|
|
|
|
{
|
|
|
|
...overrides,
|
|
|
|
value: amount,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
: await bridge.wrapAndTransferETHWithPayload(
|
|
|
|
recipientChainId,
|
|
|
|
recipientAddress,
|
|
|
|
createNonce(),
|
|
|
|
payload,
|
|
|
|
{
|
|
|
|
...overrides,
|
|
|
|
value: amount,
|
|
|
|
}
|
|
|
|
);
|
2022-05-20 15:40:17 -07:00
|
|
|
const receipt = await v.wait();
|
|
|
|
return receipt;
|
|
|
|
}
|
|
|
|
|
2021-08-20 04:22:15 -07:00
|
|
|
export async function transferFromTerra(
|
2021-08-30 03:32:31 -07:00
|
|
|
walletAddress: string,
|
2021-08-20 04:22:15 -07:00
|
|
|
tokenBridgeAddress: string,
|
|
|
|
tokenAddress: string,
|
2021-08-30 18:07:09 -07:00
|
|
|
amount: string,
|
2022-05-05 09:35:11 -07:00
|
|
|
recipientChain: ChainId | ChainName,
|
2022-03-03 07:00:18 -08:00
|
|
|
recipientAddress: Uint8Array,
|
2022-05-23 09:38:27 -07:00
|
|
|
relayerFee: string = "0",
|
|
|
|
payload: Uint8Array | null = null
|
2021-08-20 04:22:15 -07:00
|
|
|
) {
|
2022-05-05 09:35:11 -07:00
|
|
|
const recipientChainId = coalesceChainId(recipientChain);
|
2021-08-20 04:22:15 -07:00
|
|
|
const nonce = Math.round(Math.random() * 100000);
|
2021-10-04 04:52:11 -07:00
|
|
|
const isNativeAsset = isNativeDenom(tokenAddress);
|
2022-05-23 09:38:27 -07:00
|
|
|
const mk_initiate_transfer = (info: object) =>
|
|
|
|
payload
|
|
|
|
? {
|
|
|
|
initiate_transfer_with_payload: {
|
|
|
|
asset: {
|
|
|
|
amount,
|
|
|
|
info,
|
|
|
|
},
|
|
|
|
recipient_chain: recipientChainId,
|
|
|
|
recipient: Buffer.from(recipientAddress).toString("base64"),
|
|
|
|
fee: relayerFee,
|
|
|
|
nonce: nonce,
|
|
|
|
payload: payload,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
: {
|
|
|
|
initiate_transfer: {
|
|
|
|
asset: {
|
|
|
|
amount,
|
|
|
|
info,
|
|
|
|
},
|
|
|
|
recipient_chain: recipientChainId,
|
|
|
|
recipient: Buffer.from(recipientAddress).toString("base64"),
|
|
|
|
fee: relayerFee,
|
|
|
|
nonce: nonce,
|
|
|
|
},
|
|
|
|
};
|
2021-10-04 04:52:11 -07:00
|
|
|
return isNativeAsset
|
|
|
|
? [
|
2021-10-04 04:52:11 -07:00
|
|
|
new MsgExecuteContract(
|
|
|
|
walletAddress,
|
|
|
|
tokenBridgeAddress,
|
|
|
|
{
|
|
|
|
deposit_tokens: {},
|
|
|
|
},
|
|
|
|
{ [tokenAddress]: amount }
|
|
|
|
),
|
2021-10-04 04:52:11 -07:00
|
|
|
new MsgExecuteContract(
|
2021-10-04 04:52:11 -07:00
|
|
|
walletAddress,
|
|
|
|
tokenBridgeAddress,
|
2022-05-23 09:38:27 -07:00
|
|
|
mk_initiate_transfer({
|
|
|
|
native_token: {
|
|
|
|
denom: tokenAddress,
|
2021-10-04 04:52:11 -07:00
|
|
|
},
|
2022-05-23 09:38:27 -07:00
|
|
|
}),
|
2021-10-11 03:06:39 -07:00
|
|
|
{}
|
2021-10-04 04:52:11 -07:00
|
|
|
),
|
|
|
|
]
|
|
|
|
: [
|
|
|
|
new MsgExecuteContract(
|
|
|
|
walletAddress,
|
|
|
|
tokenAddress,
|
|
|
|
{
|
|
|
|
increase_allowance: {
|
|
|
|
spender: tokenBridgeAddress,
|
|
|
|
amount: amount,
|
|
|
|
expires: {
|
|
|
|
never: {},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2021-10-11 03:06:39 -07:00
|
|
|
{}
|
2021-10-04 04:52:11 -07:00
|
|
|
),
|
|
|
|
new MsgExecuteContract(
|
2021-10-04 04:52:11 -07:00
|
|
|
walletAddress,
|
|
|
|
tokenBridgeAddress,
|
2022-05-23 09:38:27 -07:00
|
|
|
mk_initiate_transfer({
|
|
|
|
token: {
|
|
|
|
contract_addr: tokenAddress,
|
2021-10-04 04:52:11 -07:00
|
|
|
},
|
2022-05-23 09:38:27 -07:00
|
|
|
}),
|
2021-10-11 03:06:39 -07:00
|
|
|
{}
|
2021-10-04 04:52:11 -07:00
|
|
|
),
|
2021-10-04 04:52:11 -07:00
|
|
|
];
|
2021-08-20 04:22:15 -07:00
|
|
|
}
|
|
|
|
|
2022-06-29 10:09:29 -07:00
|
|
|
/**
|
|
|
|
* Creates the necessary messages to transfer an asset
|
|
|
|
* @param walletAddress Address of the Inj wallet
|
|
|
|
* @param tokenBridgeAddress Address of the token bridge contract
|
|
|
|
* @param tokenAddress Address of the token being transferred
|
|
|
|
* @param amount Amount of token to be transferred
|
|
|
|
* @param recipientChain Destination chain
|
|
|
|
* @param recipientAddress Destination wallet address
|
|
|
|
* @param relayerFee Relayer fee
|
|
|
|
* @param payload Optional payload
|
|
|
|
* @returns Transfer messages to be sent on chain
|
|
|
|
*/
|
|
|
|
export async function transferFromInjective(
|
|
|
|
walletAddress: string,
|
|
|
|
tokenBridgeAddress: string,
|
|
|
|
tokenAddress: string,
|
|
|
|
amount: string,
|
|
|
|
recipientChain: ChainId | ChainName,
|
|
|
|
recipientAddress: Uint8Array,
|
|
|
|
relayerFee: string = "0",
|
|
|
|
payload: Uint8Array | null = null
|
|
|
|
) {
|
|
|
|
const recipientChainId = coalesceChainId(recipientChain);
|
|
|
|
const nonce = Math.round(Math.random() * 100000);
|
|
|
|
const isNativeAsset = isNativeDenomInjective(tokenAddress);
|
|
|
|
const mk_action: string = payload
|
|
|
|
? "initiate_transfer_with_payload"
|
|
|
|
: "initiate_transfer";
|
|
|
|
const mk_initiate_transfer = (info: object) =>
|
|
|
|
payload
|
|
|
|
? {
|
|
|
|
asset: {
|
|
|
|
amount,
|
|
|
|
info,
|
|
|
|
},
|
|
|
|
recipient_chain: recipientChainId,
|
|
|
|
recipient: Buffer.from(recipientAddress).toString("base64"),
|
|
|
|
fee: relayerFee,
|
|
|
|
nonce: nonce,
|
|
|
|
payload: payload,
|
|
|
|
}
|
|
|
|
: {
|
|
|
|
asset: {
|
|
|
|
amount,
|
|
|
|
info,
|
|
|
|
},
|
|
|
|
recipient_chain: recipientChainId,
|
|
|
|
recipient: Buffer.from(recipientAddress).toString("base64"),
|
|
|
|
fee: relayerFee,
|
|
|
|
nonce: nonce,
|
|
|
|
};
|
|
|
|
return isNativeAsset
|
|
|
|
? [
|
|
|
|
MsgExecuteContractInjective.fromJSON({
|
|
|
|
contractAddress: tokenBridgeAddress,
|
|
|
|
sender: walletAddress,
|
|
|
|
msg: {},
|
|
|
|
action: "deposit_tokens",
|
|
|
|
amount: { denom: tokenAddress, amount: amount },
|
|
|
|
}),
|
|
|
|
MsgExecuteContractInjective.fromJSON({
|
|
|
|
contractAddress: tokenBridgeAddress,
|
|
|
|
sender: walletAddress,
|
|
|
|
msg: mk_initiate_transfer({ native_token: { denom: tokenAddress } }),
|
|
|
|
action: mk_action,
|
|
|
|
}),
|
|
|
|
]
|
|
|
|
: [
|
|
|
|
MsgExecuteContractInjective.fromJSON({
|
|
|
|
contractAddress: tokenBridgeAddress,
|
|
|
|
sender: walletAddress,
|
|
|
|
msg: {
|
|
|
|
spender: tokenBridgeAddress,
|
|
|
|
amount: amount,
|
|
|
|
expires: {
|
|
|
|
never: {},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
action: "increase_allowance",
|
|
|
|
}),
|
|
|
|
MsgExecuteContractInjective.fromJSON({
|
|
|
|
contractAddress: tokenBridgeAddress,
|
|
|
|
sender: walletAddress,
|
|
|
|
msg: mk_initiate_transfer({ token: { contract_addr: tokenAddress } }),
|
|
|
|
action: mk_action,
|
|
|
|
}),
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
2021-09-22 12:07:21 -07:00
|
|
|
export async function transferNativeSol(
|
|
|
|
connection: Connection,
|
|
|
|
bridgeAddress: string,
|
|
|
|
tokenBridgeAddress: string,
|
|
|
|
payerAddress: string,
|
|
|
|
amount: BigInt,
|
|
|
|
targetAddress: Uint8Array,
|
2022-05-05 09:35:11 -07:00
|
|
|
targetChain: ChainId | ChainName,
|
2022-05-20 15:40:17 -07:00
|
|
|
relayerFee: BigInt = BigInt(0),
|
2022-05-23 09:10:35 -07:00
|
|
|
payload: Uint8Array | null = null
|
2021-09-22 12:07:21 -07:00
|
|
|
) {
|
|
|
|
//https://github.com/solana-labs/solana-program-library/blob/master/token/js/client/token.js
|
|
|
|
const rentBalance = await Token.getMinBalanceRentForExemptAccount(connection);
|
|
|
|
const mintPublicKey = new PublicKey(WSOL_ADDRESS);
|
|
|
|
const payerPublicKey = new PublicKey(payerAddress);
|
|
|
|
const ancillaryKeypair = Keypair.generate();
|
|
|
|
|
|
|
|
//This will create a temporary account where the wSOL will be created.
|
|
|
|
const createAncillaryAccountIx = SystemProgram.createAccount({
|
|
|
|
fromPubkey: payerPublicKey,
|
|
|
|
newAccountPubkey: ancillaryKeypair.publicKey,
|
|
|
|
lamports: rentBalance, //spl token accounts need rent exemption
|
|
|
|
space: AccountLayout.span,
|
|
|
|
programId: TOKEN_PROGRAM_ID,
|
|
|
|
});
|
|
|
|
|
|
|
|
//Send in the amount of SOL which we want converted to wSOL
|
|
|
|
const initialBalanceTransferIx = SystemProgram.transfer({
|
|
|
|
fromPubkey: payerPublicKey,
|
|
|
|
lamports: Number(amount),
|
|
|
|
toPubkey: ancillaryKeypair.publicKey,
|
|
|
|
});
|
|
|
|
//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
|
|
|
|
);
|
|
|
|
|
|
|
|
//Normal approve & transfer instructions, except that the wSOL is sent from the ancillary account.
|
2022-05-23 09:10:35 -07:00
|
|
|
const {
|
|
|
|
transfer_native_ix,
|
|
|
|
transfer_native_with_payload_ix,
|
|
|
|
approval_authority_address,
|
|
|
|
} = await importTokenWasm();
|
2021-09-22 12:07:21 -07:00
|
|
|
const nonce = createNonce().readUInt32LE(0);
|
|
|
|
const transferIx = await getBridgeFeeIx(
|
|
|
|
connection,
|
|
|
|
bridgeAddress,
|
|
|
|
payerAddress
|
|
|
|
);
|
|
|
|
const approvalIx = Token.createApproveInstruction(
|
|
|
|
TOKEN_PROGRAM_ID,
|
|
|
|
ancillaryKeypair.publicKey,
|
|
|
|
new PublicKey(approval_authority_address(tokenBridgeAddress)),
|
|
|
|
payerPublicKey, //owner
|
|
|
|
[],
|
|
|
|
new u64(amount.toString(16), 16)
|
|
|
|
);
|
|
|
|
let messageKey = Keypair.generate();
|
|
|
|
|
|
|
|
const ix = ixFromRust(
|
2022-05-23 09:10:35 -07:00
|
|
|
payload !== null
|
|
|
|
? transfer_native_with_payload_ix(
|
2022-05-20 15:40:17 -07:00
|
|
|
tokenBridgeAddress,
|
|
|
|
bridgeAddress,
|
|
|
|
payerAddress,
|
|
|
|
messageKey.publicKey.toString(),
|
|
|
|
ancillaryKeypair.publicKey.toString(),
|
|
|
|
WSOL_ADDRESS,
|
|
|
|
nonce,
|
2022-06-14 11:26:21 -07:00
|
|
|
amount.valueOf(),
|
2022-05-20 15:40:17 -07:00
|
|
|
targetAddress,
|
|
|
|
coalesceChainId(targetChain),
|
|
|
|
payload
|
|
|
|
)
|
2022-05-23 09:10:35 -07:00
|
|
|
: transfer_native_ix(
|
2022-05-20 15:40:17 -07:00
|
|
|
tokenBridgeAddress,
|
|
|
|
bridgeAddress,
|
|
|
|
payerAddress,
|
|
|
|
messageKey.publicKey.toString(),
|
|
|
|
ancillaryKeypair.publicKey.toString(),
|
|
|
|
WSOL_ADDRESS,
|
|
|
|
nonce,
|
2022-06-14 11:26:21 -07:00
|
|
|
amount.valueOf(),
|
2022-06-14 11:47:17 -07:00
|
|
|
relayerFee.valueOf(),
|
2022-05-20 15:40:17 -07:00
|
|
|
targetAddress,
|
|
|
|
coalesceChainId(targetChain)
|
|
|
|
)
|
2021-09-22 12:07:21 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
//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();
|
2022-04-29 12:57:41 -07:00
|
|
|
const transaction = new SolanaTransaction();
|
2021-09-22 12:07:21 -07:00
|
|
|
transaction.recentBlockhash = blockhash;
|
|
|
|
transaction.feePayer = new PublicKey(payerAddress);
|
|
|
|
transaction.add(createAncillaryAccountIx);
|
|
|
|
transaction.add(initialBalanceTransferIx);
|
|
|
|
transaction.add(initAccountIx);
|
|
|
|
transaction.add(transferIx, approvalIx, ix);
|
|
|
|
transaction.add(closeAccountIx);
|
|
|
|
transaction.partialSign(messageKey);
|
|
|
|
transaction.partialSign(ancillaryKeypair);
|
|
|
|
return transaction;
|
|
|
|
}
|
|
|
|
|
2021-08-17 18:19:15 -07:00
|
|
|
export async function transferFromSolana(
|
|
|
|
connection: Connection,
|
|
|
|
bridgeAddress: string,
|
|
|
|
tokenBridgeAddress: string,
|
|
|
|
payerAddress: string,
|
|
|
|
fromAddress: string,
|
|
|
|
mintAddress: string,
|
|
|
|
amount: BigInt,
|
|
|
|
targetAddress: Uint8Array,
|
2022-05-05 09:35:11 -07:00
|
|
|
targetChain: ChainId | ChainName,
|
2021-08-17 18:19:15 -07:00
|
|
|
originAddress?: Uint8Array,
|
2022-05-05 09:35:11 -07:00
|
|
|
originChain?: ChainId | ChainName,
|
2022-03-03 07:00:18 -08:00
|
|
|
fromOwnerAddress?: string,
|
2022-05-20 15:40:17 -07:00
|
|
|
relayerFee: BigInt = BigInt(0),
|
2022-05-23 09:10:35 -07:00
|
|
|
payload: Uint8Array | null = null
|
2021-08-17 18:19:15 -07:00
|
|
|
) {
|
2022-05-05 09:35:11 -07:00
|
|
|
const originChainId: ChainId | undefined = originChain
|
|
|
|
? coalesceChainId(originChain)
|
|
|
|
: undefined;
|
2021-08-17 18:19:15 -07:00
|
|
|
const nonce = createNonce().readUInt32LE(0);
|
|
|
|
const transferIx = await getBridgeFeeIx(
|
|
|
|
connection,
|
|
|
|
bridgeAddress,
|
|
|
|
payerAddress
|
|
|
|
);
|
|
|
|
const {
|
|
|
|
transfer_native_ix,
|
|
|
|
transfer_wrapped_ix,
|
2022-05-20 15:40:17 -07:00
|
|
|
transfer_native_with_payload_ix,
|
|
|
|
transfer_wrapped_with_payload_ix,
|
2021-08-17 18:19:15 -07:00
|
|
|
approval_authority_address,
|
2021-11-05 12:18:12 -07:00
|
|
|
} = await importTokenWasm();
|
2021-08-17 18:19:15 -07:00
|
|
|
const approvalIx = Token.createApproveInstruction(
|
|
|
|
TOKEN_PROGRAM_ID,
|
|
|
|
new PublicKey(fromAddress),
|
|
|
|
new PublicKey(approval_authority_address(tokenBridgeAddress)),
|
2021-10-15 07:11:55 -07:00
|
|
|
new PublicKey(fromOwnerAddress || payerAddress),
|
2021-08-17 18:19:15 -07:00
|
|
|
[],
|
2021-09-15 12:58:30 -07:00
|
|
|
new u64(amount.toString(16), 16)
|
2021-08-17 18:19:15 -07:00
|
|
|
);
|
|
|
|
let messageKey = Keypair.generate();
|
|
|
|
const isSolanaNative =
|
2022-05-05 09:35:11 -07:00
|
|
|
originChainId === undefined || originChainId === CHAIN_ID_SOLANA;
|
2021-08-17 18:19:15 -07:00
|
|
|
if (!isSolanaNative && !originAddress) {
|
|
|
|
throw new Error("originAddress is required when specifying originChain");
|
|
|
|
}
|
|
|
|
const ix = ixFromRust(
|
|
|
|
isSolanaNative
|
2022-05-23 09:10:35 -07:00
|
|
|
? payload !== null
|
|
|
|
? transfer_native_with_payload_ix(
|
|
|
|
tokenBridgeAddress,
|
|
|
|
bridgeAddress,
|
|
|
|
payerAddress,
|
|
|
|
messageKey.publicKey.toString(),
|
|
|
|
fromAddress,
|
|
|
|
mintAddress,
|
|
|
|
nonce,
|
2022-06-14 11:26:21 -07:00
|
|
|
amount.valueOf(),
|
2022-05-23 09:10:35 -07:00
|
|
|
targetAddress,
|
|
|
|
coalesceChainId(targetChain),
|
|
|
|
payload
|
2022-05-20 15:40:17 -07:00
|
|
|
)
|
2022-05-23 09:10:35 -07:00
|
|
|
: transfer_native_ix(
|
|
|
|
tokenBridgeAddress,
|
|
|
|
bridgeAddress,
|
|
|
|
payerAddress,
|
|
|
|
messageKey.publicKey.toString(),
|
|
|
|
fromAddress,
|
|
|
|
mintAddress,
|
|
|
|
nonce,
|
2022-06-14 11:26:21 -07:00
|
|
|
amount.valueOf(),
|
2022-06-14 11:47:17 -07:00
|
|
|
relayerFee.valueOf(),
|
2022-05-23 09:10:35 -07:00
|
|
|
targetAddress,
|
|
|
|
coalesceChainId(targetChain)
|
2022-05-20 15:40:17 -07:00
|
|
|
)
|
2022-05-23 09:10:35 -07:00
|
|
|
: payload !== null
|
|
|
|
? transfer_wrapped_with_payload_ix(
|
|
|
|
tokenBridgeAddress,
|
|
|
|
bridgeAddress,
|
|
|
|
payerAddress,
|
|
|
|
messageKey.publicKey.toString(),
|
|
|
|
fromAddress,
|
|
|
|
fromOwnerAddress || payerAddress,
|
|
|
|
originChainId as number, // checked by isSolanaNative
|
|
|
|
originAddress as Uint8Array, // checked by throw
|
|
|
|
nonce,
|
2022-06-14 11:26:21 -07:00
|
|
|
amount.valueOf(),
|
2022-05-23 09:10:35 -07:00
|
|
|
targetAddress,
|
|
|
|
coalesceChainId(targetChain),
|
|
|
|
payload
|
|
|
|
)
|
|
|
|
: transfer_wrapped_ix(
|
|
|
|
tokenBridgeAddress,
|
|
|
|
bridgeAddress,
|
|
|
|
payerAddress,
|
|
|
|
messageKey.publicKey.toString(),
|
|
|
|
fromAddress,
|
|
|
|
fromOwnerAddress || payerAddress,
|
|
|
|
originChainId as number, // checked by isSolanaNative
|
|
|
|
originAddress as Uint8Array, // checked by throw
|
|
|
|
nonce,
|
2022-06-14 11:26:21 -07:00
|
|
|
amount.valueOf(),
|
2022-06-14 11:47:17 -07:00
|
|
|
relayerFee.valueOf(),
|
2022-05-23 09:10:35 -07:00
|
|
|
targetAddress,
|
|
|
|
coalesceChainId(targetChain)
|
|
|
|
)
|
2021-08-17 18:19:15 -07:00
|
|
|
);
|
2022-04-29 12:57:41 -07:00
|
|
|
const transaction = new SolanaTransaction().add(transferIx, approvalIx, ix);
|
2021-08-17 18:19:15 -07:00
|
|
|
const { blockhash } = await connection.getRecentBlockhash();
|
|
|
|
transaction.recentBlockhash = blockhash;
|
|
|
|
transaction.feePayer = new PublicKey(payerAddress);
|
|
|
|
transaction.partialSign(messageKey);
|
|
|
|
return transaction;
|
|
|
|
}
|
2022-04-29 12:57:41 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Transfers an asset from Algorand to a receiver on another chain
|
|
|
|
* @param client AlgodV2 client
|
|
|
|
* @param tokenBridgeId Application ID of the token bridge
|
|
|
|
* @param bridgeId Application ID of the core bridge
|
|
|
|
* @param sender Sending account
|
|
|
|
* @param assetId Asset index
|
|
|
|
* @param qty Quantity to transfer
|
|
|
|
* @param receiver Receiving account
|
|
|
|
* @param chain Reeiving chain
|
|
|
|
* @param fee Transfer fee
|
2022-05-06 11:37:31 -07:00
|
|
|
* @param payload payload for payload3 transfers
|
2022-04-29 12:57:41 -07:00
|
|
|
* @returns Sequence number of confirmation
|
|
|
|
*/
|
|
|
|
export async function transferFromAlgorand(
|
|
|
|
client: Algodv2,
|
|
|
|
tokenBridgeId: bigint,
|
|
|
|
bridgeId: bigint,
|
|
|
|
senderAddr: string,
|
|
|
|
assetId: bigint,
|
|
|
|
qty: bigint,
|
|
|
|
receiver: string,
|
2022-05-05 09:35:11 -07:00
|
|
|
chain: ChainId | ChainName,
|
2022-05-06 11:37:31 -07:00
|
|
|
fee: bigint,
|
2022-05-23 09:10:35 -07:00
|
|
|
payload: Uint8Array | null = null
|
2022-04-29 12:57:41 -07:00
|
|
|
): Promise<TransactionSignerPair[]> {
|
2022-05-05 09:35:11 -07:00
|
|
|
const recipientChainId = coalesceChainId(chain);
|
2022-04-29 12:57:41 -07:00
|
|
|
const tokenAddr: string = getApplicationAddress(tokenBridgeId);
|
|
|
|
const applAddr: string = getEmitterAddressAlgorand(tokenBridgeId);
|
|
|
|
const txs: TransactionSignerPair[] = [];
|
|
|
|
// "transferAsset"
|
|
|
|
const { addr: emitterAddr, txs: emitterOptInTxs } = await optin(
|
|
|
|
client,
|
|
|
|
senderAddr,
|
|
|
|
bridgeId,
|
|
|
|
BigInt(0),
|
|
|
|
applAddr
|
|
|
|
);
|
|
|
|
txs.push(...emitterOptInTxs);
|
|
|
|
let creator;
|
|
|
|
let creatorAcctInfo: any;
|
|
|
|
let wormhole: boolean = false;
|
|
|
|
if (assetId !== BigInt(0)) {
|
|
|
|
const assetInfo: Record<string, any> = await client
|
|
|
|
.getAssetByID(safeBigIntToNumber(assetId))
|
|
|
|
.do();
|
|
|
|
creator = assetInfo["params"]["creator"];
|
|
|
|
creatorAcctInfo = await client.accountInformation(creator).do();
|
|
|
|
const authAddr: string = creatorAcctInfo["auth-addr"];
|
|
|
|
if (authAddr === tokenAddr) {
|
|
|
|
wormhole = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const params: SuggestedParams = await client.getTransactionParams().do();
|
|
|
|
const msgFee: bigint = await getMessageFee(client, bridgeId);
|
|
|
|
if (msgFee > 0) {
|
|
|
|
const payTxn: AlgorandTransaction =
|
|
|
|
makePaymentTxnWithSuggestedParamsFromObject({
|
|
|
|
from: senderAddr,
|
|
|
|
suggestedParams: params,
|
|
|
|
to: getApplicationAddress(tokenBridgeId),
|
|
|
|
amount: msgFee,
|
|
|
|
});
|
|
|
|
txs.push({ tx: payTxn, signer: null });
|
|
|
|
}
|
|
|
|
if (!wormhole) {
|
|
|
|
const bNat = Buffer.from("native", "binary").toString("hex");
|
|
|
|
// "creator"
|
|
|
|
const result = await optin(
|
|
|
|
client,
|
|
|
|
senderAddr,
|
|
|
|
tokenBridgeId,
|
|
|
|
assetId,
|
|
|
|
bNat
|
|
|
|
);
|
|
|
|
creator = result.addr;
|
|
|
|
txs.push(...result.txs);
|
|
|
|
}
|
|
|
|
if (
|
|
|
|
assetId !== BigInt(0) &&
|
|
|
|
!(await assetOptinCheck(client, assetId, creator))
|
|
|
|
) {
|
|
|
|
// Looks like we need to optin
|
|
|
|
const payTxn: AlgorandTransaction =
|
|
|
|
makePaymentTxnWithSuggestedParamsFromObject({
|
|
|
|
from: senderAddr,
|
|
|
|
to: creator,
|
|
|
|
amount: 100000,
|
|
|
|
suggestedParams: params,
|
|
|
|
});
|
|
|
|
txs.push({ tx: payTxn, signer: null });
|
|
|
|
// The tokenid app needs to do the optin since it has signature authority
|
|
|
|
const bOptin: Uint8Array = textToUint8Array("optin");
|
|
|
|
let txn = makeApplicationCallTxnFromObject({
|
|
|
|
from: senderAddr,
|
|
|
|
appIndex: safeBigIntToNumber(tokenBridgeId),
|
|
|
|
onComplete: OnApplicationComplete.NoOpOC,
|
|
|
|
appArgs: [bOptin, bigIntToBytes(assetId, 8)],
|
|
|
|
foreignAssets: [safeBigIntToNumber(assetId)],
|
|
|
|
accounts: [creator],
|
|
|
|
suggestedParams: params,
|
|
|
|
});
|
|
|
|
txn.fee *= 2;
|
|
|
|
txs.push({ tx: txn, signer: null });
|
|
|
|
}
|
|
|
|
const t = makeApplicationCallTxnFromObject({
|
|
|
|
from: senderAddr,
|
|
|
|
appIndex: safeBigIntToNumber(tokenBridgeId),
|
|
|
|
onComplete: OnApplicationComplete.NoOpOC,
|
|
|
|
appArgs: [textToUint8Array("nop")],
|
|
|
|
suggestedParams: params,
|
|
|
|
});
|
|
|
|
txs.push({ tx: t, signer: null });
|
|
|
|
|
|
|
|
let accounts: string[] = [];
|
|
|
|
if (assetId === BigInt(0)) {
|
|
|
|
const t = makePaymentTxnWithSuggestedParamsFromObject({
|
|
|
|
from: senderAddr,
|
|
|
|
to: creator,
|
|
|
|
amount: qty,
|
|
|
|
suggestedParams: params,
|
|
|
|
});
|
|
|
|
txs.push({ tx: t, signer: null });
|
|
|
|
accounts = [emitterAddr, creator, creator];
|
|
|
|
} else {
|
|
|
|
const t = makeAssetTransferTxnWithSuggestedParamsFromObject({
|
|
|
|
from: senderAddr,
|
|
|
|
to: creator,
|
|
|
|
suggestedParams: params,
|
|
|
|
amount: qty,
|
|
|
|
assetIndex: safeBigIntToNumber(assetId),
|
|
|
|
});
|
|
|
|
txs.push({ tx: t, signer: null });
|
|
|
|
accounts = [emitterAddr, creator, creatorAcctInfo["address"]];
|
|
|
|
}
|
|
|
|
let args = [
|
|
|
|
textToUint8Array("sendTransfer"),
|
|
|
|
bigIntToBytes(assetId, 8),
|
|
|
|
bigIntToBytes(qty, 8),
|
|
|
|
hexToUint8Array(receiver),
|
2022-05-05 09:35:11 -07:00
|
|
|
bigIntToBytes(recipientChainId, 8),
|
2022-04-29 12:57:41 -07:00
|
|
|
bigIntToBytes(fee, 8),
|
|
|
|
];
|
2022-05-23 09:10:35 -07:00
|
|
|
if (payload !== null) {
|
|
|
|
args.push(payload);
|
2022-05-06 11:37:31 -07:00
|
|
|
}
|
2022-04-29 12:57:41 -07:00
|
|
|
let acTxn = makeApplicationCallTxnFromObject({
|
|
|
|
from: senderAddr,
|
|
|
|
appIndex: safeBigIntToNumber(tokenBridgeId),
|
|
|
|
onComplete: OnApplicationComplete.NoOpOC,
|
|
|
|
appArgs: args,
|
|
|
|
foreignApps: [safeBigIntToNumber(bridgeId)],
|
|
|
|
foreignAssets: [safeBigIntToNumber(assetId)],
|
|
|
|
accounts: accounts,
|
|
|
|
suggestedParams: params,
|
|
|
|
});
|
|
|
|
acTxn.fee *= 2;
|
|
|
|
txs.push({ tx: acTxn, signer: null });
|
|
|
|
return txs;
|
|
|
|
}
|
2022-08-04 08:53:08 -07:00
|
|
|
|
|
|
|
export async function transferTokenFromNear(
|
2022-09-28 06:53:15 -07:00
|
|
|
provider: Provider,
|
|
|
|
account: string,
|
2022-08-04 08:53:08 -07:00
|
|
|
coreBridge: string,
|
|
|
|
tokenBridge: string,
|
|
|
|
assetId: string,
|
|
|
|
qty: bigint,
|
|
|
|
receiver: Uint8Array,
|
|
|
|
chain: ChainId | ChainName,
|
|
|
|
fee: bigint,
|
|
|
|
payload: string = ""
|
2022-09-28 06:53:15 -07:00
|
|
|
): Promise<FunctionCallOptions[]> {
|
|
|
|
const isWrapped = getIsWrappedAssetNear(tokenBridge, assetId);
|
2022-08-04 08:53:08 -07:00
|
|
|
|
2022-09-28 06:53:15 -07:00
|
|
|
const messageFee = await callFunctionNear(
|
|
|
|
provider,
|
|
|
|
coreBridge,
|
|
|
|
"message_fee",
|
|
|
|
{}
|
|
|
|
);
|
2022-08-04 08:53:08 -07:00
|
|
|
|
2022-09-28 06:53:15 -07:00
|
|
|
chain = coalesceChainId(chain);
|
2022-08-04 08:53:08 -07:00
|
|
|
|
2022-09-28 06:53:15 -07:00
|
|
|
if (isWrapped) {
|
|
|
|
return [
|
|
|
|
{
|
|
|
|
contractId: tokenBridge,
|
|
|
|
methodName: "send_transfer_wormhole_token",
|
|
|
|
args: {
|
|
|
|
token: assetId,
|
|
|
|
amount: qty.toString(10),
|
|
|
|
receiver: uint8ArrayToHex(receiver),
|
|
|
|
chain,
|
|
|
|
fee: fee.toString(10),
|
|
|
|
payload: payload,
|
|
|
|
message_fee: messageFee,
|
|
|
|
},
|
|
|
|
attachedDeposit: new BN(messageFee + 1),
|
|
|
|
gas: new BN("100000000000000"),
|
2022-08-04 08:53:08 -07:00
|
|
|
},
|
2022-09-28 06:53:15 -07:00
|
|
|
];
|
2022-08-04 08:53:08 -07:00
|
|
|
} else {
|
2022-09-28 06:53:15 -07:00
|
|
|
const options: FunctionCallOptions[] = [];
|
|
|
|
const bal = await callFunctionNear(
|
|
|
|
provider,
|
|
|
|
assetId,
|
|
|
|
"storage_balance_of",
|
|
|
|
{
|
|
|
|
account_id: tokenBridge,
|
|
|
|
}
|
|
|
|
);
|
2022-08-04 08:53:08 -07:00
|
|
|
if (bal === null) {
|
|
|
|
// Looks like we have to stake some storage for this asset
|
|
|
|
// for the token bridge...
|
2022-09-28 06:53:15 -07:00
|
|
|
options.push({
|
|
|
|
contractId: assetId,
|
|
|
|
methodName: "storage_deposit",
|
|
|
|
args: { account_id: tokenBridge, 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
|
|
|
if (messageFee > 0) {
|
|
|
|
const bank = await callFunctionNear(
|
|
|
|
provider,
|
|
|
|
tokenBridge,
|
|
|
|
"bank_balance",
|
|
|
|
{
|
|
|
|
acct: account,
|
|
|
|
}
|
|
|
|
);
|
2022-08-04 08:53:08 -07:00
|
|
|
|
|
|
|
if (!bank[0]) {
|
2022-09-28 06:53:15 -07:00
|
|
|
options.push({
|
2022-08-04 08:53:08 -07:00
|
|
|
contractId: tokenBridge,
|
|
|
|
methodName: "register_bank",
|
|
|
|
args: {},
|
|
|
|
gas: new BN("100000000000000"),
|
|
|
|
attachedDeposit: new BN("2000000000000000000000"), // 0.002 NEAR
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-09-28 06:53:15 -07:00
|
|
|
if (bank[1] < messageFee) {
|
|
|
|
options.push({
|
2022-08-04 08:53:08 -07:00
|
|
|
contractId: tokenBridge,
|
|
|
|
methodName: "fill_bank",
|
|
|
|
args: {},
|
|
|
|
gas: new BN("100000000000000"),
|
2022-09-28 06:53:15 -07:00
|
|
|
attachedDeposit: new BN(messageFee),
|
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: assetId,
|
|
|
|
methodName: "ft_transfer_call",
|
|
|
|
args: {
|
|
|
|
receiver_id: tokenBridge,
|
|
|
|
amount: qty.toString(10),
|
|
|
|
msg: JSON.stringify({
|
|
|
|
receiver: uint8ArrayToHex(receiver),
|
2022-09-28 06:53:15 -07:00
|
|
|
chain,
|
2022-08-04 08:53:08 -07:00
|
|
|
fee: fee.toString(10),
|
|
|
|
payload: payload,
|
2022-09-28 06:53:15 -07:00
|
|
|
message_fee: messageFee,
|
2022-08-04 08:53:08 -07:00
|
|
|
}),
|
|
|
|
},
|
|
|
|
attachedDeposit: new BN(1),
|
|
|
|
gas: new BN("100000000000000"),
|
|
|
|
});
|
|
|
|
|
2022-09-28 06:53:15 -07:00
|
|
|
return options;
|
|
|
|
}
|
2022-08-04 08:53:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
export async function transferNearFromNear(
|
2022-09-28 06:53:15 -07:00
|
|
|
provider: Provider,
|
2022-08-04 08:53:08 -07:00
|
|
|
coreBridge: string,
|
|
|
|
tokenBridge: string,
|
|
|
|
qty: bigint,
|
|
|
|
receiver: Uint8Array,
|
|
|
|
chain: ChainId | ChainName,
|
|
|
|
fee: bigint,
|
|
|
|
payload: string = ""
|
2022-09-28 06:53:15 -07:00
|
|
|
): Promise<FunctionCallOptions> {
|
|
|
|
const messageFee = await callFunctionNear(
|
|
|
|
provider,
|
|
|
|
coreBridge,
|
|
|
|
"message_fee",
|
|
|
|
{}
|
|
|
|
);
|
|
|
|
return {
|
2022-08-04 08:53:08 -07:00
|
|
|
contractId: tokenBridge,
|
|
|
|
methodName: "send_transfer_near",
|
|
|
|
args: {
|
|
|
|
receiver: uint8ArrayToHex(receiver),
|
2022-09-28 06:53:15 -07:00
|
|
|
chain: coalesceChainId(chain),
|
2022-09-13 13:35:06 -07:00
|
|
|
fee: fee.toString(10),
|
2022-08-04 08:53:08 -07:00
|
|
|
payload: payload,
|
2022-09-28 06:53:15 -07:00
|
|
|
message_fee: messageFee,
|
2022-08-04 08:53:08 -07:00
|
|
|
},
|
2022-09-28 06:53:15 -07:00
|
|
|
attachedDeposit: new BN(qty.toString(10)).add(new BN(messageFee)),
|
2022-08-04 08:53:08 -07:00
|
|
|
gas: new BN("100000000000000"),
|
2022-09-28 06:53:15 -07:00
|
|
|
};
|
2022-08-04 08:53:08 -07:00
|
|
|
}
|