Contract
0xaa30d6bba6285d0585722e2440ff89e23ef68864
15
My Name Tag:
Not Available, login to update
ContractCreator:
GENESIS at txn GENESIS_aa30d6bba6285d0585722e2440ff89e23ef68864
[ Download CSV Export ]
Latest 25 internal transaction
[ Download CSV Export ]
Latest 25 Deposits
[ Download CSV Export ]
Contract Source Code Verified (Genesis Bytecode Match Only)
Contract Name:
Swap
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/SwapUtils.sol": { "SwapUtils": "0xb143f7124d57987cd8a6bd9dce36b00f56fe02b7" } } }
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":false,"internalType":"uint256","name":"newAdminFee","type":"uint256"}],"name":"NewAdminFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newSwapFee","type":"uint256"}],"name":"NewSwapFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newWithdrawFee","type":"uint256"}],"name":"NewWithdrawFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldA","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newA","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"initialTime","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"futureTime","type":"uint256"}],"name":"RampA","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":false,"internalType":"uint256","name":"currentA","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"time","type":"uint256"}],"name":"StopRampA","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":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"uint256","name":"minToMint","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"addLiquidity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"calculateCurrentWithdrawFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"calculateRemoveLiquidity","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"uint8","name":"tokenIndex","type":"uint8"}],"name":"calculateRemoveLiquidityOneToken","outputs":[{"internalType":"uint256","name":"availableTokenAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"tokenIndexFrom","type":"uint8"},{"internalType":"uint8","name":"tokenIndexTo","type":"uint8"},{"internalType":"uint256","name":"dx","type":"uint256"}],"name":"calculateSwap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"bool","name":"deposit","type":"bool"}],"name":"calculateTokenAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getA","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAPrecise","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getAdminBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getDepositTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"index","type":"uint8"}],"name":"getToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"index","type":"uint8"}],"name":"getTokenBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"getTokenIndex","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVirtualPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20[]","name":"_pooledTokens","type":"address[]"},{"internalType":"uint8[]","name":"decimals","type":"uint8[]"},{"internalType":"string","name":"lpTokenName","type":"string"},{"internalType":"string","name":"lpTokenSymbol","type":"string"},{"internalType":"uint256","name":"_a","type":"uint256"},{"internalType":"uint256","name":"_fee","type":"uint256"},{"internalType":"uint256","name":"_adminFee","type":"uint256"},{"internalType":"uint256","name":"_withdrawFee","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256[]","name":"minAmounts","type":"uint256[]"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidity","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"uint256","name":"maxBurnAmount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidityImbalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"uint8","name":"tokenIndex","type":"uint8"},{"internalType":"uint256","name":"minAmount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidityOneToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"tokenIndexFrom","type":"uint8"},{"internalType":"uint8","name":"tokenIndexTo","type":"uint8"},{"internalType":"uint256","name":"dx","type":"uint256"},{"internalType":"uint256","name":"minDy","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"swapStorage","outputs":[{"internalType":"uint256","name":"initialA","type":"uint256"},{"internalType":"uint256","name":"futureA","type":"uint256"},{"internalType":"uint256","name":"initialATime","type":"uint256"},{"internalType":"uint256","name":"futureATime","type":"uint256"},{"internalType":"uint256","name":"swapFee","type":"uint256"},{"internalType":"uint256","name":"adminFee","type":"uint256"},{"internalType":"uint256","name":"defaultWithdrawFee","type":"uint256"},{"internalType":"contract LPToken","name":"lpToken","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"transferAmount","type":"uint256"}],"name":"updateUserWithdrawFee","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60806040526000604060006101000a816200001962000052565b8160ff0219169083151502179062000030620000b4565b5050503480156200004b576000806200004862000103565b50505b5062000170565b6303daa959598160e01b8152836004820152602081602483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051935060005b6040811015620000af5760008282015260200162000096565b505050565b6322bd64c0598160e01b8152836004820152846024820152600081604483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b60008152602062000096565b632a2a7adb598160e01b8152600481016020815285602082015260005b868110156200013d57808601518282016040015260200162000120565b506020828760640184336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b505050565b614dc980620001806000396000f3fe60806040523480156200001c576000806200001962002bef565b50505b5060043610620001295760003560e01c80630ba81959146200013957806331cd52b014620001555780633e3a156014620002515780634a1b0d5714620002915780634d49e87d14620002c55780635fd65f0f146200036b57806366c0bd2414620003c45780636dd4480b146200040e5780637c61e56114620006eb57806382b86600146200072557806384cdd9bc146200076f57806391695586146200081557806391ceb3eb146200085f57806398899f40146200088d578063a95b089f14620008d0578063c00c125c146200090e578063d46300fd1462000948578063da7a77be1462000952578063e25aa5fa1462000986578063ef0a712f1462000990578063f9273ffb14620009bb575b6000806200013662002bef565b50505b6200014362000a6d565b60405190815260200160405180910390f35b620001fc6004803603606081101562000178576000806200017562002bef565b50505b81359190810190604081016020820135600160201b811115620001a557600080620001a262002bef565b50505b820183602082011115620001c357600080620001c062002bef565b50505b803590602001918460208302840111600160201b83111715620001f057600080620001ed62002bef565b50505b91935091503562000b35565b60405160208082528190810183818151815260200191508051906020019060200280838360005b838110156200023d57808201518382015260200162000223565b505050509050019250505060405180910390f35b620001436004803603608081101562000274576000806200027162002bef565b50505b5080359060ff602082013516906040810135906060013562000dd6565b6200014360048036036020811015620002b457600080620002b162002bef565b50505b50356001600160a01b031662000f8d565b6200014360048036036060811015620002e857600080620002e562002bef565b50505b810190602081018135600160201b8111156200030e576000806200030b62002bef565b50505b8201836020820111156200032c576000806200032962002bef565b50505b803590602001918460208302840111600160201b8311171562000359576000806200035662002bef565b50505b91935091508035906020013562001065565b62000375620011ba565b60405197885260208801969096526040808801959095526060870193909352608086019190915260a085015260c08401526001600160a01b0390911660e0830152610100909101905180910390f35b620003f860048036036020811015620003e757600080620003e462002bef565b50505b50356001600160a01b031662001247565b60405160ff909116815260200160405180910390f35b620006e9600480360361010081101562000432576000806200042f62002bef565b50505b810190602081018135600160201b81111562000458576000806200045562002bef565b50505b82018360208201111562000476576000806200047362002bef565b50505b803590602001918460208302840111600160201b83111715620004a357600080620004a062002bef565b50505b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b811115620004fe57600080620004fb62002bef565b50505b8201836020820111156200051c576000806200051962002bef565b50505b803590602001918460208302840111600160201b8311171562000549576000806200054662002bef565b50505b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b811115620005a457600080620005a162002bef565b50505b820183602082011115620005c257600080620005bf62002bef565b50505b803590602001918460018302840111600160201b83111715620005ef57600080620005ec62002bef565b50505b91908080601f01602080910402602001604051908101604052818152929190602084018383808284376000920191909152509295949360208101935035915050600160201b8111156200064c576000806200064962002bef565b50505b8201836020820111156200066a576000806200066762002bef565b50505b803590602001918460018302840111600160201b8311171562000697576000806200069462002bef565b50505b91908080601f01602080910402602001604051908101604052818152929190602084018383808284376000920191909152509295505082359350505060208101359060408101359060600135620012f1565b005b620001fc600480360360408110156200070e576000806200070b62002bef565b50505b506001600160a01b03813516906020013562001c75565b620007536004803603602081101562000748576000806200074562002bef565b50505b503560ff1662001e13565b6040516001600160a01b03909116815260200160405180910390f35b620001436004803603606081101562000792576000806200078f62002bef565b50505b810190602081018135600160201b811115620007b857600080620007b562002bef565b50505b820183602082011115620007d657600080620007d362002bef565b50505b803590602001918460208302840111600160201b8311171562000803576000806200080062002bef565b50505b91935091508035906020013562001ebc565b62000143600480360360a081101562000838576000806200083562002bef565b50505b5060ff81358116916020810135909116906040810135906060810135906080013562002011565b620001436004803603602081101562000882576000806200087f62002bef565b50505b503560ff16620021e0565b6200014360048036036060811015620008b057600080620008ad62002bef565b50505b5080356001600160a01b0316906020810135906040013560ff1662002275565b6200014360048036036060811015620008f357600080620008f062002bef565b50505b5060ff81358116916020810135909116906040013562002369565b620006e96004803603604081101562000931576000806200092e62002bef565b50505b506001600160a01b0381351690602001356200244d565b6200014362002598565b620001436004803603602081101562000975576000806200097262002bef565b50505b50356001600160a01b0316620025ea565b620001436200264b565b6200014360048036036020811015620009b357600080620009b062002bef565b50505b50356200269d565b6200014360048036036060811015620009de57600080620009db62002bef565b50505b6001600160a01b038235169190810190604081016020820135600160201b81111562000a145760008062000a1162002bef565b50505b82018360208201111562000a325760008062000a2f62002bef565b50505b803590602001918460208302840111600160201b8311171562000a5f5760008062000a5c62002bef565b50505b9193509150351515620026f5565b600073b143f7124d57987cd8a6bd9dce36b00f56fe02b763c9b64dcb60336040516001600160e01b031960e084901b1681526004810191909152602401602060405180830381868062000abf62002c5c565b15801562000ad75760008062000ad462002bef565b50505b505a62000ae362002cbe565b505050505015801562000b05573d6000803e3d600062000b0262002bef565b50505b505050506040513d602081101562000b275760008062000b2462002bef565b50505b810190808051935050505090565b60606002600162000b4562002db4565b141562000b925760405162461bcd60e51b815260206004820152601f602482015260008051602062004d5a83398151915260448201526064016040518091039062000b8f62002bef565b50505b600280600162000ba162002e01565b50505081805a62000bb162002e50565b111562000c025760405162461bcd60e51b815260206004820152601060248201526f111958591b1a5b99481b9bdd081b595d60821b60448201526064016040518091039062000bff62002bef565b50505b73b143f7124d57987cd8a6bd9dce36b00f56fe02b76373fd6b3e60338888886040518563ffffffff1660e01b8152600401808581526020018481526020018060200182810382528484828181526020019250602002808284376000838201819052601f909101601f1916909201975090955060409450505050505180830381868062000c8d62002c5c565b15801562000ca55760008062000ca262002bef565b50505b505a62000cb162002cbe565b505050505015801562000cd3573d6000803e3d600062000cd062002bef565b50505b505050506040513d6000823e601f3d908101601f19168201604052602081101562000d085760008062000d0562002bef565b50505b8101908080516040519392919084600160201b82111562000d335760008062000d3062002bef565b50505b90830190602082018581111562000d545760008062000d5162002bef565b50505b82518660208202830111600160201b8211171562000d7c5760008062000d7962002bef565b50505b825250602001908051906020019060200280838360005b8381101562000dad57808201518382015260200162000d93565b505050509050016040525050509150506001808062000dcb62002e01565b505050949350505050565b60006002600162000de662002db4565b141562000e335760405162461bcd60e51b815260206004820152601f602482015260008051602062004d5a83398151915260448201526064016040518091039062000e3062002bef565b50505b600280600162000e4262002e01565b50505081805a62000e5262002e50565b111562000ea35760405162461bcd60e51b815260206004820152601060248201526f111958591b1a5b99481b9bdd081b595d60821b60448201526064016040518091039062000ea062002bef565b50505b73b143f7124d57987cd8a6bd9dce36b00f56fe02b763e7a4db8160338888886040516001600160e01b031960e087901b1681526004810194909452602484019290925260ff1660448301526064820152608401602060405180830381868062000f0b62002c5c565b15801562000f235760008062000f2062002bef565b50505b505a62000f2f62002cbe565b505050505015801562000f51573d6000803e3d600062000f4e62002bef565b50505b505050506040513d602081101562000f735760008062000f7062002bef565b50505b8101908080519450505050506001808062000dcb62002e01565b600073b143f7124d57987cd8a6bd9dce36b00f56fe02b763968e55946033846040516001600160e01b031960e085901b16815260048101929092526001600160a01b03166024820152604401602060405180830381868062000fee62002c5c565b15801562001006576000806200100362002bef565b50505b505a6200101262002cbe565b505050505015801562001034573d6000803e3d60006200103162002bef565b50505b505050506040513d602081101562001056576000806200105362002bef565b50505b81019080805195945050505050565b6000600260016200107562002db4565b1415620010c25760405162461bcd60e51b815260206004820152601f602482015260008051602062004d5a833981519152604482015260640160405180910390620010bf62002bef565b50505b6002806001620010d162002e01565b50505081805a620010e162002e50565b1115620011325760405162461bcd60e51b815260206004820152601060248201526f111958591b1a5b99481b9bdd081b595d60821b6044820152606401604051809103906200112f62002bef565b50505b73b143f7124d57987cd8a6bd9dce36b00f56fe02b76340370edf60338888886040518563ffffffff1660e01b815260040180858152602001806020018381526020018281038252858582818152602001925060200280828437600083820152601f01601f191690910196506020955060409450505050505180830381868062000f0b62002c5c565b603380620011c762002db4565b9080600101620011d662002db4565b9080600201620011e562002db4565b9080600301620011f462002db4565b90806004016200120362002db4565b90806005016200121262002db4565b90806006016200122162002db4565b906000600782016200123262002db4565b906101000a90046001600160a01b0316905088565b6001600160a01b0381166000908152604160205280604081206000906200126d62002db4565b906101000a900460ff169050826001600160a01b03166200128e8262001e13565b6001600160a01b031614620012eb5760405162461bcd60e51b8152602060048201526014602482015273151bdad95b88191bd95cc81b9bdd08195e1a5cdd60621b604482015260640160405180910390620012e862002bef565b50505b92915050565b60016000620012ff62002db4565b906101000a900460ff16806200131a57506200131a6200280c565b806200133a57506000806200132e62002db4565b906101000a900460ff16155b620013825760405162461bcd60e51b815260040180806020018281038252602e81526020018062004d7a602e9139604001915050604051809103906200137f62002bef565b50505b60006001816200139162002db4565b906101000a900460ff16159050801562001400576001600061010081620013b762002db4565b8160ff02191690831515021790620013ce62002e01565b50505060016000806101000a81620013e562002db4565b8160ff02191690831515021790620013fc62002e01565b5050505b6200140a62002879565b6001895111620014675760405162461bcd60e51b81526020600482015260196024820152785f706f6f6c6564546f6b656e732e6c656e677468203c3d203160381b6044820152606401604051809103906200146462002bef565b50505b602089511115620014c55760405162461bcd60e51b81526020600482015260196024820152782fb837b7b632b22a37b5b2b739973632b733ba34101f10199960391b604482015260640160405180910390620014c262002bef565b50505b8751895114620015265760405162461bcd60e51b815260206004820152601f60248201527f5f706f6f6c6564546f6b656e7320646563696d616c73206d69736d61746368006044820152606401604051809103906200152362002bef565b50505b606088516001600160401b03811180156200154b576000806200154862002bef565b50505b5060405190808252806020026020018201604052801562001576578160200160208202803683370190505b50905060005b8a518160ff161015620018315760ff8116156200168357604160008c8360ff1681518110620015a757fe5b60200260200101516001600160a01b03166001600160a01b03168152602001908152602001600020600090620015dc62002db4565b60ff6101009290920a9004161580156200163457508a8160ff16815181106200160157fe5b60200260200101516001600160a01b03168b6000815181106200162057fe5b60200260200101516001600160a01b031614155b620016835760405162461bcd60e51b815260206004820152601060248201526f4475706c696361746520746f6b656e7360801b6044820152606401604051809103906200168062002bef565b50505b60008b60ff8316815181106200169557fe5b60200260200101516001600160a01b03161415620017045760405162461bcd60e51b815260206004820152601d60248201527f546865203020616464726573732069736e277420616e204552432d32300000006044820152606401604051809103906200170162002bef565b50505b60128a60ff8316815181106200171657fe5b602002602001015160ff1611156200177c5760405162461bcd60e51b815260206004820152601a6024820152790a8ded6cadc40c8cac6d2dac2d8e640caf0c6cacac8e640dac2f60331b6044820152606401604051809103906200177962002bef565b50505b620017a48a8260ff16815181106200179057fe5b602002602001015160129060ff16620029c8565b600a0a828260ff1681518110620017b757fe5b60200260200101818152505080604160008d8460ff1681518110620017d857fe5b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000206001816200180d62002db4565b8160ff021916908360ff160217906200182562002e01565b5050506001016200157c565b50620f42408610620018895760405162461bcd60e51b81526020600482015260126024820152715f612065786365656473206d6178696d756d60701b6044820152606401604051809103906200188662002bef565b50505b6305f5e1008510620018e35760405162461bcd60e51b81526020600482015260146024820152735f6665652065786365656473206d6178696d756d60601b604482015260640160405180910390620018e062002bef565b50505b6402540be4008410620019435760405162461bcd60e51b81526020600482015260196024820152785f61646d696e4665652065786365656473206d6178696d756d60381b6044820152606401604051809103906200194062002bef565b50505b6305f5e1008310620019a55760405162461bcd60e51b815260206004820152601c60248201527b5f77697468647261774665652065786365656473206d6178696d756d60201b604482015260640160405180910390620019a262002bef565b50505b87876012604051620019b79062002e97565b8080602001806020018460ff168152602001838103835286818151815260200191508051906020019080838360005b8381101562001a00578082015183820152602001620019e6565b50505050905090810190601f16801562001a2e5780820380516001836020036101000a031916815260200191505b50838103825285818151815260200191508051906020019080838360005b8381101562001a6657808201518382015260200162001a4c565b50505050905090810190601f16801562001a945780820380516001836020036101000a031916815260200191505b509550505050505060405180910390600062001aaf62002ea5565b50508015801562001acf573d6000803e3d600062001acc62002bef565b50505b50603a60018162001adf62002db4565b816001600160a01b0302191690836001600160a01b031602179062001b0362002e01565b50603b91508b9050805162001b1d92916020019062002f31565b50603c81805162001b3392916020019062002fc8565b5089516001600160401b038111801562001b575760008062001b5462002bef565b50505b5060405190808252806020026020018201604052801562001b82578160200160208202803683370190505b50603d90805162001b9892916020019062002fc8565b5062001ba686606462002a30565b80603362001bb362002e01565b5062001bc59150879050606462002a30565b80603462001bd262002e01565b5060009150819050603562001be662002e01565b5060009150819050603662001bfa62002e01565b50869150819050603762001c0d62002e01565b50859150819050603862001c2062002e01565b50849150819050603962001c3362002e01565b50505050801562001c6a576000806101008162001c4f62002db4565b8160ff0219169083151502179062001c6662002e01565b5050505b505050505050505050565b606073b143f7124d57987cd8a6bd9dce36b00f56fe02b763f967a81f603385856040516001600160e01b031960e086901b16815260048101939093526001600160a01b0390911660248301526044820152606401600060405180830381868062001cde62002c5c565b15801562001cf65760008062001cf362002bef565b50505b505a62001d0262002cbe565b505050505015801562001d24573d6000803e3d600062001d2162002bef565b50505b505050506040513d6000823e601f3d908101601f19168201604052602081101562001d595760008062001d5662002bef565b50505b8101908080516040519392919084600160201b82111562001d845760008062001d8162002bef565b50505b90830190602082018581111562001da55760008062001da262002bef565b50505b82518660208202830111600160201b8211171562001dcd5760008062001dca62002bef565b50505b825250602001908051906020019060200280838360005b8381101562001dfe57808201518382015260200162001de4565b50505050905001604052505050905092915050565b6000603b8062001e2262002db4565b90508260ff161062001e745760405162461bcd60e51b815260206004820152600c60248201526b4f7574206f662072616e676560a01b60448201526064016040518091039062001e7162002bef565b50505b603b60ff83168162001e8562002db4565b811062001e8e57fe5b9060005260206000200160009062001ea562002db4565b906101000a90046001600160a01b03169050919050565b60006002600162001ecc62002db4565b141562001f195760405162461bcd60e51b815260206004820152601f602482015260008051602062004d5a83398151915260448201526064016040518091039062001f1662002bef565b50505b600280600162001f2862002e01565b50505081805a62001f3862002e50565b111562001f895760405162461bcd60e51b815260206004820152601060248201526f111958591b1a5b99481b9bdd081b595d60821b60448201526064016040518091039062001f8662002bef565b50505b73b143f7124d57987cd8a6bd9dce36b00f56fe02b76341b91c2660338888886040518563ffffffff1660e01b815260040180858152602001806020018381526020018281038252858582818152602001925060200280828437600083820152601f01601f191690910196506020955060409450505050505180830381868062000f0b62002c5c565b6000600260016200202162002db4565b14156200206e5760405162461bcd60e51b815260206004820152601f602482015260008051602062004d5a8339815191526044820152606401604051809103906200206b62002bef565b50505b60028060016200207d62002e01565b50505081805a6200208d62002e50565b1115620020de5760405162461bcd60e51b815260206004820152601060248201526f111958591b1a5b99481b9bdd081b595d60821b604482015260640160405180910390620020db62002bef565b50505b73b143f7124d57987cd8a6bd9dce36b00f56fe02b763a5397b226033898989896040516001600160e01b031960e088901b168152600481019590955260ff93841660248601529190921660448401526064830191909152608482015260a40160206040518083038186806200215262002c5c565b1580156200216a576000806200216762002bef565b50505b505a6200217662002cbe565b505050505015801562002198573d6000803e3d60006200219562002bef565b50505b505050506040513d6020811015620021ba57600080620021b762002bef565b50505b81019080805194505050505060018080620021d462002e01565b50505095945050505050565b6000603b80620021ef62002db4565b90508260ff1610620022475760405162461bcd60e51b8152602060048201526012602482015271496e646578206f7574206f662072616e676560701b6044820152606401604051809103906200224462002bef565b50505b603d60ff8316816200225862002db4565b81106200226157fe5b90600052602060002001620012eb62002db4565b600073b143f7124d57987cd8a6bd9dce36b00f56fe02b763e4a9a0da60338686866040516001600160e01b031960e087901b16815260048101949094526001600160a01b039092166024840152604483015260ff16606482015260840160408051808303818680620022e662002c5c565b158015620022fe57600080620022fb62002bef565b50505b505a6200230a62002cbe565b50505050501580156200232c573d6000803e3d60006200232962002bef565b50505b505050506040513d60408110156200234e576000806200234b62002bef565b50505b81019080805192919060200180515092979650505050505050565b600073b143f7124d57987cd8a6bd9dce36b00f56fe02b7634b23603c60338686866040516001600160e01b031960e087901b168152600481019490945260ff92831660248501529116604483015260648201526084016020604051808303818680620023d462002c5c565b158015620023ec57600080620023e962002bef565b50505b505a620023f862002cbe565b50505050501580156200241a573d6000803e3d60006200241762002bef565b50505b505050506040513d60208110156200243c576000806200243962002bef565b50505b810190808051979650505050505050565b6000603a6200245b62002db4565b906101000a90046001600160a01b03166001600160a01b03165a6200247f62003033565b6001600160a01b031614620024e35760405162461bcd60e51b815260206004820152601b60248201527a27b7363c9031b0b63630b1363290313c903837b7b6103a37b5b2b760291b604482015260640160405180910390620024e062002bef565b50505b73b143f7124d57987cd8a6bd9dce36b00f56fe02b763e9252d46603384846040516001600160e01b031960e086901b16815260048101939093526001600160a01b039091166024830152604482015260640160006040518083038186806200254a62002c5c565b15801562002562576000806200255f62002bef565b50505b505a6200256e62002cbe565b505050505015801562002590573d6000803e3d60006200258d62002bef565b50505b505050505050565b600073b143f7124d57987cd8a6bd9dce36b00f56fe02b763b0a14cfc60336040516001600160e01b031960e084901b1681526004810191909152602401602060405180830381868062000abf62002c5c565b600073b143f7124d57987cd8a6bd9dce36b00f56fe02b763e8c47a546033846040516001600160e01b031960e085901b16815260048101929092526001600160a01b03166024820152604401602060405180830381868062000fee62002c5c565b600073b143f7124d57987cd8a6bd9dce36b00f56fe02b76371906c2c60336040516001600160e01b031960e084901b1681526004810191909152602401602060405180830381868062000abf62002c5c565b600073b143f7124d57987cd8a6bd9dce36b00f56fe02b7637d0481606033846040516001600160e01b031960e085901b16815260048101929092526024820152604401602060405180830381868062000fee62002c5c565b600073b143f7124d57987cd8a6bd9dce36b00f56fe02b7635e58579c6033878787876040518663ffffffff1660e01b815260040180868152602001856001600160a01b031681526020018060200183151581526020018281038252858582818152602001925060200280828437600083820152601f01601f1916909101975060209650604095505050505050518083038186806200279262002c5c565b158015620027aa57600080620027a762002bef565b50505b505a620027b662002cbe565b5050505050158015620027d8573d6000803e3d6000620027d562002bef565b50505b505050506040513d6020811015620027fa57600080620027f762002bef565b50505b81019080805198975050505050505050565b6000620028735a63996d79a5598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051925060005b60408110156200286a5760008282015260200162002851565b50505062002aa0565b15905090565b600160006200288762002db4565b906101000a900460ff1680620028a25750620028a26200280c565b80620028c25750600080620028b662002db4565b906101000a900460ff16155b6200290a5760405162461bcd60e51b815260040180806020018281038252602e81526020018062004d7a602e9139604001915050604051809103906200290762002bef565b50505b60006001816200291962002db4565b906101000a900460ff161590508015620029885760016000610100816200293f62002db4565b8160ff021916908315150217906200295662002e01565b50505060016000806101000a816200296d62002db4565b8160ff021916908315150217906200298462002e01565b5050505b6200299262002ab7565b8015620029c55760008061010081620029aa62002db4565b8160ff02191690831515021790620029c162002e01565b5050505b50565b60008282111562002a2a5760405162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f77000060448201526064016040518091039062002a2762002bef565b50505b50900390565b60008262002a4157506000620012eb565b8282028284828162002a4f57fe5b041462002a995760405162461bcd60e51b815260040180806020018281038252602181526020018062004da8602191396040019150506040518091039062002a9662002bef565b50505b9392505050565b6000808262002aae62002c5c565b15159392505050565b6001600062002ac562002db4565b906101000a900460ff168062002ae0575062002ae06200280c565b8062002b00575060008062002af462002db4565b906101000a900460ff16155b62002b485760405162461bcd60e51b815260040180806020018281038252602e81526020018062004d7a602e91396040019150506040518091039062002b4562002bef565b50505b600060018162002b5762002db4565b906101000a900460ff16159050801562002bc657600160006101008162002b7d62002db4565b8160ff0219169083151502179062002b9462002e01565b50505060016000806101000a8162002bab62002db4565b8160ff0219169083151502179062002bc262002e01565b5050505b6001808062002bd462002e01565b5050508015620029c55760008061010081620029aa62002db4565b632a2a7adb598160e01b8152600481016020815285602082015260005b8681101562002c2957808601518282016040015260200162002c0c565b506020828760640184336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b505050565b638435035b598160e01b8152836004820152602081602483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051935060005b604081101562002cb95760008282015260200162002ca0565b505050565b63ffe73914598160e01b815262002cf2565b8080831115620012eb575090919050565b8080831015620012eb575090919050565b836004820152846024820152606060448201528660648201526084810160005b8881101562002d2c57808801518282015260200162002d12565b506060828960a40184336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b815160408301513d6000853e8b8b82606087013350600060045af1505962002d838d3d62002ce1565b8c0162002d91818762002cd0565b5b8281101562002da8576000815260200162002d92565b50929c50505050505050565b6303daa959598160e01b8152836004820152602081602483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051600082529350602062002ca0565b6322bd64c0598160e01b8152836004820152846024820152600081604483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b60008152602062002ca0565b63bdbf8c36598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051600082529350602062002ca0565b611c8080620030da83390190565b6314aa2ff7598160e01b8152600481016020815286602082015260005b8781101562002edf57808701518282016040015260200162002ec2565b506020828860640184336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8151965059825b8181101562002f2a576000815260200162002f14565b5050505050565b828062002f3d62002db4565b828262002f4962002e01565b505090600052602060002090810192821562002fb6579160200282015b8281111562002fb65782518260018162002f7f62002db4565b816001600160a01b0302191690836001600160a01b031602179062002fa362002e01565b5050509160200191906001019062002f66565b5062002fc49291506200307a565b5090565b828062002fd462002db4565b828262002fe062002e01565b505090600052602060002090810192821562003025579160200282015b8281111562003025578251826200301362002e01565b50509160200191906001019062002ffd565b5062002fc4929150620030b6565b6373509064598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051600082529350602062002ca0565b8082111562002fc4576000816001816200309362002db4565b906001600160a01b0302191690620030aa62002e01565b5050506001016200307a565b8082111562002fc45760008082620030cd62002e01565b505050600101620030b656fe60806040523480156200001c576000806200001962000372565b50505b5060405162001c8038038062001c80833981810160405260608110156200004d576000806200004a62000372565b50505b810190808051604051939291908464010000000082111562000079576000806200007662000372565b50505b9083019060208201858111156200009a576000806200009762000372565b50505b8251640100000000811182820188101715620000c057600080620000bd62000372565b50505b825250602001908051906020019080838360005b83811015620000ee578082015183820152602001620000d4565b50505050905090810190601f1680156200011c5780820380516001836020036101000a031916815260200191505b50604052602001805160405193929190846401000000008211156200014b576000806200014862000372565b50505b9083019060208201858111156200016c576000806200016962000372565b50505b825164010000000081118282018810171562000192576000806200018f62000372565b50505b825250602001908051906020019080838360005b83811015620001c0578082015183820152602001620001a6565b50505050905090810190601f168015620001ee5780820380516001836020036101000a031916815260200191505b5060405260200180519250849150839050600382805162000214929160200190620003df565b5060048180516200022a929160200190620003df565b50601260056001816200023c6200048e565b8160ff021916908360ff1602179062000254620004f0565b505050505060006200026b6200033260201b60201c565b9050806005610100816200027e6200048e565b816001600160a01b0302191690836001600160a01b0316021790620002a2620004f0565b5050506001600160a01b03811660007f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a350620002e98162000344565b620002f362000332565b6006600181620003026200048e565b816001600160a01b0302191690836001600160a01b031602179062000326620004f0565b505050505050620005a9565b60005a6200033f6200053f565b905090565b806005600181620003546200048e565b8160ff021916908360ff160217906200036c620004f0565b50505050565b632a2a7adb598160e01b8152600481016020815285602082015260005b86811015620003ac5780860151828201604001526020016200038f565b506020828760640184336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b505050565b8280620003eb6200048e565b600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200043657805160ff191683800117856200042e620004f0565b50506200047c565b8280016001018562000447620004f0565b505082156200047c579182015b828111156200047c578251826200046a620004f0565b50509160200191906001019062000454565b506200048a92915062000586565b5090565b6303daa959598160e01b8152836004820152602081602483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051935060005b6040811015620004eb57600082820152602001620004d2565b505050565b6322bd64c0598160e01b8152836004820152846024820152600081604483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b600081526020620004d2565b6373509064598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b80516000825293506020620004d2565b808211156200048a57600080826200059d620004f0565b50505060010162000586565b6116c780620005b96000396000f3fe608060405234801561001957600080610016611247565b50505b50600436106100ef5760003560e01c806306fdde03146100fd578063095ea7b31461017c57806318160ddd146101c557806323b872dd146101df578063313ce5671461021e578063395093511461023c57806340c10f191461027157806342966c68146102a857806370a08231146102ce578063715018a6146102fd57806379cc6790146103055780638119c0651461033a5780638da5cb5b1461035e57806395d89b4114610366578063a457c2d71461036e578063a9059cbb146103a3578063dd62ed3e146103d8578063f2fde38b1461040f575b6000806100fa611247565b50505b61010561043e565b60405160208082528190810183818151815260200191508051906020019080838360005b83811015610141578082015183820152602001610129565b50505050905090810190601f16801561016e5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101b16004803603604081101561019b57600080610198611247565b50505b506001600160a01b0381351690602001356104fb565b604051901515815260200160405180910390f35b6101cd610519565b60405190815260200160405180910390f35b6101b1600480360360608110156101fe576000806101fb611247565b50505b506001600160a01b0381358116916020810135909116906040013561052a565b6102266105be565b60405160ff909116815260200160405180910390f35b6101b16004803603604081101561025b57600080610258611247565b50505b506001600160a01b0381351690602001356105da565b6102a6600480360360408110156102905760008061028d611247565b50505b506001600160a01b03813516906020013561063f565b005b6102a6600480360360208110156102c7576000806102c4611247565b50505b50356106ff565b6101cd600480360360208110156102ed576000806102ea611247565b50505b50356001600160a01b0316610713565b6102a6610733565b6102a66004803603604081101561032457600080610321611247565b50505b506001600160a01b038135169060200135610815565b610342610861565b6040516001600160a01b03909116815260200160405180910390f35b610342610880565b6101056108a3565b6101b16004803603604081101561038d5760008061038a611247565b50505b506001600160a01b038135169060200135610949565b6101b1600480360360408110156103c2576000806103bf611247565b50505b506001600160a01b0381351690602001356109bf565b6101cd600480360360408110156103f7576000806103f4611247565b50505b506001600160a01b03813581169160200135166109d3565b6102a66004803603602081101561042e5760008061042b611247565b50505b50356001600160a01b0316610a13565b606060038061044b6112b2565b600181600116156101000203166002900480601f0160208091040260200160405190810160405281815291906020830182806104856112b2565b600181600116156101000203166002900480156104f15780601f106104bf5761010080836104b16112b2565b0402835291602001916104f1565b820191906000526020600020905b816104d66112b2565b815290600101906020018083116104cd57829003601f168201915b5050505050905090565b600061050f610508610b4b565b8484610b56565b5060015b92915050565b600060026105256112b2565b905090565b6000610537848484610c7e565b6105b484610543610b4b565b6105af8560405160608101604052602880825261158c60208301396001600160a01b038a1660009081526001602052604090206000610580610b4b565b6001600160a01b03166001600160a01b031681526020019081526020016000206105a86112b2565b9190610e0a565b610b56565b5060019392505050565b60008060056105cb6112b2565b906101000a900460ff16905090565b600061050f6105e7610b4b565b846105af85600160006105f8610b4b565b6001600160a01b03166001600160a01b031681526020019081526020016000206001600160a01b0389166000908152602091909152604090206106396112b2565b90610eaa565b610647610b4b565b6001600160a01b0316610658610880565b6001600160a01b0316146106a95760405162461bcd60e51b815260206004820181905260248201526000805160206115b48339815191526044820152606401604051809103906106a6611247565b50505b806106f15760405162461bcd60e51b815260206004820152600b60248201526a0616d6f756e74203d3d20360ac1b6044820152606401604051809103906106ee611247565b50505b6106fb8282610f10565b5050565b61071061070a610b4b565b82611017565b50565b6001600160a01b03811660009081526020819052604081206105136112b2565b61073b610b4b565b6001600160a01b031661074c610880565b6001600160a01b03161461079d5760405162461bcd60e51b815260206004820181905260248201526000805160206115b483398151915260448201526064016040518091039061079a611247565b50505b6000600160056107ab6112b2565b906101000a90046001600160a01b03166001600160a01b03166000805160206115d483398151915260405160405180910390a360006005610100816107ee6112b2565b816001600160a01b0302191690836001600160a01b031602179061081061130d565b505050565b6000610843826040516060810160405260248082526115f460208301396105a88661083e610b4b565b6109d3565b905061085783610851610b4b565b83610b56565b6108108383611017565b6000600661086d6112b2565b906101000a90046001600160a01b031681565b60006001600561088e6112b2565b906101000a90046001600160a01b0316905090565b60606004806108b06112b2565b600181600116156101000203166002900480601f0160208091040260200160405190810160405281815291906020830182806108ea6112b2565b600181600116156101000203166002900480156104f15780601f106109165761010080836104b16112b2565b820191906000526020600020905b8161092d6112b2565b8152906001019060200180831161092457509395945050505050565b600061050f610956610b4b565b846105af856040516060810160405260258082526116a260208301396001600061097e610b4b565b6001600160a01b03166001600160a01b031681526020019081526020016000206001600160a01b038a166000908152602091909152604090206105a86112b2565b600061050f6109cc610b4b565b8484610c7e565b6001600160a01b03821660009081526001602052604081206001600160a01b038316600090815260209190915260409020610a0c6112b2565b9392505050565b610a1b610b4b565b6001600160a01b0316610a2c610880565b6001600160a01b031614610a7d5760405162461bcd60e51b815260206004820181905260248201526000805160206115b4833981519152604482015260640160405180910390610a7a611247565b50505b6001600160a01b038116610acb5760405162461bcd60e51b815260040180806020018281038252602681526020018061151e6026913960400191505060405180910390610ac8611247565b50505b6001600160a01b03811660016005610ae16112b2565b906101000a90046001600160a01b03166001600160a01b03166000805160206115d483398151915260405160405180910390a380600561010081610b236112b2565b816001600160a01b0302191690836001600160a01b0316021790610b4561130d565b50505050565b60005a61052561135b565b6001600160a01b038316610ba45760405162461bcd60e51b815260040180806020018281038252602481526020018061167e6024913960400191505060405180910390610ba1611247565b50505b6001600160a01b038216610bf25760405162461bcd60e51b81526004018080602001828103825260228152602001806115446022913960400191505060405180910390610bef611247565b50505b6001600160a01b038316600090815260016020528190604090206001600160a01b0384166000908152602091909152604090208190610c2f61130d565b505050816001600160a01b0316836001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258360405190815260200160405180910390a3505050565b6001600160a01b038316610ccc5760405162461bcd60e51b81526004018080602001828103825260258152602001806116596025913960400191505060405180910390610cc9611247565b50505b6001600160a01b038216610d1a5760405162461bcd60e51b81526004018080602001828103825260238152602001806114d96023913960400191505060405180910390610d17611247565b50505b610d25838383611125565b610d608160405160608101604052602680825261156660208301396001600160a01b03861660009081526020819052604090206105a86112b2565b6001600160a01b03841660009081526020819052604090208190610d8261130d565b5050506001600160a01b03821660009081526020819052610dab908290604090206106396112b2565b6001600160a01b03831660009081526020819052604090208190610dcd61130d565b505050816001600160a01b0316836001600160a01b03166000805160206116188339815191528360405190815260200160405180910390a3505050565b60008184841115610ea25760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015610e5e578082015183820152602001610e46565b50505050905090810190601f168015610e8b5780820380516001836020036101000a031916815260200191505b509250505060405180910390610e9f611247565b50505b505050900390565b600082820183811015610a0c5760405162461bcd60e51b815260206004820152601b60248201527a536166654d6174683a206164646974696f6e206f766572666c6f7760281b604482015260640160405180910390610f07611247565b50509392505050565b6001600160a01b038216610f735760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015260640160405180910390610f70611247565b50505b610f7f60008383611125565b610f8d8160026106396112b2565b806002610f9861130d565b5050506001600160a01b03821660009081526020819052610fc1908290604090206106396112b2565b6001600160a01b03831660009081526020819052604090208190610fe361130d565b5050506001600160a01b03821660006000805160206116188339815191528360405190815260200160405180910390a35050565b6001600160a01b0382166110655760405162461bcd60e51b81526004018080602001828103825260218152602001806116386021913960400191505060405180910390611062611247565b50505b61107182600083611125565b6110ac816040516060810160405260228082526114fc60208301396001600160a01b03851660009081526020819052604090206105a86112b2565b6001600160a01b038316600090815260208190526040902081906110ce61130d565b5050506110e58160026110df6112b2565b906111e5565b8060026110f061130d565b5060009150506001600160a01b0383166000805160206116188339815191528360405190815260200160405180910390a35050565b611130838383610810565b6000600661113c6112b2565b906101000a90046001600160a01b03166001600160a01b031663c00c125c83836040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044016000604051808303816000878061119d6113a1565b1580156111b2576000806111af611247565b50505b505a6111bc6113ed565b5050505050501580156111dc573d6000803e3d60006111d9611247565b50505b50505050505050565b6000828211156112415760405162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015260640160405180910390610ea2611247565b50900390565b632a2a7adb598160e01b8152600481016020815285602082015260005b8681101561127f578086015182820160400152602001611264565b506020828760640184336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b505050565b6303daa959598160e01b8152836004820152602081602483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051935060005b6040811015610810576000828201526020016112f6565b6322bd64c0598160e01b8152836004820152846024820152600081604483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b6000815260206112f6565b6373509064598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b805160008252935060206112f6565b638435035b598160e01b8152836004820152602081602483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b805160008252935060206112f6565b6385979f76598160e01b815261141e565b8080831115610513575090919050565b8080831015610513575090919050565b836004820152846024820152606060448201528760648201526084810160005b8981101561145657808901518282015260200161143e565b506060828a60a40184336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b815160408301513d6000853e8c8c82606087013350600060045af150596114ab8e3d61140e565b8d016114b781876113fe565b5b828110156114cc57600081526020016114b8565b50929d5050505050505056fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a206275726e20616d6f756e7420657863656564732062616c616e63654f776e61626c653a206e6577206f776e657220697320746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e63654f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65728be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e045524332303a206275726e20616d6f756e74206578636565647320616c6c6f77616e6365ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef45524332303a206275726e2066726f6d20746865207a65726f206164647265737345524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737345524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00496e697469616c697a61626c653a20636f6e747261637420697320616c726561647920696e697469616c697a6564536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.