executor.ts 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. import { parseVaa } from "@certusone/wormhole-sdk";
  2. import {
  3. DataSource,
  4. EvmExecute,
  5. decodeGovernancePayload,
  6. } from "xc_admin_common";
  7. import { DefaultStore } from "./store";
  8. import { PrivateKey, TxResult } from "./base";
  9. import { EvmExecutorContract } from "./contracts";
  10. import { EvmChain } from "./chains";
  11. // TODO: A better place for this would be `base.ts`. That will require
  12. // significant refactor. Todo in separate PR.
  13. interface GovernanceContract {
  14. getId(): string;
  15. getGovernanceDataSource(): Promise<DataSource>;
  16. getLastExecutedGovernanceSequence(): Promise<number>;
  17. executeGovernanceInstruction(
  18. senderPrivateKey: PrivateKey,
  19. vaa: Buffer
  20. ): Promise<TxResult>;
  21. }
  22. async function executeForGovernanceContract(
  23. contract: GovernanceContract,
  24. vaa: Buffer,
  25. senderPrivateKey: PrivateKey
  26. ) {
  27. const parsedVaa = parseVaa(vaa);
  28. const governanceSource = await contract.getGovernanceDataSource();
  29. if (
  30. governanceSource.emitterAddress ===
  31. parsedVaa.emitterAddress.toString("hex") &&
  32. governanceSource.emitterChain === parsedVaa.emitterChain
  33. ) {
  34. const lastExecutedSequence =
  35. await contract.getLastExecutedGovernanceSequence();
  36. if (lastExecutedSequence >= parsedVaa.sequence) {
  37. console.log(
  38. `Skipping on contract ${contract.getId()} as it was already executed`
  39. );
  40. return;
  41. }
  42. const { id } = await contract.executeGovernanceInstruction(
  43. senderPrivateKey,
  44. vaa
  45. );
  46. console.log(`Executed on contract ${contract.getId()} with txHash: ${id}`);
  47. }
  48. }
  49. /**
  50. * A general executor that tries to find any contract that can execute a given VAA and executes it
  51. * @param senderPrivateKey the private key to execute the governance instruction with
  52. * @param vaa the VAA to execute
  53. */
  54. export async function executeVaa(senderPrivateKey: PrivateKey, vaa: Buffer) {
  55. const parsedVaa = parseVaa(vaa);
  56. const action = decodeGovernancePayload(parsedVaa.payload);
  57. if (!action) return; //TODO: handle other actions
  58. if (action instanceof EvmExecute) {
  59. for (const chain of Object.values(DefaultStore.chains)) {
  60. if (
  61. chain instanceof EvmChain &&
  62. chain.wormholeChainName === action.targetChainId
  63. ) {
  64. const executorContract = new EvmExecutorContract(
  65. chain,
  66. action.executorAddress
  67. );
  68. await executeForGovernanceContract(
  69. executorContract,
  70. vaa,
  71. senderPrivateKey
  72. );
  73. }
  74. }
  75. } else {
  76. for (const contract of Object.values(DefaultStore.contracts)) {
  77. if (
  78. action.targetChainId === "unset" ||
  79. contract.getChain().wormholeChainName === action.targetChainId
  80. )
  81. await executeForGovernanceContract(contract, vaa, senderPrivateKey);
  82. }
  83. }
  84. }