Skip to content

Commit

Permalink
impl contract manager for ton
Browse files Browse the repository at this point in the history
  • Loading branch information
cctdaniel committed Oct 30, 2024
1 parent b60a7cd commit 2240e44
Show file tree
Hide file tree
Showing 10 changed files with 618 additions and 537 deletions.
1 change: 1 addition & 0 deletions contract_manager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"@pythnetwork/xc-admin-common": "workspace:*",
"@solana/web3.js": "^1.73.0",
"@sqds/mesh": "^1.0.6",
"@ton/ton": "^15.1.0",
"@types/yargs": "^17.0.32",
"aptos": "^1.5.0",
"axios": "^0.24.0",
Expand Down
68 changes: 68 additions & 0 deletions contract_manager/src/chains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -738,3 +738,71 @@ export class StarknetChain extends Chain {
return new RpcProvider({ nodeUrl: this.rpcUrl });
}
}

export class TonChain extends Chain {
static type = "TonChain";

constructor(
id: string,
mainnet: boolean,
wormholeChainName: string,
nativeToken: TokenId | undefined,
public rpcUrl: string
) {
super(id, mainnet, wormholeChainName, nativeToken);
}

async getProvider(): Promise<Provider> {
return await Provider.create(this.rpcUrl);
}

async getWallet(privateKey: PrivateKey): Promise<WalletUnlocked> {
const provider = await this.getProvider();
return Wallet.fromPrivateKey(privateKey, provider);
}

/**
* Returns the payload for a governance contract upgrade instruction for contracts deployed on this chain
* @param digest hex string of the 32 byte digest for the new package without the 0x prefix
*/
generateGovernanceUpgradePayload(digest: string): Buffer {
// This might throw an error because the Fuel contract doesn't support upgrades yet (blocked on Fuel releasing Upgradeability standard)
return new UpgradeContract256Bit(this.wormholeChainName, digest).encode();
}

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

toJson(): KeyValueConfig {
return {
id: this.id,
wormholeChainName: this.wormholeChainName,
mainnet: this.mainnet,
rpcUrl: this.rpcUrl,
type: TonChain.type,
};
}

static fromJson(parsed: ChainConfig): TonChain {
if (parsed.type !== TonChain.type) throw new Error("Invalid type");
return new TonChain(
parsed.id,
parsed.mainnet,
parsed.wormholeChainName,
parsed.nativeToken,
parsed.rpcUrl
);
}

async getAccountAddress(privateKey: PrivateKey): Promise<string> {
const wallet = await this.getWallet(privateKey);
return wallet.address.toString();
}

async getAccountBalance(privateKey: PrivateKey): Promise<number> {
const wallet = await this.getWallet(privateKey);
const balance: BN = await wallet.getBalance(FUEL_ETH_ASSET_ID);
return Number(balance) / 10 ** 9;
}
}
1 change: 1 addition & 0 deletions contract_manager/src/contracts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export * from "./fuel";
export * from "./sui";
export * from "./wormhole";
export * from "./evm_abis";
export * from "./ton";
250 changes: 250 additions & 0 deletions contract_manager/src/contracts/ton.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
import { Chain, TonChain } from "../chains";
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, TonClient } from "@ton/ton";

export class TonWormholeContract extends WormholeContract {
static type = "TonWormholeContract";

getId(): string {
return `${this.chain.getId()}_${this.address}_${TonWormholeContract.type}`;
}

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

toJson() {
return {
chain: this.chain.getId(),
address: this.address,
type: TonWormholeContract.type,
};
}

static fromJson(
chain: Chain,
parsed: {
type: string;
address: string;
}
): TonWormholeContract {
if (parsed.type !== TonWormholeContract.type)
throw new Error("Invalid type");
if (!(chain instanceof TonChain))
throw new Error(`Wrong chain type ${chain}`);
return new TonWormholeContract(chain, parsed.address);
}

constructor(public chain: TonChain, public address: string) {
super();
}

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

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

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"];
}

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
// });

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

