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
This commit is contained in:
Amin Moghaddam 2024-01-11 12:35:52 +01:00 committed by GitHub
parent 7cf7420203
commit cee5da93d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 295 additions and 187 deletions

View File

@ -1,4 +1,4 @@
import { KeyValueConfig, PrivateKey, Storable } from "./base"; import { KeyValueConfig, PrivateKey, Storable, TxResult } from "./base";
import { import {
ChainName, ChainName,
SetFee, SetFee,
@ -12,7 +12,7 @@ import {
DataSource, DataSource,
EvmSetWormholeAddress, EvmSetWormholeAddress,
} from "xc_admin_common"; } from "xc_admin_common";
import { AptosClient, AptosAccount, CoinClient } from "aptos"; import { AptosClient, AptosAccount, CoinClient, TxnBuilderTypes } from "aptos";
import Web3 from "web3"; import Web3 from "web3";
import { import {
CosmwasmExecutor, CosmwasmExecutor,
@ -55,6 +55,10 @@ export abstract class Chain extends Storable {
); );
} }
public getWormholeChainId(): number {
return toChainId(this.wormholeChainName);
}
getId(): string { getId(): string {
return this.id; return this.id;
} }
@ -509,4 +513,22 @@ export class AptosChain extends Chain {
const coinClient = new CoinClient(client); const coinClient = new CoinClient(client);
return Number(await coinClient.checkBalance(account)) / 10 ** 8; 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 };
}
} }

View File

@ -1,8 +1,74 @@
import { Contract, PriceFeed, PrivateKey, TxResult } from "../base"; 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 { AptosChain, Chain } from "../chains";
import { DataSource } from "xc_admin_common"; 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 { export class AptosContract extends Contract {
static type = "AptosContract"; static type = "AptosContract";
@ -45,25 +111,11 @@ export class AptosContract extends Contract {
[BCS.bcsSerializeBytes(vaa)] [BCS.bcsSerializeBytes(vaa)]
) )
); );
return this.sendTransaction(senderPrivateKey, txPayload); return this.chain.sendTransaction(senderPrivateKey, txPayload);
} }
private async sendTransaction( public getWormholeContract(): WormholeAptosContract {
senderPrivateKey: PrivateKey, return new WormholeAptosContract(this.chain, this.wormholeStateId);
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 };
} }
async executeUpdatePriceFeed( async executeUpdatePriceFeed(
@ -78,7 +130,7 @@ export class AptosContract extends Contract {
[BCS.serializeVectorWithFunc(vaas, "serializeBytes")] [BCS.serializeVectorWithFunc(vaas, "serializeBytes")]
) )
); );
return this.sendTransaction(senderPrivateKey, txPayload); return this.chain.sendTransaction(senderPrivateKey, txPayload);
} }
getStateResources() { getStateResources() {

View File

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

View File

@ -154,6 +154,19 @@ const WORMHOLE_ABI = [
stateMutability: "view", stateMutability: "view",
type: "function", type: "function",
}, },
{
inputs: [],
name: "chainId",
outputs: [
{
internalType: "uint16",
name: "",
type: "uint16",
},
],
stateMutability: "view",
type: "function",
},
{ {
inputs: [ 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 * Returns an array of guardian addresses used for VAA verification in this contract
*/ */

View File

@ -3,6 +3,12 @@ import { PrivateKey, TxResult } from "../base";
export abstract class WormholeContract { export abstract class WormholeContract {
abstract getCurrentGuardianSetIndex(): Promise<number>; 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 * 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(); const currentIndex = await this.getCurrentGuardianSetIndex();
for (let i = currentIndex; i < MAINNET_UPGRADE_VAAS.length; i++) { for (let i = currentIndex; i < MAINNET_UPGRADE_VAAS.length; i++) {
const vaa = MAINNET_UPGRADE_VAAS[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 // make sure the upgrade is complete before continuing
while ((await this.getCurrentGuardianSetIndex()) <= i) { while ((await this.getCurrentGuardianSetIndex()) <= i) {
await new Promise((resolve) => setTimeout(resolve, 5000)); await new Promise((resolve) => setTimeout(resolve, 5000));

View File

@ -10,7 +10,7 @@ repl.evalCode(
"import { SuiContract } from './src/contracts/sui';" + "import { SuiContract } from './src/contracts/sui';" +
"import { WormholeCosmWasmContract, CosmWasmContract } from './src/contracts/cosmwasm';" + "import { WormholeCosmWasmContract, CosmWasmContract } from './src/contracts/cosmwasm';" +
"import { WormholeEvmContract, EvmContract } from './src/contracts/evm';" + "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 { DefaultStore } from './src/store';" +
"import { toPrivateKey } from './src/base';" + "import { toPrivateKey } from './src/base';" +
"DefaultStore" "DefaultStore"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,8 +2,7 @@
Install aptos cli with the same version specified in the ci workflows. 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. All the commands which submit transactions require an environment variable `APTOS_PRIVATE_KEY` 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`.
# Deploying from scratch # 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: Assuming the wormhole and deployer contracts are already deployed, we can deploy the pyth oracle with the following command:
```bash ```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. `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 ### 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 ```bash
npm run cli init-pyth -- <seed> -n testnet \ npm run cli init-pyth -- <seed> -n <network> \
--stale-price-threshold 60 \ --stale-price-threshold 60 \
--update-fee 1 \ --update-fee 1 \
--governance-emitter-chain-id 1 \ --channel <stable-or-beta>
--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
``` ```
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: # Upgrade process:
The following steps are needed to upgrade our aptos contracts: The following steps are needed to upgrade our aptos contracts:

View File

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

View File

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