From ec68c8c2a3d09f4b442f58f72a2c156eb03e32a2 Mon Sep 17 00:00:00 2001 From: o-az Date: Mon, 2 Sep 2024 15:07:08 -0700 Subject: [PATCH] feat: move app to sdk --- .github/workflows/typescript-sdk-publish.yml | 108 +--------- app/package-lock.json | 193 +++++++----------- app/package.json | 18 +- app/src/lib/wallet/evm/config.ts | 12 +- typescript-sdk/LICENSE | 21 ++ typescript-sdk/bun.lockb | Bin 107956 -> 107916 bytes typescript-sdk/jsr.json | 4 +- typescript-sdk/package.json | 4 +- typescript-sdk/scripts/publish.ts | 42 +++- typescript-sdk/src/client/evm.ts | 16 +- typescript-sdk/src/convert.ts | 87 ++++++-- typescript-sdk/src/pfm.ts | 8 +- typescript-sdk/src/query/offchain/hubble.ts | 33 ++- typescript-sdk/src/query/offchain/tenderly.ts | 15 ++ typescript-sdk/src/query/on-chain.ts | 29 +++ typescript-sdk/src/transfer/cosmos.ts | 183 ++++++++++++++++- typescript-sdk/src/transfer/evm.ts | 47 ++++- typescript-sdk/src/transport.ts | 175 ---------------- typescript-sdk/src/utilities/address.ts | 40 +++- .../src/utilities/fetch-progress.ts | 40 ---- typescript-sdk/src/utilities/index.ts | 2 +- .../src/utilities/promise/batch-scheduler.ts | 114 ----------- .../src/utilities/promise/with-retry.ts | 50 ----- 23 files changed, 579 insertions(+), 662 deletions(-) create mode 100644 typescript-sdk/LICENSE delete mode 100644 typescript-sdk/src/transport.ts delete mode 100644 typescript-sdk/src/utilities/fetch-progress.ts delete mode 100644 typescript-sdk/src/utilities/promise/batch-scheduler.ts delete mode 100644 typescript-sdk/src/utilities/promise/with-retry.ts diff --git a/.github/workflows/typescript-sdk-publish.yml b/.github/workflows/typescript-sdk-publish.yml index c0ccb71c23..3f90587136 100644 --- a/.github/workflows/typescript-sdk-publish.yml +++ b/.github/workflows/typescript-sdk-publish.yml @@ -1,4 +1,4 @@ -name: Publish TypeScript SDK +name: 'Publish TypeScript SDK' on: push: @@ -19,116 +19,22 @@ defaults: shell: bash env: - NIX_VERSION: nix-2.13.2 - NIXPKGS_CHANNEL: nixos-22.11 NODE_OPTIONS: '--no-warnings' ACTIONS_RUNNER_DEBUG: true jobs: - publish-npm: - # manually temporarily disabled - if: false - name: 'Publish NPM Registry' - permissions: - id-token: write - contents: write + publish-jsr: + name: 'Publish JSR' runs-on: ['ubuntu-latest'] - steps: - - name: 'Checkout' - uses: actions/checkout@v4 - - # This is needed to do npm authentication - - name: 'Setup Node.js' - uses: actions/setup-node@v4 - with: - node-version: 'lts/*' - registry-url: 'https://registry.npmjs.org' - - - name: 'Install Nix' - uses: cachix/install-nix-action@v25 - with: - nix_path: nixpkgs=channel:${{ env.NIXPKGS_CHANNEL }} - github_access_token: ${{ github.token }} - - run: | - nix-channel --add https://nixos.org/channels/${{ env.NIXPKGS_CHANNEL }} nixpkgs - nix-channel --update - - - name: 'Build SDK' - working-directory: './typescript-sdk' - run: | - nix build .#typescript-sdk -o dist - - - name: 'Publish to NPM' - working-directory: './typescript-sdk' - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - # https://docs.npmjs.com/generating-provenance-statements - NPM_CONFIG_PROVENANCE: true - run: | - npm publish --access='public' --no-git-checks - - publish-github: - name: 'Publish GitHub Package Registry' - # manually temporarily disabled - if: false permissions: contents: read + # The OIDC ID token is used for authentication with JSR. id-token: write - packages: write - runs-on: ['ubuntu-latest'] steps: - name: 'Checkout' uses: actions/checkout@v4 - - # This is needed to do npm authentication - - name: 'Setup Node.js' - uses: actions/setup-node@v4 - with: - node-version: 'lts/*' - registry-url: 'https://npm.pkg.github.com' - - - name: 'Install Nix' - uses: cachix/install-nix-action@v25 - with: - github_access_token: ${{ github.token }} - nix_path: nixpkgs=channel:${{ env.NIXPKGS_CHANNEL }} - - run: | - nix-channel --add https://nixos.org/channels/${{ env.NIXPKGS_CHANNEL }} nixpkgs - nix-channel --update - - - name: 'Update ~/.npmrc' - working-directory: './typescript-sdk' - run: | - echo "//npm.pkg.github.com:_authToken=${{ secrets.GITHUB_TOKEN }}" >> .npmrc - - - name: 'Build SDK' + + - name: 'Publish to JSR' working-directory: './typescript-sdk' run: | - nix build .#typescript-sdk -o dist - - - name: 'Publish to GitHub Package Registry' - working-directory: './typescript-sdk' - env: - NPM_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # https://docs.npmjs.com/generating-provenance-statements - NPM_CONFIG_PROVENANCE: true - run: | - yarn publish --access='public' --registry='https://npm.pkg.github.com' --no-git-checks - - changelog: - # manually temporarily disabled - if: false - name: 'Generate Changelog' - runs-on: ['ubuntu-latest'] - needs: ['publish-npm'] - steps: - - name: 'Checkout' - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: 'Generate Changelog' - run: npm_config_yes=true npx changelogithub - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + npm_config_yes=true npx jsr publish --allow-slow-types diff --git a/app/package-lock.json b/app/package-lock.json index 2dc8569185..fa42683d45 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -13,13 +13,13 @@ "@cosmjs/encoding": "^0.32.4", "@sentry/sveltekit": "^8.27.0", "@tanstack/match-sorter-utils": "^8.19.4", - "@tanstack/query-core": "^5.53.2", - "@tanstack/query-sync-storage-persister": "^5.53.2", - "@tanstack/svelte-query": "^5.53.2", - "@tanstack/svelte-query-persist-client": "^5.53.2", + "@tanstack/query-core": "^5.53.3", + "@tanstack/query-sync-storage-persister": "^5.53.3", + "@tanstack/svelte-query": "^5.53.3", + "@tanstack/svelte-query-persist-client": "^5.53.3", "@tanstack/svelte-table": "^8.20.5", "@tanstack/svelte-virtual": "^3.10.6", - "@union/client": "npm:@jsr/union__client@^0.0.1-rc.52", + "@union/client": "npm:@jsr/union__client@^0.0.4", "@wagmi/connectors": "^5.1.8", "@wagmi/core": "^2.13.4", "bech32": "^2.0.0", @@ -59,8 +59,8 @@ "@tailwindcss/container-queries": "^0.1.1", "@tailwindcss/forms": "^0.5.8", "@tailwindcss/typography": "^0.5.15", - "@tanstack/svelte-query-devtools": "^5.53.2", - "@total-typescript/ts-reset": "^0.6.0", + "@tanstack/svelte-query-devtools": "^5.53.3", + "@total-typescript/ts-reset": "^0.6.1", "@types/node": "^22.5.2", "@types/postcss-import": "^14.0.3", "@types/three": "^0.168.0", @@ -71,12 +71,12 @@ "graphql": "^16.9.0", "jsr": "^0.13.1", "patch-package": "^8.0.0", - "postcss": "^8.4.43", + "postcss": "^8.4.44", "postcss-import": "^16.1.0", "process": "^0.11.10", "rollup-plugin-visualizer": "^5.12.0", "svelte": "^4.2.19", - "svelte-check": "^3.8.6", + "svelte-check": "^4.0.0", "svelte-preprocess": "^6.0.2", "tailwind-merge": "^2.5.2", "tailwind-scrollbar": "^3.1.0", @@ -3948,9 +3948,9 @@ } }, "node_modules/@tanstack/query-core": { - "version": "5.53.2", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.53.2.tgz", - "integrity": "sha512-gCsABpRrYfLsmwcQ0JCE5I3LOQ9KYrDDSnseUDP3T7ukV8E7+lhlHDJS4Gegt1TSZCsxKhc1J5A7TkF5ePjDUQ==", + "version": "5.53.3", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.53.3.tgz", + "integrity": "sha512-ZfjAgd7NpqDx0e4aYBt7EmS2enbulPrJwowTy+mayRE93WUUH+sIYHun1TdRjpGwDPMNNZ5D6goh7n3CwoO+HA==", "license": "MIT", "funding": { "type": "github", @@ -3969,12 +3969,12 @@ } }, "node_modules/@tanstack/query-persist-client-core": { - "version": "5.53.2", - "resolved": "https://registry.npmjs.org/@tanstack/query-persist-client-core/-/query-persist-client-core-5.53.2.tgz", - "integrity": "sha512-UjspxAb2+wSEu64rf6cwFXra9GXPHSoIEhRRrhYlKlVBv61Kxz1h6mhyMx/jx6j3PoyFBJW+QRt2Sq16pIQqLg==", + "version": "5.53.3", + "resolved": "https://registry.npmjs.org/@tanstack/query-persist-client-core/-/query-persist-client-core-5.53.3.tgz", + "integrity": "sha512-iKc532uoQIduxwjJoWTESBabFBUye+aP7Zhqul8OsR04L0S3K2Jx4RJtunU2T7dGIXNi5pkRI4iH94mzl7KWkg==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.53.2" + "@tanstack/query-core": "5.53.3" }, "funding": { "type": "github", @@ -3982,13 +3982,13 @@ } }, "node_modules/@tanstack/query-sync-storage-persister": { - "version": "5.53.2", - "resolved": "https://registry.npmjs.org/@tanstack/query-sync-storage-persister/-/query-sync-storage-persister-5.53.2.tgz", - "integrity": "sha512-8zhdPtT0+zbBD39p5+GIIA5ls4aFksCKNbL661bmKK/bnHuoAJZ4JGVpYpgc+Y2E21LfFsNGBx1DEGnSRVfMNQ==", + "version": "5.53.3", + "resolved": "https://registry.npmjs.org/@tanstack/query-sync-storage-persister/-/query-sync-storage-persister-5.53.3.tgz", + "integrity": "sha512-MsYqg56RVVp4JhA3ne93OiUBbUkcDydTfWkSphMcLolMYp72nb00vozviRp2c81fCM2qwruHWHdpJeOZ0posyg==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.53.2", - "@tanstack/query-persist-client-core": "5.53.2" + "@tanstack/query-core": "5.53.3", + "@tanstack/query-persist-client-core": "5.53.3" }, "funding": { "type": "github", @@ -3996,12 +3996,12 @@ } }, "node_modules/@tanstack/svelte-query": { - "version": "5.53.2", - "resolved": "https://registry.npmjs.org/@tanstack/svelte-query/-/svelte-query-5.53.2.tgz", - "integrity": "sha512-LZz4LIdJ0QuScXF4KZWD5LzY5yrDUGWzTPpUlvoJaI3QYyLSL4hrlb+k2Hps4fhOHnNCrMPD7QXl3/RttnWxKQ==", + "version": "5.53.3", + "resolved": "https://registry.npmjs.org/@tanstack/svelte-query/-/svelte-query-5.53.3.tgz", + "integrity": "sha512-I61FqliBmEbqnXExtHQqU37Yb0IPNFZOE5mlVQ4WDLlBqCb0QhqMI03hBI9CP4DHWDmb5X7iQBRyv4T5/dpeHw==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.53.2" + "@tanstack/query-core": "5.53.3" }, "funding": { "type": "github", @@ -4012,9 +4012,9 @@ } }, "node_modules/@tanstack/svelte-query-devtools": { - "version": "5.53.2", - "resolved": "https://registry.npmjs.org/@tanstack/svelte-query-devtools/-/svelte-query-devtools-5.53.2.tgz", - "integrity": "sha512-HFGlq7275vbnYTCaenJoLtLs/VMxQYiol8Ho2r5U0pX2tw82MtgOHo1pSxO0DRjpTJ1vs+HkM6RXwWvu9p6R3Q==", + "version": "5.53.3", + "resolved": "https://registry.npmjs.org/@tanstack/svelte-query-devtools/-/svelte-query-devtools-5.53.3.tgz", + "integrity": "sha512-nkFuuL6afh6nY5UpqznNLcNhywnIzNoazvMbxFVavOkTKTTEcuNtWOf/G3iZRe9RTzg8QndST5R4kLOdQe5QpA==", "dev": true, "license": "MIT", "dependencies": { @@ -4026,24 +4026,24 @@ "url": "https://github.com/sponsors/tannerlinsley" }, "peerDependencies": { - "@tanstack/svelte-query": "^5.53.2", + "@tanstack/svelte-query": "^5.53.3", "svelte": "^3.54.0 || ^4.0.0 || ^5.0.0-next.0" } }, "node_modules/@tanstack/svelte-query-persist-client": { - "version": "5.53.2", - "resolved": "https://registry.npmjs.org/@tanstack/svelte-query-persist-client/-/svelte-query-persist-client-5.53.2.tgz", - "integrity": "sha512-ts/Z9SvNsjXXT5M5aWUg3Xij8vx+g26r9ZpHU8yYtjLahu0vfzurqY9rpMWA04BvpLa9KLQpmbQ6dCV+1v1u7Q==", + "version": "5.53.3", + "resolved": "https://registry.npmjs.org/@tanstack/svelte-query-persist-client/-/svelte-query-persist-client-5.53.3.tgz", + "integrity": "sha512-K8zZQX1FtHmMr53JGVs3Z280f/VWiAQ7EiaZnx9TiTzZDtuOBZyz0mbDj4dyhdLin32C/ZAMgYiUkn73G7o1UA==", "license": "MIT", "dependencies": { - "@tanstack/query-persist-client-core": "5.53.2" + "@tanstack/query-persist-client-core": "5.53.3" }, "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" }, "peerDependencies": { - "@tanstack/svelte-query": "^5.53.2", + "@tanstack/svelte-query": "^5.53.3", "svelte": "^3.54.0 || ^4.0.0 || ^5.0.0-next.0" } }, @@ -4106,9 +4106,9 @@ } }, "node_modules/@total-typescript/ts-reset": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@total-typescript/ts-reset/-/ts-reset-0.6.0.tgz", - "integrity": "sha512-HWZnkM+5z3INAUZMohVXvX8/vm9sjmfmV2NRAswvv5WsU2m+OZsHAVZ0fl8xf2QH9kyPkinghVW6g3DOQ2xt5Q==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@total-typescript/ts-reset/-/ts-reset-0.6.1.tgz", + "integrity": "sha512-cka47fVSo6lfQDIATYqb/vO1nvFfbPw7uWLayIXIhGETj0wcOOlrlkobOMDNQOFr9QOafegUPq13V2+6vtD7yg==", "dev": true, "license": "MIT" }, @@ -4270,18 +4270,16 @@ }, "node_modules/@union/client": { "name": "@jsr/union__client", - "version": "0.0.1-rc.52", - "resolved": "https://npm.jsr.io/~/11/@jsr/union__client/0.0.1-rc.52.tgz", - "integrity": "sha512-HY0QHoMPJSnyLcvo4RmtQ7lw1v1BUmn0RIImaJCCALxHcVvlV3NzMuBfhxkpbNMwJS3lUr0ox/efIoUjdBkNyg==", + "version": "0.0.4", + "resolved": "https://npm.jsr.io/~/11/@jsr/union__client/0.0.4.tgz", + "integrity": "sha512-X5RgvxTbONfmRvZonGMDbgBvI6JmRFNEH/N9MEA3+N/8ZSK51dB76i1hJ4Hs8SwhfaGJRlOcR32JOLI7vLwKJg==", "dependencies": { "@cosmjs/cosmwasm-stargate": "0.32.4", - "@cosmjs/encoding": "^0.32.4", - "@cosmjs/proto-signing": "^0.32.4", "@cosmjs/stargate": "0.32.4", - "@cosmjs/tendermint-rpc": "0.32.4", "@scure/base": "^1.1.7", + "neverthrow": "^7.1.0", "ofetch": "^1.3.4", - "viem": "^2.18.8" + "viem": "^2.21.1" } }, "node_modules/@vitest/expect": { @@ -6387,6 +6385,21 @@ "reusify": "^1.0.4" } }, + "node_modules/fdir": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.3.0.tgz", + "integrity": "sha512-QOnuT+BOtivR77wYvCWHfGt9s4Pz1VIMbD463vegT5MLqNXy8rYFT/lPVEqf/bhYeT6qmqrNHhsX+rWwe3rOCQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/fflate": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", @@ -7848,6 +7861,15 @@ "node": "^18 || >=20" } }, + "node_modules/neverthrow": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/neverthrow/-/neverthrow-7.1.0.tgz", + "integrity": "sha512-TQ+ucrkixq0lUL+KYcGBjiuyHKvJ/Vb7i6QsaPdTLjlqmHq8WuoWGg17VQnmcx23hKi5w0OTQuHP4HD9cyO0Vg==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/node-addon-api": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", @@ -8531,9 +8553,9 @@ } }, "node_modules/postcss": { - "version": "8.4.43", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.43.tgz", - "integrity": "sha512-gJAQVYbh5R3gYm33FijzCZj7CHyQ3hWMgJMprLUlIYqCwTeZhBQ19wp0e9mA25BUbEvY5+EXuuaAjqQsrBxQBQ==", + "version": "8.4.44", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.44.tgz", + "integrity": "sha512-Aweb9unOEpQ3ezu4Q00DPvvM2ZTUitJdNKeP/+uQgr1IBIqu574IaZoURId7BKtWMREwzKa9OgzPzezWGPWFQw==", "dev": true, "funding": [ { @@ -10173,86 +10195,27 @@ } }, "node_modules/svelte-check": { - "version": "3.8.6", - "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.8.6.tgz", - "integrity": "sha512-ij0u4Lw/sOTREP13BdWZjiXD/BlHE6/e2e34XzmVmsp5IN4kVa3PWP65NM32JAgwjZlwBg/+JtiNV1MM8khu0Q==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.0.0.tgz", + "integrity": "sha512-QgKO6OQbee9B2dyWZgrGruS3WHKrUZ718Ug53nK45vamsx93Al3on6tOrxyCMVX+OMOLLlrenn7b2VAomePwxQ==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.17", + "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^3.4.1", + "fdir": "^6.2.0", "picocolors": "^1.0.0", - "sade": "^1.7.4", - "svelte-preprocess": "^5.1.3", - "typescript": "^5.0.3" + "sade": "^1.7.4" }, "bin": { "svelte-check": "bin/svelte-check" }, - "peerDependencies": { - "svelte": "^3.55.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0" - } - }, - "node_modules/svelte-check/node_modules/svelte-preprocess": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.1.4.tgz", - "integrity": "sha512-IvnbQ6D6Ao3Gg6ftiM5tdbR6aAETwjhHV+UKGf5bHGYR69RQvF1ho0JKPcbUON4vy4R7zom13jPjgdOWCQ5hDA==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "@types/pug": "^2.0.6", - "detect-indent": "^6.1.0", - "magic-string": "^0.30.5", - "sorcery": "^0.11.0", - "strip-indent": "^3.0.0" - }, "engines": { - "node": ">= 16.0.0" + "node": ">= 18.0.0" }, "peerDependencies": { - "@babel/core": "^7.10.2", - "coffeescript": "^2.5.1", - "less": "^3.11.3 || ^4.0.0", - "postcss": "^7 || ^8", - "postcss-load-config": "^2.1.0 || ^3.0.0 || ^4.0.0 || ^5.0.0", - "pug": "^3.0.0", - "sass": "^1.26.8", - "stylus": "^0.55.0", - "sugarss": "^2.0.0 || ^3.0.0 || ^4.0.0", - "svelte": "^3.23.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0", - "typescript": ">=3.9.5 || ^4.0.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "coffeescript": { - "optional": true - }, - "less": { - "optional": true - }, - "postcss": { - "optional": true - }, - "postcss-load-config": { - "optional": true - }, - "pug": { - "optional": true - }, - "sass": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "typescript": { - "optional": true - } + "svelte": "^4.0.0 || ^5.0.0-next.0", + "typescript": ">=5.0.0" } }, "node_modules/svelte-hmr": { diff --git a/app/package.json b/app/package.json index f5f17318dc..c53f519b89 100644 --- a/app/package.json +++ b/app/package.json @@ -19,13 +19,13 @@ "@cosmjs/encoding": "^0.32.4", "@sentry/sveltekit": "^8.27.0", "@tanstack/match-sorter-utils": "^8.19.4", - "@tanstack/query-core": "^5.53.2", - "@tanstack/query-sync-storage-persister": "^5.53.2", - "@tanstack/svelte-query": "^5.53.2", - "@tanstack/svelte-query-persist-client": "^5.53.2", + "@tanstack/query-core": "^5.53.3", + "@tanstack/query-sync-storage-persister": "^5.53.3", + "@tanstack/svelte-query": "^5.53.3", + "@tanstack/svelte-query-persist-client": "^5.53.3", "@tanstack/svelte-table": "^8.20.5", "@tanstack/svelte-virtual": "^3.10.6", - "@union/client": "npm:@jsr/union__client@^0.0.1-rc.52", + "@union/client": "npm:@jsr/union__client@^0.0.4", "@wagmi/connectors": "^5.1.8", "@wagmi/core": "^2.13.4", "bech32": "^2.0.0", @@ -65,8 +65,8 @@ "@tailwindcss/container-queries": "^0.1.1", "@tailwindcss/forms": "^0.5.8", "@tailwindcss/typography": "^0.5.15", - "@tanstack/svelte-query-devtools": "^5.53.2", - "@total-typescript/ts-reset": "^0.6.0", + "@tanstack/svelte-query-devtools": "^5.53.3", + "@total-typescript/ts-reset": "^0.6.1", "@types/node": "^22.5.2", "@types/postcss-import": "^14.0.3", "@types/three": "^0.168.0", @@ -77,12 +77,12 @@ "graphql": "^16.9.0", "jsr": "^0.13.1", "patch-package": "^8.0.0", - "postcss": "^8.4.43", + "postcss": "^8.4.44", "postcss-import": "^16.1.0", "process": "^0.11.10", "rollup-plugin-visualizer": "^5.12.0", "svelte": "^4.2.19", - "svelte-check": "^3.8.6", + "svelte-check": "^4.0.0", "svelte-preprocess": "^6.0.2", "tailwind-merge": "^2.5.2", "tailwind-scrollbar": "^3.1.0", diff --git a/app/src/lib/wallet/evm/config.ts b/app/src/lib/wallet/evm/config.ts index 33734608b5..ea6e18e4e1 100644 --- a/app/src/lib/wallet/evm/config.ts +++ b/app/src/lib/wallet/evm/config.ts @@ -15,17 +15,17 @@ import { switchChain as _switchChain, createStorage as createWagmiStorage } from "@wagmi/core" +import type { Address } from "viem" import { sleep } from "$lib/utilities" -import { derived, writable, type Readable } from "svelte/store" import { KEY } from "$lib/constants/keys.ts" +import type { UserAddressEvm } from "$lib/types" import { APP_INFO } from "$lib/constants/app.ts" import type { ChainWalletStore } from "$lib/wallet/types" -import { sepolia, berachainTestnetbArtio, arbitrumSepolia, scrollSepolia } from "@wagmi/core/chains" +import { derived, writable, type Readable } from "svelte/store" import { injected, metaMask, coinbaseWallet } from "@wagmi/connectors" -import type { UserAddressEvm } from "$lib/types" -import type { Address } from "viem" +import { sepolia, berachainTestnetbArtio, arbitrumSepolia, scrollSepolia } from "@wagmi/core/chains" -const chains = [sepolia] as const +const chains = [sepolia, berachainTestnetbArtio, arbitrumSepolia, scrollSepolia] as const export type ConfiguredChainId = (typeof chains)[number]["id"] export type Wallet = GetAccountReturnType @@ -34,7 +34,7 @@ export type ConnectedWallet = Wallet & { status: "connected" } export type ConnectorType = "injected" | "walletConnect" export const config = createConfig({ - chains: [sepolia, berachainTestnetbArtio, arbitrumSepolia, scrollSepolia], + chains, cacheTime: 4_000, pollingInterval: 4_000, syncConnectedChain: true, diff --git a/typescript-sdk/LICENSE b/typescript-sdk/LICENSE new file mode 100644 index 0000000000..8a0d93a624 --- /dev/null +++ b/typescript-sdk/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Union.fi Labs, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/typescript-sdk/bun.lockb b/typescript-sdk/bun.lockb index bfc043f51ab6a2d0ae37125b4695c3f0ae564cc8..80e332569fcec3719696fb6cef9a173ca15f02a6 100755 GIT binary patch delta 506 zcmdmTnyu$F+XOwOU$>eCxrKWFYw4>S_6QhU_4&An?^&{w`GaRaFPxI^7x=SeV}OP4 zw0LE_gAs#o*X)0vx6b<*pYz9hXVR?`ttVO%h5LPEEM8_$ z^OCkWaMs~r_E+XvwJ${M)|Q9o@7j0a_o>afdrn0&)=amMU<`2NV`N|eB8FHH0R%8U z6Of$*72}7hVFrphf)oLP091@+`bP;yVOv(9P!d>zfk6l=#RlX@0ofoR3>9MsvZH}) z79cGG72}xhD9I=s$q5vy0b&pkgGzA$*)>2m2#7<)xPj~#ARA=71XPR%$nF4QkQtIt zu@xW&5C}|PE6I4DQFD5y6ytwJtL?4QjMYq>(}0qxKpZq(P>xZZ(?JfRc)@l@ImUK2 z&I>9KzQgp3DvSph4YyBHWxS@rIl&JiJYjpGAEP@X=K+5R-);Lre?}X3pt^t47#nyw aJC;DiXH0Kj!f45L0?PffWcsNkjIRLkc5t8o delta 562 zcmeA<&9>z<+XOwOQjK%AXP#cWQ_+k|rdvob2GsK~GB5xU12+(Z0F2KB zWXFOefPfdOh8f6Cf{O7$#aMu1jvz%qzz-E;1+v}1A`A=yP%$2IaOF*Ugfb0$+25FOoid_IPfWSg-x|AHF+VnM&j60@hNHPA|o*~Ux!6c{& zl%58}sX!bARA)8)r7WX5XNDX^+llRxa*XY4oIg|`{EX=%bKX3a?e?}X3pt@_*7#nywH!OjOADAA$gwc}g1C*<>bowe4prcMK HVLSo=F4=62 diff --git a/typescript-sdk/jsr.json b/typescript-sdk/jsr.json index eec9e8e199..345cade97e 100644 --- a/typescript-sdk/jsr.json +++ b/typescript-sdk/jsr.json @@ -1,12 +1,12 @@ { "$schema": "https://jsr.io/schema/config-file.v1.json", "name": "@union/client", - "version": "0.0.1-rc.66", + "version": "0.0.4", "license": "MIT", "exports": { ".": "./src/mod.ts" }, "publish": { - "include": ["./src/**/*.ts", "./jsr.json", "./README.md"] + "include": ["./src/**/*.ts", "./jsr.json", "./README.md", "./LICENSE"] } } diff --git a/typescript-sdk/package.json b/typescript-sdk/package.json index f68c830cf5..cddb7ebeb3 100644 --- a/typescript-sdk/package.json +++ b/typescript-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@union/client", - "version": "0.0.1-rc.66", + "version": "0.0.4", "homepage": "https://jsr.io/@union/client", "description": "Union Labs cross-chain transfers client", "type": "module", @@ -31,7 +31,7 @@ "@arethetypeswrong/cli": "^0.15.4", "@cosmjs/amino": "0.32.4", "@scure/base": "^1.1.7", - "@total-typescript/ts-reset": "^0.6.0", + "@total-typescript/ts-reset": "^0.6.1", "@types/bun": "^1.1.8", "@types/node": "^22.5.2", "consola": "^3.2.3", diff --git a/typescript-sdk/scripts/publish.ts b/typescript-sdk/scripts/publish.ts index 6c078fb1d1..e07053f26e 100644 --- a/typescript-sdk/scripts/publish.ts +++ b/typescript-sdk/scripts/publish.ts @@ -1,19 +1,35 @@ #!/usr/bin/env bun import * as Bun from "bun" +import { parseArgs } from "node:util" import { consola } from "./logger.ts" +import jsrJson from "../jsr.json" with { type: "json" } +import packageJson from "../package.json" with { type: "json" } +const CURRENT_JSR_JSON_VERSION = jsrJson.version +const CURRENT_PACKAGE_JSON_VERSION = packageJson.version /** * Use this script to publish a new version of the TypeScript SDK to JSR registry * This will check if the contracts in the SDK are up to date with the contracts in the registry * If not it will fail - * - * Usage: - * - * `bun scripts/publish` - * `DRY_RUN=1 bun scripts/publish` - */ +* +* Usage: +* +* `bun scripts/publish --period patch` -const DRY_RUN = import.meta.env.DRY_RUN === "1" ?? process.env.DRY_RUN === "1" ?? true +* `bun scripts/publish --period minor --dry-run` +*/ + +const { values } = parseArgs({ + args: process.argv.slice(2), + strict: true, + options: { + period: { type: "string", default: "patch" }, + "dry-run": { type: "boolean", default: false } + } +}) + +const PERIOD = values.period ?? "patch" +const DRY_RUN = values["dry-run"] ?? false main().catch(_ => { consola.error(_) @@ -26,20 +42,24 @@ async function main() { return await Bun.$ /* sh */`bunx jsr publish --allow-dirty --allow-slow-types --dry-run` } - const bumpPackage = await Bun.$ /* sh */`npm version prerelease --preid rc --no-git-tag-version` + const bumpPackage = await Bun.$ /* sh */`npm version --preiod ${PERIOD} --no-git-tag-version` + const version = bumpPackage.text().trim().replace(/^v/, "") // sync jsr.json version with package.json version - await Bun.$ /* sh */`jq --arg version "${version}" '.version = $version' jsr.json > jsr.temp.json && mv jsr.temp.json jsr.json` + const syncJsr = + await Bun.$ /* sh */`jq --arg version "${version}" '.version = $version' jsr.json > jsr.temp.json && mv jsr.temp.json jsr.json` + + consola.info("Sync jsr.json version with package.json version", syncJsr.text()) return await Bun.$ /* sh */`bunx jsr publish --allow-dirty --allow-slow-types` } catch (error) { const errorMessage = error instanceof Error ? error.message : error - console.error(errorMessage) + consola.error(errorMessage) // revert changes await resetVersions() - console.info("Reset package.json version") + consola.info("Reset package.json version") } } diff --git a/typescript-sdk/src/client/evm.ts b/typescript-sdk/src/client/evm.ts index 0fe51744dd..34c0b49d0d 100644 --- a/typescript-sdk/src/client/evm.ts +++ b/typescript-sdk/src/client/evm.ts @@ -19,14 +19,21 @@ import type { TransferAssetsParameters } from "./types.ts" import { createPfmMemo, getHubbleChainDetails } from "../pfm.ts" import { sepolia, scrollSepolia, arbitrumSepolia, berachainTestnetbArtio } from "viem/chains" -export const evmChainId = [ +export { + sepolia, + scrollSepolia, + arbitrumSepolia, + berachainTestnetbArtio +} from "viem/chains" + +export const evmChains = [sepolia, scrollSepolia, arbitrumSepolia, berachainTestnetbArtio] as const +export const evmChainId: ReadonlyArray<`${(typeof evmChains)[number]["id"]}`> = [ `${sepolia.id}`, `${scrollSepolia.id}`, `${arbitrumSepolia.id}`, `${berachainTestnetbArtio.id}` ] as const export type EvmChainId = `${(typeof evmChainId)[number]}` -export const evmChains = [sepolia, scrollSepolia, arbitrumSepolia, berachainTestnetbArtio] as const export interface EvmClientParameters { chainId: EvmChainId @@ -40,7 +47,10 @@ export const chainIdToChain = (chainId: EvmChainId) => ) export const createEvmClient = (parameters: EvmClientParameters) => { - return createWalletClient({ ...parameters, chain: chainIdToChain(parameters.chainId) }) + return createWalletClient({ + ...parameters, + chain: chainIdToChain(parameters.chainId) + }) .extend(publicActions) .extend(client => ({ transferAsset: async ({ diff --git a/typescript-sdk/src/convert.ts b/typescript-sdk/src/convert.ts index 062ab328a7..9fcca0fc15 100644 --- a/typescript-sdk/src/convert.ts +++ b/typescript-sdk/src/convert.ts @@ -3,11 +3,22 @@ import { bech32 } from "@scure/base" import { raise } from "./utilities/index.ts" import type { Bech32Address, HexAddress } from "./types.ts" +/** + * convert a byte array to a hex string + * @example + * ```ts + * bytesToHex(new Uint8Array([1, 2, 3])) + * ``` + */ export const bytesToHex = (byteArray: Uint8Array): string => byteArray.reduce((hex, byte) => hex + byte.toString(16).padStart(2, "0"), "") /** * convert a bech32 address (cosmos, osmosis, union addresses) to hex address (evm) + * @example + * ```ts + * bech32AddressToHex({ address: "union1qp0wtsfltjk9rnvyu3fkdv0s0skp4y5y3py96f" }) + * ``` */ export function bech32AddressToHex({ address }: { address: string }): HexAddress { const { words } = bech32.decode(address) @@ -17,6 +28,13 @@ export function bech32AddressToHex({ address }: { address: string }): HexAddress /** * convert an Hex address (evm) to a bech32 address (cosmos, osmosis, union addresses) + * @example + * ```ts + * hexAddressToBech32({ + * bech32Prefix: "union", + * address: "0x779877A7B0D9E8603169DdbD7836e478b4624789" + * }) + * ``` */ export function hexAddressToBech32({ address, @@ -27,6 +45,16 @@ export function hexAddressToBech32({ return bech32.encode(bech32Prefix, words) } +/** + * convert a bech32 address (cosmos, osmosis, union addresses) to a bech32 address with a different prefix + * @example + * ```ts + * bech32ToBech32Address({ + * toPrefix: "stride", + * address: "union1qp0wtsfltjk9rnvyu3fkdv0s0skp4y5y3py96f", + * }) + * ``` + */ export function bech32ToBech32Address({ address, toPrefix @@ -34,6 +62,16 @@ export function bech32ToBech32Address({ return bech32.encode(toPrefix, bech32.decode(address).words) as Bech32Address } +/** + * convert a byte array to a bech32 address with a different prefix + * @example + * ```ts + * bytesToBech32Address({ + * toPrefix: "stride", + * bytes: new Uint8Array([1, 2, 3]), + * }) + * ``` + */ export function bytesToBech32Address({ bytes, toPrefix @@ -44,11 +82,36 @@ export function bytesToBech32Address({ /** * @credit https://stackoverflow.com/a/78013306/10605502 */ -const LUT_HEX_4b = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"] -const LUT_HEX_8b = new Array(0x100) as Array +const LUT_HEX_4b: Array = [ + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "A", + "B", + "C", + "D", + "E", + "F" +] +const LUT_HEX_8b: Array = new Array(0x100) for (let n = 0; n < 0x100; n++) { LUT_HEX_8b[n] = `${LUT_HEX_4b[(n >>> 4) & 0xf]}${LUT_HEX_4b[n & 0xf]}` } + +/** + * convert a hex string to a byte array + * @example + * ```ts + * hexStringToUint8Array("0x779877A7B0D9E8603169DdbD7836e478b4624789") + * ``` + */ export function uint8ArrayToHexString(uintArray: Uint8Array): string { let out = "" for (let index = 0, edx = uintArray.length; index < edx; index++) { @@ -57,7 +120,14 @@ export function uint8ArrayToHexString(uintArray: Uint8Array): string { return out } -export function hexStringToUint8Array(hexString: string) { +/** + * convert a hex string to a byte array + * @example + * ```ts + * hexStringToUint8Array("0x779877A7B0D9E8603169DdbD7836e478b4624789") + * ``` + */ +export function hexStringToUint8Array(hexString: string): Uint8Array { if (hexString.length % 2 !== 0) raise("Hex must have an even number of characters") const arrayBuffer = new Uint8Array(hexString.length / 2) @@ -66,14 +136,3 @@ export function hexStringToUint8Array(hexString: string) { } return arrayBuffer } - -export const normalizeToCosmosAddress = ({ - address, - bech32Prefix -}: { address: string; bech32Prefix: string }): Bech32Address => - isHex(address) ? hexAddressToBech32({ address, bech32Prefix }) : (address as Bech32Address) - -export const normalizeToEvmAddress = (address: string): HexAddress => - isHex(address) ? address : bech32AddressToHex({ address }) - -export const munoToUno = (muno: string | number) => (Number(muno) / 1e6).toFixed(6) diff --git a/typescript-sdk/src/pfm.ts b/typescript-sdk/src/pfm.ts index 368d98b213..bf1601586c 100644 --- a/typescript-sdk/src/pfm.ts +++ b/typescript-sdk/src/pfm.ts @@ -2,7 +2,11 @@ import { err, ok, Result } from "neverthrow" import type { ChainId } from "./client/types.ts" import { offchainQuery } from "./query/offchain/hubble.ts" -export const createPfmMemo = Result.fromThrowable( +export const createPfmMemo: (_args: { + port: string + channel: string + receiver: string +}) => Result = Result.fromThrowable( ({ port, channel, @@ -11,7 +15,7 @@ export const createPfmMemo = Result.fromThrowable( port: string channel: string receiver: string - }) => + }): string => JSON.stringify({ forward: { port, diff --git a/typescript-sdk/src/query/offchain/hubble.ts b/typescript-sdk/src/query/offchain/hubble.ts index 778a60a280..842670395b 100644 --- a/typescript-sdk/src/query/offchain/hubble.ts +++ b/typescript-sdk/src/query/offchain/hubble.ts @@ -19,6 +19,17 @@ const hubbleRestFetch = ofetch.create({ }) export const offchainQuery = { + /** + * get all chains details from hubble + * @example + * ```ts + * const chains = await offchainQuery.chains({ + * includeAssets: true, + * includeEndpoints: true, + * includeContracts: true, + * }) + * ``` + */ chains: async ({ includeEndpoints = false, includeContracts = false, @@ -27,10 +38,12 @@ export const offchainQuery = { includeEndpoints?: boolean includeContracts?: boolean includeAssets?: boolean - } = {}) => { + } = {}): Promise< + OffchainQueryBaseResponse> + > => { return await hubbleRestFetch< OffchainQueryBaseResponse> - >(`/chains`, { + >("/chains", { query: { include_rpcs: includeEndpoints, include_contracts: includeContracts, @@ -38,6 +51,18 @@ export const offchainQuery = { } }) }, + /** + * get chain details from hubble + * @example + * ```ts + * const chain = await offchainQuery.chain({ + * includeAssets: true, + * includeEndpoints: true, + * includeContracts: true, + * chainId: "stride-internal-1", + * }) + * ``` + */ chain: async ({ chainId, includeEndpoints = false, @@ -48,7 +73,9 @@ export const offchainQuery = { includeEndpoints?: boolean includeContracts?: boolean includeAssets?: boolean - }) => { + }): Promise< + OffchainQueryBaseResponse> + > => { return await hubbleRestFetch< OffchainQueryBaseResponse> >(`/chains/${chainId}`, { diff --git a/typescript-sdk/src/query/offchain/tenderly.ts b/typescript-sdk/src/query/offchain/tenderly.ts index 074c6aa450..6211819538 100644 --- a/typescript-sdk/src/query/offchain/tenderly.ts +++ b/typescript-sdk/src/query/offchain/tenderly.ts @@ -10,6 +10,21 @@ const queryHeaders = new Headers({ "X-Access-Key": "zQs9t0eoXQybyVbGfV4dSihLElP0Uyl1" }) +/** + * simulate a transaction on evm using Tenderly API + * @example + * ```ts + * const gas = await simulateTransaction({ + * memo: "test", + * amount: 1n, + * account: evmAccount, + * sourceChannel: "channel-1", + * recipient: "0x8478B37E983F520dBCB5d7D3aAD8276B82631aBd", + * denomAddress: "0x779877A7B0D9E8603169DdbD7836e478b4624789", + * relayContractAddress: "0x2222222222222222222222222222222222222222", + * }) + * ``` + */ export async function simulateTransaction({ memo, amount, diff --git a/typescript-sdk/src/query/on-chain.ts b/typescript-sdk/src/query/on-chain.ts index 96c59763dc..70018ceeee 100644 --- a/typescript-sdk/src/query/on-chain.ts +++ b/typescript-sdk/src/query/on-chain.ts @@ -7,12 +7,31 @@ const queryHeaders = new Headers({ "Content-Type": "application/json" }) +/** + * get the current block height + * @example + * ```ts + * const height = await getCosmosHeight({ + * rpcUrl: "https://rpc.testnet-8.union.build", + * }) + * ``` + */ export async function getCosmosHeight({ rpcUrl }: { rpcUrl: string }) { const response = await fetch(`${rpcUrl}/header`) const json = (await response.json()) as { result: { header: { height: string } } } return Number.parseInt(json.result.header.height) } +/** + * get the transaction receipt for a given transaction hash + * @example + * ```ts + * const receipt = await getCosmosTransactionReceipt({ + * rpcUrl: "https://rpc.testnet-8.union.build", + * hash: "A6E276CE66CDB35C0CAAC49EC9AAB3CB2CF8A34C807A4C729EA385E64C88D69B", + * }) + * ``` + */ export async function getCosmosTransactionReceipt(params: { hash: string rpcUrl: string @@ -22,6 +41,16 @@ export async function getCosmosTransactionReceipt(params: { return await response.json() } +/** + * get the transactions sent and received by an address + * @example + * ```ts + * const transactions = await getCosmosAccountTransactions({ + * address: "union1qp0wtsfltjk9rnvyu3fkdv0s0skp4y5y3py96f", + * rpcUrl: "https://rpc.testnet-8.union.build", + * }) + * ``` + */ export async function getCosmosAccountTransactions({ address, rpcUrl diff --git a/typescript-sdk/src/transfer/cosmos.ts b/typescript-sdk/src/transfer/cosmos.ts index af691e27de..436b55c4fe 100644 --- a/typescript-sdk/src/transfer/cosmos.ts +++ b/typescript-sdk/src/transfer/cosmos.ts @@ -4,6 +4,7 @@ import { assertIsDeliverTxSuccess, type MsgTransferEncodeObject } from "@cosmjs/stargate" +import { SigningCosmWasmClient, type ExecuteInstruction } from "@cosmjs/cosmwasm-stargate" import type { Coin, MessageTransferWithOptionals, @@ -11,8 +12,22 @@ import type { } from "../types.ts" import { timestamp } from "../utilities/index.ts" import { ok, err, type Result, ResultAsync } from "neverthrow" -import { SigningCosmWasmClient, type ExecuteInstruction } from "@cosmjs/cosmwasm-stargate" +/** + * connect a stargate client with a signer + * @example + * ```ts + * const client = await connectStargateWithSigner({ + * account: cosmosAccount, + * rpcUrl: "https://rpc.testnet-8.union.build", + * gasPrice: { amount: "0.0025", denom: "muno" }, + * }) + * + * if (client.isOk()) { + * const tx = await client.value.getTx("A6E276CE66CDB35C0CAAC49EC9AAB3CB2CF8A34C807A4C729EA385E64C88D69B") + * } + * ``` + */ export function connectStargateWithSigner({ rpcUrl, account, @@ -30,6 +45,21 @@ export function connectStargateWithSigner({ ) } +/** + * connect a stargate client with a signer + * @example + * ```ts + * const client = await connectCosmwasmWithSigner({ + * account: cosmosAccount, + * rpcUrl: "https://rpc.testnet-8.union.build", + * gasPrice: { amount: "0.0025", denom: "muno" }, + * }) + * + * if (client.isOk()) { + * const tx = await client.value.getTx("A6E276CE66CDB35C0CAAC49EC9AAB3CB2CF8A34C807A4C729EA385E64C88D69B") + * } + * ``` + */ export function connectCosmwasmWithSigner({ rpcUrl, account, @@ -52,7 +82,25 @@ export function connectCosmwasmWithSigner({ * - https://github.com/cosmos/ibc/blob/main/spec/app/ics-020-fungible-token-transfer/README.md * - transfer tokens from ibc-enabled chain to another ibc-enabled chain * - * TODO: add JSDoc with examples + * @example + * ```ts + * const transfer = await ibcTransfer(client, { + * gasPrice: { amount: "0.0025", denom: "muno" }, + * account: cosmosAccount, + * rpcUrl: "https://rpc.testnet-8.union.build", + * messageTransfers: [ + * { + * sourcePort: "transfer", + * sourceChannel: "channel-1", + * sender: "0x8478B37E983F520dBCB5d7D3aAD8276B82631aBd", + * token: { denom: "muno", amount: "1" }, + * timeoutHeight: { revisionHeight: 888n, revisionNumber: 8n }, + * receiver: "0x8478B37E983F520dBCB5d7D3aAD8276B82631aBd", + * memo: "test", + * }, + * ], + * }) + * ``` */ export async function ibcTransfer({ gasPrice, @@ -73,7 +121,11 @@ export async function ibcTransfer({ if (accountResult.isErr()) return err(accountResult.error) const _account = accountResult.value - const signingClient = await connectStargateWithSigner({ rpcUrl, account, gasPrice }) + const signingClient = await connectStargateWithSigner({ + rpcUrl, + account, + gasPrice + }) if (signingClient.isErr()) return err(signingClient.error) const _signingClient = signingClient.value @@ -95,7 +147,26 @@ export async function ibcTransfer({ } /** - * TODO: add JSDoc with examples + * simulate an ibc transfer + * @example + * ```ts + * const transfer = await ibcTransferSimulate(client, { + * gasPrice: { amount: "0.0025", denom: "muno" }, + * account: cosmosAccount, + * rpcUrl: "https://rpc.testnet-8.union.build", + * messageTransfers: [ + * { + * sourcePort: "transfer", + * sourceChannel: "channel-1", + * sender: "0x8478B37E983F520dBCB5d7D3aAD8276B82631aBd", + * token: { denom: "muno", amount: "1" }, + * timeoutHeight: { revisionHeight: 888n, revisionNumber: 8n }, + * receiver: "0x8478B37E983F520dBCB5d7D3aAD8276B82631aBd", + * memo: "test", + * }, + * ], + * }) + * ``` */ export async function ibcTransferSimulate({ gasPrice, @@ -116,7 +187,11 @@ export async function ibcTransferSimulate({ if (accountResult.isErr()) return err(accountResult.error) const _account = accountResult.value - const signingClient = await connectStargateWithSigner({ rpcUrl, account, gasPrice }) + const signingClient = await connectStargateWithSigner({ + rpcUrl, + account, + gasPrice + }) if (signingClient.isErr()) return err(signingClient.error) const _signingClient = signingClient.value @@ -136,6 +211,30 @@ export async function ibcTransferSimulate({ return ok(gas.toString()) } +/** + * transfer a wasm contract + * @example + * ```ts + * const transfer = await cosmwasmTransfer(client, { + * gasPrice: { amount: "0.0025", denom: "muno" }, + * account: cosmosAccount, + * rpcUrl: "https://rpc.testnet-8.union.build", + * instructions: [ + * { + * contractAddress: "0x2222222222222222222222222222222222222222", + * msg: { + * transfer: { + * channel: "channel-1", + * receiver: "0x8478B37E983F520dBCB5d7D3aAD8276B82631aBd", + * memo: "test", + * }, + * }, + * funds: [{ denom: "muno", amount: "1" }], + * }, + * ], + * }) + * ``` + */ export async function cosmwasmTransfer({ gasPrice, instructions, @@ -155,7 +254,11 @@ export async function cosmwasmTransfer({ if (accountResult.isErr()) return err(accountResult.error) const _account = accountResult.value - const signingClient = await connectCosmwasmWithSigner({ rpcUrl, account, gasPrice }) + const signingClient = await connectCosmwasmWithSigner({ + rpcUrl, + account, + gasPrice + }) if (signingClient.isErr()) return err(signingClient.error) const _signingClient = signingClient.value @@ -165,6 +268,30 @@ export async function cosmwasmTransfer({ return ok(response.transactionHash) } +/** + * simulate a wasm contract + * @example + * ```ts + * const transfer = await cosmwasmTransferSimulate(client, { + * gasPrice: { amount: "0.0025", denom: "muno" }, + * account: cosmosAccount, + * rpcUrl: "https://rpc.testnet-8.union.build", + * instructions: [ + * { + * contractAddress: "0x2222222222222222222222222222222222222222", + * msg: { + * transfer: { + * channel: "channel-1", + * receiver: "0x8478B37E983F520dBCB5d7D3aAD8276B82631aBd", + * memo: "test", + * }, + * }, + * funds: [{ denom: "muno", amount: "1" }], + * }, + * ], + * }) + * ``` + */ export async function cosmwasmTransferSimulate({ gasPrice, instructions, @@ -184,7 +311,11 @@ export async function cosmwasmTransferSimulate({ if (accountResult.isErr()) return err(accountResult.error) const _account = accountResult.value - const signingClient = await connectCosmwasmWithSigner({ rpcUrl, account, gasPrice }) + const signingClient = await connectCosmwasmWithSigner({ + rpcUrl, + account, + gasPrice + }) if (signingClient.isErr()) return err(signingClient.error) const _signingClient = signingClient.value @@ -206,6 +337,19 @@ export async function cosmwasmTransferSimulate({ return ok(response.toString()) } +/** + * transfer an asset from cosmos + * @example + * ```ts + * const transfer = await cosmosSameChainTransfer(client, { + * asset: { denom: "muno", amount: "1" }, + * gasPrice: { amount: "0.0025", denom: "muno" }, + * recipient: "0x8478B37E983F520dBCB5d7D3aAD8276B82631aBd", + * account: cosmosAccount, + * rpcUrl: "https://rpc.testnet-8.union.build", + * }) + * ``` + */ export async function cosmosSameChainTransfer({ asset, gasPrice, @@ -227,7 +371,11 @@ export async function cosmosSameChainTransfer({ if (accountResult.isErr()) return err(accountResult.error) const _account = accountResult.value - const signingClient = await connectStargateWithSigner({ rpcUrl, account, gasPrice }) + const signingClient = await connectStargateWithSigner({ + rpcUrl, + account, + gasPrice + }) if (signingClient.isErr()) return err(signingClient.error) const _signingClient = signingClient.value @@ -251,6 +399,19 @@ export async function cosmosSameChainTransfer({ return ok(response.transactionHash) } +/** + * simulate a transfer asset from cosmos + * @example + * ```ts + * const transfer = await cosmosSameChainTransferSimulate(client, { + * asset: { denom: "muno", amount: "1" }, + * gasPrice: { amount: "0.0025", denom: "muno" }, + * recipient: "0x8478B37E983F520dBCB5d7D3aAD8276B82631aBd", + * account: cosmosAccount, + * rpcUrl: "https://rpc.testnet-8.union.build", + * }) + * ``` + */ export async function cosmosSameChainTransferSimulate({ asset, gasPrice, @@ -272,7 +433,11 @@ export async function cosmosSameChainTransferSimulate({ if (accountResult.isErr()) return err(accountResult.error) const _account = accountResult.value - const signingClient = await connectStargateWithSigner({ rpcUrl, account, gasPrice }) + const signingClient = await connectStargateWithSigner({ + rpcUrl, + account, + gasPrice + }) if (signingClient.isErr()) return err(signingClient.error) const _signingClient = signingClient.value diff --git a/typescript-sdk/src/transfer/evm.ts b/typescript-sdk/src/transfer/evm.ts index 07d20e5d03..cf2395407f 100644 --- a/typescript-sdk/src/transfer/evm.ts +++ b/typescript-sdk/src/transfer/evm.ts @@ -27,7 +27,20 @@ export type TransferAssetFromEvmParams = { } /** - * TODO: add JSDoc with examples + * transfer an asset from evm + * @example + * ```ts + * const transfer = await transferAssetFromEvm(client, { + * memo: "test", + * amount: 1n, + * account: evmAccount, + * sourceChannel: "channel-1", + * recipient: "0x8478B37E983F520dBCB5d7D3aAD8276B82631aBd", + * denomAddress: "0x779877A7B0D9E8603169DdbD7836e478b4624789", + * relayContractAddress: "0x2222222222222222222222222222222222222222", + * destinationChainId: "stride-internal-1", + * }) + * ``` */ export async function transferAssetFromEvm( client: WalletClient & PublicActions, @@ -38,8 +51,8 @@ export async function transferAssetFromEvm( recipient, denomAddress, sourceChannel, - autoApprove = false, simulate = true, + autoApprove = false, relayContractAddress }: TransferAssetFromEvmParams ): Promise> { @@ -109,7 +122,20 @@ export type ApproveTransferAssetFromEvmParams = Pick< > /** - * TODO: add JSDoc with examples + * approve a transfer asset from evm + * @example + * ```ts + * const transfer = await approveTransferAssetFromEvm(client, { + * memo: "test", + * amount: 1n, + * account: evmAccount, + * sourceChannel: "channel-1", + * recipient: "0x8478B37E983F520dBCB5d7D3aAD8276B82631aBd", + * denomAddress: "0x779877A7B0D9E8603169DdbD7836e478b4624789", + * relayContractAddress: "0x2222222222222222222222222222222222222222", + * destinationChainId: "stride-internal-1", + * }) + * ``` */ export async function approveTransferAssetFromEvm( client: WalletClient & PublicActions, @@ -154,7 +180,20 @@ export async function approveTransferAssetFromEvm( } /** - * TODO: add JSDoc with examples + * simulate a transfer asset from evm + * @example + * ```ts + * const transfer = await transferAssetFromEvmSimulate(client, { + * memo: "test", + * amount: 1n, + * account: evmAccount, + * sourceChannel: "channel-1", + * recipient: "0x8478B37E983F520dBCB5d7D3aAD8276B82631aBd", + * denomAddress: "0x779877A7B0D9E8603169DdbD7836e478b4624789", + * relayContractAddress: "0x2222222222222222222222222222222222222222", + * destinationChainId: "stride-internal-1", + * }) + * ``` */ export async function transferAssetFromEvmSimulate( client: WalletClient & PublicActions, diff --git a/typescript-sdk/src/transport.ts b/typescript-sdk/src/transport.ts deleted file mode 100644 index e837b4ff14..0000000000 --- a/typescript-sdk/src/transport.ts +++ /dev/null @@ -1,175 +0,0 @@ -import { - RpcRequestError, - createTransport, - UrlRequiredError, - type ClientConfig, - type TransportConfig, - type HttpTransportConfig -} from "viem" -import { sleep } from "./utilities/index.ts" -import type { RpcRequest } from "./types.ts" -import { getHttpRpcClient } from "viem/utils" -import { createBatchScheduler } from "./utilities/promise/batch-scheduler.ts" - -export type Transport> = ({ - pollingInterval, - retryCount, - timeout -}: { - pollingInterval?: ClientConfig["pollingInterval"] | undefined - retryCount?: TransportConfig["retryCount"] | undefined - timeout?: TransportConfig["timeout"] | undefined -}) => { - config: TransportConfig - value?: TRpcAttributes | undefined -} - -export type CosmosHttpTransport = Transport< - "http", - { - fetchOptions?: HttpTransportConfig["fetchOptions"] | undefined - url?: string | undefined - } -> - -/** - * @description Creates a HTTP transport that connects to a JSON-RPC API. - * @deprecated use `http` from `viem` instead - */ -export function cosmosHttp( - /** URL of the JSON-RPC API. Defaults to the chain's public RPC URL. */ - url?: string | undefined, - config: HttpTransportConfig = {} -): CosmosHttpTransport { - const { - batch, - fetchOptions, - key = "http", - name = "HTTP JSON-RPC", - onFetchRequest, - onFetchResponse, - retryDelay - } = config - return ({ retryCount: retryCount_, timeout: timeout_ }) => { - const { batchSize = 1000, wait = 0 } = typeof batch === "object" ? batch : {} - const retryCount = config.retryCount ?? retryCount_ - const timeout = timeout_ ?? config.timeout ?? 10_000 - const url_ = url // || chain?.rpcUrls.default.http[0] - if (!url_) throw new UrlRequiredError() - - const rpcClient = getHttpRpcClient(url_, { - fetchOptions, - onRequest: onFetchRequest, - onResponse: onFetchResponse, - timeout - }) - - return createTransport( - { - key, - name, - async request({ method, params }) { - const body = { method, params } - - const { schedule } = createBatchScheduler({ - id: url_, - wait, - shouldSplitBatch(requests) { - return requests.length > batchSize - }, - fn: (body: Array) => - rpcClient.request({ - body - }), - sort: (a, b) => a.id - b.id - }) - - const fn = async (body: RpcRequest) => - batch - ? schedule(body) - : [ - await rpcClient.request({ - body - }) - ] - - const [{ error, result }] = await fn(body) - if (error) - throw new RpcRequestError({ - body, - error, - url: url_ - }) - return result - }, - retryCount, - retryDelay, - timeout, - type: "http" - }, - { - fetchOptions, - url: url_ - } - ) - } -} - -/** - * Given an array of rpc URLs, check the latency of each and return them ranked by latency - */ -export function rankCosmosRpcProviders({ - interval = 1_000, - sampleCount = 10, - timeout = 1_000, - transports, - weights = {} -}: { - interval: number - sampleCount: number - timeout: number - transports?: Array - weights?: { latency?: number; stability?: number } -}) { - const { latency = 1, stability = 1 } = weights - return { - rank: async (rpcUrls?: Array) => { - const _transports = rpcUrls || transports - if (!_transports) throw new Error("No transports provided") - const results = await Promise.all( - _transports.map(async rpcUrl => { - const latencies = await Promise.all( - Array.from({ length: sampleCount }, async () => { - const start = Date.now() - try { - const controller = new AbortController() - const timeoutSignal = AbortSignal.timeout(timeout) - await fetch(rpcUrl, { - method: "head", - signal: AbortSignal.any([controller.signal, timeoutSignal]) - }) - - return Date.now() - start - } catch (error) { - return Number.POSITIVE_INFINITY - } finally { - await sleep(interval) - } - }) - ) - const validLatencies = latencies.filter(latency => latency !== Number.POSITIVE_INFINITY) - const stability = validLatencies.length - const averageLatency = validLatencies.reduce((a, b) => a + b, 0) / stability - return { rpcUrl, latency: averageLatency, stability } - }) - ) - return results - .sort((a, b) => { - const aScore = a.latency * latency + a.stability * stability - const bScore = b.latency * latency + b.stability * stability - return aScore - bScore - }) - .map(({ rpcUrl, latency }) => ({ rpcUrl, latency })) - } - } -} diff --git a/typescript-sdk/src/utilities/address.ts b/typescript-sdk/src/utilities/address.ts index 34f35e3ba9..67c1119819 100644 --- a/typescript-sdk/src/utilities/address.ts +++ b/typescript-sdk/src/utilities/address.ts @@ -1,19 +1,47 @@ import { bech32 } from "@scure/base" import type { HexAddress, Bech32Address } from "../types.ts" +/** + * check if a string is a valid cosmos transaction hash + * @example + * ```ts + * isValidCosmosTxHash("A6E276CE66CDB35C0CAAC49EC9AAB3CB2CF8A34C807A4C729EA385E64C88D69B") + * ``` + */ export function isValidCosmosTxHash(hash: unknown): hash is string { if (typeof hash !== "string") return false return typeof hash === "string" && /^[A-Fa-f0-9]{64}$/.test(hash) } +/** + * check if a string is a valid evm transaction hash + * @example + * ```ts + * isValidEvmTxHash("0xA6E276CE66CDB35C0CAAC49EC9AAB3CB2CF8A34C807A4C729EA385E64C88D69B") + * ``` + */ export function isValidEvmTxHash(hash: unknown): hash is string { if (typeof hash !== "string" || hash.indexOf("0x") !== 0) return false return typeof hash === "string" && /^0x([A-Fa-f0-9]{64})$/.test(hash) } +/** + * check if a string is a valid evm address + * @example + * ```ts + * isValidEvmAddress("0xA6E276CE66CDB35C0CAAC49EC9AAB3CB2CF8A34C") + * ``` + */ export const isValidEvmAddress = (address: unknown): address is HexAddress => typeof address === "string" && /^0x[a-fA-F0-9]{40}$/.test(address) +/** + * check if a string is a valid bech32 address + * @example + * ```ts + * isValidBech32Address("union1qp0wtsfltjk9rnvyu3fkdv0s0skp4y5y3py96f") + * ``` + */ export function isValidBech32Address(address: unknown): address is Bech32Address { if (typeof address !== "string") return false @@ -28,10 +56,20 @@ export function isValidBech32Address(address: unknown): address is Bech32Address } } +/** + * truncate an address to a given length + * @example + * ```ts + * truncateAddress({ + * length: 6, + * address: "union1qp0wtsfltjk9rnvyu3fkdv0s0skp4y5y3py96f", + * }) + * ``` + */ export const truncateAddress = ({ address, length = 6 }: { address: string length?: number -}) => (length > 0 ? `${address.slice(0, length)}...${address.slice(-length)}` : address) +}): string => (length > 0 ? `${address.slice(0, length)}...${address.slice(-length)}` : address) diff --git a/typescript-sdk/src/utilities/fetch-progress.ts b/typescript-sdk/src/utilities/fetch-progress.ts deleted file mode 100644 index d4bb1dfbb1..0000000000 --- a/typescript-sdk/src/utilities/fetch-progress.ts +++ /dev/null @@ -1,40 +0,0 @@ -import type { MaybePromise } from "../types.ts" - -/** - * WIP - */ -export async function onResponse({ - response, - onProgress -}: { - response: Response - onProgress: (progress: number) => MaybePromise -}) { - console.info(`Response status: ${response.status}`) - - const reader = response.body?.getReader() - if (!reader) return - - // Ensure the header name matches exactly as it was set - const contentLengthHeader = response.headers.get("content-length") - const length = contentLengthHeader ? +Number.parseInt(contentLengthHeader) : undefined - - let receivedLength = 0 - let chunks: Array = [] - - while (true) { - console.info("Reading...") - - const read = await reader.read() - if (read.done || !read.value) break - - receivedLength += read.value.length - chunks.push(read.value) - - // Calculate progress as a percentage if length is defined - const progress = length ? (receivedLength / length) * 100 : receivedLength - await onProgress(progress) - - console.info(`Received ${receivedLength} of ${length} bytes`) - } -} diff --git a/typescript-sdk/src/utilities/index.ts b/typescript-sdk/src/utilities/index.ts index ed2f0550a5..4c50340371 100644 --- a/typescript-sdk/src/utilities/index.ts +++ b/typescript-sdk/src/utilities/index.ts @@ -1,7 +1,7 @@ export const sleep = async (ms: number): Promise => new Promise(resolve => setTimeout(resolve, ms)) -export function timestamp() { +export function timestamp(): string { const d = new Date() const [date] = d.toISOString().split("T") const [time] = d.toTimeString().split(" ") diff --git a/typescript-sdk/src/utilities/promise/batch-scheduler.ts b/typescript-sdk/src/utilities/promise/batch-scheduler.ts deleted file mode 100644 index ce208ab334..0000000000 --- a/typescript-sdk/src/utilities/promise/batch-scheduler.ts +++ /dev/null @@ -1,114 +0,0 @@ -/** - * @source https://github.com/wevm/viem/blob/main/src/utils/promise/createBatchScheduler.ts - */ -import type { ErrorType } from "../../types.ts" - -type Resolved = any> = [ - result: TReturnType[number], - results: TReturnType -] - -type PendingPromise = any> = { - resolve?: ((data: Resolved) => void) | undefined - reject?: ((reason?: unknown) => void) | undefined -} - -type SchedulerItem = { args: unknown; pendingPromise: PendingPromise } - -type BatchResultsCompareFn = (a: TResult, b: TResult) => number - -type CreateBatchSchedulerArguments< - TParameters = unknown, - TReturnType extends ReadonlyArray = ReadonlyArray -> = { - fn: (args: Array) => Promise - id: number | string - shouldSplitBatch?: ((args: Array) => boolean) | undefined - wait?: number | undefined - sort?: BatchResultsCompareFn | undefined -} - -type CreateBatchSchedulerReturnType< - TParameters = unknown, - TReturnType extends ReadonlyArray = ReadonlyArray -> = { - flush: () => void - schedule: TParameters extends undefined - ? (args?: TParameters | undefined) => Promise> - : (args: TParameters) => Promise> -} - -export type CreateBatchSchedulerErrorType = ErrorType - -const schedulerCache = /*#__PURE__*/ new Map>() - -/** @internal */ -export function createBatchScheduler>({ - fn, - id, - shouldSplitBatch, - wait = 0, - sort -}: CreateBatchSchedulerArguments): CreateBatchSchedulerReturnType< - TParameters, - TReturnType -> { - const exec = async () => { - const scheduler = getScheduler() - flush() - - const args = scheduler.map(({ args }) => args) - - if (args.length === 0) return - - fn(args as Array) - .then(data => { - if (sort && Array.isArray(data)) data.sort(sort) - for (let index = 0; index < scheduler.length; index++) { - // @ts-expect-error - const { pendingPromise } = scheduler[index] - pendingPromise.resolve?.([data[index], data]) - } - }) - .catch(error => { - for (let index = 0; index < scheduler.length; index++) { - // @ts-expect-error - const { pendingPromise } = scheduler[index] - pendingPromise.reject?.(error) - } - }) - } - - const flush = () => schedulerCache.delete(id) - - const getBatchedArgs = () => getScheduler().map(({ args }) => args) as Array - - const getScheduler = () => schedulerCache.get(id) || [] - - const setScheduler = (item: SchedulerItem) => schedulerCache.set(id, [...getScheduler(), item]) - - return { - flush, - async schedule(args: TParameters) { - const pendingPromise: PendingPromise = {} - const promise = new Promise>((resolve, reject) => { - pendingPromise.resolve = resolve - pendingPromise.reject = reject - }) - - const split = shouldSplitBatch?.([...getBatchedArgs(), args]) - - if (split) exec() - - const hasActiveScheduler = getScheduler().length > 0 - if (hasActiveScheduler) { - setScheduler({ args, pendingPromise }) - return promise - } - - setScheduler({ args, pendingPromise }) - setTimeout(exec, wait) - return promise - } - } as unknown as CreateBatchSchedulerReturnType -} diff --git a/typescript-sdk/src/utilities/promise/with-retry.ts b/typescript-sdk/src/utilities/promise/with-retry.ts deleted file mode 100644 index 609b677337..0000000000 --- a/typescript-sdk/src/utilities/promise/with-retry.ts +++ /dev/null @@ -1,50 +0,0 @@ -/** - * source: https://github.com/wevm/viem/blob/main/src/utils/promise/withRetry.ts - */ - -import { sleep } from "../index.ts" -import type { ErrorType } from "../../types.ts" - -export type WithRetryParameters = { - // The delay (in ms) between retries. - delay?: ((config: { count: number; error: Error }) => number) | number | undefined - // The max number of times to retry. - retryCount?: number | undefined - // Whether or not to retry when an error is thrown. - shouldRetry?: - | (({ - count, - error - }: { - count: number - error: Error - }) => Promise | boolean) - | undefined -} - -export type WithRetryErrorType = ErrorType - -export function withRetry( - fn: () => Promise, - { delay: delay_ = 100, retryCount = 2, shouldRetry = () => true }: WithRetryParameters = {} -) { - return new Promise((resolve, reject) => { - const attemptRetry = async ({ count = 0 } = {}) => { - const retry = async ({ error }: { error: Error }) => { - const delay = typeof delay_ === "function" ? delay_({ count, error }) : delay_ - if (delay) await sleep(delay) - attemptRetry({ count: count + 1 }) - } - - try { - const data = await fn() - resolve(data) - } catch (err) { - if (count < retryCount && (await shouldRetry({ count, error: err as Error }))) - return retry({ error: err as Error }) - reject(err) - } - } - attemptRetry() - }) -}