diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..299ce21 --- /dev/null +++ b/.env.example @@ -0,0 +1,46 @@ +# EVM CHAINS +RPC_URL='https://sepolia.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161' +WS_URL='wss://sepolia.infura.io/ws/v3/9aa3d95b3bc440fa88ea12eaa4456161' + +# Assets +COIN_TRANSFER_TEST_IS_ACTIVE=false +TOKEN_TRANSFER_TEST_IS_ACTIVE=false +TOKEN_APPROVE_TEST_IS_ACTIVE=false +TOKEN_TRANSFER_FROM_TEST_IS_ACTIVE=false +NFT_TRANSACTION_TEST_IS_ACTIVE=false +TRANSACTION_LISTENER_TEST_IS_ACTIVE=false + +COIN_BALANCE_TEST_AMOUNT=0.01 +TOKEN_BALANCE_TEST_AMOUNT=1000 +NFT_BALANCE_TEST_AMOUNT=2 +TRANSFER_TEST_AMOUNT=0.0001 +TOKEN_TRANSFER_TEST_AMOUNT=1 +TOKEN_APPROVE_TEST_AMOUNT=100 +NFT_TRANSFER_ID=7 + +BALANCE_TEST_ADDRESS='0x760A4d3D03928D1e8541A7644B34370c1b79aa9F' +SENDER_PRIVATE_KEY='0x14bd9af4e87981b37b7b2e8a0d1d249b7fcdb7a3bc579c4c31488842d372c0e9' +RECEIVER_PRIVATE_KEY='0x22ac1009c43f251e0b5a808751990abe77a74fe12f390c0cc95ab179a0b61a5a' +SENDER_TEST_ADDRESS='0x110600bF0399174520a159ed425f0D272Ff8b459' +RECEIVER_TEST_ADDRESS='0xbBa4d06D1cEf94b35aDeCfDa893523907fdD36DE' +TOKEN_TEST_ADDRESS='0x4294cb0dD25dC9140B5127f247cBd47Eeb673431' +NFT_TEST_ADDRESS='0x06B8B36e4feD2206E980445C0f0829fc6B2aA91F' +# Assets + +# Models +NFT_ID=7 +TOKEN_AMOUNT=1 +COIN_AMOUNT=0.002548 + +COIN_SENDER='0x74dBE9cA4F93087A27f23164d4367b8ce66C33e2' +COIN_RECEIVER='0xb3c86232c163a988ce4358b10a2745864bfaa3ba' +TOKEN_SENDER='0x110600bF0399174520a159ed425f0D272Ff8b459' +TOKEN_RECEIVER='0xbBa4d06D1cEf94b35aDeCfDa893523907fdD36DE' +NFT_SENDER='0xbBa4d06D1cEf94b35aDeCfDa893523907fdD36DE' +NFT_RECEIVER='0x110600bF0399174520a159ed425f0D272Ff8b459' + +ETHER_TRANSFER_TX='0x566002399664e92f82ed654c181095bdd7ff3d3f1921d963257585891f622251' +TOKEN_TRANSFER_TX='0xdabda3905e585db91768f2ef877f7fbef7c0e8612c0a09c7b379981bdbc48975' +NFT_TRANSFER_TX='0x272a4698cd2062f2463481cf9eb78b68b35d59938383679b7642e6d669ac87eb' +# Models +# EVM CHAINS \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json index 345e634..4fa3012 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -25,5 +25,11 @@ "rules": { "filenames/match-exported": ["error", "pascal"] }, - "ignorePatterns": ["**/*.d.ts", "dist/", "node_modules/"] + "ignorePatterns": [ + "**/*.d.ts", + "dist/", + "node_modules/", + "test*.ts", + "packages/networks/boilerplate/" + ] } diff --git a/.github/workflows/npm-publish.yaml b/.github/workflows/npm-publish.yaml new file mode 100644 index 0000000..538a670 --- /dev/null +++ b/.github/workflows/npm-publish.yaml @@ -0,0 +1,48 @@ +name: NPM Auto Publish + +on: + release: + types: + - released + branches: + - master + +jobs: + check-packages: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Login to npm registry + run: npm config set //registry.npmjs.org/:_authToken=$NPM_AUTH_TOKEN + env: + NPM_AUTH_TOKEN: ${{secrets.NPM_AUTH_TOKEN}} + + - name: Check if version changed + run: | + npm install -g pnpm + + for dir in packages/*/ packages/networks/*/; do + if [ -d "$dir" ] && [ "$(basename "$dir")" != "boilerplate" ]; then + cd "$dir" || exit + + PACKAGE_INFO=$(node -p "JSON.stringify(require('./package.json'))") + PACKAGE_NAME=$(echo "$PACKAGE_INFO" | jq -r '.name') + CURRENT_VERSION=$(echo "$PACKAGE_INFO" | jq -r '.version') + + PREVIOUS_VERSION=$(npm show "$PACKAGE_NAME" version) + + if [ "$PREVIOUS_VERSION" != "$CURRENT_VERSION" ]; then + echo "Version changed for $PACKAGE_NAME from $PREVIOUS_VERSION to $CURRENT_VERSION" + pnpm install && npm publish + fi + + cd - || exit + fi + done \ No newline at end of file diff --git a/.github/workflows/test-and-lint.yaml b/.github/workflows/test-and-lint.yaml new file mode 100644 index 0000000..3270b9d --- /dev/null +++ b/.github/workflows/test-and-lint.yaml @@ -0,0 +1,49 @@ +name: Test and Lint Check + +on: + pull_request: + types: [opened, synchronize, reopened, edited] + branches: + - alpha + - master + +jobs: + test_and_lint: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Create .env file + run: cp .env.example .env + + - name: Install dependencies + run: | + npm install -g pnpm + + for dir in packages/*/ packages/networks/*/; do + if [ -d "$dir" ] && [ "$(basename "$dir")" != "boilerplate" ]; then + cd "$dir" || exit + pnpm install + cd - || exit + fi + done + + - name: Run test + run: npm run test + + - name: Runt lint + run: npm run lint + + - name: Set PR status + if: ${{ github.event_name == 'pull_request' }} + run: | + TOKEN="${{ secrets.GITHUB_TOKEN }}" + STATUS_URL="https://api.github.com/repos/${GITHUB_REPOSITORY}/statuses/${GITHUB_SHA}" + STATUS="{\"state\":\"success\",\"context\":\"Test Process\",\"description\":\"Test Process passed\"}" + curl -X POST -H "Authorization: token $TOKEN" -d "$STATUS" "$STATUS_URL" diff --git a/.gitignore b/.gitignore index f88a2dd..09d282a 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,10 @@ dist dist-ssr *.local index.html +test*.ts +test*.js +test*.cjs +.env # Editor directories and files .vscode/* diff --git a/esbuild.ts b/esbuild.ts new file mode 100644 index 0000000..6357475 --- /dev/null +++ b/esbuild.ts @@ -0,0 +1,19 @@ +import { build, type BuildOptions } from 'esbuild' + +const baseConfig: BuildOptions = { + entryPoints: ['src/index.ts'], + bundle: true, + platform: 'node', + format: 'cjs', + minify: true, + sourcemap: true, + logLevel: 'info', + outfile: 'dist/index.cjs' +} + +export default (customConfig: BuildOptions = {}): void => { + build({ + ...baseConfig, + ...customConfig + }).catch(() => process.exit(1)) +} diff --git a/package.json b/package.json index 8dc7497..7412014 100644 --- a/package.json +++ b/package.json @@ -3,10 +3,6 @@ "version": "0.0.0", "type": "module", "scripts": { - "dev": "vite", - "build": "tsc && vite build", - "preview": "vite preview", - "build:watch": "vite build --watch", "test": "vitest", "format": "prettier --write packages/", "lint": "eslint . --ext .ts --ignore-path .gitignore" @@ -14,6 +10,7 @@ "devDependencies": { "@types/node": "^20.11.20", "@typescript-eslint/eslint-plugin": "^6.21.0", + "esbuild": "^0.20.2", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-config-standard-with-typescript": "^43.0.1", @@ -25,13 +22,12 @@ "eslint-plugin-require-extensions": "^0.1.3", "jsdom": "^24.0.0", "prettier": "^3.2.5", + "tsx": "^4.7.1", "typescript": "^5.3.3", "vite": "^5.1.4", + "vite-plugin-dts": "^3.8.1", + "vite-plugin-env-compatible": "^2.0.1", "vite-plugin-node-polyfills": "^0.21.0", "vitest": "^1.3.1" - }, - "dependencies": { - "abi-decoder": "^2.4.0", - "ethers": "^6.11.1" } } diff --git a/packages/networks/boilerplate/esbuild.ts b/packages/networks/boilerplate/esbuild.ts new file mode 100644 index 0000000..5714b9c --- /dev/null +++ b/packages/networks/boilerplate/esbuild.ts @@ -0,0 +1,3 @@ +void import('../../../esbuild.ts').then((module) => { + module.default() +}) diff --git a/packages/networks/boilerplate/package.json b/packages/networks/boilerplate/package.json index be55ceb..7a27d29 100644 --- a/packages/networks/boilerplate/package.json +++ b/packages/networks/boilerplate/package.json @@ -1,9 +1,19 @@ { - "name": "@multiplechain/network-name", + "name": "@multiplechain/evm-chains", "version": "0.1.0", "type": "module", - "main": "./dist/esm/index.js", - "types": "./dist/types/index.d.ts", + "types": "dist/index.d.ts", + "main": "dist/index.cjs", + "module": "dist/index.es.js", + "unpkg": "dist/index.umd.js", + "browser": "dist/index.umd.js", + "jsdelivr": "dist/index.umd.js", + "exports": { + ".": { + "import": "./dist/index.es.js", + "require": "./dist/index.cjs" + } + }, "files": [ "dist", "README.md", @@ -11,19 +21,20 @@ ], "scripts": { "clean": "rm -rf dist", - "build": "tsc --build", "watch": "tsc --watch", + "build:vite": "vite build", + "build:node": "tsx esbuild.ts", "typecheck": "tsc --noEmit", "lint": "eslint . --ext .ts", "test": "vitest run --dir tests", - "prepublishOnly":"pnpm run build" + "prepublishOnly": "pnpm run build", + "build": "pnpm run build:vite && pnpm run build:node" }, "keywords": [ "web3", "crypto", "blockchain", - "multiple-chain", - "standard" + "multiple-chain" ], "author": "MultipleChain", "license": "MIT", @@ -36,7 +47,7 @@ "url": "https://github.com/MultipleChain/js/issues" }, "dependencies": { - "@multiplechain/types": "^0.1.13", - "@multiplechain/utils": "^0.1.7" + "@multiplechain/types": "^0.1.35", + "@multiplechain/utils": "^0.1.16" } } \ No newline at end of file diff --git a/packages/networks/boilerplate/pnpm-lock.yaml b/packages/networks/boilerplate/pnpm-lock.yaml new file mode 100644 index 0000000..a13694b --- /dev/null +++ b/packages/networks/boilerplate/pnpm-lock.yaml @@ -0,0 +1,276 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +dependencies: + '@multiplechain/types': + specifier: ^0.1.30 + version: 0.1.30 + '@multiplechain/utils': + specifier: ^0.1.10 + version: 0.1.10 + +packages: + + /@multiplechain/types@0.1.30: + resolution: {integrity: sha512-j72Ydn1FlEWF4lY6xUjI0ojf0y1JAjqYnddAaUUdzv5sRlFw2zze8VLqxzltFtjFigPbSUa7QHmbgTIXQ60HoQ==} + dev: false + + /@multiplechain/utils@0.1.10: + resolution: {integrity: sha512-Ed0YxvRakds1WiREcLWI+fr6YCNB3bSORPD/ZQNqhELtL5uVgYWunIwbx6yZmLNk4MY8Qqyh0iUjuCOBLplmiA==} + dependencies: + bignumber.js: 9.1.2 + web3-utils: 4.2.1 + dev: false + + /@noble/curves@1.3.0: + resolution: {integrity: sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==} + dependencies: + '@noble/hashes': 1.3.3 + dev: false + + /@noble/hashes@1.3.3: + resolution: {integrity: sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==} + engines: {node: '>= 16'} + dev: false + + /@scure/base@1.1.6: + resolution: {integrity: sha512-ok9AWwhcgYuGG3Zfhyqg+zwl+Wn5uE+dwC0NV/2qQkx4dABbb/bx96vWu8NSj+BNjjSjno+JRYRjle1jV08k3g==} + dev: false + + /@scure/bip32@1.3.3: + resolution: {integrity: sha512-LJaN3HwRbfQK0X1xFSi0Q9amqOgzQnnDngIt+ZlsBC3Bm7/nE7K0kwshZHyaru79yIVRv/e1mQAjZyuZG6jOFQ==} + dependencies: + '@noble/curves': 1.3.0 + '@noble/hashes': 1.3.3 + '@scure/base': 1.1.6 + dev: false + + /@scure/bip39@1.2.2: + resolution: {integrity: sha512-HYf9TUXG80beW+hGAt3TRM8wU6pQoYur9iNypTROm42dorCGmLnFe3eWjz3gOq6G62H2WRh0FCzAR1PI+29zIA==} + dependencies: + '@noble/hashes': 1.3.3 + '@scure/base': 1.1.6 + dev: false + + /available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + dependencies: + possible-typed-array-names: 1.0.0 + dev: false + + /bignumber.js@9.1.2: + resolution: {integrity: sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==} + dev: false + + /call-bind@1.0.7: + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} + 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.2 + dev: false + + /define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + gopd: 1.0.1 + dev: false + + /es-define-property@1.0.0: + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.4 + dev: false + + /es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + dev: false + + /ethereum-cryptography@2.1.3: + resolution: {integrity: sha512-BlwbIL7/P45W8FGW2r7LGuvoEZ+7PWsniMvQ4p5s2xCyw9tmaDlpfsN9HjAucbF+t/qpVHwZUisgfK24TCW8aA==} + dependencies: + '@noble/curves': 1.3.0 + '@noble/hashes': 1.3.3 + '@scure/bip32': 1.3.3 + '@scure/bip39': 1.2.2 + dev: false + + /eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + dev: false + + /for-each@0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + dependencies: + is-callable: 1.2.7 + dev: false + + /function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + dev: false + + /get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + hasown: 2.0.2 + dev: false + + /gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + dependencies: + get-intrinsic: 1.2.4 + dev: false + + /has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + dependencies: + es-define-property: 1.0.0 + dev: false + + /has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + engines: {node: '>= 0.4'} + dev: false + + /has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + dev: false + + /has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: false + + /hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + dependencies: + function-bind: 1.1.2 + dev: false + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: false + + /is-arguments@1.1.1: + resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + has-tostringtag: 1.0.2 + dev: false + + /is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + dev: false + + /is-generator-function@1.0.10: + resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.2 + dev: false + + /is-typed-array@1.1.13: + resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} + engines: {node: '>= 0.4'} + dependencies: + which-typed-array: 1.1.15 + dev: false + + /possible-typed-array-names@1.0.0: + resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} + engines: {node: '>= 0.4'} + dev: false + + /set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + dev: false + + /util@0.12.5: + resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} + dependencies: + inherits: 2.0.4 + is-arguments: 1.1.1 + is-generator-function: 1.0.10 + is-typed-array: 1.1.13 + which-typed-array: 1.1.15 + dev: false + + /web3-errors@1.1.4: + resolution: {integrity: sha512-WahtszSqILez+83AxGecVroyZsMuuRT+KmQp4Si5P4Rnqbczno1k748PCrZTS1J4UCPmXMG2/Vt+0Bz2zwXkwQ==} + engines: {node: '>=14', npm: '>=6.12.0'} + dependencies: + web3-types: 1.5.0 + dev: false + + /web3-types@1.5.0: + resolution: {integrity: sha512-geWuMIeegQ8AedKAO6wO4G4j1gyQ1F/AyKLMw2vud4bsfZayyzWJgCMDZtjYMm5uo2a7i8j1W3/4QFmzlSy5cw==} + engines: {node: '>=14', npm: '>=6.12.0'} + dev: false + + /web3-utils@4.2.1: + resolution: {integrity: sha512-Fk29BlEqD9Q9Cnw4pBkKw7czcXiRpsSco/BzEUl4ye0ZTSHANQFfjsfQmNm4t7uY11u6Ah+8F3tNjBeU4CA80A==} + engines: {node: '>=14', npm: '>=6.12.0'} + dependencies: + ethereum-cryptography: 2.1.3 + eventemitter3: 5.0.1 + web3-errors: 1.1.4 + web3-types: 1.5.0 + web3-validator: 2.0.4 + dev: false + + /web3-validator@2.0.4: + resolution: {integrity: sha512-qRxVePwdW+SByOmTpDZFWHIUAa7PswvxNszrOua6BoGqAhERo5oJZBN+EbWtK/+O+ApNxt5FR3nCPmiZldiOQA==} + engines: {node: '>=14', npm: '>=6.12.0'} + dependencies: + ethereum-cryptography: 2.1.3 + util: 0.12.5 + web3-errors: 1.1.4 + web3-types: 1.5.0 + zod: 3.22.4 + dev: false + + /which-typed-array@1.1.15: + resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.2 + dev: false + + /zod@3.22.4: + resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} + dev: false diff --git a/packages/networks/boilerplate/src/assets/Asset.ts b/packages/networks/boilerplate/src/assets/Asset.ts deleted file mode 100644 index 3a95dcd..0000000 --- a/packages/networks/boilerplate/src/assets/Asset.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { TransactionSigner } from '../services/TransactionSigner.ts' -import type { AssetInterface, TransactionSignerInterface } from '@multiplechain/types' - -export class Asset implements AssetInterface { - /** - * @returns Asset name - */ - getName(): string { - return 'example' - } - - /** - * @returns Asset symbol - */ - getSymbol(): string { - return 'example' - } - - /** - * @param owner Wallet address - * @returns Wallet balance as currency of TOKEN or COIN assets - */ - getBalance(owner: string): number { - return 0 - } - - /** - * transfer() method is the main method for processing transfers for fungible assets (TOKEN, COIN) - * @param sender Sender wallet address - * @param receiver Receiver wallet address - * @param amount Amount of assets that will be transferred - */ - transfer(sender: string, receiver: string, amount: number): TransactionSignerInterface { - return new TransactionSigner('example') - } -} diff --git a/packages/networks/boilerplate/src/assets/Coin.ts b/packages/networks/boilerplate/src/assets/Coin.ts index ce9952e..778ebd8 100644 --- a/packages/networks/boilerplate/src/assets/Coin.ts +++ b/packages/networks/boilerplate/src/assets/Coin.ts @@ -1,42 +1,60 @@ +import { Provider } from '../services/Provider.ts' import { TransactionSigner } from '../services/TransactionSigner.ts' import type { CoinInterface, TransactionSignerInterface } from '@multiplechain/types' export class Coin implements CoinInterface { /** - * @returns Coin name + * Blockchain network provider + */ + provider: Provider + + /** + * @param {Provider} provider network provider + */ + constructor(provider?: Provider) { + this.provider = provider ?? Provider.instance + } + + /** + * @returns {string} Coin name */ getName(): string { return 'example' } /** - * @returns Coin symbol + * @returns {string} Coin symbol */ getSymbol(): string { return 'example' } /** - * @returns Decimal value of the coin + * @returns {number} Decimal value of the coin */ getDecimals(): number { return 18 } /** - * @param owner Wallet address - * @returns Wallet balance as currency of TOKEN or COIN assets + * @param {string} owner Wallet address + * @returns {Promise} Wallet balance as currency of COIN */ - getBalance(owner: string): number { + async getBalance(owner: string): Promise { return 0 } /** - * @param sender Sender wallet address - * @param receiver Receiver wallet address - * @param amount Amount of assets that will be transferred + * @param {string} sender Sender wallet address + * @param {string} receiver Receiver wallet address + * @param {number} amount Amount of assets that will be transferred + * @returns {Promise} Transaction signer */ - transfer(sender: string, receiver: string, amount: number): TransactionSignerInterface { + async transfer( + sender: string, + receiver: string, + amount: number + ): Promise { return new TransactionSigner('example') } } diff --git a/packages/networks/boilerplate/src/assets/Contract.ts b/packages/networks/boilerplate/src/assets/Contract.ts index a16e5dc..d8c2edd 100644 --- a/packages/networks/boilerplate/src/assets/Contract.ts +++ b/packages/networks/boilerplate/src/assets/Contract.ts @@ -1,37 +1,48 @@ +import { Provider } from '../services/Provider.ts' import type { ContractInterface } from '@multiplechain/types' export class Contract implements ContractInterface { + /** + * Contract address + */ address: string /** - * @param address Contract address + * Blockchain network provider + */ + provider: Provider + + /** + * @param {string} address Contract address + * @param {Provider} provider Blockchain network provider */ - constructor(address: string) { + constructor(address: string, provider?: Provider) { this.address = address + this.provider = provider ?? Provider.instance } /** - * @returns Contract address + * @returns {string} Contract address */ getAddress(): string { return this.address } /** - * @param method Method name - * @param args Method parameters - * @returns Method result + * @param {string} method Method name + * @param {any[]} args Method parameters + * @returns {Promise} Method result */ - callMethod(method: string, ...args: any[]): any { + async callMethod(method: string, ...args: any[]): Promise { return {} } /** - * @param method Method name - * @param args Method parameters - * @returns Method data + * @param {string} method Method name + * @param {any[]} args Sender wallet address + * @returns {Promise} Encoded method data */ - getMethodData(method: string, ...args: any[]): any { + async getMethodData(method: string, ...args: any[]): Promise { return {} } } diff --git a/packages/networks/boilerplate/src/assets/NFT.ts b/packages/networks/boilerplate/src/assets/NFT.ts index 155c666..f3da454 100644 --- a/packages/networks/boilerplate/src/assets/NFT.ts +++ b/packages/networks/boilerplate/src/assets/NFT.ts @@ -4,64 +4,93 @@ import type { NftInterface, TransactionSignerInterface } from '@multiplechain/ty export class NFT extends Contract implements NftInterface { /** - * @returns Contract name + * @returns {Promise} NFT name */ - getName(): string { + async getName(): Promise { return 'example' } /** - * @returns Contract symbol + * @returns {Promise} NFT symbol */ - getSymbol(): string { + async getSymbol(): Promise { return 'example' } /** - * @param owner Wallet address - * @returns Wallet balance as currency of TOKEN or COIN assets + * @param {string} owner Wallet address + * @returns {Promise} Wallet balance as currency of NFT */ - getBalance(owner: string): number { + async getBalance(owner: string): Promise { return 0 } /** - * @returns Contract address + * @param {number} nftId NFT ID + * @returns {Promise} Wallet address of the owner of the NFT */ - getAddress(): string { + async getOwner(nftId: number): Promise { return 'example' } /** - * @returns NFT ID + * @param {number} nftId NFT ID + * @returns {Promise} URI of the NFT */ - getNftId(): number { - return 0 + async getTokenURI(nftId: number): Promise { + return 'example' } /** - * @param sender Sender address - * @param receiver Receiver address - * @param nftId NFT ID - * @returns Transaction signer + * @param {number} nftId ID of the NFT that will be transferred + * @returns {Promise} Wallet address of the approved spender */ - transfer(sender: string, receiver: string, nftId: number): TransactionSignerInterface { + async getApproved(nftId: number): Promise { + return 'example' + } + + /** + * @param {string} sender Sender address + * @param {string} receiver Receiver address + * @param {number} nftId NFT ID + * @returns {Promise} Transaction signer + */ + async transfer( + sender: string, + receiver: string, + nftId: number + ): Promise { return new TransactionSigner('example') } /** - * @param nftId NFT ID - * @returns NFT owner wallet address + * @param {string} spender Spender address + * @param {string} owner Owner address + * @param {string} receiver Receiver address + * @param {number} nftId NFT ID + * @returns {Promise} Transaction signer */ - getOwner(nftId: number): string { - return 'example' + async transferFrom( + spender: string, + owner: string, + receiver: string, + nftId: number + ): Promise { + return new TransactionSigner('example') } /** - * @param nftId NFT ID - * @returns NFT URI + * Gives permission to the spender to spend owner's tokens + * @param {string} owner Address of owner of the tokens that will be used + * @param {string} spender Address of the spender that will use the tokens of owner + * @param {number} nftId ID of the NFT that will be transferred + * @returns {Promise} Transaction signer */ - getTokenURI(nftId: number): string { - return 'example' + async approve( + owner: string, + spender: string, + nftId: number + ): Promise { + return new TransactionSigner('example') } } diff --git a/packages/networks/boilerplate/src/assets/Token.ts b/packages/networks/boilerplate/src/assets/Token.ts index 5853f5f..285183a 100644 --- a/packages/networks/boilerplate/src/assets/Token.ts +++ b/packages/networks/boilerplate/src/assets/Token.ts @@ -4,74 +4,93 @@ import type { TokenInterface, TransactionSignerInterface } from '@multiplechain/ export class Token extends Contract implements TokenInterface { /** - * @returns Token name + * @returns {Promise} Token name */ - getName(): string { + async getName(): Promise { return 'example' } /** - * @returns Token symbol + * @returns {Promise} Token symbol */ - getSymbol(): string { + async getSymbol(): Promise { return 'example' } /** - * @returns Contract address + * @returns {Promise} Decimal value of the token */ - getAddress(): string { - return 'example' + async getDecimals(): Promise { + return 18 } /** - * @returns Decimal value of the coin + * @param {string} owner Wallet address + * @returns {Promise} Wallet balance as currency of TOKEN */ - getDecimals(): number { - return 18 + async getBalance(owner: string): Promise { + return 0 } /** - * @param owner Wallet address - * @returns Wallet balance as currency of TOKEN or COIN assets + * @returns {Promise} Total supply of the token */ - getBalance(owner: string): number { + async getTotalSupply(): Promise { return 0 } /** - * @returns Total supply of the token + * @param {string} owner Address of owner of the tokens that is being used + * @param {string} spender Address of the spender that is using the tokens of owner + * @returns {Promise} Amount of tokens that the spender is allowed to spend */ - getTotalSupply(): number { + async getAllowance(owner: string, spender: string): Promise { return 0 } /** * transfer() method is the main method for processing transfers for fungible assets (TOKEN, COIN) - * @param sender Sender wallet address - * @param receiver Receiver wallet address - * @param amount Amount of assets that will be transferred + * @param {string} sender Sender wallet address + * @param {string} receiver Receiver wallet address + * @param {number} amount Amount of assets that will be transferred + * @returns {Promise} Transaction signer */ - transfer(sender: string, receiver: string, amount: number): TransactionSignerInterface { + async transfer( + sender: string, + receiver: string, + amount: number + ): Promise { return new TransactionSigner('example') } /** - * Gives permission to the spender to spend owner's tokens - * @param owner Address of owner of the tokens that will be used - * @param spender Address of the spender that will use the tokens of owner - * @param amount Amount of the tokens that will be used + * @param {string} spender Address of the spender of transaction + * @param {string} owner Sender wallet address + * @param {string} receiver Receiver wallet address + * @param {number} amount Amount of tokens that will be transferred + * @returns {Promise} Transaction signer */ - approve(owner: string, spender: string, amount: number): TransactionSignerInterface { + async transferFrom( + spender: string, + owner: string, + receiver: string, + amount: number + ): Promise { return new TransactionSigner('example') } /** - * @param owner Address of owner of the tokens that is being used - * @param spender Address of the spender that is using the tokens of owner - * @returns Amount of the tokens that is being used by spender + * Gives permission to the spender to spend owner's tokens + * @param {string} owner Address of owner of the tokens that will be used + * @param {string} spender Address of the spender that will use the tokens of owner + * @param {number} amount Amount of the tokens that will be used + * @returns {Promise} Transaction signer */ - allowance(owner: string, spender: string): number { - return 0 + async approve( + owner: string, + spender: string, + amount: number + ): Promise { + return new TransactionSigner('example') } } diff --git a/packages/networks/boilerplate/src/assets/index.ts b/packages/networks/boilerplate/src/assets/index.ts new file mode 100644 index 0000000..7ea522f --- /dev/null +++ b/packages/networks/boilerplate/src/assets/index.ts @@ -0,0 +1,4 @@ +export * from './NFT.ts' +export * from './Coin.ts' +export * from './Token.ts' +export * from './Contract.ts' diff --git a/packages/networks/boilerplate/src/index.ts b/packages/networks/boilerplate/src/index.ts index e69de29..668ab52 100644 --- a/packages/networks/boilerplate/src/index.ts +++ b/packages/networks/boilerplate/src/index.ts @@ -0,0 +1,8 @@ +export * from './services/Provider.ts' + +export * as assets from './assets/index.ts' +export * as models from './models/index.ts' +export * as services from './services/index.ts' + +export * as utils from '@multiplechain/utils' +export * as types from '@multiplechain/types' diff --git a/packages/networks/boilerplate/src/models/AssetTransaction.ts b/packages/networks/boilerplate/src/models/AssetTransaction.ts deleted file mode 100644 index e4cb42d..0000000 --- a/packages/networks/boilerplate/src/models/AssetTransaction.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Transaction } from './Transaction.ts' -import { TransactionStatusEnum } from '@multiplechain/types' -import type { AssetTransactionInterface, AssetDirectionEnum } from '@multiplechain/types' - -export class AssetTransaction extends Transaction implements AssetTransactionInterface { - /** - * @returns Receiver wallet address of the transaction (asset) - */ - getReceiver(): string { - return 'example' - } - - /** - * @returns Transfer amount of the transaction (coin) - */ - getAmount(): number { - return 0 - } - - /** - * @param direction - Direction of the transaction (asset) - * @param address - Wallet address of the receiver or sender of the transaction, dependant on direction - * @param amount Amount of assets that will be transferred - */ - verifyTransfer( - direction: AssetDirectionEnum, - address: string, - amount: number - ): TransactionStatusEnum { - return TransactionStatusEnum.CONFIRMED - } -} diff --git a/packages/networks/boilerplate/src/models/CoinTransaction.ts b/packages/networks/boilerplate/src/models/CoinTransaction.ts index e8e2fa8..9857f2e 100644 --- a/packages/networks/boilerplate/src/models/CoinTransaction.ts +++ b/packages/networks/boilerplate/src/models/CoinTransaction.ts @@ -4,29 +4,37 @@ import type { AssetDirectionEnum, CoinTransactionInterface } from '@multiplechai export class CoinTransaction extends Transaction implements CoinTransactionInterface { /** - * @returns Wallet address of the sender of transaction + * @returns {Promise} Wallet address of the receiver of transaction */ - getReceiver(): string { + async getReceiver(): Promise { return 'example' } /** - * @returns Transfer amount of the transaction (coin) + * @returns {Promise} Wallet address of the sender of transaction */ - getAmount(): number { + async getSender(): Promise { + return 'example' + } + + /** + * @returns {Promise} Amount of coin that will be transferred + */ + async getAmount(): Promise { return 0 } /** - * @param direction - Direction of the transaction (asset) - * @param address - Wallet address of the receiver or sender of the transaction, dependant on direction - * @param amount Amount of assets that will be transferred + * @param {AssetDirectionEnum} direction - Direction of the transaction (asset) + * @param {string} address - Wallet address of the receiver or sender of the transaction, dependant on direction + * @param {number} amount Amount of assets that will be transferred + * @returns {Promise} Status of the transaction */ - verifyTransfer( + async verifyTransfer( direction: AssetDirectionEnum, address: string, amount: number - ): TransactionStatusEnum { + ): Promise { return TransactionStatusEnum.PENDING } } diff --git a/packages/networks/boilerplate/src/models/ContractTransaction.ts b/packages/networks/boilerplate/src/models/ContractTransaction.ts index cb6f829..ad57241 100644 --- a/packages/networks/boilerplate/src/models/ContractTransaction.ts +++ b/packages/networks/boilerplate/src/models/ContractTransaction.ts @@ -2,7 +2,10 @@ import { Transaction } from './Transaction.ts' import type { ContractTransactionInterface } from '@multiplechain/types' export class ContractTransaction extends Transaction implements ContractTransactionInterface { - getAddress(): string { + /** + * @returns {Promise} Contract address of the transaction + */ + async getAddress(): Promise { return 'example' } } diff --git a/packages/networks/boilerplate/src/models/NftTransaction.ts b/packages/networks/boilerplate/src/models/NftTransaction.ts index 92dea08..57cf99a 100644 --- a/packages/networks/boilerplate/src/models/NftTransaction.ts +++ b/packages/networks/boilerplate/src/models/NftTransaction.ts @@ -4,44 +4,38 @@ import type { NftTransactionInterface, AssetDirectionEnum } from '@multiplechain export class NftTransaction extends ContractTransaction implements NftTransactionInterface { /** - * @returns Wallet address of the sender of transaction + * @returns {Promise} Receiver wallet address */ - getReceiver(): string { + async getReceiver(): Promise { return 'example' } /** - * @returns Transfer amount of the transaction (token) + * @returns {Promise} Wallet address of the sender of transaction */ - getAmount(): number { - return 0 + async getSender(): Promise { + return 'example' } /** - * @returns ID of the NFT + * @returns {Promise} NFT ID */ - getNftId(): number { + async getNftId(): Promise { return 0 } /** - * @returns Smart contract address of the transaction - */ - getAddress(): string { - return 'example' - } - - /** - * @param direction - Direction of the transaction (nft) - * @param address - Wallet address of the receiver or sender of the transaction, dependant on direction - * @param nftId ID of the NFT that will be transferred + * @param {AssetDirectionEnum} direction - Direction of the transaction (nft) + * @param {string} address - Wallet address of the receiver or sender of the transaction, dependant on direction + * @param {number} nftId ID of the NFT that will be transferred * @override verifyTransfer() in AssetTransactionInterface + * @returns {Promise} Status of the transaction */ - verifyTransfer( + async verifyTransfer( direction: AssetDirectionEnum, address: string, nftId: number - ): TransactionStatusEnum { + ): Promise { return TransactionStatusEnum.PENDING } } diff --git a/packages/networks/boilerplate/src/models/TokenTransaction.ts b/packages/networks/boilerplate/src/models/TokenTransaction.ts index c87cd2b..b1ea29e 100644 --- a/packages/networks/boilerplate/src/models/TokenTransaction.ts +++ b/packages/networks/boilerplate/src/models/TokenTransaction.ts @@ -4,49 +4,37 @@ import type { AssetDirectionEnum, TokenTransactionInterface } from '@multiplecha export class TokenTransaction extends ContractTransaction implements TokenTransactionInterface { /** - * @returns Wallet address of the sender of transaction + * @returns {Promise} Wallet address of the receiver of transaction */ - getReceiver(): string { + async getReceiver(): Promise { return 'example' } /** - * @returns Transfer amount of the transaction (token) + * @returns {Promise} Wallet address of the sender of transaction */ - getAmount(): number { - return 0 - } - - /** - * @returns Token address of the transaction - */ - getAddress(): string { + async getSender(): Promise { return 'example' } /** - * @param direction - Direction of the transaction (token) - * @param address - Wallet address of the owner or spender of the transaction, dependant on direction - * @param amount Amount of tokens that will be approved + * @returns {Promise} Amount of tokens that will be transferred */ - verifyTransfer( - direction: AssetDirectionEnum, - address: string, - amount: number - ): TransactionStatusEnum { - return TransactionStatusEnum.PENDING + async getAmount(): Promise { + return 0 } /** - * @param direction - Direction of the transaction (token) - * @param address - Wallet address of the owner or spender of the transaction, dependant on direction - * @param amount Amount of tokens that will be approved + * @param {AssetDirectionEnum} direction - Direction of the transaction (token) + * @param {string} address - Wallet address of the owner or spender of the transaction, dependant on direction + * @param {number} amount Amount of tokens that will be approved + * @returns {Promise} Status of the transaction */ - verifyApprove( + async verifyTransfer( direction: AssetDirectionEnum, address: string, amount: number - ): TransactionStatusEnum { + ): Promise { return TransactionStatusEnum.PENDING } } diff --git a/packages/networks/boilerplate/src/models/Transaction.ts b/packages/networks/boilerplate/src/models/Transaction.ts index 9f7e844..15f111d 100644 --- a/packages/networks/boilerplate/src/models/Transaction.ts +++ b/packages/networks/boilerplate/src/models/Transaction.ts @@ -1,3 +1,4 @@ +import { Provider } from '../services/Provider.ts' import type { TransactionInterface } from '@multiplechain/types' import { TransactionStatusEnum } from '@multiplechain/types' @@ -7,71 +8,87 @@ export class Transaction implements TransactionInterface { */ id: string - constructor(id: string) { + /** + * Blockchain network provider + */ + provider: Provider + + /** + * @param {string} id Transaction id + * @param {Provider} provider Blockchain network provider + */ + constructor(id: string, provider?: Provider) { this.id = id + this.provider = provider ?? Provider.instance } /** - * @returns Raw transaction data that is taken by blockchain network via RPC. + * @returns {Promise} Transaction data */ - getData(): object { + async getData(): Promise { return {} } /** - * @returns Transaction id from the blockchain network - * this can be different names like txid, hash, signature etc. + * @returns {Promise} Wait for the transaction to be confirmed + */ + async wait(): Promise { + return await Promise.resolve(TransactionStatusEnum.CONFIRMED) + } + + /** + * @returns {string} Transaction ID */ getId(): string { return this.id } /** - * @returns Blockchain explorer URL of the transaction. Dependant on network. + * @returns {string} Transaction URL */ getUrl(): string { return 'example' } /** - * @returns Wallet address of the sender of transaction + * @returns {Promise} Wallet address of the sender of transaction */ - getSender(): string { + async getSigner(): Promise { return 'example' } /** - * @returns Transaction fee as native coin amount + * @returns {Promise} Transaction fee */ - getFee(): number { + async getFee(): Promise { return 0 } /** - * @returns Block ID of the transaction + * @returns {Promise} Block number that transaction */ - getBlockNumber(): number { + async getBlockNumber(): Promise { return 0 } /** - * @returns UNIX timestamp of the date that block is added to blockchain + * @returns {Promise} Block timestamp that transaction */ - getBlockTimestamp(): number { + async getBlockTimestamp(): Promise { return 0 } /** - * @returns Confirmation count of the block that transaction is included + * @returns {Promise} Confirmation count of the block */ - getBlockConfirmationCount(): number { + async getBlockConfirmationCount(): Promise { return 0 } /** - * @returns Status of the transaction + * @returns {Promise} Status of the transaction */ - getStatus(): TransactionStatusEnum { + async getStatus(): Promise { return TransactionStatusEnum.CONFIRMED } } diff --git a/packages/networks/boilerplate/src/models/index.ts b/packages/networks/boilerplate/src/models/index.ts new file mode 100644 index 0000000..cc0a27c --- /dev/null +++ b/packages/networks/boilerplate/src/models/index.ts @@ -0,0 +1,5 @@ +export * from './Transaction.ts' +export * from './NftTransaction.ts' +export * from './CoinTransaction.ts' +export * from './TokenTransaction.ts' +export * from './ContractTransaction.ts' diff --git a/packages/networks/boilerplate/src/services/Provider.ts b/packages/networks/boilerplate/src/services/Provider.ts index 884b47a..9377756 100644 --- a/packages/networks/boilerplate/src/services/Provider.ts +++ b/packages/networks/boilerplate/src/services/Provider.ts @@ -1,4 +1,8 @@ -import type { NetworkConfigInterface, ProviderInterface } from '@multiplechain/types' +import { + ErrorTypeEnum, + type NetworkConfigInterface, + type ProviderInterface +} from '@multiplechain/types' export class Provider implements ProviderInterface { /** @@ -6,6 +10,11 @@ export class Provider implements ProviderInterface { */ network: NetworkConfigInterface + /** + * Static instance of the provider + */ + private static _instance: Provider + /** * @param network - Network configuration of the provider */ @@ -13,6 +22,47 @@ export class Provider implements ProviderInterface { this.network = network } + /** + * Get the static instance of the provider + * @returns {Provider} Provider + */ + static get instance(): Provider { + if (Provider._instance === undefined) { + throw new Error(ErrorTypeEnum.PROVIDER_IS_NOT_INITIALIZED) + } + return Provider._instance + } + + /** + * Initialize the static instance of the provider + * @param {NetworkConfigInterface} network - Network configuration of the provider + * @returns {void} + */ + static initialize(network: NetworkConfigInterface): void { + if (Provider._instance !== undefined) { + throw new Error(ErrorTypeEnum.PROVIDER_IS_ALREADY_INITIALIZED) + } + Provider._instance = new Provider(network) + } + + /** + * Check RPC connection + * @param {string} url - RPC URL + * @returns {Promise} + */ + async checkRpcConnection(url?: string): Promise { + return true + } + + /** + * Check WS connection + * @param {string} url - Websocket URL + * @returns {Promise} + */ + async checkWsConnection(url?: string): Promise { + return true + } + /** * Update network configuration of the provider * @param network - Network configuration of the provider diff --git a/packages/networks/boilerplate/src/services/TransactionListener.ts b/packages/networks/boilerplate/src/services/TransactionListener.ts index 5382093..fd17438 100644 --- a/packages/networks/boilerplate/src/services/TransactionListener.ts +++ b/packages/networks/boilerplate/src/services/TransactionListener.ts @@ -6,6 +6,7 @@ import type { DynamicTransactionListenerFilterType } from '@multiplechain/types' +import { Provider } from './Provider.ts' import { TransactionListenerProcessIndex } from '@multiplechain/types' export class TransactionListener @@ -24,29 +25,69 @@ export class TransactionListener /** * Transaction listener filter */ - filter: DynamicTransactionListenerFilterType + filter?: DynamicTransactionListenerFilterType /** - * @param type - Transaction type - * @param filter - Transaction listener filter + * Provider */ - constructor(type: T, filter: DynamicTransactionListenerFilterType) { + provider: Provider + + /** + * Listener status + */ + status: boolean = false + + /** + * Triggered transactions + */ + triggeredTransactions: string[] = [] + + /** + * @param {T} type - Transaction type + * @param {Provider} provider - Provider + * @param {DynamicTransactionListenerFilterType} filter - Transaction listener filter + */ + constructor(type: T, provider?: Provider, filter?: DynamicTransactionListenerFilterType) { this.type = type this.filter = filter - // @ts-expect-error allow dynamic access - this[TransactionListenerProcessIndex[type]]() + this.provider = provider ?? Provider.instance } /** * Close the listener + * @returns {void} */ stop(): void { - // Close the listener + if (this.status) { + this.status = false + // stop the listener + } + } + + /** + * Start the listener + * @returns {void} + */ + start(): void { + if (!this.status) { + this.status = true + // @ts-expect-error allow dynamic access + this[TransactionListenerProcessIndex[this.type]]() + } + } + + /** + * Get the listener status + * @returns {boolean} Listener status + */ + getStatus(): boolean { + return this.status } /** * Listen to the transaction events - * @param callback - Callback function + * @param {TransactionListenerCallbackType} callback - Transaction listener callback + * @returns {void} */ on(callback: TransactionListenerCallbackType): void { this.callbacks.push(callback) @@ -54,16 +95,21 @@ export class TransactionListener /** * Trigger the event when a transaction is detected - * @param transaction - Transaction data + * @param {DynamicTransactionType} transaction - Transaction data + * @returns {void} */ - trigger(transaction: DynamicTransactionType): void { - this.callbacks.forEach((callback) => { - callback(transaction) - }) + trigger(transaction: DynamicTransactionType): void { + if (!this.triggeredTransactions.includes(transaction.id)) { + this.triggeredTransactions.push(transaction.id) + this.callbacks.forEach((callback) => { + callback(transaction) + }) + } } /** * General transaction process + * @returns {void} */ generalProcess(): void { // General transaction process @@ -71,20 +117,15 @@ export class TransactionListener /** * Contract transaction process + * @returns {void} */ contractProcess(): void { // Contract transaction process } - /** - * Asset transaction process - */ - assetProcess(): void { - // Asset transaction process - } - /** * Coin transaction process + * @returns {void} */ coinProcess(): void { // Coin transaction process @@ -92,6 +133,7 @@ export class TransactionListener /** * Token transaction process + * @returns {void} */ tokenProcess(): void { // Token transaction process @@ -99,6 +141,7 @@ export class TransactionListener /** * NFT transaction process + * @returns {void} */ nftProcess(): void { // NFT transaction process diff --git a/packages/networks/boilerplate/src/services/TransactionSigner.ts b/packages/networks/boilerplate/src/services/TransactionSigner.ts index b791561..a76019e 100644 --- a/packages/networks/boilerplate/src/services/TransactionSigner.ts +++ b/packages/networks/boilerplate/src/services/TransactionSigner.ts @@ -1,4 +1,9 @@ -import type { TransactionSignerInterface, TransactionInterface } from '@multiplechain/types' +import { Provider } from '../services/Provider.ts' +import { Transaction } from '../models/Transaction.ts' +import { NftTransaction } from '../models/NftTransaction.ts' +import { CoinTransaction } from '../models/CoinTransaction.ts' +import { TokenTransaction } from '../models/TokenTransaction.ts' +import type { TransactionSignerInterface } from '@multiplechain/types' export class TransactionSigner implements TransactionSignerInterface { /** @@ -12,26 +17,33 @@ export class TransactionSigner implements TransactionSignerInterface { signedData?: any /** - * @param rawData - Transaction data + * Blockchain network provider */ - constructor(rawData: any) { + provider: Provider + + /** + * @param {any} rawData - Transaction data + */ + constructor(rawData: any, provider?: Provider) { this.rawData = rawData + this.provider = provider ?? Provider.instance } /** * Sign the transaction - * @param privateKey - Transaction data + * @param {string} privateKey - Transaction data + * @returns {Promise} Signed transaction data */ - sign(privateKey: string): this { - return this + async sign(privateKey: string): Promise { + return await Promise.resolve(this) } /** * Send the transaction to the blockchain network - * @returns Promise of the transaction + * @returns {Promise} */ - async send(): Promise { - return await Promise.resolve(this.signedData) + async send(): Promise { + return await Promise.resolve(new Transaction('example')) } /** @@ -50,3 +62,33 @@ export class TransactionSigner implements TransactionSignerInterface { return this.signedData } } + +export class CoinTransactionSigner extends TransactionSigner { + /** + * Send the transaction to the blockchain network + * @returns {Promise} Transaction data + */ + async send(): Promise { + return new CoinTransaction((await super.send()).getId()) + } +} + +export class TokenTransactionSigner extends TransactionSigner { + /** + * Send the transaction to the blockchain network + * @returns {Promise} Transaction data + */ + async send(): Promise { + return new TokenTransaction((await super.send()).getId()) + } +} + +export class NftTransactionSigner extends TransactionSigner { + /** + * Send the transaction to the blockchain network + * @returns {Promise} Transaction data + */ + async send(): Promise { + return new NftTransaction((await super.send()).getId()) + } +} diff --git a/packages/networks/boilerplate/src/services/index.ts b/packages/networks/boilerplate/src/services/index.ts new file mode 100644 index 0000000..549a382 --- /dev/null +++ b/packages/networks/boilerplate/src/services/index.ts @@ -0,0 +1,2 @@ +export * from './TransactionSigner.ts' +export * from './TransactionListener.ts' diff --git a/packages/networks/boilerplate/tests/assets.spec.ts b/packages/networks/boilerplate/tests/assets.spec.ts index e69de29..34c5579 100644 --- a/packages/networks/boilerplate/tests/assets.spec.ts +++ b/packages/networks/boilerplate/tests/assets.spec.ts @@ -0,0 +1,7 @@ +import { describe, it, expect } from 'vitest' + +describe('example', () => { + it('should work', () => { + expect(1 + 1).toBe(2) + }) +}) diff --git a/packages/networks/boilerplate/tests/models.spec.ts b/packages/networks/boilerplate/tests/models.spec.ts index e69de29..34c5579 100644 --- a/packages/networks/boilerplate/tests/models.spec.ts +++ b/packages/networks/boilerplate/tests/models.spec.ts @@ -0,0 +1,7 @@ +import { describe, it, expect } from 'vitest' + +describe('example', () => { + it('should work', () => { + expect(1 + 1).toBe(2) + }) +}) diff --git a/packages/networks/boilerplate/tests/services.spec.ts b/packages/networks/boilerplate/tests/services.spec.ts index e69de29..34c5579 100644 --- a/packages/networks/boilerplate/tests/services.spec.ts +++ b/packages/networks/boilerplate/tests/services.spec.ts @@ -0,0 +1,7 @@ +import { describe, it, expect } from 'vitest' + +describe('example', () => { + it('should work', () => { + expect(1 + 1).toBe(2) + }) +}) diff --git a/packages/networks/boilerplate/tests/setup.ts b/packages/networks/boilerplate/tests/setup.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/networks/boilerplate/tsconfig.json b/packages/networks/boilerplate/tsconfig.json index 40bb104..e40c547 100644 --- a/packages/networks/boilerplate/tsconfig.json +++ b/packages/networks/boilerplate/tsconfig.json @@ -7,5 +7,15 @@ "declarationDir": "./dist/types" }, "extends": "../../../tsconfig.json", - "include": ["src", ".eslintrc.json", "tests"] + "include": [ + "src", + ".eslintrc.json", + "tests", + "vite.config.ts", + "esbuild.ts", + "vitest.config.ts", + "../../../esbuild.ts", + "../../../vite.config.ts", + "../../../vitest.config.ts", + ] } diff --git a/packages/networks/boilerplate/vite.config.ts b/packages/networks/boilerplate/vite.config.ts new file mode 100644 index 0000000..ddc52ce --- /dev/null +++ b/packages/networks/boilerplate/vite.config.ts @@ -0,0 +1,10 @@ +import { mergeConfig } from 'vite' +import mainConfig from '../../../vite.config.ts' + +export default mergeConfig(mainConfig, { + build: { + lib: { + name: 'Boilerplate' + } + } +}) diff --git a/packages/networks/boilerplate/vitest.config.ts b/packages/networks/boilerplate/vitest.config.ts new file mode 100644 index 0000000..73ee32b --- /dev/null +++ b/packages/networks/boilerplate/vitest.config.ts @@ -0,0 +1,11 @@ +import { mergeConfig, defineConfig } from 'vitest/config' +import mainConfig from '../../../vite.config.ts' + +export default mergeConfig( + mainConfig, + defineConfig({ + test: { + setupFiles: ['./tests/setup.ts'] + } + }) +) diff --git a/packages/networks/evm-chains/.eslintrc.json b/packages/networks/evm-chains/.eslintrc.json index b62472f..e7ed887 100644 --- a/packages/networks/evm-chains/.eslintrc.json +++ b/packages/networks/evm-chains/.eslintrc.json @@ -1,7 +1,12 @@ { - "extends": [ - "plugin:require-extensions/recommended", - "../../../.eslintrc.json" - ], + "rules": { + "@typescript-eslint/no-misused-promises": [ + "error", + { + "checksVoidReturn": false + } + ] + }, + "extends": ["plugin:require-extensions/recommended", "../../../.eslintrc.json"], "plugins": ["require-extensions"] -} \ No newline at end of file +} diff --git a/packages/networks/evm-chains/README.md b/packages/networks/evm-chains/README.md index e69de29..6ae2ea3 100644 --- a/packages/networks/evm-chains/README.md +++ b/packages/networks/evm-chains/README.md @@ -0,0 +1 @@ +Preparing \ No newline at end of file diff --git a/packages/networks/evm-chains/cdt.ts b/packages/networks/evm-chains/cdt.ts new file mode 100644 index 0000000..e6c4aa6 --- /dev/null +++ b/packages/networks/evm-chains/cdt.ts @@ -0,0 +1,24 @@ +// Create dynamic types + +import fs from 'fs' +import { networks } from './src/index.ts' + +console.log('\r\nCreating dynamic types...') + +const networkTypes: string[] = [] + +Object.entries(networks).forEach(([key, value]) => { + if (typeof value === 'object' && key !== '__esModule') { + const type = ` ${key}: EvmNetworkConfigInterface;` + networkTypes.push(type) + } +}) + +const content = fs.readFileSync('./dist/services/Networks.d.ts', 'utf-8') +const insertIndex = content.lastIndexOf('}') +const updatedContent = + content.slice(0, insertIndex) + networkTypes.join('\n') + '\n' + content.slice(insertIndex) + +fs.writeFileSync('./dist/services/Networks.d.ts', updatedContent) + +console.log('Dynamic types created successfully!') diff --git a/packages/networks/evm-chains/esbuild.ts b/packages/networks/evm-chains/esbuild.ts new file mode 100644 index 0000000..5714b9c --- /dev/null +++ b/packages/networks/evm-chains/esbuild.ts @@ -0,0 +1,3 @@ +void import('../../../esbuild.ts').then((module) => { + module.default() +}) diff --git a/packages/networks/evm-chains/package.json b/packages/networks/evm-chains/package.json index d300bc7..fd6be2e 100644 --- a/packages/networks/evm-chains/package.json +++ b/packages/networks/evm-chains/package.json @@ -1,9 +1,19 @@ { "name": "@multiplechain/evm-chains", - "version": "0.3.5", + "version": "0.3.38", "type": "module", - "main": "./dist/esm/index.js", - "types": "./dist/types/index.d.ts", + "types": "dist/index.d.ts", + "main": "dist/index.cjs", + "module": "dist/index.es.js", + "unpkg": "dist/index.umd.js", + "browser": "dist/index.umd.js", + "jsdelivr": "dist/index.umd.js", + "exports": { + ".": { + "import": "./dist/index.es.js", + "require": "./dist/index.cjs" + } + }, "files": [ "dist", "README.md", @@ -11,12 +21,14 @@ ], "scripts": { "clean": "rm -rf dist", - "build": "tsc --build", "watch": "tsc --watch", + "build:vite": "vite build", + "build:node": "tsx esbuild.ts", "typecheck": "tsc --noEmit", "lint": "eslint . --ext .ts", "test": "vitest run --dir tests", - "prepublishOnly": "pnpm run build" + "prepublishOnly": "pnpm run build", + "build": "pnpm run build:vite && pnpm run build:node && tsx cdt.ts" }, "keywords": [ "web3", @@ -38,7 +50,9 @@ "url": "https://github.com/MultipleChain/js/issues" }, "dependencies": { - "@multiplechain/types": "^0.1.13", - "@multiplechain/utils": "^0.1.7" + "@multiplechain/types": "^0.1.35", + "@multiplechain/utils": "^0.1.16", + "@wagmi/chains": "^1.8.0", + "ethers": "^6.11.1" } } \ No newline at end of file diff --git a/packages/networks/evm-chains/pnpm-lock.yaml b/packages/networks/evm-chains/pnpm-lock.yaml index 16249c9..63086cb 100644 --- a/packages/networks/evm-chains/pnpm-lock.yaml +++ b/packages/networks/evm-chains/pnpm-lock.yaml @@ -6,23 +6,44 @@ settings: dependencies: '@multiplechain/types': - specifier: ^0.1.13 - version: 0.1.13 + specifier: ^0.1.35 + version: 0.1.35 '@multiplechain/utils': - specifier: ^0.1.7 - version: 0.1.7 + specifier: ^0.1.16 + version: 0.1.16 + '@wagmi/chains': + specifier: ^1.8.0 + version: 1.8.0 + ethers: + specifier: ^6.11.1 + version: 6.11.1 packages: - /@multiplechain/types@0.1.13: - resolution: {integrity: sha512-sM+J8YKUPDh35aOtUv4dytv8Lq+ezVXSktNew7ngIpEgW2pubqsTLmFmAmI4H58DHd5XowQvK6cbApne6q4/Ug==} + /@adraffy/ens-normalize@1.10.1: + resolution: {integrity: sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==} dev: false - /@multiplechain/utils@0.1.7: - resolution: {integrity: sha512-I29JEiL+OTs6I6Lb4dTSsshcODttgO8oeCIP0xvJnkebIHz8ODyNG6rnQtUV/cOHhf4S12ZBkLN4FNJFuy/Mmw==} + /@multiplechain/types@0.1.35: + resolution: {integrity: sha512-szg+arayBvT3WivUa9l6ZYjS6tNjhFMDpQCcAUL94s5yWpDM73OnHMA861XVVVY0xIUgJpzCj32PlJU+CARYow==} + dev: false + + /@multiplechain/utils@0.1.16: + resolution: {integrity: sha512-wJPLuHRt2r8DZt0S6YkeRZRRcy/bc20/Y0xolESOIWfLdBy/N5YKBMJaAwEV+pOWuP77Mx+rzZBlfeOBmvZBXw==} dependencies: + '@types/ws': 8.5.10 bignumber.js: 9.1.2 - web3-utils: 4.2.0 + web3-utils: 4.2.2 + ws: 8.16.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: false + + /@noble/curves@1.2.0: + resolution: {integrity: sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==} + dependencies: + '@noble/hashes': 1.3.2 dev: false /@noble/curves@1.3.0: @@ -31,6 +52,11 @@ packages: '@noble/hashes': 1.3.3 dev: false + /@noble/hashes@1.3.2: + resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==} + engines: {node: '>= 16'} + dev: false + /@noble/hashes@1.3.3: resolution: {integrity: sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==} engines: {node: '>= 16'} @@ -55,6 +81,29 @@ packages: '@scure/base': 1.1.5 dev: false + /@types/node@18.15.13: + resolution: {integrity: sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==} + dev: false + + /@types/ws@8.5.10: + resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==} + dependencies: + '@types/node': 18.15.13 + dev: false + + /@wagmi/chains@1.8.0: + resolution: {integrity: sha512-UXo0GF0Cl0+neKC2KAmVAahv8L/5rACbFRRqkDvHMefzY6Fh7yzJd8F4GaGNNG3w4hj8eUB/E3+dEpaTYDN62w==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + dev: false + + /aes-js@4.0.0-beta.5: + resolution: {integrity: sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==} + dev: false + /available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} @@ -107,6 +156,26 @@ packages: '@scure/bip39': 1.2.2 dev: false + /ethers@6.11.1: + resolution: {integrity: sha512-mxTAE6wqJQAbp5QAe/+o+rXOID7Nw91OZXvgpjDa1r4fAbq2Nu314oEZSbjoRLacuCzs7kUC3clEvkCQowffGg==} + engines: {node: '>=14.0.0'} + dependencies: + '@adraffy/ens-normalize': 1.10.1 + '@noble/curves': 1.2.0 + '@noble/hashes': 1.3.2 + '@types/node': 18.15.13 + aes-js: 4.0.0-beta.5 + tslib: 2.4.0 + ws: 8.5.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: false + + /eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + dev: false + /for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} dependencies: @@ -212,6 +281,10 @@ packages: has-property-descriptors: 1.0.2 dev: false + /tslib@2.4.0: + resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} + dev: false + /util@0.12.5: resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} dependencies: @@ -226,32 +299,33 @@ packages: resolution: {integrity: sha512-WahtszSqILez+83AxGecVroyZsMuuRT+KmQp4Si5P4Rnqbczno1k748PCrZTS1J4UCPmXMG2/Vt+0Bz2zwXkwQ==} engines: {node: '>=14', npm: '>=6.12.0'} dependencies: - web3-types: 1.4.0 + web3-types: 1.5.0 dev: false - /web3-types@1.4.0: - resolution: {integrity: sha512-QnGDNredYqtZ49YD1pIPhsQTJJTOnYPCOnvrUs4/3XzeQLuDM+bAJ8fZ6U2nGEV77h81z2Ins6RE/f40yltvww==} + /web3-types@1.5.0: + resolution: {integrity: sha512-geWuMIeegQ8AedKAO6wO4G4j1gyQ1F/AyKLMw2vud4bsfZayyzWJgCMDZtjYMm5uo2a7i8j1W3/4QFmzlSy5cw==} engines: {node: '>=14', npm: '>=6.12.0'} dev: false - /web3-utils@4.2.0: - resolution: {integrity: sha512-UE7tmqPnC6sD0kpHhZiO9Zu8q7hiBItCQhnmxoMxk8OI91qlBWw6L7w1VNZo7TMBWH1Qe4R5l8h2vaoQCizVyA==} + /web3-utils@4.2.2: + resolution: {integrity: sha512-z+4owWcnoB4EH8yWIL1FBeyqe+sXwaGxUDtVTNPTMf2oB5C+paCToZUdCV5Bi+M543zZEzlzNTabOD+OWNc7NA==} engines: {node: '>=14', npm: '>=6.12.0'} dependencies: ethereum-cryptography: 2.1.3 + eventemitter3: 5.0.1 web3-errors: 1.1.4 - web3-types: 1.4.0 - web3-validator: 2.0.4 + web3-types: 1.5.0 + web3-validator: 2.0.5 dev: false - /web3-validator@2.0.4: - resolution: {integrity: sha512-qRxVePwdW+SByOmTpDZFWHIUAa7PswvxNszrOua6BoGqAhERo5oJZBN+EbWtK/+O+ApNxt5FR3nCPmiZldiOQA==} + /web3-validator@2.0.5: + resolution: {integrity: sha512-2gLOSW8XqEN5pw5jVUm20EB7A8SbQiekpAtiI0JBmCIV0a2rp97v8FgWY5E3UEqnw5WFfEqvcDVW92EyynDTyQ==} engines: {node: '>=14', npm: '>=6.12.0'} dependencies: ethereum-cryptography: 2.1.3 util: 0.12.5 web3-errors: 1.1.4 - web3-types: 1.4.0 + web3-types: 1.5.0 zod: 3.22.4 dev: false @@ -266,6 +340,32 @@ packages: has-tostringtag: 1.0.2 dev: false + /ws@8.16.0: + resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dev: false + + /ws@8.5.0: + resolution: {integrity: sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dev: false + /zod@3.22.4: resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} dev: false diff --git a/packages/networks/evm-chains/src/assets/Coin.ts b/packages/networks/evm-chains/src/assets/Coin.ts index ce9952e..878d554 100644 --- a/packages/networks/evm-chains/src/assets/Coin.ts +++ b/packages/networks/evm-chains/src/assets/Coin.ts @@ -1,42 +1,93 @@ -import { TransactionSigner } from '../services/TransactionSigner.ts' -import type { CoinInterface, TransactionSignerInterface } from '@multiplechain/types' +import { Provider } from '../services/Provider.ts' +import { hexToNumber, numberToHex } from '@multiplechain/utils' +import { CoinTransactionSigner } from '../services/TransactionSigner.ts' +import type { TransactionData } from '../services/TransactionSigner.ts' +import { ErrorTypeEnum, type CoinInterface } from '@multiplechain/types' export class Coin implements CoinInterface { /** - * @returns Coin name + * Blockchain network provider + */ + provider: Provider + + /** + * @param {Provider} provider network provider + */ + constructor(provider?: Provider) { + this.provider = provider ?? Provider.instance + } + + /** + * @returns {string} Coin name */ getName(): string { - return 'example' + return ( + this.provider.network.nativeCurrency.name ?? this.provider.network.nativeCurrency.symbol + ) } /** - * @returns Coin symbol + * @returns {string} Coin symbol */ getSymbol(): string { - return 'example' + return this.provider.network.nativeCurrency.symbol } /** - * @returns Decimal value of the coin + * @returns {number} Decimal value of the coin */ getDecimals(): number { - return 18 + return this.provider.network.nativeCurrency.decimals } /** - * @param owner Wallet address - * @returns Wallet balance as currency of TOKEN or COIN assets + * @param {string} owner Wallet address + * @returns {Promise} Wallet balance as currency of COIN */ - getBalance(owner: string): number { - return 0 + async getBalance(owner: string): Promise { + const balance = await this.provider.ethers.getBalance(owner) + return hexToNumber(balance.toString(), this.getDecimals()) } /** - * @param sender Sender wallet address - * @param receiver Receiver wallet address - * @param amount Amount of assets that will be transferred + * @param {string} sender Sender wallet address + * @param {string} receiver Receiver wallet address + * @param {number} amount Amount of assets that will be transferred + * @returns {Promise} Transaction signer */ - transfer(sender: string, receiver: string, amount: number): TransactionSignerInterface { - return new TransactionSigner('example') + async transfer( + sender: string, + receiver: string, + amount: number + ): Promise { + if (amount < 0) { + throw new Error(ErrorTypeEnum.INVALID_AMOUNT) + } + + if (amount > (await this.getBalance(sender))) { + throw new Error(ErrorTypeEnum.INSUFFICIENT_BALANCE) + } + + const hexAmount = numberToHex(amount, this.getDecimals()) + + const txData: TransactionData = { + data: '0x', + to: receiver, + from: sender, + value: hexAmount, + chainId: this.provider.network.id + } + + const [gasPrice, nonce, gasLimit] = await Promise.all([ + this.provider.ethers.getGasPrice(), + this.provider.ethers.getNonce(sender), + this.provider.ethers.getEstimateGas(txData) + ]) + + txData.nonce = nonce + txData.gasPrice = gasPrice + txData.gasLimit = gasLimit + + return new CoinTransactionSigner(txData) } } diff --git a/packages/networks/evm-chains/src/assets/Contract.ts b/packages/networks/evm-chains/src/assets/Contract.ts index fd123ed..2b7de08 100644 --- a/packages/networks/evm-chains/src/assets/Contract.ts +++ b/packages/networks/evm-chains/src/assets/Contract.ts @@ -1,4 +1,7 @@ +import { Provider } from '../services/Provider.ts' +import type { Ethers } from '../services/Ethers.ts' import type { ContractInterface } from '@multiplechain/types' +import type { Contract as EthersContract, InterfaceAbi } from 'ethers' export class Contract implements ContractInterface { /** @@ -9,39 +12,68 @@ export class Contract implements ContractInterface { /** * Contract ABI */ - abi: object[] + ABI: InterfaceAbi /** - * @param address Contract address - * @param abi Contract ABI + * Ethers contract */ - constructor(address: string, abi: object[] = []) { - this.abi = abi + ethersContract: EthersContract + + /** + * Blockchain network provider + */ + provider: Provider + + /** + * Ethers service + */ + ethers: Ethers + + /** + * @param {string} address Contract address + * @param {Provider} provider Blockchain network provider + * @param {InterfaceAbi} ABI Contract ABI + */ + constructor(address: string, provider?: Provider, ABI?: InterfaceAbi) { + this.ABI = ABI ?? [] this.address = address + this.provider = provider ?? Provider.instance + this.ethers = this.provider.ethers + this.ethersContract = this.ethers.contract(address, this.ABI, this.ethers.jsonRpc) } /** - * @returns Contract address + * @returns {string} Contract address */ getAddress(): string { return this.address } /** - * @param method Method name - * @param args Method parameters - * @returns Method result + * @param {string} method Method name + * @param {any[]} args Method parameters + * @returns {Promise} Method result + */ + async callMethod(method: string, ...args: any[]): Promise { + return this.ethersContract[method](...args) // eslint-disable-line + } + + /** + * @param {string} method Method name + * @param {any[]} args Sender wallet address + * @returns {Promise} Encoded method data */ - callMethod(method: string, ...args: any[]): any { - return {} + async getMethodData(method: string, ...args: any[]): Promise { + return this.ethersContract.interface.encodeFunctionData(method, args) } /** - * @param method Method name - * @param args Method parameters - * @returns Method data + * @param {string} method Method name + * @param {string} from Sender wallet address + * @param {any[]} args Method parameters + * @returns {Promise} Gas limit */ - getMethodData(method: string, ...args: any[]): any { - return {} + async getMethodEstimateGas(method: string, from: string, ...args: any[]): Promise { + return Number(await this.ethersContract[method].estimateGas(...args, { from })) // eslint-disable-line } } diff --git a/packages/networks/evm-chains/src/assets/NFT.ts b/packages/networks/evm-chains/src/assets/NFT.ts index 155c666..35e3cfd 100644 --- a/packages/networks/evm-chains/src/assets/NFT.ts +++ b/packages/networks/evm-chains/src/assets/NFT.ts @@ -1,67 +1,174 @@ import { Contract } from './Contract.ts' -import { TransactionSigner } from '../services/TransactionSigner.ts' -import type { NftInterface, TransactionSignerInterface } from '@multiplechain/types' +import type { InterfaceAbi } from 'ethers' +import ERC721 from '../../resources/erc721.json' +import type { Provider } from '../services/Provider.ts' +import { NftTransactionSigner } from '../services/TransactionSigner.ts' +import { ErrorTypeEnum, type NftInterface } from '@multiplechain/types' export class NFT extends Contract implements NftInterface { /** - * @returns Contract name + * @param {string} address Contract address + * @param {Provider} provider Blockchain network provider + * @param {InterfaceAbi} ABI Contract ABI */ - getName(): string { - return 'example' + constructor(address: string, provider?: Provider, ABI?: InterfaceAbi) { + super(address, provider, ABI ?? ERC721) } /** - * @returns Contract symbol + * @returns {Promise} NFT name */ - getSymbol(): string { - return 'example' + async getName(): Promise { + return await this.callMethod('name') } /** - * @param owner Wallet address - * @returns Wallet balance as currency of TOKEN or COIN assets + * @returns {Promise} NFT symbol */ - getBalance(owner: string): number { - return 0 + async getSymbol(): Promise { + return await this.callMethod('symbol') } /** - * @returns Contract address + * @param {string} owner Wallet address + * @returns {Promise} Wallet balance as currency of NFT */ - getAddress(): string { - return 'example' + async getBalance(owner: string): Promise { + return Number(await this.callMethod('balanceOf', owner)) } /** - * @returns NFT ID + * @param {number | string} nftId NFT ID + * @returns {Promise} Wallet address of the owner of the NFT */ - getNftId(): number { - return 0 + async getOwner(nftId: number | string): Promise { + return await this.callMethod('ownerOf', nftId) } /** - * @param sender Sender address - * @param receiver Receiver address - * @param nftId NFT ID - * @returns Transaction signer + * @param {number | string} nftId NFT ID + * @returns {Promise} URI of the NFT */ - transfer(sender: string, receiver: string, nftId: number): TransactionSignerInterface { - return new TransactionSigner('example') + async getTokenURI(nftId: number | string): Promise { + return await this.callMethod('tokenURI', nftId) } /** - * @param nftId NFT ID - * @returns NFT owner wallet address + * @param {number | string} nftId ID of the NFT that will be transferred + * @returns {Promise} Wallet address of the approved spender */ - getOwner(nftId: number): string { - return 'example' + async getApproved(nftId: number | string): Promise { + return await this.callMethod('getApproved', nftId) } /** - * @param nftId NFT ID - * @returns NFT URI + * @param {string} sender Sender address + * @param {string} receiver Receiver address + * @param {number | string} nftId NFT ID + * @returns {Promise} Transaction signer */ - getTokenURI(nftId: number): string { - return 'example' + async transfer( + sender: string, + receiver: string, + nftId: number | string + ): Promise { + return await this.transferFrom(sender, sender, receiver, nftId) + } + + /** + * @param {string} spender Spender address + * @param {string} owner Owner address + * @param {string} receiver Receiver address + * @param {number | string} nftId NFT ID + * @returns {Promise} Transaction signer + */ + async transferFrom( + spender: string, + owner: string, + receiver: string, + nftId: number | string + ): Promise { + // Check if tokens exist + const balance = await this.getBalance(owner) + + if (balance <= 0) { + throw new Error(ErrorTypeEnum.INSUFFICIENT_BALANCE) + } + + // Check ownership + const originalOwner = await this.getOwner(nftId) + if (originalOwner !== owner) { + throw new Error(ErrorTypeEnum.UNAUTHORIZED_ADDRESS) + } + + // check if spender different from owner + if (spender !== owner) { + const approved = await this.getApproved(nftId) + if (approved !== spender) { + throw new Error(ErrorTypeEnum.UNAUTHORIZED_ADDRESS) + } + } + + const [gasPrice, nonce, data, gasLimit] = await Promise.all([ + this.provider.ethers.getGasPrice(), + this.provider.ethers.getNonce(spender), + this.getMethodData('transferFrom', owner, receiver, nftId), + this.getMethodEstimateGas('transferFrom', spender, owner, receiver, nftId) + ]) + + return new NftTransactionSigner({ + data, + nonce, + gasPrice, + gasLimit, + value: '0x0', + from: spender, + to: this.getAddress(), + chainId: this.provider.network.id + }) + } + + /** + * Gives permission to the spender to spend owner's tokens + * @param {string} owner Address of owner of the tokens that will be used + * @param {string} spender Address of the spender that will use the tokens of owner + * @param {number | string} nftId ID of the NFT that will be transferred + * @returns {Promise} Transaction signer + */ + async approve( + owner: string, + spender: string, + nftId: number | string + ): Promise { + // Check if tokens exist + const balance = await this.getBalance(owner) + + if (balance <= 0) { + throw new Error(ErrorTypeEnum.INSUFFICIENT_BALANCE) + } + + // Check ownership + const originalOwner = await this.getOwner(nftId) + if (originalOwner !== owner) { + throw new Error(ErrorTypeEnum.UNAUTHORIZED_ADDRESS) + } + + const [gasPrice, nonce, data, gasLimit] = await Promise.all([ + this.provider.ethers.getGasPrice(), + this.provider.ethers.getNonce(owner), + this.getMethodData('approve', spender, nftId), + this.getMethodEstimateGas('approve', owner, spender, nftId) + ]) + + return new NftTransactionSigner({ + data, + nonce, + gasPrice, + gasLimit, + value: '0x0', + from: owner, + to: this.getAddress(), + chainId: this.provider.network.id + }) } } diff --git a/packages/networks/evm-chains/src/assets/Token.ts b/packages/networks/evm-chains/src/assets/Token.ts index 5853f5f..985fbbd 100644 --- a/packages/networks/evm-chains/src/assets/Token.ts +++ b/packages/networks/evm-chains/src/assets/Token.ts @@ -1,77 +1,210 @@ import { Contract } from './Contract.ts' -import { TransactionSigner } from '../services/TransactionSigner.ts' -import type { TokenInterface, TransactionSignerInterface } from '@multiplechain/types' +import type { InterfaceAbi } from 'ethers' +import ERC20 from '../../resources/erc20.json' +import type { Provider } from '../services/Provider.ts' +import { hexToNumber, numberToHex } from '@multiplechain/utils' +import { TokenTransactionSigner } from '../services/TransactionSigner.ts' +import { ErrorTypeEnum, type TokenInterface } from '@multiplechain/types' export class Token extends Contract implements TokenInterface { /** - * @returns Token name + * @param {string} address Contract address + * @param {Provider} provider Blockchain network provider + * @param {InterfaceAbi} ABI Contract ABI */ - getName(): string { - return 'example' + constructor(address: string, provider?: Provider, ABI?: InterfaceAbi) { + super(address, provider, ABI ?? ERC20) } /** - * @returns Token symbol + * @returns {Promise} Token name */ - getSymbol(): string { - return 'example' + async getName(): Promise { + return await this.callMethod('name') } /** - * @returns Contract address + * @returns {Promise} Token symbol */ - getAddress(): string { - return 'example' + async getSymbol(): Promise { + return await this.callMethod('symbol') } /** - * @returns Decimal value of the coin + * @returns {Promise} Decimal value of the token */ - getDecimals(): number { - return 18 + async getDecimals(): Promise { + return Number(await this.callMethod('decimals')) } /** - * @param owner Wallet address - * @returns Wallet balance as currency of TOKEN or COIN assets + * @param {string} owner Wallet address + * @returns {Promise} Wallet balance as currency of TOKEN */ - getBalance(owner: string): number { - return 0 + async getBalance(owner: string): Promise { + const [decimals, balance] = await Promise.all([ + this.getDecimals(), + this.callMethod('balanceOf', owner) + ]) + return hexToNumber(balance as string, decimals) } /** - * @returns Total supply of the token + * @returns {Promise} Total supply of the token */ - getTotalSupply(): number { - return 0 + async getTotalSupply(): Promise { + const [decimals, totalSupply] = await Promise.all([ + this.getDecimals(), + this.callMethod('totalSupply') + ]) + return hexToNumber(totalSupply as string, decimals) + } + + /** + * @param {string} owner Address of owner of the tokens that is being used + * @param {string} spender Address of the spender that is using the tokens of owner + * @returns {Promise} Amount of tokens that the spender is allowed to spend + */ + async getAllowance(owner: string, spender: string): Promise { + const [decimals, allowance] = await Promise.all([ + this.getDecimals(), + await this.callMethod('allowance', owner, spender) + ]) + return hexToNumber(allowance as string, decimals) } /** * transfer() method is the main method for processing transfers for fungible assets (TOKEN, COIN) - * @param sender Sender wallet address - * @param receiver Receiver wallet address - * @param amount Amount of assets that will be transferred + * @param {string} sender Sender wallet address + * @param {string} receiver Receiver wallet address + * @param {number} amount Amount of assets that will be transferred + * @returns {Promise} Transaction signer */ - transfer(sender: string, receiver: string, amount: number): TransactionSignerInterface { - return new TransactionSigner('example') + async transfer( + sender: string, + receiver: string, + amount: number + ): Promise { + if (amount <= 0) { + throw new Error(ErrorTypeEnum.INVALID_AMOUNT) + } + + const balance = await this.getBalance(sender) + + if (amount > balance) { + throw new Error(ErrorTypeEnum.INSUFFICIENT_BALANCE) + } + + const hexAmount = numberToHex(amount, await this.getDecimals()) + const [gasPrice, nonce, data, gasLimit] = await Promise.all([ + this.provider.ethers.getGasPrice(), + this.provider.ethers.getNonce(sender), + this.getMethodData('transfer', receiver, hexAmount), + this.getMethodEstimateGas('transfer', sender, receiver, hexAmount) + ]) + + return new TokenTransactionSigner({ + data, + nonce, + gasPrice, + gasLimit, + value: '0x0', + from: sender, + to: this.getAddress(), + chainId: this.provider.network.id + }) } /** - * Gives permission to the spender to spend owner's tokens - * @param owner Address of owner of the tokens that will be used - * @param spender Address of the spender that will use the tokens of owner - * @param amount Amount of the tokens that will be used + * @param {string} spender Address of the spender of transaction + * @param {string} owner Sender wallet address + * @param {string} receiver Receiver wallet address + * @param {number} amount Amount of tokens that will be transferred + * @returns {Promise} Transaction signer */ - approve(owner: string, spender: string, amount: number): TransactionSignerInterface { - return new TransactionSigner('example') + async transferFrom( + spender: string, + owner: string, + receiver: string, + amount: number + ): Promise { + if (amount < 0) { + throw new Error(ErrorTypeEnum.INVALID_AMOUNT) + } + + const balance = await this.getBalance(owner) + + if (amount > balance) { + throw new Error(ErrorTypeEnum.INSUFFICIENT_BALANCE) + } + + const allowance = await this.getAllowance(owner, spender) + + if (allowance === 0) { + throw new Error(ErrorTypeEnum.UNAUTHORIZED_ADDRESS) + } + + if (amount > allowance) { + throw new Error(ErrorTypeEnum.INVALID_AMOUNT) + } + + const hexAmount = numberToHex(amount, await this.getDecimals()) + + const [gasPrice, nonce, data, gasLimit] = await Promise.all([ + this.provider.ethers.getGasPrice(), + this.provider.ethers.getNonce(spender), + this.getMethodData('transferFrom', owner, receiver, hexAmount), + this.getMethodEstimateGas('transferFrom', spender, owner, receiver, hexAmount) + ]) + + return new TokenTransactionSigner({ + data, + nonce, + gasPrice, + gasLimit, + value: '0x0', + from: spender, + to: this.getAddress(), + chainId: this.provider.network.id + }) } /** - * @param owner Address of owner of the tokens that is being used - * @param spender Address of the spender that is using the tokens of owner - * @returns Amount of the tokens that is being used by spender + * Gives permission to the spender to spend owner's tokens + * @param {string} owner Address of owner of the tokens that will be used + * @param {string} spender Address of the spender that will use the tokens of owner + * @param {number} amount Amount of the tokens that will be used + * @returns {Promise} Transaction signer */ - allowance(owner: string, spender: string): number { - return 0 + async approve(owner: string, spender: string, amount: number): Promise { + if (amount < 0) { + throw new Error(ErrorTypeEnum.INVALID_AMOUNT) + } + + const balance = await this.getBalance(owner) + + if (amount > balance) { + throw new Error(ErrorTypeEnum.INSUFFICIENT_BALANCE) + } + + const hexAmount = numberToHex(amount, await this.getDecimals()) + + const [gasPrice, nonce, data, gasLimit] = await Promise.all([ + this.provider.ethers.getGasPrice(), + this.provider.ethers.getNonce(owner), + this.getMethodData('approve', spender, hexAmount), + this.getMethodEstimateGas('approve', owner, spender, hexAmount) + ]) + + return new TokenTransactionSigner({ + data, + nonce, + gasPrice, + gasLimit, + value: '0x0', + from: owner, + to: this.getAddress(), + chainId: this.provider.network.id + }) } } diff --git a/packages/networks/evm-chains/src/assets/index.ts b/packages/networks/evm-chains/src/assets/index.ts new file mode 100644 index 0000000..7ea522f --- /dev/null +++ b/packages/networks/evm-chains/src/assets/index.ts @@ -0,0 +1,4 @@ +export * from './NFT.ts' +export * from './Coin.ts' +export * from './Token.ts' +export * from './Contract.ts' diff --git a/packages/networks/evm-chains/src/index.ts b/packages/networks/evm-chains/src/index.ts index e69de29..b06e38c 100644 --- a/packages/networks/evm-chains/src/index.ts +++ b/packages/networks/evm-chains/src/index.ts @@ -0,0 +1,9 @@ +export * from './services/Provider.ts' + +export * as assets from './assets/index.ts' +export * as models from './models/index.ts' +export * as services from './services/index.ts' +export { default as networks } from './services/Networks.ts' + +export * as utils from '@multiplechain/utils' +export * as types from '@multiplechain/types' diff --git a/packages/networks/evm-chains/src/models/CoinTransaction.ts b/packages/networks/evm-chains/src/models/CoinTransaction.ts index e8e2fa8..c1c5bca 100644 --- a/packages/networks/evm-chains/src/models/CoinTransaction.ts +++ b/packages/networks/evm-chains/src/models/CoinTransaction.ts @@ -1,32 +1,64 @@ import { Transaction } from './Transaction.ts' +import { hexToNumber } from '@multiplechain/utils' import { TransactionStatusEnum } from '@multiplechain/types' -import type { AssetDirectionEnum, CoinTransactionInterface } from '@multiplechain/types' +import { AssetDirectionEnum, type CoinTransactionInterface } from '@multiplechain/types' export class CoinTransaction extends Transaction implements CoinTransactionInterface { /** - * @returns Wallet address of the sender of transaction + * @returns {Promise} Wallet address of the receiver of transaction */ - getReceiver(): string { - return 'example' + async getReceiver(): Promise { + const data = await this.getData() + return data?.response.to ?? '' } /** - * @returns Transfer amount of the transaction (coin) + * @returns {Promise} Wallet address of the sender of transaction */ - getAmount(): number { - return 0 + async getSender(): Promise { + return await this.getSigner() } /** - * @param direction - Direction of the transaction (asset) - * @param address - Wallet address of the receiver or sender of the transaction, dependant on direction - * @param amount Amount of assets that will be transferred + * @returns {Promise} Amount of coin that will be transferred */ - verifyTransfer( + async getAmount(): Promise { + const data = await this.getData() + const { decimals } = this.provider.network.nativeCurrency + return hexToNumber((data?.response.value ?? 0).toString(), decimals) + } + + /** + * @param {AssetDirectionEnum} direction - Direction of the transaction (asset) + * @param {string} address - Wallet address of the receiver or sender of the transaction, dependant on direction + * @param {number} amount Amount of assets that will be transferred + * @returns {Promise} Status of the transaction + */ + async verifyTransfer( direction: AssetDirectionEnum, address: string, amount: number - ): TransactionStatusEnum { - return TransactionStatusEnum.PENDING + ): Promise { + const status = await this.getStatus() + + if (status === TransactionStatusEnum.PENDING) { + return TransactionStatusEnum.PENDING + } + + if ((await this.getAmount()) !== amount) { + return TransactionStatusEnum.FAILED + } + + if (direction === AssetDirectionEnum.INCOMING) { + if ((await this.getReceiver()).toLowerCase() !== address.toLowerCase()) { + return TransactionStatusEnum.FAILED + } + } else { + if ((await this.getSender()).toLowerCase() !== address.toLowerCase()) { + return TransactionStatusEnum.FAILED + } + } + + return TransactionStatusEnum.CONFIRMED } } diff --git a/packages/networks/evm-chains/src/models/ContractTransaction.ts b/packages/networks/evm-chains/src/models/ContractTransaction.ts index cb6f829..6878378 100644 --- a/packages/networks/evm-chains/src/models/ContractTransaction.ts +++ b/packages/networks/evm-chains/src/models/ContractTransaction.ts @@ -1,8 +1,51 @@ import { Transaction } from './Transaction.ts' import type { ContractTransactionInterface } from '@multiplechain/types' +import { + Interface, + type InterfaceAbi, + type TransactionResponse, + type TransactionDescription +} from 'ethers' +import type { Provider } from '../services/Provider.ts' export class ContractTransaction extends Transaction implements ContractTransactionInterface { - getAddress(): string { - return 'example' + /** + * @type {InterfaceAbi} + */ + ABI: InterfaceAbi + + /** + * @param {string} id Transaction id + * @param {Provider} provider Blockchain network provider + * @param {InterfaceAbi} ABI Contract ABI + */ + constructor(id: string, provider?: Provider, ABI?: InterfaceAbi) { + super(id, provider) + this.ABI = ABI ?? [] + } + + /** + * @returns {Promise} Contract address of the transaction + */ + async getAddress(): Promise { + const data = await this.getData() + return data?.response.to ?? '' + } + + /** + * @param {TransactionResponse} response Transaction response + * @returns {Promise} Decoded transaction data + */ + async decodeData(response?: TransactionResponse): Promise { + if (response === undefined) { + const data = await this.getData() + if (data === null) return null + response = data.response + } + + return new Interface(this.ABI).parseTransaction({ + data: response.data ?? '', + value: response.value ?? 0 + }) } } diff --git a/packages/networks/evm-chains/src/models/NftTransaction.ts b/packages/networks/evm-chains/src/models/NftTransaction.ts index 92dea08..de09fc7 100644 --- a/packages/networks/evm-chains/src/models/NftTransaction.ts +++ b/packages/networks/evm-chains/src/models/NftTransaction.ts @@ -1,47 +1,93 @@ +import type { InterfaceAbi } from 'ethers' +import ERC721 from '../../resources/erc721.json' +import type { Provider } from '../services/Provider.ts' import { ContractTransaction } from './ContractTransaction.ts' -import { TransactionStatusEnum } from '@multiplechain/types' -import type { NftTransactionInterface, AssetDirectionEnum } from '@multiplechain/types' +import type { NftTransactionInterface } from '@multiplechain/types' +import { TransactionStatusEnum, AssetDirectionEnum } from '@multiplechain/types' export class NftTransaction extends ContractTransaction implements NftTransactionInterface { /** - * @returns Wallet address of the sender of transaction + * @param {string} id Transaction id + * @param {Provider} provider Blockchain network provider + * @param {InterfaceAbi} ABI Contract ABI */ - getReceiver(): string { - return 'example' + constructor(id: string, provider?: Provider, ABI?: InterfaceAbi) { + super(id, provider, ABI ?? (ERC721 as InterfaceAbi)) } /** - * @returns Transfer amount of the transaction (token) + * @returns {Promise} Receiver wallet address */ - getAmount(): number { - return 0 + async getReceiver(): Promise { + const decoded = await this.decodeData() + + if (decoded === null) { + return '' + } + + if (decoded.name === 'transferFrom') { + return decoded.args[1] + } + + return decoded.args[0] } /** - * @returns ID of the NFT + * @returns {Promise} Sender wallet address */ - getNftId(): number { - return 0 + async getSender(): Promise { + const decoded = await this.decodeData() + + if (decoded === null) { + return '' + } + + if (decoded.name === 'transferFrom') { + return decoded.args[0] + } + + return await this.getSigner() } /** - * @returns Smart contract address of the transaction + * @returns {Promise} NFT ID */ - getAddress(): string { - return 'example' + async getNftId(): Promise { + return Number((await this.decodeData())?.args[2] ?? 0) } /** - * @param direction - Direction of the transaction (nft) - * @param address - Wallet address of the receiver or sender of the transaction, dependant on direction - * @param nftId ID of the NFT that will be transferred + * @param {AssetDirectionEnum} direction - Direction of the transaction (nft) + * @param {string} address - Wallet address of the receiver or sender of the transaction, dependant on direction + * @param {number | string} nftId ID of the NFT that will be transferred * @override verifyTransfer() in AssetTransactionInterface + * @returns {Promise} Status of the transaction */ - verifyTransfer( + async verifyTransfer( direction: AssetDirectionEnum, address: string, - nftId: number - ): TransactionStatusEnum { - return TransactionStatusEnum.PENDING + nftId: number | string + ): Promise { + const status = await this.getStatus() + + if (status === TransactionStatusEnum.PENDING) { + return TransactionStatusEnum.PENDING + } + + if ((await this.getNftId()) !== nftId) { + return TransactionStatusEnum.FAILED + } + + if (direction === AssetDirectionEnum.INCOMING) { + if ((await this.getReceiver()).toLowerCase() !== address.toLowerCase()) { + return TransactionStatusEnum.FAILED + } + } else { + if ((await this.getSender()).toLowerCase() !== address.toLowerCase()) { + return TransactionStatusEnum.FAILED + } + } + + return TransactionStatusEnum.CONFIRMED } } diff --git a/packages/networks/evm-chains/src/models/TokenTransaction.ts b/packages/networks/evm-chains/src/models/TokenTransaction.ts index c87cd2b..da166cf 100644 --- a/packages/networks/evm-chains/src/models/TokenTransaction.ts +++ b/packages/networks/evm-chains/src/models/TokenTransaction.ts @@ -1,52 +1,104 @@ -import { ContractTransaction } from './ContractTransaction.ts' +import { Token } from '../assets/Token.ts' +import type { InterfaceAbi } from 'ethers' +import ERC20 from '../../resources/erc20.json' +import { hexToNumber } from '@multiplechain/utils' +import type { Provider } from '../services/Provider.ts' import { TransactionStatusEnum } from '@multiplechain/types' -import type { AssetDirectionEnum, TokenTransactionInterface } from '@multiplechain/types' +import { ContractTransaction } from './ContractTransaction.ts' +import { AssetDirectionEnum, type TokenTransactionInterface } from '@multiplechain/types' export class TokenTransaction extends ContractTransaction implements TokenTransactionInterface { /** - * @returns Wallet address of the sender of transaction + * @param {string} id Transaction id + * @param {Provider} provider Blockchain network provider + * @param {InterfaceAbi} ABI Contract ABI */ - getReceiver(): string { - return 'example' + constructor(id: string, provider?: Provider, ABI?: InterfaceAbi) { + super(id, provider, ABI ?? (ERC20 as InterfaceAbi)) } /** - * @returns Transfer amount of the transaction (token) + * @return {Promise} Receiver wallet address */ - getAmount(): number { - return 0 + async getReceiver(): Promise { + const decoded = await this.decodeData() + + if (decoded === null) { + return '' + } + + if (decoded.name === 'transferFrom') { + return decoded.args[1] + } + + return decoded.args[0] } /** - * @returns Token address of the transaction + * @returns Wallet address of the sender of transaction */ - getAddress(): string { - return 'example' + async getSender(): Promise { + const decoded = await this.decodeData() + + if (decoded === null) { + return '' + } + + if (decoded.name === 'transferFrom') { + return decoded.args[0] + } + + return await this.getSigner() } /** - * @param direction - Direction of the transaction (token) - * @param address - Wallet address of the owner or spender of the transaction, dependant on direction - * @param amount Amount of tokens that will be approved + * @returns {Promise} Amount of tokens that will be transferred */ - verifyTransfer( - direction: AssetDirectionEnum, - address: string, - amount: number - ): TransactionStatusEnum { - return TransactionStatusEnum.PENDING + async getAmount(): Promise { + const token = new Token(await this.getAddress()) + const decoded = await this.decodeData() + if (decoded === null) { + return 0 + } + + if (decoded.name === 'transferFrom') { + return hexToNumber((decoded.args[2] as bigint).toString(), await token.getDecimals()) + } + + return hexToNumber((decoded.args[1] as bigint).toString(), await token.getDecimals()) } /** - * @param direction - Direction of the transaction (token) - * @param address - Wallet address of the owner or spender of the transaction, dependant on direction - * @param amount Amount of tokens that will be approved + * @param {AssetDirectionEnum} direction - Direction of the transaction (token) + * @param {string} address - Wallet address of the owner or spender of the transaction, dependant on direction + * @param {number} amount Amount of tokens that will be approved + * @returns {Promise} Status of the transaction */ - verifyApprove( + async verifyTransfer( direction: AssetDirectionEnum, address: string, amount: number - ): TransactionStatusEnum { - return TransactionStatusEnum.PENDING + ): Promise { + const status = await this.getStatus() + + if (status === TransactionStatusEnum.PENDING) { + return TransactionStatusEnum.PENDING + } + + if ((await this.getAmount()) !== amount) { + return TransactionStatusEnum.FAILED + } + + if (direction === AssetDirectionEnum.INCOMING) { + if ((await this.getReceiver()).toLowerCase() !== address.toLowerCase()) { + return TransactionStatusEnum.FAILED + } + } else { + if ((await this.getSender()).toLowerCase() !== address.toLowerCase()) { + return TransactionStatusEnum.FAILED + } + } + + return TransactionStatusEnum.CONFIRMED } } diff --git a/packages/networks/evm-chains/src/models/Transaction.ts b/packages/networks/evm-chains/src/models/Transaction.ts index c45b61f..10223e9 100644 --- a/packages/networks/evm-chains/src/models/Transaction.ts +++ b/packages/networks/evm-chains/src/models/Transaction.ts @@ -1,6 +1,14 @@ -import type { TransactionInterface } from '@multiplechain/types' -import { TransactionStatusEnum } from '@multiplechain/types' import { Provider } from '../services/Provider.ts' +import { ErrorTypeEnum, TransactionStatusEnum } from '@multiplechain/types' +import type { TransactionInterface } from '@multiplechain/types' +import type { TransactionReceipt, TransactionResponse } from 'ethers' +import type { Ethers } from '../services/Ethers.ts' +import { hexToNumber } from '@multiplechain/utils' + +interface TransactionData { + response: TransactionResponse + receipt: TransactionReceipt | null +} export class Transaction implements TransactionInterface { /** @@ -9,87 +17,157 @@ export class Transaction implements TransactionInterface { id: string /** - * Provider instance + * Blockchain network provider */ - private readonly provider: Provider + provider: Provider - constructor(id: string) { + /** + * Ethers service + */ + ethers: Ethers + + /** + * Transaction data after completed + */ + data: TransactionData + + /** + * @param {string} id Transaction id + * @param {Provider} provider Blockchain network provider + */ + constructor(id: string, provider?: Provider) { this.id = id - this.provider = Provider.instance + this.provider = provider ?? Provider.instance + this.ethers = this.provider.ethers } /** - * @returns Raw transaction data that is taken by blockchain network via RPC. + * @returns {Promise} Transaction data */ - async getData(): Promise { + async getData(): Promise { + if (this.data?.response !== undefined && this.data?.receipt !== null) { + return this.data + } try { - const data = (await this.provider.ethers.getTransaction(this.id)) ?? {} - const receipt = (await this.provider.ethers.getTransactionReceipt(this.id)) ?? {} - const result: object = { ...data, ...receipt } - return Object.keys(result).length !== 0 ? result : null + const response = await this.ethers.getTransaction(this.id) + if (response === null) { + return null + } + const receipt = await this.ethers.getTransactionReceipt(this.id) + return (this.data = { response, receipt }) } catch (error) { - const e = error as Error - if (String(e.message).includes('timeout')) { - throw new Error('rpc-timeout') + if (error instanceof Error && String(error.message).includes('timeout')) { + throw new Error(ErrorTypeEnum.RPC_TIMEOUT) } - throw new Error('data-request-failed') + throw new Error(ErrorTypeEnum.RPC_REQUEST_ERROR) } } /** - * @returns Transaction id from the blockchain network - * this can be different names like txid, hash, signature etc. + * @param {number} ms - Milliseconds to wait for the transaction to be confirmed. Default is 4000ms + * @returns {Promise} Status of the transaction + */ + async wait(ms: number = 4000): Promise { + return await new Promise((resolve, reject) => { + const check = async (): Promise => { + try { + const status = await this.getStatus() + if (status === TransactionStatusEnum.CONFIRMED) { + resolve(TransactionStatusEnum.CONFIRMED) + return + } else if (status === TransactionStatusEnum.FAILED) { + reject(TransactionStatusEnum.FAILED) + return + } + setTimeout(check, ms) + } catch (error) { + reject(TransactionStatusEnum.FAILED) + } + } + void check() + }) + } + + /** + * @returns {string} Transaction id from the blockchain network */ getId(): string { return this.id } /** - * @returns Blockchain explorer URL of the transaction. Dependant on network. + * @returns {string} URL of the transaction on the blockchain explorer */ getUrl(): string { - return 'example' + let explorerUrl = this.provider.network.explorerUrl + explorerUrl += explorerUrl.endsWith('/') ? '' : '/' + explorerUrl += 'tx/' + this.id + return explorerUrl } /** - * @returns Wallet address of the sender of transaction + * @returns {Promise} Signer wallet address of the transaction */ - getSender(): string { - return 'example' + async getSigner(): Promise { + const data = await this.getData() + return data?.response.from ?? '' } /** - * @returns Transaction fee as native coin amount + * @returns {Promise} Fee of the transaction */ - getFee(): number { - return 0 + async getFee(): Promise { + const data = await this.getData() + if (data?.response?.gasPrice === undefined || data?.receipt?.gasUsed === undefined) { + return 0 + } + return hexToNumber( + (data?.response.gasPrice * data?.receipt.gasUsed).toString(), + this.provider.network.nativeCurrency.decimals + ) } /** - * @returns Block ID of the transaction + * @returns {Promise} Block number that transaction */ - getBlockNumber(): number { - return 0 + async getBlockNumber(): Promise { + const data = await this.getData() + return data?.response.blockNumber ?? 0 } /** - * @returns UNIX timestamp of the date that block is added to blockchain + * @returns {Promise} Timestamp of the block that transaction */ - getBlockTimestamp(): number { - return 0 + async getBlockTimestamp(): Promise { + const blockNumber = await this.getBlockNumber() + const block = await this.ethers.getBlock(blockNumber) + return block?.timestamp ?? 0 } /** - * @returns Confirmation count of the block that transaction is included + * @returns {Promise} Confirmation count of the block that transaction */ - getBlockConfirmationCount(): number { - return 0 + async getBlockConfirmationCount(): Promise { + const blockNumber = await this.getBlockNumber() + const blockCount = await this.ethers.getBlockNumber() + const confirmations = blockCount - blockNumber + return confirmations < 0 ? 0 : confirmations } /** - * @returns Status of the transaction + * @returns {Promise} Status of the transaction */ - getStatus(): TransactionStatusEnum { - return TransactionStatusEnum.CONFIRMED + async getStatus(): Promise { + const data = await this.getData() + if (data === null) { + return TransactionStatusEnum.PENDING + } else if (data.response.blockNumber !== null && data.receipt !== null) { + if (data.receipt.status === 1) { + return TransactionStatusEnum.CONFIRMED + } else { + return TransactionStatusEnum.FAILED + } + } + return TransactionStatusEnum.PENDING } } diff --git a/packages/networks/evm-chains/src/models/index.ts b/packages/networks/evm-chains/src/models/index.ts new file mode 100644 index 0000000..cc0a27c --- /dev/null +++ b/packages/networks/evm-chains/src/models/index.ts @@ -0,0 +1,5 @@ +export * from './Transaction.ts' +export * from './NftTransaction.ts' +export * from './CoinTransaction.ts' +export * from './TokenTransaction.ts' +export * from './ContractTransaction.ts' diff --git a/packages/networks/evm-chains/src/services/Ethers.ts b/packages/networks/evm-chains/src/services/Ethers.ts index 2eb2c49..aa3b97c 100644 --- a/packages/networks/evm-chains/src/services/Ethers.ts +++ b/packages/networks/evm-chains/src/services/Ethers.ts @@ -1,29 +1,34 @@ import type { Block, BlockTag, + EthersError, + InterfaceAbi, JsonRpcSigner, TransactionReceipt, TransactionResponse } from 'ethers' - -import { Contract, ContractFactory, JsonRpcProvider, WebSocketProvider } from 'ethers' - -import { toHex } from '@multiplechain/utils' - +import { sleep, checkWebSocket } from '@multiplechain/utils' import type { EvmNetworkConfigInterface } from './Provider.ts' +import type { TransactionData } from '../services/TransactionSigner.ts' +import { Wallet, Contract, ContractFactory, JsonRpcProvider, WebSocketProvider } from 'ethers' -declare module 'ethers' { - interface JsonRpcProvider { - getGasPrice: () => Promise - } -} +export type { EthersError } from 'ethers' export class Ethers { + /** + * Network configuration of the provider + */ network: EvmNetworkConfigInterface + /** + * JSON RPC provider + */ jsonRpcProvider: JsonRpcProvider - websocketProvider?: WebSocketProvider + /** + * WebSocket provider + */ + webSocketProvider?: WebSocketProvider /** * @param {EvmNetworkConfigInterface} network @@ -31,56 +36,120 @@ export class Ethers { constructor(network: EvmNetworkConfigInterface) { this.network = network this.jsonRpcProvider = new JsonRpcProvider(network.rpcUrl) - if (network.wsUrl !== undefined) { - this.websocketProvider = new WebSocketProvider(network.wsUrl) - } } + /** + * @returns {JsonRpcProvider} + */ public get jsonRpc(): JsonRpcProvider { return this.jsonRpcProvider } - public get websocket(): WebSocketProvider | undefined { - return this.websocketProvider + /** + * @returns {WebSocketProvider | undefined} + */ + public get webSocket(): WebSocketProvider | undefined { + return this.webSocketProvider + } + + /** + * @returns {WebSocketProvider} + */ + public async connectWebSocket(): Promise { + return await new Promise((resolve, reject) => { + if (this.network.wsUrl === undefined) { + reject(new Error('WebSocket URL is not defined')) + } else { + const url = this.network.wsUrl + checkWebSocket(url) + .then((status: any) => { + if (status instanceof Error) { + reject(status) + } else { + resolve((this.webSocketProvider = new WebSocketProvider(url))) + } + }) + .catch(reject) + } + }) } /** * @param {String} address - * @param {object[]} abi - * @param {JsonRpcSigner} provider - * @returns {Object} + * @param {InterfaceAbi} abi + * @param {JsonRpcSigner} signer + * @returns {Promise} + */ + public contract( + address: string, + abi: InterfaceAbi, + signer?: JsonRpcSigner | JsonRpcProvider + ): Contract { + return new Contract(address, abi, signer) + } + + /** + * @param {string} privateKey private key of the wallet + * @param {JsonRpcProvider} provider provider of the blockchain network + * @returns {Wallet} */ - public contract(address: string, abi: object[], provider?: JsonRpcSigner): Contract { - return new Contract(address, abi, provider) + public wallet(privateKey: string, provider?: JsonRpcProvider): Wallet { + return new Wallet(privateKey, provider ?? this.jsonRpc) } /** - * @param {object[]} abi + * @param {InterfaceAbi} abi * @param {String} bytecode - * @param {JsonRpcSigner} provider - * @returns {Object} + * @param {JsonRpcSigner} signer + * @returns {Promise} */ public contractFactory( - abi: object[], + abi: InterfaceAbi, bytecode: string, - provider?: JsonRpcSigner + signer?: JsonRpcSigner | JsonRpcProvider ): ContractFactory { - return new ContractFactory(abi, bytecode, provider) + return new ContractFactory(abi, bytecode, signer) } /** - * @param {Object} data + * @param {string} address * @returns {Promise} */ - public async getEstimateGas(data: object): Promise { - return toHex((await this.jsonRpcProvider.estimateGas(data)).toString()) + async getByteCode(address: string): Promise { + try { + return await this.jsonRpc.getCode(address) + } catch (error) { + const e = error as EthersError + if (e.code === 'UNCONFIGURED_NAME') { + await sleep(1000) + return await this.getByteCode(address) + } else { + throw error + } + } + } + + /** + * @param {Object} data + * @returns {Promise} + */ + public async getEstimateGas(data: TransactionData): Promise { + return Number(await this.jsonRpcProvider.estimateGas(data)) } /** * @returns {Promise} */ public async getGasPrice(): Promise { - return toHex((await this.jsonRpcProvider.getGasPrice()).toString()) + return (await this.jsonRpc.send('eth_gasPrice', [])).toString() as string + } + + /** + * @param {String} address + * @returns {Promise} + */ + public async getNonce(address: string): Promise { + return await this.jsonRpcProvider.getTransactionCount(address) } /** diff --git a/packages/networks/evm-chains/src/services/Networks.ts b/packages/networks/evm-chains/src/services/Networks.ts new file mode 100644 index 0000000..cfc7b00 --- /dev/null +++ b/packages/networks/evm-chains/src/services/Networks.ts @@ -0,0 +1,82 @@ +import * as wagmiChains from '@wagmi/chains' +import type { EvmNetworkConfigInterface } from './Provider.ts' + +const networks: Record = {} + +Object.keys(wagmiChains).forEach((key) => { + const wagmiChain: wagmiChains.Chain = wagmiChains[key as keyof typeof wagmiChains] + + let backupRpcUrl = '' + if (wagmiChain.rpcUrls?.infura !== undefined) { + backupRpcUrl = wagmiChain.rpcUrls.infura.http[0] + } else if (wagmiChain.rpcUrls?.alchemy !== undefined) { + backupRpcUrl = wagmiChain.rpcUrls.alchemy.http[0] + } + + const network: EvmNetworkConfigInterface = { + id: wagmiChain.id, + name: wagmiChain.name, + testnet: wagmiChain.testnet ?? false, + nativeCurrency: wagmiChain.nativeCurrency, + hexId: '0x' + Number(wagmiChain.id).toString(16), + explorerUrl: wagmiChain.blockExplorers?.default.url ?? '', + rpcUrl: wagmiChain.rpcUrls?.default.http[0] ?? backupRpcUrl + } + + if (wagmiChain.rpcUrls?.default.webSocket !== undefined) { + network.wsUrl = wagmiChain.rpcUrls.default.webSocket[0] + } + + if (wagmiChain.rpcUrls?.infura?.webSocket !== undefined && network.wsUrl === undefined) { + network.wsUrl = wagmiChain.rpcUrls.infura.webSocket[0] + } + + if (wagmiChain.rpcUrls?.alchemy?.webSocket !== undefined && network.wsUrl === undefined) { + network.wsUrl = wagmiChain.rpcUrls.alchemy.webSocket[0] + } + + if (network.wsUrl === undefined) { + delete network.wsUrl + } + + networks[key === 'mainnet' ? 'ethereum' : key] = network +}) + +const findById = (id: number): EvmNetworkConfigInterface | undefined => { + return Object.values(networks).find((network) => network.id === id) +} + +const findByKey = (key: string): EvmNetworkConfigInterface | undefined => { + return networks[key] +} + +const findByName = (name: string): EvmNetworkConfigInterface | undefined => { + return Object.values(networks).find((network) => network.name?.includes(name)) +} + +const findByHexId = (hexId: string): EvmNetworkConfigInterface | undefined => { + return Object.values(networks).find((network) => network.hexId === hexId) +} + +const findBySymbol = (symbol: string): EvmNetworkConfigInterface | undefined => { + return Object.values(networks).find((network) => network.nativeCurrency.symbol === symbol) +} + +const getTestnets = (): EvmNetworkConfigInterface[] => { + return Object.values(networks).filter((network) => network.testnet ?? false) +} + +const getMainnets = (): EvmNetworkConfigInterface[] => { + return Object.values(networks).filter((network) => !(network.testnet ?? false)) +} + +export default { + findById, + findByKey, + findByName, + findByHexId, + findBySymbol, + getTestnets, + getMainnets, + ...networks +} diff --git a/packages/networks/evm-chains/src/services/Provider.ts b/packages/networks/evm-chains/src/services/Provider.ts index 9c9ef70..ede4232 100644 --- a/packages/networks/evm-chains/src/services/Provider.ts +++ b/packages/networks/evm-chains/src/services/Provider.ts @@ -1,5 +1,11 @@ import { Ethers } from './Ethers.ts' -import type { NetworkConfigInterface, ProviderInterface } from '@multiplechain/types' +import { + ErrorTypeEnum, + type NetworkConfigInterface, + type ProviderInterface +} from '@multiplechain/types' + +import { checkWebSocket } from '@multiplechain/utils' export interface EvmNetworkConfigInterface extends NetworkConfigInterface { id: number @@ -15,16 +21,16 @@ export interface EvmNetworkConfigInterface extends NetworkConfigInterface { } } -export class Provider implements Omit { +export class Provider implements Omit { /** * Network configuration of the provider */ - private network: EvmNetworkConfigInterface + public network: EvmNetworkConfigInterface /** * Ethers service */ - private _ethers: Ethers + public ethers: Ethers /** * Static instance of the provider @@ -37,48 +43,95 @@ export class Provider implements Omit { constructor(network: EvmNetworkConfigInterface) { this.network = network Provider._instance = this - this._ethers = new Ethers(network) - } - - /** - * @returns Ethers service instance - */ - get ethers(): Ethers { - return this._ethers + this.ethers = new Ethers(network) } /** * Get the static instance of the provider - * @returns Provider + * @returns {Provider} Provider */ static get instance(): Provider { if (Provider._instance === undefined) { - throw new Error('Provider is not initialized') + throw new Error(ErrorTypeEnum.PROVIDER_IS_NOT_INITIALIZED) } return Provider._instance } /** * Initialize the static instance of the provider - * @param network - Network configuration of the provider + * @param {EvmNetworkConfigInterface} network - Network configuration of the provider + * @returns {void} */ static initialize(network: EvmNetworkConfigInterface): void { + if (Provider._instance !== undefined) { + throw new Error(ErrorTypeEnum.PROVIDER_IS_ALREADY_INITIALIZED) + } Provider._instance = new Provider(network) } + /** + * Check RPC connection + * @param {string} url - RPC URL + * @returns {Promise} + */ + async checkRpcConnection(url?: string): Promise { + try { + const response = await fetch(url ?? this.network.rpcUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + jsonrpc: '2.0', + method: 'eth_getChainId', + params: [], + id: 1 + }) + }) + + if (!response.ok) { + return new Error(response.statusText + ': ' + (await response.text())) + } + + return true + } catch (error) { + return error as any + } + } + + /** + * Check WS connection + * @param {string} url - Websocket URL + * @returns {Promise} + */ + async checkWsConnection(url?: string): Promise { + try { + const result: any = await checkWebSocket(url ?? this.network.rpcUrl) + + if (result instanceof Error) { + return result + } + + return true + } catch (error) { + return error as Error + } + } + /** * Update network configuration of the provider - * @param network - Network configuration of the provider + * @param {EvmNetworkConfigInterface} network - Network configuration of the provider + * @returns {void} */ update(network: EvmNetworkConfigInterface): void { this.network = network Provider._instance = this - this._ethers = new Ethers(network) + this.ethers = new Ethers(network) } /** * Get the current network configuration is testnet or not - * @returns boolean + * @returns {boolean} */ isTestnet(): boolean { return this.network?.testnet ?? false diff --git a/packages/networks/evm-chains/src/services/TransactionListener.ts b/packages/networks/evm-chains/src/services/TransactionListener.ts index 5382093..b513638 100644 --- a/packages/networks/evm-chains/src/services/TransactionListener.ts +++ b/packages/networks/evm-chains/src/services/TransactionListener.ts @@ -6,7 +6,23 @@ import type { DynamicTransactionListenerFilterType } from '@multiplechain/types' +import { Provider } from './Provider.ts' +import type { Ethers } from './Ethers.ts' +import { id, zeroPadValue } from 'ethers' +import { objectsEqual } from '@multiplechain/utils' +import { Transaction } from '../models/Transaction.ts' +import { NftTransaction } from '../models/NftTransaction.ts' +import { CoinTransaction } from '../models/CoinTransaction.ts' +import { TokenTransaction } from '../models/TokenTransaction.ts' import { TransactionListenerProcessIndex } from '@multiplechain/types' +import { ContractTransaction } from '../models/ContractTransaction.ts' +import { + type WebSocketProvider, + type JsonRpcApiProvider, + type EventFilter, + type Log, + type TransactionResponse +} from 'ethers' export class TransactionListener implements TransactionListenerInterface @@ -24,83 +40,398 @@ export class TransactionListener /** * Transaction listener filter */ - filter: DynamicTransactionListenerFilterType + filter?: DynamicTransactionListenerFilterType | Record /** - * @param type - Transaction type - * @param filter - Transaction listener filter + * Provider */ - constructor(type: T, filter: DynamicTransactionListenerFilterType) { + provider: Provider + + /** + * Listener status + */ + status: boolean = false + + /** + * JSON-RPC provider + */ + ethers: Ethers + + /** + * JSON-RPC provider + */ + jsonRpc: JsonRpcApiProvider + + /** + * WebSocket provider + */ + webSocket: WebSocketProvider + + /** + * Dynamic stop method + */ + dynamicStop: () => void = () => {} + + /** + * Triggered transactions + */ + triggeredTransactions: string[] = [] + + /** + * @param {T} type - Transaction type + * @param {Provider} provider - Provider + * @param {DynamicTransactionListenerFilterType} filter - Transaction listener filter + */ + constructor(type: T, provider?: Provider, filter?: DynamicTransactionListenerFilterType) { this.type = type - this.filter = filter - // @ts-expect-error allow dynamic access - this[TransactionListenerProcessIndex[type]]() + this.filter = filter ?? {} + this.provider = provider ?? Provider.instance + this.ethers = this.provider.ethers + this.jsonRpc = this.provider.ethers.jsonRpc } /** * Close the listener + * @returns {void} */ stop(): void { - // Close the listener + if (this.status) { + this.status = false + this.dynamicStop() + } + } + + /** + * Start the listener + * @returns {void} + */ + start(): void { + if (!this.status) { + this.status = true + // @ts-expect-error allow dynamic access + this[TransactionListenerProcessIndex[this.type]]() + } + } + + /** + * Get the listener status + * @returns {boolean} - Listener status + */ + getStatus(): boolean { + return this.status } /** * Listen to the transaction events - * @param callback - Callback function + * @param {TransactionListenerCallbackType} callback - Callback function + * @returns {Promise} */ - on(callback: TransactionListenerCallbackType): void { + async on(callback: TransactionListenerCallbackType): Promise { + if (this.webSocket === undefined) { + const socket = await this.provider.ethers.connectWebSocket() + if (typeof socket === 'string') { + throw new Error('WebSocket connection is not available') + } else { + this.webSocket = socket + } + } + + this.start() this.callbacks.push(callback) + + return true } /** * Trigger the event when a transaction is detected - * @param transaction - Transaction data + * @param {DynamicTransactionType} transaction - Transaction data + * @returns {void} */ - trigger(transaction: DynamicTransactionType): void { - this.callbacks.forEach((callback) => { - callback(transaction) - }) + trigger(transaction: DynamicTransactionType): void { + if (!this.triggeredTransactions.includes(transaction.id)) { + this.triggeredTransactions.push(transaction.id) + this.callbacks.forEach((callback) => { + callback(transaction) + }) + } } /** * General transaction process + * @returns {void} */ generalProcess(): void { - // General transaction process + const callback = async (transactionId: string): Promise => { + if (this.filter?.signer !== undefined) { + const transaction = await this.ethers.getTransaction(transactionId) + if (transaction?.from.toLowerCase() !== this.filter.signer.toLowerCase()) { + return + } + } + + this.trigger(new Transaction(transactionId)) + } + + void this.webSocket.on('pending', callback) + this.dynamicStop = () => { + void this.webSocket.off('pending', callback) + } } /** * Contract transaction process + * @returns {void} */ contractProcess(): void { - // Contract transaction process - } + const filter = this + .filter as DynamicTransactionListenerFilterType - /** - * Asset transaction process - */ - assetProcess(): void { - // Asset transaction process + let params: string | EventFilter + if (filter.address === undefined) { + params = 'pending' + } else { + params = { + address: filter.address + } + } + + const callback = async (transactionIdOrLog: string | Log): Promise => { + let transaction: TransactionResponse | null + if (typeof transactionIdOrLog === 'string') { + transaction = await this.ethers.getTransaction(transactionIdOrLog) + const contractBytecode = await this.ethers.getByteCode(transaction?.to ?? '') + + if (contractBytecode === '0x') { + return + } + } else { + transaction = await this.ethers.getTransaction(transactionIdOrLog.transactionHash) + } + + if (transaction === null) { + return + } + + interface ParamsType { + signer?: string + } + + const expectedParams: ParamsType = {} + const receivedParams: ParamsType = {} + + if (filter.signer !== undefined) { + expectedParams.signer = filter.signer.toLowerCase() + receivedParams.signer = transaction.from.toLowerCase() + } + + if (!objectsEqual(expectedParams, receivedParams)) { + return + } + + this.trigger(new ContractTransaction(transaction.hash)) + } + + void this.webSocket.on(params, callback) + this.dynamicStop = () => { + void this.webSocket.off(params, callback) + } } /** * Coin transaction process + * @returns {void} */ coinProcess(): void { - // Coin transaction process + const filter = this.filter as DynamicTransactionListenerFilterType + + if ( + filter.signer !== undefined && + filter.sender !== undefined && + filter.signer !== filter.sender + ) { + throw new Error( + 'Sender and signer must be the same in coin transactions. Or only one of them can be defined.' + ) + } + + const callback = async (transactionId: string): Promise => { + const tx = await this.ethers.getTransaction(transactionId) + const contractBytecode = await this.ethers.getByteCode(tx?.to ?? '') + + if (contractBytecode !== '0x' || tx === null) { + return + } + + const sender = filter.sender ?? filter.signer + + interface ParamsType { + sender?: string + receiver?: string + } + + const expectedParams: ParamsType = {} + const receivedParams: ParamsType = {} + + if (sender !== undefined) { + expectedParams.sender = sender.toLowerCase() + receivedParams.sender = tx.from.toLowerCase() + } + + if (filter.receiver !== undefined) { + expectedParams.receiver = filter.receiver.toLowerCase() + receivedParams.receiver = tx.to?.toLowerCase() + } + + if (!objectsEqual(expectedParams, receivedParams)) { + return + } + + const transaction = new CoinTransaction(transactionId) + + if (filter.amount !== undefined) { + await transaction.wait() + const amount = await transaction.getAmount() + if (amount !== filter.amount) { + return + } + } + + this.trigger(transaction) + } + + void this.webSocket.on('pending', callback) + this.dynamicStop = () => { + void this.webSocket.off('pending', callback) + } } /** * Token transaction process + * @returns {void} */ tokenProcess(): void { - // Token transaction process + const filter = this + .filter as DynamicTransactionListenerFilterType + + const params: EventFilter = { + address: filter.address, + topics: [ + id('Transfer(address,address,uint256)'), + filter.sender !== undefined ? zeroPadValue(filter.sender, 32) : null, + filter.receiver !== undefined ? zeroPadValue(filter.receiver, 32) : null + ] + } + + const callback = async (transactionLog: Log): Promise => { + const transaction = new TokenTransaction(transactionLog.transactionHash) + const data = await transaction.getData() + + if (data === null) { + return + } + + const decodedData = await transaction.decodeData(data.response) + + if (decodedData === null) { + return + } + + if (decodedData.name !== 'transfer' && decodedData.name !== 'transferFrom') { + return + } + + interface ParamsType { + signer?: string + } + + const expectedParams: ParamsType = {} + const receivedParams: ParamsType = {} + + if (filter.signer !== undefined) { + expectedParams.signer = filter.signer.toLowerCase() + receivedParams.signer = data.response.from.toLowerCase() + } + + if (!objectsEqual(expectedParams, receivedParams)) { + return + } + + if (filter.amount !== undefined) { + await transaction.wait() + const amount = await transaction.getAmount() + if (amount !== filter.amount) { + return + } + } + + this.trigger(transaction) + } + + void this.webSocket.on(params, callback) + this.dynamicStop = () => { + void this.webSocket.off(params, callback) + } } /** * NFT transaction process + * @returns {void} */ nftProcess(): void { - // NFT transaction process + const filter = this.filter as DynamicTransactionListenerFilterType + + const params: EventFilter = { + address: filter.address, + topics: [ + id('Transfer(address,address,uint256)'), + filter.sender !== undefined ? zeroPadValue(filter.sender, 32) : null, + filter.receiver !== undefined ? zeroPadValue(filter.receiver, 32) : null, + filter.nftId !== undefined + ? zeroPadValue(`0x0${filter.nftId.toString(16)}`, 32) + : null + ] + } + + const callback = async (transactionLog: Log): Promise => { + const transaction = new NftTransaction(transactionLog.transactionHash) + const data = await transaction.getData() + + if (data === null) { + return + } + + const decodedData = await transaction.decodeData(data.response) + + if (decodedData === null) { + return + } + + if (decodedData.name !== 'transferFrom') { + return + } + + interface ParamsType { + signer?: string + } + + const expectedParams: ParamsType = {} + const receivedParams: ParamsType = {} + + if (filter.signer !== undefined) { + expectedParams.signer = filter.signer.toLowerCase() + receivedParams.signer = data.response.from.toLowerCase() + } + + if (!objectsEqual(expectedParams, receivedParams)) { + return + } + + this.trigger(transaction) + } + + void this.webSocket.on(params, callback) + this.dynamicStop = () => { + void this.webSocket.off(params, callback) + } } } diff --git a/packages/networks/evm-chains/src/services/TransactionSigner.ts b/packages/networks/evm-chains/src/services/TransactionSigner.ts index b791561..2b54da3 100644 --- a/packages/networks/evm-chains/src/services/TransactionSigner.ts +++ b/packages/networks/evm-chains/src/services/TransactionSigner.ts @@ -1,42 +1,80 @@ -import type { TransactionSignerInterface, TransactionInterface } from '@multiplechain/types' +import type { EthersError } from './Ethers.ts' +import { Provider } from '../services/Provider.ts' +import { Transaction } from '../models/Transaction.ts' +import { NftTransaction } from '../models/NftTransaction.ts' +import { CoinTransaction } from '../models/CoinTransaction.ts' +import { TokenTransaction } from '../models/TokenTransaction.ts' +import type { TransactionRequest, Wallet, BigNumberish } from 'ethers' +import { ErrorTypeEnum, type TransactionSignerInterface } from '@multiplechain/types' +export interface TransactionData extends TransactionRequest { + gas?: BigNumberish +} export class TransactionSigner implements TransactionSignerInterface { /** * Transaction data from the blockchain network */ - rawData: any + rawData: TransactionData /** * Signed transaction data */ - signedData?: any + signedData: string + + /** + * Wallet instance from ethers with the private key + */ + wallet: Wallet + + /** + * Blockchain network provider + */ + provider: Provider /** - * @param rawData - Transaction data + * @param {TransactionData} rawData Transaction data + * @param {Provider} provider Blockchain network provider */ - constructor(rawData: any) { + constructor(rawData: TransactionData, provider?: Provider) { this.rawData = rawData + this.provider = provider ?? Provider.instance } /** * Sign the transaction - * @param privateKey - Transaction data + * @param {string} privateKey - Transaction data + * @returns {Promise} Signed transaction data */ - sign(privateKey: string): this { - return this + public async sign(privateKey: string): Promise { + try { + this.wallet = this.provider.ethers.wallet(privateKey) + this.signedData = await this.wallet.signTransaction(this.rawData) + return this + } catch (error) { + const e = error as EthersError + if (e?.shortMessage.includes('transaction from address mismatch')) { + throw new Error(ErrorTypeEnum.INVALID_PRIVATE_KEY) + } + + throw error + } } /** * Send the transaction to the blockchain network - * @returns Promise of the transaction + * @returns {Promise} Transaction data */ - async send(): Promise { - return await Promise.resolve(this.signedData) + async send(): Promise { + return new Transaction( + (await this.provider.ethers.jsonRpc.send('eth_sendRawTransaction', [ + this.signedData + ])) as string + ) } /** * Get the raw transaction data - * @returns Transaction data + * @returns {any} Transaction data */ getRawData(): any { return this.rawData @@ -44,9 +82,39 @@ export class TransactionSigner implements TransactionSignerInterface { /** * Get the signed transaction data - * @returns Signed transaction data + * @returns {any} Signed transaction data */ getSignedData(): any { return this.signedData } } + +export class CoinTransactionSigner extends TransactionSigner { + /** + * Send the transaction to the blockchain network + * @returns {Promise} Transaction data + */ + async send(): Promise { + return new CoinTransaction((await super.send()).getId()) + } +} + +export class TokenTransactionSigner extends TransactionSigner { + /** + * Send the transaction to the blockchain network + * @returns {Promise} Transaction data + */ + async send(): Promise { + return new TokenTransaction((await super.send()).getId()) + } +} + +export class NftTransactionSigner extends TransactionSigner { + /** + * Send the transaction to the blockchain network + * @returns {Promise} Transaction data + */ + async send(): Promise { + return new NftTransaction((await super.send()).getId()) + } +} diff --git a/packages/networks/evm-chains/src/services/index.ts b/packages/networks/evm-chains/src/services/index.ts new file mode 100644 index 0000000..549a382 --- /dev/null +++ b/packages/networks/evm-chains/src/services/index.ts @@ -0,0 +1,2 @@ +export * from './TransactionSigner.ts' +export * from './TransactionListener.ts' diff --git a/packages/networks/evm-chains/tests/assets.spec.ts b/packages/networks/evm-chains/tests/assets.spec.ts index b19c282..ad037d0 100644 --- a/packages/networks/evm-chains/tests/assets.spec.ts +++ b/packages/networks/evm-chains/tests/assets.spec.ts @@ -1,7 +1,250 @@ -import { describe, it, expect } from 'vitest' +import { describe, it, expect, assert } from 'vitest' -describe('Tests for assets', () => { - it('should load assets', async () => { - expect(1).toBe(1) +import { NFT } from '../src/assets/NFT.ts' +import { Coin } from '../src/assets/Coin.ts' +import { Token } from '../src/assets/Token.ts' +import { fixFloat } from '@multiplechain/utils' +import { Transaction } from '../src/models/Transaction.ts' +import { TransactionStatusEnum } from '@multiplechain/types' +import { TransactionSigner } from '../src/services/TransactionSigner.ts' + +const coinBalanceTestAmount = Number(process.env.COIN_BALANCE_TEST_AMOUNT) +const tokenBalanceTestAmount = Number(process.env.TOKEN_BALANCE_TEST_AMOUNT) +const nftBalanceTestAmount = Number(process.env.NFT_BALANCE_TEST_AMOUNT) +const transferTestAmount = Number(process.env.TRANSFER_TEST_AMOUNT) +const tokenTransferTestAmount = Number(process.env.TOKEN_TRANSFER_TEST_AMOUNT) +const tokenApproveTestAmount = Number(process.env.TOKEN_APPROVE_TEST_AMOUNT) +const nftTransferId = Number(process.env.NFT_TRANSFER_ID) + +const coinTransferTestIsActive = Boolean(process.env.COIN_TRANSFER_TEST_IS_ACTIVE !== 'false') +const tokenTransferTestIsActive = Boolean(process.env.TOKEN_TRANSFER_TEST_IS_ACTIVE !== 'false') +const tokenApproveTestIsActive = Boolean(process.env.TOKEN_APPROVE_TEST_IS_ACTIVE !== 'false') +const nftTransactionTestIsActive = Boolean(process.env.NFT_TRANSACTION_TEST_IS_ACTIVE !== 'false') +const tokenTransferFromTestIsActive = Boolean( + process.env.TOKEN_TRANSFER_FROM_TEST_IS_ACTIVE !== 'false' +) + +const balanceTestAddress = String(process.env.BALANCE_TEST_ADDRESS) +const senderPrivateKey = String(process.env.SENDER_PRIVATE_KEY) +const receiverPrivateKey = String(process.env.RECEIVER_PRIVATE_KEY) +const senderTestAddress = String(process.env.SENDER_TEST_ADDRESS) +const receiverTestAddress = String(process.env.RECEIVER_TEST_ADDRESS) +const tokenTestAddress = String(process.env.TOKEN_TEST_ADDRESS) +const nftTestAddress = String(process.env.NFT_TEST_ADDRESS) + +const waitSecondsBeforeThanNewTx = async (seconds: number): Promise => { + return await new Promise((resolve) => setTimeout(resolve, seconds * 1000)) +} + +const checkSigner = async (signer: TransactionSigner, privateKey?: string): Promise => { + expect(signer).toBeInstanceOf(TransactionSigner) + + const rawData = signer.getRawData() + + assert.isObject(rawData) + + await signer.sign(privateKey ?? senderPrivateKey) + + assert.isString(signer.getSignedData()) +} + +const checkTx = async (transaction: Transaction): Promise => { + expect(transaction).toBeInstanceOf(Transaction) + const status = await transaction.wait(10) + expect(status).toBe(TransactionStatusEnum.CONFIRMED) +} + +describe('Coin', () => { + const coin = new Coin() + it('Name and symbol', () => { + expect(coin.getName()).toBe('Ethereum') + expect(coin.getSymbol()).toBe('ETH') + }) + + it('Decimals', () => { + expect(coin.getDecimals()).toBe(18) + }) + + it('Balance', async () => { + const balance = await coin.getBalance(balanceTestAddress) + expect(balance).toBe(coinBalanceTestAmount) + }) + + it('Transfer', async () => { + if (!coinTransferTestIsActive) return + + const signer = await coin.transfer( + senderTestAddress, + receiverTestAddress, + transferTestAmount + ) + + await checkSigner(signer) + + const beforeBalance = await coin.getBalance(receiverTestAddress) + + await checkTx(await signer.send()) + + const afterBalance = await coin.getBalance(receiverTestAddress) + expect(afterBalance).toBe(fixFloat(beforeBalance + transferTestAmount)) + }) +}) + +describe('Token', () => { + const token = new Token(tokenTestAddress) + + it('Name and symbol', async () => { + expect(await token.getName()).toBe('MyToken') + expect(await token.getSymbol()).toBe('MTK') + }) + + it('Decimals', async () => { + expect(await token.getDecimals()).toBe(18) + }) + + it('Balance', async () => { + const balance = await token.getBalance(balanceTestAddress) + expect(balance).toBe(tokenBalanceTestAmount) + }) + + it('Total supply', async () => { + const totalSupply = await token.getTotalSupply() + expect(totalSupply).toBe(1000000) + }) + + it('Transfer', async () => { + if (!tokenTransferTestIsActive) return + + await waitSecondsBeforeThanNewTx(30) + + const signer = await token.transfer( + senderTestAddress, + receiverTestAddress, + tokenTransferTestAmount + ) + + await checkSigner(signer) + + const beforeBalance = await token.getBalance(receiverTestAddress) + + await checkTx(await signer.send()) + + const afterBalance = await token.getBalance(receiverTestAddress) + expect(afterBalance).toBe(fixFloat(beforeBalance + tokenTransferTestAmount)) + }) + + it('Approve and Allowance', async () => { + if (!tokenApproveTestIsActive) return + + await waitSecondsBeforeThanNewTx(30) + + const signer = await token.approve( + senderTestAddress, + receiverTestAddress, + tokenApproveTestAmount + ) + + await checkSigner(signer) + + await checkTx(await signer.send()) + + expect(await token.getAllowance(senderTestAddress, receiverTestAddress)).toBe( + tokenApproveTestAmount + ) + }) + + it('Transfer from', async () => { + if (!tokenTransferFromTestIsActive) return + + await waitSecondsBeforeThanNewTx(30) + + const signer = await token.transferFrom( + receiverTestAddress, + senderTestAddress, + receiverTestAddress, + 2 + ) + + await checkSigner(signer, receiverPrivateKey) + + const beforeBalance = await token.getBalance(receiverTestAddress) + + await checkTx(await signer.send()) + + const afterBalance = await token.getBalance(receiverTestAddress) + expect(afterBalance).toBe(fixFloat(beforeBalance + 2)) + }) +}) + +describe('Nft', () => { + const nft = new NFT(nftTestAddress) + + it('Name and symbol', async () => { + expect(await nft.getName()).toBe('TestNFT') + expect(await nft.getSymbol()).toBe('TNFT') + }) + + it('Balance', async () => { + const balance = await nft.getBalance(balanceTestAddress) + expect(balance).toBe(nftBalanceTestAmount) + }) + + it('Owner', async () => { + expect(await nft.getOwner(5)).toBe(balanceTestAddress) + }) + + it('Token URI', async () => { + expect(await nft.getTokenURI(5)).toBe('') + }) + + it('Approved', async () => { + expect(await nft.getApproved(5)).toBe('0x0000000000000000000000000000000000000000') + }) + + it('Transfer', async () => { + if (!nftTransactionTestIsActive) return + + await waitSecondsBeforeThanNewTx(30) + + const signer = await nft.transfer(senderTestAddress, receiverTestAddress, nftTransferId) + + await checkSigner(signer) + + await checkTx(await signer.send()) + + expect(await nft.getOwner(nftTransferId)).toBe(receiverTestAddress) + }) + + it('Approve', async () => { + if (!nftTransactionTestIsActive) return + + await waitSecondsBeforeThanNewTx(30) + + const signer = await nft.approve(receiverTestAddress, senderTestAddress, nftTransferId) + + await checkSigner(signer, receiverPrivateKey) + + await checkTx(await signer.send()) + + expect(await nft.getApproved(nftTransferId)).toBe(senderTestAddress) + }) + + it('Transfer from', async () => { + if (!nftTransactionTestIsActive) return + + await waitSecondsBeforeThanNewTx(30) + + const signer = await nft.transferFrom( + senderTestAddress, + receiverTestAddress, + senderTestAddress, + nftTransferId + ) + + await checkSigner(signer) + + await checkTx(await signer.send()) + + expect(await nft.getOwner(nftTransferId)).toBe(senderTestAddress) }) }) diff --git a/packages/networks/evm-chains/tests/models.spec.ts b/packages/networks/evm-chains/tests/models.spec.ts index 7d82624..c8ce377 100644 --- a/packages/networks/evm-chains/tests/models.spec.ts +++ b/packages/networks/evm-chains/tests/models.spec.ts @@ -1,16 +1,152 @@ import { describe, it, expect } from 'vitest' import { Transaction } from '../src/models/Transaction.ts' +import { NftTransaction } from '../src/models/NftTransaction.ts' +import { CoinTransaction } from '../src/models/CoinTransaction.ts' +import { TokenTransaction } from '../src/models/TokenTransaction.ts' +import { AssetDirectionEnum, TransactionStatusEnum } from '@multiplechain/types' -const txId = '0x566002399664e92f82ed654c181095bdd7ff3d3f1921d963257585891f622251' +const nftId = Number(process.env.NFT_ID) +const tokenAmount = Number(process.env.TOKEN_AMOUNT) +const coinAmount = Number(process.env.COIN_AMOUNT) -describe('Tests for models', () => { - const tx = new Transaction(txId) - it('Transaction Id', async () => { - expect(tx.getId()).toBe(txId) +const etherTransferTx = String(process.env.ETHER_TRANSFER_TX) +const tokenTransferTx = String(process.env.TOKEN_TRANSFER_TX) +const nftTransferTx = String(process.env.NFT_TRANSFER_TX) + +const coinSender = String(process.env.COIN_SENDER) +const coinReceiver = String(process.env.COIN_RECEIVER) + +const tokenSender = String(process.env.TOKEN_SENDER) +const tokenReceiver = String(process.env.TOKEN_RECEIVER) + +const nftSender = String(process.env.NFT_SENDER) +const nftReceiver = String(process.env.NFT_RECEIVER) + +describe('Transaction', () => { + const tx = new Transaction(etherTransferTx) + it('Id', async () => { + expect(tx.getId()).toBe(etherTransferTx) }) - it('Transaction Data', async () => { + it('Data', async () => { expect(await tx.getData()).toBeTypeOf('object') }) + + it('URL', async () => { + expect(tx.getUrl()).toBe( + 'https://sepolia.etherscan.io/tx/0x566002399664e92f82ed654c181095bdd7ff3d3f1921d963257585891f622251' + ) + }) + + it('Sender', async () => { + expect(await tx.getSigner()).toBe(coinSender) + }) + + it('Fee', async () => { + expect(await tx.getFee()).toBe(0.000371822357865) + }) + + it('Block Number', async () => { + expect(await tx.getBlockNumber()).toBe(5461884) + }) + + // Not give any response in vitest environment + // it('Block Timestamp', async () => { + // expect(await tx.getBlockTimestamp()).toBe(1710141144) + // }) + + it('Block Confirmation Count', async () => { + expect(await tx.getBlockConfirmationCount()).toBeGreaterThan(129954) + }) + + it('Status', async () => { + expect(await tx.getStatus()).toBe(TransactionStatusEnum.CONFIRMED) + }) +}) + +describe('Coin Transaction', () => { + const tx = new CoinTransaction(etherTransferTx) + + it('Receiver', async () => { + expect((await tx.getReceiver()).toLowerCase()).toBe(coinReceiver.toLowerCase()) + }) + + it('Amount', async () => { + expect(await tx.getAmount()).toBe(coinAmount) + }) + + it('Verify Transfer', async () => { + expect(await tx.verifyTransfer(AssetDirectionEnum.INCOMING, coinReceiver, coinAmount)).toBe( + TransactionStatusEnum.CONFIRMED + ) + + expect(await tx.verifyTransfer(AssetDirectionEnum.OUTGOING, coinSender, coinAmount)).toBe( + TransactionStatusEnum.CONFIRMED + ) + + expect(await tx.verifyTransfer(AssetDirectionEnum.OUTGOING, coinReceiver, coinAmount)).toBe( + TransactionStatusEnum.FAILED + ) + }) +}) + +describe('Token Transaction', () => { + const tx = new TokenTransaction(tokenTransferTx) + + it('Receiver', async () => { + expect((await tx.getReceiver()).toLowerCase()).toBe(tokenReceiver.toLowerCase()) + }) + + it('Amount', async () => { + expect(await tx.getAmount()).toBe(tokenAmount) + }) + + it('Verify Transfer', async () => { + expect( + await tx.verifyTransfer(AssetDirectionEnum.INCOMING, tokenReceiver, tokenAmount) + ).toBe(TransactionStatusEnum.CONFIRMED) + + expect(await tx.verifyTransfer(AssetDirectionEnum.OUTGOING, tokenSender, tokenAmount)).toBe( + TransactionStatusEnum.CONFIRMED + ) + + expect( + await tx.verifyTransfer(AssetDirectionEnum.OUTGOING, tokenReceiver, tokenAmount) + ).toBe(TransactionStatusEnum.FAILED) + }) +}) + +describe('NFT Transaction', () => { + const tx = new NftTransaction(nftTransferTx) + + it('Receiver', async () => { + expect((await tx.getReceiver()).toLowerCase()).toBe(nftReceiver.toLowerCase()) + }) + + it('Signer', async () => { + expect((await tx.getSigner()).toLowerCase()).toBe(nftReceiver.toLowerCase()) + }) + + it('Sender', async () => { + expect((await tx.getSender()).toLowerCase()).toBe(nftSender.toLowerCase()) + }) + + it('NFT ID', async () => { + expect(await tx.getNftId()).toBe(nftId) + }) + + it('Verify Transfer', async () => { + expect(await tx.verifyTransfer(AssetDirectionEnum.INCOMING, nftReceiver, nftId)).toBe( + TransactionStatusEnum.CONFIRMED + ) + + expect(await tx.verifyTransfer(AssetDirectionEnum.OUTGOING, nftSender, nftId)).toBe( + TransactionStatusEnum.CONFIRMED + ) + + expect(await tx.verifyTransfer(AssetDirectionEnum.OUTGOING, nftReceiver, nftId)).toBe( + TransactionStatusEnum.FAILED + ) + }) }) diff --git a/packages/networks/evm-chains/tests/services.spec.ts b/packages/networks/evm-chains/tests/services.spec.ts index e2ec6b3..ec39494 100644 --- a/packages/networks/evm-chains/tests/services.spec.ts +++ b/packages/networks/evm-chains/tests/services.spec.ts @@ -1,7 +1,186 @@ import { describe, it, expect } from 'vitest' -describe('Tests for services', () => { - it('should load services', async () => { - expect(1).toBe(1) +import { provider } from './setup.ts' +import { Provider } from '../src/services/Provider.ts' +import { TransactionListener } from '../src/services/TransactionListener.ts' +import { TransactionTypeEnum } from '@multiplechain/types' +import { Coin } from '../src/assets/Coin.ts' +import { CoinTransaction } from '../src/models/CoinTransaction.ts' +import { ContractTransaction } from '../src/models/ContractTransaction.ts' +import { Token } from '../src/assets/Token.ts' +import { Transaction } from '../src/models/Transaction.ts' +import { TokenTransaction } from '../src/models/TokenTransaction.ts' +import { NftTransaction } from '../src/models/NftTransaction.ts' +import { NFT } from '../src/assets/NFT.ts' + +const senderPrivateKey = String(process.env.SENDER_PRIVATE_KEY) +const receiverPrivateKey = String(process.env.RECEIVER_PRIVATE_KEY) +const senderTestAddress = String(process.env.SENDER_TEST_ADDRESS) +const receiverTestAddress = String(process.env.RECEIVER_TEST_ADDRESS) +const tokenTestAddress = String(process.env.TOKEN_TEST_ADDRESS) +const nftTestAddress = String(process.env.NFT_TEST_ADDRESS) + +const transactionListenerTestIsActive = Boolean( + process.env.TRANSACTION_LISTENER_TEST_IS_ACTIVE !== 'false' +) + +const waitSecondsBeforeThanNewTx = async (seconds: number): Promise => { + return await new Promise((resolve) => setTimeout(resolve, seconds * 1000)) +} + +describe('Provider', () => { + it('isTestnet', () => { + expect(provider.isTestnet()).toBe(true) + }) + + it('instance', () => { + expect(Provider.instance).toBe(provider) + }) + + it('checkRpcConnection', async () => { + expect(await provider.checkRpcConnection('https://sepolia.infura.io/v3')).instanceOf(Error) + expect(await provider.checkRpcConnection(process.env.RPC_URL as unknown as string)).toBe( + true + ) + }) + + it('checkWsConnection', async () => { + expect(await provider.checkWsConnection('wss://sepolia.infura.io/v3')).instanceOf(Error) + expect(await provider.checkWsConnection(process.env.WS_URL as unknown as string)).toBe(true) + }) +}) + +describe('Transaction Listener', () => { + if (!transactionListenerTestIsActive) { + it('No test is active', () => { + expect(true).toBe(true) + }) + return + } + + it('General', async () => { + const listener = new TransactionListener(TransactionTypeEnum.GENERAL, provider, { + signer: senderTestAddress + }) + + void listener.on((transaction) => { + listener.stop() + expect(transaction).toBeInstanceOf(Transaction) + }) + + const signer = await new Coin().transfer(senderTestAddress, receiverTestAddress, 0.0001) + + const transaction = await (await signer.sign(senderPrivateKey)).send() + + expect(transaction).toBeInstanceOf(Transaction) + + void (await transaction.wait()) + }) + + it('Contract', async () => { + await waitSecondsBeforeThanNewTx(10) + + const listener = new TransactionListener(TransactionTypeEnum.CONTRACT, provider, { + signer: senderTestAddress, + address: tokenTestAddress + }) + + void listener.on((transaction) => { + listener.stop() + expect(transaction).toBeInstanceOf(ContractTransaction) + }) + + const signer = await new Token(tokenTestAddress).transfer( + senderTestAddress, + receiverTestAddress, + 0.01 + ) + + const transaction = await (await signer.sign(senderPrivateKey)).send() + + expect(transaction).toBeInstanceOf(ContractTransaction) + + void (await transaction.wait()) + }) + + it('Coin', async () => { + await waitSecondsBeforeThanNewTx(10) + + const listener = new TransactionListener(TransactionTypeEnum.COIN, provider, { + signer: senderTestAddress, + receiver: receiverTestAddress + }) + + void listener.on((transaction) => { + listener.stop() + expect(transaction).toBeInstanceOf(CoinTransaction) + }) + + const signer = await new Coin().transfer(senderTestAddress, receiverTestAddress, 0.0001) + + const transaction = await (await signer.sign(senderPrivateKey)).send() + + expect(transaction).toBeInstanceOf(CoinTransaction) + + void (await transaction.wait()) + }) + + it('Token', async () => { + await waitSecondsBeforeThanNewTx(10) + + const listener = new TransactionListener(TransactionTypeEnum.TOKEN, provider, { + signer: senderTestAddress, + receiver: receiverTestAddress, + address: tokenTestAddress + }) + + void listener.on((transaction) => { + listener.stop() + expect(transaction).toBeInstanceOf(TokenTransaction) + }) + + const signer = await new Token(tokenTestAddress).transfer( + senderTestAddress, + receiverTestAddress, + 0.01 + ) + + const transaction = await (await signer.sign(senderPrivateKey)).send() + + expect(transaction).toBeInstanceOf(TokenTransaction) + + void (await transaction.wait()) + }) + + it('NFT', async () => { + await waitSecondsBeforeThanNewTx(10) + + const listener = new TransactionListener(TransactionTypeEnum.NFT, provider, { + signer: senderTestAddress, + receiver: receiverTestAddress, + address: nftTestAddress + }) + + void listener.on((transaction) => { + listener.stop() + expect(transaction).toBeInstanceOf(NftTransaction) + }) + + const nft = new NFT(nftTestAddress) + const signer = await nft.transfer(senderTestAddress, receiverTestAddress, 9) + + const transaction = await (await signer.sign(senderPrivateKey)).send() + + expect(transaction).toBeInstanceOf(NftTransaction) + + void (await transaction.wait()) + + await waitSecondsBeforeThanNewTx(10) + + const newSigner = await nft.transfer(receiverTestAddress, senderTestAddress, 9) + + const newTransaction = await (await newSigner.sign(receiverPrivateKey)).send() + + expect(newTransaction).toBeInstanceOf(NftTransaction) }) }) diff --git a/packages/networks/evm-chains/tests/setup.ts b/packages/networks/evm-chains/tests/setup.ts index 59e6837..0257953 100644 --- a/packages/networks/evm-chains/tests/setup.ts +++ b/packages/networks/evm-chains/tests/setup.ts @@ -1,15 +1,25 @@ import { Provider } from '../src/services/Provider.ts' -export const provider = new Provider({ - id: 11155111, - hexId: '0xaa36a7', - mainnetId: 1, - name: 'Ethereum Sepolia Testnet (QR)', - rpcUrl: 'https://sepolia.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161', - wsUrl: 'wss://sepolia.infura.io/ws/v3/9aa3d95b3bc440fa88ea12eaa4456161', - explorerUrl: 'https://sepolia.etherscan.io/', - nativeCurrency: { - symbol: 'ETH', - decimals: 18 - } -}) +let provider: Provider + +try { + provider = Provider.instance +} catch (e) { + provider = new Provider({ + id: 11155111, + hexId: '0xaa36a7', + mainnetId: 1, + testnet: true, + name: 'Ethereum Sepolia Testnet (QR)', + explorerUrl: 'https://sepolia.etherscan.io/', + rpcUrl: process.env.RPC_URL as unknown as string, + wsUrl: process.env.WS_URL as unknown as string, + nativeCurrency: { + symbol: 'ETH', + name: 'Ethereum', + decimals: 18 + } + }) +} + +export { provider } diff --git a/packages/networks/evm-chains/tsconfig.json b/packages/networks/evm-chains/tsconfig.json index 40bb104..ae078b5 100644 --- a/packages/networks/evm-chains/tsconfig.json +++ b/packages/networks/evm-chains/tsconfig.json @@ -7,5 +7,17 @@ "declarationDir": "./dist/types" }, "extends": "../../../tsconfig.json", - "include": ["src", ".eslintrc.json", "tests"] + "include": [ + "src", + "cdt.ts", + ".eslintrc.json", + "tests", + "resources/*.json", + "vite.config.ts", + "esbuild.ts", + "vitest.config.ts", + "../../../esbuild.ts", + "../../../vite.config.ts", + "../../../vitest.config.ts", + ] } diff --git a/packages/networks/evm-chains/vite.config.ts b/packages/networks/evm-chains/vite.config.ts new file mode 100644 index 0000000..342217d --- /dev/null +++ b/packages/networks/evm-chains/vite.config.ts @@ -0,0 +1,10 @@ +import { mergeConfig } from 'vite' +import mainConfig from '../../../vite.config.ts' + +export default mergeConfig(mainConfig, { + build: { + lib: { + name: 'EvmChains' + } + } +}) diff --git a/packages/networks/evm-chains/vitest.config.ts b/packages/networks/evm-chains/vitest.config.ts new file mode 100644 index 0000000..433e719 --- /dev/null +++ b/packages/networks/evm-chains/vitest.config.ts @@ -0,0 +1,12 @@ +import { mergeConfig, defineConfig } from 'vitest/config' +import mainConfig from '../../../vite.config.ts' + +export default mergeConfig( + mainConfig, + defineConfig({ + test: { + testTimeout: 180000, + setupFiles: ['./tests/setup.ts'] + } + }) +) diff --git a/packages/types/README.md b/packages/types/README.md index 39c495e..4f45a25 100644 --- a/packages/types/README.md +++ b/packages/types/README.md @@ -168,15 +168,13 @@ export type DynamicTransactionType = ? TransactionInterface : T extends TransactionTypeEnum.CONTRACT ? ContractTransactionInterface - : T extends TransactionTypeEnum.ASSET - ? AssetTransactionInterface - : T extends TransactionTypeEnum.COIN - ? CoinTransactionInterface - : T extends TransactionTypeEnum.TOKEN - ? TokenTransactionInterface - : T extends TransactionTypeEnum.NFT - ? NftTransactionInterface - : never + : T extends TransactionTypeEnum.COIN + ? CoinTransactionInterface + : T extends TransactionTypeEnum.TOKEN + ? TokenTransactionInterface + : T extends TransactionTypeEnum.NFT + ? NftTransactionInterface + : never ``` #### [DynamicTransactionListenerFilterType](./packages/types/src/services/TransactionListenerInterface.ts) diff --git a/packages/types/package.json b/packages/types/package.json index 5f611a9..6b3bf65 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@multiplechain/types", - "version": "0.1.13", + "version": "0.1.35", "type": "module", "main": "./src/index.ts", "types": "./src/index.ts", diff --git a/packages/types/src/assets.ts b/packages/types/src/assets.ts index c4b866f..03a1226 100644 --- a/packages/types/src/assets.ts +++ b/packages/types/src/assets.ts @@ -13,106 +13,187 @@ export interface ContractInterface { address: string /** - * @returns Given contract address + * @returns {string} Given contract address */ getAddress: () => string /** - * @param method Method name - * @param args Method parameters + * @param {string} method Method name + * @param {any[]} args Method parameters * Runs the contract methods dynamically */ - callMethod: (method: string, ...args: any[]) => any + callMethod: (method: string, ...args: any[]) => Promise /** - * @param method Method name - * @param args Method parameters + * @param {string} method Method name + * @param {any[]} args Method parameters * To get information from called method * @returns Data used in transaction */ - getMethodData: (method: string, ...args: any[]) => any + getMethodData: (method: string, ...args: any[]) => Promise } export interface AssetInterface { /** - * @returns Name of the asset (long name) + * @returns {string} Name of the asset (long name) */ getName: () => string /** - * @returns Symbol of the asset (short name) + * @returns {string} Symbol of the asset (short name) */ getSymbol: () => string /** - * @param owner Address of the wallet - * @returns Wallet balance as currency of TOKEN or COIN assets + * @param {string} owner Address of the wallet + * @returns {Promise} Wallet balance as currency of TOKEN or COIN assets */ - getBalance: (owner: string) => number + getBalance: (owner: string) => Promise /** * transfer() method is the main method for processing transfers for fungible assets (TOKEN, COIN) - * @param sender Sender wallet address - * @param receiver Receiver wallet address - * @param amount Amount of assets that will be transferred - */ - transfer: (sender: string, receiver: string, amount: number) => TransactionSignerInterface + * @param {string} sender Sender wallet address + * @param {string} receiver Receiver wallet address + * @param {number} amount Amount of assets that will be transferred + * @returns {Promise} Transaction signer interface + */ + transfer: ( + sender: string, + receiver: string, + amount: number + ) => Promise } // Sub Interfaces export interface CoinInterface extends AssetInterface { /** - * @returns Decimal value of the coin + * @returns {number} Decimal value of the coin */ getDecimals: () => number } -export interface TokenInterface extends AssetInterface, ContractInterface { +export interface TokenInterface + extends Omit, + ContractInterface { /** - * @returns Decimal value of the token + * @returns {Promise} Name of the asset (long name) */ - getDecimals: () => number + getName: () => Promise /** - * @returns Total supply of the token + * @returns {Promise} Symbol of the asset (short name) */ - getTotalSupply: () => number + getSymbol: () => Promise /** - * Gives permission to the spender to spend owner's tokens - * @param owner Address of owner of the tokens that will be used - * @param spender Address of the spender that will use the tokens of owner - * @param amount Amount of the tokens that will be used + * @returns {Promise} Decimal value of the token + */ + getDecimals: () => Promise + + /** + * @returns {Promise} Total supply of the token + */ + getTotalSupply: () => Promise + + /** + * @param {string} owner Address of owner of the tokens that is being used + * @param {string} spender Address of the spender that is using the tokens of owner + * @returns {Promise} Amount of the tokens that is being used by spender */ - approve: (owner: string, spender: string, amount: number) => TransactionSignerInterface + getAllowance: (owner: string, spender: string) => Promise /** - * @param owner Address of owner of the tokens that is being used - * @param spender Address of the spender that is using the tokens of owner - * @returns Amount of the tokens that is being used by spender + * @param {string} spender Address of the spender of transaction + * @param {string} owner Sender wallet address + * @param {string} receiver Receiver wallet address + * @param {number} amount Amount of tokens that will be transferred + * @returns {Promise} Transaction signer interface */ - allowance: (owner: string, spender: string) => number + transferFrom: ( + spender: string, + owner: string, + receiver: string, + amount: number + ) => Promise + + /** + * Gives permission to the spender to spend owner's tokens + * @param {string} owner Address of owner of the tokens that will be used + * @param {string} spender Address of the spender that is using the tokens of owner + * @param {number} amount Amount of the tokens that will be used + * @returns {Promise} Transaction signer interface + */ + approve: (owner: string, spender: string, amount: number) => Promise } -export interface NftInterface extends Omit, ContractInterface { +export interface NftInterface + extends Omit, + ContractInterface { + /** + * @returns {Promise} Name of the asset (long name) + */ + getName: () => Promise + + /** + * @returns {Promise} Symbol of the asset (short name) + */ + getSymbol: () => Promise + + /** + * @param {number | string} nftId ID of the NFT + * @returns {Promise} Wallet address of owner of the NFT + */ + getOwner: (nftId: number | string) => Promise + /** - * @param nftId ID of the NFT - * @returns Wallet address of owner of the NFT + * @param {number | string} nftId ID of the NFT + * @returns {Promise} URL of the metadata */ - getOwner: (nftId: number) => string + getTokenURI: (nftId: number | string) => Promise /** - * @param nftId ID of the NFT - * @returns URL of the metadata + * @param {number | string} nftId ID of the NFT that will be transferred + * @returns {Promise} Amount of the tokens that is being used by spender */ - getTokenURI: (nftId: number) => string | URL + getApproved: (nftId: number | string) => Promise /** * Transfers an NFT - * @param sender Sender wallet address - * @param receiver Receiver wallet address - * @param nftId ID of the NFT that will be transferred + * @param {string} sender Sender wallet address + * @param {string} receiver Receiver wallet address + * @param {number | string} nftId ID of the NFT that will be transferred * @override transfer() in AssetInterface + * @returns {Promise} Transaction signer interface + */ + transfer: ( + sender: string, + receiver: string, + nftId: number | string + ) => Promise + + /** + * @param {string} spender Address of the spender of transaction + * @param {string} owner Address of owner of the nfts that will be used + * @param {string} receiver Address of the receiver that will receive the nfts + * @param {number | string} nftId ID of the NFT that will be transferred + * @returns {Promise} Transaction signer interface */ - transfer: (sender: string, receiver: string, nftId: number) => TransactionSignerInterface + transferFrom: ( + spender: string, + owner: string, + receiver: string, + nftId: number | string + ) => Promise + + /** + * Gives permission to the spender to spend owner's tokens + * @param {string} owner Address of owner of the tokens that will be used + * @param {string} spender Address of the spender that will use the tokens of owner + * @param {number | string} nftId ID of the NFT that will be transferred + */ + approve: ( + owner: string, + spender: string, + nftId: number | string + ) => Promise } diff --git a/packages/types/src/enums.ts b/packages/types/src/enums.ts index bad56da..7f01da7 100644 --- a/packages/types/src/enums.ts +++ b/packages/types/src/enums.ts @@ -2,8 +2,8 @@ * Asset transactions (COIN, TOKEN, NFT) has two directions */ export enum AssetDirectionEnum { - INCOMING, - OUTGOING + INCOMING = 'INCOMING', + OUTGOING = 'OUTGOING' } /** @@ -11,12 +11,11 @@ export enum AssetDirectionEnum { * COIN, TOKEN, and NFT transactions are called ASSET transactions */ export enum TransactionTypeEnum { - GENERAL, - CONTRACT, - ASSET, - COIN, - TOKEN, - NFT + GENERAL = 'GENERAL', + CONTRACT = 'CONTRACT', + COIN = 'COIN', + TOKEN = 'TOKEN', + NFT = 'NFT' } /** @@ -26,7 +25,30 @@ export enum TransactionTypeEnum { * CONFIRMED --> When a transaction is confirmed */ export enum TransactionStatusEnum { - FAILED, - PENDING, - CONFIRMED + FAILED = 'FAILED', + PENDING = 'PENDING', + CONFIRMED = 'CONFIRMED' +} + +export enum ErrorTypeEnum { + RPC_TIMEOUT = 'RPC_TIMEOUT', + RPC_REQUEST_ERROR = 'RPC_REQUEST_ERROR', + RPC_ACCESS_DENIED = 'RPC_ACCESS_DENIED', + UNACCEPTED_CHAIN = 'UNACCEPTED_CHAIN', + UNAUTHORIZED_ADDRESS = 'UNAUTHORIZED_ADDRESS', + INSUFFICIENT_BALANCE = 'INSUFFICIENT_BALANCE', + INVALID_AMOUNT = 'INVALID_AMOUNT', + INVALID_ADDRESS = 'INVALID_ADDRESS', + INVALID_PRIVATE_KEY = 'INVALID_PRIVATE_KEY', + INVALID_PUBLIC_KEY = 'INVALID_PUBLIC_KEY', + INVALID_TRANSACTION_ID = 'INVALID_TRANSACTION_ID', + PROVIDER_IS_NOT_INITIALIZED = 'PROVIDER_IS_NOT_INITIALIZED', + PROVIDER_IS_ALREADY_INITIALIZED = 'PROVIDER_IS_ALREADY_INITIALIZED', + WALLET_ALREADY_PROCESSING = 'WALLET_ALREADY_PROCESSING', + WALLET_CONNECT_REJECTED = 'WALLET_CONNECT_REJECTED', + WALLET_REQUEST_REJECTED = 'WALLET_REQUEST_REJECTED', + WALLET_CONNECTION_FAILED = 'WALLET_CONNECTION_FAILED', + WALLET_CONNECTION_TIMEOUT = 'WALLET_CONNECTION_TIMEOUT', + TRANSACTION_CREATION_FAILED = 'TRANSACTION_CREATION_FAILED', + CLOSED_WALLETCONNECT_MODAL = 'CLOSED_WALLETCONNECT_MODAL' } diff --git a/packages/types/src/models.ts b/packages/types/src/models.ts index 2682f85..9d8df91 100644 --- a/packages/types/src/models.ts +++ b/packages/types/src/models.ts @@ -7,116 +7,117 @@ export interface TransactionInterface { id: string /** - * @returns Raw transaction data that is taken by blockchain network via RPC. + * @returns {Promise} Promise of the transaction status */ - getData: () => object + wait: () => Promise /** - * @returns Transaction id from the blockchain network + * @returns {Promise} Raw transaction data that is taken by blockchain network via RPC. + */ + getData: () => Promise + + /** + * @returns {string} ID of the transaction * this can be different names like txid, hash, signature etc. */ getId: () => string /** - * @returns Blockchain explorer URL of the transaction. Dependant on network. + * @returns {string} Blockchain explorer URL of the transaction. Dependant on network. */ getUrl: () => string /** - * @returns Wallet address of the sender of transaction + * @returns {Promise} Wallet address of the signer of transaction */ - getSender: () => string + getSigner: () => Promise /** - * @returns Transaction fee as native coin amount + * @returns {Promise} Transaction fee as native coin amount */ - getFee: () => number + getFee: () => Promise /** - * @returns Block ID of the transaction + * @returns {Promise} Block ID of the transaction */ - getBlockNumber: () => number + getBlockNumber: () => Promise /** - * @returns UNIX timestamp of the date that block is added to blockchain + * @returns {Promise} UNIX timestamp of the date that block is added to blockchain */ - getBlockTimestamp: () => number + getBlockTimestamp: () => Promise /** - * @returns Block confirmation amount + * @returns {Promise} Block confirmation amount */ - getBlockConfirmationCount: () => number + getBlockConfirmationCount: () => Promise /** - * @returns Status of the transaction. + * @returns {Promise} Status of the transaction. */ - getStatus: () => TransactionStatusEnum + getStatus: () => Promise } export interface ContractTransactionInterface extends TransactionInterface { /** - * @returns Smart contract address of the transaction + * @returns {Promise} Smart contract address of the transaction */ - getAddress: () => string + getAddress: () => Promise } export interface AssetTransactionInterface extends TransactionInterface { /** - * @returns Receiver wallet address of the transaction (asset) + * @returns {Promise} Receiver wallet address of the transaction (asset) */ - getReceiver: () => string + getReceiver: () => Promise /** - * @returns Transfer amount of the transaction (coin) + * @returns {Promise} Wallet address of the sender of asset */ - getAmount: () => number + getSender: () => Promise /** - * @param direction - Direction of the transaction (asset) - * @param address - Wallet address of the receiver or sender of the transaction, dependant on direction - * @param amount Amount of assets that will be transferred + * @returns {Promise} Transfer amount of the transaction (coin) + */ + getAmount: () => Promise + + /** + * @param {AssetDirectionEnum} direction - Direction of the transaction (asset) + * @param {string} address - Wallet address of the receiver or sender of the transaction, dependant on direction + * @param {number} amount Amount of assets that will be transferred + * @returns {Promise} Status of the transaction */ verifyTransfer: ( direction: AssetDirectionEnum, address: string, amount: number - ) => TransactionStatusEnum + ) => Promise } export interface CoinTransactionInterface extends AssetTransactionInterface {} export interface TokenTransactionInterface extends AssetTransactionInterface, - ContractTransactionInterface { - /** - * @param direction - Direction of the transaction (token) - * @param address - Wallet address of the owner or spender of the transaction, dependant on direction - * @param amount Amount of tokens that will be approved - */ - verifyApprove: ( - direction: AssetDirectionEnum, - address: string, - amount: number - ) => TransactionStatusEnum -} + ContractTransactionInterface {} export interface NftTransactionInterface - extends Omit, + extends Omit, ContractTransactionInterface { /** - * @returns ID of the NFT + * @returns {Promise} ID of the NFT */ - getNftId: () => number + getNftId: () => Promise /** - * @param direction - Direction of the transaction (nft) - * @param address - Wallet address of the receiver or sender of the transaction, dependant on direction - * @param nftId ID of the NFT that will be transferred + * @param {AssetDirectionEnum} direction - Direction of the transaction (nft) + * @param {string} address - Wallet address of the receiver or sender of the transaction, dependant on direction + * @param {number | string} nftId ID of the NFT that will be transferred * @override verifyTransfer() in AssetTransactionInterface + * @returns {Promise} Status of the transaction */ verifyTransfer: ( direction: AssetDirectionEnum, address: string, - nftId: number - ) => TransactionStatusEnum + nftId: number | string + ) => Promise } diff --git a/packages/types/src/services/ProviderInterface.ts b/packages/types/src/services/ProviderInterface.ts index 231d62d..75ff200 100644 --- a/packages/types/src/services/ProviderInterface.ts +++ b/packages/types/src/services/ProviderInterface.ts @@ -17,11 +17,27 @@ export interface ProviderInterface { /** * Update network configuration of the provider + * @param {NetworkConfigInterface} network - Network configuration */ update: (network: NetworkConfigInterface) => void /** * Get the current network configuration is testnet or not + * @returns {boolean} */ isTestnet: () => boolean + + /** + * Check RPC connection + * @param {string} url - RPC URL + * @returns {Promise} + */ + checkRpcConnection: (url?: string) => Promise + + /** + * Check WS connection + * @param {string} url - Websocket URL + * @returns {Promise} + */ + checkWsConnection: (url?: string) => Promise } diff --git a/packages/types/src/services/TransactionListenerInterface.ts b/packages/types/src/services/TransactionListenerInterface.ts index 680c589..ff277ac 100644 --- a/packages/types/src/services/TransactionListenerInterface.ts +++ b/packages/types/src/services/TransactionListenerInterface.ts @@ -1,20 +1,18 @@ import type { TransactionInterface, ContractTransactionInterface, - AssetTransactionInterface, CoinTransactionInterface, TokenTransactionInterface, NftTransactionInterface } from '../models.ts' import { TransactionTypeEnum } from '../enums.ts' -import type { AssetDirectionEnum } from '../enums.ts' /** * Filter types for each transaction type in TransactionListenerInterface */ interface TransactionListenerFilterInterface { - sender?: string + signer?: string } interface ContractTransactionListenerFilterInterface extends TransactionListenerFilterInterface { @@ -22,12 +20,12 @@ interface ContractTransactionListenerFilterInterface extends TransactionListener } interface AssetTransactionListenerFilterInterface extends TransactionListenerFilterInterface { + sender?: string receiver?: string - direction?: AssetDirectionEnum } interface CoinTransactionListenerFilterInterface extends AssetTransactionListenerFilterInterface { - amount?: string + amount?: number } interface TokenTransactionListenerFilterInterface @@ -39,7 +37,7 @@ interface TokenTransactionListenerFilterInterface interface NftTransactionListenerFilterInterface extends AssetTransactionListenerFilterInterface, ContractTransactionListenerFilterInterface { - nftId?: string + nftId?: number | string } /** * Filter types for each transaction type in TransactionListenerInterface @@ -55,15 +53,13 @@ export type DynamicTransactionType = ? TransactionInterface : T extends TransactionTypeEnum.CONTRACT ? ContractTransactionInterface - : T extends TransactionTypeEnum.ASSET - ? AssetTransactionInterface - : T extends TransactionTypeEnum.COIN - ? CoinTransactionInterface - : T extends TransactionTypeEnum.TOKEN - ? TokenTransactionInterface - : T extends TransactionTypeEnum.NFT - ? NftTransactionInterface - : never + : T extends TransactionTypeEnum.COIN + ? CoinTransactionInterface + : T extends TransactionTypeEnum.TOKEN + ? TokenTransactionInterface + : T extends TransactionTypeEnum.NFT + ? NftTransactionInterface + : never /** * 'DynamicTransactionListenerFilterInterface' connects transaction types to their corresponding filter interfaces @@ -75,15 +71,13 @@ export type DynamicTransactionListenerFilterType ? TransactionListenerFilterInterface : T extends TransactionTypeEnum.CONTRACT ? ContractTransactionListenerFilterInterface - : T extends TransactionTypeEnum.ASSET - ? AssetTransactionListenerFilterInterface - : T extends TransactionTypeEnum.COIN - ? CoinTransactionListenerFilterInterface - : T extends TransactionTypeEnum.TOKEN - ? TokenTransactionListenerFilterInterface - : T extends TransactionTypeEnum.NFT - ? NftTransactionListenerFilterInterface - : never + : T extends TransactionTypeEnum.COIN + ? CoinTransactionListenerFilterInterface + : T extends TransactionTypeEnum.TOKEN + ? TokenTransactionListenerFilterInterface + : T extends TransactionTypeEnum.NFT + ? NftTransactionListenerFilterInterface + : never /** * 'TransactionListenerProcessIndex' is an object that connects transaction types to their corresponding process methods. @@ -92,7 +86,6 @@ export type DynamicTransactionListenerFilterType export const TransactionListenerProcessIndex = { [TransactionTypeEnum.GENERAL]: 'generalProcess', [TransactionTypeEnum.CONTRACT]: 'contractProcess', - [TransactionTypeEnum.ASSET]: 'assetProcess', [TransactionTypeEnum.COIN]: 'coinProcess', [TransactionTypeEnum.TOKEN]: 'tokenProcess', [TransactionTypeEnum.NFT]: 'nftProcess' @@ -117,27 +110,49 @@ export interface TransactionListenerInterface { */ callbacks: TransactionListenerCallbackType[] + /** + * 'status' is a boolean that shows the status of the listener. + * If 'status' is true, the listener is active. + * If 'status' is false, the listener is inactive. + */ + status: boolean + /** * 'filter' is an object that has values depending on transaction listener type. * E.g. no matter which type of transaction is listening, 'filter' has to have a 'sender' value */ - filter?: DynamicTransactionListenerFilterType + filter?: DynamicTransactionListenerFilterType | Record /** * stop() method closes the corresponding listener of the instance it's called from. + * @returns {void} */ stop: () => void + /** + * start() method starts the corresponding listener of the instance it's called from. + * @returns {void} + */ + start: () => void + + /** + * getStatus() method returns the status of the listener. + * @returns {boolean} + */ + getStatus: () => boolean + /** * on() method is a listener that listens to the transaction events. * When a transaction is detected, it triggers the event. - * @param callback - a function that is triggered when a transaction is detected. + * @param {TransactionListenerCallbackType} callback - a function that is triggered when a transaction is detected. + * @return {Promise} */ - on: (callback: TransactionListenerCallbackType) => void + on: (callback: TransactionListenerCallbackType) => Promise /** * trigger() method triggers the event when a transaction is detected. - * @param transaction - the transaction that is detected + * @param {DynamicTransactionType} transaction - a transaction that is detected. + * @return {void} */ trigger: (transaction: DynamicTransactionType) => void @@ -148,8 +163,6 @@ export interface TransactionListenerInterface { contractProcess: () => void - assetProcess: () => void - coinProcess: () => void tokenProcess: () => void diff --git a/packages/types/src/services/TransactionSignerInterface.ts b/packages/types/src/services/TransactionSignerInterface.ts index 8f01f34..4966879 100644 --- a/packages/types/src/services/TransactionSignerInterface.ts +++ b/packages/types/src/services/TransactionSignerInterface.ts @@ -16,22 +16,22 @@ export interface TransactionSignerInterface { signedData?: any /** - * @param privateKey - Private key of the wallet to sign the transaction + * @param {string} privateKey - Private key of the wallet to sign the transaction */ - sign: (privateKey: string) => TransactionSignerInterface + sign: (privateKey: string) => Promise /** - * @returns Send the transaction to the blockchain network, returns a promise of the transaction + * @returns {Promise} Send the transaction to the blockchain network, returns a promise of the transaction */ send: () => Promise /** - * @returns Unsigned transaction raw data + * @returns {any} Unsigned transaction raw data */ getRawData: () => any /** - * @returns Signed transaction data + * @returns {any} Signed transaction data */ getSignedData: () => any } diff --git a/packages/utils/package-lock.json b/packages/utils/package-lock.json new file mode 100644 index 0000000..7c5a1fe --- /dev/null +++ b/packages/utils/package-lock.json @@ -0,0 +1,483 @@ +{ + "name": "@multiplechain/utils", + "version": "0.1.13", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@multiplechain/utils", + "version": "0.1.13", + "license": "MIT", + "dependencies": { + "@types/ws": "^8.5.10", + "bignumber.js": "^9.1.2", + "web3-utils": "^4.2.0", + "ws": "^8.16.0" + } + }, + "node_modules/@noble/curves": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.3.0.tgz", + "integrity": "sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==", + "dependencies": { + "@noble/hashes": "1.3.3" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz", + "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/base": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.6.tgz", + "integrity": "sha512-ok9AWwhcgYuGG3Zfhyqg+zwl+Wn5uE+dwC0NV/2qQkx4dABbb/bx96vWu8NSj+BNjjSjno+JRYRjle1jV08k3g==", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.3.tgz", + "integrity": "sha512-LJaN3HwRbfQK0X1xFSi0Q9amqOgzQnnDngIt+ZlsBC3Bm7/nE7K0kwshZHyaru79yIVRv/e1mQAjZyuZG6jOFQ==", + "dependencies": { + "@noble/curves": "~1.3.0", + "@noble/hashes": "~1.3.2", + "@scure/base": "~1.1.4" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.2.tgz", + "integrity": "sha512-HYf9TUXG80beW+hGAt3TRM8wU6pQoYur9iNypTROm42dorCGmLnFe3eWjz3gOq6G62H2WRh0FCzAR1PI+29zIA==", + "dependencies": { + "@noble/hashes": "~1.3.2", + "@scure/base": "~1.1.4" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@types/node": { + "version": "20.12.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.2.tgz", + "integrity": "sha512-zQ0NYO87hyN6Xrclcqp7f8ZbXNbRfoGWNcMvHTPQp9UUrwI0mI7XBz+cu7/W6/VClYo2g63B0cjull/srU7LgQ==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/ws": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "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/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "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/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ethereum-cryptography": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.3.tgz", + "integrity": "sha512-BlwbIL7/P45W8FGW2r7LGuvoEZ+7PWsniMvQ4p5s2xCyw9tmaDlpfsN9HjAucbF+t/qpVHwZUisgfK24TCW8aA==", + "dependencies": { + "@noble/curves": "1.3.0", + "@noble/hashes": "1.3.3", + "@scure/bip32": "1.3.3", + "@scure/bip39": "1.2.2" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "dependencies": { + "which-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-length": { + "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==", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/web3-errors": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/web3-errors/-/web3-errors-1.1.4.tgz", + "integrity": "sha512-WahtszSqILez+83AxGecVroyZsMuuRT+KmQp4Si5P4Rnqbczno1k748PCrZTS1J4UCPmXMG2/Vt+0Bz2zwXkwQ==", + "dependencies": { + "web3-types": "^1.3.1" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-types": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/web3-types/-/web3-types-1.5.0.tgz", + "integrity": "sha512-geWuMIeegQ8AedKAO6wO4G4j1gyQ1F/AyKLMw2vud4bsfZayyzWJgCMDZtjYMm5uo2a7i8j1W3/4QFmzlSy5cw==", + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-utils": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-4.2.2.tgz", + "integrity": "sha512-z+4owWcnoB4EH8yWIL1FBeyqe+sXwaGxUDtVTNPTMf2oB5C+paCToZUdCV5Bi+M543zZEzlzNTabOD+OWNc7NA==", + "dependencies": { + "ethereum-cryptography": "^2.0.0", + "eventemitter3": "^5.0.1", + "web3-errors": "^1.1.4", + "web3-types": "^1.5.0", + "web3-validator": "^2.0.5" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-validator": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/web3-validator/-/web3-validator-2.0.5.tgz", + "integrity": "sha512-2gLOSW8XqEN5pw5jVUm20EB7A8SbQiekpAtiI0JBmCIV0a2rp97v8FgWY5E3UEqnw5WFfEqvcDVW92EyynDTyQ==", + "dependencies": { + "ethereum-cryptography": "^2.0.0", + "util": "^0.12.5", + "web3-errors": "^1.1.4", + "web3-types": "^1.5.0", + "zod": "^3.21.4" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ws": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/zod": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", + "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/packages/utils/package.json b/packages/utils/package.json index 855bd4f..54b9a49 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@multiplechain/utils", - "version": "0.1.7", + "version": "0.1.16", "type": "module", "main": "./src/index.ts", "types": "./src/index.ts", @@ -31,7 +31,9 @@ "url": "https://github.com/MultipleChain/js/issues" }, "dependencies": { + "@types/ws": "^8.5.10", "bignumber.js": "^9.1.2", - "web3-utils": "^4.2.0" + "web3-utils": "^4.2.0", + "ws": "^8.16.0" } } diff --git a/packages/utils/pnpm-lock.yaml b/packages/utils/pnpm-lock.yaml index 3ba3f9f..0f511d4 100644 --- a/packages/utils/pnpm-lock.yaml +++ b/packages/utils/pnpm-lock.yaml @@ -5,12 +5,18 @@ settings: excludeLinksFromLockfile: false dependencies: + '@types/ws': + specifier: ^8.5.10 + version: 8.5.10 bignumber.js: specifier: ^9.1.2 version: 9.1.2 web3-utils: specifier: ^4.2.0 version: 4.2.0 + ws: + specifier: ^8.16.0 + version: 8.16.0 packages: @@ -44,6 +50,18 @@ packages: '@scure/base': 1.1.5 dev: false + /@types/node@20.12.2: + resolution: {integrity: sha512-zQ0NYO87hyN6Xrclcqp7f8ZbXNbRfoGWNcMvHTPQp9UUrwI0mI7XBz+cu7/W6/VClYo2g63B0cjull/srU7LgQ==} + dependencies: + undici-types: 5.26.5 + dev: false + + /@types/ws@8.5.10: + resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==} + dependencies: + '@types/node': 20.12.2 + dev: false + /available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} @@ -201,6 +219,10 @@ packages: has-property-descriptors: 1.0.2 dev: false + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + dev: false + /util@0.12.5: resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} dependencies: @@ -255,6 +277,19 @@ packages: has-tostringtag: 1.0.2 dev: false + /ws@8.16.0: + resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dev: false + /zod@3.22.4: resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} dev: false diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index f42a279..8da26e5 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -1,5 +1,6 @@ import { toHex } from 'web3-utils' import BigNumber from 'bignumber.js' +import { WebSocket as NodeWebSocket, type ErrorEvent } from 'ws' const BASE58_ALPHABET: string = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' @@ -88,4 +89,78 @@ export const isNumeric = (value: string | number): boolean => { return !isNaN(Number(value)) } +/** + * Get the number of decimal places + */ +export const getDecimalPlaces = (num: number): number => { + const match = ('' + num).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/) + if (match === null) { + return num + } else { + return Math.max( + 0, + // Number of digits right of decimal point. + (match[1] !== undefined && match[1] !== null ? match[1].length : 0) - + // Adjust for scientific notation. + (match[2] !== undefined && match[2] !== null ? +match[2] : 0) + ) + } +} + +/** + * Fix the float number + * @param {number} num + * @returns number + * @example 1.0000000000000001 => 1 + */ +export const fixFloat = (num: number): number => { + return parseFloat(num.toFixed(getDecimalPlaces(num))) +} + +/** + * Sleeps the given milliseconds + * @param {number} ms + * @returns Promise + */ +export const sleep = async (ms: number): Promise => { + await new Promise((resolve) => setTimeout(resolve, ms)) +} + +/** + * Checks if the given objects are equal + * @param {object} o1 + * @param {object} o2 + * @returns boolean + */ +export const objectsEqual = (o1: object, o2: object): boolean => { + return JSON.stringify(o1) === JSON.stringify(o2) +} + +/** + * checks if the given url is a valid websocket url + * @param {string} url + * @returns {Promise} + */ +export const checkWebSocket = async (url: string): Promise => { + return await new Promise((resolve, reject) => { + let socket: WebSocket | NodeWebSocket + + if (typeof window !== 'undefined') { + socket = new WebSocket(url) + } else { + socket = new NodeWebSocket(url) + } + + socket.onopen = () => { + resolve(true) + socket.close() + } + + socket.onerror = (error: ErrorEvent) => { + reject(new Error(error.message)) + socket.close() + } + }) +} + export { toHex } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 21409da..bbf5a21 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,14 +4,6 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false -dependencies: - abi-decoder: - specifier: ^2.4.0 - version: 2.4.0 - ethers: - specifier: ^6.11.1 - version: 6.11.1 - devDependencies: '@types/node': specifier: ^20.11.20 @@ -19,6 +11,9 @@ devDependencies: '@typescript-eslint/eslint-plugin': specifier: ^6.21.0 version: 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.57.0)(typescript@5.3.3) + esbuild: + specifier: ^0.20.2 + version: 0.20.2 eslint: specifier: ^8.57.0 version: 8.57.0 @@ -52,12 +47,21 @@ devDependencies: prettier: specifier: ^3.2.5 version: 3.2.5 + tsx: + specifier: ^4.7.1 + version: 4.7.1 typescript: specifier: ^5.3.3 version: 5.3.3 vite: specifier: ^5.1.4 version: 5.1.4(@types/node@20.11.20) + vite-plugin-dts: + specifier: ^3.8.1 + version: 3.8.1(@types/node@20.11.20)(typescript@5.3.3)(vite@5.1.4) + vite-plugin-env-compatible: + specifier: ^2.0.1 + version: 2.0.1 vite-plugin-node-polyfills: specifier: ^0.21.0 version: 0.21.0(vite@5.1.4) @@ -72,9 +76,32 @@ packages: engines: {node: '>=0.10.0'} dev: true - /@adraffy/ens-normalize@1.10.1: - resolution: {integrity: sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==} - dev: false + /@babel/helper-string-parser@7.24.1: + resolution: {integrity: sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-validator-identifier@7.22.20: + resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/parser@7.24.1: + resolution: {integrity: sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.24.0 + dev: true + + /@babel/types@7.24.0: + resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.24.1 + '@babel/helper-validator-identifier': 7.22.20 + to-fast-properties: 2.0.0 + dev: true /@esbuild/aix-ppc64@0.19.12: resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} @@ -85,6 +112,15 @@ packages: dev: true optional: true + /@esbuild/aix-ppc64@0.20.2: + resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + requiresBuild: true + dev: true + optional: true + /@esbuild/android-arm64@0.19.12: resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==} engines: {node: '>=12'} @@ -94,6 +130,15 @@ packages: dev: true optional: true + /@esbuild/android-arm64@0.20.2: + resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + /@esbuild/android-arm@0.19.12: resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==} engines: {node: '>=12'} @@ -103,6 +148,15 @@ packages: dev: true optional: true + /@esbuild/android-arm@0.20.2: + resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + /@esbuild/android-x64@0.19.12: resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==} engines: {node: '>=12'} @@ -112,6 +166,15 @@ packages: dev: true optional: true + /@esbuild/android-x64@0.20.2: + resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + /@esbuild/darwin-arm64@0.19.12: resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==} engines: {node: '>=12'} @@ -121,6 +184,15 @@ packages: dev: true optional: true + /@esbuild/darwin-arm64@0.20.2: + resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@esbuild/darwin-x64@0.19.12: resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==} engines: {node: '>=12'} @@ -130,6 +202,15 @@ packages: dev: true optional: true + /@esbuild/darwin-x64@0.20.2: + resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@esbuild/freebsd-arm64@0.19.12: resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==} engines: {node: '>=12'} @@ -139,6 +220,15 @@ packages: dev: true optional: true + /@esbuild/freebsd-arm64@0.20.2: + resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/freebsd-x64@0.19.12: resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==} engines: {node: '>=12'} @@ -148,6 +238,15 @@ packages: dev: true optional: true + /@esbuild/freebsd-x64@0.20.2: + resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-arm64@0.19.12: resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==} engines: {node: '>=12'} @@ -157,6 +256,15 @@ packages: dev: true optional: true + /@esbuild/linux-arm64@0.20.2: + resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-arm@0.19.12: resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==} engines: {node: '>=12'} @@ -166,6 +274,15 @@ packages: dev: true optional: true + /@esbuild/linux-arm@0.20.2: + resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-ia32@0.19.12: resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==} engines: {node: '>=12'} @@ -175,6 +292,15 @@ packages: dev: true optional: true + /@esbuild/linux-ia32@0.20.2: + resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-loong64@0.19.12: resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==} engines: {node: '>=12'} @@ -184,6 +310,15 @@ packages: dev: true optional: true + /@esbuild/linux-loong64@0.20.2: + resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-mips64el@0.19.12: resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==} engines: {node: '>=12'} @@ -193,6 +328,15 @@ packages: dev: true optional: true + /@esbuild/linux-mips64el@0.20.2: + resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-ppc64@0.19.12: resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==} engines: {node: '>=12'} @@ -202,6 +346,15 @@ packages: dev: true optional: true + /@esbuild/linux-ppc64@0.20.2: + resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-riscv64@0.19.12: resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==} engines: {node: '>=12'} @@ -211,6 +364,15 @@ packages: dev: true optional: true + /@esbuild/linux-riscv64@0.20.2: + resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-s390x@0.19.12: resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==} engines: {node: '>=12'} @@ -220,6 +382,15 @@ packages: dev: true optional: true + /@esbuild/linux-s390x@0.20.2: + resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-x64@0.19.12: resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==} engines: {node: '>=12'} @@ -229,6 +400,15 @@ packages: dev: true optional: true + /@esbuild/linux-x64@0.20.2: + resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/netbsd-x64@0.19.12: resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==} engines: {node: '>=12'} @@ -238,6 +418,15 @@ packages: dev: true optional: true + /@esbuild/netbsd-x64@0.20.2: + resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/openbsd-x64@0.19.12: resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==} engines: {node: '>=12'} @@ -247,6 +436,15 @@ packages: dev: true optional: true + /@esbuild/openbsd-x64@0.20.2: + resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/sunos-x64@0.19.12: resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==} engines: {node: '>=12'} @@ -256,6 +454,15 @@ packages: dev: true optional: true + /@esbuild/sunos-x64@0.20.2: + resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + /@esbuild/win32-arm64@0.19.12: resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==} engines: {node: '>=12'} @@ -265,6 +472,15 @@ packages: dev: true optional: true + /@esbuild/win32-arm64@0.20.2: + resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@esbuild/win32-ia32@0.19.12: resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==} engines: {node: '>=12'} @@ -274,6 +490,15 @@ packages: dev: true optional: true + /@esbuild/win32-ia32@0.20.2: + resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@esbuild/win32-x64@0.19.12: resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==} engines: {node: '>=12'} @@ -283,6 +508,15 @@ packages: dev: true optional: true + /@esbuild/win32-x64@0.20.2: + resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@eslint-community/eslint-utils@4.4.0(eslint@8.57.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -320,180 +554,6 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@ethereumjs/rlp@4.0.1: - resolution: {integrity: sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==} - engines: {node: '>=14'} - hasBin: true - dev: false - - /@ethereumjs/util@8.1.0: - resolution: {integrity: sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==} - engines: {node: '>=14'} - dependencies: - '@ethereumjs/rlp': 4.0.1 - ethereum-cryptography: 2.1.3 - micro-ftch: 0.3.1 - dev: false - - /@ethersproject/abi@5.7.0: - resolution: {integrity: sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==} - dependencies: - '@ethersproject/address': 5.7.0 - '@ethersproject/bignumber': 5.7.0 - '@ethersproject/bytes': 5.7.0 - '@ethersproject/constants': 5.7.0 - '@ethersproject/hash': 5.7.0 - '@ethersproject/keccak256': 5.7.0 - '@ethersproject/logger': 5.7.0 - '@ethersproject/properties': 5.7.0 - '@ethersproject/strings': 5.7.0 - dev: false - - /@ethersproject/abstract-provider@5.7.0: - resolution: {integrity: sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==} - dependencies: - '@ethersproject/bignumber': 5.7.0 - '@ethersproject/bytes': 5.7.0 - '@ethersproject/logger': 5.7.0 - '@ethersproject/networks': 5.7.1 - '@ethersproject/properties': 5.7.0 - '@ethersproject/transactions': 5.7.0 - '@ethersproject/web': 5.7.1 - dev: false - - /@ethersproject/abstract-signer@5.7.0: - resolution: {integrity: sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==} - dependencies: - '@ethersproject/abstract-provider': 5.7.0 - '@ethersproject/bignumber': 5.7.0 - '@ethersproject/bytes': 5.7.0 - '@ethersproject/logger': 5.7.0 - '@ethersproject/properties': 5.7.0 - dev: false - - /@ethersproject/address@5.7.0: - resolution: {integrity: sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==} - dependencies: - '@ethersproject/bignumber': 5.7.0 - '@ethersproject/bytes': 5.7.0 - '@ethersproject/keccak256': 5.7.0 - '@ethersproject/logger': 5.7.0 - '@ethersproject/rlp': 5.7.0 - dev: false - - /@ethersproject/base64@5.7.0: - resolution: {integrity: sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==} - dependencies: - '@ethersproject/bytes': 5.7.0 - dev: false - - /@ethersproject/bignumber@5.7.0: - resolution: {integrity: sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==} - dependencies: - '@ethersproject/bytes': 5.7.0 - '@ethersproject/logger': 5.7.0 - bn.js: 5.2.1 - dev: false - - /@ethersproject/bytes@5.7.0: - resolution: {integrity: sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==} - dependencies: - '@ethersproject/logger': 5.7.0 - dev: false - - /@ethersproject/constants@5.7.0: - resolution: {integrity: sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==} - dependencies: - '@ethersproject/bignumber': 5.7.0 - dev: false - - /@ethersproject/hash@5.7.0: - resolution: {integrity: sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==} - dependencies: - '@ethersproject/abstract-signer': 5.7.0 - '@ethersproject/address': 5.7.0 - '@ethersproject/base64': 5.7.0 - '@ethersproject/bignumber': 5.7.0 - '@ethersproject/bytes': 5.7.0 - '@ethersproject/keccak256': 5.7.0 - '@ethersproject/logger': 5.7.0 - '@ethersproject/properties': 5.7.0 - '@ethersproject/strings': 5.7.0 - dev: false - - /@ethersproject/keccak256@5.7.0: - resolution: {integrity: sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==} - dependencies: - '@ethersproject/bytes': 5.7.0 - js-sha3: 0.8.0 - dev: false - - /@ethersproject/logger@5.7.0: - resolution: {integrity: sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==} - dev: false - - /@ethersproject/networks@5.7.1: - resolution: {integrity: sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==} - dependencies: - '@ethersproject/logger': 5.7.0 - dev: false - - /@ethersproject/properties@5.7.0: - resolution: {integrity: sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==} - dependencies: - '@ethersproject/logger': 5.7.0 - dev: false - - /@ethersproject/rlp@5.7.0: - resolution: {integrity: sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==} - dependencies: - '@ethersproject/bytes': 5.7.0 - '@ethersproject/logger': 5.7.0 - dev: false - - /@ethersproject/signing-key@5.7.0: - resolution: {integrity: sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==} - dependencies: - '@ethersproject/bytes': 5.7.0 - '@ethersproject/logger': 5.7.0 - '@ethersproject/properties': 5.7.0 - bn.js: 5.2.1 - elliptic: 6.5.4 - hash.js: 1.1.7 - dev: false - - /@ethersproject/strings@5.7.0: - resolution: {integrity: sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==} - dependencies: - '@ethersproject/bytes': 5.7.0 - '@ethersproject/constants': 5.7.0 - '@ethersproject/logger': 5.7.0 - dev: false - - /@ethersproject/transactions@5.7.0: - resolution: {integrity: sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==} - dependencies: - '@ethersproject/address': 5.7.0 - '@ethersproject/bignumber': 5.7.0 - '@ethersproject/bytes': 5.7.0 - '@ethersproject/constants': 5.7.0 - '@ethersproject/keccak256': 5.7.0 - '@ethersproject/logger': 5.7.0 - '@ethersproject/properties': 5.7.0 - '@ethersproject/rlp': 5.7.0 - '@ethersproject/signing-key': 5.7.0 - dev: false - - /@ethersproject/web@5.7.1: - resolution: {integrity: sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==} - dependencies: - '@ethersproject/base64': 5.7.0 - '@ethersproject/bytes': 5.7.0 - '@ethersproject/logger': 5.7.0 - '@ethersproject/properties': 5.7.0 - '@ethersproject/strings': 5.7.0 - dev: false - /@humanwhocodes/config-array@0.11.14: resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} @@ -525,27 +585,49 @@ packages: resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} dev: true - /@noble/curves@1.2.0: - resolution: {integrity: sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==} + /@microsoft/api-extractor-model@7.28.13(@types/node@20.11.20): + resolution: {integrity: sha512-39v/JyldX4MS9uzHcdfmjjfS6cYGAoXV+io8B5a338pkHiSt+gy2eXQ0Q7cGFJ7quSa1VqqlMdlPrB6sLR/cAw==} dependencies: - '@noble/hashes': 1.3.2 - dev: false + '@microsoft/tsdoc': 0.14.2 + '@microsoft/tsdoc-config': 0.16.2 + '@rushstack/node-core-library': 4.0.2(@types/node@20.11.20) + transitivePeerDependencies: + - '@types/node' + dev: true - /@noble/curves@1.3.0: - resolution: {integrity: sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==} + /@microsoft/api-extractor@7.43.0(@types/node@20.11.20): + resolution: {integrity: sha512-GFhTcJpB+MI6FhvXEI9b2K0snulNLWHqC/BbcJtyNYcKUiw7l3Lgis5ApsYncJ0leALX7/of4XfmXk+maT111w==} + hasBin: true dependencies: - '@noble/hashes': 1.3.3 - dev: false + '@microsoft/api-extractor-model': 7.28.13(@types/node@20.11.20) + '@microsoft/tsdoc': 0.14.2 + '@microsoft/tsdoc-config': 0.16.2 + '@rushstack/node-core-library': 4.0.2(@types/node@20.11.20) + '@rushstack/rig-package': 0.5.2 + '@rushstack/terminal': 0.10.0(@types/node@20.11.20) + '@rushstack/ts-command-line': 4.19.1(@types/node@20.11.20) + lodash: 4.17.21 + minimatch: 3.0.8 + resolve: 1.22.8 + semver: 7.5.4 + source-map: 0.6.1 + typescript: 5.4.2 + transitivePeerDependencies: + - '@types/node' + dev: true - /@noble/hashes@1.3.2: - resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==} - engines: {node: '>= 16'} - dev: false + /@microsoft/tsdoc-config@0.16.2: + resolution: {integrity: sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==} + dependencies: + '@microsoft/tsdoc': 0.14.2 + ajv: 6.12.6 + jju: 1.4.0 + resolve: 1.19.0 + dev: true - /@noble/hashes@1.3.3: - resolution: {integrity: sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==} - engines: {node: '>= 16'} - dev: false + /@microsoft/tsdoc@0.14.2: + resolution: {integrity: sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==} + dev: true /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} @@ -705,29 +787,62 @@ packages: dev: true optional: true - /@scure/base@1.1.5: - resolution: {integrity: sha512-Brj9FiG2W1MRQSTB212YVPRrcbjkv48FoZi/u4l/zds/ieRrqsh7aUf6CLwkAq61oKXr/ZlTzlY66gLIj3TFTQ==} - dev: false + /@rushstack/node-core-library@4.0.2(@types/node@20.11.20): + resolution: {integrity: sha512-hyES82QVpkfQMeBMteQUnrhASL/KHPhd7iJ8euduwNJG4mu2GSOKybf0rOEjOm1Wz7CwJEUm9y0yD7jg2C1bfg==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + dependencies: + '@types/node': 20.11.20 + fs-extra: 7.0.1 + import-lazy: 4.0.0 + jju: 1.4.0 + resolve: 1.22.8 + semver: 7.5.4 + z-schema: 5.0.5 + dev: true - /@scure/bip32@1.3.3: - resolution: {integrity: sha512-LJaN3HwRbfQK0X1xFSi0Q9amqOgzQnnDngIt+ZlsBC3Bm7/nE7K0kwshZHyaru79yIVRv/e1mQAjZyuZG6jOFQ==} + /@rushstack/rig-package@0.5.2: + resolution: {integrity: sha512-mUDecIJeH3yYGZs2a48k+pbhM6JYwWlgjs2Ca5f2n1G2/kgdgP9D/07oglEGf6mRyXEnazhEENeYTSNDRCwdqA==} + dependencies: + resolve: 1.22.8 + strip-json-comments: 3.1.1 + dev: true + + /@rushstack/terminal@0.10.0(@types/node@20.11.20): + resolution: {integrity: sha512-UbELbXnUdc7EKwfH2sb8ChqNgapUOdqcCIdQP4NGxBpTZV2sQyeekuK3zmfQSa/MN+/7b4kBogl2wq0vpkpYGw==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true dependencies: - '@noble/curves': 1.3.0 - '@noble/hashes': 1.3.3 - '@scure/base': 1.1.5 - dev: false + '@rushstack/node-core-library': 4.0.2(@types/node@20.11.20) + '@types/node': 20.11.20 + supports-color: 8.1.1 + dev: true - /@scure/bip39@1.2.2: - resolution: {integrity: sha512-HYf9TUXG80beW+hGAt3TRM8wU6pQoYur9iNypTROm42dorCGmLnFe3eWjz3gOq6G62H2WRh0FCzAR1PI+29zIA==} + /@rushstack/ts-command-line@4.19.1(@types/node@20.11.20): + resolution: {integrity: sha512-J7H768dgcpG60d7skZ5uSSwyCZs/S2HrWP1Ds8d1qYAyaaeJmpmmLr9BVw97RjFzmQPOYnoXcKA4GkqDCkduQg==} dependencies: - '@noble/hashes': 1.3.3 - '@scure/base': 1.1.5 - dev: false + '@rushstack/terminal': 0.10.0(@types/node@20.11.20) + '@types/argparse': 1.0.38 + argparse: 1.0.10 + string-argv: 0.3.2 + transitivePeerDependencies: + - '@types/node' + dev: true /@sinclair/typebox@0.27.8: resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} dev: true + /@types/argparse@1.0.38: + resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} + dev: true + /@types/estree@1.0.5: resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} dev: true @@ -740,10 +855,6 @@ packages: resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} dev: true - /@types/node@18.15.13: - resolution: {integrity: sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==} - dev: false - /@types/node@20.11.20: resolution: {integrity: sha512-7/rR21OS+fq8IyHTgtLkDK949uzsa6n8BkziAKtPVpugIkO6D+/ooXMvzXxDnZrmtXVfjb1bKQafYpb8s89LOg==} dependencies: @@ -929,12 +1040,65 @@ packages: pretty-format: 29.7.0 dev: true - /abi-decoder@2.4.0: - resolution: {integrity: sha512-TOLU2q1HgYOjs1GKGtVzaqrYkar6I2fT9a80rzx6/9EJ/5crb4nCGuro0grZayixem93T7omrajYmLiMkYDLDA==} + /@volar/language-core@1.11.1: + resolution: {integrity: sha512-dOcNn3i9GgZAcJt43wuaEykSluAuOkQgzni1cuxLxTV0nJKanQztp7FxyswdRILaKH+P2XZMPRp2S4MV/pElCw==} dependencies: - web3-eth-abi: 1.10.4 - web3-utils: 1.10.4 - dev: false + '@volar/source-map': 1.11.1 + dev: true + + /@volar/source-map@1.11.1: + resolution: {integrity: sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg==} + dependencies: + muggle-string: 0.3.1 + dev: true + + /@volar/typescript@1.11.1: + resolution: {integrity: sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ==} + dependencies: + '@volar/language-core': 1.11.1 + path-browserify: 1.0.1 + dev: true + + /@vue/compiler-core@3.4.21: + resolution: {integrity: sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==} + dependencies: + '@babel/parser': 7.24.1 + '@vue/shared': 3.4.21 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.0.2 + dev: true + + /@vue/compiler-dom@3.4.21: + resolution: {integrity: sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==} + dependencies: + '@vue/compiler-core': 3.4.21 + '@vue/shared': 3.4.21 + dev: true + + /@vue/language-core@1.8.27(typescript@5.3.3): + resolution: {integrity: sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@volar/language-core': 1.11.1 + '@volar/source-map': 1.11.1 + '@vue/compiler-dom': 3.4.21 + '@vue/shared': 3.4.21 + computeds: 0.0.1 + minimatch: 9.0.3 + muggle-string: 0.3.1 + path-browserify: 1.0.1 + typescript: 5.3.3 + vue-template-compiler: 2.7.16 + dev: true + + /@vue/shared@3.4.21: + resolution: {integrity: sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==} + dev: true /acorn-jsx@5.3.2(acorn@8.11.3): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} @@ -955,10 +1119,6 @@ packages: hasBin: true dev: true - /aes-js@4.0.0-beta.5: - resolution: {integrity: sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==} - dev: false - /agent-base@7.1.0: resolution: {integrity: sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==} engines: {node: '>= 14'} @@ -994,6 +1154,12 @@ packages: engines: {node: '>=10'} dev: true + /argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + dependencies: + sprintf-js: 1.0.3 + dev: true + /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} dev: true @@ -1120,15 +1286,13 @@ packages: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} dev: true - /bn.js@4.11.6: - resolution: {integrity: sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==} - dev: false - /bn.js@4.12.0: resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==} + dev: true /bn.js@5.2.1: resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==} + dev: true /brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} @@ -1152,6 +1316,7 @@ packages: /brorand@1.1.0: resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} + dev: true /browser-resolve@2.0.0: resolution: {integrity: sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==} @@ -1314,6 +1479,17 @@ packages: delayed-stream: 1.0.0 dev: true + /commander@9.5.0: + resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} + engines: {node: ^12.20.0 || >=14} + requiresBuild: true + dev: true + optional: true + + /computeds@0.0.1: + resolution: {integrity: sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==} + dev: true + /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} dev: true @@ -1398,6 +1574,10 @@ packages: whatwg-url: 14.0.0 dev: true + /de-indent@1.0.2: + resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} + dev: true + /debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -1505,6 +1685,15 @@ packages: engines: {node: '>=10'} dev: true + /dotenv-expand@5.1.0: + resolution: {integrity: sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==} + dev: true + + /dotenv@8.2.0: + resolution: {integrity: sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==} + engines: {node: '>=8'} + dev: true + /elliptic@6.5.4: resolution: {integrity: sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==} dependencies: @@ -1515,6 +1704,7 @@ packages: inherits: 2.0.4 minimalistic-assert: 1.0.1 minimalistic-crypto-utils: 1.0.1 + dev: true /entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} @@ -1639,6 +1829,37 @@ packages: '@esbuild/win32-x64': 0.19.12 dev: true + /esbuild@0.20.2: + resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/aix-ppc64': 0.20.2 + '@esbuild/android-arm': 0.20.2 + '@esbuild/android-arm64': 0.20.2 + '@esbuild/android-x64': 0.20.2 + '@esbuild/darwin-arm64': 0.20.2 + '@esbuild/darwin-x64': 0.20.2 + '@esbuild/freebsd-arm64': 0.20.2 + '@esbuild/freebsd-x64': 0.20.2 + '@esbuild/linux-arm': 0.20.2 + '@esbuild/linux-arm64': 0.20.2 + '@esbuild/linux-ia32': 0.20.2 + '@esbuild/linux-loong64': 0.20.2 + '@esbuild/linux-mips64el': 0.20.2 + '@esbuild/linux-ppc64': 0.20.2 + '@esbuild/linux-riscv64': 0.20.2 + '@esbuild/linux-s390x': 0.20.2 + '@esbuild/linux-x64': 0.20.2 + '@esbuild/netbsd-x64': 0.20.2 + '@esbuild/openbsd-x64': 0.20.2 + '@esbuild/sunos-x64': 0.20.2 + '@esbuild/win32-arm64': 0.20.2 + '@esbuild/win32-ia32': 0.20.2 + '@esbuild/win32-x64': 0.20.2 + dev: true + /escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} @@ -1959,45 +2180,6 @@ packages: engines: {node: '>=0.10.0'} dev: true - /ethereum-bloom-filters@1.0.10: - resolution: {integrity: sha512-rxJ5OFN3RwjQxDcFP2Z5+Q9ho4eIdEmSc2ht0fCu8Se9nbXjZ7/031uXoUYJ87KHCOdVeiUuwSnoS7hmYAGVHA==} - dependencies: - js-sha3: 0.8.0 - dev: false - - /ethereum-cryptography@2.1.3: - resolution: {integrity: sha512-BlwbIL7/P45W8FGW2r7LGuvoEZ+7PWsniMvQ4p5s2xCyw9tmaDlpfsN9HjAucbF+t/qpVHwZUisgfK24TCW8aA==} - dependencies: - '@noble/curves': 1.3.0 - '@noble/hashes': 1.3.3 - '@scure/bip32': 1.3.3 - '@scure/bip39': 1.2.2 - dev: false - - /ethers@6.11.1: - resolution: {integrity: sha512-mxTAE6wqJQAbp5QAe/+o+rXOID7Nw91OZXvgpjDa1r4fAbq2Nu314oEZSbjoRLacuCzs7kUC3clEvkCQowffGg==} - engines: {node: '>=14.0.0'} - dependencies: - '@adraffy/ens-normalize': 1.10.1 - '@noble/curves': 1.2.0 - '@noble/hashes': 1.3.2 - '@types/node': 18.15.13 - aes-js: 4.0.0-beta.5 - tslib: 2.4.0 - ws: 8.5.0 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - dev: false - - /ethjs-unit@0.1.6: - resolution: {integrity: sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw==} - engines: {node: '>=6.5.0', npm: '>=3'} - dependencies: - bn.js: 4.11.6 - number-to-bn: 1.7.0 - dev: false - /events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} @@ -2108,6 +2290,15 @@ packages: mime-types: 2.1.35 dev: true + /fs-extra@7.0.1: + resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} + engines: {node: '>=6 <7 || >=8'} + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + dev: true + /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} dev: true @@ -2230,6 +2421,10 @@ packages: get-intrinsic: 1.2.4 dev: true + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + dev: true + /graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} dev: true @@ -2280,6 +2475,7 @@ packages: dependencies: inherits: 2.0.4 minimalistic-assert: 1.0.1 + dev: true /hasown@2.0.1: resolution: {integrity: sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==} @@ -2288,12 +2484,18 @@ packages: function-bind: 1.1.2 dev: true + /he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + dev: true + /hmac-drbg@1.0.1: resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} dependencies: hash.js: 1.1.7 minimalistic-assert: 1.0.1 minimalistic-crypto-utils: 1.0.1 + dev: true /html-encoding-sniffer@4.0.0: resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} @@ -2355,6 +2557,11 @@ packages: resolve-from: 4.0.0 dev: true + /import-lazy@4.0.0: + resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} + engines: {node: '>=8'} + dev: true + /imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -2369,6 +2576,7 @@ packages: /inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true /internal-slot@1.0.7: resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} @@ -2453,11 +2661,6 @@ packages: is-extglob: 2.1.1 dev: true - /is-hex-prefixed@1.0.0: - resolution: {integrity: sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==} - engines: {node: '>=6.5.0', npm: '>=3'} - dev: false - /is-nan@1.3.2: resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==} engines: {node: '>= 0.4'} @@ -2552,9 +2755,9 @@ packages: engines: {node: '>=10'} dev: true - /js-sha3@0.8.0: - resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==} - dev: false + /jju@1.4.0: + resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} + dev: true /js-tokens@8.0.3: resolution: {integrity: sha512-UfJMcSJc+SEXEl9lH/VLHSZbThQyLpw1vLO1Lb+j4RWDvG3N2f7yj3PVQA3cmkTBNldJ9eFnM+xEXxHIXrYiJw==} @@ -2626,12 +2829,22 @@ packages: resolution: {integrity: sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==} dev: true + /jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + optionalDependencies: + graceful-fs: 4.2.11 + dev: true + /keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} dependencies: json-buffer: 3.0.1 dev: true + /kolorist@1.8.0: + resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + dev: true + /levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -2659,6 +2872,14 @@ packages: resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} dev: true + /lodash.get@4.4.2: + resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} + dev: true + + /lodash.isequal@4.5.0: + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + dev: true + /lodash.kebabcase@4.1.1: resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==} dev: true @@ -2675,6 +2896,10 @@ packages: resolution: {integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==} dev: true + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: true + /loupe@2.3.7: resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} dependencies: @@ -2695,6 +2920,13 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 dev: true + /magic-string@0.30.8: + resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + /md5.js@1.3.5: resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} dependencies: @@ -2712,10 +2944,6 @@ packages: engines: {node: '>= 8'} dev: true - /micro-ftch@0.3.1: - resolution: {integrity: sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==} - dev: false - /micromatch@4.0.5: resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} engines: {node: '>=8.6'} @@ -2751,9 +2979,17 @@ packages: /minimalistic-assert@1.0.1: resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + dev: true /minimalistic-crypto-utils@1.0.1: resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} + dev: true + + /minimatch@3.0.8: + resolution: {integrity: sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==} + dependencies: + brace-expansion: 1.1.11 + dev: true /minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -2785,6 +3021,10 @@ packages: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} dev: true + /muggle-string@0.3.1: + resolution: {integrity: sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==} + dev: true + /nanoid@3.3.7: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -2835,14 +3075,6 @@ packages: path-key: 4.0.0 dev: true - /number-to-bn@1.7.0: - resolution: {integrity: sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig==} - engines: {node: '>=6.5.0', npm: '>=3'} - dependencies: - bn.js: 4.11.6 - strip-hex-prefix: 1.0.0 - dev: false - /nwsapi@2.2.7: resolution: {integrity: sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==} dev: true @@ -3149,6 +3381,7 @@ packages: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} dependencies: safe-buffer: 5.2.1 + dev: true /randomfill@1.0.4: resolution: {integrity: sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==} @@ -3193,6 +3426,13 @@ packages: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} dev: true + /resolve@1.19.0: + resolution: {integrity: sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==} + dependencies: + is-core-module: 2.13.1 + path-parse: 1.0.7 + dev: true + /resolve@1.22.8: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true @@ -3266,6 +3506,7 @@ packages: /safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + dev: true /safe-regex-test@1.0.3: resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} @@ -3292,6 +3533,14 @@ packages: hasBin: true dev: true + /semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + /semver@7.6.0: resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} engines: {node: '>=10'} @@ -3375,6 +3624,15 @@ packages: engines: {node: '>=0.10.0'} dev: true + /source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + dev: true + + /sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + dev: true + /stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} dev: true @@ -3399,6 +3657,11 @@ packages: xtend: 4.0.2 dev: true + /string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + dev: true + /string.prototype.trim@1.2.8: resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==} engines: {node: '>= 0.4'} @@ -3447,13 +3710,6 @@ packages: engines: {node: '>=12'} dev: true - /strip-hex-prefix@1.0.0: - resolution: {integrity: sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==} - engines: {node: '>=6.5.0', npm: '>=3'} - dependencies: - is-hex-prefixed: 1.0.0 - dev: false - /strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -3472,6 +3728,13 @@ packages: has-flag: 4.0.0 dev: true + /supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + dependencies: + has-flag: 4.0.0 + dev: true + /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} @@ -3514,6 +3777,11 @@ packages: engines: {node: '>=14.0.0'} dev: true + /to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + dev: true + /to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -3556,14 +3824,21 @@ packages: strip-bom: 3.0.0 dev: true - /tslib@2.4.0: - resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} - dev: false - /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} dev: true + /tsx@4.7.1: + resolution: {integrity: sha512-8d6VuibXHtlN5E3zFkgY8u4DX7Y3Z27zvvPKVmLon/D4AjuKzarkUBTLDBgj9iTQ0hg5xM7c/mYiRVM+HETf0g==} + engines: {node: '>=18.0.0'} + hasBin: true + dependencies: + esbuild: 0.19.12 + get-tsconfig: 4.7.2 + optionalDependencies: + fsevents: 2.3.3 + dev: true + /tty-browserify@0.0.1: resolution: {integrity: sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==} dev: true @@ -3635,6 +3910,12 @@ packages: hasBin: true dev: true + /typescript@5.4.2: + resolution: {integrity: sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==} + engines: {node: '>=14.17'} + hasBin: true + dev: true + /ufo@1.4.0: resolution: {integrity: sha512-Hhy+BhRBleFjpJ2vchUNN40qgkh0366FWJGqVLYBHev0vpHTrXSA0ryT+74UiW6KWsldNurQMKGqCm1M2zBciQ==} dev: true @@ -3652,6 +3933,11 @@ packages: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} dev: true + /universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + dev: true + /universalify@0.2.0: resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} engines: {node: '>= 4.0.0'} @@ -3677,10 +3963,6 @@ packages: qs: 6.11.2 dev: true - /utf8@3.0.0: - resolution: {integrity: sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==} - dev: false - /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true @@ -3695,6 +3977,11 @@ packages: which-typed-array: 1.1.14 dev: true + /validator@13.11.0: + resolution: {integrity: sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==} + engines: {node: '>= 0.10'} + dev: true + /vite-node@1.3.1(@types/node@20.11.20): resolution: {integrity: sha512-azbRrqRxlWTJEVbzInZCTchx0X69M/XPTCz4H+TLvlTcR/xH/3hkRqhOakT41fMJCMzXTu4UvegkZiEoJAWvng==} engines: {node: ^18.0.0 || >=20.0.0} @@ -3716,6 +4003,38 @@ packages: - terser dev: true + /vite-plugin-dts@3.8.1(@types/node@20.11.20)(typescript@5.3.3)(vite@5.1.4): + resolution: {integrity: sha512-zEYyQxH7lKto1VTKZHF3ZZeOPkkJgnMrePY4VxDHfDSvDjmYMMfWjZxYmNwW8QxbaItWJQhhXY+geAbyNphI7g==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + typescript: '*' + vite: '*' + peerDependenciesMeta: + vite: + optional: true + dependencies: + '@microsoft/api-extractor': 7.43.0(@types/node@20.11.20) + '@rollup/pluginutils': 5.1.0 + '@vue/language-core': 1.8.27(typescript@5.3.3) + debug: 4.3.4 + kolorist: 1.8.0 + magic-string: 0.30.8 + typescript: 5.3.3 + vite: 5.1.4(@types/node@20.11.20) + vue-tsc: 1.8.27(typescript@5.3.3) + transitivePeerDependencies: + - '@types/node' + - rollup + - supports-color + dev: true + + /vite-plugin-env-compatible@2.0.1: + resolution: {integrity: sha512-DRrOZTg/W44ojVQQfGSMPEgYQGzp5TeIpt9cpaK35hTOC/b2D7Ffl8/RIgK8vQ0mlnDIUgETcA173bnMEkyzdw==} + dependencies: + dotenv: 8.2.0 + dotenv-expand: 5.1.0 + dev: true + /vite-plugin-node-polyfills@0.21.0(vite@5.1.4): resolution: {integrity: sha512-Sk4DiKnmxN8E0vhgEhzLudfJQfaT8k4/gJ25xvUPG54KjLJ6HAmDKbr4rzDD/QWEY+Lwg80KE85fGYBQihEPQA==} peerDependencies: @@ -3825,34 +4144,31 @@ packages: resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} dev: true - /w3c-xmlserializer@5.0.0: - resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} - engines: {node: '>=18'} + /vue-template-compiler@2.7.16: + resolution: {integrity: sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==} dependencies: - xml-name-validator: 5.0.0 + de-indent: 1.0.2 + he: 1.2.0 dev: true - /web3-eth-abi@1.10.4: - resolution: {integrity: sha512-cZ0q65eJIkd/jyOlQPDjr8X4fU6CRL1eWgdLwbWEpo++MPU/2P4PFk5ZLAdye9T5Sdp+MomePPJ/gHjLMj2VfQ==} - engines: {node: '>=8.0.0'} + /vue-tsc@1.8.27(typescript@5.3.3): + resolution: {integrity: sha512-WesKCAZCRAbmmhuGl3+VrdWItEvfoFIPXOvUJkjULi+x+6G/Dy69yO3TBRJDr9eUlmsNAwVmxsNZxvHKzbkKdg==} + hasBin: true + peerDependencies: + typescript: '*' dependencies: - '@ethersproject/abi': 5.7.0 - web3-utils: 1.10.4 - dev: false + '@volar/typescript': 1.11.1 + '@vue/language-core': 1.8.27(typescript@5.3.3) + semver: 7.6.0 + typescript: 5.3.3 + dev: true - /web3-utils@1.10.4: - resolution: {integrity: sha512-tsu8FiKJLk2PzhDl9fXbGUWTkkVXYhtTA+SmEFkKft+9BgwLxfCRpU96sWv7ICC8zixBNd3JURVoiR3dUXgP8A==} - engines: {node: '>=8.0.0'} + /w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} dependencies: - '@ethereumjs/util': 8.1.0 - bn.js: 5.2.1 - ethereum-bloom-filters: 1.0.10 - ethereum-cryptography: 2.1.3 - ethjs-unit: 0.1.6 - number-to-bn: 1.7.0 - randombytes: 2.1.0 - utf8: 3.0.0 - dev: false + xml-name-validator: 5.0.0 + dev: true /webidl-conversions@7.0.0: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} @@ -3934,19 +4250,6 @@ packages: optional: true dev: true - /ws@8.5.0: - resolution: {integrity: sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - dev: false - /xml-name-validator@5.0.0: resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} engines: {node: '>=18'} @@ -3974,3 +4277,15 @@ packages: resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} engines: {node: '>=12.20'} dev: true + + /z-schema@5.0.5: + resolution: {integrity: sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==} + engines: {node: '>=8.0.0'} + hasBin: true + dependencies: + lodash.get: 4.4.2 + lodash.isequal: 4.5.0 + validator: 13.11.0 + optionalDependencies: + commander: 9.5.0 + dev: true diff --git a/tsconfig.json b/tsconfig.json index c37f876..20ad9df 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,7 @@ { "compilerOptions": { + "rootDir": "./", + "declaration": true, "types": ["node"], "target": "ES2020", "useDefineForClassFields": true, @@ -18,8 +20,17 @@ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true + "noFallthroughCasesInSwitch": true, + "strictPropertyInitialization": false }, - "include": ["packages/**/*.ts", ".eslintrc.json", "vite.config.ts", "vitest.config.ts", "vite-env.d.ts"], - "exclude": ["node_modules", "dist", "packages/**/*.d.ts"] + "include": [ + "packages/networks/**/*.ts", + "packages/**/*.ts", + ".eslintrc.json", + "vite.config.ts", + "vitest.config.ts", + "vite-env.d.ts", + "esbuild.ts", + ], + "exclude": ["node_modules", "dist", "packages/**/*.d.ts", "test*.ts"] } diff --git a/vite.config.ts b/vite.config.ts index 6e367f1..ce9b83c 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,25 +1,21 @@ import { defineConfig } from 'vite' -import { nodePolyfills } from 'vite-plugin-node-polyfills' +import dts from 'vite-plugin-dts' +import envCompatible from 'vite-plugin-env-compatible' -// https://vitejs.dev/config/ export default defineConfig({ - plugins: [nodePolyfills()], + plugins: [ + dts({ + entryRoot: './src' + }), + envCompatible() + ], build: { - commonjsOptions: { - transformMixedEsModules: true - }, - assetsDir: '.', - assetsInlineLimit: 0, - rollupOptions: { - input: { - main: 'packages/main.ts' - }, - output: { - format: 'umd', - entryFileNames: 'app.min.js', - chunkFileNames: '[name].js', - assetFileNames: '[name].[ext]' - } + minify: true, + sourcemap: true, + lib: { + entry: './src/index.ts', + formats: ['es', 'umd'], + fileName: (format: string) => `index.${format}.js` } }, server: { diff --git a/vitest.config.ts b/vitest.config.ts index 791dee3..02f5bf0 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -6,10 +6,12 @@ export default mergeConfig( viteConfig, defineConfig({ test: { - environment: 'jsdom', + watch: false, + testTimeout: 180000, + environment: 'node', + setupFiles: ['./packages/networks/evm-chains/tests/setup.ts'], exclude: [...configDefaults.exclude, 'e2e/*'], - root: fileURLToPath(new URL('./', import.meta.url)), - setupFiles: ['./packages/networks/evm-chains/tests/setup.ts'] + root: fileURLToPath(new URL('./', import.meta.url)) } }) )