Skip to content

Commit ce1e0c0

Browse files
authored
feat(contract_manager): more support for starknet contract (#1716)
* feat(contract_manager): more support for starknet contract * chore(contract_manager): update pyth-starknet-js
1 parent 40a63bf commit ce1e0c0

File tree

3 files changed

+255
-45
lines changed

3 files changed

+255
-45
lines changed

contract_manager/package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"@pythnetwork/price-service-client": "workspace:*",
3535
"@pythnetwork/pyth-fuel-js": "workspace:*",
3636
"@pythnetwork/pyth-sdk-solidity": "workspace:^",
37+
"@pythnetwork/pyth-starknet-js": "^0.2.0",
3738
"@pythnetwork/pyth-sui-js": "workspace:*",
3839
"@pythnetwork/solana-utils": "workspace:^",
3940
"@pythnetwork/xc-admin-common": "workspace:*",
@@ -45,7 +46,7 @@
4546
"extract-files": "^13.0.0",
4647
"fuels": "^0.89.2",
4748
"ramda": "^0.30.1",
48-
"starknet": "^5.24.3",
49+
"starknet": "^6.9.0",
4950
"ts-node": "^10.9.1",
5051
"typescript": "^5.3.3",
5152
"web3": "^1.8.2",
@@ -54,8 +55,8 @@
5455
},
5556
"devDependencies": {
5657
"@types/web3": "^1.2.2",
58+
"eslint": "^8.0.0",
5759
"prettier": "^2.6.2",
58-
"typedoc": "^0.25.7",
59-
"eslint": "^8.0.0"
60+
"typedoc": "^0.25.7"
6061
}
6162
}

contract_manager/src/contracts/starknet.ts

Lines changed: 145 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import { DataSource } from "@pythnetwork/xc-admin-common";
22
import {
33
KeyValueConfig,
4+
Price,
45
PriceFeed,
56
PriceFeedContract,
67
PrivateKey,
78
TxResult,
89
} from "../base";
910
import { Chain, StarknetChain } from "../chains";
10-
import { Contract } from "starknet";
11+
import { Account, Contract, shortString } from "starknet";
12+
import { ByteBuffer } from "@pythnetwork/pyth-starknet-js";
1113

1214
export class StarknetPriceFeedContract extends PriceFeedContract {
1315
static type = "StarknetPriceFeedContract";
@@ -65,29 +67,146 @@ export class StarknetPriceFeedContract extends PriceFeedContract {
6567
});
6668
}
6769

