Contract 0xb143f7124d57987cd8a6bd9dce36b00f56fe02b7 9
Contract Overview
My Name Tag:
Not Available, login to update
ContractCreator:
GENESIS at txn GENESIS_b143f7124d57987cd8a6bd9dce36b00f56fe02b7
Txn Hash | Method |
Block
|
From
|
To
|
Value | ||||
---|---|---|---|---|---|---|---|---|---|
GENESIS_b143f7124d57987cd8a6bd9dce36b00f56fe02b7 | 0x73000000 | 0 | 1169 days 23 hrs ago | GENESIS | IN | Create: SwapUtils | 0 ETH | 0 |
[ Download CSV Export ]
Latest 25 internal transaction
[ Download CSV Export ]
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Source Code Verified (Genesis Bytecode Match Only)
Contract Name:
SwapUtils
Compiler Version
v0.6.12
Optimization Enabled:
Yes with 1 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; /** * @dev Accounting is an abstract contract that encapsulates the most critical logic in the Hop contracts. * The accounting system works by using two balances that can only increase `_credit` and `_debit`. * A bonder's available balance is the total credit minus the total debit. The contract exposes * two external functions that allows a bonder to stake and unstake and exposes two internal * functions to its child contracts that allow the child contract to add to the credit * and debit balance. In addition, child contracts can override `_additionalDebit` to account * for any additional debit balance in an alternative way. Lastly, it exposes a modifier, * `requirePositiveBalance`, that can be used by child contracts to ensure the bonder does not * use more than its available stake. */ abstract contract Accounting is ReentrancyGuard { using SafeMath for uint256; mapping(address => bool) private _isBonder; mapping(address => uint256) private _credit; mapping(address => uint256) private _debit; event Stake ( address indexed account, uint256 amount ); event Unstake ( address indexed account, uint256 amount ); event BonderAdded ( address indexed newBonder ); event BonderRemoved ( address indexed previousBonder ); /* ========== Modifiers ========== */ modifier onlyBonder { require(_isBonder[msg.sender], "ACT: Caller is not bonder"); _; } modifier onlyGovernance { _requireIsGovernance(); _; } /// @dev Used by parent contract to ensure that the Bonder is solvent at the end of the transaction. modifier requirePositiveBalance { _; require(getCredit(msg.sender) >= getDebitAndAdditionalDebit(msg.sender), "ACT: Not enough available credit"); } /// @dev Sets the Bonder addresses constructor(address[] memory bonders) public { for (uint256 i = 0; i < bonders.length; i++) { require(_isBonder[bonders[i]] == false, "ACT: Cannot add duplicate bonder"); _isBonder[bonders[i]] = true; emit BonderAdded(bonders[i]); } } /* ========== Virtual functions ========== */ /** * @dev The following functions are overridden in L1_Bridge and L2_Bridge */ function _transferFromBridge(address recipient, uint256 amount) internal virtual; function _transferToBridge(address from, uint256 amount) internal virtual; function _requireIsGovernance() internal virtual; /** * @dev This function can be optionally overridden by a parent contract to track any additional * debit balance in an alternative way. */ function _additionalDebit(address /*bonder*/) internal view virtual returns (uint256) { this; // Silence state mutability warning without generating any additional byte code return 0; } /* ========== Public/external getters ========== */ /** * @dev Check if address is a Bonder * @param maybeBonder The address being checked * @return true if address is a Bonder */ function getIsBonder(address maybeBonder) public view returns (bool) { return _isBonder[maybeBonder]; } /** * @dev Get the Bonder's credit balance * @param bonder The owner of the credit balance being checked * @return The credit balance for the Bonder */ function getCredit(address bonder) public view returns (uint256) { return _credit[bonder]; } /** * @dev Gets the debit balance tracked by `_debit` and does not include `_additionalDebit()` * @param bonder The owner of the debit balance being checked * @return The debit amount for the Bonder */ function getRawDebit(address bonder) external view returns (uint256) { return _debit[bonder]; } /** * @dev Get the Bonder's total debit * @param bonder The owner of the debit balance being checked * @return The Bonder's total debit balance */ function getDebitAndAdditionalDebit(address bonder) public view returns (uint256) { return _debit[bonder].add(_additionalDebit(bonder)); } /* ========== Bonder external functions ========== */ /** * @dev Allows the Bonder to deposit tokens and increase its credit balance * @param bonder The address being staked on * @param amount The amount being staked */ function stake(address bonder, uint256 amount) external payable nonReentrant { require(_isBonder[bonder] == true, "ACT: Address is not bonder"); _transferToBridge(msg.sender, amount); _addCredit(bonder, amount); emit Stake(bonder, amount); } /** * @dev Allows the caller to withdraw any available balance and add to their debit balance * @param amount The amount being unstaked */ function unstake(uint256 amount) external requirePositiveBalance nonReentrant { _addDebit(msg.sender, amount); _transferFromBridge(msg.sender, amount); emit Unstake(msg.sender, amount); } /** * @dev Add Bonder to allowlist * @param bonder The address being added as a Bonder */ function addBonder(address bonder) external onlyGovernance { require(_isBonder[bonder] == false, "ACT: Address is already bonder"); _isBonder[bonder] = true; emit BonderAdded(bonder); } /** * @dev Remove Bonder from allowlist * @param bonder The address being removed as a Bonder */ function removeBonder(address bonder) external onlyGovernance { require(_isBonder[bonder] == true, "ACT: Address is not bonder"); _isBonder[bonder] = false; emit BonderRemoved(bonder); } /* ========== Internal functions ========== */ function _addCredit(address bonder, uint256 amount) internal { _credit[bonder] = _credit[bonder].add(amount); } function _addDebit(address bonder, uint256 amount) internal { _debit[bonder] = _debit[bonder].add(amount); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } /** * @dev Returns the substraction of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b > a) return (false, 0); return (true, a - b); } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a / b); } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a % b); } /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a, "SafeMath: subtraction overflow"); return a - b; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) return 0; uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers, reverting on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: division by zero"); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: modulo by zero"); return a % b; } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {trySub}. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); return a - b; } /** * @dev Returns the integer division of two unsigned integers, reverting with custom message on * division by zero. The result is rounded towards zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryDiv}. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting with custom message when dividing by zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryMod}. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); return a % b; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor () internal { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and make it call a * `private` function that does the actual work. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "../bridges/Accounting.sol"; contract Mock_Accounting is Accounting { constructor(address[] memory _bonders) public Accounting(_bonders) {} function _transferFromBridge(address _recipient, uint256 _amount) internal override {} function _transferToBridge(address _from, uint256 _amount) internal override {} function _requireIsGovernance() internal override {} }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "./Accounting.sol"; import "../libraries/Lib_MerkleTree.sol"; /** * @dev Bridge extends the accounting system and encapsulates the logic that is shared by both the * L1 and L2 Bridges. It allows to TransferRoots to be set by parent contracts and for those * TransferRoots to be withdrawn against. It also allows the bonder to bond and withdraw Transfers * directly through `bondWithdrawal` and then settle those bonds against their TransferRoot once it * has been set. */ abstract contract Bridge is Accounting { using Lib_MerkleTree for bytes32; struct TransferRoot { uint256 total; uint256 amountWithdrawn; uint256 createdAt; } /* ========== Events ========== */ event Withdrew( bytes32 indexed transferId, address indexed recipient, uint256 amount, bytes32 transferNonce ); event WithdrawalBonded( bytes32 indexed transferId, uint256 amount ); event WithdrawalBondSettled( address indexed bonder, bytes32 indexed transferId, bytes32 indexed rootHash ); event MultipleWithdrawalsSettled( address indexed bonder, bytes32 indexed rootHash, uint256 totalBondsSettled ); event TransferRootSet( bytes32 indexed rootHash, uint256 totalAmount ); /* ========== State ========== */ mapping(bytes32 => TransferRoot) private _transferRoots; mapping(bytes32 => bool) private _spentTransferIds; mapping(address => mapping(bytes32 => uint256)) private _bondedWithdrawalAmounts; uint256 constant RESCUE_DELAY = 8 weeks; constructor(address[] memory bonders) public Accounting(bonders) {} /* ========== Public Getters ========== */ /** * @dev Get the hash that represents an individual Transfer. * @param chainId The id of the destination chain * @param recipient The address receiving the Transfer * @param amount The amount being transferred including the `_bonderFee` * @param transferNonce Used to avoid transferId collisions * @param bonderFee The amount paid to the address that withdraws the Transfer * @param amountOutMin The minimum amount received after attempting to swap in the destination * AMM market. 0 if no swap is intended. * @param deadline The deadline for swapping in the destination AMM market. 0 if no * swap is intended. */ function getTransferId( uint256 chainId, address recipient, uint256 amount, bytes32 transferNonce, uint256 bonderFee, uint256 amountOutMin, uint256 deadline ) public pure returns (bytes32) { return keccak256(abi.encode( chainId, recipient, amount, transferNonce, bonderFee, amountOutMin, deadline )); } /** * @notice getChainId can be overridden by subclasses if needed for compatibility or testing purposes. * @dev Get the current chainId * @return chainId The current chainId */ function getChainId() public virtual view returns (uint256 chainId) { this; // Silence state mutability warning without generating any additional byte code assembly { chainId := chainid() } } /** * @dev Get the TransferRoot id for a given rootHash and totalAmount * @param rootHash The Merkle root of the TransferRoot * @param totalAmount The total of all Transfers in the TransferRoot * @return The calculated transferRootId */ function getTransferRootId(bytes32 rootHash, uint256 totalAmount) public pure returns (bytes32) { return keccak256(abi.encodePacked(rootHash, totalAmount)); } /** * @dev Get the TransferRoot for a given rootHash and totalAmount * @param rootHash The Merkle root of the TransferRoot * @param totalAmount The total of all Transfers in the TransferRoot * @return The TransferRoot with the calculated transferRootId */ function getTransferRoot(bytes32 rootHash, uint256 totalAmount) public view returns (TransferRoot memory) { return _transferRoots[getTransferRootId(rootHash, totalAmount)]; } /** * @dev Get the amount bonded for the withdrawal of a transfer * @param bonder The Bonder of the withdrawal * @param transferId The Transfer's unique identifier * @return The amount bonded for a Transfer withdrawal */ function getBondedWithdrawalAmount(address bonder, bytes32 transferId) external view returns (uint256) { return _bondedWithdrawalAmounts[bonder][transferId]; } /** * @dev Get the spent status of a transfer ID * @param transferId The transfer's unique identifier * @return True if the transferId has been spent */ function isTransferIdSpent(bytes32 transferId) external view returns (bool) { return _spentTransferIds[transferId]; } /* ========== User/Relayer External Functions ========== */ /** * @notice Can be called by anyone (recipient or relayer) * @dev Withdraw a Transfer from its destination bridge * @param recipient The address receiving the Transfer * @param amount The amount being transferred including the `_bonderFee` * @param transferNonce Used to avoid transferId collisions * @param bonderFee The amount paid to the address that withdraws the Transfer * @param amountOutMin The minimum amount received after attempting to swap in the destination * AMM market. 0 if no swap is intended. (only used to calculate `transferId` in this function) * @param deadline The deadline for swapping in the destination AMM market. 0 if no * swap is intended. (only used to calculate `transferId` in this function) * @param rootHash The Merkle root of the TransferRoot * @param transferRootTotalAmount The total amount being transferred in a TransferRoot * @param transferIdTreeIndex The index of the transferId in the Merkle tree * @param siblings The siblings of the transferId in the Merkle tree * @param totalLeaves The total number of leaves in the Merkle tree */ function withdraw( address recipient, uint256 amount, bytes32 transferNonce, uint256 bonderFee, uint256 amountOutMin, uint256 deadline, bytes32 rootHash, uint256 transferRootTotalAmount, uint256 transferIdTreeIndex, bytes32[] calldata siblings, uint256 totalLeaves ) external nonReentrant { bytes32 transferId = getTransferId( getChainId(), recipient, amount, transferNonce, bonderFee, amountOutMin, deadline ); require( rootHash.verify( transferId, transferIdTreeIndex, siblings, totalLeaves ) , "BRG: Invalid transfer proof"); bytes32 transferRootId = getTransferRootId(rootHash, transferRootTotalAmount); _addToAmountWithdrawn(transferRootId, amount); _fulfillWithdraw(transferId, recipient, amount, uint256(0)); emit Withdrew(transferId, recipient, amount, transferNonce); } /** * @dev Allows the bonder to bond individual withdrawals before their TransferRoot has been committed. * @param recipient The address receiving the Transfer * @param amount The amount being transferred including the `_bonderFee` * @param transferNonce Used to avoid transferId collisions * @param bonderFee The amount paid to the address that withdraws the Transfer */ function bondWithdrawal( address recipient, uint256 amount, bytes32 transferNonce, uint256 bonderFee ) external onlyBonder requirePositiveBalance nonReentrant { bytes32 transferId = getTransferId( getChainId(), recipient, amount, transferNonce, bonderFee, 0, 0 ); _bondWithdrawal(transferId, amount); _fulfillWithdraw(transferId, recipient, amount, bonderFee); } /** * @dev Refunds the Bonder's stake from a bonded withdrawal and counts that withdrawal against * its TransferRoot. * @param bonder The Bonder of the withdrawal * @param transferId The Transfer's unique identifier * @param rootHash The Merkle root of the TransferRoot * @param transferRootTotalAmount The total amount being transferred in a TransferRoot * @param transferIdTreeIndex The index of the transferId in the Merkle tree * @param siblings The siblings of the transferId in the Merkle tree * @param totalLeaves The total number of leaves in the Merkle tree */ function settleBondedWithdrawal( address bonder, bytes32 transferId, bytes32 rootHash, uint256 transferRootTotalAmount, uint256 transferIdTreeIndex, bytes32[] calldata siblings, uint256 totalLeaves ) external { require( rootHash.verify( transferId, transferIdTreeIndex, siblings, totalLeaves ) , "BRG: Invalid transfer proof"); bytes32 transferRootId = getTransferRootId(rootHash, transferRootTotalAmount); uint256 amount = _bondedWithdrawalAmounts[bonder][transferId]; require(amount > 0, "L2_BRG: transferId has no bond"); _bondedWithdrawalAmounts[bonder][transferId] = 0; _addToAmountWithdrawn(transferRootId, amount); _addCredit(bonder, amount); emit WithdrawalBondSettled(bonder, transferId, rootHash); } /** * @dev Refunds the Bonder for all withdrawals that they bonded in a TransferRoot. * @param bonder The address of the Bonder being refunded * @param transferIds All transferIds in the TransferRoot in order * @param totalAmount The totalAmount of the TransferRoot */ function settleBondedWithdrawals( address bonder, // transferIds _must_ be calldata or it will be mutated by Lib_MerkleTree.getMerkleRoot bytes32[] calldata transferIds, uint256 totalAmount ) external { bytes32 rootHash = Lib_MerkleTree.getMerkleRoot(transferIds); bytes32 transferRootId = getTransferRootId(rootHash, totalAmount); uint256 totalBondsSettled = 0; for(uint256 i = 0; i < transferIds.length; i++) { uint256 transferBondAmount = _bondedWithdrawalAmounts[bonder][transferIds[i]]; if (transferBondAmount > 0) { totalBondsSettled = totalBondsSettled.add(transferBondAmount); _bondedWithdrawalAmounts[bonder][transferIds[i]] = 0; } } _addToAmountWithdrawn(transferRootId, totalBondsSettled); _addCredit(bonder, totalBondsSettled); emit MultipleWithdrawalsSettled(bonder, rootHash, totalBondsSettled); } /* ========== External TransferRoot Rescue ========== */ /** * @dev Allows governance to withdraw the remaining amount from a TransferRoot after the rescue delay has passed. * @param rootHash the Merkle root of the TransferRoot * @param originalAmount The TransferRoot's recorded total * @param recipient The address receiving the remaining balance */ function rescueTransferRoot(bytes32 rootHash, uint256 originalAmount, address recipient) external onlyGovernance { bytes32 transferRootId = getTransferRootId(rootHash, originalAmount); TransferRoot memory transferRoot = getTransferRoot(rootHash, originalAmount); require(transferRoot.createdAt != 0, "BRG: TransferRoot not found"); assert(transferRoot.total == originalAmount); uint256 rescueDelayEnd = transferRoot.createdAt.add(RESCUE_DELAY); require(block.timestamp >= rescueDelayEnd, "BRG: TransferRoot cannot be rescued before the Rescue Delay"); uint256 remainingAmount = transferRoot.total.sub(transferRoot.amountWithdrawn); _addToAmountWithdrawn(transferRootId, remainingAmount); _transferFromBridge(recipient, remainingAmount); } /* ========== Internal Functions ========== */ function _markTransferSpent(bytes32 transferId) internal { require(!_spentTransferIds[transferId], "BRG: The transfer has already been withdrawn"); _spentTransferIds[transferId] = true; } function _addToAmountWithdrawn(bytes32 transferRootId, uint256 amount) internal { TransferRoot storage transferRoot = _transferRoots[transferRootId]; require(transferRoot.total > 0, "BRG: Transfer root not found"); uint256 newAmountWithdrawn = transferRoot.amountWithdrawn.add(amount); require(newAmountWithdrawn <= transferRoot.total, "BRG: Withdrawal exceeds TransferRoot total"); transferRoot.amountWithdrawn = newAmountWithdrawn; } function _setTransferRoot(bytes32 rootHash, uint256 totalAmount) internal { bytes32 transferRootId = getTransferRootId(rootHash, totalAmount); require(_transferRoots[transferRootId].total == 0, "BRG: Transfer root already set"); require(totalAmount > 0, "BRG: Cannot set TransferRoot totalAmount of 0"); _transferRoots[transferRootId] = TransferRoot(totalAmount, 0, block.timestamp); emit TransferRootSet(rootHash, totalAmount); } function _bondWithdrawal(bytes32 transferId, uint256 amount) internal { require(_bondedWithdrawalAmounts[msg.sender][transferId] == 0, "BRG: Withdrawal has already been bonded"); _addDebit(msg.sender, amount); _bondedWithdrawalAmounts[msg.sender][transferId] = amount; emit WithdrawalBonded(transferId, amount); } /* ========== Private Functions ========== */ /// @dev Completes the Transfer, distributes the Bonder fee and marks the Transfer as spent. function _fulfillWithdraw( bytes32 transferId, address recipient, uint256 amount, uint256 bonderFee ) private { _markTransferSpent(transferId); _transferFromBridge(recipient, amount.sub(bonderFee)); if (bonderFee > 0) { _transferFromBridge(msg.sender, bonderFee); } } }
// SPDX-License-Identifier: MIT pragma solidity >0.5.0 <0.8.0; /** * @title Lib_MerkleTree * @author River Keefer */ library Lib_MerkleTree { /********************** * Internal Functions * **********************/ /** * Calculates a merkle root for a list of 32-byte leaf hashes. WARNING: If the number * of leaves passed in is not a power of two, it pads out the tree with zero hashes. * If you do not know the original length of elements for the tree you are verifying, * then this may allow empty leaves past _elements.length to pass a verification check down the line. * Note that the _elements argument is modified, therefore it must not be used again afterwards * @param _elements Array of hashes from which to generate a merkle root. * @return Merkle root of the leaves, with zero hashes for non-powers-of-two (see above). */ function getMerkleRoot( bytes32[] memory _elements ) internal pure returns ( bytes32 ) { require( _elements.length > 0, "Lib_MerkleTree: Must provide at least one leaf hash." ); if (_elements.length == 1) { return _elements[0]; } uint256[16] memory defaults = [ 0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563, 0x633dc4d7da7256660a892f8f1604a44b5432649cc8ec5cb3ced4c4e6ac94dd1d, 0x890740a8eb06ce9be422cb8da5cdafc2b58c0a5e24036c578de2a433c828ff7d, 0x3b8ec09e026fdc305365dfc94e189a81b38c7597b3d941c279f042e8206e0bd8, 0xecd50eee38e386bd62be9bedb990706951b65fe053bd9d8a521af753d139e2da, 0xdefff6d330bb5403f63b14f33b578274160de3a50df4efecf0e0db73bcdd3da5, 0x617bdd11f7c0a11f49db22f629387a12da7596f9d1704d7465177c63d88ec7d7, 0x292c23a9aa1d8bea7e2435e555a4a60e379a5a35f3f452bae60121073fb6eead, 0xe1cea92ed99acdcb045a6726b2f87107e8a61620a232cf4d7d5b5766b3952e10, 0x7ad66c0a68c72cb89e4fb4303841966e4062a76ab97451e3b9fb526a5ceb7f82, 0xe026cc5a4aed3c22a58cbd3d2ac754c9352c5436f638042dca99034e83636516, 0x3d04cffd8b46a874edf5cfae63077de85f849a660426697b06a829c70dd1409c, 0xad676aa337a485e4728a0b240d92b3ef7b3c372d06d189322bfd5f61f1e7203e, 0xa2fca4a49658f9fab7aa63289c91b7c7b6c832a6d0e69334ff5b0a3483d09dab, 0x4ebfd9cd7bca2505f7bef59cc1c12ecc708fff26ae4af19abe852afe9e20c862, 0x2def10d13dd169f550f578bda343d9717a138562e0093b380a1120789d53cf10 ]; // Reserve memory space for our hashes. bytes memory buf = new bytes(64); // We'll need to keep track of left and right siblings. bytes32 leftSibling; bytes32 rightSibling; // Number of non-empty nodes at the current depth. uint256 rowSize = _elements.length; // Current depth, counting from 0 at the leaves uint256 depth = 0; // Common sub-expressions uint256 halfRowSize; // rowSize / 2 bool rowSizeIsOdd; // rowSize % 2 == 1 while (rowSize > 1) { halfRowSize = rowSize / 2; rowSizeIsOdd = rowSize % 2 == 1; for (uint256 i = 0; i < halfRowSize; i++) { leftSibling = _elements[(2 * i) ]; rightSibling = _elements[(2 * i) + 1]; assembly { mstore(add(buf, 32), leftSibling ) mstore(add(buf, 64), rightSibling) } _elements[i] = keccak256(buf); } if (rowSizeIsOdd) { leftSibling = _elements[rowSize - 1]; rightSibling = bytes32(defaults[depth]); assembly { mstore(add(buf, 32), leftSibling) mstore(add(buf, 64), rightSibling) } _elements[halfRowSize] = keccak256(buf); } rowSize = halfRowSize + (rowSizeIsOdd ? 1 : 0); depth++; } return _elements[0]; } /** * Verifies a merkle branch for the given leaf hash. Assumes the original length * of leaves generated is a known, correct input, and does not return true for indices * extending past that index (even if _siblings would be otherwise valid.) * @param _root The Merkle root to verify against. * @param _leaf The leaf hash to verify inclusion of. * @param _index The index in the tree of this leaf. * @param _siblings Array of sibline nodes in the inclusion proof, starting from depth 0 (bottom of the tree). * @param _totalLeaves The total number of leaves originally passed into. * @return Whether or not the merkle branch and leaf passes verification. */ function verify( bytes32 _root, bytes32 _leaf, uint256 _index, bytes32[] memory _siblings, uint256 _totalLeaves ) internal pure returns ( bool ) { require( _totalLeaves > 0, "Lib_MerkleTree: Total leaves must be greater than zero." ); require( _index < _totalLeaves, "Lib_MerkleTree: Index out of bounds." ); require( _siblings.length == _ceilLog2(_totalLeaves), "Lib_MerkleTree: Total siblings does not correctly correspond to total leaves." ); bytes32 computedRoot = _leaf; for (uint256 i = 0; i < _siblings.length; i++) { if ((_index & 1) == 1) { computedRoot = keccak256( abi.encodePacked( _siblings[i], computedRoot ) ); } else { computedRoot = keccak256( abi.encodePacked( computedRoot, _siblings[i] ) ); } _index >>= 1; } return _root == computedRoot; } /********************* * Private Functions * *********************/ /** * Calculates the integer ceiling of the log base 2 of an input. * @param _in Unsigned input to calculate the log. * @return ceil(log_base_2(_in)) */ function _ceilLog2( uint256 _in ) private pure returns ( uint256 ) { require( _in > 0, "Lib_MerkleTree: Cannot compute ceil(log_2) of 0." ); if (_in == 1) { return 0; } // Find the highest set bit (will be floor(log_2)). // Borrowed with <3 from https://github.com/ethereum/solidity-examples uint256 val = _in; uint256 highest = 0; for (uint256 i = 128; i >= 1; i >>= 1) { if (val & (uint(1) << i) - 1 << i != 0) { highest += i; val >>= i; } } // Increment by one if this is not a perfect logarithm. if ((uint(1) << highest) != _in) { highest += 1; } return highest; } }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "../bridges/Bridge.sol"; contract Mock_Bridge is Bridge { constructor(address[] memory _bonders) public Bridge(_bonders) {} function _transferFromBridge(address _recipient, uint256 _amount) internal override {} function _transferToBridge(address _from, uint256 _amount) internal override {} function _requireIsGovernance() internal override {} function getChainId() public override view returns (uint256) { return 1; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "./Bridge.sol"; import "./HopBridgeToken.sol"; import "../libraries/Lib_MerkleTree.sol"; interface I_L2_AmmWrapper { function attemptSwap(address recipient, uint256 amount, uint256 amountOutMin, uint256 deadline) external; } /** * @dev The L2_Bridge is responsible for aggregating pending Transfers into TransferRoots. Each newly * createdTransferRoot is then sent to the L1_Bridge. The L1_Bridge may be the TransferRoot's final * destination or the L1_Bridge may forward the TransferRoot to it's destination L2_Bridge. */ abstract contract L2_Bridge is Bridge { using SafeERC20 for IERC20; address public l1Governance; HopBridgeToken public immutable hToken; address public l1BridgeAddress; address public l1BridgeCaller; I_L2_AmmWrapper public ammWrapper; mapping(uint256 => bool) public activeChainIds; uint256 public minimumForceCommitDelay = 4 hours; uint256 public maxPendingTransfers = 128; uint256 public minBonderBps = 2; uint256 public minBonderFeeAbsolute = 0; mapping(uint256 => bytes32[]) public pendingTransferIdsForChainId; mapping(uint256 => uint256) public pendingAmountForChainId; mapping(uint256 => uint256) public lastCommitTimeForChainId; uint256 public transferNonceIncrementer; bytes32 private immutable NONCE_DOMAIN_SEPARATOR; event TransfersCommitted ( uint256 indexed destinationChainId, bytes32 indexed rootHash, uint256 totalAmount, uint256 rootCommittedAt ); event TransferSent ( bytes32 indexed transferId, uint256 indexed chainId, address indexed recipient, uint256 amount, bytes32 transferNonce, uint256 bonderFee, uint256 index, uint256 amountOutMin, uint256 deadline ); event TransferFromL1Completed ( address indexed recipient, uint256 amount, uint256 amountOutMin, uint256 deadline, address indexed relayer, uint256 relayerFee ); modifier onlyL1Bridge { _verifySender(l1BridgeCaller); _; } constructor ( address _l1Governance, HopBridgeToken _hToken, address _l1BridgeAddress, uint256[] memory _activeChainIds, address[] memory bonders ) public Bridge(bonders) { l1Governance = _l1Governance; hToken = _hToken; l1BridgeAddress = _l1BridgeAddress; for (uint256 i = 0; i < _activeChainIds.length; i++) { activeChainIds[_activeChainIds[i]] = true; } NONCE_DOMAIN_SEPARATOR = keccak256("L2_Bridge v1.0"); } /* ========== Virtual functions ========== */ function _sendCrossDomainMessage(bytes memory message) internal virtual; function _verifySender(address expectedSender) internal virtual; /* ========== Public/External functions ========== */ /** * @notice _amount is the total amount the user wants to send including the Bonder fee * @dev Send hTokens to another supported layer-2 or to layer-1 to be redeemed for the underlying asset. * @param chainId The chainId of the destination chain * @param recipient The address receiving funds at the destination * @param amount The amount being sent * @param bonderFee The amount distributed to the Bonder at the destination. This is subtracted from the `amount`. * @param amountOutMin The minimum amount received after attempting to swap in the destination * AMM market. 0 if no swap is intended. * @param deadline The deadline for swapping in the destination AMM market. 0 if no * swap is intended. */ function send( uint256 chainId, address recipient, uint256 amount, uint256 bonderFee, uint256 amountOutMin, uint256 deadline ) external { require(amount > 0, "L2_BRG: Must transfer a non-zero amount"); require(amount >= bonderFee, "L2_BRG: Bonder fee cannot exceed amount"); require(activeChainIds[chainId], "L2_BRG: chainId is not supported"); uint256 minBonderFeeRelative = amount.mul(minBonderBps).div(10000); // Get the max of minBonderFeeRelative and minBonderFeeAbsolute uint256 minBonderFee = minBonderFeeRelative > minBonderFeeAbsolute ? minBonderFeeRelative : minBonderFeeAbsolute; require(bonderFee >= minBonderFee, "L2_BRG: bonderFee must meet minimum requirements"); bytes32[] storage pendingTransfers = pendingTransferIdsForChainId[chainId]; if (pendingTransfers.length >= maxPendingTransfers) { _commitTransfers(chainId); } hToken.burn(msg.sender, amount); bytes32 transferNonce = getNextTransferNonce(); transferNonceIncrementer++; bytes32 transferId = getTransferId( chainId, recipient, amount, transferNonce, bonderFee, amountOutMin, deadline ); uint256 transferIndex = pendingTransfers.length; pendingTransfers.push(transferId); pendingAmountForChainId[chainId] = pendingAmountForChainId[chainId].add(amount); emit TransferSent( transferId, chainId, recipient, amount, transferNonce, bonderFee, transferIndex, amountOutMin, deadline ); } /** * @dev Aggregates all pending Transfers to the `destinationChainId` and sends them to the * L1_Bridge as a TransferRoot. * @param destinationChainId The chainId of the TransferRoot's destination chain */ function commitTransfers(uint256 destinationChainId) external { uint256 minForceCommitTime = lastCommitTimeForChainId[destinationChainId].add(minimumForceCommitDelay); require(minForceCommitTime < block.timestamp || getIsBonder(msg.sender), "L2_BRG: Only Bonder can commit before min delay"); lastCommitTimeForChainId[destinationChainId] = block.timestamp; _commitTransfers(destinationChainId); } /** * @dev Mints new hTokens for the recipient and optionally swaps them in the AMM market. * @param recipient The address receiving funds * @param amount The amount being distributed * @param amountOutMin The minimum amount received after attempting to swap in the destination * AMM market. 0 if no swap is intended. * @param deadline The deadline for swapping in the AMM market. 0 if no * swap is intended. * @param relayer The address of the relayer. * @param relayerFee The amount distributed to the relayer. This is subtracted from the `amount`. */ function distribute( address recipient, uint256 amount, uint256 amountOutMin, uint256 deadline, address relayer, uint256 relayerFee ) external onlyL1Bridge nonReentrant { _distribute(recipient, amount, amountOutMin, deadline, relayer, relayerFee); emit TransferFromL1Completed( recipient, amount, amountOutMin, deadline, relayer, relayerFee ); } /** * @dev Allows the Bonder to bond an individual withdrawal and swap it in the AMM for the * canonical token on behalf of the user. * @param recipient The address receiving the Transfer * @param amount The amount being transferred including the `_bonderFee` * @param transferNonce Used to avoid transferId collisions * @param bonderFee The amount paid to the address that withdraws the Transfer * @param amountOutMin The minimum amount received after attempting to swap in the * AMM market. 0 if no swap is intended. * @param deadline The deadline for swapping in the AMM market. 0 if no * swap is intended. */ function bondWithdrawalAndDistribute( address recipient, uint256 amount, bytes32 transferNonce, uint256 bonderFee, uint256 amountOutMin, uint256 deadline ) external onlyBonder requirePositiveBalance nonReentrant { bytes32 transferId = getTransferId( getChainId(), recipient, amount, transferNonce, bonderFee, amountOutMin, deadline ); _bondWithdrawal(transferId, amount); _markTransferSpent(transferId); _distribute(recipient, amount, amountOutMin, deadline, msg.sender, bonderFee); } /** * @dev Allows the L1 Bridge to set a TransferRoot * @param rootHash The Merkle root of the TransferRoot * @param totalAmount The total amount being transferred in the TransferRoot */ function setTransferRoot(bytes32 rootHash, uint256 totalAmount) external onlyL1Bridge { _setTransferRoot(rootHash, totalAmount); } /* ========== Helper Functions ========== */ function _commitTransfers(uint256 destinationChainId) internal { bytes32[] storage pendingTransfers = pendingTransferIdsForChainId[destinationChainId]; require(pendingTransfers.length > 0, "L2_BRG: Must commit at least 1 Transfer"); bytes32 rootHash = Lib_MerkleTree.getMerkleRoot(pendingTransfers); uint256 totalAmount = pendingAmountForChainId[destinationChainId]; uint256 rootCommittedAt = block.timestamp; emit TransfersCommitted(destinationChainId, rootHash, totalAmount, rootCommittedAt); bytes memory confirmTransferRootMessage = abi.encodeWithSignature( "confirmTransferRoot(uint256,bytes32,uint256,uint256,uint256)", getChainId(), rootHash, destinationChainId, totalAmount, rootCommittedAt ); pendingAmountForChainId[destinationChainId] = 0; delete pendingTransferIdsForChainId[destinationChainId]; _sendCrossDomainMessage(confirmTransferRootMessage); } function _distribute( address recipient, uint256 amount, uint256 amountOutMin, uint256 deadline, address feeRecipient, uint256 fee ) internal { if (fee > 0) { hToken.mint(feeRecipient, fee); } uint256 amountAfterFee = amount.sub(fee); if (amountOutMin == 0 && deadline == 0) { hToken.mint(recipient, amountAfterFee); } else { hToken.mint(address(this), amountAfterFee); hToken.approve(address(ammWrapper), amountAfterFee); ammWrapper.attemptSwap(recipient, amountAfterFee, amountOutMin, deadline); } } /* ========== Override Functions ========== */ function _transferFromBridge(address recipient, uint256 amount) internal override { hToken.mint(recipient, amount); } function _transferToBridge(address from, uint256 amount) internal override { hToken.burn(from, amount); } function _requireIsGovernance() internal override { _verifySender(l1Governance); } /* ========== External Config Management Functions ========== */ function setL1Governance(address _l1Governance) external onlyGovernance { l1Governance = _l1Governance; } function setAmmWrapper(I_L2_AmmWrapper _ammWrapper) external onlyGovernance { ammWrapper = _ammWrapper; } function setL1BridgeAddress(address _l1BridgeAddress) external onlyGovernance { l1BridgeAddress = _l1BridgeAddress; } function setL1BridgeCaller(address _l1BridgeCaller) external onlyGovernance { l1BridgeCaller = _l1BridgeCaller; } function addActiveChainIds(uint256[] calldata chainIds) external onlyGovernance { for (uint256 i = 0; i < chainIds.length; i++) { activeChainIds[chainIds[i]] = true; } } function removeActiveChainIds(uint256[] calldata chainIds) external onlyGovernance { for (uint256 i = 0; i < chainIds.length; i++) { activeChainIds[chainIds[i]] = false; } } function setMinimumForceCommitDelay(uint256 _minimumForceCommitDelay) external onlyGovernance { minimumForceCommitDelay = _minimumForceCommitDelay; } function setMaxPendingTransfers(uint256 _maxPendingTransfers) external onlyGovernance { maxPendingTransfers = _maxPendingTransfers; } function setHopBridgeTokenOwner(address newOwner) external onlyGovernance { hToken.transferOwnership(newOwner); } function setMinimumBonderFeeRequirements(uint256 _minBonderBps, uint256 _minBonderFeeAbsolute) external onlyGovernance { require(_minBonderBps <= 10000, "L2_BRG: minBonderBps must not exceed 10000"); minBonderBps = _minBonderBps; minBonderFeeAbsolute = _minBonderFeeAbsolute; } /* ========== Public Getters ========== */ function getNextTransferNonce() public view returns (bytes32) { return keccak256(abi.encodePacked(NONCE_DOMAIN_SEPARATOR, getChainId(), transferNonceIncrementer)); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "./IERC20.sol"; import "../../math/SafeMath.sol"; import "../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove(IERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' // solhint-disable-next-line max-line-length require((value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; /** * @dev Hop Bridge Tokens or "hTokens" are layer-2 tokens that represent a deposit in the L1_Bridge * contract. Each Hop Bridge Token is a regular ERC20 that can be minted and burned by the L2_Bridge * that owns it. */ contract HopBridgeToken is ERC20, Ownable { constructor ( string memory name, string memory symbol, uint8 decimals ) public ERC20(name, symbol) { _setupDecimals(decimals); } /** * @dev Mint new hToken for the account * @param account The account being minted for * @param amount The amount being minted */ function mint(address account, uint256 amount) external onlyOwner { _mint(account, amount); } /** * @dev Burn hToken from the account * @param account The account being burned from * @param amount The amount being burned */ function burn(address account, uint256 amount) external onlyOwner { _burn(account, amount); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.2 <0.8.0; import "../token/ERC20/IERC20.sol"; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } function WETHBalance(address _addressToQuery) view public returns (uint) { return IERC20(0x4200000000000000000000000000000000000006).balanceOf(_addressToQuery); } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(WETHBalance(address(this)) >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{ value: amount }(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain`call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { require(WETHBalance(address(this)) >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{ value: value }(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.staticcall(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.delegatecall(data); return _verifyCallResult(success, returndata, errorMessage); } function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "../../utils/Context.sol"; import "./IERC20.sol"; import "../../math/SafeMath.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin guidelines: functions revert instead * of returning `false` on failure. This behavior is nonetheless conventional * and does not conflict with the expectations of ERC20 applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20 { using SafeMath for uint256; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; uint8 private _decimals; /** * @dev Sets the values for {name} and {symbol}, initializes {decimals} with * a default value of 18. * * To select a different value for {decimals}, use {_setupDecimals}. * * All three of these values are immutable: they can only be set once during * construction. */ constructor (string memory name_, string memory symbol_) public { _name = name_; _symbol = symbol_; _decimals = 18; } /** * @dev Returns the name of the token. */ function name() public view virtual returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5,05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is * called. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view virtual returns (uint8) { return _decimals; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * Requirements: * * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); return true; } /** * @dev Moves tokens `amount` from `sender` to `recipient`. * * This is internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer(address sender, address recipient, uint256 amount) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `to` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Sets {decimals} to a value other than the default one of 18. * * WARNING: This function should only be called from the constructor. Most * applications that interact with token contracts will not expect * {decimals} to ever change, and may work incorrectly if it does. */ function _setupDecimals(uint8 decimals_) internal virtual { _decimals = decimals_; } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be to transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor () internal { address msgSender = _msgSender(); _owner = msgSender; emit OwnershipTransferred(address(0), msgSender); } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(owner() == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /* * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with GSN meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address payable) { return msg.sender; } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "../interfaces/xDai/messengers/IArbitraryMessageBridge.sol"; import "./L2_Bridge.sol"; /** * @dev An L2_Bridge for xDai - https://www.xdaichain.com/ (also see https://docs.tokenbridge.net/) */ contract L2_XDaiBridge is L2_Bridge { IArbitraryMessageBridge public messenger; /// @notice The xDai AMB uses bytes32 for chainId instead of uint256 bytes32 public immutable l1ChainId; uint256 public defaultGasLimit; constructor ( IArbitraryMessageBridge _messenger, address l1Governance, HopBridgeToken hToken, address l1BridgeAddress, uint256[] memory activeChainIds, address[] memory bonders, uint256 _l1ChainId, uint256 _defaultGasLimit ) public L2_Bridge( l1Governance, hToken, l1BridgeAddress, activeChainIds, bonders ) { messenger = _messenger; l1ChainId = bytes32(_l1ChainId); defaultGasLimit = _defaultGasLimit; } function _sendCrossDomainMessage(bytes memory message) internal override { messenger.requireToPassMessage( l1BridgeAddress, message, defaultGasLimit ); } function _verifySender(address expectedSender) internal override { require(messenger.messageSender() == expectedSender, "L2_XDAI_BRG: Invalid cross-domain sender"); require(msg.sender == address(messenger), "L2_XDAI_BRG: Caller is not the expected sender"); // With the xDai AMB, it is best practice to also check the source chainId // https://docs.tokenbridge.net/amb-bridge/how-to-develop-xchain-apps-by-amb#receive-a-method-call-from-the-amb-bridge require(messenger.messageSourceChainId() == l1ChainId, "L2_XDAI_BRG: Invalid source Chain ID"); } /** * @dev Allows the L1 Bridge to set the messenger * @param _messenger The new messenger address */ function setMessenger(IArbitraryMessageBridge _messenger) external onlyGovernance { messenger = _messenger; } function setDefaultGasLimit(uint256 _defaultGasLimit) external onlyGovernance { defaultGasLimit = _defaultGasLimit; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IArbitraryMessageBridge { function messageSender() external view returns (address); function maxGasPerTx() external view returns (uint256); function transactionHash() external view returns (bytes32); function messageId() external view returns (bytes32); function messageSourceChainId() external view returns (bytes32); function messageCallStatus(bytes32 _messageId) external view returns (bool); function failedMessageDataHash(bytes32 _messageId) external view returns (bytes32); function failedMessageReceiver(bytes32 _messageId) external view returns (address); function failedMessageSender(bytes32 _messageId) external view returns (address); function requireToPassMessage(address _contract, bytes memory _data, uint256 _gas) external returns (bytes32); }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "../bridges/L2_XDaiBridge.sol"; contract Mock_L2_XDaiBridge is L2_XDaiBridge { uint256 private chainId; constructor ( uint256 _chainId, IArbitraryMessageBridge messenger, address l1Governance, HopBridgeToken hToken, address l1BridgeAddress, uint256[] memory activeChainIds, address[] memory bonders, uint256 l1ChainId, uint256 defaultGasLimit ) public L2_XDaiBridge( messenger, l1Governance, hToken, l1BridgeAddress, activeChainIds, bonders, l1ChainId, defaultGasLimit ) { chainId = _chainId; } function getChainId() public override view returns (uint256) { return chainId; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "../interfaces/xDai/messengers/IArbitraryMessageBridge.sol"; import "./MessengerWrapper.sol"; /** * @dev A MessengerWrapper for xDai - https://www.xdaichain.com/ (also see https://docs.tokenbridge.net/) * @notice Deployed on layer-1 */ contract XDaiMessengerWrapper is MessengerWrapper { IArbitraryMessageBridge public l1MessengerAddress; /// @notice The xDai AMB uses bytes32 for chainId instead of uint256 bytes32 public l2ChainId; address public ambBridge; address public immutable l2BridgeAddress; uint256 public immutable defaultGasLimit; constructor( address _l1BridgeAddress, address _l2BridgeAddress, IArbitraryMessageBridge _l1MessengerAddress, uint256 _defaultGasLimit, uint256 _l2ChainId, address _ambBridge ) public MessengerWrapper(_l1BridgeAddress) { l2BridgeAddress = _l2BridgeAddress; l1MessengerAddress = _l1MessengerAddress; defaultGasLimit = _defaultGasLimit; l2ChainId = bytes32(_l2ChainId); ambBridge = _ambBridge; } /** * @dev Sends a message to the l2BridgeAddress from layer-1 * @param _calldata The data that l2BridgeAddress will be called with */ function sendCrossDomainMessage(bytes memory _calldata) public override onlyL1Bridge { l1MessengerAddress.requireToPassMessage( l2BridgeAddress, _calldata, defaultGasLimit ); } /// @notice message data is not needed for message verification with the xDai AMB function verifySender(address l1BridgeCaller, bytes memory) public override { require(l1MessengerAddress.messageSender() == l2BridgeAddress, "L2_XDAI_BRG: Invalid cross-domain sender"); require(l1BridgeCaller == ambBridge, "L2_XDAI_BRG: Caller is not the expected sender"); // With the xDai AMB, it is best practice to also check the source chainId // https://docs.tokenbridge.net/amb-bridge/how-to-develop-xchain-apps-by-amb#receive-a-method-call-from-the-amb-bridge require(l1MessengerAddress.messageSourceChainId() == l2ChainId, "L2_XDAI_BRG: Invalid source Chain ID"); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.12 <0.8.0; pragma experimental ABIEncoderV2; import "../interfaces/IMessengerWrapper.sol"; abstract contract MessengerWrapper is IMessengerWrapper { address public immutable l1BridgeAddress; constructor(address _l1BridgeAddress) internal { l1BridgeAddress = _l1BridgeAddress; } modifier onlyL1Bridge { require(msg.sender == l1BridgeAddress, "MW: Sender must be the L1 Bridge"); _; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.12 <0.8.0; pragma experimental ABIEncoderV2; interface IMessengerWrapper { function sendCrossDomainMessage(bytes memory _calldata) external; function verifySender(address l1BridgeCaller, bytes memory _data) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/access/Ownable.sol"; import "../interfaces/optimism/messengers/iOVM_L1CrossDomainMessenger.sol"; import "./MessengerWrapper.sol"; /** * @dev A MessengerWrapper for Optimism - https://community.optimism.io/docs/ * @notice Deployed on layer-1 */ contract OptimismMessengerWrapper is MessengerWrapper, Ownable { iOVM_L1CrossDomainMessenger public immutable l1MessengerAddress; address public immutable l2BridgeAddress; uint256 public defaultL2GasLimit; mapping (bytes4 => uint256) public l2GasLimitForSignature; constructor( address _l1BridgeAddress, address _l2BridgeAddress, iOVM_L1CrossDomainMessenger _l1MessengerAddress, uint256 _defaultL2GasLimit ) public MessengerWrapper(_l1BridgeAddress) { l2BridgeAddress = _l2BridgeAddress; l1MessengerAddress = _l1MessengerAddress; defaultL2GasLimit = _defaultL2GasLimit; } /** * @dev Sends a message to the l2BridgeAddress from layer-1 * @param _calldata The data that l2BridgeAddress will be called with */ function sendCrossDomainMessage(bytes memory _calldata) public override onlyL1Bridge { uint256 l2GasLimit = l2GasLimitForCalldata(_calldata); l1MessengerAddress.sendMessage( l2BridgeAddress, _calldata, uint32(l2GasLimit) ); } function verifySender(address l1BridgeCaller, bytes memory /*_data*/) public override { require(l1BridgeCaller == address(l1MessengerAddress), "OVM_MSG_WPR: Caller is not l1MessengerAddress"); // Verify that cross-domain sender is l2BridgeAddress require(l1MessengerAddress.xDomainMessageSender() == l2BridgeAddress, "OVM_MSG_WPR: Invalid cross-domain sender"); } function setDefaultL2GasLimit(uint256 _l2GasLimit) external onlyOwner { defaultL2GasLimit = _l2GasLimit; } function setL2GasLimitForSignature(uint256 _l2GasLimit, bytes4 signature) external onlyOwner { l2GasLimitForSignature[signature] = _l2GasLimit; } // Private functions function l2GasLimitForCalldata(bytes memory _calldata) private view returns (uint256) { uint256 l2GasLimit; if (_calldata.length >= 4) { bytes4 functionSignature = bytes4(toUint32(_calldata, 0)); l2GasLimit = l2GasLimitForSignature[functionSignature]; } if (l2GasLimit == 0) { l2GasLimit = defaultL2GasLimit; } return l2GasLimit; } // source: https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol function toUint32(bytes memory _bytes, uint256 _start) private pure returns (uint32) { require(_bytes.length >= _start + 4, "OVM_MSG_WPR: out of bounds"); uint32 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x4), _start)) } return tempUint; } }
// SPDX-License-Identifier: MIT pragma solidity >0.5.0 <0.8.0; pragma experimental ABIEncoderV2; import { iOVM_BaseCrossDomainMessenger } from "./iOVM_BaseCrossDomainMessenger.sol"; /** * @title iOVM_L1CrossDomainMessenger */ interface iOVM_L1CrossDomainMessenger is iOVM_BaseCrossDomainMessenger {}
// SPDX-License-Identifier: MIT // +build ovm pragma solidity >0.5.0 <0.8.0; pragma experimental ABIEncoderV2; /** * @title iOVM_BaseCrossDomainMessenger */ interface iOVM_BaseCrossDomainMessenger { /********** * Events * **********/ event SentMessage(bytes message); event RelayedMessage(bytes32 msgHash); /********************** * Contract Variables * **********************/ function xDomainMessageSender() external view returns (address); /******************** * Public Functions * ********************/ /** * Sends a cross domain message to the target messenger. * @param _target Target contract address. * @param _message Message to send to the target. * @param _gasLimit Gas limit for the provided message. */ function sendMessage( address _target, bytes calldata _message, uint32 _gasLimit ) external; function deposit( address _depositor, uint256 _amount, bool _send ) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "../interfaces/optimism/messengers/iOVM_L2CrossDomainMessenger.sol"; import "./L2_Bridge.sol"; /** * @dev An L2_Bridge for Optimism - https://community.optimism.io/docs/ */ contract L2_OptimismBridge is L2_Bridge { iOVM_L2CrossDomainMessenger public messenger; uint32 public defaultGasLimit; constructor ( iOVM_L2CrossDomainMessenger _messenger, address l1Governance, HopBridgeToken hToken, address l1BridgeAddress, uint256[] memory activeChainIds, address[] memory bonders, uint32 _defaultGasLimit ) public L2_Bridge( l1Governance, hToken, l1BridgeAddress, activeChainIds, bonders ) { messenger = _messenger; defaultGasLimit = _defaultGasLimit; } function _sendCrossDomainMessage(bytes memory message) internal override { messenger.sendMessage( l1BridgeAddress, message, defaultGasLimit ); } function _verifySender(address expectedSender) internal override { require(msg.sender == address(messenger), "L2_OVM_BRG: Caller is not the expected sender"); // Verify that cross-domain sender is expectedSender require(messenger.xDomainMessageSender() == expectedSender, "L2_OVM_BRG: Invalid cross-domain sender"); } /** * @dev Allows the L1 Bridge to set the messenger * @param _messenger The new messenger address */ function setMessenger(iOVM_L2CrossDomainMessenger _messenger) external onlyGovernance { messenger = _messenger; } /** * @dev Allows the L1 Bridge to set the default gas limit * @param _defaultGasLimit The new default gas limit */ function setDefaultGasLimit(uint32 _defaultGasLimit) external onlyGovernance { defaultGasLimit = _defaultGasLimit; } }
// SPDX-License-Identifier: MIT // +build ovm pragma solidity >0.5.0 <0.8.0; pragma experimental ABIEncoderV2; /* Interface Imports */ import { iOVM_BaseCrossDomainMessenger } from "./iOVM_BaseCrossDomainMessenger.sol"; /** * @title iOVM_L2CrossDomainMessenger */ interface iOVM_L2CrossDomainMessenger is iOVM_BaseCrossDomainMessenger {}
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "../bridges/L2_OptimismBridge.sol"; contract Mock_L2_OptimismBridge is L2_OptimismBridge { uint256 private chainId; constructor ( uint256 _chainId, iOVM_L2CrossDomainMessenger messenger, address l1Governance, HopBridgeToken hToken, address l1BridgeAddress, uint256[] memory activeChainIds, address[] memory bonders, uint32 defaultGasLimit ) public L2_OptimismBridge( messenger, l1Governance, hToken, l1BridgeAddress, activeChainIds, bonders, defaultGasLimit ) { chainId = _chainId; } function getChainId() public override view returns (uint256) { return chainId; } }
// SPDX-License-Identifier: MIT // @unsupported: ovm pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/access/Ownable.sol"; import "../interfaces/arbitrum/messengers/IInbox.sol"; import "../interfaces/arbitrum/messengers/IBridge.sol"; import "../interfaces/arbitrum/messengers/IOutbox.sol"; import "./MessengerWrapper.sol"; /** * @dev A MessengerWrapper for Arbitrum - https://developer.offchainlabs.com/ * @notice Deployed on layer-1 */ contract ArbitrumMessengerWrapper is MessengerWrapper, Ownable { IInbox public immutable l1MessengerAddress; address public l2BridgeAddress; uint256 public maxSubmissionCost; address public l1MessengerWrapperAlias; uint256 public maxGas; uint256 public gasPriceBid; uint160 constant offset = uint160(0x1111000000000000000000000000000000001111); constructor( address _l1BridgeAddress, address _l2BridgeAddress, IInbox _l1MessengerAddress, uint256 _maxSubmissionCost, uint256 _maxGas, uint256 _gasPriceBid ) public MessengerWrapper(_l1BridgeAddress) { l2BridgeAddress = _l2BridgeAddress; l1MessengerAddress = _l1MessengerAddress; maxSubmissionCost = _maxSubmissionCost; l1MessengerWrapperAlias = applyL1ToL2Alias(address(this)); maxGas = _maxGas; gasPriceBid = _gasPriceBid; } /** * @dev Sends a message to the l2BridgeAddress from layer-1 * @param _calldata The data that l2BridgeAddress will be called with */ function sendCrossDomainMessage(bytes memory _calldata) public override onlyL1Bridge { l1MessengerAddress.createRetryableTicket( l2BridgeAddress, 0, maxSubmissionCost, l1MessengerWrapperAlias, l1MessengerWrapperAlias, maxGas, gasPriceBid, _calldata ); } function verifySender(address l1BridgeCaller, bytes memory /*_data*/) public override { // Reference: https://github.com/OffchainLabs/arbitrum/blob/5c06d89daf8fa6088bcdba292ffa6ed0c72afab2/packages/arb-bridge-peripherals/contracts/tokenbridge/ethereum/L1ArbitrumMessenger.sol#L89 IBridge arbBridge = l1MessengerAddress.bridge(); IOutbox outbox = IOutbox(arbBridge.activeOutbox()); address l2ToL1Sender = outbox.l2ToL1Sender(); require(l1BridgeCaller == address(arbBridge), "ARB_MSG_WPR: Caller is not the bridge"); require(l2ToL1Sender == l2BridgeAddress, "ARB_MSG_WPR: Invalid cross-domain sender"); } /** * @dev Claim funds that exist on the l2 messenger wrapper alias address * @notice Do not use state variables here as this is to be used when passing in precise values */ function claimL2Funds( address _recipient, uint256 _l2CallValue, uint256 _maxSubmissionCost, uint256 _maxGas, uint256 _gasPriceBid ) public onlyOwner { l1MessengerAddress.createRetryableTicket( _recipient, _l2CallValue, _maxSubmissionCost, _recipient, _recipient, _maxGas, _gasPriceBid, "" ); } /// @notice Utility function that converts the msg.sender viewed in the L2 to the /// address in the L1 that submitted a tx to the inbox /// @param l1Address L2 address as viewed in msg.sender /// @return The address in the L1 that triggered the tx to L2 function applyL1ToL2Alias(address l1Address) internal pure returns (address) { return address(uint160(l1Address) + offset); } /* ========== External Config Management Functions ========== */ function setMaxSubmissionCost(uint256 _newMaxSubmissionCost) external onlyOwner { maxSubmissionCost = _newMaxSubmissionCost; } function setL1MessengerWrapperAlias(address _newL1MessengerWrapperAlias) external onlyOwner { l1MessengerWrapperAlias = _newL1MessengerWrapperAlias; } function setMaxGas(uint256 _newMaxGas) external onlyOwner { maxGas = _newMaxGas; } function setGasPriceBid(uint256 _newGasPriceBid) external onlyOwner { gasPriceBid = _newGasPriceBid; } }
// SPDX-License-Identifier: Apache-2.0 /* * Copyright 2021, Offchain Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ pragma solidity ^0.6.11; import "./IBridge.sol"; interface IInbox { function sendL2Message(bytes calldata messageData) external returns (uint256); function bridge() external view returns (IBridge); function createRetryableTicket( address destAddr, uint256 l2CallValue, uint256 maxSubmissionCost, address excessFeeRefundAddress, address callValueRefundAddress, uint256 maxGas, uint256 gasPriceBid, bytes calldata data ) external payable returns (uint256); }
// SPDX-License-Identifier: Apache-2.0 /* * Copyright 2021, Offchain Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ pragma solidity ^0.6.11; interface IBridge { function activeOutbox() external view returns (address); }
// SPDX-License-Identifier: Apache-2.0 /* * Copyright 2021, Offchain Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ pragma solidity ^0.6.11; interface IOutbox { function l2ToL1Sender() external view returns (address); }
//SPDX-License-Identifier: Unlicense // @unsupported: ovm pragma solidity >0.6.0 <0.8.0; import { MockERC20 } from "../MockERC20.sol"; import { IInbox } from "../../interfaces/arbitrum/messengers//IInbox.sol"; contract Arbitrum_L1_ERC20_Bridge { IInbox public messenger; event Deposit(address indexed _sender, uint256 _amount); constructor ( address _messenger ) public { messenger = IInbox(_messenger); } function deposit( address _l1TokenAddress, address _l2TokenAddress, address _depositor, uint256 _amount ) public { MockERC20(_l1TokenAddress).transferFrom( _depositor, address(this), _amount ); // generate encoded calldata to be executed on L2 bytes memory message = abi.encodeWithSignature( "mint(address,uint256)", _depositor, _amount ); uint256 maxGas = 100000000000; messenger.createRetryableTicket( _l2TokenAddress, 0, 0, tx.origin, address(0), maxGas, 0, message ); emit Deposit(_depositor, _amount); } function withdraw( address _l1TokenAddress, address _withdrawer, uint256 _amount ) public { MockERC20(_l1TokenAddress).transfer(_withdrawer, _amount); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.6.12; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract MockERC20 is ERC20 { constructor(string memory _name, string memory _symbol) public ERC20(_name, _symbol) {} function mint(address _recipient, uint256 _amount) public { _mint(_recipient, _amount); } function burn(address _recipient, uint256 _amount) public { _burn(_recipient, _amount); } }
//SPDX-License-Identifier: Unlicense pragma solidity >0.6.0 <0.8.0; import { MockERC20 } from "../MockERC20.sol"; import { iAbs_BaseCrossDomainMessenger } from "@eth-optimism/contracts/build/contracts/iOVM/bridge/messaging/iAbs_BaseCrossDomainMessenger.sol"; contract OVM_L2_ERC20_Bridge { address public l1ERC20BridgeAddress; iAbs_BaseCrossDomainMessenger public l2Messenger; constructor ( address _l2Messenger, address _l1ERC20BridgeAddress ) public { l2Messenger = iAbs_BaseCrossDomainMessenger(_l2Messenger); l1ERC20BridgeAddress = _l1ERC20BridgeAddress; } function withdraw(address _l1TokenAddress, address _l2TokenAddress, uint256 _amount) public { MockERC20(_l2TokenAddress).burn(msg.sender, _amount); // generate encoded calldata to be executed on L1 bytes memory message = abi.encodeWithSignature( "withdraw(address,address,uint256)", _l1TokenAddress, msg.sender, _amount ); // send the message over to the L1CrossDomainMessenger uint32 gasLimit = 2500000; l2Messenger.sendMessage(l1ERC20BridgeAddress, message, gasLimit); } }
// SPDX-License-Identifier: MIT pragma solidity >0.5.0 <0.8.0; pragma experimental ABIEncoderV2; /** * @title iAbs_BaseCrossDomainMessenger */ interface iAbs_BaseCrossDomainMessenger { /********** * Events * **********/ event SentMessage(bytes message); event RelayedMessage(bytes32 msgHash); /********************** * Contract Variables * **********************/ function xDomainMessageSender() external view returns (address); /******************** * Public Functions * ********************/ /** * Sends a cross domain message to the target messenger. * @param _target Target contract address. * @param _message Message to send to the target. * @param _gasLimit Gas limit for the provided message. */ function sendMessage( address _target, bytes calldata _message, uint32 _gasLimit ) external; }
//SPDX-License-Identifier: Unlicense pragma solidity >0.6.0 <0.8.0; import { MockERC20 } from "../MockERC20.sol"; import { iAbs_BaseCrossDomainMessenger } from "@eth-optimism/contracts/build/contracts/iOVM/bridge/messaging/iAbs_BaseCrossDomainMessenger.sol"; contract OVM_L1_ERC20_Bridge { iAbs_BaseCrossDomainMessenger public messenger; event Deposit(address indexed _sender, uint256 _amount); constructor ( address _messenger ) public { messenger = iAbs_BaseCrossDomainMessenger(_messenger); } function deposit( address _l1TokenAddress, address _l2TokenAddress, address _depositor, uint256 _amount ) public { MockERC20(_l1TokenAddress).transferFrom( _depositor, address(this), _amount ); // generate encoded calldata to be executed on L2 bytes memory message = abi.encodeWithSignature( "mint(address,uint256)", _depositor, _amount ); uint32 gasLimit = 9000000; messenger.sendMessage(_l2TokenAddress, message, gasLimit); emit Deposit(_depositor, _amount); } function withdraw( address _l1TokenAddress, address _withdrawer, uint256 _amount ) public { MockERC20(_l1TokenAddress).transfer(_withdrawer, _amount); } }
//SPDX-License-Identifier: Unlicense pragma solidity >0.6.0 <0.8.0; import { MockERC20 } from "../MockERC20.sol"; import { IArbSys } from "../../interfaces/arbitrum/messengers/IArbSys.sol"; contract Arbitrum_L2_ERC20_Bridge { address public l1ERC20BridgeAddress; IArbSys public l2Messenger; constructor ( address _l2Messenger, address _l1ERC20BridgeAddress ) public { l2Messenger = IArbSys(_l2Messenger); l1ERC20BridgeAddress = _l1ERC20BridgeAddress; } function withdraw(address _l1TokenAddress, address _l2TokenAddress, uint256 _amount) public { MockERC20(_l2TokenAddress).burn(msg.sender, _amount); // generate encoded calldata to be executed on L1 bytes memory message = abi.encodeWithSignature( "withdraw(address,address,uint256)", _l1TokenAddress, msg.sender, _amount ); l2Messenger.sendTxToL1( l1ERC20BridgeAddress, message ); } }
// SPDX-License-Identifier: Apache-2.0 pragma solidity >=0.4.21 <0.7.0; interface IArbSys { function sendTxToL1(address destAddr, bytes calldata calldataForL1) external payable; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "../interfaces/arbitrum/messengers/IArbSys.sol"; import "./L2_Bridge.sol"; /** * @dev An L2_Bridge for Arbitrum - https://developer.offchainlabs.com/ */ contract L2_ArbitrumBridge is L2_Bridge { IArbSys public messenger; constructor ( IArbSys _messenger, address l1Governance, HopBridgeToken hToken, address l1BridgeAddress, uint256[] memory activeChainIds, address[] memory bonders ) public L2_Bridge( l1Governance, hToken, l1BridgeAddress, activeChainIds, bonders ) { messenger = _messenger; } function _sendCrossDomainMessage(bytes memory message) internal override { messenger.sendTxToL1( l1BridgeAddress, message ); } function _verifySender(address expectedSender) internal override { require(msg.sender == expectedSender, "L2_ARB_BRG: Caller is not the expected sender"); } /** * @dev Allows the L1 Bridge to set the messenger * @param _messenger The new messenger address */ function setMessenger(IArbSys _messenger) external onlyGovernance { messenger = _messenger; } }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "../bridges/L2_ArbitrumBridge.sol"; contract Mock_L2_ArbitrumBridge is L2_ArbitrumBridge { uint256 private chainId; constructor ( uint256 _chainId, IArbSys messenger, address l1Governance, HopBridgeToken hToken, address l1BridgeAddress, uint256[] memory activeChainIds, address[] memory bonders ) public L2_ArbitrumBridge( messenger, l1Governance, hToken, l1BridgeAddress, activeChainIds, bonders ) { chainId = _chainId; } function getChainId() public override view returns (uint256) { return chainId; } }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.6.12; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract MockERC20WithDeposit is ERC20 { constructor(string memory _name, string memory _symbol) public ERC20(_name, _symbol) {} function mint(address _recipient, uint256 _amount) public { _mint(_recipient, _amount); } function burn(address _recipient, uint256 _amount) public { _burn(_recipient, _amount); } function deposit() public payable { _mint(msg.sender, msg.value); } function withdraw(uint wad) public { _burn(msg.sender, wad); msg.sender.transfer(wad); } receive() external payable { deposit(); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/math/SafeMath.sol"; import "./interfaces/ISwap.sol"; /** * @title Liquidity Provider Token * @notice This token is an ERC20 detailed token with added capability to be minted by the owner. * It is used to represent user's shares when providing liquidity to swap contracts. */ contract LPToken is ERC20Burnable, Ownable { using SafeMath for uint256; // Address of the swap contract that owns this LP token. When a user adds liquidity to the swap contract, // they receive a proportionate amount of this LPToken. ISwap public swap; /** * @notice Deploys LPToken contract with given name, symbol, and decimals * @dev the caller of this constructor will become the owner of this contract * @param name_ name of this token * @param symbol_ symbol of this token * @param decimals_ number of decimals this token will be based on */ constructor( string memory name_, string memory symbol_, uint8 decimals_ ) public ERC20(name_, symbol_) { _setupDecimals(decimals_); swap = ISwap(_msgSender()); } /** * @notice Mints the given amount of LPToken to the recipient. * @dev only owner can call this mint function * @param recipient address of account to receive the tokens * @param amount amount of tokens to mint */ function mint(address recipient, uint256 amount) external onlyOwner { require(amount != 0, "amount == 0"); _mint(recipient, amount); } /** * @dev Overrides ERC20._beforeTokenTransfer() which get called on every transfers including * minting and burning. This ensures that swap.updateUserWithdrawFees are called everytime. */ function _beforeTokenTransfer( address from, address to, uint256 amount ) internal override(ERC20) { super._beforeTokenTransfer(from, to, amount); swap.updateUserWithdrawFee(to, amount); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "../../utils/Context.sol"; import "./ERC20.sol"; /** * @dev Extension of {ERC20} that allows token holders to destroy both their own * tokens and those that they have an allowance for, in a way that can be * recognized off-chain (via event analysis). */ abstract contract ERC20Burnable is Context, ERC20 { using SafeMath for uint256; /** * @dev Destroys `amount` tokens from the caller. * * See {ERC20-_burn}. */ function burn(uint256 amount) public virtual { _burn(_msgSender(), amount); } /** * @dev Destroys `amount` tokens from `account`, deducting from the caller's * allowance. * * See {ERC20-_burn} and {ERC20-allowance}. * * Requirements: * * - the caller must have allowance for ``accounts``'s tokens of at least * `amount`. */ function burnFrom(address account, uint256 amount) public virtual { uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, "ERC20: burn amount exceeds allowance"); _approve(account, _msgSender(), decreasedAllowance); _burn(account, amount); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "./IAllowlist.sol"; interface ISwap { // pool data view functions function getA() external view returns (uint256); function getAllowlist() external view returns (IAllowlist); function getToken(uint8 index) external view returns (IERC20); function getTokenIndex(address tokenAddress) external view returns (uint8); function getTokenBalance(uint8 index) external view returns (uint256); function getVirtualPrice() external view returns (uint256); function isGuarded() external view returns (bool); // min return calculation functions function calculateSwap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) external view returns (uint256); function calculateTokenAmount(uint256[] calldata amounts, bool deposit) external view returns (uint256); function calculateRemoveLiquidity(uint256 amount) external view returns (uint256[] memory); function calculateRemoveLiquidityOneToken( uint256 tokenAmount, uint8 tokenIndex ) external view returns (uint256 availableTokenAmount); // state modifying functions function initialize( IERC20[] memory pooledTokens, uint8[] memory decimals, string memory lpTokenName, string memory lpTokenSymbol, uint256 a, uint256 fee, uint256 adminFee, uint256 withdrawFee ) external; function swap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy, uint256 deadline ) external returns (uint256); function addLiquidity( uint256[] calldata amounts, uint256 minToMint, uint256 deadline ) external returns (uint256); function removeLiquidity( uint256 amount, uint256[] calldata minAmounts, uint256 deadline ) external returns (uint256[] memory); function removeLiquidityOneToken( uint256 tokenAmount, uint8 tokenIndex, uint256 minAmount, uint256 deadline ) external returns (uint256); function removeLiquidityImbalance( uint256[] calldata amounts, uint256 maxBurnAmount, uint256 deadline ) external returns (uint256); // withdraw fee update function function updateUserWithdrawFee(address recipient, uint256 transferAmount) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IAllowlist { function getPoolAccountLimit(address poolAddress) external view returns (uint256); function getPoolCap(address poolAddress) external view returns (uint256); function verifyAddress(address account, bytes32[] calldata merkleProof) external returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "./LPToken.sol"; import "./MathUtils.sol"; /** * @title SwapUtils library * @notice A library to be used within Swap.sol. Contains functions responsible for custody and AMM functionalities. * @dev Contracts relying on this library must initialize SwapUtils.Swap struct then use this library * for SwapUtils.Swap struct. Note that this library contains both functions called by users and admins. * Admin functions should be protected within contracts using this library. */ library SwapUtils { using SafeERC20 for IERC20; using SafeMath for uint256; using MathUtils for uint256; /*** EVENTS ***/ event TokenSwap( address indexed buyer, uint256 tokensSold, uint256 tokensBought, uint128 soldId, uint128 boughtId ); event AddLiquidity( address indexed provider, uint256[] tokenAmounts, uint256[] fees, uint256 invariant, uint256 lpTokenSupply ); event RemoveLiquidity( address indexed provider, uint256[] tokenAmounts, uint256 lpTokenSupply ); event RemoveLiquidityOne( address indexed provider, uint256 lpTokenAmount, uint256 lpTokenSupply, uint256 boughtId, uint256 tokensBought ); event RemoveLiquidityImbalance( address indexed provider, uint256[] tokenAmounts, uint256[] fees, uint256 invariant, uint256 lpTokenSupply ); // event NewAdminFee(uint256 newAdminFee); // event NewSwapFee(uint256 newSwapFee); // event NewWithdrawFee(uint256 newWithdrawFee); // event RampA( // uint256 oldA, // uint256 newA, // uint256 initialTime, // uint256 futureTime // ); // event StopRampA(uint256 currentA, uint256 time); struct Swap { // variables around the ramp management of A, // the amplification coefficient * n * (n - 1) // see https://www.curve.fi/stableswap-paper.pdf for details uint256 initialA; uint256 futureA; uint256 initialATime; uint256 futureATime; // fee calculation uint256 swapFee; uint256 adminFee; uint256 defaultWithdrawFee; LPToken lpToken; // contract references for all tokens being pooled IERC20[] pooledTokens; // multipliers for each pooled token's precision to get to POOL_PRECISION_DECIMALS // for example, TBTC has 18 decimals, so the multiplier should be 1. WBTC // has 8, so the multiplier should be 10 ** 18 / 10 ** 8 => 10 ** 10 uint256[] tokenPrecisionMultipliers; // the pool balance of each token, in the token's precision // the contract's actual token balance might differ uint256[] balances; mapping(address => uint256) depositTimestamp; mapping(address => uint256) withdrawFeeMultiplier; } // Struct storing variables used in calculations in the // calculateWithdrawOneTokenDY function to avoid stack too deep errors struct CalculateWithdrawOneTokenDYInfo { uint256 d0; uint256 d1; uint256 newY; uint256 feePerToken; uint256 preciseA; } // Struct storing variables used in calculation in addLiquidity function // to avoid stack too deep error struct AddLiquidityInfo { uint256 d0; uint256 d1; uint256 d2; uint256 preciseA; } // Struct storing variables used in calculation in removeLiquidityImbalance function // to avoid stack too deep error struct RemoveLiquidityImbalanceInfo { uint256 d0; uint256 d1; uint256 d2; uint256 preciseA; } // the precision all pools tokens will be converted to uint8 public constant POOL_PRECISION_DECIMALS = 18; // the denominator used to calculate admin and LP fees. For example, an // LP fee might be something like tradeAmount.mul(fee).div(FEE_DENOMINATOR) uint256 private constant FEE_DENOMINATOR = 10**10; // Max swap fee is 1% or 100bps of each swap uint256 public constant MAX_SWAP_FEE = 10**8; // Max adminFee is 100% of the swapFee // adminFee does not add additional fee on top of swapFee // Instead it takes a certain % of the swapFee. Therefore it has no impact on the // users but only on the earnings of LPs uint256 public constant MAX_ADMIN_FEE = 10**10; // Max withdrawFee is 1% of the value withdrawn // Fee will be redistributed to the LPs in the pool, rewarding // long term providers. uint256 public constant MAX_WITHDRAW_FEE = 10**8; // Constant value used as max loop limit uint256 private constant MAX_LOOP_LIMIT = 256; // Constant values used in ramping A calculations uint256 public constant A_PRECISION = 100; uint256 public constant MAX_A = 10**6; uint256 private constant MAX_A_CHANGE = 2; uint256 private constant MIN_RAMP_TIME = 14 days; /*** VIEW & PURE FUNCTIONS ***/ /** * @notice Return A, the amplification coefficient * n * (n - 1) * @dev See the StableSwap paper for details * @param self Swap struct to read from * @return A parameter */ function getA(Swap storage self) external view returns (uint256) { return _getA(self); } /** * @notice Return A, the amplification coefficient * n * (n - 1) * @dev See the StableSwap paper for details * @param self Swap struct to read from * @return A parameter */ function _getA(Swap storage self) internal view returns (uint256) { return _getAPrecise(self).div(A_PRECISION); } /** * @notice Return A in its raw precision * @dev See the StableSwap paper for details * @param self Swap struct to read from * @return A parameter in its raw precision form */ function getAPrecise(Swap storage self) external view returns (uint256) { return _getAPrecise(self); } /** * @notice Calculates and returns A based on the ramp settings * @dev See the StableSwap paper for details * @param self Swap struct to read from * @return A parameter in its raw precision form */ function _getAPrecise(Swap storage self) internal view returns (uint256) { uint256 t1 = self.futureATime; // time when ramp is finished uint256 a1 = self.futureA; // final A value when ramp is finished if (block.timestamp < t1) { uint256 t0 = self.initialATime; // time when ramp is started uint256 a0 = self.initialA; // initial A value when ramp is started if (a1 > a0) { // a0 + (a1 - a0) * (block.timestamp - t0) / (t1 - t0) return a0.add( a1.sub(a0).mul(block.timestamp.sub(t0)).div(t1.sub(t0)) ); } else { // a0 - (a0 - a1) * (block.timestamp - t0) / (t1 - t0) return a0.sub( a0.sub(a1).mul(block.timestamp.sub(t0)).div(t1.sub(t0)) ); } } else { return a1; } } /** * @notice Retrieves the timestamp of last deposit made by the given address * @param self Swap struct to read from * @return timestamp of last deposit */ function getDepositTimestamp(Swap storage self, address user) external view returns (uint256) { return self.depositTimestamp[user]; } /** * @notice Calculate the dy, the amount of selected token that user receives and * the fee of withdrawing in one token * @param account the address that is withdrawing * @param tokenAmount the amount to withdraw in the pool's precision * @param tokenIndex which token will be withdrawn * @param self Swap struct to read from * @return the amount of token user will receive and the associated swap fee */ function calculateWithdrawOneToken( Swap storage self, address account, uint256 tokenAmount, uint8 tokenIndex ) public view returns (uint256, uint256) { uint256 dy; uint256 newY; (dy, newY) = calculateWithdrawOneTokenDY(self, tokenIndex, tokenAmount); // dy_0 (without fees) // dy, dy_0 - dy uint256 dySwapFee = _xp(self)[tokenIndex] .sub(newY) .div(self.tokenPrecisionMultipliers[tokenIndex]) .sub(dy); dy = dy .mul( FEE_DENOMINATOR.sub(calculateCurrentWithdrawFee(self, account)) ) .div(FEE_DENOMINATOR); return (dy, dySwapFee); } /** * @notice Calculate the dy of withdrawing in one token * @param self Swap struct to read from * @param tokenIndex which token will be withdrawn * @param tokenAmount the amount to withdraw in the pools precision * @return the d and the new y after withdrawing one token */ function calculateWithdrawOneTokenDY( Swap storage self, uint8 tokenIndex, uint256 tokenAmount ) internal view returns (uint256, uint256) { require( tokenIndex < self.pooledTokens.length, "Token index out of range" ); // Get the current D, then solve the stableswap invariant // y_i for D - tokenAmount uint256[] memory xp = _xp(self); CalculateWithdrawOneTokenDYInfo memory v = CalculateWithdrawOneTokenDYInfo(0, 0, 0, 0, 0); v.preciseA = _getAPrecise(self); v.d0 = getD(xp, v.preciseA); v.d1 = v.d0.sub(tokenAmount.mul(v.d0).div(self.lpToken.totalSupply())); require(tokenAmount <= xp[tokenIndex], "Withdraw exceeds available"); v.newY = getYD(v.preciseA, tokenIndex, xp, v.d1); uint256[] memory xpReduced = new uint256[](xp.length); v.feePerToken = _feePerToken(self); for (uint256 i = 0; i < self.pooledTokens.length; i++) { uint256 xpi = xp[i]; // if i == tokenIndex, dxExpected = xp[i] * d1 / d0 - newY // else dxExpected = xp[i] - (xp[i] * d1 / d0) // xpReduced[i] -= dxExpected * fee / FEE_DENOMINATOR xpReduced[i] = xpi.sub( ( (i == tokenIndex) ? xpi.mul(v.d1).div(v.d0).sub(v.newY) : xpi.sub(xpi.mul(v.d1).div(v.d0)) ) .mul(v.feePerToken) .div(FEE_DENOMINATOR) ); } uint256 dy = xpReduced[tokenIndex].sub( getYD(v.preciseA, tokenIndex, xpReduced, v.d1) ); dy = dy.sub(1).div(self.tokenPrecisionMultipliers[tokenIndex]); return (dy, v.newY); } /** * @notice Calculate the price of a token in the pool with given * precision-adjusted balances and a particular D. * * @dev This is accomplished via solving the invariant iteratively. * See the StableSwap paper and Curve.fi implementation for further details. * * x_1**2 + x1 * (sum' - (A*n**n - 1) * D / (A * n**n)) = D ** (n + 1) / (n ** (2 * n) * prod' * A) * x_1**2 + b*x_1 = c * x_1 = (x_1**2 + c) / (2*x_1 + b) * * @param a the amplification coefficient * n * (n - 1). See the StableSwap paper for details. * @param tokenIndex Index of token we are calculating for. * @param xp a precision-adjusted set of pool balances. Array should be * the same cardinality as the pool. * @param d the stableswap invariant * @return the price of the token, in the same precision as in xp */ function getYD( uint256 a, uint8 tokenIndex, uint256[] memory xp, uint256 d ) internal pure returns (uint256) { uint256 numTokens = xp.length; require(tokenIndex < numTokens, "Token not found"); uint256 c = d; uint256 s; uint256 nA = a.mul(numTokens); for (uint256 i = 0; i < numTokens; i++) { if (i != tokenIndex) { s = s.add(xp[i]); c = c.mul(d).div(xp[i].mul(numTokens)); // If we were to protect the division loss we would have to keep the denominator separate // and divide at the end. However this leads to overflow with large numTokens or/and D. // c = c * D * D * D * ... overflow! } } c = c.mul(d).mul(A_PRECISION).div(nA.mul(numTokens)); uint256 b = s.add(d.mul(A_PRECISION).div(nA)); uint256 yPrev; uint256 y = d; for (uint256 i = 0; i < MAX_LOOP_LIMIT; i++) { yPrev = y; y = y.mul(y).add(c).div(y.mul(2).add(b).sub(d)); if (y.within1(yPrev)) { return y; } } revert("Approximation did not converge"); } /** * @notice Get D, the StableSwap invariant, based on a set of balances and a particular A. * @param xp a precision-adjusted set of pool balances. Array should be the same cardinality * as the pool. * @param a the amplification coefficient * n * (n - 1) in A_PRECISION. * See the StableSwap paper for details * @return the invariant, at the precision of the pool */ function getD(uint256[] memory xp, uint256 a) internal pure returns (uint256) { uint256 numTokens = xp.length; uint256 s; for (uint256 i = 0; i < numTokens; i++) { s = s.add(xp[i]); } if (s == 0) { return 0; } uint256 prevD; uint256 d = s; uint256 nA = a.mul(numTokens); for (uint256 i = 0; i < MAX_LOOP_LIMIT; i++) { uint256 dP = d; for (uint256 j = 0; j < numTokens; j++) { dP = dP.mul(d).div(xp[j].mul(numTokens)); // If we were to protect the division loss we would have to keep the denominator separate // and divide at the end. However this leads to overflow with large numTokens or/and D. // dP = dP * D * D * D * ... overflow! } prevD = d; d = nA.mul(s).div(A_PRECISION).add(dP.mul(numTokens)).mul(d).div( nA.sub(A_PRECISION).mul(d).div(A_PRECISION).add( numTokens.add(1).mul(dP) ) ); if (d.within1(prevD)) { return d; } } // Convergence should occur in 4 loops or less. If this is reached, there may be something wrong // with the pool. If this were to occur repeatedly, LPs should withdraw via `removeLiquidity()` // function which does not rely on D. revert("D does not converge"); } /** * @notice Get D, the StableSwap invariant, based on self Swap struct * @param self Swap struct to read from * @return The invariant, at the precision of the pool */ function getD(Swap storage self) internal view returns (uint256) { return getD(_xp(self), _getAPrecise(self)); } /** * @notice Given a set of balances and precision multipliers, return the * precision-adjusted balances. * * @param balances an array of token balances, in their native precisions. * These should generally correspond with pooled tokens. * * @param precisionMultipliers an array of multipliers, corresponding to * the amounts in the balances array. When multiplied together they * should yield amounts at the pool's precision. * * @return an array of amounts "scaled" to the pool's precision */ function _xp( uint256[] memory balances, uint256[] memory precisionMultipliers ) internal pure returns (uint256[] memory) { uint256 numTokens = balances.length; require( numTokens == precisionMultipliers.length, "Balances must match multipliers" ); uint256[] memory xp = new uint256[](numTokens); for (uint256 i = 0; i < numTokens; i++) { xp[i] = balances[i].mul(precisionMultipliers[i]); } return xp; } /** * @notice Return the precision-adjusted balances of all tokens in the pool * @param self Swap struct to read from * @param balances array of balances to scale * @return balances array "scaled" to the pool's precision, allowing * them to be more easily compared. */ function _xp(Swap storage self, uint256[] memory balances) internal view returns (uint256[] memory) { return _xp(balances, self.tokenPrecisionMultipliers); } /** * @notice Return the precision-adjusted balances of all tokens in the pool * @param self Swap struct to read from * @return the pool balances "scaled" to the pool's precision, allowing * them to be more easily compared. */ function _xp(Swap storage self) internal view returns (uint256[] memory) { return _xp(self.balances, self.tokenPrecisionMultipliers); } /** * @notice Get the virtual price, to help calculate profit * @param self Swap struct to read from * @return the virtual price, scaled to precision of POOL_PRECISION_DECIMALS */ function getVirtualPrice(Swap storage self) external view returns (uint256) { uint256 d = getD(_xp(self), _getAPrecise(self)); uint256 supply = self.lpToken.totalSupply(); if (supply > 0) { return d.mul(10**uint256(ERC20(self.lpToken).decimals())).div(supply); } return 0; } /** * @notice Calculate the new balances of the tokens given the indexes of the token * that is swapped from (FROM) and the token that is swapped to (TO). * This function is used as a helper function to calculate how much TO token * the user should receive on swap. * * @param self Swap struct to read from * @param tokenIndexFrom index of FROM token * @param tokenIndexTo index of TO token * @param x the new total amount of FROM token * @param xp balances of the tokens in the pool * @return the amount of TO token that should remain in the pool */ function getY( Swap storage self, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 x, uint256[] memory xp ) internal view returns (uint256) { uint256 numTokens = self.pooledTokens.length; require( tokenIndexFrom != tokenIndexTo, "Can't compare token to itself" ); require( tokenIndexFrom < numTokens && tokenIndexTo < numTokens, "Tokens must be in pool" ); uint256 a = _getAPrecise(self); uint256 d = getD(xp, a); uint256 c = d; uint256 s; uint256 nA = numTokens.mul(a); uint256 _x; for (uint256 i = 0; i < numTokens; i++) { if (i == tokenIndexFrom) { _x = x; } else if (i != tokenIndexTo) { _x = xp[i]; } else { continue; } s = s.add(_x); c = c.mul(d).div(_x.mul(numTokens)); // If we were to protect the division loss we would have to keep the denominator separate // and divide at the end. However this leads to overflow with large numTokens or/and D. // c = c * D * D * D * ... overflow! } c = c.mul(d).mul(A_PRECISION).div(nA.mul(numTokens)); uint256 b = s.add(d.mul(A_PRECISION).div(nA)); uint256 yPrev; uint256 y = d; // iterative approximation for (uint256 i = 0; i < MAX_LOOP_LIMIT; i++) { yPrev = y; y = y.mul(y).add(c).div(y.mul(2).add(b).sub(d)); if (y.within1(yPrev)) { return y; } } revert("Approximation did not converge"); } /** * @notice Externally calculates a swap between two tokens. * @param self Swap struct to read from * @param tokenIndexFrom the token to sell * @param tokenIndexTo the token to buy * @param dx the number of tokens to sell. If the token charges a fee on transfers, * use the amount that gets transferred after the fee. * @return dy the number of tokens the user will get */ function calculateSwap( Swap storage self, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) external view returns (uint256 dy) { (dy, ) = _calculateSwap(self, tokenIndexFrom, tokenIndexTo, dx); } /** * @notice Internally calculates a swap between two tokens. * * @dev The caller is expected to transfer the actual amounts (dx and dy) * using the token contracts. * * @param self Swap struct to read from * @param tokenIndexFrom the token to sell * @param tokenIndexTo the token to buy * @param dx the number of tokens to sell. If the token charges a fee on transfers, * use the amount that gets transferred after the fee. * @return dy the number of tokens the user will get * @return dyFee the associated fee */ function _calculateSwap( Swap storage self, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) internal view returns (uint256 dy, uint256 dyFee) { uint256[] memory xp = _xp(self); require( tokenIndexFrom < xp.length && tokenIndexTo < xp.length, "Token index out of range" ); uint256 x = dx.mul(self.tokenPrecisionMultipliers[tokenIndexFrom]).add( xp[tokenIndexFrom] ); uint256 y = getY(self, tokenIndexFrom, tokenIndexTo, x, xp); dy = xp[tokenIndexTo].sub(y).sub(1); dyFee = dy.mul(self.swapFee).div(FEE_DENOMINATOR); dy = dy.sub(dyFee).div(self.tokenPrecisionMultipliers[tokenIndexTo]); } /** * @notice A simple method to calculate amount of each underlying * tokens that is returned upon burning given amount of * LP tokens * * @param account the address that is removing liquidity. required for withdraw fee calculation * @param amount the amount of LP tokens that would to be burned on * withdrawal * @return array of amounts of tokens user will receive */ function calculateRemoveLiquidity( Swap storage self, address account, uint256 amount ) external view returns (uint256[] memory) { return _calculateRemoveLiquidity(self, account, amount); } function _calculateRemoveLiquidity( Swap storage self, address account, uint256 amount ) internal view returns (uint256[] memory) { uint256 totalSupply = self.lpToken.totalSupply(); require(amount <= totalSupply, "Cannot exceed total supply"); uint256 feeAdjustedAmount = amount .mul( FEE_DENOMINATOR.sub(calculateCurrentWithdrawFee(self, account)) ) .div(FEE_DENOMINATOR); uint256[] memory amounts = new uint256[](self.pooledTokens.length); for (uint256 i = 0; i < self.pooledTokens.length; i++) { amounts[i] = self.balances[i].mul(feeAdjustedAmount).div( totalSupply ); } return amounts; } /** * @notice Calculate the fee that is applied when the given user withdraws. * Withdraw fee decays linearly over 4 weeks. * @param user address you want to calculate withdraw fee of * @return current withdraw fee of the user */ function calculateCurrentWithdrawFee(Swap storage self, address user) public view returns (uint256) { uint256 endTime = self.depositTimestamp[user].add(4 weeks); if (endTime > block.timestamp) { uint256 timeLeftover = endTime.sub(block.timestamp); return self .defaultWithdrawFee .mul(self.withdrawFeeMultiplier[user]) .mul(timeLeftover) .div(4 weeks) .div(FEE_DENOMINATOR); } return 0; } /** * @notice A simple method to calculate prices from deposits or * withdrawals, excluding fees but including slippage. This is * helpful as an input into the various "min" parameters on calls * to fight front-running * * @dev This shouldn't be used outside frontends for user estimates. * * @param self Swap struct to read from * @param account address of the account depositing or withdrawing tokens * @param amounts an array of token amounts to deposit or withdrawal, * corresponding to pooledTokens. The amount should be in each * pooled token's native precision. If a token charges a fee on transfers, * use the amount that gets transferred after the fee. * @param deposit whether this is a deposit or a withdrawal * @return if deposit was true, total amount of lp token that will be minted and if * deposit was false, total amount of lp token that will be burned */ function calculateTokenAmount( Swap storage self, address account, uint256[] calldata amounts, bool deposit ) external view returns (uint256) { uint256 numTokens = self.pooledTokens.length; uint256 a = _getAPrecise(self); uint256 d0 = getD(_xp(self, self.balances), a); uint256[] memory balances1 = self.balances; for (uint256 i = 0; i < numTokens; i++) { if (deposit) { balances1[i] = balances1[i].add(amounts[i]); } else { balances1[i] = balances1[i].sub( amounts[i], "Cannot withdraw more than available" ); } } uint256 d1 = getD(_xp(self, balances1), a); uint256 totalSupply = self.lpToken.totalSupply(); if (deposit) { return d1.sub(d0).mul(totalSupply).div(d0); } else { return d0.sub(d1).mul(totalSupply).div(d0).mul(FEE_DENOMINATOR).div( FEE_DENOMINATOR.sub( calculateCurrentWithdrawFee(self, account) ) ); } } /** * @notice return accumulated amount of admin fees of the token with given index * @param self Swap struct to read from * @param index Index of the pooled token * @return admin balance in the token's precision */ function getAdminBalance(Swap storage self, uint256 index) external view returns (uint256) { require(index < self.pooledTokens.length, "Token index out of range"); return self.pooledTokens[index].balanceOf(address(this)).sub( self.balances[index] ); } /** * @notice internal helper function to calculate fee per token multiplier used in * swap fee calculations * @param self Swap struct to read from */ function _feePerToken(Swap storage self) internal view returns (uint256) { return self.swapFee.mul(self.pooledTokens.length).div( self.pooledTokens.length.sub(1).mul(4) ); } /*** STATE MODIFYING FUNCTIONS ***/ /** * @notice swap two tokens in the pool * @param self Swap struct to read from and write to * @param tokenIndexFrom the token the user wants to sell * @param tokenIndexTo the token the user wants to buy * @param dx the amount of tokens the user wants to sell * @param minDy the min amount the user would like to receive, or revert. * @return amount of token user received on swap */ function swap( Swap storage self, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy ) external returns (uint256) { require( dx <= self.pooledTokens[tokenIndexFrom].balanceOf(msg.sender), "Cannot swap more than you own" ); // Transfer tokens first to see if a fee was charged on transfer uint256 beforeBalance = self.pooledTokens[tokenIndexFrom].balanceOf(address(this)); self.pooledTokens[tokenIndexFrom].safeTransferFrom( msg.sender, address(this), dx ); // Use the actual transferred amount for AMM math uint256 transferredDx = self.pooledTokens[tokenIndexFrom].balanceOf(address(this)).sub( beforeBalance ); (uint256 dy, uint256 dyFee) = _calculateSwap(self, tokenIndexFrom, tokenIndexTo, transferredDx); require(dy >= minDy, "Swap didn't result in min tokens"); uint256 dyAdminFee = dyFee.mul(self.adminFee).div(FEE_DENOMINATOR).div( self.tokenPrecisionMultipliers[tokenIndexTo] ); self.balances[tokenIndexFrom] = self.balances[tokenIndexFrom].add( transferredDx ); self.balances[tokenIndexTo] = self.balances[tokenIndexTo].sub(dy).sub( dyAdminFee ); self.pooledTokens[tokenIndexTo].safeTransfer(msg.sender, dy); emit TokenSwap( msg.sender, transferredDx, dy, tokenIndexFrom, tokenIndexTo ); return dy; } /** * @notice Add liquidity to the pool * @param self Swap struct to read from and write to * @param amounts the amounts of each token to add, in their native precision * @param minToMint the minimum LP tokens adding this amount of liquidity * should mint, otherwise revert. Handy for front-running mitigation * allowed addresses. If the pool is not in the guarded launch phase, this parameter will be ignored. * @return amount of LP token user received */ function addLiquidity( Swap storage self, uint256[] memory amounts, uint256 minToMint ) external returns (uint256) { require( amounts.length == self.pooledTokens.length, "Amounts must match pooled tokens" ); uint256[] memory fees = new uint256[](self.pooledTokens.length); // current state AddLiquidityInfo memory v = AddLiquidityInfo(0, 0, 0, 0); uint256 totalSupply = self.lpToken.totalSupply(); if (totalSupply != 0) { v.d0 = getD(self); } uint256[] memory newBalances = self.balances; for (uint256 i = 0; i < self.pooledTokens.length; i++) { require( totalSupply != 0 || amounts[i] > 0, "Must supply all tokens in pool" ); // Transfer tokens first to see if a fee was charged on transfer if (amounts[i] != 0) { uint256 beforeBalance = self.pooledTokens[i].balanceOf(address(this)); self.pooledTokens[i].safeTransferFrom( msg.sender, address(this), amounts[i] ); // Update the amounts[] with actual transfer amount amounts[i] = self.pooledTokens[i].balanceOf(address(this)).sub( beforeBalance ); } newBalances[i] = self.balances[i].add(amounts[i]); } // invariant after change v.preciseA = _getAPrecise(self); v.d1 = getD(_xp(self, newBalances), v.preciseA); require(v.d1 > v.d0, "D should increase"); // updated to reflect fees and calculate the user's LP tokens v.d2 = v.d1; if (totalSupply != 0) { uint256 feePerToken = _feePerToken(self); for (uint256 i = 0; i < self.pooledTokens.length; i++) { uint256 idealBalance = v.d1.mul(self.balances[i]).div(v.d0); fees[i] = feePerToken .mul(idealBalance.difference(newBalances[i])) .div(FEE_DENOMINATOR); self.balances[i] = newBalances[i].sub( fees[i].mul(self.adminFee).div(FEE_DENOMINATOR) ); newBalances[i] = newBalances[i].sub(fees[i]); } v.d2 = getD(_xp(self, newBalances), v.preciseA); } else { // the initial depositor doesn't pay fees self.balances = newBalances; } uint256 toMint; if (totalSupply == 0) { toMint = v.d1; } else { toMint = v.d2.sub(v.d0).mul(totalSupply).div(v.d0); } require(toMint >= minToMint, "Couldn't mint min requested"); // mint the user's LP tokens self.lpToken.mint(msg.sender, toMint); emit AddLiquidity( msg.sender, amounts, fees, v.d1, totalSupply.add(toMint) ); return toMint; } /** * @notice Update the withdraw fee for `user`. If the user is currently * not providing liquidity in the pool, sets to default value. If not, recalculate * the starting withdraw fee based on the last deposit's time & amount relative * to the new deposit. * * @param self Swap struct to read from and write to * @param user address of the user depositing tokens * @param toMint amount of pool tokens to be minted */ function updateUserWithdrawFee( Swap storage self, address user, uint256 toMint ) external { _updateUserWithdrawFee(self, user, toMint); } function _updateUserWithdrawFee( Swap storage self, address user, uint256 toMint ) internal { // If token is transferred to address 0 (or burned), don't update the fee. if (user == address(0)) { return; } if (self.defaultWithdrawFee == 0) { // If current fee is set to 0%, set multiplier to FEE_DENOMINATOR self.withdrawFeeMultiplier[user] = FEE_DENOMINATOR; } else { // Otherwise, calculate appropriate discount based on last deposit amount uint256 currentFee = calculateCurrentWithdrawFee(self, user); uint256 currentBalance = self.lpToken.balanceOf(user); // ((currentBalance * currentFee) + (toMint * defaultWithdrawFee)) * FEE_DENOMINATOR / // ((toMint + currentBalance) * defaultWithdrawFee) self.withdrawFeeMultiplier[user] = currentBalance .mul(currentFee) .add(toMint.mul(self.defaultWithdrawFee)) .mul(FEE_DENOMINATOR) .div(toMint.add(currentBalance).mul(self.defaultWithdrawFee)); } self.depositTimestamp[user] = block.timestamp; } /** * @notice Burn LP tokens to remove liquidity from the pool. * @dev Liquidity can always be removed, even when the pool is paused. * @param self Swap struct to read from and write to * @param amount the amount of LP tokens to burn * @param minAmounts the minimum amounts of each token in the pool * acceptable for this burn. Useful as a front-running mitigation * @return amounts of tokens the user received */ function removeLiquidity( Swap storage self, uint256 amount, uint256[] calldata minAmounts ) external returns (uint256[] memory) { require(amount <= self.lpToken.balanceOf(msg.sender), ">LP.balanceOf"); require( minAmounts.length == self.pooledTokens.length, "minAmounts must match poolTokens" ); uint256[] memory amounts = _calculateRemoveLiquidity(self, msg.sender, amount); for (uint256 i = 0; i < amounts.length; i++) { require(amounts[i] >= minAmounts[i], "amounts[i] < minAmounts[i]"); self.balances[i] = self.balances[i].sub(amounts[i]); self.pooledTokens[i].safeTransfer(msg.sender, amounts[i]); } self.lpToken.burnFrom(msg.sender, amount); emit RemoveLiquidity(msg.sender, amounts, self.lpToken.totalSupply()); return amounts; } /** * @notice Remove liquidity from the pool all in one token. * @param self Swap struct to read from and write to * @param tokenAmount the amount of the lp tokens to burn * @param tokenIndex the index of the token you want to receive * @param minAmount the minimum amount to withdraw, otherwise revert * @return amount chosen token that user received */ function removeLiquidityOneToken( Swap storage self, uint256 tokenAmount, uint8 tokenIndex, uint256 minAmount ) external returns (uint256) { uint256 totalSupply = self.lpToken.totalSupply(); uint256 numTokens = self.pooledTokens.length; require( tokenAmount <= self.lpToken.balanceOf(msg.sender), ">LP.balanceOf" ); require(tokenIndex < numTokens, "Token not found"); uint256 dyFee; uint256 dy; (dy, dyFee) = calculateWithdrawOneToken( self, msg.sender, tokenAmount, tokenIndex ); require(dy >= minAmount, "dy < minAmount"); self.balances[tokenIndex] = self.balances[tokenIndex].sub( dy.add(dyFee.mul(self.adminFee).div(FEE_DENOMINATOR)) ); self.lpToken.burnFrom(msg.sender, tokenAmount); self.pooledTokens[tokenIndex].safeTransfer(msg.sender, dy); emit RemoveLiquidityOne( msg.sender, tokenAmount, totalSupply, tokenIndex, dy ); return dy; } /** * @notice Remove liquidity from the pool, weighted differently than the * pool's current balances. * * @param self Swap struct to read from and write to * @param amounts how much of each token to withdraw * @param maxBurnAmount the max LP token provider is willing to pay to * remove liquidity. Useful as a front-running mitigation. * @return actual amount of LP tokens burned in the withdrawal */ function removeLiquidityImbalance( Swap storage self, uint256[] memory amounts, uint256 maxBurnAmount ) public returns (uint256) { require( amounts.length == self.pooledTokens.length, "Amounts should match pool tokens" ); require( maxBurnAmount <= self.lpToken.balanceOf(msg.sender) && maxBurnAmount != 0, ">LP.balanceOf" ); RemoveLiquidityImbalanceInfo memory v = RemoveLiquidityImbalanceInfo(0, 0, 0, 0); uint256 tokenSupply = self.lpToken.totalSupply(); uint256 feePerToken = _feePerToken(self); uint256[] memory balances1 = self.balances; v.preciseA = _getAPrecise(self); v.d0 = getD(_xp(self), v.preciseA); for (uint256 i = 0; i < self.pooledTokens.length; i++) { balances1[i] = balances1[i].sub( amounts[i], "Cannot withdraw more than available" ); } v.d1 = getD(_xp(self, balances1), v.preciseA); uint256[] memory fees = new uint256[](self.pooledTokens.length); for (uint256 i = 0; i < self.pooledTokens.length; i++) { uint256 idealBalance = v.d1.mul(self.balances[i]).div(v.d0); uint256 difference = idealBalance.difference(balances1[i]); fees[i] = feePerToken.mul(difference).div(FEE_DENOMINATOR); self.balances[i] = balances1[i].sub( fees[i].mul(self.adminFee).div(FEE_DENOMINATOR) ); balances1[i] = balances1[i].sub(fees[i]); } v.d2 = getD(_xp(self, balances1), v.preciseA); uint256 tokenAmount = v.d0.sub(v.d2).mul(tokenSupply).div(v.d0); require(tokenAmount != 0, "Burnt amount cannot be zero"); tokenAmount = tokenAmount.add(1).mul(FEE_DENOMINATOR).div( FEE_DENOMINATOR.sub(calculateCurrentWithdrawFee(self, msg.sender)) ); require(tokenAmount <= maxBurnAmount, "tokenAmount > maxBurnAmount"); self.lpToken.burnFrom(msg.sender, tokenAmount); for (uint256 i = 0; i < self.pooledTokens.length; i++) { self.pooledTokens[i].safeTransfer(msg.sender, amounts[i]); } emit RemoveLiquidityImbalance( msg.sender, amounts, fees, v.d1, tokenSupply.sub(tokenAmount) ); return tokenAmount; } // /** // * @notice withdraw all admin fees to a given address // * @param self Swap struct to withdraw fees from // * @param to Address to send the fees to // */ // function withdrawAdminFees(Swap storage self, address to) external { // for (uint256 i = 0; i < self.pooledTokens.length; i++) { // IERC20 token = self.pooledTokens[i]; // uint256 balance = // token.balanceOf(address(this)).sub(self.balances[i]); // if (balance != 0) { // token.safeTransfer(to, balance); // } // } // } // /** // * @notice Sets the admin fee // * @dev adminFee cannot be higher than 100% of the swap fee // * @param self Swap struct to update // * @param newAdminFee new admin fee to be applied on future transactions // */ // function setAdminFee(Swap storage self, uint256 newAdminFee) external { // require(newAdminFee <= MAX_ADMIN_FEE, "Fee is too high"); // self.adminFee = newAdminFee; // emit NewAdminFee(newAdminFee); // } // /** // * @notice update the swap fee // * @dev fee cannot be higher than 1% of each swap // * @param self Swap struct to update // * @param newSwapFee new swap fee to be applied on future transactions // */ // function setSwapFee(Swap storage self, uint256 newSwapFee) external { // require(newSwapFee <= MAX_SWAP_FEE, "Fee is too high"); // self.swapFee = newSwapFee; // emit NewSwapFee(newSwapFee); // } // /** // * @notice update the default withdraw fee. This also affects deposits made in the past as well. // * @param self Swap struct to update // * @param newWithdrawFee new withdraw fee to be applied // */ // function setDefaultWithdrawFee(Swap storage self, uint256 newWithdrawFee) // external // { // require(newWithdrawFee <= MAX_WITHDRAW_FEE, "Fee is too high"); // self.defaultWithdrawFee = newWithdrawFee; // emit NewWithdrawFee(newWithdrawFee); // } // /** // * @notice Start ramping up or down A parameter towards given futureA_ and futureTime_ // * Checks if the change is too rapid, and commits the new A value only when it falls under // * the limit range. // * @param self Swap struct to update // * @param futureA_ the new A to ramp towards // * @param futureTime_ timestamp when the new A should be reached // */ // function rampA( // Swap storage self, // uint256 futureA_, // uint256 futureTime_ // ) external { // require( // block.timestamp >= self.initialATime.add(1 days), // "Wait 1 day before starting ramp" // ); // require( // futureTime_ >= block.timestamp.add(MIN_RAMP_TIME), // "Insufficient ramp time" // ); // require( // futureA_ > 0 && futureA_ < MAX_A, // "futureA_ must be > 0 and < MAX_A" // ); // uint256 initialAPrecise = _getAPrecise(self); // uint256 futureAPrecise = futureA_.mul(A_PRECISION); // if (futureAPrecise < initialAPrecise) { // require( // futureAPrecise.mul(MAX_A_CHANGE) >= initialAPrecise, // "futureA_ is too small" // ); // } else { // require( // futureAPrecise <= initialAPrecise.mul(MAX_A_CHANGE), // "futureA_ is too large" // ); // } // self.initialA = initialAPrecise; // self.futureA = futureAPrecise; // self.initialATime = block.timestamp; // self.futureATime = futureTime_; // emit RampA( // initialAPrecise, // futureAPrecise, // block.timestamp, // futureTime_ // ); // } // /** // * @notice Stops ramping A immediately. Once this function is called, rampA() // * cannot be called for another 24 hours // * @param self Swap struct to update // */ // function stopRampA(Swap storage self) external { // require(self.futureATime > block.timestamp, "Ramp is already stopped"); // uint256 currentA = _getAPrecise(self); // self.initialA = currentA; // self.futureA = currentA; // self.initialATime = block.timestamp; // self.futureATime = block.timestamp; // emit StopRampA(currentA, block.timestamp); // } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; // import "@openzeppelin/contracts/math/SafeMath.sol"; /** * @title MathUtils library * @notice A library to be used in conjunction with SafeMath. Contains functions for calculating * differences between two uint256. */ library MathUtils { /** * @notice Compares a and b and returns true if the difference between a and b * is less than 1 or equal to each other. * @param a uint256 to compare with * @param b uint256 to compare with * @return True if the difference between a and b is less than 1 or equal, * otherwise return false */ function within1(uint256 a, uint256 b) external pure returns (bool) { return (_difference(a, b) <= 1); } /** * @notice Calculates absolute difference between a and b * @param a uint256 to compare with * @param b uint256 to compare with * @return Difference between a and b */ function difference(uint256 a, uint256 b) external pure returns (uint256) { return _difference(a, b); } /** * @notice Calculates absolute difference between a and b * @param a uint256 to compare with * @param b uint256 to compare with * @return Difference between a and b */ function _difference(uint256 a, uint256 b) internal pure returns (uint256) { if (a > b) { return a - b; } return b - a; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; // import "./OwnerPausableUpgradeable.sol"; import "./SwapUtils.sol"; import "./MathUtils.sol"; /** * @title Swap - A StableSwap implementation in solidity. * @notice This contract is responsible for custody of closely pegged assets (eg. group of stablecoins) * and automatic market making system. Users become an LP (Liquidity Provider) by depositing their tokens * in desired ratios for an exchange of the pool token that represents their share of the pool. * Users can burn pool tokens and withdraw their share of token(s). * * Each time a swap between the pooled tokens happens, a set fee incurs which effectively gets * distributed to the LPs. * * In case of emergencies, admin can pause additional deposits, swaps, or single-asset withdraws - which * stops the ratio of the tokens in the pool from changing. * Users can always withdraw their tokens via multi-asset withdraws. * * @dev Most of the logic is stored as a library `SwapUtils` for the sake of reducing contract's * deployment size. */ contract Swap is ReentrancyGuardUpgradeable { using SafeERC20 for IERC20; using SafeMath for uint256; using MathUtils for uint256; using SwapUtils for SwapUtils.Swap; // Struct storing data responsible for automatic market maker functionalities. In order to // access this data, this contract uses SwapUtils library. For more details, see SwapUtils.sol SwapUtils.Swap public swapStorage; // True if the contract is initialized. bool private initialized = false; // Maps token address to an index in the pool. Used to prevent duplicate tokens in the pool. // getTokenIndex function also relies on this mapping to retrieve token index. mapping(address => uint8) private tokenIndexes; /*** EVENTS ***/ // events replicated from SwapUtils to make the ABI easier for dumb // clients event TokenSwap( address indexed buyer, uint256 tokensSold, uint256 tokensBought, uint128 soldId, uint128 boughtId ); event AddLiquidity( address indexed provider, uint256[] tokenAmounts, uint256[] fees, uint256 invariant, uint256 lpTokenSupply ); event RemoveLiquidity( address indexed provider, uint256[] tokenAmounts, uint256 lpTokenSupply ); event RemoveLiquidityOne( address indexed provider, uint256 lpTokenAmount, uint256 lpTokenSupply, uint256 boughtId, uint256 tokensBought ); event RemoveLiquidityImbalance( address indexed provider, uint256[] tokenAmounts, uint256[] fees, uint256 invariant, uint256 lpTokenSupply ); event NewAdminFee(uint256 newAdminFee); event NewSwapFee(uint256 newSwapFee); event NewWithdrawFee(uint256 newWithdrawFee); event RampA( uint256 oldA, uint256 newA, uint256 initialTime, uint256 futureTime ); event StopRampA(uint256 currentA, uint256 time); /** * @notice Initializes this Swap contract with the given parameters. * This will also deploy the LPToken that represents users * LP position. The owner of LPToken will be this contract - which means * only this contract is allowed to mint new tokens. * * @param _pooledTokens an array of ERC20s this pool will accept * @param decimals the decimals to use for each pooled token, * eg 8 for WBTC. Cannot be larger than POOL_PRECISION_DECIMALS * @param lpTokenName the long-form name of the token to be deployed * @param lpTokenSymbol the short symbol for the token to be deployed * @param _a the amplification coefficient * n * (n - 1). See the * StableSwap paper for details * @param _fee default swap fee to be initialized with * @param _adminFee default adminFee to be initialized with * @param _withdrawFee default withdrawFee to be initialized with */ function initialize( IERC20[] memory _pooledTokens, uint8[] memory decimals, string memory lpTokenName, string memory lpTokenSymbol, uint256 _a, uint256 _fee, uint256 _adminFee, uint256 _withdrawFee ) public virtual initializer { // __OwnerPausable_init(); __ReentrancyGuard_init(); // Check _pooledTokens and precisions parameter require(_pooledTokens.length > 1, "_pooledTokens.length <= 1"); require(_pooledTokens.length <= 32, "_pooledTokens.length > 32"); require( _pooledTokens.length == decimals.length, "_pooledTokens decimals mismatch" ); uint256[] memory precisionMultipliers = new uint256[](decimals.length); for (uint8 i = 0; i < _pooledTokens.length; i++) { if (i > 0) { // Check if index is already used. Check if 0th element is a duplicate. require( tokenIndexes[address(_pooledTokens[i])] == 0 && _pooledTokens[0] != _pooledTokens[i], "Duplicate tokens" ); } require( address(_pooledTokens[i]) != address(0), "The 0 address isn't an ERC-20" ); require( decimals[i] <= SwapUtils.POOL_PRECISION_DECIMALS, "Token decimals exceeds max" ); precisionMultipliers[i] = 10 ** uint256(SwapUtils.POOL_PRECISION_DECIMALS).sub( uint256(decimals[i]) ); tokenIndexes[address(_pooledTokens[i])] = i; } // Check _a, _fee, _adminFee, _withdrawFee parameters require(_a < SwapUtils.MAX_A, "_a exceeds maximum"); require(_fee < SwapUtils.MAX_SWAP_FEE, "_fee exceeds maximum"); require( _adminFee < SwapUtils.MAX_ADMIN_FEE, "_adminFee exceeds maximum" ); require( _withdrawFee < SwapUtils.MAX_WITHDRAW_FEE, "_withdrawFee exceeds maximum" ); // Initialize swapStorage struct swapStorage.lpToken = new LPToken( lpTokenName, lpTokenSymbol, SwapUtils.POOL_PRECISION_DECIMALS ); swapStorage.pooledTokens = _pooledTokens; swapStorage.tokenPrecisionMultipliers = precisionMultipliers; swapStorage.balances = new uint256[](_pooledTokens.length); swapStorage.initialA = _a.mul(SwapUtils.A_PRECISION); swapStorage.futureA = _a.mul(SwapUtils.A_PRECISION); swapStorage.initialATime = 0; swapStorage.futureATime = 0; swapStorage.swapFee = _fee; swapStorage.adminFee = _adminFee; swapStorage.defaultWithdrawFee = _withdrawFee; } /*** MODIFIERS ***/ /** * @notice Modifier to check deadline against current timestamp * @param deadline latest timestamp to accept this transaction */ modifier deadlineCheck(uint256 deadline) { require(block.timestamp <= deadline, "Deadline not met"); _; } /*** VIEW FUNCTIONS ***/ /** * @notice Return A, the amplification coefficient * n * (n - 1) * @dev See the StableSwap paper for details * @return A parameter */ function getA() external view returns (uint256) { return swapStorage.getA(); } /** * @notice Return A in its raw precision form * @dev See the StableSwap paper for details * @return A parameter in its raw precision form */ function getAPrecise() external view returns (uint256) { return swapStorage.getAPrecise(); } /** * @notice Return address of the pooled token at given index. Reverts if tokenIndex is out of range. * @param index the index of the token * @return address of the token at given index */ function getToken(uint8 index) public view returns (IERC20) { require(index < swapStorage.pooledTokens.length, "Out of range"); return swapStorage.pooledTokens[index]; } /** * @notice Return the index of the given token address. Reverts if no matching * token is found. * @param tokenAddress address of the token * @return the index of the given token address */ function getTokenIndex(address tokenAddress) public view returns (uint8) { uint8 index = tokenIndexes[tokenAddress]; require( address(getToken(index)) == tokenAddress, "Token does not exist" ); return index; } /** * @notice Return timestamp of last deposit of given address * @return timestamp of the last deposit made by the given address */ function getDepositTimestamp(address user) external view returns (uint256) { return swapStorage.getDepositTimestamp(user); } /** * @notice Return current balance of the pooled token at given index * @param index the index of the token * @return current balance of the pooled token at given index with token's native precision */ function getTokenBalance(uint8 index) external view returns (uint256) { require(index < swapStorage.pooledTokens.length, "Index out of range"); return swapStorage.balances[index]; } /** * @notice Get the virtual price, to help calculate profit * @return the virtual price, scaled to the POOL_PRECISION_DECIMALS */ function getVirtualPrice() external view returns (uint256) { return swapStorage.getVirtualPrice(); } /** * @notice Calculate amount of tokens you receive on swap * @param tokenIndexFrom the token the user wants to sell * @param tokenIndexTo the token the user wants to buy * @param dx the amount of tokens the user wants to sell. If the token charges * a fee on transfers, use the amount that gets transferred after the fee. * @return amount of tokens the user will receive */ function calculateSwap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) external view returns (uint256) { return swapStorage.calculateSwap(tokenIndexFrom, tokenIndexTo, dx); } /** * @notice A simple method to calculate prices from deposits or * withdrawals, excluding fees but including slippage. This is * helpful as an input into the various "min" parameters on calls * to fight front-running * * @dev This shouldn't be used outside frontends for user estimates. * * @param account address that is depositing or withdrawing tokens * @param amounts an array of token amounts to deposit or withdrawal, * corresponding to pooledTokens. The amount should be in each * pooled token's native precision. If a token charges a fee on transfers, * use the amount that gets transferred after the fee. * @param deposit whether this is a deposit or a withdrawal * @return token amount the user will receive */ function calculateTokenAmount( address account, uint256[] calldata amounts, bool deposit ) external view returns (uint256) { return swapStorage.calculateTokenAmount(account, amounts, deposit); } /** * @notice A simple method to calculate amount of each underlying * tokens that is returned upon burning given amount of LP tokens * @param account the address that is withdrawing tokens * @param amount the amount of LP tokens that would be burned on withdrawal * @return array of token balances that the user will receive */ function calculateRemoveLiquidity(address account, uint256 amount) external view returns (uint256[] memory) { return swapStorage.calculateRemoveLiquidity(account, amount); } /** * @notice Calculate the amount of underlying token available to withdraw * when withdrawing via only single token * @param account the address that is withdrawing tokens * @param tokenAmount the amount of LP token to burn * @param tokenIndex index of which token will be withdrawn * @return availableTokenAmount calculated amount of underlying token * available to withdraw */ function calculateRemoveLiquidityOneToken( address account, uint256 tokenAmount, uint8 tokenIndex ) external view returns (uint256 availableTokenAmount) { (availableTokenAmount, ) = swapStorage.calculateWithdrawOneToken( account, tokenAmount, tokenIndex ); } /** * @notice Calculate the fee that is applied when the given user withdraws. The withdraw fee * decays linearly over period of 4 weeks. For example, depositing and withdrawing right away * will charge you the full amount of withdraw fee. But withdrawing after 4 weeks will charge you * no additional fees. * @dev returned value should be divided by FEE_DENOMINATOR to convert to correct decimals * @param user address you want to calculate withdraw fee of * @return current withdraw fee of the user */ function calculateCurrentWithdrawFee(address user) external view returns (uint256) { return swapStorage.calculateCurrentWithdrawFee(user); } /** * @notice This function reads the accumulated amount of admin fees of the token with given index * @param index Index of the pooled token * @return admin's token balance in the token's precision */ function getAdminBalance(uint256 index) external view returns (uint256) { return swapStorage.getAdminBalance(index); } /*** STATE MODIFYING FUNCTIONS ***/ /** * @notice Swap two tokens using this pool * @param tokenIndexFrom the token the user wants to swap from * @param tokenIndexTo the token the user wants to swap to * @param dx the amount of tokens the user wants to swap from * @param minDy the min amount the user would like to receive, or revert. * @param deadline latest timestamp to accept this transaction */ function swap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy, uint256 deadline ) external nonReentrant // whenNotPaused deadlineCheck(deadline) returns (uint256) { return swapStorage.swap(tokenIndexFrom, tokenIndexTo, dx, minDy); } /** * @notice Add liquidity to the pool with the given amounts of tokens * @param amounts the amounts of each token to add, in their native precision * @param minToMint the minimum LP tokens adding this amount of liquidity * should mint, otherwise revert. Handy for front-running mitigation * @param deadline latest timestamp to accept this transaction * @return amount of LP token user minted and received */ function addLiquidity( uint256[] calldata amounts, uint256 minToMint, uint256 deadline ) external nonReentrant // whenNotPaused deadlineCheck(deadline) returns (uint256) { return swapStorage.addLiquidity(amounts, minToMint); } /** * @notice Burn LP tokens to remove liquidity from the pool. Withdraw fee that decays linearly * over period of 4 weeks since last deposit will apply. * @dev Liquidity can always be removed, even when the pool is paused. * @param amount the amount of LP tokens to burn * @param minAmounts the minimum amounts of each token in the pool * acceptable for this burn. Useful as a front-running mitigation * @param deadline latest timestamp to accept this transaction * @return amounts of tokens user received */ function removeLiquidity( uint256 amount, uint256[] calldata minAmounts, uint256 deadline ) external nonReentrant deadlineCheck(deadline) returns (uint256[] memory) { return swapStorage.removeLiquidity(amount, minAmounts); } /** * @notice Remove liquidity from the pool all in one token. Withdraw fee that decays linearly * over period of 4 weeks since last deposit will apply. * @param tokenAmount the amount of the token you want to receive * @param tokenIndex the index of the token you want to receive * @param minAmount the minimum amount to withdraw, otherwise revert * @param deadline latest timestamp to accept this transaction * @return amount of chosen token user received */ function removeLiquidityOneToken( uint256 tokenAmount, uint8 tokenIndex, uint256 minAmount, uint256 deadline ) external nonReentrant // whenNotPaused deadlineCheck(deadline) returns (uint256) { return swapStorage.removeLiquidityOneToken( tokenAmount, tokenIndex, minAmount ); } /** * @notice Remove liquidity from the pool, weighted differently than the * pool's current balances. Withdraw fee that decays linearly * over period of 4 weeks since last deposit will apply. * @param amounts how much of each token to withdraw * @param maxBurnAmount the max LP token provider is willing to pay to * remove liquidity. Useful as a front-running mitigation. * @param deadline latest timestamp to accept this transaction * @return amount of LP tokens burned */ function removeLiquidityImbalance( uint256[] calldata amounts, uint256 maxBurnAmount, uint256 deadline ) external nonReentrant // whenNotPaused deadlineCheck(deadline) returns (uint256) { return swapStorage.removeLiquidityImbalance(amounts, maxBurnAmount); } /*** ADMIN FUNCTIONS ***/ /** * @notice Updates the user withdraw fee. This function can only be called by * the pool token. Should be used to update the withdraw fee on transfer of pool tokens. * Transferring your pool token will reset the 4 weeks period. If the recipient is already * holding some pool tokens, the withdraw fee will be discounted in respective amounts. * @param recipient address of the recipient of pool token * @param transferAmount amount of pool token to transfer */ function updateUserWithdrawFee(address recipient, uint256 transferAmount) external { require( msg.sender == address(swapStorage.lpToken), "Only callable by pool token" ); swapStorage.updateUserWithdrawFee(recipient, transferAmount); } // /** // * @notice Withdraw all admin fees to the contract owner // */ // function withdrawAdminFees() external onlyOwner { // swapStorage.withdrawAdminFees(owner()); // } // /** // * @notice Update the admin fee. Admin fee takes portion of the swap fee. // * @param newAdminFee new admin fee to be applied on future transactions // */ // function setAdminFee(uint256 newAdminFee) external onlyOwner { // swapStorage.setAdminFee(newAdminFee); // } // /** // * @notice Update the swap fee to be applied on swaps // * @param newSwapFee new swap fee to be applied on future transactions // */ // function setSwapFee(uint256 newSwapFee) external onlyOwner { // swapStorage.setSwapFee(newSwapFee); // } // /** // * @notice Update the withdraw fee. This fee decays linearly over 4 weeks since // * user's last deposit. // * @param newWithdrawFee new withdraw fee to be applied on future deposits // */ // function setDefaultWithdrawFee(uint256 newWithdrawFee) external onlyOwner { // swapStorage.setDefaultWithdrawFee(newWithdrawFee); // } // /** // * @notice Start ramping up or down A parameter towards given futureA and futureTime // * Checks if the change is too rapid, and commits the new A value only when it falls under // * the limit range. // * @param futureA the new A to ramp towards // * @param futureTime timestamp when the new A should be reached // */ // function rampA(uint256 futureA, uint256 futureTime) external onlyOwner { // swapStorage.rampA(futureA, futureTime); // } // /** // * @notice Stop ramping A immediately. Reverts if ramp A is already stopped. // */ // function stopRampA() external onlyOwner { // swapStorage.stopRampA(); // } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "../proxy/Initializable.sol"; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuardUpgradeable is Initializable { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; function __ReentrancyGuard_init() internal initializer { __ReentrancyGuard_init_unchained(); } function __ReentrancyGuard_init_unchained() internal initializer { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and make it call a * `private` function that does the actual work. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } uint256[49] private __gap; }
// SPDX-License-Identifier: MIT // solhint-disable-next-line compiler-version pragma solidity >=0.4.24 <0.8.0; import "../utils/AddressUpgradeable.sol"; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {UpgradeableProxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. */ abstract contract Initializable { /** * @dev Indicates that the contract has been initialized. */ bool private _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private _initializing; /** * @dev Modifier to protect an initializer function from being invoked twice. */ modifier initializer() { require(_initializing || _isConstructor() || !_initialized, "Initializable: contract is already initialized"); bool isTopLevelCall = !_initializing; if (isTopLevelCall) { _initializing = true; _initialized = true; } _; if (isTopLevelCall) { _initializing = false; } } /// @dev Returns true if and only if the function is running in the constructor function _isConstructor() private view returns (bool) { return !AddressUpgradeable.isContract(address(this)); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.2 <0.8.0; /** * @dev Collection of functions related to the address type */ library AddressUpgradeable { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{ value: amount }(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain`call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{ value: value }(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.staticcall(data); return _verifyCallResult(success, returndata, errorMessage); } function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; /** * @title OwnerPausable * @notice An ownable contract allows the owner to pause and unpause the * contract without a delay. * @dev Only methods using the provided modifiers will be paused. */ abstract contract OwnerPausableUpgradeable is OwnableUpgradeable, PausableUpgradeable { function __OwnerPausable_init() internal initializer { __Context_init_unchained(); __Ownable_init_unchained(); __Pausable_init_unchained(); } /** * @notice Pause the contract. Revert if already paused. */ function pause() external onlyOwner { PausableUpgradeable._pause(); } /** * @notice Unpause the contract. Revert if already unpaused. */ function unpause() external onlyOwner { PausableUpgradeable._unpause(); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "../utils/ContextUpgradeable.sol"; import "../proxy/Initializable.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ function __Ownable_init() internal initializer { __Context_init_unchained(); __Ownable_init_unchained(); } function __Ownable_init_unchained() internal initializer { address msgSender = _msgSender(); _owner = msgSender; emit OwnershipTransferred(address(0), msgSender); } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(owner() == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } uint256[49] private __gap; }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "./ContextUpgradeable.sol"; import "../proxy/Initializable.sol"; /** * @dev Contract module which allows children to implement an emergency stop * mechanism that can be triggered by an authorized account. * * This module is used through inheritance. It will make available the * modifiers `whenNotPaused` and `whenPaused`, which can be applied to * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ abstract contract PausableUpgradeable is Initializable, ContextUpgradeable { /** * @dev Emitted when the pause is triggered by `account`. */ event Paused(address account); /** * @dev Emitted when the pause is lifted by `account`. */ event Unpaused(address account); bool private _paused; /** * @dev Initializes the contract in unpaused state. */ function __Pausable_init() internal initializer { __Context_init_unchained(); __Pausable_init_unchained(); } function __Pausable_init_unchained() internal initializer { _paused = false; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view virtual returns (bool) { return _paused; } /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ modifier whenNotPaused() { require(!paused(), "Pausable: paused"); _; } /** * @dev Modifier to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ modifier whenPaused() { require(paused(), "Pausable: not paused"); _; } /** * @dev Triggers stopped state. * * Requirements: * * - The contract must not be paused. */ function _pause() internal virtual whenNotPaused { _paused = true; emit Paused(_msgSender()); } /** * @dev Returns to normal state. * * Requirements: * * - The contract must be paused. */ function _unpause() internal virtual whenPaused { _paused = false; emit Unpaused(_msgSender()); } uint256[49] private __gap; }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "../proxy/Initializable.sol"; /* * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with GSN meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract ContextUpgradeable is Initializable { function __Context_init() internal initializer { __Context_init_unchained(); } function __Context_init_unchained() internal initializer { } function _msgSender() internal view virtual returns (address payable) { return msg.sender; } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } uint256[50] private __gap; }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "../interfaces/polygon/messengers/IPolygonFxChild.sol"; import "./BytesLib.sol"; abstract contract MockMessenger { using SafeERC20 for IERC20; using BytesLib for bytes; struct Message { address target; bytes message; address sender; } Message public nextMessage; IERC20 public canonicalToken; /** * Chain specific params */ // Optimism address public xDomainMessageSender; // XDai address public messageSender; bytes32 public messageSourceChainId = 0x000000000000000000000000000000000000000000000000000000000000002a; constructor(IERC20 _canonicalToken) public { canonicalToken = _canonicalToken; } function relayNextMessage() public { messageSender = nextMessage.sender; xDomainMessageSender = nextMessage.sender; // Use sender address to signify where the message is coming from bool isFromPolygonL1 = nextMessage.sender == address(1); if (isFromPolygonL1) { uint256 stateId = 0; IPolygonFxChild(nextMessage.target).onStateReceive(stateId, nextMessage.message); } else { (bool success, bytes memory res) = nextMessage.target.call(nextMessage.message); require(success, _getRevertMsgFromRes(res)); } } function receiveMessage( address _target, bytes memory _message, address _sender ) public { nextMessage = Message( _target, _message, _sender ); } function _getRevertMsgFromRes(bytes memory _res) internal pure returns (string memory) { // If the _res length is less than 68, then the transaction failed silently (without a revert message) if (_res.length < 68) return 'BA: Transaction reverted silently'; bytes memory revertData = _res.slice(4, _res.length - 4); // Remove the selector which is the first 4 bytes return abi.decode(revertData, (string)); // All that remains is the revert string } }
// SPDX-License-Identifier: MIT pragma solidity ^0.6.0; interface IPolygonFxChild { function onStateReceive(uint256 stateId, bytes calldata _data) external; }
// SPDX-License-Identifier: Unlicense /* * @title Solidity Bytes Arrays Utils * @author Gonçalo Sá <[email protected]> * * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity. * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage. */ pragma solidity >=0.5.0 <0.7.0; library BytesLib { function concat( bytes memory _preBytes, bytes memory _postBytes ) internal pure returns (bytes memory) { bytes memory tempBytes; assembly { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // Store the length of the first bytes array at the beginning of // the memory for tempBytes. let length := mload(_preBytes) mstore(tempBytes, length) // Maintain a memory counter for the current write location in the // temp bytes array by adding the 32 bytes for the array length to // the starting location. let mc := add(tempBytes, 0x20) // Stop copying when the memory counter reaches the length of the // first bytes array. let end := add(mc, length) for { // Initialize a copy counter to the start of the _preBytes data, // 32 bytes into its memory. let cc := add(_preBytes, 0x20) } lt(mc, end) { // Increase both counters by 32 bytes each iteration. mc := add(mc, 0x20) cc := add(cc, 0x20) } { // Write the _preBytes data into the tempBytes memory 32 bytes // at a time. mstore(mc, mload(cc)) } // Add the length of _postBytes to the current length of tempBytes // and store it as the new length in the first 32 bytes of the // tempBytes memory. length := mload(_postBytes) mstore(tempBytes, add(length, mload(tempBytes))) // Move the memory counter back from a multiple of 0x20 to the // actual end of the _preBytes data. mc := end // Stop copying when the memory counter reaches the new combined // length of the arrays. end := add(mc, length) for { let cc := add(_postBytes, 0x20) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } // Update the free-memory pointer by padding our last write location // to 32 bytes: add 31 bytes to the end of tempBytes to move to the // next 32 byte block, then round down to the nearest multiple of // 32. If the sum of the length of the two arrays is zero then add // one before rounding down to leave a blank 32 bytes (the length block with 0). mstore(0x40, and( add(add(end, iszero(add(length, mload(_preBytes)))), 31), not(31) // Round down to the nearest 32 bytes. )) } return tempBytes; } function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal { assembly { // Read the first 32 bytes of _preBytes storage, which is the length // of the array. (We don't need to use the offset into the slot // because arrays use the entire slot.) let fslot := sload(_preBytes_slot) // Arrays of 31 bytes or less have an even value in their slot, // while longer arrays have an odd value. The actual length is // the slot divided by two for odd values, and the lowest order // byte divided by two for even values. // If the slot is even, bitwise and the slot with 255 and divide by // two to get the length. If the slot is odd, bitwise and the slot // with -1 and divide by two. let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) let mlength := mload(_postBytes) let newlength := add(slength, mlength) // slength can contain both the length and contents of the array // if length < 32 bytes so let's prepare for that // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage switch add(lt(slength, 32), lt(newlength, 32)) case 2 { // Since the new array still fits in the slot, we just need to // update the contents of the slot. // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length sstore( _preBytes_slot, // all the modifications to the slot are inside this // next block add( // we can just add to the slot contents because the // bytes we want to change are the LSBs fslot, add( mul( div( // load the bytes from memory mload(add(_postBytes, 0x20)), // zero all bytes to the right exp(0x100, sub(32, mlength)) ), // and now shift left the number of bytes to // leave space for the length in the slot exp(0x100, sub(32, newlength)) ), // increase length by the double of the memory // bytes length mul(mlength, 2) ) ) ) } case 1 { // The stored value fits in the slot, but the combined value // will exceed it. // get the keccak hash to get the contents of the array mstore(0x0, _preBytes_slot) let sc := add(keccak256(0x0, 0x20), div(slength, 32)) // save new length sstore(_preBytes_slot, add(mul(newlength, 2), 1)) // The contents of the _postBytes array start 32 bytes into // the structure. Our first read should obtain the `submod` // bytes that can fit into the unused space in the last word // of the stored array. To get this, we read 32 bytes starting // from `submod`, so the data we read overlaps with the array // contents by `submod` bytes. Masking the lowest-order // `submod` bytes allows us to add that value directly to the // stored value. let submod := sub(32, slength) let mc := add(_postBytes, submod) let end := add(_postBytes, mlength) let mask := sub(exp(0x100, submod), 1) sstore( sc, add( and( fslot, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00 ), and(mload(mc), mask) ) ) for { mc := add(mc, 0x20) sc := add(sc, 1) } lt(mc, end) { sc := add(sc, 1) mc := add(mc, 0x20) } { sstore(sc, mload(mc)) } mask := exp(0x100, sub(mc, end)) sstore(sc, mul(div(mload(mc), mask), mask)) } default { // get the keccak hash to get the contents of the array mstore(0x0, _preBytes_slot) // Start copying to the last used word of the stored array. let sc := add(keccak256(0x0, 0x20), div(slength, 32)) // save new length sstore(_preBytes_slot, add(mul(newlength, 2), 1)) // Copy over the first `submod` bytes of the new data as in // case 1 above. let slengthmod := mod(slength, 32) let mlengthmod := mod(mlength, 32) let submod := sub(32, slengthmod) let mc := add(_postBytes, submod) let end := add(_postBytes, mlength) let mask := sub(exp(0x100, submod), 1) sstore(sc, add(sload(sc), and(mload(mc), mask))) for { sc := add(sc, 1) mc := add(mc, 0x20) } lt(mc, end) { sc := add(sc, 1) mc := add(mc, 0x20) } { sstore(sc, mload(mc)) } mask := exp(0x100, sub(mc, end)) sstore(sc, mul(div(mload(mc), mask), mask)) } } } function slice( bytes memory _bytes, uint256 _start, uint256 _length ) internal pure returns (bytes memory) { require(_length + 31 >= _length, "slice_overflow"); require(_start + _length >= _start, "slice_overflow"); require(_bytes.length >= _start + _length, "slice_outOfBounds"); bytes memory tempBytes; assembly { switch iszero(_length) case 0 { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // The first word of the slice result is potentially a partial // word read from the original array. To read it, we calculate // the length of that partial word and start copying that many // bytes into the array. The first word we copy will start with // data we don't care about, but the last `lengthmod` bytes will // land at the beginning of the contents of the new array. When // we're done copying, we overwrite the full first word with // the actual length of the slice. let lengthmod := and(_length, 31) // The multiplication in the next line is necessary // because when slicing multiples of 32 bytes (lengthmod == 0) // the following copy loop was copying the origin's length // and then ending prematurely not copying everything it should. let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) let end := add(mc, _length) for { // The multiplication in the next line has the same exact purpose // as the one above. let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } mstore(tempBytes, _length) //update free-memory pointer //allocating the array padded to 32 bytes like the compiler does now mstore(0x40, and(add(mc, 31), not(31))) } //if we want a zero-length slice let's just return a zero-length array default { tempBytes := mload(0x40) //zero out the 32 bytes slice we are about to return //we need to do it because Solidity does not garbage collect mstore(tempBytes, 0) mstore(0x40, add(tempBytes, 0x20)) } } return tempBytes; } function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) { require(_start + 20 >= _start, "toAddress_overflow"); require(_bytes.length >= _start + 20, "toAddress_outOfBounds"); address tempAddress; assembly { tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) } return tempAddress; } function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) { require(_start + 1 >= _start, "toUint8_overflow"); require(_bytes.length >= _start + 1 , "toUint8_outOfBounds"); uint8 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x1), _start)) } return tempUint; } function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) { require(_start + 2 >= _start, "toUint16_overflow"); require(_bytes.length >= _start + 2, "toUint16_outOfBounds"); uint16 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x2), _start)) } return tempUint; } function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) { require(_start + 4 >= _start, "toUint32_overflow"); require(_bytes.length >= _start + 4, "toUint32_outOfBounds"); uint32 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x4), _start)) } return tempUint; } function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) { require(_start + 8 >= _start, "toUint64_overflow"); require(_bytes.length >= _start + 8, "toUint64_outOfBounds"); uint64 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x8), _start)) } return tempUint; } function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) { require(_start + 12 >= _start, "toUint96_overflow"); require(_bytes.length >= _start + 12, "toUint96_outOfBounds"); uint96 tempUint; assembly { tempUint := mload(add(add(_bytes, 0xc), _start)) } return tempUint; } function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) { require(_start + 16 >= _start, "toUint128_overflow"); require(_bytes.length >= _start + 16, "toUint128_outOfBounds"); uint128 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x10), _start)) } return tempUint; } function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) { require(_start + 32 >= _start, "toUint256_overflow"); require(_bytes.length >= _start + 32, "toUint256_outOfBounds"); uint256 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x20), _start)) } return tempUint; } function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) { require(_start + 32 >= _start, "toBytes32_overflow"); require(_bytes.length >= _start + 32, "toBytes32_outOfBounds"); bytes32 tempBytes32; assembly { tempBytes32 := mload(add(add(_bytes, 0x20), _start)) } return tempBytes32; } function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) { bool success = true; assembly { let length := mload(_preBytes) // if lengths don't match the arrays are not equal switch eq(length, mload(_postBytes)) case 1 { // cb is a circuit breaker in the for loop since there's // no said feature for inline assembly loops // cb = 1 - don't breaker // cb = 0 - break let cb := 1 let mc := add(_preBytes, 0x20) let end := add(mc, length) for { let cc := add(_postBytes, 0x20) // the next line is the loop condition: // while(uint256(mc < end) + cb == 2) } eq(add(lt(mc, end), cb), 2) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { // if any of these checks fails then arrays are not equal if iszero(eq(mload(mc), mload(cc))) { // unsuccess: success := 0 cb := 0 } } } default { // unsuccess: success := 0 } } return success; } function equalStorage( bytes storage _preBytes, bytes memory _postBytes ) internal view returns (bool) { bool success = true; assembly { // we know _preBytes_offset is 0 let fslot := sload(_preBytes_slot) // Decode the length of the stored array like in concatStorage(). let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) let mlength := mload(_postBytes) // if lengths don't match the arrays are not equal switch eq(slength, mlength) case 1 { // slength can contain both the length and contents of the array // if length < 32 bytes so let's prepare for that // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage if iszero(iszero(slength)) { switch lt(slength, 32) case 1 { // blank the last byte which is the length fslot := mul(div(fslot, 0x100), 0x100) if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) { // unsuccess: success := 0 } } default { // cb is a circuit breaker in the for loop since there's // no said feature for inline assembly loops // cb = 1 - don't breaker // cb = 0 - break let cb := 1 // get the keccak hash to get the contents of the array mstore(0x0, _preBytes_slot) let sc := keccak256(0x0, 0x20) let mc := add(_postBytes, 0x20) let end := add(mc, mlength) // the next line is the loop condition: // while(uint256(mc < end) + cb == 2) for {} eq(add(lt(mc, end), cb), 2) { sc := add(sc, 1) mc := add(mc, 0x20) } { if iszero(eq(sload(sc), mload(mc))) { // unsuccess: success := 0 cb := 0 } } } } } default { // unsuccess: success := 0 } } return success; } }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "./MockMessenger.sol"; import "./Mock_L1_Messenger.sol"; contract Mock_L2_Messenger is MockMessenger { Mock_L1_Messenger public targetMessenger; // This should be the PolygonMessengerWrapper address public polygonTarget; constructor (IERC20 _canonicalToken) public MockMessenger(_canonicalToken) {} function setTargetMessenger(address _targetMessenger) public { targetMessenger = Mock_L1_Messenger(_targetMessenger); } function setPolygonTarget(address _polygonTarget) public { polygonTarget = _polygonTarget; } /* ========== Arbitrum ========== */ function sendTxToL1( address _destAddr, bytes calldata _calldataForL1 ) external payable { targetMessenger.receiveMessage( _destAddr, _calldataForL1, msg.sender ); } /* ========== Optimism ========== */ function sendMessage( address _target, bytes calldata _message, uint32 /* _gasLimit */ ) public { targetMessenger.receiveMessage( _target, _message, msg.sender ); } /* ========== xDai ========== */ function requireToPassMessage( address _target, bytes calldata _message, uint256 /* _gasLimit */ ) public returns (bytes32) { targetMessenger.receiveMessage( _target, _message, msg.sender ); return bytes32(0); } /* ========== Polygon ========== */ // Polygon L2 to L1 messaging is event-based }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "./MockMessenger.sol"; import "./Mock_L2_Messenger.sol"; contract Mock_L1_Messenger is MockMessenger { Mock_L2_Messenger public targetMessenger; constructor (IERC20 _canonicalToken) public MockMessenger(_canonicalToken) {} function setTargetMessenger(address _targetMessenger) public { targetMessenger = Mock_L2_Messenger(_targetMessenger); } /* ========== Arbitrum ========== */ function createRetryableTicket( address _destAddr, uint256 /* _arbTxCallValue */, uint256 /* _maxSubmissionCost */, address /* _submissionRefundAddress */, address /* _valueRefundAddress */, uint256 /* _maxGas */, uint256 /* _gasPriceBid */, bytes calldata _data ) external payable returns (uint256) { targetMessenger.receiveMessage( _destAddr, _data, msg.sender ); } /* ========== Optimism ========== */ function sendMessage( address _target, bytes calldata _message, uint32 /* _gasLimit */ ) public { targetMessenger.receiveMessage( _target, _message, msg.sender ); } /* ========== xDai ========== */ function requireToPassMessage( address _target, bytes calldata _message, uint256 /* _gasLimit */ ) public returns (bytes32) { targetMessenger.receiveMessage( _target, _message, msg.sender ); return bytes32('0'); } /* ========== Polygon ========== */ function syncState( address _fxChild, bytes memory _message ) external { targetMessenger.receiveMessage( _fxChild, _message, address(1) ); } function syncStateCanonicalToken( address _target, bytes memory _message ) public { targetMessenger.receiveMessage( _target, _message, msg.sender ); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "./Mock_L1_Messenger.sol"; contract Mock_L1_CanonicalBridge { using SafeERC20 for IERC20; IERC20 public canonicalToken; Mock_L1_Messenger public messenger; constructor ( IERC20 _canonicalToken, Mock_L1_Messenger _messenger ) public { canonicalToken = _canonicalToken; messenger = _messenger; } function sendMessage( address _target, bytes memory _message ) public { messenger.sendMessage( _target, _message, uint32(0) ); } /// @notice Polygon has a different messenger for each token function sendTokens( address _target, address _recipient, uint256 _amount, bool isPolygon ) public { bytes memory mintCalldata = abi.encodeWithSignature("mint(address,uint256)", _recipient, _amount); canonicalToken.safeTransferFrom(msg.sender, address(this), _amount); if (isPolygon) { messenger.syncStateCanonicalToken(_target, mintCalldata); } else { sendMessage(_target, mintCalldata); } } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "./L1_Bridge.sol"; /** * @dev A L1_Bridge that uses an ERC20 as the canonical token */ contract L1_ERC20_Bridge is L1_Bridge { using SafeERC20 for IERC20; IERC20 public immutable l1CanonicalToken; constructor (IERC20 _l1CanonicalToken, address[] memory bonders, address _governance) public L1_Bridge(bonders, _governance) { l1CanonicalToken = _l1CanonicalToken; } /* ========== Override Functions ========== */ function _transferFromBridge(address recipient, uint256 amount) internal override { l1CanonicalToken.safeTransfer(recipient, amount); } function _transferToBridge(address from, uint256 amount) internal override { l1CanonicalToken.safeTransferFrom(from, address(this), amount); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "./Bridge.sol"; import "../interfaces/IMessengerWrapper.sol"; /** * @dev L1_Bridge is responsible for the bonding and challenging of TransferRoots. All TransferRoots * originate in the L1_Bridge through `bondTransferRoot` and are propagated up to destination L2s. */ abstract contract L1_Bridge is Bridge { struct TransferBond { address bonder; uint256 createdAt; uint256 totalAmount; uint256 challengeStartTime; address challenger; bool challengeResolved; } /* ========== State ========== */ mapping(uint256 => mapping(bytes32 => uint256)) public transferRootCommittedAt; mapping(bytes32 => TransferBond) public transferBonds; mapping(uint256 => mapping(address => uint256)) public timeSlotToAmountBonded; mapping(uint256 => uint256) public chainBalance; /* ========== Config State ========== */ address public governance; mapping(uint256 => IMessengerWrapper) public crossDomainMessengerWrappers; mapping(uint256 => bool) public isChainIdPaused; uint256 public challengePeriod = 1 days; uint256 public challengeResolutionPeriod = 10 days; uint256 public minTransferRootBondDelay = 15 minutes; uint256 public constant CHALLENGE_AMOUNT_DIVISOR = 10; uint256 public constant TIME_SLOT_SIZE = 4 hours; /* ========== Events ========== */ event TransferSentToL2( uint256 indexed chainId, address indexed recipient, uint256 amount, uint256 amountOutMin, uint256 deadline, address indexed relayer, uint256 relayerFee ); event TransferRootBonded ( bytes32 indexed root, uint256 amount ); event TransferRootConfirmed( uint256 indexed originChainId, uint256 indexed destinationChainId, bytes32 indexed rootHash, uint256 totalAmount ); event TransferBondChallenged( bytes32 indexed transferRootId, bytes32 indexed rootHash, uint256 originalAmount ); event ChallengeResolved( bytes32 indexed transferRootId, bytes32 indexed rootHash, uint256 originalAmount ); /* ========== Modifiers ========== */ modifier onlyL2Bridge(uint256 chainId) { IMessengerWrapper messengerWrapper = crossDomainMessengerWrappers[chainId]; messengerWrapper.verifySender(msg.sender, msg.data); _; } constructor (address[] memory bonders, address _governance) public Bridge(bonders) { governance = _governance; } /* ========== Send Functions ========== */ /** * @notice `amountOutMin` and `deadline` should be 0 when no swap is intended at the destination. * @notice `amount` is the total amount the user wants to send including the relayer fee * @dev Send tokens to a supported layer-2 to mint hToken and optionally swap the hToken in the * AMM at the destination. * @param chainId The chainId of the destination chain * @param recipient The address receiving funds at the destination * @param amount The amount being sent * @param amountOutMin The minimum amount received after attempting to swap in the destination * AMM market. 0 if no swap is intended. * @param deadline The deadline for swapping in the destination AMM market. 0 if no * swap is intended. * @param relayer The address of the relayer at the destination. * @param relayerFee The amount distributed to the relayer at the destination. This is subtracted from the `amount`. */ function sendToL2( uint256 chainId, address recipient, uint256 amount, uint256 amountOutMin, uint256 deadline, address relayer, uint256 relayerFee ) external payable { IMessengerWrapper messengerWrapper = crossDomainMessengerWrappers[chainId]; require(messengerWrapper != IMessengerWrapper(0), "L1_BRG: chainId not supported"); require(isChainIdPaused[chainId] == false, "L1_BRG: Sends to this chainId are paused"); require(amount > 0, "L1_BRG: Must transfer a non-zero amount"); require(amount >= relayerFee, "L1_BRG: Relayer fee cannot exceed amount"); _transferToBridge(msg.sender, amount); bytes memory message = abi.encodeWithSignature( "distribute(address,uint256,uint256,uint256,address,uint256)", recipient, amount, amountOutMin, deadline, relayer, relayerFee ); chainBalance[chainId] = chainBalance[chainId].add(amount); messengerWrapper.sendCrossDomainMessage(message); emit TransferSentToL2( chainId, recipient, amount, amountOutMin, deadline, relayer, relayerFee ); } /* ========== TransferRoot Functions ========== */ /** * @dev Setting a TransferRoot is a two step process. * @dev 1. The TransferRoot is bonded with `bondTransferRoot`. Withdrawals can now begin on L1 * @dev and recipient L2's * @dev 2. The TransferRoot is confirmed after `confirmTransferRoot` is called by the l2 bridge * @dev where the TransferRoot originated. */ /** * @dev Used by the Bonder to bond a TransferRoot and propagate it up to destination L2s * @param rootHash The Merkle root of the TransferRoot Merkle tree * @param destinationChainId The id of the destination chain * @param totalAmount The amount destined for the destination chain */ function bondTransferRoot( bytes32 rootHash, uint256 destinationChainId, uint256 totalAmount ) external onlyBonder requirePositiveBalance { bytes32 transferRootId = getTransferRootId(rootHash, totalAmount); require(transferRootCommittedAt[destinationChainId][transferRootId] == 0, "L1_BRG: TransferRoot has already been confirmed"); require(transferBonds[transferRootId].createdAt == 0, "L1_BRG: TransferRoot has already been bonded"); uint256 currentTimeSlot = getTimeSlot(block.timestamp); uint256 bondAmount = getBondForTransferAmount(totalAmount); timeSlotToAmountBonded[currentTimeSlot][msg.sender] = timeSlotToAmountBonded[currentTimeSlot][msg.sender].add(bondAmount); transferBonds[transferRootId] = TransferBond( msg.sender, block.timestamp, totalAmount, uint256(0), address(0), false ); _distributeTransferRoot(rootHash, destinationChainId, totalAmount); emit TransferRootBonded(rootHash, totalAmount); } /** * @dev Used by an L2 bridge to confirm a TransferRoot via cross-domain message. Once a TransferRoot * has been confirmed, any challenge against that TransferRoot can be resolved as unsuccessful. * @param originChainId The id of the origin chain * @param rootHash The Merkle root of the TransferRoot Merkle tree * @param destinationChainId The id of the destination chain * @param totalAmount The amount destined for each destination chain * @param rootCommittedAt The block timestamp when the TransferRoot was committed on its origin chain */ function confirmTransferRoot( uint256 originChainId, bytes32 rootHash, uint256 destinationChainId, uint256 totalAmount, uint256 rootCommittedAt ) external onlyL2Bridge(originChainId) { bytes32 transferRootId = getTransferRootId(rootHash, totalAmount); require(transferRootCommittedAt[destinationChainId][transferRootId] == 0, "L1_BRG: TransferRoot already confirmed"); require(rootCommittedAt > 0, "L1_BRG: rootCommittedAt must be greater than 0"); transferRootCommittedAt[destinationChainId][transferRootId] = rootCommittedAt; chainBalance[originChainId] = chainBalance[originChainId].sub(totalAmount, "L1_BRG: Amount exceeds chainBalance. This indicates a layer-2 failure."); // If the TransferRoot was never bonded, distribute the TransferRoot. TransferBond storage transferBond = transferBonds[transferRootId]; if (transferBond.createdAt == 0) { _distributeTransferRoot(rootHash, destinationChainId, totalAmount); } emit TransferRootConfirmed(originChainId, destinationChainId, rootHash, totalAmount); } function _distributeTransferRoot( bytes32 rootHash, uint256 chainId, uint256 totalAmount ) internal { // Set TransferRoot on recipient Bridge if (chainId == getChainId()) { // Set L1 TransferRoot _setTransferRoot(rootHash, totalAmount); } else { chainBalance[chainId] = chainBalance[chainId].add(totalAmount); IMessengerWrapper messengerWrapper = crossDomainMessengerWrappers[chainId]; require(messengerWrapper != IMessengerWrapper(0), "L1_BRG: chainId not supported"); // Set L2 TransferRoot bytes memory setTransferRootMessage = abi.encodeWithSignature( "setTransferRoot(bytes32,uint256)", rootHash, totalAmount ); messengerWrapper.sendCrossDomainMessage(setTransferRootMessage); } } /* ========== External TransferRoot Challenges ========== */ /** * @dev Challenge a TransferRoot believed to be fraudulent * @param rootHash The Merkle root of the TransferRoot Merkle tree * @param originalAmount The total amount bonded for this TransferRoot * @param destinationChainId The id of the destination chain */ function challengeTransferBond(bytes32 rootHash, uint256 originalAmount, uint256 destinationChainId) external payable { bytes32 transferRootId = getTransferRootId(rootHash, originalAmount); TransferBond storage transferBond = transferBonds[transferRootId]; require(transferRootCommittedAt[destinationChainId][transferRootId] == 0, "L1_BRG: TransferRoot has already been confirmed"); require(transferBond.createdAt != 0, "L1_BRG: TransferRoot has not been bonded"); uint256 challengePeriodEnd = transferBond.createdAt.add(challengePeriod); require(challengePeriodEnd >= block.timestamp, "L1_BRG: TransferRoot cannot be challenged after challenge period"); require(transferBond.challengeStartTime == 0, "L1_BRG: TransferRoot already challenged"); transferBond.challengeStartTime = block.timestamp; transferBond.challenger = msg.sender; // Move amount from timeSlotToAmountBonded to debit uint256 timeSlot = getTimeSlot(transferBond.createdAt); uint256 bondAmount = getBondForTransferAmount(originalAmount); address bonder = transferBond.bonder; timeSlotToAmountBonded[timeSlot][bonder] = timeSlotToAmountBonded[timeSlot][bonder].sub(bondAmount); _addDebit(transferBond.bonder, bondAmount); // Get stake for challenge uint256 challengeStakeAmount = getChallengeAmountForTransferAmount(originalAmount); _transferToBridge(msg.sender, challengeStakeAmount); emit TransferBondChallenged(transferRootId, rootHash, originalAmount); } /** * @dev Resolve a challenge after the `challengeResolutionPeriod` has passed * @param rootHash The Merkle root of the TransferRoot Merkle tree * @param originalAmount The total amount originally bonded for this TransferRoot * @param destinationChainId The id of the destination chain */ function resolveChallenge(bytes32 rootHash, uint256 originalAmount, uint256 destinationChainId) external { bytes32 transferRootId = getTransferRootId(rootHash, originalAmount); TransferBond storage transferBond = transferBonds[transferRootId]; require(transferBond.challengeStartTime != 0, "L1_BRG: TransferRoot has not been challenged"); require(block.timestamp > transferBond.challengeStartTime.add(challengeResolutionPeriod), "L1_BRG: Challenge period has not ended"); require(transferBond.challengeResolved == false, "L1_BRG: TransferRoot already resolved"); transferBond.challengeResolved = true; uint256 challengeStakeAmount = getChallengeAmountForTransferAmount(originalAmount); if (transferRootCommittedAt[destinationChainId][transferRootId] > 0) { // Invalid challenge if (transferBond.createdAt > transferRootCommittedAt[destinationChainId][transferRootId].add(minTransferRootBondDelay)) { // Credit the bonder back with the bond amount plus the challenger's stake _addCredit(transferBond.bonder, getBondForTransferAmount(originalAmount).add(challengeStakeAmount)); } else { // If the TransferRoot was bonded before it was committed, the challenger and Bonder // get their stake back. This discourages Bonders from tricking challengers into // challenging a valid TransferRoots that haven't yet been committed. It also ensures // that Bonders are not punished if a TransferRoot is bonded too soon in error. // Return the challenger's stake _addCredit(transferBond.challenger, challengeStakeAmount); // Credit the bonder back with the bond amount _addCredit(transferBond.bonder, getBondForTransferAmount(originalAmount)); } } else { // Valid challenge // Burn 25% of the challengers stake _transferFromBridge(address(0xdead), challengeStakeAmount.mul(1).div(4)); // Reward challenger with the remaining 75% of their stake plus 100% of the Bonder's stake _addCredit(transferBond.challenger, challengeStakeAmount.mul(7).div(4)); } emit ChallengeResolved(transferRootId, rootHash, originalAmount); } /* ========== Override Functions ========== */ function _additionalDebit(address bonder) internal view override returns (uint256) { uint256 currentTimeSlot = getTimeSlot(block.timestamp); uint256 bonded = 0; uint256 numTimeSlots = challengePeriod / TIME_SLOT_SIZE; for (uint256 i = 0; i < numTimeSlots; i++) { bonded = bonded.add(timeSlotToAmountBonded[currentTimeSlot - i][bonder]); } return bonded; } function _requireIsGovernance() internal override { require(governance == msg.sender, "L1_BRG: Caller is not the owner"); } /* ========== External Config Management Setters ========== */ function setGovernance(address _newGovernance) external onlyGovernance { require(_newGovernance != address(0), "L1_BRG: _newGovernance cannot be address(0)"); governance = _newGovernance; } function setCrossDomainMessengerWrapper(uint256 chainId, IMessengerWrapper _crossDomainMessengerWrapper) external onlyGovernance { crossDomainMessengerWrappers[chainId] = _crossDomainMessengerWrapper; } function setChainIdDepositsPaused(uint256 chainId, bool isPaused) external onlyGovernance { isChainIdPaused[chainId] = isPaused; } function setChallengePeriod(uint256 _challengePeriod) external onlyGovernance { require(_challengePeriod % TIME_SLOT_SIZE == 0, "L1_BRG: challengePeriod must be divisible by TIME_SLOT_SIZE"); challengePeriod = _challengePeriod; } function setChallengeResolutionPeriod(uint256 _challengeResolutionPeriod) external onlyGovernance { challengeResolutionPeriod = _challengeResolutionPeriod; } function setMinTransferRootBondDelay(uint256 _minTransferRootBondDelay) external onlyGovernance { minTransferRootBondDelay = _minTransferRootBondDelay; } /* ========== Public Getters ========== */ function getBondForTransferAmount(uint256 amount) public pure returns (uint256) { // Bond covers amount plus a bounty to pay a potential challenger return amount.add(getChallengeAmountForTransferAmount(amount)); } function getChallengeAmountForTransferAmount(uint256 amount) public pure returns (uint256) { // Bond covers amount plus a bounty to pay a potential challenger return amount.div(CHALLENGE_AMOUNT_DIVISOR); } function getTimeSlot(uint256 time) public pure returns (uint256) { return time / TIME_SLOT_SIZE; } }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "../bridges/L1_ERC20_Bridge.sol"; contract Mock_L1_ERC20_Bridge is L1_ERC20_Bridge { constructor (IERC20 _canonicalToken, address[] memory _bonders, address _governance) public L1_ERC20_Bridge(_canonicalToken, _bonders, _governance) {} function getChainId() public override view returns (uint256) { return 1; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "./L1_Bridge.sol"; /** * @dev A L1_Bridge that uses an ETH as the canonical token */ contract L1_ETH_Bridge is L1_Bridge { constructor (address[] memory bonders, address _governance) public L1_Bridge(bonders, _governance) {} /* ========== Override Functions ========== */ function _transferFromBridge(address recipient, uint256 amount) internal override { (bool success, ) = recipient.call{value: amount}(new bytes(0)); require(success, 'L1_ETH_BRG: ETH transfer failed'); } function _transferToBridge(address /*from*/, uint256 amount) internal override { require(msg.value == amount, "L1_ETH_BRG: Value does not match amount"); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "../bridges/L1_ETH_Bridge.sol"; contract Mock_L1_ETH_Bridge is L1_ETH_Bridge { constructor (address[] memory _bonders, address _governance) public L1_ETH_Bridge(_bonders, _governance) {} function getChainId() public override view returns (uint256) { return 1; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "./ISwap.sol"; interface ISwapFlashLoan is ISwap { function flashLoan( address receiver, IERC20 token, uint256 amount, bytes memory params ) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "./IAllowlist.sol"; interface ISwapGuarded { // pool data view functions function getA() external view returns (uint256); function getAllowlist() external view returns (IAllowlist); function getToken(uint8 index) external view returns (IERC20); function getTokenIndex(address tokenAddress) external view returns (uint8); function getTokenBalance(uint8 index) external view returns (uint256); function getVirtualPrice() external view returns (uint256); function isGuarded() external view returns (bool); // min return calculation functions function calculateSwap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) external view returns (uint256); function calculateTokenAmount(uint256[] calldata amounts, bool deposit) external view returns (uint256); function calculateRemoveLiquidity(uint256 amount) external view returns (uint256[] memory); function calculateRemoveLiquidityOneToken( uint256 tokenAmount, uint8 tokenIndex ) external view returns (uint256 availableTokenAmount); // state modifying functions function swap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy, uint256 deadline ) external returns (uint256); function addLiquidity( uint256[] calldata amounts, uint256 minToMint, uint256 deadline, bytes32[] calldata merkleProof ) external returns (uint256); function removeLiquidity( uint256 amount, uint256[] calldata minAmounts, uint256 deadline ) external returns (uint256[] memory); function removeLiquidityOneToken( uint256 tokenAmount, uint8 tokenIndex, uint256 minAmount, uint256 deadline ) external returns (uint256); function removeLiquidityImbalance( uint256[] calldata amounts, uint256 maxBurnAmount, uint256 deadline ) external returns (uint256); // withdraw fee update function function updateUserWithdrawFee(address recipient, uint256 transferAmount) external; }
// SPDX-License-Identifier: MIT // @unsupported: ovm pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "./L2_Bridge.sol"; import "../interfaces/polygon/messengers/I_L2_PolygonMessengerProxy.sol"; /** * @dev An L2_Bridge for Polygon - https://docs.matic.network/docs */ contract L2_PolygonBridge is L2_Bridge { I_L2_PolygonMessengerProxy public messengerProxy; event L1_BridgeMessage(bytes data); constructor ( I_L2_PolygonMessengerProxy _messengerProxy, address l1Governance, HopBridgeToken hToken, address l1BridgeAddress, uint256[] memory activeChainIds, address[] memory bonders ) public L2_Bridge( l1Governance, hToken, l1BridgeAddress, activeChainIds, bonders ) { messengerProxy = _messengerProxy; } function _sendCrossDomainMessage(bytes memory message) internal override { messengerProxy.sendCrossDomainMessage(message); } function _verifySender(address expectedSender) internal override { require(msg.sender == address(messengerProxy), "L2_PLGN_BRG: Caller is not the expected sender"); // Verify that cross-domain sender is expectedSender require(messengerProxy.xDomainMessageSender() == expectedSender, "L2_PLGN_BRG: Invalid cross-domain sender"); } /** * @dev Allows the L1 Bridge to set the messengerProxy proxy * @param _messengerProxy The new messengerProxy address */ function setMessengerProxy(I_L2_PolygonMessengerProxy _messengerProxy) external onlyGovernance { messengerProxy = _messengerProxy; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.6.0; interface I_L2_PolygonMessengerProxy { function sendCrossDomainMessage(bytes memory _calldata) external; function xDomainMessageSender() external view returns (address); function processMessageFromRoot( bytes calldata message ) external; }
// SPDX-License-Identifier: UNLICENSED // @unsupported: ovm pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "../interfaces/polygon/messengers/I_L2_PolygonMessengerProxy.sol"; import "../bridges/L2_PolygonBridge.sol"; contract Mock_L2_PolygonBridge is L2_PolygonBridge { uint256 private chainId; constructor ( uint256 _chainId, I_L2_PolygonMessengerProxy messenger, address l1Governance, HopBridgeToken hToken, address l1BridgeAddress, uint256[] memory supportedChainIds, address[] memory bonders ) public L2_PolygonBridge( messenger, l1Governance, hToken, l1BridgeAddress, supportedChainIds, bonders ) { chainId = _chainId; } function getChainId() public override view returns (uint256) { return chainId; } }
{ "optimizer": { "enabled": true, "runs": 1 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "abi" ] } }, "libraries": { "contracts/saddle/MathUtils.sol": { "MathUtils": "0x30e344c8f517becaeed04245ced2e7301f06f21b" } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenAmounts","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"fees","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"invariant","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lpTokenSupply","type":"uint256"}],"name":"AddLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenAmounts","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"lpTokenSupply","type":"uint256"}],"name":"RemoveLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenAmounts","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"fees","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"invariant","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lpTokenSupply","type":"uint256"}],"name":"RemoveLiquidityImbalance","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256","name":"lpTokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lpTokenSupply","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"boughtId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokensBought","type":"uint256"}],"name":"RemoveLiquidityOne","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokensSold","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokensBought","type":"uint256"},{"indexed":false,"internalType":"uint128","name":"soldId","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"boughtId","type":"uint128"}],"name":"TokenSwap","type":"event"},{"inputs":[],"name":"A_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_A","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_ADMIN_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_SWAP_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_WITHDRAW_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"POOL_PRECISION_DECIMALS","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
61536762000082600b82828239805160001a6073146200001b57fe5b5a63996d79a5598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051925060005b604081101562000073576000828201526020016200005a565b505050600052607381538281f3fe7300000000000000000000000000000000000000005a63996d79a5598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051925060005b604081101561006a57600082820152602001610053565b505050146080604052600436106101745760003560e01c80630296ab501461018257806339698415146101a057806340370edf146101ba57806341b91c261461029e5780634b23603c146103825780635e58579c146103c157806371906c2c1461046a57806373fd6b3e146104905780637d04816014610597578063968e5594146105c3578063a5397b22146105f8578063ab3d854414610653578063b0a14cfc1461065b578063c9b64dcb14610681578063d011f918146106a7578063d4b0de2f14610653578063e4a9a0da146106af578063e7a4db811461070b578063e8c47a541461075c578063e9252d4614610791578063f3de0362146107e4578063f967a81f146107ec575b60008061017f614e0a565b50505b61018a610827565b60405160ff909116815260200160405180910390f35b6101a861082c565b60405190815260200160405180910390f35b8180156101cf576000806101cc614e0a565b50505b506101a8600480360360608110156101ef576000806101ec614e0a565b50505b81359190810190604081016020820135600160201b81111561021957600080610216614e0a565b50505b82018360208201111561023457600080610231614e0a565b50505b803590602001918460208302840111600160201b8311171561025e5760008061025b614e0a565b50505b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505091359250610833915050565b8180156102b3576000806102b0614e0a565b50505b506101a8600480360360608110156102d3576000806102d0614e0a565b50505b81359190810190604081016020820135600160201b8111156102fd576000806102fa614e0a565b50505b82018360208201111561031857600080610315614e0a565b50505b803590602001918460208302840111600160201b831117156103425760008061033f614e0a565b50505b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505091359250611411915050565b6101a8600480360360808110156103a15760008061039e614e0a565b50505b5080359060ff602082013581169160408101359091169060600135611d76565b6101a8600480360360808110156103e0576000806103dd614e0a565b50505b8135916001600160a01b0360208201351691810190606081016040820135600160201b81111561041857600080610415614e0a565b50505b82018360208201111561043357600080610430614e0a565b50505b803590602001918460208302840111600160201b8311171561045d5760008061045a614e0a565b50505b9193509150351515611d8e565b6101a86004803603602081101561048957600080610486614e0a565b50505b5035612078565b8180156104a5576000806104a2614e0a565b50505b50610544600480360360608110156104c5576000806104c2614e0a565b50505b813591602081013591810190606081016040820135600160201b8111156104f4576000806104f1614e0a565b50505b82018360208201111561050f5760008061050c614e0a565b50505b803590602001918460208302840111600160201b8311171561053957600080610536614e0a565b50505b50909250905061223a565b60405160208082528190810183818151815260200191508051906020019060200280838360005b8381101561058357808201518382015260200161056b565b505050509050019250505060405180910390f35b6101a8600480360360408110156105b6576000806105b3614e0a565b50505b5080359060200135612720565b6101a8600480360360408110156105e2576000806105df614e0a565b50505b50803590602001356001600160a01b03166127bc565b81801561060d5760008061060a614e0a565b50505b506101a8600480360360a081101561062d5760008061062a614e0a565b50505b5080359060ff602082013581169160408101359091169060608101359060800135612874565b6101a8612d86565b6101a86004803603602081101561067a57600080610677614e0a565b50505b5035612d8e565b6101a8600480360360208110156106a05760008061069d614e0a565b50505b5035612d99565b6101a8612da4565b6106f3600480360360808110156106ce576000806106cb614e0a565b50505b5080359060208101356001600160a01b0316906040810135906060013560ff16612da9565b60405191825260208201526040908101905180910390f35b8180156107205760008061071d614e0a565b50505b506101a8600480360360808110156107405760008061073d614e0a565b50505b5080359060208101359060ff6040820135169060600135612e3e565b6101a86004803603604081101561077b57600080610778614e0a565b50505b50803590602001356001600160a01b0316613299565b8180156107a6576000806107a3614e0a565b50505b506107e2600480360360608110156107c6576000806107c3614e0a565b50505b508035906001600160a01b0360208201351690604001356132bb565b005b6101a86132cb565b6105446004803603606081101561080b57600080610808614e0a565b50505b508035906001600160a01b0360208201351690604001356132d4565b601281565b620f424081565b60008360080180610842614e75565b90508351146108a05760405162461bcd60e51b815260206004820181905260248201527f416d6f756e7473206d757374206d6174636820706f6f6c656420746f6b656e7360448201526064016040518091039061089d614e0a565b50505b606084600801806108af614e75565b90506001600160401b03811180156108cf576000806108cc614e0a565b50505b506040519080825280602002602001820160405280156108f9578160200160208202803683370190505b509050610904614ed0565b60405180608001604052806000815260200160008152602001600081526020016000815250905060008660070160009061093c614e75565b906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381868061097f614ef8565b15801561099457600080610991614e0a565b50505b505a61099e614f44565b50505050501580156109bd573d6000803e3d60006109ba614e0a565b50505b505050506040513d60208110156109dc576000806109d9614e0a565b50505b810190808051935050821591506109fb9050576109f8876132e1565b82525b606087600a0180610a0a614e75565b806020026020016040519081016040528181529190602083018280610a2d614e75565b8015610a5f57602002820191906000526020600020905b81610a4d614e75565b81526020019060010190808311610a44575b5050505050905060005b8860080180610a76614e75565b9050811015610ee35782151580610aa057506000888281518110610a9657fe5b6020026020010151115b610af95760405162461bcd60e51b815260206004820152601e60248201527f4d75737420737570706c7920616c6c20746f6b656e7320696e20706f6f6c0000604482015260640160405180910390610af6614e0a565b50505b878181518110610b0557fe5b602002602001015115610e7f576000896008018281610b22614e75565b8110610b2a57fe5b90600052602060002001600090610b3f614e75565b906101000a90046001600160a01b03166001600160a01b03166370a082315a63996d79a5598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051925060005b6040811015610bb357600082820152602001610b9c565b5050506040516001600160e01b031960e084901b1681526001600160a01b0390911660048201526024016020604051808303818680610bf0614ef8565b158015610c0557600080610c02614e0a565b50505b505a610c0f614f44565b5050505050158015610c2e573d6000803e3d6000610c2b614e0a565b50505b505050506040513d6020811015610c4d57600080610c4a614e0a565b50505b8101908080519350610d17925050505a610c6561502f565b5a63996d79a5598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051925060005b6040811015610cbb57600082820152602001610ca4565b5050508b8581518110610cca57fe5b60200260200101518d6008018681610ce0614e75565b8110610ce857fe5b90600052602060002001600090610cfd614e75565b6001600160a01b036101009290920a9004169291906132f8565b610e65818b6008018481610d29614e75565b8110610d3157fe5b90600052602060002001600090610d46614e75565b906101000a90046001600160a01b03166001600160a01b03166370a082315a63996d79a5598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051925060005b6040811015610dba57600082820152602001610da3565b5050506040516001600160e01b031960e084901b1681526001600160a01b0390911660048201526024016020604051808303818680610df7614ef8565b158015610e0c57600080610e09614e0a565b50505b505a610e16614f44565b5050505050158015610e35573d6000803e3d6000610e32614e0a565b50505b505050506040513d6020811015610e5457600080610e51614e0a565b50505b810190808051939250506133679050565b898381518110610e7157fe5b602002602001018181525050505b610ec4888281518110610e8e57fe5b60200260200101518a600a018381610ea4614e75565b8110610eac57fe5b90600052602060002001610ebe614e75565b906133cc565b828281518110610ed057fe5b6020908102919091010152600101610a69565b50610eed88613432565b6060840152610f09610eff89836134fe565b8460600151613570565b60208401528251836020015111610f635760405162461bcd60e51b8152602060048201526011602482015270442073686f756c6420696e63726561736560781b604482015260640160405180910390610f60614e0a565b50505b8260200151604084015281156111b8576000610f7e8961378a565b905060005b8960080180610f90614e75565b9050811015611195576000610fdc8651610fd68d600a018581610fb1614e75565b8110610fb957fe5b90600052602060002001610fcb614e75565b8960200151906137cc565b9061382b565b90506110be6402540be400610fd67330e344c8f517becaeed04245ced2e7301f06f21b63a95cb7f78589888151811061101157fe5b60200260200101516040516001600160e01b031960e085901b168152600481019290925260248201526044016020604051808303818680611050614ef8565b15801561106557600080611062614e0a565b50505b505a61106f615075565b505050505015801561108e573d6000803e3d600061108b614e0a565b50505b505050506040513d60208110156110ad576000806110aa614e0a565b50505b8101908080518993509150506137cc565b8783815181106110ca57fe5b60200260200101818152505061112561110b6402540be400610fd68e6005016110f1614e75565b8b87815181106110fd57fe5b6020026020010151906137cc565b85848151811061111757fe5b602002602001015190613367565b8b600a018381611133614e75565b811061113b57fe5b90600052602060002001819061114f615086565b50505061117587838151811061116157fe5b602002602001015185848151811061111757fe5b84838151811061118157fe5b602090810291909101015250600101610f83565b506111ad6111a38a846134fe565b8560600151613570565b6040850152506111cf565b600a88018180516111cd9291602001906150d4565b505b6000826111e25783602001519050611204565b6112018451610fd6856111fb8851896040015190613367565b906137cc565b90505b8681101561125f5760405162461bcd60e51b815260206004820152601b60248201527a10dbdd5b191b89dd081b5a5b9d081b5a5b881c995c5d595cdd1959602a1b60448201526064016040518091039061125c614e0a565b50505b600060078a0161126d614e75565b906101000a90046001600160a01b03166001600160a01b03166340c10f195a61129461502f565b836040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401600060405180830381600087806112d6614ef8565b1580156112eb576000806112e8614e0a565b50505b505a6112f5615138565b505050505050158015611315573d6000803e3d6000611312614e0a565b50505b505050505a61132261502f565b6001600160a01b03167f189c623b666b1b45b83d7178f39b8c087cb09774317ca2f53c2d3c3726f222a28987876020015161135d88876133cc565b604051808060200180602001858152602001848152602001838103835287818151815260200191508051906020019060200280838360005b838110156113ad578082015183820152602001611395565b50505050905001838103825286818151815260200191508051906020019060200280838360005b838110156113ec5780820151838201526020016113d4565b50505050905001965050505050505060405180910390a29450505050505b9392505050565b60008360080180611420614e75565b905083511461147e5760405162461bcd60e51b815260206004820181905260248201527f416d6f756e74732073686f756c64206d6174636820706f6f6c20746f6b656e7360448201526064016040518091039061147b614e0a565b50505b60006007850161148c614e75565b906101000a90046001600160a01b03166001600160a01b03166370a082315a6114b361502f565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260240160206040518083038186806114ed614ef8565b158015611502576000806114ff614e0a565b50505b505a61150c614f44565b505050505015801561152b573d6000803e3d6000611528614e0a565b50505b505050506040513d602081101561154a57600080611547614e0a565b50505b8101908080518511801593509150611563905057508115155b6115ac5760405162461bcd60e51b815260206004820152600d60248201526c1f2628173130b630b731b2a7b360991b6044820152606401604051809103906115a9614e0a565b50505b6115b4614ed0565b6040518060800160405280600081526020016000815260200160008152602001600081525090506000856007016000906115ec614e75565b906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381868061162f614ef8565b15801561164457600080611641614e0a565b50505b505a61164e614f44565b505050505015801561166d573d6000803e3d600061166a614e0a565b50505b505050506040513d602081101561168c57600080611689614e0a565b50505b8101908080519350600092506116a5915088905061378a565b9050606087600a01806116b6614e75565b8060200260200160405190810160405281815291906020830182806116d9614e75565b801561170b57602002820191906000526020600020905b816116f9614e75565b815260200190600101908083116116f0575b5050505050905061171b88613432565b606085015261172c6111a389613897565b845260005b886008018061173e614e75565b90508110156117af5761179088828151811061175657fe5b6020026020010151604051606081016040526023808252615344602083013984848151811061178157fe5b6020026020010151919061396e565b82828151811061179c57fe5b6020908102919091010152600101611731565b506117bd6111a389836134fe565b6020850152606060088901806117d1614e75565b90506001600160401b03811180156117f1576000806117ee614e0a565b50505b5060405190808252806020026020018201604052801561181b578160200160208202803683370190505b50905060005b896008018061182e614e75565b9050811015611a185760006118748751610fd68d600a01858161184f614e75565b811061185757fe5b90600052602060002001611869614e75565b8a60200151906137cc565b905060007330e344c8f517becaeed04245ced2e7301f06f21b63a95cb7f78387868151811061189f57fe5b60200260200101516040516001600160e01b031960e085901b1681526004810192909252602482015260440160206040518083038186806118de614ef8565b1580156118f3576000806118f0614e0a565b50505b505a6118fd615075565b505050505015801561191c573d6000803e3d6000611919614e0a565b50505b505050506040513d602081101561193b57600080611938614e0a565b50505b810190808051935061195c92506402540be4009150610fd6905088846137cc565b84848151811061196857fe5b6020026020010181815250506119a761199b6402540be400610fd68f60050161198f614e75565b8888815181106110fd57fe5b86858151811061111757fe5b8c600a0184816119b5614e75565b81106119bd57fe5b9060005260206000200181906119d1615086565b5050506119f78484815181106119e357fe5b602002602001015186858151811061111757fe5b858481518110611a0357fe5b60209081029190910101525050600101611821565b50611a30611a268a846134fe565b8660600151613570565b60408601526000611a508651610fd6876111fb8a604001518b5190613367565b905080611aaa5760405162461bcd60e51b815260206004820152601b60248201527a4275726e7420616d6f756e742063616e6e6f74206265207a65726f60281b604482015260640160405180910390611aa7614e0a565b50505b611ae2611ace611ac28c5a611abd61502f565b6127bc565b6402540be40090613367565b610fd66402540be4006111fb8560016133cc565b905087811115611b3f5760405162461bcd60e51b815260206004820152601b60248201527a1d1bdad95b905b5bdd5b9d080f881b585e109d5c9b905b5bdd5b9d602a1b604482015260640160405180910390611b3c614e0a565b50505b600060078b01611b4d614e75565b906101000a90046001600160a01b03166001600160a01b03166379cc67905a611b7461502f565b836040516001600160e01b031960e085901b1681526001600160a01b039092166004830152602482015260440160006040518083038160008780611bb6614ef8565b158015611bcb57600080611bc8614e0a565b50505b505a611bd5615138565b505050505050158015611bf5573d6000803e3d6000611bf2614e0a565b50505b5050505060005b8a60080180611c09614e75565b9050811015611c7e57611c765a611c1e61502f565b8b8381518110611c2a57fe5b60200260200101518d6008018481611c40614e75565b8110611c4857fe5b90600052602060002001600090611c5d614e75565b6001600160a01b036101009290920a9004169190613a0d565b600101611bfc565b505a611c8861502f565b6001600160a01b03167f3631c28b1f9dd213e0319fb167b554d76b6c283a41143eb400a0d1adb1af17558a848960200151611cc38a87613367565b604051808060200180602001858152602001848152602001838103835287818151815260200191508051906020019060200280838360005b83811015611d13578082015183820152602001611cfb565b50505050905001838103825286818151815260200191508051906020019060200280838360005b83811015611d52578082015183820152602001611d3a565b50505050905001965050505050505060405180910390a29998505050505050505050565b6000611d8485858585613a69565b5095945050505050565b6000808660080180611d9e614e75565b905090506000611dad88613432565b90506000611e2a611e248a8b600a0180611dc5614e75565b806020026020016040519081016040528181529190602083018280611de8614e75565b8015611e1a57602002820191906000526020600020905b81611e08614e75565b81526020019060010190808311611dff575b50505050506134fe565b83613570565b9050606089600a0180611e3b614e75565b806020026020016040519081016040528181529190602083018280611e5e614e75565b8015611e9057602002820191906000526020600020905b81611e7e614e75565b81526020019060010190808311611e75575b5050505050905060005b84811015611f4f578615611ef557611ed8898983818110611eb757fe5b90506020020135838381518110611eca57fe5b6020026020010151906133cc565b828281518110611ee457fe5b602002602001018181525050611f47565b611f2e898983818110611f0457fe5b90506020020135604051606081016040526023808252615344602083013984848151811061178157fe5b828281518110611f3a57fe5b6020026020010181815250505b600101611e9a565b506000611f65611f5f8c846134fe565b85613570565b905060008060078d01611f76614e75565b906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b81526004016020604051808303818680611fb9614ef8565b158015611fce57600080611fcb614e0a565b50505b505a611fd8614f44565b5050505050158015611ff7573d6000803e3d6000611ff4614e0a565b50505b505050506040513d602081101561201657600080612013614e0a565b50505b8101908080519350508915915061204890505761203b84610fd6836111fb8684613367565b965050505050505061206f565b61203b612058611ac28e8e6127bc565b610fd66402540be4006111fb88838783838b613367565b95945050505050565b60008061209561208784613897565b61209085613432565b613570565b9050600080600785016120a6614e75565b906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186806120e9614ef8565b1580156120fe576000806120fb614e0a565b50505b505a612108614f44565b5050505050158015612127573d6000803e3d6000612124614e0a565b50505b505050506040513d602081101561214657600080612143614e0a565b50505b8101908080519350508215915061222e90505761222581610fd660006007880161216e614e75565b906101000a90046001600160a01b03166001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186806121b1614ef8565b1580156121c6576000806121c3614e0a565b50505b505a6121d0614f44565b50505050501580156121ef573d6000803e3d60006121ec614e0a565b50505b505050506040513d602081101561220e5760008061220b614e0a565b50505b81019080805188935060ff16600a0a9150506137cc565b92505050612235565b6000925050505b919050565b606060006007860161224a614e75565b906101000a90046001600160a01b03166001600160a01b03166370a082315a61227161502f565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260240160206040518083038186806122ab614ef8565b1580156122c0576000806122bd614e0a565b50505b505a6122ca614f44565b50505050501580156122e9573d6000803e3d60006122e6614e0a565b50505b505050506040513d602081101561230857600080612305614e0a565b50505b810190808051871115925061235f9150505760405162461bcd60e51b815260206004820152600d60248201526c1f2628173130b630b731b2a7b360991b60448201526064016040518091039061235c614e0a565b50505b846008018061236c614e75565b831490506123c95760405162461bcd60e51b815260206004820181905260248201527f6d696e416d6f756e7473206d757374206d6174636820706f6f6c546f6b656e736044820152606401604051809103906123c6614e0a565b50505b60606123de865a6123d861502f565b87613bb4565b905060005b8151811015612513578484828181106123f857fe5b9050602002013582828151811061240b57fe5b6020026020010151101561246b5760405162461bcd60e51b815260206004820152601a602482015279616d6f756e74735b695d203c206d696e416d6f756e74735b695d60301b604482015260640160405180910390612468614e0a565b50505b6124b082828151811061247a57fe5b602002602001015188600a018381612490614e75565b811061249857fe5b906000526020600020016124aa614e75565b90613367565b87600a0182816124be614e75565b81106124c657fe5b9060005260206000200181906124da615086565b50505061250b5a6124e961502f565b8383815181106124f557fe5b6020026020010151896008018481611c40614e75565b6001016123e3565b50600060078701612522614e75565b906101000a90046001600160a01b03166001600160a01b03166379cc67905a61254961502f565b876040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044016000604051808303816000878061258b614ef8565b1580156125a05760008061259d614e0a565b50505b505a6125aa615138565b5050505050501580156125ca573d6000803e3d60006125c7614e0a565b50505b505050505a6125d761502f565b6001600160a01b03167f88d38ed598fdd809c2bf01ee49cd24b7fdabf379a83d29567952b60324d58cef82600060078a01612610614e75565b906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b81526004016020604051808303818680612653614ef8565b15801561266857600080612665614e0a565b50505b505a612672614f44565b5050505050158015612691573d6000803e3d600061268e614e0a565b50505b505050506040513d60208110156126b0576000806126ad614e0a565b50505b81019080805192506040915050518080602001838152602001828103825284818151815260200191508051906020019060200280838360005b838110156127015780820151838201526020016126e9565b50505050905001935050505060405180910390a290505b949350505050565b6000826008018061272f614e75565b9050821061277a5760405162461bcd60e51b815260206004820152601860248201526000805160206152fa833981519152604482015260640160405180910390612777614e0a565b50505b6127b383600a01838161278b614e75565b811061279357fe5b906000526020600020016127a5614e75565b846008018481610d29614e75565b90505b92915050565b6001600160a01b0381166000908152600b8301602052806127e66224ea0060408320610ebe614e75565b90505a6127f16151fe565b81111561286a57600061280d5a6128066151fe565b8390613367565b90506128616402540be400610fd66224ea00610fd6856111fb8b600c0160008c6001600160a01b03166001600160a01b03168152602001908152602001600020612855614e75565b8c6006016111fb614e75565b925050506127b6565b5060009392505050565b6000856008018560ff1681612887614e75565b811061288f57fe5b906000526020600020016000906128a4614e75565b906101000a90046001600160a01b03166001600160a01b03166370a082315a6128cb61502f565b6040516001600160e01b031960e084901b1681526001600160a01b0390911660048201526024016020604051808303818680612905614ef8565b15801561291a57600080612917614e0a565b50505b505a612924614f44565b5050505050158015612943573d6000803e3d6000612940614e0a565b50505b505050506040513d60208110156129625760008061295f614e0a565b50505b81019080805186111592506129c99150505760405162461bcd60e51b815260206004820152601d60248201527f43616e6e6f742073776170206d6f7265207468616e20796f75206f776e0000006044820152606401604051809103906129c6614e0a565b50505b6000866008018660ff16816129dc614e75565b81106129e457fe5b906000526020600020016000906129f9614e75565b906101000a90046001600160a01b03166001600160a01b03166370a082315a63996d79a5598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051925060005b6040811015612a6d57600082820152602001612a56565b5050506040516001600160e01b031960e084901b1681526001600160a01b0390911660048201526024016020604051808303818680612aaa614ef8565b158015612abf57600080612abc614e0a565b50505b505a612ac9614f44565b5050505050158015612ae8573d6000803e3d6000612ae5614e0a565b50505b505050506040513d6020811015612b0757600080612b04614e0a565b50505b8101908080519350612b8a925050505a612b1f61502f565b5a63996d79a5598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051925060005b6040811015612b7557600082820152602001612b5e565b505050868a6008018a60ff1681610ce0614e75565b6000612ba182896008018960ff1681610d29614e75565b9050600080612bb28a8a8a86613a69565b9150915085821015612c135760405162461bcd60e51b815260206004820181905260248201527f53776170206469646e277420726573756c7420696e206d696e20746f6b656e73604482015260640160405180910390612c10614e0a565b50505b6000612c628b6009018a60ff1681612c29614e75565b8110612c3157fe5b90600052602060002001612c43614e75565b610fd66402540be400610fd68f600501612c5b614e75565b87906137cc565b9050612c79848c600a018c60ff1681610ea4614e75565b8b600a018b60ff1681612c8a614e75565b8110612c9257fe5b906000526020600020018190612ca6615086565b505050612cc2816124aa858e600a018d60ff1681612490614e75565b8b600a018a60ff1681612cd3614e75565b8110612cdb57fe5b906000526020600020018190612cef615086565b505050612d105a612cfe61502f565b848d6008018c60ff1681611c40614e75565b5a612d1961502f565b6001600160a01b03167fc6c1e0630dbe9130cc068028486c0d118ddcea348550819defd5cb8c257f8a3885858d8d604051808581526020018481526020018360ff1681526020018260ff16815260200194505050505060405180910390a250909998505050505050505050565b6305f5e10081565b60006127b682613db6565b60006127b682613432565b606481565b600080600080612dba888688613dc6565b90925090506000612e0e836124aa60098c0160ff8a1681612dd9614e75565b8110612de157fe5b90600052602060002001612df3614e75565b610fd686612e008f613897565b8c60ff168151811061111757fe5b9050612e306402540be400610fd6612e29611ac28d8d6127bc565b86906137cc565b999098509650505050505050565b6000808060078701612e4e614e75565b906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b81526004016020604051808303818680612e91614ef8565b158015612ea657600080612ea3614e0a565b50505b505a612eb0614f44565b5050505050158015612ecf573d6000803e3d6000612ecc614e0a565b50505b505050506040513d6020811015612eee57600080612eeb614e0a565b50505b81019080805193506000925050506008870180612f09614e75565b91506000905060078801612f1b614e75565b906101000a90046001600160a01b03166001600160a01b03166370a082315a612f4261502f565b6040516001600160e01b031960e084901b1681526001600160a01b0390911660048201526024016020604051808303818680612f7c614ef8565b158015612f9157600080612f8e614e0a565b50505b505a612f9b614f44565b5050505050158015612fba573d6000803e3d6000612fb7614e0a565b50505b505050506040513d6020811015612fd957600080612fd6614e0a565b50505b81019080805189111592506130309150505760405162461bcd60e51b815260206004820152600d60248201526c1f2628173130b630b731b2a7b360991b60448201526064016040518091039061302d614e0a565b50505b808560ff16106130815760405162461bcd60e51b815260206004820152600f60248201526e151bdad95b881b9bdd08199bdd5b99608a1b60448201526064016040518091039061307e614e0a565b50505b600080613098895a61309161502f565b8a8a612da9565b92509050858110156130ea5760405162461bcd60e51b815260206004820152600e60248201526d191e480f081b5a5b905b5bdd5b9d60921b6044820152606401604051809103906130e7614e0a565b50505b61312061310f6131086402540be400610fd68d600501612c5b614e75565b83906133cc565b8a600a018960ff1681612490614e75565b89600a018860ff1681613131614e75565b811061313957fe5b90600052602060002001819061314d615086565b5050508860070160009061315f614e75565b906101000a90046001600160a01b03166001600160a01b03166379cc67905a61318661502f565b8a6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401600060405180830381600087806131c8614ef8565b1580156131dd576000806131da614e0a565b50505b505a6131e7615138565b505050505050158015613207573d6000803e3d6000613204614e0a565b50505b505050506132295a61321761502f565b828b6008018a60ff1681611c40614e75565b5a61323261502f565b6001600160a01b03167f43fb02998f4e03da2e0e6fff53fdbf0c40a9f45f145dc377fc30615d7d7a8a6489868a85604051808581526020018481526020018360ff16815260200182815260200194505050505060405180910390a298975050505050505050565b6001600160a01b0381166000908152600b8301602052604081206127b3614e75565b6132c683838361416e565b505050565b6402540be40081565b6060612718848484613bb4565b60006127b66132ef83613897565b61209084613432565b613361846323b872dd60e01b8585856040516001600160a01b0393841660248201529190921660448201526064810191909152608401604051602081830303815290604052906001600160e01b0319166020820180516001600160e01b0316909117905261434b565b50505050565b6000828211156133c65760405162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f7700006044820152606401604051809103906133c3614e0a565b50505b50900390565b6000828201838110156127b35760405162461bcd60e51b815260206004820152601b60248201527a536166654d6174683a206164646974696f6e206f766572666c6f7760281b604482015260640160405180910390613429614e0a565b50509392505050565b60008082600301613441614e75565b9050600083600101613451614e75565b9050815a61345d6151fe565b10156134f557600084600201613471614e75565b905060008561347e614e75565b9050808311156134c4576134b96134b26134988685613367565b610fd66134a8865a6124aa6151fe565b6111fb8887613367565b82906133cc565b945050505050612235565b6134b96134ee6134d48685613367565b610fd66134e4865a6124aa6151fe565b6111fb8689613367565b8290613367565b91506122359050565b60606127b3828460090180613511614e75565b806020026020016040519081016040528181529190602083018280613534614e75565b801561356657602002820191906000526020600020905b81613554614e75565b8152602001906001019080831161354b575b5050505050614412565b600080835190506000805b828110156135ab576135a186828151811061359257fe5b602002602001015183906133cc565b915060010161357b565b50806135bc576000925050506127b6565b600081816135ca87866137cc565b905060005b610100811015613734578260005b8781101561360c576136026135f8898d84815181106110fd57fe5b610fd684886137cc565b91506001016135dd565b50929350839261365a61363a613627836111fb8b60016133cc565b610ebe6064610fd6896111fb8a84613367565b610fd6866111fb61364b868d6137cc565b610ebe6064610fd68b8f6137cc565b93507330e344c8f517becaeed04245ced2e7301f06f21b6313a1ec7585876040516001600160e01b031960e085901b1681526004810192909252602482015260440160206040518083038186806136af614ef8565b1580156136c4576000806136c1614e0a565b50505b505a6136ce615075565b50505050501580156136ed573d6000803e3d60006136ea614e0a565b50505b505050506040513d602081101561370c57600080613709614e0a565b50505b81019080805115925061372b91505057839750505050505050506127b6565b506001016135cf565b5060405162461bcd60e51b81526020600482015260136024820152724420646f6573206e6f7420636f6e766572676560681b60448201526064016040518091039061377d614e0a565b5050505050505092915050565b60006127b66137ae60046111fb600186600801806137a6614e75565b919050613367565b610fd684600801806137be614e75565b9050856004016111fb614e75565b6000826137db575060006127b6565b828202828482816137e857fe5b04146127b35760405162461bcd60e51b81526004018080602001828103825260218152602001806152b96021913960400191505060405180910390613429614e0a565b60008082116138865760405162461bcd60e51b815260206004820152601a602482015279536166654d6174683a206469766973696f6e206279207a65726f60301b604482015260640160405180910390613883614e0a565b50505b81838161388f57fe5b049392505050565b60606127b682600a01806138a9614e75565b8060200260200160405190810160405281815291906020830182806138cc614e75565b80156138fe57602002820191906000526020600020905b816138ec614e75565b815260200190600101908083116138e3575b50505050508360090180613910614e75565b806020026020016040519081016040528181529190602083018280613933614e75565b801561356657602002820191906000526020600020905b81613953614e75565b8152602001906001019080831161394a575050505050614412565b600081848411156133c35760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156139c25780820151838201526020016139aa565b50505050905090810190601f1680156139ef5780820380516001836020036101000a031916815260200191505b509250505060405180910390613a03614e0a565b5050505050900390565b6132c68363a9059cbb60e01b84846040516001600160a01b0390921660248301526044820152606401604051602081830303815290604052906001600160e01b0319166020820180516001600160e01b0316909117905261434b565b6000806060613a7787613897565b905080518660ff16108015613a8f575080518560ff16105b613ad65760405162461bcd60e51b815260206004820152601860248201526000805160206152fa833981519152604482015260640160405180910390613ad3614e0a565b50505b6000613b27828860ff1681518110613aea57fe5b6020026020010151610ebe8a6009018a60ff1681613b06614e75565b8110613b0e57fe5b90600052602060002001613b20614e75565b88906137cc565b90506000613b38898989858761451b565b9050613b5260016124aa83868b60ff168151811061111757fe5b9450613b6c6402540be400610fd68b600401613b20614e75565b9350613ba6896009018860ff1681613b82614e75565b8110613b8a57fe5b90600052602060002001613b9c614e75565b610fd68787613367565b945050505094509492505050565b606060008060078601613bc5614e75565b906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b81526004016020604051808303818680613c08614ef8565b158015613c1d57600080613c1a614e0a565b50505b505a613c27614f44565b5050505050158015613c46573d6000803e3d6000613c43614e0a565b50505b505050506040513d6020811015613c6557600080613c62614e0a565b50505b810190808051935050505080831115613cca5760405162461bcd60e51b815260206004820152601a60248201527943616e6e6f742065786365656420746f74616c20737570706c7960301b604482015260640160405180910390613cc7614e0a565b50505b6000613ce56402540be400610fd6612c5b611ac28a8a6127bc565b905060608660080180613cf6614e75565b90506001600160401b0381118015613d1657600080613d13614e0a565b50505b50604051908082528060200260200182016040528015613d40578160200160208202803683370190505b50905060005b8760080180613d53614e75565b9050811015613dab57613d8c84610fd6858b600a018581613d72614e75565b8110613d7a57fe5b906000526020600020016111fb614e75565b828281518110613d9857fe5b6020908102919091010152600101613d46565b509695505050505050565b60006127b66064610fd684613432565b6000808460080180613dd6614e75565b90508460ff1610613e245760405162461bcd60e51b815260206004820152601860248201526000805160206152fa833981519152604482015260640160405180910390613e21614e0a565b50505b6060613e2f86613897565b9050613e39615244565b6040518060a001604052806000815260200160008152602001600081526020016000815260200160008152509050613e7087613432565b60808201908152613e8390839051613570565b8152613f59613f51600060078a01613e99614e75565b906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b81526004016020604051808303818680613edc614ef8565b158015613ef157600080613eee614e0a565b50505b505a613efb614f44565b5050505050158015613f1a573d6000803e3d6000613f17614e0a565b50505b505050506040513d6020811015613f3957600080613f36614e0a565b50505b8101908080519250610fd691508590505189906137cc565b825190613367565b60208201528160ff871681518110613f6d57fe5b6020026020010151851115613fce5760405162461bcd60e51b815260206004820152601a6024820152795769746864726177206578636565647320617661696c61626c6560301b604482015260640160405180910390613fcb614e0a565b50505b613fe2816080015187848460200151614840565b6040820152606082516001600160401b038111801561400957600080614006614e0a565b50505b50604051908082528060200260200182016040528015614033578160200160208202803683370190505b50905061403f8861378a565b606083015260005b8860080180614054614e75565b90508110156140f257600084828151811061406b57fe5b602002602001015190506140d26134ee6402540be400610fd687606001518d60ff1687146140b6576140b16140aa8a51610fd68c602001518a906137cc565b8790613367565b6111fb565b6111fb89604001516124aa8b51610fd68d602001518b906137cc565b8383815181106140de57fe5b602090810291909101015250600101614047565b50600061411b61410c84608001518a858760200151614840565b838a60ff168151811061111757fe5b9050614156896009018960ff1681614131614e75565b811061413957fe5b9060005260206000200161414b614e75565b610fd6836001613367565b90508083604001519550955050505050935093915050565b6001600160a01b038216614181576132c6565b8260060161418d614e75565b6141c4576001600160a01b0382166000908152600c84016020526402540be400906040902081906141bc615086565b505050614316565b60006141d084846127bc565b9050600080600786016141e1614e75565b906101000a90046001600160a01b03166001600160a01b03166370a08231856040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381868061423a614ef8565b15801561424f5760008061424c614e0a565b50505b505a614259614f44565b5050505050158015614278573d6000803e3d6000614275614e0a565b50505b505050506040513d602081101561429757600080614294614e0a565b50505b81019080805193506142ec92506142c0915050600687016142b6614e75565b6111fb86856133cc565b610fd66402540be4006111fb6142e28a6006016142db614e75565b89906137cc565b610ebe87896137cc565b6001600160a01b0385166000908152600c8701602052604090208190614310615086565b50505050505b5a61431f6151fe565b6001600160a01b0383166000908152600b8501602052604090208190614343615086565b505050505050565b6060614396826040516040808201905260208082527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564908201526001600160a01b0386169190614aa4565b90506000815111156132c65760208101815160208110156143bf576000806143bc614e0a565b50505b81019080805192506132c69150505760405162461bcd60e51b815260040180806020018281038252602a81526020018061531a602a91396040019150506040518091039061440b614e0a565b5050505050565b6060600083519050825181146144775760405162461bcd60e51b815260206004820152601f60248201527f42616c616e636573206d757374206d61746368206d756c7469706c6965727300604482015260640160405180910390614474614e0a565b50505b6060816001600160401b038111801561449857600080614495614e0a565b50505b506040519080825280602002602001820160405280156144c2578160200160208202803683370190505b50905060005b82811015614512576144f38582815181106144df57fe5b60200260200101518783815181106110fd57fe5b8282815181106144ff57fe5b60209081029190910101526001016144c8565b50949350505050565b600080866008018061452b614e75565b905090508460ff168660ff1614156145925760405162461bcd60e51b815260206004820152601d60248201527f43616e277420636f6d7061726520746f6b656e20746f20697473656c6600000060448201526064016040518091039061458f614e0a565b50505b808660ff161080156145a65750808560ff16105b6145f85760405162461bcd60e51b8152602060048201526016602482015275151bdad95b9cc81b5d5cdd081899481a5b881c1bdbdb60521b6044820152606401604051809103906145f5614e0a565b50505b600061460388613432565b905060006146118583613570565b90508060008061462186866137cc565b90506000805b8781101561469a578c60ff16811415614642578a915061466c565b8b60ff1681146146675789818151811061465857fe5b6020026020010151915061466c565b614692565b61467684836133cc565b935061468f614685838a6137cc565b610fd687896137cc565b94505b600101614627565b506146b76146a883896137cc565b610fd660646111fb888a6137cc565b935060006146d46146cd84610fd68960646137cc565b85906133cc565b9050600086815b6101008110156147ea57909150819061470e6147008a6124aa87610ebe8760026137cc565b610fd68a610ebe86806137cc565b91507330e344c8f517becaeed04245ced2e7301f06f21b6313a1ec7583856040516001600160e01b031960e085901b168152600481019290925260248201526044016020604051808303818680614763614ef8565b15801561477857600080614775614e0a565b50505b505a614782615075565b50505050501580156147a1573d6000803e3d600061479e614e0a565b50505b505050506040513d60208110156147c0576000806147bd614e0a565b50505b8101908080511592506147e29150505750995061206f98505050505050505050565b6001016146db565b5060405162461bcd60e51b815260206004820152601e60248201526000805160206152da83398151915260448201526064016040518091039061482b614e0a565b50505050505050505050505095945050505050565b60008083519050808560ff16106148985760405162461bcd60e51b815260206004820152600f60248201526e151bdad95b881b9bdd08199bdd5b99608a1b604482015260640160405180910390614895614e0a565b50505b826000806148a689856137cc565b905060005b84811015614905578860ff1681146148fd576148db8882815181106148cc57fe5b602002602001015184906133cc565b92506148fa6148f0868a84815181106110fd57fe5b610fd6868a6137cc565b93505b6001016148ab565b5061492261491382866137cc565b610fd660646111fb878b6137cc565b9250600061493f61493883610fd68a60646137cc565b84906133cc565b9050600087815b610100811015614a5257909150819061497961496b8b6124aa87610ebe8760026137cc565b610fd689610ebe86806137cc565b91507330e344c8f517becaeed04245ced2e7301f06f21b6313a1ec7583856040516001600160e01b031960e085901b1681526004810192909252602482015260440160206040518083038186806149ce614ef8565b1580156149e3576000806149e0614e0a565b50505b505a6149ed615075565b5050505050158015614a0c573d6000803e3d6000614a09614e0a565b50505b505050506040513d6020811015614a2b57600080614a28614e0a565b50505b810190808051159250614a4a9150505750965061271895505050505050565b600101614946565b5060405162461bcd60e51b815260206004820152601e60248201526000805160206152da833981519152604482015260640160405180910390614a93614e0a565b505050505050505050949350505050565b60606127188484600085606082614b125a63996d79a5598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051925060005b6040811015614b0a57600082820152602001614af3565b505050614c88565b1015614b585760405162461bcd60e51b81526004018080602001828103825260268152602001806152936026913960400191505060405180910390614b55614e0a565b50505b614b6185614d3e565b614bba5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640160405180910390614bb7614e0a565b50505b60006060866001600160a01b031685876040518082805190602001908083835b60208310614bf95780518252601f199092019160209182019101614bda565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875a614c32615138565b505050505050925050503d8060008114614c68576040513d603f01601f191681016040523d815291503d6000602084013e614c6d565b606091505b5091509150614c7d828286614d53565b979650505050505050565b60006006602160991b016370a08231836040516001600160e01b031960e084901b1681526001600160a01b0390911660048201526024016020604051808303818680614cd2614ef8565b158015614ce757600080614ce4614e0a565b50505b505a614cf1614f44565b5050505050158015614d10573d6000803e3d6000614d0d614e0a565b50505b505050506040513d6020811015614d2f57600080614d2c614e0a565b50505b81019080805195945050505050565b60008082614d4a614ef8565b15159392505050565b60608315614d6257508161140a565b600083511115614d835782518084602001614d7b614e0a565b50505061140a565b8160405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015614dcd578082015183820152602001614db5565b50505050905090810190601f168015614dfa5780820380516001836020036101000a031916815260200191505b5092505050604051809103906134295b632a2a7adb598160e01b8152600481016020815285602082015260005b86811015614e42578086015182820160400152602001614e27565b506020828760640184336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b505050565b6303daa959598160e01b8152836004820152602081602483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051935060005b60408110156132c657600082820152602001614eb9565b6040518060800160405280600081526020016000815260200160008152602001600081525090565b638435035b598160e01b8152836004820152602081602483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b80516000825293506020614eb9565b638540661f598160e01b8152614f75565b80808311156127b6575090919050565b80808310156127b6575090919050565b836004820152846024820152606060448201528660648201526084810160005b88811015614fad578088015182820152602001614f95565b506060828960a40184336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b815160408301513d6000853e8b8b82606087013350600060045af150596150028d3d614f65565b8c0161500e8187614f55565b5b82811015615023576000815260200161500f565b50929c50505050505050565b6373509064598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b80516000825293506020614eb9565b63ffe73914598160e01b8152614f75565b6322bd64c0598160e01b8152836004820152846024820152600081604483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b600081526020614eb9565b82806150de614e75565b82826150e8615086565b5050906000526020600020908101928215615128579160200282015b8281111561512857825182615117615086565b505091602001919060010190615104565b50615134929150615273565b5090565b6385979f76598160e01b8152836004820152846024820152606060448201528760648201526084810160005b8981101561517c578089015182820152602001615164565b506060828a60a40184336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b815160408301513d6000853e8c8c82606087013350600060045af150596151d18e3d614f65565b8d016151dd8187614f55565b5b828110156151f257600081526020016151de565b50929d50505050505050565b63bdbf8c36598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b80516000825293506020614eb9565b6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b808211156151345760008082615287615086565b50505060010161527356fe416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77417070726f78696d6174696f6e20646964206e6f7420636f6e76657267650000546f6b656e20696e646578206f7574206f662072616e676500000000000000005361666545524332303a204552433230206f7065726174696f6e20646964206e6f74207375636365656443616e6e6f74207769746864726177206d6f7265207468616e20617661696c61626c65
Deployed Bytecode
0x730000000000000000000000000000000000000000301460806040526004361061011c5760003560e01c80630296ab5014610121578063396984151461013f57806340370edf1461015957806341b91c26146102105780634b23603c146102c75780635e58579c146102fd57806371906c2c1461038257806373fd6b3e1461039f5780637d04816014610476578063968e559414610499578063a5397b22146104c5578063ab3d85441461050e578063b0a14cfc14610516578063c9b64dcb14610533578063d011f91814610550578063d4b0de2f1461050e578063e4a9a0da14610558578063e7a4db81146105ac578063e8c47a54146105eb578063e9252d4614610617578063f3de036214610658578063f967a81f14610660575b600080fd5b610129610692565b6040805160ff9092168252519081900360200190f35b610147610697565b60408051918252519081900360200190f35b81801561016557600080fd5b506101476004803603606081101561017c57600080fd5b81359190810190604081016020820135600160201b81111561019d57600080fd5b8201836020820111156101af57600080fd5b803590602001918460208302840111600160201b831117156101d057600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550509135925061069e915050565b81801561021c57600080fd5b506101476004803603606081101561023357600080fd5b81359190810190604081016020820135600160201b81111561025457600080fd5b82018360208201111561026657600080fd5b803590602001918460208302840111600160201b8311171561028757600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505091359250610f57915050565b610147600480360360808110156102dd57600080fd5b5080359060ff6020820135811691604081013590911690606001356116ea565b6101476004803603608081101561031357600080fd5b8135916001600160a01b0360208201351691810190606081016040820135600160201b81111561034257600080fd5b82018360208201111561035457600080fd5b803590602001918460208302840111600160201b8311171561037557600080fd5b9193509150351515611702565b6101476004803603602081101561039857600080fd5b5035611985565b8180156103ab57600080fd5b50610426600480360360608110156103c257600080fd5b813591602081013591810190606081016040820135600160201b8111156103e857600080fd5b8201836020820111156103fa57600080fd5b803590602001918460208302840111600160201b8311171561041b57600080fd5b509092509050611acf565b60408051602080825283518183015283519192839290830191858101910280838360005b8381101561046257818101518382015260200161044a565b505050509050019250505060405180910390f35b6101476004803603604081101561048c57600080fd5b5080359060200135611e7c565b610147600480360360408110156104af57600080fd5b50803590602001356001600160a01b0316611efa565b8180156104d157600080fd5b50610147600480360360a08110156104e857600080fd5b5080359060ff602082013581169160408101359091169060608101359060800135611fa1565b6101476122e3565b6101476004803603602081101561052c57600080fd5b50356122eb565b6101476004803603602081101561054957600080fd5b50356122f6565b610147612301565b6105936004803603608081101561056e57600080fd5b5080359060208101356001600160a01b0316906040810135906060013560ff16612306565b6040805192835260208301919091528051918290030190f35b8180156105b857600080fd5b50610147600480360360808110156105cf57600080fd5b5080359060208101359060ff6040820135169060600135612390565b6101476004803603604081101561060157600080fd5b50803590602001356001600160a01b03166126ac565b81801561062357600080fd5b506106566004803603606081101561063a57600080fd5b508035906001600160a01b0360208201351690604001356126cb565b005b6101476126db565b6104266004803603606081101561067657600080fd5b508035906001600160a01b0360208201351690604001356126e4565b601281565b620f424081565b60088301548251600091146106fa576040805162461bcd60e51b815260206004820181905260248201527f416d6f756e7473206d757374206d6174636820706f6f6c656420746f6b656e73604482015290519081900360640190fd5b60088401546060906001600160401b038111801561071757600080fd5b50604051908082528060200260200182016040528015610741578160200160208202803683370190505b50905061074c613da1565b60405180608001604052806000815260200160008152602001600081526020016000815250905060008660070160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156107c557600080fd5b505afa1580156107d9573d6000803e3d6000fd5b505050506040513d60208110156107ef57600080fd5b50519050801561080557610802876126f1565b82525b606087600a0180548060200260200160405190810160405280929190818152602001828054801561085557602002820191906000526020600020905b815481526020019060010190808311610841575b5050505050905060005b6008890154811015610add578215158061088c5750600088828151811061088257fe5b6020026020010151115b6108dd576040805162461bcd60e51b815260206004820152601e60248201527f4d75737420737570706c7920616c6c20746f6b656e7320696e20706f6f6c0000604482015290519081900360640190fd5b8781815181106108e957fe5b6020026020010151600014610a7f57600089600801828154811061090957fe5b60009182526020918290200154604080516370a0823160e01b815230600482015290516001600160a01b03909216926370a0823192602480840193829003018186803b15801561095857600080fd5b505afa15801561096c573d6000803e3d6000fd5b505050506040513d602081101561098257600080fd5b505189519091506109d190339030908c908690811061099d57fe5b60200260200101518d60080186815481106109b457fe5b6000918252602090912001546001600160a01b0316929190612708565b610a65818b60080184815481106109e457fe5b60009182526020918290200154604080516370a0823160e01b815230600482015290516001600160a01b03909216926370a0823192602480840193829003018186803b158015610a3357600080fd5b505afa158015610a47573d6000803e3d6000fd5b505050506040513d6020811015610a5d57600080fd5b505190612768565b898381518110610a7157fe5b602002602001018181525050505b610abe888281518110610a8e57fe5b60200260200101518a600a018381548110610aa557fe5b90600052602060002001546127c590919063ffffffff16565b828281518110610aca57fe5b602090810291909101015260010161085f565b50610ae78861281d565b6060840152610b03610af989836128b2565b8460600151612910565b60208401819052835110610b52576040805162461bcd60e51b8152602060048201526011602482015270442073686f756c6420696e63726561736560781b604482015290519081900360640190fd5b602083015160408401528115610d5e576000610b6d89612aed565b905060005b60088a0154811015610d3b576000610bbe8660000151610bb88d600a018581548110610b9a57fe5b90600052602060002001548960200151612b1a90919063ffffffff16565b90612b73565b9050610c6b6402540be400610bb8837330e344c8f517becaeed04245ced2e7301f06f21b63a95cb7f79091898881518110610bf557fe5b60200260200101516040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b158015610c3857600080fd5b505af4158015610c4c573d6000803e3d6000fd5b505050506040513d6020811015610c6257600080fd5b50518690612b1a565b878381518110610c7757fe5b602002602001018181525050610cdb610cb96402540be400610bb88e600501548b8781518110610ca357fe5b6020026020010151612b1a90919063ffffffff16565b858481518110610cc557fe5b602002602001015161276890919063ffffffff16565b8b600a018381548110610cea57fe5b9060005260206000200181905550610d1b878381518110610d0757fe5b6020026020010151858481518110610cc557fe5b848381518110610d2757fe5b602090810291909101015250600101610b72565b50610d53610d498a846128b2565b8560600151612910565b604085015250610d75565b8051610d7390600a8a01906020840190613dc9565b505b600082610d8757506020830151610dac565b83516040850151610da99190610bb8908690610da39084612768565b90612b1a565b90505b86811015610dff576040805162461bcd60e51b815260206004820152601b60248201527a10dbdd5b191b89dd081b5a5b9d081b5a5b881c995c5d595cdd1959602a1b604482015290519081900360640190fd5b6007890154604080516340c10f1960e01b81523360048201526024810184905290516001600160a01b03909216916340c10f199160448082019260009290919082900301818387803b158015610e5457600080fd5b505af1158015610e68573d6000803e3d6000fd5b50505060208501513391507f189c623b666b1b45b83d7178f39b8c087cb09774317ca2f53c2d3c3726f222a2908a908890610ea388876127c5565b604051808060200180602001858152602001848152602001838103835287818151815260200191508051906020019060200280838360005b83811015610ef3578181015183820152602001610edb565b50505050905001838103825286818151815260200191508051906020019060200280838360005b83811015610f32578181015183820152602001610f1a565b50505050905001965050505050505060405180910390a29450505050505b9392505050565b6008830154825160009114610fb3576040805162461bcd60e51b815260206004820181905260248201527f416d6f756e74732073686f756c64206d6174636820706f6f6c20746f6b656e73604482015290519081900360640190fd5b6007840154604080516370a0823160e01b815233600482015290516001600160a01b03909216916370a0823191602480820192602092909190829003018186803b15801561100057600080fd5b505afa158015611014573d6000803e3d6000fd5b505050506040513d602081101561102a57600080fd5b5051821180159061103a57508115155b61107b576040805162461bcd60e51b815260206004820152600d60248201526c1f2628173130b630b731b2a7b360991b604482015290519081900360640190fd5b611083613da1565b60405180608001604052806000815260200160008152602001600081526020016000815250905060008560070160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156110fc57600080fd5b505afa158015611110573d6000803e3d6000fd5b505050506040513d602081101561112657600080fd5b50519050600061113587612aed565b9050606087600a0180548060200260200160405190810160405280929190818152602001828054801561118757602002820191906000526020600020905b815481526020019060010190808311611173575b505050505090506111978861281d565b60608501526111a8610d4989612bd7565b845260005b600889015481101561122b5761120c8882815181106111c857fe5b6020026020010151604051806060016040528060238152602001613eea602391398484815181106111f557fe5b6020026020010151612c859092919063ffffffff16565b82828151811061121857fe5b60209081029190910101526001016111ad565b50611239610d4989836128b2565b602085015260088801546060906001600160401b038111801561125b57600080fd5b50604051908082528060200260200182016040528015611285578160200160208202803683370190505b50905060005b60088a01548110156114275760006112d18760000151610bb88d600a0185815481106112b357fe5b90600052602060002001548a60200151612b1a90919063ffffffff16565b90506000817330e344c8f517becaeed04245ced2e7301f06f21b63a95cb7f790918786815181106112fe57fe5b60200260200101516040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b15801561134157600080fd5b505af4158015611355573d6000803e3d6000fd5b505050506040513d602081101561136b57600080fd5b505190506113826402540be400610bb88884612b1a565b84848151811061138e57fe5b6020026020010181815250506113c66113ba6402540be400610bb88f60050154888881518110610ca357fe5b868581518110610cc557fe5b8c600a0184815481106113d557fe5b90600052602060002001819055506114068484815181106113f257fe5b6020026020010151868581518110610cc557fe5b85848151811061141257fe5b6020908102919091010152505060010161128b565b5061143f6114358a846128b2565b8660600151612910565b6040860181905285516000916114619190610bb8908890610da3908490612768565b9050806114b3576040805162461bcd60e51b815260206004820152601b60248201527a4275726e7420616d6f756e742063616e6e6f74206265207a65726f60281b604482015290519081900360640190fd5b6114e36114cf6114c38c33611efa565b6402540be40090612768565b610bb86402540be400610da38560016127c5565b905087811115611538576040805162461bcd60e51b815260206004820152601b60248201527a1d1bdad95b905b5bdd5b9d080f881b585e109d5c9b905b5bdd5b9d602a1b604482015290519081900360640190fd5b60078a01546040805163079cc67960e41b81523360048201526024810184905290516001600160a01b03909216916379cc67909160448082019260009290919082900301818387803b15801561158d57600080fd5b505af11580156115a1573d6000803e3d6000fd5b5050505060005b60088b01548110156115ff576115f7338b83815181106115c457fe5b60200260200101518d60080184815481106115db57fe5b6000918252602090912001546001600160a01b03169190612d1c565b6001016115a8565b50602086015133907f3631c28b1f9dd213e0319fb167b554d76b6c283a41143eb400a0d1adb1af1755908b9085906116378a87612768565b604051808060200180602001858152602001848152602001838103835287818151815260200191508051906020019060200280838360005b8381101561168757818101518382015260200161166f565b50505050905001838103825286818151815260200191508051906020019060200280838360005b838110156116c65781810151838201526020016116ae565b50505050905001965050505050505060405180910390a29998505050505050505050565b60006116f885858585612d6e565b5095945050505050565b6008850154600090816117148861281d565b9050600061177d6117778a8b600a0180548060200260200160405190810160405280929190818152602001828054801561176d57602002820191906000526020600020905b815481526020019060010190808311611759575b50505050506128b2565b83612910565b9050606089600a018054806020026020016040519081016040528092919081815260200182805480156117cf57602002820191906000526020600020905b8154815260200190600101908083116117bb575b5050505050905060005b8481101561189857861561183c5761181f8989838181106117f657fe5b9050602002013583838151811061180957fe5b60200260200101516127c590919063ffffffff16565b82828151811061182b57fe5b602002602001018181525050611890565b61187789898381811061184b57fe5b90506020020135604051806060016040528060238152602001613eea602391398484815181106111f557fe5b82828151811061188357fe5b6020026020010181815250505b6001016117d9565b5060006118ae6118a88c846128b2565b85612910565b905060008b60070160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561190257600080fd5b505afa158015611916573d6000803e3d6000fd5b505050506040513d602081101561192c57600080fd5b5051905087156119555761194884610bb883610da38684612768565b965050505050505061197c565b6119486119656114c38e8e611efa565b610bb86402540be400610da388838783838b612768565b95945050505050565b6000806119a261199484612bd7565b61199d8561281d565b612910565b905060008360070160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156119f657600080fd5b505afa158015611a0a573d6000803e3d6000fd5b505050506040513d6020811015611a2057600080fd5b505190508015611ac357611aba81610bb88660070160009054906101000a90046001600160a01b03166001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015611a8157600080fd5b505afa158015611a95573d6000803e3d6000fd5b505050506040513d6020811015611aab57600080fd5b5051859060ff16600a0a612b1a565b92505050611aca565b6000925050505b919050565b6007840154604080516370a0823160e01b815233600482015290516060926001600160a01b0316916370a08231916024808301926020929190829003018186803b158015611b1c57600080fd5b505afa158015611b30573d6000803e3d6000fd5b505050506040513d6020811015611b4657600080fd5b5051841115611b8c576040805162461bcd60e51b815260206004820152600d60248201526c1f2628173130b630b731b2a7b360991b604482015290519081900360640190fd5b60088501548214611be4576040805162461bcd60e51b815260206004820181905260248201527f6d696e416d6f756e7473206d757374206d6174636820706f6f6c546f6b656e73604482015290519081900360640190fd5b6060611bf1863387612ea6565b905060005b8151811015611d0157848482818110611c0b57fe5b90506020020135828281518110611c1e57fe5b60200260200101511015611c76576040805162461bcd60e51b815260206004820152601a602482015279616d6f756e74735b695d203c206d696e416d6f756e74735b695d60301b604482015290519081900360640190fd5b611cb5828281518110611c8557fe5b602002602001015188600a018381548110611c9c57fe5b906000526020600020015461276890919063ffffffff16565b87600a018281548110611cc457fe5b9060005260206000200181905550611cf933838381518110611ce257fe5b60200260200101518960080184815481106115db57fe5b600101611bf6565b5060078601546040805163079cc67960e41b81523360048201526024810188905290516001600160a01b03909216916379cc67909160448082019260009290919082900301818387803b158015611d5757600080fd5b505af1158015611d6b573d6000803e3d6000fd5b50505050336001600160a01b03167f88d38ed598fdd809c2bf01ee49cd24b7fdabf379a83d29567952b60324d58cef828860070160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015611deb57600080fd5b505afa158015611dff573d6000803e3d6000fd5b505050506040513d6020811015611e1557600080fd5b50516040805160208181018490528282528451928201929092528351909182916060830191868101910280838360005b83811015611e5d578181015183820152602001611e45565b50505050905001935050505060405180910390a290505b949350505050565b60088201546000908210611ec5576040805162461bcd60e51b81526020600482015260186024820152600080516020613ea0833981519152604482015290519081900360640190fd5b611ef183600a018381548110611ed757fe5b90600052602060002001548460080184815481106109e457fe5b90505b92915050565b6001600160a01b0381166000908152600b830160205260408120548190611f24906224ea006127c5565b905042811115611f97576000611f3a8242612768565b9050611f8e6402540be400610bb86224ea00610bb885610da38b600c0160008c6001600160a01b03166001600160a01b03168152602001908152602001600020548c60060154612b1a90919063ffffffff16565b92505050611ef4565b5060009392505050565b6000856008018560ff1681548110611fb557fe5b60009182526020918290200154604080516370a0823160e01b815233600482015290516001600160a01b03909216926370a0823192602480840193829003018186803b15801561200457600080fd5b505afa158015612018573d6000803e3d6000fd5b505050506040513d602081101561202e57600080fd5b5051831115612084576040805162461bcd60e51b815260206004820152601d60248201527f43616e6e6f742073776170206d6f7265207468616e20796f75206f776e000000604482015290519081900360640190fd5b6000866008018660ff168154811061209857fe5b60009182526020918290200154604080516370a0823160e01b815230600482015290516001600160a01b03909216926370a0823192602480840193829003018186803b1580156120e757600080fd5b505afa1580156120fb573d6000803e3d6000fd5b505050506040513d602081101561211157600080fd5b5051600888018054919250612134913391309188919060ff8c169081106109b457fe5b600061214c82896008018960ff16815481106109e457fe5b905060008061215d8a8a8a86612d6e565b91509150858210156121b6576040805162461bcd60e51b815260206004820181905260248201527f53776170206469646e277420726573756c7420696e206d696e20746f6b656e73604482015290519081900360640190fd5b60006121f88b6009018a60ff16815481106121cd57fe5b9060005260206000200154610bb86402540be400610bb88f6005015487612b1a90919063ffffffff16565b9050612210848c600a018c60ff1681548110610aa557fe5b8b600a018b60ff168154811061222257fe5b90600052602060002001819055506122508161224a858e600a018d60ff1681548110611c9c57fe5b90612768565b8b600a018a60ff168154811061226257fe5b906000526020600020018190555061228733848d6008018c60ff16815481106115db57fe5b604080518581526020810185905260ff808d16828401528b166060820152905133917fc6c1e0630dbe9130cc068028486c0d118ddcea348550819defd5cb8c257f8a38919081900360800190a250909998505050505050505050565b6305f5e10081565b6000611ef482613052565b6000611ef48261281d565b606481565b600080600080612317888688613062565b809250819350505060006123608361224a8b6009018960ff168154811061233a57fe5b9060005260206000200154610bb8866123528f612bd7565b8c60ff1681518110610cc557fe5b90506123826402540be400610bb861237b6114c38d8d611efa565b8690612b1a565b999098509650505050505050565b6000808560070160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156123e357600080fd5b505afa1580156123f7573d6000803e3d6000fd5b505050506040513d602081101561240d57600080fd5b505160088701546007880154604080516370a0823160e01b8152336004820152905193945091926001600160a01b03909116916370a08231916024808301926020929190829003018186803b15801561246557600080fd5b505afa158015612479573d6000803e3d6000fd5b505050506040513d602081101561248f57600080fd5b50518611156124d5576040805162461bcd60e51b815260206004820152600d60248201526c1f2628173130b630b731b2a7b360991b604482015290519081900360640190fd5b808560ff161061251e576040805162461bcd60e51b815260206004820152600f60248201526e151bdad95b881b9bdd08199bdd5b99608a1b604482015290519081900360640190fd5b60008061252d89338a8a612306565b9250905085811015612577576040805162461bcd60e51b815260206004820152600e60248201526d191e480f081b5a5b905b5bdd5b9d60921b604482015290519081900360640190fd5b6125b66125a461259d6402540be400610bb88d6005015487612b1a90919063ffffffff16565b83906127c5565b8a600a018960ff1681548110611c9c57fe5b89600a018860ff16815481106125c857fe5b600091825260208220019190915560078a01546040805163079cc67960e41b8152336004820152602481018c905290516001600160a01b03909216926379cc67909260448084019382900301818387803b15801561262557600080fd5b505af1158015612639573d6000803e3d6000fd5b5050505061265433828b6008018a60ff16815481106115db57fe5b604080518981526020810186905260ff89168183015260608101839052905133917f43fb02998f4e03da2e0e6fff53fdbf0c40a9f45f145dc377fc30615d7d7a8a64919081900360800190a298975050505050505050565b6001600160a01b03166000908152600b91909101602052604090205490565b6126d683838361339d565b505050565b6402540be40081565b6060611e74848484612ea6565b6000611ef46126ff83612bd7565b61199d8461281d565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b1790526127629085906134f3565b50505050565b6000828211156127bf576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b600082820183811015611ef1576040805162461bcd60e51b815260206004820152601b60248201527a536166654d6174683a206164646974696f6e206f766572666c6f7760281b604482015290519081900360640190fd5b6003810154600182015460009190428211156128a957600284015484548083111561287b576128706128696128528685612768565b610bb861285f4287612768565b610da38887612768565b82906127c5565b945050505050611aca565b6128706128a261288b8685612768565b610bb86128984287612768565b610da38689612768565b8290612768565b9150611aca9050565b6060611ef1828460090180548060200260200160405190810160405280929190818152602001828054801561290657602002820191906000526020600020905b8154815260200190600101908083116128f2575b50505050506135a4565b815160009081805b828110156129505761294686828151811061292f57fe5b6020026020010151836127c590919063ffffffff16565b9150600101612918565b508061296157600092505050611ef4565b6000818161296f8786612b1a565b905060005b610100811015612aa9578260005b878110156129b1576129a761299d898d8481518110610ca357fe5b610bb88488612b1a565b9150600101612982565b509293508392612a056129e56129cc83610da38b60016127c5565b6129df6064610bb889610da38a84612768565b906127c5565b610bb886610da36129f6868d612b1a565b6129df6064610bb88b8f612b1a565b9350837330e344c8f517becaeed04245ced2e7301f06f21b6313a1ec759091876040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b158015612a6057600080fd5b505af4158015612a74573d6000803e3d6000fd5b505050506040513d6020811015612a8a57600080fd5b505115612aa05783975050505050505050611ef4565b50600101612974565b506040805162461bcd60e51b81526020600482015260136024820152724420646f6573206e6f7420636f6e766572676560681b604482015290519081900360640190fd5b6008810154600090611ef490612b0b90600490610da3906001612768565b60088401546004850154610bb8915b600082612b2957506000611ef4565b82820282848281612b3657fe5b0414611ef15760405162461bcd60e51b8152600401808060200182810382526021815260200180613e7f6021913960400191505060405180910390fd5b6000808211612bc6576040805162461bcd60e51b815260206004820152601a602482015279536166654d6174683a206469766973696f6e206279207a65726f60301b604482015290519081900360640190fd5b818381612bcf57fe5b049392505050565b6060611ef482600a01805480602002602001604051908101604052809291908181526020018280548015612c2a57602002820191906000526020600020905b815481526020019060010190808311612c16575b50505050508360090180548060200260200160405190810160405280929190818152602001828054801561290657602002820191906000526020600020908154815260200190600101908083116128f25750505050506135a4565b60008184841115612d145760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015612cd9578181015183820152602001612cc1565b50505050905090810190601f168015612d065780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526126d69084906134f3565b6000806060612d7c87612bd7565b905080518660ff16108015612d94575080518560ff16105b612dd3576040805162461bcd60e51b81526020600482015260186024820152600080516020613ea0833981519152604482015290519081900360640190fd5b6000612e1e828860ff1681518110612de757fe5b60200260200101516129df8a6009018a60ff1681548110612e0457fe5b906000526020600020015488612b1a90919063ffffffff16565b90506000612e2f898989858761369a565b9050612e49600161224a83868b60ff1681518110610cc557fe5b9450612e6b6402540be400610bb88b6004015488612b1a90919063ffffffff16565b9350612e98896009018860ff1681548110612e8257fe5b600091825260209091200154610bb88787612768565b945050505094509492505050565b606060008460070160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015612efa57600080fd5b505afa158015612f0e573d6000803e3d6000fd5b505050506040513d6020811015612f2457600080fd5b5051905080831115612f7a576040805162461bcd60e51b815260206004820152601a60248201527943616e6e6f742065786365656420746f74616c20737570706c7960301b604482015290519081900360640190fd5b6000612f9c6402540be400610bb8612f956114c38a8a611efa565b8790612b1a565b60088701549091506060906001600160401b0381118015612fbc57600080fd5b50604051908082528060200260200182016040528015612fe6578160200160208202803683370190505b50905060005b60088801548110156130475761302884610bb8858b600a01858154811061300f57fe5b9060005260206000200154612b1a90919063ffffffff16565b82828151811061303457fe5b6020908102919091010152600101612fec565b509695505050505050565b6000611ef46064610bb88461281d565b6008830154600090819060ff8516106130b0576040805162461bcd60e51b81526020600482015260186024820152600080516020613ea0833981519152604482015290519081900360640190fd5b60606130bb86612bd7565b90506130c5613e14565b6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090506130fc8761281d565b6080820181905261310e908390612910565b81526007870154604080516318160ddd60e01b815290516131a092613198926001600160a01b03909116916318160ddd91600480820192602092909190829003018186803b15801561315f57600080fd5b505afa158015613173573d6000803e3d6000fd5b505050506040513d602081101561318957600080fd5b50518351610bb8908990612b1a565b825190612768565b60208201528151829060ff88169081106131b657fe5b602002602001015185111561320f576040805162461bcd60e51b815260206004820152601a6024820152795769746864726177206578636565647320617661696c61626c6560301b604482015290519081900360640190fd5b613223816080015187848460200151613965565b604082015281516060906001600160401b038111801561324257600080fd5b5060405190808252806020026020018201604052801561326c578160200160208202803683370190505b50905061327888612aed565b606083015260005b600889015481101561332f57600084828151811061329a57fe5b6020026020010151905061330f6128a26402540be400610bb887606001518d60ff1687146132e857885160208a01516132e3916132dc91610bb8908a90612b1a565b8790612768565b610da3565b610da3896040015161224a8b60000151610bb88d602001518b612b1a90919063ffffffff16565b83838151811061331b57fe5b602090810291909101015250600101613280565b50600061335861334984608001518a858760200151613965565b838a60ff1681518110610cc557fe5b9050613386896009018960ff168154811061336f57fe5b600091825260209091200154610bb8836001612768565b604090930151929550919350505050935093915050565b6001600160a01b0382166133b0576126d6565b60068301546133e0576001600160a01b0382166000908152600c8401602052604090206402540be40090556134d2565b60006133ec8484611efa565b6007850154604080516370a0823160e01b81526001600160a01b038781166004830152915193945060009391909216916370a08231916024808301926020929190829003018186803b15801561344157600080fd5b505afa158015613455573d6000803e3d6000fd5b505050506040513d602081101561346b57600080fd5b505160068601549091506134b49061348790610da386856127c5565b610bb86402540be400610da36134aa8a6006015489612b1a90919063ffffffff16565b6129df8789612b1a565b6001600160a01b0385166000908152600c8701602052604090205550505b506001600160a01b03166000908152600b9190910160205260409020429055565b6060613548826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613b3f9092919063ffffffff16565b8051909150156126d65780806020019051602081101561356757600080fd5b50516126d65760405162461bcd60e51b815260040180806020018281038252602a815260200180613ec0602a913960400191505060405180910390fd5b815181516060919081146135ff576040805162461bcd60e51b815260206004820152601f60248201527f42616c616e636573206d757374206d61746368206d756c7469706c6965727300604482015290519081900360640190fd5b6060816001600160401b038111801561361757600080fd5b50604051908082528060200260200182016040528015613641578160200160208202803683370190505b50905060005b828110156136915761367285828151811061365e57fe5b6020026020010151878381518110610ca357fe5b82828151811061367e57fe5b6020908102919091010152600101613647565b50949350505050565b600885015460009060ff86811690861614156136fd576040805162461bcd60e51b815260206004820152601d60248201527f43616e277420636f6d7061726520746f6b656e20746f20697473656c66000000604482015290519081900360640190fd5b808660ff161080156137115750808560ff16105b61375b576040805162461bcd60e51b8152602060048201526016602482015275151bdad95b9cc81b5d5cdd081899481a5b881c1bdbdb60521b604482015290519081900360640190fd5b60006137668861281d565b905060006137748583612910565b9050806000806137848686612b1a565b90506000805b878110156137fd578c60ff168114156137a5578a91506137cf565b8b60ff1681146137ca578981815181106137bb57fe5b602002602001015191506137cf565b6137f5565b6137d984836127c5565b93506137f26137e8838a612b1a565b610bb88789612b1a565b94505b60010161378a565b5061381a61380b8389612b1a565b610bb86064610da3888a612b1a565b9350600061383761383084610bb8896064612b1a565b85906127c5565b9050600086815b6101008110156139175790915081906138716138638a61224a876129df876002612b1a565b610bb88a6129df8680612b1a565b9150817330e344c8f517becaeed04245ced2e7301f06f21b6313a1ec759091856040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b1580156138cc57600080fd5b505af41580156138e0573d6000803e3d6000fd5b505050506040513d60208110156138f657600080fd5b50511561390f5750995061197c98505050505050505050565b60010161383e565b506040805162461bcd60e51b815260206004820152601e60248201527f417070726f78696d6174696f6e20646964206e6f7420636f6e76657267650000604482015290519081900360640190fd5b815160009060ff851681116139b3576040805162461bcd60e51b815260206004820152600f60248201526e151bdad95b881b9bdd08199bdd5b99608a1b604482015290519081900360640190fd5b826000806139c18985612b1a565b905060005b84811015613a28578860ff168114613a20576139fe8882815181106139e757fe5b6020026020010151846127c590919063ffffffff16565b9250613a1d613a13868a8481518110610ca357fe5b610bb8868a612b1a565b93505b6001016139c6565b50613a45613a368286612b1a565b610bb86064610da3878b612b1a565b92506000613a62613a5b83610bb88a6064612b1a565b84906127c5565b9050600087815b610100811015613917579091508190613a9c613a8e8b61224a876129df876002612b1a565b610bb8896129df8680612b1a565b9150817330e344c8f517becaeed04245ced2e7301f06f21b6313a1ec759091856040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b158015613af757600080fd5b505af4158015613b0b573d6000803e3d6000fd5b505050506040513d6020811015613b2157600080fd5b505115613b3757509650611e7495505050505050565b600101613a69565b6060611e748484600085606082613b5530613cad565b1015613b925760405162461bcd60e51b8152600401808060200182810382526026815260200180613e596026913960400191505060405180910390fd5b613b9b85613d35565b613bec576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b60208310613c2b5780518252601f199092019160209182019101613c0c565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114613c8d576040519150601f19603f3d011682016040523d82523d6000602084013e613c92565b606091505b5091509150613ca2828286613d3b565b979650505050505050565b60006006602160991b016001600160a01b03166370a08231836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015613d0357600080fd5b505afa158015613d17573d6000803e3d6000fd5b505050506040513d6020811015613d2d57600080fd5b505192915050565b3b151590565b60608315613d4a575081610f50565b825115613d5a5782518084602001fd5b60405162461bcd60e51b8152602060048201818152845160248401528451859391928392604401919085019080838360008315612cd9578181015183820152602001612cc1565b6040518060800160405280600081526020016000815260200160008152602001600081525090565b828054828255906000526020600020908101928215613e04579160200282015b82811115613e04578251825591602001919060010190613de9565b50613e10929150613e43565b5090565b6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b5b80821115613e105760008155600101613e4456fe416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77546f6b656e20696e646578206f7574206f662072616e676500000000000000005361666545524332303a204552433230206f7065726174696f6e20646964206e6f74207375636365656443616e6e6f74207769746864726177206d6f7265207468616e20617661696c61626c65a264697066735822122006a77de8bf1ecfe13c83b71020039c0cdb2766369d9af623e02f2a7dd44c244f64736f6c634300060c0033
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.