Prechádzať zdrojové kódy

Movement move (#1211)

* [contract manager] Implement WormholeAptosContract and integrate aptos cli with the manager

* Add getChainId for wormhole contracts

* Add movement contracts

* Simplify aptos cli
Amin Moghaddam 1 rok pred
rodič
commit
cee5da93d8

+ 24 - 2
contract_manager/src/chains.ts

@@ -1,4 +1,4 @@
-import { KeyValueConfig, PrivateKey, Storable } from "./base";
+import { KeyValueConfig, PrivateKey, Storable, TxResult } from "./base";
 import {
   ChainName,
   SetFee,
@@ -12,7 +12,7 @@ import {
   DataSource,
   EvmSetWormholeAddress,
 } from "xc_admin_common";
-import { AptosClient, AptosAccount, CoinClient } from "aptos";
+import { AptosClient, AptosAccount, CoinClient, TxnBuilderTypes } from "aptos";
 import Web3 from "web3";
 import {
   CosmwasmExecutor,
@@ -55,6 +55,10 @@ export abstract class Chain extends Storable {
       );
   }
 
+  public getWormholeChainId(): number {
+    return toChainId(this.wormholeChainName);
+  }
+
   getId(): string {
     return this.id;
   }
@@ -509,4 +513,22 @@ export class AptosChain extends Chain {
     const coinClient = new CoinClient(client);
     return Number(await coinClient.checkBalance(account)) / 10 ** 8;
   }
+
+  async sendTransaction(
+    senderPrivateKey: PrivateKey,
+    txPayload: TxnBuilderTypes.TransactionPayloadEntryFunction
+  ): Promise<TxResult> {
+    const client = this.getClient();
+    const sender = new AptosAccount(
+      new Uint8Array(Buffer.from(senderPrivateKey, "hex"))
+    );
+    const result = await client.generateSignSubmitWaitForTransaction(
+      sender,
+      txPayload,
+      {
+        maxGasAmount: BigInt(30000),
+      }
+    );
+    return { id: result.hash, info: result };
+  }
 }

+ 72 - 20
contract_manager/src/contracts/aptos.ts

@@ -1,8 +1,74 @@
 import { Contract, PriceFeed, PrivateKey, TxResult } from "../base";
-import { ApiError, AptosAccount, BCS, TxnBuilderTypes } from "aptos";
+import { ApiError, BCS, CoinClient, TxnBuilderTypes } from "aptos";
 import { AptosChain, Chain } from "../chains";
 import { DataSource } from "xc_admin_common";
-import { CoinClient } from "aptos";
+import { WormholeContract } from "./wormhole";
+
+type WormholeState = {
+  chain_id: { number: string };
+  guardian_set_index: { number: string };
+  guardian_sets: { handle: string };
+};
+
+type GuardianSet = {
+  guardians: { address: { bytes: string } }[];
+  expiration_time: { number: string };
+  index: { number: string };
+};
+
+export class WormholeAptosContract extends WormholeContract {
+  constructor(public chain: AptosChain, public address: string) {
+    super();
+  }
+
+  async getState(): Promise<WormholeState> {
+    const client = this.chain.getClient();
+    const resources = await client.getAccountResources(this.address);
+    const type = "WormholeState";
+    for (const resource of resources) {
+      if (resource.type === `${this.address}::state::${type}`) {
+        return resource.data as WormholeState;
+      }
+    }
+    throw new Error(`${type} resource not found in account ${this.address}`);
+  }
+
+  async getCurrentGuardianSetIndex(): Promise<number> {
+    const data = await this.getState();
+    return Number(data.guardian_set_index.number);
+  }
+
+  async getChainId(): Promise<number> {
+    const data = await this.getState();
+    return Number(data.chain_id.number);
+  }
+
+  async getGuardianSet(): Promise<string[]> {
+    const data = await this.getState();
+    const client = this.chain.getClient();
+    const result = (await client.getTableItem(data.guardian_sets.handle, {
+      key_type: `u64`,
+      value_type: `${this.address}::structs::GuardianSet`,
+      key: data.guardian_set_index.number.toString(),
+    })) as GuardianSet;
+    return result.guardians.map((guardian) => guardian.address.bytes);
+  }
+
+  async upgradeGuardianSets(
+    senderPrivateKey: PrivateKey,
+    vaa: Buffer
+  ): Promise<TxResult> {
+    const txPayload = new TxnBuilderTypes.TransactionPayloadEntryFunction(
+      TxnBuilderTypes.EntryFunction.natural(
+        `${this.address}::guardian_set_upgrade`,
+        "submit_vaa_entry",
+        [],
+        [BCS.bcsSerializeBytes(vaa)]
+      )
+    );
+    return this.chain.sendTransaction(senderPrivateKey, txPayload);
+  }
+}
 
 export class AptosContract extends Contract {
   static type = "AptosContract";
@@ -45,25 +111,11 @@ export class AptosContract extends Contract {
         [BCS.bcsSerializeBytes(vaa)]
       )
     );
-    return this.sendTransaction(senderPrivateKey, txPayload);
+    return this.chain.sendTransaction(senderPrivateKey, txPayload);
   }
 
