Forráskód Böngészése

feat(governance): add SetFeeInToken directive (#1654)

* feat(governance): add SetFeeInToken directive

* refactor(governance): custom decode/encode for SetFeeInToken
Pavel Strakhov 1 éve
szülő
commit
046ba8ab75

+ 41 - 1
governance/xc_admin/packages/xc_admin_common/src/__tests__/GovernancePayload.test.ts

@@ -26,7 +26,7 @@ import {
   AuthorizeGovernanceDataSourceTransfer,
   RequestGovernanceDataSourceTransfer,
 } from "../governance_payload/GovernanceDataSourceTransfer";
-import { SetFee } from "../governance_payload/SetFee";
+import { SetFee, SetFeeInToken } from "../governance_payload/SetFee";
 import { SetValidPeriod } from "../governance_payload/SetValidPeriod";
 import {
   DataSource,
@@ -196,6 +196,28 @@ test("GovernancePayload ser/de", (done) => {
     )
   ).toBeTruthy();
 
+  const setFeeInToken = new SetFeeInToken(
+    "starknet",
+    42n,
+    8n,
+    Buffer.from(
+      "049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7",
+      "hex"
+    )
+  );
+  const setFeeInTokenBuffer = setFeeInToken.encode();
+  console.log(setFeeInTokenBuffer.toJSON());
+  expect(
+    setFeeInTokenBuffer.equals(
+      Buffer.from([
+        80, 84, 71, 77, 1, 7, 234, 147, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0,
+        0, 0, 8, 32, 4, 157, 54, 87, 13, 78, 70, 244, 142, 153, 103, 75, 211,
+        252, 200, 70, 68, 221, 214, 185, 111, 124, 116, 27, 21, 98, 184, 47,
+        158, 0, 77, 199,
+      ])
+    )
+  ).toBeTruthy();
+
   const setDataSources = new SetDataSources("starknet", [
     {
       emitterChain: 1,
@@ -384,6 +406,24 @@ function governanceActionArb(): Arbitrary<PythGovernanceAction> {
               callData
             )
         );
+    } else if (header.action === "SetFeeInToken") {
+      return fc
+        .record({
+          value: fc.bigUintN(64),
+          expo: fc.bigUintN(64),
+          token: fc.array(fc.integer({ min: 0, max: 255 }), {
+            minLength: 0,
+            maxLength: 128,
+          }),
+        })
+        .map(({ value, expo, token }) => {
+          return new SetFeeInToken(
+            header.targetChainId,
+            value,
+            expo,
+            Buffer.from(token)
+          );
+        });
     } else {
       throw new Error("Unsupported action type");
     }

+ 3 - 0
governance/xc_admin/packages/xc_admin_common/src/governance_payload/PythGovernanceAction.ts

@@ -15,6 +15,7 @@ export const TargetAction = {
   SetValidPeriod: 4,
   RequestGovernanceDataSourceTransfer: 5,
   SetWormholeAddress: 6,
+  SetFeeInToken: 7,
 } as const;
 
 export const EvmExecutorAction = {
@@ -43,6 +44,8 @@ export function toActionName(
         return "RequestGovernanceDataSourceTransfer";
       case 6:
         return "SetWormholeAddress";
+      case 7:
+        return "SetFeeInToken";
     }
   } else if (
     deserialized.moduleId == MODULE_EVM_EXECUTOR &&

+ 67 - 1
governance/xc_admin/packages/xc_admin_common/src/governance_payload/SetFee.ts

@@ -1,4 +1,7 @@
-import { PythGovernanceActionImpl } from "./PythGovernanceAction";
+import {
+  PythGovernanceActionImpl,
+  PythGovernanceHeader,
+} from "./PythGovernanceAction";
 import * as BufferLayout from "@solana/buffer-layout";
 import * as BufferLayoutExt from "./BufferLayoutExt";
 import { ChainName } from "../chains";
@@ -42,3 +45,66 @@ export class SetFee extends PythGovernanceActionImpl {
     });
   }
 }
+
+/** Set the fee in the specified token on the target chain to newFeeValue * 10^newFeeExpo.
+ * The length and encoding of token is chain-specific:
+ * - On Starknet, it's a 256-bit BE integer representing the token account address.
+ */
+export class SetFeeInToken extends PythGovernanceActionImpl {
+  static layout: BufferLayout.Structure<
+    Readonly<{
+      newFeeValue: bigint;
+      newFeeExpo: bigint;
+      tokenLen: number;
+    }>
+  > = BufferLayout.struct([
+    BufferLayoutExt.u64be("newFeeValue"),
+    BufferLayoutExt.u64be("newFeeExpo"),
+    BufferLayout.u8("tokenLen"),
+  ]);
+
+  constructor(
+    targetChainId: ChainName,
+    readonly newFeeValue: bigint,
+    readonly newFeeExpo: bigint,
+    readonly token: Buffer
+  ) {
+    super(targetChainId, "SetFeeInToken");
+  }
+
+  static decode(data: Buffer): SetFeeInToken | undefined {
+    const header = PythGovernanceHeader.decode(data);
+    if (!header || header.action !== "SetFeeInToken") {
+      return undefined;
+    }
+
+    let index = PythGovernanceHeader.span;
+    const fields = SetFeeInToken.layout.decode(data, index);
+    index += SetFeeInToken.layout.span;
+    return new SetFeeInToken(
+      header.targetChainId,
+      fields.newFeeValue,
+      fields.newFeeExpo,
+      data.subarray(index)
+    );
+  }
+
+  encode(): Buffer {
+    const headerBuffer = new PythGovernanceHeader(
+      this.targetChainId,
+      "SetFeeInToken"
+    ).encode();
+
+    const fieldsBuf = Buffer.alloc(SetFeeInToken.layout.span);
+    SetFeeInToken.layout.encode(
+      {
+        newFeeValue: this.newFeeValue,
+        newFeeExpo: this.newFeeExpo,
+        tokenLen: this.token.length,
+      },
+      fieldsBuf
+    );
+
+    return Buffer.concat([headerBuffer, fieldsBuf, this.token]);
+  }
+}

+ 3 - 1
governance/xc_admin/packages/xc_admin_common/src/governance_payload/index.ts

@@ -14,7 +14,7 @@ import {
 } from "./GovernanceDataSourceTransfer";
 import { SetDataSources } from "./SetDataSources";
 import { SetValidPeriod } from "./SetValidPeriod";
-import { SetFee } from "./SetFee";
+import { SetFee, SetFeeInToken } from "./SetFee";
 import {
   EvmSetWormholeAddress,
   StarknetSetWormholeAddress,
@@ -52,6 +52,8 @@ export function decodeGovernancePayload(
       return SetDataSources.decode(data);
     case "SetFee":
       return SetFee.decode(data);
+    case "SetFeeInToken":
+      return SetFeeInToken.decode(data);
     case "SetValidPeriod":
       return SetValidPeriod.decode(data);
     case "RequestGovernanceDataSourceTransfer":