wormhole/sdk/js/src/solana/tokenBridge/coder/instruction.ts

255 lines
6.0 KiB
TypeScript

import { Idl, InstructionCoder } from "@project-serum/anchor";
import { PublicKey } from "@solana/web3.js";
export class TokenBridgeInstructionCoder implements InstructionCoder {
constructor(_: Idl) {}
encode(ixName: string, ix: any): Buffer {
switch (ixName) {
case "initialize": {
return encodeInitialize(ix);
}
case "attestToken": {
return encodeAttestToken(ix);
}
case "completeNative": {
return encodeCompleteNative(ix);
}
case "completeWrapped": {
return encodeCompleteWrapped(ix);
}
case "transferWrapped": {
return encodeTransferWrapped(ix);
}
case "transferNative": {
return encodeTransferNative(ix);
}
case "registerChain": {
return encodeRegisterChain(ix);
}
case "createWrapped": {
return encodeCreateWrapped(ix);
}
case "upgradeContract": {
return encodeUpgradeContract(ix);
}
case "transferWrappedWithPayload": {
return encodeTransferWrappedWithPayload(ix);
}
case "transferNativeWithPayload": {
return encodeTransferNativeWithPayload(ix);
}
default: {
throw new Error(`Invalid instruction: ${ixName}`);
}
}
}
encodeState(_ixName: string, _ix: any): Buffer {
throw new Error("Token Bridge program does not have state");
}
}
/** Solitaire enum of existing the Token Bridge's instructions.
*
* https://github.com/certusone/wormhole/blob/main/solana/modules/token_bridge/program/src/lib.rs#L100
*/
export enum TokenBridgeInstruction {
Initialize,
AttestToken,
CompleteNative,
CompleteWrapped,
TransferWrapped,
TransferNative,
RegisterChain,
CreateWrapped,
UpgradeContract,
CompleteNativeWithPayload,
CompleteWrappedWithPayload,
TransferWrappedWithPayload,
TransferNativeWithPayload,
}
function encodeTokenBridgeInstructionData(
instructionType: TokenBridgeInstruction,
data?: Buffer
): Buffer {
const dataLen = data === undefined ? 0 : data.length;
const instructionData = Buffer.alloc(1 + dataLen);
instructionData.writeUInt8(instructionType, 0);
if (dataLen > 0) {
instructionData.write(data!.toString("hex"), 1, "hex");
}
return instructionData;
}
function encodeInitialize({ wormhole }: any): Buffer {
const serialized = Buffer.alloc(32);
serialized.write(
new PublicKey(wormhole).toBuffer().toString("hex"),
0,
"hex"
);
return encodeTokenBridgeInstructionData(
TokenBridgeInstruction.Initialize,
serialized
);
}
function encodeAttestToken({ nonce }: any) {
const serialized = Buffer.alloc(4);
serialized.writeUInt32LE(nonce, 0);
return encodeTokenBridgeInstructionData(
TokenBridgeInstruction.AttestToken,
serialized
);
}
function encodeCompleteNative({}: any) {
return encodeTokenBridgeInstructionData(
TokenBridgeInstruction.CompleteNative
);
}
function encodeCompleteWrapped({}: any) {
return encodeTokenBridgeInstructionData(
TokenBridgeInstruction.CompleteWrapped
);
}
function encodeTransferData({
nonce,
amount,
fee,
targetAddress,
targetChain,
}: any) {
if (typeof amount != "bigint") {
amount = BigInt(amount);
}
if (typeof fee != "bigint") {
fee = BigInt(fee);
}
if (!Buffer.isBuffer(targetAddress)) {
throw new Error("targetAddress must be Buffer");
}
const serialized = Buffer.alloc(54);
serialized.writeUInt32LE(nonce, 0);
serialized.writeBigUInt64LE(amount, 4);
serialized.writeBigUInt64LE(fee, 12);
serialized.write(targetAddress.toString("hex"), 20, "hex");
serialized.writeUInt16LE(targetChain, 52);
return serialized;
}
function encodeTransferWrapped({
nonce,
amount,
fee,
targetAddress,
targetChain,
}: any) {
return encodeTokenBridgeInstructionData(
TokenBridgeInstruction.TransferWrapped,
encodeTransferData({ nonce, amount, fee, targetAddress, targetChain })
);
}
function encodeTransferNative({
nonce,
amount,
fee,
targetAddress,
targetChain,
}: any) {
return encodeTokenBridgeInstructionData(
TokenBridgeInstruction.TransferNative,
encodeTransferData({ nonce, amount, fee, targetAddress, targetChain })
);
}
function encodeRegisterChain({}: any) {
return encodeTokenBridgeInstructionData(TokenBridgeInstruction.RegisterChain);
}
function encodeCreateWrapped({}: any) {
return encodeTokenBridgeInstructionData(TokenBridgeInstruction.CreateWrapped);
}
function encodeUpgradeContract({}: any) {
return encodeTokenBridgeInstructionData(
TokenBridgeInstruction.UpgradeContract
);
}
function encodeTransferWithPayloadData({
nonce,
amount,
targetAddress,
targetChain,
payload,
}: any) {
if (typeof amount != "bigint") {
amount = BigInt(amount);
}
if (!Buffer.isBuffer(targetAddress)) {
throw new Error("targetAddress must be Buffer");
}
if (!Buffer.isBuffer(payload)) {
throw new Error("payload must be Buffer");
}
const serializedWithPayloadLen = Buffer.alloc(50);
serializedWithPayloadLen.writeUInt32LE(nonce, 0);
serializedWithPayloadLen.writeBigUInt64LE(amount, 4);
serializedWithPayloadLen.write(targetAddress.toString("hex"), 12, "hex");
serializedWithPayloadLen.writeUInt16LE(targetChain, 44);
serializedWithPayloadLen.writeUInt32LE(payload.length, 46);
return Buffer.concat([
serializedWithPayloadLen,
payload,
Buffer.alloc(1), // option == None
]);
}
function encodeTransferWrappedWithPayload({
nonce,
amount,
fee,
targetAddress,
targetChain,
payload,
}: any) {
return encodeTokenBridgeInstructionData(
TokenBridgeInstruction.TransferWrappedWithPayload,
encodeTransferWithPayloadData({
nonce,
amount,
fee,
targetAddress,
targetChain,
payload,
})
);
}
function encodeTransferNativeWithPayload({
nonce,
amount,
fee,
targetAddress,
targetChain,
payload,
}: any) {
return encodeTokenBridgeInstructionData(
TokenBridgeInstruction.TransferNativeWithPayload,
encodeTransferWithPayloadData({
nonce,
amount,
fee,
targetAddress,
targetChain,
payload,
})
);
}