dequanto 0.2.50 → 0.2.51

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 (67) hide show
  1. package/lib/cjs/explorer/BlockchainExplorer.js +9 -9
  2. package/lib/cjs/explorer/BlockchainExplorer.js.map +1 -1
  3. package/lib/cjs/explorer/BlockchainExplorerFactory.js +1 -1
  4. package/lib/cjs/explorer/BlockchainExplorerFactory.js.map +1 -1
  5. package/lib/cjs/explorer/BlockchainExplorerProvider.js +1 -1
  6. package/lib/cjs/explorer/BlockchainExplorerProvider.js.map +1 -1
  7. package/lib/cjs/explorer/BlockchainExplorerStorage.js +2 -2
  8. package/lib/cjs/explorer/BlockchainExplorerStorage.js.map +1 -1
  9. package/lib/cjs/explorer/ContractVerifier.js +10 -10
  10. package/lib/cjs/explorer/ContractVerifier.js.map +1 -1
  11. package/lib/cjs/explorer/verifiers/FsHtmlVerifier.js +1 -1
  12. package/lib/cjs/explorer/verifiers/FsHtmlVerifier.js.map +1 -1
  13. package/lib/esm/explorer/BlockchainExplorer.js.map +1 -1
  14. package/lib/esm/explorer/BlockchainExplorer.mjs +9 -9
  15. package/lib/esm/explorer/BlockchainExplorerFactory.js.map +1 -1
  16. package/lib/esm/explorer/BlockchainExplorerFactory.mjs +1 -1
  17. package/lib/esm/explorer/BlockchainExplorerProvider.js.map +1 -1
  18. package/lib/esm/explorer/BlockchainExplorerProvider.mjs +1 -1
  19. package/lib/esm/explorer/BlockchainExplorerStorage.js.map +1 -1
  20. package/lib/esm/explorer/BlockchainExplorerStorage.mjs +2 -2
  21. package/lib/esm/explorer/ContractVerifier.js.map +1 -1
  22. package/lib/esm/explorer/ContractVerifier.mjs +10 -10
  23. package/lib/esm/explorer/verifiers/FsHtmlVerifier.js.map +1 -1
  24. package/lib/esm/explorer/verifiers/FsHtmlVerifier.mjs +1 -1
  25. package/lib/types/explorer/BlockchainExplorerFactory.d.ts +1 -1
  26. package/package.json +1 -1
  27. package/skills/README.md +45 -0
  28. package/{docs/ai → skills/dequanto}/INDEX.md +12 -2
  29. package/{docs/ai/skill → skills}/dequanto/SKILL.md +27 -17
  30. package/{docs/ai → skills/dequanto}/examples/config.spec.ts +5 -5
  31. package/{docs/ai → skills/dequanto}/examples/contract-read.spec.ts +7 -7
  32. package/{docs/ai → skills/dequanto}/examples/contract-write.spec.ts +7 -7
  33. package/{docs/ai → skills/dequanto}/examples/deploy-contract.spec.ts +5 -5
  34. package/{docs/ai → skills/dequanto}/examples/erc4337-userop.spec.ts +7 -7
  35. package/{docs/ai → skills/dequanto}/examples/explorer-abi.spec.ts +3 -3
  36. package/{docs/ai → skills/dequanto}/examples/generate-contract-class.spec.ts +2 -2
  37. package/skills/dequanto/examples/hardhat-debug.spec.ts +29 -0
  38. package/skills/dequanto/examples/hardhat-fork.spec.ts +31 -0
  39. package/{docs/ai → skills/dequanto}/examples/index-events.spec.ts +4 -4
  40. package/{docs/ai → skills/dequanto}/examples/rpc-read.spec.ts +21 -4
  41. package/{docs/ai → skills/dequanto}/examples/safe-batch.spec.ts +6 -6
  42. package/{docs/ai → skills/dequanto}/examples/send-transaction.spec.ts +7 -7
  43. package/skills/dequanto/examples/storage.spec.ts +58 -0
  44. package/{docs/ai → skills/dequanto}/examples/token-transfer.spec.ts +5 -5
  45. package/skills/dequanto/examples/utils.spec.ts +63 -0
  46. package/{docs/ai → skills/dequanto}/references/contracts.md +1 -1
  47. package/{docs/ai → skills/dequanto}/references/deployments.md +4 -2
  48. package/{docs/ai → skills/dequanto}/references/events-indexing.md +1 -1
  49. package/{docs/ai → skills/dequanto}/references/explorer.md +1 -1
  50. package/{docs/ai → skills/dequanto}/references/generation.md +6 -6
  51. package/skills/dequanto/references/hardhat.md +113 -0
  52. package/{docs/ai → skills/dequanto}/references/rpc-clients.md +25 -5
  53. package/{docs/ai → skills/dequanto}/references/safe-erc4337.md +2 -2
  54. package/skills/dequanto/references/storage.md +122 -0
  55. package/{docs/ai → skills/dequanto}/references/tokens-accounts.md +12 -6
  56. package/{docs/ai → skills/dequanto}/references/transactions.md +6 -4
  57. package/skills/dequanto/references/utils.md +131 -0
  58. package/src/explorer/BlockchainExplorer.ts +9 -9
  59. package/src/explorer/BlockchainExplorerFactory.ts +1 -1
  60. package/src/explorer/BlockchainExplorerProvider.ts +1 -1
  61. package/src/explorer/BlockchainExplorerStorage.ts +2 -2
  62. package/src/explorer/ContractVerifier.ts +10 -10
  63. package/src/explorer/verifiers/FsHtmlVerifier.ts +1 -1
  64. package/tsconfig.json +2 -1
  65. package/docs/ai/examples/storage-read.spec.ts +0 -35
  66. package/docs/ai/references/storage.md +0 -93
  67. /package/{docs/ai → skills/dequanto}/references/migration-from-ethers-viem.md +0 -0
