├── .editorconfig ├── .github └── workflows │ └── npm-publish.yml ├── .gitignore ├── .vscode ├── launch.json └── settings.json ├── LICENSE ├── LICENSE.1 ├── README.md ├── package-lock.json ├── package.json ├── source ├── index.tests.ts └── index.ts ├── tsconfig-es.json ├── tsconfig-node.json ├── tsconfig-tests.json └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | charset = utf-8 6 | end_of_line = lf 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | 10 | [*.yml] 11 | indent_style = space 12 | indent_size = 2 13 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v[0-9]+.[0-9]+.[0-9]+' 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: 'ubuntu-latest' 12 | 13 | steps: 14 | - uses: 'actions/checkout@v2' 15 | with: 16 | ref: '${{ github.event.release.target_commitish }}' 17 | - name: 'Use Node.js 14' 18 | uses: 'actions/setup-node@v1' 19 | with: 20 | node-version: '14' 21 | registry-url: 'https://registry.npmjs.org/' 22 | - run: 'echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV' 23 | - run: 'npm version --no-git-tag-version ${{ env.RELEASE_VERSION }}' 24 | - run: 'npm ci' 25 | - run: 'npm run build' 26 | - run: 'npm run test' 27 | - run: 'npm publish' 28 | env: 29 | NODE_AUTH_TOKEN: '${{ secrets.NPM_TOKEN }}' 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /output-es/ 3 | /output-node/ 4 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "node", 6 | "request": "launch", 7 | "name": "test", 8 | "cwd": "${workspaceFolder}", 9 | "runtimeArgs": [ "-r", "ts-node/register", ], 10 | "args": [ "${workspaceFolder}/source/index.tests.ts", ], 11 | "env": { 12 | "TS_NODE_PROJECT": "${workspaceFolder}/tsconfig-tests.json", 13 | } 14 | }, 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules\\typescript\\lib" 3 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /LICENSE.1: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @zoltu/ethereum-types 2 | [![npm (scoped)](https://img.shields.io/npm/v/@zoltu/ethereum-types?label=npm%20-%20%40zoltu%2Fethereum-types)](https://www.npmjs.com/package/@zoltu/ethereum-types) 3 | 4 | A collection of typed primitives that can be shared/used across libraries when interacting with Ethereum. 5 | 6 | This library uses `bigint` heavily, which is part of ES2020. If you are targeting legacy browsers, this is not the library for you. If you don't hate yourself, then this library is a great choice! 7 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@zoltu/ethereum-types", 3 | "version": "0.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/chai": { 8 | "version": "4.2.15", 9 | "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.15.tgz", 10 | "integrity": "sha512-rYff6FI+ZTKAPkJUoyz7Udq3GaoDZnxYDEvdEdFZASiA7PoErltHezDishqQiSDWrGxvxmplH304jyzQmjp0AQ==", 11 | "dev": true 12 | }, 13 | "@types/chai-datetime": { 14 | "version": "0.0.36", 15 | "resolved": "https://registry.npmjs.org/@types/chai-datetime/-/chai-datetime-0.0.36.tgz", 16 | "integrity": "sha512-1LGVPvZaMjcBE1XyA2mvVBTjIpzmjr9gGwdd7gmc6u0qDQYIfHWYQfKOAY2sj/GkjN1wsMFkM/IlCMwoVEmSJw==", 17 | "dev": true, 18 | "requires": { 19 | "@types/chai": "*" 20 | } 21 | }, 22 | "arg": { 23 | "version": "4.1.3", 24 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", 25 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", 26 | "dev": true 27 | }, 28 | "assertion-error": { 29 | "version": "1.1.0", 30 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", 31 | "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", 32 | "dev": true 33 | }, 34 | "buffer-from": { 35 | "version": "1.1.1", 36 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", 37 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", 38 | "dev": true 39 | }, 40 | "chai": { 41 | "version": "4.3.0", 42 | "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.0.tgz", 43 | "integrity": "sha512-/BFd2J30EcOwmdOgXvVsmM48l0Br0nmZPlO0uOW4XKh6kpsUumRXBgPV+IlaqFaqr9cYbeoZAM1Npx0i4A+aiA==", 44 | "dev": true, 45 | "requires": { 46 | "assertion-error": "^1.1.0", 47 | "check-error": "^1.0.2", 48 | "deep-eql": "^3.0.1", 49 | "get-func-name": "^2.0.0", 50 | "pathval": "^1.1.0", 51 | "type-detect": "^4.0.5" 52 | } 53 | }, 54 | "chai-bytes": { 55 | "version": "0.1.2", 56 | "resolved": "https://registry.npmjs.org/chai-bytes/-/chai-bytes-0.1.2.tgz", 57 | "integrity": "sha512-0ol6oJS0y1ozj6AZK8n1pyv1/G+l44nqUJygAkK1UrYl+IOGie5vcrEdrAlwmLYGIA9NVvtHWosPYwWWIXf/XA==", 58 | "dev": true 59 | }, 60 | "chai-datetime": { 61 | "version": "1.8.0", 62 | "resolved": "https://registry.npmjs.org/chai-datetime/-/chai-datetime-1.8.0.tgz", 63 | "integrity": "sha512-qBG84K8oQNz8LWacuzmCBfdoeG2UBFfbGKTSQj6lS+sjuzGUdBvjJxfZfGA4zDAMiCSqApKcuqSLO0lQQ25cHw==", 64 | "dev": true, 65 | "requires": { 66 | "chai": ">1.9.0" 67 | } 68 | }, 69 | "check-error": { 70 | "version": "1.0.2", 71 | "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", 72 | "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", 73 | "dev": true 74 | }, 75 | "create-require": { 76 | "version": "1.1.1", 77 | "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", 78 | "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", 79 | "dev": true 80 | }, 81 | "deep-eql": { 82 | "version": "3.0.1", 83 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", 84 | "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", 85 | "dev": true, 86 | "requires": { 87 | "type-detect": "^4.0.0" 88 | } 89 | }, 90 | "diff": { 91 | "version": "4.0.2", 92 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 93 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 94 | "dev": true 95 | }, 96 | "get-func-name": { 97 | "version": "2.0.0", 98 | "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", 99 | "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", 100 | "dev": true 101 | }, 102 | "make-error": { 103 | "version": "1.3.6", 104 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", 105 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", 106 | "dev": true 107 | }, 108 | "pathval": { 109 | "version": "1.1.1", 110 | "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", 111 | "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", 112 | "dev": true 113 | }, 114 | "source-map": { 115 | "version": "0.6.1", 116 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 117 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 118 | "dev": true 119 | }, 120 | "source-map-support": { 121 | "version": "0.5.19", 122 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", 123 | "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", 124 | "dev": true, 125 | "requires": { 126 | "buffer-from": "^1.0.0", 127 | "source-map": "^0.6.0" 128 | } 129 | }, 130 | "ts-node": { 131 | "version": "9.1.1", 132 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", 133 | "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", 134 | "dev": true, 135 | "requires": { 136 | "arg": "^4.1.0", 137 | "create-require": "^1.1.0", 138 | "diff": "^4.0.1", 139 | "make-error": "^1.1.1", 140 | "source-map-support": "^0.5.17", 141 | "yn": "3.1.1" 142 | } 143 | }, 144 | "type-detect": { 145 | "version": "4.0.8", 146 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", 147 | "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", 148 | "dev": true 149 | }, 150 | "typescript": { 151 | "version": "4.1.5", 152 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.5.tgz", 153 | "integrity": "sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA==", 154 | "dev": true 155 | }, 156 | "yn": { 157 | "version": "3.1.1", 158 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", 159 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", 160 | "dev": true 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@zoltu/ethereum-types", 3 | "description": "Some type definitions to use when working with Ethereum.", 4 | "version": "0.0.0", 5 | "repository": { 6 | "url": "https://github.com/zoltu/ethereum-types" 7 | }, 8 | "license": "Unlicense", 9 | "main": "output-node/index.js", 10 | "module": "output-es/index.js", 11 | "devDependencies": { 12 | "@types/chai": "4.2.15", 13 | "@types/chai-datetime": "0.0.36", 14 | "chai": "4.3.0", 15 | "chai-bytes": "0.1.2", 16 | "chai-datetime": "1.8.0", 17 | "ts-node": "9.1.1", 18 | "typescript": "4.1.5" 19 | }, 20 | "scripts": { 21 | "build": "tsc --project tsconfig-es.json && tsc --project tsconfig-node.json", 22 | "test": "ts-node --project tsconfig-tests.json source/index.tests.ts" 23 | }, 24 | "files": [ 25 | "/output-es/", 26 | "/output-node/", 27 | "/source/", 28 | "README.md", 29 | "LICENSE" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /source/index.tests.ts: -------------------------------------------------------------------------------- 1 | import { Bytes, TransactionReceipt, Transaction, Block, wireEncodeByteArray, wireEncodeOffChainTransaction, wireEncodeNumber, wireEncodeBlockTag, wireEncodeOnChainTransaction, validateJsonRpcResponse } from "./index" 2 | import { assert, use as chaiUse } from "chai" 3 | import chaiBytes from 'chai-bytes' 4 | import chaiDatetime from 'chai-datetime' 5 | chaiUse(chaiBytes) 6 | chaiUse(chaiDatetime) 7 | 8 | declare global { const console: { log: (message: string) => void; error: (message: string) => void } } 9 | 10 | function testBytes() { 11 | assert.equalBytes(Bytes.fromUnsignedInteger(0n, 8), [0x00]) 12 | assert.equalBytes(Bytes.fromUnsignedInteger(0n, 256), [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) 13 | assert.equalBytes(Bytes.fromSignedInteger(0n, 8), [0x00]) 14 | assert.equalBytes(Bytes.fromUnsignedInteger(1n, 8), [0x01]) 15 | assert.equalBytes(Bytes.fromSignedInteger(1n, 8), [0x01]) 16 | assert.equalBytes(Bytes.fromSignedInteger(-1n, 8), [0xff]) 17 | assert.equalBytes(Bytes.fromHexString('0x00'), [0x00]) 18 | assert.equalBytes(Bytes.fromHexString('00'), [0x00]) 19 | assert.equalBytes(Bytes.fromHexString('0x000000'), [0x00, 0x00, 0x00]) 20 | assert.equalBytes(Bytes.fromHexString('00000000'), [0x00, 0x00, 0x00, 0x00]) 21 | assert.equalBytes(new Bytes(1), [0x00]) 22 | assert.equal(new Bytes(1).to0xString(), '0x00') 23 | assert.equal(new Bytes(1).toString(), '00') 24 | assert.equal(new Bytes(1).toUnsignedBigint(), 0n) 25 | assert.equal(new Bytes(1).toSignedBigint(), 0n) 26 | assert.equal(Bytes.fromUnsignedInteger(1n, 8).toUnsignedBigint(), 1n) 27 | assert.equal(Bytes.fromSignedInteger(1n, 8).toSignedBigint(), 1n) 28 | assert.equal(Bytes.fromSignedInteger(-1n, 8).toSignedBigint(), -1n) 29 | assert.equal(Bytes.fromHexString('ff').toSignedBigint(), -1n) 30 | assert.equal(Bytes.fromHexString('ff').toUnsignedBigint(), 255n) 31 | assert.throws(() => Bytes.fromUnsignedInteger(256n, 8)) 32 | assert.equalBytes(new Bytes(1), [0x00]) 33 | assert.equalBytes(Bytes.fromHexString('0x'), []) 34 | assert.equalBytes(Bytes.fromHexString(''), []) 35 | assert.equalBytes(Bytes.fromStringLiteral('hello'), [0x68, 0x65, 0x6c, 0x6c, 0x6f]) 36 | assert.equalBytes(Bytes.fromStringLiteral('hello', 'none'), [0x68, 0x65, 0x6c, 0x6c, 0x6f]) 37 | assert.equalBytes(Bytes.fromStringLiteral('hello', 'left'), [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x65, 0x6c, 0x6c, 0x6f]) 38 | assert.equalBytes(Bytes.fromStringLiteral('hello', 'right'), [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) 39 | } 40 | 41 | function testTransactionReceipt() { 42 | const sampleRawLog = { 43 | "logIndex": "0x1", // 1 44 | "blockNumber": "0x1b4", // 436 45 | "blockHash": "0xb10cb10cb10cb10cb10cb10cb10cb10cb10cb10cb10cb10cb10cb10cb10cb10c", 46 | "transactionHash": "0xcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeef", 47 | "transactionIndex": "0x0", // 0 48 | "address": "0xdeadbabedeadbabedeadbabedeadbabedeadbabe", 49 | "data": "0x0000000000000000000000000000000000000000000000000000000000000000", 50 | "topics": ["0xdeaffacedeaffacedeaffacedeaffacedeaffacedeaffacedeaffacedeafface"] 51 | } 52 | const sampleRawTransactionReceipt = { 53 | "blockHash": "0xb10cb10cb10cb10cb10cb10cb10cb10cb10cb10cb10cb10cb10cb10cb10cb10c", 54 | "blockNumber": "0x6914b0", // 6886576 55 | "contractAddress": "0xbaadf00dbaadf00dbaadf00dbaadf00dbaadf00d", 56 | "from": "0xbabebabebabebabebabebabebabebabebabebabe", 57 | "to": null, 58 | "cumulativeGasUsed": "0x158e33", // 1412659 59 | "gasUsed": "0xba2e6", // 762598 60 | "logs": [sampleRawLog], 61 | "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", // technically not correct since we have a log, but code doesn't validate this 62 | "root": null, 63 | "status": "0x1", // 1 64 | "transactionHash": "0xcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeef", 65 | "transactionIndex": "0x4" // 4 66 | } 67 | const sampleTransactionReceipt = new TransactionReceipt(sampleRawTransactionReceipt) 68 | assert.equal(sampleTransactionReceipt.blockHash, 0xb10cb10cb10cb10cb10cb10cb10cb10cb10cb10cb10cb10cb10cb10cb10cb10cn) 69 | assert.equal(sampleTransactionReceipt.blockNumber, 6886576n) 70 | assert.isNotNull(sampleTransactionReceipt.contractAddress) 71 | assert.equal(sampleTransactionReceipt.contractAddress!, 0xbaadf00dbaadf00dbaadf00dbaadf00dbaadf00dn) 72 | assert.equal(sampleTransactionReceipt.cumulativeGasUsed, 1412659n) 73 | assert.equal(sampleTransactionReceipt.from, 0xbabebabebabebabebabebabebabebabebabebaben) 74 | assert.equal(sampleTransactionReceipt.gasUsed, 762598n) 75 | assert.equal(sampleTransactionReceipt.hash, 0xcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefn) 76 | assert.equal(sampleTransactionReceipt.index, 4n) 77 | assert.equal(sampleTransactionReceipt.status, true) 78 | assert.isNull(sampleTransactionReceipt.to) 79 | assert.equal(sampleTransactionReceipt.logsBloom, 0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n) 80 | assert.equal(sampleTransactionReceipt.logs.length, 1) 81 | const sampleLog = sampleTransactionReceipt.logs[0] 82 | assert.equal(sampleLog.address, 0xdeadbabedeadbabedeadbabedeadbabedeadbaben) 83 | assert.equal(sampleLog.blockHash, 0xb10cb10cb10cb10cb10cb10cb10cb10cb10cb10cb10cb10cb10cb10cb10cb10cn) 84 | assert.equal(sampleLog.blockNumber, 436n) 85 | assert.equalBytes(sampleLog.data, '0000000000000000000000000000000000000000000000000000000000000000') 86 | assert.equal(sampleLog.logIndex, 1n) 87 | assert.equal(sampleLog.topics.length, 1) 88 | assert.equal(sampleLog.topics[0], 0xdeaffacedeaffacedeaffacedeaffacedeaffacedeaffacedeaffacedeaffacen) 89 | assert.equal(sampleLog.transactionHash, 0xcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefn) 90 | assert.equal(sampleLog.transactionIndex, 0n) 91 | } 92 | 93 | function testTransaction() { 94 | const rawTransaction = { 95 | "blockHash": "0xd0007d3e0884b25c214aa15a0daeca341efeadfc46f9d323d4916b5ac4f87533", 96 | "blockNumber": "0x7a729b", 97 | "chainId": "0x1", 98 | "condition": null, 99 | "creates": null, 100 | "from": "0xfc15680a8423a540d33ca90c5e3616e93a032f94", 101 | "gas": "0xc801", 102 | "gasPrice": "0x98bca5a00", 103 | "hash": "0x55066e2f47b7fdede6ab6f4a1abb026d9e7bbff94259d2e8da8ed7fb40e87673", 104 | "input": "0xa9059cbb0000000000000000000000002c55161d18a002307dc721d9bbc6502e1523e9b50000000000000000000000000000000000000000000000000000000000000050", 105 | "nonce": "0x35d", 106 | "publicKey": "0xd8b42a0bb61c0dc876fae704807949e4c38e132233304b92deeae2e9e2d7d404e0941835bac5e556ef509e4591ff6d15285cb2519eb43d377d620115c03834a5", 107 | "r": "0x7bb1ee45cab7da0a651ad45f3b28f4f1503125132d35c211648139cedbd99bbb", 108 | "raw": "0xf8ab82035d85098bca5a0082c80194d9dbe80995dbe64e371464b94d78baf10a694ed080b844a9059cbb0000000000000000000000002c55161d18a002307dc721d9bbc6502e1523e9b5000000000000000000000000000000000000000000000000000000000000005026a07bb1ee45cab7da0a651ad45f3b28f4f1503125132d35c211648139cedbd99bbba05257df01b2ec8b3384344f29b4c03c1715c1eee30e9c3fd82c0b29691344200e", 109 | "s": "0x5257df01b2ec8b3384344f29b4c03c1715c1eee30e9c3fd82c0b29691344200e", 110 | "standardV": "0x1", 111 | "to": "0xd9dbe80995dbe64e371464b94d78baf10a694ed0", 112 | "transactionIndex": "0x0", 113 | "v": "0x26", 114 | "value": "0x0" 115 | } 116 | const transaction = new Transaction(rawTransaction) 117 | assert.equal(transaction.blockHash!, 0xd0007d3e0884b25c214aa15a0daeca341efeadfc46f9d323d4916b5ac4f87533n) 118 | assert.equal(transaction.blockNumber, 0x7a729bn) 119 | assert.equalBytes(transaction.data, 'a9059cbb0000000000000000000000002c55161d18a002307dc721d9bbc6502e1523e9b50000000000000000000000000000000000000000000000000000000000000050') 120 | assert.equal(transaction.from, 0xfc15680a8423a540d33ca90c5e3616e93a032f94n) 121 | assert.equal(transaction.gas, 0xc801n) 122 | assert.equal(transaction.gasPrice, 0x98bca5a00n) 123 | assert.equal(transaction.hash, 0x55066e2f47b7fdede6ab6f4a1abb026d9e7bbff94259d2e8da8ed7fb40e87673n) 124 | assert.equal(transaction.index, 0n) 125 | assert.equal(transaction.nonce, 0x35dn) 126 | assert.equal(transaction.r, 0x7bb1ee45cab7da0a651ad45f3b28f4f1503125132d35c211648139cedbd99bbbn) 127 | assert.equal(transaction.s, 0x5257df01b2ec8b3384344f29b4c03c1715c1eee30e9c3fd82c0b29691344200en) 128 | assert.equal(transaction.to!, 0xd9dbe80995dbe64e371464b94d78baf10a694ed0n) 129 | assert.equal(transaction.v, 0x26n) 130 | assert.equal(transaction.value, 0n) 131 | } 132 | 133 | function testBlock() { 134 | const rawBlock = { 135 | "author": "0x5a0b54d5dc17e0aadc383d2db43b0a0d3e029c4c", 136 | "difficulty": "0x78844c551bb64", 137 | "extraData": "0x5050594520737061726b706f6f6c2d6574682d636e2d687a32", 138 | "gasLimit": "0x7a1200", 139 | "gasUsed": "0x79d085", 140 | "baseFeePerGas": "0x8", 141 | "hash": "0xc2d892b79e5dd49ac92b09e4b3fdecad98e64d0abcd73adb9f942ae5fa6b858a", 142 | "logsBloom": "0x00c0c0420145010000490200301900000092000a45408440028000080008110080080a41000900b0400020014010050042002d008e800810005000320025011a040c00000026081008a41088080004002200146082040018c080400424500060022c000009002802201048502080000184033a08000830820044803405000819140001010101000008e2041404050031203654402130124085804290001048c820c08000225071809085009ec044030020180ca0048518000000082000220000000004028200000000050110221c0c2008000a0140081002000880820c81001e20062000004901008850629404012004102108000001d4000404110902000021", 143 | "miner": "0x5a0b54d5dc17e0aadc383d2db43b0a0d3e029c4c", 144 | "mixHash": "0x0d9effe3c486645832f8da82b2e96f85ceae55cf8965128040939824e1fbe234", 145 | "nonce": "0x8a7ec0380d2e65f1", 146 | "number": "0x7a876d", 147 | "parentHash": "0xcf512261ac81443eaadfd1b92bbcd73d053f3b4bcc6004fe6da4f148120e8da9", 148 | "receiptsRoot": "0x13b6265c328b199e3eb30b29a17e0da9d77991630ff7d20d58f201881854e244", 149 | "sealFields": [ 150 | "0xa00d9effe3c486645832f8da82b2e96f85ceae55cf8965128040939824e1fbe234", 151 | "0x888a7ec0380d2e65f1" 152 | ], 153 | "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", 154 | "size": "0x2e23", 155 | "stateRoot": "0x0f5c0beb0a2d2e9d5af0c8de1d43642d52933677320d5b490fb13d6bb496f816", 156 | "timestamp": "0x5d12ac07", 157 | "totalDifficulty": "0x246e0f10a832a4c2d73", 158 | "transactions": [ 159 | "0x36752158b33526c07a6dd016153f2c734f71f714363bcc21161806e7ceb18430", 160 | "0x25a8a35de22e90826f61008f3decb36a3f64b7fae9dd55c6b0461fee541973e4", 161 | "0xd36c3373bb6323a4be7d1765352dc5d37fbd1f792353b827e3f5f7150bd5f8a9", 162 | "0x115bcff9e66e7cfc6fb8e3cc21096494d28027f0c628144d9524cc2aadd84fb7", 163 | "0x0fde07c15405854f0b6633f8706ada2a657c5604a3ba01c08520babc856395c1", 164 | "0x5e68f4367e22b39123a05339b98e90dad01c2da0a0a1b13685baad76536a15f0", 165 | "0x7b39a7f229c4197c94109550d0a8fc7cb484c7108bf228bec7ca2368c53a1c47", 166 | "0xa8067ee8f252dde7f22e32a58fc9334853c766fd8a863bad538a86334e5bbf27", 167 | "0x34bec87186e3ec3b5870f22526bda2809bcadcdbe9cf50262f50d2b4203b3352", 168 | "0xba6a03aae8a81f8df1e07ebb11c8127db0d9ebf7b321303948b7d301985a25f5", 169 | "0x3f81cd4211d72f5053455cdcf22577e8d34c33f90943d7b107beb1808c57b3fd", 170 | "0x03e7e26a363a294fc847f6bedddde35f82bfbea6f9f8e9b6231d39909631b7d7", 171 | "0x573833cf054694f0ebf558614cd73b33b7cf19f2e9545163e6e2795db7eef945", 172 | "0x075530f150fc6e602215930a7b680157eb81bbe7814c6fe8d3eb902d67710e80", 173 | "0x2fa036810a17bf36c28b501ec26ca5b3f3de9b6226d19eb41b7d39ee005c5976", 174 | "0x1c9d193301fc78f3fbfaea1c5b4de18ff9ebddb5e38e79ba7a229cc078d72125", 175 | "0x95453232a721bcb04f5ec757696c3211633502f18f51d5557652425d791e01be", 176 | "0x8964b0539c0085ed265152235dd511b1dccd96428e7857189e752c731f630be6", 177 | "0xa157b97d0818c20c7157f79e348154f0a5dc55574aa76b9f397fb7b4768bdaf6", 178 | "0x9c0dfe66d63611df2fa2226d82f9209b42ff2abcb2cf877b6dcf74c64d671574", 179 | "0xb6fdc87e79d901c4bf22d2b9aaec3eda6a7cd88938f9aa3f55c0d52b7fdc27b6", 180 | "0x9ec26eaf211b114bcc6f0ba5151d3f30753023084f27375f0433f0db6466c687", 181 | "0x6436d803b9170427bc2791f13e761bde5176b24ccd609d78306809303f4c7f9d", 182 | "0xe5c10153e21d12b5d5d40d48b0b45c7c561550ec65e6857a1580cfbb9417d7d6", 183 | "0x1cee551ad0c520e4e9f56b2ea5d0670742e3514083bc3d303133ec7d85f3f92e", 184 | "0x8e7769743db85507a4887ccb2d290efc2dde40aa6f347333602fb8d0e33b4644", 185 | "0xe84718d938847984f60a2e3f7551f5ed8d82964936130ebf002a9bb59f25f88e", 186 | "0xd31db75d672da40d9f9f89512e499231e9cc37e9c6cffc29cb8ee5239b9387e6", 187 | "0x5174a50cabf63cf90c2d29d5ce20a88e23504380f2f99bf541c0c3e9fc000428", 188 | "0x4fced0509e77dcdf4fea01e22e0155cfd465548de3110e7a1390b5152ae031e9", 189 | "0x0cecabadbd8d6d796607e503f9b1586a47e56e07c4963ae6902948940ee1f13c", 190 | "0x298a213a984e9c008a7c1f0cd34dfe08bcd55fd1e69c5254f0bd1972ba7ed44c", 191 | "0x12ad0e6c39788cc027d88f66daeaa60bbaa3848324ac840ae8c79f068a13163c", 192 | "0x18d90d9aabad22d16c1a33118a62c9db49f6129fcb3368ab76333f1b3df56ac4", 193 | "0x17bf1e6b9c04f8133b51b4c2ec9ed86068ebedf8a67a68b654b6bb54f2dbcb81", 194 | "0x8fd0d5741063fecf0fceb4df29d025bafac2272deb8c8fb6210da845b603a188", 195 | "0xaf26613cdcfdbd754a2bb516186e3f7177cb18352551074cccb31e5d0691d454", 196 | "0xb4902531cdbc24b918632977a8e666e3c55bf41418f5ef99426a791932a8ac98", 197 | "0x712c7bbe5a6ea9cd6eb2517c8cd00ad51122ff0500ece63a85a8f3e179182d13", 198 | "0xb90915ef37bd6d4a7dc90f222d006975406c35d56d0510865016b2808a0a4bd4", 199 | "0x551d1d590c558cc0c8f1c730d77921c6f9967552cd6e36818f7e69caf7aa2124", 200 | "0xfff185d6b2cea7f077f2d860486c4e091ce8cec0f5d494c20c37973ce9c23bea", 201 | "0x775abea223453e60ea4e4abf5beabd7e4f36618737e2f09bd737620c8b933e30", 202 | "0x9c18c72dfeb23e91af3a5b872467c850d62e344fc606e82c04e7663248b6a84b", 203 | "0xb7b159a80fc56005d39d9ece332aea9265f93c132cada747d3b1fec97949922a", 204 | "0x6423d4297f90733e452efba5b79e3180adb4c37280e1c4a4dbf5de0f9231240b", 205 | "0xa9d8fcec3b2e95ce113bc3e8d892b8d1c52301dc8d4ea15f363c5683a59b4242", 206 | "0x21ff06072dbf105ab18bb0f0b9693bd984694bccf810ba5285887a93ea92cd22", 207 | "0x58399d30d0187df4614244447a7415ff36b3fe45c5b5b713091504ad76f197bf", 208 | "0x3eb9611449f719b9a7960d84ef4fe3d220398f9dc8bd61ad92b7155c42694793", 209 | "0xa8b1eeee7ae640b2e58205b7b10cfd12de84ade9a12d64d33037916ccd8dcd3e", 210 | "0x76aeb275327ac76df10c716ab54207293006f22c50e6f78186878a5e1ce7a699", 211 | "0xf418bde323b5bc2b523feb3e079799168e424400a5a019b07c244fb8d04228ea", 212 | "0xeadc8c4fd30e5187ab92a030f58912282b2a91f875d6f37dada260a249455ea3", 213 | "0xd08bf6c095f6d650abeed64a2a6b8640cc85c714a447f046c6c3050c0fff0385", 214 | "0xa05b152874f98844fbb190e8aa9921d8fa0bfc4772afaf69e89c7d31c287454d", 215 | "0xaea7b35ce32c18c995b42d12581333f99bbb2e70503e2fadf987a03347bb6b37", 216 | "0xa672d5956982090fb4d4947e8a61df742d703ff135e86a90fa2c21bcf0edd8a9", 217 | "0xce3fa76e086bb91580617c43123767ed8f2af8d595d79e0629ea733031600df4", 218 | "0x3f7f5c488b207d3661106493027fcdca92cfb6e2e6f9a88a490407503cd7ac98", 219 | "0xe123930bb6baf208cc4a6c0e5a52e605d4973cc1833680d44d10c3abe49e3514", 220 | "0x4aaf4cd51330e35f456fe82b72702e5db51c624ee8deea0fd55447f49e8bba4b", 221 | "0xa1a37a894aa14a7c284fdbda1944e1a68776bd58e77abc2b021427b895fec92b", 222 | "0x8f7adae2b468cdf5529c27fc75a0108b4d51326fe34d71b4a6a2fbf133448ebe", 223 | "0xc3fcd9bc864d7cab1b2016883392eda489caeb4dcce54758441e6e57d4f58081", 224 | "0x2110ce8e6188bd28fcfcbab309176dff75f5cff1a739b9909b06a1a908bc7e5d", 225 | "0x54f4815366d1f215fa5913b86b30c78edb4fd0d233edeb0fcc3442860fc4a77f", 226 | "0x27eeff55f00caafbd494095d3314493f95cf7217fab97d0455acef560ca53f80", 227 | "0x025c12541b28c37bcbb6d354bf26331cc87ae55f7fc4b065568c030fbc398904", 228 | "0xce9a369c48643caebcd5e5b1f72d478e7937fdf22e2a9ecdb66ff3c608fb1128", 229 | "0xb784f8db73e99131c17b72c7dcf51e23e0d90f2bde81d983118b0ec879fbb627", 230 | "0x945f4f71c8d48ccdc17859e53cfeb5b1b17f6def79296b4dcc4e424857bf90b4", 231 | "0x5c4d30f7fa62950e39ebc94795ddf655aecd1fecb751a6bd2a6d217a586749bc", 232 | "0x1d50aacac83d0fcfa6b3138aa1af2d2f8f43ff915b6d9f500a30c8c0f2548a89", 233 | "0xef1591509970829a80fed0aa36fec496fbf8e663e23202e5bb4f8a2d955b2ce8", 234 | "0xf6735ee6ae2cc6c4c003d75eb04422cd16a3d46c98175bfa3b74b0906e0ae9cc", 235 | "0xa5980a5a2f0a7ad36995c9f972e7a0e28b28bd856c76bcd361a09f2e21d47a0a", 236 | "0xd0e4950e6ec31f2dd5134e07edb407e62975f9f98c203923dc0ee483bda9271a", 237 | "0x71bf7ace2e6cbd35957818b679016f77d5b9ea093259d310fb307d23936d904c", 238 | "0x2bafaedfa9ecf124fbf5502867d8629bc2035039a011610df080093ef72c9210", 239 | "0x0262f20da2c025add32d8ceb36cbca8bedd05ca1464f54719e048bdd23bcf4cd", 240 | "0x5c305b78a5f4d7c6d0418adeb43e830322c20ac610126c0b6505a64903566d3b", 241 | "0x35e8c1656beb4a849571643f6d5c3456af01402aeaadf8531f572a745275238b", 242 | "0x028c1e92eaabfae8ad569d956df204d45a8f9c2b0fd18f1d8fbbdaf936cbebae", 243 | "0x72a7c495e027c277b3d5b308b608c0960764e4def294b990c8ecd2d49beae443", 244 | "0xa6d7cbd82437e4c384f518310482e8a0d253c0bc177d336bf2e2ba70a93ceab7", 245 | "0x3c0c98ad8ee279a59a0b93ddadc65848d422ba25516103f045cf74128be1e599", 246 | "0x0174afcd5924fd321f9f3f28c537bd348b22bc673cbe6d21c4373809ae0e5f6d", 247 | "0x5f911a7c1513ea2e6de05aff09475eaaf8deecd5bd15be9be06c8d9eaf07f46d", 248 | "0x073e2764343a061349c690f9546b85916fea14ebd6432542e5ae7b17143ffad7", 249 | "0xa2dcb7b7f99443555569f304afd024b4dae4a77296f5ca15f9541c90098647d1", 250 | "0x5736964d7d81db7d0f3042623132aad34a1bb587e951901c595b74faeb59686d" 251 | ], 252 | "transactionsRoot": "0x0e999f06814a61a5d23f99adeee92911f782b551580a08bb5c8d6fc3519573d4", 253 | "uncles": [] 254 | } 255 | const block = new Block(rawBlock) 256 | assert.equal(block.difficulty, 0x78844c551bb64n) 257 | assert.equalBytes(block.extraData, '5050594520737061726b706f6f6c2d6574682d636e2d687a32') 258 | assert.equal(block.gasLimit, 0x7a1200n) 259 | assert.equal(block.gasUsed, 0x79d085n) 260 | assert.equal(block.hash!, 0xc2d892b79e5dd49ac92b09e4b3fdecad98e64d0abcd73adb9f942ae5fa6b858an) 261 | assert.isNotNull(block.logsBloom) 262 | assert.equal(block.logsBloom!, 0x00c0c0420145010000490200301900000092000a45408440028000080008110080080a41000900b0400020014010050042002d008e800810005000320025011a040c00000026081008a41088080004002200146082040018c080400424500060022c000009002802201048502080000184033a08000830820044803405000819140001010101000008e2041404050031203654402130124085804290001048c820c08000225071809085009ec044030020180ca0048518000000082000220000000004028200000000050110221c0c2008000a0140081002000880820c81001e20062000004901008850629404012004102108000001d4000404110902000021n) 263 | assert.equal(block.miner, 0x5a0b54d5dc17e0aadc383d2db43b0a0d3e029c4cn) 264 | assert.equal(block.nonce, 0x8a7ec0380d2e65f1n) 265 | assert.equal(block.number, 0x7a876dn) 266 | assert.equal(block.parentHash, 0xcf512261ac81443eaadfd1b92bbcd73d053f3b4bcc6004fe6da4f148120e8da9n) 267 | assert.equal(block.receiptsRoot, 0x13b6265c328b199e3eb30b29a17e0da9d77991630ff7d20d58f201881854e244n) 268 | assert.equal(block.sha3Uncles, 0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347n) 269 | assert.equal(block.size, 0x2e23n) 270 | assert.equal(block.stateRoot, 0x0f5c0beb0a2d2e9d5af0c8de1d43642d52933677320d5b490fb13d6bb496f816n) 271 | assert.equalTime(block.timestamp, new Date(0x5d12ac07 * 1000)) 272 | assert.equal(block.totalDifficulty, 0x246e0f10a832a4c2d73n) 273 | assert.equal(block.transactions.length, 92) 274 | assert.equal(block.transactions[0] as bigint, 0x36752158b33526c07a6dd016153f2c734f71f714363bcc21161806e7ceb18430n) 275 | assert.equal(block.transactionsRoot, 0x0e999f06814a61a5d23f99adeee92911f782b551580a08bb5c8d6fc3519573d4n) 276 | assert.equal(block.uncles.length, 0) 277 | } 278 | 279 | function testWireEncoding() { 280 | assert.equal(wireEncodeByteArray([0xab, 0xcd]), '0xabcd') 281 | assert.equal(wireEncodeByteArray(new Uint8Array([0xab, 0xcd])), '0xabcd') 282 | assert.equal(wireEncodeByteArray(Bytes.fromHexString('abcd')), '0xabcd') 283 | assert.equal(wireEncodeNumber(0), '0x0') 284 | assert.equal(wireEncodeNumber(1), '0x1') 285 | assert.equal(wireEncodeNumber(0n), '0x0') 286 | assert.equal(wireEncodeNumber(1n), '0x1') 287 | assert.equal(wireEncodeNumber(255), '0xff') 288 | assert.equal(wireEncodeNumber(255n), '0xff') 289 | assert.equal(wireEncodeNumber(256), '0x100') 290 | assert.equal(wireEncodeNumber(256n), '0x100') 291 | assert.equal(wireEncodeNumber(2**52), '0x10000000000000') 292 | assert.equal(wireEncodeNumber(2n**256n-1n), '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff') 293 | assert.throws(() => wireEncodeNumber(-1), 'Wire encoded values must be positive. Received: -1') 294 | assert.throws(() => wireEncodeNumber(-1n), 'Wire encoded values must be positive. Received: -1') 295 | assert.throws(() => wireEncodeNumber(2**53), 'Wire encoded number values cannot be bigger than 4503599627370496. Received: 9007199254740992') 296 | assert.throws(() => wireEncodeNumber(2n**256n), 'Wire encoded bigint values must be smaller than 115792089237316195423570985008687907853269984665640564039457584007913129639936. Received: 115792089237316195423570985008687907853269984665640564039457584007913129639936') 297 | assert.equal(wireEncodeBlockTag('latest'), 'latest') 298 | assert.equal(wireEncodeBlockTag(0xabcdn), '0xabcd') 299 | assert.deepEqual(wireEncodeOffChainTransaction({ 300 | from: 0xdeadbabedeadbabedeadbabedeadbabedeadbaben, 301 | to: 0xcafebeefcafebeefcafebeefcafebeefcafebeefn, 302 | data: new Bytes(), 303 | value: 0n, 304 | gasLimit: 100_000n, 305 | gasPrice: 2_001_000_000n, 306 | }), { 307 | from: '0xdeadbabedeadbabedeadbabedeadbabedeadbabe', 308 | to: '0xcafebeefcafebeefcafebeefcafebeefcafebeef', 309 | data: '0x', 310 | value: '0x0', 311 | gas: '0x186a0', 312 | gasPrice: '0x7744d640', 313 | }) 314 | assert.deepEqual(wireEncodeOnChainTransaction({ 315 | from: 0xdeadbabedeadbabedeadbabedeadbabedeadbaben, 316 | to: 0xcafebeefcafebeefcafebeefcafebeefcafebeefn, 317 | data: new Bytes(), 318 | value: 0n, 319 | gasLimit: 100_000n, 320 | gasPrice: 2_001_000_000n, 321 | nonce: 5n, 322 | }), { 323 | from: '0xdeadbabedeadbabedeadbabedeadbabedeadbabe', 324 | to: '0xcafebeefcafebeefcafebeefcafebeefcafebeef', 325 | data: '0x', 326 | value: '0x0', 327 | gas: '0x186a0', 328 | gasPrice: '0x7744d640', 329 | nonce: '0x5', 330 | }) 331 | } 332 | 333 | function testValidation() { 334 | assert.doesNotThrow(() => validateJsonRpcResponse({ 335 | "jsonrpc": "2.0", 336 | "result": {}, 337 | "id": 1, 338 | })) 339 | assert.doesNotThrow(() => validateJsonRpcResponse({ 340 | "jsonrpc": "2.0", 341 | "error": { 342 | code: 5, 343 | message: 'hello', 344 | }, 345 | "id": 1, 346 | })) 347 | assert.throws(() => validateJsonRpcResponse({ 348 | "jsonrpc": "2.0", 349 | "result": {}, 350 | "error": { 351 | code: 5, 352 | message: 'hello', 353 | }, 354 | "id": 1, 355 | }), /Expected JSON-RPC response, received something else\..*/) 356 | } 357 | 358 | try { 359 | testBytes() 360 | testTransactionReceipt() 361 | testTransaction() 362 | testBlock() 363 | testWireEncoding() 364 | testValidation() 365 | 366 | console.log(`\x1b[32mTests passed.\x1b[0m`) 367 | } catch (error) { 368 | console.error(error) 369 | console.log(`\x1b[31mOne or more tests failed.\x1b[0m`) 370 | } 371 | -------------------------------------------------------------------------------- /source/index.ts: -------------------------------------------------------------------------------- 1 | export class Bytes extends Uint8Array { 2 | public static fromByteArray(bytes: ArrayLike, pad: 'left' | 'right' = 'right'): Bytes { 3 | const result = new this(bytes.length) 4 | if (bytes.length > result.length) throw new Error(`Source bytes are longer (${bytes.length}) than destination bytes (${result.length})\n${bytes}`) 5 | for (let i = 0; i < bytes.length; ++i) { 6 | const byte = bytes[i] 7 | if (byte > 0xff || byte < 0) throw new Error(`Source array must only include numbers between 0 and ${0xff}.\n${bytes}`) 8 | } 9 | result.set(bytes, (pad === 'left') ? result.length - bytes.length : 0) 10 | return result 11 | } 12 | 13 | public static fromHexString(hex: string, pad?: 'left' | 'right'): Bytes { 14 | const match = /^(?:0x)?([a-fA-F0-9]*)$/.exec(hex) 15 | if (match === null) throw new Error(`Expected a hex string encoded byte array with an optional '0x' prefix but received ${hex}`) 16 | const normalized = match[1] 17 | if (normalized.length % 2) throw new Error(`Hex string encoded byte array must be an even number of charcaters long.`) 18 | const bytes = [] 19 | for (let i = 0; i < normalized.length; i += 2) { 20 | bytes.push(Number.parseInt(`${normalized[i]}${normalized[i + 1]}`, 16)) 21 | } 22 | return this.fromByteArray(bytes, pad) 23 | } 24 | 25 | public static fromStringLiteral(literal: string, pad32: 'left' | 'right' | 'none' = 'none'): Bytes { 26 | const encoded = new TextEncoder().encode(literal) 27 | const padding = new Uint8Array((32 - encoded.length % 32) % 32) 28 | switch (pad32) { 29 | case 'none': 30 | return this.fromByteArray(encoded) 31 | case 'left': 32 | return this.fromByteArray([...padding, ...encoded]) 33 | case 'right': 34 | return this.fromByteArray([...encoded, ...padding]) 35 | default: 36 | throw new Error(`Invalid 'pad32' parameter: ${pad32}.`) 37 | } 38 | } 39 | 40 | public static fromUnsignedInteger(value: bigint | number, numberOfBits: number): Bytes { 41 | if (numberOfBits % 8) throw new Error(`numberOfBits must be a multiple of 8.`) 42 | if (typeof value === 'number') value = BigInt(value) 43 | if (value >= 2n ** BigInt(numberOfBits) || value < 0n) throw new Error(`Cannot fit ${value} into a ${numberOfBits}-bit unsigned integer.`) 44 | const numberOfBytes = numberOfBits / 8 45 | const result = new this(numberOfBytes) 46 | if (result.length !== numberOfBytes) throw new Error(`Cannot a ${numberOfBits} value into a ${result.length} byte array.`) 47 | for (let i = 0; i < result.length; ++i) { 48 | result[i] = Number((value >> BigInt(numberOfBits - i * 8 - 8)) & 0xffn) 49 | } 50 | return result 51 | } 52 | 53 | public static fromSignedInteger(value: bigint | number, numberOfBits: number): Bytes { 54 | if (typeof value === 'number') value = BigInt(value) 55 | if (value >= 2n ** BigInt(numberOfBits - 1) || value < -(2n ** BigInt(numberOfBits - 1))) throw new Error(`Cannot fit ${value} into a ${numberOfBits}-bit signed integer.`) 56 | const unsignedValue = this.twosComplement(value, numberOfBits) 57 | return this.fromUnsignedInteger(unsignedValue, numberOfBits) 58 | } 59 | 60 | public readonly toString = () => this.reduce((result: string, byte: number) => result + ('0' + byte.toString(16)).slice(-2), '') 61 | 62 | public readonly to0xString = () => wireEncodeByteArray(this) 63 | 64 | public readonly toUnsignedBigint = () => { 65 | let value = 0n 66 | for (let byte of this) { 67 | value = (value << 8n) + BigInt(byte) 68 | } 69 | return value 70 | } 71 | 72 | public readonly toSignedBigint = () => { 73 | const unsignedValue = this.toUnsignedBigint() 74 | return Bytes.twosComplement(unsignedValue, this.length * 8) 75 | } 76 | 77 | public readonly equals = (other: { length: number, [i: number]: number } | undefined | null): boolean => { 78 | if (other === undefined || other === null) return false 79 | if (this.length !== other.length) return false 80 | for (let i = 0; i < this.length; ++i) { 81 | if (this[i] !== other[i]) return false 82 | } 83 | return true 84 | } 85 | 86 | // this is important TypeScript magic whose provenance and purpose has been lost to time 87 | public static get [Symbol.species]() { return Uint8Array } 88 | 89 | private static twosComplement(value: bigint, numberOfBits: number): bigint { 90 | const mask = 2n ** (BigInt(numberOfBits) - 1n) - 1n 91 | return (value & mask) - (value & ~mask) 92 | } 93 | } 94 | 95 | export type Encodable = EncodablePrimitive | EncodableTuple | EncodableArray 96 | export type EncodablePrimitive = Uint8Array | string | boolean | bigint 97 | export interface EncodableTuple { readonly [x: string]: Encodable } 98 | export interface EncodableArray extends ReadonlyArray { } 99 | 100 | export type RawHash = string 101 | export type RawQuantity = string 102 | export type RawBlockTag = string 103 | export type RawAddress = string 104 | export type RawData = string 105 | 106 | export interface RawLog { 107 | readonly blockHash: RawHash 108 | readonly blockNumber: RawQuantity 109 | readonly transactionHash: RawHash 110 | readonly transactionIndex: RawQuantity 111 | readonly logIndex: RawQuantity 112 | readonly address: RawAddress 113 | readonly topics: Array 114 | readonly data: RawData 115 | } 116 | 117 | export interface RawTransactionReceipt { 118 | readonly blockHash: RawHash 119 | readonly blockNumber: RawQuantity 120 | readonly transactionHash: RawHash 121 | readonly transactionIndex: RawQuantity 122 | readonly from: RawAddress 123 | readonly to: RawAddress | null 124 | readonly contractAddress: RawAddress | null 125 | readonly cumulativeGasUsed: RawQuantity 126 | readonly gasUsed: RawQuantity 127 | readonly logs: Array 128 | readonly logsBloom: RawData 129 | readonly status: RawQuantity 130 | } 131 | 132 | export interface RawTransaction { 133 | readonly blockHash: RawHash | null 134 | readonly blockNumber: RawQuantity | null 135 | readonly hash: RawHash 136 | readonly transactionIndex: RawQuantity | null 137 | readonly from: RawAddress 138 | readonly to: RawAddress | null 139 | readonly value: RawQuantity 140 | readonly input: RawData 141 | readonly nonce: RawQuantity 142 | readonly gas: RawQuantity 143 | readonly gasPrice: RawQuantity 144 | readonly r: RawQuantity 145 | readonly s: RawQuantity 146 | readonly v: RawQuantity 147 | } 148 | 149 | export interface RawBlock { 150 | readonly hash: RawHash | null 151 | readonly number: RawQuantity | null 152 | readonly nonce: RawData | null | undefined 153 | readonly logsBloom: RawData | null 154 | readonly parentHash: RawHash 155 | readonly sha3Uncles: RawHash 156 | readonly transactionsRoot: RawData 157 | readonly stateRoot: RawData 158 | readonly receiptsRoot: RawData 159 | readonly miner: RawAddress 160 | readonly difficulty: RawQuantity 161 | readonly totalDifficulty: RawQuantity 162 | readonly extraData: RawData 163 | readonly size: RawQuantity 164 | readonly gasLimit: RawQuantity 165 | readonly gasUsed: RawQuantity 166 | readonly baseFeePerGas: RawQuantity 167 | readonly timestamp: RawQuantity 168 | readonly mixHash: RawHash | undefined 169 | readonly transactions: Array 170 | readonly uncles: Array 171 | } 172 | 173 | export interface RawTypedData { 174 | readonly types: { 175 | readonly EIP712Domain: Array<{ name: string, type: string }> 176 | readonly [type: string]: Array<{ name: string, type: string }> 177 | } 178 | readonly primaryType: string 179 | readonly domain: unknown 180 | readonly message: unknown 181 | } 182 | 183 | export interface RawMerklePatritiaProof { 184 | balance: RawQuantity 185 | codeHash: RawHash 186 | nonce: RawQuantity 187 | storageHash: RawHash 188 | accountProof: Array 189 | storageProof: Array<{ 190 | key: RawQuantity 191 | // https://github.com/NethermindEth/nethermind/pull/1987 192 | value: RawQuantity | null 193 | proof: Array 194 | }> 195 | } 196 | 197 | export interface RawOffChainTransaction { 198 | readonly from: RawAddress 199 | readonly to: RawAddress | null 200 | readonly value: RawQuantity 201 | readonly data: RawData 202 | readonly gas?: RawQuantity 203 | readonly gasPrice?: RawQuantity 204 | } 205 | 206 | export interface RawOnChainTransaction extends RawOffChainTransaction { 207 | readonly gas: RawQuantity 208 | readonly gasPrice: RawQuantity 209 | readonly nonce: RawQuantity 210 | } 211 | 212 | export interface RawSignedTransaction { 213 | readonly from: RawAddress 214 | readonly to: RawAddress | null 215 | readonly value: RawQuantity 216 | readonly input: RawData 217 | readonly nonce: RawQuantity 218 | readonly gas: RawQuantity 219 | readonly gasPrice: RawQuantity 220 | readonly r: RawQuantity 221 | readonly s: RawQuantity 222 | readonly v: RawQuantity 223 | } 224 | 225 | export interface ILog { 226 | readonly blockHash: bigint 227 | readonly blockNumber: bigint 228 | readonly transactionHash: bigint 229 | readonly transactionIndex: bigint 230 | readonly logIndex: bigint 231 | readonly address: bigint 232 | readonly topics: Array 233 | readonly data: Uint8Array 234 | } 235 | 236 | export class Log implements ILog { 237 | public readonly blockHash: bigint 238 | public readonly blockNumber: bigint 239 | public readonly transactionHash: bigint 240 | public readonly transactionIndex: bigint 241 | public readonly logIndex: bigint 242 | public readonly address: bigint 243 | public readonly topics: Array 244 | public readonly data: Bytes 245 | public constructor(raw: RawLog) { 246 | this.blockHash = BigInt(raw.blockHash) 247 | this.blockNumber = BigInt(raw.blockNumber) 248 | this.transactionHash = BigInt(raw.transactionHash) 249 | this.transactionIndex = BigInt(raw.transactionIndex) 250 | this.logIndex = BigInt(raw.logIndex) 251 | this.address = BigInt(raw.address) 252 | this.topics = raw.topics.map(x => BigInt(x)) 253 | this.data = Bytes.fromHexString(raw.data) 254 | } 255 | } 256 | 257 | export interface ITransactionReceipt { 258 | readonly blockHash: bigint 259 | readonly blockNumber: bigint 260 | readonly hash: bigint 261 | readonly index: bigint 262 | readonly from: bigint 263 | readonly to: bigint | null 264 | readonly contractAddress: bigint | null 265 | readonly cumulativeGasUsed: bigint 266 | readonly gasUsed: bigint 267 | readonly logs: Array 268 | readonly logsBloom: bigint 269 | readonly status: boolean 270 | } 271 | 272 | export class TransactionReceipt implements ITransactionReceipt { 273 | public readonly blockHash: bigint 274 | public readonly blockNumber: bigint 275 | public readonly hash: bigint 276 | public readonly index: bigint 277 | public readonly from: bigint 278 | public readonly to: bigint | null 279 | public readonly contractAddress: bigint | null 280 | public readonly cumulativeGasUsed: bigint 281 | public readonly gasUsed: bigint 282 | public readonly logs: Array 283 | public readonly logsBloom: bigint 284 | public readonly status: boolean 285 | public constructor(raw: RawTransactionReceipt) { 286 | this.blockHash = BigInt(raw.blockHash) 287 | this.blockNumber = BigInt(raw.blockNumber) 288 | this.hash = BigInt(raw.transactionHash) 289 | this.index = BigInt(raw.transactionIndex) 290 | this.from = BigInt(raw.from) 291 | this.to = (raw.to) ? BigInt(raw.to!) : null 292 | this.contractAddress = (raw.contractAddress) ? BigInt(raw.contractAddress) : null 293 | this.cumulativeGasUsed = BigInt(raw.cumulativeGasUsed) 294 | this.gasUsed = BigInt(raw.gasUsed) 295 | this.logs = raw.logs.map(x => new Log(x)) 296 | this.logsBloom = BigInt(raw.logsBloom) 297 | this.status = !!Number.parseInt(raw.status, 16) 298 | } 299 | } 300 | 301 | export interface ITransaction { 302 | readonly blockHash: bigint | null 303 | readonly blockNumber: bigint | null 304 | readonly hash: bigint 305 | readonly index: bigint | null 306 | readonly from: bigint 307 | readonly to: bigint | null 308 | readonly value: bigint 309 | readonly data: Uint8Array 310 | readonly nonce: bigint 311 | readonly gas: bigint 312 | readonly gasPrice: bigint 313 | readonly r: bigint 314 | readonly s: bigint 315 | readonly v: bigint 316 | } 317 | 318 | export class Transaction implements ITransaction { 319 | public readonly blockHash: bigint | null 320 | public readonly blockNumber: bigint | null 321 | public readonly hash: bigint 322 | public readonly index: bigint | null 323 | public readonly from: bigint 324 | public readonly to: bigint | null 325 | public readonly value: bigint 326 | public readonly data: Bytes 327 | public readonly nonce: bigint 328 | public readonly gas: bigint 329 | public readonly gasPrice: bigint 330 | public readonly r: bigint 331 | public readonly s: bigint 332 | public readonly v: bigint 333 | public constructor(raw: RawTransaction) { 334 | this.blockHash = (raw.blockHash !== null) ? BigInt(raw.blockHash) : null 335 | this.blockNumber = (raw.blockNumber !== null) ? BigInt(raw.blockNumber) : null 336 | this.hash = BigInt(raw.hash) 337 | this.index = (raw.transactionIndex !== null) ? BigInt(raw.transactionIndex) : null 338 | this.from = BigInt(raw.from) 339 | this.to = (raw.to !== null) ? BigInt(raw.to) : null 340 | this.value = BigInt(raw.value) 341 | this.data = Bytes.fromHexString(raw.input) 342 | this.nonce = BigInt(raw.nonce) 343 | this.gas = BigInt(raw.gas) 344 | this.gasPrice = BigInt(raw.gasPrice) 345 | this.r = BigInt(raw.r) 346 | this.s = BigInt(raw.s) 347 | this.v = BigInt(raw.v) 348 | } 349 | } 350 | 351 | export interface IBlock { 352 | readonly hash: bigint | null 353 | readonly number: bigint | null 354 | readonly nonce: bigint | null 355 | readonly logsBloom: bigint | null 356 | readonly parentHash: bigint 357 | readonly sha3Uncles: bigint 358 | readonly transactionsRoot: bigint 359 | readonly stateRoot: bigint 360 | readonly receiptsRoot: bigint 361 | readonly miner: bigint 362 | readonly difficulty: bigint 363 | readonly totalDifficulty: bigint 364 | readonly extraData: Uint8Array 365 | readonly size: bigint 366 | readonly gasLimit: bigint 367 | readonly gasUsed: bigint 368 | readonly baseFeePerGas: bigint 369 | readonly timestamp: Date 370 | readonly mixHash: bigint | null 371 | readonly transactions: Array 372 | readonly uncles: Array 373 | } 374 | 375 | export class Block implements IBlock { 376 | public readonly hash: bigint | null 377 | public readonly number: bigint | null 378 | /** Will be null for OpenEthereum Proof of Authority networks. */ 379 | public readonly nonce: bigint | null 380 | public readonly logsBloom: bigint | null 381 | public readonly parentHash: bigint 382 | public readonly sha3Uncles: bigint 383 | public readonly transactionsRoot: bigint 384 | public readonly stateRoot: bigint 385 | public readonly receiptsRoot: bigint 386 | public readonly miner: bigint 387 | public readonly difficulty: bigint 388 | public readonly totalDifficulty: bigint 389 | public readonly extraData: Bytes 390 | public readonly size: bigint 391 | public readonly gasLimit: bigint 392 | public readonly gasUsed: bigint 393 | public readonly baseFeePerGas: bigint 394 | public readonly timestamp: Date 395 | /** Will be null for OpenEthereum Proof of Authority networks. */ 396 | public readonly mixHash: bigint | null 397 | public readonly transactions: Array 398 | public readonly uncles: Array 399 | public constructor(raw: RawBlock) { 400 | this.hash = (raw.hash !== null) ? BigInt(raw.hash) : null 401 | this.number = (raw.number !== null && raw.number) ? BigInt(raw.number) : null 402 | this.nonce = (raw.nonce !== null && raw.nonce !== undefined) ? BigInt(raw.nonce) : null 403 | this.logsBloom = (raw.logsBloom !== null) ? BigInt(raw.logsBloom) : null 404 | this.parentHash = BigInt(raw.parentHash) 405 | this.sha3Uncles = BigInt(raw.sha3Uncles) 406 | this.transactionsRoot = BigInt(raw.transactionsRoot) 407 | this.stateRoot = BigInt(raw.stateRoot) 408 | this.receiptsRoot = BigInt(raw.receiptsRoot) 409 | this.miner = BigInt(raw.miner) 410 | this.difficulty = BigInt(raw.difficulty) 411 | this.totalDifficulty = BigInt(raw.totalDifficulty) 412 | this.extraData = Bytes.fromHexString(raw.extraData) 413 | this.size = BigInt(raw.size) 414 | this.gasLimit = BigInt(raw.gasLimit) 415 | this.gasUsed = BigInt(raw.gasUsed) 416 | this.baseFeePerGas = BigInt(raw.baseFeePerGas) 417 | this.timestamp = new Date(Number.parseInt(raw.timestamp) * 1000) 418 | this.mixHash = (raw.mixHash !== undefined) ? BigInt(raw.mixHash) : null 419 | this.transactions = raw.transactions.map(x => (typeof x === 'string') ? BigInt(x) : new Transaction(x)) 420 | this.uncles = raw.uncles.map(x => BigInt(x)) 421 | } 422 | } 423 | 424 | export class MerklePatritiaProof { 425 | balance: bigint 426 | codeHash: bigint 427 | nonce: bigint 428 | storageHash: bigint 429 | accountProof: readonly Bytes[] 430 | storageProof: readonly { 431 | key: bigint 432 | value: bigint 433 | proof: readonly Bytes[] 434 | }[] 435 | constructor(raw: RawMerklePatritiaProof) { 436 | this.balance = BigInt(raw.balance) 437 | this.codeHash = BigInt(raw.codeHash) 438 | this.nonce = BigInt(raw.nonce) 439 | this.storageHash = BigInt(raw.storageHash) 440 | this.accountProof = raw.accountProof.map(x => Bytes.fromHexString(x)) 441 | this.storageProof = raw.storageProof.map(x => ({ 442 | key: BigInt(x.key), 443 | value: BigInt(x.value || '0x0'), 444 | proof: x.proof.map(y => Bytes.fromHexString(y)), 445 | })) 446 | } 447 | } 448 | 449 | export interface ISignature { 450 | readonly r: bigint 451 | readonly s: bigint 452 | readonly v: bigint 453 | } 454 | 455 | export interface IOffChainTransaction { 456 | readonly from: bigint 457 | readonly to: bigint | null 458 | readonly value: bigint 459 | readonly data: Uint8Array 460 | readonly gasLimit?: bigint 461 | readonly gasPrice?: bigint 462 | } 463 | 464 | export interface IOnChainTransaction extends IOffChainTransaction { 465 | readonly gasLimit: bigint 466 | readonly gasPrice: bigint 467 | readonly nonce: bigint 468 | } 469 | 470 | export interface IUnsignedTransaction extends IOnChainTransaction { 471 | readonly chainId: bigint 472 | } 473 | 474 | export interface ISignedTransaction extends IOnChainTransaction, ISignature { 475 | } 476 | 477 | export class SignedTransaction implements ISignedTransaction { 478 | public readonly from: bigint 479 | public readonly to: bigint | null 480 | public readonly value: bigint 481 | public readonly data: Uint8Array 482 | public readonly gasPrice: bigint 483 | public readonly gasLimit: bigint 484 | public readonly nonce: bigint 485 | public readonly r: bigint 486 | public readonly s: bigint 487 | public readonly v: bigint 488 | public constructor(raw: RawSignedTransaction) { 489 | this.from = BigInt(raw.from) 490 | this.to = (raw.to !== null) ? BigInt(raw.to) : null 491 | this.value = BigInt(raw.value) 492 | this.data = Bytes.fromHexString(raw.input) 493 | this.nonce = BigInt(raw.nonce) 494 | this.gasLimit = BigInt(raw.gas) 495 | this.gasPrice = BigInt(raw.gasPrice) 496 | this.r = BigInt(raw.r) 497 | this.s = BigInt(raw.s) 498 | this.v = BigInt(raw.v) 499 | } 500 | } 501 | 502 | export function wireEncodeByteArray(bytes: ArrayLike): string { 503 | let result = '' 504 | for (let i = 0; i < bytes.length; ++i) { 505 | result += ('0' + bytes[i].toString(16)).slice(-2) 506 | } 507 | return `0x${result}` 508 | } 509 | 510 | export function wireEncodeNumber(value: number | bigint, padding: number = 0): RawQuantity { 511 | if (value < 0) throw new Error(`Wire encoded values must be positive. Received: ${value}`) 512 | if (typeof value === 'number' && value > 2**52) throw new Error(`Wire encoded number values cannot be bigger than ${2**52}. Received: ${value}`) 513 | if (typeof value === 'bigint' && value >= 2**256) throw new Error(`Wire encoded bigint values must be smaller than ${2n**256n}. Received: ${value}`) 514 | return `0x${value.toString(16).padStart(padding, '0')}` 515 | } 516 | 517 | export type BlockTag = 'latest' | 'earliest' | 'pending' | bigint 518 | export function wireEncodeBlockTag(tag: BlockTag): RawBlockTag { return (typeof tag === 'string') ? tag : wireEncodeNumber(tag) } 519 | 520 | export function wireEncodeOffChainTransaction(transaction: IOffChainTransaction): RawOffChainTransaction { 521 | return { 522 | from: wireEncodeNumber(transaction.from, 40), 523 | to: transaction.to ? wireEncodeNumber(transaction.to, 40) : null, 524 | value: wireEncodeNumber(transaction.value), 525 | data: wireEncodeByteArray(transaction.data), 526 | ...transaction.gasLimit === undefined ? {} : { gas: wireEncodeNumber(transaction.gasLimit) }, 527 | ...transaction.gasPrice === undefined ? {} : { gasPrice: wireEncodeNumber(transaction.gasPrice) }, 528 | } 529 | } 530 | 531 | export function wireEncodeOnChainTransaction(transaction: IOnChainTransaction): RawOnChainTransaction { 532 | return { 533 | ...wireEncodeOffChainTransaction(transaction), 534 | gas: wireEncodeNumber(transaction.gasLimit), 535 | gasPrice: wireEncodeNumber(transaction.gasPrice), 536 | nonce: wireEncodeNumber(transaction.nonce), 537 | } 538 | } 539 | 540 | export type JsonRpcMethod = 'eth_accounts' | 'eth_blockNumber' | 'eth_call' | 'eth_chainId' | 'eth_coinbase' | 'eth_estimateGas' | 'eth_gasPrice' | 'eth_getBalance' | 'eth_getBlockByHash' | 'eth_getBlockByNumber' | 'eth_getBlockTransactionCountByHash' | 'eth_getBlockTransactionCountByNumber' | 'eth_getCode' | 'eth_getLogs' | 'eth_getProof' | 'eth_getStorageAt' | 'eth_getTransactionByBlockHashAndIndex' | 'eth_getTransactionByBlockNumberAndIndex' | 'eth_getTransactionByHash' | 'eth_getTransactionCount' | 'eth_getTransactionReceipt' | 'eth_getUncleByBlockHashAndIndex' | 'eth_getUncleByBlockNumberAndIndex' | 'eth_getUncleCountByBlockHash' | 'eth_getUncleCountByBlockNumber' | 'eth_protocolVersion' | 'eth_sendRawTransaction' | 'eth_sendTransaction' | 'eth_sign' | 'eth_signTransaction' | 'eth_signTypedData' | 'eth_syncing' 541 | export interface IJsonRpcRequest> { 542 | readonly jsonrpc: '2.0' 543 | readonly id: string | number | null 544 | readonly method: TMethod 545 | readonly params: TParams 546 | } 547 | export interface IJsonRpcSuccess { 548 | readonly jsonrpc: '2.0' 549 | readonly id: string | number | null 550 | readonly result: TResult 551 | } 552 | export interface IJsonRpcError { 553 | readonly jsonrpc: '2.0' 554 | readonly id: string | number | null 555 | readonly error: { 556 | readonly code: number 557 | readonly message: string 558 | readonly data?: unknown 559 | } 560 | } 561 | export type IJsonRpcResponse = IJsonRpcSuccess | IJsonRpcError 562 | export function validateJsonRpcResponse(response: any): response is IJsonRpcResponse { 563 | if (response.jsonrpc !== '2.0' 564 | || (typeof response.id !== 'string' && typeof response.id !== 'number' && response.id !== null) 565 | || ('result' in response && 'error' in response) 566 | || (!('result' in response) && !('error' in response)) 567 | || (response.error && typeof response.error.code !== 'number') 568 | || (response.error && typeof response.error.message !== 'string')) 569 | throw new Error(`Expected JSON-RPC response, received something else.\n${JSON.stringify(response)}`) 570 | return true 571 | } 572 | export function isJsonRpcSuccess(response: IJsonRpcResponse): response is IJsonRpcSuccess { 573 | return !!(response as IJsonRpcSuccess).result && !(response as IJsonRpcError).error 574 | } 575 | export function isJsonRpcError(response: IJsonRpcResponse): response is IJsonRpcError { 576 | return !!(response as IJsonRpcError).error && !(response as IJsonRpcSuccess).result 577 | } 578 | 579 | export namespace Rpc { 580 | export namespace Eth { 581 | export namespace Accounts { 582 | export interface RawRequest extends IJsonRpcRequest<'eth_accounts', []> { } 583 | export interface RawResponse extends IJsonRpcSuccess> { } 584 | export class Request { 585 | public constructor(public readonly id: string | number | null) { } 586 | public readonly wireEncode = (): RawRequest => ({ 587 | jsonrpc: '2.0', 588 | id: this.id, 589 | method: 'eth_accounts', 590 | params: [], 591 | }) 592 | } 593 | export class Response { 594 | public readonly id: string | number | null 595 | public readonly result: Array 596 | public constructor(raw: RawResponse) { 597 | this.id = raw.id 598 | this.result = raw.result.map(x => BigInt(x)) 599 | } 600 | } 601 | } 602 | export namespace BlockNumber { 603 | export interface RawRequest extends IJsonRpcRequest<'eth_blockNumber', []> { } 604 | export interface RawResponse extends IJsonRpcSuccess { } 605 | export class Request { 606 | public constructor(public readonly id: string | number | null) { } 607 | public readonly wireEncode = (): RawRequest => ({ 608 | jsonrpc: '2.0', 609 | id: this.id, 610 | method: 'eth_blockNumber', 611 | params: [], 612 | }) 613 | } 614 | export class Response { 615 | public readonly result: bigint 616 | public constructor(raw: RawResponse) { 617 | this.result = BigInt(raw.result) 618 | } 619 | } 620 | } 621 | export namespace Call { 622 | export interface RawRequest extends IJsonRpcRequest<'eth_call', [RawOffChainTransaction, RawBlockTag]> { } 623 | export interface RawResponse extends IJsonRpcSuccess { } 624 | export class Request { 625 | public constructor( 626 | public readonly id: string | number | null, 627 | public readonly transaction: IOffChainTransaction, 628 | public readonly blockTag: BlockTag = 'latest', 629 | ) { } 630 | public readonly wireEncode = (): RawRequest => ({ 631 | jsonrpc: '2.0', 632 | id: this.id, 633 | method: 'eth_call', 634 | params: [ wireEncodeOffChainTransaction(this.transaction), wireEncodeBlockTag(this.blockTag) ], 635 | }) 636 | } 637 | export class Response { 638 | public readonly result: Bytes 639 | public constructor(raw: RawResponse) { 640 | this.result = Bytes.fromHexString(raw.result) 641 | } 642 | } 643 | } 644 | export namespace ChainId { 645 | export interface RawRequest extends IJsonRpcRequest<'eth_chainId', []> { } 646 | export interface RawResponse extends IJsonRpcSuccess { } 647 | export class Request { 648 | public constructor(public readonly id: string | number | null) { } 649 | public readonly wireEncode = (): RawRequest => ({ 650 | jsonrpc: '2.0', 651 | id: this.id, 652 | method: 'eth_chainId', 653 | params: [], 654 | }) 655 | } 656 | export class Response { 657 | public readonly result: bigint 658 | public constructor(raw: RawResponse) { 659 | const result = raw.result ? BigInt(raw.result) : null 660 | if (result === null) throw new Error(`eth_chainId returned null`) 661 | this.result = result 662 | } 663 | } 664 | } 665 | export namespace Coinbase { 666 | export interface RawRequest extends IJsonRpcRequest<'eth_coinbase', []> { } 667 | export interface RawResponse extends IJsonRpcSuccess { } 668 | export class Request { 669 | public constructor(public readonly id: string | number | null) { } 670 | public readonly wireEncode = (): RawRequest => ({ 671 | jsonrpc: '2.0', 672 | id: this.id, 673 | method: 'eth_coinbase', 674 | params: [], 675 | }) 676 | } 677 | export class Response { 678 | public readonly result: bigint | null 679 | public constructor(raw: RawResponse) { 680 | this.result = raw !== null ? BigInt(raw.result) : null 681 | } 682 | } 683 | } 684 | export namespace EstimateGas { 685 | export interface RawRequest extends IJsonRpcRequest<'eth_estimateGas', [RawOffChainTransaction]> { } 686 | export interface RawResponse extends IJsonRpcSuccess { } 687 | export class Request { 688 | public constructor( 689 | public readonly id: string | number | null, 690 | public readonly transaction: IOffChainTransaction, 691 | ) { } 692 | public readonly wireEncode = (): RawRequest => ({ 693 | jsonrpc: '2.0', 694 | id: this.id, 695 | method: 'eth_estimateGas', 696 | params: [ wireEncodeOffChainTransaction(this.transaction) ], 697 | }) 698 | } 699 | export class Response { 700 | public readonly result: bigint 701 | public constructor(raw: RawResponse) { 702 | this.result = BigInt(raw.result) 703 | } 704 | } 705 | } 706 | export namespace GasPrice { 707 | export interface RawRequest extends IJsonRpcRequest<'eth_gasPrice', []> { } 708 | export interface RawResponse extends IJsonRpcSuccess { } 709 | export class Request { 710 | public constructor(public readonly id: string | number | null) { } 711 | public readonly wireEncode = (): RawRequest => ({ 712 | jsonrpc: '2.0', 713 | id: this.id, 714 | method: 'eth_gasPrice', 715 | params: [], 716 | }) 717 | } 718 | export class Response { 719 | public readonly result: bigint 720 | public constructor(raw: RawResponse) { 721 | this.result = BigInt(raw.result) 722 | } 723 | } 724 | } 725 | export namespace GetBalance { 726 | export interface RawRequest extends IJsonRpcRequest<'eth_getBalance', [RawAddress, RawBlockTag]> { } 727 | export interface RawResponse extends IJsonRpcSuccess { } 728 | export class Request { 729 | public constructor( 730 | public readonly id: string | number | null, 731 | public readonly address: bigint, 732 | public readonly blockTag: BlockTag = 'latest', 733 | ) { } 734 | public readonly wireEncode = (): RawRequest => ({ 735 | jsonrpc: '2.0', 736 | id: this.id, 737 | method: 'eth_getBalance', 738 | params: [wireEncodeNumber(this.address, 40), wireEncodeBlockTag(this.blockTag)], 739 | }) 740 | } 741 | export class Response { 742 | public readonly result: bigint 743 | public constructor(raw: RawResponse) { 744 | this.result = BigInt(raw.result) 745 | } 746 | } 747 | } 748 | export namespace GetBlockByHash { 749 | export interface RawRequest extends IJsonRpcRequest<'eth_getBlockByHash', [RawHash, boolean]> { } 750 | export interface RawResponse extends IJsonRpcSuccess { } 751 | export class Request { 752 | public constructor( 753 | public readonly id: string | number | null, 754 | public readonly hash: bigint, 755 | public readonly fullTransactions: boolean = false, 756 | ) { } 757 | public readonly wireEncode = (): RawRequest => ({ 758 | jsonrpc: '2.0', 759 | id: this.id, 760 | method: 'eth_getBlockByHash', 761 | params: [wireEncodeNumber(this.hash, 64), this.fullTransactions], 762 | }) 763 | } 764 | export class Response { 765 | public readonly result: Block | null 766 | public constructor(raw: RawResponse) { 767 | this.result = (raw.result !== null) ? new Block(raw.result) : null 768 | } 769 | } 770 | } 771 | export namespace GetBlockByNumber { 772 | export interface RawRequest extends IJsonRpcRequest<'eth_getBlockByNumber', [RawBlockTag, boolean]> { } 773 | export interface RawResponse extends IJsonRpcSuccess { } 774 | export class Request { 775 | public constructor( 776 | public readonly id: string | number | null, 777 | public readonly fullTransactions: boolean = false, 778 | public readonly blockTag: BlockTag = 'latest', 779 | ) { } 780 | public readonly wireEncode = (): RawRequest => ({ 781 | jsonrpc: '2.0', 782 | id: this.id, 783 | method: 'eth_getBlockByNumber', 784 | params: [wireEncodeBlockTag(this.blockTag), this.fullTransactions], 785 | }) 786 | } 787 | export class Response { 788 | public readonly result: Block | null 789 | public constructor(raw: RawResponse) { 790 | this.result = (raw.result !== null) ? new Block(raw.result) : null 791 | } 792 | } 793 | } 794 | export namespace GetBlockTransactionCountByHash { 795 | export interface RawRequest extends IJsonRpcRequest<'eth_getBlockTransactionCountByHash', [RawHash]> { } 796 | export interface RawResponse extends IJsonRpcSuccess { } 797 | export class Request { 798 | public constructor( 799 | public readonly id: string | number | null, 800 | public readonly blockHash: bigint, 801 | ) { } 802 | public readonly wireEncode = (): RawRequest => ({ 803 | jsonrpc: '2.0', 804 | id: this.id, 805 | method: 'eth_getBlockTransactionCountByHash', 806 | params: [wireEncodeNumber(this.blockHash, 64)], 807 | }) 808 | } 809 | export class Response { 810 | public readonly result: bigint 811 | public constructor(raw: RawResponse) { 812 | this.result = BigInt(raw.result) 813 | } 814 | } 815 | } 816 | export namespace GetBlockTransactionCountByNumber { 817 | export interface RawRequest extends IJsonRpcRequest<'eth_getBlockTransactionCountByNumber', [RawBlockTag]> { } 818 | export interface RawResponse extends IJsonRpcSuccess { } 819 | export class Request { 820 | public constructor( 821 | public readonly id: string | number | null, 822 | public readonly blockTag: BlockTag = 'latest', 823 | ) { } 824 | public readonly wireEncode = (): RawRequest => ({ 825 | jsonrpc: '2.0', 826 | id: this.id, 827 | method: 'eth_getBlockTransactionCountByNumber', 828 | params: [wireEncodeBlockTag(this.blockTag)], 829 | }) 830 | } 831 | export class Response { 832 | public readonly result: bigint 833 | public constructor(raw: RawResponse) { 834 | this.result = BigInt(raw.result) 835 | } 836 | } 837 | } 838 | export namespace GetCode { 839 | export interface RawRequest extends IJsonRpcRequest<'eth_getCode', [RawAddress, RawBlockTag]> { } 840 | export interface RawResponse extends IJsonRpcSuccess { } 841 | export class Request { 842 | public constructor( 843 | public readonly id: string | number | null, 844 | public readonly address: bigint, 845 | public readonly blockTag: BlockTag = 'latest', 846 | ) { } 847 | public readonly wireEncode = (): RawRequest => ({ 848 | jsonrpc: '2.0', 849 | id: this.id, 850 | method: 'eth_getCode', 851 | params: [wireEncodeNumber(this.address, 40), wireEncodeBlockTag(this.blockTag)], 852 | }) 853 | } 854 | export class Response { 855 | public readonly result: Bytes 856 | public constructor(raw: RawResponse) { 857 | this.result = Bytes.fromHexString(raw.result) 858 | } 859 | } 860 | } 861 | export namespace GetLogs { 862 | export interface RawRequest extends IJsonRpcRequest<'eth_getLogs', [{ address: RawAddress | Array, topics: Array } & ({ fromBlock: RawBlockTag, toBlock: RawBlockTag } | { blockHash: RawHash })]> { } 863 | export interface RawResponse extends IJsonRpcSuccess> { } 864 | export class Request { 865 | public constructor( 866 | id: string | number | null, 867 | criteria: CriteriaTag, 868 | ) 869 | public constructor( 870 | id: string | number | null, 871 | criteria: CriteriaHash, 872 | ) 873 | public constructor( 874 | id: string | number | null, 875 | criteria: Criteria, 876 | ) 877 | public constructor( 878 | public readonly id: string | number | null, 879 | public readonly criteria: Criteria 880 | ){ } 881 | public readonly wireEncode = (): RawRequest => { 882 | const address = (Array.isArray(this.criteria.address)) ? this.criteria.address.map(x => wireEncodeNumber(x, 40)) : wireEncodeNumber(this.criteria.address, 40) 883 | const topics = this.criteria.topics.map(x => wireEncodeNumber(x, 64)) 884 | const criteriaBlockTarget = this.isCriteriaHash(this.criteria) 885 | ? { blockHash: wireEncodeNumber(this.criteria.blockHash, 64) } 886 | : { fromBlock: wireEncodeBlockTag(this.criteria.fromBlock), toBlock: wireEncodeBlockTag(this.criteria.toBlock) } 887 | const criteria = { address, topics, ...criteriaBlockTarget } 888 | return { 889 | jsonrpc: '2.0', 890 | id: this.id, 891 | method: 'eth_getLogs', 892 | params: [criteria], 893 | } 894 | } 895 | private readonly isCriteriaHash = (criteria: Criteria): criteria is CriteriaHash => !!(criteria as any).blockHash 896 | } 897 | export class Response { 898 | public readonly result: Array 899 | public constructor(raw: RawResponse) { 900 | this.result = raw.result.map(x => new Log(x)) 901 | } 902 | } 903 | export interface CriteriaBase { 904 | address: bigint | Array 905 | topics: Array 906 | } 907 | export interface CriteriaHash extends CriteriaBase { 908 | blockHash: bigint 909 | } 910 | export interface CriteriaTag extends CriteriaBase { 911 | fromBlock: bigint 912 | toBlock: bigint 913 | } 914 | type Criteria = CriteriaHash | CriteriaTag 915 | } 916 | export namespace GetProof { 917 | export interface RawRequest extends IJsonRpcRequest<'eth_getProof', [RawAddress, Array, RawBlockTag]> { } 918 | export interface RawResponse extends IJsonRpcSuccess { } 919 | export class Request { 920 | public constructor( 921 | public readonly id: string | number | null, 922 | public readonly address: bigint, 923 | public readonly storageKeys: readonly bigint[], 924 | public readonly blockTag: BlockTag = 'latest', 925 | ) { } 926 | public readonly wireEncode = (): RawRequest => ({ 927 | jsonrpc: '2.0', 928 | id: this.id, 929 | method: 'eth_getProof', 930 | params: [wireEncodeNumber(this.address, 40), this.storageKeys.map(x => wireEncodeNumber(x, 64)), wireEncodeBlockTag(this.blockTag)] 931 | }) 932 | } 933 | export class Response { 934 | public readonly result: MerklePatritiaProof 935 | public constructor(raw: RawResponse) { 936 | this.result = new MerklePatritiaProof(raw.result) 937 | } 938 | } 939 | } 940 | export namespace GetStorageAt { 941 | export interface RawRequest extends IJsonRpcRequest<'eth_getStorageAt', [RawAddress, RawQuantity, RawBlockTag]> { } 942 | export interface RawResponse extends IJsonRpcSuccess { } 943 | export class Request { 944 | public constructor( 945 | public readonly id: string | number | null, 946 | public readonly address: bigint, 947 | public readonly index: bigint, 948 | public readonly blockTag: BlockTag = 'latest', 949 | ) { } 950 | public readonly wireEncode = (): RawRequest => ({ 951 | jsonrpc: '2.0', 952 | id: this.id, 953 | method: 'eth_getStorageAt', 954 | params: [wireEncodeNumber(this.address, 40), wireEncodeNumber(this.index), wireEncodeBlockTag(this.blockTag)], 955 | }) 956 | } 957 | export class Response { 958 | public readonly result: bigint 959 | public constructor(raw: RawResponse) { 960 | this.result = BigInt(raw.result) 961 | } 962 | } 963 | } 964 | export namespace GetTransactionByBlockHashAndIndex { 965 | export interface RawRequest extends IJsonRpcRequest<'eth_getTransactionByBlockHashAndIndex', [RawHash, RawQuantity]> { } 966 | export interface RawResponse extends IJsonRpcSuccess { } 967 | export class Request { 968 | public constructor( 969 | public readonly id: string | number | null, 970 | public readonly blockHash: bigint, 971 | public readonly transactionIndex: bigint, 972 | ) { } 973 | public readonly wireEncode = (): RawRequest => ({ 974 | jsonrpc: '2.0', 975 | id: this.id, 976 | method: 'eth_getTransactionByBlockHashAndIndex', 977 | params: [wireEncodeNumber(this.blockHash, 64), wireEncodeNumber(this.transactionIndex)], 978 | }) 979 | } 980 | export class Response { 981 | public readonly id: string | number | null 982 | public readonly result: Transaction | null 983 | public constructor(raw: RawResponse) { 984 | this.id = raw.id 985 | this.result = (raw.result !== null) ? new Transaction(raw.result): null 986 | } 987 | } 988 | } 989 | export namespace GetTransactionByBlockNumberAndIndex { 990 | export interface RawRequest extends IJsonRpcRequest<'eth_getTransactionByBlockNumberAndIndex', [RawBlockTag, RawQuantity]> { } 991 | export interface RawResponse extends IJsonRpcSuccess { } 992 | export class Request { 993 | public constructor( 994 | public readonly id: string | number | null, 995 | public readonly transactionIndex: bigint, 996 | public readonly blockTag: BlockTag = 'latest', 997 | ) { } 998 | public readonly wireEncode = (): RawRequest => ({ 999 | jsonrpc: '2.0', 1000 | id: this.id, 1001 | method: 'eth_getTransactionByBlockNumberAndIndex', 1002 | params: [wireEncodeBlockTag(this.blockTag), wireEncodeNumber(this.transactionIndex)], 1003 | }) 1004 | } 1005 | export class Response { 1006 | public readonly id: string | number | null 1007 | public readonly result: Transaction | null 1008 | public constructor(raw: RawResponse) { 1009 | this.id = raw.id 1010 | this.result = (raw.result !== null) ? new Transaction(raw.result): null 1011 | } 1012 | } 1013 | } 1014 | export namespace GetTransactionByHash { 1015 | export interface RawRequest extends IJsonRpcRequest<'eth_getTransactionByHash', [RawHash]> { } 1016 | export interface RawResponse extends IJsonRpcSuccess { } 1017 | export class Request { 1018 | public constructor( 1019 | public readonly id: string | number | null, 1020 | public readonly transactionHash: bigint, 1021 | ) { } 1022 | public readonly wireEncode = (): RawRequest => ({ 1023 | jsonrpc: '2.0', 1024 | id: this.id, 1025 | method: 'eth_getTransactionByHash', 1026 | params: [wireEncodeNumber(this.transactionHash, 64)], 1027 | }) 1028 | } 1029 | export class Response { 1030 | public readonly id: string | number | null 1031 | public readonly result: Transaction | null 1032 | public constructor(raw: RawResponse) { 1033 | this.id = raw.id 1034 | this.result = (raw.result !== null) ? new Transaction(raw.result): null 1035 | } 1036 | } 1037 | } 1038 | export namespace GetTransactionCount { 1039 | export interface RawRequest extends IJsonRpcRequest<'eth_getTransactionCount', [RawAddress, RawBlockTag]> { } 1040 | export interface RawResponse extends IJsonRpcSuccess { } 1041 | export class Request { 1042 | public constructor( 1043 | public readonly id: string | number | null, 1044 | public readonly address: bigint, 1045 | public readonly blockTag: BlockTag = 'latest', 1046 | ) { } 1047 | public readonly wireEncode = (): RawRequest => ({ 1048 | jsonrpc: '2.0', 1049 | id: this.id, 1050 | method: 'eth_getTransactionCount', 1051 | params: [wireEncodeNumber(this.address, 40), wireEncodeBlockTag(this.blockTag)], 1052 | }) 1053 | } 1054 | export class Response { 1055 | public readonly id: string | number | null 1056 | public readonly result: bigint 1057 | public constructor(raw: RawResponse) { 1058 | this.id = raw.id 1059 | this.result = BigInt(raw.result) 1060 | } 1061 | } 1062 | } 1063 | export namespace GetTransactionReceipt { 1064 | export interface RawRequest extends IJsonRpcRequest<'eth_getTransactionReceipt', [RawHash]> { } 1065 | export interface RawResponse extends IJsonRpcSuccess { } 1066 | export class Request { 1067 | public constructor( 1068 | public readonly id: string | number | null, 1069 | public readonly transactionHash: bigint, 1070 | ) { } 1071 | public readonly wireEncode = (): RawRequest => ({ 1072 | jsonrpc: '2.0', 1073 | id: this.id, 1074 | method: 'eth_getTransactionReceipt', 1075 | params: [wireEncodeNumber(this.transactionHash, 64)], 1076 | }) 1077 | } 1078 | export class Response { 1079 | public readonly id: string | number | null 1080 | public readonly result: TransactionReceipt | null 1081 | public constructor(raw: RawResponse) { 1082 | this.id = raw.id 1083 | this.result = (raw.result !== null) ? new TransactionReceipt(raw.result) : null 1084 | } 1085 | } 1086 | } 1087 | export namespace GetUncleByBlockHashAndIndex { 1088 | export interface RawRequest extends IJsonRpcRequest<'eth_getUncleByBlockHashAndIndex', [RawHash, RawQuantity]> { } 1089 | export interface RawResponse extends IJsonRpcSuccess { } 1090 | export class Request { 1091 | public constructor( 1092 | public readonly id: string | number | null, 1093 | public readonly blockHash: bigint, 1094 | public readonly uncleIndex: bigint, 1095 | ) { } 1096 | public readonly wireEncode = (): RawRequest => ({ 1097 | jsonrpc: '2.0', 1098 | id: this.id, 1099 | method: 'eth_getUncleByBlockHashAndIndex', 1100 | params: [wireEncodeNumber(this.blockHash, 64), wireEncodeNumber(this.uncleIndex)], 1101 | }) 1102 | } 1103 | export class Response { 1104 | public readonly id: string | number | null 1105 | public readonly result: Block | null 1106 | public constructor(raw: RawResponse) { 1107 | this.id = raw.id 1108 | this.result = (raw.result !== null) ? new Block(raw.result) : null 1109 | } 1110 | } 1111 | } 1112 | export namespace GetUncleByBlockNumberAndIndex { 1113 | export interface RawRequest extends IJsonRpcRequest<'eth_getUncleByBlockNumberAndIndex', [RawBlockTag, RawQuantity]> { } 1114 | export interface RawResponse extends IJsonRpcSuccess { } 1115 | export class Request { 1116 | public constructor( 1117 | public readonly id: string | number | null, 1118 | public readonly blockTag: BlockTag, 1119 | public readonly uncleIndex: bigint, 1120 | ) { } 1121 | public readonly wireEncode = (): RawRequest => ({ 1122 | jsonrpc: '2.0', 1123 | id: this.id, 1124 | method: 'eth_getUncleByBlockNumberAndIndex', 1125 | params: [wireEncodeBlockTag(this.blockTag), wireEncodeNumber(this.uncleIndex)], 1126 | }) 1127 | } 1128 | export class Response { 1129 | public readonly id: string | number | null 1130 | public readonly result: Block | null 1131 | public constructor(raw: RawResponse) { 1132 | this.id = raw.id 1133 | this.result = (raw.result !== null) ? new Block(raw.result) : null 1134 | } 1135 | } 1136 | } 1137 | export namespace GetUncleCountByBlockHash { 1138 | export interface RawRequest extends IJsonRpcRequest<'eth_getUncleCountByBlockHash', [RawHash]> { } 1139 | export interface RawResponse extends IJsonRpcSuccess { } 1140 | export class Request { 1141 | public constructor( 1142 | public readonly id: string | number | null, 1143 | public readonly blockHash: bigint, 1144 | ) { } 1145 | public readonly wireEncode = (): RawRequest => ({ 1146 | jsonrpc: '2.0', 1147 | id: this.id, 1148 | method: 'eth_getUncleCountByBlockHash', 1149 | params: [wireEncodeNumber(this.blockHash, 64)], 1150 | }) 1151 | } 1152 | export class Response { 1153 | public readonly id: string | number | null 1154 | public readonly result: bigint 1155 | public constructor(raw: RawResponse) { 1156 | this.id = raw.id 1157 | this.result = BigInt(raw.result) 1158 | } 1159 | } 1160 | } 1161 | export namespace GetUncleCountByBlockNumber { 1162 | export interface RawRequest extends IJsonRpcRequest<'eth_getUncleCountByBlockNumber', [RawBlockTag]> { } 1163 | export interface RawResponse extends IJsonRpcSuccess { } 1164 | export class Request { 1165 | public constructor( 1166 | public readonly id: string | number | null, 1167 | public readonly blockTag: BlockTag, 1168 | ) { } 1169 | public readonly wireEncode = (): RawRequest => ({ 1170 | jsonrpc: '2.0', 1171 | id: this.id, 1172 | method: 'eth_getUncleCountByBlockNumber', 1173 | params: [wireEncodeBlockTag(this.blockTag)], 1174 | }) 1175 | } 1176 | export class Response { 1177 | public readonly id: string | number | null 1178 | public readonly result: bigint 1179 | public constructor(raw: RawResponse) { 1180 | this.id = raw.id 1181 | this.result = BigInt(raw.result) 1182 | } 1183 | } 1184 | } 1185 | export namespace ProtocolVersion { 1186 | export interface RawRequest extends IJsonRpcRequest<'eth_protocolVersion', []> { } 1187 | export interface RawResponse extends IJsonRpcSuccess { } 1188 | export class Request { 1189 | public constructor( 1190 | public readonly id: string | number | null, 1191 | ) { } 1192 | public readonly wireEncode = (): RawRequest => ({ 1193 | jsonrpc: '2.0', 1194 | id: this.id, 1195 | method: 'eth_protocolVersion', 1196 | params: [], 1197 | }) 1198 | } 1199 | export class Response { 1200 | public readonly id: string | number | null 1201 | public readonly result: string 1202 | public constructor(raw: RawResponse) { 1203 | this.id = raw.id 1204 | this.result = raw.result 1205 | } 1206 | } 1207 | } 1208 | export namespace SendRawTransaction { 1209 | export interface RawRequest extends IJsonRpcRequest<'eth_sendRawTransaction', [RawData]> { } 1210 | export interface RawResponse extends IJsonRpcSuccess { } 1211 | export class Request { 1212 | public constructor( 1213 | public readonly id: string | number | null, 1214 | public readonly signedTransaction: Uint8Array, 1215 | ) { } 1216 | public readonly wireEncode = (): RawRequest => ({ 1217 | jsonrpc: '2.0', 1218 | id: this.id, 1219 | method: 'eth_sendRawTransaction', 1220 | params: [wireEncodeByteArray(this.signedTransaction)], 1221 | }) 1222 | } 1223 | export class Response { 1224 | public readonly id: string | number | null 1225 | public readonly result: bigint 1226 | public constructor(raw: RawResponse) { 1227 | this.id = raw.id 1228 | this.result = BigInt(raw.result) 1229 | } 1230 | } 1231 | } 1232 | export namespace SendTransaction { 1233 | export interface RawRequest extends IJsonRpcRequest<'eth_sendTransaction', [RawOnChainTransaction]> { } 1234 | export interface RawResponse extends IJsonRpcSuccess { } 1235 | export class Request { 1236 | public constructor( 1237 | public readonly id: string | number | null, 1238 | public readonly transaction: IOnChainTransaction, 1239 | ) { } 1240 | public readonly wireEncode = (): RawRequest => ({ 1241 | jsonrpc: '2.0', 1242 | id: this.id, 1243 | method: 'eth_sendTransaction', 1244 | params: [wireEncodeOnChainTransaction(this.transaction)], 1245 | }) 1246 | } 1247 | export class Response { 1248 | public readonly id: string | number | null 1249 | public readonly result: bigint 1250 | public constructor(raw: RawResponse) { 1251 | this.id = raw.id 1252 | this.result = BigInt(raw.result) 1253 | } 1254 | } 1255 | } 1256 | export namespace Sign { 1257 | export interface RawRequest extends IJsonRpcRequest<'eth_sign', [RawAddress, RawData]> { } 1258 | export interface RawResponse extends IJsonRpcSuccess { } 1259 | export class Request { 1260 | public constructor( 1261 | public readonly id: string | number | null, 1262 | public readonly signerAddress: bigint, 1263 | public readonly data: Uint8Array, 1264 | ) { } 1265 | public readonly wireEncode = (): RawRequest => ({ 1266 | jsonrpc: '2.0', 1267 | id: this.id, 1268 | method: 'eth_sign', 1269 | params: [wireEncodeNumber(this.signerAddress, 40), wireEncodeByteArray(this.data)], 1270 | }) 1271 | } 1272 | export class Response { 1273 | public readonly id: string | number | null 1274 | public readonly result: Bytes 1275 | public constructor(raw: RawResponse) { 1276 | this.id = raw.id 1277 | this.result = Bytes.fromHexString(raw.result) 1278 | } 1279 | } 1280 | } 1281 | export namespace SignTransaction { 1282 | export interface RawRequest extends IJsonRpcRequest<'eth_signTransaction', [RawOnChainTransaction]> { } 1283 | export interface RawResponse extends IJsonRpcSuccess<{raw:RawData, tx: RawSignedTransaction}> { } 1284 | export class Request { 1285 | public constructor( 1286 | public readonly id: string | number | null, 1287 | public readonly transaction: IOnChainTransaction, 1288 | ) { } 1289 | public readonly wireEncode = (): RawRequest => ({ 1290 | jsonrpc: '2.0', 1291 | id: this.id, 1292 | method: 'eth_signTransaction', 1293 | params: [wireEncodeOnChainTransaction(this.transaction)], 1294 | }) 1295 | } 1296 | export class Response { 1297 | public readonly id: string | number | null 1298 | public readonly result: { 1299 | decodedTransaction: ISignedTransaction 1300 | encodedTransaction: Uint8Array 1301 | } 1302 | public constructor(raw: RawResponse) { 1303 | this.id = raw.id 1304 | this.result = { 1305 | decodedTransaction: new SignedTransaction(raw.result.tx), 1306 | encodedTransaction: Bytes.fromHexString(raw.result.raw), 1307 | } 1308 | } 1309 | } 1310 | } 1311 | export namespace Syncing { 1312 | export interface RawRequest extends IJsonRpcRequest<'eth_syncing', []> { } 1313 | export interface RawResponse extends IJsonRpcSuccess { } 1314 | export class Request { 1315 | public constructor( 1316 | public readonly id: string | number | null, 1317 | ) { } 1318 | public readonly wireEncode = (): RawRequest => ({ 1319 | jsonrpc: '2.0', 1320 | id: this.id, 1321 | method: 'eth_syncing', 1322 | params: [], 1323 | }) 1324 | } 1325 | export class Response { 1326 | public readonly id: string | number | null 1327 | public readonly result: false | { readonly currentBlock: bigint, readonly highestBlock: bigint, readonly startingBlock: bigint } 1328 | public constructor(raw: RawResponse) { 1329 | this.id = raw.id 1330 | this.result = (typeof raw.result === 'boolean') ? raw.result : { 1331 | currentBlock: BigInt(raw.result.currentBlock), 1332 | highestBlock: BigInt(raw.result.highestBlock), 1333 | startingBlock: BigInt(raw.result.startingBlock), 1334 | } 1335 | } 1336 | } 1337 | } 1338 | } 1339 | } 1340 | 1341 | type DropFirst = ((...t: T) => void) extends ((x: any, ...u: infer U) => void) ? U : never 1342 | type ResultType = T extends { readonly result: infer R } ? R : never 1343 | type RpcMethod< 1344 | TRequestConstructor extends new (id: string | number | null, ...args: any[]) => { wireEncode: () => IJsonRpcRequest }, 1345 | TResponseConstructor extends new (rawResponse: IJsonRpcSuccess) => { readonly result: any }, 1346 | > = (...args: DropFirst>) => Promise>> 1347 | type MakeRequired = T & { [Key in K]-?: T[Key] } 1348 | 1349 | export interface JsonRpc { 1350 | readonly sendEth: (destination: bigint, amount: bigint) => Promise 1351 | readonly deployContract: (bytecode: Uint8Array, value?: bigint) => Promise 1352 | readonly onChainContractCall: (transaction: MakeRequired, 'to'|'data'>) => Promise 1353 | readonly offChainContractCall: (transaction: MakeRequired, 'to'|'data'>) => Promise 1354 | readonly remoteProcedureCall: < 1355 | TRawRequest extends IJsonRpcRequest>, 1356 | TRawResponse extends IJsonRpcSuccess 1357 | >(request: TRawRequest) => Promise 1358 | 1359 | readonly call: RpcMethod 1360 | readonly coinbase: RpcMethod 1361 | readonly estimateGas: RpcMethod 1362 | readonly getAccounts: RpcMethod 1363 | readonly getBalance: RpcMethod 1364 | readonly getBlockByHash: RpcMethod 1365 | readonly getBlockByNumber: RpcMethod 1366 | readonly getBlockNumber: RpcMethod 1367 | readonly getBlockTransactionCountByHash: RpcMethod 1368 | readonly getBlockTransactionCountByNumber: RpcMethod 1369 | readonly getChainId: RpcMethod 1370 | readonly getCode: RpcMethod 1371 | readonly getGasPrice: RpcMethod 1372 | readonly getLogs: RpcMethod 1373 | readonly getProof: RpcMethod 1374 | readonly getStorageAt: RpcMethod 1375 | readonly getTransactionByBlockHashAndIndex: RpcMethod 1376 | readonly getTransactionByBlockNumberAndIndex: RpcMethod 1377 | readonly getTransactionByHash: RpcMethod 1378 | readonly getTransactionCount: RpcMethod 1379 | readonly getTransactionReceipt: RpcMethod 1380 | readonly getUncleByBlockHashAndIndex: RpcMethod 1381 | readonly getUncleByBlockNumberAndIndex: RpcMethod 1382 | readonly getUncleCountByBlockHash: RpcMethod 1383 | readonly getUncleCountByBlockNumber: RpcMethod 1384 | readonly getProtocolVersion: RpcMethod 1385 | readonly sendRawTransaction: RpcMethod 1386 | readonly sendTransaction: RpcMethod 1387 | readonly signTransaction: RpcMethod 1388 | readonly sign: RpcMethod 1389 | readonly syncing: RpcMethod 1390 | } 1391 | 1392 | // https://github.com/microsoft/TypeScript/issues/31535 1393 | interface TextEncoder { 1394 | /** Returns "utf-8". */ 1395 | readonly encoding: string 1396 | /** Returns the result of running UTF-8's encoder. */ 1397 | encode(input?: string): Uint8Array 1398 | } 1399 | declare var TextEncoder: { prototype: TextEncoder; new(): TextEncoder } 1400 | -------------------------------------------------------------------------------- /tsconfig-es.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "es2015", 5 | "moduleResolution": "classic", 6 | "outDir": "output-es", 7 | "noEmit": false, 8 | }, 9 | "exclude": [ 10 | "source/**/*.tests.ts", 11 | ], 12 | } 13 | -------------------------------------------------------------------------------- /tsconfig-node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "outDir": "output-node", 7 | "noEmit": false, 8 | }, 9 | "exclude": [ 10 | "source/**/*.tests.ts", 11 | ], 12 | } 13 | -------------------------------------------------------------------------------- /tsconfig-tests.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "esModuleInterop": true, 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "none", 5 | "moduleResolution": "node", 6 | "rootDir": "source", 7 | "strict": true, 8 | "sourceMap": true, 9 | "inlineSources": true, 10 | "declaration": true, 11 | "declarationMap": true, 12 | "noEmit": true, 13 | "noImplicitAny": true, 14 | "noImplicitThis": true, 15 | "noImplicitReturns": true, 16 | "noUnusedLocals": true, 17 | "noUnusedParameters": true, 18 | "esModuleInterop": true, 19 | "lib": [ "esnext" ], 20 | }, 21 | "include": [ 22 | "source/**/*.ts", 23 | ], 24 | } 25 | --------------------------------------------------------------------------------