dequanto 0.1.81 → 0.1.85

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.
Files changed (61) hide show
  1. package/lib/cjs/blocks/BlockDateResolver.js +59 -23
  2. package/lib/cjs/blocks/BlockDateResolver.js.map +1 -1
  3. package/lib/cjs/contracts/deploy/proxy/$proxyDeploy.js +5 -1
  4. package/lib/cjs/contracts/deploy/proxy/$proxyDeploy.js.map +1 -1
  5. package/lib/cjs/erc4337/Erc4337Factory.js +0 -1
  6. package/lib/cjs/erc4337/Erc4337Factory.js.map +1 -1
  7. package/lib/cjs/prebuilt-factories/TimelockControllerFactory.js +251 -0
  8. package/lib/cjs/prebuilt-factories/TimelockControllerFactory.js.map +1 -0
  9. package/lib/cjs/services/TimelockService/TimelockService.js +24 -4
  10. package/lib/cjs/services/TimelockService/TimelockService.js.map +1 -1
  11. package/lib/cjs/txs/TxWriter.js +1 -1
  12. package/lib/cjs/txs/TxWriter.js.map +1 -1
  13. package/lib/cjs/txs/agents/TimelockAgent.js +90 -0
  14. package/lib/cjs/txs/agents/TimelockAgent.js.map +1 -0
  15. package/lib/cjs/txs/agents/TxWriterAccountAgents.js +2 -0
  16. package/lib/cjs/txs/agents/TxWriterAccountAgents.js.map +1 -1
  17. package/lib/cjs/utils/$account.js +17 -1
  18. package/lib/cjs/utils/$account.js.map +1 -1
  19. package/lib/cjs/utils/$date.js +11 -0
  20. package/lib/cjs/utils/$date.js.map +1 -1
  21. package/lib/esm/blocks/BlockDateResolver.js.map +1 -1
  22. package/lib/esm/blocks/BlockDateResolver.mjs +59 -23
  23. package/lib/esm/contracts/deploy/proxy/$proxyDeploy.js.map +1 -1
  24. package/lib/esm/contracts/deploy/proxy/$proxyDeploy.mjs +5 -1
  25. package/lib/esm/erc4337/Erc4337Factory.js.map +1 -1
  26. package/lib/esm/erc4337/Erc4337Factory.mjs +0 -1
  27. package/lib/esm/prebuilt-factories/TimelockControllerFactory.js.map +1 -0
  28. package/lib/esm/prebuilt-factories/TimelockControllerFactory.mjs +244 -0
  29. package/lib/esm/services/TimelockService/TimelockService.js.map +1 -1
  30. package/lib/esm/services/TimelockService/TimelockService.mjs +24 -4
  31. package/lib/esm/txs/TxWriter.js.map +1 -1
  32. package/lib/esm/txs/TxWriter.mjs +1 -1
  33. package/lib/esm/txs/agents/TimelockAgent.js.map +1 -0
  34. package/lib/esm/txs/agents/TimelockAgent.mjs +85 -0
  35. package/lib/esm/txs/agents/TxWriterAccountAgents.js.map +1 -1
  36. package/lib/esm/txs/agents/TxWriterAccountAgents.mjs +2 -0
  37. package/lib/esm/utils/$account.js.map +1 -1
  38. package/lib/esm/utils/$account.mjs +17 -1
  39. package/lib/esm/utils/$date.js.map +1 -1
  40. package/lib/esm/utils/$date.mjs +11 -0
  41. package/lib/types/blocks/BlockDateResolver.d.ts +7 -5
  42. package/lib/types/models/TAccount.d.ts +7 -1
  43. package/lib/types/prebuilt-factories/TimelockControllerFactory.d.ts +188 -0
  44. package/lib/types/services/TimelockService/TimelockService.d.ts +14 -3
  45. package/lib/types/txs/TxWriter.d.ts +20 -13
  46. package/lib/types/txs/agents/TimelockAgent.d.ts +26 -0
  47. package/lib/types/txs/agents/TxWriterAccountAgents.d.ts +2 -2
  48. package/lib/types/utils/$account.d.ts +2 -1
  49. package/lib/types/utils/$date.d.ts +1 -0
  50. package/package.json +1 -1
  51. package/src/blocks/BlockDateResolver.ts +61 -28
  52. package/src/contracts/deploy/proxy/$proxyDeploy.ts +5 -1
  53. package/src/erc4337/Erc4337Factory.ts +0 -2
  54. package/src/models/TAccount.ts +11 -1
  55. package/src/prebuilt-factories/TimelockControllerFactory.ts +582 -0
  56. package/src/services/TimelockService/TimelockService.ts +48 -8
  57. package/src/txs/TxWriter.ts +29 -17
  58. package/src/txs/agents/TimelockAgent.ts +116 -0
  59. package/src/txs/agents/TxWriterAccountAgents.ts +4 -2
  60. package/src/utils/$account.ts +18 -2
  61. package/src/utils/$date.ts +11 -1