@@ -22,6 +22,8 @@ Useful tests:
22
22
 
23
23
  ## TxWriter Lifecycle
24
24
 
25
+ For transactions from any address on a Hardhat development network or fork, pass `{ address, type: 'impersonated' }` as the account. The writer enables impersonation automatically. See [Hardhat development and forking](hardhat.md#impersonate-an-account) for a complete ERC20 transfer example.
26
+
25
27
  Generated write methods return a `TxWriter`.
26
28
 
27
29
  ```ts
@@ -45,7 +47,7 @@ const receipt = await tx.wait();
45
47
  Use `BatchAgent` when a script should run its full transaction flow without immediately submitting transactions on-chain. `BatchAgent` plugs into `TxWriter.DEFAULTS.agent`, intercepts write calls, returns completed mock writers/receipts to the script, and caches the transactions for later review or execution.
46
48
 
47
49
  ```ts
48
- import { BatchAgent } from '@dequanto/txs/agents/BatchAgent';
50
+ import { BatchAgent } from 'dequanto/txs/agents/BatchAgent';
49
51
 
50
52
  const batch = new BatchAgent().enable();
51
53
 
@@ -76,7 +78,7 @@ Important behavior:
76
78
  ## Build And Sign Raw Tx
77
79
 
78
80
  ```ts
79
- import { TxDataBuilder } from '@dequanto/txs/TxDataBuilder';
81
+ import { TxDataBuilder } from 'dequanto/txs/TxDataBuilder';
80
82
 
81
83
  const builder = new TxDataBuilder(client, sender, {
82
84
  to: receiver
@@ -93,7 +95,7 @@ const receipt = await client.sendSignedTransaction(signed);
93
95
  ## Send Existing Tx Data
94
96
 
95
97
  ```ts
96
- import { TxWriter } from '@dequanto/txs/TxWriter';
98
+ import { TxWriter } from 'dequanto/txs/TxWriter';
97
99
 
98
100
  const tx = await TxWriter.writeTxData(client, {
99
101
  to: receiver,
@@ -133,7 +135,7 @@ const service = new TokenTransferService(client).$config({ nonce });
133
135
  Use `TxLogParser` for receipts when no generated contract parser is available.
134
136
 
135
137
  ```ts
136
- import { TxLogParser } from '@dequanto/txs/receipt/TxLogParser';
138
+ import { TxLogParser } from 'dequanto/txs/receipt/TxLogParser';
137
139
 
138
140
  const parser = new TxLogParser();
139
141
  const logs = await parser.parse(receipt, { abi });
@@ -0,0 +1,131 @@
1
+ # Utility Helpers
2
+
3
+ Dequanto includes helpers for common EVM and TypeScript operations. Prefer them when they make contract code shorter and more consistent. Verify unfamiliar helpers in the installed declarations.
4
+
5
+ Key source files:
6
+
7
+ - `src/abi/$abi.ts`
8
+ - `src/utils/$contract.ts`
9
+ - `src/utils/$require.ts`
10
+ - `src/utils/$hex.ts`
11
+ - `src/utils/$date.ts`
12
+ - `src/utils/$sig.ts`
13
+ - `src/utils/$promise.ts`
14
+
15
+ Runnable example: [utility helpers](../examples/utils.spec.ts).
16
+
17
+ ## ABI Encoding And Decoding
18
+
19
+ Import `$abi` from `dequanto/abi/$abi`. Use it instead of another ABI coder or manually constructed calldata.
20
+
21
+ ```ts
22
+ import { $abi } from 'dequanto/abi/$abi';
23
+
24
+ const encoded = $abi.encode(['address', 'uint256'], [account, 5n]);
25
+ const [decodedAccount, decodedAmount] = $abi.decode(['address', 'uint256'], encoded);
26
+ const calldata = $abi.encodeCall(
27
+ 'function transfer(address to, uint256 amount)',
28
+ [account, 5n]
29
+ );
30
+ const totalSupply = $abi.decodeReturn<bigint>(
31
+ 'function totalSupply() view returns (uint256)',
32
+ responseHex
33
+ );
34
+ ```
35
+
36
+ - `encode` and `decode` accept one type/value or arrays of types/values.
37
+ - `encodeCall` accepts a Solidity function declaration or JSON ABI item.
38
+ - `decodeReturn` accepts a function declaration, JSON ABI item, or output definitions. Multiple named outputs are returned as an object.
39
+
40
+ ## Solidity-Compatible Keccak-256
41
+
42
+ Use `$contract.keccak256(...)` for the same Keccak-256 operation used by Solidity:
43
+
44
+ ```ts
45
+ import { $contract } from 'dequanto/utils/$contract';
46
+
47
+ const topic = $contract.keccak256('Transfer(address,address,uint256)');
48
+ const bytes = $contract.keccak256('0x1234', 'buffer');
49
+ ```
50
+
51
+ A string beginning with `0x` is hashed as bytes; other strings are UTF-8 encoded. The default output is hex. Pass `'buffer'` for a `Uint8Array`.
52
+
53
+ ## Runtime Requirements
54
+
55
+ `$require` provides compact runtime validation similar in purpose to Solidity's `require`:
56
+
57
+ ```ts
58
+ import { $require } from 'dequanto/utils/$require';
59
+
60
+ const address = $require.AddressNotEmpty(inputAddress, 'Recipient is required');
61
+ $require.BigInt(amount, 'Amount must be bigint', { min: 1n });
62
+ $require.gte(balance, amount, 'Insufficient balance');
63
+ $require.Hex(calldata, 'Invalid calldata');
64
+ $require.notNull(receipt, 'Receipt was not returned');
65
+ ```
66
+
67
+ Useful checks include `True`, `notNull`, `Null`, `notEmpty`, `eq`, `notEq`, `gt`, `gte`, `lt`, `lte`, `match`, `has`, `oneOf`, `Address`, `AddressNotEmpty`, `AddressChecked`, `TxHash`, `Hex`, `Number`, `BigInt`, and `Numeric`. Most value validators return the validated value.
68
+
69
+ ## Hex Manipulation
70
+
71
+ Use `$hex` for `0x` normalization, conversion, padding, concatenation, slicing, and byte counts:
72
+
73
+ ```ts
74
+ import { $hex } from 'dequanto/utils/$hex';
75
+
76
+ const value = $hex.ensure('1234'); // 0x1234
77
+ const word = $hex.padBytes(value, 32); // left-pad to 32 bytes
78
+ const payload = $hex.concat([value, '0xabcd']);
79
+ const firstTwoBytes = $hex.getBytes(payload, 0, 2);
80
+ const byteLength = $hex.getBytesLength(payload);
81
+ ```
82
+
83
+ `toHex('hello')` encodes a normal string as UTF-8. `toHexBuffer(...)` also ensures an even number of hex digits. `padBytes` takes a byte count and pads left by default; pass `{ padEnd: true }` to pad right.
84
+
85
+ ## Date And Time
86
+
87
+ Use `$date` for time spans, Unix timestamps, formatting, date boundaries, and arithmetic:
88
+
89
+ ```ts
90
+ import { $date } from 'dequanto/utils/$date';
91
+
92
+ const timeoutMs = $date.parseTimespan('5min');
93
+ const timeoutSeconds = $date.parseTimespan('5min', { get: 's' });
94
+ const deadline = $date.tool().add('5min').toUnixTimestamp();
95
+ const date = $date.fromUnixTimestamp(deadline);
96
+ ```
97
+
98
+ `parseTimespan` returns milliseconds by default. Request seconds explicitly when passing time to a contract or RPC API.
99
+
100
+ ## Signing
101
+
102
+ Use `$sig` for account creation, message and typed-data signatures, transaction signing, and signer recovery:
103
+
104
+ ```ts
105
+ import { $sig } from 'dequanto/utils/$sig';
106
+
107
+ const account = $sig.$account.generate();
108
+ const signature = await $sig.signMessage('Approve operation', account);
109
+ const signer = $sig.recoverMessage('Approve operation', signature);
110
+ ```
111
+
112
+ Use `signMessage`/`recoverMessage` for EIP-191 messages, `signTypedData` for typed data, and `signTx`/`recoverTx` for transactions. Account helpers include `generate`, `fromMnemonic`, and `fromKey`.
113
+
114
+ ## Promise Results Without Try/Catch
115
+
116
+ `$promise.caught(...)` converts a rejection into a result object:
117
+
118
+ ```ts
119
+ import { $promise } from 'dequanto/utils/$promise';
120
+
121
+ const { result, error } = await $promise.caught(loadValue());
122
+ if (error) {
123
+ console.error(error.message);
124
+ } else {
125
+ console.log(result);
126
+ }
127
+ ```
128
+
129
+ It accepts a promise or a function returning a promise and returns `{ result }` on success or `{ error }` on failure. Other helpers cover delays, timeouts, events, callbacks, and polling.
130
+
131
+ The remaining files under `src/utils/` mostly support organization, conversion, formatting, and readability. Use them when relevant, but verify current signatures in `lib/types/utils/` rather than guessing.
@@ -175,10 +175,10 @@ export class BlockchainExplorer implements IBlockchainExplorer {
175
175
  try {
176
176
  abi = await this.client.get(url);
177
177
  } catch (err) {
178
- $logger.log(`Blockchain explorer request filed "${url}": ${err.message}`);
178
+ $logger.log(`Blockchain explorer request failed for "${url}": ${err.message}`);
179
179
  let addressByByteCode = await this.getSimilarContract(address);
180
180
  if (addressByByteCode != null) {
181
- $logger.log(`Found similar byte code address: ${addressByByteCode}`);
181
+ $logger.log(`Found similar bytecode address: ${addressByByteCode}`);
182
182
  return this.getContractAbi(addressByByteCode);
183
183
  }
184
184
  throw err;
@@ -195,7 +195,7 @@ export class BlockchainExplorer implements IBlockchainExplorer {
195
195
  let hex = $address.fromBytes32(uin256Hex);
196
196
  return this.getContractAbi(hex)
197
197
  }
198
- throw new Error(`Implement ${params.implementation} support`);
198
+ throw new Error(`Implementation lookup is not supported for ${params.implementation}`);
199
199
  }
200
200
  if (isOpenZeppelinProxy(abiJson) || mightBeProxy(abiJson)) {
201
201
  let web3 = this.getWeb3(this.platform);
@@ -384,12 +384,12 @@ export class BlockchainExplorer implements IBlockchainExplorer {
384
384
  try {
385
385
  return JSON.parse(str)
386
386
  } catch (error) {
387
- // etherscan returns code wrapped into {{}}
387
+ // Etherscan can return source code wrapped in {{...}}
388
388
  }
389
389
  str = str
390
390
  .replace(/^\s*\{\{/g, '{')
391
391
  .replace(/\}\}\s*$/g, '}');
392
- // @TODO check etherscan serialized jsons. Does it always has "{{...}}" wrappings
392
+ // @TODO Check Etherscan serialized JSON. Does it always use "{{...}}" wrapping?
393
393
 
394
394
  return JSON.parse(str)
395
395
  }
@@ -555,7 +555,7 @@ function hasMethod(abi: TAbiItem[], name: string) {
555
555
  function ensureDefaults(opts: TExplorerDefinition) {
556
556
 
557
557
  let platform = opts.platform;
558
- $require.notNull(platform, `Generic Blockchain Explorer Config should contain platform name`);
558
+ $require.notNull(platform, `Generic blockchain explorer config must include a platform name`);
559
559
 
560
560
  opts.ABI_CACHE ??= `./cache/${$platform.toPath(platform)}/abis.json`
561
561
  opts.CONTRACTS ??= [];
@@ -594,7 +594,7 @@ class HttpClient {
594
594
  async getHtml(url: string) {
595
595
  let resp = await $http.get(url);
596
596
  if (resp.status !== 200) {
597
- throw new Error(`${url} not loaded with status ${resp.status}.`);
597
+ throw new Error(`${url} failed to load with status ${resp.status}.`);
598
598
  }
599
599
  return resp.data;
600
600
  }
@@ -644,7 +644,7 @@ class HttpClient {
644
644
  throw error;
645
645
  }
646
646
  if (data.result == null) {
647
- $logger.warn(`Blockchain "${url}" explorer returned empty result`, data);
647
+ $logger.warn(`Blockchain explorer "${url}" returned an empty result`, data);
648
648
  }
649
649
  return data.result as TOut;
650
650
  }
@@ -669,7 +669,7 @@ class HttpClient {
669
669
  throw new Error(str);
670
670
  }
671
671
  if (data.result == null) {
672
- $logger.warn(`Blockchain "${url}" explorer returned empty result`, data);
672
+ $logger.warn(`Blockchain explorer "${url}" returned an empty result`, data);
673
673
  }
674
674
  return data.result;
675
675
  }
@@ -50,7 +50,7 @@ export namespace BlockchainExplorerFactory {
50
50
  return new BlockchainExplorer(params);
51
51
  }
52
52
 
53
- /** Same as sync variation, but ensures the config is being fetched */
53
+ /** Same as the synchronous variant, but ensures the config has been fetched */
54
54
  export async function getAsync (platform: TPlatform, opts?: IBlockchainExplorerFactoryParams): Promise<IBlockchainExplorer> {
55
55
  await Config.get();
56
56
  return get(platform, opts);
@@ -26,7 +26,7 @@ export namespace BlockchainExplorerProvider {
26
26
  if (cfg != null) {
27
27
  return new BlockchainExplorer({ platform, ...cfg });
28
28
  }
29
- throw new Error(`Unsupported platform ${platform} for block chain explorer`);
29
+ throw new Error(`Unsupported platform ${platform} for blockchain explorer`);
30
30
  }
31
31
  }
32
32
 
@@ -82,7 +82,7 @@ export class BlockchainExplorerStorage implements IBlockchainExplorer {
82
82
  let $address = opts?.implementation ?? address;
83
83
  let $contract = this.contracts[$address.toLowerCase()];
84
84
  $require.notNull($contract, `Contract "${$address}" not found`);
85
- // @TODO: L1: return abi as json (means in *scan providers we must serialize it earlier)
85
+ // @TODO: L1: Return ABI as JSON. *scan providers should serialize it earlier.
86
86
  return {
87
87
  implementation: $address,
88
88
  abi: JSON.stringify($contract.abi)
@@ -92,7 +92,7 @@ export class BlockchainExplorerStorage implements IBlockchainExplorer {
92
92
  let _address = address;
93
93
  let _contract = this.contracts[_address.toLowerCase()];
94
94
  $require.notNull(_contract, `Contract "${_address}" not found`);
95
- // @TODO: L1: return abi as json (means in *scan providers we must serialize it earlier)
95
+ // @TODO: L1: Return ABI as JSON. *scan providers should serialize it earlier.
96
96
  return {
97
97
  SourceCode: {
98
98
  contractName: _contract.name,
@@ -42,7 +42,7 @@ export class ContractVerifier {
42
42
  }
43
43
 
44
44
  async ensure (Ctor: TContractInfo, opts: TSubmissionOptions = {}): Promise<void> {
45
- this.logger.log(`Submit sources for ${typeof Ctor === 'string' ? Ctor : Ctor.name}`);
45
+ this.logger.log(`Submitting sources for ${typeof Ctor === 'string' ? Ctor : Ctor.name}`);
46
46
 
47
47
  let status = await this.submit(Ctor, opts);
48
48
  await this.waitForVerification(status, opts);
@@ -66,7 +66,7 @@ export class ContractVerifier {
66
66
  } catch (e) {
67
67
  let message = e.message;
68
68
  if (/verified/i.test(message)) {
69
- // was already verified
69
+ // Already verified.
70
70
  return true;
71
71
  }
72
72
  if (/(pending|queue)/i.test(message)) {
@@ -100,13 +100,13 @@ export class ContractVerifier {
100
100
  }
101
101
 
102
102
  try {
103
- this.logger.log(`Checking if already verified ${address}`);
103
+ this.logger.log(`Checking whether contract ${address} is already verified`);
104
104
  let currentSources = await this.explorer.getContractSource(address);
105
105
  if ($is.notEmpty(currentSources?.ContractName)) {
106
106
  return { status:'verified' };
107
107
  }
108
108
  } catch (error) {
109
- // ignore any pre-check error and continue with the verification
109
+ // Ignore pre-check errors and continue with verification.
110
110
  }
111
111
 
112
112
  let deployedBytecode = await client.getCode(address);
@@ -140,7 +140,7 @@ export class ContractVerifier {
140
140
  }
141
141
 
142
142
  let jsonMetaPath = info.ctx.source.path;
143
- $require.notEmpty(jsonMetaPath, `Deployment should return a path to compilation JSON`);
143
+ $require.notEmpty(jsonMetaPath, `Deployment should return a path to the compilation JSON`);
144
144
  $require.True(await File.existsAsync(jsonMetaPath) , `${jsonMetaPath} does not exist`);
145
145
 
146
146
  let jsonMeta = await File.readAsync<{ sourceName: string, contractName: string }>(jsonMetaPath);
@@ -150,7 +150,7 @@ export class ContractVerifier {
150
150
  let jsonMetaDbg = await File.readAsync<{ buildInfo: string }>(jsonMetaDbgPath);
151
151
 
152
152
  let jsonMetaBuildInfoPath = jsonMetaDbg.buildInfo;
153
- $require.notEmpty(jsonMetaPath, `${jsonMetaDbgPath} should contain the path to the buildInfo json file`);
153
+ $require.notEmpty(jsonMetaPath, `${jsonMetaDbgPath} should contain the path to the build info JSON file`);
154
154
 
155
155
  let buildInfoPath = new class_Uri(new class_Uri(jsonMetaPath).toDir()).combine(jsonMetaBuildInfoPath).toString();
156
156
  $require.True(await File.existsAsync(buildInfoPath) , `${buildInfoPath} does not exist`);
@@ -170,7 +170,7 @@ export class ContractVerifier {
170
170
  }
171
171
  let buildInfo = await File.readAsync<TBuildInfo>(buildInfoPath);
172
172
 
173
- $require.notNull(buildInfo.solcLongVersion, `${buildInfoPath} should contain the "solcLongVersion" information`);
173
+ $require.notNull(buildInfo.solcLongVersion, `${buildInfoPath} should contain the "solcLongVersion" value`);
174
174
 
175
175
  let sources = await this.getSources(jsonMeta.sourceName);
176
176
  let sourcesSerialized = JSON.stringify({
@@ -181,7 +181,7 @@ export class ContractVerifier {
181
181
 
182
182
  let sourcesSerializedWrapped = `${sourcesSerialized}`;
183
183
 
184
- this.logger.log(`Submit ${jsonMeta.sourceName}:${jsonMeta.contractName} and dependencies to verify ${address} contract`);
184
+ this.logger.log(`Submitting ${jsonMeta.sourceName}:${jsonMeta.contractName} and dependencies to verify contract ${address}`);
185
185
 
186
186
  try {
187
187
  let guid = await this.explorer.submitContractVerification({
@@ -243,7 +243,7 @@ export class ContractVerifier {
243
243
 
244
244
  await crawlSourceFile(new SourceFile(main));
245
245
 
246
- this.logger.log(`Found ${Object.keys(sources).length} source files: `, Object.keys(sources));
246
+ this.logger.log(`Found ${Object.keys(sources).length} source files:`, Object.keys(sources));
247
247
  return sources;
248
248
  }
249
249
 
@@ -284,7 +284,7 @@ export class ContractVerifier {
284
284
  } catch (e) {
285
285
  let message = e.message;
286
286
  if (/verified|success/i.test(message)) {
287
- // was already verified
287
+ // Already verified.
288
288
  return true;
289
289
  }
290
290
  if (/(pending|queue)/i.test(message)) {
@@ -115,7 +115,7 @@ export class FsHtmlVerifier implements IVerifier {
115
115
 
116
116
 
117
117
  if (contractData.contractName.includes('Utils/Proxy')) {
118
- // fix Proxy, rename contract duplicates
118
+ // Rename duplicate Proxy contracts before writing the verification form.
119
119
  html = html.replaceAll('contracts/Utils/Proxy.sol:Proxy', 'contracts/Utils/TUProxy.sol:TUProxy');
120
120
  html = html.replace('Utils/Proxy.sol":', 'Utils/TUProxy.sol":');
121
121
  html = html.replace('contract Proxy is ', 'contract TUProxy is ');
package/tsconfig.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "exclude": [
3
- "exports/core.lib.ts"
3
+ "exports/core.lib.ts",
4
+ "ts-temp"
4
5
  ],
5
6
  "compilerOptions": {
6
7
  "baseUrl": "./",
@@ -1,35 +0,0 @@
1
- import { Web3ClientFactory } from '@dequanto/clients/Web3ClientFactory';
2
- import { HardhatProvider } from '@dequanto/hardhat/HardhatProvider';
3
- import { SlotsParser } from '@dequanto/solidity/SlotsParser';
4
- import { SlotsStorage } from '@dequanto/solidity/SlotsStorage';
5
- import { $require } from '@dequanto/utils/$require';
6
-
7
- // This is the low-level path for creating a storage reader directly from Solidity source.
8
- // Classes generated from source with 0xweb install or @0xweb/hardhat expose storage on the instance: foo.storage.get('count').
9
- UTest({
10
- async 'read and write Solidity storage with SlotsParser and SlotsStorage' () {
11
- const code = `
12
- contract Vault {
13
- uint256 public count = 123;
14
- mapping(address => uint256) balances;
15
-
16
- constructor () {
17
- balances[address(0x1000000000000000000000000000000000000001)] = 7;
18
- }
19
- }
20
- `;
21
- const hh = new HardhatProvider();
22
- const client = await Web3ClientFactory.getAsync('hh:memory');
23
- const { contract } = await hh.deployCode(code, { client });
24
-
25
- const slots = await SlotsParser.slots({ path: '', code }, 'Vault');
26
- const storage = SlotsStorage.createWithClient(client, contract.address, slots);
27
-
28
- $require.eq(await storage.get('count'), 123n);
29
- $require.eq(await storage.get('balances["0x1000000000000000000000000000000000000001"]'), 7n);
30
-
31
- // Direct storage writes require a debug network such as Hardhat.
32
- await storage.set('count', 500n);
33
- $require.eq(await storage.get('count'), 500n);
34
- }
35
- });
@@ -1,93 +0,0 @@
1
- # Solidity Storage
2
-
3
- Use `SlotsParser` to derive layout and `SlotsStorage` to read or write slots. Do not decode raw storage words as ABI payloads.
4
-
5
- Key source files:
6
-
7
- - `src/solidity/SlotsParser.ts`
8
- - `src/solidity/SlotsStorage.ts`
9
- - `src/solidity/storage/handlers/SlotStructHandler.ts`
10
- - `src/solidity/storage/handlers/SlotMappingHandler.ts`
11
- - `src/solidity/storage/handlers/SlotDynamicArrayHandler.ts`
12
- - `src/solidity/storage/SlotsStorageTransport.ts`
13
- - `src/gen/GeneratorStorageReader.ts`
14
-
15
- Useful tests:
16
-
17
- - `test/solidity/SlotsReader.spec.ts`
18
- - `test/solidity/Storage.spec.ts`
19
- - `test/solidity/SlotsParser.spec.ts`
20
- - `test/generate/slotreader.spec.ts`
21
-
22
- ## Parse Slots From Solidity
23
-
24
- ```ts
25
- import { SlotsParser } from '@dequanto/solidity/SlotsParser';
26
-
27
- const slots = await SlotsParser.slots({
28
- path: './contracts/Vault.sol'
29
- }, 'Vault');
30
- ```
31
-
32
- For inline code:
33
-
34
- ```ts
35
- const slots = await SlotsParser.slots({ path: '', code }, 'Foo');
36
- ```
37
-
38
- ## Parse Slots From ABI Inputs
39
-
40
- Use this for standalone struct or tuple ABI data:
41
-
42
- ```ts
43
- const slots = await SlotsParser.slotsFromAbi(
44
- '(uint256 foo, address owner, bool enabled)'
45
- );
46
- ```
47
-
48
- ## Read Contract Storage
49
-
50
- ```ts
51
- import { SlotsStorage } from '@dequanto/solidity/SlotsStorage';
52
-
53
- const storage = SlotsStorage.createWithClient(client, contract.address, slots);
54
-
55
- const owner = await storage.get('owner');
56
- const user = await storage.get('users[0]');
57
- const balance = await storage.get(`balances["${account}"]`);
58
- ```
59
-
60
- Supported path forms include:
61
-
62
- - `field`
63
- - `struct.field`
64
- - `array[0]`
65
- - `mapping["0x..."]`
66
- - `mapping["0x..."].field`
67
- - `nested[0].balances[1]`
68
- - breadcrumb arrays such as `['users', account, 'balance']`
69
-
70
- ## Read Diamond Or Offset Storage
71
-
72
- For library-selected storage roots:
73
-
74
- ```ts
75
- const storage = SlotsStorage.createWithClient(client, contract.address, slots, {
76
- storageOffset: $contract.keccak256('diamond.app.storage')
77
- });
78
- ```
79
-
80
- ## Write Storage In Tests
81
-
82
- `SlotsStorage.set(...)` exists and is useful in Hardhat/debug contexts:
83
-
84
- ```ts
85
- await storage.set('user.balance', 1000n);
86
- ```
87
-
88
- Do not use storage writes in production code unless the transport explicitly supports it and the task requires test/debug mutation.
89
-
90
- ## Raw Slot Reads
91
-
92
- Use `client.getStorageAt(address, slot)` or `client.getStorageAtBatched(address, slots)` only when raw words are needed.
93
-