Skip to content

Commit f410820

Browse files
authored
refactor(contract_manager): add docs to ton upgrade + use parseUrl (#2160)
1 parent 868e7fe commit f410820

File tree

4 files changed

+66
-53
lines changed

4 files changed

+66
-53
lines changed

contract_manager/scripts/upgrade_ton_contract.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,17 @@ import fs from "fs";
55
import path from "path";
66
import { Cell } from "@ton/ton";
77

8+
// This script upgrades the Pyth contract on TON after the governance has authorized the upgrade
9+
// If you are starting over, the process is like the following:
10+
// 1. create a governance proposal using generate_upgrade_ton_contract_proposal script
11+
// 2. once approved and executed, relay it to TON using sync_governance_vaas script
12+
// 3. upgrade the contract on TON using this script
813
const parser = yargs(hideBin(process.argv))
914
.usage(
1015
"Upgrades the Pyth contract on TON and creates a governance proposal for it.\n" +
11-
"Usage: $0 --network <mainnet|testnet> --contract <contract_name> --private-key <private_key>"
16+
"Usage: $0 --contract <contract_name> --private-key <private_key>"
1217
)
1318
.options({
14-
network: {
15-
type: "string",
16-
choices: ["mainnet", "testnet"],
17-
description: "Network to deploy to",
18-
demandOption: true,
19-
},
2019
contract: {
2120
type: "string",
2221
description: "Contract name",

contract_manager/src/chains.ts

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,26 @@ import {
3737
import { keyPairFromSeed } from "@ton/crypto";
3838
import { PythContract } from "@pythnetwork/pyth-ton-js";
3939

40+
/**
41+
* Returns the chain rpc url with any environment variables replaced or throws an error if any are missing
42+
*/
43+
export function parseRpcUrl(rpcUrl: string): string {
44+
const envMatches = rpcUrl.match(/\$ENV_\w+/);
45+
if (envMatches) {
46+
for (const envMatch of envMatches) {
47+
const envName = envMatch.replace("$ENV_", "");
48+
const envValue = process.env[envName];
49+
if (!envValue) {
50+
throw new Error(
51+
`Missing env variable ${envName} required for this RPC: ${rpcUrl}`
52+
);
53+
}
54+
rpcUrl = rpcUrl.replace(envMatch, envValue);
55+
}
56+
}
57+
return rpcUrl;
58+
}
59+
4060
export type ChainConfig = Record<string, string> & {
4161
mainnet: boolean;
4262
id: string;
@@ -352,23 +372,10 @@ export class EvmChain extends Chain {
352372
}
353373

354374
/**
355-
* Returns the chain rpc url with any environment variables replaced or throws an error if any are missing
375+
* Returns a web3 provider for this chain
356376
*/
357-
getRpcUrl(): string {
358-
const envMatches = this.rpcUrl.match(/\$ENV_\w+/);
359-
if (envMatches) {
360-
for (const envMatch of envMatches) {
361-
const envName = envMatch.replace("$ENV_", "");
362-
const envValue = process.env[envName];
363-
if (!envValue) {
364-
throw new Error(
365-
`Missing env variable ${envName} required for chain ${this.id} rpc: ${this.rpcUrl}`
366-
);
367-
}
368-
this.rpcUrl = this.rpcUrl.replace(envMatch, envValue);
369-
}
370-
}
371-
return this.rpcUrl;
377+
getWeb3(): Web3 {
378+
return new Web3(parseRpcUrl(this.rpcUrl));
372379
}
373380

374381
/**
@@ -419,7 +426,7 @@ export class EvmChain extends Chain {
419426
}
420427

421428
async getGasPrice() {
422-
const web3 = new Web3(this.getRpcUrl());
429+
const web3 = this.getWeb3();
423430
let gasPrice = await web3.eth.getGasPrice();
424431
// some testnets have inaccuarte gas prices that leads to transactions not being mined, we double it since it's free!
425432
if (!this.isMainnet()) {
@@ -459,7 +466,7 @@ export class EvmChain extends Chain {
459466
gasMultiplier = 1,
460467
gasPriceMultiplier = 1
461468
): Promise<string> {
462-
const web3 = new Web3(this.getRpcUrl());
469+
const web3 = this.getWeb3();
463470
const signer = web3.eth.accounts.privateKeyToAccount(privateKey);
464471
web3.eth.accounts.wallet.add(signer);
465472
const contract = new web3.eth.Contract(abi);
@@ -494,13 +501,13 @@ export class EvmChain extends Chain {
494501
}
495502

496503
async getAccountAddress(privateKey: PrivateKey): Promise<string> {
497-
const web3 = new Web3(this.getRpcUrl());
504+
const web3 = this.getWeb3();
498505
const signer = web3.eth.accounts.privateKeyToAccount(privateKey);
499506
return signer.address;
500507
}
501508

502509
async getAccountBalance(privateKey: PrivateKey): Promise<number> {
503-
const web3 = new Web3(this.getRpcUrl());
510+
const web3 = this.getWeb3();
504511
const balance = await web3.eth.getBalance(
505512
await this.getAccountAddress(privateKey)
506513
);
@@ -757,15 +764,19 @@ export class TonChain extends Chain {
757764
mainnet: boolean,
758765
wormholeChainName: string,
759766
nativeToken: TokenId | undefined,
760-
public rpcUrl: string
767+
private rpcUrl: string
761768
) {
762769
super(id, mainnet, wormholeChainName, nativeToken);
763770
}
764771

765772
async getClient(): Promise<TonClient> {
766-
// add apiKey if facing rate limit
773+
// We are hacking rpcUrl to include the apiKey header which is a
774+
// header that is used to bypass rate limits on the TON network
775+
const [rpcUrl, apiKey] = parseRpcUrl(this.rpcUrl).split("#");
776+
767777
const client = new TonClient({
768-
endpoint: this.rpcUrl,
778+
endpoint: rpcUrl,
779+
apiKey,
769780
});
770781
return client;
771782
}

contract_manager/src/contracts/evm.ts

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,9 @@ import {
2323
* with the deployedCode property generated by truffle builds
2424
*/
2525
export async function getCodeDigestWithoutAddress(
26-
rpcUrl: string,
26+
web3: Web3,
2727
address: string
2828
): Promise<string> {
29-
const web3 = new Web3(rpcUrl);
3029
const code = await web3.eth.getCode(address);
3130
const strippedCode = code.replaceAll(
3231
address.toLowerCase().replace("0x", ""),
@@ -70,7 +69,7 @@ export class EvmWormholeContract extends WormholeContract {
7069
super();
7170
}
7271
getContract(): Contract {
73-
const web3 = new Web3(this.chain.getRpcUrl());
72+
const web3 = this.chain.getWeb3();
7473
return new web3.eth.Contract(WORMHOLE_ABI, this.address);
7574
}
7675

@@ -99,7 +98,7 @@ export class EvmWormholeContract extends WormholeContract {
9998
}
10099

101100
async upgradeGuardianSets(senderPrivateKey: PrivateKey, vaa: Buffer) {
102-
const web3 = new Web3(this.chain.getRpcUrl());
101+
const web3 = this.chain.getWeb3();
103102
const { address } = web3.eth.accounts.wallet.add(senderPrivateKey);
104103
const wormholeContract = new web3.eth.Contract(WORMHOLE_ABI, this.address);
105104
const transactionObject = wormholeContract.methods.submitNewGuardianSet(
@@ -222,7 +221,7 @@ export class EvmEntropyContract extends Storable {
222221
): Promise<Buffer> {
223222
// Executor contract is the owner of entropy contract
224223
const executorAddr = await this.getOwner();
225-
const web3 = new Web3(this.chain.getRpcUrl());
224+
const web3 = this.chain.getWeb3();
226225
const executor = new web3.eth.Contract(EXECUTOR_ABI, executorAddr);
227226
const data = executor.methods.upgradeTo(newImplementation).encodeABI();
228227
return this.chain.generateExecutorPayload(executorAddr, executorAddr, data);
@@ -252,7 +251,7 @@ export class EvmEntropyContract extends Storable {
252251
}
253252

254253
getContract() {
255-
const web3 = new Web3(this.chain.getRpcUrl());
254+
const web3 = this.chain.getWeb3();
256255
return new web3.eth.Contract(EXTENDED_ENTROPY_ABI, this.address);
257256
}
258257

@@ -327,7 +326,7 @@ export class EvmEntropyContract extends Storable {
327326
sequenceNumber: number,
328327
senderPrivateKey: PrivateKey
329328
) {
330-
const web3 = new Web3(this.chain.getRpcUrl());
329+
const web3 = this.chain.getWeb3();
331330
// can not use `this.getContract()` because it uses another web3 instance without the wallet
332331
const contract = new web3.eth.Contract(EXTENDED_ENTROPY_ABI, this.address);
333332
const { address } = web3.eth.accounts.wallet.add(senderPrivateKey);
@@ -343,7 +342,7 @@ export class EvmEntropyContract extends Storable {
343342
}
344343

345344
generateUserRandomNumber() {
346-
const web3 = new Web3(this.chain.getRpcUrl());
345+
const web3 = this.chain.getWeb3();
347346
return web3.utils.randomHex(32);
348347
}
349348

@@ -358,7 +357,7 @@ export class EvmEntropyContract extends Storable {
358357
senderPrivateKey: PrivateKey,
359358
withCallback?: boolean
360359
) {
361-
const web3 = new Web3(this.chain.getRpcUrl());
360+
const web3 = this.chain.getWeb3();
362361
const userCommitment = web3.utils.keccak256(userRandomNumber);
363362
const contract = new web3.eth.Contract(EXTENDED_ENTROPY_ABI, this.address);
364363
const fee = await contract.methods.getFee(provider).call();
@@ -392,7 +391,7 @@ export class EvmEntropyContract extends Storable {
392391
sequenceNumber: string,
393392
senderPrivateKey: PrivateKey
394393
) {
395-
const web3 = new Web3(this.chain.getRpcUrl());
394+
const web3 = this.chain.getWeb3();
396395
const contract = new web3.eth.Contract(EXTENDED_ENTROPY_ABI, this.address);
397396
const { address } = web3.eth.accounts.wallet.add(senderPrivateKey);
398397
const transactionObject = contract.methods.reveal(
@@ -486,7 +485,7 @@ export class EvmExpressRelayContract extends Storable {
486485
}
487486

488487
getContract() {
489-
const web3 = new Web3(this.chain.getRpcUrl());
488+
const web3 = this.chain.getWeb3();
490489
return new web3.eth.Contract(EXPRESS_RELAY_ABI, this.address);
491490
}
492491
}
@@ -499,7 +498,7 @@ export class EvmExecutorContract {
499498
}
500499

501500
async getWormholeContract(): Promise<EvmWormholeContract> {
502-
const web3 = new Web3(this.chain.getRpcUrl());
501+
const web3 = this.chain.getWeb3();
503502
//Unfortunately, there is no public method to get the wormhole address
504503
//Found 251 by using `forge build --extra-output storageLayout` and finding the slot for the wormhole variable.
505504
let address = await web3.eth.getStorageAt(this.address, 251);
@@ -508,7 +507,7 @@ export class EvmExecutorContract {
508507
}
509508

510509
getContract() {
511-
const web3 = new Web3(this.chain.getRpcUrl());
510+
const web3 = this.chain.getWeb3();
512511
return new web3.eth.Contract(EXECUTOR_ABI, this.address);
513512
}
514513

@@ -542,7 +541,7 @@ export class EvmExecutorContract {
542541
senderPrivateKey: PrivateKey,
543542
vaa: Buffer
544543
) {
545-
const web3 = new Web3(this.chain.getRpcUrl());
544+
const web3 = this.chain.getWeb3();
546545
const { address } = web3.eth.accounts.wallet.add(senderPrivateKey);
547546
const executorContract = new web3.eth.Contract(EXECUTOR_ABI, this.address);
548547
const transactionObject = executorContract.methods.execute(
@@ -589,7 +588,7 @@ export class EvmPriceFeedContract extends PriceFeedContract {
589588
}
590589

591590
getContract() {
592-
const web3 = new Web3(this.chain.getRpcUrl());
591+
const web3 = this.chain.getWeb3();
593592
const pythContract = new web3.eth.Contract(EXTENDED_PYTH_ABI, this.address);
594593
return pythContract;
595594
}
@@ -599,12 +598,12 @@ export class EvmPriceFeedContract extends PriceFeedContract {
599598
*/
600599
async getCode(): Promise<string> {
601600
// TODO: handle proxy contracts
602-
const web3 = new Web3(this.chain.getRpcUrl());
601+
const web3 = this.chain.getWeb3();
603602
return web3.eth.getCode(this.address);
604603
}
605604

606605
async getImplementationAddress(): Promise<string> {
607-
const web3 = new Web3(this.chain.getRpcUrl());
606+
const web3 = this.chain.getWeb3();
608607
// bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) according to EIP-1967
609608
const storagePosition =
610609
"0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
@@ -617,11 +616,11 @@ export class EvmPriceFeedContract extends PriceFeedContract {
617616
* Returns the keccak256 digest of the contract bytecode
618617
*/
619618
async getCodeDigestWithoutAddress(): Promise<string> {
620-
return getCodeDigestWithoutAddress(this.chain.getRpcUrl(), this.address);
619+
return getCodeDigestWithoutAddress(this.chain.getWeb3(), this.address);
621620
}
622621

623622
async getTotalFee(): Promise<TokenQty> {
624-
const web3 = new Web3(this.chain.getRpcUrl());
623+
const web3 = this.chain.getWeb3();
625624
const amount = BigInt(await web3.eth.getBalance(this.address));
626625
return {
627626
amount,
@@ -712,7 +711,7 @@ export class EvmPriceFeedContract extends PriceFeedContract {
712711
}
713712

714713
async executeUpdatePriceFeed(senderPrivateKey: PrivateKey, vaas: Buffer[]) {
715-
const web3 = new Web3(this.chain.getRpcUrl());
714+
const web3 = this.chain.getWeb3();
716715
const { address } = web3.eth.accounts.wallet.add(senderPrivateKey);
717716
const pythContract = new web3.eth.Contract(EXTENDED_PYTH_ABI, this.address);
718717
const priceFeedUpdateData = vaas.map((vaa) => "0x" + vaa.toString("hex"));
@@ -732,7 +731,7 @@ export class EvmPriceFeedContract extends PriceFeedContract {
732731
senderPrivateKey: PrivateKey,
733732
vaa: Buffer
734733
) {
735-
const web3 = new Web3(this.chain.getRpcUrl());
734+
const web3 = this.chain.getWeb3();
736735
const { address } = web3.eth.accounts.wallet.add(senderPrivateKey);
737736
const pythContract = new web3.eth.Contract(EXTENDED_PYTH_ABI, this.address);
738737
const transactionObject = pythContract.methods.executeGovernanceInstruction(
Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
- id: ton_testnet
22
wormholeChainName: ton_testnet
33
mainnet: false
4-
rpcUrl: https://testnet.toncenter.com/api/v2/jsonRPC
4+
# using # is a hack so we can later separate the key from the URL and pass
5+
# it as a header
6+
rpcUrl: https://testnet.toncenter.com/api/v2/#$ENV_TON_TESTNET_API_KEY
57
type: TonChain
68
- id: ton_mainnet
79
wormholeChainName: ton_mainnet
810
mainnet: true
9-
rpcUrl: https://toncenter.com/api/v2/jsonRPC
11+
# using # is a hack so we can later separate the key from the URL and pass
12+
# it as a header
13+
rpcUrl: https://toncenter.com/api/v2/jsonRPC#$ENV_TON_MAINNET_API_KEY
1014
type: TonChain

0 commit comments

Comments
 (0)