dequanto 0.2.46 → 0.2.48

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 (107) hide show
  1. package/docs/ai/INDEX.md +41 -0
  2. package/docs/ai/examples/contract-read.spec.ts +44 -0
  3. package/docs/ai/examples/contract-write.spec.ts +16 -0
  4. package/docs/ai/examples/deploy-contract.spec.ts +32 -0
  5. package/docs/ai/examples/erc4337-userop.spec.ts +37 -0
  6. package/docs/ai/examples/explorer-abi.spec.ts +18 -0
  7. package/docs/ai/examples/generate-client.spec.ts +22 -0
  8. package/docs/ai/examples/index-events.spec.ts +27 -0
  9. package/docs/ai/examples/rpc-read.spec.ts +15 -0
  10. package/docs/ai/examples/safe-batch.spec.ts +55 -0
  11. package/docs/ai/examples/send-transaction.spec.ts +22 -0
  12. package/docs/ai/examples/storage-read.spec.ts +27 -0
  13. package/docs/ai/examples/token-transfer.spec.ts +22 -0
  14. package/docs/ai/references/contracts.md +111 -0
  15. package/docs/ai/references/deployments.md +98 -0
  16. package/docs/ai/references/events-indexing.md +97 -0
  17. package/docs/ai/references/explorer.md +77 -0
  18. package/docs/ai/references/generation.md +182 -0
  19. package/docs/ai/references/migration-from-ethers-viem.md +43 -0
  20. package/docs/ai/references/rpc-clients.md +115 -0
  21. package/docs/ai/references/safe-erc4337.md +134 -0
  22. package/docs/ai/references/storage.md +93 -0
  23. package/docs/ai/references/tokens-accounts.md +99 -0
  24. package/docs/ai/references/transactions.md +146 -0
  25. package/docs/ai/skill/dequanto/SKILL.md +59 -0
  26. package/lib/cjs/abi/$abiCoder.js +14 -0
  27. package/lib/cjs/abi/$abiCoder.js.map +1 -1
  28. package/lib/cjs/clients/ClientPool.js +14 -0
  29. package/lib/cjs/clients/ClientPool.js.map +1 -1
  30. package/lib/cjs/clients/Web3Client.js +14 -4
  31. package/lib/cjs/clients/Web3Client.js.map +1 -1
  32. package/lib/cjs/clients/Web3ClientFactory.js.map +1 -1
  33. package/lib/cjs/clients/model/ClientStatus.js +1 -0
  34. package/lib/cjs/clients/model/ClientStatus.js.map +1 -1
  35. package/lib/cjs/clients/utils/ClientErrorUtil.js +9 -0
  36. package/lib/cjs/clients/utils/ClientErrorUtil.js.map +1 -1
  37. package/lib/cjs/config/ConfigDefaults.js +0 -3
  38. package/lib/cjs/config/ConfigDefaults.js.map +1 -1
  39. package/lib/cjs/contracts/ContractCreationResolver.js +1 -0
  40. package/lib/cjs/contracts/ContractCreationResolver.js.map +1 -1
  41. package/lib/cjs/rpc/RpcBase.js +4 -1
  42. package/lib/cjs/rpc/RpcBase.js.map +1 -1
  43. package/lib/cjs/solidity/utils/$str.js +1 -1
  44. package/lib/cjs/solidity/utils/$str.js.map +1 -1
  45. package/lib/cjs/utils/$abiParser.js +85 -1
  46. package/lib/cjs/utils/$abiParser.js.map +1 -1
  47. package/lib/cjs/utils/$base.js +1 -1
  48. package/lib/cjs/utils/$base.js.map +1 -1
  49. package/lib/cjs/utils/$crypto.js.map +1 -1
  50. package/lib/cjs/utils/$hex.js +15 -3
  51. package/lib/cjs/utils/$hex.js.map +1 -1
  52. package/lib/esm/abi/$abiCoder.js.map +1 -1
  53. package/lib/esm/abi/$abiCoder.mjs +14 -0
  54. package/lib/esm/clients/ClientPool.js.map +1 -1
  55. package/lib/esm/clients/ClientPool.mjs +14 -0
  56. package/lib/esm/clients/Web3Client.js.map +1 -1
  57. package/lib/esm/clients/Web3Client.mjs +14 -4
  58. package/lib/esm/clients/Web3ClientFactory.js.map +1 -1
  59. package/lib/esm/clients/model/ClientStatus.js.map +1 -1
  60. package/lib/esm/clients/model/ClientStatus.mjs +1 -0
  61. package/lib/esm/clients/utils/ClientErrorUtil.js.map +1 -1
  62. package/lib/esm/clients/utils/ClientErrorUtil.mjs +9 -0
  63. package/lib/esm/config/ConfigDefaults.js.map +1 -1
  64. package/lib/esm/config/ConfigDefaults.mjs +0 -3
  65. package/lib/esm/contracts/ContractCreationResolver.js.map +1 -1
  66. package/lib/esm/contracts/ContractCreationResolver.mjs +1 -0
  67. package/lib/esm/rpc/RpcBase.js.map +1 -1
  68. package/lib/esm/rpc/RpcBase.mjs +4 -1
  69. package/lib/esm/solidity/utils/$str.js.map +1 -1
  70. package/lib/esm/solidity/utils/$str.mjs +1 -1
  71. package/lib/esm/utils/$abiParser.js.map +1 -1
  72. package/lib/esm/utils/$abiParser.mjs +85 -1
  73. package/lib/esm/utils/$base.js.map +1 -1
  74. package/lib/esm/utils/$base.mjs +1 -1
  75. package/lib/esm/utils/$crypto.js.map +1 -1
  76. package/lib/esm/utils/$hex.js.map +1 -1
  77. package/lib/esm/utils/$hex.mjs +15 -3
  78. package/lib/types/abi/$abiCoder.d.ts +11 -9
  79. package/lib/types/clients/ClientPool.d.ts +2 -0
  80. package/lib/types/clients/Web3Client.d.ts +4 -1
  81. package/lib/types/clients/Web3ClientFactory.d.ts +3 -2
  82. package/lib/types/clients/model/ClientStatus.d.ts +1 -0
  83. package/lib/types/clients/utils/ClientErrorUtil.d.ts +4 -0
  84. package/lib/types/contracts/ContractBase.d.ts +1 -1
  85. package/lib/types/contracts/wrappers/FnRequestWrapper.d.ts +1 -1
  86. package/lib/types/models/TAccount.d.ts +1 -1
  87. package/lib/types/rpc/RpcBase.d.ts +1 -0
  88. package/lib/types/utils/$abiParser.d.ts +6 -0
  89. package/lib/types/utils/$crypto.d.ts +3 -0
  90. package/lib/types/utils/$hex.d.ts +2 -0
  91. package/package.json +1 -1
  92. package/src/abi/$abiCoder.ts +31 -10
  93. package/src/clients/ClientPool.ts +17 -1
  94. package/src/clients/Web3Client.ts +20 -7
  95. package/src/clients/Web3ClientFactory.ts +10 -5
  96. package/src/clients/model/ClientStatus.ts +1 -0
  97. package/src/clients/utils/ClientErrorUtil.ts +8 -0
  98. package/src/config/ConfigDefaults.ts +0 -3
  99. package/src/contracts/ContractCreationResolver.ts +1 -0
  100. package/src/contracts/wrappers/FnRequestWrapper.ts +1 -1
  101. package/src/models/TAccount.ts +1 -1
  102. package/src/rpc/RpcBase.ts +5 -1
  103. package/src/solidity/utils/$str.ts +1 -1
  104. package/src/utils/$abiParser.ts +93 -1
  105. package/src/utils/$base.ts +1 -1
  106. package/src/utils/$crypto.ts +1 -0
  107. package/src/utils/$hex.ts +12 -3