@@ -1,6 +1,4 @@
1
1
  import memd from 'memd';
2
- import { TimelockController } from '@dequanto/prebuilt/openzeppelin/TimelockController';
3
- import { ContractBase } from '@dequanto/contracts/ContractBase';
4
2
  import { JsonArrayStore } from '@dequanto/json/JsonArrayStore';
5
3
  import { IAccount } from '@dequanto/models/TAccount';
6
4
  import { TAddress } from '@dequanto/models/TAddress';
@@ -19,10 +17,25 @@ import { File } from 'atma-io';
19
17
  import { ITimelockTx, ITimelockTxParamsNormalized, ITimelockTxParams, ITimelockService, ETimelockTxStatus } from './ITimelockService';
20
18
  import { TTxWriteMethodKeys } from '@dequanto/utils/types';
21
19
 
20
+ import type { TimelockController } from '@dequanto/prebuilt/openzeppelin/TimelockController';
21
+ import type { ContractBase } from '@dequanto/contracts/ContractBase';
22
+
23
+ import { TxDataBuilder } from '@dequanto/txs/TxDataBuilder';
24
+
25
+ const CONFIG = {
26
+ dir: `0x/data`
27
+ };
22
28
 
23
29
 
24
30
  type TTimelockController = ContractBase & Pick<TimelockController,
25
- 'schedule' | 'scheduleBatch' | 'execute' | 'executeBatch' | 'getMinDelay' | 'cancel'
31
+ 'schedule'
32
+ | 'scheduleBatch'
33
+ | 'execute'
34
+ | 'executeBatch'
35
+ | 'getMinDelay'
36
+ | 'cancel'
37
+ | 'extractLogsCallScheduled'
38
+ | 'extractLogsCallSalt'
26
39
  >;
27
40
 
