From c7c2964eb80748f69c997c64cb5648acd4f52f24 Mon Sep 17 00:00:00 2001 From: Aaron Choo Date: Thu, 12 Oct 2023 11:32:46 +0800 Subject: [PATCH] feat: add `registry` pkg (#12) Co-authored-by: ryoid --- CONTRIBUTING.md | 4 + README.md | 5 + package.json | 3 + pnpm-lock.yaml | 223 +++++++++++++++++ scripts/gen-registry.mjs | 53 ++++ .../apis/getChainRegistryAssetList.ts | 10 + .../apis/getChainRegistryChainInfo.ts | 10 + src/registry/index.ts | 7 + src/registry/keplr/toKeplrChainInfo.ts | 151 ++++++++++++ src/registry/types/ChainRegistryAssetList.ts | 192 +++++++++++++++ src/registry/types/ChainRegistryChainInfo.ts | 227 ++++++++++++++++++ src/registry/types/KeplrChainInfo.ts | 3 + 12 files changed, 888 insertions(+) create mode 100644 scripts/gen-registry.mjs create mode 100644 src/registry/apis/getChainRegistryAssetList.ts create mode 100644 src/registry/apis/getChainRegistryChainInfo.ts create mode 100644 src/registry/index.ts create mode 100644 src/registry/keplr/toKeplrChainInfo.ts create mode 100644 src/registry/types/ChainRegistryAssetList.ts create mode 100644 src/registry/types/ChainRegistryChainInfo.ts create mode 100644 src/registry/types/KeplrChainInfo.ts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 73e62481..56d94d9b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -46,6 +46,10 @@ pnpm add --save-peer [dependency_name] ### Building For Production ```sh +# [OPTIONAL] To regenerate the code built using scripts +pnpm gen:protobufs +pnpm gen:registry + # To build to the `dist` directory pnpm build ``` diff --git a/README.md b/README.md index 001bd126..a1fcbb34 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ A tree-shakeable, framework agnostic, [pure ESM](https://gist.github.com/sindres - [`cosmes/client`](#cosmesclient) - [`cosmes/codec`](#cosmescodec) - [`cosmes/protobufs`](#cosmesprotobufs) + - [`cosmes/registry`](#cosmesregistry) - [`cosmes/wallet`](#cosmeswallet) - [Benchmarks](#benchmarks) - [Results](#results) @@ -108,6 +109,10 @@ This directory contains various encoding and decoding functions that relies sole This directory contains the auto-generated code for various Cosmos SDK based protobufs. See `scripts/gen-protobufs.mjs` for the script that generates the code. +### `cosmes/registry` + +This directory contains various APIs, data, and types needed for wallet interactions (ie. Keplr). Some types are auto-generated, see `scripts/gen-registry.mjs` for the script that generates the types. + ### `cosmes/wallet` This directory is a [Cosmos Kit](https://cosmoskit.com) alternative to manage various wallets (Keplr, Station, Cosmostation, Leap, etc.) across various different Cosmos SDK based blockchains. See [`examples/solid-vite`](./examples/solid-vite) for a working example. diff --git a/package.json b/package.json index eddb9a87..ddbfc167 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "./client": "./dist/client/index.js", "./codec": "./dist/codec/index.js", "./protobufs": "./dist/protobufs/index.js", + "./registry": "./dist/registry/index.js", "./wallet": "./dist/wallet/index.js" }, "scripts": { @@ -20,6 +21,7 @@ "build": "pnpm clean && tsc && tsc-alias", "dev": "concurrently \"tsc -w\" \"tsc-alias -w\"", "gen:protobufs": "node scripts/gen-protobufs.mjs", + "gen:registry": "node scripts/gen-registry.mjs", "lint": "eslint **/*.ts", "typecheck": "tsc --noEmit", "test": "vitest --run", @@ -52,6 +54,7 @@ "eslint": "^8.40.0", "eslint-config-prettier": "^8.8.0", "glob": "^10.2.3", + "json-schema-to-typescript": "^13.1.1", "lodash-es": "^4.17.21", "postcss": "^8.4.23", "prettier": "^2.8.8", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 74e7445c..78768eeb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,5 +1,9 @@ lockfileVersion: '6.0' +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + importers: .: @@ -61,6 +65,9 @@ importers: glob: specifier: ^10.2.3 version: 10.2.3 + json-schema-to-typescript: + specifier: ^13.1.1 + version: 13.1.1 lodash-es: specifier: ^4.17.21 version: 4.17.21 @@ -277,6 +284,16 @@ packages: regenerator-runtime: 0.13.11 dev: true + /@bcherny/json-schema-ref-parser@10.0.5-fork: + resolution: {integrity: sha512-E/jKbPoca1tfUPj3iSbitDZTGnq6FUFjkH6L8U2oDwSuwK1WhnnVtCG7oFOTg/DDnyoXbQYUiUiGOibHqaGVnw==} + engines: {node: '>= 16'} + dependencies: + '@jsdevtools/ono': 7.1.3 + '@types/json-schema': 7.0.11 + call-me-maybe: 1.0.2 + js-yaml: 4.1.0 + dev: true + /@bufbuild/buf-darwin-arm64@1.15.0-1: resolution: {integrity: sha512-KV3rvScwjMCcj0DSkmx6Ejk8YF7LtnqA3jSokeYbGaLLfSvPXu4q9d0I2pq+jaYIA8RfbMerAqzR8MccdY4Vqw==} engines: {node: '>=12'} @@ -743,6 +760,10 @@ packages: resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} dev: true + /@jsdevtools/ono@7.1.3: + resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} + dev: true + /@keplr-wallet/types@0.11.59: resolution: {integrity: sha512-/zuB3JgGZqbCSpK/RK2lhnTtdpBbMNuH1hLQ4eYKqrG3UX6bWD1l+SGDFLq6UulnoIGadfTe3Tbgbi9Jl1cgpw==} dependencies: @@ -1086,6 +1107,13 @@ packages: resolution: {integrity: sha512-CL7y71j2zaDmtPLD5Xq5S1Gv2dFoHl0/GBZm6s39Mj/ls28L3NzAOqf7H4H0/2TNVMgMjMVf9CAFYSjmXhi3bw==} dev: true + /@types/glob@7.2.0: + resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} + dependencies: + '@types/minimatch': 5.1.2 + '@types/node': 20.1.3 + dev: true + /@types/json-schema@7.0.11: resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} dev: true @@ -1104,6 +1132,10 @@ packages: resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==} dev: true + /@types/minimatch@5.1.2: + resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==} + dev: true + /@types/node@10.12.18: resolution: {integrity: sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==} dev: true @@ -1115,6 +1147,10 @@ packages: resolution: {integrity: sha512-NP2yfZpgmf2eDRPmgGq+fjGjSwFgYbihA8/gK+ey23qT9RkxsgNTZvGOEpXgzIGqesTYkElELLgtKoMQTys5vA==} dev: true + /@types/prettier@2.7.3: + resolution: {integrity: sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==} + dev: true + /@types/semver@7.5.0: resolution: {integrity: sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==} dev: true @@ -1841,6 +1877,10 @@ packages: engines: {node: '>=12'} dev: true + /any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + dev: true + /anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} @@ -2031,6 +2071,10 @@ packages: engines: {node: '>=8'} dev: true + /call-me-maybe@1.0.2: + resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} + dev: true + /callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -2092,6 +2136,17 @@ packages: safe-buffer: 5.2.1 dev: true + /cli-color@2.0.3: + resolution: {integrity: sha512-OkoZnxyC4ERN3zLzZaY9Emb7f/MhBOIpePv0Ycok0fJYT+Ouo00UBEIwsVsr0yoow++n5YWlSUgST9GKhNHiRQ==} + engines: {node: '>=0.10'} + dependencies: + d: 1.0.1 + es5-ext: 0.10.62 + es6-iterator: 2.0.3 + memoizee: 0.4.15 + timers-ext: 0.1.7 + dev: true + /cliui@6.0.0: resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} dependencies: @@ -2205,6 +2260,13 @@ packages: resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} dev: false + /d@1.0.1: + resolution: {integrity: sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==} + dependencies: + es5-ext: 0.10.62 + type: 1.2.0 + dev: true + /date-fns@2.30.0: resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} engines: {node: '>=0.11'} @@ -2340,6 +2402,40 @@ packages: once: 1.4.0 dev: true + /es5-ext@0.10.62: + resolution: {integrity: sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==} + engines: {node: '>=0.10'} + requiresBuild: true + dependencies: + es6-iterator: 2.0.3 + es6-symbol: 3.1.3 + next-tick: 1.1.0 + dev: true + + /es6-iterator@2.0.3: + resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==} + dependencies: + d: 1.0.1 + es5-ext: 0.10.62 + es6-symbol: 3.1.3 + dev: true + + /es6-symbol@3.1.3: + resolution: {integrity: sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==} + dependencies: + d: 1.0.1 + ext: 1.7.0 + dev: true + + /es6-weak-map@2.0.3: + resolution: {integrity: sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==} + dependencies: + d: 1.0.1 + es5-ext: 0.10.62 + es6-iterator: 2.0.3 + es6-symbol: 3.1.3 + dev: true + /esbuild@0.17.19: resolution: {integrity: sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==} engines: {node: '>=12'} @@ -2497,11 +2593,24 @@ packages: engines: {node: '>=0.10.0'} dev: true + /event-emitter@0.3.5: + resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==} + dependencies: + d: 1.0.1 + es5-ext: 0.10.62 + dev: true + /events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} dev: true + /ext@1.7.0: + resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} + dependencies: + type: 2.7.2 + dev: true + /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} dev: true @@ -2643,6 +2752,11 @@ packages: resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==} dev: true + /get-stdin@8.0.0: + resolution: {integrity: sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==} + engines: {node: '>=10'} + dev: true + /get-tsconfig@4.5.0: resolution: {integrity: sha512-MjhiaIWCJ1sAU4pIQ5i5OfOuHHxVo1oYeNsWTON7jxYkod8pHocXeh+SSbmu5OZZZK73B6cbJ2XADzXehLyovQ==} dev: true @@ -2661,6 +2775,16 @@ packages: is-glob: 4.0.3 dev: true + /glob-promise@4.2.2(glob@7.2.3): + resolution: {integrity: sha512-xcUzJ8NWN5bktoTIX7eOclO1Npxd/dyVqUJxlLIDasT4C7KZyqlPIwkdJ0Ypiy3p2ZKahTjK4M9uC3sNSfNMzw==} + engines: {node: '>=12'} + peerDependencies: + glob: ^7.1.6 + dependencies: + '@types/glob': 7.2.0 + glob: 7.2.3 + dev: true + /glob@10.2.2: resolution: {integrity: sha512-Xsa0BcxIC6th9UwNjZkhrMtNo/MnyRL8jGCP+uEwhA5oFOCY1f2s1/oNKY47xQ0Bg5nkjsfAEIej1VeH62bDDQ==} engines: {node: '>=16 || 14 >=14.17'} @@ -2822,6 +2946,10 @@ packages: engines: {node: '>=8'} dev: true + /is-promise@2.2.2: + resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==} + dev: true + /is-typedarray@1.0.0: resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} dev: true @@ -2877,6 +3005,27 @@ packages: hasBin: true dev: true + /json-schema-to-typescript@13.1.1: + resolution: {integrity: sha512-F3CYhtA7F3yPbb8vF7sFchk/2dnr1/yTKf8RcvoNpjnh67ZS/ZMH1ElLt5KHAtf2/bymiejLQQszszPWEeTdSw==} + engines: {node: '>=12.0.0'} + hasBin: true + dependencies: + '@bcherny/json-schema-ref-parser': 10.0.5-fork + '@types/json-schema': 7.0.11 + '@types/lodash': 4.14.195 + '@types/prettier': 2.7.3 + cli-color: 2.0.3 + get-stdin: 8.0.0 + glob: 7.2.3 + glob-promise: 4.2.2(glob@7.2.3) + is-glob: 4.0.3 + lodash: 4.17.21 + minimist: 1.2.8 + mkdirp: 1.0.4 + mz: 2.7.0 + prettier: 2.8.8 + dev: true + /json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} dev: true @@ -2993,6 +3142,12 @@ packages: engines: {node: 14 || >=16.14} dev: true + /lru-queue@0.1.0: + resolution: {integrity: sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==} + dependencies: + es5-ext: 0.10.62 + dev: true + /magic-string@0.30.0: resolution: {integrity: sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==} engines: {node: '>=12'} @@ -3015,6 +3170,19 @@ packages: safe-buffer: 5.2.1 dev: true + /memoizee@0.4.15: + resolution: {integrity: sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==} + dependencies: + d: 1.0.1 + es5-ext: 0.10.62 + es6-weak-map: 2.0.3 + event-emitter: 0.3.5 + is-promise: 2.2.2 + lru-queue: 0.1.0 + next-tick: 1.1.0 + timers-ext: 0.1.7 + dev: true + /merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -3061,11 +3229,21 @@ packages: brace-expansion: 2.0.1 dev: true + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + dev: true + /minipass@5.0.0: resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} engines: {node: '>=8'} dev: true + /mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + dev: true + /mlly@1.2.1: resolution: {integrity: sha512-1aMEByaWgBPEbWV2BOPEMySRrzl7rIHXmQxam4DM8jVjalTQDjpN2ZKOLUrwyhfZQO7IXHml2StcHMhooDeEEQ==} dependencies: @@ -3099,6 +3277,14 @@ packages: engines: {node: '>=12.0.0'} dev: true + /mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + dev: true + /nan@2.17.0: resolution: {integrity: sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==} dev: true @@ -3117,6 +3303,10 @@ packages: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true + /next-tick@1.1.0: + resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} + dev: true + /node-addon-api@2.0.2: resolution: {integrity: sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==} dev: true @@ -3140,6 +3330,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + dev: true + /on-exit-leak-free@0.2.0: resolution: {integrity: sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg==} dev: true @@ -3778,6 +3973,19 @@ packages: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} dev: true + /thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + dependencies: + thenify: 3.3.1 + dev: true + + /thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + dependencies: + any-promise: 1.3.0 + dev: true + /thread-stream@0.15.2: resolution: {integrity: sha512-UkEhKIg2pD+fjkHQKyJO3yoIvAP3N6RlNFt2dUhcS1FGvCD1cQa1M/PGknCLFIyZdtJOWQjejp7bdNqmN7zwdA==} dependencies: @@ -3789,6 +3997,13 @@ packages: engines: {node: '>=4'} dev: true + /timers-ext@0.1.7: + resolution: {integrity: sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==} + dependencies: + es5-ext: 0.10.62 + next-tick: 1.1.0 + dev: true + /tiny-secp256k1@1.1.6: resolution: {integrity: sha512-FmqJZGduTyvsr2cF3375fqGHUovSwDi/QytexX1Se4BPuPZpTE5Ftp5fg+EFSuEf3lhZqgCRjEG3ydUQ/aNiwA==} engines: {node: '>=6.0.0'} @@ -3892,6 +4107,14 @@ packages: engines: {node: '>=10'} dev: true + /type@1.2.0: + resolution: {integrity: sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==} + dev: true + + /type@2.7.2: + resolution: {integrity: sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==} + dev: true + /typedarray-to-buffer@3.1.5: resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} dependencies: diff --git a/scripts/gen-registry.mjs b/scripts/gen-registry.mjs new file mode 100644 index 00000000..789a0afe --- /dev/null +++ b/scripts/gen-registry.mjs @@ -0,0 +1,53 @@ +import { writeFileSync } from "fs"; +import { compile } from "json-schema-to-typescript"; +import { dirname, join } from "path"; +import { fileURLToPath } from "url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +async function genChainRegistryChainInfo() { + const tsName = "ChainRegistryChainInfo"; + const tsFile = tsName + ".ts"; + + console.log("Retrieving JSON schema..."); + const res = await fetch( + "https://raw.githubusercontent.com/cosmos/chain-registry/master/chain.schema.json" + ); + const schema = await res.json(); + schema.title = tsName; + + console.log("Compiling JSON schema to TypeScript..."); + const types = await compile(schema, tsName, { + // See: https://github.com/bcherny/json-schema-to-typescript?tab=readme-ov-file#options + strictIndexSignatures: true, + }); + + const target = join(__dirname, "..", "src", "registry", "types", tsFile); + writeFileSync(target, types); + console.log("Wrote types to", target); +} + +async function genChainRegistryAssetList() { + const tsName = "ChainRegistryAssetList"; + const tsFile = tsName + ".ts"; + + console.log("Retrieving JSON schema..."); + const res = await fetch( + "https://raw.githubusercontent.com/cosmos/chain-registry/master/assetlist.schema.json" + ); + const schema = await res.json(); + schema.title = tsName; + + console.log("Compiling JSON schema to TypeScript..."); + const types = await compile(schema, tsName, { + // See: https://github.com/bcherny/json-schema-to-typescript?tab=readme-ov-file#options + strictIndexSignatures: true, + }); + + const target = join(__dirname, "..", "src", "registry", "types", tsFile); + writeFileSync(target, types); + console.log("Wrote types to", target); +} + +await genChainRegistryChainInfo(); +await genChainRegistryAssetList(); diff --git a/src/registry/apis/getChainRegistryAssetList.ts b/src/registry/apis/getChainRegistryAssetList.ts new file mode 100644 index 00000000..99f5af1b --- /dev/null +++ b/src/registry/apis/getChainRegistryAssetList.ts @@ -0,0 +1,10 @@ +import { ChainRegistryAssetList } from "../types/ChainRegistryAssetList"; + +export async function getChainRegistryAssetList( + chain: string +): Promise { + const res = await fetch( + `https://raw.githubusercontent.com/cosmos/chain-registry/master/${chain}/assetlist.json` + ); + return res.json(); +} diff --git a/src/registry/apis/getChainRegistryChainInfo.ts b/src/registry/apis/getChainRegistryChainInfo.ts new file mode 100644 index 00000000..735ded73 --- /dev/null +++ b/src/registry/apis/getChainRegistryChainInfo.ts @@ -0,0 +1,10 @@ +import { ChainRegistryChainInfo } from "../types/ChainRegistryChainInfo"; + +export async function getChainRegistryChainInfo( + chain: string +): Promise { + const res = await fetch( + `https://raw.githubusercontent.com/cosmos/chain-registry/master/${chain}/chain.json` + ); + return res.json(); +} diff --git a/src/registry/index.ts b/src/registry/index.ts new file mode 100644 index 00000000..78d9b604 --- /dev/null +++ b/src/registry/index.ts @@ -0,0 +1,7 @@ +export type { ChainRegistryAssetList } from "./types/ChainRegistryAssetList"; +export type { ChainRegistryChainInfo } from "./types/ChainRegistryChainInfo"; +export type { KeplrChainInfo } from "./types/KeplrChainInfo"; + +export { getChainRegistryAssetList } from "./apis/getChainRegistryAssetList"; +export { getChainRegistryChainInfo } from "./apis/getChainRegistryChainInfo"; +export { toKeplrChainInfo } from "./keplr/toKeplrChainInfo"; diff --git a/src/registry/keplr/toKeplrChainInfo.ts b/src/registry/keplr/toKeplrChainInfo.ts new file mode 100644 index 00000000..99158a73 --- /dev/null +++ b/src/registry/keplr/toKeplrChainInfo.ts @@ -0,0 +1,151 @@ +import type { Currency, FeeCurrency } from "@keplr-wallet/types"; + +import { ChainRegistryAssetList } from "../types/ChainRegistryAssetList"; +import { ChainRegistryChainInfo } from "../types/ChainRegistryChainInfo"; +import { KeplrChainInfo } from "../types/KeplrChainInfo"; + +const getRpcEndpoint = (chain: ChainRegistryChainInfo): string => + chain.apis?.rpc?.[0]?.address ?? ""; +const getRestEndpoint = (chain: ChainRegistryChainInfo): string => + chain.apis?.rest?.[0]?.address ?? ""; + +/** + * Generates the Keplr compatible chain info using Chain Registry's chain info and asset list. + * This is useful when using Keplr's `experimentalSuggestChain` API. + * + * ```typescript + * // Example + * const chain = "archway" + * const [chainInfo, assetList] = await Promise.all([ + * getChainRegistryChainInfo(chain), + * getChainRegistryAssetList(chain), + * ]); + * const info = toKeplrChainInfo(chainInfo, assetList); + * window.keplr.experimentalSuggestChain(info); + * ``` + * + * @see https://docs.keplr.app/api/suggest-chain.html + */ +export function toKeplrChainInfo( + chainRegistryChainInfo: ChainRegistryChainInfo, + chainRegistryAssetList: ChainRegistryAssetList, + options: { + /** + * Override the default RPC endpoint getter. + * + * Default getter will use the first RPC endpoint in the chain's `apis.rpc` array + */ + getRpcEndpoint?: (chain: ChainRegistryChainInfo) => string; + /** + * Override the default REST endpoint getter. + * + * Default getter will use the first REST endpoint in the chain's `apis.rest` array + */ + getRestEndpoint?: (chain: ChainRegistryChainInfo) => string; + } = {} +): KeplrChainInfo { + // Adapted from: https://github.com/cosmology-tech/chain-registry/blob/main/packages/keplr/src/index.ts + + const currencies: Currency[] = chainRegistryAssetList.assets.map((asset) => ({ + coinDenom: asset.symbol, + coinMinimalDenom: asset.base, + coinDecimals: asset.denom_units.filter( + (denomUnit: { denom: string }) => denomUnit.denom === asset.display + )[0]?.exponent, + coinGeckoId: asset.coingecko_id, + coinImageUrl: asset.logo_URIs?.svg ?? asset.logo_URIs?.png, + })); + + /** + * Find first matching staking token + * If no staking token is found, use the first currency + */ + let stakeCurrency: Currency = currencies[0]; + if (chainRegistryChainInfo.staking) { + for (const token of chainRegistryChainInfo.staking.staking_tokens) { + const currency = currencies.find( + (currency) => currency.coinDenom === token.denom + ); + if (currency) { + stakeCurrency = currency; + break; + } + } + } + + /** + * FROM KEPLR chain-info.d.ts: + * This is used to set the fee of the transaction. + * If this field is empty, it just use the default gas price step (low: 0.01, average: 0.025, high: 0.04). + * And, set field's type as primitive number because it is hard to restore the prototype after deserialzing if field's type is `Dec`. + */ + const gasPriceSteps = chainRegistryChainInfo.fees?.fee_tokens?.reduce( + (m, feeToken) => { + m[feeToken.denom] = { + low: feeToken.low_gas_price ?? 0.01, + average: feeToken.average_gas_price ?? 0.025, + high: feeToken.high_gas_price ?? 0.04, + }; + return m; + }, + {} as Record> + ); + + const feeCurrencies: FeeCurrency[] = currencies + .filter((currency) => + chainRegistryChainInfo.fees?.fee_tokens.find( + (t) => t.denom === currency.coinMinimalDenom + ) + ) + .map((feeCurrency) => { + if (!gasPriceSteps?.hasOwnProperty(feeCurrency.coinMinimalDenom)) { + return feeCurrency; + } + const gasPriceStep = gasPriceSteps[feeCurrency.coinMinimalDenom]; + return { + ...feeCurrency, + gasPriceStep, + }; + }); + + const feeCurrenciesDefault: FeeCurrency[] = currencies + .filter((currency) => stakeCurrency.coinDenom === currency.coinDenom) + .map((feeCurrency) => { + if (!gasPriceSteps?.hasOwnProperty(feeCurrency.coinMinimalDenom)) { + return feeCurrency; + } + const gasPriceStep = gasPriceSteps[feeCurrency.coinMinimalDenom]; + return { + ...feeCurrency, + gasPriceStep, + }; + }); + + return { + chainId: chainRegistryChainInfo.chain_id, + chainName: chainRegistryChainInfo.chain_name, + + rpc: (options.getRpcEndpoint ?? getRpcEndpoint)(chainRegistryChainInfo), + rest: (options.getRestEndpoint ?? getRestEndpoint)(chainRegistryChainInfo), + + bip44: { + coinType: chainRegistryChainInfo.slip44 ?? 118, + }, + bech32Config: { + bech32PrefixAccAddr: chainRegistryChainInfo.bech32_prefix, + bech32PrefixAccPub: `${chainRegistryChainInfo.bech32_prefix}pub`, + bech32PrefixValAddr: `${chainRegistryChainInfo.bech32_prefix}valoper`, + bech32PrefixValPub: `${chainRegistryChainInfo.bech32_prefix}valoperpub`, + bech32PrefixConsAddr: `${chainRegistryChainInfo.bech32_prefix}valcons`, + bech32PrefixConsPub: `${chainRegistryChainInfo.bech32_prefix}valconspub`, + }, + + currencies, + stakeCurrency, + feeCurrencies: + feeCurrencies.length !== 0 ? feeCurrencies : feeCurrenciesDefault, + + // TODO: Support features (?) + // features: ["stargate", "ibc-transfer"], + }; +} diff --git a/src/registry/types/ChainRegistryAssetList.ts b/src/registry/types/ChainRegistryAssetList.ts new file mode 100644 index 00000000..ac7f5008 --- /dev/null +++ b/src/registry/types/ChainRegistryAssetList.ts @@ -0,0 +1,192 @@ +/* eslint-disable */ +/** + * This file was automatically generated by json-schema-to-typescript. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run json-schema-to-typescript to regenerate this file. + */ + +/** + * Asset lists are a similar mechanism to allow frontends and other UIs to fetch metadata associated with Cosmos SDK denoms, especially for assets sent over IBC. + */ +export interface ChainRegistryAssetList { + $schema?: string; + chain_name: string; + assets: Asset[]; +} +export interface Asset { + /** + * [OPTIONAL] A short description of the asset + */ + description?: string; + denom_units: DenomUnit[]; + /** + * [OPTIONAL] The potential options for type of asset. By default, assumes sdk.coin + */ + type_asset?: "sdk.coin" | "cw20" | "erc20" | "ics20" | "snip20" | "snip25"; + /** + * [OPTIONAL] The address of the asset. Only required for type_asset : cw20, snip20 + */ + address?: string; + /** + * The base unit of the asset. Must be in denom_units. + */ + base: string; + /** + * The project name of the asset. For example Bitcoin. + */ + name: string; + /** + * The human friendly unit of the asset. Must be in denom_units. + */ + display: string; + /** + * The symbol of an asset. For example BTC. + */ + symbol: string; + /** + * The origin of the asset, starting with the index, and capturing all transitions in form and location. + */ + traces?: (IbcTransition | IbcCw20Transition | NonIbcTransition)[]; + /** + * [OPTIONAL] IBC Channel between src and dst between chain + */ + ibc?: { + source_channel: string; + dst_channel: string; + source_denom: string; + }; + logo_URIs?: { + png?: string; + svg?: string; + }; + /** + * @minItems 1 + */ + images?: [ + { + image_sync?: Pointer; + png?: string; + svg?: string; + theme?: { + primary_color_hex?: string; + }; + }, + ...{ + image_sync?: Pointer; + png?: string; + svg?: string; + theme?: { + primary_color_hex?: string; + }; + }[] + ]; + /** + * [OPTIONAL] The coingecko id to fetch asset data from coingecko v3 api. See https://api.coingecko.com/api/v3/coins/list + */ + coingecko_id?: string; + keywords?: string[]; +} +export interface DenomUnit { + denom: string; + exponent: number; + aliases?: string[]; +} +export interface IbcTransition { + type: "ibc"; + counterparty: { + /** + * The name of the counterparty chain. (must match exactly the chain name used in the Chain Registry) + */ + chain_name: string; + /** + * The base unit of the asset on its source platform. E.g., when describing ATOM from Cosmos Hub, specify 'uatom', NOT 'atom' nor 'ATOM'; base units are unique per platform. + */ + base_denom: string; + /** + * The counterparty IBC transfer channel(, e.g., 'channel-1'). + */ + channel_id: string; + }; + chain: { + /** + * The chain's IBC transfer channel(, e.g., 'channel-1'). + */ + channel_id: string; + /** + * The port/channel/denom input string that generates the 'ibc/...' denom. + */ + path: string; + }; +} +export interface IbcCw20Transition { + type: "ibc-cw20"; + counterparty: { + /** + * The name of the counterparty chain. (must match exactly the chain name used in the Chain Registry) + */ + chain_name: string; + /** + * The base unit of the asset on its source platform. E.g., when describing ATOM from Cosmos Hub, specify 'uatom', NOT 'atom' nor 'ATOM'; base units are unique per platform. + */ + base_denom: string; + /** + * The port used to transfer IBC assets; often 'transfer', but sometimes varies, e.g., for outgoing cw20 transfers. + */ + port: string; + /** + * The counterparty IBC transfer channel(, e.g., 'channel-1'). + */ + channel_id: string; + }; + chain: { + /** + * The port used to transfer IBC assets; often 'transfer', but sometimes varies, e.g., for outgoing cw20 transfers. + */ + port: string; + /** + * The chain's IBC transfer channel(, e.g., 'channel-1'). + */ + channel_id: string; + /** + * The port/channel/denom input string that generates the 'ibc/...' denom. + */ + path: string; + }; +} +export interface NonIbcTransition { + type: "bridge" | "liquid-stake" | "synthetic" | "wrapped" | "additional-mintage" | "test-mintage"; + counterparty: { + /** + * The chain or platform from which the asset originates. E.g., 'cosmoshub', 'ethereum', 'forex', or 'nasdaq' + */ + chain_name: string; + base_denom: string; + /** + * The contract address where the transition takes place, where applicable. E.g., The Ethereum contract that locks up the asset while it's minted on another chain. + */ + contract?: string; + }; + chain?: { + /** + * The contract address where the transition takes place, where applicable. E.g., The Ethereum contract that locks up the asset while it's minted on another chain. + */ + contract: string; + }; + /** + * The entity offering the service. E.g., 'Gravity Bridge' [Network] or 'Tether' [Company]. + */ + provider: string; +} +/** + * The (primary) key used to identify an object within the Chain Registry. + */ +export interface Pointer { + /** + * The chain name or platform from which the object resides. E.g., 'cosmoshub', 'ethereum', 'forex', or 'nasdaq' + */ + chain_name: string; + /** + * The base denom of the asset from which the object originates. E.g., when describing ATOM from Cosmos Hub, specify 'uatom', NOT 'atom' nor 'ATOM'; base units are unique per platform. + */ + base_denom?: string; +} diff --git a/src/registry/types/ChainRegistryChainInfo.ts b/src/registry/types/ChainRegistryChainInfo.ts new file mode 100644 index 00000000..abbe5172 --- /dev/null +++ b/src/registry/types/ChainRegistryChainInfo.ts @@ -0,0 +1,227 @@ +/* eslint-disable */ +/** + * This file was automatically generated by json-schema-to-typescript. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run json-schema-to-typescript to regenerate this file. + */ + +/** + * Cosmos Chain.json is a metadata file that contains information about a cosmos sdk based chain. + */ +export interface ChainRegistryChainInfo { + $schema?: string; + chain_name: string; + chain_id: string; + pre_fork_chain_name?: string; + pretty_name?: string; + website?: string; + update_link?: string; + status?: "live" | "upcoming" | "killed"; + network_type?: "mainnet" | "testnet" | "devnet"; + /** + * The default prefix for the human-readable part of addresses that identifies the coin type. Must be registered with SLIP-0173. E.g., 'cosmos' + */ + bech32_prefix: string; + /** + * Used to override the bech32_prefix for specific uses. + */ + bech32_config?: { + /** + * e.g., 'cosmos' + */ + bech32PrefixAccAddr?: string; + /** + * e.g., 'cosmospub' + */ + bech32PrefixAccPub?: string; + /** + * e.g., 'cosmosvaloper' + */ + bech32PrefixValAddr?: string; + /** + * e.g., 'cosmosvaloperpub' + */ + bech32PrefixValPub?: string; + /** + * e.g., 'cosmosvalcons' + */ + bech32PrefixConsAddr?: string; + /** + * e.g., 'cosmosvalconspub' + */ + bech32PrefixConsPub?: string; + }; + daemon_name?: string; + node_home?: string; + key_algos?: ("secp256k1" | "ethsecp256k1" | "ed25519" | "sr25519")[]; + slip44?: number; + alternative_slip44s?: number[]; + fees?: { + fee_tokens: FeeToken[]; + }; + staking?: { + staking_tokens: StakingToken[]; + lock_duration?: { + /** + * The number of blocks for which the staked tokens are locked. + */ + blocks?: number; + /** + * The approximate time for which the staked tokens are locked. + */ + time?: string; + }; + }; + codebase?: { + git_repo?: string; + recommended_version?: string; + compatible_versions?: string[]; + binaries?: { + "linux/amd64"?: string; + "linux/arm64"?: string; + "darwin/amd64"?: string; + "darwin/arm64"?: string; + "windows/amd64"?: string; + "windows/arm64"?: string; + }; + cosmos_sdk_version?: string; + consensus?: { + type: "tendermint" | "cometbft"; + version?: string; + }; + cosmwasm_version?: string; + cosmwasm_enabled?: boolean; + /** + * Relative path to the cosmwasm directory. ex. $HOME/.juno/data/wasm + */ + cosmwasm_path?: string; + ibc_go_version?: string; + /** + * List of IBC apps (usually corresponding to a ICS standard) which have been enabled on the network. + */ + ics_enabled?: ("ics20-1" | "ics27-1" | "mauth")[]; + genesis?: { + name?: string; + genesis_url: string; + ics_ccv_url?: string; + }; + versions?: { + /** + * Official Upgrade Name + */ + name: string; + /** + * Git Upgrade Tag + */ + tag?: string; + /** + * Block Height + */ + height?: number; + /** + * Proposal that will officially signal community acceptance of the upgrade. + */ + proposal?: number; + /** + * [Optional] Name of the following version + */ + next_version_name?: string; + recommended_version?: string; + compatible_versions?: string[]; + cosmos_sdk_version?: string; + consensus?: { + type: "tendermint" | "cometbft"; + version?: string; + }; + cosmwasm_version?: string; + cosmwasm_enabled?: boolean; + /** + * Relative path to the cosmwasm directory. ex. $HOME/.juno/data/wasm + */ + cosmwasm_path?: string; + ibc_go_version?: string; + /** + * List of IBC apps (usually corresponding to a ICS standard) which have been enabled on the network. + */ + ics_enabled?: ("ics20-1" | "ics27-1" | "mauth")[]; + binaries?: { + "linux/amd64"?: string; + "linux/arm64"?: string; + "darwin/amd64"?: string; + "darwin/arm64"?: string; + "windows/amd64"?: string; + "windows/arm64"?: string; + }; + }[]; + }; + images?: { + image_sync?: Pointer; + png?: string; + svg?: string; + theme?: { + primary_color_hex?: string; + }; + }[]; + logo_URIs?: { + png?: string; + svg?: string; + }; + peers?: { + seeds?: Peer[]; + persistent_peers?: Peer[]; + }; + apis?: { + rpc?: Endpoint[]; + rest?: Endpoint[]; + grpc?: Endpoint[]; + wss?: Endpoint[]; + "grpc-web"?: Endpoint[]; + "evm-http-jsonrpc"?: Endpoint[]; + }; + explorers?: Explorer[]; + keywords?: string[]; + extra_codecs?: ("ethermint" | "injective")[]; +} +export interface FeeToken { + denom: string; + fixed_min_gas_price?: number; + low_gas_price?: number; + average_gas_price?: number; + high_gas_price?: number; + gas_costs?: { + cosmos_send?: number; + ibc_transfer?: number; + }; +} +export interface StakingToken { + denom: string; +} +/** + * The (primary) key used to identify an object within the Chain Registry. + */ +export interface Pointer { + /** + * The chain name or platform from which the object resides. E.g., 'cosmoshub', 'ethereum', 'forex', or 'nasdaq' + */ + chain_name: string; + /** + * The base denom of the asset from which the object originates. E.g., when describing ATOM from Cosmos Hub, specify 'uatom', NOT 'atom' nor 'ATOM'; base units are unique per platform. + */ + base_denom?: string; +} +export interface Peer { + id: string; + address: string; + provider?: string; +} +export interface Endpoint { + address: string; + provider?: string; + archive?: boolean; +} +export interface Explorer { + kind?: string; + url?: string; + tx_page?: string; + account_page?: string; +} diff --git a/src/registry/types/KeplrChainInfo.ts b/src/registry/types/KeplrChainInfo.ts new file mode 100644 index 00000000..4fae00e0 --- /dev/null +++ b/src/registry/types/KeplrChainInfo.ts @@ -0,0 +1,3 @@ +import type { ChainInfo as KeplrChainInfo } from "@keplr-wallet/types"; + +export type { KeplrChainInfo };