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

feat(near): contract manager integration

Reisen 2 éve
szülő
commit
dd56ac64f6

+ 10 - 0
contract_manager/README.md

@@ -7,3 +7,13 @@ It has the following structure:
 - `store` contains all the necessary information for registered chains and deployed contracts
 - `scripts` contains utility scripts to interact with the contract manager and accomplish common tasks
 - `src` contains the contract manager code
+
+
+## Guide to add new Chain
+
+Adding a new chain type to the contract manager can be done like so:
+
+1. Add a new Chain type to `src/chains.ts` extending `Chain`.
+2. Add a new Contract under `src/contracts/`. See another contract for reference.
+3. Update `shell.ts` and `store.ts` with your new Chain type.
+4. Add scripts that use the new contract under `scripts/` for useful tasks.

+ 1 - 0
contract_manager/package.json

@@ -28,6 +28,7 @@
     "@injectivelabs/networks": "1.0.68",
     "aptos": "^1.5.0",
     "bs58": "^5.0.0",
+    "near-api-js",
     "ts-node": "^10.9.1",
     "typescript": "^4.9.3"
   },

+ 48 - 0
contract_manager/scripts/upgrade_near.ts

@@ -0,0 +1,48 @@
+import yargs from "yargs";
+import { hideBin } from "yargs/helpers";
+import { NearChain } from "../src/chains";
+import { NearContract } from "../src/contracts/near";
+import { DefaultStore } from "../src/store";
+
+const parser = yargs(hideBin(process.argv))
+  .scriptName("deploy_near.ts")
+  .usage(
+    "Usage: $0 --code <path/to/artifact.wasm> --private-key <private-key> --chain <chain>"
+  )
+  .options({
+    code: {
+      type: "string",
+      demandOption: true,
+      desc: "Path to the artifact .wasm file",
+    },
+    "private-key": {
+      type: "string",
+      demandOption: true,
+      desc: "Private key to use for the deployment",
+    },
+    chain: {
+      type: "string",
+      demandOption: true,
+      desc: "Chain to upload the code on. Can be one of the chains available in the store",
+    },
+    wormholeContract: {
+      type: "string",
+      demandOption: true,
+      desc: "Wormhole contract address deployed on this chain",
+    },
+  });
+
+async function main() {
+  const argv = await parser.argv;
+  const { code, wormholeContract } = argv;
+  console.log(
+    await CosmWasmContract.deploy(
+      DefaultStore.chains[argv.chain] as CosmWasmChain,
+      wormholeContract,
+      argv["private-key"],
+      code
+    )
+  );
+}
+
+main();

+ 49 - 0
contract_manager/src/chains.ts

@@ -510,3 +510,52 @@ export class AptosChain extends Chain {
     return Number(await coinClient.checkBalance(account)) / 10 ** 8;
   }
 }
+
+/** NEAR **/
+export class NearChain extends Chain {
+  static type = "NearChain";
+
+  constructor(
+    id: string,
+    mainnet: boolean,
+    wormholeChainName: string,
+    public rpcUrl: string
+  ) {
+    super(id, mainnet, wormholeChainName);
+  }
+
+  /**
+   * Returns the payload for a governance contract upgrade instruction for contracts deployed on this chain
+   * @param digest hex string of the 32 byte sha256 digest for the new code without the 0x prefix
+   */
+  generateGovernanceUpgradePayload(digest: string): Buffer {
+    return new NearAuthorizeUpgradeContract(
+      this.wormholeChainName,
+      digest
+    ).encode();
+  }
+
+  getType(): string {
+    return NearChain.type;
+  }
+
+  toJson(): KeyValueConfig {
+    return {
+      id: this.id,
+      wormholeChainName: this.wormholeChainName,
+      mainnet: this.mainnet,
+      rpcUrl: this.rpcUrl,
+      type: NearChain.type,
+    };
+  }
+
+  static fromJson(parsed: ChainConfig): NearChain {
+    if (parsed.type !== NearChain.type) throw new Error("Invalid type");
+    return new NearChain(
+      parsed.id,
+      parsed.mainnet,
+      parsed.wormholeChainName,
+      parsed.rpcUrl
+    );
+  }
+}