export class TonPriceFeedContract extends PriceFeedContract {
static type = "TonPriceFeedContract";

constructor(public chain: TonChain, public address: string) {
super();
}

static fromJson(
chain: Chain,
parsed: { type: string; address: string }
): TonPriceFeedContract {
if (parsed.type !== TonPriceFeedContract.type)
throw new Error("Invalid type");
if (!(chain instanceof TonChain))
throw new Error(`Wrong chain type ${chain}`);
return new TonPriceFeedContract(chain, parsed.address);
}

getId(): string {
return `${this.chain.getId()}_${this.address}_${TonPriceFeedContract.type}`;
}

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

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

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

async getPriceFeed(feedId: string): Promise<PriceFeed | undefined> {
// const contract = await this.getContract();
// try {
// const result = await contract.get("get_price_unsafe", [feedId]);
// return {
// price: {
// price: result.price.toString(),
// conf: result.conf.toString(),
// expo: result.expo.toString(),
// publishTime: result.publishTime.toString(),
// },
// emaPrice: {
// price: result.emaPrice.price.toString(),
// conf: result.emaPrice.conf.toString(),
// expo: result.emaPrice.expo.toString(),
// publishTime: result.emaPrice.publishTime.toString(),
// },
// };
// } catch (e) {
// return undefined;
// }
return undefined;
}

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

async getWormholeContract(): Promise<TonWormholeContract> {
// Price feed contract and wormhole contract live at same address in TON
return new TonWormholeContract(this.chain, this.address);
}

async getBaseUpdateFee() {
// const pythContract = await this.getContract();
// const amount = (await pythContract.functions.single_update_fee().get())
// .value;
return {
amount: "0",
denom: this.chain.getNativeToken(),
};
}

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 [];
}

async getGovernanceDataSource(): Promise<DataSource> {
// const contract = await this.getContract();
// const result = await contract.get("get_governance_data_source");
return {
emitterChain: 1,
emitterAddress: "0x1",
};
}

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 tx = await contract.sendMessage({
// body: {
// op: "update_price_feeds",
// data: vaas[0], // TON contract expects single VAA
// },
// value: updateFee.toString(),
// });

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

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
// });

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

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

toJson() {
return {
chain: this.chain.getId(),
address: this.address,
type: TonPriceFeedContract.type,
};
}
}
4 changes: 4 additions & 0 deletions contract_manager/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import {
WormholeContract,
FuelPriceFeedContract,
EvmExpressRelayContract,
TonPriceFeedContract,
TonWormholeContract,
} from "./contracts";
import { Token } from "./token";
import { PriceFeedContract, Storable } from "./base";
Expand Down Expand Up @@ -150,6 +152,8 @@ export class Store {
[FuelWormholeContract.type]: FuelWormholeContract,
[StarknetPriceFeedContract.type]: StarknetPriceFeedContract,
[StarknetWormholeContract.type]: StarknetWormholeContract,
[TonPriceFeedContract.type]: TonPriceFeedContract,
[TonWormholeContract.type]: TonWormholeContract,
};
this.getYamlFiles(`${this.path}/contracts/`).forEach((yamlFile) => {
const parsedArray = parse(readFileSync(yamlFile, "utf-8"));
Expand Down
5 changes: 5 additions & 0 deletions contract_manager/store/chains/TonChains.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
- id: ton_testnet
wormholeChainName: ton_testnet
mainnet: false
rpcUrl: https://testnet.tonapi.io
type: TonChain
4 changes: 4 additions & 0 deletions contract_manager/store/contracts/TonPriceFeedContracts.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- chain: ton_testnet
address: "EQDwGkJmcj7MMmWAHmhldnY-lAKI6hcTQ2tAEcapmwCnztQU"
type: TonPriceFeedContract

3 changes: 3 additions & 0 deletions contract_manager/store/contracts/TonWormholeContracts.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- chain: ton_testnet
address: "EQDwGkJmcj7MMmWAHmhldnY-lAKI6hcTQ2tAEcapmwCnztQU"
type: TonWormholeContract
1 change: 1 addition & 0 deletions governance/xc_admin/packages/xc_admin_common/src/chains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ export const RECEIVER_CHAINS = {
sanko_testnet: 50101,
skate_testnet: 50102,
movement_porto_testnet: 50103,
ton_testnet: 50104,
};

// If there is any overlapping value the receiver chain will replace the wormhole
Expand Down
Loading

0 comments on commit 2240e44

Please sign in to comment.