Skip to content

Commit 9335898

Browse files
committed
feat(contract-manager): add script to fetch account balances
1 parent a22b202 commit 9335898

File tree

6 files changed

+192
-7
lines changed

6 files changed

+192
-7
lines changed

contract_manager/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"@pythnetwork/price-service-client": "*",
2727
"@pythnetwork/pyth-sui-js": "*",
2828
"@injectivelabs/networks": "1.0.68",
29+
"aptos": "^1.5.0",
2930
"bs58": "^5.0.0",
3031
"ts-node": "^10.9.1",
3132
"typescript": "^4.9.3"
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import yargs from "yargs";
2+
import { hideBin } from "yargs/helpers";
3+
import { DefaultStore, PrivateKey, toPrivateKey } from "../src";
4+
5+
const parser = yargs(hideBin(process.argv))
6+
.usage("Usage: $0 --private-key <private-key> [--chain <chain>]")
7+
.options({
8+
"private-key": {
9+
type: "string",
10+
demandOption: true,
11+
desc: "Private key to use to sign transaction",
12+
},
13+
chain: {
14+
type: "string",
15+
desc: "Chain to get the balance for. If not provided the balance for all chains is returned.",
16+
},
17+
});
18+
19+
type AccountBalance = {
20+
chain: string;
21+
address: string | undefined;
22+
balance: number | undefined;
23+
};
24+
25+
async function getBalance(
26+
chain: string,
27+
privateKey: PrivateKey
28+
): Promise<AccountBalance | undefined> {
29+
const address = await DefaultStore.chains[chain].getAccountAddress(
30+
privateKey
31+
);
32+
33+
try {
34+
const balance = await DefaultStore.chains[chain].getAccountBalance(
35+
privateKey
36+
);
37+
return { chain, address, balance };
38+
} catch (e) {
39+
console.error(`Error fetching balance for ${chain}`, e);
40+
}
41+
return { chain, address, balance: undefined };
42+
}
43+
44+
async function main() {
45+
const argv = await parser.argv;
46+
const chains = argv.chain
47+
? [argv.chain]
48+
: Object.keys(DefaultStore.chains).filter((chain) => chain !== "global");
49+
50+
const privateKey = toPrivateKey(argv["private-key"]);
51+
52+
const balances = await Promise.all(
53+
chains.map((chain) => getBalance(chain, privateKey))
54+
);
55+
56+
console.table(balances);
57+
}
58+
59+
main();

contract_manager/src/chains.ts

Lines changed: 100 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,20 @@ import {
1212
DataSource,
1313
EvmSetWormholeAddress,
1414
} from "xc_admin_common";
15-
import { AptosClient } from "aptos";
15+
import { AptosClient, AptosAccount, CoinClient } from "aptos";
1616
import Web3 from "web3";
1717
import {
1818
CosmwasmExecutor,
1919
CosmwasmQuerier,
2020
InjectiveExecutor,
2121
} from "@pythnetwork/cosmwasm-deploy-tools";
2222
import { Network } from "@injectivelabs/networks";
23+
import {
24+
Connection,
25+
Ed25519Keypair,
26+
JsonRpcProvider,
27+
RawSigner,
28+
} from "@mysten/sui.js";
2329