+ 75 - 0
contract_manager/src/contracts/near.ts

@@ -0,0 +1,75 @@
+import { Contract, PriceFeed, PrivateKey, TxResult } from "../base";
+import { ApiError, AptosAccount, BCS, TxnBuilderTypes } from "aptos";
+import { connect, KeyPair, keyStores, Contract } from "near-api-js";
+import { DataSource } from "xc_admin_common";
+
+export class NearContract extends Contract {
+  static type = "NearContract";
+
+  /**
+   * Given the ids of the pyth state and wormhole state, create a new NearContract
+   * The package ids are derived based on the state ids
+   *
+   * @param chain the chain which this contract is deployed on
+   * @param stateId id of the pyth state for the deployed contract
+   * @param wormholeStateId id of the wormhole state for the wormhole contract that pyth binds to
+   */
+  constructor(
+    public chain: NearChain,
+    public stateId: string,
+    public wormholeStateId: string
+  ) {
+    super();
+  }
+
+  static fromJson(
+    chain: Chain,
+    parsed: { type: string; stateId: string; wormholeStateId: string }
+  ): NearContract {
+    if (parsed.type !== NearContract.type) throw new Error("Invalid type");
+    if (!(chain instanceof NearChain))
+      throw new Error(`Wrong chain type ${chain}`);
+    return new NearContract(chain, parsed.stateId, parsed.wormholeStateId);
+  }
+
+  // TODO: Where should I implement the wallet details?
+  async executeGovernanceInstruction(
+    senderPrivateKey: PrivateKey,
+    vaa: Buffer
+  ) {
+    // VAA as Hex-Encoded String
+    const hex = vaa.toString("hex");
+
+    const conn = await connect({
+      networkId: "testnet",
+      keyStore: new keyStores.InMemoryKeyStore(),
+      nodeUrl: "https://rpc.testnet.near.org",
+    });
+
+    const account = conn.account();
+    const contract = new Contract(
+      account,
+      "pyth.testnet",
+      { changeMethods: ["execute_governance_instruction"] }
+    );
+    
+     return await contract.execute_governance_instruction({
+      gas: "300000000000000",
+      args: { vaa: hex },
+      amount: "0",
+    );
+  }
+
+  getChain(): NearChain {
+    return this.chain;
+  }
+
+  toJson() {
+    return {
+      chain: this.chain.getId(),
+      stateId: this.stateId,
+      wormholeStateId: this.wormholeStateId,
+      type: NearContract.type,
+    };
+  }
+}

+ 1 - 0
contract_manager/src/shell.ts

@@ -11,6 +11,7 @@ repl.evalCode(
     "import { WormholeCosmWasmContract, CosmWasmContract } from './src/contracts/cosmwasm';" +
     "import { WormholeEvmContract, EvmContract } from './src/contracts/evm';" +
     "import { AptosContract } from './src/contracts/aptos';" +
+    "import { NearContract } from './src/contracts/near';" +
     "import { DefaultStore } from './src/store';" +
     "import { toPrivateKey } from './src/base';" +
     "DefaultStore"

+ 4 - 0
contract_manager/src/store.ts

@@ -4,12 +4,14 @@ import {
   CosmWasmChain,
   EvmChain,
   GlobalChain,
+  NearChain,
   SuiChain,
 } from "./chains";
 import {
   AptosContract,
   CosmWasmContract,
   EvmContract,
+  NearContract, 
   SuiContract,
 } from "./contracts";
 import { Contract } from "./base";
@@ -58,6 +60,7 @@ export class Store {
       [SuiChain.type]: SuiChain,
       [EvmChain.type]: EvmChain,
       [AptosChain.type]: AptosChain,
+      [NearChain.type]: NearChain,
     };
 
     this.getYamlFiles(`${this.path}/chains/`).forEach((yamlFile) => {
@@ -110,6 +113,7 @@ export class Store {
       [SuiContract.type]: SuiContract,
       [EvmContract.type]: EvmContract,
       [AptosContract.type]: AptosContract,
+      [NearContract.type]: NearContract,
     };
     this.getYamlFiles(`${this.path}/contracts/`).forEach((yamlFile) => {
       const parsedArray = parse(readFileSync(yamlFile, "utf-8"));