chore(target_chains/sui): update sui js packages (#1340)
* chore(target_chains/sui): update sui js packages * chore: bump version --------- Co-authored-by: Amin Moghaddam <amin@pyth.network>
This commit is contained in:
parent
f9de5430d1
commit
736a20208b
|
@ -23,7 +23,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certusone/wormhole-sdk": "^0.9.8",
|
"@certusone/wormhole-sdk": "^0.9.8",
|
||||||
"@injectivelabs/networks": "1.0.68",
|
"@injectivelabs/networks": "1.0.68",
|
||||||
"@mysten/sui.js": "^0.37.1",
|
"@mysten/sui.js": "^0.49.1",
|
||||||
"@pythnetwork/cosmwasm-deploy-tools": "*",
|
"@pythnetwork/cosmwasm-deploy-tools": "*",
|
||||||
"@pythnetwork/entropy-sdk-solidity": "*",
|
"@pythnetwork/entropy-sdk-solidity": "*",
|
||||||
"@pythnetwork/price-service-client": "*",
|
"@pythnetwork/price-service-client": "*",
|
||||||
|
@ -32,7 +32,7 @@
|
||||||
"aptos": "^1.5.0",
|
"aptos": "^1.5.0",
|
||||||
"bs58": "^5.0.0",
|
"bs58": "^5.0.0",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^4.9.3"
|
"typescript": "^5.3.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"prettier": "^2.6.2",
|
"prettier": "^2.6.2",
|
||||||
|
|
|
@ -20,12 +20,8 @@ import {
|
||||||
InjectiveExecutor,
|
InjectiveExecutor,
|
||||||
} from "@pythnetwork/cosmwasm-deploy-tools";
|
} from "@pythnetwork/cosmwasm-deploy-tools";
|
||||||
import { Network } from "@injectivelabs/networks";
|
import { Network } from "@injectivelabs/networks";
|
||||||
import {
|
import { SuiClient } from "@mysten/sui.js/client";
|
||||||
Connection,
|
import { Ed25519Keypair } from "@mysten/sui.js/keypairs/ed25519";
|
||||||
Ed25519Keypair,
|
|
||||||
JsonRpcProvider,
|
|
||||||
RawSigner,
|
|
||||||
} from "@mysten/sui.js";
|
|
||||||
|
|
||||||
export type ChainConfig = Record<string, string> & {
|
export type ChainConfig = Record<string, string> & {
|
||||||
mainnet: boolean;
|
mainnet: boolean;
|
||||||
|
@ -290,17 +286,15 @@ export class SuiChain extends Chain {
|
||||||
).encode();
|
).encode();
|
||||||
}
|
}
|
||||||
|
|
||||||
getProvider(): JsonRpcProvider {
|
getProvider(): SuiClient {
|
||||||
return new JsonRpcProvider(new Connection({ fullnode: this.rpcUrl }));
|
return new SuiClient({ url: this.rpcUrl });
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAccountAddress(privateKey: PrivateKey): Promise<string> {
|
async getAccountAddress(privateKey: PrivateKey): Promise<string> {
|
||||||
const provider = this.getProvider();
|
|
||||||
const keypair = Ed25519Keypair.fromSecretKey(
|
const keypair = Ed25519Keypair.fromSecretKey(
|
||||||
Buffer.from(privateKey, "hex")
|
Buffer.from(privateKey, "hex")
|
||||||
);
|
);
|
||||||
const wallet = new RawSigner(keypair, provider);
|
return keypair.toSuiAddress();
|
||||||
return await wallet.getAddress();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAccountBalance(privateKey: PrivateKey): Promise<number> {
|
async getAccountBalance(privateKey: PrivateKey): Promise<number> {
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
import {
|
|
||||||
Ed25519Keypair,
|
|
||||||
ObjectId,
|
|
||||||
RawSigner,
|
|
||||||
SUI_CLOCK_OBJECT_ID,
|
|
||||||
TransactionBlock,
|
|
||||||
} from "@mysten/sui.js";
|
|
||||||
import { Chain, SuiChain } from "../chains";
|
import { Chain, SuiChain } from "../chains";
|
||||||
import { DataSource } from "xc_admin_common";
|
import { DataSource } from "xc_admin_common";
|
||||||
import { PriceFeedContract, PrivateKey, TxResult } from "../base";
|
import { PriceFeedContract, PrivateKey, TxResult } from "../base";
|
||||||
import { SuiPythClient } from "@pythnetwork/pyth-sui-js";
|
import { SuiPythClient } from "@pythnetwork/pyth-sui-js";
|
||||||
|
import { SUI_CLOCK_OBJECT_ID } from "@mysten/sui.js/utils";
|
||||||
|
import { Ed25519Keypair } from "@mysten/sui.js/keypairs/ed25519";
|
||||||
|
import { TransactionBlock } from "@mysten/sui.js/transactions";
|
||||||
|
|
||||||
|
type ObjectId = string;
|
||||||
|
|
||||||
export class SuiPriceFeedContract extends PriceFeedContract {
|
export class SuiPriceFeedContract extends PriceFeedContract {
|
||||||
static type = "SuiPriceFeedContract";
|
static type = "SuiPriceFeedContract";
|
||||||
|
@ -96,13 +94,6 @@ export class SuiPriceFeedContract extends PriceFeedContract {
|
||||||
timestamp: string;
|
timestamp: string;
|
||||||
};
|
};
|
||||||
}) {
|
}) {
|
||||||
const packageId = await this.getPythPackageId();
|
|
||||||
const expectedType = `${packageId}::price::Price`;
|
|
||||||
if (priceInfo.type !== expectedType) {
|
|
||||||
throw new Error(
|
|
||||||
`Price type mismatch, expected ${expectedType} but found ${priceInfo.type}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let expo = priceInfo.fields.expo.fields.magnitude;
|
let expo = priceInfo.fields.expo.fields.magnitude;
|
||||||
if (priceInfo.fields.expo.fields.negative) expo = "-" + expo;
|
if (priceInfo.fields.expo.fields.negative) expo = "-" + expo;
|
||||||
let price = priceInfo.fields.price.fields.magnitude;
|
let price = priceInfo.fields.price.fields.magnitude;
|
||||||
|
@ -135,10 +126,14 @@ export class SuiPriceFeedContract extends PriceFeedContract {
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
emaPrice: await this.parsePrice(
|
emaPrice: await this.parsePrice(
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
priceInfo.data.content.fields.price_info.fields.price_feed.fields
|
priceInfo.data.content.fields.price_info.fields.price_feed.fields
|
||||||
.ema_price
|
.ema_price
|
||||||
),
|
),
|
||||||
price: await this.parsePrice(
|
price: await this.parsePrice(
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
priceInfo.data.content.fields.price_info.fields.price_feed.fields.price
|
priceInfo.data.content.fields.price_info.fields.price_feed.fields.price
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
@ -303,21 +298,25 @@ export class SuiPriceFeedContract extends PriceFeedContract {
|
||||||
keypair: Ed25519Keypair
|
keypair: Ed25519Keypair
|
||||||
) {
|
) {
|
||||||
const provider = this.getProvider();
|
const provider = this.getProvider();
|
||||||
const txBlock = {
|
tx.setSender(keypair.toSuiAddress());
|
||||||
|
const dryRun = await provider.dryRunTransactionBlock({
|
||||||
|
transactionBlock: await tx.build({ client: provider }),
|
||||||
|
});
|
||||||
|
tx.setGasBudget(BigInt(dryRun.input.gasData.budget.toString()) * BigInt(2));
|
||||||
|
return provider.signAndExecuteTransactionBlock({
|
||||||
|
signer: keypair,
|
||||||
transactionBlock: tx,
|
transactionBlock: tx,
|
||||||
options: {
|
options: {
|
||||||
showEffects: true,
|
showEffects: true,
|
||||||
showEvents: true,
|
showEvents: true,
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
const wallet = new RawSigner(keypair, provider);
|
|
||||||
const gasCost = await wallet.getGasCostEstimation(txBlock);
|
|
||||||
tx.setGasBudget(gasCost * BigInt(2));
|
|
||||||
return wallet.signAndExecuteTransactionBlock(txBlock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getValidTimePeriod() {
|
async getValidTimePeriod() {
|
||||||
const fields = await this.getStateFields();
|
const fields = await this.getStateFields();
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
return Number(fields.stale_price_threshold);
|
return Number(fields.stale_price_threshold);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,6 +337,8 @@ export class SuiPriceFeedContract extends PriceFeedContract {
|
||||||
if (result.data.content.dataType !== "moveObject") {
|
if (result.data.content.dataType !== "moveObject") {
|
||||||
throw new Error("Data Sources type mismatch");
|
throw new Error("Data Sources type mismatch");
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
return result.data.content.fields.value.fields.keys.map(
|
return result.data.content.fields.value.fields.keys.map(
|
||||||
({
|
({
|
||||||
fields,
|
fields,
|
||||||
|
@ -359,6 +360,8 @@ export class SuiPriceFeedContract extends PriceFeedContract {
|
||||||
|
|
||||||
async getGovernanceDataSource(): Promise<DataSource> {
|
async getGovernanceDataSource(): Promise<DataSource> {
|
||||||
const fields = await this.getStateFields();
|
const fields = await this.getStateFields();
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
const governanceFields = fields.governance_data_source.fields;
|
const governanceFields = fields.governance_data_source.fields;
|
||||||
const chainId = governanceFields.emitter_chain;
|
const chainId = governanceFields.emitter_chain;
|
||||||
const emitterAddress =
|
const emitterAddress =
|
||||||
|
@ -371,11 +374,15 @@ export class SuiPriceFeedContract extends PriceFeedContract {
|
||||||
|
|
||||||
async getBaseUpdateFee() {
|
async getBaseUpdateFee() {
|
||||||
const fields = await this.getStateFields();
|
const fields = await this.getStateFields();
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
return { amount: fields.base_update_fee };
|
return { amount: fields.base_update_fee };
|
||||||
}
|
}
|
||||||
|
|
||||||
async getLastExecutedGovernanceSequence() {
|
async getLastExecutedGovernanceSequence() {
|
||||||
const fields = await this.getStateFields();
|
const fields = await this.getStateFields();
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
return Number(fields.last_executed_governance_sequence);
|
return Number(fields.last_executed_governance_sequence);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,7 @@
|
||||||
- chain: sui_mainnet
|
|
||||||
stateId: "0xf9ff3ef935ef6cdfb659a203bf2754cebeb63346e29114a535ea6f41315e5a3f"
|
|
||||||
wormholeStateId: "0xaeab97f96cf9877fee2883315d459552b2b921edc16d7ceac6eab944dd88919c"
|
|
||||||
type: SuiPriceFeedContract
|
|
||||||
- chain: sui_mainnet
|
- chain: sui_mainnet
|
||||||
stateId: "0x1f9310238ee9298fb703c3419030b35b22bb1cc37113e3bb5007c99aec79e5b8"
|
stateId: "0x1f9310238ee9298fb703c3419030b35b22bb1cc37113e3bb5007c99aec79e5b8"
|
||||||
wormholeStateId: "0xaeab97f96cf9877fee2883315d459552b2b921edc16d7ceac6eab944dd88919c"
|
wormholeStateId: "0xaeab97f96cf9877fee2883315d459552b2b921edc16d7ceac6eab944dd88919c"
|
||||||
type: SuiPriceFeedContract
|
type: SuiPriceFeedContract
|
||||||
- chain: sui_testnet
|
|
||||||
stateId: "0xb3142a723792001caafc601b7c6fe38f09f3684e360b56d8d90fc574e71e75f3"
|
|
||||||
wormholeStateId: "0xebba4cc4d614f7a7cdbe883acc76d1cc767922bc96778e7b68be0d15fce27c02"
|
|
||||||
type: SuiPriceFeedContract
|
|
||||||
- chain: sui_testnet
|
- chain: sui_testnet
|
||||||
stateId: "0x2d82612a354f0b7e52809fc2845642911c7190404620cec8688f68808f8800d8"
|
stateId: "0x2d82612a354f0b7e52809fc2845642911c7190404620cec8688f68808f8800d8"
|
||||||
wormholeStateId: "0xebba4cc4d614f7a7cdbe883acc76d1cc767922bc96778e7b68be0d15fce27c02"
|
wormholeStateId: "0xebba4cc4d614f7a7cdbe883acc76d1cc767922bc96778e7b68be0d15fce27c02"
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@pythnetwork/price-pusher",
|
"name": "@pythnetwork/price-pusher",
|
||||||
"version": "6.2.0",
|
"version": "6.3.0",
|
||||||
"description": "Pyth Price Pusher",
|
"description": "Pyth Price Pusher",
|
||||||
"homepage": "https://pyth.network",
|
"homepage": "https://pyth.network",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
|
@ -45,14 +45,14 @@
|
||||||
"@typescript-eslint/eslint-plugin": "^5.20.0",
|
"@typescript-eslint/eslint-plugin": "^5.20.0",
|
||||||
"@typescript-eslint/parser": "^5.20.0",
|
"@typescript-eslint/parser": "^5.20.0",
|
||||||
"eslint": "^8.13.0",
|
"eslint": "^8.13.0",
|
||||||
"jest": "^27.5.1",
|
"jest": "^29.7.0",
|
||||||
"prettier": "^2.6.2",
|
"prettier": "^2.6.2",
|
||||||
"ts-jest": "^27.1.4",
|
"ts-jest": "^29.1.1",
|
||||||
"typescript": "^4.6.3"
|
"typescript": "^5.3.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@injectivelabs/sdk-ts": "1.10.72",
|
"@injectivelabs/sdk-ts": "1.10.72",
|
||||||
"@mysten/sui.js": "^0.37.1",
|
"@mysten/sui.js": "^0.49.1",
|
||||||
"@pythnetwork/price-service-client": "*",
|
"@pythnetwork/price-service-client": "*",
|
||||||
"@pythnetwork/pyth-sdk-solidity": "*",
|
"@pythnetwork/pyth-sdk-solidity": "*",
|
||||||
"@pythnetwork/pyth-sui-js": "*",
|
"@pythnetwork/pyth-sui-js": "*",
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { PythPriceListener } from "../pyth-price-listener";
|
||||||
import { Controller } from "../controller";
|
import { Controller } from "../controller";
|
||||||
import { Options } from "yargs";
|
import { Options } from "yargs";
|
||||||
import { SuiPriceListener, SuiPricePusher } from "./sui";
|
import { SuiPriceListener, SuiPricePusher } from "./sui";
|
||||||
import { Ed25519Keypair } from "@mysten/sui.js";
|
import { Ed25519Keypair } from "@mysten/sui.js/keypairs/ed25519";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
command: "sui",
|
command: "sui",
|
||||||
|
|
|
@ -6,30 +6,22 @@ import {
|
||||||
} from "../interface";
|
} from "../interface";
|
||||||
import { DurationInSeconds } from "../utils";
|
import { DurationInSeconds } from "../utils";
|
||||||
import { PriceServiceConnection } from "@pythnetwork/price-service-client";
|
import { PriceServiceConnection } from "@pythnetwork/price-service-client";
|
||||||
import {
|
|
||||||
JsonRpcProvider,
|
|
||||||
Connection,
|
|
||||||
Ed25519Keypair,
|
|
||||||
RawSigner,
|
|
||||||
TransactionBlock,
|
|
||||||
getCreatedObjects,
|
|
||||||
SuiObjectRef,
|
|
||||||
getTransactionEffects,
|
|
||||||
getExecutionStatusError,
|
|
||||||
PaginatedCoins,
|
|
||||||
SuiAddress,
|
|
||||||
ObjectId,
|
|
||||||
} from "@mysten/sui.js";
|
|
||||||
import { SuiPythClient } from "@pythnetwork/pyth-sui-js";
|
import { SuiPythClient } from "@pythnetwork/pyth-sui-js";
|
||||||
|
import { Ed25519Keypair } from "@mysten/sui.js/keypairs/ed25519";
|
||||||
|
import { TransactionBlock } from "@mysten/sui.js/transactions";
|
||||||
|
import { SuiClient, SuiObjectRef, PaginatedCoins } from "@mysten/sui.js/client";
|
||||||
|
|
||||||
const GAS_FEE_FOR_SPLIT = 2_000_000_000;
|
const GAS_FEE_FOR_SPLIT = 2_000_000_000;
|
||||||
// TODO: read this from on chain config
|
// TODO: read this from on chain config
|
||||||
const MAX_NUM_GAS_OBJECTS_IN_PTB = 256;
|
const MAX_NUM_GAS_OBJECTS_IN_PTB = 256;
|
||||||
const MAX_NUM_OBJECTS_IN_ARGUMENT = 510;
|
const MAX_NUM_OBJECTS_IN_ARGUMENT = 510;
|
||||||
|
|
||||||
|
type ObjectId = string;
|
||||||
|
type SuiAddress = string;
|
||||||
|
|
||||||
export class SuiPriceListener extends ChainPriceListener {
|
export class SuiPriceListener extends ChainPriceListener {
|
||||||
private pythClient: SuiPythClient;
|
private pythClient: SuiPythClient;
|
||||||
private provider: JsonRpcProvider;
|
private provider: SuiClient;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
pythStateId: ObjectId,
|
pythStateId: ObjectId,
|
||||||
|
@ -41,7 +33,7 @@ export class SuiPriceListener extends ChainPriceListener {
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
super("sui", config.pollingFrequency, priceItems);
|
super("sui", config.pollingFrequency, priceItems);
|
||||||
this.provider = new JsonRpcProvider(new Connection({ fullnode: endpoint }));
|
this.provider = new SuiClient({ url: endpoint });
|
||||||
this.pythClient = new SuiPythClient(
|
this.pythClient = new SuiPythClient(
|
||||||
this.provider,
|
this.provider,
|
||||||
pythStateId,
|
pythStateId,
|
||||||
|
@ -64,26 +56,22 @@ export class SuiPriceListener extends ChainPriceListener {
|
||||||
options: { showContent: true },
|
options: { showContent: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (
|
if (!priceInfoObject.data || !priceInfoObject.data.content)
|
||||||
priceInfoObject.data === undefined ||
|
|
||||||
priceInfoObject.data.content === undefined
|
|
||||||
)
|
|
||||||
throw new Error("Price not found on chain for price id " + priceId);
|
throw new Error("Price not found on chain for price id " + priceId);
|
||||||
|
|
||||||
if (priceInfoObject.data.content.dataType !== "moveObject")
|
if (priceInfoObject.data.content.dataType !== "moveObject")
|
||||||
throw new Error("fetched object datatype should be moveObject");
|
throw new Error("fetched object datatype should be moveObject");
|
||||||
|
|
||||||
const { magnitude, negative } =
|
const priceInfo =
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
priceInfoObject.data.content.fields.price_info.fields.price_feed.fields
|
priceInfoObject.data.content.fields.price_info.fields.price_feed.fields
|
||||||
.price.fields.price.fields;
|
.price.fields;
|
||||||
|
const { magnitude, negative } = priceInfo.price.fields;
|
||||||
|
|
||||||
const conf =
|
const conf = priceInfo.conf;
|
||||||
priceInfoObject.data.content.fields.price_info.fields.price_feed.fields
|
|
||||||
.price.fields.conf;
|
|
||||||
|
|
||||||
const timestamp =
|
const timestamp = priceInfo.timestamp;
|
||||||
priceInfoObject.data.content.fields.price_info.fields.price_feed.fields
|
|
||||||
.price.fields.timestamp;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
price: negative ? "-" + magnitude : magnitude,
|
price: negative ? "-" + magnitude : magnitude,
|
||||||
|
@ -114,7 +102,8 @@ export class SuiPriceListener extends ChainPriceListener {
|
||||||
*/
|
*/
|
||||||
export class SuiPricePusher implements IPricePusher {
|
export class SuiPricePusher implements IPricePusher {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly signer: RawSigner,
|
private readonly signer: Ed25519Keypair,
|
||||||
|
private readonly provider: SuiClient,
|
||||||
private priceServiceConnection: PriceServiceConnection,
|
private priceServiceConnection: PriceServiceConnection,
|
||||||
private pythPackageId: string,
|
private pythPackageId: string,
|
||||||
private pythStateId: string,
|
private pythStateId: string,
|
||||||
|
@ -135,7 +124,7 @@ export class SuiPricePusher implements IPricePusher {
|
||||||
* @returns package id
|
* @returns package id
|
||||||
*/
|
*/
|
||||||
static async getPackageId(
|
static async getPackageId(
|
||||||
provider: JsonRpcProvider,
|
provider: SuiClient,
|
||||||
objectId: ObjectId
|
objectId: ObjectId
|
||||||
): Promise<ObjectId> {
|
): Promise<ObjectId> {
|
||||||
const state = await provider
|
const state = await provider
|
||||||
|
@ -154,6 +143,8 @@ export class SuiPricePusher implements IPricePusher {
|
||||||
});
|
});
|
||||||
|
|
||||||
if ("upgrade_cap" in state) {
|
if ("upgrade_cap" in state) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
return state.upgrade_cap.fields.package;
|
return state.upgrade_cap.fields.package;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,10 +170,7 @@ export class SuiPricePusher implements IPricePusher {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new JsonRpcProvider(
|
const provider = new SuiClient({ url: endpoint });
|
||||||
new Connection({ fullnode: endpoint })
|
|
||||||
);
|
|
||||||
const signer = new RawSigner(keypair, provider);
|
|
||||||
const pythPackageId = await SuiPricePusher.getPackageId(
|
const pythPackageId = await SuiPricePusher.getPackageId(
|
||||||
provider,
|
provider,
|
||||||
pythStateId
|
pythStateId
|
||||||
|
@ -193,7 +181,8 @@ export class SuiPricePusher implements IPricePusher {
|
||||||
);
|
);
|
||||||
|
|
||||||
const gasPool = await SuiPricePusher.initializeGasPool(
|
const gasPool = await SuiPricePusher.initializeGasPool(
|
||||||
signer,
|
keypair,
|
||||||
|
provider,
|
||||||
numGasObjects
|
numGasObjects
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -204,7 +193,8 @@ export class SuiPricePusher implements IPricePusher {
|
||||||
);
|
);
|
||||||
|
|
||||||
return new SuiPricePusher(
|
return new SuiPricePusher(
|
||||||
signer,
|
keypair,
|
||||||
|
provider,
|
||||||
priceServiceConnection,
|
priceServiceConnection,
|
||||||
pythPackageId,
|
pythPackageId,
|
||||||
pythStateId,
|
pythStateId,
|
||||||
|
@ -282,15 +272,16 @@ export class SuiPricePusher implements IPricePusher {
|
||||||
try {
|
try {
|
||||||
tx.setGasPayment([gasObject]);
|
tx.setGasPayment([gasObject]);
|
||||||
tx.setGasBudget(this.gasBudget);
|
tx.setGasBudget(this.gasBudget);
|
||||||
const result = await this.signer.signAndExecuteTransactionBlock({
|
const result = await this.provider.signAndExecuteTransactionBlock({
|
||||||
|
signer: this.signer,
|
||||||
transactionBlock: tx,
|
transactionBlock: tx,
|
||||||
options: {
|
options: {
|
||||||
showEffects: true,
|
showEffects: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
nextGasObject = getTransactionEffects(result)
|
nextGasObject = result.effects?.mutated
|
||||||
?.mutated?.map((obj) => obj.reference)
|
?.map((obj) => obj.reference)
|
||||||
.find((ref) => ref.objectId === gasObject.objectId);
|
.find((ref) => ref.objectId === gasObject.objectId);
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
|
@ -308,7 +299,7 @@ export class SuiPricePusher implements IPricePusher {
|
||||||
} else {
|
} else {
|
||||||
// Refresh the coin object here in case the error is caused by an object version mismatch.
|
// Refresh the coin object here in case the error is caused by an object version mismatch.
|
||||||
nextGasObject = await SuiPricePusher.tryRefreshObjectReference(
|
nextGasObject = await SuiPricePusher.tryRefreshObjectReference(
|
||||||
this.signer.provider,
|
this.provider,
|
||||||
gasObject
|
gasObject
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -328,16 +319,18 @@ export class SuiPricePusher implements IPricePusher {
|
||||||
// This function will smash all coins owned by the signer into one, and then
|
// This function will smash all coins owned by the signer into one, and then
|
||||||
// split them equally into numGasObjects.
|
// split them equally into numGasObjects.
|
||||||
private static async initializeGasPool(
|
private static async initializeGasPool(
|
||||||
signer: RawSigner,
|
signer: Ed25519Keypair,
|
||||||
|
provider: SuiClient,
|
||||||
numGasObjects: number
|
numGasObjects: number
|
||||||
): Promise<SuiObjectRef[]> {
|
): Promise<SuiObjectRef[]> {
|
||||||
const signerAddress = await signer.getAddress();
|
const signerAddress = await signer.toSuiAddress();
|
||||||
|
|
||||||
const consolidatedCoin = await SuiPricePusher.mergeGasCoinsIntoOne(
|
const consolidatedCoin = await SuiPricePusher.mergeGasCoinsIntoOne(
|
||||||
signer,
|
signer,
|
||||||
|
provider,
|
||||||
signerAddress
|
signerAddress
|
||||||
);
|
);
|
||||||
const coinResult = await signer.provider.getObject({
|
const coinResult = await provider.getObject({
|
||||||
id: consolidatedCoin.objectId,
|
id: consolidatedCoin.objectId,
|
||||||
options: { showContent: true },
|
options: { showContent: true },
|
||||||
});
|
});
|
||||||
|
@ -347,6 +340,8 @@ export class SuiPricePusher implements IPricePusher {
|
||||||
coinResult.data.content &&
|
coinResult.data.content &&
|
||||||
coinResult.data.content.dataType == "moveObject"
|
coinResult.data.content.dataType == "moveObject"
|
||||||
) {
|
) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
balance = coinResult.data.content.fields.balance;
|
balance = coinResult.data.content.fields.balance;
|
||||||
} else throw new Error("Bad coin object");
|
} else throw new Error("Bad coin object");
|
||||||
const splitAmount =
|
const splitAmount =
|
||||||
|
@ -354,6 +349,7 @@ export class SuiPricePusher implements IPricePusher {
|
||||||
|
|
||||||
const gasPool = await SuiPricePusher.splitGasCoinEqually(
|
const gasPool = await SuiPricePusher.splitGasCoinEqually(
|
||||||
signer,
|
signer,
|
||||||
|
provider,
|
||||||
signerAddress,
|
signerAddress,
|
||||||
Number(splitAmount),
|
Number(splitAmount),
|
||||||
numGasObjects,
|
numGasObjects,
|
||||||
|
@ -367,16 +363,16 @@ export class SuiPricePusher implements IPricePusher {
|
||||||
// of the object. Return the provided object reference if an error occurs or the object could not
|
// of the object. Return the provided object reference if an error occurs or the object could not
|
||||||
// be retrieved.
|
// be retrieved.
|
||||||
private static async tryRefreshObjectReference(
|
private static async tryRefreshObjectReference(
|
||||||
provider: JsonRpcProvider,
|
provider: SuiClient,
|
||||||
ref: SuiObjectRef
|
ref: SuiObjectRef
|
||||||
): Promise<SuiObjectRef> {
|
): Promise<SuiObjectRef> {
|
||||||
try {
|
try {
|
||||||
const objectResponse = await provider.getObject({ id: ref.objectId });
|
const objectResponse = await provider.getObject({ id: ref.objectId });
|
||||||
if (objectResponse.data !== undefined) {
|
if (objectResponse.data !== undefined) {
|
||||||
return {
|
return {
|
||||||
digest: objectResponse.data.digest,
|
digest: objectResponse.data!.digest,
|
||||||
objectId: objectResponse.data.objectId,
|
objectId: objectResponse.data!.objectId,
|
||||||
version: objectResponse.data.version,
|
version: objectResponse.data!.version,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return ref;
|
return ref;
|
||||||
|
@ -387,7 +383,7 @@ export class SuiPricePusher implements IPricePusher {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async getAllGasCoins(
|
private static async getAllGasCoins(
|
||||||
provider: JsonRpcProvider,
|
provider: SuiClient,
|
||||||
owner: SuiAddress
|
owner: SuiAddress
|
||||||
): Promise<SuiObjectRef[]> {
|
): Promise<SuiObjectRef[]> {
|
||||||
let hasNextPage = true;
|
let hasNextPage = true;
|
||||||
|
@ -420,7 +416,8 @@ export class SuiPricePusher implements IPricePusher {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async splitGasCoinEqually(
|
private static async splitGasCoinEqually(
|
||||||
signer: RawSigner,
|
signer: Ed25519Keypair,
|
||||||
|
provider: SuiClient,
|
||||||
signerAddress: SuiAddress,
|
signerAddress: SuiAddress,
|
||||||
splitAmount: number,
|
splitAmount: number,
|
||||||
numGasObjects: number,
|
numGasObjects: number,
|
||||||
|
@ -438,17 +435,18 @@ export class SuiPricePusher implements IPricePusher {
|
||||||
tx.pure(signerAddress)
|
tx.pure(signerAddress)
|
||||||
);
|
);
|
||||||
tx.setGasPayment([gasCoin]);
|
tx.setGasPayment([gasCoin]);
|
||||||
const result = await signer.signAndExecuteTransactionBlock({
|
const result = await provider.signAndExecuteTransactionBlock({
|
||||||
|
signer,
|
||||||
transactionBlock: tx,
|
transactionBlock: tx,
|
||||||
options: { showEffects: true },
|
options: { showEffects: true },
|
||||||
});
|
});
|
||||||
const error = getExecutionStatusError(result);
|
const error = result?.effects?.status.error;
|
||||||
if (error) {
|
if (error) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Failed to initialize gas pool: ${error}. Try re-running the script`
|
`Failed to initialize gas pool: ${error}. Try re-running the script`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const newCoins = getCreatedObjects(result)!.map((obj) => obj.reference);
|
const newCoins = result.effects!.created!.map((obj) => obj.reference);
|
||||||
if (newCoins.length !== numGasObjects) {
|
if (newCoins.length !== numGasObjects) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Failed to initialize gas pool. Expected ${numGasObjects}, got: ${newCoins}`
|
`Failed to initialize gas pool. Expected ${numGasObjects}, got: ${newCoins}`
|
||||||
|
@ -458,13 +456,11 @@ export class SuiPricePusher implements IPricePusher {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async mergeGasCoinsIntoOne(
|
private static async mergeGasCoinsIntoOne(
|
||||||
signer: RawSigner,
|
signer: Ed25519Keypair,
|
||||||
|
provider: SuiClient,
|
||||||
owner: SuiAddress
|
owner: SuiAddress
|
||||||
): Promise<SuiObjectRef> {
|
): Promise<SuiObjectRef> {
|
||||||
const gasCoins = await SuiPricePusher.getAllGasCoins(
|
const gasCoins = await SuiPricePusher.getAllGasCoins(provider, owner);
|
||||||
signer.provider,
|
|
||||||
owner
|
|
||||||
);
|
|
||||||
// skip merging if there is only one coin
|
// skip merging if there is only one coin
|
||||||
if (gasCoins.length === 1) {
|
if (gasCoins.length === 1) {
|
||||||
return gasCoins[0];
|
return gasCoins[0];
|
||||||
|
@ -486,7 +482,8 @@ export class SuiPricePusher implements IPricePusher {
|
||||||
mergeTx.setGasPayment(coins);
|
mergeTx.setGasPayment(coins);
|
||||||
let mergeResult;
|
let mergeResult;
|
||||||
try {
|
try {
|
||||||
mergeResult = await signer.signAndExecuteTransactionBlock({
|
mergeResult = await provider.signAndExecuteTransactionBlock({
|
||||||
|
signer,
|
||||||
transactionBlock: mergeTx,
|
transactionBlock: mergeTx,
|
||||||
options: { showEffects: true },
|
options: { showEffects: true },
|
||||||
});
|
});
|
||||||
|
@ -507,15 +504,13 @@ export class SuiPricePusher implements IPricePusher {
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
const error = getExecutionStatusError(mergeResult);
|
const error = mergeResult?.effects?.status.error;
|
||||||
if (error) {
|
if (error) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Failed to merge coins when initializing gas pool: ${error}. Try re-running the script`
|
`Failed to merge coins when initializing gas pool: ${error}. Try re-running the script`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
finalCoin = getTransactionEffects(mergeResult)!.mutated!.map(
|
finalCoin = mergeResult.effects!.mutated!.map((obj) => obj.reference)[0];
|
||||||
(obj) => obj.reference
|
|
||||||
)[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return finalCoin as SuiObjectRef;
|
return finalCoin as SuiObjectRef;
|
||||||
|
|
|
@ -12,9 +12,10 @@
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"rimraf": "^5.0.0",
|
|
||||||
"@pythnetwork/cosmwasm-deploy-tools": "*",
|
"@pythnetwork/cosmwasm-deploy-tools": "*",
|
||||||
"contract_manager": "*",
|
"contract_manager": "*",
|
||||||
|
"rimraf": "^5.0.0",
|
||||||
|
"typescript": "^5.3.3",
|
||||||
"yargs": "^17.0.1"
|
"yargs": "^17.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@pythnetwork/pyth-sui-js",
|
"name": "@pythnetwork/pyth-sui-js",
|
||||||
"version": "1.2.5",
|
"version": "2.0.0",
|
||||||
"description": "Pyth Network Sui Utilities",
|
"description": "Pyth Network Sui Utilities",
|
||||||
"homepage": "https://pyth.network",
|
"homepage": "https://pyth.network",
|
||||||
"author": {
|
"author": {
|
||||||
|
@ -48,13 +48,13 @@
|
||||||
"jest": "^29.4.1",
|
"jest": "^29.4.1",
|
||||||
"prettier": "^2.6.2",
|
"prettier": "^2.6.2",
|
||||||
"ts-jest": "^29.0.5",
|
"ts-jest": "^29.0.5",
|
||||||
"typescript": "^4.6.3",
|
"typescript": "^5.3.3",
|
||||||
"web3": "^1.8.2",
|
"web3": "^1.8.2",
|
||||||
"yargs": "^17.0.20"
|
"yargs": "^17.0.20"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@mysten/sui.js": "^0.49.1",
|
||||||
"@pythnetwork/price-service-client": "*",
|
"@pythnetwork/price-service-client": "*",
|
||||||
"@mysten/sui.js": "^0.37.1",
|
|
||||||
"buffer": "^6.0.3"
|
"buffer": "^6.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,12 @@
|
||||||
import {
|
import { SuiClient } from "@mysten/sui.js/client";
|
||||||
builder,
|
import { SUI_CLOCK_OBJECT_ID } from "@mysten/sui.js/utils";
|
||||||
JsonRpcProvider,
|
import { TransactionBlock } from "@mysten/sui.js/transactions";
|
||||||
ObjectId,
|
import { bcs } from "@mysten/sui.js/bcs";
|
||||||
SUI_CLOCK_OBJECT_ID,
|
import { HexString } from "@pythnetwork/price-service-client";
|
||||||
TransactionBlock,
|
|
||||||
} from "@mysten/sui.js";
|
|
||||||
import {
|
|
||||||
HexString,
|
|
||||||
isAccumulatorUpdateData,
|
|
||||||
parseAccumulatorUpdateData,
|
|
||||||
} from "@pythnetwork/price-service-client";
|
|
||||||
import { Buffer } from "buffer";
|
import { Buffer } from "buffer";
|
||||||
|
|
||||||
const MAX_ARGUMENT_SIZE = 16 * 1024;
|
const MAX_ARGUMENT_SIZE = 16 * 1024;
|
||||||
|
export type ObjectId = string;
|
||||||
|
|
||||||
export class SuiPythClient {
|
export class SuiPythClient {
|
||||||
private pythPackageId: ObjectId | undefined;
|
private pythPackageId: ObjectId | undefined;
|
||||||
|
@ -21,7 +15,7 @@ export class SuiPythClient {
|
||||||
private priceFeedObjectIdCache: Map<HexString, ObjectId> = new Map();
|
private priceFeedObjectIdCache: Map<HexString, ObjectId> = new Map();
|
||||||
private baseUpdateFee: number | undefined;
|
private baseUpdateFee: number | undefined;
|
||||||
constructor(
|
constructor(
|
||||||
public provider: JsonRpcProvider,
|
public provider: SuiClient,
|
||||||
public pythStateId: ObjectId,
|
public pythStateId: ObjectId,
|
||||||
public wormholeStateId: ObjectId
|
public wormholeStateId: ObjectId
|
||||||
) {
|
) {
|
||||||
|
@ -41,6 +35,8 @@ export class SuiPythClient {
|
||||||
result.data.content.dataType !== "moveObject"
|
result.data.content.dataType !== "moveObject"
|
||||||
)
|
)
|
||||||
throw new Error("Unable to fetch pyth state object");
|
throw new Error("Unable to fetch pyth state object");
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
this.baseUpdateFee = result.data.content.fields.base_update_fee as number;
|
this.baseUpdateFee = result.data.content.fields.base_update_fee as number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,11 +61,14 @@ export class SuiPythClient {
|
||||||
if (result.data?.content?.dataType == "moveObject") {
|
if (result.data?.content?.dataType == "moveObject") {
|
||||||
return result.data.content.fields;
|
return result.data.content.fields;
|
||||||
}
|
}
|
||||||
|
console.log(result.data?.content);
|
||||||
|
|
||||||
throw new Error("not move object");
|
throw new Error(`Cannot fetch package id for object ${objectId}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
if ("upgrade_cap" in state) {
|
if ("upgrade_cap" in state) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
return state.upgrade_cap.fields.package;
|
return state.upgrade_cap.fields.package;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +89,7 @@ export class SuiPythClient {
|
||||||
arguments: [
|
arguments: [
|
||||||
tx.object(this.wormholeStateId),
|
tx.object(this.wormholeStateId),
|
||||||
tx.pure(
|
tx.pure(
|
||||||
builder
|
bcs
|
||||||
.ser("vector<u8>", Array.from(vaa), {
|
.ser("vector<u8>", Array.from(vaa), {
|
||||||
maxSize: MAX_ARGUMENT_SIZE,
|
maxSize: MAX_ARGUMENT_SIZE,
|
||||||
})
|
})
|
||||||
|
@ -115,49 +114,31 @@ export class SuiPythClient {
|
||||||
updates: Buffer[],
|
updates: Buffer[],
|
||||||
feedIds: HexString[]
|
feedIds: HexString[]
|
||||||
): Promise<ObjectId[]> {
|
): Promise<ObjectId[]> {
|
||||||
const wormholePackageId = await this.getWormholePackageId();
|
|
||||||
const packageId = await this.getPythPackageId();
|
const packageId = await this.getPythPackageId();
|
||||||
|
|
||||||
let priceUpdatesHotPotato;
|
let priceUpdatesHotPotato;
|
||||||
if (updates.every((update) => isAccumulatorUpdateData(update))) {
|
if (updates.length > 1) {
|
||||||
if (updates.length > 1) {
|
throw new Error(
|
||||||
throw new Error(
|
"SDK does not support sending multiple accumulator messages in a single transaction"
|
||||||
"SDK does not support sending multiple accumulator messages in a single transaction"
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
const vaa = parseAccumulatorUpdateData(updates[0]).vaa;
|
|
||||||
const verifiedVaas = await this.verifyVaas([vaa], tx);
|
|
||||||
[priceUpdatesHotPotato] = tx.moveCall({
|
|
||||||
target: `${packageId}::pyth::create_authenticated_price_infos_using_accumulator`,
|
|
||||||
arguments: [
|
|
||||||
tx.object(this.pythStateId),
|
|
||||||
tx.pure(
|
|
||||||
builder
|
|
||||||
.ser("vector<u8>", Array.from(updates[0]), {
|
|
||||||
maxSize: MAX_ARGUMENT_SIZE,
|
|
||||||
})
|
|
||||||
.toBytes()
|
|
||||||
),
|
|
||||||
verifiedVaas[0],
|
|
||||||
tx.object(SUI_CLOCK_OBJECT_ID),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
} else if (updates.every((vaa) => !isAccumulatorUpdateData(vaa))) {
|
|
||||||
const verifiedVaas = await this.verifyVaas(updates, tx);
|
|
||||||
[priceUpdatesHotPotato] = tx.moveCall({
|
|
||||||
target: `${packageId}::pyth::create_price_infos_hot_potato`,
|
|
||||||
arguments: [
|
|
||||||
tx.object(this.pythStateId),
|
|
||||||
tx.makeMoveVec({
|
|
||||||
type: `${wormholePackageId}::vaa::VAA`,
|
|
||||||
objects: verifiedVaas,
|
|
||||||
}),
|
|
||||||
tx.object(SUI_CLOCK_OBJECT_ID),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
throw new Error("Can't mix accumulator and non-accumulator messages");
|
|
||||||
}
|
}
|
||||||
|
const vaa = this.extractVaaBytesFromAccumulatorMessage(updates[0]);
|
||||||
|
const verifiedVaas = await this.verifyVaas([vaa], tx);
|
||||||
|
[priceUpdatesHotPotato] = tx.moveCall({
|
||||||
|
target: `${packageId}::pyth::create_authenticated_price_infos_using_accumulator`,
|
||||||
|
arguments: [
|
||||||
|
tx.object(this.pythStateId),
|
||||||
|
tx.pure(
|
||||||
|
bcs
|
||||||
|
.ser("vector<u8>", Array.from(updates[0]), {
|
||||||
|
maxSize: MAX_ARGUMENT_SIZE,
|
||||||
|
})
|
||||||
|
.toBytes()
|
||||||
|
),
|
||||||
|
verifiedVaas[0],
|
||||||
|
tx.object(SUI_CLOCK_OBJECT_ID),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
const priceInfoObjects: ObjectId[] = [];
|
const priceInfoObjects: ObjectId[] = [];
|
||||||
const baseUpdateFee = await this.getBaseUpdateFee();
|
const baseUpdateFee = await this.getBaseUpdateFee();
|
||||||
|
@ -194,47 +175,29 @@ export class SuiPythClient {
|
||||||
return priceInfoObjects;
|
return priceInfoObjects;
|
||||||
}
|
}
|
||||||
async createPriceFeed(tx: TransactionBlock, updates: Buffer[]) {
|
async createPriceFeed(tx: TransactionBlock, updates: Buffer[]) {
|
||||||
const wormholePackageId = await this.getWormholePackageId();
|
|
||||||
const packageId = await this.getPythPackageId();
|
const packageId = await this.getPythPackageId();
|
||||||
if (updates.every((update) => isAccumulatorUpdateData(update))) {
|
if (updates.length > 1) {
|
||||||
if (updates.length > 1) {
|
throw new Error(
|
||||||
throw new Error(
|
"SDK does not support sending multiple accumulator messages in a single transaction"
|
||||||
"SDK does not support sending multiple accumulator messages in a single transaction"
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
const vaa = parseAccumulatorUpdateData(updates[0]).vaa;
|
|
||||||
const verifiedVaas = await this.verifyVaas([vaa], tx);
|
|
||||||
tx.moveCall({
|
|
||||||
target: `${packageId}::pyth::create_price_feeds_using_accumulator`,
|
|
||||||
arguments: [
|
|
||||||
tx.object(this.pythStateId),
|
|
||||||
tx.pure(
|
|
||||||
builder
|
|
||||||
.ser("vector<u8>", Array.from(updates[0]), {
|
|
||||||
maxSize: MAX_ARGUMENT_SIZE,
|
|
||||||
})
|
|
||||||
.toBytes()
|
|
||||||
),
|
|
||||||
verifiedVaas[0],
|
|
||||||
tx.object(SUI_CLOCK_OBJECT_ID),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
} else if (updates.every((vaa) => !isAccumulatorUpdateData(vaa))) {
|
|
||||||
const verifiedVaas = await this.verifyVaas(updates, tx);
|
|
||||||
tx.moveCall({
|
|
||||||
target: `${packageId}::pyth::create_price_feeds`,
|
|
||||||
arguments: [
|
|
||||||
tx.object(this.pythStateId),
|
|
||||||
tx.makeMoveVec({
|
|
||||||
type: `${wormholePackageId}::vaa::VAA`,
|
|
||||||
objects: verifiedVaas,
|
|
||||||
}),
|
|
||||||
tx.object(SUI_CLOCK_OBJECT_ID),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
throw new Error("Can't mix accumulator and non-accumulator messages");
|
|
||||||
}
|
}
|
||||||
|
const vaa = this.extractVaaBytesFromAccumulatorMessage(updates[0]);
|
||||||
|
const verifiedVaas = await this.verifyVaas([vaa], tx);
|
||||||
|
tx.moveCall({
|
||||||
|
target: `${packageId}::pyth::create_price_feeds_using_accumulator`,
|
||||||
|
arguments: [
|
||||||
|
tx.object(this.pythStateId),
|
||||||
|
tx.pure(
|
||||||
|
bcs
|
||||||
|
.ser("vector<u8>", Array.from(updates[0]), {
|
||||||
|
maxSize: MAX_ARGUMENT_SIZE,
|
||||||
|
})
|
||||||
|
.toBytes()
|
||||||
|
),
|
||||||
|
verifiedVaas[0],
|
||||||
|
tx.object(SUI_CLOCK_OBJECT_ID),
|
||||||
|
],
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -282,6 +245,8 @@ export class SuiPythClient {
|
||||||
}
|
}
|
||||||
this.priceFeedObjectIdCache.set(
|
this.priceFeedObjectIdCache.set(
|
||||||
normalizedFeedId,
|
normalizedFeedId,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
result.data.content.fields.value
|
result.data.content.fields.value
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -315,4 +280,22 @@ export class SuiPythClient {
|
||||||
}
|
}
|
||||||
return this.priceTableInfo;
|
return this.priceTableInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains the vaa bytes embedded in an accumulator message.
|
||||||
|
* @param accumulatorMessage - the accumulator price update message
|
||||||
|
* @returns vaa bytes as a uint8 array
|
||||||
|
*/
|
||||||
|
extractVaaBytesFromAccumulatorMessage(accumulatorMessage: Buffer): Buffer {
|
||||||
|
// the first 6 bytes in the accumulator message encode the header, major, and minor bytes
|
||||||
|
// we ignore them, since we are only interested in the VAA bytes
|
||||||
|
const trailingPayloadSize = accumulatorMessage.readUint8(6);
|
||||||
|
const vaaSizeOffset =
|
||||||
|
7 + // header bytes (header(4) + major(1) + minor(1) + trailing payload size(1))
|
||||||
|
trailingPayloadSize + // trailing payload (variable number of bytes)
|
||||||
|
1; // proof_type (1 byte)
|
||||||
|
const vaaSize = accumulatorMessage.readUint16BE(vaaSizeOffset);
|
||||||
|
const vaaOffset = vaaSizeOffset + 2;
|
||||||
|
return accumulatorMessage.subarray(vaaOffset, vaaOffset + vaaSize);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
import yargs from "yargs";
|
import yargs from "yargs";
|
||||||
import { hideBin } from "yargs/helpers";
|
import { hideBin } from "yargs/helpers";
|
||||||
import {
|
import { SuiClient } from "@mysten/sui.js/client";
|
||||||
Connection,
|
import { TransactionBlock } from "@mysten/sui.js/transactions";
|
||||||
Ed25519Keypair,
|
import { Ed25519Keypair } from "@mysten/sui.js/keypairs/ed25519";
|
||||||
JsonRpcProvider,
|
|
||||||
RawSigner,
|
|
||||||
TransactionBlock,
|
|
||||||
} from "@mysten/sui.js";
|
|
||||||
import { Buffer } from "buffer";
|
import { Buffer } from "buffer";
|
||||||
import { SuiPythClient } from "../client";
|
import { SuiPythClient } from "../client";
|
||||||
import { SuiPriceServiceConnection } from "../index";
|
import { SuiPriceServiceConnection } from "../index";
|
||||||
|
@ -41,7 +38,7 @@ const argvPromise = yargs(hideBin(process.argv))
|
||||||
}).argv;
|
}).argv;
|
||||||
|
|
||||||
export function getProvider(url: string) {
|
export function getProvider(url: string) {
|
||||||
return new JsonRpcProvider(new Connection({ fullnode: url }));
|
return new SuiClient({ url });
|
||||||
}
|
}
|
||||||
async function run() {
|
async function run() {
|
||||||
if (process.env.SUI_KEY === undefined) {
|
if (process.env.SUI_KEY === undefined) {
|
||||||
|
@ -53,29 +50,47 @@ async function run() {
|
||||||
// Fetch the latest price feed update data from the Price Service
|
// Fetch the latest price feed update data from the Price Service
|
||||||
const connection = new SuiPriceServiceConnection(argv["hermes"]);
|
const connection = new SuiPriceServiceConnection(argv["hermes"]);
|
||||||
const feeds = argv["feed-id"] as string[];
|
const feeds = argv["feed-id"] as string[];
|
||||||
const priceFeedUpdateData = await connection.getPriceFeedsUpdateData(feeds);
|
|
||||||
|
|
||||||
const provider = getProvider(argv["full-node"]);
|
const provider = getProvider(argv["full-node"]);
|
||||||
const wormholeStateId = argv["wormhole-state-id"];
|
const wormholeStateId = argv["wormhole-state-id"];
|
||||||
const pythStateId = argv["pyth-state-id"];
|
const pythStateId = argv["pyth-state-id"];
|
||||||
|
|
||||||
const client = new SuiPythClient(provider, pythStateId, wormholeStateId);
|
const client = new SuiPythClient(provider, pythStateId, wormholeStateId);
|
||||||
|
const newFeeds = [];
|
||||||
|
const existingFeeds = [];
|
||||||
|
for (const feed of feeds) {
|
||||||
|
if ((await client.getPriceFeedObjectId(feed)) == undefined) {
|
||||||
|
newFeeds.push(feed);
|
||||||
|
} else {
|
||||||
|
existingFeeds.push(feed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log({
|
||||||
|
newFeeds,
|
||||||
|
existingFeeds,
|
||||||
|
});
|
||||||
const tx = new TransactionBlock();
|
const tx = new TransactionBlock();
|
||||||
await client.updatePriceFeeds(tx, priceFeedUpdateData, feeds);
|
if (existingFeeds.length > 0) {
|
||||||
|
const updateData = await connection.getPriceFeedsUpdateData(existingFeeds);
|
||||||
|
await client.updatePriceFeeds(tx, updateData, existingFeeds);
|
||||||
|
}
|
||||||
|
if (newFeeds.length > 0) {
|
||||||
|
const updateData = await connection.getPriceFeedsUpdateData(newFeeds);
|
||||||
|
await client.createPriceFeed(tx, updateData);
|
||||||
|
}
|
||||||
|
|
||||||
const wallet = new RawSigner(
|
const wallet = Ed25519Keypair.fromSecretKey(
|
||||||
Ed25519Keypair.fromSecretKey(Buffer.from(process.env.SUI_KEY, "hex")),
|
Buffer.from(process.env.SUI_KEY, "hex")
|
||||||
provider
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const txBlock = {
|
const result = await provider.signAndExecuteTransactionBlock({
|
||||||
|
signer: wallet,
|
||||||
transactionBlock: tx,
|
transactionBlock: tx,
|
||||||
options: {
|
options: {
|
||||||
showEffects: true,
|
showEffects: true,
|
||||||
showEvents: true,
|
showEvents: true,
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
const result = await wallet.signAndExecuteTransactionBlock(txBlock);
|
|
||||||
console.dir(result, { depth: null });
|
console.dir(result, { depth: null });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue