dequanto 0.1.78 → 0.1.81
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/lib/cjs/contracts/deploy/Deployments.js +5 -1
- package/lib/cjs/contracts/deploy/Deployments.js.map +1 -1
- package/lib/cjs/contracts/deploy/proxy/ProxyDeployment.js +11 -5
- package/lib/cjs/contracts/deploy/proxy/ProxyDeployment.js.map +1 -1
- package/lib/cjs/hardhat/HardhatProvider.js +3 -1
- package/lib/cjs/hardhat/HardhatProvider.js.map +1 -1
- package/lib/cjs/indexer/EventsIndexer.js +14 -2
- package/lib/cjs/indexer/EventsIndexer.js.map +1 -1
- package/lib/cjs/services/TimelockService/TimelockService.js +30 -0
- package/lib/cjs/services/TimelockService/TimelockService.js.map +1 -1
- package/lib/cjs/utils/$bigint.js +19 -2
- package/lib/cjs/utils/$bigint.js.map +1 -1
- package/lib/esm/contracts/deploy/Deployments.js.map +1 -1
- package/lib/esm/contracts/deploy/Deployments.mjs +5 -1
- package/lib/esm/contracts/deploy/proxy/ProxyDeployment.js.map +1 -1
- package/lib/esm/contracts/deploy/proxy/ProxyDeployment.mjs +11 -5
- package/lib/esm/hardhat/HardhatProvider.js.map +1 -1
- package/lib/esm/hardhat/HardhatProvider.mjs +3 -1
- package/lib/esm/indexer/EventsIndexer.js.map +1 -1
- package/lib/esm/indexer/EventsIndexer.mjs +14 -2
- package/lib/esm/services/TimelockService/TimelockService.js.map +1 -1
- package/lib/esm/services/TimelockService/TimelockService.mjs +30 -0
- package/lib/esm/utils/$bigint.js.map +1 -1
- package/lib/esm/utils/$bigint.mjs +19 -2
- package/lib/types/contracts/deploy/Deployments.d.ts +7 -0
- package/lib/types/contracts/deploy/proxy/ProxyDeployment.d.ts +2 -0
- package/lib/types/hardhat/HardhatProvider.d.ts +1 -0
- package/lib/types/services/TimelockService/TimelockService.d.ts +4 -0
- package/lib/types/utils/$bigint.d.ts +2 -1
- package/package.json +1 -1
- package/src/contracts/deploy/Deployments.ts +16 -1
- package/src/contracts/deploy/proxy/ProxyDeployment.ts +18 -12
- package/src/hardhat/HardhatProvider.ts +4 -1
- package/src/indexer/EventsIndexer.ts +18 -3
- package/src/services/TimelockService/TimelockService.ts +36 -0
- package/src/utils/$bigint.ts +19 -2
|
@@ -22,6 +22,7 @@ export interface IBeacon extends ContractBase {
|
|
|
22
22
|
interface IDeploymentCtx {
|
|
23
23
|
ImplementationContract: Constructor<ContractBase>;
|
|
24
24
|
deployer: IAccount;
|
|
25
|
+
owner?: IAccount;
|
|
25
26
|
deployments: Deployments;
|
|
26
27
|
implementation: {
|
|
27
28
|
address: TAddress;
|
|
@@ -30,6 +31,7 @@ interface IDeploymentCtx {
|
|
|
30
31
|
options?: {
|
|
31
32
|
skipStorageLayoutCheck?: boolean;
|
|
32
33
|
};
|
|
34
|
+
upgradeImplementation?: boolean;
|
|
33
35
|
}
|
|
34
36
|
interface IProxyDeploymentCtx extends IDeploymentCtx {
|
|
35
37
|
proxyId: string;
|
|
@@ -18,6 +18,10 @@ export declare class TimelockService implements ITimelockService {
|
|
|
18
18
|
status: ETimelockTxStatus;
|
|
19
19
|
schedule: ITimelockTx;
|
|
20
20
|
}>;
|
|
21
|
+
getPendingFromTx(txHash: TEth.Hex): Promise<{
|
|
22
|
+
status: ETimelockTxStatus;
|
|
23
|
+
schedule: ITimelockTx;
|
|
24
|
+
}>;
|
|
21
25
|
executePendingByTitle(sender: IAccount, title: string): Promise<{
|
|
22
26
|
tx: TxWriter;
|
|
23
27
|
schedule: ITimelockTx;
|
|
@@ -6,8 +6,9 @@ export declare namespace $bigint {
|
|
|
6
6
|
function max(...args: bigint[]): bigint;
|
|
7
7
|
function min(...args: bigint[]): bigint;
|
|
8
8
|
function toBigInt(amount: string | number | bigint | Uint8Array): bigint;
|
|
9
|
+
function from(amount: number | bigint | string): bigint;
|
|
9
10
|
/**
|
|
10
|
-
* @param amount e.g "2.4 ether", "10 gwei", "1.7^18", "123456"
|
|
11
|
+
* @param amount e.g "2.4 ether", "10 gwei", "1.7^18", "123456", "1.7e18"
|
|
11
12
|
*/
|
|
12
13
|
function parse(amount: string): bigint;
|
|
13
14
|
function ensureWei(amount: number | bigint, decimals: number): bigint;
|
package/package.json
CHANGED
|
@@ -22,6 +22,7 @@ import { TAddress } from '@dequanto/models/TAddress';
|
|
|
22
22
|
import { $promise } from '@dequanto/utils/$promise';
|
|
23
23
|
import { l } from '@dequanto/utils/$logger';
|
|
24
24
|
import { $bytecode } from '@dequanto/evm/utils/$bytecode';
|
|
25
|
+
import { THex } from '@dequanto/models/THex';
|
|
25
26
|
|
|
26
27
|
|
|
27
28
|
|
|
@@ -37,6 +38,15 @@ type TDeploymentOptions = {
|
|
|
37
38
|
|
|
38
39
|
// Will be used for verification process
|
|
39
40
|
proxyFor?: TAddress
|
|
41
|
+
|
|
42
|
+
deployment?: {
|
|
43
|
+
// Pending deployment transaction will be re-checked
|
|
44
|
+
tx?: TEth.Hex
|
|
45
|
+
// When false, the implementation won't be updated in proxy
|
|
46
|
+
upgradeProxy?: boolean
|
|
47
|
+
// Owner, if differes from deployer
|
|
48
|
+
owner?: TEth.IAccount
|
|
49
|
+
}
|
|
40
50
|
}
|
|
41
51
|
type TVerificationOptions = TDeploymentOptions & {
|
|
42
52
|
// otherwise will be fetched from the deployment TX
|
|
@@ -67,6 +77,7 @@ export class Deployments {
|
|
|
67
77
|
}
|
|
68
78
|
|
|
69
79
|
constructor(public client: Web3Client, public deployer: IAccount, public opts: {
|
|
80
|
+
owner?: IAccount
|
|
70
81
|
Proxy?: Constructor<ContractBase>,
|
|
71
82
|
ProxyAdmin?: Constructor<IProxyAdmin>
|
|
72
83
|
directory?: string
|
|
@@ -252,6 +263,7 @@ export class Deployments {
|
|
|
252
263
|
arguments: constructorArgs,
|
|
253
264
|
client: this.client,
|
|
254
265
|
deployer: this.deployer as any,
|
|
266
|
+
tx: opts.deployment?.tx
|
|
255
267
|
});
|
|
256
268
|
|
|
257
269
|
let deployment = await this.store.saveDeployment(deployedContract, {
|
|
@@ -304,6 +316,7 @@ export class Deployments {
|
|
|
304
316
|
force: opts?.force,
|
|
305
317
|
latest: this.opts?.checkBytecode !== false,
|
|
306
318
|
verification: opts?.verification,
|
|
319
|
+
deployment: opts?.deployment
|
|
307
320
|
});
|
|
308
321
|
|
|
309
322
|
let data = serializeInitData(id, contractImpl, opts.initialize);
|
|
@@ -317,11 +330,13 @@ export class Deployments {
|
|
|
317
330
|
ImplementationContract: CtorImpl,
|
|
318
331
|
proxyId: proxyId,
|
|
319
332
|
deployer: this.deployer,
|
|
333
|
+
owner: opts.deployment?.owner ?? this.opts?.owner ?? this.deployer,
|
|
320
334
|
deployments: this,
|
|
321
335
|
implementation: {
|
|
322
336
|
address: implementationAddress,
|
|
323
337
|
initData: data
|
|
324
|
-
}
|
|
338
|
+
},
|
|
339
|
+
upgradeImplementation: opts.deployment?.upgradeProxy ?? true
|
|
325
340
|
})
|
|
326
341
|
|
|
327
342
|
if (contractImplDeployment.implementation == null) {
|
|
@@ -37,6 +37,7 @@ export interface IBeacon extends ContractBase {
|
|
|
37
37
|
interface IDeploymentCtx {
|
|
38
38
|
ImplementationContract: Constructor<ContractBase>
|
|
39
39
|
deployer: IAccount
|
|
40
|
+
owner?: IAccount
|
|
40
41
|
deployments: Deployments
|
|
41
42
|
implementation: {
|
|
42
43
|
address: TAddress
|
|
@@ -45,6 +46,7 @@ interface IDeploymentCtx {
|
|
|
45
46
|
options?: {
|
|
46
47
|
skipStorageLayoutCheck?: boolean
|
|
47
48
|
}
|
|
49
|
+
upgradeImplementation?: boolean
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
interface IProxyDeploymentCtx extends IDeploymentCtx {
|
|
@@ -151,6 +153,7 @@ export class ProxyDeployment {
|
|
|
151
153
|
|
|
152
154
|
|
|
153
155
|
let hasProxy = await deployments.has(Proxy, proxyOpts);
|
|
156
|
+
let shouldUpdate = ctx.upgradeImplementation ?? true;
|
|
154
157
|
let {
|
|
155
158
|
contract: contractProxy,
|
|
156
159
|
receipt: contractProxyReceipt,
|
|
@@ -201,18 +204,21 @@ export class ProxyDeployment {
|
|
|
201
204
|
let slotValue = await client.getStorageAt(contractProxy.address, SLOT);
|
|
202
205
|
let address = `0x` + slotValue.slice(-40);
|
|
203
206
|
if ($address.eq(address, implAddress) === false) {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
207
|
+
if (shouldUpdate) {
|
|
208
|
+
await this.requireCompatibleStorageLayout(proxyId, ctx);
|
|
209
|
+
$logger.log(`Upgrading ProxyAdmin(${contractProxyAdmin.address}) to ${implAddress} (${v}) from ${address}`);
|
|
210
|
+
let receipt = await Interfaces.call(
|
|
211
|
+
ctx.owner ?? deployer,
|
|
212
|
+
contractProxyAdmin,
|
|
213
|
+
Interfaces.TransparentProxy[v].contractProxyAdmin.upgradeAndCall,
|
|
214
|
+
contractProxy.address,
|
|
215
|
+
implAddress,
|
|
216
|
+
null // data
|
|
217
|
+
);
|
|
218
|
+
await this.saveStorageLayout(proxyId, ctx);
|
|
219
|
+
} else {
|
|
220
|
+
$logger.log(`Skip upgrade ProxyAdmin(${contractProxyAdmin.address}) to ${implAddress} (${v}) from ${address}`);
|
|
221
|
+
}
|
|
216
222
|
}
|
|
217
223
|
}
|
|
218
224
|
if (hasProxy === false && contractProxyReceipt != null) {
|
|
@@ -142,6 +142,7 @@ export class HardhatProvider {
|
|
|
142
142
|
deployer?: EoAccount
|
|
143
143
|
arguments?: any[]
|
|
144
144
|
client?: Web3Client
|
|
145
|
+
tx?: TEth.Hex
|
|
145
146
|
}): Promise<{
|
|
146
147
|
contract: T
|
|
147
148
|
receipt: TEth.TxReceipt
|
|
@@ -155,7 +156,9 @@ export class HardhatProvider {
|
|
|
155
156
|
let receipt: TEth.TxReceipt;
|
|
156
157
|
|
|
157
158
|
try {
|
|
158
|
-
receipt =
|
|
159
|
+
receipt = options?.tx
|
|
160
|
+
? await client.getTransactionReceipt(options.tx)
|
|
161
|
+
: await factory.deploy();
|
|
159
162
|
} catch (error) {
|
|
160
163
|
let wrapped = new Error(`Deploy ${Ctor.name} failed: ` + error.message + `\n ${error.stack}`);
|
|
161
164
|
(wrapped as any).data = error.data;
|
|
@@ -106,7 +106,9 @@ export class EventsIndexer <TContract extends ContractBase> {
|
|
|
106
106
|
> (
|
|
107
107
|
event: TLogName | TLogName[] | '*',
|
|
108
108
|
options?: {
|
|
109
|
+
// includes block
|
|
109
110
|
fromBlock?: number
|
|
111
|
+
// includes block
|
|
110
112
|
toBlock?: number
|
|
111
113
|
blockRangeLimits?: WClient['blockRangeLimits']
|
|
112
114
|
params?: Partial<TContract['Types']['Events'][TLogName]['outputParams']>
|
|
@@ -183,6 +185,10 @@ export class EventsIndexer <TContract extends ContractBase> {
|
|
|
183
185
|
fromBlock?: number
|
|
184
186
|
filterKey?: string
|
|
185
187
|
}): Promise<TRange[]> {
|
|
188
|
+
let fromBlock = opts?.fromBlock ?? initialBlockNumber;
|
|
189
|
+
if (fromBlock != null) {
|
|
190
|
+
$require.lte(fromBlock, toBlock, `Invalid block range order`);
|
|
191
|
+
}
|
|
186
192
|
let logsMetaArr = await this.storeMeta.fetch();
|
|
187
193
|
let eventsBlock = alot(logsMetaArr)
|
|
188
194
|
.filter(x => x.filterKey == opts?.filterKey)
|
|
@@ -198,15 +204,24 @@ export class EventsIndexer <TContract extends ContractBase> {
|
|
|
198
204
|
if (hasInitialBlock === false) {
|
|
199
205
|
let blockNr = opts?.fromBlock ?? await this.getInitialBlockNumber();
|
|
200
206
|
logsMeta.filter(x => x.lastBlock == null).forEach(x => x.lastBlock = blockNr);
|
|
201
|
-
}
|
|
207
|
+
}
|
|
202
208
|
|
|
203
209
|
let ranges = [] as TRange[];
|
|
204
|
-
let blockNumbers = [ ...logsMeta.map(x => x.lastBlock), toBlock ];
|
|
205
|
-
let blockNumberSteps = alot(blockNumbers).distinct().sortBy(x => x).toArray();
|
|
210
|
+
let blockNumbers = [ ...logsMeta.map(x => x.lastBlock), toBlock ].filter(x => x <= toBlock);
|
|
206
211
|
|
|
212
|
+
let blockNumberSteps = alot(blockNumbers).distinct().sortBy(x => x).toArray();
|
|
213
|
+
if (blockNumberSteps.length === 1) {
|
|
214
|
+
let [ block ] = blockNumberSteps;
|
|
215
|
+
ranges.push({
|
|
216
|
+
fromBlock: block,
|
|
217
|
+
toBlock: block,
|
|
218
|
+
events: events
|
|
219
|
+
});
|
|
220
|
+
}
|
|
207
221
|
for (let i = 0; i < blockNumberSteps.length - 1; i++) {
|
|
208
222
|
let from = blockNumberSteps[i];
|
|
209
223
|
if (i > 0) {
|
|
224
|
+
// The range includes both blocks: [x, y], but we want (x, y], so we make [x + 1, y]
|
|
210
225
|
from += 1;
|
|
211
226
|
}
|
|
212
227
|
let to = blockNumberSteps[i + 1];
|
|
@@ -57,6 +57,42 @@ export class TimelockService implements ITimelockService {
|
|
|
57
57
|
return { status, schedule };
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
async getPendingFromTx (txHash: TEth.Hex): Promise<{
|
|
61
|
+
status: ETimelockTxStatus;
|
|
62
|
+
schedule: ITimelockTx;
|
|
63
|
+
}> {
|
|
64
|
+
let receipt = await this.timelock.client.getTransactionReceipt(txHash);
|
|
65
|
+
$require.notNull(receipt, `Tx ${txHash} not found`);
|
|
66
|
+
let block = await this.timelock.client.getBlock(receipt.blockNumber);
|
|
67
|
+
let contract = new TimelockController(void 0, this.timelock.client);
|
|
68
|
+
let callScheduled = contract.extractLogsCallScheduled(receipt);
|
|
69
|
+
$require.gte(callScheduled.length, 1, `Expected at least 1 CallScheduled log`);
|
|
70
|
+
|
|
71
|
+
let callSaltArr = contract.extractLogsCallSalt(receipt);
|
|
72
|
+
$require.eq(callSaltArr.length, 1, `Expected 1 CallSalt log, but got ${callSaltArr.length}`);
|
|
73
|
+
let [ callSalt ] = callSaltArr;
|
|
74
|
+
|
|
75
|
+
let [ callScheduleFirst ] = callScheduled;
|
|
76
|
+
callScheduleFirst.params.delay
|
|
77
|
+
|
|
78
|
+
let schedule = {
|
|
79
|
+
sender: null,
|
|
80
|
+
key: callSalt.params.id,
|
|
81
|
+
id: callSalt.params.id,
|
|
82
|
+
salt: callSalt.params.salt,
|
|
83
|
+
to: callScheduled.map(x => x.params.target),
|
|
84
|
+
data: callScheduled.map(x => x.params.data),
|
|
85
|
+
value: callScheduled.map(x => $hex.ensure(x.params.value)),
|
|
86
|
+
predecessor: null,
|
|
87
|
+
createdAt: block.timestamp,
|
|
88
|
+
validAt: block.timestamp + Number(callScheduleFirst.params.delay),
|
|
89
|
+
status: 'pending',
|
|
90
|
+
txSchedule: txHash,
|
|
91
|
+
} as ITimelockTx;
|
|
92
|
+
let status = await this.getScheduleStatus(schedule);
|
|
93
|
+
return { status, schedule };
|
|
94
|
+
}
|
|
95
|
+
|
|
60
96
|
async executePendingByTitle (sender: IAccount, title: string): Promise<{
|
|
61
97
|
tx: TxWriter;
|
|
62
98
|
schedule: ITimelockTx;
|
package/src/utils/$bigint.ts
CHANGED
|
@@ -49,8 +49,25 @@ export namespace $bigint {
|
|
|
49
49
|
return BigInt(Math.round(amount));
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
export function from (amount: number | bigint | string): bigint {
|
|
53
|
+
if (amount == null) {
|
|
54
|
+
return 0n;
|
|
55
|
+
}
|
|
56
|
+
switch (typeof amount) {
|
|
57
|
+
case 'bigint':
|
|
58
|
+
return amount;
|
|
59
|
+
case 'number':
|
|
60
|
+
return BigInt(amount);
|
|
61
|
+
case'string':
|
|
62
|
+
return parse(amount);
|
|
63
|
+
default:
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
throw new Error(`Invalid $bigint.from type: ${amount}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
52
69
|
/**
|
|
53
|
-
* @param amount e.g "2.4 ether", "10 gwei", "1.7^18", "123456"
|
|
70
|
+
* @param amount e.g "2.4 ether", "10 gwei", "1.7^18", "123456", "1.7e18"
|
|
54
71
|
*/
|
|
55
72
|
export function parse (amount: string): bigint {
|
|
56
73
|
if (/^\d+$/.test(amount) || $is.Hex(amount) ) {
|
|
@@ -76,7 +93,7 @@ export namespace $bigint {
|
|
|
76
93
|
}
|
|
77
94
|
|
|
78
95
|
// 2.5^18
|
|
79
|
-
let rgxMantissa = /^(?<number>[\d.]+)\s
|
|
96
|
+
let rgxMantissa = /^(?<number>[\d.]+)\s*[\^e]\s*(?<decimals>\d+)$/;
|
|
80
97
|
let rgxMantissaMatch = rgxMantissa.exec(amount);
|
|
81
98
|
if (rgxMantissaMatch != null) {
|
|
82
99
|
let number = Number(rgxMantissaMatch.groups.number);
|