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