Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion contract_manager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
"@pythnetwork/cosmwasm-deploy-tools": "workspace:*",
"@pythnetwork/entropy-sdk-solidity": "workspace:*",
"@pythnetwork/price-service-client": "workspace:*",
"@pythnetwork/pyth-sdk-solidity": "workspace:^",
"@pythnetwork/pyth-fuel-js": "workspace:*",
"@pythnetwork/pyth-sdk-solidity": "workspace:^",
"@pythnetwork/pyth-sui-js": "workspace:*",
"@pythnetwork/solana-utils": "workspace:^",
"@pythnetwork/xc-admin-common": "workspace:*",
Expand All @@ -45,6 +45,7 @@
"extract-files": "^13.0.0",
"fuels": "^0.89.2",
"ramda": "^0.30.1",
"starknet": "^5.24.3",
"ts-node": "^10.9.1",
"typescript": "^5.3.3",
"web3": "^1.8.2",
Expand Down
60 changes: 60 additions & 0 deletions contract_manager/src/chains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { Ed25519Keypair } from "@mysten/sui.js/keypairs/ed25519";
import { TokenId } from "./token";
import { BN, Provider, Wallet, WalletUnlocked } from "fuels";
import { FUEL_ETH_ASSET_ID } from "@pythnetwork/pyth-fuel-js";
import { RpcProvider } from "starknet";

export type ChainConfig = Record<string, string> & {
mainnet: boolean;
Expand Down Expand Up @@ -630,3 +631,62 @@ export class FuelChain extends Chain {
return Number(balance) / 10 ** 9;
}
}

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

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

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

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

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

/**
* Returns the payload for a governance contract upgrade instruction for contracts deployed on this chain
* @param digest hex string of the felt252 class hash of the new contract class extended to uint256 in BE
*/
generateGovernanceUpgradePayload(digest: string): Buffer {
return new UpgradeContract256Bit(this.wormholeChainName, digest).encode();
}

// Account address derivation on Starknet depends
// on the wallet application and constructor arguments used.
async getAccountAddress(privateKey: PrivateKey): Promise<string> {
throw new Error("Unsupported");
}

async getAccountBalance(privateKey: PrivateKey): Promise<number> {
throw new Error("Unsupported");
}

getProvider(): RpcProvider {
return new RpcProvider({ nodeUrl: this.rpcUrl });
}
}
100 changes: 100 additions & 0 deletions contract_manager/src/contracts/starknet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { DataSource } from "@pythnetwork/xc-admin-common";
import {
KeyValueConfig,
PriceFeed,
PriceFeedContract,
PrivateKey,
TxResult,
} from "../base";
import { Chain, StarknetChain } from "../chains";
import { Contract } from "starknet";

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

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

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

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

// Not implemented in the Starknet contract.
getValidTimePeriod(): Promise<number> {
throw new Error("Unsupported");
}

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

async getContractClient(): Promise<Contract> {
const provider = this.chain.getProvider();
const classData = await provider.getClassAt(this.address);
return new Contract(classData.abi, this.address, provider);
}

async getDataSources(): Promise<DataSource[]> {
const contract = await this.getContractClient();
const sources: { emitter_chain_id: bigint; emitter_address: bigint }[] =
await contract.valid_data_sources();
return sources.map((source) => {
return {
emitterChain: Number(source.emitter_chain_id),
emitterAddress: source.emitter_address.toString(16),
};
});
}

getBaseUpdateFee(): Promise<{ amount: string; denom?: string | undefined }> {
throw new Error("Method not implemented.");
}
getLastExecutedGovernanceSequence(): Promise<number> {
throw new Error("Method not implemented.");
}
getPriceFeed(feedId: string): Promise<PriceFeed | undefined> {
throw new Error("Method not implemented.");
}
executeUpdatePriceFeed(
senderPrivateKey: PrivateKey,
vaas: Buffer[]
): Promise<TxResult> {
throw new Error("Method not implemented.");
}
executeGovernanceInstruction(
senderPrivateKey: PrivateKey,
vaa: Buffer
): Promise<TxResult> {
throw new Error("Method not implemented.");
}
getGovernanceDataSource(): Promise<DataSource> {
throw new Error("Method not implemented.");
}

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

getType(): string {
return StarknetPriceFeedContract.type;
}
}
3 changes: 2 additions & 1 deletion contract_manager/src/shell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ repl.setService(service);
repl.start();
repl.evalCode(
"import { loadHotWallet, Vault } from './src/governance';" +
"import { SuiChain, CosmWasmChain, AptosChain, EvmChain } from './src/chains';" +
"import { SuiChain, CosmWasmChain, AptosChain, EvmChain, StarknetChain } from './src/chains';" +
"import { SuiPriceFeedContract } from './src/contracts/sui';" +
"import { CosmWasmWormholeContract, CosmWasmPriceFeedContract } from './src/contracts/cosmwasm';" +
"import { EvmWormholeContract, EvmPriceFeedContract } from './src/contracts/evm';" +
"import { AptosWormholeContract, AptosPriceFeedContract } from './src/contracts/aptos';" +
"import { StarknetPriceFeedContract } from './src/contracts/starknet';" +
"import { DefaultStore } from './src/store';" +
"import { toPrivateKey } from './src/base';" +
"DefaultStore"
Expand Down
4 changes: 4 additions & 0 deletions contract_manager/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
AptosChain,
Chain,
CosmWasmChain,
StarknetChain,
EvmChain,
FuelChain,
GlobalChain,
Expand All @@ -26,6 +27,7 @@ import { PriceFeedContract, Storable } from "./base";
import { parse, stringify } from "yaml";
import { readdirSync, readFileSync, statSync, writeFileSync } from "fs";
import { Vault } from "./governance";
import { StarknetPriceFeedContract } from "./contracts/starknet";

export class Store {
public chains: Record<string, Chain> = { global: new GlobalChain() };
Expand Down Expand Up @@ -73,6 +75,7 @@ export class Store {
[EvmChain.type]: EvmChain,
[AptosChain.type]: AptosChain,
[FuelChain.type]: FuelChain,
[StarknetChain.type]: StarknetChain,
};

this.getYamlFiles(`${this.path}/chains/`).forEach((yamlFile) => {
Expand Down Expand Up @@ -135,6 +138,7 @@ export class Store {
[EvmWormholeContract.type]: EvmWormholeContract,
[FuelPriceFeedContract.type]: FuelPriceFeedContract,
[FuelWormholeContract.type]: FuelWormholeContract,
[StarknetPriceFeedContract.type]: StarknetPriceFeedContract,
};
this.getYamlFiles(`${this.path}/contracts/`).forEach((yamlFile) => {
const parsedArray = parse(readFileSync(yamlFile, "utf-8"));
Expand Down
10 changes: 10 additions & 0 deletions contract_manager/store/chains/StarknetChains.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
- id: starknet_sepolia
wormholeChainName: starknet_sepolia
mainnet: false
rpcUrl: https://starknet-sepolia.public.blastapi.io/
type: StarknetChain
- id: starknet_mainnet
wormholeChainName: starknet
mainnet: true
rpcUrl: https://starknet-mainnet.public.blastapi.io/
type: StarknetChain
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- chain: starknet_sepolia
type: StarknetPriceFeedContract
address: "0x07f2b07b6b5365e7ee055bda4c0ecabd867e6d3ee298d73aea32b027667186d6"
46 changes: 46 additions & 0 deletions pnpm-lock.yaml

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