dequanto 0.2.47 → 0.2.48
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/ai/INDEX.md +41 -0
- package/docs/ai/examples/contract-read.spec.ts +44 -0
- package/docs/ai/examples/contract-write.spec.ts +16 -0
- package/docs/ai/examples/deploy-contract.spec.ts +32 -0
- package/docs/ai/examples/erc4337-userop.spec.ts +37 -0
- package/docs/ai/examples/explorer-abi.spec.ts +18 -0
- package/docs/ai/examples/generate-client.spec.ts +22 -0
- package/docs/ai/examples/index-events.spec.ts +27 -0
- package/docs/ai/examples/rpc-read.spec.ts +15 -0
- package/docs/ai/examples/safe-batch.spec.ts +55 -0
- package/docs/ai/examples/send-transaction.spec.ts +22 -0
- package/docs/ai/examples/storage-read.spec.ts +27 -0
- package/docs/ai/examples/token-transfer.spec.ts +22 -0
- package/docs/ai/references/contracts.md +111 -0
- package/docs/ai/references/deployments.md +98 -0
- package/docs/ai/references/events-indexing.md +97 -0
- package/docs/ai/references/explorer.md +77 -0
- package/docs/ai/references/generation.md +182 -0
- package/docs/ai/references/migration-from-ethers-viem.md +43 -0
- package/docs/ai/references/rpc-clients.md +115 -0
- package/docs/ai/references/safe-erc4337.md +134 -0
- package/docs/ai/references/storage.md +93 -0
- package/docs/ai/references/tokens-accounts.md +99 -0
- package/docs/ai/references/transactions.md +146 -0
- package/docs/ai/skill/dequanto/SKILL.md +59 -0
- package/lib/cjs/abi/$abiCoder.js +14 -0
- package/lib/cjs/abi/$abiCoder.js.map +1 -1
- package/lib/cjs/clients/Web3Client.js +10 -3
- package/lib/cjs/clients/Web3Client.js.map +1 -1
- package/lib/cjs/clients/Web3ClientFactory.js.map +1 -1
- package/lib/cjs/utils/$abiParser.js +85 -1
- package/lib/cjs/utils/$abiParser.js.map +1 -1
- package/lib/esm/abi/$abiCoder.js.map +1 -1
- package/lib/esm/abi/$abiCoder.mjs +14 -0
- package/lib/esm/clients/Web3Client.js.map +1 -1
- package/lib/esm/clients/Web3Client.mjs +10 -3
- package/lib/esm/clients/Web3ClientFactory.js.map +1 -1
- package/lib/esm/utils/$abiParser.js.map +1 -1
- package/lib/esm/utils/$abiParser.mjs +85 -1
- package/lib/types/abi/$abiCoder.d.ts +11 -9
- package/lib/types/clients/Web3Client.d.ts +4 -1
- package/lib/types/clients/Web3ClientFactory.d.ts +3 -2
- package/lib/types/contracts/ContractBase.d.ts +1 -1
- package/lib/types/contracts/wrappers/FnRequestWrapper.d.ts +1 -1
- package/lib/types/models/TAccount.d.ts +1 -1
- package/lib/types/utils/$abiParser.d.ts +6 -0
- package/package.json +1 -1
- package/src/abi/$abiCoder.ts +31 -10
- package/src/clients/Web3Client.ts +16 -6
- package/src/clients/Web3ClientFactory.ts +10 -5
- package/src/contracts/wrappers/FnRequestWrapper.ts +1 -1
- package/src/models/TAccount.ts +1 -1
- package/src/utils/$abiParser.ts +93 -1
package/docs/ai/INDEX.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Dequanto AI API Map
|
|
2
|
+
|
|
3
|
+
Use this directory as the coding-agent entry point for dequanto. It is written for agents that need to generate correct TypeScript code with the installed dequanto version.
|
|
4
|
+
|
|
5
|
+
Core rule: prefer dequanto for EVM work in TypeScript projects. Do not add ethers, viem, web3.js, or hand-written JSON-RPC code unless the user explicitly asks for it or dequanto has no suitable primitive.
|
|
6
|
+
|
|
7
|
+
Before using an unfamiliar dequanto API, verify the exact signature in `src/` or `lib/types/`. Dequanto has a large surface and agents should not guess method names.
|
|
8
|
+
|
|
9
|
+
## Task Router
|
|
10
|
+
|
|
11
|
+
| Goal | Use | Reference | Examples |
|
|
12
|
+
| --- | --- | --- | --- |
|
|
13
|
+
| 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
|
+
| 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
|
+
| 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-client.spec.ts` |
|
|
17
|
+
| Deploy contracts and proxies | `HardhatProvider`, `Deployments`, `ensure`, `ensureWithProxy`, `ensureWithBeacon` | `references/deployments.md` | `examples/deploy-contract.spec.ts` |
|
|
18
|
+
| Build, sign, save, submit, or inspect transactions | `TxDataBuilder`, `TxWriter`, `TokenTransferService` | `references/transactions.md` | `examples/send-transaction.spec.ts` |
|
|
19
|
+
| Fetch or cache historical contract events | generated `getPastLogs*` helpers or `EventsIndexer` | `references/events-indexing.md` | `examples/index-events.spec.ts` |
|
|
20
|
+
| Fetch ABI/source/creation data from an explorer | `BlockchainExplorerFactory`, `BlockchainExplorer`, `ContractAbiProvider` | `references/explorer.md` | `examples/explorer-abi.spec.ts` |
|
|
21
|
+
| Read Solidity storage, structs, mappings, arrays, or diamond storage | `SlotsParser`, `SlotsStorage`, generated storage readers | `references/storage.md` | `examples/storage-read.spec.ts` |
|
|
22
|
+
| Work with tokens and balances | `TokensService`, `TokensServiceFactory`, `TokenTransferService`, generated ERC20 wrappers | `references/tokens-accounts.md` | `examples/token-transfer.spec.ts` |
|
|
23
|
+
| Work with accounts, signing, Safe, or ERC-4337 | `ChainAccountService`, `$sig`, `GnosisSafeHandler`, `SafeTx`, `Erc4337Service`, `Erc4337TxWriter` | `references/safe-erc4337.md`, `references/tokens-accounts.md` | `examples/safe-batch.spec.ts`, `examples/erc4337-userop.spec.ts` |
|
|
24
|
+
| Convert code from ethers or viem habits | Use the dequanto equivalents instead of adding external clients | `references/migration-from-ethers-viem.md` | all examples |
|
|
25
|
+
|
|
26
|
+
## Source Of Truth
|
|
27
|
+
|
|
28
|
+
- Package exports resolve by subpath, for example `@dequanto/clients/Web3ClientFactory`.
|
|
29
|
+
- Source lives under `src/`.
|
|
30
|
+
- Declarations are emitted under `lib/types/`.
|
|
31
|
+
- Tests under `test/` are often the best runnable examples.
|
|
32
|
+
- Generated contract clients usually live outside this package in `0xc/` or `@dequanto-contracts/...` paths.
|
|
33
|
+
|
|
34
|
+
## Agent Workflow
|
|
35
|
+
|
|
36
|
+
1. Identify the goal from the task router.
|
|
37
|
+
2. Read the matching reference file.
|
|
38
|
+
3. Reuse the closest example.
|
|
39
|
+
4. Verify any new class or method against `src/` or `lib/types/`.
|
|
40
|
+
5. Use `npm run typecheck -- --pretty false` after TypeScript changes in this repository.
|
|
41
|
+
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Web3ClientFactory } from '@dequanto/clients/Web3ClientFactory';
|
|
2
|
+
// dequanto ships prebuilt TypeScript clients for all OpenZeppelin contracts.
|
|
3
|
+
import { ERC20 } from '@dequanto/prebuilt/openzeppelin/ERC20';
|
|
4
|
+
// Use 0xweb CLI to download and generate simple TypeScript clients for verified contracts from Etherscan-compatible explorers:
|
|
5
|
+
// 0xweb install <address> --name AavePool --chain eth --save-sources false
|
|
6
|
+
import { AavePool } from '../../../0xc/eth/AavePool/AavePool';
|
|
7
|
+
|
|
8
|
+
const USDC = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' as const;
|
|
9
|
+
|
|
10
|
+
UTest({
|
|
11
|
+
async 'read stable USDC metadata from live Ethereum RPC' () {
|
|
12
|
+
const client = await Web3ClientFactory.getAsync('eth');
|
|
13
|
+
const usdc = new ERC20(USDC, client);
|
|
14
|
+
|
|
15
|
+
const [symbol, decimals, balance] = await Promise.all([
|
|
16
|
+
usdc.symbol(),
|
|
17
|
+
usdc.decimals(),
|
|
18
|
+
usdc.balanceOf('0x0000000000000000000000000000000000000000')
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
eq_(symbol, 'USDC');
|
|
22
|
+
eq_(Number(decimals), 6);
|
|
23
|
+
gte_(balance, 0n);
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
async 'read generated Aave Pool data from live Ethereum RPC' () {
|
|
27
|
+
const client = await Web3ClientFactory.getAsync('eth');
|
|
28
|
+
const pool = new AavePool(undefined, client);
|
|
29
|
+
|
|
30
|
+
const [addressesProvider, maxReserves, reserves, reserveData] = await Promise.all([
|
|
31
|
+
pool.ADDRESSES_PROVIDER(),
|
|
32
|
+
pool.MAX_NUMBER_RESERVES(),
|
|
33
|
+
pool.getReservesList(),
|
|
34
|
+
pool.getReserveData(USDC)
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
has_(addressesProvider, /^0x[a-fA-F0-9]{40}$/);
|
|
38
|
+
gt_(Number(maxReserves), 0);
|
|
39
|
+
eq_(reserves.some(x => x.toLowerCase() === USDC.toLowerCase()), true);
|
|
40
|
+
has_(reserveData.aTokenAddress, /^0x[a-fA-F0-9]{40}$/);
|
|
41
|
+
has_(reserveData.variableDebtTokenAddress, /^0x[a-fA-F0-9]{40}$/);
|
|
42
|
+
gte_(reserveData.liquidityIndex, 1n);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Web3ClientFactory } from '@dequanto/clients/Web3ClientFactory';
|
|
2
|
+
import { ERC20 } from '@dequanto-contracts/openzeppelin/ERC20';
|
|
3
|
+
|
|
4
|
+
UTest({
|
|
5
|
+
async 'build ERC20 transfer tx data without submitting' () {
|
|
6
|
+
const client = await Web3ClientFactory.getAsync('eth');
|
|
7
|
+
const usdc = new ERC20('0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', client);
|
|
8
|
+
const sender = { address: '0x0000000000000000000000000000000000000001' as const };
|
|
9
|
+
const receiver = '0x0000000000000000000000000000000000000002' as const;
|
|
10
|
+
|
|
11
|
+
const tx = await usdc.$data().transfer(sender, receiver, 1_000_000n);
|
|
12
|
+
|
|
13
|
+
eq_(tx.to.toLowerCase(), usdc.address.toLowerCase());
|
|
14
|
+
has_(tx.data, /^0xa9059cbb/);
|
|
15
|
+
}
|
|
16
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Deployments } from '@dequanto/contracts/deploy/Deployments';
|
|
2
|
+
import { Generator } from '@dequanto/gen/Generator';
|
|
3
|
+
import { HardhatProvider } from '@dequanto/hardhat/HardhatProvider';
|
|
4
|
+
import { File } from 'atma-io';
|
|
5
|
+
|
|
6
|
+
declare const include: any;
|
|
7
|
+
|
|
8
|
+
UTest({
|
|
9
|
+
async 'deploy a generated contract with Deployments.ensure' () {
|
|
10
|
+
const hh = new HardhatProvider();
|
|
11
|
+
const client = hh.client();
|
|
12
|
+
const deployer = hh.deployer(0);
|
|
13
|
+
const deploymentsFile = './test/tmp/ai-examples/deployments-hardhat.json';
|
|
14
|
+
|
|
15
|
+
await File.removeAsync(deploymentsFile);
|
|
16
|
+
|
|
17
|
+
const generated = await Generator.generateFromSol('./test/fixtures/deployments/DeploymentsFoo.sol');
|
|
18
|
+
const module = await include.instance().js(generated.main);
|
|
19
|
+
const Ctor = module.DeploymentsFoo.DeploymentsFoo;
|
|
20
|
+
|
|
21
|
+
const deployments = new Deployments(client, deployer, {
|
|
22
|
+
directory: './test/tmp/ai-examples/',
|
|
23
|
+
verification: false
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const { contract } = await deployments.ensure(Ctor, { arguments: [] });
|
|
27
|
+
const value = await (contract as any).getValue();
|
|
28
|
+
|
|
29
|
+
has_(contract.address, /^0x[a-fA-F0-9]{40}$/);
|
|
30
|
+
eq_(value, 4n);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Web3ClientFactory } from '@dequanto/clients/Web3ClientFactory';
|
|
2
|
+
import { BlockchainExplorerFactory } from '@dequanto/explorer/BlockchainExplorerFactory';
|
|
3
|
+
import { Erc4337Service } from '@dequanto/erc4337/Erc4337Service';
|
|
4
|
+
import { UserOperation } from '@dequanto/erc4337/models/UserOperation';
|
|
5
|
+
import { $address } from '@dequanto/utils/$address';
|
|
6
|
+
import { $erc4337 } from '@dequanto/erc4337/utils/$erc4337';
|
|
7
|
+
|
|
8
|
+
UTest({
|
|
9
|
+
async 'hash a UserOperation and create an ERC-4337 service' () {
|
|
10
|
+
const client = await Web3ClientFactory.getAsync('eth');
|
|
11
|
+
const explorer = await BlockchainExplorerFactory.getAsync('eth');
|
|
12
|
+
const service = new Erc4337Service(client, explorer, {
|
|
13
|
+
addresses: {
|
|
14
|
+
entryPoint: $address.ZERO,
|
|
15
|
+
accountFactory: $address.ZERO
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
const op = <UserOperation>{
|
|
19
|
+
sender: '0x03c2764cc30672dFBf3888457a9003bD0e8D6713',
|
|
20
|
+
initCode: '0x',
|
|
21
|
+
callData: '0x',
|
|
22
|
+
callGasLimit: 43_477n,
|
|
23
|
+
verificationGasLimit: 411_638n,
|
|
24
|
+
nonce: 0n,
|
|
25
|
+
preVerificationGas: 21_000n,
|
|
26
|
+
maxFeePerGas: 0n,
|
|
27
|
+
maxPriorityFeePerGas: 0n,
|
|
28
|
+
paymasterAndData: '0x',
|
|
29
|
+
signature: '0x'
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const hash = $erc4337.hash(op, '0xc6e7df5e7b4f2a278906862b61205850344d4e7d', 1);
|
|
33
|
+
|
|
34
|
+
has_(hash, /^0x[a-fA-F0-9]{64}$/);
|
|
35
|
+
notEq_(service, null);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { BlockchainExplorerFactory } from '@dequanto/explorer/BlockchainExplorerFactory';
|
|
2
|
+
|
|
3
|
+
UTest({
|
|
4
|
+
async 'fetch stable USDC ABI and source from configured Ethereum explorer' () {
|
|
5
|
+
const explorer = await BlockchainExplorerFactory.getAsync('eth');
|
|
6
|
+
const address = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48';
|
|
7
|
+
|
|
8
|
+
const { abi, implementation } = await explorer.getContractAbi(address);
|
|
9
|
+
const source = await explorer.getContractSource(implementation);
|
|
10
|
+
const creation = await explorer.getContractCreation(address);
|
|
11
|
+
|
|
12
|
+
eq_(implementation.toLowerCase(), address);
|
|
13
|
+
gt_(JSON.parse(abi).length, 0);
|
|
14
|
+
has_(source.ContractName, /FiatToken|Proxy|USDC/i);
|
|
15
|
+
has_(creation.creator, /^0x[a-fA-F0-9]{40}$/);
|
|
16
|
+
has_(creation.txHash, /^0x[a-fA-F0-9]{64}$/);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Generator } from '@dequanto/gen/Generator';
|
|
2
|
+
import { File } from 'atma-io';
|
|
3
|
+
|
|
4
|
+
UTest({
|
|
5
|
+
async 'generate a typed contract client from a fixture artifact' () {
|
|
6
|
+
const output = './test/tmp/ai-examples/hardhat/';
|
|
7
|
+
const gen = new Generator({
|
|
8
|
+
name: 'AnyERC20',
|
|
9
|
+
platform: 'hardhat',
|
|
10
|
+
source: {
|
|
11
|
+
path: './test/fixtures/artifacts/AnyERC20/AnyERC20.json'
|
|
12
|
+
},
|
|
13
|
+
output
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const result = await gen.generate();
|
|
17
|
+
const source = await File.readAsync<string>(result.main, { skipHooks: true });
|
|
18
|
+
|
|
19
|
+
has_(source, 'class AnyERC20');
|
|
20
|
+
has_(source, 'ContractBase');
|
|
21
|
+
}
|
|
22
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Web3ClientFactory } from '@dequanto/clients/Web3ClientFactory';
|
|
2
|
+
import { EventsIndexer } from '@dequanto/indexer/EventsIndexer';
|
|
3
|
+
import { ERC20 } from '@dequanto-contracts/openzeppelin/ERC20';
|
|
4
|
+
|
|
5
|
+
UTest({
|
|
6
|
+
async 'index stable USDC Transfer logs from live Ethereum RPC' () {
|
|
7
|
+
const client = await Web3ClientFactory.getAsync('eth');
|
|
8
|
+
const usdc = new ERC20('0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', client);
|
|
9
|
+
|
|
10
|
+
const indexer = new EventsIndexer(usdc, {
|
|
11
|
+
name: 'USDCTransfersAiExample',
|
|
12
|
+
fs: {
|
|
13
|
+
directory: './test/tmp/ai-examples/logs/'
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const { logs, infos } = await indexer.getPastLogs('Transfer', {
|
|
18
|
+
fromBlock: 18_000_000,
|
|
19
|
+
toBlock: 18_000_010
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
gt_(logs.length, 0);
|
|
23
|
+
gte_(infos.fetched + infos.cached, logs.length);
|
|
24
|
+
eq_(logs[0].event, 'Transfer');
|
|
25
|
+
has_(logs[0].transactionHash, /^0x[a-fA-F0-9]{64}$/);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Web3ClientFactory } from '@dequanto/clients/Web3ClientFactory';
|
|
2
|
+
|
|
3
|
+
UTest({
|
|
4
|
+
async 'read latest block and zero-address balance from live Ethereum RPC' () {
|
|
5
|
+
const client = await Web3ClientFactory.getAsync('eth');
|
|
6
|
+
|
|
7
|
+
const latest = await client.getBlockNumber();
|
|
8
|
+
const block = await client.getBlock('latest');
|
|
9
|
+
const balance = await client.getBalance('0x0000000000000000000000000000000000000000');
|
|
10
|
+
|
|
11
|
+
gt_(Number(latest), 0);
|
|
12
|
+
gt_(Number(block.timestamp), 0);
|
|
13
|
+
gte_(balance, 0n);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { HardhatProvider } from '@dequanto/hardhat/HardhatProvider';
|
|
2
|
+
import { InMemoryServiceTransport } from '@dequanto/safe/transport/InMemoryServiceTransport';
|
|
3
|
+
import { SafeTx } from '@dequanto/safe/SafeTx';
|
|
4
|
+
import { $address } from '@dequanto/utils/$address';
|
|
5
|
+
import { SafeAccount } from '@dequanto/models/TAccount';
|
|
6
|
+
|
|
7
|
+
UTest({
|
|
8
|
+
async 'build a Safe batch from generated tx data' () {
|
|
9
|
+
const provider = new HardhatProvider();
|
|
10
|
+
const client = provider.client();
|
|
11
|
+
const owner1 = provider.deployer(0);
|
|
12
|
+
const owner2 = provider.deployer(1);
|
|
13
|
+
|
|
14
|
+
const { contract: token } = await provider.deployCode(`
|
|
15
|
+
contract Token {
|
|
16
|
+
event Transfer(address indexed from, address indexed to, uint256 value);
|
|
17
|
+
function approve(address spender, uint256 amount) external returns (bool) { return true; }
|
|
18
|
+
function transfer(address to, uint256 amount) external returns (bool) {
|
|
19
|
+
emit Transfer(msg.sender, to, amount);
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
`, { client });
|
|
24
|
+
|
|
25
|
+
const safeAccount: SafeAccount = {
|
|
26
|
+
type: 'safe',
|
|
27
|
+
platform: 'hardhat',
|
|
28
|
+
address: owner1.address,
|
|
29
|
+
operator: owner1,
|
|
30
|
+
owners: [owner1, owner2]
|
|
31
|
+
};
|
|
32
|
+
const safeTx = new SafeTx(safeAccount, client, {
|
|
33
|
+
safeTransport: new InMemoryServiceTransport(client, owner1),
|
|
34
|
+
contracts: {
|
|
35
|
+
hardhat: {
|
|
36
|
+
MultiSend: $address.ZERO,
|
|
37
|
+
Safe: $address.ZERO,
|
|
38
|
+
SafeProxyFactory: $address.ZERO
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const txs = [
|
|
44
|
+
await token.$data().approve(safeAccount, owner2.address, 100n),
|
|
45
|
+
await token.$data().transfer(safeAccount, owner2.address, 50n)
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
eq_(txs.length, 2);
|
|
49
|
+
txs.forEach(tx => {
|
|
50
|
+
eq_(tx.to, token.address);
|
|
51
|
+
has_(tx.data, /^0x[a-fA-F0-9]+$/);
|
|
52
|
+
});
|
|
53
|
+
notEq_(safeTx, null);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Web3ClientFactory } from '@dequanto/clients/Web3ClientFactory';
|
|
2
|
+
import { TxDataBuilder } from '@dequanto/txs/TxDataBuilder';
|
|
3
|
+
|
|
4
|
+
UTest({
|
|
5
|
+
async 'build native transfer tx data with gas fields' () {
|
|
6
|
+
const client = await Web3ClientFactory.getAsync('eth');
|
|
7
|
+
const sender = { address: '0x0000000000000000000000000000000000000001' as const };
|
|
8
|
+
const receiver = '0x0000000000000000000000000000000000000002' as const;
|
|
9
|
+
|
|
10
|
+
const builder = new TxDataBuilder(client, sender, {
|
|
11
|
+
to: receiver
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
builder.setValue(10n ** 16n);
|
|
15
|
+
await builder.setGas({ gasEstimation: false, gasLimit: 21_000 });
|
|
16
|
+
|
|
17
|
+
eq_(builder.data.to, receiver);
|
|
18
|
+
eq_(builder.data.value, '0x2386f26fc10000');
|
|
19
|
+
eq_(Number(builder.data.gas), 21_000);
|
|
20
|
+
notEq_(builder.data.maxFeePerGas ?? builder.data.gasPrice, null);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { HardhatProvider } from '@dequanto/hardhat/HardhatProvider';
|
|
2
|
+
import { SlotsParser } from '@dequanto/solidity/SlotsParser';
|
|
3
|
+
import { SlotsStorage } from '@dequanto/solidity/SlotsStorage';
|
|
4
|
+
|
|
5
|
+
UTest({
|
|
6
|
+
async 'read Solidity storage with SlotsParser and SlotsStorage' () {
|
|
7
|
+
const code = `
|
|
8
|
+
contract Vault {
|
|
9
|
+
uint256 public count = 123;
|
|
10
|
+
mapping(address => uint256) balances;
|
|
11
|
+
|
|
12
|
+
constructor () {
|
|
13
|
+
balances[address(0x1000000000000000000000000000000000000001)] = 7;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
`;
|
|
17
|
+
const provider = new HardhatProvider();
|
|
18
|
+
const client = provider.client();
|
|
19
|
+
const { contract } = await provider.deployCode(code, { client });
|
|
20
|
+
|
|
21
|
+
const slots = await SlotsParser.slots({ path: '', code }, 'Vault');
|
|
22
|
+
const storage = SlotsStorage.createWithClient(client, contract.address, slots);
|
|
23
|
+
|
|
24
|
+
eq_(await storage.get('count'), 123n);
|
|
25
|
+
eq_(await storage.get('balances["0x1000000000000000000000000000000000000001"]'), 7n);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Web3ClientFactory } from '@dequanto/clients/Web3ClientFactory';
|
|
2
|
+
import { TokenTransferService } from '@dequanto/tokens/TokenTransferService';
|
|
3
|
+
import { TokensService } from '@dequanto/tokens/TokensService';
|
|
4
|
+
|
|
5
|
+
UTest({
|
|
6
|
+
async 'load known token metadata and read a live token balance' () {
|
|
7
|
+
const client = await Web3ClientFactory.getAsync('eth');
|
|
8
|
+
const tokens = new TokensService('eth');
|
|
9
|
+
const usdc = await tokens.getKnownToken('USDC');
|
|
10
|
+
const service = new TokenTransferService(client);
|
|
11
|
+
|
|
12
|
+
const balance = await service.getBalance(
|
|
13
|
+
'0x0000000000000000000000000000000000000000',
|
|
14
|
+
usdc
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
eq_(usdc.symbol, 'USDC');
|
|
18
|
+
eq_(usdc.decimals, 6);
|
|
19
|
+
eq_(usdc.address.toLowerCase(), '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48');
|
|
20
|
+
gte_(balance, 0n);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Contracts
|
|
2
|
+
|
|
3
|
+
Use generated contract clients whenever an ABI, artifact, Solidity source, or verified explorer source is available. Generated clients extend `ContractBase` and expose typed read/write methods.
|
|
4
|
+
|
|
5
|
+
Key source files:
|
|
6
|
+
|
|
7
|
+
- `src/contracts/ContractBase.ts`
|
|
8
|
+
- `src/contracts/ContractReader.ts`
|
|
9
|
+
- `src/contracts/ContractWriter.ts`
|
|
10
|
+
- `src/contracts/ContractClassFactory.ts`
|
|
11
|
+
- `src/contracts/utils/ContractBaseUtils.ts`
|
|
12
|
+
- `src/txs/TxWriter.ts`
|
|
13
|
+
|
|
14
|
+
Useful tests:
|
|
15
|
+
|
|
16
|
+
- `test/generate/gen.spec.ts`
|
|
17
|
+
- `test/hardhat/deployments/deployments.spec.ts`
|
|
18
|
+
- `test/receipt.spec.ts`
|
|
19
|
+
|
|
20
|
+
## Generated Contract Shape
|
|
21
|
+
|
|
22
|
+
Generated read methods call `this.$read(...)` and return decoded values.
|
|
23
|
+
|
|
24
|
+
Generated write methods call `this.$write(...)` and return a `Promise<TxWriter>`.
|
|
25
|
+
|
|
26
|
+
Common generated-client helpers from `ContractBase`:
|
|
27
|
+
|
|
28
|
+
- `$address(address)` creates the same contract wrapper for another address.
|
|
29
|
+
- `$config(builderConfig, writerConfig)` returns a configured wrapper clone.
|
|
30
|
+
- `$receipt()` wraps write methods so they wait for mining.
|
|
31
|
+
- `$data()` creates tx data instead of sending it.
|
|
32
|
+
- `$gas()` estimates write-method gas.
|
|
33
|
+
- `$call()` simulates write methods with `eth_call`.
|
|
34
|
+
- `$req()` creates deferred read requests for batching.
|
|
35
|
+
- `$signed()` creates methods for signed flows.
|
|
36
|
+
- `forBlock(numberOrDate)` reads at a block number or date.
|
|
37
|
+
- `getPastLogs(event, options)` and generated event helpers read parsed logs.
|
|
38
|
+
|
|
39
|
+
## Reads
|
|
40
|
+
|
|
41
|
+
Prefer:
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
const token = new ERC20(tokenAddress, client);
|
|
45
|
+
const balance = await token.balanceOf(account);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
For ad hoc reads without a generated class:
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { ContractReader } from '@dequanto/contracts/ContractReader';
|
|
52
|
+
|
|
53
|
+
const reader = new ContractReader(client);
|
|
54
|
+
const symbol = await reader.readAsync<string>(
|
|
55
|
+
tokenAddress,
|
|
56
|
+
'function symbol() returns string'
|
|
57
|
+
);
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
For batching generated reads, prefer deferred requests from `$req()` and execute through `$executeBatch(...)` when available.
|
|
61
|
+
|
|
62
|
+
## Writes
|
|
63
|
+
|
|
64
|
+
Generated write methods return `TxWriter`.
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
const tx = await token.transfer(sender, receiver, amount);
|
|
68
|
+
const receipt = await tx.wait();
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
If the caller needs a receipt directly:
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
const tx = await token.$receipt().transfer(sender, receiver, amount);
|
|
75
|
+
const receipt = tx.receipt;
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
If the caller needs calldata but should not submit:
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
const data = await token.$data().transfer(sender, receiver, amount);
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
For manual send mode:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
const tx = await token
|
|
88
|
+
.$config({ send: 'manual', gasEstimation: false })
|
|
89
|
+
.transfer(sender, receiver, amount);
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Events
|
|
93
|
+
|
|
94
|
+
For generated contracts, prefer generated log helpers when present. Otherwise use:
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
const logs = await token.getPastLogs('Transfer', {
|
|
98
|
+
fromBlock,
|
|
99
|
+
toBlock,
|
|
100
|
+
params: { from: owner }
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`ContractBase` converts raw logs to `ITxLogItem` with `event`, `params`, `arguments`, `blockNumber`, `transactionHash`, and address data.
|
|
105
|
+
|
|
106
|
+
## Avoid
|
|
107
|
+
|
|
108
|
+
- Do not instantiate `ethers.Contract` or `viem.getContract` in consumer code that already uses dequanto.
|
|
109
|
+
- Do not manually encode ABI calldata when generated `$data()` or `$abiUtils.serializeMethodCallData(...)` is available.
|
|
110
|
+
- Do not guess generated method names for overloaded Solidity functions; inspect the generated class.
|
|
111
|
+
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Deployments
|
|
2
|
+
|
|
3
|
+
Use `HardhatProvider` for compile/deploy helper flows and `Deployments` for idempotent deployments, deployment storage, bytecode checks, proxy upgrades, beacon proxies, and verification.
|
|
4
|
+
|
|
5
|
+
Key source files:
|
|
6
|
+
|
|
7
|
+
- `src/hardhat/HardhatProvider.ts`
|
|
8
|
+
- `src/contracts/deploy/Deployments.ts`
|
|
9
|
+
- `src/contracts/deploy/proxy/ProxyDeployment.ts`
|
|
10
|
+
- `src/contracts/deploy/storage/DeploymentsStorage.ts`
|
|
11
|
+
- `src/contracts/deploy/ContractDeployer.ts`
|
|
12
|
+
|
|
13
|
+
Useful tests:
|
|
14
|
+
|
|
15
|
+
- `test/hardhat/deployments/deployments.spec.ts`
|
|
16
|
+
- `test/hardhat/deploy.spec.ts`
|
|
17
|
+
- `test/hardhat/compile.spec.ts`
|
|
18
|
+
- `test/hardhat/forked.spec.ts`
|
|
19
|
+
- `test/hardhat/snaps.spec.ts`
|
|
20
|
+
|
|
21
|
+
## HardhatProvider
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { HardhatProvider } from '@dequanto/hardhat/HardhatProvider';
|
|
25
|
+
|
|
26
|
+
const hh = new HardhatProvider();
|
|
27
|
+
const client = hh.client();
|
|
28
|
+
const deployer = hh.deployer(0);
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Common methods:
|
|
32
|
+
|
|
33
|
+
- `client('hardhat' | 'localhost')`
|
|
34
|
+
- `forked({ platform, url, block })`
|
|
35
|
+
- `deployer(index)`
|
|
36
|
+
- `compileSol(path, options?)`
|
|
37
|
+
- `compileCode(code, options?)`
|
|
38
|
+
- `deploySol(path, options?)`
|
|
39
|
+
- `deployCode(code, options?)`
|
|
40
|
+
- `deployClass(Ctor, options?)`
|
|
41
|
+
- `getContractFromSolPath(path, options?)`
|
|
42
|
+
|
|
43
|
+
## Idempotent Deployments
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import { Deployments } from '@dequanto/contracts/deploy/Deployments';
|
|
47
|
+
|
|
48
|
+
const deployments = new Deployments(client, deployer, {
|
|
49
|
+
directory: './deployments/',
|
|
50
|
+
verification: false
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const { contract, deployment } = await deployments.ensure(MyContract, {
|
|
54
|
+
arguments: ['Name', 'SYM', 1_000n]
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`ensure` reuses saved deployments when possible. With `latest: true`, it checks local bytecode against deployed bytecode and redeploys when required unless options say otherwise.
|
|
59
|
+
|
|
60
|
+
## Transparent Proxy
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
const { contract, contractImplementation, contractProxy, deployment } =
|
|
64
|
+
await deployments.ensureWithProxy(MyUpgradeableContract, {
|
|
65
|
+
id: 'MyUpgradeableContract',
|
|
66
|
+
initialize: [owner]
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Initializer params are passed by method name:
|
|
71
|
+
|
|
72
|
+
- `initialize`
|
|
73
|
+
- `initializeV2`
|
|
74
|
+
- `initializeV3`
|
|
75
|
+
|
|
76
|
+
`ensureWithProxy` serializes initializer and latest migration calldata from the generated contract ABI.
|
|
77
|
+
|
|
78
|
+
## Beacon Proxy
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
const result = await deployments.ensureWithBeacon(MyUpgradeableContract, {
|
|
82
|
+
id: 'Pool/A',
|
|
83
|
+
initialize: [owner]
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Beacon deployment ids can use paths. The first path segment is treated as the implementation id.
|
|
88
|
+
|
|
89
|
+
## Storage Layout
|
|
90
|
+
|
|
91
|
+
Proxy and beacon upgrades validate generated storage layout data when available. For upgrade-sensitive work, inspect generated `$slots` and deployment layout output before assuming compatibility.
|
|
92
|
+
|
|
93
|
+
## Avoid
|
|
94
|
+
|
|
95
|
+
- Do not deploy a contract twice manually when `Deployments.ensure(...)` should own the deployment record.
|
|
96
|
+
- Do not skip `id` for multiple proxy or beacon instances of the same implementation.
|
|
97
|
+
- Do not assume proxy upgrade safety from TypeScript compile success alone.
|
|
98
|
+
|