dequanto 0.2.31 → 0.2.33

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.
@@ -60,7 +60,7 @@ export abstract class ContractBase {
60
60
 
61
61
  storage?: ContractStorageReaderBase
62
62
 
63
- constructor (
63
+ constructor(
64
64
  public address: TAddress,
65
65
  public client: Web3Client,
66
66
  public explorer: IBlockchainExplorer
@@ -77,28 +77,28 @@ export abstract class ContractBase {
77
77
  }
78
78
  }
79
79
 
80
- public async $getStorageAt (position: number | bigint | TEth.Hex) {
80
+ public async $getStorageAt(position: number | bigint | TEth.Hex) {
81
81
  let reader = await this.getContractReader();
82
82
  return reader.getStorageAt(this.address, position);
83
83
  }
84
- public $parseInputData (buffer: TEth.BufferLike, value?: string) {
84
+ public $parseInputData(buffer: TEth.BufferLike, value?: string) {
85
85
  return $abiUtils.parseMethodCallData(this.abi, buffer);
86
86
  }
87
- public async $executeBatch <T extends readonly unknown[] | ContractReaderUtils.IContractReadParams[]>(values: T): Promise<
87
+ public async $executeBatch<T extends readonly unknown[] | ContractReaderUtils.IContractReadParams[]>(values: T): Promise<
88
88
  { -readonly [P in keyof T]: ContractReaderUtils.TIContractReadParamsInferred<T[P]>; }
89
89
  > {
90
90
  let reader = await this.getContractReader();
91
91
  return reader.executeBatch(values);
92
92
  }
93
93
 
94
- async getPastLogs <TEvents extends TEventsBase, TEventName extends keyof TEvents> (
94
+ async getPastLogs<TEvents extends TEventsBase, TEventName extends keyof TEvents>(
95
95
  event: TEventName | TEventName[]
96
96
  , options?: TEventLogOptions<TEventParams<TEvents, TEventName>>
97
97
  ): Promise<ITxLogItem<TEventParams<TEvents, TEventName>, TEventName>[]> {
98
98
  return await this.$getPastLogsParsed(event as string | string[], options) as any;
99
99
  }
100
100
 
101
- public $config (builderConfig?: ITxBuilderOptions, writerConfig?: ITxWriterOptions): this {
101
+ public $config(builderConfig?: ITxBuilderOptions, writerConfig?: ITxWriterOptions): this {
102
102
  let $contract = $class.curry(this, {
103
103
  builderConfig: {
104
104
  ...(this.builderConfig ?? {}),
@@ -119,7 +119,7 @@ export abstract class ContractBase {
119
119
  }
120
120
 
121
121
  @memd.deco.memoize({ perInstance: true })
122
- public $call () {
122
+ public $call() {
123
123
  let abiArr = this.abi;
124
124
  let writer = this.getContractWriter();
125
125
 
@@ -127,7 +127,7 @@ export abstract class ContractBase {
127
127
  let fns = alot(methods).map(abiMethod => {
128
128
  return {
129
129
  name: abiMethod.name,
130
- async fn (sender: TAccount,...args: any[]) {
130
+ async fn(sender: TAccount, ...args: any[]) {
131
131
  return ContractBaseUtils.$call(
132
132
  writer,
133
133
  abiMethod,
@@ -146,7 +146,7 @@ export abstract class ContractBase {
146
146
  }
147
147
 
148
148
  @memd.deco.memoize({ perInstance: true })
149
- public $data (params?: {
149
+ public $data(params?: {
150
150
  estimateGas?: boolean
151
151
  getNonce?: boolean
152
152
  from?: TAddress
@@ -160,14 +160,14 @@ export abstract class ContractBase {
160
160
  let writeFns = alot(writeMethods).map(method => {
161
161
  return {
162
162
  name: method.name,
163
- async fn (sender: TAccount,...args: any[]) {
163
+ async fn(sender: TAccount, ...args: any[]) {
164
164
  let writer: TxWriter = await $top
165
165
  .$config({
166
166
  send: 'manual',
167
167
  gasEstimation: false,
168
168
  nonce: params?.getNonce ? void 0 : 0,
169
169
  })
170
- [method.name](sender, ...args);
170
+ [method.name](sender, ...args);
171
171
 
172
172
  if (params?.getNonce) {
173
173
  await writer.builder.ensureNonce();
@@ -192,7 +192,7 @@ export abstract class ContractBase {
192
192
  let readFns = alot(readMethods).map(method => {
193
193
  return {
194
194
  name: method.name,
195
- async fn (...args: any[]) {
195
+ async fn(...args: any[]) {
196
196
  return {
197
197
  to: $top.address,
198
198
  data: $abiUtils.serializeMethodCallData(method, args)
@@ -209,7 +209,7 @@ export abstract class ContractBase {
209
209
  }
210
210
 
211
211
  @memd.deco.memoize({ perInstance: true })
212
- public $gas () {
212
+ public $gas() {
213
213
  let abiArr = this.abi;
214
214
  let writer = this.getContractWriter();
215
215
 
@@ -217,7 +217,7 @@ export abstract class ContractBase {
217
217
  let fns = alot(methods).map(abiMethod => {
218
218
  return {
219
219
  name: abiMethod.name,
220
- async fn (sender: IAccount,...args: any[]) {
220
+ async fn(sender: IAccount, ...args: any[]) {
221
221
  return ContractBaseUtils.$gas(
222
222
  writer,
223
223
  abiMethod,
@@ -235,11 +235,11 @@ export abstract class ContractBase {
235
235
  return $contract as any;
236
236
  }
237
237
 
238
- public $req () {
238
+ public $req() {
239
239
  return FnRequestWrapper.create(this);
240
240
  }
241
241
 
242
- public $signed (builderConfig?: ITxBuilderOptions, writerConfig?: ITxWriterOptions) {
242
+ public $signed(builderConfig?: ITxBuilderOptions, writerConfig?: ITxWriterOptions) {
243
243
  let instance = this.$signedCreate();
244
244
  if (builderConfig != null || writerConfig != null) {
245
245
  instance = instance.$config(builderConfig, writerConfig);
@@ -248,18 +248,18 @@ export abstract class ContractBase {
248
248
  }
249
249
 
250
250
  @memd.deco.memoize({ perInstance: true })
251
- private $signedCreate () {
251
+ private $signedCreate() {
252
252
  return FnSignedWrapper.create(this);
253
253
  }
254
254
 
255
255
  @memd.deco.memoize({ perInstance: true })
256
- public $receipt <T extends this> (this: T): T {
256
+ public $receipt<T extends this>(this: T): T {
257
257
  let $top = this;
258
258
  let methods = this.abi.filter(abi => abi.type === 'function' && $abiUtils.isReadMethod(abi) === false);
259
259
  let fns = alot(methods).map(abiMethod => {
260
260
  return {
261
261
  name: abiMethod.name,
262
- async fn (sender: IAccount,...args: any[]) {
262
+ async fn(sender: IAccount, ...args: any[]) {
263
263
  let tx: TxWriter = await $top[abiMethod.name](sender, ...args);
264
264
  let receipt = await tx.wait();
265
265
  return tx;
@@ -273,7 +273,7 @@ export abstract class ContractBase {
273
273
  return $contract as any;
274
274
  }
275
275
 
276
- public forBlock (mix: number | bigint | undefined | Date): this {
276
+ public forBlock(mix: number | bigint | undefined | Date): this {
277
277
  if (mix == null) {
278
278
  return this;
279
279
  }
@@ -282,14 +282,14 @@ export abstract class ContractBase {
282
282
  }
283
283
  return this.forBlockAt(mix);
284
284
  }
285
- protected forBlockNumber (blockNumber: number | bigint | undefined): this {
285
+ protected forBlockNumber(blockNumber: number | bigint | undefined): this {
286
286
  let $contract = $class.curry(this, {
287
287
  blockNumber: blockNumber,
288
288
  blockDate: null
289
289
  })
290
290
  return $contract;
291
291
  }
292
- protected forBlockAt (date: Date | undefined): this {
292
+ protected forBlockAt(date: Date | undefined): this {
293
293
  let $contract = $class.curry(this, {
294
294
  blockNumber: null,
295
295
  blockDate: date
@@ -297,7 +297,7 @@ export abstract class ContractBase {
297
297
  return $contract;
298
298
  }
299
299
 
300
- protected $read (abi: string | TAbiItem, ...params) {
300
+ protected $read(abi: string | TAbiItem, ...params) {
301
301
  if (this.builderConfig?.send === 'manual') {
302
302
  let req = <ContractReaderUtils.IContractReadParams>{
303
303
  address: this.address,
@@ -314,7 +314,7 @@ export abstract class ContractBase {
314
314
  return reader.readAsync(this.address, abi, ...params);
315
315
  }
316
316
 
317
- public $onLog (event: string, cb?) {
317
+ public $onLog(event: string, cb?) {
318
318
  let stream = this.getContractStream();
319
319
  let events = stream.on(event);
320
320
  if (cb) {
@@ -323,12 +323,12 @@ export abstract class ContractBase {
323
323
  return events;
324
324
  }
325
325
 
326
- public $onTransaction (options?: BlockWalker.IBlockWalkerOptions): SubjectStream<{ tx: TEth.Tx, block: TEth.Block, calldata: { method, arguments: any[] } }> {
326
+ public $onTransaction(options?: BlockWalker.IBlockWalkerOptions): SubjectStream<{ tx: TEth.Tx, block: TEth.Block, calldata: { method, arguments: any[] } }> {
327
327
 
328
328
  options ??= {};
329
329
  options.logProgress ??= false;
330
330
 
331
- type TSubject = {
331
+ type TSubject = {
332
332
  tx: TEth.Tx,
333
333
  block: TEth.Block,
334
334
  calldata: { method, arguments: any[] }
@@ -375,7 +375,7 @@ export abstract class ContractBase {
375
375
  return stream;
376
376
  }
377
377
 
378
- protected async $write (abi: string | TAbiItem, account: TAccount & { value?: number | string | bigint }, ...params): Promise<TxWriter> {
378
+ protected async $write(abi: string | TAbiItem, account: TAccount & { value?: number | string | bigint }, ...params): Promise<TxWriter> {
379
379
  let writer = await this.getContractWriter();
380
380
  return writer.writeAsync(account, abi, params, {
381
381
  abi: this.abi,
@@ -384,20 +384,20 @@ export abstract class ContractBase {
384
384
  });
385
385
  }
386
386
 
387
- protected $getAbiItem (type: 'event' | 'function' | 'string', name: string, argsCount?: number) {
387
+ protected $getAbiItem(type: 'event' | 'function' | 'string', name: string, argsCount?: number) {
388
388
  return ContractBaseUtils.$getAbiItem(this.abi, type, name, argsCount);
389
389
  }
390
390
 
391
- protected $getAbiItemOverload (fnName: string, args: any[])
392
- protected $getAbiItemOverload (abis: (string | TAbiItem)[], args: any[])
393
- protected $getAbiItemOverload (mix: string | (string | TAbiItem)[], args: any[]) {
391
+ protected $getAbiItemOverload(fnName: string, args: any[])
392
+ protected $getAbiItemOverload(abis: (string | TAbiItem)[], args: any[])
393
+ protected $getAbiItemOverload(mix: string | (string | TAbiItem)[], args: any[]) {
394
394
  let abis = typeof mix === 'string'
395
- ? this.abi.filter(x => x.type === 'function' && x.name === mix)
396
- : mix;
395
+ ? this.abi.filter(x => x.type === 'function' && x.name === mix)
396
+ : mix;
397
397
 
398
398
  let $abis = abis
399
399
  .map(methodAbi => {
400
- if (typeof methodAbi ==='string') {
400
+ if (typeof methodAbi === 'string') {
401
401
  return $abiParser.parseMethod(methodAbi);
402
402
  }
403
403
  return methodAbi;
@@ -410,43 +410,30 @@ export abstract class ContractBase {
410
410
  if ($abis.length === 1) {
411
411
  return $abis[0];
412
412
  }
413
- $abis = $abis.filter(abi => {
414
- for (let i = 0; i < args.length; i++) {
415
- let item = abi.inputs[i];
416
- let arg = args[i];
417
- if (item.type === 'address' && $address.isValid(arg)) {
418
- continue;
419
- }
420
- if (item.type === 'bool' && typeof arg === 'boolean') {
421
- continue;
422
- }
423
- if (item.type === 'string' && typeof arg === 'string') {
424
- continue;
425
- }
426
- if (/^u?int/.test(item.type) && (typeof arg === 'number' || typeof arg === 'bigint')) {
427
- continue;
428
- }
429
- if (/tuple/.test(item.type) && (typeof arg === 'object')) {
430
- continue;
431
- }
432
- return false;
433
- }
434
- return true;
435
- });
413
+ $abis = filterABIbyArguments($abis, args, 'shallow');
414
+ if ($abis.length === 1) {
415
+ return $abis[0];
416
+ }
417
+ $abis = filterABIbyArguments($abis, args, 'single');
436
418
  if ($abis.length === 1) {
437
419
  return $abis[0];
438
420
  }
421
+ $abis = filterABIbyArguments($abis, args, 'both');
422
+ if ($abis.length === 1) {
423
+ return $abis[0];
424
+ }
425
+
439
426
  throw new Error(`ABI not found. Got multiple overloads for the argument count ${args.length}. We should pick the ABI by parameters type.`)
440
427
  }
441
428
 
442
- protected $extractLogs (tx: TEth.TxReceipt, abiItem: TAbiItem) {
429
+ protected $extractLogs(tx: TEth.TxReceipt, abiItem: TAbiItem) {
443
430
  let logs = $contract.extractLogsForAbi(tx, abiItem);
444
431
  return logs;
445
432
  }
446
- protected $extractLog (log: TEth.Log, mix: string | string[] | TAbiItem | TAbiItem[] | '*') {
433
+ protected $extractLog(log: TEth.Log, mix: string | string[] | TAbiItem | TAbiItem[] | '*') {
447
434
  let abi: TAbiItem | TAbiItem[];
448
435
 
449
- let mixArr = typeof mix === 'string' ? [ mix ] : ((mix as TAbiItem[] | string[]) ?? []);
436
+ let mixArr = typeof mix === 'string' ? [mix] : ((mix as TAbiItem[] | string[]) ?? []);
450
437
  if (mixArr.length === 0 || (mixArr.length === 1 && mixArr[0] === '*')) {
451
438
  abi = this.abi;
452
439
  } else {
@@ -459,18 +446,18 @@ export abstract class ContractBase {
459
446
  protected async $getPastLogs(filters: RpcTypes.Filter, options?: {
460
447
  streamed?: boolean
461
448
  blockRangeLimits?: WClient['blockRangeLimits']
462
- onProgress? (info: TLogsRangeProgress<TEth.Log>)
449
+ onProgress?(info: TLogsRangeProgress<TEth.Log>)
463
450
  }) {
464
451
  return this.getContractReader().getLogs(filters, options);
465
452
  }
466
- public async $getPastLogsParsed (mix: string | TAbiItem | string[] | TAbiItem[], options?: {
453
+ public async $getPastLogsParsed(mix: string | TAbiItem | string[] | TAbiItem[], options?: {
467
454
  addresses?: TAddress[]
468
455
  fromBlock?: number | Date
469
456
  toBlock?: number | Date
470
457
  params?: {
471
458
  [key: string]: any
472
459
  }
473
- onProgress? (info: TLogsRangeProgress<ITxLogItem>)
460
+ onProgress?(info: TLogsRangeProgress<ITxLogItem>)
474
461
 
475
462
  /** if TRUE the data will be only forwarded via onProgress callback.
476
463
  * And the final array will be undefined.
@@ -516,7 +503,7 @@ export abstract class ContractBase {
516
503
  abi = this.$getAbiItem('event', mix);
517
504
  } else if (Array.isArray(mix) === false) {
518
505
  abi = mix;
519
- } else if (mix.length === 1 && typeof mix[0] ==='string' && mix[0] === '*') {
506
+ } else if (mix.length === 1 && typeof mix[0] === 'string' && mix[0] === '*') {
520
507
  abi = '*';
521
508
  } else {
522
509
  abi = mix.map(x => {
@@ -535,7 +522,7 @@ export abstract class ContractBase {
535
522
  );
536
523
  }
537
524
 
538
- private getContractReader () {
525
+ private getContractReader() {
539
526
  let reader = this.getContractReaderInner();
540
527
  if (this.blockDate != null) {
541
528
  reader.forBlockAt(this.blockDate);
@@ -551,13 +538,13 @@ export abstract class ContractBase {
551
538
  }
552
539
 
553
540
  @memd.deco.memoize({ perInstance: true })
554
- private getContractReaderInner () {
541
+ private getContractReaderInner() {
555
542
  let reader = new ContractReader(this.client, { name: this.constructor.name });
556
543
  return reader;
557
544
  }
558
545
 
559
546
  @memd.deco.memoize({ perInstance: true })
560
- protected getContractWriter () {
547
+ protected getContractWriter() {
561
548
  if (this.abi != null) {
562
549
  // Updates the singleton instance
563
550
  let logParser = di.resolve(TxTopicInMemoryProvider);
@@ -568,7 +555,7 @@ export abstract class ContractBase {
568
555
  }
569
556
 
570
557
  @memd.deco.memoize({ perInstance: true })
571
- private getContractStream () {
558
+ private getContractStream() {
572
559
  let stream = new ContractStream(this.address, this.abi, this.client);
573
560
  return stream;
574
561
  }
@@ -587,9 +574,9 @@ namespace BlockWalker {
587
574
  }
588
575
  }
589
576
 
590
- const indexers = {} as { [key: string]: BlocksTxIndexer } ;
577
+ const indexers = {} as { [key: string]: BlocksTxIndexer };
591
578
 
592
- export function onBlock (client: Web3Client, options: IBlockWalkerOptions, cb: TBlockListener ) {
579
+ export function onBlock(client: Web3Client, options: IBlockWalkerOptions, cb: TBlockListener) {
593
580
 
594
581
  let key = `${client.platform}_${options?.name ?? ''}_${options?.persistence ?? false}`;
595
582
  let current = indexers[key];
@@ -621,7 +608,7 @@ export type TContractTypes = {
621
608
  type TEventsBase = {
622
609
  [name: string]: {
623
610
  outputParams: Record<string, any>,
624
- outputArgs: any[],
611
+ outputArgs: any[],
625
612
  }
626
613
  }
627
614
 
@@ -631,8 +618,71 @@ export type TEventLogOptions<TParams> = {
631
618
  toBlock?: number | Date
632
619
  params?: TParams
633
620
  streamed?: boolean
634
- onProgress? (info: TLogsRangeProgress<ITxLogItem>)
621
+ onProgress?(info: TLogsRangeProgress<ITxLogItem>)
635
622
  blockRangeLimits?: WClient['blockRangeLimits']
636
623
  }
637
624
 
638
625
  type TEventParams<TEvents extends TEventsBase, TEventName extends keyof TEvents> = Partial<TEvents[TEventName]['outputParams']>;
626
+
627
+
628
+ // Tuple check type detects how an argument satisfies the "tuple"
629
+ // shallow: checks only that the argument is an object type
630
+ // single: checks that all tuple components (keys) are present in the argument
631
+ // both: checks that all tuple components are present in the argument AND all argument keys exist in the tuple components
632
+ function filterABIbyArguments(abis: TAbiItem[], args: any[], tupleCheck: 'shallow' | 'single' | 'both') {
633
+ return abis.filter(abi => {
634
+ for (let i = 0; i < args.length; i++) {
635
+ let item = abi.inputs[i];
636
+ let arg = args[i];
637
+ if (item.type === 'address' && $address.isValid(arg)) {
638
+ continue;
639
+ }
640
+ if (item.type === 'bool' && typeof arg === 'boolean') {
641
+ continue;
642
+ }
643
+ if (item.type === 'string' && typeof arg === 'string') {
644
+ continue;
645
+ }
646
+ if (/^u?int/.test(item.type) && (typeof arg === 'number' || typeof arg === 'bigint')) {
647
+ continue;
648
+ }
649
+ if (/tuple/.test(item.type)) {
650
+ arg ??= {};
651
+ if (tupleCheck === 'shallow') {
652
+ if (typeof arg === 'object') {
653
+ continue;
654
+ }
655
+ }
656
+ if (tupleCheck === 'single' || tupleCheck === 'both') {
657
+ // E.g. Solidity structs:
658
+ // 1. struct FooV1 { uint256 a; uint256 b; }
659
+ // 2. struct FooV2 { uint256 a; uint256 b; uint256 c; }
660
+ // If we call the 2. overloaded function the single check will match both structs
661
+ // therefore we need later to call with tupleCheck === 'both' to detect correct overload
662
+ const components = item.components ?? [];
663
+ const satisfies = components.every(x => x.name in arg) ?? false;
664
+ if (!satisfies) {
665
+ return false;
666
+ }
667
+
668
+ if (tupleCheck === 'single') {
669
+ continue;
670
+ }
671
+ }
672
+ if (tupleCheck === 'both') {
673
+ const components = item.components ?? [];
674
+ for (let key in arg) {
675
+ const found = components.find(x => x.name === key);
676
+ if (!found) {
677
+ return false;
678
+ }
679
+ }
680
+ continue;
681
+ }
682
+
683
+ }
684
+ return false;
685
+ }
686
+ return true;
687
+ });
688
+ }
@@ -85,6 +85,7 @@ export abstract class RpcBase {
85
85
  let body = this._wrapBody(req);
86
86
  let subscription = await this._transport.subscribe<TReturn>(body);
87
87
 
88
+
88
89
  let mapped = RpcSubscription.createMapping(subscription, this._transport, x => this._deserialize(`${req.method}.${req.params[0]}`, x))
89
90
  return mapped as RpcSubscription<TReturn>;
90
91
  //return subscription.map(x => this._deserialize(`${req.method}.${req.params[0]}`, x));
@@ -54,9 +54,16 @@ export class SafeTx {
54
54
  });
55
55
 
56
56
  let transactionsHex = $hex.concat(transactionsHexArr);
57
+ let isCallOnly = txData.every(tx => !tx.operation /** 0: call, 1: delegatecall */);
58
+ let getAddress = (key: 'MultiSend' | 'MultiSendCallOnly') => {
59
+ return this.options?.contracts?.[this.client.network]?.[key]
60
+ ?? config.safe?.contracts?.[this.client.network]?.[key];
61
+ }
62
+
63
+ const multiSendAddress = isCallOnly
64
+ ? (getAddress('MultiSendCallOnly') ?? getAddress('MultiSend'))
65
+ : (getAddress('MultiSend'))
57
66
 
58
- const multiSendAddress = this.options?.contracts?.[this.client.network]?.MultiSend
59
- ?? config.safe?.contracts?.[this.client.network]?.MultiSend;
60
67
 
61
68
  $require.Address(multiSendAddress, `MultiSend contract for ${this.client.network}`);
62
69
  const safeMultiSendContract = new MultiSend(multiSendAddress, this.client);
@@ -6,6 +6,7 @@ import { $bigint } from './$bigint';
6
6
  import { $hex } from '@dequanto/utils/$hex';
7
7
  import { $color, ColorData } from './$color';
8
8
  import alot from 'alot';
9
+ import { $contract } from './$contract';
9
10
 
10
11
  export namespace $traces {
11
12
 
@@ -335,10 +336,11 @@ export namespace $traces {
335
336
  }
336
337
 
337
338
  function formatFrameLabel(frame: TTraceFrame, contractName: string | null, abi: TEth.Abi.Item | null, params: {
338
- shortAddress?: boolean
339
- color?: boolean
340
- addresses?: Record<string, string>
341
- }): string {
339
+ shortAddress?: boolean
340
+ color?: boolean
341
+ addresses?: Record<string, string>
342
+ }
343
+ ): string {
342
344
  let head = color('gray', frame.callOp.toLowerCase(), params);
343
345
  let suffix = '';
344
346
 
@@ -513,6 +515,13 @@ export namespace $traces {
513
515
  // Fall back to raw bytes if decoding fails.
514
516
  }
515
517
  }
518
+ if (frame.failed) {
519
+ let abis = $contract.store.getFlattened();
520
+ let error = $contract.decodeCustomError(frame.output, abis);
521
+ if (error) {
522
+ return JSON.stringify(error);
523
+ }
524
+ }
516
525
  return shortHex(frame.output, 18, 10);
517
526
  }
518
527