@feelyourprotocol/util 8141.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +297 -0
  3. package/dist/cjs/account.d.ts +165 -0
  4. package/dist/cjs/account.d.ts.map +1 -0
  5. package/dist/cjs/account.js +530 -0
  6. package/dist/cjs/account.js.map +1 -0
  7. package/dist/cjs/address.d.ts +67 -0
  8. package/dist/cjs/address.d.ts.map +1 -0
  9. package/dist/cjs/address.js +136 -0
  10. package/dist/cjs/address.js.map +1 -0
  11. package/dist/cjs/authorization.d.ts +41 -0
  12. package/dist/cjs/authorization.d.ts.map +1 -0
  13. package/dist/cjs/authorization.js +135 -0
  14. package/dist/cjs/authorization.js.map +1 -0
  15. package/dist/cjs/bal.d.ts +129 -0
  16. package/dist/cjs/bal.d.ts.map +1 -0
  17. package/dist/cjs/bal.js +529 -0
  18. package/dist/cjs/bal.js.map +1 -0
  19. package/dist/cjs/binaryTree.d.ts +148 -0
  20. package/dist/cjs/binaryTree.d.ts.map +1 -0
  21. package/dist/cjs/binaryTree.js +240 -0
  22. package/dist/cjs/binaryTree.js.map +1 -0
  23. package/dist/cjs/blobs.d.ts +76 -0
  24. package/dist/cjs/blobs.d.ts.map +1 -0
  25. package/dist/cjs/blobs.js +175 -0
  26. package/dist/cjs/blobs.js.map +1 -0
  27. package/dist/cjs/bytes.d.ts +291 -0
  28. package/dist/cjs/bytes.d.ts.map +1 -0
  29. package/dist/cjs/bytes.js +606 -0
  30. package/dist/cjs/bytes.js.map +1 -0
  31. package/dist/cjs/constants.d.ts +91 -0
  32. package/dist/cjs/constants.d.ts.map +1 -0
  33. package/dist/cjs/constants.js +97 -0
  34. package/dist/cjs/constants.js.map +1 -0
  35. package/dist/cjs/db.d.ts +65 -0
  36. package/dist/cjs/db.d.ts.map +1 -0
  37. package/dist/cjs/db.js +14 -0
  38. package/dist/cjs/db.js.map +1 -0
  39. package/dist/cjs/env.d.ts +9 -0
  40. package/dist/cjs/env.d.ts.map +1 -0
  41. package/dist/cjs/env.js +13 -0
  42. package/dist/cjs/env.js.map +1 -0
  43. package/dist/cjs/errors.d.ts +3 -0
  44. package/dist/cjs/errors.d.ts.map +1 -0
  45. package/dist/cjs/errors.js +19 -0
  46. package/dist/cjs/errors.js.map +1 -0
  47. package/dist/cjs/helpers.d.ts +21 -0
  48. package/dist/cjs/helpers.d.ts.map +1 -0
  49. package/dist/cjs/helpers.js +50 -0
  50. package/dist/cjs/helpers.js.map +1 -0
  51. package/dist/cjs/index.d.ts +67 -0
  52. package/dist/cjs/index.d.ts.map +1 -0
  53. package/dist/cjs/index.js +93 -0
  54. package/dist/cjs/index.js.map +1 -0
  55. package/dist/cjs/internal.d.ts +72 -0
  56. package/dist/cjs/internal.d.ts.map +1 -0
  57. package/dist/cjs/internal.js +182 -0
  58. package/dist/cjs/internal.js.map +1 -0
  59. package/dist/cjs/kzg.d.ts +14 -0
  60. package/dist/cjs/kzg.d.ts.map +1 -0
  61. package/dist/cjs/kzg.js +3 -0
  62. package/dist/cjs/kzg.js.map +1 -0
  63. package/dist/cjs/lock.d.ts +15 -0
  64. package/dist/cjs/lock.d.ts.map +1 -0
  65. package/dist/cjs/lock.js +45 -0
  66. package/dist/cjs/lock.js.map +1 -0
  67. package/dist/cjs/mapDB.d.ts +17 -0
  68. package/dist/cjs/mapDB.d.ts.map +1 -0
  69. package/dist/cjs/mapDB.js +46 -0
  70. package/dist/cjs/mapDB.js.map +1 -0
  71. package/dist/cjs/package.json +3 -0
  72. package/dist/cjs/provider.d.ts +46 -0
  73. package/dist/cjs/provider.d.ts.map +1 -0
  74. package/dist/cjs/provider.js +84 -0
  75. package/dist/cjs/provider.js.map +1 -0
  76. package/dist/cjs/request.d.ts +20 -0
  77. package/dist/cjs/request.d.ts.map +1 -0
  78. package/dist/cjs/request.js +35 -0
  79. package/dist/cjs/request.js.map +1 -0
  80. package/dist/cjs/signature.d.ts +47 -0
  81. package/dist/cjs/signature.d.ts.map +1 -0
  82. package/dist/cjs/signature.js +147 -0
  83. package/dist/cjs/signature.js.map +1 -0
  84. package/dist/cjs/tasks.d.ts +32 -0
  85. package/dist/cjs/tasks.d.ts.map +1 -0
  86. package/dist/cjs/tasks.js +51 -0
  87. package/dist/cjs/tasks.js.map +1 -0
  88. package/dist/cjs/types.d.ts +64 -0
  89. package/dist/cjs/types.d.ts.map +1 -0
  90. package/dist/cjs/types.js +78 -0
  91. package/dist/cjs/types.js.map +1 -0
  92. package/dist/cjs/units.d.ts +22 -0
  93. package/dist/cjs/units.d.ts.map +1 -0
  94. package/dist/cjs/units.js +51 -0
  95. package/dist/cjs/units.js.map +1 -0
  96. package/dist/cjs/withdrawal.d.ts +72 -0
  97. package/dist/cjs/withdrawal.d.ts.map +1 -0
  98. package/dist/cjs/withdrawal.js +93 -0
  99. package/dist/cjs/withdrawal.js.map +1 -0
  100. package/dist/esm/account.d.ts +165 -0
  101. package/dist/esm/account.d.ts.map +1 -0
  102. package/dist/esm/account.js +505 -0
  103. package/dist/esm/account.js.map +1 -0
  104. package/dist/esm/address.d.ts +67 -0
  105. package/dist/esm/address.d.ts.map +1 -0
  106. package/dist/esm/address.js +125 -0
  107. package/dist/esm/address.js.map +1 -0
  108. package/dist/esm/authorization.d.ts +41 -0
  109. package/dist/esm/authorization.d.ts.map +1 -0
  110. package/dist/esm/authorization.js +126 -0
  111. package/dist/esm/authorization.js.map +1 -0
  112. package/dist/esm/bal.d.ts +129 -0
  113. package/dist/esm/bal.d.ts.map +1 -0
  114. package/dist/esm/bal.js +522 -0
  115. package/dist/esm/bal.js.map +1 -0
  116. package/dist/esm/binaryTree.d.ts +148 -0
  117. package/dist/esm/binaryTree.d.ts.map +1 -0
  118. package/dist/esm/binaryTree.js +226 -0
  119. package/dist/esm/binaryTree.js.map +1 -0
  120. package/dist/esm/blobs.d.ts +76 -0
  121. package/dist/esm/blobs.d.ts.map +1 -0
  122. package/dist/esm/blobs.js +163 -0
  123. package/dist/esm/blobs.js.map +1 -0
  124. package/dist/esm/bytes.d.ts +291 -0
  125. package/dist/esm/bytes.d.ts.map +1 -0
  126. package/dist/esm/bytes.js +562 -0
  127. package/dist/esm/bytes.js.map +1 -0
  128. package/dist/esm/constants.d.ts +91 -0
  129. package/dist/esm/constants.d.ts.map +1 -0
  130. package/dist/esm/constants.js +94 -0
  131. package/dist/esm/constants.js.map +1 -0
  132. package/dist/esm/db.d.ts +65 -0
  133. package/dist/esm/db.d.ts.map +1 -0
  134. package/dist/esm/db.js +11 -0
  135. package/dist/esm/db.js.map +1 -0
  136. package/dist/esm/env.d.ts +9 -0
  137. package/dist/esm/env.d.ts.map +1 -0
  138. package/dist/esm/env.js +9 -0
  139. package/dist/esm/env.js.map +1 -0
  140. package/dist/esm/errors.d.ts +3 -0
  141. package/dist/esm/errors.d.ts.map +1 -0
  142. package/dist/esm/errors.js +14 -0
  143. package/dist/esm/errors.js.map +1 -0
  144. package/dist/esm/helpers.d.ts +21 -0
  145. package/dist/esm/helpers.d.ts.map +1 -0
  146. package/dist/esm/helpers.js +43 -0
  147. package/dist/esm/helpers.js.map +1 -0
  148. package/dist/esm/index.d.ts +67 -0
  149. package/dist/esm/index.d.ts.map +1 -0
  150. package/dist/esm/index.js +67 -0
  151. package/dist/esm/index.js.map +1 -0
  152. package/dist/esm/internal.d.ts +72 -0
  153. package/dist/esm/internal.d.ts.map +1 -0
  154. package/dist/esm/internal.js +170 -0
  155. package/dist/esm/internal.js.map +1 -0
  156. package/dist/esm/kzg.d.ts +14 -0
  157. package/dist/esm/kzg.d.ts.map +1 -0
  158. package/dist/esm/kzg.js +2 -0
  159. package/dist/esm/kzg.js.map +1 -0
  160. package/dist/esm/lock.d.ts +15 -0
  161. package/dist/esm/lock.d.ts.map +1 -0
  162. package/dist/esm/lock.js +41 -0
  163. package/dist/esm/lock.js.map +1 -0
  164. package/dist/esm/mapDB.d.ts +17 -0
  165. package/dist/esm/mapDB.d.ts.map +1 -0
  166. package/dist/esm/mapDB.js +42 -0
  167. package/dist/esm/mapDB.js.map +1 -0
  168. package/dist/esm/package.json +3 -0
  169. package/dist/esm/provider.d.ts +46 -0
  170. package/dist/esm/provider.d.ts.map +1 -0
  171. package/dist/esm/provider.js +79 -0
  172. package/dist/esm/provider.js.map +1 -0
  173. package/dist/esm/request.d.ts +20 -0
  174. package/dist/esm/request.d.ts.map +1 -0
  175. package/dist/esm/request.js +30 -0
  176. package/dist/esm/request.js.map +1 -0
  177. package/dist/esm/signature.d.ts +47 -0
  178. package/dist/esm/signature.d.ts.map +1 -0
  179. package/dist/esm/signature.js +137 -0
  180. package/dist/esm/signature.js.map +1 -0
  181. package/dist/esm/tasks.d.ts +32 -0
  182. package/dist/esm/tasks.d.ts.map +1 -0
  183. package/dist/esm/tasks.js +47 -0
  184. package/dist/esm/tasks.js.map +1 -0
  185. package/dist/esm/types.d.ts +64 -0
  186. package/dist/esm/types.d.ts.map +1 -0
  187. package/dist/esm/types.js +71 -0
  188. package/dist/esm/types.js.map +1 -0
  189. package/dist/esm/units.d.ts +22 -0
  190. package/dist/esm/units.d.ts.map +1 -0
  191. package/dist/esm/units.js +46 -0
  192. package/dist/esm/units.js.map +1 -0
  193. package/dist/esm/withdrawal.d.ts +72 -0
  194. package/dist/esm/withdrawal.d.ts.map +1 -0
  195. package/dist/esm/withdrawal.js +86 -0
  196. package/dist/esm/withdrawal.js.map +1 -0
  197. package/dist/tsconfig.prod.cjs.tsbuildinfo +1 -0
  198. package/dist/tsconfig.prod.esm.tsbuildinfo +1 -0
  199. package/package.json +116 -0
  200. package/src/account.ts +630 -0
  201. package/src/address.ts +158 -0
  202. package/src/authorization.ts +180 -0
  203. package/src/bal.ts +761 -0
  204. package/src/binaryTree.ts +353 -0
  205. package/src/blobs.ts +209 -0
  206. package/src/bytes.ts +659 -0
  207. package/src/constants.ts +125 -0
  208. package/src/db.ts +86 -0
  209. package/src/env.ts +9 -0
  210. package/src/errors.ts +28 -0
  211. package/src/helpers.ts +46 -0
  212. package/src/index.ts +88 -0
  213. package/src/internal.ts +212 -0
  214. package/src/kzg.ts +24 -0
  215. package/src/lock.ts +42 -0
  216. package/src/mapDB.ts +57 -0
  217. package/src/provider.ts +109 -0
  218. package/src/request.ts +48 -0
  219. package/src/signature.ts +202 -0
  220. package/src/tasks.ts +59 -0
  221. package/src/types.ts +177 -0
  222. package/src/units.ts +56 -0
  223. package/src/withdrawal.ts +133 -0
