Skip to content

Commit

Permalink
finish up contract manager
Browse files Browse the repository at this point in the history
  • Loading branch information
cctdaniel committed Oct 31, 2024
1 parent 9e32362 commit 1e82c8e
Show file tree
Hide file tree
Showing 12 changed files with 304 additions and 143 deletions.
38 changes: 32 additions & 6 deletions contract_manager/src/chains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,11 @@ import {
WalletContractV4,
ContractProvider,
Address,
OpenedContract,
Sender,
} from "@ton/ton";
import { keyPairFromSecretKey } from "@ton/crypto";
import { keyPairFromSeed } from "@ton/crypto";
import { PythContract } from "@pythnetwork/pyth-ton-js";

export type ChainConfig = Record<string, string> & {
mainnet: boolean;
Expand Down Expand Up @@ -759,21 +762,44 @@ export class TonChain extends Chain {
super(id, mainnet, wormholeChainName, nativeToken);
}

async getProvider(address: string): Promise<ContractProvider> {
async getClient(): Promise<TonClient> {
// add apiKey if facing rate limit
const client = new TonClient({
endpoint: this.rpcUrl,
});
return client;
}

async getContract(address: string): Promise<OpenedContract<PythContract>> {
const client = await this.getClient();
const contract = client.open(
PythContract.createFromAddress(Address.parse(address))
);
return contract;
}

async getContractProvider(address: string): Promise<ContractProvider> {
const client = await this.getClient();
return client.provider(Address.parse(address));
}

async getWallet(privateKey: PrivateKey): Promise<WalletContractV4> {
const keyPair = keyPairFromSecretKey(Buffer.from(privateKey, "hex"));
const wallet = WalletContractV4.create({
const keyPair = keyPairFromSeed(Buffer.from(privateKey, "hex"));
return WalletContractV4.create({
publicKey: keyPair.publicKey,
workchain: 0,
});
}

return wallet;
async getSender(privateKey: PrivateKey): Promise<Sender> {
const client = await this.getClient();
const keyPair = keyPairFromSeed(Buffer.from(privateKey, "hex"));
const wallet = WalletContractV4.create({
publicKey: keyPair.publicKey,
workchain: 0,
});
const provider = client.open(wallet);
return provider.sender(keyPair.secretKey);
}

/**
Expand Down Expand Up @@ -817,7 +843,7 @@ export class TonChain extends Chain {

async getAccountBalance(privateKey: PrivateKey): Promise<number> {
const wallet = await this.getWallet(privateKey);
const provider = await this.getProvider(wallet.address.toString());
const provider = await this.getContractProvider(wallet.address.toString());
const balance = await wallet.getBalance(provider);
return Number(balance) / 10 ** 9;
}
Expand Down
186 changes: 101 additions & 85 deletions contract_manager/src/contracts/ton.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,8 @@ import { WormholeContract } from "./wormhole";
import { PriceFeed, PriceFeedContract, PrivateKey, TxResult } from "../base";
import { TokenQty } from "../token";
import { DataSource } from "@pythnetwork/xc-admin-common";
import {
Address,
Contract,
OpenedContract,
TonClient,
WalletContractV4,
} from "@ton/ton";
import {
PythContract,
PYTH_CONTRACT_ADDRESS_TESTNET,
} from "@pythnetwork/pyth-ton-js";
import { Address, OpenedContract } from "@ton/ton";
import { PythContract } from "@pythnetwork/pyth-ton-js";

export class TonWormholeContract extends WormholeContract {
static type = "TonWormholeContract";
Expand All @@ -22,6 +13,10 @@ export class TonWormholeContract extends WormholeContract {
return `${this.chain.getId()}_${this.address}_${TonWormholeContract.type}`;
}

getChain(): TonChain {
return this.chain;
}

getType(): string {
return TonWormholeContract.type;
}
Expand Down Expand Up @@ -52,45 +47,54 @@ export class TonWormholeContract extends WormholeContract {
super();
}

async getContract(): Promise<OpenedContract<PythContract>> {
const provider = await this.chain.getContractProvider(this.address);
const contract = provider.open(
PythContract.createFromAddress(Address.parse(this.address))
);

return contract;
}

async getCurrentGuardianSetIndex(): Promise<number> {
// const contract = await this.getContract();
// const result = await contract.get("get_current_guardian_set_index");
// return Number(result);
return 1;
const contract = await this.getContract();
const result = await contract.getCurrentGuardianSetIndex();
return result;
}

async getChainId(): Promise<number> {
// const contract = await this.getContract();
// const result = await contract.get("get_chain_id");
// return Number(result);
return 1;
const contract = await this.getContract();
const result = await contract.getChainId();
return Number(result);
}

async getGuardianSet(): Promise<string[]> {
// const contract = await this.getContract();
// const guardianSetIndex = await this.getCurrentGuardianSetIndex();
// const result = await contract.get("get_guardian_set", [guardianSetIndex]);
// return result.map((guardian: string) => `0x${guardian}`);
return ["0x1"];
const contract = await this.getContract();
const guardianSetIndex = await this.getCurrentGuardianSetIndex();
const result = await contract.getGuardianSet(guardianSetIndex);
return result.keys;
}

async upgradeGuardianSets(
senderPrivateKey: PrivateKey,
vaa: Buffer
): Promise<TxResult> {
// const client = await this.chain.getClient(senderPrivateKey);
// const contract = await this.getContract(client);

// const tx = await contract.sendMessage({
// body: {
// op: "update_guardian_set",
// data: vaa,
// },
// value: "0.05", // TON to attach
// });
const contract = await this.getContract();
const provider = await this.chain.getContractProvider(this.address);
const sender = await this.chain.getSender(senderPrivateKey);
const wallet = await this.chain.getWallet(senderPrivateKey);
await contract.sendUpdateGuardianSet(sender, vaa);

// Get recent transactions for this address
const transactions = await provider.getTransactions(
wallet.address,
BigInt(0),
Buffer.alloc(0),
1
);

return {
id: "0x1",
id: transactions[0].hash.toString(),
info: JSON.stringify("0x1"),
};
}
Expand Down Expand Up @@ -118,12 +122,16 @@ export class TonPriceFeedContract extends PriceFeedContract {
return `${this.chain.getId()}_${this.address}_${TonPriceFeedContract.type}`;
}

getChain(): TonChain {
return this.chain;
}

getType(): string {
return TonPriceFeedContract.type;
}

async getContract(): Promise<OpenedContract<PythContract>> {
const provider = await this.chain.getProvider(this.address);
const provider = await this.chain.getContractProvider(this.address);
const contract = provider.open(
PythContract.createFromAddress(Address.parse(this.address))
);
Expand All @@ -132,19 +140,18 @@ export class TonPriceFeedContract extends PriceFeedContract {
}

async getTotalFee(): Promise<TokenQty> {
// const contract = await this.getContract();
// const balance = await contract.getBalance();
const client = await this.chain.getClient();
const balance = await client.getBalance(Address.parse(this.address));
return {
amount: BigInt(0),
amount: balance,
denom: this.chain.getNativeToken(),
};
}

async getLastExecutedGovernanceSequence(): Promise<number> {
// const contract = await this.getContract();
// const result = await contract.get
// return Number(result);
return 1;
const contract = await this.getContract();
const result = await contract.getLastExecutedGovernanceSequence();
return Number(result);
}

async getPriceFeed(feedId: string): Promise<PriceFeed | undefined> {
Expand Down Expand Up @@ -174,8 +181,7 @@ export class TonPriceFeedContract extends PriceFeedContract {
}

async getValidTimePeriod(): Promise<number> {
// const contract = await this.getContract();
// const result = await contract.get("get_valid_time_period");
// Not supported but return 1 because it's required by the abstract class
return 1;
}

Expand All @@ -194,72 +200,82 @@ export class TonPriceFeedContract extends PriceFeedContract {
}

async getDataSources(): Promise<DataSource[]> {
// const contract = await this.getContract();
// const result = await contract.get("get_data_sources");
// return result.map((ds: any) => ({
// emitterChain: ds.emitterChain,
// emitterAddress: ds.emitterAddress.replace("0x", ""),
// }));
return [];
const contract = await this.getContract();
const dataSources = await contract.getDataSources();
return dataSources.map((ds: DataSource) => ({
emitterChain: ds.emitterChain,
emitterAddress: ds.emitterAddress.replace("0x", ""),
}));
}

async getGovernanceDataSource(): Promise<DataSource> {
// const contract = await this.getContract();
// const result = await contract.get("get_governance_data_source");
const contract = await this.getContract();
const result = await contract.getGovernanceDataSource();
if (result === null) {
throw new Error("Governance data source not found");
}
return {
emitterChain: 1,
emitterAddress: "0x1",
emitterChain: result.emitterChain,
emitterAddress: result.emitterAddress,
};
}

async executeUpdatePriceFeed(
senderPrivateKey: PrivateKey,
vaas: Buffer[]
): Promise<TxResult> {
// const client = await this.chain.getClient(senderPrivateKey);
// const contract = await this.getContract(client);

// const updateFee = await contract.get("get_update_fee", [vaas[0]]);
const client = await this.chain.getClient();
const contract = await this.getContract();
const wallet = await this.chain.getWallet(senderPrivateKey);
const sender = await this.chain.getSender(senderPrivateKey);
for (const vaa of vaas) {
const fee = await contract.getUpdateFee(vaa);
console.log(fee);
await contract.sendUpdatePriceFeeds(
sender,
vaa,
156000000n + BigInt(fee)
); // 156000000 = 390000 (estimated gas used for the transaction, this is defined in target_chains/ton/contracts/common/gas.fc as UPDATE_PRICE_FEEDS_GAS) * 400 (current settings in basechain are as follows: 1 unit of gas costs 400 nanotons)
}

// const tx = await contract.sendMessage({
// body: {
// op: "update_price_feeds",
// data: vaas[0], // TON contract expects single VAA
// },
// value: updateFee.toString(),
// });
const txDetails = await client.getTransactions(wallet.address, {
limit: 1,
});
const txHash = Buffer.from(txDetails[0].hash()).toString("hex");
const txInfo = JSON.stringify(txDetails[0].description, (_, value) =>
typeof value === "bigint" ? value.toString() : value
);

return {
id: "0x1",
info: JSON.stringify("0x1"),
id: txHash,
info: txInfo,
};
}

async executeGovernanceInstruction(
senderPrivateKey: PrivateKey,
vaa: Buffer
): Promise<TxResult> {
// const client = await this.chain.getClient(senderPrivateKey);
// const contract = await this.getContract(client);

// const tx = await contract.sendMessage({
// body: {
// op: "execute_governance_action",
// data: vaa,
// },
// value: "0.05", // TON to attach
// });
const client = await this.chain.getClient();
const contract = await this.getContract();
const wallet = await this.chain.getWallet(senderPrivateKey);
const sender = await this.chain.getSender(senderPrivateKey);
await contract.sendExecuteGovernanceAction(sender, vaa);

const txDetails = await client.getTransactions(wallet.address, {
limit: 1,
});
const txHash = Buffer.from(txDetails[0].hash()).toString("hex");
const txInfo = JSON.stringify(txDetails[0].description, (_, value) =>
typeof value === "bigint" ? value.toString() : value
);

return {
id: "0x1",
info: JSON.stringify("0x1"),
id: txHash,
info: txInfo,
};
}

getChain(): TonChain {
return this.chain;
}

toJson() {
return {
chain: this.chain.getId(),
Expand Down
2 changes: 1 addition & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions target_chains/ton/contracts/contracts/Pyth.fc
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ int get_governance_data_source_index() method_id {
return governance_data_source_index;
}

cell get_data_sources() method_id {
load_data();
return data_sources;
}

cell get_governance_data_source() method_id {
load_data();
return governance_data_source;
Expand Down
4 changes: 4 additions & 0 deletions target_chains/ton/contracts/contracts/tests/PythTest.fc
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,7 @@
(int) test_get_is_valid_data_source(cell data_source) method_id {
return get_is_valid_data_source(data_source);
}

(cell) test_get_data_sources() method_id {
return get_data_sources();
}
Loading

0 comments on commit 1e82c8e

Please sign in to comment.