From 7240e6f989320aef7cb05e91a10a4dcab3ef5519 Mon Sep 17 00:00:00 2001 From: ALPAC-4 Date: Fri, 13 Sep 2024 21:32:47 +0900 Subject: [PATCH 01/30] use sqlite to store events --- package-lock.json | 305 +++++++++++++++- package.json | 3 + src/chain/index.ts | 599 -------------------------------- src/chain/types.ts | 59 ---- src/chain/wallet.ts | 170 --------- src/db/constants.ts | 6 + src/db/controller/client.ts | 82 +++++ src/db/controller/connection.ts | 51 +++ src/db/controller/packet.ts | 385 ++++++++++++++++++++ src/db/controller/syncInfo.ts | 99 ++++++ src/db/index.ts | 6 + src/db/migration/0.0.1.sql | 109 ++++++ src/db/migration/index.ts | 68 ++++ src/db/utils.ts | 212 +++++++++++ src/lib/chainPair.ts | 56 --- src/lib/config.ts | 43 ++- src/lib/eventParser.ts | 110 +----- src/lib/rawProof.ts | 4 +- src/msgs/ack.ts | 29 +- src/msgs/index.ts | 91 ----- src/msgs/recvPacet.ts | 21 +- src/msgs/signedHeader.ts | 4 +- src/msgs/timeout.ts | 29 +- src/msgs/updateClient.ts | 18 +- src/types/event.ts | 48 +++ src/types/index.ts | 3 + src/types/response.ts | 19 + src/types/table.ts | 84 +++++ src/workers/chain.ts | 226 ++++++++++++ src/workers/index.ts | 180 ++++++++++ src/workers/wallet.ts | 413 ++++++++++++++++++++++ 31 files changed, 2406 insertions(+), 1126 deletions(-) delete mode 100644 src/chain/index.ts delete mode 100644 src/chain/types.ts delete mode 100644 src/chain/wallet.ts create mode 100644 src/db/constants.ts create mode 100644 src/db/controller/client.ts create mode 100644 src/db/controller/connection.ts create mode 100644 src/db/controller/packet.ts create mode 100644 src/db/controller/syncInfo.ts create mode 100644 src/db/index.ts create mode 100644 src/db/migration/0.0.1.sql create mode 100644 src/db/migration/index.ts create mode 100644 src/db/utils.ts delete mode 100644 src/lib/chainPair.ts create mode 100644 src/types/event.ts create mode 100644 src/types/index.ts create mode 100644 src/types/response.ts create mode 100644 src/types/table.ts create mode 100644 src/workers/chain.ts create mode 100644 src/workers/index.ts create mode 100644 src/workers/wallet.ts diff --git a/package-lock.json b/package-lock.json index b3518db..1f1cd61 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "@initia/initia.proto": "^0.2.0", "axios": "^1.7.2", "bech32": "^2.0.0", + "better-sqlite3": "^11.3.0", "bluebird": "^3.7.2", "express": "^4.19.2", "http": "^0.0.1-security", @@ -30,8 +31,10 @@ "winston": "^3.13.0" }, "devDependencies": { + "@types/better-sqlite3": "^7.6.11", "@types/bluebird": "^3.5.42", "@types/express": "^4.17.21", + "@types/node": "^22.5.4", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", @@ -620,6 +623,15 @@ "version": "1.0.4", "license": "MIT" }, + "node_modules/@types/better-sqlite3": { + "version": "7.6.11", + "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.11.tgz", + "integrity": "sha512-i8KcD3PgGtGBLl3+mMYA8PdKkButvPyARxA7IQAd6qeslht13qxb1zzO8dRCtE7U3IoJS782zDBAeoKiM695kg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/bluebird": { "version": "3.5.42", "dev": true, @@ -685,10 +697,11 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.12.12", - "license": "MIT", + "version": "22.5.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz", + "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.19.2" } }, "node_modules/@types/qs": { @@ -1183,6 +1196,16 @@ "version": "2.0.0", "license": "MIT" }, + "node_modules/better-sqlite3": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.3.0.tgz", + "integrity": "sha512-iHt9j8NPYF3oKCNOO5ZI4JwThjt3Z6J6XrcwG85VNMVzv1ByqrHWv5VILEbCMFWDsoHhXvQ7oC8vgRXFAKgl9w==", + "hasInstallScript": true, + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + } + }, "node_modules/bignumber.js": { "version": "9.1.2", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", @@ -1234,6 +1257,39 @@ "@noble/hashes": "^1.2.0" } }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/bluebird": { "version": "3.7.2", "license": "MIT" @@ -1403,6 +1459,11 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, "node_modules/cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", @@ -1555,6 +1616,28 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -1615,6 +1698,14 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "engines": { + "node": ">=8" + } + }, "node_modules/diff": { "version": "4.0.2", "license": "BSD-3-Clause", @@ -1680,6 +1771,14 @@ "node": ">= 0.8" } }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/es-define-property": { "version": "1.0.0", "license": "MIT", @@ -1937,6 +2036,14 @@ "node": ">=0.8.x" } }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "engines": { + "node": ">=6" + } + }, "node_modules/express": { "version": "4.19.2", "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", @@ -2175,6 +2282,11 @@ "node": ">= 0.6" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -2205,6 +2317,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -2502,6 +2619,11 @@ "version": "2.0.4", "license": "ISC" }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -2810,6 +2932,17 @@ "node": ">= 0.6" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimalistic-assert": { "version": "1.0.1", "license": "ISC" @@ -2837,6 +2970,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + }, "node_modules/ms": { "version": "2.1.3", "license": "MIT" @@ -2846,6 +2984,11 @@ "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz", "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==" }, + "node_modules/napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -2860,6 +3003,17 @@ "node": ">= 0.6" } }, + "node_modules/node-abi": { + "version": "3.67.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.67.0.tgz", + "integrity": "sha512-bLn/fU/ALVBE9wj+p4Y21ZJWYFjUXLXPi/IewyLZkx3ApxKDNBWCKdReeKOtD8dWpOdDCeMyLh6ZewzcLsG2Nw==", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/node-addon-api": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", @@ -2905,7 +3059,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "dependencies": { "wrappy": "1" } @@ -3037,6 +3190,31 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/prebuild-install": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", + "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -3124,6 +3302,15 @@ "version": "1.1.0", "license": "MIT" }, + "node_modules/pump": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -3189,6 +3376,28 @@ "node": ">= 0.8" } }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "license": "MIT", @@ -3448,6 +3657,49 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "node_modules/simple-swizzle": { "version": "0.2.2", "license": "MIT", @@ -3552,6 +3804,32 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/tdigest": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.2.tgz", @@ -3696,6 +3974,17 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -3775,8 +4064,9 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "license": "MIT" + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" }, "node_modules/unpipe": { "version": "1.0.0", @@ -3886,8 +4176,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/ws": { "version": "7.5.10", diff --git a/package.json b/package.json index d13feb0..b978cb9 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "@initia/initia.proto": "^0.2.0", "axios": "^1.7.2", "bech32": "^2.0.0", + "better-sqlite3": "^11.3.0", "bluebird": "^3.7.2", "express": "^4.19.2", "http": "^0.0.1-security", @@ -32,8 +33,10 @@ "winston": "^3.13.0" }, "devDependencies": { + "@types/better-sqlite3": "^7.6.11", "@types/bluebird": "^3.5.42", "@types/express": "^4.17.21", + "@types/node": "^22.5.4", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", diff --git a/src/chain/index.ts b/src/chain/index.ts deleted file mode 100644 index afc1436..0000000 --- a/src/chain/index.ts +++ /dev/null @@ -1,599 +0,0 @@ -import { - LCDClient, - Wallet, - Msg, - APIRequester, - ConnectionCounterparty, -} from '@initia/initia.js' -import { delay } from 'bluebird' -import { info, error, debug } from 'src/lib/logger' -import * as http from 'http' -import * as https from 'https' - -import * as fs from 'fs' -import { parseSendPacketEvent, parseWriteAckEvent } from 'src/lib/eventParser' -import { WalletManager } from './wallet' -import { - generateMsgUpdateClient, - generateCounterpartyChainMessages, - generateThisChainMessages, -} from 'src/msgs' -import { RPCClient } from 'src/lib/rpcClient' -import { - ChainConfig, - ChainStatus, - ClientState, - PacketEventWithIndex, - SendPacketEventWithIndex, - SyncInfo, - WriteAckEventWithIndex, -} from './types' -import { metrics } from 'src/lib/metric' -import { Counter } from 'prom-client' - -export class Chain { - private syncInfo: { - height: number - txIndex: number - } - public latestHeight: number - private fedHeight: number - public latestTimestamp: number - private packetsToHandle: PacketEventWithIndex[] - private counterpartyChain: Chain - public clientId: string - // worker name => latest heartbeat - private workers: Record - - private constructor( - public lcd: LCDClient, - public rpc: RPCClient, - public wallet: WalletManager, - public connectionId: string - ) { - this.packetsToHandle = [] - this.workers = {} - } - - // initializer - - static async init(config: ChainConfig): Promise { - const lcd = new LCDClient( - config.lcdUri, - { - chainId: config.chainId, - gasPrices: config.gasPrice, - }, - new APIRequester(config.lcdUri, { - httpAgent: new http.Agent({ keepAlive: true }), - httpsAgent: new https.Agent({ keepAlive: true }), - timeout: 60000, - }) - ) - const rpc = new RPCClient(config.rpcUri) - const wallet = new Wallet(lcd, config.key) - const walletManager = new WalletManager(wallet, config.bech32Prefix) - - const chain = new Chain(lcd, rpc, walletManager, config.connectionId) - chain.clientId = (await lcd.ibc.connection(config.connectionId)).client_id - - await chain.updateLatestHeight() - const syncInfo = fs.existsSync(chain.syncFilePath()) - const dir = fs.existsSync('./.syncInfo') - if (!dir) { - fs.mkdirSync('./.syncInfo') - } - if (!syncInfo) { - chain.updatesyncInfo(config.syncInfo ?? { height: 1, txIndex: 0 }) - } - chain.syncInfo = JSON.parse( - fs.readFileSync(chain.syncFilePath()).toString() - ) as SyncInfo - chain.fedHeight = - chain.syncInfo.txIndex == -1 - ? chain.syncInfo.height - : chain.syncInfo.height - 1 - - return chain - } - - public async registerCounterpartyChain(counterpartyChain: Chain) { - if (this.counterpartyChain) { - throw Error('already has counterpartyChain') - } - - this.debug('register counterparty chain') - this.counterpartyChain = counterpartyChain - await this.validateConfig() - void this.latestHeightWorker() - void this.handlePackets() - void this.feedEvents() - } - - // workers - - private async handlePackets() { - // to prevent rerun - if ( - (this.workers['packet_handler'] ?? 0) > - new Date().valueOf() - 5 * 60 * 1000 - ) { - return - } - - this.debug('Activate packet handler') - - for (;;) { - try { - const packets = this.packetsToHandle.slice(0, 50) // TODO make this configurable - if (packets.length === 0) { - this.updatesyncInfo({ height: this.fedHeight, txIndex: -1 }) - continue - } - - // wait until next height - if (packets[packets.length - 1].height >= this.latestHeight) { - continue - } - - const syncInfo = { - height: packets[packets.length - 1].height, - txIndex: packets[packets.length - 1].txIndex, - } - - // filter packets - - // get send packet events - const sendPackets: SendPacketEventWithIndex[] = packets.filter( - (packet) => packet.type === 'send_packet' - ) as SendPacketEventWithIndex[] - - // get write acknowledgement events - const writeAcks: WriteAckEventWithIndex[] = packets.filter( - (packet) => packet.type === 'write_acknowledgement' - ) as WriteAckEventWithIndex[] - - this.info( - `Found events. Send packets - ${sendPackets.length}. Recv packets - ${writeAcks.length}` - ) - - // filter and split send packets - const { timeoutPackets, recvPackets } = - await this.splitSendPackets(sendPackets) - - // filter write ack - const acks = await this.filterAckPackets(writeAcks) - - this.info( - `Filtered events. Message to generate: timeout - ${timeoutPackets.length}. recvPacket - ${recvPackets.length}. ack - ${acks.length}` - ) - - // if nothing to do - if (timeoutPackets.length + recvPackets.length + acks.length === 0) { - this.updatesyncInfo(syncInfo) - this.packetsToHandle = this.packetsToHandle.slice(packets.length) - continue - } - - // generate msgs - - // counterparty msgs - const counterpartyMsgs: Msg[] = await generateCounterpartyChainMessages( - this, - this.counterpartyChain, - recvPackets, - acks - ) - - this.info( - `Request handle packet to wallet manager. This chain - ${ - timeoutPackets.length == 0 ? 0 : timeoutPackets.length + 1 - }. Counterparty chain - ${counterpartyMsgs.length}` - ) - - const [thisResult, counterpartyResult] = await Promise.all([ - this.delayedTimeout(timeoutPackets), - this.counterpartyChain.wallet.request(counterpartyMsgs), - ]) - - this.info( - `Packet Handled. This chain - ${thisResult.txhash} (code - ${thisResult.code}, rawLog - ${thisResult.rawLog}). Counterparty chain - ${counterpartyResult.txhash} (code - ${counterpartyResult.code} rawLog - ${counterpartyResult.rawLog})` - ) - - // All must succeed to update syncinfo or retry - if (thisResult.code === 0 && counterpartyResult.code === 0) { - this.updatesyncInfo(syncInfo) - this.packetsToHandle = this.packetsToHandle.slice(packets.length) - } - } catch (e) { - this.error(`Fail to handle packet. resonse - ${e}`) - } finally { - this.workers['packet_handler'] = new Date().valueOf() - await delay(1000) - } - } - } - - private async feedEvents() { - // to prevent rerun - if ( - (this.workers['event_feeder'] ?? 0) > - new Date().valueOf() - 5 * 60 * 1000 - ) { - return - } - - this.debug('Activate event feeder') - - for (;;) { - try { - if (this.packetsToHandle.length > 1000) continue - - // height to fetch - const heights = Array.from( - { length: 20 }, - (_, i) => i + this.fedHeight + 1 - ).filter((height) => height <= this.latestHeight) - - if (heights.length === 0) continue - - const blockResults = await Promise.all( - heights.map((height) => this.fetchBlockResult(height)) - ) - - this.debug( - `Fetched block results for heights (${JSON.stringify(heights)})` - ) - const results: PacketEventWithIndex[] = [] - - for (const events of blockResults) { - results.push(...events) - } - - this.debug(`push packets to packet to handle (${results.length})`) - this.packetsToHandle.push(...results) - this.fedHeight = heights[heights.length - 1] - } catch (e) { - this.error(`Fail to fecth block result. resonse - ${e}`) - } finally { - this.workers['event_feeder'] = new Date().valueOf() - await delay(100) - } - } - } - - private async latestHeightWorker() { - // to prevent rerun - if ( - (this.workers['latest_height_worekr'] ?? 0) > - new Date().valueOf() - 5 * 60 * 1000 - ) { - return - } - - this.debug('Activate latest height worekr') - - // TODO add websocket options - const MAX_RETRY = 10 - let retried = 0 - for (;;) { - try { - await this.updateLatestHeight() - this.debug( - `Set latest height. Height - ${this.latestHeight}, Timestamp - ${this.latestTimestamp}` - ) - const needUpdateClient = await this.checkTrustPeriod() - if (needUpdateClient) { - const { msg: msgUpdateClient } = await generateMsgUpdateClient( - this.counterpartyChain, - this - ) - const updateClientResult = await this.wallet.request([ - msgUpdateClient, - ]) - this.info( - `Client Updated. txhash - ${updateClientResult.txhash} (code - ${updateClientResult.code}).` - ) - } - retried = 0 - } catch (e) { - this.error( - `[latestHeightWorker] Got error while fetching latest height (${e})` - ) - retried++ - if (retried >= MAX_RETRY) { - throw Error( - `<${this.chainId()}> [latestHeightWorker] Max retry exceeded` - ) - } - } finally { - this.workers['latest_height_worekr'] = new Date().valueOf() - await delay(1000) - } - } - } - - // filters - - // split and filter - private async splitSendPackets(packets: SendPacketEventWithIndex[]): Promise<{ - timeoutPackets: SendPacketEventWithIndex[] - recvPackets: SendPacketEventWithIndex[] - }> { - const cutoffHeight = this.counterpartyChain.latestHeight - const cutoffTime = this.counterpartyChain.latestTimestamp + 10000 - - // source path => packet - const timeoutPackets: Record = {} - const recvPackets: Record = {} - - for (const packet of packets) { - const path = `${packet.packetData.source_port}/${packet.packetData.source_channel}` - const heightTimeout = - packet.packetData.timeout_height !== undefined && - packet.packetData.timeout_height.revision_height != 0 && - packet.packetData.timeout_height.revision_number != 0 && - cutoffHeight >= packet.packetData.timeout_height.revision_height - - const timestampTimeout = - Number(packet.packetData.timeout_timestamp) != 0 && - cutoffTime * 1000000 >= Number(packet.packetData.timeout_timestamp) - - if (!heightTimeout && !timestampTimeout) { - if (recvPackets[path] === undefined) { - recvPackets[path] = [] - } - recvPackets[path].push(packet) - } else { - if (timeoutPackets[path] === undefined) { - timeoutPackets[path] = [] - } - timeoutPackets[path].push(packet) - } - } - - // filter timeout that already done. - // filter by unreceivedAcks - await Promise.all( - Object.keys(timeoutPackets).map(async (path) => { - if (timeoutPackets[path].length === 0) return - const unrecivedPackets = await this.lcd.ibc.unreceivedAcks( - timeoutPackets[path][0].packetData.source_port, - timeoutPackets[path][0].packetData.source_channel, - timeoutPackets[path].map((packet) => packet.packetData.sequence) - ) - - const unrecivedSequences = unrecivedPackets.sequences.map((sequence) => - Number(sequence) - ) - - timeoutPackets[path] = timeoutPackets[path].filter((packet) => - unrecivedSequences.includes(packet.packetData.sequence) - ) - }) - ) - - // filter by unreceivedPacket - await Promise.all( - Object.keys(timeoutPackets).map(async (path) => { - if (timeoutPackets[path].length === 0) return - const unrecivedPackets = - await this.counterpartyChain.lcd.ibc.unreceivedPackets( - timeoutPackets[path][0].packetData.destination_port, - timeoutPackets[path][0].packetData.destination_channel, - timeoutPackets[path].map((packet) => packet.packetData.sequence) - ) - - const unrecivedSequences = unrecivedPackets.sequences.map((sequence) => - Number(sequence) - ) - - timeoutPackets[path] = timeoutPackets[path].filter((packet) => - unrecivedSequences.includes(packet.packetData.sequence) - ) - }) - ) - - // filter recv packets that already done. - await Promise.all( - Object.keys(recvPackets).map(async (path) => { - if (recvPackets[path].length === 0) return - const unrecivedPackets = - await this.counterpartyChain.lcd.ibc.unreceivedPackets( - recvPackets[path][0].packetData.destination_port, - recvPackets[path][0].packetData.destination_channel, - recvPackets[path].map((packet) => packet.packetData.sequence) - ) - - const unrecivedSequences = unrecivedPackets.sequences.map((sequence) => - Number(sequence) - ) - - recvPackets[path] = recvPackets[path].filter((packet) => - unrecivedSequences.includes(packet.packetData.sequence) - ) - }) - ) - - return { - timeoutPackets: Object.values(timeoutPackets).flat(), - recvPackets: Object.values(recvPackets).flat(), - } - } - - private async delayedTimeout(timeoutPackets: SendPacketEventWithIndex[]) { - if (timeoutPackets.length === 0) { - return this.wallet.request([]) - } - - await delay(15000) - // chain msgs - const thisMsgs: Msg[] = await generateThisChainMessages( - this, - this.counterpartyChain, - timeoutPackets - ) - - return this.wallet.request(thisMsgs) - } - - private async filterAckPackets( - packets: WriteAckEventWithIndex[] - ): Promise { - const unrecivedSequences: number[] = [] - await Promise.all( - packets.map(async (packet) => { - const unrecivedPackets = - await this.counterpartyChain.lcd.ibc.unreceivedAcks( - packet.packetData.packet.source_port, - packet.packetData.packet.source_channel, - [packet.packetData.packet.sequence] - ) - - if (unrecivedPackets.sequences[0]) { - unrecivedSequences.push(Number(unrecivedPackets.sequences[0])) - } - }) - ) - return packets.filter((packet) => - unrecivedSequences.includes(packet.packetData.packet.sequence) - ) - } - - private async validateConfig() { - // check connection mapping - const counterpartyConnectionId = ( - (await this.lcd.ibc.connection(this.connectionId)) - .counterparty as ConnectionCounterparty - ).connection_id - - if (counterpartyConnectionId !== this.counterpartyChain.connectionId) { - throw Error( - `"${this.chainId()} - ${ - this.connectionId - }" is not connected with "${this.counterpartyChain.chainId()} - ${ - this.counterpartyChain.connectionId - }"` - ) - } - } - - // return true if it need update client - private async checkTrustPeriod(): Promise { - const state = await this.lcd.apiRequester.get( - `/ibc/core/client/v1/client_states/${this.clientId}` - ) - const trustingPeriod = - Number(state.client_state.trusting_period.replace('s', '')) * 1000 - const revisionHeight = Number( - state.client_state.latest_height.revision_height - ) - - const header = await this.counterpartyChain.rpc.header(revisionHeight) - const revisionTimestamp = new Date(header.header.time).valueOf() - const timeDiff = new Date().valueOf() - revisionTimestamp - return timeDiff >= trustingPeriod * 0.66 - } - - private updatesyncInfo(syncInfo: SyncInfo) { - fs.writeFileSync(this.syncFilePath(), JSON.stringify(syncInfo)) - this.syncInfo = syncInfo - } - - private async updateLatestHeight() { - const abciInfo = await this.rpc.abciInfo() - if (!abciInfo.lastBlockHeight) { - throw Error('Can not get last block height') - } - const height = abciInfo.lastBlockHeight - this.latestHeight = Number(height) - this.latestTimestamp = new Date().valueOf() // is it okay to use local timestamp? - - this.inc(metrics.chain.latestHeightWorker) - } - - private async fetchBlockResult( - height: number - ): Promise { - this.debug(`Fecth new block results (height - ${height})`) - const blockResult = await this.rpc.blockResults(height) - const txData = [...blockResult.results] - - const packetEvents: PacketEventWithIndex[] = [] - - txData.map((data, i) => { - for (const event of data.events) { - const sendPacket = parseSendPacketEvent(event, this.connectionId) - if (sendPacket) { - packetEvents.push({ - height, - txIndex: i, - type: 'send_packet', - packetData: sendPacket, - }) - - this.inc(metrics.chain.eventFeederWorker.sendPacket) - } - - const writeAck = parseWriteAckEvent(event, this.connectionId) - if (writeAck) { - packetEvents.push({ - height, - txIndex: i, - type: 'write_acknowledgement', - packetData: writeAck, - }) - - this.inc(metrics.chain.eventFeederWorker.writeAck) - } - } - }) - - return packetEvents - } - - private syncFilePath(): string { - return `./.syncInfo/${this.lcd.config.chainId}_${this.connectionId}.json` - } - - private chainId(): string { - return this.lcd.config.chainId as string - } - - public chainStatus(): ChainStatus { - return { - chainId: this.chainId(), - connectionId: this.connectionId, - latestHeightInfo: { - height: this.latestHeight, - timestamp: new Date(this.latestTimestamp), - }, - lastFeedHeight: this.fedHeight, - syncInfo: { ...this.syncInfo }, - } - } - // logs - - private info(log: string) { - info(`<${this.chainId()}/${this.connectionId}> ${log}`) - } - - private error(log: string) { - error(`<${this.chainId()}/${this.connectionId}> ${log}`) - } - - private debug(log: string) { - debug(`<${this.chainId()}/${this.connectionId}> ${log}`) - } - - // metrics - - public inc(counter: Counter, inc?: number) { - counter - .labels({ - chainId: this.chainId(), - connectionId: this.connectionId, - }) - .inc(inc) - } -} diff --git a/src/chain/types.ts b/src/chain/types.ts deleted file mode 100644 index ab10b10..0000000 --- a/src/chain/types.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { Key, Packet } from '@initia/initia.js' -import { Ack } from 'src/msgs' - -export interface ChainConfig { - bech32Prefix: string - chainId: string - gasPrice: string - lcdUri: string - rpcUri: string - key: Key - connectionId: string - syncInfo?: { - height: number - txIndex: number - } -} - -export interface SyncInfo { - height: number - txIndex: number -} - -export type PacketEventWithIndex = - | SendPacketEventWithIndex - | WriteAckEventWithIndex - -export interface SendPacketEventWithIndex { - height: number - txIndex: number - type: 'send_packet' - packetData: Packet -} - -export interface WriteAckEventWithIndex { - height: number - txIndex: number - type: 'write_acknowledgement' - packetData: Ack -} - -export interface ChainStatus { - chainId: string - connectionId: string - latestHeightInfo: { - height: number - timestamp: Date - } - lastFeedHeight: number - syncInfo: SyncInfo -} - -export interface ClientState { - client_state: { - trusting_period: string - latest_height: { - revision_height: string - } - } -} diff --git a/src/chain/wallet.ts b/src/chain/wallet.ts deleted file mode 100644 index 8ac4114..0000000 --- a/src/chain/wallet.ts +++ /dev/null @@ -1,170 +0,0 @@ -import { - Msg, - SyncTxBroadcastResult, - Wallet, - isTxError, -} from '@initia/initia.js' -import { bech32 } from 'bech32' -import { delay } from 'bluebird' -import { error, info, warn } from 'src/lib/logger' - -export class WalletManager { - private requests: Record< - number, - { msgs: Msg[]; result: SyncTxBroadcastResult | undefined } - > - private requestIndexInprogress: number - private requestIndex: number - private sequence: number - constructor( - private wallet: Wallet, - private bech32Prefix: string - ) { - this.requests = {} - this.requestIndex = 0 - this.requestIndexInprogress = 0 - void this.runRequestWorker() - } - - async init() { - const accountInfo = await this.wallet.lcd.auth.accountInfo(this.address()) - this.sequence = accountInfo.getSequenceNumber() - } - - async request(msgs: Msg[], executeDelay = 0): Promise { - await delay(executeDelay) - if (msgs.length === 0) { - return { - txhash: '', - code: 0, - } - } - const index = this.requestIndex++ - - this.requests[index] = { msgs, result: undefined } - - while (this.requests[index].result === undefined) { - await delay(500) - } - - const result = this.requests[index].result - // just for avoid lint error - if (result === undefined) { - throw Error('Result Not Found') - } - - delete this.requests[index] - // polling tx - - if (isTxError(result) && result.code !== 19) { - return { - txhash: result.txhash, - code: Number(result.code), - rawLog: result.raw_log, - } - } - - let retry = 0 - // TODO make this as config - while (retry < 120) { - const txResult = await this.wallet.lcd.tx - .txInfo(result.txhash) - .catch(() => undefined) - await delay(500) - retry++ - - if (txResult) { - return { - txhash: result.txhash, - code: txResult.code, - rawLog: txResult.code === 0 ? undefined : txResult.raw_log, - } - } - } - - return { - txhash: result.txhash, - code: -1, - rawLog: 'timeout', - } - } - - private async runRequestWorker() { - const MAX_RETRY = 10 - let retried = 0 - for (;;) { - const request = this.requests[this.requestIndexInprogress] - try { - if (!this.sequence) { - await this.init() - } - - if (!request) continue - - const signedTx = await this.wallet.createAndSignTx({ - msgs: request.msgs, - sequence: this.sequence, - }) - - const result = await this.wallet.lcd.tx.broadcastSync(signedTx) - request.result = result - - this.requestIndexInprogress++ - - if (result.raw_log.startsWith('account sequence mismatch')) { - try { - const expected = result.raw_log.split(', ')[1] - this.sequence = Number(expected.split(' ')[1]) - info(`update sequence`) - } catch (e) { - warn(`error to parse sequence`) - } - } - - if (!(isTxError(result) && result.code !== 19)) { - this.sequence++ - } - } catch (e) { - const errorMsg = e as { response?: { data: string } } - const errorContent = JSON.stringify(errorMsg?.response?.data ?? e) - if (errorContent.indexOf('account sequence mismatch') !== -1) { - const expected = errorContent - .slice(errorContent.indexOf('account sequence mismatch')) - .split(', ')[1] - this.sequence = Number(expected.split(' ')[1]) - info(`update sequence`) - } - - error( - `[runRequestWorker] (${JSON.stringify(errorMsg?.response?.data ?? error)})` - ) - retried++ - - // if fail to broadcast, return error result to make regenerate msgs - if (retried >= MAX_RETRY) { - this.requestIndexInprogress++ - error(`[runRequestWorker] Max retry exceeded`) - request.result = { - txhash: '', - height: -1, - code: -1, - raw_log: '[runRequestWorker] Max retry exceeded', - } - } - } finally { - await delay(1000) - } - } - } - - public address(): string { - const address = this.wallet.key.accAddress - return bech32.encode(this.bech32Prefix, bech32.decode(address).words) - } -} - -interface TxResult { - txhash: string - code?: number - rawLog?: string // only include in code is not zero -} diff --git a/src/db/constants.ts b/src/db/constants.ts new file mode 100644 index 0000000..12681f5 --- /dev/null +++ b/src/db/constants.ts @@ -0,0 +1,6 @@ +// constants + +export const U64_MAX = 0xffffffffffffffffn + +// db path +export const DB_PATH = './.db/main.db' diff --git a/src/db/controller/client.ts b/src/db/controller/client.ts new file mode 100644 index 0000000..88ef830 --- /dev/null +++ b/src/db/controller/client.ts @@ -0,0 +1,82 @@ +import { DB } from '..' +import { insert, selectOne } from '../utils' +import { Any } from 'cosmjs-types/google/protobuf/any' +import { ClientState, UpdateClientEvent, ClientTable } from 'src/types' +import { Header } from 'cosmjs-types/ibc/lightclients/tendermint/v1/tendermint' +import { LCDClient } from '@initia/initia.js' + +export class ClientController { + static tableName = 'client' + public static async addClient( + lcd: LCDClient, + chainId: string, + clientId: string + ): Promise { + const state = await lcd.apiRequester.get( + `/ibc/core/client/v1/client_states/${clientId}` + ) + + const client: ClientTable = { + chain_id: chainId, + client_id: clientId, + counterparty_chain_id: state.client_state.chain_id, + trusting_period: parseInt( + state.client_state.trusting_period.replace('s', '') + ), + revision_height: parseInt( + state.client_state.latest_height.revision_height + ), + last_update_time: 0, + } + + insert(DB, this.tableName, client) + + return client + } + + public static async feedUpdateClientEvent( + lcd: LCDClient, + chainId: string, + event: UpdateClientEvent + ) { + // create key + const clientId = event.clientId + + // decode header + const msg = Any.decode( + new Uint8Array([...Buffer.from(event.header, 'hex')]) + ) + if (msg.typeUrl !== '/ibc.lightclients.tendermint.v1.Header') return + const header = Header.decode(msg.value) + + // get client + const client = await this.getClient(lcd, chainId, clientId) + + // update client + client.revision_height = parseInt( + event.consensusHeights.split(',')[0].split('-')[1] + ) + + if (header.signedHeader?.header?.time.seconds) { + client.last_update_time = Number(header.signedHeader.header.time.seconds) + } + + insert(DB, this.tableName, client) + } + + public static async getClient( + lcd: LCDClient, + chainId: string, + clientId: string + ): Promise { + // get client + const client = selectOne(DB, this.tableName, [ + { + chain_id: chainId, + client_id: clientId, + }, + ]) + + return client ?? this.addClient(lcd, chainId, clientId) + } +} diff --git a/src/db/controller/connection.ts b/src/db/controller/connection.ts new file mode 100644 index 0000000..b585699 --- /dev/null +++ b/src/db/controller/connection.ts @@ -0,0 +1,51 @@ +import { DB } from '..' +import { ConnectionInfo, ConnectionTable } from 'src/types' +import { LCDClient } from '@initia/initia.js' +import { ClientController } from './client' +import { insert, selectOne } from '../utils' + +export class ConnectionController { + static tableName = 'connection' + public static async addConnection( + lcd: LCDClient, + chainId: string, + connectionId: string + ): Promise { + const connectionInfo = await lcd.apiRequester.get( + `/ibc/core/connection/v1/connections/${connectionId}` + ) + const clientId = connectionInfo.connection.client_id + const client = await ClientController.getClient(lcd, chainId, clientId) + + const connection: ConnectionTable = { + chain_id: chainId, + connection_id: connectionId, + client_id: client.client_id, + counterparty_chain_id: client.counterparty_chain_id, + counterparty_connection_id: + connectionInfo.connection.counterparty.connection_id, + counterparty_client_id: connectionInfo.connection.counterparty.client_id, + } + + insert(DB, this.tableName, connection) + + return connection + } + + // TODO: add connection_open_init event feeder + + public static async getConnection( + lcd: LCDClient, + chainId: string, + connectionId: string + ): Promise { + const connection = selectOne(DB, this.tableName, [ + { + chain_id: chainId, + connection_id: connectionId, + }, + ]) + + return connection ?? this.addConnection(lcd, chainId, connectionId) + } +} diff --git a/src/db/controller/packet.ts b/src/db/controller/packet.ts new file mode 100644 index 0000000..3421c38 --- /dev/null +++ b/src/db/controller/packet.ts @@ -0,0 +1,385 @@ +import { + AcknowledgePacketEvent, + PacketEvent, + SendPacketEvent, + TimeoutPacketEvent, + WriteAckEvent, +} from 'src/chain/types' +import { DB } from '..' +import { In, WhereOptions, del, insert, select, update } from '../utils' +import { LCDClient } from '@initia/initia.js' +import { ConnectionController } from './connection' +import { + PacketSendTable, + PacketTimeoutTable, + PacketWriteAckTable, +} from '../types' + +export class PacketController { + private static tableNamePacketSend = 'packet_send' + private static tableNamePacketTimeout = 'packet_timeout' + private static tableNamePacketWriteAck = 'packet_write_ack' + + public static async feedEvents( + lcd: LCDClient, + chainId: string, + events: PacketEvent[] + ) { + for (const event of events) { + switch (event.type) { + case 'send_packet': + await this.feedSendPacketEvent(lcd, chainId, event) + break + case 'write_acknowledgement': + await this.feedWriteAckEvent(lcd, chainId, event) + break + case 'acknowledge_packet': + await this.feedAcknowledgePacketEvent(lcd, chainId, event) + break + case 'timeout_packet': + await this.feedTimeoutPacketEvent(lcd, chainId, event) + break + } + } + } + + public static getSendPackets( + chainId: string, + counterpartyChainIds: string[], + filter: PacketFilter = {}, + limit = 100 + ): PacketSendTable[] { + const wheres: WhereOptions[] = [] + if (filter.connections) { + wheres.push( + ...filter.connections.map((conn) => ({ + in_progress: false, + dst_chain_id: chainId, + dst_connection_id: conn.connectionId, + dst_channel_id: conn.channels ? In(conn.channels) : undefined, + src_chain_id: In(counterpartyChainIds), // TODO: make this more efficientnet, like filter it on outside of this. + })) + ) + } else { + wheres.push({ + in_progress: false, + dst_chain_id: chainId, + src_chain_id: In(counterpartyChainIds), + }) + } + + return select(DB, this.tableNamePacketSend, wheres, limit) + } + + public static getTimeoutPackets( + chainId: string, + height: number, + timetstamp: number, + counterpartyChainIds: string[], + filter: PacketFilter = {}, + limit = 100 + ): PacketTimeoutTable[] { + const wheres: WhereOptions[] = [] + + if (filter.connections) { + wheres.push( + ...filter.connections.map((conn) => ({ + in_progress: false, + src_chain_id: chainId, + src_connection_id: conn.connectionId, + src_channel_id: conn.channels ? In(conn.channels) : undefined, + dst_chain_id: In(counterpartyChainIds), // TODO: make this more efficientnet, like filter it on outside of this. + height: { lt: height, gt: 0 }, + timetstamp: { lt: timetstamp, gt: 0 }, + })) + ) + } else { + wheres.push({ + in_progress: false, + src_chain_id: chainId, + dst_chain_id: In(counterpartyChainIds), + }) + } + + return select( + DB, + this.tableNamePacketTimeout, + wheres, + limit + ) + } + + public static getWriteAckPackets( + chainId: string, + counterpartyChainIds: string[], + filter: PacketFilter = {}, + limit = 100 + ): PacketWriteAckTable[] { + const wheres: WhereOptions[] = [] + + if (filter.connections) { + wheres.push( + ...filter.connections.map((conn) => ({ + in_progress: false, + src_chain_id: chainId, + src_connection_id: conn.connectionId, + src_channel_id: conn.channels ? In(conn.channels) : undefined, + dst_chain_id: In(counterpartyChainIds), // TODO: make this more efficientnet, like filter it on outside of this. + })) + ) + } else { + wheres.push({ + in_progress: false, + src_chain_id: chainId, + dst_chain_id: In(counterpartyChainIds), + }) + } + + return select( + DB, + this.tableNamePacketWriteAck, + wheres, + limit + ) + } + + public static updateSendPacketInProgress( + packet: PacketSendTable, + inProgress = true + ) { + update( + DB, + this.tableNamePacketSend, + { in_progress: inProgress }, + [ + { + dst_chain_id: packet.dst_chain_id, + dst_connection_id: packet.dst_connection_id, + dst_channel_id: packet.dst_channel_id, + sequence: packet.sequence, + }, + ] + ) + } + + public static updateTimeoutPacketInProgress( + packet: PacketTimeoutTable, + inProgress = true + ) { + update( + DB, + this.tableNamePacketTimeout, + { in_progress: inProgress }, + [ + { + src_chain_id: packet.src_chain_id, + src_connection_id: packet.src_connection_id, + src_channel_id: packet.src_channel_id, + sequence: packet.sequence, + }, + ] + ) + } + + public static updateWriteAckPacketInProgress( + packet: PacketWriteAckTable, + inProgress = true + ) { + update( + DB, + this.tableNamePacketWriteAck, + { in_progress: inProgress }, + [ + { + src_chain_id: packet.src_chain_id, + src_connection_id: packet.src_connection_id, + src_channel_id: packet.src_channel_id, + sequence: packet.sequence, + }, + ] + ) + } + + private static async feedSendPacketEvent( + lcd: LCDClient, + chainId: string, + event: SendPacketEvent + ) { + // get counterparty's info + const connection = await ConnectionController.getConnection( + lcd, + chainId, + event.packetInfo.connectionId + ) + + // add pakcet send on dst chain + const packetSend: PacketSendTable = { + dst_chain_id: connection.counterparty_chain_id, + dst_connection_id: connection.counterparty_connection_id, + dst_channel_id: event.packetInfo.dstChannel, + sequence: Number(event.packetInfo.sequence), + in_progress: false, + dst_port: event.packetInfo.dstPort, + src_chain_id: chainId, + src_connection_id: event.packetInfo.connectionId, + src_port: event.packetInfo.srcPort, + src_channel_id: event.packetInfo.srcChannel, + packet_data: event.packetInfo.data, + timeout_height: Number(event.packetInfo.timeoutHeight), + timeout_timestamp: Number(event.packetInfo.timeoutTimestamp), + timeout_height_raw: event.packetInfo.timeoutHeightRaw, + timeout_timestamp_raw: event.packetInfo.timeoutTimestampRaw, + } + + insert(DB, this.tableNamePacketSend, packetSend) + + // add packet timeout on source chain + const packetTimeout: PacketTimeoutTable = { + src_chain_id: chainId, + src_connection_id: event.packetInfo.connectionId, + src_channel_id: event.packetInfo.srcChannel, + sequence: Number(event.packetInfo.sequence), + in_progress: false, + src_port: event.packetInfo.srcPort, + dst_chain_id: connection.counterparty_chain_id, + dst_connection_id: connection.counterparty_connection_id, + dst_port: event.packetInfo.dstPort, + dst_channel_id: event.packetInfo.dstChannel, + packet_data: event.packetInfo.data, + timeout_height: Number(event.packetInfo.timeoutHeight), + timeout_timestamp: Number(event.packetInfo.timeoutTimestamp), + timeout_height_raw: event.packetInfo.timeoutHeightRaw, + timeout_timestamp_raw: event.packetInfo.timeoutTimestampRaw, + } + + insert(DB, this.tableNamePacketTimeout, packetTimeout) + } + + private static async feedWriteAckEvent( + lcd: LCDClient, + chainId: string, + event: WriteAckEvent + ) { + // get counterparty's info + const connection = await ConnectionController.getConnection( + lcd, + chainId, + event.packetInfo.connectionId + ) + + // remove pakcet send + del(DB, this.tableNamePacketSend, [ + { + dst_chain_id: chainId, + dst_connection_id: event.packetInfo.connectionId, + dst_channel_id: event.packetInfo.dstChannel, + sequence: Number(event.packetInfo.sequence), + }, + ]) + + // add packet write ack on src chain + const packetWriteAck: PacketWriteAckTable = { + src_chain_id: connection.counterparty_chain_id, + src_connection_id: connection.counterparty_connection_id, + src_channel_id: event.packetInfo.srcChannel, + sequence: Number(event.packetInfo.sequence), + in_progress: false, + src_port: event.packetInfo.srcPort, + dst_chain_id: chainId, + dst_connection_id: event.packetInfo.connectionId, + dst_port: event.packetInfo.dstPort, + dst_channel_id: event.packetInfo.dstChannel, + packet_data: event.packetInfo.data, + ack: event.packetInfo.ack as string, + timeout_height: Number(event.packetInfo.timeoutHeight), + timeout_timestamp: Number(event.packetInfo.timeoutTimestamp), + timeout_height_raw: event.packetInfo.timeoutHeightRaw, + timeout_timestamp_raw: event.packetInfo.timeoutTimestampRaw, + } + + insert(DB, this.tableNamePacketWriteAck, packetWriteAck) + } + + private static async feedAcknowledgePacketEvent( + lcd: LCDClient, + chainId: string, + event: AcknowledgePacketEvent + ) { + // get counterparty's info + const connection = await ConnectionController.getConnection( + lcd, + chainId, + event.packetInfo.connectionId + ) + + // remove pakcet send + del(DB, this.tableNamePacketSend, [ + { + dst_chain_id: connection.counterparty_chain_id, + dst_connection_id: connection.counterparty_connection_id, + dst_channel_id: event.packetInfo.dstChannel, + sequence: Number(event.packetInfo.sequence), + }, + ]) + + // remove packet timeout + del(DB, this.tableNamePacketSend, [ + { + src_chain_id: chainId, + src_connection_id: event.packetInfo.connectionId, + src_channel_id: event.packetInfo.srcChannel, + sequence: Number(event.packetInfo.sequence), + }, + ]) + + // remove packet write ack + del(DB, this.tableNamePacketSend, [ + { + src_chain_id: chainId, + src_connection_id: event.packetInfo.connectionId, + src_channel_id: event.packetInfo.srcChannel, + sequence: Number(event.packetInfo.sequence), + }, + ]) + } + + private static async feedTimeoutPacketEvent( + lcd: LCDClient, + chainId: string, + event: TimeoutPacketEvent + ) { + // get counterparty's info + const connection = await ConnectionController.getConnection( + lcd, + chainId, + event.packetInfo.connectionId + ) + + // remove pakcet send + del(DB, this.tableNamePacketSend, [ + { + dst_chain_id: connection.counterparty_chain_id, + dst_connection_id: connection.counterparty_connection_id, + dst_channel_id: event.packetInfo.dstChannel, + sequence: Number(event.packetInfo.sequence), + }, + ]) + + // remove packet timeout + del(DB, this.tableNamePacketSend, [ + { + src_chain_id: chainId, + src_connection_id: event.packetInfo.connectionId, + src_channel_id: event.packetInfo.srcChannel, + sequence: Number(event.packetInfo.sequence), + }, + ]) + } +} + +export interface PacketFilter { + connections?: { + connectionId: string + channels?: string[] // if empty search all + }[] // if empty search all +} diff --git a/src/db/controller/syncInfo.ts b/src/db/controller/syncInfo.ts new file mode 100644 index 0000000..68a2ee2 --- /dev/null +++ b/src/db/controller/syncInfo.ts @@ -0,0 +1,99 @@ +import { DB } from '..' +import { SyncInfoTable } from 'src/types' +import { del, insert, select, update } from '../utils' + +export class SyncInfoController { + private static tableName = 'sync_info' + public static init( + chainId: string, + startHeights: number[], + latestHeight: number + ): SyncInfoTable[] { + startHeights = startHeights.sort().reverse() + let syncInfos = this.getSyncInfos(chainId) + + if (syncInfos.length === 0) { + if (startHeights.length === 0) { + startHeights.push(latestHeight) + } + const startHeight = startHeights.pop() as number + + const syncInfo: SyncInfoTable = { + chain_id: chainId, + start_height: startHeight, + end_height: -1, + synced_height: startHeight - 1, + } + + insert(DB, this.tableName, syncInfo) + syncInfos.unshift(syncInfo) + } + + while (startHeights.length !== 0) { + const startHeight = startHeights.pop() as number + for (const syncInfo of syncInfos) { + if (syncInfo.start_height > startHeight) { + const newSyncInfo: SyncInfoTable = { + chain_id: chainId, + start_height: startHeight, + end_height: syncInfo.start_height - 1, + synced_height: startHeight - 1, + } + + syncInfos.unshift(newSyncInfo) + insert(DB, this.tableName, newSyncInfo) + } + + // TODO: split sync info when syncInfo.syncedHeight < startHeight < syncInfo.endHeight + break + } + } + + return syncInfos + } + + public static getSyncInfos(chainId: string): SyncInfoTable[] { + return select(DB, this.tableName, [{ chain_id: chainId }]) + } + + /** + * update `syncedHeight`. + * If `syncedHeight` reach to `endHeight`, merge syncInfo to next range and return true + * @param chainId + * @param startHeight + * @param endHeight + * @param syncedHeight + * @returns + */ + public static update( + chainId: string, + startHeight: number, + endHeight: number, + syncedHeight: number + ): boolean { + // check finish + if (syncedHeight === endHeight) { + del(DB, this.tableName, [ + { chain_id: chainId, start_height: startHeight }, + ]) + + update(DB, this.tableName, { start_height: startHeight }, [ + { + chain_id: chainId, + start_height: endHeight, + }, + ]) + + return true + } + + update(DB, this.tableName, { synced_height: syncedHeight }, [ + { + chain_id: chainId, + start_height: startHeight, + }, + ]) + + return false + } +} diff --git a/src/db/index.ts b/src/db/index.ts new file mode 100644 index 0000000..fe25871 --- /dev/null +++ b/src/db/index.ts @@ -0,0 +1,6 @@ +import DatabaseConstructor, { Database } from 'better-sqlite3' +import { DB_PATH } from './constants' +import { migrate } from './migration' + +const db = new DatabaseConstructor(DB_PATH) +export const DB: Database = migrate(db) diff --git a/src/db/migration/0.0.1.sql b/src/db/migration/0.0.1.sql new file mode 100644 index 0000000..4a0d631 --- /dev/null +++ b/src/db/migration/0.0.1.sql @@ -0,0 +1,109 @@ +-- insert version +INSERT INTO version (id, version) VALUES (1, '0.0.1'); + +-- create sync info table +CREATE TABLE sync_info ( + chain_id TEXT PRIMARY KEY NOT NULL, + start_height BIGINT PRIMARY KEY NOT NULL, + end_height BIGINT, + synced_height BIGINT +); + +-- create client table +CREATE TABLE client ( + chain_id TEXT PRIMARY KEY NOT NULL, + client_id TEXT PRIMARY KEY NOT NULL, + counterparty_chain_id TEXT NOT NULL, + counterparty_client_id TEXT NOT NULL + trusting_period BIGINT NOT NULL, + revision_height BIGINT NOT NULL, + last_update_time BIGINT NOT NULL -- in second +); + +-- create connection table +CREATE TABLE connection ( + chain_id TEXT PRIMARY KEY NOT NULL, + connection_id TEXT PRIMARY KEY NOT NULL, + client_id TEXT NOT NULL, + counterparty_chain_id TEXT NOT NULL, + counterparty_connection_id TEXT NOT NULL + counterparty_client_id TEXT NOT NULL +); + +-- create packet tables + +-- create packet send table, table for execute packet_recv +-- store send_packet event +CREATE TABLE packet_send ( + -- pk + dst_chain_id TEXT PRIMARY KEY NOT NULL, + dst_connection_id TEXT PRIMARY KEY NOT NULL, + dst_channel_id TEXT PRIMARY KEY NOT NULL, + sequence BIGINT PRIMARY KEY NOT NULL, + + -- in progress + in_progress BOOLEAN, + + -- packet data + dst_port TEXT NOT NULL, + src_chain_id TEXT NOT NULL, -- add this for filtering + src_connection_id TEXT NOT NULL, + src_port TEXT NOT NULL, + src_channel_id TEXT NOT NULL, + packet_data TEXT NOT NULL, + timeout_height BIGINT NOT NULL, + timeout_timestamp BIGINT NOT NULL, + timeout_height_raw TEXT NOT NULL, + timeout_timestamp_raw: TEXT NOT NULL +) + +-- create packet timeout table, table for execute timeout +-- store send_packet event +CREATE TABLE packet_timeout ( + -- pk + src_chain_id TEXT PRIMARY KEY NOT NULL, + src_connection_id TEXT PRIMARY KEY NOT NULL, + src_channel_id TEXT PRIMARY KEY NOT NULL, + sequence BIGINT PRIMARY KEY NOT NULL, + + -- in progress + in_progress BOOLEAN, + + -- packet data + src_port TEXT NOT NULL, + dst_chain_id TEXT NOT NULL, -- add this for filtering + dst_connection_id TEXT NOT NULL, + dst_port TEXT NOT NULL, + dst_channel_id TEXT NOT NULL, + packet_data TEXT NOT NULL, + timeout_height BIGINT NOT NULL, + timeout_timestamp BIGINT NOT NULL, + timeout_height_raw TEXT NOT NULL, + timeout_timestamp_raw: TEXT NOT NULL +) + +-- create packet write ack table, table for execute ack +-- store write_acknowledgement event +CREATE TABLE packet_write_ack ( + -- pk + src_chain_id TEXT PRIMARY KEY NOT NULL, + src_connection_id TEXT PRIMARY KEY NOT NULL, + src_channel_id TEXT PRIMARY KEY NOT NULL, + sequence BIGINT PRIMARY KEY NOT NULL, + + -- in progress + in_progress BOOLEAN, + + -- packet data + src_port TEXT NOT NULL, + dst_chain_id TEXT NOT NULL, -- add this for filtering + dst_connection_id TEXT NOT NULL, + dst_port TEXT NOT NULL, + dst_channel_id TEXT NOT NULL, + packet_data TEXT NOT NULL, + ack TEXT NOT NULL, + timeout_height BIGINT NOT NULL, + timeout_timestamp BIGINT NOT NULL, + timeout_height_raw TEXT NOT NULL, + timeout_timestamp_raw: TEXT NOT NULL +) \ No newline at end of file diff --git a/src/db/migration/index.ts b/src/db/migration/index.ts new file mode 100644 index 0000000..a50ec40 --- /dev/null +++ b/src/db/migration/index.ts @@ -0,0 +1,68 @@ +import { Database } from 'better-sqlite3' +import fs from 'fs' +import { VersionTable } from 'src/types' + +export function migrate(db: Database): Database { + // create version table + db.exec(` + CREATE TABLE IF NOT EXISTS version ( + id INT PRIMARY KEY NOT NULL, + version TEXT NOT NULL + );`) + + // get version + const versions = db + .prepare(`SELECT * FROM version WHERE id = 1`) + .all() as VersionTable[] + const version = versions.length === 0 ? '0.0.0' : versions[0].version + + // get migrations + const migrations = fs + .readdirSync('../') + .filter((f) => f.endsWith('.sql')) + .map((f) => f.replace('.sql', '')) + + // get unapplied versions + const numberizedVersion = numberize(version) + const unappliedVersions = migrations + .map(numberize) + .sort() + .filter((v) => v > numberizedVersion) + .map(versionize) + + // apply versions + for (const version of unappliedVersions) { + const sql = fs.readFileSync(`${version}.sql`).toString() + db.exec(sql) + } + + return db +} + +function numberize(version: string): number { + const ns = version.split('.').map((n) => parseInt(n)) // '1.2.3' => [1,2,3] + if (ns.length !== 3) { + throw Error('invalid version') + } + return ns.reduce((p, c) => { + if (c > 255) { + throw Error('version numbering is too big') + } + + return (p << 8) + c + }) +} + +function versionize(num: number): string { + if (num > 16777215) { + throw Error('invalid numberized version') + } + + const ns = [] + for (let i = 0; i < 3; i++) { + ns.push(num % 256) + num = num >> 8 + } + + return ns.reverse().join('.') +} diff --git a/src/db/utils.ts b/src/db/utils.ts new file mode 100644 index 0000000..71e3c34 --- /dev/null +++ b/src/db/utils.ts @@ -0,0 +1,212 @@ +import { Database } from 'better-sqlite3' +import { + PacketSendTable, + PacketTimeoutTable, + PacketWriteAckTable, +} from 'src/types' +import { Height, Packet } from '@initia/initia.js' + +export function toBE(bigint: bigint, len = 8): Uint8Array { + const result = [] + while (bigint > 0) { + result.push(Number(bigint % BigInt(256))) + bigint = bigint / BigInt(256) + } + + // fill 0 + while (result.length < len) { + result.push(0) + } + + if (result.length !== len) { + throw Error('Number is too big to convert') + } + + return new Uint8Array(result.reverse()) +} + +export function fromBE(bytes: number[] | Buffer): bigint { + bytes = [...bytes] + let result = 0n + bytes = bytes.reverse() + while (bytes.length > 0) { + result = result * 256n + BigInt(bytes.pop() as number) + } + return result +} + +// type convert +export function packetTableToPacket( + packetTable: PacketSendTable | PacketWriteAckTable | PacketTimeoutTable +): Packet { + const height = new Height( + Number(packetTable.timeout_height_raw.split('-')[0]), + Number(packetTable.timeout_height_raw.split('-')[1]) + ) + return new Packet( + packetTable.sequence, + packetTable.src_port, + packetTable.src_channel_id, + packetTable.dst_port, + packetTable.dst_channel_id, + packetTable.packet_data, + height, + packetTable.timeout_timestamp_raw + ) +} + +// sql helper + +export function insert(db: Database, tableName: string, obj: T) { + const keys = Object.keys(obj as Object) + const placeHolder = keys.map((key) => `$${key}`).join(',') + const prepare = db.prepare( + `INSERT INTO ${tableName} (${keys.join(',')}) VALUES (${placeHolder})` + ) + + prepare.run(obj) +} + +export function select( + db: Database, + tableName: string, + wheres?: WhereOptions[], + limit?: number +): T[] { + let sql = `SELECT * FROM ${tableName}` + const params = [] + const [whereSql, whereParams] = where(wheres) + sql += whereSql + params.push(...whereParams) + + if (limit) { + sql += ' limit ?' + params.push(limit) + } + + return db.prepare(sql).all(params) +} + +export function selectOne( + db: Database, + tableName: string, + wheres?: WhereOptions[] +): T | undefined { + const res = select(db, tableName, wheres, 1) + if (res.length === 0) return undefined + + return res[0] +} + +export function del( + db: Database, + tableName: string, + wheres?: WhereOptions[] +) { + let sql = `DELETE from ${tableName}` + const params = [] + const [whereSql, whereParams] = where(wheres) + sql += whereSql + params.push(...whereParams) + + db.prepare(sql).run(params) +} + +export function update( + db: Database, + tableName: string, + set: Partial, + wheres?: WhereOptions[] +) { + let sql = `UPDATE ${tableName} SET` + const params = [] + const keys = Object.keys(set as Object) + const placeHolder = keys.map((key) => ` ${key} = $${key}`).join(',') + sql += placeHolder + + const [whereSql, whereParams] = where(wheres) + sql += whereSql + params.push(...whereParams) + + db.prepare(sql).run(params, set) +} + +function where(wheres?: WhereOptions[]): [string, unknown[]] { + let sql = '' + + const conditions = [] + const params = [] + + if (wheres) { + for (const where of wheres) { + const condition = [] + const keys = Object.keys(where) as Array + for (const key of keys) { + const value = where[key] + if (typeof value === 'object' && value !== null) { + if ('in' in value) { + const vals = value.in as unknown[] + const placeHolder = vals.map((_) => '?').join(',') + condition.push(`IN(${placeHolder})`) + params.push(...vals) + } else { + const rangeConditions: string[] = [] + + // if both gt and gte are given, use gt + if ('gte' in value && !('gt' in value)) { + rangeConditions.push(`${String(key)} >= ?`) + params.push(value.gte) + } + if ('gt' in value) { + rangeConditions.push(`${String(key)} > ?`) + params.push(value.gt) + } + + // if both lt and lte are given, use lt + if ('lte' in value && !('lt' in value)) { + rangeConditions.push(`${String(key)} <= ?`) + params.push(value.lte) + } + if ('lt' in value) { + rangeConditions.push(`${String(key)} < ?`) + params.push(value.lt) + } + + condition.push(`(${rangeConditions.join(' AND ')})`) + } + } else { + condition.push(`${String(key)} = ?`) + params.push(value) + } + } + if (condition.length !== 0) { + conditions.push(`(${condition.join(' AND ')})`) + } + } + } + + if (conditions.length !== 0) { + sql += ` WHERE ${conditions.join(' OR ')}` + } + + return [sql, params] +} + +export type WhereOptions = { + [P in keyof T]?: T[P] | Range | In +} + +interface Range { + gt?: V + gte?: V + lt?: V + lte?: V +} + +interface In { + in: V[] +} + +export function In(array: T[]): In { + return { in: array } +} diff --git a/src/lib/chainPair.ts b/src/lib/chainPair.ts deleted file mode 100644 index 374e076..0000000 --- a/src/lib/chainPair.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { MnemonicKey, RawKey } from '@initia/initia.js' -import { Chain } from 'src/chain' -import { SyncInfo } from 'src/chain/types' - -export async function runPair( - config: ConfigPair -): Promise<{ name: string; chainA: Chain; chainB: Chain }> { - const keyA = - config.chainA.key.type === 'mnemonic' - ? new MnemonicKey({ mnemonic: config.chainA.key.privateKey }) - : new RawKey(Buffer.from(config.chainA.key.privateKey, 'hex')) - - const keyB = - config.chainB.key.type === 'mnemonic' - ? new MnemonicKey({ mnemonic: config.chainB.key.privateKey }) - : new RawKey(Buffer.from(config.chainB.key.privateKey, 'hex')) - - const chainA = await Chain.init({ - ...config.chainA, - key: keyA, - }) - - const chainB = await Chain.init({ - ...config.chainB, - key: keyB, - }) - - await chainA.registerCounterpartyChain(chainB) - await chainB.registerCounterpartyChain(chainA) - - return { - name: config.name ?? `${config.chainA.chainId} - ${config.chainB.chainId}`, - chainA, - chainB, - } -} - -interface ChainRawConfig { - bech32Prefix: string - chainId: string - gasPrice: string - lcdUri: string - rpcUri: string - key: { - type: 'raw' | 'mnemonic' - privateKey: string - } - connectionId: string - syncInfo?: SyncInfo // if syncInfo file exists, ignore start height -} - -export interface ConfigPair { - name?: string - chainA: ChainRawConfig - chainB: ChainRawConfig -} diff --git a/src/lib/config.ts b/src/lib/config.ts index 002c4f1..40bfc40 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -1,13 +1,50 @@ import * as fs from 'fs' -import { ConfigPair } from './chainPair' +import { env } from 'node:process' +import { PacketFilter } from 'src/db/controller/packet' export const config: Config = JSON.parse( - fs.readFileSync('./config.json').toString() + fs.readFileSync(env.CONFIGFILE || './config.json').toString() ) as Config // TODO: get path of config export interface Config { port: number metricPort: number logLevel: string - pairs: ConfigPair[] + chains: ChainConfig[] +} + +interface ChainConfig { + bech32Prefix: string + chainId: string + gasPrice: string + lcdUri: string + rpcUri: string + wallets: WalletConfig[] +} + +interface WalletConfig { + chainId: string + key: KeyConfig + maxHandlePakcet?: number // max packet amount that handle at once + packetFilter?: PacketFilter + startHeight?: number +} + +export interface KeyConfig { + type: 'raw' | 'mnemonic' | 'env_raw' | 'env_mnemonic' + privateKey: string + /** + * for mnemonic type keys only + */ + options?: { + account?: number + /** + * BIP44 index number + */ + index?: number + /** + * Coin type. Default is INIT, 118. + */ + coinType?: number + } } diff --git a/src/lib/eventParser.ts b/src/lib/eventParser.ts index f5f5876..6a06d1e 100644 --- a/src/lib/eventParser.ts +++ b/src/lib/eventParser.ts @@ -1,20 +1,10 @@ -import { Height, Packet } from '@initia/initia.js' import { Event } from '@cosmjs/tendermint-rpc/build/comet38/responses' -import { Ack } from 'src/msgs' +import { PacketInfo } from 'src/types' -export function parseSendPacketEvent( - event: Event, - connectionId: string -): Packet | undefined { - if (event.type !== 'send_packet') return - - // connection filter - if ( - event.attributes.filter((v) => v.key === 'connection_id')[0].value !== - connectionId - ) { - return - } +export function parsePacketEvent(event: Event): PacketInfo { + const connectionId = event.attributes.filter( + (v) => v.key === 'connection_id' + )[0].value const sequence = Number( event.attributes.filter((v) => v.key === 'packet_sequence')[0].value @@ -43,77 +33,22 @@ export function parseSendPacketEvent( (v) => v.key === 'packet_timeout_height' )[0].value - const timeoutHeight = new Height( - Number(timeoutHeightRaw.split('-')[0]), - Number(timeoutHeightRaw.split('-')[1]) - ) + const timeoutHeight = Number(timeoutHeightRaw.split('-')[1]) - const timeoutTimestamp = event.attributes.filter( + const timeoutTimestampRaw = event.attributes.filter( (v) => v.key === 'packet_timeout_timestamp' )[0].value + const timeoutTimestamp = Number(BigInt(timeoutTimestampRaw) / 1_000_000_000n) // store in second - return new Packet( - sequence, - srcPort, - srcChannel, - dstPort, - dstChannel, - data, - timeoutHeight, - timeoutTimestamp - ) -} - -export function parseWriteAckEvent( - event: Event, - connectionId: string -): Ack | undefined { - if (event.type !== 'write_acknowledgement') return - - // connection filter - if ( - event.attributes.filter((v) => v.key === 'connection_id')[0].value !== - connectionId - ) { - return - } - - const sequence = Number( - event.attributes.filter((v) => v.key === 'packet_sequence')[0].value - ) - - const srcPort = event.attributes.filter((v) => v.key === 'packet_src_port')[0] - .value - - const srcChannel = event.attributes.filter( - (v) => v.key === 'packet_src_channel' - )[0].value - - const dstPort = event.attributes.filter((v) => v.key === 'packet_dst_port')[0] - .value - - const dstChannel = event.attributes.filter( - (v) => v.key === 'packet_dst_channel' - )[0].value - - const data = Buffer.from( - event.attributes.filter((v) => v.key === 'packet_data')[0].value - ).toString('base64') - - const timeoutHeightRaw = event.attributes.filter( - (v) => v.key === 'packet_timeout_height' - )[0].value - - const timeoutHeight = new Height( - Number(timeoutHeightRaw.split('-')[0]), - Number(timeoutHeightRaw.split('-')[1]) - ) + const ack_hex = event.attributes.filter((v) => v.key === 'packet_ack_hex') - const timeoutTimestamp = event.attributes.filter( - (v) => v.key === 'packet_timeout_timestamp' - )[0].value + const ack = + ack_hex.length === 0 + ? undefined + : Buffer.from(ack_hex[0].value, 'hex').toString('base64') - const packet = new Packet( + return { + connectionId, sequence, srcPort, srcChannel, @@ -121,16 +56,9 @@ export function parseWriteAckEvent( dstChannel, data, timeoutHeight, - timeoutTimestamp - ) - - const acknowledgement = Buffer.from( - event.attributes.filter((v) => v.key === 'packet_ack_hex')[0].value, - 'hex' - ).toString('base64') - - return { - packet, - acknowledgement, + timeoutTimestamp, + timeoutHeightRaw, + timeoutTimestampRaw, + ack, } } diff --git a/src/lib/rawProof.ts b/src/lib/rawProof.ts index d740f62..07760c5 100644 --- a/src/lib/rawProof.ts +++ b/src/lib/rawProof.ts @@ -1,11 +1,11 @@ import { ics23 } from '@confio/ics23' import { tendermint34 } from '@cosmjs/tendermint-rpc' -import { Chain } from 'src/chain' import { Height } from 'cosmjs-types/ibc/core/client/v1/client' import { ProofOps } from 'cosmjs-types/tendermint/crypto/proof' +import { ChainWorker } from 'src/workers/chain' export async function getRawProof( - chain: Chain, + chain: ChainWorker, queryKey: Uint8Array, proofHeight: Height ): Promise { diff --git a/src/msgs/ack.ts b/src/msgs/ack.ts index 2b24509..c8396f7 100644 --- a/src/msgs/ack.ts +++ b/src/msgs/ack.ts @@ -4,41 +4,44 @@ import { Height } from 'cosmjs-types/ibc/core/client/v1/client' import { ProofOps } from 'cosmjs-types/tendermint/crypto/proof' import { CommitmentProof } from 'cosmjs-types/cosmos/ics23/v1/proofs' import { MerkleProof } from 'cosmjs-types/ibc/core/commitment/v1/commitment' -import { Chain } from 'src/chain' + import { Transfrom } from 'src/lib/transform' import { getRawProof } from 'src/lib/rawProof' +import { ChainWorker } from 'src/workers/chain' +import { PacketWriteAckTable } from 'src/types' +import { packetTableToPacket } from 'src/db/utils' export async function generateMsgAck( - srcChain: Chain, - destChain: Chain, - ack: Ack, - height: Height + dstChain: ChainWorker, + ack: PacketWriteAckTable, + height: Height, + executorAddress: string ) { - const proof = await getAckProof(destChain, ack, height) + const packet = packetTableToPacket(ack) + const proof = await getAckProof(dstChain, packet, height) const msg = new MsgAcknowledgement( - ack.packet, - ack.acknowledgement, + packet, + ack.ack, proof, Transfrom.height(height), - srcChain.wallet.address() + executorAddress ) return msg } async function getAckProof( - destChain: Chain, - ack: Ack, + dstChain: ChainWorker, + packet: Packet, headerHeight: Height ): Promise { - const packet = ack.packet const key = new Uint8Array( Buffer.from( `acks/ports/${packet.destination_port}/channels/${packet.destination_channel}/sequences/${packet.sequence}` ) ) - const proof = await getRawProof(destChain, key, headerHeight) + const proof = await getRawProof(dstChain, key, headerHeight) const ics23Proof = convertProofsToIcs23(proof) diff --git a/src/msgs/index.ts b/src/msgs/index.ts index 4db1a56..136d3f0 100644 --- a/src/msgs/index.ts +++ b/src/msgs/index.ts @@ -1,95 +1,4 @@ -import { Msg } from '@initia/initia.js' -import { - SendPacketEventWithIndex, - WriteAckEventWithIndex, -} from 'src/chain/types' -import { generateMsgUpdateClient } from './updateClient' -import { Chain } from 'src/chain' -import { generateMsgRecvPacket } from './recvPacet' -import { generateMsgAck } from './ack' -import { generateMsgTimeout } from './timeout' -import { metrics } from 'src/lib/metric' - export * from './ack' export * from './recvPacet' export * from './timeout' export * from './updateClient' - -export async function generateThisChainMessages( - thisChain: Chain, - counterpartyChain: Chain, - timeoutPackets: SendPacketEventWithIndex[] -): Promise { - const thisMsgs: Msg[] = [] - if (timeoutPackets.length !== 0) { - const { msg: msgUpdateClient, height } = await generateMsgUpdateClient( - counterpartyChain, - thisChain - ) - thisMsgs.push(msgUpdateClient) - thisChain.inc(metrics.chain.handlePacketWorker.updateClientMsg) - - const msgTimeouts = await Promise.all( - timeoutPackets.map(async (packet) => - generateMsgTimeout( - thisChain, - counterpartyChain, - packet.packetData, - height - ) - ) - ) - thisMsgs.push(...msgTimeouts) - thisChain.inc( - metrics.chain.handlePacketWorker.timeoutMsg, - msgTimeouts.length - ) - } - - return thisMsgs -} - -export async function generateCounterpartyChainMessages( - thisChain: Chain, - counterpartyChain: Chain, - recvPackets: SendPacketEventWithIndex[], - acks: WriteAckEventWithIndex[] -): Promise { - const counterpartyMsgs: Msg[] = [] - if (recvPackets.length + acks.length !== 0) { - const { msg: msgUpdateClient, height } = await generateMsgUpdateClient( - thisChain, - counterpartyChain - ) - counterpartyMsgs.push(msgUpdateClient) - counterpartyChain.inc(metrics.chain.handlePacketWorker.updateClientMsg) - - const msgRecvPackets = await Promise.all( - recvPackets.map(async (packet) => - generateMsgRecvPacket( - counterpartyChain, - thisChain, - packet.packetData, - height - ) - ) - ) - counterpartyMsgs.push(...msgRecvPackets) - counterpartyChain.inc( - metrics.chain.handlePacketWorker.recvMsg, - msgRecvPackets.length - ) - - const msgAcks = await Promise.all( - acks.map(async (ack) => - generateMsgAck(counterpartyChain, thisChain, ack.packetData, height) - ) - ) - counterpartyMsgs.push(...msgAcks) - counterpartyChain.inc( - metrics.chain.handlePacketWorker.ackMsg, - msgAcks.length - ) - } - return counterpartyMsgs -} diff --git a/src/msgs/recvPacet.ts b/src/msgs/recvPacet.ts index 8f8c858..bd58bd2 100644 --- a/src/msgs/recvPacet.ts +++ b/src/msgs/recvPacet.ts @@ -1,30 +1,33 @@ import { Packet } from '@initia/initia.js' import { MsgRecvPacket } from '@initia/initia.js/dist/core/ibc/core/channel/msgs' import { Height } from 'cosmjs-types/ibc/core/client/v1/client' -import { Chain } from 'src/chain' import { Transfrom } from 'src/lib/transform' import { getRawProof } from 'src/lib/rawProof' import { convertProofsToIcs23 } from './ack' +import { ChainWorker } from 'src/workers/chain' +import { PacketSendTable } from 'src/db/types' +import { packetTableToPacket } from 'src/db/utils' export async function generateMsgRecvPacket( - srcChain: Chain, - destChain: Chain, - packet: Packet, - height: Height + srcChain: ChainWorker, + packetSend: PacketSendTable, + height: Height, + msgExecutor: string ) { - const proof = await getPacketProof(destChain, packet, height) + const packet = packetTableToPacket(packetSend) + const proof = await getPacketProof(srcChain, packet, height) const msg = new MsgRecvPacket( packet, proof, Transfrom.height(height), - srcChain.wallet.address() + msgExecutor ) return msg } async function getPacketProof( - destChain: Chain, + dstChain: ChainWorker, packet: Packet, headerHeight: Height ): Promise { @@ -33,7 +36,7 @@ async function getPacketProof( `commitments/ports/${packet.source_port}/channels/${packet.source_channel}/sequences/${packet.sequence}` ) ) - const proof = await getRawProof(destChain, key, headerHeight) + const proof = await getRawProof(dstChain, key, headerHeight) const ics23Proof = convertProofsToIcs23(proof) diff --git a/src/msgs/signedHeader.ts b/src/msgs/signedHeader.ts index 7b2612e..888c53a 100644 --- a/src/msgs/signedHeader.ts +++ b/src/msgs/signedHeader.ts @@ -5,14 +5,14 @@ import { } from 'cosmjs-types/tendermint/types/types' import { Timestamp } from 'cosmjs-types/google/protobuf/timestamp' import { BlockIDFlag } from '@initia/initia.proto/tendermint/types/validator' -import { Chain } from 'src/chain' import { BlockIdFlag, ReadonlyDateWithNanoseconds, } from '@cosmjs/tendermint-rpc' +import { ChainWorker } from 'src/workers/chain' export async function getSignedHeader( - chain: Chain, + chain: ChainWorker, height?: number ): Promise { const commitRes = await chain.rpc.commit(height) diff --git a/src/msgs/timeout.ts b/src/msgs/timeout.ts index dab6f63..e1f779e 100644 --- a/src/msgs/timeout.ts +++ b/src/msgs/timeout.ts @@ -1,34 +1,37 @@ import { MsgTimeout } from '@initia/initia.js/dist/core/ibc/core/channel/msgs' import { Height } from 'cosmjs-types/ibc/core/client/v1/client' import { Uint64 } from '@cosmjs/math' -import { Chain } from 'src/chain' import { Transfrom } from 'src/lib/transform' import { Packet } from '@initia/initia.js' import { getRawProof } from 'src/lib/rawProof' import { convertProofsToIcs23 } from './ack' import { delay } from 'bluebird' +import { ChainWorker } from 'src/workers/chain' +import { PacketTimeoutTable } from 'src/types' +import { packetTableToPacket } from 'src/db/utils' export async function generateMsgTimeout( - srcChain: Chain, - destChain: Chain, - packet: Packet, - proofHeight: Height + dstChain: ChainWorker, + packetTable: PacketTimeoutTable, + proofHeight: Height, + executorAddress: string ): Promise { - const sequence = await getNextSequenceRecv(packet, destChain, proofHeight) - const proof = await getTimeoutProof(destChain, packet, proofHeight) + const packet = packetTableToPacket(packetTable) + const sequence = await getNextSequenceRecv(packet, dstChain, proofHeight) + const proof = await getTimeoutProof(dstChain, packet, proofHeight) return new MsgTimeout( packet, proof, Transfrom.height(proofHeight), sequence, - srcChain.wallet.address() + executorAddress ) } async function getNextSequenceRecv( packet: Packet, - destChain: Chain, + dstChain: ChainWorker, headerHeight: Height ) { const key = new Uint8Array( @@ -37,7 +40,7 @@ async function getNextSequenceRecv( ) ) - let { value } = await destChain.rpc.abciQuery({ + let { value } = await dstChain.rpc.abciQuery({ path: `/store/ibc/key`, data: key, prove: true, @@ -46,7 +49,7 @@ async function getNextSequenceRecv( let count = 0 while (value.length === 0 && count < 5) { - const result = await destChain.rpc.abciQuery({ + const result = await dstChain.rpc.abciQuery({ path: `/store/ibc/key`, data: key, prove: true, @@ -63,7 +66,7 @@ async function getNextSequenceRecv( } async function getTimeoutProof( - destChain: Chain, + dstChain: ChainWorker, packet: Packet, headerHeight: Height ): Promise { @@ -72,7 +75,7 @@ async function getTimeoutProof( `receipts/ports/${packet.destination_port}/channels/${packet.destination_channel}/sequences/${packet.sequence}` ) ) - const proof = await getRawProof(destChain, queryKey, headerHeight) + const proof = await getRawProof(dstChain, queryKey, headerHeight) const ics23Proof = convertProofsToIcs23(proof) return Buffer.from(ics23Proof).toString('base64') diff --git a/src/msgs/updateClient.ts b/src/msgs/updateClient.ts index d94826c..2d2404d 100644 --- a/src/msgs/updateClient.ts +++ b/src/msgs/updateClient.ts @@ -1,6 +1,5 @@ import { getSignedHeader } from './signedHeader' import { MsgUpdateClient } from '@initia/initia.js/dist/core/ibc/core/client/msgs' -import { Chain } from 'src/chain' import { Header } from 'cosmjs-types/ibc/lightclients/tendermint/v1/tendermint' import { ValidatorSet, @@ -8,16 +7,19 @@ import { } from 'cosmjs-types/tendermint/types/validator' import { Height } from 'cosmjs-types/ibc/core/client/v1/client' import { delay } from 'bluebird' +import { ChainWorker } from 'src/workers/chain' export async function generateMsgUpdateClient( - srcChain: Chain, - destChain: Chain + srcChain: ChainWorker, + dstChain: ChainWorker, + dstClientId: string, + executorAddress: string ): Promise<{ msg: MsgUpdateClient height: Height }> { const latestHeight = Number( - ((await destChain.lcd.ibc.clientState(destChain.clientId)) as ClientState) + ((await dstChain.lcd.ibc.clientState(dstClientId)) as ClientState) .client_state.latest_height.revision_height ) const signedHeader = await getSignedHeader(srcChain) @@ -45,11 +47,7 @@ export async function generateMsgUpdateClient( const revisionHeight = getRevisionHeight(currentHeight, header.chainId) return { - msg: new MsgUpdateClient( - destChain.clientId, - tmHeader, - destChain.wallet.address() - ), + msg: new MsgUpdateClient(dstClientId, tmHeader, executorAddress), height: revisionHeight, } } @@ -72,7 +70,7 @@ export function getRevisionHeight(height: number, chainId: string): Height { } async function getValidatorSet( - chain: Chain, + chain: ChainWorker, height: number ): Promise { let block = await chain.lcd.tendermint diff --git a/src/types/event.ts b/src/types/event.ts new file mode 100644 index 0000000..6ffa862 --- /dev/null +++ b/src/types/event.ts @@ -0,0 +1,48 @@ +export type PacketEvent = + | AcknowledgePacketEvent + | SendPacketEvent + | TimeoutPacketEvent + | WriteAckEvent + +export type PacketType = PacketEvent['type'] + +export interface AcknowledgePacketEvent { + type: 'acknowledge_packet' + packetInfo: PacketInfo +} + +export interface SendPacketEvent { + type: 'send_packet' + packetInfo: PacketInfo +} + +export interface TimeoutPacketEvent { + type: 'timeout_packet' + packetInfo: PacketInfo +} + +export interface WriteAckEvent { + type: 'write_acknowledgement' + packetInfo: PacketInfo +} + +export interface PacketInfo { + connectionId: string + sequence: number + srcPort: string + srcChannel: string + dstPort: string + dstChannel: string + data: string + timeoutHeight: number + timeoutTimestamp: number + timeoutHeightRaw: string + timeoutTimestampRaw: string + ack?: string +} + +export interface UpdateClientEvent { + clientId: string + header: string + consensusHeights: string +} diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..13bd68e --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,3 @@ +export * from './event' +export * from './response' +export * from './table' diff --git a/src/types/response.ts b/src/types/response.ts new file mode 100644 index 0000000..49eedcb --- /dev/null +++ b/src/types/response.ts @@ -0,0 +1,19 @@ +export interface ClientState { + client_state: { + chain_id: string + trusting_period: string + latest_height: { + revision_height: string + } + } +} + +export interface ConnectionInfo { + connection: { + client_id: string + counterparty: { + client_id: string + connection_id: string + } + } +} diff --git a/src/types/table.ts b/src/types/table.ts new file mode 100644 index 0000000..bf2f456 --- /dev/null +++ b/src/types/table.ts @@ -0,0 +1,84 @@ +export interface VersionTable { + id: number + version: string +} + +export interface SyncInfoTable { + chain_id: string + start_height: number + end_height: number + synced_height: number +} + +export interface ClientTable { + chain_id: string + client_id: string + counterparty_chain_id: string + trusting_period: number + revision_height: number + last_update_time: number +} + +export interface ConnectionTable { + chain_id: string + connection_id: string + client_id: string + counterparty_chain_id: string + counterparty_connection_id: string + counterparty_client_id: string +} + +export interface PacketSendTable { + dst_chain_id: string + dst_connection_id: string + dst_channel_id: string + sequence: number + in_progress: boolean + dst_port: string + src_chain_id: string + src_connection_id: string + src_port: string + src_channel_id: string + packet_data: string + timeout_height: number + timeout_timestamp: number + timeout_height_raw: string + timeout_timestamp_raw: string +} + +export interface PacketTimeoutTable { + src_chain_id: string + src_connection_id: string + src_channel_id: string + sequence: number + in_progress: boolean + src_port: string + dst_chain_id: string + dst_connection_id: string + dst_port: string + dst_channel_id: string + packet_data: string + timeout_height: number + timeout_timestamp: number + timeout_height_raw: string + timeout_timestamp_raw: string +} + +export interface PacketWriteAckTable { + src_chain_id: string + src_connection_id: string + src_channel_id: string + sequence: number + in_progress: boolean + src_port: string + dst_chain_id: string + dst_connection_id: string + dst_port: string + dst_channel_id: string + packet_data: string + ack: string + timeout_height: number + timeout_timestamp: number + timeout_height_raw: string + timeout_timestamp_raw: string +} diff --git a/src/workers/chain.ts b/src/workers/chain.ts new file mode 100644 index 0000000..8d1a1bc --- /dev/null +++ b/src/workers/chain.ts @@ -0,0 +1,226 @@ +import { LCDClient } from '@initia/initia.js' +import { RPCClient } from 'src/lib/rpcClient' +import { info, error, debug } from 'src/lib/logger' +import { PacketEvent, PacketType } from 'src/types' +import { parsePacketEvent } from 'src/lib/eventParser' +import { DB } from 'src/db' +import { SyncInfoController } from 'src/db/controller/syncInfo' +import { PacketController } from 'src/db/controller/packet' + +export class ChainWorker { + public latestHeight: number + public latestTimestamp: number + public syncWorkers: Record + + public constructor( + public chainId: string, + public lcd: LCDClient, + public rpc: RPCClient, + public bech32Prefix: string, + latestHeight: number, + startHeights: number[] + ) { + this.latestHeight = 0 + const syncInfos = SyncInfoController.init( + chainId, + startHeights, + latestHeight + ) + for (const syncInfo of syncInfos) { + this.syncWorkers[syncInfo.start_height] = new SyncWorker( + this, + syncInfo.start_height, + syncInfo.end_height, + syncInfo.synced_height + ) + this.latestHeightWorker() + } + } + + public terminateSyncWorker(startHeight: number) { + const endHeight = this.syncWorkers[startHeight].endHeight + + const nextWorker = this.syncWorkers[endHeight] + + // drop sync workers + delete this.syncWorkers[startHeight] + delete this.syncWorkers[endHeight] + + // update and store next range worker + nextWorker.startHeight = startHeight + this.syncWorkers[startHeight] = nextWorker + } + + private async latestHeightWorker() { + this.debug('Activate latest height worekr') + // TODO add websocket options + const MAX_RETRY = 10 + let retried = 0 + for (;;) { + try { + await this.updateLatestHeight() + this.debug( + `Set latest height. Height - ${this.latestHeight}, Timestamp - ${this.latestTimestamp}` + ) + + retried = 0 + } catch (e) { + this.error( + `[latestHeightWorker] Got error while fetching latest height (${e})` + ) + retried++ + if (retried >= MAX_RETRY) { + throw Error( + `<${this.chainId}> [latestHeightWorker] Max retry exceeded` + ) + } + } + } + } + + private async updateLatestHeight() { + this.latestHeight = await queryLatestHeight(this.rpc) + this.latestTimestamp = new Date().valueOf() // is it okay to use local timestamp? + + // this.inc(metrics.chain.latestHeightWorker) + } + + private info(log: string) { + info(` ${log}`) + } + + private error(log: string) { + error(` ${log}`) + } + + private debug(log: string) { + debug(` ${log}`) + } +} + +class SyncWorker { + public constructor( + public chain: ChainWorker, + public startHeight: number, + public endHeight: number, + public syncedHeight: number + ) { + this.feedEvents() + } + + private async feedEvents() { + this.debug('Activate event feeder') + + for (;;) { + try { + // height to fetch + const endHeight = + this.endHeight === -1 ? this.chain.latestHeight : this.endHeight + const heights = Array.from( + { length: 20 }, + (_, i) => i + this.syncedHeight + 1 + ).filter( + (height) => height <= endHeight && height <= this.chain.latestHeight + ) + + if (heights.length === 0) continue + + const packetEvenets = await Promise.all( + heights.map((height) => this.fetchPacketEvents(height)) + ) + + this.debug( + `Fetched block results for heights (${JSON.stringify(heights)})` + ) + + let finish = false + + await DB.transaction(async () => { + finish = SyncInfoController.update( + this.chain.chainId, + this.startHeight, + this.endHeight, + heights[heights.length - 1] + ) + + await PacketController.feedEvents( + this.chain.lcd, + this.chain.chainId, + packetEvenets.flat() + ) + + this.debug(`Store packet events(${packetEvenets.flat().length})`) + })() + + // terminate worker + if (finish) { + this.info( + 'Synced height reached to end height. Terminate sync worker' + ) + this.chain.terminateSyncWorker(this.startHeight) + break + } + } catch (e) { + this.error(`Fail to fecth block result. resonse - ${e}`) + } + } + } + + private async fetchPacketEvents(height: number): Promise { + this.debug(`Fecth new block results (height - ${height})`) + const blockResult = await this.chain.rpc.blockResults(height) + const txData = [...blockResult.results] + + const packetEvents: PacketEvent[] = [] + + txData.map((data, i) => { + for (const event of data.events) { + if ( + event.type === 'send_packet' || + event.type === 'write_acknowledgement' || + event.type === 'acknowledge_packet' || + event.type === 'timeout_packet' + ) { + let packetInfo = parsePacketEvent(event) + if (packetInfo) { + packetEvents.push({ + type: event.type as PacketType, + packetInfo, + }) + } + } + } + }) + + return packetEvents + } + + // logs + + private info(log: string) { + info( + ` ${log}` + ) + } + + private error(log: string) { + error( + ` ${log}` + ) + } + + private debug(log: string) { + debug( + ` ${log}` + ) + } +} + +export async function queryLatestHeight(rpc: RPCClient): Promise { + const abciInfo = await rpc.abciInfo() + if (!abciInfo.lastBlockHeight) { + throw Error('Can not get last block height') + } + const height = abciInfo.lastBlockHeight + return Number(height) +} diff --git a/src/workers/index.ts b/src/workers/index.ts new file mode 100644 index 0000000..24b749e --- /dev/null +++ b/src/workers/index.ts @@ -0,0 +1,180 @@ +import { ChainWorker, queryLatestHeight } from './chain' +import { WalletWorker } from './wallet' +import { + generateMsgAck, + generateMsgRecvPacket, + generateMsgTimeout, + generateMsgUpdateClient, +} from 'src/msgs' +import { Height } from 'cosmjs-types/ibc/core/client/v1/client' +import { MsgUpdateClient } from '@initia/initia.js/dist/core/ibc/core/client/msgs' +import { ClientController } from 'src/db/controller/client' +import { + PacketSendTable, + PacketTimeoutTable, + PacketWriteAckTable, +} from 'src/types' +import { + MsgRecvPacket, + Key, + MnemonicKey, + RawKey, + LCDClient, + APIRequester, + Wallet, +} from '@initia/initia.js' +import { Config, KeyConfig } from 'src/lib/config' +import { env } from 'node:process' +import { RPCClient } from 'src/lib/rpcClient' +import * as http from 'http' +import * as https from 'https' + +export class WorkerController { + public chains: Record // chainId => ChainWorker + public wallets: Record // chainId::addr => WalletWorker + public initiated: boolean + + public constructor() { + this.chains = {} + this.wallets = {} + this.initiated = false + } + + public async init(config: Config) { + if (this.initiated) { + throw Error('already initiated') + } + this.initiated = true + + for (const chainConfig of config.chains) { + const lcd = new LCDClient( + chainConfig.lcdUri, + { + chainId: chainConfig.chainId, + gasPrices: chainConfig.gasPrice, + }, + new APIRequester(chainConfig.lcdUri, { + httpAgent: new http.Agent({ keepAlive: true }), + httpsAgent: new https.Agent({ keepAlive: true }), + timeout: 60000, + }) + ) + const rpc = new RPCClient(chainConfig.rpcUri) + const latestHeight = await queryLatestHeight(rpc) + const chain = new ChainWorker( + chainConfig.chainId, + lcd, + rpc, + chainConfig.bech32Prefix, + latestHeight, + chainConfig.wallets + .map((wallet) => wallet.startHeight) + .filter((v) => v !== undefined) as number[] + ) + + this.chains[chainConfig.chainId] = chain + + for (const walletConfig of chainConfig.wallets) { + const key = createKey(walletConfig.key) + const wallet = new WalletWorker( + chain, + this, + walletConfig.maxHandlePakcet ?? 100, + new Wallet(lcd, key), + walletConfig.packetFilter + ) + + this.wallets[`${chainConfig.chainId}::${wallet.address()}`] + } + } + } + + public getChainIds(): string[] { + return Object.keys(this.chains) + } + + async generateMsgUpdateClient( + chainId: string, + clientId: string, + executorAddress: string + ): Promise<{ + msg: MsgUpdateClient + height: Height + }> { + // get client + const client = await ClientController.getClient( + this.chains[chainId].lcd, + chainId, + clientId + ) + + return generateMsgUpdateClient( + this.chains[chainId], + this.chains[client.counterparty_chain_id], + client.counterparty_chain_id, + executorAddress + ) + } + + async generateRecvPacketMsg( + packet: PacketSendTable, + height: Height, + executorAddress: string + ): Promise { + const srcChain = this.chains[packet.src_chain_id] + return generateMsgRecvPacket(srcChain, packet, height, executorAddress) + } + + async generateAckMsg( + packet: PacketWriteAckTable, + height: Height, + executorAddress: string + ) { + const dstChain = this.chains[packet.dst_chain_id] + return generateMsgAck(dstChain, packet, height, executorAddress) + } + + async generateTimeoutMsg( + packet: PacketTimeoutTable, + height: Height, + executorAddress: string + ) { + const dstChain = this.chains[packet.dst_chain_id] + return generateMsgTimeout(dstChain, packet, height, executorAddress) + } +} + +function createKey(rawKey: KeyConfig): Key { + let keyReturn + switch (rawKey.type) { + case 'mnemonic': { + const options = rawKey.options || {} + + keyReturn = new MnemonicKey({ mnemonic: rawKey.privateKey, ...options }) + break + } + case 'env_mnemonic': { + const key = env[rawKey.privateKey] + if (!key) { + throw Error(`missing environment ${rawKey.privateKey}`) + } + const options = rawKey.options || {} + + keyReturn = new MnemonicKey({ mnemonic: key, ...options }) + break + } + case 'raw': { + keyReturn = new RawKey(Buffer.from(rawKey.privateKey, 'hex')) + break + } + case 'env_raw': { + const key = env[rawKey.privateKey] + if (!key) { + throw Error(`missing environment ${rawKey.privateKey}`) + } + keyReturn = new RawKey(Buffer.from(key, 'hex')) + break + } + } + return keyReturn +} diff --git a/src/workers/wallet.ts b/src/workers/wallet.ts new file mode 100644 index 0000000..9850917 --- /dev/null +++ b/src/workers/wallet.ts @@ -0,0 +1,413 @@ +import { PacketController, PacketFilter } from 'src/db/controller/packet' +import { ChainWorker } from './chain' +import { + PacketSendTable, + PacketTimeoutTable, + PacketWriteAckTable, +} from 'src/types' +import { WorkerController } from '.' +import { DB } from 'src/db' +import { MsgUpdateClient } from '@initia/initia.js/dist/core/ibc/core/client/msgs' +import { Height } from 'cosmjs-types/ibc/core/client/v1/client' +import { ConnectionController } from 'src/db/controller/connection' +import { Wallet, isTxError } from '@initia/initia.js' +import { debug, error, info, warn } from 'src/lib/logger' +import { bech32 } from 'bech32' +import { delay } from 'bluebird' + +// TODO: add update client worker +export class WalletWorker { + private sequence?: number + + constructor( + public chain: ChainWorker, + public workerController: WorkerController, + private maxHandlePacket: number, + private wallet: Wallet, + private packetFilter?: PacketFilter + ) { + this.run() + } + + public async run() { + for (;;) { + try { + await this.handlePackets() + } catch (e) { + this.error(`[run] ${e}`) + } + await delay(500) + } + } + + private async handlePackets() { + // get packets to handle + let remain = this.maxHandlePacket + + const counterpartyChainIds = this.workerController + .getChainIds() + .filter((v) => v !== this.chain.chainId) + + const sendPakcets = PacketController.getSendPackets( + this.chain.chainId, + counterpartyChainIds, + this.packetFilter, + remain + ) + + remain -= sendPakcets.length + + const writeAckPackets = + remain === 0 + ? [] + : PacketController.getWriteAckPackets( + this.chain.chainId, + counterpartyChainIds, + this.packetFilter, + remain + ) + + remain -= writeAckPackets.length + + const timeoutPackets = + remain === 0 + ? [] + : PacketController.getTimeoutPackets( + this.chain.chainId, + this.chain.latestHeight, + Number((this.chain.latestTimestamp / 1000).toFixed()), + counterpartyChainIds, + this.packetFilter, + remain + ) + + // update packet in progress + DB.transaction(() => { + sendPakcets.map((packet) => + PacketController.updateSendPacketInProgress(packet) + ) + writeAckPackets.map((packet) => + PacketController.updateWriteAckPacketInProgress(packet) + ) + timeoutPackets.map((packet) => + PacketController.updateTimeoutPacketInProgress(packet) + ) + })() + + try { + // filter packets + const filteredSendPackets = await this.filterSendPackets(sendPakcets) + const filteredWriteAckPackets = + await this.filterWriteAckPackets(writeAckPackets) + const filteredTimeoutPackets = + await this.filterTimeoutPackets(timeoutPackets) + + if ( + filteredSendPackets.length === 0 && + filteredWriteAckPackets.length === 0 && + filteredTimeoutPackets.length === 0 + ) { + return + } + + // create msgs + + // generate update client msgs + // get unique client id + const connections = [ + ...filteredSendPackets.map((packet) => packet.src_connection_id), + ...filteredWriteAckPackets.map((packet) => packet.dst_connection_id), + ...filteredTimeoutPackets.map((packet) => packet.dst_connection_id), + ].filter((v, i, a) => a.indexOf(v) === i) // filter by connection first + + const connectionClientMap: Record = {} + await Promise.all( + connections.map(async (connection) => { + const connectionInfo = await ConnectionController.getConnection( + this.chain.lcd, + this.chain.chainId, + connection + ) + connectionClientMap[connection] = connectionInfo.client_id + }) + ) + + const clientIds = Object.values(connectionClientMap).filter( + (v, i, a) => a.indexOf(v) === i + ) + + // generate msgs + const updateClientMsgs: Record< + string, + { msg: MsgUpdateClient; height: Height } + > = {} + + await Promise.all( + clientIds.map(async (clientId) => { + updateClientMsgs[clientId] = + await this.workerController.generateMsgUpdateClient( + this.chain.chainId, + clientId, + this.address() + ) + }) + ) + + // generate recv packet msgs + const recvPacketMsgs = await Promise.all( + filteredSendPackets.map((packet) => { + const clientId = connectionClientMap[packet.dst_connection_id] + const height = updateClientMsgs[clientId].height + + return this.workerController.generateRecvPacketMsg( + packet, + height, + this.address() + ) + }) + ) + + // generate ack msgs + const ackMsgs = await Promise.all( + filteredWriteAckPackets.map((packet) => { + const clientId = connectionClientMap[packet.src_connection_id] + const height = updateClientMsgs[clientId].height + + return this.workerController.generateAckMsg( + packet, + height, + this.address() + ) + }) + ) + + // generate timeout msgs + const timeoutMsgs = await Promise.all( + filteredTimeoutPackets.map((packet) => { + const clientId = connectionClientMap[packet.src_connection_id] + const height = updateClientMsgs[clientId].height + + return this.workerController.generateTimeoutMsg( + packet, + height, + this.address() + ) + }) + ) + + const msgs = [ + ...Object.values(updateClientMsgs).map((v) => v.msg), + ...recvPacketMsgs, + ...ackMsgs, + ...timeoutMsgs, + ] + + const signedTx = await this.wallet.createAndSignTx({ + msgs, + sequence: this.sequence, + }) + + // update sequence + if (!this.sequence) { + this.sequence = signedTx.auth_info.signer_infos[0].sequence + } + + const result = await this.wallet.lcd.tx.broadcast(signedTx) + + if (isTxError(result)) { + if (result.raw_log.startsWith('account sequence mismatch')) { + try { + const expected = result.raw_log.split(', ')[1] + this.sequence = Number(expected.split(' ')[1]) + this.info(`update sequence`) + } catch (e) { + this.warn(`error to parse sequence`) + } + } + + this.error( + `Tx failed. raw log - ${result.raw_log}, code - ${result.code}` + ) + throw Error( + `Tx failed. raw log - ${result.raw_log}, code - ${result.code}` + ) + } + + this.sequence++ + } catch { + // revert packet in progress + DB.transaction(() => { + sendPakcets.map((packet) => + PacketController.updateSendPacketInProgress(packet, false) + ) + writeAckPackets.map((packet) => + PacketController.updateWriteAckPacketInProgress(packet, false) + ) + timeoutPackets.map((packet) => + PacketController.updateTimeoutPacketInProgress(packet, false) + ) + })() + } + } + + public address(): string { + const address = this.wallet.key.accAddress + return bech32.encode(this.chain.bech32Prefix, bech32.decode(address).words) + } + + private async filterSendPackets( + sendPackets: PacketSendTable[] + ): Promise { + // create path => packet map + const sendPacketMap: Record = {} + + for (const packet of sendPackets) { + const path = `${packet.dst_port}/${packet.dst_channel_id}` + if (!sendPacketMap[path]) { + sendPacketMap[path] = [] + } + + sendPacketMap[path].push(packet) + } + + // filter send packet + await Promise.all( + Object.keys(sendPacketMap).map(async (path) => { + if (sendPacketMap[path].length === 0) return + const unrecivedPackets = await this.chain.lcd.ibc.unreceivedPackets( + sendPacketMap[path][0].dst_port, + sendPacketMap[path][0].dst_channel_id, + sendPacketMap[path].map((packet) => packet.sequence) + ) + + const unrecivedSequences = unrecivedPackets.sequences.map((sequence) => + Number(sequence) + ) + + sendPacketMap[path] = sendPacketMap[path].filter((packet) => + unrecivedSequences.includes(packet.sequence) + ) + }) + ) + + return Object.values(sendPacketMap).flat() + } + + private async filterWriteAckPackets( + writeAckPackets: PacketWriteAckTable[] + ): Promise { + // create path => packet map + const writeAckPacketMap: Record = {} + + for (const packet of writeAckPackets) { + const path = `${packet.src_port}/${packet.src_port}` + if (!writeAckPacketMap[path]) { + writeAckPacketMap[path] = [] + } + + writeAckPacketMap[path].push(packet) + } + + // filter write ack packet + await Promise.all( + Object.keys(writeAckPacketMap).map(async (path) => { + if (writeAckPacketMap[path].length === 0) return + const unrecivedAcks = await this.chain.lcd.ibc.unreceivedAcks( + writeAckPacketMap[path][0].src_port, + writeAckPacketMap[path][0].src_channel_id, + writeAckPacketMap[path].map((packet) => packet.sequence) + ) + + const unrecivedSequences = unrecivedAcks.sequences.map((sequence) => + Number(sequence) + ) + + writeAckPacketMap[path] = writeAckPacketMap[path].filter((packet) => + unrecivedSequences.includes(packet.sequence) + ) + }) + ) + + return Object.values(writeAckPacketMap).flat() + } + + private async filterTimeoutPackets( + timeoutPackets: PacketTimeoutTable[] + ): Promise { + // create path => packet map + const timeoutPacketMap: Record = {} + + for (const packet of timeoutPackets) { + const path = `${packet.src_port}/${packet.src_port}` + if (!timeoutPacketMap[path]) { + timeoutPacketMap[path] = [] + } + + timeoutPacketMap[path].push(packet) + } + + // filter timeout packet + + // check unreceived ack + await Promise.all( + Object.keys(timeoutPacketMap).map(async (path) => { + if (timeoutPacketMap[path].length === 0) return + const unrecivedAcks = await this.chain.lcd.ibc.unreceivedAcks( + timeoutPacketMap[path][0].src_port, + timeoutPacketMap[path][0].src_channel_id, + timeoutPacketMap[path].map((packet) => packet.sequence) + ) + + const unrecivedSequences = unrecivedAcks.sequences.map((sequence) => + Number(sequence) + ) + + timeoutPacketMap[path] = timeoutPacketMap[path].filter((packet) => + unrecivedSequences.includes(packet.sequence) + ) + }) + ) + + // check unreceived packet + await Promise.all( + Object.keys(timeoutPacketMap).map(async (path) => { + if (timeoutPacketMap[path].length === 0) return + const chain = + this.workerController.chains[timeoutPacketMap[path][0].dst_chain_id] + const unrecivedPackets = await chain.lcd.ibc.unreceivedPackets( + timeoutPacketMap[path][0].dst_port, + timeoutPacketMap[path][0].dst_channel_id, + timeoutPacketMap[path].map((packet) => packet.sequence) + ) + + const unrecivedSequences = unrecivedPackets.sequences.map((sequence) => + Number(sequence) + ) + + timeoutPacketMap[path] = timeoutPacketMap[path].filter((packet) => + unrecivedSequences.includes(packet.sequence) + ) + }) + ) + + return Object.values(timeoutPacketMap).flat() + } + + // logs + + private info(log: string) { + info(` ${log}`) + } + + private warn(log: string) { + warn(` ${log}`) + } + + private error(log: string) { + error(` ${log}`) + } + + private debug(log: string) { + debug(` ${log}`) + } +} From 8f18b49ca4ace7b84985165b9f176d1146348b25 Mon Sep 17 00:00:00 2001 From: ALPAC-4 Date: Fri, 13 Sep 2024 21:35:18 +0900 Subject: [PATCH 02/30] delete unuse file --- src/lib/chainPair.ts | 101 ------------------------------------------- 1 file changed, 101 deletions(-) delete mode 100644 src/lib/chainPair.ts diff --git a/src/lib/chainPair.ts b/src/lib/chainPair.ts deleted file mode 100644 index d07ee59..0000000 --- a/src/lib/chainPair.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { Key, MnemonicKey, RawKey } from '@initia/initia.js' -import { Chain } from 'src/chain' -import { SyncInfo } from 'src/chain/types' -import { env } from 'node:process' - -export async function runPair( - config: ConfigPair -): Promise<{ name: string; chainA: Chain; chainB: Chain }> { - const keyA = createKey(config.chainA.key) - const keyB = createKey(config.chainB.key) - - const chainA = await Chain.init({ - ...config.chainA, - key: keyA, - }) - - const chainB = await Chain.init({ - ...config.chainB, - key: keyB, - }) - - await chainA.registerCounterpartyChain(chainB) - await chainB.registerCounterpartyChain(chainA) - - return { - name: config.name ?? `${config.chainA.chainId} - ${config.chainB.chainId}`, - chainA, - chainB, - } -} - -function createKey(rawKey: ChainRawKeyConfig): Key { - let keyReturn - switch (rawKey.type) { - case 'mnemonic': { - const options = rawKey.options || {} - - keyReturn = new MnemonicKey({ mnemonic: rawKey.privateKey, ...options }) - break - } - case 'env_mnemonic': { - const key = env[rawKey.privateKey] - if (!key) { - throw Error(`missing environment ${rawKey.privateKey}`) - } - const options = rawKey.options || {} - - keyReturn = new MnemonicKey({ mnemonic: key, ...options }) - break - } - case 'raw': { - keyReturn = new RawKey(Buffer.from(rawKey.privateKey, 'hex')) - break - } - case 'env_raw': { - const key = env[rawKey.privateKey] - if (!key) { - throw Error(`missing environment ${rawKey.privateKey}`) - } - keyReturn = new RawKey(Buffer.from(key, 'hex')) - break - } - } - return keyReturn -} - -interface ChainRawKeyConfig { - type: 'raw' | 'mnemonic' | 'env_raw' | 'env_mnemonic' - privateKey: string - /** - * for mnemonic type keys only - */ - options?: { - account?: number - /** - * BIP44 index number - */ - index?: number - /** - * Coin type. Default is INIT, 118. - */ - coinType?: number - } -} - -interface ChainRawConfig { - bech32Prefix: string - chainId: string - gasPrice: string - lcdUri: string - rpcUri: string - key: ChainRawKeyConfig - connectionId: string - syncInfo?: SyncInfo // if syncInfo file exists, ignore start height -} - -export interface ConfigPair { - name?: string - chainA: ChainRawConfig - chainB: ChainRawConfig -} From c77f51939593e57233a31f1aee0ade6be9dda7de Mon Sep 17 00:00:00 2001 From: ALPAC-4 Date: Fri, 13 Sep 2024 21:40:09 +0900 Subject: [PATCH 03/30] fix main --- src/index.ts | 84 +++++++++++++++++++++++++--------------------------- 1 file changed, 41 insertions(+), 43 deletions(-) diff --git a/src/index.ts b/src/index.ts index 6febb20..cd6927e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,49 +1,47 @@ -import { Chain } from './chain' -import express from 'express' -import { runPair } from './lib/chainPair' +// import { Chain } from './chain' +// import express from 'express' +// import { runPair } from './lib/chainPair' +// import { config } from './lib/config' +// import { ChainStatus } from './chain/types' +// import { registery } from './lib/metric' +// import { info } from './lib/logger' + import { config } from './lib/config' -import { ChainStatus } from './chain/types' -import { registery } from './lib/metric' -import { info } from './lib/logger' +import { WorkerController } from './workers' async function main() { - const pairs: Record = {} - await Promise.all( - config.pairs.map(async (config) => { - const pair = await runPair(config) - pairs[pair.name] = { chainA: pair.chainA, chainB: pair.chainB } - }) - ) - - const app = express() - - app.get('/status', (req, res) => { - const result: Record = - {} - Object.keys(pairs).map((name) => { - result[name] = { - chainA: pairs[name].chainA.chainStatus(), - chainB: pairs[name].chainB.chainStatus(), - } - }) - - res.json(result) - }) - - const metricApp = express() - - metricApp.get('/metrics', (req, res) => { - res.setHeader('content-type', registery.contentType) - registery - .metrics() - .then((response) => res.send(response)) - .catch(() => res.status(500).send('Fail to get metrics')) - }) - - app.listen(config.port) - info(`status app listen to port ${config.port}`) - metricApp.listen(config.metricPort) - info(`metric app listen to port ${config.metricPort}`) + const workerController = new WorkerController() + await workerController.init(config) + + // const app = express() + + // app.get('/status', (req, res) => { + // const result: Record = + // {} + // Object.keys(pairs).map((name) => { + // result[name] = { + // chainA: pairs[name].chainA.chainStatus(), + // chainB: pairs[name].chainB.chainStatus(), + // } + // }) + + // res.json(result) + // }) + + // const metricApp = express() + + // metricApp.get('/metrics', (req, res) => { + // res.setHeader('content-type', registery.contentType) + // registery + // .metrics() + // .then((response) => res.send(response)) + // .catch(() => res.status(500).send('Fail to get metrics')) + // }) + + // app.listen(config.port) + // info(`status app listen to port ${config.port}`) + // metricApp.listen(config.metricPort) + // info(`metric app listen to port ${config.metricPort}`) } void main() From 9e58402d46ebb94150d2270c029dc09125d98ec5 Mon Sep 17 00:00:00 2001 From: ALPAC-4 Date: Fri, 13 Sep 2024 21:41:36 +0900 Subject: [PATCH 04/30] fix conflicts --- src/chain/index.ts | 0 src/db/controller/packet.ts | 10 ++++------ src/msgs/recvPacet.ts | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) delete mode 100644 src/chain/index.ts diff --git a/src/chain/index.ts b/src/chain/index.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/db/controller/packet.ts b/src/db/controller/packet.ts index 3421c38..53bc2e6 100644 --- a/src/db/controller/packet.ts +++ b/src/db/controller/packet.ts @@ -4,16 +4,14 @@ import { SendPacketEvent, TimeoutPacketEvent, WriteAckEvent, -} from 'src/chain/types' + PacketSendTable, + PacketTimeoutTable, + PacketWriteAckTable, +} from 'src/types' import { DB } from '..' import { In, WhereOptions, del, insert, select, update } from '../utils' import { LCDClient } from '@initia/initia.js' import { ConnectionController } from './connection' -import { - PacketSendTable, - PacketTimeoutTable, - PacketWriteAckTable, -} from '../types' export class PacketController { private static tableNamePacketSend = 'packet_send' diff --git a/src/msgs/recvPacet.ts b/src/msgs/recvPacet.ts index bd58bd2..06c2c03 100644 --- a/src/msgs/recvPacet.ts +++ b/src/msgs/recvPacet.ts @@ -5,7 +5,7 @@ import { Transfrom } from 'src/lib/transform' import { getRawProof } from 'src/lib/rawProof' import { convertProofsToIcs23 } from './ack' import { ChainWorker } from 'src/workers/chain' -import { PacketSendTable } from 'src/db/types' +import { PacketSendTable } from 'src/types' import { packetTableToPacket } from 'src/db/utils' export async function generateMsgRecvPacket( From 1f4b299f4c0ad176d4a586717c4e248b8b1d032b Mon Sep 17 00:00:00 2001 From: ALPAC-4 Date: Thu, 26 Sep 2024 14:11:01 +0900 Subject: [PATCH 05/30] fix: fix db type, transactoin --- .db/main.db | Bin 0 -> 61440 bytes package-lock.json | 159 +++++++++++++++++++++---------- src/db/controller/packet.ts | 184 +++++++++++++++++++----------------- src/db/index.ts | 9 ++ src/db/migration/0.0.1.sql | 66 +++++++------ src/db/migration/index.ts | 10 +- src/db/utils.ts | 38 +++++--- src/index.ts | 1 - src/lib/config.ts | 1 - src/lib/eventParser.ts | 16 ++-- src/types/event.ts | 2 +- src/types/table.ts | 11 ++- src/workers/chain.ts | 28 ++++-- 13 files changed, 321 insertions(+), 204 deletions(-) create mode 100644 .db/main.db diff --git a/.db/main.db b/.db/main.db new file mode 100644 index 0000000000000000000000000000000000000000..67fbdfbe728a9b81783e9934f38e2ce2176c47bc GIT binary patch literal 61440 zcmeI5Pi))P9mh#gmTbo{+pUQbWLg+xLSs0}NXm*G3_)ktvSTljYgdUP<{-!tZP6Af zS)yZGm!WZTX-~axJM6IA+F>Ym888eOuwj3Z(igC6M zag8rwMUQ;H_wV!L@A19&dsN+Wt~e=uTV5ieU;-VDt()xD3*NeBT52c z`-FVcuJC{$$>T<6=PdgVYCLp~VprLpk1w-d9Q*Tl{7il9d&AW;>7nfK>!EW)V*@h4 z<(EO=seU#ZOD5^}F6rgEt`#&?$?59 zTOZUygG0Gm<@B1;Hmr`@Q0lteQnXDoS2N}F+HMFvr|1AKmVS(e_|={0*YC2fsB&Xh zFV$pav!)+qYN*(jF}~Y+S#1W3b+an9%6mZtI+^(in#|r>TguL^6}hivirh8#&2^nK z&)hhzOU{i$&aK;IVszq-@ks3LBuyz=Ro-v!)Xm#{S8tf-rT+W6B=lclpBBzUV-pkf zyNcUxEG(9^bvEW_Ggcfd#@#2Jc#E;LcB)O|D`Sz^%EWU_!vKGMyLy#= zx8gR7wyaexn=pc-egFCQ%`unfT&VeWKACw+1S;{ zIHXx_=?8U_dY81&3y(Zb4@F}a zFVaskUGQc-x;P?U;AYHmUkkNNmnP}|t zW%}`s3AufsRZ5Ds(QpD`4{GP~F=&&)WGi^+Y1o>Fk2RGbm5_a72DIK?5&(a4lmsU@^oWkeE?uJEdDEouNNyRrv%W$e0@g*V{TxSTL&iMCT{G{*h~w-eo>`Nr z&52|v5}Um=sH#@Nl3)cGGOZjorj}>Phi3mykv|+D00JNY0w4eaAOHd&00JNY0w4ea zr;5NZ&5XqO8~hDnl(dUx|3#5M93TJ!AOHd&00JNY0w4eaAOHd&00O6w0K<%o82*2T z{Q<@Pm3>COaDV^^fB*=900@8p2!H?xfB*=900^8I0vDK}1Vb}zy`jl_<+hp>rm3I2 z_3x1U&kLbmjM{i4+cBH(y+#kk?KIX^`r$7P|3Ab2lw$wJ{*HX%009sH0T2KI5C8!X z009sH0T2KI5IC^}UJH}ufWFlLrgyQo5D;R+ac0Dn?5+VA{{JvLL$SYMzrk*i01glU z0T2KI5C8!X009sH0T2KI5C8#(03VJfCgThv2{u2S)X6Uq$}LsV^dxUjvty5c zOnv-qo^hwJy!HVp{_u4^6pc?N>@vuF-A~#oI{K@R=^y=qrvp<6?j-^v#SeMI{~uG%a00ck)1V8`;KmY_l00ck)1V8`;K;Y9Qz=ac&UH85JLPEGF;J~~p-jgkNk5C8!X009sH0T2KI5C8!X009s< zAp};5@0~c`6O(S=d+)z{O!D42uH%Of5F!6}p({&kk{uv#yY^iog11y*m3Z$HlO$Ik zS>GC3bAMNs{bH-cy+0_+{^H%P{(vmI{{QzW_SfuR*!S5FPl$?94G;hU5C8!X009sH z0T2KI5C8!XINb!|;Y7l-7r;Gv5BFky)+m^Z;kcES>;iD73tb>F&K3YyjOqWUUZJS* z&9P^v8!TD}0w4eaAOHd&00JNY0w8eO2ppaZy&6wk9vO}&67kDVGQ;7|r~DkSm0DTT z?zXX{oQquCvnT| ztTL0zbr!aBTiLXR+DbNMyZxRzC=tzu_Ml(x3_bA_Ui%V#>p zPBB%?uhp{Z>RwUJ?o0EtX9;fCTq=IdA{B;^Ahjxl6oI3Z8el~A>FLb zFSMntxlT1DiRC-novOA_t3FsGWea*$BeJTnmtCwLNZRs_lxp5w*EZ9YgXLQrpX~gR zPx8XFI4!35sZ{#rl*k*$;d$Cn3w><0FgADS77009sH0T2KI5CDOfNZ{~%_|*h)n~lWdUT>z4qp~-q^SI9X zMu&EsBMo^WusL+3@`w(smpab1Xme{`Y(SOksK^Jw4O)XBz&0Vlg8x zWHB~-=;i7E#{Q$R<$OnxjIQFN`(b=Wqv`;7S?J~A=Ak& zuI`=GeHojfkBVJ;nKAZd%-ot5yZbV%-~WF|u^*CO|9{B-_9Z^-P&5dD00@8p2!H?x zfB*=900@8p2z(X^Ohk;O+kx-$+Y1*w6F#nFh`DCuY5Dzw)__B=n}S{Ef6x+(;r|bf zeUlnrA^+e20T2KI5C8!X009sH0T2Lzmqp<4%c0M?x4+zW-sTUQ`*>d+$Nrc9{&jxh zQa_L$i_Qodz_r=_x4UIY5FM1januOZEe4qUn@bkaO?=K8e ySg`*;#C|}rAHetjvbs5x3j!bj0w4eaAOHd&00JNY0w4eaUING6D8L-s|NkGj0%ySh literal 0 HcmV?d00001 diff --git a/package-lock.json b/package-lock.json index 52829c9..4878bb8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1300,9 +1300,10 @@ "license": "MIT" }, "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "license": "MIT", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", @@ -1312,7 +1313,7 @@ "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.11.0", + "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" @@ -1413,6 +1414,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -1421,6 +1423,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -1540,6 +1543,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -1608,6 +1612,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -1615,7 +1620,8 @@ "node_modules/debug/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/decompress-response": { "version": "6.0.0", @@ -1686,6 +1692,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -1694,6 +1701,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", "engines": { "node": ">= 0.8", "npm": "1.2.8000 || >= 1.4.16" @@ -1741,10 +1749,13 @@ "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" }, "node_modules/elliptic": { - "version": "6.5.5", + "version": "6.5.7", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.7.tgz", + "integrity": "sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==", "license": "MIT", "dependencies": { "bn.js": "^4.11.9", @@ -1765,9 +1776,10 @@ "license": "MIT" }, "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -1800,7 +1812,8 @@ "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" }, "node_modules/escape-string-regexp": { "version": "4.0.0", @@ -2025,6 +2038,7 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -2046,36 +2060,37 @@ } }, "node_modules/express": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", + "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.2", + "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", + "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", + "path-to-regexp": "0.1.10", "proxy-addr": "~2.0.7", - "qs": "6.11.0", + "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", + "send": "0.19.0", + "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", @@ -2181,12 +2196,13 @@ } }, "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "license": "MIT", "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", @@ -2279,6 +2295,7 @@ "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -2511,6 +2528,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", @@ -2545,6 +2563,7 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -2866,14 +2885,19 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/merge2": { "version": "1.4.1", @@ -2893,10 +2917,11 @@ } }, "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -2909,6 +2934,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", "bin": { "mime": "cli.js" }, @@ -3031,9 +3057,13 @@ } }, "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -3049,6 +3079,7 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", "dependencies": { "ee-first": "1.1.1" }, @@ -3134,6 +3165,7 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -3166,9 +3198,10 @@ } }, "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", + "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", + "license": "MIT" }, "node_modules/path-type": { "version": "4.0.0", @@ -3322,11 +3355,12 @@ } }, "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -3359,6 +3393,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -3367,6 +3402,7 @@ "version": "2.5.2", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "license": "MIT", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", @@ -3518,7 +3554,8 @@ "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" }, "node_modules/secp256k1": { "version": "5.0.0", @@ -3551,9 +3588,10 @@ } }, "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", @@ -3573,15 +3611,25 @@ "node": ">= 0.8.0" } }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", "dependencies": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.18.0" + "send": "0.19.0" }, "engines": { "node": ">= 0.8.0" @@ -3591,6 +3639,7 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -3606,7 +3655,8 @@ "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" }, "node_modules/sha.js": { "version": "2.4.11", @@ -3645,6 +3695,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "es-errors": "^1.3.0", @@ -3728,6 +3779,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -3894,6 +3946,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", "engines": { "node": ">=0.6" } @@ -4014,6 +4067,7 @@ "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" @@ -4073,6 +4127,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } diff --git a/src/db/controller/packet.ts b/src/db/controller/packet.ts index 53bc2e6..228c174 100644 --- a/src/db/controller/packet.ts +++ b/src/db/controller/packet.ts @@ -7,6 +7,7 @@ import { PacketSendTable, PacketTimeoutTable, PacketWriteAckTable, + Boolean, } from 'src/types' import { DB } from '..' import { In, WhereOptions, del, insert, select, update } from '../utils' @@ -22,23 +23,32 @@ export class PacketController { lcd: LCDClient, chainId: string, events: PacketEvent[] - ) { + ): Promise<() => void> { + const feedFns: (() => void)[] = [] for (const event of events) { switch (event.type) { case 'send_packet': - await this.feedSendPacketEvent(lcd, chainId, event) + feedFns.push(await this.feedSendPacketEvent(lcd, chainId, event)) break case 'write_acknowledgement': - await this.feedWriteAckEvent(lcd, chainId, event) + feedFns.push(await this.feedWriteAckEvent(lcd, chainId, event)) break case 'acknowledge_packet': - await this.feedAcknowledgePacketEvent(lcd, chainId, event) + feedFns.push( + await this.feedAcknowledgePacketEvent(lcd, chainId, event) + ) break case 'timeout_packet': - await this.feedTimeoutPacketEvent(lcd, chainId, event) + feedFns.push(await this.feedTimeoutPacketEvent(lcd, chainId, event)) break } } + + return () => { + for (const fn of feedFns) { + fn() + } + } } public static getSendPackets( @@ -51,7 +61,7 @@ export class PacketController { if (filter.connections) { wheres.push( ...filter.connections.map((conn) => ({ - in_progress: false, + in_progress: Boolean.FALSE, dst_chain_id: chainId, dst_connection_id: conn.connectionId, dst_channel_id: conn.channels ? In(conn.channels) : undefined, @@ -60,7 +70,7 @@ export class PacketController { ) } else { wheres.push({ - in_progress: false, + in_progress: Boolean.TRUE, dst_chain_id: chainId, src_chain_id: In(counterpartyChainIds), }) @@ -82,7 +92,7 @@ export class PacketController { if (filter.connections) { wheres.push( ...filter.connections.map((conn) => ({ - in_progress: false, + in_progress: Boolean.FALSE, src_chain_id: chainId, src_connection_id: conn.connectionId, src_channel_id: conn.channels ? In(conn.channels) : undefined, @@ -93,7 +103,7 @@ export class PacketController { ) } else { wheres.push({ - in_progress: false, + in_progress: Boolean.FALSE, src_chain_id: chainId, dst_chain_id: In(counterpartyChainIds), }) @@ -118,7 +128,7 @@ export class PacketController { if (filter.connections) { wheres.push( ...filter.connections.map((conn) => ({ - in_progress: false, + in_progress: Boolean.FALSE, src_chain_id: chainId, src_connection_id: conn.connectionId, src_channel_id: conn.channels ? In(conn.channels) : undefined, @@ -127,7 +137,7 @@ export class PacketController { ) } else { wheres.push({ - in_progress: false, + in_progress: Boolean.FALSE, src_chain_id: chainId, dst_chain_id: In(counterpartyChainIds), }) @@ -148,7 +158,7 @@ export class PacketController { update( DB, this.tableNamePacketSend, - { in_progress: inProgress }, + { in_progress: inProgress ? Boolean.TRUE : Boolean.FALSE }, [ { dst_chain_id: packet.dst_chain_id, @@ -167,7 +177,7 @@ export class PacketController { update( DB, this.tableNamePacketTimeout, - { in_progress: inProgress }, + { in_progress: inProgress ? Boolean.TRUE : Boolean.FALSE }, [ { src_chain_id: packet.src_chain_id, @@ -186,7 +196,7 @@ export class PacketController { update( DB, this.tableNamePacketWriteAck, - { in_progress: inProgress }, + { in_progress: inProgress ? Boolean.TRUE : Boolean.FALSE }, [ { src_chain_id: packet.src_chain_id, @@ -202,7 +212,7 @@ export class PacketController { lcd: LCDClient, chainId: string, event: SendPacketEvent - ) { + ): Promise<() => void> { // get counterparty's info const connection = await ConnectionController.getConnection( lcd, @@ -216,48 +226,49 @@ export class PacketController { dst_connection_id: connection.counterparty_connection_id, dst_channel_id: event.packetInfo.dstChannel, sequence: Number(event.packetInfo.sequence), - in_progress: false, + in_progress: Boolean.FALSE, dst_port: event.packetInfo.dstPort, src_chain_id: chainId, src_connection_id: event.packetInfo.connectionId, src_port: event.packetInfo.srcPort, src_channel_id: event.packetInfo.srcChannel, - packet_data: event.packetInfo.data, + packet_data: event.packetInfo.data as string, timeout_height: Number(event.packetInfo.timeoutHeight), timeout_timestamp: Number(event.packetInfo.timeoutTimestamp), timeout_height_raw: event.packetInfo.timeoutHeightRaw, timeout_timestamp_raw: event.packetInfo.timeoutTimestampRaw, } - insert(DB, this.tableNamePacketSend, packetSend) - // add packet timeout on source chain const packetTimeout: PacketTimeoutTable = { src_chain_id: chainId, src_connection_id: event.packetInfo.connectionId, src_channel_id: event.packetInfo.srcChannel, sequence: Number(event.packetInfo.sequence), - in_progress: false, + in_progress: Boolean.FALSE, src_port: event.packetInfo.srcPort, dst_chain_id: connection.counterparty_chain_id, dst_connection_id: connection.counterparty_connection_id, dst_port: event.packetInfo.dstPort, dst_channel_id: event.packetInfo.dstChannel, - packet_data: event.packetInfo.data, + packet_data: event.packetInfo.data as string, timeout_height: Number(event.packetInfo.timeoutHeight), timeout_timestamp: Number(event.packetInfo.timeoutTimestamp), timeout_height_raw: event.packetInfo.timeoutHeightRaw, timeout_timestamp_raw: event.packetInfo.timeoutTimestampRaw, } - insert(DB, this.tableNamePacketTimeout, packetTimeout) + return () => { + insert(DB, this.tableNamePacketSend, packetSend) + insert(DB, this.tableNamePacketTimeout, packetTimeout) + } } private static async feedWriteAckEvent( lcd: LCDClient, chainId: string, event: WriteAckEvent - ) { + ): Promise<() => void> { // get counterparty's info const connection = await ConnectionController.getConnection( lcd, @@ -265,44 +276,44 @@ export class PacketController { event.packetInfo.connectionId ) - // remove pakcet send - del(DB, this.tableNamePacketSend, [ - { - dst_chain_id: chainId, - dst_connection_id: event.packetInfo.connectionId, - dst_channel_id: event.packetInfo.dstChannel, - sequence: Number(event.packetInfo.sequence), - }, - ]) - // add packet write ack on src chain const packetWriteAck: PacketWriteAckTable = { src_chain_id: connection.counterparty_chain_id, src_connection_id: connection.counterparty_connection_id, src_channel_id: event.packetInfo.srcChannel, sequence: Number(event.packetInfo.sequence), - in_progress: false, + in_progress: Boolean.FALSE, src_port: event.packetInfo.srcPort, dst_chain_id: chainId, dst_connection_id: event.packetInfo.connectionId, dst_port: event.packetInfo.dstPort, dst_channel_id: event.packetInfo.dstChannel, - packet_data: event.packetInfo.data, + packet_data: event.packetInfo.data as string, ack: event.packetInfo.ack as string, timeout_height: Number(event.packetInfo.timeoutHeight), timeout_timestamp: Number(event.packetInfo.timeoutTimestamp), timeout_height_raw: event.packetInfo.timeoutHeightRaw, timeout_timestamp_raw: event.packetInfo.timeoutTimestampRaw, } - - insert(DB, this.tableNamePacketWriteAck, packetWriteAck) + return () => { + // remove pakcet send + del(DB, this.tableNamePacketSend, [ + { + dst_chain_id: chainId, + dst_connection_id: event.packetInfo.connectionId, + dst_channel_id: event.packetInfo.dstChannel, + sequence: Number(event.packetInfo.sequence), + }, + ]) + insert(DB, this.tableNamePacketWriteAck, packetWriteAck) + } } private static async feedAcknowledgePacketEvent( lcd: LCDClient, chainId: string, event: AcknowledgePacketEvent - ) { + ): Promise<() => void> { // get counterparty's info const connection = await ConnectionController.getConnection( lcd, @@ -310,68 +321,71 @@ export class PacketController { event.packetInfo.connectionId ) - // remove pakcet send - del(DB, this.tableNamePacketSend, [ - { - dst_chain_id: connection.counterparty_chain_id, - dst_connection_id: connection.counterparty_connection_id, - dst_channel_id: event.packetInfo.dstChannel, - sequence: Number(event.packetInfo.sequence), - }, - ]) + return () => { + // remove pakcet send + del(DB, this.tableNamePacketSend, [ + { + dst_chain_id: connection.counterparty_chain_id, + dst_connection_id: connection.counterparty_connection_id, + dst_channel_id: event.packetInfo.dstChannel, + sequence: Number(event.packetInfo.sequence), + }, + ]) - // remove packet timeout - del(DB, this.tableNamePacketSend, [ - { - src_chain_id: chainId, - src_connection_id: event.packetInfo.connectionId, - src_channel_id: event.packetInfo.srcChannel, - sequence: Number(event.packetInfo.sequence), - }, - ]) + // remove packet timeout + del(DB, this.tableNamePacketSend, [ + { + src_chain_id: chainId, + src_connection_id: event.packetInfo.connectionId, + src_channel_id: event.packetInfo.srcChannel, + sequence: Number(event.packetInfo.sequence), + }, + ]) - // remove packet write ack - del(DB, this.tableNamePacketSend, [ - { - src_chain_id: chainId, - src_connection_id: event.packetInfo.connectionId, - src_channel_id: event.packetInfo.srcChannel, - sequence: Number(event.packetInfo.sequence), - }, - ]) + // remove packet write ack + del(DB, this.tableNamePacketSend, [ + { + src_chain_id: chainId, + src_connection_id: event.packetInfo.connectionId, + src_channel_id: event.packetInfo.srcChannel, + sequence: Number(event.packetInfo.sequence), + }, + ]) + } } private static async feedTimeoutPacketEvent( lcd: LCDClient, chainId: string, event: TimeoutPacketEvent - ) { + ): Promise<() => void> { // get counterparty's info const connection = await ConnectionController.getConnection( lcd, chainId, event.packetInfo.connectionId ) + return () => { + // remove pakcet send + del(DB, this.tableNamePacketSend, [ + { + dst_chain_id: connection.counterparty_chain_id, + dst_connection_id: connection.counterparty_connection_id, + dst_channel_id: event.packetInfo.dstChannel, + sequence: Number(event.packetInfo.sequence), + }, + ]) - // remove pakcet send - del(DB, this.tableNamePacketSend, [ - { - dst_chain_id: connection.counterparty_chain_id, - dst_connection_id: connection.counterparty_connection_id, - dst_channel_id: event.packetInfo.dstChannel, - sequence: Number(event.packetInfo.sequence), - }, - ]) - - // remove packet timeout - del(DB, this.tableNamePacketSend, [ - { - src_chain_id: chainId, - src_connection_id: event.packetInfo.connectionId, - src_channel_id: event.packetInfo.srcChannel, - sequence: Number(event.packetInfo.sequence), - }, - ]) + // remove packet timeout + del(DB, this.tableNamePacketSend, [ + { + src_chain_id: chainId, + src_connection_id: event.packetInfo.connectionId, + src_channel_id: event.packetInfo.srcChannel, + sequence: Number(event.packetInfo.sequence), + }, + ]) + } } } diff --git a/src/db/index.ts b/src/db/index.ts index fe25871..43c3188 100644 --- a/src/db/index.ts +++ b/src/db/index.ts @@ -1,6 +1,15 @@ import DatabaseConstructor, { Database } from 'better-sqlite3' import { DB_PATH } from './constants' import { migrate } from './migration' +import * as fs from 'fs' +if (!fs.existsSync('./.db')) { + fs.mkdirSync('./.db') +} const db = new DatabaseConstructor(DB_PATH) export const DB: Database = migrate(db) + +process.on('SIGINT', () => { + DB.close() + process.exit(0) +}) diff --git a/src/db/migration/0.0.1.sql b/src/db/migration/0.0.1.sql index 4a0d631..ac56f5b 100644 --- a/src/db/migration/0.0.1.sql +++ b/src/db/migration/0.0.1.sql @@ -3,31 +3,33 @@ INSERT INTO version (id, version) VALUES (1, '0.0.1'); -- create sync info table CREATE TABLE sync_info ( - chain_id TEXT PRIMARY KEY NOT NULL, - start_height BIGINT PRIMARY KEY NOT NULL, + chain_id TEXT NOT NULL, + start_height BIGINT NOT NULL, end_height BIGINT, - synced_height BIGINT + synced_height BIGINT, + PRIMARY KEY (chain_id, start_height) ); -- create client table CREATE TABLE client ( - chain_id TEXT PRIMARY KEY NOT NULL, - client_id TEXT PRIMARY KEY NOT NULL, + chain_id TEXT NOT NULL, + client_id TEXT NOT NULL, counterparty_chain_id TEXT NOT NULL, - counterparty_client_id TEXT NOT NULL trusting_period BIGINT NOT NULL, revision_height BIGINT NOT NULL, - last_update_time BIGINT NOT NULL -- in second + last_update_time BIGINT NOT NULL, -- in second + PRIMARY KEY (chain_id, client_id) ); -- create connection table CREATE TABLE connection ( - chain_id TEXT PRIMARY KEY NOT NULL, - connection_id TEXT PRIMARY KEY NOT NULL, + chain_id TEXT NOT NULL, + connection_id TEXT NOT NULL, client_id TEXT NOT NULL, counterparty_chain_id TEXT NOT NULL, - counterparty_connection_id TEXT NOT NULL - counterparty_client_id TEXT NOT NULL + counterparty_connection_id TEXT NOT NULL, + counterparty_client_id TEXT NOT NULL, + PRIMARY KEY (chain_id, connection_id) ); -- create packet tables @@ -36,10 +38,10 @@ CREATE TABLE connection ( -- store send_packet event CREATE TABLE packet_send ( -- pk - dst_chain_id TEXT PRIMARY KEY NOT NULL, - dst_connection_id TEXT PRIMARY KEY NOT NULL, - dst_channel_id TEXT PRIMARY KEY NOT NULL, - sequence BIGINT PRIMARY KEY NOT NULL, + dst_chain_id TEXT NOT NULL, + dst_connection_id TEXT NOT NULL, + dst_channel_id TEXT NOT NULL, + sequence BIGINT NOT NULL, -- in progress in_progress BOOLEAN, @@ -54,17 +56,19 @@ CREATE TABLE packet_send ( timeout_height BIGINT NOT NULL, timeout_timestamp BIGINT NOT NULL, timeout_height_raw TEXT NOT NULL, - timeout_timestamp_raw: TEXT NOT NULL -) + timeout_timestamp_raw TEXT NOT NULL, + + PRIMARY KEY (dst_chain_id, dst_connection_id, dst_channel_id, sequence) +); -- create packet timeout table, table for execute timeout -- store send_packet event CREATE TABLE packet_timeout ( -- pk - src_chain_id TEXT PRIMARY KEY NOT NULL, - src_connection_id TEXT PRIMARY KEY NOT NULL, - src_channel_id TEXT PRIMARY KEY NOT NULL, - sequence BIGINT PRIMARY KEY NOT NULL, + src_chain_id TEXT NOT NULL, + src_connection_id TEXT NOT NULL, + src_channel_id TEXT NOT NULL, + sequence BIGINT NOT NULL, -- in progress in_progress BOOLEAN, @@ -79,17 +83,19 @@ CREATE TABLE packet_timeout ( timeout_height BIGINT NOT NULL, timeout_timestamp BIGINT NOT NULL, timeout_height_raw TEXT NOT NULL, - timeout_timestamp_raw: TEXT NOT NULL -) + timeout_timestamp_raw TEXT NOT NULL, + + PRIMARY KEY (src_chain_id, src_connection_id, src_channel_id, sequence) +); -- create packet write ack table, table for execute ack -- store write_acknowledgement event CREATE TABLE packet_write_ack ( -- pk - src_chain_id TEXT PRIMARY KEY NOT NULL, - src_connection_id TEXT PRIMARY KEY NOT NULL, - src_channel_id TEXT PRIMARY KEY NOT NULL, - sequence BIGINT PRIMARY KEY NOT NULL, + src_chain_id TEXT NOT NULL, + src_connection_id TEXT NOT NULL, + src_channel_id TEXT NOT NULL, + sequence BIGINT NOT NULL, -- in progress in_progress BOOLEAN, @@ -105,5 +111,7 @@ CREATE TABLE packet_write_ack ( timeout_height BIGINT NOT NULL, timeout_timestamp BIGINT NOT NULL, timeout_height_raw TEXT NOT NULL, - timeout_timestamp_raw: TEXT NOT NULL -) \ No newline at end of file + timeout_timestamp_raw TEXT NOT NULL, + + PRIMARY KEY (src_chain_id, src_connection_id, src_channel_id, sequence) +); \ No newline at end of file diff --git a/src/db/migration/index.ts b/src/db/migration/index.ts index a50ec40..baee56b 100644 --- a/src/db/migration/index.ts +++ b/src/db/migration/index.ts @@ -18,12 +18,13 @@ export function migrate(db: Database): Database { // get migrations const migrations = fs - .readdirSync('../') + .readdirSync('./src/db/migration') .filter((f) => f.endsWith('.sql')) .map((f) => f.replace('.sql', '')) // get unapplied versions const numberizedVersion = numberize(version) + const unappliedVersions = migrations .map(numberize) .sort() @@ -32,10 +33,11 @@ export function migrate(db: Database): Database { // apply versions for (const version of unappliedVersions) { - const sql = fs.readFileSync(`${version}.sql`).toString() - db.exec(sql) + const sql = fs.readFileSync(`./src/db/migration/${version}.sql`).toString() + db.transaction(() => { + db.exec(sql) + })() } - return db } diff --git a/src/db/utils.ts b/src/db/utils.ts index 71e3c34..a38910c 100644 --- a/src/db/utils.ts +++ b/src/db/utils.ts @@ -74,7 +74,7 @@ export function select( limit?: number ): T[] { let sql = `SELECT * FROM ${tableName}` - const params = [] + const params: ParamType[] = [] const [whereSql, whereParams] = where(wheres) sql += whereSql params.push(...whereParams) @@ -104,7 +104,7 @@ export function del( wheres?: WhereOptions[] ) { let sql = `DELETE from ${tableName}` - const params = [] + const params: ParamType[] = [] const [whereSql, whereParams] = where(wheres) sql += whereSql params.push(...whereParams) @@ -131,11 +131,11 @@ export function update( db.prepare(sql).run(params, set) } -function where(wheres?: WhereOptions[]): [string, unknown[]] { +function where(wheres?: WhereOptions[]): [string, ParamType[]] { let sql = '' const conditions = [] - const params = [] + const params: ParamType[] = [] if (wheres) { for (const where of wheres) { @@ -147,36 +147,36 @@ function where(wheres?: WhereOptions[]): [string, unknown[]] { if ('in' in value) { const vals = value.in as unknown[] const placeHolder = vals.map((_) => '?').join(',') - condition.push(`IN(${placeHolder})`) - params.push(...vals) + condition.push(`${String(key)} IN(${placeHolder})`) + params.push(...vals.map(toParamType)) } else { const rangeConditions: string[] = [] // if both gt and gte are given, use gt if ('gte' in value && !('gt' in value)) { rangeConditions.push(`${String(key)} >= ?`) - params.push(value.gte) + params.push(toParamType(value.gte)) } if ('gt' in value) { rangeConditions.push(`${String(key)} > ?`) - params.push(value.gt) + params.push(toParamType(value.gt)) } // if both lt and lte are given, use lt if ('lte' in value && !('lt' in value)) { rangeConditions.push(`${String(key)} <= ?`) - params.push(value.lte) + params.push(toParamType(value.lte)) } if ('lt' in value) { rangeConditions.push(`${String(key)} < ?`) - params.push(value.lt) + params.push(toParamType(value.lt)) } condition.push(`(${rangeConditions.join(' AND ')})`) } } else { condition.push(`${String(key)} = ?`) - params.push(value) + params.push(toParamType(value)) } } if (condition.length !== 0) { @@ -210,3 +210,19 @@ interface In { export function In(array: T[]): In { return { in: array } } + +type ParamType = number | string | bigint | Buffer | null + +function toParamType(p: T): ParamType { + if ( + typeof p === 'number' || + typeof p === 'string' || + typeof p === 'bigint' || + p === null || + Buffer.isBuffer(p) + ) { + return p as number | string | bigint | Buffer | null + } + + return String(p) +} diff --git a/src/index.ts b/src/index.ts index cd6927e..5301936 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,7 +12,6 @@ import { WorkerController } from './workers' async function main() { const workerController = new WorkerController() await workerController.init(config) - // const app = express() // app.get('/status', (req, res) => { diff --git a/src/lib/config.ts b/src/lib/config.ts index 40bfc40..c4090ba 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -23,7 +23,6 @@ interface ChainConfig { } interface WalletConfig { - chainId: string key: KeyConfig maxHandlePakcet?: number // max packet amount that handle at once packetFilter?: PacketFilter diff --git a/src/lib/eventParser.ts b/src/lib/eventParser.ts index 6a06d1e..4729015 100644 --- a/src/lib/eventParser.ts +++ b/src/lib/eventParser.ts @@ -24,10 +24,12 @@ export function parsePacketEvent(event: Event): PacketInfo { (v) => v.key === 'packet_dst_channel' )[0].value - const data = Buffer.from( - event.attributes.filter((v) => v.key === 'packet_data_hex')[0].value, - 'hex' - ).toString('base64') + const dataHex = event.attributes.filter((v) => v.key === 'packet_data_hex') + + const data = + dataHex.length === 0 + ? undefined + : Buffer.from(dataHex[0].value, 'hex').toString('base64') const timeoutHeightRaw = event.attributes.filter( (v) => v.key === 'packet_timeout_height' @@ -40,12 +42,12 @@ export function parsePacketEvent(event: Event): PacketInfo { )[0].value const timeoutTimestamp = Number(BigInt(timeoutTimestampRaw) / 1_000_000_000n) // store in second - const ack_hex = event.attributes.filter((v) => v.key === 'packet_ack_hex') + const ackHex = event.attributes.filter((v) => v.key === 'packet_ack_hex') const ack = - ack_hex.length === 0 + ackHex.length === 0 ? undefined - : Buffer.from(ack_hex[0].value, 'hex').toString('base64') + : Buffer.from(ackHex[0].value, 'hex').toString('base64') return { connectionId, diff --git a/src/types/event.ts b/src/types/event.ts index 6ffa862..2ad5a09 100644 --- a/src/types/event.ts +++ b/src/types/event.ts @@ -33,7 +33,7 @@ export interface PacketInfo { srcChannel: string dstPort: string dstChannel: string - data: string + data?: string timeoutHeight: number timeoutTimestamp: number timeoutHeightRaw: string diff --git a/src/types/table.ts b/src/types/table.ts index bf2f456..0dd181d 100644 --- a/src/types/table.ts +++ b/src/types/table.ts @@ -33,7 +33,7 @@ export interface PacketSendTable { dst_connection_id: string dst_channel_id: string sequence: number - in_progress: boolean + in_progress: Boolean dst_port: string src_chain_id: string src_connection_id: string @@ -51,7 +51,7 @@ export interface PacketTimeoutTable { src_connection_id: string src_channel_id: string sequence: number - in_progress: boolean + in_progress: Boolean src_port: string dst_chain_id: string dst_connection_id: string @@ -69,7 +69,7 @@ export interface PacketWriteAckTable { src_connection_id: string src_channel_id: string sequence: number - in_progress: boolean + in_progress: Boolean src_port: string dst_chain_id: string dst_connection_id: string @@ -82,3 +82,8 @@ export interface PacketWriteAckTable { timeout_height_raw: string timeout_timestamp_raw: string } + +export enum Boolean { + TRUE = 1, + FALSE = 0, +} diff --git a/src/workers/chain.ts b/src/workers/chain.ts index 8d1a1bc..2adb8c3 100644 --- a/src/workers/chain.ts +++ b/src/workers/chain.ts @@ -6,6 +6,7 @@ import { parsePacketEvent } from 'src/lib/eventParser' import { DB } from 'src/db' import { SyncInfoController } from 'src/db/controller/syncInfo' import { PacketController } from 'src/db/controller/packet' +import { delay } from 'bluebird' export class ChainWorker { public latestHeight: number @@ -26,6 +27,7 @@ export class ChainWorker { startHeights, latestHeight ) + this.syncWorkers = {} for (const syncInfo of syncInfos) { this.syncWorkers[syncInfo.start_height] = new SyncWorker( this, @@ -135,7 +137,15 @@ class SyncWorker { let finish = false - await DB.transaction(async () => { + const feed = await PacketController.feedEvents( + this.chain.lcd, + this.chain.chainId, + packetEvenets.flat() + ) + + DB.transaction(() => { + feed() + finish = SyncInfoController.update( this.chain.chainId, this.startHeight, @@ -143,15 +153,11 @@ class SyncWorker { heights[heights.length - 1] ) - await PacketController.feedEvents( - this.chain.lcd, - this.chain.chainId, - packetEvenets.flat() - ) - this.debug(`Store packet events(${packetEvenets.flat().length})`) })() + this.syncedHeight = heights[heights.length - 1] + // terminate worker if (finish) { this.info( @@ -162,6 +168,8 @@ class SyncWorker { } } catch (e) { this.error(`Fail to fecth block result. resonse - ${e}`) + } finally { + await delay(500) } } } @@ -199,19 +207,19 @@ class SyncWorker { private info(log: string) { info( - ` ${log}` + ` ${log}` ) } private error(log: string) { error( - ` ${log}` + ` ${log}` ) } private debug(log: string) { debug( - ` ${log}` + ` ${log}` ) } } From 3adcc8836849a85b57cf68071c9152f069748414 Mon Sep 17 00:00:00 2001 From: ALPAC-4 Date: Thu, 26 Sep 2024 14:13:55 +0900 Subject: [PATCH 06/30] remove db folder --- .db/main.db | Bin 61440 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .db/main.db diff --git a/.db/main.db b/.db/main.db deleted file mode 100644 index 67fbdfbe728a9b81783e9934f38e2ce2176c47bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61440 zcmeI5Pi))P9mh#gmTbo{+pUQbWLg+xLSs0}NXm*G3_)ktvSTljYgdUP<{-!tZP6Af zS)yZGm!WZTX-~axJM6IA+F>Ym888eOuwj3Z(igC6M zag8rwMUQ;H_wV!L@A19&dsN+Wt~e=uTV5ieU;-VDt()xD3*NeBT52c z`-FVcuJC{$$>T<6=PdgVYCLp~VprLpk1w-d9Q*Tl{7il9d&AW;>7nfK>!EW)V*@h4 z<(EO=seU#ZOD5^}F6rgEt`#&?$?59 zTOZUygG0Gm<@B1;Hmr`@Q0lteQnXDoS2N}F+HMFvr|1AKmVS(e_|={0*YC2fsB&Xh zFV$pav!)+qYN*(jF}~Y+S#1W3b+an9%6mZtI+^(in#|r>TguL^6}hivirh8#&2^nK z&)hhzOU{i$&aK;IVszq-@ks3LBuyz=Ro-v!)Xm#{S8tf-rT+W6B=lclpBBzUV-pkf zyNcUxEG(9^bvEW_Ggcfd#@#2Jc#E;LcB)O|D`Sz^%EWU_!vKGMyLy#= zx8gR7wyaexn=pc-egFCQ%`unfT&VeWKACw+1S;{ zIHXx_=?8U_dY81&3y(Zb4@F}a zFVaskUGQc-x;P?U;AYHmUkkNNmnP}|t zW%}`s3AufsRZ5Ds(QpD`4{GP~F=&&)WGi^+Y1o>Fk2RGbm5_a72DIK?5&(a4lmsU@^oWkeE?uJEdDEouNNyRrv%W$e0@g*V{TxSTL&iMCT{G{*h~w-eo>`Nr z&52|v5}Um=sH#@Nl3)cGGOZjorj}>Phi3mykv|+D00JNY0w4eaAOHd&00JNY0w4ea zr;5NZ&5XqO8~hDnl(dUx|3#5M93TJ!AOHd&00JNY0w4eaAOHd&00O6w0K<%o82*2T z{Q<@Pm3>COaDV^^fB*=900@8p2!H?xfB*=900^8I0vDK}1Vb}zy`jl_<+hp>rm3I2 z_3x1U&kLbmjM{i4+cBH(y+#kk?KIX^`r$7P|3Ab2lw$wJ{*HX%009sH0T2KI5C8!X z009sH0T2KI5IC^}UJH}ufWFlLrgyQo5D;R+ac0Dn?5+VA{{JvLL$SYMzrk*i01glU z0T2KI5C8!X009sH0T2KI5C8#(03VJfCgThv2{u2S)X6Uq$}LsV^dxUjvty5c zOnv-qo^hwJy!HVp{_u4^6pc?N>@vuF-A~#oI{K@R=^y=qrvp<6?j-^v#SeMI{~uG%a00ck)1V8`;KmY_l00ck)1V8`;K;Y9Qz=ac&UH85JLPEGF;J~~p-jgkNk5C8!X009sH0T2KI5C8!X009s< zAp};5@0~c`6O(S=d+)z{O!D42uH%Of5F!6}p({&kk{uv#yY^iog11y*m3Z$HlO$Ik zS>GC3bAMNs{bH-cy+0_+{^H%P{(vmI{{QzW_SfuR*!S5FPl$?94G;hU5C8!X009sH z0T2KI5C8!XINb!|;Y7l-7r;Gv5BFky)+m^Z;kcES>;iD73tb>F&K3YyjOqWUUZJS* z&9P^v8!TD}0w4eaAOHd&00JNY0w8eO2ppaZy&6wk9vO}&67kDVGQ;7|r~DkSm0DTT z?zXX{oQquCvnT| ztTL0zbr!aBTiLXR+DbNMyZxRzC=tzu_Ml(x3_bA_Ui%V#>p zPBB%?uhp{Z>RwUJ?o0EtX9;fCTq=IdA{B;^Ahjxl6oI3Z8el~A>FLb zFSMntxlT1DiRC-novOA_t3FsGWea*$BeJTnmtCwLNZRs_lxp5w*EZ9YgXLQrpX~gR zPx8XFI4!35sZ{#rl*k*$;d$Cn3w><0FgADS77009sH0T2KI5CDOfNZ{~%_|*h)n~lWdUT>z4qp~-q^SI9X zMu&EsBMo^WusL+3@`w(smpab1Xme{`Y(SOksK^Jw4O)XBz&0Vlg8x zWHB~-=;i7E#{Q$R<$OnxjIQFN`(b=Wqv`;7S?J~A=Ak& zuI`=GeHojfkBVJ;nKAZd%-ot5yZbV%-~WF|u^*CO|9{B-_9Z^-P&5dD00@8p2!H?x zfB*=900@8p2z(X^Ohk;O+kx-$+Y1*w6F#nFh`DCuY5Dzw)__B=n}S{Ef6x+(;r|bf zeUlnrA^+e20T2KI5C8!X009sH0T2Lzmqp<4%c0M?x4+zW-sTUQ`*>d+$Nrc9{&jxh zQa_L$i_Qodz_r=_x4UIY5FM1januOZEe4qUn@bkaO?=K8e ySg`*;#C|}rAHetjvbs5x3j!bj0w4eaAOHd&00JNY0w4eaUING6D8L-s|NkGj0%ySh From 5ea1a381ad72617e3f55cd2e19c0e56d153e8dc2 Mon Sep 17 00:00:00 2001 From: ALPAC-4 Date: Thu, 26 Sep 2024 14:17:36 +0900 Subject: [PATCH 07/30] add gitignore deafult db path --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 2f54cb9..9772291 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,6 @@ node_modules/ config.json -/.idea \ No newline at end of file +/.idea + +/.db \ No newline at end of file From a73a5bfff263599d6e900c40d66aaa5101735fcd Mon Sep 17 00:00:00 2001 From: ALPAC-4 Date: Thu, 26 Sep 2024 18:03:13 +0900 Subject: [PATCH 08/30] add status query --- src/db/controller/packet.ts | 26 ++++++++++++--- src/db/migration/index.ts | 4 +++ src/db/utils.ts | 4 ++- src/index.ts | 25 +++++--------- src/workers/index.ts | 65 +++++++++++++++++++++++++++++++++++-- src/workers/wallet.ts | 15 ++++++--- 6 files changed, 109 insertions(+), 30 deletions(-) diff --git a/src/db/controller/packet.ts b/src/db/controller/packet.ts index 228c174..f33b9a3 100644 --- a/src/db/controller/packet.ts +++ b/src/db/controller/packet.ts @@ -13,6 +13,7 @@ import { DB } from '..' import { In, WhereOptions, del, insert, select, update } from '../utils' import { LCDClient } from '@initia/initia.js' import { ConnectionController } from './connection' +import { Database } from 'better-sqlite3' export class PacketController { private static tableNamePacketSend = 'packet_send' @@ -53,6 +54,8 @@ export class PacketController { public static getSendPackets( chainId: string, + height: number, + timestamp: number, counterpartyChainIds: string[], filter: PacketFilter = {}, limit = 100 @@ -66,13 +69,15 @@ export class PacketController { dst_connection_id: conn.connectionId, dst_channel_id: conn.channels ? In(conn.channels) : undefined, src_chain_id: In(counterpartyChainIds), // TODO: make this more efficientnet, like filter it on outside of this. + custom: `(timeout_height > ${height} OR timeout_timestamp > ${timestamp})`, })) ) } else { wheres.push({ - in_progress: Boolean.TRUE, + in_progress: Boolean.FALSE, dst_chain_id: chainId, src_chain_id: In(counterpartyChainIds), + custom: `(timeout_height > ${height} OR timeout_timestamp > ${timestamp})`, }) } @@ -82,7 +87,7 @@ export class PacketController { public static getTimeoutPackets( chainId: string, height: number, - timetstamp: number, + timestamp: number, counterpartyChainIds: string[], filter: PacketFilter = {}, limit = 100 @@ -97,8 +102,7 @@ export class PacketController { src_connection_id: conn.connectionId, src_channel_id: conn.channels ? In(conn.channels) : undefined, dst_chain_id: In(counterpartyChainIds), // TODO: make this more efficientnet, like filter it on outside of this. - height: { lt: height, gt: 0 }, - timetstamp: { lt: timetstamp, gt: 0 }, + custom: `((timeout_height < ${height} AND timeout_height != 0) OR timeout_timestamp < ${timestamp} AND timeout_timestamp != 0)`, })) ) } else { @@ -106,6 +110,7 @@ export class PacketController { in_progress: Boolean.FALSE, src_chain_id: chainId, dst_chain_id: In(counterpartyChainIds), + custom: `((timeout_height < ${height} AND timeout_height != 0) OR timeout_timestamp < ${timestamp} AND timeout_timestamp != 0)`, }) } @@ -208,6 +213,19 @@ export class PacketController { ) } + public static resetPacketInProgress(db?: Database) { + db = db ?? DB + update(db, this.tableNamePacketSend, { + in_progress: Boolean.FALSE, + }) + update(db, this.tableNamePacketTimeout, { + in_progress: Boolean.FALSE, + }) + update(db, this.tableNamePacketWriteAck, { + in_progress: Boolean.FALSE, + }) + } + private static async feedSendPacketEvent( lcd: LCDClient, chainId: string, diff --git a/src/db/migration/index.ts b/src/db/migration/index.ts index baee56b..b00c764 100644 --- a/src/db/migration/index.ts +++ b/src/db/migration/index.ts @@ -1,6 +1,7 @@ import { Database } from 'better-sqlite3' import fs from 'fs' import { VersionTable } from 'src/types' +import { PacketController } from '../controller/packet' export function migrate(db: Database): Database { // create version table @@ -38,6 +39,9 @@ export function migrate(db: Database): Database { db.exec(sql) })() } + + // reset in progress + PacketController.resetPacketInProgress(db) return db } diff --git a/src/db/utils.ts b/src/db/utils.ts index a38910c..beb84b2 100644 --- a/src/db/utils.ts +++ b/src/db/utils.ts @@ -174,6 +174,8 @@ function where(wheres?: WhereOptions[]): [string, ParamType[]] { condition.push(`(${rangeConditions.join(' AND ')})`) } + } else if (key === 'custom') { + condition.push(value) } else { condition.push(`${String(key)} = ?`) params.push(toParamType(value)) @@ -194,7 +196,7 @@ function where(wheres?: WhereOptions[]): [string, ParamType[]] { export type WhereOptions = { [P in keyof T]?: T[P] | Range | In -} +} & { custom?: string } interface Range { gt?: V diff --git a/src/index.ts b/src/index.ts index 5301936..1cdd3f4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,10 @@ // import { Chain } from './chain' -// import express from 'express' +import express from 'express' // import { runPair } from './lib/chainPair' // import { config } from './lib/config' // import { ChainStatus } from './chain/types' // import { registery } from './lib/metric' -// import { info } from './lib/logger' +import { info } from './lib/logger' import { config } from './lib/config' import { WorkerController } from './workers' @@ -12,20 +12,11 @@ import { WorkerController } from './workers' async function main() { const workerController = new WorkerController() await workerController.init(config) - // const app = express() + const app = express() - // app.get('/status', (req, res) => { - // const result: Record = - // {} - // Object.keys(pairs).map((name) => { - // result[name] = { - // chainA: pairs[name].chainA.chainStatus(), - // chainB: pairs[name].chainB.chainStatus(), - // } - // }) - - // res.json(result) - // }) + app.get('/status', (req, res) => { + res.json(workerController.getStatus()) + }) // const metricApp = express() @@ -37,8 +28,8 @@ async function main() { // .catch(() => res.status(500).send('Fail to get metrics')) // }) - // app.listen(config.port) - // info(`status app listen to port ${config.port}`) + app.listen(config.port) + info(`status app listen to port ${config.port}`) // metricApp.listen(config.metricPort) // info(`metric app listen to port ${config.metricPort}`) } diff --git a/src/workers/index.ts b/src/workers/index.ts index 24b749e..916ac5e 100644 --- a/src/workers/index.ts +++ b/src/workers/index.ts @@ -28,6 +28,7 @@ import { env } from 'node:process' import { RPCClient } from 'src/lib/rpcClient' import * as http from 'http' import * as https from 'https' +import { PacketFilter } from 'src/db/controller/packet' export class WorkerController { public chains: Record // chainId => ChainWorker @@ -84,7 +85,7 @@ export class WorkerController { walletConfig.packetFilter ) - this.wallets[`${chainConfig.chainId}::${wallet.address()}`] + this.wallets[`${chainConfig.chainId}::${wallet.address()}`] = wallet } } } @@ -93,6 +94,49 @@ export class WorkerController { return Object.keys(this.chains) } + public getStatus(): { chains: ChainStatus[] } { + const chainKeys = Object.keys(this.chains) + const walletKeys = Object.keys(this.wallets) + + const chains: ChainStatus[] = chainKeys.map((key) => { + const chain = this.chains[key] + const syncWorkerKeys = Object.keys(chain.syncWorkers) + const syncWorkers = syncWorkerKeys.map((key) => { + const syncWorker = chain.syncWorkers[Number(key)] + return { + startHeight: syncWorker.startHeight, + endHeight: syncWorker.endHeight === -1 ? null : syncWorker.endHeight, + syncedHeight: syncWorker.syncedHeight, + } + }) + + return { + chainId: chain.chainId, + latestHeight: chain.latestHeight, + latestTimestamp: new Date(chain.latestTimestamp), + syncWorkers, + walletWorkers: [], + } + }) + + walletKeys.map((key) => { + const wallet = this.wallets[key] + const walletWorker = { + address: wallet.address(), + packetFilter: wallet.packetFilter, + } + + const chain = chains.filter( + (chain) => chain.chainId === wallet.chain.chainId + ) + chain[0].walletWorkers.push(walletWorker) + }) + + return { + chains, + } + } + async generateMsgUpdateClient( chainId: string, clientId: string, @@ -109,9 +153,9 @@ export class WorkerController { ) return generateMsgUpdateClient( - this.chains[chainId], this.chains[client.counterparty_chain_id], - client.counterparty_chain_id, + this.chains[chainId], + client.client_id, executorAddress ) } @@ -178,3 +222,18 @@ function createKey(rawKey: KeyConfig): Key { } return keyReturn } + +interface ChainStatus { + chainId: string + latestHeight: number + latestTimestamp: Date + syncWorkers: { + startHeight: number + endHeight: number | null + syncedHeight: number + }[] + walletWorkers: { + address: string + packetFilter?: PacketFilter + }[] +} diff --git a/src/workers/wallet.ts b/src/workers/wallet.ts index 9850917..be10f7a 100644 --- a/src/workers/wallet.ts +++ b/src/workers/wallet.ts @@ -24,7 +24,7 @@ export class WalletWorker { public workerController: WorkerController, private maxHandlePacket: number, private wallet: Wallet, - private packetFilter?: PacketFilter + public packetFilter?: PacketFilter ) { this.run() } @@ -41,6 +41,8 @@ export class WalletWorker { } private async handlePackets() { + if (this.chain.latestHeight === 0) return + // get packets to handle let remain = this.maxHandlePacket @@ -50,6 +52,8 @@ export class WalletWorker { const sendPakcets = PacketController.getSendPackets( this.chain.chainId, + this.chain.latestHeight, + Number((this.chain.latestTimestamp / 1000).toFixed()), counterpartyChainIds, this.packetFilter, remain @@ -115,9 +119,9 @@ export class WalletWorker { // generate update client msgs // get unique client id const connections = [ - ...filteredSendPackets.map((packet) => packet.src_connection_id), - ...filteredWriteAckPackets.map((packet) => packet.dst_connection_id), - ...filteredTimeoutPackets.map((packet) => packet.dst_connection_id), + ...filteredSendPackets.map((packet) => packet.dst_connection_id), + ...filteredWriteAckPackets.map((packet) => packet.src_connection_id), + ...filteredTimeoutPackets.map((packet) => packet.src_connection_id), ].filter((v, i, a) => a.indexOf(v) === i) // filter by connection first const connectionClientMap: Record = {} @@ -234,7 +238,8 @@ export class WalletWorker { } this.sequence++ - } catch { + } catch (e) { + this.error(JSON.stringify(e, undefined, 2)) // revert packet in progress DB.transaction(() => { sendPakcets.map((packet) => From 1fac1550999d2097c58f6fc26023f9825dd09fae Mon Sep 17 00:00:00 2001 From: ALPAC-4 Date: Fri, 27 Sep 2024 15:36:47 +0900 Subject: [PATCH 09/30] fix: filter packet that create in latest height --- src/db/controller/packet.ts | 12 +- src/db/migration/0.0.1.sql | 2 + src/db/utils.ts | 143 ++++++++++++++--------- src/lib/eventParser.ts | 3 +- src/msgs/index.ts | 2 +- src/msgs/{recvPacet.ts => recvPacket.ts} | 4 +- src/types/event.ts | 1 + src/types/table.ts | 2 + src/workers/chain.ts | 2 +- src/workers/wallet.ts | 17 ++- 10 files changed, 125 insertions(+), 63 deletions(-) rename src/msgs/{recvPacet.ts => recvPacket.ts} (97%) diff --git a/src/db/controller/packet.ts b/src/db/controller/packet.ts index f33b9a3..518d96d 100644 --- a/src/db/controller/packet.ts +++ b/src/db/controller/packet.ts @@ -81,7 +81,13 @@ export class PacketController { }) } - return select(DB, this.tableNamePacketSend, wheres, limit) + return select( + DB, + this.tableNamePacketSend, + wheres, + { sequence: 'ASC' }, + limit + ) } public static getTimeoutPackets( @@ -118,6 +124,7 @@ export class PacketController { DB, this.tableNamePacketTimeout, wheres, + { sequence: 'ASC' }, limit ) } @@ -152,6 +159,7 @@ export class PacketController { DB, this.tableNamePacketWriteAck, wheres, + { sequence: 'ASC' }, limit ) } @@ -245,6 +253,7 @@ export class PacketController { dst_channel_id: event.packetInfo.dstChannel, sequence: Number(event.packetInfo.sequence), in_progress: Boolean.FALSE, + height: event.packetInfo.height, dst_port: event.packetInfo.dstPort, src_chain_id: chainId, src_connection_id: event.packetInfo.connectionId, @@ -301,6 +310,7 @@ export class PacketController { src_channel_id: event.packetInfo.srcChannel, sequence: Number(event.packetInfo.sequence), in_progress: Boolean.FALSE, + height: event.packetInfo.height, src_port: event.packetInfo.srcPort, dst_chain_id: chainId, dst_connection_id: event.packetInfo.connectionId, diff --git a/src/db/migration/0.0.1.sql b/src/db/migration/0.0.1.sql index ac56f5b..3da4714 100644 --- a/src/db/migration/0.0.1.sql +++ b/src/db/migration/0.0.1.sql @@ -47,6 +47,7 @@ CREATE TABLE packet_send ( in_progress BOOLEAN, -- packet data + height BIGINT NOT NULL, dst_port TEXT NOT NULL, src_chain_id TEXT NOT NULL, -- add this for filtering src_connection_id TEXT NOT NULL, @@ -101,6 +102,7 @@ CREATE TABLE packet_write_ack ( in_progress BOOLEAN, -- packet data + height BIGINT NOT NULL, src_port TEXT NOT NULL, dst_chain_id TEXT NOT NULL, -- add this for filtering dst_connection_id TEXT NOT NULL, diff --git a/src/db/utils.ts b/src/db/utils.ts index beb84b2..08ec360 100644 --- a/src/db/utils.ts +++ b/src/db/utils.ts @@ -71,13 +71,21 @@ export function select( db: Database, tableName: string, wheres?: WhereOptions[], + orders?: Order, limit?: number ): T[] { let sql = `SELECT * FROM ${tableName}` const params: ParamType[] = [] - const [whereSql, whereParams] = where(wheres) - sql += whereSql - params.push(...whereParams) + + if (wheres) { + const [whereSql, whereParams] = where(wheres) + sql += whereSql + params.push(...whereParams) + } + + if (orders) { + sql += order(orders) + } if (limit) { sql += ' limit ?' @@ -90,9 +98,10 @@ export function select( export function selectOne( db: Database, tableName: string, - wheres?: WhereOptions[] + wheres?: WhereOptions[], + orders?: Order ): T | undefined { - const res = select(db, tableName, wheres, 1) + const res = select(db, tableName, wheres, orders, 1) if (res.length === 0) return undefined return res[0] @@ -105,9 +114,11 @@ export function del( ) { let sql = `DELETE from ${tableName}` const params: ParamType[] = [] - const [whereSql, whereParams] = where(wheres) - sql += whereSql - params.push(...whereParams) + if (wheres) { + const [whereSql, whereParams] = where(wheres) + sql += whereSql + params.push(...whereParams) + } db.prepare(sql).run(params) } @@ -124,66 +135,66 @@ export function update( const placeHolder = keys.map((key) => ` ${key} = $${key}`).join(',') sql += placeHolder - const [whereSql, whereParams] = where(wheres) - sql += whereSql - params.push(...whereParams) + if (wheres) { + const [whereSql, whereParams] = where(wheres) + sql += whereSql + params.push(...whereParams) + } db.prepare(sql).run(params, set) } -function where(wheres?: WhereOptions[]): [string, ParamType[]] { +function where(wheres: WhereOptions[]): [string, ParamType[]] { let sql = '' const conditions = [] const params: ParamType[] = [] - if (wheres) { - for (const where of wheres) { - const condition = [] - const keys = Object.keys(where) as Array - for (const key of keys) { - const value = where[key] - if (typeof value === 'object' && value !== null) { - if ('in' in value) { - const vals = value.in as unknown[] - const placeHolder = vals.map((_) => '?').join(',') - condition.push(`${String(key)} IN(${placeHolder})`) - params.push(...vals.map(toParamType)) - } else { - const rangeConditions: string[] = [] - - // if both gt and gte are given, use gt - if ('gte' in value && !('gt' in value)) { - rangeConditions.push(`${String(key)} >= ?`) - params.push(toParamType(value.gte)) - } - if ('gt' in value) { - rangeConditions.push(`${String(key)} > ?`) - params.push(toParamType(value.gt)) - } - - // if both lt and lte are given, use lt - if ('lte' in value && !('lt' in value)) { - rangeConditions.push(`${String(key)} <= ?`) - params.push(toParamType(value.lte)) - } - if ('lt' in value) { - rangeConditions.push(`${String(key)} < ?`) - params.push(toParamType(value.lt)) - } - - condition.push(`(${rangeConditions.join(' AND ')})`) - } - } else if (key === 'custom') { - condition.push(value) + for (const where of wheres) { + const condition = [] + const keys = Object.keys(where) as Array + for (const key of keys) { + const value = where[key] + if (typeof value === 'object' && value !== null) { + if ('in' in value) { + const vals = value.in as unknown[] + const placeHolder = vals.map((_) => '?').join(',') + condition.push(`${String(key)} IN(${placeHolder})`) + params.push(...vals.map(toParamType)) } else { - condition.push(`${String(key)} = ?`) - params.push(toParamType(value)) + const rangeConditions: string[] = [] + + // if both gt and gte are given, use gt + if ('gte' in value && !('gt' in value)) { + rangeConditions.push(`${String(key)} >= ?`) + params.push(toParamType(value.gte)) + } + if ('gt' in value) { + rangeConditions.push(`${String(key)} > ?`) + params.push(toParamType(value.gt)) + } + + // if both lt and lte are given, use lt + if ('lte' in value && !('lt' in value)) { + rangeConditions.push(`${String(key)} <= ?`) + params.push(toParamType(value.lte)) + } + if ('lt' in value) { + rangeConditions.push(`${String(key)} < ?`) + params.push(toParamType(value.lt)) + } + + condition.push(`(${rangeConditions.join(' AND ')})`) } + } else if (key === 'custom') { + condition.push(value) + } else { + condition.push(`${String(key)} = ?`) + params.push(toParamType(value)) } - if (condition.length !== 0) { - conditions.push(`(${condition.join(' AND ')})`) - } + } + if (condition.length !== 0) { + conditions.push(`(${condition.join(' AND ')})`) } } @@ -194,10 +205,30 @@ function where(wheres?: WhereOptions[]): [string, ParamType[]] { return [sql, params] } +function order(order: Order): string { + let sql = '' + + const orders: string[] = [] + const keys = Object.keys(where) as Array + for (const key of keys) { + orders.push(`${String(key)} ${order[key]}`) + } + + if (orders.length !== 0) { + sql = ` ORDER BY ${orders.join(', ')} ` + } + + return sql +} + export type WhereOptions = { [P in keyof T]?: T[P] | Range | In } & { custom?: string } +export type Order = { + [P in keyof T]?: 'ASC' | 'DESC' +} + interface Range { gt?: V gte?: V diff --git a/src/lib/eventParser.ts b/src/lib/eventParser.ts index 4729015..af6c035 100644 --- a/src/lib/eventParser.ts +++ b/src/lib/eventParser.ts @@ -1,7 +1,7 @@ import { Event } from '@cosmjs/tendermint-rpc/build/comet38/responses' import { PacketInfo } from 'src/types' -export function parsePacketEvent(event: Event): PacketInfo { +export function parsePacketEvent(event: Event, height: number): PacketInfo { const connectionId = event.attributes.filter( (v) => v.key === 'connection_id' )[0].value @@ -50,6 +50,7 @@ export function parsePacketEvent(event: Event): PacketInfo { : Buffer.from(ackHex[0].value, 'hex').toString('base64') return { + height, connectionId, sequence, srcPort, diff --git a/src/msgs/index.ts b/src/msgs/index.ts index 136d3f0..865227f 100644 --- a/src/msgs/index.ts +++ b/src/msgs/index.ts @@ -1,4 +1,4 @@ export * from './ack' -export * from './recvPacet' +export * from './recvPacket' export * from './timeout' export * from './updateClient' diff --git a/src/msgs/recvPacet.ts b/src/msgs/recvPacket.ts similarity index 97% rename from src/msgs/recvPacet.ts rename to src/msgs/recvPacket.ts index 06c2c03..0e4af81 100644 --- a/src/msgs/recvPacet.ts +++ b/src/msgs/recvPacket.ts @@ -13,9 +13,10 @@ export async function generateMsgRecvPacket( packetSend: PacketSendTable, height: Height, msgExecutor: string -) { +): Promise { const packet = packetTableToPacket(packetSend) const proof = await getPacketProof(srcChain, packet, height) + const msg = new MsgRecvPacket( packet, proof, @@ -37,7 +38,6 @@ async function getPacketProof( ) ) const proof = await getRawProof(dstChain, key, headerHeight) - const ics23Proof = convertProofsToIcs23(proof) return Buffer.from(ics23Proof).toString('base64') diff --git a/src/types/event.ts b/src/types/event.ts index 2ad5a09..19abe26 100644 --- a/src/types/event.ts +++ b/src/types/event.ts @@ -27,6 +27,7 @@ export interface WriteAckEvent { } export interface PacketInfo { + height: number connectionId: string sequence: number srcPort: string diff --git a/src/types/table.ts b/src/types/table.ts index 0dd181d..bec593a 100644 --- a/src/types/table.ts +++ b/src/types/table.ts @@ -34,6 +34,7 @@ export interface PacketSendTable { dst_channel_id: string sequence: number in_progress: Boolean + height: number dst_port: string src_chain_id: string src_connection_id: string @@ -70,6 +71,7 @@ export interface PacketWriteAckTable { src_channel_id: string sequence: number in_progress: Boolean + height: number src_port: string dst_chain_id: string dst_connection_id: string diff --git a/src/workers/chain.ts b/src/workers/chain.ts index 2adb8c3..d6211f9 100644 --- a/src/workers/chain.ts +++ b/src/workers/chain.ts @@ -189,7 +189,7 @@ class SyncWorker { event.type === 'acknowledge_packet' || event.type === 'timeout_packet' ) { - let packetInfo = parsePacketEvent(event) + let packetInfo = parsePacketEvent(event, height) if (packetInfo) { packetEvents.push({ type: event.type as PacketType, diff --git a/src/workers/wallet.ts b/src/workers/wallet.ts index be10f7a..70fb6b1 100644 --- a/src/workers/wallet.ts +++ b/src/workers/wallet.ts @@ -57,6 +57,10 @@ export class WalletWorker { counterpartyChainIds, this.packetFilter, remain + ).filter( + (packet) => + packet.height < + this.workerController.chains[packet.src_chain_id].latestHeight ) remain -= sendPakcets.length @@ -69,6 +73,10 @@ export class WalletWorker { counterpartyChainIds, this.packetFilter, remain + ).filter( + (packet) => + packet.height < + this.workerController.chains[packet.dst_chain_id].latestHeight ) remain -= writeAckPackets.length @@ -237,9 +245,16 @@ export class WalletWorker { ) } + this.info(`Handled msgs(${msgs.length}). txhash - ${result.txhash}`) + this.sequence++ } catch (e) { - this.error(JSON.stringify(e, undefined, 2)) + if (e?.response?.data) { + this.error(e.response.data) + } else { + this.error(e) + } + // revert packet in progress DB.transaction(() => { sendPakcets.map((packet) => From 6b65084e6a70cbd5d30178ecc8844cf2fb5d687b Mon Sep 17 00:00:00 2001 From: ALPAC-4 Date: Fri, 27 Sep 2024 15:44:19 +0900 Subject: [PATCH 10/30] update logger --- src/lib/logger.ts | 14 ++++++++++ src/workers/chain.ts | 61 ++++++++++++++----------------------------- src/workers/wallet.ts | 41 +++++++++++------------------ 3 files changed, 48 insertions(+), 68 deletions(-) diff --git a/src/lib/logger.ts b/src/lib/logger.ts index 6f37cd0..1cb69fd 100644 --- a/src/lib/logger.ts +++ b/src/lib/logger.ts @@ -27,3 +27,17 @@ export function error(msg: string): void { export function debug(msg: string): void { logger.debug(msg) } + +export function createLoggerWithPrefix(prefix: string) { + const format = winston.format.printf( + ({ level, message, prefix, timestamp }) => { + return `${timestamp} ${level}: ${prefix}${message}` + } + ) + + return winston.createLogger({ + level: config.logLevel, + format: winston.format.combine(winston.format.timestamp(), format), + transports: [new winston.transports.Console()], + }) +} diff --git a/src/workers/chain.ts b/src/workers/chain.ts index d6211f9..de24607 100644 --- a/src/workers/chain.ts +++ b/src/workers/chain.ts @@ -1,17 +1,19 @@ import { LCDClient } from '@initia/initia.js' import { RPCClient } from 'src/lib/rpcClient' -import { info, error, debug } from 'src/lib/logger' +import { createLoggerWithPrefix } from 'src/lib/logger' import { PacketEvent, PacketType } from 'src/types' import { parsePacketEvent } from 'src/lib/eventParser' import { DB } from 'src/db' import { SyncInfoController } from 'src/db/controller/syncInfo' import { PacketController } from 'src/db/controller/packet' import { delay } from 'bluebird' +import { Logger } from 'winston' export class ChainWorker { public latestHeight: number public latestTimestamp: number public syncWorkers: Record + public logger: Logger public constructor( public chainId: string, @@ -21,6 +23,7 @@ export class ChainWorker { latestHeight: number, startHeights: number[] ) { + this.logger = createLoggerWithPrefix(``) this.latestHeight = 0 const syncInfos = SyncInfoController.init( chainId, @@ -54,20 +57,20 @@ export class ChainWorker { } private async latestHeightWorker() { - this.debug('Activate latest height worekr') + this.logger.debug('Activate latest height worekr') // TODO add websocket options const MAX_RETRY = 10 let retried = 0 for (;;) { try { await this.updateLatestHeight() - this.debug( + this.logger.debug( `Set latest height. Height - ${this.latestHeight}, Timestamp - ${this.latestTimestamp}` ) retried = 0 } catch (e) { - this.error( + this.logger.error( `[latestHeightWorker] Got error while fetching latest height (${e})` ) retried++ @@ -86,32 +89,24 @@ export class ChainWorker { // this.inc(metrics.chain.latestHeightWorker) } - - private info(log: string) { - info(` ${log}`) - } - - private error(log: string) { - error(` ${log}`) - } - - private debug(log: string) { - debug(` ${log}`) - } } class SyncWorker { + private logger: Logger public constructor( public chain: ChainWorker, public startHeight: number, public endHeight: number, public syncedHeight: number ) { + this.logger = createLoggerWithPrefix( + `` + ) this.feedEvents() } private async feedEvents() { - this.debug('Activate event feeder') + this.logger.debug('Activate event feeder') for (;;) { try { @@ -131,7 +126,7 @@ class SyncWorker { heights.map((height) => this.fetchPacketEvents(height)) ) - this.debug( + this.logger.debug( `Fetched block results for heights (${JSON.stringify(heights)})` ) @@ -153,21 +148,23 @@ class SyncWorker { heights[heights.length - 1] ) - this.debug(`Store packet events(${packetEvenets.flat().length})`) + this.logger.debug( + `Store packet events(${packetEvenets.flat().length})` + ) })() this.syncedHeight = heights[heights.length - 1] // terminate worker if (finish) { - this.info( + this.logger.info( 'Synced height reached to end height. Terminate sync worker' ) this.chain.terminateSyncWorker(this.startHeight) break } } catch (e) { - this.error(`Fail to fecth block result. resonse - ${e}`) + this.logger.error(`Fail to fecth block result. resonse - ${e}`) } finally { await delay(500) } @@ -175,7 +172,7 @@ class SyncWorker { } private async fetchPacketEvents(height: number): Promise { - this.debug(`Fecth new block results (height - ${height})`) + this.logger.debug(`Fecth new block results (height - ${height})`) const blockResult = await this.chain.rpc.blockResults(height) const txData = [...blockResult.results] @@ -202,26 +199,6 @@ class SyncWorker { return packetEvents } - - // logs - - private info(log: string) { - info( - ` ${log}` - ) - } - - private error(log: string) { - error( - ` ${log}` - ) - } - - private debug(log: string) { - debug( - ` ${log}` - ) - } } export async function queryLatestHeight(rpc: RPCClient): Promise { diff --git a/src/workers/wallet.ts b/src/workers/wallet.ts index 70fb6b1..a4295d1 100644 --- a/src/workers/wallet.ts +++ b/src/workers/wallet.ts @@ -11,13 +11,15 @@ import { MsgUpdateClient } from '@initia/initia.js/dist/core/ibc/core/client/msg import { Height } from 'cosmjs-types/ibc/core/client/v1/client' import { ConnectionController } from 'src/db/controller/connection' import { Wallet, isTxError } from '@initia/initia.js' -import { debug, error, info, warn } from 'src/lib/logger' +import { createLoggerWithPrefix } from 'src/lib/logger' import { bech32 } from 'bech32' import { delay } from 'bluebird' +import { Logger } from 'winston' // TODO: add update client worker export class WalletWorker { private sequence?: number + private logger: Logger constructor( public chain: ChainWorker, @@ -26,6 +28,9 @@ export class WalletWorker { private wallet: Wallet, public packetFilter?: PacketFilter ) { + this.logger = createLoggerWithPrefix( + `` + ) this.run() } @@ -34,7 +39,7 @@ export class WalletWorker { try { await this.handlePackets() } catch (e) { - this.error(`[run] ${e}`) + this.logger.error(`[run] ${e}`) } await delay(500) } @@ -231,13 +236,13 @@ export class WalletWorker { try { const expected = result.raw_log.split(', ')[1] this.sequence = Number(expected.split(' ')[1]) - this.info(`update sequence`) + this.logger.info(`update sequence`) } catch (e) { - this.warn(`error to parse sequence`) + this.logger.warn(`error to parse sequence`) } } - this.error( + this.logger.error( `Tx failed. raw log - ${result.raw_log}, code - ${result.code}` ) throw Error( @@ -245,14 +250,16 @@ export class WalletWorker { ) } - this.info(`Handled msgs(${msgs.length}). txhash - ${result.txhash}`) + this.logger.info( + `Handled msgs(${msgs.length}). txhash - ${result.txhash}` + ) this.sequence++ } catch (e) { if (e?.response?.data) { - this.error(e.response.data) + this.logger.error(e.response.data) } else { - this.error(e) + this.logger.error(e) } // revert packet in progress @@ -412,22 +419,4 @@ export class WalletWorker { return Object.values(timeoutPacketMap).flat() } - - // logs - - private info(log: string) { - info(` ${log}`) - } - - private warn(log: string) { - warn(` ${log}`) - } - - private error(log: string) { - error(` ${log}`) - } - - private debug(log: string) { - debug(` ${log}`) - } } From f055a38bf2665f51fd8e8297dfe80d85af039c2f Mon Sep 17 00:00:00 2001 From: ALPAC-4 Date: Fri, 4 Oct 2024 11:13:54 +0900 Subject: [PATCH 11/30] support create channel --- src/db/controller/channel.ts | 249 ++++++++++++++++++++++++++++++ src/db/controller/client.ts | 8 +- src/db/controller/connection.ts | 9 +- src/db/controller/packet.ts | 62 +++++++- src/db/migration/0.0.1.sql | 19 +++ src/db/migration/index.ts | 2 + src/db/utils.ts | 2 + src/lib/eventParser.ts | 40 ++++- src/lib/lcdClient.ts | 90 +++++++++++ src/lib/logger.ts | 8 +- src/lib/{rawProof.ts => proof.ts} | 25 +++ src/msgs/ack.ts | 14 +- src/msgs/channelOpenAck.ts | 29 ++++ src/msgs/channelOpenConfirm.ts | 23 +++ src/msgs/channelOpenTry.ts | 38 +++++ src/msgs/index.ts | 3 + src/msgs/recvPacket.ts | 3 +- src/msgs/timeout.ts | 3 +- src/types/event.ts | 19 +++ src/types/table.ts | 20 +++ src/workers/chain.ts | 63 +++++--- src/workers/index.ts | 62 +++++++- src/workers/wallet.ts | 119 +++++++++++++- 23 files changed, 853 insertions(+), 57 deletions(-) create mode 100644 src/db/controller/channel.ts create mode 100644 src/lib/lcdClient.ts rename src/lib/{rawProof.ts => proof.ts} (63%) create mode 100644 src/msgs/channelOpenAck.ts create mode 100644 src/msgs/channelOpenConfirm.ts create mode 100644 src/msgs/channelOpenTry.ts diff --git a/src/db/controller/channel.ts b/src/db/controller/channel.ts new file mode 100644 index 0000000..92f8c9c --- /dev/null +++ b/src/db/controller/channel.ts @@ -0,0 +1,249 @@ +import { DB } from '..' +import { + Boolean, + ChannelOnOpenTable, + ChannelOpenEvent, + ChannelState, +} from 'src/types' +import { In, WhereOptions, del, insert, select, update } from '../utils' +import { LCDClient } from 'src/lib/lcdClient' +import { ConnectionController } from './connection' +import { PacketFilter } from './packet' +import { Database } from 'better-sqlite3' + +export class ChannelController { + static tableName = 'channel_on_open' + public static async feedEvents( + lcd: LCDClient, + chainId: string, + events: ChannelOpenEvent[] + ): Promise<() => void> { + const feedFns: (() => void)[] = [] + for (const event of events) { + switch (event.type) { + case 'channel_open_init': + feedFns.push(await this.feedChannelOpenInitEvent(lcd, chainId, event)) + break + case 'channel_open_try': + feedFns.push(await this.feedChannelOpenTryEvent(lcd, chainId, event)) + break + case 'channel_open_ack': + feedFns.push(await this.feedChannelOpenAckEvent(lcd, chainId, event)) + break + case 'channel_open_confirm': + feedFns.push( + await this.feedChannelOpenConfirmEvent(lcd, chainId, event) + ) + break + } + } + + return () => { + for (const fn of feedFns) { + fn() + } + } + } + + public static getOpenEvent( + chainId: string, + counterpartyChainIds: string[], + filter: PacketFilter = {}, + state?: ChannelState, + limit = 100 + ): ChannelOnOpenTable[] { + const wheres: WhereOptions[] = [] + const deleteWheres: WhereOptions[] = [] + + if (filter.connections) { + for (const connectionFilter of filter.connections) { + if (connectionFilter.channels) continue + wheres.push({ + in_progress: Boolean.FALSE, + state, + chain_id: chainId, + connection_id: connectionFilter.connectionId, + counterparty_chain_id: In(counterpartyChainIds), + }) + deleteWheres.push({ + in_progress: Boolean.TRUE, + state, + chain_id: chainId, + connection_id: connectionFilter.connectionId, + counterparty_chain_id: In(counterpartyChainIds), + }) + } + } else { + wheres.push({ + in_progress: Boolean.FALSE, + state, + chain_id: chainId, + counterparty_chain_id: In(counterpartyChainIds), + }) + deleteWheres.push({ + in_progress: Boolean.TRUE, + state, + chain_id: chainId, + counterparty_chain_id: In(counterpartyChainIds), + }) + } + + // delete with same where condition but in_progress is true (which means those packets were filtered) + del(DB, this.tableName, deleteWheres) + + return select( + DB, + this.tableName, + wheres, + { id: 'ASC' }, + limit + ) + } + + public static updateInProgress(id?: number, inProgress = true) { + update( + DB, + this.tableName, + { in_progress: inProgress ? Boolean.TRUE : Boolean.FALSE }, + [{ id }] + ) + } + + public static resetPacketInProgress(db?: Database) { + db = db ?? DB + update(db, this.tableName, { + in_progress: Boolean.FALSE, + }) + } + + private static async feedChannelOpenInitEvent( + lcd: LCDClient, + chainId: string, + event: ChannelOpenEvent + ): Promise<() => void> { + const connection = await ConnectionController.getConnection( + lcd, + chainId, + event.channelOpenInfo.srcConnectionId + ) + + // add channel on open for dst chain + const channelOnOpen: ChannelOnOpenTable = { + in_progress: Boolean.FALSE, + state: ChannelState.INIT, + chain_id: connection.counterparty_chain_id, + connection_id: connection.counterparty_connection_id, + port_id: event.channelOpenInfo.dstPortId, + channel_id: event.channelOpenInfo.dstChannelId, + counterparty_chain_id: chainId, + counterparty_connection_id: event.channelOpenInfo.srcConnectionId, + counterparty_port_id: event.channelOpenInfo.srcPortId, + counterparty_channel_id: event.channelOpenInfo.srcChannelId, + } + + return () => { + insert(DB, this.tableName, channelOnOpen) + } + } + + private static async feedChannelOpenTryEvent( + lcd: LCDClient, + chainId: string, + event: ChannelOpenEvent + ): Promise<() => void> { + const connection = await ConnectionController.getConnection( + lcd, + chainId, + event.channelOpenInfo.dstConnectionId + ) + + // add channel on open for src chain + const channelOnOpen: ChannelOnOpenTable = { + in_progress: Boolean.FALSE, + state: ChannelState.TRYOPEN, + chain_id: connection.counterparty_chain_id, + connection_id: connection.counterparty_connection_id, + port_id: event.channelOpenInfo.srcPortId, + channel_id: event.channelOpenInfo.srcChannelId, + counterparty_chain_id: chainId, + counterparty_connection_id: event.channelOpenInfo.dstConnectionId, + counterparty_port_id: event.channelOpenInfo.dstPortId, + counterparty_channel_id: event.channelOpenInfo.dstChannelId, + } + + return () => { + del(DB, this.tableName, [ + { + state: ChannelState.INIT, + counterparty_chain_id: connection.counterparty_chain_id, + counterparty_port_id: channelOnOpen.port_id, + counterparty_channel_id: channelOnOpen.channel_id, + }, + ]) // remove init + insert(DB, this.tableName, channelOnOpen) + } + } + + private static async feedChannelOpenAckEvent( + lcd: LCDClient, + chainId: string, + event: ChannelOpenEvent + ): Promise<() => void> { + const connection = await ConnectionController.getConnection( + lcd, + chainId, + event.channelOpenInfo.srcConnectionId + ) + + // add channel on open for dst chain + const channelOnOpen: ChannelOnOpenTable = { + in_progress: Boolean.FALSE, + state: ChannelState.ACK, + chain_id: connection.counterparty_chain_id, + connection_id: connection.counterparty_connection_id, + port_id: event.channelOpenInfo.dstPortId, + channel_id: event.channelOpenInfo.dstChannelId, + counterparty_chain_id: chainId, + counterparty_connection_id: event.channelOpenInfo.srcConnectionId, + counterparty_port_id: event.channelOpenInfo.srcPortId, + counterparty_channel_id: event.channelOpenInfo.srcChannelId, + } + + return () => { + del(DB, this.tableName, [ + { + state: ChannelState.INIT, + counterparty_chain_id: chainId, + counterparty_port_id: channelOnOpen.counterparty_port_id, + counterparty_channel_id: channelOnOpen.counterparty_channel_id, + }, + ]) // remove init + insert(DB, this.tableName, channelOnOpen) + } + } + + private static async feedChannelOpenConfirmEvent( + _lcd: LCDClient, + chainId: string, + event: ChannelOpenEvent + ): Promise<() => void> { + return () => { + del(DB, this.tableName, [ + { + state: ChannelState.TRYOPEN, + counterparty_chain_id: chainId, + counterparty_port_id: event.channelOpenInfo.srcPortId, + counterparty_channel_id: event.channelOpenInfo.srcChannelId, + }, + ]) // remove open try + del(DB, this.tableName, [ + { + state: ChannelState.ACK, + counterparty_chain_id: chainId, + counterparty_port_id: event.channelOpenInfo.dstPortId, + counterparty_channel_id: event.channelOpenInfo.dstChannelId, + }, + ]) // remove ack + } + } +} diff --git a/src/db/controller/client.ts b/src/db/controller/client.ts index 88ef830..fd8570c 100644 --- a/src/db/controller/client.ts +++ b/src/db/controller/client.ts @@ -1,9 +1,9 @@ import { DB } from '..' import { insert, selectOne } from '../utils' import { Any } from 'cosmjs-types/google/protobuf/any' -import { ClientState, UpdateClientEvent, ClientTable } from 'src/types' +import { UpdateClientEvent, ClientTable } from 'src/types' import { Header } from 'cosmjs-types/ibc/lightclients/tendermint/v1/tendermint' -import { LCDClient } from '@initia/initia.js' +import { LCDClient } from 'src/lib/lcdClient' export class ClientController { static tableName = 'client' @@ -12,9 +12,7 @@ export class ClientController { chainId: string, clientId: string ): Promise { - const state = await lcd.apiRequester.get( - `/ibc/core/client/v1/client_states/${clientId}` - ) + const state = await lcd.ibc.getClientState(clientId) const client: ClientTable = { chain_id: chainId, diff --git a/src/db/controller/connection.ts b/src/db/controller/connection.ts index b585699..7f3185e 100644 --- a/src/db/controller/connection.ts +++ b/src/db/controller/connection.ts @@ -1,8 +1,8 @@ import { DB } from '..' -import { ConnectionInfo, ConnectionTable } from 'src/types' -import { LCDClient } from '@initia/initia.js' +import { ConnectionTable } from 'src/types' import { ClientController } from './client' import { insert, selectOne } from '../utils' +import { LCDClient } from 'src/lib/lcdClient' export class ConnectionController { static tableName = 'connection' @@ -11,9 +11,8 @@ export class ConnectionController { chainId: string, connectionId: string ): Promise { - const connectionInfo = await lcd.apiRequester.get( - `/ibc/core/connection/v1/connections/${connectionId}` - ) + const connectionInfo = await lcd.ibc.getConnection(connectionId) + const clientId = connectionInfo.connection.client_id const client = await ClientController.getClient(lcd, chainId, clientId) diff --git a/src/db/controller/packet.ts b/src/db/controller/packet.ts index 518d96d..17fdc9b 100644 --- a/src/db/controller/packet.ts +++ b/src/db/controller/packet.ts @@ -11,9 +11,9 @@ import { } from 'src/types' import { DB } from '..' import { In, WhereOptions, del, insert, select, update } from '../utils' -import { LCDClient } from '@initia/initia.js' import { ConnectionController } from './connection' import { Database } from 'better-sqlite3' +import { LCDClient } from 'src/lib/lcdClient' export class PacketController { private static tableNamePacketSend = 'packet_send' @@ -61,6 +61,7 @@ export class PacketController { limit = 100 ): PacketSendTable[] { const wheres: WhereOptions[] = [] + const deleteWheres: WhereOptions[] = [] if (filter.connections) { wheres.push( ...filter.connections.map((conn) => ({ @@ -72,6 +73,17 @@ export class PacketController { custom: `(timeout_height > ${height} OR timeout_timestamp > ${timestamp})`, })) ) + + deleteWheres.push( + ...filter.connections.map((conn) => ({ + in_progress: Boolean.TRUE, + dst_chain_id: chainId, + dst_connection_id: conn.connectionId, + dst_channel_id: conn.channels ? In(conn.channels) : undefined, + src_chain_id: In(counterpartyChainIds), // TODO: make this more efficientnet, like filter it on outside of this. + custom: `(timeout_height > ${height} OR timeout_timestamp > ${timestamp})`, + })) + ) } else { wheres.push({ in_progress: Boolean.FALSE, @@ -79,8 +91,18 @@ export class PacketController { src_chain_id: In(counterpartyChainIds), custom: `(timeout_height > ${height} OR timeout_timestamp > ${timestamp})`, }) + + deleteWheres.push({ + in_progress: Boolean.TRUE, + dst_chain_id: chainId, + src_chain_id: In(counterpartyChainIds), + custom: `(timeout_height > ${height} OR timeout_timestamp > ${timestamp})`, + }) } + // delete with same where condition but in_progress is false (which means those packets were filtered) + del(DB, this.tableNamePacketSend, deleteWheres) + return select( DB, this.tableNamePacketSend, @@ -99,6 +121,7 @@ export class PacketController { limit = 100 ): PacketTimeoutTable[] { const wheres: WhereOptions[] = [] + const deleteWheres: WhereOptions[] = [] if (filter.connections) { wheres.push( @@ -111,6 +134,16 @@ export class PacketController { custom: `((timeout_height < ${height} AND timeout_height != 0) OR timeout_timestamp < ${timestamp} AND timeout_timestamp != 0)`, })) ) + deleteWheres.push( + ...filter.connections.map((conn) => ({ + in_progress: Boolean.TRUE, + src_chain_id: chainId, + src_connection_id: conn.connectionId, + src_channel_id: conn.channels ? In(conn.channels) : undefined, + dst_chain_id: In(counterpartyChainIds), // TODO: make this more efficientnet, like filter it on outside of this. + custom: `((timeout_height < ${height} AND timeout_height != 0) OR timeout_timestamp < ${timestamp} AND timeout_timestamp != 0)`, + })) + ) } else { wheres.push({ in_progress: Boolean.FALSE, @@ -118,8 +151,17 @@ export class PacketController { dst_chain_id: In(counterpartyChainIds), custom: `((timeout_height < ${height} AND timeout_height != 0) OR timeout_timestamp < ${timestamp} AND timeout_timestamp != 0)`, }) + deleteWheres.push({ + in_progress: Boolean.TRUE, + src_chain_id: chainId, + dst_chain_id: In(counterpartyChainIds), + custom: `((timeout_height < ${height} AND timeout_height != 0) OR timeout_timestamp < ${timestamp} AND timeout_timestamp != 0)`, + }) } + // delete with same where condition but in_progress is false (which means those packets were filtered) + del(DB, this.tableNamePacketTimeout, deleteWheres) + return select( DB, this.tableNamePacketTimeout, @@ -136,6 +178,7 @@ export class PacketController { limit = 100 ): PacketWriteAckTable[] { const wheres: WhereOptions[] = [] + const deleteWheres: WhereOptions[] = [] if (filter.connections) { wheres.push( @@ -147,14 +190,31 @@ export class PacketController { dst_chain_id: In(counterpartyChainIds), // TODO: make this more efficientnet, like filter it on outside of this. })) ) + deleteWheres.push( + ...filter.connections.map((conn) => ({ + in_progress: Boolean.TRUE, + src_chain_id: chainId, + src_connection_id: conn.connectionId, + src_channel_id: conn.channels ? In(conn.channels) : undefined, + dst_chain_id: In(counterpartyChainIds), // TODO: make this more efficientnet, like filter it on outside of this. + })) + ) } else { wheres.push({ in_progress: Boolean.FALSE, src_chain_id: chainId, dst_chain_id: In(counterpartyChainIds), }) + deleteWheres.push({ + in_progress: Boolean.TRUE, + src_chain_id: chainId, + dst_chain_id: In(counterpartyChainIds), + }) } + // delete with same where condition but in_progress is false (which means those packets were filtered) + del(DB, this.tableNamePacketWriteAck, deleteWheres) + return select( DB, this.tableNamePacketWriteAck, diff --git a/src/db/migration/0.0.1.sql b/src/db/migration/0.0.1.sql index 3da4714..7e01666 100644 --- a/src/db/migration/0.0.1.sql +++ b/src/db/migration/0.0.1.sql @@ -116,4 +116,23 @@ CREATE TABLE packet_write_ack ( timeout_timestamp_raw TEXT NOT NULL, PRIMARY KEY (src_chain_id, src_connection_id, src_channel_id, sequence) +); + +-- create channel on open table +CREATE TABLE channel_on_open ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + + -- in progress + in_progress BOOLEAN, + + -- channel data + state BIGINT NOT NULL, -- 1: INIT, 2: TRYOPEN + chain_id TEXT NOT NULL, -- chain_id that need to execute msg + connection_id TEXT NOT NULL, + port_id TEXT NOT NULL, + channel_id TEXT NOT NULL, + counterparty_chain_id TEXT NOT NULL, + counterparty_connection_id TEXT NOT NULL, + counterparty_port_id TEXT NOT NULL, + counterparty_channel_id TEXT NOT NULL ); \ No newline at end of file diff --git a/src/db/migration/index.ts b/src/db/migration/index.ts index b00c764..32e3e43 100644 --- a/src/db/migration/index.ts +++ b/src/db/migration/index.ts @@ -2,6 +2,7 @@ import { Database } from 'better-sqlite3' import fs from 'fs' import { VersionTable } from 'src/types' import { PacketController } from '../controller/packet' +import { ChannelController } from '../controller/channel' export function migrate(db: Database): Database { // create version table @@ -42,6 +43,7 @@ export function migrate(db: Database): Database { // reset in progress PacketController.resetPacketInProgress(db) + ChannelController.resetPacketInProgress(db) return db } diff --git a/src/db/utils.ts b/src/db/utils.ts index 08ec360..b1ae6ac 100644 --- a/src/db/utils.ts +++ b/src/db/utils.ts @@ -188,6 +188,8 @@ function where(wheres: WhereOptions[]): [string, ParamType[]] { } } else if (key === 'custom') { condition.push(value) + } else if (typeof value === 'undefined') { + // do nothing } else { condition.push(`${String(key)} = ?`) params.push(toParamType(value)) diff --git a/src/lib/eventParser.ts b/src/lib/eventParser.ts index af6c035..8ec9a51 100644 --- a/src/lib/eventParser.ts +++ b/src/lib/eventParser.ts @@ -1,5 +1,5 @@ import { Event } from '@cosmjs/tendermint-rpc/build/comet38/responses' -import { PacketInfo } from 'src/types' +import { ChannelOpenInfo, PacketInfo } from 'src/types' export function parsePacketEvent(event: Event, height: number): PacketInfo { const connectionId = event.attributes.filter( @@ -65,3 +65,41 @@ export function parsePacketEvent(event: Event, height: number): PacketInfo { ack, } } + +export function parseChannelOpenEvent( + event: Event, + height: number +): ChannelOpenInfo { + const isSrc = + event.type === 'channel_open_init' || event.type === 'channel_open_ack' + + const connectionId = find(event, 'connection_id') + + const portId = find(event, 'port_id') + + const channelId = find(event, 'channel_id') + + const counterpartyPortId = find(event, 'counterparty_port_id') + + const counterpartyChannelId = find(event, 'counterparty_channel_id') + + return { + height, + srcConnectionId: isSrc ? connectionId : '', + srcPortId: isSrc ? portId : counterpartyPortId, + srcChannelId: isSrc ? channelId : counterpartyChannelId, + dstConnectionId: isSrc ? '' : connectionId, + dstPortId: isSrc ? counterpartyPortId : portId, + dstChannelId: isSrc ? counterpartyChannelId : channelId, + } +} + +function find(event: Event, key: string, defaultValue = ''): string { + const filtered = event.attributes.filter((v) => v.key === key) + + if (filtered.length === 0) { + return defaultValue + } + + return filtered[0].value +} diff --git a/src/lib/lcdClient.ts b/src/lib/lcdClient.ts new file mode 100644 index 0000000..d3fe2c2 --- /dev/null +++ b/src/lib/lcdClient.ts @@ -0,0 +1,90 @@ +import { + APIRequester, + Channel, + IbcAPI as IbcAPI_, + LCDClientConfig, + LCDClient as LCDClient_, +} from '@initia/initia.js' +import { Order, State } from '@initia/initia.proto/ibc/core/channel/v1/channel' +import { ClientState, ConnectionInfo } from 'src/types' + +export class LCDClient extends LCDClient_ { + public ibc: IbcAPI + constructor( + URL: string, + config?: LCDClientConfig, + apiRequester?: APIRequester + ) { + super(URL, config, apiRequester) + + this.ibc = new IbcAPI(this.apiRequester) + } +} + +class IbcAPI extends IbcAPI_ { + constructor(c: APIRequester) { + super(c) + } + + async channel(portId: string, channelId: string): Promise { + const rawRes = await this.c.get( + `/ibc/core/channel/v1/channels/${channelId}/ports/${portId}` + ) + + const state = + State[rawRes.channel.state as keyof typeof State] || State.UNRECOGNIZED + const ordering = + Order[rawRes.channel.ordering as keyof typeof Order] || Order.UNRECOGNIZED + + return { + channel: { + state, + ordering, + counterparty: rawRes.channel.counterparty, + connection_hops: rawRes.channel.connection_hops, + version: rawRes.channel.version, + }, + proof: rawRes.proof, + proof_height: rawRes.proof_height, + } + } + + async getClientState(clientId: string): Promise { + return this.c.get( + `/ibc/core/client/v1/client_states/${clientId}` + ) + } + + async getConnection(connectionId: string): Promise { + return this.c.get( + `/ibc/core/connection/v1/connections/${connectionId}` + ) + } +} + +interface ChannelResponse { + channel: Channel.Data + proof: null | string + proof_height: { + revision_number: number + revision_height: number + } +} + +interface ChannelResponseRaw { + channel: { + state: string + ordering: string + counterparty?: { + port_id: string + channel_id: string + } + connection_hops: string[] + version: string + } + proof: null | string + proof_height: { + revision_number: number + revision_height: number + } +} diff --git a/src/lib/logger.ts b/src/lib/logger.ts index 1cb69fd..603bf81 100644 --- a/src/lib/logger.ts +++ b/src/lib/logger.ts @@ -29,11 +29,9 @@ export function debug(msg: string): void { } export function createLoggerWithPrefix(prefix: string) { - const format = winston.format.printf( - ({ level, message, prefix, timestamp }) => { - return `${timestamp} ${level}: ${prefix}${message}` - } - ) + const format = winston.format.printf(({ level, message, timestamp }) => { + return `${timestamp} ${level}: ${prefix}${message}` + }) return winston.createLogger({ level: config.logLevel, diff --git a/src/lib/rawProof.ts b/src/lib/proof.ts similarity index 63% rename from src/lib/rawProof.ts rename to src/lib/proof.ts index 07760c5..79c03f2 100644 --- a/src/lib/rawProof.ts +++ b/src/lib/proof.ts @@ -3,6 +3,8 @@ import { tendermint34 } from '@cosmjs/tendermint-rpc' import { Height } from 'cosmjs-types/ibc/core/client/v1/client' import { ProofOps } from 'cosmjs-types/tendermint/crypto/proof' import { ChainWorker } from 'src/workers/chain' +import { CommitmentProof } from 'cosmjs-types/cosmos/ics23/v1/proofs' +import { MerkleProof } from 'cosmjs-types/ibc/core/commitment/v1/commitment' export async function getRawProof( chain: ChainWorker, @@ -42,6 +44,29 @@ export async function getRawProof( } } +export async function getChannelProof( + srcChain: ChainWorker, + srcPortId: string, + srcChannelId: string, + headerHeight: Height +): Promise { + const key = new Uint8Array( + Buffer.from(`channelEnds/ports/${srcPortId}/channels/${srcChannelId}`) + ) + const proof = await getRawProof(srcChain, key, headerHeight) + const ics23Proof = convertProofsToIcs23(proof) + + return Buffer.from(ics23Proof).toString('base64') +} + +export function convertProofsToIcs23(ops: ProofOps): Uint8Array { + const proofs = ops.ops.map((op) => CommitmentProof.decode(op.data)) + const resp = MerkleProof.fromPartial({ + proofs, + }) + return MerkleProof.encode(resp).finish() +} + function checkAndParseOp( op: tendermint34.ProofOp, kind: string diff --git a/src/msgs/ack.ts b/src/msgs/ack.ts index c8396f7..d958144 100644 --- a/src/msgs/ack.ts +++ b/src/msgs/ack.ts @@ -1,12 +1,8 @@ import { Packet } from '@initia/initia.js' import { MsgAcknowledgement } from '@initia/initia.js/dist/core/ibc/core/channel/msgs' import { Height } from 'cosmjs-types/ibc/core/client/v1/client' -import { ProofOps } from 'cosmjs-types/tendermint/crypto/proof' -import { CommitmentProof } from 'cosmjs-types/cosmos/ics23/v1/proofs' -import { MerkleProof } from 'cosmjs-types/ibc/core/commitment/v1/commitment' - import { Transfrom } from 'src/lib/transform' -import { getRawProof } from 'src/lib/rawProof' +import { convertProofsToIcs23, getRawProof } from 'src/lib/proof' import { ChainWorker } from 'src/workers/chain' import { PacketWriteAckTable } from 'src/types' import { packetTableToPacket } from 'src/db/utils' @@ -48,14 +44,6 @@ async function getAckProof( return Buffer.from(ics23Proof).toString('base64') } -export function convertProofsToIcs23(ops: ProofOps): Uint8Array { - const proofs = ops.ops.map((op) => CommitmentProof.decode(op.data)) - const resp = MerkleProof.fromPartial({ - proofs, - }) - return MerkleProof.encode(resp).finish() -} - export interface Ack { acknowledgement: string packet: Packet diff --git a/src/msgs/channelOpenAck.ts b/src/msgs/channelOpenAck.ts new file mode 100644 index 0000000..f20a321 --- /dev/null +++ b/src/msgs/channelOpenAck.ts @@ -0,0 +1,29 @@ +import { MsgChannelOpenAck } from '@initia/initia.js/dist/core/ibc/core/channel/msgs' +import { Height } from 'cosmjs-types/ibc/core/client/v1/client' +import { getChannelProof } from 'src/lib/proof' +import { ChainWorker } from 'src/workers/chain' +import { Transfrom } from 'src/lib/transform' + +export async function generateMsgChannelOpenAck( + srcPortId: string, + srcChannelId: string, + dstChain: ChainWorker, + dstPortId: string, + dstChannelId: string, + height: Height, + msgExecutor: string +): Promise { + const { + channel: { version }, + } = await dstChain.lcd.ibc.channel(dstPortId, dstChannelId) + + return new MsgChannelOpenAck( + srcPortId, + srcChannelId, + dstChannelId, + version, + await getChannelProof(dstChain, dstPortId, dstChannelId, height), + Transfrom.height(height), + msgExecutor + ) +} diff --git a/src/msgs/channelOpenConfirm.ts b/src/msgs/channelOpenConfirm.ts new file mode 100644 index 0000000..99c5d89 --- /dev/null +++ b/src/msgs/channelOpenConfirm.ts @@ -0,0 +1,23 @@ +import { MsgChannelOpenConfirm } from '@initia/initia.js/dist/core/ibc/core/channel/msgs' +import { Height } from 'cosmjs-types/ibc/core/client/v1/client' +import { getChannelProof } from 'src/lib/proof' +import { ChainWorker } from 'src/workers/chain' +import { Transfrom } from 'src/lib/transform' + +export async function generateMsgChannelOpenConfirm( + srcChain: ChainWorker, + srcPortId: string, + srcChannelId: string, + dstPortId: string, + dstChannelId: string, + height: Height, + msgExecutor: string +): Promise { + return new MsgChannelOpenConfirm( + dstPortId, + dstChannelId, + await getChannelProof(srcChain, srcPortId, srcChannelId, height), + Transfrom.height(height), + msgExecutor + ) +} diff --git a/src/msgs/channelOpenTry.ts b/src/msgs/channelOpenTry.ts new file mode 100644 index 0000000..2831cce --- /dev/null +++ b/src/msgs/channelOpenTry.ts @@ -0,0 +1,38 @@ +import { Channel, ChannelCounterparty, State } from '@initia/initia.js' +import { MsgChannelOpenTry } from '@initia/initia.js/dist/core/ibc/core/channel/msgs' +import { Height } from 'cosmjs-types/ibc/core/client/v1/client' +import { getChannelProof } from 'src/lib/proof' +import { ChainWorker } from 'src/workers/chain' +import { Transfrom } from 'src/lib/transform' + +export async function generateMsgChannelOpenTry( + srcChain: ChainWorker, + srcPortId: string, + srcChannelId: string, + dstConnectionId: string, + dstPortId: string, + height: Height, + msgExecutor: string +): Promise { + const { + channel: { ordering, version }, + } = await srcChain.lcd.ibc.channel(srcPortId, srcChannelId) + + const channel = new Channel( + State.STATE_TRYOPEN, + ordering, + new ChannelCounterparty(srcPortId, srcChannelId), + [dstConnectionId], + version + ) + + return new MsgChannelOpenTry( + dstPortId, + '', + channel, + version, + await getChannelProof(srcChain, srcPortId, srcChannelId, height), + Transfrom.height(height), + msgExecutor + ) +} diff --git a/src/msgs/index.ts b/src/msgs/index.ts index 865227f..c13a0a6 100644 --- a/src/msgs/index.ts +++ b/src/msgs/index.ts @@ -2,3 +2,6 @@ export * from './ack' export * from './recvPacket' export * from './timeout' export * from './updateClient' +export * from './channelOpenAck' +export * from './channelOpenConfirm' +export * from './channelOpenTry' diff --git a/src/msgs/recvPacket.ts b/src/msgs/recvPacket.ts index 0e4af81..483b9d6 100644 --- a/src/msgs/recvPacket.ts +++ b/src/msgs/recvPacket.ts @@ -2,8 +2,7 @@ import { Packet } from '@initia/initia.js' import { MsgRecvPacket } from '@initia/initia.js/dist/core/ibc/core/channel/msgs' import { Height } from 'cosmjs-types/ibc/core/client/v1/client' import { Transfrom } from 'src/lib/transform' -import { getRawProof } from 'src/lib/rawProof' -import { convertProofsToIcs23 } from './ack' +import { convertProofsToIcs23, getRawProof } from 'src/lib/proof' import { ChainWorker } from 'src/workers/chain' import { PacketSendTable } from 'src/types' import { packetTableToPacket } from 'src/db/utils' diff --git a/src/msgs/timeout.ts b/src/msgs/timeout.ts index e1f779e..5dc6b4e 100644 --- a/src/msgs/timeout.ts +++ b/src/msgs/timeout.ts @@ -3,8 +3,7 @@ import { Height } from 'cosmjs-types/ibc/core/client/v1/client' import { Uint64 } from '@cosmjs/math' import { Transfrom } from 'src/lib/transform' import { Packet } from '@initia/initia.js' -import { getRawProof } from 'src/lib/rawProof' -import { convertProofsToIcs23 } from './ack' +import { convertProofsToIcs23, getRawProof } from 'src/lib/proof' import { delay } from 'bluebird' import { ChainWorker } from 'src/workers/chain' import { PacketTimeoutTable } from 'src/types' diff --git a/src/types/event.ts b/src/types/event.ts index 19abe26..c7667a3 100644 --- a/src/types/event.ts +++ b/src/types/event.ts @@ -47,3 +47,22 @@ export interface UpdateClientEvent { header: string consensusHeights: string } + +export interface ChannelOpenEvent { + type: + | 'channel_open_init' + | 'channel_open_try' + | 'channel_open_ack' + | 'channel_open_confirm' + channelOpenInfo: ChannelOpenInfo +} + +export interface ChannelOpenInfo { + height: number + srcConnectionId: string + srcPortId: string + srcChannelId: string + dstConnectionId: string + dstPortId: string + dstChannelId: string +} diff --git a/src/types/table.ts b/src/types/table.ts index bec593a..b881781 100644 --- a/src/types/table.ts +++ b/src/types/table.ts @@ -85,7 +85,27 @@ export interface PacketWriteAckTable { timeout_timestamp_raw: string } +export interface ChannelOnOpenTable { + id?: number + in_progress: Boolean + state: ChannelState + chain_id: string + connection_id: string + port_id: string + channel_id: string + counterparty_chain_id: string + counterparty_connection_id: string + counterparty_port_id: string + counterparty_channel_id: string +} + export enum Boolean { TRUE = 1, FALSE = 0, } + +export enum ChannelState { + INIT = 1, + TRYOPEN = 2, + ACK = 3, +} diff --git a/src/workers/chain.ts b/src/workers/chain.ts index de24607..330bced 100644 --- a/src/workers/chain.ts +++ b/src/workers/chain.ts @@ -1,13 +1,14 @@ -import { LCDClient } from '@initia/initia.js' import { RPCClient } from 'src/lib/rpcClient' import { createLoggerWithPrefix } from 'src/lib/logger' -import { PacketEvent, PacketType } from 'src/types' -import { parsePacketEvent } from 'src/lib/eventParser' +import { ChannelOpenEvent, PacketEvent } from 'src/types' +import { parseChannelOpenEvent, parsePacketEvent } from 'src/lib/eventParser' import { DB } from 'src/db' import { SyncInfoController } from 'src/db/controller/syncInfo' import { PacketController } from 'src/db/controller/packet' import { delay } from 'bluebird' import { Logger } from 'winston' +import { LCDClient } from 'src/lib/lcdClient' +import { ChannelController } from 'src/db/controller/channel' export class ChainWorker { public latestHeight: number @@ -24,7 +25,6 @@ export class ChainWorker { startHeights: number[] ) { this.logger = createLoggerWithPrefix(``) - this.latestHeight = 0 const syncInfos = SyncInfoController.init( chainId, startHeights, @@ -122,9 +122,11 @@ class SyncWorker { if (heights.length === 0) continue - const packetEvenets = await Promise.all( - heights.map((height) => this.fetchPacketEvents(height)) + const events = await Promise.all( + heights.map((height) => this.fetchEvents(height)) ) + const packetEvents = events.map((e) => e.packetEvents).flat() + const channelOpenEvents = events.map((e) => e.channelOpenEvents).flat() this.logger.debug( `Fetched block results for heights (${JSON.stringify(heights)})` @@ -132,14 +134,21 @@ class SyncWorker { let finish = false - const feed = await PacketController.feedEvents( + const pakcetEventFeed = await PacketController.feedEvents( this.chain.lcd, this.chain.chainId, - packetEvenets.flat() + packetEvents.flat() + ) + + const channelOpenEventFeed = await ChannelController.feedEvents( + this.chain.lcd, + this.chain.chainId, + channelOpenEvents.flat() ) DB.transaction(() => { - feed() + pakcetEventFeed() + channelOpenEventFeed() finish = SyncInfoController.update( this.chain.chainId, @@ -149,7 +158,7 @@ class SyncWorker { ) this.logger.debug( - `Store packet events(${packetEvenets.flat().length})` + `Store packet events(${packetEvents.flat().length})` ) })() @@ -171,12 +180,16 @@ class SyncWorker { } } - private async fetchPacketEvents(height: number): Promise { + private async fetchEvents(height: number): Promise<{ + packetEvents: PacketEvent[] + channelOpenEvents: ChannelOpenEvent[] + }> { this.logger.debug(`Fecth new block results (height - ${height})`) const blockResult = await this.chain.rpc.blockResults(height) const txData = [...blockResult.results] const packetEvents: PacketEvent[] = [] + const channelOpenEvents: ChannelOpenEvent[] = [] txData.map((data, i) => { for (const event of data.events) { @@ -186,18 +199,30 @@ class SyncWorker { event.type === 'acknowledge_packet' || event.type === 'timeout_packet' ) { - let packetInfo = parsePacketEvent(event, height) - if (packetInfo) { - packetEvents.push({ - type: event.type as PacketType, - packetInfo, - }) - } + packetEvents.push({ + type: event.type, + packetInfo: parsePacketEvent(event, height), + }) + } + + if ( + event.type === 'channel_open_init' || + event.type === 'channel_open_try' || + event.type === 'channel_open_ack' || + event.type === 'channel_open_confirm' + ) { + channelOpenEvents.push({ + type: event.type, + channelOpenInfo: parseChannelOpenEvent(event, height), + }) } } }) - return packetEvents + return { + packetEvents, + channelOpenEvents, + } } } diff --git a/src/workers/index.ts b/src/workers/index.ts index 916ac5e..3658d76 100644 --- a/src/workers/index.ts +++ b/src/workers/index.ts @@ -5,11 +5,15 @@ import { generateMsgRecvPacket, generateMsgTimeout, generateMsgUpdateClient, + generateMsgChannelOpenTry, + generateMsgChannelOpenAck, + generateMsgChannelOpenConfirm, } from 'src/msgs' import { Height } from 'cosmjs-types/ibc/core/client/v1/client' import { MsgUpdateClient } from '@initia/initia.js/dist/core/ibc/core/client/msgs' import { ClientController } from 'src/db/controller/client' import { + ChannelOnOpenTable, PacketSendTable, PacketTimeoutTable, PacketWriteAckTable, @@ -19,7 +23,6 @@ import { Key, MnemonicKey, RawKey, - LCDClient, APIRequester, Wallet, } from '@initia/initia.js' @@ -29,6 +32,12 @@ import { RPCClient } from 'src/lib/rpcClient' import * as http from 'http' import * as https from 'https' import { PacketFilter } from 'src/db/controller/packet' +import { LCDClient } from 'src/lib/lcdClient' +import { + MsgChannelOpenAck, + MsgChannelOpenConfirm, + MsgChannelOpenTry, +} from '@initia/initia.js/dist/core/ibc/core/channel/msgs' export class WorkerController { public chains: Record // chainId => ChainWorker @@ -186,6 +195,57 @@ export class WorkerController { const dstChain = this.chains[packet.dst_chain_id] return generateMsgTimeout(dstChain, packet, height, executorAddress) } + + async generateChannelOpenTryMsg( + event: ChannelOnOpenTable, + height: Height, + executorAddress: string + ): Promise { + const srcChain = this.chains[event.counterparty_chain_id] + return generateMsgChannelOpenTry( + srcChain, + event.counterparty_port_id, + event.counterparty_channel_id, + event.connection_id, + event.port_id, + height, + executorAddress + ) + } + + async generateChannelOpenAckMsg( + event: ChannelOnOpenTable, + height: Height, + executorAddress: string + ): Promise { + const dstChain = this.chains[event.counterparty_chain_id] + return generateMsgChannelOpenAck( + event.port_id, + event.channel_id, + dstChain, + event.counterparty_port_id, + event.counterparty_channel_id, + height, + executorAddress + ) + } + + async generateChannelOpenConfirmMsg( + event: ChannelOnOpenTable, + height: Height, + executorAddress: string + ): Promise { + const srcChain = this.chains[event.counterparty_chain_id] + return generateMsgChannelOpenConfirm( + srcChain, + event.counterparty_port_id, + event.counterparty_channel_id, + event.port_id, + event.channel_id, + height, + executorAddress + ) + } } function createKey(rawKey: KeyConfig): Key { diff --git a/src/workers/wallet.ts b/src/workers/wallet.ts index a4295d1..30b8a58 100644 --- a/src/workers/wallet.ts +++ b/src/workers/wallet.ts @@ -1,6 +1,8 @@ import { PacketController, PacketFilter } from 'src/db/controller/packet' import { ChainWorker } from './chain' import { + ChannelOnOpenTable, + ChannelState, PacketSendTable, PacketTimeoutTable, PacketWriteAckTable, @@ -15,6 +17,8 @@ import { createLoggerWithPrefix } from 'src/lib/logger' import { bech32 } from 'bech32' import { delay } from 'bluebird' import { Logger } from 'winston' +import { ChannelController } from 'src/db/controller/channel' +import { State } from '@initia/initia.proto/ibc/core/channel/v1/channel' // TODO: add update client worker export class WalletWorker { @@ -46,7 +50,7 @@ export class WalletWorker { } private async handlePackets() { - if (this.chain.latestHeight === 0) return + if (this.chain.latestHeight === undefined) return // get packets to handle let remain = this.maxHandlePacket @@ -98,6 +102,17 @@ export class WalletWorker { remain ) + const channelOpenEvents = + remain === 0 + ? [] + : ChannelController.getOpenEvent( + this.chain.chainId, + counterpartyChainIds, + this.packetFilter, + undefined, + remain + ) + // update packet in progress DB.transaction(() => { sendPakcets.map((packet) => @@ -109,6 +124,7 @@ export class WalletWorker { timeoutPackets.map((packet) => PacketController.updateTimeoutPacketInProgress(packet) ) + channelOpenEvents.map((e) => ChannelController.updateInProgress(e.id)) })() try { @@ -118,11 +134,14 @@ export class WalletWorker { await this.filterWriteAckPackets(writeAckPackets) const filteredTimeoutPackets = await this.filterTimeoutPackets(timeoutPackets) + const filteredChannelOpenEvents = + await this.filterChannelOpenEvents(channelOpenEvents) if ( filteredSendPackets.length === 0 && filteredWriteAckPackets.length === 0 && - filteredTimeoutPackets.length === 0 + filteredTimeoutPackets.length === 0 && + filteredChannelOpenEvents.length === 0 ) { return } @@ -135,7 +154,8 @@ export class WalletWorker { ...filteredSendPackets.map((packet) => packet.dst_connection_id), ...filteredWriteAckPackets.map((packet) => packet.src_connection_id), ...filteredTimeoutPackets.map((packet) => packet.src_connection_id), - ].filter((v, i, a) => a.indexOf(v) === i) // filter by connection first + ...filteredChannelOpenEvents.map((event) => event.connection_id), + ].filter((v, i, a) => a.indexOf(v) === i) const connectionClientMap: Record = {} await Promise.all( @@ -212,11 +232,43 @@ export class WalletWorker { }) ) + // generate channel open msgs + const channelOpenMsgs = await Promise.all( + filteredChannelOpenEvents.map((event) => { + const clientId = connectionClientMap[event.connection_id] + const height = updateClientMsgs[clientId].height + + switch (event.state) { + case ChannelState.INIT: + return this.workerController.generateChannelOpenTryMsg( + event, + height, + this.address() + ) + // check src channel state + case ChannelState.TRYOPEN: + return this.workerController.generateChannelOpenAckMsg( + event, + height, + this.address() + ) + // check dst channel state + case ChannelState.ACK: + return this.workerController.generateChannelOpenConfirmMsg( + event, + height, + this.address() + ) + } + }) + ) + const msgs = [ ...Object.values(updateClientMsgs).map((v) => v.msg), ...recvPacketMsgs, ...ackMsgs, ...timeoutMsgs, + ...channelOpenMsgs, ] const signedTx = await this.wallet.createAndSignTx({ @@ -273,6 +325,9 @@ export class WalletWorker { timeoutPackets.map((packet) => PacketController.updateTimeoutPacketInProgress(packet, false) ) + channelOpenEvents.map((event) => + ChannelController.updateInProgress(event.id, false) + ) })() } } @@ -419,4 +474,62 @@ export class WalletWorker { return Object.values(timeoutPacketMap).flat() } + + private async filterChannelOpenEvents( + channelOnOpens: ChannelOnOpenTable[] + ): Promise { + // filter duplicated open try + channelOnOpens = channelOnOpens.filter((v, i, a) => { + if (v.state !== ChannelState.TRYOPEN) { + return true + } + + return ( + i === + a.findIndex( + (val) => val.channel_id === v.channel_id && val.port_id === v.port_id + ) + ) + }) + + // check already executed + const res = await Promise.all( + channelOnOpens.map(async (v) => { + const counterpartyChain = + this.workerController.chains[v.counterparty_chain_id] + const counterpartyChannel = await counterpartyChain.lcd.ibc.channel( + v.counterparty_port_id, + v.counterparty_channel_id + ) + const channel = + v.channel_id !== '' + ? await this.chain.lcd.ibc.channel(v.port_id, v.channel_id) + : undefined + switch (v.state) { + // check src channel state + case ChannelState.INIT: + if (counterpartyChannel.channel.state === State.STATE_INIT) { + return v + } + break + // check src channel state + case ChannelState.TRYOPEN: + if (channel && channel.channel.state === State.STATE_INIT) { + return v + } + break + // check dst channel state + case ChannelState.ACK: + if (channel && channel.channel.state === State.STATE_TRYOPEN) { + return v + } + break + } + + return undefined + }) + ) + + return res.filter((v) => v !== undefined) as ChannelOnOpenTable[] + } } From 9622103a12a9af952a4e9ce76ab66f2109570d2e Mon Sep 17 00:00:00 2001 From: ALPAC-4 Date: Tue, 8 Oct 2024 19:03:13 +0900 Subject: [PATCH 12/30] support packet fee filter --- src/db/controller/channel.ts | 31 ++-- src/db/controller/packet.ts | 261 ++++++++++++++++++++++----------- src/db/controller/packetFee.ts | 60 ++++++++ src/db/migration/0.0.1.sql | 29 +++- src/lib/config.ts | 12 ++ src/lib/eventParser.ts | 25 +++- src/types/event.ts | 9 ++ src/types/table.ts | 15 ++ src/workers/chain.ts | 24 ++- src/workers/index.ts | 10 +- src/workers/wallet.ts | 63 +++++++- 11 files changed, 416 insertions(+), 123 deletions(-) create mode 100644 src/db/controller/packetFee.ts diff --git a/src/db/controller/channel.ts b/src/db/controller/channel.ts index 92f8c9c..ff74b2e 100644 --- a/src/db/controller/channel.ts +++ b/src/db/controller/channel.ts @@ -53,7 +53,6 @@ export class ChannelController { limit = 100 ): ChannelOnOpenTable[] { const wheres: WhereOptions[] = [] - const deleteWheres: WhereOptions[] = [] if (filter.connections) { for (const connectionFilter of filter.connections) { @@ -65,13 +64,6 @@ export class ChannelController { connection_id: connectionFilter.connectionId, counterparty_chain_id: In(counterpartyChainIds), }) - deleteWheres.push({ - in_progress: Boolean.TRUE, - state, - chain_id: chainId, - connection_id: connectionFilter.connectionId, - counterparty_chain_id: In(counterpartyChainIds), - }) } } else { wheres.push({ @@ -80,17 +72,8 @@ export class ChannelController { chain_id: chainId, counterparty_chain_id: In(counterpartyChainIds), }) - deleteWheres.push({ - in_progress: Boolean.TRUE, - state, - chain_id: chainId, - counterparty_chain_id: In(counterpartyChainIds), - }) } - // delete with same where condition but in_progress is true (which means those packets were filtered) - del(DB, this.tableName, deleteWheres) - return select( DB, this.tableName, @@ -100,6 +83,20 @@ export class ChannelController { ) } + public static delOpenEvents(events: ChannelOnOpenTable[]) { + if (events.filter((event) => event.id === undefined).length !== 0) { + throw new Error('id must be exists to remove channel on open') + } + + if (events.length === 0) return + + del( + DB, + this.tableName, + events.map((v) => ({ id: v.id as number })) + ) + } + public static updateInProgress(id?: number, inProgress = true) { update( DB, diff --git a/src/db/controller/packet.ts b/src/db/controller/packet.ts index 17fdc9b..8856c86 100644 --- a/src/db/controller/packet.ts +++ b/src/db/controller/packet.ts @@ -8,12 +8,15 @@ import { PacketTimeoutTable, PacketWriteAckTable, Boolean, + FeeType, } from 'src/types' import { DB } from '..' import { In, WhereOptions, del, insert, select, update } from '../utils' import { ConnectionController } from './connection' import { Database } from 'better-sqlite3' import { LCDClient } from 'src/lib/lcdClient' +import { PacketFeeController } from './packetFee' +import { FeeFilter } from 'src/lib/config' export class PacketController { private static tableNamePacketSend = 'packet_send' @@ -56,60 +59,60 @@ export class PacketController { chainId: string, height: number, timestamp: number, - counterpartyChainIds: string[], + chainIdsWithFeeFilters: { chainId: string; feeFilter: FeeFilter }[], filter: PacketFilter = {}, limit = 100 ): PacketSendTable[] { - const wheres: WhereOptions[] = [] - const deleteWheres: WhereOptions[] = [] - if (filter.connections) { - wheres.push( - ...filter.connections.map((conn) => ({ + const res: PacketSendTable[] = [] + + // query for each chain id + for (const { + chainId: counterpartyChainId, + feeFilter, + } of chainIdsWithFeeFilters) { + const wheres: WhereOptions[] = [] + let custom = `(timeout_height > ${height} OR timeout_timestamp > ${timestamp})` // filter timeout packet + if (feeFilter.recvFee && feeFilter.recvFee.length !== 0) { + const conditions = feeFilter.recvFee.map( + (v) => + `((SELECT amount FROM packet_fee WHERE chain_id = packet_send.src_chain_id AND channel_id = packet_send.src_channel_id AND sequence = packet_send.sequence AND fee_type = ${FeeType.RECV} AND denom = '${v.denom}') >= ${v.amount})` + ) + custom += ` AND (${conditions.join(' OR ')})` + } + + if (filter.connections) { + // TODO: make this more efficientnet. filter connection by chain id + wheres.push( + ...filter.connections.map((conn) => ({ + in_progress: Boolean.FALSE, + dst_chain_id: chainId, + dst_connection_id: conn.connectionId, + dst_channel_id: conn.channels ? In(conn.channels) : undefined, + src_chain_id: counterpartyChainId, + custom, + })) + ) + } else { + wheres.push({ in_progress: Boolean.FALSE, dst_chain_id: chainId, - dst_connection_id: conn.connectionId, - dst_channel_id: conn.channels ? In(conn.channels) : undefined, - src_chain_id: In(counterpartyChainIds), // TODO: make this more efficientnet, like filter it on outside of this. - custom: `(timeout_height > ${height} OR timeout_timestamp > ${timestamp})`, - })) - ) + src_chain_id: counterpartyChainId, + custom, + }) + } - deleteWheres.push( - ...filter.connections.map((conn) => ({ - in_progress: Boolean.TRUE, - dst_chain_id: chainId, - dst_connection_id: conn.connectionId, - dst_channel_id: conn.channels ? In(conn.channels) : undefined, - src_chain_id: In(counterpartyChainIds), // TODO: make this more efficientnet, like filter it on outside of this. - custom: `(timeout_height > ${height} OR timeout_timestamp > ${timestamp})`, - })) + res.push( + ...select( + DB, + this.tableNamePacketSend, + wheres, + { sequence: 'ASC' }, + limit - res.length + ) ) - } else { - wheres.push({ - in_progress: Boolean.FALSE, - dst_chain_id: chainId, - src_chain_id: In(counterpartyChainIds), - custom: `(timeout_height > ${height} OR timeout_timestamp > ${timestamp})`, - }) - - deleteWheres.push({ - in_progress: Boolean.TRUE, - dst_chain_id: chainId, - src_chain_id: In(counterpartyChainIds), - custom: `(timeout_height > ${height} OR timeout_timestamp > ${timestamp})`, - }) } - // delete with same where condition but in_progress is false (which means those packets were filtered) - del(DB, this.tableNamePacketSend, deleteWheres) - - return select( - DB, - this.tableNamePacketSend, - wheres, - { sequence: 'ASC' }, - limit - ) + return res } public static getTimeoutPackets( @@ -117,31 +120,32 @@ export class PacketController { height: number, timestamp: number, counterpartyChainIds: string[], + feeFilter: FeeFilter, filter: PacketFilter = {}, limit = 100 ): PacketTimeoutTable[] { - const wheres: WhereOptions[] = [] - const deleteWheres: WhereOptions[] = [] + let custom = `((timeout_height < ${height} AND timeout_height != 0) OR timeout_timestamp < ${timestamp} AND timeout_timestamp != 0)` // filter timeout packet + + if (feeFilter.timeoutFee && feeFilter.timeoutFee.length !== 0) { + const conditions = feeFilter.timeoutFee.map( + (v) => + `((SELECT amount FROM packet_fee WHERE chain_id = packet_timeout.src_chain_id AND channel_id = packet_timeout.src_channel_id AND sequence = packet_timeout.sequence AND fee_type = ${FeeType.TIMEOUT} AND denom = '${v.denom}') >= ${v.amount})` + ) + custom += ` AND (${conditions.join(' OR ')})` + } + + const wheres: WhereOptions[] = [] if (filter.connections) { + // TODO: make this more efficientnet. filter connection by chain id wheres.push( ...filter.connections.map((conn) => ({ in_progress: Boolean.FALSE, src_chain_id: chainId, src_connection_id: conn.connectionId, src_channel_id: conn.channels ? In(conn.channels) : undefined, - dst_chain_id: In(counterpartyChainIds), // TODO: make this more efficientnet, like filter it on outside of this. - custom: `((timeout_height < ${height} AND timeout_height != 0) OR timeout_timestamp < ${timestamp} AND timeout_timestamp != 0)`, - })) - ) - deleteWheres.push( - ...filter.connections.map((conn) => ({ - in_progress: Boolean.TRUE, - src_chain_id: chainId, - src_connection_id: conn.connectionId, - src_channel_id: conn.channels ? In(conn.channels) : undefined, - dst_chain_id: In(counterpartyChainIds), // TODO: make this more efficientnet, like filter it on outside of this. - custom: `((timeout_height < ${height} AND timeout_height != 0) OR timeout_timestamp < ${timestamp} AND timeout_timestamp != 0)`, + dst_chain_id: In(counterpartyChainIds), + custom, })) ) } else { @@ -149,19 +153,10 @@ export class PacketController { in_progress: Boolean.FALSE, src_chain_id: chainId, dst_chain_id: In(counterpartyChainIds), - custom: `((timeout_height < ${height} AND timeout_height != 0) OR timeout_timestamp < ${timestamp} AND timeout_timestamp != 0)`, - }) - deleteWheres.push({ - in_progress: Boolean.TRUE, - src_chain_id: chainId, - dst_chain_id: In(counterpartyChainIds), - custom: `((timeout_height < ${height} AND timeout_height != 0) OR timeout_timestamp < ${timestamp} AND timeout_timestamp != 0)`, + custom, }) } - // delete with same where condition but in_progress is false (which means those packets were filtered) - del(DB, this.tableNamePacketTimeout, deleteWheres) - return select( DB, this.tableNamePacketTimeout, @@ -174,11 +169,21 @@ export class PacketController { public static getWriteAckPackets( chainId: string, counterpartyChainIds: string[], + feeFilter: FeeFilter, filter: PacketFilter = {}, limit = 100 ): PacketWriteAckTable[] { + let custom = 'TRUE' + + if (feeFilter.ackFee && feeFilter.ackFee.length !== 0) { + const conditions = feeFilter.ackFee.map( + (v) => + `((SELECT amount FROM packet_fee WHERE chain_id = packet_write_ack.src_chain_id AND channel_id = packet_write_ack.src_channel_id AND sequence = packet_write_ack.sequence AND fee_type = ${FeeType.ACK} AND denom = '${v.denom}') >= ${v.amount})` + ) + custom += ` AND (${conditions.join(' OR ')})` + } + const wheres: WhereOptions[] = [] - const deleteWheres: WhereOptions[] = [] if (filter.connections) { wheres.push( @@ -188,15 +193,7 @@ export class PacketController { src_connection_id: conn.connectionId, src_channel_id: conn.channels ? In(conn.channels) : undefined, dst_chain_id: In(counterpartyChainIds), // TODO: make this more efficientnet, like filter it on outside of this. - })) - ) - deleteWheres.push( - ...filter.connections.map((conn) => ({ - in_progress: Boolean.TRUE, - src_chain_id: chainId, - src_connection_id: conn.connectionId, - src_channel_id: conn.channels ? In(conn.channels) : undefined, - dst_chain_id: In(counterpartyChainIds), // TODO: make this more efficientnet, like filter it on outside of this. + custom, })) ) } else { @@ -204,17 +201,10 @@ export class PacketController { in_progress: Boolean.FALSE, src_chain_id: chainId, dst_chain_id: In(counterpartyChainIds), - }) - deleteWheres.push({ - in_progress: Boolean.TRUE, - src_chain_id: chainId, - dst_chain_id: In(counterpartyChainIds), + custom, }) } - // delete with same where condition but in_progress is false (which means those packets were filtered) - del(DB, this.tableNamePacketWriteAck, deleteWheres) - return select( DB, this.tableNamePacketWriteAck, @@ -224,6 +214,48 @@ export class PacketController { ) } + public static delSendPackets(packets: PacketSendTable[]) { + if (packets.length === 0) return + del( + DB, + this.tableNamePacketSend, + packets.map((packet) => ({ + dst_chain_id: packet.dst_chain_id, + dst_connection_id: packet.dst_connection_id, + dst_channel_id: packet.dst_channel_id, + sequence: packet.sequence, + })) + ) + } + + public static delTimeoutPackets(packets: PacketTimeoutTable[]) { + if (packets.length === 0) return + del( + DB, + this.tableNamePacketTimeout, + packets.map((packet) => ({ + src_chain_id: packet.src_chain_id, + src_connection_id: packet.src_connection_id, + src_channel_id: packet.src_channel_id, + sequence: packet.sequence, + })) + ) + } + + public static delWriteAckPackets(packets: PacketWriteAckTable[]) { + if (packets.length === 0) return + del( + DB, + this.tableNamePacketWriteAck, + packets.map((packet) => ({ + src_chain_id: packet.src_chain_id, + src_connection_id: packet.src_connection_id, + src_channel_id: packet.src_channel_id, + sequence: packet.sequence, + })) + ) + } + public static updateSendPacketInProgress( packet: PacketSendTable, inProgress = true @@ -393,6 +425,21 @@ export class PacketController { sequence: Number(event.packetInfo.sequence), }, ]) + + // remove packet fees + PacketFeeController.removePacketFee( + packetWriteAck.src_chain_id, + packetWriteAck.src_channel_id, + packetWriteAck.sequence, + FeeType.RECV + ) + PacketFeeController.removePacketFee( + packetWriteAck.src_chain_id, + packetWriteAck.src_channel_id, + packetWriteAck.sequence, + FeeType.TIMEOUT + ) + insert(DB, this.tableNamePacketWriteAck, packetWriteAck) } } @@ -439,6 +486,26 @@ export class PacketController { sequence: Number(event.packetInfo.sequence), }, ]) + + // remove packet fees + PacketFeeController.removePacketFee( + chainId, + event.packetInfo.srcChannel, + event.packetInfo.sequence, + FeeType.RECV + ) + PacketFeeController.removePacketFee( + chainId, + event.packetInfo.srcChannel, + event.packetInfo.sequence, + FeeType.TIMEOUT + ) + PacketFeeController.removePacketFee( + chainId, + event.packetInfo.srcChannel, + event.packetInfo.sequence, + FeeType.ACK + ) } } @@ -473,6 +540,26 @@ export class PacketController { sequence: Number(event.packetInfo.sequence), }, ]) + + // remove packet fees + PacketFeeController.removePacketFee( + chainId, + event.packetInfo.srcChannel, + event.packetInfo.sequence, + FeeType.RECV + ) + PacketFeeController.removePacketFee( + chainId, + event.packetInfo.srcChannel, + event.packetInfo.sequence, + FeeType.TIMEOUT + ) + PacketFeeController.removePacketFee( + chainId, + event.packetInfo.srcChannel, + event.packetInfo.sequence, + FeeType.ACK + ) } } } diff --git a/src/db/controller/packetFee.ts b/src/db/controller/packetFee.ts new file mode 100644 index 0000000..06b3783 --- /dev/null +++ b/src/db/controller/packetFee.ts @@ -0,0 +1,60 @@ +import { DB } from '..' +import { FeeType, PacketFeeEvent, PacketFeeTable } from 'src/types' +import { del, insert } from '../utils' +import { Coin, Coins } from '@initia/initia.js' + +export class PacketFeeController { + static tableName = 'packet_fee' + + public static feedEvents( + chainId: string, + events: PacketFeeEvent[] + ): () => void { + const feedFns: (() => void)[] = [] + const toFn = ( + event: PacketFeeEvent, + coins: Coin[], + type: FeeType + ): (() => void) => { + return () => { + for (const coin of coins) { + const packetFee: PacketFeeTable = { + chain_id: chainId, + channel_id: event.channelId, + sequence: event.sequence, + fee_type: type, + denom: coin.denom, + amount: Number(coin.amount), + } + + insert(DB, this.tableName, packetFee) + } + } + } + for (const event of events) { + const recvFee = new Coins(event.recvFee).toArray() + feedFns.push(toFn(event, recvFee, FeeType.RECV)) + const ackFee = new Coins(event.ackFee).toArray() + feedFns.push(toFn(event, ackFee, FeeType.ACK)) + const timeoutFee = new Coins(event.timeoutFee).toArray() + feedFns.push(toFn(event, timeoutFee, FeeType.TIMEOUT)) + } + + return () => { + for (const fn of feedFns) { + fn() + } + } + } + + public static removePacketFee( + chainId: string, + channelId: string, + sequence: number, + type: FeeType + ) { + del(DB, this.tableName, [ + { chain_id: chainId, channel_id: channelId, sequence, fee_type: type }, + ]) + } +} diff --git a/src/db/migration/0.0.1.sql b/src/db/migration/0.0.1.sql index 7e01666..bb5f87e 100644 --- a/src/db/migration/0.0.1.sql +++ b/src/db/migration/0.0.1.sql @@ -39,7 +39,6 @@ CREATE TABLE connection ( CREATE TABLE packet_send ( -- pk dst_chain_id TEXT NOT NULL, - dst_connection_id TEXT NOT NULL, dst_channel_id TEXT NOT NULL, sequence BIGINT NOT NULL, @@ -48,6 +47,7 @@ CREATE TABLE packet_send ( -- packet data height BIGINT NOT NULL, + dst_connection_id TEXT NOT NULL, dst_port TEXT NOT NULL, src_chain_id TEXT NOT NULL, -- add this for filtering src_connection_id TEXT NOT NULL, @@ -59,7 +59,7 @@ CREATE TABLE packet_send ( timeout_height_raw TEXT NOT NULL, timeout_timestamp_raw TEXT NOT NULL, - PRIMARY KEY (dst_chain_id, dst_connection_id, dst_channel_id, sequence) + PRIMARY KEY (dst_chain_id, dst_channel_id, sequence) ); -- create packet timeout table, table for execute timeout @@ -67,7 +67,6 @@ CREATE TABLE packet_send ( CREATE TABLE packet_timeout ( -- pk src_chain_id TEXT NOT NULL, - src_connection_id TEXT NOT NULL, src_channel_id TEXT NOT NULL, sequence BIGINT NOT NULL, @@ -75,6 +74,7 @@ CREATE TABLE packet_timeout ( in_progress BOOLEAN, -- packet data + src_connection_id TEXT NOT NULL, src_port TEXT NOT NULL, dst_chain_id TEXT NOT NULL, -- add this for filtering dst_connection_id TEXT NOT NULL, @@ -86,7 +86,7 @@ CREATE TABLE packet_timeout ( timeout_height_raw TEXT NOT NULL, timeout_timestamp_raw TEXT NOT NULL, - PRIMARY KEY (src_chain_id, src_connection_id, src_channel_id, sequence) + PRIMARY KEY (src_chain_id, src_channel_id, sequence) ); -- create packet write ack table, table for execute ack @@ -94,7 +94,6 @@ CREATE TABLE packet_timeout ( CREATE TABLE packet_write_ack ( -- pk src_chain_id TEXT NOT NULL, - src_connection_id TEXT NOT NULL, src_channel_id TEXT NOT NULL, sequence BIGINT NOT NULL, @@ -103,6 +102,7 @@ CREATE TABLE packet_write_ack ( -- packet data height BIGINT NOT NULL, + src_connection_id TEXT NOT NULL, src_port TEXT NOT NULL, dst_chain_id TEXT NOT NULL, -- add this for filtering dst_connection_id TEXT NOT NULL, @@ -115,7 +115,7 @@ CREATE TABLE packet_write_ack ( timeout_height_raw TEXT NOT NULL, timeout_timestamp_raw TEXT NOT NULL, - PRIMARY KEY (src_chain_id, src_connection_id, src_channel_id, sequence) + PRIMARY KEY (src_chain_id, src_channel_id, sequence) ); -- create channel on open table @@ -135,4 +135,19 @@ CREATE TABLE channel_on_open ( counterparty_connection_id TEXT NOT NULL, counterparty_port_id TEXT NOT NULL, counterparty_channel_id TEXT NOT NULL -); \ No newline at end of file +); + +-- create packet fee table +CREATE TABLE packet_fee ( + -- pk + chain_id TEXT NOT NULL, + channel_id TEXT NOT NULL, + sequence BIGINT NOT NULL, + fee_type BIGINT NOT NULL, -- 1: recv, 2: ack, 3: timeout + denom TEXT NOT NULL, + + -- fee data + amount BIGINT NOT NULL, + + PRIMARY KEY (chain_id, channel_id, sequence, fee_type, denom) +) \ No newline at end of file diff --git a/src/lib/config.ts b/src/lib/config.ts index c4090ba..a2ed969 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -20,6 +20,7 @@ interface ChainConfig { lcdUri: string rpcUri: string wallets: WalletConfig[] + feeFilter?: PacketFee } interface WalletConfig { @@ -29,6 +30,17 @@ interface WalletConfig { startHeight?: number } +interface PacketFee { + recvFee?: Coin[] + ackFee?: Coin[] + timeoutFee?: Coin[] +} + +interface Coin { + denom: string + amount: number +} + export interface KeyConfig { type: 'raw' | 'mnemonic' | 'env_raw' | 'env_mnemonic' privateKey: string diff --git a/src/lib/eventParser.ts b/src/lib/eventParser.ts index 8ec9a51..3c98102 100644 --- a/src/lib/eventParser.ts +++ b/src/lib/eventParser.ts @@ -1,5 +1,5 @@ import { Event } from '@cosmjs/tendermint-rpc/build/comet38/responses' -import { ChannelOpenInfo, PacketInfo } from 'src/types' +import { ChannelOpenInfo, PacketFeeEvent, PacketInfo } from 'src/types' export function parsePacketEvent(event: Event, height: number): PacketInfo { const connectionId = event.attributes.filter( @@ -94,6 +94,29 @@ export function parseChannelOpenEvent( } } +export function parsePacketFeeEvent(event: Event): PacketFeeEvent { + const portId = find(event, 'port_id') + + const channelId = find(event, 'channel_id') + + const sequence = Number(find(event, 'packet_sequence')) + + const recvFee = find(event, 'recv_fee') + + const ackFee = find(event, 'ack_fee') + + const timeoutFee = find(event, 'timeout_fee') + + return { + portId, + channelId, + sequence, + recvFee, + ackFee, + timeoutFee, + } +} + function find(event: Event, key: string, defaultValue = ''): string { const filtered = event.attributes.filter((v) => v.key === key) diff --git a/src/types/event.ts b/src/types/event.ts index c7667a3..7fe6647 100644 --- a/src/types/event.ts +++ b/src/types/event.ts @@ -66,3 +66,12 @@ export interface ChannelOpenInfo { dstPortId: string dstChannelId: string } + +export interface PacketFeeEvent { + portId: string + channelId: string + sequence: number + recvFee: string + ackFee: string + timeoutFee: string +} diff --git a/src/types/table.ts b/src/types/table.ts index b881781..8feeabb 100644 --- a/src/types/table.ts +++ b/src/types/table.ts @@ -99,6 +99,15 @@ export interface ChannelOnOpenTable { counterparty_channel_id: string } +export interface PacketFeeTable { + chain_id: string + channel_id: string + sequence: number + fee_type: FeeType + denom: string + amount: number +} + export enum Boolean { TRUE = 1, FALSE = 0, @@ -109,3 +118,9 @@ export enum ChannelState { TRYOPEN = 2, ACK = 3, } + +export enum FeeType { + RECV = 1, + ACK = 2, + TIMEOUT = 3, +} diff --git a/src/workers/chain.ts b/src/workers/chain.ts index 330bced..a357921 100644 --- a/src/workers/chain.ts +++ b/src/workers/chain.ts @@ -1,7 +1,11 @@ import { RPCClient } from 'src/lib/rpcClient' import { createLoggerWithPrefix } from 'src/lib/logger' -import { ChannelOpenEvent, PacketEvent } from 'src/types' -import { parseChannelOpenEvent, parsePacketEvent } from 'src/lib/eventParser' +import { ChannelOpenEvent, PacketEvent, PacketFeeEvent } from 'src/types' +import { + parseChannelOpenEvent, + parsePacketEvent, + parsePacketFeeEvent, +} from 'src/lib/eventParser' import { DB } from 'src/db' import { SyncInfoController } from 'src/db/controller/syncInfo' import { PacketController } from 'src/db/controller/packet' @@ -9,6 +13,8 @@ import { delay } from 'bluebird' import { Logger } from 'winston' import { LCDClient } from 'src/lib/lcdClient' import { ChannelController } from 'src/db/controller/channel' +import { PacketFeeController } from 'src/db/controller/packetFee' +import { FeeFilter } from 'src/lib/config' export class ChainWorker { public latestHeight: number @@ -21,6 +27,7 @@ export class ChainWorker { public lcd: LCDClient, public rpc: RPCClient, public bech32Prefix: string, + public feeFilter: FeeFilter, latestHeight: number, startHeights: number[] ) { @@ -127,6 +134,7 @@ class SyncWorker { ) const packetEvents = events.map((e) => e.packetEvents).flat() const channelOpenEvents = events.map((e) => e.channelOpenEvents).flat() + const packetFeeEvents = events.map((e) => e.packetFeeEvents).flat() this.logger.debug( `Fetched block results for heights (${JSON.stringify(heights)})` @@ -137,18 +145,19 @@ class SyncWorker { const pakcetEventFeed = await PacketController.feedEvents( this.chain.lcd, this.chain.chainId, - packetEvents.flat() + packetEvents ) const channelOpenEventFeed = await ChannelController.feedEvents( this.chain.lcd, this.chain.chainId, - channelOpenEvents.flat() + channelOpenEvents ) DB.transaction(() => { pakcetEventFeed() channelOpenEventFeed() + PacketFeeController.feedEvents(this.chain.chainId, packetFeeEvents)() finish = SyncInfoController.update( this.chain.chainId, @@ -183,6 +192,7 @@ class SyncWorker { private async fetchEvents(height: number): Promise<{ packetEvents: PacketEvent[] channelOpenEvents: ChannelOpenEvent[] + packetFeeEvents: PacketFeeEvent[] }> { this.logger.debug(`Fecth new block results (height - ${height})`) const blockResult = await this.chain.rpc.blockResults(height) @@ -190,6 +200,7 @@ class SyncWorker { const packetEvents: PacketEvent[] = [] const channelOpenEvents: ChannelOpenEvent[] = [] + const packetFeeEvents: PacketFeeEvent[] = [] txData.map((data, i) => { for (const event of data.events) { @@ -216,12 +227,17 @@ class SyncWorker { channelOpenInfo: parseChannelOpenEvent(event, height), }) } + + if (event.type === 'incentivized_ibc_packet') { + packetFeeEvents.push(parsePacketFeeEvent(event)) + } } }) return { packetEvents, channelOpenEvents, + packetFeeEvents, } } } diff --git a/src/workers/index.ts b/src/workers/index.ts index 3658d76..badff5a 100644 --- a/src/workers/index.ts +++ b/src/workers/index.ts @@ -26,7 +26,7 @@ import { APIRequester, Wallet, } from '@initia/initia.js' -import { Config, KeyConfig } from 'src/lib/config' +import { Config, FeeFilter, KeyConfig } from 'src/lib/config' import { env } from 'node:process' import { RPCClient } from 'src/lib/rpcClient' import * as http from 'http' @@ -76,6 +76,7 @@ export class WorkerController { lcd, rpc, chainConfig.bech32Prefix, + chainConfig.feeFilter ?? {}, latestHeight, chainConfig.wallets .map((wallet) => wallet.startHeight) @@ -99,8 +100,11 @@ export class WorkerController { } } - public getChainIds(): string[] { - return Object.keys(this.chains) + public getFeeFilters(): { chainId: string; feeFilter: FeeFilter }[] { + return Object.keys(this.chains).map((chainId) => ({ + chainId, + feeFilter: this.chains[chainId].feeFilter, + })) } public getStatus(): { chains: ChainStatus[] } { diff --git a/src/workers/wallet.ts b/src/workers/wallet.ts index 30b8a58..f3b7d6b 100644 --- a/src/workers/wallet.ts +++ b/src/workers/wallet.ts @@ -19,6 +19,7 @@ import { delay } from 'bluebird' import { Logger } from 'winston' import { ChannelController } from 'src/db/controller/channel' import { State } from '@initia/initia.proto/ibc/core/channel/v1/channel' +import { FeeFilter } from 'src/lib/config' // TODO: add update client worker export class WalletWorker { @@ -55,15 +56,25 @@ export class WalletWorker { // get packets to handle let remain = this.maxHandlePacket - const counterpartyChainIds = this.workerController - .getChainIds() - .filter((v) => v !== this.chain.chainId) + const chainIdsWithFeeFilter = this.workerController.getFeeFilters() + const counterpartyChainIdsWithFeeFilter = chainIdsWithFeeFilter.filter( + (v) => v.chainId !== this.chain.chainId + ) + const counterpartyChainIds = counterpartyChainIdsWithFeeFilter.map( + (v) => v.chainId + ) + const feeFilter = ( + chainIdsWithFeeFilter.find((v) => v.chainId === this.chain.chainId) as { + chainId: string + feeFilter: FeeFilter + } + ).feeFilter const sendPakcets = PacketController.getSendPackets( this.chain.chainId, this.chain.latestHeight, Number((this.chain.latestTimestamp / 1000).toFixed()), - counterpartyChainIds, + counterpartyChainIdsWithFeeFilter, this.packetFilter, remain ).filter( @@ -80,6 +91,7 @@ export class WalletWorker { : PacketController.getWriteAckPackets( this.chain.chainId, counterpartyChainIds, + feeFilter, this.packetFilter, remain ).filter( @@ -98,6 +110,7 @@ export class WalletWorker { this.chain.latestHeight, Number((this.chain.latestTimestamp / 1000).toFixed()), counterpartyChainIds, + feeFilter, this.packetFilter, remain ) @@ -342,6 +355,7 @@ export class WalletWorker { ): Promise { // create path => packet map const sendPacketMap: Record = {} + const sendPacketsToDel: PacketSendTable[] = [] for (const packet of sendPackets) { const path = `${packet.dst_port}/${packet.dst_channel_id}` @@ -366,12 +380,21 @@ export class WalletWorker { Number(sequence) ) + sendPacketsToDel.push( + ...sendPacketMap[path].filter( + (packet) => !unrecivedSequences.includes(packet.sequence) + ) + ) + sendPacketMap[path] = sendPacketMap[path].filter((packet) => unrecivedSequences.includes(packet.sequence) ) }) ) + // delete packets that already executed + PacketController.delSendPackets(sendPacketsToDel) + return Object.values(sendPacketMap).flat() } @@ -380,6 +403,7 @@ export class WalletWorker { ): Promise { // create path => packet map const writeAckPacketMap: Record = {} + const writeAckPacketsToDel: PacketWriteAckTable[] = [] for (const packet of writeAckPackets) { const path = `${packet.src_port}/${packet.src_port}` @@ -404,12 +428,21 @@ export class WalletWorker { Number(sequence) ) + writeAckPacketsToDel.push( + ...writeAckPacketMap[path].filter( + (packet) => !unrecivedSequences.includes(packet.sequence) + ) + ) + writeAckPacketMap[path] = writeAckPacketMap[path].filter((packet) => unrecivedSequences.includes(packet.sequence) ) }) ) + // delete packets that already executed + PacketController.delWriteAckPackets(writeAckPacketsToDel) + return Object.values(writeAckPacketMap).flat() } @@ -418,6 +451,7 @@ export class WalletWorker { ): Promise { // create path => packet map const timeoutPacketMap: Record = {} + const timeoutPacketsToDel: PacketTimeoutTable[] = [] for (const packet of timeoutPackets) { const path = `${packet.src_port}/${packet.src_port}` @@ -444,6 +478,12 @@ export class WalletWorker { Number(sequence) ) + timeoutPacketsToDel.push( + ...timeoutPacketMap[path].filter( + (packet) => !unrecivedSequences.includes(packet.sequence) + ) + ) + timeoutPacketMap[path] = timeoutPacketMap[path].filter((packet) => unrecivedSequences.includes(packet.sequence) ) @@ -466,12 +506,21 @@ export class WalletWorker { Number(sequence) ) + timeoutPacketsToDel.push( + ...timeoutPacketMap[path].filter( + (packet) => !unrecivedSequences.includes(packet.sequence) + ) + ) + timeoutPacketMap[path] = timeoutPacketMap[path].filter((packet) => unrecivedSequences.includes(packet.sequence) ) }) ) + // delete packets that already executed + PacketController.delTimeoutPackets(timeoutPacketsToDel) + return Object.values(timeoutPacketMap).flat() } @@ -492,6 +541,8 @@ export class WalletWorker { ) }) + const eventsToDel: ChannelOnOpenTable[] = [] + // check already executed const res = await Promise.all( channelOnOpens.map(async (v) => { @@ -526,10 +577,14 @@ export class WalletWorker { break } + eventsToDel.push(v) + return undefined }) ) + ChannelController.delOpenEvents(eventsToDel) + return res.filter((v) => v !== undefined) as ChannelOnOpenTable[] } } From 0571f4df86814969028e1e16403f898d34eeb2f3 Mon Sep 17 00:00:00 2001 From: ALPAC-4 Date: Mon, 14 Oct 2024 16:38:52 +0900 Subject: [PATCH 13/30] add channel close --- src/db/controller/channel.ts | 146 ++++++++++++++++++++++---------- src/db/controller/packet.ts | 12 +++ src/db/migration/0.0.1.sql | 13 ++- src/index.ts | 4 +- src/lib/eventParser.ts | 31 ++++++- src/lib/proof.ts | 10 +-- src/msgs/channelCloseConfirm.ts | 23 +++++ src/msgs/timeout.ts | 61 +++++++++++-- src/types/event.ts | 10 ++- src/types/table.ts | 6 +- src/workers/chain.ts | 20 ++++- src/workers/index.ts | 42 ++++++++- src/workers/wallet.ts | 74 ++++++++-------- 13 files changed, 345 insertions(+), 107 deletions(-) create mode 100644 src/msgs/channelCloseConfirm.ts diff --git a/src/db/controller/channel.ts b/src/db/controller/channel.ts index ff74b2e..90c92e5 100644 --- a/src/db/controller/channel.ts +++ b/src/db/controller/channel.ts @@ -1,8 +1,8 @@ import { DB } from '..' import { Boolean, - ChannelOnOpenTable, - ChannelOpenEvent, + ChannelOpenCloseTable, + ChannelOpenCloseEvent, ChannelState, } from 'src/types' import { In, WhereOptions, del, insert, select, update } from '../utils' @@ -12,11 +12,11 @@ import { PacketFilter } from './packet' import { Database } from 'better-sqlite3' export class ChannelController { - static tableName = 'channel_on_open' + static tableName = 'channel_open_close' public static async feedEvents( lcd: LCDClient, chainId: string, - events: ChannelOpenEvent[] + events: ChannelOpenCloseEvent[] ): Promise<() => void> { const feedFns: (() => void)[] = [] for (const event of events) { @@ -35,6 +35,17 @@ export class ChannelController { await this.feedChannelOpenConfirmEvent(lcd, chainId, event) ) break + case 'channel_close': + case 'channel_close_init': + feedFns.push( + await this.feedChannelCloseInitEvent(lcd, chainId, event) + ) + break + case 'channel_close_confirm': + feedFns.push( + await this.feedChannelCloseConfirmEvent(lcd, chainId, event) + ) + break } } @@ -51,8 +62,8 @@ export class ChannelController { filter: PacketFilter = {}, state?: ChannelState, limit = 100 - ): ChannelOnOpenTable[] { - const wheres: WhereOptions[] = [] + ): ChannelOpenCloseTable[] { + const wheres: WhereOptions[] = [] if (filter.connections) { for (const connectionFilter of filter.connections) { @@ -74,7 +85,7 @@ export class ChannelController { }) } - return select( + return select( DB, this.tableName, wheres, @@ -83,14 +94,14 @@ export class ChannelController { ) } - public static delOpenEvents(events: ChannelOnOpenTable[]) { + public static delOpenEvents(events: ChannelOpenCloseTable[]) { if (events.filter((event) => event.id === undefined).length !== 0) { throw new Error('id must be exists to remove channel on open') } if (events.length === 0) return - del( + del( DB, this.tableName, events.map((v) => ({ id: v.id as number })) @@ -98,7 +109,7 @@ export class ChannelController { } public static updateInProgress(id?: number, inProgress = true) { - update( + update( DB, this.tableName, { in_progress: inProgress ? Boolean.TRUE : Boolean.FALSE }, @@ -108,7 +119,7 @@ export class ChannelController { public static resetPacketInProgress(db?: Database) { db = db ?? DB - update(db, this.tableName, { + update(db, this.tableName, { in_progress: Boolean.FALSE, }) } @@ -116,26 +127,26 @@ export class ChannelController { private static async feedChannelOpenInitEvent( lcd: LCDClient, chainId: string, - event: ChannelOpenEvent + event: ChannelOpenCloseEvent ): Promise<() => void> { const connection = await ConnectionController.getConnection( lcd, chainId, - event.channelOpenInfo.srcConnectionId + event.channelOpenCloseInfo.srcConnectionId ) // add channel on open for dst chain - const channelOnOpen: ChannelOnOpenTable = { + const channelOnOpen: ChannelOpenCloseTable = { in_progress: Boolean.FALSE, state: ChannelState.INIT, chain_id: connection.counterparty_chain_id, connection_id: connection.counterparty_connection_id, - port_id: event.channelOpenInfo.dstPortId, - channel_id: event.channelOpenInfo.dstChannelId, + port_id: event.channelOpenCloseInfo.dstPortId, + channel_id: event.channelOpenCloseInfo.dstChannelId, counterparty_chain_id: chainId, - counterparty_connection_id: event.channelOpenInfo.srcConnectionId, - counterparty_port_id: event.channelOpenInfo.srcPortId, - counterparty_channel_id: event.channelOpenInfo.srcChannelId, + counterparty_connection_id: event.channelOpenCloseInfo.srcConnectionId, + counterparty_port_id: event.channelOpenCloseInfo.srcPortId, + counterparty_channel_id: event.channelOpenCloseInfo.srcChannelId, } return () => { @@ -146,30 +157,30 @@ export class ChannelController { private static async feedChannelOpenTryEvent( lcd: LCDClient, chainId: string, - event: ChannelOpenEvent + event: ChannelOpenCloseEvent ): Promise<() => void> { const connection = await ConnectionController.getConnection( lcd, chainId, - event.channelOpenInfo.dstConnectionId + event.channelOpenCloseInfo.dstConnectionId ) // add channel on open for src chain - const channelOnOpen: ChannelOnOpenTable = { + const channelOnOpen: ChannelOpenCloseTable = { in_progress: Boolean.FALSE, state: ChannelState.TRYOPEN, chain_id: connection.counterparty_chain_id, connection_id: connection.counterparty_connection_id, - port_id: event.channelOpenInfo.srcPortId, - channel_id: event.channelOpenInfo.srcChannelId, + port_id: event.channelOpenCloseInfo.srcPortId, + channel_id: event.channelOpenCloseInfo.srcChannelId, counterparty_chain_id: chainId, - counterparty_connection_id: event.channelOpenInfo.dstConnectionId, - counterparty_port_id: event.channelOpenInfo.dstPortId, - counterparty_channel_id: event.channelOpenInfo.dstChannelId, + counterparty_connection_id: event.channelOpenCloseInfo.dstConnectionId, + counterparty_port_id: event.channelOpenCloseInfo.dstPortId, + counterparty_channel_id: event.channelOpenCloseInfo.dstChannelId, } return () => { - del(DB, this.tableName, [ + del(DB, this.tableName, [ { state: ChannelState.INIT, counterparty_chain_id: connection.counterparty_chain_id, @@ -184,30 +195,30 @@ export class ChannelController { private static async feedChannelOpenAckEvent( lcd: LCDClient, chainId: string, - event: ChannelOpenEvent + event: ChannelOpenCloseEvent ): Promise<() => void> { const connection = await ConnectionController.getConnection( lcd, chainId, - event.channelOpenInfo.srcConnectionId + event.channelOpenCloseInfo.srcConnectionId ) // add channel on open for dst chain - const channelOnOpen: ChannelOnOpenTable = { + const channelOnOpen: ChannelOpenCloseTable = { in_progress: Boolean.FALSE, state: ChannelState.ACK, chain_id: connection.counterparty_chain_id, connection_id: connection.counterparty_connection_id, - port_id: event.channelOpenInfo.dstPortId, - channel_id: event.channelOpenInfo.dstChannelId, + port_id: event.channelOpenCloseInfo.dstPortId, + channel_id: event.channelOpenCloseInfo.dstChannelId, counterparty_chain_id: chainId, - counterparty_connection_id: event.channelOpenInfo.srcConnectionId, - counterparty_port_id: event.channelOpenInfo.srcPortId, - counterparty_channel_id: event.channelOpenInfo.srcChannelId, + counterparty_connection_id: event.channelOpenCloseInfo.srcConnectionId, + counterparty_port_id: event.channelOpenCloseInfo.srcPortId, + counterparty_channel_id: event.channelOpenCloseInfo.srcChannelId, } return () => { - del(DB, this.tableName, [ + del(DB, this.tableName, [ { state: ChannelState.INIT, counterparty_chain_id: chainId, @@ -222,25 +233,72 @@ export class ChannelController { private static async feedChannelOpenConfirmEvent( _lcd: LCDClient, chainId: string, - event: ChannelOpenEvent + event: ChannelOpenCloseEvent ): Promise<() => void> { return () => { - del(DB, this.tableName, [ + del(DB, this.tableName, [ { state: ChannelState.TRYOPEN, counterparty_chain_id: chainId, - counterparty_port_id: event.channelOpenInfo.srcPortId, - counterparty_channel_id: event.channelOpenInfo.srcChannelId, + counterparty_port_id: event.channelOpenCloseInfo.srcPortId, + counterparty_channel_id: event.channelOpenCloseInfo.srcChannelId, }, ]) // remove open try - del(DB, this.tableName, [ + del(DB, this.tableName, [ { state: ChannelState.ACK, counterparty_chain_id: chainId, - counterparty_port_id: event.channelOpenInfo.dstPortId, - counterparty_channel_id: event.channelOpenInfo.dstChannelId, + counterparty_port_id: event.channelOpenCloseInfo.dstPortId, + counterparty_channel_id: event.channelOpenCloseInfo.dstChannelId, }, ]) // remove ack } } + + private static async feedChannelCloseInitEvent( + lcd: LCDClient, + chainId: string, + event: ChannelOpenCloseEvent + ): Promise<() => void> { + const connection = await ConnectionController.getConnection( + lcd, + chainId, + event.channelOpenCloseInfo.srcConnectionId + ) + + // add channel on open for dst chain + const channelOnOpen: ChannelOpenCloseTable = { + in_progress: Boolean.FALSE, + state: ChannelState.CLOSE, + chain_id: connection.counterparty_chain_id, + connection_id: connection.counterparty_connection_id, + port_id: event.channelOpenCloseInfo.dstPortId, + channel_id: event.channelOpenCloseInfo.dstChannelId, + counterparty_chain_id: chainId, + counterparty_connection_id: event.channelOpenCloseInfo.srcConnectionId, + counterparty_port_id: event.channelOpenCloseInfo.srcPortId, + counterparty_channel_id: event.channelOpenCloseInfo.srcChannelId, + } + + return () => { + insert(DB, this.tableName, channelOnOpen) + } + } + + private static async feedChannelCloseConfirmEvent( + _lcd: LCDClient, + chainId: string, + event: ChannelOpenCloseEvent + ): Promise<() => void> { + return () => { + del(DB, this.tableName, [ + { + state: ChannelState.CLOSE, + chain_id: chainId, + port_id: event.channelOpenCloseInfo.dstPortId, + channel_id: event.channelOpenCloseInfo.dstChannelId, + }, + ]) + } + } } diff --git a/src/db/controller/packet.ts b/src/db/controller/packet.ts index 8856c86..978bb1d 100644 --- a/src/db/controller/packet.ts +++ b/src/db/controller/packet.ts @@ -345,6 +345,10 @@ export class PacketController { dst_channel_id: event.packetInfo.dstChannel, sequence: Number(event.packetInfo.sequence), in_progress: Boolean.FALSE, + is_ordered: + 'ORDER_ORDERED' === event.packetInfo.ordering + ? Boolean.TRUE + : Boolean.FALSE, height: event.packetInfo.height, dst_port: event.packetInfo.dstPort, src_chain_id: chainId, @@ -365,6 +369,10 @@ export class PacketController { src_channel_id: event.packetInfo.srcChannel, sequence: Number(event.packetInfo.sequence), in_progress: Boolean.FALSE, + is_ordered: + 'ORDER_ORDERED' === event.packetInfo.ordering + ? Boolean.TRUE + : Boolean.FALSE, src_port: event.packetInfo.srcPort, dst_chain_id: connection.counterparty_chain_id, dst_connection_id: connection.counterparty_connection_id, @@ -402,6 +410,10 @@ export class PacketController { src_channel_id: event.packetInfo.srcChannel, sequence: Number(event.packetInfo.sequence), in_progress: Boolean.FALSE, + is_ordered: + 'ORDER_ORDERED' === event.packetInfo.ordering + ? Boolean.TRUE + : Boolean.FALSE, height: event.packetInfo.height, src_port: event.packetInfo.srcPort, dst_chain_id: chainId, diff --git a/src/db/migration/0.0.1.sql b/src/db/migration/0.0.1.sql index bb5f87e..759715b 100644 --- a/src/db/migration/0.0.1.sql +++ b/src/db/migration/0.0.1.sql @@ -45,6 +45,9 @@ CREATE TABLE packet_send ( -- in progress in_progress BOOLEAN, + -- order + is_ordered BOOLEAN, + -- packet data height BIGINT NOT NULL, dst_connection_id TEXT NOT NULL, @@ -73,6 +76,9 @@ CREATE TABLE packet_timeout ( -- in progress in_progress BOOLEAN, + -- order + is_ordered BOOLEAN, + -- packet data src_connection_id TEXT NOT NULL, src_port TEXT NOT NULL, @@ -100,6 +106,9 @@ CREATE TABLE packet_write_ack ( -- in progress in_progress BOOLEAN, + -- order + is_ordered BOOLEAN, + -- packet data height BIGINT NOT NULL, src_connection_id TEXT NOT NULL, @@ -119,14 +128,14 @@ CREATE TABLE packet_write_ack ( ); -- create channel on open table -CREATE TABLE channel_on_open ( +CREATE TABLE channel_open_close ( id INTEGER PRIMARY KEY AUTOINCREMENT, -- in progress in_progress BOOLEAN, -- channel data - state BIGINT NOT NULL, -- 1: INIT, 2: TRYOPEN + state BIGINT NOT NULL, -- 1: INIT, 2: TRYOPEN, 3: ACK, 4: CLOSE (counter party state) chain_id TEXT NOT NULL, -- chain_id that need to execute msg connection_id TEXT NOT NULL, port_id TEXT NOT NULL, diff --git a/src/index.ts b/src/index.ts index 1cdd3f4..947510b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -17,7 +17,6 @@ async function main() { app.get('/status', (req, res) => { res.json(workerController.getStatus()) }) - // const metricApp = express() // metricApp.get('/metrics', (req, res) => { @@ -30,6 +29,9 @@ async function main() { app.listen(config.port) info(`status app listen to port ${config.port}`) + info(`rapid relayer has been started`) + info(JSON.stringify(workerController.getStatus(), undefined, 2)) + // metricApp.listen(config.metricPort) // info(`metric app listen to port ${config.metricPort}`) } diff --git a/src/lib/eventParser.ts b/src/lib/eventParser.ts index 3c98102..7c57f1f 100644 --- a/src/lib/eventParser.ts +++ b/src/lib/eventParser.ts @@ -1,5 +1,5 @@ import { Event } from '@cosmjs/tendermint-rpc/build/comet38/responses' -import { ChannelOpenInfo, PacketFeeEvent, PacketInfo } from 'src/types' +import { ChannelOpenCloseInfo, PacketFeeEvent, PacketInfo } from 'src/types' export function parsePacketEvent(event: Event, height: number): PacketInfo { const connectionId = event.attributes.filter( @@ -44,6 +44,10 @@ export function parsePacketEvent(event: Event, height: number): PacketInfo { const ackHex = event.attributes.filter((v) => v.key === 'packet_ack_hex') + const ordering = event.attributes.filter( + (v) => v.key === 'packet_channel_ordering' + )[0].value + const ack = ackHex.length === 0 ? undefined @@ -62,6 +66,7 @@ export function parsePacketEvent(event: Event, height: number): PacketInfo { timeoutTimestamp, timeoutHeightRaw, timeoutTimestampRaw, + ordering, ack, } } @@ -69,7 +74,7 @@ export function parsePacketEvent(event: Event, height: number): PacketInfo { export function parseChannelOpenEvent( event: Event, height: number -): ChannelOpenInfo { +): ChannelOpenCloseInfo { const isSrc = event.type === 'channel_open_init' || event.type === 'channel_open_ack' @@ -94,6 +99,28 @@ export function parseChannelOpenEvent( } } +export function parseChannelCloseEvent( + event: Event, + height: number +): ChannelOpenCloseInfo { + const portId = find(event, 'port_id') + const channelId = find(event, 'channel_id') + const connectionId = find(event, 'connection_id') + const counterpartyPortId = find(event, 'counterparty_port_id') + const counterpartyChannelId = find(event, 'counterparty_channel_id') + const isSrc = event.type === 'channel_close_init' || 'channel_close' + + return { + height, + srcConnectionId: isSrc ? connectionId : '', + srcPortId: isSrc ? portId : counterpartyPortId, + srcChannelId: isSrc ? channelId : counterpartyChannelId, + dstConnectionId: isSrc ? '' : connectionId, + dstPortId: isSrc ? counterpartyPortId : portId, + dstChannelId: isSrc ? counterpartyChannelId : channelId, + } +} + export function parsePacketFeeEvent(event: Event): PacketFeeEvent { const portId = find(event, 'port_id') diff --git a/src/lib/proof.ts b/src/lib/proof.ts index 79c03f2..8dfc215 100644 --- a/src/lib/proof.ts +++ b/src/lib/proof.ts @@ -45,15 +45,15 @@ export async function getRawProof( } export async function getChannelProof( - srcChain: ChainWorker, - srcPortId: string, - srcChannelId: string, + chain: ChainWorker, + portId: string, + channelId: string, headerHeight: Height ): Promise { const key = new Uint8Array( - Buffer.from(`channelEnds/ports/${srcPortId}/channels/${srcChannelId}`) + Buffer.from(`channelEnds/ports/${portId}/channels/${channelId}`) ) - const proof = await getRawProof(srcChain, key, headerHeight) + const proof = await getRawProof(chain, key, headerHeight) const ics23Proof = convertProofsToIcs23(proof) return Buffer.from(ics23Proof).toString('base64') diff --git a/src/msgs/channelCloseConfirm.ts b/src/msgs/channelCloseConfirm.ts new file mode 100644 index 0000000..23d6586 --- /dev/null +++ b/src/msgs/channelCloseConfirm.ts @@ -0,0 +1,23 @@ +import { MsgChannelCloseConfirm } from '@initia/initia.js/dist/core/ibc/core/channel/msgs' +import { Height } from 'cosmjs-types/ibc/core/client/v1/client' +import { getChannelProof } from 'src/lib/proof' +import { ChainWorker } from 'src/workers/chain' +import { Transfrom } from 'src/lib/transform' + +export async function generateMsgChannelCloseConfirm( + srcChain: ChainWorker, + srcPortId: string, + srcChannelId: string, + dstPortId: string, + dstChannelId: string, + height: Height, + msgExecutor: string +): Promise { + return new MsgChannelCloseConfirm( + dstPortId, + dstChannelId, + await getChannelProof(srcChain, srcPortId, srcChannelId, height), + Transfrom.height(height), + msgExecutor + ) +} diff --git a/src/msgs/timeout.ts b/src/msgs/timeout.ts index 5dc6b4e..66eb196 100644 --- a/src/msgs/timeout.ts +++ b/src/msgs/timeout.ts @@ -1,12 +1,19 @@ -import { MsgTimeout } from '@initia/initia.js/dist/core/ibc/core/channel/msgs' +import { + MsgTimeout, + MsgTimeoutOnClose, +} from '@initia/initia.js/dist/core/ibc/core/channel/msgs' import { Height } from 'cosmjs-types/ibc/core/client/v1/client' import { Uint64 } from '@cosmjs/math' import { Transfrom } from 'src/lib/transform' import { Packet } from '@initia/initia.js' -import { convertProofsToIcs23, getRawProof } from 'src/lib/proof' +import { + convertProofsToIcs23, + getChannelProof, + getRawProof, +} from 'src/lib/proof' import { delay } from 'bluebird' import { ChainWorker } from 'src/workers/chain' -import { PacketTimeoutTable } from 'src/types' +import { Boolean, PacketTimeoutTable } from 'src/types' import { packetTableToPacket } from 'src/db/utils' export async function generateMsgTimeout( @@ -17,7 +24,12 @@ export async function generateMsgTimeout( ): Promise { const packet = packetTableToPacket(packetTable) const sequence = await getNextSequenceRecv(packet, dstChain, proofHeight) - const proof = await getTimeoutProof(dstChain, packet, proofHeight) + const proof = await getTimeoutProof( + dstChain, + packet, + proofHeight, + packetTable.is_ordered === Boolean.TRUE + ) return new MsgTimeout( packet, @@ -28,11 +40,43 @@ export async function generateMsgTimeout( ) } +export async function generateMsgTimeoutOnClose( + dstChain: ChainWorker, + packetTable: PacketTimeoutTable, + proofHeight: Height, + executorAddress: string +): Promise { + const packet = packetTableToPacket(packetTable) + const sequence = await getNextSequenceRecv(packet, dstChain, proofHeight) + const proof = await getTimeoutProof( + dstChain, + packet, + proofHeight, + packetTable.is_ordered === Boolean.TRUE + ) + + const channelProof = await getChannelProof( + dstChain, + packet.destination_port, + packet.destination_channel, + proofHeight + ) + + return new MsgTimeoutOnClose( + packet, + proof, + channelProof, + Transfrom.height(proofHeight), + sequence, + executorAddress + ) +} + async function getNextSequenceRecv( packet: Packet, dstChain: ChainWorker, headerHeight: Height -) { +): Promise { const key = new Uint8Array( Buffer.from( `nextSequenceRecv/ports/${packet.destination_port}/channels/${packet.destination_channel}` @@ -67,11 +111,14 @@ async function getNextSequenceRecv( async function getTimeoutProof( dstChain: ChainWorker, packet: Packet, - headerHeight: Height + headerHeight: Height, + isOrdererd: boolean ): Promise { const queryKey = new Uint8Array( Buffer.from( - `receipts/ports/${packet.destination_port}/channels/${packet.destination_channel}/sequences/${packet.sequence}` + isOrdererd + ? `nextSequenceRecv/ports/${packet.destination_port}/channels/${packet.destination_channel}` + : `receipts/ports/${packet.destination_port}/channels/${packet.destination_channel}/sequences/${packet.sequence}` ) ) const proof = await getRawProof(dstChain, queryKey, headerHeight) diff --git a/src/types/event.ts b/src/types/event.ts index 7fe6647..9f469f3 100644 --- a/src/types/event.ts +++ b/src/types/event.ts @@ -39,6 +39,7 @@ export interface PacketInfo { timeoutTimestamp: number timeoutHeightRaw: string timeoutTimestampRaw: string + ordering: string ack?: string } @@ -48,16 +49,19 @@ export interface UpdateClientEvent { consensusHeights: string } -export interface ChannelOpenEvent { +export interface ChannelOpenCloseEvent { type: | 'channel_open_init' | 'channel_open_try' | 'channel_open_ack' | 'channel_open_confirm' - channelOpenInfo: ChannelOpenInfo + | 'channel_close_init' + | 'channel_close' + | 'channel_close_confirm' + channelOpenCloseInfo: ChannelOpenCloseInfo } -export interface ChannelOpenInfo { +export interface ChannelOpenCloseInfo { height: number srcConnectionId: string srcPortId: string diff --git a/src/types/table.ts b/src/types/table.ts index 8feeabb..c018e44 100644 --- a/src/types/table.ts +++ b/src/types/table.ts @@ -34,6 +34,7 @@ export interface PacketSendTable { dst_channel_id: string sequence: number in_progress: Boolean + is_ordered: Boolean height: number dst_port: string src_chain_id: string @@ -53,6 +54,7 @@ export interface PacketTimeoutTable { src_channel_id: string sequence: number in_progress: Boolean + is_ordered: Boolean src_port: string dst_chain_id: string dst_connection_id: string @@ -71,6 +73,7 @@ export interface PacketWriteAckTable { src_channel_id: string sequence: number in_progress: Boolean + is_ordered: Boolean height: number src_port: string dst_chain_id: string @@ -85,7 +88,7 @@ export interface PacketWriteAckTable { timeout_timestamp_raw: string } -export interface ChannelOnOpenTable { +export interface ChannelOpenCloseTable { id?: number in_progress: Boolean state: ChannelState @@ -117,6 +120,7 @@ export enum ChannelState { INIT = 1, TRYOPEN = 2, ACK = 3, + CLOSE = 4, } export enum FeeType { diff --git a/src/workers/chain.ts b/src/workers/chain.ts index a357921..8850dfb 100644 --- a/src/workers/chain.ts +++ b/src/workers/chain.ts @@ -1,7 +1,8 @@ import { RPCClient } from 'src/lib/rpcClient' import { createLoggerWithPrefix } from 'src/lib/logger' -import { ChannelOpenEvent, PacketEvent, PacketFeeEvent } from 'src/types' +import { ChannelOpenCloseEvent, PacketEvent, PacketFeeEvent } from 'src/types' import { + parseChannelCloseEvent, parseChannelOpenEvent, parsePacketEvent, parsePacketFeeEvent, @@ -191,7 +192,7 @@ class SyncWorker { private async fetchEvents(height: number): Promise<{ packetEvents: PacketEvent[] - channelOpenEvents: ChannelOpenEvent[] + channelOpenEvents: ChannelOpenCloseEvent[] packetFeeEvents: PacketFeeEvent[] }> { this.logger.debug(`Fecth new block results (height - ${height})`) @@ -199,7 +200,7 @@ class SyncWorker { const txData = [...blockResult.results] const packetEvents: PacketEvent[] = [] - const channelOpenEvents: ChannelOpenEvent[] = [] + const channelOpenEvents: ChannelOpenCloseEvent[] = [] const packetFeeEvents: PacketFeeEvent[] = [] txData.map((data, i) => { @@ -224,7 +225,18 @@ class SyncWorker { ) { channelOpenEvents.push({ type: event.type, - channelOpenInfo: parseChannelOpenEvent(event, height), + channelOpenCloseInfo: parseChannelOpenEvent(event, height), + }) + } + + if ( + event.type === 'channel_close_init' || + event.type === 'channel_close' || + event.type === 'channel_close_confirm' + ) { + channelOpenEvents.push({ + type: event.type, + channelOpenCloseInfo: parseChannelCloseEvent(event, height), }) } diff --git a/src/workers/index.ts b/src/workers/index.ts index badff5a..3aa8527 100644 --- a/src/workers/index.ts +++ b/src/workers/index.ts @@ -8,12 +8,13 @@ import { generateMsgChannelOpenTry, generateMsgChannelOpenAck, generateMsgChannelOpenConfirm, + generateMsgTimeoutOnClose, } from 'src/msgs' import { Height } from 'cosmjs-types/ibc/core/client/v1/client' import { MsgUpdateClient } from '@initia/initia.js/dist/core/ibc/core/client/msgs' import { ClientController } from 'src/db/controller/client' import { - ChannelOnOpenTable, + ChannelOpenCloseTable, PacketSendTable, PacketTimeoutTable, PacketWriteAckTable, @@ -34,10 +35,13 @@ import * as https from 'https' import { PacketFilter } from 'src/db/controller/packet' import { LCDClient } from 'src/lib/lcdClient' import { + MsgChannelCloseConfirm, MsgChannelOpenAck, MsgChannelOpenConfirm, MsgChannelOpenTry, } from '@initia/initia.js/dist/core/ibc/core/channel/msgs' +import { State } from '@initia/initia.proto/ibc/core/channel/v1/channel' +import { generateMsgChannelCloseConfirm } from 'src/msgs/channelCloseConfirm' export class WorkerController { public chains: Record // chainId => ChainWorker @@ -197,11 +201,24 @@ export class WorkerController { executorAddress: string ) { const dstChain = this.chains[packet.dst_chain_id] + // check dst channel state + const channel = await dstChain.lcd.ibc.channel( + packet.dst_port, + packet.dst_channel_id + ) + if (channel.channel.state === State.STATE_CLOSED) { + return generateMsgTimeoutOnClose( + dstChain, + packet, + height, + executorAddress + ) + } return generateMsgTimeout(dstChain, packet, height, executorAddress) } async generateChannelOpenTryMsg( - event: ChannelOnOpenTable, + event: ChannelOpenCloseTable, height: Height, executorAddress: string ): Promise { @@ -218,7 +235,7 @@ export class WorkerController { } async generateChannelOpenAckMsg( - event: ChannelOnOpenTable, + event: ChannelOpenCloseTable, height: Height, executorAddress: string ): Promise { @@ -235,7 +252,7 @@ export class WorkerController { } async generateChannelOpenConfirmMsg( - event: ChannelOnOpenTable, + event: ChannelOpenCloseTable, height: Height, executorAddress: string ): Promise { @@ -250,6 +267,23 @@ export class WorkerController { executorAddress ) } + + async generateChannelCloseConfirmMsg( + event: ChannelOpenCloseTable, + height: Height, + executorAddress: string + ): Promise { + const srcChain = this.chains[event.counterparty_chain_id] + return generateMsgChannelCloseConfirm( + srcChain, + event.counterparty_port_id, + event.counterparty_channel_id, + event.port_id, + event.channel_id, + height, + executorAddress + ) + } } function createKey(rawKey: KeyConfig): Key { diff --git a/src/workers/wallet.ts b/src/workers/wallet.ts index f3b7d6b..2707eb9 100644 --- a/src/workers/wallet.ts +++ b/src/workers/wallet.ts @@ -1,7 +1,7 @@ import { PacketController, PacketFilter } from 'src/db/controller/packet' import { ChainWorker } from './chain' import { - ChannelOnOpenTable, + ChannelOpenCloseTable, ChannelState, PacketSendTable, PacketTimeoutTable, @@ -30,7 +30,7 @@ export class WalletWorker { public chain: ChainWorker, public workerController: WorkerController, private maxHandlePacket: number, - private wallet: Wallet, + public wallet: Wallet, public packetFilter?: PacketFilter ) { this.logger = createLoggerWithPrefix( @@ -245,35 +245,41 @@ export class WalletWorker { }) ) - // generate channel open msgs + // generate channel open, close msgs const channelOpenMsgs = await Promise.all( - filteredChannelOpenEvents.map((event) => { - const clientId = connectionClientMap[event.connection_id] - const height = updateClientMsgs[clientId].height - - switch (event.state) { - case ChannelState.INIT: - return this.workerController.generateChannelOpenTryMsg( - event, - height, - this.address() - ) - // check src channel state - case ChannelState.TRYOPEN: - return this.workerController.generateChannelOpenAckMsg( - event, - height, - this.address() - ) - // check dst channel state - case ChannelState.ACK: - return this.workerController.generateChannelOpenConfirmMsg( - event, - height, - this.address() - ) - } - }) + filteredChannelOpenEvents + .sort((a, b) => b.state - a.state) // to make execute close first + .map((event) => { + const clientId = connectionClientMap[event.connection_id] + const height = updateClientMsgs[clientId].height + + switch (event.state) { + case ChannelState.INIT: + return this.workerController.generateChannelOpenTryMsg( + event, + height, + this.address() + ) + case ChannelState.TRYOPEN: + return this.workerController.generateChannelOpenAckMsg( + event, + height, + this.address() + ) + case ChannelState.ACK: + return this.workerController.generateChannelOpenConfirmMsg( + event, + height, + this.address() + ) + case ChannelState.CLOSE: + return this.workerController.generateChannelCloseConfirmMsg( + event, + height, + this.address() + ) + } + }) ) const msgs = [ @@ -525,8 +531,8 @@ export class WalletWorker { } private async filterChannelOpenEvents( - channelOnOpens: ChannelOnOpenTable[] - ): Promise { + channelOnOpens: ChannelOpenCloseTable[] + ): Promise { // filter duplicated open try channelOnOpens = channelOnOpens.filter((v, i, a) => { if (v.state !== ChannelState.TRYOPEN) { @@ -541,7 +547,7 @@ export class WalletWorker { ) }) - const eventsToDel: ChannelOnOpenTable[] = [] + const eventsToDel: ChannelOpenCloseTable[] = [] // check already executed const res = await Promise.all( @@ -585,6 +591,6 @@ export class WalletWorker { ChannelController.delOpenEvents(eventsToDel) - return res.filter((v) => v !== undefined) as ChannelOnOpenTable[] + return res.filter((v) => v !== undefined) as ChannelOpenCloseTable[] } } From 5c4977d6f853e302e47f2c849f179e8d5657ece2 Mon Sep 17 00:00:00 2001 From: ALPAC-4 Date: Mon, 14 Oct 2024 17:04:24 +0900 Subject: [PATCH 14/30] fix: fix release time --- src/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/index.ts b/src/index.ts index 947510b..b3d7068 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,6 +8,8 @@ import { info } from './lib/logger' import { config } from './lib/config' import { WorkerController } from './workers' +import { DB } from './db' +import { select } from './db/utils' async function main() { const workerController = new WorkerController() From bd53ed55a08850ed5a99d550d1fae2329e3e173b Mon Sep 17 00:00:00 2001 From: ALPAC-4 Date: Wed, 16 Oct 2024 18:54:43 +0900 Subject: [PATCH 15/30] support ordered channels --- src/db/controller/channel.ts | 10 ++++- src/db/controller/packet.ts | 60 +++++++++++++++++++++---- src/db/controller/syncInfo.ts | 6 ++- src/db/migration/0.0.1.sql | 1 + src/index.ts | 2 - src/lib/config.ts | 2 +- src/lib/lcdClient.ts | 15 +++++++ src/types/table.ts | 1 + src/workers/chain.ts | 8 ++-- src/workers/index.ts | 4 +- src/workers/wallet.ts | 82 ++++++++++++++++++++++++++++++----- 11 files changed, 161 insertions(+), 30 deletions(-) diff --git a/src/db/controller/channel.ts b/src/db/controller/channel.ts index 90c92e5..5ecc9e1 100644 --- a/src/db/controller/channel.ts +++ b/src/db/controller/channel.ts @@ -8,7 +8,7 @@ import { import { In, WhereOptions, del, insert, select, update } from '../utils' import { LCDClient } from 'src/lib/lcdClient' import { ConnectionController } from './connection' -import { PacketFilter } from './packet' +import { PacketController, PacketFilter } from './packet' import { Database } from 'better-sqlite3' export class ChannelController { @@ -138,6 +138,7 @@ export class ChannelController { // add channel on open for dst chain const channelOnOpen: ChannelOpenCloseTable = { in_progress: Boolean.FALSE, + height: event.channelOpenCloseInfo.height, state: ChannelState.INIT, chain_id: connection.counterparty_chain_id, connection_id: connection.counterparty_connection_id, @@ -168,6 +169,7 @@ export class ChannelController { // add channel on open for src chain const channelOnOpen: ChannelOpenCloseTable = { in_progress: Boolean.FALSE, + height: event.channelOpenCloseInfo.height, state: ChannelState.TRYOPEN, chain_id: connection.counterparty_chain_id, connection_id: connection.counterparty_connection_id, @@ -206,6 +208,7 @@ export class ChannelController { // add channel on open for dst chain const channelOnOpen: ChannelOpenCloseTable = { in_progress: Boolean.FALSE, + height: event.channelOpenCloseInfo.height, state: ChannelState.ACK, chain_id: connection.counterparty_chain_id, connection_id: connection.counterparty_connection_id, @@ -269,6 +272,7 @@ export class ChannelController { // add channel on open for dst chain const channelOnOpen: ChannelOpenCloseTable = { in_progress: Boolean.FALSE, + height: event.channelOpenCloseInfo.height, state: ChannelState.CLOSE, chain_id: connection.counterparty_chain_id, connection_id: connection.counterparty_connection_id, @@ -299,6 +303,10 @@ export class ChannelController { channel_id: event.channelOpenCloseInfo.dstChannelId, }, ]) + PacketController.updateTimeout( + chainId, + event.channelOpenCloseInfo.srcChannelId + ) } } } diff --git a/src/db/controller/packet.ts b/src/db/controller/packet.ts index 978bb1d..ed78314 100644 --- a/src/db/controller/packet.ts +++ b/src/db/controller/packet.ts @@ -16,12 +16,12 @@ import { ConnectionController } from './connection' import { Database } from 'better-sqlite3' import { LCDClient } from 'src/lib/lcdClient' import { PacketFeeController } from './packetFee' -import { FeeFilter } from 'src/lib/config' +import { PacketFee } from 'src/lib/config' export class PacketController { - private static tableNamePacketSend = 'packet_send' - private static tableNamePacketTimeout = 'packet_timeout' - private static tableNamePacketWriteAck = 'packet_write_ack' + public static tableNamePacketSend = 'packet_send' + public static tableNamePacketTimeout = 'packet_timeout' + public static tableNamePacketWriteAck = 'packet_write_ack' public static async feedEvents( lcd: LCDClient, @@ -59,7 +59,7 @@ export class PacketController { chainId: string, height: number, timestamp: number, - chainIdsWithFeeFilters: { chainId: string; feeFilter: FeeFilter }[], + chainIdsWithFeeFilters: { chainId: string; feeFilter: PacketFee }[], filter: PacketFilter = {}, limit = 100 ): PacketSendTable[] { @@ -120,7 +120,7 @@ export class PacketController { height: number, timestamp: number, counterpartyChainIds: string[], - feeFilter: FeeFilter, + feeFilter: PacketFee, filter: PacketFilter = {}, limit = 100 ): PacketTimeoutTable[] { @@ -169,7 +169,7 @@ export class PacketController { public static getWriteAckPackets( chainId: string, counterpartyChainIds: string[], - feeFilter: FeeFilter, + feeFilter: PacketFee, filter: PacketFilter = {}, limit = 100 ): PacketWriteAckTable[] { @@ -388,6 +388,21 @@ export class PacketController { return () => { insert(DB, this.tableNamePacketSend, packetSend) insert(DB, this.tableNamePacketTimeout, packetTimeout) + + // if channel is ordered channel, update in progress for higher sequence + if (packetSend.is_ordered === Boolean.TRUE) { + update( + DB, + this.tableNamePacketSend, + { in_progress: Boolean.FALSE }, + [ + { + dst_chain_id: packetSend.dst_chain_id, + dst_channel_id: packetSend.dst_channel_id, + }, + ] + ) + } } } @@ -432,7 +447,6 @@ export class PacketController { del(DB, this.tableNamePacketSend, [ { dst_chain_id: chainId, - dst_connection_id: event.packetInfo.connectionId, dst_channel_id: event.packetInfo.dstChannel, sequence: Number(event.packetInfo.sequence), }, @@ -453,6 +467,21 @@ export class PacketController { ) insert(DB, this.tableNamePacketWriteAck, packetWriteAck) + + // if channel is ordered channel, update in progress for higher sequence + if (packetWriteAck.is_ordered === Boolean.TRUE) { + update( + DB, + this.tableNamePacketSend, + { in_progress: Boolean.FALSE }, + [ + { + dst_chain_id: chainId, + dst_channel_id: event.packetInfo.dstChannel, + }, + ] + ) + } } } @@ -574,6 +603,21 @@ export class PacketController { ) } } + + // update timeout timestamp to -1 + public static updateTimeout(chainId: string, channelId: string) { + update( + DB, + PacketController.tableNamePacketTimeout, + { timeout_timestamp: -1 }, + [ + { + dst_channel_id: channelId, + dst_chain_id: chainId, + }, + ] + ) + } } export interface PacketFilter { diff --git a/src/db/controller/syncInfo.ts b/src/db/controller/syncInfo.ts index 68a2ee2..37d437f 100644 --- a/src/db/controller/syncInfo.ts +++ b/src/db/controller/syncInfo.ts @@ -9,7 +9,7 @@ export class SyncInfoController { startHeights: number[], latestHeight: number ): SyncInfoTable[] { - startHeights = startHeights.sort().reverse() + startHeights = startHeights.sort() let syncInfos = this.getSyncInfos(chainId) if (syncInfos.length === 0) { @@ -80,10 +80,12 @@ export class SyncInfoController { update(DB, this.tableName, { start_height: startHeight }, [ { chain_id: chainId, - start_height: endHeight, + start_height: endHeight + 1, }, ]) + console.log(select(DB, this.tableName)) + return true } diff --git a/src/db/migration/0.0.1.sql b/src/db/migration/0.0.1.sql index 759715b..af0335b 100644 --- a/src/db/migration/0.0.1.sql +++ b/src/db/migration/0.0.1.sql @@ -135,6 +135,7 @@ CREATE TABLE channel_open_close ( in_progress BOOLEAN, -- channel data + height BIGINT NOT NULL, state BIGINT NOT NULL, -- 1: INIT, 2: TRYOPEN, 3: ACK, 4: CLOSE (counter party state) chain_id TEXT NOT NULL, -- chain_id that need to execute msg connection_id TEXT NOT NULL, diff --git a/src/index.ts b/src/index.ts index b3d7068..947510b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,8 +8,6 @@ import { info } from './lib/logger' import { config } from './lib/config' import { WorkerController } from './workers' -import { DB } from './db' -import { select } from './db/utils' async function main() { const workerController = new WorkerController() diff --git a/src/lib/config.ts b/src/lib/config.ts index a2ed969..66f1d24 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -30,7 +30,7 @@ interface WalletConfig { startHeight?: number } -interface PacketFee { +export interface PacketFee { recvFee?: Coin[] ackFee?: Coin[] timeoutFee?: Coin[] diff --git a/src/lib/lcdClient.ts b/src/lib/lcdClient.ts index d3fe2c2..1ae2265 100644 --- a/src/lib/lcdClient.ts +++ b/src/lib/lcdClient.ts @@ -60,6 +60,12 @@ class IbcAPI extends IbcAPI_ { `/ibc/core/connection/v1/connections/${connectionId}` ) } + + async nextSequence(portId: string, channelId: string): Promise { + return this.c.get( + `/ibc/core/channel/v1/channels/${channelId}/ports/${portId}/next_sequence` + ) + } } interface ChannelResponse { @@ -88,3 +94,12 @@ interface ChannelResponseRaw { revision_height: number } } + +interface NextSequence { + next_sequence_receive: string + proof: null | string + proof_height: { + revision_number: string + revision_height: string + } +} diff --git a/src/types/table.ts b/src/types/table.ts index c018e44..3837148 100644 --- a/src/types/table.ts +++ b/src/types/table.ts @@ -91,6 +91,7 @@ export interface PacketWriteAckTable { export interface ChannelOpenCloseTable { id?: number in_progress: Boolean + height: number state: ChannelState chain_id: string connection_id: string diff --git a/src/workers/chain.ts b/src/workers/chain.ts index 8850dfb..1d1b6fc 100644 --- a/src/workers/chain.ts +++ b/src/workers/chain.ts @@ -15,7 +15,7 @@ import { Logger } from 'winston' import { LCDClient } from 'src/lib/lcdClient' import { ChannelController } from 'src/db/controller/channel' import { PacketFeeController } from 'src/db/controller/packetFee' -import { FeeFilter } from 'src/lib/config' +import { PacketFee } from 'src/lib/config' export class ChainWorker { public latestHeight: number @@ -28,7 +28,7 @@ export class ChainWorker { public lcd: LCDClient, public rpc: RPCClient, public bech32Prefix: string, - public feeFilter: FeeFilter, + public feeFilter: PacketFee, latestHeight: number, startHeights: number[] ) { @@ -53,11 +53,11 @@ export class ChainWorker { public terminateSyncWorker(startHeight: number) { const endHeight = this.syncWorkers[startHeight].endHeight - const nextWorker = this.syncWorkers[endHeight] + const nextWorker = this.syncWorkers[endHeight + 1] // drop sync workers delete this.syncWorkers[startHeight] - delete this.syncWorkers[endHeight] + delete this.syncWorkers[endHeight + 1] // update and store next range worker nextWorker.startHeight = startHeight diff --git a/src/workers/index.ts b/src/workers/index.ts index 3aa8527..d83c3d2 100644 --- a/src/workers/index.ts +++ b/src/workers/index.ts @@ -27,7 +27,7 @@ import { APIRequester, Wallet, } from '@initia/initia.js' -import { Config, FeeFilter, KeyConfig } from 'src/lib/config' +import { Config, PacketFee, KeyConfig } from 'src/lib/config' import { env } from 'node:process' import { RPCClient } from 'src/lib/rpcClient' import * as http from 'http' @@ -104,7 +104,7 @@ export class WorkerController { } } - public getFeeFilters(): { chainId: string; feeFilter: FeeFilter }[] { + public getFeeFilters(): { chainId: string; feeFilter: PacketFee }[] { return Object.keys(this.chains).map((chainId) => ({ chainId, feeFilter: this.chains[chainId].feeFilter, diff --git a/src/workers/wallet.ts b/src/workers/wallet.ts index 2707eb9..47726e6 100644 --- a/src/workers/wallet.ts +++ b/src/workers/wallet.ts @@ -1,6 +1,7 @@ import { PacketController, PacketFilter } from 'src/db/controller/packet' import { ChainWorker } from './chain' import { + Boolean, ChannelOpenCloseTable, ChannelState, PacketSendTable, @@ -19,7 +20,7 @@ import { delay } from 'bluebird' import { Logger } from 'winston' import { ChannelController } from 'src/db/controller/channel' import { State } from '@initia/initia.proto/ibc/core/channel/v1/channel' -import { FeeFilter } from 'src/lib/config' +import { PacketFee } from 'src/lib/config' // TODO: add update client worker export class WalletWorker { @@ -66,7 +67,7 @@ export class WalletWorker { const feeFilter = ( chainIdsWithFeeFilter.find((v) => v.chainId === this.chain.chainId) as { chainId: string - feeFilter: FeeFilter + feeFilter: PacketFee } ).feeFilter @@ -124,6 +125,11 @@ export class WalletWorker { this.packetFilter, undefined, remain + ).filter( + (event) => + event.height < + this.workerController.chains[event.counterparty_chain_id] + .latestHeight ) // update packet in progress @@ -147,14 +153,13 @@ export class WalletWorker { await this.filterWriteAckPackets(writeAckPackets) const filteredTimeoutPackets = await this.filterTimeoutPackets(timeoutPackets) - const filteredChannelOpenEvents = - await this.filterChannelOpenEvents(channelOpenEvents) - + const filteredChannelOpenCloseEvents = + await this.filterChannelOpenCloseEvents(channelOpenEvents) if ( filteredSendPackets.length === 0 && filteredWriteAckPackets.length === 0 && filteredTimeoutPackets.length === 0 && - filteredChannelOpenEvents.length === 0 + filteredChannelOpenCloseEvents.length === 0 ) { return } @@ -167,7 +172,7 @@ export class WalletWorker { ...filteredSendPackets.map((packet) => packet.dst_connection_id), ...filteredWriteAckPackets.map((packet) => packet.src_connection_id), ...filteredTimeoutPackets.map((packet) => packet.src_connection_id), - ...filteredChannelOpenEvents.map((event) => event.connection_id), + ...filteredChannelOpenCloseEvents.map((event) => event.connection_id), ].filter((v, i, a) => a.indexOf(v) === i) const connectionClientMap: Record = {} @@ -247,7 +252,7 @@ export class WalletWorker { // generate channel open, close msgs const channelOpenMsgs = await Promise.all( - filteredChannelOpenEvents + filteredChannelOpenCloseEvents .sort((a, b) => b.state - a.state) // to make execute close first .map((event) => { const clientId = connectionClientMap[event.connection_id] @@ -376,6 +381,58 @@ export class WalletWorker { await Promise.all( Object.keys(sendPacketMap).map(async (path) => { if (sendPacketMap[path].length === 0) return + // check channel state + const dstChannel = await this.chain.lcd.ibc.channel( + sendPacketMap[path][0].dst_port, + sendPacketMap[path][0].dst_channel_id + ) + + if (dstChannel.channel.state === State.STATE_CLOSED) { + sendPacketsToDel.push(...sendPacketMap[path]) + delete sendPacketMap[path] + return + } + + const srcChannel = await this.workerController.chains[ + sendPacketMap[path][0].src_chain_id + ].lcd.ibc.channel( + sendPacketMap[path][0].src_port, + sendPacketMap[path][0].src_channel_id + ) + + if (srcChannel.channel.state === State.STATE_CLOSED) { + sendPacketsToDel.push(...sendPacketMap[path]) + delete sendPacketMap[path] + return + } + + // handle ordered channels + if (sendPacketMap[path][0].is_ordered === Boolean.TRUE) { + // check next sequence + const nextSequence = await this.chain.lcd.ibc.nextSequence( + sendPacketMap[path][0].dst_port, + sendPacketMap[path][0].dst_channel_id + ) + + let sequence = Number(nextSequence.next_sequence_receive) + const sequences: number[] = [] + + for (const packet of sendPacketMap[path]) { + if (packet.sequence !== sequence) { + break + } + + sequences.push(sequence) + sequence++ + } + + sendPacketMap[path] = sendPacketMap[path].filter((packet) => + sequences.includes(packet.sequence) + ) + + return + } + const unrecivedPackets = await this.chain.lcd.ibc.unreceivedPackets( sendPacketMap[path][0].dst_port, sendPacketMap[path][0].dst_channel_id, @@ -453,7 +510,7 @@ export class WalletWorker { } private async filterTimeoutPackets( - timeoutPackets: PacketTimeoutTable[] + timeoutPackets: PacketTimeoutTable[] | PacketSendTable[] ): Promise { // create path => packet map const timeoutPacketMap: Record = {} @@ -530,7 +587,7 @@ export class WalletWorker { return Object.values(timeoutPacketMap).flat() } - private async filterChannelOpenEvents( + private async filterChannelOpenCloseEvents( channelOnOpens: ChannelOpenCloseTable[] ): Promise { // filter duplicated open try @@ -581,6 +638,11 @@ export class WalletWorker { return v } break + case ChannelState.CLOSE: + if (channel && channel.channel.state !== State.STATE_CLOSED) { + return v + } + break } eventsToDel.push(v) From 08cb13aff78a0cc6c9fd09068ae73556159125be Mon Sep 17 00:00:00 2001 From: ALPAC-4 Date: Fri, 1 Nov 2024 11:49:40 +0900 Subject: [PATCH 16/30] add config example, fix readme --- README.md | 99 +++++++++++++++++------------- config.example.json | 77 +++++++++++++++--------- config.schema.json | 142 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 248 insertions(+), 70 deletions(-) create mode 100644 config.schema.json diff --git a/README.md b/README.md index 265a934..059c23c 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,24 @@ # Rapid Relayer + Rapid Relayer is a fast, scalable, stateful IBC Relayer optimized for interwoven rollups. -Rapid Relayer does not use the `tx_search` query of Hermes to handle packets from several blocks at once. Initia Labs has developed this IBC Relayer to replace Hermes, only using the necessary functions for packet handling. +Rapid Relayer does not use the `tx_search` query of Hermes to handle packets from several blocks at once. Initia Labs has developed this IBC Relayer to replace Hermes, only using the necessary functions for packet handling. ### Problems We Faced + - Minitia L2s generate blocks extremely quick at 500ms per block. - Due to the interwoven nature of Initia, often many IBC packets are generated within blocks. Hermes can handle batches of packets but on a single block basis. - Hermes handles these IBC packets sequentially leading to unprocessed packets accumulating very quickly when having fast blocktimes. -- If Hermes stops, unprocessed packets will continue to pile up. +- If Hermes stops, unprocessed packets will continue to pile up. - When Hermes misses a packet, it finds them using `tx_search` query on every sequence, this can take minutes for just a few hundred packets. - We need something more rapid. ### How We Fix This + - We removed the `tx_search` query, and handle packets in parallel across several blocks at once. - Keep track of `synced_height` and `latest_height`. - Multi-threaded workers: packet handler and event feeder. The event feeder feeds the packet from new blocks to a cache and the packet handler fetches packets from it. This way, even if the packet handler stops, the event feeder will continue to operate. -- We remove the slow call of `tx_search`. - +- We remove the slow call of `tx_search`. ## Installation @@ -38,39 +40,53 @@ npm install ```json { - "port": 3000, + "$schema": "./config.schema.json", + "port": 7010, + "metricPort": 70001, "logLevel": "info", - "pairs": [ + "chains": [ { - "name": "chainA - chainB", // default chainA.chainId - chainB.chainId - "chainA": { - "bech32Prefix": "init", // bech 32 prefix - "chainId": "chain-1", // chainId - "gasPrice": "0.2uinit", // gas price - "lcdUri": "http://rest.chain-1.com", // lcd (rest) uri - "rpcUri": "http://rpc.chain-1.com", // rpc uri - "key": { - "type": "raw", // raw | mnemonic - "privateKey": "12af.." // for raw hex based private key, for mnemonic 12/24 words + "bech32Prefix": "init", + "chainId": "chain-1", + "gasPrice": "0.15gas", + "lcdUri": "https://rest.chain-1.com", + "rpcUri": "https://rpc.chain-1.com", + "wallets": [ + { + "key": { + "type": "raw", + "privateKey": "123..." + }, + "maxHandlePacket": 10, + "startHeight": 0 // if empty start from the latest height }, - "connectionId": "connection-1", // connection id to relay - "syncInfo": { - "height": 12345, // synced height - "txIndex": 30 // synced tx index - } // Optional, If a syncInfo file exists, this field is ignored. - }, - "chainB": { - "bech32Prefix": "init", - "chainId": "chain-2", - "gasPrice": "0umin", - "lcdUri": "http://rest.chain-2.com", - "rpcUri": "http://rpc.chain-2.com", - "key": { - "type": "mnemonic", - "privateKey": "bus ..." - }, - "connectionId": "connection-0" - } + { + "key": { + "type": "mnemonic", + "privateKey": "repair family apology column ..." + }, + "maxHandlePacket": 10, + "packetFilter": { + "connections": [{ "connectionId": "conneciton-1" }] + } + } + ] + }, + { + "bech32Prefix": "init", + "chainId": "chain-2", + "gasPrice": "0.15gas", + "lcdUri": "https://rest.chain-2.com", + "rpcUri": "https://rpc.chain-2.com", + "wallets": [ + { + "key": { + "type": "raw", + "privateKey": "123..." + }, + "maxHandlePacket": 10 + } + ] } ] } @@ -81,17 +97,18 @@ npm install ```bash npm start ``` + ## Install via docker -```bash + +```bash docker build -t your-tag . ``` -mount a volume called '/config' which contains your config.json -and a /syncInfo volume which will contain the state + +mount a volume called '/config' which contains your config.json +and a /syncInfo volume which will contain the state + ```bash docker run -it -v/tmp/rr/config:/config -v/tmp/rr/syncInfo:/syncInfo -d rapid-relayer:latest ``` -this should start the relayer in a docker container using your config, and placing the state in a separate volume - -## SyncInfo -rapid-relayer checks events and stores processed information in `.syncInfo`. To move migrate relayer to other, please copy `.syncInfo` +this should start the relayer in a docker container using your config, and placing the state in a separate volume diff --git a/config.example.json b/config.example.json index 2f6d396..a04b908 100644 --- a/config.example.json +++ b/config.example.json @@ -1,37 +1,56 @@ { - "port": 3000, + "$schema": "./config.schema.json", + "port": 7010, + "metricPort": 70001, "logLevel": "info", - "pairs": [ + "chains": [ { - "name": "chainA - chainB", - "chainA": { - "bech32Prefix": "init", - "chainId": "chain-1", - "gasPrice": "0.2uinit", - "lcdUri": "http://rest.chain-1.com", - "rpcUri": "http://rpc.chain-1.com", - "key": { - "type": "raw", - "privateKey": "12af.." + "bech32Prefix": "init", + "chainId": "chain-1", + "gasPrice": "0.15gas", + "lcdUri": "https://rest.chain-1.com", + "rpcUri": "https://rpc.chain-1.com", + "wallets": [ + { + "key": { + "type": "raw", + "privateKey": "123..." + }, + "maxHandlePacket": 10, + "startHeight": 0 // if empty start from the latest height }, - "connectionId": "connection-1", - "syncInfo": { - "height": 12345, - "txIndex": 30 + { + "key": { + "type": "mnemonic", + "privateKey": "repair family apology column ..." + }, + "maxHandlePacket": 10, + "packetFilter": { + "connections": [{ "connectionId": "conneciton-1" }] + }, + "feeFilter": { + "recvFee": "100gas", + "timeoutFee": "200gas", + "ackFee": "300gas" + } } - }, - "chainB": { - "bech32Prefix": "init", - "chainId": "chain-2", - "gasPrice": "0umin", - "lcdUri": "http://rest.chain-2.com", - "rpcUri": "http://rpc.chain-2.com", - "key": { - "type": "mnemonic", - "privateKey": "bus ..." - }, - "connectionId": "connection-0" - } + ] + }, + { + "bech32Prefix": "init", + "chainId": "chain-2", + "gasPrice": "0umin", + "lcdUri": "https://rest.chain-2.com", + "rpcUri": "https://rpc.chain-2.com", + "wallets": [ + { + "key": { + "type": "raw", + "privateKey": "123..." + }, + "maxHandlePacket": 10 + } + ] } ] } diff --git a/config.schema.json b/config.schema.json new file mode 100644 index 0000000..198f077 --- /dev/null +++ b/config.schema.json @@ -0,0 +1,142 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema", + "title": "Rapid Relayer Config", + "description": "Rapid Relayer config schema", + "type": "object", + "required": ["port", "metricPort", "logLevel", "chains"], + "properties": { + "$schema": { + "type": "string" + }, + "port": { + "type": "number" + }, + "metricPort": { + "type": "number" + }, + "logLevel": { + "type": "string" + }, + "chains": { + "type": "array", + "items": { + "$ref": "#/$defs/chain" + }, + "minContains": 2 + } + }, + "additionalProperties": false, + "$defs": { + "chain": { + "type": "object", + "required": [ + "bech32Prefix", + "chainId", + "gasPrice", + "lcdUri", + "rpcUri", + "wallets" + ], + "properties": { + "bech32Prefix": { + "type": "string", + "description": "bech32 prefix of chain" + }, + "chainId": { + "type": "string", + "description": "chain id" + }, + "gasPrice": { + "type": "string", + "description": "gas price in format 0.1denom" + }, + "lcdUri": { + "type": "string", + "description": "cosmos rest api uri" + }, + "rpcUri": { + "type": "string", + "description": "cosmos rest rpc uri" + }, + "wallets": { + "type": "array", + "items": { + "$ref": "#/$defs/wallet" + }, + "minContains": "1" + }, + "feeFilter": { + "type": "object", + "properties": { + "recvFee": { + "type": "array", + "items": { + "$ref": "#/$defs/coin" + }, + "minContains": "1" + }, + "ackFee": { + "type": "array", + "items": { + "$ref": "#/$defs/coin" + }, + "minContains": "1" + }, + "timeoutFee": { + "type": "array", + "items": { + "$ref": "#/$defs/coin" + }, + "minContains": "1" + } + } + } + } + }, + "wallet": { + "type": "object", + "required": ["key"], + "properties": { + "key": { + "type": "object", + "required": ["type", "privateKey"], + "properties": { + "type": { + "type": "string", + "enum": ["raw", "mnemonic", "env_raw", "env_mnemonic"] + }, + "privateKey": { + "type": "string" + }, + "options": { + "type": "object", + "properties": { + "account": { + "type": "number" + }, + "index": { + "type": "number" + }, + "coinType": { + "type": "number" + } + } + } + } + } + } + }, + "coin": { + "type": "object", + "required": ["denom", "amount"], + "properties": { + "denom": { + "type": "string" + }, + "amount": { + "type": "number" + } + } + } + } +} From 3e83c80938762b2a3ea1c6ee1bfcd2c8350cbafe Mon Sep 17 00:00:00 2001 From: ALPAC-4 Date: Fri, 1 Nov 2024 11:56:16 +0900 Subject: [PATCH 17/30] add metric app --- src/index.ts | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/index.ts b/src/index.ts index 947510b..3183d60 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,9 +1,5 @@ -// import { Chain } from './chain' import express from 'express' -// import { runPair } from './lib/chainPair' -// import { config } from './lib/config' -// import { ChainStatus } from './chain/types' -// import { registery } from './lib/metric' +import { registery } from './lib/metric' import { info } from './lib/logger' import { config } from './lib/config' @@ -17,23 +13,23 @@ async function main() { app.get('/status', (req, res) => { res.json(workerController.getStatus()) }) - // const metricApp = express() + const metricApp = express() - // metricApp.get('/metrics', (req, res) => { - // res.setHeader('content-type', registery.contentType) - // registery - // .metrics() - // .then((response) => res.send(response)) - // .catch(() => res.status(500).send('Fail to get metrics')) - // }) + metricApp.get('/metrics', (req, res) => { + res.setHeader('content-type', registery.contentType) + registery + .metrics() + .then((response) => res.send(response)) + .catch(() => res.status(500).send('Fail to get metrics')) + }) app.listen(config.port) info(`status app listen to port ${config.port}`) info(`rapid relayer has been started`) info(JSON.stringify(workerController.getStatus(), undefined, 2)) - // metricApp.listen(config.metricPort) - // info(`metric app listen to port ${config.metricPort}`) + metricApp.listen(config.metricPort) + info(`metric app listen to port ${config.metricPort}`) } void main() From 6aeedceb16e32e11c2be5cf55285aaa3a53c8686 Mon Sep 17 00:00:00 2001 From: ALPAC-4 Date: Mon, 4 Nov 2024 11:54:18 +0900 Subject: [PATCH 18/30] fix: delete right channel-open-close row --- config.example.json | 12 ++++++------ src/db/controller/channel.ts | 26 ++++++++++++++++++++------ 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/config.example.json b/config.example.json index a04b908..1500402 100644 --- a/config.example.json +++ b/config.example.json @@ -27,14 +27,14 @@ "maxHandlePacket": 10, "packetFilter": { "connections": [{ "connectionId": "conneciton-1" }] - }, - "feeFilter": { - "recvFee": "100gas", - "timeoutFee": "200gas", - "ackFee": "300gas" } } - ] + ], + "feeFilter": { + "recvFee": [{ "denom": "gas", "amount": 100 }], + "timeoutFee": [{ "denom": "gas", "amount": 200 }], + "ackFee": [{ "denom": "gas", "amount": 300 }] + } }, { "bech32Prefix": "init", diff --git a/src/db/controller/channel.ts b/src/db/controller/channel.ts index 5ecc9e1..ec24b50 100644 --- a/src/db/controller/channel.ts +++ b/src/db/controller/channel.ts @@ -221,6 +221,14 @@ export class ChannelController { } return () => { + del(DB, this.tableName, [ + { + state: ChannelState.TRYOPEN, + counterparty_chain_id: channelOnOpen.chain_id, + counterparty_port_id: channelOnOpen.port_id, + counterparty_channel_id: channelOnOpen.channel_id, + }, + ]) // remove open try del(DB, this.tableName, [ { state: ChannelState.INIT, @@ -234,25 +242,30 @@ export class ChannelController { } private static async feedChannelOpenConfirmEvent( - _lcd: LCDClient, + lcd: LCDClient, chainId: string, event: ChannelOpenCloseEvent ): Promise<() => void> { + const connection = await ConnectionController.getConnection( + lcd, + chainId, + event.channelOpenCloseInfo.dstConnectionId + ) return () => { del(DB, this.tableName, [ { state: ChannelState.TRYOPEN, counterparty_chain_id: chainId, - counterparty_port_id: event.channelOpenCloseInfo.srcPortId, - counterparty_channel_id: event.channelOpenCloseInfo.srcChannelId, + counterparty_port_id: event.channelOpenCloseInfo.dstPortId, + counterparty_channel_id: event.channelOpenCloseInfo.dstChannelId, }, ]) // remove open try del(DB, this.tableName, [ { state: ChannelState.ACK, - counterparty_chain_id: chainId, - counterparty_port_id: event.channelOpenCloseInfo.dstPortId, - counterparty_channel_id: event.channelOpenCloseInfo.dstChannelId, + counterparty_chain_id: connection.counterparty_chain_id, + counterparty_port_id: event.channelOpenCloseInfo.srcPortId, + counterparty_channel_id: event.channelOpenCloseInfo.srcChannelId, }, ]) // remove ack } @@ -303,6 +316,7 @@ export class ChannelController { channel_id: event.channelOpenCloseInfo.dstChannelId, }, ]) + // Mark all packets as timed out PacketController.updateTimeout( chainId, event.channelOpenCloseInfo.srcChannelId From 3a1ddefa8c6bd80d177bb3a5d7e113436c1c22a3 Mon Sep 17 00:00:00 2001 From: ALPAC-4 Date: Mon, 4 Nov 2024 13:43:24 +0900 Subject: [PATCH 19/30] support base64 events --- src/lib/eventParser.ts | 108 ++++++++++++++++++++--------------------- src/workers/wallet.ts | 21 ++++++-- 2 files changed, 68 insertions(+), 61 deletions(-) diff --git a/src/lib/eventParser.ts b/src/lib/eventParser.ts index 7c57f1f..fc1bcde 100644 --- a/src/lib/eventParser.ts +++ b/src/lib/eventParser.ts @@ -2,56 +2,36 @@ import { Event } from '@cosmjs/tendermint-rpc/build/comet38/responses' import { ChannelOpenCloseInfo, PacketFeeEvent, PacketInfo } from 'src/types' export function parsePacketEvent(event: Event, height: number): PacketInfo { - const connectionId = event.attributes.filter( - (v) => v.key === 'connection_id' - )[0].value + const connectionId = getConnection(event) as string - const sequence = Number( - event.attributes.filter((v) => v.key === 'packet_sequence')[0].value - ) + const sequence = Number(find(event, 'packet_sequence') as string) - const srcPort = event.attributes.filter((v) => v.key === 'packet_src_port')[0] - .value + const srcPort = find(event, 'packet_src_port') as string - const srcChannel = event.attributes.filter( - (v) => v.key === 'packet_src_channel' - )[0].value + const srcChannel = find(event, 'packet_src_channel') as string - const dstPort = event.attributes.filter((v) => v.key === 'packet_dst_port')[0] - .value + const dstPort = find(event, 'packet_dst_port') as string - const dstChannel = event.attributes.filter( - (v) => v.key === 'packet_dst_channel' - )[0].value + const dstChannel = find(event, 'packet_dst_channel') as string - const dataHex = event.attributes.filter((v) => v.key === 'packet_data_hex') + const dataHex = find(event, 'packet_data_hex') as string - const data = - dataHex.length === 0 - ? undefined - : Buffer.from(dataHex[0].value, 'hex').toString('base64') + const data = dataHex + ? Buffer.from(dataHex, 'hex').toString('base64') + : undefined - const timeoutHeightRaw = event.attributes.filter( - (v) => v.key === 'packet_timeout_height' - )[0].value + const timeoutHeightRaw = find(event, 'packet_timeout_height') as string const timeoutHeight = Number(timeoutHeightRaw.split('-')[1]) - const timeoutTimestampRaw = event.attributes.filter( - (v) => v.key === 'packet_timeout_timestamp' - )[0].value + const timeoutTimestampRaw = find(event, 'packet_timeout_timestamp') as string const timeoutTimestamp = Number(BigInt(timeoutTimestampRaw) / 1_000_000_000n) // store in second - const ackHex = event.attributes.filter((v) => v.key === 'packet_ack_hex') + const ackHex = find(event, 'packet_ack_hex') - const ordering = event.attributes.filter( - (v) => v.key === 'packet_channel_ordering' - )[0].value + const ordering = find(event, 'packet_channel_ordering') as string - const ack = - ackHex.length === 0 - ? undefined - : Buffer.from(ackHex[0].value, 'hex').toString('base64') + const ack = ackHex ? Buffer.from(ackHex, 'hex').toString('base64') : undefined return { height, @@ -78,15 +58,15 @@ export function parseChannelOpenEvent( const isSrc = event.type === 'channel_open_init' || event.type === 'channel_open_ack' - const connectionId = find(event, 'connection_id') + const connectionId = getConnection(event) as string - const portId = find(event, 'port_id') + const portId = find(event, 'port_id') as string - const channelId = find(event, 'channel_id') + const channelId = find(event, 'channel_id') as string - const counterpartyPortId = find(event, 'counterparty_port_id') + const counterpartyPortId = find(event, 'counterparty_port_id') as string - const counterpartyChannelId = find(event, 'counterparty_channel_id') + const counterpartyChannelId = find(event, 'counterparty_channel_id') as string return { height, @@ -103,11 +83,11 @@ export function parseChannelCloseEvent( event: Event, height: number ): ChannelOpenCloseInfo { - const portId = find(event, 'port_id') - const channelId = find(event, 'channel_id') - const connectionId = find(event, 'connection_id') - const counterpartyPortId = find(event, 'counterparty_port_id') - const counterpartyChannelId = find(event, 'counterparty_channel_id') + const portId = find(event, 'port_id') as string + const channelId = find(event, 'channel_id') as string + const connectionId = getConnection(event) as string + const counterpartyPortId = find(event, 'counterparty_port_id') as string + const counterpartyChannelId = find(event, 'counterparty_channel_id') as string const isSrc = event.type === 'channel_close_init' || 'channel_close' return { @@ -122,17 +102,17 @@ export function parseChannelCloseEvent( } export function parsePacketFeeEvent(event: Event): PacketFeeEvent { - const portId = find(event, 'port_id') + const portId = find(event, 'port_id') as string - const channelId = find(event, 'channel_id') + const channelId = find(event, 'channel_id') as string - const sequence = Number(find(event, 'packet_sequence')) + const sequence = Number(find(event, 'packet_sequence') as string) - const recvFee = find(event, 'recv_fee') + const recvFee = find(event, 'recv_fee') as string - const ackFee = find(event, 'ack_fee') + const ackFee = find(event, 'ack_fee') as string - const timeoutFee = find(event, 'timeout_fee') + const timeoutFee = find(event, 'timeout_fee') as string return { portId, @@ -144,12 +124,28 @@ export function parsePacketFeeEvent(event: Event): PacketFeeEvent { } } -function find(event: Event, key: string, defaultValue = ''): string { - const filtered = event.attributes.filter((v) => v.key === key) +function getConnection(event: Event): string | undefined { + return find(event, 'connection_id') || find(event, 'packet_connection') +} + +function find(event: Event, key: string, defaultVal = ''): string | undefined { + // check key + { + const vals = event.attributes.filter((v) => v.key === key) + if (vals.length !== 0) { + return vals[0].value + } + } - if (filtered.length === 0) { - return defaultValue + { + // check base64 encoded key + const vals = event.attributes.filter( + (v) => v.key === Buffer.from(`${key}`).toString('base64') + ) + if (vals.length !== 0) { + return Buffer.from(vals[0].value, 'base64').toString() + } } - return filtered[0].value + return undefined } diff --git a/src/workers/wallet.ts b/src/workers/wallet.ts index 47726e6..88a0904 100644 --- a/src/workers/wallet.ts +++ b/src/workers/wallet.ts @@ -25,6 +25,7 @@ import { PacketFee } from 'src/lib/config' // TODO: add update client worker export class WalletWorker { private sequence?: number + private accountNumber?: number private logger: Logger constructor( @@ -51,6 +52,12 @@ export class WalletWorker { } } + private async initAccInfo() { + const accInfo = await this.wallet.lcd.auth.accountInfo(this.address()) + this.sequence = accInfo.getSequenceNumber() + this.accountNumber = accInfo.getAccountNumber() + } + private async handlePackets() { if (this.chain.latestHeight === undefined) return @@ -295,16 +302,20 @@ export class WalletWorker { ...channelOpenMsgs, ] + // init sequence + if (!this.sequence) { + await this.initAccInfo() + if (this.sequence === undefined) { + throw Error('Failed to update sequence number') + } + } + const signedTx = await this.wallet.createAndSignTx({ msgs, sequence: this.sequence, + accountNumber: this.accountNumber, }) - // update sequence - if (!this.sequence) { - this.sequence = signedTx.auth_info.signer_infos[0].sequence - } - const result = await this.wallet.lcd.tx.broadcast(signedTx) if (isTxError(result)) { From 4041416d9625e48945e98df5ada1c5685477e94a Mon Sep 17 00:00:00 2001 From: ALPAC-4 Date: Mon, 4 Nov 2024 18:34:43 +0900 Subject: [PATCH 20/30] fix lint --- README.md | 13 ++-- config.example.json | 4 +- config.schema.json | 4 +- eslint.config.mjs | 16 ++--- package-lock.json | 92 +++++++++++++------------ package.json | 2 +- src/db/controller/channel.ts | 64 ++++++++--------- src/db/controller/client.ts | 14 ++-- src/db/controller/connection.ts | 12 ++-- src/db/controller/packet.ts | 80 ++++++++++----------- src/db/controller/syncInfo.ts | 2 +- src/db/utils.ts | 10 +-- src/lib/config.ts | 2 +- src/lib/eventParser.ts | 2 +- src/lib/{lcdClient.ts => restClient.ts} | 8 +-- src/msgs/ack.ts | 3 +- src/msgs/channelCloseConfirm.ts | 2 +- src/msgs/channelOpenAck.ts | 4 +- src/msgs/channelOpenConfirm.ts | 2 +- src/msgs/channelOpenTry.ts | 4 +- src/msgs/recvPacket.ts | 2 +- src/msgs/timeout.ts | 12 ++-- src/msgs/updateClient.ts | 8 +-- src/types/table.ts | 16 ++--- src/workers/chain.ts | 14 ++-- src/workers/index.ts | 29 ++++---- src/workers/wallet.ts | 35 +++++----- 27 files changed, 227 insertions(+), 229 deletions(-) rename src/lib/{lcdClient.ts => restClient.ts} (94%) diff --git a/README.md b/README.md index 059c23c..2fbdc99 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ npm install "bech32Prefix": "init", "chainId": "chain-1", "gasPrice": "0.15gas", - "lcdUri": "https://rest.chain-1.com", + "restUri": "https://rest.chain-1.com", "rpcUri": "https://rpc.chain-1.com", "wallets": [ { @@ -70,13 +70,18 @@ npm install "connections": [{ "connectionId": "conneciton-1" }] } } - ] + ], + "feeFilter": { + "recvFee": [{ "denom": "gas", "amount": 100 }], + "timeoutFee": [{ "denom": "gas", "amount": 200 }], + "ackFee": [{ "denom": "gas", "amount": 300 }] + } }, { "bech32Prefix": "init", "chainId": "chain-2", - "gasPrice": "0.15gas", - "lcdUri": "https://rest.chain-2.com", + "gasPrice": "0umin", + "restUri": "https://rest.chain-2.com", "rpcUri": "https://rpc.chain-2.com", "wallets": [ { diff --git a/config.example.json b/config.example.json index 1500402..641fcb4 100644 --- a/config.example.json +++ b/config.example.json @@ -8,7 +8,7 @@ "bech32Prefix": "init", "chainId": "chain-1", "gasPrice": "0.15gas", - "lcdUri": "https://rest.chain-1.com", + "restUri": "https://rest.chain-1.com", "rpcUri": "https://rpc.chain-1.com", "wallets": [ { @@ -40,7 +40,7 @@ "bech32Prefix": "init", "chainId": "chain-2", "gasPrice": "0umin", - "lcdUri": "https://rest.chain-2.com", + "restUri": "https://rest.chain-2.com", "rpcUri": "https://rpc.chain-2.com", "wallets": [ { diff --git a/config.schema.json b/config.schema.json index 198f077..05c572d 100644 --- a/config.schema.json +++ b/config.schema.json @@ -33,7 +33,7 @@ "bech32Prefix", "chainId", "gasPrice", - "lcdUri", + "restUri", "rpcUri", "wallets" ], @@ -50,7 +50,7 @@ "type": "string", "description": "gas price in format 0.1denom" }, - "lcdUri": { + "restUri": { "type": "string", "description": "cosmos rest api uri" }, diff --git a/eslint.config.mjs b/eslint.config.mjs index bdf3d4e..ea442eb 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,8 +1,7 @@ // @ts-check -import eslint from "@eslint/js"; -import tseslint from "typescript-eslint"; -import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended"; +import eslint from '@eslint/js' +import tseslint from 'typescript-eslint' export default tseslint.config( eslint.configs.recommended, @@ -12,17 +11,16 @@ export default tseslint.config( { languageOptions: { parserOptions: { - project: "./tsconfig.json", + project: './tsconfig.json', tsconfigRootDir: import.meta.dirname, }, }, }, { - files: ["**/*.js"], + files: ['**/*.js'], ...tseslint.configs.disableTypeChecked, }, - eslintPluginPrettierRecommended, { - ignores: ["dist/**/*", "**/*.config.mjs"], - }, -); + ignores: ['dist/**/*', '**/*.config.mjs'], + } +) diff --git a/package-lock.json b/package-lock.json index 4878bb8..0d2286d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@cosmjs/json-rpc": "^0.32.3", "@cosmjs/stargate": "^0.32.3", "@cosmjs/tendermint-rpc": "^0.32.3", - "@initia/initia.js": "^0.2.5", + "@initia/initia.js": "^0.2.20", "@initia/initia.proto": "^0.2.0", "axios": "^1.7.5", "bech32": "^2.0.0", @@ -369,17 +369,18 @@ } }, "node_modules/@initia/initia.js": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@initia/initia.js/-/initia.js-0.2.5.tgz", - "integrity": "sha512-smK0h2MlroXJQCY8yR73bUKuIU3WRnB3vPzbIDnCQKZHcqIZ5A8Sl/qz6LQX9cWeAlvuyDYnFDyd+UgSqLOYqg==", + "version": "0.2.21", + "resolved": "https://registry.npmjs.org/@initia/initia.js/-/initia.js-0.2.21.tgz", + "integrity": "sha512-R5fTGXAu0sBwaPHEIoT5BFtvflyfJn54ZgVbzn0Olke5hHKDHHOkZiuF0ymN+4EvgHEnX4ZTjZmtf4gMD4ddhg==", + "license": "Apache-2.0", "dependencies": { - "@initia/initia.proto": "^0.2.0", - "@initia/opinit.proto": "^0.0.8", + "@initia/initia.proto": "^0.2.3", + "@initia/opinit.proto": "^0.0.10", "@ledgerhq/hw-transport": "^6.27.12", "@ledgerhq/hw-transport-webhid": "^6.27.12", "@ledgerhq/hw-transport-webusb": "^6.27.12", - "@mysten/bcs": "^0.10.1", - "axios": "^1.6.5", + "@mysten/bcs": "^1.0.2", + "axios": "^1.7.4", "bech32": "^2.0.0", "bignumber.js": "^9.1.0", "bip32": "^2.0.6", @@ -389,32 +390,35 @@ "long": "^5.2.0", "ripemd160": "^2.0.2", "secp256k1": "^5.0.0", - "tmp": "^0.2.1", - "ws": "^7.5.9" + "semver": "^7.6.3", + "ws": "^7.5.10" }, "engines": { - "node": ">=14" + "node": ">=20" } }, "node_modules/@initia/initia.proto": { - "version": "0.2.0", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@initia/initia.proto/-/initia.proto-0.2.3.tgz", + "integrity": "sha512-lmhRNFTzYBGIYZgtiy2DLXIf6VsOXf9/WDgtSPjl9wlFchhbEJrR+PHBxQFYhHen5JmXpXWzESmTSGPUILOXHw==", "license": "Apache-2.0", "dependencies": { "@improbable-eng/grpc-web": "^0.15.0", - "google-protobuf": "^3.21.0", + "google-protobuf": "^3.21.4", "long": "^5.2.3", - "protobufjs": "^7.1.1" + "protobufjs": "^7.3.2" } }, "node_modules/@initia/opinit.proto": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/@initia/opinit.proto/-/opinit.proto-0.0.8.tgz", - "integrity": "sha512-MddIGje1CX2lT+jezpWDQv7ex/wLbAK9qCHu+RM/kY13yOwzMeeD539Au2llVB4LWwPEkd0h71HwxJWE78IE1A==", + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@initia/opinit.proto/-/opinit.proto-0.0.10.tgz", + "integrity": "sha512-AlLIAZMqeZSld1g0Fp/zXkVjQME19r8N3JEy9rISKqQpjKAIbgnZJ3ukR78nYGdr9TPpt8RBMdDWsjPnzD0xSQ==", + "license": "Apache-2.0", "dependencies": { "@improbable-eng/grpc-web": "^0.15.0", - "google-protobuf": "^3.21.0", - "long": "^5.2.0", - "protobufjs": "^7.1.1" + "google-protobuf": "^3.21.4", + "long": "^5.2.3", + "protobufjs": "^7.3.2" } }, "node_modules/@jridgewell/resolve-uri": { @@ -491,11 +495,12 @@ "integrity": "sha512-ExDoj1QV5eC6TEbMdLUMMk9cfvNKhhv5gXol4SmULRVCx/3iyCPhJ74nsb3S0Vb+/f+XujBEj3vQn5+cwS0fNA==" }, "node_modules/@mysten/bcs": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@mysten/bcs/-/bcs-0.10.1.tgz", - "integrity": "sha512-cQDb7Rhz2J82ZqgVQiHykuwKUlgiLWS2bjoajPPW0uvXlb75qrgKuaxh1UzsaRhHy3egk/APc0xjiZoqdbzB4w==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@mysten/bcs/-/bcs-1.1.0.tgz", + "integrity": "sha512-yy9/1Y4d0FlRywS1+9ze/T7refCbrvwFwJIOKs9M3QBK1njbcHZp+LkVeLqBvIJA5eZ3ZCzmhQ1Xq4Sed5mEBA==", + "license": "Apache-2.0", "dependencies": { - "bs58": "^5.0.0" + "bs58": "^6.0.0" } }, "node_modules/@noble/hashes": { @@ -1171,9 +1176,10 @@ "dev": true }, "node_modules/base-x": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz", - "integrity": "sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==" + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-5.0.0.tgz", + "integrity": "sha512-sMW3VGSX1QWVFA6l8U62MLKz29rRfpTlYdCqLdpLo1/Yd4zZwSbnUaDfciIAowAqvq7YFnWq9hrhdg1KYgc1lQ==", + "license": "MIT" }, "node_modules/base64-js": { "version": "1.5.1", @@ -1354,11 +1360,12 @@ "license": "Apache-2.0" }, "node_modules/bs58": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz", - "integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-6.0.0.tgz", + "integrity": "sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==", + "license": "MIT", "dependencies": { - "base-x": "^4.0.0" + "base-x": "^5.0.0" } }, "node_modules/bs58check": { @@ -2423,7 +2430,9 @@ } }, "node_modules/google-protobuf": { - "version": "3.21.2", + "version": "3.21.4", + "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.4.tgz", + "integrity": "sha512-MnG7N936zcKTco4Jd2PX2U96Kf9PxygAPKBug+74LHzmHXmceN16MmRcdgZv+DGef/S9YvQAfRsNCn4cjf9yyQ==", "license": "(BSD-3-Clause AND Apache-2.0)" }, "node_modules/gopd": { @@ -3299,7 +3308,9 @@ } }, "node_modules/protobufjs": { - "version": "7.3.0", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", + "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", "hasInstallScript": true, "license": "BSD-3-Clause", "dependencies": { @@ -3577,9 +3588,10 @@ "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" }, "node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -3922,14 +3934,6 @@ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, - "node_modules/tmp": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", - "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", - "engines": { - "node": ">=14.14" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", diff --git a/package.json b/package.json index 5e6b980..b7935b7 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@cosmjs/json-rpc": "^0.32.3", "@cosmjs/stargate": "^0.32.3", "@cosmjs/tendermint-rpc": "^0.32.3", - "@initia/initia.js": "^0.2.5", + "@initia/initia.js": "^0.2.20", "@initia/initia.proto": "^0.2.0", "axios": "^1.7.5", "bech32": "^2.0.0", diff --git a/src/db/controller/channel.ts b/src/db/controller/channel.ts index ec24b50..56eede3 100644 --- a/src/db/controller/channel.ts +++ b/src/db/controller/channel.ts @@ -1,12 +1,12 @@ import { DB } from '..' import { - Boolean, + Bool, ChannelOpenCloseTable, ChannelOpenCloseEvent, ChannelState, } from 'src/types' import { In, WhereOptions, del, insert, select, update } from '../utils' -import { LCDClient } from 'src/lib/lcdClient' +import { RESTClient } from 'src/lib/restClient' import { ConnectionController } from './connection' import { PacketController, PacketFilter } from './packet' import { Database } from 'better-sqlite3' @@ -14,7 +14,7 @@ import { Database } from 'better-sqlite3' export class ChannelController { static tableName = 'channel_open_close' public static async feedEvents( - lcd: LCDClient, + rest: RESTClient, chainId: string, events: ChannelOpenCloseEvent[] ): Promise<() => void> { @@ -22,28 +22,30 @@ export class ChannelController { for (const event of events) { switch (event.type) { case 'channel_open_init': - feedFns.push(await this.feedChannelOpenInitEvent(lcd, chainId, event)) + feedFns.push( + await this.feedChannelOpenInitEvent(rest, chainId, event) + ) break case 'channel_open_try': - feedFns.push(await this.feedChannelOpenTryEvent(lcd, chainId, event)) + feedFns.push(await this.feedChannelOpenTryEvent(rest, chainId, event)) break case 'channel_open_ack': - feedFns.push(await this.feedChannelOpenAckEvent(lcd, chainId, event)) + feedFns.push(await this.feedChannelOpenAckEvent(rest, chainId, event)) break case 'channel_open_confirm': feedFns.push( - await this.feedChannelOpenConfirmEvent(lcd, chainId, event) + await this.feedChannelOpenConfirmEvent(rest, chainId, event) ) break case 'channel_close': case 'channel_close_init': feedFns.push( - await this.feedChannelCloseInitEvent(lcd, chainId, event) + await this.feedChannelCloseInitEvent(rest, chainId, event) ) break case 'channel_close_confirm': feedFns.push( - await this.feedChannelCloseConfirmEvent(lcd, chainId, event) + await this.feedChannelCloseConfirmEvent(rest, chainId, event) ) break } @@ -69,7 +71,7 @@ export class ChannelController { for (const connectionFilter of filter.connections) { if (connectionFilter.channels) continue wheres.push({ - in_progress: Boolean.FALSE, + in_progress: Bool.FALSE, state, chain_id: chainId, connection_id: connectionFilter.connectionId, @@ -78,7 +80,7 @@ export class ChannelController { } } else { wheres.push({ - in_progress: Boolean.FALSE, + in_progress: Bool.FALSE, state, chain_id: chainId, counterparty_chain_id: In(counterpartyChainIds), @@ -112,7 +114,7 @@ export class ChannelController { update( DB, this.tableName, - { in_progress: inProgress ? Boolean.TRUE : Boolean.FALSE }, + { in_progress: inProgress ? Bool.TRUE : Bool.FALSE }, [{ id }] ) } @@ -120,24 +122,24 @@ export class ChannelController { public static resetPacketInProgress(db?: Database) { db = db ?? DB update(db, this.tableName, { - in_progress: Boolean.FALSE, + in_progress: Bool.FALSE, }) } private static async feedChannelOpenInitEvent( - lcd: LCDClient, + rest: RESTClient, chainId: string, event: ChannelOpenCloseEvent ): Promise<() => void> { const connection = await ConnectionController.getConnection( - lcd, + rest, chainId, event.channelOpenCloseInfo.srcConnectionId ) // add channel on open for dst chain const channelOnOpen: ChannelOpenCloseTable = { - in_progress: Boolean.FALSE, + in_progress: Bool.FALSE, height: event.channelOpenCloseInfo.height, state: ChannelState.INIT, chain_id: connection.counterparty_chain_id, @@ -156,19 +158,19 @@ export class ChannelController { } private static async feedChannelOpenTryEvent( - lcd: LCDClient, + rest: RESTClient, chainId: string, event: ChannelOpenCloseEvent ): Promise<() => void> { const connection = await ConnectionController.getConnection( - lcd, + rest, chainId, event.channelOpenCloseInfo.dstConnectionId ) // add channel on open for src chain const channelOnOpen: ChannelOpenCloseTable = { - in_progress: Boolean.FALSE, + in_progress: Bool.FALSE, height: event.channelOpenCloseInfo.height, state: ChannelState.TRYOPEN, chain_id: connection.counterparty_chain_id, @@ -195,19 +197,19 @@ export class ChannelController { } private static async feedChannelOpenAckEvent( - lcd: LCDClient, + rest: RESTClient, chainId: string, event: ChannelOpenCloseEvent ): Promise<() => void> { const connection = await ConnectionController.getConnection( - lcd, + rest, chainId, event.channelOpenCloseInfo.srcConnectionId ) // add channel on open for dst chain const channelOnOpen: ChannelOpenCloseTable = { - in_progress: Boolean.FALSE, + in_progress: Bool.FALSE, height: event.channelOpenCloseInfo.height, state: ChannelState.ACK, chain_id: connection.counterparty_chain_id, @@ -242,12 +244,12 @@ export class ChannelController { } private static async feedChannelOpenConfirmEvent( - lcd: LCDClient, + rest: RESTClient, chainId: string, event: ChannelOpenCloseEvent ): Promise<() => void> { const connection = await ConnectionController.getConnection( - lcd, + rest, chainId, event.channelOpenCloseInfo.dstConnectionId ) @@ -272,19 +274,19 @@ export class ChannelController { } private static async feedChannelCloseInitEvent( - lcd: LCDClient, + rest: RESTClient, chainId: string, event: ChannelOpenCloseEvent ): Promise<() => void> { const connection = await ConnectionController.getConnection( - lcd, + rest, chainId, event.channelOpenCloseInfo.srcConnectionId ) // add channel on open for dst chain const channelOnOpen: ChannelOpenCloseTable = { - in_progress: Boolean.FALSE, + in_progress: Bool.FALSE, height: event.channelOpenCloseInfo.height, state: ChannelState.CLOSE, chain_id: connection.counterparty_chain_id, @@ -302,12 +304,12 @@ export class ChannelController { } } - private static async feedChannelCloseConfirmEvent( - _lcd: LCDClient, + private static feedChannelCloseConfirmEvent( + _rest: RESTClient, chainId: string, event: ChannelOpenCloseEvent ): Promise<() => void> { - return () => { + return new Promise(() => { del(DB, this.tableName, [ { state: ChannelState.CLOSE, @@ -321,6 +323,6 @@ export class ChannelController { chainId, event.channelOpenCloseInfo.srcChannelId ) - } + }) } } diff --git a/src/db/controller/client.ts b/src/db/controller/client.ts index fd8570c..cab0991 100644 --- a/src/db/controller/client.ts +++ b/src/db/controller/client.ts @@ -3,16 +3,16 @@ import { insert, selectOne } from '../utils' import { Any } from 'cosmjs-types/google/protobuf/any' import { UpdateClientEvent, ClientTable } from 'src/types' import { Header } from 'cosmjs-types/ibc/lightclients/tendermint/v1/tendermint' -import { LCDClient } from 'src/lib/lcdClient' +import { RESTClient } from 'src/lib/restClient' export class ClientController { static tableName = 'client' public static async addClient( - lcd: LCDClient, + rest: RESTClient, chainId: string, clientId: string ): Promise { - const state = await lcd.ibc.getClientState(clientId) + const state = await rest.ibc.getClientState(clientId) const client: ClientTable = { chain_id: chainId, @@ -33,7 +33,7 @@ export class ClientController { } public static async feedUpdateClientEvent( - lcd: LCDClient, + rest: RESTClient, chainId: string, event: UpdateClientEvent ) { @@ -48,7 +48,7 @@ export class ClientController { const header = Header.decode(msg.value) // get client - const client = await this.getClient(lcd, chainId, clientId) + const client = await this.getClient(rest, chainId, clientId) // update client client.revision_height = parseInt( @@ -63,7 +63,7 @@ export class ClientController { } public static async getClient( - lcd: LCDClient, + rest: RESTClient, chainId: string, clientId: string ): Promise { @@ -75,6 +75,6 @@ export class ClientController { }, ]) - return client ?? this.addClient(lcd, chainId, clientId) + return client ?? this.addClient(rest, chainId, clientId) } } diff --git a/src/db/controller/connection.ts b/src/db/controller/connection.ts index 7f3185e..c222abb 100644 --- a/src/db/controller/connection.ts +++ b/src/db/controller/connection.ts @@ -2,19 +2,19 @@ import { DB } from '..' import { ConnectionTable } from 'src/types' import { ClientController } from './client' import { insert, selectOne } from '../utils' -import { LCDClient } from 'src/lib/lcdClient' +import { RESTClient } from 'src/lib/restClient' export class ConnectionController { static tableName = 'connection' public static async addConnection( - lcd: LCDClient, + rest: RESTClient, chainId: string, connectionId: string ): Promise { - const connectionInfo = await lcd.ibc.getConnection(connectionId) + const connectionInfo = await rest.ibc.getConnection(connectionId) const clientId = connectionInfo.connection.client_id - const client = await ClientController.getClient(lcd, chainId, clientId) + const client = await ClientController.getClient(rest, chainId, clientId) const connection: ConnectionTable = { chain_id: chainId, @@ -34,7 +34,7 @@ export class ConnectionController { // TODO: add connection_open_init event feeder public static async getConnection( - lcd: LCDClient, + rest: RESTClient, chainId: string, connectionId: string ): Promise { @@ -45,6 +45,6 @@ export class ConnectionController { }, ]) - return connection ?? this.addConnection(lcd, chainId, connectionId) + return connection ?? this.addConnection(rest, chainId, connectionId) } } diff --git a/src/db/controller/packet.ts b/src/db/controller/packet.ts index ed78314..3001992 100644 --- a/src/db/controller/packet.ts +++ b/src/db/controller/packet.ts @@ -7,14 +7,14 @@ import { PacketSendTable, PacketTimeoutTable, PacketWriteAckTable, - Boolean, + Bool, FeeType, } from 'src/types' import { DB } from '..' import { In, WhereOptions, del, insert, select, update } from '../utils' import { ConnectionController } from './connection' import { Database } from 'better-sqlite3' -import { LCDClient } from 'src/lib/lcdClient' +import { RESTClient } from 'src/lib/restClient' import { PacketFeeController } from './packetFee' import { PacketFee } from 'src/lib/config' @@ -24,7 +24,7 @@ export class PacketController { public static tableNamePacketWriteAck = 'packet_write_ack' public static async feedEvents( - lcd: LCDClient, + rest: RESTClient, chainId: string, events: PacketEvent[] ): Promise<() => void> { @@ -32,18 +32,18 @@ export class PacketController { for (const event of events) { switch (event.type) { case 'send_packet': - feedFns.push(await this.feedSendPacketEvent(lcd, chainId, event)) + feedFns.push(await this.feedSendPacketEvent(rest, chainId, event)) break case 'write_acknowledgement': - feedFns.push(await this.feedWriteAckEvent(lcd, chainId, event)) + feedFns.push(await this.feedWriteAckEvent(rest, chainId, event)) break case 'acknowledge_packet': feedFns.push( - await this.feedAcknowledgePacketEvent(lcd, chainId, event) + await this.feedAcknowledgePacketEvent(rest, chainId, event) ) break case 'timeout_packet': - feedFns.push(await this.feedTimeoutPacketEvent(lcd, chainId, event)) + feedFns.push(await this.feedTimeoutPacketEvent(rest, chainId, event)) break } } @@ -84,7 +84,7 @@ export class PacketController { // TODO: make this more efficientnet. filter connection by chain id wheres.push( ...filter.connections.map((conn) => ({ - in_progress: Boolean.FALSE, + in_progress: Bool.FALSE, dst_chain_id: chainId, dst_connection_id: conn.connectionId, dst_channel_id: conn.channels ? In(conn.channels) : undefined, @@ -94,7 +94,7 @@ export class PacketController { ) } else { wheres.push({ - in_progress: Boolean.FALSE, + in_progress: Bool.FALSE, dst_chain_id: chainId, src_chain_id: counterpartyChainId, custom, @@ -140,7 +140,7 @@ export class PacketController { // TODO: make this more efficientnet. filter connection by chain id wheres.push( ...filter.connections.map((conn) => ({ - in_progress: Boolean.FALSE, + in_progress: Bool.FALSE, src_chain_id: chainId, src_connection_id: conn.connectionId, src_channel_id: conn.channels ? In(conn.channels) : undefined, @@ -150,7 +150,7 @@ export class PacketController { ) } else { wheres.push({ - in_progress: Boolean.FALSE, + in_progress: Bool.FALSE, src_chain_id: chainId, dst_chain_id: In(counterpartyChainIds), custom, @@ -188,7 +188,7 @@ export class PacketController { if (filter.connections) { wheres.push( ...filter.connections.map((conn) => ({ - in_progress: Boolean.FALSE, + in_progress: Bool.FALSE, src_chain_id: chainId, src_connection_id: conn.connectionId, src_channel_id: conn.channels ? In(conn.channels) : undefined, @@ -198,7 +198,7 @@ export class PacketController { ) } else { wheres.push({ - in_progress: Boolean.FALSE, + in_progress: Bool.FALSE, src_chain_id: chainId, dst_chain_id: In(counterpartyChainIds), custom, @@ -263,7 +263,7 @@ export class PacketController { update( DB, this.tableNamePacketSend, - { in_progress: inProgress ? Boolean.TRUE : Boolean.FALSE }, + { in_progress: inProgress ? Bool.TRUE : Bool.FALSE }, [ { dst_chain_id: packet.dst_chain_id, @@ -282,7 +282,7 @@ export class PacketController { update( DB, this.tableNamePacketTimeout, - { in_progress: inProgress ? Boolean.TRUE : Boolean.FALSE }, + { in_progress: inProgress ? Bool.TRUE : Bool.FALSE }, [ { src_chain_id: packet.src_chain_id, @@ -301,7 +301,7 @@ export class PacketController { update( DB, this.tableNamePacketWriteAck, - { in_progress: inProgress ? Boolean.TRUE : Boolean.FALSE }, + { in_progress: inProgress ? Bool.TRUE : Bool.FALSE }, [ { src_chain_id: packet.src_chain_id, @@ -316,24 +316,24 @@ export class PacketController { public static resetPacketInProgress(db?: Database) { db = db ?? DB update(db, this.tableNamePacketSend, { - in_progress: Boolean.FALSE, + in_progress: Bool.FALSE, }) update(db, this.tableNamePacketTimeout, { - in_progress: Boolean.FALSE, + in_progress: Bool.FALSE, }) update(db, this.tableNamePacketWriteAck, { - in_progress: Boolean.FALSE, + in_progress: Bool.FALSE, }) } private static async feedSendPacketEvent( - lcd: LCDClient, + rest: RESTClient, chainId: string, event: SendPacketEvent ): Promise<() => void> { // get counterparty's info const connection = await ConnectionController.getConnection( - lcd, + rest, chainId, event.packetInfo.connectionId ) @@ -344,11 +344,9 @@ export class PacketController { dst_connection_id: connection.counterparty_connection_id, dst_channel_id: event.packetInfo.dstChannel, sequence: Number(event.packetInfo.sequence), - in_progress: Boolean.FALSE, + in_progress: Bool.FALSE, is_ordered: - 'ORDER_ORDERED' === event.packetInfo.ordering - ? Boolean.TRUE - : Boolean.FALSE, + 'ORDER_ORDERED' === event.packetInfo.ordering ? Bool.TRUE : Bool.FALSE, height: event.packetInfo.height, dst_port: event.packetInfo.dstPort, src_chain_id: chainId, @@ -368,11 +366,9 @@ export class PacketController { src_connection_id: event.packetInfo.connectionId, src_channel_id: event.packetInfo.srcChannel, sequence: Number(event.packetInfo.sequence), - in_progress: Boolean.FALSE, + in_progress: Bool.FALSE, is_ordered: - 'ORDER_ORDERED' === event.packetInfo.ordering - ? Boolean.TRUE - : Boolean.FALSE, + 'ORDER_ORDERED' === event.packetInfo.ordering ? Bool.TRUE : Bool.FALSE, src_port: event.packetInfo.srcPort, dst_chain_id: connection.counterparty_chain_id, dst_connection_id: connection.counterparty_connection_id, @@ -390,11 +386,11 @@ export class PacketController { insert(DB, this.tableNamePacketTimeout, packetTimeout) // if channel is ordered channel, update in progress for higher sequence - if (packetSend.is_ordered === Boolean.TRUE) { + if (packetSend.is_ordered === Bool.TRUE) { update( DB, this.tableNamePacketSend, - { in_progress: Boolean.FALSE }, + { in_progress: Bool.FALSE }, [ { dst_chain_id: packetSend.dst_chain_id, @@ -407,13 +403,13 @@ export class PacketController { } private static async feedWriteAckEvent( - lcd: LCDClient, + rest: RESTClient, chainId: string, event: WriteAckEvent ): Promise<() => void> { // get counterparty's info const connection = await ConnectionController.getConnection( - lcd, + rest, chainId, event.packetInfo.connectionId ) @@ -424,11 +420,9 @@ export class PacketController { src_connection_id: connection.counterparty_connection_id, src_channel_id: event.packetInfo.srcChannel, sequence: Number(event.packetInfo.sequence), - in_progress: Boolean.FALSE, + in_progress: Bool.FALSE, is_ordered: - 'ORDER_ORDERED' === event.packetInfo.ordering - ? Boolean.TRUE - : Boolean.FALSE, + 'ORDER_ORDERED' === event.packetInfo.ordering ? Bool.TRUE : Bool.FALSE, height: event.packetInfo.height, src_port: event.packetInfo.srcPort, dst_chain_id: chainId, @@ -469,11 +463,11 @@ export class PacketController { insert(DB, this.tableNamePacketWriteAck, packetWriteAck) // if channel is ordered channel, update in progress for higher sequence - if (packetWriteAck.is_ordered === Boolean.TRUE) { + if (packetWriteAck.is_ordered === Bool.TRUE) { update( DB, this.tableNamePacketSend, - { in_progress: Boolean.FALSE }, + { in_progress: Bool.FALSE }, [ { dst_chain_id: chainId, @@ -486,13 +480,13 @@ export class PacketController { } private static async feedAcknowledgePacketEvent( - lcd: LCDClient, + rest: RESTClient, chainId: string, event: AcknowledgePacketEvent ): Promise<() => void> { // get counterparty's info const connection = await ConnectionController.getConnection( - lcd, + rest, chainId, event.packetInfo.connectionId ) @@ -551,13 +545,13 @@ export class PacketController { } private static async feedTimeoutPacketEvent( - lcd: LCDClient, + rest: RESTClient, chainId: string, event: TimeoutPacketEvent ): Promise<() => void> { // get counterparty's info const connection = await ConnectionController.getConnection( - lcd, + rest, chainId, event.packetInfo.connectionId ) diff --git a/src/db/controller/syncInfo.ts b/src/db/controller/syncInfo.ts index 37d437f..b95afff 100644 --- a/src/db/controller/syncInfo.ts +++ b/src/db/controller/syncInfo.ts @@ -10,7 +10,7 @@ export class SyncInfoController { latestHeight: number ): SyncInfoTable[] { startHeights = startHeights.sort() - let syncInfos = this.getSyncInfos(chainId) + const syncInfos = this.getSyncInfos(chainId) if (syncInfos.length === 0) { if (startHeights.length === 0) { diff --git a/src/db/utils.ts b/src/db/utils.ts index b1ae6ac..2cf1617 100644 --- a/src/db/utils.ts +++ b/src/db/utils.ts @@ -58,7 +58,7 @@ export function packetTableToPacket( // sql helper export function insert(db: Database, tableName: string, obj: T) { - const keys = Object.keys(obj as Object) + const keys = Object.keys(obj as object) const placeHolder = keys.map((key) => `$${key}`).join(',') const prepare = db.prepare( `INSERT INTO ${tableName} (${keys.join(',')}) VALUES (${placeHolder})` @@ -131,7 +131,7 @@ export function update( ) { let sql = `UPDATE ${tableName} SET` const params = [] - const keys = Object.keys(set as Object) + const keys = Object.keys(set as object) const placeHolder = keys.map((key) => ` ${key} = $${key}`).join(',') sql += placeHolder @@ -152,13 +152,13 @@ function where(wheres: WhereOptions[]): [string, ParamType[]] { for (const where of wheres) { const condition = [] - const keys = Object.keys(where) as Array + const keys = Object.keys(where) as (keyof T)[] for (const key of keys) { const value = where[key] if (typeof value === 'object' && value !== null) { if ('in' in value) { const vals = value.in as unknown[] - const placeHolder = vals.map((_) => '?').join(',') + const placeHolder = vals.map(() => '?').join(',') condition.push(`${String(key)} IN(${placeHolder})`) params.push(...vals.map(toParamType)) } else { @@ -211,7 +211,7 @@ function order(order: Order): string { let sql = '' const orders: string[] = [] - const keys = Object.keys(where) as Array + const keys = Object.keys(where) as (keyof T)[] for (const key of keys) { orders.push(`${String(key)} ${order[key]}`) } diff --git a/src/lib/config.ts b/src/lib/config.ts index 66f1d24..d46ddd5 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -17,7 +17,7 @@ interface ChainConfig { bech32Prefix: string chainId: string gasPrice: string - lcdUri: string + restUri: string rpcUri: string wallets: WalletConfig[] feeFilter?: PacketFee diff --git a/src/lib/eventParser.ts b/src/lib/eventParser.ts index fc1bcde..c3197c8 100644 --- a/src/lib/eventParser.ts +++ b/src/lib/eventParser.ts @@ -128,7 +128,7 @@ function getConnection(event: Event): string | undefined { return find(event, 'connection_id') || find(event, 'packet_connection') } -function find(event: Event, key: string, defaultVal = ''): string | undefined { +function find(event: Event, key: string): string | undefined { // check key { const vals = event.attributes.filter((v) => v.key === key) diff --git a/src/lib/lcdClient.ts b/src/lib/restClient.ts similarity index 94% rename from src/lib/lcdClient.ts rename to src/lib/restClient.ts index 1ae2265..5901939 100644 --- a/src/lib/lcdClient.ts +++ b/src/lib/restClient.ts @@ -2,17 +2,17 @@ import { APIRequester, Channel, IbcAPI as IbcAPI_, - LCDClientConfig, - LCDClient as LCDClient_, + RESTClientConfig, + RESTClient as RESTClient_, } from '@initia/initia.js' import { Order, State } from '@initia/initia.proto/ibc/core/channel/v1/channel' import { ClientState, ConnectionInfo } from 'src/types' -export class LCDClient extends LCDClient_ { +export class RESTClient extends RESTClient_ { public ibc: IbcAPI constructor( URL: string, - config?: LCDClientConfig, + config?: RESTClientConfig, apiRequester?: APIRequester ) { super(URL, config, apiRequester) diff --git a/src/msgs/ack.ts b/src/msgs/ack.ts index d958144..fe0ab83 100644 --- a/src/msgs/ack.ts +++ b/src/msgs/ack.ts @@ -1,5 +1,4 @@ -import { Packet } from '@initia/initia.js' -import { MsgAcknowledgement } from '@initia/initia.js/dist/core/ibc/core/channel/msgs' +import { Packet, MsgAcknowledgement } from '@initia/initia.js' import { Height } from 'cosmjs-types/ibc/core/client/v1/client' import { Transfrom } from 'src/lib/transform' import { convertProofsToIcs23, getRawProof } from 'src/lib/proof' diff --git a/src/msgs/channelCloseConfirm.ts b/src/msgs/channelCloseConfirm.ts index 23d6586..f835595 100644 --- a/src/msgs/channelCloseConfirm.ts +++ b/src/msgs/channelCloseConfirm.ts @@ -1,4 +1,4 @@ -import { MsgChannelCloseConfirm } from '@initia/initia.js/dist/core/ibc/core/channel/msgs' +import { MsgChannelCloseConfirm } from '@initia/initia.js' import { Height } from 'cosmjs-types/ibc/core/client/v1/client' import { getChannelProof } from 'src/lib/proof' import { ChainWorker } from 'src/workers/chain' diff --git a/src/msgs/channelOpenAck.ts b/src/msgs/channelOpenAck.ts index f20a321..1963f5c 100644 --- a/src/msgs/channelOpenAck.ts +++ b/src/msgs/channelOpenAck.ts @@ -1,4 +1,4 @@ -import { MsgChannelOpenAck } from '@initia/initia.js/dist/core/ibc/core/channel/msgs' +import { MsgChannelOpenAck } from '@initia/initia.js' import { Height } from 'cosmjs-types/ibc/core/client/v1/client' import { getChannelProof } from 'src/lib/proof' import { ChainWorker } from 'src/workers/chain' @@ -15,7 +15,7 @@ export async function generateMsgChannelOpenAck( ): Promise { const { channel: { version }, - } = await dstChain.lcd.ibc.channel(dstPortId, dstChannelId) + } = await dstChain.rest.ibc.channel(dstPortId, dstChannelId) return new MsgChannelOpenAck( srcPortId, diff --git a/src/msgs/channelOpenConfirm.ts b/src/msgs/channelOpenConfirm.ts index 99c5d89..a896d45 100644 --- a/src/msgs/channelOpenConfirm.ts +++ b/src/msgs/channelOpenConfirm.ts @@ -1,4 +1,4 @@ -import { MsgChannelOpenConfirm } from '@initia/initia.js/dist/core/ibc/core/channel/msgs' +import { MsgChannelOpenConfirm } from '@initia/initia.js' import { Height } from 'cosmjs-types/ibc/core/client/v1/client' import { getChannelProof } from 'src/lib/proof' import { ChainWorker } from 'src/workers/chain' diff --git a/src/msgs/channelOpenTry.ts b/src/msgs/channelOpenTry.ts index 2831cce..d74b9ea 100644 --- a/src/msgs/channelOpenTry.ts +++ b/src/msgs/channelOpenTry.ts @@ -1,5 +1,5 @@ import { Channel, ChannelCounterparty, State } from '@initia/initia.js' -import { MsgChannelOpenTry } from '@initia/initia.js/dist/core/ibc/core/channel/msgs' +import { MsgChannelOpenTry } from '@initia/initia.js' import { Height } from 'cosmjs-types/ibc/core/client/v1/client' import { getChannelProof } from 'src/lib/proof' import { ChainWorker } from 'src/workers/chain' @@ -16,7 +16,7 @@ export async function generateMsgChannelOpenTry( ): Promise { const { channel: { ordering, version }, - } = await srcChain.lcd.ibc.channel(srcPortId, srcChannelId) + } = await srcChain.rest.ibc.channel(srcPortId, srcChannelId) const channel = new Channel( State.STATE_TRYOPEN, diff --git a/src/msgs/recvPacket.ts b/src/msgs/recvPacket.ts index 483b9d6..e5b3e6f 100644 --- a/src/msgs/recvPacket.ts +++ b/src/msgs/recvPacket.ts @@ -1,5 +1,5 @@ import { Packet } from '@initia/initia.js' -import { MsgRecvPacket } from '@initia/initia.js/dist/core/ibc/core/channel/msgs' +import { MsgRecvPacket } from '@initia/initia.js' import { Height } from 'cosmjs-types/ibc/core/client/v1/client' import { Transfrom } from 'src/lib/transform' import { convertProofsToIcs23, getRawProof } from 'src/lib/proof' diff --git a/src/msgs/timeout.ts b/src/msgs/timeout.ts index 66eb196..5d65ae9 100644 --- a/src/msgs/timeout.ts +++ b/src/msgs/timeout.ts @@ -1,11 +1,7 @@ -import { - MsgTimeout, - MsgTimeoutOnClose, -} from '@initia/initia.js/dist/core/ibc/core/channel/msgs' import { Height } from 'cosmjs-types/ibc/core/client/v1/client' import { Uint64 } from '@cosmjs/math' import { Transfrom } from 'src/lib/transform' -import { Packet } from '@initia/initia.js' +import { Packet, MsgTimeout, MsgTimeoutOnClose } from '@initia/initia.js' import { convertProofsToIcs23, getChannelProof, @@ -13,7 +9,7 @@ import { } from 'src/lib/proof' import { delay } from 'bluebird' import { ChainWorker } from 'src/workers/chain' -import { Boolean, PacketTimeoutTable } from 'src/types' +import { Bool, PacketTimeoutTable } from 'src/types' import { packetTableToPacket } from 'src/db/utils' export async function generateMsgTimeout( @@ -28,7 +24,7 @@ export async function generateMsgTimeout( dstChain, packet, proofHeight, - packetTable.is_ordered === Boolean.TRUE + packetTable.is_ordered === Bool.TRUE ) return new MsgTimeout( @@ -52,7 +48,7 @@ export async function generateMsgTimeoutOnClose( dstChain, packet, proofHeight, - packetTable.is_ordered === Boolean.TRUE + packetTable.is_ordered === Bool.TRUE ) const channelProof = await getChannelProof( diff --git a/src/msgs/updateClient.ts b/src/msgs/updateClient.ts index 2d2404d..c8db151 100644 --- a/src/msgs/updateClient.ts +++ b/src/msgs/updateClient.ts @@ -1,5 +1,5 @@ import { getSignedHeader } from './signedHeader' -import { MsgUpdateClient } from '@initia/initia.js/dist/core/ibc/core/client/msgs' +import { MsgUpdateClient } from '@initia/initia.js' import { Header } from 'cosmjs-types/ibc/lightclients/tendermint/v1/tendermint' import { ValidatorSet, @@ -19,7 +19,7 @@ export async function generateMsgUpdateClient( height: Height }> { const latestHeight = Number( - ((await dstChain.lcd.ibc.clientState(dstClientId)) as ClientState) + ((await dstChain.rest.ibc.clientState(dstClientId)) as ClientState) .client_state.latest_height.revision_height ) const signedHeader = await getSignedHeader(srcChain) @@ -73,12 +73,12 @@ async function getValidatorSet( chain: ChainWorker, height: number ): Promise { - let block = await chain.lcd.tendermint + let block = await chain.rest.tendermint .blockInfo(height) .catch(() => undefined) let count = 0 while (block === undefined) { - block = await chain.lcd.tendermint.blockInfo(height).catch((e) => { + block = await chain.rest.tendermint.blockInfo(height).catch((e) => { if (count > 5) { throw e } diff --git a/src/types/table.ts b/src/types/table.ts index 3837148..dbfbfc4 100644 --- a/src/types/table.ts +++ b/src/types/table.ts @@ -33,8 +33,8 @@ export interface PacketSendTable { dst_connection_id: string dst_channel_id: string sequence: number - in_progress: Boolean - is_ordered: Boolean + in_progress: Bool + is_ordered: Bool height: number dst_port: string src_chain_id: string @@ -53,8 +53,8 @@ export interface PacketTimeoutTable { src_connection_id: string src_channel_id: string sequence: number - in_progress: Boolean - is_ordered: Boolean + in_progress: Bool + is_ordered: Bool src_port: string dst_chain_id: string dst_connection_id: string @@ -72,8 +72,8 @@ export interface PacketWriteAckTable { src_connection_id: string src_channel_id: string sequence: number - in_progress: Boolean - is_ordered: Boolean + in_progress: Bool + is_ordered: Bool height: number src_port: string dst_chain_id: string @@ -90,7 +90,7 @@ export interface PacketWriteAckTable { export interface ChannelOpenCloseTable { id?: number - in_progress: Boolean + in_progress: Bool height: number state: ChannelState chain_id: string @@ -112,7 +112,7 @@ export interface PacketFeeTable { amount: number } -export enum Boolean { +export enum Bool { TRUE = 1, FALSE = 0, } diff --git a/src/workers/chain.ts b/src/workers/chain.ts index 1d1b6fc..e7e77ab 100644 --- a/src/workers/chain.ts +++ b/src/workers/chain.ts @@ -12,7 +12,7 @@ import { SyncInfoController } from 'src/db/controller/syncInfo' import { PacketController } from 'src/db/controller/packet' import { delay } from 'bluebird' import { Logger } from 'winston' -import { LCDClient } from 'src/lib/lcdClient' +import { RESTClient } from 'src/lib/restClient' import { ChannelController } from 'src/db/controller/channel' import { PacketFeeController } from 'src/db/controller/packetFee' import { PacketFee } from 'src/lib/config' @@ -25,7 +25,7 @@ export class ChainWorker { public constructor( public chainId: string, - public lcd: LCDClient, + public rest: RESTClient, public rpc: RPCClient, public bech32Prefix: string, public feeFilter: PacketFee, @@ -46,7 +46,7 @@ export class ChainWorker { syncInfo.end_height, syncInfo.synced_height ) - this.latestHeightWorker() + void this.latestHeightWorker() } } @@ -110,7 +110,7 @@ class SyncWorker { this.logger = createLoggerWithPrefix( `` ) - this.feedEvents() + void this.feedEvents() } private async feedEvents() { @@ -144,13 +144,13 @@ class SyncWorker { let finish = false const pakcetEventFeed = await PacketController.feedEvents( - this.chain.lcd, + this.chain.rest, this.chain.chainId, packetEvents ) const channelOpenEventFeed = await ChannelController.feedEvents( - this.chain.lcd, + this.chain.rest, this.chain.chainId, channelOpenEvents ) @@ -203,7 +203,7 @@ class SyncWorker { const channelOpenEvents: ChannelOpenCloseEvent[] = [] const packetFeeEvents: PacketFeeEvent[] = [] - txData.map((data, i) => { + txData.map((data) => { for (const event of data.events) { if ( event.type === 'send_packet' || diff --git a/src/workers/index.ts b/src/workers/index.ts index d83c3d2..1081e2d 100644 --- a/src/workers/index.ts +++ b/src/workers/index.ts @@ -11,7 +11,6 @@ import { generateMsgTimeoutOnClose, } from 'src/msgs' import { Height } from 'cosmjs-types/ibc/core/client/v1/client' -import { MsgUpdateClient } from '@initia/initia.js/dist/core/ibc/core/client/msgs' import { ClientController } from 'src/db/controller/client' import { ChannelOpenCloseTable, @@ -21,11 +20,16 @@ import { } from 'src/types' import { MsgRecvPacket, + MsgUpdateClient, Key, MnemonicKey, RawKey, APIRequester, Wallet, + MsgChannelCloseConfirm, + MsgChannelOpenAck, + MsgChannelOpenConfirm, + MsgChannelOpenTry, } from '@initia/initia.js' import { Config, PacketFee, KeyConfig } from 'src/lib/config' import { env } from 'node:process' @@ -33,13 +37,8 @@ import { RPCClient } from 'src/lib/rpcClient' import * as http from 'http' import * as https from 'https' import { PacketFilter } from 'src/db/controller/packet' -import { LCDClient } from 'src/lib/lcdClient' -import { - MsgChannelCloseConfirm, - MsgChannelOpenAck, - MsgChannelOpenConfirm, - MsgChannelOpenTry, -} from '@initia/initia.js/dist/core/ibc/core/channel/msgs' +import { RESTClient } from 'src/lib/restClient' + import { State } from '@initia/initia.proto/ibc/core/channel/v1/channel' import { generateMsgChannelCloseConfirm } from 'src/msgs/channelCloseConfirm' @@ -61,13 +60,13 @@ export class WorkerController { this.initiated = true for (const chainConfig of config.chains) { - const lcd = new LCDClient( - chainConfig.lcdUri, + const rest = new RESTClient( + chainConfig.restUri, { chainId: chainConfig.chainId, gasPrices: chainConfig.gasPrice, }, - new APIRequester(chainConfig.lcdUri, { + new APIRequester(chainConfig.restUri, { httpAgent: new http.Agent({ keepAlive: true }), httpsAgent: new https.Agent({ keepAlive: true }), timeout: 60000, @@ -77,7 +76,7 @@ export class WorkerController { const latestHeight = await queryLatestHeight(rpc) const chain = new ChainWorker( chainConfig.chainId, - lcd, + rest, rpc, chainConfig.bech32Prefix, chainConfig.feeFilter ?? {}, @@ -95,7 +94,7 @@ export class WorkerController { chain, this, walletConfig.maxHandlePakcet ?? 100, - new Wallet(lcd, key), + new Wallet(rest, key), walletConfig.packetFilter ) @@ -164,7 +163,7 @@ export class WorkerController { }> { // get client const client = await ClientController.getClient( - this.chains[chainId].lcd, + this.chains[chainId].rest, chainId, clientId ) @@ -202,7 +201,7 @@ export class WorkerController { ) { const dstChain = this.chains[packet.dst_chain_id] // check dst channel state - const channel = await dstChain.lcd.ibc.channel( + const channel = await dstChain.rest.ibc.channel( packet.dst_port, packet.dst_channel_id ) diff --git a/src/workers/wallet.ts b/src/workers/wallet.ts index 88a0904..9a6fc76 100644 --- a/src/workers/wallet.ts +++ b/src/workers/wallet.ts @@ -1,7 +1,7 @@ import { PacketController, PacketFilter } from 'src/db/controller/packet' import { ChainWorker } from './chain' import { - Boolean, + Bool, ChannelOpenCloseTable, ChannelState, PacketSendTable, @@ -10,10 +10,9 @@ import { } from 'src/types' import { WorkerController } from '.' import { DB } from 'src/db' -import { MsgUpdateClient } from '@initia/initia.js/dist/core/ibc/core/client/msgs' import { Height } from 'cosmjs-types/ibc/core/client/v1/client' import { ConnectionController } from 'src/db/controller/connection' -import { Wallet, isTxError } from '@initia/initia.js' +import { Wallet, isTxError, MsgUpdateClient } from '@initia/initia.js' import { createLoggerWithPrefix } from 'src/lib/logger' import { bech32 } from 'bech32' import { delay } from 'bluebird' @@ -38,7 +37,7 @@ export class WalletWorker { this.logger = createLoggerWithPrefix( `` ) - this.run() + void this.run() } public async run() { @@ -53,7 +52,7 @@ export class WalletWorker { } private async initAccInfo() { - const accInfo = await this.wallet.lcd.auth.accountInfo(this.address()) + const accInfo = await this.wallet.rest.auth.accountInfo(this.address()) this.sequence = accInfo.getSequenceNumber() this.accountNumber = accInfo.getAccountNumber() } @@ -186,7 +185,7 @@ export class WalletWorker { await Promise.all( connections.map(async (connection) => { const connectionInfo = await ConnectionController.getConnection( - this.chain.lcd, + this.chain.rest, this.chain.chainId, connection ) @@ -316,7 +315,7 @@ export class WalletWorker { accountNumber: this.accountNumber, }) - const result = await this.wallet.lcd.tx.broadcast(signedTx) + const result = await this.wallet.rest.tx.broadcast(signedTx) if (isTxError(result)) { if (result.raw_log.startsWith('account sequence mismatch')) { @@ -343,7 +342,9 @@ export class WalletWorker { this.sequence++ } catch (e) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access if (e?.response?.data) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access this.logger.error(e.response.data) } else { this.logger.error(e) @@ -393,7 +394,7 @@ export class WalletWorker { Object.keys(sendPacketMap).map(async (path) => { if (sendPacketMap[path].length === 0) return // check channel state - const dstChannel = await this.chain.lcd.ibc.channel( + const dstChannel = await this.chain.rest.ibc.channel( sendPacketMap[path][0].dst_port, sendPacketMap[path][0].dst_channel_id ) @@ -406,7 +407,7 @@ export class WalletWorker { const srcChannel = await this.workerController.chains[ sendPacketMap[path][0].src_chain_id - ].lcd.ibc.channel( + ].rest.ibc.channel( sendPacketMap[path][0].src_port, sendPacketMap[path][0].src_channel_id ) @@ -418,9 +419,9 @@ export class WalletWorker { } // handle ordered channels - if (sendPacketMap[path][0].is_ordered === Boolean.TRUE) { + if (sendPacketMap[path][0].is_ordered === Bool.TRUE) { // check next sequence - const nextSequence = await this.chain.lcd.ibc.nextSequence( + const nextSequence = await this.chain.rest.ibc.nextSequence( sendPacketMap[path][0].dst_port, sendPacketMap[path][0].dst_channel_id ) @@ -444,7 +445,7 @@ export class WalletWorker { return } - const unrecivedPackets = await this.chain.lcd.ibc.unreceivedPackets( + const unrecivedPackets = await this.chain.rest.ibc.unreceivedPackets( sendPacketMap[path][0].dst_port, sendPacketMap[path][0].dst_channel_id, sendPacketMap[path].map((packet) => packet.sequence) @@ -492,7 +493,7 @@ export class WalletWorker { await Promise.all( Object.keys(writeAckPacketMap).map(async (path) => { if (writeAckPacketMap[path].length === 0) return - const unrecivedAcks = await this.chain.lcd.ibc.unreceivedAcks( + const unrecivedAcks = await this.chain.rest.ibc.unreceivedAcks( writeAckPacketMap[path][0].src_port, writeAckPacketMap[path][0].src_channel_id, writeAckPacketMap[path].map((packet) => packet.sequence) @@ -542,7 +543,7 @@ export class WalletWorker { await Promise.all( Object.keys(timeoutPacketMap).map(async (path) => { if (timeoutPacketMap[path].length === 0) return - const unrecivedAcks = await this.chain.lcd.ibc.unreceivedAcks( + const unrecivedAcks = await this.chain.rest.ibc.unreceivedAcks( timeoutPacketMap[path][0].src_port, timeoutPacketMap[path][0].src_channel_id, timeoutPacketMap[path].map((packet) => packet.sequence) @@ -570,7 +571,7 @@ export class WalletWorker { if (timeoutPacketMap[path].length === 0) return const chain = this.workerController.chains[timeoutPacketMap[path][0].dst_chain_id] - const unrecivedPackets = await chain.lcd.ibc.unreceivedPackets( + const unrecivedPackets = await chain.rest.ibc.unreceivedPackets( timeoutPacketMap[path][0].dst_port, timeoutPacketMap[path][0].dst_channel_id, timeoutPacketMap[path].map((packet) => packet.sequence) @@ -622,13 +623,13 @@ export class WalletWorker { channelOnOpens.map(async (v) => { const counterpartyChain = this.workerController.chains[v.counterparty_chain_id] - const counterpartyChannel = await counterpartyChain.lcd.ibc.channel( + const counterpartyChannel = await counterpartyChain.rest.ibc.channel( v.counterparty_port_id, v.counterparty_channel_id ) const channel = v.channel_id !== '' - ? await this.chain.lcd.ibc.channel(v.port_id, v.channel_id) + ? await this.chain.rest.ibc.channel(v.port_id, v.channel_id) : undefined switch (v.state) { // check src channel state From d80118eed4a3eb9ccf08d7b04e641fd6bce931ee Mon Sep 17 00:00:00 2001 From: ALPAC-4 Date: Mon, 11 Nov 2024 12:16:51 +0900 Subject: [PATCH 21/30] add tests --- src/test/config.test.json | 41 ++++++++++++++++ src/test/mockServer/rest.ts | 43 ++++++++++++++++ src/test/mockServer/rpc.ts | 0 src/test/testSetup.ts | 98 +++++++++++++++++++++++++++++++++++++ 4 files changed, 182 insertions(+) create mode 100644 src/test/config.test.json create mode 100644 src/test/mockServer/rest.ts create mode 100644 src/test/mockServer/rpc.ts create mode 100644 src/test/testSetup.ts diff --git a/src/test/config.test.json b/src/test/config.test.json new file mode 100644 index 0000000..a78aa1d --- /dev/null +++ b/src/test/config.test.json @@ -0,0 +1,41 @@ +{ + "$schema": "../../config.schema.json", + "dbPath": "./test_db", + "port": 7010, + "metricPort": 70001, + "logLevel": "info", + "chains": [ + { + "bech32Prefix": "init", + "chainId": "chain-1", + "gasPrice": "0.15gas", + "restUri": "https://rest.chain-1.com", + "rpcUri": "https://rpc.chain-1.com", + "wallets": [ + { + "key": { + "type": "mnemonic", + "privateKey": "repair family apology column ..." + }, + "maxHandlePacket": 10 + } + ] + }, + { + "bech32Prefix": "init", + "chainId": "chain-2", + "gasPrice": "0gas", + "restUri": "https://rest.chain-2.com", + "rpcUri": "https://rpc.chain-2.com", + "wallets": [ + { + "key": { + "type": "raw", + "privateKey": "123..." + }, + "maxHandlePacket": 10 + } + ] + } + ] +} diff --git a/src/test/mockServer/rest.ts b/src/test/mockServer/rest.ts new file mode 100644 index 0000000..9843cfe --- /dev/null +++ b/src/test/mockServer/rest.ts @@ -0,0 +1,43 @@ +import { ClientState, ConnectionInfo } from 'src/types' +import { RESTClient } from 'src/lib/restClient' +import { APIRequester } from '@initia/initia.js' + +export class RestMockServer { + public connections: Record + private clients: Record + constructor( + public chainId: string, + public restUri: string + ) { + this.connections = {} + this.clients = {} + } + + client(): RESTClient { + return new RESTClient( + this.restUri, + undefined, + new APIRequester(this.restUri, { timeout: 500 }) + ) + } + + addConnection(connectionId: string, connectionInfo: ConnectionInfo) { + // check client exists + if (this.clients[connectionInfo.connection.client_id] === undefined) { + throw Error('client not found') + } + this.connections[connectionId] = connectionInfo + } + + getConnection(connectionId: string): ConnectionInfo { + return this.connections[connectionId] + } + + addClientState(clientId: string, clientState: ClientState) { + this.clients[clientId] = clientState + } + + getClientState(clientId: string): ClientState { + return this.clients[clientId] + } +} diff --git a/src/test/mockServer/rpc.ts b/src/test/mockServer/rpc.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/test/testSetup.ts b/src/test/testSetup.ts new file mode 100644 index 0000000..552ee38 --- /dev/null +++ b/src/test/testSetup.ts @@ -0,0 +1,98 @@ +import 'tsconfig-paths/register' +import { env } from 'node:process' +env.CONFIGFILE = 'src/test/config.test.json' +import { config } from 'src/lib/config' +import * as fs from 'fs' +import { initDBConnection } from 'src/db' +import { RestMockServer } from './mockServer/rest' +import { http, RequestHandler, HttpResponse } from 'msw' +import { setupServer } from 'msw/node' + +export let mockServers: { rest: RestMockServer }[] = [] + +const setup = () => { + // remove db + const dbPath = config.dbPath as string + + if (fs.existsSync(dbPath)) { + fs.rmSync(dbPath, { recursive: true }) + } + + fs.mkdirSync(dbPath) + + // init db and mock server + initDBConnection() + const handlers: RequestHandler[] = [] + mockServers = config.chains.map((chain, i) => { + const index = i + 1 + const counterpartyIndex = index + (index % 2 === 0 ? -1 : 1) + const restServer = new RestMockServer(chain.chainId, chain.restUri) + + restServer.addClientState(`07-tendermint-${index}`, { + client_state: { + chain_id: config.chains[counterpartyIndex - 1].chainId, + trusting_period: '3000s', + latest_height: { + revision_height: `${index}-100`, + }, + }, + }) + restServer.addConnection(`connection-${index}`, { + connection: { + client_id: `07-tendermint-${index}`, + counterparty: { + client_id: `07-tendermint-${counterpartyIndex}`, + connection_id: `connection-${counterpartyIndex}`, + }, + }, + }) + + handlers.push( + http.get( + new URL( + '/ibc/core/connection/v1/connections/:connectionId', + chain.restUri + ).href, + ({ params }) => { + const { connectionId } = params + // ...and respond to them using this JSON response. + try { + const connectionInfo = restServer.getConnection( + connectionId as string + ) + return HttpResponse.json(connectionInfo) + } catch (error) { + return new HttpResponse('Failed to retrieve connection info', { + status: 500, + }) + } + } + ) + ) + + handlers.push( + http.get( + new URL('/ibc/core/client/v1/client_states/:clientId', chain.restUri) + .href, + ({ params }) => { + const { clientId } = params + + try { + const clientState = restServer.getClientState(clientId as string) + return HttpResponse.json(clientState) + } catch (error) { + return new HttpResponse('Failed to retrieve client info', { + status: 500, + }) + } + } + ) + ) + + return { rest: restServer } + }) + + setupServer(...handlers).listen() +} + +beforeAll(() => setup()) From 9324fa80bef82d9e385837eda34e3d169af357b7 Mon Sep 17 00:00:00 2001 From: ALPAC-4 Date: Mon, 11 Nov 2024 12:18:14 +0900 Subject: [PATCH 22/30] add tests --- .gitignore | 4 +- config.schema.json | 3 + jest.config.js | 11 + package-lock.json | 7559 ++++++++++++++++++++------- package.json | 9 +- src/db/controller/channel.spec.ts | 315 ++ src/db/controller/channel.ts | 6 +- src/db/controller/connection.ts | 1 - src/db/controller/packet.spec.ts | 108 + src/db/controller/packetFee.spec.ts | 69 + src/db/controller/packetFee.ts | 1 - src/db/index.ts | 28 +- src/index.ts | 2 + src/lib/config.ts | 1 + src/lib/eventParser.spec.ts | 224 + src/lib/eventParser.ts | 2 +- src/types/event.ts | 2 +- tsconfig.json | 3 +- 18 files changed, 6441 insertions(+), 1907 deletions(-) create mode 100644 jest.config.js create mode 100644 src/db/controller/channel.spec.ts create mode 100644 src/db/controller/packet.spec.ts create mode 100644 src/db/controller/packetFee.spec.ts create mode 100644 src/lib/eventParser.spec.ts diff --git a/.gitignore b/.gitignore index 9772291..81080c9 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,6 @@ node_modules/ config.json /.idea -/.db \ No newline at end of file +/.db + +/test_db \ No newline at end of file diff --git a/config.schema.json b/config.schema.json index 05c572d..769037c 100644 --- a/config.schema.json +++ b/config.schema.json @@ -11,6 +11,9 @@ "port": { "type": "number" }, + "dbPath": { + "type": "string" + }, "metricPort": { "type": "number" }, diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..a90fbb3 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,11 @@ +// jest.config.js +module.exports = { + preset: 'ts-jest', + setupFilesAfterEnv: ['/src/test/testSetup.ts'], + testEnvironment: 'node', + testMatch: ['**/?(*.)spec.ts'], + moduleNameMapper: { + '^src/(.*)$': '/src/$1', + }, + maxWorkers: 1, +} diff --git a/package-lock.json b/package-lock.json index 0d2286d..30b9969 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,854 +27,881 @@ "prom-client": "^15.1.2", "ts-node": "^10.9.2", "tsconfig-paths": "^4.2.0", - "typescript": "^5.4.5", "winston": "^3.13.0" }, "devDependencies": { "@types/better-sqlite3": "^7.6.11", "@types/bluebird": "^3.5.42", "@types/express": "^4.17.21", + "@types/jest": "^29.5.14", "@types/node": "^22.5.4", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", "husky": "^9.0.11", + "jest": "^29.7.0", + "msw": "^2.6.2", + "ts-jest": "^29.2.5", + "typescript": "^5.6.3", "typescript-eslint": "^7.16.0" } }, - "node_modules/@colors/colors": { - "version": "1.6.0", - "license": "MIT", - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/@confio/ics23": { - "version": "0.6.8", + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, "license": "Apache-2.0", "dependencies": { - "@noble/hashes": "^1.0.0", - "protobufjs": "^6.8.8" - } - }, - "node_modules/@confio/ics23/node_modules/long": { - "version": "4.0.0", - "license": "Apache-2.0" - }, - "node_modules/@confio/ics23/node_modules/protobufjs": { - "version": "6.11.4", - "hasInstallScript": true, - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/long": "^4.0.1", - "@types/node": ">=13.7.0", - "long": "^4.0.0" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" }, - "bin": { - "pbjs": "bin/pbjs", - "pbts": "bin/pbts" + "engines": { + "node": ">=6.0.0" } }, - "node_modules/@cosmjs/amino": { - "version": "0.32.3", - "license": "Apache-2.0", + "node_modules/@ampproject/remapping/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@cosmjs/crypto": "^0.32.3", - "@cosmjs/encoding": "^0.32.3", - "@cosmjs/math": "^0.32.3", - "@cosmjs/utils": "^0.32.3" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@cosmjs/crypto": { - "version": "0.32.3", - "license": "Apache-2.0", + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@cosmjs/encoding": "^0.32.3", - "@cosmjs/math": "^0.32.3", - "@cosmjs/utils": "^0.32.3", - "@noble/hashes": "^1", - "bn.js": "^5.2.0", - "elliptic": "^6.5.4", - "libsodium-wrappers-sumo": "^0.7.11" + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@cosmjs/encoding": { - "version": "0.32.3", - "license": "Apache-2.0", - "dependencies": { - "base64-js": "^1.3.0", - "bech32": "^1.1.4", - "readonly-date": "^1.0.0" + "node_modules/@babel/compat-data": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", + "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@cosmjs/encoding/node_modules/bech32": { - "version": "1.1.4", - "license": "MIT" - }, - "node_modules/@cosmjs/json-rpc": { - "version": "0.32.3", - "resolved": "https://registry.npmjs.org/@cosmjs/json-rpc/-/json-rpc-0.32.3.tgz", - "integrity": "sha512-JwFRWZa+Y95KrAG8CuEbPVOSnXO2uMSEBcaAB/FBU3Mo4jQnDoUjXvt3vwtFWxfAytrWCn1I4YDFaOAinnEG/Q==", + "node_modules/@babel/core": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", + "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", + "dev": true, + "license": "MIT", "dependencies": { - "@cosmjs/stream": "^0.32.3", - "xstream": "^11.14.0" + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.0", + "@babel/generator": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.0", + "@babel/parser": "^7.26.0", + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.26.0", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "node_modules/@cosmjs/math": { - "version": "0.32.3", - "license": "Apache-2.0", + "node_modules/@babel/core/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", "dependencies": { - "bn.js": "^5.2.0" + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/@cosmjs/proto-signing": { - "version": "0.32.3", - "license": "Apache-2.0", - "dependencies": { - "@cosmjs/amino": "^0.32.3", - "@cosmjs/crypto": "^0.32.3", - "@cosmjs/encoding": "^0.32.3", - "@cosmjs/math": "^0.32.3", - "@cosmjs/utils": "^0.32.3", - "cosmjs-types": "^0.9.0" + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/@cosmjs/socket": { - "version": "0.32.3", - "license": "Apache-2.0", + "node_modules/@babel/generator": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", + "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", + "dev": true, + "license": "MIT", "dependencies": { - "@cosmjs/stream": "^0.32.3", - "isomorphic-ws": "^4.0.1", - "ws": "^7", - "xstream": "^11.14.0" + "@babel/parser": "^7.26.2", + "@babel/types": "^7.26.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@cosmjs/stargate": { - "version": "0.32.3", - "license": "Apache-2.0", + "node_modules/@babel/generator/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@confio/ics23": "^0.6.8", - "@cosmjs/amino": "^0.32.3", - "@cosmjs/encoding": "^0.32.3", - "@cosmjs/math": "^0.32.3", - "@cosmjs/proto-signing": "^0.32.3", - "@cosmjs/stream": "^0.32.3", - "@cosmjs/tendermint-rpc": "^0.32.3", - "@cosmjs/utils": "^0.32.3", - "cosmjs-types": "^0.9.0", - "xstream": "^11.14.0" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@cosmjs/stream": { - "version": "0.32.3", - "license": "Apache-2.0", + "node_modules/@babel/helper-compilation-targets": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", + "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", + "dev": true, + "license": "MIT", "dependencies": { - "xstream": "^11.14.0" + "@babel/compat-data": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@cosmjs/tendermint-rpc": { - "version": "0.32.3", - "license": "Apache-2.0", - "dependencies": { - "@cosmjs/crypto": "^0.32.3", - "@cosmjs/encoding": "^0.32.3", - "@cosmjs/json-rpc": "^0.32.3", - "@cosmjs/math": "^0.32.3", - "@cosmjs/socket": "^0.32.3", - "@cosmjs/stream": "^0.32.3", - "@cosmjs/utils": "^0.32.3", - "axios": "^1.6.0", - "readonly-date": "^1.0.0", - "xstream": "^11.14.0" + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/@cosmjs/utils": { - "version": "0.32.3", - "license": "Apache-2.0" - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { - "node": ">=12" - } - }, - "node_modules/@dabh/diagnostics": { - "version": "2.0.3", - "license": "MIT", - "dependencies": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" + "node": ">=6.9.0" } }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", "dev": true, + "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=6.9.0" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "@babel/core": "^7.0.0" } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", - "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", + "node_modules/@babel/helper-plugin-utils": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", + "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", "dev": true, + "license": "MIT", "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + "node": ">=6.9.0" } }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, + "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=6.9.0" } }, - "node_modules/@eslint/eslintrc/node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "dev": true, - "dependencies": { - "ms": "2.1.2" - }, + "license": "MIT", "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=6.9.0" } }, - "node_modules/@eslint/eslintrc/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", "dev": true, + "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=6.9.0" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", - "deprecated": "Use @eslint/config-array instead", + "node_modules/@babel/helpers": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", + "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", "dev": true, + "license": "MIT", "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.0" }, "engines": { - "node": ">=10.10.0" + "node": ">=6.9.0" } }, - "node_modules/@humanwhocodes/config-array/node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "node_modules/@babel/parser": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "@babel/types": "^7.26.0" }, - "engines": { - "node": ">=6.0" + "bin": { + "parser": "bin/babel-parser.js" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "engines": { + "node": ">=6.0.0" } }, - "node_modules/@humanwhocodes/config-array/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", "dev": true, - "engines": { - "node": ">=12.22" + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", - "dev": true + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "node_modules/@improbable-eng/grpc-web": { - "version": "0.15.0", - "license": "Apache-2.0", + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", "dependencies": { - "browser-headers": "^0.4.1" + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { - "google-protobuf": "^3.14.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@initia/initia.js": { - "version": "0.2.21", - "resolved": "https://registry.npmjs.org/@initia/initia.js/-/initia.js-0.2.21.tgz", - "integrity": "sha512-R5fTGXAu0sBwaPHEIoT5BFtvflyfJn54ZgVbzn0Olke5hHKDHHOkZiuF0ymN+4EvgHEnX4ZTjZmtf4gMD4ddhg==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", + "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", + "dev": true, + "license": "MIT", "dependencies": { - "@initia/initia.proto": "^0.2.3", - "@initia/opinit.proto": "^0.0.10", - "@ledgerhq/hw-transport": "^6.27.12", - "@ledgerhq/hw-transport-webhid": "^6.27.12", - "@ledgerhq/hw-transport-webusb": "^6.27.12", - "@mysten/bcs": "^1.0.2", - "axios": "^1.7.4", - "bech32": "^2.0.0", - "bignumber.js": "^9.1.0", - "bip32": "^2.0.6", - "bip39": "^3.0.4", - "jscrypto": "^1.0.3", - "keccak256": "^1.0.6", - "long": "^5.2.0", - "ripemd160": "^2.0.2", - "secp256k1": "^5.0.0", - "semver": "^7.6.3", - "ws": "^7.5.10" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { - "node": ">=20" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@initia/initia.proto": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@initia/initia.proto/-/initia.proto-0.2.3.tgz", - "integrity": "sha512-lmhRNFTzYBGIYZgtiy2DLXIf6VsOXf9/WDgtSPjl9wlFchhbEJrR+PHBxQFYhHen5JmXpXWzESmTSGPUILOXHw==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", "dependencies": { - "@improbable-eng/grpc-web": "^0.15.0", - "google-protobuf": "^3.21.4", - "long": "^5.2.3", - "protobufjs": "^7.3.2" + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@initia/opinit.proto": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/@initia/opinit.proto/-/opinit.proto-0.0.10.tgz", - "integrity": "sha512-AlLIAZMqeZSld1g0Fp/zXkVjQME19r8N3JEy9rISKqQpjKAIbgnZJ3ukR78nYGdr9TPpt8RBMdDWsjPnzD0xSQ==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", "dependencies": { - "@improbable-eng/grpc-web": "^0.15.0", - "google-protobuf": "^3.21.4", - "long": "^5.2.3", - "protobufjs": "^7.3.2" + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", + "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", + "dev": true, "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, "engines": { - "node": ">=6.0.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@ledgerhq/devices": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/@ledgerhq/devices/-/devices-8.3.0.tgz", - "integrity": "sha512-h5Scr+yIae8yjPOViCHLdMjpqn4oC2Whrsq8LinRxe48LEGMdPqSV1yY7+3Ch827wtzNpMv+/ilKnd8rY+rTlg==", + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@ledgerhq/errors": "^6.16.4", - "@ledgerhq/logs": "^6.12.0", - "rxjs": "^7.8.1", - "semver": "^7.3.5" + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@ledgerhq/errors": { - "version": "6.16.4", - "resolved": "https://registry.npmjs.org/@ledgerhq/errors/-/errors-6.16.4.tgz", - "integrity": "sha512-M57yFaLYSN+fZCX0E0zUqOmrV6eipK+s5RhijHoUNlHUqrsvUz7iRQgpd5gRgHB5VkIjav7KdaZjKiWGcHovaQ==" - }, - "node_modules/@ledgerhq/hw-transport": { - "version": "6.30.6", - "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport/-/hw-transport-6.30.6.tgz", - "integrity": "sha512-fT0Z4IywiuJuZrZE/+W0blkV5UCotDPFTYKLkKCLzYzuE6javva7D/ajRaIeR+hZ4kTmKF4EqnsmDCXwElez+w==", + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", "dependencies": { - "@ledgerhq/devices": "^8.3.0", - "@ledgerhq/errors": "^6.16.4", - "@ledgerhq/logs": "^6.12.0", - "events": "^3.3.0" + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@ledgerhq/hw-transport-webhid": { - "version": "6.28.6", - "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-webhid/-/hw-transport-webhid-6.28.6.tgz", - "integrity": "sha512-npU1mgL97KovpTUgcdORoOZ7eVFgwCA7zt0MpgUGUMRNJWDgCFsJslx7KrVXlCGOg87gLfDojreIre502I5pYg==", + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", "dependencies": { - "@ledgerhq/devices": "^8.3.0", - "@ledgerhq/errors": "^6.16.4", - "@ledgerhq/hw-transport": "^6.30.6", - "@ledgerhq/logs": "^6.12.0" + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@ledgerhq/hw-transport-webusb": { - "version": "6.28.6", - "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-webusb/-/hw-transport-webusb-6.28.6.tgz", - "integrity": "sha512-rzICsvhcFcL4wSAvRPe+b9EEWB8cxj6yWy3FZdfs7ufi/0muNpFXWckWv1TC34em55sGXu2cMcwMKXg/O/Lc0Q==", + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", "dependencies": { - "@ledgerhq/devices": "^8.3.0", - "@ledgerhq/errors": "^6.16.4", - "@ledgerhq/hw-transport": "^6.30.6", - "@ledgerhq/logs": "^6.12.0" + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@ledgerhq/logs": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/@ledgerhq/logs/-/logs-6.12.0.tgz", - "integrity": "sha512-ExDoj1QV5eC6TEbMdLUMMk9cfvNKhhv5gXol4SmULRVCx/3iyCPhJ74nsb3S0Vb+/f+XujBEj3vQn5+cwS0fNA==" - }, - "node_modules/@mysten/bcs": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@mysten/bcs/-/bcs-1.1.0.tgz", - "integrity": "sha512-yy9/1Y4d0FlRywS1+9ze/T7refCbrvwFwJIOKs9M3QBK1njbcHZp+LkVeLqBvIJA5eZ3ZCzmhQ1Xq4Sed5mEBA==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", "dependencies": { - "bs58": "^6.0.0" + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@noble/hashes": { - "version": "1.4.0", + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, "engines": { - "node": ">= 16" + "node": ">=6.9.0" }, - "funding": { - "url": "https://paulmillr.com/funding/" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "dev": true, + "license": "MIT", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "@babel/helper-plugin-utils": "^7.14.5" }, "engines": { - "node": ">= 8" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", + "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, "engines": { - "node": ">= 8" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "node_modules/@babel/template": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", "dev": true, + "license": "MIT", "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { - "node": ">= 8" + "node": ">=6.9.0" } }, - "node_modules/@opentelemetry/api": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", - "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "node_modules/@babel/traverse": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", + "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/generator": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/template": "^7.25.9", + "@babel/types": "^7.25.9", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, "engines": { - "node": ">=8.0.0" + "node": ">=6.9.0" } }, - "node_modules/@pkgr/core": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", - "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "node_modules/@babel/traverse/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" }, - "funding": { - "url": "https://opencollective.com/unts" + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" } }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "license": "BSD-3-Clause" - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.11", - "license": "MIT" - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "license": "MIT" - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "license": "MIT" - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "license": "MIT" - }, - "node_modules/@types/better-sqlite3": { - "version": "7.6.11", - "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.11.tgz", - "integrity": "sha512-i8KcD3PgGtGBLl3+mMYA8PdKkButvPyARxA7IQAd6qeslht13qxb1zzO8dRCtE7U3IoJS782zDBAeoKiM695kg==", + "node_modules/@babel/types": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", "dev": true, + "license": "MIT", "dependencies": { - "@types/node": "*" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@types/bluebird": { - "version": "3.5.42", + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true, "license": "MIT" }, - "node_modules/@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "node_modules/@bundled-es-modules/cookie": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/cookie/-/cookie-2.0.1.tgz", + "integrity": "sha512-8o+5fRPLNbjbdGRRmJj3h6Hh1AQJf2dk3qQ/5ZFb+PXkRNiSoMGGUKlsgLfrxneb72axVJyIYji64E2+nNfYyw==", "dev": true, + "license": "ISC", "dependencies": { - "@types/connect": "*", - "@types/node": "*" + "cookie": "^0.7.2" } }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "node_modules/@bundled-es-modules/cookie/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "dev": true, - "dependencies": { - "@types/node": "*" + "license": "MIT", + "engines": { + "node": ">= 0.6" } }, - "node_modules/@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "node_modules/@bundled-es-modules/statuses": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/statuses/-/statuses-1.0.1.tgz", + "integrity": "sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==", "dev": true, + "license": "ISC", "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" + "statuses": "^2.0.1" } }, - "node_modules/@types/express-serve-static-core": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.1.tgz", - "integrity": "sha512-ej0phymbFLoCB26dbbq5PGScsf2JAJ4IJHjG10LalgUV36XKTmA4GdA+PVllKvRk0sEKt64X8975qFnkSi0hqA==", + "node_modules/@bundled-es-modules/tough-cookie": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/tough-cookie/-/tough-cookie-0.1.6.tgz", + "integrity": "sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==", "dev": true, + "license": "ISC", "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" + "@types/tough-cookie": "^4.0.5", + "tough-cookie": "^4.1.4" } }, - "node_modules/@types/http-errors": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", - "dev": true + "node_modules/@colors/colors": { + "version": "1.6.0", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } }, - "node_modules/@types/long": { - "version": "4.0.2", - "license": "MIT" + "node_modules/@confio/ics23": { + "version": "0.6.8", + "license": "Apache-2.0", + "dependencies": { + "@noble/hashes": "^1.0.0", + "protobufjs": "^6.8.8" + } }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true + "node_modules/@confio/ics23/node_modules/long": { + "version": "4.0.0", + "license": "Apache-2.0" }, - "node_modules/@types/node": { - "version": "22.5.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz", - "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==", + "node_modules/@confio/ics23/node_modules/protobufjs": { + "version": "6.11.4", + "hasInstallScript": true, + "license": "BSD-3-Clause", "dependencies": { - "undici-types": "~6.19.2" + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": ">=13.7.0", + "long": "^4.0.0" + }, + "bin": { + "pbjs": "bin/pbjs", + "pbts": "bin/pbts" } }, - "node_modules/@types/qs": { - "version": "6.9.15", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", - "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==", - "dev": true - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true + "node_modules/@cosmjs/amino": { + "version": "0.32.3", + "license": "Apache-2.0", + "dependencies": { + "@cosmjs/crypto": "^0.32.3", + "@cosmjs/encoding": "^0.32.3", + "@cosmjs/math": "^0.32.3", + "@cosmjs/utils": "^0.32.3" + } }, - "node_modules/@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "dev": true, + "node_modules/@cosmjs/crypto": { + "version": "0.32.3", + "license": "Apache-2.0", "dependencies": { - "@types/mime": "^1", - "@types/node": "*" + "@cosmjs/encoding": "^0.32.3", + "@cosmjs/math": "^0.32.3", + "@cosmjs/utils": "^0.32.3", + "@noble/hashes": "^1", + "bn.js": "^5.2.0", + "elliptic": "^6.5.4", + "libsodium-wrappers-sumo": "^0.7.11" } }, - "node_modules/@types/serve-static": { - "version": "1.15.7", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", - "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", - "dev": true, + "node_modules/@cosmjs/encoding": { + "version": "0.32.3", + "license": "Apache-2.0", "dependencies": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "*" + "base64-js": "^1.3.0", + "bech32": "^1.1.4", + "readonly-date": "^1.0.0" } }, - "node_modules/@types/triple-beam": { - "version": "1.3.5", + "node_modules/@cosmjs/encoding/node_modules/bech32": { + "version": "1.1.4", "license": "MIT" }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.16.0.tgz", - "integrity": "sha512-py1miT6iQpJcs1BiJjm54AMzeuMPBSPuKPlnT8HlfudbcS5rYeX5jajpLf3mrdRh9dA/Ec2FVUY0ifeVNDIhZw==", - "dev": true, + "node_modules/@cosmjs/json-rpc": { + "version": "0.32.3", + "resolved": "https://registry.npmjs.org/@cosmjs/json-rpc/-/json-rpc-0.32.3.tgz", + "integrity": "sha512-JwFRWZa+Y95KrAG8CuEbPVOSnXO2uMSEBcaAB/FBU3Mo4jQnDoUjXvt3vwtFWxfAytrWCn1I4YDFaOAinnEG/Q==", "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.16.0", - "@typescript-eslint/type-utils": "7.16.0", - "@typescript-eslint/utils": "7.16.0", - "@typescript-eslint/visitor-keys": "7.16.0", - "graphemer": "^1.4.0", - "ignore": "^5.3.1", - "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^7.0.0", - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "@cosmjs/stream": "^0.32.3", + "xstream": "^11.14.0" } }, - "node_modules/@typescript-eslint/parser": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.16.0.tgz", - "integrity": "sha512-ar9E+k7CU8rWi2e5ErzQiC93KKEFAXA2Kky0scAlPcxYblLt8+XZuHUZwlyfXILyQa95P6lQg+eZgh/dDs3+Vw==", - "dev": true, + "node_modules/@cosmjs/math": { + "version": "0.32.3", + "license": "Apache-2.0", "dependencies": { - "@typescript-eslint/scope-manager": "7.16.0", - "@typescript-eslint/types": "7.16.0", - "@typescript-eslint/typescript-estree": "7.16.0", - "@typescript-eslint/visitor-keys": "7.16.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "bn.js": "^5.2.0" } }, - "node_modules/@typescript-eslint/parser/node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", - "dev": true, + "node_modules/@cosmjs/proto-signing": { + "version": "0.32.3", + "license": "Apache-2.0", "dependencies": { - "ms": "2.1.2" + "@cosmjs/amino": "^0.32.3", + "@cosmjs/crypto": "^0.32.3", + "@cosmjs/encoding": "^0.32.3", + "@cosmjs/math": "^0.32.3", + "@cosmjs/utils": "^0.32.3", + "cosmjs-types": "^0.9.0" + } + }, + "node_modules/@cosmjs/socket": { + "version": "0.32.3", + "license": "Apache-2.0", + "dependencies": { + "@cosmjs/stream": "^0.32.3", + "isomorphic-ws": "^4.0.1", + "ws": "^7", + "xstream": "^11.14.0" + } + }, + "node_modules/@cosmjs/stargate": { + "version": "0.32.3", + "license": "Apache-2.0", + "dependencies": { + "@confio/ics23": "^0.6.8", + "@cosmjs/amino": "^0.32.3", + "@cosmjs/encoding": "^0.32.3", + "@cosmjs/math": "^0.32.3", + "@cosmjs/proto-signing": "^0.32.3", + "@cosmjs/stream": "^0.32.3", + "@cosmjs/tendermint-rpc": "^0.32.3", + "@cosmjs/utils": "^0.32.3", + "cosmjs-types": "^0.9.0", + "xstream": "^11.14.0" + } + }, + "node_modules/@cosmjs/stream": { + "version": "0.32.3", + "license": "Apache-2.0", + "dependencies": { + "xstream": "^11.14.0" + } + }, + "node_modules/@cosmjs/tendermint-rpc": { + "version": "0.32.3", + "license": "Apache-2.0", + "dependencies": { + "@cosmjs/crypto": "^0.32.3", + "@cosmjs/encoding": "^0.32.3", + "@cosmjs/json-rpc": "^0.32.3", + "@cosmjs/math": "^0.32.3", + "@cosmjs/socket": "^0.32.3", + "@cosmjs/stream": "^0.32.3", + "@cosmjs/utils": "^0.32.3", + "axios": "^1.6.0", + "readonly-date": "^1.0.0", + "xstream": "^11.14.0" + } + }, + "node_modules/@cosmjs/utils": { + "version": "0.32.3", + "license": "Apache-2.0" + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=12" } }, - "node_modules/@typescript-eslint/parser/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "license": "MIT", + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.16.0.tgz", - "integrity": "sha512-8gVv3kW6n01Q6TrI1cmTZ9YMFi3ucDT7i7aI5lEikk2ebk1AEjrwX8MDTdaX5D7fPXMBLvnsaa0IFTAu+jcfOw==", + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.16.0", - "@typescript-eslint/visitor-keys": "7.16.0" + "eslint-visitor-keys": "^3.3.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.16.0.tgz", - "integrity": "sha512-j0fuUswUjDHfqV/UdW6mLtOQQseORqfdmoBNDFOqs9rvNVR2e+cmu6zJu/Ku4SDuqiJko6YnhwcL8x45r8Oqxg==", + "node_modules/@eslint-community/regexpp": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", + "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.16.0", - "@typescript-eslint/utils": "7.16.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "url": "https://opencollective.com/eslint" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/debug": { + "node_modules/@eslint/eslintrc/node_modules/debug": { "version": "4.3.5", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", @@ -891,63 +918,37 @@ } } }, - "node_modules/@typescript-eslint/type-utils/node_modules/ms": { + "node_modules/@eslint/eslintrc/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "node_modules/@typescript-eslint/types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.16.0.tgz", - "integrity": "sha512-fecuH15Y+TzlUutvUl9Cc2XJxqdLr7+93SQIbcZfd4XRGGKoxyljK27b+kxKamjRkU7FYC6RrbSCg0ALcZn/xw==", + "node_modules/@eslint/js": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", "dev": true, "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.16.0.tgz", - "integrity": "sha512-a5NTvk51ZndFuOLCh5OaJBELYc2O3Zqxfl3Js78VFE1zE46J2AaVuW+rEbVkQznjkmlzWsUI15BG5tQMixzZLw==", + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "deprecated": "Use @eslint/config-array instead", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.16.0", - "@typescript-eslint/visitor-keys": "7.16.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" }, "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" + "node": ">=10.10.0" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { + "node_modules/@humanwhocodes/config-array/node_modules/debug": { "version": "4.3.5", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", @@ -964,869 +965,3433 @@ } } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "node_modules/@humanwhocodes/config-array/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=12.22" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", "dev": true }, - "node_modules/@typescript-eslint/utils": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.16.0.tgz", - "integrity": "sha512-PqP4kP3hb4r7Jav+NiRCntlVzhxBNWq6ZQ+zQwII1y/G/1gdIPeYDCKr2+dH6049yJQsWZiHU6RlwvIFBXXGNA==", - "dev": true, + "node_modules/@improbable-eng/grpc-web": { + "version": "0.15.0", + "license": "Apache-2.0", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.16.0", - "@typescript-eslint/types": "7.16.0", - "@typescript-eslint/typescript-estree": "7.16.0" + "browser-headers": "^0.4.1" + }, + "peerDependencies": { + "google-protobuf": "^3.14.0" + } + }, + "node_modules/@initia/initia.js": { + "version": "0.2.21", + "resolved": "https://registry.npmjs.org/@initia/initia.js/-/initia.js-0.2.21.tgz", + "integrity": "sha512-R5fTGXAu0sBwaPHEIoT5BFtvflyfJn54ZgVbzn0Olke5hHKDHHOkZiuF0ymN+4EvgHEnX4ZTjZmtf4gMD4ddhg==", + "license": "Apache-2.0", + "dependencies": { + "@initia/initia.proto": "^0.2.3", + "@initia/opinit.proto": "^0.0.10", + "@ledgerhq/hw-transport": "^6.27.12", + "@ledgerhq/hw-transport-webhid": "^6.27.12", + "@ledgerhq/hw-transport-webusb": "^6.27.12", + "@mysten/bcs": "^1.0.2", + "axios": "^1.7.4", + "bech32": "^2.0.0", + "bignumber.js": "^9.1.0", + "bip32": "^2.0.6", + "bip39": "^3.0.4", + "jscrypto": "^1.0.3", + "keccak256": "^1.0.6", + "long": "^5.2.0", + "ripemd160": "^2.0.2", + "secp256k1": "^5.0.0", + "semver": "^7.6.3", + "ws": "^7.5.10" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": ">=20" + } + }, + "node_modules/@initia/initia.proto": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@initia/initia.proto/-/initia.proto-0.2.3.tgz", + "integrity": "sha512-lmhRNFTzYBGIYZgtiy2DLXIf6VsOXf9/WDgtSPjl9wlFchhbEJrR+PHBxQFYhHen5JmXpXWzESmTSGPUILOXHw==", + "license": "Apache-2.0", + "dependencies": { + "@improbable-eng/grpc-web": "^0.15.0", + "google-protobuf": "^3.21.4", + "long": "^5.2.3", + "protobufjs": "^7.3.2" + } + }, + "node_modules/@initia/opinit.proto": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@initia/opinit.proto/-/opinit.proto-0.0.10.tgz", + "integrity": "sha512-AlLIAZMqeZSld1g0Fp/zXkVjQME19r8N3JEy9rISKqQpjKAIbgnZJ3ukR78nYGdr9TPpt8RBMdDWsjPnzD0xSQ==", + "license": "Apache-2.0", + "dependencies": { + "@improbable-eng/grpc-web": "^0.15.0", + "google-protobuf": "^3.21.4", + "long": "^5.2.3", + "protobufjs": "^7.3.2" + } + }, + "node_modules/@inquirer/confirm": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.0.1.tgz", + "integrity": "sha512-6ycMm7k7NUApiMGfVc32yIPp28iPKxhGRMqoNDiUjq2RyTAkbs5Fx0TdzBqhabcKvniDdAAvHCmsRjnNfTsogw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.0.1", + "@inquirer/type": "^3.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "engines": { + "node": ">=18" }, "peerDependencies": { - "eslint": "^8.56.0" + "@types/node": ">=18" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.16.0.tgz", - "integrity": "sha512-rMo01uPy9C7XxG7AFsxa8zLnWXTF8N3PYclekWSrurvhwiw1eW88mrKiAYe6s53AUY57nTRz8dJsuuXdkAhzCg==", + "node_modules/@inquirer/core": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.0.1.tgz", + "integrity": "sha512-KKTgjViBQUi3AAssqjUFMnMO3CM3qwCHvePV9EW+zTKGKafFGFF01sc1yOIYjLJ7QU52G/FbzKc+c01WLzXmVQ==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.16.0", - "eslint-visitor-keys": "^3.4.3" + "@inquirer/figures": "^1.0.7", + "@inquirer/type": "^3.0.0", + "ansi-escapes": "^4.3.2", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": ">=18" + } + }, + "node_modules/@inquirer/core/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "node_modules/@inquirer/core/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">= 0.6" + "node": ">=8" } }, - "node_modules/acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", - "bin": { - "acorn": "bin/acorn" - }, + "node_modules/@inquirer/figures": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.7.tgz", + "integrity": "sha512-m+Trk77mp54Zma6xLkLuY+mvanPxlE4A7yNKs2HBiyZ4UkVs28Mv5c/pgWrHeInx+USHeX/WEPzjrWrcJiQgjw==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=0.4.0" + "node": ">=18" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "node_modules/@inquirer/type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.0.tgz", + "integrity": "sha512-YYykfbw/lefC7yKj7nanzQXILM7r3suIvyFlCcMskc99axmsSewXWkAfXKwMbgxL76iAFVmRwmYdwNZNc8gjog==", "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "@types/node": ">=18" } }, - "node_modules/acorn-walk": { - "version": "8.3.2", - "license": "MIT", + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, "engines": { - "node": ">=0.4.0" + "node": ">=8" } }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, + "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "sprintf-js": "~1.0.2" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, "engines": { "node": ">=8" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/ansi-styles/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, + "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "p-try": "^2.0.0" }, "engines": { - "node": ">=7.0.0" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ansi-styles/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/arg": { - "version": "4.1.3", - "license": "MIT" - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, "engines": { "node": ">=8" } }, - "node_modules/async": { - "version": "3.2.5", - "license": "MIT" + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "node_modules/asynckit": { - "version": "0.4.0", - "license": "MIT" + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "node_modules/axios": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz", - "integrity": "sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==", + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } }, - "node_modules/base-x": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-5.0.0.tgz", - "integrity": "sha512-sMW3VGSX1QWVFA6l8U62MLKz29rRfpTlYdCqLdpLo1/Yd4zZwSbnUaDfciIAowAqvq7YFnWq9hrhdg1KYgc1lQ==", - "license": "MIT" + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } }, - "node_modules/base64-js": { - "version": "1.5.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } }, - "node_modules/bech32": { - "version": "2.0.0", - "license": "MIT" + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } }, - "node_modules/better-sqlite3": { - "version": "11.3.0", - "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.3.0.tgz", - "integrity": "sha512-iHt9j8NPYF3oKCNOO5ZI4JwThjt3Z6J6XrcwG85VNMVzv1ByqrHWv5VILEbCMFWDsoHhXvQ7oC8vgRXFAKgl9w==", - "hasInstallScript": true, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "license": "MIT", "dependencies": { - "bindings": "^1.5.0", - "prebuild-install": "^7.1.1" + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/bignumber.js": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", - "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, "engines": { - "node": "*" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "license": "MIT", "dependencies": { - "file-uri-to-path": "1.0.0" + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/bintrees": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.2.tgz", - "integrity": "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==" + "node_modules/@jest/reporters/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } }, - "node_modules/bip32": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.6.tgz", - "integrity": "sha512-HpV5OMLLGTjSVblmrtYRfFFKuQB+GArM0+XP8HGWfJ5vxYBqo+DesvJwOdC2WJ3bCkZShGf0QIfoIpeomVzVdA==", + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", "dependencies": { - "@types/node": "10.12.18", - "bs58check": "^2.1.1", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "tiny-secp256k1": "^1.1.3", - "typeforce": "^1.11.5", - "wif": "^2.0.6" + "@sinclair/typebox": "^0.27.8" }, "engines": { - "node": ">=6.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/bip32/node_modules/@types/node": { - "version": "10.12.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", - "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==" + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } }, - "node_modules/bip39": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.1.0.tgz", - "integrity": "sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A==", + "node_modules/@jest/source-map/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@noble/hashes": "^1.2.0" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "license": "MIT", "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/bl/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "license": "MIT", "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/bluebird": { - "version": "3.7.2", - "license": "MIT" + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } }, - "node_modules/bn.js": { - "version": "5.2.1", - "license": "MIT" + "node_modules/@jest/transform/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } }, - "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" }, "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, + "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "node_modules/@jridgewell/gen-mapping/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@ledgerhq/devices": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@ledgerhq/devices/-/devices-8.3.0.tgz", + "integrity": "sha512-h5Scr+yIae8yjPOViCHLdMjpqn4oC2Whrsq8LinRxe48LEGMdPqSV1yY7+3Ch827wtzNpMv+/ilKnd8rY+rTlg==", + "dependencies": { + "@ledgerhq/errors": "^6.16.4", + "@ledgerhq/logs": "^6.12.0", + "rxjs": "^7.8.1", + "semver": "^7.3.5" + } + }, + "node_modules/@ledgerhq/errors": { + "version": "6.16.4", + "resolved": "https://registry.npmjs.org/@ledgerhq/errors/-/errors-6.16.4.tgz", + "integrity": "sha512-M57yFaLYSN+fZCX0E0zUqOmrV6eipK+s5RhijHoUNlHUqrsvUz7iRQgpd5gRgHB5VkIjav7KdaZjKiWGcHovaQ==" + }, + "node_modules/@ledgerhq/hw-transport": { + "version": "6.30.6", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport/-/hw-transport-6.30.6.tgz", + "integrity": "sha512-fT0Z4IywiuJuZrZE/+W0blkV5UCotDPFTYKLkKCLzYzuE6javva7D/ajRaIeR+hZ4kTmKF4EqnsmDCXwElez+w==", + "dependencies": { + "@ledgerhq/devices": "^8.3.0", + "@ledgerhq/errors": "^6.16.4", + "@ledgerhq/logs": "^6.12.0", + "events": "^3.3.0" + } + }, + "node_modules/@ledgerhq/hw-transport-webhid": { + "version": "6.28.6", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-webhid/-/hw-transport-webhid-6.28.6.tgz", + "integrity": "sha512-npU1mgL97KovpTUgcdORoOZ7eVFgwCA7zt0MpgUGUMRNJWDgCFsJslx7KrVXlCGOg87gLfDojreIre502I5pYg==", + "dependencies": { + "@ledgerhq/devices": "^8.3.0", + "@ledgerhq/errors": "^6.16.4", + "@ledgerhq/hw-transport": "^6.30.6", + "@ledgerhq/logs": "^6.12.0" + } + }, + "node_modules/@ledgerhq/hw-transport-webusb": { + "version": "6.28.6", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-webusb/-/hw-transport-webusb-6.28.6.tgz", + "integrity": "sha512-rzICsvhcFcL4wSAvRPe+b9EEWB8cxj6yWy3FZdfs7ufi/0muNpFXWckWv1TC34em55sGXu2cMcwMKXg/O/Lc0Q==", + "dependencies": { + "@ledgerhq/devices": "^8.3.0", + "@ledgerhq/errors": "^6.16.4", + "@ledgerhq/hw-transport": "^6.30.6", + "@ledgerhq/logs": "^6.12.0" + } + }, + "node_modules/@ledgerhq/logs": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@ledgerhq/logs/-/logs-6.12.0.tgz", + "integrity": "sha512-ExDoj1QV5eC6TEbMdLUMMk9cfvNKhhv5gXol4SmULRVCx/3iyCPhJ74nsb3S0Vb+/f+XujBEj3vQn5+cwS0fNA==" + }, + "node_modules/@mswjs/interceptors": { + "version": "0.36.10", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.36.10.tgz", + "integrity": "sha512-GXrJgakgJW3DWKueebkvtYgGKkxA7s0u5B0P5syJM5rvQUnrpLPigvci8Hukl7yEM+sU06l+er2Fgvx/gmiRgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/logger": "^0.3.0", + "@open-draft/until": "^2.0.0", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.3", + "strict-event-emitter": "^0.5.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@mysten/bcs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@mysten/bcs/-/bcs-1.1.0.tgz", + "integrity": "sha512-yy9/1Y4d0FlRywS1+9ze/T7refCbrvwFwJIOKs9M3QBK1njbcHZp+LkVeLqBvIJA5eZ3ZCzmhQ1Xq4Sed5mEBA==", + "license": "Apache-2.0", + "dependencies": { + "bs58": "^6.0.0" + } + }, + "node_modules/@noble/hashes": { + "version": "1.4.0", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@open-draft/deferred-promise": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@open-draft/logger": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", + "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-node-process": "^1.2.0", + "outvariant": "^1.4.0" + } + }, + "node_modules/@open-draft/until": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", + "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "license": "BSD-3-Clause" + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "license": "MIT" + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/better-sqlite3": { + "version": "7.6.11", + "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.11.tgz", + "integrity": "sha512-i8KcD3PgGtGBLl3+mMYA8PdKkButvPyARxA7IQAd6qeslht13qxb1zzO8dRCtE7U3IoJS782zDBAeoKiM695kg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/bluebird": { + "version": "3.5.42", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.1.tgz", + "integrity": "sha512-ej0phymbFLoCB26dbbq5PGScsf2JAJ4IJHjG10LalgUV36XKTmA4GdA+PVllKvRk0sEKt64X8975qFnkSi0hqA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "dev": true + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/long": { + "version": "4.0.2", + "license": "MIT" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true + }, + "node_modules/@types/node": { + "version": "22.5.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz", + "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/qs": { + "version": "6.9.15", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", + "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "dev": true, + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/statuses": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.5.tgz", + "integrity": "sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.16.0.tgz", + "integrity": "sha512-py1miT6iQpJcs1BiJjm54AMzeuMPBSPuKPlnT8HlfudbcS5rYeX5jajpLf3mrdRh9dA/Ec2FVUY0ifeVNDIhZw==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.16.0", + "@typescript-eslint/type-utils": "7.16.0", + "@typescript-eslint/utils": "7.16.0", + "@typescript-eslint/visitor-keys": "7.16.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.16.0.tgz", + "integrity": "sha512-ar9E+k7CU8rWi2e5ErzQiC93KKEFAXA2Kky0scAlPcxYblLt8+XZuHUZwlyfXILyQa95P6lQg+eZgh/dDs3+Vw==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "7.16.0", + "@typescript-eslint/types": "7.16.0", + "@typescript-eslint/typescript-estree": "7.16.0", + "@typescript-eslint/visitor-keys": "7.16.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.16.0.tgz", + "integrity": "sha512-8gVv3kW6n01Q6TrI1cmTZ9YMFi3ucDT7i7aI5lEikk2ebk1AEjrwX8MDTdaX5D7fPXMBLvnsaa0IFTAu+jcfOw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.16.0", + "@typescript-eslint/visitor-keys": "7.16.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.16.0.tgz", + "integrity": "sha512-j0fuUswUjDHfqV/UdW6mLtOQQseORqfdmoBNDFOqs9rvNVR2e+cmu6zJu/Ku4SDuqiJko6YnhwcL8x45r8Oqxg==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "7.16.0", + "@typescript-eslint/utils": "7.16.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/@typescript-eslint/types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.16.0.tgz", + "integrity": "sha512-fecuH15Y+TzlUutvUl9Cc2XJxqdLr7+93SQIbcZfd4XRGGKoxyljK27b+kxKamjRkU7FYC6RrbSCg0ALcZn/xw==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.16.0.tgz", + "integrity": "sha512-a5NTvk51ZndFuOLCh5OaJBELYc2O3Zqxfl3Js78VFE1zE46J2AaVuW+rEbVkQznjkmlzWsUI15BG5tQMixzZLw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.16.0", + "@typescript-eslint/visitor-keys": "7.16.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/@typescript-eslint/utils": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.16.0.tgz", + "integrity": "sha512-PqP4kP3hb4r7Jav+NiRCntlVzhxBNWq6ZQ+zQwII1y/G/1gdIPeYDCKr2+dH6049yJQsWZiHU6RlwvIFBXXGNA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.16.0", + "@typescript-eslint/types": "7.16.0", + "@typescript-eslint/typescript-estree": "7.16.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.16.0.tgz", + "integrity": "sha512-rMo01uPy9C7XxG7AFsxa8zLnWXTF8N3PYclekWSrurvhwiw1eW88mrKiAYe6s53AUY57nTRz8dJsuuXdkAhzCg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.16.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.2", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansi-styles/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ansi-styles/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.5", + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz", + "integrity": "sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", + "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base-x": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-5.0.0.tgz", + "integrity": "sha512-sMW3VGSX1QWVFA6l8U62MLKz29rRfpTlYdCqLdpLo1/Yd4zZwSbnUaDfciIAowAqvq7YFnWq9hrhdg1KYgc1lQ==", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bech32": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/better-sqlite3": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.3.0.tgz", + "integrity": "sha512-iHt9j8NPYF3oKCNOO5ZI4JwThjt3Z6J6XrcwG85VNMVzv1ByqrHWv5VILEbCMFWDsoHhXvQ7oC8vgRXFAKgl9w==", + "hasInstallScript": true, + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + } + }, + "node_modules/bignumber.js": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", + "engines": { + "node": "*" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bintrees": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.2.tgz", + "integrity": "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==" + }, + "node_modules/bip32": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.6.tgz", + "integrity": "sha512-HpV5OMLLGTjSVblmrtYRfFFKuQB+GArM0+XP8HGWfJ5vxYBqo+DesvJwOdC2WJ3bCkZShGf0QIfoIpeomVzVdA==", + "dependencies": { + "@types/node": "10.12.18", + "bs58check": "^2.1.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "tiny-secp256k1": "^1.1.3", + "typeforce": "^1.11.5", + "wif": "^2.0.6" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bip32/node_modules/@types/node": { + "version": "10.12.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", + "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==" + }, + "node_modules/bip39": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.1.0.tgz", + "integrity": "sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A==", + "dependencies": { + "@noble/hashes": "^1.2.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "license": "MIT" + }, + "node_modules/bn.js": { + "version": "5.2.1", + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "license": "MIT" + }, + "node_modules/browser-headers": { + "version": "0.4.1", + "license": "Apache-2.0" + }, + "node_modules/browserslist": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", + "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001669", + "electron-to-chromium": "^1.5.41", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bs58": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-6.0.0.tgz", + "integrity": "sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==", + "license": "MIT", + "dependencies": { + "base-x": "^5.0.0" + } + }, + "node_modules/bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "dependencies": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/bs58check/node_modules/base-x": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", + "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/bs58check/node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "dependencies": { + "base-x": "^3.0.2" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001677", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001677.tgz", + "integrity": "sha512-fmfjsOlJUpMWu+mAAtZZZHz7UEwsUxIIvu1TJfO1HqFQvB/B+ii0xr9B5HpbZY/mC4XZ8SvjHJqtAY6pDPQEog==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", + "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/color": { + "version": "3.2.1", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.1", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/colorspace": { + "version": "1.1.4", + "license": "MIT", + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/cosmjs-types": { + "version": "0.9.0", + "license": "Apache-2.0" + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dedent": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.51", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.51.tgz", + "integrity": "sha512-kKeWV57KSS8jH4alKt/jKnvHPmJgBxXzGUSbMd4eQF+iOsVPl7bz2KUmu6eo80eMP8wVioTfTyTzdMgM15WXNg==", + "dev": true, + "license": "ISC" + }, + "node_modules/elliptic": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.0.tgz", + "integrity": "sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA==", + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "license": "MIT" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/enabled": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-ex/node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", + "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.6" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", "dev": true, "dependencies": { - "fill-range": "^7.1.1" + "ms": "2.1.2" }, "engines": { - "node": ">=8" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/brorand": { - "version": "1.1.0", - "license": "MIT" - }, - "node_modules/browser-headers": { - "version": "0.4.1", - "license": "Apache-2.0" + "node_modules/eslint/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, - "node_modules/bs58": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-6.0.0.tgz", - "integrity": "sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==", - "license": "MIT", + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, "dependencies": { - "base-x": "^5.0.0" + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/bs58check": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", - "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", - "dependencies": { - "bs58": "^4.0.0", - "create-hash": "^1.1.0", - "safe-buffer": "^5.1.2" + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" } }, - "node_modules/bs58check/node_modules/base-x": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", - "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, "dependencies": { - "safe-buffer": "^5.0.1" + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" } }, - "node_modules/bs58check/node_modules/bs58": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", - "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, "dependencies": { - "base-x": "^3.0.2" + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" } }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" } }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, "engines": { - "node": ">= 0.8" + "node": ">=0.10.0" } }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.6" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "engines": { - "node": ">=6" + "node": ">=0.8.x" } }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, - "node_modules/cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/color": { - "version": "3.2.1", - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/color-convert": { - "version": "1.9.3", - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/color-name": { - "version": "1.1.3", - "license": "MIT" - }, - "node_modules/color-string": { - "version": "1.9.1", - "license": "MIT", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "engines": { + "node": ">=6" } }, - "node_modules/colorspace": { - "version": "1.1.4", + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, "license": "MIT", "dependencies": { - "color": "^3.1.3", - "text-hex": "1.0.x" + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/combined-stream": { - "version": "1.0.8", + "node_modules/express": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "license": "MIT", "dependencies": { - "delayed-stream": "~1.0.0" + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.10", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.10.0" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", "dev": true }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, "dependencies": { - "safe-buffer": "5.2.1" + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" }, "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" + "node": ">=8.6.0" } }, - "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, "engines": { - "node": ">= 0.6" + "node": ">= 6" } }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true }, - "node_modules/cosmjs-types": { - "version": "0.9.0", - "license": "Apache-2.0" + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true }, - "node_modules/create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, "dependencies": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" + "reusify": "^1.0.4" } }, - "node_modules/create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" + "bser": "2.1.1" } }, - "node_modules/create-require": { - "version": "1.1.1", + "node_modules/fecha": { + "version": "4.2.3", "license": "MIT" }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "flat-cache": "^3.0.4" }, "engines": { - "node": ">= 8" + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "ms": "2.0.0" + "minimatch": "^5.0.1" } }, - "node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", "dependencies": { - "mimic-response": "^3.1.0" + "brace-expansion": "^2.0.1" }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, "engines": { - "node": ">=4.0.0" + "node": ">=8" } }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/define-data-property": { - "version": "1.1.4", + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", "license": "MIT", "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.8" } }, - "node_modules/define-properties": { - "version": "1.2.1", - "license": "MIT", + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "license": "MIT", + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, "engines": { - "node": ">=0.4.0" + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "node_modules/fn.name": { + "version": "1.1.0", + "license": "MIT" + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], "license": "MIT", "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } } }, - "node_modules/detect-libc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", - "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "node_modules/form-data": { + "version": "4.0.0", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, "engines": { - "node": ">=8" + "node": ">= 6" } }, - "node_modules/diff": { - "version": "4.0.2", - "license": "BSD-3-Clause", + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "engines": { - "node": ">=0.3.1" + "node": ">= 0.6" } }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 0.6" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=6.0.0" + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/elliptic": { - "version": "6.5.7", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.7.tgz", - "integrity": "sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==", + "node_modules/function-bind": { + "version": "1.1.2", "license": "MIT", - "dependencies": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/elliptic/node_modules/bn.js": { - "version": "4.12.0", - "license": "MIT" - }, - "node_modules/enabled": { - "version": "2.0.0", - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, "license": "MIT", "engines": { - "node": ">= 0.8" + "node": ">=6.9.0" } }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dependencies": { - "once": "^1.4.0" + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/es-define-property": { - "version": "1.0.0", + "node_modules/get-intrinsic": { + "version": "1.2.4", "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.4" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-errors": { - "version": "1.3.0", + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=8.0.0" } }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -1834,899 +4399,1203 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "*" }, "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-config-prettier": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", - "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", - "dev": true, - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=7.0.0" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/eslint-plugin-prettier": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", - "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "dependencies": { - "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.8.6" + "is-glob": "^4.0.3" }, "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint-plugin-prettier" - }, - "peerDependencies": { - "@types/eslint": ">=8.0.0", - "eslint": ">=8.0.0", - "eslint-config-prettier": "*", - "prettier": ">=3.0.0" - }, - "peerDependenciesMeta": { - "@types/eslint": { - "optional": true - }, - "eslint-config-prettier": { - "optional": true - } + "node": ">=10.13.0" } }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" + "type-fest": "^0.20.2" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=8" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, + "node_modules/globalthis": { + "version": "1.0.4", + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">= 0.4" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint/node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "dependencies": { - "ms": "2.1.2" + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" }, "engines": { - "node": ">=6.0" + "node": ">=10" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "node_modules/google-protobuf": { + "version": "3.21.4", + "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.4.tgz", + "integrity": "sha512-MnG7N936zcKTco4Jd2PX2U96Kf9PxygAPKBug+74LHzmHXmceN16MmRcdgZv+DGef/S9YvQAfRsNCn4cjf9yyQ==", + "license": "(BSD-3-Clause AND Apache-2.0)" }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, + "node_modules/gopd": { + "version": "1.0.1", + "license": "MIT", "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "get-intrinsic": "^1.1.3" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/graphql": { + "version": "16.9.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.9.0.tgz", + "integrity": "sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10" + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "license": "MIT", "dependencies": { - "estraverse": "^5.2.0" + "es-define-property": "^1.0.0" }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "license": "MIT", "engines": { - "node": ">=4.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, + "node_modules/has-symbols": { + "version": "1.0.3", + "license": "MIT", "engines": { - "node": ">=4.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "node_modules/hash.js": { + "version": "1.1.7", "license": "MIT", - "engines": { - "node": ">= 0.6" + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" } }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "node_modules/hasown": { + "version": "2.0.2", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, "engines": { - "node": ">=0.8.x" + "node": ">= 0.4" } }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "engines": { - "node": ">=6" + "node_modules/headers-polyfill": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.3.tgz", + "integrity": "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" } }, - "node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/http": { + "version": "0.0.1-security" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "license": "MIT", "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.6.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", + "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" + "toidentifier": "1.0.1" }, "engines": { - "node": ">= 0.10.0" + "node": ">= 0.8" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "node_modules/https": { + "version": "1.0.0", + "license": "ISC" }, - "node_modules/fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "dev": true + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "node_modules/husky": { + "version": "9.0.11", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.11.tgz", + "integrity": "sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==", "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "bin": { + "husky": "bin.mjs" }, "engines": { - "node": ">=8.6.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" } }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", "dependencies": { - "is-glob": "^4.0.1" + "safer-buffer": ">= 2.1.2 < 3" }, "engines": { - "node": ">= 6" + "node": ">=0.10.0" } }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true, - "dependencies": { - "reusify": "^1.0.4" + "engines": { + "node": ">= 4" } }, - "node_modules/fecha": { - "version": "4.2.3", - "license": "MIT" - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, "dependencies": { - "flat-cache": "^3.0.4" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", "dev": true, + "license": "MIT", "dependencies": { - "to-regex-range": "^5.0.1" + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "license": "MIT", + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "engines": { - "node": ">= 0.8" + "node": ">= 0.10" } }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/is-arrayish": { + "version": "0.3.2", + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", "dev": true, + "license": "MIT", "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "hasown": "^2.0.2" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=0.10.0" } }, - "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", - "dev": true - }, - "node_modules/fn.name": { - "version": "1.1.0", - "license": "MIT" - }, - "node_modules/follow-redirects": { - "version": "1.15.6", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, "license": "MIT", "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } + "node": ">=8" } }, - "node_modules/form-data": { - "version": "4.0.0", + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "is-extglob": "^2.1.1" }, "engines": { - "node": ">= 6" + "node": ">=0.10.0" } }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "node_modules/is-node-process": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", + "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, "engines": { - "node": ">= 0.6" + "node": ">=0.12.0" } }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "node_modules/function-bind": { - "version": "1.1.2", + "node_modules/isomorphic-ws": { + "version": "4.0.1", "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "ws": "*" } }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=10" } }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "is-glob": "^4.0.3" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=10.13.0" + "node": ">=10" } }, - "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "type-fest": "^0.20.2" + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=10" } }, - "node_modules/globalthis": { - "version": "1.0.4", + "node_modules/istanbul-lib-source-maps/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, "license": "MIT", "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" + "ms": "^2.1.3" }, "engines": { - "node": ">= 0.4" + "node": ">=6.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/google-protobuf": { - "version": "3.21.4", - "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.4.tgz", - "integrity": "sha512-MnG7N936zcKTco4Jd2PX2U96Kf9PxygAPKBug+74LHzmHXmceN16MmRcdgZv+DGef/S9YvQAfRsNCn4cjf9yyQ==", - "license": "(BSD-3-Clause AND Apache-2.0)" - }, - "node_modules/gopd": { - "version": "1.0.1", - "license": "MIT", + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "get-intrinsic": "^1.1.3" + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" } }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, "license": "MIT", "dependencies": { - "es-define-property": "^1.0.0" + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/has-proto": { - "version": "1.0.3", + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, "engines": { - "node": ">= 0.4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/has-symbols": { - "version": "1.0.3", + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, "engines": { - "node": ">= 0.4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } } }, - "node_modules/hash-base": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", - "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "license": "MIT", "dependencies": { - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { - "node": ">=4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/hash.js": { - "version": "1.1.7", + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, "license": "MIT", "dependencies": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/hasown": { - "version": "2.0.2", + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, "license": "MIT", "dependencies": { - "function-bind": "^1.1.2" + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" }, "engines": { - "node": ">= 0.4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/hmac-drbg": { - "version": "1.0.1", + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, "license": "MIT", "dependencies": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/http": { - "version": "0.0.1-security" + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, "license": "MIT", "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" }, "engines": { - "node": ">= 0.8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" } }, - "node_modules/https": { - "version": "1.0.0", - "license": "ISC" - }, - "node_modules/husky": { - "version": "9.0.11", - "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.11.tgz", - "integrity": "sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==", + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", "dev": true, - "bin": { - "husky": "bin.mjs" + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/typicode" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { - "node": ">=0.10.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, "engines": { - "node": ">= 4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", "dev": true, + "license": "MIT", "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", "engines": { "node": ">=6" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.8.19" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", "dev": true, + "license": "MIT", "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/inherits": { - "version": "2.0.4", - "license": "ISC" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, "engines": { - "node": ">= 0.10" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/is-arrayish": { - "version": "0.3.2", - "license": "MIT" + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "node_modules/jest-runtime/node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", "dev": true, + "license": "MIT", "dependencies": { - "is-extglob": "^2.1.1" + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" }, "engines": { - "node": ">=0.10.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, "engines": { - "node": ">=0.12.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/is-stream": { - "version": "2.0.1", + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } }, - "node_modules/isomorphic-ws": { - "version": "4.0.1", + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, "license": "MIT", - "peerDependencies": { - "ws": "*" + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -2747,12 +5616,32 @@ "jscrypto": "bin/cli.js" } }, + "node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -2808,10 +5697,30 @@ "json-buffer": "3.0.1" } }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/kuler": { "version": "2.0.0", "license": "MIT" }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -2836,6 +5745,13 @@ "libsodium-sumo": "^0.7.13" } }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -2851,6 +5767,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -2876,10 +5799,46 @@ "version": "5.2.3", "license": "Apache-2.0" }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/make-error": { "version": "1.3.6", "license": "ISC" }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, "node_modules/md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -2908,6 +5867,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -2968,6 +5934,16 @@ "node": ">= 0.6" } }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/mimic-response": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", @@ -3015,6 +5991,81 @@ "version": "2.1.3", "license": "MIT" }, + "node_modules/msw": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.6.2.tgz", + "integrity": "sha512-RdRgPvjfuzMIACkWv7VOVAeSRYMU3ofokLv1w0RsbFX960qnj/tFEyOFXY0G2GTUd9trA6rHuHciM/FKpBp6/A==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@bundled-es-modules/cookie": "^2.0.1", + "@bundled-es-modules/statuses": "^1.0.1", + "@bundled-es-modules/tough-cookie": "^0.1.6", + "@inquirer/confirm": "^5.0.0", + "@mswjs/interceptors": "^0.36.5", + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/until": "^2.1.0", + "@types/cookie": "^0.6.0", + "@types/statuses": "^2.0.4", + "chalk": "^4.1.2", + "graphql": "^16.8.1", + "headers-polyfill": "^4.0.2", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.3", + "path-to-regexp": "^6.3.0", + "strict-event-emitter": "^0.5.1", + "type-fest": "^4.26.1", + "yargs": "^17.7.2" + }, + "bin": { + "msw": "cli/index.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mswjs" + }, + "peerDependencies": { + "typescript": ">= 4.8.x" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/msw/node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/msw/node_modules/type-fest": { + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.1.tgz", + "integrity": "sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/nan": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz", @@ -3065,6 +6116,43 @@ "node-gyp-build-test": "build-test.js" } }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/object-inspect": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", @@ -3111,6 +6199,22 @@ "fn.name": "1.x.x" } }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -3128,6 +6232,13 @@ "node": ">= 0.8.0" } }, + "node_modules/outvariant": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz", + "integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==", + "dev": true, + "license": "MIT" + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -3158,6 +6269,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -3170,6 +6291,25 @@ "node": ">=6" } }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -3206,6 +6346,13 @@ "node": ">=8" } }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, "node_modules/path-to-regexp": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", @@ -3221,6 +6368,13 @@ "node": ">=8" } }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -3233,6 +6387,85 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/prebuild-install": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", @@ -3295,6 +6528,34 @@ "node": ">=6.0.0" } }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/prom-client": { "version": "15.1.2", "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-15.1.2.tgz", @@ -3307,6 +6568,20 @@ "node": "^16 || ^18 || >=20" } }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/protobufjs": { "version": "7.4.0", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", @@ -3347,6 +6622,16 @@ "version": "1.1.0", "license": "MIT" }, + "node_modules/psl": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.10.0.tgz", + "integrity": "sha512-KSKHEbjAnpUuAUserOq0FxGXCUrzC3WniuSJhvdbs102rL55266ZcHBqLWOsG30spQMlPdpy7icATiAQehg/iA==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + } + }, "node_modules/pump": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", @@ -3365,6 +6650,23 @@ "node": ">=6" } }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, "node_modules/qs": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", @@ -3380,6 +6682,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true, + "license": "MIT" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -3446,21 +6755,86 @@ "node": ">=0.10.0" } }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, "node_modules/readable-stream": { "version": "3.6.2", "license": "MIT", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readonly-date": { + "version": "1.0.0", + "license": "Apache-2.0" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" }, "engines": { - "node": ">= 6" + "node": ">=8" } }, - "node_modules/readonly-date": { - "version": "1.0.0", - "license": "Apache-2.0" + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, "node_modules/resolve-from": { "version": "4.0.0", @@ -3471,6 +6845,16 @@ "node": ">=4" } }, + "node_modules/resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -3569,17 +6953,18 @@ "license": "MIT" }, "node_modules/secp256k1": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-5.0.0.tgz", - "integrity": "sha512-TKWX8xvoGHrxVdqbYeZM9w+izTF4b9z3NhSaDkdn81btvuh+ivbIMGT/zQvDtTFWhRlThpoz6LEYTr7n8A5GcA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-5.0.1.tgz", + "integrity": "sha512-lDFs9AAIaWP9UCdtWrotXWWF9t8PWgQDcxqgAnpM9rMqxb3Oaq2J0thzPVSxBwdJgyQtkU/sYtFtbM1RSt/iYA==", "hasInstallScript": true, + "license": "MIT", "dependencies": { - "elliptic": "^6.5.4", + "elliptic": "^6.5.7", "node-addon-api": "^5.0.0", "node-gyp-build": "^4.2.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=18.0.0" } }, "node_modules/secp256k1/node_modules/node-addon-api": { @@ -3721,6 +7106,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, "node_modules/simple-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", @@ -3771,6 +7163,13 @@ "is-arrayish": "^0.3.1" } }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -3780,6 +7179,34 @@ "node": ">=8" } }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/stack-trace": { "version": "0.0.10", "license": "MIT", @@ -3787,6 +7214,29 @@ "node": "*" } }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -3796,6 +7246,13 @@ "node": ">= 0.8" } }, + "node_modules/strict-event-emitter": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", + "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", + "dev": true, + "license": "MIT" + }, "node_modules/string_decoder": { "version": "1.3.0", "license": "MIT", @@ -3803,6 +7260,35 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -3822,6 +7308,16 @@ "node": ">=4" } }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -3846,6 +7342,19 @@ "node": ">=8" } }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/symbol-observable": { "version": "2.0.3", "license": "MIT", @@ -3903,6 +7412,21 @@ "bintrees": "1.0.2" } }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/text-hex": { "version": "1.0.0", "license": "MIT" @@ -3934,6 +7458,13 @@ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -3955,6 +7486,22 @@ "node": ">=0.6" } }, + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/triple-beam": { "version": "1.4.1", "license": "MIT", @@ -3974,6 +7521,55 @@ "typescript": ">=4.2.0" } }, + "node_modules/ts-jest": { + "version": "29.2.5", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz", + "integrity": "sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bs-logger": "^0.2.6", + "ejs": "^3.1.10", + "fast-json-stable-stringify": "^2.1.0", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.6.3", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, "node_modules/ts-node": { "version": "10.9.2", "license": "MIT", @@ -4055,6 +7651,16 @@ "node": ">= 0.8.0" } }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -4086,7 +7692,9 @@ "integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==" }, "node_modules/typescript": { - "version": "5.4.5", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -4127,6 +7735,16 @@ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" }, + "node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -4136,6 +7754,37 @@ "node": ">= 0.8" } }, + "node_modules/update-browserslist-db": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -4145,6 +7794,17 @@ "punycode": "^2.1.0" } }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "license": "MIT" @@ -4161,6 +7821,32 @@ "version": "3.0.1", "license": "MIT" }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/v8-to-istanbul/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -4169,6 +7855,16 @@ "node": ">= 0.8" } }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -4233,11 +7929,43 @@ "node": ">=0.10.0" } }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/ws": { "version": "7.5.10", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", @@ -4266,6 +7994,52 @@ "symbol-observable": "^2.0.3" } }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/yn": { "version": "3.1.1", "license": "MIT", @@ -4284,6 +8058,19 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", + "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index b7935b7..af481ae 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "scripts": { "start": "node --stack_size=4096 --max-old-space-size=4096 -r ts-node/register/transpile-only -r tsconfig-paths/register src/index.ts", "lint": "npx eslint . --fix", - "prepare": "husky" + "prepare": "husky", + "test": "jest" }, "author": "", "license": "ISC", @@ -29,18 +30,22 @@ "prom-client": "^15.1.2", "ts-node": "^10.9.2", "tsconfig-paths": "^4.2.0", - "typescript": "^5.4.5", "winston": "^3.13.0" }, "devDependencies": { "@types/better-sqlite3": "^7.6.11", "@types/bluebird": "^3.5.42", "@types/express": "^4.17.21", + "@types/jest": "^29.5.14", "@types/node": "^22.5.4", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", "husky": "^9.0.11", + "jest": "^29.7.0", + "msw": "^2.6.2", + "ts-jest": "^29.2.5", + "typescript": "^5.6.3", "typescript-eslint": "^7.16.0" }, "lint-staged": { diff --git a/src/db/controller/channel.spec.ts b/src/db/controller/channel.spec.ts new file mode 100644 index 0000000..c826013 --- /dev/null +++ b/src/db/controller/channel.spec.ts @@ -0,0 +1,315 @@ +import { + Bool, + ChannelOpenCloseEvent, + ChannelOpenCloseTable, + ChannelState, +} from 'src/types' +import { ChannelController } from './channel' +import { DB } from '..' +import { insert, select } from '../utils' +import { mockServers } from 'src/test/testSetup' + +describe('channel controler', () => { + test('channel open e2e', async () => { + const [mockServer1, mockServer2] = mockServers + // test channel_open_init + { + // create mock events + const events: ChannelOpenCloseEvent[] = [ + { + type: 'channel_open_init', + channelOpenCloseInfo: { + height: 100, + srcConnectionId: 'connection-1', + srcPortId: 'transfer', + srcChannelId: 'channel-1', + dstConnectionId: '', + dstPortId: '', + dstChannelId: '', + }, + }, + ] + + // create feed functions + const fns = await ChannelController.feedEvents( + mockServer1.rest.client(), + mockServer1.rest.chainId, + events + ) + + // execute feed functions + fns() + + // check db insertion + const res = select(DB, ChannelController.tableName) + const expectVal: ChannelOpenCloseTable[] = [ + { + id: 1, + in_progress: Bool.FALSE, + height: 100, + state: ChannelState.INIT, + chain_id: mockServer2.rest.chainId, // counterparty's chain id + connection_id: 'connection-2', + port_id: '', + channel_id: '', + counterparty_chain_id: mockServer1.rest.chainId, + counterparty_connection_id: 'connection-1', + counterparty_port_id: 'transfer', + counterparty_channel_id: 'channel-1', + }, + ] + expect(res).toEqual(expectVal) + } + + // test channel_open_try + { + // create mock events + const events: ChannelOpenCloseEvent[] = [ + { + type: 'channel_open_try', + channelOpenCloseInfo: { + height: 100, + srcConnectionId: 'connection-2', + srcPortId: 'transfer', + srcChannelId: 'channel-1', + dstConnectionId: 'connection-2', + dstPortId: 'transfer', + dstChannelId: 'channel-2', + }, + }, + ] + + // create feed functions + const fns = await ChannelController.feedEvents( + mockServer2.rest.client(), + mockServer2.rest.chainId, + events + ) + + // execute feed functions + fns() + + // check deletion and insertion + const res = select(DB, ChannelController.tableName) + // id 1 must be removed + const expectVal: ChannelOpenCloseTable[] = [ + { + id: 2, + in_progress: Bool.FALSE, + height: 100, + state: ChannelState.TRYOPEN, + chain_id: mockServer1.rest.chainId, // counterparty's chain id + connection_id: 'connection-1', + port_id: 'transfer', + channel_id: 'channel-1', + counterparty_chain_id: mockServer2.rest.chainId, + counterparty_connection_id: 'connection-2', + counterparty_port_id: 'transfer', + counterparty_channel_id: 'channel-2', + }, + ] + expect(res).toEqual(expectVal) + } + + // test channel_open_ack + { + // create mock events + const events: ChannelOpenCloseEvent[] = [ + { + type: 'channel_open_ack', + channelOpenCloseInfo: { + height: 100, + srcConnectionId: 'connection-1', + srcPortId: 'transfer', + srcChannelId: 'channel-1', + dstConnectionId: 'connection-2', + dstPortId: 'transfer', + dstChannelId: 'channel-2', + }, + }, + ] + + // to check deletion of open init, insert + insert(DB, ChannelController.tableName, { + id: 1, + in_progress: Bool.FALSE, + height: 100, + state: ChannelState.INIT, + chain_id: mockServer2.rest.chainId, // counterparty's chain id + connection_id: 'connection-2', + port_id: '', + channel_id: '', + counterparty_chain_id: mockServer1.rest.chainId, + counterparty_connection_id: 'connection-1', + counterparty_port_id: 'transfer', + counterparty_channel_id: 'channel-1', + }) + + // create feed functions + const fns = await ChannelController.feedEvents( + mockServer1.rest.client(), + mockServer1.rest.chainId, + events + ) + + // execute feed functions + fns() + + // check deletion and insertion + const res = select(DB, ChannelController.tableName) + // id 1 and 2 must be removed + const expectVal: ChannelOpenCloseTable[] = [ + { + id: 3, + in_progress: Bool.FALSE, + height: 100, + state: ChannelState.ACK, + chain_id: mockServer2.rest.chainId, // counterparty's chain id + connection_id: 'connection-2', + port_id: 'transfer', + channel_id: 'channel-2', + counterparty_chain_id: mockServer1.rest.chainId, + counterparty_connection_id: 'connection-1', + counterparty_port_id: 'transfer', + counterparty_channel_id: 'channel-1', + }, + ] + expect(res).toEqual(expectVal) + } + + // test channel_open_confirm + { + // create mock events + const events: ChannelOpenCloseEvent[] = [ + { + type: 'channel_open_confirm', + channelOpenCloseInfo: { + height: 100, + srcConnectionId: 'connection-1', + srcPortId: 'transfer', + srcChannelId: 'channel-1', + dstConnectionId: 'connection-2', + dstPortId: 'transfer', + dstChannelId: 'channel-2', + }, + }, + ] + + // to check deletion of open try_open, insert + insert(DB, ChannelController.tableName, { + id: 2, + in_progress: Bool.FALSE, + height: 100, + state: ChannelState.TRYOPEN, + chain_id: mockServer1.rest.chainId, // counterparty's chain id + connection_id: 'connection-1', + port_id: 'transfer', + channel_id: 'channel-1', + counterparty_chain_id: mockServer2.rest.chainId, + counterparty_connection_id: 'connection-2', + counterparty_port_id: 'transfer', + counterparty_channel_id: 'channel-2', + }) + + // create feed functions + const fns = await ChannelController.feedEvents( + mockServer2.rest.client(), + mockServer2.rest.chainId, + events + ) + + // execute feed functions + fns() + + // check deletions + const res = select(DB, ChannelController.tableName) + // id 2 and 3 must be removed and noting remain + const expectVal: ChannelOpenCloseTable[] = [] + expect(res).toEqual(expectVal) + } + }) + + test('channel close e2e', async () => { + const [mockServer1, mockServer2] = mockServers + { + // create mock events + const events: ChannelOpenCloseEvent[] = [ + { + type: 'channel_close', + channelOpenCloseInfo: { + height: 100, + srcConnectionId: 'connection-1', + srcPortId: 'transfer', + srcChannelId: 'channel-1', + dstConnectionId: 'connection-2', + dstPortId: 'transfer', + dstChannelId: 'channel-2', + }, + }, + ] + + // create feed functions + const fns = await ChannelController.feedEvents( + mockServer1.rest.client(), + mockServer1.rest.chainId, + events + ) + + // execute feed functions + fns() + + // check db insertion + const res = select(DB, ChannelController.tableName) + const expectVal: ChannelOpenCloseTable[] = [ + { + id: 4, + in_progress: Bool.FALSE, + height: 100, + state: ChannelState.CLOSE, + chain_id: mockServer2.rest.chainId, // counterparty's chain id + connection_id: 'connection-2', + port_id: 'transfer', + channel_id: 'channel-2', + counterparty_chain_id: mockServer1.rest.chainId, + counterparty_connection_id: 'connection-1', + counterparty_port_id: 'transfer', + counterparty_channel_id: 'channel-1', + }, + ] + expect(res).toEqual(expectVal) + } + + { + // create mock events + const events: ChannelOpenCloseEvent[] = [ + { + type: 'channel_close_confirm', + channelOpenCloseInfo: { + height: 100, + srcConnectionId: 'connection-1', + srcPortId: 'transfer', + srcChannelId: 'channel-1', + dstConnectionId: 'connection-2', + dstPortId: 'transfer', + dstChannelId: 'channel-2', + }, + }, + ] + + // create feed functions + const fns = await ChannelController.feedEvents( + mockServer2.rest.client(), + mockServer2.rest.chainId, + events + ) + + // execute feed functions + fns() + + // check db deletion + const res = select(DB, ChannelController.tableName) + const expectVal: ChannelOpenCloseTable[] = [] + expect(res).toEqual(expectVal) + } + }) +}) diff --git a/src/db/controller/channel.ts b/src/db/controller/channel.ts index 56eede3..36751b7 100644 --- a/src/db/controller/channel.ts +++ b/src/db/controller/channel.ts @@ -304,12 +304,12 @@ export class ChannelController { } } - private static feedChannelCloseConfirmEvent( + private static async feedChannelCloseConfirmEvent( _rest: RESTClient, chainId: string, event: ChannelOpenCloseEvent ): Promise<() => void> { - return new Promise(() => { + return () => { del(DB, this.tableName, [ { state: ChannelState.CLOSE, @@ -323,6 +323,6 @@ export class ChannelController { chainId, event.channelOpenCloseInfo.srcChannelId ) - }) + } } } diff --git a/src/db/controller/connection.ts b/src/db/controller/connection.ts index c222abb..9eb9c36 100644 --- a/src/db/controller/connection.ts +++ b/src/db/controller/connection.ts @@ -12,7 +12,6 @@ export class ConnectionController { connectionId: string ): Promise { const connectionInfo = await rest.ibc.getConnection(connectionId) - const clientId = connectionInfo.connection.client_id const client = await ClientController.getClient(rest, chainId, clientId) diff --git a/src/db/controller/packet.spec.ts b/src/db/controller/packet.spec.ts new file mode 100644 index 0000000..bbf158c --- /dev/null +++ b/src/db/controller/packet.spec.ts @@ -0,0 +1,108 @@ +import { FeeType, PacketFeeTable } from 'src/types' +import { DB } from '..' +import { select } from '../utils' +import { mockServers } from 'src/test/testSetup' +import { parsePacketFeeEvent } from 'src/lib/eventParser' +import { PacketFeeController } from './packetFee' + +describe('packet controler', () => { + test('packet send e2e', async () => { + const [mockServer1, _] = mockServers + + const event = parsePacketFeeEvent({ + type: 'incentivized_ibc_packet', + attributes: [ + { + key: 'port_id', + value: 'transfer', + }, + { + key: 'channel_id', + value: 'channel-1', + }, + { + key: 'packet_sequence', + value: '1', + }, + { + key: 'recv_fee', + value: '100tokena,200tokenb', + }, + { + key: 'ack_fee', + value: '200tokena,100tokenb', + }, + { + key: 'timeout_fee', + value: '300tokena,300tokenb', + }, + { + key: 'msg_index', + value: '0', + }, + ], + }) + + // create feed functions + const fns = PacketFeeController.feedEvents(mockServer1.rest.chainId, [ + event, + ]) + + // execute feed functions + fns() + + // check db insertion + const res = select(DB, PacketFeeController.tableName) + const expectVal: PacketFeeTable[] = [ + { + chain_id: mockServer1.rest.chainId, + channel_id: 'channel-1', + sequence: event.sequence, + fee_type: FeeType.RECV, + denom: 'tokena', + amount: 100, + }, + { + chain_id: mockServer1.rest.chainId, + channel_id: 'channel-1', + sequence: event.sequence, + fee_type: FeeType.RECV, + denom: 'tokenb', + amount: 200, + }, + { + chain_id: mockServer1.rest.chainId, + channel_id: 'channel-1', + sequence: event.sequence, + fee_type: FeeType.ACK, + denom: 'tokena', + amount: 200, + }, + { + chain_id: mockServer1.rest.chainId, + channel_id: 'channel-1', + sequence: event.sequence, + fee_type: FeeType.ACK, + denom: 'tokenb', + amount: 100, + }, + { + chain_id: mockServer1.rest.chainId, + channel_id: 'channel-1', + sequence: event.sequence, + fee_type: FeeType.TIMEOUT, + denom: 'tokena', + amount: 300, + }, + { + chain_id: mockServer1.rest.chainId, + channel_id: 'channel-1', + sequence: event.sequence, + fee_type: FeeType.TIMEOUT, + denom: 'tokenb', + amount: 300, + }, + ] + expect(res).toEqual(expectVal) + }) +}) diff --git a/src/db/controller/packetFee.spec.ts b/src/db/controller/packetFee.spec.ts new file mode 100644 index 0000000..18b57bd --- /dev/null +++ b/src/db/controller/packetFee.spec.ts @@ -0,0 +1,69 @@ +import { Bool, PacketEvent, PacketSendTable } from 'src/types' +import { DB } from '..' +import { select } from '../utils' +import { PacketController } from './packet' +import { mockServers } from 'src/test/testSetup' + +describe('channel controler', () => { + test('packet send e2e', async () => { + const [mockServer1, _] = mockServers + // test send_packet + { + // create mock events + const events: PacketEvent[] = [ + { + type: 'send_packet', + packetInfo: { + height: 100, + sequence: 1, + connectionId: 'connection-1', + srcPort: 'transfer', + srcChannel: 'channel-1', + dstPort: 'transfer', + data: 'e2RhdGE6IG51bGx9', + dstChannel: 'channel-2', + timeoutHeight: 0, + timeoutTimestamp: 1731051545, + timeoutHeightRaw: '0-0', + timeoutTimestampRaw: '1731051545000000000', + }, + }, + ] + + // create feed functions + const fns = await PacketController.feedEvents( + mockServer1.rest.client(), + mockServer1.rest.chainId, + events + ) + + // execute feed functions + fns() + + // check db insertion + const res = select(DB, PacketController.tableNamePacketSend) + const expectVal: PacketSendTable[] = [ + { + dst_chain_id: 'chain-2', + dst_connection_id: 'connection-2', + dst_channel_id: 'channel-2', + sequence: 1, + in_progress: Bool.FALSE, + is_ordered: Bool.FALSE, + height: 100, + dst_port: 'transfer', + src_chain_id: 'chain-1', + src_connection_id: 'connection-1', + src_port: 'transfer', + src_channel_id: 'channel-1', + packet_data: 'e2RhdGE6IG51bGx9', + timeout_height: 0, + timeout_timestamp: 1731051545, + timeout_height_raw: '0-0', + timeout_timestamp_raw: '1731051545000000000', + }, + ] + expect(res).toEqual(expectVal) + } + }) +}) diff --git a/src/db/controller/packetFee.ts b/src/db/controller/packetFee.ts index 06b3783..022a4c1 100644 --- a/src/db/controller/packetFee.ts +++ b/src/db/controller/packetFee.ts @@ -26,7 +26,6 @@ export class PacketFeeController { denom: coin.denom, amount: Number(coin.amount), } - insert(DB, this.tableName, packetFee) } } diff --git a/src/db/index.ts b/src/db/index.ts index 43c3188..68e565c 100644 --- a/src/db/index.ts +++ b/src/db/index.ts @@ -1,15 +1,23 @@ import DatabaseConstructor, { Database } from 'better-sqlite3' -import { DB_PATH } from './constants' import { migrate } from './migration' import * as fs from 'fs' +import * as path from 'path' +import { config } from 'src/lib/config' -if (!fs.existsSync('./.db')) { - fs.mkdirSync('./.db') -} -const db = new DatabaseConstructor(DB_PATH) -export const DB: Database = migrate(db) +export let DB: Database + +export function initDBConnection() { + const dbPath = config.dbPath ?? './' + + if (!fs.existsSync(path.join(dbPath, '.db'))) { + fs.mkdirSync(path.join(dbPath, '.db')) + } + const db = new DatabaseConstructor(path.join(dbPath, '.db/main.db')) -process.on('SIGINT', () => { - DB.close() - process.exit(0) -}) + DB = migrate(db) + + process.on('SIGINT', () => { + DB.close() + process.exit(0) + }) +} diff --git a/src/index.ts b/src/index.ts index 3183d60..04477ff 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,8 +4,10 @@ import { info } from './lib/logger' import { config } from './lib/config' import { WorkerController } from './workers' +import { initDBConnection } from './db' async function main() { + initDBConnection() const workerController = new WorkerController() await workerController.init(config) const app = express() diff --git a/src/lib/config.ts b/src/lib/config.ts index d46ddd5..9900fcb 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -10,6 +10,7 @@ export interface Config { port: number metricPort: number logLevel: string + dbPath?: string chains: ChainConfig[] } diff --git a/src/lib/eventParser.spec.ts b/src/lib/eventParser.spec.ts new file mode 100644 index 0000000..d84bf89 --- /dev/null +++ b/src/lib/eventParser.spec.ts @@ -0,0 +1,224 @@ +import { parsePacketEvent } from './eventParser' + +describe('event parser', () => { + test('packet parser', () => { + // test send packet + { + const packetInfo = parsePacketEvent(sendPacket, 123) + const expectValue = { + height: 123, + connectionId: 'connection-1559', + sequence: 293662, + srcPort: 'transfer', + srcChannel: 'channel-259', + dstPort: 'transfer', + dstChannel: 'channel-3', + data: 'eyJkZW5vbSI6InRyYW5zZmVyL2NoYW5uZWwtMjU5L3VrdWppIiwiYW1vdW50IjoiMTQ5NjY5MDEiLCJzZW5kZXIiOiJvc21vMTd5cDN5dmE3ZXhnMHJ1MnlsMndhZXdjZ2RzdzlhYzNmMm00M3VrZ2Q3ZDM5ZTA0NHlyNnF6d2t5ZDMiLCJyZWNlaXZlciI6Imt1amlyYTE1M3o5YTkwZjA5NzdwMnFkZTI4bTBjc3h1YXZxdXlhdGVmOXh0cCJ9', + timeoutHeight: 0, + timeoutTimestamp: 1730462659, + timeoutHeightRaw: '0-0', + timeoutTimestampRaw: '1730462659929766700', + ordering: 'ORDER_UNORDERED', + ack: undefined, + } + + expect(expectValue).toEqual(packetInfo) + } + + // test wirte ack + { + const packetInfo = parsePacketEvent(writeAck, 123) + const expectValue = { + height: 123, + connectionId: 'connection-2566', + sequence: 399171, + srcPort: 'transfer', + srcChannel: 'channel-2', + dstPort: 'transfer', + dstChannel: 'channel-19774', + data: 'eyJhbW91bnQiOiIzNzAwMDAwMDAwMDAwMDAwMDAwMCIsImRlbm9tIjoiYWR5bSIsIm1lbW8iOiJ7XCJ3YXNtXCI6e1wiY29udHJhY3RcIjpcIm9zbW8xZThwbGQ4ZnQ1cmMzcW03OGhjbGR5MnAwNXJqcmZ6YWVlYTUwbXAwenY1YzI5NHBsMnB5cWs1anJuZFwiLFwibXNnXCI6e1wic3dhcF9hbmRfYWN0aW9uXCI6e1widXNlcl9zd2FwXCI6e1wic3dhcF9leGFjdF9hc3NldF9pblwiOntcInN3YXBfdmVudWVfbmFtZVwiOlwib3Ntb3Npcy1wb29sbWFuYWdlclwiLFwib3BlcmF0aW9uc1wiOlt7XCJwb29sXCI6XCIxNDQ5XCIsXCJkZW5vbV9pblwiOlwiaWJjLzlBNzZDREYwQ0JDRUYzNzkyM0YzMjUxOEZBMTVFNURDOTJCOUY1NjEyODI5MkJDNEQ2M0M0QUVBNzZDQkIxMTBcIixcImRlbm9tX291dFwiOlwidW9zbW9cIn1dfX0sXCJtaW5fYXNzZXRcIjp7XCJuYXRpdmVcIjp7XCJkZW5vbVwiOlwidW9zbW9cIixcImFtb3VudFwiOlwiMTI5Mjc5MjY0XCJ9fSxcInRpbWVvdXRfdGltZXN0YW1wXCI6MTczMDQ2MjY1NDg5NzQxNjAyMCxcInBvc3Rfc3dhcF9hY3Rpb25cIjp7XCJ0cmFuc2ZlclwiOntcInRvX2FkZHJlc3NcIjpcIm9zbW8xdXl4ZHJxeWt0cWZ6Z3o0bWpyajg4ZWV3ZzUyOTh1NTQwZjAzN3hcIn19LFwiYWZmaWxpYXRlc1wiOlt7XCJiYXNpc19wb2ludHNfZmVlXCI6XCI2MFwiLFwiYWRkcmVzc1wiOlwib3NtbzFteTR0azQyMGdqbWhnZ3F3dnZoYTZleTkzOTBncXdmcmVlMnA0dVwifSx7XCJiYXNpc19wb2ludHNfZmVlXCI6XCIxNVwiLFwiYWRkcmVzc1wiOlwib3NtbzE0Z2Y2eHNsd2U5cGhuMno5NjV0NGRjdTd2Y2hodGhmZ3YybGh5eVwifV19fX19IiwicmVjZWl2ZXIiOiJvc21vMWU4cGxkOGZ0NXJjM3FtNzhoY2xkeTJwMDVyanJmemFlZWE1MG1wMHp2NWMyOTRwbDJweXFrNWpybmQiLCJzZW5kZXIiOiJkeW0xYXkyY2t4NHM4dm5oY3l2MzZoaDVxNnNxNDR2MDJ2aGNyYXdyYXYifQ==', + timeoutHeight: 23278545, + timeoutTimestamp: 0, + timeoutHeightRaw: '1-23278545', + timeoutTimestampRaw: '0', + ordering: 'ORDER_UNORDERED', + ack: 'eyJyZXN1bHQiOiJleUpqYjI1MGNtRmpkRjl5WlhOMWJIUWlPbTUxYkd3c0ltbGlZMTloWTJzaU9pSmxlVXA1V2xoT01XSklVV2xQYVVwQ1ZWUXdPVWx1TUQwaWZRPT0ifQ==', + } + + expect(expectValue).toEqual(packetInfo) + } + + // test send packet (base64 encoded events) + { + const packetInfo = parsePacketEvent(sendPacketBase64, 123) + const expectValue = { + height: 123, + connectionId: 'connection-61', + sequence: 15598, + srcPort: 'transfer', + srcChannel: 'channel-39', + dstPort: 'transfer', + dstChannel: 'channel-45', + data: 'eyJhbW91bnQiOiIzNDczMDAwMDAwIiwiZGVub20iOiJ1dXNkYyIsInJlY2VpdmVyIjoic2VpMWt3Z25lY3lzcTVxeGhnMmg4NmNwN2cwNWU2enN6dHJjZzAzOWg2Iiwic2VuZGVyIjoibm9ibGUxcGswYzB4MGxtdjVtbTgweXJtcmNtMno1eTgzZjN2bXV2dmNqNHgifQ==', + timeoutHeight: 14691118, + timeoutTimestamp: 0, + timeoutHeightRaw: '14691018-14691118', + timeoutTimestampRaw: '0', + ordering: 'ORDER_UNORDERED', + ack: undefined, + } + + expect(expectValue).toEqual(packetInfo) + } + + // test wirte ack (base64 encoded events) + { + const packetInfo = parsePacketEvent(writeAckBase64, 123) + const expectValue = { + height: 123, + connectionId: 'connection-34', + sequence: 30288, + srcPort: 'transfer', + srcChannel: 'channel-30', + dstPort: 'transfer', + dstChannel: 'channel-18', + data: 'eyJhbW91bnQiOiIyMDAzNDU4NTciLCJkZW5vbSI6InRyYW5zZmVyL2NoYW5uZWwtMzAvdXVzZGMiLCJyZWNlaXZlciI6Im5vYmxlMTZlbjlhdGVxNTU2NWM0bjI2ZWQwd3lrbW5xaGQwdGhwYzU3dG1oIiwic2VuZGVyIjoibmV1dHJvbjE2ZW45YXRlcTU1NjVjNG4yNmVkMHd5a21ucWhkMHRocDVnenBlNyJ9', + timeoutHeight: 0, + timeoutTimestamp: 1730467768, + timeoutHeightRaw: '0-0', + timeoutTimestampRaw: '1730467768229000000', + ordering: undefined, + ack: 'eyJyZXN1bHQiOiJBUT09In0=', + } + + expect(expectValue).toEqual(packetInfo) + } + }) +}) + +// Real data from chains for test +const sendPacketBase64 = { + type: 'send_packet', + attributes: [ + { + key: 'cGFja2V0X2RhdGE=', // packet data + value: + 'eyJhbW91bnQiOiIzNDczMDAwMDAwIiwiZGVub20iOiJ1dXNkYyIsInJlY2VpdmVyIjoic2VpMWt3Z25lY3lzcTVxeGhnMmg4NmNwN2cwNWU2enN6dHJjZzAzOWg2Iiwic2VuZGVyIjoibm9ibGUxcGswYzB4MGxtdjVtbTgweXJtcmNtMno1eTgzZjN2bXV2dmNqNHgifQ==', + }, // '{"amount":"3473000000","denom":"uusdc","receiver":"sei1kwgnecysq5qxhg2h86cp7g05e6zsztrcg039h6","sender":"noble1pk0c0x0lmv5mm80yrmrcm2z5y83f3vmuvvcj4x"}' + { + key: 'cGFja2V0X2RhdGFfaGV4', // packet data hex + value: + 'N2IyMjYxNmQ2Zjc1NmU3NDIyM2EyMjMzMzQzNzMzMzAzMDMwMzAzMDMwMjIyYzIyNjQ2NTZlNmY2ZDIyM2EyMjc1NzU3MzY0NjMyMjJjMjI3MjY1NjM2NTY5NzY2NTcyMjIzYTIyNzM2NTY5MzE2Yjc3Njc2ZTY1NjM3OTczNzEzNTcxNzg2ODY3MzI2ODM4MzY2MzcwMzc2NzMwMzU2NTM2N2E3MzdhNzQ3MjYzNjczMDMzMzk2ODM2MjIyYzIyNzM2NTZlNjQ2NTcyMjIzYTIyNmU2ZjYyNmM2NTMxNzA2YjMwNjMzMDc4MzA2YzZkNzYzNTZkNmQzODMwNzk3MjZkNzI2MzZkMzI3YTM1NzkzODMzNjYzMzc2NmQ3NTc2NzY2MzZhMzQ3ODIyN2Q=', + }, // '7b22616d6f756e74223a2233343733303030303030222c2264656e6f6d223a227575736463222c227265636569766572223a22736569316b77676e656379737135717868673268383663703767303565367a737a747263673033396836222c2273656e646572223a226e6f626c6531706b30633078306c6d76356d6d383079726d72636d327a357938336633766d757676636a3478227d + { + key: 'cGFja2V0X3RpbWVvdXRfaGVpZ2h0', // packet timeout height + value: 'MTQ2OTEwMTgtMTQ2OTExMTg=', // 14691018-14691118 ? + }, + { key: 'cGFja2V0X3RpbWVvdXRfdGltZXN0YW1w', value: 'MA==' }, // packet timeout, 0 + { key: 'cGFja2V0X3NlcXVlbmNl', value: 'MTU1OTg=' }, // packet sequence, 15598 + { key: 'cGFja2V0X3NyY19wb3J0', value: 'dHJhbnNmZXI=' }, // packet src port, transfer + { key: 'cGFja2V0X3NyY19jaGFubmVs', value: 'Y2hhbm5lbC0zOQ==' }, // packet src channel, channel-39 + { key: 'cGFja2V0X2RzdF9wb3J0', value: 'dHJhbnNmZXI=' }, // packet dst port, transfer + { key: 'cGFja2V0X2RzdF9jaGFubmVs', value: 'Y2hhbm5lbC00NQ==' }, // packet dst channel, channel-45 + { + key: 'cGFja2V0X2NoYW5uZWxfb3JkZXJpbmc=', // packet channel ordering + value: 'T1JERVJfVU5PUkRFUkVE', // , ORDER_UNORDERED + }, + { key: 'cGFja2V0X2Nvbm5lY3Rpb24=', value: 'Y29ubmVjdGlvbi02MQ==' }, // packet connection, connection-61 + ], +} +const writeAckBase64 = { + type: 'write_acknowledgement', + attributes: [ + { + key: 'cGFja2V0X2RhdGE=', // packet data + value: + 'eyJhbW91bnQiOiIyMDAzNDU4NTciLCJkZW5vbSI6InRyYW5zZmVyL2NoYW5uZWwtMzAvdXVzZGMiLCJyZWNlaXZlciI6Im5vYmxlMTZlbjlhdGVxNTU2NWM0bjI2ZWQwd3lrbW5xaGQwdGhwYzU3dG1oIiwic2VuZGVyIjoibmV1dHJvbjE2ZW45YXRlcTU1NjVjNG4yNmVkMHd5a21ucWhkMHRocDVnenBlNyJ9', + }, // {"amount":"200345857","denom":"transfer/channel-30/uusdc","receiver":"noble16en9ateq5565c4n26ed0wykmnqhd0thpc57tmh","sender":"neutron16en9ateq5565c4n26ed0wykmnqhd0thp5gzpe7"} + { + key: 'cGFja2V0X2RhdGFfaGV4', // packet data hex + value: + 'N2IyMjYxNmQ2Zjc1NmU3NDIyM2EyMjMyMzAzMDMzMzQzNTM4MzUzNzIyMmMyMjY0NjU2ZTZmNmQyMjNhMjI3NDcyNjE2ZTczNjY2NTcyMmY2MzY4NjE2ZTZlNjU2YzJkMzMzMDJmNzU3NTczNjQ2MzIyMmMyMjcyNjU2MzY1Njk3NjY1NzIyMjNhMjI2ZTZmNjI2YzY1MzEzNjY1NmUzOTYxNzQ2NTcxMzUzNTM2MzU2MzM0NmUzMjM2NjU2NDMwNzc3OTZiNmQ2ZTcxNjg2NDMwNzQ2ODcwNjMzNTM3NzQ2ZDY4MjIyYzIyNzM2NTZlNjQ2NTcyMjIzYTIyNmU2NTc1NzQ3MjZmNmUzMTM2NjU2ZTM5NjE3NDY1NzEzNTM1MzYzNTYzMzQ2ZTMyMzY2NTY0MzA3Nzc5NmI2ZDZlNzE2ODY0MzA3NDY4NzAzNTY3N2E3MDY1MzcyMjdk', + }, // 7b22616d6f756e74223a22323030333435383537222c2264656e6f6d223a227472616e736665722f6368616e6e656c2d33302f7575736463222c227265636569766572223a226e6f626c653136656e39617465713535363563346e323665643077796b6d6e71686430746870633537746d68222c2273656e646572223a226e657574726f6e3136656e39617465713535363563346e323665643077796b6d6e7168643074687035677a706537227d + { key: 'cGFja2V0X3RpbWVvdXRfaGVpZ2h0', value: 'MC0w' }, // packet timeout height, 0-0 + { + key: 'cGFja2V0X3RpbWVvdXRfdGltZXN0YW1w', // packet timeout timestamp + value: 'MTczMDQ2Nzc2ODIyOTAwMDAwMA==', // 1730467768229000000 + }, + { key: 'cGFja2V0X3NlcXVlbmNl', value: 'MzAyODg=' }, // packet sequence 30288 + { key: 'cGFja2V0X3NyY19wb3J0', value: 'dHJhbnNmZXI=' }, // packet src port, transfer + { key: 'cGFja2V0X3NyY19jaGFubmVs', value: 'Y2hhbm5lbC0zMA==' }, // packet src channel, channel-30 + { key: 'cGFja2V0X2RzdF9wb3J0', value: 'dHJhbnNmZXI=' }, // packet dst port, transfer + { key: 'cGFja2V0X2RzdF9jaGFubmVs', value: 'Y2hhbm5lbC0xOA==' }, // packet dst channel, channel-18 + { key: 'cGFja2V0X2Fjaw==', value: 'eyJyZXN1bHQiOiJBUT09In0=' }, // packet ack, {"result":"AQ=="} + { + key: 'cGFja2V0X2Fja19oZXg=', // packet ack hex + value: 'N2IyMjcyNjU3Mzc1NmM3NDIyM2EyMjQxNTEzZDNkMjI3ZA==', // 7b22726573756c74223a2241513d3d227d + }, + { key: 'cGFja2V0X2Nvbm5lY3Rpb24=', value: 'Y29ubmVjdGlvbi0zNA==' }, // packet connection, connection-34 + ], +} + +const sendPacket = { + type: 'send_packet', + attributes: [ + { key: 'connection_id', value: 'connection-1559' }, + { key: 'packet_channel_ordering', value: 'ORDER_UNORDERED' }, + { key: 'packet_connection', value: 'connection-1559' }, + { + key: 'packet_data', + value: + '{"denom":"transfer/channel-259/ukuji","amount":"14966901","sender":"osmo17yp3yva7exg0ru2yl2waewcgdsw9ac3f2m43ukgd7d39e044yr6qzwkyd3","receiver":"kujira153z9a90f0977p2qde28m0csxuavquyatef9xtp"}', + }, + { + key: 'packet_data_hex', + value: + '7b2264656e6f6d223a227472616e736665722f6368616e6e656c2d3235392f756b756a69222c22616d6f756e74223a223134393636393031222c2273656e646572223a226f736d6f31377970337976613765786730727532796c327761657763676473773961633366326d3433756b67643764333965303434797236717a776b796433222c227265636569766572223a226b756a6972613135337a396139306630393737703271646532386d306373787561767175796174656639787470227d', + }, + { key: 'packet_dst_channel', value: 'channel-3' }, + { key: 'packet_dst_port', value: 'transfer' }, + { key: 'packet_sequence', value: '293662' }, + { key: 'packet_src_channel', value: 'channel-259' }, + { key: 'packet_src_port', value: 'transfer' }, + { key: 'packet_timeout_height', value: '0-0' }, + { key: 'packet_timeout_timestamp', value: '1730462659929766700' }, + { key: 'msg_index', value: '1' }, + ], +} + +const writeAck = { + type: 'write_acknowledgement', + attributes: [ + { + key: 'packet_data', + value: + '{"amount":"37000000000000000000","denom":"adym","memo":"{\\"wasm\\":{\\"contract\\":\\"osmo1e8pld8ft5rc3qm78hcldy2p05rjrfzaeea50mp0zv5c294pl2pyqk5jrnd\\",\\"msg\\":{\\"swap_and_action\\":{\\"user_swap\\":{\\"swap_exact_asset_in\\":{\\"swap_venue_name\\":\\"osmosis-poolmanager\\",\\"operations\\":[{\\"pool\\":\\"1449\\",\\"denom_in\\":\\"ibc/9A76CDF0CBCEF37923F32518FA15E5DC92B9F56128292BC4D63C4AEA76CBB110\\",\\"denom_out\\":\\"uosmo\\"}]}},\\"min_asset\\":{\\"native\\":{\\"denom\\":\\"uosmo\\",\\"amount\\":\\"129279264\\"}},\\"timeout_timestamp\\":1730462654897416020,\\"post_swap_action\\":{\\"transfer\\":{\\"to_address\\":\\"osmo1uyxdrqyktqfzgz4mjrj88eewg5298u540f037x\\"}},\\"affiliates\\":[{\\"basis_points_fee\\":\\"60\\",\\"address\\":\\"osmo1my4tk420gjmhggqwvvha6ey9390gqwfree2p4u\\"},{\\"basis_points_fee\\":\\"15\\",\\"address\\":\\"osmo14gf6xslwe9phn2z965t4dcu7vchhthfgv2lhyy\\"}]}}}}","receiver":"osmo1e8pld8ft5rc3qm78hcldy2p05rjrfzaeea50mp0zv5c294pl2pyqk5jrnd","sender":"dym1ay2ckx4s8vnhcyv36hh5q6sq44v02vhcrawrav"}', + }, + { + key: 'packet_data_hex', + value: + '7b22616d6f756e74223a223337303030303030303030303030303030303030222c2264656e6f6d223a226164796d222c226d656d6f223a227b5c227761736d5c223a7b5c22636f6e74726163745c223a5c226f736d6f316538706c6438667435726333716d373868636c647932703035726a72667a6165656135306d70307a763563323934706c327079716b356a726e645c222c5c226d73675c223a7b5c22737761705f616e645f616374696f6e5c223a7b5c22757365725f737761705c223a7b5c22737761705f65786163745f61737365745f696e5c223a7b5c22737761705f76656e75655f6e616d655c223a5c226f736d6f7369732d706f6f6c6d616e616765725c222c5c226f7065726174696f6e735c223a5b7b5c22706f6f6c5c223a5c22313434395c222c5c2264656e6f6d5f696e5c223a5c226962632f394137364344463043424345463337393233463332353138464131354535444339324239463536313238323932424334443633433441454137364342423131305c222c5c2264656e6f6d5f6f75745c223a5c22756f736d6f5c227d5d7d7d2c5c226d696e5f61737365745c223a7b5c226e61746976655c223a7b5c2264656e6f6d5c223a5c22756f736d6f5c222c5c22616d6f756e745c223a5c223132393237393236345c227d7d2c5c2274696d656f75745f74696d657374616d705c223a313733303436323635343839373431363032302c5c22706f73745f737761705f616374696f6e5c223a7b5c227472616e736665725c223a7b5c22746f5f616464726573735c223a5c226f736d6f31757978647271796b7471667a677a346d6a726a383865657767353239387535343066303337785c227d7d2c5c22616666696c69617465735c223a5b7b5c2262617369735f706f696e74735f6665655c223a5c2236305c222c5c22616464726573735c223a5c226f736d6f316d7934746b343230676a6d6867677177767668613665793933393067717766726565327034755c227d2c7b5c2262617369735f706f696e74735f6665655c223a5c2231355c222c5c22616464726573735c223a5c226f736d6f313467663678736c77653970686e327a393635743464637537766368687468666776326c6879795c227d5d7d7d7d7d222c227265636569766572223a226f736d6f316538706c6438667435726333716d373868636c647932703035726a72667a6165656135306d70307a763563323934706c327079716b356a726e64222c2273656e646572223a2264796d31617932636b78347338766e686379763336686835713673713434763032766863726177726176227d', + }, + { key: 'packet_timeout_height', value: '1-23278545' }, + { key: 'packet_timeout_timestamp', value: '0' }, + { key: 'packet_sequence', value: '399171' }, + { key: 'packet_src_port', value: 'transfer' }, + { key: 'packet_src_channel', value: 'channel-2' }, + { key: 'packet_dst_port', value: 'transfer' }, + { key: 'packet_dst_channel', value: 'channel-19774' }, + { + key: 'packet_ack', + value: + '{"result":"eyJjb250cmFjdF9yZXN1bHQiOm51bGwsImliY19hY2siOiJleUp5WlhOMWJIUWlPaUpCVVQwOUluMD0ifQ=="}', + }, + { + key: 'packet_ack_hex', + value: + '7b22726573756c74223a2265794a6a62323530636d466a644639795a584e31624851694f6d353162477773496d6c6959313968593273694f694a6c65557035576c684f4d574a4955576c5061557043565651774f556c754d44306966513d3d227d', + }, + { key: 'packet_channel_ordering', value: 'ORDER_UNORDERED' }, + { key: 'packet_connection', value: 'connection-2566' }, + { key: 'connection_id', value: 'connection-2566' }, + { key: 'msg_index', value: '1' }, + ], +} diff --git a/src/lib/eventParser.ts b/src/lib/eventParser.ts index c3197c8..0f941ec 100644 --- a/src/lib/eventParser.ts +++ b/src/lib/eventParser.ts @@ -29,7 +29,7 @@ export function parsePacketEvent(event: Event, height: number): PacketInfo { const ackHex = find(event, 'packet_ack_hex') - const ordering = find(event, 'packet_channel_ordering') as string + const ordering = find(event, 'packet_channel_ordering') const ack = ackHex ? Buffer.from(ackHex, 'hex').toString('base64') : undefined diff --git a/src/types/event.ts b/src/types/event.ts index 9f469f3..1b4f6c8 100644 --- a/src/types/event.ts +++ b/src/types/event.ts @@ -39,7 +39,7 @@ export interface PacketInfo { timeoutTimestamp: number timeoutHeightRaw: string timeoutTimestampRaw: string - ordering: string + ordering?: string // some version doesn't have ordering in write ack event ack?: string } diff --git a/tsconfig.json b/tsconfig.json index 7dd7fb3..45277fb 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,7 +22,8 @@ "noUnusedLocals": true, "noFallthroughCasesInSwitch": true, "skipLibCheck": true, + "esModuleInterop": true }, "include": ["src/**/*.ts", "**/*.mjs"], - "exclude": ["node_modules", "dist"], + "exclude": ["node_modules", "dist"] } From efaf272c486e146df43ff2ad97003b8fcbb6d446 Mon Sep 17 00:00:00 2001 From: ALPAC-4 Date: Mon, 11 Nov 2024 14:10:55 +0900 Subject: [PATCH 23/30] feat: add changelog --- CHANGELOG.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..6a4a458 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog + +## v0.0.1 - 2024-11-11 + +Initial release diff --git a/package.json b/package.json index af481ae..c04e6ba 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "ibc-relayer", + "name": "rapid-relayer", "version": "0.0.1", "description": "", "main": "index.js", From e7172ef0d5a55e9dc90eb77cee7f956a7965223b Mon Sep 17 00:00:00 2001 From: ALPAC-4 Date: Thu, 21 Nov 2024 18:19:16 +0900 Subject: [PATCH 24/30] fix: handle seq mismatch error --- src/workers/wallet.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/workers/wallet.ts b/src/workers/wallet.ts index 9a6fc76..1496038 100644 --- a/src/workers/wallet.ts +++ b/src/workers/wallet.ts @@ -346,6 +346,8 @@ export class WalletWorker { if (e?.response?.data) { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access this.logger.error(e.response.data) + // tmp: refresh sequence when got error. TODO: parse sequence from error + await this.initAccInfo() } else { this.logger.error(e) } From 9bd3ea3d60e302259a40e1be46df09e8cdc5e12b Mon Sep 17 00:00:00 2001 From: ALPAC-4 Date: Tue, 26 Nov 2024 16:38:44 +0900 Subject: [PATCH 25/30] fix: rest after update latest height --- src/workers/chain.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/workers/chain.ts b/src/workers/chain.ts index e7e77ab..cd9a3a1 100644 --- a/src/workers/chain.ts +++ b/src/workers/chain.ts @@ -87,6 +87,8 @@ export class ChainWorker { `<${this.chainId}> [latestHeightWorker] Max retry exceeded` ) } + } finally { + await delay(1000) } } } From 73d5ca42b28a96c9ef2d2fcee480f7e3d4073b20 Mon Sep 17 00:00:00 2001 From: beer-1 Date: Mon, 2 Dec 2024 15:35:26 +0900 Subject: [PATCH 26/30] fix typo and minor correction --- README.md | 4 +-- package-lock.json | 4 +-- src/db/controller/channel.spec.ts | 2 +- src/lib/config.ts | 2 +- src/lib/transform.ts | 2 +- src/msgs/ack.ts | 4 +-- src/msgs/channelCloseConfirm.ts | 4 +-- src/msgs/channelOpenAck.ts | 4 +-- src/msgs/channelOpenConfirm.ts | 4 +-- src/msgs/channelOpenTry.ts | 4 +-- src/msgs/recvPacket.ts | 4 +-- src/msgs/timeout.ts | 6 ++--- src/msgs/updateClient.ts | 3 +++ src/workers/chain.ts | 8 +++--- src/workers/index.ts | 41 ++++++++++++---------------- src/workers/wallet.ts | 44 +++++++++++++++---------------- 16 files changed, 68 insertions(+), 72 deletions(-) diff --git a/README.md b/README.md index 2fbdc99..2e195b6 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Rapid Relayer is a fast, scalable, stateful IBC Relayer optimized for interwoven Rapid Relayer does not use the `tx_search` query of Hermes to handle packets from several blocks at once. Initia Labs has developed this IBC Relayer to replace Hermes, only using the necessary functions for packet handling. -### Problems We Faced +## Problems We Faced - Minitia L2s generate blocks extremely quick at 500ms per block. - Due to the interwoven nature of Initia, often many IBC packets are generated within blocks. Hermes can handle batches of packets but on a single block basis. @@ -13,7 +13,7 @@ Rapid Relayer does not use the `tx_search` query of Hermes to handle packets fro - When Hermes misses a packet, it finds them using `tx_search` query on every sequence, this can take minutes for just a few hundred packets. - We need something more rapid. -### How We Fix This +## How We Fix This - We removed the `tx_search` query, and handle packets in parallel across several blocks at once. - Keep track of `synced_height` and `latest_height`. diff --git a/package-lock.json b/package-lock.json index 30b9969..4af4d57 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "ibc-relayer", + "name": "rapid-relayer", "version": "0.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "ibc-relayer", + "name": "rapid-relayer", "version": "0.0.1", "license": "ISC", "dependencies": { diff --git a/src/db/controller/channel.spec.ts b/src/db/controller/channel.spec.ts index c826013..d4f5811 100644 --- a/src/db/controller/channel.spec.ts +++ b/src/db/controller/channel.spec.ts @@ -9,7 +9,7 @@ import { DB } from '..' import { insert, select } from '../utils' import { mockServers } from 'src/test/testSetup' -describe('channel controler', () => { +describe('channel controller', () => { test('channel open e2e', async () => { const [mockServer1, mockServer2] = mockServers // test channel_open_init diff --git a/src/lib/config.ts b/src/lib/config.ts index 9900fcb..324a7d8 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -26,7 +26,7 @@ interface ChainConfig { interface WalletConfig { key: KeyConfig - maxHandlePakcet?: number // max packet amount that handle at once + maxHandlePacket?: number // max packet amount that handle at once packetFilter?: PacketFilter startHeight?: number } diff --git a/src/lib/transform.ts b/src/lib/transform.ts index 360d381..31030a6 100644 --- a/src/lib/transform.ts +++ b/src/lib/transform.ts @@ -1,7 +1,7 @@ import { Height as CosmjsHeight } from 'cosmjs-types/ibc/core/client/v1/client' import { Height as InitiajsHeight } from '@initia/initia.js' -export class Transfrom { +export class Transform { static height(height: CosmjsHeight): InitiajsHeight static height(height: InitiajsHeight): CosmjsHeight diff --git a/src/msgs/ack.ts b/src/msgs/ack.ts index fe0ab83..68d925a 100644 --- a/src/msgs/ack.ts +++ b/src/msgs/ack.ts @@ -1,6 +1,6 @@ import { Packet, MsgAcknowledgement } from '@initia/initia.js' import { Height } from 'cosmjs-types/ibc/core/client/v1/client' -import { Transfrom } from 'src/lib/transform' +import { Transform } from 'src/lib/transform' import { convertProofsToIcs23, getRawProof } from 'src/lib/proof' import { ChainWorker } from 'src/workers/chain' import { PacketWriteAckTable } from 'src/types' @@ -18,7 +18,7 @@ export async function generateMsgAck( packet, ack.ack, proof, - Transfrom.height(height), + Transform.height(height), executorAddress ) diff --git a/src/msgs/channelCloseConfirm.ts b/src/msgs/channelCloseConfirm.ts index f835595..555264b 100644 --- a/src/msgs/channelCloseConfirm.ts +++ b/src/msgs/channelCloseConfirm.ts @@ -2,7 +2,7 @@ import { MsgChannelCloseConfirm } from '@initia/initia.js' import { Height } from 'cosmjs-types/ibc/core/client/v1/client' import { getChannelProof } from 'src/lib/proof' import { ChainWorker } from 'src/workers/chain' -import { Transfrom } from 'src/lib/transform' +import { Transform } from 'src/lib/transform' export async function generateMsgChannelCloseConfirm( srcChain: ChainWorker, @@ -17,7 +17,7 @@ export async function generateMsgChannelCloseConfirm( dstPortId, dstChannelId, await getChannelProof(srcChain, srcPortId, srcChannelId, height), - Transfrom.height(height), + Transform.height(height), msgExecutor ) } diff --git a/src/msgs/channelOpenAck.ts b/src/msgs/channelOpenAck.ts index 1963f5c..9736001 100644 --- a/src/msgs/channelOpenAck.ts +++ b/src/msgs/channelOpenAck.ts @@ -2,7 +2,7 @@ import { MsgChannelOpenAck } from '@initia/initia.js' import { Height } from 'cosmjs-types/ibc/core/client/v1/client' import { getChannelProof } from 'src/lib/proof' import { ChainWorker } from 'src/workers/chain' -import { Transfrom } from 'src/lib/transform' +import { Transform } from 'src/lib/transform' export async function generateMsgChannelOpenAck( srcPortId: string, @@ -23,7 +23,7 @@ export async function generateMsgChannelOpenAck( dstChannelId, version, await getChannelProof(dstChain, dstPortId, dstChannelId, height), - Transfrom.height(height), + Transform.height(height), msgExecutor ) } diff --git a/src/msgs/channelOpenConfirm.ts b/src/msgs/channelOpenConfirm.ts index a896d45..0c49954 100644 --- a/src/msgs/channelOpenConfirm.ts +++ b/src/msgs/channelOpenConfirm.ts @@ -2,7 +2,7 @@ import { MsgChannelOpenConfirm } from '@initia/initia.js' import { Height } from 'cosmjs-types/ibc/core/client/v1/client' import { getChannelProof } from 'src/lib/proof' import { ChainWorker } from 'src/workers/chain' -import { Transfrom } from 'src/lib/transform' +import { Transform } from 'src/lib/transform' export async function generateMsgChannelOpenConfirm( srcChain: ChainWorker, @@ -17,7 +17,7 @@ export async function generateMsgChannelOpenConfirm( dstPortId, dstChannelId, await getChannelProof(srcChain, srcPortId, srcChannelId, height), - Transfrom.height(height), + Transform.height(height), msgExecutor ) } diff --git a/src/msgs/channelOpenTry.ts b/src/msgs/channelOpenTry.ts index d74b9ea..5afbc89 100644 --- a/src/msgs/channelOpenTry.ts +++ b/src/msgs/channelOpenTry.ts @@ -3,7 +3,7 @@ import { MsgChannelOpenTry } from '@initia/initia.js' import { Height } from 'cosmjs-types/ibc/core/client/v1/client' import { getChannelProof } from 'src/lib/proof' import { ChainWorker } from 'src/workers/chain' -import { Transfrom } from 'src/lib/transform' +import { Transform } from 'src/lib/transform' export async function generateMsgChannelOpenTry( srcChain: ChainWorker, @@ -32,7 +32,7 @@ export async function generateMsgChannelOpenTry( channel, version, await getChannelProof(srcChain, srcPortId, srcChannelId, height), - Transfrom.height(height), + Transform.height(height), msgExecutor ) } diff --git a/src/msgs/recvPacket.ts b/src/msgs/recvPacket.ts index e5b3e6f..aa260ba 100644 --- a/src/msgs/recvPacket.ts +++ b/src/msgs/recvPacket.ts @@ -1,7 +1,7 @@ import { Packet } from '@initia/initia.js' import { MsgRecvPacket } from '@initia/initia.js' import { Height } from 'cosmjs-types/ibc/core/client/v1/client' -import { Transfrom } from 'src/lib/transform' +import { Transform } from 'src/lib/transform' import { convertProofsToIcs23, getRawProof } from 'src/lib/proof' import { ChainWorker } from 'src/workers/chain' import { PacketSendTable } from 'src/types' @@ -19,7 +19,7 @@ export async function generateMsgRecvPacket( const msg = new MsgRecvPacket( packet, proof, - Transfrom.height(height), + Transform.height(height), msgExecutor ) diff --git a/src/msgs/timeout.ts b/src/msgs/timeout.ts index 5d65ae9..49c332d 100644 --- a/src/msgs/timeout.ts +++ b/src/msgs/timeout.ts @@ -1,6 +1,6 @@ import { Height } from 'cosmjs-types/ibc/core/client/v1/client' import { Uint64 } from '@cosmjs/math' -import { Transfrom } from 'src/lib/transform' +import { Transform } from 'src/lib/transform' import { Packet, MsgTimeout, MsgTimeoutOnClose } from '@initia/initia.js' import { convertProofsToIcs23, @@ -30,7 +30,7 @@ export async function generateMsgTimeout( return new MsgTimeout( packet, proof, - Transfrom.height(proofHeight), + Transform.height(proofHeight), sequence, executorAddress ) @@ -62,7 +62,7 @@ export async function generateMsgTimeoutOnClose( packet, proof, channelProof, - Transfrom.height(proofHeight), + Transform.height(proofHeight), sequence, executorAddress ) diff --git a/src/msgs/updateClient.ts b/src/msgs/updateClient.ts index c8db151..bf3cd0c 100644 --- a/src/msgs/updateClient.ts +++ b/src/msgs/updateClient.ts @@ -9,6 +9,9 @@ import { Height } from 'cosmjs-types/ibc/core/client/v1/client' import { delay } from 'bluebird' import { ChainWorker } from 'src/workers/chain' +// generateMsgUpdateClient generates a MsgUpdateClient message +// which is used to update the client state on the destination chain +// with the latest header from the source chain and the latest validator set. export async function generateMsgUpdateClient( srcChain: ChainWorker, dstChain: ChainWorker, diff --git a/src/workers/chain.ts b/src/workers/chain.ts index cd9a3a1..37d0d7d 100644 --- a/src/workers/chain.ts +++ b/src/workers/chain.ts @@ -65,7 +65,7 @@ export class ChainWorker { } private async latestHeightWorker() { - this.logger.debug('Activate latest height worekr') + this.logger.debug('Activate latest height worker') // TODO add websocket options const MAX_RETRY = 10 let retried = 0 @@ -145,7 +145,7 @@ class SyncWorker { let finish = false - const pakcetEventFeed = await PacketController.feedEvents( + const packetEventFeed = await PacketController.feedEvents( this.chain.rest, this.chain.chainId, packetEvents @@ -158,7 +158,7 @@ class SyncWorker { ) DB.transaction(() => { - pakcetEventFeed() + packetEventFeed() channelOpenEventFeed() PacketFeeController.feedEvents(this.chain.chainId, packetFeeEvents)() @@ -197,7 +197,7 @@ class SyncWorker { channelOpenEvents: ChannelOpenCloseEvent[] packetFeeEvents: PacketFeeEvent[] }> { - this.logger.debug(`Fecth new block results (height - ${height})`) + this.logger.debug(`Fetch new block results (height - ${height})`) const blockResult = await this.chain.rpc.blockResults(height) const txData = [...blockResult.results] diff --git a/src/workers/index.ts b/src/workers/index.ts index 1081e2d..768dd3a 100644 --- a/src/workers/index.ts +++ b/src/workers/index.ts @@ -93,7 +93,7 @@ export class WorkerController { const wallet = new WalletWorker( chain, this, - walletConfig.maxHandlePakcet ?? 100, + walletConfig.maxHandlePacket ?? 100, new Wallet(rest, key), walletConfig.packetFilter ) @@ -104,18 +104,17 @@ export class WorkerController { } public getFeeFilters(): { chainId: string; feeFilter: PacketFee }[] { - return Object.keys(this.chains).map((chainId) => ({ - chainId, - feeFilter: this.chains[chainId].feeFilter, - })) + return Object.entries(this.chains).map(([chainId, chain]) => { + return { + chainId, + feeFilter: chain.feeFilter, + } + }) } public getStatus(): { chains: ChainStatus[] } { - const chainKeys = Object.keys(this.chains) - const walletKeys = Object.keys(this.wallets) - - const chains: ChainStatus[] = chainKeys.map((key) => { - const chain = this.chains[key] + const wallets = Object.values(this.wallets); + const chains: ChainStatus[] = Object.values(this.chains).map(chain => { const syncWorkerKeys = Object.keys(chain.syncWorkers) const syncWorkers = syncWorkerKeys.map((key) => { const syncWorker = chain.syncWorkers[Number(key)] @@ -126,26 +125,20 @@ export class WorkerController { } }) + const walletWorkers = wallets.filter(wallet => wallet.chain.chainId === chain.chainId).map(wallet => { + return { + address: wallet.address(), + packetFilter: wallet.packetFilter, + } + }); + return { chainId: chain.chainId, latestHeight: chain.latestHeight, latestTimestamp: new Date(chain.latestTimestamp), syncWorkers, - walletWorkers: [], - } - }) - - walletKeys.map((key) => { - const wallet = this.wallets[key] - const walletWorker = { - address: wallet.address(), - packetFilter: wallet.packetFilter, + walletWorkers, } - - const chain = chains.filter( - (chain) => chain.chainId === wallet.chain.chainId - ) - chain[0].walletWorkers.push(walletWorker) }) return { diff --git a/src/workers/wallet.ts b/src/workers/wallet.ts index 1496038..02d270d 100644 --- a/src/workers/wallet.ts +++ b/src/workers/wallet.ts @@ -77,7 +77,7 @@ export class WalletWorker { } ).feeFilter - const sendPakcets = PacketController.getSendPackets( + const sendPackets = PacketController.getSendPackets( this.chain.chainId, this.chain.latestHeight, Number((this.chain.latestTimestamp / 1000).toFixed()), @@ -90,7 +90,7 @@ export class WalletWorker { this.workerController.chains[packet.src_chain_id].latestHeight ) - remain -= sendPakcets.length + remain -= sendPackets.length const writeAckPackets = remain === 0 @@ -140,7 +140,7 @@ export class WalletWorker { // update packet in progress DB.transaction(() => { - sendPakcets.map((packet) => + sendPackets.map((packet) => PacketController.updateSendPacketInProgress(packet) ) writeAckPackets.map((packet) => @@ -154,7 +154,7 @@ export class WalletWorker { try { // filter packets - const filteredSendPackets = await this.filterSendPackets(sendPakcets) + const filteredSendPackets = await this.filterSendPackets(sendPackets) const filteredWriteAckPackets = await this.filterWriteAckPackets(writeAckPackets) const filteredTimeoutPackets = @@ -354,7 +354,7 @@ export class WalletWorker { // revert packet in progress DB.transaction(() => { - sendPakcets.map((packet) => + sendPackets.map((packet) => PacketController.updateSendPacketInProgress(packet, false) ) writeAckPackets.map((packet) => @@ -447,29 +447,29 @@ export class WalletWorker { return } - const unrecivedPackets = await this.chain.rest.ibc.unreceivedPackets( + const unreceivedPackets = await this.chain.rest.ibc.unreceivedPackets( sendPacketMap[path][0].dst_port, sendPacketMap[path][0].dst_channel_id, sendPacketMap[path].map((packet) => packet.sequence) ) - const unrecivedSequences = unrecivedPackets.sequences.map((sequence) => + const unreceivedSequences = unreceivedPackets.sequences.map((sequence) => Number(sequence) ) sendPacketsToDel.push( ...sendPacketMap[path].filter( - (packet) => !unrecivedSequences.includes(packet.sequence) + (packet) => !unreceivedSequences.includes(packet.sequence) ) ) sendPacketMap[path] = sendPacketMap[path].filter((packet) => - unrecivedSequences.includes(packet.sequence) + unreceivedSequences.includes(packet.sequence) ) }) ) - // delete packets that already executed + // delete packets that already executed or in closed channel PacketController.delSendPackets(sendPacketsToDel) return Object.values(sendPacketMap).flat() @@ -495,24 +495,24 @@ export class WalletWorker { await Promise.all( Object.keys(writeAckPacketMap).map(async (path) => { if (writeAckPacketMap[path].length === 0) return - const unrecivedAcks = await this.chain.rest.ibc.unreceivedAcks( + const unreceivedAcks = await this.chain.rest.ibc.unreceivedAcks( writeAckPacketMap[path][0].src_port, writeAckPacketMap[path][0].src_channel_id, writeAckPacketMap[path].map((packet) => packet.sequence) ) - const unrecivedSequences = unrecivedAcks.sequences.map((sequence) => + const unreceivedSequences = unreceivedAcks.sequences.map((sequence) => Number(sequence) ) writeAckPacketsToDel.push( ...writeAckPacketMap[path].filter( - (packet) => !unrecivedSequences.includes(packet.sequence) + (packet) => !unreceivedSequences.includes(packet.sequence) ) ) writeAckPacketMap[path] = writeAckPacketMap[path].filter((packet) => - unrecivedSequences.includes(packet.sequence) + unreceivedSequences.includes(packet.sequence) ) }) ) @@ -545,24 +545,24 @@ export class WalletWorker { await Promise.all( Object.keys(timeoutPacketMap).map(async (path) => { if (timeoutPacketMap[path].length === 0) return - const unrecivedAcks = await this.chain.rest.ibc.unreceivedAcks( + const unreceivedAcks = await this.chain.rest.ibc.unreceivedAcks( timeoutPacketMap[path][0].src_port, timeoutPacketMap[path][0].src_channel_id, timeoutPacketMap[path].map((packet) => packet.sequence) ) - const unrecivedSequences = unrecivedAcks.sequences.map((sequence) => + const unreceivedSequences = unreceivedAcks.sequences.map((sequence) => Number(sequence) ) timeoutPacketsToDel.push( ...timeoutPacketMap[path].filter( - (packet) => !unrecivedSequences.includes(packet.sequence) + (packet) => !unreceivedSequences.includes(packet.sequence) ) ) timeoutPacketMap[path] = timeoutPacketMap[path].filter((packet) => - unrecivedSequences.includes(packet.sequence) + unreceivedSequences.includes(packet.sequence) ) }) ) @@ -573,24 +573,24 @@ export class WalletWorker { if (timeoutPacketMap[path].length === 0) return const chain = this.workerController.chains[timeoutPacketMap[path][0].dst_chain_id] - const unrecivedPackets = await chain.rest.ibc.unreceivedPackets( + const unreceivedPackets = await chain.rest.ibc.unreceivedPackets( timeoutPacketMap[path][0].dst_port, timeoutPacketMap[path][0].dst_channel_id, timeoutPacketMap[path].map((packet) => packet.sequence) ) - const unrecivedSequences = unrecivedPackets.sequences.map((sequence) => + const unreceivedSequences = unreceivedPackets.sequences.map((sequence) => Number(sequence) ) timeoutPacketsToDel.push( ...timeoutPacketMap[path].filter( - (packet) => !unrecivedSequences.includes(packet.sequence) + (packet) => !unreceivedSequences.includes(packet.sequence) ) ) timeoutPacketMap[path] = timeoutPacketMap[path].filter((packet) => - unrecivedSequences.includes(packet.sequence) + unreceivedSequences.includes(packet.sequence) ) }) ) From 91bef0485d9e9b21f9780dbde480b9181bf3068d Mon Sep 17 00:00:00 2001 From: beer-1 Date: Mon, 2 Dec 2024 15:37:30 +0900 Subject: [PATCH 27/30] add comments --- src/db/controller/channel.ts | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/db/controller/channel.ts b/src/db/controller/channel.ts index 36751b7..b252208 100644 --- a/src/db/controller/channel.ts +++ b/src/db/controller/channel.ts @@ -126,6 +126,7 @@ export class ChannelController { }) } + // executed on the src chain private static async feedChannelOpenInitEvent( rest: RESTClient, chainId: string, @@ -153,10 +154,11 @@ export class ChannelController { } return () => { - insert(DB, this.tableName, channelOnOpen) + insert(DB, this.tableName, channelOnOpen) // store INIT state to the dst chain } } + // executed on the dst chain private static async feedChannelOpenTryEvent( rest: RESTClient, chainId: string, @@ -191,11 +193,12 @@ export class ChannelController { counterparty_port_id: channelOnOpen.port_id, counterparty_channel_id: channelOnOpen.channel_id, }, - ]) // remove init - insert(DB, this.tableName, channelOnOpen) + ]) // remove INIT from the dst chain + insert(DB, this.tableName, channelOnOpen) // store TRYOPEN state to the src chain } } + // executed on the src chain private static async feedChannelOpenAckEvent( rest: RESTClient, chainId: string, @@ -230,7 +233,7 @@ export class ChannelController { counterparty_port_id: channelOnOpen.port_id, counterparty_channel_id: channelOnOpen.channel_id, }, - ]) // remove open try + ]) // remove TRYOPEN from src chain del(DB, this.tableName, [ { state: ChannelState.INIT, @@ -238,11 +241,12 @@ export class ChannelController { counterparty_port_id: channelOnOpen.counterparty_port_id, counterparty_channel_id: channelOnOpen.counterparty_channel_id, }, - ]) // remove init - insert(DB, this.tableName, channelOnOpen) + ]) // remove INIT from dst chain + insert(DB, this.tableName, channelOnOpen) // store ACK state to the dst chain } } + // executed on the dst chain private static async feedChannelOpenConfirmEvent( rest: RESTClient, chainId: string, @@ -261,7 +265,7 @@ export class ChannelController { counterparty_port_id: event.channelOpenCloseInfo.dstPortId, counterparty_channel_id: event.channelOpenCloseInfo.dstChannelId, }, - ]) // remove open try + ]) // remove TRYOPEN from src chain del(DB, this.tableName, [ { state: ChannelState.ACK, @@ -269,7 +273,7 @@ export class ChannelController { counterparty_port_id: event.channelOpenCloseInfo.srcPortId, counterparty_channel_id: event.channelOpenCloseInfo.srcChannelId, }, - ]) // remove ack + ]) // remove ACK from dst chain } } From 36dd4a27cc6043c6c9e19fcf1ce2aed08d7bd639 Mon Sep 17 00:00:00 2001 From: ALPAC-4 Date: Mon, 2 Dec 2024 16:56:18 +0900 Subject: [PATCH 28/30] fix: revert default value --- src/lib/eventParser.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/lib/eventParser.ts b/src/lib/eventParser.ts index 0f941ec..d7fabc0 100644 --- a/src/lib/eventParser.ts +++ b/src/lib/eventParser.ts @@ -128,7 +128,11 @@ function getConnection(event: Event): string | undefined { return find(event, 'connection_id') || find(event, 'packet_connection') } -function find(event: Event, key: string): string | undefined { +function find( + event: Event, + key: string, + defaultValue = '' +): string | undefined { // check key { const vals = event.attributes.filter((v) => v.key === key) @@ -147,5 +151,5 @@ function find(event: Event, key: string): string | undefined { } } - return undefined + return defaultValue } From 6521916fb15c3517166175cbc3a832af12eecbc6 Mon Sep 17 00:00:00 2001 From: ALPAC-4 Date: Thu, 5 Dec 2024 14:54:37 +0900 Subject: [PATCH 29/30] fix: fix errors from comments --- src/db/constants.ts | 3 --- src/db/controller/channel.ts | 8 +++----- src/db/controller/packet.ts | 2 +- src/lib/eventParser.ts | 5 ++--- src/workers/wallet.ts | 16 +++++++++------- 5 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/db/constants.ts b/src/db/constants.ts index 12681f5..373d562 100644 --- a/src/db/constants.ts +++ b/src/db/constants.ts @@ -1,6 +1,3 @@ // constants export const U64_MAX = 0xffffffffffffffffn - -// db path -export const DB_PATH = './.db/main.db' diff --git a/src/db/controller/channel.ts b/src/db/controller/channel.ts index b252208..90c7fbf 100644 --- a/src/db/controller/channel.ts +++ b/src/db/controller/channel.ts @@ -44,9 +44,7 @@ export class ChannelController { ) break case 'channel_close_confirm': - feedFns.push( - await this.feedChannelCloseConfirmEvent(rest, chainId, event) - ) + feedFns.push(this.feedChannelCloseConfirmEvent(rest, chainId, event)) break } } @@ -308,11 +306,11 @@ export class ChannelController { } } - private static async feedChannelCloseConfirmEvent( + private static feedChannelCloseConfirmEvent( _rest: RESTClient, chainId: string, event: ChannelOpenCloseEvent - ): Promise<() => void> { + ): () => void { return () => { del(DB, this.tableName, [ { diff --git a/src/db/controller/packet.ts b/src/db/controller/packet.ts index 3001992..e0eaa86 100644 --- a/src/db/controller/packet.ts +++ b/src/db/controller/packet.ts @@ -598,7 +598,7 @@ export class PacketController { } } - // update timeout timestamp to -1 + // update timeout timestamp to -1 to execute timeout closed channel public static updateTimeout(chainId: string, channelId: string) { update( DB, diff --git a/src/lib/eventParser.ts b/src/lib/eventParser.ts index d7fabc0..8e34ba6 100644 --- a/src/lib/eventParser.ts +++ b/src/lib/eventParser.ts @@ -143,9 +143,8 @@ function find( { // check base64 encoded key - const vals = event.attributes.filter( - (v) => v.key === Buffer.from(`${key}`).toString('base64') - ) + const base64Key = Buffer.from(key).toString('base64') + const vals = event.attributes.filter((v) => v.key === base64Key) if (vals.length !== 0) { return Buffer.from(vals[0].value, 'base64').toString() } diff --git a/src/workers/wallet.ts b/src/workers/wallet.ts index 02d270d..fa9c64d 100644 --- a/src/workers/wallet.ts +++ b/src/workers/wallet.ts @@ -122,6 +122,8 @@ export class WalletWorker { remain ) + remain -= timeoutPackets.length + const channelOpenEvents = remain === 0 ? [] @@ -321,7 +323,7 @@ export class WalletWorker { if (result.raw_log.startsWith('account sequence mismatch')) { try { const expected = result.raw_log.split(', ')[1] - this.sequence = Number(expected.split(' ')[1]) + this.sequence = Number(expected.split(' ')[1]) - 1 this.logger.info(`update sequence`) } catch (e) { this.logger.warn(`error to parse sequence`) @@ -453,8 +455,8 @@ export class WalletWorker { sendPacketMap[path].map((packet) => packet.sequence) ) - const unreceivedSequences = unreceivedPackets.sequences.map((sequence) => - Number(sequence) + const unreceivedSequences = unreceivedPackets.sequences.map( + (sequence) => Number(sequence) ) sendPacketsToDel.push( @@ -483,7 +485,7 @@ export class WalletWorker { const writeAckPacketsToDel: PacketWriteAckTable[] = [] for (const packet of writeAckPackets) { - const path = `${packet.src_port}/${packet.src_port}` + const path = `${packet.src_port}/${packet.src_channel_id}` if (!writeAckPacketMap[path]) { writeAckPacketMap[path] = [] } @@ -531,7 +533,7 @@ export class WalletWorker { const timeoutPacketsToDel: PacketTimeoutTable[] = [] for (const packet of timeoutPackets) { - const path = `${packet.src_port}/${packet.src_port}` + const path = `${packet.src_port}/${packet.src_channel_id}` if (!timeoutPacketMap[path]) { timeoutPacketMap[path] = [] } @@ -579,8 +581,8 @@ export class WalletWorker { timeoutPacketMap[path].map((packet) => packet.sequence) ) - const unreceivedSequences = unreceivedPackets.sequences.map((sequence) => - Number(sequence) + const unreceivedSequences = unreceivedPackets.sequences.map( + (sequence) => Number(sequence) ) timeoutPacketsToDel.push( From d32228380d5d4e24a579372ee4938f689cce6235 Mon Sep 17 00:00:00 2001 From: ALPAC-4 Date: Fri, 6 Dec 2024 14:57:55 +0900 Subject: [PATCH 30/30] fix: set timeout for close init event --- src/db/controller/channel.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/db/controller/channel.ts b/src/db/controller/channel.ts index 90c7fbf..a9a9a28 100644 --- a/src/db/controller/channel.ts +++ b/src/db/controller/channel.ts @@ -303,6 +303,12 @@ export class ChannelController { return () => { insert(DB, this.tableName, channelOnOpen) + + // Mark all packets as timed out + PacketController.updateTimeout( + chainId, + event.channelOpenCloseInfo.srcChannelId + ) } }