wormhole/sdk/js/src/nft_bridge/transfer.ts

172 lines
4.8 KiB
TypeScript

import { createApproveInstruction } from "@solana/spl-token";
import {
Commitment,
Connection,
Keypair,
PublicKey,
PublicKeyInitData,
Transaction,
} from "@solana/web3.js";
import { MsgExecuteContract } from "@terra-money/terra.js";
import { ethers, Overrides } from "ethers";
import { BN } from "@project-serum/anchor";
import {
NFTBridge__factory,
NFTImplementation__factory,
} from "../ethers-contracts";
import { createBridgeFeeTransferInstruction } from "../solana";
import {
createApproveAuthoritySignerInstruction,
createTransferNativeInstruction,
createTransferWrappedInstruction,
} from "../solana/nftBridge";
import {
ChainId,
ChainName,
CHAIN_ID_SOLANA,
coalesceChainId,
createNonce,
} from "../utils";
import { isBytes } from "ethers/lib/utils";
export async function transferFromEth(
nftBridgeAddress: string,
signer: ethers.Signer,
tokenAddress: string,
tokenID: ethers.BigNumberish,
recipientChain: ChainId | ChainName,
recipientAddress: Uint8Array,
overrides: Overrides & { from?: string | Promise<string> } = {}
): Promise<ethers.ContractReceipt> {
const recipientChainId = coalesceChainId(recipientChain);
//TODO: should we check if token attestation exists on the target chain
const token = NFTImplementation__factory.connect(tokenAddress, signer);
await (await token.approve(nftBridgeAddress, tokenID, overrides)).wait();
const bridge = NFTBridge__factory.connect(nftBridgeAddress, signer);
const v = await bridge.transferNFT(
tokenAddress,
tokenID,
recipientChainId,
recipientAddress,
createNonce(),
overrides
);
const receipt = await v.wait();
return receipt;
}
export async function transferFromSolana(
connection: Connection,
bridgeAddress: PublicKeyInitData,
nftBridgeAddress: PublicKeyInitData,
payerAddress: PublicKeyInitData,
fromAddress: PublicKeyInitData,
mintAddress: PublicKeyInitData,
targetAddress: Uint8Array | Buffer,
targetChain: ChainId | ChainName,
originAddress?: Uint8Array | Buffer,
originChain?: ChainId | ChainName,
originTokenId?: Uint8Array | Buffer | number | bigint,
commitment?: Commitment
): Promise<Transaction> {
const originChainId: ChainId | undefined = originChain
? coalesceChainId(originChain)
: undefined;
const nonce = createNonce().readUInt32LE(0);
const transferIx = await createBridgeFeeTransferInstruction(
connection,
bridgeAddress,
payerAddress
);
const approvalIx = createApproveAuthoritySignerInstruction(
nftBridgeAddress,
fromAddress,
payerAddress
);
let message = Keypair.generate();
const isSolanaNative =
originChain === undefined || originChain === CHAIN_ID_SOLANA;
if (!isSolanaNative && (!originAddress || !originTokenId)) {
return Promise.reject(
"originAddress and originTokenId are required when specifying originChain"
);
}
const nftBridgeTransferIx = isSolanaNative
? createTransferNativeInstruction(
nftBridgeAddress,
bridgeAddress,
payerAddress,
message.publicKey,
fromAddress,
mintAddress,
nonce,
targetAddress,
coalesceChainId(targetChain)
)
: createTransferWrappedInstruction(
nftBridgeAddress,
bridgeAddress,
payerAddress,
message.publicKey,
fromAddress,
payerAddress,
originChainId!,
originAddress!,
isBytes(originTokenId)
? BigInt(new BN(originTokenId).toString())
: originTokenId!,
nonce,
targetAddress,
coalesceChainId(targetChain)
);
const transaction = new Transaction().add(
transferIx,
approvalIx,
nftBridgeTransferIx
);
const { blockhash } = await connection.getLatestBlockhash(commitment);
transaction.recentBlockhash = blockhash;
transaction.feePayer = new PublicKey(payerAddress);
transaction.partialSign(message);
return transaction;
}
export async function transferFromTerra(
walletAddress: string,
nftBridgeAddress: string,
tokenAddress: string,
tokenID: string,
recipientChain: ChainId | ChainName,
recipientAddress: Uint8Array
): Promise<MsgExecuteContract[]> {
const recipientChainId = coalesceChainId(recipientChain);
const nonce = Math.round(Math.random() * 100000);
return [
new MsgExecuteContract(
walletAddress,
tokenAddress,
{
approve: {
spender: nftBridgeAddress,
token_id: tokenID,
},
},
{}
),
new MsgExecuteContract(
walletAddress,
nftBridgeAddress,
{
initiate_transfer: {
contract_addr: tokenAddress,
token_id: tokenID,
recipient_chain: recipientChainId,
recipient: Buffer.from(recipientAddress).toString("base64"),
nonce: nonce,
},
},
{}
),
];
}