28
41
  export class TimelockService implements ITimelockService {
@@ -37,7 +50,7 @@ export class TimelockService implements ITimelockService {
37
50
  // Only for hardhat client. Default: false
38
51
  execute?: boolean
39
52
 
40
- // Directory to store the submitted schedules, default: `./0x/data/`
53
+ // Directory to store the submitted schedules, default: `./0x/data/`, or use `TimelockService.config` to override it globally
41
54
  dir?: string
42
55
  }
43
56
  ) {
@@ -64,7 +77,7 @@ export class TimelockService implements ITimelockService {
64
77
  let receipt = await this.timelock.client.getTransactionReceipt(txHash);
65
78
  $require.notNull(receipt, `Tx ${txHash} not found`);
66
79
  let block = await this.timelock.client.getBlock(receipt.blockNumber);
67
- let contract = new TimelockController(void 0, this.timelock.client);
80
+ let contract = this.timelock;
68
81
  let callScheduled = contract.extractLogsCallScheduled(receipt);
69
82
  $require.gte(callScheduled.length, 1, `Expected at least 1 CallScheduled log`);
70
83
 
@@ -139,11 +152,32 @@ export class TimelockService implements ITimelockService {
139
152
  schedule: ITimelockTx
140
153
  tx?: TEth.Hex
141
154
  }> {
142
-
143
155
  let txParams = await this.getTxParamsNormalizedFromContract(uniqueTaskName, sender, contract, method, ...params);
144
156
  return await this.processTxParams(txParams);
145
157
  }
146
158
 
159
+ async processData (
160
+ sender: IAccount,
161
+ builder: TxDataBuilder
162
+ ): Promise<{
163
+ prevStatus: ETimelockTxStatus
164
+ status: ETimelockTxStatus
165
+ schedule: ITimelockTx
166
+ tx?: TEth.Hex
167
+ }> {
168
+ let { to, data, value } = builder.data;
169
+ let titleKey = `${to}${data}`;
170
+ let params = <ITimelockTxParams> {
171
+ title: $contract.keccak256(titleKey, 'hex'),
172
+ sender: sender,
173
+ to: to,
174
+ data: data,
175
+ value: value
176
+ };
177
+ let txParams = await this.getTxParamsNormalized (params);
178
+ return await this.processTxParams(txParams);
179
+ }
180
+
147
181
  /** Schedules-Wait-Execute a task distinguished by the unique task name
148
182
  *
149
183
  * 1. if schedule doesn't exist, create it
@@ -521,7 +555,7 @@ export class TimelockService implements ITimelockService {
521
555
  private async getUniqueByKey (key: TEth.Hex): Promise<ITimelockTx> {
522
556
  let store = await this.getStore();
523
557
  let all = await store.getAll();
524
- let arr = all.filter(x => x.key === key);
558
+ let arr = all.filter(x => x.key === key && x.status === 'pending');
525
559
  $require.lt(arr.length, 2, `Timelock service expects ${key} to be unique. Found ${arr.length}`);
526
560
  return arr.length === 1 ? arr[0] : null;
527
561
  }
@@ -613,7 +647,7 @@ export class TimelockService implements ITimelockService {
613
647
  @memd.deco.memoize({ perInstance: true })
614
648
  private async getStore () {
615
649
  let { platform, network } = this.timelock.client;
616
- let dir = this.options?.dir ?? `0x/data`;
650
+ let dir = this.options?.dir ?? CONFIG.dir;
617
651
 
618
652
  let path = getPath(platform, dir);
619
653
  if (platform === 'hardhat' && network !== platform) {
@@ -631,6 +665,12 @@ export class TimelockService implements ITimelockService {
631
665
  key: x => x.id
632
666
  });
633
667
  }
668
+
669
+ static config (config: Partial<typeof CONFIG>) {
670
+ if (config.dir) {
671
+ CONFIG.dir = CONFIG.dir;
672
+ }
673
+ }
634
674
  }
635
675
 
636
676
  namespace util {
@@ -26,15 +26,17 @@ import { TxLogParser } from './receipt/TxLogParser';
26
26
 
27
27
  import { ISafeServiceTransport } from '@dequanto/safe/transport/ISafeServiceTransport';
28
28
  import { SigFileTransport } from './sig-transports/SigFileTransport';
29
- import { TxWriterAccountAgents } from './agents/TxWriterAccountAgents';
30
29
  import { PromiseEvent } from '@dequanto/class/PromiseEvent';
31
30
  import { $sig } from '@dequanto/utils/$sig';
32
31
  import { TEth } from '@dequanto/models/TEth';
33
32
  import { SafeServiceTypes } from '@dequanto/safe/types/SafeServiceTypes';
34
33
  import { ChainAccountService } from '@dequanto/ChainAccountService';
35
34
  import { $is } from '@dequanto/utils/$is';
35
+ import { TxWriterAccountAgents } from './agents/TxWriterAccountAgents';
36
+
36
37
 
37
- interface ITxWriterEvents {
38
+
39
+ export interface ITxWriterEvents {
38
40
  transactionHash (hash: TEth.Hex)
39
41
  receipt (receipt: TEth.TxReceipt)
40
42
  confirmation (confNumber: number, receipt: TEth.TxReceipt)
@@ -43,6 +45,25 @@ interface ITxWriterEvents {
43
45
  log (message: string)
44
46
  safeTxProposed (safeTx: SafeServiceTypes.ProposeTransactionProps)
45
47
  }
48
+ export interface ITxWriterEmitter {
49
+ onSent: PromiseLike<string | TEth.Hex>
50
+ onCompleted: PromiseLike<TEth.TxReceipt>
51
+
52
+ tx: ITxWriterTransaction
53
+ receipt: TEth.TxReceipt
54
+
55
+ on (event: keyof ITxWriterEvents, callback: Function): any | this
56
+ }
57
+
58
+ export interface ITxWriterTransaction {
59
+ timestamp: number
60
+ confirmations: number,
61
+ hash?: TEth.Hex
62
+ timeout?: NodeJS.Timer
63
+ receipt?: TEth.TxReceipt
64
+ error?: Error
65
+ knownLogs?: ITxLogItem[]
66
+ }
46
67
  export interface ITxWriterOptions {
47
68
  timeout?: number | boolean
48
69
  retries?: number
@@ -80,7 +101,7 @@ export interface ITxWriterOptions {
80
101
 
81
102
  const DEFAULTS: ITxWriterOptions = {};
82
103
 
83
- export class TxWriter extends class_EventEmitter<ITxWriterEvents> {
104
+ export class TxWriter extends class_EventEmitter<ITxWriterEvents> implements ITxWriterEmitter {
84
105
 
85
106
  onSent = new class_Dfr<string | TEth.Hex>();
86
107
  onCompleted = new class_Dfr<TEth.TxReceipt>();
@@ -108,17 +129,9 @@ export class TxWriter extends class_EventEmitter<ITxWriterEvents> {
108
129
  }
109
130
 
110
131
  id = Math.round(Math.random() * 10**10) + '';
111
- tx: {
112
- timestamp: number
113
- confirmations: number,
114
- hash?: TEth.Hex
115
- timeout?: NodeJS.Timer
116
- receipt?: TEth.TxReceipt
117
- error?: Error
118
- knownLogs?: ITxLogItem[]
119
- } = null
120
-
121
- txs: TxWriter['tx'][] = []
132
+ tx: ITxWriterTransaction = null
133
+
134
+ txs: ITxWriterTransaction[] = []
122
135
  options: ITxWriterOptions;
123
136
 
124
137
 
@@ -173,9 +186,8 @@ export class TxWriter extends class_EventEmitter<ITxWriterEvents> {
173
186
 
174
187
  let agent = TxWriterAccountAgents.get(this.account);
175
188
  if (agent != null) {
176
-
177
189
  let sender = await this.getSender();
178
- let innerWriter: TxWriter;
190
+ let innerWriter: ITxWriterEmitter;
179
191
  try {
180
192
  innerWriter = await agent.process(sender, this.account, this);
181
193
  } catch (error) {
@@ -479,7 +491,7 @@ export class TxWriter extends class_EventEmitter<ITxWriterEvents> {
479
491
  }
480
492
  this.sendTxInner();
481
493
  }
482
- private pipeInnerWriter (innerWriter: TxWriter) {
494
+ private pipeInnerWriter (innerWriter: ITxWriterEmitter) {
483
495
  innerWriter.onCompleted.then(
484
496
  (receipt) => {
485
497
  this.tx = innerWriter.tx;
@@ -0,0 +1,116 @@
1
+ import { EoAccount, TAccount, TimelockAccount } from '@dequanto/models/TAccount';
2
+ import { ITxWriterAccountAgent } from './TxWriterAccountAgents';
3
+ import { $account } from '@dequanto/utils/$account';
4
+ import { ITxWriterEmitter, ITxWriterEvents, ITxWriterTransaction, TxWriter } from '../TxWriter';
5
+
6
+ import { ChainAccountService } from '@dequanto/ChainAccountService';
7
+ import { $promise } from '@dequanto/utils/$promise';
8
+ import { class_Dfr, class_EventEmitter } from 'atma-utils';
9
+ import { TEth } from '@dequanto/models/TEth';
10
+ import { $require } from '@dequanto/utils/$require';
11
+ import { $date } from '@dequanto/utils/$date';
12
+ import { ITimelockTx } from '@dequanto/services/TimelockService/ITimelockService';
13
+ import { Web3Client } from '@dequanto/clients/Web3Client';
14
+
15
+ import { TimelockService } from '@dequanto/services/TimelockService/TimelockService';
16
+ import { TimelockControllerFactory } from '@dequanto/prebuilt-factories/TimelockControllerFactory';
17
+
18
+
19
+ export class TimelockAgent implements ITxWriterAccountAgent {
20
+ supports (account: TAccount) {
21
+ return $account.isTimelock(account);
22
+ }
23
+ async process (senderMix: string | EoAccount, timelockAccount: TimelockAccount, outerWriter: TxWriter) {
24
+
25
+ let { client, options } = outerWriter;
26
+ let TimelockController = TimelockControllerFactory();
27
+ let timelock = new TimelockController(timelockAccount.address, client);
28
+ let service = new TimelockService(timelock, {
29
+ simulate: false,
30
+ });
31
+ let sender = typeof senderMix === 'string'
32
+ ? await ChainAccountService.get(senderMix) as EoAccount
33
+ : senderMix;
34
+
35
+ let inner = new TimelockTxWriter(
36
+ client,
37
+ sender,
38
+ service,
39
+ outerWriter,
40
+ );
41
+
42
+ await inner.process();
43
+ return inner;
44
+ }
45
+ }
46
+
47
+ export class TimelockTxWriter extends class_EventEmitter<ITxWriterEvents> implements ITxWriterEmitter {
48
+
49
+ onSent = new class_Dfr<string | TEth.Hex>()
50
+ onCompleted = new class_Dfr<TEth.TxReceipt>()
51
+ receipt: TEth.TxReceipt
52
+ tx: ITxWriterTransaction
53
+
54
+ schedule: ITimelockTx
55
+ delayTimer: any
56
+
57
+ constructor (
58
+ private client: Web3Client,
59
+ private sender: TEth.IAccount,
60
+ private service: TimelockService,
61
+ private outerWriter: TxWriter,
62
+ ) {
63
+ super();
64
+ }
65
+
66
+ async process () {
67
+ let { sender, service, client, outerWriter } = this;
68
+
69
+ let { error, result } = await $promise.caught(service.processData(sender, outerWriter.builder));
70
+ if (error) {
71
+ this.onCompleted.reject(error);
72
+ this.emit('error', error);
73
+ return this;
74
+ }
75
+ if (result.status === 'completed') {
76
+ $require.Hex(result.tx, 'Timelock transaction hash expected');
77
+ let receipt = await client.getTransactionReceipt(result.tx);
78
+ let block = await client.getBlock(receipt.blockNumber);
79
+ let latestBlock = await client.getBlockNumber();
80
+ $require.notNull(receipt, `Receipt for ${result.tx} not found`);
81
+
82
+ let tx = {
83
+ timestamp: block.timestamp,
84
+ confirmations: latestBlock - block.number,
85
+ hash: receipt.transactionHash,
86
+ receipt: receipt,
87
+ error: null,
88
+ knownLogs: null,
89
+ } as ITxWriterTransaction;
90
+
91
+ this.tx = tx;
92
+ this.emit('transactionHash', receipt.transactionHash);
93
+ this.emit('receipt', receipt);
94
+ this.onCompleted.resolve(receipt);
95
+ return this;
96
+ }
97
+ if (result.status === 'pending') {
98
+ let readyAt = $date.fromUnixTimestamp(result.schedule.validAt);
99
+ let readyAtF = $date.format(readyAt, 'yyyy-MM-DD HH:mm:ss');
100
+ this.emit('log', `Timelock transaction pending: ${result.schedule.id}, ready at: ${readyAtF}`);
101
+ this.onSent.resolve(result.schedule.id);
102
+
103
+ let ms = readyAt.valueOf() - Date.now();
104
+ $require.gt(ms, 0, `The transaction must be in 'ready' state`);
105
+ this.delayTimer = setTimeout(() => {
106
+ this.process();
107
+ }, ms);
108
+ return this;
109
+ }
110
+ if (result.status === 'ready') {
111
+ throw new Error(`[TimelockAgent] Service emits 'ready' status, however it should execute the transaction directly`);
112
+ }
113
+
114
+ throw new Error(`[TimelockAgent] Unsupported status: ${result.status}`);
115
+ }
116
+ }
@@ -1,13 +1,14 @@
1
1
  import type { EoAccount, TAccount } from '@dequanto/models/TAccount';
2
- import type { TxWriter } from '../TxWriter';
2
+ import type { ITxWriterEmitter, TxWriter } from '../TxWriter';
3
3
  import { SafeAgent } from './SafeAgent';
4
4
  import { Erc4337Agent } from './Erc4337Agent';
5
+ import { TimelockAgent } from './TimelockAgent';
5
6
 
6
7
 
7
8
 
8
9
  export interface ITxWriterAccountAgent {
9
10
  supports (account: TAccount): boolean
10
- process (sender: EoAccount, account: TAccount, outerWriter: TxWriter): Promise<TxWriter>
11
+ process (sender: EoAccount, account: TAccount, outerWriter: TxWriter): Promise<ITxWriterEmitter>
11
12
  }
12
13
 
13
14
  export namespace TxWriterAccountAgents {
@@ -15,6 +16,7 @@ export namespace TxWriterAccountAgents {
15
16
  const agents: ITxWriterAccountAgent[] = [
16
17
  new SafeAgent(),
17
18
  new Erc4337Agent(),
19
+ new TimelockAgent(),
18
20
  ];
19
21
 
20
22
  export function register (agent: ITxWriterAccountAgent) {
@@ -1,5 +1,6 @@
1
- import { EoAccount, Erc4337Account, SafeAccount, TAccount } from "@dequanto/models/TAccount";
1
+ import { EoAccount, Erc4337Account, SafeAccount, TAccount, TimelockAccount } from "@dequanto/models/TAccount";
2
2
  import { $address } from './$address';
3
+ import { $require } from './$require';
3
4
 
4
5
  export namespace $account {
5
6
 
@@ -11,10 +12,11 @@ export namespace $account {
11
12
  return { name: account };
12
13
  }
13
14
 
14
- let acc = isSafe(account) || isErc4337 (account)
15
+ let acc = isSafe(account) || isErc4337 (account) || isTimelock(account)
15
16
  ? account.operator
16
17
  : account;
17
18
 
19
+ $require.notNull(acc, `Sender not resolved for ${ account?.address ?? account }`);
18
20
  return acc as EoAccount;
19
21
  }
20
22
 
@@ -45,4 +47,18 @@ export namespace $account {
45
47
  }
46
48
  return false;
47
49
  }
50
+
51
+ export function isTimelock (account:TAccount): account is TimelockAccount {
52
+ if (account == null) {
53
+ return false;
54
+ }
55
+ let rgx = /^timelock\//;
56
+ if (typeof account === 'string') {
57
+ return rgx.test(account);
58
+ }
59
+ if (account.type === 'timelock' || rgx.test(account.name)) {
60
+ return true;
61
+ }
62
+ return false;
63
+ }
48
64
  }
@@ -250,6 +250,17 @@ export namespace $date {
250
250
  result.setHours(23, 59, 59, 999);
251
251
  return result;
252
252
  }
253
+ export function weekNumber(date = new Date()) {
254
+ date = new Date(date);
255
+ date.setHours(0, 0, 0, 0);
256
+
257
+ // Set to nearest Thursday: current date + 4 - current day number
258
+ const dayNum = date.getDay() || 7;
259
+ date.setDate(date.getDate() + 4 - dayNum);
260
+ // Calculate week number
261
+ const yearStart = new Date(Date.UTC(date.getFullYear(), 0, 1));
262
+ return Math.ceil((((date.getTime() - yearStart.getTime()) / 86400000) + 1) / 7);
263
+ }
253
264
  export function monthStart(date = new Date()) {
254
265
  let result = new Date(date);
255
266
  result.setDate(1);
@@ -264,7 +275,6 @@ export namespace $date {
264
275
  return result;
265
276
  }
266
277
 
267
-
268
278
  /** date1 + X === date2 */
269
279
  export function daysBetween(a: Date, b: Date, abs: boolean = true) {
270
280
  let aStart = dayStart(a);