hermez-node/eth/wdelayer.go
eth/wdelayer.goRaw
1package eth
2
3import (
4 "context"
5 "fmt"
6 "math/big"
7 "strings"
8
9 "github.com/ethereum/go-ethereum"
10 "github.com/ethereum/go-ethereum/accounts/abi"
11 "github.com/ethereum/go-ethereum/accounts/abi/bind"
12 ethCommon "github.com/ethereum/go-ethereum/common"
13 "github.com/ethereum/go-ethereum/core/types"
14 "github.com/ethereum/go-ethereum/crypto"
15 "github.com/ethereum/go-ethereum/ethclient"
16 WithdrawalDelayer "github.com/hermeznetwork/hermez-node/eth/contracts/withdrawdelayer"
17)
18
19// WDelayerConstants are the constants of the Rollup Smart Contract
20type WDelayerConstants struct {
21 // Max Withdrawal Delay
22 MaxWithdrawalDelay uint64
23 // Max Emergency mode time
24 MaxEmergencyModeTime uint64
25 // HermezRollup smartcontract address
26 HermezRollup ethCommon.Address
27}
28
29// DepositState is the state of Deposit
30type DepositState struct {
31 Amount *big.Int
32 DepositTimestamp uint64
33}
34
35// WDelayerEventDeposit is an event of the WithdrawalDelayer Smart Contract
36type WDelayerEventDeposit struct {
37 Owner ethCommon.Address
38 Token ethCommon.Address
39 Amount *big.Int
40 DepositTimestamp uint64
41}
42
43// WDelayerEventWithdraw is an event of the WithdrawalDelayer Smart Contract
44type WDelayerEventWithdraw struct {
45 Owner ethCommon.Address
46 Token ethCommon.Address
47 Amount *big.Int
48}
49
50// WDelayerEventEmergencyModeEnabled an event of the WithdrawalDelayer Smart Contract
51type WDelayerEventEmergencyModeEnabled struct {
52}
53
54// WDelayerEventNewWithdrawalDelay an event of the WithdrawalDelayer Smart Contract
55type WDelayerEventNewWithdrawalDelay struct {
56 WithdrawalDelay uint64
57}
58
59// WDelayerEventEscapeHatchWithdrawal an event of the WithdrawalDelayer Smart Contract
60type WDelayerEventEscapeHatchWithdrawal struct {
61 Who ethCommon.Address
62 To ethCommon.Address
63 Token ethCommon.Address
64 Amount *big.Int
65}
66
67// WDelayerEventNewHermezKeeperAddress an event of the WithdrawalDelayer Smart Contract
68type WDelayerEventNewHermezKeeperAddress struct {
69 NewHermezKeeperAddress ethCommon.Address
70}
71
72// WDelayerEventNewWhiteHackGroupAddress an event of the WithdrawalDelayer Smart Contract
73type WDelayerEventNewWhiteHackGroupAddress struct {
74 NewWhiteHackGroupAddress ethCommon.Address
75}
76
77// WDelayerEventNewHermezGovernanceDAOAddress an event of the WithdrawalDelayer Smart Contract
78type WDelayerEventNewHermezGovernanceDAOAddress struct {
79 NewHermezGovernanceDAOAddress ethCommon.Address
80}
81
82// WDelayerEvents is the lis of events in a block of the WithdrawalDelayer Smart Contract
83type WDelayerEvents struct {
84 Deposit []WDelayerEventDeposit
85 Withdraw []WDelayerEventWithdraw
86 EmergencyModeEnabled []WDelayerEventEmergencyModeEnabled
87 NewWithdrawalDelay []WDelayerEventNewWithdrawalDelay
88 EscapeHatchWithdrawal []WDelayerEventEscapeHatchWithdrawal
89 NewHermezKeeperAddress []WDelayerEventNewHermezKeeperAddress
90 NewWhiteHackGroupAddress []WDelayerEventNewWhiteHackGroupAddress
91 NewHermezGovernanceDAOAddress []WDelayerEventNewHermezGovernanceDAOAddress
92}
93
94// NewWDelayerEvents creates an empty WDelayerEvents with the slices initialized.
95func NewWDelayerEvents() WDelayerEvents {
96 return WDelayerEvents{
97 Deposit: make([]WDelayerEventDeposit, 0),
98 Withdraw: make([]WDelayerEventWithdraw, 0),
99 EmergencyModeEnabled: make([]WDelayerEventEmergencyModeEnabled, 0),
100 NewWithdrawalDelay: make([]WDelayerEventNewWithdrawalDelay, 0),
101 EscapeHatchWithdrawal: make([]WDelayerEventEscapeHatchWithdrawal, 0),
102 NewHermezKeeperAddress: make([]WDelayerEventNewHermezKeeperAddress, 0),
103 NewWhiteHackGroupAddress: make([]WDelayerEventNewWhiteHackGroupAddress, 0),
104 NewHermezGovernanceDAOAddress: make([]WDelayerEventNewHermezGovernanceDAOAddress, 0),
105 }
106}
107
108// WDelayerInterface is the inteface to WithdrawalDelayer Smart Contract
109type WDelayerInterface interface {
110 //
111 // Smart Contract Methods
112 //
113
114 WDelayerGetHermezGovernanceDAOAddress() (*ethCommon.Address, error)
115 WDelayerSetHermezGovernanceDAOAddress(newAddress ethCommon.Address) (*types.Transaction, error)
116 WDelayerGetHermezKeeperAddress() (*ethCommon.Address, error)
117 WDelayerSetHermezKeeperAddress(newAddress ethCommon.Address) (*types.Transaction, error)
118 WDelayerGetWhiteHackGroupAddress() (*ethCommon.Address, error)
119 WDelayerSetWhiteHackGroupAddress(newAddress ethCommon.Address) (*types.Transaction, error)
120 WDelayerIsEmergencyMode() (bool, error)
121 WDelayerGetWithdrawalDelay() (*big.Int, error)
122 WDelayerGetEmergencyModeStartingTime() (*big.Int, error)
123 WDelayerEnableEmergencyMode() (*types.Transaction, error)
124 WDelayerChangeWithdrawalDelay(newWithdrawalDelay uint64) (*types.Transaction, error)
125 WDelayerDepositInfo(owner, token ethCommon.Address) (*big.Int, uint64)
126 WDelayerDeposit(onwer, token ethCommon.Address, amount *big.Int) (*types.Transaction, error)
127 WDelayerWithdrawal(owner, token ethCommon.Address) (*types.Transaction, error)
128 WDelayerEscapeHatchWithdrawal(to, token ethCommon.Address, amount *big.Int) (*types.Transaction, error)
129}
130
131//
132// Implementation
133//
134
135// WDelayerClient is the implementation of the interface to the WithdrawDelayer Smart Contract in ethereum.
136type WDelayerClient struct {
137 client *EthereumClient
138 address ethCommon.Address
139 gasLimit uint64
140 contractAbi abi.ABI
141}
142
143// NewWDelayerClient creates a new WDelayerClient
144func NewWDelayerClient(client *EthereumClient, address ethCommon.Address) (*WDelayerClient, error) {
145 contractAbi, err := abi.JSON(strings.NewReader(string(WithdrawalDelayer.WithdrawalDelayerABI)))
146 if err != nil {
147 return nil, err
148 }
149 return &WDelayerClient{
150 client: client,
151 address: address,
152 gasLimit: 1000000, //nolint:gomnd
153 contractAbi: contractAbi,
154 }, nil
155}
156
157// WDelayerGetHermezGovernanceDAOAddress is the interface to call the smart contract function
158func (c *WDelayerClient) WDelayerGetHermezGovernanceDAOAddress() (*ethCommon.Address, error) {
159 var hermezGovernanceDAOAddress ethCommon.Address
160 if err := c.client.Call(func(ec *ethclient.Client) error {
161 wdelayer, err := WithdrawalDelayer.NewWithdrawalDelayer(c.address, ec)
162 if err != nil {
163 return err
164 }
165 hermezGovernanceDAOAddress, err = wdelayer.GetHermezGovernanceDAOAddress(nil)
166 return err
167 }); err != nil {
168 return nil, err
169 }
170 return &hermezGovernanceDAOAddress, nil
171}
172
173// WDelayerSetHermezGovernanceDAOAddress is the interface to call the smart contract function
174func (c *WDelayerClient) WDelayerSetHermezGovernanceDAOAddress(newAddress ethCommon.Address) (*types.Transaction, error) {
175 var tx *types.Transaction
176 var err error
177 if tx, err = c.client.CallAuth(
178 c.gasLimit,
179 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
180 wdelayer, err := WithdrawalDelayer.NewWithdrawalDelayer(c.address, ec)
181 if err != nil {
182 return nil, err
183 }
184 return wdelayer.SetHermezGovernanceDAOAddress(auth, newAddress)
185 },
186 ); err != nil {
187 return nil, fmt.Errorf("Failed setting hermezGovernanceDAOAddress: %w", err)
188 }
189 return tx, nil
190}
191
192// WDelayerGetHermezKeeperAddress is the interface to call the smart contract function
193func (c *WDelayerClient) WDelayerGetHermezKeeperAddress() (*ethCommon.Address, error) {
194 var hermezKeeperAddress ethCommon.Address
195 if err := c.client.Call(func(ec *ethclient.Client) error {
196 wdelayer, err := WithdrawalDelayer.NewWithdrawalDelayer(c.address, ec)
197 if err != nil {
198 return err
199 }
200 hermezKeeperAddress, err = wdelayer.GetHermezKeeperAddress(nil)
201 return err
202 }); err != nil {
203 return nil, err
204 }
205 return &hermezKeeperAddress, nil
206}
207
208// WDelayerSetHermezKeeperAddress is the interface to call the smart contract function
209func (c *WDelayerClient) WDelayerSetHermezKeeperAddress(newAddress ethCommon.Address) (*types.Transaction, error) {
210 var tx *types.Transaction
211 var err error
212 if tx, err = c.client.CallAuth(
213 c.gasLimit,
214 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
215 wdelayer, err := WithdrawalDelayer.NewWithdrawalDelayer(c.address, ec)
216 if err != nil {
217 return nil, err
218 }
219 return wdelayer.SetHermezKeeperAddress(auth, newAddress)
220 },
221 ); err != nil {
222 return nil, fmt.Errorf("Failed setting hermezKeeperAddress: %w", err)
223 }
224 return tx, nil
225}
226
227// WDelayerGetWhiteHackGroupAddress is the interface to call the smart contract function
228func (c *WDelayerClient) WDelayerGetWhiteHackGroupAddress() (*ethCommon.Address, error) {
229 var whiteHackGroupAddress ethCommon.Address
230 if err := c.client.Call(func(ec *ethclient.Client) error {
231 wdelayer, err := WithdrawalDelayer.NewWithdrawalDelayer(c.address, ec)
232 if err != nil {
233 return err
234 }
235 whiteHackGroupAddress, err = wdelayer.GetWhiteHackGroupAddress(nil)
236 return err
237 }); err != nil {
238 return nil, err
239 }
240 return &whiteHackGroupAddress, nil
241}
242
243// WDelayerSetWhiteHackGroupAddress is the interface to call the smart contract function
244func (c *WDelayerClient) WDelayerSetWhiteHackGroupAddress(newAddress ethCommon.Address) (*types.Transaction, error) {
245 var tx *types.Transaction
246 var err error
247 if tx, err = c.client.CallAuth(
248 c.gasLimit,
249 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
250 wdelayer, err := WithdrawalDelayer.NewWithdrawalDelayer(c.address, ec)
251 if err != nil {
252 return nil, err
253 }
254 return wdelayer.SetWhiteHackGroupAddress(auth, newAddress)
255 },
256 ); err != nil {
257 return nil, fmt.Errorf("Failed setting whiteHackGroupAddress: %w", err)
258 }
259 return tx, nil
260}
261
262// WDelayerIsEmergencyMode is the interface to call the smart contract function
263func (c *WDelayerClient) WDelayerIsEmergencyMode() (bool, error) {
264 var ermergencyMode bool
265 if err := c.client.Call(func(ec *ethclient.Client) error {
266 wdelayer, err := WithdrawalDelayer.NewWithdrawalDelayer(c.address, ec)
267 if err != nil {
268 return err
269 }
270 ermergencyMode, err = wdelayer.IsEmergencyMode(nil)
271 return err
272 }); err != nil {
273 return false, err
274 }
275 return ermergencyMode, nil
276}
277
278// WDelayerGetWithdrawalDelay is the interface to call the smart contract function
279func (c *WDelayerClient) WDelayerGetWithdrawalDelay() (*big.Int, error) {
280 var withdrawalDelay *big.Int
281 if err := c.client.Call(func(ec *ethclient.Client) error {
282 wdelayer, err := WithdrawalDelayer.NewWithdrawalDelayer(c.address, ec)
283 if err != nil {
284 return err
285 }
286 withdrawalDelay, err = wdelayer.GetWithdrawalDelay(nil)
287 return err
288 }); err != nil {
289 return nil, err
290 }
291 return withdrawalDelay, nil
292}
293
294// WDelayerGetEmergencyModeStartingTime is the interface to call the smart contract function
295func (c *WDelayerClient) WDelayerGetEmergencyModeStartingTime() (*big.Int, error) {
296 var emergencyModeStartingTime *big.Int
297 if err := c.client.Call(func(ec *ethclient.Client) error {
298 wdelayer, err := WithdrawalDelayer.NewWithdrawalDelayer(c.address, ec)
299 if err != nil {
300 return err
301 }
302 emergencyModeStartingTime, err = wdelayer.GetEmergencyModeStartingTime(nil)
303 return err
304 }); err != nil {
305 return nil, err
306 }
307 return emergencyModeStartingTime, nil
308}
309
310// WDelayerEnableEmergencyMode is the interface to call the smart contract function
311func (c *WDelayerClient) WDelayerEnableEmergencyMode() (*types.Transaction, error) {
312 var tx *types.Transaction
313 var err error
314 if tx, err = c.client.CallAuth(
315 c.gasLimit,
316 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
317 wdelayer, err := WithdrawalDelayer.NewWithdrawalDelayer(c.address, ec)
318 if err != nil {
319 return nil, err
320 }
321 return wdelayer.EnableEmergencyMode(auth)
322 },
323 ); err != nil {
324 return nil, fmt.Errorf("Failed setting enable emergency mode: %w", err)
325 }
326 return tx, nil
327}
328
329// WDelayerChangeWithdrawalDelay is the interface to call the smart contract function
330func (c *WDelayerClient) WDelayerChangeWithdrawalDelay(newWithdrawalDelay uint64) (*types.Transaction, error) {
331 var tx *types.Transaction
332 var err error
333 if tx, err = c.client.CallAuth(
334 c.gasLimit,
335 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
336 wdelayer, err := WithdrawalDelayer.NewWithdrawalDelayer(c.address, ec)
337 if err != nil {
338 return nil, err
339 }
340 return wdelayer.ChangeWithdrawalDelay(auth, newWithdrawalDelay)
341 },
342 ); err != nil {
343 return nil, fmt.Errorf("Failed setting withdrawal delay: %w", err)
344 }
345 return tx, nil
346}
347
348// WDelayerDepositInfo is the interface to call the smart contract function
349func (c *WDelayerClient) WDelayerDepositInfo(owner, token ethCommon.Address) (DepositState, error) {
350 var depositInfo DepositState
351 if err := c.client.Call(func(ec *ethclient.Client) error {
352 wdelayer, err := WithdrawalDelayer.NewWithdrawalDelayer(c.address, ec)
353 if err != nil {
354 return err
355 }
356 amount, depositTimestamp, err := wdelayer.DepositInfo(nil, owner, token)
357 depositInfo.Amount = amount
358 depositInfo.DepositTimestamp = depositTimestamp
359 return err
360 }); err != nil {
361 return depositInfo, err
362 }
363 return depositInfo, nil
364}
365
366// WDelayerDeposit is the interface to call the smart contract function
367func (c *WDelayerClient) WDelayerDeposit(owner, token ethCommon.Address, amount *big.Int) (*types.Transaction, error) {
368 var tx *types.Transaction
369 var err error
370 if tx, err = c.client.CallAuth(
371 c.gasLimit,
372 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
373 wdelayer, err := WithdrawalDelayer.NewWithdrawalDelayer(c.address, ec)
374 if err != nil {
375 return nil, err
376 }
377 return wdelayer.Deposit(auth, owner, token, amount)
378 },
379 ); err != nil {
380 return nil, fmt.Errorf("Failed deposit: %w", err)
381 }
382 return tx, nil
383}
384
385// WDelayerWithdrawal is the interface to call the smart contract function
386func (c *WDelayerClient) WDelayerWithdrawal(owner, token ethCommon.Address) (*types.Transaction, error) {
387 var tx *types.Transaction
388 var err error
389 if tx, err = c.client.CallAuth(
390 c.gasLimit,
391 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
392 wdelayer, err := WithdrawalDelayer.NewWithdrawalDelayer(c.address, ec)
393 if err != nil {
394 return nil, err
395 }
396 return wdelayer.Withdrawal(auth, owner, token)
397 },
398 ); err != nil {
399 return nil, fmt.Errorf("Failed withdrawal: %w", err)
400 }
401 return tx, nil
402}
403
404// WDelayerEscapeHatchWithdrawal is the interface to call the smart contract function
405func (c *WDelayerClient) WDelayerEscapeHatchWithdrawal(to, token ethCommon.Address, amount *big.Int) (*types.Transaction, error) {
406 var tx *types.Transaction
407 var err error
408 if tx, err = c.client.CallAuth(
409 c.gasLimit,
410 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
411 wdelayer, err := WithdrawalDelayer.NewWithdrawalDelayer(c.address, ec)
412 if err != nil {
413 return nil, err
414 }
415 return wdelayer.EscapeHatchWithdrawal(auth, to, token, amount)
416 },
417 ); err != nil {
418 return nil, fmt.Errorf("Failed escapeHatchWithdrawal: %w", err)
419 }
420 return tx, nil
421}
422
423var (
424 logWDelayerDeposit = crypto.Keccak256Hash([]byte("Deposit(address,address,uint192,uint64)"))
425 logWDelayerWithdraw = crypto.Keccak256Hash([]byte("Withdraw(address,address,uint192)"))
426 logWDelayerEmergencyModeEnabled = crypto.Keccak256Hash([]byte("EmergencyModeEnabled()"))
427 logWDelayerNewWithdrawalDelay = crypto.Keccak256Hash([]byte("NewWithdrawalDelay(uint64)"))
428 logWDelayerEscapeHatchWithdrawal = crypto.Keccak256Hash([]byte("EscapeHatchWithdrawal(address,address,address,uint256)"))
429 logWDelayerNewHermezKeeperAddress = crypto.Keccak256Hash([]byte("NewHermezKeeperAddress(address)"))
430 logWDelayerNewWhiteHackGroupAddress = crypto.Keccak256Hash([]byte("NewWhiteHackGroupAddress(address)"))
431 logWDelayerNewHermezGovernanceDAOAddress = crypto.Keccak256Hash([]byte("NewHermezGovernanceDAOAddress(address)"))
432)
433
434// WDelayerEventsByBlock returns the events in a block that happened in the
435// WDelayer Smart Contract and the blockHash where the eents happened. If
436// there are no events in that block, blockHash is nil.
437func (c *WDelayerClient) WDelayerEventsByBlock(blockNum int64) (*WDelayerEvents, *ethCommon.Hash, error) {
438 var wdelayerEvents WDelayerEvents
439 var blockHash ethCommon.Hash
440
441 query := ethereum.FilterQuery{
442 FromBlock: big.NewInt(blockNum),
443 ToBlock: big.NewInt(blockNum),
444 Addresses: []ethCommon.Address{
445 c.address,
446 },
447 BlockHash: nil,
448 Topics: [][]ethCommon.Hash{},
449 }
450
451 logs, err := c.client.client.FilterLogs(context.Background(), query)
452 if err != nil {
453 return nil, nil, err
454 }
455 if len(logs) > 0 {
456 blockHash = logs[0].BlockHash
457 }
458 for _, vLog := range logs {
459 if vLog.BlockHash != blockHash {
460 return nil, nil, ErrBlockHashMismatchEvent
461 }
462 switch vLog.Topics[0] {
463 case logWDelayerDeposit:
464 var deposit WDelayerEventDeposit
465 err := c.contractAbi.Unpack(&deposit, "Deposit", vLog.Data)
466 if err != nil {
467 return nil, nil, err
468 }
469 deposit.Owner = ethCommon.BytesToAddress(vLog.Topics[1].Bytes())
470 deposit.Token = ethCommon.BytesToAddress(vLog.Topics[2].Bytes())
471 wdelayerEvents.Deposit = append(wdelayerEvents.Deposit, deposit)
472
473 case logWDelayerWithdraw:
474 var withdraw WDelayerEventWithdraw
475 err := c.contractAbi.Unpack(&withdraw, "Withdraw", vLog.Data)
476 if err != nil {
477 return nil, nil, err
478 }
479 withdraw.Token = ethCommon.BytesToAddress(vLog.Topics[1].Bytes())
480 withdraw.Owner = ethCommon.BytesToAddress(vLog.Topics[2].Bytes())
481 wdelayerEvents.Withdraw = append(wdelayerEvents.Withdraw, withdraw)
482
483 case logWDelayerEmergencyModeEnabled:
484 var emergencyModeEnabled WDelayerEventEmergencyModeEnabled
485 wdelayerEvents.EmergencyModeEnabled = append(wdelayerEvents.EmergencyModeEnabled, emergencyModeEnabled)
486
487 case logWDelayerNewWithdrawalDelay:
488 var withdrawalDelay WDelayerEventNewWithdrawalDelay
489 err := c.contractAbi.Unpack(&withdrawalDelay, "NewWithdrawalDelay", vLog.Data)
490 if err != nil {
491 return nil, nil, err
492 }
493 wdelayerEvents.NewWithdrawalDelay = append(wdelayerEvents.NewWithdrawalDelay, withdrawalDelay)
494
495 case logWDelayerEscapeHatchWithdrawal:
496 var escapeHatchWithdrawal WDelayerEventEscapeHatchWithdrawal
497 err := c.contractAbi.Unpack(&escapeHatchWithdrawal, "EscapeHatchWithdrawal", vLog.Data)
498 if err != nil {
499 return nil, nil, err
500 }
501 escapeHatchWithdrawal.Who = ethCommon.BytesToAddress(vLog.Topics[1].Bytes())
502 escapeHatchWithdrawal.To = ethCommon.BytesToAddress(vLog.Topics[2].Bytes())
503 escapeHatchWithdrawal.Token = ethCommon.BytesToAddress(vLog.Topics[3].Bytes())
504 wdelayerEvents.EscapeHatchWithdrawal = append(wdelayerEvents.EscapeHatchWithdrawal, escapeHatchWithdrawal)
505
506 case logWDelayerNewHermezKeeperAddress:
507 var keeperAddress WDelayerEventNewHermezKeeperAddress
508 err := c.contractAbi.Unpack(&keeperAddress, "NewHermezKeeperAddress", vLog.Data)
509 if err != nil {
510 return nil, nil, err
511 }
512 wdelayerEvents.NewHermezKeeperAddress = append(wdelayerEvents.NewHermezKeeperAddress, keeperAddress)
513
514 case logWDelayerNewWhiteHackGroupAddress:
515 var whiteHackGroupAddress WDelayerEventNewWhiteHackGroupAddress
516 err := c.contractAbi.Unpack(&whiteHackGroupAddress, "NewWhiteHackGroupAddress", vLog.Data)
517 if err != nil {
518 return nil, nil, err
519 }
520 wdelayerEvents.NewWhiteHackGroupAddress = append(wdelayerEvents.NewWhiteHackGroupAddress, whiteHackGroupAddress)
521
522 case logWDelayerNewHermezGovernanceDAOAddress:
523 var governanceDAOAddress WDelayerEventNewHermezGovernanceDAOAddress
524 err := c.contractAbi.Unpack(&governanceDAOAddress, "NewHermezGovernanceDAOAddress", vLog.Data)
525 if err != nil {
526 return nil, nil, err
527 }
528 wdelayerEvents.NewHermezGovernanceDAOAddress = append(wdelayerEvents.NewHermezGovernanceDAOAddress, governanceDAOAddress)
529 }
530 }
531 return &wdelayerEvents, &blockHash, nil
532}