dequanto 0.2.49 → 0.2.50
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/docs/ai/INDEX.md +4 -3
- package/docs/ai/examples/config.spec.ts +113 -0
- package/docs/ai/examples/contract-read.spec.ts +81 -13
- package/docs/ai/examples/contract-write.spec.ts +61 -9
- package/docs/ai/examples/deploy-contract.spec.ts +53 -16
- package/docs/ai/examples/erc4337-userop.spec.ts +7 -5
- package/docs/ai/examples/explorer-abi.spec.ts +9 -6
- package/docs/ai/examples/{generate-client.spec.ts → generate-contract-class.spec.ts} +6 -3
- package/docs/ai/examples/index-events.spec.ts +8 -6
- package/docs/ai/examples/rpc-read.spec.ts +32 -5
- package/docs/ai/examples/safe-batch.spec.ts +5 -4
- package/docs/ai/examples/send-transaction.spec.ts +23 -11
- package/docs/ai/examples/storage-read.spec.ts +15 -7
- package/docs/ai/examples/token-transfer.spec.ts +22 -15
- package/docs/ai/references/generation.md +30 -7
- package/docs/ai/skill/dequanto/SKILL.md +5 -1
- 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/BlockchainExplorerFactory.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/BlockchainExplorerFactory.js.map +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/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/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/BlockchainExplorerFactory.ts +0 -1
- package/src/explorer/IBlockchainExplorer.ts +4 -0
- 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 +1 -2
- package/tsconfig.umd.json +1 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
|
package/docs/ai/INDEX.md
CHANGED
|
@@ -10,10 +10,11 @@ Before using an unfamiliar dequanto API, verify the exact signature in `src/` or
|
|
|
10
10
|
|
|
11
11
|
| Goal | Use | Reference | Examples |
|
|
12
12
|
| --- | --- | --- | --- |
|
|
13
|
+
| Configure RPC endpoints, explorer keys, or custom chains | `Config.fetch`, `ConfigDefaults`, `RPC_<PLATFORM>` env vars, `blockchainExplorer` | `src/config/ConfigDefaults.ts` | `examples/config.spec.ts` |
|
|
13
14
|
| Create an RPC client, read blocks, balances, logs, storage, or raw tx data | `Web3ClientFactory`, `Web3Client`, chain clients | `references/rpc-clients.md` | `examples/rpc-read.spec.ts` |
|
|
14
15
|
| Call a smart contract | generated contract classes extending `ContractBase`, or `ContractReader` for ad hoc ABI calls | `references/contracts.md` | `examples/contract-read.spec.ts` |
|
|
15
16
|
| Send a contract transaction | generated write methods returning `TxWriter`; use `$receipt()` when the receipt is needed | `references/contracts.md`, `references/transactions.md` | `examples/contract-write.spec.ts` |
|
|
16
|
-
| Generate typed contract classes | `Generator`, `HardhatProvider`, 0xweb-generated classes | `references/generation.md` | `examples/generate-
|
|
17
|
+
| Generate typed contract classes | `Generator`, `HardhatProvider`, 0xweb-generated classes | `references/generation.md` | `examples/generate-contract-class.spec.ts` |
|
|
17
18
|
| Deploy contracts and proxies | `HardhatProvider`, `Deployments`, `ensure`, `ensureWithProxy`, `ensureWithBeacon` | `references/deployments.md` | `examples/deploy-contract.spec.ts` |
|
|
18
19
|
| Build, sign, save, submit, or inspect transactions | `TxDataBuilder`, `TxWriter`, `TokenTransferService` | `references/transactions.md` | `examples/send-transaction.spec.ts` |
|
|
19
20
|
| Fetch or cache historical contract events | generated `getPastLogs*` helpers or `EventsIndexer` | `references/events-indexing.md` | `examples/index-events.spec.ts` |
|
|
@@ -25,11 +26,11 @@ Before using an unfamiliar dequanto API, verify the exact signature in `src/` or
|
|
|
25
26
|
|
|
26
27
|
## Source Of Truth
|
|
27
28
|
|
|
28
|
-
- Package exports resolve by subpath
|
|
29
|
+
- Package exports resolve by subpath for both ESM imports and CommonJS requires: `import { Web3ClientFactory } from "dequanto/clients/Web3ClientFactory"` or `const { Web3ClientFactory } = require("dequanto/clients/Web3ClientFactory")`.
|
|
29
30
|
- Source lives under `src/`.
|
|
30
31
|
- Declarations are emitted under `lib/types/`.
|
|
31
32
|
- Tests under `test/` are often the best runnable examples.
|
|
32
|
-
- Generated contract clients
|
|
33
|
+
- Generated contract clients are published under `dequanto/prebuilt/...` and can be imported or required by subpath in the same way.
|
|
33
34
|
|
|
34
35
|
## Agent Workflow
|
|
35
36
|
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { Config } from '@dequanto/config/Config';
|
|
2
|
+
import { IRpcConfig } from '@dequanto/clients/ClientPool';
|
|
3
|
+
import { Web3ClientFactory } from '@dequanto/clients/Web3ClientFactory';
|
|
4
|
+
import { $require } from '@dequanto/utils/$require';
|
|
5
|
+
import { BlockchainExplorerFactory } from '@dequanto/explorer/BlockchainExplorerFactory';
|
|
6
|
+
|
|
7
|
+
UTest({
|
|
8
|
+
async 'load default configuration before RPC actions' () {
|
|
9
|
+
const config = await Config.fetch();
|
|
10
|
+
|
|
11
|
+
// Defaults are defined in src/config/ConfigDefaults.ts. They are useful for basic reads,
|
|
12
|
+
// but paid/custom RPCs are preferable for indexing and higher-volume requests.
|
|
13
|
+
$require.True(config.web3.eth.endpoints.some(x => x.url.includes('publicnode') || x.url.includes('drpc')));
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
async 'override a built-in platform RPC with the rpc alias' () {
|
|
17
|
+
await Config.fetch({
|
|
18
|
+
rpc: {
|
|
19
|
+
eth: 'https://example.bar'
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const client = await Web3ClientFactory.getAsync('eth');
|
|
24
|
+
$require.eq(client.options.endpoints.length, 1);
|
|
25
|
+
$require.eq(client.options.endpoints[0].url, 'https://example.bar');
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
async 'override a built-in platform with full RPC endpoint config' () {
|
|
29
|
+
await Config.fetch({
|
|
30
|
+
rpc: {
|
|
31
|
+
eth: [
|
|
32
|
+
<IRpcConfig>{
|
|
33
|
+
url: 'https://example2.bar',
|
|
34
|
+
rateLimit: '100/1sec',
|
|
35
|
+
blockRangeLimit: 100_000,
|
|
36
|
+
batchLimit: 100,
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const client = await Web3ClientFactory.getAsync('eth');
|
|
43
|
+
const endpoint = client.options.endpoints[0];
|
|
44
|
+
|
|
45
|
+
$require.eq(client.options.endpoints.length, 1);
|
|
46
|
+
$require.eq(endpoint.url, 'https://example2.bar');
|
|
47
|
+
$require.eq(endpoint.rateLimit, '100/1sec');
|
|
48
|
+
$require.eq(endpoint.blockRangeLimit, 100_000);
|
|
49
|
+
$require.eq(endpoint.batchLimit, 100);
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
async 'define a custom chain config' () {
|
|
53
|
+
await Config.fetch({
|
|
54
|
+
config: {
|
|
55
|
+
web3: {
|
|
56
|
+
foo: {
|
|
57
|
+
chainId: 500,
|
|
58
|
+
chainToken: 'FOO',
|
|
59
|
+
endpoints: [
|
|
60
|
+
{ url: 'wss://foo.example' },
|
|
61
|
+
{ url: 'https://foo.example' }
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const client = await Web3ClientFactory.getAsync('foo');
|
|
69
|
+
$require.eq(client.chainId, 500);
|
|
70
|
+
$require.eq(client.options.endpoints[0].url, 'wss://foo.example');
|
|
71
|
+
$require.eq(client.options.endpoints[1].url, 'https://foo.example');
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
async 'override a built-in blockchain explorer API key' () {
|
|
75
|
+
// Explorer APIs are used for contract verification, source loading, 0xweb install, and metadata.
|
|
76
|
+
const config = await Config.fetch({
|
|
77
|
+
config: {
|
|
78
|
+
blockchainExplorer: {
|
|
79
|
+
eth: { key: 'Foo' }
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
const explorer = await BlockchainExplorerFactory.getAsync('eth');
|
|
84
|
+
$require.eq(explorer.config.api.key, 'Foo');
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
async 'define blockchain explorer config for a custom platform' () {
|
|
88
|
+
await Config.fetch({
|
|
89
|
+
config: {
|
|
90
|
+
// web3 config is also required for the chain
|
|
91
|
+
web3: {
|
|
92
|
+
foo: { chainId: 4000, endpoints: [] },
|
|
93
|
+
},
|
|
94
|
+
blockchainExplorer: {
|
|
95
|
+
foo: { api: 'https://api.foo.bar/?token=BAR' }
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
const explorer = await BlockchainExplorerFactory.getAsync('foo');
|
|
100
|
+
$require.eq(explorer.config.api.url, 'https://api.foo.bar/?token=BAR' );
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
$teardown () {
|
|
104
|
+
Config.clean();
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Config sources agents should know about:
|
|
109
|
+
// 1. Global dequanto config, editable with: 0xweb config -e
|
|
110
|
+
// 2. Workspace config: ./configs/dequanto.yml
|
|
111
|
+
// 3. Runtime overrides: Config.fetch({ rpc }) or Config.fetch({ config })
|
|
112
|
+
// 4. Built-in platform RPC env vars: RPC_<PLATFORM>, for example RPC_ETH
|
|
113
|
+
// Explorer API keys can also be configured globally, which is important for deploy verification and 0xweb install.
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import { Web3ClientFactory } from '@dequanto/clients/Web3ClientFactory';
|
|
2
|
+
import { ContractClassFactory } from '@dequanto/contracts/ContractClassFactory';
|
|
2
3
|
// dequanto ships prebuilt TypeScript clients for all OpenZeppelin contracts.
|
|
3
4
|
import { ERC20 } from '@dequanto/prebuilt/openzeppelin/ERC20';
|
|
4
5
|
// Use 0xweb CLI to download and generate simple TypeScript clients for verified contracts from Etherscan-compatible explorers:
|
|
5
6
|
// 0xweb install <address> --name AavePool --chain eth --save-sources false
|
|
6
7
|
import { AavePool } from '../../../0xc/eth/AavePool/AavePool';
|
|
8
|
+
import { $abi } from '@dequanto/abi/$abi';
|
|
9
|
+
import { $require } from '@dequanto/utils/$require';
|
|
10
|
+
import type { TEth } from '@dequanto/models/TEth';
|
|
11
|
+
import { $address } from '@dequanto/utils/$address';
|
|
7
12
|
|
|
8
13
|
const USDC = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' as const;
|
|
14
|
+
const AAVE_POOL = '0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2' as const;
|
|
9
15
|
|
|
10
16
|
UTest({
|
|
11
|
-
async 'read
|
|
17
|
+
async 'read data from mainnet using the prebuilt ERC20 contract' () {
|
|
12
18
|
const client = await Web3ClientFactory.getAsync('eth');
|
|
13
19
|
const usdc = new ERC20(USDC, client);
|
|
14
20
|
|
|
@@ -18,12 +24,12 @@ UTest({
|
|
|
18
24
|
usdc.balanceOf('0x0000000000000000000000000000000000000000')
|
|
19
25
|
]);
|
|
20
26
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
27
|
+
$require.eq(symbol, 'USDC');
|
|
28
|
+
$require.eq(Number(decimals), 6);
|
|
29
|
+
$require.gte(balance, 0n);
|
|
24
30
|
},
|
|
25
31
|
|
|
26
|
-
async 'read
|
|
32
|
+
async 'read data from mainnet using the generated AavePool' () {
|
|
27
33
|
const client = await Web3ClientFactory.getAsync('eth');
|
|
28
34
|
const pool = new AavePool(undefined, client);
|
|
29
35
|
|
|
@@ -34,11 +40,73 @@ UTest({
|
|
|
34
40
|
pool.getReserveData(USDC)
|
|
35
41
|
]);
|
|
36
42
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
|
|
43
|
+
$require.AddressNotEmpty(addressesProvider);
|
|
44
|
+
$require.gt(Number(maxReserves), 0);
|
|
45
|
+
$require.True(reserves.some(x => $address.eq(x, USDC)));
|
|
46
|
+
$require.AddressNotEmpty(reserveData.aTokenAddress);
|
|
47
|
+
$require.AddressNotEmpty(reserveData.variableDebtTokenAddress);
|
|
48
|
+
$require.gte(reserveData.liquidityIndex, 1n);
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
async 'read data from mainnet by generating the class from ABI as JSON or string' () {
|
|
52
|
+
const client = await Web3ClientFactory.getAsync('eth');
|
|
53
|
+
const { contract: pool } = ContractClassFactory.fromAbi(AAVE_POOL, [
|
|
54
|
+
'function ADDRESSES_PROVIDER() view returns (address)',
|
|
55
|
+
{
|
|
56
|
+
type: 'function',
|
|
57
|
+
name: 'MAX_NUMBER_RESERVES',
|
|
58
|
+
stateMutability: 'view',
|
|
59
|
+
inputs: [],
|
|
60
|
+
outputs: [
|
|
61
|
+
{ type: 'uint16', name: '' }
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
], client);
|
|
65
|
+
|
|
66
|
+
const [addressesProvider, maxReserves] = await Promise.all([
|
|
67
|
+
pool.ADDRESSES_PROVIDER(),
|
|
68
|
+
pool.MAX_NUMBER_RESERVES()
|
|
69
|
+
]);
|
|
70
|
+
|
|
71
|
+
$require.AddressNotEmpty(addressesProvider);
|
|
72
|
+
$require.gt(Number(maxReserves), 0);
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
async 'read data from mainnet using a low-level RPC call' () {
|
|
76
|
+
const client = await Web3ClientFactory.getAsync('eth');
|
|
77
|
+
const rpc = await client.getRpc();
|
|
78
|
+
|
|
79
|
+
const addressesProviderAbi = 'function ADDRESSES_PROVIDER() view returns (address)';
|
|
80
|
+
const responseHex = await rpc.eth_call({
|
|
81
|
+
to: AAVE_POOL,
|
|
82
|
+
input: $abi.encodeCall(addressesProviderAbi),
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const addressesProvider = $abi.decodeReturn<TEth.Address>(addressesProviderAbi, responseHex);
|
|
86
|
+
|
|
87
|
+
$require.AddressNotEmpty(addressesProvider);
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
async 'batch-read data from mainnet using the generated AavePool' () {
|
|
92
|
+
const client = await Web3ClientFactory.getAsync('eth');
|
|
93
|
+
const pool = new AavePool(undefined, client);
|
|
94
|
+
|
|
95
|
+
const [addressesProvider, maxReserves, reserves, isNull, reserveData] = await client.batchContractCalls([
|
|
96
|
+
pool.$req().ADDRESSES_PROVIDER(),
|
|
97
|
+
pool.$req().MAX_NUMBER_RESERVES(),
|
|
98
|
+
pool.$req().getReservesList(),
|
|
99
|
+
// allows null
|
|
100
|
+
null,
|
|
101
|
+
pool.$req().getReserveData(USDC),
|
|
102
|
+
]);
|
|
103
|
+
|
|
104
|
+
$require.AddressNotEmpty(addressesProvider);
|
|
105
|
+
$require.gt(Number(maxReserves), 0);
|
|
106
|
+
$require.True(reserves.some(x => $address.eq(x, USDC)));
|
|
107
|
+
$require.AddressNotEmpty(reserveData.aTokenAddress);
|
|
108
|
+
$require.AddressNotEmpty(reserveData.variableDebtTokenAddress);
|
|
109
|
+
$require.gte(reserveData.liquidityIndex, 1n);
|
|
110
|
+
$require.Null(isNull);
|
|
111
|
+
},
|
|
112
|
+
});
|
|
@@ -1,16 +1,68 @@
|
|
|
1
1
|
import { Web3ClientFactory } from '@dequanto/clients/Web3ClientFactory';
|
|
2
|
-
import {
|
|
2
|
+
import { ContractReader } from '@dequanto/contracts/ContractReader';
|
|
3
|
+
import { ERC20 } from '@dequanto/prebuilt/openzeppelin/ERC20';
|
|
4
|
+
import { AavePool } from '../../../0xc/eth/AavePool/AavePool';
|
|
5
|
+
import type { TEth } from '@dequanto/models/TEth';
|
|
6
|
+
import { $account } from '@dequanto/utils/$account';
|
|
7
|
+
import { $address } from '@dequanto/utils/$address';
|
|
8
|
+
import { $require } from '@dequanto/utils/$require';
|
|
9
|
+
|
|
10
|
+
const USDC = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' as const;
|
|
3
11
|
|
|
4
12
|
UTest({
|
|
5
|
-
async '
|
|
13
|
+
async 'submit a contract write transaction' () {
|
|
14
|
+
// Use getAsync(platform) with a real funded account to submit the same write on mainnet
|
|
15
|
+
const client = await Web3ClientFactory.getAsync('hh:memory:eth');
|
|
16
|
+
const pool = new AavePool(undefined, client);
|
|
17
|
+
|
|
18
|
+
const addressesProvider = await pool.ADDRESSES_PROVIDER();
|
|
19
|
+
const poolConfigurator = await ContractReader.read<TEth.Address>(
|
|
20
|
+
client,
|
|
21
|
+
addressesProvider,
|
|
22
|
+
'function getPoolConfigurator() view returns (address)'
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const configuratorAccount = {
|
|
26
|
+
address: poolConfigurator,
|
|
27
|
+
type: 'impersonated'
|
|
28
|
+
} as const;
|
|
29
|
+
|
|
30
|
+
const newFlashloanPremium = 12_345n;
|
|
31
|
+
const tx = await pool.$receipt().updateFlashloanPremium(configuratorAccount, newFlashloanPremium);
|
|
32
|
+
$require.Hex(tx.receipt.transactionHash);
|
|
33
|
+
|
|
34
|
+
const premium = await pool.FLASHLOAN_PREMIUM_TOTAL();
|
|
35
|
+
$require.eq(premium, newFlashloanPremium);
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
async 'submit a contract write transaction and read a typed event log' () {
|
|
39
|
+
// Use getAsync(platform) with a real funded account to submit the same write on mainnet
|
|
40
|
+
const client = await Web3ClientFactory.getAsync('hh:memory:eth');
|
|
41
|
+
const pool = new AavePool(undefined, client);
|
|
42
|
+
|
|
43
|
+
const user = $account.generate('user');
|
|
44
|
+
const positionManager = $account.generate('positionManager');
|
|
45
|
+
|
|
46
|
+
await client.debug.setBalance(user.address, BigInt(1e18));
|
|
47
|
+
|
|
48
|
+
const tx = await pool.$receipt().approvePositionManager(user, positionManager.address, true);
|
|
49
|
+
$require.Hex(tx.receipt.transactionHash);
|
|
50
|
+
|
|
51
|
+
const [approvalLog] = await pool.extractLogsPositionManagerApproved(tx.receipt);
|
|
52
|
+
$require.True($address.eq(approvalLog.params.user, user.address));
|
|
53
|
+
$require.True($address.eq(approvalLog.params.positionManager, positionManager.address));
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
async 'build ERC20 transfer transaction data without submitting it' () {
|
|
6
57
|
const client = await Web3ClientFactory.getAsync('eth');
|
|
7
|
-
const usdc = new ERC20(
|
|
8
|
-
const
|
|
9
|
-
const
|
|
58
|
+
const usdc = new ERC20(USDC, client);
|
|
59
|
+
const transferSender = { address: '0x0000000000000000000000000000000000000001' as const };
|
|
60
|
+
const transferRecipient = '0x0000000000000000000000000000000000000002' as const;
|
|
61
|
+
const amount = 1_000_000n;
|
|
10
62
|
|
|
11
|
-
const tx = await usdc.$data().transfer(
|
|
63
|
+
const tx = await usdc.$data().transfer(transferSender, transferRecipient, amount);
|
|
12
64
|
|
|
13
|
-
|
|
14
|
-
|
|
65
|
+
$require.True($address.eq(tx.to, usdc.address));
|
|
66
|
+
$require.match(/^0xa9059cbb/, tx.data);
|
|
15
67
|
}
|
|
16
|
-
});
|
|
68
|
+
});
|
|
@@ -1,32 +1,69 @@
|
|
|
1
|
+
import { Web3ClientFactory } from '@dequanto/clients/Web3ClientFactory';
|
|
1
2
|
import { Deployments } from '@dequanto/contracts/deploy/Deployments';
|
|
2
3
|
import { Generator } from '@dequanto/gen/Generator';
|
|
3
|
-
import {
|
|
4
|
+
import { $account } from '@dequanto/utils/$account';
|
|
5
|
+
import { $require } from '@dequanto/utils/$require';
|
|
4
6
|
import { File } from 'atma-io';
|
|
5
7
|
|
|
8
|
+
// In a Hardhat project, include the @0xweb/hardhat plugin in hardhat.config.*.
|
|
9
|
+
// See https://github.com/0xweb-org/hardhat for plugin setup.
|
|
10
|
+
// Then hardhat compile auto-generates contract classes in 0xc/hardhat/**.
|
|
11
|
+
// A deployment script can import the generated class directly:
|
|
12
|
+
// import { FooContract } from '0xc/hardhat/FooContract/FooContract';
|
|
6
13
|
declare const include: any;
|
|
7
14
|
|
|
8
15
|
UTest({
|
|
9
|
-
async 'deploy a generated contract with Deployments.ensure' () {
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
16
|
+
async 'deploy a generated contract class with Deployments.ensure' () {
|
|
17
|
+
const { client, deployer, FooContract } = await getContractClass();
|
|
18
|
+
const deployments = new Deployments(client, deployer, {
|
|
19
|
+
directory: './test/tmp/ai-examples/'
|
|
20
|
+
});
|
|
14
21
|
|
|
15
|
-
|
|
22
|
+
// ensure returns an existing deployment by contract name, or by DeploymentOptions.id when provided.
|
|
23
|
+
// On mainnet, new deployments are automatically verified on the configured block explorer.
|
|
24
|
+
const { contract } = await deployments.ensure(FooContract, { arguments: [] });
|
|
25
|
+
// With an imported autogenerated class, contract is typed as FooContract with typed read/write methods.
|
|
26
|
+
const value = await (contract as any).getValue();
|
|
16
27
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
28
|
+
$require.AddressNotEmpty(contract.address);
|
|
29
|
+
$require.eq(value, 4n);
|
|
30
|
+
},
|
|
20
31
|
|
|
32
|
+
async 'deploy a generated contract class with an upgradeable proxy' () {
|
|
33
|
+
const { client, deployer, FooContract } = await getContractClass();
|
|
21
34
|
const deployments = new Deployments(client, deployer, {
|
|
22
|
-
directory: './test/tmp/ai-examples/'
|
|
23
|
-
verification: false
|
|
35
|
+
directory: './test/tmp/ai-examples/'
|
|
24
36
|
});
|
|
25
37
|
|
|
26
|
-
|
|
38
|
+
// ensureWithProxy uses OpenZeppelin TransparentUpgradeableProxy by default.
|
|
39
|
+
// It tracks Proxy, ProxyAdmin, and implementation deployments separately.
|
|
40
|
+
// The implementation bytecode hash is saved, so Solidity source changes deploy a new implementation.
|
|
41
|
+
// Existing proxies are then upgraded with upgradeToAndCall; the deployment call stays almost the same.
|
|
42
|
+
const { contract } = await deployments.ensureWithProxy(FooContract, {
|
|
43
|
+
arguments: [],
|
|
44
|
+
initialize: []
|
|
45
|
+
});
|
|
46
|
+
// With an imported autogenerated class, contract is typed as FooContract with typed read/write methods.
|
|
27
47
|
const value = await (contract as any).getValue();
|
|
28
48
|
|
|
29
|
-
|
|
30
|
-
|
|
49
|
+
$require.AddressNotEmpty(contract.address);
|
|
50
|
+
$require.eq(value, 6n);
|
|
31
51
|
}
|
|
32
|
-
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
async function getContractClass () {
|
|
55
|
+
const client = await Web3ClientFactory.getAsync('hh:memory');
|
|
56
|
+
const deployer = $account.generate('deployer');
|
|
57
|
+
await client.debug.setBalance(deployer.address, BigInt(1e18));
|
|
58
|
+
|
|
59
|
+
// Test-only cleanup: real projects usually keep deployment records between runs.
|
|
60
|
+
const deploymentsFile = './test/tmp/ai-examples/deployments-hardhat.json';
|
|
61
|
+
await File.removeAsync(deploymentsFile);
|
|
62
|
+
|
|
63
|
+
// Test-only generation/loading: hardhat compile normally generates this class via @0xweb/hardhat.
|
|
64
|
+
const generated = await Generator.generateFromSol('./test/fixtures/deployments/DeploymentsFoo.sol');
|
|
65
|
+
const module = await include.instance().js(generated.main);
|
|
66
|
+
const FooContract = module.DeploymentsFoo.DeploymentsFoo;
|
|
67
|
+
|
|
68
|
+
return { client, deployer, FooContract };
|
|
69
|
+
}
|
|
@@ -4,6 +4,7 @@ import { Erc4337Service } from '@dequanto/erc4337/Erc4337Service';
|
|
|
4
4
|
import { UserOperation } from '@dequanto/erc4337/models/UserOperation';
|
|
5
5
|
import { $address } from '@dequanto/utils/$address';
|
|
6
6
|
import { $erc4337 } from '@dequanto/erc4337/utils/$erc4337';
|
|
7
|
+
import { $require } from '@dequanto/utils/$require';
|
|
7
8
|
|
|
8
9
|
UTest({
|
|
9
10
|
async 'hash a UserOperation and create an ERC-4337 service' () {
|
|
@@ -28,10 +29,11 @@ UTest({
|
|
|
28
29
|
paymasterAndData: '0x',
|
|
29
30
|
signature: '0x'
|
|
30
31
|
};
|
|
32
|
+
const entryPointAddress = '0xc6e7df5e7b4f2a278906862b61205850344d4e7d';
|
|
33
|
+
const chainId = 1;
|
|
34
|
+
const hash = $erc4337.hash(op,entryPointAddress , chainId);
|
|
31
35
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
has_(hash, /^0x[a-fA-F0-9]{64}$/);
|
|
35
|
-
notEq_(service, null);
|
|
36
|
+
$require.match(/^0x[a-fA-F0-9]{64}$/, hash);
|
|
37
|
+
$require.notNull(service);
|
|
36
38
|
}
|
|
37
|
-
});
|
|
39
|
+
});
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { BlockchainExplorerFactory } from '@dequanto/explorer/BlockchainExplorerFactory';
|
|
2
|
+
import { $address } from '@dequanto/utils/$address';
|
|
3
|
+
import { $require } from '@dequanto/utils/$require';
|
|
2
4
|
|
|
3
5
|
UTest({
|
|
4
6
|
async 'fetch stable USDC ABI and source from configured Ethereum explorer' () {
|
|
@@ -9,10 +11,11 @@ UTest({
|
|
|
9
11
|
const source = await explorer.getContractSource(implementation);
|
|
10
12
|
const creation = await explorer.getContractCreation(address);
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
$require.AddressNotEmpty(implementation);
|
|
15
|
+
$require.True($address.eq(implementation, address) === false, `USDC is Proxy: ${implementation} != ${address}`);
|
|
16
|
+
$require.gt(JSON.parse(abi).length, 0);
|
|
17
|
+
$require.match(/FiatToken|Proxy|USDC/i, source.ContractName);
|
|
18
|
+
$require.AddressNotEmpty(creation.creator);
|
|
19
|
+
$require.TxHash(creation.txHash);
|
|
17
20
|
}
|
|
18
|
-
});
|
|
21
|
+
});
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { Generator } from '@dequanto/gen/Generator';
|
|
2
|
+
import { $require } from '@dequanto/utils/$require';
|
|
2
3
|
import { File } from 'atma-io';
|
|
3
4
|
|
|
5
|
+
// Generator is the low-level API for creating contract classes programmatically.
|
|
6
|
+
// Prefer 0xweb install for third-party contracts and @0xweb/hardhat for local Hardhat contracts.
|
|
4
7
|
UTest({
|
|
5
|
-
async 'generate a typed contract
|
|
8
|
+
async 'generate a typed contract class from a fixture artifact' () {
|
|
6
9
|
const output = './test/tmp/ai-examples/hardhat/';
|
|
7
10
|
const gen = new Generator({
|
|
8
11
|
name: 'AnyERC20',
|
|
@@ -16,7 +19,7 @@ UTest({
|
|
|
16
19
|
const result = await gen.generate();
|
|
17
20
|
const source = await File.readAsync<string>(result.main, { skipHooks: true });
|
|
18
21
|
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
$require.has('class AnyERC20', source);
|
|
23
|
+
$require.has('ContractBase', source);
|
|
21
24
|
}
|
|
22
25
|
});
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { Web3ClientFactory } from '@dequanto/clients/Web3ClientFactory';
|
|
2
2
|
import { EventsIndexer } from '@dequanto/indexer/EventsIndexer';
|
|
3
|
-
import { ERC20 } from '@dequanto
|
|
3
|
+
import { ERC20 } from '@dequanto/prebuilt/openzeppelin/ERC20';
|
|
4
|
+
import { $require } from '@dequanto/utils/$require';
|
|
4
5
|
|
|
5
6
|
UTest({
|
|
6
7
|
async 'index stable USDC Transfer logs from live Ethereum RPC' () {
|
|
7
8
|
const client = await Web3ClientFactory.getAsync('eth');
|
|
8
9
|
const usdc = new ERC20('0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', client);
|
|
9
10
|
|
|
11
|
+
// EventsIndexer stores fetched logs in the local cache directory, so later runs can reuse cached ranges.
|
|
10
12
|
const indexer = new EventsIndexer(usdc, {
|
|
11
13
|
name: 'USDCTransfersAiExample',
|
|
12
14
|
fs: {
|
|
@@ -19,9 +21,9 @@ UTest({
|
|
|
19
21
|
toBlock: 18_000_010
|
|
20
22
|
});
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
$require.gt(logs.length, 0);
|
|
25
|
+
$require.gte(infos.fetched + infos.cached, logs.length);
|
|
26
|
+
$require.eq(logs[0].event, 'Transfer');
|
|
27
|
+
$require.TxHash(logs[0].transactionHash);
|
|
26
28
|
}
|
|
27
|
-
});
|
|
29
|
+
});
|
|
@@ -1,15 +1,42 @@
|
|
|
1
1
|
import { Web3ClientFactory } from '@dequanto/clients/Web3ClientFactory';
|
|
2
|
+
import { Config } from '@dequanto/config/Config';
|
|
3
|
+
import { $require } from '@dequanto/utils/$require';
|
|
2
4
|
|
|
3
5
|
UTest({
|
|
4
|
-
async 'read
|
|
6
|
+
async '!read common RPC data via Web3Client directly' () {
|
|
7
|
+
const cfg = await Config.fetch();
|
|
8
|
+
|
|
5
9
|
const client = await Web3ClientFactory.getAsync('eth');
|
|
6
10
|
|
|
11
|
+
const chainId = await client.getChainId();
|
|
7
12
|
const latest = await client.getBlockNumber();
|
|
8
13
|
const block = await client.getBlock('latest');
|
|
9
14
|
const balance = await client.getBalance('0x0000000000000000000000000000000000000000');
|
|
10
15
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
16
|
+
$require.eq(chainId, 1);
|
|
17
|
+
$require.gt(Number(latest), 0);
|
|
18
|
+
$require.gt(Number(block.timestamp), 0);
|
|
19
|
+
$require.gte(balance, 0n);
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
async 'call low-level RPC methods from the generated Rpc client' () {
|
|
23
|
+
const client = await Web3ClientFactory.getAsync('eth');
|
|
24
|
+
// getRpc exposes the full generated RPC method list from OpenRPC specs, including eth, core-geth, MetaMask, and Flashbots methods.
|
|
25
|
+
const rpc = await client.getRpc();
|
|
26
|
+
|
|
27
|
+
const latest = await rpc.eth_blockNumber();
|
|
28
|
+
const block = await rpc.eth_getBlockByNumber('latest', false);
|
|
29
|
+
const balance = await rpc.eth_getBalance('0x0000000000000000000000000000000000000000', 'latest');
|
|
30
|
+
|
|
31
|
+
$require.gt(Number(latest), 0);
|
|
32
|
+
$require.gt(Number(block.timestamp), 0);
|
|
33
|
+
$require.gte(balance, 0n);
|
|
34
|
+
|
|
35
|
+
// Use rpc.request for custom or not-yet-generated RPC methods.
|
|
36
|
+
const response = await rpc.request({
|
|
37
|
+
method: 'eth_blockNumber',
|
|
38
|
+
params: []
|
|
39
|
+
});
|
|
40
|
+
$require.gt(Number(response), 0);
|
|
14
41
|
}
|
|
15
|
-
});
|
|
42
|
+
});
|
|
@@ -2,6 +2,7 @@ import { HardhatProvider } from '@dequanto/hardhat/HardhatProvider';
|
|
|
2
2
|
import { InMemoryServiceTransport } from '@dequanto/safe/transport/InMemoryServiceTransport';
|
|
3
3
|
import { SafeTx } from '@dequanto/safe/SafeTx';
|
|
4
4
|
import { $address } from '@dequanto/utils/$address';
|
|
5
|
+
import { $require } from '@dequanto/utils/$require';
|
|
5
6
|
import { SafeAccount } from '@dequanto/models/TAccount';
|
|
6
7
|
|
|
7
8
|
UTest({
|
|
@@ -45,11 +46,11 @@ UTest({
|
|
|
45
46
|
await token.$data().transfer(safeAccount, owner2.address, 50n)
|
|
46
47
|
];
|
|
47
48
|
|
|
48
|
-
|
|
49
|
+
$require.eq(txs.length, 2);
|
|
49
50
|
txs.forEach(tx => {
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
$require.True($address.eq(tx.to, token.address));
|
|
52
|
+
$require.match(/^0x[a-fA-F0-9]+$/, tx.data);
|
|
52
53
|
});
|
|
53
|
-
|
|
54
|
+
$require.notNull(safeTx);
|
|
54
55
|
}
|
|
55
56
|
});
|