diff --git a/.github/workflows/ci-lazer-js.yml b/.github/workflows/ci-lazer-js.yml new file mode 100644 index 0000000..67f1881 --- /dev/null +++ b/.github/workflows/ci-lazer-js.yml @@ -0,0 +1,22 @@ +name: "Lazer JS Test Suite" +on: + push: + branches: + - main + pull_request: + +jobs: + lazer-js-test-suite: + name: Lazer JS Test Suite + runs-on: ubuntu-22.04 + defaults: + run: + working-directory: lazer/js + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 18 + - run: npm install --global pnpm@9.2.0 + - run: pnpm install --frozen-lockfile + - run: pnpm run test diff --git a/lazer/js/.gitignore b/lazer/js/.gitignore new file mode 100644 index 0000000..809f867 --- /dev/null +++ b/lazer/js/.gitignore @@ -0,0 +1,2 @@ +tsconfig.tsbuildinfo +dist diff --git a/lazer/js/package.json b/lazer/js/package.json new file mode 100644 index 0000000..94c2d3f --- /dev/null +++ b/lazer/js/package.json @@ -0,0 +1,27 @@ +{ + "name": "pyth-lazer-js-example", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "start": "node --loader ts-node/esm src/index.js", + "test": "pnpm run test:format && pnpm run build:cjs && pnpm run build:esm", + "build:cjs": "tsc --project tsconfig.json --verbatimModuleSyntax false --module commonjs --outDir ./dist/cjs && echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json", + "build:esm": "tsc --project tsconfig.json --outDir ./dist/esm && echo '{\"type\":\"module\"}' > dist/esm/package.json", + "fix:format": "prettier --write **/*.*", + "test:format": "prettier --check **/*.*" + }, + "author": "", + "license": "Apache-2.0", + "dependencies": { + "@pythnetwork/pyth-lazer-sdk": "^0.1.1" + }, + "devDependencies": { + "@cprussin/tsconfig": "^3.0.1", + "@types/node": "^22.10.0", + "prettier": "^3.4.1", + "ts-node": "^10.9.2", + "typescript": "^5.7.2" + } +} diff --git a/lazer/js/pnpm-lock.yaml b/lazer/js/pnpm-lock.yaml new file mode 100644 index 0000000..3bf0f3c --- /dev/null +++ b/lazer/js/pnpm-lock.yaml @@ -0,0 +1,224 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@pythnetwork/pyth-lazer-sdk': + specifier: ^0.1.1 + version: 0.1.1 + devDependencies: + '@cprussin/tsconfig': + specifier: ^3.0.1 + version: 3.0.1 + '@types/node': + specifier: ^22.10.0 + version: 22.10.0 + prettier: + specifier: ^3.4.1 + version: 3.4.1 + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@types/node@22.10.0)(typescript@5.7.2) + typescript: + specifier: ^5.7.2 + version: 5.7.2 + +packages: + + '@cprussin/tsconfig@3.0.1': + resolution: {integrity: sha512-OBS2ew/pNjiWBI12kEt++8xKFE1rwZhitHOLHmqhNUs/1b4noi6k8nN0ZriV5ltTDP1l1jzYuaA69gwujaR28A==} + + '@cspotcode/source-map-support@0.8.1': + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/trace-mapping@0.3.9': + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + + '@pythnetwork/pyth-lazer-sdk@0.1.1': + resolution: {integrity: sha512-/Zr9qbNi9YZb9Nl3ilkUKgeSQovevsXV57pIGrw04NFUmK4Ua92o2SyK8RRaqcw8zYtiDbseU1CgWHCfGYjRRQ==} + + '@tsconfig/node10@1.0.11': + resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} + + '@tsconfig/node12@1.0.11': + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + + '@tsconfig/node14@1.0.3': + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + + '@tsconfig/node16@1.0.4': + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + + '@types/node@22.10.0': + resolution: {integrity: sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==} + + acorn-walk@8.3.4: + resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} + engines: {node: '>=0.4.0'} + + acorn@8.14.0: + resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} + engines: {node: '>=0.4.0'} + hasBin: true + + arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + + create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + + diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + + isomorphic-ws@5.0.0: + resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==} + peerDependencies: + ws: '*' + + make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + + prettier@3.4.1: + resolution: {integrity: sha512-G+YdqtITVZmOJje6QkXQWzl3fSfMxFwm1tjTyo9exhkmWSqC4Yhd1+lug++IlR2mvRVAxEDDWYkQdeSztajqgg==} + engines: {node: '>=14'} + hasBin: true + + ts-node@10.9.2: + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + + typescript@5.7.2: + resolution: {integrity: sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.20.0: + resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} + + v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + +snapshots: + + '@cprussin/tsconfig@3.0.1': {} + + '@cspotcode/source-map-support@0.8.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.9': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@pythnetwork/pyth-lazer-sdk@0.1.1': + dependencies: + isomorphic-ws: 5.0.0(ws@8.18.0) + ws: 8.18.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@tsconfig/node10@1.0.11': {} + + '@tsconfig/node12@1.0.11': {} + + '@tsconfig/node14@1.0.3': {} + + '@tsconfig/node16@1.0.4': {} + + '@types/node@22.10.0': + dependencies: + undici-types: 6.20.0 + + acorn-walk@8.3.4: + dependencies: + acorn: 8.14.0 + + acorn@8.14.0: {} + + arg@4.1.3: {} + + create-require@1.1.1: {} + + diff@4.0.2: {} + + isomorphic-ws@5.0.0(ws@8.18.0): + dependencies: + ws: 8.18.0 + + make-error@1.3.6: {} + + prettier@3.4.1: {} + + ts-node@10.9.2(@types/node@22.10.0)(typescript@5.7.2): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.11 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 22.10.0 + acorn: 8.14.0 + acorn-walk: 8.3.4 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.7.2 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + + typescript@5.7.2: {} + + undici-types@6.20.0: {} + + v8-compile-cache-lib@3.0.1: {} + + ws@8.18.0: {} + + yn@3.1.1: {} diff --git a/lazer/js/src/index.ts b/lazer/js/src/index.ts new file mode 100644 index 0000000..752820e --- /dev/null +++ b/lazer/js/src/index.ts @@ -0,0 +1,44 @@ +import { PythLazerClient } from "@pythnetwork/pyth-lazer-sdk"; + +/* eslint-disable no-console */ +const client = new PythLazerClient( + "wss://pyth-lazer-staging.dourolabs.app/v1/stream", + "my_token", +); +client.addMessageListener((message) => { + console.log("got message:", message); + switch (message.type) { + case "json": { + if (message.value.type == "streamUpdated") { + console.log( + "stream updated for subscription", + message.value.subscriptionId, + ":", + message.value.parsed?.priceFeeds, + ); + } + break; + } + case "binary": { + if ("solana" in message.value) { + console.log("solana message:", message.value.solana?.toString("hex")); + } + if ("evm" in message.value) { + console.log("evm message:", message.value.evm?.toString("hex")); + } + break; + } + } +}); +client.ws.addEventListener("open", () => { + client.send({ + type: "subscribe", + subscriptionId: 1, + priceFeedIds: [1, 2], + properties: ["price"], + chains: ["solana"], + deliveryFormat: "json", + channel: "fixed_rate@200ms", + jsonBinaryEncoding: "hex", + }); +}); diff --git a/lazer/js/tsconfig.json b/lazer/js/tsconfig.json new file mode 100644 index 0000000..0c7ef3c --- /dev/null +++ b/lazer/js/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "@cprussin/tsconfig/base.json", + "exclude": [ + "node_modules", + "dist" + ] +}