dequanto 0.2.49 → 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.
- package/contracts/oz/Proxy.sol +1 -0
- package/lib/cjs/abi/$abi.js +33 -0
- package/lib/cjs/abi/$abi.js.map +1 -1
- package/lib/cjs/blocks/BlockDateResolver.js.map +1 -1
- package/lib/cjs/clients/Web3Client.js +43 -2
- package/lib/cjs/clients/Web3Client.js.map +1 -1
- package/lib/cjs/clients/Web3ClientFactory.js +26 -4
- package/lib/cjs/clients/Web3ClientFactory.js.map +1 -1
- package/lib/cjs/clients/debug/ClientDebugMethods.js +4 -0
- package/lib/cjs/clients/debug/ClientDebugMethods.js.map +1 -1
- package/lib/cjs/clients/utils/$web3Abi.js +1 -1
- package/lib/cjs/clients/utils/$web3Abi.js.map +1 -1
- package/lib/cjs/config/Config.js +28 -6
- package/lib/cjs/config/Config.js.map +1 -1
- package/lib/cjs/config/ConfigDefaults.js +6 -2
- package/lib/cjs/config/ConfigDefaults.js.map +1 -1
- package/lib/cjs/config/ConfigProvider.js +5 -0
- package/lib/cjs/config/ConfigProvider.js.map +1 -1
- package/lib/cjs/config/DefaultFactory.js +27 -0
- package/lib/cjs/config/DefaultFactory.js.map +1 -0
- package/lib/cjs/contracts/ContractBase.js +6 -2
- package/lib/cjs/contracts/ContractBase.js.map +1 -1
- package/lib/cjs/contracts/ContractReader.js +1 -35
- package/lib/cjs/contracts/ContractReader.js.map +1 -1
- package/lib/cjs/explorer/BlockchainExplorer.js +9 -9
- package/lib/cjs/explorer/BlockchainExplorer.js.map +1 -1
- package/lib/cjs/explorer/BlockchainExplorerFactory.js +1 -1
- package/lib/cjs/explorer/BlockchainExplorerFactory.js.map +1 -1
- package/lib/cjs/explorer/BlockchainExplorerProvider.js +1 -1
- package/lib/cjs/explorer/BlockchainExplorerProvider.js.map +1 -1
- package/lib/cjs/explorer/BlockchainExplorerStorage.js +2 -2
- package/lib/cjs/explorer/BlockchainExplorerStorage.js.map +1 -1
- package/lib/cjs/explorer/ContractVerifier.js +10 -10
- package/lib/cjs/explorer/ContractVerifier.js.map +1 -1
- package/lib/cjs/explorer/verifiers/FsHtmlVerifier.js +1 -1
- package/lib/cjs/explorer/verifiers/FsHtmlVerifier.js.map +1 -1
- package/lib/cjs/rpc/RpcContract.js +4 -32
- package/lib/cjs/rpc/RpcContract.js.map +1 -1
- package/lib/cjs/utils/$account.js +5 -0
- package/lib/cjs/utils/$account.js.map +1 -1
- package/lib/cjs/utils/$config.js +4 -5
- package/lib/cjs/utils/$config.js.map +1 -1
- package/lib/cjs/utils/$erc20.js +47 -0
- package/lib/cjs/utils/$erc20.js.map +1 -0
- package/lib/cjs/utils/$require.js +2 -2
- package/lib/cjs/utils/$require.js.map +1 -1
- package/lib/esm/abi/$abi.js.map +1 -1
- package/lib/esm/abi/$abi.mjs +33 -0
- package/lib/esm/blocks/BlockDateResolver.js.map +1 -1
- package/lib/esm/clients/Web3Client.js.map +1 -1
- package/lib/esm/clients/Web3Client.mjs +43 -2
- package/lib/esm/clients/Web3ClientFactory.js.map +1 -1
- package/lib/esm/clients/Web3ClientFactory.mjs +26 -4
- package/lib/esm/clients/debug/ClientDebugMethods.js.map +1 -1
- package/lib/esm/clients/debug/ClientDebugMethods.mjs +4 -0
- package/lib/esm/clients/utils/$web3Abi.js.map +1 -1
- package/lib/esm/clients/utils/$web3Abi.mjs +1 -1
- package/lib/esm/config/Config.js.map +1 -1
- package/lib/esm/config/Config.mjs +28 -6
- package/lib/esm/config/ConfigDefaults.js.map +1 -1
- package/lib/esm/config/ConfigDefaults.mjs +6 -2
- package/lib/esm/config/ConfigProvider.js.map +1 -1
- package/lib/esm/config/ConfigProvider.mjs +5 -0
- package/lib/esm/config/DefaultFactory.js.map +1 -0
- package/lib/esm/config/DefaultFactory.mjs +23 -0
- package/lib/esm/contracts/ContractBase.js.map +1 -1
- package/lib/esm/contracts/ContractBase.mjs +6 -2
- package/lib/esm/contracts/ContractReader.js.map +1 -1
- package/lib/esm/contracts/ContractReader.mjs +1 -35
- package/lib/esm/explorer/BlockchainExplorer.js.map +1 -1
- package/lib/esm/explorer/BlockchainExplorer.mjs +9 -9
- package/lib/esm/explorer/BlockchainExplorerFactory.js.map +1 -1
- package/lib/esm/explorer/BlockchainExplorerFactory.mjs +1 -1
- package/lib/esm/explorer/BlockchainExplorerProvider.js.map +1 -1
- package/lib/esm/explorer/BlockchainExplorerProvider.mjs +1 -1
- package/lib/esm/explorer/BlockchainExplorerStorage.js.map +1 -1
- package/lib/esm/explorer/BlockchainExplorerStorage.mjs +2 -2
- package/lib/esm/explorer/ContractVerifier.js.map +1 -1
- package/lib/esm/explorer/ContractVerifier.mjs +10 -10
- package/lib/esm/explorer/verifiers/FsHtmlVerifier.js.map +1 -1
- package/lib/esm/explorer/verifiers/FsHtmlVerifier.mjs +1 -1
- package/lib/esm/rpc/RpcContract.js.map +1 -1
- package/lib/esm/rpc/RpcContract.mjs +4 -32
- package/lib/esm/utils/$account.js.map +1 -1
- package/lib/esm/utils/$account.mjs +5 -0
- package/lib/esm/utils/$config.js.map +1 -1
- package/lib/esm/utils/$config.mjs +4 -5
- package/lib/esm/utils/$erc20.js.map +1 -0
- package/lib/esm/utils/$erc20.mjs +41 -0
- package/lib/esm/utils/$require.js.map +1 -1
- package/lib/esm/utils/$require.mjs +2 -2
- package/lib/types/abi/$abi.d.ts +3 -1
- package/lib/types/blocks/BlockDateResolver.d.ts +1 -1
- package/lib/types/clients/Web3Client.d.ts +1 -1
- package/lib/types/clients/Web3ClientFactory.d.ts +10 -2
- package/lib/types/clients/debug/ClientDebugMethods.d.ts +1 -1
- package/lib/types/config/AConfigBase.d.ts +3 -0
- package/lib/types/config/Config.d.ts +2 -1
- package/lib/types/config/DefaultFactory.d.ts +2 -0
- package/lib/types/config/interface/IConfigData.d.ts +14 -2
- package/lib/types/contracts/ContractReader.d.ts +1 -1
- package/lib/types/explorer/BlockchainExplorerFactory.d.ts +1 -1
- package/lib/types/explorer/IBlockchainExplorer.d.ts +6 -0
- package/lib/types/models/TPlatform.d.ts +1 -1
- package/lib/types/utils/$account.d.ts +1 -0
- package/lib/types/utils/$erc20.d.ts +5 -0
- package/lib/types/utils/$require.d.ts +2 -2
- package/package.json +1 -1
- package/skills/README.md +45 -0
- package/{docs/ai → skills/dequanto}/INDEX.md +16 -5
- package/skills/dequanto/SKILL.md +73 -0
- package/skills/dequanto/examples/config.spec.ts +113 -0
- package/skills/dequanto/examples/contract-read.spec.ts +112 -0
- package/skills/dequanto/examples/contract-write.spec.ts +68 -0
- package/skills/dequanto/examples/deploy-contract.spec.ts +69 -0
- package/{docs/ai → skills/dequanto}/examples/erc4337-userop.spec.ts +13 -11
- package/skills/dequanto/examples/explorer-abi.spec.ts +21 -0
- package/{docs/ai/examples/generate-client.spec.ts → skills/dequanto/examples/generate-contract-class.spec.ts} +7 -4
- package/skills/dequanto/examples/hardhat-debug.spec.ts +29 -0
- package/skills/dequanto/examples/hardhat-fork.spec.ts +31 -0
- package/{docs/ai → skills/dequanto}/examples/index-events.spec.ts +10 -8
- package/skills/dequanto/examples/rpc-read.spec.ts +59 -0
- package/{docs/ai → skills/dequanto}/examples/safe-batch.spec.ts +10 -9
- package/skills/dequanto/examples/send-transaction.spec.ts +34 -0
- package/skills/dequanto/examples/storage.spec.ts +58 -0
- package/skills/dequanto/examples/token-transfer.spec.ts +29 -0
- package/skills/dequanto/examples/utils.spec.ts +63 -0
- package/{docs/ai → skills/dequanto}/references/contracts.md +1 -1
- package/{docs/ai → skills/dequanto}/references/deployments.md +4 -2
- package/{docs/ai → skills/dequanto}/references/events-indexing.md +1 -1
- package/{docs/ai → skills/dequanto}/references/explorer.md +1 -1
- package/{docs/ai → skills/dequanto}/references/generation.md +36 -13
- package/skills/dequanto/references/hardhat.md +113 -0
- package/{docs/ai → skills/dequanto}/references/rpc-clients.md +25 -5
- package/{docs/ai → skills/dequanto}/references/safe-erc4337.md +2 -2
- package/skills/dequanto/references/storage.md +122 -0
- package/{docs/ai → skills/dequanto}/references/tokens-accounts.md +12 -6
- package/{docs/ai → skills/dequanto}/references/transactions.md +6 -4
- package/skills/dequanto/references/utils.md +131 -0
- package/src/abi/$abi.ts +34 -1
- package/src/blocks/BlockDateResolver.ts +1 -1
- package/src/clients/Web3Client.ts +48 -3
- package/src/clients/Web3ClientFactory.ts +33 -5
- package/src/clients/debug/ClientDebugMethods.ts +5 -1
- package/src/clients/utils/$web3Abi.ts +2 -2
- package/src/config/AConfigBase.ts +4 -0
- package/src/config/Config.ts +30 -8
- package/src/config/ConfigDefaults.ts +6 -2
- package/src/config/ConfigProvider.ts +5 -1
- package/src/config/DefaultFactory.ts +26 -0
- package/src/config/interface/IConfigData.ts +15 -2
- package/src/contracts/ContractBase.ts +6 -2
- package/src/contracts/ContractReader.ts +3 -40
- package/src/explorer/BlockchainExplorer.ts +9 -9
- package/src/explorer/BlockchainExplorerFactory.ts +1 -2
- package/src/explorer/BlockchainExplorerProvider.ts +1 -1
- package/src/explorer/BlockchainExplorerStorage.ts +2 -2
- package/src/explorer/ContractVerifier.ts +10 -10
- package/src/explorer/IBlockchainExplorer.ts +4 -0
- package/src/explorer/verifiers/FsHtmlVerifier.ts +1 -1
- package/src/models/TPlatform.ts +1 -0
- package/src/rpc/RpcContract.ts +5 -33
- package/src/utils/$account.ts +5 -0
- package/src/utils/$config.ts +4 -6
- package/src/utils/$erc20.ts +53 -0
- package/src/utils/$require.ts +4 -4
- package/tsconfig.json +3 -3
- package/tsconfig.umd.json +1 -2
- package/docs/ai/examples/contract-read.spec.ts +0 -44
- package/docs/ai/examples/contract-write.spec.ts +0 -16
- package/docs/ai/examples/deploy-contract.spec.ts +0 -32
- package/docs/ai/examples/explorer-abi.spec.ts +0 -18
- package/docs/ai/examples/rpc-read.spec.ts +0 -15
- package/docs/ai/examples/send-transaction.spec.ts +0 -22
- package/docs/ai/examples/storage-read.spec.ts +0 -27
- package/docs/ai/examples/token-transfer.spec.ts +0 -22
- package/docs/ai/references/storage.md +0 -93
- package/docs/ai/skill/dequanto/SKILL.md +0 -59
- /package/{docs/ai → skills/dequanto}/references/migration-from-ethers-viem.md +0 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Hardhat Development And Forking
|
|
2
|
+
|
|
3
|
+
Dequanto integrates Hardhat as a development and forking network for contract development and experimenting with live contracts on a local fork. Use `client.debug` to mine blocks, change balances and storage, impersonate accounts, and restore snapshots. These changes affect the local network only.
|
|
4
|
+
|
|
5
|
+
Key source files:
|
|
6
|
+
|
|
7
|
+
- `src/clients/Web3ClientFactory.ts`
|
|
8
|
+
- `src/hardhat/HardhatProvider.ts`
|
|
9
|
+
- `src/hardhat/HardhatWeb3Client.ts`
|
|
10
|
+
- `src/clients/debug/ClientDebugMethods.ts`
|
|
11
|
+
- `src/utils/$erc20.ts`
|
|
12
|
+
- `src/txs/TxWriter.ts`
|
|
13
|
+
|
|
14
|
+
Runnable examples: [Hardhat debug operations](../examples/hardhat-debug.spec.ts) and [forked ERC20 transfers](../examples/hardhat-fork.spec.ts). The fork example requires an accessible Ethereum RPC.
|
|
15
|
+
|
|
16
|
+
## Create A Network Or Fork
|
|
17
|
+
|
|
18
|
+
Use a project with Hardhat installed and a `hardhat.config.*` file.
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { Web3ClientFactory } from 'dequanto/clients/Web3ClientFactory';
|
|
22
|
+
|
|
23
|
+
// Initialize an in-memory Ethereum fork using the platform's default/configured RPCs.
|
|
24
|
+
const client = await Web3ClientFactory.getAsync('hh:memory:eth');
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Replace `eth` with the platform to fork. Optionally pass `{ block: 20_000_000, url: 'https://...' }` as the second argument to select a block and override the source RPC. Initializing an in-memory fork resets the shared Hardhat network; create it before taking snapshots or changing state.
|
|
28
|
+
|
|
29
|
+
To use a separately running local Hardhat node, start it with Ethereum forking configured through Hardhat's normal node setup, then connect:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
const client = await Web3ClientFactory.getAsync('hh:eth');
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
This connects to the local node (by default `127.0.0.1:8545`) and associates it with Ethereum. It does not start the node or configure its fork.
|
|
36
|
+
|
|
37
|
+
For an in-memory development network without a fork, use `hh:memory`. Use `hardhat` to connect to a separately running local development node. See [deployments](deployments.md) for compiling and deploying contracts.
|
|
38
|
+
|
|
39
|
+
## Impersonate An Account
|
|
40
|
+
|
|
41
|
+
On Hardhat, submit transactions from any address, including a contract address, by passing an account with `type: 'impersonated'`. No private key is required. The transaction writer enables impersonation automatically.
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
import { ERC20 } from 'dequanto/prebuilt/openzeppelin/ERC20';
|
|
45
|
+
import { $erc20 } from 'dequanto/utils/$erc20';
|
|
46
|
+
|
|
47
|
+
const usdc = new ERC20('0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', client);
|
|
48
|
+
const account = {
|
|
49
|
+
address: '0x0000000000000000000000000000000000001234',
|
|
50
|
+
type: 'impersonated'
|
|
51
|
+
} as const;
|
|
52
|
+
const to = '0x0000000000000000000000000000000000005678';
|
|
53
|
+
const amount = 1_000_000n; // 1 USDC in its smallest units.
|
|
54
|
+
|
|
55
|
+
await client.debug.setBalance(account.address, 10n ** 18n);
|
|
56
|
+
await $erc20.setBalanceAny(client, usdc.address, account.address, amount);
|
|
57
|
+
const writer = await usdc.$receipt().transfer(account, to, amount);
|
|
58
|
+
console.log(writer.receipt.transactionHash);
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Contract checks still apply, including sufficient token balances and allowances. For explicit control, use `client.debug.impersonateAccount(address)` and `client.debug.stopImpersonatingAccount(address)`.
|
|
62
|
+
|
|
63
|
+
## Advance The Network
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
await client.debug.mine('5days');
|
|
67
|
+
await client.debug.mine(10); // Mine 10 blocks.
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Time strings are converted to seconds and used as the block count, with a default interval of one second per block. `mine('5days')` therefore mines `432_000` blocks, advancing network time by approximately five days. The optional second argument sets the interval in seconds.
|
|
71
|
+
|
|
72
|
+
## Set Native And ERC20 Balances
|
|
73
|
+
|
|
74
|
+
Set the native balance of any account or contract in wei:
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
await client.debug.setBalance(account.address, 10n ** 18n); // 1 ETH.
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Use `$erc20.setBalanceAny` to set an ERC20 balance without knowing its storage slot:
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
await $erc20.setBalanceAny(client, usdc.address, account.address, 1_000_000n);
|
|
84
|
+
// A number is interpreted in whole-token units instead:
|
|
85
|
+
await $erc20.setBalanceAny(client, usdc.address, account.address, 1);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The helper traces `balanceOf(account)` with `debug_traceCall`, examines accessed storage for the returned balance, and writes the matching slot. A `bigint` amount is in the token's smallest units; a `number` is converted using the token's decimals. This changes balance storage directly without minting tokens or updating total supply. Tokens whose balances are computed rather than stored directly may not be supported.
|
|
89
|
+
|
|
90
|
+
## Set Contract Storage
|
|
91
|
+
|
|
92
|
+
For a known storage slot, use:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
await client.debug.setStorageAt(contractAddress, slot, '0x2a');
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The value is padded to 32 bytes. For named variables, mappings, and packed fields, use a resolved storage layout with `SlotsStorage.set(...)`; see [Solidity storage](storage.md#write-storage-in-tests).
|
|
99
|
+
|
|
100
|
+
## Snapshots And Rollbacks
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
const id = await client.debug.snapshot();
|
|
104
|
+
try {
|
|
105
|
+
await client.debug.mine('5days');
|
|
106
|
+
await client.debug.setBalance(account.address, 10n ** 18n);
|
|
107
|
+
// Run transactions or assertions against the modified state.
|
|
108
|
+
} finally {
|
|
109
|
+
await client.debug.revert(id);
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Take the snapshot before modifying state. Create a fresh snapshot for each independent scenario.
|
|
@@ -8,6 +8,7 @@ Key source files:
|
|
|
8
8
|
- `src/clients/Web3Client.ts`
|
|
9
9
|
- `src/clients/EvmWeb3Client.ts`
|
|
10
10
|
- `src/clients/ClientPool.ts`
|
|
11
|
+
- `src/blocks/BlockDateResolver.ts`
|
|
11
12
|
- `src/rpc/Rpc.ts`
|
|
12
13
|
- `src/rpc/RpcBase.ts`
|
|
13
14
|
|
|
@@ -21,27 +22,29 @@ Useful tests:
|
|
|
21
22
|
## Client Creation
|
|
22
23
|
|
|
23
24
|
```ts
|
|
24
|
-
import { Web3ClientFactory } from '
|
|
25
|
+
import { Web3ClientFactory } from 'dequanto/clients/Web3ClientFactory';
|
|
25
26
|
|
|
26
27
|
const client = await Web3ClientFactory.getAsync('eth');
|
|
27
28
|
```
|
|
28
29
|
|
|
29
|
-
For Hardhat:
|
|
30
|
+
For an in-memory Hardhat fork using the platform's default/configured RPCs:
|
|
30
31
|
|
|
31
32
|
```ts
|
|
32
|
-
const client = await Web3ClientFactory.getAsync('
|
|
33
|
+
const client = await Web3ClientFactory.getAsync('hh:memory:eth');
|
|
33
34
|
```
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
To connect to an already running local Hardhat node configured as an Ethereum fork:
|
|
36
37
|
|
|
37
38
|
```ts
|
|
38
39
|
const client = await Web3ClientFactory.getAsync('hh:eth');
|
|
39
40
|
```
|
|
40
41
|
|
|
42
|
+
Use `hh:memory` for an in-memory development network without a fork, or `hardhat` for a separately running local development node. See [Hardhat development and forking](hardhat.md) for setup, impersonation, balance/storage changes, mining, and snapshots.
|
|
43
|
+
|
|
41
44
|
For direct endpoints:
|
|
42
45
|
|
|
43
46
|
```ts
|
|
44
|
-
import { EvmWeb3Client } from '
|
|
47
|
+
import { EvmWeb3Client } from 'dequanto/clients/EvmWeb3Client';
|
|
45
48
|
|
|
46
49
|
const client = new EvmWeb3Client({
|
|
47
50
|
platform: 'eth',
|
|
@@ -80,6 +83,23 @@ const result = await client.readContract({
|
|
|
80
83
|
});
|
|
81
84
|
```
|
|
82
85
|
|
|
86
|
+
## Resolve Blocks By Date
|
|
87
|
+
|
|
88
|
+
Use `BlockDateResolver` to find the block nearest to a date, or to resolve a block number back to its date:
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
import { BlockDateResolver } from 'dequanto/blocks/BlockDateResolver';
|
|
92
|
+
|
|
93
|
+
const resolver = new BlockDateResolver(client);
|
|
94
|
+
const date = new Date('2024-03-03T10:20:00Z');
|
|
95
|
+
|
|
96
|
+
const blockNumber = await resolver.getBlockNumberFor(date);
|
|
97
|
+
const { block, timestamp } = await resolver.getBlockInfoFor(date);
|
|
98
|
+
const blockDate = await resolver.getDate(blockNumber);
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`getBlockNumberFor(date)` returns the nearest block number. `getBlockInfoFor(date)` also returns that block's Unix timestamp. The resolver estimates from the chain's average block time and refines the estimate by loading blocks, so the result can be immediately before or after the requested time. Compare the returned timestamp when an exact time boundary matters. Dates before the chain was active throw a `Date out of range` error.
|
|
102
|
+
|
|
83
103
|
## Logs
|
|
84
104
|
|
|
85
105
|
Use `getPastLogs(filter, options?)` for raw log queries. It handles block-range pagination and node range limits.
|
|
@@ -51,8 +51,8 @@ const tx = await token
|
|
|
51
51
|
## Manual Safe Flow
|
|
52
52
|
|
|
53
53
|
```ts
|
|
54
|
-
import { GnosisSafeHandler } from '
|
|
55
|
-
import { ContractWriter } from '
|
|
54
|
+
import { GnosisSafeHandler } from 'dequanto/safe/GnosisSafeHandler';
|
|
55
|
+
import { ContractWriter } from 'dequanto/contracts/ContractWriter';
|
|
56
56
|
|
|
57
57
|
const writer = new ContractWriter(target.address, client);
|
|
58
58
|
const txWriter = await writer.writeAsync(safeAccount, 'airdrop()', [], {
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Solidity Storage
|
|
2
|
+
|
|
3
|
+
Prefer the storage reader included in generated contract classes. For manual layouts, 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
|
+
## Generated Contract Storage Readers
|
|
23
|
+
|
|
24
|
+
Contract classes installed from a blockchain explorer with `0xweb install`, or generated from Solidity sources with `@0xweb/hardhat`, include a storage reader on `contractInstance.storage`. It contains the storage layout derived from the source, including slot positions and packed fields, so you can access storage by variable name without manually resolving slots.
|
|
25
|
+
|
|
26
|
+
On a Hardhat development network or fork, write a variable with `await contractInstance.storage.$set('someVar', value)`. The generated reader exposes `$set` and `$get`; `set` and `get` are methods on the underlying `SlotsStorage`.
|
|
27
|
+
|
|
28
|
+
For example, using the generated AavePool class:
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { Web3ClientFactory } from 'dequanto/clients/Web3ClientFactory';
|
|
32
|
+
import { AavePool } from '../../../0xc/eth/AavePool/AavePool';
|
|
33
|
+
|
|
34
|
+
const client = await Web3ClientFactory.getAsync('hh:memory:eth');
|
|
35
|
+
const pool = new AavePool(undefined, client);
|
|
36
|
+
const id = await client.debug.snapshot();
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
await pool.storage.$set('_flashLoanPremium', 12_345n);
|
|
40
|
+
const premium = await pool.storage._flashLoanPremium();
|
|
41
|
+
const publicPremium = await pool.FLASHLOAN_PREMIUM_TOTAL();
|
|
42
|
+
} finally {
|
|
43
|
+
await client.debug.revert(id);
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The AavePool import points to this repository's generated class; use your project's generated path. Keep generated storage layouts in sync with the deployed implementation, especially after proxy upgrades. See the [storage example](../examples/storage.spec.ts) for assertions and rollback verification.
|
|
48
|
+
|
|
49
|
+
## Parse Slots From Solidity
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import { SlotsParser } from 'dequanto/solidity/SlotsParser';
|
|
53
|
+
|
|
54
|
+
const slots = await SlotsParser.slots({
|
|
55
|
+
path: './contracts/Vault.sol'
|
|
56
|
+
}, 'Vault');
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
For inline code:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
const slots = await SlotsParser.slots({ path: '', code }, 'Foo');
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Parse Slots From ABI Inputs
|
|
66
|
+
|
|
67
|
+
Use this for standalone struct or tuple ABI data:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
const slots = await SlotsParser.slotsFromAbi(
|
|
71
|
+
'(uint256 foo, address owner, bool enabled)'
|
|
72
|
+
);
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Read Contract Storage
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import { SlotsStorage } from 'dequanto/solidity/SlotsStorage';
|
|
79
|
+
|
|
80
|
+
const storage = SlotsStorage.createWithClient(client, contract.address, slots);
|
|
81
|
+
|
|
82
|
+
const owner = await storage.get('owner');
|
|
83
|
+
const user = await storage.get('users[0]');
|
|
84
|
+
const balance = await storage.get(`balances["${account}"]`);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Supported path forms include:
|
|
88
|
+
|
|
89
|
+
- `field`
|
|
90
|
+
- `struct.field`
|
|
91
|
+
- `array[0]`
|
|
92
|
+
- `mapping["0x..."]`
|
|
93
|
+
- `mapping["0x..."].field`
|
|
94
|
+
- `nested[0].balances[1]`
|
|
95
|
+
- breadcrumb arrays such as `['users', account, 'balance']`
|
|
96
|
+
|
|
97
|
+
## Read Diamond Or Offset Storage
|
|
98
|
+
|
|
99
|
+
For library-selected storage roots:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
const storage = SlotsStorage.createWithClient(client, contract.address, slots, {
|
|
103
|
+
storageOffset: $contract.keccak256('diamond.app.storage')
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Write Storage In Tests
|
|
108
|
+
|
|
109
|
+
`SlotsStorage.set(...)` exists and is useful in Hardhat/debug contexts:
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
await storage.set('user.balance', 1000n);
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
For a known raw slot on Hardhat, use `await client.debug.setStorageAt(contractAddress, slot, '0x2a')`; the value is padded to 32 bytes. To set an ERC20 balance without resolving its layout manually, use `$erc20.setBalanceAny(client, tokenAddress, accountAddress, amount)`. See [Hardhat development and forking](hardhat.md) for examples and snapshot/rollback handling.
|
|
116
|
+
|
|
117
|
+
Do not use storage writes in production code unless the transport explicitly supports it and the task requires test/debug mutation.
|
|
118
|
+
|
|
119
|
+
## Raw Slot Reads
|
|
120
|
+
|
|
121
|
+
Use `client.getStorageAt(address, slot)` or `client.getStorageAtBatched(address, slots)` only when raw words are needed.
|
|
122
|
+
|
|
@@ -21,7 +21,7 @@ Useful tests:
|
|
|
21
21
|
## Token Metadata
|
|
22
22
|
|
|
23
23
|
```ts
|
|
24
|
-
import { TokensService } from '
|
|
24
|
+
import { TokensService } from 'dequanto/tokens/TokensService';
|
|
25
25
|
|
|
26
26
|
const tokens = new TokensService('eth');
|
|
27
27
|
const usdc = await tokens.getKnownToken('USDC');
|
|
@@ -30,7 +30,7 @@ const usdc = await tokens.getKnownToken('USDC');
|
|
|
30
30
|
For forked Hardhat platform strings:
|
|
31
31
|
|
|
32
32
|
```ts
|
|
33
|
-
import { TokensServiceFactory } from '
|
|
33
|
+
import { TokensServiceFactory } from 'dequanto/tokens/TokensServiceFactory';
|
|
34
34
|
|
|
35
35
|
const tokens = await TokensServiceFactory.getAsync('hh:eth');
|
|
36
36
|
```
|
|
@@ -38,7 +38,7 @@ const tokens = await TokensServiceFactory.getAsync('hh:eth');
|
|
|
38
38
|
## Token Transfer Service
|
|
39
39
|
|
|
40
40
|
```ts
|
|
41
|
-
import { TokenTransferService } from '
|
|
41
|
+
import { TokenTransferService } from 'dequanto/tokens/TokenTransferService';
|
|
42
42
|
|
|
43
43
|
const service = new TokenTransferService(client);
|
|
44
44
|
const tx = await service.transfer(sender, receiver, 'USDC', 10);
|
|
@@ -69,7 +69,7 @@ const tx = await service.transferAllWithRemainder(sender, receiver, 'USDC', 1);
|
|
|
69
69
|
Generate or derive EOA accounts:
|
|
70
70
|
|
|
71
71
|
```ts
|
|
72
|
-
import { $sig } from '
|
|
72
|
+
import { $sig } from 'dequanto/utils/$sig';
|
|
73
73
|
|
|
74
74
|
const account = $sig.$account.generate();
|
|
75
75
|
const fromMnemonic = await $sig.$account.fromMnemonic(mnemonic, 0);
|
|
@@ -79,7 +79,7 @@ const fromKey = await $sig.$account.fromKey(privateKey);
|
|
|
79
79
|
Use `ChainAccountService` for named encrypted account storage:
|
|
80
80
|
|
|
81
81
|
```ts
|
|
82
|
-
import { ChainAccountService } from '
|
|
82
|
+
import { ChainAccountService } from 'dequanto/ChainAccountService';
|
|
83
83
|
|
|
84
84
|
const service = new ChainAccountService();
|
|
85
85
|
const account = await service.get('deployer');
|
|
@@ -91,9 +91,15 @@ Transaction APIs accept several account shapes:
|
|
|
91
91
|
|
|
92
92
|
- EOA account: `{ address, key }`
|
|
93
93
|
- named account: string resolved through `ChainAccountService`
|
|
94
|
-
- impersonated account in Hardhat
|
|
94
|
+
- impersonated account in Hardhat: `{ type: 'impersonated', address }` (no private key required)
|
|
95
95
|
- Safe account: `{ type: 'safe', address, operator, owners? }`
|
|
96
96
|
- ERC-4337 account: `{ type: 'erc4337', address, operator }`
|
|
97
97
|
|
|
98
98
|
Inspect `src/models/TAccount.ts` before adding a new account shape.
|
|
99
99
|
|
|
100
|
+
## Balances And Impersonation On Hardhat
|
|
101
|
+
|
|
102
|
+
Use `client.debug.setBalance(address, 10n ** 18n)` to set a native balance to 1 ETH. Use `$erc20.setBalanceAny(client, tokenAddress, accountAddress, amount)` to resolve and overwrite an ERC20 balance slot: `bigint` amounts are raw smallest units, while `number` amounts are converted using token decimals.
|
|
103
|
+
|
|
104
|
+
Pass an impersonated account to generated write methods, for example `await usdc.$receipt().transfer({ address, type: 'impersonated' }, to, amount)`. See [Hardhat development and forking](hardhat.md) and the [fork example](../examples/hardhat-fork.spec.ts).
|
|
105
|
+
|
|
@@ -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 '
|
|
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 '
|
|
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 '
|
|
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 '
|
|
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.
|
package/src/abi/$abi.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { TAbiInput } from '@dequanto/types/TAbi';
|
|
1
|
+
import { TAbiInput, TAbiItem, TAbiOutput } from '@dequanto/types/TAbi';
|
|
2
2
|
import { ParamType } from './fragments';
|
|
3
3
|
import { TEth } from '@dequanto/models/TEth';
|
|
4
4
|
import { $abiCoder } from './$abiCoder';
|
|
5
|
+
import { $abiUtils } from '@dequanto/utils/$abiUtils';
|
|
6
|
+
import { $abiParser } from '@dequanto/utils/$abiParser';
|
|
5
7
|
|
|
6
8
|
export namespace $abi {
|
|
7
9
|
type TAbiType = string | ParamType | TAbiInput;
|
|
@@ -29,6 +31,37 @@ export namespace $abi {
|
|
|
29
31
|
return isSingle ? result[0] : result;
|
|
30
32
|
}
|
|
31
33
|
|
|
34
|
+
export function encodeCall(abi: string | TAbiItem, params?: any[]): TEth.Hex {
|
|
35
|
+
return $abiUtils.serializeMethodCallData(abi, params);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function decodeReturn<T = any>(abi: string | TAbiItem | TAbiOutput[], hex: string): T {
|
|
39
|
+
let outputs = typeof abi === 'string'
|
|
40
|
+
? ($abiParser.parseMethod(abi).outputs ?? [])
|
|
41
|
+
: (Array.isArray(abi) ? abi : abi.outputs ?? []);
|
|
42
|
+
let returnAbi = outputs;
|
|
43
|
+
let isDynamic: boolean = null;
|
|
44
|
+
if (outputs.length > 1) {
|
|
45
|
+
let isNamedTuple = outputs.every(x => x.name != null && x.name !== '');
|
|
46
|
+
if (isNamedTuple) {
|
|
47
|
+
returnAbi = [{ type: 'tuple', components: outputs, name: null }];
|
|
48
|
+
isDynamic = false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
let arr = $abiCoder.decode(returnAbi as any, hex, {
|
|
54
|
+
dynamic: isDynamic
|
|
55
|
+
});
|
|
56
|
+
return returnAbi.length === 1 ? arr[0] : arr;
|
|
57
|
+
} catch (error) {
|
|
58
|
+
if (outputs.length === 1 && returnAbi.length === 1) {
|
|
59
|
+
return $abiCoder.decodeSingle(returnAbi[0], hex);
|
|
60
|
+
}
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
32
65
|
function ensureTypes (types: TAbiType | TAbiType[]): TAbiType[] {
|
|
33
66
|
let arr = Array.isArray(types) ? types : [ types ];
|
|
34
67
|
return arr.map($abiCoder.normalizeType);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import alot from 'alot';
|
|
2
2
|
import memd from 'memd';
|
|
3
|
-
import { Web3Client } from '@dequanto/clients/Web3Client';
|
|
4
3
|
import { TEth } from '@dequanto/models/TEth';
|
|
5
4
|
import { $block } from '@dequanto/utils/$block';
|
|
6
5
|
import { $date } from '@dequanto/utils/$date';
|
|
6
|
+
import type { Web3Client } from '@dequanto/clients/Web3Client';
|
|
7
7
|
|
|
8
8
|
export class BlockDateResolver {
|
|
9
9
|
private AVG_INITIAL = {
|
|
@@ -32,6 +32,8 @@ import { DataLike } from '@dequanto/utils/types';
|
|
|
32
32
|
import { ErrorCode } from './ClientPoolStats';
|
|
33
33
|
import { $date } from '@dequanto/utils/$date';
|
|
34
34
|
import { WalletClient } from './WalletClient';
|
|
35
|
+
import alot from 'alot';
|
|
36
|
+
import { $abiParser } from '@dequanto/utils/$abiParser';
|
|
35
37
|
|
|
36
38
|
export abstract class Web3Client implements IWeb3Client {
|
|
37
39
|
|
|
@@ -108,12 +110,55 @@ export abstract class Web3Client implements IWeb3Client {
|
|
|
108
110
|
}
|
|
109
111
|
|
|
110
112
|
// Submit contract calls in a batch and decode the responses.
|
|
111
|
-
async batchContractCalls(requests: TRpcContractCall[], options?: {
|
|
113
|
+
async batchContractCalls(requests: TRpcContractCall[] | Promise<TRpcContractCall>[], options?: {
|
|
112
114
|
allowErrors?: boolean
|
|
113
115
|
}) {
|
|
114
116
|
let reader = new RpcContract(this);
|
|
115
|
-
|
|
116
|
-
|
|
117
|
+
|
|
118
|
+
let requestsArrRaw = await Promise.all(requests);
|
|
119
|
+
let requestsArr = await alot(requestsArrRaw).mapAsync(async request => {
|
|
120
|
+
if (request == null) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
let abi = typeof request.abi === 'string'
|
|
124
|
+
? [ $abiParser.parseMethod(request.abi) ]
|
|
125
|
+
: (Array.isArray(request.abi) ? request.abi : [ request.abi ])
|
|
126
|
+
|
|
127
|
+
let blockNumber = request.blockNumber;
|
|
128
|
+
if (blockNumber instanceof Date) {
|
|
129
|
+
let resolver = di.resolve(BlockDateResolver, this);
|
|
130
|
+
blockNumber = await resolver.getBlockNumberFor(blockNumber);
|
|
131
|
+
}
|
|
132
|
+
let method = request.method;
|
|
133
|
+
if (method == null) {
|
|
134
|
+
$require.eq(abi.length, 1, `No function method is provided, the single ABI item expected`);
|
|
135
|
+
method = abi[0].name;
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
address: request.address,
|
|
139
|
+
abi: abi,
|
|
140
|
+
method: method,
|
|
141
|
+
params: request.params,
|
|
142
|
+
blockNumber: blockNumber,
|
|
143
|
+
options: options
|
|
144
|
+
} as TRpcContractCall;
|
|
145
|
+
}).toArrayAsync();
|
|
146
|
+
|
|
147
|
+
// Skip NULL request: mirror later as the NULL result
|
|
148
|
+
let mapped = [];
|
|
149
|
+
let rpcRequestsNotEmpty = [];
|
|
150
|
+
for (let i = 0; i < requestsArr.length; i++) {
|
|
151
|
+
if (requestsArr[i] == null) {
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
let idx = rpcRequestsNotEmpty.push(requestsArr[i]) - 1;
|
|
155
|
+
mapped[i] = idx;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
let outputs = await reader.batch(rpcRequestsNotEmpty, options);
|
|
159
|
+
return requestsArr.map((_, i) => {
|
|
160
|
+
return outputs[mapped[i]] ?? null;
|
|
161
|
+
});
|
|
117
162
|
}
|
|
118
163
|
|
|
119
164
|
getEventStream(address: TEth.Address, abi: TAbiItem[], event: string) {
|