From e129ecfd5b90dbc80ae84e6a8ca67cca3e4b8c4b Mon Sep 17 00:00:00 2001 From: ukorvl Date: Wed, 29 May 2024 17:27:28 +0300 Subject: [PATCH] add unit tests for public client #2 --- .changeset/eleven-dogs-shop.md | 5 + .github/actions/build/action.yml | 6 +- biome.json | 2 +- package-lock.json | 374 +++++++++++++++++- package.json | 3 + src/clients/PublicClient.test.ts | 100 +++++ src/clients/PublicClient.ts | 128 ++++-- src/clients/WalletClient.test.ts | 80 +++- src/clients/WalletClient.ts | 48 ++- src/clients/types/IDeployContractOption.ts | 10 + src/signers/publicKey.test.ts | 16 +- src/signers/publicKey.ts | 3 +- src/signers/types/IAddress.ts | 2 +- src/types/IDeployData.ts | 21 + src/types/IMessage.ts | 6 +- src/utils/assert.ts | 14 + src/utils/block.ts | 12 + src/utils/index.ts | 1 + test/mocks/accounts.ts | 4 +- test/mocks/address.ts | 6 + .../simpleStorage/bin/SimpleStorage.abi | 1 + .../simpleStorage/bin/SimpleStorage.bin | 1 + .../contracts/simpleStorage/contract.sol | 13 + test/mocks/message.ts | 4 + test/mocks/shard.ts | 3 + test/vitest.config.ts | 9 +- 26 files changed, 801 insertions(+), 71 deletions(-) create mode 100644 .changeset/eleven-dogs-shop.md create mode 100644 src/clients/types/IDeployContractOption.ts create mode 100644 src/types/IDeployData.ts create mode 100644 src/utils/block.ts create mode 100644 test/mocks/address.ts create mode 100644 test/mocks/contracts/simpleStorage/bin/SimpleStorage.abi create mode 100644 test/mocks/contracts/simpleStorage/bin/SimpleStorage.bin create mode 100644 test/mocks/contracts/simpleStorage/contract.sol create mode 100644 test/mocks/message.ts create mode 100644 test/mocks/shard.ts diff --git a/.changeset/eleven-dogs-shop.md b/.changeset/eleven-dogs-shop.md new file mode 100644 index 00000000..c41d1526 --- /dev/null +++ b/.changeset/eleven-dogs-shop.md @@ -0,0 +1,5 @@ +--- +"@nilfoundation/niljs": patch +--- + +Add unit tests for PublicClient diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml index e6410103..eeca7850 100644 --- a/.github/actions/build/action.yml +++ b/.github/actions/build/action.yml @@ -10,9 +10,9 @@ runs: - name: Format shell: bash run: npm run format - # - name: Test - # shell: bash - # run: npm run test:ci + - name: Test + shell: bash + run: npm run test:ci -- related ./src/clients/PublicClient.test.ts' - name: Build shell: bash run: | diff --git a/biome.json b/biome.json index bd997c32..97a5036c 100644 --- a/biome.json +++ b/biome.json @@ -20,6 +20,6 @@ "lineWidth": 80 }, "files": { - "ignore": ["node_modules", "dist", "package.json"] + "ignore": ["node_modules", "dist", "package.json", "test/coverage"] } } diff --git a/package-lock.json b/package-lock.json index 0901e37e..155bcfda 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "MIT", "dependencies": { "@chainsafe/ssz": "^0.16.0", + "abitype": "^1.0.2", "tiny-invariant": "^1.3.3" }, "devDependencies": { @@ -20,6 +21,7 @@ "@commitlint/config-conventional": "^19.2.2", "@rollup/plugin-node-resolve": "^15.2.3", "@types/elliptic": "^6.4.18", + "@vitest/coverage-v8": "^1.6.0", "elliptic": "^6.5.5", "rimraf": "^5.0.7", "rollup": "^4.17.2", @@ -38,6 +40,19 @@ "@open-rpc/client-js": ">=1.8.1 <1.9.0" } }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.24.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", @@ -51,10 +66,19 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-string-parser": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.6.tgz", + "integrity": "sha512-WdJjwMEkmBicq5T9fm/cHND3+UlFa2Yj8ALLgmoSQAJZysYbBjw+azChSGPN4DSPLXOcooGRvDwZWMcF/mLO2Q==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", - "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.6.tgz", + "integrity": "sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw==", "dev": true, "engines": { "node": ">=6.9.0" @@ -107,6 +131,18 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, + "node_modules/@babel/parser": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.6.tgz", + "integrity": "sha512-eNZXdfU35nJC2h24RznROuOpO94h6x8sg9ju0tT9biNtLZ2vuP8SduLqqV+/8+cebSLV9SJEAN5Z3zQbJG/M+Q==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@babel/runtime": { "version": "7.24.5", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz", @@ -119,6 +155,26 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/types": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.6.tgz", + "integrity": "sha512-WaMsgi6Q8zMgMth93GvWPXkhAIEobfsIkLTacoVZoK1J0CevIPGYY2Vo5YvJGqyHqXM6P4ppOYGsIRU8MM9pFQ==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.24.6", + "@babel/helper-validator-identifier": "^7.24.6", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, "node_modules/@biomejs/biome": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.7.3.tgz", @@ -1489,6 +1545,15 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/@jest/schemas": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", @@ -1975,6 +2040,14 @@ "node": ">=14" } }, + "node_modules/@polka/url": { + "version": "1.0.0-next.25", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.25.tgz", + "integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==", + "dev": true, + "optional": true, + "peer": true + }, "node_modules/@rollup/plugin-node-resolve": { "version": "15.2.3", "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz", @@ -2457,6 +2530,33 @@ "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, + "node_modules/@vitest/coverage-v8": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.6.0.tgz", + "integrity": "sha512-KvapcbMY/8GYIG0rlwwOKCVNRc0OL20rrhFkg/CHNzncV03TE2XWvO5w9uZYoxNiMEBacAJt3unSOiZ7svePew==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.1", + "@bcoe/v8-coverage": "^0.2.3", + "debug": "^4.3.4", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-lib-source-maps": "^5.0.4", + "istanbul-reports": "^3.1.6", + "magic-string": "^0.30.5", + "magicast": "^0.3.3", + "picocolors": "^1.0.0", + "std-env": "^3.5.0", + "strip-literal": "^2.0.0", + "test-exclude": "^6.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "1.6.0" + } + }, "node_modules/@vitest/expect": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.0.tgz", @@ -2511,6 +2611,29 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/@vitest/ui": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-1.6.0.tgz", + "integrity": "sha512-k3Lyo+ONLOgylctiGovRKy7V4+dIN2yxstX3eY5cWFXH6WP+ooVX79YSyi0GagdTQzLmT43BF27T0s6dOIPBXA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@vitest/utils": "1.6.0", + "fast-glob": "^3.3.2", + "fflate": "^0.8.1", + "flatted": "^3.2.9", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "sirv": "^2.0.4" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "1.6.0" + } + }, "node_modules/@vitest/utils": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.0.tgz", @@ -2532,6 +2655,26 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, + "node_modules/abitype": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.0.2.tgz", + "integrity": "sha512-aFt4k2H+eiAKy/zxtnORa9iIb10BMBeWL18l8v4+QuwYEBXPxxjSB1bFZCzQmKPoj8m7j68K705l3uY+E2gAjg==", + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3 >=3.22.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, "node_modules/acorn": { "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", @@ -4010,6 +4153,14 @@ "reusify": "^1.0.4" } }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "dev": true, + "optional": true, + "peer": true + }, "node_modules/filesize": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.4.0.tgz", @@ -4058,6 +4209,14 @@ "pkg-dir": "^4.2.0" } }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true, + "optional": true, + "peer": true + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -4553,6 +4712,12 @@ "node": ">=12" } }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, "node_modules/http-cache-semantics": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", @@ -5133,6 +5298,77 @@ "ws": "*" } }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.4.tgz", + "integrity": "sha512-wHOoEsNJTVltaJp8eVkm8w+GVkVNHT2YDYo53YdzQEL2gWm1hBX5cGFR9hQJtuGLebidVX7et3+dmDZrmclduw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/jackspeak": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", @@ -5409,6 +5645,32 @@ "@jridgewell/sourcemap-codec": "^1.4.15" } }, + "node_modules/magicast": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.4.tgz", + "integrity": "sha512-TyDF/Pn36bBji9rWKHlZe+PZb6Mx5V8IHCSxk7X4aljM4e/vyDvZZYwHewdVaqiA0nb3ghfHU/6AUpDxWoER2Q==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.24.4", + "@babel/types": "^7.24.0", + "source-map-js": "^1.2.0" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/make-fetch-happen": { "version": "10.2.1", "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", @@ -5972,6 +6234,17 @@ "ufo": "^1.5.3" } }, + "node_modules/mrmime": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", + "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -7761,6 +8034,22 @@ "simple-git-hooks": "cli.js" } }, + "node_modules/sirv": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", + "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -8448,6 +8737,63 @@ "node": ">=10" } }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/text-extensions": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz", @@ -8507,6 +8853,15 @@ "node": ">=0.6.0" } }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -8519,6 +8874,17 @@ "node": ">=8.0" } }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -8806,7 +9172,7 @@ "version": "5.4.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", - "dev": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/package.json b/package.json index c95f2b93..a295681b 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "scripts": { "test": "vitest -c ./test/vitest.config.ts", "test:ci": "CI=true vitest -c ./test/vitest.config.ts", + "test:coverage": "npm run test:ci -- --coverage", "build": "rimraf dist && rollup -c ./rollup/rollup.config.js --bundleConfigAsCjs", "prepare": "npx simple-git-hooks", "format": "biome format .", @@ -50,6 +51,7 @@ "@commitlint/config-conventional": "^19.2.2", "@rollup/plugin-node-resolve": "^15.2.3", "@types/elliptic": "^6.4.18", + "@vitest/coverage-v8": "^1.6.0", "elliptic": "^6.5.5", "rimraf": "^5.0.7", "rollup": "^4.17.2", @@ -75,6 +77,7 @@ }, "dependencies": { "@chainsafe/ssz": "^0.16.0", + "abitype": "^1.0.2", "tiny-invariant": "^1.3.3" }, "keywords": [ diff --git a/src/clients/PublicClient.test.ts b/src/clients/PublicClient.test.ts index 619cd54e..0aa215f4 100644 --- a/src/clients/PublicClient.test.ts +++ b/src/clients/PublicClient.test.ts @@ -1,6 +1,106 @@ +import { defaultAddress } from "../../test/mocks/address.js"; import { endpoint } from "../../test/mocks/endpoint.js"; +import { rawMsg } from "../../test/mocks/message.js"; +import { masterShardId } from "../../test/mocks/shard.js"; +import { addHexPrefix } from "../index.js"; import { PublicClient } from "./PublicClient.js"; const client = new PublicClient({ endpoint, }); + +test("getBlockByHash", async ({ expect }) => { + const block = await client.getBlockByHash( + masterShardId, + "0x158c4be17b52b92dc03cef7e8cd9cec64c6413175df3cce9f6ae1fb0d12106fa", + ); + + expect(block).toBeDefined(); +}); + +test("getBlockByNumber", async ({ expect }) => { + const block = await client.getBlockByNumber(masterShardId, "0x1b4"); + + expect(block).toBeDefined(); +}); + +// not implemented on the node +// test("getBlockMessageCountByNumber", async ({ expect }) => { +// const count = await client.getBlockMessageCountByNumber( +// masterShardId, +// "0x1b4", +// ); + +// expect(count).toBeDefined(); +// }); + +// not implemented on the node +// test("getBlockMessageCountByHash", async ({ expect }) => { +// const count = await client.getBlockMessageCountByHash( +// masterShardId, +// "0x158c4be17b52b92dc03cef7e8cd9cec64c6413175df3cce9f6ae1fb0d12106fa", +// ); + +// expect(count).toBeDefined(); +// }); + +// not implemented on the node +// test("getCode", async ({ expect }) => { +// const code = await client.getCode( +// masterShardId, +// addHexPrefix(defaultAddress), +// "0x1b4", +// ); + +// expect(code).toBeDefined(); +// }); + +test("getMessageCount", async ({ expect }) => { + const count = await client.getMessageCount( + masterShardId, + addHexPrefix(defaultAddress), + "latest", + ); + + expect(count).toBeDefined(); +}); + +test("getBalance", async ({ expect }) => { + const balance = await client.getBalance( + masterShardId, + addHexPrefix(defaultAddress), + "latest", + ); + + expect(balance).toBeDefined(); +}); + +test("getMessageByHash", async ({ expect }) => { + const message = await client.getMessageByHash( + masterShardId, + "0x158c4be17b52b92dc03cef7e8cd9cec64c6413175df3cce9f6ae1fb0d12106fa", + ); + + expect(message).toBeDefined(); +}); + +test("getMessageReceiptByHash", async ({ expect }) => { + const receipt = await client.getMessageReceiptByHash( + masterShardId, + "0x158c4be17b52b92dc03cef7e8cd9cec64c6413175df3cce9f6ae1fb0d12106fa", + ); + + expect(receipt).toBeDefined(); +}); + +test("sendRawMessage", async ({ expect }) => { + const hash = await client.sendRawMessage(rawMsg); + + expect(hash).toBeDefined(); +}); + +test("getGasPrice", async ({ expect }) => { + const gasPrice = await client.getGasPrice(masterShardId); + + expect(gasPrice).toBeDefined(); +}); diff --git a/src/clients/PublicClient.ts b/src/clients/PublicClient.ts index d98f02bd..b4ea6f45 100644 --- a/src/clients/PublicClient.ts +++ b/src/clients/PublicClient.ts @@ -1,4 +1,6 @@ +import type { Hex } from "@noble/curves/abstract/utils"; import type { IReceipt } from "../index.js"; +import type { IAddress } from "../signers/types/IAddress.js"; import { BaseClient } from "./BaseClient.js"; import type { IPublicClientConfig } from "./types/ClientConfigs.js"; @@ -21,7 +23,9 @@ class PublicClient extends BaseClient { /** * getBlockByNumber returns the block by the block number. + * @param shardId - The shard id. * @param number - The block number. + * @param fullTx - The flag to include full transactions. * @returns The block. * @example import { PublicClient } from '@nilfoundation/niljs'; @@ -32,18 +36,24 @@ class PublicClient extends BaseClient { * * const block = await client.getBlockByNumber(1); */ - public async getBlockByHash(hash: Uint8Array): Promise { - const block = await this.rpcClient.request({ + public async getBlockByHash( + shardId: number, + hash: Hex, + fullTx = false, + ): Promise { + const res = await this.rpcClient.request({ method: "eth_getBlockByHash", - params: [hash, true], + params: [shardId, hash, fullTx], }); - return block; + return res; } /** * getBlockByNumber returns the block by the block number. + * @param shardId - The shard id. * @param number - The block number. + * @param fullTx - The flag to include full transactions. * @returns The block. * @example import { PublicClient } from '@nilfoundation/niljs'; @@ -52,19 +62,24 @@ class PublicClient extends BaseClient { * endpoint: 'http://127.0.0.1:8529' * }) * - * const block = await client.getBlockByNumber(1); + * const block = await client.getBlockByNumber('0x1'); */ - public async getBlockByNumber(number: number): Promise { + public async getBlockByNumber( + shardId: number, + blockNumber: string, + fullTx = false, + ): Promise { const res = await this.rpcClient.request({ method: "eth_getBlockByNumber", - params: [number, true], + params: [shardId, blockNumber, fullTx], }); - return res.result; + return res; } /** * getBlockMessageCountByNumber returns the message count by the block number. + * @param shardId - The shard id. * @param number - The block number. * @returns The message count. * @example @@ -76,17 +91,21 @@ class PublicClient extends BaseClient { * * const count = await client.getBlockMessageCountByNumber(1); */ - public async getBlockMessageCountByNumber(number: number): Promise { + public async getBlockMessageCountByNumber( + shardId: number, + blockNumber: string, + ): Promise { const res = await this.rpcClient.request({ - method: "eth_getBlockMessageCountByNumber", - params: [number], + method: "eth_getBlockTransactionCountByNumber", + params: [shardId, blockNumber], }); - return res.result; + return res; } /** * getBlockMessageCountByHash returns the message count by the block hash. + * @param shardId - The shard id. * @param hash - The block hash. * @returns The message count. * @example @@ -98,17 +117,21 @@ class PublicClient extends BaseClient { * * const count = await client.getBlockMessageCountByHash(Uint8Array.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])); */ - public async getBlockMessageCountByHash(hash: Uint8Array): Promise { + public async getBlockMessageCountByHash( + shardId: number, + hash: Hex, + ): Promise { const res = await this.rpcClient.request({ - method: "eth_getBlockMessageCountByHash", - params: [hash], + method: "eth_getBlockTransactionCountByHash", + params: [shardId, hash], }); - return res.result; + return res; } /** - * getCode returns the code of the contract. + * getCode returns the bytecode of the contract. + * @param shardId - The shard id. * @param address - The contract address. * @param blockNumberOrHash - The block number or hash. * @returns The code of the contract. @@ -122,19 +145,21 @@ class PublicClient extends BaseClient { * const code = await client.getCode(Uint8Array.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]), 'latest'); */ public async getCode( - address: Uint8Array, - blockNumberOrHash: string, + shardId: number, + address: IAddress, + blockNumberOrHash: Hex, ): Promise { const res = await this.rpcClient.request({ method: "eth_getCode", - params: [address, blockNumberOrHash], + params: [shardId, address, blockNumberOrHash], }); - return res.result; + return res; } /** * getMessageCount returns the message count of the address. + * @param shardId - The shard id. * @param address - The address. * @param blockNumberOrHash - The block number or hash. * @returns The message count. @@ -148,19 +173,21 @@ class PublicClient extends BaseClient { * const count = await client.getMessageCount(Uint8Array.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]), 'latest'); */ public async getMessageCount( - address: Uint8Array, + shardId: number, + address: IAddress, blockNumberOrHash: string, ): Promise { const res = await this.rpcClient.request({ - method: "eth_getMessageCount", - params: [address, blockNumberOrHash], + method: "eth_getTransactionCount", + params: [shardId, address, blockNumberOrHash], }); - return res.result; + return res; } /** * getBalance returns the balance of the address. + * @param shardId - The shard id. * @param address - The address. * @param blockNumberOrHash - The block number or hash. * @returns The balance of the address. @@ -174,19 +201,21 @@ class PublicClient extends BaseClient { * const balance = await client.getBalance(Uint8Array.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]), 'latest'); */ public async getBalance( - address: Uint8Array, - blockNumberOrHash: string, + shardId: number, + address: IAddress, + blockNumberOrHash: Hex, ): Promise { const res = await this.rpcClient.request({ method: "eth_getBalance", - params: [address, blockNumberOrHash], + params: [shardId, address, blockNumberOrHash], }); - return res.result; + return res; } /** * getMessageByHash returns the message by the hash. + * @param shardId - The shard id. * @param hash - The hash. * @returns The message. * @example @@ -198,13 +227,16 @@ class PublicClient extends BaseClient { * * const message = await client.getMessageByHash(Uint8Array.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])); */ - public async getMessageByHash(hash: Uint8Array): Promise { + public async getMessageByHash( + shardId: number, + hash: Hex, + ): Promise { const res = await this.rpcClient.request({ - method: "eth_getMessageByHash", - params: [hash], + method: "eth_getInMessageByHash", + params: [shardId, hash], }); - return res.result; + return res; } /** @@ -212,17 +244,25 @@ class PublicClient extends BaseClient { * @param shardId - The shard id. * @param hash - The hash. * @returns The message receipt. + * @example + * import { PublicClient } from '@nilfoundation/niljs'; + * + * const client = new PublicClient({ + * endpoint: 'http://127.0.0.1:8529' + * }) + * + * const receipt = await client.getMessageReceiptByHash(1, Uint8Array.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])); */ public async getMessageReceiptByHash( shardId: number, - hash: Uint8Array, + hash: Hex, ): Promise { const res = await this.rpcClient.request({ - method: "eth_getMessageReceipt", + method: "eth_getInMessageReceipt", params: [shardId, hash], }); - return res.result; + return res; } /** @@ -239,13 +279,23 @@ class PublicClient extends BaseClient { * const message = Uint8Array.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); * const hash = await client.sendRawMessage(message); */ - public async sendRawMessage(message: Uint8Array): Promise { + public async sendRawMessage(message: Hex): Promise { const res = await this.rpcClient.request({ - method: "eth_sendRawMessage", + method: "eth_sendRawTransaction", params: [message], }); - return res.hash; + return res; + } + + /** + * getGasPrice returns the gas price in wei. + * @returns The gas price. + */ + public async getGasPrice(shardId: number): Promise { + const stubGasPrice = BigInt(1000000000); + + return stubGasPrice; } } diff --git a/src/clients/WalletClient.test.ts b/src/clients/WalletClient.test.ts index 7fde6882..c0006336 100644 --- a/src/clients/WalletClient.test.ts +++ b/src/clients/WalletClient.test.ts @@ -1,5 +1,75 @@ -// i'm not sure for now how to test it. -// there are at least 3 options: -// 1. use mock local node -// 2. use real public node -// 3. use some snapshots, but updating them every time will hurt. Anyway worth to try. +import { endpoint } from "../../test/mocks/endpoint.js"; + +import abi from "../../test/mocks/contracts/simpleStorage/bin/SimpleStorage.abi"; +import { type IMessage, LocalKeySigner, generatePrivateKey } from "../index.js"; +import { WalletClient } from "./WalletClient.js"; + +const client = new WalletClient({ + endpoint, + signer: new LocalKeySigner({ + privateKey: generatePrivateKey(), + }), +}); + +test("sendMessage", async ({ expect }) => { + const newMessage = { + to: "0x1234", + data: 100, + } as unknown as IMessage; + + const hash = await client.sendMessage(newMessage); + + expect(hash).toBeDefined(); +}); + +test("sendMessage with from field", async ({ expect }) => { + const newMessage = { + from: "0x1234", + to: "0x1234", + data: 100, + } as unknown as IMessage; + + const hash = await client.sendMessage(newMessage); + + expect(hash).toBeDefined(); +}); + +test("sendMessage with from field and shouldValidate false", async ({ + expect, +}) => { + const newMessage = { + from: "0x1234", + to: "0x1234", + data: 100, + } as unknown as IMessage; + + const hash = await client.sendMessage(newMessage, { shouldValidate: false }); + + expect(hash).toBeDefined(); +}); + +test("sendRawMessage", async ({ expect }) => { + const newMessage = { + to: "0x1234", + data: 100, + } as unknown as IMessage; + const signedMessage = client.signMessage(newMessage); + + const hash = await client.sendRawMessage(signedMessage); + + expect(hash).toBeDefined(); +}); + +test("Deploy contract", async ({ expect }) => { + const newMessage = { + data: 100, + } as unknown as IMessage; + + const hash = await client.deployContract({ + bytecode: new Uint8Array(), + args: new Uint8Array(), + abi: abi, + }); + + expect(hash).toBeDefined(); +}); diff --git a/src/clients/WalletClient.ts b/src/clients/WalletClient.ts index 873a524c..3a631d50 100644 --- a/src/clients/WalletClient.ts +++ b/src/clients/WalletClient.ts @@ -1,12 +1,13 @@ import invariant from "tiny-invariant"; import { messageToSsz, signedMessageToSsz } from "../encoding/toSsz.js"; -import { type IReceipt, getShardIdFromAddress } from "../index.js"; +import { type IReceipt, getShardIdFromAddress, toHex } from "../index.js"; import type { ISigner } from "../signers/index.js"; import type { IMessage } from "../types/IMessage.js"; import { assertIsValidMessage } from "../utils/assert.js"; import { startPollingUntilCondition } from "../utils/polling.js"; import { PublicClient } from "./PublicClient.js"; import type { IWalletClientConfig } from "./types/ClientConfigs.js"; +import type { IDeployContractOption } from "./types/IDeployContractOption.js"; import type { ISendMessageOptions } from "./types/ISendMessageOptions.js"; import type { ISignMessageOptions } from "./types/ISignMessageOptions.js"; @@ -25,14 +26,40 @@ import type { ISignMessageOptions } from "./types/ISignMessageOptions.js"; */ class WalletClient extends PublicClient { private signer: ISigner; + private shardId: number; constructor(config: IWalletClientConfig) { super(config); this.signer = config.signer; + + const address = this.signer.getAddress(); + this.shardId = getShardIdFromAddress(address); + } + + /** + * prepareMessage prepares a message to send. + * @param message - The message to send. + * @returns The prepared message. + */ + private async prepareMessage(message: IMessage): Promise { + const { gasPrice } = message; + const finalMsg = { + ...message, + from: message.from ? message.from : this.signer.getAddress(), + }; + + if (!gasPrice) { + const gasPrice = await this.getGasPrice(this.shardId); + finalMsg.gasPrice = gasPrice; + } + + return finalMsg; } /** * sendMessage sends a message to the network. + * "from" field in the message is automatically filled with the signer address, but + * can be overwritten by providing the "from" field in the message. * @param message - The message to send. It will be signed with the signer. * @param options - The options to send a message. * @returns The hash of the message. @@ -50,9 +77,10 @@ class WalletClient extends PublicClient { message: IMessage, { shouldValidate = true } = {} as ISendMessageOptions, ): Promise { - shouldValidate && assertIsValidMessage(message); + const preparedMsg = await this.prepareMessage(message); + shouldValidate && assertIsValidMessage(preparedMsg); - const signedMessage = this.signMessage(message, { + const signedMessage = this.signMessage(preparedMsg, { shouldValidate: false, }); @@ -105,15 +133,19 @@ class WalletClient extends PublicClient { * const contract = Uint8Array.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); * const hash = await client.deployContract(contract); */ - public async deployContract(contract: Uint8Array): Promise { - const hash = await this.sendRawMessage(contract); - const address = this.signer.getAddress(); - const shardId = getShardIdFromAddress(address); + public async deployContract({ + bytecode, + ...rest + }: IDeployContractOption): Promise { + const hash = await this.sendMessage({ + data: toHex(bytecode), + ...rest, + } as IMessage); // in the future we want to use subscribe method to get the receipt // for now it is simple short polling const receipt = await startPollingUntilCondition( - async () => await this.getMessageReceiptByHash(shardId, hash), + async () => await this.getMessageReceiptByHash(this.shardId, hash), (receipt) => receipt !== undefined, 1000, ); diff --git a/src/clients/types/IDeployContractOption.ts b/src/clients/types/IDeployContractOption.ts new file mode 100644 index 00000000..b3d83e46 --- /dev/null +++ b/src/clients/types/IDeployContractOption.ts @@ -0,0 +1,10 @@ +import type { IMessage } from "../../index.js"; +import type { IDeployData } from "../../types/IDeployData.js"; + +/** + * The options for deploying a contract. + */ +type IDeployContractOption = IDeployData & + Pick; + +export type { IDeployContractOption }; diff --git a/src/signers/publicKey.test.ts b/src/signers/publicKey.test.ts index 951e9e51..915853bd 100644 --- a/src/signers/publicKey.test.ts +++ b/src/signers/publicKey.test.ts @@ -1,5 +1,11 @@ import { accounts } from "../../test/mocks/accounts.js"; -import { getPublicKey } from "./publicKey.js"; +import { generatePrivateKey, getPublicKey } from "./publicKey.js"; + +test("generatePrivateKey", async ({ expect }) => { + const result = generatePrivateKey(); + + expect(result).toBeDefined(); +}); test("getPublicKey", async ({ expect }) => { const account = accounts[0]; @@ -10,3 +16,11 @@ test("getPublicKey", async ({ expect }) => { expect(result).toBe(expectedOutput); }); + +test("getAddressFromPublicKey", async ({ expect }) => { + // const account = accounts[0]; + // const input = account.publicKey; + // const expectedOutput = account.address; + // const result = getAddressFromPublicKey(input); + // expect(result).toBe(expectedOutput); +}); diff --git a/src/signers/publicKey.ts b/src/signers/publicKey.ts index 9db23f54..5bcdada3 100644 --- a/src/signers/publicKey.ts +++ b/src/signers/publicKey.ts @@ -25,7 +25,8 @@ const getPublicKey = (privateKey: IPrivateKey): Hex => { * @example * const privateKey = generatePrivateKey(); */ -const generatePrivateKey = (): Hex => toHex(secp256k1.utils.randomPrivateKey()); +const generatePrivateKey = (): IPrivateKey => + addHexPrefix(toHex(secp256k1.utils.randomPrivateKey())) as IPrivateKey; const recoverPublicKey = ( messageHash: Hex | Uint8Array, diff --git a/src/signers/types/IAddress.ts b/src/signers/types/IAddress.ts index f9816ffc..9743f77b 100644 --- a/src/signers/types/IAddress.ts +++ b/src/signers/types/IAddress.ts @@ -1,6 +1,6 @@ /** * Address type represents an address in hexadecimal format. */ -type IAddress = `0x${string}`; +type IAddress = `0x${string}` | string; export type { IAddress }; diff --git a/src/types/IDeployData.ts b/src/types/IDeployData.ts new file mode 100644 index 00000000..bf99e34e --- /dev/null +++ b/src/types/IDeployData.ts @@ -0,0 +1,21 @@ +import type { Abi } from "abitype"; + +/** + * Deploy data is a data structure that contains information to deploy a contract. + */ +type IDeployData = { + /** + * Compiled contract bytecode. + */ + bytecode: Uint8Array; + /** + * The contract's constructor arguments. + */ + args?: Uint8Array; + /** + * The contract's Application Binary Interface (ABI). + */ + abi?: Abi | string[]; +}; + +export type { IDeployData }; diff --git a/src/types/IMessage.ts b/src/types/IMessage.ts index d5c100f0..f18b1342 100644 --- a/src/types/IMessage.ts +++ b/src/types/IMessage.ts @@ -1,3 +1,5 @@ +import type { Hex } from "@noble/curves/abstract/utils"; + /** * The interface for the message object. This object is used to represent a message in the network. */ @@ -7,9 +9,9 @@ interface IMessage { from: string; to: string; value: bigint; - data: string; + data: Hex; seqno: number; - signature: string | null; + signature?: string | null; maxPriorityFeePerGas: bigint; gasPrice: bigint; maxFeePerGas: bigint; diff --git a/src/utils/assert.ts b/src/utils/assert.ts index 175ce4b9..a58ce5f6 100644 --- a/src/utils/assert.ts +++ b/src/utils/assert.ts @@ -1,5 +1,6 @@ import type { Hex } from "@noble/curves/abstract/utils"; import invariant from "tiny-invariant"; +import { type IBlock, isValidBlock } from "../index.js"; import type { IPrivateKey } from "../signers/index.js"; import type { IMessage } from "../types/IMessage.js"; import { isHexString } from "./hex.js"; @@ -88,10 +89,23 @@ const assertIsAddress = (address: string, message?: string): void => { ); }; +/** + * Checks if the block is valid. If the block is valid, it returns nothing. + * @param block - The block to check. + * @param message - The message to throw if the block is invalid. + */ +const assertIsValidBlock = (block: IBlock, message?: string): void => { + invariant( + isValidBlock(block), + message ?? `Expected a valid block but got ${block}`, + ); +}; + export { assertIsBuffer, assertIsHexString, assertIsValidPrivateKey, assertIsValidMessage, assertIsAddress, + assertIsValidBlock, }; diff --git a/src/utils/block.ts b/src/utils/block.ts new file mode 100644 index 00000000..df425628 --- /dev/null +++ b/src/utils/block.ts @@ -0,0 +1,12 @@ +import type { IBlock } from "../index.js"; + +/** + * isValidBlock checks if the block is valid. + * @param block - The block to check. + * @returns True if the block is valid, false otherwise. + */ +const isValidBlock = (block: IBlock): boolean => { + return true; +}; + +export { isValidBlock }; diff --git a/src/utils/index.ts b/src/utils/index.ts index bec19073..6f221441 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -2,3 +2,4 @@ export * from "./assert.js"; export * from "./hex.js"; export * from "./message.js"; export * from "./keccak256.js"; +export * from "./block.js"; diff --git a/test/mocks/accounts.ts b/test/mocks/accounts.ts index 6d149f91..9d05fa34 100644 --- a/test/mocks/accounts.ts +++ b/test/mocks/accounts.ts @@ -1,9 +1,9 @@ const accounts = [ { privateKey: - "0xcfd72fffd4f2ba4b1b021e457b6c7e1eeaa7d80e17b026e77025de6b30f286f5", + "0xf558bd0b1daca5280edf69903c696d8fda5be4e9579c41c61f5b8be535492200", publicKey: - "04a3dbbca6c794b60be1e54455d98c3df16b48883ce073a241c86a2a9b43af6808b3f4fc1553f9194985fa53b040620f7a77626383198898282bc40e41bc1cb01d", + "0x040ff157f75fb559fd0326888411d1ed9008999e78cd668547988933c2192bd3e963d996a10823e653241be2d22fc9ced07d77d298f09b9bc22f0f60be02c5578d", }, ] as const; diff --git a/test/mocks/address.ts b/test/mocks/address.ts new file mode 100644 index 00000000..22672a18 --- /dev/null +++ b/test/mocks/address.ts @@ -0,0 +1,6 @@ +/** + * Created by default in the system. + */ +const defaultAddress = "0000186F9cC19906Dba062697c3179c1cCe6d4C4"; + +export { defaultAddress }; diff --git a/test/mocks/contracts/simpleStorage/bin/SimpleStorage.abi b/test/mocks/contracts/simpleStorage/bin/SimpleStorage.abi new file mode 100644 index 00000000..a379d581 --- /dev/null +++ b/test/mocks/contracts/simpleStorage/bin/SimpleStorage.abi @@ -0,0 +1 @@ +[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newValue","type":"uint256"}],"name":"ValueChanged","type":"event"},{"inputs":[],"name":"increment","outputs":[],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/test/mocks/contracts/simpleStorage/bin/SimpleStorage.bin b/test/mocks/contracts/simpleStorage/bin/SimpleStorage.bin new file mode 100644 index 00000000..c7ac2fe7 --- /dev/null +++ b/test/mocks/contracts/simpleStorage/bin/SimpleStorage.bin @@ -0,0 +1 @@ +0x6080604052348015600e575f80fd5b506101508061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610029575f3560e01c8063d09de08a1461002d575b5f80fd5b610035610037565b005b60015f8082825461004891906100bf565b925050819055507f93fe6d397c74fdf1402a8b72e47b68512f0510d7b98a4bc4cbdf6ac7108b3c595f5460405161007f9190610101565b60405180910390a1565b5f819050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f6100c982610089565b91506100d483610089565b92508282019050808211156100ec576100eb610092565b5b92915050565b6100fb81610089565b82525050565b5f6020820190506101145f8301846100f2565b9291505056fea26469706673582212200151a7fd2785bb9d825712b669f5faa0d4b843a26a04e3e64f4ca35fdeb830c464736f6c63430008190033 \ No newline at end of file diff --git a/test/mocks/contracts/simpleStorage/contract.sol b/test/mocks/contracts/simpleStorage/contract.sol new file mode 100644 index 00000000..96f6b9d2 --- /dev/null +++ b/test/mocks/contracts/simpleStorage/contract.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract SimpleStorage { + uint256 private value; + + event ValueChanged(uint256 newValue); + + function increment() public { + value += 1; + emit ValueChanged(value); + } +} diff --git a/test/mocks/message.ts b/test/mocks/message.ts new file mode 100644 index 00000000..ce2d3866 --- /dev/null +++ b/test/mocks/message.ts @@ -0,0 +1,4 @@ +const rawMsg = + "0x000000000000000081000000a10000003f644fd5f74c59dd8e55508432fff9318c19bb890000000000000000000000000000000000000000c1000000e100000049343ff18c445d6f9bca0dfae730442e3630a86716b66bb489f1601d7e4b610b08196268849d3e56a2f3523e709be088013a83d34499b1929c1734f7cb3ed427010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f80fd5b506101818061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80632096525514610038578063d09de08a14610056575b5f80fd5b610040610060565b60405161004d91906100d2565b60405180910390f35b61005e610068565b005b5f8054905090565b60015f808282546100799190610118565b925050819055507f93fe6d397c74fdf1402a8b72e47b68512f0510d7b98a4bc4cbdf6ac7108b3c595f546040516100b091906100d2565b60405180910390a1565b5f819050919050565b6100cc816100ba565b82525050565b5f6020820190506100e55f8301846100c3565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f610122826100ba565b915061012d836100ba565b9250828201905080821115610145576101446100eb565b5b9291505056fea2646970667358221220ac3087fbe9b74312e7012ab227b9cbfacddc07a5096bd4017448806139d7dca364736f6c63430008190033"; + +export { rawMsg }; diff --git a/test/mocks/shard.ts b/test/mocks/shard.ts new file mode 100644 index 00000000..572b46db --- /dev/null +++ b/test/mocks/shard.ts @@ -0,0 +1,3 @@ +const masterShardId = 0; + +export { masterShardId }; diff --git a/test/vitest.config.ts b/test/vitest.config.ts index 15eb3d44..393f40a9 100644 --- a/test/vitest.config.ts +++ b/test/vitest.config.ts @@ -7,9 +7,10 @@ export default defineConfig({ hookTimeout: 60_000, testTimeout: 60_000, globals: true, + coverage: { + reportsDirectory: "./test/coverage", + provider: "v8", + reportOnFailure: true, + }, }, }); - -// start testing with local node -// we will have something public in the future. -// or we can mock api in the future.