package/src/mapDB.ts ADDED
@@ -0,0 +1,57 @@
1
+ import { bytesToUnprefixedHex } from './bytes.ts'
2
+
3
+ import type { BatchDBOp, DB, DBObject } from './db.ts'
4
+
5
+ export class MapDB<
6
+ TKey extends Uint8Array | string | number,
7
+ TValue extends Uint8Array | string | DBObject,
8
+ > implements DB<TKey, TValue>
9
+ {
10
+ _database: Map<TKey, TValue>
11
+
12
+ constructor(database?: Map<TKey, TValue>) {
13
+ this._database = database ?? new Map<TKey, TValue>()
14
+ }
15
+
16
+ async get(key: TKey): Promise<TValue | undefined> {
17
+ const dbKey = key instanceof Uint8Array ? bytesToUnprefixedHex(key) : key.toString()
18
+ return this._database.get(dbKey as TKey)
19
+ }
20
+
21
+ async put(key: TKey, val: TValue): Promise<void> {
22
+ // Using deprecated bytesToUnprefixedHex for performance: used as Map keys (string encoding).
23
+ const dbKey = key instanceof Uint8Array ? bytesToUnprefixedHex(key) : key.toString()
24
+ this._database.set(dbKey as TKey, val)
25
+ }
26
+
27
+ async del(key: TKey): Promise<void> {
28
+ // Using deprecated bytesToUnprefixedHex for performance: used as Map keys (string encoding).
29
+ const dbKey = key instanceof Uint8Array ? bytesToUnprefixedHex(key) : key.toString()
30
+ this._database.delete(dbKey as TKey)
31
+ }
32
+
33
+ async batch(opStack: BatchDBOp<TKey, TValue>[]): Promise<void> {
34
+ for (const op of opStack) {
35
+ if (op.type === 'del') {
36
+ await this.del(op.key)
37
+ }
38
+
39
+ if (op.type === 'put') {
40
+ await this.put(op.key, op.value)
41
+ }
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Note that the returned shallow copy will share the underlying database with the original
47
+ *
48
+ * @returns DB
49
+ */
50
+ shallowCopy(): DB<TKey, TValue> {
51
+ return new MapDB<TKey, TValue>(this._database)
52
+ }
53
+
54
+ open() {
55
+ return Promise.resolve()
56
+ }
57
+ }
@@ -0,0 +1,109 @@
1
+ import { EthereumJSErrorWithoutCode } from './errors.ts'
2
+
3
+ type rpcParams = {
4
+ method: string
5
+ params: (string | string[] | boolean | number)[]
6
+ }
7
+
8
+ export type FetchFromProviderOptions = {
9
+ /** Request timeout in milliseconds (default: 60000) */
10
+ timeout?: number
11
+ }
12
+
13
+ /**
14
+ * Makes a simple RPC call to a remote Ethereum JSON-RPC provider and passes through the response.
15
+ * No parameter or response validation is done.
16
+ *
17
+ * @param url the URL for the JSON RPC provider
18
+ * @param params the parameters for the JSON-RPC method - refer to
19
+ * https://ethereum.org/en/developers/docs/apis/json-rpc/ for details on RPC methods
20
+ * @param options optional settings (e.g. timeout)
21
+ * @returns the `result` field from the JSON-RPC response
22
+ * @example
23
+ * ```ts
24
+ * const provider = 'https://mainnet.infura.io/v3/...'
25
+ * const params = {
26
+ * method: 'eth_getBlockByNumber',
27
+ * params: ['latest', false],
28
+ * }
29
+ * const block = await fetchFromProvider(provider, params)
30
+ * ```
31
+ */
32
+ export const fetchFromProvider = async (
33
+ url: string,
34
+ params: rpcParams,
35
+ options?: FetchFromProviderOptions,
36
+ ) => {
37
+ const timeout = options?.timeout ?? 60_000
38
+ const data = JSON.stringify({
39
+ method: params.method,
40
+ params: params.params,
41
+ jsonrpc: '2.0',
42
+ id: 1,
43
+ })
44
+
45
+ let signal: AbortSignal
46
+ let timer: ReturnType<typeof setTimeout> | undefined
47
+ if (typeof AbortSignal.timeout === 'function') {
48
+ signal = AbortSignal.timeout(timeout)
49
+ } else {
50
+ const controller = new AbortController()
51
+ signal = controller.signal
52
+ timer = setTimeout(() => controller.abort(), timeout)
53
+ }
54
+
55
+ const res = await fetch(url, {
56
+ headers: {
57
+ 'content-type': 'application/json',
58
+ },
59
+ method: 'POST',
60
+ body: data,
61
+ signal,
62
+ }).finally(() => {
63
+ if (timer !== undefined) clearTimeout(timer)
64
+ })
65
+ if (!res.ok) {
66
+ throw EthereumJSErrorWithoutCode(
67
+ `JSONRPCError: ${JSON.stringify(
68
+ {
69
+ method: params.method,
70
+ status: res.status,
71
+ message: await res.text().catch(() => {
72
+ return 'Could not parse error message likely because of a network error'
73
+ }),
74
+ },
75
+ null,
76
+ 2,
77
+ )}`,
78
+ )
79
+ }
80
+ const json = await res.json()
81
+ // TODO we should check json.error here
82
+ return json.result
83
+ }
84
+
85
+ /**
86
+ *
87
+ * @param provider a URL string or {@link EthersProvider}
88
+ * @returns the extracted URL string for the JSON-RPC Provider
89
+ */
90
+ export const getProvider = (provider: string | EthersProvider) => {
91
+ if (typeof provider === 'string') {
92
+ return provider
93
+ } else if (typeof provider === 'object' && provider._getConnection !== undefined) {
94
+ return provider._getConnection().url
95
+ } else {
96
+ throw EthereumJSErrorWithoutCode('Must provide valid provider URL or Web3Provider')
97
+ }
98
+ }
99
+
100
+ /**
101
+ * A partial interface for an `ethers` `JSONRPCProvider`
102
+ * We only use the url string since we do raw `fetch` calls to
103
+ * retrieve the necessary data
104
+ */
105
+ export interface EthersProvider {
106
+ _getConnection: () => {
107
+ url: string
108
+ }
109
+ }
package/src/request.ts ADDED
@@ -0,0 +1,48 @@
1
+ import { concatBytes } from './bytes.ts'
2
+
3
+ import type { PrefixedHexString } from './types.ts'
4
+
5
+ export type RequestBytes = Uint8Array
6
+
7
+ export type CLRequestType = (typeof CLRequestType)[keyof typeof CLRequestType]
8
+
9
+ export const CLRequestType = {
10
+ Deposit: 0,
11
+ Withdrawal: 1,
12
+ Consolidation: 2,
13
+ } as const
14
+
15
+ export interface RequestJSON {
16
+ type: PrefixedHexString
17
+ data: PrefixedHexString
18
+ }
19
+
20
+ export class CLRequest<T extends CLRequestType> {
21
+ // for easy use
22
+ public readonly bytes: Uint8Array
23
+
24
+ get type() {
25
+ return this.bytes[0] as T
26
+ }
27
+
28
+ get data() {
29
+ return this.bytes.subarray(1)
30
+ }
31
+
32
+ constructor(requestType: T, requestData: Uint8Array) {
33
+ this.bytes = concatBytes(new Uint8Array([requestType]), requestData)
34
+ }
35
+ }
36
+
37
+ export function createCLRequest(bytes: Uint8Array): CLRequest<CLRequestType> {
38
+ switch (bytes[0]) {
39
+ case CLRequestType.Deposit:
40
+ return new CLRequest(CLRequestType.Deposit, bytes.subarray(1))
41
+ case CLRequestType.Withdrawal:
42
+ return new CLRequest(CLRequestType.Withdrawal, bytes.subarray(1))
43
+ case CLRequestType.Consolidation:
44
+ return new CLRequest(CLRequestType.Consolidation, bytes.subarray(1))
45
+ default:
46
+ throw Error(`Invalid request type=${bytes[0]}`)
47
+ }
48
+ }
@@ -0,0 +1,202 @@
1
+ import { secp256k1 } from '@noble/curves/secp256k1.js'
2
+ import { keccak_256 } from '@noble/hashes/sha3.js'
3
+
4
+ import {
5
+ bigIntToBytes,
6
+ bytesToBigInt,
7
+ bytesToHex,
8
+ bytesToInt,
9
+ concatBytes,
10
+ hexToBytes,
11
+ setLengthLeft,
12
+ utf8ToBytes,
13
+ } from './bytes.ts'
14
+ import {
15
+ BIGINT_0,
16
+ BIGINT_1,
17
+ BIGINT_2,
18
+ BIGINT_27,
19
+ SECP256K1_ORDER,
20
+ SECP256K1_ORDER_DIV_2,
21
+ } from './constants.ts'
22
+ import { EthereumJSErrorWithoutCode } from './errors.ts'
23
+ import { assertIsBytes } from './helpers.ts'
24
+
25
+ import type { PrefixedHexString } from './types.ts'
26
+
27
+ export function calculateSigRecovery(v: bigint, chainId?: bigint): bigint {
28
+ if (v === BIGINT_0 || v === BIGINT_1) return v
29
+
30
+ if (chainId === undefined) {
31
+ return v - BIGINT_27
32
+ }
33
+ return v - (chainId * BIGINT_2 + BigInt(35))
34
+ }
35
+
36
+ function isValidSigRecovery(recovery: bigint): boolean {
37
+ return recovery === BIGINT_0 || recovery === BIGINT_1
38
+ }
39
+
40
+ /**
41
+ * ECDSA public key recovery from signature.
42
+ * NOTE: Accepts `v === 0 | v === 1` for EIP1559 transactions
43
+ * @returns Recovered public key
44
+ */
45
+ export const ecrecover = function (
46
+ msgHash: Uint8Array,
47
+ v: bigint,
48
+ r: Uint8Array,
49
+ s: Uint8Array,
50
+ chainId?: bigint,
51
+ ): Uint8Array {
52
+ const signature = concatBytes(setLengthLeft(r, 32), setLengthLeft(s, 32))
53
+ const recovery = calculateSigRecovery(v, chainId)
54
+ if (!isValidSigRecovery(recovery)) {
55
+ throw EthereumJSErrorWithoutCode('Invalid signature v value')
56
+ }
57
+
58
+ const sig = secp256k1.Signature.fromBytes(signature).addRecoveryBit(Number(recovery))
59
+ const senderPubKey = sig.recoverPublicKey(msgHash)
60
+ return senderPubKey.toBytes(false).slice(1)
61
+ }
62
+
63
+ /**
64
+ * Convert signature parameters into the format of `eth_sign` RPC method.
65
+ * NOTE: Accepts `v === 0 | v === 1` for EIP1559 transactions
66
+ * @returns Signature
67
+ */
68
+ export const toRPCSig = function (
69
+ v: bigint,
70
+ r: Uint8Array,
71
+ s: Uint8Array,
72
+ chainId?: bigint,
73
+ ): string {
74
+ const recovery = calculateSigRecovery(v, chainId)
75
+ if (!isValidSigRecovery(recovery)) {
76
+ throw EthereumJSErrorWithoutCode('Invalid signature v value')
77
+ }
78
+
79
+ // geth (and the RPC eth_sign method) uses the 65 byte format used by Bitcoin
80
+
81
+ return bytesToHex(concatBytes(setLengthLeft(r, 32), setLengthLeft(s, 32), bigIntToBytes(v)))
82
+ }
83
+
84
+ /**
85
+ * Convert signature parameters into the format of Compact Signature Representation (EIP-2098).
86
+ * NOTE: Accepts `v === 0 | v === 1` for EIP1559 transactions
87
+ * @returns Signature
88
+ */
89
+ export const toCompactSig = function (
90
+ v: bigint,
91
+ r: Uint8Array,
92
+ s: Uint8Array,
93
+ chainId?: bigint,
94
+ ): string {
95
+ const recovery = calculateSigRecovery(v, chainId)
96
+ if (!isValidSigRecovery(recovery)) {
97
+ throw EthereumJSErrorWithoutCode('Invalid signature v value')
98
+ }
99
+
100
+ const ss = Uint8Array.from([...s])
101
+ if ((v > BigInt(28) && v % BIGINT_2 === BIGINT_1) || v === BIGINT_1 || v === BigInt(28)) {
102
+ ss[0] |= 0x80
103
+ }
104
+
105
+ return bytesToHex(concatBytes(setLengthLeft(r, 32), setLengthLeft(ss, 32)))
106
+ }
107
+
108
+ /**
109
+ * Convert signature format of the `eth_sign` RPC method to signature parameters
110
+ *
111
+ * NOTE: For an extracted `v` value < 27 (see Geth bug https://github.com/ethereum/go-ethereum/issues/2053)
112
+ * `v + 27` is returned for the `v` value
113
+ * NOTE: After EIP1559, `v` could be `0` or `1` but this function assumes
114
+ * it's a signed message (EIP-191 or EIP-712) adding `27` at the end. Remove if needed.
115
+ */
116
+ export const fromRPCSig = function (sig: PrefixedHexString): {
117
+ v: bigint
118
+ r: Uint8Array
119
+ s: Uint8Array
120
+ } {
121
+ const bytes: Uint8Array = hexToBytes(sig)
122
+
123
+ let r: Uint8Array
124
+ let s: Uint8Array
125
+ let v: bigint
126
+ if (bytes.length >= 65) {
127
+ r = bytes.subarray(0, 32)
128
+ s = bytes.subarray(32, 64)
129
+ v = bytesToBigInt(bytes.subarray(64))
130
+ } else if (bytes.length === 64) {
131
+ // Compact Signature Representation (https://eips.ethereum.org/EIPS/eip-2098)
132
+ r = bytes.subarray(0, 32)
133
+ s = bytes.subarray(32, 64)
134
+ v = BigInt(bytesToInt(bytes.subarray(32, 33)) >> 7)
135
+ s[0] &= 0x7f
136
+ } else {
137
+ throw EthereumJSErrorWithoutCode('Invalid signature length')
138
+ }
139
+
140
+ // support both versions of `eth_sign` responses
141
+ if (v < 27) {
142
+ // TODO: verify this behavior, and verify in which context this method (`fromRPCSig`) is used
143
+ v = v + BIGINT_27
144
+ }
145
+
146
+ return {
147
+ v,
148
+ r,
149
+ s,
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Validate a ECDSA signature.
155
+ * NOTE: Accepts `v === 0 | v === 1` for EIP1559 transactions
156
+ * @param homesteadOrLater Indicates whether this is being used on either the homestead hardfork or a later one
157
+ */
158
+ export const isValidSignature = function (
159
+ v: bigint,
160
+ r: Uint8Array,
161
+ s: Uint8Array,
162
+ homesteadOrLater: boolean = true,
163
+ chainId?: bigint,
164
+ ): boolean {
165
+ if (r.length !== 32 || s.length !== 32) {
166
+ return false
167
+ }
168
+
169
+ if (!isValidSigRecovery(calculateSigRecovery(v, chainId))) {
170
+ return false
171
+ }
172
+
173
+ const rBigInt = bytesToBigInt(r)
174
+ const sBigInt = bytesToBigInt(s)
175
+
176
+ if (
177
+ rBigInt === BIGINT_0 ||
178
+ rBigInt >= SECP256K1_ORDER ||
179
+ sBigInt === BIGINT_0 ||
180
+ sBigInt >= SECP256K1_ORDER
181
+ ) {
182
+ return false
183
+ }
184
+
185
+ if (homesteadOrLater && sBigInt >= SECP256K1_ORDER_DIV_2) {
186
+ return false
187
+ }
188
+
189
+ return true
190
+ }
191
+
192
+ /**
193
+ * Returns the keccak-256 hash of `message`, prefixed with the header used by the `eth_sign` RPC call.
194
+ * The output of this function can be fed into `ecsign` to produce the same signature as the `eth_sign`
195
+ * call for a given `message`, or fed to `ecrecover` along with a signature to recover the public key
196
+ * used to produce the signature.
197
+ */
198
+ export const hashPersonalMessage = function (message: Uint8Array): Uint8Array {
199
+ assertIsBytes(message)
200
+ const prefix = utf8ToBytes(`\u0019Ethereum Signed Message:\n${message.length}`)
201
+ return keccak_256(concatBytes(prefix, message))
202
+ }
package/src/tasks.ts ADDED
@@ -0,0 +1,59 @@
1
+ interface Task {
2
+ priority: number
3
+ fn: Function
4
+ }
5
+
6
+ export class PrioritizedTaskExecutor {
7
+ /** The maximum size of the pool */
8
+ private maxPoolSize: number
9
+ /** The current size of the pool */
10
+ private currentPoolSize: number
11
+ /** The task queue */
12
+ private queue: Task[]
13
+
14
+ /**
15
+ * Executes tasks up to maxPoolSize at a time, other items are put in a priority queue.
16
+ * @class PrioritizedTaskExecutor
17
+ * @private
18
+ * @param maxPoolSize The maximum size of the pool
19
+ */
20
+ constructor(maxPoolSize: number) {
21
+ this.maxPoolSize = maxPoolSize
22
+ this.currentPoolSize = 0
23
+ this.queue = []
24
+ }
25
+
26
+ /**
27
+ * Executes the task or queues it if no spots are available.
28
+ * When a task is added, check if there are spots left in the pool.
29
+ * If a spot is available, claim that spot and give back the spot once the asynchronous task has been resolved.
30
+ * When no spots are available, add the task to the task queue. The task will be executed at some point when another task has been resolved.
31
+ * @private
32
+ * @param priority The priority of the task
33
+ * @param fn The function that accepts the callback, which must be called upon the task completion.
34
+ */
35
+ executeOrQueue(priority: number, fn: Function) {
36
+ if (this.currentPoolSize < this.maxPoolSize) {
37
+ this.currentPoolSize++
38
+ fn(() => {
39
+ this.currentPoolSize--
40
+ if (this.queue.length > 0) {
41
+ this.queue.sort((a, b) => b.priority - a.priority)
42
+ const item = this.queue.shift()
43
+ this.executeOrQueue(item!.priority, item!.fn)
44
+ }
45
+ })
46
+ } else {
47
+ this.queue.push({ priority, fn })
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Checks if the taskExecutor is finished.
53
+ * @private
54
+ * @returns Returns `true` if the taskExecutor is finished, otherwise returns `false`.
55
+ */
56
+ finished(): boolean {
57
+ return this.currentPoolSize === 0
58
+ }
59
+ }
package/src/types.ts ADDED
@@ -0,0 +1,177 @@
1
+ import { bytesToBigInt, bytesToHex, toBytes } from './bytes.ts'
2
+ import { EthereumJSErrorWithoutCode } from './errors.ts'
3
+ import { isHexString } from './internal.ts'
4
+
5
+ import type { Address } from './address.ts'
6
+ import type { ToBytesInputTypes } from './bytes.ts'
7
+
8
+ /*
9
+ * A type that represents an input that can be converted to a BigInt.
10
+ */
11
+ export type BigIntLike = bigint | PrefixedHexString | number | Uint8Array
12
+
13
+ /*
14
+ * A type that represents an input that can be converted to a Uint8Array.
15
+ */
16
+ export type BytesLike =
17
+ | Uint8Array
18
+ | number[]
19
+ | number
20
+ | bigint
21
+ | TransformableToBytes
22
+ | PrefixedHexString
23
+
24
+ /*
25
+ * A type that represents a number-like string.
26
+ */
27
+ export type NumericString = `${number}`
28
+
29
+ /*
30
+ * A type that represents a `0x`-prefixed hex string.
31
+ */
32
+ export type PrefixedHexString = `0x${string}`
33
+
34
+ /**
35
+ * A type that represents an input that can be converted to an Address.
36
+ */
37
+ export type AddressLike = Address | Uint8Array | PrefixedHexString
38
+
39
+ export interface TransformableToBytes {
40
+ toBytes?(): Uint8Array
41
+ }
42
+
43
+ export type NestedUint8Array = Array<Uint8Array | NestedUint8Array>
44
+
45
+ export function isNestedUint8Array(value: unknown): value is NestedUint8Array {
46
+ if (!Array.isArray(value)) {
47
+ return false
48
+ }
49
+ for (const item of value) {
50
+ if (Array.isArray(item)) {
51
+ if (!isNestedUint8Array(item)) {
52
+ return false
53
+ }
54
+ } else if (!(item instanceof Uint8Array)) {
55
+ return false
56
+ }
57
+ }
58
+ return true
59
+ }
60
+
61
+ export type TypeOutput = (typeof TypeOutput)[keyof typeof TypeOutput]
62
+
63
+ export const TypeOutput = {
64
+ Number: 0,
65
+ BigInt: 1,
66
+ Uint8Array: 2,
67
+ PrefixedHexString: 3,
68
+ } as const
69
+
70
+ export type TypeOutputReturnType = {
71
+ [TypeOutput.Number]: number
72
+ [TypeOutput.BigInt]: bigint
73
+ [TypeOutput.Uint8Array]: Uint8Array
74
+ [TypeOutput.PrefixedHexString]: PrefixedHexString
75
+ }
76
+
77
+ /**
78
+ * Convert an input to a specified type.
79
+ * Input of null/undefined returns null/undefined regardless of the output type.
80
+ * @param input value to convert
81
+ * @param outputType type to output
82
+ */
83
+ export function toType<T extends TypeOutput>(input: null, outputType: T): null
84
+ export function toType<T extends TypeOutput>(input: undefined, outputType: T): undefined
85
+ export function toType<T extends TypeOutput>(
86
+ input: ToBytesInputTypes,
87
+ outputType: T,
88
+ ): TypeOutputReturnType[T]
89
+ export function toType<T extends TypeOutput>(
90
+ input: ToBytesInputTypes,
91
+ outputType: T,
92
+ ): TypeOutputReturnType[T] | undefined | null {
93
+ if (input === null) {
94
+ return null
95
+ }
96
+ if (input === undefined) {
97
+ return undefined
98
+ }
99
+
100
+ if (typeof input === 'string' && !isHexString(input)) {
101
+ throw EthereumJSErrorWithoutCode(`A string must be provided with a 0x-prefix, given: ${input}`)
102
+ } else if (typeof input === 'number' && !Number.isSafeInteger(input)) {
103
+ throw EthereumJSErrorWithoutCode(
104
+ 'The provided number is greater than MAX_SAFE_INTEGER (please use an alternative input type)',
105
+ )
106
+ }
107
+
108
+ const output = toBytes(input)
109
+
110
+ switch (outputType) {
111
+ case TypeOutput.Uint8Array:
112
+ return output as TypeOutputReturnType[T]
113
+ case TypeOutput.BigInt:
114
+ return bytesToBigInt(output) as TypeOutputReturnType[T]
115
+ case TypeOutput.Number: {
116
+ const bigInt = bytesToBigInt(output)
117
+ if (bigInt > BigInt(Number.MAX_SAFE_INTEGER)) {
118
+ throw EthereumJSErrorWithoutCode(
119
+ 'The provided number is greater than MAX_SAFE_INTEGER (please use an alternative output type)',
120
+ )
121
+ }
122
+ return Number(bigInt) as TypeOutputReturnType[T]
123
+ }
124
+ case TypeOutput.PrefixedHexString:
125
+ return bytesToHex(output) as TypeOutputReturnType[T]
126
+ default:
127
+ throw EthereumJSErrorWithoutCode('unknown outputType')
128
+ }
129
+ }
130
+
131
+ /**
132
+ * EIP-7702 Authorization list types
133
+ */
134
+ export type EOACode7702AuthorizationListItemUnsigned = {
135
+ chainId: PrefixedHexString
136
+ address: PrefixedHexString
137
+ nonce: PrefixedHexString
138
+ }
139
+
140
+ export type EOACode7702AuthorizationListItem = {
141
+ yParity: PrefixedHexString
142
+ r: PrefixedHexString
143
+ s: PrefixedHexString
144
+ } & EOACode7702AuthorizationListItemUnsigned
145
+
146
+ // Tuple of [chain_id, address, nonce, y_parity, r, s]
147
+ export type EOACode7702AuthorizationListBytesItem = [
148
+ Uint8Array,
149
+ Uint8Array,
150
+ Uint8Array,
151
+ Uint8Array,
152
+ Uint8Array,
153
+ Uint8Array,
154
+ ]
155
+ export type EOACode7702AuthorizationListBytes = EOACode7702AuthorizationListBytesItem[]
156
+ export type EOACode7702AuthorizationList = EOACode7702AuthorizationListItem[]
157
+
158
+ export type EOACode7702AuthorizationListBytesItemUnsigned = [Uint8Array, Uint8Array, Uint8Array]
159
+
160
+ export function isEOACode7702AuthorizationListBytes(
161
+ input: EOACode7702AuthorizationListBytes | EOACode7702AuthorizationList,
162
+ ): input is EOACode7702AuthorizationListBytes {
163
+ if (input.length === 0) {
164
+ return true
165
+ }
166
+ const firstItem = input[0]
167
+ if (Array.isArray(firstItem)) {
168
+ return true
169
+ }
170
+ return false
171
+ }
172
+
173
+ export function isEOACode7702AuthorizationList(
174
+ input: EOACode7702AuthorizationListBytes | EOACode7702AuthorizationList,
175
+ ): input is EOACode7702AuthorizationList {
176
+ return !isEOACode7702AuthorizationListBytes(input) // This is exactly the same method, except the output is negated.
177
+ }
package/src/units.ts ADDED
@@ -0,0 +1,56 @@
1
+ import { BIGINT_0, BIGINT_1 } from './constants.ts'
2
+ import { EthereumJSErrorWithoutCode } from './errors.ts'
3
+
4
+ /** Conversion constants to wei */
5
+ export const GWEI_TO_WEI = BigInt(10 ** 9) // Multiplier to convert from Gwei to Wei
6
+ export const ETHER_TO_WEI = BigInt(10 ** 18) // Multiplier to convert from Ether to Wei
7
+
8
+ export function formatBigDecimal(
9
+ numerator: bigint,
10
+ denominator: bigint,
11
+ maxDecimalFactor: bigint,
12
+ ): string {
13
+ if (denominator === BIGINT_0) {
14
+ denominator = BIGINT_1
15
+ }
16
+
17
+ const full = numerator / denominator
18
+ const fraction = ((numerator - full * denominator) * maxDecimalFactor) / denominator
19
+
20
+ // zeros to be added post decimal are number of zeros in maxDecimalFactor - number of digits in fraction
21
+ const zerosPostDecimal = String(maxDecimalFactor).length - 1 - String(fraction).length
22
+ return `${full}.${'0'.repeat(zerosPostDecimal)}${fraction}`
23
+ }
24
+
25
+ export class Units {
26
+ static validateInput(amount: number | bigint): void {
27
+ if (typeof amount === 'number' && !Number.isInteger(amount)) {
28
+ throw EthereumJSErrorWithoutCode('Input must be an integer number')
29
+ }
30
+ if (BigInt(amount) < 0) {
31
+ throw EthereumJSErrorWithoutCode('Input must be a positive number')
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Convert a number or bigint input of ether to wei
37
+ *
38
+ * @param {number | bigint} amount amount of units of ether to convert to wei
39
+ * @returns {bigint} amount of units in wei
40
+ */
41
+ static ether(amount: number | bigint): bigint {
42
+ Units.validateInput(amount)
43
+ return BigInt(amount) * ETHER_TO_WEI
44
+ }
45
+
46
+ /**
47
+ * Convert a number or bigint input of gwei to wei
48
+ *
49
+ * @param amount amount of units of gwei to convert to wei
50
+ * @returns {bigint} amount of units in wei
51
+ */
52
+ static gwei(amount: number | bigint): bigint {
53
+ Units.validateInput(amount)
54
+ return BigInt(amount) * GWEI_TO_WEI
55
+ }
56
+ }