Transaction Hash:
Block:
20647984 at Aug-31-2024 10:33:59 AM +UTC
Transaction Fee:
0.000140525321709142 ETH
$0.32
Gas Used:
128,182 Gas / 1.096295281 Gwei
Emitted Events:
43 |
L1_ERC20_Bridge.WithdrawalBonded( transferId=0265CC49CCD2109735FAECE4BFEA944A4D168A8CF72767E7D7CB61B581A0FB6C, amount=156094538006050657476 )
|
44 |
MaticToken.Transfer( from=[Receiver] L1_ERC20_Bridge, to=0x7892a230429fFED593a8966c4f28a91dDfb178E9, value=154310082525828638743 )
|
45 |
MaticToken.Transfer( from=[Receiver] L1_ERC20_Bridge, to=[Sender] 0xd8781ca9163e9f132a4d8392332e64115688013a, value=1784455480222018733 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x22B1Cbb8...A76Eb1cc2 | (Hop Protocol: MATIC Bridge) | ||||
0x388C818C...7ccB19297
Miner
| (Lido: Execution Layer Rewards Vault) | 52.814540861575157839 Eth | 52.814549834315157839 Eth | 0.00000897274 | |
0x7D1AfA7B...8AaCfeBB0 | |||||
0xd8781cA9...15688013a | (Hop Protocol: Matic Bonder) |
9.788939047183323857 Eth
Nonce: 29419
|
9.788798521861614715 Eth
Nonce: 29420
| 0.000140525321709142 |
Execution Trace
L1_ERC20_Bridge.bondWithdrawal( recipient=0x7892a230429fFED593a8966c4f28a91dDfb178E9, amount=156094538006050657476, transferNonce=0D47784D3B7225D96D7226DEBA169983F9526EF10BC9283966985D5F75D67E0B, bonderFee=1784455480222018733 )
bondWithdrawal[Bridge (ln:1058)]
getTransferId[Bridge (ln:1069)]
encode[Bridge (ln:939)]
getChainId[Bridge (ln:1070)]
_bondWithdrawal[Bridge (ln:1078)]
_addDebit[Bridge (ln:1186)]
WithdrawalBonded[Bridge (ln:1188)]
_fulfillWithdraw[Bridge (ln:1079)]
_markTransferSpent[Bridge (ln:1198)]
_transferFromBridge[Bridge (ln:1199)]
sub[Bridge (ln:1199)]
_transferFromBridge[Bridge (ln:1201)]
File 1 of 2: L1_ERC20_Bridge
File 2 of 2: MaticToken
// 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.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; 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: 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.2 <0.8.0; /** * @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; } /** * @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); } /** * @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.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.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/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.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: 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; } }
File 2 of 2: MaticToken
pragma solidity 0.5.2; // File: openzeppelin-solidity/contracts/token/ERC20/IERC20.sol /** * @title ERC20 interface * @dev see https://github.com/ethereum/EIPs/issues/20 */ interface IERC20 { function transfer(address to, uint256 value) external returns (bool); function approve(address spender, uint256 value) external returns (bool); function transferFrom(address from, address to, uint256 value) external returns (bool); function totalSupply() external view returns (uint256); function balanceOf(address who) external view returns (uint256); function allowance(address owner, address spender) external view returns (uint256); event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); } // File: openzeppelin-solidity/contracts/math/SafeMath.sol /** * @title SafeMath * @dev Unsigned math operations with safety checks that revert on error */ library SafeMath { /** * @dev Multiplies two unsigned integers, reverts on overflow. */ function mul(uint256 a, uint256 b) internal pure returns (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-solidity/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b); return c; } /** * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a); uint256 c = a - b; return c; } /** * @dev Adds two unsigned integers, reverts on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a); return c; } /** * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo), * reverts when dividing by zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b != 0); return a % b; } } // File: openzeppelin-solidity/contracts/token/ERC20/ERC20.sol /** * @title Standard ERC20 token * * @dev Implementation of the basic standard token. * https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md * Originally based on code by FirstBlood: * https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol * * This implementation emits additional Approval events, allowing applications to reconstruct the allowance status for * all accounts just by listening to said events. Note that this isn't required by the specification, and other * compliant implementations may not do it. */ contract ERC20 is IERC20 { using SafeMath for uint256; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowed; uint256 private _totalSupply; /** * @dev Total number of tokens in existence */ function totalSupply() public view returns (uint256) { return _totalSupply; } /** * @dev Gets the balance of the specified address. * @param owner The address to query the balance of. * @return An uint256 representing the amount owned by the passed address. */ function balanceOf(address owner) public view returns (uint256) { return _balances[owner]; } /** * @dev Function to check the amount of tokens that an owner allowed to a spender. * @param owner address The address which owns the funds. * @param spender address The address which will spend the funds. * @return A uint256 specifying the amount of tokens still available for the spender. */ function allowance(address owner, address spender) public view returns (uint256) { return _allowed[owner][spender]; } /** * @dev Transfer token for a specified address * @param to The address to transfer to. * @param value The amount to be transferred. */ function transfer(address to, uint256 value) public returns (bool) { _transfer(msg.sender, to, value); return true; } /** * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. * 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 * @param spender The address which will spend the funds. * @param value The amount of tokens to be spent. */ function approve(address spender, uint256 value) public returns (bool) { require(spender != address(0)); _allowed[msg.sender][spender] = value; emit Approval(msg.sender, spender, value); return true; } /** * @dev Transfer tokens from one address to another. * Note that while this function emits an Approval event, this is not required as per the specification, * and other compliant implementations may not emit the event. * @param from address The address which you want to send tokens from * @param to address The address which you want to transfer to * @param value uint256 the amount of tokens to be transferred */ function transferFrom(address from, address to, uint256 value) public returns (bool) { _allowed[from][msg.sender] = _allowed[from][msg.sender].sub(value); _transfer(from, to, value); emit Approval(from, msg.sender, _allowed[from][msg.sender]); return true; } /** * @dev Increase the amount of tokens that an owner allowed to a spender. * approve should be called when allowed_[_spender] == 0. To increment * allowed value is better to use this function to avoid 2 calls (and wait until * the first transaction is mined) * From MonolithDAO Token.sol * Emits an Approval event. * @param spender The address which will spend the funds. * @param addedValue The amount of tokens to increase the allowance by. */ function increaseAllowance(address spender, uint256 addedValue) public returns (bool) { require(spender != address(0)); _allowed[msg.sender][spender] = _allowed[msg.sender][spender].add(addedValue); emit Approval(msg.sender, spender, _allowed[msg.sender][spender]); return true; } /** * @dev Decrease the amount of tokens that an owner allowed to a spender. * approve should be called when allowed_[_spender] == 0. To decrement * allowed value is better to use this function to avoid 2 calls (and wait until * the first transaction is mined) * From MonolithDAO Token.sol * Emits an Approval event. * @param spender The address which will spend the funds. * @param subtractedValue The amount of tokens to decrease the allowance by. */ function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) { require(spender != address(0)); _allowed[msg.sender][spender] = _allowed[msg.sender][spender].sub(subtractedValue); emit Approval(msg.sender, spender, _allowed[msg.sender][spender]); return true; } /** * @dev Transfer token for a specified addresses * @param from The address to transfer from. * @param to The address to transfer to. * @param value The amount to be transferred. */ function _transfer(address from, address to, uint256 value) internal { require(to != address(0)); _balances[from] = _balances[from].sub(value); _balances[to] = _balances[to].add(value); emit Transfer(from, to, value); } /** * @dev Internal function that mints an amount of the token and assigns it to * an account. This encapsulates the modification of balances such that the * proper events are emitted. * @param account The account that will receive the created tokens. * @param value The amount that will be created. */ function _mint(address account, uint256 value) internal { require(account != address(0)); _totalSupply = _totalSupply.add(value); _balances[account] = _balances[account].add(value); emit Transfer(address(0), account, value); } /** * @dev Internal function that burns an amount of the token of a given * account. * @param account The account whose tokens will be burnt. * @param value The amount that will be burnt. */ function _burn(address account, uint256 value) internal { require(account != address(0)); _totalSupply = _totalSupply.sub(value); _balances[account] = _balances[account].sub(value); emit Transfer(account, address(0), value); } /** * @dev Internal function that burns an amount of the token of a given * account, deducting from the sender's allowance for said account. Uses the * internal burn function. * Emits an Approval event (reflecting the reduced allowance). * @param account The account whose tokens will be burnt. * @param value The amount that will be burnt. */ function _burnFrom(address account, uint256 value) internal { _allowed[account][msg.sender] = _allowed[account][msg.sender].sub(value); _burn(account, value); emit Approval(account, msg.sender, _allowed[account][msg.sender]); } } // File: openzeppelin-solidity/contracts/access/Roles.sol /** * @title Roles * @dev Library for managing addresses assigned to a Role. */ library Roles { struct Role { mapping (address => bool) bearer; } /** * @dev give an account access to this role */ function add(Role storage role, address account) internal { require(account != address(0)); require(!has(role, account)); role.bearer[account] = true; } /** * @dev remove an account's access to this role */ function remove(Role storage role, address account) internal { require(account != address(0)); require(has(role, account)); role.bearer[account] = false; } /** * @dev check if an account has this role * @return bool */ function has(Role storage role, address account) internal view returns (bool) { require(account != address(0)); return role.bearer[account]; } } // File: openzeppelin-solidity/contracts/access/roles/PauserRole.sol contract PauserRole { using Roles for Roles.Role; event PauserAdded(address indexed account); event PauserRemoved(address indexed account); Roles.Role private _pausers; constructor () internal { _addPauser(msg.sender); } modifier onlyPauser() { require(isPauser(msg.sender)); _; } function isPauser(address account) public view returns (bool) { return _pausers.has(account); } function addPauser(address account) public onlyPauser { _addPauser(account); } function renouncePauser() public { _removePauser(msg.sender); } function _addPauser(address account) internal { _pausers.add(account); emit PauserAdded(account); } function _removePauser(address account) internal { _pausers.remove(account); emit PauserRemoved(account); } } // File: openzeppelin-solidity/contracts/lifecycle/Pausable.sol /** * @title Pausable * @dev Base contract which allows children to implement an emergency stop mechanism. */ contract Pausable is PauserRole { event Paused(address account); event Unpaused(address account); bool private _paused; constructor () internal { _paused = false; } /** * @return true if the contract is paused, false otherwise. */ function paused() public view returns (bool) { return _paused; } /** * @dev Modifier to make a function callable only when the contract is not paused. */ modifier whenNotPaused() { require(!_paused); _; } /** * @dev Modifier to make a function callable only when the contract is paused. */ modifier whenPaused() { require(_paused); _; } /** * @dev called by the owner to pause, triggers stopped state */ function pause() public onlyPauser whenNotPaused { _paused = true; emit Paused(msg.sender); } /** * @dev called by the owner to unpause, returns to normal state */ function unpause() public onlyPauser whenPaused { _paused = false; emit Unpaused(msg.sender); } } // File: openzeppelin-solidity/contracts/token/ERC20/ERC20Pausable.sol /** * @title Pausable token * @dev ERC20 modified with pausable transfers. **/ contract ERC20Pausable is ERC20, Pausable { function transfer(address to, uint256 value) public whenNotPaused returns (bool) { return super.transfer(to, value); } function transferFrom(address from, address to, uint256 value) public whenNotPaused returns (bool) { return super.transferFrom(from, to, value); } function approve(address spender, uint256 value) public whenNotPaused returns (bool) { return super.approve(spender, value); } function increaseAllowance(address spender, uint addedValue) public whenNotPaused returns (bool success) { return super.increaseAllowance(spender, addedValue); } function decreaseAllowance(address spender, uint subtractedValue) public whenNotPaused returns (bool success) { return super.decreaseAllowance(spender, subtractedValue); } } // File: openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol /** * @title ERC20Detailed token * @dev The decimals are only for visualization purposes. * All the operations are done using the smallest and indivisible token unit, * just as on Ethereum all the operations are done in wei. */ contract ERC20Detailed is IERC20 { string private _name; string private _symbol; uint8 private _decimals; constructor (string memory name, string memory symbol, uint8 decimals) public { _name = name; _symbol = symbol; _decimals = decimals; } /** * @return the name of the token. */ function name() public view returns (string memory) { return _name; } /** * @return the symbol of the token. */ function symbol() public view returns (string memory) { return _symbol; } /** * @return the number of decimals of the token. */ function decimals() public view returns (uint8) { return _decimals; } } // File: contracts/MaticToken.sol contract MaticToken is ERC20Pausable, ERC20Detailed { constructor (string memory name, string memory symbol, uint8 decimals, uint256 totalSupply) public ERC20Detailed (name, symbol, decimals) { _mint(msg.sender, totalSupply); } }