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/src/abi/$abiCoder.ts
CHANGED
|
@@ -6,11 +6,24 @@ import { TEth } from '@dequanto/models/TEth';
|
|
|
6
6
|
import { $abiParser } from '@dequanto/utils/$abiParser';
|
|
7
7
|
|
|
8
8
|
export namespace $abiCoder {
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+
type TAbiType = string | ParamType | TAbiInput;
|
|
11
|
+
type TDecodeOptions = {
|
|
12
|
+
loose?: boolean
|
|
13
|
+
dynamic?: boolean
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function encode(types: TAbiType, values: any): TEth.Hex
|
|
17
|
+
export function encode(types: TAbiType[], values: any[]): TEth.Hex
|
|
18
|
+
export function encode(types: TAbiType | TAbiType[], values: any | any[]): TEth.Hex {
|
|
19
|
+
if (Array.isArray(types) == false) {
|
|
20
|
+
return encodeSingle(types, values)
|
|
21
|
+
}
|
|
10
22
|
let coder = new AbiCoder();
|
|
11
23
|
return coder.encode(types, values) as TEth.Hex;
|
|
12
24
|
}
|
|
13
|
-
export function encodeSingle(type:
|
|
25
|
+
export function encodeSingle(type: TAbiType, value: any): TEth.Hex {
|
|
26
|
+
type = normalizeType(type);
|
|
14
27
|
let coder = new AbiCoder();
|
|
15
28
|
return coder.encodeSingle(type, value) as TEth.Hex;
|
|
16
29
|
}
|
|
@@ -19,20 +32,20 @@ export namespace $abiCoder {
|
|
|
19
32
|
return solidityPacked(types, values) as TEth.Hex;
|
|
20
33
|
}
|
|
21
34
|
|
|
22
|
-
export function decode(types:
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
35
|
+
export function decode(types: TAbiType, hex: string, opts?: TDecodeOptions): any
|
|
36
|
+
export function decode(types: TAbiType[], hex: string, opts?: TDecodeOptions): any[]
|
|
37
|
+
export function decode(types: TAbiType| TAbiType[], hex: string, opts?: TDecodeOptions): any {
|
|
38
|
+
if (Array.isArray(types) == false) {
|
|
39
|
+
return decodeSingle(types, hex, opts)
|
|
40
|
+
}
|
|
26
41
|
let coder = new AbiCoder();
|
|
27
42
|
let arr = coder.decode(types, hex, opts);
|
|
28
43
|
return arr.map((x, i) => {
|
|
29
44
|
return unwrap(types[i] as TAbiInput, x);
|
|
30
45
|
});
|
|
31
46
|
}
|
|
32
|
-
export function decodeSingle(type:
|
|
33
|
-
|
|
34
|
-
dynamic?: boolean
|
|
35
|
-
}): any {
|
|
47
|
+
export function decodeSingle(type: TAbiType, hex: string, opts?: TDecodeOptions): any {
|
|
48
|
+
type = normalizeType(type);
|
|
36
49
|
let coder = new AbiCoder();
|
|
37
50
|
let x = coder.decodeSingle(type, hex, opts);
|
|
38
51
|
return unwrap(type as TAbiInput, x);
|
|
@@ -72,4 +85,12 @@ export namespace $abiCoder {
|
|
|
72
85
|
}
|
|
73
86
|
return mixValue;
|
|
74
87
|
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
function normalizeType (type: TAbiType) {
|
|
91
|
+
if (typeof type === 'string' && /\bstruct /.test(type)) {
|
|
92
|
+
return $abiParser.parseStruct(type);
|
|
93
|
+
}
|
|
94
|
+
return type;
|
|
95
|
+
}
|
|
75
96
|
}
|
|
@@ -97,6 +97,8 @@ export abstract class Web3Client implements IWeb3Client {
|
|
|
97
97
|
});
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
// Low-level batch for raw RPC calls.
|
|
101
|
+
// For contract batch calls, use batchContractCalls to decode the responses.
|
|
100
102
|
async batch(requests: TRpc.IRpcAction[], options?: {
|
|
101
103
|
allowErrors?: boolean
|
|
102
104
|
}): Promise<any[]> {
|
|
@@ -105,6 +107,15 @@ export abstract class Web3Client implements IWeb3Client {
|
|
|
105
107
|
});
|
|
106
108
|
}
|
|
107
109
|
|
|
110
|
+
// Submit contract calls in a batch and decode the responses.
|
|
111
|
+
async batchContractCalls(requests: TRpcContractCall[], options?: {
|
|
112
|
+
allowErrors?: boolean
|
|
113
|
+
}) {
|
|
114
|
+
let reader = new RpcContract(this);
|
|
115
|
+
let result = await reader.batch(requests, options);
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
|
|
108
119
|
getEventStream(address: TEth.Address, abi: TAbiItem[], event: string) {
|
|
109
120
|
let eventAbi = abi.find(x => x.type === 'event' && x.name === event);
|
|
110
121
|
if (eventAbi == null) {
|
|
@@ -189,12 +200,11 @@ export abstract class Web3Client implements IWeb3Client {
|
|
|
189
200
|
return result;
|
|
190
201
|
}
|
|
191
202
|
|
|
203
|
+
// Alias for batchContractCalls.
|
|
192
204
|
async readContractBatch(requests: TRpcContractCall[], options?: {
|
|
193
205
|
allowErrors?: boolean
|
|
194
206
|
}) {
|
|
195
|
-
|
|
196
|
-
let result = await reader.batch(requests, options);
|
|
197
|
-
return result;
|
|
207
|
+
return this.batchContractCalls(requests, options);
|
|
198
208
|
}
|
|
199
209
|
|
|
200
210
|
|
|
@@ -412,12 +422,12 @@ export abstract class Web3Client implements IWeb3Client {
|
|
|
412
422
|
// return tx;
|
|
413
423
|
// }
|
|
414
424
|
|
|
415
|
-
async sign(address: TEth.Address, message: string): Promise<
|
|
425
|
+
async sign(address: TEth.Address, message: string): Promise<TEth.Hex> {
|
|
416
426
|
if (this.wallet.isConnected(address)) {
|
|
417
|
-
return this.wallet.eth_sign(address, message)
|
|
427
|
+
return this.wallet.eth_sign(address, message) as Promise<TEth.Hex>;
|
|
418
428
|
}
|
|
419
429
|
return this.pool.call(wClient => {
|
|
420
|
-
return wClient.sign(address, message)
|
|
430
|
+
return wClient.sign(address, message) as Promise<TEth.Hex>;
|
|
421
431
|
}, {
|
|
422
432
|
wallet: true
|
|
423
433
|
});
|
|
@@ -6,16 +6,19 @@ import { Config } from '@dequanto/config/Config';
|
|
|
6
6
|
import { EvmWeb3Client } from './EvmWeb3Client';
|
|
7
7
|
import { $require } from '@dequanto/utils/$require';
|
|
8
8
|
import { $config } from '@dequanto/utils/$config';
|
|
9
|
+
import type { HardhatWeb3Client } from '@dequanto/hardhat/HardhatWeb3Client';
|
|
9
10
|
|
|
10
11
|
export namespace Web3ClientFactory {
|
|
11
12
|
|
|
12
|
-
export function get
|
|
13
|
+
export function get <
|
|
14
|
+
TClient extends EvmWeb3Client | HardhatWeb3Client = EvmWeb3Client
|
|
15
|
+
> (platform: TPlatform | number, opts?: IWeb3EndpointOptions): TClient {
|
|
13
16
|
if (typeof platform === 'string' && (platform === 'hardhat' || platform.startsWith('hh:'))) {
|
|
14
17
|
let client = di.resolve(HardhatProvider).client('localhost', opts);
|
|
15
18
|
if (platform.startsWith('hh:')) {
|
|
16
19
|
client.configureFork(platform.slice(3));
|
|
17
20
|
}
|
|
18
|
-
return client;
|
|
21
|
+
return client as TClient;
|
|
19
22
|
}
|
|
20
23
|
|
|
21
24
|
let options = $config.getWeb3Options(platform);
|
|
@@ -23,12 +26,14 @@ export namespace Web3ClientFactory {
|
|
|
23
26
|
return new EvmWeb3Client({
|
|
24
27
|
...options,
|
|
25
28
|
...(opts ?? {})
|
|
26
|
-
});
|
|
29
|
+
}) as TClient;
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
/** Same as the sync variant, but ensures the config is fetched */
|
|
30
|
-
export async function getAsync
|
|
33
|
+
export async function getAsync <
|
|
34
|
+
TClient extends EvmWeb3Client | HardhatWeb3Client = EvmWeb3Client
|
|
35
|
+
> (platform: TPlatform | string | number, opts?: IWeb3EndpointOptions): Promise<TClient> {
|
|
31
36
|
let cfg = await Config.get();
|
|
32
|
-
return get(platform, opts);
|
|
37
|
+
return get<TClient>(platform, opts);
|
|
33
38
|
}
|
|
34
39
|
}
|
|
@@ -9,7 +9,7 @@ export namespace FnRequestWrapper {
|
|
|
9
9
|
|
|
10
10
|
type TRequests<T extends ContractBase> = {
|
|
11
11
|
[K in keyof T]: T[K] extends (...any) => Promise<any>
|
|
12
|
-
? (...params: Parameters<T[K]>) => ContractReaderUtils.IContractReadParams<Awaited<ReturnType<T[K]
|
|
12
|
+
? (...params: Parameters<T[K]>) => Promise<ContractReaderUtils.IContractReadParams<Awaited<ReturnType<T[K]>>>>
|
|
13
13
|
: never
|
|
14
14
|
}
|
|
15
15
|
|
package/src/models/TAccount.ts
CHANGED
|
@@ -65,7 +65,7 @@ export interface TimelockAccount extends IAccount {
|
|
|
65
65
|
// Erc4337 account must include platform information
|
|
66
66
|
platform: TPlatform
|
|
67
67
|
|
|
68
|
-
operator:
|
|
68
|
+
operator: IAccount
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
export type TAccount = string | IAccount | EoAccount | SafeAccount | Erc4337Account;
|
package/src/utils/$abiParser.ts
CHANGED
|
@@ -86,6 +86,69 @@ export namespace $abiParser {
|
|
|
86
86
|
};
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Parses Solidity struct declarations into a tuple ABI component.
|
|
92
|
+
* Supports multiple declarations so later structs can reference earlier ones,
|
|
93
|
+
* and returns the ABI component for the last struct in the input.
|
|
94
|
+
*/
|
|
95
|
+
export function parseStruct (struct: string): TAbiItem {
|
|
96
|
+
let structs = Parse.structs(struct);
|
|
97
|
+
$require.gt(structs.length, 0, `No structs found in ${struct}`);
|
|
98
|
+
|
|
99
|
+
let byName = new Map(structs.map(x => [ x.name, x ]));
|
|
100
|
+
let cache = new Map<string, TAbiInput>();
|
|
101
|
+
|
|
102
|
+
function toTuple (name: string): TAbiInput {
|
|
103
|
+
let cached = cache.get(name);
|
|
104
|
+
if (cached != null) {
|
|
105
|
+
return cached;
|
|
106
|
+
}
|
|
107
|
+
let def = byName.get(name);
|
|
108
|
+
$require.notNull(def, `Struct ${name} is not found`);
|
|
109
|
+
|
|
110
|
+
let tuple: TAbiInput = {
|
|
111
|
+
name: '',
|
|
112
|
+
type: 'tuple',
|
|
113
|
+
internalType: `struct ${name}`,
|
|
114
|
+
components: []
|
|
115
|
+
};
|
|
116
|
+
cache.set(name, tuple);
|
|
117
|
+
tuple.components = Parse.splitByDelimiter(def.body, ';')
|
|
118
|
+
.map(field => $abiParser.parseArguments(field)[0])
|
|
119
|
+
.map(resolveStructs);
|
|
120
|
+
|
|
121
|
+
return tuple;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function resolveStructs (param: TAbiInput): TAbiInput {
|
|
125
|
+
let { type, array } = Parse.extractArray(param.type);
|
|
126
|
+
if (byName.has(type) === false) {
|
|
127
|
+
if (param.components != null) {
|
|
128
|
+
return {
|
|
129
|
+
...param,
|
|
130
|
+
components: param.components.map(resolveStructs)
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
return param;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
let tuple = toTuple(type);
|
|
137
|
+
return {
|
|
138
|
+
name: param.name,
|
|
139
|
+
type: `tuple${array}`,
|
|
140
|
+
internalType: `struct ${type}${array}`,
|
|
141
|
+
components: tuple.components
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
let last = structs[structs.length - 1];
|
|
146
|
+
return {
|
|
147
|
+
...toTuple(last.name),
|
|
148
|
+
name: last.name
|
|
149
|
+
} as any as TAbiItem;
|
|
150
|
+
}
|
|
151
|
+
|
|
89
152
|
// uint256
|
|
90
153
|
// address[]
|
|
91
154
|
// (uint256, uint256)
|
|
@@ -247,6 +310,35 @@ namespace Parse {
|
|
|
247
310
|
|
|
248
311
|
const CLOSE_CHARS = {
|
|
249
312
|
'[': ']',
|
|
250
|
-
'(': ')'
|
|
313
|
+
'(': ')',
|
|
314
|
+
'{': '}'
|
|
251
315
|
};
|
|
316
|
+
|
|
317
|
+
export function structs (source: string) {
|
|
318
|
+
let rgx = /\bstruct\s+(?<name>[\w_$]+)\s*\{/g;
|
|
319
|
+
let arr: { name: string, body: string }[] = [];
|
|
320
|
+
let match: RegExpExecArray;
|
|
321
|
+
while ((match = rgx.exec(source)) != null) {
|
|
322
|
+
let openI = source.indexOf('{', match.index);
|
|
323
|
+
let closeI = goToClosing(source, openI, '{');
|
|
324
|
+
arr.push({
|
|
325
|
+
name: match.groups.name,
|
|
326
|
+
body: source.substring(openI + 1, closeI).trim()
|
|
327
|
+
});
|
|
328
|
+
rgx.lastIndex = closeI + 1;
|
|
329
|
+
}
|
|
330
|
+
return arr;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export function extractArray (type: string) {
|
|
334
|
+
let array = '';
|
|
335
|
+
while (true) {
|
|
336
|
+
let match = /^(?<type>.+?)(?<array>\[[^\]]*\])$/.exec(type);
|
|
337
|
+
if (match == null) {
|
|
338
|
+
return { type, array };
|
|
339
|
+
}
|
|
340
|
+
type = match.groups.type;
|
|
341
|
+
array = `${match.groups.array}${array}`;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
252
344
|
}
|