From ec68c8c2a3d09f4b442f58f72a2c156eb03e32a2 Mon Sep 17 00:00:00 2001 From: o-az Date: Mon, 2 Sep 2024 15:07:08 -0700 Subject: [PATCH 01/12] 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() - }) -} From 3c2ea7595092e49dd60ca1df396cab162d229356 Mon Sep 17 00:00:00 2001 From: o-az Date: Mon, 2 Sep 2024 16:52:09 -0700 Subject: [PATCH 02/12] feat: move transfer form to ts sdk --- app/app.nix | 2 +- app/package-lock.json | 29 ++- app/package.json | 4 +- app/src/hooks.client.ts | 22 -- app/src/lib/components/loading-bar.svelte | 4 +- app/src/lib/wallet/cosmos/config.ts | 31 ++- .../(components)/transfer-form.svelte | 229 +++++++++++------- app/src/routes/transfer/state-machine.ts | 1 + app/vite.config.ts | 8 - typescript-sdk/jsr.json | 2 +- typescript-sdk/package.json | 2 +- typescript-sdk/src/client/evm.ts | 16 +- typescript-sdk/src/mod.ts | 17 +- typescript-sdk/src/pfm.ts | 3 +- typescript-sdk/src/transfer/evm.ts | 5 +- typescript-sdk/src/utilities/address.ts | 15 ++ 16 files changed, 224 insertions(+), 166 deletions(-) create mode 100644 app/src/routes/transfer/state-machine.ts diff --git a/app/app.nix b/app/app.nix index 915b7ecb5d..4795baab27 100644 --- a/app/app.nix +++ b/app/app.nix @@ -9,7 +9,7 @@ { packages = { app = unstablePkgs.buildNpmPackage { - npmDepsHash = "sha256-VRPczr/PHVSjiJpTmVHVQxXGkmPhqn5bEQ8qVarYcz8="; + npmDepsHash = "sha256-dsbzL/WaaSxnQEX5f9o7/RMOrwrvy7a1X+5eVHxbUWE="; src = ./.; sourceRoot = "app"; npmFlags = [ "--legacy-peer-deps" ]; diff --git a/app/package-lock.json b/app/package-lock.json index fa42683d45..9994847574 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -19,7 +19,7 @@ "@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.4", + "@union/client": "npm:@jsr/union__client@^0.0.7", "@wagmi/connectors": "^5.1.8", "@wagmi/core": "^2.13.4", "bech32": "^2.0.0", @@ -59,7 +59,7 @@ "@tailwindcss/container-queries": "^0.1.1", "@tailwindcss/forms": "^0.5.8", "@tailwindcss/typography": "^0.5.15", - "@tanstack/svelte-query-devtools": "^5.53.3", + "@tanstack/svelte-query-devtools": "^5.54.0", "@total-typescript/ts-reset": "^0.6.1", "@types/node": "^22.5.2", "@types/postcss-import": "^14.0.3", @@ -3865,6 +3865,7 @@ "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-2.1.0.tgz", "integrity": "sha512-9QX28IymvBlSCqsCll5t0kQVxipsfhFFL+L2t3nTWfXnddYwxBuAEtTtlaVQpRz9c37BhJjltSeY4AJSC03SSg==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^4.3.4" }, @@ -3958,9 +3959,9 @@ } }, "node_modules/@tanstack/query-devtools": { - "version": "5.52.3", - "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.52.3.tgz", - "integrity": "sha512-oGX9qJuNpr4vOQyeksqHr+FgLQGs5UooK87R1wTtcsUUdrRKGSgs3cBllZMtWBJxg+yVvg0TlHNGYLMjvqX3GA==", + "version": "5.54.0", + "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.54.0.tgz", + "integrity": "sha512-B8Sa6mh7/4m2fyk2/YnUXeOZ1/us7G/C/i1It8YcCbieXc8vf1AdSYjR+mZIoJeKOKLqA741hZqfj8d4F1NCVg==", "dev": true, "license": "MIT", "funding": { @@ -4012,13 +4013,13 @@ } }, "node_modules/@tanstack/svelte-query-devtools": { - "version": "5.53.3", - "resolved": "https://registry.npmjs.org/@tanstack/svelte-query-devtools/-/svelte-query-devtools-5.53.3.tgz", - "integrity": "sha512-nkFuuL6afh6nY5UpqznNLcNhywnIzNoazvMbxFVavOkTKTTEcuNtWOf/G3iZRe9RTzg8QndST5R4kLOdQe5QpA==", + "version": "5.54.0", + "resolved": "https://registry.npmjs.org/@tanstack/svelte-query-devtools/-/svelte-query-devtools-5.54.0.tgz", + "integrity": "sha512-k3ckTCbUSyW0fD/4JaSmKcoJsDZ9uh7MoRuKQu1KEL8nVQf3GXedRkMswdUHcoCCr4+xMIesPb8iI1jB8ARM3A==", "dev": true, "license": "MIT", "dependencies": { - "@tanstack/query-devtools": "5.52.3", + "@tanstack/query-devtools": "5.54.0", "esm-env": "^1.0.0" }, "funding": { @@ -4270,9 +4271,9 @@ }, "node_modules/@union/client": { "name": "@jsr/union__client", - "version": "0.0.4", - "resolved": "https://npm.jsr.io/~/11/@jsr/union__client/0.0.4.tgz", - "integrity": "sha512-X5RgvxTbONfmRvZonGMDbgBvI6JmRFNEH/N9MEA3+N/8ZSK51dB76i1hJ4Hs8SwhfaGJRlOcR32JOLI7vLwKJg==", + "version": "0.0.7", + "resolved": "https://npm.jsr.io/~/11/@jsr/union__client/0.0.7.tgz", + "integrity": "sha512-PxkuD60Fy0HaGRaZlpifdqTHuAf3toSJ1L81VYqduAqnpFvz2ZBXVOhZFDX+kS+F4dwVGoRcIHbv7qvA9LAJBg==", "dependencies": { "@cosmjs/cosmwasm-stargate": "0.32.4", "@cosmjs/stargate": "0.32.4", @@ -5507,6 +5508,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz", "integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==", + "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15", "@types/estree": "^1.0.1", @@ -7119,6 +7121,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", + "license": "MIT", "dependencies": { "@types/estree": "*" } @@ -8417,6 +8420,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", + "license": "MIT", "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^3.0.0", @@ -10223,6 +10227,7 @@ "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.16.0.tgz", "integrity": "sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA==", "dev": true, + "license": "ISC", "engines": { "node": "^12.20 || ^14.13.1 || >= 16" }, diff --git a/app/package.json b/app/package.json index c53f519b89..2238ae40b3 100644 --- a/app/package.json +++ b/app/package.json @@ -25,7 +25,7 @@ "@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.4", + "@union/client": "npm:@jsr/union__client@^0.0.7", "@wagmi/connectors": "^5.1.8", "@wagmi/core": "^2.13.4", "bech32": "^2.0.0", @@ -65,7 +65,7 @@ "@tailwindcss/container-queries": "^0.1.1", "@tailwindcss/forms": "^0.5.8", "@tailwindcss/typography": "^0.5.15", - "@tanstack/svelte-query-devtools": "^5.53.3", + "@tanstack/svelte-query-devtools": "^5.54.0", "@total-typescript/ts-reset": "^0.6.1", "@types/node": "^22.5.2", "@types/postcss-import": "^14.0.3", diff --git a/app/src/hooks.client.ts b/app/src/hooks.client.ts index 0b0b3f3ed7..90c2712cd4 100644 --- a/app/src/hooks.client.ts +++ b/app/src/hooks.client.ts @@ -1,30 +1,8 @@ -import * as Sentry from "@sentry/sveltekit" import type { HandleClientError } from "@sveltejs/kit" -if (import.meta.env.MODE === "production") { - Sentry.init({ - enabled: import.meta.env.MODE === "production", - dsn: "https://b410cea864cbfaefea5fc8b18e40ae4f@o4506911891783680.ingest.us.sentry.io/4507500708954112", - tracesSampleRate: 1, - replaysOnErrorSampleRate: 1, - replaysSessionSampleRate: 0.1, - integrations: [ - Sentry.replayIntegration(), - Sentry.breadcrumbsIntegration(), - Sentry.extraErrorDataIntegration() - ] - }) -} - // biome-ignore lint/suspicious/useAwait: no need export const handleError = (async ({ error, event, status, message, ...context }) => { const errorId = crypto.randomUUID() - if (import.meta.env.MODE === "production") { - Sentry.captureException(error, { - extra: { event, errorId, status, message } - }) - } - return { errorId, message: `${message} - ${error}` } }) satisfies HandleClientError diff --git a/app/src/lib/components/loading-bar.svelte b/app/src/lib/components/loading-bar.svelte index 3d2f9113ed..5f509b2aba 100644 --- a/app/src/lib/components/loading-bar.svelte +++ b/app/src/lib/components/loading-bar.svelte @@ -28,5 +28,5 @@ $: $navigationState = $navigating ? "loading" : "loaded"
-
-
\ No newline at end of file +
+
diff --git a/app/src/lib/wallet/cosmos/config.ts b/app/src/lib/wallet/cosmos/config.ts index 5633cbda26..3fcff5a890 100644 --- a/app/src/lib/wallet/cosmos/config.ts +++ b/app/src/lib/wallet/cosmos/config.ts @@ -1,11 +1,12 @@ -import { derived, get, type Readable } from "svelte/store" +import type { Address } from "viem" import { sleep } from "$lib/utilities/index.ts" import { persisted } from "svelte-persisted-store" +import type { UserAddressCosmos } from "$lib/types" +import type { OfflineSigner } from "@leapwallet/types" import type { ChainWalletStore } from "$lib/wallet/types" +import { derived, get, type Readable } from "svelte/store" +import { bytesToBech32Address, extractBech32AddressPrefix } from "@union/client" import { unionKeplrChainInfo, unionLeapChainInfo } from "$lib/wallet/cosmos/chain-info.ts" -import type { UserAddressCosmos } from "$lib/types" -import { rawToHex } from "$lib/utilities/address" -import type { Address } from "viem" export const cosmosWalletsInformation = [ { @@ -38,7 +39,10 @@ export const cosmosWalletsInformation = [ export type CosmosWalletId = (typeof cosmosWalletsInformation)[number]["id"] function createCosmosStore( - previousState: ChainWalletStore<"cosmos"> = { + previousState: ChainWalletStore<"cosmos"> & { + rawAddress: Uint8Array | undefined + connectedWallet: CosmosWalletId | "none" + } = { chain: "cosmos", hoverState: "none", address: undefined, @@ -49,7 +53,7 @@ function createCosmosStore( ) { const { subscribe, set, update } = persisted("cosmos-store", previousState, { syncTabs: true, - storage: "session" + storage: "local" }) return { set, @@ -115,11 +119,24 @@ function createCosmosStore( export const cosmosStore = createCosmosStore() +export const getCosmosOfflineSigner = (chainId: string): OfflineSigner => + get(cosmosStore).connectedWallet === "keplr" + ? window.keplr?.getOfflineSigner(chainId, { disableBalanceCheck: false }) + : window.leap?.getOfflineSigner(chainId, { disableBalanceCheck: false }) + export const userAddrCosmos: Readable = derived( [cosmosStore], ([$cosmosStore]) => { + console.info("[cosmos] userAddrCosmos", $cosmosStore) if ($cosmosStore?.rawAddress && $cosmosStore?.address) { - const cosmos_normalized = rawToHex($cosmosStore.rawAddress) + const bech32Prefix = extractBech32AddressPrefix($cosmosStore.address) + if (!bech32Prefix) return null + console.info("[cosmos] bech32Prefix", bech32Prefix) + if (!($cosmosStore.rawAddress instanceof Uint8Array)) return null + const cosmos_normalized = bytesToBech32Address({ + toPrefix: bech32Prefix, + bytes: $cosmosStore.rawAddress + }) return { canonical: $cosmosStore.address, normalized: cosmos_normalized, diff --git a/app/src/routes/transfer/(components)/transfer-form.svelte b/app/src/routes/transfer/(components)/transfer-form.svelte index d965c754d0..f82c3bab06 100644 --- a/app/src/routes/transfer/(components)/transfer-form.svelte +++ b/app/src/routes/transfer/(components)/transfer-form.svelte @@ -2,7 +2,7 @@ import { onMount } from "svelte" import { toast } from "svelte-sonner" import Chevron from "./chevron.svelte" -import { UnionClient } from "@union/client/v0" +import { createUnionClient, type CosmosChainId, type EvmChainId } from "@union/client" import { cn } from "$lib/utilities/shadcn.ts" import { raise, sleep } from "$lib/utilities/index.ts" import type { OfflineSigner } from "@leapwallet/types" @@ -19,14 +19,21 @@ import { rawToBech32, userAddrOnChain } from "$lib/utilities/address.ts" import { userBalancesQuery } from "$lib/queries/balance" import { page } from "$app/stores" import { goto } from "$app/navigation" -import { ucs01abi } from "$lib/abi/ucs-01.ts" -import { type Address, parseUnits, toHex, formatUnits, type Chain as ViemChain } from "viem" +import { + type Address, + parseUnits, + toHex, + formatUnits, + type Chain as ViemChain, + http, + custom, + type HttpTransport +} from "viem" import Stepper from "$lib/components/stepper.svelte" import { type TransferState, stepBefore, stepAfter } from "$lib/transfer/transfer.ts" import type { Chain, UserAddresses } from "$lib/types.ts" import CardSectionHeading from "./card-section-heading.svelte" import ArrowLeftRight from "virtual:icons/lucide/arrow-left-right" -import { erc20Abi } from "viem" import { getSupportedAsset } from "$lib/utilities/helpers.ts" import { submittedTransfers } from "$lib/stores/submitted-transfers.ts" import { toIsoString } from "$lib/utilities/date" @@ -34,13 +41,7 @@ import { config } from "$lib/wallet/evm/config" import { userAddrEvm } from "$lib/wallet/evm" import { userAddrCosmos } from "$lib/wallet/cosmos" import { getCosmosChainInfo } from "$lib/wallet/cosmos/chain-info.ts" -import { - writeContract, - simulateContract, - waitForTransactionReceipt, - getConnectorClient, - switchChain -} from "@wagmi/core" +import { waitForTransactionReceipt, getConnectorClient, switchChain } from "@wagmi/core" import { sepolia, berachainTestnetbArtio, arbitrumSepolia, scrollSepolia } from "viem/chains" function getChainById(chainId: number): ViemChain | null { @@ -62,6 +63,9 @@ let userAddr: Readable = derived( }) ) +$: { + // console.info("userAddr", JSON.stringify($userAddr, undefined, 2)) +} $: userBalances = userBalancesQuery({ chains, userAddr: $userAddr, connected: true }) // CURRENT FORM STATE @@ -176,6 +180,7 @@ let ucs01Configuration = derived( pfmMemo = generatePfmMemo( forwardConfig.channel_id, forwardConfig.port_id, + // @ts-expect-error $toChain?.rpc_type === "evm" ? $recipient.slice(2) : $recipient ) break @@ -317,57 +322,77 @@ const transfer = async () => { }) : undefined ) as OfflineSigner - let cosmosClient = new UnionClient({ - cosmosOfflineSigner, - evmSigner: undefined, - bech32Prefix: $fromChain.addr_prefix, - chainId: $fromChain.chain_id, - gas: { denom: $assetSymbol, amount: "0.0025" }, - rpcUrl: `https://${rpcUrl}` + // let cosmosClient = new UnionClient({ + // cosmosOfflineSigner, + // evmSigner: undefined, + // bech32Prefix: $fromChain.addr_prefix, + // chainId: $fromChain.chain_id, + // gas: { denom: $assetSymbol, amount: "0.0025" }, + // rpcUrl: `https://${rpcUrl}` + // }) + const unionClient = createUnionClient({ + account: cosmosOfflineSigner, + transport: http(`https://${rpcUrl}`), + chainId: $fromChain.chain_id as CosmosChainId, + gasPrice: { amount: "0.0025", denom: $assetSymbol } }) - let transferAssetsMessage: Parameters[0] - console.log({ ucs1_configuration }) - if (ucs1_configuration.contract_address === "ics20") { - console.log({ $recipient }) - transferAssetsMessage = { - kind: "ibc", - messageTransfers: [ - { - sourcePort: "transfer", - sourceChannel: ucs1_configuration.channel_id, - token: { denom: $assetAddress, amount: parsedAmount.toString() }, - sender: rawToBech32($fromChain.addr_prefix, $userAddrCosmos.bytes), - receiver: $recipient, - memo: pfmMemo ?? "", - timeoutHeight: { revisionHeight: 888888888n, revisionNumber: 8n } - } - ] - } - } else { - console.log("THIS SHOULD NOT HAPPEN") - transferAssetsMessage = { - kind: "cosmwasm", - instructions: [ - { - contractAddress: ucs1_configuration.contract_address, - msg: { - transfer: { - channel: ucs1_configuration.channel_id, - receiver: $toChain.rpc_type === "evm" ? $recipient?.slice(2) : $recipient, - memo: pfmMemo ?? "" - } - }, - funds: [{ denom: $assetAddress, amount: parsedAmount.toString() }] - } - ] - } - } - - console.log({ transferAssetsMessage }) + console.info({ + account: cosmosOfflineSigner, + chainId: $fromChain.chain_id as CosmosChainId, + gasPrice: { amount: "0.0025", denom: $assetSymbol } + }) - const cosmosTransfer = await cosmosClient.transferAssets(transferAssetsMessage) - transferState.set({ kind: "TRANSFERRING", transferHash: cosmosTransfer.transactionHash }) + // let transferAssetsMessage: Parameters[0] + + // if (ucs1_configuration.contract_address === "ics20") { + // console.log({ $recipient }) + // transferAssetsMessage = { + // kind: "ibc", + // messageTransfers: [ + // { + // sourcePort: "transfer", + // sourceChannel: ucs1_configuration.channel_id, + // token: { denom: $assetAddress, amount: parsedAmount.toString() }, + // sender: rawToBech32($fromChain.addr_prefix, $userAddrCosmos.bytes), + // receiver: $recipient, + // memo: pfmMemo ?? "", + // timeoutHeight: { revisionHeight: 888888888n, revisionNumber: 8n } + // } + // ] + // } + // } else { + // console.log("THIS SHOULD NOT HAPPEN") + // transferAssetsMessage = { + // kind: "cosmwasm", + // instructions: [ + // { + // contractAddress: ucs1_configuration.contract_address, + // msg: { + // transfer: { + // channel: ucs1_configuration.channel_id, + // receiver: $toChain.rpc_type === "evm" ? $recipient?.slice(2) : $recipient, + // memo: pfmMemo ?? "" + // } + // }, + // funds: [{ denom: $assetAddress, amount: parsedAmount.toString() }] + // } + // ] + // } + // } + + // console.log({ transferAssetsMessage }) + + // const cosmosTransfer = await cosmosClient.transferAssets(transferAssetsMessage) + const transfer = await unionClient.transferAsset({ + autoApprove: true, + amount: parsedAmount, + recipient: $recipient, + denomAddress: $assetAddress, + destinationChainId: $toChain.chain_id + }) + if (transfer.isErr()) throw transfer.error + transferState.set({ kind: "TRANSFERRING", transferHash: transfer.value }) } catch (error) { if (error instanceof Error) { // @ts-ignore @@ -380,6 +405,12 @@ const transfer = async () => { const connectorClient = await getConnectorClient(config) const selectedChain = getChainById(Number($fromChainId)) + const unionClient = createUnionClient({ + account: connectorClient.account, + chainId: $fromChain.chain_id as EvmChainId, + transport: custom(window.ethereum) as unknown as HttpTransport + }) + if (!selectedChain) { toast.error("From chain not found or supported") return @@ -411,6 +442,7 @@ const transfer = async () => { // ^ the user is continuing continuing after having seen the warning try { + // @ts-expect-error await switchChain(config, { chainId: selectedChain.id }) } catch (error) { if (error instanceof Error) { @@ -425,14 +457,24 @@ const transfer = async () => { let hash: `0x${string}` | null = null try { - hash = await writeContract(config, { - chain: selectedChain, - account: $userAddrEvm.canonical, - abi: erc20Abi, - address: $asset.address as Address, - functionName: "approve", - args: [ucs01address, parsedAmount] + // hash = await writeContract(config, { + // chain: selectedChain, + // account: $userAddrEvm.canonical, + // abi: erc20Abi, + // address: $asset.address as Address, + // functionName: "approve", + // args: [ucs01address, parsedAmount] + // }) + const approve = await unionClient.approveTransaction({ + amount: parsedAmount, + recipient: $recipient, + denomAddress: $assetAddress, + destinationChainId: $toChain.chain_id }) + + if (approve.isErr()) throw approve.error + // @ts-expect-error + hash = approve.value } catch (error) { if (error instanceof Error) { transferState.set({ kind: "APPROVING_ASSET", error }) @@ -440,6 +482,7 @@ const transfer = async () => { return } + // @ts-expect-error transferState.set({ kind: "AWAITING_APPROVAL_RECEIPT", hash }) } @@ -465,25 +508,25 @@ const transfer = async () => { if (pfmMemo === null && $userAddrCosmos === null) return toast.error("Destination is a Cosmos chain, but no Cosmos user address found") - const contractRequest = { - chainId: selectedChain.id, - abi: ucs01abi, - account: $userAddrEvm.canonical, - functionName: "send", - address: ucs01address, - args: [ - ucs1_configuration.channel_id, - // @ts-ignore see the assertion above - pfmMemo === null ? $userAddrCosmos.normalized_prefixed : "0x01", // TODO: make dependent on target - [{ denom: $asset.address.toLowerCase() as Address, amount: parsedAmount }], - pfmMemo ?? "", // memo - { revision_number: 9n, revision_height: BigInt(999_999_999) + 100n }, - 0n - ] - } as const + // const contractRequest = { + // chainId: selectedChain.id, + // abi: ucs01abi, + // account: $userAddrEvm.canonical, + // functionName: "send", + // address: ucs01address, + // args: [ + // ucs1_configuration.channel_id, + // // @ts-ignore see the assertion above + // pfmMemo === null ? $userAddrCosmos.normalized_prefixed : "0x01", // TODO: make dependent on target + // [{ denom: $asset.address.toLowerCase() as Address, amount: parsedAmount }], + // pfmMemo ?? "", // memo + // { revision_number: 9n, revision_height: BigInt(999_999_999) + 100n }, + // 0n + // ] + // } as const if ($transferState.warning) { - transferState.set({ kind: "CONFIRMING_TRANSFER", contractRequest }) + transferState.set({ kind: "CONFIRMING_TRANSFER", contractRequest: null }) transfer() return } @@ -493,9 +536,9 @@ const transfer = async () => { console.log("confirming transfers test") try { - console.log("contract request", contractRequest) - const simulationResult = await simulateContract(config, contractRequest) - transferState.set({ kind: "CONFIRMING_TRANSFER", contractRequest }) + // console.log("contract request", contractRequest) + // const simulationResult = await simulateContract(config, contractRequest) + transferState.set({ kind: "CONFIRMING_TRANSFER", contractRequest: null }) } catch (error) { if (error instanceof Error) { transferState.set({ kind: "SIMULATING_TRANSFER", warning: error }) @@ -506,8 +549,17 @@ const transfer = async () => { if ($transferState.kind === "CONFIRMING_TRANSFER") { try { - const transferHash = await writeContract(config, $transferState.contractRequest) - transferState.set({ kind: "AWAITING_TRANSFER_RECEIPT", transferHash }) + // const transferHash = await writeContract(config, $transferState.contractRequest) + const transfer = await unionClient.transferAsset({ + autoApprove: false, + amount: parsedAmount, + recipient: $recipient, + denomAddress: $assetAddress, + destinationChainId: $toChain.chain_id + }) + if (transfer.isErr()) throw transfer.error + // @ts-expect-error + transferState.set({ kind: "AWAITING_TRANSFER_RECEIPT", transferHash: transfer.value }) } catch (error) { if (error instanceof Error) { transferState.set({ @@ -726,6 +778,7 @@ let stepperSteps = derived([fromChain, transferState], ([$fromChain, $transferSt ts => ({ status: "ERROR", title: `Error simulating transfer on ${$fromChain.display_name}`, + // @ts-expect-error description: `${ts.error}` }), () => ({ diff --git a/app/src/routes/transfer/state-machine.ts b/app/src/routes/transfer/state-machine.ts new file mode 100644 index 0000000000..f668d51809 --- /dev/null +++ b/app/src/routes/transfer/state-machine.ts @@ -0,0 +1 @@ +export type {} diff --git a/app/vite.config.ts b/app/vite.config.ts index daf1323fa9..10a10fa976 100644 --- a/app/vite.config.ts +++ b/app/vite.config.ts @@ -1,7 +1,6 @@ import Icons from "unplugin-icons/vite" import Inspect from "vite-plugin-inspect" import { sveltekit } from "@sveltejs/kit/vite" -import { sentrySvelteKit } from "@sentry/sveltekit" import { visualizer } from "rollup-plugin-visualizer" import { purgeCss } from "vite-plugin-tailwind-purgecss" import { partytownVite } from "@builder.io/partytown/utils" @@ -17,13 +16,6 @@ export default defineConfig(config => { const plugins = [ purgeCss(), - sentrySvelteKit({ - sourceMapsUploadOptions: { - project: "app", - telemetry: true, - org: "unionlabs" - } - }), sveltekit(), partytownVite({ debug: NODE_ENV === "development", diff --git a/typescript-sdk/jsr.json b/typescript-sdk/jsr.json index 345cade97e..5e4b2fca17 100644 --- a/typescript-sdk/jsr.json +++ b/typescript-sdk/jsr.json @@ -1,7 +1,7 @@ { "$schema": "https://jsr.io/schema/config-file.v1.json", "name": "@union/client", - "version": "0.0.4", + "version": "0.0.7", "license": "MIT", "exports": { ".": "./src/mod.ts" diff --git a/typescript-sdk/package.json b/typescript-sdk/package.json index cddb7ebeb3..cc22bc97f9 100644 --- a/typescript-sdk/package.json +++ b/typescript-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@union/client", - "version": "0.0.4", + "version": "0.0.7", "homepage": "https://jsr.io/@union/client", "description": "Union Labs cross-chain transfers client", "type": "module", diff --git a/typescript-sdk/src/client/evm.ts b/typescript-sdk/src/client/evm.ts index 34c0b49d0d..4dbb36680e 100644 --- a/typescript-sdk/src/client/evm.ts +++ b/typescript-sdk/src/client/evm.ts @@ -1,10 +1,12 @@ import { erc20Abi, + type Hex, getAddress, type Account, publicActions, type HttpTransport, createWalletClient, + type CustomTransport, type FallbackTransport } from "viem" import { @@ -19,13 +21,7 @@ import type { TransferAssetsParameters } from "./types.ts" import { createPfmMemo, getHubbleChainDetails } from "../pfm.ts" import { sepolia, scrollSepolia, arbitrumSepolia, berachainTestnetbArtio } from "viem/chains" -export { - sepolia, - scrollSepolia, - arbitrumSepolia, - berachainTestnetbArtio -} from "viem/chains" - +export { sepolia, scrollSepolia, arbitrumSepolia, berachainTestnetbArtio } export const evmChains = [sepolia, scrollSepolia, arbitrumSepolia, berachainTestnetbArtio] as const export const evmChainId: ReadonlyArray<`${(typeof evmChains)[number]["id"]}`> = [ `${sepolia.id}`, @@ -37,8 +33,8 @@ export type EvmChainId = `${(typeof evmChainId)[number]}` export interface EvmClientParameters { chainId: EvmChainId - transport: FallbackTransport | HttpTransport account?: `0x${string}` | Account | undefined + transport: FallbackTransport | HttpTransport | CustomTransport } export const chainIdToChain = (chainId: EvmChainId) => @@ -61,7 +57,7 @@ export const createEvmClient = (parameters: EvmClientParameters) => { simulate = true, destinationChainId, autoApprove = false - }: TransferAssetsParameters): Promise> => { + }: TransferAssetsParameters): Promise> => { account ||= client.account const pfmDetails = await getHubbleChainDetails({ @@ -100,7 +96,7 @@ export const createEvmClient = (parameters: EvmClientParameters) => { denomAddress, simulate = true, destinationChainId - }: TransferAssetsParameters): Promise> => { + }: TransferAssetsParameters): Promise> => { const ucsDetails = await getHubbleChainDetails({ destinationChainId, sourceChainId: parameters.chainId diff --git a/typescript-sdk/src/mod.ts b/typescript-sdk/src/mod.ts index 89cdefcc84..6545ba4a0e 100644 --- a/typescript-sdk/src/mod.ts +++ b/typescript-sdk/src/mod.ts @@ -1,5 +1,3 @@ -export const zkgm = "o______o" - import "./patch.ts" export type * from "./types.ts" export { @@ -11,13 +9,6 @@ export { hexStringToUint8Array, uint8ArrayToHexString } from "./convert.ts" -export { - truncateAddress, - isValidEvmTxHash, - isValidEvmAddress, - isValidCosmosTxHash, - isValidBech32Address -} from "./utilities/address.ts" import { evmChains, evmChainId, @@ -31,6 +22,14 @@ import { createCosmosClient, type CosmosClientParameters } from "./client/cosmos.ts" +export { + truncateAddress, + isValidEvmTxHash, + isValidEvmAddress, + isValidCosmosTxHash, + isValidBech32Address, + extractBech32AddressPrefix +} from "./utilities/address.ts" export { offchainQuery } from "./query/offchain/hubble.ts" export { createPfmMemo, getHubbleChainDetails } from "./pfm.ts" import type { ChainId, TransferAssetsParameters } from "./client/types.ts" diff --git a/typescript-sdk/src/pfm.ts b/typescript-sdk/src/pfm.ts index bf1601586c..fb966e469e 100644 --- a/typescript-sdk/src/pfm.ts +++ b/typescript-sdk/src/pfm.ts @@ -64,8 +64,9 @@ export async function getHubbleChainDetails({ if (!data) return err(new Error("Chain not found in hubble")) + const checkAgainst = sourceChainId === "union-testnet-8" ? destinationChainId : "union-testnet-8" const ucsConfiguration = data.ucs1_configurations - ?.filter(config => config.destination_chain.chain_id === "union-testnet-8") + ?.filter(config => config.destination_chain.chain_id === checkAgainst) .at(0) if (!ucsConfiguration) return err(new Error("UCS configuration not found")) diff --git a/typescript-sdk/src/transfer/evm.ts b/typescript-sdk/src/transfer/evm.ts index cf2395407f..099293e17d 100644 --- a/typescript-sdk/src/transfer/evm.ts +++ b/typescript-sdk/src/transfer/evm.ts @@ -1,5 +1,6 @@ import { erc20Abi, + type Hex, getAddress, type Address, type Account, @@ -55,7 +56,7 @@ export async function transferAssetFromEvm( autoApprove = false, relayContractAddress }: TransferAssetFromEvmParams -): Promise> { +): Promise> { account ||= client.account if (!account) return err(new Error("No account found")) @@ -146,7 +147,7 @@ export async function approveTransferAssetFromEvm( simulate = true, relayContractAddress }: ApproveTransferAssetFromEvmParams -): Promise> { +): Promise> { account ||= client.account if (!account) return err(new Error("No account found")) diff --git a/typescript-sdk/src/utilities/address.ts b/typescript-sdk/src/utilities/address.ts index 67c1119819..4e7ca41e62 100644 --- a/typescript-sdk/src/utilities/address.ts +++ b/typescript-sdk/src/utilities/address.ts @@ -1,6 +1,21 @@ import { bech32 } from "@scure/base" import type { HexAddress, Bech32Address } from "../types.ts" +/** + * extract the bech32 prefix from a bech32 address + * @example + * ```ts + * extractBech32AddressPrefix("union1qp0wtsfltjk9rnvyu3fkdv0s0skp4y5y3py96f") + * ``` + */ +export function extractBech32AddressPrefix(address: string) { + const pattern = /^([a-z]+)1[a-zA-Z0-9]{38,58}$/ + + const match = address.match(pattern) + if (match) return match[1] + return +} + /** * check if a string is a valid cosmos transaction hash * @example From c1c9e68922834b82fe7e2f76dbd5999a021dde90 Mon Sep 17 00:00:00 2001 From: o-az Date: Mon, 2 Sep 2024 16:55:43 -0700 Subject: [PATCH 03/12] chore: fmt --- .github/workflows/typescript-sdk-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/typescript-sdk-publish.yml b/.github/workflows/typescript-sdk-publish.yml index 3f90587136..17fe630c66 100644 --- a/.github/workflows/typescript-sdk-publish.yml +++ b/.github/workflows/typescript-sdk-publish.yml @@ -33,7 +33,7 @@ jobs: steps: - name: 'Checkout' uses: actions/checkout@v4 - + - name: 'Publish to JSR' working-directory: './typescript-sdk' run: | From 6a2755ae02021e1236ca797340e4626c0fc74de0 Mon Sep 17 00:00:00 2001 From: o-az Date: Mon, 2 Sep 2024 17:04:20 -0700 Subject: [PATCH 04/12] fix: comment out swap form component --- app/src/lib/swap-form.svelte | 124 +++++++++++++++++------------------ 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/app/src/lib/swap-form.svelte b/app/src/lib/swap-form.svelte index 664219c143..1cd80cbedb 100644 --- a/app/src/lib/swap-form.svelte +++ b/app/src/lib/swap-form.svelte @@ -7,7 +7,7 @@ import { userAddrEvm } from "$lib/wallet/evm" import { userAddrCosmos } from "$lib/wallet/cosmos" import * as Card from "$lib/components/ui/card/index.ts" -import { UnionClient } from "@union/client" +// import { UnionClient } from "@union/client" import { Button } from "$lib/components/ui/button" import type { Chain, UserAddresses } from "$lib/types.ts" @@ -42,66 +42,66 @@ const BERACHAIN_CONTRACTS = { ucs01_handler: "0x6F270608fB562133777AF0f71F6386ffc1737C30" } -const swap = async () => { - const cosmosOfflineSigner = window?.keplr?.getOfflineSigner($fromChainId, { - disableBalanceCheck: false - }) - - // TODO: don't hardcode union - const cosmosClient = new UnionClient({ - cosmosOfflineSigner, - evmSigner: undefined, - bech32Prefix: "union", - chainId: $fromChainId, - gas: { denom: "UNO", amount: "0.0025" }, - rpcUrl: `https://rpc.testnet-8.union.build` - }) - - const evmNoteMsg = { - kind: "cosmwasm", - instructions: [ - { - contractAddress: "union1c4wl7ytmf7kp6vupf50y3n8myu7m6xn8vspufledqd8x8hj9dn2s3clks5", - msg: { - execute: { - msgs: [ - { - call: { - to: "0x08247b1C6D6AACF6C655f711661D5810380C8385", - data: "095ea7b3000000000000000000000000ab827b1cc3535a9e549ee387a6e9c3f02f481b490000000000000000000000000000000000000000000000000000000000000007" - } - }, - { - call: { - to: "0xAB827b1Cc3535A9e549EE387A6E9C3F02F481B49", - data: "3d719cd900000000000000000000000008247b1c6d6aacf6c655f711661d5810380c83850000000000000000000000000e4aaf1351de4c0264c5c7056ef3777b41bd8e030000000000000000000000000000000000000000000000000000000000008ca00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffff5433e2b3d8211706e6102aa947100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - } - }, - { - ibc_send: { - tokens: [ - { - address: "0x0E4aaF1351de4c0264C5c7056Ef3777b41BD8e03", - amount: "718283233153" - } - ], - channel_id: "channel-3", - receiver: "15cbba30256b961c37b3fd7224523abdf562fd72" - } - } - ], - callback: null, - timeout_seconds: "5000000" - } - } - } - ] - } - - // @ts-ignore - const cosmosTransfer = await cosmosClient.transferAssets(evmNoteMsg) - console.log(cosmosTransfer.transactionHash) -} +// const swap = async () => { +// const cosmosOfflineSigner = window?.keplr?.getOfflineSigner($fromChainId, { +// disableBalanceCheck: false +// }) + +// // TODO: don't hardcode union +// const cosmosClient = new UnionClient({ +// cosmosOfflineSigner, +// evmSigner: undefined, +// bech32Prefix: "union", +// chainId: $fromChainId, +// gas: { denom: "UNO", amount: "0.0025" }, +// rpcUrl: `https://rpc.testnet-8.union.build` +// }) + +// const evmNoteMsg = { +// kind: "cosmwasm", +// instructions: [ +// { +// contractAddress: "union1c4wl7ytmf7kp6vupf50y3n8myu7m6xn8vspufledqd8x8hj9dn2s3clks5", +// msg: { +// execute: { +// msgs: [ +// { +// call: { +// to: "0x08247b1C6D6AACF6C655f711661D5810380C8385", +// data: "095ea7b3000000000000000000000000ab827b1cc3535a9e549ee387a6e9c3f02f481b490000000000000000000000000000000000000000000000000000000000000007" +// } +// }, +// { +// call: { +// to: "0xAB827b1Cc3535A9e549EE387A6E9C3F02F481B49", +// data: "3d719cd900000000000000000000000008247b1c6d6aacf6c655f711661d5810380c83850000000000000000000000000e4aaf1351de4c0264c5c7056ef3777b41bd8e030000000000000000000000000000000000000000000000000000000000008ca00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffff5433e2b3d8211706e6102aa947100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +// } +// }, +// { +// ibc_send: { +// tokens: [ +// { +// address: "0x0E4aaF1351de4c0264C5c7056Ef3777b41BD8e03", +// amount: "718283233153" +// } +// ], +// channel_id: "channel-3", +// receiver: "15cbba30256b961c37b3fd7224523abdf562fd72" +// } +// } +// ], +// callback: null, +// timeout_seconds: "5000000" +// } +// } +// } +// ] +// } + +// // @ts-ignore +// const cosmosTransfer = await cosmosClient.transferAssets(evmNoteMsg) +// console.log(cosmosTransfer.transactionHash) +// } @@ -131,7 +131,7 @@ const swap = async () => { To - {$toChain?.display_name ?? "Select chain"} + {$toChain?.display_name ?? 'Select chain'}
@@ -953,18 +961,26 @@ const resetInput = () => { {#if $sendableBalances === null} Failed to load sendable balances for {$fromChain?.display_name}. {:else if $sendableBalances && $sendableBalances.length === 0} - You don't have sendable assets on {$fromChain?.display_name}. You can get some from the faucet + You don't have sendable assets on {$fromChain?.display_name}. You can get some + from the faucet {:else} {/if} {:else} @@ -972,99 +988,99 @@ const resetInput = () => { {/if} {#if $assetSymbol !== '' && $sendableBalances !== null && $asset?.address}
- {truncate(supportedAsset ? supportedAsset?.display_symbol : $assetSymbol, 12)} balance on + {truncate(supportedAsset ? supportedAsset?.display_symbol : $assetSymbol, 12)} + balance on {$fromChain?.display_name} is {formatUnits(BigInt($asset.balance), supportedAsset?.decimals ?? 0)}
{/if}
- -
- Amount - -
-
- Recipient -
-
-
- -
-
- {#if userInput} - - {/if} -
+ +
+ Amount + +
+
+ Recipient +
+
+
+ +
+
+ {#if userInput} + + {/if}
- - - - - - - - - - - -
-
- - -
+
+ + +