Ver Fonte

Refactor

Guillermo Bescos Alapont há 2 anos atrás
pai
commit
7e2b5cc279

+ 8 - 8
xc-admin/packages/xc-admin-common/src/__tests__/GovernancePayload.test.ts

@@ -14,8 +14,8 @@ test("GovernancePayload ser/de", (done) => {
     buffer.equals(Buffer.from([80, 84, 71, 77, 0, 0, 0, 26]))
   ).toBeTruthy();
   let governanceHeader = PythGovernanceHeader.decode(buffer);
-  expect(governanceHeader.targetChainId).toBe("pythnet");
-  expect(governanceHeader.action).toBe("ExecutePostedVaa");
+  expect(governanceHeader?.targetChainId).toBe("pythnet");
+  expect(governanceHeader?.action).toBe("ExecutePostedVaa");
 
   // Valid header 2
   expectedGovernanceHeader = new PythGovernanceHeader(
@@ -37,25 +37,25 @@ test("GovernancePayload ser/de", (done) => {
   expect(governanceHeader?.action).toBe("SetFee");
 
   // Wrong magic number
-  expect(() =>
+  expect(
     PythGovernanceHeader.decode(
       Buffer.from([0, 0, 0, 0, 0, 0, 0, 26, 0, 0, 0, 0])
     )
-  ).toThrow("Wrong magic number");
+  ).toBeUndefined();
 
   // Wrong chain
-  expect(() =>
+  expect(
     PythGovernanceHeader.decode(
       Buffer.from([80, 84, 71, 77, 0, 0, 255, 255, 0, 0, 0, 0])
     )
-  ).toThrow("Chain Id not found");
+  ).toBeUndefined();
 
   // Wrong module/action combination
-  expect(() =>
+  expect(
     PythGovernanceHeader.decode(
       Buffer.from([80, 84, 71, 77, 0, 1, 0, 26, 0, 0, 0, 0])
     )
-  ).toThrow("Invalid header, action doesn't match module");
+  ).toBeUndefined();
 
   // Decode executePostVaa with empty instructions
   let expectedExecutePostedVaa = new ExecutePostedVaa("pythnet", []);

+ 3 - 8
xc-admin/packages/xc-admin-common/src/__tests__/WormholeMultisigInstruction.test.ts

@@ -199,9 +199,6 @@ test("Wormhole multisig instruction parse: send message with governance payload"
     .then((instruction) => {
       const parsedInstruction = parser.parseInstruction(instruction);
       if (parsedInstruction instanceof WormholeMultisigInstruction) {
-        expect(
-          parsedInstruction instanceof WormholeMultisigInstruction
-        ).toBeTruthy();
         expect(parsedInstruction.program).toBe(
           MultisigInstructionProgram.WormholeBridge
         );
@@ -313,15 +310,13 @@ test("Wormhole multisig instruction parse: send message with governance payload"
         );
         expect(parsedInstruction.args.consistencyLevel).toBe(0);
 
-        if (
-          parsedInstruction.args.governanceAction instanceof ExecutePostedVaa
-        ) {
-          expect(parsedInstruction.args.governanceAction.targetChainId).toBe(
+        if (parsedInstruction.governanceAction instanceof ExecutePostedVaa) {
+          expect(parsedInstruction.governanceAction.targetChainId).toBe(
             "pythnet"
           );
 
           (
-            parsedInstruction.args.governanceAction
+            parsedInstruction.governanceAction
               .instructions as TransactionInstruction[]
           ).forEach((instruction, i) => {
             expect(

+ 14 - 4
xc-admin/packages/xc-admin-common/src/governance_payload/ExecutePostedVaa.ts

@@ -75,11 +75,21 @@ export class ExecutePostedVaa implements PythGovernanceAction {
   }
 
   /** Decode ExecutePostedVaa */
-  static decode(data: Buffer): ExecutePostedVaa {
+  static decode(data: Buffer): ExecutePostedVaa | undefined {
     let header = PythGovernanceHeader.decode(data);
-    let deserialized = this.layout.decode(
-      data.subarray(PythGovernanceHeader.span)
-    );
+    if (!header) {
+      return undefined;
+    }
+
+    let deserialized;
+    try {
+      deserialized = this.layout.decode(
+        data.subarray(PythGovernanceHeader.span)
+      );
+    } catch {
+      return undefined;
+    }
+
     let instructions: TransactionInstruction[] = deserialized.map((ix) => {
       let programId: PublicKey = new PublicKey(ix.programId);
       let keys: AccountMeta[] = ix.accounts.map((acc) => {

+ 33 - 15
xc-admin/packages/xc-admin-common/src/governance_payload/index.ts

@@ -30,7 +30,7 @@ export const TargetAction = {
 /** Helper to get the ActionName from a (moduleId, actionId) tuple*/
 export function toActionName(
   deserialized: Readonly<{ moduleId: number; actionId: number }>
-): ActionName {
+): ActionName | undefined {
   if (deserialized.moduleId == MODULE_EXECUTOR && deserialized.actionId == 0) {
     return "ExecutePostedVaa";
   } else if (deserialized.moduleId == MODULE_TARGET) {
@@ -49,7 +49,7 @@ export function toActionName(
         return "RequestGovernanceDataSourceTransfer";
     }
   }
-  throw new Error("Invalid header, action doesn't match module");
+  return undefined;
 }
 
 export declare type ActionName =
@@ -84,23 +84,35 @@ export class PythGovernanceHeader {
     this.action = action;
   }
   /** Decode Pyth Governance Header */
-  static decode(data: Buffer): PythGovernanceHeader {
-    let deserialized = this.layout.decode(data);
+  static decode(data: Buffer): PythGovernanceHeader | undefined {
+    let deserialized;
+    try {
+      deserialized = this.layout.decode(data);
+    } catch {
+      return undefined;
+    }
+
     if (deserialized.magicNumber !== MAGIC_NUMBER) {
-      throw new Error("Wrong magic number");
+      return undefined;
     }
 
     if (!toChainName(deserialized.chain)) {
-      throw new Error("Chain Id not found");
+      return undefined;
     }
 
-    return new PythGovernanceHeader(
-      toChainName(deserialized.chain),
-      toActionName({
-        actionId: deserialized.action,
-        moduleId: deserialized.module,
-      })
-    );
+    const actionName = toActionName({
+      actionId: deserialized.action,
+      moduleId: deserialized.module,
+    });
+
+    if (actionName) {
+      return new PythGovernanceHeader(
+        toChainName(deserialized.chain),
+        actionName
+      );
+    } else {
+      return undefined;
+    }
   }
 
   /** Encode Pyth Governance Header */
@@ -134,13 +146,19 @@ export const MODULE_EXECUTOR = 0;
 export const MODULE_TARGET = 1;
 
 /** Decode a governance payload */
-export function decodeGovernancePayload(data: Buffer): PythGovernanceAction {
+export function decodeGovernancePayload(
+  data: Buffer
+): PythGovernanceAction | undefined {
   const header = PythGovernanceHeader.decode(data);
+  if (!header) {
+    return undefined;
+  }
+
   switch (header.action) {
     case "ExecutePostedVaa":
       return ExecutePostedVaa.decode(data);
     default:
-      throw "Not supported";
+      return undefined;
   }
 }
 

+ 7 - 3
xc-admin/packages/xc-admin-common/src/multisig_transaction/PythMultisigInstruction.ts

@@ -1,4 +1,8 @@
-import { MultisigInstruction, MultisigInstructionProgram } from ".";
+import {
+  MultisigInstruction,
+  MultisigInstructionProgram,
+  UNRECOGNIZED_INSTRUCTION,
+} from ".";
 import { AnchorAccounts, resolveAccountNames } from "./anchor";
 import { pythIdl, pythOracleCoder } from "@pythnetwork/client";
 import { TransactionInstruction } from "@solana/web3.js";
@@ -35,8 +39,8 @@ export class PythMultisigInstruction implements MultisigInstruction {
       );
     } else {
       return new PythMultisigInstruction(
-        "Unrecognized instruction",
-        {},
+        UNRECOGNIZED_INSTRUCTION,
+        { data: instruction.data },
         { named: {}, remaining: instruction.keys }
       );
     }

+ 37 - 24
xc-admin/packages/xc-admin-common/src/multisig_transaction/WormholeMultisigInstruction.ts

@@ -2,7 +2,11 @@ import { createReadOnlyWormholeProgramInterface } from "@certusone/wormhole-sdk/
 import { WormholeInstructionCoder } from "@certusone/wormhole-sdk/lib/cjs/solana/wormhole/coder/instruction";
 import { getPythClusterApiUrl } from "@pythnetwork/client/lib/cluster";
 import { Connection, TransactionInstruction } from "@solana/web3.js";
-import { MultisigInstruction, MultisigInstructionProgram } from ".";
+import {
+  MultisigInstruction,
+  MultisigInstructionProgram,
+  UNRECOGNIZED_INSTRUCTION,
+} from ".";
 import {
   decodeGovernancePayload,
   PythGovernanceAction,
@@ -14,15 +18,18 @@ export class WormholeMultisigInstruction implements MultisigInstruction {
   readonly name: string;
   readonly args: { [key: string]: any };
   readonly accounts: AnchorAccounts;
+  readonly governanceAction: PythGovernanceAction | undefined;
 
   constructor(
     name: string,
     args: { [key: string]: any },
-    accounts: AnchorAccounts
+    accounts: AnchorAccounts,
+    governanceAction: PythGovernanceAction | undefined
   ) {
     this.name = name;
     this.args = args;
     this.accounts = accounts;
+    this.governanceAction = governanceAction;
   }
 
   static fromTransactionInstruction(
@@ -38,32 +45,38 @@ export class WormholeMultisigInstruction implements MultisigInstruction {
     ).decode(instruction.data);
 
     if (deserializedData) {
-      let result = new WormholeMultisigInstruction(
-        deserializedData.name,
-        deserializedData.data,
-        resolveAccountNames(
-          wormholeProgram.idl,
-          deserializedData.name,
-          instruction
-        )
-      );
+      if (deserializedData.name === "postMessage") {
+        const decodedGovernanceAction: PythGovernanceAction | undefined =
+          decodeGovernancePayload((deserializedData.data as any).payload);
 
-      if (result.name === "postMessage") {
-        try {
-          const decoded: PythGovernanceAction = decodeGovernancePayload(
-            result.args.payload
-          );
-          result.args.governanceAction = decoded;
-        } catch {
-          result.args.governanceAction = {};
-        }
+        return new WormholeMultisigInstruction(
+          deserializedData.name,
+          deserializedData.data,
+          resolveAccountNames(
+            wormholeProgram.idl,
+            deserializedData.name,
+            instruction
+          ),
+          decodedGovernanceAction
+        );
+      } else {
+        return new WormholeMultisigInstruction(
+          deserializedData.name,
+          deserializedData.data,
+          resolveAccountNames(
+            wormholeProgram.idl,
+            deserializedData.name,
+            instruction
+          ),
+          undefined
+        );
       }
-      return result;
     } else {
       return new WormholeMultisigInstruction(
-        "Unrecognized instruction",
-        {},
-        { named: {}, remaining: instruction.keys }
+        UNRECOGNIZED_INSTRUCTION,
+        { data: instruction.data },
+        { named: {}, remaining: instruction.keys },
+        undefined
       );
     }
   }

+ 1 - 1
xc-admin/packages/xc-admin-common/src/multisig_transaction/anchor.ts

@@ -15,7 +15,7 @@ export function resolveAccountNames(
 ): { named: NamedAccounts; remaining: RemainingAccounts } {
   const ix = idl.instructions.find((ix) => ix.name == name);
   if (!ix) {
-    throw Error("Instruction name not found");
+    return { named: {}, remaining: instruction.keys };
   }
   const named: NamedAccounts = {};
   const remaining: RemainingAccounts = [];

+ 1 - 0
xc-admin/packages/xc-admin-common/src/multisig_transaction/index.ts

@@ -7,6 +7,7 @@ import { WORMHOLE_ADDRESS } from "../wormhole";
 import { PythMultisigInstruction } from "./PythMultisigInstruction";
 import { WormholeMultisigInstruction } from "./WormholeMultisigInstruction";
 
+export const UNRECOGNIZED_INSTRUCTION = "unrecognizedInstruction";
 export enum MultisigInstructionProgram {
   PythOracle,
   WormholeBridge,