Skip to content

Commit 2ab6165

Browse files
committed
feat(governance): add SetTransactionFee action and implementation for transaction fee management
1 parent b74f034 commit 2ab6165

File tree

5 files changed

+87
-1
lines changed

5 files changed

+87
-1
lines changed

governance/xc_admin/packages/xc_admin_common/src/governance_payload/PythGovernanceAction.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export const TargetAction = {
1616
RequestGovernanceDataSourceTransfer: 5,
1717
SetWormholeAddress: 6,
1818
SetFeeInToken: 7,
19+
SetTransactionFee: 8,
1920
} as const;
2021

2122
export const EvmExecutorAction = {
@@ -46,6 +47,8 @@ export function toActionName(
4647
return "SetWormholeAddress";
4748
case 7:
4849
return "SetFeeInToken";
50+
case 8:
51+
return "SetTransactionFee";
4952
}
5053
} else if (
5154
deserialized.moduleId == MODULE_EVM_EXECUTOR &&
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { PythGovernanceActionImpl } from "./PythGovernanceAction";
2+
import * as BufferLayout from "@solana/buffer-layout";
3+
import * as BufferLayoutExt from "./BufferLayoutExt";
4+
import { ChainName } from "../chains";
5+
6+
/** Set the transaction fee on the target chain to newFeeValue * 10^newFeeExpo */
7+
export class SetTransactionFee extends PythGovernanceActionImpl {
8+
static layout: BufferLayout.Structure<
9+
Readonly<{ newFeeValue: bigint; newFeeExpo: bigint }>
10+
> = BufferLayout.struct([
11+
BufferLayoutExt.u64be("newFeeValue"),
12+
BufferLayoutExt.u64be("newFeeExpo"),
13+
]);
14+
15+
constructor(
16+
targetChainId: ChainName,
17+
readonly newFeeValue: bigint,
18+
readonly newFeeExpo: bigint,
19+
) {
20+
super(targetChainId, "SetTransactionFee");
21+
}
22+
23+
static decode(data: Buffer): SetTransactionFee | undefined {
24+
const decoded = PythGovernanceActionImpl.decodeWithPayload(
25+
data,
26+
"SetTransactionFee",
27+
SetTransactionFee.layout,
28+
);
29+
if (!decoded) return undefined;
30+
31+
return new SetTransactionFee(
32+
decoded[0].targetChainId,
33+
decoded[1].newFeeValue,
34+
decoded[1].newFeeExpo,
35+
);
36+
}
37+
38+
encode(): Buffer {
39+
return super.encodeWithPayload(SetTransactionFee.layout, {
40+
newFeeValue: this.newFeeValue,
41+
newFeeExpo: this.newFeeExpo,
42+
});
43+
}
44+
}

governance/xc_admin/packages/xc_admin_common/src/governance_payload/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
StarknetSetWormholeAddress,
2121
} from "./SetWormholeAddress";
2222
import { EvmExecute } from "./ExecuteAction";
23+
import { SetTransactionFee } from "./SetTransactionFee";
2324

2425
/** Decode a governance payload */
2526
export function decodeGovernancePayload(
@@ -73,6 +74,8 @@ export function decodeGovernancePayload(
7374
}
7475
case "Execute":
7576
return EvmExecute.decode(data);
77+
case "SetTransactionFee":
78+
return SetTransactionFee.decode(data);
7679
default:
7780
return undefined;
7881
}
@@ -86,5 +89,6 @@ export * from "./GovernanceDataSourceTransfer";
8689
export * from "./SetDataSources";
8790
export * from "./SetValidPeriod";
8891
export * from "./SetFee";
92+
export * from "./SetTransactionFee";
8993
export * from "./SetWormholeAddress";
9094
export * from "./ExecuteAction";

target_chains/ethereum/contracts/contracts/pyth/PythGovernance.sol

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ abstract contract PythGovernance is
3838
address oldWormholeAddress,
3939
address newWormholeAddress
4040
);
41+
event TransactionFeeSet(uint oldFee, uint newFee);
4142

4243
function verifyGovernanceVM(
4344
bytes memory encodedVM
@@ -97,6 +98,8 @@ abstract contract PythGovernance is
9798
parseSetWormholeAddressPayload(gi.payload),
9899
encodedVM
99100
);
101+
} else if (gi.action == GovernanceAction.SetTransactionFee) {
102+
setTransactionFee(parseSetTransactionFeePayload(gi.payload));
100103
} else {
101104
revert PythErrors.InvalidGovernanceMessage();
102105
}
@@ -243,4 +246,13 @@ abstract contract PythGovernance is
243246

244247
emit WormholeAddressSet(oldWormholeAddress, address(wormhole()));
245248
}
249+
250+
function setTransactionFee(
251+
SetTransactionFeePayload memory payload
252+
) internal {
253+
uint oldFee = transactionFeeInWei();
254+
setTransactionFeeInWei(payload.newFee);
255+
256+
emit TransactionFeeSet(oldFee, transactionFeeInWei());
257+
}
246258
}

target_chains/ethereum/contracts/contracts/pyth/PythGovernanceInstructions.sol

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ contract PythGovernanceInstructions {
3434
SetFee, // 3
3535
SetValidPeriod, // 4
3636
RequestGovernanceDataSourceTransfer, // 5
37-
SetWormholeAddress // 6
37+
SetWormholeAddress, // 6
38+
SetTransactionFee // 7
3839
}
3940

4041
struct GovernanceInstruction {
@@ -77,6 +78,10 @@ contract PythGovernanceInstructions {
7778
address newWormholeAddress;
7879
}
7980

81+
struct SetTransactionFeePayload {
82+
uint newFee;
83+
}
84+
8085
/// @dev Parse a GovernanceInstruction
8186
function parseGovernanceInstruction(
8287
bytes memory encodedInstruction
@@ -220,4 +225,22 @@ contract PythGovernanceInstructions {
220225
if (encodedPayload.length != index)
221226
revert PythErrors.InvalidGovernanceMessage();
222227
}
228+
229+
/// @dev Parse a SetTransactionFeePayload (action 7) with minimal validation
230+
function parseSetTransactionFeePayload(
231+
bytes memory encodedPayload
232+
) public pure returns (SetTransactionFeePayload memory stf) {
233+
uint index = 0;
234+
235+
uint64 val = encodedPayload.toUint64(index);
236+
index += 8;
237+
238+
uint64 expo = encodedPayload.toUint64(index);
239+
index += 8;
240+
241+
stf.newFee = uint256(val) * uint256(10) ** uint256(expo);
242+
243+
if (encodedPayload.length != index)
244+
revert PythErrors.InvalidGovernanceMessage();
245+
}
223246
}

0 commit comments

Comments
 (0)