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

155 lines
4.2 KiB
TypeScript

import { Idl, InstructionCoder } from "@project-serum/anchor";
import { ETHEREUM_KEY_LENGTH } from "../../utils";
export class WormholeInstructionCoder implements InstructionCoder {
constructor(_: Idl) {}
encode(ixName: string, ix: any): Buffer {
switch (ixName) {
case "initialize": {
return encodeInitialize(ix);
}
case "postVaa": {
return encodePostVaa(ix);
}
case "setFees": {
return encodeSetFees(ix);
}
case "transferFees": {
return encodeTransferFees(ix);
}
case "upgradeContract": {
return encodeUpgradeContract(ix);
}
case "upgradeGuardianSet": {
return encodeUpgradeGuardianSet(ix);
}
case "verifySignatures": {
return encodeVerifySignatures(ix);
}
default: {
throw new Error(`Invalid instruction: ${ixName}`);
}
}
}
encodeState(_ixName: string, _ix: any): Buffer {
throw new Error("Wormhole program does not have state");
}
}
/** Solitaire enum of existing the Core Bridge's instructions.
*
* https://github.com/certusone/wormhole/blob/main/solana/bridge/program/src/lib.rs#L92
*/
export enum WormholeInstruction {
Initialize,
PostMessage,
PostVAA,
SetFees,
TransferFees,
UpgradeContract,
UpgradeGuardianSet,
VerifySignatures,
PostMessageUnreliable, // sounds useful
}
function encodeInitialize({
guardianSetExpirationTime,
fee,
initialGuardians,
}: any): Buffer {
if (typeof fee != "bigint") {
fee = BigInt(fee);
}
const initialGuardiansLen = initialGuardians.length;
const serialized = Buffer.alloc(
16 + initialGuardiansLen * ETHEREUM_KEY_LENGTH
);
serialized.writeUInt32LE(guardianSetExpirationTime, 0);
serialized.writeBigUInt64LE(fee, 4);
serialized.writeUInt32LE(initialGuardiansLen, 12);
for (let i = 0; i < initialGuardiansLen; ++i) {
const key = initialGuardians.at(i)!;
if (!Buffer.isBuffer(key)) {
throw new Error("key must be Buffer");
}
serialized.write(key.toString("hex"), 16 + i * ETHEREUM_KEY_LENGTH, "hex");
}
return encodeWormholeInstructionData(
WormholeInstruction.Initialize,
serialized
);
}
function encodeWormholeInstructionData(
instructionType: WormholeInstruction,
data?: Buffer
): Buffer {
const instructionData = Buffer.alloc(
1 + (data === undefined ? 0 : data.length)
);
instructionData.writeUInt8(instructionType, 0);
if (data !== undefined) {
instructionData.write(data.toString("hex"), 1, "hex");
}
return instructionData;
}
function encodePostVaa({
version,
guardianSetIndex,
timestamp,
nonce,
emitterChain,
emitterAddress,
sequence,
consistencyLevel,
payload,
}: any) {
if (!Buffer.isBuffer(emitterAddress)) {
throw new Error("emitterAddress must be Buffer");
}
if (!Buffer.isBuffer(payload)) {
throw new Error("payload must be Buffer");
}
if (typeof sequence != "bigint") {
sequence = BigInt(sequence);
}
const serialized = Buffer.alloc(60 + payload.length);
serialized.writeUInt8(version, 0);
serialized.writeUInt32LE(guardianSetIndex, 1);
serialized.writeUInt32LE(timestamp, 5);
serialized.writeUInt32LE(nonce, 9);
serialized.writeUInt16LE(emitterChain, 13);
serialized.write(emitterAddress.toString("hex"), 15, "hex");
serialized.writeBigInt64LE(sequence, 47);
serialized.writeUInt8(consistencyLevel, 55);
serialized.writeUInt32LE(payload.length, 56);
serialized.write(payload.toString("hex"), 60, "hex");
return encodeWormholeInstructionData(WormholeInstruction.PostVAA, serialized);
}
function encodeSetFees({}: any) {
return encodeWormholeInstructionData(WormholeInstruction.SetFees);
}
function encodeTransferFees({}: any) {
return encodeWormholeInstructionData(WormholeInstruction.TransferFees);
}
function encodeUpgradeContract({}: any) {
return encodeWormholeInstructionData(WormholeInstruction.UpgradeContract);
}
function encodeUpgradeGuardianSet({}: any) {
return encodeWormholeInstructionData(WormholeInstruction.UpgradeGuardianSet);
}
function encodeVerifySignatures({ signatureStatus }: any) {
return encodeWormholeInstructionData(
WormholeInstruction.VerifySignatures,
Buffer.from(signatureStatus, "hex")
);
}