@@ -6,16 +6,19 @@ import { Config } from '@dequanto/config/Config';
6
6
  import { EvmWeb3Client } from './EvmWeb3Client';
7
7
  import { $require } from '@dequanto/utils/$require';
8
8
  import { $config } from '@dequanto/utils/$config';
9
+ import type { HardhatWeb3Client } from '@dequanto/hardhat/HardhatWeb3Client';
9
10
 
10
11
  export namespace Web3ClientFactory {
11
12
 
12
- export function get (platform: TPlatform | number, opts?: IWeb3EndpointOptions) {
13
+ export function get <
14
+ TClient extends EvmWeb3Client | HardhatWeb3Client = EvmWeb3Client
15
+ > (platform: TPlatform | number, opts?: IWeb3EndpointOptions): TClient {
13
16
  if (typeof platform === 'string' && (platform === 'hardhat' || platform.startsWith('hh:'))) {
14
17
  let client = di.resolve(HardhatProvider).client('localhost', opts);
15
18
  if (platform.startsWith('hh:')) {
16
19
  client.configureFork(platform.slice(3));
17
20
  }
18
- return client;
21
+ return client as TClient;
19
22
  }
20
23
 
21
24
  let options = $config.getWeb3Options(platform);
@@ -23,12 +26,14 @@ export namespace Web3ClientFactory {
23
26
  return new EvmWeb3Client({
24
27
  ...options,
25
28
  ...(opts ?? {})
26
- });
29
+ }) as TClient;
27
30
  }
28
31
 
29
32
  /** Same as the sync variant, but ensures the config is fetched */
30
- export async function getAsync (platform: TPlatform | string | number, opts?: IWeb3EndpointOptions) {
33
+ export async function getAsync <
34
+ TClient extends EvmWeb3Client | HardhatWeb3Client = EvmWeb3Client
35
+ > (platform: TPlatform | string | number, opts?: IWeb3EndpointOptions): Promise<TClient> {
31
36
  let cfg = await Config.get();
32
- return get(platform, opts);
37
+ return get<TClient>(platform, opts);
33
38
  }
34
39
  }
@@ -2,5 +2,6 @@ export enum ClientStatus {
2
2
  Ok = 200,
3
3
  NetworkError = 500,
4
4
  RateLimited = 501,
5
+ NodeConfigurationError = 502,
5
6
  CallError = 400
6
7
  }
@@ -14,6 +14,14 @@ export namespace ClientErrorUtil {
14
14
  }
15
15
  return false;
16
16
  }
