From 085c2ccda9695ff0005786bdb1b2218849b0e0cc Mon Sep 17 00:00:00 2001 From: Carlos Beltran Date: Tue, 1 Nov 2022 17:55:24 -0400 Subject: [PATCH 01/14] wip - lz gated follow module; need to figure out async follow validation --- contracts/LZGatedFollowModule.sol | 162 ++++++++++++++++++++++++ contracts/LZGatedProxy.sol | 82 ++++++++++++ contracts/lz/NonblockingFlexLzApp.sol | 84 ++++++++++++ contracts/lz/SimpleLzApp.sol | 98 ++++++++++++++ contracts/utils/ExcessivelySafeCall.sol | 138 ++++++++++++++++++++ 5 files changed, 564 insertions(+) create mode 100644 contracts/LZGatedFollowModule.sol create mode 100644 contracts/LZGatedProxy.sol create mode 100644 contracts/lz/NonblockingFlexLzApp.sol create mode 100644 contracts/lz/SimpleLzApp.sol create mode 100644 contracts/utils/ExcessivelySafeCall.sol diff --git a/contracts/LZGatedFollowModule.sol b/contracts/LZGatedFollowModule.sol new file mode 100644 index 0000000..2645334 --- /dev/null +++ b/contracts/LZGatedFollowModule.sol @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.10; + +import {ModuleBase, Errors} from "@aave/lens-protocol/contracts/core/modules/ModuleBase.sol"; +import {FollowValidatorFollowModuleBase} from "@aave/lens-protocol/contracts/core/modules/follow/FollowValidatorFollowModuleBase.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import {NonblockingFlexLzApp} from "./lz/NonblockingFlexLzApp.sol"; + +/** + * @title LZGatedFollowModule + * @author Hypotenuse Labs + * + * @notice A Lens Follow Module that allows profile holders to gate their following with ERC20 or ERC721 balances held + * on other chains. + */ +contract LZGatedFollowModule is FollowValidatorFollowModuleBase, NonblockingFlexLzApp { + address public zroPaymentAddress; // ZRO payment address + uint16 public sourceChainId; // chainId (lz) this module is deployed on + + struct GatedFollowData { + address remoteContract; // the remote contract to read from + uint256 balanceThreshold; // result of balanceOf() should be greater than or equal to + uint16 remoteChainId; // the remote chainId to read against + } + + mapping (uint256 => GatedFollowData) public gatedFollowPerProfile; // profileId => gated follow data + + event InitFollowModule(uint256 indexed profileId, address tokenContract, uint256 balanceThreshold, uint16 chainId); + + /** + * @dev contract constructor + * @param hub LensHub + * @param _lzEndpoint: LayerZero endpoint on this chain to relay messages + * @param remoteChainIds: whitelisted destination chain ids (supported by LayerZero) + * @param remoteProxies: proxy destination contracts (deployed by us) + * NOTE: we set `zroPaymentAddress` to the zero address as it does not make sense to make this module ownable only to + * set this variable once their token is out, logistics, etc. + */ + constructor( + address hub, + address _lzEndpoint, + uint16[] memory remoteChainIds, + uint16 _sourceChainId, + bytes[] memory remoteProxies + ) ModuleBase(hub) NonblockingFlexLzApp(_lzEndpoint, msg.sender, remoteChainIds, remoteProxies) { + zroPaymentAddress = address(0); + sourceChainId = _sourceChainId; + } + + /** + * @notice Initialize this follow module for the given profile + * + * @param profileId The profile ID of the profile to initialize this module for. + * @param data The arbitrary data parameter, which in this particular module initialization will be just ignored. + * + * @return bytes Empty bytes. + */ + function initializeFollowModule(uint256 profileId, bytes calldata data) + external + override + onlyHub + returns (bytes memory) + { + ( + address tokenContract, + uint256 balanceThreshold, + uint16 chainId + ) = abi.decode(data, (address, uint256, uint16)); + + if (address(tokenContract) == address(0) || (chainId != sourceChainId && _lzRemoteLookup[chainId].length == 0)) { + revert Errors.InitParamsInvalid(); + } + + gatedFollowPerProfile[profileId] = GatedFollowData({ + remoteChainId: chainId, + remoteContract: tokenContract, + balanceThreshold: balanceThreshold + }); + + emit InitFollowModule(profileId, tokenContract, balanceThreshold, chainId); + + return new bytes(0); + } + + /** + * @dev Process a follow by: + * - revert if the `follower` is already following the profile + * - if the profile has set gated follow data on this chain, just do the token balance check inline + * - else send the payload for one of our cross-chain proxies to do the check for us and return the result async + */ + function processFollow( + address follower, + uint256 profileId, + bytes calldata // data + ) external override onlyHub { + if (gatedFollowPerProfile[profileId].remoteChainId == sourceChainId) { + if (!_checkThreshold(follower, gatedFollowPerProfile[profileId])) { + revert Errors.FollowInvalid(); + } + } else { + // make the async balance check, expect result in `#_nonblockingLzReceive` + _lzSend( + gatedFollowPerProfile[profileId].remoteChainId, + abi.encode( + follower, + gatedFollowPerProfile[profileId].remoteContract, + profileId, + gatedFollowPerProfile[profileId].balanceThreshold + ), + payable(follower), + zroPaymentAddress, + bytes("") + ); + + // @TODO: the only feedback to give back to LensHub is a revert if the follow is invalid + // - we won't know if the follow is valid or not until after the async execution on the remote chain + // - even if we used an oracle for a data read, it's async + // - we either + // 1) return some kind of pending state so LensHub doesn't immediately mint the follow nft + // 2) maintain some permission to later execute the minting of follow nfts if the async check is successful + } + } + + /** + * @dev We don't need to execute any additional logic on transfers in this follow module. + */ + function followModuleTransferHook( + uint256 profileId, + address from, + address to, + uint256 followNFTTokenId + ) external override {} + + // @TODO: pending how to handle async requests + function _nonblockingLzReceive( + uint16 _srcChainId, + bytes memory _srcAddress, + uint64 _nonce, + bytes memory _payload + ) internal override { + + } + + /** + * @dev Check that `account` meets the threshold of held tokens in `tokenContract`; we use the standard `#balanceOf` + * function signature for ERC721 and ERC20 + */ + function _checkThreshold(address account, GatedFollowData memory data) private returns (bool) { + ( + bool success, + bytes memory result + ) = address(data.remoteContract).call(abi.encodeWithSignature("balanceOf(address)", account)); + + if (!success) return false; + + (uint256 balance) = abi.decode(result, (uint256)); + + return balance >= data.balanceThreshold; + } +} diff --git a/contracts/LZGatedProxy.sol b/contracts/LZGatedProxy.sol new file mode 100644 index 0000000..c4e913e --- /dev/null +++ b/contracts/LZGatedProxy.sol @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.10; + +import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import "./lz/SimpleLzApp.sol"; +import "./utils/ExcessivelySafeCall.sol"; + +/** + * @title LZGatedProxy + * @notice This contract acts as a proxy for `LZGatedFollowModule` and `LZGatedReference` in order to read + * token balances from remote contracts on any chain supported by layerzero. + */ +contract LZGatedProxy is SimpleLzApp { + using ExcessivelySafeCall for address; + + address public zroPaymentAddress; // ZRO payment address + + /** + * @dev contract constructor + * @param _lzEndpoint: The LZ endpoint contract deployed on this chain + * @param _remoteChainId: remote chain id to be set as the trusted remote + * @param _remoteContract: remote contract to be set as the trusted remote + * NOTE: we set `zroPaymentAddress` to the zero address as it does not make sense to make this contract ownable only + * to set this variable once their token is out, logistics, etc. + */ + constructor( + address _lzEndpoint, + uint16 _remoteChainId, + bytes memory _remoteContract + ) SimpleLzApp(_lzEndpoint, msg.sender, _remoteChainId, _remoteContract) { + zroPaymentAddress = address(0); + } + + /** + * @dev receiving a request from the remote module to validate the token balance for a given account on a given + * token contract. The result is then sent back to the remote contract. + */ + function _blockingLzReceive( + uint16, // _srcChainId + bytes memory, // _srcAddress + uint64, // _nonce + bytes memory _payload + ) internal override { + ( + address follower, + address tokenContract, + uint256 profileId, + uint256 balanceThreshold + ) = abi.decode(_payload, (address, address, uint256, uint256)); + + _lzSend( + abi.encode(follower, profileId, _checkThreshold(follower, tokenContract, balanceThreshold)), + payable(follower), + zroPaymentAddress, + bytes("") + ); + } + + /** + * @dev Check that `account` meets the threshold of held tokens in `tokenContract`; we use the standard `#balanceOf` + * function signature for ERC721 and ERC20 + */ + function _checkThreshold(address account, address tokenContract, uint256 balanceThreshold) private returns (bool) { + ( + bool success, + bytes memory result + ) = tokenContract.excessivelySafeCall( + gasleft(), + 150, + abi.encodeWithSignature("balanceOf(address)", account) + ); + + if (!success) return false; + + (uint256 balance) = abi.decode(result, (uint256)); + + return balance >= balanceThreshold; + } +} diff --git a/contracts/lz/NonblockingFlexLzApp.sol b/contracts/lz/NonblockingFlexLzApp.sol new file mode 100644 index 0000000..4991957 --- /dev/null +++ b/contracts/lz/NonblockingFlexLzApp.sol @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./LzApp.sol"; + +/** + * @title NonblockingFlexLzApp + * @notice This contract implement's Layerzero's non-blocking pattern, and allows specific chain ids to be configured + * on deployment. After that, _any_ number of trusted remote contracts can be added via the implementing class, as long + * as they pass the necessary checks. + * NOTE: for some level of safety against these remote contracts, we use their `ExcessivelySafeCall` library to limit + * the attack vector. + */ +abstract contract NonblockingFlexLzApp is LzApp { + mapping(uint16 => mapping(bytes => mapping(uint64 => bytes32))) public failedMessages; + + event MessageFailed(uint16 _srcChainId, bytes _srcAddress, uint64 _nonce, bytes _payload, bytes _reason); + event RetryMessageSuccess(uint16 _srcChainId, bytes _srcAddress, uint64 _nonce, bytes32 _payloadHash); + + constructor( + address _lzEndpoint, + address owner, + uint16[] memory remoteChainIds, + bytes[] memory remoteContracts + ) LzApp(_lzEndpoint, owner, remoteChainIds, remoteContracts) {} + + // overriding the virtual function in LzReceiver + function _blockingLzReceive( + uint16 _srcChainId, + bytes memory _srcAddress, + uint64 _nonce, + bytes memory _payload + ) internal virtual override { + (bool success, bytes memory reason) = address(this).call( + abi.encodeWithSelector(this.nonblockingLzReceive.selector, _srcChainId, _srcAddress, _nonce, _payload) + ); + + // try-catch all errors/exceptions + if (!success) { + failedMessages[_srcChainId][_srcAddress][_nonce] = keccak256(_payload); + emit MessageFailed(_srcChainId, _srcAddress, _nonce, _payload, reason); + } + } + + function nonblockingLzReceive( + uint16 _srcChainId, + bytes calldata _srcAddress, + uint64 _nonce, + bytes calldata _payload + ) public virtual { + // only internal transaction + require(msg.sender == address(this), "NonblockingLzApp: caller must be LzApp"); + + _nonblockingLzReceive(_srcChainId, _srcAddress, _nonce, _payload); + } + + function retryMessage( + uint16 _srcChainId, + bytes calldata _srcAddress, + uint64 _nonce, + bytes calldata _payload + ) public payable virtual { + // assert there is message to retry + bytes32 payloadHash = failedMessages[_srcChainId][_srcAddress][_nonce]; + require(payloadHash != bytes32(0), "NonblockingLzApp: no stored message"); + require(keccak256(_payload) == payloadHash, "NonblockingLzApp: invalid payload"); + + // clear the stored message + failedMessages[_srcChainId][_srcAddress][_nonce] = bytes32(0); + + // execute the message. revert if it fails again + _nonblockingLzReceive(_srcChainId, _srcAddress, _nonce, _payload); + emit RetryMessageSuccess(_srcChainId, _srcAddress, _nonce, payloadHash); + } + + // @notice override this function + function _nonblockingLzReceive( + uint16 _srcChainId, + bytes memory _srcAddress, + uint64 _nonce, + bytes memory _payload + ) internal virtual; +} diff --git a/contracts/lz/SimpleLzApp.sol b/contracts/lz/SimpleLzApp.sol new file mode 100644 index 0000000..c17fadb --- /dev/null +++ b/contracts/lz/SimpleLzApp.sol @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.10; + +import "@rari-capital/solmate/src/auth/Owned.sol"; +import "./interfaces/ILayerZeroReceiver.sol"; +import "./interfaces/ILayerZeroUserApplicationConfig.sol"; +import "./interfaces/ILayerZeroEndpoint.sol"; + +/** + * @title SimpleLzApp + * @notice Simple, blocking LayerZero-enabled contract that only has one trusted remote chainId and contract + */ +abstract contract SimpleLzApp is Owned, ILayerZeroReceiver, ILayerZeroUserApplicationConfig { + error NotZeroAddress(); + + ILayerZeroEndpoint public immutable lzEndpoint; + + uint16 public remoteChainId; + bytes public remoteContract; + + /** + * @dev contract constructor + * @param _lzEndpoint: The LZ endpoint contract deployed on this chain + * @param _remoteChainId: remote chain id to be set as the trusted remote + * @param _remoteContract: remote contract to be set as the trusted remote + */ + constructor(address _lzEndpoint, address owner, uint16 _remoteChainId, bytes memory _remoteContract) Owned(owner) { + if (_lzEndpoint == address(0)) { revert NotZeroAddress(); } + + lzEndpoint = ILayerZeroEndpoint(_lzEndpoint); + + remoteChainId = _remoteChainId; + remoteContract = _remoteContract; + } + + function _lzSend( + bytes memory _payload, + address payable _refundAddress, + address _zroPaymentAddress, + bytes memory _adapterParams + ) internal virtual { + lzEndpoint.send{value: msg.value}( + remoteChainId, + remoteContract, + _payload, + _refundAddress, + _zroPaymentAddress, + _adapterParams + ); + } + + function lzReceive( + uint16 _srcChainId, + bytes memory _srcAddress, + uint64 _nonce, + bytes memory _payload + ) public virtual override { + // sanity checks, but we don't want to revert + if (msg.sender != address(lzEndpoint) || _srcChainId != remoteChainId) return; + + _blockingLzReceive(_srcChainId, _srcAddress, _nonce, _payload); + } + + // @dev to be overriden by the concrete class + function _blockingLzReceive( + uint16 _srcChainId, + bytes memory _srcAddress, + uint64 _nonce, + bytes memory _payload + ) internal virtual; + + // @dev generic config for LayerZero user Application + function setConfig( + uint16 _version, + uint16 _chainId, + uint _configType, + bytes calldata _config + ) external override onlyOwner { + lzEndpoint.setConfig(_version, _chainId, _configType, _config); + } + + function setSendVersion(uint16 _version) external override onlyOwner { + lzEndpoint.setSendVersion(_version); + } + + function setReceiveVersion(uint16 _version) external override onlyOwner { + lzEndpoint.setReceiveVersion(_version); + } + + function forceResumeReceive(uint16 _srcChainId, bytes calldata _srcAddress) external override onlyOwner { + lzEndpoint.forceResumeReceive(_srcChainId, _srcAddress); + } + + function getConfig(uint16 _version, uint16 _chainId, address, uint _configType) external view returns (bytes memory) { + return lzEndpoint.getConfig(_version, _chainId, address(this), _configType); + } +} diff --git a/contracts/utils/ExcessivelySafeCall.sol b/contracts/utils/ExcessivelySafeCall.sol new file mode 100644 index 0000000..558308c --- /dev/null +++ b/contracts/utils/ExcessivelySafeCall.sol @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity >=0.7.6; + +// https://github.com/LayerZero-Labs/solidity-examples/blob/main/contracts/util/ExcessivelySafeCall.sol + +library ExcessivelySafeCall { + uint256 constant LOW_28_MASK = + 0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff; + + /// @notice Use when you _really_ really _really_ don't trust the called + /// contract. This prevents the called contract from causing reversion of + /// the caller in as many ways as we can. + /// @dev The main difference between this and a solidity low-level call is + /// that we limit the number of bytes that the callee can cause to be + /// copied to caller memory. This prevents stupid things like malicious + /// contracts returning 10,000,000 bytes causing a local OOG when copying + /// to memory. + /// @param _target The address to call + /// @param _gas The amount of gas to forward to the remote contract + /// @param _maxCopy The maximum number of bytes of returndata to copy + /// to memory. + /// @param _calldata The data to send to the remote contract + /// @return success and returndata, as `.call()`. Returndata is capped to + /// `_maxCopy` bytes. + function excessivelySafeCall( + address _target, + uint256 _gas, + uint16 _maxCopy, + bytes memory _calldata + ) internal returns (bool, bytes memory) { + // set up for assembly call + uint256 _toCopy; + bool _success; + bytes memory _returnData = new bytes(_maxCopy); + // dispatch message to recipient + // by assembly calling "handle" function + // we call via assembly to avoid memcopying a very large returndata + // returned by a malicious contract + assembly { + _success := call( + _gas, // gas + _target, // recipient + 0, // ether value + add(_calldata, 0x20), // inloc + mload(_calldata), // inlen + 0, // outloc + 0 // outlen + ) + // limit our copy to 256 bytes + _toCopy := returndatasize() + if gt(_toCopy, _maxCopy) { + _toCopy := _maxCopy + } + // Store the length of the copied bytes + mstore(_returnData, _toCopy) + // copy the bytes from returndata[0:_toCopy] + returndatacopy(add(_returnData, 0x20), 0, _toCopy) + } + return (_success, _returnData); + } + + /// @notice Use when you _really_ really _really_ don't trust the called + /// contract. This prevents the called contract from causing reversion of + /// the caller in as many ways as we can. + /// @dev The main difference between this and a solidity low-level call is + /// that we limit the number of bytes that the callee can cause to be + /// copied to caller memory. This prevents stupid things like malicious + /// contracts returning 10,000,000 bytes causing a local OOG when copying + /// to memory. + /// @param _target The address to call + /// @param _gas The amount of gas to forward to the remote contract + /// @param _maxCopy The maximum number of bytes of returndata to copy + /// to memory. + /// @param _calldata The data to send to the remote contract + /// @return success and returndata, as `.call()`. Returndata is capped to + /// `_maxCopy` bytes. + function excessivelySafeStaticCall( + address _target, + uint256 _gas, + uint16 _maxCopy, + bytes memory _calldata + ) internal view returns (bool, bytes memory) { + // set up for assembly call + uint256 _toCopy; + bool _success; + bytes memory _returnData = new bytes(_maxCopy); + // dispatch message to recipient + // by assembly calling "handle" function + // we call via assembly to avoid memcopying a very large returndata + // returned by a malicious contract + assembly { + _success := staticcall( + _gas, // gas + _target, // recipient + add(_calldata, 0x20), // inloc + mload(_calldata), // inlen + 0, // outloc + 0 // outlen + ) + // limit our copy to 256 bytes + _toCopy := returndatasize() + if gt(_toCopy, _maxCopy) { + _toCopy := _maxCopy + } + // Store the length of the copied bytes + mstore(_returnData, _toCopy) + // copy the bytes from returndata[0:_toCopy] + returndatacopy(add(_returnData, 0x20), 0, _toCopy) + } + return (_success, _returnData); + } + + /** + * @notice Swaps function selectors in encoded contract calls + * @dev Allows reuse of encoded calldata for functions with identical + * argument types but different names. It simply swaps out the first 4 bytes + * for the new selector. This function modifies memory in place, and should + * only be used with caution. + * @param _newSelector The new 4-byte selector + * @param _buf The encoded contract args + */ + function swapSelector(bytes4 _newSelector, bytes memory _buf) + internal + pure + { + require(_buf.length >= 4); + uint256 _mask = LOW_28_MASK; + assembly { + // load the first word of + let _word := mload(add(_buf, 0x20)) + // mask out the top 4 bytes + // /x + _word := and(_word, _mask) + _word := or(_newSelector, _word) + mstore(add(_buf, 0x20), _word) + } + } +} From 81c95092526fef9c857e32e6b90eb2dc35f498fb Mon Sep 17 00:00:00 2001 From: Carlos Beltran Date: Thu, 10 Nov 2022 11:11:53 -0500 Subject: [PATCH 02/14] update comments for proposal --- contracts/LZGatedFollowModule.sol | 33 ++++++---- proposal.md | 101 ++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 11 deletions(-) create mode 100644 proposal.md diff --git a/contracts/LZGatedFollowModule.sol b/contracts/LZGatedFollowModule.sol index 2645334..08e4d7d 100644 --- a/contracts/LZGatedFollowModule.sol +++ b/contracts/LZGatedFollowModule.sol @@ -6,6 +6,7 @@ import {ModuleBase, Errors} from "@aave/lens-protocol/contracts/core/modules/Mod import {FollowValidatorFollowModuleBase} from "@aave/lens-protocol/contracts/core/modules/follow/FollowValidatorFollowModuleBase.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import {ILensHub} from '@aave/lens-protocol/contracts/interfaces/ILensHub.sol'; import {NonblockingFlexLzApp} from "./lz/NonblockingFlexLzApp.sol"; /** @@ -86,19 +87,23 @@ contract LZGatedFollowModule is FollowValidatorFollowModuleBase, NonblockingFlex /** * @dev Process a follow by: - * - revert if the `follower` is already following the profile - * - if the profile has set gated follow data on this chain, just do the token balance check inline - * - else send the payload for one of our cross-chain proxies to do the check for us and return the result async + * - if the profile has set gated follow data on this chain, just do the token balance check inline and revert if the + * threshold is not met + * - else send the payload to our cross-chain proxy on the remote chain to do the check and asynchronously return the + * result. + * NOTE: we must have an intermediate state to report back to the LensHub contract */ function processFollow( address follower, uint256 profileId, bytes calldata // data - ) external override onlyHub { + ) external override onlyHub returns (bool pendingAsyncRequest) { if (gatedFollowPerProfile[profileId].remoteChainId == sourceChainId) { if (!_checkThreshold(follower, gatedFollowPerProfile[profileId])) { revert Errors.FollowInvalid(); } + + pendingAsyncRequest = false; } else { // make the async balance check, expect result in `#_nonblockingLzReceive` _lzSend( @@ -114,12 +119,7 @@ contract LZGatedFollowModule is FollowValidatorFollowModuleBase, NonblockingFlex bytes("") ); - // @TODO: the only feedback to give back to LensHub is a revert if the follow is invalid - // - we won't know if the follow is valid or not until after the async execution on the remote chain - // - even if we used an oracle for a data read, it's async - // - we either - // 1) return some kind of pending state so LensHub doesn't immediately mint the follow nft - // 2) maintain some permission to later execute the minting of follow nfts if the async check is successful + pendingAsyncRequest = true; } } @@ -133,14 +133,25 @@ contract LZGatedFollowModule is FollowValidatorFollowModuleBase, NonblockingFlex uint256 followNFTTokenId ) external override {} - // @TODO: pending how to handle async requests + /** + * @dev Callback from our `LZGatedProxy` contract deployed on a remote chain + * - contains the result of the token balance check in `shouldFollow` + * - make a call to the LensHub to complete the async follow request (they should require that there is a pending + * follow request, process, and clear it out) + */ function _nonblockingLzReceive( uint16 _srcChainId, bytes memory _srcAddress, uint64 _nonce, bytes memory _payload ) internal override { + ( + address follower, + uint256 profileId, + bool shouldFollow + ) = abi.decode(data, (address, uint256, bool)); + ILensHub(HUB).completePendingFollow(follower, profileId, shouldFollow); } /** diff --git a/proposal.md b/proposal.md new file mode 100644 index 0000000..be1d446 --- /dev/null +++ b/proposal.md @@ -0,0 +1,101 @@ +# Proposal for handling async, cross-chain execution within Lens Modules + +## What are we building +Cross-chain, token-gated follow: a Lens profile can set its follow module such that only wallets above a certain ERC20/ERC721 balance can follow them (e.g., you must own a CryptoPunk on ethereum L1 to follow). + +## How are we building it +We have a follow module `LZGatedFollowModule` that is a LayerZero-enabled smart contract; it can send and receive cross-chain messages. When it processes a follow for a profile, we send a message to the destination chain - where we have a separate `LZGatedProxy` contract that simply makes the balance check against the ERC20/ERC721 contract specified on that chain. + +## The problem +The only feedback our module can give back to LensHub is a revert if the follow is invalid. + - we won't know if the follow is valid or not until after the async execution on the remote chain + - even if we used an oracle for a data read, it's async + +## The proposed solution +1. return some kind of pending state so LensHub doesn't immediately mint the follow nft +2. maintain some permission to later execute the follow if the async check is successful + +Within our `LZGatedFollowModule` - notice that we now return a boolean value in `#processFollow` meant to signal to the LensHub an intermediate state in this follow request +```solidity +/** + * @dev Process a follow by: + * - if the profile has set gated follow data on this chain, just do the token balance check inline and revert if the + * threshold is not met + * - else send the payload to our cross-chain proxy on the remote chain to do the check and asynchronously return the + * result. + * NOTE: we must have an intermediate state to report back to the LensHub contract + */ +function processFollow( + address follower, + uint256 profileId, + bytes calldata // data +) external override onlyHub returns (bool pendingAsyncRequest) { + if (gatedFollowPerProfile[profileId].remoteChainId == sourceChainId) { + if (!_checkThreshold(follower, gatedFollowPerProfile[profileId])) { + revert Errors.FollowInvalid(); + } + + pendingAsyncRequest = false; + } else { + // make the async balance check, expect result in `#_nonblockingLzReceive` + _lzSend( + gatedFollowPerProfile[profileId].remoteChainId, + abi.encode( + follower, + gatedFollowPerProfile[profileId].remoteContract, + profileId, + gatedFollowPerProfile[profileId].balanceThreshold + ), + payable(follower), + zroPaymentAddress, + bytes("") + ); + + pendingAsyncRequest = true; + } +} +``` + +And the proposed change in the lens protocol, within the `InteractionLogic` library function `#follow` +```solidity +... + +if (followModule != address(0)) { + bool pendingAsyncRequest = IFollowModule(followModule).processFollow( + follower, + profileIds[i], + followModuleDatas[i] + ); + + if (!pendingAsyncRequest) { + tokenIds[i] = IFollowNFT(followNFT).mint(follower); + } else { + pendingFollows[followModule][follower][profileId] = true; // or some nonce? + } +} +... +``` + +Finally, to complete the flow - our module receives the result from the remote contract and fulfills the process follow request. **This requires a new function in the lens protocol - only callable by whitelisted modules** +```solidity +/** + * @dev Callback from our `LZGatedProxy` contract deployed on a remote chain + * - contains the result of the token balance check in `shouldFollow` + * - make a call to the LensHub to complete the async follow request (they should require that there is a pending + * follow request, process, and clear it out) + */ +function _nonblockingLzReceive( + uint16 _srcChainId, + bytes memory _srcAddress, + uint64 _nonce, + bytes memory _payload +) internal override { + ( + address follower, + uint256 profileId, + bool shouldFollow + ) = abi.decode(data, (address, uint256, bool)); + + LensHub(HUB).completePendingFollow(follower, profileId, shouldFollow); +} +``` From ca5b5b03b0c6ff3ddc344904ccd4f9837c83c11f Mon Sep 17 00:00:00 2001 From: Carlos Beltran Date: Fri, 9 Dec 2022 09:59:20 -0600 Subject: [PATCH 03/14] lz gated follow module + test --- contracts/LZGatedFollowModule.sol | 92 +++---- contracts/LZGatedProxy.sol | 46 ++-- contracts/lz/NonblockingFlexLzApp.sol | 84 ------ contracts/lz/SimpleLzApp.sol | 23 +- contracts/mocks/ERC20Mock.sol | 14 + contracts/mocks/ERC721Mock.sol | 21 ++ contracts/utils/ExcessivelySafeCall.sol | 138 ---------- test/LZCollectModule.spec.ts | 16 +- test/LZGatedFollowModule.spec.ts | 335 ++++++++++++++++++++++++ test/OmniSBT.spec.ts | 2 - test/lens/helpers/utils.ts | 54 +++- test/utils/parseLogs.ts | 14 +- 12 files changed, 488 insertions(+), 351 deletions(-) delete mode 100644 contracts/lz/NonblockingFlexLzApp.sol create mode 100644 contracts/mocks/ERC20Mock.sol create mode 100644 contracts/mocks/ERC721Mock.sol delete mode 100644 contracts/utils/ExcessivelySafeCall.sol create mode 100644 test/LZGatedFollowModule.spec.ts diff --git a/contracts/LZGatedFollowModule.sol b/contracts/LZGatedFollowModule.sol index 08e4d7d..f9e2a3c 100644 --- a/contracts/LZGatedFollowModule.sol +++ b/contracts/LZGatedFollowModule.sol @@ -3,11 +3,12 @@ pragma solidity 0.8.10; import {ModuleBase, Errors} from "@aave/lens-protocol/contracts/core/modules/ModuleBase.sol"; -import {FollowValidatorFollowModuleBase} from "@aave/lens-protocol/contracts/core/modules/follow/FollowValidatorFollowModuleBase.sol"; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; -import {ILensHub} from '@aave/lens-protocol/contracts/interfaces/ILensHub.sol'; -import {NonblockingFlexLzApp} from "./lz/NonblockingFlexLzApp.sol"; +import { + FollowValidatorFollowModuleBase +} from "@aave/lens-protocol/contracts/core/modules/follow/FollowValidatorFollowModuleBase.sol"; +import {ILensHub} from "@aave/lens-protocol/contracts/interfaces/ILensHub.sol"; +import {DataTypes} from "@aave/lens-protocol/contracts/libraries/DataTypes.sol"; +import {LzApp} from "./lz/LzApp.sol"; /** * @title LZGatedFollowModule @@ -16,9 +17,8 @@ import {NonblockingFlexLzApp} from "./lz/NonblockingFlexLzApp.sol"; * @notice A Lens Follow Module that allows profile holders to gate their following with ERC20 or ERC721 balances held * on other chains. */ -contract LZGatedFollowModule is FollowValidatorFollowModuleBase, NonblockingFlexLzApp { +contract LZGatedFollowModule is FollowValidatorFollowModuleBase, LzApp { address public zroPaymentAddress; // ZRO payment address - uint16 public sourceChainId; // chainId (lz) this module is deployed on struct GatedFollowData { address remoteContract; // the remote contract to read from @@ -27,8 +27,10 @@ contract LZGatedFollowModule is FollowValidatorFollowModuleBase, NonblockingFlex } mapping (uint256 => GatedFollowData) public gatedFollowPerProfile; // profileId => gated follow data + mapping (uint256 => mapping (address => bool)) public validatedFollowers; // profileId => address which has been validated event InitFollowModule(uint256 indexed profileId, address tokenContract, uint256 balanceThreshold, uint16 chainId); + event MessageFailed(uint16 _srcChainId, bytes _srcAddress, uint64 _nonce, bytes _payload, string _reason); /** * @dev contract constructor @@ -43,11 +45,9 @@ contract LZGatedFollowModule is FollowValidatorFollowModuleBase, NonblockingFlex address hub, address _lzEndpoint, uint16[] memory remoteChainIds, - uint16 _sourceChainId, bytes[] memory remoteProxies - ) ModuleBase(hub) NonblockingFlexLzApp(_lzEndpoint, msg.sender, remoteChainIds, remoteProxies) { + ) ModuleBase(hub) LzApp(_lzEndpoint, msg.sender, remoteChainIds, remoteProxies) { zroPaymentAddress = address(0); - sourceChainId = _sourceChainId; } /** @@ -70,7 +70,7 @@ contract LZGatedFollowModule is FollowValidatorFollowModuleBase, NonblockingFlex uint16 chainId ) = abi.decode(data, (address, uint256, uint16)); - if (address(tokenContract) == address(0) || (chainId != sourceChainId && _lzRemoteLookup[chainId].length == 0)) { + if (address(tokenContract) == address(0) || _lzRemoteLookup[chainId].length == 0) { revert Errors.InitParamsInvalid(); } @@ -87,39 +87,15 @@ contract LZGatedFollowModule is FollowValidatorFollowModuleBase, NonblockingFlex /** * @dev Process a follow by: - * - if the profile has set gated follow data on this chain, just do the token balance check inline and revert if the - * threshold is not met - * - else send the payload to our cross-chain proxy on the remote chain to do the check and asynchronously return the - * result. - * NOTE: we must have an intermediate state to report back to the LensHub contract + * - checking that we have already validated the follower through our `LZGatedProxy` on a remote chain */ function processFollow( address follower, uint256 profileId, bytes calldata // data - ) external override onlyHub returns (bool pendingAsyncRequest) { - if (gatedFollowPerProfile[profileId].remoteChainId == sourceChainId) { - if (!_checkThreshold(follower, gatedFollowPerProfile[profileId])) { - revert Errors.FollowInvalid(); - } - - pendingAsyncRequest = false; - } else { - // make the async balance check, expect result in `#_nonblockingLzReceive` - _lzSend( - gatedFollowPerProfile[profileId].remoteChainId, - abi.encode( - follower, - gatedFollowPerProfile[profileId].remoteContract, - profileId, - gatedFollowPerProfile[profileId].balanceThreshold - ), - payable(follower), - zroPaymentAddress, - bytes("") - ); - - pendingAsyncRequest = true; + ) external view override onlyHub { + if (!validatedFollowers[profileId][follower]) { + revert Errors.FollowInvalid(); } } @@ -134,12 +110,10 @@ contract LZGatedFollowModule is FollowValidatorFollowModuleBase, NonblockingFlex ) external override {} /** - * @dev Callback from our `LZGatedProxy` contract deployed on a remote chain - * - contains the result of the token balance check in `shouldFollow` - * - make a call to the LensHub to complete the async follow request (they should require that there is a pending - * follow request, process, and clear it out) + * @dev Callback from our `LZGatedProxy` contract deployed on a remote chain, signals that the follow is validated + * NOTE: this function is actually non-blocking in that it catches errors thrown from LensHub */ - function _nonblockingLzReceive( + function _blockingLzReceive( uint16 _srcChainId, bytes memory _srcAddress, uint64 _nonce, @@ -147,27 +121,25 @@ contract LZGatedFollowModule is FollowValidatorFollowModuleBase, NonblockingFlex ) internal override { ( address follower, + address token, uint256 profileId, - bool shouldFollow - ) = abi.decode(data, (address, uint256, bool)); + uint256 threshold, + DataTypes.FollowWithSigData memory followSig + ) = abi.decode(_payload, (address, address, uint256, uint256, DataTypes.FollowWithSigData)); - ILensHub(HUB).completePendingFollow(follower, profileId, shouldFollow); - } + GatedFollowData memory data = gatedFollowPerProfile[profileId]; - /** - * @dev Check that `account` meets the threshold of held tokens in `tokenContract`; we use the standard `#balanceOf` - * function signature for ERC721 and ERC20 - */ - function _checkThreshold(address account, GatedFollowData memory data) private returns (bool) { - ( - bool success, - bytes memory result - ) = address(data.remoteContract).call(abi.encodeWithSignature("balanceOf(address)", account)); + if (data.remoteChainId != _srcChainId || data.balanceThreshold != threshold || data.remoteContract != token) { + emit MessageFailed(_srcChainId, _srcAddress, _nonce, _payload, 'FollowInvalid'); + } - if (!success) return false; + validatedFollowers[profileId][follower] = true; - (uint256 balance) = abi.decode(result, (uint256)); + try ILensHub(HUB).followWithSig(followSig) {} + catch Error (string memory reason) { + emit MessageFailed(_srcChainId, _srcAddress, _nonce, _payload, reason); + } - return balance >= data.balanceThreshold; + delete validatedFollowers[profileId][follower]; } } diff --git a/contracts/LZGatedProxy.sol b/contracts/LZGatedProxy.sol index c4e913e..b0584bd 100644 --- a/contracts/LZGatedProxy.sol +++ b/contracts/LZGatedProxy.sol @@ -2,11 +2,8 @@ pragma solidity 0.8.10; -import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import {DataTypes} from "@aave/lens-protocol/contracts/libraries/DataTypes.sol"; import "./lz/SimpleLzApp.sol"; -import "./utils/ExcessivelySafeCall.sol"; /** * @title LZGatedProxy @@ -14,7 +11,7 @@ import "./utils/ExcessivelySafeCall.sol"; * token balances from remote contracts on any chain supported by layerzero. */ contract LZGatedProxy is SimpleLzApp { - using ExcessivelySafeCall for address; + error InsufficientBalance(); address public zroPaymentAddress; // ZRO payment address @@ -34,26 +31,23 @@ contract LZGatedProxy is SimpleLzApp { zroPaymentAddress = address(0); } - /** - * @dev receiving a request from the remote module to validate the token balance for a given account on a given - * token contract. The result is then sent back to the remote contract. - */ - function _blockingLzReceive( - uint16, // _srcChainId - bytes memory, // _srcAddress - uint64, // _nonce - bytes memory _payload - ) internal override { - ( - address follower, - address tokenContract, - uint256 profileId, - uint256 balanceThreshold - ) = abi.decode(_payload, (address, address, uint256, uint256)); + function validateAndRelayWithSig( + uint256 profileId, + address tokenContract, + uint256 balanceThreshold, + DataTypes.FollowWithSigData memory followSig + ) external { + if (!_checkThreshold(msg.sender, tokenContract, balanceThreshold)) { revert InsufficientBalance(); } _lzSend( - abi.encode(follower, profileId, _checkThreshold(follower, tokenContract, balanceThreshold)), - payable(follower), + abi.encode( + msg.sender, + tokenContract, + profileId, + balanceThreshold, + followSig + ), + payable(msg.sender), zroPaymentAddress, bytes("") ); @@ -67,11 +61,7 @@ contract LZGatedProxy is SimpleLzApp { ( bool success, bytes memory result - ) = tokenContract.excessivelySafeCall( - gasleft(), - 150, - abi.encodeWithSignature("balanceOf(address)", account) - ); + ) = tokenContract.call(abi.encodeWithSignature("balanceOf(address)", account)); if (!success) return false; diff --git a/contracts/lz/NonblockingFlexLzApp.sol b/contracts/lz/NonblockingFlexLzApp.sol deleted file mode 100644 index 4991957..0000000 --- a/contracts/lz/NonblockingFlexLzApp.sol +++ /dev/null @@ -1,84 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "./LzApp.sol"; - -/** - * @title NonblockingFlexLzApp - * @notice This contract implement's Layerzero's non-blocking pattern, and allows specific chain ids to be configured - * on deployment. After that, _any_ number of trusted remote contracts can be added via the implementing class, as long - * as they pass the necessary checks. - * NOTE: for some level of safety against these remote contracts, we use their `ExcessivelySafeCall` library to limit - * the attack vector. - */ -abstract contract NonblockingFlexLzApp is LzApp { - mapping(uint16 => mapping(bytes => mapping(uint64 => bytes32))) public failedMessages; - - event MessageFailed(uint16 _srcChainId, bytes _srcAddress, uint64 _nonce, bytes _payload, bytes _reason); - event RetryMessageSuccess(uint16 _srcChainId, bytes _srcAddress, uint64 _nonce, bytes32 _payloadHash); - - constructor( - address _lzEndpoint, - address owner, - uint16[] memory remoteChainIds, - bytes[] memory remoteContracts - ) LzApp(_lzEndpoint, owner, remoteChainIds, remoteContracts) {} - - // overriding the virtual function in LzReceiver - function _blockingLzReceive( - uint16 _srcChainId, - bytes memory _srcAddress, - uint64 _nonce, - bytes memory _payload - ) internal virtual override { - (bool success, bytes memory reason) = address(this).call( - abi.encodeWithSelector(this.nonblockingLzReceive.selector, _srcChainId, _srcAddress, _nonce, _payload) - ); - - // try-catch all errors/exceptions - if (!success) { - failedMessages[_srcChainId][_srcAddress][_nonce] = keccak256(_payload); - emit MessageFailed(_srcChainId, _srcAddress, _nonce, _payload, reason); - } - } - - function nonblockingLzReceive( - uint16 _srcChainId, - bytes calldata _srcAddress, - uint64 _nonce, - bytes calldata _payload - ) public virtual { - // only internal transaction - require(msg.sender == address(this), "NonblockingLzApp: caller must be LzApp"); - - _nonblockingLzReceive(_srcChainId, _srcAddress, _nonce, _payload); - } - - function retryMessage( - uint16 _srcChainId, - bytes calldata _srcAddress, - uint64 _nonce, - bytes calldata _payload - ) public payable virtual { - // assert there is message to retry - bytes32 payloadHash = failedMessages[_srcChainId][_srcAddress][_nonce]; - require(payloadHash != bytes32(0), "NonblockingLzApp: no stored message"); - require(keccak256(_payload) == payloadHash, "NonblockingLzApp: invalid payload"); - - // clear the stored message - failedMessages[_srcChainId][_srcAddress][_nonce] = bytes32(0); - - // execute the message. revert if it fails again - _nonblockingLzReceive(_srcChainId, _srcAddress, _nonce, _payload); - emit RetryMessageSuccess(_srcChainId, _srcAddress, _nonce, payloadHash); - } - - // @notice override this function - function _nonblockingLzReceive( - uint16 _srcChainId, - bytes memory _srcAddress, - uint64 _nonce, - bytes memory _payload - ) internal virtual; -} diff --git a/contracts/lz/SimpleLzApp.sol b/contracts/lz/SimpleLzApp.sol index c17fadb..de3f25a 100644 --- a/contracts/lz/SimpleLzApp.sol +++ b/contracts/lz/SimpleLzApp.sol @@ -3,7 +3,6 @@ pragma solidity 0.8.10; import "@rari-capital/solmate/src/auth/Owned.sol"; -import "./interfaces/ILayerZeroReceiver.sol"; import "./interfaces/ILayerZeroUserApplicationConfig.sol"; import "./interfaces/ILayerZeroEndpoint.sol"; @@ -11,7 +10,7 @@ import "./interfaces/ILayerZeroEndpoint.sol"; * @title SimpleLzApp * @notice Simple, blocking LayerZero-enabled contract that only has one trusted remote chainId and contract */ -abstract contract SimpleLzApp is Owned, ILayerZeroReceiver, ILayerZeroUserApplicationConfig { +abstract contract SimpleLzApp is Owned, ILayerZeroUserApplicationConfig { error NotZeroAddress(); ILayerZeroEndpoint public immutable lzEndpoint; @@ -50,26 +49,6 @@ abstract contract SimpleLzApp is Owned, ILayerZeroReceiver, ILayerZeroUserApplic ); } - function lzReceive( - uint16 _srcChainId, - bytes memory _srcAddress, - uint64 _nonce, - bytes memory _payload - ) public virtual override { - // sanity checks, but we don't want to revert - if (msg.sender != address(lzEndpoint) || _srcChainId != remoteChainId) return; - - _blockingLzReceive(_srcChainId, _srcAddress, _nonce, _payload); - } - - // @dev to be overriden by the concrete class - function _blockingLzReceive( - uint16 _srcChainId, - bytes memory _srcAddress, - uint64 _nonce, - bytes memory _payload - ) internal virtual; - // @dev generic config for LayerZero user Application function setConfig( uint16 _version, diff --git a/contracts/mocks/ERC20Mock.sol b/contracts/mocks/ERC20Mock.sol new file mode 100644 index 0000000..751b699 --- /dev/null +++ b/contracts/mocks/ERC20Mock.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.9; + +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; + +contract ERC20Mock is ERC20, Ownable { + constructor() ERC20("ERC20Mock", "MOCK") {} + + function mint(address to, uint256 amount) public onlyOwner { + _mint(to, amount); + } +} diff --git a/contracts/mocks/ERC721Mock.sol b/contracts/mocks/ERC721Mock.sol new file mode 100644 index 0000000..f268dd4 --- /dev/null +++ b/contracts/mocks/ERC721Mock.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.8.0; + +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin/contracts/utils/Counters.sol"; + +contract ERC721Mock is ERC721, Ownable { + using Counters for Counters.Counter; + + Counters.Counter private _tokenIdCounter; + + constructor() ERC721("ERC721Mock", "MOCK") {} + + function safeMint(address to) public onlyOwner { + uint256 tokenId = _tokenIdCounter.current(); + _tokenIdCounter.increment(); + _safeMint(to, tokenId); + } +} diff --git a/contracts/utils/ExcessivelySafeCall.sol b/contracts/utils/ExcessivelySafeCall.sol deleted file mode 100644 index 558308c..0000000 --- a/contracts/utils/ExcessivelySafeCall.sol +++ /dev/null @@ -1,138 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity >=0.7.6; - -// https://github.com/LayerZero-Labs/solidity-examples/blob/main/contracts/util/ExcessivelySafeCall.sol - -library ExcessivelySafeCall { - uint256 constant LOW_28_MASK = - 0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff; - - /// @notice Use when you _really_ really _really_ don't trust the called - /// contract. This prevents the called contract from causing reversion of - /// the caller in as many ways as we can. - /// @dev The main difference between this and a solidity low-level call is - /// that we limit the number of bytes that the callee can cause to be - /// copied to caller memory. This prevents stupid things like malicious - /// contracts returning 10,000,000 bytes causing a local OOG when copying - /// to memory. - /// @param _target The address to call - /// @param _gas The amount of gas to forward to the remote contract - /// @param _maxCopy The maximum number of bytes of returndata to copy - /// to memory. - /// @param _calldata The data to send to the remote contract - /// @return success and returndata, as `.call()`. Returndata is capped to - /// `_maxCopy` bytes. - function excessivelySafeCall( - address _target, - uint256 _gas, - uint16 _maxCopy, - bytes memory _calldata - ) internal returns (bool, bytes memory) { - // set up for assembly call - uint256 _toCopy; - bool _success; - bytes memory _returnData = new bytes(_maxCopy); - // dispatch message to recipient - // by assembly calling "handle" function - // we call via assembly to avoid memcopying a very large returndata - // returned by a malicious contract - assembly { - _success := call( - _gas, // gas - _target, // recipient - 0, // ether value - add(_calldata, 0x20), // inloc - mload(_calldata), // inlen - 0, // outloc - 0 // outlen - ) - // limit our copy to 256 bytes - _toCopy := returndatasize() - if gt(_toCopy, _maxCopy) { - _toCopy := _maxCopy - } - // Store the length of the copied bytes - mstore(_returnData, _toCopy) - // copy the bytes from returndata[0:_toCopy] - returndatacopy(add(_returnData, 0x20), 0, _toCopy) - } - return (_success, _returnData); - } - - /// @notice Use when you _really_ really _really_ don't trust the called - /// contract. This prevents the called contract from causing reversion of - /// the caller in as many ways as we can. - /// @dev The main difference between this and a solidity low-level call is - /// that we limit the number of bytes that the callee can cause to be - /// copied to caller memory. This prevents stupid things like malicious - /// contracts returning 10,000,000 bytes causing a local OOG when copying - /// to memory. - /// @param _target The address to call - /// @param _gas The amount of gas to forward to the remote contract - /// @param _maxCopy The maximum number of bytes of returndata to copy - /// to memory. - /// @param _calldata The data to send to the remote contract - /// @return success and returndata, as `.call()`. Returndata is capped to - /// `_maxCopy` bytes. - function excessivelySafeStaticCall( - address _target, - uint256 _gas, - uint16 _maxCopy, - bytes memory _calldata - ) internal view returns (bool, bytes memory) { - // set up for assembly call - uint256 _toCopy; - bool _success; - bytes memory _returnData = new bytes(_maxCopy); - // dispatch message to recipient - // by assembly calling "handle" function - // we call via assembly to avoid memcopying a very large returndata - // returned by a malicious contract - assembly { - _success := staticcall( - _gas, // gas - _target, // recipient - add(_calldata, 0x20), // inloc - mload(_calldata), // inlen - 0, // outloc - 0 // outlen - ) - // limit our copy to 256 bytes - _toCopy := returndatasize() - if gt(_toCopy, _maxCopy) { - _toCopy := _maxCopy - } - // Store the length of the copied bytes - mstore(_returnData, _toCopy) - // copy the bytes from returndata[0:_toCopy] - returndatacopy(add(_returnData, 0x20), 0, _toCopy) - } - return (_success, _returnData); - } - - /** - * @notice Swaps function selectors in encoded contract calls - * @dev Allows reuse of encoded calldata for functions with identical - * argument types but different names. It simply swaps out the first 4 bytes - * for the new selector. This function modifies memory in place, and should - * only be used with caution. - * @param _newSelector The new 4-byte selector - * @param _buf The encoded contract args - */ - function swapSelector(bytes4 _newSelector, bytes memory _buf) - internal - pure - { - require(_buf.length >= 4); - uint256 _mask = LOW_28_MASK; - assembly { - // load the first word of - let _word := mload(add(_buf, 0x20)) - // mask out the top 4 bytes - // /x - _word := and(_word, _mask) - _word := or(_newSelector, _word) - mstore(add(_buf, 0x20), _word) - } - } -} diff --git a/test/LZCollectModule.spec.ts b/test/LZCollectModule.spec.ts index 7ee3976..928e7ca 100644 --- a/test/LZCollectModule.spec.ts +++ b/test/LZCollectModule.spec.ts @@ -27,25 +27,13 @@ import { } from '../typechain-types' import { abi } from './lens/abi/Events.json'; import LZCollectModuleABI from './../build/contracts/LZCollectModule.sol/LZCollectModule.json'; +import { parseLogsNested } from './utils/parseLogs'; const EMPTY_BYTES = '0x'; const FIRST_PUB_ID = 1; const FIRST_COLLECTION_ID = 1; const CHAIN_ID = 123; -// HACKY: attempt to parse logs from Lens=>Events lib and LZCollectModule -const parseLogs = ({ logs }) => { - const iface = new ethers.utils.Interface(abi); - const followIFace = new ethers.utils.Interface(LZCollectModuleABI.abi); - return (logs.map((log) => { - try { return iface.parseLog(log); } - catch { - try { return followIFace.parseLog(log); } - catch {} - } - })).filter((l) => l); -}; - makeSuiteCleanRoom('LZCollectModule', function () { let omniNFTSource: OmniSBT, omniNFTDestination: OmniSBT; let lzEndpoint: LZEndpointMock @@ -153,7 +141,7 @@ makeSuiteCleanRoom('LZCollectModule', function () { }); it('emits an event', async () => { - const logs = parseLogs(await tx.wait()); + const logs = parseLogsNested(await tx.wait(), abi, LZCollectModuleABI.abi); const event = logs.find(({ name }) => name === 'InitCollectModule'); expect(event).not.to.equal(undefined); expect(event.args.collectionId.toNumber()).to.equal(FIRST_COLLECTION_ID); diff --git a/test/LZGatedFollowModule.spec.ts b/test/LZGatedFollowModule.spec.ts new file mode 100644 index 0000000..442b347 --- /dev/null +++ b/test/LZGatedFollowModule.spec.ts @@ -0,0 +1,335 @@ +import '@nomiclabs/hardhat-ethers'; +import { expect } from 'chai'; +import { Signer, VoidSigner } from 'ethers'; +const { getContractAddress } = require('@ethersproject/address'); +import { ethers } from 'hardhat'; +import { + FIRST_PROFILE_ID, + MOCK_URI, + governance, + lensHub, + makeSuiteCleanRoom, + MOCK_FOLLOW_NFT_URI, + MOCK_PROFILE_HANDLE, + MOCK_PROFILE_URI, + user, + userTwo, + userThree, + testWallet, +} from './lens/__setup.spec'; +import { ERRORS } from './lens/helpers/errors'; +import { getFollowWithSigParts } from './lens/helpers/utils'; +import { ZERO_ADDRESS, MAX_UINT256 } from './lens/helpers/constants'; +import { + LZGatedFollowModule, + LZGatedFollowModule__factory, + LZGatedProxy, + LZGatedProxy__factory, + LZEndpointMock, + LZEndpointMock__factory, + ERC721Mock, + ERC721Mock__factory, + ERC20Mock, + ERC20Mock__factory, +} from '../typechain-types' +import { abi } from './lens/abi/Events.json'; +import { FollowNFT__factory } from '../typechain-types-lens'; +import LZGatedFollowModuleABI from './../build/contracts/LZGatedFollowModule.sol/LZGatedFollowModule.json'; +import { parseLogsNested } from './utils/parseLogs'; + +const EMPTY_BYTES = '0x'; +const REMOTE_CHAIN_ID = 123; +const BALANCE_THRESHOLD = 1; + +makeSuiteCleanRoom('LZGatedFollowModule', function () { + let lzGatedProxy: LZGatedProxy; + let lzEndpoint: LZEndpointMock; + let followModule: LZGatedFollowModule; + let erc721: ERC721Mock; + let erc20: ERC20Mock; + let deployer: Signer, user: Signer, userTwo: Signer, userThree: Signer; + let deployerAddress: string, userAddress: string, userTwoAddress: string; + + const setFollowModule = async ({ + tokenContract = erc721.address, + tokenThreshold = BALANCE_THRESHOLD, + chainId = REMOTE_CHAIN_ID + }) => { + const followModuleInitData = ethers.utils.defaultAbiCoder.encode( + ['address', 'uint256', 'uint16'], + [tokenContract, tokenThreshold, chainId] + ); + + return await lensHub.setFollowModule(FIRST_PROFILE_ID, followModule.address, followModuleInitData); + }; + + const getNonBlockingError = (receipt) => { + const logs = parseLogsNested(receipt, abi, LZGatedFollowModuleABI.abi); + const event = logs.find(({ name }) => name === 'MessageFailed'); + + return event.args._reason; + }; + + beforeEach(async () => { + ([deployer, user, userTwo, userThree] = await ethers.getSigners()); + deployerAddress = await deployer.getAddress(); + userAddress = await user.getAddress(); + userTwoAddress = await userTwo.getAddress(); + + lzEndpoint = await new LZEndpointMock__factory(deployer).deploy(REMOTE_CHAIN_ID); + const transactionCount = await deployer.getTransactionCount(); + const followModuleAddress = getContractAddress({ from: deployerAddress, nonce: transactionCount + 1 }); + + lzGatedProxy = await new LZGatedProxy__factory(deployer).deploy( + lzEndpoint.address, + REMOTE_CHAIN_ID, + followModuleAddress, + ); + followModule = await new LZGatedFollowModule__factory(deployer).deploy( + lensHub.address, + lzEndpoint.address, + [REMOTE_CHAIN_ID], + [lzGatedProxy.address] + ); + erc721 = await new ERC721Mock__factory(deployer).deploy(); + erc20 = await new ERC20Mock__factory(deployer).deploy(); + + // use same lz endpoint mock + await lzEndpoint.setDestLzEndpoint(followModule.address, lzEndpoint.address); + await lzEndpoint.setDestLzEndpoint(lzGatedProxy.address, lzEndpoint.address); + + await lensHub.connect(governance).whitelistFollowModule(followModule.address, true); + + await lensHub.createProfile({ + to: userAddress, + handle: MOCK_PROFILE_HANDLE, + imageURI: MOCK_PROFILE_URI, + followModule: ZERO_ADDRESS, + followModuleInitData: [], + followNFTURI: MOCK_FOLLOW_NFT_URI, + }); + }); + + describe('#constructor', () => { + it('reverts when the hub arg is the null address', async () => { + expect( + new LZGatedFollowModule__factory(deployer).deploy(ZERO_ADDRESS, lzEndpoint.address, [], []) + ).to.be.revertedWith('InitParamsInvalid'); + }); + + it('sets storage', async () => { + const owner = await followModule.owner(); + const endpoint = await followModule.lzEndpoint(); + + expect(owner).to.equal(deployerAddress); + expect(endpoint).to.equal(lzEndpoint.address); + }); + }); + + describe('#initializeFollowModule', () => { + it('reverts when the caller is not LensHub', async () => { + await expect( + followModule.initializeFollowModule(FIRST_PROFILE_ID, EMPTY_BYTES) + ).to.be.revertedWith(ERRORS.NOT_HUB); + }); + + it('reverts when an invalid chain id is provided in the encoded data', async () => { + const followModuleInitData = ethers.utils.defaultAbiCoder.encode( + ['address', 'uint256', 'uint16'], + [erc721.address, BALANCE_THRESHOLD, 12345] + ); + + await expect( + lensHub.setFollowModule(FIRST_PROFILE_ID, followModule.address, followModuleInitData) + ).to.be.revertedWith(ERRORS.INIT_PARAMS_INVALID); + }); + + it('reverts when token contract as zero address is provided in the encoded data', async () => { + const followModuleInitData = ethers.utils.defaultAbiCoder.encode( + ['address', 'uint256', 'uint16'], + [ZERO_ADDRESS, BALANCE_THRESHOLD, REMOTE_CHAIN_ID] + ); + + await expect( + lensHub.setFollowModule(FIRST_PROFILE_ID, followModule.address, followModuleInitData) + ).to.be.revertedWith(ERRORS.INIT_PARAMS_INVALID); + }); + + context('context: with valid params', () => { + let tx; + + beforeEach(async() => { + tx = await setFollowModule({ + tokenContract: erc721.address, + tokenThreshold: BALANCE_THRESHOLD, + chainId: REMOTE_CHAIN_ID + }); + }); + + it('sets storage', async () => { + const res = await followModule.gatedFollowPerProfile(FIRST_PROFILE_ID); + + expect(res.balanceThreshold.toNumber()).to.equal(BALANCE_THRESHOLD); + expect(res.remoteContract).to.equal(erc721.address); + expect(res.remoteChainId).to.equal(REMOTE_CHAIN_ID); + }); + + it('emits an event', async () => { + const logs = parseLogsNested(await tx.wait(), abi, LZGatedFollowModuleABI.abi); + const event = logs.find(({ name }) => name === 'InitFollowModule'); + + expect(event).not.to.equal(undefined); + expect(event.args.profileId.toNumber()).to.equal(FIRST_PROFILE_ID); + expect(event.args.tokenContract).to.equal(erc721.address); + expect(event.args.chainId).to.equal(REMOTE_CHAIN_ID); + }); + }); + }); + + describe('#processFollow (triggered from LZGatedProxy#validateAndRelayWithSig)', () => { + let followWithSigData; + + beforeEach(async() => { + await setFollowModule({ + tokenContract: erc721.address, + tokenThreshold: BALANCE_THRESHOLD, + chainId: REMOTE_CHAIN_ID + }); + + const nonce = (await lensHub.sigNonces(userAddress)).toNumber(); + const { v, r, s } = await getFollowWithSigParts( + user as VoidSigner, + [FIRST_PROFILE_ID], + [[]], + nonce, + MAX_UINT256 + ); + + followWithSigData = { + follower: userAddress, + profileIds: [FIRST_PROFILE_ID], + datas: [[]], + sig: { + v, + r, + s, + deadline: MAX_UINT256, + }, + }; + }); + + it('reverts if called without going through lzGatedProxy', async () => { + await expect( + lensHub.followWithSig(followWithSigData) + ).to.be.revertedWith(ERRORS.FOLLOW_INVALID); + }); + + it('reverts if the caller does not have suffient balance', async () => { + await expect( + lzGatedProxy + .connect(user) + .validateAndRelayWithSig( + FIRST_PROFILE_ID, + erc721.address, + BALANCE_THRESHOLD, + followWithSigData + ) + ).to.be.revertedWith('InsufficientBalance'); + }); + + it('reverts if the contract call for balanceOf() fails', async () => { + await expect( + lzGatedProxy + .connect(user) + .validateAndRelayWithSig( + FIRST_PROFILE_ID, + lzEndpoint.address, + BALANCE_THRESHOLD, + followWithSigData + ) + ).to.be.revertedWith('InsufficientBalance'); + }); + + it('[non-blocking] fails if the caller passed an invalid threshold', async () => { + await erc721.safeMint(userAddress); + + const tx = await lzGatedProxy + .connect(user) + .validateAndRelayWithSig( + FIRST_PROFILE_ID, + erc721.address, + 0, + followWithSigData + ); + + const messageFailedReason = getNonBlockingError(await tx.wait()); + expect(messageFailedReason).to.equal('FollowInvalid'); + }); + + + it('[non-blocking] fails if the caller passed an invalid token contract', async () => { + await erc20.mint(userAddress, BALANCE_THRESHOLD); + + const tx = await lzGatedProxy + .connect(user) + .validateAndRelayWithSig( + FIRST_PROFILE_ID, + erc20.address, + BALANCE_THRESHOLD, + followWithSigData + ); + + const messageFailedReason = getNonBlockingError(await tx.wait()); + expect(messageFailedReason).to.equal('FollowInvalid'); + }); + + it('processes a valid follow', async () => { + await erc721.safeMint(userAddress); + + const tx = await lzGatedProxy + .connect(user) + .validateAndRelayWithSig( + FIRST_PROFILE_ID, + erc721.address, + BALANCE_THRESHOLD, + followWithSigData + ); + const logs = parseLogsNested(await tx.wait(), abi, LZGatedFollowModuleABI.abi); + const event = logs.find(({ name }) => name === 'MessageFailed'); + + expect(event).to.equal(undefined); + + const followNFTAddress = await lensHub.getFollowNFT(FIRST_PROFILE_ID); + const followNFT = FollowNFT__factory.connect(followNFTAddress, user); + const id = await followNFT.tokenOfOwnerByIndex(userAddress, 0); + expect(id).to.eq(1); + }); + + /** + * NOTE: it reverts here in the test because we mocked lzEndpoint; it should actually just fail (emit error) + */ + it('reverts if the same signature is used', async () => { + await erc721.safeMint(userAddress); + + await lzGatedProxy + .connect(user) + .validateAndRelayWithSig( + FIRST_PROFILE_ID, + erc721.address, + BALANCE_THRESHOLD, + followWithSigData + ); + + await expect( + lzGatedProxy + .connect(user) + .validateAndRelayWithSig( + FIRST_PROFILE_ID, + erc721.address, + BALANCE_THRESHOLD, + followWithSigData + ) + ).to.be.reverted; // pretty sure the revert is sig-related but no reason is provided + }); + }); +}); diff --git a/test/OmniSBT.spec.ts b/test/OmniSBT.spec.ts index a3894ee..3b3a53d 100644 --- a/test/OmniSBT.spec.ts +++ b/test/OmniSBT.spec.ts @@ -8,8 +8,6 @@ import { LZEndpointMock, LZEndpointMock__factory, } from '../typechain-types' -import { abi } from './../build/contracts/OmniSBT.sol/OmniSBT.json'; -import parseLogs from './utils/parseLogs'; import { ZERO_ADDRESS } from './lens/helpers/constants'; const CHAIN_ID = 123; diff --git a/test/lens/helpers/utils.ts b/test/lens/helpers/utils.ts index b54a6fd..a71a79d 100644 --- a/test/lens/helpers/utils.ts +++ b/test/lens/helpers/utils.ts @@ -1,5 +1,5 @@ import '@nomiclabs/hardhat-ethers'; -import { BigNumberish, Bytes, logger, utils, BigNumber, Contract, Signer } from 'ethers'; +import { BigNumberish, Bytes, logger, utils, BigNumber, Contract, Signer, VoidSigner } from 'ethers'; import { eventsLib, helper, @@ -101,3 +101,55 @@ export async function takeSnapshot() { export async function revertToSnapshot() { await hre.ethers.provider.send('evm_revert', [snapshotId]); } + +export async function getFollowWithSigParts( + wallet: VoidSigner, + profileIds: string[] | number[], + datas: Bytes[] | string[], + nonce: number, + deadline: string +): Promise<{ v: number; r: string; s: string }> { + const msgParams = buildFollowWithSigParams(profileIds, datas, nonce, deadline); + return await getSig(wallet, msgParams); +} + +async function getSig(wallet: VoidSigner, msgParams: { + domain: any; + types: any; + value: any; +}): Promise<{ v: number; r: string; s: string }> { + const sig = await wallet._signTypedData(msgParams.domain, msgParams.types, msgParams.value); + return utils.splitSignature(sig); +} + +const buildFollowWithSigParams = ( + profileIds: string[] | number[], + datas: Bytes[] | string[], + nonce: number, + deadline: string +) => ({ + types: { + FollowWithSig: [ + { name: 'profileIds', type: 'uint256[]' }, + { name: 'datas', type: 'bytes[]' }, + { name: 'nonce', type: 'uint256' }, + { name: 'deadline', type: 'uint256' }, + ], + }, + domain: domain(), + value: { + profileIds: profileIds, + datas: datas, + nonce: nonce, + deadline: deadline, + }, +}); + +function domain(): { name: string; version: string; chainId: number; verifyingContract: string } { + return { + name: LENS_HUB_NFT_NAME, + version: '1', + chainId: getChainId(), + verifyingContract: lensHub.address, + }; +} diff --git a/test/utils/parseLogs.ts b/test/utils/parseLogs.ts index 2285048..a4584a3 100644 --- a/test/utils/parseLogs.ts +++ b/test/utils/parseLogs.ts @@ -1,6 +1,6 @@ import { ethers } from 'hardhat'; -const parseLogs = ({ logs }, abi) => { +export const parseLogs = ({ logs }, abi) => { const iface = new ethers.utils.Interface(abi); return (logs.map((log) => { try { return iface.parseLog(log); } @@ -8,4 +8,14 @@ const parseLogs = ({ logs }, abi) => { })).filter((l) => l); }; -export default parseLogs; +export const parseLogsNested = ({ logs }, abi, nestedAbi) => { + const iface = new ethers.utils.Interface(abi); + const ifaceNested = new ethers.utils.Interface(nestedAbi); + return (logs.map((log) => { + try { return iface.parseLog(log); } + catch { + try { return ifaceNested.parseLog(log); } + catch {} + } + })).filter((l) => l); +}; From e7f850b6438d9fb52c07899ba54167165ce0c878 Mon Sep 17 00:00:00 2001 From: Carlos Beltran Date: Fri, 9 Dec 2022 16:29:23 -0600 Subject: [PATCH 04/14] gated reference module + tests --- contracts/LZGatedFollowModule.sol | 3 +- contracts/LZGatedProxy.sol | 108 +++++- contracts/LZGatedReferenceModule.sol | 194 ++++++++++ contracts/lz/SimpleLzApp.sol | 11 +- test/LZGatedFollowModule.spec.ts | 29 +- test/LZGatedReferenceModule.spec.ts | 521 +++++++++++++++++++++++++++ test/lens/helpers/utils.ts | 147 +++++++- 7 files changed, 983 insertions(+), 30 deletions(-) create mode 100644 contracts/LZGatedReferenceModule.sol create mode 100644 test/LZGatedReferenceModule.spec.ts diff --git a/contracts/LZGatedFollowModule.sol b/contracts/LZGatedFollowModule.sol index f9e2a3c..5b6131f 100644 --- a/contracts/LZGatedFollowModule.sol +++ b/contracts/LZGatedFollowModule.sol @@ -130,7 +130,8 @@ contract LZGatedFollowModule is FollowValidatorFollowModuleBase, LzApp { GatedFollowData memory data = gatedFollowPerProfile[profileId]; if (data.remoteChainId != _srcChainId || data.balanceThreshold != threshold || data.remoteContract != token) { - emit MessageFailed(_srcChainId, _srcAddress, _nonce, _payload, 'FollowInvalid'); + emit MessageFailed(_srcChainId, _srcAddress, _nonce, _payload, 'InvalidRemoteInput'); + return; } validatedFollowers[profileId][follower] = true; diff --git a/contracts/LZGatedProxy.sol b/contracts/LZGatedProxy.sol index b0584bd..ebd97be 100644 --- a/contracts/LZGatedProxy.sol +++ b/contracts/LZGatedProxy.sol @@ -14,34 +14,47 @@ contract LZGatedProxy is SimpleLzApp { error InsufficientBalance(); address public zroPaymentAddress; // ZRO payment address + bytes public remoteFollowModule; // LZGatedFollowModule + bytes public remoteReferenceModule; // LZGatedReferenceModule + bytes public remoteCollectModule; // LZGatedCollectModule /** * @dev contract constructor * @param _lzEndpoint: The LZ endpoint contract deployed on this chain * @param _remoteChainId: remote chain id to be set as the trusted remote - * @param _remoteContract: remote contract to be set as the trusted remote + * @param _remoteFollowModule: trusted follow module on the remote chain + * @param _remoteReferenceModule: trusted reference module on the remote chain + * @param _remoteCollectModule: trusted collect module on the remote chain * NOTE: we set `zroPaymentAddress` to the zero address as it does not make sense to make this contract ownable only * to set this variable once their token is out, logistics, etc. */ constructor( address _lzEndpoint, uint16 _remoteChainId, - bytes memory _remoteContract - ) SimpleLzApp(_lzEndpoint, msg.sender, _remoteChainId, _remoteContract) { + bytes memory _remoteFollowModule, + bytes memory _remoteReferenceModule, + bytes memory _remoteCollectModule + ) SimpleLzApp(_lzEndpoint, msg.sender, _remoteChainId) { zroPaymentAddress = address(0); + + remoteFollowModule = _remoteFollowModule; + remoteReferenceModule = _remoteReferenceModule; + remoteCollectModule = _remoteCollectModule; } - function validateAndRelayWithSig( + function relayFollowWithSig( + address follower, uint256 profileId, address tokenContract, uint256 balanceThreshold, DataTypes.FollowWithSigData memory followSig ) external { - if (!_checkThreshold(msg.sender, tokenContract, balanceThreshold)) { revert InsufficientBalance(); } + if (!_checkThreshold(follower, tokenContract, balanceThreshold)) { revert InsufficientBalance(); } _lzSend( + remoteFollowModule, abi.encode( - msg.sender, + follower, tokenContract, profileId, balanceThreshold, @@ -53,6 +66,89 @@ contract LZGatedProxy is SimpleLzApp { ); } + function relayCommentWithSig( + address sender, + uint256 profileId, + uint256 profileIdPointed, + uint256 pubIdPointed, + address tokenContract, + uint256 balanceThreshold, + DataTypes.CommentWithSigData memory commentSig + ) external { + if (!_checkThreshold(sender, tokenContract, balanceThreshold)) { revert InsufficientBalance(); } + + _lzSend( + remoteReferenceModule, + abi.encode( + true, // isComment + tokenContract, + profileId, + profileIdPointed, + pubIdPointed, + balanceThreshold, + commentSig + ), + payable(msg.sender), + zroPaymentAddress, + bytes("") + ); + } + + function relayMirrorWithSig( + address sender, + uint256 profileId, + uint256 profileIdPointed, + uint256 pubIdPointed, + address tokenContract, + uint256 balanceThreshold, + DataTypes.MirrorWithSigData memory mirrorSig + ) external { + if (!_checkThreshold(sender, tokenContract, balanceThreshold)) { revert InsufficientBalance(); } + + _lzSend( + remoteReferenceModule, + abi.encode( + false, // isComment + tokenContract, + profileId, + profileIdPointed, + pubIdPointed, + balanceThreshold, + mirrorSig + ), + payable(msg.sender), + zroPaymentAddress, + bytes("") + ); + } + + // @TODO + // function relayCollectWithSig( + // address collector, + // uint256 profileId, + // uint256 pubId, + // address tokenContract, + // uint256 balanceThreshold, + // DataTypes.CollectWithSigData memory collectSig + // ) external { + // if (!_checkThreshold(collector, tokenContract, balanceThreshold)) { revert InsufficientBalance(); } + // + // _lzSend( + // remoteCollectModule, + // abi.encode( + // tokenContract, + // collector, + // profileId, + // pubId, + // balanceThreshold, + // collectSig + // ), + // payable(msg.sender), + // zroPaymentAddress, + // bytes("") + // ); + // } + /** * @dev Check that `account` meets the threshold of held tokens in `tokenContract`; we use the standard `#balanceOf` * function signature for ERC721 and ERC20 diff --git a/contracts/LZGatedReferenceModule.sol b/contracts/LZGatedReferenceModule.sol new file mode 100644 index 0000000..9c5837e --- /dev/null +++ b/contracts/LZGatedReferenceModule.sol @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.10; + +import {IReferenceModule} from '@aave/lens-protocol/contracts/interfaces/IReferenceModule.sol'; +import {ModuleBase, Errors} from "@aave/lens-protocol/contracts/core/modules/ModuleBase.sol"; +import {FollowValidationModuleBase} from '@aave/lens-protocol/contracts/core/modules/FollowValidationModuleBase.sol'; +import {IERC721} from '@openzeppelin/contracts/token/ERC721/IERC721.sol'; +import {ILensHub} from "@aave/lens-protocol/contracts/interfaces/ILensHub.sol"; +import {DataTypes} from "@aave/lens-protocol/contracts/libraries/DataTypes.sol"; +import {LzApp} from "./lz/LzApp.sol"; +import "hardhat/console.sol"; + +/** + * @title LZGatedReferenceModule + * @author Hypotenuse Labs + * + * @notice A Lens Reference Module that allows publication creators to gate who can mirror their post with ERC20 or + * ERC721 balances held on other chains. + */ +contract LZGatedReferenceModule is FollowValidationModuleBase, IReferenceModule, LzApp { + address public zroPaymentAddress; // ZRO payment address + + struct GatedReferenceData { + address remoteContract; // the remote contract to read from + uint256 balanceThreshold; // result of balanceOf() should be greater than or equal to + uint16 remoteChainId; // the remote chainId to read against + } + + mapping (uint256 => mapping (uint256 => GatedReferenceData)) public gatedReferencedDataPerPub; // profileId => pubId => gated reference data + mapping (uint256 => mapping (uint256 => mapping (uint256 => bool))) public validatedReferencers; // profileIdPointed => pubId => profiles which have been validated + + event InitReferenceModule(uint256 indexed profileId, address tokenContract, uint256 balanceThreshold, uint16 chainId); + event MessageFailed(uint16 _srcChainId, bytes _srcAddress, uint64 _nonce, bytes _payload, string _reason); + + error CommentOrMirrorInvalid(); + + /** + * @dev contract constructor + * @param hub LensHub + * @param _lzEndpoint: LayerZero endpoint on this chain to relay messages + * @param remoteChainIds: whitelisted destination chain ids (supported by LayerZero) + * @param remoteProxies: proxy destination contracts (deployed by us) + * NOTE: we set `zroPaymentAddress` to the zero address as it does not make sense to make this module ownable only to + * set this variable once their token is out, logistics, etc. + */ + constructor( + address hub, + address _lzEndpoint, + uint16[] memory remoteChainIds, + bytes[] memory remoteProxies + ) ModuleBase(hub) LzApp(_lzEndpoint, msg.sender, remoteChainIds, remoteProxies) { + zroPaymentAddress = address(0); + } + + /** + * @notice Initialize this reference module for the given profile/publication + * + * @param profileId The profile ID of the profile creating the pub + * @param pubId The pub to init this reference module to + * @param data The arbitrary data parameter, which in this particular module initialization will be just ignored. + * + * @return bytes Empty bytes. + */ + function initializeReferenceModule(uint256 profileId, uint256 pubId, bytes calldata data) + external + override + onlyHub + returns (bytes memory) + { + ( + address tokenContract, + uint256 balanceThreshold, + uint16 chainId + ) = abi.decode(data, (address, uint256, uint16)); + + if (address(tokenContract) == address(0) || _lzRemoteLookup[chainId].length == 0) { + revert Errors.InitParamsInvalid(); + } + + gatedReferencedDataPerPub[profileId][pubId] = GatedReferenceData({ + remoteChainId: chainId, + remoteContract: tokenContract, + balanceThreshold: balanceThreshold + }); + + emit InitReferenceModule(profileId, tokenContract, balanceThreshold, chainId); + + return new bytes(0); + } + + /** + * @dev Process a comment by: + * - checking that we have already validated the commentor through our `LZGatedProxy` on a remote chain + */ + function processComment( + uint256 profileId, + uint256 profileIdPointed, + uint256 pubIdPointed, + bytes calldata // data + ) external view override onlyHub { + if (!validatedReferencers[profileIdPointed][pubIdPointed][profileId]) { + revert CommentOrMirrorInvalid(); + } + } + + /** + * @dev Process a mirror by: + * - checking that we have already validated the mirrorer through our `LZGatedProxy` on a remote chain + */ + function processMirror( + uint256 profileId, + uint256 profileIdPointed, + uint256 pubIdPointed, + bytes calldata // data + ) external view override onlyHub { + if (!validatedReferencers[profileIdPointed][pubIdPointed][profileId]) { + revert CommentOrMirrorInvalid(); + } + } + + /** + * @dev Callback from our `LZGatedProxy` contract deployed on a remote chain, signals that the comment/mirror + * is validated + * NOTE: this function is actually non-blocking in that it catches errors thrown from LensHub + */ + function _blockingLzReceive( + uint16 _srcChainId, + bytes memory _srcAddress, + uint64 _nonce, + bytes memory _payload + ) internal override { + ( + bool isComment, + address token, + uint256 profileId, + uint256 profileIdPointed, + uint256 pubIdPointed, + uint256 threshold, + ) = abi.decode(_payload, (bool, address, uint256, uint256, uint256, uint256, bytes)); + + GatedReferenceData memory data = gatedReferencedDataPerPub[profileIdPointed][pubIdPointed]; + + if (data.remoteChainId != _srcChainId || data.balanceThreshold != threshold || data.remoteContract != token) { + emit MessageFailed(_srcChainId, _srcAddress, _nonce, _payload, 'InvalidRemoteInput'); + return; + } + + // @TODO: hash the vars vs deeply nested? + validatedReferencers[profileIdPointed][pubIdPointed][profileId] = true; + + string memory error = isComment ? _handleComment(_payload) : _handleMirror(_payload); + + delete validatedReferencers[profileIdPointed][pubIdPointed][profileId]; + + if (bytes(error).length > 0) { + emit MessageFailed(_srcChainId, _srcAddress, _nonce, _payload, error); + } + } + + /** + * @dev Decodes the `payload` for Lens#commentWithSig + * @return an error string if the call failed, else empty string + */ + function _handleComment(bytes memory _payload) internal returns (string memory) { + (,,,,,,DataTypes.CommentWithSigData memory commentSig) = abi.decode( + _payload, + (bool, address, uint256, uint256, uint256, uint256, DataTypes.CommentWithSigData) + ); + + try ILensHub(HUB).commentWithSig(commentSig) { + return ""; + } catch Error (string memory reason) { + return reason; + } + } + + /** + * @dev Decodes the `payload` for Lens#mirrorWithSig + * @return an error string if the call failed, else empty string + */ + function _handleMirror(bytes memory _payload) internal returns (string memory) { + (,,,,,,DataTypes.MirrorWithSigData memory mirrorSig) = abi.decode( + _payload, + (bool, address, uint256, uint256, uint256, uint256, DataTypes.MirrorWithSigData) + ); + + try ILensHub(HUB).mirrorWithSig(mirrorSig) { + return ""; + } catch Error (string memory reason) { + return reason; + } + } +} diff --git a/contracts/lz/SimpleLzApp.sol b/contracts/lz/SimpleLzApp.sol index de3f25a..c371282 100644 --- a/contracts/lz/SimpleLzApp.sol +++ b/contracts/lz/SimpleLzApp.sol @@ -8,7 +8,8 @@ import "./interfaces/ILayerZeroEndpoint.sol"; /** * @title SimpleLzApp - * @notice Simple, blocking LayerZero-enabled contract that only has one trusted remote chainId and contract + * @notice Simple, blocking LayerZero-enabled contract that only has one trusted remote chainId and a hardcoded + * module contracts */ abstract contract SimpleLzApp is Owned, ILayerZeroUserApplicationConfig { error NotZeroAddress(); @@ -16,24 +17,22 @@ abstract contract SimpleLzApp is Owned, ILayerZeroUserApplicationConfig { ILayerZeroEndpoint public immutable lzEndpoint; uint16 public remoteChainId; - bytes public remoteContract; /** * @dev contract constructor * @param _lzEndpoint: The LZ endpoint contract deployed on this chain * @param _remoteChainId: remote chain id to be set as the trusted remote - * @param _remoteContract: remote contract to be set as the trusted remote */ - constructor(address _lzEndpoint, address owner, uint16 _remoteChainId, bytes memory _remoteContract) Owned(owner) { + constructor(address _lzEndpoint, address owner, uint16 _remoteChainId) Owned(owner) { if (_lzEndpoint == address(0)) { revert NotZeroAddress(); } lzEndpoint = ILayerZeroEndpoint(_lzEndpoint); remoteChainId = _remoteChainId; - remoteContract = _remoteContract; } function _lzSend( + bytes memory _remoteContract, bytes memory _payload, address payable _refundAddress, address _zroPaymentAddress, @@ -41,7 +40,7 @@ abstract contract SimpleLzApp is Owned, ILayerZeroUserApplicationConfig { ) internal virtual { lzEndpoint.send{value: msg.value}( remoteChainId, - remoteContract, + _remoteContract, _payload, _refundAddress, _zroPaymentAddress, diff --git a/test/LZGatedFollowModule.spec.ts b/test/LZGatedFollowModule.spec.ts index 442b347..15b04c3 100644 --- a/test/LZGatedFollowModule.spec.ts +++ b/test/LZGatedFollowModule.spec.ts @@ -84,6 +84,8 @@ makeSuiteCleanRoom('LZGatedFollowModule', function () { lzEndpoint.address, REMOTE_CHAIN_ID, followModuleAddress, + ZERO_ADDRESS, // _remoteReferenceModule + ZERO_ADDRESS // _remoteCollectModule ); followModule = await new LZGatedFollowModule__factory(deployer).deploy( lensHub.address, @@ -186,7 +188,7 @@ makeSuiteCleanRoom('LZGatedFollowModule', function () { }); }); - describe('#processFollow (triggered from LZGatedProxy#validateAndRelayWithSig)', () => { + describe('#processFollow (triggered from LZGatedProxy#relayFollowWithSig)', () => { let followWithSigData; beforeEach(async() => { @@ -228,7 +230,8 @@ makeSuiteCleanRoom('LZGatedFollowModule', function () { await expect( lzGatedProxy .connect(user) - .validateAndRelayWithSig( + .relayFollowWithSig( + userAddress, FIRST_PROFILE_ID, erc721.address, BALANCE_THRESHOLD, @@ -241,7 +244,8 @@ makeSuiteCleanRoom('LZGatedFollowModule', function () { await expect( lzGatedProxy .connect(user) - .validateAndRelayWithSig( + .relayFollowWithSig( + userAddress, FIRST_PROFILE_ID, lzEndpoint.address, BALANCE_THRESHOLD, @@ -255,7 +259,8 @@ makeSuiteCleanRoom('LZGatedFollowModule', function () { const tx = await lzGatedProxy .connect(user) - .validateAndRelayWithSig( + .relayFollowWithSig( + userAddress, FIRST_PROFILE_ID, erc721.address, 0, @@ -263,7 +268,7 @@ makeSuiteCleanRoom('LZGatedFollowModule', function () { ); const messageFailedReason = getNonBlockingError(await tx.wait()); - expect(messageFailedReason).to.equal('FollowInvalid'); + expect(messageFailedReason).to.equal('InvalidRemoteInput'); }); @@ -272,7 +277,8 @@ makeSuiteCleanRoom('LZGatedFollowModule', function () { const tx = await lzGatedProxy .connect(user) - .validateAndRelayWithSig( + .relayFollowWithSig( + userAddress, FIRST_PROFILE_ID, erc20.address, BALANCE_THRESHOLD, @@ -280,7 +286,7 @@ makeSuiteCleanRoom('LZGatedFollowModule', function () { ); const messageFailedReason = getNonBlockingError(await tx.wait()); - expect(messageFailedReason).to.equal('FollowInvalid'); + expect(messageFailedReason).to.equal('InvalidRemoteInput'); }); it('processes a valid follow', async () => { @@ -288,7 +294,8 @@ makeSuiteCleanRoom('LZGatedFollowModule', function () { const tx = await lzGatedProxy .connect(user) - .validateAndRelayWithSig( + .relayFollowWithSig( + userAddress, FIRST_PROFILE_ID, erc721.address, BALANCE_THRESHOLD, @@ -313,7 +320,8 @@ makeSuiteCleanRoom('LZGatedFollowModule', function () { await lzGatedProxy .connect(user) - .validateAndRelayWithSig( + .relayFollowWithSig( + userAddress, FIRST_PROFILE_ID, erc721.address, BALANCE_THRESHOLD, @@ -323,7 +331,8 @@ makeSuiteCleanRoom('LZGatedFollowModule', function () { await expect( lzGatedProxy .connect(user) - .validateAndRelayWithSig( + .relayFollowWithSig( + userAddress, FIRST_PROFILE_ID, erc721.address, BALANCE_THRESHOLD, diff --git a/test/LZGatedReferenceModule.spec.ts b/test/LZGatedReferenceModule.spec.ts new file mode 100644 index 0000000..60a2dd3 --- /dev/null +++ b/test/LZGatedReferenceModule.spec.ts @@ -0,0 +1,521 @@ +import '@nomiclabs/hardhat-ethers'; +import { expect } from 'chai'; +import { Signer, VoidSigner } from 'ethers'; +const { getContractAddress } = require('@ethersproject/address'); +import { ethers } from 'hardhat'; +import { + FIRST_PROFILE_ID, + MOCK_URI, + OTHER_MOCK_URI, + governance, + lensHub, + makeSuiteCleanRoom, + MOCK_FOLLOW_NFT_URI, + MOCK_PROFILE_HANDLE, + MOCK_PROFILE_URI, + user, + userTwo, + userThree, + testWallet, + freeCollectModule, +} from './lens/__setup.spec'; +import { ERRORS } from './lens/helpers/errors'; +import { getCommentWithSigParts, getMirrorWithSigParts } from './lens/helpers/utils'; +import { ZERO_ADDRESS, MAX_UINT256 } from './lens/helpers/constants'; +import { + LZGatedReferenceModule, + LZGatedReferenceModule__factory, + LZGatedProxy, + LZGatedProxy__factory, + LZEndpointMock, + LZEndpointMock__factory, + ERC721Mock, + ERC721Mock__factory, + ERC20Mock, + ERC20Mock__factory, +} from '../typechain-types' +import { abi } from './lens/abi/Events.json'; +import LZGatedReferenceModuleABI from './../build/contracts/LZGatedReferenceModule.sol/LZGatedReferenceModule.json'; +import { parseLogsNested } from './utils/parseLogs'; + +const EMPTY_BYTES = '0x'; +const REMOTE_CHAIN_ID = 123; +const BALANCE_THRESHOLD = 1; + +makeSuiteCleanRoom('LZGatedReferenceModule', function () { + let lzGatedProxy: LZGatedProxy; + let lzEndpoint: LZEndpointMock; + let referenceModule: LZGatedReferenceModule; + let erc721: ERC721Mock; + let erc20: ERC20Mock; + let deployer: Signer, user: Signer, userTwo: Signer, userThree: Signer; + let deployerAddress: string, userAddress: string, userTwoAddress: string; + + const getNonBlockingError = (receipt) => { + const logs = parseLogsNested(receipt, abi, LZGatedReferenceModuleABI.abi); + const event = logs.find(({ name }) => name === 'MessageFailed'); + + return event.args._reason; + }; + + beforeEach(async () => { + ([deployer, user, userTwo, userThree] = await ethers.getSigners()); + deployerAddress = await deployer.getAddress(); + userAddress = await user.getAddress(); + userTwoAddress = await userTwo.getAddress(); + + lzEndpoint = await new LZEndpointMock__factory(deployer).deploy(REMOTE_CHAIN_ID); + const transactionCount = await deployer.getTransactionCount(); + const referenceModuleAddress = getContractAddress({ from: deployerAddress, nonce: transactionCount + 1 }); + + lzGatedProxy = await new LZGatedProxy__factory(deployer).deploy( + lzEndpoint.address, + REMOTE_CHAIN_ID, + ZERO_ADDRESS, // _remoteFollowModule + referenceModuleAddress, + ZERO_ADDRESS // _remoteCollectModule + ); + referenceModule = await new LZGatedReferenceModule__factory(deployer).deploy( + lensHub.address, + lzEndpoint.address, + [REMOTE_CHAIN_ID], + [lzGatedProxy.address] + ); + erc721 = await new ERC721Mock__factory(deployer).deploy(); + erc20 = await new ERC20Mock__factory(deployer).deploy(); + + // use same lz endpoint mock + await lzEndpoint.setDestLzEndpoint(referenceModule.address, lzEndpoint.address); + await lzEndpoint.setDestLzEndpoint(lzGatedProxy.address, lzEndpoint.address); + + await lensHub.connect(governance).whitelistCollectModule(freeCollectModule.address, true) + await lensHub.connect(governance).whitelistReferenceModule(referenceModule.address, true); + + await lensHub.createProfile({ + to: userAddress, + handle: MOCK_PROFILE_HANDLE, + imageURI: MOCK_PROFILE_URI, + followModule: ZERO_ADDRESS, + followModuleInitData: [], + followNFTURI: MOCK_FOLLOW_NFT_URI, + }); + + await lensHub.createProfile({ + to: userTwoAddress, + handle: 'test.lens', + imageURI: MOCK_PROFILE_URI, + followModule: ZERO_ADDRESS, + followModuleInitData: [], + followNFTURI: MOCK_FOLLOW_NFT_URI, + }); + }); + + describe('#constructor', () => { + it('reverts when the hub arg is the null address', async () => { + expect( + new LZGatedReferenceModule__factory(deployer).deploy(ZERO_ADDRESS, lzEndpoint.address, [], []) + ).to.be.revertedWith('InitParamsInvalid'); + }); + + it('sets storage', async () => { + const owner = await referenceModule.owner(); + const endpoint = await referenceModule.lzEndpoint(); + + expect(owner).to.equal(deployerAddress); + expect(endpoint).to.equal(lzEndpoint.address); + }); + }); + + describe('#initializeReferenceModule', () => { + it('reverts when the caller is not LensHub', async () => { + await expect( + referenceModule.initializeReferenceModule(FIRST_PROFILE_ID, 1, EMPTY_BYTES) + ).to.be.revertedWith(ERRORS.NOT_HUB); + }); + + it('reverts when an invalid chain id is provided in the encoded data', async () => { + const referenceModuleInitData = ethers.utils.defaultAbiCoder.encode( + ['address', 'uint256', 'uint16'], + [erc721.address, BALANCE_THRESHOLD, 12345] + ); + + await expect( + lensHub.post({ + profileId: FIRST_PROFILE_ID, + contentURI: MOCK_URI, + collectModule: freeCollectModule.address, + collectModuleInitData: ethers.utils.defaultAbiCoder.encode(['bool'], [true]), + referenceModule: referenceModule.address, + referenceModuleInitData: referenceModuleInitData, + }) + ).to.be.revertedWith(ERRORS.INIT_PARAMS_INVALID); + }); + + it('reverts when token contract as zero address is provided in the encoded data', async () => { + const referenceModuleInitData = ethers.utils.defaultAbiCoder.encode( + ['address', 'uint256', 'uint16'], + [ZERO_ADDRESS, BALANCE_THRESHOLD, REMOTE_CHAIN_ID] + ); + + await expect( + lensHub.post({ + profileId: FIRST_PROFILE_ID, + contentURI: MOCK_URI, + collectModule: freeCollectModule.address, + collectModuleInitData: ethers.utils.defaultAbiCoder.encode(['bool'], [true]), + referenceModule: referenceModule.address, + referenceModuleInitData: referenceModuleInitData, + }) + ).to.be.revertedWith(ERRORS.INIT_PARAMS_INVALID); + }); + + context('context: with valid params', () => { + let tx; + + beforeEach(async() => { + const referenceModuleInitData = ethers.utils.defaultAbiCoder.encode( + ['address', 'uint256', 'uint16'], + [erc721.address, BALANCE_THRESHOLD, REMOTE_CHAIN_ID] + ); + tx = await lensHub.post({ + profileId: FIRST_PROFILE_ID, + contentURI: MOCK_URI, + collectModule: freeCollectModule.address, + collectModuleInitData: ethers.utils.defaultAbiCoder.encode(['bool'], [true]), + referenceModule: referenceModule.address, + referenceModuleInitData: referenceModuleInitData, + }); + }); + + it('sets storage', async () => { + const res = await referenceModule.gatedReferencedDataPerPub(FIRST_PROFILE_ID, 1); + + expect(res.balanceThreshold.toNumber()).to.equal(BALANCE_THRESHOLD); + expect(res.remoteContract).to.equal(erc721.address); + expect(res.remoteChainId).to.equal(REMOTE_CHAIN_ID); + }); + + it('emits an event', async () => { + const logs = parseLogsNested(await tx.wait(), abi, LZGatedReferenceModuleABI.abi); + const event = logs.find(({ name }) => name === 'InitReferenceModule'); + + expect(event).not.to.equal(undefined); + expect(event.args.profileId.toNumber()).to.equal(FIRST_PROFILE_ID); + expect(event.args.tokenContract).to.equal(erc721.address); + expect(event.args.chainId).to.equal(REMOTE_CHAIN_ID); + }); + }); + }); + + describe('#processComment (triggered from LZGatedProxy#relayCommentWithSig)', () => { + let commentWithSigData; + let referenceModuleInitData; + + beforeEach(async() => { + const collectModuleInitData = ethers.utils.defaultAbiCoder.encode(['bool'], [true]); + referenceModuleInitData = ethers.utils.defaultAbiCoder.encode( + ['address', 'uint256', 'uint16'], + [erc721.address, BALANCE_THRESHOLD, REMOTE_CHAIN_ID] + ); + + await lensHub.post({ + profileId: FIRST_PROFILE_ID, + contentURI: MOCK_URI, + collectModule: freeCollectModule.address, + collectModuleInitData, + referenceModule: referenceModule.address, + referenceModuleInitData: referenceModuleInitData, + }); + + const nonce = (await lensHub.sigNonces(userTwoAddress)).toNumber(); + const { v, r, s } = await getCommentWithSigParts( + userTwo as VoidSigner, + FIRST_PROFILE_ID + 1, + OTHER_MOCK_URI, + FIRST_PROFILE_ID, + '1', + [], + freeCollectModule.address, + collectModuleInitData, + ZERO_ADDRESS, + [], + nonce, + MAX_UINT256 + ); + + // userTwo signs that they would like to comment on user's first post + commentWithSigData = { + profileId: FIRST_PROFILE_ID + 1, + contentURI: OTHER_MOCK_URI, + profileIdPointed: FIRST_PROFILE_ID, + pubIdPointed: '1', + collectModule: freeCollectModule.address, + collectModuleInitData, + referenceModuleData: [], + referenceModule: ZERO_ADDRESS, + referenceModuleInitData: [], + sig: { + v, + r, + s, + deadline: MAX_UINT256, + }, + }; + }); + + it('reverts if called without going through lzGatedProxy', async () => { + await expect( + lensHub.commentWithSig(commentWithSigData) + ).to.be.revertedWith('CommentOrMirrorInvalid()'); + }); + + it('reverts if the caller does not have suffient balance', async () => { + await expect( + lzGatedProxy + .connect(userTwo) + .relayCommentWithSig( + userTwoAddress, + FIRST_PROFILE_ID + 1, + FIRST_PROFILE_ID, + '1', + erc721.address, + BALANCE_THRESHOLD, + commentWithSigData + ) + ).to.be.revertedWith('InsufficientBalance'); + }); + + it('reverts if the contract call for balanceOf() fails', async () => { + await expect( + lzGatedProxy + .connect(userTwo) + .relayCommentWithSig( + userAddress, + FIRST_PROFILE_ID + 1, + FIRST_PROFILE_ID, + '1', + lzEndpoint.address, + BALANCE_THRESHOLD, + commentWithSigData + ) + ).to.be.revertedWith('InsufficientBalance'); + }); + + it('[non-blocking] fails if the caller passed an invalid threshold', async () => { + await erc721.safeMint(userTwoAddress); + + const tx = await lzGatedProxy + .connect(userTwo) + .relayCommentWithSig( + userTwoAddress, + FIRST_PROFILE_ID + 1, + FIRST_PROFILE_ID, + '1', + erc721.address, + 0, + commentWithSigData + ); + + const messageFailedReason = getNonBlockingError(await tx.wait()); + expect(messageFailedReason).to.equal('InvalidRemoteInput'); + }); + + + it('[non-blocking] fails if the caller passed an invalid token contract', async () => { + await erc20.mint(userTwoAddress, BALANCE_THRESHOLD); + + const tx = await lzGatedProxy + .connect(userTwo) + .relayCommentWithSig( + userTwoAddress, + FIRST_PROFILE_ID + 1, + FIRST_PROFILE_ID, + '1', + erc20.address, + BALANCE_THRESHOLD, + commentWithSigData + ); + + const messageFailedReason = getNonBlockingError(await tx.wait()); + expect(messageFailedReason).to.equal('InvalidRemoteInput'); + }); + + it('processes a valid comment', async () => { + await erc721.safeMint(userTwoAddress); + + const tx = await lzGatedProxy + .connect(userTwo) + .relayCommentWithSig( + userTwoAddress, + FIRST_PROFILE_ID + 1, + FIRST_PROFILE_ID, + '1', + erc721.address, + BALANCE_THRESHOLD, + commentWithSigData + ); + const logs = parseLogsNested(await tx.wait(), abi, LZGatedReferenceModuleABI.abi); + + const event = logs.find(({ name }) => name === 'MessageFailed'); + const commentEvent = logs.find(({ name }) => name === 'CommentCreated'); + + expect(event).to.equal(undefined); + expect(commentEvent).not.to.equal(undefined); + expect(commentEvent.args.pubIdPointed.toNumber()).to.equal(1); + expect(commentEvent.args.profileIdPointed.toNumber()).to.equal(1); + }); + }); + + describe('#processMirror (triggered from LZGatedProxy#relayMirrorWithSig)', () => { + let mirrorWithSigData; + let referenceModuleInitData; + + beforeEach(async() => { + const collectModuleInitData = ethers.utils.defaultAbiCoder.encode(['bool'], [true]); + referenceModuleInitData = ethers.utils.defaultAbiCoder.encode( + ['address', 'uint256', 'uint16'], + [erc721.address, BALANCE_THRESHOLD, REMOTE_CHAIN_ID] + ); + + await lensHub.post({ + profileId: FIRST_PROFILE_ID, + contentURI: MOCK_URI, + collectModule: freeCollectModule.address, + collectModuleInitData, + referenceModule: referenceModule.address, + referenceModuleInitData: referenceModuleInitData, + }); + + const nonce = (await lensHub.sigNonces(userTwoAddress)).toNumber(); + const { v, r, s } = await getMirrorWithSigParts( + userTwo as VoidSigner, + FIRST_PROFILE_ID + 1, + FIRST_PROFILE_ID, + '1', + [], + ZERO_ADDRESS, + [], + nonce, + MAX_UINT256 + ); + + // userTwo signs that they would like to mirror the user's first post + mirrorWithSigData = { + profileId: FIRST_PROFILE_ID + 1, + profileIdPointed: FIRST_PROFILE_ID, + pubIdPointed: '1', + referenceModuleData: [], + referenceModule: ZERO_ADDRESS, + referenceModuleInitData: [], + sig: { + v, + r, + s, + deadline: MAX_UINT256, + }, + }; + }); + + it('reverts if called without going through lzGatedProxy', async () => { + await expect( + lensHub.mirrorWithSig(mirrorWithSigData) + ).to.be.revertedWith('CommentOrMirrorInvalid()'); + }); + + it('reverts if the caller does not have suffient balance', async () => { + await expect( + lzGatedProxy + .connect(userTwo) + .relayMirrorWithSig( + userTwoAddress, + FIRST_PROFILE_ID + 1, + FIRST_PROFILE_ID, + '1', + erc721.address, + BALANCE_THRESHOLD, + mirrorWithSigData + ) + ).to.be.revertedWith('InsufficientBalance'); + }); + + it('reverts if the contract call for balanceOf() fails', async () => { + await expect( + lzGatedProxy + .connect(userTwo) + .relayMirrorWithSig( + userTwoAddress, + FIRST_PROFILE_ID + 1, + FIRST_PROFILE_ID, + '1', + lzEndpoint.address, + BALANCE_THRESHOLD, + mirrorWithSigData + ) + ).to.be.revertedWith('InsufficientBalance'); + }); + + it('[non-blocking] fails if the caller passed an invalid threshold', async () => { + await erc721.safeMint(userTwoAddress); + + const tx = await lzGatedProxy + .connect(userTwo) + .relayMirrorWithSig( + userTwoAddress, + FIRST_PROFILE_ID + 1, + FIRST_PROFILE_ID, + '1', + erc721.address, + 0, + mirrorWithSigData + ); + + const messageFailedReason = getNonBlockingError(await tx.wait()); + expect(messageFailedReason).to.equal('InvalidRemoteInput'); + }); + + + it('[non-blocking] fails if the caller passed an invalid token contract', async () => { + await erc20.mint(userTwoAddress, BALANCE_THRESHOLD); + + const tx = await lzGatedProxy + .connect(userTwo) + .relayMirrorWithSig( + userTwoAddress, + FIRST_PROFILE_ID + 1, + FIRST_PROFILE_ID, + '1', + erc20.address, + BALANCE_THRESHOLD, + mirrorWithSigData + ); + + const messageFailedReason = getNonBlockingError(await tx.wait()); + expect(messageFailedReason).to.equal('InvalidRemoteInput'); + }); + + it('processes a valid mirror', async () => { + await erc721.safeMint(userTwoAddress); + + const tx = await lzGatedProxy + .connect(userTwo) + .relayMirrorWithSig( + userTwoAddress, + FIRST_PROFILE_ID + 1, + FIRST_PROFILE_ID, + '1', + erc721.address, + BALANCE_THRESHOLD, + mirrorWithSigData + ); + const logs = parseLogsNested(await tx.wait(), abi, LZGatedReferenceModuleABI.abi); + + const event = logs.find(({ name }) => name === 'MessageFailed'); + const commentEvent = logs.find(({ name }) => name === 'MirrorCreated'); + + expect(event).to.equal(undefined); + expect(commentEvent).not.to.equal(undefined); + expect(commentEvent.args.pubIdPointed.toNumber()).to.equal(1); + expect(commentEvent.args.profileIdPointed.toNumber()).to.equal(1); + }); + }); +}); diff --git a/test/lens/helpers/utils.ts b/test/lens/helpers/utils.ts index a71a79d..a5d44f2 100644 --- a/test/lens/helpers/utils.ts +++ b/test/lens/helpers/utils.ts @@ -113,15 +113,139 @@ export async function getFollowWithSigParts( return await getSig(wallet, msgParams); } -async function getSig(wallet: VoidSigner, msgParams: { - domain: any; - types: any; - value: any; -}): Promise<{ v: number; r: string; s: string }> { - const sig = await wallet._signTypedData(msgParams.domain, msgParams.types, msgParams.value); - return utils.splitSignature(sig); +export async function getCommentWithSigParts( + wallet: VoidSigner, + profileId: BigNumberish, + contentURI: string, + profileIdPointed: BigNumberish, + pubIdPointed: string, + referenceModuleData: Bytes | string, + collectModule: string, + collectModuleInitData: Bytes | string, + referenceModule: string, + referenceModuleInitData: Bytes | string, + nonce: number, + deadline: string +): Promise<{ v: number; r: string; s: string }> { + const msgParams = buildCommentWithSigParams( + profileId, + contentURI, + profileIdPointed, + pubIdPointed, + referenceModuleData, + collectModule, + collectModuleInitData, + referenceModule, + referenceModuleInitData, + nonce, + deadline + ); + return await getSig(wallet, msgParams); } +export async function getMirrorWithSigParts( + wallet: VoidSigner, + profileId: BigNumberish, + profileIdPointed: BigNumberish, + pubIdPointed: string, + referenceModuleData: Bytes | string, + referenceModule: string, + referenceModuleInitData: Bytes | string, + nonce: number, + deadline: string +): Promise<{ v: number; r: string; s: string }> { + const msgParams = buildMirrorWithSigParams( + profileId, + profileIdPointed, + pubIdPointed, + referenceModuleData, + referenceModule, + referenceModuleInitData, + nonce, + deadline + ); + return await getSig(wallet, msgParams); +} + +const buildCommentWithSigParams = ( + profileId: BigNumberish, + contentURI: string, + profileIdPointed: BigNumberish, + pubIdPointed: string, + referenceModuleData: Bytes | string, + collectModule: string, + collectModuleInitData: Bytes | string, + referenceModule: string, + referenceModuleInitData: Bytes | string, + nonce: number, + deadline: string +) => ({ + types: { + CommentWithSig: [ + { name: 'profileId', type: 'uint256' }, + { name: 'contentURI', type: 'string' }, + { name: 'profileIdPointed', type: 'uint256' }, + { name: 'pubIdPointed', type: 'uint256' }, + { name: 'referenceModuleData', type: 'bytes' }, + { name: 'collectModule', type: 'address' }, + { name: 'collectModuleInitData', type: 'bytes' }, + { name: 'referenceModule', type: 'address' }, + { name: 'referenceModuleInitData', type: 'bytes' }, + { name: 'nonce', type: 'uint256' }, + { name: 'deadline', type: 'uint256' }, + ], + }, + domain: domain(), + value: { + profileId: profileId, + contentURI: contentURI, + profileIdPointed: profileIdPointed, + pubIdPointed: pubIdPointed, + referenceModuleData: referenceModuleData, + collectModule: collectModule, + collectModuleInitData: collectModuleInitData, + referenceModule: referenceModule, + referenceModuleInitData: referenceModuleInitData, + nonce: nonce, + deadline: deadline, + }, +}); + +const buildMirrorWithSigParams = ( + profileId: BigNumberish, + profileIdPointed: BigNumberish, + pubIdPointed: string, + referenceModuleData: Bytes | string, + referenceModule: string, + referenceModuleInitData: Bytes | string, + nonce: number, + deadline: string +) => ({ + types: { + MirrorWithSig: [ + { name: 'profileId', type: 'uint256' }, + { name: 'profileIdPointed', type: 'uint256' }, + { name: 'pubIdPointed', type: 'uint256' }, + { name: 'referenceModuleData', type: 'bytes' }, + { name: 'referenceModule', type: 'address' }, + { name: 'referenceModuleInitData', type: 'bytes' }, + { name: 'nonce', type: 'uint256' }, + { name: 'deadline', type: 'uint256' }, + ], + }, + domain: domain(), + value: { + profileId: profileId, + profileIdPointed: profileIdPointed, + pubIdPointed: pubIdPointed, + referenceModuleData: referenceModuleData, + referenceModule: referenceModule, + referenceModuleInitData: referenceModuleInitData, + nonce: nonce, + deadline: deadline, + }, +}); + const buildFollowWithSigParams = ( profileIds: string[] | number[], datas: Bytes[] | string[], @@ -145,6 +269,15 @@ const buildFollowWithSigParams = ( }, }); +async function getSig(wallet: VoidSigner, msgParams: { + domain: any; + types: any; + value: any; +}): Promise<{ v: number; r: string; s: string }> { + const sig = await wallet._signTypedData(msgParams.domain, msgParams.types, msgParams.value); + return utils.splitSignature(sig); +} + function domain(): { name: string; version: string; chainId: number; verifyingContract: string } { return { name: LENS_HUB_NFT_NAME, From a1861f2696e97ad2d9be7dc790d51a189c7dc21e Mon Sep 17 00:00:00 2001 From: Carlos Beltran Date: Fri, 9 Dec 2022 16:39:38 -0600 Subject: [PATCH 05/14] update comment --- contracts/LZGatedProxy.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/LZGatedProxy.sol b/contracts/LZGatedProxy.sol index ebd97be..18de1b7 100644 --- a/contracts/LZGatedProxy.sol +++ b/contracts/LZGatedProxy.sol @@ -7,7 +7,7 @@ import "./lz/SimpleLzApp.sol"; /** * @title LZGatedProxy - * @notice This contract acts as a proxy for `LZGatedFollowModule` and `LZGatedReference` in order to read + * @notice This contract acts as a proxy for `LZGatedFollowModule` and `LZGatedReferenceModule` in order to read * token balances from remote contracts on any chain supported by layerzero. */ contract LZGatedProxy is SimpleLzApp { From 03d8893e46a31e99f8df4d0d21915fc028652763 Mon Sep 17 00:00:00 2001 From: Carlos Beltran Date: Mon, 12 Dec 2022 13:48:00 -0600 Subject: [PATCH 06/14] add: missing comments; move zroPaymentAddress to abstract contract + allow owner to set --- contracts/LZGatedFollowModule.sol | 32 ++++++----- contracts/LZGatedProxy.sol | 65 ++++++++++++++++++----- contracts/LZGatedReferenceModule.sol | 29 +++++----- contracts/OmniSBT.sol | 11 ---- contracts/lz/LzApp.sol | 62 ++++++++++++---------- contracts/lz/SimpleLzApp.sol | 79 +++++++++++++++++++--------- test/LZGatedFollowModule.spec.ts | 2 +- test/LZGatedReferenceModule.spec.ts | 2 +- 8 files changed, 175 insertions(+), 107 deletions(-) diff --git a/contracts/LZGatedFollowModule.sol b/contracts/LZGatedFollowModule.sol index 5b6131f..01bf76d 100644 --- a/contracts/LZGatedFollowModule.sol +++ b/contracts/LZGatedFollowModule.sol @@ -12,43 +12,38 @@ import {LzApp} from "./lz/LzApp.sol"; /** * @title LZGatedFollowModule - * @author Hypotenuse Labs * * @notice A Lens Follow Module that allows profile holders to gate their following with ERC20 or ERC721 balances held * on other chains. */ contract LZGatedFollowModule is FollowValidatorFollowModuleBase, LzApp { - address public zroPaymentAddress; // ZRO payment address - struct GatedFollowData { - address remoteContract; // the remote contract to read from + address tokenContract; // the remote contract to read from uint256 balanceThreshold; // result of balanceOf() should be greater than or equal to uint16 remoteChainId; // the remote chainId to read against } - mapping (uint256 => GatedFollowData) public gatedFollowPerProfile; // profileId => gated follow data - mapping (uint256 => mapping (address => bool)) public validatedFollowers; // profileId => address which has been validated + error NotAccepting(); event InitFollowModule(uint256 indexed profileId, address tokenContract, uint256 balanceThreshold, uint16 chainId); event MessageFailed(uint16 _srcChainId, bytes _srcAddress, uint64 _nonce, bytes _payload, string _reason); + mapping (uint256 => GatedFollowData) public gatedFollowPerProfile; // profileId => gated follow data + mapping (uint256 => mapping (address => bool)) public validatedFollowers; // profileId => address which has been validated + /** * @dev contract constructor * @param hub LensHub * @param _lzEndpoint: LayerZero endpoint on this chain to relay messages * @param remoteChainIds: whitelisted destination chain ids (supported by LayerZero) * @param remoteProxies: proxy destination contracts (deployed by us) - * NOTE: we set `zroPaymentAddress` to the zero address as it does not make sense to make this module ownable only to - * set this variable once their token is out, logistics, etc. */ constructor( address hub, address _lzEndpoint, uint16[] memory remoteChainIds, bytes[] memory remoteProxies - ) ModuleBase(hub) LzApp(_lzEndpoint, msg.sender, remoteChainIds, remoteProxies) { - zroPaymentAddress = address(0); - } + ) ModuleBase(hub) LzApp(_lzEndpoint, msg.sender, remoteChainIds, remoteProxies) {} /** * @notice Initialize this follow module for the given profile @@ -74,9 +69,10 @@ contract LZGatedFollowModule is FollowValidatorFollowModuleBase, LzApp { revert Errors.InitParamsInvalid(); } + // anyone can read this data before attempting to follow the given profile gatedFollowPerProfile[profileId] = GatedFollowData({ remoteChainId: chainId, - remoteContract: tokenContract, + tokenContract: tokenContract, balanceThreshold: balanceThreshold }); @@ -109,9 +105,14 @@ contract LZGatedFollowModule is FollowValidatorFollowModuleBase, LzApp { uint256 followNFTTokenId ) external override {} + /** + * @dev not accepting native tokens + */ + receive() external payable { revert NotAccepting(); } + /** * @dev Callback from our `LZGatedProxy` contract deployed on a remote chain, signals that the follow is validated - * NOTE: this function is actually non-blocking in that it catches errors thrown from LensHub + * NOTE: this function is actually non-blocking in that it does not explicitly revert and catches external errors */ function _blockingLzReceive( uint16 _srcChainId, @@ -129,13 +130,16 @@ contract LZGatedFollowModule is FollowValidatorFollowModuleBase, LzApp { GatedFollowData memory data = gatedFollowPerProfile[profileId]; - if (data.remoteChainId != _srcChainId || data.balanceThreshold != threshold || data.remoteContract != token) { + // validate that remote check was against the contract/threshold defined + if (data.remoteChainId != _srcChainId || data.balanceThreshold != threshold || data.tokenContract != token) { emit MessageFailed(_srcChainId, _srcAddress, _nonce, _payload, 'InvalidRemoteInput'); return; } + // allow the follow in the callback to #processFollow validatedFollowers[profileId][follower] = true; + // use the signature to execute the follow try ILensHub(HUB).followWithSig(followSig) {} catch Error (string memory reason) { emit MessageFailed(_srcChainId, _srcAddress, _nonce, _payload, reason); diff --git a/contracts/LZGatedProxy.sol b/contracts/LZGatedProxy.sol index 18de1b7..0f9fdeb 100644 --- a/contracts/LZGatedProxy.sol +++ b/contracts/LZGatedProxy.sol @@ -7,26 +7,24 @@ import "./lz/SimpleLzApp.sol"; /** * @title LZGatedProxy - * @notice This contract acts as a proxy for `LZGatedFollowModule` and `LZGatedReferenceModule` in order to read - * token balances from remote contracts on any chain supported by layerzero. + * @notice This contract acts as a proxy for our `LZGated*` Lens modules in order to read + * token balances from remote contracts on any chain supported by LayerZero. */ contract LZGatedProxy is SimpleLzApp { error InsufficientBalance(); + error NotAccepting(); - address public zroPaymentAddress; // ZRO payment address bytes public remoteFollowModule; // LZGatedFollowModule bytes public remoteReferenceModule; // LZGatedReferenceModule bytes public remoteCollectModule; // LZGatedCollectModule /** * @dev contract constructor - * @param _lzEndpoint: The LZ endpoint contract deployed on this chain + * @param _lzEndpoint: The lz endpoint contract deployed on this chain * @param _remoteChainId: remote chain id to be set as the trusted remote * @param _remoteFollowModule: trusted follow module on the remote chain * @param _remoteReferenceModule: trusted reference module on the remote chain * @param _remoteCollectModule: trusted collect module on the remote chain - * NOTE: we set `zroPaymentAddress` to the zero address as it does not make sense to make this contract ownable only - * to set this variable once their token is out, logistics, etc. */ constructor( address _lzEndpoint, @@ -35,13 +33,23 @@ contract LZGatedProxy is SimpleLzApp { bytes memory _remoteReferenceModule, bytes memory _remoteCollectModule ) SimpleLzApp(_lzEndpoint, msg.sender, _remoteChainId) { - zroPaymentAddress = address(0); - remoteFollowModule = _remoteFollowModule; remoteReferenceModule = _remoteReferenceModule; remoteCollectModule = _remoteCollectModule; } + /** + * @notice validate a token balance on this chain before relaying the intent to follow a Lens profile on the remote + * chain. + * NOTE: callers of this function MUST pass the exact values for `tokenContract` and `balanceThreshold` returned from + * the call to LZGatedFollowModule.gatedFollowPerProfile(profileId) - or the transaction on the remote chain WILL + * revert. + * @param follower: the account wishing to perform the follow action + * @param profileId: the id of the profile being followed + * @param tokenContract: the ERC20/ERC721 contract set by the `profileId` to check a balance against + * @param balanceThreshold: the amount of tokens required in order for a successful follow + * @param followSig: the follow signature expected by the LensHub + */ function relayFollowWithSig( address follower, uint256 profileId, @@ -61,11 +69,24 @@ contract LZGatedProxy is SimpleLzApp { followSig ), payable(msg.sender), - zroPaymentAddress, bytes("") ); } + /** + * @notice validate a token balance on this chain before relaying the intent to comment on a Lens post on the remote + * chain. + * NOTE: callers of this function MUST pass the exact values for `tokenContract` and `balanceThreshold` returned from + * the call to LZGatedReferenceModule.gatedReferencedDataPerPub(profileIdPointed, pubIdPointed) - or the transaction + * on the remote chain WILL revert. + * @param sender: the account wishing to perform the comment action + * @param profileId: the id of the profile wishing to comment + * @param profileIdPointed: the id of the profile that owns the post + * @param pubIdPointed: the id of the post + * @param tokenContract: the ERC20/ERC721 contract set by the `profileId` to check a balance against + * @param balanceThreshold: the amount of tokens required in order for a successful follow + * @param commentSig: the comment signature expected by the LensHub + */ function relayCommentWithSig( address sender, uint256 profileId, @@ -89,11 +110,24 @@ contract LZGatedProxy is SimpleLzApp { commentSig ), payable(msg.sender), - zroPaymentAddress, bytes("") ); } + /** + * @notice validate a token balance on this chain before relaying the intent to mirror a Lens post on the remote + * chain. + * NOTE: callers of this function MUST pass the exact values for `tokenContract` and `balanceThreshold` returned from + * the call to LZGatedReferenceModule.gatedReferencedDataPerPub(profileIdPointed, pubIdPointed) - or the transaction + * on the remote chain WILL revert. + * @param sender: the account wishing to perform the mirror action + * @param profileId: the id of the profile wishing to mirror + * @param profileIdPointed: the id of the profile that owns the post + * @param pubIdPointed: the id of the post + * @param tokenContract: the ERC20/ERC721 contract set by the `profileId` to check a balance against + * @param balanceThreshold: the amount of tokens required in order for a successful follow + * @param mirrorSig: the mirror signature expected by the LensHub + */ function relayMirrorWithSig( address sender, uint256 profileId, @@ -117,7 +151,6 @@ contract LZGatedProxy is SimpleLzApp { mirrorSig ), payable(msg.sender), - zroPaymentAddress, bytes("") ); } @@ -144,14 +177,18 @@ contract LZGatedProxy is SimpleLzApp { // collectSig // ), // payable(msg.sender), - // zroPaymentAddress, // bytes("") // ); // } /** - * @dev Check that `account` meets the threshold of held tokens in `tokenContract`; we use the standard `#balanceOf` - * function signature for ERC721 and ERC20 + * @dev not accepting native tokens + */ + receive() external payable { revert NotAccepting(); } + + /** + * @dev Check that `account` meets the `balanceThreshold` of held tokens in `tokenContract`; we use the standard + * `#balanceOf` function signature for ERC721 and ERC20, and simply return false on any error thrown. */ function _checkThreshold(address account, address tokenContract, uint256 balanceThreshold) private returns (bool) { ( diff --git a/contracts/LZGatedReferenceModule.sol b/contracts/LZGatedReferenceModule.sol index 9c5837e..8fb2fd1 100644 --- a/contracts/LZGatedReferenceModule.sol +++ b/contracts/LZGatedReferenceModule.sol @@ -13,16 +13,15 @@ import "hardhat/console.sol"; /** * @title LZGatedReferenceModule - * @author Hypotenuse Labs * - * @notice A Lens Reference Module that allows publication creators to gate who can mirror their post with ERC20 or - * ERC721 balances held on other chains. + * @notice A Lens Reference Module that allows publication creators to gate who can comment/mirror their post with + * ERC20 or ERC721 balances held on other chains. */ contract LZGatedReferenceModule is FollowValidationModuleBase, IReferenceModule, LzApp { - address public zroPaymentAddress; // ZRO payment address + error NotAccepting(); struct GatedReferenceData { - address remoteContract; // the remote contract to read from + address tokenContract; // the remote contract to read from uint256 balanceThreshold; // result of balanceOf() should be greater than or equal to uint16 remoteChainId; // the remote chainId to read against } @@ -41,17 +40,13 @@ contract LZGatedReferenceModule is FollowValidationModuleBase, IReferenceModule, * @param _lzEndpoint: LayerZero endpoint on this chain to relay messages * @param remoteChainIds: whitelisted destination chain ids (supported by LayerZero) * @param remoteProxies: proxy destination contracts (deployed by us) - * NOTE: we set `zroPaymentAddress` to the zero address as it does not make sense to make this module ownable only to - * set this variable once their token is out, logistics, etc. */ constructor( address hub, address _lzEndpoint, uint16[] memory remoteChainIds, bytes[] memory remoteProxies - ) ModuleBase(hub) LzApp(_lzEndpoint, msg.sender, remoteChainIds, remoteProxies) { - zroPaymentAddress = address(0); - } + ) ModuleBase(hub) LzApp(_lzEndpoint, msg.sender, remoteChainIds, remoteProxies) {} /** * @notice Initialize this reference module for the given profile/publication @@ -78,9 +73,10 @@ contract LZGatedReferenceModule is FollowValidationModuleBase, IReferenceModule, revert Errors.InitParamsInvalid(); } + // anyone can read this data before attempting to follow the given profile gatedReferencedDataPerPub[profileId][pubId] = GatedReferenceData({ remoteChainId: chainId, - remoteContract: tokenContract, + tokenContract: tokenContract, balanceThreshold: balanceThreshold }); @@ -119,10 +115,15 @@ contract LZGatedReferenceModule is FollowValidationModuleBase, IReferenceModule, } } + /** + * @dev not accepting native tokens + */ + receive() external payable { revert NotAccepting(); } + /** * @dev Callback from our `LZGatedProxy` contract deployed on a remote chain, signals that the comment/mirror * is validated - * NOTE: this function is actually non-blocking in that it catches errors thrown from LensHub + * NOTE: this function is actually non-blocking in that it does not explicitly revert and catches external errors */ function _blockingLzReceive( uint16 _srcChainId, @@ -141,7 +142,8 @@ contract LZGatedReferenceModule is FollowValidationModuleBase, IReferenceModule, GatedReferenceData memory data = gatedReferencedDataPerPub[profileIdPointed][pubIdPointed]; - if (data.remoteChainId != _srcChainId || data.balanceThreshold != threshold || data.remoteContract != token) { + // validate that remote check was against the contract/threshold defined + if (data.remoteChainId != _srcChainId || data.balanceThreshold != threshold || data.tokenContract != token) { emit MessageFailed(_srcChainId, _srcAddress, _nonce, _payload, 'InvalidRemoteInput'); return; } @@ -149,6 +151,7 @@ contract LZGatedReferenceModule is FollowValidationModuleBase, IReferenceModule, // @TODO: hash the vars vs deeply nested? validatedReferencers[profileIdPointed][pubIdPointed][profileId] = true; + // parse the payload for either #commentWithSig or #mirrorWithSig string memory error = isComment ? _handleComment(_payload) : _handleMirror(_payload); delete validatedReferencers[profileIdPointed][pubIdPointed][profileId]; diff --git a/contracts/OmniSBT.sol b/contracts/OmniSBT.sol index 44b5ec9..178d0af 100644 --- a/contracts/OmniSBT.sol +++ b/contracts/OmniSBT.sol @@ -17,7 +17,6 @@ contract OmniSBT is IOmniSBT, URIStorage, ERC4973, LzApp { error OnlyTokenOwner(); address public collectModule; // the FollowCampaignModule contract that can create collections and mint - address public zroPaymentAddress; // ZRO payment address uint256 public collections; // counter for collections; 1-based mapping (uint256 => uint256) public tokenToCollectionId; // _tokenIdCounter => collections @@ -45,7 +44,6 @@ contract OmniSBT is IOmniSBT, URIStorage, ERC4973, LzApp { LzApp(_lzEndpoint, msg.sender, remoteChainIds, remoteContracts) ERC4973("Omni Soulbound Token", "OMNI-SBT") { - zroPaymentAddress = address(0); isSource = _isSource; } @@ -86,7 +84,6 @@ contract OmniSBT is IOmniSBT, URIStorage, ERC4973, LzApp { chainId, abi.encode(collector, collectionId, _tokenIdCounter, URIStorage.tokenURI(collectionId)), payable(collector), - zroPaymentAddress, bytes("") ); @@ -130,14 +127,6 @@ contract OmniSBT is IOmniSBT, URIStorage, ERC4973, LzApp { collectModule = _collectModule; } - /** - * @notice allows the contract owner to set the ZRO payment address - * @param _zroPaymentAddress: ZRO token address used as alternative payment for relayed messages - */ - function setZroPaymentAddress(address _zroPaymentAddress) external onlyOwner { - zroPaymentAddress = _zroPaymentAddress; - } - /** * @notice EIP-165 */ diff --git a/contracts/lz/LzApp.sol b/contracts/lz/LzApp.sol index 61ec264..9c43e96 100644 --- a/contracts/lz/LzApp.sol +++ b/contracts/lz/LzApp.sol @@ -8,7 +8,8 @@ import "./interfaces/ILayerZeroUserApplicationConfig.sol"; import "./interfaces/ILayerZeroEndpoint.sol"; /** - * + * @title LzApp + * @notice LayerZero-enabled contract that can have multiple remote chain ids */ abstract contract LzApp is Owned, ILayerZeroReceiver, ILayerZeroUserApplicationConfig { error NotZeroAddress(); @@ -19,6 +20,8 @@ abstract contract LzApp is Owned, ILayerZeroReceiver, ILayerZeroUserApplicationC ILayerZeroEndpoint public immutable lzEndpoint; + address public zroPaymentAddress; // the address of the ZRO token holder who would pay for the transaction + mapping (uint16 => bytes) internal _lzRemoteLookup; // chainId (lz) => endpoint /** @@ -46,25 +49,6 @@ abstract contract LzApp is Owned, ILayerZeroReceiver, ILayerZeroUserApplicationC } } - function _lzSend( - uint16 _dstChainId, - bytes memory _payload, - address payable _refundAddress, - address _zroPaymentAddress, - bytes memory _adapterParams - ) internal virtual { - if (_lzRemoteLookup[_dstChainId].length == 0) { revert RemoteNotFound(); } - - lzEndpoint.send{value: msg.value}( - _dstChainId, - _lzRemoteLookup[_dstChainId], - _payload, - _refundAddress, - _zroPaymentAddress, - _adapterParams - ); - } - function lzReceive( uint16 _srcChainId, bytes memory _srcAddress, @@ -81,14 +65,6 @@ abstract contract LzApp is Owned, ILayerZeroReceiver, ILayerZeroUserApplicationC _blockingLzReceive(_srcChainId, _srcAddress, _nonce, _payload); } - // @dev to be overriden by the concrete class - function _blockingLzReceive( - uint16 _srcChainId, - bytes memory _srcAddress, - uint64 _nonce, - bytes memory _payload - ) internal virtual; - function setTrustedRemote(uint16 _srcChainId, bytes calldata _srcAddress) external onlyOwner { _lzRemoteLookup[_srcChainId] = _srcAddress; } @@ -111,6 +87,10 @@ abstract contract LzApp is Owned, ILayerZeroReceiver, ILayerZeroUserApplicationC lzEndpoint.setReceiveVersion(_version); } + function setZroPaymentAddress(address _zroPaymentAddress) external onlyOwner { + zroPaymentAddress = _zroPaymentAddress; +} + function forceResumeReceive(uint16 _srcChainId, bytes calldata _srcAddress) external override onlyOwner { lzEndpoint.forceResumeReceive(_srcChainId, _srcAddress); } @@ -118,4 +98,30 @@ abstract contract LzApp is Owned, ILayerZeroReceiver, ILayerZeroUserApplicationC function getConfig(uint16 _version, uint16 _chainId, address, uint _configType) external view returns (bytes memory) { return lzEndpoint.getConfig(_version, _chainId, address(this), _configType); } + + function _lzSend( + uint16 _dstChainId, + bytes memory _payload, + address payable _refundAddress, + bytes memory _adapterParams + ) internal virtual { + if (_lzRemoteLookup[_dstChainId].length == 0) { revert RemoteNotFound(); } + + lzEndpoint.send{value: msg.value}( + _dstChainId, + _lzRemoteLookup[_dstChainId], + _payload, + _refundAddress, + zroPaymentAddress, + _adapterParams + ); + } + + // @dev to be overriden by the concrete class + function _blockingLzReceive( + uint16 _srcChainId, + bytes memory _srcAddress, + uint64 _nonce, + bytes memory _payload + ) internal virtual; } diff --git a/contracts/lz/SimpleLzApp.sol b/contracts/lz/SimpleLzApp.sol index c371282..e27761d 100644 --- a/contracts/lz/SimpleLzApp.sol +++ b/contracts/lz/SimpleLzApp.sol @@ -8,47 +8,34 @@ import "./interfaces/ILayerZeroEndpoint.sol"; /** * @title SimpleLzApp - * @notice Simple, blocking LayerZero-enabled contract that only has one trusted remote chainId and a hardcoded - * module contracts + * @notice LayerZero-enabled contract that has only one trusted remote chain and sends messages cross-chain (does NOT + * receive) to remote contracts provided by the concrete class. + * NOTE: this contract is ownable only to set layerzero configs. */ abstract contract SimpleLzApp is Owned, ILayerZeroUserApplicationConfig { error NotZeroAddress(); - ILayerZeroEndpoint public immutable lzEndpoint; + ILayerZeroEndpoint public immutable lzEndpoint; // lz endpoint contract deployed on this chain - uint16 public remoteChainId; + uint16 public remoteChainId; // lz chain id + address public zroPaymentAddress; // the address of the ZRO token holder who would pay for all transactions /** * @dev contract constructor - * @param _lzEndpoint: The LZ endpoint contract deployed on this chain + * @param _lzEndpoint: the lz endpoint contract deployed on this chain + * @param _owner: the Contract owner * @param _remoteChainId: remote chain id to be set as the trusted remote */ - constructor(address _lzEndpoint, address owner, uint16 _remoteChainId) Owned(owner) { + constructor(address _lzEndpoint, address _owner, uint16 _remoteChainId) Owned(_owner) { if (_lzEndpoint == address(0)) { revert NotZeroAddress(); } lzEndpoint = ILayerZeroEndpoint(_lzEndpoint); - remoteChainId = _remoteChainId; } - function _lzSend( - bytes memory _remoteContract, - bytes memory _payload, - address payable _refundAddress, - address _zroPaymentAddress, - bytes memory _adapterParams - ) internal virtual { - lzEndpoint.send{value: msg.value}( - remoteChainId, - _remoteContract, - _payload, - _refundAddress, - _zroPaymentAddress, - _adapterParams - ); - } - - // @dev generic config for LayerZero user Application + /** + * @notice generic config for LayerZero user application + */ function setConfig( uint16 _version, uint16 _chainId, @@ -58,19 +45,61 @@ abstract contract SimpleLzApp is Owned, ILayerZeroUserApplicationConfig { lzEndpoint.setConfig(_version, _chainId, _configType, _config); } + /** + * @notice allows the contract owner to set the lz config for send version + */ function setSendVersion(uint16 _version) external override onlyOwner { lzEndpoint.setSendVersion(_version); } + /** + * @notice allows the contract owner to set the lz config for receive version + */ function setReceiveVersion(uint16 _version) external override onlyOwner { lzEndpoint.setReceiveVersion(_version); } + /** + * @notice allows the contract owner to set the `_zroPaymentAddress` responsible for paying all transactions in ZRO + */ + function setZroPaymentAddress(address _zroPaymentAddress) external onlyOwner { + zroPaymentAddress = _zroPaymentAddress; + } + + /** + * @notice allows the contract owner to unblock the queue of messages + */ function forceResumeReceive(uint16 _srcChainId, bytes calldata _srcAddress) external override onlyOwner { lzEndpoint.forceResumeReceive(_srcChainId, _srcAddress); } + /** + * @notice returns the lz config for this contract + */ function getConfig(uint16 _version, uint16 _chainId, address, uint _configType) external view returns (bytes memory) { return lzEndpoint.getConfig(_version, _chainId, address(this), _configType); } + + /** + * @dev sends a cross-chain message to the lz endpoint contract deployed on this chain, to be relayed + * @param _remoteContract: the trusted contract on the remote chain to receive the message + * @param _payload: the actual message to be relayed + * @param _refundAddress: the address on this chain to receive the refund - excess paid for gas + * @param _adapterParams: the custom adapter params to use in sending this message + */ + function _lzSend( + bytes memory _remoteContract, + bytes memory _payload, + address payable _refundAddress, + bytes memory _adapterParams + ) internal virtual { + lzEndpoint.send{value: msg.value}( + remoteChainId, + _remoteContract, + _payload, + _refundAddress, + zroPaymentAddress, + _adapterParams + ); + } } diff --git a/test/LZGatedFollowModule.spec.ts b/test/LZGatedFollowModule.spec.ts index 15b04c3..3610073 100644 --- a/test/LZGatedFollowModule.spec.ts +++ b/test/LZGatedFollowModule.spec.ts @@ -172,7 +172,7 @@ makeSuiteCleanRoom('LZGatedFollowModule', function () { const res = await followModule.gatedFollowPerProfile(FIRST_PROFILE_ID); expect(res.balanceThreshold.toNumber()).to.equal(BALANCE_THRESHOLD); - expect(res.remoteContract).to.equal(erc721.address); + expect(res.tokenContract).to.equal(erc721.address); expect(res.remoteChainId).to.equal(REMOTE_CHAIN_ID); }); diff --git a/test/LZGatedReferenceModule.spec.ts b/test/LZGatedReferenceModule.spec.ts index 60a2dd3..4f31ca4 100644 --- a/test/LZGatedReferenceModule.spec.ts +++ b/test/LZGatedReferenceModule.spec.ts @@ -191,7 +191,7 @@ makeSuiteCleanRoom('LZGatedReferenceModule', function () { const res = await referenceModule.gatedReferencedDataPerPub(FIRST_PROFILE_ID, 1); expect(res.balanceThreshold.toNumber()).to.equal(BALANCE_THRESHOLD); - expect(res.remoteContract).to.equal(erc721.address); + expect(res.tokenContract).to.equal(erc721.address); expect(res.remoteChainId).to.equal(REMOTE_CHAIN_ID); }); From ec70328ee3b22e09550d965beded31a15815325a Mon Sep 17 00:00:00 2001 From: Carlos Beltran Date: Mon, 12 Dec 2022 14:47:33 -0600 Subject: [PATCH 07/14] add: lz gated proxy test file --- test/LZGatedProxy.spec.ts | 97 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 test/LZGatedProxy.spec.ts diff --git a/test/LZGatedProxy.spec.ts b/test/LZGatedProxy.spec.ts new file mode 100644 index 0000000..5955ca8 --- /dev/null +++ b/test/LZGatedProxy.spec.ts @@ -0,0 +1,97 @@ +import '@nomiclabs/hardhat-ethers'; +import { expect } from 'chai'; +import { Signer, utils } from 'ethers'; +const { getContractAddress } = require('@ethersproject/address'); +import { ethers } from 'hardhat'; +import { ZERO_ADDRESS } from './lens/helpers/constants'; +import { + LZGatedFollowModule, + LZGatedFollowModule__factory, + LZGatedReferenceModule, + LZGatedReferenceModule__factory, + LZGatedProxy, + LZGatedProxy__factory, + LZEndpointMock, + LZEndpointMock__factory, +} from '../typechain-types' + +const REMOTE_CHAIN_ID = 123; +const CONFIG_TYPE_INBOUND_PROOF_LIBRARY_VERSION = 1; + +describe('LZGatedProxy', () => { + let lzGatedProxy: LZGatedProxy; + let lzEndpoint: LZEndpointMock; + let followModule: LZGatedFollowModule; + let referenceModule: LZGatedReferenceModule; + let deployer: Signer, user: Signer, userTwo: Signer, userThree: Signer; + let deployerAddress: string, userAddress: string, userTwoAddress: string; + + beforeEach(async () => { + ([deployer, user, userTwo, userThree] = await ethers.getSigners()); + deployerAddress = await deployer.getAddress(); + userAddress = await user.getAddress(); + userTwoAddress = await userTwo.getAddress(); + + lzEndpoint = await new LZEndpointMock__factory(deployer).deploy(REMOTE_CHAIN_ID); + const transactionCount = await deployer.getTransactionCount(); + const followModuleAddress = getContractAddress({ from: deployerAddress, nonce: transactionCount + 1 }); + const referenceModuleAddress = getContractAddress({ from: deployerAddress, nonce: transactionCount + 2 }); + + lzGatedProxy = await new LZGatedProxy__factory(deployer).deploy( + lzEndpoint.address, + REMOTE_CHAIN_ID, + followModuleAddress, + referenceModuleAddress, // _remoteReferenceModule + ZERO_ADDRESS // _remoteCollectModule + ); + followModule = await new LZGatedFollowModule__factory(deployer).deploy( + lzGatedProxy.address, // lensHub does not matter + lzEndpoint.address, + [REMOTE_CHAIN_ID], + [lzGatedProxy.address] + ); + referenceModule = await new LZGatedReferenceModule__factory(deployer).deploy( + lzGatedProxy.address, // lensHub does not matter + lzEndpoint.address, + [REMOTE_CHAIN_ID], + [lzGatedProxy.address] + ); + + // use same lz endpoint mock + await lzEndpoint.setDestLzEndpoint(referenceModule.address, lzEndpoint.address); + await lzEndpoint.setDestLzEndpoint(followModule.address, lzEndpoint.address); + await lzEndpoint.setDestLzEndpoint(lzGatedProxy.address, lzEndpoint.address); + }); + + describe('#constructor', () => { + it('reverts when the _lzEndpoint arg is the null address', async () => { + expect( + new LZGatedProxy__factory(deployer).deploy( + ZERO_ADDRESS, + REMOTE_CHAIN_ID, + followModule.address, + referenceModule.address, + ZERO_ADDRESS + ) + ).to.be.revertedWith('NotZeroAddress'); + }); + + it('sets storage', async () => { + const owner = await lzGatedProxy.owner(); + const endpoint = await lzGatedProxy.lzEndpoint(); + const followModuleAddress = await lzGatedProxy.remoteFollowModule(); + const remoteReferenceModule = await lzGatedProxy.remoteReferenceModule(); + + expect(owner).to.equal(deployerAddress); + expect(endpoint).to.equal(lzEndpoint.address); + expect(utils.getAddress(followModuleAddress)).to.equal(followModule.address); + expect(utils.getAddress(remoteReferenceModule)).to.equal(referenceModule.address); + }); + }); + + describe.skip('#relayFollowWithSig (part of LZGatedFollowModule.spec.ts)', () => {}); + + describe.skip('#relayCommentWithSig (part of LZGatedReferenceModule.spec.ts)', () => {}); + + describe.skip('#relayMirrorWithSig (part of LZGatedReferenceModule.spec.ts)', () => {}); +}); From 461408f2ce0f3323b2249889d5bed82e1299f65c Mon Sep 17 00:00:00 2001 From: Carlos Beltran Date: Mon, 12 Dec 2022 16:11:54 -0600 Subject: [PATCH 08/14] add: collect module + tests --- contracts/LZGatedCollectModule.sol | 148 +++++++++++ contracts/LZGatedFollowModule.sol | 6 +- contracts/LZGatedProxy.sol | 66 ++--- contracts/LZGatedReferenceModule.sol | 17 +- test/LZGatedCollectModule.spec.ts | 350 +++++++++++++++++++++++++++ test/LZGatedReferenceModule.spec.ts | 2 +- test/lens/helpers/utils.ts | 38 +++ 7 files changed, 587 insertions(+), 40 deletions(-) create mode 100644 contracts/LZGatedCollectModule.sol create mode 100644 test/LZGatedCollectModule.spec.ts diff --git a/contracts/LZGatedCollectModule.sol b/contracts/LZGatedCollectModule.sol new file mode 100644 index 0000000..305ce66 --- /dev/null +++ b/contracts/LZGatedCollectModule.sol @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.10; + +import {ModuleBase, Errors} from "@aave/lens-protocol/contracts/core/modules/ModuleBase.sol"; +import {ICollectModule} from '@aave/lens-protocol/contracts/interfaces/ICollectModule.sol'; +import {FollowValidationModuleBase} from '@aave/lens-protocol/contracts/core/modules/FollowValidationModuleBase.sol'; +import {ILensHub} from "@aave/lens-protocol/contracts/interfaces/ILensHub.sol"; +import {DataTypes} from "@aave/lens-protocol/contracts/libraries/DataTypes.sol"; +import {LzApp} from "./lz/LzApp.sol"; + +/** + * @title LZGatedCollectModule + * + * @notice A Lens Collect Module that allows profiles to gate who can collect their post with ERC20 or ERC721 balances + * held on other chains. + */ +contract LZGatedCollectModule is FollowValidationModuleBase, ICollectModule, LzApp { + struct GatedCollectData { + address tokenContract; // the remote contract to read from + uint256 balanceThreshold; // result of balanceOf() should be greater than or equal to + uint16 remoteChainId; // the remote chainId to read against + } + + event InitCollectModule( + uint256 indexed profileId, + uint256 indexed pubId, + address tokenContract, + uint256 balanceThreshold, + uint16 chainId + ); + event MessageFailed(uint16 _srcChainId, bytes _srcAddress, uint64 _nonce, bytes _payload, string _reason); + + error NotAccepting(); + + mapping (uint256 => mapping (uint256 => GatedCollectData)) public gatedCollectDataPerPub; // profileId => pubId => gated collect data + mapping (uint256 => mapping (uint256 => mapping (address => bool))) public validatedCollectors; // profileIdPointed => pubId => profiles which have been validated + + /** + * @dev contract constructor + * @param hub LensHub + * @param _lzEndpoint: LayerZero endpoint on this chain to relay messages + * @param remoteChainIds: whitelisted destination chain ids (supported by LayerZero) + * @param remoteProxies: proxy destination contracts (deployed by us) + */ + constructor( + address hub, + address _lzEndpoint, + uint16[] memory remoteChainIds, + bytes[] memory remoteProxies + ) ModuleBase(hub) LzApp(_lzEndpoint, msg.sender, remoteChainIds, remoteProxies) {} + + /** + * @notice Initialize this collect module for the given profile/publication + * + * @param profileId The profile ID of the profile creating the pub + * @param pubId The pub to init this reference module to + * @param data The arbitrary data parameter, which in this particular module initialization will be just ignored. + * + * @return bytes Empty bytes. + */ + function initializePublicationCollectModule( + uint256 profileId, + uint256 pubId, + bytes calldata data + ) external override onlyHub returns (bytes memory) { + ( + address tokenContract, + uint256 balanceThreshold, + uint16 chainId + ) = abi.decode(data, (address, uint256, uint16)); + + if (address(tokenContract) == address(0) || _lzRemoteLookup[chainId].length == 0) { + revert Errors.InitParamsInvalid(); + } + + // anyone can read this data before attempting to follow the given profile + gatedCollectDataPerPub[profileId][pubId] = GatedCollectData({ + remoteChainId: chainId, + tokenContract: tokenContract, + balanceThreshold: balanceThreshold + }); + + emit InitCollectModule(profileId, pubId, tokenContract, balanceThreshold, chainId); + + return new bytes(0); + } + + /** + * @dev Process a collect by: + * - checking that we have already validated the collector through our `LZGatedProxy` on a remote chain + */ + function processCollect( + uint256, // referrerProfileId + address collector, + uint256 profileId, + uint256 pubId, + bytes calldata // data + ) external view override onlyHub { + if (!validatedCollectors[profileId][pubId][collector]) { + revert Errors.CollectNotAllowed(); + } + } + + /** + * @dev not accepting native tokens + */ + receive() external payable { revert NotAccepting(); } + + /** + * @dev Callback from our `LZGatedProxy` contract deployed on a remote chain, signals that the collect is validated + * NOTE: this function is actually non-blocking in that it does not explicitly revert and catches external errors + */ + function _blockingLzReceive( + uint16 _srcChainId, + bytes memory _srcAddress, + uint64 _nonce, + bytes memory _payload + ) internal override { + ( + address token, + address collector, + uint256 profileId, + uint256 pubId, + uint256 threshold, + DataTypes.CollectWithSigData memory collectSig + ) = abi.decode(_payload, (address, address, uint256, uint256, uint256, DataTypes.CollectWithSigData)); + + GatedCollectData memory data = gatedCollectDataPerPub[profileId][pubId]; + + // validate that remote check was against the contract/threshold defined + if (data.remoteChainId != _srcChainId || data.balanceThreshold != threshold || data.tokenContract != token) { + emit MessageFailed(_srcChainId, _srcAddress, _nonce, _payload, 'InvalidRemoteInput'); + return; + } + + // @TODO: hash the vars vs deeply nested? + validatedCollectors[profileId][pubId][collector] = true; + + // use the signature to execute the collect + try ILensHub(HUB).collectWithSig(collectSig) {} + catch Error (string memory reason) { + emit MessageFailed(_srcChainId, _srcAddress, _nonce, _payload, reason); + } + + delete validatedCollectors[profileId][pubId][collector]; + } +} diff --git a/contracts/LZGatedFollowModule.sol b/contracts/LZGatedFollowModule.sol index 01bf76d..6ffa3cc 100644 --- a/contracts/LZGatedFollowModule.sol +++ b/contracts/LZGatedFollowModule.sol @@ -13,7 +13,7 @@ import {LzApp} from "./lz/LzApp.sol"; /** * @title LZGatedFollowModule * - * @notice A Lens Follow Module that allows profile holders to gate their following with ERC20 or ERC721 balances held + * @notice A Lens Follow Module that allows profiles to gate their following with ERC20 or ERC721 balances held * on other chains. */ contract LZGatedFollowModule is FollowValidatorFollowModuleBase, LzApp { @@ -23,11 +23,11 @@ contract LZGatedFollowModule is FollowValidatorFollowModuleBase, LzApp { uint16 remoteChainId; // the remote chainId to read against } - error NotAccepting(); - event InitFollowModule(uint256 indexed profileId, address tokenContract, uint256 balanceThreshold, uint16 chainId); event MessageFailed(uint16 _srcChainId, bytes _srcAddress, uint64 _nonce, bytes _payload, string _reason); + error NotAccepting(); + mapping (uint256 => GatedFollowData) public gatedFollowPerProfile; // profileId => gated follow data mapping (uint256 => mapping (address => bool)) public validatedFollowers; // profileId => address which has been validated diff --git a/contracts/LZGatedProxy.sol b/contracts/LZGatedProxy.sol index 0f9fdeb..4fbd150 100644 --- a/contracts/LZGatedProxy.sol +++ b/contracts/LZGatedProxy.sol @@ -77,7 +77,7 @@ contract LZGatedProxy is SimpleLzApp { * @notice validate a token balance on this chain before relaying the intent to comment on a Lens post on the remote * chain. * NOTE: callers of this function MUST pass the exact values for `tokenContract` and `balanceThreshold` returned from - * the call to LZGatedReferenceModule.gatedReferencedDataPerPub(profileIdPointed, pubIdPointed) - or the transaction + * the call to LZGatedReferenceModule.gatedReferenceDataPerPub(profileIdPointed, pubIdPointed) - or the transaction * on the remote chain WILL revert. * @param sender: the account wishing to perform the comment action * @param profileId: the id of the profile wishing to comment @@ -118,7 +118,7 @@ contract LZGatedProxy is SimpleLzApp { * @notice validate a token balance on this chain before relaying the intent to mirror a Lens post on the remote * chain. * NOTE: callers of this function MUST pass the exact values for `tokenContract` and `balanceThreshold` returned from - * the call to LZGatedReferenceModule.gatedReferencedDataPerPub(profileIdPointed, pubIdPointed) - or the transaction + * the call to LZGatedReferenceModule.gatedReferenceDataPerPub(profileIdPointed, pubIdPointed) - or the transaction * on the remote chain WILL revert. * @param sender: the account wishing to perform the mirror action * @param profileId: the id of the profile wishing to mirror @@ -155,31 +155,43 @@ contract LZGatedProxy is SimpleLzApp { ); } - // @TODO - // function relayCollectWithSig( - // address collector, - // uint256 profileId, - // uint256 pubId, - // address tokenContract, - // uint256 balanceThreshold, - // DataTypes.CollectWithSigData memory collectSig - // ) external { - // if (!_checkThreshold(collector, tokenContract, balanceThreshold)) { revert InsufficientBalance(); } - // - // _lzSend( - // remoteCollectModule, - // abi.encode( - // tokenContract, - // collector, - // profileId, - // pubId, - // balanceThreshold, - // collectSig - // ), - // payable(msg.sender), - // bytes("") - // ); - // } + /** + * @notice validate a token balance on this chain before relaying the intent to collect a Lens post on the remote + * chain. + * NOTE: callers of this function MUST pass the exact values for `tokenContract` and `balanceThreshold` returned from + * the call to LZGatedCollectModule.gatedCollectDataPerPub(profileId, pubId) - or the transaction + * on the remote chain WILL revert. + * @param collector: the account wishing to perform the collect action + * @param profileId: the id of the profile wishing to collect + * @param pubId: the id of the post + * @param tokenContract: the ERC20/ERC721 contract set by the `profileId` to check a balance against + * @param balanceThreshold: the amount of tokens required in order for a successful follow + * @param collectSig: the collect signature expected by the LensHub + */ + function relayCollectWithSig( + address collector, + uint256 profileId, + uint256 pubId, + address tokenContract, + uint256 balanceThreshold, + DataTypes.CollectWithSigData memory collectSig + ) external { + if (!_checkThreshold(collector, tokenContract, balanceThreshold)) { revert InsufficientBalance(); } + + _lzSend( + remoteCollectModule, + abi.encode( + tokenContract, + collector, + profileId, + pubId, + balanceThreshold, + collectSig + ), + payable(msg.sender), + bytes("") + ); + } /** * @dev not accepting native tokens diff --git a/contracts/LZGatedReferenceModule.sol b/contracts/LZGatedReferenceModule.sol index 8fb2fd1..ecf65f2 100644 --- a/contracts/LZGatedReferenceModule.sol +++ b/contracts/LZGatedReferenceModule.sol @@ -18,21 +18,20 @@ import "hardhat/console.sol"; * ERC20 or ERC721 balances held on other chains. */ contract LZGatedReferenceModule is FollowValidationModuleBase, IReferenceModule, LzApp { - error NotAccepting(); - struct GatedReferenceData { address tokenContract; // the remote contract to read from uint256 balanceThreshold; // result of balanceOf() should be greater than or equal to uint16 remoteChainId; // the remote chainId to read against } - mapping (uint256 => mapping (uint256 => GatedReferenceData)) public gatedReferencedDataPerPub; // profileId => pubId => gated reference data - mapping (uint256 => mapping (uint256 => mapping (uint256 => bool))) public validatedReferencers; // profileIdPointed => pubId => profiles which have been validated - - event InitReferenceModule(uint256 indexed profileId, address tokenContract, uint256 balanceThreshold, uint16 chainId); + event InitReferenceModule(uint256 indexed profileId, uint256 indexed pubId, address tokenContract, uint256 balanceThreshold, uint16 chainId); event MessageFailed(uint16 _srcChainId, bytes _srcAddress, uint64 _nonce, bytes _payload, string _reason); error CommentOrMirrorInvalid(); + error NotAccepting(); + + mapping (uint256 => mapping (uint256 => GatedReferenceData)) public gatedReferenceDataPerPub; // profileId => pubId => gated reference data + mapping (uint256 => mapping (uint256 => mapping (uint256 => bool))) public validatedReferencers; // profileIdPointed => pubId => profiles which have been validated /** * @dev contract constructor @@ -74,13 +73,13 @@ contract LZGatedReferenceModule is FollowValidationModuleBase, IReferenceModule, } // anyone can read this data before attempting to follow the given profile - gatedReferencedDataPerPub[profileId][pubId] = GatedReferenceData({ + gatedReferenceDataPerPub[profileId][pubId] = GatedReferenceData({ remoteChainId: chainId, tokenContract: tokenContract, balanceThreshold: balanceThreshold }); - emit InitReferenceModule(profileId, tokenContract, balanceThreshold, chainId); + emit InitReferenceModule(profileId, pubId, tokenContract, balanceThreshold, chainId); return new bytes(0); } @@ -140,7 +139,7 @@ contract LZGatedReferenceModule is FollowValidationModuleBase, IReferenceModule, uint256 threshold, ) = abi.decode(_payload, (bool, address, uint256, uint256, uint256, uint256, bytes)); - GatedReferenceData memory data = gatedReferencedDataPerPub[profileIdPointed][pubIdPointed]; + GatedReferenceData memory data = gatedReferenceDataPerPub[profileIdPointed][pubIdPointed]; // validate that remote check was against the contract/threshold defined if (data.remoteChainId != _srcChainId || data.balanceThreshold != threshold || data.tokenContract != token) { diff --git a/test/LZGatedCollectModule.spec.ts b/test/LZGatedCollectModule.spec.ts new file mode 100644 index 0000000..7e7f414 --- /dev/null +++ b/test/LZGatedCollectModule.spec.ts @@ -0,0 +1,350 @@ +import '@nomiclabs/hardhat-ethers'; +import { expect } from 'chai'; +import { Signer, VoidSigner } from 'ethers'; +const { getContractAddress } = require('@ethersproject/address'); +import { ethers } from 'hardhat'; +import { + FIRST_PROFILE_ID, + MOCK_URI, + OTHER_MOCK_URI, + governance, + lensHub, + makeSuiteCleanRoom, + MOCK_FOLLOW_NFT_URI, + MOCK_PROFILE_HANDLE, + MOCK_PROFILE_URI, + user, + userTwo, + userThree, + testWallet, + freeCollectModule, +} from './lens/__setup.spec'; +import { ERRORS } from './lens/helpers/errors'; +import { getCollectWithSigParts } from './lens/helpers/utils'; +import { ZERO_ADDRESS, MAX_UINT256 } from './lens/helpers/constants'; +import { + LZGatedCollectModule, + LZGatedCollectModule__factory, + LZGatedProxy, + LZGatedProxy__factory, + LZEndpointMock, + LZEndpointMock__factory, + ERC721Mock, + ERC721Mock__factory, + ERC20Mock, + ERC20Mock__factory, +} from '../typechain-types' +import { abi } from './lens/abi/Events.json'; +import LZGatedCollectModuleABI from './../build/contracts/LZGatedCollectModule.sol/LZGatedCollectModule.json'; +import { parseLogsNested } from './utils/parseLogs'; + +const EMPTY_BYTES = '0x'; +const REMOTE_CHAIN_ID = 123; +const BALANCE_THRESHOLD = 1; + +makeSuiteCleanRoom('LZGatedCollectModule', function () { + let lzGatedProxy: LZGatedProxy; + let lzEndpoint: LZEndpointMock; + let collectModule: LZGatedCollectModule; + let erc721: ERC721Mock; + let erc20: ERC20Mock; + let deployer: Signer, user: Signer, userTwo: Signer, userThree: Signer; + let deployerAddress: string, userAddress: string, userTwoAddress: string; + + const getNonBlockingError = (receipt) => { + const logs = parseLogsNested(receipt, abi, LZGatedCollectModuleABI.abi); + const event = logs.find(({ name }) => name === 'MessageFailed'); + + return event.args._reason; + }; + + beforeEach(async () => { + ([deployer, user, userTwo, userThree] = await ethers.getSigners()); + deployerAddress = await deployer.getAddress(); + userAddress = await user.getAddress(); + userTwoAddress = await userTwo.getAddress(); + + lzEndpoint = await new LZEndpointMock__factory(deployer).deploy(REMOTE_CHAIN_ID); + const transactionCount = await deployer.getTransactionCount(); + const collectModuleAddress = getContractAddress({ from: deployerAddress, nonce: transactionCount + 1 }); + + lzGatedProxy = await new LZGatedProxy__factory(deployer).deploy( + lzEndpoint.address, + REMOTE_CHAIN_ID, + ZERO_ADDRESS, // _remoteFollowModule + ZERO_ADDRESS, // _remoteReferenceModule + collectModuleAddress + ); + collectModule = await new LZGatedCollectModule__factory(deployer).deploy( + lensHub.address, + lzEndpoint.address, + [REMOTE_CHAIN_ID], + [lzGatedProxy.address] + ); + erc721 = await new ERC721Mock__factory(deployer).deploy(); + erc20 = await new ERC20Mock__factory(deployer).deploy(); + + // use same lz endpoint mock + await lzEndpoint.setDestLzEndpoint(collectModule.address, lzEndpoint.address); + await lzEndpoint.setDestLzEndpoint(lzGatedProxy.address, lzEndpoint.address); + + await lensHub.connect(governance).whitelistCollectModule(collectModule.address, true); + + await lensHub.createProfile({ + to: userAddress, + handle: MOCK_PROFILE_HANDLE, + imageURI: MOCK_PROFILE_URI, + followModule: ZERO_ADDRESS, + followModuleInitData: [], + followNFTURI: MOCK_FOLLOW_NFT_URI, + }); + + await lensHub.createProfile({ + to: userTwoAddress, + handle: 'test.lens', + imageURI: MOCK_PROFILE_URI, + followModule: ZERO_ADDRESS, + followModuleInitData: [], + followNFTURI: MOCK_FOLLOW_NFT_URI, + }); + }); + + describe('#constructor', () => { + it('reverts when the hub arg is the null address', async () => { + expect( + new LZGatedCollectModule__factory(deployer).deploy(ZERO_ADDRESS, lzEndpoint.address, [], []) + ).to.be.revertedWith('InitParamsInvalid'); + }); + + it('sets storage', async () => { + const owner = await collectModule.owner(); + const endpoint = await collectModule.lzEndpoint(); + + expect(owner).to.equal(deployerAddress); + expect(endpoint).to.equal(lzEndpoint.address); + }); + }); + + describe('#initializePublicationCollectModule', () => { + it('reverts when the caller is not LensHub', async () => { + await expect( + collectModule.initializePublicationCollectModule(FIRST_PROFILE_ID, 1, EMPTY_BYTES) + ).to.be.revertedWith(ERRORS.NOT_HUB); + }); + + it('reverts when an invalid chain id is provided in the encoded data', async () => { + const collectModuleInitData = ethers.utils.defaultAbiCoder.encode( + ['address', 'uint256', 'uint16'], + [erc721.address, BALANCE_THRESHOLD, 12345] + ); + + await expect( + lensHub.post({ + profileId: FIRST_PROFILE_ID, + contentURI: MOCK_URI, + collectModule: collectModule.address, + collectModuleInitData, + referenceModule: ZERO_ADDRESS, + referenceModuleInitData: [], + }) + ).to.be.revertedWith(ERRORS.INIT_PARAMS_INVALID); + }); + + it('reverts when token contract as zero address is provided in the encoded data', async () => { + const collectModuleInitData = ethers.utils.defaultAbiCoder.encode( + ['address', 'uint256', 'uint16'], + [ZERO_ADDRESS, BALANCE_THRESHOLD, REMOTE_CHAIN_ID] + ); + + await expect( + lensHub.post({ + profileId: FIRST_PROFILE_ID, + contentURI: MOCK_URI, + collectModule: collectModule.address, + collectModuleInitData, + referenceModule: ZERO_ADDRESS, + referenceModuleInitData: [], + }) + ).to.be.revertedWith(ERRORS.INIT_PARAMS_INVALID); + }); + + context('context: with valid params', () => { + let tx; + + beforeEach(async() => { + const collectModuleInitData = ethers.utils.defaultAbiCoder.encode( + ['address', 'uint256', 'uint16'], + [erc721.address, BALANCE_THRESHOLD, REMOTE_CHAIN_ID] + ); + tx = await lensHub.post({ + profileId: FIRST_PROFILE_ID, + contentURI: MOCK_URI, + collectModule: collectModule.address, + collectModuleInitData, + referenceModule: ZERO_ADDRESS, + referenceModuleInitData: [], + }); + }); + + it('sets storage', async () => { + const res = await collectModule.gatedCollectDataPerPub(FIRST_PROFILE_ID, 1); + + expect(res.balanceThreshold.toNumber()).to.equal(BALANCE_THRESHOLD); + expect(res.tokenContract).to.equal(erc721.address); + expect(res.remoteChainId).to.equal(REMOTE_CHAIN_ID); + }); + + it('emits an event', async () => { + const logs = parseLogsNested(await tx.wait(), abi, LZGatedCollectModuleABI.abi); + const event = logs.find(({ name }) => name === 'InitCollectModule'); + + expect(event).not.to.equal(undefined); + expect(event.args.profileId.toNumber()).to.equal(FIRST_PROFILE_ID); + expect(event.args.tokenContract).to.equal(erc721.address); + expect(event.args.chainId).to.equal(REMOTE_CHAIN_ID); + }); + }); + }); + + describe('#processCollect (triggered from LZGatedProxy#relayCollectWithSig)', () => { + let collectWithSigData; + let collectModuleInitData; + + beforeEach(async() => { + collectModuleInitData = ethers.utils.defaultAbiCoder.encode( + ['address', 'uint256', 'uint16'], + [erc721.address, BALANCE_THRESHOLD, REMOTE_CHAIN_ID] + ); + + await lensHub.post({ + profileId: FIRST_PROFILE_ID, + contentURI: MOCK_URI, + collectModule: collectModule.address, + collectModuleInitData, + referenceModule: ZERO_ADDRESS, + referenceModuleInitData: [], + }); + + const nonce = (await lensHub.sigNonces(userTwoAddress)).toNumber(); + const { v, r, s } = await getCollectWithSigParts( + userTwo as VoidSigner, + FIRST_PROFILE_ID, + '1', + [], + nonce, + MAX_UINT256 + ); + + // userTwo signs that they would like to collect user's first post + collectWithSigData = { + collector: userTwoAddress, + profileId: FIRST_PROFILE_ID, + pubId: '1', + data: [], + sig: { + v, + r, + s, + deadline: MAX_UINT256, + }, + }; + }); + + it('reverts if called without going through lzGatedProxy', async () => { + await expect( + lensHub.collectWithSig(collectWithSigData) + ).to.be.revertedWith('CollectNotAllowed()'); + }); + + it('reverts if the caller does not have suffient balance', async () => { + await expect( + lzGatedProxy + .connect(userTwo) + .relayCollectWithSig( + userTwoAddress, + FIRST_PROFILE_ID, + '1', + erc721.address, + BALANCE_THRESHOLD, + collectWithSigData + ) + ).to.be.revertedWith('InsufficientBalance'); + }); + + it('reverts if the contract call for balanceOf() fails', async () => { + await expect( + lzGatedProxy + .connect(userTwo) + .relayCollectWithSig( + userTwoAddress, + FIRST_PROFILE_ID, + '1', + lzEndpoint.address, + BALANCE_THRESHOLD, + collectWithSigData + ) + ).to.be.revertedWith('InsufficientBalance'); + }); + + it('[non-blocking] fails if the caller passed an invalid threshold', async () => { + await erc721.safeMint(userTwoAddress); + + const tx = await lzGatedProxy + .connect(userTwo) + .relayCollectWithSig( + userTwoAddress, + FIRST_PROFILE_ID, + '1', + erc721.address, + 0, + collectWithSigData + ); + + const messageFailedReason = getNonBlockingError(await tx.wait()); + expect(messageFailedReason).to.equal('InvalidRemoteInput'); + }); + + + it('[non-blocking] fails if the caller passed an invalid token contract', async () => { + await erc20.mint(userTwoAddress, BALANCE_THRESHOLD); + + const tx = await lzGatedProxy + .connect(userTwo) + .relayCollectWithSig( + userTwoAddress, + FIRST_PROFILE_ID, + '1', + erc20.address, + BALANCE_THRESHOLD, + collectWithSigData + ); + + const messageFailedReason = getNonBlockingError(await tx.wait()); + expect(messageFailedReason).to.equal('InvalidRemoteInput'); + }); + + it('processes a valid collect', async () => { + await erc721.safeMint(userTwoAddress); + + const tx = await lzGatedProxy + .connect(userTwo) + .relayCollectWithSig( + userTwoAddress, + FIRST_PROFILE_ID, + '1', + erc721.address, + BALANCE_THRESHOLD, + collectWithSigData + ); + const logs = parseLogsNested(await tx.wait(), abi, LZGatedCollectModuleABI.abi); + + const event = logs.find(({ name }) => name === 'MessageFailed'); + const collectEvent = logs.find(({ name }) => name === 'Collected'); + + expect(event).to.equal(undefined); + expect(collectEvent).not.to.equal(undefined); + expect(collectEvent.args.rootPubId.toNumber()).to.equal(1); + expect(collectEvent.args.rootProfileId.toNumber()).to.equal(1); + }); + }); +}); diff --git a/test/LZGatedReferenceModule.spec.ts b/test/LZGatedReferenceModule.spec.ts index 4f31ca4..e2160df 100644 --- a/test/LZGatedReferenceModule.spec.ts +++ b/test/LZGatedReferenceModule.spec.ts @@ -188,7 +188,7 @@ makeSuiteCleanRoom('LZGatedReferenceModule', function () { }); it('sets storage', async () => { - const res = await referenceModule.gatedReferencedDataPerPub(FIRST_PROFILE_ID, 1); + const res = await referenceModule.gatedReferenceDataPerPub(FIRST_PROFILE_ID, 1); expect(res.balanceThreshold.toNumber()).to.equal(BALANCE_THRESHOLD); expect(res.tokenContract).to.equal(erc721.address); diff --git a/test/lens/helpers/utils.ts b/test/lens/helpers/utils.ts index a5d44f2..97f895d 100644 --- a/test/lens/helpers/utils.ts +++ b/test/lens/helpers/utils.ts @@ -269,6 +269,44 @@ const buildFollowWithSigParams = ( }, }); +export async function getCollectWithSigParts( + wallet: VoidSigner, + profileId: BigNumberish, + pubId: string, + data: Bytes | string, + nonce: number, + deadline: string +): Promise<{ v: number; r: string; s: string }> { + const msgParams = buildCollectWithSigParams(profileId, pubId, data, nonce, deadline); + return await getSig(wallet, msgParams); +} + +const buildCollectWithSigParams = ( + profileId: BigNumberish, + pubId: string, + data: Bytes | string, + nonce: number, + deadline: string +) => ({ + types: { + CollectWithSig: [ + { name: 'profileId', type: 'uint256' }, + { name: 'pubId', type: 'uint256' }, + { name: 'data', type: 'bytes' }, + { name: 'nonce', type: 'uint256' }, + { name: 'deadline', type: 'uint256' }, + ], + }, + domain: domain(), + value: { + profileId: profileId, + pubId: pubId, + data: data, + nonce: nonce, + deadline: deadline, + }, +}); + async function getSig(wallet: VoidSigner, msgParams: { domain: any; types: any; From 6c75fb3c502ec53fb01be6d68f9102f67304135a Mon Sep 17 00:00:00 2001 From: Carlos Beltran Date: Mon, 12 Dec 2022 16:20:09 -0600 Subject: [PATCH 09/14] update: LZGatedProxy.spec.ts --- test/LZGatedProxy.spec.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/test/LZGatedProxy.spec.ts b/test/LZGatedProxy.spec.ts index 5955ca8..ea93d72 100644 --- a/test/LZGatedProxy.spec.ts +++ b/test/LZGatedProxy.spec.ts @@ -9,6 +9,8 @@ import { LZGatedFollowModule__factory, LZGatedReferenceModule, LZGatedReferenceModule__factory, + LZGatedCollectModule, + LZGatedCollectModule__factory, LZGatedProxy, LZGatedProxy__factory, LZEndpointMock, @@ -23,6 +25,7 @@ describe('LZGatedProxy', () => { let lzEndpoint: LZEndpointMock; let followModule: LZGatedFollowModule; let referenceModule: LZGatedReferenceModule; + let collectModule: LZGatedCollectModule; let deployer: Signer, user: Signer, userTwo: Signer, userThree: Signer; let deployerAddress: string, userAddress: string, userTwoAddress: string; @@ -36,13 +39,14 @@ describe('LZGatedProxy', () => { const transactionCount = await deployer.getTransactionCount(); const followModuleAddress = getContractAddress({ from: deployerAddress, nonce: transactionCount + 1 }); const referenceModuleAddress = getContractAddress({ from: deployerAddress, nonce: transactionCount + 2 }); + const collectModuleAddress = getContractAddress({ from: deployerAddress, nonce: transactionCount + 3 }); lzGatedProxy = await new LZGatedProxy__factory(deployer).deploy( lzEndpoint.address, REMOTE_CHAIN_ID, followModuleAddress, - referenceModuleAddress, // _remoteReferenceModule - ZERO_ADDRESS // _remoteCollectModule + referenceModuleAddress, + collectModuleAddress ); followModule = await new LZGatedFollowModule__factory(deployer).deploy( lzGatedProxy.address, // lensHub does not matter @@ -56,10 +60,17 @@ describe('LZGatedProxy', () => { [REMOTE_CHAIN_ID], [lzGatedProxy.address] ); + collectModule = await new LZGatedCollectModule__factory(deployer).deploy( + lzGatedProxy.address, // lensHub does not matter + lzEndpoint.address, + [REMOTE_CHAIN_ID], + [lzGatedProxy.address] + ); // use same lz endpoint mock await lzEndpoint.setDestLzEndpoint(referenceModule.address, lzEndpoint.address); await lzEndpoint.setDestLzEndpoint(followModule.address, lzEndpoint.address); + await lzEndpoint.setDestLzEndpoint(collectModule.address, lzEndpoint.address); await lzEndpoint.setDestLzEndpoint(lzGatedProxy.address, lzEndpoint.address); }); @@ -81,11 +92,13 @@ describe('LZGatedProxy', () => { const endpoint = await lzGatedProxy.lzEndpoint(); const followModuleAddress = await lzGatedProxy.remoteFollowModule(); const remoteReferenceModule = await lzGatedProxy.remoteReferenceModule(); + const remoteCollectModule = await lzGatedProxy.remoteCollectModule(); expect(owner).to.equal(deployerAddress); expect(endpoint).to.equal(lzEndpoint.address); expect(utils.getAddress(followModuleAddress)).to.equal(followModule.address); expect(utils.getAddress(remoteReferenceModule)).to.equal(referenceModule.address); + expect(utils.getAddress(remoteCollectModule)).to.equal(collectModule.address); }); }); @@ -94,4 +107,6 @@ describe('LZGatedProxy', () => { describe.skip('#relayCommentWithSig (part of LZGatedReferenceModule.spec.ts)', () => {}); describe.skip('#relayMirrorWithSig (part of LZGatedReferenceModule.spec.ts)', () => {}); + + describe.skip('#relayCollectWithSig (part of LZGatedCollectModule.spec.ts)', () => {}); }); From 6a30338b5bd56b8e7757fec0c3a4b28edcb421d7 Mon Sep 17 00:00:00 2001 From: Carlos Beltran Date: Tue, 13 Dec 2022 22:05:37 -0600 Subject: [PATCH 10/14] wip - estimate fee for gated follow --- README.md | 41 +- hardhat.config.ts | 14 +- package.json | 3 +- scripts/utils/goerli-contracts.json | 3 + scripts/utils/mumbai-contracts.json | 5 +- scripts/utils/optimismTestnet-contracts.json | 3 + tasks/gated-modules/deploy-modules-source.ts | 56 + tasks/gated-modules/deploy-proxy-remote.ts | 31 + tasks/gated-modules/estimate-fee-follow.ts | 115 + tasks/gated-modules/set-trusted-remotes.ts | 44 + tasks/helpers/abi/LensHub.json | 2609 ++++++++++++++++++ tasks/helpers/constants.ts | 27 + tasks/helpers/lens.ts | 16 +- tasks/stub-collect-post.ts | 2 +- test/lens/helpers/utils.ts | 8 +- yarn.lock | 5 + 16 files changed, 2963 insertions(+), 19 deletions(-) create mode 100644 scripts/utils/goerli-contracts.json create mode 100644 scripts/utils/optimismTestnet-contracts.json create mode 100644 tasks/gated-modules/deploy-modules-source.ts create mode 100644 tasks/gated-modules/deploy-proxy-remote.ts create mode 100644 tasks/gated-modules/estimate-fee-follow.ts create mode 100644 tasks/gated-modules/set-trusted-remotes.ts create mode 100644 tasks/helpers/abi/LensHub.json diff --git a/README.md b/README.md index f50cb4a..273f4b4 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,25 @@ - a creator attaches this module to one of their posts and specifies parameters such as the destination chain id - any user that collects the post gets an OmniSBT minted on the destination chain +`LZGatedFollowModule` is a Lens Follow Module that allows profile holders to gate their following with ERC20 or ERC721 balances held on other chains +- a profile initializes this module with a supported LayerZero chain id and a ERC20/ERC721 contract + balance to check against +- another user wishes to follow, they read the requirements using `gatedFollowPerProfile` which points to the chain/token/balance +- user generates `followWithSigData` for a given profile +- user (or relayer) submits this sig along with appropriate input to the `LZGatedProxy` contract on the other chain +- `LZGatedProxy` makes the token check, and relays the message to the `LZGatedFollowModule` on polygon +- `LZGatedFollowModule` contract validates the input, submits sig to `LensHub#followWithSig` +- follow ✅ + +`LZGatedReferenceModule` is a Lens Reference Module that allows publication creators to gate who can comment/mirror their post with ERC20 or ERC721 balances held on other chains. + +`LZGatedCollectModule` is a Lens Collect Module that allows publication creators to gate who can collect their post with ERC20 or ERC721 balances held on other chains. + +`LZGatedProxy.sol` acts acts as a proxy for `LZGatedFollowModule`, `LZGatedReferenceModule`, and `LZGatedCollectModule` in order to read token balances from remote contracts on any chain supported by LayerZero. +- we deploy one of these on any EVM chain to be supported by the modules, wire up for trusted LayerZero messaging +- when a user wishes to follow/comment/mirror/collect - they sign the correct data payload and submit along with the correct gate data +- the contract makes the token balance check against the ERC20/ERC721 contract defined +- if all good, relay the payload to our module contract on polygon + ## read the [docs](./blog/blog.md) ## setup + compile contracts @@ -33,18 +52,34 @@ yarn test yarn coverage ``` -## deploying contracts -We deploy our `OmniSBT` contract on the destination chain (`fantom_testnet`) and the source chain (`mumbai`), as well as setting trusted remotes on both ends. Finally, we stub the `FollowCampaignModule` to be the deployer address +## deploying contracts (OmniSBT + LZCollectModule) +We deploy our `OmniSBT` contract on the destination chain (`fantom_testnet`) and the source chain (`mumbai`), as well as setting trusted remotes on both ends. Finally, we stub the `FollowCampaignModule` to be the deployer address. All the lz config can be found under `tasks/helpers/constants.ts` 1. deploy `OmniSBT` contract on the destination chain `npx hardhat deploy-token-remote --network fantom_testnet` + 2. deploy `OmniSBT` contract on the source chain + set trusted remote `npx hardhat deploy-token-source --network mumbai` + 3. set trusted remote on the destination chain `npx hardhat set-trusted-remote --network fantom_testnet` -## stubbed transactions + tenderly infra +## deploying contracts (LZGated* modules) +We deploy our Lens modules on the source chain (`mumbai`) and we deploy our `LZGatedProxy` contract to all the remote chains we want to support (`goerli` and `optimismTestnet`). Finally, we set the trusted remotes on each module. All the lz config can be found under `tasks/helpers/constants.ts` +1. deploy our modules on the source chain `npx hardhat deploy-modules-source --network mumbai` +2. deploy our `LZGatedProxy` contract on all remote chains + + a. `npx hardhat deploy-proxy-remote --network goerli` + + b. `npx hardhat deploy-proxy-remote --network optimismTestnet` +3. set our trusted remotes on the source chain `npx hardhat set-trusted-remotes --network mumbai` + +## stubbed transactions + tenderly infra (OmniSBT + LZCollectModule) Considering that our `LZCollectModule` contract is what triggers mints of `OmniSBT` and it relies on lens module whitelisting - we can stub those transaction by setting the collect module to some permissioned address and triggering mints from an off-chain process 1. create a collection and set our lens testnet address as the collect module `npx hardhat stub-create-collection --network mumbai` + 2. create a post that we will listen for collects on https://testnet.lenster.xyz/ + 3. address the `@TODO` in `processCollected.ts` and deploy our tenderly action `cd tenderly && npm run deploy` - NOTE: in order for our action to process the `Collect` event off `lens-protocol`, we must add their `InteractionLogic` lib to our tenderly project. You can do so by clicking "Add to project" here: https://dashboard.tenderly.co/contract/mumbai/0xefd400326635e016cbfcc309725d5b62fd9d3468 + + 4. collect our post via lenster or by modifying our task `tasks/stub-collect-post.ts` diff --git a/hardhat.config.ts b/hardhat.config.ts index e9c0565..ddc6de3 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -95,11 +95,15 @@ const config = { chainId: 97, accounts: [process.env.DEPLOYER_PRIVATE_KEY] }, - // // not supported by LayerZero yet - // optimism_goerli: { - // url: process.env.ALCHEMY_OPTIMISM_GOERLI_URL, - // accounts: [process.env.DEPLOYER_PRIVATE_KEY] - // }, + goerli: { + url: process.env.ALCHEMY_GOERLI_URL, + accounts: [process.env.DEPLOYER_PRIVATE_KEY, ...lensTestWallets()] + }, + optimismTestnet: { + url: process.env.ALCHEMY_OPTIMISM_GOERLI_URL, + accounts: [process.env.DEPLOYER_PRIVATE_KEY, ...lensTestWallets()], + chainId: 420, + }, docker: { url: 'http://127.0.0.1:8545', accounts: lensTestWallets() diff --git a/package.json b/package.json index 4dc109c..1dcae51 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,8 @@ "@aave/lens-protocol": "^1.0.2", "@openzeppelin/contracts": "^4.3.2", "@rari-capital/solmate": "^6.4.0", - "@tenderly/hardhat-tenderly": "^1.1.5" + "@tenderly/hardhat-tenderly": "^1.1.5", + "promise-limit": "^2.7.0" }, "devDependencies": { "@nomiclabs/hardhat-ethers": "npm:hardhat-deploy-ethers", diff --git a/scripts/utils/goerli-contracts.json b/scripts/utils/goerli-contracts.json new file mode 100644 index 0000000..f30828a --- /dev/null +++ b/scripts/utils/goerli-contracts.json @@ -0,0 +1,3 @@ +{ + "LZGatedProxy": "0x9C2D31E3BB1a174D964cCFb33D3878b71C8555D8" +} \ No newline at end of file diff --git a/scripts/utils/mumbai-contracts.json b/scripts/utils/mumbai-contracts.json index 68c8fc4..737981a 100644 --- a/scripts/utils/mumbai-contracts.json +++ b/scripts/utils/mumbai-contracts.json @@ -1,3 +1,6 @@ { - "OmniSBT": "0x1a255753a44EEedbf6d3Afe65A8F006F8C513b0C" + "OmniSBT": "0x1a255753a44EEedbf6d3Afe65A8F006F8C513b0C", + "LZGatedFollowModule": "0x8cf0c18f335a5316359F310497870F99f4Ff9e10", + "LZGatedReferenceModule": "0x46720e2AC58f69d20A952F51AE73cFE0ae4a73b7", + "LZGatedCollectModule": "0x2f4A8D959fD7227d0942D220d3E8FDCECd757dC8" } \ No newline at end of file diff --git a/scripts/utils/optimismTestnet-contracts.json b/scripts/utils/optimismTestnet-contracts.json new file mode 100644 index 0000000..d8ee7d6 --- /dev/null +++ b/scripts/utils/optimismTestnet-contracts.json @@ -0,0 +1,3 @@ +{ + "LZGatedProxy": "0xd88a7966DD122Ece54a30454CF6de95E9022516b" +} \ No newline at end of file diff --git a/tasks/gated-modules/deploy-modules-source.ts b/tasks/gated-modules/deploy-modules-source.ts new file mode 100644 index 0000000..7d9a339 --- /dev/null +++ b/tasks/gated-modules/deploy-modules-source.ts @@ -0,0 +1,56 @@ +import { task } from 'hardhat/config'; +import { LZ_CONFIG_GATED_MODULES } from './../helpers/constants'; +import deployContract from './../helpers/deployContract'; +import { getLensHubDeployed } from './../helpers/lens'; +import getContract from './../helpers/getContract'; + +task('deploy-modules-source', 'deploys our Lens modules on the source chain').setAction(async ({}, hre) => { + const ethers = hre.ethers; + const networkName = hre.network.name; + const [deployer, _, governance] = await ethers.getSigners(); + + if (!LZ_CONFIG_GATED_MODULES[networkName]) throw new Error('invalid network'); + + // using the sandbox deployment for module whitelisting + const lensHub = await getLensHubDeployed('lensHub sandbox', governance, networkName); + + const followModule = await deployContract( + ethers, + networkName, + 'LZGatedFollowModule', + [lensHub.address, LZ_CONFIG_GATED_MODULES[networkName].endpoint, [], []] + ); + + const referenceModule = await deployContract( + ethers, + networkName, + 'LZGatedReferenceModule', + [lensHub.address, LZ_CONFIG_GATED_MODULES[networkName].endpoint, [], []] + ); + + const collectModule = await deployContract( + ethers, + networkName, + 'LZGatedCollectModule', + [lensHub.address, LZ_CONFIG_GATED_MODULES[networkName].endpoint, [], []] + ); + + // const followModule = await getContract(ethers, 'LZGatedFollowModule', deployer); + // const referenceModule = await getContract(ethers, 'LZGatedReferenceModule', deployer); + // const collectModule = await getContract(ethers, 'LZGatedCollectModule', deployer); + + // @TODO: might not need to whitelist in SANDBOX + + let tx; + console.log('lensHub.whitelistFollowModule()'); + tx = await lensHub.connect(governance).whitelistFollowModule(followModule.address, true, { gasLimit: 50000 }); + await tx.wait(); + + console.log('lensHub.whitelistReferenceModule()'); + tx = await lensHub.connect(governance).whitelistReferenceModule(referenceModule.address, true, { gasLimit: 50000 }); + await tx.wait(); + + console.log('lensHub.whitelistCollectModule()'); + tx = await lensHub.connect(governance).whitelistCollectModule(collectModule.address, true, { gasLimit: 50000 }); + await tx.wait(); +}); diff --git a/tasks/gated-modules/deploy-proxy-remote.ts b/tasks/gated-modules/deploy-proxy-remote.ts new file mode 100644 index 0000000..0b83af6 --- /dev/null +++ b/tasks/gated-modules/deploy-proxy-remote.ts @@ -0,0 +1,31 @@ +import { task } from 'hardhat/config'; +import { LZ_CONFIG_GATED_MODULES } from './../helpers/constants'; +import deployContract from './../helpers/deployContract'; +import { contractsDeployedOn } from './../../scripts/utils/migrations'; + +const SOURCE_NETWORK_NAME = 'mumbai'; // @TODO: flip to polygon + +task('deploy-proxy-remote', 'deploys LZGatedProxy on a remote chain').setAction(async ({}, hre) => { + const ethers = hre.ethers; + const networkName = hre.network.name; + const [deployer] = await ethers.getSigners(); + + if (!LZ_CONFIG_GATED_MODULES[networkName]) throw new Error('invalid network'); + + // modules deployed on the source chain + const contracts = contractsDeployedOn(SOURCE_NETWORK_NAME); + + console.log(`deploying LZGatedProxy on networkName: ${networkName}...`); + const lzGatedProxy = await deployContract( + ethers, + networkName, + 'LZGatedProxy', + [ + LZ_CONFIG_GATED_MODULES[networkName].endpoint, + LZ_CONFIG_GATED_MODULES[SOURCE_NETWORK_NAME].chainId, + contracts.LZGatedFollowModule, + contracts.LZGatedReferenceModule, + contracts.LZGatedCollectModule + ] + ); +}); diff --git a/tasks/gated-modules/estimate-fee-follow.ts b/tasks/gated-modules/estimate-fee-follow.ts new file mode 100644 index 0000000..ed86bf5 --- /dev/null +++ b/tasks/gated-modules/estimate-fee-follow.ts @@ -0,0 +1,115 @@ +import { task } from 'hardhat/config'; +import { Contract, utils, Bytes } from 'ethers'; +import { contractsDeployed } from './../../scripts/utils/migrations'; +import { LZ_CONFIG_GATED_MODULES } from './../helpers/constants'; +import ILayerZeroMessagingLibrary from './../helpers/ILayerZeroMessagingLibrary.json'; +import { getLensHubDeployed } from './../helpers/lens'; +import { MAX_UINT256, HARDHAT_CHAINID } from './../../test/lens/helpers/constants'; + +const TOKEN_CONTRACT = '0xDBF49B20eC2E48ef4CeEb79927eA4Ac2eFc2c961'; // goerli +const TOKEN_THRESHOLD = '1'; +const PROFILE_ID_TO_FOLLOW = '1'; + +const buildFollowWithSigParams = ( + chainId: number | undefined, + lensHubAddress: string, + profileIds: string[] | number[], + datas: Bytes[] | string[], + nonce: number, + deadline: string +) => ({ + types: { + FollowWithSig: [ + { name: 'profileIds', type: 'uint256[]' }, + { name: 'datas', type: 'bytes[]' }, + { name: 'nonce', type: 'uint256' }, + { name: 'deadline', type: 'uint256' }, + ], + }, + domain: { + name: 'Lens Protocol Profiles', + version: '1', + chainId: chainId || HARDHAT_CHAINID, + verifyingContract: lensHubAddress, + }, + value: { + profileIds: profileIds, + datas: datas, + nonce: nonce, + deadline: deadline, + }, +}); + +task('estimate-fee-follow', 'estimate the fee of sending message from remote chain to source chain').setAction(async ({}, hre) => { + const ethers = hre.ethers; + const networkName = hre.network.name; + const [deployer] = await ethers.getSigners(); + + // using the sandbox deployment for module whitelisting + const lensHub = await getLensHubDeployed('lensHub sandbox', deployer); + + if (!(LZ_CONFIG_GATED_MODULES[networkName] && LZ_CONFIG_GATED_MODULES[networkName].remote)) throw new Error('invalid network'); + + const endpoint = new Contract(LZ_CONFIG_GATED_MODULES[networkName].endpoint, ILayerZeroMessagingLibrary.abi, deployer.provider); + + const destination = LZ_CONFIG_GATED_MODULES[networkName].remote; + + const followerAddress = await deployer.getAddress(); + let nonce; + try { + nonce = (await lensHub.sigNonces(followerAddress)).toNumber() + } catch (error) { + nonce = 0; + } + + const msgParams = buildFollowWithSigParams( + hre.network.config.chainId, + lensHub.address, + [PROFILE_ID_TO_FOLLOW], + [[]], + nonce, + MAX_UINT256 + ); + const sig = await deployer._signTypedData(msgParams.domain, msgParams.types, msgParams.value); + const { v, r, s } = ethers.utils.splitSignature(sig); + + const followWithSigData = { + follower: followerAddress, + profileIds: [PROFILE_ID_TO_FOLLOW], + datas: [[]], + sig: { + v, + r, + s, + deadline: MAX_UINT256, + }, + }; + + // dummy payload + const followWithSigType = 'tuple(address follower, uint256[] profileIds, bytes[] datas, tuple(uint8 v, bytes32 r, bytes32 s, uint256 deadline) sig) followSig'; + const types = ['address', 'address', 'uint256', 'uint256', followWithSigType]; + const payload = ethers.utils.defaultAbiCoder.encode( + types, + [followerAddress, TOKEN_CONTRACT, PROFILE_ID_TO_FOLLOW, TOKEN_THRESHOLD, followWithSigData] + ); + + console.log(`endpoint.estimateFees( + ${LZ_CONFIG_GATED_MODULES[destination].chainId}, + ${contractsDeployed.LZGatedProxy}, + ${payload}, + ${false}, + ${"0x"} + )`); + + // @TODO: missing revert data in call exception; Transaction reverted without a reason string + const fees = await endpoint.estimateFees( + LZ_CONFIG_GATED_MODULES[destination].chainId, // the destination LayerZero chainId + contractsDeployed.LZGatedProxy, // your contract address that calls Endpoint.send() + payload, + false, // _payInZRO + "0x" // default '0x' adapterParams, see: Relayer Adapter Param docs + ); + + console.log('payload types', types); + console.log('fees in eth', utils.formatEther(fees[0])); +}); diff --git a/tasks/gated-modules/set-trusted-remotes.ts b/tasks/gated-modules/set-trusted-remotes.ts new file mode 100644 index 0000000..6c31be8 --- /dev/null +++ b/tasks/gated-modules/set-trusted-remotes.ts @@ -0,0 +1,44 @@ +import { task } from 'hardhat/config'; +import promiseLimit from 'promise-limit'; +import { LZ_CONFIG_GATED_MODULES } from './../helpers/constants'; +import getContract from './../helpers/getContract'; +import { contractsDeployedOn } from './../../scripts/utils/migrations'; + +task('set-trusted-remotes', 'sets the trusted remotes for each module / remote pair').setAction(async ({}, hre) => { + const ethers = hre.ethers; + const networkName = hre.network.name; + const [deployer] = await ethers.getSigners(); + + const limit = promiseLimit(1); + + if (!LZ_CONFIG_GATED_MODULES[networkName]) throw new Error('invalid network'); + + const followModule = await getContract(ethers, 'LZGatedFollowModule', deployer); + const referenceModule = await getContract(ethers, 'LZGatedReferenceModule', deployer); + const collectModule = await getContract(ethers, 'LZGatedCollectModule', deployer); + + const { remotes } = LZ_CONFIG_GATED_MODULES[networkName]; + + await Promise.all(remotes.map((remote) => limit(async () => { + const { LZGatedProxy } = contractsDeployedOn(remote); + + if (!LZGatedProxy) throw new Error(`missing LZGatedProxy at remote: ${remote}`); + + console.log(`setting trusted remote (${LZ_CONFIG_GATED_MODULES[remote].chainId}, ${LZGatedProxy})`); + + let tx; + tx = await followModule.setTrustedRemote(LZ_CONFIG_GATED_MODULES[remote].chainId, LZGatedProxy); + console.log(`tx: ${tx.hash}`); + await tx.wait(); + + tx = await referenceModule.setTrustedRemote(LZ_CONFIG_GATED_MODULES[remote].chainId, LZGatedProxy); + console.log(`tx: ${tx.hash}`); + await tx.wait(); + + tx = await collectModule.setTrustedRemote(LZ_CONFIG_GATED_MODULES[remote].chainId, LZGatedProxy); + console.log(`tx: ${tx.hash}`); + await tx.wait(); + }))); + + console.log('done!'); +}); diff --git a/tasks/helpers/abi/LensHub.json b/tasks/helpers/abi/LensHub.json new file mode 100644 index 0000000..25f846b --- /dev/null +++ b/tasks/helpers/abi/LensHub.json @@ -0,0 +1,2609 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "LensHub", + "sourceName": "contracts/core/LensHub.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "followNFTImpl", + "type": "address" + }, + { + "internalType": "address", + "name": "collectNFTImpl", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "CallerNotCollectNFT", + "type": "error" + }, + { + "inputs": [], + "name": "CallerNotFollowNFT", + "type": "error" + }, + { + "inputs": [], + "name": "CannotInitImplementation", + "type": "error" + }, + { + "inputs": [], + "name": "EmergencyAdminCannotUnpause", + "type": "error" + }, + { + "inputs": [], + "name": "InitParamsInvalid", + "type": "error" + }, + { + "inputs": [], + "name": "Initialized", + "type": "error" + }, + { + "inputs": [], + "name": "NotGovernance", + "type": "error" + }, + { + "inputs": [], + "name": "NotGovernanceOrEmergencyAdmin", + "type": "error" + }, + { + "inputs": [], + "name": "NotOwnerOrApproved", + "type": "error" + }, + { + "inputs": [], + "name": "NotProfileOwner", + "type": "error" + }, + { + "inputs": [], + "name": "NotProfileOwnerOrDispatcher", + "type": "error" + }, + { + "inputs": [], + "name": "Paused", + "type": "error" + }, + { + "inputs": [], + "name": "ProfileCreatorNotWhitelisted", + "type": "error" + }, + { + "inputs": [], + "name": "ProfileImageURILengthInvalid", + "type": "error" + }, + { + "inputs": [], + "name": "PublicationDoesNotExist", + "type": "error" + }, + { + "inputs": [], + "name": "PublishingPaused", + "type": "error" + }, + { + "inputs": [], + "name": "SignatureExpired", + "type": "error" + }, + { + "inputs": [], + "name": "SignatureInvalid", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroSpender", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct DataTypes.EIP712Signature", + "name": "sig", + "type": "tuple" + } + ], + "name": "burnWithSig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pubId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "collect", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "collector", + "type": "address" + }, + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pubId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct DataTypes.EIP712Signature", + "name": "sig", + "type": "tuple" + } + ], + "internalType": "struct DataTypes.CollectWithSigData", + "name": "vars", + "type": "tuple" + } + ], + "name": "collectWithSig", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + }, + { + "internalType": "string", + "name": "contentURI", + "type": "string" + }, + { + "internalType": "uint256", + "name": "profileIdPointed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pubIdPointed", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "referenceModuleData", + "type": "bytes" + }, + { + "internalType": "address", + "name": "collectModule", + "type": "address" + }, + { + "internalType": "bytes", + "name": "collectModuleInitData", + "type": "bytes" + }, + { + "internalType": "address", + "name": "referenceModule", + "type": "address" + }, + { + "internalType": "bytes", + "name": "referenceModuleInitData", + "type": "bytes" + } + ], + "internalType": "struct DataTypes.CommentData", + "name": "vars", + "type": "tuple" + } + ], + "name": "comment", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + }, + { + "internalType": "string", + "name": "contentURI", + "type": "string" + }, + { + "internalType": "uint256", + "name": "profileIdPointed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pubIdPointed", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "referenceModuleData", + "type": "bytes" + }, + { + "internalType": "address", + "name": "collectModule", + "type": "address" + }, + { + "internalType": "bytes", + "name": "collectModuleInitData", + "type": "bytes" + }, + { + "internalType": "address", + "name": "referenceModule", + "type": "address" + }, + { + "internalType": "bytes", + "name": "referenceModuleInitData", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct DataTypes.EIP712Signature", + "name": "sig", + "type": "tuple" + } + ], + "internalType": "struct DataTypes.CommentWithSigData", + "name": "vars", + "type": "tuple" + } + ], + "name": "commentWithSig", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "string", + "name": "handle", + "type": "string" + }, + { + "internalType": "string", + "name": "imageURI", + "type": "string" + }, + { + "internalType": "address", + "name": "followModule", + "type": "address" + }, + { + "internalType": "bytes", + "name": "followModuleInitData", + "type": "bytes" + }, + { + "internalType": "string", + "name": "followNFTURI", + "type": "string" + } + ], + "internalType": "struct DataTypes.CreateProfileData", + "name": "vars", + "type": "tuple" + } + ], + "name": "createProfile", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "wallet", + "type": "address" + } + ], + "name": "defaultProfile", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pubId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "collectNFTId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "emitCollectNFTTransferEvent", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "followNFTId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "emitFollowNFTTransferEvent", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "exists", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "profileIds", + "type": "uint256[]" + }, + { + "internalType": "bytes[]", + "name": "datas", + "type": "bytes[]" + } + ], + "name": "follow", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "follower", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "profileIds", + "type": "uint256[]" + }, + { + "internalType": "bytes[]", + "name": "datas", + "type": "bytes[]" + }, + { + "components": [ + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct DataTypes.EIP712Signature", + "name": "sig", + "type": "tuple" + } + ], + "internalType": "struct DataTypes.FollowWithSigData", + "name": "vars", + "type": "tuple" + } + ], + "name": "followWithSig", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "getApproved", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pubId", + "type": "uint256" + } + ], + "name": "getCollectModule", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pubId", + "type": "uint256" + } + ], + "name": "getCollectNFT", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getCollectNFTImpl", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pubId", + "type": "uint256" + } + ], + "name": "getContentURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + } + ], + "name": "getDispatcher", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getDomainSeparator", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + } + ], + "name": "getFollowModule", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + } + ], + "name": "getFollowNFT", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getFollowNFTImpl", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + } + ], + "name": "getFollowNFTURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getGovernance", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + } + ], + "name": "getHandle", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + } + ], + "name": "getProfile", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "pubCount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "followModule", + "type": "address" + }, + { + "internalType": "address", + "name": "followNFT", + "type": "address" + }, + { + "internalType": "string", + "name": "handle", + "type": "string" + }, + { + "internalType": "string", + "name": "imageURI", + "type": "string" + }, + { + "internalType": "string", + "name": "followNFTURI", + "type": "string" + } + ], + "internalType": "struct DataTypes.ProfileStruct", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "handle", + "type": "string" + } + ], + "name": "getProfileIdByHandle", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pubId", + "type": "uint256" + } + ], + "name": "getPub", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "profileIdPointed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pubIdPointed", + "type": "uint256" + }, + { + "internalType": "string", + "name": "contentURI", + "type": "string" + }, + { + "internalType": "address", + "name": "referenceModule", + "type": "address" + }, + { + "internalType": "address", + "name": "collectModule", + "type": "address" + }, + { + "internalType": "address", + "name": "collectNFT", + "type": "address" + } + ], + "internalType": "struct DataTypes.PublicationStruct", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + } + ], + "name": "getPubCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pubId", + "type": "uint256" + } + ], + "name": "getPubPointer", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pubId", + "type": "uint256" + } + ], + "name": "getPubType", + "outputs": [ + { + "internalType": "enum DataTypes.PubType", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pubId", + "type": "uint256" + } + ], + "name": "getReferenceModule", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getState", + "outputs": [ + { + "internalType": "enum DataTypes.ProtocolState", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol", + "type": "string" + }, + { + "internalType": "address", + "name": "newGovernance", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "collectModule", + "type": "address" + } + ], + "name": "isCollectModuleWhitelisted", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "followModule", + "type": "address" + } + ], + "name": "isFollowModuleWhitelisted", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "profileCreator", + "type": "address" + } + ], + "name": "isProfileCreatorWhitelisted", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "referenceModule", + "type": "address" + } + ], + "name": "isReferenceModuleWhitelisted", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "mintTimestampOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "profileIdPointed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pubIdPointed", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "referenceModuleData", + "type": "bytes" + }, + { + "internalType": "address", + "name": "referenceModule", + "type": "address" + }, + { + "internalType": "bytes", + "name": "referenceModuleInitData", + "type": "bytes" + } + ], + "internalType": "struct DataTypes.MirrorData", + "name": "vars", + "type": "tuple" + } + ], + "name": "mirror", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "profileIdPointed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pubIdPointed", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "referenceModuleData", + "type": "bytes" + }, + { + "internalType": "address", + "name": "referenceModule", + "type": "address" + }, + { + "internalType": "bytes", + "name": "referenceModuleInitData", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct DataTypes.EIP712Signature", + "name": "sig", + "type": "tuple" + } + ], + "internalType": "struct DataTypes.MirrorWithSigData", + "name": "vars", + "type": "tuple" + } + ], + "name": "mirrorWithSig", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ownerOf", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct DataTypes.EIP712Signature", + "name": "sig", + "type": "tuple" + } + ], + "name": "permit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + }, + { + "components": [ + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct DataTypes.EIP712Signature", + "name": "sig", + "type": "tuple" + } + ], + "name": "permitForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + }, + { + "internalType": "string", + "name": "contentURI", + "type": "string" + }, + { + "internalType": "address", + "name": "collectModule", + "type": "address" + }, + { + "internalType": "bytes", + "name": "collectModuleInitData", + "type": "bytes" + }, + { + "internalType": "address", + "name": "referenceModule", + "type": "address" + }, + { + "internalType": "bytes", + "name": "referenceModuleInitData", + "type": "bytes" + } + ], + "internalType": "struct DataTypes.PostData", + "name": "vars", + "type": "tuple" + } + ], + "name": "post", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + }, + { + "internalType": "string", + "name": "contentURI", + "type": "string" + }, + { + "internalType": "address", + "name": "collectModule", + "type": "address" + }, + { + "internalType": "bytes", + "name": "collectModuleInitData", + "type": "bytes" + }, + { + "internalType": "address", + "name": "referenceModule", + "type": "address" + }, + { + "internalType": "bytes", + "name": "referenceModuleInitData", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct DataTypes.EIP712Signature", + "name": "sig", + "type": "tuple" + } + ], + "internalType": "struct DataTypes.PostWithSigData", + "name": "vars", + "type": "tuple" + } + ], + "name": "postWithSig", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + } + ], + "name": "setDefaultProfile", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct DataTypes.EIP712Signature", + "name": "sig", + "type": "tuple" + } + ], + "internalType": "struct DataTypes.SetDefaultProfileWithSigData", + "name": "vars", + "type": "tuple" + } + ], + "name": "setDefaultProfileWithSig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "dispatcher", + "type": "address" + } + ], + "name": "setDispatcher", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "dispatcher", + "type": "address" + }, + { + "components": [ + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct DataTypes.EIP712Signature", + "name": "sig", + "type": "tuple" + } + ], + "internalType": "struct DataTypes.SetDispatcherWithSigData", + "name": "vars", + "type": "tuple" + } + ], + "name": "setDispatcherWithSig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newEmergencyAdmin", + "type": "address" + } + ], + "name": "setEmergencyAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "followModule", + "type": "address" + }, + { + "internalType": "bytes", + "name": "followModuleInitData", + "type": "bytes" + } + ], + "name": "setFollowModule", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "followModule", + "type": "address" + }, + { + "internalType": "bytes", + "name": "followModuleInitData", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct DataTypes.EIP712Signature", + "name": "sig", + "type": "tuple" + } + ], + "internalType": "struct DataTypes.SetFollowModuleWithSigData", + "name": "vars", + "type": "tuple" + } + ], + "name": "setFollowModuleWithSig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + }, + { + "internalType": "string", + "name": "followNFTURI", + "type": "string" + } + ], + "name": "setFollowNFTURI", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + }, + { + "internalType": "string", + "name": "followNFTURI", + "type": "string" + }, + { + "components": [ + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct DataTypes.EIP712Signature", + "name": "sig", + "type": "tuple" + } + ], + "internalType": "struct DataTypes.SetFollowNFTURIWithSigData", + "name": "vars", + "type": "tuple" + } + ], + "name": "setFollowNFTURIWithSig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newGovernance", + "type": "address" + } + ], + "name": "setGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + }, + { + "internalType": "string", + "name": "imageURI", + "type": "string" + } + ], + "name": "setProfileImageURI", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "profileId", + "type": "uint256" + }, + { + "internalType": "string", + "name": "imageURI", + "type": "string" + }, + { + "components": [ + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct DataTypes.EIP712Signature", + "name": "sig", + "type": "tuple" + } + ], + "internalType": "struct DataTypes.SetProfileImageURIWithSigData", + "name": "vars", + "type": "tuple" + } + ], + "name": "setProfileImageURIWithSig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "enum DataTypes.ProtocolState", + "name": "newState", + "type": "uint8" + } + ], + "name": "setState", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "sigNonces", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "tokenByIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "tokenDataOf", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint96", + "name": "mintTimestamp", + "type": "uint96" + } + ], + "internalType": "struct IERC721Time.TokenData", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "tokenOfOwnerByIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "tokenURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "collectModule", + "type": "address" + }, + { + "internalType": "bool", + "name": "whitelist", + "type": "bool" + } + ], + "name": "whitelistCollectModule", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "followModule", + "type": "address" + }, + { + "internalType": "bool", + "name": "whitelist", + "type": "bool" + } + ], + "name": "whitelistFollowModule", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "profileCreator", + "type": "address" + }, + { + "internalType": "bool", + "name": "whitelist", + "type": "bool" + } + ], + "name": "whitelistProfileCreator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "referenceModule", + "type": "address" + }, + { + "internalType": "bool", + "name": "whitelist", + "type": "bool" + } + ], + "name": "whitelistReferenceModule", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x60e06040526000600b553480156200001657600080fd5b50604051620060af380380620060af8339810160408190526200003991620000c2565b306080526001600160a01b03821662000065576040516348be0eb360e01b815260040160405180910390fd5b6001600160a01b0381166200008d576040516348be0eb360e01b815260040160405180910390fd5b6001600160a01b0391821660a0521660c052620000fa565b80516001600160a01b0381168114620000bd57600080fd5b919050565b60008060408385031215620000d657600080fd5b620000e183620000a5565b9150620000f160208401620000a5565b90509250929050565b60805160a05160c051615f77620001386000396000818161082d01528181611fb301526127c30152600061061e01526000610d7c0152615f776000f3fe608060405234801561001057600080fd5b50600436106104805760003560e01c806384114ad411610257578063bd12d3f011610146578063dd69cdb1116100c3578063f08f4f6411610087578063f08f4f6414610c21578063f1b2f8bc14610c41578063f990ccd714610c54578063fb78ae6c14610c74578063ffea138e14610c8757600080fd5b8063dd69cdb114610ba4578063df457c3414610bb7578063e985e9c514610bca578063ec81d19414610c06578063ed24911d14610c1957600080fd5b8063c6b5d06c1161010a578063c6b5d06c14610b1f578063c736c99014610b32578063c87b56dd14610b52578063d923d20c14610b65578063dc21725314610b9157600080fd5b8063bd12d3f014610a93578063bdfdd4bc14610aa6578063bfbf0b4b14610ab9578063bfd24f4714610acc578063c0da9bcd14610adf57600080fd5b8063a6d8e1e4116101d4578063b48951e411610198578063b48951e414610a10578063b5a3149614610a23578063b6f32d2b14610a36578063b7984c0514610a49578063b88d4fde14610a8057600080fd5b8063a6d8e1e414610966578063a9ec656314610979578063ab033ea9146109a5578063ad3e72bf146109b8578063af05dd22146109e457600080fd5b806392254a621161021b57806392254a62146108fc57806395d89b4114610925578063963ff1411461092d5780639b84a14c14610940578063a22cb4651461095357600080fd5b806384114ad41461087757806386e2947b1461088a57806389028a131461089d5780638e204fb4146108b05780638e4fd6a9146108dc57600080fd5b80633a15ff0711610373578063540528b9116102f05780636dea40b3116102b45780636dea40b31461080557806370a082311461081857806377349a5f1461082b5780637a375716146108515780637ef67f991461086457600080fd5b8063540528b91461075757806356de96db1461078057806357ff49f6146107935780635ca3eebf146107ca5780636352211e146107f257600080fd5b806342966c681161033757806342966c68146106d45780634f558e79146106e75780634f6ccce7146106fa57806350ddf35c1461070d57806352aaef551461072057600080fd5b80633a15ff07146106685780633b28b89f146106885780633b5081321461069b5780634187e4c5146106ae57806342842e0e146106c157600080fd5b806320fa728a1161040157806331dcebe3116103c557806331dcebe3146105e957806331fff07c146105fc5780633502ac4b1461061c57806335da339414610642578063374c94731461065557600080fd5b806320fa728a1461058c57806323b872dd1461059f578063289b3c0d146105b25780632f745c59146105c35780632faeed81146105d657600080fd5b8063095ea7b311610448578063095ea7b31461051557806318160ddd146105285780631865c57d1461053a5780631cbb26201461054d578063209055061461057957600080fd5b806301ffc9a714610485578063054871b8146104ad57806306fdde03146104c2578063077f224a146104d7578063081812fc146104ea575b600080fd5b610498610493366004614904565b610c9a565b60405190151581526020015b60405180910390f35b6104c06104bb366004614962565b610cc5565b005b6104ca610ce6565b6040516104a49190614a05565b6104c06104e5366004614a2f565b610d78565b6104fd6104f8366004614aaf565b610e12565b6040516001600160a01b0390911681526020016104a4565b6104c0610523366004614ac8565b610eac565b6008545b6040519081526020016104a4565b600c5460ff166040516104a49190614b08565b61049861055b366004614b22565b6001600160a01b03166000908152600e602052604090205460ff1690565b6104c0610587366004614b4d565b610fbd565b61052c61059a366004614b80565b611028565b6104c06105ad366004614bc1565b61105a565b6017546001600160a01b03166104fd565b61052c6105d1366004614ac8565b61108b565b61052c6105e4366004614c15565b611121565b6104c06105f7366004614b4d565b61114b565b61060f61060a366004614c49565b6111ae565b6040516104a49190614c6b565b7f00000000000000000000000000000000000000000000000000000000000000006104fd565b6104c0610650366004614b22565b611237565b6104ca610663366004614aaf565b6112a4565b61052c610676366004614aaf565b60009081526013602052604090205490565b6104c0610696366004614c91565b611349565b61052c6106a9366004614cd8565b6114e5565b6104c06106bc366004614b4d565b61172d565b6104c06106cf366004614bc1565b611790565b6104c06106e2366004614aaf565b6117ab565b6104986106f5366004614aaf565b6117c8565b61052c610708366004614aaf565b6117e7565b61052c61071b366004614aaf565b61187a565b6104fd61072e366004614c49565b60009182526014602090815260408084209284529190529020600501546001600160a01b031690565b6104fd610765366004614aaf565b6000908152601160205260409020546001600160a01b031690565b6104c061078e366004614d0c565b611910565b6104fd6107a1366004614c49565b60009182526014602090815260408084209284529190529020600401546001600160a01b031690565b6107dd6107d8366004614c49565b611997565b604080519283526020830191909152016104a4565b6104fd610800366004614aaf565b6119c0565b6104c0610813366004614d2d565b611a37565b61052c610826366004614b22565b611ace565b7f00000000000000000000000000000000000000000000000000000000000000006104fd565b61052c61085f366004614d86565b611b55565b6104c0610872366004614dd3565b611ea1565b61052c610885366004614e10565b611f70565b6104c0610898366004614e4a565b61202d565b6104c06108ab366004614e8e565b6120cb565b6104986108be366004614b22565b6001600160a01b031660009081526010602052604090205460ff1690565b6108ef6108ea366004614c91565b612190565b6040516104a49190614ee3565b61052c61090a366004614b22565b6001600160a01b031660009081526015602052604090205490565b6104ca612416565b61052c61093b366004614c15565b612425565b6104c061094e366004614c15565b6124ef565b6104c0610961366004614b4d565b6125cb565b6104c0610974366004614b4d565b61262f565b6104fd610987366004614aaf565b6000908152601360205260409020600201546001600160a01b031690565b6104c06109b3366004614b22565b612692565b6104986109c6366004614b22565b6001600160a01b03166000908152600f602052604090205460ff1690565b6104986109f2366004614b22565b6001600160a01b03166000908152600d602052604090205460ff1690565b61052c610a1e366004614f27565b6126a3565b6104ca610a31366004614c49565b61284a565b61052c610a44366004614f62565b612912565b6104fd610a57366004614c49565b60009182526014602090815260408084209284529190529020600301546001600160a01b031690565b6104c0610a8e3660046150ab565b612937565b6104c0610aa1366004614c15565b612969565b6104c0610ab4366004615112565b6129cc565b6104c0610ac736600461514d565b612a5b565b6104c0610ada366004615169565b612b1a565b610af2610aed366004614aaf565b612b35565b6040805182516001600160a01b031681526020928301516001600160601b031692810192909252016104a4565b6104c0610b2d366004614962565b612c03565b610b45610b40366004614c49565b612c1f565b6040516104a4919061518c565b6104ca610b60366004614aaf565b612d7e565b6104fd610b73366004614aaf565b6000908152601360205260409020600101546001600160a01b031690565b6104c0610b9f36600461514d565b612eb0565b6104c0610bb23660046151f6565b612f93565b61052c610bc5366004614cd8565b612fae565b610498610bd836600461521a565b6001600160a01b03918216600090815260056020908152604080832093909416825291909152205460ff1690565b6104ca610c14366004614aaf565b6131a5565b61052c6131c5565b610c34610c2f366004614aaf565b6131d4565b6040516104a49190615244565b6104c0610c4f366004614aaf565b613428565b61052c610c62366004614b22565b600a6020526000908152604090205481565b6108ef610c82366004615315565b61343a565b61052c610c95366004614c15565b6134ce565b60006001600160e01b0319821663780e9d6360e01b1480610cbf5750610cbf826135a2565b92915050565b610ccd6135f2565b610cd68361362c565b610ce183838361368b565b505050565b606060008054610cf590615374565b80601f0160208091040260200160405190810160405280929190818152602001828054610d2190615374565b8015610d6e5780601f10610d4357610100808354040283529160200191610d6e565b820191906000526020600020905b815481529060010190602001808311610d5157829003601f168201915b5050505050905090565b60017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316301415610dc4576040516325c7410560e21b815260040160405180910390fd5b600b548111610de6576040516302ed543d60e51b815260040160405180910390fd5b600b819055610df78686868661370c565b610e01600261375d565b610e0a826137db565b505050505050565b6000818152600260205260408120546001600160a01b0316610e905760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a20617070726f76656420717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b60648201526084015b60405180910390fd5b506000908152600460205260409020546001600160a01b031690565b6000610eb7826119c0565b9050806001600160a01b0316836001600160a01b03161415610f255760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b6064820152608401610e87565b336001600160a01b0382161480610f415750610f418133610bd8565b610fb35760405162461bcd60e51b815260206004820152603860248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f74206f7760448201527f6e6572206e6f7220617070726f76656420666f7220616c6c00000000000000006064820152608401610e87565b610ce18383613834565b610fc56138a2565b6001600160a01b0382166000818152600d6020908152604091829020805460ff191685151590811790915591514281529192917f8f617843889b94892bd44852d36ca6a7f49ecf4350a01e7b68e22d80f4ed95bc91015b60405180910390a35050565b600080838360405161103b9291906153a9565b6040805191829003909120600090815260126020522054949350505050565b61106433826138cd565b6110805760405162461bcd60e51b8152600401610e87906153b9565b610ce18383836139c0565b600061109683611ace565b82106110f85760405162461bcd60e51b815260206004820152602b60248201527f455243373231456e756d657261626c653a206f776e657220696e646578206f7560448201526a74206f6620626f756e647360a81b6064820152608401610e87565b506001600160a01b03919091166000908152600660209081526040808320938352929052205490565b600061112b613b6b565b611135823561362c565b610cbf6111418361540a565b613ba2565b919050565b6111536138a2565b6001600160a01b0382166000818152600f6020908152604091829020805460ff191685151590811790915591514281529192917f6cc19a794d6a439023150cd58748eed4353190c0bb060d2e6250e2df4a68b673910161101c565b60008115806111ca575060008381526013602052604090205482115b156111d757506003610cbf565b60008381526014602090815260408083208584529091529020600401546001600160a01b031661120957506002610cbf565b600083815260146020908152604080832085845290915290205461122f57506000610cbf565b506001610cbf565b61123f6138a2565b601880546001600160a01b038381166001600160a01b03198316811790935560405191169190829033907f676c0801b0f400762e958ee31cfbb10870e70786f6761f57c8647e766b0db3d9906112989042815260200190565b60405180910390a45050565b60008181526013602052604090206005018054606091906112c490615374565b80601f01602080910402602001604051908101604052809291908181526020018280546112f090615374565b801561133d5780601f106113125761010080835404028352916020019161133d565b820191906000526020600020905b81548152906001019060200180831161132057829003601f168201915b50505050509050919050565b6113516135f2565b600061135d82356119c0565b905061144761143d7f61332f0343256b019dca2e0ffc75766fde55292d645df285bb1a3b74a493b51084356113986040870160208801614b22565b6113a560408801886154a5565b6040516113b39291906153a9565b604080519182900382206001600160a01b0389166000908152600a6020908152929020805460018101909155611422969594939192909160c08c0135910195865260208601949094526001600160a01b039290921660408501526060840152608083015260a082015260c00190565b60405160208183030381529060405280519060200120613bfa565b8284606001613c44565b73__$1f7cbacb1f9f5d323b85b0487838426c8d$__636a7ecf1383356114736040860160208701614b22565b61148060408701876154a5565b87356000908152601360205260409081902090516001600160e01b031960e088901b1681526114b9959493929190600e90600401615514565b60006040518083038186803b1580156114d157600080fd5b505af4158015610e0a573d6000803e3d6000fd5b60006114ef613b6b565b60006114fb83356119c0565b905061163661162c7ffb2be94758c5f2515e99eae2d54860ea468ce314ffb38e328c234793acbcccf4853561153360208801886154a5565b6040516115419291906153a9565b604051809103902087604001602081019061155c9190614b22565b61156960608a018a6154a5565b6040516115779291906153a9565b60405190819003902061159060a08b0160808c01614b22565b61159d60a08c018c6154a5565b6040516115ab9291906153a9565b604080519182900382206001600160a01b03808d166000908152600a6020908152908490208054600181019091559085019a909a52918301979097526060820195909552928416608084015260a083019190915290911660c082015260e0810191909152610100810191909152610120808701359082015261014001611422565b828560c001613c44565b611726833561164860208601866154a5565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061168d925050506060870160408801614b22565b61169a60608801886154a5565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506116df9250505060a0890160808a01614b22565b6116ec60a08a018a6154a5565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613d2292505050565b9392505050565b6117356138a2565b6001600160a01b038216600081815260106020908152604091829020805460ff191685151590811790915591514281529192917f37872a053ef20cb52defb7c9ec20e1a87cb8dd5098ac9e76a144be263dfef572910161101c565b610ce183838360405180602001604052806000815250612937565b6117b36135f2565b6117bc81613dc1565b6117c581613df1565b50565b6000818152600260205260408120546001600160a01b03161515610cbf565b60006117f260085490565b82106118555760405162461bcd60e51b815260206004820152602c60248201527f455243373231456e756d657261626c653a20676c6f62616c20696e646578206f60448201526b7574206f6620626f756e647360a01b6064820152608401610e87565b6008828154811061186857611868615554565b90600052602060002001549050919050565b600081815260026020526040812054600160a01b90046001600160601b0316806119015760405162461bcd60e51b815260206004820152603260248201527f4552433732313a206d696e742074696d657374616d7020717565727920666f72604482015271103737b732bc34b9ba32b73a103a37b5b2b760711b6064820152608401610e87565b6001600160601b031692915050565b6018546001600160a01b031633141561196357600081600281111561193757611937614af2565b141561195657604051630357a1ab60e31b815260040160405180910390fd5b61195e6135f2565b61198e565b6017546001600160a01b0316331461198e57604051635010559960e11b815260040160405180910390fd5b6117c58161375d565b6000828152601460209081526040808320848452909152902080546001909101545b9250929050565b6000818152600260205260408120546001600160a01b031680610cbf5760405162461bcd60e51b815260206004820152602960248201527f4552433732313a206f776e657220717565727920666f72206e6f6e657869737460448201526832b73a103a37b5b2b760b91b6064820152608401610e87565b611a3f6135f2565b611a4884613e2c565b600084815260136020526040908190209051636a7ecf1360e01b815273__$1f7cbacb1f9f5d323b85b0487838426c8d$__91636a7ecf1391611a9891889188918891889190600e90600401615514565b60006040518083038186803b158015611ab057600080fd5b505af4158015611ac4573d6000803e3d6000fd5b5050505050505050565b60006001600160a01b038216611b395760405162461bcd60e51b815260206004820152602a60248201527f4552433732313a2062616c616e636520717565727920666f7220746865207a65604482015269726f206164647265737360b01b6064820152608401610e87565b506001600160a01b031660009081526003602052604090205490565b6000611b5f613b6b565b6000611b6b83356119c0565b9050611d14611d097f35b861ea91c543d61c3f094b5effa29dfcd22a439290ae248fdccbc7162016918535611ba360208801886154a5565b604051611bb19291906153a9565b604051809103902087604001358860600135898060800190611bd391906154a5565b604051611be19291906153a9565b604051908190039020611bfa60c08c0160a08d01614b22565b611c0760c08d018d6154a5565b604051611c159291906153a9565b604051908190039020611c2f6101008e0160e08f01614b22565b611c3d6101008f018f6154a5565b604051611c4b9291906153a9565b6040518091039020600a60008e6001600160a01b03166001600160a01b031681526020019081526020016000206000815480929190600101919050558f61012001606001356040516020016114229c9b9a999897969594939291909b8c5260208c019a909a5260408b019890985260608a0196909652608089019490945260a08801929092526001600160a01b0390811660c088015260e0870191909152166101008501526101208401526101408301526101608201526101800190565b828561012001613c44565b61172660405180610120016040528085600001358152602001858060200190611d3d91906154a5565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509082525060408087013560208301526060808801359183019190915201611d9a60808701876154a5565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250505090825250602001611de460c0870160a08801614b22565b6001600160a01b03168152602001611dff60c08701876154a5565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250505090825250602001611e4a610100870160e08801614b22565b6001600160a01b03168152602001611e666101008701876154a5565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050915250613e66565b6001600160a01b038316611ec8576040516307eb16dd60e21b815260040160405180910390fd5b6000611ed3836119c0565b6001600160a01b038181166000908152600a602090815260409182902080546001810190915582517f49ecf333e5b8c95c40fdafc95c1ad136e8914a8fb55e9dc8bb01eaa83a2df9ad9281019290925292881691810191909152606080820187905260808201929092529084013560a0820152909150611f6090611f599060c001611422565b8284613c44565b611f6a8484613834565b50505050565b6000611f7a6135f2565b60405163334ce6f160e21b815273__$1e68a60ae0444699fe08192a29ecc09930$__9063cd339bc490611fe190339089908990899089907f00000000000000000000000000000000000000000000000000000000000000009060149060139060040161556a565b602060405180830381865af4158015611ffe573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061202291906155b8565b90505b949350505050565b60008581526014602090815260408083208784529091529020600501546001600160a01b03163381146120735760405163c6d1651b60e01b815260040160405180910390fd5b604080516001600160a01b03858116825284166020820152428183015290518591879189917f68edb7ec2c37d21b3b72233960b487f2966f4ac82b7430d39f24d1f8d6f99106919081900360600190a4505050505050565b6001600160a01b0383166120f2576040516307eb16dd60e21b815260040160405180910390fd5b6001600160a01b038481166000818152600a602090815260409182902080546001810190915582517f47ab88482c90e4bb94b82a947ae78fa91fb25de1469ab491f4c15b9a0a2677ee9281019290925291810192909252918516606080830191909152841515608083015260a08201929092529082013560c08201526121859061217e9060e001611422565b8583613c44565b611f6a848484613ec5565b606061219a6135f2565b60006121a960408401846155d1565b905090506000816001600160401b038111156121c7576121c7614f9d565b6040519080825280602002602001820160405280156121f0578160200160208202803683370190505b50905060005b828110156122685761220b60408601866155d1565b8281811061221b5761221b615554565b905060200281019061222d91906154a5565b60405161223b9291906153a9565b604051809103902082828151811061225557612255615554565b60209081029190910101526001016121f6565b5061236a6123547ffb6b7f1cd1b38daf3822aff0abbe78124db5d62a4748bcff007c15ccd6d30bc561229d60208801886155d1565b6040516020016122ae92919061561a565b60405160208183030381529060405280519060200120846040516020016122d59190615646565b60405160208183030381529060405280519060200120600a60008a60000160208101906123029190614b22565b6001600160a01b0316815260208082019290925260409081016000208054600181019091558151928301959095528101929092526060820152608081019190915260c08781013560a083015201611422565b6123616020870187614b22565b86606001613c44565b73__$1e68a60ae0444699fe08192a29ecc09930$__636fb4b4636123916020870187614b22565b61239e60208801886155d1565b6123ab60408a018a6155d1565b601360126040518863ffffffff1660e01b81526004016123d197969594939291906156c1565b600060405180830381865af41580156123ee573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612025919081019061577b565b606060018054610cf590615374565b600061242f613b6b565b612439823561362c565b610cbf823561244b60208501856154a5565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250612490925050506060860160408701614b22565b61249d60608701876154a5565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506124e29250505060a0880160808901614b22565b6116ec60a08901896154a5565b6124f76135f2565b600061250382356119c0565b90506125b06125a67f5b9860bd835e648945b22d053515bc1f53b7d9fab4b23b1b49db15722e945d14843561253b60208701876154a5565b6040516125499291906153a9565b604080519182900382206001600160a01b0388166000908152600a60209081529083902080546001810190915590840195909552908201929092526060810191909152608081019190915260a0858101359082015260c001611422565b8284604001613c44565b6125c782356125c260208501856154a5565b61368b565b5050565b6001600160a01b0382163314156126245760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c6572000000000000006044820152606401610e87565b6125c7338383613ec5565b6126376138a2565b6001600160a01b0382166000818152600e6020908152604091829020805460ff191685151590811790915591514281529192917f52c5b7889df9f12f84ec3da051e854e5876678370d8357959c23ef59dd6486df910161101c565b61269a6138a2565b6117c5816137db565b60006126ad6135f2565b61278361276d7f7f9b4ea1fc678b4fda1611ac5cbd28f339e235d89b1540635e9b2e0223a3c101602085013560408601356126eb60608801886154a5565b6040516126f99291906153a9565b604051908190039020600a600061271360208b018b614b22565b6001600160a01b0316815260208082019290925260409081016000208054600181019091558151928301969096528101939093526060830191909152608082015260a081019190915260e08581013560c083015201611422565b61277a6020850185614b22565b84608001613c44565b73__$1e68a60ae0444699fe08192a29ecc09930$__63cd339bc46127aa6020850185614b22565b602085013560408601356127c160608801886154a5565b7f0000000000000000000000000000000000000000000000000000000000000000601460136040518963ffffffff1660e01b815260040161280998979695949392919061556a565b602060405180830381865af4158015612826573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cbf91906155b8565b606060008061285b85856014613f32565b506000828152601460209081526040808320848452909152902060020180549294509092509061288a90615374565b80601f01602080910402602001604051908101604052809291908181526020018280546128b690615374565b80156129035780601f106128d857610100808354040283529160200191612903565b820191906000526020600020905b8154815290600101906020018083116128e657829003601f168201915b50505050509250505092915050565b600061291c613b6b565b612926823561362c565b610cbf61293283615820565b613e66565b61294133836138cd565b61295d5760405162461bcd60e51b8152600401610e87906153b9565b611f6a84848484613ff4565b6129716135f2565b600061297d82356119c0565b90506129b56125a67fd8d76e8b2b26e1ebe72def13fb559a68561ef064055b0de01f955bc26e25d42f843561253b60208701876154a5565b6125c782356129c760208501856154a5565b614027565b6000848152601360205260409020600201546001600160a01b0316338114612a075760405163646785d560e11b815260040160405180910390fd5b604080516001600160a01b0385811682528416602082015242818301529051859187917f4996ad2257e7db44908136c43128cc10ca988096f67dc6bb0bcee11d151368fb9181900360600190a35050505050565b612a636135f2565b6000612a6f82356119c0565b9050612b006125a67f77ba3e9f5fa75343bbad1241fb539a0064de97694b47d463d1eb5c54aba11f0f8435612aaa6040870160208801614b22565b6001600160a01b038681166000908152600a602090815260409182902080546001810190915582519182019690965290810193909352166060820152608081019190915260a0858101359082015260c001611422565b6125c78235612b156040850160208601614b22565b614078565b612b226135f2565b612b2b82613e2c565b6125c78282614078565b60408051808201909152600080825260208201526000828152600260205260409020546001600160a01b0316612bc45760405162461bcd60e51b815260206004820152602e60248201527f4552433732313a20746f6b656e206461746120717565727920666f72206e6f6e60448201526d32bc34b9ba32b73a103a37b5b2b760911b6064820152608401610e87565b506000908152600260209081526040918290208251808401909352546001600160a01b0381168352600160a01b90046001600160601b03169082015290565b612c0b6135f2565b612c148361362c565b610ce1838383614027565b612c736040518060c0016040528060008152602001600081526020016060815260200160006001600160a01b0316815260200160006001600160a01b0316815260200160006001600160a01b031681525090565b6014600084815260200190815260200160002060008381526020019081526020016000206040518060c00160405290816000820154815260200160018201548152602001600282018054612cc690615374565b80601f0160208091040260200160405190810160405280929190818152602001828054612cf290615374565b8015612d3f5780601f10612d1457610100808354040283529160200191612d3f565b820191906000526020600020905b815481529060010190602001808311612d2257829003601f168201915b505050918352505060038201546001600160a01b0390811660208301526004830154811660408301526005909201549091166060909101529392505050565b6000818152601360205260409020600201546060906001600160a01b031673__$f906f20d797116ee89ed79945048c6ad36$__633edea73c848315612e2457836001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612dfb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e1f91906155b8565b612e27565b60005b612e30876119c0565b6000888152601360205260409081902090516001600160e01b031960e087901b168152612e6b94939291600381019160049182019101615993565b600060405180830381865af4158015612e88573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261172691908101906159d2565b612eb86135f2565b612f79612f637e198a25e19615d82f53977db0fc1ee7f3701b0fdf1299a971cf997545879c04612eeb6020850185614b22565b60208501803590600a90600090612f029089614b22565b6001600160a01b03168152602080820192909252604090810160002080546001810190915590516114229594939260a08a013591019485526001600160a01b0393909316602085015260408401919091526060830152608082015260a00190565b612f706020840184614b22565b83604001613c44565b6117c5612f896020830183614b22565b82602001356140d5565b612f9b6135f2565b612fa5828261416d565b6125c782613df1565b6000612fb8613b6b565b6000612fc483356119c0565b90506130c961162c7f4178441487eb967c15821c9ffee94853812f263a69ccb6352bdaac59f77e486f85356020870135604088013561300660608a018a6154a5565b6040516130149291906153a9565b60405190819003902061302d60a08b0160808c01614b22565b61303a60a08c018c6154a5565b6040516130489291906153a9565b604080519182900382206001600160a01b03808d166000908152600a6020908152908490208054600181019091559085019a909a52918301979097526060820195909552608081019390935260a083019190915290911660c082015260e0810191909152610100810191909152610120808701359082015261014001611422565b6117266040518060c0016040528085600001358152602001856020013581526020018560400135815260200185806060019061310591906154a5565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509082525060200161314f60a0870160808801614b22565b6001600160a01b0316815260200161316a60a08701876154a5565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050915250613ba2565b60008181526013602052604090206003018054606091906112c490615374565b60006131cf6141fb565b905090565b61321f6040518060c001604052806000815260200160006001600160a01b0316815260200160006001600160a01b031681526020016060815260200160608152602001606081525090565b600082815260136020908152604091829020825160c0810184528154815260018201546001600160a01b039081169382019390935260028201549092169282019290925260038201805491929160608401919061327b90615374565b80601f01602080910402602001604051908101604052809291908181526020018280546132a790615374565b80156132f45780601f106132c9576101008083540402835291602001916132f4565b820191906000526020600020905b8154815290600101906020018083116132d757829003601f168201915b5050505050815260200160048201805461330d90615374565b80601f016020809104026020016040519081016040528092919081815260200182805461333990615374565b80156133865780601f1061335b57610100808354040283529160200191613386565b820191906000526020600020905b81548152906001019060200180831161336957829003601f168201915b5050505050815260200160058201805461339f90615374565b80601f01602080910402602001604051908101604052809291908181526020018280546133cb90615374565b80156134185780601f106133ed57610100808354040283529160200191613418565b820191906000526020600020905b8154815290600101906020018083116133fb57829003601f168201915b5050505050815250509050919050565b6134306135f2565b6117c533826140d5565b60606134446135f2565b604051636fb4b46360e01b815273__$1e68a60ae0444699fe08192a29ecc09930$__90636fb4b4639061348990339089908990899089906013906012906004016156c1565b600060405180830381865af41580156134a6573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612022919081019061577b565b60006134d86135f2565b336000908152600d602052604090205460ff166135085760405163561a858760e01b815260040160405180910390fd5b60168054600101908190556135296135236020850185614b22565b82614290565b604051633f67a66f60e11b815273__$1f7cbacb1f9f5d323b85b0487838426c8d$__90637ecf4cde9061356b9086908590601290601390600e90600401615a3f565b60006040518083038186803b15801561358357600080fd5b505af4158015613597573d6000803e3d6000fd5b509295945050505050565b60006001600160e01b031982166380ac58cd60e01b14806135d357506001600160e01b03198216635b5e139f60e01b145b80610cbf57506301ffc9a760e01b6001600160e01b0319831614610cbf565b6002600c5460ff16600281111561360b5761360b614af2565b141561362a576040516313d0ff5960e31b815260040160405180910390fd5b565b613635816119c0565b6001600160a01b0316336001600160a01b0316148061366a57506000818152601160205260409020546001600160a01b031633145b156136725750565b60405163f33ac98f60e01b815260040160405180910390fd5b6117708111156136ae5760405163781548c760e01b815260040160405180910390fd5b60008381526013602052604090206136ca906004018383614855565b50827fd5a5879cad33c830cc1432c1850107029a09c80c60e9bce1ecd08d24880bd46c8383426040516136ff93929190615b36565b60405180910390a2505050565b613718848484846143e1565b7f414cd0b34676984f09a5f76ce9718d4062e50283abe0e7e274a9a5b4e0c99c30848484844260405161374f959493929190615b5a565b60405180910390a150505050565b600c805460ff811691839160ff1916600183600281111561378057613780614af2565b021790555081600281111561379757613797614af2565b8160028111156137a9576137a9614af2565b60405142815233907fa2f9a1499fc1f9b7796d21fe5761290ccb7e0ef6ccf35fa58b668f304a62a1ca90602001611298565b601780546001600160a01b038381166001600160a01b03198316811790935560405191169190829033907fe552a55455b740845a5c07ed445d1724142fc997b389835495a29b30cddc1ccd906112989042815260200190565b600081815260046020526040902080546001600160a01b0319166001600160a01b0384169081179091558190613869826119c0565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6017546001600160a01b0316331461362a57604051632d5be4cb60e21b815260040160405180910390fd5b6000818152600260205260408120546001600160a01b03166139465760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a206f70657261746f7220717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b6064820152608401610e87565b6000613951836119c0565b9050806001600160a01b0316846001600160a01b0316148061398c5750836001600160a01b031661398184610e12565b6001600160a01b0316145b8061202557506001600160a01b0380821660009081526005602090815260408083209388168352929052205460ff16612025565b826001600160a01b03166139d3826119c0565b6001600160a01b031614613a3b5760405162461bcd60e51b815260206004820152602960248201527f4552433732313a207472616e73666572206f6620746f6b656e2074686174206960448201526839903737ba1037bbb760b91b6064820152608401610e87565b6001600160a01b038216613a9d5760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b6064820152608401610e87565b613aa8838383614401565b613ab3600082613834565b6001600160a01b0383166000908152600360205260408120805460019290613adc908490615baa565b90915550506001600160a01b0382166000908152600360205260408120805460019290613b0a908490615bc1565b909155505060008181526002602052604080822080546001600160a01b0319166001600160a01b0386811691821790925591518493918716917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b6000600c5460ff166002811115613b8457613b84614af2565b1461362a57604051630f392a3b60e31b815260040160405180910390fd5b8051600090815260136020526040808220805460010190819055905163764e3e9360e01b815273__$1f7cbacb1f9f5d323b85b0487838426c8d$__9063764e3e939061356b9086908590601490601090600401615bd9565b600080613c056141fb565b60405161190160f01b602082015260228101919091526042810184905260620160408051601f1981840301815291905280516020909101209392505050565b4281606001351015613c6957604051630819bdcd60e01b815260040160405180910390fd5b6000600184613c7b6020850185615c65565b604080516000815260208181018084529490945260ff9092168282015291850135606082015290840135608082015260a0016020604051602081039080840390855afa158015613ccf573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381161580613d045750826001600160a01b0316816001600160a01b031614155b15611f6a576040516337e8456b60e01b815260040160405180910390fd5b60008681526013602052604080822080546001019081905590516309dbf05160e41b815273__$1f7cbacb1f9f5d323b85b0487838426c8d$__90639dbf051090613d85908b908b908b908b908b908b908a90601490600f90601090600401615c88565b60006040518083038186803b158015613d9d57600080fd5b505af4158015613db1573d6000803e3d6000fd5b50929a9950505050505050505050565b613dcb33826138cd565b613de857604051636d8a29e760e11b815260040160405180910390fd5b6117c581614476565b6000818152601360205260408082209051613e0f9160030190615d0f565b604080519182900390912060009081526012602052908120555050565b613e35816119c0565b6001600160a01b0316336001600160a01b0316146117c55760405163f194fae560e01b815260040160405180910390fd5b80516000908152601360208190526040808320805460010190819055905163731d549f60e11b8152909173__$1f7cbacb1f9f5d323b85b0487838426c8d$__9163e63aa93e9161356b9187918691601490600f90601090600401615d7d565b6001600160a01b03838116600081815260056020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b600083815260208281526040808320858452909152812060040154819081906001600160a01b03168015613f6d578693508592509050613feb565b60008781526020868152604080832089845290915290205480613fa35760405163a43d2a7160e01b815260040160405180910390fd5b6000888152602087815260408083208a84528252808320600101548484528983528184208185529092529091206004015491955093506001600160a01b03169150613feb9050565b93509350939050565b613fff8484846139c0565b61400b84848484614512565b611f6a5760405162461bcd60e51b8152600401610e8790615e7f565b6000838152601360205260409020614043906005018383614855565b50827fe82886e1af6fcab5caef13815b22f51384e970c367a785f265d13860a7d6966d8383426040516136ff93929190615b36565b60008281526011602090815260409182902080546001600160a01b0319166001600160a01b038516908117909155915142815284917f22baaec4952f35f59e45bd2ddb287e1ccc6d319375770c09428eb8f8d604e065910161101c565b6000811180156140ff57506140e9816119c0565b6001600160a01b0316826001600160a01b031614155b1561411d5760405163f194fae560e01b815260040160405180910390fd5b6001600160a01b03821660008181526015602052604090819020839055518291907f0afd7c479e8bc7dcdb856b3cc27d2332dfe1f018fde574ea124919ddcae8a9339061101c9042815260200190565b6000614178836119c0565b6001600160a01b0381166000908152600a602090815260409182902080546001810190915582517f108ccda6d7331b00561a3eea66a2ae331622356585681c62731e4a01aae2261a928101929092529181018690526060808201929092529084013560808201529091506141f290611f599060a001611422565b610ce183614476565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f614226610ce6565b80516020918201206040805192830193909352918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b6001600160a01b0382166142e65760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152606401610e87565b6000818152600260205260409020546001600160a01b03161561434b5760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606401610e87565b61435760008383614401565b6001600160a01b0382166000908152600360205260408120805460019290614380908490615bc1565b90915550506000818152600260205260408082206001600160a01b038516600160a01b426001600160601b031602811790915590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b6143ed60008585614855565b506143fa60018383614855565b5050505050565b6144096135f2565b6000818152601160205260409020546001600160a01b03161561443157614431816000614078565b6001600160a01b03831660009081526015602052604090205481141561446b576001600160a01b0383166000908152601560205260408120555b610ce183838361460d565b6000614481826119c0565b905061448f81600084614401565b61449a600083613834565b6001600160a01b03811660009081526003602052604081208054600192906144c3908490615baa565b9091555050600082815260026020526040808220829055518391906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b60006001600160a01b0384163b1561460557604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290614556903390899088908890600401615ed1565b6020604051808303816000875af1925050508015614591575060408051601f3d908101601f1916820190925261458e91810190615f0e565b60015b6145eb573d8080156145bf576040519150601f19603f3d011682016040523d82523d6000602084013e6145c4565b606091505b5080516145e35760405162461bcd60e51b8152600401610e8790615e7f565b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050612025565b506001612025565b6001600160a01b0383166146685761466381600880546000838152600960205260408120829055600182018355919091527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30155565b61468b565b816001600160a01b0316836001600160a01b03161461468b5761468b83826146c5565b6001600160a01b0382166146a257610ce181614762565b826001600160a01b0316826001600160a01b031614610ce157610ce18282614811565b600060016146d284611ace565b6146dc9190615baa565b60008381526007602052604090205490915080821461472f576001600160a01b03841660009081526006602090815260408083208584528252808320548484528184208190558352600790915290208190555b5060009182526007602090815260408084208490556001600160a01b039094168352600681528383209183525290812055565b60085460009061477490600190615baa565b6000838152600960205260408120546008805493945090928490811061479c5761479c615554565b9060005260206000200154905080600883815481106147bd576147bd615554565b60009182526020808320909101929092558281526009909152604080822084905585825281205560088054806147f5576147f5615f2b565b6001900381819060005260206000200160009055905550505050565b600061481c83611ace565b6001600160a01b039093166000908152600660209081526040808320868452825280832085905593825260079052919091209190915550565b82805461486190615374565b90600052602060002090601f01602090048101928261488357600085556148c9565b82601f1061489c5782800160ff198235161785556148c9565b828001600101855582156148c9579182015b828111156148c95782358255916020019190600101906148ae565b506148d59291506148d9565b5090565b5b808211156148d557600081556001016148da565b6001600160e01b0319811681146117c557600080fd5b60006020828403121561491657600080fd5b8135611726816148ee565b60008083601f84011261493357600080fd5b5081356001600160401b0381111561494a57600080fd5b6020830191508360208285010111156119b957600080fd5b60008060006040848603121561497757600080fd5b8335925060208401356001600160401b0381111561499457600080fd5b6149a086828701614921565b9497909650939450505050565b60005b838110156149c85781810151838201526020016149b0565b83811115611f6a5750506000910152565b600081518084526149f18160208601602086016149ad565b601f01601f19169290920160200192915050565b60208152600061172660208301846149d9565b80356001600160a01b038116811461114657600080fd5b600080600080600060608688031215614a4757600080fd5b85356001600160401b0380821115614a5e57600080fd5b614a6a89838a01614921565b90975095506020880135915080821115614a8357600080fd5b50614a9088828901614921565b9094509250614aa3905060408701614a18565b90509295509295909350565b600060208284031215614ac157600080fd5b5035919050565b60008060408385031215614adb57600080fd5b614ae483614a18565b946020939093013593505050565b634e487b7160e01b600052602160045260246000fd5b6020810160038310614b1c57614b1c614af2565b91905290565b600060208284031215614b3457600080fd5b61172682614a18565b8035801515811461114657600080fd5b60008060408385031215614b6057600080fd5b614b6983614a18565b9150614b7760208401614b3d565b90509250929050565b60008060208385031215614b9357600080fd5b82356001600160401b03811115614ba957600080fd5b614bb585828601614921565b90969095509350505050565b600080600060608486031215614bd657600080fd5b614bdf84614a18565b9250614bed60208501614a18565b9150604084013590509250925092565b600060c08284031215614c0f57600080fd5b50919050565b600060208284031215614c2757600080fd5b81356001600160401b03811115614c3d57600080fd5b61202584828501614bfd565b60008060408385031215614c5c57600080fd5b50508035926020909101359150565b6020810160048310614b1c57614b1c614af2565b600060e08284031215614c0f57600080fd5b600060208284031215614ca357600080fd5b81356001600160401b03811115614cb957600080fd5b61202584828501614c7f565b60006101408284031215614c0f57600080fd5b600060208284031215614cea57600080fd5b81356001600160401b03811115614d0057600080fd5b61202584828501614cc5565b600060208284031215614d1e57600080fd5b81356003811061172657600080fd5b60008060008060608587031215614d4357600080fd5b84359350614d5360208601614a18565b925060408501356001600160401b03811115614d6e57600080fd5b614d7a87828801614921565b95989497509550505050565b600060208284031215614d9857600080fd5b81356001600160401b03811115614dae57600080fd5b82016101a0818503121561172657600080fd5b600060808284031215614c0f57600080fd5b600080600060c08486031215614de857600080fd5b614df184614a18565b925060208401359150614e078560408601614dc1565b90509250925092565b60008060008060608587031215614e2657600080fd5b843593506020850135925060408501356001600160401b03811115614d6e57600080fd5b600080600080600060a08688031215614e6257600080fd5b853594506020860135935060408601359250614e8060608701614a18565b9150614aa360808701614a18565b60008060008060e08587031215614ea457600080fd5b614ead85614a18565b9350614ebb60208601614a18565b9250614ec960408601614b3d565b9150614ed88660608701614dc1565b905092959194509250565b6020808252825182820181905260009190848201906040850190845b81811015614f1b57835183529284019291840191600101614eff565b50909695505050505050565b600060208284031215614f3957600080fd5b81356001600160401b03811115614f4f57600080fd5b8201610100818503121561172657600080fd5b600060208284031215614f7457600080fd5b81356001600160401b03811115614f8a57600080fd5b8201610120818503121561172657600080fd5b634e487b7160e01b600052604160045260246000fd5b60405160c081016001600160401b0381118282101715614fd557614fd5614f9d565b60405290565b60405161012081016001600160401b0381118282101715614fd557614fd5614f9d565b604051601f8201601f191681016001600160401b038111828210171561502657615026614f9d565b604052919050565b60006001600160401b0382111561504757615047614f9d565b50601f01601f191660200190565b600082601f83011261506657600080fd5b81356150796150748261502e565b614ffe565b81815284602083860101111561508e57600080fd5b816020850160208301376000918101602001919091529392505050565b600080600080608085870312156150c157600080fd5b6150ca85614a18565b93506150d860208601614a18565b92506040850135915060608501356001600160401b038111156150fa57600080fd5b61510687828801615055565b91505092959194509250565b6000806000806080858703121561512857600080fd5b843593506020850135925061513f60408601614a18565b9150614ed860608601614a18565b600060c0828403121561515f57600080fd5b6117268383614bfd565b6000806040838503121561517c57600080fd5b82359150614b7760208401614a18565b6020815281516020820152602082015160408201526000604083015160c060608401526151bc60e08401826149d9565b9050606084015160018060a01b0380821660808601528060808701511660a08601528060a08701511660c086015250508091505092915050565b60008060a0838503121561520957600080fd5b82359150614b778460208501614dc1565b6000806040838503121561522d57600080fd5b61523683614a18565b9150614b7760208401614a18565b60208152815160208201526000602083015160018060a01b0380821660408501528060408601511660608501525050606083015160c0608084015261528c60e08401826149d9565b90506080840151601f19808584030160a08601526152aa83836149d9565b925060a08601519150808584030160c0860152506152c882826149d9565b95945050505050565b60008083601f8401126152e357600080fd5b5081356001600160401b038111156152fa57600080fd5b6020830191508360208260051b85010111156119b957600080fd5b6000806000806040858703121561532b57600080fd5b84356001600160401b038082111561534257600080fd5b61534e888389016152d1565b9096509450602087013591508082111561536757600080fd5b50614d7a878288016152d1565b600181811c9082168061538857607f821691505b60208210811415614c0f57634e487b7160e01b600052602260045260246000fd5b8183823760009101908152919050565b60208082526031908201527f4552433732313a207472616e736665722063616c6c6572206973206e6f74206f6040820152701ddb995c881b9bdc88185c1c1c9bdd9959607a1b606082015260800190565b600060c0823603121561541c57600080fd5b615424614fb3565b82358152602083013560208201526040830135604082015260608301356001600160401b038082111561545657600080fd5b61546236838701615055565b606084015261547360808601614a18565b608084015260a085013591508082111561548c57600080fd5b5061549936828601615055565b60a08301525092915050565b6000808335601e198436030181126154bc57600080fd5b8301803591506001600160401b038211156154d657600080fd5b6020019150368190038213156119b957600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b8681526001600160a01b038616602082015260a06040820181905260009061553f90830186886154eb565b60608301949094525060800152949350505050565b634e487b7160e01b600052603260045260246000fd5b600060018060a01b03808b16835289602084015288604084015260e0606084015261559960e08401888a6154eb565b951660808301525060a081019290925260c09091015295945050505050565b6000602082840312156155ca57600080fd5b5051919050565b6000808335601e198436030181126155e857600080fd5b8301803591506001600160401b0382111561560257600080fd5b6020019150600581901b36038213156119b957600080fd5b60006001600160fb1b0383111561563057600080fd5b8260051b80858437600092019182525092915050565b815160009082906020808601845b8381101561567057815185529382019390820190600101615654565b50929695505050505050565b6000808335601e1984360301811261569357600080fd5b83016020810192503590506001600160401b038111156156b257600080fd5b8036038313156119b957600080fd5b6001600160a01b038816815260a060208083018290529082018790526000906001600160fb1b038811156156f457600080fd5b8760051b808a60c0860137830183810360c090810160408601528101879052600587901b810160e09081019082018960005b8a8110156157605784840360df19018352615741828d61567c565b61574c8682846154eb565b955050509185019190850190600101615726565b50505060608501969096525050506080015295945050505050565b6000602080838503121561578e57600080fd5b82516001600160401b03808211156157a557600080fd5b818501915085601f8301126157b957600080fd5b8151818111156157cb576157cb614f9d565b8060051b91506157dc848301614ffe565b81815291830184019184810190888411156157f657600080fd5b938501935b83851015615814578451825293850193908501906157fb565b98975050505050505050565b6000610120823603121561583357600080fd5b61583b614fdb565b8235815260208301356001600160401b038082111561585957600080fd5b61586536838701615055565b60208401526040850135604084015260608501356060840152608085013591508082111561589257600080fd5b61589e36838701615055565b60808401526158af60a08601614a18565b60a084015260c08501359150808211156158c857600080fd5b6158d436838701615055565b60c08401526158e560e08601614a18565b60e08401526101009150818501358181111561590057600080fd5b61590c36828801615055565b8385015250505080915050919050565b6000815461592981615374565b808552602060018381168015615946576001811461595a57615988565b60ff19851688840152604088019550615988565b866000528260002060005b858110156159805781548a8201860152908301908401615965565b890184019650505b505050505092915050565b85815284602082015260018060a01b038416604082015260a0606082015260006159c060a083018561591c565b8281036080840152615814818561591c565b6000602082840312156159e457600080fd5b81516001600160401b038111156159fa57600080fd5b8201601f81018413615a0b57600080fd5b8051615a196150748261502e565b818152856020838501011115615a2e57600080fd5b6152c88260208301602086016149ad565b60a0815260006001600160a01b0380615a5789614a18565b1660a0840152615a6a602089018961567c565b60c080860152615a7f610160860182846154eb565b915050615a8f60408a018a61567c565b609f19808785030160e0880152615aa78483856154eb565b935084615ab660608e01614a18565b16610100880152615aca60808d018d61567c565b955092508087850301610120880152615ae48486856154eb565b9450615af360a08d018d61567c565b9450925080878603016101408801525050615b0f8383836154eb565b93505050508560208301528460408301528360608301528260808301529695505050505050565b604081526000615b4a6040830185876154eb565b9050826020830152949350505050565b606081526000615b6e6060830187896154eb565b8281036020840152615b818186886154eb565b9150508260408301529695505050505050565b634e487b7160e01b600052601160045260246000fd5b600082821015615bbc57615bbc615b94565b500390565b60008219821115615bd457615bd4615b94565b500190565b6080815284516080820152602085015160a0820152604085015160c08201526000606086015160c060e0840152615c146101408401826149d9565b60808801516001600160a01b031661010085015260a0880151848203607f1901610120860152909150615c4782826149d9565b60208501979097525050506040810192909252606090910152919050565b600060208284031215615c7757600080fd5b813560ff8116811461172657600080fd5b60006101408c8352806020840152615ca28184018d6149d9565b6001600160a01b038c811660408601528482036060860152909150615cc7828c6149d9565b908a16608085015283810360a08501529050615ce381896149d9565b9150508560c08301528460e083015283610100830152826101208301529b9a5050505050505050505050565b6000808354615d1d81615374565b60018281168015615d355760018114615d4657615670565b60ff19841687528287019450615670565b8760005260208060002060005b85811015615d6c5781548a820152908401908201615d53565b505050959091019695505050505050565b60c08152865160c0820152600060208801516101208060e0850152615da66101e08501836149d9565b915060408a0151610100818187015260608c01518387015260808c0151925060bf1991508186850301610140870152615ddf84846149d9565b935060a08c01519250615dfe6101608701846001600160a01b03169052565b60c08c015192508186850301610180870152615e1a84846149d9565b935060e08c01519250615e396101a08701846001600160a01b03169052565b8b01518584039091016101c08601529050615e5482826149d9565b602085019990995250505060408101949094526060840192909252608083015260a090910152919050565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090615f04908301846149d9565b9695505050505050565b600060208284031215615f2057600080fd5b8151611726816148ee565b634e487b7160e01b600052603160045260246000fdfea2646970667358221220b22320e1e51caa663be0e995a3451f4dfdfa558ea5b06e71a5454229c1bc455b64736f6c634300080a0033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106104805760003560e01c806384114ad411610257578063bd12d3f011610146578063dd69cdb1116100c3578063f08f4f6411610087578063f08f4f6414610c21578063f1b2f8bc14610c41578063f990ccd714610c54578063fb78ae6c14610c74578063ffea138e14610c8757600080fd5b8063dd69cdb114610ba4578063df457c3414610bb7578063e985e9c514610bca578063ec81d19414610c06578063ed24911d14610c1957600080fd5b8063c6b5d06c1161010a578063c6b5d06c14610b1f578063c736c99014610b32578063c87b56dd14610b52578063d923d20c14610b65578063dc21725314610b9157600080fd5b8063bd12d3f014610a93578063bdfdd4bc14610aa6578063bfbf0b4b14610ab9578063bfd24f4714610acc578063c0da9bcd14610adf57600080fd5b8063a6d8e1e4116101d4578063b48951e411610198578063b48951e414610a10578063b5a3149614610a23578063b6f32d2b14610a36578063b7984c0514610a49578063b88d4fde14610a8057600080fd5b8063a6d8e1e414610966578063a9ec656314610979578063ab033ea9146109a5578063ad3e72bf146109b8578063af05dd22146109e457600080fd5b806392254a621161021b57806392254a62146108fc57806395d89b4114610925578063963ff1411461092d5780639b84a14c14610940578063a22cb4651461095357600080fd5b806384114ad41461087757806386e2947b1461088a57806389028a131461089d5780638e204fb4146108b05780638e4fd6a9146108dc57600080fd5b80633a15ff0711610373578063540528b9116102f05780636dea40b3116102b45780636dea40b31461080557806370a082311461081857806377349a5f1461082b5780637a375716146108515780637ef67f991461086457600080fd5b8063540528b91461075757806356de96db1461078057806357ff49f6146107935780635ca3eebf146107ca5780636352211e146107f257600080fd5b806342966c681161033757806342966c68146106d45780634f558e79146106e75780634f6ccce7146106fa57806350ddf35c1461070d57806352aaef551461072057600080fd5b80633a15ff07146106685780633b28b89f146106885780633b5081321461069b5780634187e4c5146106ae57806342842e0e146106c157600080fd5b806320fa728a1161040157806331dcebe3116103c557806331dcebe3146105e957806331fff07c146105fc5780633502ac4b1461061c57806335da339414610642578063374c94731461065557600080fd5b806320fa728a1461058c57806323b872dd1461059f578063289b3c0d146105b25780632f745c59146105c35780632faeed81146105d657600080fd5b8063095ea7b311610448578063095ea7b31461051557806318160ddd146105285780631865c57d1461053a5780631cbb26201461054d578063209055061461057957600080fd5b806301ffc9a714610485578063054871b8146104ad57806306fdde03146104c2578063077f224a146104d7578063081812fc146104ea575b600080fd5b610498610493366004614904565b610c9a565b60405190151581526020015b60405180910390f35b6104c06104bb366004614962565b610cc5565b005b6104ca610ce6565b6040516104a49190614a05565b6104c06104e5366004614a2f565b610d78565b6104fd6104f8366004614aaf565b610e12565b6040516001600160a01b0390911681526020016104a4565b6104c0610523366004614ac8565b610eac565b6008545b6040519081526020016104a4565b600c5460ff166040516104a49190614b08565b61049861055b366004614b22565b6001600160a01b03166000908152600e602052604090205460ff1690565b6104c0610587366004614b4d565b610fbd565b61052c61059a366004614b80565b611028565b6104c06105ad366004614bc1565b61105a565b6017546001600160a01b03166104fd565b61052c6105d1366004614ac8565b61108b565b61052c6105e4366004614c15565b611121565b6104c06105f7366004614b4d565b61114b565b61060f61060a366004614c49565b6111ae565b6040516104a49190614c6b565b7f00000000000000000000000000000000000000000000000000000000000000006104fd565b6104c0610650366004614b22565b611237565b6104ca610663366004614aaf565b6112a4565b61052c610676366004614aaf565b60009081526013602052604090205490565b6104c0610696366004614c91565b611349565b61052c6106a9366004614cd8565b6114e5565b6104c06106bc366004614b4d565b61172d565b6104c06106cf366004614bc1565b611790565b6104c06106e2366004614aaf565b6117ab565b6104986106f5366004614aaf565b6117c8565b61052c610708366004614aaf565b6117e7565b61052c61071b366004614aaf565b61187a565b6104fd61072e366004614c49565b60009182526014602090815260408084209284529190529020600501546001600160a01b031690565b6104fd610765366004614aaf565b6000908152601160205260409020546001600160a01b031690565b6104c061078e366004614d0c565b611910565b6104fd6107a1366004614c49565b60009182526014602090815260408084209284529190529020600401546001600160a01b031690565b6107dd6107d8366004614c49565b611997565b604080519283526020830191909152016104a4565b6104fd610800366004614aaf565b6119c0565b6104c0610813366004614d2d565b611a37565b61052c610826366004614b22565b611ace565b7f00000000000000000000000000000000000000000000000000000000000000006104fd565b61052c61085f366004614d86565b611b55565b6104c0610872366004614dd3565b611ea1565b61052c610885366004614e10565b611f70565b6104c0610898366004614e4a565b61202d565b6104c06108ab366004614e8e565b6120cb565b6104986108be366004614b22565b6001600160a01b031660009081526010602052604090205460ff1690565b6108ef6108ea366004614c91565b612190565b6040516104a49190614ee3565b61052c61090a366004614b22565b6001600160a01b031660009081526015602052604090205490565b6104ca612416565b61052c61093b366004614c15565b612425565b6104c061094e366004614c15565b6124ef565b6104c0610961366004614b4d565b6125cb565b6104c0610974366004614b4d565b61262f565b6104fd610987366004614aaf565b6000908152601360205260409020600201546001600160a01b031690565b6104c06109b3366004614b22565b612692565b6104986109c6366004614b22565b6001600160a01b03166000908152600f602052604090205460ff1690565b6104986109f2366004614b22565b6001600160a01b03166000908152600d602052604090205460ff1690565b61052c610a1e366004614f27565b6126a3565b6104ca610a31366004614c49565b61284a565b61052c610a44366004614f62565b612912565b6104fd610a57366004614c49565b60009182526014602090815260408084209284529190529020600301546001600160a01b031690565b6104c0610a8e3660046150ab565b612937565b6104c0610aa1366004614c15565b612969565b6104c0610ab4366004615112565b6129cc565b6104c0610ac736600461514d565b612a5b565b6104c0610ada366004615169565b612b1a565b610af2610aed366004614aaf565b612b35565b6040805182516001600160a01b031681526020928301516001600160601b031692810192909252016104a4565b6104c0610b2d366004614962565b612c03565b610b45610b40366004614c49565b612c1f565b6040516104a4919061518c565b6104ca610b60366004614aaf565b612d7e565b6104fd610b73366004614aaf565b6000908152601360205260409020600101546001600160a01b031690565b6104c0610b9f36600461514d565b612eb0565b6104c0610bb23660046151f6565b612f93565b61052c610bc5366004614cd8565b612fae565b610498610bd836600461521a565b6001600160a01b03918216600090815260056020908152604080832093909416825291909152205460ff1690565b6104ca610c14366004614aaf565b6131a5565b61052c6131c5565b610c34610c2f366004614aaf565b6131d4565b6040516104a49190615244565b6104c0610c4f366004614aaf565b613428565b61052c610c62366004614b22565b600a6020526000908152604090205481565b6108ef610c82366004615315565b61343a565b61052c610c95366004614c15565b6134ce565b60006001600160e01b0319821663780e9d6360e01b1480610cbf5750610cbf826135a2565b92915050565b610ccd6135f2565b610cd68361362c565b610ce183838361368b565b505050565b606060008054610cf590615374565b80601f0160208091040260200160405190810160405280929190818152602001828054610d2190615374565b8015610d6e5780601f10610d4357610100808354040283529160200191610d6e565b820191906000526020600020905b815481529060010190602001808311610d5157829003601f168201915b5050505050905090565b60017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316301415610dc4576040516325c7410560e21b815260040160405180910390fd5b600b548111610de6576040516302ed543d60e51b815260040160405180910390fd5b600b819055610df78686868661370c565b610e01600261375d565b610e0a826137db565b505050505050565b6000818152600260205260408120546001600160a01b0316610e905760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a20617070726f76656420717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b60648201526084015b60405180910390fd5b506000908152600460205260409020546001600160a01b031690565b6000610eb7826119c0565b9050806001600160a01b0316836001600160a01b03161415610f255760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b6064820152608401610e87565b336001600160a01b0382161480610f415750610f418133610bd8565b610fb35760405162461bcd60e51b815260206004820152603860248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f74206f7760448201527f6e6572206e6f7220617070726f76656420666f7220616c6c00000000000000006064820152608401610e87565b610ce18383613834565b610fc56138a2565b6001600160a01b0382166000818152600d6020908152604091829020805460ff191685151590811790915591514281529192917f8f617843889b94892bd44852d36ca6a7f49ecf4350a01e7b68e22d80f4ed95bc91015b60405180910390a35050565b600080838360405161103b9291906153a9565b6040805191829003909120600090815260126020522054949350505050565b61106433826138cd565b6110805760405162461bcd60e51b8152600401610e87906153b9565b610ce18383836139c0565b600061109683611ace565b82106110f85760405162461bcd60e51b815260206004820152602b60248201527f455243373231456e756d657261626c653a206f776e657220696e646578206f7560448201526a74206f6620626f756e647360a81b6064820152608401610e87565b506001600160a01b03919091166000908152600660209081526040808320938352929052205490565b600061112b613b6b565b611135823561362c565b610cbf6111418361540a565b613ba2565b919050565b6111536138a2565b6001600160a01b0382166000818152600f6020908152604091829020805460ff191685151590811790915591514281529192917f6cc19a794d6a439023150cd58748eed4353190c0bb060d2e6250e2df4a68b673910161101c565b60008115806111ca575060008381526013602052604090205482115b156111d757506003610cbf565b60008381526014602090815260408083208584529091529020600401546001600160a01b031661120957506002610cbf565b600083815260146020908152604080832085845290915290205461122f57506000610cbf565b506001610cbf565b61123f6138a2565b601880546001600160a01b038381166001600160a01b03198316811790935560405191169190829033907f676c0801b0f400762e958ee31cfbb10870e70786f6761f57c8647e766b0db3d9906112989042815260200190565b60405180910390a45050565b60008181526013602052604090206005018054606091906112c490615374565b80601f01602080910402602001604051908101604052809291908181526020018280546112f090615374565b801561133d5780601f106113125761010080835404028352916020019161133d565b820191906000526020600020905b81548152906001019060200180831161132057829003601f168201915b50505050509050919050565b6113516135f2565b600061135d82356119c0565b905061144761143d7f61332f0343256b019dca2e0ffc75766fde55292d645df285bb1a3b74a493b51084356113986040870160208801614b22565b6113a560408801886154a5565b6040516113b39291906153a9565b604080519182900382206001600160a01b0389166000908152600a6020908152929020805460018101909155611422969594939192909160c08c0135910195865260208601949094526001600160a01b039290921660408501526060840152608083015260a082015260c00190565b60405160208183030381529060405280519060200120613bfa565b8284606001613c44565b73__$1f7cbacb1f9f5d323b85b0487838426c8d$__636a7ecf1383356114736040860160208701614b22565b61148060408701876154a5565b87356000908152601360205260409081902090516001600160e01b031960e088901b1681526114b9959493929190600e90600401615514565b60006040518083038186803b1580156114d157600080fd5b505af4158015610e0a573d6000803e3d6000fd5b60006114ef613b6b565b60006114fb83356119c0565b905061163661162c7ffb2be94758c5f2515e99eae2d54860ea468ce314ffb38e328c234793acbcccf4853561153360208801886154a5565b6040516115419291906153a9565b604051809103902087604001602081019061155c9190614b22565b61156960608a018a6154a5565b6040516115779291906153a9565b60405190819003902061159060a08b0160808c01614b22565b61159d60a08c018c6154a5565b6040516115ab9291906153a9565b604080519182900382206001600160a01b03808d166000908152600a6020908152908490208054600181019091559085019a909a52918301979097526060820195909552928416608084015260a083019190915290911660c082015260e0810191909152610100810191909152610120808701359082015261014001611422565b828560c001613c44565b611726833561164860208601866154a5565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061168d925050506060870160408801614b22565b61169a60608801886154a5565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506116df9250505060a0890160808a01614b22565b6116ec60a08a018a6154a5565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613d2292505050565b9392505050565b6117356138a2565b6001600160a01b038216600081815260106020908152604091829020805460ff191685151590811790915591514281529192917f37872a053ef20cb52defb7c9ec20e1a87cb8dd5098ac9e76a144be263dfef572910161101c565b610ce183838360405180602001604052806000815250612937565b6117b36135f2565b6117bc81613dc1565b6117c581613df1565b50565b6000818152600260205260408120546001600160a01b03161515610cbf565b60006117f260085490565b82106118555760405162461bcd60e51b815260206004820152602c60248201527f455243373231456e756d657261626c653a20676c6f62616c20696e646578206f60448201526b7574206f6620626f756e647360a01b6064820152608401610e87565b6008828154811061186857611868615554565b90600052602060002001549050919050565b600081815260026020526040812054600160a01b90046001600160601b0316806119015760405162461bcd60e51b815260206004820152603260248201527f4552433732313a206d696e742074696d657374616d7020717565727920666f72604482015271103737b732bc34b9ba32b73a103a37b5b2b760711b6064820152608401610e87565b6001600160601b031692915050565b6018546001600160a01b031633141561196357600081600281111561193757611937614af2565b141561195657604051630357a1ab60e31b815260040160405180910390fd5b61195e6135f2565b61198e565b6017546001600160a01b0316331461198e57604051635010559960e11b815260040160405180910390fd5b6117c58161375d565b6000828152601460209081526040808320848452909152902080546001909101545b9250929050565b6000818152600260205260408120546001600160a01b031680610cbf5760405162461bcd60e51b815260206004820152602960248201527f4552433732313a206f776e657220717565727920666f72206e6f6e657869737460448201526832b73a103a37b5b2b760b91b6064820152608401610e87565b611a3f6135f2565b611a4884613e2c565b600084815260136020526040908190209051636a7ecf1360e01b815273__$1f7cbacb1f9f5d323b85b0487838426c8d$__91636a7ecf1391611a9891889188918891889190600e90600401615514565b60006040518083038186803b158015611ab057600080fd5b505af4158015611ac4573d6000803e3d6000fd5b5050505050505050565b60006001600160a01b038216611b395760405162461bcd60e51b815260206004820152602a60248201527f4552433732313a2062616c616e636520717565727920666f7220746865207a65604482015269726f206164647265737360b01b6064820152608401610e87565b506001600160a01b031660009081526003602052604090205490565b6000611b5f613b6b565b6000611b6b83356119c0565b9050611d14611d097f35b861ea91c543d61c3f094b5effa29dfcd22a439290ae248fdccbc7162016918535611ba360208801886154a5565b604051611bb19291906153a9565b604051809103902087604001358860600135898060800190611bd391906154a5565b604051611be19291906153a9565b604051908190039020611bfa60c08c0160a08d01614b22565b611c0760c08d018d6154a5565b604051611c159291906153a9565b604051908190039020611c2f6101008e0160e08f01614b22565b611c3d6101008f018f6154a5565b604051611c4b9291906153a9565b6040518091039020600a60008e6001600160a01b03166001600160a01b031681526020019081526020016000206000815480929190600101919050558f61012001606001356040516020016114229c9b9a999897969594939291909b8c5260208c019a909a5260408b019890985260608a0196909652608089019490945260a08801929092526001600160a01b0390811660c088015260e0870191909152166101008501526101208401526101408301526101608201526101800190565b828561012001613c44565b61172660405180610120016040528085600001358152602001858060200190611d3d91906154a5565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509082525060408087013560208301526060808801359183019190915201611d9a60808701876154a5565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250505090825250602001611de460c0870160a08801614b22565b6001600160a01b03168152602001611dff60c08701876154a5565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250505090825250602001611e4a610100870160e08801614b22565b6001600160a01b03168152602001611e666101008701876154a5565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050915250613e66565b6001600160a01b038316611ec8576040516307eb16dd60e21b815260040160405180910390fd5b6000611ed3836119c0565b6001600160a01b038181166000908152600a602090815260409182902080546001810190915582517f49ecf333e5b8c95c40fdafc95c1ad136e8914a8fb55e9dc8bb01eaa83a2df9ad9281019290925292881691810191909152606080820187905260808201929092529084013560a0820152909150611f6090611f599060c001611422565b8284613c44565b611f6a8484613834565b50505050565b6000611f7a6135f2565b60405163334ce6f160e21b815273__$1e68a60ae0444699fe08192a29ecc09930$__9063cd339bc490611fe190339089908990899089907f00000000000000000000000000000000000000000000000000000000000000009060149060139060040161556a565b602060405180830381865af4158015611ffe573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061202291906155b8565b90505b949350505050565b60008581526014602090815260408083208784529091529020600501546001600160a01b03163381146120735760405163c6d1651b60e01b815260040160405180910390fd5b604080516001600160a01b03858116825284166020820152428183015290518591879189917f68edb7ec2c37d21b3b72233960b487f2966f4ac82b7430d39f24d1f8d6f99106919081900360600190a4505050505050565b6001600160a01b0383166120f2576040516307eb16dd60e21b815260040160405180910390fd5b6001600160a01b038481166000818152600a602090815260409182902080546001810190915582517f47ab88482c90e4bb94b82a947ae78fa91fb25de1469ab491f4c15b9a0a2677ee9281019290925291810192909252918516606080830191909152841515608083015260a08201929092529082013560c08201526121859061217e9060e001611422565b8583613c44565b611f6a848484613ec5565b606061219a6135f2565b60006121a960408401846155d1565b905090506000816001600160401b038111156121c7576121c7614f9d565b6040519080825280602002602001820160405280156121f0578160200160208202803683370190505b50905060005b828110156122685761220b60408601866155d1565b8281811061221b5761221b615554565b905060200281019061222d91906154a5565b60405161223b9291906153a9565b604051809103902082828151811061225557612255615554565b60209081029190910101526001016121f6565b5061236a6123547ffb6b7f1cd1b38daf3822aff0abbe78124db5d62a4748bcff007c15ccd6d30bc561229d60208801886155d1565b6040516020016122ae92919061561a565b60405160208183030381529060405280519060200120846040516020016122d59190615646565b60405160208183030381529060405280519060200120600a60008a60000160208101906123029190614b22565b6001600160a01b0316815260208082019290925260409081016000208054600181019091558151928301959095528101929092526060820152608081019190915260c08781013560a083015201611422565b6123616020870187614b22565b86606001613c44565b73__$1e68a60ae0444699fe08192a29ecc09930$__636fb4b4636123916020870187614b22565b61239e60208801886155d1565b6123ab60408a018a6155d1565b601360126040518863ffffffff1660e01b81526004016123d197969594939291906156c1565b600060405180830381865af41580156123ee573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612025919081019061577b565b606060018054610cf590615374565b600061242f613b6b565b612439823561362c565b610cbf823561244b60208501856154a5565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250612490925050506060860160408701614b22565b61249d60608701876154a5565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506124e29250505060a0880160808901614b22565b6116ec60a08901896154a5565b6124f76135f2565b600061250382356119c0565b90506125b06125a67f5b9860bd835e648945b22d053515bc1f53b7d9fab4b23b1b49db15722e945d14843561253b60208701876154a5565b6040516125499291906153a9565b604080519182900382206001600160a01b0388166000908152600a60209081529083902080546001810190915590840195909552908201929092526060810191909152608081019190915260a0858101359082015260c001611422565b8284604001613c44565b6125c782356125c260208501856154a5565b61368b565b5050565b6001600160a01b0382163314156126245760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c6572000000000000006044820152606401610e87565b6125c7338383613ec5565b6126376138a2565b6001600160a01b0382166000818152600e6020908152604091829020805460ff191685151590811790915591514281529192917f52c5b7889df9f12f84ec3da051e854e5876678370d8357959c23ef59dd6486df910161101c565b61269a6138a2565b6117c5816137db565b60006126ad6135f2565b61278361276d7f7f9b4ea1fc678b4fda1611ac5cbd28f339e235d89b1540635e9b2e0223a3c101602085013560408601356126eb60608801886154a5565b6040516126f99291906153a9565b604051908190039020600a600061271360208b018b614b22565b6001600160a01b0316815260208082019290925260409081016000208054600181019091558151928301969096528101939093526060830191909152608082015260a081019190915260e08581013560c083015201611422565b61277a6020850185614b22565b84608001613c44565b73__$1e68a60ae0444699fe08192a29ecc09930$__63cd339bc46127aa6020850185614b22565b602085013560408601356127c160608801886154a5565b7f0000000000000000000000000000000000000000000000000000000000000000601460136040518963ffffffff1660e01b815260040161280998979695949392919061556a565b602060405180830381865af4158015612826573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cbf91906155b8565b606060008061285b85856014613f32565b506000828152601460209081526040808320848452909152902060020180549294509092509061288a90615374565b80601f01602080910402602001604051908101604052809291908181526020018280546128b690615374565b80156129035780601f106128d857610100808354040283529160200191612903565b820191906000526020600020905b8154815290600101906020018083116128e657829003601f168201915b50505050509250505092915050565b600061291c613b6b565b612926823561362c565b610cbf61293283615820565b613e66565b61294133836138cd565b61295d5760405162461bcd60e51b8152600401610e87906153b9565b611f6a84848484613ff4565b6129716135f2565b600061297d82356119c0565b90506129b56125a67fd8d76e8b2b26e1ebe72def13fb559a68561ef064055b0de01f955bc26e25d42f843561253b60208701876154a5565b6125c782356129c760208501856154a5565b614027565b6000848152601360205260409020600201546001600160a01b0316338114612a075760405163646785d560e11b815260040160405180910390fd5b604080516001600160a01b0385811682528416602082015242818301529051859187917f4996ad2257e7db44908136c43128cc10ca988096f67dc6bb0bcee11d151368fb9181900360600190a35050505050565b612a636135f2565b6000612a6f82356119c0565b9050612b006125a67f77ba3e9f5fa75343bbad1241fb539a0064de97694b47d463d1eb5c54aba11f0f8435612aaa6040870160208801614b22565b6001600160a01b038681166000908152600a602090815260409182902080546001810190915582519182019690965290810193909352166060820152608081019190915260a0858101359082015260c001611422565b6125c78235612b156040850160208601614b22565b614078565b612b226135f2565b612b2b82613e2c565b6125c78282614078565b60408051808201909152600080825260208201526000828152600260205260409020546001600160a01b0316612bc45760405162461bcd60e51b815260206004820152602e60248201527f4552433732313a20746f6b656e206461746120717565727920666f72206e6f6e60448201526d32bc34b9ba32b73a103a37b5b2b760911b6064820152608401610e87565b506000908152600260209081526040918290208251808401909352546001600160a01b0381168352600160a01b90046001600160601b03169082015290565b612c0b6135f2565b612c148361362c565b610ce1838383614027565b612c736040518060c0016040528060008152602001600081526020016060815260200160006001600160a01b0316815260200160006001600160a01b0316815260200160006001600160a01b031681525090565b6014600084815260200190815260200160002060008381526020019081526020016000206040518060c00160405290816000820154815260200160018201548152602001600282018054612cc690615374565b80601f0160208091040260200160405190810160405280929190818152602001828054612cf290615374565b8015612d3f5780601f10612d1457610100808354040283529160200191612d3f565b820191906000526020600020905b815481529060010190602001808311612d2257829003601f168201915b505050918352505060038201546001600160a01b0390811660208301526004830154811660408301526005909201549091166060909101529392505050565b6000818152601360205260409020600201546060906001600160a01b031673__$f906f20d797116ee89ed79945048c6ad36$__633edea73c848315612e2457836001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612dfb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e1f91906155b8565b612e27565b60005b612e30876119c0565b6000888152601360205260409081902090516001600160e01b031960e087901b168152612e6b94939291600381019160049182019101615993565b600060405180830381865af4158015612e88573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261172691908101906159d2565b612eb86135f2565b612f79612f637e198a25e19615d82f53977db0fc1ee7f3701b0fdf1299a971cf997545879c04612eeb6020850185614b22565b60208501803590600a90600090612f029089614b22565b6001600160a01b03168152602080820192909252604090810160002080546001810190915590516114229594939260a08a013591019485526001600160a01b0393909316602085015260408401919091526060830152608082015260a00190565b612f706020840184614b22565b83604001613c44565b6117c5612f896020830183614b22565b82602001356140d5565b612f9b6135f2565b612fa5828261416d565b6125c782613df1565b6000612fb8613b6b565b6000612fc483356119c0565b90506130c961162c7f4178441487eb967c15821c9ffee94853812f263a69ccb6352bdaac59f77e486f85356020870135604088013561300660608a018a6154a5565b6040516130149291906153a9565b60405190819003902061302d60a08b0160808c01614b22565b61303a60a08c018c6154a5565b6040516130489291906153a9565b604080519182900382206001600160a01b03808d166000908152600a6020908152908490208054600181019091559085019a909a52918301979097526060820195909552608081019390935260a083019190915290911660c082015260e0810191909152610100810191909152610120808701359082015261014001611422565b6117266040518060c0016040528085600001358152602001856020013581526020018560400135815260200185806060019061310591906154a5565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509082525060200161314f60a0870160808801614b22565b6001600160a01b0316815260200161316a60a08701876154a5565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050915250613ba2565b60008181526013602052604090206003018054606091906112c490615374565b60006131cf6141fb565b905090565b61321f6040518060c001604052806000815260200160006001600160a01b0316815260200160006001600160a01b031681526020016060815260200160608152602001606081525090565b600082815260136020908152604091829020825160c0810184528154815260018201546001600160a01b039081169382019390935260028201549092169282019290925260038201805491929160608401919061327b90615374565b80601f01602080910402602001604051908101604052809291908181526020018280546132a790615374565b80156132f45780601f106132c9576101008083540402835291602001916132f4565b820191906000526020600020905b8154815290600101906020018083116132d757829003601f168201915b5050505050815260200160048201805461330d90615374565b80601f016020809104026020016040519081016040528092919081815260200182805461333990615374565b80156133865780601f1061335b57610100808354040283529160200191613386565b820191906000526020600020905b81548152906001019060200180831161336957829003601f168201915b5050505050815260200160058201805461339f90615374565b80601f01602080910402602001604051908101604052809291908181526020018280546133cb90615374565b80156134185780601f106133ed57610100808354040283529160200191613418565b820191906000526020600020905b8154815290600101906020018083116133fb57829003601f168201915b5050505050815250509050919050565b6134306135f2565b6117c533826140d5565b60606134446135f2565b604051636fb4b46360e01b815273__$1e68a60ae0444699fe08192a29ecc09930$__90636fb4b4639061348990339089908990899089906013906012906004016156c1565b600060405180830381865af41580156134a6573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612022919081019061577b565b60006134d86135f2565b336000908152600d602052604090205460ff166135085760405163561a858760e01b815260040160405180910390fd5b60168054600101908190556135296135236020850185614b22565b82614290565b604051633f67a66f60e11b815273__$1f7cbacb1f9f5d323b85b0487838426c8d$__90637ecf4cde9061356b9086908590601290601390600e90600401615a3f565b60006040518083038186803b15801561358357600080fd5b505af4158015613597573d6000803e3d6000fd5b509295945050505050565b60006001600160e01b031982166380ac58cd60e01b14806135d357506001600160e01b03198216635b5e139f60e01b145b80610cbf57506301ffc9a760e01b6001600160e01b0319831614610cbf565b6002600c5460ff16600281111561360b5761360b614af2565b141561362a576040516313d0ff5960e31b815260040160405180910390fd5b565b613635816119c0565b6001600160a01b0316336001600160a01b0316148061366a57506000818152601160205260409020546001600160a01b031633145b156136725750565b60405163f33ac98f60e01b815260040160405180910390fd5b6117708111156136ae5760405163781548c760e01b815260040160405180910390fd5b60008381526013602052604090206136ca906004018383614855565b50827fd5a5879cad33c830cc1432c1850107029a09c80c60e9bce1ecd08d24880bd46c8383426040516136ff93929190615b36565b60405180910390a2505050565b613718848484846143e1565b7f414cd0b34676984f09a5f76ce9718d4062e50283abe0e7e274a9a5b4e0c99c30848484844260405161374f959493929190615b5a565b60405180910390a150505050565b600c805460ff811691839160ff1916600183600281111561378057613780614af2565b021790555081600281111561379757613797614af2565b8160028111156137a9576137a9614af2565b60405142815233907fa2f9a1499fc1f9b7796d21fe5761290ccb7e0ef6ccf35fa58b668f304a62a1ca90602001611298565b601780546001600160a01b038381166001600160a01b03198316811790935560405191169190829033907fe552a55455b740845a5c07ed445d1724142fc997b389835495a29b30cddc1ccd906112989042815260200190565b600081815260046020526040902080546001600160a01b0319166001600160a01b0384169081179091558190613869826119c0565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6017546001600160a01b0316331461362a57604051632d5be4cb60e21b815260040160405180910390fd5b6000818152600260205260408120546001600160a01b03166139465760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a206f70657261746f7220717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b6064820152608401610e87565b6000613951836119c0565b9050806001600160a01b0316846001600160a01b0316148061398c5750836001600160a01b031661398184610e12565b6001600160a01b0316145b8061202557506001600160a01b0380821660009081526005602090815260408083209388168352929052205460ff16612025565b826001600160a01b03166139d3826119c0565b6001600160a01b031614613a3b5760405162461bcd60e51b815260206004820152602960248201527f4552433732313a207472616e73666572206f6620746f6b656e2074686174206960448201526839903737ba1037bbb760b91b6064820152608401610e87565b6001600160a01b038216613a9d5760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b6064820152608401610e87565b613aa8838383614401565b613ab3600082613834565b6001600160a01b0383166000908152600360205260408120805460019290613adc908490615baa565b90915550506001600160a01b0382166000908152600360205260408120805460019290613b0a908490615bc1565b909155505060008181526002602052604080822080546001600160a01b0319166001600160a01b0386811691821790925591518493918716917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b6000600c5460ff166002811115613b8457613b84614af2565b1461362a57604051630f392a3b60e31b815260040160405180910390fd5b8051600090815260136020526040808220805460010190819055905163764e3e9360e01b815273__$1f7cbacb1f9f5d323b85b0487838426c8d$__9063764e3e939061356b9086908590601490601090600401615bd9565b600080613c056141fb565b60405161190160f01b602082015260228101919091526042810184905260620160408051601f1981840301815291905280516020909101209392505050565b4281606001351015613c6957604051630819bdcd60e01b815260040160405180910390fd5b6000600184613c7b6020850185615c65565b604080516000815260208181018084529490945260ff9092168282015291850135606082015290840135608082015260a0016020604051602081039080840390855afa158015613ccf573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381161580613d045750826001600160a01b0316816001600160a01b031614155b15611f6a576040516337e8456b60e01b815260040160405180910390fd5b60008681526013602052604080822080546001019081905590516309dbf05160e41b815273__$1f7cbacb1f9f5d323b85b0487838426c8d$__90639dbf051090613d85908b908b908b908b908b908b908a90601490600f90601090600401615c88565b60006040518083038186803b158015613d9d57600080fd5b505af4158015613db1573d6000803e3d6000fd5b50929a9950505050505050505050565b613dcb33826138cd565b613de857604051636d8a29e760e11b815260040160405180910390fd5b6117c581614476565b6000818152601360205260408082209051613e0f9160030190615d0f565b604080519182900390912060009081526012602052908120555050565b613e35816119c0565b6001600160a01b0316336001600160a01b0316146117c55760405163f194fae560e01b815260040160405180910390fd5b80516000908152601360208190526040808320805460010190819055905163731d549f60e11b8152909173__$1f7cbacb1f9f5d323b85b0487838426c8d$__9163e63aa93e9161356b9187918691601490600f90601090600401615d7d565b6001600160a01b03838116600081815260056020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b600083815260208281526040808320858452909152812060040154819081906001600160a01b03168015613f6d578693508592509050613feb565b60008781526020868152604080832089845290915290205480613fa35760405163a43d2a7160e01b815260040160405180910390fd5b6000888152602087815260408083208a84528252808320600101548484528983528184208185529092529091206004015491955093506001600160a01b03169150613feb9050565b93509350939050565b613fff8484846139c0565b61400b84848484614512565b611f6a5760405162461bcd60e51b8152600401610e8790615e7f565b6000838152601360205260409020614043906005018383614855565b50827fe82886e1af6fcab5caef13815b22f51384e970c367a785f265d13860a7d6966d8383426040516136ff93929190615b36565b60008281526011602090815260409182902080546001600160a01b0319166001600160a01b038516908117909155915142815284917f22baaec4952f35f59e45bd2ddb287e1ccc6d319375770c09428eb8f8d604e065910161101c565b6000811180156140ff57506140e9816119c0565b6001600160a01b0316826001600160a01b031614155b1561411d5760405163f194fae560e01b815260040160405180910390fd5b6001600160a01b03821660008181526015602052604090819020839055518291907f0afd7c479e8bc7dcdb856b3cc27d2332dfe1f018fde574ea124919ddcae8a9339061101c9042815260200190565b6000614178836119c0565b6001600160a01b0381166000908152600a602090815260409182902080546001810190915582517f108ccda6d7331b00561a3eea66a2ae331622356585681c62731e4a01aae2261a928101929092529181018690526060808201929092529084013560808201529091506141f290611f599060a001611422565b610ce183614476565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f614226610ce6565b80516020918201206040805192830193909352918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b6001600160a01b0382166142e65760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152606401610e87565b6000818152600260205260409020546001600160a01b03161561434b5760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606401610e87565b61435760008383614401565b6001600160a01b0382166000908152600360205260408120805460019290614380908490615bc1565b90915550506000818152600260205260408082206001600160a01b038516600160a01b426001600160601b031602811790915590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b6143ed60008585614855565b506143fa60018383614855565b5050505050565b6144096135f2565b6000818152601160205260409020546001600160a01b03161561443157614431816000614078565b6001600160a01b03831660009081526015602052604090205481141561446b576001600160a01b0383166000908152601560205260408120555b610ce183838361460d565b6000614481826119c0565b905061448f81600084614401565b61449a600083613834565b6001600160a01b03811660009081526003602052604081208054600192906144c3908490615baa565b9091555050600082815260026020526040808220829055518391906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b60006001600160a01b0384163b1561460557604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290614556903390899088908890600401615ed1565b6020604051808303816000875af1925050508015614591575060408051601f3d908101601f1916820190925261458e91810190615f0e565b60015b6145eb573d8080156145bf576040519150601f19603f3d011682016040523d82523d6000602084013e6145c4565b606091505b5080516145e35760405162461bcd60e51b8152600401610e8790615e7f565b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050612025565b506001612025565b6001600160a01b0383166146685761466381600880546000838152600960205260408120829055600182018355919091527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30155565b61468b565b816001600160a01b0316836001600160a01b03161461468b5761468b83826146c5565b6001600160a01b0382166146a257610ce181614762565b826001600160a01b0316826001600160a01b031614610ce157610ce18282614811565b600060016146d284611ace565b6146dc9190615baa565b60008381526007602052604090205490915080821461472f576001600160a01b03841660009081526006602090815260408083208584528252808320548484528184208190558352600790915290208190555b5060009182526007602090815260408084208490556001600160a01b039094168352600681528383209183525290812055565b60085460009061477490600190615baa565b6000838152600960205260408120546008805493945090928490811061479c5761479c615554565b9060005260206000200154905080600883815481106147bd576147bd615554565b60009182526020808320909101929092558281526009909152604080822084905585825281205560088054806147f5576147f5615f2b565b6001900381819060005260206000200160009055905550505050565b600061481c83611ace565b6001600160a01b039093166000908152600660209081526040808320868452825280832085905593825260079052919091209190915550565b82805461486190615374565b90600052602060002090601f01602090048101928261488357600085556148c9565b82601f1061489c5782800160ff198235161785556148c9565b828001600101855582156148c9579182015b828111156148c95782358255916020019190600101906148ae565b506148d59291506148d9565b5090565b5b808211156148d557600081556001016148da565b6001600160e01b0319811681146117c557600080fd5b60006020828403121561491657600080fd5b8135611726816148ee565b60008083601f84011261493357600080fd5b5081356001600160401b0381111561494a57600080fd5b6020830191508360208285010111156119b957600080fd5b60008060006040848603121561497757600080fd5b8335925060208401356001600160401b0381111561499457600080fd5b6149a086828701614921565b9497909650939450505050565b60005b838110156149c85781810151838201526020016149b0565b83811115611f6a5750506000910152565b600081518084526149f18160208601602086016149ad565b601f01601f19169290920160200192915050565b60208152600061172660208301846149d9565b80356001600160a01b038116811461114657600080fd5b600080600080600060608688031215614a4757600080fd5b85356001600160401b0380821115614a5e57600080fd5b614a6a89838a01614921565b90975095506020880135915080821115614a8357600080fd5b50614a9088828901614921565b9094509250614aa3905060408701614a18565b90509295509295909350565b600060208284031215614ac157600080fd5b5035919050565b60008060408385031215614adb57600080fd5b614ae483614a18565b946020939093013593505050565b634e487b7160e01b600052602160045260246000fd5b6020810160038310614b1c57614b1c614af2565b91905290565b600060208284031215614b3457600080fd5b61172682614a18565b8035801515811461114657600080fd5b60008060408385031215614b6057600080fd5b614b6983614a18565b9150614b7760208401614b3d565b90509250929050565b60008060208385031215614b9357600080fd5b82356001600160401b03811115614ba957600080fd5b614bb585828601614921565b90969095509350505050565b600080600060608486031215614bd657600080fd5b614bdf84614a18565b9250614bed60208501614a18565b9150604084013590509250925092565b600060c08284031215614c0f57600080fd5b50919050565b600060208284031215614c2757600080fd5b81356001600160401b03811115614c3d57600080fd5b61202584828501614bfd565b60008060408385031215614c5c57600080fd5b50508035926020909101359150565b6020810160048310614b1c57614b1c614af2565b600060e08284031215614c0f57600080fd5b600060208284031215614ca357600080fd5b81356001600160401b03811115614cb957600080fd5b61202584828501614c7f565b60006101408284031215614c0f57600080fd5b600060208284031215614cea57600080fd5b81356001600160401b03811115614d0057600080fd5b61202584828501614cc5565b600060208284031215614d1e57600080fd5b81356003811061172657600080fd5b60008060008060608587031215614d4357600080fd5b84359350614d5360208601614a18565b925060408501356001600160401b03811115614d6e57600080fd5b614d7a87828801614921565b95989497509550505050565b600060208284031215614d9857600080fd5b81356001600160401b03811115614dae57600080fd5b82016101a0818503121561172657600080fd5b600060808284031215614c0f57600080fd5b600080600060c08486031215614de857600080fd5b614df184614a18565b925060208401359150614e078560408601614dc1565b90509250925092565b60008060008060608587031215614e2657600080fd5b843593506020850135925060408501356001600160401b03811115614d6e57600080fd5b600080600080600060a08688031215614e6257600080fd5b853594506020860135935060408601359250614e8060608701614a18565b9150614aa360808701614a18565b60008060008060e08587031215614ea457600080fd5b614ead85614a18565b9350614ebb60208601614a18565b9250614ec960408601614b3d565b9150614ed88660608701614dc1565b905092959194509250565b6020808252825182820181905260009190848201906040850190845b81811015614f1b57835183529284019291840191600101614eff565b50909695505050505050565b600060208284031215614f3957600080fd5b81356001600160401b03811115614f4f57600080fd5b8201610100818503121561172657600080fd5b600060208284031215614f7457600080fd5b81356001600160401b03811115614f8a57600080fd5b8201610120818503121561172657600080fd5b634e487b7160e01b600052604160045260246000fd5b60405160c081016001600160401b0381118282101715614fd557614fd5614f9d565b60405290565b60405161012081016001600160401b0381118282101715614fd557614fd5614f9d565b604051601f8201601f191681016001600160401b038111828210171561502657615026614f9d565b604052919050565b60006001600160401b0382111561504757615047614f9d565b50601f01601f191660200190565b600082601f83011261506657600080fd5b81356150796150748261502e565b614ffe565b81815284602083860101111561508e57600080fd5b816020850160208301376000918101602001919091529392505050565b600080600080608085870312156150c157600080fd5b6150ca85614a18565b93506150d860208601614a18565b92506040850135915060608501356001600160401b038111156150fa57600080fd5b61510687828801615055565b91505092959194509250565b6000806000806080858703121561512857600080fd5b843593506020850135925061513f60408601614a18565b9150614ed860608601614a18565b600060c0828403121561515f57600080fd5b6117268383614bfd565b6000806040838503121561517c57600080fd5b82359150614b7760208401614a18565b6020815281516020820152602082015160408201526000604083015160c060608401526151bc60e08401826149d9565b9050606084015160018060a01b0380821660808601528060808701511660a08601528060a08701511660c086015250508091505092915050565b60008060a0838503121561520957600080fd5b82359150614b778460208501614dc1565b6000806040838503121561522d57600080fd5b61523683614a18565b9150614b7760208401614a18565b60208152815160208201526000602083015160018060a01b0380821660408501528060408601511660608501525050606083015160c0608084015261528c60e08401826149d9565b90506080840151601f19808584030160a08601526152aa83836149d9565b925060a08601519150808584030160c0860152506152c882826149d9565b95945050505050565b60008083601f8401126152e357600080fd5b5081356001600160401b038111156152fa57600080fd5b6020830191508360208260051b85010111156119b957600080fd5b6000806000806040858703121561532b57600080fd5b84356001600160401b038082111561534257600080fd5b61534e888389016152d1565b9096509450602087013591508082111561536757600080fd5b50614d7a878288016152d1565b600181811c9082168061538857607f821691505b60208210811415614c0f57634e487b7160e01b600052602260045260246000fd5b8183823760009101908152919050565b60208082526031908201527f4552433732313a207472616e736665722063616c6c6572206973206e6f74206f6040820152701ddb995c881b9bdc88185c1c1c9bdd9959607a1b606082015260800190565b600060c0823603121561541c57600080fd5b615424614fb3565b82358152602083013560208201526040830135604082015260608301356001600160401b038082111561545657600080fd5b61546236838701615055565b606084015261547360808601614a18565b608084015260a085013591508082111561548c57600080fd5b5061549936828601615055565b60a08301525092915050565b6000808335601e198436030181126154bc57600080fd5b8301803591506001600160401b038211156154d657600080fd5b6020019150368190038213156119b957600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b8681526001600160a01b038616602082015260a06040820181905260009061553f90830186886154eb565b60608301949094525060800152949350505050565b634e487b7160e01b600052603260045260246000fd5b600060018060a01b03808b16835289602084015288604084015260e0606084015261559960e08401888a6154eb565b951660808301525060a081019290925260c09091015295945050505050565b6000602082840312156155ca57600080fd5b5051919050565b6000808335601e198436030181126155e857600080fd5b8301803591506001600160401b0382111561560257600080fd5b6020019150600581901b36038213156119b957600080fd5b60006001600160fb1b0383111561563057600080fd5b8260051b80858437600092019182525092915050565b815160009082906020808601845b8381101561567057815185529382019390820190600101615654565b50929695505050505050565b6000808335601e1984360301811261569357600080fd5b83016020810192503590506001600160401b038111156156b257600080fd5b8036038313156119b957600080fd5b6001600160a01b038816815260a060208083018290529082018790526000906001600160fb1b038811156156f457600080fd5b8760051b808a60c0860137830183810360c090810160408601528101879052600587901b810160e09081019082018960005b8a8110156157605784840360df19018352615741828d61567c565b61574c8682846154eb565b955050509185019190850190600101615726565b50505060608501969096525050506080015295945050505050565b6000602080838503121561578e57600080fd5b82516001600160401b03808211156157a557600080fd5b818501915085601f8301126157b957600080fd5b8151818111156157cb576157cb614f9d565b8060051b91506157dc848301614ffe565b81815291830184019184810190888411156157f657600080fd5b938501935b83851015615814578451825293850193908501906157fb565b98975050505050505050565b6000610120823603121561583357600080fd5b61583b614fdb565b8235815260208301356001600160401b038082111561585957600080fd5b61586536838701615055565b60208401526040850135604084015260608501356060840152608085013591508082111561589257600080fd5b61589e36838701615055565b60808401526158af60a08601614a18565b60a084015260c08501359150808211156158c857600080fd5b6158d436838701615055565b60c08401526158e560e08601614a18565b60e08401526101009150818501358181111561590057600080fd5b61590c36828801615055565b8385015250505080915050919050565b6000815461592981615374565b808552602060018381168015615946576001811461595a57615988565b60ff19851688840152604088019550615988565b866000528260002060005b858110156159805781548a8201860152908301908401615965565b890184019650505b505050505092915050565b85815284602082015260018060a01b038416604082015260a0606082015260006159c060a083018561591c565b8281036080840152615814818561591c565b6000602082840312156159e457600080fd5b81516001600160401b038111156159fa57600080fd5b8201601f81018413615a0b57600080fd5b8051615a196150748261502e565b818152856020838501011115615a2e57600080fd5b6152c88260208301602086016149ad565b60a0815260006001600160a01b0380615a5789614a18565b1660a0840152615a6a602089018961567c565b60c080860152615a7f610160860182846154eb565b915050615a8f60408a018a61567c565b609f19808785030160e0880152615aa78483856154eb565b935084615ab660608e01614a18565b16610100880152615aca60808d018d61567c565b955092508087850301610120880152615ae48486856154eb565b9450615af360a08d018d61567c565b9450925080878603016101408801525050615b0f8383836154eb565b93505050508560208301528460408301528360608301528260808301529695505050505050565b604081526000615b4a6040830185876154eb565b9050826020830152949350505050565b606081526000615b6e6060830187896154eb565b8281036020840152615b818186886154eb565b9150508260408301529695505050505050565b634e487b7160e01b600052601160045260246000fd5b600082821015615bbc57615bbc615b94565b500390565b60008219821115615bd457615bd4615b94565b500190565b6080815284516080820152602085015160a0820152604085015160c08201526000606086015160c060e0840152615c146101408401826149d9565b60808801516001600160a01b031661010085015260a0880151848203607f1901610120860152909150615c4782826149d9565b60208501979097525050506040810192909252606090910152919050565b600060208284031215615c7757600080fd5b813560ff8116811461172657600080fd5b60006101408c8352806020840152615ca28184018d6149d9565b6001600160a01b038c811660408601528482036060860152909150615cc7828c6149d9565b908a16608085015283810360a08501529050615ce381896149d9565b9150508560c08301528460e083015283610100830152826101208301529b9a5050505050505050505050565b6000808354615d1d81615374565b60018281168015615d355760018114615d4657615670565b60ff19841687528287019450615670565b8760005260208060002060005b85811015615d6c5781548a820152908401908201615d53565b505050959091019695505050505050565b60c08152865160c0820152600060208801516101208060e0850152615da66101e08501836149d9565b915060408a0151610100818187015260608c01518387015260808c0151925060bf1991508186850301610140870152615ddf84846149d9565b935060a08c01519250615dfe6101608701846001600160a01b03169052565b60c08c015192508186850301610180870152615e1a84846149d9565b935060e08c01519250615e396101a08701846001600160a01b03169052565b8b01518584039091016101c08601529050615e5482826149d9565b602085019990995250505060408101949094526060840192909252608083015260a090910152919050565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090615f04908301846149d9565b9695505050505050565b600060208284031215615f2057600080fd5b8151611726816148ee565b634e487b7160e01b600052603160045260246000fdfea2646970667358221220b22320e1e51caa663be0e995a3451f4dfdfa558ea5b06e71a5454229c1bc455b64736f6c634300080a0033", + "linkReferences": { + "contracts/libraries/InteractionLogic.sol": { + "InteractionLogic": [ + { + "length": 20, + "start": 8385 + }, + { + "length": 20, + "start": 9380 + }, + { + "length": 20, + "start": 10429 + }, + { + "length": 20, + "start": 13707 + } + ] + }, + "contracts/libraries/ProfileTokenURILogic.sol": { + "ProfileTokenURILogic": [ + { + "length": 20, + "start": 11990 + } + ] + }, + "contracts/libraries/PublishingLogic.sol": { + "PublishingLogic": [ + { + "length": 20, + "start": 5505 + }, + { + "length": 20, + "start": 7070 + }, + { + "length": 20, + "start": 13936 + }, + { + "length": 20, + "start": 15618 + }, + { + "length": 20, + "start": 16000 + }, + { + "length": 20, + "start": 16330 + } + ] + } + }, + "deployedLinkReferences": { + "contracts/libraries/InteractionLogic.sol": { + "InteractionLogic": [ + { + "length": 20, + "start": 8073 + }, + { + "length": 20, + "start": 9068 + }, + { + "length": 20, + "start": 10117 + }, + { + "length": 20, + "start": 13395 + } + ] + }, + "contracts/libraries/ProfileTokenURILogic.sol": { + "ProfileTokenURILogic": [ + { + "length": 20, + "start": 11678 + } + ] + }, + "contracts/libraries/PublishingLogic.sol": { + "PublishingLogic": [ + { + "length": 20, + "start": 5193 + }, + { + "length": 20, + "start": 6758 + }, + { + "length": 20, + "start": 13624 + }, + { + "length": 20, + "start": 15306 + }, + { + "length": 20, + "start": 15688 + }, + { + "length": 20, + "start": 16018 + } + ] + } + } +} diff --git a/tasks/helpers/constants.ts b/tasks/helpers/constants.ts index 77a383d..cca6ff8 100644 --- a/tasks/helpers/constants.ts +++ b/tasks/helpers/constants.ts @@ -4,6 +4,9 @@ export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; export const LZ_SOURCE_CHAINS = ['mumbai', 'polygon']; export const LZ_DESTINATION_CHAINS = ['fantom_testnet', 'fantom']; +// chainId: value not related to EVM IDs +// endpoint: LZEndpoint contract deployed on the network +// remote: the remote chain to be configured to send/receive messages from export const LZ_CONFIG = { fantom_testnet: { chainId: '10012', @@ -21,3 +24,27 @@ export const LZ_CONFIG = { remote: 'mumbai' } }; + +export const LZ_CONFIG_GATED_MODULES = { + mumbai: { + chainId: '10009', + endpoint: '0xf69186dfBa60DdB133E91E9A4B5673624293d8F8', + remotes: ['goerli', 'optimismTestnet'] + }, + goerli: { + chainId: '10121', + endpoint: '0xbfD2135BFfbb0B5378b56643c2Df8a87552Bfa23', + remote: 'mumbai' + }, + optimismTestnet: { + chainId: '10132', + endpoint: '0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1', + remote: 'mumbai' + }, + // would need to add their hardhat plugin for deployment + // zkSync_testnet: { + // chainId: '10147', + // endpoint: '0xeaa8d1D0E736C59F7F0211C272d25f7AEC9FCB51', + // remote: 'mumbai' + // } +}; diff --git a/tasks/helpers/lens.ts b/tasks/helpers/lens.ts index ee12563..e75b41c 100644 --- a/tasks/helpers/lens.ts +++ b/tasks/helpers/lens.ts @@ -1,9 +1,11 @@ -import { Contract } from "ethers"; +import { Contract, Signer } from "ethers"; +import LensHub from './abi/LensHub.json'; const lensAddresses = { 'mumbai': { 'interaction logic lib': '0xefd400326635e016CbfCc309725D5B62FD9d3468', - 'lensHub proxy': '0x60Ae865ee4C725cd04353b5AAb364553f56ceF82' + 'lensHub proxy': '0x60Ae865ee4C725cd04353b5AAb364553f56ceF82', + 'lensHub sandbox': '0x7582177F9E536aB0b6c721e11f383C326F2Ad1D5', } }; @@ -11,10 +13,16 @@ const collectAbi = [ "function collect(uint256 profileId, uint256 pubId, bytes calldata data) external returns (uint256)" ]; -export const getLensHub = async (provider: any, networkName: string = 'mumbai') => ( - new Contract(lensAddresses[networkName]['lensHub proxy'], collectAbi, provider) +export const getLensHub = async (key: string = 'lensHub proxy', provider: any, networkName: string = 'mumbai') => ( + new Contract(lensAddresses[networkName][key], collectAbi, provider) ); +export const getLensHubDeployed = async (key: string = 'lensHub proxy', signer: Signer, networkName: string = 'mumbai') => { + const contract = new Contract(lensAddresses[networkName][key], LensHub.abi, signer.provider); + + return contract.connect(signer); +}; + // testnet accounts export const accounts = [ { secretKey: '0xc5e8f61d1ab959b397eecc0a37a6517b8e67a0e7cf1f4bce5591f3ed80199122' }, diff --git a/tasks/stub-collect-post.ts b/tasks/stub-collect-post.ts index f029ea5..5cb8238 100644 --- a/tasks/stub-collect-post.ts +++ b/tasks/stub-collect-post.ts @@ -8,7 +8,7 @@ task('stub-collect-post', 'collects our lens post').setAction(async ({}, hre) => const ethers = hre.ethers; const networkName = hre.network.name; const [_, governance] = await ethers.getSigners(); // uses priv key defined in this hardhat config - const lensHub = await getLensHub(governance.provider); + const lensHub = await getLensHub('lensHub proxy', governance.provider); const { maxFeePerGas, maxPriorityFeePerGas } = await lensHub.provider.getFeeData(); diff --git a/test/lens/helpers/utils.ts b/test/lens/helpers/utils.ts index 97f895d..73fe094 100644 --- a/test/lens/helpers/utils.ts +++ b/test/lens/helpers/utils.ts @@ -167,7 +167,7 @@ export async function getMirrorWithSigParts( return await getSig(wallet, msgParams); } -const buildCommentWithSigParams = ( +export const buildCommentWithSigParams = ( profileId: BigNumberish, contentURI: string, profileIdPointed: BigNumberish, @@ -211,7 +211,7 @@ const buildCommentWithSigParams = ( }, }); -const buildMirrorWithSigParams = ( +export const buildMirrorWithSigParams = ( profileId: BigNumberish, profileIdPointed: BigNumberish, pubIdPointed: string, @@ -246,7 +246,7 @@ const buildMirrorWithSigParams = ( }, }); -const buildFollowWithSigParams = ( +export const buildFollowWithSigParams = ( profileIds: string[] | number[], datas: Bytes[] | string[], nonce: number, @@ -281,7 +281,7 @@ export async function getCollectWithSigParts( return await getSig(wallet, msgParams); } -const buildCollectWithSigParams = ( +export const buildCollectWithSigParams = ( profileId: BigNumberish, pubId: string, data: Bytes | string, diff --git a/yarn.lock b/yarn.lock index 57ff6e8..007df8a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7302,6 +7302,11 @@ process@^0.11.10: resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== +promise-limit@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/promise-limit/-/promise-limit-2.7.0.tgz#eb5737c33342a030eaeaecea9b3d3a93cb592b26" + integrity sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw== + promise-to-callback@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/promise-to-callback/-/promise-to-callback-1.0.0.tgz#5d2a749010bfb67d963598fcd3960746a68feef7" From 90ac30e9901f684023f7a0d924f6348fe802c852 Mon Sep 17 00:00:00 2001 From: Carlos Beltran Date: Wed, 14 Dec 2022 17:29:13 -0600 Subject: [PATCH 11/14] fix: relay methods must be payable; setup final tasks for estimating fee and testing the follow module --- contracts/LZGatedProxy.sol | 8 +- proposal.md | 101 ---------------- tasks/deploy-token-remote.ts | 2 +- tasks/gated-modules/deploy-modules-source.ts | 59 +++++----- tasks/gated-modules/estimate-fee-follow.ts | 115 ------------------- tasks/gated-modules/estimate-fee.ts | 64 +++++++++++ tasks/gated-modules/set-follow-module.ts | 45 ++++++++ tasks/gated-modules/test-follow-module.ts | 58 ++++++++++ tasks/helpers/constants.ts | 12 +- tasks/helpers/lens.ts | 94 ++++++++++++++- 10 files changed, 299 insertions(+), 259 deletions(-) delete mode 100644 proposal.md delete mode 100644 tasks/gated-modules/estimate-fee-follow.ts create mode 100644 tasks/gated-modules/estimate-fee.ts create mode 100644 tasks/gated-modules/set-follow-module.ts create mode 100644 tasks/gated-modules/test-follow-module.ts diff --git a/contracts/LZGatedProxy.sol b/contracts/LZGatedProxy.sol index 4fbd150..e48a7bd 100644 --- a/contracts/LZGatedProxy.sol +++ b/contracts/LZGatedProxy.sol @@ -56,7 +56,7 @@ contract LZGatedProxy is SimpleLzApp { address tokenContract, uint256 balanceThreshold, DataTypes.FollowWithSigData memory followSig - ) external { + ) external payable { if (!_checkThreshold(follower, tokenContract, balanceThreshold)) { revert InsufficientBalance(); } _lzSend( @@ -95,7 +95,7 @@ contract LZGatedProxy is SimpleLzApp { address tokenContract, uint256 balanceThreshold, DataTypes.CommentWithSigData memory commentSig - ) external { + ) external payable { if (!_checkThreshold(sender, tokenContract, balanceThreshold)) { revert InsufficientBalance(); } _lzSend( @@ -136,7 +136,7 @@ contract LZGatedProxy is SimpleLzApp { address tokenContract, uint256 balanceThreshold, DataTypes.MirrorWithSigData memory mirrorSig - ) external { + ) external payable { if (!_checkThreshold(sender, tokenContract, balanceThreshold)) { revert InsufficientBalance(); } _lzSend( @@ -175,7 +175,7 @@ contract LZGatedProxy is SimpleLzApp { address tokenContract, uint256 balanceThreshold, DataTypes.CollectWithSigData memory collectSig - ) external { + ) external payable { if (!_checkThreshold(collector, tokenContract, balanceThreshold)) { revert InsufficientBalance(); } _lzSend( diff --git a/proposal.md b/proposal.md deleted file mode 100644 index be1d446..0000000 --- a/proposal.md +++ /dev/null @@ -1,101 +0,0 @@ -# Proposal for handling async, cross-chain execution within Lens Modules - -## What are we building -Cross-chain, token-gated follow: a Lens profile can set its follow module such that only wallets above a certain ERC20/ERC721 balance can follow them (e.g., you must own a CryptoPunk on ethereum L1 to follow). - -## How are we building it -We have a follow module `LZGatedFollowModule` that is a LayerZero-enabled smart contract; it can send and receive cross-chain messages. When it processes a follow for a profile, we send a message to the destination chain - where we have a separate `LZGatedProxy` contract that simply makes the balance check against the ERC20/ERC721 contract specified on that chain. - -## The problem -The only feedback our module can give back to LensHub is a revert if the follow is invalid. - - we won't know if the follow is valid or not until after the async execution on the remote chain - - even if we used an oracle for a data read, it's async - -## The proposed solution -1. return some kind of pending state so LensHub doesn't immediately mint the follow nft -2. maintain some permission to later execute the follow if the async check is successful - -Within our `LZGatedFollowModule` - notice that we now return a boolean value in `#processFollow` meant to signal to the LensHub an intermediate state in this follow request -```solidity -/** - * @dev Process a follow by: - * - if the profile has set gated follow data on this chain, just do the token balance check inline and revert if the - * threshold is not met - * - else send the payload to our cross-chain proxy on the remote chain to do the check and asynchronously return the - * result. - * NOTE: we must have an intermediate state to report back to the LensHub contract - */ -function processFollow( - address follower, - uint256 profileId, - bytes calldata // data -) external override onlyHub returns (bool pendingAsyncRequest) { - if (gatedFollowPerProfile[profileId].remoteChainId == sourceChainId) { - if (!_checkThreshold(follower, gatedFollowPerProfile[profileId])) { - revert Errors.FollowInvalid(); - } - - pendingAsyncRequest = false; - } else { - // make the async balance check, expect result in `#_nonblockingLzReceive` - _lzSend( - gatedFollowPerProfile[profileId].remoteChainId, - abi.encode( - follower, - gatedFollowPerProfile[profileId].remoteContract, - profileId, - gatedFollowPerProfile[profileId].balanceThreshold - ), - payable(follower), - zroPaymentAddress, - bytes("") - ); - - pendingAsyncRequest = true; - } -} -``` - -And the proposed change in the lens protocol, within the `InteractionLogic` library function `#follow` -```solidity -... - -if (followModule != address(0)) { - bool pendingAsyncRequest = IFollowModule(followModule).processFollow( - follower, - profileIds[i], - followModuleDatas[i] - ); - - if (!pendingAsyncRequest) { - tokenIds[i] = IFollowNFT(followNFT).mint(follower); - } else { - pendingFollows[followModule][follower][profileId] = true; // or some nonce? - } -} -... -``` - -Finally, to complete the flow - our module receives the result from the remote contract and fulfills the process follow request. **This requires a new function in the lens protocol - only callable by whitelisted modules** -```solidity -/** - * @dev Callback from our `LZGatedProxy` contract deployed on a remote chain - * - contains the result of the token balance check in `shouldFollow` - * - make a call to the LensHub to complete the async follow request (they should require that there is a pending - * follow request, process, and clear it out) - */ -function _nonblockingLzReceive( - uint16 _srcChainId, - bytes memory _srcAddress, - uint64 _nonce, - bytes memory _payload -) internal override { - ( - address follower, - uint256 profileId, - bool shouldFollow - ) = abi.decode(data, (address, uint256, bool)); - - LensHub(HUB).completePendingFollow(follower, profileId, shouldFollow); -} -``` diff --git a/tasks/deploy-token-remote.ts b/tasks/deploy-token-remote.ts index a5f03ed..ceb62d5 100644 --- a/tasks/deploy-token-remote.ts +++ b/tasks/deploy-token-remote.ts @@ -1,5 +1,5 @@ import { task } from 'hardhat/config'; -import { LZ_DESTINATION_CHAINS, LZ_CONFIG } from './helpers/constants'; +import { LZ_CONFIG } from './helpers/constants'; import deployContract from './helpers/deployContract'; task('deploy-token-remote', 'deploys OmniSBT on the destination chain').setAction(async ({}, hre) => { diff --git a/tasks/gated-modules/deploy-modules-source.ts b/tasks/gated-modules/deploy-modules-source.ts index 7d9a339..b3fd0bb 100644 --- a/tasks/gated-modules/deploy-modules-source.ts +++ b/tasks/gated-modules/deploy-modules-source.ts @@ -7,40 +7,39 @@ import getContract from './../helpers/getContract'; task('deploy-modules-source', 'deploys our Lens modules on the source chain').setAction(async ({}, hre) => { const ethers = hre.ethers; const networkName = hre.network.name; - const [deployer, _, governance] = await ethers.getSigners(); + const [deployer, governance] = await ethers.getSigners(); if (!LZ_CONFIG_GATED_MODULES[networkName]) throw new Error('invalid network'); // using the sandbox deployment for module whitelisting - const lensHub = await getLensHubDeployed('lensHub sandbox', governance, networkName); - - const followModule = await deployContract( - ethers, - networkName, - 'LZGatedFollowModule', - [lensHub.address, LZ_CONFIG_GATED_MODULES[networkName].endpoint, [], []] - ); - - const referenceModule = await deployContract( - ethers, - networkName, - 'LZGatedReferenceModule', - [lensHub.address, LZ_CONFIG_GATED_MODULES[networkName].endpoint, [], []] - ); - - const collectModule = await deployContract( - ethers, - networkName, - 'LZGatedCollectModule', - [lensHub.address, LZ_CONFIG_GATED_MODULES[networkName].endpoint, [], []] - ); - - // const followModule = await getContract(ethers, 'LZGatedFollowModule', deployer); - // const referenceModule = await getContract(ethers, 'LZGatedReferenceModule', deployer); - // const collectModule = await getContract(ethers, 'LZGatedCollectModule', deployer); - - // @TODO: might not need to whitelist in SANDBOX - + const lensHub = await getLensHubDeployed('lensHub sandbox', networkName, governance.provider); + + // const followModule = await deployContract( + // ethers, + // networkName, + // 'LZGatedFollowModule', + // [lensHub.address, LZ_CONFIG_GATED_MODULES[networkName].endpoint, [], []] + // ); + // + // const referenceModule = await deployContract( + // ethers, + // networkName, + // 'LZGatedReferenceModule', + // [lensHub.address, LZ_CONFIG_GATED_MODULES[networkName].endpoint, [], []] + // ); + // + // const collectModule = await deployContract( + // ethers, + // networkName, + // 'LZGatedCollectModule', + // [lensHub.address, LZ_CONFIG_GATED_MODULES[networkName].endpoint, [], []] + // ); + + const followModule = await getContract(ethers, 'LZGatedFollowModule', deployer); + const referenceModule = await getContract(ethers, 'LZGatedReferenceModule', deployer); + const collectModule = await getContract(ethers, 'LZGatedCollectModule', deployer); + + console.log(await governance.getAddress()); let tx; console.log('lensHub.whitelistFollowModule()'); tx = await lensHub.connect(governance).whitelistFollowModule(followModule.address, true, { gasLimit: 50000 }); diff --git a/tasks/gated-modules/estimate-fee-follow.ts b/tasks/gated-modules/estimate-fee-follow.ts deleted file mode 100644 index ed86bf5..0000000 --- a/tasks/gated-modules/estimate-fee-follow.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { task } from 'hardhat/config'; -import { Contract, utils, Bytes } from 'ethers'; -import { contractsDeployed } from './../../scripts/utils/migrations'; -import { LZ_CONFIG_GATED_MODULES } from './../helpers/constants'; -import ILayerZeroMessagingLibrary from './../helpers/ILayerZeroMessagingLibrary.json'; -import { getLensHubDeployed } from './../helpers/lens'; -import { MAX_UINT256, HARDHAT_CHAINID } from './../../test/lens/helpers/constants'; - -const TOKEN_CONTRACT = '0xDBF49B20eC2E48ef4CeEb79927eA4Ac2eFc2c961'; // goerli -const TOKEN_THRESHOLD = '1'; -const PROFILE_ID_TO_FOLLOW = '1'; - -const buildFollowWithSigParams = ( - chainId: number | undefined, - lensHubAddress: string, - profileIds: string[] | number[], - datas: Bytes[] | string[], - nonce: number, - deadline: string -) => ({ - types: { - FollowWithSig: [ - { name: 'profileIds', type: 'uint256[]' }, - { name: 'datas', type: 'bytes[]' }, - { name: 'nonce', type: 'uint256' }, - { name: 'deadline', type: 'uint256' }, - ], - }, - domain: { - name: 'Lens Protocol Profiles', - version: '1', - chainId: chainId || HARDHAT_CHAINID, - verifyingContract: lensHubAddress, - }, - value: { - profileIds: profileIds, - datas: datas, - nonce: nonce, - deadline: deadline, - }, -}); - -task('estimate-fee-follow', 'estimate the fee of sending message from remote chain to source chain').setAction(async ({}, hre) => { - const ethers = hre.ethers; - const networkName = hre.network.name; - const [deployer] = await ethers.getSigners(); - - // using the sandbox deployment for module whitelisting - const lensHub = await getLensHubDeployed('lensHub sandbox', deployer); - - if (!(LZ_CONFIG_GATED_MODULES[networkName] && LZ_CONFIG_GATED_MODULES[networkName].remote)) throw new Error('invalid network'); - - const endpoint = new Contract(LZ_CONFIG_GATED_MODULES[networkName].endpoint, ILayerZeroMessagingLibrary.abi, deployer.provider); - - const destination = LZ_CONFIG_GATED_MODULES[networkName].remote; - - const followerAddress = await deployer.getAddress(); - let nonce; - try { - nonce = (await lensHub.sigNonces(followerAddress)).toNumber() - } catch (error) { - nonce = 0; - } - - const msgParams = buildFollowWithSigParams( - hre.network.config.chainId, - lensHub.address, - [PROFILE_ID_TO_FOLLOW], - [[]], - nonce, - MAX_UINT256 - ); - const sig = await deployer._signTypedData(msgParams.domain, msgParams.types, msgParams.value); - const { v, r, s } = ethers.utils.splitSignature(sig); - - const followWithSigData = { - follower: followerAddress, - profileIds: [PROFILE_ID_TO_FOLLOW], - datas: [[]], - sig: { - v, - r, - s, - deadline: MAX_UINT256, - }, - }; - - // dummy payload - const followWithSigType = 'tuple(address follower, uint256[] profileIds, bytes[] datas, tuple(uint8 v, bytes32 r, bytes32 s, uint256 deadline) sig) followSig'; - const types = ['address', 'address', 'uint256', 'uint256', followWithSigType]; - const payload = ethers.utils.defaultAbiCoder.encode( - types, - [followerAddress, TOKEN_CONTRACT, PROFILE_ID_TO_FOLLOW, TOKEN_THRESHOLD, followWithSigData] - ); - - console.log(`endpoint.estimateFees( - ${LZ_CONFIG_GATED_MODULES[destination].chainId}, - ${contractsDeployed.LZGatedProxy}, - ${payload}, - ${false}, - ${"0x"} - )`); - - // @TODO: missing revert data in call exception; Transaction reverted without a reason string - const fees = await endpoint.estimateFees( - LZ_CONFIG_GATED_MODULES[destination].chainId, // the destination LayerZero chainId - contractsDeployed.LZGatedProxy, // your contract address that calls Endpoint.send() - payload, - false, // _payInZRO - "0x" // default '0x' adapterParams, see: Relayer Adapter Param docs - ); - - console.log('payload types', types); - console.log('fees in eth', utils.formatEther(fees[0])); -}); diff --git a/tasks/gated-modules/estimate-fee.ts b/tasks/gated-modules/estimate-fee.ts new file mode 100644 index 0000000..89a6428 --- /dev/null +++ b/tasks/gated-modules/estimate-fee.ts @@ -0,0 +1,64 @@ +import { task } from 'hardhat/config'; +import { Contract, utils, Bytes, providers } from 'ethers'; +import { contractsDeployed } from './../../scripts/utils/migrations'; +import ILayerZeroMessagingLibrary from './../helpers/ILayerZeroMessagingLibrary.json'; +import { getLensHubDeployed, getFollowWithSigParts } from './../helpers/lens'; +import { MAX_UINT256 } from './../../test/lens/helpers/constants'; +import { + LZ_CONFIG_GATED_MODULES, + SANDBOX_USER_PROFILE_ID, + TOKEN_CONTRACT, + TOKEN_THRESHOLD, +} from './../helpers/constants'; + +const { ALCHEMY_MUMBAI_URL, ALCHEMY_POLYGON_URL } = process.env; + +// the same can be done for LZGatedCollectModule + LZGatedReferenceModule, just need to setup the correct sig data +task('estimate-fee', 'estimate the fee of sending the followSig from source chain to remote chain').setAction(async ({}, hre) => { + const ethers = hre.ethers; + const networkName = hre.network.name; + const [deployer] = await ethers.getSigners(); + + if (!(LZ_CONFIG_GATED_MODULES[networkName] && LZ_CONFIG_GATED_MODULES[networkName].remote)) throw new Error('invalid network'); + + const destination = LZ_CONFIG_GATED_MODULES[networkName].remote; + const provider = new providers.JsonRpcProvider(destination === 'mumbai' ? ALCHEMY_MUMBAI_URL : ALCHEMY_POLYGON_URL); + const lensHub = await getLensHubDeployed('lensHub sandbox', destination, provider); + const endpoint = new Contract(LZ_CONFIG_GATED_MODULES[networkName].endpoint, ILayerZeroMessagingLibrary.abi, deployer.provider); + + const followerAddress = await deployer.getAddress(); + const nonce = (await lensHub.sigNonces(followerAddress)).toNumber(); + + const followWithSigData = await getFollowWithSigParts({ + chainId: hre.network.config.chainId, + wallet: deployer, + lensHubAddress: lensHub.address, + profileIds: [SANDBOX_USER_PROFILE_ID], + datas: [[]], + nonce, + deadline: MAX_UINT256, + follower: followerAddress, + }); + + // example payload + const followWithSigType = 'tuple(address follower, uint256[] profileIds, bytes[] datas, tuple(uint8 v, bytes32 r, bytes32 s, uint256 deadline) sig) followSig'; + const types = ['address', 'address', 'uint256', 'uint256', followWithSigType]; + const payload = ethers.utils.defaultAbiCoder.encode( + types, + [followerAddress, TOKEN_CONTRACT, SANDBOX_USER_PROFILE_ID, TOKEN_THRESHOLD, followWithSigData] + ); + + console.log(`networkName: ${networkName}`); + console.log(`endpoint.address: ${endpoint.address}`); + + const fees = await endpoint.estimateFees( + LZ_CONFIG_GATED_MODULES[destination].chainId, // the destination LayerZero chainId + contractsDeployed.LZGatedProxy, // your contract address that calls Endpoint.send() + payload, + false, // _payInZRO + "0x" // default '0x' adapterParams, see: Relayer Adapter Param docs + ); + + console.log('payload types: ', types); + console.log(`fees in ${['mumbai', 'polygon'].includes(networkName) ? 'matic' : 'ether'}`, utils.formatEther(fees[0])); +}); diff --git a/tasks/gated-modules/set-follow-module.ts b/tasks/gated-modules/set-follow-module.ts new file mode 100644 index 0000000..99e1287 --- /dev/null +++ b/tasks/gated-modules/set-follow-module.ts @@ -0,0 +1,45 @@ +import { task } from 'hardhat/config'; +import { + LZ_CONFIG_GATED_MODULES, + SANDBOX_USER_PROFILE_ID, // owned by `deployer` + TOKEN_CONTRACT, + TOKEN_THRESHOLD, + TOKEN_CHAIN_ID, +} from './../helpers/constants'; +import { getLensHubDeployed } from './../helpers/lens'; +import { contractsDeployed } from './../../scripts/utils/migrations'; + +task('set-follow-module', 'sets the LZGatedFollowModule on our test profile').setAction(async ({}, hre) => { + const ethers = hre.ethers; + const networkName = hre.network.name; + const [deployer] = await ethers.getSigners(); + const accounts = await ethers.getSigners(); + console.log(await Promise.all(accounts.map(async(account) => await account.getAddress()))); + return; + + if (!LZ_CONFIG_GATED_MODULES[networkName]) throw new Error('invalid network'); + + // using the sandbox deployment for module whitelisting + const lensHub = await getLensHubDeployed('lensHub sandbox', networkName, deployer.provider); + + // tokenContract, balanceThreshold, chainId + const data = ethers.utils.defaultAbiCoder.encode( + ['address', 'uint256', 'uint16'], + [TOKEN_CONTRACT, TOKEN_THRESHOLD, TOKEN_CHAIN_ID] + ); + + // `SANDBOX_USER_PROFILE_ID` profile was created thru MockProfileCreationProxy, owned by `deployer` + // https://docs.lens.xyz/docs/deployed-contract-addresses#sandbox-mumbai-testnet-addresses + + console.log('lensHub.setFollowModule()') + const tx = await lensHub.connect(deployer).setFollowModule( + SANDBOX_USER_PROFILE_ID, + contractsDeployed.LZGatedFollowModule, + data, + { gasLimit: 210000 } + ); + console.log(`tx: ${tx.hash}`); + await tx.wait(); + + console.log('follow module set!'); +}); diff --git a/tasks/gated-modules/test-follow-module.ts b/tasks/gated-modules/test-follow-module.ts new file mode 100644 index 0000000..3a6e4a5 --- /dev/null +++ b/tasks/gated-modules/test-follow-module.ts @@ -0,0 +1,58 @@ +import { task } from 'hardhat/config'; +import { Contract, utils, Bytes, providers } from 'ethers'; +import getContract from './../helpers/getContract'; +import { getLensHubDeployed, getFollowWithSigParts } from './../helpers/lens'; +import { MAX_UINT256 } from './../../test/lens/helpers/constants'; +import { + LZ_CONFIG_GATED_MODULES, + SANDBOX_USER_PROFILE_ID, + TOKEN_CONTRACT, + TOKEN_THRESHOLD, +} from './../helpers/constants'; + +const { ALCHEMY_MUMBAI_URL, ALCHEMY_POLYGON_URL } = process.env; + +const SANDBOX_USER_PROFILE_ID_TWO = '317'; // owned by `governance` +const ESTIMATED_FOLLOW_FEE_GWEI = '1000'; // derived from `npx hardhat estimate-fee` + +// the same can be done for LZGatedCollectModule + LZGatedReferenceModule, just need to setup the correct sig data +task('test-follow-module', 'try to folllow a profile which has set their follow module to LZGatedFollowModule').setAction(async ({}, hre) => { + const ethers = hre.ethers; + const networkName = hre.network.name; + const [deployer] = await ethers.getSigners(); + + if (!(LZ_CONFIG_GATED_MODULES[networkName] && LZ_CONFIG_GATED_MODULES[networkName].remote)) throw new Error('invalid network'); + + const destination = LZ_CONFIG_GATED_MODULES[networkName].remote; + const provider = new providers.JsonRpcProvider(destination === 'mumbai' ? ALCHEMY_MUMBAI_URL : ALCHEMY_POLYGON_URL); + const lensHub = await getLensHubDeployed('lensHub sandbox', destination, provider); + const lzGatedProxy = await getContract(ethers, 'LZGatedProxy', deployer); + const followerAddress = await deployer.getAddress(); + const nonce = (await lensHub.sigNonces(followerAddress)).toNumber(); + + const followWithSigData = await getFollowWithSigParts({ + chainId: hre.network.config.chainId, + wallet: deployer, + lensHubAddress: lensHub.address, + profileIds: [SANDBOX_USER_PROFILE_ID_TWO], + datas: [[]], + nonce, + deadline: MAX_UINT256, + follower: followerAddress, + }); + + console.log('lzGatedProxy.relayFollowWithSig()'); + const tx = await lzGatedProxy.relayFollowWithSig( + followerAddress, + SANDBOX_USER_PROFILE_ID_TWO, + TOKEN_CONTRACT, + TOKEN_THRESHOLD, + followWithSigData, + { value: utils.parseUnits(ESTIMATED_FOLLOW_FEE_GWEI, 'gwei'), gasLimit: 100000 } + ); + console.log(`tx: ${tx.hash}`); + await tx.wait(); + + // assuming `followerAddress` has a balance of `TOKEN_CONTRACT` >= `TOKEN_THRESHOLD` - likely good + // check the latext tx against the deployed LZGatedFollowModule +}); diff --git a/tasks/helpers/constants.ts b/tasks/helpers/constants.ts index cca6ff8..1482d67 100644 --- a/tasks/helpers/constants.ts +++ b/tasks/helpers/constants.ts @@ -2,7 +2,7 @@ export const MAX_UINT256 = '0xffffffffffffffffffffffffffffffffffffffffffffffffff export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; export const LZ_SOURCE_CHAINS = ['mumbai', 'polygon']; -export const LZ_DESTINATION_CHAINS = ['fantom_testnet', 'fantom']; +export const LZ_DESTINATION_CHAINS = ['fantom_testnet', 'fantom', 'goerli', 'optimismTestnet']; // chainId: value not related to EVM IDs // endpoint: LZEndpoint contract deployed on the network @@ -14,7 +14,7 @@ export const LZ_CONFIG = { remote: 'mumbai' }, mumbai: { - chainId: '10009', + chainId: '10109', endpoint: '0xf69186dfBa60DdB133E91E9A4B5673624293d8F8', remotes: ['bsc_testnet'] }, @@ -27,7 +27,7 @@ export const LZ_CONFIG = { export const LZ_CONFIG_GATED_MODULES = { mumbai: { - chainId: '10009', + chainId: '10109', endpoint: '0xf69186dfBa60DdB133E91E9A4B5673624293d8F8', remotes: ['goerli', 'optimismTestnet'] }, @@ -48,3 +48,9 @@ export const LZ_CONFIG_GATED_MODULES = { // remote: 'mumbai' // } }; + +// @TODO: these should change per setup +export const TOKEN_CONTRACT = '0xDBF49B20eC2E48ef4CeEb79927eA4Ac2eFc2c961'; // goerli +export const TOKEN_THRESHOLD = '1'; +export const TOKEN_CHAIN_ID = LZ_CONFIG_GATED_MODULES.goerli.chainId; +export const SANDBOX_USER_PROFILE_ID = '67'; diff --git a/tasks/helpers/lens.ts b/tasks/helpers/lens.ts index e75b41c..425a435 100644 --- a/tasks/helpers/lens.ts +++ b/tasks/helpers/lens.ts @@ -1,5 +1,12 @@ -import { Contract, Signer } from "ethers"; +import { + Contract, + Signer, + BigNumber, + utils, + Bytes, +} from "ethers"; import LensHub from './abi/LensHub.json'; +import { HARDHAT_CHAINID } from './../../test/lens/helpers/constants'; const lensAddresses = { 'mumbai': { @@ -13,16 +20,93 @@ const collectAbi = [ "function collect(uint256 profileId, uint256 pubId, bytes calldata data) external returns (uint256)" ]; -export const getLensHub = async (key: string = 'lensHub proxy', provider: any, networkName: string = 'mumbai') => ( +export const getLensHub = (key: string = 'lensHub proxy', provider: any, networkName: string = 'mumbai') => ( new Contract(lensAddresses[networkName][key], collectAbi, provider) ); -export const getLensHubDeployed = async (key: string = 'lensHub proxy', signer: Signer, networkName: string = 'mumbai') => { - const contract = new Contract(lensAddresses[networkName][key], LensHub.abi, signer.provider); +export const getLensHubDeployed = ( + key: string = 'lensHub proxy', + networkName: string = 'mumbai', + provider: any, +) => ( + new Contract(lensAddresses[networkName][key], LensHub.abi, provider) +); - return contract.connect(signer); +type FollowWithSigDataProps = { + chainId: number | undefined; + wallet: any; // Signer + lensHubAddress: string; + profileIds: string[] | number[]; + datas: Bytes[] | string[]; + nonce: number; + deadline: string; + follower: string; }; +const buildFollowWithSigParams = ( + chainId: number | undefined, + lensHubAddress: string, + profileIds: string[] | number[], + datas: Bytes[] | string[], + nonce: number, + deadline: string +) => ({ + types: { + FollowWithSig: [ + { name: 'profileIds', type: 'uint256[]' }, + { name: 'datas', type: 'bytes[]' }, + { name: 'nonce', type: 'uint256' }, + { name: 'deadline', type: 'uint256' }, + ], + }, + domain: { + name: 'Lens Protocol Profiles', + version: '1', + chainId: chainId || HARDHAT_CHAINID, + verifyingContract: lensHubAddress, + }, + value: { + profileIds: profileIds, + datas: datas, + nonce: nonce, + deadline: deadline, + }, +}); + +export const getFollowWithSigParts = async ({ + chainId = HARDHAT_CHAINID, + wallet, + lensHubAddress, + profileIds, + datas, + nonce, + deadline, + follower +}: FollowWithSigDataProps) => { + const msgParams = buildFollowWithSigParams( + chainId, + lensHubAddress, + profileIds, + datas, + nonce, + deadline + ); + const sig = await wallet._signTypedData(msgParams.domain, msgParams.types, msgParams.value); + const { v, r, s } = utils.splitSignature(sig); + + return { + follower, + profileIds, + datas, + sig: { + v, + r, + s, + deadline, + }, + }; +} + // testnet accounts export const accounts = [ { secretKey: '0xc5e8f61d1ab959b397eecc0a37a6517b8e67a0e7cf1f4bce5591f3ed80199122' }, From 24e95c14520584c61d00342b689d1d14e0d8eb39 Mon Sep 17 00:00:00 2001 From: Carlos Beltran Date: Fri, 16 Dec 2022 15:58:37 -0600 Subject: [PATCH 12/14] wip - still testing on goerli; fix: use calldata for remote contract in _lzSend --- blog/blog_gated_modules.md | 190 +++++++++++++++++++ blog/lz-gated-follow-module@2x.png | Bin 0 -> 519827 bytes contracts/lz/SimpleLzApp.sol | 2 +- scripts/utils/goerli-contracts.json | 2 +- scripts/utils/optimismTestnet-contracts.json | 2 +- tasks/gated-modules/deploy-modules-source.ts | 89 ++++----- tasks/gated-modules/set-follow-module.ts | 5 +- tasks/gated-modules/test-follow-module.ts | 9 +- tasks/helpers/constants.ts | 13 +- tasks/helpers/lens.ts | 11 ++ 10 files changed, 264 insertions(+), 59 deletions(-) create mode 100644 blog/blog_gated_modules.md create mode 100644 blog/lz-gated-follow-module@2x.png diff --git a/blog/blog_gated_modules.md b/blog/blog_gated_modules.md new file mode 100644 index 0000000..1a12612 --- /dev/null +++ b/blog/blog_gated_modules.md @@ -0,0 +1,190 @@ +# Gate your Lens follows, collects, and mirrors - cross-chain - via LayerZero + +This is a technical overview of the [Lens](https://lens.xyz/) modules we developed to allow token-gated, cross-chain actions via [LayerZero](https://layerzero.network/). The source code for this entire project can be found on [this github repo](https://github.com/hyplabs/lz-collect-module). + +// @TODO: update the repo + +## Overview + +Consider the different ways that a Lens profile would want to set to gate their social content +``` +- Only wallets with 50 AAVE on Optimism can follow me +- Only owners of a CryptoPunk on Ethereum can collect my latest post +- Only collectors of my NFT collection on Arbitrum can comment on my posts +``` + +For each of these scenarios, we have a Lens module - a smart contract that processes the Lens action (follow, collect, comment/mirror) before allowing it to execute. Using LayerZero, we can enable token-gating against contracts deployed on chains other than where the Lens action is occurring (ex: `mumbai`, `polygon`). These modules are available for Lens profiles to configure with whatever parameters they want. Any supported chain, and any ERC20/ERC721 contract + token balance. + +## Cross-chain gated follows + +Let's go in depth with our `LZGatedFollowModule`, which processes our cross-chain, token-gated follows. + +In this example, a Lens profile has set their follow module to our `LZGatedFollowModule` and configured such that new followers must hold 50 AAVE tokens on Optimism. + +![lz-gated-follow-module](./lz-gated-follow-module@2x.png) + +1. **lensprotocol.lens** has set their gated follow data. A new follower must have 50 AAVE on Optimism. +2. **carlosbeltran.lens** wishes to follow - fetches the requirements and generates the signature for `#followWithSig` +3. anyone (ex: a relayer) can submit the follow requirements + the signature to the `LZGatedProxy` contract on Optimism, which relays the payload to the `LZGatedFollowModule` contract on Polygon via LayerZero + +We can see that the token balance check happens on Optimism, and the `#followWithSig` transaction is executed on Polygon. + +The intermediate contract is `LZGatedProxy`; this contract simply does the token balance check and relays the payload to our module contract on the destination chain. It's a LayerZero-enabled contract, meaning it can _send_ cross-chain payloads to trusted remote contracts. Our modules are configured to _receive_ cross-chain payloads from a trusted remote contracts. We'll explain this LayerZero wiring later. + +For now, let's look at the main functions within our follow module. + +### LZGatedFollowModule.sol +`LZGatedFollowModule` is Lens Follow Module that allows profiles to gate their following with ERC20 or ERC721 balances held on other chains. The typical flow is: +- a profile initializes this module with a supported LayerZero chain id and a erc20/erc721 contract + balance to check against +- another user wishes to follow, they read the requirements using `gatedFollowPerProfile` which points to the chain/token/balance +- user generates `followWithSigData` +- user (or relayer) submits this sig along with appropriate input to the `LZGatedProxy` contract on the other chain +- `LZGatedProxy` makes the token check, and relays the message to the `LZGatedFollowModule` on polygon +- `LZGatedFollowModule` contract validates the input, submits sig to `LensHub#followWithSig` +- follow is processed ✅ + +The two main functions in this contract are `#initializeFollowModule` and `#processFollow`; these are the callback functions the [LensHub](https://docs.lens.xyz/docs/lenshub) contract invokes when a this module is set as a profile's follow module, and when a follow action on the given profile is attempted. + +// @TODO: keep it going +// - finish this module +// - conclude that similar setup in others +// - hardhat tasks +// - finish testing +// - conclusion + +The constructor arguments include the address for the `OmniSBT` contract. The interactions with that contract are +1. validate that the `chainId` param provided in the init callback is supported +2. create a collection pointer +3. send the "mint" payload to the destination chain via layer zero + +Here's the function interface for `#initializePublicationCollectModule` + +```solidity +/** + * @dev Initialize publication collect data for the given `pubId`, including the destination chain to mint the OmniSBT + * @param profileId: the lens profile + * @param pubId: the post + * @param data: encoded data to init this module (followerOnly, chainId) + */ +function initializePublicationCollectModule( + uint256 profileId, + uint256 pubId, + bytes calldata data +) external override onlyHub returns (bytes memory) {} +``` + +When a post is initialized with this module, the caller must specify two things - whether the post can be only be collected by followers, and which LayerZero `chainId` the NFT should be minted for collectors. These values are encoded in the `data` param. + +When a post that has been initialized with this module is collected, our `#processCollect` function is invoked + +```solidity +/** + * @dev Processes a collect by: + * - [optional] ensuring the collector is a follower + * - minting a OmniSBT on the destination chain set + */ +function processCollect( + uint256, // referrerProfileId + address collector, + uint256 profileId, + uint256 pubId, + bytes calldata // data +) external override onlyHub {} +``` + +This is the function that triggers the cross-chain messaging logic in our `OmniSBT` contract. + +### OmniSBT.sol +`OmniSBT` creates Soulbound Tokens (SBTs) that mint on a remote destination chain; they are non-transferrable, and burnable. + +This contract inherits from our `LzApp` contract which wires up our contract for sending and receiving messages via LayerZero "endpoint" contracts. + +Let's look at the constructor for both contracts, as it gives us all the context we need to know before looking at the mint flow. + +```solidity +/** + * @dev OmniSBT contract constructor + * NOTE: array length will only be one when deploying to a "destination" chain. the "source" contract will contain + * all references to other deployed contracts + * @param _lzEndpoint: LayerZero endpoint on this chain to relay messages + * @param remoteChainIds: whitelisted destination chain ids (supported by LayerZero) + * @param remoteContracts: whitelisted destination contracts (deployed by us) + * @param _isSource: whether this contract is deployed on the "source" chain + */ +constructor(address _lzEndpoint, uint16[] memory remoteChainIds, bytes[] memory remoteContracts, bool _isSource) + LzApp(_lzEndpoint, msg.sender, remoteChainIds, remoteContracts) + ERC4973("Omni Soulbound Token", "OMNI-SBT") +{ + zroPaymentAddress = address(0); + isSource = _isSource; +} +``` + +```solidity +/** + * @dev LzApp contract constructor + * @param _lzEndpoint: The LZ endpoint contract deployed on this chain + * @param owner: The contract owner + * @param remoteChainIds: remote chain ids to set as trusted remotes + * @param remoteContracts: remote contracts to set as trusted remotes + */ +constructor( + address _lzEndpoint, + address owner, + uint16[] memory remoteChainIds, + bytes[] memory remoteContracts +) Owned(owner) { + if (_lzEndpoint == address(0)) { revert NotZeroAddress(); } + if (remoteChainIds.length != remoteContracts.length) { revert ArrayMismatch(); } + + lzEndpoint = ILayerZeroEndpoint(_lzEndpoint); + + uint256 length = remoteChainIds.length; + for (uint256 i = 0; i < length;) { + _lzRemoteLookup[remoteChainIds[i]] = remoteContracts[i]; + unchecked { i++; } + } +} +``` + +In short, we define the accepted chain ids and set the trusted remote contract addresses to relay messages to, and receive messages from. This way, when we process a collect for a post, we know where to relay messages based on the validated info set by the post creator. For more info on the LayerZero endpoint, check out their [docs](https://layerzero.gitbook.io/docs/faq/layerzero-endpoint). + +From here, it's helpful to picture the chain of function calls in order to understand the flow. + +![OmniSBT mint flow](./lz-collect-module@2x.png) +** it's important to note that `OmniSBT.sol` is a single contract deployed on both Polygon _and_ Optimism - it can send _and_ receive lz messages + +1. [on Polygon] someone collects our lens post +2. our callback `LZCollectModule#processCollect` is triggered +3. we call our mint function on `OmnitSBT` which makes an internal call to `#_lzSend` +4. we call `#send` on the `LayerZeroEndpoint` contract with our payload +5. LayerZero moves our payload from Polygon to Optimism via an Oracle and Relayer +6. [on Optimism] the `LayerZeroEndpoint` contract receives our payload +7. our payload is received in our `OmnitSBT` contract via the callback `#lzReceive` which makes an internal call to `#mint`, minting the NFT for the collector + +It's worth seeing how we receive messages in our `OmnitSBT` contract +```solidity +// LzApp.sol + +function lzReceive( + uint16 _srcChainId, + bytes memory _srcAddress, + uint64 _nonce, + bytes memory _payload +) public virtual override { + if (msg.sender != address(lzEndpoint)) { revert OnlyEndpoint(); } + + bytes memory trustedRemote = _lzRemoteLookup[_srcChainId]; + if (_srcAddress.length != trustedRemote.length || keccak256(_srcAddress) != keccak256(trustedRemote)) { + revert OnlyTrustedRemote(); + } + + _blockingLzReceive(_srcChainId, _srcAddress, _nonce, _payload); +} +``` + +- we assert that only the `LayerZeroEndpoint` contract can call +- we assert that we only receive messages from trusted remote contracts (set in the constructor or via `#setTrustedRemote`) +- we pass along the arguments to `#_blockingLzReceive` to process in a **blocking** way. This means that on transaction reverts/errors `LayerZeroEndpoint` contract will block the message queue from the "source" chain until the transaction is retried successfully ([see more](https://layerzero.gitbook.io/docs/faq/messaging-properties#message-ordering)) + +The end result: the account that collected the post on Polygon now has a soulbound NFT on Optimism :partying_face: diff --git a/blog/lz-gated-follow-module@2x.png b/blog/lz-gated-follow-module@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..7354a4fa3f9b0fdcf845c716af4852294720eecb GIT binary patch literal 519827 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⪻s?AH$1lgTSNd(kYPQs z4>mC7z?zf~j7YoK&yf+n_r&FVRzz2MB%?1!qi7DE_*nKrx=C3tp6laXKP) zPD|xfvrkJ)i=x<|AyBu*f%(A$GBcGFX(;r=obV&_+VnS3zziWNEhzMC^dmfPH5Rq; z=!V61>AFe(XP)(BYq^-_D+v|g4J0S&HIx%GRSPy$2U>=4b4)M>Rv{2D&f8%s2n>`- zbLb)9y0PelNRKgjxy}TuPTJp1wVj#r7o!{Qdo$5(xt|{uDcwg%x8k4o1L;|v!Jivo zzcD@|BM-de(+4U2Pih~CU;@n6;#1)@|6MiUiYUs-5t{XBZa;tIeOx|FHZ?V)Ce?04 zI6n4FY@~g5I|9z=f7?bAnSyX6^#ZA>5e0@tJQtWWj{SD<=&lkuXzk`Yz1E2f_jwvI zH#`#IK<+%wEeZG$Gdi{sFXP|3b+_}1ntmEq#XwTEf7!3w;QzT>lMru&3LB?W53N_; z5AQYo?0W$#7U`A-Y3VMdOS-$eQ=}V2>6Da^knRRar5mKX>x6f6>HR%3 z-ygw6UUnf;Ki6Epgum7nrs>h=G+59(k1>39F>8UrN$>plXthGYMy1u(h? zh9#XwD_HgNWfNNHZ$ma(yvwETC9)R95`>{KL_K^*{u?UOOT-T}nf{lSSvGLahIIPp zH7!G1@=~*}3;3D7TKLVSjWrx8^0acNa{Izq#0I~d7`~AMUM~CnRIS3x#1}%cKA*q7 zyv4S$<@aSqYC(v8&T0KS=!@U-kJWE@f02B7OA0)$nbm*VJ1ys;*#B?KjJPCX= zt!aLMd7(kpa+Nwd-CHSJj|UD56oD(5n%z15iGyW-^XGGQHfC3g|8%J0H*~wQ7xc+g z2cXB5n#sS|Z?n#s=qjhp2cz8j&_PorWKV#{`LNj}34iWdd~UoP&5^#&hzKhcXutCRdy=qI>y#v!f6_5%}@~*M$xY+)i7-x z=1+hBxVD+K0>(+3l!D8|SVQ2#s5D!j;)himNpA~q)wk=zpKryoBuZp?+0s-e)%58n z+=>XjC(Lt3%~k}Cjvwz-`&gE39dDfXYzeu6M|^17&(b_CJGwzdP}s#KX>rTj{}x`w zjH&;o4CujzGcKV!JNL=y>Cy}lV7ZvH7}7rT_W z#i1xk5|#X^2$xa5P>FPw!q{6-n)1Da+SDhyE@% zxw1w#Fw$@PW5fDcKfjYd{~jkV-Ix^7_K?5La%w#|lwPXt(m%9|TMB&QJ^3bLWx_E} zlfgoZD{S~5C+@szm;d_yIGC*wT5|s!3NSH4yN~_f#LUWJ6ZCIl76GVYyc!Y`xR69b zws=!MRqv=e9xAvze2>$yysUoqYAinl7#_)=7i+^I0SRn^0#{H&CAC`1&^%ic}zxJ;d&5Sy~PstMzKQPb~xiUaGPThp~CHXuKf&6 z5W3%kTlFaIQTcK+wu-qgO~d&7m(F!b^xnM+u7R7|?US~#x~()j#1F!WPiCG!Otfw0%ycD@T#(pI9%Ajb+bLTM%rM9qMu9C}# z#PBb=xIACyoFk~76hx2UwF%wCcZ1&4uv|Xa3aNme;RUK z2q>-5TmlGwWCcIwRc2~IPbgiF||ivIb-8}mV)@nUX!RFV*1M2y*5 zNY7SQ&oIXF&zoUvp=!L-Zg+&My+9y;cP`B;MRiW`iJ3*w=&u_uIIw;(R*HSvHz#Hw z8%_I^`w+)&WlJu536Ez)0LFM~z_cv+a;=p^l@ys=HNVx*-^LSlnqbO+`u^E)YR}=O z)1JrA23A>xSw`|tr8)r~Uf{=zmj)A`$6o=!m`zsPDt@+|Z0M6Ihv7|Q^yd}_Xi!Eh z!bKA)bFZSQ2{ZKkiSt6yCBlt*;=UyRsjeVE^~zsT=xcnZ&E_}9k)OZhG^e*4+VMQ( zJJKjfvM~!&2Vz+|(fId+w0Pn--7sNM9M6<2i7N8;vju`a$>1wHtaUSW(EfI4`36ZB z!F{>4k%nVAa4IJ1KliYB`{f{Os#F5acpm~{Va0TzHL?ePrg0y~z1vt(oQ}AAvRPju zr-Q{}6cQ1eKK)8+&1cX52T8AGWI6mD&wiB$Jo#;NnzW8$0Xf2UduR&;Eqas)Y75pl zMMR$PCDO!ZbEVGOs0YjJin&!oF_RCE8S)9#|?i!BF<)Ka8K3TmMTC<$;Y z)WLaH8_bSao2c%1t{Cuij$c2WV=Kep;C~ecS@p)DSBD%c*qGvYqx3>fsmua%++8ix zPwAXMf^HUoOr!Yo`Crbej52(wPDJ)qy1iluMYGT8>}!QoxH(NBCkOqe*O_l$3BarQ*iuSdc$6riD3LbR zd81J-4IcFU3yVmKZHI?>QMLp6_M-Xx=vT{eegb2BgiZjQj)U%-6qEF0?O__Df-iZl z`nz?SEcQOR+#1Fdh!~BhQ-rUrvxSo-7^VX$YCRH`NM;BZ`C1QU;?tX*`WDBpKO4f1 z=Ug!Gk}~jSJ|&Te#`Nrw=;=|^{VJa%H`)~P(c z=)l*uc7h=}|LN!P17EPypi)w*d0O4cEBYwg0wg)N$mj@RadD4V(jC4}OfU;h ze}BLDZZK-owi6IoIg|gTcZiFwBr{{-QvN}F$Ay291sbasAy6&5x0`;DU$rza$YD!j zbbtSyv&zHiI526Xn0bxu{izm`~K{ezgt6Q`2`O`%lOZV zpJs}t&?Mz?+Zxd=oaKL?I}SJ05|6zOosohkvDQGq1R*2H$Ld3c|L+vO0?=#eH+3$d zS+B?g%g8BjMbJ%O6RI|V!1h%4ZL|oS)z5P+Q$$BCkgppdVyUXWlFekt-NwHwotFCC zOgC&+4yRgimk56Km8Wx3z}T)GUVEYN-z!qc;<3csSz7Wq?elChy{EQHJCJ-B20>|{ z1ufIO3$YNDap*h8kVN9E!onWR7C$ix`o^G9ks}frL(v4s*!;VYkfc}>+CPgwdNxuW zcr%@IeZ1?+N2+SC3iZd#w=HBKRzf!~WK+Ump?UAnNfL%B@m1r`h9qA;oa!?BtlO~j z|M4z16acveC=Q`X`ZF+isz2JEj`Fq*w9OSh!*l({d@@prmK+NTh=|1Mp-)_+`S9}NXNk5rQZ zERDPjKd2824YGZHz7ju-9G%9!z5D5ZWxN_uOQE~{!|Z(Q!LF~93lceKnkA>_QW!RKY`#qJeoZ1$cs=-EvyAve_HVDD<_!Nw zzR9knM*+g)n+ytq8vm65_%hANB0I)EoC01amR-m9-{_t&ub%v~BE8!0Q;k0%PuN8W z?lok_v70st$QlH<{|f((gj={z4W;W}{lzf?NdE&rsq=x=;(w3co0+q$N%KXs^jd?4 zNdfF-JFV~uY5^gWG1~%>gpMyPb`SULI4oM-whTMj1wrtqO+UAeP_>}c?!U#e-#t~3 z+?pdxOA`6Hr$2>O3Wz+Z=B9k=2t(H+aL#3byFZr|Y-VdN<6Cu@ z?At@mGWaJ>X@|K!lY%coXjp#X3%Vna^4NXI;$XhCn`!d-Kj0PvNZP2kQc-*O|DC7i=%@1pn2nJmP@d#} z2&Wh+QjbMA`)aBGGkSEJl4|9e64%>50Ssv{z?4ME_&OC38)SL^6Udx&y#IHQ=+!|N zbyv%Gn{wdSYS1hMe&m7vVI1^bIsc5;>^W zD?%sHV{X+&rK4t^NBU%2VoRB!VfQ!!WfvZ1Gwp^In|C~cPSxtvvW_rn*xSv<%lWA>jd|Lj~&J-vt-A0rL}A(Vo1=jY;su`*Y&@9R^b~ zMtsG`E#80CJBrXzfRvN@9|N#b{8YVnErkkBOD`U_F?a&dZ|@KLU^Ux35g+ciYBqA> z&krl*0jyH$Ob7yi{j(SQobe+sNya20o+^Em^s=b+WtvyiTsAj@cA6lcpwcfK(gc}z zKQvmOs;Kl@#~TlfQvG}>>S8&Mr7&GWw}W+u@em=CHH`ne+UStOhu@wJSW*=|V(s45 z^aY}Ox$qAw40`Og+<6p;u?)Di{Hi1o%w(M_PQ!Xu>D7g2v(%t{j@~T2Dp~|mM7-SW zA_4#Hw}*f8$ntncT*__Nb12{~aWS@9S@ZZLV@S7K)0o5GVV&^n>LU}NoGcfZ6ugdg zklr7$e~eTyANk`WB`Cu>7NDoA*ge2!Gx*kvMXL(0rmvq1v+Pt9(r9tJc4qOrwoN+t zk8@$p)Ozfu&*5+un7LuTj%Wpt`rk^p9E_2q+Z|{ljYU-FgS1d!VXZq_GS=vstR?9y zBEN*>`igGZ`eY(Ej~5VAP;^#-(9fslfsc?y@R4x|L`165zrUQ;#D?gNEfLJ0R6;V* zmkT(oq=gEn3spqExAhMXcV1Ssbwz-r4iKc1yYKRCjC20J;A4^Y-9UPmRl$A&M%kSc zU+c_e@^&hv(C%Wbz+9~j4V0GhK%%e2;Vo{_(eJ<6O}+m?tv+GDDFG5mrQ{V1FQtma zCWSJ7__}DVEzlesGi|2k&p<+q{0jvUWLf@=0owU_6MT2^oJRRj8N{jLm_7mYJ`b~~ zO{0IrRd?nNIioyEEBZ;8tbD32817wnQJFyp&Y+RFC~1BIq*OqT()xH3&{WO`S#s;h zUp{sZ2_cvlPaFlvf5f2sAvaDN39s2zLz{5&x_^fFp52-ROc2|%xXO2Jk0-Qq7cZ%- zjyk`P=73Z9VN~FY;vl?Ql3gASzo_k_?~%Nr;bCyG(Tl#i$qf~K|0_yKKJj@+IH{s2 zc16?4!JD`@Xkvq3q?dnrUo8aDD?jWG_uZlrM}t%eBMoAv=gPk@WCKs1t}Te@q-%J# zIUv#E`dd`B%;5cXd}4Zs*~s9;kqO8;0j6uQ?Tf~A8`&6I0@MGmdmm_^5o3!5r16Br z&Xb)1X3KUdm>!MQa`+V$O_HeD*YSfL+rYhech2{|0z(#^5f_AP)~ljAjXomVDeJB) z@6b&0{>_d_uhdcD?sf(Ezx}M!`)S&b zElBmT%7vg!9c2*1?OGTQckf&~-Az->*LeE%&GpD*;OVqp%Dr_9Fi&@#m|eoxzmqr$ zm5NCV(p`qEs4pit_Izkzb!pErR-VIG$P^6Kc%y}`enExl@O?Wm_Q~#|R0T){(OB+f z$0h}-5iqjNu6@j!F3z0l~?rFkm zBV%L5uc}BnOTldq9}i)j@mtEGbHX8DLYS#NIB%fhu%1@=1=d6?_e7#);tF1wh|=M! zDm6yeyb?x1hzY-aYx^BO!lG(*C5O`O{gymoY(~>n7{vyFudrL*{HPt;Lsp%w+vAIR z!980bHxAQ&$r*V9=88smwfy_vUt(n>u5%i}1Wr*ytN5^MulVFI&Y6d*^7nQk#%`wE zN+Q)xbLkwU&0;#se+i;!TPvY#(TYnqJe0+ra+3cRPZC`d@+96TOCOdh8ww#rjP46` zb-3S*!_L|h!bqunsOLf_tofvc1bHVMgS1KFl3$hBgD{J}+Z-Sa>(xI`snAnJRV5F6 zQ)3s+CiXEGOmzLjgQo;ofv)T7VU9z5Ao!L^Y#uMYT8WvRnqnKYpJ; zRq3E$1noaDXglqHAWxyZIX(b+lq`8x=1DJ&!O%e>_vQwIDM|$^`xGjCRuQ&*BPW5E z<4bbj-9}w9I+e+N^p=qS3RUptShuN#Vx%2LBO`88-%l$QN}?f{6}W?$izuX|BSdTLP72+8)twl9fHt)z z%it4Ok0AP>Dc6k&B3m#>^7B-d$+KaN1m5-ee~(RF|0x*8Y^&eZ-*JhE>;1i=f0FBt zq3v%(+~f=>Wh@vNj!jRuivmGSbU~^9P7h>!MQD5<0I~0~{{OWAQ5Qyg}Kg_=Jso66jH z%ueA|Y+T+()+A!0K#N?s|XK^Y$BH%f3g!xUz0MxFa~AF)r^4SUmJrC5!Gh1(qv zyGj~U$SQ7SR9yTe93Xw!ZD2^YzQB&e3ve23mR&Up6>E6#IYHYTqM!$*~W>nEa> zJU0;!kW}deJ7b{^U~m-3pyJ%HZw(GkpRJ$GO3C>P-SfUdeF{$Vmk2zok( zIMU5_zJ2{S8Fz<#cfd}OvYJZh7gFCS#4Hcd0Q&I+4q~;3j}Nf&l71rA(9u+D#A!sT zUgs8pBmxmDF7&J)@?_uK%Q%Ko(IA`AmN`eC!?!Vt<%5aUl)v$8rIaFz zdvlePy^@M8&v35Y)nNghxO4&xA(y>`l4IoaXTJuSF-NUs=)L9=e-=0*@f|G{r*g}B zcR4}-({jV$^iPG9gRL!aS;mpzo-NvL%l2W!obo%@L%f;4*`Go{@zsHJL zMb(+gRhYn+a5iv=)){b2OQfzHYML!>zB+$$`eT>f3ukObkxyWVIr?Oszm3CuBgo-= zwz`6-#V%?s)d}wumfdjK`(aclU8${#He)%->*wP=N>m%4i=d!-g9X#iTwZ&`8q@sN z7Ys~q1T8z6cpw|`&`N0ls}}9BmRxh3c&4!_4kD5KDj8%b<|$^gk&_>fG-7*j@iQ3m zzoV2~!&?Gb(FSR&eYU0c3)w1zYY(gK9Firv?Ah0{Hj42a&^9SUuC=j!4Y@ zjS-#PjI_m8Gy!*Fls=d(8!X(wN@u*KmsEljDjrO4GUPd8Ivsj2e$u+4<9Y_i_;48- zSJTO~^n*e|OZ?$n%zeGy7$MT4?ct&DML!b0Mdeqy!Wg3t5YGTVWjlj|^^GoDHot?k z=@5Ct%n^c%ANe|DGStoUGw+|-xYwcDikXaxAqf2=B!qK_dj6ZM&j}Ug3|EKc%8}@f z7!>3?SGq7Tv--|NE%8-lTnLKOSD_LdeE0oPwhRW-j#h7`&BY;`7+WMbbfk~<<8JBM z9mPFoW)v)=rYNcnUiaNl+%E>RZs~c0;6fdI=i7w7mbBf9$*U7|iM*~!Lp`83hHrw{ z%tom3tgnp;${n&XY2)Z!bur>mAt)GAl|8w%8Q7aID4>`yEV3i9!3pIn#M$=5R(!U0UG5sm%Ua* zft*>+ccU~YjiCAE2}vcQ7jLdD76cyf&bOBc^kVTPv9lwi5@fh5&AJd6^&5~mIe*8Q z5IiH84FA#n^;1iudtZCoJv_4p2}h16HY-{eoSK!@?(u3fyvO;hxssm9ap?|@?W_ks3}u%m?usmcRQ&RznYKETx{B^LD1oSy3Wlo zdt5fck?OXyY6X}Rn-ldPGTe_;e-d(nRl+#n15aU-RP=G*P~Kyc%hWBAVxyJBeG+1U zz#5k~%k;~pkxiuAPRkRE+pAFCp={UoVdeibonv8U{}3jiuW1I7KB zTI~Rs)lMP1#+Ue+&R&q5uL1PfefqIyBRte(X%^YCh3E$kYmQ=7g*B+voA8Zhn&AsB z7n(Xduyot(S`1ht$-z?xM9v$AMdiit4Ojb1*h@0}B#I&Dd97k;j7+z?^Oa7#_(M2ZK{8;|h6lS#BKasc z!l%-GXZ`5cKRO!e@h*h#xPOu*kc{$C*jg#mV+9Cdy;=4bTC{ywe%@^8UyKN^a`i{o zbg&jk*eP&iLS}P&4Ck|2?K8bMGH)qnbdNAKBoc{uwU3S(&Pn9o4QI24*S_)YMj{sqCUmm?4*@u0Zz~BK5x=j_H>i9aa7ZM^#6Io*D*gRhn;W$# z0;{sWQ&d6R`_2hVZoYyITV$pcIOItjBv>&lefNyRZw!849Zl`HxjSDPO+f+??HGy3{IWNcP7@n z{S8dkHwy5Xso1fBQQqpvHoAvvOTBq`8O& zMKwHs{(w6Mw#DZb(P@Q@HfCi-s2yh~&^`;I-Yp&TObQiz=U_ATX) zwA;V~?Xu5L(`=E7mFGW|Le>uFS`fiLWiJxqCa?X^USo9XZ*crOVPivy#gPwA?MFQ%QPvVxlIZJ`P%Y9nD_rn4NNOE=YvVnR6gxih)=XMdZdI9jSvl`$jQozplSFu*hUD`Pde$ixL*`v)*o;E;ELK`6DAqzp1HtZD7kT)@Ry)6ekZe zNT`5CI@0_)bDMxLMI<@O_9e3>9^G>kRk{)P2pJdj|5Q2MdBAp}=t}hQYZgdevakK%?Na-X3A(E|_O}iT)~On$UXw#_}?)B23~uzH-C-lRx$%LIi)v3Ez!?|t;NwL@V;5rSB!kg{5Z(DLa*Z28>QHk?^d zP|RlF)t)KeILt*}K+Zz5{Cj$)qcG^Q{P}1anNpEUn$PCgulEAl!d^>h9f?lk>gqoP zPB}2?*V3$1JB&&HLSfimN#MS8ve40*;gtUv|3r5(5;dR+z-5wX_Z ze9#VO48~hZWxqwhAr%)52(vI=mXH716@dX0k6L3hi^Zf}Cv0elnJ$lbdUi_2P(mhl zc6yp^`KF|_bj8uzN1pE7Ks9n=LV2HVF}S3J{$xd-4(r;8d4p90uRj6qy;8Pkiz0QW z$}^LAai5Hb#J6$O4u3w{@3xX?=6u(lyo?@aY}sNhO5leOfOqASNOJdOwL>;?OY@j; zaE9>m05M+;_Og4tg}dxluq8{j+7O;nVt&m%qFU?SGM0|l;y1(5hVArmp2uO8y;W>4 znRKv!)WdzMkNjlE)Tv{*_yQ%Tld1J;`F*?WN}JF_RMqq{NrE10(dn;96_84#%kHPe z!EHeH5$pDNUBPiWe}LRl&-*5%{_;DDn4tc3mi_TaD5>%Ng~kVAf_O&vVCYckV737m zHHtts4j#6vZyMu`YH~`W76yrJZVyde*C;qPC~x~Lt`cbVZDs;{B94S+B(kU*qtpfc zvyKC1L}-+=7r8C)(Y?5Me7d(QSth3%IN~O&y{F^y_n~MWuZ~qb>@V-*obi8pq7cug z1^c;8g#U66a)0CLKLWD=8A~L;(RJ^Us5dLKj7{Hucxky3aXX9eyo42LP6rC9M*Rut zRY(#O;MwO#xBcFdDIS+I)s!MO2#lkPJOmmf6zXS2d;+Wj;T1+eyqVv0@p2Frp!) z9_o9KY+eO83F~ql!mz>4@-GP#h1UPIX7DNU5)z&n*RAM3#)PPY3hA)-GMudkl*{g& zV|I$L;PVvKXg^{QL?9le1}q6QB41PBFa2h`*_&VH-khY$?yh*Z_k+PQA~AwRfS$6U zR&DCnTE_e8llJx4yxi}MvKR^3Ma(WcUaxL12?Ou%r=X&(sb6UxkbaCu8WxE<82_vl z^jcozHED~#8nTm&AKp7%{x;tqNTN#FSr2X6SvQY0R!MTpXan}tBjl5~VV63pYD#)- zVHA^l9e;qkA#hkFz%7iK(GJte|IeM}dEMaO*$K78WT|$D+UUs$ONNWgv2_#vG&X^1 z^n2MtMPjadRH*s(ml)4!W$>;L@{bcr;(Mf4b?T zcMOcmlgTtg(%)vw#f7~mH~Y~3oNYipu>g6l&N_6dV^U_QsSRQ(k_ zb5O~f4-64qhFZ5@&dO@uzI5Qh{q@^ZQA2|=UHO?hT}4rQoh|iDqbY;>H@y)1)*A!{ zjdJmKwrHrj+|%~b0V=M-)2>y|exo9&s(sX;y{6XG7?SB3o<|MWri$BD2li*r2==0h zY+GhVhe~G_+20?;>P0?yc%POLu;kbuwXkmYudfSmQnd;ZD7?vV-KjPFCio2-sl?H{ z8S|SU-BOqP@<$Z~o;tw_>B#E=-axmpY{5Feb+ZMREeJUuiXsJYm*w{?BR;r7#XQXC z@Hn&u@_0OoP(1$B{t>P#+Zp04FGL_ku`n-n0q57H;MQoiXjWjmjjzKZytIUe(Z^Sl(wVBNpZ>D zqt~f~{X7=)f@5Wcbo*ttHM!RN;4|GSFD6Od=YC2gXk3^aB2BJv^Nm`bZ~Yyee3unm z{0sOg4k|^amv(4lyUPU%$l@4vo*}5M?U-NjpW>ML;UL4r^G$3gN*c6@K$LWKqrtB! z7{U7+^r{jq39VZgIiDT_|J^P}GtZx&0Tc)r9OF0m`p$Cg?&QAuV^GY#S9P|F$V2Mo zy&GG4xJ~ibi2ir`U{d_!xCMz_&AZ78?dNkjUgfcP6j{^4KydHmlKkW|lTI6y%c)e3 zCT=R@{??Q2fWJvO2TLF(0bTJ=XB7y=Ksh2haY?$ZOQcFly8oCwWX6rG8cwSaGw%Xl zw5X~kV#*mv@9gln9-E^KQu6#A6iE``B-lifeQ%?M_57@>pyizRj-r+Pro8uyL>s}7 z|9PovYa;=sYC*MxVQ}o5b&5{O>**&muX0g)@4GamnOxkalI~(p=4iPY&nx-g%}VJ% z+sFTNO&P!^lnFa$X7x|q8Gjc}ju@03^SM@#^XvP{q}`MH(SVh@o(};Cz~$M_f1QbW zG5s`5nC}VSit1@ZOM{7tZ97DcB6e1Xm_uS;jOXk5j~;NZ?JLoSb5I1*Kb-Bly>|BI z=P}>EFr0Reo9vQPcpI^M{I(p!T*oImql}{W%h_h>_I#6h;x6a<;(HIUXI$*``ta=i zD`xsl*6SNyQcm4C_iq4U08QKWj&Pb&Cb zzJ6m)`vjs8NXT^?OfUDS8RN{{BuKUApoml;wgZpYlr3BTfP>`6R?94YUdNhYTxL+n z1SWv73#sWG|7=c#nkh1#d>GXnPH7AwTt5Lh2hXC4@wsS}QIe2DaG(4c^+VWMkXkmM zndJJdk@`U(3hCZAXymRJkAB9A1v2{jjv484N2{ui;P_ej#n@;-^I=paspsNrR^~-4 z;+;lHorlLR9upM)QmCDzYGfQ3b0Ln%7Y>QwvYpG(I9htaV?c(0`tm~{EMl&6O8Ki- zj97=XNjQOYdhgh2*WiaK!i}Nld1PxQ8#SOPYvSTgPpldPjLJenI;kbNKMW04v^_*n z%Y9mj6?$mT@@MWE$>3AeR$&^+TWoI8!$4K^`ULxyiiuIN08`7fpYCb8B7d5$xbX4D zXBzMetUAdtfo^X1?tCXiBsg!rY_37HKR#O{*6u2U5%ub{((dP}a@dc1#qvjv6BV zX2?$=s-U zlOGf3n}Cxo89nx$9!=7~NHs$;`}jKWD(9D9Ktz&lsoM-CD1lbTLqjfO_uZ%rPsuBw zReqQ9+6+{z&sabMTV3k#fk=Z4uV^BdOabZx(SJy(MHCE4EIux0;F!`Gw-YUG@>Kxjkd;9`r&aKWz#?2=Sfohrv1g`oU{Zc0rd|WmlB3@X*Vj*N z0X=97u3Ov9C6)eb3o`%1HfHudarDoPNg|+T5c3~QfRj--SYtthm|h*~UYib#Uf)+MoOE3+=jG?a*~Mz-7hqRX>>G5# z4g6Rx;7M<32X>pwTTf#n@UCZTF~9BocR44)T9ccUha0?1PgARf6!aF&QIDN)sN)bU za>THBb3H6~uPG!@>pdl?KgLGfV*%g>pk%mtEHCk=~R&=iTnM{K^pDsJECKE5};3L*Lmjr_9c8UDRUvZn1SMhO*|C1xpjYZo{Um7yaw z0|&jX!^REwHjWr^h&q@Juu4^C-qEI_NY(J)Bd(Qsgzr}*ZQ2igTHmkAX)5b3;UmUC zP}^p2f<&!JzTA&Wz8awXxH|5AR1}N%)W#O({gGNGa9;K!-rzS45wF{N^t(aB*YkDm zrT~0#DfdS2$y2KztuEukv=54Cc{qs_@KB%F63|Hi471Rc!Wrn^;;keVfU{qP6#Uin z7Ej@@3fV$^>jeQMKf1Kh#U{%WehUkbjKb=4m8SFP8-MVBu?VAZ<0qh;QHI+j{U2^1 zUWZxO^A|T5a{+=|%dxRXf#H{lBi(n#xgyCoS^j}1?m{y~BRJ@)@9z&V>UTGh$akW7 z&p4WI9`*x9Ly0_JSd5G!yV`N1zi@KO8#Kz;XxkRV8ml@lg@;aymmE6&-!xA6=S%1# zOE)f+PPg=!I6v-1DCbM<3TZ-kgf2wYatxsd9QSvrM9Sj+S8?K3J%ELX=ORZ#qzCXZ zLZLnfbAGv-NOBh5PYFG}?BKUl5CBHV4Oh@MGC3is=6t-iokfOD?RIiKwdc_4x~RY~ z5336+giPDb;NazYJ5A`m;N7=~2WIFKA8k%1JLZ810z7<+= zvzYuYlM3#SuHZ|}S~A=qnf?}WR^kLJ^!}F!L=iB$lPikE+mlV)F?@$SM6SP-w&#Cf zAfM@W(n(BmLRY>jfz{LHhQ@^vVqB3(5JMI{4&Ny^B2IvkU?*1Gg-z~&(|cRl!^i^H zycTTn3N2*iWsnh-%}hP?vfmw3>;0M>77DXR_wzC&kLXW8jQ?sQ~+|8#O!P`>>Dl^U$EiQ8d zWG_^6oGE7gCaMe+Lj?2icZTemVVka!<_56f92T7K-d*tNYtsJy^=obS1Y3+z<$x;H zMlr-qrQxW`T$oR32B{B&G${126_>e?o$B_;zr6c>iXI^sfj_X3h|j;Hd7&y}Lh!9q z!b$cECxrTZzR6vfxN~;#a&Mln+DyDorWWOM_XK~Yiv{8rzC&NClF7Ja(hf|M1Mhxl ze>6H#io4R}Yf6Q-50*zhFG?H9`?><@PtMt$rqa&4ojvY?8|qGJbS?06Rbogvv|7dVPjNnds{xqo#)MZ;YNASXX1J zs$;iIT{;lZnlSTBMjptks8G`LHwaW^5-JT7Oci@@z}C567rY~MXE)zT}%a(w2$nRq{CfTH-N zRuhoX*7WXINQJnw=G?T~>As1SSWf*K`R*Y|j7L#ok^`0Z55KlGZl>S19g+L+N`T27 zDH{b9saC*Ji?z9S%BCrPx-Ld62i|t&>)dyblDZD2GoJhG^1LKh%LJa20*{@NPW#!e zv`>ssSqjCFhr`;p?eTAsVe>Oy?|koI`EQ4Z$ZtYmnJB&~-K;KG_vo;NfbA@w?(0yu zbRF})2@)cSR-%;n)UF8BSmM@SawqsbLiWXKUNMX2&QnYa<67}Jse9gmbrV@Y&!*7! zKyvmb=UL)WRsePkF_xps#B`SD3n?Ne17WSd4P^rj6+|O5{X<76DTN?N@%eO*6rKdy zNUCSS^hi5+>C%-jZU~;+=}$DFVvn9am4_HPnZz_X!E26p2m-^nn_EbRC>+8oGJ>Z6 z&~#0Pi#Z8r;6hqtL@g)Jv{574p}U1Q7BRoxQOBgsYJaHHeD!emiiJh&ohzpcbaB`c zO++!4&iX|}qq#L;E;nk4fEsLWvoprf?NFjP)ge37)2ql{mh39DT=$LPSbQHhHGyM! zEuTpJ2idxV_Myq8-YtC}T*t8w*&z(fZ!}PC#voK$nzwyU3)ivUUFceI$=lu96@5GS z9}fqQ&r(Q3L+D!XZ~6Ei?yaBbU*S6Q3^$6ymi4c3xvJ`F#)E0>Y-f2ln0xFF%YOAM zWs?rONPUuJvaau<9WugAOz-@EEIyl)*l3wOgPkCYwm`&WNcxn#Zr_(hkT80op%Ycw z7h~x%Z#Po||LC*l+swDOUTGOOg`dIxvMZKXdEtyQWb4WwHb5lP z1$Og+wNi+ZXFv!}Ov>#g!;*_dyHiJ7E8P19ggx1taf9iGJO#|hMz_Py=MTGC9ok2u zn17HF+AM@!CO&AxFLB%hAF-vEL4t`#sr#(6@h~ECm#GSZa=Lr& ze!LN>0yBtLuAuTEE9D^&%=Da_{;jVlIK*$FiJiH6(_K?v2uI?8D-Y-0!sh&GOvvL0 z4vcAkE3byAbLO*%sgDg@&yyJ-oZ}ajfCBZT@gfQ#R>N9Mh@S*HT~{sI*R;?j{jce* ztxiVDc2j0rsTIpa4V98?L|5ucGQUmh-rEcd7|F78$$7#ar>uO$(!{jpC4r4&fyIa^hu}fM_D^Y zY&)d36BB^qptxvSF8Li%e!d%M6Z=kPvDvl5@*9@0idju7^0kfXcIq?#zK@!g*QE9~ zEoLm&M341HRdHqDmyZs6wMe_-?4*)|rWM?EwCtnxC?DV7Dt$vWp+v2$^<$X_9Y2fj zO}19e*Ue0+OyuHHT}9K|$VA8EhhUhIEYRQ@a_d4|Pkf3>BE)}4FbB~;1Wlw)f17tR z=o`_UsW)Y4eK>kNJ~2lTo7?}$z|v7KH`>?xQ=9d4SWYx1)cTPZ>;xiiU-ImB)1Sbg!33Eq=mGhiqw8{)2r+UM_1pa|D9 z0KMYApBXegoYffG@)&i0BSrs4JY{Up38hrFe_M-^swR|h@U#kI;f(7dip;q1$Sb%w zO_h_466K?SwIRLF+0V?AJ{n}0voXO~#G`H}pueawn1WHU=%{vOHl+W~`hFa~eeF(r zgXK$Qgvibq1(X_3hj2=z^|ZXt=>V;V`zD{}!&cT;*r>NB)MG&+&;#3W119ERAoD!v z1%idThRU#B5C=$0M4_*czuXv|R%N(uR>k@TC~~JqGR7KILW@wUDGTbIr|ru0nxLEH zhQ_MBHO>!b1-dWUGfUI(>{4u&(P5Ncx$OL6+hrzH_q8}_n?Jz264o+U&a*bJv7MZA zn@U=qyCAHpHR01?rhWLXkX4DYZcF=lB@7NzvFU)030u*u@A6=**x1{}Vw$TwecP|W zc*|~|E>FVaxt5aFD$IL~$!x3+N*oKRFKfA-VK>wzQI2Y_hA6BXW7Mpt!l4lwA0m)Z z#v8yl*jd2zEEjHCuuzi&xYeFTPf4w2t(`ywGIFJTTdU(0)8i?)*xUTO#vA>jzF{ky zYipv8kW0{(ytQCnVm9zKA9KgoRez(31AOaO|dOpwrl~*TPLC0jU8;n4KHRZGKv_=mc`;Dpv^b|+948( zt3&l34)cz{|6~MT85Ffd2p7i@+&8b$`AWp6IsP^VXU$5J6>Rck+9ulIj{CCgLg{}hvVl7W@Yup990l*#hQy`do&YE#d`8jRzAme z{ky*Ajrm$j*ibQC<5j5)7x`z~VwEG!?u8$Q`6g+y;cjafnLE)mbYH#}0*!+NOd^m|k+QE~{2Fo1qwuiDN%*ao)NZ8yh%e&7d~JBKg2M;U$ZzyI&Nv1NBma@|eO&*c zFIM`AN&J!n(Rc1ML#N$_1-@!dw2;34WxQ7756#m?bBF}&Lx&F?J zULHV#-e5XV;RC+Ih@~aevr4dz;yaaBAebGh_y{fKQ4Y3yB_d%_(mesH4C}ZaJ)P(O z=r^|D%IboF^FEY7LK|pd*N8}TS`I+iSG-1p6C7f&HVmwMrU5k$bFyt#C2WgoVdb*;sO#U)Bf)s z5&1J*F$&9+!s^wRq42M3SrrJ}i`y+Rp-v&pvQ~5~lqhxDR}E&1bSY=hr_kSQ{i8_& z;S$5*$!Js^n&xHy8UC@%gJ{UA&OI1xL?`!UQ*5garzr|y*zI_O}(moBaBH|Tz`J^@rdgp$^u&LBgz5X|oLjLjVvEReR&B;|;Lui22g_lo~){5|3 zuFnEvDpHt(vTxMs$fy4@LJtD5mNt|B5vSnX@~b2NJbUQWT^KWaOcu%H{m$U2J^;R{ zr2DGf5hNpxg@O^DvpdHYi++va2E}A~U;N)3Z!)_0%&kW_ZOxK}_-JW&g{-GW2mNN` z5EC~6(0BX^6wVd(*{_`_AbB= zP(u;#R*gLU5)+;ovKu2sc=#(u*Tw$v z&~t@h?;kDUbV5fw`=|5V(*BQVFS(!m8JYP8q_ZKMO?QdH6@Lzmt~1VriP=vDE(1t_ zP39gW=tXyIckgF4a|QzGe^oa9Jt%dMlqmNY%0Dg40MU<9hrEs=cu$8bQ78`GA|jpe z^Go&oJ}JJ0R2-^+Q_Q%-oeC(ef@ox4vC*3qps{DSLYK5r5zMT>q&WJDVyNz05w@eC z8LbV!HU6kiL^>9No}5`%O2`WnLk(}`JEP>^c|i}#P2d}zBQna=26;zdx=`XEUv)y_ zG@6G6c}0kA|I828IxyvxJVu6U$JxNo^S+Gto;OCA)_Z<%gy7yKrT8M45QRiG00ryY zNJ}b9;E^So7zA4Zt5K*(d+O8w_tsd=a4OtUJagWijB?is*pGg$;V;)GMd_D$HZkCx zX1{PDe3~i=cjgqcbW9&d7O7HJJD!q!^}Vf|LXj7T^kPHWqf*gOW$@P z$~cbNZPNq)Z2KPZ>FHfdx|QH~hQY3F383p59*Ozv;j8ujuNAokI$wdS&=R?=&rU+v z4uTWEWS*)H_oE#afrV584dMZJv@OPWXOS>{M<7ol3ns9Q_?w&dA3h-XA)fko)w^Nd zzYGfdf-)Y9itsK+695=lmg0kOp7p=t-4=@JiU43c zYMOU%-Mx0kvKi;zT+ma~g2M}5EWVS?!rim9{{EoUxWcnUHM$4WpLKqJi@SX~Dt8qlZpCRiCF~@7bsN`5oa( zdTz5ucK{uXiE3p*k8>)FUmWd?)9yWblR31i)fxV;niXi0j2o(#(8NTY;BDxkygnf$I!%${Ap}BN}lF$;}cQ=*YbAjBz9=eJ91a{k>{Z=X;OVO3hdGT z58I!m1PZa?QQ0XpTJGbdJ#SU#QhJKXee`)lVP!(0Rvk^yqFT{?3v~ppG-G82cF3NY z;&9~Cs|X4_!xYgBQ*>Ucf7#SF>ZjGQBtWa^I!@>bR+Y3;3wyTl%#Z{9ar4KMB@yiP zQ9gUyy{78s<9Y)VDkqehsl<;SfIfKyL>Y!p)f>3e|n z$9>eV&dVrqe&+_vO)!_~cY~=-4qx%Ew&P^Pu^^r!1Q;>Qv$F*H&qSc2S5~K zByo>ty!pI;ZxAP^YLM&>qCON$mihkLGdNw3DJU!n&I(Sv?@BH9dhBXg)959wcZRDGUf24K!zj(cXQCS;GTC<_!4yp}@7_opH~ zmrKU~1&!rOd4HkNB`vi{^W@)Oa2U1h*1TZimwfI9eW!HPVHF4gN&vx=;L4@%(~@kl zC_;Jj!}&DIg~<3DlRsD%c<4Md_E~9E+)O)UCOF-DGb75j5-9##01$x5X*U>;rK{L0 zqa%T(mPqDm;tkunQqVY1jy_x}h;`kRNreDHf~7Ou{FVG0r#%o2+4p1&njo2<5pJPC z%uC<<5dKzxIgyhgPt5D+!~3U%fl*z1#gvQq%lbvT?b-uVEum9T~;3oMHYjjg5|A zU2G?I$P18|2>G3exPrAMl3S@DwE+IuLj(ZeXus)CV<+gPSpP~W!rbi%&MNs%QrZ>q zP4*A*H`!BcF^3t9F}9YS5@oYSBGD@BQv7dp^DZBnXH9XCw-pMWYM?zjb7lZR-Bn%r zYk>46QD1H4h)jD;@ko5)i=;Tq->Cxtq~`<3>Xfua+^>_VQyE?z_oIL;MqmrsBD|$VCb6{qQO#n1uQRP+;-fjcc7jL(K6lel<97JSv*===k zT0pjwmS|4F@I|=O2FlyYmi5imjBfbVM}Fmx+~un|Td;AP_(X%juv#{vyTw`;^`Z`! zPoRl#w_R_5HKj`!+F6~Ckv;p_sj;KyoDd1W#%dUCN@w70%jlWs=%leSI`{*>hwDF4m;gtza(nxp^ z?oPx7?smMOHBeH&#}{t~!BzW!A)Qp?Gv1yCD2~L5At+e#IE44*Laly&7zfpM9|eEL zzty@P#g72dRuqVII@aSpB8p5Ph&j+eAf9)N%5OQ^jJ3i|h^U)huMV%TD4T|rC^sv5X z6X!r**9_WkRLzGaOx^;?k%+8u4ILGi3@ONKJ2bW}x^ww>p7%TcAG|5-oyM;87CcwE z@`Ta-wy=fF`j;dpEah6~7vB=Zw}$Q7@_@BXcfA~xdx7Q!v|tdVIyvojN73~oP>%;v z*aQc`owD}?L@)|Ez<)L35b=j$)s5SE2X5d_dD?^tt*1v+eq$nm3n5E^p%;$gaAx5d zkZvr;eO6y@wl+RrT9Zf)B$*I}KpZU--jG1(3-7FyS7GuQr>Y-9d0)GJ9>guBz3TA^ zRPVm4JduH~it_-gKq2FU6Jiskz)HmNRZ|(W1+qkk$tT;ajB%G;<}8v)54OQ-pd45% z&i0THrrd&%OI#|t&0UQESXs!`|;!y?c93lv7-x}ckZwXu!oOOgd5Fl>y(vyH9TXXET z{x_h={^udAM|)+mz|;zdl%}L&Rt5Pot)_*G4flD`=+DY`!SZZvF)WBM?zs>%nfm7C z%<{1IR@xy#Xi7wliI+ix6DiF=O6Y;iCKkLks#^*2r{ZqHi7%YjcD8T?vNK!Mh_P0a zkOd4fBMN}zN65x+jx$8{zmZ;F>x(TX-~_TWf~Eb!e!Tz#l=|du+D4WBw`WCU`l*2N z$7MH9xo-_sXi5QlxVrzLmA&S=ALK9a-1zSm6zGGj-2PA30I9886;~Lvm+)2rogmDJ!fPk$x~A9?3}%Kc#H z^kDS!&oNKA(7RpXUCcp5-(+Q;Fmh)J_#Vu`jyAq4uRPk3Ip~9`5TE&ck4zdYeYxi^ zd<_AzU9eAhbxH=f`5tue8XQblAiZ_3Y5`5A)9;Bb#pqK-5jR0fRuQ|ihoItAJ7 zNO#(DUQySRy}8%QJL;Ma5#eOA6riW%4f`uO^`HZ0kEv4-?0{^MDH?wpC31oF>+7vc z8L1qWmGq;vKb8BvpF9=(KdbE3VsXbXaSb@XmY%rUh*uaO!)r*;hbrSR z;dXP(rOOjoF$BCLC;Q9$+m)MtbU&`kYPhwTWG%QpE{B3$*mO8?NZ}7M=Ds-naabPm zn{PzA-9KQWl&(r}>ZYWg#4;>741i$g5ALSYYm7d!PmDdbs{#(nBQb&d~EDk`zq;7UT~+cq7GL3(%c z!D{V6_MP$AgG=OBR-(7e6hohBGbzuga+Wf};j8b(93W<|FGNRS`X5*cokf2|i^9m{ zXz6tX{FX+8VLTf@lTliPpdd&O)d%?mTnB)-GLu_=_mBALT3guwx{=5K?)z6B|BtNH zi?(4xgBead|LOw)y4dWL01f%9GJDhtg0nPi@dH33xX@AHM-sojY@?9~9?^PJ07AwX zg&sgUH)+)GCOQ;0lkq*&SCFGy9;H%R)E1bdD0%Jv$Ia+NC;maz)1JNQCV4(Eb}>N(OZ<#0swQ(5w0x+ zfMri+T{pyoAmJOLseeZsPv%3pzCE&^XaK+LC!ED;+B2JV-qs1rgnwP)A{aRC(msV0 zq07B@yzS@eG%+aOc>jU|2oyu^;jT37}n{jmve4{T5OS>EtH=m2mG;}kDtG|G%0J4p=C4}mQl4thW9q^KDJ@p|Q|&OAZAS~J^5+~6 z*v+axD{E@rt#+J_mi%lyn*XRY-AU3$@whV#(54m7*U-OK|0+MOp<~G=>G{K-pIM4k z;G6Fs9fA!bWA0%{P|4QP>*%QhpnJ#}jBE_^*q(`!v9?}n5c_Bx64JY*F47Wv@AQ)8 zBl=nr@(WO3wlJr}-va~CmR$$HZ8Vxal_)THK9)@(k3wL1@wkC4+uvr^c+Xhd7@A<@ zXUTuJTSSUB7X%TL#A)1bH&PW2;KD9^#Tt=;OKf;=pMs$v;e~vNO5a|}aZUOFX5Gx$l7=(nC4KIPj1g%B#dvfuYl8X|Jjd~ z#HFL!+=8`v?96UTVzrR0_#y;R+)6Mety|4P2uRVr^&_KpD!;g!fQ8K^%Y2`HXx zHs&s)*jpDPR-_V`)3#%sQqfwBjcLm~s0s0a!65jdk0yhsJwTo(_WdRR*nZUnA}T?E zuajQ1_;axE_QvyHPsSbwcVDB%MDhOzHNFmLuply6*kNbG+k*p!a#!apxN!lci`-EbUEKV-m;I)=?Gg_Gr<^9=ETPV%8#!5nlXn9P7%+ zjczlN@+1=>-o;EA0YsCnIAMg&V1?*{6X9T}OIDiNR)ZOJ6;JE%eB#o8)K$QI5`V~= zM_aDs!hQ`K0ko?)+~3WuWqTCQ&Ri?th{Gm+z~Bq8D%F5c^b&ADzHg-3Xv-#ESW6wV zr#qEpb9DW;yInW?26#1QW;s@^Zv7D3nD%_!Dl!{Lu14}!jy|75_Ni7ZSTz6yaAZ)c0d`A5tAh&}BhKrx?*{-7wbqGnuasVAymnau%6Fc|R#?oKVNQfR**t<<26<){PM@ydxuVX32)C*T}u$%x+QSLMTUUJOWph zWdpk$AlnUxF(!%BpuDnsJatT9CYvw`+g;&Sx;>@8D8nmY#&M2Da%tDT*!9 zfM6Wm{_^Aq$fcMim_WoXs>*GjJ2F8c_izFMj*E3i@M>)VHOL=OD_l$D_SOX+O}6 zU|>t`e+=9VIfT|@>C<%bTP37u=?&-IDm#eNNh(6&>dyxy8Qc~tI<~*2LJ49*nWwWff z#eW)H!a4DrDd$QBG&tr%+mRuNiqe1yOjVrS5Ld%qOh_qYy95c zkkMVBRjCjdnEF#RnHmQmM0Gp23n-h;X$~kXLagXoG1~`4KG}_sj+?uZXwiGI+d_J^ zk=!kVkl2iTd0{kbCPSQ)_C7`&ZK#tf+kNotdft*NTxjDZ)fd(<8p1ja48m27uD(O! zS6i4ghwo|Q2{!Y4&CDbLdBKu+P#~{bTbAM_2LHC8nI>m zvkhD|!gP6!{(s^&)to-8ODEhfH4Mu=b!m-;WRfHS)p+@?lv}EN!kwB| zwSR*XhkSrWI>64Nk#}b6-P*e|{S<)(4)fV3m+wzV!6dK`XXj7FkD4ziGkEj633F5d zHPKCzGdYg%b+``%!aaYdkvgB9@ar+B$lYt_jV$8BqAUkEtqv>g+!s;S5@>NNoVdK~ z*GVNct7j=rN3lVRY&(tgwMR%vE;cd11eFE+I?e;A(W@Au8&&G^LRI;?73IqXFE z%3<5CFd@8WJ+Suk)ET(OeHs$n+I%Dtkp!B~>RuL+YD=&lzQIpSNZh83!a}&yqgW1` zL7zrWx5#EH2I9O7NViZ&unswgks=b`)Ty&JcY05Bo0t?FS-4qNKEqj^qo@Xbq@gP~^TiV638!a2#DZosU z(@VrY%Yha@N9if!v~H$i-Jn!G+R$;5cY1n%(h~p(FUJ|^d}y!R1ENkOU)0E7S0Y8- zy>c4<3JTUuVUl{l-de z4Shiof5^yYz;14aBNQFTUvH_(LI=`d8z(Ys=3PN642GYWW~CAMjt5{rT`0@kIjQX} z&CMg0qcJ1O`dRYmBb!bXVp4Y6GYMyVgQcNfYVUXYV|};EQjMJTTa=;E3HGyrmlwYs ze@m8Q0FMD_g5!JTiX-W{$?$Wgy95%DynZtv;Uy5da68<=1}8%%gTM2cs&b{OFzXtL955aT{hPJU!NWutB{g)#YLT zF=hhL0MibP2YBY$QFA0w2<4ie&nCE*@xXw6DJ};G>VCP`QHvL7V^uPa{Tg13+z<1Y zr4G8BZ(hN>)fpScd?!_|!F{aTH9=9R7uCITsY7a*Ijdg8##n^}bnY)z)GQsJ1oFle zw;;cO41nssGPs3m)UAXdmoBtxz2He89|*QzTlC&_9pt_*QOE<5rD#St!Pvw*q_q|h z7%<`y`3{sgcEMX~KDX%2vM0#bkt^i>2(}N=5Nh)N^q+e}nuGuS1)?EE43 zed&1bs(nqA(}|9sCzQ|-{ZQTO4D>HMlIgBl$z&dtfpo|Jv63c7lmKWgpI0d2I9Pnn@3YIo$@#U+ zr2_c+r|C55k~cout? z`53Ki65iyb-Lm&r+*BXHQ)IUC3_AJ{%i9HU;QscIKJx2qDdm1i7Yg&@Ud|^I;_GY) zj+1zKiC5>b2V|)4a0mWUG14#J73oWj|HZjC2t}UxW?B8=_qkZ6ES@c+H+yN@wXAdWACgjs4#Ao48wyCJZKj=QRns@O1pVwTYLf75gfVBx{>aSYRUt3$%wY4or zR1s39qKV@4AH?$AqH?S!QH++RuB)f?B4w`A!^$@zG|XZ?u}K<+bn9|jO_Bga2Qt_M zhVKkSQpJ6ca0^GMzov!_&LB?T6V0#`48)}4(MYf^@nW&UeC-flZe#~07Q_v+hP?QRWcfxi z=(Dkkdll{U=ftO7-(?CW%%a@=?3<_N$1T?L(vYGq$XFD;KNs&$2w_ejJCE{5ory*w z!fE#h&VTs3`D_Uk-7~AT9>68;f>m1UZ;#ck5{4HF>Jn>7d#n zKx$FwM-(@gzo0*;z7w`xDB3mB3guaeRA8(--+U31YotGjZ}hW+#@=V7!utLv$5|@$ z4-xqsk@x7n%Z0>GX6fO*KoX|J+p{Txhx0_{YJ0Ml`HlCnEvc>(Gf;!GMiCa9RnmN% zb|>ewSg-g=5_^-m$y{e{B4)@`(VI~G0>(ObN)QN}J8|;u1VJu!J$?+*a1%2}M(<1q z!{6`4yx4yA&!>Qr0sQv(GJUkQJ@(ag!M&dd6x_w>R0CDG;n@Pu++aXU9SlhAmR>$^ zQE(P#9+bgFn1s-IZW=^NzQ87gk)UBD9R>b(#^mpN5Qr)*Ysmzq zK# zFo5TX-uaFQd=4X*yib>`5Z|Ih(maG8O6#J@rk#taH{|0_BvDBh3Ti8AE}|b4p&uKQ zw1qSj%$NpE>E8EvIT@u{bl-BdB4Of_Gu4kD(yeLAI;Nmx=PigE=dB7Oe0_O7aUw)! zwGF0d6DI;OC7=D`vcc%ii9IgFPV56RnoVB#0OOTKSvH}^M*X6@&iXzfohpqT8;8v& z^;Y{|CsJ<+zgYm@4=C1jC-%kp{ugP<|5DK@FqLokOsno8uyJ8y0hU1C)&?j5OVekO2fu}hF#*N94Jl23Ms$S?rxU(5 z=g-->vxg%j#q&EY$9x`HooQU!IJjEU`&K3%r(m1uY7&vCUip+tE3cRLi0RN&%5r`3 z`;UlF-``<=`VjH*F@2H8C`|=+jl0Eg*`fo`A6{srlbnvyJ5HI;n-2E z*&_JAPXp^K8e;pFfK-pFzdJ!S7F3?@=l1CCRFy+Y*07Tp5OD`hz!SB0v{rcihd@VN zA=%0>(0TA0*e%)rE5(1S|GK9KdVWZCtIzyxg5CG$QEq6(j>0a|7GQKs@VgItF@__N zCfa{bEkGOqz<%M~R13yH=2E%t;%0m^$|4w;a=`f<%F)BCcx>CPZz}#PIR59cvp$>o zFNI-sHlTAO_Mwip1@v$i^v(#w9i&Im^pJ&0gKZ^<2}TT#E0&L&K`x*QVTZSB)A7S7 zmjpFfl|ymW*cL(pwbFUnLXz-d6h&0jsw;Wy8gP6;9k%cg;=HYJ)r{yr&D3f&#mFrL zs15n%C&CaHQsktQDYD^-P~?ZmT6DMpJt{r}cUiz@ODe2wMMZ-mICUza`BT@)oh_eT znZs9LUQ!vjD6zD))16W0u~|f&I@!Srh%OmR_`qVYR$F^r7sclVlnxPkX_6MJopNQ* z{^-`^x@0U9kB#Hd<=ENMSe9k0pN~h)-UE;TZG=3y{!8fcjSlVaLcJ_tuv^U3EYaH9can&8qv|K1CxGrzFoi}vXs5qT$g6%Q)!Q9JG1+##%0i86kzla&dVjg zvEaH8|*o-j&ISBMK};* z!wrsE0KtoUu5;Z@ab(p9+8=l%eK{Oi?DTkFztKH{rshZ1Jq3oKy_gU4a<#!XUzLHP z9<$E32DdZ%US6Y+ICPdtb;{8jsCB^*oL(9mPoBae~w@xFxm@_Ga=_A*qd; z7Xsw9cq*pX&oTj(4e~X?MLeefM-P^o_J1IZ(R#}PjKtG2p3F9PvQ+xH!9z&+V~YLFqm9z_X1SMig&;(|uCUr_B1 zH!C(gT6#iiaW`)1I?R3y5w(D4JV<=WebqsmM6-L?N{`JpA9SRPv1|pXhV}s zR5&uCA8svtyoY)X?nV$91t8879mu<+iGa-lmO@&$lfdYL?oW`a0n)3sd zMZX5(x&d860AzJ_?1dqI#@wYPh9n8g`)J5kk4|nf6P*1v_9aSvk0mc#9Wp;gJnKJ_ z5Z19$u!IGNjq!Fu&yz?}L;cMq6LlZ=BK=6{fACONko=T!~bSrbUxLRZ;AiEFgHIzC|1vY$!z0Wd-U8k->*Ke?X~A^4#`- z2~9u$vs>Y>T+u@G%nQ?DxI|b>yn|55`*(S|r$fj^o2EvnpC+KZxn3%)< z#>H{=^DYk?V;=dUIl}x;L|R>r-3R>51;JUbEL7bEyM>g`51~mt1Rdlk!rIp?P&kw#mR6c(@yi6cw;5%5aD?3!6BV>0 z37V0&2_^Ce<Y|Q)7N27r#xEL4EiVa;vuNeG?HaxGge!!w1%XnXa*=jn5^s3+I2-B>(^)<<8_<^ zW*(bK+#M-T#l5DZu7{7!I#O4>>N_VY3+PqEn&7RA1~9AiL4YW+Ng;~ozadJH z4O`*=(|IkyS&i`!OCO&%>|d*3=FaXXodA7u@j^GSNMt2f5ENXl_yBE|a}RY}EtIX3 zykGzbvwfu69fAvPYftyHCaa;~eli$EG~@+gUUsiICVJ2yS;8Y>HQ);RhC+BmB(l=qx^4J?0B85EcC$0o06uJT~5j=32sNi-rtU zxpqCv#L18b*{<_5r6*xZ&2H zXh~n5%VEz_=)9W*lV_ zXifEmaeYs#LjYFzd2_YY5dx6BkVE6gh@u+khOIX&qF14WWBPA+qL|&?UW005x0Z4v0b@&gJ0@XDp zltDWsFBJGd5TNjyTny5Nsjj3epa@Ks=oWA3C)p7XIgkty3J1%($ zp@{RSi$N?aBc)YZGxbyqEKsHS4&__4(1z<&11w*bTOMG#hPnwth@Zls&38y`k_kU| z`F9m5CUKCLX4Mfcbn_{FRB5ZaZxWP|bROB@L+dt66w}4%fHfLyJ`3PxCc|gg|4f%! z`E^h0!?ob(T7hnX)5>>U8uU1R`xhzqt?SeQ1hu9yKvT23(e*-K^&j5GHd+7+@%YVv zR0{RmU)zVD3c4`qXON zjXnjbiUX`+ZXXx7nOReSp>OJh3rqO^Gz7O%?wL1{%jrM=Gln8)!|`$F zTKvYL%chJ;WYs&p@9h(h?ha9MiG4A#L$;&i8%MA2c@s#9W*_7|4dXDQBc5$8RE+L$ z7&phtHzBI=%!2ZU1e$6m$RHD3x?M;_3ULwJA;ciK2$YQ$jD;kji^B{zc|Z+_Kf9G` zipf8+QB}$BWGU(}f$%duA^Ep&v5?hPkXvODh0&Di;VDxz^7#T9J_sR4+bWi(#(i+N z*8ZdW8crQ<209ok^2Zna{?v=fPk;%n_X8kYgG)r9tbI&Dn&2g=m}crqu{Hh=!QaTY zvl!!?^fSqT-0BL|{16(Y_UR;A$3$|3cY(e%6VddgF0i42UB^Q+DK^7A*c@m-|2*r> zDCTH@XE?m5ZB-+B;H=iKR|ZTMYII~^3rEdll8@se{>DPdI6m?^Y=1^|Ki>N{SoY#@Y?#igQE(V!EQEfj8Z># zoc%fc0``Zk@DSj*McGr; z@L{nQs%R%+AvQ4k1utoBj5u31Ii(TwuttfY&V;bdNl{Ov8;xryWU-N4O8G9G08Q?% zAc((;*+Za8N{5YxikiYXzz8R!p8N7NIy8se#9uW-F2&M}H1cVCI|KFcOEPPYY6(i{ z*!hLHY51Q!;S;n%Zbsy&<<%oFTsdCxJaW+$y88^pC@d1C0f*`6L2j>F(&0QaTY3rR z*B2sg3)p=8d||)qR>&Q%Os_4aMq;!{5c8SiE7P2M+6pE9`+4r>fe)AyG|x-b#on3QvoJi3_WuvJI9Xcy3^VH{`KeqfZqV_w&Zij z8sJwY$JQGv)qwO-)_p8tW2W$_7(D!I?=`zo-26Sx!3j(q&HxNFm_h<=928rZ04YXe z-fiM`;}nt${-@YD>WF=xXmA!>rOAtHZ{ZE21|7fDY&>71W+~;&LfQ=0tcVtqDe!!=8=FgQM3?BdV zXusp|f=fbUoq92koxs0qtDkn&V|6wA`%|bcp;E1TX@E|)R$JoTt%Wgk$RFUL0jmV+ zCTA(>>zD^WjaKjR6TLSeNVn=)Rig#?=xV2zMpsLf|LT1Llz`#2Sjyp}tR_bPmMU+6 z0o^@xH|;=YsXaUbMKpWMjkC8aswS8CKi5it%HI4@FE+0v9~+Z;!x$R;NFB~h=X)ds z^o|3HhipdOIWrNiRY>!aa%H*AoZGtu#}Kq+h_QncYKXx!smvoE&s<BVi2%6A<#I*E8~{+W_G=NC-Wm%_Jf-FaE#`J64D4w^%vUcYFz&6N=DN$B`joKU`?pSJ zwsOQ0Q<12C7!Y`b;Q{I5|BVU8m0p{m2rjS>a{KXMZO|qi zE#J=zp+p6rYr!Dk=va-x-79}yi)gw!ya5=HNpBqMSG7ApH~<6A&_;C;{|P=>jCT^5 zntSus81|)eb2Dbm@qpA|md8D#)iH(GW2pN(g*eZa&Ef!#am)(5It>(pl%C~)ghDW! z!w%Uaq+FBt9v`n;Im9x#n}k*px8gAL zHE6+S+yr6Zuka3R+wP+FR`uW~*ctdPA-?&pee>=?Tyr7)hv{dS1y_LDn-`4iZEORLJU;6-iZy;~m2*B1a#f>;{Hwj#g3mRsfo8 zK+KnvMs-7FT_+a|AFpY6(YjW#M}UrvCuPjhV+fd;!u6Dib9DjBf9Uz62VS)}==Xao z{hS6n4cQbZ^^9v$4oT(s14L3j*vV{uH#I8cFfTjn6E*?^4(bxU6JywqhxUo*((Yh# z$JCPA5s75<6rHNF32zb00=W%q7moro(>tc_z8X!VT?UbS*n4^aIk^SzSv z!d?f72hWutCM|Bhs`O;B(4u}r=l40+H2;{L2f{;GfZ|bZ3FI+o4*l{(9~(?WLtV@h z$b-P|#Y1GVzeTYU@~{)q++kHM^)zXEcX$3UL-V&w?Tl#&N778cG?__UTH3*Y{j+;g zOY5F4qx2=z_vdQ{7hn;# zA0CGH0!ge7KZk7n(iv>a_9Lh%luUO{4KCa}yAbsw7cSeVE zpFdrR$C=}PIgE@HP5n8KjY1<+{uu^NIW9W;a84rZD$#F+-A_m z;SSQoUQ2%R{FHSeojq(z6N_p`?gQ*^oOf9lh*GODFf+zOhgp4)0$cc`HCj zYsBfFbsz|)>?ev{;8zUWu3{@34AHs0+cow^rMVl6Is;^@ra1f|KDOZN0`U1%9)wp# zm}Vc36t0t)(ROnkHP=_ngDjv@9@5DcAFC6i06To46unU0HO&@^(Pc#ScC?v89kpVs zJ}72Y8^U}0rwlPjcS}>VdrwqYO<6vTX?E$`9EH_UTqh z$HAGfJjRLi!lf=h($<|^iGryBL$WW98K>r{orlg544p=2DF690CO2cI5N4(yHNd78fF+@3SxATUOC z832JHkSo2dqsl@-$q960HO49Q#2(-ykKZ;m1kPJ?ZK&g$ zy{GuJD?i@v+9zwnGUyH>nBW^33clnH1M38+fZ2$&Y#ixHg>cL=-ZI6I;kY&Shj+RV z25XQ-%ABh92(#`Sx5n>7ja=p$>lzKPZ;bKQXXy zjcY`;MWOt=-PAIKBJ2Jwv+K3GE4A6FG||?{!0GBpL3Ck zNhZrvePbhUDG{3qOoV;dK=tZdhk6~yhfQ?)ANU}S%PuMjH4Y4BW#YOhc5UTEr5rAa z<%0GG9>BVwZ?suIqwsCJzufkO2^lx?_dirRm`;(qk0#TXF1>x@UU@!3rR6f-V#6f- zn&~%&3l$TR6JuiFaZwrV|C|{C3Mw75x7wqhs?YbwrEMW z-W|cRfK+ATiT6}0XwCLWwQpIqBTz&vna&JoGLdLCE$pMJK+sz>vi0lbg=XGM(32kPX!x5P7vxC+>jK?qdYnV_~ZOhYwvsg;z1ntLHj z!CGX9-l(pnnKb)T3Sm*AdEJxG)Za5vk2y)aPaGu+kjbB@a=aA$Z8Bd$L|h&Wji9J= zSqoWl2JEGLlqo&dR~Iw3?`%IN8d4QRrW#_bf7f8L%aNaT#-BV@{+XMfRVELKi}`_3 z`W08@DJ^M)a*f?m+sW2XerklQ%_vtLZg?-}pj_psZ)icUzL+Phu)+b1mV)5|dC7rIKL#UF zJ6&rWz%g2Ho`nB%9^~tP9i!W|V@xq$1f(tIyaYxKs*X&^2S$zw$KbF^>A;%_D3TJ6cDI=6X? zX%vue{MoQV;}i6$tv#Yixv_bC^}#OM)q7#FX=J%NxZ5hsI5Ky!5cj}d5{^vOTV>3X z#2M%N%;`pgBw?Ha4G$=$+jPIX>p`MkI998;gYe+!Y_rpK7j4cHsY34y5$})GUcG-k z86B>JI)8TH-f^wY4}MP4iS$LuhTs z;P`lix!~St9+uU!XZh1_w z!WYV3-tIa9h~3wOhlhh`<;pq(cQbAvda_1?C#s&`GU?$h4#tFr1VNq!jpcfUBRW|n z2QnBt_CtCjI&a)qR>8HK;~|1c)IF6V7SE=QGl{n4ZR_f??df{71|*Qz8H>0cYkJr5 zfmPANrnX6GB0Tug2}ErnOWB3$;n*@t-T8KW7`{x`I9)q*mxC}lKX>T^-h(VVM%tWn z2h~JUMR#k(PxYA-u(xqxExef8%H-V=?Rd?2K`1at1Yd#Tg3+;}>BuZ>Kw5>k+ev;j z?I(?-vr`P!V{=1bt*LdvoCt2(TVaXMuLPY2!^LZ3m`Remk!c~w6?VEnvN)SCOeu?r z3l!l_@un3&B#^bTsm|j57RLl#F#yG}W7KcfwX%Yz)Zy6e&32fQubBlXs$?(>r6xh?1|mwP~y6aHp!SO zhq%zVw-x`?F(B`^}Qx9l^_1ez-949azNBQbxPm z7P`Ij6F&q41~eNxU;*;C1(=MJcJ7ZUxEmK9906X6hJb(3_@@4ov0oo}vL34rH^SB^ zZ1CtmYqPUs0n)I9h;*xHNPwmYWmP{~*YWT^w-Yh|QUvT=P(1zB4d%R9 z56*bfk!9H3KS!jN*RQHzXWrnf>TYUPlx6o*LZsu5ILJO@@J>W(m$ zkx3Nj>ZTZcdsuw7Pka7*oMys7%uQ~W&TYpB7Uez;7#%5k^!=zb+l^sTJJrifl)omc zaCI4Kc=7a;&3EcJjt(7y&&6*xA@j@Qtb@K;pNXh>NAKp-%xaVr3xbRFHI2 z?Q1j&-qj3Tu#5J)bb30_=PvFB4a*-SFto*1;l1!U?y0fA7`50g5QZ&EWt!34uK?b!jHQOztBh?zx&9z_~<5CgP_qS zks2{Or)N@`5o%5%FC&k#r&}Mtk7@|i*q%Nli|h+h{Vwf9z~F^}o7*SR*&tdCm%#bS=*lA-r1;i4x{_Zt5o$QTIzVEqJr8|c1?g0r2X&6Efq=#-Kr5mKXBo$CXU;ydv7Lb%iy1S7M zLAXDBo^$?Z-F4sGH>|}QSc_rr@2B?OJnPeU^a!dpc%~&O!{Z&ETp7;$13)}cqjDET zuitB&zoGNUqvrh=KRYbty=DI9&u75Iuu2E88}CcQN3YX8xu*@vFH46u_kNjm?ig#4 zb|{sk2yPeR2o`VP?K*ckTQ606f8HrIXd_k;y^ePG9l6jRFMXAZaW4N8ZNc+U1_M>I zOTf5vhl#oMN;%3U!%fO6WA1K~d{2)k7IF%hl@>dz#rnqI~;mW+4F3RQK%=&`7rOWbKLcM&~vjDLOE+HN)(SAXsA${gdm7Z&2QaH*X_ z@fhDBurb0DukXB^`5OV4De7Dv{rpI_oFi6IUm(`;)MXq7g@9+j0A9E4$bJ;Np1WCS`b^$O|(@z^hHuajS+M zLEa)zV9B2%1oy3+_vg!o(Y_aSKIE6dDAHz6qk-ti+d8yH|~Fva$LG>AC-LLiFhIg}D_5;fC(2 z(%nFaq!9^w;HS#E^7CnTN;vKXY=wl)nRC4TpJORO%I%~biX$yE7qV?4Qu=>iJ5t_z z6109sHMu6DivrO=WD_|$8H988lv(xg51TR5mXX3@(_x*mVy9ibvR(%fqpQSXrIgVf zhhQH0(jvh^{0igPCrI~Mhtoe=DxTeYFoB?O}tN! z#nxNn05dt4U*xggggC|D>*J?@TZ$Y3C2HRG0#Dzk{*-Y5$_<^UBn<_?KE;$072<5F z<9;e?{+xRCtUjl{Fx$&t^+%+>Jxa;tDDVg>;%;2;u0@L`cC@xb7h8QVD7_Nh2lGzj z%}gFA**_q~>mwr{lk(#H=rDYfDRh&;%X50lgIB<}Uu9M4HTMFq4_Btu`MXp7tQaNI zWhYAvkHb16`*4`+*Ienl<|giBaFfrdk>CdnUB9MWg@JOyAKp<^E!Sov!A$wTUHUxA zoTv8+XvLo8Py7+R361W}_CnE|cD$itaA=9wj3Az0@t)+@nz}k%h_4BcGx;@2GfOin zc4|`R7Tr7WRC4Z%IM$@GS2vcIdhaPO3#VSQlZ?o2y!^@lb&BLO;+q%Iv)OMB(m=t2 zKuqy|tOvV}c7^suL*kuo@mDX;D*`H|v7b+zF2TIFx|lSqS|et@j#ek>wH&^|M0&SY zxA*DvA!52pV%nEX;MxAK;^OK{zP+L%P|wMMwEwL=L3!)#7jha~iHCbd(YrIKVnOtL z?lqg3(}BBow*Cw!(N*sCN(PkeQ$4J8W6#UaUp%pYNuT)z}kU(RJfMAL+eJ(8R0hcutANF~=l!$K2qfGmzpJP+AsAxsxid zS~7L+2aIQHG(zZGOh})*Dh!N>yyrAbUkg@jhPlhp80mOnEtwxIyhP6Jc%M-d}U;}*beWhLLL?JjuS8PeHu-O_Zv?WkW z1~%nIQ4O?}+@`6p2sK0ewLS44|DwXBY_h@oJ!C z7Tq1f!rk^h{ZH|h!)en^?@+zfeSOZ=|9{U0r7IcfdnYSYDprn+Dkp~ zi0B`4P_6ou>NqLkX!c-%F-NzwO4^9x1`SzQP6&$L^U}L5Z3A8xLZ5&;YzC*hSms)M z6*{%P)cv>xpw-+Od95&PRBaw4bC@EiXgzz#9(pZxMSpnu?}CFO=dG^+Ppw$dr!bV9E6IoxO(v>VC(u)uNO3lu4Z<@dK_BIX{Qm zwCZXUfH)RD)^ArtroG}x?CVQY3oQ5#Ty)nAk}(Vb{a9J6vi1S`pVZd7PjXLSQBjGj z5)@Hiq*g~Fb|WydKivRL%Y7N!wK?#?mV*m{-&|0#F^u-DQxwW^`!+aZ>C(E<*JrJk zCUkcewKEaBAD@v4t~dcP8eWaDpJBC7Dmxk#Pz2E+jMSt_qsCeyZv13P<4CgJ13Fh* zqdHY~T%z6>d`e-wY}jGQ;-n`znl0uA&cg%N?s*hfo6_8Q2D41S)yC#LAZ!FjF#iXV z@VN$~pC8#nezUl1m8|Diu~Z=nJXk=IPa^jQC5RpChE{^;rSr!zDD4g&0@W-H0hBFV z6Yayek~}b+qgN^&iUKbZjzPLv4Ey|&HzC`H6%T#P!tVEPT)XrOlKH|t>#_pcx&-fU zm91?_I9Z3tgjBKQdGu3iq4tmV!7~TEIkc}rOWC=xkek;+N1eE>dzks879MiguhS#Gk}or zlJ));MkHRYjC4?fWIdPYE%yk8>TfcOMzgSBu;+?PS<|?IDk}#^*HvZ<1arOy$0}9O zqfbej*D85cbwx6HsFVZkx!5>6isqmXRqin!$=r7rBbcZv6K(J3HkIC2ENUt7f}G?vqb_oDVu6^WRH{sVFmH8O99Lr8f`yHzRO%Zi zmxgkFuzj8~+g~8qLtEvIDc(mg+V*y<_99o;dJ3jHo6+&$fqhKLTmHQY^|%pz#!#0W zlrJxy6q2(WbMdcT1YPKdZ!CRTpYJC0hCnu7<&O_kUDM40bZ+P`Sj! z>Y+kxBfHK7vpYz2%08zyz|Qf^yD?I?ESH+X(NPwBZ{N}*|L*ghf0ct9fXMb;>pbAZ z%VGiw3o&@6Ta>;jib)qhHoKGvNYX*2{bPRAwT_E$Dk~EB&%R6L4SY`6{8L%5EnPz- zNyy*tuZj#qHP@xXyMQEw7b3tYK&{+v6o`EN7CoSTQ<>mn#o!!)@5@x*cCzfgJ5pfM z6?(|OlqNN!#)5?P9YFdR(oIi#md0~pC=@0*<;#6{(@+K~dHV>v6M(>mq!UIb2@EO7 zgb1a0cGh@uE6ju}Sj%n9Jidm5q@D&VM@TAn~QERP(hXen$yL zQxT{XHYtG}u6p=YDgi?u&Mfk42s&Bm&Ec5ubE5z|;rYi!QaM2ihWs%}hN*Bj!ys5m z=$Isd=t(w6;8kcg<;(ULs7-owNU}l7IAtg%1Rrvw!gE@(F>&Oj-cTkbB?P#Lv^MR? zaojp0Vjyubb_Wh;9}f71exINN3dW&>=!vEA0bqNk-b z0Kh`k5f49n@%QTQP@|TmFu*wKIqRuO@bRKCPy1N>jZL6(N}l5f_>)JgT}XnG&sn8| zt3Y%sAB`3bBC6)wnknFyrSG4&+A0L0SF~x+v*}^IjtRD<|L8v2{RQ!+SV||NyzSoXefvR5;ZH6#5_~z?_oPhk zrk-7=Sg%Od`zebcS9F{}W%7Z8S^Z&Qi#mfCva$}Z(}hqct%mDhV?rzb;<(BM-M>rQ zgTPHq-iNwhyj65ttBiJQlA2Er8DYvqglsA2_=$mFC|@w^=#i(MMTuD~e-HuU8YrIYItN9DvPr@5w9(Gp;Tse2>hTt-#S{3=9r}@5NsagQk)9mnuq-rx=r~mB{E=Ssn z+>hxB7W;-I>(e_s1c^gReFrx>B)_RE5dWGHs~9pWK{8Y;Z*E0I-|dqW0GL3h&;a*5 za+5j59MFHAcewklzoRlzcw{VVaPHq+}g=DOQDNx`!nyrN+L51+B*-Q*OmBH>M9_tHRcTP9iXoY0lZm8ztC|UdA z;gpm!&k%PET^RN1N5hiR&~;Bg6-KVX{OImyBXJ-Te+cT3@<4?!ZXM(Vc)BS!-JAe! zDyU45fo6YHSZO!82q4#i;|obBmh4{=<)~NA6H4t89Lpa@y6#%aeVAR$HG>H;iabieW&FhhZMc zP5TW~b0{L(myq>#Oe_qv7LG+z_|k|})ieUk0+~^#CFOI@djDDujM3cCcis^YQs*tQ z#>_&2n7|jTkAza6;EdT;th^cro>Hs0cNHqccMAbs&@#V^mV2=!-%Br^gTExOTJ+y% zEc^N2N6cbhC%Mtxp1v4N{xiar{$D9L1WbgF>t1?la~w=uPbGZ8O^B~FV>|69N@n$&hNZAGk@<+TMyH&U!@{6W-Bsl(%7O~}TBx@JUBWZr| z_69HCtrC=YZ8Z%e%>?lg^;GYuyluwb;BWhV?@JG0M6V;2R)hZ-Dm1`HdPDseBeMN3 zMg*kpXpsmy3Xm@Xxxy<__YHo*G7V|Lx`4IXqcF44h-}h?L*+;_JYbfga~qkF2CFlW zbs=XD;hc;PM}~o0nvfP#xzLCN!zJTp@6J%C#ghH%h$s6Ux>;gQ5yD|KqIec+`0mS! z$#}`fns}LW5ApsHKalLUmSP0SHa`2_YCl zA3q(lZ3pAh@a25-tr)<92Cao55$WY%QuvETHe}r*{E591oL`2EJ$L7uq)gzSPzhGJ zBoYNB;6hjf_1&armOy+BG~78!ktc%!g-#H;=h+L{dp7;A6<+>OL^119F<#GADp}QV zlboS_J9L8rM)WSUoLU$M;NbY3`c}3=RlU2ZcBOnT%*$^T)MhmDy!6@8f3g4?|GEu; zJ$n!S$BVB0e|ynSc3)CKLb}E-D%yI$sjt!%+f}*-$mxVO2)vS0$ogY3B9{Eg4X=Km}|BaP_U(^xLFb9;MWjy0dj^cY9snou>Cq z!ukU!X(!7sZ1%)hy!D;xBgW)Z;j>n8nq6VE6`W4~Tmd}Vq!y<|R0K_PK06maVGmJW zQ_1A5A75g9EEw2mQ8nq+qWYq&kO%c?GZIsxc~vCHkjkL^G6=kzW#WNrh3AJ4Da6Pbwn<0xETgkzIIO^G$(H3c3XSU5ZOj@Mg% zbx>&=x#V271COOq1<9=gO^Jjjlx=PcC|NZ!)i8xu4w8s>S5gHG^IBe+Nd6n45B}&6 z8SUNcmrdiZtc?1Jw;LShD2+z~8f5&qnmiZ<{a6?hE@2Ec+Rn&;fmRDC`1|LdD|MYZfYxyH_jd|uSIk2C$>8RbBIg8%37Xq52 zj%bA!2li&iWe1hu;~7OavDMjkXTkIL8j4hy%Ga~XR6GHeJhk{XZEx|RMJMec+rq&& zNhYcF*1^Mx){qrEzAw{1#WWbbwO%L#vI*Y6qFZZR{FJ??zxcmT=1=?oDkm3k{~uLN zmY|83N#5}NJI^)kTmx#+Ada9NYR>FAoNPqiES-WwWD-d*!0HC?C7{DN5jAv+4na*R zUZUcJ+qSbUA|wHSswmPQ2fI7}kVE6KtL06?`r$~r#}bSaV!sYEWi0=azc^k&8NfcH z6>*`Rk0F(W9U!I7(ne#*d@{j8h7i(5;=k@AAW+TeK>D&994nNMHe?e<&6=1SjevNe zhvG&6!(cv$(VrXaQZ|T3(i^lngC26m&R_;2a3SnF*S( zn@jRIll5CXyCk&k%(xz!KhZs|{t(=JMCDrkmot>NxMTg>LjJX3N1Sky|LDB<7f_GIdrw<(x8xL%wUC+k4D3*Zy zJj9GiAQ4uK3MdqYEzu*JKw(P(JioBvWaGbf=_6bReOdR1}oEseKpoU zXRU)T#%l2u1p=UdT;VTqp>zma--60!>px2-+XbXz7ld@0gtWKanJgmWW@l~{;&&T4 zz`;1)Twgt#ZNK#NipHROwHc~kjb$f+2alyHAelx3vVQ9^jHJy7_jUIgweCPx zd3j~q=w79`=b2Fiv>Zl);_wQ(&UgUwt$Hc?(w7}HfUw5vllA_C@~%OCG*H!i@=C?( z^HDD9nk22tpZKtAw*BlhU8hKBV32ouMQ=a!miXVGyGj^EN<0D!qfl|G1Z#CpHysR} z$8QnGTXtSq3~J7WeXp&&-5Blou0rA8U4JBUdYw|HHDBxWkr;|g^Bm33=Neq6aqYQM z(M@9P*S*ftWYG9{Me)OsvCaKXxmC>P3-a)?`*~fy0b;R!02tFIGd;C$P`0J^g9m2E zJ^4z-9ofN)`w{V99==wK{u^i)!bSRZ&CA~16@39B9#wqSki*snmfW3hF{Os#eidniJmN?%Mj%E&O@$( zx=-7y49L)k7!j{OJnTq(;(gT6Vo{o@avGR<_@&5#a&cq`E6?IEvTUPqS%$k3e#Vdd z=D2<8cSd`Ggi&K?o8v5Ph~Ew%KqJ^rm-h|wOFW-JM+kU{MH7wJ9Zt_+{=Vq=+7{r+ znp6!x4&K+AcER%5PxjU=W~=R6wr(IB*qsE@DA?bDHJkdD_=yOtiM^O3k*jfYG`_%y zX|WIOn3zn~q;jW)^k~H79;;^ki9_B1k0`}iuSK3}1MZ)@fC$^Q+ zLu2o*x68adkL9KTj@>%9pX!!%zIpzk>T0S~rIENTH7iJ=C@1eN|F8-=7RI0)a;0fG))V~_MQop=~Z3>pY8W= z{ssDd{|d#_2YIHQR55`D3Ivoa z7w}6X0aEp4tvtc&u027d0W~!+fuca)h$>#id`Z1 zt-XzUb0?`EV-Y#D`S23zRs!8yR60^4EgCgcK8SfU zAcOdK5I%8t0f4)&_zx=n-=Ds@_#f^r_P^XcUqCV$Gh946Bg0o>@j9a#gBX{3KLz<35hnk}q) z&XBY9^4RXRkB_j+`FY6iH{7M`D%zmLFXO~zde?!&+u#{ZX00O)HF2b${n0ClvuS*|n80wE^vFF9U zM9gbsD;D4g7ChP{DJFOQh>3|cc z%swiYHxA>i_$#MB1wz7&9g{8gF;#MY*%W+a)!%g|^p&W8Gee!>x>vWg@41yPEqQDc z1i~m@^zV*zsZa!8w`hEki?~`d%#)(J`Qmp7Z{SsqUZKe!8e-G6)#29T*|@V-FgO%G z@6hj!c-l_$EjRKYHGr7dxA?7YiI35fGbX?B#(m*)*66=B%BA9yByuqS8vu7dD-Jj< z>tX>p1fv_0Ecsy!0SSE|IIblETRMx=K9Q3qSZNNs<$#+}A6_Qv8zEj&o)oZ;dxB0L z!Ht1z?laD7KYOaP>Yy%P^yrT`@evnHv@)S+WX-0M5vVAg%u;j{f7fp^d9N4L)Di1=y0i(b(Ar;Ak`d{i0<^+%A z3vTZqeT@tzKxwM^_83o#9~CXP8cw5X8bh^Px0ftMh}mr>9A}j^pMJ=pH_(qeLv&H6X#jpihVuodV63B`(K(D=OGG~2t zgF}CH@Om~YU3>#p$2dZa9^@fJ>c#FcUjYZ-SMUZsdr3AR#1P z%j4y$jnnwN{LVsZ_5xr+@3MS~kb5$wcKBL_*PPiMl%^yI0i5jekK-`b0JAoY?>GUr zj-UJjE90dAl89?0x*pH!Vj&h$cT(+vwu-8T(I#Ju z8x6rHgAL)wg^F}PgbOwpJM!66E<4i#k68r!Hd~Vbg1qTq$cgELjb4 z0gqd9`5DrKh?<3dqwRUVs#|G4IKgXfgu>g}c_LISI$I^kHFK9_mZm{T>u-MTDtrAr14Y(Y0#>A0M-Bt&Ds8|m{!-qG^ExCzwsG6`GunKPq z1{4LF<1#+d|Bout^=qF}?@IpId)*?)sn)GxC|FxB@Ah_L%j=wTVK#-^ z_$R2ZbhJS98tHfD@{f1M8kby{(44URf-j4iLd$*~$0YR|_a`|!HA}}O_=QTpvDz11 z-U>D8YY%0ZamG!#BU)j%O}u5-<8F5vxp1i|;wY?kxzt+}DE8{)VrsV>@Xag8*Sq~G zr>Fad*pFq2(^av?i>?z+L%`4tMsgtMp|oG9?K&2_lw`yx=xAO;17oTFaJ?c~OrOO64Y(CoIs zR)O($#CCM!R)LIvpM1uJ&cUNGVit3c#d93uEXC%Gm_&!cY_TH`zZ76=^j;apo6` zm)VyOeHG$Clcrqo9iUmMKt)=ll@Fj$X<9{VR$ORK_x*WP%s^EBy^e`;WeeG+zTCzy^eJSi}bZA2Zy>EZ2dXb8VULV`IM-Lrez~aSiD!6?hC=c z`W9m`Lh_$X8m`My+XF-ZVsnX=#3esPgNmO6IoNO(H;90pb?V`_7~I8R=9k0>JmOWp}4Zo&-p++AHHO*_31rheD}|wXN9c6%*A7~Uvp3hl|s$r zPtZ}^=E7b>BDjX5Hsn?ijzZt8x(g;2uzq^%krF-K)S1lesSZCC`5wCD$9KjB4?v)D zO^Y`!03524(I3MvFJmB!0bY<_gGPv={}CIVkl$+j?Q!*%fGGr?f5-Ipcj98p%QpZt zf)Kgvn0zg}WQI~fKcDO}2*T{yZ+wgQ-r`%)kwWJxzepwz5EtSfz@Dpa)w=RHom?^( z!fZeT@Pe$BO&o9o<%}rBt%B~)UFe;mrrCoEVGe1JT^4N~Ys2`5a3&PYmT{$5+w7qFeG`i$J z&Hr=P5xUL`1WT`%wuFjXx$Ol!HKKqzW#A`jFfhd5 z&4n=o9cq`CYb`fS@zcDq?+MO)Jty^x1jhkM52T`d$KUMvlnnh* zU2EK#v@S~So9gC5fEVhd<2~T!Y~4@6zb1EGc}LXeDLxsWAPUn8j z&_!8Rko?`q=FaQ^YL+ifmftCn4;5C_#9a+nQ#gc~P>MT)x~Sa0;7Ohj5QqLi4E1TAuyl(HeqL+h;&*jT zp#4~@B4{#ZtQ-k0iq}f0i?t-$Ngy@4z-!|n^&sm0G+|&MPz$3if!hFV=XcMu1$t1= z+xv{uMOlYfSQ$SzRptpKjFswQr15#eWDgk4`S4feUAkpC!JO|@E2QxKDii0>`^jX3 zO<0F6Lo(FS$nlt@Abcn(a58nQ2{NcqnxNc=Z zODUVG)E}0ilKl-dP!);yx?J9H$)(F*Gif zTaLdQo1Fw*(X+@1W>X*!y^>{;Ba;qPVwfSJcch#xA)yx;j(dZW5$7?3-H8(cXZR|7 zO}_VQnZDD}C4;Jt8)>j4oa!lc5ipaqewF?n2vI3%kPr*XJSU#IL|t+AsX7gI{!i?e z_Wj>+q@&(aAV4@i&8{{Xzq!m$e$EP@FN=5}6TtEFX&4O}r%Vkf6z*jhh1I_#nWsx1 zi55H#w__}FC&j#r@xWvG8mJkQk9_@fEfJ_$)B|EFhFKLQ`@z~1iNu7Q8Rx`L9xV(o z8_#1Z*p}<==lZC>h3(*=Ex6R|Jo{Gj!Kfk5ug*k3kI(jPTg9Q&dUSJNko4zWksBBG z`S$|^<28Lz(?0tIA)GDnk4LjT;+Q+sNye(c(MWQ$OzC6hH^-btlKLMmagjBxbB+eX z9@*>I{beu9cfEFDt5e3lk?$|~4r<4Qmwbn}8h54-244#R1d8=>b)#12&xURVFH6Si zHwY16M0};`(O(1>c&;WvtWv;QusCTTAdLH@eea(?nQRSnrOn6zS)n0>h}e5kb(^hB~ZHLeku3x$hp@pa~YiR z1G$qbXchxnYIf18)AGqdnNP@rRYvp-GqK1=r1d{--t6Hf&Ij2CAEjT^zc+fkZ-k;}a;kvz0Z%cK(|w;Reu@s`xmr zrBKI?ZbrP^yEd}@Dae76Zol)Va%gfab)Wy!uo4N#(pq4u<}3!C7=vqsAOKj@y*`Ws z>~~U;FfZkQIetRn|1hMecgQ{7O;1DDU?5^JmVj^={+@?{vy<$IH{t8i%Vu_xVqVm= zNkqsgk7^GmDYrR|tHd}m7cIJ~Cc3Pw^5)i{pqr|!(HGO^oY_OCM#lat`dNb}`B&$i ziFpt$Uo6@nx=sg`AVVHn(lBnUNv1I30^SqppGu!(Tp=kFYnQVL=@o0EO8S*PGp2-u zq^fr2y6mu5H7~6LWEAsWIMFdpp%;W?W6`)3CRj*L77cx5!{d35<)Sq%i&nk4qY>eFJ23qVTWjb$fn=~ro5)~ zLlnGlT6WROUtJ))#)7ZB9 zAt!qUzZ}tR&}9qkk^X=Ml3MtrnExhK}7CV`sCr+mJQNKG~UH4BVc5pz>Lf z=X0FTgFXP-3n}#0I;M;|<3Cw|Qy1VIRNKotRFzUoLQ)rH3Cg7^!=mr_?v(p}%iQ&i zB8%%6B@Rt9!Q=wIEocI==132;YngE&*(uPO`?FE09+HNs)39@bvMe>D|*Ui3{9EbXF^m~OOi|G zd|WuZtwwwgbesYhwFz8apoapAa_6e6LWosbdG1bN4zaoGKZ$3CO3z35453Z%!M)0| z52BpA_2}}XyNo+pDBGx5kbl9oR3YX-8q;}qbB`DeEwUKr~W}FioI{mox8pnA_ETX|A{KKLaC6c_qf#1}fqb zWPrJ7O$6`*e%|dV88AQ)re&7A$^Wfm)dnK@pO>iaz>XYL&HXEPE!(=7Cxx^RIBXP! zgyw|qA?h=8DJZTam)4AFQ%>oX*)*0RE~!R`AxsIwkq0C744Yu739M?i^$$#~T@I-7 zlaV(ibI-~I+kG%?h*7l@CPaXmBgi44e2Rn#y<)-?CR7V8A6I^bf%GbZ)hCC{fm9D; z@z&;MBlY!tOHW9dJ~pNBCrqW}oCBn0MmnZvWYyRS=0L6|uKtL3EI%R~hblc#K>34t zNYgiY7#tN+Q9sBHK})6*t@AwwTMp(B858OD7;9E4kzFj-qN`*@eGwL#e=O!+e=I(K7? z`e(CP_J(w6&AgHiY+#v$0?ZtdO=0PZ8lqKIUQ}grZ{)lUJq8g&nH<({%mtE?&s&XX zsC}*Z+Ha59Nnv})-O4-J3T1v=vPnEZQb-d3O^jmUeQW=OU;hs0FasY!6fEs&xzm4n zK^=d57ZBjPB7Jh#?EhM9yPKUrx^!kR7)lR3Un`9N$ESDrAN+2R5$(TX~{+~3! zVSpB~Pf<7l3~BB9UL-Qr&U>!XYLy$=`gx3e_B)xF>=Dt7z@Wf*x9EFmQ8^dmxqus& zm7b0pO@pQ)=1ap#?HhO54$h8V*y#M)A@F7;NP0G4l{Q+GnqBOj*4cK;W6g2|qNbE5 zPk~{h&5L8nM*E++Db#C}+He1sH!!jmpI+Wx8m%pTGudwN@a&lCuGYP>)bO(4FD4e6 zjJ{s3O&u$0`ZZK3&|MiXF}g$Gqgzz$QDKmT$2%j}{NefH)q>+psEcR56nOEkWXIUy)8 zT=K?ruTNg-6;6pZ_x;3Q9pbkl8j22jGvJ_z*84Mq!VLph{2$L9udb?NP&NRI`MaXV zO0Ry+PSvROdjswk(pIz)!TD?jZYY7?8m_o*s53!KrWJitc*tb7t<&Ae((J{xCCK;e z5-@i>WWoIrFjr!qK6SyatY> z)7Cd9FS-Hoejp*U8CKj#ryz{vB7%oL11BoZ6}?Dp{oQ^S#SLP9AHl&kbR?sOrh`YT zNRI@GU`0RVMX4brcU!`F<z~|`A_l?r4j!faec`TivEVu6)Vo(@s@eeYx`~b$gF0diVBYi-eiiPdWuT-2 z@yRzM4S?1N_!~d8&NliX9)ID(N4?}I^eATU)I^Xmi=maQ*&iZ*?f~SpqtnAfz|IRw z-ktvr2L`gMTK(-n)-UA$cjDqFuoNV6>drpH%iezE&6p+A=g*I(zmDCVNG#Gt^giO5 z>p~tU?^Ztp8w>jC%T~|FedQ0`d3VeQIa!8>RNH*Txdh4|;bSDy5T-IWR;n24pAo@n zqGSY6sZWqg&eW=7GU|w+AtZfBI+-|dT<@7NmR43)j`AKHQ^yPry+D!6c3&{6TreDS zZyO3DsVZ|`8i)mU6zFRaL%mO9uW#08n_bC%7NwczeO*r};o{UR_^0(fcNh{Q75HxO zi42XpBJ~JKxy2t<%DY9QLwv*U^_K^S5?Fzm`!H&#{J z6^f|I${9p~x>XM%PX(}a2sn?d6I>E7 z9|>eI&Uu=eN#yuPe@zr2j?g&0Y${?L6uh`>uyq0?!zpz}H#{Fk%tLA3(gDDUp){6{ zS5?g~-sq}pkO-s+Sng*$_EW%Z725m!e8Cf#U?JtZe|K=hJ}Rz-AR3J%X}a-azm8OB zUyVI88FL5boSRhcdEkFbH1nR)qR0Dj_?6&ke=Zru@GMcVNe;kp; z*}Fl&tefTcXTWEuE@qXO{E-z&666B)_CHI{=WE((uev$Db3b%N02>Fa6+QoP{&26t z^U%XKeVWyw8yS)H4qma{Xh57o{K8jM{C7G24zS1pKEVR8b97HIga#98UN?dU+-q$| z#j#p%-X2`2j{rv3idd_E%517c<$*))f683%7jT}{2h|HD#F{)eTY2e9>6Ap?^o z`AM#-J$4eZAZNP|TBHFR7S^T5FQYeT`K)kOe`ceJ&qh60jb5$urd1$+L;ZRT>Ex}^ zm@>E-GD44(2r8tPqI*H07RZR6K!glq4D!{o7DLnxBu8iyfa5_gHkGWUF~b3;wb1by z3#UB$gTBJS7TWL)-jKkg&}jC8P|FBHoJ}t5rtli1wtFg7EtHUw7Q_&L9BNrcAx&^z z^=pioa)BR_#mx~*TY=Ng1<%|t!fqB`GsyHq4&DHWP-g|*@J^DGvGl@UUUtSbQW#4* zA_p^&Q*sh(Hw`UIJ6L7LMDbPlQZg2{M`Sobnp_ZzN}!(1NhtPRO-&V-CyBv;xZtKi zUQ#^_z9k{Za#^5XubBq5;|5phj6!nE4n#E|xI196M_o;nt@|t2X`KkMYIqOwRU_D1 zCEQ^)d-i$u0cT`1BCln8?0^O{B*{(6{xvFTu|7XZGQd;bt@r?w$kQo+(Q{xWYVofk z^t6-#*dzS3(ad4=zx&MQ)8vYtsWOQaWZx7x`~I?0bv2AWGKNT^CL}j4A!#l~{7}mB zfVa#Zd=n3QvoJl>R#)#|qM1>FUJM-9L5UIf#V$@Ko}MsJ_Ks8>9-z<(f8{E>q(T*1 z6JZeX@h8ey7ck+$Iv>%KeAt!81~izW4~bOhg-PQ$WeZ<0(uXZZ&VQ!1>$?s^Jrod5 z{&-gO&=(Z$&ey>&GA>1_HC?2f1x`3mznV$O5SC>q~0A>XSW?c~7*4%IN?yos)#l4!uOrsmbl zh0z_DT6a>69YBc2Q8cAG(+4BvVkBlbZqcIE=h^5Q`FCD=##NoA-A$J~ zjrm)oJo7rlMJ=Wj{TL=55|i2A8{0$yoDu5E9c>MbCr+EW5^FGuNUvz3%#p+qDgfoZ zoA}%Vcl4imHB0Y@dn-)N_OG*e+nF<8`lxbgYT)Jv2>RBBc$J?yKRBj8^qx>ecaA

4Ux(<>%5+Fl!gvT<_gA{_=UuF4;U)xiT*VpCi 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 z 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

2RhLIWgx?G9(BgoDjby{z9m@u&sgX7oO-c z8vuqm4gHHw%EvL7FmfF(jG$>x=loYZuS>jTFI+ZklB#BpD$0tU6;vJ`n~eb$!R$9U zP9guK`Tl@L%94Itzz>M+s`NC$gdI_KuEViZd%7KiVA#yq;R%%XSlw47l(owTk1tnU z8P1XFnuFeVx$DyvJooi$1O|w_SC{b_m?nX4WOwjG{PlLkKvV08@?PA(AAmmgjsg^$ zv!BWWpi%aJw9nt+f2pPWia+IYnOrtjDrWJ>*A?MB-YF=o%bEucEIqL(7eao6y=VI+~DmQxt< zEoy)n|Kt1nP=CLBD2}~zR${@B(pj%mmC6h0=81-ZrtXHxP+aw#mq-?FS9=B~IuLzK z5Eo5PE;+6K{Hf;k>NM09(0q!&H=9p>Ms2hU2#C!R!XYkl%*GSkuJJZQ~`uO-P)WeV2j3RYN49Qt2^m406r=;TmRqa z`*jqrCOcrA7rtwGz6I)9O?}1(emuD80fMqjcYrOQ?pCS-*C0fx+WM1cS1}XvaByVTyLWABbY1a(UxZ?2 z6%D`beIMq3xBz@$8^wWKUjp;C$R7B;?_c=HE`ND6{~3q-0Xis*Rf@ReC`IMXu1WqT z&x_k)!^>Cp@5~0T+4+>^vI3wHoHncCq`w*h)>8BBs)kCUSkmVb;|W!6_>k-Zk*oFP zkwf8_w6#~0^22b-kN?b<*X*eMz|c4F_r9I}ZIi;8P7IwGOA#6^#rCghR|O-I>;~oI zhaUvImjSO*pXGrhQZ2Q6>9NAa7v$yTil*|}W7^C5ot={&OFE&k_X@o zhexqte@@r%hkl5}Z~NFqq=GOqmF5+(UC`oI3SHO27tS)*PL^tyPgEo^uWkv3{5o9G z$&C-|D$VIGd`aCU@D^xDdP5LnNfR#kSx4IuU5e_VvVZ8GwF=&9Uiz50|5HFV@`a8< z^at24VdITAcRS)NuSQdD774sGqP~fx1{_%ULM%?wSgps^2|YVrrKOr5sDIs*{g6BR zE;4`&+^Z2BC_wgST?NR6*pu_z)AGw%?fapgVzSy?WEZEkcJFC_qP;F&dI83uknns; zduqQ4SCGxObaq*RgTPgJ0xfv!dGDX;#hCe~Q7)6sc;d@S+i~7s+*vhUtlM>$UM9T1 zrm9{nQg?}%ZuuCUzjibB*W0c|wcN!5Bh6obierdQ61pZ)9yd9o#rPup;HYw;Ghl!ia?xy2b%@xR1=!-gK42I z0fdycaFO0e7yEi0ZS<2Ce#rI*ITMo^mHt^L2Ewn*fforHsKUoQi`=$D{;l04fzsr_ zr46F>k6MuVj_EPauH-*MpzkG5OmemLPqN7Cc1|YK{Ma_W08)e%{sE7GtXCy^&+6P< z+cy`a!IV6k)u_y2uq@%(uXjzmO-&|f%eMOlU_f?zp);(1z9RdP_wW{AsQU97lxBAu zhvfrei!hV+QessrgAakxb{i?XTge-P2R=o@Q^kk7zaQAy**#8FyE9|n7X?TG4_tQ+ zv%*Ay94h@tDeK_d&6uQP*JE^oy%iR6X=*T1Dr?e2%igYNt_J!ETV-@0ZsoHB4VnUvOEhANI|} z*xwxj?A*SxlEg4(ZzR$;vt)JH@7l{2VNH+C<(mS)ch-aI$kJFS!BDycoz=|ptm$F( z@UqeNO}w)k-kyP{VmmfN!$7@=A7l+&7k)O~`&QhU#t6@4S(pn%Fb-nCj#<{5$8l%f z>x*94%VsB0MUssDK-&_NnsGP>z+ow#_(6q6*&9nURd~rdcWMOlQ)=zR1TN4u0pUEC z3XjMYUi01R~}M_%E1w70V%6*sW_?%;y`}F#hkikW^`1 zPA`H*s2?6!@dDtDw*Vm@k=TwnmcK;YWDG-a zY?=VF&=7fgNAKUvkdcQ_Vqk&YgN*&9z*qW=LKzRyFQ5G7lLaw!-Flx%`aR(bwIzY9 zUbx=CU#>lc!9AU#HJLAIT1!q6lY7}jQ?=fpb<$0NuG~^BnHgxXT0t5Tc4E9fb`xwi z^Q(I5c5l)K^j5fxaucnQ?SQB1*8WsfGfV!$2j;i3VAz%owC{n+8d285Am~#cd{?{a zrGB2_Y$@hjd?#g|9=`3~Im#I*Gs#7^*wR69`@CpRsg6#Nn;;k7*UjKbHcfwG*xmbB z^v+9S0Wb-UTHOTMs+vS*F2)zc?2LXHNIX;QRk(ivgbXxU<`Tl^RO}fNK+lC=dEyjY zuUZb*NWW<$9UCpw(z`5*eX&kPRPQS0dILG7r(#UyXB@0NFoHSZq)+JWFKEc}iT0On zoWUok_R)=nzWG8q*_(gk?*D~Oz}(;?_%8x=MV^9>Bko;Ummsg*MY8bw567JkRF=8U zg_u#lSvZ9oTtrN)QXXRl!ZRh4La6OO3t7=9jCPTtqky9<4(-*)2hk8w;`=)I!!kMS zN05v=0GX5!_QymF zplxzAcmo1(#8fdfOPGD75y7T@cs1AYQNSgWGrR$?f37HLxciB@^@4B+SNg9tt8#7fzY1B#1>nWSqy~c*q*-j7mMzv~DTPgM{p_ zsARKi_ko^UO4|qc1B-kimYC_CAzuVU@t$vvSO6hxUVbwBOh(z5*_E6YYFMwL+V?E) z{%bfc0z8WoO?fA1@!y;nLTEN|YF;?kOz!%NG*7gO!a_%L0zq|7u7`KdJ zoRRGKG6+(EN`F7cSz?V*wIev*2~c%QwK&h*Tq(@kO~p1hY}ZD~DDJjW^tTTA9zO({NE#90#a^{zEm zktjQf^hxPr?8D!L?#^6=jD-PZYN`@S5ah#LLIPbqs($?vjx)TSjfndF!zPVh6Tr~O zjr77zx)?N~*zi`Y>7wjHhijLQTPh(Qm?@Sd{hE7`b7ex(HJ>TQ?t;4)f?!x>XkODP zrakyF*gAjw+u@eibgPSI)TbAVt5_Q&keVCnFeLjdx`+9X$vYg;6h1V- zfk3{?B#Bd?Dt!BIER6Cx9Jc0Uv`T3uS_r{}+jt{t94{0Jr0X+^- z(Ni>1*JclEXQ6r?!~pR`dfUj6tX5amEV= z2XTHaxT8;7#4=uK_nj-PVRv+3?o~kE0hieJ>~+h%f8W^%z`~5qTnHQjfB7#?+pmD@ z>1)Jb#CP56&;MJZ8MY4U+1@0(Yku*CLR_`M3ot3QD9#8tp~J8~R$;1@_m~BmEoujk zhe~vs-qp&d66_rThl?IDf~wDZRe+b2EdA{#bdU%Y*nvD({{3f1N8?fk6!O_0clB&P zAm2C0lB}>&%6~&NY9>www{eV6tI6rla>IS-+Y)D_>M7UF)ATe~lI+aYv#6O|GRP4; zXbP#x$047PJv#=YQ%!r_`miJ=BTy#_#LSld^+U3^P_I-IAXUKW66!PIM_XYl3cnqD z*b9UZKi%l5!E5R&89Fhckg;0b%enm5DM(ZmjcSgs#vkE^_!ubsLrlAK!p!+tOi9?S z;N_k?jzvstkLwz(@mb^ks8_`(Om0#0g% zK;^v?e04;SQ#^ZSEPg ziO7bK8QO4v&2P-Uj`;7eS?(BWW5*Op(90+g#Ym~L&ommGRXCiTz$L&W2Qq z1TbOMIFRq?WN%d8eLpXAy&u0kqIJ)%w`2%P_VN3@z^Um5*w=84+kai(2KUxs*mcgQ z5z0gwWJYO!exKHvH3!mw=RklX0Y1&=zge%(e2P+s*~_RsVbpJ`wH6moy~{Vb_g-?D zAod|+`c88F@6!!hCv?j?mrGyJ>G$R(JQWhb?ONA2)1xr}-c+;YOdaeQfFL6-EmR+r zNpgutudULq6NeaB$?31nb634HBSkJ>lq_M%+-+Bn^U<&=PUM4CQZ}Gbu_7WmR`smp z7kZ+-%lEhSgFRo*iT#%ujQi#FS^(VUF?N@4I&INZUHVP0*0*qtS{{e}tuIX*MFn%2 z2bmntp5mJp-P~{q2@cG|79Yr)tf1g=$;PJ)Ab>Ffb<9I3@$P6a8d-sMU5?p~?MI2aud=eq5`h5z9 zlUWmSyKe^EK;;3XE0{>$!m-FHEbsPnM+4!H;fxg-$-&+}H)CGx<7BzNQoL)jE*Jfm zR15hrxrQzT(u_at1%Wl|NHs>+RG{uNc+qdovW?f94JjVz))mxJj=zvhRDQkjV~?|9 zO)B^cF|f#d`~nmAF;2F962HtmrO(Ce#smCs`x?O|KQZJv zxr58|+`Jq0ezk+dz|OCA$qe<7@YT1DXkEKMvW!lK0MS4(AmbH&t;G#U*jC$Zhbbwc zsmg`$Z01{W-#}%6-*21o5TVPf#- z>T|IF)oXlzi+NrN;UGbkM?HN_l9}K4jX0oM6YJ}Rcw(;*Z1F%{2vlfdMHMf%-a3W? zFe2WeCvis~45-qjH@+}lYf%;h4Y_GjV{FS8$SvOsIwaH1 z`Mai2D5GU`nWJoM7oe?u75Pd!c+;To&ah-qu0n&MAmHg_&|mIoT#&}(esgv)OuckS zhC6A2t~V4+DKo;6TYHe9#J)N|twu2(*vG5`7{f?(|78q2C8C%fuaZ#7f1(4}2rFHP z2P!l+ZPlK60uU1bD>8v4KIC-z^v~m8R~eHX!q|dc_^`pA{M$qg^%^zMUHQAS?+&03 z61(kRO=}<**i8<(t2WhcI8*_{;8&Wk{roB!O))@g+pJud?kB?Dm(k_6GZlDJbUja} z1*+->*YmtuYvi&=>fAL=Iz*Q_;U`9WE5U#;ma%ND+K&NQ8P$2vE$lS~@GA^O0CFt4 z$$HPfsip)>2HOdMBUYXj0K`gJwQ(MXf`QH#pM47gx;^{TB%T7n@p~l|&N5%u7R*9u zP{;9>Q*6K*DBU1yG7cxZCDW*>Cdt~N%^atVqC9}3axnV{Ph~;YS#`Xbzn;^^XFN&z zh9S0h_#0!Z?Awd7Q%yTZyT?V{zR78LHM7W}rjy3V?WJJG>5KfV=g1-5toZzRpX`+tt4prILle>I7Ssqu^ocv5?k zLUn_2HI8z|&4y$(52-^hXYYH+_CA zJ*CcQx3;`UO~kYNwae>#a)W^rGo_*}43&ELhRNCto7dWoboIqH2Sj&Nl$MENW;Z@q z7n=A3+)W$|abco=Ik9uY8rK!;k^1+WtpK|NnQ^YrDhL(Yl=TFV8BddWh5n%oK|8AZ zUuzw{@1;7;Js)$U@l@S7w^~+`up`Ey(nR0z~ZhSi;_^r!_z^QBuSDCSmeh3eu>2 zOv2rY?bM3D!-mjiqC22x0FGPelCrD264M;ha?QZ0u}eiK|H20@-%6XbGEU`@#R29# zux+A~wbUAe+#GRgYN+o&x*# zUyT4?DfP7@hhd%2b7v?&-ndBzd73St@Ew6&{RII%lT@kKV;x2!D)Kv`ELMA}G!Ml6 zHK|O20LRta9Z*f0RM4VBj`D|7fN&{Hn=?Ll7tAv}fTt#FJuop=Y*Gk5*nRD~@tlF@ z2@~aC5~VnFl`MGb!rAXyMvZAtIK};FW_zRN1r4Qh6?2@qLDmq?@Z4WBVRb%n>T}b| zcgTqsHb>d3Jq3XVmy6X3qvVmD^ zo|cJ1^}q_)2wX$eFS_ypEI)GF%dN_uS;EUpJ@6yhk15?SV6T+BN*nt|0H3Sbc|i!v zz(5;JNo|Qdmh!y^9>;;xyjeCej z6fEDr3^)A#UIV{)JJjnM5%|@Euvms7_zisW5mYX2M$ulePpTyO^Kzj%8Dt-b)S@Q( z>hx3nUn(nam%@V%26zNEF!cG2rvrk;%4Q@dV1$_U5}9=A&IrH&S3?@uA)zw?#;!ts zfMuqEPZ=(?i3x779?e3(fwsy@EeLqfDAM(0{hDToD+19BLGX)N1(ufbvFzWx0lFWV zADBNiGSK=%P45+FkfVIJYU3V9@r>^3r4z5X?{UFzk|-8IM%%H!zD#E?O?bsu;vT@z(>-h$iV+P8b{UUjD4!YNKpFioCHc{YQpFKq9$Id89xKS9x1ps1HG-eUz!P+6 z1l^na-);Ev`1-J;|6ig}?(iJyu~cnN5JaF+;OD9o12+0`a} zbGie2BuW&4A<5g9v!h<&s8Ht2(8P9WYhR;nDZ=KQB9w|r*ruOgbg;DzVyUzUTN3h6J@FQuF9a*z`vKS9d{vZAlUGpA(!H^x_d7A zS{qJIn`2I_DO_sN#J;@X8hL5+cua01k|lTdXvM>ha za|Pt6bkSZu5WH~{=}hxoYCwuLX}>7=13Dk{i(%o zg}u1LkGh5&cW|P&q;TSSN`Bs(BRLcq15>9O5j%CHeU@>HWoS4#vPKy8qgeAi;Zeld z=zYp%^O>*6>TTccq+QOeA39K0lcJdQZ?RW~`Pd=?Dej|6(QhuTE zBJ-E7^Ang2Kk}1xrqk_D2#>9c0$T?xShOf?$<|SAD=w?xeru39XK(h4PY%4aBk8vo z4`a)ilYH2fyv9Q>7_n5ERj?763nmf3c)H2xo88b$!ei;|q7?X%fzHX#YWy(u1`eI* z2P)A$VRB6+26OsGjZ8WKbTO<7>dGo(BS=cCT&t&$oi9o~B5>nX_GQ2$f67Q)8TWu6 z$0Uc-W(!M+EEn@zPC_&pSmg@_uo(?Up`*}}-y?;7p=`LpA3Z;P+Zg;|3WqdzR?Auh zY%AwY7N#c{N;4x@A0iENzl8*g=uQsD02@rjCr35&`uQGrL~6j1r&s44=2_yO;+H3N zRI6rDJXJaOl_S)tl4H(BcRPO9ysQWyfBUjK!o0F*#d2(LnRGB&^XH?JmIyQT8DyH+EtFr;%(00L zt}>1(BMf_$oh7C$>sj&b@UmGN==n zqdoNDK{!qS7}6_a%S1Bs!-AnxMY0TPS=LDMssJ;xyEp;}+hH`j<#ql#=`4Zhg@ZBc z*X7LjLvwrl?^k~O_;G|QXEArt%p=;lJagZ#iC}ZxPWSQ`6>@! zcUHY$Ei^^P9!aAa z4{m!`aW6Dh?4`qHq~7p0X9sqBB7_c;5t64C?lr$6ILIB+1;5Rf>8^39?JUV==X|-v zMcZcJm6s}88K;Hmo=hS52=8LI6|oco_Z?CB{muS79rd`dNH6SruVL`5heMW3+6|<2 zi~GecXDbm7Sw_)ADAnafr5X6;c~jbE*!iaIw2Yhe_Gj=UX$_oodA<0bjIdGBV9P2( zSXIeplhm80-iut%YVN({D9%3w?i#j0j^ zw>}?pwSm9(;xf$_SbIw}P~Wz!E>_BVe!t{N@IpZ7eI+SvZuzey=aVbt9Sd|!)`Khb zGkP<=mN^ArWO|HVqwUe{E3S3V9NZolZ^?TA!6H8Q>lKhr&wmr%#nteiKkQAk9w zB>}m0JMrE9yxih|g@7t29jn&uX|7K9P|Dods<)=odqLCnF$br^rX(tDmznnPgyIOl zbC%0QPVxs|yP})q{jj8z>acYh_F16FRCn2vVd8C<%-+Uwl+@#klI&T1=TsWtt~S_dwqXohPu45jm-&Av?g`8T@v zj0hw^#Xv>RdeMFN)PEwRiYn0aQf6pfqhMO0JH*wfT)KZxrTbSUfk|7v*U@UqFr&Pv zI?OUs*RuB|Q%9ra1H+D)-@a?ggY3x+d$sfKlsSsHgM3K@3mJ?!=_lV7avlvE60JW= zs7$O?Y)HssrVnN(RAuTPmhJbXkE!`qd+BbS(=Dp?Iv?3(AggPkO+`c!OgOMStPZAN zFHSGLAB#-I_<_sQ9>C4}OSUFv9LZ*&dH_KmOK0LS9$xUmuTOH;U)2jb5>9RwD}<=$!#VaeM}0BlXp@>8612)z`GN~txa|*9bIWo*D4&$! zgt?FIN`eWq;oOgD!=aWqgsP%W3S*jo;@g}AvD*zGy$Z-RTca^yLABB?P&i4vvwq$I zRZ-U=cl#R%xuIArAdnw_em8v=ro@H=gEb=*^rGF4=H@TXU-`Rje{XGzdvxob>VwZN zKR%@fY$f5XH5`;H?{M^0t2g4RPP|^LIoe!iI>^U8AUY2&*(B_b^vJ_isP1weUo;AH zWaAL4NN3}EP4B^p{>pRaoBLVQHWJg)=W~JxEuRX{9ck`U$4RGol>7$f;lAmVr;Z)C zbUghvFZW(vRCC~tQRs?*dJm6k3@QvZvF<%Y2`?^|6HmJ^!BLQ>nD~a3ab6js+i-m- zNpj{9No#gzjm%YUTZNhTB+1nJj4%%DyoA1Oc&+V)4u12BSrxuu>CIWt*8FmaOJL~V zNv_ss!iGxn3SaBIUbD(Uc=P8(3@c6lt1sk$!A{rvk}(ibj}RLR0%@b72{~`O?*7p9 zHxDfV0RbvSLu$+E;||;i$2$W~Ki4XY{KxXwL+5Qwy&~E5&lFc1skvDL@*DQ@Qs@rmncPyYhG|K6($_D3zy3a zj}4Oz;f&KGqv=_KF%}FO1@g~Qx27J!_mhSt(+fpR^DRM%W2#zc`=VfiyJ_q$x>AwP z+sENk!H6o88?MX@sTW^o!zFp`j$gi(1u4{C$Cv<9iJ4BAQwO8?jF&``H885U_&LhM zhl3X;Cr4pn=o=;nvIo2lqnG_ELycU}@$z~s7p9j|=`I_~+eOXgAzgW~n zq@f}o6c*>`FRoWa5d4`C_3ufBmWf#AU21#N$qp+x%_)3*NHA!mj1?rK#gKJ~?mI2vtb zM#)D-J})#XQP4pZJ^QsuNc5^`wu0(pWf_kDUfg>E;LG!WTcO8$Pl|i*b85g?>nChI za72Z}tZ6z#zc(Ny*6AANr30s^=`ocLwBpmD8qAfM9s8B2oX&X)l6gAS{C%8D;<3mV zJaw`nOe9&nM|yjD5l`O|$DZ0QfFEA?rgsPzG)%{hjL+{Pq2Dup?^bfOFf@4ye{XaC z^v^ofILX|HY7mWELzhJ2zopKpFsSyTA_(#9+6Tu~43|oH%h3ZDBvLr)fX{PIEF(a=Bydc z->(>gdi@na+>d;CcPJrQDHgFMQB}JjU74fz=W3rYopa)Gpl;_q8E90VABMxOdl;x9 zrc8mloiYZx$an|yFx6u%G)}M6(QI&G?YRD znw~>RXzqx)*3zx%I`!C9>w5*-<=OPp6;dSnZvrtQI1Q|v9+{5BE@H&Yrrl0*GT4#` z=&=Dec<(rooB2E9e9f@MT4zm5*3H1TgZgN5Iudk%C@iLb3Q_|Qd$8ZXal7j1+?F<3 zjP^}F)9s}deCmB5kd}6tbQ0Dhx8Wpm{^cU?B8TzRYxWcP;-v51aUz4DJaEx>d!vSO znqtyaRtNkop8^tS-hOr0ctr^}c*QQyK|ydBUKyEQ6+mV5BpHSD>XzSHepQAI1%_0x zPe=ofrBD8I&)*xOljcUCE~EMbY?v0TxY`LG;Y}p4o`bCiDC&hf0midX!eHQ~OE2 z{Ma|pDD{p(Oi{pVrprckkc zGT|Y)F#QV27&qE#a9j8jcaG`6CgGMT7b2igA?A>$S{y+Wm|t1g zUfnRMJoTcp#oiQA>71ji|`G7Ahz zb&|&~Pai==%vTBx!~V2jJ8DF^W{t;2!(m>(gkKkpod)Kf ztGq4KT%}66D7!NRY86F1pi{=xJUEeMNnlUX0Kf-!2s0u~sp zMq~;U>pt2uv5VB`WH>Vj#%3ndbfk)yXl;aYta&A!DUPRrhm*u76%Mkj@?4>kD33)T z!x@~G#5-4KLkH^`At>6w--7@jp~C4ZM$z;~9Xk(N=SO@p>~_YGf9;6LfS$f`zH2|1 zE4<;uVXT=AxU@d0_ZyI};!iydNU|@FeAJ-(d>xUFp|$lEAc$+#YFO!!`QZ7rMVnC8EUzD~?F4wO8;$nUa??YI=Y0!fDTfn>1p?Ug=)8 z?A!&)2h2%h$6mo3BHj7L9owB8gnlJ=p4wMw6}-_{i>EMGQh$21pA)kqjyBb0T_FSETVy}sT; zaUXcaZQ?LIT=YT41HW}E|KN7QBdajewfa8Z)xl!f4-Gg+dR#FuS6SSoPC;bAFS0}?7bi_N^ z><0hcQ>1hdmiZ=NLmzM4@&`eD#vMea((4@!aQga;olzO_dy4_LFv-c3Pn17e*6U35 z@VsW&UI$+N369Y7cn$i)2HU-i#}pRHCjiG$$GKak0#qYeI*~WkqtRq_ydQ%{5Ln?a zI&)P-%mvjO-V}K%&)~t*=Jgm)d4BK1c)XBTG>?dyvf%+OKVfs7_8Ao`JDN;1J8#76 z>PFuQYEF2p?1EnQU1WS@e8PE!@pRD(^Es*WIkW_{NUpfO&+!=^Z`zj0aH>ll!4n%C zweK*kThqE{Arzxw90Ts${KLuj;PfLLvdU*Ck$DyQB8SSfnM3AXZiu}Q+S8G{Ap^G! z)I-_6gs^PJG8ryis+h{h%#F#EpK!a4uo%UCp)=7sk7F(#^~;9ejcY;u;EM$Npc|=1 z4rN1=TgLUGm`Xck(ni(ScSQ9n5jHX*T?PKD_5SUlr+od7P&T}v0Qbu4Fdz<;LvA3X ze`^ib2GQOBF9;!|iAz#e)9%ZIF+zu^GAONDJX`(3SaU?tyte>uLTP&d|@+t^6s#1Eh*j2}=9*uh->j0zC zgsnz@a(yo8YnSp3e!IRLJ3OqX#2eWj^CUw=WPba)M`D# zrG<1$!x}e zR5gZbtt9+&l0O>sK^1VWA(cF?tOc)7pkSn-SeVbkDm~bKoWN4lRl{*KG~Cndy#S); zn5YQZ^MeyK&RFW>I!l!dZ)N@#gaWa(^jfZ$;pNiDnyx7P@5^8E%_O^HJhV$q+Gs$2 z3Ddbm2y2W(=wgVdUHohE@)Ar?+1Xz8G}~-9S*9sa;KyTy%p}*@z4HN78tPw^_h4RK z?F0Zr)aLzG{qH$?{|qF2bf;)GGVq8b_}5n?04w}*eTrt281SnsJ`-}d$&q|f^I6<= zuM%PRPN~7q^5Of!B+8F7xcEKpeD_T@mwM{YW%Z*Y^|&g%YESd|juf%4sH0e{8_f9$ z7ZG(C5@?4`C_oyo5!yL+1?b4TaD!2gZfxQEWs5q@0N3_+cA zQiQ|8AZ-Db)Gwa49pj|REd3uj`;M($B++TpXfgvHAmEnTbq9R(a5__OY?>96q755x zadmeV_VH9YZXGl~zi@alS6Ot>nq9{%C?Ft!X>UM6t>DHw->j#8+>R(kv&;Q1c4Xo< zR&MJgXEat_B!v8%UIk>&h3NL8u^^X?Lm1w%7H>ka{NI@DS_5F;m=ldfYQ*UqEu6N# zo;Shp_=x+MYK6(&UZl-$V|%CuHyvvIxF~vi$0h?ur%1Qs81z;BCGSl z$g$(0iZ^Xn3+(8$YmG`?C8In$xZ~v@ZLssI!cy(h`ljp1BAE0T^*j)?@YSeuite1+ zFSt+(U2+xo7nP)TkvL@9Y5>Sj)Nc(&ey>V>D{=Sy z8{P?ChR2N!4r~O2Uqwpd`?6insuUhgHh74lNk(<3XjcyePG8>L<#X){a4JpquJeQc zd2tb2;)=D9r>Rxfc?UMMr9;Q!8HffO^|PU$YiXX0mFGo`1@PHHp48?mX@xVd=*I_X zoZhtUU-pKrS8@E5sIZOnxRZ^@4`;%S9n&aJpNHJ1$dij$P{0Y{`D{*5T{1y1Y!v}Q zK}Ka#nQ6>f^%3rd3C=3sxZ?-u3QDIWn!$#1HESIp0bo#=9L(T56#_BY=BjH)a+m(z zc_ap!s;}mqcdt9`J+L+R;GcOJ%7q{_VmljbrnMQ% zuDf6t^RE$pXV1Vc?bmeYi(aW{hL<0z4^Tn;jp^#29^iF_I@ilbiet+j4wgMypx|cz z@qrsdNq&%cAnS${F{z1PXDDPaI0On@(WM1JRJINTO;;_un_?X|D2|T4L=kxUa_&x1( zla5|(_@9XT&((B>a1;X;X{qb*4|}bPMvwA4$6hI(s&TuqBZ7zzSCuLQl>m_H*v(^O zi0x^&6j#c-mQsNbAQn&P9UXc*jSQ+?M%`ZQtKdlYHw(8bKfiaq5yYT61$ZCD-9VYE zOGc~?@DM?)zJCn|!a#&Y*}eUJ7vsMO!c85_Yp}U{Yoz3XsIU(XeQcQ-@eorSK*4|h zY-#fkY+cw5?ob!TI4E0Ch}HzqbN1zWS89N(+Qm;FEW48yDoahAN5%C6l1IZt^^{KcnM_;~tqx zlc~tQ(CFT=%i_0r?hPK{hg*XI%CP~+bibAtGRjl>b7YuODv0UQ$kNi`G4?0fk=nxZ z*E4P%sfb9hhp}|L|BJo1jEl1E`bIAiySPw9Km?_9C8_!>LlF=VP&x#Zl$Oqcz0MiD?|bk4e0ksJeLn5|#UJCGGv{%xV;w90>%Z27fk!me z8*howIm+d`@0v%mx%pC$Lw|`Dr?->56-)ky3m`4Ja2lI7l68*!3>U6~-@g7*zw6_$ z9&L>5Oy{%bt*i6*X!Hgj1VWIZPBpvIpVt#TFyWjU*+)LEYE&M zt!^2T@;i^%ci+3)Gu2og(O7Y2_&jE)ejr;X*dxJ{1F0C)g`WPlgO2~3?21&g0e9MC z>W=H-sqZQ`=623{`IcKNZ>wXj&uHdkyldb1XLvYTG~|WRxzA|^br1I!+=>i?%y}!- z%wi7;cigk`7{zVYUs>+!;985Cxp@{e!?HQ~W@7^^IJMa{D`@Z41e}}08`po51IgU? z=%HZIIe^U#I$80L*UQ=V@|_1|);+=2;?KmvFdoO}6)v&_!zff@@!&YYFm@6QqYw)h z!7yUM^yOG{Ui!CT{0lLRwGR!Xz%Zu4yJz97{)b^i(6=1Tc=6d0+raU5<=UUMIP%qR z_h_Ni^R}mZfZo`Q+U;fqkpnS?n^2}VwU^9_W_p3bI=#d`e)}%NeAs$f7H28th}Lv; z6wZrluG-lcZ%wBT;ySh#=qfMf%5Bui!ulzRjZFU%xCG^`l-{T-+joMY@9y>Q<9RWI zfH{UC*kh3J#g=?(nZja$4(n{QwpagO2#gS#I=#kWzNk8a*}X72s%45LBP{ASV1?eCsM9CVW7M3Ab~rJI&z63}CM-K{04=PgE?SU$=W`BcBu7@- zNsjd}S27aJjfqe7W02ChcdMWPCqkFr)8hBXT^)0$$iO-GtoG}7F(=mZU0-_;K80XH z;o<(eR0X0T?e2RI)}y{>tts3w`g?jPEw=8KoE9Ggmha6(KJgtE;R?<)>$zQpNGxNU zn?qyHwj$J$vLA|J;uX4$=h!iRzjo$*R|7D~@0v20KDX)d7q+8sK0MOZWkAiR=zfta z>$hj68OAYrkaJOLy0Mx5QeCuBb4WqV_zTkUbCgxI7bq#QIJGOJl&Gs$uO_%D@1ysX z!f?Vbq5>!I6pvCh!Wpp?Pvs}SQ zy$=xgG4$C-tzG+}cM#KWJuGm~s|H$7vh`2YtHT+%XSfXeZNbF2=1f0M{nqMk^~hC7 z+SKY{R%IB;2B1GUAs2V~X~ zZfh+dgn{TInDW-S1eMk_4QzF#;ua=sN)IP;kbjriEc&lMt+V!KpYdze zf`6|V9NnFWr306BuHBj=1ao1u+xwL?~qLTo*&Q^VgH!k*=)=U*M z5@*urt!Bi^qOQq7-b--7@!PDh5eFHfdZbe_)#sKFb;@-+$S$V2SEz9@ z;ov-Or(w^C_a=J=xcuI}se)v2CHs$b}6Qqy$4m0qWK z*W2>fi(^j{unwwhM&10E?IxRBcG^5qJ@Hq_1Sr=#t}ApaUYXpsuRE=_v*E7GNcJT6 zALXugo#JTY&YF`cX}`T(HExI9%BtyN=Akzb0wdPqz#3d%!`|g}Y2fic<8AK;pM36* ztPbgRR+-*tD5hY4o8B>vKRb(if{r$($Ep@j9zjW`rt(v@F&_Z8ZtQs@4`;DbJv(|j zFX1=NmCM~$imTmwEHHf!Uk<-vBkY4>+wu0-04QC+XEmD<&%+nr5@TE}df%TT$axZ9 z=)2{vZVdP}$oV0}bi-YSEQHenj>ZFeO43MQS2RZeKynMgH_t)wK$*agT57wfc8sU% zwP5VNh|d(8F6Xlc=5J1G_vxH_mKG^4N;Zo`VeR;Ai<+2z-p`dgCy z?%P&X0p8TrLGOOvh_+0#dvgEw-gx(|AYP7w&Z$hQQXqU3#QC^NCL)EpGHsO;Zv%;K z6fp|GdAhMC!xLyKW_Bm4z>eYUtT_C{W9UNrdZA<6UX&_KEZ4Rs@#OFYS8x4$?hr+h zdFk*xAoV`jNzyA9Ejiz*?>7p6I+pZ?NNGL9UR_`qz?xVltHCeEO+D~F&Z2jF zDCxnl^($P(3gYHlV@?h9gP;C+|9@`NROl4wu%=`=%B~N6CQxkYbc$wVipuk9H^2>9 za$bdF1Sp{SK>81lleDUAMT`6a;0sh-O*+HCw>o`r8>w+#E4J!!)bJT+x%M`_Q|g(q zi@XTtmwX5xOBl9tSu=c!fs5cV-K%bjuyz|#SU&NK^vWiQ$y(xQO zCBE9)(xIo!Q|0=&n_-S0H`{fEVx`j(G&Fsyt7o z!mTGVg}Zc5jC!5G5-&{_*ur6~l|af@Ky*orY%Dy$mGPwlA8)<8=aJL;pM|JDAcj2H zSRzw?HK-cChceY;B5y`G;}F##ir|UbAVNa01(sMEGW!moP$+A?WFs9ugvw%{ULew8S-Ye`*#`cCuGNjK-$Xi ztMPgo$5QW7J}%b6X)r+j&DTk*^=*WOSYqHm{9PzBSXFra>RAJ%x@+jwGf+l2u`})E z>VEy+mtJyZ#zt4WMVQ~o9ZsqE!;-r6csyShp(f&NigFGTD8p2}zBY4tF~Q26Pizzw zkKar$L5+*CPMgx?($VRfxPXfbc{K+V)i~EoB6cWpNPT6Lcy*a$cOqb|eQb20C70n| z(arGwS>QTK0h&q8t)d$dQ&2v*8#54K=D4{~#osYK%VWN+cr}J+##Y*vZ-Ea&M7si^ z!UCv(fAszzN{^~o2kiATfS7jm+aV0Y(b+($tWmopyQ0k*k{R8#(cSN?Hu^y7@m^2X z9cdRF-fbK1e)1GqDN_MGcBBid=LPEg>+izSqk^20H|DoL&Vjb96 zg!$fSv8B%hKGdQlfA@51jc42r9tZSx05{dl{SJzV-<||%oV5zI;&Dklb~lqM1aP(i zbZlY29vl^h9`-Ny=q^zRVF7GFf!;`5l>7*N@nz+*dEjM7pT0 z$VAS}>(7sv>Erf_1{xnSSJFaVw|Kgtdce!|Ic=U+8JUet`2%+utAuT3y;AFREa$z8 z=elT~Dj%w=wjv`{)Frn||N04vJp7mc(BTVrfJGSCW;b%noN9ws_7s+7xcc)~Wx*t@ zEmuz8M_rM+@-gR)MXANI#o3*aYD0;1^`!EjePP);?>43?Q%9)2+UAb8gpQCY))i}< zvkJXo#%QvEt~GSoX(GLL)q8!Xy^JpZ(X3`EMf{V(6#u%=?di+c(gLi$6g6Ca@J-n247$$N%G zZ6IUj9_oof8R;pRd4cMC4OzQ)&9K;^H>O+ZNvGweZQZ{=P0{3?m1Vkr_&=C8|Ie!H ze;ev;DiA{|j1!Sz6WjAE`o94>Z}U7I0eH=5Lau!j*w2|Ko`)pixldFdCo9%0O7#37)J#Hf784I7WI>v-+vp zUX_dDLPLY(ebs-8&o%R06S!tvid2mo*)InJr%l=?_d*bZ)={Jj&~(Nv@t zDH;1aj5t@7Y9v%FwchgIDq(F#bl{FFbR|7eZ zH|KCZ*qe3pj9P!7t+ks7k2o}iP#guabWg7@mD(xap8Z$;RJyE`Ewo! z9iJ&h=7Eg=&CKysCB)~Ro6mC*zXfiI9QYA)8nwuaL&S50^61ArLK8%-JAr66(%JF_ z;l9)Ye~Gkjr)7rV;*d`WK>4(OrQr=EXw^WyuP<4D(N9ZaOGwB_Z5ut0;1|3Z2n(he zIGy5&#IbQF@Z9W|b@*4$pYKCF1r(;5)Ua^mY7b}xV94DF>;88Q_L(;D`lkFSpo;4v z1Aki0A1e^0{Y4;hF#PgAa!MQzHeZDhSg4TV!4V{j5g`=$PkP~B0Z*1dadeks=fqPS zHGtyyXpueR-%P3BNadlOijxRPVWIHn$iO+n7b0Iz5UTfIsZU*egO9`Sf#-HH=6sJ( znqa&Zp)=$x!bOMW?=j<(*!>u7Lw~rMxERXEwZl5f&}4Gp`F}XVGGllQi2)vGg+Hr{ zXucfvh=b(Oxb!<%II-8q2TFJC0=)vgL8T|hPvtoj0s7Lw!U z@I@X~HJN7#2^?Hhi$L)LR$q=oRH1>a*i88S86r_8LYYL=fkpKue*NbllUs^!eT2d< z?;s1g!$Mf>W)5D}_MDxN zHL!eG;^MTyBwQ`>2!Xiga0YgShea-mffF(6s3#7096?sBvGc*4Nx1s2g9_t`;T!H;f>Ue`e|TCn0xePqtQS%>6)HA)fUn@urokB|?zS&bg>S88 zKOoGIYc$d0^_~QY=kChC)H#E{=?J_UjokT|NjS!cOW2iwld(TX#tBs84TP^5KvxAv zjTB)q-j`?aE){r%w}j8B;6|l~-m>98`W>pb2u-vksFQ-QVr_!C&N$}(0e=Mo?%sMR zcJJR7f%r5$;**ha1bo2Asgo8|LBn=lH??6bDHW zP{l`h%<*ImC0?xmJDe`ZgAL$89`^7xExf5I1t<)uj9N0JB0e1CQVIM+05?_6U4DUg zZ12IFK92Btg}*Q1pGyd^@CeqNi>$er+NcVl+T&ZFHv3X;Q6pdE5eHU@6g}Ab7}<{+ z=>0%i-E_dNen+S_g8r-E zh@~zN+zxK~{{cfJgy+JnaI? z?CxJ4pFl^R2!XPw9cZFj$?Fe8^f#S5OY&Fx&SsTJzeLHb_^^KOviWVtX{;+;_H-&m z=9HD%7Uo85jc1(^6789-@1dACc3FVCnP(D)55K=# ztVVv#I}7#7SQ~yx#+kdf+otZ1ZMM>4yj!b`TI)j-}8YXc!`` zIG^5g)Q9o9{T?*sv5&X-e`wepBpdq)TJ&G;$f=;|w1V`Hr)Ex`IwP|>;l@cHySJG) zx8so0QwW@s@m4;oNXIwaM~UW{Y9{k#W=$=UfE)X&U8~eyTbzxG4Vq<`269fOuUZI* z;G^)(wkw+l$zfIaNzN+m9af_N1}EWlw@jo;`0#g%mfu;$EaNj??PoMN?(gwB+^@(X z;?oYv+OnMFwH@^%C zXw;10`ePJPawe%VRrowI;9O7I%x!2iOB9xnufGv5#{rUmJj6Q5H*hKW!yzZziZ)MYXS9o-z6J}WITWE@s4mvLK}(*;tU zOc7FVHS6i`-v@d$t#1iCDaNkey6D-AfywV0qiBDVVvj@;FKl@tc9DK9qSDFJoIn5e zhq2g7K5-5SL+-itjy2!qy{L^<=L1lhKlX0c7_`$ z0=)CR9dS@X3}SWV?wmLIr42@HVJ(RVZz8?O@zQCopPHAU`6;iqty3G>+VUlBUSpRzZwAB3C3g!WTam4fd{137ZS4DEdG8 z!v%N%Af;aCNKKerQ|;ntMqrvLYHaXfXdubPuKQsL7HX7N;#nrudr(1>EHN6rN0-gg zQT@?-t$-b4iooJe`i1}s8;`Y-DBLm4BAkp*87Cg@+EtqG8$}MME=y6rRp0LyN?TR{ z=03BYrAp}iL47#qL2ous<8|ZF^@9xMpsetJAVU$#djv5<-kDdyPgLY5M%R2hTCh%% z1x%4G&}&EGzty=g0qrV8xM65@t4RLXxMen#yzxEeY@V>YMHFHyCtCYljDsI&@%R7c zCHth{aarQu*p;(DuME_;I&^mqTHJANTf1aQHbV~P1Lx6C9Z$K=z-|i_Q9n3}*m;pX z=ch_qJy2fE{&-_1_T1=bq5J+~#rWYe_x$+-!# zRP9gBzeNf*UdrGWu=|2@<#WBG*>TN3)9!IRo|bUh_*0#cboetqpz0juTF5nCUm>oQ zfC{%jXnjcC=**Wkiwyj1);%EAI$ET8WvY`#IMj82p*u!NG;Bu9ta(|hW!3Ff@ujO} z)M&0U+p(ZZ;fji3;Fzfc$Bf_0rVsch&;c!8v)|JhJ<&u&W-+j%C_e%HQvnr} zDkud>Hh$EEJ?f5S_i*|feWEMcKnuUO*yq3${Ocry#eAUgI zJ^Rr>62~${8D97KDD(X(2L<-keVc{X&?_JfOn){Y@P%T7 zdSzkty^O79PVL6A_63B>)r3c2k-m~B3L7b4z9*YkF9$U&Y#}r+c4y_!HDUW9=g9+R zU6HQU>Q{h(Dq9;lbusA9{t&76l#|+t$Z2_Xcy{7P{|oEiU);*~&~G@6Codlq?e`tBm#87a8KjA6UEdPfdn!#kDg`?ZVP(;MYIi;VCaV>aBFRD|o8 zycju8T><9f1)bf2HD7dE{%D1P!Y)+*g5AkYpW?;`mWfy=~;z%<~rJ z64>-Df>A0QF=}z#&Y!>U^}!$b_(2bk@%(c2v*O|XpeEfGCq0K#?YGzWHRmftV7MOl z-mWqh=#YiZjE7L$ewUVqi&X$i-qP@*#>AZ)%W3sg(KTH@5UYM(k{lTK0}p$Uz>oM9 z!I=~3<)|G&jJe1Ye4;}d#T1J{QmkqOg4)=4P4RD*z$RcWWFx~ghJd*&Ao;wTk`qM7 zxK#7T4jWHI-O5}|51gDH z=BzaGg>K&sZ>@uF@h1Z{c86!JDE|j)}x{5E4@;M9sd<w5?XX zC4M$q@cpkS^k1o+omMi<7t$ZJ4yC;XR?kaoblR_RXz<3h*)_f64i*TVc*^?wohpXL z`NdCmzo5$j<6Kd_TNzVH8+!q9}8Ju0{w#ABw7uj_Qlt27jb>)U4B0{HnAZm--Sq%M7JS2 zIXQczGV7RGGj#H6&D4A8vqUqg$cr5*6;czDKKGFKF4}0n$kgIY-5u2PZe8o$MY(xW zyYYa;!L?dng;=XzSBaP>OYO%ysiIDwIf11=%y$pJ-}DE0ZCkksl+3mIWTx9cdgqNa?B@}~(p7&FHy4;3cj1$3` zF?^e<=yF%d_&nNdK#jh&qnUxtk(wu!V={K*ShyJ z`4E#gY-A~luf%M~I4iAar2jRhr5GzMAD}k0>O601`_5D>gFn(VVT0T}nu&RDSIUZW zySp7}Mb6vTa>*N&Nx&w&uY#cwshGbN!?vlZ*P4yzN8F(!45{%rbmlaPcy;@-4VBU_ zwl8FtU_$)SfpvR|A&y8BsQWJP<-{@2MT zU!}go>(HP{N=Z4{(QxSq2Av!Z{1(ZGcP8Tv(Ny`P?KEr8J!Ujp)n1arq@eR}imcn) zIow+`vpB?oE>w8(S~wl%^QZ?>X6TvMp`$vxn|DSmAcy$l1(b zs~-0(hb5B?c6z;mH?Op`VOU7H`yi%f<6zzsXB}0s0_G_~h`+L|s2ibuX2;l~d+~mI z|BxL!S?N|<{c$C|Xn!Hr!xwjZ1Iiq$8ol$Y%d)*ms3!fVBv-80f4}QKZ!r6o z)roU$2c7qb0pjY|w2>7-CWl$~v#ONog1P6od4YlI!w2n#UDyuIN;grYRQ2$>_|WXC zt&vgZs_zZ}$d4o6(}N`-K@-q|6Ii+rc%X!s=mC-fhWy3f!Ghpi$1gz$d8i*P>w3s5eKV*S^<5YBLmBTc2iOgnQ(PG?WWU*`^E zh%dTZo2#Uz`0_A|PF8r5y8uuOypIP+E*th8!yq)j;mpn^#^2V!GNPz*NxE`Y{zdxp z2l1C*o_|9H0VueSte2u6@0~xY)^+&2J2m^@et(K!hIYsPZqZY2T;F1ut8rt;vpJtz z`A~$fifvbSjc?b$xvl4()Jzkn)7iTZ8M-BBDE4AnZUCX z;dh@|hhpbVIvcL1bwH2|ZB|;5_9)sOcTuaz+-8*=IXUYI+5)pRaem-V$r}=80H_?m znm*Sp2x$Sn4g)2U+4~$+czy7N2%-&-*&bxi=a@v~Cw}feqXdLg4na8QDLBQU<(SY* z%&%cdqki*bh^irnb^lFX7`q{q2gE8FzsF1L{Th~^h66%k3M!`A^TS|>kFE2hVC0c9 z{MLzr`RtwHH=h++wI75y>->7TW-fwDG5rSbDU zn?`jjNl?QxsTDIhAxNVsZkUNxIQWZHTBt4!+GMG#MKK5MDkzY6R}z(LJ2g!0%0ky@ z=bjT1PL(oiae3a`^vV4C?P=};-{E3RH@9NN{f-wXpbr=*JvBCuTtdpP_f$BI`m0G> zfSlDFNB*jB?xpMIr)oZbJ3X>W`I?$gnIkr6kw)wO{qR41Os6-72Sv$tmM*tG#1vDk z)jf%P(l_qkNiU{)(v^B0T-_zg-H#Haq(L`-q+oBR=LSw#qm!@SsA+H7It|_OT}I~k z?V>yIC-(ox{7GMp?Vja}jdAF8yLH)RY4MZ+fOy+g_=rgvLzPLheE@9DCu&mIv`Day z-n%rz>2M75XV#FM=n*~XiLKb5G85iSP8?|o+O{fW9B;A_q$o9Kj9D^G{`E;O`U$?v zcBmYD+$H0nn#w@hR{_U04D z>e!`Y>7fC%>#*p|_p}uVL`x#+)9E@cEf^`jKmmLc1gc-=A`X+{7^%)?_QpnTZ`(%B zi1kog3KdGFWJhPZCw&w|jq8;`1HhTRjm)$s@4gkmB!UDilUlZr-!VzBa+)zUIf4+` z_uBung~vMyHDjCqGd1IS{>G0&X&lf2hxGWpdNHVy@V;XS=IWi|G3N&s{FdD!`+#7g z5B0)XEXy{IAL-lWwozd3n`(`aL%2*Z_C5$n89mI8kY-K(Y>Jb9xhp4~TE5iHJNGj) z<0Hb3=3?pX`*~G9LZDfg&E zrzh{dv0ICRyrbp0;R}n&$2tsRFHJKu6jXVt9XC?a*5L$6pr3#0o<*9}+1F#%RX@xf zp*~8Ic~3^%W`DkxZ_Cit<@i*mvW{d!A;pWs;gz%1lxp67RAxKXD>xnA+7 zyI4Etm1~l5_Tz+)RM4Lt_A4^1$eCckM-SgzkM2K)DHk48y3)Furc?vXqJSoTG~wSl z+H;6Ev8u$<9p>&(Pj0HP(55V|Euc1eLfS|UX{EG`@xEXYm?F`%M;{F(vVnD&=X`DO5< zA|gkuGhdu&ll4&mB3vR$Bu$!RCOGZakqkEgPQ%MfSj2WPcVu}W#2S3fv_H5{6 zb*Rt0%babdV=F4k#>U^3PSp0!$xYzP&pEo%l%Yp&qiJN^)}vj?1gBNOxIgw%;2Rq_5+~(dzphoMGney)v~KMcfK5K3G|F^ z3z9`B)|?XWouCR!o|?1VW-Zb&9g&5fzkES{=Bu#7h2lJ}+;dp^^(kMO$ho*YyGyD1 zF^bE>1&jQ1+%wH;)BO8~ac&2cuhrOj5aCB(?CN9N{tYpCG0kkl34dHF@R1Sb2mz3l z{t8%rX6c5=#UeB<#@tgwQc7Q_Pv);&WP?0>~ z6;#@o6EUPE6r$4}edsC6pZ~0!`V1w&S-K^00?WN=Ek6N-dy4XBf%moitjmRFkNSI} znoX8xPB?X5X|bsFtSgyc83a%RnM}8D{;7pVX8knp=?WqD`O4LpL8Y1vr}>g3 z#rS5!6x~koBk9A@-})z}Sj;X#Gvz;CjG5X=p6sZ>Hv>AGsme4auC&HxUhj=}#7{*qM5um#^AEK>yvZia9!^D!U{@v((2nb1em-Ldt6K zCA(&$EYrw0=W{}wmHW_B@*dA~zur{x;31WQvVrX0E_*9R`M@T(kOe5gQRe#@b zENC0#Zgu5NTmJAp#`@=gqeV3`XUT!8(&*CJt*P&SBN<6G8`9wZ=wKr6YB4V2bfII9 zg2>}Kj}d(s^093bTf;5ekt(~mbdQSMK?hySn1{Mzs%`2v(+uNokO@t#l}ME*G4xEY zu+pJ7zzn2l!+q_4fds#};hpx4(8p!I6_v6@G^RR1PJ%Gyq*5U@xOEKaj1HT&VknZC zFFRDE_z}MGgbEcP_Da~# z{HVQ^KOt8*a_rpbeGGM;W*gT3jVd1+W%FuNUd) z9U8vBT2a}-uJ>B|L1*MOrQ}X`Mx_2e&3@wyUac$H2z90$t5bvmH0=*=usA>(uz&vx z-vwEl1-tOQS?Yj7fS7qvq$BS*Pc#D^$vC{}x%*eH3vrMJfyGf-Ak##F&T~Jp7aSTZ zHEFA6e$fqJ%&kYV=L7}fEs=b+Rv%hWJ2t%;gV-~cTDE8ESFZXVZGtxBqFXSmhz(G% zLJF&$ZzBey*iF=FaT{v`BO%<9UO(`L=l@PT$=2g)7uhccxowJTSK-G~9l4r%xOEr} zJP~a*%4}Mxu*2$zh0#3e4+tG0X(KW)C-;Fj@*OkiMuUdF)3`CC76JBF=HgtF zi@^WQxSv48KBM?E0C4*xj80xPn|96h!!@&JotBz|-UzpqM!^xhGDoa!1CP?3w_sN(0?889tw!~R+`vu`^-^d6?SsN_b%#L7B8X z?b3gXL(?a(<|0&?47zog*LF;0TTzv?Vm9Qxp63wm@EEy&6L_^XL9}k5N2}1g!W*6C z<7t%crN(hrI2472Rf^-4>&4cR8@6Z1uY?ybFh{x_L#8$D?+d7n$ahO;Mx*0IF)xu3 zT|IL!z6L_HGVZb4Y$Iq&;cs0NjvEL0S$le5n-PqtHI_{4bepS_>iHQ3v3GJ~=4Kp{ z-UG_C>BH}Bx7V94$T6Av&FMUuxMDng;z4VgrKiSvJcYy*CF}1|thP05Ph&g$W^*sVAM4ML$b}@k}rz(U19!x(4R<6f_l(3QQSip4^IkCu`@o zp?KutaO}{~6MymZN_sBempNf%?Q};aM&9W2sOUDQIGHx-?33^8I-j5Q6KDM40zeb( zDkI`t`3s;38IMWN(**G92DH0z-!6gQpPk-r9faS#pD%Kqpu7Ef(?0mYe7viCeHU>S zWl)&wsd51NJvb2qA_g zAq1KC@;Qq?!r!k!Zq53}mXJ;(aR|vLLMMmXG^28X>{ea04$Xxu<=piWS z`KZT-llNni$z;S%uDyYMfgn4)z%kUEuk-`gz%jpV>9PcNeLVpF%vU@G>qZBw~y?>6)+aF{odq|T!c zW7ZPwii(3c1d`L&_Zx>@Yh~0!9mZ-5W&5@4r>oeje4am!XYRQ0A^W&#<}L3AO0zeN zw;JescD=Q}^_}Sv9woX)BuyrI|IiI0nc(4z#Zk*{F}(Z*HikxRUwDlszbI7)w>!#( z@02a>Y-#rlP+&fe2}YZoZS5mQ}t3tPIt_p)zaS5WMXgBBu6)2 z^zNcFcGid#Ej1~#5Ip`V);?g|oq3UW&-HE8(ZU|w#j&M<_8RQ^m z>|={>He5G`il&Bbc6aE?juq?~uOE%KF1vhq!sB{KivlX-+Lq8(lnZ@8L%SMJt~(L& zaT3We5hnM53uq~o_PJMo9fJRQAR!_zCU3yE;hP8V)82F-@IbD2gtX3)S_YqW|fQ!R1Hb7a`XarZ|@=cxZN z%h*-4we_dG9U&(B{>QzWzt8QLt$$~d-Lo~%eNtD{qvbS`wX~Aa?tdyymps zfl*(TTz6M|g)R?TR%)dhq}WHrNt4B15&m^eSjk)>wCy3U3_5d}BlRj&y#{e2c z8wLthW}cBiP$y8iNqG@@gR^<8V$VfT#KaiIZ$~3(=* z_;e(3gK_crYrozh`k+qkAWDIJ*599(B}ONm_bxLlXT6U1h;J4QUo8%%RVrqV#Vs7c z7+M)0MTGM!n;@;}?6Q8mL~1qRd>zMrnZOysqBNYi!c)NZ8;~!Ni(XO1jF~y zio~UhRK!V_X=d+wM$SlZNG;pprZT@3?<_`+*H|<(HY9ATZ+&k5RysfTRyw?iJ!c6y z_J@dUuF|G)kT>@bULpHh0KPk>B7>WVI0Eih$_|Uo5UFr~p0VFpl9#u1iJICkSw?y5 zSZTF=>nL(-d3sp!H9LN$BF05Is&*;XX8dY0Y9ttibE>_So*m((teQ*`=aWpa3Js{I9+C>MK zCafSPUw`J{@o&h}pws_B2PvUb?!f;WXme9p^x_|c7jE*U=*z2rse>XTd}02pzO#pj z1j$Lz)4X{{j+4BX1{aSL#fztd6)srV>!XG{`dq&m9VbnyDJ6?}njro6;)UDiVLL^X zk3<~79Bn=v(y1sdWt^-Tvsu27sGe~ilRsd$!-yyp%kkq_{dk#m$peYg1{%UbxEvaX zVbfoN^x4s^QqM&iLFCj*6ojj`uhFJ;klXu`!Yyhq5LvuHHJxCV5-~963W{s)>wEUy zy1}RUP2Ih^zZFJh2;ZB8ek9PK*NqJc8M4@)b5vjpd;I88oOOb9Mxf7mOjKG>$6$Xk zvY-5L_wPk$QA8#L?PLM%Z2S>QCla>^i*U8_I74#17#5bdljDTwfMgB4*ml;3&MK1Y z5w&QjAeGTa1rcK!8iE6xKY62M+9|#B-hR78dD%xXU6YGr4O#bJ9$${X2?(gizQrh9 zVQDOuI8$F+`{@MA6Lo&yRq4#Af97Ss!L}>ut9H*|ss<-dsULibb_`ZPVHG!z2p6xR zt6Fe~7g=**3no>;+WCKAm3UsOe@K+fff!K)63G%+UHOil1F|}-2yTQSIA>Nk=!taZ z*~|x(;Wh%6eEWOXh4W?UYo5A3d2}-Y?K8eFKzMkM!s8Ei9fdgfIF_ci%LD4qdRcP8 z?mEViDogF{$_g>L$MCq(W4dB9gF>-Ni}DZ>kWiMZez8AhvLnK4-FmD?qawH>UF^(Q`!FbN*4diCV#v*+r66+6_A z3KnNarzoCC%RHOj<%J1CVGogshODl4wIP?%(ttF#ekG#*ERT4b2^34%nfW~JR~pcR zy;6;kzdX8$qASbNwlzi}`Q*kw%A*Ca6O+T=eUMjQwd1-$sIe+;W6zc&`)1*XphlRi zswayt0YmxHGZ%$7es79&J{?D%gI45`?2+adhi~QoczfMyTa{&4FS6MLC3QVn)AWj> z$>}sO&n6Wt;>g|}xd5_y)EV;N&%yYLfhA>IN4)vV<1}(;;-rctY1`ou92c?&@XvD& zdGRZO5?%xfd!&1!x^rQhv(R!FsgI}-C1vGfYS`G|r>&DMu@OGVC!UMAABi|BBc0sr zeZ5;r5%u9+b7MrH^s;N0pHzd2aF9<_8b?lzUlAXoN*BaI)ox}fU&ree^5Fn*s!qga z>R^1B!4wSi^j-3J3`@I^*p8(~6krZanSP0by)67Ka0=x~c})EG>e~!ta4S z$f;n(=Jp|?e(-or+U;}iMY4&(o@(NRs~N0Nl>hQCnnOo?@P3c$)NyQ58UH$LjmP6t z52nx|OKCAF`HWqPG-xS$sww8;>>0e}6tN zrPJ@^2rC$j&U(jU?lRk54Vh_=&{DE9)})gWTH8OPxbL*3&h+5~RzE9i9W18DcW}Go z*~urb9he!C_j;gwyhlqeA0l!^oPqNx^XDRYa14Bg+`I9nej8qNjL`AJ#)~K%p?rbz zzPDBq0Lwa;@xU#6O$v1vC013?^$>aTDw6knnH?nRCdbn=sf}-7idBVEVZX`^@%u%4 zQw=tZ`v0|IGHcyUGNEJX!||T0iLjH?BGpI|R zMl-#uaC`GzLrqu;wABy-5H)gRC(7m-J!R9>3@(@0E=_kLneI5ZY?g{br|3#b<@Bh` z(fc7RC4Ix7|AD7G2F&UaUnb8BZeQU<>bsVZb_mOj3$Eb}4x*G!*TKr((^LXkNrSA= z#q}x2FFlH&35yozX@rhn3408`FNxIJcbyv{A4P_11{<(OFM@J zzjVK>aQi~YMJ9;E^-FpY%Sc$pS0uZ}0$UFw zI?F#k0{Ptl`N?S6;Ex^85%rgtpxc!D3u;;=OA&@U)A|p1W^a|`_*6ZjaT06 zgbmTqCDK90?DzP^OR9!PjyG|UCD7vzAtWUC&6y`2;$>%Z{VNN)W9CALzQ*0_Hhq_6 z{>C4#i$qE<Bh4rMaB)^$Awx3%{;rBcc5%%{&#jC`} zZ4qU>`c9Yl_$uPxc(RAyuZ22W`4?oXlagz>>vm29LJ)Rm+q>Q0wPYm6y3m1=#=Uwf z<<7*H{cMZ*`SvZZKBu}YrGjqusFnWGg|=DLPQOuw>nO&nn(?-C!QXI0G-51HA?pmh z?O_jx-ia8K-=npID4~bI$-bC$z=9k7g9G; zxr{_$f!uK8Lc33H$a#-b@=(5d;paQqBlw6Aay|B&(O;XtFJ_l8_tYrIxM=MwoWN!Z zVHg~>Z1rJhcV~|!?0)78=@mbA>?oadvgBU-3;YSORe}IM_`boLH~#277D|ncJXM#k zuWprY-w#oQFg~5@Z&j&17Sm11^`OSiujMA@Z1|l0}s8I_WpXsOwK+@ge5pSSm2D|zi8xKWk&;o})+?XNFduDmXBA7d@D8+P5@d0fKk zU{#(SC*3}%3!89~7F_gmM&`G$I$rYkQa244d&i>`91+=zJnA9hbPS$(kR84zzQZM2 z(c3&LhoOL|LL-i~1L8xIff^B8EtyPc#+ zqby~Pb@wNqZeK|lIzRI-ll49Z4$x}l8%7b@9oW$R#rTuQ_0{8sgsc4m{4la@l6!w! znwtv*xj)x{KstHyXS`#0wxD06@cw{KgG&2Ob5fQ?P1EQd(^$R78D~m-J}f`_zVWMS zX;v7=$5ycvBsI1^@>6OlC9#KC(g`zC74ZH1;&u%r7|k(m5|% z7(q~#rycwyudGw0T zB|SR50xzD!c*|ydO&+qQU!DdE1Nv&_B|FNu?%W}n=$Lsf?F-^Ff%7Jd-wC{VWrPDz zN{3`;aQ|<7+1f-mAnucryB9=PF|yTK(oh#5tU1_Kup!8%z1kJVH8j2$rcD+rFs2tA zv-9=T{_WyH{hgSLw;{uo<9Tciigf{;XELpyG9#i1EVQ+XKPH)@5k1{zlm8?l&AbZ1 zch*PL`I;XlqhSIMDLI9%ymh(1Y_dF6%`9dS$#fmPVZYKaNF zlPS_lFFR)KEk0JN*qZMJh7HREWdt&zTwd|nfDy|%LKYKg2)H*_HOjIWx72lOnoMmi z^ZVZvjgP-Zld`PRhV~Fo0h(AMA#r;!7x+*>5PiN*T8T=3E9jND*D--*N#6;6+V2jq zjP=;NzRm#saikiX`jC=y?e*K|M3$@bvu}GcyKSn@CSH`o z*5B-7EnkqTrX3tP|J`HPVtG`X&%#W+2M91%HxDSFMpdGc2#$zVxpqVJ(pYq)?=@Ol z6LQA-)rA`@T=@L<+M3GIwAJA$Wr?`z7f?`hp}+wiuQ%lS{MlsK@a&7?&Gi>T%V1k>fzVkQUb%yWP%n0M$6xPH^Kx^a$0HV9b2V1 z4?&2)fP8NhV4VRHqj_B3B1onNNnNc9Zrj-cgYpwuZ7fR>tkxNyi%UoA)*n4FEGf;H zlGZ{_+@|T;#x=sV6^>nbf^A@5E)SOjqE3Nk8Y-%Y(N{8hKnZiD^&Fukx4iZtT z8AvxbF>Dt(_u;Ka4C&V=yXf*gb zFENR;Xj9ti+ugj&jfPVYM-^=iG7i+9nt78)k{idw(9zaPq>IC$Unt0_HX2oS?!!#` z(uQs8`w)(9VPGc204euq_zB-Hm;z9aq^hrsfn`9>tfW_R)sVN0m;e2?b8(Vp;zeak zrW(u;CWeHXs`wp^6vKPMYtoGWnuVZL+lqn0{o@^Y2*@c zmO!M(Yj~!E13?TlMtGF7-KH;VvoJ3|%6U@rFHY>8-69OTVq0UZ+|h2^KQMPGGW%2w zrZ?v=dbHW@NM+1=v~N~qPn8KiKklpkmN>J#f4=Iz;+=6^?>y2C!TeE<-Rr|4AsMUh z<`-TniV+!=C?$G$Y>g>t_t~vc?2^dqW-o`cT`CWn3m+}_VBCFLZQ?cQ4RXn>psq1$ zR}RySIgiX>Yg487F~$h96?xlke`Xx5$Q_f>s8S`;`7G_Tg2GzsOI{-1Ze{JJ=yI?0 zu3#7}+)Z-*-WrNNBT2d)pE4bJ76_h0_WP9y;KHcDW#gCgzE7n~`SYJzeDP5Pb^Ab?G90F;U(IKBsG48*ZO`@0hv*j>RaJX{Y)4fLO-uPB2Uzfw ze$z6`Tf8_w5yH%v+O$c(yizN7JG&+=4$#+?>1+_?p%St-WnS5&$f9Nb`977ANnDw5 zD-*DejKNaE^SDiar?d47nx?S=;!op>1-y7;?qsnii(lrRXH(*eMTP@ zPD)aG(P{?=HC`0O79Urzt2qqUc-jCvXlhjyD85G`z+AReJ9gV~E; zI0jf*6)r*fzdXI2`mMG0%1{@R*KIM|Q<QCcRO}>MmH=E}PWj z)Y{T4Fn!PR(TVJYFE8cM~vj+SEHRGMNvys-8iNE#(2voGN( zyDz$}o_Qp!KAGGwz3b~lVnyA%vdr3+C@5^iHec-hY(A3XTgBMJ?&bPmNM5`FCbKxk zcVI;ptoakzg)ijnG{8V$|CXsS4&eiPcr38F#Cu3S9LBXF`a=X^q_ay=(4@)9MqH2# zFi%~u*6sc#LPav@yC`Ky89@wLabtdP!^Z+WA7T8=5vDK!3Ue=1Nw3mCR4Gc^?(4S8 zWfS*ldphGRAhon7EL4HAp{VXjGebdD2O^&d=T;kVcy9sNCyLcR|0MM}ZXl^>sk4*V zX?ot(yyc^|1FeM6P8YWlNmNh<c0p4M=qDv%5XKK*)$t_&Yj zi1`M(XeOCkwG(B=fX`wI+8Y%PY5}l-S;Ir5 z<8Ub6pbw|X>$PMluEe~SfpMCEFq;wDGYJ^PpvHH@@EKw-qsW`2OQ^kt<>hQp1-I*B z<*~%a@7;WK7=QUCCY}!R4+d`mEM)*J3m4k(MebeI-&&Y_-p6f}bZubhrBX}7NMio5 z%i1hOXuR7tkXF)cG!Yw`XJX0w-919qQZj_MAC^&3#ahp$ueK%Hl!fqpqdgap@1b}~ z>anAMmypd8)h@nW?qY7|j-tt!_CWHTb4t}TY|}%*eb(B;KHfv`P>sBfi41;L5FK{V zu6rv{w`n+?T34_lc!TqHx0uqzsaq$dMpDbP^jiVvg$n~hFriPm0+G=HbzoKGz->e+ zXfj$R#}Y!~=Ht1ZQj9*SLWZpMPRE}MC6{11Daw8}rdg@$dqMy*kcRv<91JOy+Ix4C z3a*?B$uhi~A;%Djz+E@?H7fJI6%uW@7E z$#BMQtJi7}B%pzAY#QMx-wa(9@h)3ok6!Ro=_L{Aw*BsWefjM!l%-yxwH4oN;8WZn zO({Jo&yp~_%dse7Q2MS3_(xi3ZyAz>39t zc*R5{XejI$%yI~T<+iVhEBt{OUAPegRAwg-p6RSi4#yBA018W3W|t&9+}+A1i`FJO z@lH*fKFnX4YcpdmQ3Dby^vSbpySf|U)v+q7M6te9;c?*49KC ztWGy&2Y>#Z|Mn;n9wZ7o54X14cJvY^z1A~(%=6k`H1MX3=YA+-0qg@=8)tk@1xzA4w=erD9kU%M6iHGzYyJ%(bkTUj#7j-cv;--UC8>1reXW2ir|E4#thzM6j%ox?71% zr7VYJ%o2@q0m(X0K}FnSIFl4?_dLk#dyelY>yvCT>#ZRXp-Ww$1;=oK|AMF1O7@S= z#lhyqLP@I$!6fcmRrFu4^c9SC8H`AA$GHY;iBjaO)WrFHGh^J4?ox!L@@+8mUVT*1LdB5NfMg)0LAU^KAw{5>E+TqKatW_s!@Z&y;v5fTrZNNI@)7Z_$3T35*dN7Nh2&ExXkdfi z!Dgi(UoX{Bzx8o_o;zvX+UHZV=}RT{)kTB;P+J9}RVBvV2RivPlmMA8VT_g&f&(3! zByEPhjG>#3Ys;awQWk|B{Ny}9AmV>k`JA#;C_mV`spq^&lEXM-w^Ufw1~fZ+B@@P~ z^;Eb>DR6P7M;&0g`YI=YNeqQArVr)v9zQl-sfWF~8sz1^(+=g*7U)2LMIhvIGct*U zM36EIAjGr=Bl_bgav-Tx`K)a!0PYUV-o~byx)2+IC#Fpi8E0*Nppm*Ds7|9YrIV0xNIT{PZGmp>*e%nf(R^F0iR0%Gnq?LOT92iT>tBALQ+7e_%fl0><8~JvJ)VYfihI<2kFuyF2YB)jgEk@i(Hq zIM*FcDB_k++ig8ww4Emiefu`C=tb2s>5~aV@8bG>2?@;Be!ANX(L(-n4DWHdIyX-YM42 zu+d4`@k>!QziX*y$#Df9b`4kz?(feWW|a1{aSS$=GQSY54wTObz{7f2sWYh)bij#Z zrGRj0^xPA$$?BnSW=L|N&oP8BzyiB(;0FT@g$(jSqlPR>dcPnq!*9KOWzs}CcqdN0 zG}!&TT3XtsbWuYwL$xt`2-RZHqVQK(DBGqF6yx9s;b+a6nqpaXFVwe ziJI&|L|0Uh732`ElI-sAQ#my&%>ipHDd=kB8dM)wR(;-*XSzHXm6@GTBKQRZs2>Tm z%buQC8(`#;;h>LDxF3={%{A`_yntbGi^h7}7SIQts1icuc@|Ki&Qc}APl@!qhJmaq zdP#Un7W03CJ9IB&TMsxb<09VB8Tc~$-ugZ?cHimfn|rjZezKvK8ij!E0V%I zmU6L8VjM=RjcV<_KFeA9`H!=9VJq8q!Aq6x<_%@uty8LxmK}h7b(_nsRs+^oOBzTT z1ybqSj`w0!?6y9TmO&wh;Vy;Db_zfrfi{xmnlOGAIz%Hik3mGB^%dpO-~Y}D5NJ*_ zfIPz-5qur>G{{tm7e~?tzyl%O-R#yo#RDa*)InmP_z7fnfwaq?0~~pR$One{P)6DW zsIv71<&##(DqCQ?``QWP6G#T&r~~&79;N|Sf9Lb9pK+W+%0BWz6brC~1M_2W&P6j= zR`(eIE|8IrNZK+=%?1T?*X1G6eg|;xIAH>~>i8c!MmRvpn(&4ya(EaBcMqt8wIqSH z*bIkym!e6Hyz00L=w}Sd(AH@R9R^ztT$4XW$Av4GR{73XGFqppMZA1@V!oEOF!k64 z0WkbSFf7kR=3|U-C^hdWH2%d8ax_$iD_p8-HfQIC+EsgmER$VmphbrffVT@oZ&4lD z{vQ)lzlYOYEyI*voG_d%*hQ>0X4y8Uu9*-d;MlFoFw#@H$6{br0Co`2laITO(gCd0 zO5V^ZF9_8xpom7iyEpDo|NT!X`M#jojg`@Dmt%XBo~l#F+il<$BgfMf%m66UOaqFn z_{DDOIKr`krgUjH*(=5K1K3GzrnfpwBmDuZ1Kc!Hz9?;JD-9^hPae+9>{GA4J#SGS zuG_T&Y6uZ>5TUu$l>!fkSiViZ35gFl0R2f#24GfM z0nqfMF++LbUkfH7dOE`Szdgad`(x`ZqX(v)kyN$#XdRJzR^d+gRnh2o#}Ezx!?^MN zRTRHva(^o-{B5{%3I?@nd>NtN&6C&8RVjf1Ci#n zaI%90kq^QLLj@%h$k0nX_@Y|ya^;^&!jM7K<<0z3i~0I&l5<*Jo_Up9@QbDXlyU}+ zaQ#Ko2N0D4Ay;#uei(oXM1a&xZ4>uw_5$F>jb|jU|C>rVPjAg?MU`JR>uE|&UtfGZ zC}g+Xc#-&0jmr9SAUb~lu%i_n_Yn!}1E02M?3a;47B@qbiuHHcLwr`egqoN)@3+=p zXM1Cq$7(uPg>yB!B$73y0idno4UiAKm()I>E&%1>)m9sej6p4cP^Oo^aO_?}{D52k z3(M~@5kofb`gR(;vX@u#BA?9gl;5E>Rm`7$qEmSZOz_?a2yHg1EVAP$P&Q!Z6mr48 z)-wNtK#ehi?B?|czxHkERS8WOHVv5frqn%bE1QN|Jrp1-=W1jOn5PiRRDXZ6=dz9f zlT?i%BZ23C%C!T!QkDBrm(@$sw_PcxD+o!SZ!29o9i~CRuEnZ|1;CN!(C9-ogy7-L z3q!)g5UN1zYK6J}i}JC?J{8AG#SILG?(%jKuJ-Ci-JYqq-{pQ2T9q6W$1q%-3WlEp z^1!hE0n}Jv4^IS=!mvlN6j_{dl-_^ajlzYsk@K~0BFwfTrgf6c`wdc-{XT$0x1m-2 zo?qI_i2z!C0x>~ufy-fh?EngYU^tg-E>1A%jgo?;T30|wcAK8T)Xj6?1Z)uQKvxBx zF~#KNbUPG6P@h+6enK}Cn$r+6NH+0lJ^ulg(^S_*8dl#0%ULNo%+m@`ZFiO|0{Ze-0xf1n^;*5NGu5qBE@hWrI2fn6 zx-eQ!Nu*mpZ+-$Ws;3DUDc-Dp2SXJ!K~M~yO}BwKprQcGHX|Po|0n;3-A7F7M6(DF-#6hui^09l=GQ0GCGfd7?}=uxHP zpAgC@nW=2`=MqKXiSkEsj1ZnGE3}#}^8*t&Liyz<)p(D{6aciwC!BhaKtb?={PXHw z?f2h+1ez5#Nz9Wk2qfesz4DW#@ zp9bw3;%``Jkv7ZUFo7$sJPd5Fxz8S@*E|^B^7-FknO{y-o3FT5JuuQW^QNI8g)oQM zZs}>tC<90B*kb|+^@lags-T>mqU3v;34{#lR{%3tI^!QgUo@}HjYedZH*)M;@L*KJ z_TVnG9xxhK8JK+cPV6=K`~TebvwnwlgReK7%1!0<`5~#{2-uy*coR<~Y!|?Adlqqd z4~8)SOWmZb3N;~*B`!M;dH=$6KqO>VpD zH0dIq%Ko>#&?MJ7fb{1|SJ?~)qkAxcK1X%O6R0xX8WO;HMX}dBEdhp_vnozP!~9h7 z;1%N2oVio;w+DkT2O{P5>zp{xsLRjFOS`@e=z1VT?(AF4tbfKlc!ES`?6(`qP(-!5;|JyfQF;Q*sEUMt~W^^#rC-$TA3%9EBidK590t#mDcI^ zs!ltBk(5rsgv1Ne)fZjCcFy|NyRL-OrpjT&UHWNKKq3pr=e@WK{=eQTpDr9<)cZ-+ ztl{f>R!+{(77D=70topFzbycd)vG)))*1d9B9vx2dC0m(+ynFCrcS#+kHe{ny*Y}_FfH1uM0`( z67DjycKbijWCyK0;L2vnaoVC%B!$D}k5e6@P8{}Pa@wpc%QicFu;=pH5LMX~k)9;{ z?5VYTkghw*cSF*klo(GfCqek>FSuI*W^#mCraC1&>;zzfbDn4RJTDu_mb+FM0S%;F zhO)}NCThL7sqrnf+~173T+o9XW(o++z3kFIi2?l)lovY(T$Vk5KI=A;cr<}9Y4odr z>GkW^o$j~(MQ;F0Ak!9clj<}01W|*B zvIJK`2|}WmtP5CI@L-4}AQu@mm2xi4h|#rgyFWLxi))Q$v+RpVN7D4|J)HfGBoO1k zJ$h7EtqH2hK)q0-nrPEr$ua|wBF2Qx#t=~JB@kV+`Ba5F+f7}GDf6$1zDTd1;YhU^>U_b z*H%3I(tEyRpW>!C*KU@SIe6v|+WKQEbr8d0y2&a70{9ewtQhVF?)8E%fdcXfzT`-x zJV?ac8q*jp%V6#09M0dI$`Rz;3^-+Gql@#Kkp${wz0&Z#ZxZacdHpJSes+oeFZm`; zhSaR-O?F(v!kL}Vn#@ZvLvbR!ST96`sbwDJk)!)ygWLiGBVS%JOvo}F^SXmX!#gOx z(ruFglq?wsO4RO_L-s}|QDpbrqKA$8RA$pk2Thgmf3VyfxVw{jAthScaGW?z}YvFt6}1W?q76f^&mWIdmO zad277ThVbrGBW~QLHZg5xnC+|&~fkoc>Te@Oe<>Fy6Tw+=aklbIMtNc87!&a;Qt>p zAh-a{FXbHfqY^a+vwZ41J^{sK5b5A?c)|S@WJ#6A_QI#N>6W(4SF-v&JoBA6B5y28 zN|OJ+YG@QTU_l3*P!$C11fr#-J$p{%uPr|454KkQi|{<|6n>&8MTqoLowMom7X7!A zW&~PgnfU7cfGo-ZB7tMip$-RaVrr4wgdT4iA2FIjO|76g;83&Y(;i0a6aIs7fSI;ceh~uJH4F5NQBsH(*o&S7vLc z)9>a?-m0EKP5&uV10IQgFMk^7>`n{|P~j2+6!wx&+YoX|ptc?4WGI=BLjYP5{lO<# z>!z)roilveiPL2F_Lx1@%8OHxf3c4DrN$+&%5I#)Xfgg?G*1^i?YuyUWJnuaNaTE@ z&+6$gJXOaw@WcEoN#2rylixoFFNJ&~#X6}+WKv+UG0&a8BUuiS0k+hQ3vI(w9g$=` zT5hmfu$t99aG=MS{gw6q1$Q#&+=?9S7zcD!P~Siyznu#1K?EKOD9RU@w@iEnzNqs5 zuq)i?F&}CGvAcB>Mmt-b)P|FCuu8>$3WE`_f2O{BXjm0ci*e?r#wv2Pc7VOMR%IQ* zmx|Qla<0{)8%#`{sjsq%`ok}hkklGFuk6`$e_<;o5M1@j@$0ybum&n@C@yrblyM9C zk`x641}!`UmCLd!M#66~gtdTOWH|P)CgOi-UR6+$K!Ky1%8#-RNU&00UkaJKUx%Du zK2j;bvcKdT?BPa0CApUDSr-2Z=ZBC7J|VCA5_JJ;E}z|rh1?p{R49tEO%e+&!vd1U zMrM0O0{=P49)Zb03wk~}^#PQ&g6K`-LuC0XTn_-)u`}_C56AJMe5gJe>XPb1yEwZ^7wVNb@t*_k`Q5{ zBT@ac{rxZFARRf7zL?I>#Rz{8<_)#I%esQfE71M!>h50FND&9iBP9v+_k!u{Nw*=H zFga1RFixytV5e5{#W2sC#7M3`BykH}jQh+4?Pme2_Bo@M;q=x)eqy-h`0U&1cEelzU{hPKsqD3@8EMcG(?B;*kgkztZ+%ib?44NQE*>Mf z`S6j~Ea;;A!tVyn^RojS5_Esr#3QJWVh6d0;FF-~zxU?Kpp_Gl;0yyb z?JV!09xA&};1mm`E>#00VI~(Fzl60kNnS|ZNWy#+ZH&b3M*zq3215UYqdIWfnC{>9 z2PWHP#5k&y*AD^1Sa3!{u8KZB5)k#ypn5XGFi^nIr3x47V>JHs9NFT!U+*>Y7yjNO zs~#vD7!nZxMFQbnprEZv;DRV2ksEn56AKGTR#3{P)JAYs+d^KaMslp+9_$gqsAVdg z(g}~{2y?g7MtO3c#G*ncONhbzX{jQ~`=~-Exnqqu#3FFiRG?q{QuOp!>|u?$0l?@# z0WH~ZClm?LKspKzN4OwKNF;I(hC>xgcSoDNjF(qM^98j)grO5H;eK7!BN=6DYdUGS zc$Wobw#+-vsVBfWHV&}w_h7=30g0*e(4^O)Hp7mw94J=t*G25N9)e7+@+B0|w7-xu ze+?t*+uB+shS3+uMtGw&kTqeDW>ToP!{K&rfsmzY@vgH1a)bGJ5B_Ums4m6+>%UiN)SU@wK zE8eR{{%kbxnfN+nAt?NhzK%UC{|YBd=r9Hj`J13>{^K4?gC0;5Wi=gZ=pRnK-;Y=@ z$@bd!XW>yO!(R{=<>nsxu-ee!&%S#lNqLWkUz7slqmNgTjFS|s&tJed22F|`j|Hh+ za)A(PJj-MB7q}q*gqQ*H&bX}!9PwELR|d_Rx8g6v-KSlC>RUxa+k#a+QCFF*zW7~P z9^~a97S6%fyhJB=`F6}n(Vr(PFb9QSjrN+9=P>}VcnCGb#dfI}{gpaE6jBu6&w)j; z1GSM{^sFDbl|Z51M#GS#Z>KYDi?_@~VnQ=3mm+PNts8TT9wTzFFj5rIm!&qH>n$3K z@CL0)3u)?6F_Pshwu!AbwcKoRwS*vS{1#aKi_oul4^~=sNj(pwq^<``NR_GN8+rky z6DTUUtcn*EGqik84B2zMbT6zU28Q#+$1olvA%2I8l8Zwqf*|-hGI3r5Vx4M{hKc`7 zbre7nA~+!cn9i^F4?2((5s|wr9dO%5ZQ5JbD}qQw%pkUk`O-6F5Oqj8Y(Ct31M~wm ziFrM$>b&ABO%>BzH_7bV-OF_v^8F`(h81tSK=hZm-QM=WrQx6~gA5M^)X1tz;9T*^ z;GQUfd50?D2xE)$^T_KWV@l$%>n5^lSHr|NqdE8^q#jbxNQhEIe9weVRvLnG3ZrMG zNq(i~Kj|A?739MxymiyGP}Xsg-6mRb$pX`(DU>zHUXxJa*uBN};8Ocis>@HR`pV)+ zJ8%3D=xJ-2d?lINfo%j?@x{hqYmuMT=>9Ps)-UG>T}FqxE^^YPWFvNRs@iV66V_sp zIVFD`N{~Y*eT2znhvVYJ_)b*P#1#ctf32Db6EJzQJzAE8`7%ZnBAl@gfFSzD_B!kT zM!t^$(`2&&TK9!VJ@r*>YMB#=ux}}kLXQrQ4Q4?eOGJpZf5gDV3EGyVNfAZxn<$r7l|Ci25EBlA z)(u*45G`MPR9mI*`jpiRY`0J7f%XxF-jCx*48bKpG=%l!zd-pg6il{Z`HT&*hyl(0%*~qTq5X-{ z0a@{ESw7=$NCEHttYyO$50i@yZ=r!_RoU^~8mFSOSl z^&HrBddTC&{(=cIPcE>8rR8t#pimx16OT)a;2C1CY689Hkuv*J+$+8Tgn2H|)^i~t ze@F1;?CZ{`HslfBHFld~z^S003I0YL^c3B(sBuzx?iC zNJFS}Wbq`e$I-R^8ZJAsxZj5Hgc|VZ%<7=SN`7AgRN+taV$t_se)R8-+pnW2!=ThF zh@|omT)aqKK35F)sgAfv?f*#*hkT)gYf#DA5m{Uh1@`$KfjR-t zYltrhL%JYt&X`YNrzy{k=|HvAGaYMAz82UYp^JJugkcQsi4(&Y1-LHRT2y?lmXTKU zoQiBA1{(T2HNR@xP?w>tX$2QI8v+pN=u+Z{gOzo zkTB?$kp90pJ{)qZVg;6g&FYox0!Zly(ZFZXyukSe$pXtanAq{F{gS4(ze*o$K^3ZF zd?}QscJx=r5a=Mp4+3^PII{Y|7T~i_f(^ z0EL!#B#c!n(jm{R0_%C02N2N7p=x(zZ2(b1K(KXR1O|iJ5s*j8SGc1+%hZ|PMbZ{_aY5dCI>sSB-TpQ7+_pleX3M5?tYbH2O z;Rvz8&x64%?Vk&Pazc7ASd5;>_oFrh8Z_AP!1xFIgVBP)6rM6eB@74BYwtrbRDxBL zk-dxzg0bFP$~)0|w7ArT;m2~oL52g2zz&7nC97`#K9=WW#QYoT2vai_-Y^+x?OwM4 z!D`{n@xDnnEi3$e+GB`M=;k(&)WOn7=Ll$ac_3;!PUnXF3KHZJ!x+I}HczvFvtxq` z2ICr#|3iRdVJn6j5FIEacF2ROsigjWeURGmfkbQkmCXD0@H#ZWRHNVUk6;0Gtke8h zCS*mHs)GT;Zi02y2D$#JV}9xCSb74u%2HV8Pw4spW;b~G|1S&)ng53nt0*Wof8b-N z6oK+nY7c=E%qT>9Br%Vp1f!c@dq5>nb^z={g0rLo*a_C??8JClLtK1K0H|odw5~U!5;neRcdtbP0W*+Tmb(8 zlfzKZyCCwH7aoY-y@B|Iij7J>8dS0%?53|_7HMUwZmMA!8C7Ic%duLopwKUoVKrJC z*N1<2kI&B=ec*HGSdthaIbq0=THg_gG&Yku*H`z%Lx`|6s(Nz>k)P-S0ddKcZ_GCzqGG+d0wC&l z;wXLqJY|m;hGN8XCtTaZo;_&okA1I^I7aj5yY}!i07rDEP_dE&?Hh=$Fev8I>2i%x ztderR@ntvo`oL?`k$Ay+CEn|rl{C$hX!{2b9<13|`hnx>N9g7Bv_~CB)2b`J*lhPw z{z)wM8ECFMwq>22t|a@nB-PLX)2SwnuMc@;#P9pV1UI&&%njW%Mx}!(FY&(lyi4ML z@_Uz6LNL9c`glh~aQ@OWy`;Y75l81HZg0nG=?=xpAezr~!8c1kw9&l%ld#O`m6uj; zDw=r`9882D!v>%w$GOXT@Z{IqoALqN2Z9MAu;Im9D7^Zw-e`+b2v}CV7YTV`&*TcsGs#8#nivk}iMDo`QaHd;5$x z;@}VOr2s$5X}H_&Bmdwz#KidE%8qCAXvK5b$zc$(gI>b!aAbKu_pQ$H*;Uz9VtUc8 z$!4+a{fzPeMQ)%9`8m0j^)#8=Z--fv;$;ROR zF~T$K>l>SdlqU}Ua6JwTm{v=?&3gdzpXj6m_r#PAVA}UzQ@(rrGwZ!?;7-5D2*KIo zsf(iuD}40MDdMTPh{J-seO`u_6jFAp$h|_*OzNKU(_r#jU#GO#()RcQI+B zm4oFTy8R*Ux`EX(O&UX!`eqd&1)FNKJ1;ZMZgOp=OESpt!u;*z8zcGEGDwNaj&iEK z2vSN!1xGO*kXbPt0Pp0pCfxrJSoX6(nyuA(1p+V<@3Uz{U2%Wva^9?NG^`>;ZTfsfOMr7xf zE*zl;1Q{-%?f^3KFu?;&YtILmiSyZ~{hM|U9wS*9o(wJ$$H*l7$c;|+L;rxnh0$VK zqJ6syjmpbD3iFBezj-fHWA99*4!i!z;aaVKURc$v-Hdl8skNqEg6?8jW40}Q59R8v z$-}^`+2?kx5%+0hwuRL;i~NCH=M{QC!f!UM_5@8zIGF94o4iRTQBGXCcuwPj*s5^g zvsw^NSdJcr#iu^+f7IoCL9gBJOtz`mic&b;q_?;*`Q+y`-k7d0me4xjvFS*$s6 zAhZ0HarurNZ^FD-fc>>Atjg^}R(N0iJBVvdCr@U?@jJDCpV~D|l(<9vINLK{aR`q; z;Hu-6%^GP(`m(%}H?LxqKVPIfVPpWe*QsI~}`3 z%3j4%7A=?|ktvBGudq7P<2`HFOVL%d+wd{b`I^(ik0|TJGdZVYne`<_3`eVIJa~pY z3N~+1wMFARj+Kc~-UvN)@~9aFCxes*C6-FK&>_te0Hj-ge0cc}aDpGd272R0P|!(i zI9V}7^DoZ%M&s}b_|lDp&8^L=u-Wx{ZCMSxqEkqz?f3Bn>|O2E;Z32C%e-QCNbR$^ zw_*iGSLJUvTdY?}S!Ai5a#F6S!MHYD+Rb>|$A~~u%#najfyP_CCZ&kgs*zg$&9#x` zv4(3g;#tlG1*|Rwl%eOwbQu?VR=N4>Y#XA!s#>pTiACj;3;VPyj?4t-Ehu)KA6l!F zSh2Fa>+2m=_i(x_VOq_n0Wi~WhX&K_rSUYhYSoP(v=u8@%}q;9ZYL(oyLbij(`)kt z<2wyxn|Y3kwTw7?XU@$%kF+>|%YmG&p@Fpdh~%fX0|42Bz?Ss!{}K^2;3ly5hMi-~ zDCpnrLlV|9D%8Ab+qNM0;W`t=bW*gaU_k8h)KfnlIi_k230}dkRH4n1n@veY{G*Bu zUtC$fU#he^`<%mPkv!V4e}E$9aZ=27f#t0@fv8OPFYgtKc4?;bmQ(!2uHfNGJLgi? z1x>zyRl3e!d*CPF9?hFFN+?ShY93F^90mCQ_?faI_pDM!UtfxrFEAR%TC@7& zbeUePUEEA#lPD8N4CE%Un0`D zJ$PlsjPKkGZRrkoy}|5`O^j^XyKNtW0txHGyjA4@f#auG8c3bI=^;Xo0;T8upAkDyynD1{WhSd2o9} z;Pg`Xr~#+!2}#w9%yfZw4|u{FGImD8C6PBEAEJHFU;BZH=rWtBDy^1zBAubxQYQOB zOSB-rdNklB*WgO6_S`lYkePkkCGT&jN*-qA6M@s^e(qnt40 z%6QNt(|-vsRZJ#FoFd$AHj!a7UKlP$^)XtI5uB=_P1;c~D$=zG*}y8Vm(AqbAf;MB zsXJZAJ-++VhfcA1M}IV|>XRG}1yG;4%L#qiJg;4e_3e1USxANve~^4GL>?u~haRgs z9@13vft!AJIRbj+>7)i9l{kyAzP$>#ki;`zywMMJY~WiiA+MZbCZyMyZO5*VC`GN{JA$MB#G5pTAim{%&OgLIsw_~m_Eh#GN8g3iZaPW{ zrR|8BpDMri!_7#O?cQ@pm#y%^b<1n7z@clKyX@rOyZAzRnGA-oXe7x(1w(pIyspfg z-gU$`0*Wn-%FU2^a@-j<5us8p@^D*ydTQ3LIpK4@)Tj48i5Ir}P6YSqUop{^Y*~u3 zS@lqD)3pP}GXMQx~Ahu$QbRIUy4!@}Kpg04mXh2eKGDz9^h zcl)pLMTo@-uZ+T)KYgVaBe;vFw5=+3{(#f%;hN~2`0tYIFP{DkcsmRqvYW!n!i)}( z3#S7rRF>r6>#x*tm+HDk$743D9WSL#UltRXRzp~zQdgKE5E{k)kZhbY5#_7QEP;!dhXPGkklcK9dpz~?x=J?0_TqI0i6-RR;j3{&iB?EVs z2yZ{hv|TNjw)OhvthN|fdI6gxu}dG%I>%~zOijNovm`%4eOfZPg=f=`ErFqSpgqJzHh81~V30XthD`c^Od zxOMGp(r)bJRuwJh;+o&^FN%#)gtj@lzs=#^?M?C2XY_f3;}=TRUi6XV3KK9Wdc`gW zc80dZ$w{$i==O`Y0u$_YQf5_gzsj@;r05~PaJsMbExsT2ORmQ0>h98NwTXC(6^#jI zKY`(D%__a_1-CO7SvqZ%7^9iY8b+B+OpR}=HPkqJ(QB*snDU9dX9!J_)3?ifT8d%; zQ#-v`gb};8#pinZC#eRZHm{RU1YKZ&L^(q9_~)X~F9(qWtiL&Z_ns(MuY?Y_-Pzn? zs`|=UL>=LRw<6esbeGozAp@ZBTr9P}slsN(%pJQ`Z0ZR&QG{l)$RkX0uY>yWN;; z#OS0^Ju2euTF0qU6otZ=aVg7?`ZGAi^H6-|7^?e^5As!bV_dkMpEtS69buEjwPMwg zs_Ms+M#=62N{*63nJ4yxKZnjiaR$WP*9i`Y*?OKc@1%0#pJoI%H6j-t|rBvthJ3c`nO{fCAb^@07$P2sjofQQ;7&sAn-55-mK34#l6^fIS^P_*@n}( zbWbww>h&NvLChplYb>I)~v86u) zhAtiTfj#rr(8`-)B`1%{3AZk5rcTRQAMg0OJCokI(NK8y`eTUjchF<0Z<>fd{7_p! zER1KM)=?-Wp(PuYJ2U8Xo>ZVfeHL=sFNes)JrTAv)f+XmoM6$KtY3+D{!CuCYnU4> zjORMet>X$@OTH|fWfpm%Lvm2v^9LcW5ngFJ%Mm7mflBVzS zS*j&|x`tzOVr(`mLE$1Fco(*2(JHs^30DTwMlF%7Ia}uq^2yumtBKrr`QRBQgHD}} zv|0PeM?e=u+?{?d9`{%cxT-mNm+eTouFhtR>)2&lHqcdias}&kBxUOQdpNl!-;prU zS4@+=`nBW^2`k#3M7(~_-rfbfDt#}s2UkYDN0SUu1|s0b<3njNzzEVmJFlCM7=OsG z%J%l`Qar(l7yKuEw=BYIKJW}Iz-Gym4H4y6FQ_lvsNbeWh<2mTQ!_Qdm*z3nZN8)T4|~d`_JwZSNHdBzM*`!1Iie%}cpM-1HGp(vN{} ze%bN)AWr+1-I-S*qc4U|fj!+&{=2Vs+whM*r=i+lE3Y4ws0&j~;}nB8MFhXT!ssa! z+-Ku^JjLFgE>vpwN?#i6*=))z$3+~dLws(OWZ$3PB)pm3#JA4}*F!qZUFbIjLWWCrWJ#tCDT67pqcMK3}`!h4+M-ZwjG>7+jDl5*ipnRLBzV*U@5 z8GLw3`Nncy=2QNkxWdtZZ7cV^Ch`${;<<*^WgtvF^I|qxiV`qgRNR*)`V_(&)<*Alz$!`Y%HLb4v6h6G( zTpRgL;~mj+q-+yn7E+qapX9u**syz4OCT@OK6X3~Bl~m=F}EqO{JyK)I=3GVg;$*3 zN---^t&QE~yIONex_>jkQz7xpnSDyJhVmxmVUkY2+O+#yPAIZZ5S5hczx-kahEJaO|U{UEre+U*mtP#W1TvIGP1G-KVq z$4nqrTW)f&Oxn|9Xi^{yZl;I5l0M*|T!WnL3hj%PZVUeR;tmiT6Z5Dbz+C=`H-;gwAc`zG0%crhFB z`TJtLsOHrqnjQC%b(ieLaG9MJAtO0F*vg?jOtVuk=IS>}VE#d{m zi>+qDj`_veOmC@P>x8hOiVb2}uq*h((}4KMqkQi|+yoF!gJ>6)5W_;1qRSw@NC&OQ zo(J_AW-K*Inj^%%S26wf`N1BZ4=l-VA3RV0X(Am=K34?jW=sDO<{kjaekdV_uM>}Y zIMH`blw*It7a3lxZ_qnkcj*w3>faY z)zkNp^y%1LU(q0`vONI>pMqSuEBo`?+us7?$r|+%-rAq~(8E7`qY_GT>U2@23Zn z@e1XO@lGKjcq!Zad!iKID1-YCNd*>Uy*zTc&`OQw@X;Te!y1D8Yioxwl6^w^2C})1 zXwb}nrT(Bo@HcQU#cwo;m5NeJM0U?K>_B+G#e6@Wq*tZ^n&rOnd6*sSx*kk~a_2<4 z2lD5@yaPZe3zj}WCISvZ{uoPV>bZU!-QFIQa73=Tdb#Yu>Ust|`lJk?d`r1T5He5B zFIK_ENXth#*7|?3_uWBJZP~kus0aoW5fM;Cf@H~(g9HJ|4X7j~N)`|#2T?&JC^_fQ zWF%)$5hM#VAQ{OtIcI$Pbl}dtcV_Cns`vZjy}DC1*36;1&pCUq@U6AJwKgzox(ho- z>!?@kf%0HOWkwU27k*+6w!bIVO%i=~>>IoWtqLI@!X;0kjiY<}NipU;F_2=EeT@y% z|5BP01wu&UP3xI+w4|r-L8*c;D)ldW>C7l1@~tEm@fQ8MaQxRYPu|4Kz;hJq(RR|E z3#`CHy;xEj^b5B^27m~=L4RL*wB2(&4m$M!TolATfzj15oY=RD0?Z^W(O@GT>}JR? z>sJa@_vO8((ZBdQ6=>CuXmv@fk;eP;-;Z8Ll!j1adt&=jQlOC56jtVb^r7+%N(Gw5s1-^Tix&p;Vz?5E$xqSa+o z0qnpbA_W7w2t_FEfePX-4gTQxiyBZIuZnMikeW+BB9L7IzXh|hwPQFRKYp~HT_twd zzWd-3oqSfu9h--5po~AySF|^)g{1j`kjIM~XbX53?9IDd-2XuRAoy1}bo0zxteuR-6R_V_Jbz#Vi){^;E-5Ab$^dGj z5e`k5oe{|c?{cOmB0&fJ=-cX`*5T%oPJ#7dcu-;FGatwLQJjep5K$3Uc;ezvjg2gO zvVzwU(Q1@3US5^g0qL83KjiVc7(I}_JJ?lm{{+_2cO#Vxy)~~S0AR^$!96%KSH6&b zW$|PmEiC$*b)pa;QB66_w+H_rH<`rJweSp%Z|wY9ta7s65=}M_PastLB_5)v{mJ(n z##KS9w`fX_FphQDVtF~M66xSlG zG3fQ~GU<&!WcAR~%X)6nWpAMEDRDte1~8ER1RlK@d7``xnV@f+KhB`v@+>49cX_cc z{TPnuO(4OyQ$6I-TRK1eFaUEMB6%e@F8eknl)oF$4r z3)~Hlgj5Sl6bpbn&I1Wi^v%R%z^mMsqw_p^IROZlu@Pf};iEUixk)%+%z%7^yexg9 zgSj6oUPO3Srh$Nv(^iCvyyfk3&BJvsll7SuH$6K z`;+QXz5tK-dU22)!%Ns_9wmndfn0u?s^l=i z{ucO~=@FttzXv7!K`0ek97dYPal6dvI3trR;Im&mWt!J zVt{NX<1Kgl>mRFh8LZOH%isQDZGB0e57)*!656sNh0kPM6=p3&mk8V+;U>Of$f!}G zOM9N^^T;s36$98r;1I2#h}1v;ExGmp%zxvclQ|OT1aN@{VHmt}WEoK(yh%N3a>DZ( z*mJ_jXKyiXG7fhg+=Ra1t*SDu+ytfVLXVbkqY^V_Hz-G)fM!T>R<4{ zvEw7a^{rIVb77th>z%gm0~HUTOh?1HS*g^%KmWe|`_-LO-z;mo*(-r;Y zSGN`2&z%*jOJ+d-Fw<@Dy;nhPA28pee0Fj>mhM^FTYvQTM%BSnxK5@(6$zBIfDZtH zSU~(>!(PD%5%-DlMzI@Hc^T#mm79te zU@}Nx8MnpVmmHkG+^0cra=1KL(AiYq^GBsdA;6CtBa&n|k}vd)mc5RVUENQRi9vtw zp%nO@?NsDPtne%oEDI_uw_g-Qt7GjApkRM|9E2;&U@y)b@+Nwre*hyx?gl4+);{S= zHyTA>29Fo-qi{9-4|3_dV3*kdAcAh`@-dqdUEiA)wlw{N?b~NsmBStGg&qu}|9|tLyQON%8+lXH{+GW%x zG~?%ae9l+Fg6L%c&57ggC_|ZJlt_&d9j~5VIddxn{ePeEfjv!)V&^*|4gT#7(R&RR z%}{n_5&;bsF- zd}U`ca}|A;R4BLR+$AnTvpx0tI!c%9S-{+P8^)?Y=XN4t;bF9}{5^~A4HAkW+dC_N z2~pi|0HGX}(#$}8Z`s3rm){k+v}1J=kPbseBS}$s^GuFX2N@(19*UQislCUfBNW+J zCLeCcHiSiD;{Lz0ZbxOowpJsOvFQrTRveFmq$({MhK9!OU~Y8=ZhLSAems;V>P(Y0 z-K$w%ULKvjBZyMVyew2YqtsryEvAMtqQJ1rmNM&7$>irP0V|tvOfswmVg}4}-2aUg zV2@I;&hMhgEdOGru2OdYHrc=o@+iCPefF^6~An=#d^`%yOG&|En5!w&@g6~(4gkuRvi@jw%x{QOBp<_6w z&VaY3e1;)2P>Ms=&Q(_{CK-i0Wk%DyuA{czo-+OmL%>&}nNaGivExsCshr!#Q6yc+ z9O4m!B~4%t@;%~)E<-l$rFQU{5tT9bq5RxGoO6)eq@hCjumzqOA zoc+yZP}&RRHRT@S!OOHi-?$|ZtD%IZ?SbtEuP0N``0(iU+M(C8kdXe1mG*>8+nu|u z=;bB^lc7{`_!9u%$#&^5Mgyb-%WLiYd(gYHEH*k{wib9tn!SS{1vnG(z$FKPl0<5> zgeO2DRg*@A!Pv9SmiTYos2*8Ryc^-+*BT&{sa%3T<^#%qD0DhSI*RNk5 zbL7luTR)nD&53PZc}mQ>qI-GTDYkTAg%(HcqJ5TY`(uMVU!vm#KxRJu(C2_*UykxE zuy}vvoQwPb0(|oiSM~%SY>Gz|t$N%zxHFByrBeS6cJ#YgT`sjAFFuf)&cjd3uz%YG zmX=ou?XNu)A-iS5(PaO}(t6uZQ1)H~4?-?2`t=a@yeJw{+d`0y zGNW}0l*REr84-CtPCC`oR+i!zTq~voK-BFv20bhi#T^9e`N!X}xA!;(B=n}pGAEj_ zD&h@gL|Vk5`#6U3Np#k}@Wn*y-d?qnb0y?oZbxT~3DSqf3q3^N$2AIx27={l)#3b`%YIw-#t~_|L${!5S z9*OMZ!W1R6l<3j{K-`L56uO0iplj8-*hdES2(VFzu6xtil{#JnwqS*fnfM4tpe3_T z9lX>E@q`One6pCqt8eeTe2Lm9;_>M>S5Wdl#tPwTu1jhwZ6Qkzm22$zg^^L2y*bT& zYa|o9ej0&%sxs1Hv_;7eK57=U`OTAZsH{VkSf$lk zD#D~w9#q0cyPenw)ai93g`N$xOzydX$}d4!?#Cjs;+atC7$kkVQdu-CbGul-J(9QW z0I!IoU8j=Tum43yOKY%_Cyl!Q{N^v$;$@qN8Z*_(x56)1Z#%GzbF5_CnrcQ)=Shwi zTQBU|dht2gS}+=nIDY*V%VXfu^Z6JIQElYb_q%uGJadh9>4&q6UESlu z7l*6nmfOCIx+Fh2BoWMMxN%ZYE4SKgfWfpO%y5p|m02&f#Tw3T9?6++wu!65O_Wy- z%z|(1+at%|4h7P;h5i1rYIU?v-kdv|bqu5W%TW34cVBr%M7+<+6gr#$O-R6l@@xEZ zij~u-DKJ-iSxtL>i8W=|d5E#jhJ>X_LzT>a!{6o?!=!BUva2AM?O>&?M)@WM)xgWc zF{^`x$DDy`esi*~a24$Lcazv5%NV&e2|uo3D|L9Jl`)h!yGPpgr;yBjmNZ8O#FN|)pw#o69D zMjVp45gUEiR)CMy6VJNnZHK<5P{ajXPL*l}7d(5N+;_Y}uCMXO!YQ@@|dJ@4Qz=QXD9fiV{mP zSBV5K86l#%F2OonbMU7k9z<|{ytkDzWGx^^AHF9)a-`(9z~f&{m(%#mt7=29>Zwk7 z8S8O#A*=muGU$u5>lzjc<0go}4``M}2sM7$ydnih8dhwCSOOMpn{2#IRB9)(YS+qj zWavGTGo-hv=f~yu&FP$r{XTOxuNeBf)W=Ln&n^LiHN4tftKfdDd>nU=g@2roRH|$7 z{gSib9j?4$$O*e7N(}Nn%X=S$j{IabltM67LShr^SW%8}CkElc+4quMigg7 zBtD%~eaw|Z7c;A=&OV0?8@;sn=TKSecxhsp?bJ-)s!`$zRPt)sx~#p4{ObD zbsLT2SgLhqTTj#uE3YgBMjGk3Zuq^}vxhHK_vh)9I2xp@WC!ZEt!Gu_yu;_!1dIXD zAxG61Nc$E$l(G-R7TF;llaz?~dTo)%kt|FUv7Pe0NMN__4&*}=^bNxCU zh)fUG-fGx+BNcG{9iPK==VT@*)UyNbLP)cS1fpX2wL&HRoXH(z-ABmQ0=5dMkOvpr zF6+O-UAbPt%IM7uPwclM&QYxCLbQ*38mH5;Yv_VHD|pOJnOBvPlcf)W&0V01FjvcV zj(YAj8>gKuQ5Cy}bs;dc@Qw&pnkZ!3xE*mWhaZ1Asw?637fJ@Y`jazAG} z=#UbSU-)JqbS4AD7G1jIG}wKj{0>AAcL%pa5PJo+bG1!4Y5*2b?ikFYt;n_pK6{nLu0|$n=+(ecoqna@sKM z7o%&7s%?-zc))G8Be^w%NIx8?{4Fm2+L?Pr1tKQO9Y_Vl1xv)`e(ct<2-PTAx+|c* z6vm_`9?6rz*vD?kH8W~ex=s~wdeCtdztnj7 zk^kiqpKqq>xTA84v$2=c$BzLi<6b8wMu^hpxA==4+qs3=a>;ls8~29xnz~JS$QOCi zbyQ&CvRaF;7)*w1G&ds*s}}bSN;sE(GWHZ*)=-qlE=XbQZT7Jxu%jEUb1rE^sZY4A9H7<(>kq4F{2`a-&X|AtoHNwN`+rEnY*xeWQ1Os=H+NtWe(u1fyf|o(b>jK$IXU;*sC*^A{oTE|&f+<# z>pQC1ijs~m%$U>lqFItit~EyShcQJ7ett^6cj0>IV>xn9bqQ^H$~d{_`w>5j$EA#W z5A^)hTZkN?ie__=)cO3AF}m6r+Ny45VVUthJaa`_?{rCOC* zOsDo!*d14yTJyG+e9zl`GgyEJy^Jhgx;S@+OUci$HCj3~fRrI3)}?=;{Z%Foz+F_? z73(Y8F2p!0!YYqqo8b-(Bx}-&plsCe{^KjGZ+CwO);|1M{tPN6ILfO%;X2M}_9U-A znjK;kgN^fruypdKoB7JW)n9mgQ)8D=idk!Yc~EDyfu2K6Uqz(R`_SBw&q=fgz;dRQ zzKi8rxSdi(&>rqG^C4q*M@^!UF#ky#T7I*vEn1OLxkugL(A5LENZXm&Za&2{82q_( zqWBr}CAmm09op9D+;qK$x9t=-%w5yWA3v!Wnl>GjI;4C(L(Nc`EQZ_GyJ$W)Z|-;r zoUpS@6TxBpy6T#(k910T>!80X;5M7LQIYbP`wzU-yYfu4jPjz@+WJr zo!8E`Bv#Y7YwnvY7u3%vtNJjeF6otg8y#y|Cj7xWU3S-p!d|-7kqWOsJ!>#qIQ9s3 z8$xSY|0t;)y9uae2Q&`g-9J;QG~PiYc06yOJ>z{l`-YXg%&3s>Du6FEhiz)x(NDw` zf#Z={9o`$#-@38K8I&j=`;gPcS74=%DAK5a#Fa(iTz|^3W0c3F#YJJa=-Pe^#&NlZ zUv?>q|9SxUp!IyeKs)TEix+<>O2*{H(YFX>tfiO`3N)q(V5hBC;IfowbxOeUIF(vB>g?ks=Bs(2uRjU=UK?D9@srG z6zB2j-kwZf$#7m+=EIVRR0Y+hu+U!zi6^+AKBYH|Q}V@eiX3M1u!s^EnY-2XC>uqZ z;tvrOjaqliec?q!wmXhhr`X}_`UvVrrOtl*IIU~t-Hfs|*D0!8S#yLHOs~j?0oHW? zz}lqqC4fQ3HpIXrZVc}I8}|dc-c0a1UAWcE;iN1M1Vm`)LPgQr2nbiA#GdhboL~RF z)q2IiF~$6mjgL+mPTIe{L*5uY`)H`qdLx#PMl9yaMO&AhZl2DC@dOLH!JXIdfu)y8 ze|fE6{eCec>?Tsu7cy z_j~lITp1!gHh*Jf6EyR!Eeb+lN$UpKQwuAJWXqEC$~A_>8XYP|ZH)t5*Ji8L;3C=% zyd&@Pe~;eW`87RMZh%N;f0(9~ z`093B7~hUWcG_zNA!Daxl5lT_-L{a--sFcT8@iq+BC|jo4Mn+|?FG}U&ckR ze+`zdwqj1&Us9n70!3{`jjut*m#2$0Pf`bq?sU>ni@w6mcNnl6;!;Dl_8N>WAG&Jf zdVsKDyVe0KuE{@0^e+m&}8!tHGt*cG9?}yg@Cox0DC@QS9(DNf<0_X z7cemrp&;X-l{P5{lvV*me2^nyli@3;5UL@ylz$Jdfp!*njbh@Vvh=bxuIiXD576Mz zrT8X5a+6+2U3fuv&%8}NYF$gKYl-)B&9M_tOu)IkpA7f>Y$h09d*5q;Qs>vjD?~l*rM)K=?W9;cLL*}Yv zC$2B`^(DZE-&zwV_Z2$lWDf#fUGk{L@00hUSyd%NRC~(^NK&k;riw-U*Cuo0o0>MF zCX#L@aV~UcDoF?}4u5ua>ulL!H|}wkE6Z7Ue1Glb+|R{5MtG;`Xj;DA*DG=ZNvAeG z;v2~9_iiRhbyn)JYW!^2ft+<^Fi*)s@iotDvMsW2-W0oif+38B$cmjAwf4?;#4r+X zZ?9ykf)*Njy~+LGK)&ZWY@WA5%r7ko$&NTzZuhXHNKL61K^+0`uWG+z?5grEcI109Wj zAFddI7DalK%HEzs014W>`9A6H=ed5856mP|js8judd*5TXtac`QIXXg_9Q&a+(ODu zb9Y3wSAjq9y?Z*FbtaHCK<}a9l1EL7e8FZU%i>_67)au?NcmHQHEn1-i!HJ2dpK;l zne+9a{R(r~MWR&Grd>w(7!vVTOQTn2tnS_Xf&KP!!ewT46{6?fWkUPJunq!VI1$5? z0_nnNvOFw(817e}qtI*Dnb*T#yqD9I6)CK0J^3B(zJ(p4XIM>nC z4CHp$pH1MlUR7_2VJg%4{wr+b8a&=-c$Y;f%liQ-?|qI#yCLCFbAHzsr6XBS&1ZY6 zQz~?X&$nxxe9%c&O6pZszbP{AU{#$O@CCB z3x~>%(zHGnM+sEa#;IM-`*P|OV;iF$rh)|3i6y0_O8&iV2Y}?QZzMHH2zESp&1bAT zlt>CiaG&Hm=ikINe}BipX<+DggH}R^`B}}9vEt<3xj55)7@eWn4rm4{2W=>PE^7WU zcT;#5>dH=lT_)e&%R@LX9oRFs6%iQS_n&_x9&<{i#9@7wS}7!>b=9QHxkWYxuQOTe zrCdez*$kl1(nwgQzG;$6`ym*HaD%+RIf&7VDURb&msgM0?)S}@n!AJQbf9ru76BAm zCN|N4%S9|?J0?UpTCsYDbJYB=JDyz`02KshcHRh%EjiOeze@!I}CJW z<}-qwAMKTZmKs7YIAsywkNPl0~h?RS?Brw8EXBz+ru+Fq-t{1+V zgF|b1g^`MQj)V#BL7v=;)&+>yC z{K>h&%o}fyHZCgeDeqOS^tZdj7NFN=Z{G-1){f*m-Ou)CX5^Ls&8;h zKYSPtGCDAwl1Fi#UUv;TM|g9{X+yJ754n0*=FEgN|E}A%&xfb~nLAa)B4( z89Y7ZPS;=^{C>2>LNx%1_8l|`c~O0PUrE)~aK5{;2jl6y_K@PnIR@<>Pe71p<2>$zgfr-(?A4>j(s{8%{G z(Vjrsz1XrNOR)%rj&EJE&fWTEcd03H($ruX{*EP0D(2aGYrn*59g#xmxeWV)djIRf z^dU&@u0k?mpOsV=gY8Nj_x_PoXb4|;+zeyho9*(7F9r_2VwqYHj(7^VfQV+RQ0DNr~sUgFt(8{cf{V_eiPs zIi7 zMrcDn^P?Ux?o!l(wzwALZ*Yi1a}IQRGPQrloDk2r7i8EPCe1Nzx?AivZMY|B*%L7w zsY;yAwb+;%zvU6QKumwU0fXtSs zkRJgMTHv7!Bbj&q0ze)>0ECG<%Jg8gICsSp)9)c0jM%y3Yl zQE!{SuF!&phy5z7u#=8tlidd{ZWMw2as$TwZ~)3RYVT=5$*S)I-JIZ{0q2V3vx`IB zH}7{DE#H5%A+c^~w^0p71zdf<8@lRCj1{0J6f`C00 zQ1S8u_+GCUkHLn#v{C{g@%eWSF?Vh2ugpBJ);%SZ7%LZBB|_U4PBO>I0=yy!RWwx0dS&SOP$hlRL zh23RTDfx*KDibqXcVV!pBxWc4FR_xYa zB_(u?U4})cEQ9{H@o#PAGEMpc7xkzj68Rg9tkcco%Q?fRUV^C&z(&7NXm~b~|EY3h zUaQrp9l{RlY{r3gtKT~N0)@=4d9AAFYK4K&=6=&eW3t{{>ZR+~4c%PFr{w5Yi}ffa z1Yz8BB<;az%BguFRaR@Em0fu$v}Y4zcNpRM&vqV@3Ya7sUIz{_`&w_o$V*}~ALF!F z5)SW;6*$v1s`A(8e%X)t^BC}NU8jyJG4EZs`i$sMUQVgp+!NeA7qk&oV$$ToZS@+& z$$?)FYJT%FbPt;^SjS|R?N}Tr4+yYmf094tx4(qAT9s+?hMj$W{Cau&;K>K_u|kXn zrrbT119^5_vt2a12%?u8rTDh#R#_UH_ml-gI(zCtA5*ep5_h7QAS#_?CY*HmFgFM1 zw#k7}dt_fv2muMJ>^p`IQx?PDx~EhtAMrExTIL_*SXK8IJ)>TT%w(`V1vxw<_{e~TpN6+Rbd_i63r$}>5j0U zls2ip%DIvT(%Tb3#q3CZBHJ(T)>7p4;Y#UQmBFk5gVin?=l2f+7V2<_Un_<25gRHl zv7D#l23aU&D5IOoVsMGLrLWMoVqWmMVs8WRC_iz?ke7qW`|^L{+ym)h#DY5F458A6 z4S8Xuc9R+vC2f15Oaz#j02+7AzX>u|=uh&I-W4OetaQ^Cq?n|UU^GsS;GX_??+9{p zB`ml1XAb}bpDZ|d?e>!w`OleF-Et{Ed^r}7SqnqFN9vQ5{WD<}FI4+#=5nwl;40SE3L&2LNb6f#k_6*fp%97{j&D~yp@hxLF*!*(0 zJ4Jg{T~VTYC{cwK%5`aPWdpv2!RJD@^GjuXytgubDClHgw!IcmF2J0LBSMpjC^+p= zISc3Gx)&3h@Dk&B()P^yXKt=acBn&Rev0M~4P&Tf!WB!dE8x%?wZ(*yu7x%o>}{Fb zUUsC~BuH8vo2F;1Sr&FQ7Xc)~oH%XF1p1-6$Aq*TV}CK)=$)7Wo~` z9`pVFu2k|Sm%~14hZQ$o^DO}|m!`J=XL{VXbY@$I<{qy1^GmzdLT>vTJjOjYcejdc zPIuEk)*22NSSr7JuBUKj7691v)+ZjZ+Z-zA4&-VU2Rrk{haFTktLI9u;1^Zp z&>>HPz+0DJm8SSSYb={QRCP-5t{{YgZ}J%Qnr+JnkzaKg^P>BJEIe;3rr6Z7QtruF7-Tdmhy2%^)R~QL<;p|#)3KWEbp?<*WqVgbfn$PP2q#x3ObbxdJesL zy5b-?UG3_~_BkF4nOT!_TGyabY4FYh4WN4`rRZV;Bs?|%-EMLxoCTwDk4!P(H_|{# zftRwQ%Y@bn+$f+F<3_LlEpjsi3K1dTx?1>RZ=0ykX*9lpXdvB|$apB(!(!)s7>n%m zuqRX(5XfQ8hzHXtV%ie84|J-nRZE1X<9+z(4ZqL4%eVi=7tpUBH6Xk=tb};S(bfxK z{D)1!JttUz%}ub@Fb(0Q;agRw&Tie-Pp-e~FhS|CF}WZ4398OC^}JXhjAQv{6i(1CUU`$Y zAz;6(c*_6kl2vJ@^X#S7#)cu9UqK4HwpAHTJeET&F?(mNd0<1+U8hoE=EFYqR zkF}cZ)Gz@`6ImAy23h@`P=@k;ualt|_4Py@8)hWOD=1&ILiBIxBCi3fMY`W^j*K0s>YWR(RL^}|sgYK$?@W6y z%S9=wNa{f}3qfdhPq0eDJOAuRZ$7psomo5Q+bfDJ>07UvD6o~;9lZ68*^S5=SudJ; z?ZKQm)m)%px|j3{pU9}C;3VHVOZSDM*&3JT`oNnpbI)Hp>cq5tGFmz=$3^2q{L!v; zB!sV(?^$eiIR7K-nh#%H-4MwU?53aD<;rxbwQpsxT9rXV4WNu{kP+(#5DXz7_kyG^ z;`cXhBL-Yfi+=JQ=hm=DKG)&=$6#K8ZVljBZ3IS6(|~EEzGd%i6}%Kb2dADhk= zwqI24+pO!zMIoo}I^NbFdG;y1VJLAK$co?5&sU8acE)Pwf=^6SoAaSTj6)}0Sp+g~=< zZTjUGbLmq2|yHcxC1eaZJFF%L(%LdO2$>q@)+cet#sP8!>=|vd4e8{LS%ls>fjS3wEBh5N7>QiS`T>h( zr9OctG>|s7@q>J<3$3TR3rsl7WSCX6+-yw_Fhum*SEDTgD(N#BN^Pcd_HE{M%AU{q z06i9gJrh6q8g@gEK)cT4Do{LWKOg|-Rg_dPx^J|{yFRtL;V1}3tIQAmF3Bd~ zwb2h^ieQ(qw;y!%uhQbrsAN~FkzE|AvD_H!>4$uC7Kbg#n1$9KxtR`Rl7x zAcLyRz)~$J;QZ|0oLcR69fp5GIbTQ&meF=xqH zD+xfF3->`oAX*~GgL;fk+lWyjjM0fRk6MD)C@1Zqqq2@=PPBl=yl>=d;;dLsC)$U| zLVX2+U2XB`%gYhqN#c|oH7~}E2&5*W#L>MGSIpegfO2~$nh?(x(@jUYjFYg*3(gcB z?Y%6Bt!UW*_A^_9K!@zVx+js^K+w37H&Mv(41L*|mXKXEd-gcWGvW>o?`uq3Fg(3QMZ=c(`Je21GU%lYz9HNZr92y#;V6daaLuMy>zj60Y zMzHL-ME`2eEBM@2#P1Xn6VbcBR+#%$q!;@mM(gcVM~{z-19kM2l3E@GG#Gy>?SA5{=qo#Tga%dO`GQsSp9ytv z1&ClfZi=>qRP$~lu{D=3>A+iE^W5IQ#P-#YUs~=zF6;FWC*!S)n208Z{aDL^qdfrd z?n+CiJ|+4Nl!OgXzWHi6^3S`2tIJY*{B1ZyxkrD{+nx=6$xi!BMqU%9(jV*Id;Z}m zg+&Y5{Sk*3@Gi_JcTZLp2T;Ki-TsiW=a7`W{F{_H)7Kt9rSpfBK_J6lsXxd2Aj+Hp zU#kBXGnNa0Dt(kbs5L#!WV>9T@FPJ!3jI_wB+LCmKJh+>ToM;Bz@(M}ywD(C(DyZ!bT2tbYD$b@cCKOhn465C=hrVGykG7gOJ8h{7qwY z@SnSX4m;AA=b+uvM=!XTQTMlD6hYyu=<|5KabEl%m-Jt&6h z9>o=42D+i3!0L%|?Jaq_6`(DYb!myd&unq5GFe0kYFnM5D(}%wb2+>)sk|j+Zg3CASYdh&tg7L$yf| zvYtd)O!mK7%>Sk!F9@C;_TIO>5Vwi8Oh0JA+G(v8LY*_Z4Rhn-k9;WyGXf`|;7YbYH{Sw{)161M@p_3GB^pfLU>KjG>qzBJS=gz_vsO zCSU)Pn>lv8;pE-g(Yy4-Xqz$02n^ZhIEzio&zK^S$0(ltg1fyZ5!s7o|r-+d36+371jbsqF%LC8L-!x5#q{8?oOt=~4ShH*sB@jEi{ zGzOrIo(I~r7*6MiHaVyaF8F6#89<}zXuDJB$>Q#MKr_jc3biio@eIJw!z-UhK+26A zHHwSB7sHLQ4XmR@E|*1#4@`Z~4V(a`@dGGOsuw$h8SR3-uk7*jcebtjNx_(Q2zU7d z^I_%NX(us1Vg_beK`5fu07skuNxEm8%d7KPcq&rk-n$86uk~DdM{HZ&BIr6D^LI!L z!~8V*x;ramHJXi$exNVkg@zii$qW#Gax0L-Or8ik1MEi1dDqP|d{~Tq`~VH&I%-m9 zu;b-R!TAL~{8R6=(FO>6-|EYw$2?inVlec-(Qp968g84b*B!QUV@6kK(n3CjZ@cfG zO+bO`|5r9)MH~1vYVWP8WKXP6xewq1c`7DNszG0=V@0{43;(zwY$WK zGEHdo)+EvWEi@#?hApfRQf@A2{)u*W-p~+4D@*d~lm8`p{7hKtdQdK)QCiu7uu+%} znJl1t9LcPdo)dl7K}I>{!A+-;k*f)^H(ZOQ`d`c$xgK^*5eI_P)$Y#jLJSMQsJT$O z2+p``94+COP1w)H-Otg`*VEOW94hbz0ZB*G33C zjSsG;suLNmYN5h$iscXJRTe0`OA3v(humWwXd(}}`!Dkn(IKeEPrT>uH@O`vv8RlJ zA;2~NEv0|z-jnuf%Vg=j>yj!9Q9OY?a&r-_*LfW^n|ewqbkkQQjY)e-G+wjJp3S)2 zw3T1EKUQ#Y_r>C%!_L8XrJb1q!Hd-cWriLC+qDOu$lU3P)1mmu7trA9LrBCCKu5GMsR- z3ychDmTF9X#a-p)8+tHu@Fdh&Gc9s`<_G)LLTF^<^^54rpVSrFuNWcrxC>QLr|UTX z1)xAOL8t;AwGQTwWs!=2Ox~G8z9;{=3_yC;<4o6dW0nM?Z)#{;BqgiBT5{h!ILAi6 z*85C-yWXiS|8+_KZ7>(=;Bb0GMLA(p1DbTKM@|Y3Cs8QL#y`bu4#1HVraJQRP2(N3 zUWrhm3psx~ySxqt(wS@_h)L=6Fn*#+sMHM?nUXe`%SjE>uH6^0&ji$j4tBf9iQ zWzL1R8O0>onns~}FZN6de*e-DTAy3Wx1V&}*&SG#47erLnoB@pFClY<{x;|e46S=d zBsDNK%VA{jZI^lVGo8-&I5!FozqN@r8UxuYAFqXsc;{bu$q-vBH6+b^tubTBkuz}Nj_!6($kwEhha@$XwF+S5r1z%$m<&p4wm!aC;F*tAqvo8! zasl+zHgBC;4-S;jCa`B-#=+sk@C|IZILZATY*-jh%3sa`c z1L<%tQFBhTMY!fi zaB358L;lG<6SvZ;b`?aoKc?D*M9BotH6)1qB$UdewN1JAzT$W&Xf~jyVeI93oU2&= z+V>iQu`~83AnTHZpyF&Phf?3PHZY;CkDPN4r+bKc1%d!2Ko*49}TJe|Bb-eY`&;uR&=qxq zvl&7yXXgf5SLK0|s?;v>h~{-_+uxt9bV!DMdbRpuxM6Z;g=ls0G_VU5a4|m<^^jN! zjflw@r0fgPki50bYq@Hlvd+#9(R|s5J2v$^GaIf>tS>Mb)-TZ1*Hh#EhCdl5b;IF4 zfMPrjTVE9n8uo5PEWro#?XtDNk;HWVU%TbH@$S-%`ne#qF1%uB%RXmexG^$yHwz=hybXAUptxe-)jW?$e+L1lB#Bt!EzXn`{IXyqRci}jSe z)_QiXOEm*_TaSBZgJ(q6v!}$9z&x@i%&wJ8vY(A;h#*6L39PadwZT<$%b8aQ%0VSwt?3|nxuHA z96>)pT#oszpIcY&e7Pikjiau|M^#Hkhe)yr_v`Jxc~k~hkpt>yDV=Rh`_GTaTUff29ugnYMvj z409;bZ)v!a9RpY%>7)(c)9*bU7cRW?unHX2TPoaXab0*NU@(r^GD$-h`n9%qUh07v(_B7c_d=0ox<^4;Fi#C%l6K%XkPuH#MwZB2(J0W>Wb$p zfx(%5b(Q9lZ?s@DZ%!ZQ!9(*8&Sm-wVGNmwGc>>UhM|WLX2G!jR-6G)9 zmD#%PCd*z)c-ac-5CM^WK$Qa2pfQZQC0M^E01kk4Yky|g3lw<#(9AGMWtu58-G+or zm^GSPQrqEI-~9@(a828@Yxp0<^rSe?HJ$c3Y58_z9T=gdxWHB)1 z4>!t2z)ipECOR zhVe`F@>hkL`+l_MtpQD8476p}+em2+_ER!j<7K|((-=&+#~Sw05e!z@rb>M6?@YJt z!mc5_?c6mkX|t~@q_cjz@oiJQZ}`0qeKa9hai z*Gu7Tn5)o!)XK;5UxvVmpIA+}gHw)1sfngE9oVt*rE6b_0uD5QHYx#q znea3OMUHVrGW7jtQW78Y^Ql1xdRUHRR)!X*OIGFnK8GO%+d0ttAr^8fyTuW9SGTM4 zk>P!+eEGK*GS1HTNt>v#Skhl}+{g&d(j2LryysG8(~9G;{M(cJ*W8fPD8DfxRm1mz zHtS;RCLgYB^ zg@$zsQcNBhOlcCh zLMAtgy@8pE@)X1F%Em+-VFttt5Uz~d5SJ_cipmRMSQG2OXUkV%y$E~9M!|aO5gK-i zYKw4Uhc@PbBSgmf)06%@O4NM|veyT^hOg1fR0rkAOA&sR|Bd@(b#uA<(9%!wn}U~X zNQa6cnOyMg)ks9 zKepHOTNd+JizuW&EY>glT0{mrbLLD)Zzgx%U`D>)22Nwd_#vLTGsvf;_Z3#{IF#76 z(kzV!NDh#wiVSt4OdZguVD8^G6;tO@My7};Kay1s6rk?Pw6ge{UJB?}*m&GXY(6o#*UlJ2A zqnrts!O0%P3J-31Vlg@zK2$=ySIaiWBs9FBdsMVEaF_=RruAMuC+rtdYE-ux z8ecJsan#)4Ww1n;n79fV*Bx`HdM3(0^iFXqBrv3f>^|(tc#P}NiHwHEYTQdOUe~c3 z4plNI3UV3rZMI8iu8>$sKF=p+JbwtPMI z%mXXvSPK^0bPOED0w_u~fStUFZ?K|S?5IOGKI`DjkATyoq3J{55JsSc$D2ptk2Id5 z(lq1BPXNkhHyd177Ttbc5AtVEhf@?XReXwcj?)TuX$SCG-G?V<$rJC!9& zYU`b+hFgO;_Hw#n^I3nmV@XalUBE=kQ{%DzqPbPgkE>Hg5(HC0#!08iN*Zu?IV}dX z5Pn=paJXNPnNF1mXdf}DxJI{k>As?bZQi2>?I~uSbkOcwy0OqNk9=EZMK|yM_yv+V zZn4kEIT(a9edW42$+A)Po8k1{?qrzo|E;Rj(P?5rkb$^RAc95l*ocja7?2|guyf-7 zcjC8`!=%D;p;`S;I6v9_;%wZrw=|MP~jv=$Ag=-Jo zPIb1AZS34Frwh(>$r78Kq0iImJM*7&>rU0oJyUf* z9IJ~DT`FC_e)hBXUVH7eZVWV{w-lD=1~&>BpPslz`Q^73w)!q>BKlSCPD>1AMBr}U zqTrLG94%lPW{a;9L*0>;MEoftT}xSh{IleX01wltQ>7m;%>E_9J_lil*t0o&&;Go9 z7bw@w-OKBS10r@n)^B5mS8KnS`QNerurZxj`^uGRj~6~P$`gA}8#CKzaIt&T|W_6_WuT zCyLYGX~W8=v2ci#!cQyon+W(}&f5bPtQVHlog4QIb-Ln=2*j)l4jUuHV}KNezWCa7 zNo_+siqVr}^ga<0CB4M?P6~%ft%>{Q#TW;|dGndF-eT#~@3|`bt?^!U)%Hw`Aq|$3 z3(g?3Y9faBysbX!TXy}N#-Mq-R{1AH)*5nClDk^LLif9ZaMNH^9$8*$X(F88*hb2H zj0=^nJyq!yOVuS}Qn-h_@RJ>?$?GwG9=|qP+&EOA!^4&E(Cz-u#J@GWAtmIGi6DU7 z0K7K0jbr)pZ%+IJv%N~e;hXZ*P`xRGY==^u1&V?Cn+Q186?y&QzpT5ukJfkpEu&&xk=zt91-R7IM*?NhYn<6z$356R)q?^@jD^KagEYuxAk zwb8}#=Ebnp;bO8*EA>i#XqJ}o{6S2cG)L5aL2n;Vx!)RBr!KH%kDj7Rph)Kgt(v^r zN5ZWa?j`yx1mclXKHQ*?Kyw+zM#rt`J3z-PU8@&*_9Js8*j{KOp=8JNZT;4#1P&_cQkR_Qm7=aQ$In&jrbL^v=!x_*?)~$^8YYEG+s7{s!ZF6s9^nz11)A zAEWa-COWql_+0-+C#Dg+Wc~^Y+UDD#bH5)?3Fhiorr!G7s^EM?qu$(M(e8#dnH9qa z3bomlwM%1*o?(fGkacitKPCvTrXu4qiN@huYTv0!=`Lq`lJ$mZRTJR~n-IC;D?VK= z%{!IQ@3R5$1Fbp(ncd|wnG=QD(GT*58)p`F&%g$+Wv#gJgi_;b9DRewaR-ThgL7k- ze!~FK^IGi(psvC_-Kw0 zPBaA&z(Z+AbUR{MoYmR))j(mt_&uF~YVn7FBMWHoBSv&dG@j~6#VPWUkqhn19zZw@ zwL}np%8>WLpy&GsVl8dLqIYKZ^N5=Mv)cTV3e=xXNw(Mjqj*YA@&6?FRs>(C0Eu2?zItb_asnfmGervzPDKW%v}Q?@`O^rq1p&x(2<5j5DPT}u zI%OFBD$J&Aa{(>(mHM~EvC{#!LCRVQZ^8EczPHtDFUq;AslqJ25Tno7C1@{H zrKM#U^}$JHeR{q9yiEqosfK3L^aR`2z*ywZtup6Yt0DZiS$&D|%?e_Q$^4HktqBIK z8~ry&uiHi#Bw$~sxa|KOUHP-a_Fo|2a$YVqw(7oo-EdHi?w?aD4O-`|gzNv8f`}`2QKpdn5bWuMYt#jODNPpY-j2TKxag`kRTw=Zsba>hs}%OW%vmGbWWp z4S51DLz9;WtfoEngKwKWcjJAk&{Qj8&s;+b)jmEf+glmX2_fwwn-`fp%Q7g;S4)pN zDXwd)vK!5!RvO4rWCSvZp4`abd4ozL@&O9EhD5!WE+) z)ph~%jdF|jyD6)lqj{fechT)sRWs2Q%RMQMIclj&7#v1jY&)^rpy=Ds}hn;KjPP3LXFCme6JNscXIZ1Ql*XAi+@4 zO5if6G1pKEQrpLe(pODpkk?FO{~E=7_`PoM!s5TXZcs}5L}Pwp`{MgRRrpS2}qPT{TRoQxUnj z@pL9r!4_md=Az3`N+Ke*7)$QRYn>y>C`K^HT!j>~h>2F?!nmME&EG!ox^~?eZ*cLD zM8Tivx6_xlzke;pXcNtFxp6JN84J4J3${c~b=2baPArWxq)O{S_x({_pN% zY@J-b4)U1(2o}^FwGI^d_oV|#z1*!?-QbNBr)q(PuQNjCb1v&1f%w%b*0d8oIFwkb zFVM=3ZU1^h;X>C08`c!rr;&ts7N?R_@i60X^NtSUXsB3Iu?}HJlYKaUs*L&QxBn+U zIvcF~ypbUH^c5kGgT_?fgBlO30!RrYbnV6N2eoTZB8J=Tu{_!903O%MQP^a`0Tw92 zS4&im{SUuN6^xcwEKa%F47LTOBz(XPAM+jZ67`$Qfxt(NKqv<9Re`2SA!MOF>&&Dv z=Z8tfljKK{Vue$A#@gW-A?F43F!Py?f@pBEgcoJSeiw_Ie^y@k{?yX{y=x0V6fZjs zs8_|$PSX#cZ-G)5$m-A^`=BmBvrBhsU$~@rP^AB6bmirc7DOrzOobJzLKbsmKFC`- zLLdA>`KxvGOr^I?G8WQT-5o&`g3-rhKOqssDVFbN#}FP%1zAbt%5Fk33fQs8vLQ7M zt{<$EYxo}4sWQs#EDU5?JY@wq_(F|D%9-1lLcrV8aq30tax01nJo z=nfOi+&>;0K8A3H=ZqdYstIfUHY0g;`lp?}_1|4Cd^Jk&0E-=#g};(^v}}5eq2NS{ zr(%)@?CPXQI=im)Vha-8fhic>Rv|k-&&L@6Zd$fEu-K`)(3|}oLs!Ch$xOISH@GPu zP#>vLA~t9;304K;JWkwVgp?BASNpGS9R>TV3yW}7sbMtQ(H|cH`Fe}ku0x9kD+!3k zLf5#<4r+z3m6J4Svy%n%;6!d$bA75e|)&7ulXf|BWW`RWTVz<1I;fSZgz@b1_>sw<9heYJb}mr10Ha{ zNGCu$=)?dE4hn%SdsdT&{*qAB23N%&2jus>5B;q?x%JC)2HLRG_^W(M-f+5*md4t+ zBu4xTV*WV7YwU23-AwVTD=wX0I2^|@E?^fW5mm`pK-^cNjwYv@H|(8zjv1oOPy~60!>i*Mp;9&fHG4OuXzT8 zx&vMfZ0CbcNlaCWhe1c7464&o^^;HDNf{U>>NmMpI6V+q&&H1>V^37494g{CHu0mA z_II2j65j4rw3)HX2ry~1^vlpz+x~|OP>R19L9vf@ogOMTXPFSOB~& z-*hu(2J~3cjU51prU48Wc7@!XT-m8#nQ54wZgvJ8S#myI))2#Qqh-^d5g*H{&(M4X zd5~j=G?TwQ*8|=svN1u%$NDzHEX0vx9+DXRu*?5wChjq959^{5Ner0d%27VRY4jSd znR>Q-BVEr=7kANHAFji~p3U4FizyTmN(0De3RBeKx@^qdR3qV<8dAki_Yrvp*&~D_ z+umyl?{SN;c0P)GtKlY;QkYRoeB=u%GJI$ZdBS*>-Aui2;2tFruD7fa=moF~>xTux zr@N)u1NNU^)KV^?-k*5G9DI+@3eU@JPU|hl}6 zwWcgGpxL<%w^+n%$A5kG9<-XD6Rv=2`KK$;Y(t>yX>$%E`*%jm zkoLt%0U-ay2`P0(iW`URtbRT&3hcBPC<=@}BxY1%i3uWzG!ML~Cr(i71vM!(njw0p z`Q`l;Zsa`|rmzPcaKD1va)SpWPo$qwLvDP9Ce8sYpA*pp6Y&$-OM%!u7sD?7;l(lf z)%n+GJ>p?NvF;R0Ir?k&VFRJeHSZHVT5c*tquok0T+}YUtK6*LRWU_SHDHsikQ;d+ z59~@vimNCSM1F}EIqG)S3}tSUtW~#ta?<8>gYEQ+od?`K+Wd~~R)`vs+5v`BR5Qj6 zKKduODDI>Dj#k7@Gh1WkFN(kj1K1W?f3ZHjTP>;bmJQ4zm`H359g!NdS!|B6QBsW} zNve}Io`Vl_X0lvR)~C5|xB$~C?n|R+P(=f)>E;A?x2XP_LK1x~@LQ6D)ZAYuru2ed z4^6z6;@&`Jp8|iiqm3Z!zALJ-v(#>1Wann5S{C9)-!+99zlI1CbEcctyQ}KhkCB>{ zXy+-=RWLQ&=N3PW)x0P<15`zZpKXOEnJKr3h!S*{^P zNwSKKhTS$mRCnh(>T#5s#4)l9wT;<$T-9A&x+|cr8Q73zyWsw7Q3ykM$$gJI z2posB-W{wEtrXgJfiQ|DSwrYGsi!bib*1t*V=olUARI3|G$<>pM=~8&JsGUiVnB$R9P1sSm@3Ok5zar*Z=~KPD<~@>s za=5Oo-nKw^HI&M5h&U!A1b4j4v|2u|%jAwdT02jL4pb66Z_FJ0G40>=#{a|T`8Jsp z35m|HA#VlAHMuED-UfdH49jzSx{MgDP{4-A;Rci0TvR4W=zqKUjyLp47eYeT>zCnF ze5M1hro~YAZTIH89+VKN(9sVhv1M<)QxN{LB#_!s$>G_}JhwvNB;Y8guR+Nr*8Uq2atZI|7oLmXtnN3IPM`Xq{A>uh?l|>cFsQKPhL8!~g3b&PLp2sn_F;ai zlsu9l4F_-|e4HrvsngDc)21@9<<;vE01hNKBdb>j*ECxqkJrv~{c^9jxfH|b15QdR z#POtH4Qu+wICF8`LDdBHyiWW8V@O-uWwB!c7(MDURHfZ%{3By07(L5ljL0~kk= za1VAn>{I8~_TaPOR{MBeK!#=oMlj_8Pa)nLUbiB-52+t?!e?u&pqqv;u zh?KDQ%fIpv__)P8my+FtiSLVMUhx37o)r!G4w{&0p{sTO!+{e3%2`ZD5A#}t3j}03 zXeIGyAs4(?0$JX&9j<01TUF_#2*K}9OrQ5*U%9>mn=p8Hu*sIIe5bl0uUWdd{xAmB zgl^YyTXlSgJE1}?(1xvYjS|e3tvC(n_{caahSS(`Ms!B2yK~%!L#k(jBd%dCOr8IU z3zIY2Zq!j>YKeL=C>{S=rDgfErrsJJNwL+8mqCad7+$?u!F@p0*clHO$ig?FT6CC> z-5y-(Xvi(RAA4#(YM^;zSfiMJLDZ=T#Oh$yrucoKNR}}ZrE)-Lzn%AJQ+AHRPIhv`qmLzN~ zE@v$gAOS@zoJUARXjF+DNtnI#XVv;iGB4_OU!^#2<0@__><}Ubc#n|--yCmTg8XER zq=Lq#ubO6j;TkTf!^<-tk8Z`2y>W5)mfza#R6a{IqE0c}zT)~okrb!PIgNyqmJ+n; z`e#TjJ|`8`<;$o> zojF`MEpU5gyYqF{gG?K>QMmN@_>>GPGwL`A4l>RA7xm$F+hC0D1%B6tf+^JB4saG2 zvYEQeeM&D(!YC#uu-)a{vXvmdno?Qo`h1-~0te-QjoO5T1LQuQ?!bVx+(@7Q#RdbkT2p+{&T59c~?={ZMs@JZr2)u*@R2_KrziBnly0LiZa~^h!RzmCz zqHgM0_nV1U!1EXq)s>dcu|K@L1DGO<{g0iM7I$(Pt-?- z`*7F;HkQ67x%>0Gow_9oZ%3vUe)b|07l#@OJWcAKB*XSyfo(Ac$h$?(^F=EGSM#}n zh;q3_H$54*N%X4fkB+G9@0=D8$!zt6>Yeo%b@Iy@i)(_N-_!jqSYv?JKZV#Y(CyW{ z+CFXsk6i-vruS@t?DqF`xxkWg%XS73HY8B>jNakG(P#Uu#ekr|zdH;N0_FZUr5zv> zUoBQEF-4l4sa3BIFu#$(P)=;7CWN*F{D)8%fQht_y6thA*}*a&b=8YfupjvA%}4=% z6e1#eFiV-*`t_q4*j~Ax$`cMFdYeuR3_Ld2SKlSTNjf_C)R&^x^?9l6j`L0Z;=X~| zf4jC7#}p_XQ!(hv&<5A4u{tno^xVqrSVyw~334o5P zH0UJ|R*f0|r+CS$B+`N6`qQ2=fHm`z*%uM>=j!y6uX29a>(#?upHm#G3SL(K^o|VF z542NjU`p~~;J0mwq|cw3UCDoU?HPmXIe~ut=Av4kEcF}#y7Xru%1^=>!ZzgBS)Mm^ zc_q$;nM#Nc_W1k*deN4@8p~kl@*z>+&9;T8t6_qEXDeg{{gtU8fcm`q_ndal20g=M zW*rGA?1g`ou4>qGq=~ByUG~|B{rn!E%2xDX02~GFb%T0^a?2)E8Zf>e>;&%1p;wpS zrpU9CAfIxpbPOfQX-)oF1g-#waigZZO65CRh(3v|Ud4;#1;2$%g&An(RkyWoBgP(A zZ`m5DOKjJ0d=zZGn^5Ou*%>~yb8y=ZTZWo<` zDbr}f>+abdsWWj%^^g1xF74>m578`U(jTl8tYN^%V7mO>FeSHf(I5uDYS*V(oL)mGR8nZG8yFhY(t2PO&nPTTFPT&h@yw;jrII zK5`?DEG?|xzZWJVazI0$jynF~W4d4OIO^VNfVZdVa=wyq3fQ}T@w(a!>P#Dk{PC@3 z4k#SMpYBgAQ*KdN;RO|BbZlLS{^%`j!((JD)J zXU;{va~%iuKA9Q1If3$z4sX1n4Di5bPkO_bW|l==9=Xi&CRM!%)Q>n3-2)pFVHzr9 z%K*;O%2eYM_PCJPeY#(-hwzeH87T=oPQtB(y_jmHQ^V55oN6!> z(xvaKm;3?En*4PD=+KCL8ogsU5o6bXqtA)rjf5R9XUFrq1fdyOTIZysoxt;vJbvE} zQoW~2o#nspwr0h)&hK<|iS;9Tk$eh9H1)I8xSp$X)FGOX*m34nHci!%iPwd`{)2t@ zON!%X-Y21V3iZBgrBXhwxL)-eT3RR=)H>^~(?Kt^hoy*{aO?D8*FXPpk%l4T!w`wUWwftjx?v!b8s2Jn~K?&I(#YkWQAM}X9~+`Pfr7! zDzwHPS5?@IKW~q2r(F1?Z37X4!!nfWm%?%sr&gnMh*PMb5NnxX@8$TrqUWJ-aK@H% zA;Z^~R9WCg$wLJeJ8hSDRzLJo(0#5-ac}D@8!^m@9pB@T;Xu-M2=VX185E<(ut@`b z*QbGTQ#{)Bmr%iU%gG^^Z(Sv?$ACin=~1?2)vDtGXpN#sQyqgoZ9cMXgt=v({AvvX zb8)Ng;#0)2vTo-bbdB=~+rw(V?OTdT^m1EtVn61Z950x~uIo8Y+;oMlE;WfF-d{T@ zq3U>CO?q|Xu-WEm*j850;|+hx%2o5pX7Yn1ad+54d#bY4=yQaaS*y?Z9j4C(CLD<< z3qZR0upz=iUiI10|AMhwWG__X39vJf*ANmH$7r>D9V-T6eLDXrswVuU+2szm>-`-U z-Q&W@CFXLu{hNL1A8t~M(&x1s&Pj6ws=-Lu&kOOfflj!E0tz3o)8pre54U{md>sv-fAaVC6Mv> zM!|(;Eoi3L^myGBXu;Z7 zI{oh6vp3-~%jdux#+Ia0o&us6&yfVOk_PWg0?&YZ$xIF12c4oA9&Jm*CUr39^r^I{ z1w-DBGb3v8bi}hNl|)aWv5Y2&g~U{~5&+ohm<6RsERJ7p8p+>gJ9`PyTLrYvEIaWI z)bIxXd3{v^8%amYLs*u>=IJ1w2T%5Mqo7wsNc6iuC z*XZV?Lz&YG$9OI1w*`J}c8jcvlFW@Y*)}=Zg^DO3Ujjb~Q8$NB4v4RQ1%{avPl|S0%jUM2Q}wa%^T`Ms7X%WpfJ2$k9$#sZHb~bvZ2YEcBh?jk&iE8bLL%W((^NF9JEihZH>dG>k$y$#Awep8F$cXV{O^|9>rxxKz-b9eZ-6VAIViQ7+CR7+Y$|3 zvp5Q<7m^&)zZ(Ur%3^BjX&Me%pPfMcK#|*~KZnV3t%BmU3QTS)53lM(9gc8Db0qOm zq?T-+D)Y%Z**Ks{+Eb!!O@2-G#BlW zT*c4zMA$oCw59HVnmS%rS>N&Cy#t3CQWr7cYCj4lSa3{YpzfHduxoURkp^FOx=IQ{ z*0Rgg^4K6y?Vu6a?|e0gau;ymEq;-&;o-x>jO|Dj^G>sn#`obFAqE7Pp)^^#?~lZ6 zP%(7?u)SrP=4+Ql}8^!~PT?wg7YJveBprDZ?MimL|0o zx8gQ3tEUAKI>FU6S=Dp&uskE=#Fs4*&6vbvX&pL&jn2ZQ+^6K%nnoYF?6Q-*(wc`G za#|u!S5e_7qX#LKYi2fTfzin$g>)M!hK`Q4+AIuvaG3vVai2?IWCq~{<{%gjohBe^ zQ|y=4BgvPl)7v7YWn?U7L)@}1-1a5YK7rEom+Oy7MqZD0Hf564k?06S(WkS`=|a4E zfVPpJZB=mR&QH9(x>BB7ROf5a6EM58KpX*B0jbgUUE3vuh)v=-Y6j{*^mHI{2y6re&0PQ5 zb79?nfnjXPL_+@A0seDZ@BM7Rj1eCK8}fbosr}b`QHS1L5ZeH`8Z{_@=4PrM-lNf)Vp&d^Oc)MTY$@g=q&Q+&zQ zUvivoU^Lwnz1J-w-=HrU__!50*h?&1NTEJY?$|ECVjyg?`=M*P#bZ z2&q^paMa)`sTF#bTt*JZ2G^iX(~a#rNx1N$@k-w-RKb{z6j?nx7Mv{4ScC6D)uF*n4}u}Rovv*8Y#?)}zyaLl5@IVqK6 zyacY`!!hfMdN%=c*HCf;a|x0dH95183>a^W|93k^pjK{ce15^-%U3w)E#! z66k}sY@-Wr)th3~eBWVBqC~lYWqN3t%;I={)2d|2tWjzg@pue4|(8G+w_ph$v^gBM5kRk$!it@YN5 zeHgpboa{iQ10DbfB)i_S2|17>nqd=W&El(qH%W9fc6~to*J%CCfP&9WEufs%(U_Hu zpK$7_5G?Bo|87EQSYIj30jeqF+@Mt+oqH_F5d((Qs%tt4=7Bpm?m%kYH5$Y3 z1{4@87&(R@b=8-BAJwh!oFoajFo6__aI#Z<@P^H{#KU(h*mX@}ay{MlG%0_lqg;PJ zdttyx-P~KpRJXIc!xVL7q6}KWjTFx56kG_095?1PPTUkI~qZ5 z&ciSB>@7YG7z<7E_)GSdY_U6qxfVnl=Ix*5-p-@=%L%_Nd5;JjT#lRnoa4X3A1~Q4 z0EZf8BWIQL4N#KMThgZ9^sCplD=;q3a(ZP9+A-%cp*6i`W=)W+H?9LzJ*K^aY|14S z_O^@GL7#4Qf4?R@2zKJ?}IpV=L%Afp|edQDU@7@(gJAf`7n z@N2nKg{1%@JW-@hJ+LX^(|~Dga`D_wJDLuKDCvTJ(SX$B*4bLXXO&~r&E$N%L!w+2 zH3iSYpU{QRG@BPVAPP(?%3U32=<6Kxn~+ZPc|Ns{NPg7N#Exgjopy_FlkL%`Q9)h3 z&@q@Ha<=?5ruS=6H&*NG8e!tcXSCi{m+j^D1GgituQ=pjtwi2ld938&f#6qA^Z8m_#bT60xFm7yB6I0qvYGAbzK0Q0 ziH>qf53~f|HJ`=F3@PGqo#bC1I}Nin>>}sstFm{gd~Woe&YM$I!f)pe80>sbs#tdI zl}o7ZzLS~Y8Dr%<6`x8j>Q=H;*Wg(GOevzfbH>y|2Sz`ycD_TE?6F!LR!m@?X*zlU zgBE9<_{G^u$5B;(xmp32UXqSW;5IcoA2BB9a|^dDIoh5xZ|~YZI?7gYFRRdn(WAB` zxQwJzC1XaX&E!R9#M-GOVs=8DI5Tk0;n)V8pwN~EqZ zNMc^ERbENQNgi+=%Kv=>yf=cQ0c-ws?=sQ<_S(|F@;dGF?RX6fjZ!Wa{mMw=reI>x z>YWc4hHnqeezqwl^Jsbg9Qv}6jEfaPD}bxoVTa0!)F6Xc4-pDAyTRn z0@~7vw_6lwsKi2h9H+_%{gT4R2Xt9-AK>rFz?}HK@oD8LQB==aLAEA~Kg~vyNmI+t zzTx(=X#!7;2M}YbDG+9d;CLS51K!Q!w{n{isKaK;p;xBUXsHULrv) zBDqwgu{d0)J67{lNM3CSx1%L!#=*3>$#hY;)^jbq`CCKMO{gr(%k}(pw>NAB_>d-7 zp3qY`+P~KUyTk3%8o;=+ZsM;iBZIz_UH`N!RsMn7n3ZI?S{j%{j((y ztu6Dw;*$=VLB#Yvq9vF}vMIk|d?EF;H?{ud$sUFI_lrv_TKPjh%Ma})fUVPZZ?ULC zG6P{WqvCoWzXbY3loGM#@X&HSvI%zJsh_foGw<&(qsdmnsB)Wt704h6x2EBTuPdir zD^Pm5JdF%V6jhv)?KmEZR~REMv#!+@raJfCcxP_)j$rWNovZZNNVnxQqEQ>|d`Pd= z<{dcpcktiIMYrIPgfm@k5)+RR?>T9nKDOT{mQN@(dE7Q}J2xu{yY;JW8WFq!59dpx zKWcs&iI!=oDT{ifuhmUJF!m$i*jG44@opQ<;uI6=;qGo>zfpeAN4XgMA4(nrjuULS z(7R?RN8j`Pk3Sth+Hm7UUc$VNC@-V1q0^VK)6u2hLfD!}%_@Vi6kh#)ebxmbJy)2P zY5XVZ|MjPmbd)EbuOOPw>8vlu-(Lot!MQeZhW=vnr0B$lXUgkiyJ1}VqDrxC*D_n3TYslpX@sJQ&C z^$X? z@=_+s;UNqgjx)Og*TV*Ii*nkO&uX0dYaHAby5nRN6Gab~O~)#I5j*Vb^p)+YV(gZx zRw!bZA_vzPK*-w=g%O zjDE(}V{=*t=pFGpF<#Aj9Ff;z74_zL3x%d(LE(GG$C9gk#P+SLlF2R4jqO#kh`0Yj z&Sz0A*B~p^^ITuET2}K8Yb-6;ert}&SLbn_Q+Yl^Y;Z1rJ-A!zORm8;-nNVF$1l&0 zaC*)TZs-(E6pbu^#GZffRs2OZdDu9DT{Uk1NHaqE~K;dsEFAg zVg5s9^m7~iWh6Tbw+zWSuBJWmePle;c#UR|txYAnBkg! zqVV>^jK_})d-IDCO=lC<%hQgZjQsBXoMSZh@YGpIDmEy)6+*(5fxTZW5d7LL#lw6s ze5%e$Z4PF?`_395GfOwHyOy&wzQrT1>^33Oh_4J%O_Kb**ItId@(5s?MM%t$zys1B zGM`yHTmDm-yq08=%v8GPo#1`{f3H>~m~a2WxwT0I23`7h2IUD_)uZn@ZhfdEx*mO$ zE@Gi%grrF3x2B;n!-2M_DLz~uvU<;zrIHnmr))_npTf|k3+H}w$(QJv-Yn~`(!gnl zXG|O}LkTVv03H#<`F>6(!MDx6f8zFLF~4CGEvsK;W|w-YW#meEDKL|J^6g{rdmufe zEKNdqQ$a2;>1M)&fsTiSP8a#ZPxb-~Q8Q^v-~w8TX#rHCB{M)1O6TdQpTXj~*BfA+| zG!C^lUOA;9$dtpup(?4}fo#sI@Jt29JDqTu>H`*>^hS{?skc9%aUF6uBJRo^UcP@T zsGYk?`OWiF(y(dWXkl-B)nXrsfU1(CDzJ4zKJeJEu|2-H$u(V?q}ThaX|LlZ=?w*F zfz>b96oT|VY$PtrP0{%Y+qjPQ7$eYgn8l(nXq{m;Qi-$>2mW z{RE+r(+jR1jXZf`{8-BpKGN?7I?K!sgMp_Fn%}#3YBL+-){yPv*MYjkl#-@r`@#xr3 zE=Bq;cosu*v!ZiWOT#eX(DNr8+JwLK??${-hC3)SUbUg$<1{Xg>Is&Qlk52?7WB>@ z*|#r{jyfvfv+fs$U_VuN#fm#pA(TzgMSo)Az_t@Fc%xG{mT$&Yi=McDG}k%%&R3TF z9?Ctt`o%X9g?)<02I*Y({pC)^-M4iQ7^O20#IFAgn$N$>a}}&$zk`fEa0D$Ssx?<3 zjl(8!_d|_A&)ubN{mhUqGgjAVyWTVjU0IOH$wzKg>B7tj6bhs(9r|6n)23E#>ISeK zjB(QsU7gTjZT7L5WrYxCI>q3NZJi!}I7rM}**(>?AHG|_&u2fa6|`idgCsriKxI(K zWQNFKU(Kt)Mkh zKOMm5WL69@KEWHtQF{I@33DL$EICbrg+J)n=0!iNLAjSeTNp-?L#Id^Xs(L^*A63@x$@{a}M}3O3LdNzE?6Iq_qrU#^kJS<1$7D%pA-fw>y8nC|$o3 zE*n9rdd#>t(!E;9VL=#NrETRWQ=!)Addf!zZ|lzQO*89?tsQG{iICxVjsp!C-l#{@ z!|DXf9(wIrD}9Rs5*!A`z5LaN=*)-Sn{QO>W8u6>eC7+E)7s)0D}G2k3S=XyVD`JX z1~{(TqcH(v1cQ04;Q}|^kg@hI#AdFyY3OfL@HjCYP(OM;3fWPMX4B5TzAp`mA%Ukx zgm8sW8VNVbMkM8j8CwmE%g$AvykUNixTwh6;GE!U&dM2 z<2uzu5#4kaTX6iPB4{MO)!4XvvPcjdAF_6m(~DyuyuSA<)H(OVc!g^E2Kb`O?9v{2 zmz-r?Q~=LkUQZ$h)sKDl;(MdJDfNMDbabe`3{|0zVjk0U)mp*}3R_-op&!>|_w2^Q z{ToS~%>6S=NHgDI3vx`fU!B;JD$?Pwfb?<%^oRY;fz>ke~Cu?5(5fEmhUxY ze;x=>ni_nN^JbzI_>{nAB(RU^S6mc^qkU@23p+)%6u-7vdVufPuM^I&xhMA$g!Bas zwIQpOtjz%~wCkGnK}+DCQ+#J4ov+6E?je*h*BI$}6YP{!c<8Gl<974z`ETA`6~Kjl zZ2ool_6+oNhd*ooxtq6mFXKWR)-Q3X|2sNkLNA#alJMC@|NnWBPhedUdo6WvxDiF$ zUyIVMHq%?7Yj3G4)M?q`_J)aVPBsQLeQljRQnzx=*2-6SH*0#(QuK{IHo#AtII{!p zsU0c${rfxi;7d|0MCG8t4Z@cyl;XE7 zlu3qN3F}LY8ei*w(7E>#x$oP~{T^XAeY>{YYB)5Z#j{|Y=VjNO?)?@V;-B@YYxhDo zZ06EI&GqGM(avcv;3A>n8#1mlL3&<9z*TUJ54pZOvQf{Xt(}6+W&~7J!Ua{=*7BmT zrd-IlvcPt12e)I2kYX0#Ytd=tD&(2?q2}!ptE?H_{m+Xt^~)nN=H&XSZG0ChpV&~| z#6KMb2aP&OvP4u$YZRWL+V9#mU~K01w1Jj%IJ{vCZ%!JA_@&P zPWOUilK6bc^UjF|bxUVHXYj;0fz<+4({068g24={?}CX4AAz2ac*P<2ce#_r@rA`N z6b>(5?j7Oi6wSODrt1>W>CGqK8~A3e&5Oya z96O;^j?)^Rs6?THBbU7eRxsYUE`g>zp?*gLvwoStS&2dhU3%ZMOqT7M@!ICdA<1fmX`aMP_BDK^3JbheaPz0#$2xi+f2IkXnv2-9i(^b;jv%-Q49Bpj=ym>t++ZJMczc zOkWM^d-_vh>d{W>2+%lWP>v>-NEI&h@z)bP?!hrZ=rul9hSA5V%LaICv`Tc4A74~F zx|&ocMc010ImL zYhU!mL5H{*lIye+xidQJUe?}x{|_Sd3TvGX++$*~WB&$H{d-zx$4dPClk(uZ zdfHqmyKbE97XSsa;p~_}#c}KevQ2B_1I~qGiui#UhEOk?Ttbc)nKurf?3z&a< z{~C~V>E?rMsMTtmt!H0sp81*j&mK&9qYg>64%rC!i72ioRfctwrR- zIgx^(t8v<8;xcW_d{4ofryf*Upv9sX7+wv4KGtSN7rGz_PP7uavSql3wa9kMTdG(F4q^ z6?ur7@18(J+A@N+OAg2YY`uPqlhQ-U?nn5#Jr_KnRpA}K?kzLQgadApWLd1;!z!xV zW^!Es(y-Dca?O1cSI}qASI%=CCb(9ZPU$TJqWR)*1t4| zyxH)9gzMF@woGB!OC=cna@N#Z!aawNhgPzLH3w9JY6t?tZxQ5u+O=FWUyqgT0w3gE zZq`avis3T?I+l2W6d;Q)h`s<)vbH`-azccXhJr2!^yH<~JVvFSW2E@slfWJCHd{Lg1#tm94FdskjiP=b)EN zV+0|a=FwK~K`voeka=GgM*oIHHyN2W)2s|L|6uWjci63c@@8JRRDW|iOlFze@NI$1 z{#+Ky!%?ysYn#V=TdB(^<9st}!B_DvS!P}(u3PdNo8R8b575pwmi9fdokQMDhxR3j zdCy_z8ocATdxKvPdyj#J$N`oLsBOndZDV@>+lG5GX9U|Qg(j{I0eW?3ezn9OuXDyfOEIb}QXV0Fx z=9-z$EFk91Neo9|udoiKV6?DOA8O(d(-r4VIJ0Fu5RrS4Zv1i7k*h+odVxGa)J<8A zE?H8+a*|XsK0!)}V{1+TUUOH(=2q%zp>w{`;INrF$XqWBPq7$g=t!|vRLGVJ{xd^!AS49Z)(vjosV1*%9vo+mUF9=B$<-@>I> ztsQ7w-?h@8_-Us>7ngz#NGnwC#XBJiLv3Q&{ospSi=KeQA^ocoXAn!uk zmd6`G@tzEjJe%pOk1J4fFZtnB6y<`N_lK?l7FM=^jp;;rO1eqa0)zV`;HIse_PcBt z3^E($ll5x(Jxdrg@q0t#NTE`YwiN!FE~xydjq!)TGAFa+s-I3(xzytDbZ?U5nv|(; zdQxy2|5ETVRSdWSNj3!}j&%7}SAuWbBkfJFjVZLj(4m6Op8Hs#OCuX%I7=X_RAbE0 zA`xpiyhoTXUnu&3FR> zi%5ai1thX4e5fsfU+J44^YBylyaDc$xl%pg4%D=f1b~hp^gmfH|66GE4F0#Z=EMI$ zJXiHxq8Utx^Wt+$P3|UW&( z-|YwbZ@B=c^ci|D_s$O*)ru4S+f)6=tv|L!lCz@Kj^{rW2_=%rQSohlS-%!crU5Nf z^7C&oiL4TxZh?$=V-;3&6p-&od%xajac+<2P1+T`HssC^${pPveTDw z{4SVQ!OCAfnO+z84a=HnR)bd^k1!N+-lffL&#wnV0W|IH!*AsBun+bO;%)8x_lC_~ zt4}|2-KB9KfkIkw|i)&jk-+$J>*Uyog~O3<=y*A|LC4D2H_j~-^P{xPGv|G@b`pI z@x|92di&-y+Isss;jTZemCG<<>GyM|fRw6E?$kwfiY#tO`ac5Wnq39nqA(CajTqYj)G{iGJtd`J` z?#5=3Xk&QB&mB*wgjzO^=|UunBGmQ-#p_p-yd0_dPjBYcwQfpub>_KC093y4`2#u@ z+~IkMzY&wknl2bEme(A>$~8r^vlPti{T_ptI8a;xj@0g;h4W5t{*&$C;sr6XUMtp{ z(f~BHfDVJ$xJTJf`&wW7(_Xkv+i&)|<^d^(z zC~!^Xe`zTH0bPA*eaSe+ljT2EDnZlkGrAu?-@j4H90>-crKUj4;U#QoLZOyF^$tNa z;uI;lg{R6DI%qAD@jq31h4u0^C!hL7u?h_zHZhIk=M!|t=9BG}ze_U%gHRbioqD{d zGZc6KkSn#=wuvbG4Ic>B@XT?d6l-U$#ih~#MUNN5g*;3SlqkLVrtuon)g23uzYBi_ zO-*xq1;k;8a@F$hEk8aL$MYq8uxFTa>3Bi7TJ$G*jm@cp4E?m|hgmEyM&-6$RVc8! zbW}ze3!)E7h>&FMc7dw2;?zI5LH_XTfXhSufV61!kFwNB0PPup^O^kTN4NO|yQDK5 zLY%H~&0JEeWWM&W+F}Ticgovf1Y0vLH=>!s;CpcHs6h*}keDZ>HkUx~?;qhPc5-^% zw}AuXp=f9TOpJglBa|(-S$uv`eR(f;x=LW7Ef|H^jA*e}`0^Z!IAq0Ki$4GzPCBtI z{&MniD^ArHedXK%>&0 zpGWM?ccrPOJ|pxt7!=$Cy2#cH1pi7O&F1qIm6bhStt?tW2A>Y&$g#-z6)ykc~rD)lYSR)%(sPb^Wa~AFI_ou!50P|)XPKoxB zeUSgD_B>C)p=JWhw;%wh>(J8bp}-?X6DhI6?0pqCz@CzwR0EfrDxLxE!S2#;KRos? z^dJ{airI^3tnaYvpR9r9Pw-)3M;SafKc(56I>j6|zHr-eI)h_7#QC+eF_iOwwdj+( zA(L?;Z|u{GQ^AYrno~p2zNR*8J(-Br7RHfjHW@9Y4Twz;Wa(vKZtKZIW!DrGW+8V9 zdbJvBz)ElhFhf!26F_guGc;en4VZ{NgmM3fO`6!b)O|*Cr}9L|D3BY3Qq{!~@}IGC zJSvEl<5>fAZsIBDK;+MYX@rBg@+OmRpfYgUM&qp4mzb{k422~UhQ=B8!$K_?@`l^W z7ow`IBrjb;A(>`+!V(D4X!-JFe6~78JPbOl16`ouXr}>tEq9s8aPFQ+hC~Q+!uzUZ zUT@f^*~FbOAuHmc98MR|@i21z-9{h>!Vq@NV1n;|3hIZy%d|t?O+~OvKl$qVH3DLi zK8@J!NkMUp>A}PUgth7S>9QJA-!UxOuz~#+0Q>^P!&jgOYN0cBl4Om@8yl|Wa_&j zaGwS~#foRft;$5=A#Eghok)P>BK$g_VX?>+T}e{Y32YM*-kv_4-3k4fHdU6bF^FyF zLE*8xT%U+FFRKwz?9etQ{0=aDhQbO z?qcFhCe6N%{XauFECzDvAIN+dQG1I4FgVFp#1Hsc=rjuVL1;PLMaemm{{;&5LIG-d zbZ^;5u~!D0)UAsW@%(IUOK&2lk|uTWy;Qm?`PYw=X!oJ1>wkMiCGlePV-s_o!wA4H zl&m}i0oKUGGq$@N$z25hSC2ICzcc?*UEqY6lveNR|28RA+`y!$Irk9XS|n#!=9jXg zRi`ghkJb$CNq?S}pKI~=@8D?;;xwYj3_V#p?v7@zL;2!nXARap$mIjt+g}`3c_xd7 zZCdFB3sYj2v)QE9XJCa;$Oq$52xm?`!jaE(=GPPX?y;gzZ@>`5tjb!$OP;@2^n%v! z`EkV8hMP!j%?Nb-o|SD6YkMIJpkAbCcYjCmO%0TDhfQ7bkxJ>tJP)JL$|(Yk>Pev1 z+JP>U&qracSn?K&lRvv-F_=`rirwx2VVUO4I-n95%>F ztu7W(MT-_eb3hL75Q5no$IPJXx?oU(CM=524FPeSP`2jZs1I6>TBcUF$h8rUYvs8G)Ht!35n67WE zD9ceyB{I|QbS+5n;h{hlvD=v=-i^q~SYy*~;>tJh){TM5fS7o`ZZtt{<0Pq|r6zGR$ol&pRC>~-6$QD{> zE;<6T6>^*7{FHFrB+NqP%G@I5Y7u~P1-_5q>4gVxISU0&U%sk)l&?tu2A@Zt9(2tb zSF+&21#usteqO@HsCioc2AomO&b;1jymu89gn7*yZhrf(I3obW8D~A(jFC70V(;V3 zz#Ham9x)HrcySg#QD;ciOz#b1(v{~cgoF`Drge33vA+NcshA1u7YW>)VA#XAhmC-p zMQ97cIK)x0#G`NwbHuG`5RGH3GBTixj`-w)Sr{L{sv55s%MRKYpjt;=dC;66`qPLS zYsmp6av(=pdc}NY%P{TtkK%WGQID}zCx=}Z4e7LdG=HuiY4i#fqk6xc2~cFjq$*cQ zqyJb>4~8puq*r&~1G9WUMF^eOu_&7P9#jHN;Nwh3WwH?^zVd@1ZczVRfEH^+yKlvX*u-3Ax7mqR|^qsouTE zIQIE`0%97*!;PqyQpcw1M966{*c?K~tdHR3>?iq(0&itdrvO~T9A(@OoPXTKb!Np4 z%?kbY!w0E5qfy3NC!Nqyh)GN5Fl(R??)U1Wo6l4J1bdeIOAwJbi=+|C;ZO{kV5B}H z&f@@T8dWJy1eWSS`PtA@Fff8wR@* zpn!q)`a@|msVT0FZG8gKvCyAR0)+w9S@c%2z>~RyvG8->dRa>h{6IIAVG9yA3yO-> zhPfVRIt`;?!{^D!ye2~zeA!WS`+H7mzZLM!VB_$t0-jS-WQ0+BQ$-bFplVT`U9vm= zk&1o`jf9%V{tj_t7-!#rnM5{U7wJ*ArZu?Zv>@MT?nIi|{pT2Gh=D=1fn$##{?B{{ zHBTHE)Do=xN4F_1sNX^=r>Xf2v0_X9UXFjJQ&Nn_LH=p}mAMWi0c5p+6UMrarAbDX zk3k7?E3NhQW=cBqdb3H!@Gus?q0#FIVk#VN_1voa`HzolBwi>S6<@kBc)nIrW_U@1 z>f$Sd-M64+s4>T(J=f~VNYani?L#x!930822*HZbzx#X)O)+IT_X zEISZF6v)EeGg37-+uV2N5*!%L*#Id3&guah8wgv;VAfHhMl#Ygd$F6up^0YHA?0vd zr#|Id8v}*<852|xCj>(F?r!bfi?Uc?WhMf?q20tl-0p-kep1>SCDmnS8#?ZD3tc+q z+NZE#o#_3D`@?(V$x4s&^%lSAN~L}53T<7S1Ju&)%_hmC?uz?pkxK5GUENCz*7eV+<`ed>>} z@d~xlOK)c6BHIg_MJDo%umEQI`kZLYbs9Y^P9R6dUuS+cfYrFtEI2!mV#Gis8O=Z3 z8s-osNpkVM)Z%pjHk-blP>NX!sI7@-a#{&dZI0%lul!Dy8Nzf@NK4B0A{Z{siuBEj zVleo$&h+U!4Gk4#Z{$PRWpeYU^PuOt!a?-R!gvy9U)geSk^!@?P23PVfx2^ed-g2Z>syvfC*r!iGX7A7VEWep|RAyppR606_y+`s>atY{V5_E zN&71XQkxfCMO+!Oure)ehpJPkVV3j$Feac%UVmW{2yueU_J7^w ztU&N29Ty5J>K^TftNSsE1z%o*5{&)`EhrR=Hvgbc@mX!6S-Rh&Uge(OzqC3P#!?8@ zST-}L+P*Jy$V!3>M^Q=(0j0Eo^ehY*A3jJ)`QXExI}vgn%z_`eF8sUDE%DE_dp1J%ngo)i42!2a{EG%-Ks5LP8|MGe;IH5| zttT}7OJWU)iOLUaA1Rg^;o)-Giss3~Y6-_Y*A_+R4RMFF;IMJ*=L5_wtc!{>CJ5sd zMOdpN2nlYk)tsVc^K#3Dkk#g7yzW)~rgac@pEP3g54RxCF{YddQ|S zka7=`1a{!)jJF4W7QSXA_^0{CqV%w%aK(bs(G0DZd!inRLXvU#`g8ok5v&+8z|m~| z73Fd|Y~EX@-R|9BvpHT3D~N~0Qh)XOur)u79z&S)r(=j`TlFv77|=+qJ|%-BGd+Zq zuPgH4RB|aycf*H%m=EkoBimxxkuD(e#wn1V;`v~fuTS8AqoJ|kyO7G~(q_Zhs4q_7)_%osz&HBnttxtt``Jjo9UIIi;< z?jI2BNQ$G=0hoz9jfcG8zK8q)n(rQSu*xl%M2iYZaF)OrZ+Vf5YU@h^P`CFlB_I&p zH4=v1mbkFT+?_7A-s1Y&wrmY+`}3SiH!8^51Z8(<_X4Dg(;jC`7dX5ZFD?PekQTMD z+M>Q{6h*cOnNWZ}=X?ELjFD&0-?G1luLaf%3>x3OZ$?JpUZ1XljziaAE_@U~0QC~O z#GN8p5W0rHt`DYD#rmSTqr2W_Z^@t5=%uQW_lxEGMJ6&Y0HF0+_3N_x=jPt_o1nW+ z7moQ$!0xF3neH2%sbjU#gW6Q0#_?cfeL|it#CJeWwbd5TU$K!N#-3qu^ z$cxI?Y~hybb8QRTNP5+}d$#mSIp#_;qs7@2H<-$-6&SPL4=%B^e+pb~Xl^7C4%Td`$w^0ahed8|_5I8P&J|mai(cbz6f$*;Xs6+T4 zw6<;~|0dQeDeJ+$>I;UtZS5hNfp_6X&WPNa#_>kBzN`)uFf3tle+_|EFv7mIhmK&k z?!}1nBUvAvp9u`;_qBgkU4NoEyPdP|g~j06xMv1>3ZQ}au&;kyRIdi@68=HIXe*QH zqJEXyZe1YpAX7CpQk*W+Vu__*qX_#pMuOsjWKx?)mkdy|ipH+DVU;t?Cx9$_5b)lL z==S{pfPnzkg*gctx3?M-2(Pblp@X!%Ai<)~)$93dpZA>0$K5nDP93TzRKSP};-p{z(e7@BFX_MGk`e<|As8;QIZ7;LxBnH#*# z9NmKg3L|a;;Z~)&nIUS9J!@ugcRlW248y*1sekQ=4FZF!vqp&pCv*Y}`Uq&kO(w8( z^pYil(Nlb%@@Tm6&nlynLLUgNTnhq^}lz`$_xAryyirOb&Mnq#T|djEUs zjeQ1^yq53mGpg@*z%%|22u>xnxuA1*6t{<(2gFN|ch)|6V+Ndmg7R-7sqr`GXm))< zNSy(+L0OCnu-BIR1)*KP>4?mQTP%;U;gfjUuIbWWGKEA~O(*1j;;rrPGpL+Mw`GP} zaD-Rq^9Eg?`Sz+ge+8dEr!k`%@Q+I0JWHJ;P>Z~H47)qU`2%?t-QD$LvA60)%GJRD z--M-5M#gJ0quv={BxSOXW1PUodanyzD|v-{3G zfmo`AF(SD56F#A#*J3zAh8$3l=fMJP?fe>FYY?L;A$nUZB0!2wV=!$bKEDu^xP=xS z1+F2ZBe|nK#k*fZ3ly^1ce|YN_T27n5%&nVMRl+QxAy(B2hhiyU*^VCy(T@T^JLc_ zF%9k}T=*1zkGU&Tnz{JR=WcVlaI*=4_RsUk#0Pib*aZ*pD3{x4*+jRWd2>NsU`0SW zh4Oo2;`TAh{J}3X=F>mqXRjUg_rbY7{WCwv>3_M;)eATEqv!RlUp<9^-oO?}@HzbG z^YqTi96bWo@vKp@DC9roPe=sRm0!5jy#LmKzX4XU3^8RAZRbpfiNfmo@wOQtE@TDq zjGffRjdl3;nYYlc@BLIDx$-|201sjuo^bVv-W}}C3$Gw>ipT_$J>s|c>^LM}fAh^s zvS}}7GHoRI>hbD&VIgLTX&Y!h;;L%s%`;a?UdzmeD^(BbRzl8Bz%w;-o$*uN9Kdbi zKp3AFfGK?L7HmwnM%4HvQ2cSJF%Fbo6RRXmq3fsOdU++Zy59d^XxBpX`UOJ>WrLXj z_j&K`wh|$m!U2;tcM``&dUIc`AqS6xjuYdy%+zX=&vYdb=SyUWLo^9mW~qNMP)>3q zCYyK&kPmK!wcCj3?}hmGP}Hoe%`tQkqa?M;JG~ zF4!D!{a}uQ_`gu8s$JrjVN9f$)b^TvBVcVY6DyAao4Dp<@*^rp2~Mr%ONt8lmm)n7 zf@D0Rnn8v9Uu3!)x8{XXIzfDQnIKliG?qa3&R5*MfF%WH5|%XYd-%<5B!mSj#lEE% zH_hJro$~q{*mz#4q-V4hMjTod&->b?!sT9R{IrS$x|r~8!4j${heoLJ_`X2s2O7K40s?k4K1vQ%`r5x2e)KDR$zs?U-uR=h=ht~!Sm2NJ)@>caxKSM&29EJsiyXU-nG0P7u z*epzV|84vO1E?q{`lw%Sxd1-PINf_O%TeY6*O#I9>elp1_2)F0H`w8VDICpX;^Sep zCTg6RAf4j~4BJuS z>Tm~h4wyM2em<-SbveHH*y866I*bUwzLdBzpU$>ZzItRpKesAp2r84f-&j%B>V35b#$=_$>AA?xtzq%9& z#7~G_D?|IMBTYia%nv&mh#fX4k00c!tp8Pw zdrsDySNSY*OMU%v<7>4eu`Ib_Y6Zoo4A~rX5+uACa;9fFg^q@=JOg4@^AL~Cm+QP- zs)GpLKmXc#vgQmXWP3_fKgwLnjpo8QHq$;F-Z=7~yb9}^O?U%6?+k)X6u4w+R`(*V zt&=(|hP%}G=pOK3N6u(^w+x#jx+~3O|35WzYL84l9T$KUP^iRjodM_M zbx^WsCS4-wcm=37cd)*dhC4kxdV*v$)AWQY+#7BYKrBlc@h6_>r&Bu=*n z*Hr$h@=7MF@%hG61MB4M0BlQsAe!CX(ci@vHZCAutGX8} zcB}CBAo2KK&%$x{UROEcwQJD5^6I*2`UuQzjcU%y<0LFY*Axb zJB?BmHYn2%CD+cvZ#>PoRPyYda^V8PrO#IvbC&;<2tL#)@N4ys^6MhELFf-s@ZRMl z*JY+&hGn1YkBB3H#0YT-sqa0OVP@I_Nq^iAo1{Dgx4Ds?48rR{{q++JYCOnb|Q|~_mgtrH0NlM$ zYViyS*A zgNFQpn|3+e7;`R3BmasHumROWSM)i`qJsDf1E3#F>Jm1Kr(Tg*_Ju&Ysi9BgMzs~ZWNDIBV z%SG&6fd8tQc`$aD9o@b7AdH^~z)b8ra9{#LG&}rkq4gBNISI`**QRGyU?!F*vch&3 z=&}U^Ze^-(+A_?ZyHzE(lErh-(+dXsE_F#Y(gncEjoJ6%aba_%biK>^3{~ZL`mj|Z zuEanLe5YTT<|>(*aNaJUSn?5!G1S-;YQpVrMc}z$XF)ur2D`hWgrbScn6lnecd-cE zWB52B)#Q&hzFGmwcwpgoz~r?3Pk32Bk{n>HYgSJ^2%dlnj^l6&XxaEeruqC`VHPgYFtvlt zT#ONG%mj{;rb^}J{m-irhjWxk@?aOX{pmT~U@ngGo8R8NTHh<(DKy2vUPDEwF zF7amAgBI#wH;z4FymV%I%fBBoCvh+`Mk;e;YmoEZv?0@}E>raf!ST$&@0EKD@3Mp< z2(TP@$-XuBzNdFFK-U&QS;<;WozO8JglxaF_!*n|uN!NsP^-M&cot72=eJ_e!n(eQ zBY$SO#)Sas5wCtneh#e!nh5B;?cS|TSKp5w%uShn85Xx%yq+a5mH!}DY+UEhK!&$Q zsdT9B$3f5Rl2oZpG3?%W`sWb7?`dUcIGChT5a|^Kiht=jc4uqZo{3ms+M79EO2s!u z6l90ekBEu<-iZO0TD|m+^C3z>J}fRH*AW-KAo<}fm_vk&3nCM^CmDc4aCfZ2Gv|^| zJ0vj+Akm8RV;DjMXt&30%y)E+IzJ$yxF?VPca#|BPqCiaH*qP7HVlO{WJW>4014NWspH1 znIFu~{Op8>??~~Mv*5tq6)}snNZCa{FIY-oXybP(xeSS$mohx!=>n_?pOj2845xvnZed)?ESU&u>0GZc+I7)1XEE zw2F$8G7^svfyHu(>cD<4R#5l@(Nh}`R&@*+WTg8L)KDp8keTUpx~?&qXk6C6jD|U1 zLNrL2#>5#YHiDBQ>52>lB(98eLxpTF6tMVU*QzMdM;}#pUUDweYEPo#=rM&F40#A~ zb8qdLr$5`lpA1j;QYnGB1ZuWJ&tn^tsp2QQXOYUIAp&#`>k83y0kX2TXA9)v*`w}Y zc9p@hBK!S@;VLB`JEEzS4&gZL(a)!w9A}@;Nd0@Y*EjdYwl&5EqHX<-9)%qt5c{C> z0zKPqjgP?3cU)Yxw4CTN+)&!S3mjTFdvEn-A)}Phm`kwo`0qi zQpURVdcjI>p}vL(L9ck&iI)ITtrdj519Yz`BU;mVswej=4%2O~K{81XO#eGfZ(9Sz zP)B*@de3k1fLr(v5bseqTnG|R;{nP3I^w#T$wa}^+PZhPbO$bPKe^7qR#DEsxJY(9k%Zpx&?Az<-k~qx~5rm1B!=LfSH|LdC)I= zrpRJVzq{O?KH69pj0PyLocqzT#zF8%OB>Utmwfvq8?QB8iXZ=cX&n&w`W*D}0U~h| z=vYOs_qB~-G$eSMt(vPALS!aF!er0`D<}Gt+HVxi6U1=Gi#dnlFa*WX)&oQnzPZiI zOWn8$qJWzBEgS4qoI-JNg~jNKk+lfwKuRTW!;({*qmhHYr0S=GqM={%cOk00yPV zT&r(p)Wzr?X4&CZttUpD^6MPmsrN;8mwUf4WKAX{Vqe+~b%6nC6q@{jIZ?yE5BCk_ z>lGX-^-um{Gm4_fMn(61*)?9Bu!e|uxfnK!n!~|$ojps*%qS71qW%6xewo6X z)SL3%c)1P%XafA{STeEqP1+5g?$cBpr(J+@H|q>!Mo{PUr}ycwo6y-0u1X=?zEYca zjgRoFfCHd-O>ZdrCX1l1G_=~AnDrMxyByVk5ZYic5EQbT0M*I@Zj1f^ zbR{Gh9nucI_G=;z=f&vkc$^C6LbLPCniUU}H)L$jrb!7j;e&=dH(9g%PcqUeI{i{8 z5;qN2;!Jk#6yb7uelYh^MSk|G`;?2xXBsN{cz{{W3F)s2bsJ@p#X>u_d-_sM)hO&m zd8t98yk9iC%Im=x-Y~n%DIxz<9L9!hh$rJG3-6WzQ_R@ZnoL1I*e`D%FkZt`I#eh9 z9hRIZ-Q@aj6EY<16P{x-&FK5%ydR}u6g)aTI4?CX#{~Kjs|w4m$mdp^|9Bo z@cDndc1>q4R~Vt2Ek76Or>SyGH$ickUn=k{U1;Z(p#41{`}x-x-|rY;GFv$jSeOks z5lP%qmizLDAlJ-My1&2Y55_U7W~Z($JE~i7?>$U_M(t$r1r3!X%MiOs$WLbia2?QF z{RSPUEfQ$f+wI((nrIA^a~aUD}LTY?SV*>5aDM25LIRd_GG0<_8-mT zv%`%cT;PMP$Sx+deW?rrrJ4%@Wt~DOq*AR`I^<+QL*O}k(KKe7LQ=uWF`(h3DIQiI zNhvGVgr0)2Jqqy&;r^>XB!_>AzCm)?o$a{>p1!*I-xl;h@f;n1NcJ5#jGq4zQe4*- z>b`UHOMdvkc&F%P5O8|!DMoYB)SS*be^7B3~%2;JS5$lu-idQzPKP zVvU)Q6Rv>RO{Tncpv&VqAt5SVa8kD!Vw980wxgG@;CoU-oh)_s$H6a!;LX3-+3V5B zni5~|{|)}hPdtc=0_F9rPMJY>5rmUo-VAv<{YZmNVDuPo#$zQza65lDa|+P=!sFLo zTzplQ6I{wiS5WLRy1*92hz9#YmENnznZ6UiSSbQ@chJBsxZ~6+&r@*rUpm%7Su+#P~eY=Y$RY{Oh(4MJ!w!q!}NcPH< zKl{H-Ij9gC#|n0Yi6)lqIn_>oVY$Saj%<59Zm^sF?nPtk$J5Q+us{IuDaupjPjvk!Q&qNZ6U}@c&pFtN1;OY<93)b)1fOC! zLBw^BXgLaZmRb%@Mofs44yWTf>%>iaw_#qXIr@S(Oq}iD!}g!{3?Yn#N49&|6lpXLMZ{2f0_w}4Lxvy|r zzUhV3su;d^E6o0HK+@Z58F&$jk_;>6c_9;6IY;(m24zdcY~(zvS&I~9zhWI4njN`> zjykp2zA_2ROuJ{C8V`qz9o13zGNiHL)N)r01EsWw8Zf|U5)anR!=%#&i}G?s;ZJBU zQmP$JpO*W0z%!g!==+BIud!ltg0LNw?jN7Np%wz2)!8|-A_*OUyCIeK332eLN*I6F z1FpEIkm2=6h~th4V-%W@!FB{{6U-VXo!|i9Lgrztb9@$#6{D~ge()q|DB0l&gzWlt zp8p}xN;JSJlx|VpY#-Xgiz{iH0KX0p===*_$7C!;W{jPkoZ1eUOv+V58vIjG!)@8D zb{UneHrCMD(mfdfhxlj9$?lKqthTdHaQyDZvx{)kOA_60Rt9{v(5V^aJH@$sfrJ7P z0TPs^?ybOFUO|`#$M^p4gLmI^QfmOiUeS@WEB6&HzO6n_dgvjKNmoniq|WG&1;05T zNbeD~zU7BkO(2{9c3q9$92(v!q&4Gs@{wO10K~#hZ#a~U4jZ7Y=b^iT0EUX32pB8u zoLzs4!Qj{Q1{jL)tSK)zhNVn9E(bkK;97LTR3Hw?&M4+JH^6ZRdY29>ii|uZvZI-? zIYAR9$QFbEZuDUxIcoJgWC{=Ht;o*N2z)XrC*3TQ)?dvh=Jls8tr`)ICnphOP$dpM z10aR82Cvs34S$zCK_%whyjpMLds1@&y9NxoKq-!?%`JKv{WUFR15eRJ4pibI$K!_J;zW z4<>txBN@%10Irc7NuHRMWXH5}q_kNM7SMv(QK9e8ey2)!Fs93l3=YS$UIncfPClGt zqPH0Lj5x%2g>nx62d)hC1vZqM9}wk*@6@UcGX7)pqyE-gz&;T3Ill>G{IA0pCw@-S zNMF8%h{Hw9qwbh=SGV9F^^-Nbo>Ggy!!px;=lNG)psae8)hlO?7D32$$rBe@QjS#L zpHfT(8yD|W}ZME(lFA*2(b4nG|_dehMzf0Wi`{S}Kl8 zpb?+jlA)P6B)*!Uu>8w9COv5}j(Cb_lhEw(Ral9_5?v0V+F$<>f`pZ@)Cd1f&D|z* z@%mmjBmGO&?;0uh<5b}2EgAS;Jy>DAIop&j;4zh)gO!P*517oxh7LjjjP#a#$DOyJw^bpkwZJUE>qQAw{71#? zMqIp#cWIiO5hE22gV0(%M+wH4ZbFhRsW^56Yhe1a7ruzQNa~W_;0pjmhC7DXjFjq^ z_eg?q+KmgNzOpd@wee(UDP%!s_4!Thh!h(_X_oJFXSOSFfJlSyELN1CFQjk^@GgO2;FKV8G(W5U1+oJYs@R z0TV-lyL}u0UpvAmQb*SSdgt|H2K36Wg6*y_5dA`Pj{wt3FB2Ue7nz_?WYA@g-t?1J zJVGJy=5DX@zQ-N1CBD&>@PXg=sa*duU{SQGvohAJ8*fgQArKmg8yV1BV6TQ%q34LY7An6Cj%h@haYs$0QL$Xwi&)N7@ z&}T;wZd=&;6@)TfDizQ5&q3!QWt=sZhXIT(c4N(Y03gz)pHJ%hz634HAO4i3 zBiDqB!%z^)<_`gXm}wgU7tvyA_@|=NycmSAS)*0=lOF90=Bz7a_pJ0XT|-D*LNASO zK_S#5E<0;n>vw~fPG@dx<}ZVVLuxiv3LlD~dZ1#V(G$~mzK!}xNdHv82O)}Hm&~G{ zU0+5)2*tZej1|Scmt+oObW+wuUd3W6X!t8qKrkD{E?}zo;0)gMBRT zVoGins$FlffP(o=jJ$9kVZ}A^)y-O#CUpF?xr>GmmrFG))P&CWpkd2i&XnE{-%L|d zf!pPrO<{Sg{gg%JCwq?#>NHPajLt`Z!o#f}MYJeTty7iloj$9kg^ zV(P{5H_ch#=NsyU)V17T&_4zizB&-}_Sy?@9)I#xN*$WC?nvWtpxQV;>oiWE`jN6& z2!{2&D9urouZqM5qs49avJd$u$xy!xWMi%#`2Iq}kzJucTuYvMp3rf=f5=mwY5Y!1 zPv`viX!tJ|3SC{KScHuA%X8-mwZdTSGXq5Vn(V6YcAG4_$Kl!G_yTD^V!^U@s-jOa z(+86K-wS)+7)#1XYTe^=I9Tm!58+auD%)S|2~(k=ihRy1^PvBQtT%#T(@AzLtEI95 zlLLRv-+*o=q z!SPi5dGb^Vs?vwa7VBiOX@c;;!L73)Gp05SV%pH8)_@MC>5U+zy$G+9Q@X!h@to}u z8W^=3Nw66s7nFgnH+!OxKZ6A+Q0n&(@a}e|K(1F?2Q&|7T~OGvM-2hw9^ubmfcI_o zqMtGycI%@+-dl$QRg05nA=Eu48gvM^S^{Hf==w#zE0!ce-74KX$#_tTNil8tIR9m` z=5b62D@TPZ1vxqS?=tb>NDXJ3`|=u(F)F<5*WIysw}{x%Li7qUoCS}#OZAINW1^X5|Er9JAgpUsI6piSs{=dMb#&;dT8WV-;x~#LXz<>Sf}-_gis!g!IBDT!AYvnxK;@%Vvhav0t)w? zz7vMMAFc=#zk=d~JVf76#TeQWSA(@rW()p!e0Xalh?|wIGFwj5$J}7^p<1o}{$Y*X zW=QaTI~AYr?6xwiZ%j2y|L)P&Dp#BxWIQmd zKVELe@vI=oFkKVs-?xrae!)P>MNJX3RN86+*(&F#lHm8~Mgk$k+)~6!1p~g2bH6(L ztG|NuUGVWfv@zmh+-_e2M!^Xs&&hoH`_rpEI1?z_<4|JO16kQHw(-c0`1X4-ltk8> zsw6!>g6Fko3*=hj6?$T5h-s^d()*Jr(-I$A&IEF$=vQv;&iA~BYaJy zoy;MM(J1w;t*OMsZ06^IGV*@y-kNj4D6L0wtOw7@Olr;@6Ib%=a~n`7h)^!Hh|+dv zmwHpQTh(-4XG|e>E(!M(_7n_oSofJ>UHoMzIT=~*v-*AJQV~L}Qj)fNdT>e8J!ToG zyazU)Db638N(zq}=J;hadP8(6k(F+L(U#t^!&k>i(<4_CDrI?#9YX@^vWc7AYDb_X zt6IM8N;qmNE`M+LtyzH8SbfBjmH#RkARf2}uJ+CZ1V|5VcU~sk;|X);55NSrn3){o z$`)&(Kon-6{`rCf@1)L#*7yNVi#WxqVA%Fg$l4vNMw0n(k5YN+powxBKC1S}pKU)x z7Z7P@jXb9hew8^=WBn>LpP-R5`PIPgj7zzI{AK4)-$#0f1L&xoj>O1P_p~5^@o@i? zGjZ+tk`&_y(xq-`LLNLrG|^b4pN@>)>=-ji(`Kp|4Q#wwEkErMrMI%-5{nHc@Hc&V z;tQ{ZU~l%R%XA>uved#cS)eOOG;J=n(yH@UAKK#N6Q&rWW^?&^IE){P`8p0=`9syE z(d-lJF77;}mYt2egKJv92e5RWy{u|LZT1z}qA{XQ2>ErGkC35I&Y|N}Grp6=sK1w{ zk7k=U-Gfc?@jSgJBZf{s>pd;kMUj{z+h=akurk>kns>@MHM`R-;*8oI3Jk3S*$$NY z{Z-Nf&oT#$QTuy)Oyse0qn$|p{xY9ID_Hfv9JB4FdRHMUqC~&O(#dA<9(37voPC-M zQ8L>R@IV_HuUWn~derksr<|CUi!>#d3~PUCS*4^SQuk*&X}poQl5Y8n+)yx#S8UWU zSG2I3@9SkS<}y{#vRc6uEZP^5L54hqh)5=Aj}EO8T9w*KnthezDdi%^l~3d{Wm3`S zQ-z02>4V9|<#9hHv7?rzrDwy=%@@47qHCLuP8YYigc8T3p%NYzZ-!fCFz}perK~G*C8IoXKEnRaShyE!*bTUnA zS&4Zv{t~!(?Ek6xd6K^pa3^)Nq!tr-9}uh8*bCO{#y=-)_I74tHD8*nyuGY$HqMdGbnJ&m$DJXU z{wH6WD7SKTD=>S!q%+>>(kP2-zq0yoNTON2?K0@8+gCY0IK|`lD3+0t5OI0j6VXXS z9j=o{SpfC(BqS$5H!<&oZR=-jrPk>aE-a_z#oh=>`1gw3qTysTECsr~{M}PFgI8)j zrD**Z_2$g~J_c&d_2}_J$-2S)XEj#REs(=WBCT>V%{z0Twj zm8DnV?ZFdQHQhJ^QbUUY4>kAc64WWxFRm0c7NI2A=u-7wiJ0o>OwaOc7L`}H=JX4j zZJ7L+M~E|c@sXh&$A!QWwWb2?pqt-0<9v??;}LQ?^uJvWZhRJK><_{Ll*Lz;1s@sx z$~!auFYgS0>h6~r2z=%7+LjwuE;m2Lgqhnw&CiT8a9Jl5Q>?lw!h54gQbg6F)2*uh z?xw-@ws+b3CRNxwZ9Jz>Hc_BO^r#s_R?M>qL%@Bl)}dye!cO-?VPJ!2-1>~jf7~Lt z@e-TPpfiFg@RKr&`tCxzQIeGpzB9M-^|#TezT5kI00V*#^F;K2^J~t|5227=>2d!Q zx!%%dPsAWHP<8lhWWvDG$l~=sgZx5sVX}`B| zxV!4}I(+N&@DGvr^NFw2(6G$-Go=pb;Ow71kh^iX-1y-6VKk$$i2m&h%!q^Mw=tjk z^ZNS7N&;Xb=P5(|MYn^wGC$--eHMb;kxA*ok_JwK`PIo2#E8bPm+dwW2xS%zc7La- zWxpM)?}W9H`v82x?;<0ny^?SI^JMQTzJaif{PS!!KaG7Lx8Q@bl0d?=kiM`wyPMmA z%n0Pp6Jy%)@3Df7u=9Nc+-Rx_pQ{jp`{`9)koU-SYq37T@$M4T6DSyL)a`LjAFycjv75n-Zqyw@>=suu0`k9I&FvhZGF5EWf5DPm z@J^R3e26%e{%2025&wfP_6-U*zAE=QN0Ihg>FyxY^u?BwBWbz$a)?aCdkPS%@Ecbj zKO}b$5cvgSP7SE52$s2uI}PdP*Vd@n`4yQ}Z_1>mUCj2k&xl zI563a=_iE9$(aq>k?q4uKy)$20UYaqKHBxionK$tzS`73P&ZO8!w(YYm(b0@0m{EO zZ}cC#1c~53I}>=9$@*U(^*wM!%DCFsrzerXwo z7G|Q8c;Ee-Pu%tds&3MSR{hu4`Th!ht<)=|fB9YOu;BXyplaWIOZ+(S=ELEK|6Ai> za8_>@aywq1*dO(GcgC%X0G!2(z5mzVcgIux|BIKFQ79oZD#?)%A)9QnSI8*Ync177 zjF1^wna3{5UM<;5Mlz1#*eiScz20YNefr$*y}#f6{qFDHPk;2_c)wq-=j*xG>v_c4 zFct`nF&`1=Bw-(0lhGgBTmfus7+K7GW5@V$pf8H!Xh|RslVI4m=aCQcYqj|B+V*LEn637ox2ZFXz&8s32PG)y~k&X)}F~g zg+osGdiQZbEU=ibbp9as0|~&_NrnNni#z-`R* zx5^=5@U7_oULrhYfjF&~RbF&|VKF#L;qvBte)LX-F41~=|$P(E-;i9zFYSDPKUR7+fMX7V2W@ujnX zD=x_Pl0Ne)l9v!gZ5xWWI_;fK==pC*h_e`A6`qj*Jv2VC`}!Oo+#^PpnIMWb0E3Uf z5#F7%)O@qu!cBSr#bp9#q5K5jY7gU>Ql>omsEcSHNn$51;Qpyzzz9S@>~&)X1i1$F9k-J=^ky=uo%2$_2XVQFH}1fyyo&U?>>R_$9BfwUN8 zc=G^OaW+gGV?c{1W?y1ypxXD_eWtjG{x}5sV%*?%M~NxKEmdWd;;S`;^hnm7q<@Hj zBFld&5h#c+v7GARn4>KKf`&K%soK~7TdMXS1%)QsKUU)ZA1eWzs((!N|7%nIKb{D1 zkwfZ*0rh78$wt(NFndg2Vb^sPfPzD@2no%fUDsqh;JTV}wmM^6R}J8g6eT*E8}1lO z1#qB@x!!S<>w3HPzoO&!Kc1WZle};53LL3W+K6f4O;c{|lvM^$y3vzkbe#Fj;&oEM z)IFL!LnF$H(0`W!>Ivj173LNyn?28)+Qp?HB7l(fKT?Ee6?mbAPK&gukuILCLSJjK- zQ%#w*LJ;nL-NEPgZxZm~3^_?a5tgoKkm+5t$6BlFb3jf%;$`aDM-K>RKB`l8$>=M+ z{XY%?>}Lg56zd6&i~w7=g+`jmu2#y&MLfv=B-G>_g}#2Az0=plW%fpx;64ahzq1mQ5;wrIhawALR51;?tl~~OisonR>Nz&!l{6v2RdS` zy#pe{xxus#M(B?V!=QML@yW=T zABNN0Os(2NCDFbzJGtelp5U_`J5+BPA{bec7*Z|Sy2ep;Bewj@>uAqp4-OwlE<*WD zlLH+)RZgFKU|GPoz@)N)S5fE><$CJ`-V>ldtb~CeXDin5uPb2ak_mV%o@{losQ~#> zs+_V@A?cDzBSNifb+#*XIm$`6?|hVXg+wBaV8^(zpl%W>B`i=yJuZMrZB;)~>Z@ zt;pEqwW|Xt#hG=K2i@u^mxnxD>#AZAyU|vo$VXgW0Rp_9f!`3Sb%!HlNgIvUzLj>0 zq6$2vFYHXQ{V7AcqzP%@*0?)gMCQufF4q+n-Vm^ZUqw;?MRysGI(2~cZ zTUR0e()Ht>m2OgHIVyf{*bY7wH_oM<=+p81T-KzNqx_<9ajN5NmM?QxZj7yJ29@PC zcjfD`Tbre6#pHTxbxuNIos&F``TCwwpp1UjLAOQs`8B;qTy3VQi=;7Bhn5Tls}4Pg z3>DsZwVG|awUH*jAerxSCg(})0$EeDCK*L1Ih%%>p6duxLo|y{-$)28|5^E7$B2Di zn@phS6;06=5)GPc(t4^qU%#t&J88+?*yr%ha^AUw(0I+}V<6Hxn%<+WZ~5T|##oEx zycMDFQIqDV7JYZpPC?SmbGq4SYB?&(lzmR2!z~evGsN+ zY{t9@IJ`Dp5ueeI;l`or6Y+oT?_R#KH0!l0W6mXWME)=;#>jfO`gTureQ>9|Q&%)p zK<=2SE(#B(8DPuu{iv77IFwOo`uqvuSFh)uS}MH+>H|(=s)g=L)}kxhZ* zLA}F=PI{e%v6H&`uT0V}nQMgVY=bTum0~-Vg^jKj+OQcKTJ9ZcE7!>uBcvbR>C_38 zot1A-#O^(4>pVhd<|h*V%vinjRxOF4!~yjwfr*lN>7Y7Lz?BCLev9(Vj&-*(6BZ+R zUFHE95Z9KcdaWyq(G4SE>4Db`&o(d@zy>(g6g?nmq=x>tX<0jaBU^? zk??0+iO_&vj9;Q0w!ww#sVLSAPYGyzuCrP#RwTc?qn{_Z;QBsG6y4Skvb@BGT zu+|;2;@MT188>#Wo7^d0idk;fFG`PO4Tj_2xtnMn#oV~cc<>F&PJ@X~n%2h5$Vyu% z4LxZS;^uabf~nz~k8O?d5+;{bcw(x$O5ZXJ4B0C&Ffd4HJ5Df|Q;&Om1-CVVd8;fL zUumiA%@owSK~)n_ag9vX8u3`OoQgcry`;r++5D zU!Q@)kn}F-p|XUrPg=Z4{Xj4qeFlu_6{>9%@-jqs)}3M zPEAkWsor6DSUfRu=7s<3q&A+s@S@gr&1N&_>s{^?{4V8d{Tl^r#m(1eMNcCa3W_Na znps!%)&)MZX45)6ZpdNoI+2va#lEp-Yn$0Oo-FI^@kCNROzMC=E33*?LRx_{!dsg` z?pxC<9422>d0si zK4~Oq!K=H704+jU?)nk%Sz`s-(>%If^36^iEVL8cL`#uOy0T;~kK$+6Iw;i2@+8H0 z!Nq>#q|?@S@lG})4iPm}zGV)kGKsqgXX*c3BD->xbNdyDX~-6qYPN;WfvXlgH9AE$ z{>RxIZJP_fbP@4bo)hFNZgq}vpU*%UHTObxih~$wIiu^HEZZJn_OneBp-Tmtsvb@~ zi|%Cg_C_TN`2LWn3A-xD`6cMhi;Wy$7kojePjt}Uv{McALL@F8AYKH0!J3C@1RLx% zL`g}n2DnET=rK;zJ+2>pIPxIPB3MaMRyMFtB`?uMOls1YNP2~kDAP^I;efiQ@Z3GM z%9XuEH33$S@krH5I&QYn@?Q*}dC5O_d9^a?9B8*}@w#A@XT7;%>9Y9jN!<#0yjNmn zmATJ8!8u1#l&E}Lc1>Hs#M6VQ27p{MJS?@*#HULDhm~eL@fKq&YXNV(-8%56W|U{H z#;?c&EEHL6hwGIaVi(RgfQy~3dOec*pC@t}w^()(-$_(i889E8sZnKvSLf82v~Ren(w`xfR0*A^p_Hl| zjnQ9`?R0HvF5o$zaGIR7`h6C?b0ICtuW+`bXvaD???GpZ#6CXxpNrPGpGXGx+!17y z#c+$rElAfE9R6Efo3p%Cb24mAPA`H7;i*_XP~3msby|JrVQ*vqIidl5omMc!rBvO0 zvnQs|`7Pzp1`c^V8{p&UZiKtsz}6hen#}?^)Pq~+1Zk&4cqi*95mXsB8s*N-() zSNbx;Zyi&&WKL1Sesh>Y&@6ij@NMqZq5*f^{%srGt2;vvq*`9*9*~o0DVaYa2pIxg z$&1-Ks-W$QrbMR!KO4SNG4rAu5owe&5C-zDXAW(zejK~QQNFOOlipg@IlP|JB)%k9 z#dB-%j51F+d~};{rS5!YHNW%N)8GUF-4Gi7v!jW(i|uFMYq#<-I}#grQtgnwaO_O= zNIIEt~oq?&PhF+Zj!s&Fgk$L6w|3TaDHKXG6)%8 zw4gJgZX~oZ->hApHT5Ezu71LS3JE-?NcmZho1e4n9L&|Y8cjgc;`++e&9#;~?IMb; zmKxAx*#4|rq^M@g%|0Bofj8C97YmNmHzIacRbzRrpSiCsalaPXBJ3>JsIgdbTOAx~ z8?KqHw61h~i;QUylcyA4QuOmvGnE$fs3?vYA?gU?Klp+yKU=AP)AMV+Xvzp*eJDzU z`BuhJ@MAPsniSAruFub5+qqbAL2LKu_SraUe*CR2cAXRSshz&WLwO;zZdJn5Yc`zU`0m3OJ0k73dg zUGdreYV0?^M(j(UdZGDtN|VDECOp;0K;$&T; z25!iiw$KM#I#;V*6BcRKBYWx=15QKj&Z(c(G)QMXY7e{j+4QS(STtTOx(3``qiKKn zx+KH?u=%%|2OPc<6P=4oH-eQuX{{Jf2iR$T+aefR-;qtW(aKsb+LqJ6>a z9C-D{Nh_xM_s?>h0evq6;B%< z{P6jr^%-FH8ate;whK|rP+5IyTo}U)rQjaGTNbm=qCeX+=@KYLldmJd7y@FbZ)Xcx z`-gTzl!kw1u6byr^QB8g+w*UVvVL|v;-o~v&#g-Gu`b{Fe5-p(JR+Ywv<01OKmb&y zBEYG^cL(P9F838|GP;@DYDK!u5U81{Z!;;rG*1`>3~pqykVY!`1k}G9Pi}0hnTmF8 z>3u93A?}gmX3a(Mp(Zo^6Z+ag$BX4J<>}C^8F+U2BF&Amp5d1plhQRaUA;;ja=q2P z^bU_pI1!TYwhxdo;-mJ60Uzz!FuOB&S*f$u_wXi(l2lWcaNyKlqzu{)rS+SO|&10xDFi@%_TrVQD)>HgQDiUY^N+V z@4cSQ?rs(s%&*owv^HBhO*WKq$YF5%!A>=cMVIwO{VKSUsLNJo3`>a$oXwC%TeHN% z-n_s0QmzLFgM>z|7F$8hT68*9)5w)5X4&Yc?!`MR`|R9WBu7lL**s2KGHS*!vAhdC zN$zl`&P3nSvd`NFT>E&SBLLcd%{SZT7~3aa9(<8*j_f^lDoMye8wA6>=bcs(qHa#j zc}A~b6aAY*G_z* zMEZypyjQh@<hsoS7$ks8pqJ zNZpl)l8IR#VX&-|=M&eNT_3emeHjXksSsOA+_zl2zb>?Mrl$8D>5faPS{jgQFLs;Q zRB*OSR;zklt`I`a3*%9fSpwG^in;UR*?NjhiaJiN>3n)>A977?^>Wdbo5|N3A6-|; zy3D(U$r#2 zCF3bov*jsLr;y0nC?;rr z3tyUlvXFMol@9Amc_!`8V>ys-c_ojMHbcpWi%lC_awrdG)#ELP5GFliBMWjKs^J}X zP!+J<{{~BehM;Clzl?qi2ht;3uS(JaVwF_=&=BQ>`-k^I z>aPQ+!SrjxUpAo&Kx-cvwX6b_*aWtw2%vi>3;p@iwBb_P7mGWVS`Q^jKh6)0P0G`o z3%}+n^8xNj!|!>m%i3L2Hm1B8w@X~;84g*1U#i>~es*&#pI&}Vd2qTI`1;?7YOy2^ zVb$vaC7S8387A&uO6u$_5wf&qiIjx+1&s5>4}kHEsMb}rKCEoy1>BJitIu7>T;o<= ziIrt-QaydD>jYKt#I1u=Hx5v$C?tgIWOs4(T70a0Xm5dN z9Bb3}IvEKrxjgoG%w?1de;pVPVZHiYFdG7g>#v{%* zax2SkJoqM?GI%$l#-%e6f1kscppww)YSG=Mt6Kw;9dH=PJ;W@$wsHw@zwqHvx3#M# z-wMU8>Wu9Hm6E;4mIz9A<*jT);hoLW+c8?KU6uL0t08b5r^{`cv-wfCdUEMA>bI*d zIz0?dyx6&V^?)4|Jbd}Ibq(Fg>Zz9(j7>4dL(Si5XB{y9ES5_y!9cxOiZ|;pDt_>( zMU{&i|L90Kallgu)pK5VPAK0?Bd-yVup4b8?F5rT390T4cjggHl+`#Mp=dA30o?Lr z$wcxK z{5@X!#DCeY&f(nQTtx5;M{S}hGV=IPMi9t|8AWf|56L#2c=h@cY;GR-f(%k2)W&Ob zd!$w$^$K=~mkWebMh8?L8GDvLo!+z0A$97tm)n;oha85Ky5HT4MbJjm0zOW$8{HX2IiQIbX7SjFj z@G-6D?k^6z&o{Hc91${VnK3mhsJ8nU&C?#3`tx2MsO>RJUI$}|u+6sPAlMfxFFl9N zIUfa03w`>>{?HwHEQcQ*k8`>oo8^vD3Vmg*9^i072%_?O(rw4>NBi(=Y`=Cr22;dH zB()ncN>_NH2xw>NS=LbLmpJ5S4&pO^K_)jgx_nF@PLFv*Z9U9F{E;yv5?mRUk9h#Q z#R#8oDsW3TuQE37_n@_8WT8wy_}~=P5q(t{Jna?1m(U2&sQS7380&)KyK=93A5;by ze+X_+$*^ZCss|U9*l662T->4dGz`Emhhl1qF6egR{MUFt$)0*0_EWtshTqOJX{XtA z^fiMltiSM9@Y>ots%VN5XZYX){6TmoCUs#P3mmfukb$N0BHTIBW!a@rY>Bw;K2T`u zG2WkU+594GDp}UpbKxP)HZTEN&m8Z#&-cWnYzs**zR`&ZsLLI>)>7qqtY6_c?eRcT zHLXIsyhS+^CFn{eXy%B0N-9cv6s4TctRv?Iu`Yp)q<1nt^WIFgiFOK?*;l1abimJ&^6{?+Y_QX1!>mXem&bI%CX?j2G;MpH^yb95Nox@-(K zk<$6RHPlv<_DXO?YDvNmr(^FHl%u&J5mdjU>lu5^%YkB{E})+LR9*)Q!;<+x0`Xc$ z*72;Z!PCau`1B>~C58ISEY(b8HhxygB{-FFhdk%AL?Rw9Ru=wcjv8%rF6LRf z>CN|ydG95LOV5T-p8>&Fn9a3^2d4I`gV0x(^7)iOHJ{~Xgvyh?BeuQx!mIkram+@` z3no@UxPl4?)xj4)o~PojvQYEr^pb7zC1-p)t^H+i3EKK!7R<8`$uspr!gyaIfP?1=E@NI!cEKg~YzH2gLGYF3VZ;DrGm;9uWpy zrkY3(9)O4UAl$WsG!OVW&h{|(dVOL_vhIP+dX{Px-|8n@YOEt&7=Gp3#jcZbfyV-& z3*Apw)=bAo-fVo-(R=F@6|TND5bK9~3t47!qrknR^tB3iv)0QLTg?|!uiPACY9aSn zPe*62%9SX{FuZaJDskAVzD(DLCFF2J zgq{4`PcH-CR1vSOS9P%dTTc?c5XkwM|>+pvJ;3r0PRPdW#q2Jg(_y zWoTuV_lWn~9GeWNOiyn*&4O%|Euq|-^}gu(ICU9(&Tk~?^y`bK*HCY{D-l)i@T*x7g#y8B8 z>hD|Z2&=ZOI4a~1u0Yl!8t+X;%Yy5TAE~?EWN{9c#f(rIS(VPfV!4cNxt?&>>_S}| zsn$t3jEj--aL@$CAoElW8^B}31s8CS;O0OWC8k(Xh$@yO{asw}$|6)iEveMVA^_Zl zRkNZ+K|@zA05xCLM9ndkjDJl_=9l;A*nv3qc8v`!Y>nhk%;+%3CvRFTR^ ztfZ`R<%6+Hshs)y>(eO|b~4J7ExccjIF9p+cUii>TO9+oL>Bn}4G}|`C%mfB89z=Q z%e%r~_EL~0t8BR~`)Qd~leh{in~Fs($SO%Jan1Y3UIh`Qq1C7CA{TzSToLV359cr@ z4J9LRPfn#oi+>@ChpY{>ZQ*CG-KL)J5Ra|7nGUY_2c}s`;m;32b=WAn0^_<6jMFoI zlnx$F%hLEu#;+7|g*3~|(5}#41j-eX{i7=cpXMNi4uatmROWJ1ddHgwe7^%PR_%%12za#50-ro=Y>@P`R*q?NgaWNuP(8+(O0G zCj|T?X28NCyL*Ry?(skFtXga~WpaA`!Zv4ieU;s1tED7(R%~y`+$)6v_%Yhg()Lsm zH}8x1X{(hoY_5&gA1!+(Yq@aR;kK;bW9zW;mbSY@PQ;wX!20LAs5v^>vI$Xq3$8QSNu;{AkACP#7QZ+`i&?v>?P#v$if4+Iow8zX@u_ZX;v%i=;H%dfq0ddv>-MJmXH9MT zZ)wz_#PIJ%C{&Xx?daoXdI-xNgzip_k%APjo|iV-rK+7onPAaBnqW{(yPvyNOnC%Z zd(w;YB#&2e;^H*rPMBLtA_Fu0Sb?rXPQCY0L*z7(5O)xq>RgBX@h6NZ!rGgEyvFz3 zoU*7Qr`s};cAtv%##0itwb?+;G^Lj~7bb%aSHHp6Jrl>aR0V`Je#IgQ? z6st&Fzxy35^lVp7n2iSlM`@0VRZVGpTAko4;YLddA||BjU>u?i9Z6kLTf>;X;5PLW zqWyLt>^VHt2Bm`UxH?@x;1N-PaY9-dt?q9tUDBoAbmbSgO;SJ z_Fn%}R0~yc>OY?xUoWLtR64Y!|CmR&Rd96bO~QTUSJTOX6PJfYy&R}~%Sq`ML6uZl zN#ey(y$s_Quz6qsE=;Sv{{pmq9HIv=8xsagHLoWo2+1XST#sJsZ|`Yq0xp$S`DlxR z67tixAv=%sIOBdNgqBPl;VgL(3n^{3yFLk!QXcn96l%&Z!@jju-7GF~Z@tK*&cOvg zKG1MfQJDEQ84IVdtI5)3-kr+ZsdYywU@BkypkjEVJPprfs|toCvS=^|Ci>w}5@$65 zTjuuW=S^~C;n%nA@g$_rOVhOli?-)pC0V+**zqf^I(1S#4@>e| z-bH;1$X&{db(NDF3nI1Xl1rjbZMOS%>s5D;$4VaqushCfhk}baVVTZa7 zND=BE709* zC-dRdND?PhGdbp&>Thn3nHRz0AqXZBs8o60mOJqhM4Sf*L3)gmRA3bh8%R_5SRBn= z?e6vhX|VGl^&K=?AP#&Xa*g=>XcLu@DI+{Z&%EbZ41A49(ENc~6>xJrCLD1|@u8|f za~mV@khrCCQrm;y8JGikRZU z5J(|6^qG;Q;Ltwgc@HB0j%og9i6GpT1Jw^h&z14IC@8bGP~lwiIFeOeCAyi9#6V`z zeuDEVlrK}c63bNK+=jW!&!V5nm7(-f{K*k@RsmC6DD)J)wE8 z%FV%wndh2Q)N$C=+FJ`B*=nL&Jq|Yc2yb5$QsvQhFSW`-Dmeqs>EV+gFy*8{_0C2k zNK?KV`>2UDtl4Wxx zuP!fDfU>iIp*xa^1va<1{Vu72Wa#YUNpbZ{AeDdNwZ@aW8-9?FpBYTq!PxjzBZ6Cb zq4$LcvrC%GX`PyeIUTLbY==E^A73n;O)QN_C#bYi(IyzQabhcU>G||PTkgg~vIprF758rCQ3#XIqtYfxn#_h+UBn3KqAVHR zUvyCjq>GdlU>IFwgVIHIf7C^%Uy}zQo<`q4%pL4v99MbC23L`bRm>)k#9J$@c_Z1hCr|Nq6 zuEK#`IK;u?{yN8e*I`m)u&<aE!N4B_&zWIs zJ>E(IuYfCwLUM#Vx6gc86+6%oPdL>n0gJ4lAK`bb%hglc3tMths4VW3S7lU)4+B9S z<#ZaU8eYve$-G#n?n+A1;J6CYvcq2M6sce;lKw@*!s!beWyz?##%8591r;{00ZrYI zk)Qab&D2H>dO$H6T$PRV|jtVO4tHUG32m)yWZ+)Crmk?~b{guE*RyN^SHHwKXVc zZHFf=MTcCyy*th8(7H9NTOLSHTm@ad-10*|j^aX6b{8=0b9F4PTp5shZ1m}o$@wpj zoDJ99n>3(~ja%DabKu45SCfr~4$cp5y+BspZhOYH^QGiQpHrJIhn>OfquH#Qq6sc* z>wIMgH~(^p)1Yxj8qTZQ2I=qK=m6_+yriMR?rTg%Nhf2THn4i!t1o{rdjfgGzZUzk#zp_8q+4&>b9JWB?yY3My-=g26- z@9%I*Y3=p(NTll|yJ|dr1m0TOXF$z_Z`S=0U@=tSoPXU_DrDMJkjg;#!%V%u6t03K~TN zx^vsRcuBR_klljw3p0Xs52#_o%Fm^*D~pER>M2yfpR1q|ZX^=kKHpPl62XyPz`p*d z#{^8Xn}4mykE~7dYT#X4kbC&bFYRW)*Kom#YTF!x2)^bTw>CZH8ZCz=f65%bPf3ZR zZvr9;O%T>~@}z#O=g+MzARnx)3rmI64TwqXwsUV)dBh5j?Avl^fT)RkK5Xh=>NXFY-0n*e^h{w>Hb2lBRlhDmJ%P=o=pM zlpL=ry?-GSN<_5epRnvSZ?8L?`WAoF&q>;^8!AV+r{NeG`U_ckRt;q)r5mT-dmtPG zd`+^Jcxq15(e*t=B6m=g@dlirKvryDFe5Zc<3`-r&qI=kuRxPDZVymkh9oIKNfz05 zPr)g~l#ViL3|FlVYLbT7_emPQEXl9f!2}{whYf{XlXs!~>Bd7{Bbu0woS>kLZ6~cF z$Nm;7Pa$W+fcOOABxTR}pdw_f85`A(ASMI-vW|$V3ZD>F9}+>BfhBaMI6B2v5%>6(xUIq{1=9*5YqD|=fHeGebbcsN z1W27*c?y|KIG1ftaG0fJxk7cuNRU9xg?d%$ym%~oTX3I3$+lps55lE$7bIs;*9W3^ zZJ>c7DDr;qN5evFN(^!s2kqm~9&@b}4siU&>TM0h!5Az~DY9+ELxd6Xi`y5v{Tn5@ zmA+#rl`LWm4X5GYB%|<%@whCBbOxiHKo@8g&cxX$x09v}_jY)1RHJ&I*K-OYeGjz8 z=vJ)MgC5QE2+f>p%5}mQYBf}u027X@W)AD`tjA~xb(s{y2t+`-D7A8KM~KAR=q)j} z^>81s(*o==_NZ~8yF!VZEmFDfaO3I4{sW`K1Tdl@v{m2Zvma&FPX!PrXVX6Oy6vTB zih7Z50q4-R&@GK)_*Jf4n{h4=f;+=Zlaj01Y^=6})mt%ju)BEFe4C*cL4Q9~+UYd{ zqh3|gZ0LKDOs4t(e;&-TyGcJ!sQ)vw=jM4VH=t1J-L@apFItMVGHtxih^Z5+H4sy=q4F&^RSZD=9GZO#&EIMm)whKD zI~$NOq%mN0Z&T+E2p!<`26LzK##5nBB8)F6TgM@+#g=AA!3kUiMS3g;(aOp{P@T z!O_39rX5IH(@~gt|3&EiK9giqpt@fLV)|Sk0dDc-oejo}dfE?W9KE941py6FeLGG7 z;RSf>XxMSymH69*iFMOlFwh+rLqI^f+?n`_rfN(BTq_FC)J1Qy2&nO4QgIT?)!wXtUgg7P z*vX|BBGpEFC)^3iKBYbrljJ1(4N$IC}>?YGj^$jj20Q z>B_rl9`Wki4KS8zw;e?Qnm$JIQJiu2I?p>OG*giE!$OWxaSHh2v^SI-?s<`|&psy< z_g^Q;VP)Kq#YS*p?mo^h!<|bq(i|QKPOylQj*gx`y;0)4adFwYwT*}iCGTnYX(sw# zG6ntexj?gTjKDNn3nXg-ov@n#2CC@~V_l06v@aCc-P3G*DcSo5Jy1%d_9KqtWth3Q z-cC$gm0fCJA!w`_Pdc6eC6jTO!@Bh7^O6H&d<*y(W0C_EJU*k(gG|R-IzOmW$-bg1#b4ViT(}jYhoR@3U`a&yqN)jznxjNrI z)wnF1NWX*L*T>!Op!mdM{y)GDbhM{?keWy%b?Z;qp3tu4X{u#ydM!0im$nJ)vm z`ftlO%>2}$4H?lJB5==q<~*}Lz(XuUi7Og)=A{alm)^Fodn-!P(nuBPOTSLVD`5hL^8M}jv1jn}?yg@^VpuDBx6QIv_Z$@jhVl9& z&oFUhMt>{8%f8tc!h^=!98^hTYop!mj@ZuuN~sJ(j$pVwM$*yRb+FL8|8+>V&Rvyj ziPUlEXkVmA#C6;6Hk-fN@q%tEH5m)V=&@pfmZpFa)Z#ejiSGi&8E1f}$R#?W_j?b9 z4N4RU=k1QPEQhZ4?aCll5&9g)OJmGE*0bkJ)ZvE<$1u22s8x7>MvsDj*0^l%PJx)- z;cR%miTzI908383_&Vf^tjf`S7^n?(!`J1M{tvJ#1)22Z`%{M~Ihz}U(89EOnJ({j zMgU=ovBRE%gX)pYz!|76A3F&_B$DQxv~s-M_;GR|tIKD!*DO*1@L9F}99uEN9RtSl z`|)@6D+hxCW7v(%D;a!XZY3JS$-1>xt8c7$r}xRG?$aa8hF|aLdECPk5jA=rJwx1C zr?%Der2>UD^O=H`{p#T+axLgrZyEtjrRCy?_EGlir5Z87_5uJn!vr9^QXWUf4@0 zz$Ymqfx)C~t@S;bEpQ=(g?-t&dOhpH2>=ing6Y|-Iu^(NqpGa}rfG5gBOjIlljVT6 z*+ucLBV=>UdtYcsgbF?Sxy0jhlh;**52#-z2-8%rLc={q!{e^GZ0u&(1`HECT4411f14umYeu$S>plIZDzKkE~eQUb#ye>~zI z95V3xd0g}feL?Pn;n6}X9IZJ3s(fL&zd`gJQ~_vE>b?kZa?JB|`4BYNs1;a?W6drm z9T?<7Q-Qnw0wNSx;6GYuO$AtxB7h0?+czYj$!b5}bG1L%N zu*4h4sr!m%q+bdCmrmUeJXiZ`gz*)e?7A(Q|9&SQ_}dZm-~I}|!MzYMADt;jP1giZ z*n*Qi#qbg?l$kVWLJqGUv2Vom?sDbbWC0`R#IClQfAzxp)d7+&!Tp4^Cq(ZM;?8*l64 zuO{BbO-JYd&>v)Lzqg%ypLF%^KOrf24Xs9)2L606B@nm<5s zo6ics!7W_Odm;*8o%q|`T$pu#LF0ZBz^B~oNDQCO0w6vSqv%4H3H-;_vdK8~hIDlB z8$>?|3g&a@SbG4T_xh)aV-QvVY*vdXY9~8<-0b1_ihMlGt04RH{k@+-!UbC#Gmw@& zgT~}Cc=HqWKcM^<#tJolcJRr~tq4V24ErSDZH~Te$_eqmUb;Hjv{Sw5_t*=;;j<7M zoteswW{zYAz>m=xPssk7uj!sj)k@&%pr!u}$%%p+Ghd?w<=$@i!iJhyy{4UN)=F=2 zKh_$sR>m-!i_f&9&Nle2E^@YNTFIkoTApMB(ikqkstttG%y#zlAB0m12s*XT$Z519 z65+&A^@Uk`x@uK#el_FdV!n%JRp&>wPHl-XA46&N&!2+Nv&$u&#jGa|q$hy;!V>2A z=w){KTVw&}_U8DwFRUe0*9ZN#S{i5z#P-lEvdwG#v=jy>BtA8pOV-Gof|{~1Nu~6N zC+1l7$F2-ca4Pbh=g~()Jeoa!y%CtsE*kY(G7+tD_J1b(M6OY`-KN;+vbNX|QB0+b zQGhl|gZIeU8eMqHTxU4oc7jhiw!gO2faGT*(WN9UmpyulXuRD8>I9Dy=J(gTi5TxC zr{_@ApG6%hnaGC;kv+fJ~vc+N=>^(W0zg~NIoqXoQ0?0uL-qn^F{R_5ig(Ki3~V_VZ` z($*8e4r6&aNhq}2vpwy%YAPB<#LOlNvgw07{eVrNwc5BRvuPNHE;jT)lb!x;zZAT~ zLwO0N5;-x@bF@G|&nt;0F`^0GMOI3oMllI+%DS^&?B!Ur+}ovHAv}xzv(|nqe|gr8 z>gmU-Gwa+@-)?VonRm9YUCD`1CP%Bd_i;d%T97Cc{jBjY^ui@Bl>kiN>x+hPOmt8P zk!@F{|1y#`qu>z4B4g@<`PMRJpy!Pxjia?~?Mgw#Redx^iC6;Ke@^N86N_`=mf~L6 zm(74yz&>veWO(K@8eQMOf%!5Ag|>oeg#e1$sM<(tHHwonfJ$|V@olT*J{WAV%LD$4 zi^%-hN5cFOyqFE>liN6p!~i4Vv*E8^r%8~ZM(v%BdPl^_r`MNR&|giGdz3SZClrZZ z$;S$KMCCMjb$`8ZG9%zI*SHKl^cq$^S%`2u%}WwC3@fzlf&hyn$4}IG5682O8BBX1 z;2gz{3*8x>8?wjAMmd$wrYm4{1B9CwjDuTf=ysvT{$Swi9YjnLCU66YZq1bqx3#z{YCF-sQuBAF;nqxWZ(U(|z+N&Jy7U z=O~oFR)c~d*~@3+u6n)&ZvJ8ok3C9BO%qV#KVDpxhsBWC3VqxjP@~6ij6dKQs@qBj z{=_k=fMcjI3TjMwF#mmkh-@fHHwpCoBCCz2H&UsT!S}&JEUv1`Y0R3wU`+y5rG0-` z6MXZ=skqnHS}wkZgNam<0Yw4&K{?tZ485E0arpbT|f$>!FNRj z^Ml55o1-@*+zT)on2dz@s#fslAQTyDr*H%hsIfkaZ)>i$ti&uZCJr2>2+>`I`imu4 zC$l-4GP^brf(F=GU&P>T2zoEu1FnbR*A!l_UFq}4vcqtc7T_qOs;URy&-W8DsTl@O zy-~g;B#z$Qo{sK$`%B^%s7?C`7O54=gak%8`>l2NclHy#Xd9fRj1u4<_UW z5Q}3(`jfk$pnnh{!D%&#J8|a}8uFk0$~mRI2)ZAXGWK+n1f{sowhycQ3eqq@QH0xe zIYx(P5CH$WWd6)g^xDNI@m2X+iTj;nRgZq9gbn?mdz~Z}wO4?}j~y8X{yKL1pMH3n z4Kzy5yc_SYy8!ag?yhkhtbvQX@TWC0K*STbK7b7nv9A%&J%BBX9Q4s;GOB+Mr-kIj zz!_xp!42Q&Ud|Q7-WRWfsnMj(m@VOZ7DRuwA0nhctE->DdT?hz3Q~ssCd+Ry zqwpi*4p#ZvrUcT$KOrBCT^i+lHk#>*UfqBfSQun(Rlr}L2$z}#D6J3nLfD9V_h|4_ zgYFslsS@bbL63B0})S4gX)h0G(bpy|6c9&_eKTH8DZ0LkN<}aDiUx{>89k7l1T2=h7EUFaW_Gv$c3w+D~1AdGb zK*usau@`DR4N~X%)z=({U#lP4aa$_C|O2DU!PE+cn$OX{jekCC&XZeSCR9h zEj=!4;}42r*N|11i2wdMkcG81H~%f-KvBcT9%rZ|D;NaDTPBBd;uEZ!*4Byvvh2@N z{`NvM(^Cq6JG(-r%ptFQKeali?bU_HL7WuAn3L?qsR172Gv?pnLwzDb?DN>m&Lpfb z3gZ}J*#V?OWDPdYMt!4F{~m1jv4X6Q$@fRFoP>s21SiRJUOCx33d5M+pBqygycZ+9 z(j6Is1%niLj<6fr=*0T>2eAvDr=J^JOmp8Hvk%a8IrJNqYx#YON=A{Llbp1>$2f zd-f1$$%u=pVKK9Npuhh?8dONaZw-F_z3zj>;qO@8{iGSl5Jya|!(Z#t->~=dIT`?> z!;f6Uipsx<1!64&O<9UlJjsq&8T4rd)aN_-bot+TX7tJAAj8ctE%^8N`D-lv&)V6O z;VE?fZyF*j zPyW@3X2DlAp5gbc-2?|%pmO``jNSlV@ObRE65;P!a#zfVO*Lx|3^Y zI%9aJzvSYP7vG=zYi9i`kXAreqm1#7z`^?WLAw8#gWbe$HtcUU-2K!4jh%hWam1L6G%dLZe#;Kv64!%r zdbravAI2U1m(aBfy&5U8T2`akpOR5ONXzenDWV6Mrn0E}w`|uB0{Tb#B?C8JR$U?J zFAnr?({3IrD<@R7{)d=N9jJ_KJMG}#5A<(;5(e5q7-pv&{^ZjBE%M=j%~Ya3-uTxz zdY43~AABl-sJoaH-2NBk;itDn)B&NgG!`h5?5(@Vh1a=9B#GSur%shH}ct z=O_Ni27kjUbfAwwcA#hSH^=^OnG!=oC3TSZhh@I~SAhKO85h7I^R0TLe~Xu&F#Mf! ze6j(d)OlI{)Ufi~W { const ethers = hre.ethers; const networkName = hre.network.name; - const [deployer, governance] = await ethers.getSigners(); + const [deployer] = await ethers.getSigners(); if (!LZ_CONFIG_GATED_MODULES[networkName]) throw new Error('invalid network'); - // using the sandbox deployment for module whitelisting - const lensHub = await getLensHubDeployed('lensHub sandbox', networkName, governance.provider); - - // const followModule = await deployContract( - // ethers, - // networkName, - // 'LZGatedFollowModule', - // [lensHub.address, LZ_CONFIG_GATED_MODULES[networkName].endpoint, [], []] - // ); - // - // const referenceModule = await deployContract( - // ethers, - // networkName, - // 'LZGatedReferenceModule', - // [lensHub.address, LZ_CONFIG_GATED_MODULES[networkName].endpoint, [], []] - // ); - // - // const collectModule = await deployContract( - // ethers, - // networkName, - // 'LZGatedCollectModule', - // [lensHub.address, LZ_CONFIG_GATED_MODULES[networkName].endpoint, [], []] - // ); - - const followModule = await getContract(ethers, 'LZGatedFollowModule', deployer); - const referenceModule = await getContract(ethers, 'LZGatedReferenceModule', deployer); - const collectModule = await getContract(ethers, 'LZGatedCollectModule', deployer); - - console.log(await governance.getAddress()); - let tx; - console.log('lensHub.whitelistFollowModule()'); - tx = await lensHub.connect(governance).whitelistFollowModule(followModule.address, true, { gasLimit: 50000 }); - await tx.wait(); - - console.log('lensHub.whitelistReferenceModule()'); - tx = await lensHub.connect(governance).whitelistReferenceModule(referenceModule.address, true, { gasLimit: 50000 }); - await tx.wait(); - - console.log('lensHub.whitelistCollectModule()'); - tx = await lensHub.connect(governance).whitelistCollectModule(collectModule.address, true, { gasLimit: 50000 }); - await tx.wait(); + const key = networkName === 'mumbai' ? 'lensHub sandbox' : 'lensHub proxy'; // using sandbox for module whitelisting + const lensHub = getLensHubDeployed(key, networkName, deployer.provider); + + const followModule = await deployContract( + ethers, + networkName, + 'LZGatedFollowModule', + [lensHub.address, LZ_CONFIG_GATED_MODULES[networkName].endpoint, [], []] + ); + + const referenceModule = await deployContract( + ethers, + networkName, + 'LZGatedReferenceModule', + [lensHub.address, LZ_CONFIG_GATED_MODULES[networkName].endpoint, [], []] + ); + + const collectModule = await deployContract( + ethers, + networkName, + 'LZGatedCollectModule', + [lensHub.address, LZ_CONFIG_GATED_MODULES[networkName].endpoint, [], []] + ); + + if (networkName === 'mumbai') { + const mockSandboxGovernance = getMockSandboxGovernance(deployer.provider); + console.log(`whitelisting modules through mock sandbox governance: ${mockSandboxGovernance.address}`); + + let tx; + console.log('mockSandboxGovernance.whitelistFollowModule()'); + tx = await mockSandboxGovernance.connect(deployer).whitelistFollowModule(followModule.address, true, { gasLimit: 100000 }); + console.log(`tx: ${tx.hash}`); + await tx.wait(); + + console.log('mockSandboxGovernance.whitelistReferenceModule()'); + tx = await mockSandboxGovernance.connect(deployer).whitelistReferenceModule(referenceModule.address, true, { gasLimit: 100000 }); + console.log(`tx: ${tx.hash}`); + await tx.wait(); + + console.log('mockSandboxGovernance.whitelistCollectModule()'); + tx = await mockSandboxGovernance.connect(deployer).whitelistCollectModule(collectModule.address, true, { gasLimit: 100000 }); + console.log(`tx: ${tx.hash}`); + await tx.wait(); + } }); diff --git a/tasks/gated-modules/set-follow-module.ts b/tasks/gated-modules/set-follow-module.ts index 99e1287..879e0f1 100644 --- a/tasks/gated-modules/set-follow-module.ts +++ b/tasks/gated-modules/set-follow-module.ts @@ -1,7 +1,7 @@ import { task } from 'hardhat/config'; import { LZ_CONFIG_GATED_MODULES, - SANDBOX_USER_PROFILE_ID, // owned by `deployer` + SANDBOX_USER_PROFILE_ID, TOKEN_CONTRACT, TOKEN_THRESHOLD, TOKEN_CHAIN_ID, @@ -13,9 +13,6 @@ task('set-follow-module', 'sets the LZGatedFollowModule on our test profile').se const ethers = hre.ethers; const networkName = hre.network.name; const [deployer] = await ethers.getSigners(); - const accounts = await ethers.getSigners(); - console.log(await Promise.all(accounts.map(async(account) => await account.getAddress()))); - return; if (!LZ_CONFIG_GATED_MODULES[networkName]) throw new Error('invalid network'); diff --git a/tasks/gated-modules/test-follow-module.ts b/tasks/gated-modules/test-follow-module.ts index 3a6e4a5..58690c8 100644 --- a/tasks/gated-modules/test-follow-module.ts +++ b/tasks/gated-modules/test-follow-module.ts @@ -19,7 +19,7 @@ const ESTIMATED_FOLLOW_FEE_GWEI = '1000'; // derived from `npx hardhat estimate- task('test-follow-module', 'try to folllow a profile which has set their follow module to LZGatedFollowModule').setAction(async ({}, hre) => { const ethers = hre.ethers; const networkName = hre.network.name; - const [deployer] = await ethers.getSigners(); + const [deployer, governance] = await ethers.getSigners(); if (!(LZ_CONFIG_GATED_MODULES[networkName] && LZ_CONFIG_GATED_MODULES[networkName].remote)) throw new Error('invalid network'); @@ -27,12 +27,13 @@ task('test-follow-module', 'try to folllow a profile which has set their follow const provider = new providers.JsonRpcProvider(destination === 'mumbai' ? ALCHEMY_MUMBAI_URL : ALCHEMY_POLYGON_URL); const lensHub = await getLensHubDeployed('lensHub sandbox', destination, provider); const lzGatedProxy = await getContract(ethers, 'LZGatedProxy', deployer); - const followerAddress = await deployer.getAddress(); + + const followerAddress = await governance.getAddress(); const nonce = (await lensHub.sigNonces(followerAddress)).toNumber(); const followWithSigData = await getFollowWithSigParts({ chainId: hre.network.config.chainId, - wallet: deployer, + wallet: governance, lensHubAddress: lensHub.address, profileIds: [SANDBOX_USER_PROFILE_ID_TWO], datas: [[]], @@ -48,7 +49,7 @@ task('test-follow-module', 'try to folllow a profile which has set their follow TOKEN_CONTRACT, TOKEN_THRESHOLD, followWithSigData, - { value: utils.parseUnits(ESTIMATED_FOLLOW_FEE_GWEI, 'gwei'), gasLimit: 100000 } + { value: utils.parseUnits(ESTIMATED_FOLLOW_FEE_GWEI, 'gwei'), gasLimit: 210000 } ); console.log(`tx: ${tx.hash}`); await tx.wait(); diff --git a/tasks/helpers/constants.ts b/tasks/helpers/constants.ts index 1482d67..18abc85 100644 --- a/tasks/helpers/constants.ts +++ b/tasks/helpers/constants.ts @@ -49,8 +49,11 @@ export const LZ_CONFIG_GATED_MODULES = { // } }; -// @TODO: these should change per setup -export const TOKEN_CONTRACT = '0xDBF49B20eC2E48ef4CeEb79927eA4Ac2eFc2c961'; // goerli -export const TOKEN_THRESHOLD = '1'; -export const TOKEN_CHAIN_ID = LZ_CONFIG_GATED_MODULES.goerli.chainId; -export const SANDBOX_USER_PROFILE_ID = '67'; +// @TODO: all the constants below should change per setup + +// https://hq.decent.xyz/5/Editions/0xBbD9a6186C084F7148FA9787E94828faF769c9A3 +export const TOKEN_CONTRACT = '0xBbD9a6186C084F7148FA9787E94828faF769c9A3'; // the ERC721 for token gate +export const TOKEN_THRESHOLD = '1'; // one token required to follow +export const TOKEN_CHAIN_ID = LZ_CONFIG_GATED_MODULES.goerli.chainId; // where our `TOKEN_CONTRACT` lives (goerli) + +export const SANDBOX_USER_PROFILE_ID = '322'; // thereisnosecondbest2.test diff --git a/tasks/helpers/lens.ts b/tasks/helpers/lens.ts index 425a435..a26b86c 100644 --- a/tasks/helpers/lens.ts +++ b/tasks/helpers/lens.ts @@ -13,6 +13,7 @@ const lensAddresses = { 'interaction logic lib': '0xefd400326635e016CbfCc309725D5B62FD9d3468', 'lensHub proxy': '0x60Ae865ee4C725cd04353b5AAb364553f56ceF82', 'lensHub sandbox': '0x7582177F9E536aB0b6c721e11f383C326F2Ad1D5', + 'mock sandbox governance': '0x1677d9cc4861f1c85ac7009d5f06f49c928ca2ad', } }; @@ -20,6 +21,12 @@ const collectAbi = [ "function collect(uint256 profileId, uint256 pubId, bytes calldata data) external returns (uint256)" ]; +const whitelistAbi = [ + 'function whitelistFollowModule(address followModule, bool whitelist)', + 'function whitelistReferenceModule(address referenceModule, bool whitelist)', + 'function whitelistCollectModule(address collectModule, bool whitelist)' +]; + export const getLensHub = (key: string = 'lensHub proxy', provider: any, networkName: string = 'mumbai') => ( new Contract(lensAddresses[networkName][key], collectAbi, provider) ); @@ -32,6 +39,10 @@ export const getLensHubDeployed = ( new Contract(lensAddresses[networkName][key], LensHub.abi, provider) ); +export const getMockSandboxGovernance = (provider: any) => ( + new Contract(lensAddresses['mumbai']['mock sandbox governance'], whitelistAbi, provider) +); + type FollowWithSigDataProps = { chainId: number | undefined; wallet: any; // Signer From 0746ef8c1568c3da5133d73a228b7afc0099c8f0 Mon Sep 17 00:00:00 2001 From: Carlos Beltran Date: Wed, 21 Dec 2022 16:07:22 -0600 Subject: [PATCH 13/14] wip - testing on testnet --- contracts/LZGatedCollectModule.sol | 8 ---- contracts/LZGatedFollowModule.sol | 26 +++++------ contracts/LZGatedProxy.sol | 2 + contracts/LZGatedReferenceModule.sol | 7 --- contracts/lz/LzApp.sol | 27 ++++++++++-- contracts/lz/SimpleLzApp.sol | 5 ++- scripts/utils/goerli-contracts.json | 2 +- scripts/utils/mumbai-contracts.json | 2 +- tasks/estimate-fee.ts | 8 +++- tasks/gated-modules/deploy-modules-source.ts | 46 ++++++++++---------- tasks/gated-modules/deploy-proxy-remote.ts | 10 ++--- tasks/gated-modules/estimate-fee.ts | 7 +-- tasks/gated-modules/force-resume-receive.ts | 27 ++++++++++++ tasks/gated-modules/set-follow-module.ts | 5 +++ tasks/gated-modules/set-trusted-remotes.ts | 24 ++++++---- tasks/gated-modules/test-follow-module.ts | 24 ++++++---- tasks/helpers/constants.ts | 1 + tasks/helpers/lens.ts | 19 ++++---- test/LZGatedFollowModule.spec.ts | 16 +------ 19 files changed, 158 insertions(+), 108 deletions(-) create mode 100644 tasks/gated-modules/force-resume-receive.ts diff --git a/contracts/LZGatedCollectModule.sol b/contracts/LZGatedCollectModule.sol index 305ce66..ba1c867 100644 --- a/contracts/LZGatedCollectModule.sol +++ b/contracts/LZGatedCollectModule.sol @@ -29,9 +29,6 @@ contract LZGatedCollectModule is FollowValidationModuleBase, ICollectModule, LzA uint256 balanceThreshold, uint16 chainId ); - event MessageFailed(uint16 _srcChainId, bytes _srcAddress, uint64 _nonce, bytes _payload, string _reason); - - error NotAccepting(); mapping (uint256 => mapping (uint256 => GatedCollectData)) public gatedCollectDataPerPub; // profileId => pubId => gated collect data mapping (uint256 => mapping (uint256 => mapping (address => bool))) public validatedCollectors; // profileIdPointed => pubId => profiles which have been validated @@ -102,11 +99,6 @@ contract LZGatedCollectModule is FollowValidationModuleBase, ICollectModule, LzA } } - /** - * @dev not accepting native tokens - */ - receive() external payable { revert NotAccepting(); } - /** * @dev Callback from our `LZGatedProxy` contract deployed on a remote chain, signals that the collect is validated * NOTE: this function is actually non-blocking in that it does not explicitly revert and catches external errors diff --git a/contracts/LZGatedFollowModule.sol b/contracts/LZGatedFollowModule.sol index 6ffa3cc..1516a97 100644 --- a/contracts/LZGatedFollowModule.sol +++ b/contracts/LZGatedFollowModule.sol @@ -24,9 +24,6 @@ contract LZGatedFollowModule is FollowValidatorFollowModuleBase, LzApp { } event InitFollowModule(uint256 indexed profileId, address tokenContract, uint256 balanceThreshold, uint16 chainId); - event MessageFailed(uint16 _srcChainId, bytes _srcAddress, uint64 _nonce, bytes _payload, string _reason); - - error NotAccepting(); mapping (uint256 => GatedFollowData) public gatedFollowPerProfile; // profileId => gated follow data mapping (uint256 => mapping (address => bool)) public validatedFollowers; // profileId => address which has been validated @@ -105,11 +102,6 @@ contract LZGatedFollowModule is FollowValidatorFollowModuleBase, LzApp { uint256 followNFTTokenId ) external override {} - /** - * @dev not accepting native tokens - */ - receive() external payable { revert NotAccepting(); } - /** * @dev Callback from our `LZGatedProxy` contract deployed on a remote chain, signals that the follow is validated * NOTE: this function is actually non-blocking in that it does not explicitly revert and catches external errors @@ -139,11 +131,19 @@ contract LZGatedFollowModule is FollowValidatorFollowModuleBase, LzApp { // allow the follow in the callback to #processFollow validatedFollowers[profileId][follower] = true; - // use the signature to execute the follow - try ILensHub(HUB).followWithSig(followSig) {} - catch Error (string memory reason) { - emit MessageFailed(_srcChainId, _srcAddress, _nonce, _payload, reason); - } + ILensHub(HUB).followWithSig(followSig); + + // (bool success, bytes memory reason) = HUB.call(abi.encodeWithSelector(ILensHub.followWithSig.selector, followSig)); + // // try-catch all errors/exceptions + // if (!success) { + // emit MessageFailed(_srcChainId, _srcAddress, _nonce, _payload, reason); + // } + + // // use the signature to execute the follow + // try ILensHub(HUB).followWithSig(followSig) {} + // catch Error (string memory reason) { + // emit MessageFailed(_srcChainId, _srcAddress, _nonce, _payload, reason); + // } delete validatedFollowers[profileId][follower]; } diff --git a/contracts/LZGatedProxy.sol b/contracts/LZGatedProxy.sol index e48a7bd..10b3ade 100644 --- a/contracts/LZGatedProxy.sol +++ b/contracts/LZGatedProxy.sol @@ -59,6 +59,8 @@ contract LZGatedProxy is SimpleLzApp { ) external payable { if (!_checkThreshold(follower, tokenContract, balanceThreshold)) { revert InsufficientBalance(); } + bytes memory customAdapterParams = abi.encode(1, 750_000); + _lzSend( remoteFollowModule, abi.encode( diff --git a/contracts/LZGatedReferenceModule.sol b/contracts/LZGatedReferenceModule.sol index ecf65f2..b8951d1 100644 --- a/contracts/LZGatedReferenceModule.sol +++ b/contracts/LZGatedReferenceModule.sol @@ -25,10 +25,8 @@ contract LZGatedReferenceModule is FollowValidationModuleBase, IReferenceModule, } event InitReferenceModule(uint256 indexed profileId, uint256 indexed pubId, address tokenContract, uint256 balanceThreshold, uint16 chainId); - event MessageFailed(uint16 _srcChainId, bytes _srcAddress, uint64 _nonce, bytes _payload, string _reason); error CommentOrMirrorInvalid(); - error NotAccepting(); mapping (uint256 => mapping (uint256 => GatedReferenceData)) public gatedReferenceDataPerPub; // profileId => pubId => gated reference data mapping (uint256 => mapping (uint256 => mapping (uint256 => bool))) public validatedReferencers; // profileIdPointed => pubId => profiles which have been validated @@ -114,11 +112,6 @@ contract LZGatedReferenceModule is FollowValidationModuleBase, IReferenceModule, } } - /** - * @dev not accepting native tokens - */ - receive() external payable { revert NotAccepting(); } - /** * @dev Callback from our `LZGatedProxy` contract deployed on a remote chain, signals that the comment/mirror * is validated diff --git a/contracts/lz/LzApp.sol b/contracts/lz/LzApp.sol index 9c43e96..260c6b2 100644 --- a/contracts/lz/LzApp.sol +++ b/contracts/lz/LzApp.sol @@ -9,7 +9,7 @@ import "./interfaces/ILayerZeroEndpoint.sol"; /** * @title LzApp - * @notice LayerZero-enabled contract that can have multiple remote chain ids + * @notice LayerZero-enabled contract that can have multiple remote chain ids. */ abstract contract LzApp is Owned, ILayerZeroReceiver, ILayerZeroUserApplicationConfig { error NotZeroAddress(); @@ -17,6 +17,9 @@ abstract contract LzApp is Owned, ILayerZeroReceiver, ILayerZeroUserApplicationC error OnlyEndpoint(); error RemoteNotFound(); error OnlyTrustedRemote(); + error NotAccepting(); + + event MessageFailed(uint16 _srcChainId, bytes _srcAddress, uint64 _nonce, bytes _payload, string _reason); ILayerZeroEndpoint public immutable lzEndpoint; @@ -49,17 +52,33 @@ abstract contract LzApp is Owned, ILayerZeroReceiver, ILayerZeroUserApplicationC } } + /** + * @dev not accepting native tokens + */ + receive() external virtual payable { revert NotAccepting(); } + + /** + * @dev receives a cross-chain message from the lz endpoint contract deployed on this chain + * NOTE: this is non-blocking in the sense that it does not explicitly revert, but of course does not catch all + * potential errors thrown. + * @param _srcChainId: the remote chain id + * @param _srcAddress: the remote contract sending the message + * @param _nonce: the message nonce + * @param _payload: the message payload + */ function lzReceive( uint16 _srcChainId, bytes memory _srcAddress, uint64 _nonce, bytes memory _payload ) public virtual override { - if (msg.sender != address(lzEndpoint)) { revert OnlyEndpoint(); } + if (msg.sender != address(lzEndpoint)) { + emit MessageFailed(_srcChainId, _srcAddress, _nonce, _payload, 'OnlyEndpoint'); + } bytes memory trustedRemote = _lzRemoteLookup[_srcChainId]; if (_srcAddress.length != trustedRemote.length || keccak256(_srcAddress) != keccak256(trustedRemote)) { - revert OnlyTrustedRemote(); + emit MessageFailed(_srcChainId, _srcAddress, _nonce, _payload, 'OnlyTrustedRemote'); } _blockingLzReceive(_srcChainId, _srcAddress, _nonce, _payload); @@ -89,7 +108,7 @@ abstract contract LzApp is Owned, ILayerZeroReceiver, ILayerZeroUserApplicationC function setZroPaymentAddress(address _zroPaymentAddress) external onlyOwner { zroPaymentAddress = _zroPaymentAddress; -} + } function forceResumeReceive(uint16 _srcChainId, bytes calldata _srcAddress) external override onlyOwner { lzEndpoint.forceResumeReceive(_srcChainId, _srcAddress); diff --git a/contracts/lz/SimpleLzApp.sol b/contracts/lz/SimpleLzApp.sol index 733d548..f5bc486 100644 --- a/contracts/lz/SimpleLzApp.sol +++ b/contracts/lz/SimpleLzApp.sol @@ -93,9 +93,12 @@ abstract contract SimpleLzApp is Owned, ILayerZeroUserApplicationConfig { address payable _refundAddress, bytes memory _adapterParams ) internal virtual { + // remote address concated with local address packed into 40 bytes + bytes memory remoteAndLocalAddresses = abi.encodePacked(_remoteContract, address(this)); + lzEndpoint.send{value: msg.value}( remoteChainId, - _remoteContract, + remoteAndLocalAddresses, _payload, _refundAddress, zroPaymentAddress, diff --git a/scripts/utils/goerli-contracts.json b/scripts/utils/goerli-contracts.json index 6af68fb..6c8d87c 100644 --- a/scripts/utils/goerli-contracts.json +++ b/scripts/utils/goerli-contracts.json @@ -1,3 +1,3 @@ { - "LZGatedProxy": "0x2543a1a1eA5D6F3153b22A0772A0431AF0002392" + "LZGatedProxy": "0x0Cf77dC470497b6D80194F4D028b95feAC14d274" } \ No newline at end of file diff --git a/scripts/utils/mumbai-contracts.json b/scripts/utils/mumbai-contracts.json index 737981a..580420a 100644 --- a/scripts/utils/mumbai-contracts.json +++ b/scripts/utils/mumbai-contracts.json @@ -1,6 +1,6 @@ { "OmniSBT": "0x1a255753a44EEedbf6d3Afe65A8F006F8C513b0C", - "LZGatedFollowModule": "0x8cf0c18f335a5316359F310497870F99f4Ff9e10", + "LZGatedFollowModule": "0xa079d888415900c05ddF50DBaffAcF168aBfE467", "LZGatedReferenceModule": "0x46720e2AC58f69d20A952F51AE73cFE0ae4a73b7", "LZGatedCollectModule": "0x2f4A8D959fD7227d0942D220d3E8FDCECd757dC8" } \ No newline at end of file diff --git a/tasks/estimate-fee.ts b/tasks/estimate-fee.ts index ff5a448..32dbc8c 100644 --- a/tasks/estimate-fee.ts +++ b/tasks/estimate-fee.ts @@ -22,12 +22,18 @@ task('estimate-fee', 'estimate the fee of sending message from source chain to d [await deployer.getAddress(), 1, 1, 'ipfs://QmfJ3ET9FjpV4X9oTi5T2aDZjg4h3pnwr7Au242c5RZH6b'] ); + const ESTIMATED_GAS = 750_000 // based on some tests... + const adapterParams = ethers.utils.defaultAbiCoder.encode( + ['uint16', 'uint256'], + [1, ESTIMATED_GAS] + ); + const fees = await endpoint.estimateFees( LZ_CONFIG[destination].chainId, // the destination LayerZero chainId contractsDeployed.OmniSBT, // your contract address that calls Endpoint.send() payload, false, // _payInZRO - "0x" // default '0x' adapterParams, see: Relayer Adapter Param docs + adapterParams // https://layerzero.gitbook.io/docs/evm-guides/advanced/relayer-adapter-parameters ); console.log('payload types', types); diff --git a/tasks/gated-modules/deploy-modules-source.ts b/tasks/gated-modules/deploy-modules-source.ts index bfd0dbe..5edb67c 100644 --- a/tasks/gated-modules/deploy-modules-source.ts +++ b/tasks/gated-modules/deploy-modules-source.ts @@ -21,19 +21,19 @@ task('deploy-modules-source', 'deploys our Lens modules on the source chain').se [lensHub.address, LZ_CONFIG_GATED_MODULES[networkName].endpoint, [], []] ); - const referenceModule = await deployContract( - ethers, - networkName, - 'LZGatedReferenceModule', - [lensHub.address, LZ_CONFIG_GATED_MODULES[networkName].endpoint, [], []] - ); - - const collectModule = await deployContract( - ethers, - networkName, - 'LZGatedCollectModule', - [lensHub.address, LZ_CONFIG_GATED_MODULES[networkName].endpoint, [], []] - ); + // const referenceModule = await deployContract( + // ethers, + // networkName, + // 'LZGatedReferenceModule', + // [lensHub.address, LZ_CONFIG_GATED_MODULES[networkName].endpoint, [], []] + // ); + // + // const collectModule = await deployContract( + // ethers, + // networkName, + // 'LZGatedCollectModule', + // [lensHub.address, LZ_CONFIG_GATED_MODULES[networkName].endpoint, [], []] + // ); if (networkName === 'mumbai') { const mockSandboxGovernance = getMockSandboxGovernance(deployer.provider); @@ -44,15 +44,15 @@ task('deploy-modules-source', 'deploys our Lens modules on the source chain').se tx = await mockSandboxGovernance.connect(deployer).whitelistFollowModule(followModule.address, true, { gasLimit: 100000 }); console.log(`tx: ${tx.hash}`); await tx.wait(); - - console.log('mockSandboxGovernance.whitelistReferenceModule()'); - tx = await mockSandboxGovernance.connect(deployer).whitelistReferenceModule(referenceModule.address, true, { gasLimit: 100000 }); - console.log(`tx: ${tx.hash}`); - await tx.wait(); - - console.log('mockSandboxGovernance.whitelistCollectModule()'); - tx = await mockSandboxGovernance.connect(deployer).whitelistCollectModule(collectModule.address, true, { gasLimit: 100000 }); - console.log(`tx: ${tx.hash}`); - await tx.wait(); + // + // console.log('mockSandboxGovernance.whitelistReferenceModule()'); + // tx = await mockSandboxGovernance.connect(deployer).whitelistReferenceModule(referenceModule.address, true, { gasLimit: 100000 }); + // console.log(`tx: ${tx.hash}`); + // await tx.wait(); + // + // console.log('mockSandboxGovernance.whitelistCollectModule()'); + // tx = await mockSandboxGovernance.connect(deployer).whitelistCollectModule(collectModule.address, true, { gasLimit: 100000 }); + // console.log(`tx: ${tx.hash}`); + // await tx.wait(); } }); diff --git a/tasks/gated-modules/deploy-proxy-remote.ts b/tasks/gated-modules/deploy-proxy-remote.ts index 0b83af6..6a9b785 100644 --- a/tasks/gated-modules/deploy-proxy-remote.ts +++ b/tasks/gated-modules/deploy-proxy-remote.ts @@ -3,8 +3,6 @@ import { LZ_CONFIG_GATED_MODULES } from './../helpers/constants'; import deployContract from './../helpers/deployContract'; import { contractsDeployedOn } from './../../scripts/utils/migrations'; -const SOURCE_NETWORK_NAME = 'mumbai'; // @TODO: flip to polygon - task('deploy-proxy-remote', 'deploys LZGatedProxy on a remote chain').setAction(async ({}, hre) => { const ethers = hre.ethers; const networkName = hre.network.name; @@ -12,17 +10,19 @@ task('deploy-proxy-remote', 'deploys LZGatedProxy on a remote chain').setAction( if (!LZ_CONFIG_GATED_MODULES[networkName]) throw new Error('invalid network'); + const sourceNetwork = LZ_CONFIG_GATED_MODULES[networkName].remote; + // modules deployed on the source chain - const contracts = contractsDeployedOn(SOURCE_NETWORK_NAME); + const contracts = contractsDeployedOn(sourceNetwork); - console.log(`deploying LZGatedProxy on networkName: ${networkName}...`); + console.log(`deploying LZGatedProxy on remote: ${networkName} with source: ${sourceNetwork}...`); const lzGatedProxy = await deployContract( ethers, networkName, 'LZGatedProxy', [ LZ_CONFIG_GATED_MODULES[networkName].endpoint, - LZ_CONFIG_GATED_MODULES[SOURCE_NETWORK_NAME].chainId, + LZ_CONFIG_GATED_MODULES[sourceNetwork].chainId, contracts.LZGatedFollowModule, contracts.LZGatedReferenceModule, contracts.LZGatedCollectModule diff --git a/tasks/gated-modules/estimate-fee.ts b/tasks/gated-modules/estimate-fee.ts index 89a6428..313afd0 100644 --- a/tasks/gated-modules/estimate-fee.ts +++ b/tasks/gated-modules/estimate-fee.ts @@ -1,5 +1,5 @@ import { task } from 'hardhat/config'; -import { Contract, utils, Bytes, providers } from 'ethers'; +import { Contract, utils, Bytes, providers, BigNumber } from 'ethers'; import { contractsDeployed } from './../../scripts/utils/migrations'; import ILayerZeroMessagingLibrary from './../helpers/ILayerZeroMessagingLibrary.json'; import { getLensHubDeployed, getFollowWithSigParts } from './../helpers/lens'; @@ -28,12 +28,13 @@ task('estimate-fee', 'estimate the fee of sending the followSig from source chai const followerAddress = await deployer.getAddress(); const nonce = (await lensHub.sigNonces(followerAddress)).toNumber(); + const chainId = parseInt(await hre.getChainId()); const followWithSigData = await getFollowWithSigParts({ - chainId: hre.network.config.chainId, + chainId, wallet: deployer, lensHubAddress: lensHub.address, - profileIds: [SANDBOX_USER_PROFILE_ID], + profileIds: [BigNumber.from(SANDBOX_USER_PROFILE_ID)], datas: [[]], nonce, deadline: MAX_UINT256, diff --git a/tasks/gated-modules/force-resume-receive.ts b/tasks/gated-modules/force-resume-receive.ts new file mode 100644 index 0000000..8ba6277 --- /dev/null +++ b/tasks/gated-modules/force-resume-receive.ts @@ -0,0 +1,27 @@ +import { task } from 'hardhat/config'; +import { LZ_CONFIG_GATED_MODULES } from './../helpers/constants'; +import getContract from './../helpers/getContract'; +import { contractsDeployedOn } from './../../scripts/utils/migrations'; + +// worst case, in the case of a revert +task('force-resume-receive', 'force our lz contract to receive new messages after a revert').setAction(async ({}, hre) => { + const ethers = hre.ethers; + const networkName = hre.network.name; + const [deployer] = await ethers.getSigners(); + + if (!LZ_CONFIG_GATED_MODULES[networkName]) throw new Error('invalid network'); + + const followModule = await getContract(ethers, 'LZGatedFollowModule', deployer); + // const referenceModule = await getContract(ethers, 'LZGatedReferenceModule', deployer); + // const collectModule = await getContract(ethers, 'LZGatedCollectModule', deployer); + + const REMOTE = 'goerli'; + const { LZGatedProxy } = contractsDeployedOn(REMOTE); + + const trustedRemote = ethers.utils.solidityPack(['address','address'], [LZGatedProxy, followModule.address]); + const tx = await followModule.forceResumeReceive(LZ_CONFIG_GATED_MODULES[REMOTE].chainId, trustedRemote); + console.log(`tx: ${tx.hash}`); + await tx.wait(); + + console.log('done!'); +}); diff --git a/tasks/gated-modules/set-follow-module.ts b/tasks/gated-modules/set-follow-module.ts index 879e0f1..c8cb55b 100644 --- a/tasks/gated-modules/set-follow-module.ts +++ b/tasks/gated-modules/set-follow-module.ts @@ -8,6 +8,7 @@ import { } from './../helpers/constants'; import { getLensHubDeployed } from './../helpers/lens'; import { contractsDeployed } from './../../scripts/utils/migrations'; +import getContract from './../helpers/getContract'; task('set-follow-module', 'sets the LZGatedFollowModule on our test profile').setAction(async ({}, hre) => { const ethers = hre.ethers; @@ -19,6 +20,8 @@ task('set-follow-module', 'sets the LZGatedFollowModule on our test profile').se // using the sandbox deployment for module whitelisting const lensHub = await getLensHubDeployed('lensHub sandbox', networkName, deployer.provider); + const followModule = await getContract(ethers, 'LZGatedFollowModule', deployer); + // tokenContract, balanceThreshold, chainId const data = ethers.utils.defaultAbiCoder.encode( ['address', 'uint256', 'uint16'], @@ -39,4 +42,6 @@ task('set-follow-module', 'sets the LZGatedFollowModule on our test profile').se await tx.wait(); console.log('follow module set!'); + const res = await followModule.gatedFollowPerProfile(SANDBOX_USER_PROFILE_ID); + console.log(res); }); diff --git a/tasks/gated-modules/set-trusted-remotes.ts b/tasks/gated-modules/set-trusted-remotes.ts index 6c31be8..799ee13 100644 --- a/tasks/gated-modules/set-trusted-remotes.ts +++ b/tasks/gated-modules/set-trusted-remotes.ts @@ -4,6 +4,7 @@ import { LZ_CONFIG_GATED_MODULES } from './../helpers/constants'; import getContract from './../helpers/getContract'; import { contractsDeployedOn } from './../../scripts/utils/migrations'; +// https://layerzero.gitbook.io/docs/evm-guides/master/set-trusted-remotes task('set-trusted-remotes', 'sets the trusted remotes for each module / remote pair').setAction(async ({}, hre) => { const ethers = hre.ethers; const networkName = hre.network.name; @@ -17,7 +18,8 @@ task('set-trusted-remotes', 'sets the trusted remotes for each module / remote p const referenceModule = await getContract(ethers, 'LZGatedReferenceModule', deployer); const collectModule = await getContract(ethers, 'LZGatedCollectModule', deployer); - const { remotes } = LZ_CONFIG_GATED_MODULES[networkName]; + // const { remotes } = LZ_CONFIG_GATED_MODULES[networkName]; + const remotes = ['goerli']; await Promise.all(remotes.map((remote) => limit(async () => { const { LZGatedProxy } = contractsDeployedOn(remote); @@ -27,17 +29,21 @@ task('set-trusted-remotes', 'sets the trusted remotes for each module / remote p console.log(`setting trusted remote (${LZ_CONFIG_GATED_MODULES[remote].chainId}, ${LZGatedProxy})`); let tx; - tx = await followModule.setTrustedRemote(LZ_CONFIG_GATED_MODULES[remote].chainId, LZGatedProxy); + let trustedRemote; + trustedRemote = ethers.utils.solidityPack(['address','address'], [LZGatedProxy, followModule.address]); + tx = await followModule.setTrustedRemote(LZ_CONFIG_GATED_MODULES[remote].chainId, trustedRemote); console.log(`tx: ${tx.hash}`); await tx.wait(); - tx = await referenceModule.setTrustedRemote(LZ_CONFIG_GATED_MODULES[remote].chainId, LZGatedProxy); - console.log(`tx: ${tx.hash}`); - await tx.wait(); - - tx = await collectModule.setTrustedRemote(LZ_CONFIG_GATED_MODULES[remote].chainId, LZGatedProxy); - console.log(`tx: ${tx.hash}`); - await tx.wait(); + // trustedRemote = ethers.utils.solidityPack(['address','address'], [LZGatedProxy, referenceModule.address]); + // tx = await referenceModule.setTrustedRemote(LZ_CONFIG_GATED_MODULES[remote].chainId, trustedRemote); + // console.log(`tx: ${tx.hash}`); + // await tx.wait(); + // + // trustedRemote = ethers.utils.solidityPack(['address','address'], [LZGatedProxy, collectModule.address]); + // tx = await collectModule.setTrustedRemote(LZ_CONFIG_GATED_MODULES[remote].chainId, LZGatedProxy); + // console.log(`tx: ${tx.hash}`); + // await tx.wait(); }))); console.log('done!'); diff --git a/tasks/gated-modules/test-follow-module.ts b/tasks/gated-modules/test-follow-module.ts index 58690c8..8bd4bc5 100644 --- a/tasks/gated-modules/test-follow-module.ts +++ b/tasks/gated-modules/test-follow-module.ts @@ -1,55 +1,61 @@ import { task } from 'hardhat/config'; -import { Contract, utils, Bytes, providers } from 'ethers'; +import { Contract, utils, Bytes, providers, BigNumber } from 'ethers'; import getContract from './../helpers/getContract'; import { getLensHubDeployed, getFollowWithSigParts } from './../helpers/lens'; import { MAX_UINT256 } from './../../test/lens/helpers/constants'; import { LZ_CONFIG_GATED_MODULES, SANDBOX_USER_PROFILE_ID, + SANDBOX_USER_PROFILE_ID_USER, TOKEN_CONTRACT, TOKEN_THRESHOLD, } from './../helpers/constants'; const { ALCHEMY_MUMBAI_URL, ALCHEMY_POLYGON_URL } = process.env; -const SANDBOX_USER_PROFILE_ID_TWO = '317'; // owned by `governance` -const ESTIMATED_FOLLOW_FEE_GWEI = '1000'; // derived from `npx hardhat estimate-fee` +const ESTIMATED_FOLLOW_FEE_GWEI = '1500'; // derived from `npx hardhat estimate-fee` +const ESTIMATED_GAS = 250000; +const REMOTE_CHAIN_IDS = { 'mumbai': 80001, 'polygon': 137 }; // the same can be done for LZGatedCollectModule + LZGatedReferenceModule, just need to setup the correct sig data task('test-follow-module', 'try to folllow a profile which has set their follow module to LZGatedFollowModule').setAction(async ({}, hre) => { const ethers = hre.ethers; const networkName = hre.network.name; - const [deployer, governance] = await ethers.getSigners(); + const [deployer, governance, user] = await ethers.getSigners(); if (!(LZ_CONFIG_GATED_MODULES[networkName] && LZ_CONFIG_GATED_MODULES[networkName].remote)) throw new Error('invalid network'); - const destination = LZ_CONFIG_GATED_MODULES[networkName].remote; + const destination = 'mumbai'; //LZ_CONFIG_GATED_MODULES[networkName].remote; const provider = new providers.JsonRpcProvider(destination === 'mumbai' ? ALCHEMY_MUMBAI_URL : ALCHEMY_POLYGON_URL); const lensHub = await getLensHubDeployed('lensHub sandbox', destination, provider); const lzGatedProxy = await getContract(ethers, 'LZGatedProxy', deployer); const followerAddress = await governance.getAddress(); const nonce = (await lensHub.sigNonces(followerAddress)).toNumber(); + const chainId = REMOTE_CHAIN_IDS[destination]; const followWithSigData = await getFollowWithSigParts({ - chainId: hre.network.config.chainId, + chainId, wallet: governance, lensHubAddress: lensHub.address, - profileIds: [SANDBOX_USER_PROFILE_ID_TWO], + profileIds: [SANDBOX_USER_PROFILE_ID], datas: [[]], nonce, deadline: MAX_UINT256, follower: followerAddress, }); + console.log(`followWithSigData:`); + console.log(JSON.stringify(followWithSigData,null,2)); + console.log('lzGatedProxy.relayFollowWithSig()'); const tx = await lzGatedProxy.relayFollowWithSig( followerAddress, - SANDBOX_USER_PROFILE_ID_TWO, + SANDBOX_USER_PROFILE_ID, TOKEN_CONTRACT, TOKEN_THRESHOLD, followWithSigData, - { value: utils.parseUnits(ESTIMATED_FOLLOW_FEE_GWEI, 'gwei'), gasLimit: 210000 } + { value: utils.parseUnits(ESTIMATED_FOLLOW_FEE_GWEI, 'gwei'), gasLimit: ESTIMATED_GAS } ); console.log(`tx: ${tx.hash}`); await tx.wait(); diff --git a/tasks/helpers/constants.ts b/tasks/helpers/constants.ts index 18abc85..5c36b78 100644 --- a/tasks/helpers/constants.ts +++ b/tasks/helpers/constants.ts @@ -57,3 +57,4 @@ export const TOKEN_THRESHOLD = '1'; // one token required to follow export const TOKEN_CHAIN_ID = LZ_CONFIG_GATED_MODULES.goerli.chainId; // where our `TOKEN_CONTRACT` lives (goerli) export const SANDBOX_USER_PROFILE_ID = '322'; // thereisnosecondbest2.test +export const SANDBOX_USER_PROFILE_ID_USER = '329'; // lenstestwalletuser.test diff --git a/tasks/helpers/lens.ts b/tasks/helpers/lens.ts index a26b86c..5ce9f09 100644 --- a/tasks/helpers/lens.ts +++ b/tasks/helpers/lens.ts @@ -8,6 +8,9 @@ import { import LensHub from './abi/LensHub.json'; import { HARDHAT_CHAINID } from './../../test/lens/helpers/constants'; +const LENS_DOMAIN_NAME = 'Lens Protocol Profiles'; +const LENS_DOMAIN_VERSION = '1'; + const lensAddresses = { 'mumbai': { 'interaction logic lib': '0xefd400326635e016CbfCc309725D5B62FD9d3468', @@ -44,10 +47,10 @@ export const getMockSandboxGovernance = (provider: any) => ( ); type FollowWithSigDataProps = { - chainId: number | undefined; + chainId: number; wallet: any; // Signer lensHubAddress: string; - profileIds: string[] | number[]; + profileIds: BigNumber[] | string[]; datas: Bytes[] | string[]; nonce: number; deadline: string; @@ -55,9 +58,9 @@ type FollowWithSigDataProps = { }; const buildFollowWithSigParams = ( - chainId: number | undefined, + chainId: number, lensHubAddress: string, - profileIds: string[] | number[], + profileIds: BigNumber[] | string[], datas: Bytes[] | string[], nonce: number, deadline: string @@ -71,9 +74,9 @@ const buildFollowWithSigParams = ( ], }, domain: { - name: 'Lens Protocol Profiles', - version: '1', - chainId: chainId || HARDHAT_CHAINID, + name: LENS_DOMAIN_NAME, + version: LENS_DOMAIN_VERSION, + chainId, verifyingContract: lensHubAddress, }, value: { @@ -85,7 +88,7 @@ const buildFollowWithSigParams = ( }); export const getFollowWithSigParts = async ({ - chainId = HARDHAT_CHAINID, + chainId, wallet, lensHubAddress, profileIds, diff --git a/test/LZGatedFollowModule.spec.ts b/test/LZGatedFollowModule.spec.ts index 3610073..5bf6672 100644 --- a/test/LZGatedFollowModule.spec.ts +++ b/test/LZGatedFollowModule.spec.ts @@ -231,8 +231,6 @@ makeSuiteCleanRoom('LZGatedFollowModule', function () { lzGatedProxy .connect(user) .relayFollowWithSig( - userAddress, - FIRST_PROFILE_ID, erc721.address, BALANCE_THRESHOLD, followWithSigData @@ -245,8 +243,6 @@ makeSuiteCleanRoom('LZGatedFollowModule', function () { lzGatedProxy .connect(user) .relayFollowWithSig( - userAddress, - FIRST_PROFILE_ID, lzEndpoint.address, BALANCE_THRESHOLD, followWithSigData @@ -260,8 +256,6 @@ makeSuiteCleanRoom('LZGatedFollowModule', function () { const tx = await lzGatedProxy .connect(user) .relayFollowWithSig( - userAddress, - FIRST_PROFILE_ID, erc721.address, 0, followWithSigData @@ -278,8 +272,6 @@ makeSuiteCleanRoom('LZGatedFollowModule', function () { const tx = await lzGatedProxy .connect(user) .relayFollowWithSig( - userAddress, - FIRST_PROFILE_ID, erc20.address, BALANCE_THRESHOLD, followWithSigData @@ -289,14 +281,12 @@ makeSuiteCleanRoom('LZGatedFollowModule', function () { expect(messageFailedReason).to.equal('InvalidRemoteInput'); }); - it('processes a valid follow', async () => { + it.only('processes a valid follow', async () => { await erc721.safeMint(userAddress); const tx = await lzGatedProxy .connect(user) .relayFollowWithSig( - userAddress, - FIRST_PROFILE_ID, erc721.address, BALANCE_THRESHOLD, followWithSigData @@ -321,8 +311,6 @@ makeSuiteCleanRoom('LZGatedFollowModule', function () { await lzGatedProxy .connect(user) .relayFollowWithSig( - userAddress, - FIRST_PROFILE_ID, erc721.address, BALANCE_THRESHOLD, followWithSigData @@ -332,8 +320,6 @@ makeSuiteCleanRoom('LZGatedFollowModule', function () { lzGatedProxy .connect(user) .relayFollowWithSig( - userAddress, - FIRST_PROFILE_ID, erc721.address, BALANCE_THRESHOLD, followWithSigData From daae97e75ea459e3c3496a4da81f9d860ec76583 Mon Sep 17 00:00:00 2001 From: Carlos Beltran Date: Wed, 21 Dec 2022 19:56:39 -0600 Subject: [PATCH 14/14] remove redundant function params from LZGatedProxy; update dates; update tasks --- contracts/LZGatedCollectModule.sol | 11 ++- contracts/LZGatedFollowModule.sol | 27 +++---- contracts/LZGatedProxy.sol | 67 +++++++---------- contracts/LZGatedReferenceModule.sol | 72 ++++++++++--------- scripts/utils/goerli-contracts.json | 2 +- scripts/utils/mumbai-contracts.json | 2 +- tasks/estimate-fee.ts | 4 +- tasks/gated-modules/deploy-modules-source.ts | 46 ++++++------ ...{estimate-fee.ts => estimate-fee-gated.ts} | 15 ++-- tasks/gated-modules/set-trusted-remotes.ts | 21 +++--- tasks/gated-modules/test-follow-module.ts | 9 +-- test/LZGatedCollectModule.spec.ts | 20 ++---- test/LZGatedFollowModule.spec.ts | 11 ++- test/LZGatedReferenceModule.spec.ts | 40 +++-------- 14 files changed, 154 insertions(+), 193 deletions(-) rename tasks/gated-modules/{estimate-fee.ts => estimate-fee-gated.ts} (81%) diff --git a/contracts/LZGatedCollectModule.sol b/contracts/LZGatedCollectModule.sol index ba1c867..2e63cf1 100644 --- a/contracts/LZGatedCollectModule.sol +++ b/contracts/LZGatedCollectModule.sol @@ -111,14 +111,11 @@ contract LZGatedCollectModule is FollowValidationModuleBase, ICollectModule, LzA ) internal override { ( address token, - address collector, - uint256 profileId, - uint256 pubId, uint256 threshold, DataTypes.CollectWithSigData memory collectSig - ) = abi.decode(_payload, (address, address, uint256, uint256, uint256, DataTypes.CollectWithSigData)); + ) = abi.decode(_payload, (address, uint256, DataTypes.CollectWithSigData)); - GatedCollectData memory data = gatedCollectDataPerPub[profileId][pubId]; + GatedCollectData memory data = gatedCollectDataPerPub[collectSig.profileId][collectSig.pubId]; // validate that remote check was against the contract/threshold defined if (data.remoteChainId != _srcChainId || data.balanceThreshold != threshold || data.tokenContract != token) { @@ -127,7 +124,7 @@ contract LZGatedCollectModule is FollowValidationModuleBase, ICollectModule, LzA } // @TODO: hash the vars vs deeply nested? - validatedCollectors[profileId][pubId][collector] = true; + validatedCollectors[collectSig.profileId][collectSig.pubId][collectSig.collector] = true; // use the signature to execute the collect try ILensHub(HUB).collectWithSig(collectSig) {} @@ -135,6 +132,6 @@ contract LZGatedCollectModule is FollowValidationModuleBase, ICollectModule, LzA emit MessageFailed(_srcChainId, _srcAddress, _nonce, _payload, reason); } - delete validatedCollectors[profileId][pubId][collector]; + delete validatedCollectors[collectSig.profileId][collectSig.pubId][collectSig.collector]; } } diff --git a/contracts/LZGatedFollowModule.sol b/contracts/LZGatedFollowModule.sol index 1516a97..3529ec5 100644 --- a/contracts/LZGatedFollowModule.sol +++ b/contracts/LZGatedFollowModule.sol @@ -113,13 +113,12 @@ contract LZGatedFollowModule is FollowValidatorFollowModuleBase, LzApp { bytes memory _payload ) internal override { ( - address follower, address token, - uint256 profileId, uint256 threshold, DataTypes.FollowWithSigData memory followSig - ) = abi.decode(_payload, (address, address, uint256, uint256, DataTypes.FollowWithSigData)); + ) = abi.decode(_payload, (address, uint256, DataTypes.FollowWithSigData)); + uint256 profileId = followSig.profileIds[0]; GatedFollowData memory data = gatedFollowPerProfile[profileId]; // validate that remote check was against the contract/threshold defined @@ -129,22 +128,14 @@ contract LZGatedFollowModule is FollowValidatorFollowModuleBase, LzApp { } // allow the follow in the callback to #processFollow - validatedFollowers[profileId][follower] = true; + validatedFollowers[profileId][followSig.follower] = true; - ILensHub(HUB).followWithSig(followSig); - - // (bool success, bytes memory reason) = HUB.call(abi.encodeWithSelector(ILensHub.followWithSig.selector, followSig)); - // // try-catch all errors/exceptions - // if (!success) { - // emit MessageFailed(_srcChainId, _srcAddress, _nonce, _payload, reason); - // } - - // // use the signature to execute the follow - // try ILensHub(HUB).followWithSig(followSig) {} - // catch Error (string memory reason) { - // emit MessageFailed(_srcChainId, _srcAddress, _nonce, _payload, reason); - // } + // use the signature to execute the follow + try ILensHub(HUB).followWithSig(followSig) {} + catch Error (string memory reason) { + emit MessageFailed(_srcChainId, _srcAddress, _nonce, _payload, reason); + } - delete validatedFollowers[profileId][follower]; + delete validatedFollowers[profileId][followSig.follower]; } } diff --git a/contracts/LZGatedProxy.sol b/contracts/LZGatedProxy.sol index 10b3ade..9f4a52b 100644 --- a/contracts/LZGatedProxy.sol +++ b/contracts/LZGatedProxy.sol @@ -44,58 +44,49 @@ contract LZGatedProxy is SimpleLzApp { * NOTE: callers of this function MUST pass the exact values for `tokenContract` and `balanceThreshold` returned from * the call to LZGatedFollowModule.gatedFollowPerProfile(profileId) - or the transaction on the remote chain WILL * revert. - * @param follower: the account wishing to perform the follow action - * @param profileId: the id of the profile being followed * @param tokenContract: the ERC20/ERC721 contract set by the `profileId` to check a balance against * @param balanceThreshold: the amount of tokens required in order for a successful follow + * @param lzCustomGasAmount: custom gas amount that is paid for lz.send() * @param followSig: the follow signature expected by the LensHub */ function relayFollowWithSig( - address follower, - uint256 profileId, address tokenContract, uint256 balanceThreshold, + uint256 lzCustomGasAmount, DataTypes.FollowWithSigData memory followSig ) external payable { - if (!_checkThreshold(follower, tokenContract, balanceThreshold)) { revert InsufficientBalance(); } - - bytes memory customAdapterParams = abi.encode(1, 750_000); + if (!_checkThreshold(followSig.follower, tokenContract, balanceThreshold)) { revert InsufficientBalance(); } _lzSend( remoteFollowModule, abi.encode( - follower, tokenContract, - profileId, balanceThreshold, followSig ), payable(msg.sender), - bytes("") + _getAdapterParams(lzCustomGasAmount) ); } /** + * TODO: validate that `sender` is the one who signed `commentSig` (ecrecover) * @notice validate a token balance on this chain before relaying the intent to comment on a Lens post on the remote * chain. * NOTE: callers of this function MUST pass the exact values for `tokenContract` and `balanceThreshold` returned from * the call to LZGatedReferenceModule.gatedReferenceDataPerPub(profileIdPointed, pubIdPointed) - or the transaction * on the remote chain WILL revert. * @param sender: the account wishing to perform the comment action - * @param profileId: the id of the profile wishing to comment - * @param profileIdPointed: the id of the profile that owns the post - * @param pubIdPointed: the id of the post * @param tokenContract: the ERC20/ERC721 contract set by the `profileId` to check a balance against * @param balanceThreshold: the amount of tokens required in order for a successful follow + * @param lzCustomGasAmount: custom gas amount that is paid for lz.send() * @param commentSig: the comment signature expected by the LensHub */ function relayCommentWithSig( address sender, - uint256 profileId, - uint256 profileIdPointed, - uint256 pubIdPointed, address tokenContract, uint256 balanceThreshold, + uint256 lzCustomGasAmount, DataTypes.CommentWithSigData memory commentSig ) external payable { if (!_checkThreshold(sender, tokenContract, balanceThreshold)) { revert InsufficientBalance(); } @@ -105,38 +96,32 @@ contract LZGatedProxy is SimpleLzApp { abi.encode( true, // isComment tokenContract, - profileId, - profileIdPointed, - pubIdPointed, balanceThreshold, commentSig ), payable(msg.sender), - bytes("") + _getAdapterParams(lzCustomGasAmount) ); } /** + * TODO: validate that `sender` is the one who signed `mirrorSig` (ecrecover) * @notice validate a token balance on this chain before relaying the intent to mirror a Lens post on the remote * chain. * NOTE: callers of this function MUST pass the exact values for `tokenContract` and `balanceThreshold` returned from * the call to LZGatedReferenceModule.gatedReferenceDataPerPub(profileIdPointed, pubIdPointed) - or the transaction * on the remote chain WILL revert. * @param sender: the account wishing to perform the mirror action - * @param profileId: the id of the profile wishing to mirror - * @param profileIdPointed: the id of the profile that owns the post - * @param pubIdPointed: the id of the post * @param tokenContract: the ERC20/ERC721 contract set by the `profileId` to check a balance against * @param balanceThreshold: the amount of tokens required in order for a successful follow + * @param lzCustomGasAmount: custom gas amount that is paid for lz.send() * @param mirrorSig: the mirror signature expected by the LensHub */ function relayMirrorWithSig( address sender, - uint256 profileId, - uint256 profileIdPointed, - uint256 pubIdPointed, address tokenContract, uint256 balanceThreshold, + uint256 lzCustomGasAmount, DataTypes.MirrorWithSigData memory mirrorSig ) external payable { if (!_checkThreshold(sender, tokenContract, balanceThreshold)) { revert InsufficientBalance(); } @@ -146,14 +131,11 @@ contract LZGatedProxy is SimpleLzApp { abi.encode( false, // isComment tokenContract, - profileId, - profileIdPointed, - pubIdPointed, balanceThreshold, mirrorSig ), payable(msg.sender), - bytes("") + _getAdapterParams(lzCustomGasAmount) ); } @@ -163,35 +145,28 @@ contract LZGatedProxy is SimpleLzApp { * NOTE: callers of this function MUST pass the exact values for `tokenContract` and `balanceThreshold` returned from * the call to LZGatedCollectModule.gatedCollectDataPerPub(profileId, pubId) - or the transaction * on the remote chain WILL revert. - * @param collector: the account wishing to perform the collect action - * @param profileId: the id of the profile wishing to collect - * @param pubId: the id of the post * @param tokenContract: the ERC20/ERC721 contract set by the `profileId` to check a balance against * @param balanceThreshold: the amount of tokens required in order for a successful follow + * @param lzCustomGasAmount: custom gas amount that is paid for lz.send() * @param collectSig: the collect signature expected by the LensHub */ function relayCollectWithSig( - address collector, - uint256 profileId, - uint256 pubId, address tokenContract, uint256 balanceThreshold, + uint256 lzCustomGasAmount, DataTypes.CollectWithSigData memory collectSig ) external payable { - if (!_checkThreshold(collector, tokenContract, balanceThreshold)) { revert InsufficientBalance(); } + if (!_checkThreshold(collectSig.collector, tokenContract, balanceThreshold)) { revert InsufficientBalance(); } _lzSend( remoteCollectModule, abi.encode( tokenContract, - collector, - profileId, - pubId, balanceThreshold, collectSig ), payable(msg.sender), - bytes("") + _getAdapterParams(lzCustomGasAmount) ); } @@ -216,4 +191,14 @@ contract LZGatedProxy is SimpleLzApp { return balance >= balanceThreshold; } + + /** + * @dev returns the adapter params (version 1) required to override the gas provided for the tx on the destination + * chain: https://layerzero.gitbook.io/docs/evm-guides/advanced/relayer-adapter-parameters + */ + function _getAdapterParams(uint256 gasAmount) private pure returns (bytes memory adapterParams) { + adapterParams = gasAmount > 0 + ? abi.encodePacked(uint16(1), gasAmount) + : bytes(""); + } } diff --git a/contracts/LZGatedReferenceModule.sol b/contracts/LZGatedReferenceModule.sol index b8951d1..d9714cd 100644 --- a/contracts/LZGatedReferenceModule.sol +++ b/contracts/LZGatedReferenceModule.sol @@ -123,30 +123,10 @@ contract LZGatedReferenceModule is FollowValidationModuleBase, IReferenceModule, uint64 _nonce, bytes memory _payload ) internal override { - ( - bool isComment, - address token, - uint256 profileId, - uint256 profileIdPointed, - uint256 pubIdPointed, - uint256 threshold, - ) = abi.decode(_payload, (bool, address, uint256, uint256, uint256, uint256, bytes)); - - GatedReferenceData memory data = gatedReferenceDataPerPub[profileIdPointed][pubIdPointed]; - - // validate that remote check was against the contract/threshold defined - if (data.remoteChainId != _srcChainId || data.balanceThreshold != threshold || data.tokenContract != token) { - emit MessageFailed(_srcChainId, _srcAddress, _nonce, _payload, 'InvalidRemoteInput'); - return; - } - - // @TODO: hash the vars vs deeply nested? - validatedReferencers[profileIdPointed][pubIdPointed][profileId] = true; + (bool isComment,,,) = abi.decode(_payload, (bool, address, uint256, bytes)); // parse the payload for either #commentWithSig or #mirrorWithSig - string memory error = isComment ? _handleComment(_payload) : _handleMirror(_payload); - - delete validatedReferencers[profileIdPointed][pubIdPointed][profileId]; + string memory error = isComment ? _handleComment(_srcChainId, _payload) : _handleMirror(_srcChainId, _payload); if (bytes(error).length > 0) { emit MessageFailed(_srcChainId, _srcAddress, _nonce, _payload, error); @@ -155,35 +135,59 @@ contract LZGatedReferenceModule is FollowValidationModuleBase, IReferenceModule, /** * @dev Decodes the `payload` for Lens#commentWithSig - * @return an error string if the call failed, else empty string + * @return error an error string if the call failed, else empty string */ - function _handleComment(bytes memory _payload) internal returns (string memory) { - (,,,,,,DataTypes.CommentWithSigData memory commentSig) = abi.decode( + function _handleComment(uint16 _srcChainId, bytes memory _payload) internal returns (string memory error) { + (,address token, uint256 threshold, DataTypes.CommentWithSigData memory commentSig) = abi.decode( _payload, - (bool, address, uint256, uint256, uint256, uint256, DataTypes.CommentWithSigData) + (bool, address, uint256, DataTypes.CommentWithSigData) ); + GatedReferenceData memory data = gatedReferenceDataPerPub[commentSig.profileIdPointed][commentSig.pubIdPointed]; + + // validate that remote check was against the contract/threshold defined + if (data.remoteChainId != _srcChainId || data.balanceThreshold != threshold || data.tokenContract != token) { + return error = "InvalidRemoteInput"; + } + + // @TODO: hash the vars vs deeply nested? + validatedReferencers[commentSig.profileIdPointed][commentSig.pubIdPointed][commentSig.profileId] = true; + try ILensHub(HUB).commentWithSig(commentSig) { - return ""; + error = ""; } catch Error (string memory reason) { - return reason; + error = reason; } + + delete validatedReferencers[commentSig.profileIdPointed][commentSig.pubIdPointed][commentSig.profileId]; } /** * @dev Decodes the `payload` for Lens#mirrorWithSig - * @return an error string if the call failed, else empty string + * @return error an error string if the call failed, else empty string */ - function _handleMirror(bytes memory _payload) internal returns (string memory) { - (,,,,,,DataTypes.MirrorWithSigData memory mirrorSig) = abi.decode( + function _handleMirror(uint16 _srcChainId, bytes memory _payload) internal returns (string memory error) { + (,address token, uint256 threshold, DataTypes.MirrorWithSigData memory mirrorSig) = abi.decode( _payload, - (bool, address, uint256, uint256, uint256, uint256, DataTypes.MirrorWithSigData) + (bool, address, uint256, DataTypes.MirrorWithSigData) ); + GatedReferenceData memory data = gatedReferenceDataPerPub[mirrorSig.profileIdPointed][mirrorSig.pubIdPointed]; + + // validate that remote check was against the contract/threshold defined + if (data.remoteChainId != _srcChainId || data.balanceThreshold != threshold || data.tokenContract != token) { + return error = "InvalidRemoteInput"; + } + + // @TODO: hash the vars vs deeply nested? + validatedReferencers[mirrorSig.profileIdPointed][mirrorSig.pubIdPointed][mirrorSig.profileId] = true; + try ILensHub(HUB).mirrorWithSig(mirrorSig) { - return ""; + error = ""; } catch Error (string memory reason) { - return reason; + error = reason; } + + delete validatedReferencers[mirrorSig.profileIdPointed][mirrorSig.pubIdPointed][mirrorSig.profileId]; } } diff --git a/scripts/utils/goerli-contracts.json b/scripts/utils/goerli-contracts.json index 6c8d87c..549141f 100644 --- a/scripts/utils/goerli-contracts.json +++ b/scripts/utils/goerli-contracts.json @@ -1,3 +1,3 @@ { - "LZGatedProxy": "0x0Cf77dC470497b6D80194F4D028b95feAC14d274" + "LZGatedProxy": "0x2cBF9A2F164872cB4FF1424F7729847ffc58F3Fb" } \ No newline at end of file diff --git a/scripts/utils/mumbai-contracts.json b/scripts/utils/mumbai-contracts.json index 580420a..edec06b 100644 --- a/scripts/utils/mumbai-contracts.json +++ b/scripts/utils/mumbai-contracts.json @@ -1,6 +1,6 @@ { "OmniSBT": "0x1a255753a44EEedbf6d3Afe65A8F006F8C513b0C", - "LZGatedFollowModule": "0xa079d888415900c05ddF50DBaffAcF168aBfE467", + "LZGatedFollowModule": "0x68D0aA7D24926e5665e92E9A2aED8de772b64385", "LZGatedReferenceModule": "0x46720e2AC58f69d20A952F51AE73cFE0ae4a73b7", "LZGatedCollectModule": "0x2f4A8D959fD7227d0942D220d3E8FDCECd757dC8" } \ No newline at end of file diff --git a/tasks/estimate-fee.ts b/tasks/estimate-fee.ts index 32dbc8c..c719208 100644 --- a/tasks/estimate-fee.ts +++ b/tasks/estimate-fee.ts @@ -22,7 +22,7 @@ task('estimate-fee', 'estimate the fee of sending message from source chain to d [await deployer.getAddress(), 1, 1, 'ipfs://QmfJ3ET9FjpV4X9oTi5T2aDZjg4h3pnwr7Au242c5RZH6b'] ); - const ESTIMATED_GAS = 750_000 // based on some tests... + const ESTIMATED_GAS = 0; const adapterParams = ethers.utils.defaultAbiCoder.encode( ['uint16', 'uint256'], [1, ESTIMATED_GAS] @@ -33,7 +33,7 @@ task('estimate-fee', 'estimate the fee of sending message from source chain to d contractsDeployed.OmniSBT, // your contract address that calls Endpoint.send() payload, false, // _payInZRO - adapterParams // https://layerzero.gitbook.io/docs/evm-guides/advanced/relayer-adapter-parameters + adapterParams // https://layerzero.gitbook.io/docs/evm-guides/advanced/relayer-adapter-parameters ); console.log('payload types', types); diff --git a/tasks/gated-modules/deploy-modules-source.ts b/tasks/gated-modules/deploy-modules-source.ts index 5edb67c..fce1377 100644 --- a/tasks/gated-modules/deploy-modules-source.ts +++ b/tasks/gated-modules/deploy-modules-source.ts @@ -21,19 +21,19 @@ task('deploy-modules-source', 'deploys our Lens modules on the source chain').se [lensHub.address, LZ_CONFIG_GATED_MODULES[networkName].endpoint, [], []] ); - // const referenceModule = await deployContract( - // ethers, - // networkName, - // 'LZGatedReferenceModule', - // [lensHub.address, LZ_CONFIG_GATED_MODULES[networkName].endpoint, [], []] - // ); - // - // const collectModule = await deployContract( - // ethers, - // networkName, - // 'LZGatedCollectModule', - // [lensHub.address, LZ_CONFIG_GATED_MODULES[networkName].endpoint, [], []] - // ); + const referenceModule = await deployContract( + ethers, + networkName, + 'LZGatedReferenceModule', + [lensHub.address, LZ_CONFIG_GATED_MODULES[networkName].endpoint, [], []] + ); + + const collectModule = await deployContract( + ethers, + networkName, + 'LZGatedCollectModule', + [lensHub.address, LZ_CONFIG_GATED_MODULES[networkName].endpoint, [], []] + ); if (networkName === 'mumbai') { const mockSandboxGovernance = getMockSandboxGovernance(deployer.provider); @@ -44,15 +44,15 @@ task('deploy-modules-source', 'deploys our Lens modules on the source chain').se tx = await mockSandboxGovernance.connect(deployer).whitelistFollowModule(followModule.address, true, { gasLimit: 100000 }); console.log(`tx: ${tx.hash}`); await tx.wait(); - // - // console.log('mockSandboxGovernance.whitelistReferenceModule()'); - // tx = await mockSandboxGovernance.connect(deployer).whitelistReferenceModule(referenceModule.address, true, { gasLimit: 100000 }); - // console.log(`tx: ${tx.hash}`); - // await tx.wait(); - // - // console.log('mockSandboxGovernance.whitelistCollectModule()'); - // tx = await mockSandboxGovernance.connect(deployer).whitelistCollectModule(collectModule.address, true, { gasLimit: 100000 }); - // console.log(`tx: ${tx.hash}`); - // await tx.wait(); + + console.log('mockSandboxGovernance.whitelistReferenceModule()'); + tx = await mockSandboxGovernance.connect(deployer).whitelistReferenceModule(referenceModule.address, true, { gasLimit: 100000 }); + console.log(`tx: ${tx.hash}`); + await tx.wait(); + + console.log('mockSandboxGovernance.whitelistCollectModule()'); + tx = await mockSandboxGovernance.connect(deployer).whitelistCollectModule(collectModule.address, true, { gasLimit: 100000 }); + console.log(`tx: ${tx.hash}`); + await tx.wait(); } }); diff --git a/tasks/gated-modules/estimate-fee.ts b/tasks/gated-modules/estimate-fee-gated.ts similarity index 81% rename from tasks/gated-modules/estimate-fee.ts rename to tasks/gated-modules/estimate-fee-gated.ts index 313afd0..8d5dd68 100644 --- a/tasks/gated-modules/estimate-fee.ts +++ b/tasks/gated-modules/estimate-fee-gated.ts @@ -14,7 +14,7 @@ import { const { ALCHEMY_MUMBAI_URL, ALCHEMY_POLYGON_URL } = process.env; // the same can be done for LZGatedCollectModule + LZGatedReferenceModule, just need to setup the correct sig data -task('estimate-fee', 'estimate the fee of sending the followSig from source chain to remote chain').setAction(async ({}, hre) => { +task('estimate-fee-gated', 'estimate the fee of sending the followSig from source chain to remote chain').setAction(async ({}, hre) => { const ethers = hre.ethers; const networkName = hre.network.name; const [deployer] = await ethers.getSigners(); @@ -43,21 +43,28 @@ task('estimate-fee', 'estimate the fee of sending the followSig from source chai // example payload const followWithSigType = 'tuple(address follower, uint256[] profileIds, bytes[] datas, tuple(uint8 v, bytes32 r, bytes32 s, uint256 deadline) sig) followSig'; - const types = ['address', 'address', 'uint256', 'uint256', followWithSigType]; + const types = ['address', 'uint256', followWithSigType]; const payload = ethers.utils.defaultAbiCoder.encode( types, - [followerAddress, TOKEN_CONTRACT, SANDBOX_USER_PROFILE_ID, TOKEN_THRESHOLD, followWithSigData] + [TOKEN_CONTRACT, TOKEN_THRESHOLD, followWithSigData] ); console.log(`networkName: ${networkName}`); console.log(`endpoint.address: ${endpoint.address}`); + const ESTIMATED_GAS_REMOTE = 500_000 // based on some tests... + console.log(`ESTIMATED_GAS_REMOTE: ${ESTIMATED_GAS_REMOTE}`); + const adapterParams = ethers.utils.solidityPack( + ['uint16', 'uint256'], + [1, ESTIMATED_GAS_REMOTE] + ); + const fees = await endpoint.estimateFees( LZ_CONFIG_GATED_MODULES[destination].chainId, // the destination LayerZero chainId contractsDeployed.LZGatedProxy, // your contract address that calls Endpoint.send() payload, false, // _payInZRO - "0x" // default '0x' adapterParams, see: Relayer Adapter Param docs + adapterParams // https://layerzero.gitbook.io/docs/evm-guides/advanced/relayer-adapter-parameters ); console.log('payload types: ', types); diff --git a/tasks/gated-modules/set-trusted-remotes.ts b/tasks/gated-modules/set-trusted-remotes.ts index 799ee13..1f32459 100644 --- a/tasks/gated-modules/set-trusted-remotes.ts +++ b/tasks/gated-modules/set-trusted-remotes.ts @@ -18,8 +18,7 @@ task('set-trusted-remotes', 'sets the trusted remotes for each module / remote p const referenceModule = await getContract(ethers, 'LZGatedReferenceModule', deployer); const collectModule = await getContract(ethers, 'LZGatedCollectModule', deployer); - // const { remotes } = LZ_CONFIG_GATED_MODULES[networkName]; - const remotes = ['goerli']; + const { remotes } = LZ_CONFIG_GATED_MODULES[networkName]; await Promise.all(remotes.map((remote) => limit(async () => { const { LZGatedProxy } = contractsDeployedOn(remote); @@ -35,15 +34,15 @@ task('set-trusted-remotes', 'sets the trusted remotes for each module / remote p console.log(`tx: ${tx.hash}`); await tx.wait(); - // trustedRemote = ethers.utils.solidityPack(['address','address'], [LZGatedProxy, referenceModule.address]); - // tx = await referenceModule.setTrustedRemote(LZ_CONFIG_GATED_MODULES[remote].chainId, trustedRemote); - // console.log(`tx: ${tx.hash}`); - // await tx.wait(); - // - // trustedRemote = ethers.utils.solidityPack(['address','address'], [LZGatedProxy, collectModule.address]); - // tx = await collectModule.setTrustedRemote(LZ_CONFIG_GATED_MODULES[remote].chainId, LZGatedProxy); - // console.log(`tx: ${tx.hash}`); - // await tx.wait(); + trustedRemote = ethers.utils.solidityPack(['address','address'], [LZGatedProxy, referenceModule.address]); + tx = await referenceModule.setTrustedRemote(LZ_CONFIG_GATED_MODULES[remote].chainId, trustedRemote); + console.log(`tx: ${tx.hash}`); + await tx.wait(); + + trustedRemote = ethers.utils.solidityPack(['address','address'], [LZGatedProxy, collectModule.address]); + tx = await collectModule.setTrustedRemote(LZ_CONFIG_GATED_MODULES[remote].chainId, LZGatedProxy); + console.log(`tx: ${tx.hash}`); + await tx.wait(); }))); console.log('done!'); diff --git a/tasks/gated-modules/test-follow-module.ts b/tasks/gated-modules/test-follow-module.ts index 8bd4bc5..1981c13 100644 --- a/tasks/gated-modules/test-follow-module.ts +++ b/tasks/gated-modules/test-follow-module.ts @@ -13,7 +13,7 @@ import { const { ALCHEMY_MUMBAI_URL, ALCHEMY_POLYGON_URL } = process.env; -const ESTIMATED_FOLLOW_FEE_GWEI = '1500'; // derived from `npx hardhat estimate-fee` +const ESTIMATED_FOLLOW_FEE_GWEI = '2500'; // derived from `npx hardhat estimate-fee-gated` const ESTIMATED_GAS = 250000; const REMOTE_CHAIN_IDS = { 'mumbai': 80001, 'polygon': 137 }; @@ -48,14 +48,15 @@ task('test-follow-module', 'try to folllow a profile which has set their follow console.log(`followWithSigData:`); console.log(JSON.stringify(followWithSigData,null,2)); + const ESTIMATED_GAS_REMOTE = 500_000 // based on some tests... + const GAS_LIMIT = 250_000; console.log('lzGatedProxy.relayFollowWithSig()'); const tx = await lzGatedProxy.relayFollowWithSig( - followerAddress, - SANDBOX_USER_PROFILE_ID, TOKEN_CONTRACT, TOKEN_THRESHOLD, followWithSigData, - { value: utils.parseUnits(ESTIMATED_FOLLOW_FEE_GWEI, 'gwei'), gasLimit: ESTIMATED_GAS } + ESTIMATED_GAS_REMOTE, + { value: utils.parseUnits(ESTIMATED_FOLLOW_FEE_GWEI, 'gwei'), gasLimit: GAS_LIMIT } ); console.log(`tx: ${tx.hash}`); await tx.wait(); diff --git a/test/LZGatedCollectModule.spec.ts b/test/LZGatedCollectModule.spec.ts index 7e7f414..8305db4 100644 --- a/test/LZGatedCollectModule.spec.ts +++ b/test/LZGatedCollectModule.spec.ts @@ -261,11 +261,9 @@ makeSuiteCleanRoom('LZGatedCollectModule', function () { lzGatedProxy .connect(userTwo) .relayCollectWithSig( - userTwoAddress, - FIRST_PROFILE_ID, - '1', erc721.address, BALANCE_THRESHOLD, + 0, // lzCustomGasAmount collectWithSigData ) ).to.be.revertedWith('InsufficientBalance'); @@ -276,11 +274,9 @@ makeSuiteCleanRoom('LZGatedCollectModule', function () { lzGatedProxy .connect(userTwo) .relayCollectWithSig( - userTwoAddress, - FIRST_PROFILE_ID, - '1', lzEndpoint.address, BALANCE_THRESHOLD, + 0, // lzCustomGasAmount collectWithSigData ) ).to.be.revertedWith('InsufficientBalance'); @@ -292,11 +288,9 @@ makeSuiteCleanRoom('LZGatedCollectModule', function () { const tx = await lzGatedProxy .connect(userTwo) .relayCollectWithSig( - userTwoAddress, - FIRST_PROFILE_ID, - '1', erc721.address, 0, + 0, // lzCustomGasAmount collectWithSigData ); @@ -311,11 +305,9 @@ makeSuiteCleanRoom('LZGatedCollectModule', function () { const tx = await lzGatedProxy .connect(userTwo) .relayCollectWithSig( - userTwoAddress, - FIRST_PROFILE_ID, - '1', erc20.address, BALANCE_THRESHOLD, + 0, // lzCustomGasAmount collectWithSigData ); @@ -329,11 +321,9 @@ makeSuiteCleanRoom('LZGatedCollectModule', function () { const tx = await lzGatedProxy .connect(userTwo) .relayCollectWithSig( - userTwoAddress, - FIRST_PROFILE_ID, - '1', erc721.address, BALANCE_THRESHOLD, + 0, // lzCustomGasAmount collectWithSigData ); const logs = parseLogsNested(await tx.wait(), abi, LZGatedCollectModuleABI.abi); diff --git a/test/LZGatedFollowModule.spec.ts b/test/LZGatedFollowModule.spec.ts index 5bf6672..03873f4 100644 --- a/test/LZGatedFollowModule.spec.ts +++ b/test/LZGatedFollowModule.spec.ts @@ -233,6 +233,7 @@ makeSuiteCleanRoom('LZGatedFollowModule', function () { .relayFollowWithSig( erc721.address, BALANCE_THRESHOLD, + 0, // customGasAmount followWithSigData ) ).to.be.revertedWith('InsufficientBalance'); @@ -245,6 +246,7 @@ makeSuiteCleanRoom('LZGatedFollowModule', function () { .relayFollowWithSig( lzEndpoint.address, BALANCE_THRESHOLD, + 0, // customGasAmount followWithSigData ) ).to.be.revertedWith('InsufficientBalance'); @@ -258,6 +260,7 @@ makeSuiteCleanRoom('LZGatedFollowModule', function () { .relayFollowWithSig( erc721.address, 0, + 0, // customGasAmount followWithSigData ); @@ -274,6 +277,7 @@ makeSuiteCleanRoom('LZGatedFollowModule', function () { .relayFollowWithSig( erc20.address, BALANCE_THRESHOLD, + 0, // customGasAmount followWithSigData ); @@ -281,7 +285,7 @@ makeSuiteCleanRoom('LZGatedFollowModule', function () { expect(messageFailedReason).to.equal('InvalidRemoteInput'); }); - it.only('processes a valid follow', async () => { + it('processes a valid follow', async () => { await erc721.safeMint(userAddress); const tx = await lzGatedProxy @@ -289,7 +293,8 @@ makeSuiteCleanRoom('LZGatedFollowModule', function () { .relayFollowWithSig( erc721.address, BALANCE_THRESHOLD, - followWithSigData + 0, // customGasAmount + followWithSigData, ); const logs = parseLogsNested(await tx.wait(), abi, LZGatedFollowModuleABI.abi); const event = logs.find(({ name }) => name === 'MessageFailed'); @@ -313,6 +318,7 @@ makeSuiteCleanRoom('LZGatedFollowModule', function () { .relayFollowWithSig( erc721.address, BALANCE_THRESHOLD, + 0, // customGasAmount followWithSigData ); @@ -322,6 +328,7 @@ makeSuiteCleanRoom('LZGatedFollowModule', function () { .relayFollowWithSig( erc721.address, BALANCE_THRESHOLD, + 0, // customGasAmount followWithSigData ) ).to.be.reverted; // pretty sure the revert is sig-related but no reason is provided diff --git a/test/LZGatedReferenceModule.spec.ts b/test/LZGatedReferenceModule.spec.ts index e2160df..7f77f08 100644 --- a/test/LZGatedReferenceModule.spec.ts +++ b/test/LZGatedReferenceModule.spec.ts @@ -275,11 +275,9 @@ makeSuiteCleanRoom('LZGatedReferenceModule', function () { .connect(userTwo) .relayCommentWithSig( userTwoAddress, - FIRST_PROFILE_ID + 1, - FIRST_PROFILE_ID, - '1', erc721.address, BALANCE_THRESHOLD, + 0, // lzCustomGasAmount commentWithSigData ) ).to.be.revertedWith('InsufficientBalance'); @@ -291,11 +289,9 @@ makeSuiteCleanRoom('LZGatedReferenceModule', function () { .connect(userTwo) .relayCommentWithSig( userAddress, - FIRST_PROFILE_ID + 1, - FIRST_PROFILE_ID, - '1', lzEndpoint.address, BALANCE_THRESHOLD, + 0, // lzCustomGasAmount commentWithSigData ) ).to.be.revertedWith('InsufficientBalance'); @@ -308,11 +304,9 @@ makeSuiteCleanRoom('LZGatedReferenceModule', function () { .connect(userTwo) .relayCommentWithSig( userTwoAddress, - FIRST_PROFILE_ID + 1, - FIRST_PROFILE_ID, - '1', erc721.address, 0, + 0, // lzCustomGasAmount commentWithSigData ); @@ -328,11 +322,9 @@ makeSuiteCleanRoom('LZGatedReferenceModule', function () { .connect(userTwo) .relayCommentWithSig( userTwoAddress, - FIRST_PROFILE_ID + 1, - FIRST_PROFILE_ID, - '1', erc20.address, BALANCE_THRESHOLD, + 0, // lzCustomGasAmount commentWithSigData ); @@ -347,11 +339,9 @@ makeSuiteCleanRoom('LZGatedReferenceModule', function () { .connect(userTwo) .relayCommentWithSig( userTwoAddress, - FIRST_PROFILE_ID + 1, - FIRST_PROFILE_ID, - '1', erc721.address, BALANCE_THRESHOLD, + 0, // lzCustomGasAmount commentWithSigData ); const logs = parseLogsNested(await tx.wait(), abi, LZGatedReferenceModuleABI.abi); @@ -428,11 +418,9 @@ makeSuiteCleanRoom('LZGatedReferenceModule', function () { .connect(userTwo) .relayMirrorWithSig( userTwoAddress, - FIRST_PROFILE_ID + 1, - FIRST_PROFILE_ID, - '1', erc721.address, BALANCE_THRESHOLD, + 0, // lzCustomGasAmount mirrorWithSigData ) ).to.be.revertedWith('InsufficientBalance'); @@ -444,11 +432,9 @@ makeSuiteCleanRoom('LZGatedReferenceModule', function () { .connect(userTwo) .relayMirrorWithSig( userTwoAddress, - FIRST_PROFILE_ID + 1, - FIRST_PROFILE_ID, - '1', lzEndpoint.address, BALANCE_THRESHOLD, + 0, // lzCustomGasAmount mirrorWithSigData ) ).to.be.revertedWith('InsufficientBalance'); @@ -461,11 +447,9 @@ makeSuiteCleanRoom('LZGatedReferenceModule', function () { .connect(userTwo) .relayMirrorWithSig( userTwoAddress, - FIRST_PROFILE_ID + 1, - FIRST_PROFILE_ID, - '1', erc721.address, 0, + 0, // lzCustomGasAmount mirrorWithSigData ); @@ -481,11 +465,9 @@ makeSuiteCleanRoom('LZGatedReferenceModule', function () { .connect(userTwo) .relayMirrorWithSig( userTwoAddress, - FIRST_PROFILE_ID + 1, - FIRST_PROFILE_ID, - '1', erc20.address, BALANCE_THRESHOLD, + 0, // lzCustomGasAmount mirrorWithSigData ); @@ -500,11 +482,9 @@ makeSuiteCleanRoom('LZGatedReferenceModule', function () { .connect(userTwo) .relayMirrorWithSig( userTwoAddress, - FIRST_PROFILE_ID + 1, - FIRST_PROFILE_ID, - '1', erc721.address, BALANCE_THRESHOLD, + 0, // lzCustomGasAmount mirrorWithSigData ); const logs = parseLogsNested(await tx.wait(), abi, LZGatedReferenceModuleABI.abi);