From 5ad83ce45f4631c0b2f6f74f21bd06ef7e64fb24 Mon Sep 17 00:00:00 2001 From: Daniel Chew Date: Wed, 16 Oct 2024 16:37:49 +0900 Subject: [PATCH] feat(target_chains/ton): add ton js sdk (#2044) * init ton js sdk * fix docs * fix * address comments --- pnpm-lock.yaml | 285 +++++++++++++++--- pnpm-workspace.yaml | 1 + target_chains/ton/contracts/package.json | 7 +- target_chains/ton/contracts/tests/utils.ts | 46 +-- .../ton/contracts/wrappers/BaseWrapper.ts | 2 +- .../ton/contracts/wrappers/PythTest.ts | 2 +- .../ton/contracts/wrappers/WormholeTest.ts | 2 +- target_chains/ton/sdk/js/.eslintrc.js | 6 + target_chains/ton/sdk/js/.gitignore | 1 + target_chains/ton/sdk/js/README.md | 106 +++++++ target_chains/ton/sdk/js/package.json | 51 ++++ target_chains/ton/sdk/js/src/index.ts | 184 +++++++++++ target_chains/ton/sdk/js/tsconfig.json | 15 + 13 files changed, 621 insertions(+), 87 deletions(-) create mode 100644 target_chains/ton/sdk/js/.eslintrc.js create mode 100644 target_chains/ton/sdk/js/.gitignore create mode 100644 target_chains/ton/sdk/js/README.md create mode 100644 target_chains/ton/sdk/js/package.json create mode 100644 target_chains/ton/sdk/js/src/index.ts create mode 100644 target_chains/ton/sdk/js/tsconfig.json diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2cc936f8ba..35773736ce 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -775,7 +775,7 @@ importers: dependencies: '@certusone/wormhole-sdk': specifier: ^0.10.15 - version: 0.10.15(bufferutil@4.0.8)(encoding@0.1.13)(google-protobuf@3.21.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@5.0.10) + version: 0.10.15(bufferutil@4.0.8)(encoding@0.1.13)(google-protobuf@3.21.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@5.0.10) '@coral-xyz/anchor': specifier: ^0.29.0 version: 0.29.0(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) @@ -809,7 +809,7 @@ importers: dependencies: '@certusone/wormhole-sdk': specifier: ^0.10.15 - version: 0.10.15(bufferutil@4.0.8)(encoding@0.1.13)(google-protobuf@3.21.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@5.0.10) + version: 0.10.15(bufferutil@4.0.8)(encoding@0.1.13)(google-protobuf@3.21.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@5.0.10) '@coral-xyz/anchor': specifier: ^0.29.0 version: 0.29.0(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) @@ -1950,27 +1950,30 @@ importers: '@pythnetwork/price-service-sdk': specifier: workspace:* version: link:../../../price_service/sdk/js + '@pythnetwork/pyth-ton-js': + specifier: workspace:* + version: link:../sdk/js '@pythnetwork/xc-admin-common': specifier: workspace:* version: link:../../../governance/xc_admin/packages/xc_admin_common '@ton/blueprint': specifier: ^0.22.0 - version: 0.22.0(@ton/core@0.57.0(@ton/crypto@3.3.0))(@ton/crypto@3.3.0)(@ton/ton@13.11.2(@ton/core@0.57.0(@ton/crypto@3.3.0))(@ton/crypto@3.3.0))(@types/node@20.14.15)(encoding@0.1.13)(typescript@5.5.4) + version: 0.22.0(@ton/core@0.59.0(@ton/crypto@3.3.0))(@ton/crypto@3.3.0)(@ton/ton@15.1.0(@ton/core@0.59.0(@ton/crypto@3.3.0))(@ton/crypto@3.3.0))(@types/node@20.14.15)(encoding@0.1.13)(typescript@5.5.4) '@ton/core': - specifier: ~0 - version: 0.57.0(@ton/crypto@3.3.0) + specifier: ^0.59.0 + version: 0.59.0(@ton/crypto@3.3.0) '@ton/crypto': - specifier: ^3.2.0 + specifier: ^3.3.0 version: 3.3.0 '@ton/sandbox': specifier: ^0.20.0 - version: 0.20.0(@ton/core@0.57.0(@ton/crypto@3.3.0))(@ton/crypto@3.3.0) + version: 0.20.0(@ton/core@0.59.0(@ton/crypto@3.3.0))(@ton/crypto@3.3.0) '@ton/test-utils': specifier: ^0.4.2 - version: 0.4.2(@jest/globals@29.7.0)(@ton/core@0.57.0(@ton/crypto@3.3.0))(chai@4.5.0) + version: 0.4.2(@jest/globals@29.7.0)(@ton/core@0.59.0(@ton/crypto@3.3.0))(chai@4.5.0) '@ton/ton': - specifier: ^13.11.2 - version: 13.11.2(@ton/core@0.57.0(@ton/crypto@3.3.0))(@ton/crypto@3.3.0) + specifier: ^15.1.0 + version: 15.1.0(@ton/core@0.59.0(@ton/crypto@3.3.0))(@ton/crypto@3.3.0) '@types/jest': specifier: ^29.5.12 version: 29.5.12 @@ -1996,6 +1999,45 @@ importers: specifier: ^5.5.3 version: 5.5.4 + target_chains/ton/sdk/js: + devDependencies: + '@ton/core': + specifier: ^0.59.0 + version: 0.59.0(@ton/crypto@3.3.0) + '@ton/crypto': + specifier: ^3.3.0 + version: 3.3.0 + '@ton/ton': + specifier: ^15.1.0 + version: 15.1.0(@ton/core@0.59.0(@ton/crypto@3.3.0))(@ton/crypto@3.3.0) + '@types/node': + specifier: ^18.11.18 + version: 18.19.44 + '@typescript-eslint/eslint-plugin': + specifier: ^5.21.0 + version: 5.49.0(@typescript-eslint/parser@5.49.0(eslint@8.57.0)(typescript@4.9.5))(eslint@8.57.0)(typescript@4.9.5) + '@typescript-eslint/parser': + specifier: ^5.21.0 + version: 5.49.0(eslint@8.57.0)(typescript@4.9.5) + eslint: + specifier: ^8.14.0 + version: 8.57.0 + jest: + specifier: ^29.4.1 + version: 29.7.0(@types/node@18.19.44)(ts-node@10.9.2(@types/node@18.19.44)(typescript@4.9.5)) + prettier: + specifier: ^2.6.2 + version: 2.8.8 + ts-jest: + specifier: ^29.0.5 + version: 29.2.4(@babel/core@7.24.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@29.7.0(@types/node@18.19.44)(ts-node@10.9.2(@types/node@18.19.44)(typescript@4.9.5)))(typescript@4.9.5) + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@types/node@18.19.44)(typescript@4.9.5) + typescript: + specifier: ^4.6.3 + version: 4.9.5 + packages: '@0no-co/graphql.web@1.0.7': @@ -8052,8 +8094,8 @@ packages: peerDependencies: '@ton/crypto': '>=3.2.0' - '@ton/core@0.57.0': - resolution: {integrity: sha512-UOehEXEV5yqi+17qmmWdD01YfVgQlYtitSm5OfN/WMg6PAMkt+Uf91JRC4mdPNtkKDhyKuujJuhYs6QiOsHPfw==} + '@ton/core@0.59.0': + resolution: {integrity: sha512-LSIkGst7BoY7fMWshejzcH0UJnoW21JGlRrW0ch+6A7Xb/7EuekxgdKym7fHxcry6OIf6FoeFg97lJ960N/Ghg==} peerDependencies: '@ton/crypto': '>=3.2.0' @@ -8081,10 +8123,10 @@ packages: chai: optional: true - '@ton/ton@13.11.2': - resolution: {integrity: sha512-EPqW+ZTe0MmfqguJEIGMuAqTAFRKMEce95HlDx8h6CGn2y3jiMgV1/oO+WpDIOiX+1wnTu+xtajk8JTWr8nKRQ==} + '@ton/ton@15.1.0': + resolution: {integrity: sha512-almetcfTu7jLjcNcEEPB7wAc8yl90ES1M//sOr1QE+kv7RbmEvMkaPSc7kFxzs10qrjIPKxlodBJlMSWP5LuVQ==} peerDependencies: - '@ton/core': '>=0.56.0' + '@ton/core': '>=0.59.0' '@ton/crypto': '>=3.2.0' '@tonconnect/isomorphic-eventsource@0.0.1': @@ -11906,11 +11948,6 @@ packages: engines: {node: '>=0.10.0'} hasBin: true - ejs@3.1.8: - resolution: {integrity: sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==} - engines: {node: '>=0.10.0'} - hasBin: true - electron-to-chromium@1.4.797: resolution: {integrity: sha512-RWMYymqyWwIdCEb7Psag5zyAHirYnB354ZREoF8c5QOHbt8AodF7lwVxGUnu5gzBVjzDo9R3XeTwy7pbvubxGw==} @@ -27006,6 +27043,41 @@ snapshots: - supports-color - ts-node + '@jest/core@29.7.0(ts-node@10.9.2(@types/node@18.19.44)(typescript@4.9.5))': + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.14.15 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + exit: 0.1.2 + graceful-fs: 4.2.10 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@20.14.15)(ts-node@10.9.2(@types/node@18.19.44)(typescript@4.9.5)) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0 + jest-runner: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.5 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + '@jest/core@29.7.0(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4))': dependencies: '@jest/console': 29.7.0 @@ -29315,7 +29387,7 @@ snapshots: '@nrwl/devkit@15.6.3(nx@15.6.3)(typescript@4.9.5)': dependencies: '@phenomnomnominal/tsquery': 4.1.1(typescript@4.9.5) - ejs: 3.1.8 + ejs: 3.1.10 ignore: 5.3.1 nx: 15.6.3 semver: 7.3.4 @@ -33243,14 +33315,14 @@ snapshots: '@ton-community/func-js-bin': 0.4.4-newops.1 arg: 5.0.2 - '@ton/blueprint@0.22.0(@ton/core@0.57.0(@ton/crypto@3.3.0))(@ton/crypto@3.3.0)(@ton/ton@13.11.2(@ton/core@0.57.0(@ton/crypto@3.3.0))(@ton/crypto@3.3.0))(@types/node@20.14.15)(encoding@0.1.13)(typescript@5.5.4)': + '@ton/blueprint@0.22.0(@ton/core@0.59.0(@ton/crypto@3.3.0))(@ton/crypto@3.3.0)(@ton/ton@15.1.0(@ton/core@0.59.0(@ton/crypto@3.3.0))(@ton/crypto@3.3.0))(@types/node@20.14.15)(encoding@0.1.13)(typescript@5.5.4)': dependencies: '@orbs-network/ton-access': 2.3.3(encoding@0.1.13) '@tact-lang/compiler': 1.4.4(encoding@0.1.13) '@ton-community/func-js': 0.7.0 - '@ton/core': 0.57.0(@ton/crypto@3.3.0) + '@ton/core': 0.59.0(@ton/crypto@3.3.0) '@ton/crypto': 3.3.0 - '@ton/ton': 13.11.2(@ton/core@0.57.0(@ton/crypto@3.3.0))(@ton/crypto@3.3.0) + '@ton/ton': 15.1.0(@ton/core@0.59.0(@ton/crypto@3.3.0))(@ton/crypto@3.3.0) '@tonconnect/sdk': 2.2.0(encoding@0.1.13) arg: 5.0.2 chalk: 4.1.2 @@ -33273,7 +33345,7 @@ snapshots: '@ton/crypto': 3.3.0 symbol.inspect: 1.0.1 - '@ton/core@0.57.0(@ton/crypto@3.3.0)': + '@ton/core@0.59.0(@ton/crypto@3.3.0)': dependencies: '@ton/crypto': 3.3.0 symbol.inspect: 1.0.1 @@ -33288,24 +33360,24 @@ snapshots: jssha: 3.2.0 tweetnacl: 1.0.3 - '@ton/sandbox@0.20.0(@ton/core@0.57.0(@ton/crypto@3.3.0))(@ton/crypto@3.3.0)': + '@ton/sandbox@0.20.0(@ton/core@0.59.0(@ton/crypto@3.3.0))(@ton/crypto@3.3.0)': dependencies: - '@ton/core': 0.57.0(@ton/crypto@3.3.0) + '@ton/core': 0.59.0(@ton/crypto@3.3.0) '@ton/crypto': 3.3.0 - '@ton/test-utils@0.4.2(@jest/globals@29.7.0)(@ton/core@0.57.0(@ton/crypto@3.3.0))(chai@4.5.0)': + '@ton/test-utils@0.4.2(@jest/globals@29.7.0)(@ton/core@0.59.0(@ton/crypto@3.3.0))(chai@4.5.0)': dependencies: - '@ton/core': 0.57.0(@ton/crypto@3.3.0) + '@ton/core': 0.59.0(@ton/crypto@3.3.0) node-inspect-extracted: 2.0.2 optionalDependencies: '@jest/globals': 29.7.0 chai: 4.5.0 - '@ton/ton@13.11.2(@ton/core@0.57.0(@ton/crypto@3.3.0))(@ton/crypto@3.3.0)': + '@ton/ton@15.1.0(@ton/core@0.59.0(@ton/crypto@3.3.0))(@ton/crypto@3.3.0)': dependencies: - '@ton/core': 0.57.0(@ton/crypto@3.3.0) + '@ton/core': 0.59.0(@ton/crypto@3.3.0) '@ton/crypto': 3.3.0 - axios: 1.7.3 + axios: 1.7.4(debug@4.3.6) dataloader: 2.1.0 symbol.inspect: 1.0.1 teslabot: 1.5.0 @@ -38661,6 +38733,21 @@ snapshots: - supports-color - ts-node + create-jest@29.7.0(@types/node@18.19.44)(ts-node@10.9.2(@types/node@18.19.44)(typescript@4.9.5)): + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@18.19.44)(ts-node@10.9.2(@types/node@18.19.44)(typescript@4.9.5)) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + create-jest@29.7.0(@types/node@20.14.15)(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4)): dependencies: '@jest/types': 29.6.3 @@ -39460,10 +39547,6 @@ snapshots: dependencies: jake: 10.8.5 - ejs@3.1.8: - dependencies: - jake: 10.8.5 - electron-to-chromium@1.4.797: {} electron-to-chromium@1.5.6: {} @@ -43334,6 +43417,25 @@ snapshots: - supports-color - ts-node + jest-cli@29.7.0(@types/node@18.19.44)(ts-node@10.9.2(@types/node@18.19.44)(typescript@4.9.5)): + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@18.19.44)(typescript@4.9.5)) + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@18.19.44)(ts-node@10.9.2(@types/node@18.19.44)(typescript@4.9.5)) + exit: 0.1.2 + import-local: 3.1.0 + jest-config: 29.7.0(@types/node@18.19.44)(ts-node@10.9.2(@types/node@18.19.44)(typescript@4.9.5)) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + jest-cli@29.7.0(@types/node@20.14.15)(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4)): dependencies: '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4)) @@ -43606,6 +43708,37 @@ snapshots: - babel-plugin-macros - supports-color + jest-config@29.7.0(@types/node@18.19.44)(ts-node@10.9.2(@types/node@18.19.44)(typescript@4.9.5)): + dependencies: + '@babel/core': 7.24.7 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.24.7) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.7 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 18.19.44 + ts-node: 10.9.2(@types/node@18.19.44)(typescript@4.9.5) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + jest-config@29.7.0(@types/node@20.14.15)(ts-node@10.9.1(@types/node@22.5.1)(typescript@5.4.5)): dependencies: '@babel/core': 7.24.7 @@ -43761,6 +43894,37 @@ snapshots: - babel-plugin-macros - supports-color + jest-config@29.7.0(@types/node@20.14.15)(ts-node@10.9.2(@types/node@18.19.44)(typescript@4.9.5)): + dependencies: + '@babel/core': 7.24.7 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.24.7) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.7 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 20.14.15 + ts-node: 10.9.2(@types/node@18.19.44)(typescript@4.9.5) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + jest-config@29.7.0(@types/node@20.14.15)(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4)): dependencies: '@babel/core': 7.24.7 @@ -44653,6 +44817,18 @@ snapshots: - supports-color - ts-node + jest@29.7.0(@types/node@18.19.44)(ts-node@10.9.2(@types/node@18.19.44)(typescript@4.9.5)): + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@18.19.44)(typescript@4.9.5)) + '@jest/types': 29.6.3 + import-local: 3.1.0 + jest-cli: 29.7.0(@types/node@18.19.44)(ts-node@10.9.2(@types/node@18.19.44)(typescript@4.9.5)) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + jest@29.7.0(@types/node@20.14.15)(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4)): dependencies: '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4)) @@ -50782,6 +50958,25 @@ snapshots: '@jest/types': 29.6.3 babel-jest: 29.7.0(@babel/core@7.24.7) + ts-jest@29.2.4(@babel/core@7.24.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@29.7.0(@types/node@18.19.44)(ts-node@10.9.2(@types/node@18.19.44)(typescript@4.9.5)))(typescript@4.9.5): + dependencies: + bs-logger: 0.2.6 + ejs: 3.1.10 + fast-json-stable-stringify: 2.1.0 + jest: 29.7.0(@types/node@18.19.44)(ts-node@10.9.2(@types/node@18.19.44)(typescript@4.9.5)) + jest-util: 29.7.0 + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.6.3 + typescript: 4.9.5 + yargs-parser: 21.1.1 + optionalDependencies: + '@babel/core': 7.24.7 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.24.7) + ts-jest@29.2.4(@babel/core@7.24.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@29.7.0(@types/node@20.14.15)(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4)))(typescript@5.5.4): dependencies: bs-logger: 0.2.6 @@ -51012,6 +51207,24 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 + ts-node@10.9.2(@types/node@18.19.44)(typescript@4.9.5): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.9 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.3 + '@types/node': 18.19.44 + acorn: 8.12.1 + acorn-walk: 8.2.0 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 4.9.5 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4): dependencies: '@cspotcode/source-map-support': 0.8.1 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index b8abd396d1..0accc1c0d6 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -26,4 +26,5 @@ packages: - "target_chains/solana/sdk/js/solana_utils" - "target_chains/solana/sdk/js/pyth_solana_receiver" - "target_chains/ton/contracts" + - "target_chains/ton/sdk/js" - "contract_manager" diff --git a/target_chains/ton/contracts/package.json b/target_chains/ton/contracts/package.json index 7085400b83..92efcd3cc4 100644 --- a/target_chains/ton/contracts/package.json +++ b/target_chains/ton/contracts/package.json @@ -8,15 +8,16 @@ "test": "jest --verbose" }, "devDependencies": { + "@pythnetwork/pyth-ton-js": "workspace:*", "@pythnetwork/price-service-sdk": "workspace:*", "@pythnetwork/xc-admin-common": "workspace:*", "@pythnetwork/hermes-client": "workspace:*", "@ton/blueprint": "^0.22.0", - "@ton/core": "~0", - "@ton/crypto": "^3.2.0", + "@ton/core": "^0.59.0", + "@ton/crypto": "^3.3.0", "@ton/sandbox": "^0.20.0", "@ton/test-utils": "^0.4.2", - "@ton/ton": "^13.11.2", + "@ton/ton": "^15.1.0", "@types/jest": "^29.5.12", "@types/node": "^20.14.10", "@wormhole-foundation/sdk-definitions": "^0.10.7", diff --git a/target_chains/ton/contracts/tests/utils.ts b/target_chains/ton/contracts/tests/utils.ts index bd94fd8789..cae3fa6cba 100644 --- a/target_chains/ton/contracts/tests/utils.ts +++ b/target_chains/ton/contracts/tests/utils.ts @@ -1,5 +1,5 @@ import { DataSource } from "@pythnetwork/xc-admin-common"; -import { Cell, Transaction, beginCell } from "@ton/core"; +import { Cell, Transaction } from "@ton/core"; import { Buffer } from "buffer"; const GOVERNANCE_MAGIC = 0x5054474d; @@ -7,50 +7,6 @@ const GOVERNANCE_MODULE = 1; const AUTHORIZE_UPGRADE_CONTRACT_ACTION = 0; const TARGET_CHAIN_ID = 1; -export function createCellChain(buffer: Buffer): Cell { - let chunks = bufferToChunks(buffer, 127); - let lastCell: Cell | null = null; - // Iterate through chunks in reverse order - for (let i = chunks.length - 1; i >= 0; i--) { - const chunk = chunks[i]; - const cellBuilder = beginCell(); - const buffer = Buffer.from(chunk); - cellBuilder.storeBuffer(buffer); - - if (lastCell) { - cellBuilder.storeRef(lastCell); - } - - lastCell = cellBuilder.endCell(); - } - - // lastCell will be the root cell of our chain - if (!lastCell) { - throw new Error("Failed to create cell chain"); - } - return lastCell; -} - -function bufferToChunks( - buff: Buffer, - chunkSizeBytes: number = 127 -): Uint8Array[] { - const chunks: Uint8Array[] = []; - const uint8Array = new Uint8Array( - buff.buffer, - buff.byteOffset, - buff.byteLength - ); - - for (let offset = 0; offset < uint8Array.length; offset += chunkSizeBytes) { - const remainingBytes = Math.min(chunkSizeBytes, uint8Array.length - offset); - const chunk = uint8Array.subarray(offset, offset + remainingBytes); - chunks.push(chunk); - } - - return chunks; -} - // Helper function to parse DataSource from a Cell export function parseDataSource(cell: Cell): DataSource { const slice = cell.beginParse(); diff --git a/target_chains/ton/contracts/wrappers/BaseWrapper.ts b/target_chains/ton/contracts/wrappers/BaseWrapper.ts index 4b51787076..548534858f 100644 --- a/target_chains/ton/contracts/wrappers/BaseWrapper.ts +++ b/target_chains/ton/contracts/wrappers/BaseWrapper.ts @@ -9,7 +9,7 @@ import { SendMode, toNano, } from "@ton/core"; -import { createCellChain } from "../tests/utils"; +import { createCellChain } from "@pythnetwork/pyth-ton-js"; import { createGuardianSetsDict } from "../tests/utils/wormhole"; import { HexString, Price } from "@pythnetwork/price-service-sdk"; import { DataSource } from "@pythnetwork/xc-admin-common"; diff --git a/target_chains/ton/contracts/wrappers/PythTest.ts b/target_chains/ton/contracts/wrappers/PythTest.ts index 383dc0ec61..809219dcef 100644 --- a/target_chains/ton/contracts/wrappers/PythTest.ts +++ b/target_chains/ton/contracts/wrappers/PythTest.ts @@ -9,8 +9,8 @@ import { } from "@ton/core"; import { BaseWrapper } from "./BaseWrapper"; import { HexString, Price } from "@pythnetwork/price-service-sdk"; +import { createCellChain } from "@pythnetwork/pyth-ton-js"; import { DataSource } from "@pythnetwork/xc-admin-common"; -import { createCellChain } from "../tests/utils"; export type PythTestConfig = { priceFeedId: HexString; diff --git a/target_chains/ton/contracts/wrappers/WormholeTest.ts b/target_chains/ton/contracts/wrappers/WormholeTest.ts index b23ffa8b22..946e5f00b6 100644 --- a/target_chains/ton/contracts/wrappers/WormholeTest.ts +++ b/target_chains/ton/contracts/wrappers/WormholeTest.ts @@ -1,6 +1,6 @@ import { Cell, contractAddress, ContractProvider, Sender } from "@ton/core"; import { BaseWrapper } from "./BaseWrapper"; -import { createCellChain } from "../tests/utils"; +import { createCellChain } from "@pythnetwork/pyth-ton-js"; import { parseGuardianSetKeys } from "../tests/utils/wormhole"; export type WormholeTestConfig = { diff --git a/target_chains/ton/sdk/js/.eslintrc.js b/target_chains/ton/sdk/js/.eslintrc.js new file mode 100644 index 0000000000..fca472f38b --- /dev/null +++ b/target_chains/ton/sdk/js/.eslintrc.js @@ -0,0 +1,6 @@ +module.exports = { + root: true, + parser: "@typescript-eslint/parser", + plugins: ["@typescript-eslint"], + extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"], +}; diff --git a/target_chains/ton/sdk/js/.gitignore b/target_chains/ton/sdk/js/.gitignore new file mode 100644 index 0000000000..a65b41774a --- /dev/null +++ b/target_chains/ton/sdk/js/.gitignore @@ -0,0 +1 @@ +lib diff --git a/target_chains/ton/sdk/js/README.md b/target_chains/ton/sdk/js/README.md new file mode 100644 index 0000000000..da66eedcd7 --- /dev/null +++ b/target_chains/ton/sdk/js/README.md @@ -0,0 +1,106 @@ +# Pyth Network TON SDK + +This SDK provides a JavaScript interface for interacting with the Pyth Network on the TON blockchain. + +## Installation + +```bash +npm install @pythnetwork/pyth-ton-js +``` + +## Usage + +Here's a basic example of how to use the SDK: + +```bash +npm install @pythnetwork/pyth-ton-js @pythnetwork/hermes-client @ton/core @ton/ton @ton/crypto +``` + +```typescript +import { TonClient, Address, WalletContractV4 } from "@ton/ton"; +import { toNano } from "@ton/core"; +import { mnemonicToPrivateKey } from "@ton/crypto"; +import { HermesClient } from "@pythnetwork/hermes-client"; +import { + PythContract, + PYTH_CONTRACT_ADDRESS_TESTNET, +} from "@pythnetwork/pyth-ton-js"; + +const BTC_PRICE_FEED_ID = + "0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43"; + +async function main() { + const client = new TonClient({ + endpoint: "https://testnet.toncenter.com/api/v2/jsonRPC", + apiKey: "your-api-key-here", // Optional, but note that without api-key you need to send requests once per second + }); + + const contractAddress = Address.parse(PYTH_CONTRACT_ADDRESS_TESTNET); + const contract = client.open(PythContract.createFromAddress(contractAddress)); + + const guardianSetIndex = await contract.getCurrentGuardianSetIndex(); + console.log("Guardian Set Index:", guardianSetIndex); + + const price = await contract.getPriceUnsafe(BTC_PRICE_FEED_ID); + console.log("BTC Price from TON contract:", price); + + const hermesEndpoint = "https://hermes.pyth.network"; + const hermesClient = new HermesClient(hermesEndpoint); + + const priceIds = [BTC_PRICE_FEED_ID]; + const latestPriceUpdates = await hermesClient.getLatestPriceUpdates( + priceIds, + { + encoding: "hex", + } + ); + console.log("Hermes BTC price:", latestPriceUpdates.parsed?.[0].price); + + const updateData = Buffer.from(latestPriceUpdates.binary.data[0], "hex"); + console.log("Update data:", updateData); + + const updateFee = await contract.getUpdateFee(updateData); + console.log("Update fee:", updateFee); + + const mnemonic = "your mnemonic here"; + const key = await mnemonicToPrivateKey(mnemonic.split(" ")); + const wallet = WalletContractV4.create({ + publicKey: key.publicKey, + workchain: 0, + }); + const provider = client.open(wallet); + + await contract.sendUpdatePriceFeeds( + provider.sender(key.secretKey), + updateData, + 156000000n + BigInt(updateFee) // 156000000 = 390000 (estimated gas used for the transaction, this is defined in contracts/common/gas.fc as UPDATE_PRICE_FEEDS_GAS) * 400 (current settings in basechain are as follows: 1 unit of gas costs 400 nanotons) + ); + console.log("Price feeds updated successfully."); + + const updatedPrice = await contract.getPriceUnsafe(BTC_PRICE_FEED_ID); + console.log("Updated BTC Price from TON contract:", updatedPrice); +} + +main().catch(console.error); +``` + +## API Reference + +### `PythContract` + +The main class for interacting with the Pyth contract on TON. + +#### Methods: + +- `getCurrentGuardianSetIndex(provider: ContractProvider): Promise` +- `getPriceUnsafe(provider: ContractProvider, priceFeedId: string): Promise` +- `getPriceNoOlderThan(provider: ContractProvider, timePeriod: number, priceFeedId: string): Promise` +- `getEmaPriceUnsafe(provider: ContractProvider, priceFeedId: string): Promise` +- `getEmaPriceNoOlderThan(provider: ContractProvider, timePeriod: number, priceFeedId: string): Promise` +- `getUpdateFee(provider: ContractProvider, vm: Buffer): Promise` +- `getSingleUpdateFee(provider: ContractProvider): Promise` +- `sendUpdatePriceFeeds(provider: ContractProvider, via: Sender, updateData: Buffer, updateFee: bigint): Promise` + +### Constants + +- `PYTH_CONTRACT_ADDRESS_TESTNET`: The address of the Pyth contract on the TON testnet. diff --git a/target_chains/ton/sdk/js/package.json b/target_chains/ton/sdk/js/package.json new file mode 100644 index 0000000000..6dc9d711cd --- /dev/null +++ b/target_chains/ton/sdk/js/package.json @@ -0,0 +1,51 @@ +{ + "name": "@pythnetwork/pyth-ton-js", + "version": "0.1.0", + "description": "Pyth Network TON Utilities", + "homepage": "https://pyth.network", + "author": { + "name": "Pyth Data Association" + }, + "main": "lib/index.js", + "types": "lib/index.d.ts", + "files": [ + "lib/**/*" + ], + "repository": { + "type": "git", + "url": "https://github.com/pyth-network/pyth-crosschain", + "directory": "target_chains/ton/sdk/js" + }, + "publishConfig": { + "access": "public" + }, + "scripts": { + "test": "jest --passWithNoTests", + "build": "tsc", + "format": "prettier --write \"src/**/*.ts\"", + "lint": "eslint src/", + "prepublishOnly": "pnpm run build && pnpm test && pnpm run lint", + "preversion": "pnpm run lint", + "version": "pnpm run format && git add -A src" + }, + "keywords": [ + "pyth", + "oracle" + ], + "license": "Apache-2.0", + "devDependencies": { + "@ton/core": "^0.59.0", + "@ton/ton": "^15.1.0", + "@ton/crypto": "^3.3.0", + "@types/node": "^18.11.18", + "@typescript-eslint/eslint-plugin": "^5.21.0", + "@typescript-eslint/parser": "^5.21.0", + "eslint": "^8.14.0", + "jest": "^29.4.1", + "prettier": "^2.6.2", + "ts-jest": "^29.0.5", + "typescript": "^4.6.3", + "ts-node": "^10.9.2" + }, + "dependencies": {} +} diff --git a/target_chains/ton/sdk/js/src/index.ts b/target_chains/ton/sdk/js/src/index.ts new file mode 100644 index 0000000000..7d6fdfda5e --- /dev/null +++ b/target_chains/ton/sdk/js/src/index.ts @@ -0,0 +1,184 @@ +import { + Address, + beginCell, + Cell, + Contract, + Sender, + SendMode, +} from "@ton/core"; +import { ContractProvider } from "@ton/ton"; + +export const PYTH_CONTRACT_ADDRESS_TESTNET = + "EQDwGkJmcj7MMmWAHmhldnY-lAKI6hcTQ2tAEcapmwCnztQU"; + +export class PythContract implements Contract { + constructor( + readonly address: Address, + readonly init?: { code: Cell; data: Cell } + ) {} + + static createFromAddress(address: Address) { + return new PythContract(address); + } + + async getCurrentGuardianSetIndex(provider: ContractProvider) { + const result = await provider.get("get_current_guardian_set_index", []); + + return result.stack.readNumber(); + } + + async sendUpdatePriceFeeds( + provider: ContractProvider, + via: Sender, + updateData: Buffer, + updateFee: bigint + ) { + const messageBody = beginCell() + .storeUint(2, 32) // OP_UPDATE_PRICE_FEEDS + .storeRef(createCellChain(updateData)) + .endCell(); + + await provider.internal(via, { + value: updateFee, + sendMode: SendMode.PAY_GAS_SEPARATELY, + body: messageBody, + }); + } + + async getPriceUnsafe(provider: ContractProvider, priceFeedId: string) { + const result = await provider.get("get_price_unsafe", [ + { type: "int", value: BigInt(priceFeedId) }, + ]); + + const price = result.stack.readNumber(); + const conf = result.stack.readNumber(); + const expo = result.stack.readNumber(); + const publishTime = result.stack.readNumber(); + + return { + price, + conf, + expo, + publishTime, + }; + } + + async getPriceNoOlderThan( + provider: ContractProvider, + timePeriod: number, + priceFeedId: string + ) { + const result = await provider.get("get_price_no_older_than", [ + { type: "int", value: BigInt(timePeriod) }, + { type: "int", value: BigInt(priceFeedId) }, + ]); + + const price = result.stack.readNumber(); + const conf = result.stack.readNumber(); + const expo = result.stack.readNumber(); + const publishTime = result.stack.readNumber(); + + return { + price, + conf, + expo, + publishTime, + }; + } + + async getEmaPriceUnsafe(provider: ContractProvider, priceFeedId: string) { + const result = await provider.get("get_ema_price_unsafe", [ + { type: "int", value: BigInt(priceFeedId) }, + ]); + + const price = result.stack.readNumber(); + const conf = result.stack.readNumber(); + const expo = result.stack.readNumber(); + const publishTime = result.stack.readNumber(); + + return { + price, + conf, + expo, + publishTime, + }; + } + + async getEmaPriceNoOlderThan( + provider: ContractProvider, + timePeriod: number, + priceFeedId: string + ) { + const result = await provider.get("get_ema_price_no_older_than", [ + { type: "int", value: BigInt(timePeriod) }, + { type: "int", value: BigInt(priceFeedId) }, + ]); + + const price = result.stack.readNumber(); + const conf = result.stack.readNumber(); + const expo = result.stack.readNumber(); + const publishTime = result.stack.readNumber(); + + return { + price, + conf, + expo, + publishTime, + }; + } + + async getUpdateFee(provider: ContractProvider, vm: Buffer) { + const result = await provider.get("get_update_fee", [ + { type: "slice", cell: createCellChain(vm) }, + ]); + + return result.stack.readNumber(); + } + + async getSingleUpdateFee(provider: ContractProvider) { + const result = await provider.get("get_single_update_fee", []); + + return result.stack.readNumber(); + } +} + +export function createCellChain(buffer: Buffer): Cell { + const chunks = bufferToChunks(buffer, 127); + let lastCell: Cell | null = null; + // Iterate through chunks in reverse order + for (let i = chunks.length - 1; i >= 0; i--) { + const chunk = chunks[i]; + const cellBuilder = beginCell(); + const buffer = Buffer.from(chunk); + cellBuilder.storeBuffer(buffer); + + if (lastCell) { + cellBuilder.storeRef(lastCell); + } + + lastCell = cellBuilder.endCell(); + } + + // lastCell will be the root cell of our chain + if (!lastCell) { + throw new Error("Failed to create cell chain"); + } + return lastCell; +} + +function bufferToChunks(buff: Buffer, chunkSizeBytes = 127): Uint8Array[] { + const chunks: Uint8Array[] = []; + const uint8Array = new Uint8Array( + buff.buffer, + buff.byteOffset, + buff.byteLength + ); + + for (let offset = 0; offset < uint8Array.length; offset += chunkSizeBytes) { + const remainingBytes = Math.min(chunkSizeBytes, uint8Array.length - offset); + const chunk = uint8Array.subarray(offset, offset + remainingBytes); + chunks.push(chunk); + } + + return chunks; +} diff --git a/target_chains/ton/sdk/js/tsconfig.json b/target_chains/ton/sdk/js/tsconfig.json new file mode 100644 index 0000000000..bae5d05cde --- /dev/null +++ b/target_chains/ton/sdk/js/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "target": "esnext", + "module": "commonjs", + "declaration": true, + "outDir": "./lib", + "rootDir": "src/", + "strict": true, + "esModuleInterop": true, + "resolveJsonModule": true + }, + "include": ["src", "src/**/*.json"], + "exclude": ["node_modules", "**/__tests__/*"] +}