-  private async sendTransaction(
-    senderPrivateKey: PrivateKey,
-    txPayload: TxnBuilderTypes.TransactionPayloadEntryFunction
-  ): Promise<TxResult> {
-    const client = this.chain.getClient();
-    const sender = new AptosAccount(
-      new Uint8Array(Buffer.from(senderPrivateKey, "hex"))
-    );
-    const result = await client.generateSignSubmitWaitForTransaction(
-      sender,
-      txPayload,
-      {
-        maxGasAmount: BigInt(30000),
-      }
-    );
-    return { id: result.hash, info: result };
+  public getWormholeContract(): WormholeAptosContract {
+    return new WormholeAptosContract(this.chain, this.wormholeStateId);
   }
 
   async executeUpdatePriceFeed(
@@ -78,7 +130,7 @@ export class AptosContract extends Contract {
         [BCS.serializeVectorWithFunc(vaas, "serializeBytes")]
       )
     );
-    return this.sendTransaction(senderPrivateKey, txPayload);
+    return this.chain.sendTransaction(senderPrivateKey, txPayload);
   }
 
   getStateResources() {

+ 5 - 0
contract_manager/src/contracts/cosmwasm.ts

@@ -54,6 +54,11 @@ export class WormholeCosmWasmContract extends WormholeContract {
     return JSON.parse(config["\x00\x06config"])["guardian_set_index"];
   }
 
+  async getChainId(): Promise<number> {
+    const config = await this.getConfig();
+    return JSON.parse(config["\x00\x06config"])["chain_id"];
+  }
+
   async getGuardianSet(): Promise<string[]> {
     const config = await this.getConfig();
     const guardianSetIndex = JSON.parse(config["\x00\x06config"])[

+ 18 - 0
contract_manager/src/contracts/evm.ts

@@ -154,6 +154,19 @@ const WORMHOLE_ABI = [
     stateMutability: "view",
     type: "function",
   },
+  {
+    inputs: [],
+    name: "chainId",
+    outputs: [
+      {
+        internalType: "uint16",
+        name: "",
+        type: "uint16",
+      },
+    ],
+    stateMutability: "view",
+    type: "function",
+  },
   {
     inputs: [
       {
@@ -228,6 +241,11 @@ export class WormholeEvmContract extends WormholeContract {
     );
   }
 
+  async getChainId(): Promise<number> {
+    const wormholeContract = this.getContract();
+    return Number(await wormholeContract.methods.chainId().call());
+  }
+
   /**
    * Returns an array of guardian addresses used for VAA verification in this contract
    */

+ 11 - 1
contract_manager/src/contracts/wormhole.ts

@@ -3,6 +3,12 @@ import { PrivateKey, TxResult } from "../base";
 export abstract class WormholeContract {
   abstract getCurrentGuardianSetIndex(): Promise<number>;
 
+  /**
+   * Returns the chain id set in this contract.
+   * This should match to the chain ids stored in this repo in the chains.ts file based on the network
+   */
+  abstract getChainId(): Promise<number>;
+
   /**
    * Returns an array of guardian addresses used for VAA verification in this contract
    */
@@ -31,7 +37,11 @@ export abstract class WormholeContract {
     const currentIndex = await this.getCurrentGuardianSetIndex();
     for (let i = currentIndex; i < MAINNET_UPGRADE_VAAS.length; i++) {
       const vaa = MAINNET_UPGRADE_VAAS[i];
-      await this.upgradeGuardianSets(senderPrivateKey, Buffer.from(vaa, "hex"));
+      const result = await this.upgradeGuardianSets(
+        senderPrivateKey,
+        Buffer.from(vaa, "hex")
+      );
+      console.log(`Submitted upgrade VAA ${i} with tx id ${result.id}`);
       // make sure the upgrade is complete before continuing
       while ((await this.getCurrentGuardianSetIndex()) <= i) {
         await new Promise((resolve) => setTimeout(resolve, 5000));

+ 1 - 1
contract_manager/src/shell.ts

@@ -10,7 +10,7 @@ repl.evalCode(
     "import { SuiContract } from './src/contracts/sui';" +
     "import { WormholeCosmWasmContract, CosmWasmContract } from './src/contracts/cosmwasm';" +
     "import { WormholeEvmContract, EvmContract } from './src/contracts/evm';" +
-    "import { AptosContract } from './src/contracts/aptos';" +
+    "import { WormholeAptosContract, AptosContract } from './src/contracts/aptos';" +
     "import { DefaultStore } from './src/store';" +
     "import { toPrivateKey } from './src/base';" +
     "DefaultStore"

+ 5 - 0
contract_manager/store/chains/AptosChains.yaml

@@ -8,3 +8,8 @@
   mainnet: true
   rpcUrl: https://fullnode.mainnet.aptoslabs.com/v1
   type: AptosChain
+- id: movement_move_devnet
+  wormholeChainName: movement_move_devnet
+  mainnet: false
+  rpcUrl: https://devnet.m1.movementlabs.xyz/v1
+  type: AptosChain

+ 5 - 0
contract_manager/store/chains/EvmChains.yaml

@@ -388,3 +388,8 @@
   rpcUrl: https://sepolia.base.org
   networkId: 84532
   type: EvmChain
+- id: movement_evm_devnet
+  mainnet: false
+  rpcUrl: https://mevm.devnet.m1.movementlabs.xyz/v1
+  networkId: 336
+  type: EvmChain

+ 4 - 0
contract_manager/store/contracts/AptosContracts.yaml

@@ -6,3 +6,7 @@
   stateId: "0x7e783b349d3e89cf5931af376ebeadbfab855b3fa239b7ada8f5a92fbea6b387"
   wormholeStateId: "0x5bc11445584a763c1fa7ed39081f1b920954da14e04b32440cba863d03e19625"
   type: AptosContract
+- chain: movement_move_devnet
+  stateId: "0x9357e76fe965c9956a76181ee49f66d51b7f9c3800182a944ed96be86301e49f"
+  wormholeStateId: "0x9236893d6444b208b7e0b3e8d4be4ace90b6d17817ab7d1584e46a33ef5c50c9"
+  type: AptosContract

+ 3 - 0
contract_manager/store/contracts/EvmContracts.yaml

@@ -232,3 +232,6 @@
 - chain: base_sepolia
   address: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729"
   type: EvmContract
+- chain: movement_evm_devnet
+  address: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729"
+  type: EvmContract

+ 2 - 0
governance/xc_admin/packages/xc_admin_common/src/chains.ts

@@ -109,6 +109,8 @@ export const RECEIVER_CHAINS = {
   bttc_testnet: 50041,
   zksync_sepolia: 50042,
   base_sepolia: 50043,
+  movement_evm_devnet: 50044,
+  movement_move_devnet: 50045,
 };
 
 // If there is any overlapping value the receiver chain will replace the wormhole

+ 8 - 31
target_chains/aptos/cli/README.md

@@ -2,8 +2,7 @@
 
 Install aptos cli with the same version specified in the ci workflows.
 
-All the commands which submit transactions require an environment variable for the private key to be set.
-Depending on the network, this can be either `APTOS_LOCALNET_KEY`, `APTOS_TESTNET_KEY` or `APTOS_MAINNET_KEY`.
+All the commands which submit transactions require an environment variable `APTOS_PRIVATE_KEY` for the private key to be set.
 
 # Deploying from scratch
 
@@ -13,7 +12,10 @@ capability. You can read more about it [here](https://github.com/wormhole-founda
 Assuming the wormhole and deployer contracts are already deployed, we can deploy the pyth oracle with the following command:
 
 ```bash
-npm run cli deploy-pyth -- ../contracts <seed> -n testnet
+npm run cli deploy-pyth -- ../contracts <seed> \
+-n aptos_testnet \
+--deployer <deployer-address> \
+--wormhole <wormhole-address>
 ```
 
 `seed` can be any random string that is used for determining a specific contract address based on the seed value and the signer address.
@@ -30,40 +32,15 @@ wormhole = "_"
 
 ### Initializing pyth
 
-You can run the following to initialize the pyth contract, the following is a sample (testnet) config:
+You can run the following to initialize the pyth contract:
 
 ```bash
-npm run cli init-pyth -- <seed> -n testnet \
+npm run cli init-pyth -- <seed> -n <network> \
 --stale-price-threshold 60 \
 --update-fee 1 \
---governance-emitter-chain-id 1 \
---governance-emitter-address 63278d271099bfd491951b3e648f08b1c71631e4a53674ad43e8f9f98068c385 \
---data-source-chain-ids 1 \
---data-source-chain-ids 26 \
---data-source-chain-ids 26 \
---data-source-emitter-addresses f346195ac02f37d60d4db8ffa6ef74cb1be3550047543a4a9ee9acf4d78697b0 \
---data-source-emitter-addresses a27839d641b07743c0cb5f68c51f8cd31d2c0762bec00dc6fcd25433ef1ab5b6 \
---data-source-emitter-addresses e101faedac5851e32b9b23b5f9411a8c2bac4aae3ed4dd7b811dd1a72ea4aa71
+--channel <stable-or-beta>
 ```
 
-The following is a sample mainnet config:
-
-```bash
-npm run cli init-pyth -- <seed> -n mainnet \
---stale-price-threshold 60 \
---update-fee 1 \
---governance-emitter-chain-id 1 \
---governance-emitter-address 5635979a221c34931e32620b9293a463065555ea71fe97cd6237ade875b12e9e \
---data-source-chain-ids 1 \
---data-source-chain-ids 26 \
---data-source-chain-ids 26 \
---data-source-emitter-addresses 6bb14509a612f01fbbc4cffeebd4bbfb492a86df717ebe92eb6df432a3f00a25 \
---data-source-emitter-addresses f8cd23c2ab91237730770bbea08d61005cdda0984348f3f6eecb559638c0bba0 \
---data-source-emitter-addresses e101faedac5851e32b9b23b5f9411a8c2bac4aae3ed4dd7b811dd1a72ea4aa71
-```
-
-Note that the `data-source-chain-ids` are paired with `data-source-emitter-addresses` and their order matters.
-
 # Upgrade process:
 
 The following steps are needed to upgrade our aptos contracts:

+ 121 - 132
target_chains/aptos/cli/src/commands/aptos.ts

@@ -4,34 +4,40 @@ import { AptosAccount, AptosClient, BCS, TxnBuilderTypes } from "aptos";
 import fs from "fs";
 import sha3 from "js-sha3";
 import { ethers } from "ethers";
+import {
+  AptosChain,
+  DefaultStore,
+  getDefaultDeploymentConfig,
+} from "contract_manager";
 
-const LOCALNET: string = "localnet";
-const TESTNET: string = "testnet";
-const MAINNET: string = "mainnet";
-
-interface Network {
-  // RPC endpoint of the network
-  endpoint: string;
-  // Private key of the network
-  key: string | undefined;
-}
+const NETWORK_CHOICES = Object.entries(DefaultStore.chains)
+  .filter(([chain, config]) => {
+    return config instanceof AptosChain;
+  })
+  .map(([chain, _]) => {
+    return chain;
+  });
 
 const NETWORK_OPTION = {
   alias: "n",
   describe: "network",
   type: "string",
-  choices: [LOCALNET, TESTNET, MAINNET],
+  choices: NETWORK_CHOICES,
+  demandOption: true,
+} as const;
+const CHANNEL_OPTION = {
+  describe: "channel",
+  type: "string",
+  choices: ["stable", "beta"],
   demandOption: true,
 } as const;
 const DEPLOYER_OPTION = {
   describe: "deployer contract address deployed in the network",
   type: "string",
-  default: "0xb31e712b26fd295357355f6845e77c888298636609e93bc9b05f0f604049f434",
 } as const;
 const WORMHOLE_OPTION = {
   describe: "wormhole contract address deployed in the network",
   type: "string",
-  default: "0x5bc11445584a763c1fa7ed39081f1b920954da14e04b32440cba863d03e19625",
 } as const;
 const PYTH_OPTION = {
   describe: "pyth contract address deployed in the network",
@@ -50,29 +56,6 @@ interface PackageBCS {
   codeHash: Uint8Array;
 }
 
-const networks = new Map<string, Network>([
-  [
-    LOCALNET,
-    {
-      key: process.env["APTOS_LOCALNET_KEY"],
-      endpoint: "http://0.0.0.0:8080",
-    },
-  ],
-  [
-    TESTNET,
-    {
-      key: process.env["APTOS_TESTNET_KEY"],
-      endpoint: "https://fullnode.testnet.aptoslabs.com/v1",
-    },
-  ],
-  [
-    MAINNET,
-    {
-      key: process.env["APTOS_MAINNET_KEY"],
-      endpoint: "https://fullnode.mainnet.aptoslabs.com/v1",
-    },
-  ],
-]);
 export const builder: (args: Argv<any>) => Argv<any> = (yargs) =>
   yargs
     .command(
@@ -101,6 +84,37 @@ export const builder: (args: Argv<any>) => Argv<any> = (yargs) =>
         await executeTransaction(argv.network, txPayload);
       }
     )
+    .command(
+      "deploy-wormhole <package-dir> <seed>",
+      "Deploy the wormhole package using a resource account.",
+      (yargs) => {
+        return yargs
+          .positional("package-dir", { type: "string" })
+          .positional("seed", { type: "string" })
+          .option("deployer", DEPLOYER_OPTION)
+          .option("network", NETWORK_OPTION);
+      },
+      async (argv) => {
+        const sender = getSender();
+        const derivedAddress = generateDerivedAddress(
+          sender.address().toString(),
+          argv.seed!
+        );
+
+        const namedAddresses = `deployer=${argv.deployer},wormhole=0x${derivedAddress}`;
+        console.log("Building the package with the following named addresses:");
+        console.log(`Deployer=${argv.deployer}`);
+        console.log(`Wormhole=${derivedAddress}`);
+        const txPayload = createDeployDerivedTransaction(
+          argv["package-dir"],
+          argv.deployer,
+          argv.seed,
+          namedAddresses
+        );
+
+        await executeTransaction(argv.network, txPayload);
+      }
+    )
     .command(
       "deploy-pyth <package-dir> <seed>",
       "Deploy the pyth package using a resource account.",
@@ -113,7 +127,7 @@ export const builder: (args: Argv<any>) => Argv<any> = (yargs) =>
           .option("network", NETWORK_OPTION);
       },
       async (argv) => {
-        const sender = getSender(argv.network);
+        const sender = getSender();
         const derivedAddress = generateDerivedAddress(
           sender.address().toString(),
           argv.seed!
@@ -124,36 +138,31 @@ export const builder: (args: Argv<any>) => Argv<any> = (yargs) =>
         console.log(`Wormhole=${argv.wormhole}`);
         console.log(`Deployer=${argv.deployer}`);
         console.log(`Pyth=${derivedAddress}`);
-        const artifact = serializePackage(
-          buildPackage(argv["package-dir"]!, namedAddresses)
-        );
-
-        const txPayload = new TxnBuilderTypes.TransactionPayloadEntryFunction(
-          TxnBuilderTypes.EntryFunction.natural(
-            argv.deployer + "::deployer",
-            "deploy_derived",
-            [],
-            [
-              artifact.meta,
-              artifact.bytecodes,
-              BCS.bcsSerializeBytes(Buffer.from(argv["seed"]!, "ascii")),
-            ]
-          )
+        const txPayload = createDeployDerivedTransaction(
+          argv["package-dir"],
+          argv.deployer,
+          argv.seed,
+          namedAddresses
         );
 
         await executeTransaction(argv.network, txPayload);
       }
     )
     .command(
-      "derived-address <seed> <signer>",
+      "derived-address <seed>",
       "Generate the derived address for the given seed and sender address",
       (yargs) => {
         return yargs
           .positional("seed", { type: "string", demandOption: true })
-          .positional("signer", { type: "string", demandOption: true });
+          .option("signer", { type: "string" });
       },
       async (argv) => {
-        console.log(generateDerivedAddress(argv.signer, argv.seed));
+        console.log(
+          generateDerivedAddress(
+            argv.signer || getSender().address().toString(),
+            argv.seed
+          )
+        );
       }
     )
     .command(
@@ -162,39 +171,15 @@ export const builder: (args: Argv<any>) => Argv<any> = (yargs) =>
       (yargs) => {
         return yargs
           .option("network", NETWORK_OPTION)
-          .option("chain-id", {
-            describe: "Chain id",
-            type: "number",
-            default: 22,
-            demandOption: false,
-          })
-          .option("governance-chain-id", {
-            describe: "Governance chain id",
-            type: "number",
-            default: 1,
-            demandOption: false,
-          })
-          .option("governance-address", {
-            describe: "Governance address",
-            type: "string",
-            default:
-              "0x0000000000000000000000000000000000000000000000000000000000000004",
-            demandOption: false,
-          })
-          .option("guardian-address", {
-            alias: "g",
-            demandOption: true,
-            describe: "Initial guardian's address",
-            type: "string",
-          });
+          .option("channel", CHANNEL_OPTION);
       },
       async (argv) => {
-        const guardian_address = evm_address(
-          argv["guardian-address"]
-        ).substring(24);
-        const chain_id = argv["chain-id"];
-        const governance_address = evm_address(argv["governance-address"]);
-        const governance_chain_id = argv["governance-chain-id"];
+        const chain_id = DefaultStore.chains[argv.network].getWormholeChainId();
+        const config = getDefaultDeploymentConfig(argv.channel).wormholeConfig;
+
+        const governance_contract = config.governanceContract;
+        const governance_chain_id = config.governanceChainId;
+        const guardian_address = config.initialGuardianSet[0]; // assuming only one guardian for now
 
         const guardian_addresses_serializer = new BCS.Serializer();
         guardian_addresses_serializer.serializeU32AsUleb128(1);
@@ -205,10 +190,10 @@ export const builder: (args: Argv<any>) => Argv<any> = (yargs) =>
         const args = [
           BCS.bcsSerializeUint64(chain_id),
           BCS.bcsSerializeUint64(governance_chain_id),
-          BCS.bcsSerializeBytes(Buffer.from(governance_address, "hex")),
+          BCS.bcsSerializeBytes(Buffer.from(governance_contract, "hex")),
           guardian_addresses_serializer.getBytes(),
         ];
-        const sender = getSender(argv.network);
+        const sender = getSender();
         const wormholeAddress = generateDerivedAddress(
           sender.address().hex(),
           "wormhole"
@@ -216,7 +201,7 @@ export const builder: (args: Argv<any>) => Argv<any> = (yargs) =>
         const txPayload = new TxnBuilderTypes.TransactionPayloadEntryFunction(
           TxnBuilderTypes.EntryFunction.natural(
             `${wormholeAddress}::wormhole`,
-            "init_2",
+            "init",
             [],
             args
           )
@@ -237,57 +222,37 @@ export const builder: (args: Argv<any>) => Argv<any> = (yargs) =>
             type: "number",
             demandOption: true,
           })
-          .option("governance-emitter-chain-id", {
-            describe: "Governance emitter chain id",
-            type: "number",
-            demandOption: true,
-          })
-          .option("governance-emitter-address", {
-            describe: "Governance emitter address",
-            type: "string",
-            demandOption: true,
-          })
           .option("update-fee", {
             describe: "Update fee",
             type: "number",
             demandOption: true,
           })
-          .option("data-source-chain-ids", {
-            describe: "Data source chain IDs",
-            type: "array",
-            demandOption: true,
-          })
-          .option("data-source-emitter-addresses", {
-            describe: "Data source emitter addresses",
-            type: "array",
-            demandOption: true,
-          });
+          .option("channel", CHANNEL_OPTION);
       },
       async (argv) => {
         const stale_price_threshold = argv["stale-price-threshold"];
-        const governance_emitter_chain_id = argv["governance-emitter-chain-id"];
-        const governance_emitter_address = evm_address(
-          argv["governance-emitter-address"]
-        );
+        const update_fee = argv["update-fee"];
+
+        const config = getDefaultDeploymentConfig(argv.channel);
+        const governance_emitter_chain_id =
+          config.governanceDataSource.emitterChain;
+        const governance_emitter_address =
+          config.governanceDataSource.emitterAddress;
 
         const dataSourceChainIdsSerializer = new BCS.Serializer();
         dataSourceChainIdsSerializer.serializeU32AsUleb128(
-          argv["data-source-chain-ids"].length
-        );
-        argv["data-source-chain-ids"].forEach((chain_id: number) =>
-          dataSourceChainIdsSerializer.serializeU64(chain_id)
+          config.dataSources.length
         );
-
         const dataSourceEmitterAddressesSerializer = new BCS.Serializer();
         dataSourceEmitterAddressesSerializer.serializeU32AsUleb128(
-          argv["data-source-emitter-addresses"].length
+          config.dataSources.length
         );
-        argv["data-source-emitter-addresses"].forEach((emitter_address) => {
+        config.dataSources.forEach((ds) => {
+          dataSourceChainIdsSerializer.serializeU64(ds.emitterChain);
           dataSourceEmitterAddressesSerializer.serializeBytes(
-            Buffer.from(emitter_address as string, "hex")
+            Buffer.from(ds.emitterAddress, "hex")
           );
         });
-        const update_fee = argv["update-fee"];
 
         const args = [
           BCS.bcsSerializeUint64(stale_price_threshold),
@@ -297,7 +262,7 @@ export const builder: (args: Argv<any>) => Argv<any> = (yargs) =>
           dataSourceEmitterAddressesSerializer.getBytes(),
           BCS.bcsSerializeUint64(update_fee),
         ];
-        const sender = getSender(argv.network);
+        const sender = getSender();
         const pythAddress = generateDerivedAddress(
           sender.address().hex(),
           argv.seed
@@ -383,7 +348,8 @@ export const builder: (args: Argv<any>) => Argv<any> = (yargs) =>
           .option("network", NETWORK_OPTION);
       },
       async (argv) => {
-        const endpoint = networks.get(argv.network)!.endpoint;
+        const endpoint = (DefaultStore.chains[argv.network] as AptosChain)
+          .rpcUrl;
         const addr1 = argv["addr-1"];
         const addr2 = argv["addr-2"];
         const url = `${endpoint}/accounts/${addr1}/resource/0x1::code::PackageRegistry`;
@@ -416,22 +382,23 @@ export const builder: (args: Argv<any>) => Argv<any> = (yargs) =>
     )
     .demandCommand();
 
-function getSender(network: string) {
-  if (networks.get(network)!.key === undefined) {
+function getSender() {
+  const key = process.env["APTOS_PRIVATE_KEY"];
+  if (key === undefined) {
     throw new Error(
-      `No key for network ${network}. Please set the APTOS_${network.toUpperCase()}_KEY environment variable.`
+      `Please set the APTOS_PRIVATE_KEY environment variable to the private key of the sender in hex format`
     );
   }
-  return new AptosAccount(
-    new Uint8Array(Buffer.from(networks.get(network)!.key!, "hex"))
-  );
+  return new AptosAccount(new Uint8Array(Buffer.from(key, "hex")));
 }
+
 async function executeTransaction(
   network: string,
   txPayload: TxnBuilderTypes.TransactionPayloadEntryFunction
 ) {
-  const client = new AptosClient(networks.get(network)!.endpoint);
-  const sender = getSender(network);
+  const endpoint = (DefaultStore.chains[network] as AptosChain).rpcUrl;
+  const client = new AptosClient(endpoint);
+  const sender = getSender();
   console.log(
     await client.generateSignSubmitWaitForTransaction(sender, txPayload, {
       maxGasAmount: BigInt(30000),
@@ -528,6 +495,28 @@ function serializePackage(p: Package): PackageBCS {
   };
 }
 
+function createDeployDerivedTransaction(
+  packageDir: string,
+  deployer: string,
+  seed: string,
+  namedAddresses: string
+) {
+  const artifact = serializePackage(buildPackage(packageDir, namedAddresses));
+
+  return new TxnBuilderTypes.TransactionPayloadEntryFunction(
+    TxnBuilderTypes.EntryFunction.natural(
+      deployer + "::deployer",
+      "deploy_derived",
+      [],
+      [
+        artifact.meta,
+        artifact.bytecodes,
+        BCS.bcsSerializeBytes(Buffer.from(seed, "ascii")),
+      ]
+    )
+  );
+}
+
 function hex(x: string): string {
   return ethers.utils.hexlify(x, { allowMissingPrefix: true });
 }

+ 16 - 0
target_chains/ethereum/contracts/networks/336.json

@@ -0,0 +1,16 @@
+[
+  {
+    "contractName": "Migrations",
+    "address": "0xf5BBe9558F4Bf37F1eB82fb2CEdb1C775FA56832"
+  },
+  {
+    "contractName": "WormholeReceiver",
+    "address": "0x8250f4aF4B972684F7b336503E2D6dFeDeB1487a",
+    "transactionHash": "0x9e86ade70d9383cb16171450d6047f60d9df8061a165988abd52735e66d35126"
+  },
+  {
+    "contractName": "PythUpgradable",
+    "address": "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729",
+    "transactionHash": "0xc5a932f9bf50032493843be32939929f6bd9ec848ff8aa52f91489d31f403145"
+  }
+]