@pythnetwork/pyth-lazer-sui-js 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -20,16 +20,22 @@ A runnable example is provided at `examples/FetchAndVerifyUpdate.ts`. It:
20
20
 
21
21
  ### Run the example
22
22
 
23
- Install `tsx` to run TypeScript scripts:
24
-
25
23
  ```sh
26
- npm install -g tsx
27
- ```
24
+ # Your Sui private key in Bech32 format
25
+ # SUI_KEY=
28
26
 
29
- Execute the example script:
27
+ # Lazer contract state ID
28
+ # STATE_ID=
30
29
 
31
- ```sh
32
- SUI_KEY=<YOUR_SUI_PRIVATE_KEY> pnpm -F @pythnetwork/pyth-lazer-sui-js example:fetch-and-verify --fullnodeUrl <SUI_FULLNODE_URL> --packageId <PYTH_LAZER_PACKAGE_ID> --stateObjectId <PYTH_LAZER_STATE_OBJECT_ID> --token <LAZER_TOKEN>
30
+ # Your Lazer API token
31
+ # LAZER_TOKEN=
32
+
33
+ SUI_FULLNODE_URL="https://fullnode.mainnet.sui.io:443"
34
+
35
+ pnpm run example:fetch-and-verify \
36
+ --fullnode-url "$SUI_FULLNODE_URL" \
37
+ --state-id "$STATE_ID" \
38
+ --lazer-token "$LAZER_TOKEN"
33
39
  ```
34
40
 
35
41
  The script's core logic is summarized below:
@@ -37,38 +43,40 @@ The script's core logic is summarized below:
37
43
  ```ts
38
44
  import { SuiClient } from "@mysten/sui/client";
39
45
  import { Transaction } from "@mysten/sui/transactions";
40
- import { SuiLazerClient } from "@pythnetwork/pyth-lazer-sui-js";
41
-
42
- // Prepare Mysten Sui client
43
- const provider = new SuiClient({ url: "<sui-fullnode-url>" });
44
-
45
- // Create SDK client
46
- const client = new SuiLazerClient(provider);
47
-
48
- // Obtain a Lazer leEcdsa payload using @pythnetwork/pyth-lazer-sdk.
49
- // See examples/FetchAndVerifyUpdate.ts for a runnable end-to-end example.
50
- const leEcdsa: Buffer = /* fetch via @pythnetwork/pyth-lazer-sdk */ Buffer.from(
51
- [],
52
- );
46
+ import { PythLazerClient } from "@pythnetwork/pyth-lazer-sdk";
47
+ import { addParseAndVerifyLeEcdsaUpdateCall } from "@pythnetwork/pyth-lazer-sui-js";
48
+
49
+ // 1. Fetch the price update from Pyth Lazer in "leEcdsa" format:
50
+ const lazer = await PythLazerClient.create({ token: lazerToken });
51
+ const latestPrice = await lazer.getLatestPrice({
52
+ priceFeedIds: [1],
53
+ properties: ["price", "bestBidPrice", "bestAskPrice", "exponent"],
54
+ formats: ["leEcdsa"],
55
+ channel: "fixed_rate@200ms",
56
+ jsonBinaryEncoding: "hex",
57
+ });
58
+ const update = Buffer.from(latestPrice.leEcdsa?.data ?? "", "hex");
53
59
 
54
- // Build transaction calling parse_and_verify_le_ecdsa_update
60
+ // 2. Create a new Sui transaction:
61
+ const client = new SuiClient({ url: fullnodeUrl });
55
62
  const tx = new Transaction();
56
- const packageId = "<pyth_lazer_package_id>";
57
- const stateObjectId = "<pyth_lazer_state_object_id>";
58
63
 
59
- const updateVal = client.addParseAndVerifyLeEcdsaUpdateCall({
64
+ // 3. Add the parse and verify call:
65
+ const verifiedUpdate = await addParseAndVerifyLeEcdsaUpdateCall({
66
+ client,
60
67
  tx,
61
- packageId,
62
- stateObjectId,
63
- updateBytes: leEcdsa,
68
+ stateObjectId: stateId,
69
+ update,
64
70
  });
65
71
 
66
- // Sign and execute the transaction using your signer.
67
- ```
68
-
69
- ## Notes
72
+ // 4. Consume `verifiedUpdate` in your own contract with additional calls...
70
73
 