2430
export type ChainConfig = Record<string, string> & {
2531
mainnet: boolean;
@@ -96,19 +102,44 @@ export abstract class Chain extends Storable {
96102
* @param upgradeInfo based on the contract type, this can be a contract address, codeId, package digest, etc.
97103
*/
98104
abstract generateGovernanceUpgradePayload(upgradeInfo: unknown): Buffer;
105+
106+
/**
107+
* Returns the account address associated with the given private key.
108+
* @param privateKey the account private key
109+
*/
110+
abstract getAccountAddress(privateKey: PrivateKey): Promise<string>;
111+
112+
/**
113+
* Returns the balance of the account associated with the given private key.
114+
* @param privateKey the account private key
115+
*/
116+
abstract getAccountBalance(privateKey: PrivateKey): Promise<number>;
99117
}
100118

119+
/**
120+
* A Chain object that represents all chains. This is used for governance instructions that apply to all chains.
121+
* For example, governance instructions to upgrade Pyth data sources.
122+
*/
101123
export class GlobalChain extends Chain {
102124
static type = "GlobalChain";
103125
constructor() {
104126
super("global", true, "unset");
105127
}
128+
106129
generateGovernanceUpgradePayload(): Buffer {
107130
throw new Error(
108131
"Can not create a governance message for upgrading contracts on all chains!"
109132
);
110133
}
111134

135+
async getAccountAddress(_privateKey: PrivateKey): Promise<string> {
136+
throw new Error("Can not get account for GlobalChain.");
137+
}
138+
139+
async getAccountBalance(_privateKey: PrivateKey): Promise<number> {
140+
throw new Error("Can not get account balance for GlobalChain.");
141+
}
142+
112143
getType(): string {
113144
return GlobalChain.type;
114145
}
@@ -177,7 +208,9 @@ export class CosmWasmChain extends Chain {
177208
return new CosmosUpgradeContract(this.wormholeChainName, codeId).encode();
178209
}
179210

180-
async getExecutor(privateKey: PrivateKey) {
211+
async getExecutor(
212+
privateKey: PrivateKey
213+
): Promise<CosmwasmExecutor | InjectiveExecutor> {
181214
if (this.getId().indexOf("injective") > -1) {
182215
return InjectiveExecutor.fromPrivateKey(
183216
this.isMainnet() ? Network.Mainnet : Network.Testnet,
@@ -190,6 +223,20 @@ export class CosmWasmChain extends Chain {
190223
this.gasPrice + this.feeDenom
191224
);
192225
}
226+
227+
async getAccountAddress(privateKey: PrivateKey): Promise<string> {
228+
const executor = await this.getExecutor(privateKey);
229+
if (executor instanceof InjectiveExecutor) {
230+
return executor.getAddress();
231+
} else {
232+
return await executor.getAddress();
233+
}
234+
}
235+
236+
async getAccountBalance(privateKey: PrivateKey): Promise<number> {
237+
const executor = await this.getExecutor(privateKey);
238+
return await executor.getBalance();
239+
}
193240
}
194241

195242
export class SuiChain extends Chain {
@@ -238,6 +285,27 @@ export class SuiChain extends Chain {
238285
digest
239286
).encode();
240287
}
288+
289+
getProvider(): JsonRpcProvider {
290+
return new JsonRpcProvider(new Connection({ fullnode: this.rpcUrl }));
291+
}
292+
293+
async getAccountAddress(privateKey: PrivateKey): Promise<string> {
294+
const provider = this.getProvider();
295+
const keypair = Ed25519Keypair.fromSecretKey(
296+
Buffer.from(privateKey, "hex")
297+
);
298+
const wallet = new RawSigner(keypair, provider);
299+
return await wallet.getAddress();
300+
}
301+
302+
async getAccountBalance(privateKey: PrivateKey): Promise<number> {
303+
const provider = this.getProvider();
304+
const balance = await provider.getBalance({
305+
owner: await this.getAccountAddress(privateKey),
306+
});
307+
return Number(balance.totalBalance) / 10 ** 9;
308+
}
241309
}
242310

243311
export class EvmChain extends Chain {
@@ -361,6 +429,20 @@ export class EvmChain extends Chain {
361429
});
362430
return deployedContract.options.address;
363431
}
432+
433+
async getAccountAddress(privateKey: PrivateKey): Promise<string> {
434+
const web3 = new Web3(this.getRpcUrl());
435+
const signer = web3.eth.accounts.privateKeyToAccount(privateKey);
436+
return signer.address;
437+
}
438+
439+
async getAccountBalance(privateKey: PrivateKey): Promise<number> {
440+
const web3 = new Web3(this.getRpcUrl());
441+
const balance = await web3.eth.getBalance(
442+
await this.getAccountAddress(privateKey)
443+
);
444+
return Number(balance) / 10 ** 18;
445+
}
364446
}
365447

366448
export class AptosChain extends Chain {
@@ -413,4 +495,20 @@ export class AptosChain extends Chain {
413495
parsed.rpcUrl
414496
);
415497
}
498+
499+
async getAccountAddress(privateKey: PrivateKey): Promise<string> {
500+
const account = new AptosAccount(
501+
new Uint8Array(Buffer.from(privateKey, "hex"))
502+
);
503+
return account.address().toString();
504+
}
505+
506+
async getAccountBalance(privateKey: PrivateKey): Promise<number> {
507+
const client = this.getClient();
508+
const account = new AptosAccount(
509+
new Uint8Array(Buffer.from(privateKey, "hex"))
510+
);
511+
const coinClient = new CoinClient(client);
512+
return Number(await coinClient.checkBalance(account)) / 10 ** 8;
513+
}
416514
}

