Selaa lähdekoodia

feat(governance): add SetTransactionFee action and implementation for transaction fee management

Daniel Chew 7 kuukautta sitten
vanhempi
sitoutus
2ab61658b1

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

@@ -16,6 +16,7 @@ export const TargetAction = {
   RequestGovernanceDataSourceTransfer: 5,
   SetWormholeAddress: 6,
   SetFeeInToken: 7,
+  SetTransactionFee: 8,
 } as const;
 
 export const EvmExecutorAction = {
@@ -46,6 +47,8 @@ export function toActionName(
         return "SetWormholeAddress";
       case 7:
         return "SetFeeInToken";
+      case 8:
+        return "SetTransactionFee";
     }
   } else if (
     deserialized.moduleId == MODULE_EVM_EXECUTOR &&

+ 44 - 0
governance/xc_admin/packages/xc_admin_common/src/governance_payload/SetTransactionFee.ts

@@ -0,0 +1,44 @@
+import { PythGovernanceActionImpl } from "./PythGovernanceAction";
+import * as BufferLayout from "@solana/buffer-layout";
+import * as BufferLayoutExt from "./BufferLayoutExt";
+import { ChainName } from "../chains";
+
+/** Set the transaction fee on the target chain to newFeeValue * 10^newFeeExpo */
+export class SetTransactionFee extends PythGovernanceActionImpl {
+  static layout: BufferLayout.Structure<
+    Readonly<{ newFeeValue: bigint; newFeeExpo: bigint }>
+  > = BufferLayout.struct([
+    BufferLayoutExt.u64be("newFeeValue"),
+    BufferLayoutExt.u64be("newFeeExpo"),
+  ]);
+
+  constructor(
+    targetChainId: ChainName,
+    readonly newFeeValue: bigint,
+    readonly newFeeExpo: bigint,
+  ) {
+    super(targetChainId, "SetTransactionFee");
+  }
+
+  static decode(data: Buffer): SetTransactionFee | undefined {
+    const decoded = PythGovernanceActionImpl.decodeWithPayload(
+      data,
+      "SetTransactionFee",
+      SetTransactionFee.layout,
+    );
+    if (!decoded) return undefined;
+
+    return new SetTransactionFee(
+      decoded[0].targetChainId,
+      decoded[1].newFeeValue,
+      decoded[1].newFeeExpo,
+    );
+  }
+
+  encode(): Buffer {
+    return super.encodeWithPayload(SetTransactionFee.layout, {
+      newFeeValue: this.newFeeValue,
+      newFeeExpo: this.newFeeExpo,
+    });
+  }
+}

+ 4 - 0
governance/xc_admin/packages/xc_admin_common/src/governance_payload/index.ts

@@ -20,6 +20,7 @@ import {
   StarknetSetWormholeAddress,
 } from "./SetWormholeAddress";
 import { EvmExecute } from "./ExecuteAction";
+import { SetTransactionFee } from "./SetTransactionFee";
 
 /** Decode a governance payload */
 export function decodeGovernancePayload(
@@ -73,6 +74,8 @@ export function decodeGovernancePayload(
     }
     case "Execute":
       return EvmExecute.decode(data);
+    case "SetTransactionFee":
+      return SetTransactionFee.decode(data);
     default:
       return undefined;
   }
@@ -86,5 +89,6 @@ export * from "./GovernanceDataSourceTransfer";
 export * from "./SetDataSources";
 export * from "./SetValidPeriod";
 export * from "./SetFee";
+export * from "./SetTransactionFee";
 export * from "./SetWormholeAddress";
 export * from "./ExecuteAction";

+ 12 - 0
target_chains/ethereum/contracts/contracts/pyth/PythGovernance.sol

@@ -38,6 +38,7 @@ abstract contract PythGovernance is
         address oldWormholeAddress,
         address newWormholeAddress
     );
+    event TransactionFeeSet(uint oldFee, uint newFee);
 
     function verifyGovernanceVM(
         bytes memory encodedVM
@@ -97,6 +98,8 @@ abstract contract PythGovernance is
                 parseSetWormholeAddressPayload(gi.payload),
                 encodedVM
             );
+        } else if (gi.action == GovernanceAction.SetTransactionFee) {
+            setTransactionFee(parseSetTransactionFeePayload(gi.payload));
         } else {
             revert PythErrors.InvalidGovernanceMessage();
         }
@@ -243,4 +246,13 @@ abstract contract PythGovernance is
 
         emit WormholeAddressSet(oldWormholeAddress, address(wormhole()));
     }
+
+    function setTransactionFee(
+        SetTransactionFeePayload memory payload
+    ) internal {
+        uint oldFee = transactionFeeInWei();
+        setTransactionFeeInWei(payload.newFee);
+
+        emit TransactionFeeSet(oldFee, transactionFeeInWei());
+    }
 }

+ 24 - 1
target_chains/ethereum/contracts/contracts/pyth/PythGovernanceInstructions.sol

@@ -34,7 +34,8 @@ contract PythGovernanceInstructions {
         SetFee, // 3
         SetValidPeriod, // 4
         RequestGovernanceDataSourceTransfer, // 5
-        SetWormholeAddress // 6
+        SetWormholeAddress, // 6
+        SetTransactionFee // 7
     }
 
     struct GovernanceInstruction {
@@ -77,6 +78,10 @@ contract PythGovernanceInstructions {
         address newWormholeAddress;
     }
 
+    struct SetTransactionFeePayload {
+        uint newFee;
+    }
+
     /// @dev Parse a GovernanceInstruction
     function parseGovernanceInstruction(
         bytes memory encodedInstruction
@@ -220,4 +225,22 @@ contract PythGovernanceInstructions {
         if (encodedPayload.length != index)
             revert PythErrors.InvalidGovernanceMessage();
     }
+
+    /// @dev Parse a SetTransactionFeePayload (action 7) with minimal validation
+    function parseSetTransactionFeePayload(
+        bytes memory encodedPayload
+    ) public pure returns (SetTransactionFeePayload memory stf) {
+        uint index = 0;
+
+        uint64 val = encodedPayload.toUint64(index);
+        index += 8;
+
+        uint64 expo = encodedPayload.toUint64(index);
+        index += 8;
+
+        stf.newFee = uint256(val) * uint256(10) ** uint256(expo);
+
+        if (encodedPayload.length != index)
+            revert PythErrors.InvalidGovernanceMessage();
+    }
 }