17
+ export function isNodeConfigurationError (error: Error & { code?, reason? }) {
18
+ let str = error.message ?? error.stack;
19
+ let noHistory = /history unavailable/i.test(str);
20
+ if (noHistory) {
21
+ return true;
22
+ }
23
+ return false;
24
+ }
17
25
  export function isAlreadyKnown (error: Error & { code?, reason? }) {
18
26
  return /already known/i.test(error.message);
19
27
  }
@@ -116,9 +116,6 @@ export const ConfigDefaults = <IConfigData> {
116
116
  "chainId": 1,
117
117
  "chainToken": "ETH",
118
118
  "endpoints": [
119
- {
120
- "url": "https://ethereum-rpc.publicnode.com"
121
- },
122
119
  {
123
120
  "url": "https://eth.drpc.org",
124
121
  "rateLimit": "2000/5m"
@@ -72,6 +72,7 @@ class BlockchainExplorerDateResolver {
72
72
  throw error;
73
73
  }
74
74
  let tx = await this.client.getTransaction(info.txHash);
75
+ $require.notNull(tx, `Tx not found: ${info.txHash} in ${this.client.network} (${this.client.options?.endpoints?.[0]?.url})`);
75
76
  let block = await this.client.getBlock(tx.blockNumber);
76
77
  return {
77
78
  tx: tx.hash,
@@ -9,7 +9,7 @@ export namespace FnRequestWrapper {
9
9
 
10
10
  type TRequests<T extends ContractBase> = {
11
11
  [K in keyof T]: T[K] extends (...any) => Promise<any>
12
- ? (...params: Parameters<T[K]>) => ContractReaderUtils.IContractReadParams<Awaited<ReturnType<T[K]>>>
12
+ ? (...params: Parameters<T[K]>) => Promise<ContractReaderUtils.IContractReadParams<Awaited<ReturnType<T[K]>>>>
13
13
  : never
14
14
  }
15
15
 
@@ -65,7 +65,7 @@ export interface TimelockAccount extends IAccount {
65
65
  // Erc4337 account must include platform information
66
66
  platform: TPlatform
67
67
 
68
- operator: EoAccount
68
+ operator: IAccount
69
69
  }
70
70
 
71
71
  export type TAccount = string | IAccount | EoAccount | SafeAccount | Erc4337Account;
@@ -122,7 +122,7 @@ export abstract class RpcBase {
122
122
  };
123
123
  }
124
124
  private _unwrapBody (resp: any): any {
125
- return resp.result;
125
+ return RpcBase.unwrapBody(resp);
126
126
  }
127
127
  private _deserialize (method: string, result: any) {
128
128
  let { methods, schemas } = this.returnSchemas;
@@ -141,6 +141,10 @@ export abstract class RpcBase {
141
141
 
142
142
 
143
143
  protected abstract returnSchemas
144
+
145
+ static unwrapBody (resp: any) {
146
+ return resp.result;
147
+ }
144
148
  }
145
149
 
146
150
 
@@ -44,7 +44,7 @@ export namespace $str {
44
44
  }
45
45
 
46
46
  export function fromHex(hex: TEth.Hex) {
47
- let trimmed = $hex.trimBytes(hex);
47
+ let trimmed = $hex.trimBytesEnd(hex);
48
48
  return $buffer.fromHex(trimmed).toString();
49
49
  }
50
50
 
@@ -86,6 +86,69 @@ export namespace $abiParser {
86
86
  };
87
87
  }
88
88
 
89
+
90
+ /**
91
+ * Parses Solidity struct declarations into a tuple ABI component.
92
+ * Supports multiple declarations so later structs can reference earlier ones,
93
+ * and returns the ABI component for the last struct in the input.
94
+ */
95
+ export function parseStruct (struct: string): TAbiItem {
96
+ let structs = Parse.structs(struct);
97
+ $require.gt(structs.length, 0, `No structs found in ${struct}`);
98
+
99
+ let byName = new Map(structs.map(x => [ x.name, x ]));
100
+ let cache = new Map<string, TAbiInput>();
101
+
102
+ function toTuple (name: string): TAbiInput {
103
+ let cached = cache.get(name);
104
+ if (cached != null) {
105
+ return cached;
106
+ }
107
+ let def = byName.get(name);
108
+ $require.notNull(def, `Struct ${name} is not found`);
109
+
110
+ let tuple: TAbiInput = {
111
+ name: '',
112
+ type: 'tuple',
113
+ internalType: `struct ${name}`,
114
+ components: []
115
+ };
116
+ cache.set(name, tuple);
117
+ tuple.components = Parse.splitByDelimiter(def.body, ';')
118
+ .map(field => $abiParser.parseArguments(field)[0])
119
+ .map(resolveStructs);
120
+
121
+ return tuple;
122
+ }
123
+
124
+ function resolveStructs (param: TAbiInput): TAbiInput {
125
+ let { type, array } = Parse.extractArray(param.type);
126
+ if (byName.has(type) === false) {
127
+ if (param.components != null) {
128
+ return {
129
+ ...param,
130
+ components: param.components.map(resolveStructs)
131
+ };
132
+ }
133
+ return param;
134
+ }
135
+
136
+ let tuple = toTuple(type);
137
+ return {
138
+ name: param.name,
139
+ type: `tuple${array}`,
140
+ internalType: `struct ${type}${array}`,
141
+ components: tuple.components
142
+ };
143
+ }
144
+
145
+ let last = structs[structs.length - 1];
146
+ return {
147
+ ...toTuple(last.name),
148
+ name: last.name
149
+ } as any as TAbiItem;
150
+ }
151
+
89
152
  // uint256
90
153
  // address[]
91
154
  // (uint256, uint256)
@@ -247,6 +310,35 @@ namespace Parse {
247
310
 
248
311
  const CLOSE_CHARS = {
249
312
  '[': ']',
250
- '(': ')'
313
+ '(': ')',
314
+ '{': '}'
251
315
  };
316
+
317
+ export function structs (source: string) {
318
+ let rgx = /\bstruct\s+(?<name>[\w_$]+)\s*\{/g;
319
+ let arr: { name: string, body: string }[] = [];
320
+ let match: RegExpExecArray;
321
+ while ((match = rgx.exec(source)) != null) {
322
+ let openI = source.indexOf('{', match.index);
323
+ let closeI = goToClosing(source, openI, '{');
324
+ arr.push({
325
+ name: match.groups.name,
326
+ body: source.substring(openI + 1, closeI).trim()
327
+ });
328
+ rgx.lastIndex = closeI + 1;
329
+ }
330
+ return arr;
331
+ }
332
+
333
+ export function extractArray (type: string) {
334
+ let array = '';
335
+ while (true) {
336
+ let match = /^(?<type>.+?)(?<array>\[[^\]]*\])$/.exec(type);
337
+ if (match == null) {
338
+ return { type, array };
339
+ }
340
+ type = match.groups.type;
341
+ array = `${match.groups.array}${array}`;
342
+ }
343
+ }
252
344
  }
@@ -18,7 +18,7 @@ export namespace $base {
18
18
 
19
19
  public encode (hex: TEth.Hex): string {
20
20
  hex = $hex.toHexBuffer(hex);
21
- hex = $hex.trimBytes(hex);
21
+ hex = $hex.trimBytesStart(hex);
22
22
  let buffer = $buffer.fromHex(hex);
23
23
 
24
24
  let result = [];
@@ -8,6 +8,7 @@ interface ICrypto {
8
8
  randomUUID(): `${string}-${string}-${string}-${string}-${string}`;
9
9
  createECDH(curve: string)
10
10
 
11
+ sha256 (mix: string | TEth.Hex | Uint8Array, opts?: { encoding: 'hex' }): Promise<TEth.Hex>;
11
12
  sha256 (mix: string | TEth.Hex | Uint8Array): Promise<Uint8Array>;
12
13
 
13
14
  encrypt(buffer: string | TEth.Hex | Uint8Array, opts: IEncryptionParams & { encoding?: undefined | 'binary' }): Promise<Uint8Array>
package/src/utils/$hex.ts CHANGED
@@ -24,10 +24,19 @@ export namespace $hex {
24
24
  * Trims '00' bytes from start or end, e.g. 0x68656c6c6f000000 => 0x68656c6c6f
25
25
  */
26
26
  export function trimBytes(hex: TEth.Hex): TEth.Hex {
27
- if (hex.startsWith('0x00') === false && hex.endsWith('00') === false) {
27
+ return trimBytesEnd(trimBytesStart(hex));
28
+ }
29
+ export function trimBytesStart(hex: TEth.Hex): TEth.Hex {
30
+ if (hex.startsWith('0x00') === false) {
28
31
  return hex;
29
32
  }
30
- return hex.replace(/^0x(0{2})+/, '0x').replace(/(0{2})+$/, '') as TEth.Hex;
33
+ return hex.replace(/^0x(0{2})+/, '0x') as TEth.Hex;
34
+ }
35
+ export function trimBytesEnd(hex: TEth.Hex): TEth.Hex {
36
+ if (hex.endsWith('00') === false) {
37
+ return hex;
38
+ }
39
+ return hex.replace(/(0{2})+$/, '') as TEth.Hex;
31
40
  }
32
41
  export function trimLeadingZerosFromNumber(hex: string) {
33
42
  hex = hex.replace(/^0x0*/, '');
@@ -114,7 +123,6 @@ export namespace $hex {
114
123
  return hex;
115
124
  }
116
125
 
117
-
118
126
  /**
119
127
  * Adds `0x` to the start if not present
120
128
  */
@@ -131,6 +139,7 @@ export namespace $hex {
131
139
  if (typeof mix ==='boolean') {
132
140
  return mix ? '0x1' : '0x0';
133
141
  }
142
+ $require.match(/^(0x)?[\da-f]*$/i, mix, 'NotValidHex');
134
143
  if (mix.startsWith('0x')) {
135
144
  return mix as TEth.Hex;
136
145
  }