contract_manager/src/contracts/sui.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import {
2-
Connection,
32
Ed25519Keypair,
4-
JsonRpcProvider,
53
ObjectId,
64
RawSigner,
75
SUI_CLOCK_OBJECT_ID,
@@ -377,7 +375,7 @@ export class SuiContract extends Contract {
377375
}
378376

379377
getProvider() {
380-
return new JsonRpcProvider(new Connection({ fullnode: this.chain.rpcUrl }));
378+
return this.chain.getProvider();
381379
}
382380

383381
private async getStateFields() {

target_chains/cosmwasm/tools/src/chains-manager/cosmwasm.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
UpdateContractAdminResponse,
1919
} from "./chain-executor";
2020
import {
21+
CosmWasmClient,
2122
DeliverTxResponse,
2223
MsgExecuteContractEncodeObject,
2324
MsgInstantiateContractEncodeObject,
@@ -63,7 +64,20 @@ export class CosmwasmExecutor implements ChainExecutor {
6364
);
6465
}
6566

66-
private async getAddress(): Promise<string> {
67+
async getBalance(): Promise<number> {
68+
const address = (await this.signer.getAccounts())[0].address;
69+
const cosmwasmClient = await CosmWasmClient.connect(this.endpoint);
70+
71+
// We are interested only in the coin that we pay gas fees in.
72+
const denom = GasPrice.fromString(this.gasPrice).denom;
73+
const balance = await cosmwasmClient.getBalance(address, denom);
74+
75+
// By default the coins have 6 decimal places in CosmWasm
76+
// and the denom is usually `u<chain>`.
77+
return Number(balance.amount) / 10 ** 6;
78+
}
79+
80+
async getAddress(): Promise<string> {
6781
return (await this.signer.getAccounts())[0].address;
6882
}
6983

target_chains/cosmwasm/tools/src/chains-manager/injective.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {
1010
TxGrpcClient,
1111
TxResponse,
1212
createTransactionFromMsg,
13+
GrpcAccountPortfolio,
14+
ChainGrpcBankApi,
1315
} from "@injectivelabs/sdk-ts";
1416
import {
1517
ChainExecutor,
@@ -53,10 +55,23 @@ export class InjectiveExecutor implements ChainExecutor {
5355
return new InjectiveExecutor(network, wallet);
5456
}
5557

56-
private getAddress(): string {
58+
getAddress(): string {
5759
return this.wallet.toBech32();
5860
}
5961

62+
async getBalance(): Promise<number> {
63+
const endpoints = getNetworkEndpoints(this.network);
64+
65+
const chainGrpcAuthApi = new ChainGrpcBankApi(endpoints.grpc);
66+
67+
const balance = await chainGrpcAuthApi.fetchBalance({
68+
accountAddress: this.getAddress(),
69+
denom: "inj",
70+
});
71+
72+
return Number(balance.amount) / 10 ** 18;
73+
}
74+
6075
private async signAndBroadcastMsg(msg: Msgs): Promise<TxResponse> {
6176
const networkInfo = getNetworkInfo(this.network);
6277
const endpoints = getNetworkEndpoints(this.network);

0 commit comments

Comments
 (0)