68-
getBaseUpdateFee(): Promise<{ amount: string; denom?: string | undefined }> {
69-
throw new Error("Method not implemented.");
70+
async getBaseUpdateFee(): Promise<{
71+
amount: string;
72+
denom?: string | undefined;
73+
}> {
74+
const tokens = await this.getFeeTokenAddresses();
75+
return await this.getBaseUpdateFeeInToken(tokens[0]);
7076
}
71-
getLastExecutedGovernanceSequence(): Promise<number> {
72-
throw new Error("Method not implemented.");
77+
78+
/**
79+
* Returns the list of accepted fee tokens.
80+
* @returns hex encoded token addresses without 0x prefix
81+
*/
82+
async getFeeTokenAddresses(): Promise<string[]> {
83+
const contract = await this.getContractClient();
84+
const tokens: bigint[] = await contract.fee_token_addresses();
85+
return tokens.map((t) => t.toString(16));
7386
}
74-
getPriceFeed(feedId: string): Promise<PriceFeed | undefined> {
75-
throw new Error("Method not implemented.");
87+
88+
/**
89+
* Returns the single update fee and symbol of the specified token.
90+
* @param token hex encoded token address without 0x prefix
91+
*/
92+
async getBaseUpdateFeeInToken(
93+
token: string
94+
): Promise<{ amount: string; denom?: string | undefined }> {
95+
token = "0x" + token;
96+
const provider = this.chain.getProvider();
97+
const contract = await this.getContractClient();
98+
const fee: bigint = await contract.get_single_update_fee(token);
99+
100+
const tokenClassData = await provider.getClassAt(token);
101+
const tokenContract = new Contract(tokenClassData.abi, token, provider);
102+
const denom = shortString.decodeShortString(await tokenContract.symbol());
103+
return { amount: fee.toString(), denom };
76104
}
77-
executeUpdatePriceFeed(
105+
106+
async getLastExecutedGovernanceSequence(): Promise<number> {
107+
const contract = await this.getContractClient();
108+
return Number(await contract.last_executed_governance_sequence());
109+
}
110+
111+
async getPriceFeed(feedId: string): Promise<PriceFeed | undefined> {
112+
const contract = await this.getContractClient();
113+
const result = await contract.query_price_feed_unsafe("0x" + feedId);
114+
console.log(result);
115+
if (result.Ok !== undefined) {
116+
return {
117+
price: convertPrice(result.Ok.price),
118+
emaPrice: convertPrice(result.Ok.ema_price),
119+
};
120+
} else {
121+
throw new Error(JSON.stringify(result.Err));
122+
}
123+
}
124+
125+
async executeUpdatePriceFeed(
78126
senderPrivateKey: PrivateKey,
79127
vaas: Buffer[]
80128
): Promise<TxResult> {
81-
throw new Error("Method not implemented.");
129+
// We need the account address to send transactions.
130+
throw new Error("Use executeUpdatePriceFeedWithAddress instead");
82131
}
132+
133+
/**
134+
* Executes the update instructions contained in the VAAs using the sender credentials
135+
* @param senderPrivateKey private key of the sender in hex format without 0x prefix
136+
* @param senderAddress address of the sender's account in hex format without 0x prefix
137+
* @param vaa VAA containing price update messages to execute
138+
*/
139+
async executeUpdatePriceFeedWithAddress(
140+
senderPrivateKey: PrivateKey,
141+
senderAddress: string,
142+
vaa: Buffer
143+
): Promise<TxResult> {
144+
const provider = this.chain.getProvider();
145+
const contract = await this.getContractClient();
146+
const account = new Account(
147+
provider,
148+
"0x" + senderAddress,
149+
"0x" + senderPrivateKey
150+
);
151+
contract.connect(account);
152+
153+
const feeToken = "0x" + (await this.getFeeTokenAddresses())[0];
154+
const tokenClassData = await provider.getClassAt(feeToken);
155+
const tokenContract = new Contract(tokenClassData.abi, feeToken, provider);
156+
tokenContract.connect(account);
157+
158+
const updateData = ByteBuffer.fromBuffer(vaa);
159+
const feeAmount = await contract.get_update_fee(updateData, feeToken);
160+
const feeTx = await tokenContract.approve(this.address, feeAmount);
161+
await provider.waitForTransaction(feeTx.transaction_hash);
162+
163+
const tx = await contract.update_price_feeds(updateData);
164+
const info = await provider.waitForTransaction(tx.transaction_hash);
165+
return { id: tx.transaction_hash, info };
166+
}
167+
83168
executeGovernanceInstruction(
84169
senderPrivateKey: PrivateKey,
85170
vaa: Buffer
86171
): Promise<TxResult> {
87-
throw new Error("Method not implemented.");
172+
// We need the account address to send transactions.
173+
throw new Error("Use executeGovernanceInstructionWithAddress instead");
174+
}
175+
176+
/**
177+
* Executes the governance instruction contained in the VAA using the sender credentials
178+
* @param senderPrivateKey private key of the sender in hex format without 0x prefix
179+
* @param senderAddress address of the sender's account in hex format without 0x prefix
180+
* @param vaa the VAA to execute
181+
*/
182+
async executeGovernanceInstructionWithAddress(
183+
senderPrivateKey: PrivateKey,
184+
senderAddress: string,
185+
vaa: Buffer
186+
): Promise<TxResult> {
187+
const provider = this.chain.getProvider();
188+
const contract = await this.getContractClient();
189+
const account = new Account(
190+
provider,
191+
"0x" + senderAddress,
192+
"0x" + senderPrivateKey
193+
);
194+
contract.connect(account);
195+
196+
const updateData = ByteBuffer.fromBuffer(vaa);
197+
const tx = await contract.execute_governance_instruction(updateData);
198+
const info = await provider.waitForTransaction(tx.transaction_hash);
199+
return { id: tx.transaction_hash, info };
88200
}
89-
getGovernanceDataSource(): Promise<DataSource> {
90-
throw new Error("Method not implemented.");
201+
202+
async getGovernanceDataSource(): Promise<DataSource> {
203+
const contract = await this.getContractClient();
204+
const source: { emitter_chain_id: bigint; emitter_address: bigint } =
205+
await contract.governance_data_source();
206+
return {
207+
emitterChain: Number(source.emitter_chain_id),
208+
emitterAddress: source.emitter_address.toString(16),
209+
};
91210
}
92211

93212
getId(): string {
@@ -98,3 +217,17 @@ export class StarknetPriceFeedContract extends PriceFeedContract {
98217
return StarknetPriceFeedContract.type;
99218
}
100219
}
220+
221+
function convertPrice(obj: {
222+
price: bigint;
223+
conf: bigint;
224+
expo: bigint;
225+
publish_time: bigint;
226+
}): Price {
227+
return {
228+
price: obj.price.toString(),
229+
conf: obj.conf.toString(),
230+
expo: obj.expo.toString(),
231+
publishTime: obj.publish_time.toString(),
232+
};
233+
}

0 commit comments

Comments
 (0)