hopli/
methods.rs

1//! This module contains all the methods used for onchain interaction, especially with Safe instance, Mutlicall, and
2//! Multisend contracts.
3//!
4//! [SafeTxOperation] corresponds to the `Operation` Enum used in Safe smart contract.
5//!
6//! [MultisendTransaction] struct is used for building transactions interacting with Multisend contract
7
8#![allow(clippy::too_many_arguments)]
9
10use std::{ops::Add, str::FromStr, sync::Arc};
11
12use IMulticall3Extract::IMulticall3ExtractInstance;
13use SafeSingleton::{SafeSingletonInstance, execTransactionCall, removeOwnerCall, setupCall};
14use hex_literal::hex;
15use hopr_bindings::{
16    exports::alloy::{
17        network::TransactionBuilder,
18        primitives::{Address, B256, Bytes, U256, keccak256, utils::format_units},
19        providers::{
20            CallInfoTrait, CallItem, MULTICALL3_ADDRESS, MulticallBuilder, MulticallError, Provider, WalletProvider,
21            bindings::IMulticall3::{Call3, Call3Value, aggregate3Call, aggregate3ValueCall},
22        },
23        rpc::types::TransactionRequest,
24        signers::{Signer, local::PrivateKeySigner},
25        sol,
26        sol_types::{SolCall, SolValue},
27    },
28    hopr_node_management_module::HoprNodeManagementModule::{
29        HoprNodeManagementModuleInstance, addChannelsAndTokenTargetCall, includeNodeCall, initializeCall,
30        removeNodeCall, scopeTargetTokenCall,
31    },
32    hopr_node_safe_registry::HoprNodeSafeRegistry::{HoprNodeSafeRegistryInstance, deregisterNodeBySafeCall},
33    hopr_node_stake_factory::HoprNodeStakeFactory::{HoprNodeStakeFactoryInstance, cloneCall},
34    hopr_token::HoprToken::{HoprTokenInstance, approveCall},
35};
36use hopr_crypto_types::keypairs::{ChainKeypair, Keypair};
37use tracing::{debug, info};
38
39use crate::{
40    constants::{
41        DEFAULT_ANNOUNCEMENT_PERMISSIONS, DEFAULT_CAPABILITY_PERMISSIONS, DEFAULT_NODE_PERMISSIONS,
42        DOMAIN_SEPARATOR_TYPEHASH, ERC_1967_PROXY_CREATION_CODE, ETH_VALUE_FOR_MULTICALL3_DEPLOYER,
43        MULTICALL3_DEPLOYER, SAFE_COMPATIBILITYFALLBACKHANDLER_ADDRESS, SAFE_MULTISEND_ADDRESS, SAFE_SAFE_L2_ADDRESS,
44        SAFE_SAFEPROXYFACTORY_ADDRESS, SAFE_TX_TYPEHASH, SENTINEL_OWNERS,
45    },
46    utils::{HelperErrors, get_create2_address},
47};
48
49sol!(
50    #![sol(abi)]
51    #![sol(rpc)]
52    contract SafeSingleton {
53        event ExecutionSuccess(bytes32 indexed txHash, uint256 payment);
54
55        function setup(address[],uint256,address,bytes,address,address,uint256,address);
56        function execTransaction(address to, uint256 value, bytes calldata data, uint8 operation, uint256 safeTxGas, uint256 baseGas, uint256 gasPrice, address gasToken, address payable refundReceiver, bytes memory signatures) public payable returns (bool);
57        function removeOwner(address prevOwner, address owner, uint256 _threshold) public;
58        function getThreshold() public view returns (uint256);
59        function getOwners() public view returns (address[] memory);
60        function nonce() public view returns (uint256);
61        function domainSeparator() public view returns (bytes32);
62        function encodeTransactionData(address to, uint256 value, bytes calldata data, uint8 operation, uint256 safeTxGas, uint256 baseGas, uint256 gasPrice, address gasToken, address refundReceiver, uint256 _nonce) public view returns (bytes memory);
63        function getTransactionHash(address to, uint256 value, bytes calldata data, uint8 operation, uint256 safeTxGas, uint256 baseGas, uint256 gasPrice, address gasToken, address refundReceiver, uint256 _nonce) public view returns (bytes32);
64        function isModuleEnabled(address module) public view returns (bool);
65    }
66);
67
68sol!(
69    #![sol(abi)]
70    #![sol(rpc)]
71    contract ModuleSingleton {
72        function isNode(address) external view returns (bool);
73        function getTargets() external view returns (uint256[] memory);
74        function owner() public view returns (address);
75    }
76);
77
78sol!(
79    #![sol(abi)]
80    #![sol(rpc)]
81    function multiSend(bytes memory transactions) public payable;
82);
83
84sol!(
85    #![sol(abi)]
86    #![sol(rpc)]
87    interface IMulticall3Extract {
88        function getEthBalance(address addr) external view returns (uint256 balance);
89    }
90);
91
92/// Enums of Safe transaction operation
93#[derive(Debug, Clone, PartialEq, Eq)]
94pub enum SafeTxOperation {
95    Call,
96    DelegateCall,
97}
98impl SafeTxOperation {
99    /// convert the SafeTxOperation to exact one byte
100    pub fn to_byte(&self) -> [u8; 1] {
101        match self {
102            SafeTxOperation::Call => hex!("00"),
103            SafeTxOperation::DelegateCall => hex!("01"),
104        }
105    }
106}
107
108impl From<SafeTxOperation> for u8 {
109    fn from(s: SafeTxOperation) -> u8 {
110        s as u8
111    }
112}
113
114/// Struct to make a multisend transaction, mainly used by safe instances
115#[derive(Debug, Clone)]
116pub struct MultisendTransaction {
117    // data paylaod encoded with selector
118    pub encoded_data: Bytes,
119    // transaction type
120    pub tx_operation: SafeTxOperation,
121    // target address
122    pub to: Address,
123    // payable eth sending along the tx
124    pub value: U256,
125}
126
127/// Methods for Multisend transaction
128impl MultisendTransaction {
129    /// encode a multisend transaction
130    fn encode_packed(&self) -> Vec<u8> {
131        let tx_operation_bytes: Bytes = self.tx_operation.to_byte().into();
132
133        let value = (
134            tx_operation_bytes,                  // 1 bytes
135            self.to,                             // 20 bytes
136            U256::from(self.value),              // 32 bytes
137            U256::from(self.encoded_data.len()), // 32 bytes
138            self.encoded_data.clone(),           // bytes
139        );
140        value.abi_encode_packed()
141    }
142
143    /// build a multisend transaction data payload
144    fn build_multisend_tx(transactions: Vec<MultisendTransaction>) -> Vec<u8> {
145        let mut payload: Vec<u8> = Vec::new();
146        for transaction in transactions {
147            payload = [payload, transaction.encode_packed()].concat();
148        }
149        debug!("payload {:?}", hex::encode(&payload));
150        payload
151    }
152}
153
154/// get the domain separator of a safe instance
155/// contract_address should be safe address
156fn get_domain_separator(chain_id: U256, contract_address: Address) -> [u8; 32] {
157    keccak256(
158        (
159            B256::from_str(DOMAIN_SEPARATOR_TYPEHASH)
160                .unwrap_or_else(|_| panic!("decode the DOMAIN_SEPARATOR_TYPEHASH")), // DOMAIN_SEPARATOR_TYPEHASH
161            chain_id,         // getChainId
162            contract_address, // this
163        )
164            .abi_encode(),
165    )
166    .into()
167}
168
169/// Implement getTransactionHash() function as in vendor/solidity/safe-contracts-1.4.1/contracts/Safe.sol
170/// Note that `safeTxGas`, `baseGas`, and `gasPrice` are zero; `gasToken` is also address zero
171fn get_safe_transaction_hash(
172    to: Address,
173    value: U256,
174    data: Vec<u8>,
175    operation: SafeTxOperation,
176    refund_address: Address,
177    nonce: U256,
178    domain_separator: [u8; 32],
179) -> [u8; 32] {
180    // first encodeTransactionData()
181    let data_hash = keccak256(data);
182
183    let encoded = (
184        B256::from_str(SAFE_TX_TYPEHASH).unwrap_or_else(|_| panic!("failed to decode the SAFE_TX_TYPEHASH")), // SAFE_TX_TYPEHASH
185        to,                                                                                                   // to
186        value,                                                                                                // value
187        data_hash,                   // keccak256
188        U256::from(operation as u8), // operation
189        U256::ZERO,                  // safeTxGas
190        U256::ZERO,                  // baseGas
191        U256::ZERO,                  // gasPrice
192        Address::ZERO,               // gasToken
193        refund_address,              // refundReceiver
194        nonce,                       // _nonce
195    )
196        .abi_encode();
197
198    let safe_hash = keccak256(encoded);
199
200    let encoded_transaction_data = (hex!("1901"), domain_separator, safe_hash).abi_encode_packed();
201
202    let transaction_hash = keccak256(encoded_transaction_data);
203    debug!("transaction_hash {:?}", hex::encode(transaction_hash));
204    transaction_hash.0
205}
206
207/// Use safe to delegatecall to multisend contract
208/// Note that when no additional signature is provided, the safe must have a threshold of one,
209/// so that the transaction can be executed.
210/// Note that the refund address is the caller (safe owner) wallet
211pub async fn send_multisend_safe_transaction_with_threshold_one<P: WalletProvider + Provider>(
212    safe: SafeSingletonInstance<Arc<P>>,
213    signer_key: ChainKeypair,
214    multisend_contract: Address,
215    multisend_txns: Vec<MultisendTransaction>,
216    chain_id: U256,
217    nonce: U256,
218) -> Result<(), HelperErrors> {
219    // get signer
220    let signer = safe.provider().default_signer_address();
221    // let signer = safe.client().default_sender().expect("client must have a sender");
222    let wallet = PrivateKeySigner::from_slice(signer_key.secret().as_ref()).expect("failed to construct wallet");
223
224    // prepare a safe transaction:
225    // 1. calculate total value
226    let total_value = multisend_txns
227        .clone()
228        .into_iter()
229        .fold(U256::ZERO, |acc, cur| acc.add(cur.value));
230    // 2. prepare tx payload
231    let tx_payload = MultisendTransaction::build_multisend_tx(multisend_txns);
232    let multisend_payload = multiSendCall {
233        transactions: tx_payload.into(),
234    }
235    .abi_encode();
236    // 3. get domain separator
237    let domain_separator = get_domain_separator(chain_id, *safe.address());
238
239    debug!("multisend_payload {:?}", hex::encode(&multisend_payload));
240
241    // get transaction hash
242    let transaction_hash = get_safe_transaction_hash(
243        multisend_contract,
244        total_value,
245        multisend_payload.clone(),
246        SafeTxOperation::DelegateCall,
247        signer,
248        nonce,
249        domain_separator,
250    );
251
252    // sign the transaction
253    let signature = wallet
254        .sign_hash(&B256::from_slice(&transaction_hash))
255        .await
256        .unwrap_or_else(|_| panic!("failed to sign a transaction hash"));
257    debug!("signature {:?}", hex::encode(signature.as_bytes()));
258
259    // execute the transaction
260    let tx_receipt = safe
261        .execTransaction(
262            multisend_contract,
263            total_value,
264            multisend_payload.into(),
265            SafeTxOperation::DelegateCall.into(),
266            U256::ZERO,
267            U256::ZERO,
268            U256::ZERO,
269            Address::ZERO,
270            signer,
271            Bytes::from(signature.as_bytes()),
272        )
273        .send()
274        .await?
275        // .unwrap_or_else(|_| panic!("failed to exeute a pending transaction"))
276        .get_receipt()
277        .await?;
278
279    tx_receipt
280        .decoded_log::<SafeSingleton::ExecutionSuccess>()
281        .ok_or(HelperErrors::MultiSendError)?;
282    Ok(())
283}
284
285/// Deploy a MULTICALL contract into Anvil local chain for testing
286pub async fn deploy_multicall3_for_testing<P: Provider>(provider: Arc<P>) -> Result<(), HelperErrors> {
287    // check if the multicall3 contract is already deployed. If deployed, skip all
288    let code = provider.get_code_at(MULTICALL3_ADDRESS).await?;
289    if code != Bytes::default() {
290        info!(
291            "Multicall3 contract is already deployed at address {:?}",
292            MULTICALL3_ADDRESS
293        );
294        return Ok(());
295    }
296
297    // Fund Multicall3 deployer and deploy ERC1820Registry
298    let tx = TransactionRequest::default()
299        .with_to(MULTICALL3_DEPLOYER)
300        .with_value(ETH_VALUE_FOR_MULTICALL3_DEPLOYER);
301
302    provider
303        .send_transaction(tx)
304        .await?
305        .watch()
306        // .map_err(|e| ContractError::MiddlewareError { e })?
307        .await?;
308
309    // provider.send_raw_transaction(
310    //     &hex!("f90f538085174876e800830f42408080b90f00608060405234801561001057600080fd5b50610ee0806100206000396000f3fe6080604052600436106100f35760003560e01c80634d2301cc1161008a578063a8b0574e11610059578063a8b0574e1461025a578063bce38bd714610275578063c3077fa914610288578063ee82ac5e1461029b57600080fd5b80634d2301cc146101ec57806372425d9d1461022157806382ad56cb1461023457806386d516e81461024757600080fd5b80633408e470116100c65780633408e47014610191578063399542e9146101a45780633e64a696146101c657806342cbb15c146101d957600080fd5b80630f28c97d146100f8578063174dea711461011a578063252dba421461013a57806327e86d6e1461015b575b600080fd5b34801561010457600080fd5b50425b6040519081526020015b60405180910390f35b61012d610128366004610a85565b6102ba565b6040516101119190610bbe565b61014d610148366004610a85565b6104ef565b604051610111929190610bd8565b34801561016757600080fd5b50437fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0140610107565b34801561019d57600080fd5b5046610107565b6101b76101b2366004610c60565b610690565b60405161011193929190610cba565b3480156101d257600080fd5b5048610107565b3480156101e557600080fd5b5043610107565b3480156101f857600080fd5b50610107610207366004610ce2565b73ffffffffffffffffffffffffffffffffffffffff163190565b34801561022d57600080fd5b5044610107565b61012d610242366004610a85565b6106ab565b34801561025357600080fd5b5045610107565b34801561026657600080fd5b50604051418152602001610111565b61012d610283366004610c60565b61085a565b6101b7610296366004610a85565b610a1a565b3480156102a757600080fd5b506101076102b6366004610d18565b4090565b60606000828067ffffffffffffffff8111156102d8576102d8610d31565b60405190808252806020026020018201604052801561031e57816020015b6040805180820190915260008152606060208201528152602001906001900390816102f65790505b5092503660005b8281101561047757600085828151811061034157610341610d60565b6020026020010151905087878381811061035d5761035d610d60565b905060200281019061036f9190610d8f565b6040810135958601959093506103886020850185610ce2565b73ffffffffffffffffffffffffffffffffffffffff16816103ac6060870187610dcd565b6040516103ba929190610e32565b60006040518083038185875af1925050503d80600081146103f7576040519150601f19603f3d011682016040523d82523d6000602084013e6103fc565b606091505b50602080850191909152901515808452908501351761046d577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260846000fd5b5050600101610325565b508234146104e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4d756c746963616c6c333a2076616c7565206d69736d6174636800000000000060448201526064015b60405180910390fd5b50505092915050565b436060828067ffffffffffffffff81111561050c5761050c610d31565b60405190808252806020026020018201604052801561053f57816020015b606081526020019060019003908161052a5790505b5091503660005b8281101561068657600087878381811061056257610562610d60565b90506020028101906105749190610e42565b92506105836020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff166105a66020850185610dcd565b6040516105b4929190610e32565b6000604051808303816000865af19150503d80600081146105f1576040519150601f19603f3d011682016040523d82523d6000602084013e6105f6565b606091505b5086848151811061060957610609610d60565b602090810291909101015290508061067d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064016104dd565b50600101610546565b5050509250929050565b43804060606106a086868661085a565b905093509350939050565b6060818067ffffffffffffffff8111156106c7576106c7610d31565b60405190808252806020026020018201604052801561070d57816020015b6040805180820190915260008152606060208201528152602001906001900390816106e55790505b5091503660005b828110156104e657600084828151811061073057610730610d60565b6020026020010151905086868381811061074c5761074c610d60565b905060200281019061075e9190610e76565b925061076d6020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff166107906040850185610dcd565b60405161079e929190610e32565b6000604051808303816000865af19150503d80600081146107db576040519150601f19603f3d011682016040523d82523d6000602084013e6107e0565b606091505b506020808401919091529015158083529084013517610851577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260646000fd5b50600101610714565b6060818067ffffffffffffffff81111561087657610876610d31565b6040519080825280602002602001820160405280156108bc57816020015b6040805180820190915260008152606060208201528152602001906001900390816108945790505b5091503660005b82811015610a105760008482815181106108df576108df610d60565b602002602001015190508686838181106108fb576108fb610d60565b905060200281019061090d9190610e42565b925061091c6020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff1661093f6020850185610dcd565b60405161094d929190610e32565b6000604051808303816000865af19150503d806000811461098a576040519150601f19603f3d011682016040523d82523d6000602084013e61098f565b606091505b506020830152151581528715610a07578051610a07576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064016104dd565b506001016108c3565b5050509392505050565b6000806060610a2b60018686610690565b919790965090945092505050565b60008083601f840112610a4b57600080fd5b50813567ffffffffffffffff811115610a6357600080fd5b6020830191508360208260051b8501011115610a7e57600080fd5b9250929050565b60008060208385031215610a9857600080fd5b823567ffffffffffffffff811115610aaf57600080fd5b610abb85828601610a39565b90969095509350505050565b6000815180845260005b81811015610aed57602081850181015186830182015201610ad1565b81811115610aff576000602083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b600082825180855260208086019550808260051b84010181860160005b84811015610bb1578583037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001895281518051151584528401516040858501819052610b9d81860183610ac7565b9a86019a9450505090830190600101610b4f565b5090979650505050505050565b602081526000610bd16020830184610b32565b9392505050565b600060408201848352602060408185015281855180845260608601915060608160051b870101935082870160005b82811015610c52577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0888703018452610c40868351610ac7565b95509284019290840190600101610c06565b509398975050505050505050565b600080600060408486031215610c7557600080fd5b83358015158114610c8557600080fd5b9250602084013567ffffffffffffffff811115610ca157600080fd5b610cad86828701610a39565b9497909650939450505050565b838152826020820152606060408201526000610cd96060830184610b32565b95945050505050565b600060208284031215610cf457600080fd5b813573ffffffffffffffffffffffffffffffffffffffff81168114610bd157600080fd5b600060208284031215610d2a57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112610dc357600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112610e0257600080fd5b83018035915067ffffffffffffffff821115610e1d57600080fd5b602001915036819003821315610a7e57600080fd5b8183823760009101908152919050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1833603018112610dc357600080fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa1833603018112610dc357600080fdfea2646970667358221220bb2b5c71a328032f97c676ae39a1ec2148d3e5d6f73d95e9b17910152d61f16264736f6c634300080c00331ca0edce47092c0f398cebf3ffc267f05c8e7076e3b89445e0fe50f6332273d4569ba01b0b9d000e19b24c5869b0fc3b22b0d6fa47cd63316875cbbd577d76e6fde086")
311    //         ).await?
312    //         .watch()
313    //         // .map_err(|e| ContractError::MiddlewareError {e})?
314    //         .await?;
315    Ok(())
316}
317
318/// Get chain id and safe nonce
319pub async fn get_chain_id_and_safe_nonce<P: Provider>(
320    safe: SafeSingletonInstance<P>,
321) -> Result<(U256, U256), HelperErrors> {
322    let provider = safe.provider();
323    let multicall = provider.multicall().get_chain_id().add(safe.nonce());
324    let (get_chain_id_return, nonce_return) = multicall.aggregate().await?;
325
326    Ok((get_chain_id_return, nonce_return))
327}
328
329/// Get native balance and hopr token balance for given addresses
330pub async fn get_native_and_token_balances<P: Provider>(
331    hopr_token: HoprTokenInstance<P>,
332    addresses: Vec<Address>,
333) -> Result<(Vec<U256>, Vec<U256>), MulticallError> {
334    let provider = hopr_token.provider();
335    let multicall3_instance = IMulticall3ExtractInstance::new(MULTICALL3_ADDRESS, provider);
336
337    // if there is less than two addresses, use multicall3 on each address
338    // otherwise, make multicall on all addresses
339    if addresses.is_empty() {
340        Ok((vec![], vec![]))
341    } else if addresses.len() == 1 {
342        let address = addresses[0];
343        let multicall = provider
344            .multicall()
345            .get_eth_balance(address)
346            .add(hopr_token.balanceOf(address));
347
348        let (native_balance, token_balance) = multicall.aggregate().await?;
349        Ok((vec![native_balance], vec![token_balance]))
350    } else {
351        let mut native_balances_multicall = MulticallBuilder::new_dynamic(provider);
352        let mut token_balances_multicall = MulticallBuilder::new_dynamic(provider);
353
354        for address in addresses {
355            native_balances_multicall =
356                native_balances_multicall.add_dynamic(multicall3_instance.getEthBalance(address));
357            token_balances_multicall = token_balances_multicall.add_dynamic(hopr_token.balanceOf(address));
358            // balances_multicall.add_call(hopr_token.balanceOf(address));
359        }
360
361        let native_balances_return = native_balances_multicall.aggregate().await?;
362        let token_balances_return = token_balances_multicall.aggregate().await?;
363
364        Ok((native_balances_return, token_balances_return))
365    }
366}
367
368/// Transfer some HOPR tokens from the caller to the list of addresses
369/// Address_i receives amounts_i HOPR tokens.
370/// When there's not enough token in caller's balance, if the caller is
371/// a minter, mint the missing tokens. If not, returns error
372///
373/// Attention! Do not use this function to distribute large amount of tokens
374///
375/// Note that to save gas in batch funding, we use multicall to facilitate token distribution via `transferFrom`
376/// To use this functionality, caller must grant Multicall3 contract the exact allowance equal to the sum of tokens
377/// to be transferred. As it's a separate function, there is a window between granting the allowance and executing
378/// the transactin. Attacker may take advantage of this window and steal tokens from the caller's account.
379///
380/// TODO: To mitigate this risk, create a MulticallErc777Recipient contract to enable receiption of tokens
381/// on the multicall contract and purposely re-entrance with forwarded payload
382pub async fn transfer_or_mint_tokens<P: Provider + WalletProvider>(
383    hopr_token: HoprTokenInstance<Arc<P>>,
384    addresses: Vec<Address>,
385    amounts: Vec<U256>,
386) -> Result<U256, HelperErrors> {
387    let provider = hopr_token.provider();
388    let caller = hopr_token.provider().default_signer_address();
389
390    // check if two vectors have the same length
391    assert_eq!(
392        addresses.len(),
393        amounts.len(),
394        "addresses and amounts are of different lengths in transfer_or_mint_tokens"
395    );
396
397    // early return if no recipient is provided
398    if addresses.is_empty() {
399        return Ok(U256::ZERO);
400    }
401
402    // calculate the sum of tokens to be sent
403    let total = amounts.iter().fold(U256::ZERO, |acc, cur| acc.add(cur));
404    info!("total amount of HOPR tokens to be transferred {:?}", total.to_string());
405
406    // get caller balance and its role
407    let encoded_minter_role = keccak256(b"MINTER_ROLE");
408    let multicall = provider
409        .multicall()
410        .add(
411            hopr_token.balanceOf(caller), /* .method::<_, U256>("balanceOf", caller)
412                                           * .map_err(|e| HelperErrors::MulticallError(e.to_string()))?,
413                                           * false, */
414        )
415        .add(
416            hopr_token.hasRole(encoded_minter_role, caller), /* hopr_token
417                                                              *     .method::<_, bool>("hasRole",
418                                                              * (encoded_minter_role, caller))
419                                                              *     .map_err(|e|
420                                                              * HelperErrors::MulticallError(e.to_string()))?,
421                                                              * false, */
422        );
423    let (token_balance_return, has_role_return) = multicall.aggregate().await?;
424
425    // compare the total with caller's current balance. If caller doens't have enough balance, try to mint some.
426    // Otherwise, revert
427    if total.gt(&token_balance_return) {
428        info!("caller does not have enough balance to transfer tokens to recipients.");
429        if has_role_return {
430            info!("caller tries to mint tokens");
431            hopr_token
432                .mint(caller, total, Bytes::default(), Bytes::default())
433                .send()
434                .await?
435                // .unwrap_or_else(|_| panic!("failed to exeute a pending transaction"))
436                .watch()
437                .await?;
438            // .unwrap_or_else(|_| panic!("failed to resolve a transaction receipt"));
439        } else {
440            return Err(HelperErrors::NotAMinter);
441        }
442    }
443
444    // when there are multiple recipients, use multicall; when single recipient, direct transfer
445    if addresses.len() == 1 {
446        info!("doing direct transfer...");
447
448        // direct transfer
449        hopr_token
450            .transfer(addresses[0], amounts[0])
451            .send()
452            .await?
453            // .unwrap_or_else(|_| panic!("failed to exeute a pending transaction"))
454            .watch()
455            .await?;
456        // .unwrap_or_else(|_| panic!("failed to resolve a transaction receipt"));
457    } else {
458        info!("using multicall...");
459        // use multicall
460        // TODO: introduce a new ERC777Recipient contract and batch the following separated steps into one, to mitigate
461        // the attack vector approve the multicall to be able to transfer from caller's wallet
462        hopr_token
463            .approve(MULTICALL3_ADDRESS, total)
464            .send()
465            .await?
466            // .unwrap_or_else(|_| panic!("failed to exeute a pending transaction"))
467            .watch()
468            .await?;
469
470        let calls: Vec<Call3> = addresses
471            .clone()
472            .into_iter()
473            .enumerate()
474            .map(|(i, addr)| {
475                let calldata = hopr_token.transferFrom(caller, addr, amounts[i]);
476                let call = Call3 {
477                    target: *hopr_token.address(),
478                    allowFailure: false,
479                    callData: calldata.calldata().clone(),
480                };
481                call
482            })
483            .collect::<Vec<_>>();
484        let aggregate3_payload = aggregate3Call { calls }.abi_encode();
485        let tx = TransactionRequest::default()
486            .with_to(MULTICALL3_ADDRESS)
487            .with_input(aggregate3_payload);
488        provider.send_transaction(tx).await?.watch().await?;
489    }
490
491    Ok(total)
492}
493
494/// Transfer some native tokens from the caller to the list of addresses
495/// Address_i receives amounts_i native tokens.
496pub async fn transfer_native_tokens<P: Provider + WalletProvider>(
497    provider: Arc<P>,
498    addresses: Vec<Address>,
499    amounts: Vec<U256>,
500) -> Result<U256, HelperErrors> {
501    // check if two vectors have the same length
502    assert_eq!(
503        addresses.len(),
504        amounts.len(),
505        "addresses and amounts are of different lengths in transfer_native_tokens"
506    );
507    // calculate the sum of tokens to be sent
508    let total = amounts.iter().fold(U256::ZERO, |acc, cur| acc.add(cur));
509    info!(
510        "total amount of native tokens to be transferred {:?}",
511        total.to_string()
512    );
513
514    let calls: Vec<Call3Value> = addresses
515        .clone()
516        .into_iter()
517        .enumerate()
518        .map(|(i, addr)| Call3Value {
519            target: addr,
520            allowFailure: false,
521            value: amounts[i],
522            callData: Bytes::default(),
523        })
524        .collect::<Vec<_>>();
525    let aggregate3_value_payload = aggregate3ValueCall { calls }.abi_encode();
526    let tx = TransactionRequest::default()
527        .with_to(MULTICALL3_ADDRESS)
528        .with_input(aggregate3_value_payload)
529        .with_value(total);
530    provider.send_transaction(tx).await?.watch().await?;
531    Ok(total)
532}
533
534/// Helper function to predict module address. Note that here the caller is the contract deployer
535/// FIXME: The result mismatch from predicted module address from smart contract
536pub fn predict_module_address(
537    caller: Address,
538    nonce: B256,
539    safe_address: Address,
540    announcement_address: Address,
541    factory_address: Address,
542    default_target: U256,
543    implementation_address: Address,
544) -> Result<Address, HelperErrors> {
545    let module_salt = keccak256((caller, nonce).abi_encode_packed());
546    // debug!("module_salt {:?}", module_salt);
547
548    let default_announcement_target =
549        U256::from_str(format!("{announcement_address:?}{DEFAULT_ANNOUNCEMENT_PERMISSIONS}").as_str()).unwrap();
550
551    let initialize_parameters = (
552        safe_address,
553        SAFE_MULTISEND_ADDRESS,
554        default_announcement_target,
555        default_target,
556    )
557        .abi_encode();
558
559    let encode_initialization = initializeCall {
560        initParams: initialize_parameters.into(),
561    }
562    .abi_encode();
563
564    let erc1967_initialize_code = (implementation_address, encode_initialization).abi_encode()[32..].to_vec();
565    debug!("erc1967_initialize_code {:?}", hex::encode(&erc1967_initialize_code));
566
567    let module_creation_code = (
568        Bytes::copy_from_slice(ERC_1967_PROXY_CREATION_CODE),
569        erc1967_initialize_code,
570    )
571        .abi_encode_packed();
572    debug!("module_creation_code {:?}", hex::encode(&module_creation_code));
573    debug!(
574        "module_creation_code_hash {:?}",
575        hex::encode(keccak256(&module_creation_code))
576    );
577
578    let predict_module_addr = get_create2_address(factory_address, module_salt, keccak256(&module_creation_code));
579    debug!("predict_module_addr {:?}", predict_module_addr);
580
581    Ok(predict_module_addr)
582}
583
584/// Helper function to predict safe address
585pub fn predict_safe_address(
586    stake_factory: Address,
587    admins: Vec<Address>,
588    nonce: B256,
589    safe_fallback: Address,
590    safe_singleton: Address,
591    safe_factory: Address,
592) -> Result<Address, HelperErrors> {
593    let mut temp_admins = admins.clone();
594    temp_admins.push(stake_factory);
595
596    let initializer = setupCall {
597        _0: temp_admins,
598        _1: U256::ONE,
599        _2: Address::ZERO,
600        _3: Bytes::from(hex!("00")),
601        _4: safe_fallback,
602        _5: Address::ZERO,
603        _6: U256::ZERO,
604        _7: Address::ZERO,
605    }
606    .abi_encode();
607
608    let safe_salt = get_salt_from_salt_nonce(initializer, nonce)?;
609    debug!("safe_salt {:?}", hex::encode(safe_salt));
610
611    let predict_safe_addr = deploy_proxy(safe_singleton, safe_salt, safe_factory)?;
612    debug!("predict_safe_addr {:?}", hex::encode(predict_safe_addr));
613
614    Ok(predict_safe_addr)
615}
616
617/// helper function to get salt nonce
618fn get_salt_from_salt_nonce(initializer: Vec<u8>, salt_nonce: B256) -> Result<[u8; 32], HelperErrors> {
619    let hashed_initializer = keccak256(initializer);
620    let encoded = (hashed_initializer, salt_nonce).abi_encode_packed();
621
622    Ok(keccak256(encoded).into())
623}
624
625/// helper function to compute create2 safe proxy address
626fn deploy_proxy(safe_singleton: Address, safe_salt: [u8; 32], safe_factory: Address) -> Result<Address, HelperErrors> {
627    let safe_creation_code = (
628        Bytes::from_static(&hex!("608060405234801561001057600080fd5b506040516101e63803806101e68339818101604052602081101561003357600080fd5b8101908080519060200190929190505050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156100ca576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806101c46022913960400191505060405180910390fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505060ab806101196000396000f3fe608060405273ffffffffffffffffffffffffffffffffffffffff600054167fa619486e0000000000000000000000000000000000000000000000000000000060003514156050578060005260206000f35b3660008037600080366000845af43d6000803e60008114156070573d6000fd5b3d6000f3fea264697066735822122003d1488ee65e08fa41e58e888a9865554c535f2c77126a82cb4c0f917f31441364736f6c63430007060033496e76616c69642073696e676c65746f6e20616464726573732070726f7669646564")),
629        Bytes::from_static(&hex!("000000000000000000000000")),    // pad address to bytes32
630        safe_singleton,
631    ).abi_encode_packed();
632    debug!("safe_creation_code {:?}", hex::encode(safe_creation_code.clone()));
633
634    let predict_safe_addr = get_create2_address(safe_factory, safe_salt, safe_creation_code);
635    debug!("predict_safe_addr {:?}", hex::encode(predict_safe_addr));
636
637    Ok(predict_safe_addr.0.into())
638}
639
640pub fn prepare_safe_tx_multicall_payload_from_owner_contract(
641    deployed_safe: Address,
642    target: Address,
643    refund_address: Address,
644    tx_payload: Vec<u8>,
645) -> CallItem<execTransactionCall> {
646    let approval_hash_sig = (
647        Bytes::from_static(&hex!("000000000000000000000000")),
648        MULTICALL3_ADDRESS,
649        Bytes::from_static(&hex!(
650            "0000000000000000000000000000000000000000000000000000000000000000"
651        )),
652        Bytes::from_static(&hex!("01")),
653    )
654        .abi_encode_packed();
655
656    let input = execTransactionCall {
657        to: target,
658        value: U256::ZERO,
659        data: Bytes::from(tx_payload),
660        operation: 0u8,
661        safeTxGas: U256::ZERO,
662        baseGas: U256::ZERO,
663        gasPrice: U256::ZERO,
664        gasToken: Address::ZERO,
665        refundReceiver: refund_address,
666        signatures: Bytes::from(approval_hash_sig),
667    }
668    .abi_encode();
669
670    CallItem::<execTransactionCall>::new(deployed_safe, input.into())
671}
672
673/// Deploy a safe and a module proxies via v4 HoprStakeFactory contract with default permissions and announcement
674/// targets With the multicall contract, it deploys a safe proxy instance and a module proxy instance with multicall as
675/// an owner, and completes necessary setup.
676/// Then the multicall includes some additional steps:
677/// 1. if node addresses are known, include nodes to the module by safe
678/// 2. transfer safe ownership to actual admins
679/// 3. set desired threshold
680///
681/// Returns safe proxy address and module proxy address
682#[allow(clippy::too_many_arguments)]
683pub async fn deploy_safe_module_with_targets_and_nodes<P: WalletProvider + Provider>(
684    hopr_node_stake_factory: HoprNodeStakeFactoryInstance<Arc<P>>,
685    hopr_channels_address: Address,
686    node_addresses: Option<Vec<Address>>,
687    admins: Vec<Address>,
688    threshold: U256,
689) -> Result<(SafeSingletonInstance<Arc<P>>, HoprNodeManagementModuleInstance<Arc<P>>), HelperErrors> {
690    let caller = hopr_node_stake_factory.provider().default_signer_address();
691    let provider = hopr_node_stake_factory.provider();
692
693    // check safes owners are provided and threshold is valid
694    assert!(!admins.is_empty(), "safe must have valid admin(s)");
695    assert!(
696        threshold.ge(&U256::ONE) && threshold.le(&U256::from(admins.len())),
697        "safe threshold must be at least one and not greater than the total number of admins"
698    );
699    assert!(
700        !admins.contains(&MULTICALL3_ADDRESS),
701        "multicall contract cannot be an admin"
702    );
703
704    // build a new temporary admin
705    let mut temporary_admins: Vec<Address> = admins.clone();
706    temporary_admins.insert(0, MULTICALL3_ADDRESS);
707    info!(
708        "temporary_admins expends from admin from {:?} addresses to {:?}",
709        admins.len(),
710        temporary_admins.len()
711    );
712
713    // build the default permissions of capabilities
714    let default_target =
715    // let default_target: [u8; 32] =
716        U256::from_str(format!("{hopr_channels_address:?}{DEFAULT_CAPABILITY_PERMISSIONS}").as_str())
717            .unwrap();
718    debug!("default target {:?}", default_target);
719    // salt nonce
720    let curr_nonce = provider
721        .get_transaction_count(caller)
722        .pending()
723        .await
724        .map_err(|e| HelperErrors::MiddlewareError(e.to_string()))?;
725    let nonce = keccak256((caller, U256::from(curr_nonce)).abi_encode_packed());
726
727    debug!("curr_nonce {} and nonce {:?}", curr_nonce, nonce);
728
729    let safe_address = predict_safe_address(
730        *hopr_node_stake_factory.address(),
731        temporary_admins.clone(),
732        nonce,
733        SAFE_COMPATIBILITYFALLBACKHANDLER_ADDRESS,
734        SAFE_SAFE_L2_ADDRESS,
735        SAFE_SAFEPROXYFACTORY_ADDRESS,
736    )?;
737    debug!("predicted safe address {:?}", safe_address.to_string());
738
739    let module_address = hopr_node_stake_factory
740        .predictModuleAddress_1(MULTICALL3_ADDRESS, nonce.into(), safe_address, default_target.into())
741        .call()
742        .await?;
743    debug!("predicted module address {:?}", module_address.to_string());
744
745    let deployed_module = HoprNodeManagementModuleInstance::new(module_address, provider.clone());
746    let deployed_safe = SafeSingleton::new(safe_address, provider.clone());
747
748    // Use multicall to deploy a safe proxy instance and a module proxy instance with multicall as an owner
749    let mut multicall_payloads: Vec<Call3> = vec![];
750    let safe_address = *deployed_safe.address();
751    multicall_payloads.push(Call3 {
752        target: *hopr_node_stake_factory.address(),
753        allowFailure: false,
754        callData: cloneCall {
755            nonce: nonce.into(),
756            defaultTarget: default_target.into(),
757            admins: temporary_admins,
758        }
759        .abi_encode()
760        .into(),
761    });
762    info!("Safe and module deployment multicall payload is created");
763
764    // if node addresses are known, include nodes to the module by safe
765    if let Some(nodes) = node_addresses {
766        for node in nodes {
767            let node_target =
768                U256::from_str(&format!("{node:?}{DEFAULT_NODE_PERMISSIONS}")).expect("Invalid node_target format");
769
770            let encoded_call = includeNodeCall {
771                nodeDefaultTarget: node_target,
772            }
773            .abi_encode();
774
775            let payload = prepare_safe_tx_multicall_payload_from_owner_contract(
776                safe_address,
777                module_address,
778                caller,
779                encoded_call,
780            );
781
782            multicall_payloads.push(payload.to_call3());
783        }
784
785        info!("Nodes inclusion multicall payload is created");
786    } else {
787        info!("No node has been provided. Skip node inclusion action for multicall payload generation");
788    }
789
790    // renounce ownership granted to multicall so that only actual admins are included. Set the threshold.
791    let remove_owner_tx_payload = removeOwnerCall {
792        prevOwner: Address::from_str(SENTINEL_OWNERS).unwrap(),
793        owner: MULTICALL3_ADDRESS,
794        _threshold: threshold,
795    }
796    .abi_encode();
797
798    let multicall_payload_5 = prepare_safe_tx_multicall_payload_from_owner_contract(
799        safe_address,
800        safe_address,
801        caller,
802        remove_owner_tx_payload,
803    );
804    // let multicall = multicall.add_call(multicall_payload_5);
805
806    multicall_payloads.push(multicall_payload_5.to_call3());
807    info!("Admins and threshold setting multicall payload is created");
808
809    // build multicall transaction
810    let aggregate3_payload = aggregate3Call {
811        calls: multicall_payloads,
812    }
813    .abi_encode();
814    let tx = TransactionRequest::default()
815        .with_to(MULTICALL3_ADDRESS)
816        .with_input(aggregate3_payload);
817    let tx_receipt = provider.send_transaction(tx).await?.get_receipt().await?;
818    info!("multicall is sent {:?}", tx_receipt.transaction_hash.to_string());
819
820    let safe_address_from_log = tx_receipt
821        .decoded_log::<hopr_bindings::hopr_node_stake_factory::HoprNodeStakeFactory::NewHoprNodeStakeSafe>()
822        .ok_or_else(|| HelperErrors::ContractNotDeployed("cannot find safe from log".into()))?
823        .instance;
824    let module_address_from_log = tx_receipt
825        .decoded_log::<hopr_bindings::hopr_node_stake_factory::HoprNodeStakeFactory::NewHoprNodeStakeModule>()
826        .ok_or_else(|| HelperErrors::ContractNotDeployed("cannot find module from log".into()))?
827        .instance;
828    info!("tx_receipt {:?}", tx_receipt);
829
830    assert_eq!(
831        safe_address,
832        safe_address_from_log,
833        "safe address mismatch: predicted {:?} actual {:?}",
834        safe_address.to_string(),
835        safe_address_from_log.to_string(),
836    );
837    assert_eq!(
838        module_address,
839        module_address_from_log,
840        "module address mismatch: predicted {:?} actual {:?}",
841        module_address.to_string(),
842        module_address_from_log.to_string(),
843    );
844    Ok((deployed_safe, deployed_module))
845}
846
847/// Get registered safes for given nodes on the node-safe registry
848pub async fn get_registered_safes_for_nodes_on_node_safe_registry<P: Provider>(
849    node_safe_registry: HoprNodeSafeRegistryInstance<P>,
850    node_addresses: Vec<Address>,
851) -> Result<Vec<Address>, MulticallError> {
852    let provider = node_safe_registry.provider();
853    let mut dyn_multicall = MulticallBuilder::new_dynamic(provider);
854
855    for node in node_addresses {
856        dyn_multicall = dyn_multicall.add_dynamic(node_safe_registry.nodeToSafe(node));
857    }
858
859    let native_balances_return = dyn_multicall.aggregate().await?;
860
861    Ok(native_balances_return)
862}
863
864/// Deregister safes and nodes from the node-safe registry.
865/// It returns the number of removed nodes
866/// - If nodes have been registered to a safe, remove the node
867/// - If nodes have not been registered to any safe, no op
868///
869/// When deregsitering one node, also remove the node from the module
870pub async fn deregister_nodes_from_node_safe_registry_and_remove_from_module<P: WalletProvider + Provider>(
871    node_safe_registry: HoprNodeSafeRegistryInstance<Arc<P>>,
872    node_addresses: Vec<Address>,
873    module_addresses: Vec<Address>,
874    owner_chain_key: ChainKeypair,
875) -> Result<u32, HelperErrors> {
876    let provider = node_safe_registry.provider();
877    // check registered safes of given node addresses
878    let registered_safes =
879        get_registered_safes_for_nodes_on_node_safe_registry(node_safe_registry.clone(), node_addresses.clone())
880            .await
881            .unwrap();
882
883    let mut nodes_to_remove_counter = 0u32;
884
885    for (i, registered_safe) in registered_safes.iter().enumerate() {
886        if registered_safe.ne(&Address::ZERO) {
887            // connect to safe
888            let safe = SafeSingleton::new(registered_safe.to_owned(), provider.clone());
889            // update counter
890            nodes_to_remove_counter += 1;
891            // get chain id and nonce
892            let (chain_id, safe_nonce) = get_chain_id_and_safe_nonce(safe.clone()).await?;
893
894            // for each safe, prepare a multisend transaction to dergister node from safe and remove node from module
895            let multisend_txns: Vec<MultisendTransaction> = vec![
896                MultisendTransaction {
897                    // build multisend tx payload
898                    encoded_data: deregisterNodeBySafeCall {
899                        nodeAddr: node_addresses[i],
900                    }
901                    .abi_encode()
902                    .into(),
903                    tx_operation: SafeTxOperation::Call,
904                    to: *node_safe_registry.address(),
905                    value: U256::ZERO,
906                },
907                MultisendTransaction {
908                    // build multisend tx payload
909                    encoded_data: removeNodeCall {
910                        nodeAddress: node_addresses[i],
911                    }
912                    .abi_encode()
913                    .into(),
914                    tx_operation: SafeTxOperation::Call,
915                    to: module_addresses[i],
916                    value: U256::ZERO,
917                },
918            ];
919
920            // send safe transaction
921            send_multisend_safe_transaction_with_threshold_one(
922                safe,
923                owner_chain_key.clone(),
924                SAFE_MULTISEND_ADDRESS,
925                multisend_txns,
926                chain_id,
927                safe_nonce,
928            )
929            .await?;
930        }
931    }
932
933    Ok(nodes_to_remove_counter)
934}
935
936/// Include nodes to the module
937pub async fn include_nodes_to_module<P: WalletProvider + Provider>(
938    safe: SafeSingletonInstance<Arc<P>>,
939    node_addresses: Vec<Address>,
940    module_address: Address,
941    owner_chain_key: ChainKeypair,
942) -> Result<(), HelperErrors> {
943    // get chain id and nonce
944    let (chain_id, safe_nonce) = get_chain_id_and_safe_nonce(safe.clone()).await?;
945
946    // prepare a multisend transaction to include each node to the  module
947    let mut multisend_txns: Vec<MultisendTransaction> = Vec::new();
948    for node_address in node_addresses {
949        let node_target = U256::from_str(format!("{node_address:?}{DEFAULT_NODE_PERMISSIONS}").as_str()).unwrap();
950        multisend_txns.push(MultisendTransaction {
951            encoded_data: includeNodeCall {
952                nodeDefaultTarget: node_target,
953            }
954            .abi_encode()
955            .into(),
956            tx_operation: SafeTxOperation::Call,
957            to: module_address,
958            value: U256::ZERO,
959        });
960    }
961
962    // send safe transaction
963    send_multisend_safe_transaction_with_threshold_one(
964        safe,
965        owner_chain_key.clone(),
966        SAFE_MULTISEND_ADDRESS,
967        multisend_txns,
968        chain_id,
969        safe_nonce,
970    )
971    .await?;
972
973    Ok(())
974}
975
976/// Migrate nodes to be able to run in a new network.
977// - scope the Channel contract of the new network to the module as target and set default permissions.
978// - scope the Announcement contract as target to the module
979// - approve HOPR tokens of the Safe proxy to be transferred by the new Channels contract
980pub async fn migrate_nodes<P: WalletProvider + Provider>(
981    safe: SafeSingletonInstance<Arc<P>>,
982    module_addresses: Address,
983    channels_address: Address,
984    token_address: Address,
985    announcement_address: Address,
986    allowance: U256,
987    owner_chain_key: ChainKeypair,
988) -> Result<(), HelperErrors> {
989    let (chain_id, safe_nonce) = get_chain_id_and_safe_nonce(safe.clone()).await?;
990
991    let mut multisend_txns: Vec<MultisendTransaction> = Vec::new();
992
993    // scope channels and tokens contract of the network
994    let default_target =
995        U256::from_str(format!("{channels_address:?}{DEFAULT_CAPABILITY_PERMISSIONS}").as_str()).unwrap();
996    debug!("default target {:?}", default_target);
997
998    multisend_txns.push(MultisendTransaction {
999        // build multisend tx payload
1000        encoded_data: addChannelsAndTokenTargetCall {
1001            defaultTarget: default_target,
1002        }
1003        .abi_encode()
1004        .into(),
1005        tx_operation: SafeTxOperation::Call,
1006        to: module_addresses,
1007        value: U256::ZERO,
1008    });
1009
1010    // scope announcement contract of the new network
1011    let announcement_target =
1012        U256::from_str(format!("{announcement_address:?}{DEFAULT_ANNOUNCEMENT_PERMISSIONS}").as_str()).unwrap();
1013
1014    multisend_txns.push(MultisendTransaction {
1015        // build multisend tx payload
1016        encoded_data: scopeTargetTokenCall {
1017            defaultTarget: announcement_target,
1018        }
1019        .abi_encode()
1020        .into(),
1021        tx_operation: SafeTxOperation::Call,
1022        to: module_addresses,
1023        value: U256::ZERO,
1024    });
1025
1026    // approve token transfer by the new Channels contract
1027    multisend_txns.push(MultisendTransaction {
1028        // build multisend tx payload
1029        encoded_data: approveCall {
1030            spender: channels_address,
1031            value: allowance,
1032        }
1033        .abi_encode()
1034        .into(),
1035        tx_operation: SafeTxOperation::Call,
1036        to: token_address,
1037        value: U256::ZERO,
1038    });
1039
1040    // send safe transaction
1041    send_multisend_safe_transaction_with_threshold_one(
1042        safe,
1043        owner_chain_key.clone(),
1044        SAFE_MULTISEND_ADDRESS,
1045        multisend_txns,
1046        chain_id,
1047        safe_nonce,
1048    )
1049    .await?;
1050
1051    Ok(())
1052}
1053
1054/// Quick check if the following values are correct, for one single node:
1055/// 1. node xDAI balance
1056/// 2. If node and safe are associated on Node Safe Registry
1057pub async fn debug_node_safe_module_setup_on_balance_and_registries<P: Provider>(
1058    node_safe_registry: HoprNodeSafeRegistryInstance<Arc<P>>,
1059    node_address: &Address,
1060) -> Result<Address, MulticallError> {
1061    let provider = node_safe_registry.provider();
1062    // let mut multicall = Multicall::new(provider.clone(), Some(MULTICALL_ADDRESS))
1063    //     .await
1064    //     .expect("cannot create multicall");
1065
1066    info!("checking for node {:?}", node_address);
1067    let multicall = provider
1068        .multicall()
1069        // 1. node xDAI balance
1070        .get_eth_balance(*node_address)
1071        // 2. get the safe address from the Node Safe Registry
1072        .add(node_safe_registry.nodeToSafe(*node_address));
1073
1074    let (node_native_balance, safe_in_nodesafe_registry) = multicall.aggregate().await?;
1075
1076    info!(
1077        "node does{:?} have xDAI balance {:?}",
1078        if node_native_balance.ge(
1079            &U256::from_str("100000000000000000").unwrap() // 0.1 ether
1080        ) {
1081            ""
1082        } else {
1083            " NOT"
1084        },
1085        format_units(node_native_balance, "ether").unwrap_or("Unknown balance".into())
1086    );
1087
1088    if safe_in_nodesafe_registry.eq(&Address::ZERO) {
1089        info!("Please start the node. It will auto-register to node-safe registry");
1090    } else {
1091        info!("safe in node-safe registry {:?}", safe_in_nodesafe_registry);
1092    }
1093
1094    Ok(safe_in_nodesafe_registry)
1095}
1096
1097/// Quick check if the following values are correct, for one single node:
1098/// 4. If Safe is owned by the correct owner(s)
1099/// 5. Safe’s wxHOPR balance and allowance
1100/// 6. if the module is enabled
1101/// 7. if node is included in the module
1102/// 8. Get all the targets of the safe (then check if channel and announcement are there)
1103/// 9. Get the owner of the module
1104pub async fn debug_node_safe_module_setup_main<P: Provider>(
1105    hopr_token: HoprTokenInstance<Arc<P>>,
1106    module_address: &Address,
1107    node_address: &Address,
1108    safe_address: &Address,
1109    channel_address: &Address,
1110    announce_address: &Address,
1111) -> Result<(), MulticallError> {
1112    let provider = hopr_token.provider();
1113
1114    let safe = SafeSingleton::new(safe_address.to_owned(), provider.clone());
1115    let module = ModuleSingleton::new(module_address.to_owned(), provider.clone());
1116
1117    info!("checking for safe {:?} module {:?}", safe_address, module_address);
1118    let multicall = provider
1119        .multicall()
1120        // 4. get owners of the safe
1121        .add(safe.getOwners())
1122        // 5.a. get the wxHOPR balance for the safe address
1123        .add(hopr_token.balanceOf(*safe_address))
1124        // 5.b. get the wxHOPR balance for the safe address
1125        .add(hopr_token.allowance(*safe_address, *channel_address))
1126        // 6. if the module is enabled
1127        .add(safe.isModuleEnabled(*module_address))
1128        // 7. if node is included in the module
1129        .add(module.isNode(*node_address))
1130        // 7. get targets of the safe
1131        .add(module.getTargets())
1132        // 8. get owner of the module
1133        .add(module.owner());
1134
1135    let (
1136        safe_owners,
1137        safe_wxhopr_balance,
1138        safe_wxhopr_allownace,
1139        is_module_enabled,
1140        is_node_included,
1141        module_targets,
1142        module_owner,
1143    ) = multicall.aggregate().await?;
1144
1145    info!("safe has owners: {:?}", safe_owners);
1146    info!(
1147        "safe has wxHOPR balance: {:?}",
1148        format_units(safe_wxhopr_balance, "ether").unwrap_or("Unknown balance".into())
1149    );
1150    info!(
1151        "safe has wxHOPR allowance: {:?}",
1152        format_units(safe_wxhopr_allownace, "ether").unwrap_or("Unknown balance".into())
1153    );
1154    info!("module is enabled: {:?}", is_module_enabled);
1155    info!("node is included in the module: {:?}", is_node_included);
1156    info!("module has targets:");
1157    for target in module_targets {
1158        let target_address = format!("{target:#x}");
1159        let has_channels = target_address.contains(&format!("{channel_address:#x}"));
1160        let has_announcement = target_address.contains(&format!("{announce_address:#x}"));
1161        // check if it contains channel and announcement
1162        info!(
1163            "Target {:?} has channels {:?} has announcement {:?}",
1164            target_address, has_channels, has_announcement
1165        );
1166    }
1167
1168    info!(
1169        "module owner: {:?} same as safe address: {:?}",
1170        module_owner,
1171        module_owner.eq(safe_address)
1172    );
1173    Ok(())
1174}
1175
1176#[cfg(test)]
1177mod tests {
1178    use std::vec;
1179
1180    use hopr_bindings::{
1181        exports::alloy::{
1182            contract::Result as ContractResult,
1183            network::{EthereumWallet, TransactionBuilder},
1184            primitives::address,
1185            providers::{Identity, RootProvider, fillers::*},
1186            rpc::types::TransactionRequest,
1187            sol_types::SolValue,
1188        },
1189        hopr_announcements::HoprAnnouncements,
1190        hopr_channels::HoprChannels,
1191        hopr_node_safe_registry::HoprNodeSafeRegistry,
1192        hopr_node_stake_factory::HoprNodeStakeFactory,
1193        hopr_token::HoprToken,
1194    };
1195    use hopr_crypto_types::keypairs::{ChainKeypair, Keypair};
1196    use hopr_primitive_types::prelude::BytesRepresentable;
1197    use tracing_subscriber::{EnvFilter, Registry, fmt, prelude::*};
1198
1199    use super::*;
1200    use crate::utils::{ContractInstances, a2h, create_anvil};
1201
1202    pub type AnvilRpcClient = FillProvider<
1203        JoinFill<
1204            JoinFill<Identity, JoinFill<GasFiller, JoinFill<BlobGasFiller, JoinFill<NonceFiller, ChainIdFiller>>>>,
1205            WalletFiller<EthereumWallet>,
1206        >,
1207        RootProvider,
1208    >;
1209
1210    fn init_tracing() {
1211        // Use RUST_LOG if set, otherwise default to "debug" for verbose test output
1212        let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("debug"));
1213
1214        // Match main.rs formatting style
1215        let format = fmt::layer()
1216            .with_level(true)
1217            .with_target(true)
1218            .with_thread_ids(true)
1219            .with_thread_names(false)
1220            .with_test_writer(); // ensures logs show up in `cargo test`
1221
1222        // Set the global subscriber (harmless no-op if already initialized)
1223        let _ = Registry::default().with(env_filter).with(format).try_init();
1224    }
1225
1226    /// Used for testing. Creates RPC client to the local Anvil instance.
1227    pub fn create_rpc_client_to_anvil(
1228        anvil: &hopr_bindings::exports::alloy::node_bindings::AnvilInstance,
1229        signer: &hopr_crypto_types::keypairs::ChainKeypair,
1230    ) -> Arc<AnvilRpcClient> {
1231        use hopr_bindings::exports::alloy::{
1232            providers::ProviderBuilder, rpc::client::ClientBuilder, signers::local::PrivateKeySigner,
1233            transports::http::ReqwestTransport,
1234        };
1235        use hopr_crypto_types::keypairs::Keypair;
1236
1237        let wallet = PrivateKeySigner::from_slice(signer.secret().as_ref()).expect("failed to construct wallet");
1238
1239        let transport_client = ReqwestTransport::new(anvil.endpoint_url());
1240
1241        let rpc_client = ClientBuilder::default().transport(transport_client.clone(), transport_client.guess_local());
1242
1243        let provider = ProviderBuilder::new().wallet(wallet).connect_client(rpc_client);
1244
1245        Arc::new(provider)
1246    }
1247
1248    fn get_random_address_for_testing() -> Address {
1249        // Creates a random Ethereum address, only used for testing
1250        Address::new(hopr_crypto_random::random_bytes::<
1251            { hopr_primitive_types::primitives::Address::SIZE },
1252        >())
1253    }
1254
1255    async fn deploy_safe_suites<P: Provider>(provider: Arc<P>) -> ContractResult<()> {
1256        // Check if safe suite has been deployed. If so, skip this step
1257        let code = provider
1258            .get_code_at(address!("0x914d7Fec6aaC8cd542e72Bca78B30650d45643d7"))
1259            .await?;
1260        // .map_err(|e| ContractError::MiddlewareError { e })?;
1261
1262        // only deploy contracts when needed
1263        if code.is_empty() {
1264            debug!("deploying safe code");
1265            // Deploy Safe diamond deployment proxy singleton
1266            let safe_diamond_proxy_address = {
1267                // Fund Safe singleton deployer 0.01 anvil-eth and deploy Safe singleton
1268                let tx = TransactionRequest::default()
1269                    .with_to(address!("E1CB04A0fA36DdD16a06ea828007E35e1a3cBC37"))
1270                    .with_value(U256::from(10000000000000000u128));
1271
1272                provider.send_transaction(tx).await?.watch().await?;
1273
1274                let tx = provider.send_raw_transaction(
1275            &hex!("f8a78085174876e800830186a08080b853604580600e600039806000f350fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf382f4f5a00dc4d1d21b308094a30f5f93da35e4d72e99115378f135f2295bea47301a3165a0636b822daad40aa8c52dd5132f378c0c0e6d83b4898228c7e21c84e631a0b891")
1276                ).await?.get_receipt()
1277                .await?;
1278                // .unwrap();
1279                tx.contract_address.unwrap()
1280            };
1281            debug!("Safe diamond proxy singleton {:?}", safe_diamond_proxy_address);
1282
1283            // Deploy minimum Safe suite
1284            // 1. Safe proxy factory deploySafeProxyFactory();
1285            let _tx_safe_proxy_factory = TransactionRequest::default().with_to(safe_diamond_proxy_address).with_input(
1286            hex!("0000000000000000000000000000000000000000000000000000000000000000608060405234801561001057600080fd5b50610bee806100206000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c80631688f0b91461005c5780633408e4701461016b57806353e5d93514610189578063d18af54d1461020c578063ec9e80bb1461033b575b600080fd5b61013f6004803603606081101561007257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001906401000000008111156100af57600080fd5b8201836020820111156100c157600080fd5b803590602001918460018302840111640100000000831117156100e357600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019092919050505061044a565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101736104fe565b6040518082815260200191505060405180910390f35b61019161050b565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101d15780820151818401526020810190506101b6565b50505050905090810190601f1680156101fe5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61030f6004803603608081101561022257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561025f57600080fd5b82018360208201111561027157600080fd5b8035906020019184600183028401116401000000008311171561029357600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050919291929080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610536565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61041e6004803603606081101561035157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561038e57600080fd5b8201836020820111156103a057600080fd5b803590602001918460018302840111640100000000831117156103c257600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290803590602001909291905050506106e5565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008083805190602001208360405160200180838152602001828152602001925050506040516020818303038152906040528051906020012090506104908585836107a8565b91508173ffffffffffffffffffffffffffffffffffffffff167f4f51faf6c4561ff95f067657e43439f0f856d97c04d9ec9070a6199ad418e23586604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a2509392505050565b6000804690508091505090565b60606040518060200161051d906109c5565b6020820181038252601f19601f82011660405250905090565b6000808383604051602001808381526020018273ffffffffffffffffffffffffffffffffffffffff1660601b8152601401925050506040516020818303038152906040528051906020012060001c905061059186868361044a565b9150600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16146106dc578273ffffffffffffffffffffffffffffffffffffffff16631e52b518838888886040518563ffffffff1660e01b8152600401808573ffffffffffffffffffffffffffffffffffffffff1681526020018473ffffffffffffffffffffffffffffffffffffffff16815260200180602001838152602001828103825284818151815260200191508051906020019080838360005b83811015610674578082015181840152602081019050610659565b50505050905090810190601f1680156106a15780820380516001836020036101000a031916815260200191505b5095505050505050600060405180830381600087803b1580156106c357600080fd5b505af11580156106d7573d6000803e3d6000fd5b505050505b50949350505050565b6000808380519060200120836106f96104fe565b60405160200180848152602001838152602001828152602001935050505060405160208183030381529060405280519060200120905061073a8585836107a8565b91508173ffffffffffffffffffffffffffffffffffffffff167f4f51faf6c4561ff95f067657e43439f0f856d97c04d9ec9070a6199ad418e23586604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a2509392505050565b60006107b3846109b2565b610825576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f53696e676c65746f6e20636f6e7472616374206e6f74206465706c6f7965640081525060200191505060405180910390fd5b600060405180602001610837906109c5565b6020820181038252601f19601f820116604052508573ffffffffffffffffffffffffffffffffffffffff166040516020018083805190602001908083835b602083106108985780518252602082019150602081019050602083039250610875565b6001836020036101000a038019825116818451168082178552505050505050905001828152602001925050506040516020818303038152906040529050828151826020016000f59150600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415610984576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f437265617465322063616c6c206661696c65640000000000000000000000000081525060200191505060405180910390fd5b6000845111156109aa5760008060008651602088016000875af114156109a957600080fd5b5b509392505050565b600080823b905060008111915050919050565b6101e6806109d38339019056fe608060405234801561001057600080fd5b506040516101e63803806101e68339818101604052602081101561003357600080fd5b8101908080519060200190929190505050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156100ca576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806101c46022913960400191505060405180910390fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505060ab806101196000396000f3fe608060405273ffffffffffffffffffffffffffffffffffffffff600054167fa619486e0000000000000000000000000000000000000000000000000000000060003514156050578060005260206000f35b3660008037600080366000845af43d6000803e60008114156070573d6000fd5b3d6000f3fea264697066735822122003d1488ee65e08fa41e58e888a9865554c535f2c77126a82cb4c0f917f31441364736f6c63430007060033496e76616c69642073696e676c65746f6e20616464726573732070726f7669646564a26469706673582212200fd975ca8e62d9bf08aa3d09c74b9bdc9d7acba7621835be4187989ddd0e54b164736f6c63430007060033")
1287        );
1288            // 2. Handler: only CompatibilityFallbackHandler and omit TokenCallbackHandler as it's not used now
1289            // 2. Hanlder: deploy Safe ExtensibleFallbackHandler, v1.5.0
1290            let _tx_safe_compatibility_fallback_handler = TransactionRequest::default().with_to(safe_diamond_proxy_address).with_input(
1291            hex!("0000000000000000000000000000000000000000000000000000000000000000608060405234801561001057600080fd5b50612d8b806100206000396000f3fe608060405234801561001057600080fd5b50600436106100d35760003560e01c806351cad5ee1161008c5780637f73528b116100665780637f73528b14610ac5578063b435a13b14610b1e578063bc197c8114610ba1578063f23a6e6114610d37576100d4565b806351cad5ee1461095457806361f5401b146109cc57806364f95acc14610a6e576100d4565b806301ffc9a7146105d45780630a3fe05414610637578063150b7a02146106b85780631626ba7e146107ae578063327b533c146108645780633365582c14610906576100d4565b5b6000366060601860003690501015610154576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260178152602001807f696e76616c6964206d6574686f642073656c6563746f7200000000000000000081525060200191505060405180910390fd5b600080600080610162610e37565b9350935093509350600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561020d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6d6574686f642068616e646c6572206e6f74207365740000000000000000000081525060200191505060405180910390fd5b81156103ed578073ffffffffffffffffffffffffffffffffffffffff166325d6803f85856000803660009060146000369050039261024d93929190612d22565b6040518663ffffffff1660e01b8152600401808673ffffffffffffffffffffffffffffffffffffffff1681526020018573ffffffffffffffffffffffffffffffffffffffff168152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050965050505050505060006040518083038186803b1580156102ed57600080fd5b505afa158015610301573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250602081101561032b57600080fd5b810190808051604051939291908464010000000082111561034b57600080fd5b8382019150602082018581111561036157600080fd5b825186600182028301116401000000008211171561037e57600080fd5b8083526020830192505050908051906020019080838360005b838110156103b2578082015181840152602081019050610397565b50505050905090810190601f1680156103df5780820380516001836020036101000a031916815260200191505b5060405250505094506105c5565b8073ffffffffffffffffffffffffffffffffffffffff166325d6803f85856000803660009060146000369050039261042793929190612d22565b6040518663ffffffff1660e01b8152600401808673ffffffffffffffffffffffffffffffffffffffff1681526020018573ffffffffffffffffffffffffffffffffffffffff168152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f8201169050808301925050509650505050505050600060405180830381600087803b1580156104c957600080fd5b505af11580156104dd573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250602081101561050757600080fd5b810190808051604051939291908464010000000082111561052757600080fd5b8382019150602082018581111561053d57600080fd5b825186600182028301116401000000008211171561055a57600080fd5b8083526020830192505050908051906020019080838360005b8381101561058e578082015181840152602081019050610573565b50505050905090810190601f1680156105bb5780820380516001836020036101000a031916815260200191505b5060405250505094505b50505050915050805190602001f35b61061f600480360360208110156105ea57600080fd5b8101908080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190505050610f14565b60405180821515815260200191505060405180910390f35b6106a26004803603604081101561064d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690602001909291905050506110a0565b6040518082815260200191505060405180910390f35b610779600480360360808110156106ce57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019064010000000081111561073557600080fd5b82018360208201111561074757600080fd5b8035906020019184600183028401116401000000008311171561076957600080fd5b90919293919293905050506110c5565b60405180827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200191505060405180910390f35b61082f600480360360408110156107c457600080fd5b8101908080359060200190929190803590602001906401000000008111156107eb57600080fd5b8201836020820111156107fd57600080fd5b8035906020019184600183028401116401000000008311171561081f57600080fd5b90919293919293905050506110e2565b60405180827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200191505060405180910390f35b6109046004803603604081101561087a57600080fd5b8101908080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190803590602001906401000000008111156108c057600080fd5b8201836020820111156108d257600080fd5b803590602001918460208302840111640100000000831117156108f457600080fd5b90919293919293905050506115ff565b005b6109526004803603604081101561091c57600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506117e9565b005b6109a06004803603604081101561096a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050611a38565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610a6c600480360360408110156109e257600080fd5b8101908080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916906020019092919080359060200190640100000000811115610a2857600080fd5b820183602082011115610a3a57600080fd5b80359060200191846020830284011164010000000083111715610a5c57600080fd5b9091929391929390505050611a7a565b005b610ac360048036036040811015610a8457600080fd5b8101908080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916906020019092919080359060200190929190505050611c97565b005b610b1c60048036036040811015610adb57600080fd5b8101908080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190803515159060200190929190505050611d5c565b005b610b8960048036036040811015610b3457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690602001909291905050506120b9565b60405180821515815260200191505060405180910390f35b610d02600480360360a0811015610bb757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190640100000000811115610c1457600080fd5b820183602082011115610c2657600080fd5b80359060200191846020830284011164010000000083111715610c4857600080fd5b909192939192939080359060200190640100000000811115610c6957600080fd5b820183602082011115610c7b57600080fd5b80359060200191846020830284011164010000000083111715610c9d57600080fd5b909192939192939080359060200190640100000000811115610cbe57600080fd5b820183602082011115610cd057600080fd5b80359060200191846001830284011164010000000083111715610cf257600080fd5b90919293919293905050506120e8565b60405180827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200191505060405180910390f35b610e02600480360360a0811015610d4d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019092919080359060200190640100000000811115610dbe57600080fd5b820183602082011115610dd057600080fd5b80359060200191846001830284011164010000000083111715610df257600080fd5b9091929391929390505050612108565b60405180827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200191505060405180910390f35b600080600080610e45612126565b8094508195505050610f066000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600080357fffffffff00000000000000000000000000000000000000000000000000000000167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002054612141565b809250819350505090919293565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161480610fdf57507f98c8e097000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b80610fef5750610fee82612169565b5b806110995750600260006110016123db565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060009054906101000a900460ff165b9050919050565b6000602052816000526040600020602052806000526040600020600091509150505481565b60006110cf6123e3565b63150b7a0260e01b905095945050505050565b60008060006110ef612126565b91509150600485859050106115a457600085359050635fd7e97d60e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614801561115c575060448686905010155b156115a257600080878760049060449261117893929190612d22565b604081101561118657600080fd5b810190808035906020019092919080359060200190929190505050915091506000600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600084815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161461159e576000808a8a600490809261126493929190612d22565b608081101561127257600080fd5b810190808035906020019092919080359060200190929190803590602001906401000000008111156112a357600080fd5b8201836020820111156112b557600080fd5b803590602001918460018302840111640100000000831117156112d757600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019064010000000081111561133a57600080fd5b82018360208201111561134c57600080fd5b8035906020019184600183028401116401000000008311171561136e57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192905050509350935050508b6113cd86868561262f565b80519060200120141561159b578273ffffffffffffffffffffffffffffffffffffffff166353f00b1489898f898988886040518863ffffffff1660e01b8152600401808873ffffffffffffffffffffffffffffffffffffffff1681526020018773ffffffffffffffffffffffffffffffffffffffff1681526020018681526020018581526020018481526020018060200180602001838103835285818151815260200191508051906020019080838360005b8381101561149a57808201518184015260208101905061147f565b50505050905090810190601f1680156114c75780820380516001836020036101000a031916815260200191505b50838103825284818151815260200191508051906020019080838360005b838110156115005780820151818401526020810190506114e5565b50505050905090810190601f16801561152d5780820380516001836020036101000a031916815260200191505b50995050505050505050505060206040518083038186803b15801561155157600080fd5b505afa158015611565573d6000803e3d6000fd5b505050506040513d602081101561157b57600080fd5b8101908080519060200190929190505050985050505050505050506115f8565b50505b5050505b505b6115f3828787878080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050612737565b925050505b9392505050565b6116076123db565b73ffffffffffffffffffffffffffffffffffffffff16611625612a55565b73ffffffffffffffffffffffffffffffffffffffff16146116ae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f6f6e6c7920736166652063616e2063616c6c2074686973206d6574686f64000081525060200191505060405180910390fd5b60006116b8612a55565b905060008060e01b9050600084849050905060005b818110156117225760008060006116f58989868181106116e957fe5b90506020020135612adf565b92509250925061170f878361170a8685612b14565b612b67565b81861895505050508060010190506116cd565b50857bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916146117d6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f696e74657266616365206964206d69736d61746368000000000000000000000081525060200191505060405180910390fd5b6117e1866001611d5c565b505050505050565b6117f16123db565b73ffffffffffffffffffffffffffffffffffffffff1661180f612a55565b73ffffffffffffffffffffffffffffffffffffffff1614611898576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f6f6e6c7920736166652063616e2063616c6c2074686973206d6574686f64000081525060200191505060405180910390fd5b60006118a2612a55565b90506000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600085815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905082600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600086815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff167f06341ac2f62eb79165de8c8b54ab89a8825b12746c2b98e01dff1007837d2ba8858386604051808481526020018373ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff168152602001935050505060405180910390a250505050565b60016020528160005260406000206020528060005260406000206000915091509054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b611a826123db565b73ffffffffffffffffffffffffffffffffffffffff16611aa0612a55565b73ffffffffffffffffffffffffffffffffffffffff1614611b29576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f6f6e6c7920736166652063616e2063616c6c2074686973206d6574686f64000081525060200191505060405180910390fd5b6000611b33612a55565b905060008060e01b9050600084849050905060005b81811015611bd057611b8f84878784818110611b6057fe5b905060200201357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166000801b612b67565b858582818110611b9b57fe5b905060200201357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191683189250806001019050611b48565b50857bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614611c84576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f696e74657266616365206964206d69736d61746368000000000000000000000081525060200191505060405180910390fd5b611c8f866000611d5c565b505050505050565b611c9f6123db565b73ffffffffffffffffffffffffffffffffffffffff16611cbd612a55565b73ffffffffffffffffffffffffffffffffffffffff1614611d46576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f6f6e6c7920736166652063616e2063616c6c2074686973206d6574686f64000081525060200191505060405180910390fd5b611d58611d51612a55565b8383612b67565b5050565b611d646123db565b73ffffffffffffffffffffffffffffffffffffffff16611d82612a55565b73ffffffffffffffffffffffffffffffffffffffff1614611e0b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f6f6e6c7920736166652063616e2063616c6c2074686973206d6574686f64000081525060200191505060405180910390fd5b6000611e156123db565b905063ffffffff60e01b837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161415611eb3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f696e76616c696420696e7465726661636520696400000000000000000000000081525060200191505060405180910390fd5b6000600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090506000816000867bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060009054906101000a900460ff169050801515841515146120b25783826000877bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060006101000a81548160ff0219169083151502179055508315612043578273ffffffffffffffffffffffffffffffffffffffff167f3d5024c13f12fa602dbf52b1979058940c224ebf170c83a4e358725ae50db36d8660405180827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200191505060405180910390a26120b1565b8273ffffffffffffffffffffffffffffffffffffffff167ff159d834f487747c1b3f17e2107743e42e6eecff444d894e511c18943072b29f8660405180827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200191505060405180910390a25b5b5050505050565b60026020528160005260406000206020528060005260406000206000915091509054906101000a900460ff1681565b60006120f26123e3565b63bc197c8160e01b905098975050505050505050565b60006121126123e3565b63f23a6e6160e01b90509695505050505050565b6000806121316123db565b915061213b612a55565b90509091565b6000808260f81c15915073ffffffffffffffffffffffffffffffffffffffff83169050915091565b60007f150b7a02000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061223457507f4e2312e0000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b8061229c57507f1626ba7e000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b8061230457507f62af8dc2000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b8061236c57507f99372930000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b806123d457507f64f95acc000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b9050919050565b600033905090565b60003373ffffffffffffffffffffffffffffffffffffffff16635624b25b7f6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d560001b60001c60016040518363ffffffff1660e01b8152600401808381526020018281526020019250505060006040518083038186803b15801561246557600080fd5b505afa158015612479573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525060208110156124a357600080fd5b81019080805160405193929190846401000000008211156124c357600080fd5b838201915060208201858111156124d957600080fd5b82518660018202830111640100000000821117156124f657600080fd5b8083526020830192505050908051906020019080838360005b8381101561252a57808201518184015260208101905061250f565b50505050905090810190601f1680156125575780820380516001836020036101000a031916815260200191505b506040525050509050600081806020019051602081101561257757600080fd5b810190808051906020019092919050505090503073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161461262b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f6e6f7420612066616c6c6261636b2063616c6c0000000000000000000000000081525060200191505060405180910390fd5b5050565b6060601960f81b600160f81b8585856040516020018083815260200182805190602001908083835b6020831061267a5780518252602082019150602081019050602083039250612657565b6001836020036101000a038019825116818451168082178552505050505050905001925050506040516020818303038152906040528051906020012060405160200180857effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152600101847effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260010183815260200182815260200194505050505060405160208183030381529060405290509392505050565b60008061282d8573ffffffffffffffffffffffffffffffffffffffff1663f698da256040518163ffffffff1660e01b815260040160206040518083038186803b15801561278357600080fd5b505afa158015612797573d6000803e3d6000fd5b505050506040513d60208110156127ad57600080fd5b81019080805190602001909291905050507f60b3cbf8b4a223d68d641b3b6ddf9a298e7f33710cf3d3a9d1146b5a6150fbca60001b8660405160200180828152602001915050604051602081830303815290604052805190602001206040516020018082815260200191505060405160208183030381529060405261262f565b905060008180519060200120905060008451141561294c5760008673ffffffffffffffffffffffffffffffffffffffff16635ae6bd37836040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561289857600080fd5b505afa1580156128ac573d6000803e3d6000fd5b505050506040513d60208110156128c257600080fd5b81019080805190602001909291905050501415612947576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f48617368206e6f7420617070726f76656400000000000000000000000000000081525060200191505060405180910390fd5b612a42565b8573ffffffffffffffffffffffffffffffffffffffff1663f855438b600083876040518463ffffffff1660e01b8152600401808473ffffffffffffffffffffffffffffffffffffffff16815260200183815260200180602001828103825283818151815260200191508051906020019080838360005b838110156129dd5780820151818401526020810190506129c2565b50505050905090810190601f168015612a0a5780820380516001836020036101000a031916815260200191505b5094505050505060006040518083038186803b158015612a2957600080fd5b505afa158015612a3d573d6000803e3d6000fd5b505050505b631626ba7e60e01b925050509392505050565b6000601460003690501015612ad2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260178152602001807f496e76616c69642063616c6c64617461206c656e67746800000000000000000081525060200191505060405180910390fd5b601436033560601c905090565b60008060008360f81c15925073ffffffffffffffffffffffffffffffffffffffff841690508360a01c60a81b91509193909250565b600082612b41577f0100000000000000000000000000000000000000000000000000000000000000612b44565b60005b8273ffffffffffffffffffffffffffffffffffffffff161760001b905092915050565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090506000816000857bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019081526020016000205490506000612c0984612141565b915050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415612c48576000801b93505b83836000877bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001908152602001600020819055508573ffffffffffffffffffffffffffffffffffffffff167fe6e8ad7e5547dc860775f9f0638e195a4751a4cfbb7fd2ab60a52eb6c07082ec86848760405180847bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001838152602001828152602001935050505060405180910390a2505050505050565b60008085851115612d3257600080fd5b83861115612d3f57600080fd5b600185028301915084860390509450949250505056fea26469706673582212205ef3d30ba0400c51cffe4d66020db6f8e0785fe148ba70591b7ba7b501e647ac64736f6c63430007060033")
1292        );
1293            // 3. Library: only MultiSendCallOnly and omit MultiSendCall
1294            let _tx_safe_multisend_call_only = TransactionRequest::default().with_to(safe_diamond_proxy_address).with_input(
1295            hex!("0000000000000000000000000000000000000000000000000000000000000000608060405234801561001057600080fd5b5061019a806100206000396000f3fe60806040526004361061001e5760003560e01c80638d80ff0a14610023575b600080fd5b6100dc6004803603602081101561003957600080fd5b810190808035906020019064010000000081111561005657600080fd5b82018360208201111561006857600080fd5b8035906020019184600183028401116401000000008311171561008a57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192905050506100de565b005b805160205b8181101561015f578083015160f81c6001820184015160601c60158301850151603584018601516055850187016000856000811461012857600181146101385761013d565b6000808585888a5af1915061013d565b600080fd5b50600081141561014c57600080fd5b82605501870196505050505050506100e3565b50505056fea26469706673582212208d297bb003abee230b5dfb38774688f37a6fbb97a82a21728e8049b2acb9b73564736f6c63430007060033")
1296        );
1297            // 4. Safe singleton deploySafe();
1298            let _tx_safe_singleton = TransactionRequest::default().with_to(safe_diamond_proxy_address).with_input(
1299            hex!("0000000000000000000000000000000000000000000000000000000000000000608060405234801561001057600080fd5b506001600481905550615c1b80620000296000396000f3fe6080604052600436106101d15760003560e01c8063affed0e0116100f7578063e19a9dd911610095578063f08a032311610064578063f08a03231461156b578063f698da25146115bc578063f8dc5dd9146115e7578063ffa1ad741461166257610226565b8063e19a9dd9146112bf578063e318b52b14611310578063e75235b8146113a1578063e86637db146113cc57610226565b8063cc2f8452116100d1578063cc2f84521461100c578063d4d9bdcd146110d9578063d8d11f7814611114578063e009cfde1461124e57610226565b8063affed0e014610d89578063b4faba0914610db4578063b63e800d14610e9c57610226565b80635624b25b1161016f5780636a7612021161013e5780636a761202146109895780637d83297414610b45578063934f3a1114610bb4578063a0e67e2b14610d1d57610226565b80635624b25b146107f05780635ae6bd37146108ae578063610b5925146108fd578063694e80c31461094e57610226565b80632f54bf6e116101ab5780632f54bf6e146104c85780633408e4701461052f578063468721a71461055a5780635229073f1461066f57610226565b80630d582f131461029357806312fb68e0146102ee5780632d9ad53d1461046157610226565b36610226573373ffffffffffffffffffffffffffffffffffffffff167f3d0ce9bfc3ed7d6862dbb28b2dea94561fe714a1b4d019aa8af39730d1ad7c3d346040518082815260200191505060405180910390a2005b34801561023257600080fd5b5060007f6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d560001b905080548061026757600080f35b36600080373360601b365260008060143601600080855af13d6000803e8061028e573d6000fd5b3d6000f35b34801561029f57600080fd5b506102ec600480360360408110156102b657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506116f2565b005b3480156102fa57600080fd5b5061045f6004803603608081101561031157600080fd5b81019080803590602001909291908035906020019064010000000081111561033857600080fd5b82018360208201111561034a57600080fd5b8035906020019184600183028401116401000000008311171561036c57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290803590602001906401000000008111156103cf57600080fd5b8201836020820111156103e157600080fd5b8035906020019184600183028401116401000000008311171561040357600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050919291929080359060200190929190505050611ad8565b005b34801561046d57600080fd5b506104b06004803603602081101561048457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506123d6565b60405180821515815260200191505060405180910390f35b3480156104d457600080fd5b50610517600480360360208110156104eb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506124a8565b60405180821515815260200191505060405180910390f35b34801561053b57600080fd5b5061054461257a565b6040518082815260200191505060405180910390f35b34801561056657600080fd5b506106576004803603608081101561057d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001906401000000008111156105c457600080fd5b8201836020820111156105d657600080fd5b803590602001918460018302840111640100000000831117156105f857600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290803560ff169060200190929190505050612587565b60405180821515815260200191505060405180910390f35b34801561067b57600080fd5b5061076c6004803603608081101561069257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001906401000000008111156106d957600080fd5b8201836020820111156106eb57600080fd5b8035906020019184600183028401116401000000008311171561070d57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290803560ff16906020019092919050505061278d565b60405180831515815260200180602001828103825283818151815260200191508051906020019080838360005b838110156107b4578082015181840152602081019050610799565b50505050905090810190601f1680156107e15780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b3480156107fc57600080fd5b506108336004803603604081101561081357600080fd5b8101908080359060200190929190803590602001909291905050506127c3565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610873578082015181840152602081019050610858565b50505050905090810190601f1680156108a05780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156108ba57600080fd5b506108e7600480360360208110156108d157600080fd5b810190808035906020019092919050505061284a565b6040518082815260200191505060405180910390f35b34801561090957600080fd5b5061094c6004803603602081101561092057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612862565b005b34801561095a57600080fd5b506109876004803603602081101561097157600080fd5b8101908080359060200190929190505050612bea565b005b610b2d60048036036101408110156109a057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001906401000000008111156109e757600080fd5b8201836020820111156109f957600080fd5b80359060200191846001830284011164010000000083111715610a1b57600080fd5b9091929391929390803560ff169060200190929190803590602001909291908035906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190640100000000811115610aa757600080fd5b820183602082011115610ab957600080fd5b80359060200191846001830284011164010000000083111715610adb57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050612d24565b60405180821515815260200191505060405180910390f35b348015610b5157600080fd5b50610b9e60048036036040811015610b6857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050613253565b6040518082815260200191505060405180910390f35b348015610bc057600080fd5b50610d1b60048036036060811015610bd757600080fd5b810190808035906020019092919080359060200190640100000000811115610bfe57600080fd5b820183602082011115610c1057600080fd5b80359060200191846001830284011164010000000083111715610c3257600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050919291929080359060200190640100000000811115610c9557600080fd5b820183602082011115610ca757600080fd5b80359060200191846001830284011164010000000083111715610cc957600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050613278565b005b348015610d2957600080fd5b50610d32613307565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610d75578082015181840152602081019050610d5a565b505050509050019250505060405180910390f35b348015610d9557600080fd5b50610d9e6134b0565b6040518082815260200191505060405180910390f35b348015610dc057600080fd5b50610e9a60048036036040811015610dd757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190640100000000811115610e1457600080fd5b820183602082011115610e2657600080fd5b80359060200191846001830284011164010000000083111715610e4857600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192905050506134b6565b005b348015610ea857600080fd5b5061100a6004803603610100811015610ec057600080fd5b8101908080359060200190640100000000811115610edd57600080fd5b820183602082011115610eef57600080fd5b80359060200191846020830284011164010000000083111715610f1157600080fd5b909192939192939080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190640100000000811115610f5c57600080fd5b820183602082011115610f6e57600080fd5b80359060200191846001830284011164010000000083111715610f9057600080fd5b9091929391929390803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506134d8565b005b34801561101857600080fd5b506110656004803603604081101561102f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050613696565b60405180806020018373ffffffffffffffffffffffffffffffffffffffff168152602001828103825284818151815260200191508051906020019060200280838360005b838110156110c45780820151818401526020810190506110a9565b50505050905001935050505060405180910390f35b3480156110e557600080fd5b50611112600480360360208110156110fc57600080fd5b81019080803590602001909291905050506139f9565b005b34801561112057600080fd5b50611238600480360361014081101561113857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019064010000000081111561117f57600080fd5b82018360208201111561119157600080fd5b803590602001918460018302840111640100000000831117156111b357600080fd5b9091929391929390803560ff169060200190929190803590602001909291908035906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050613b98565b6040518082815260200191505060405180910390f35b34801561125a57600080fd5b506112bd6004803603604081101561127157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613bc5565b005b3480156112cb57600080fd5b5061130e600480360360208110156112e257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613f4c565b005b34801561131c57600080fd5b5061139f6004803603606081101561133357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050614138565b005b3480156113ad57600080fd5b506113b6614796565b6040518082815260200191505060405180910390f35b3480156113d857600080fd5b506114f060048036036101408110156113f057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019064010000000081111561143757600080fd5b82018360208201111561144957600080fd5b8035906020019184600183028401116401000000008311171561146b57600080fd5b9091929391929390803560ff169060200190929190803590602001909291908035906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506147a0565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015611530578082015181840152602081019050611515565b50505050905090810190601f16801561155d5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561157757600080fd5b506115ba6004803603602081101561158e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050614948565b005b3480156115c857600080fd5b506115d161499f565b6040518082815260200191505060405180910390f35b3480156115f357600080fd5b506116606004803603606081101561160a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050614a1d565b005b34801561166e57600080fd5b50611677614e46565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156116b757808201518184015260208101905061169c565b50505050905090810190601f1680156116e45780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6116fa614e7f565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141580156117645750600173ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b801561179c57503073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b61180e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303300000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff16600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161461190f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303400000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b60026000600173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508160026000600173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506003600081548092919060010191905055508173ffffffffffffffffffffffffffffffffffffffff167f9465fa0c962cc76958e6373a993326400c1c94f8be2fe3a952adfa7f60b2ea2660405160405180910390a28060045414611ad457611ad381612bea565b5b5050565b611aec604182614f2290919063ffffffff16565b82511015611b62576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330323000000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b6000808060008060005b868110156123ca57611b7e8882614f5c565b80945081955082965050505060008460ff1614156120035789898051906020012014611c12576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330323700000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b8260001c9450611c2c604188614f2290919063ffffffff16565b8260001c1015611ca4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330323100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b8751611cbd60208460001c614f8b90919063ffffffff16565b1115611d31576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330323200000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b60006020838a01015190508851611d6782611d5960208760001c614f8b90919063ffffffff16565b614f8b90919063ffffffff16565b1115611ddb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330323300000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b60606020848b010190506320c13b0b60e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168773ffffffffffffffffffffffffffffffffffffffff166320c13b0b8d846040518363ffffffff1660e01b8152600401808060200180602001838103835285818151815260200191508051906020019080838360005b83811015611e7d578082015181840152602081019050611e62565b50505050905090810190601f168015611eaa5780820380516001836020036101000a031916815260200191505b50838103825284818151815260200191508051906020019080838360005b83811015611ee3578082015181840152602081019050611ec8565b50505050905090810190601f168015611f105780820380516001836020036101000a031916815260200191505b5094505050505060206040518083038186803b158015611f2f57600080fd5b505afa158015611f43573d6000803e3d6000fd5b505050506040513d6020811015611f5957600080fd5b81019080805190602001909291905050507bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614611ffc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330323400000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b5050612248565b60018460ff161415612117578260001c94508473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806120a057506000600860008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008c81526020019081526020016000205414155b612112576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330323500000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b612247565b601e8460ff1611156121df5760018a60405160200180807f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250601c018281526020019150506040516020818303038152906040528051906020012060048603858560405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa1580156121ce573d6000803e3d6000fd5b505050602060405103519450612246565b60018a85858560405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015612239573d6000803e3d6000fd5b5050506020604051035194505b5b5b8573ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1611801561230f5750600073ffffffffffffffffffffffffffffffffffffffff16600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614155b80156123485750600173ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614155b6123ba576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330323600000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b8495508080600101915050611b6c565b50505050505050505050565b60008173ffffffffffffffffffffffffffffffffffffffff16600173ffffffffffffffffffffffffffffffffffffffff16141580156124a15750600073ffffffffffffffffffffffffffffffffffffffff16600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614155b9050919050565b6000600173ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141580156125735750600073ffffffffffffffffffffffffffffffffffffffff16600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614155b9050919050565b6000804690508091505090565b6000600173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141580156126525750600073ffffffffffffffffffffffffffffffffffffffff16600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614155b6126c4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475331303400000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b6126f1858585857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff614faa565b90508015612741573373ffffffffffffffffffffffffffffffffffffffff167f6895c13664aa4f67288b25d7a21d7aaa34916e355fb9b6fae0a139a9085becb860405160405180910390a2612785565b3373ffffffffffffffffffffffffffffffffffffffff167facd2c8702804128fdb0db2bb49f6d127dd0181c13fd45dbfe16de0930e2bd37560405160405180910390a25b949350505050565b6000606061279d86868686612587565b915060405160203d0181016040523d81523d6000602083013e8091505094509492505050565b606060006020830267ffffffffffffffff811180156127e157600080fd5b506040519080825280601f01601f1916602001820160405280156128145781602001600182028036833780820191505090505b50905060005b8381101561283f5780850154806020830260208501015250808060010191505061281a565b508091505092915050565b60076020528060005260406000206000915090505481565b61286a614e7f565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141580156128d45750600173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b612946576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475331303100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff16600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614612a47576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475331303200000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b60016000600173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508060016000600173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167fecdf3a3effea5783a3c4c2140e677577666428d44ed9d474a0b3a4c9943f844060405160405180910390a250565b612bf2614e7f565b600354811115612c6a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b6001811015612ce1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303200000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b806004819055507f610f7ff2b304ae8903c3de74c60c6ab1f7d6226b3f52c5161905bb5ad4039c936004546040518082815260200191505060405180910390a150565b6000806000612d3e8e8e8e8e8e8e8e8e8e8e6005546147a0565b905060056000815480929190600101919050555080805190602001209150612d67828286613278565b506000612d72614ff6565b9050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614612f58578073ffffffffffffffffffffffffffffffffffffffff166375f0bb528f8f8f8f8f8f8f8f8f8f8f336040518d63ffffffff1660e01b8152600401808d73ffffffffffffffffffffffffffffffffffffffff1681526020018c8152602001806020018a6001811115612e1557fe5b81526020018981526020018881526020018781526020018673ffffffffffffffffffffffffffffffffffffffff1681526020018573ffffffffffffffffffffffffffffffffffffffff168152602001806020018473ffffffffffffffffffffffffffffffffffffffff16815260200183810383528d8d82818152602001925080828437600081840152601f19601f820116905080830192505050838103825285818151815260200191508051906020019080838360005b83811015612ee7578082015181840152602081019050612ecc565b50505050905090810190601f168015612f145780820380516001836020036101000a031916815260200191505b509e505050505050505050505050505050600060405180830381600087803b158015612f3f57600080fd5b505af1158015612f53573d6000803e3d6000fd5b505050505b6101f4612f7f6109c48b01603f60408d0281612f7057fe5b0461502790919063ffffffff16565b015a1015612ff5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330313000000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b60005a905061305e8f8f8f8f8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050508e60008d14613053578e613059565b6109c45a035b614faa565b93506130735a8261504190919063ffffffff16565b90508380613082575060008a14155b8061308e575060008814155b613100576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330313300000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b60008089111561311a57613117828b8b8b8b615061565b90505b841561315d57837f442e715f626346e8c54381002da614f62bee8d27386535b2521ec8540898556e826040518082815260200191505060405180910390a2613196565b837f23428b18acfb3ea64b08dc0c1d296ea9c09702c09083ca5272e64d115b687d23826040518082815260200191505060405180910390a25b5050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614613242578073ffffffffffffffffffffffffffffffffffffffff16639327136883856040518363ffffffff1660e01b815260040180838152602001821515815260200192505050600060405180830381600087803b15801561322957600080fd5b505af115801561323d573d6000803e3d6000fd5b505050505b50509b9a5050505050505050505050565b6008602052816000526040600020602052806000526040600020600091509150505481565b60006004549050600081116132f5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330303100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b61330184848484611ad8565b50505050565b6060600060035467ffffffffffffffff8111801561332457600080fd5b506040519080825280602002602001820160405280156133535781602001602082028036833780820191505090505b50905060008060026000600173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690505b600173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146134a757808383815181106133fe57fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081806001019250506133bd565b82935050505090565b60055481565b600080825160208401855af4806000523d6020523d600060403e60403d016000fd5b6135238a8a80806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f8201169050808301925050505050505089615267565b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16146135615761356084615767565b5b6135af8787878080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050615838565b60008211156135c9576135c782600060018685615061565b505b3373ffffffffffffffffffffffffffffffffffffffff167f141df868a6331af528e38c83b7aa03edc19be66e37ae67f9285bf4f8e3c6a1a88b8b8b8b8960405180806020018581526020018473ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1681526020018281038252878782818152602001925060200280828437600081840152601f19601f820116905080830192505050965050505050505060405180910390a250505050505050505050565b60606000600173ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614806136da57506136d9846123d6565b5b61374c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475331303500000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600083116137c2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475331303600000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b8267ffffffffffffffff811180156137d957600080fd5b506040519080825280602002602001820160405280156138085781602001602082028036833780820191505090505b5091506000600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1691505b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141580156138da5750600173ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b80156138e557508381105b156139a057818382815181106138f757fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1691508080600101915050613870565b600173ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16146139ee578260018203815181106139e357fe5b602002602001015191505b808352509250929050565b600073ffffffffffffffffffffffffffffffffffffffff16600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415613afb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330333000000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b6001600860003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000838152602001908152602001600020819055503373ffffffffffffffffffffffffffffffffffffffff16817ff2a0eb156472d1440255b0d7c1e19cc07115d1051fe605b0dce69acfec884d9c60405160405180910390a350565b6000613bad8c8c8c8c8c8c8c8c8c8c8c6147a0565b8051906020012090509b9a5050505050505050505050565b613bcd614e7f565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614158015613c375750600173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b613ca9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475331303100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff16600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614613da9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475331303300000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600160008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167faab4fa2b463f581b2b32cb3b7e3b704b9ce37cc209b5fb4d77e593ace405427660405160405180910390a25050565b613f54614e7f565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146140c6578073ffffffffffffffffffffffffffffffffffffffff166301ffc9a77fe6d7a83a000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b815260040180827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200191505060206040518083038186803b15801561401857600080fd5b505afa15801561402c573d6000803e3d6000fd5b505050506040513d602081101561404257600080fd5b81019080805190602001909291905050506140c5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475333303000000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b5b60007f4a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c860001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f1151116914515bc0891ff9047a6cb32cf902546f83066499bcf8ba33d2353fa260405160405180910390a25050565b614140614e7f565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141580156141aa5750600173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b80156141e257503073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b614254576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303300000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff16600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614614355576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303400000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141580156143bf5750600173ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b614431576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303300000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff16600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614614531576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303500000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff167ff8d49fc529812e9a7c5c50e69c20f0dccc0db8fa95c98bc58cc9a4f1c1299eaf60405160405180910390a28073ffffffffffffffffffffffffffffffffffffffff167f9465fa0c962cc76958e6373a993326400c1c94f8be2fe3a952adfa7f60b2ea2660405160405180910390a2505050565b6000600454905090565b606060007fbb8310d486368db6bd6f849402fdd73ad53d316b5a4b2644ad6efe0f941286d860001b8d8d8d8d60405180838380828437808301925050509250505060405180910390208c8c8c8c8c8c8c604051602001808c81526020018b73ffffffffffffffffffffffffffffffffffffffff1681526020018a815260200189815260200188600181111561483157fe5b81526020018781526020018681526020018581526020018473ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019b505050505050505050505050604051602081830303815290604052805190602001209050601960f81b600160f81b6148bd61499f565b8360405160200180857effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152600101847effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526001018381526020018281526020019450505050506040516020818303038152906040529150509b9a5050505050505050505050565b614950614e7f565b61495981615767565b8073ffffffffffffffffffffffffffffffffffffffff167f5ac6c46c93c8d0e53714ba3b53db3e7c046da994313d7ed0d192028bc7c228b060405160405180910390a250565b60007f47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a7946921860001b6149cd61257a565b30604051602001808481526020018381526020018273ffffffffffffffffffffffffffffffffffffffff168152602001935050505060405160208183030381529060405280519060200120905090565b614a25614e7f565b806001600354031015614aa0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614158015614b0a5750600173ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b614b7c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303300000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff16600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614614c7c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303500000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600360008154809291906001900391905055508173ffffffffffffffffffffffffffffffffffffffff167ff8d49fc529812e9a7c5c50e69c20f0dccc0db8fa95c98bc58cc9a4f1c1299eaf60405160405180910390a28060045414614e4157614e4081612bea565b5b505050565b6040518060400160405280600581526020017f312e342e3100000000000000000000000000000000000000000000000000000081525081565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614614f20576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330333100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b565b600080831415614f355760009050614f56565b6000828402905082848281614f4657fe5b0414614f5157600080fd5b809150505b92915050565b60008060008360410260208101860151925060408101860151915060ff60418201870151169350509250925092565b600080828401905083811015614fa057600080fd5b8091505092915050565b6000600180811115614fb857fe5b836001811115614fc457fe5b1415614fdd576000808551602087018986f49050614fed565b600080855160208701888a87f190505b95945050505050565b6000807f4a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c860001b9050805491505090565b6000818310156150375781615039565b825b905092915050565b60008282111561505057600080fd5b600082840390508091505092915050565b600080600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161461509e57826150a0565b325b9050600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614156151b85761510a3a86106150e7573a6150e9565b855b6150fc888a614f8b90919063ffffffff16565b614f2290919063ffffffff16565b91508073ffffffffffffffffffffffffffffffffffffffff166108fc839081150290604051600060405180830381858888f193505050506151b3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330313100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b61525d565b6151dd856151cf888a614f8b90919063ffffffff16565b614f2290919063ffffffff16565b91506151ea848284615b0e565b61525c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330313200000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b5b5095945050505050565b6000600454146152df576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303000000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b8151811115615356576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b60018110156153cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303200000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b60006001905060005b83518110156156d35760008482815181106153ed57fe5b60200260200101519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141580156154615750600173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b801561549957503073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b80156154d157508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614155b615543576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303300000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff16600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614615644576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303400000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b80600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508092505080806001019150506153d6565b506001600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550825160038190555081600481905550505050565b3073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415615809576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475334303000000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b60007f6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d560001b90508181555050565b600073ffffffffffffffffffffffffffffffffffffffff1660016000600173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161461593a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475331303000000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b6001806000600173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614615b0a576159f682615bd2565b615a68576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330303200000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b615a978260008360017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff614faa565b615b09576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330303000000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b5b5050565b60008063a9059cbb8484604051602401808373ffffffffffffffffffffffffffffffffffffffff168152602001828152602001925050506040516020818303038152906040529060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050602060008251602084016000896127105a03f13d60008114615bb55760208114615bbd5760009350615bc8565b819350615bc8565b600051158215171593505b5050509392505050565b600080823b90506000811191505091905056fea264697066735822122057398fa72884cf9a6cb78aab2fb58a6b927f0e9d97d75b015daaee0959a153bf64736f6c63430007060033")
1300        );
1301            // 5. Safe L2 singleton deploySafe();
1302            let _tx_safe_l2_singleton = TransactionRequest::default().with_to(safe_diamond_proxy_address).with_input(
1303            hex!("0000000000000000000000000000000000000000000000000000000000000000608060405234801561001057600080fd5b506001600481905550615f6580620000296000396000f3fe6080604052600436106101d15760003560e01c8063affed0e0116100f7578063e19a9dd911610095578063f08a032311610064578063f08a03231461156b578063f698da25146115bc578063f8dc5dd9146115e7578063ffa1ad741461166257610226565b8063e19a9dd9146112bf578063e318b52b14611310578063e75235b8146113a1578063e86637db146113cc57610226565b8063cc2f8452116100d1578063cc2f84521461100c578063d4d9bdcd146110d9578063d8d11f7814611114578063e009cfde1461124e57610226565b8063affed0e014610d89578063b4faba0914610db4578063b63e800d14610e9c57610226565b80635624b25b1161016f5780636a7612021161013e5780636a761202146109895780637d83297414610b45578063934f3a1114610bb4578063a0e67e2b14610d1d57610226565b80635624b25b146107f05780635ae6bd37146108ae578063610b5925146108fd578063694e80c31461094e57610226565b80632f54bf6e116101ab5780632f54bf6e146104c85780633408e4701461052f578063468721a71461055a5780635229073f1461066f57610226565b80630d582f131461029357806312fb68e0146102ee5780632d9ad53d1461046157610226565b36610226573373ffffffffffffffffffffffffffffffffffffffff167f3d0ce9bfc3ed7d6862dbb28b2dea94561fe714a1b4d019aa8af39730d1ad7c3d346040518082815260200191505060405180910390a2005b34801561023257600080fd5b5060007f6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d560001b905080548061026757600080f35b36600080373360601b365260008060143601600080855af13d6000803e8061028e573d6000fd5b3d6000f35b34801561029f57600080fd5b506102ec600480360360408110156102b657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506116f2565b005b3480156102fa57600080fd5b5061045f6004803603608081101561031157600080fd5b81019080803590602001909291908035906020019064010000000081111561033857600080fd5b82018360208201111561034a57600080fd5b8035906020019184600183028401116401000000008311171561036c57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290803590602001906401000000008111156103cf57600080fd5b8201836020820111156103e157600080fd5b8035906020019184600183028401116401000000008311171561040357600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050919291929080359060200190929190505050611ad8565b005b34801561046d57600080fd5b506104b06004803603602081101561048457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506123d6565b60405180821515815260200191505060405180910390f35b3480156104d457600080fd5b50610517600480360360208110156104eb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506124a8565b60405180821515815260200191505060405180910390f35b34801561053b57600080fd5b5061054461257a565b6040518082815260200191505060405180910390f35b34801561056657600080fd5b506106576004803603608081101561057d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001906401000000008111156105c457600080fd5b8201836020820111156105d657600080fd5b803590602001918460018302840111640100000000831117156105f857600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290803560ff169060200190929190505050612587565b60405180821515815260200191505060405180910390f35b34801561067b57600080fd5b5061076c6004803603608081101561069257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001906401000000008111156106d957600080fd5b8201836020820111156106eb57600080fd5b8035906020019184600183028401116401000000008311171561070d57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290803560ff169060200190929190505050612692565b60405180831515815260200180602001828103825283818151815260200191508051906020019080838360005b838110156107b4578082015181840152602081019050610799565b50505050905090810190601f1680156107e15780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b3480156107fc57600080fd5b506108336004803603604081101561081357600080fd5b8101908080359060200190929190803590602001909291905050506126c8565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610873578082015181840152602081019050610858565b50505050905090810190601f1680156108a05780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156108ba57600080fd5b506108e7600480360360208110156108d157600080fd5b810190808035906020019092919050505061274f565b6040518082815260200191505060405180910390f35b34801561090957600080fd5b5061094c6004803603602081101561092057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612767565b005b34801561095a57600080fd5b506109876004803603602081101561097157600080fd5b8101908080359060200190929190505050612aef565b005b610b2d60048036036101408110156109a057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001906401000000008111156109e757600080fd5b8201836020820111156109f957600080fd5b80359060200191846001830284011164010000000083111715610a1b57600080fd5b9091929391929390803560ff169060200190929190803590602001909291908035906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190640100000000811115610aa757600080fd5b820183602082011115610ab957600080fd5b80359060200191846001830284011164010000000083111715610adb57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050612c29565b60405180821515815260200191505060405180910390f35b348015610b5157600080fd5b50610b9e60048036036040811015610b6857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050612e68565b6040518082815260200191505060405180910390f35b348015610bc057600080fd5b50610d1b60048036036060811015610bd757600080fd5b810190808035906020019092919080359060200190640100000000811115610bfe57600080fd5b820183602082011115610c1057600080fd5b80359060200191846001830284011164010000000083111715610c3257600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050919291929080359060200190640100000000811115610c9557600080fd5b820183602082011115610ca757600080fd5b80359060200191846001830284011164010000000083111715610cc957600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050612e8d565b005b348015610d2957600080fd5b50610d32612f1c565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610d75578082015181840152602081019050610d5a565b505050509050019250505060405180910390f35b348015610d9557600080fd5b50610d9e6130c5565b6040518082815260200191505060405180910390f35b348015610dc057600080fd5b50610e9a60048036036040811015610dd757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190640100000000811115610e1457600080fd5b820183602082011115610e2657600080fd5b80359060200191846001830284011164010000000083111715610e4857600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192905050506130cb565b005b348015610ea857600080fd5b5061100a6004803603610100811015610ec057600080fd5b8101908080359060200190640100000000811115610edd57600080fd5b820183602082011115610eef57600080fd5b80359060200191846020830284011164010000000083111715610f1157600080fd5b909192939192939080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190640100000000811115610f5c57600080fd5b820183602082011115610f6e57600080fd5b80359060200191846001830284011164010000000083111715610f9057600080fd5b9091929391929390803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506130ed565b005b34801561101857600080fd5b506110656004803603604081101561102f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506132ab565b60405180806020018373ffffffffffffffffffffffffffffffffffffffff168152602001828103825284818151815260200191508051906020019060200280838360005b838110156110c45780820151818401526020810190506110a9565b50505050905001935050505060405180910390f35b3480156110e557600080fd5b50611112600480360360208110156110fc57600080fd5b810190808035906020019092919050505061360e565b005b34801561112057600080fd5b50611238600480360361014081101561113857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019064010000000081111561117f57600080fd5b82018360208201111561119157600080fd5b803590602001918460018302840111640100000000831117156111b357600080fd5b9091929391929390803560ff169060200190929190803590602001909291908035906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506137ad565b6040518082815260200191505060405180910390f35b34801561125a57600080fd5b506112bd6004803603604081101561127157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506137da565b005b3480156112cb57600080fd5b5061130e600480360360208110156112e257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613b61565b005b34801561131c57600080fd5b5061139f6004803603606081101561133357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613d4d565b005b3480156113ad57600080fd5b506113b66143ab565b6040518082815260200191505060405180910390f35b3480156113d857600080fd5b506114f060048036036101408110156113f057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019064010000000081111561143757600080fd5b82018360208201111561144957600080fd5b8035906020019184600183028401116401000000008311171561146b57600080fd5b9091929391929390803560ff169060200190929190803590602001909291908035906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506143b5565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015611530578082015181840152602081019050611515565b50505050905090810190601f16801561155d5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561157757600080fd5b506115ba6004803603602081101561158e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061455d565b005b3480156115c857600080fd5b506115d16145b4565b6040518082815260200191505060405180910390f35b3480156115f357600080fd5b506116606004803603606081101561160a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050614632565b005b34801561166e57600080fd5b50611677614a5b565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156116b757808201518184015260208101905061169c565b50505050905090810190601f1680156116e45780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6116fa614a94565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141580156117645750600173ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b801561179c57503073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b61180e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303300000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff16600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161461190f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303400000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b60026000600173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508160026000600173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506003600081548092919060010191905055508173ffffffffffffffffffffffffffffffffffffffff167f9465fa0c962cc76958e6373a993326400c1c94f8be2fe3a952adfa7f60b2ea2660405160405180910390a28060045414611ad457611ad381612aef565b5b5050565b611aec604182614b3790919063ffffffff16565b82511015611b62576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330323000000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b6000808060008060005b868110156123ca57611b7e8882614b71565b80945081955082965050505060008460ff1614156120035789898051906020012014611c12576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330323700000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b8260001c9450611c2c604188614b3790919063ffffffff16565b8260001c1015611ca4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330323100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b8751611cbd60208460001c614ba090919063ffffffff16565b1115611d31576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330323200000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b60006020838a01015190508851611d6782611d5960208760001c614ba090919063ffffffff16565b614ba090919063ffffffff16565b1115611ddb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330323300000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b60606020848b010190506320c13b0b60e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168773ffffffffffffffffffffffffffffffffffffffff166320c13b0b8d846040518363ffffffff1660e01b8152600401808060200180602001838103835285818151815260200191508051906020019080838360005b83811015611e7d578082015181840152602081019050611e62565b50505050905090810190601f168015611eaa5780820380516001836020036101000a031916815260200191505b50838103825284818151815260200191508051906020019080838360005b83811015611ee3578082015181840152602081019050611ec8565b50505050905090810190601f168015611f105780820380516001836020036101000a031916815260200191505b5094505050505060206040518083038186803b158015611f2f57600080fd5b505afa158015611f43573d6000803e3d6000fd5b505050506040513d6020811015611f5957600080fd5b81019080805190602001909291905050507bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614611ffc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330323400000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b5050612248565b60018460ff161415612117578260001c94508473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806120a057506000600860008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008c81526020019081526020016000205414155b612112576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330323500000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b612247565b601e8460ff1611156121df5760018a60405160200180807f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250601c018281526020019150506040516020818303038152906040528051906020012060048603858560405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa1580156121ce573d6000803e3d6000fd5b505050602060405103519450612246565b60018a85858560405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015612239573d6000803e3d6000fd5b5050506020604051035194505b5b5b8573ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1611801561230f5750600073ffffffffffffffffffffffffffffffffffffffff16600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614155b80156123485750600173ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614155b6123ba576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330323600000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b8495508080600101915050611b6c565b50505050505050505050565b60008173ffffffffffffffffffffffffffffffffffffffff16600173ffffffffffffffffffffffffffffffffffffffff16141580156124a15750600073ffffffffffffffffffffffffffffffffffffffff16600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614155b9050919050565b6000600173ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141580156125735750600073ffffffffffffffffffffffffffffffffffffffff16600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614155b9050919050565b6000804690508091505090565b60007fb648d3644f584ed1c2232d53c46d87e693586486ad0d1175f8656013110b714e3386868686604051808673ffffffffffffffffffffffffffffffffffffffff1681526020018573ffffffffffffffffffffffffffffffffffffffff1681526020018481526020018060200183600181111561260157fe5b8152602001828103825284818151815260200191508051906020019080838360005b8381101561263e578082015181840152602081019050612623565b50505050905090810190601f16801561266b5780820380516001836020036101000a031916815260200191505b50965050505050505060405180910390a161268885858585614bbf565b9050949350505050565b600060606126a286868686612587565b915060405160203d0181016040523d81523d6000602083013e8091505094509492505050565b606060006020830267ffffffffffffffff811180156126e657600080fd5b506040519080825280601f01601f1916602001820160405280156127195781602001600182028036833780820191505090505b50905060005b838110156127445780850154806020830260208501015250808060010191505061271f565b508091505092915050565b60076020528060005260406000206000915090505481565b61276f614a94565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141580156127d95750600173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b61284b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475331303100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff16600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161461294c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475331303200000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b60016000600173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508060016000600173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167fecdf3a3effea5783a3c4c2140e677577666428d44ed9d474a0b3a4c9943f844060405160405180910390a250565b612af7614a94565b600354811115612b6f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b6001811015612be6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303200000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b806004819055507f610f7ff2b304ae8903c3de74c60c6ab1f7d6226b3f52c5161905bb5ad4039c936004546040518082815260200191505060405180910390a150565b6000606060055433600454604051602001808481526020018373ffffffffffffffffffffffffffffffffffffffff168152602001828152602001935050505060405160208183030381529060405290507f66753cd2356569ee081232e3be8909b950e0a76c1f8460c3a5e3c2be32b11bed8d8d8d8d8d8d8d8d8d8d8d8c604051808d73ffffffffffffffffffffffffffffffffffffffff1681526020018c8152602001806020018a6001811115612cdc57fe5b81526020018981526020018881526020018781526020018673ffffffffffffffffffffffffffffffffffffffff1681526020018573ffffffffffffffffffffffffffffffffffffffff168152602001806020018060200184810384528e8e82818152602001925080828437600081840152601f19601f820116905080830192505050848103835286818151815260200191508051906020019080838360005b83811015612d96578082015181840152602081019050612d7b565b50505050905090810190601f168015612dc35780820380516001836020036101000a031916815260200191505b50848103825285818151815260200191508051906020019080838360005b83811015612dfc578082015181840152602081019050612de1565b50505050905090810190601f168015612e295780820380516001836020036101000a031916815260200191505b509f5050505050505050505050505050505060405180910390a1612e568d8d8d8d8d8d8d8d8d8d8d614dc5565b9150509b9a5050505050505050505050565b6008602052816000526040600020602052806000526040600020600091509150505481565b6000600454905060008111612f0a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330303100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b612f1684848484611ad8565b50505050565b6060600060035467ffffffffffffffff81118015612f3957600080fd5b50604051908082528060200260200182016040528015612f685781602001602082028036833780820191505090505b50905060008060026000600173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690505b600173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146130bc578083838151811061301357fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508180600101925050612fd2565b82935050505090565b60055481565b600080825160208401855af4806000523d6020523d600060403e60403d016000fd5b6131388a8a80806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050896152f4565b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161461317657613175846157f4565b5b6131c48787878080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506158c5565b60008211156131de576131dc82600060018685615b9b565b505b3373ffffffffffffffffffffffffffffffffffffffff167f141df868a6331af528e38c83b7aa03edc19be66e37ae67f9285bf4f8e3c6a1a88b8b8b8b8960405180806020018581526020018473ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1681526020018281038252878782818152602001925060200280828437600081840152601f19601f820116905080830192505050965050505050505060405180910390a250505050505050505050565b60606000600173ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614806132ef57506132ee846123d6565b5b613361576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475331303500000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600083116133d7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475331303600000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b8267ffffffffffffffff811180156133ee57600080fd5b5060405190808252806020026020018201604052801561341d5781602001602082028036833780820191505090505b5091506000600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1691505b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141580156134ef5750600173ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b80156134fa57508381105b156135b5578183828151811061350c57fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1691508080600101915050613485565b600173ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614613603578260018203815181106135f857fe5b602002602001015191505b808352509250929050565b600073ffffffffffffffffffffffffffffffffffffffff16600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415613710576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330333000000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b6001600860003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000838152602001908152602001600020819055503373ffffffffffffffffffffffffffffffffffffffff16817ff2a0eb156472d1440255b0d7c1e19cc07115d1051fe605b0dce69acfec884d9c60405160405180910390a350565b60006137c28c8c8c8c8c8c8c8c8c8c8c6143b5565b8051906020012090509b9a5050505050505050505050565b6137e2614a94565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415801561384c5750600173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b6138be576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475331303100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff16600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146139be576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475331303300000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600160008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167faab4fa2b463f581b2b32cb3b7e3b704b9ce37cc209b5fb4d77e593ace405427660405160405180910390a25050565b613b69614a94565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614613cdb578073ffffffffffffffffffffffffffffffffffffffff166301ffc9a77fe6d7a83a000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b815260040180827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200191505060206040518083038186803b158015613c2d57600080fd5b505afa158015613c41573d6000803e3d6000fd5b505050506040513d6020811015613c5757600080fd5b8101908080519060200190929190505050613cda576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475333303000000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b5b60007f4a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c860001b90508181558173ffffffffffffffffffffffffffffffffffffffff167f1151116914515bc0891ff9047a6cb32cf902546f83066499bcf8ba33d2353fa260405160405180910390a25050565b613d55614a94565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614158015613dbf5750600173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b8015613df757503073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b613e69576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303300000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff16600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614613f6a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303400000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614158015613fd45750600173ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b614046576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303300000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff16600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614614146576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303500000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff167ff8d49fc529812e9a7c5c50e69c20f0dccc0db8fa95c98bc58cc9a4f1c1299eaf60405160405180910390a28073ffffffffffffffffffffffffffffffffffffffff167f9465fa0c962cc76958e6373a993326400c1c94f8be2fe3a952adfa7f60b2ea2660405160405180910390a2505050565b6000600454905090565b606060007fbb8310d486368db6bd6f849402fdd73ad53d316b5a4b2644ad6efe0f941286d860001b8d8d8d8d60405180838380828437808301925050509250505060405180910390208c8c8c8c8c8c8c604051602001808c81526020018b73ffffffffffffffffffffffffffffffffffffffff1681526020018a815260200189815260200188600181111561444657fe5b81526020018781526020018681526020018581526020018473ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019b505050505050505050505050604051602081830303815290604052805190602001209050601960f81b600160f81b6144d26145b4565b8360405160200180857effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152600101847effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526001018381526020018281526020019450505050506040516020818303038152906040529150509b9a5050505050505050505050565b614565614a94565b61456e816157f4565b8073ffffffffffffffffffffffffffffffffffffffff167f5ac6c46c93c8d0e53714ba3b53db3e7c046da994313d7ed0d192028bc7c228b060405160405180910390a250565b60007f47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a7946921860001b6145e261257a565b30604051602001808481526020018381526020018273ffffffffffffffffffffffffffffffffffffffff168152602001935050505060405160208183030381529060405280519060200120905090565b61463a614a94565b8060016003540310156146b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415801561471f5750600173ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b614791576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303300000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff16600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614614891576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303500000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600360008154809291906001900391905055508173ffffffffffffffffffffffffffffffffffffffff167ff8d49fc529812e9a7c5c50e69c20f0dccc0db8fa95c98bc58cc9a4f1c1299eaf60405160405180910390a28060045414614a5657614a5581612aef565b5b505050565b6040518060400160405280600581526020017f312e342e3100000000000000000000000000000000000000000000000000000081525081565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614614b35576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330333100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b565b600080831415614b4a5760009050614b6b565b6000828402905082848281614b5b57fe5b0414614b6657600080fd5b809150505b92915050565b60008060008360410260208101860151925060408101860151915060ff60418201870151169350509250925092565b600080828401905083811015614bb557600080fd5b8091505092915050565b6000600173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614158015614c8a5750600073ffffffffffffffffffffffffffffffffffffffff16600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614155b614cfc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475331303400000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b614d29858585857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff615da1565b90508015614d79573373ffffffffffffffffffffffffffffffffffffffff167f6895c13664aa4f67288b25d7a21d7aaa34916e355fb9b6fae0a139a9085becb860405160405180910390a2614dbd565b3373ffffffffffffffffffffffffffffffffffffffff167facd2c8702804128fdb0db2bb49f6d127dd0181c13fd45dbfe16de0930e2bd37560405160405180910390a25b949350505050565b6000806000614ddf8e8e8e8e8e8e8e8e8e8e6005546143b5565b905060056000815480929190600101919050555080805190602001209150614e08828286612e8d565b506000614e13615ded565b9050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614614ff9578073ffffffffffffffffffffffffffffffffffffffff166375f0bb528f8f8f8f8f8f8f8f8f8f8f336040518d63ffffffff1660e01b8152600401808d73ffffffffffffffffffffffffffffffffffffffff1681526020018c8152602001806020018a6001811115614eb657fe5b81526020018981526020018881526020018781526020018673ffffffffffffffffffffffffffffffffffffffff1681526020018573ffffffffffffffffffffffffffffffffffffffff168152602001806020018473ffffffffffffffffffffffffffffffffffffffff16815260200183810383528d8d82818152602001925080828437600081840152601f19601f820116905080830192505050838103825285818151815260200191508051906020019080838360005b83811015614f88578082015181840152602081019050614f6d565b50505050905090810190601f168015614fb55780820380516001836020036101000a031916815260200191505b509e505050505050505050505050505050600060405180830381600087803b158015614fe057600080fd5b505af1158015614ff4573d6000803e3d6000fd5b505050505b6101f46150206109c48b01603f60408d028161501157fe5b04615e1e90919063ffffffff16565b015a1015615096576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330313000000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b60005a90506150ff8f8f8f8f8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050508e60008d146150f4578e6150fa565b6109c45a035b615da1565b93506151145a82615e3890919063ffffffff16565b90508380615123575060008a14155b8061512f575060008814155b6151a1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330313300000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b6000808911156151bb576151b8828b8b8b8b615b9b565b90505b84156151fe57837f442e715f626346e8c54381002da614f62bee8d27386535b2521ec8540898556e826040518082815260200191505060405180910390a2615237565b837f23428b18acfb3ea64b08dc0c1d296ea9c09702c09083ca5272e64d115b687d23826040518082815260200191505060405180910390a25b5050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146152e3578073ffffffffffffffffffffffffffffffffffffffff16639327136883856040518363ffffffff1660e01b815260040180838152602001821515815260200192505050600060405180830381600087803b1580156152ca57600080fd5b505af11580156152de573d6000803e3d6000fd5b505050505b50509b9a5050505050505050505050565b60006004541461536c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303000000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b81518111156153e3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600181101561545a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303200000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b60006001905060005b835181101561576057600084828151811061547a57fe5b60200260200101519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141580156154ee5750600173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b801561552657503073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b801561555e57508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614155b6155d0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303300000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff16600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146156d1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303400000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b80600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550809250508080600101915050615463565b506001600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550825160038190555081600481905550505050565b3073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415615896576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475334303000000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b60007f6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d560001b90508181555050565b600073ffffffffffffffffffffffffffffffffffffffff1660016000600173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146159c7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475331303000000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b6001806000600173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614615b9757615a8382615e58565b615af5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330303200000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b615b248260008360017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff615da1565b615b96576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330303000000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b5b5050565b600080600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614615bd85782615bda565b325b9050600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161415615cf257615c443a8610615c21573a615c23565b855b615c36888a614ba090919063ffffffff16565b614b3790919063ffffffff16565b91508073ffffffffffffffffffffffffffffffffffffffff166108fc839081150290604051600060405180830381858888f19350505050615ced576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330313100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b615d97565b615d1785615d09888a614ba090919063ffffffff16565b614b3790919063ffffffff16565b9150615d24848284615e6b565b615d96576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330313200000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b5b5095945050505050565b6000600180811115615daf57fe5b836001811115615dbb57fe5b1415615dd4576000808551602087018986f49050615de4565b600080855160208701888a87f190505b95945050505050565b6000807f4a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c860001b9050805491505090565b600081831015615e2e5781615e30565b825b905092915050565b600082821115615e4757600080fd5b600082840390508091505092915050565b600080823b905060008111915050919050565b60008063a9059cbb8484604051602401808373ffffffffffffffffffffffffffffffffffffffff168152602001828152602001925050506040516020818303038152906040529060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050602060008251602084016000896127105a03f13d60008114615f125760208114615f1a5760009350615f25565b819350615f25565b600051158215171593505b505050939250505056fea2646970667358221220cd2bdb262f44c0636136d3c6bfed2c2458921f82c3bf476053bd2e9ac618b2da64736f6c63430007060033")
1304        );
1305            // 6. Safe multisend:
1306            let _tx_safe_multisend = TransactionRequest::default().with_to(safe_diamond_proxy_address).with_input(
1307                hex!("000000000000000000000000000000000000000000000000000000000000000060a060405234801561001057600080fd5b503073ffffffffffffffffffffffffffffffffffffffff1660808173ffffffffffffffffffffffffffffffffffffffff1660601b8152505060805160601c6102756100646000398060e052506102756000f3fe60806040526004361061001e5760003560e01c80638d80ff0a14610023575b600080fd5b6100dc6004803603602081101561003957600080fd5b810190808035906020019064010000000081111561005657600080fd5b82018360208201111561006857600080fd5b8035906020019184600183028401116401000000008311171561008a57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192905050506100de565b005b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff161415610183576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260308152602001806102106030913960400191505060405180910390fd5b805160205b8181101561020a578083015160f81c6001820184015160601c6015830185015160358401860151605585018701600085600081146101cd57600181146101dd576101e8565b6000808585888a5af191506101e8565b6000808585895af491505b5060008114156101f757600080fd5b8260550187019650505050505050610188565b50505056fe4d756c746953656e642073686f756c64206f6e6c792062652063616c6c6564207669612064656c656761746563616c6ca264697066735822122021102e6d5bc1da75411b41fe2792a1748bf5c49c794e51e81405ccd2399da13564736f6c63430007060033")
1308            );
1309            // other omitted libs: SimulateTxAccessor, CreateCall, and SignMessageLib
1310            // broadcast those transactions
1311            provider.send_transaction(_tx_safe_proxy_factory).await?.watch().await?;
1312            provider
1313                .send_transaction(_tx_safe_compatibility_fallback_handler)
1314                .await?
1315                .watch()
1316                .await?;
1317            provider
1318                .send_transaction(_tx_safe_multisend_call_only)
1319                .await?
1320                .watch()
1321                .await?;
1322            provider.send_transaction(_tx_safe_singleton).await?.watch().await?;
1323            provider.send_transaction(_tx_safe_l2_singleton).await?.watch().await?;
1324            provider.send_transaction(_tx_safe_multisend).await?.watch().await?;
1325        }
1326        Ok(())
1327    }
1328
1329    #[tokio::test]
1330    async fn test_native_and_token_balances_in_anvil_with_multicall() -> anyhow::Result<()> {
1331        // create a keypair
1332        let kp = ChainKeypair::random();
1333        let kp_address = Address::from(&kp.public().to_address().into());
1334
1335        // launch local anvil instance
1336        let anvil = create_anvil(None);
1337        let contract_deployer = ChainKeypair::from_secret(anvil.keys()[0].to_bytes().as_ref())?;
1338        let client = create_rpc_client_to_anvil(&anvil, &contract_deployer);
1339        // deploy hopr contracts
1340        let instances = ContractInstances::deploy_for_testing(client.clone(), &contract_deployer)
1341            .await
1342            .expect("failed to deploy");
1343        // deploy multicall contract
1344        deploy_multicall3_for_testing(client.clone()).await?;
1345
1346        // get native and token balances
1347        let (native_balance, token_balance) = get_native_and_token_balances(instances.token, vec![kp_address]).await?;
1348        assert_eq!(native_balance.len(), 1, "invalid native balance lens");
1349        assert_eq!(token_balance.len(), 1, "invalid token balance lens");
1350        assert_eq!(native_balance[0].to::<u64>(), 0u64, "wrong native balance");
1351        assert_eq!(token_balance[0].to::<u64>(), 0u64, "wrong token balance");
1352        drop(anvil);
1353        Ok(())
1354    }
1355
1356    #[tokio::test]
1357    async fn test_transfer_or_mint_tokens_in_anvil_with_multicall() -> anyhow::Result<()> {
1358        let _ = env_logger::builder().is_test(true).try_init();
1359
1360        let mut addresses: Vec<Address> = Vec::new();
1361        for _ in 0..4 {
1362            addresses.push(get_random_address_for_testing());
1363        }
1364        let desired_amount = vec![U256::from(1), U256::from(2), U256::from(3), U256::from(4)];
1365
1366        // launch local anvil instance
1367        let anvil = create_anvil(None);
1368        let contract_deployer = ChainKeypair::from_secret(anvil.keys()[0].to_bytes().as_ref())?;
1369        let client = create_rpc_client_to_anvil(&anvil, &contract_deployer);
1370        // deploy hopr contracts
1371        let instances = ContractInstances::deploy_for_testing(client.clone(), &contract_deployer)
1372            .await
1373            .expect("failed to deploy");
1374
1375        // deploy multicall contract
1376        deploy_multicall3_for_testing(client.clone()).await?;
1377        // grant deployer token minter role
1378        let encoded_minter_role = keccak256(b"MINTER_ROLE");
1379        instances
1380            .token
1381            .grantRole(encoded_minter_role, a2h(contract_deployer.public().to_address()))
1382            .send()
1383            .await?
1384            .watch()
1385            .await?;
1386
1387        // test the deployer has minter role now
1388        let check_minter_role = instances
1389            .token
1390            .hasRole(encoded_minter_role, a2h(contract_deployer.public().to_address()))
1391            .call()
1392            .await?;
1393        assert!(check_minter_role, "deployer does not have minter role yet");
1394
1395        // transfer or mint tokens to addresses
1396        let total_transferred_amount =
1397            transfer_or_mint_tokens(instances.token.clone(), addresses.clone(), desired_amount.clone()).await?;
1398
1399        // get native and token balances
1400        let (native_balance, token_balance) = get_native_and_token_balances(instances.token, addresses.clone()).await?;
1401
1402        assert_eq!(native_balance.len(), 4, "invalid native balance lens");
1403        assert_eq!(token_balance.len(), 4, "invalid token balance lens");
1404        for (i, amount) in desired_amount.iter().enumerate() {
1405            assert_eq!(&token_balance[i], amount, "token balance unmatch");
1406        }
1407
1408        assert_eq!(
1409            total_transferred_amount,
1410            U256::from(10),
1411            "amount transferred does not equal to the desired amount"
1412        );
1413        Ok(())
1414    }
1415
1416    #[tokio::test]
1417    async fn test_transfer_or_mint_tokens_in_anvil_with_one_recipient() -> anyhow::Result<()> {
1418        let addresses: Vec<Address> = vec![get_random_address_for_testing()];
1419        let desired_amount = vec![U256::from(42)];
1420
1421        // launch local anvil instance
1422        let anvil = create_anvil(None);
1423        let contract_deployer = ChainKeypair::from_secret(anvil.keys()[0].to_bytes().as_ref())?;
1424        let client = create_rpc_client_to_anvil(&anvil, &contract_deployer);
1425        // deploy hopr contracts
1426        let instances = ContractInstances::deploy_for_testing(client.clone(), &contract_deployer)
1427            .await
1428            .expect("failed to deploy");
1429
1430        // deploy multicall contract
1431        deploy_multicall3_for_testing(client.clone()).await?;
1432        // grant deployer token minter role
1433        let encoded_minter_role = keccak256(b"MINTER_ROLE");
1434        instances
1435            .token
1436            .grantRole(encoded_minter_role, a2h(contract_deployer.public().to_address()))
1437            .send()
1438            .await?
1439            .watch()
1440            .await?;
1441
1442        // test the deployer has minter role now
1443        let check_minter_role = instances
1444            .token
1445            .hasRole(encoded_minter_role, a2h(contract_deployer.public().to_address()))
1446            .call()
1447            .await?;
1448        assert!(check_minter_role, "deployer does not have minter role yet");
1449
1450        // transfer or mint tokens to addresses
1451        let total_transferred_amount =
1452            transfer_or_mint_tokens(instances.token.clone(), addresses.clone(), desired_amount.clone()).await?;
1453
1454        // get native and token balances
1455        let (native_balance, token_balance) = get_native_and_token_balances(instances.token, addresses.clone()).await?;
1456        assert_eq!(native_balance.len(), 1, "invalid native balance lens");
1457        assert_eq!(token_balance.len(), 1, "invalid token balance lens");
1458        for (i, amount) in desired_amount.iter().enumerate() {
1459            assert_eq!(&token_balance[i], amount, "token balance unmatch");
1460        }
1461
1462        assert_eq!(
1463            total_transferred_amount, desired_amount[0],
1464            "amount transferred does not equal to the desired amount"
1465        );
1466        Ok(())
1467    }
1468
1469    #[tokio::test]
1470    async fn test_transfer_or_mint_tokens_in_anvil_without_recipient() -> anyhow::Result<()> {
1471        let addresses: Vec<Address> = Vec::new();
1472        let desired_amount: Vec<U256> = Vec::new();
1473
1474        // launch local anvil instance
1475        let anvil = create_anvil(None);
1476        let contract_deployer = ChainKeypair::from_secret(anvil.keys()[0].to_bytes().as_ref())?;
1477        let client = create_rpc_client_to_anvil(&anvil, &contract_deployer);
1478        // deploy hopr contracts
1479        let instances = ContractInstances::deploy_for_testing(client.clone(), &contract_deployer)
1480            .await
1481            .expect("failed to deploy");
1482
1483        // deploy multicall contract
1484        deploy_multicall3_for_testing(client.clone()).await?;
1485
1486        // transfer or mint tokens to addresses
1487        let total_transferred_amount =
1488            transfer_or_mint_tokens(instances.token.clone(), addresses.clone(), desired_amount.clone()).await?;
1489
1490        // get native and token balances
1491        let (native_balance, token_balance) = get_native_and_token_balances(instances.token, addresses.clone()).await?;
1492        assert_eq!(native_balance.len(), 0, "invalid native balance lens");
1493        assert_eq!(token_balance.len(), 0, "invalid token balance lens");
1494        // for (i, amount) in desired_amount.iter().enumerate() {
1495        //     assert_eq!(token_balance[i].as_u64(), amount.as_u64(), "token balance unmatch");
1496        // }
1497
1498        assert_eq!(
1499            total_transferred_amount,
1500            U256::from(0),
1501            "amount transferred does not equal to the desired amount"
1502        );
1503        Ok(())
1504    }
1505
1506    #[tokio::test]
1507    async fn test_transfer_native_tokens_in_anvil_with_multicall() -> anyhow::Result<()> {
1508        let mut addresses: Vec<Address> = Vec::new();
1509        for _ in 0..4 {
1510            addresses.push(get_random_address_for_testing());
1511        }
1512        let desired_amount = vec![U256::from(1), U256::from(2), U256::from(3), U256::from(4)];
1513
1514        // launch local anvil instance
1515        let anvil = create_anvil(None);
1516        let contract_deployer = ChainKeypair::from_secret(anvil.keys()[0].to_bytes().as_ref())?;
1517        let client = create_rpc_client_to_anvil(&anvil, &contract_deployer);
1518        let instances = ContractInstances::deploy_for_testing(client.clone(), &contract_deployer)
1519            .await
1520            .expect("failed to deploy");
1521
1522        // deploy multicall contract
1523        deploy_multicall3_for_testing(client.clone()).await?;
1524
1525        // transfer native tokens to addresses
1526        let total_transferred_amount =
1527            transfer_native_tokens(client.clone(), addresses.clone(), desired_amount.clone()).await?;
1528
1529        // get native and token balances
1530        let (native_balance, token_balance) = get_native_and_token_balances(instances.token, addresses.clone()).await?;
1531        assert_eq!(native_balance.len(), 4, "invalid native balance lens");
1532        assert_eq!(token_balance.len(), 4, "invalid token balance lens");
1533        for (i, amount) in desired_amount.iter().enumerate() {
1534            assert_eq!(&native_balance[i], amount, "native balance unmatch");
1535        }
1536
1537        assert_eq!(
1538            total_transferred_amount,
1539            U256::from(10),
1540            "amount transferred does not equal to the desired amount"
1541        );
1542        Ok(())
1543    }
1544
1545    #[tokio::test]
1546    async fn test_deploy_proxy() -> anyhow::Result<()> {
1547        let prediction = deploy_proxy(
1548            address!("41675c099f32341bf84bfc5382af534df5c7461a"),
1549            hex!("09e458584ce79e57b65cb303dc136c5d53e17b676599b9b7bc03815e0eef5172"),
1550            SAFE_SAFEPROXYFACTORY_ADDRESS,
1551        )?;
1552
1553        assert_eq!(
1554            prediction,
1555            address!("ec5c8d045dfa1f93785125c26e187e9439f67105"),
1556            "cannot reproduce proxy"
1557        );
1558        Ok(())
1559    }
1560    #[tokio::test]
1561    async fn test_get_salt_from_salt_nonce() -> anyhow::Result<()> {
1562        let salt = get_salt_from_salt_nonce(
1563            hex!("b63e800d00000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001400000000000000000000000002a15de4410d4c8af0a7b6c12803120f43c42b8200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000098b275485c406573d042848d66eb9d63fca311c00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000").into(),
1564            B256::from_str("E5EAFDE6416CCB48925026B6313D62A98C0997E03591E29EB4CF1EA968D6BC8F")?// &U256::from_str("103994836888229670573364883831672511342967953907147914065931589108526220754063")?.into(),
1565        )?;
1566
1567        assert_eq!(
1568            salt.to_vec(),
1569            Bytes::from_str("09e458584ce79e57b65cb303dc136c5d53e17b676599b9b7bc03815e0eef5172")?,
1570            "cannot reproduce salt"
1571        );
1572        Ok(())
1573    }
1574
1575    #[tokio::test]
1576    async fn test_safe_and_module_address_prediction() -> anyhow::Result<()> {
1577        init_tracing();
1578        // testing value extracted from https://dashboard.tenderly.co/tx/xdai/0x510e3ac3dc7939cae2525a0b0f096ad709b23d94169e0fbf2e1154fdd6911c49?trace=0
1579        let _ = env_logger::builder().is_test(true).try_init();
1580
1581        // prepare some input data
1582        let mut admin_addresses: Vec<Address> = Vec::new();
1583        for _ in 0..2 {
1584            admin_addresses.push(get_random_address_for_testing());
1585        }
1586
1587        // launch local anvil instance
1588        let anvil = create_anvil(None);
1589        let contract_deployer = ChainKeypair::from_secret(anvil.keys()[0].to_bytes().as_ref())?;
1590        let client = create_rpc_client_to_anvil(&anvil, &contract_deployer);
1591        let instances = ContractInstances::deploy_for_testing(client.clone(), &contract_deployer)
1592            .await
1593            .expect("failed to deploy");
1594        // deploy multicall contract
1595        deploy_multicall3_for_testing(client.clone()).await?;
1596        // deploy safe suits
1597        deploy_safe_suites(client.clone()).await?;
1598
1599        let caller = client.default_signer_address();
1600
1601        // build the default permissions of capabilities
1602        let default_target = format!("{:?}{}", instances.channels.address(), DEFAULT_CAPABILITY_PERMISSIONS);
1603        debug!("default target {:?}", default_target);
1604        // salt nonce
1605        let curr_nonce = client.get_transaction_count(caller).pending().await?;
1606        let nonce = keccak256((caller, U256::from(curr_nonce)).abi_encode_packed());
1607
1608        let safe_address = predict_safe_address(
1609            *instances.stake_factory.address(),
1610            vec![caller],
1611            nonce,
1612            SAFE_COMPATIBILITYFALLBACKHANDLER_ADDRESS,
1613            SAFE_SAFE_L2_ADDRESS,
1614            SAFE_SAFEPROXYFACTORY_ADDRESS,
1615        )?;
1616
1617        debug!("predict_safe_address {:?}", safe_address);
1618
1619        let safe_address_predicted_from_sc = instances
1620            .stake_factory
1621            .predictSafeAddress(vec![caller], nonce.into())
1622            .call()
1623            .await?;
1624        debug!(
1625            "predicted safe address from smart contract {:?}",
1626            safe_address_predicted_from_sc.to_string()
1627        );
1628
1629        assert_eq!(
1630            safe_address, safe_address_predicted_from_sc,
1631            "safe address prediction local vs smart contract does not match"
1632        );
1633
1634        let module_address_predicted_from_sc = instances
1635            .stake_factory
1636            .predictModuleAddress_1(
1637                caller,
1638                nonce.into(),
1639                safe_address,
1640                U256::from_str(default_target.as_str())?.into(),
1641            )
1642            .call()
1643            .await?;
1644        info!(
1645            "predicted module address from smart contract {:?}",
1646            module_address_predicted_from_sc.to_string()
1647        );
1648
1649        // deploy a safe proxy instance and a module proxy instance with multicall as an owner
1650        let deployment_receipt = instances
1651            .stake_factory
1652            .clone(
1653                //*instances.module_implementation.address(),
1654                nonce.into(),
1655                U256::from_str(&default_target)?.into(),
1656                vec![caller],
1657            )
1658            .send()
1659            .await?
1660            .get_receipt()
1661            .await?;
1662
1663        // parse the safe and module addresses
1664        let module_log = deployment_receipt
1665            .decoded_log::<HoprNodeStakeFactory::NewHoprNodeStakeModule>()
1666            .ok_or_else(|| anyhow::anyhow!("Module log not found"))?;
1667
1668        let safe_log = deployment_receipt
1669            .decoded_log::<HoprNodeStakeFactory::NewHoprNodeStakeSafe>()
1670            .ok_or_else(|| anyhow::anyhow!("Safe log not found"))?;
1671
1672        let module_addr = module_log.instance;
1673        let safe_addr = safe_log.instance;
1674
1675        info!("deployed module address {:?}", module_addr);
1676
1677        assert_eq!(safe_addr, safe_address, "safe prediction does not match");
1678        assert_eq!(
1679            module_addr, module_address_predicted_from_sc,
1680            "module prediction does not match"
1681        );
1682        Ok(())
1683    }
1684
1685    #[tokio::test]
1686    async fn test_deploy_safe_and_module() -> anyhow::Result<()> {
1687        init_tracing();
1688        let _ = env_logger::builder().is_test(true).try_init();
1689
1690        // prepare some input data
1691        let mut admin_addresses: Vec<Address> = Vec::new();
1692        for _ in 0..2 {
1693            admin_addresses.push(get_random_address_for_testing());
1694        }
1695        let mut node_addresses: Vec<Address> = Vec::new();
1696        for _ in 0..2 {
1697            node_addresses.push(get_random_address_for_testing());
1698        }
1699
1700        // launch local anvil instance
1701        let anvil = create_anvil(None);
1702        let contract_deployer = ChainKeypair::from_secret(anvil.keys()[0].to_bytes().as_ref())?;
1703        let client = create_rpc_client_to_anvil(&anvil, &contract_deployer);
1704        let instances = ContractInstances::deploy_for_testing(client.clone(), &contract_deployer)
1705            .await
1706            .expect("failed to deploy");
1707        // deploy multicall contract
1708        deploy_multicall3_for_testing(client.clone()).await?;
1709        // deploy safe suits
1710        deploy_safe_suites(client.clone()).await?;
1711
1712        // register some nodes
1713        let (safe, node_module) = deploy_safe_module_with_targets_and_nodes(
1714            instances.stake_factory,
1715            *instances.channels.address(),
1716            Some(node_addresses.clone()),
1717            admin_addresses.clone(),
1718            U256::from(2),
1719        )
1720        .await?;
1721
1722        // check announcement is a target
1723        let try_get_announcement_target = node_module
1724            .tryGetTarget(*instances.announcements.address())
1725            .call()
1726            .await?;
1727
1728        assert!(try_get_announcement_target._0, "announcement is not a target");
1729
1730        // check allowance for channel contract has increased
1731        let allowance = instances
1732            .token
1733            .allowance(*safe.address(), *instances.channels.address())
1734            .call()
1735            .await?;
1736
1737        assert_eq!(
1738            allowance,
1739            U256::from(1_000_000_000_000_000_000_000_u128),
1740            "allowance is not set"
1741        );
1742
1743        // check nodes have been included in the module
1744        for node_address in node_addresses {
1745            let is_node_included = node_module.isNode(node_address).call().await?;
1746            assert!(is_node_included, "failed to include a node");
1747        }
1748
1749        // check owners are provided admins
1750        let owners = safe.getOwners().call().await?;
1751        let thresold = safe.getThreshold().call().await?;
1752
1753        assert_eq!(owners.len(), 2, "should have 2 owners");
1754        for (i, owner) in owners.iter().enumerate() {
1755            assert_eq!(owner, &admin_addresses[i], "admin is wrong");
1756        }
1757        assert_eq!(thresold, U256::from(2), "threshold should be two");
1758        Ok(())
1759    }
1760
1761    #[tokio::test]
1762    async fn test_safe_tx_via_multisend() -> anyhow::Result<()> {
1763        // set allowance for token transfer for the safe multiple times
1764        let _ = env_logger::builder().is_test(true).try_init();
1765
1766        // prepare some input data
1767        let desired_amount = vec![U256::from(1), U256::from(2), U256::from(3), U256::from(4)];
1768
1769        // launch local anvil instance
1770        let anvil = create_anvil(None);
1771        let contract_deployer = ChainKeypair::from_secret(anvil.keys()[0].to_bytes().as_ref())?;
1772        let client = create_rpc_client_to_anvil(&anvil, &contract_deployer);
1773        let instances = ContractInstances::deploy_for_testing(client.clone(), &contract_deployer)
1774            .await
1775            .expect("failed to deploy");
1776        // deploy multicall contract
1777        deploy_multicall3_for_testing(client.clone()).await?;
1778        // deploy safe suits
1779        deploy_safe_suites(client.clone()).await?;
1780
1781        // create a safe
1782        let (safe, _node_module) = deploy_safe_module_with_targets_and_nodes(
1783            instances.stake_factory,
1784            *instances.channels.address(),
1785            None,
1786            vec![a2h(contract_deployer.public().to_address())],
1787            U256::from(1),
1788        )
1789        .await?;
1790
1791        // check owner of safe
1792        let is_owner = safe.getOwners().call().await?;
1793        assert_eq!(is_owner.len(), 1, "safe has too many owners");
1794        assert_eq!(
1795            is_owner[0].0.0,
1796            contract_deployer.public().to_address().as_ref(),
1797            "safe wrong owner"
1798        );
1799
1800        // check allowance for channel contract is zero
1801        let allowance = instances
1802            .token
1803            .allowance(*safe.address(), *instances.channels.address())
1804            .call()
1805            .await?;
1806
1807        assert_eq!(
1808            allowance,
1809            U256::from(1_000_000_000_000_000_000_000_u128),
1810            "allowance is not set"
1811        );
1812
1813        let mut multisend_txns: Vec<MultisendTransaction> = Vec::new();
1814        for val in desired_amount {
1815            multisend_txns.push(MultisendTransaction {
1816                // build multisend tx payload
1817                encoded_data: approveCall {
1818                    spender: *instances.channels.address(),
1819                    value: val,
1820                }
1821                .abi_encode()
1822                .into(),
1823                tx_operation: SafeTxOperation::Call,
1824                to: *instances.token.address(),
1825                value: U256::ZERO,
1826            });
1827        }
1828
1829        // get chain_id and safe_nonce
1830        let chain_id = client.get_chain_id().await?;
1831        let safe_nonce = safe.nonce().call().await?;
1832        debug!("safe address {:?}", safe.address());
1833        debug!("chain_id {:?}", chain_id);
1834        debug!("safe_nonce {:?}", safe_nonce);
1835
1836        // send safe transaction
1837        send_multisend_safe_transaction_with_threshold_one(
1838            safe.clone(),
1839            contract_deployer,
1840            SAFE_MULTISEND_ADDRESS,
1841            multisend_txns,
1842            U256::from(chain_id),
1843            safe_nonce,
1844        )
1845        .await?;
1846
1847        // check allowance for channel contract is 4
1848        let new_allowance = instances
1849            .token
1850            .allowance(*safe.address(), *instances.channels.address())
1851            .call()
1852            .await?;
1853
1854        assert_eq!(new_allowance, U256::from(4), "final allowance is not desired");
1855        Ok(())
1856    }
1857
1858    #[tokio::test]
1859    async fn test_register_nodes_to_node_safe_registry() -> anyhow::Result<()> {
1860        // set allowance for token transfer for the safe multiple times
1861        let _ = env_logger::builder().is_test(true).try_init();
1862
1863        // launch local anvil instance
1864        let anvil = create_anvil(None);
1865        let contract_deployer = ChainKeypair::from_secret(anvil.keys()[0].to_bytes().as_ref())?;
1866        let client = create_rpc_client_to_anvil(&anvil, &contract_deployer);
1867        let instances = ContractInstances::deploy_for_testing(client.clone(), &contract_deployer)
1868            .await
1869            .expect("failed to deploy");
1870        // deploy multicall contract
1871        deploy_multicall3_for_testing(client.clone()).await?;
1872        // deploy safe suits
1873        deploy_safe_suites(client.clone()).await?;
1874
1875        let deployer_vec: Vec<Address> = vec![a2h(contract_deployer.public().to_address())];
1876
1877        // create a safe
1878        let (safe, node_module) = deploy_safe_module_with_targets_and_nodes(
1879            instances.stake_factory,
1880            *instances.channels.address(),
1881            Some(deployer_vec.clone()),
1882            deployer_vec.clone(),
1883            U256::from(1),
1884        )
1885        .await?;
1886
1887        // register one node to safe
1888        instances
1889            .safe_registry
1890            .registerSafeByNode(*safe.address())
1891            .send()
1892            .await?
1893            .watch()
1894            .await?;
1895
1896        // get registration info
1897        let get_registered_safe =
1898            get_registered_safes_for_nodes_on_node_safe_registry(instances.safe_registry.clone(), deployer_vec.clone())
1899                .await?;
1900
1901        assert_eq!(get_registered_safe.len(), 1, "cannot read registered safe");
1902        assert_eq!(&get_registered_safe[0], safe.address(), "registered safe is wrong");
1903
1904        // deregister the node from safe
1905        deregister_nodes_from_node_safe_registry_and_remove_from_module(
1906            instances.safe_registry.clone(),
1907            deployer_vec.clone(),
1908            vec![*node_module.address()],
1909            contract_deployer.clone(),
1910        )
1911        .await?;
1912
1913        // get registration info (updated)
1914        let get_registered_safe =
1915            get_registered_safes_for_nodes_on_node_safe_registry(instances.safe_registry.clone(), deployer_vec.clone())
1916                .await?;
1917
1918        assert_eq!(get_registered_safe.len(), 1, "cannot read registered safe");
1919        assert_eq!(get_registered_safe[0], Address::ZERO, "node is still registered");
1920
1921        // node is removed
1922        let is_removed = node_module
1923            .isNode(a2h(contract_deployer.public().to_address()))
1924            .call()
1925            .await?;
1926        assert!(!is_removed, "node is not removed");
1927        Ok(())
1928    }
1929
1930    #[tokio::test]
1931    async fn test_include_nodes_to_module() -> anyhow::Result<()> {
1932        // set allowance for token transfer for the safe multiple times
1933        let _ = env_logger::builder().is_test(true).try_init();
1934
1935        let mut node_addresses: Vec<Address> = Vec::new();
1936        for _ in 0..2 {
1937            node_addresses.push(get_random_address_for_testing());
1938        }
1939
1940        // launch local anvil instance
1941        let anvil = create_anvil(None);
1942        let contract_deployer = ChainKeypair::from_secret(anvil.keys()[0].to_bytes().as_ref())?;
1943        let client = create_rpc_client_to_anvil(&anvil, &contract_deployer);
1944        let instances = ContractInstances::deploy_for_testing(client.clone(), &contract_deployer)
1945            .await
1946            .expect("failed to deploy");
1947        // deploy multicall contract
1948        deploy_multicall3_for_testing(client.clone()).await?;
1949        // deploy safe suits
1950        deploy_safe_suites(client.clone()).await?;
1951
1952        let deployer_vec: Vec<Address> = vec![a2h(contract_deployer.public().to_address())];
1953
1954        // create a safe
1955        let (safe, node_module) = deploy_safe_module_with_targets_and_nodes(
1956            instances.stake_factory,
1957            *instances.channels.address(),
1958            None,
1959            deployer_vec.clone(),
1960            U256::from(1),
1961        )
1962        .await?;
1963
1964        // check ndoes are not included
1965        for node_addr in node_addresses.clone() {
1966            // node is removed
1967            let node_is_not_included = node_module.isNode(node_addr).call().await?;
1968            assert!(!node_is_not_included, "node should not be included");
1969        }
1970
1971        // include nodes to safe
1972        include_nodes_to_module(safe, node_addresses.clone(), *node_module.address(), contract_deployer).await?;
1973
1974        // check nodes are included
1975        // check nodes are not included
1976        for node_addr in node_addresses {
1977            // node is removed
1978            let node_is_included = node_module.isNode(node_addr).call().await?;
1979            assert!(node_is_included, "node should be included");
1980        }
1981        Ok(())
1982    }
1983
1984    #[tokio::test]
1985    async fn test_migrate_nodes_to_new_network() -> anyhow::Result<()> {
1986        // set allowance for token transfer for the safe multiple times
1987        let _ = env_logger::builder().is_test(true).try_init();
1988
1989        let mut node_addresses: Vec<Address> = Vec::new();
1990        for _ in 0..2 {
1991            node_addresses.push(get_random_address_for_testing());
1992        }
1993
1994        // launch local anvil instance
1995        let anvil = create_anvil(None);
1996        let contract_deployer = ChainKeypair::from_secret(anvil.keys()[0].to_bytes().as_ref())?;
1997        let self_address: Address = a2h(contract_deployer.public().to_address());
1998        let client = create_rpc_client_to_anvil(&anvil, &contract_deployer);
1999        let instances = ContractInstances::deploy_for_testing(client.clone(), &contract_deployer)
2000            .await
2001            .expect("failed to deploy");
2002        // deploy multicall contract
2003        deploy_multicall3_for_testing(client.clone()).await?;
2004        // deploy safe suits
2005        deploy_safe_suites(client.clone()).await?;
2006
2007        // deploy some new contracts for the new network
2008        let new_safe_registry = HoprNodeSafeRegistry::deploy(client.clone()).await?;
2009        let new_token = HoprToken::deploy(client.clone()).await?;
2010        let new_channels = HoprChannels::deploy(
2011            client.clone(),
2012            *new_token.address(),
2013            1_u32,
2014            *new_safe_registry.address(),
2015        )
2016        .await?;
2017        let new_announcements = HoprAnnouncements::deploy(client.clone()).await?;
2018
2019        let deployer_vec: Vec<Address> = vec![self_address];
2020
2021        // create a safe
2022        let (safe, node_module) = deploy_safe_module_with_targets_and_nodes(
2023            instances.stake_factory,
2024            *instances.channels.address(),
2025            None,
2026            deployer_vec.clone(),
2027            U256::from(1),
2028        )
2029        .await?;
2030
2031        // check new network is not included
2032        let old_channels_inclusion = node_module.tryGetTarget(*instances.channels.address()).call().await?;
2033        assert!(old_channels_inclusion._0, "old channel should be included");
2034        let new_channels_inclusion = node_module.tryGetTarget(*new_channels.address()).call().await?;
2035        assert!(!new_channels_inclusion._0, "new channel should not be included");
2036
2037        // migrate nodes
2038        migrate_nodes(
2039            safe,
2040            *node_module.address(),
2041            *new_channels.address(),
2042            *new_token.address(),
2043            *new_announcements.address(),
2044            U256::MAX,
2045            contract_deployer,
2046        )
2047        .await?;
2048
2049        // check new network is included
2050        let old_channels_inclusion = node_module.tryGetTarget(*instances.channels.address()).call().await?;
2051        assert!(old_channels_inclusion._0, "old channel should still be included");
2052        let new_channels_inclusion = node_module.tryGetTarget(*new_channels.address()).call().await?;
2053        assert!(new_channels_inclusion._0, "new channel should now be included");
2054        Ok(())
2055    }
2056
2057    #[tokio::test]
2058    async fn test_debug_node_safe_module_setup_main() -> anyhow::Result<()> {
2059        // set allowance for token transfer for the safe multiple times
2060        let _ = env_logger::builder().is_test(true).try_init();
2061
2062        let mut node_addresses: Vec<Address> = Vec::new();
2063        for _ in 0..2 {
2064            node_addresses.push(get_random_address_for_testing());
2065        }
2066
2067        // launch local anvil instance
2068        let anvil = create_anvil(None);
2069        let contract_deployer = ChainKeypair::from_secret(anvil.keys()[0].to_bytes().as_ref())?;
2070        let client = create_rpc_client_to_anvil(&anvil, &contract_deployer);
2071        let instances = ContractInstances::deploy_for_testing(client.clone(), &contract_deployer)
2072            .await
2073            .expect("failed to deploy");
2074        // deploy multicall contract
2075        deploy_multicall3_for_testing(client.clone()).await?;
2076        // deploy safe suits
2077        deploy_safe_suites(client.clone()).await?;
2078
2079        let deployer_vec: Vec<Address> = vec![a2h(contract_deployer.public().to_address())];
2080
2081        // create a safe
2082        let (_safe, _node_module) = deploy_safe_module_with_targets_and_nodes(
2083            instances.stake_factory,
2084            *instances.channels.address(),
2085            None,
2086            deployer_vec.clone(),
2087            U256::from(1),
2088        )
2089        .await?;
2090
2091        Ok(())
2092    }
2093}