71
- - FIXME: Automatic `packageId` management is coming soon. The Lazer contract doesn't support upgradeability yet.
74
+ // 5. Sign and execute the transaction:
75
+ const result = await client.signAndExecuteTransaction({
76
+ transaction: tx,
77
+ signer,
78
+ });
79
+ ```
72
80
 
73
81
  ## References
74
82
 
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "addParseAndVerifyLeEcdsaUpdateCall", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return addParseAndVerifyLeEcdsaUpdateCall;
9
+ }
10
+ });
11
+ async function addParseAndVerifyLeEcdsaUpdateCall(opts) {
12
+ const { client, tx, stateObjectId, update } = opts;
13
+ const latestPackageId = await getLatestPackageId(client, stateObjectId);
14
+ return tx.moveCall({
15
+ target: `${latestPackageId}::pyth_lazer::parse_and_verify_le_ecdsa_update`,
16
+ arguments: [
17
+ tx.object(stateObjectId),
18
+ tx.object.clock(),
19
+ tx.pure.vector("u8", update)
20
+ ]
21
+ });
22
+ }
23
+ async function getLatestPackageId(client, stateObjectId) {
24
+ const { data: stateObject, error } = await client.getObject({
25
+ id: stateObjectId,
26
+ options: {
27
+ showContent: true
28
+ }
29
+ });
30
+ if (!stateObject?.content || error) {
31
+ throw new Error(`Failed to get Sui Lazer State: ${error?.code ?? "undefined"}`);
32
+ }
33
+ if (stateObject.content.dataType !== "moveObject") {
34
+ throw new Error(`Sui Lazer State must be an object, got: ${stateObject.content.dataType}`);
35
+ }
36
+ const state = stateObject.content;
37
+ if (!hasStructField(state, "upgrade_cap")) {
38
+ throw new Error("Missing 'upgrade_cap' in Sui Lazer State");
39
+ }
40
+ const upgradeCap = state.fields.upgrade_cap;
41
+ if (!hasStructField(upgradeCap, "package") || typeof upgradeCap.fields.package !== "string") {
42
+ throw new Error("Could not find 'package' string in Sui Lazer UpgradeCap");
43
+ }
44
+ return upgradeCap.fields.package;
45
+ }
46
+ function hasStructField(value, name) {
47
+ return hasProperty(value, "fields") && hasProperty(value.fields, name);
48
+ }
49
+ function hasProperty(value, name) {
50
+ return typeof value === "object" && !!value && name in value;
51
+ }
@@ -0,0 +1,8 @@
1
+ import type { SuiClient } from "@mysten/sui/client";
2
+ import { Transaction } from "@mysten/sui/transactions";
3
+ export declare function addParseAndVerifyLeEcdsaUpdateCall(opts: {
4
+ client: SuiClient;
5
+ tx: Transaction;
6
+ stateObjectId: string;
7
+ update: Uint8Array;
8
+ }): Promise<import("@mysten/sui/transactions").TransactionResult>;
@@ -0,0 +1,8 @@
1
+ import type { SuiClient } from "@mysten/sui/client";
2
+ import { Transaction } from "@mysten/sui/transactions";
3
+ export declare function addParseAndVerifyLeEcdsaUpdateCall(opts: {
4
+ client: SuiClient;
5
+ tx: Transaction;
6
+ stateObjectId: string;
7
+ update: Uint8Array;
8
+ }): Promise<import("@mysten/sui/transactions").TransactionResult>;
@@ -0,0 +1,41 @@
1
+ export async function addParseAndVerifyLeEcdsaUpdateCall(opts) {
2
+ const { client, tx, stateObjectId, update } = opts;
3
+ const latestPackageId = await getLatestPackageId(client, stateObjectId);
4
+ return tx.moveCall({
5
+ target: `${latestPackageId}::pyth_lazer::parse_and_verify_le_ecdsa_update`,
6
+ arguments: [
7
+ tx.object(stateObjectId),
8
+ tx.object.clock(),
9
+ tx.pure.vector("u8", update)
10
+ ]
11
+ });
12
+ }
13
+ async function getLatestPackageId(client, stateObjectId) {
14
+ const { data: stateObject, error } = await client.getObject({
15
+ id: stateObjectId,
16
+ options: {
17
+ showContent: true
18
+ }
19
+ });
20
+ if (!stateObject?.content || error) {
21
+ throw new Error(`Failed to get Sui Lazer State: ${error?.code ?? "undefined"}`);
22
+ }
23
+ if (stateObject.content.dataType !== "moveObject") {
24
+ throw new Error(`Sui Lazer State must be an object, got: ${stateObject.content.dataType}`);
25
+ }
26
+ const state = stateObject.content;
27
+ if (!hasStructField(state, "upgrade_cap")) {
28
+ throw new Error("Missing 'upgrade_cap' in Sui Lazer State");
29
+ }
30
+ const upgradeCap = state.fields.upgrade_cap;
31
+ if (!hasStructField(upgradeCap, "package") || typeof upgradeCap.fields.package !== "string") {
32
+ throw new Error("Could not find 'package' string in Sui Lazer UpgradeCap");
33
+ }
34
+ return upgradeCap.fields.package;
35
+ }
36
+ function hasStructField(value, name) {
37
+ return hasProperty(value, "fields") && hasProperty(value.fields, name);
38
+ }
39
+ function hasProperty(value, name) {
40
+ return typeof value === "object" && !!value && name in value;
41
+ }
package/package.json CHANGED
@@ -1,53 +1,67 @@
1
1
  {
2
2
  "name": "@pythnetwork/pyth-lazer-sui-js",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "TypeScript SDK for the Pyth Lazer Sui contract",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
7
- "engines": {
8
- "node": ">=22.14.0"
9
- },
10
7
  "files": [
11
- "dist/**/*"
8
+ "dist/**"
12
9
  ],
13
- "exports": {
14
- "./client": {
15
- "require": {
16
- "types": "./dist/cjs/client.d.ts",
17
- "default": "./dist/cjs/client.cjs"
18
- },
19
- "import": {
20
- "types": "./dist/esm/client.d.ts",
21
- "default": "./dist/esm/client.mjs"
22
- }
23
- },
24
- "./package.json": "./package.json"
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/pyth-network/pyth-crosschain",
13
+ "directory": "lazer/contracts/pyth-lazer-sui-js"
25
14
  },
26
- "sideEffects": false,
27
- "dependencies": {
28
- "@mysten/sui": "^1.3.0",
29
- "@types/yargs": "^17.0.33",
30
- "yargs": "^18.0.0",
31
- "@pythnetwork/pyth-lazer-sdk": "5.2.0"
15
+ "engines": {
16
+ "node": "^24.0.0"
32
17
  },
18
+ "sideEffects": false,
33
19
  "devDependencies": {
34
20
  "@cprussin/eslint-config": "^4.0.2",
21
+ "@cprussin/prettier-config": "^2.2.2",
35
22
  "@cprussin/tsconfig": "^3.1.2",
23
+ "@pythnetwork/jest-config": "",
24
+ "@types/jest": "^29.5.14",
36
25
  "@types/node": "^22.14.0",
26
+ "@types/yargs": "^17.0.33",
37
27
  "eslint": "^9.23.0",
38
- "prettier": "^3.5.3"
28
+ "jest": "^29.7.0",
29
+ "tsx": "4.20.6",
30
+ "yargs": "^18.0.0",
31
+ "@pythnetwork/pyth-lazer-sdk": "5.2.1"
32
+ },
33
+ "dependencies": {
34
+ "@mysten/sui": "^1.3.0"
39
35
  },
36
+ "private": false,
40
37
  "publishConfig": {
41
38
  "access": "public"
42
39
  },
40
+ "exports": {
41
+ ".": {
42
+ "require": {
43
+ "types": "./dist/cjs/index.d.ts",
44
+ "default": "./dist/cjs/index.cjs"
45
+ },
46
+ "import": {
47
+ "types": "./dist/esm/index.d.ts",
48
+ "default": "./dist/esm/index.mjs"
49
+ }
50
+ },
51
+ "./package.json": "./package.json"
52
+ },
53
+ "module": "./dist/esm/index.mjs",
54
+ "types": "./dist/cjs/index.d.ts",
55
+ "main": "./dist/cjs/index.cjs",
43
56
  "scripts": {
44
- "build": "ts-duality --clean",
57
+ "build": "ts-duality --copyOtherFiles",
58
+ "clean": "rm -rf ./dist",
59
+ "fix:lint": "eslint --fix . --max-warnings 0",
45
60
  "fix:format": "prettier --write .",
46
- "fix:lint": "eslint --fix .",
47
- "test:format": "prettier --check .",
48
61
  "test:lint": "eslint . --max-warnings 0",
62
+ "test:format": "prettier --check .",
49
63
  "test:types": "tsc",
50
- "example:fetch-and-verify": "tsx examples/fetch-and-verify-update.ts",
51
- "clean": "rm -rf ./dist"
64
+ "test:unit": "test-unit",
65
+ "example:fetch-and-verify": "tsx src/examples/fetch-and-verify.ts"
52
66
  }
53
67
  }
@@ -1,23 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- Object.defineProperty(exports, "addParseAndVerifyLeEcdsaUpdateCall", {
6
- enumerable: true,
7
- get: function() {
8
- return addParseAndVerifyLeEcdsaUpdateCall;
9
- }
10
- });
11
- const _bcs = require("@mysten/sui/bcs");
12
- const _utils = require("@mysten/sui/utils");
13
- function addParseAndVerifyLeEcdsaUpdateCall({ tx, packageId, stateObjectId, updateBytes }) {
14
- const [updateObj] = tx.moveCall({
15
- target: `${packageId}::pyth_lazer::parse_and_verify_le_ecdsa_update`,
16
- arguments: [
17
- tx.object(stateObjectId),
18
- tx.object(_utils.SUI_CLOCK_OBJECT_ID),
19
- tx.pure(_bcs.bcs.vector(_bcs.bcs.U8).serialize(updateBytes).toBytes())
20
- ]
21
- });
22
- return updateObj;
23
- }
@@ -1,10 +0,0 @@
1
- import { Transaction } from "@mysten/sui/transactions";
2
- export declare function addParseAndVerifyLeEcdsaUpdateCall({ tx, packageId, stateObjectId, updateBytes, }: {
3
- tx: Transaction;
4
- packageId: string;
5
- stateObjectId: string;
6
- updateBytes: Uint8Array;
7
- }): {
8
- $kind: "NestedResult";
9
- NestedResult: [number, number];
10
- } | undefined;
@@ -1,10 +0,0 @@
1
- import { Transaction } from "@mysten/sui/transactions";
2
- export declare function addParseAndVerifyLeEcdsaUpdateCall({ tx, packageId, stateObjectId, updateBytes, }: {
3
- tx: Transaction;
4
- packageId: string;
5
- stateObjectId: string;
6
- updateBytes: Uint8Array;
7
- }): {
8
- $kind: "NestedResult";
9
- NestedResult: [number, number];
10
- } | undefined;
@@ -1,13 +0,0 @@
1
- import { bcs } from "@mysten/sui/bcs";
2
- import { SUI_CLOCK_OBJECT_ID } from "@mysten/sui/utils";
3
- export function addParseAndVerifyLeEcdsaUpdateCall({ tx, packageId, stateObjectId, updateBytes }) {
4
- const [updateObj] = tx.moveCall({
5
- target: `${packageId}::pyth_lazer::parse_and_verify_le_ecdsa_update`,
6
- arguments: [
7
- tx.object(stateObjectId),
8
- tx.object(SUI_CLOCK_OBJECT_ID),
9
- tx.pure(bcs.vector(bcs.U8).serialize(updateBytes).toBytes())
10
- ]
11
- });
12
- return updateObj;
13
- }