Balance
1,179.614233206229645062 ETH
👉Balance Chart
Token Balance
Transactions 95,234
Rank 5371
// SPDX-License-Identifier: GPL-2.0-only // Copyright 2020 Spilsbury Holdings Ltd. pragma solidity >=0.6.10 <0.8.0; import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol'; import {Pausable} from '@openzeppelin/contracts/utils/Pausable.sol'; import {SafeMath} from '@openzeppelin/contracts/math/SafeMath.sol'; import {IVerifier} from './interfaces/IVerifier.sol'; import {IRollupProcessor} from './interfaces/IRollupProcessor.sol'; import {IFeeDistributor} from './interfaces/IFeeDistributor.sol'; import {IERC20Permit} from './interfaces/IERC20Permit.sol'; import {Decoder} from './Decoder.sol'; import './libraries/RollupProcessorLibrary.sol'; /** * @title Rollup Processor * @dev Smart contract responsible for processing Aztec zkRollups, including relaying them to a verifier * contract for validation and performing all relevant ERC20 token transfers */ contract RollupProcessor is IRollupProcessor, Decoder, Ownable, Pausable { using SafeMath for uint256; bytes32 public dataRoot = 0x2708a627d38d74d478f645ec3b4e91afa325331acf1acebe9077891146b75e39; bytes32 public nullRoot = 0x2694dbe3c71a25d92213422d392479e7b8ef437add81e1e17244462e6edca9b1; bytes32 public rootRoot = 0x2d264e93dc455751a721aead9dba9ee2a9fef5460921aeede73f63f6210e6851; uint256 public dataSize; uint256 public nextRollupId; IVerifier public verifier; uint256 public constant numberOfAssets = 4; uint256 public constant txNumPubInputs = 12; uint256 public constant rollupNumPubInputs = 10 + numberOfAssets; uint256 public constant txPubInputLength = txNumPubInputs * 32; // public inputs length for of each inner proof tx uint256 public constant rollupPubInputLength = rollupNumPubInputs * 32; uint256 public constant ethAssetId = 0; uint256 public immutable escapeBlockLowerBound; uint256 public immutable escapeBlockUpperBound; event RollupProcessed( uint256 indexed rollupId, bytes32 dataRoot, bytes32 nullRoot, bytes32 rootRoot, uint256 dataSize ); event Deposit(uint256 assetId, address depositorAddress, uint256 depositValue); event Withdraw(uint256 assetId, address withdrawAddress, uint256 withdrawValue); event WithdrawError(bytes errorReason); event AssetAdded(uint256 indexed assetId, address indexed assetAddress); event RollupProviderUpdated(address indexed providerAddress, bool valid); event VerifierUpdated(address indexed verifierAddress); // Array of supported ERC20 token address. address[] public supportedAssets; // Mapping which maps an asset address to a bool, determining whether it supports // permit as according to ERC-2612 mapping(address => bool) assetPermitSupport; // Mapping from assetId to mapping of userAddress to public userBalance stored on this contract mapping(uint256 => mapping(address => uint256)) public userPendingDeposits; mapping(address => mapping(bytes32 => bool)) public depositProofApprovals; mapping(address => bool) public rollupProviders; address public override feeDistributor; // Metrics uint256[] public totalPendingDeposit; uint256[] public totalDeposited; uint256[] public totalWithdrawn; uint256[] public totalFees; constructor( address _verifierAddress, uint256 _escapeBlockLowerBound, uint256 _escapeBlockUpperBound, address _contractOwner ) public { verifier = IVerifier(_verifierAddress); escapeBlockLowerBound = _escapeBlockLowerBound; escapeBlockUpperBound = _escapeBlockUpperBound; rollupProviders[msg.sender] = true; totalPendingDeposit.push(0); totalDeposited.push(0); totalWithdrawn.push(0); totalFees.push(0); transferOwnership(_contractOwner); } function setRollupProvider(address providerAddress, bool valid) public override onlyOwner { rollupProviders[providerAddress] = valid; emit RollupProviderUpdated(providerAddress, valid); } function setVerifier(address _verifierAddress) public override onlyOwner { verifier = IVerifier(_verifierAddress); emit VerifierUpdated(_verifierAddress); } function setFeeDistributor(address feeDistributorAddress) public override onlyOwner { feeDistributor = feeDistributorAddress; } /** * @dev Approve a proofHash for spending a users deposited funds, this is one way and must be called by the owner of the funds * @param _proofHash - keccack256 hash of the inner proof public inputs */ function approveProof(bytes32 _proofHash) public override whenNotPaused { depositProofApprovals[msg.sender][_proofHash] = true; } /** * @dev Get the ERC20 token address of a supported asset, for a given assetId * @param assetId - identifier used to denote a particular asset */ function getSupportedAsset(uint256 assetId) public view override returns (address) { if (assetId == ethAssetId) { return address(0x0); } return supportedAssets[assetId - 1]; } /** * @dev Get the addresses of all supported ERC20 tokens */ function getSupportedAssets() external view override returns (address[] memory) { return supportedAssets; } function getTotalDeposited() external view override returns (uint256[] memory) { return totalDeposited; } function getTotalWithdrawn() external view override returns (uint256[] memory) { return totalWithdrawn; } function getTotalPendingDeposit() external view override returns (uint256[] memory) { return totalPendingDeposit; } function getTotalFees() external view override returns (uint256[] memory) { return totalFees; } /** * @dev Get the status of whether an asset supports the permit ERC-2612 approval flow * @param assetId - unique identifier of the supported asset */ function getAssetPermitSupport(uint256 assetId) external view override returns (bool) { address assetAddress = getSupportedAsset(assetId); return assetPermitSupport[assetAddress]; } /** * @dev Get the status of the escape hatch, specifically retrieve whether the * hatch is open and also the number of blocks until the hatch will switch from * open to closed or vice versa */ function getEscapeHatchStatus() public view override returns (bool, uint256) { uint256 blockNum = block.number; bool isOpen = blockNum % escapeBlockUpperBound >= escapeBlockLowerBound; uint256 blocksRemaining = 0; if (isOpen) { // num blocks escape hatch will remain open for blocksRemaining = escapeBlockUpperBound - (blockNum % escapeBlockUpperBound); } else { // num blocks until escape hatch will be opened blocksRemaining = escapeBlockLowerBound - (blockNum % escapeBlockUpperBound); } return (isOpen, blocksRemaining); } /** * @dev Get the balance of a user, for a particular asset, held on the user's behalf * by this contract * @param assetId - unique identifier of the asset * @param userAddress - Ethereum address of the user who's balance is being queried */ function getUserPendingDeposit(uint256 assetId, address userAddress) external view override returns (uint256) { return userPendingDeposits[assetId][userAddress]; } /** * @dev Increase the userPendingDeposits mapping */ function increasePendingDepositBalance( uint256 assetId, address depositorAddress, uint256 amount ) internal { userPendingDeposits[assetId][depositorAddress] = userPendingDeposits[assetId][depositorAddress].add(amount); totalPendingDeposit[assetId] = totalPendingDeposit[assetId].add(amount); } /** * @dev Decrease the userPendingDeposits mapping */ function decreasePendingDepositBalance( uint256 assetId, address transferFromAddress, uint256 amount ) internal { uint256 userBalance = userPendingDeposits[assetId][transferFromAddress]; require(userBalance >= amount, 'Rollup Processor: INSUFFICIENT_DEPOSIT'); userPendingDeposits[assetId][transferFromAddress] = userBalance.sub(amount); totalPendingDeposit[assetId] = totalPendingDeposit[assetId].sub(amount); totalDeposited[assetId] = totalDeposited[assetId].add(amount); } /** * @dev Set the mapping between an assetId and the address of the linked asset. * Protected by onlyOwner * @param linkedToken - address of the asset * @param supportsPermit - bool determining whether this supports permit */ function setSupportedAsset(address linkedToken, bool supportsPermit) external override onlyOwner { require(linkedToken != address(0x0), 'Rollup Processor: ZERO_ADDRESS'); supportedAssets.push(linkedToken); assetPermitSupport[linkedToken] = supportsPermit; uint256 assetId = supportedAssets.length; require(assetId < numberOfAssets, 'Rollup Processor: MAX_ASSET_REACHED'); totalPendingDeposit.push(0); totalDeposited.push(0); totalWithdrawn.push(0); totalFees.push(0); emit AssetAdded(assetId, linkedToken); } /** * @dev Update the value indicating whether a linked asset supports permit. * Protected by onlyOwner * @param assetId - unique ID of the asset * @param supportsPermit - bool determining whether this supports permit */ function setAssetPermitSupport(uint256 assetId, bool supportsPermit) external override onlyOwner { address assetAddress = getSupportedAsset(assetId); require(assetAddress != address(0x0), 'Rollup Processor: TOKEN_ASSET_NOT_LINKED'); assetPermitSupport[assetAddress] = supportsPermit; } /** * @dev Deposit funds as part of the first stage of the two stage deposit. Non-permit flow * @param assetId - unique ID of the asset * @param amount - number of tokens being deposited * @param depositorAddress - address from which funds are being transferred to the contract */ function depositPendingFunds( uint256 assetId, uint256 amount, address depositorAddress ) external payable override whenNotPaused { if (assetId == ethAssetId) { require(msg.value == amount, 'Rollup Processor: WRONG_AMOUNT'); increasePendingDepositBalance(assetId, depositorAddress, amount); } else { require(msg.value == 0, 'Rollup Processor: WRONG_PAYMENT_TYPE'); address assetAddress = getSupportedAsset(assetId); internalDeposit(assetId, assetAddress, depositorAddress, amount); } } /** * @dev Deposit funds as part of the first stage of the two stage deposit. Permit flow * @param assetId - unique ID of the asset * @param amount - number of tokens being deposited * @param depositorAddress - address from which funds are being transferred to the contract * @param spender - address being granted approval to spend the funds * @param permitApprovalAmount - amount permit signature is approving * @param deadline - when the permit signature expires * @param v - ECDSA sig param * @param r - ECDSA sig param * @param s - ECDSA sig param */ function depositPendingFundsPermit( uint256 assetId, uint256 amount, address depositorAddress, address spender, uint256 permitApprovalAmount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external override whenNotPaused { address assetAddress = getSupportedAsset(assetId); IERC20Permit(assetAddress).permit(depositorAddress, spender, permitApprovalAmount, deadline, v, r, s); internalDeposit(assetId, assetAddress, depositorAddress, amount); } /** * @dev Deposit funds as part of the first stage of the two stage deposit. Non-permit flow * @param assetId - unique ID of the asset * @param assetAddress - address of the ERC20 asset * @param depositorAddress - address from which funds are being transferred to the contract * @param amount - amount being deposited */ function internalDeposit( uint256 assetId, address assetAddress, address depositorAddress, uint256 amount ) internal { // check user approved contract to transfer funds, so can throw helpful error to user uint256 rollupAllowance = IERC20(assetAddress).allowance(depositorAddress, address(this)); require(rollupAllowance >= amount, 'Rollup Processor: INSUFFICIENT_TOKEN_APPROVAL'); IERC20(assetAddress).transferFrom(depositorAddress, address(this), amount); increasePendingDepositBalance(assetId, depositorAddress, amount); emit Deposit(assetId, depositorAddress, amount); } /** * @dev Process a rollup - decode the rollup, update relevant state variables and * verify the proof * @param proofData - cryptographic proof data associated with a rollup * @param signatures - bytes array of secp256k1 ECDSA signatures, authorising a transfer of tokens * from the publicOwner for the particular inner proof in question. There is a signature for each * inner proof. * * Structure of each signature in the bytes array is: * 0x00 - 0x20 : r * 0x20 - 0x40 : s * 0x40 - 0x60 : v (in form: 0x0000....0001b for example) * * @param viewingKeys - viewingKeys for the notes submitted in the rollup. Note: not used in the logic * of the rollupProcessor contract, but called here as a convenient to place data on chain */ function escapeHatch( bytes calldata proofData, bytes calldata signatures, bytes calldata viewingKeys ) external override whenNotPaused { (bool isOpen, ) = getEscapeHatchStatus(); require(isOpen, 'Rollup Processor: ESCAPE_BLOCK_RANGE_INCORRECT'); processRollupProof(proofData, signatures, viewingKeys); } function processRollup( bytes calldata proofData, bytes calldata signatures, bytes calldata viewingKeys, bytes calldata providerSignature, address provider, address payable feeReceiver, uint256 feeLimit ) external override whenNotPaused { uint256 initialGas = gasleft(); require(rollupProviders[provider], 'Rollup Processor: UNKNOWN_PROVIDER'); bytes memory sigData = abi.encodePacked(proofData[0:rollupPubInputLength], feeReceiver, feeLimit, feeDistributor); RollupProcessorLibrary.validateSignature(keccak256(sigData), providerSignature, provider); processRollupProof(proofData, signatures, viewingKeys); transferFee(proofData); (bool success, ) = feeDistributor.call( abi.encodeWithSignature( 'reimburseGas(uint256,uint256,address)', initialGas - gasleft(), feeLimit, feeReceiver ) ); require(success, 'Rollup Processor: REIMBURSE_GAS_FAILED'); } function processRollupProof( bytes memory proofData, bytes memory signatures, bytes calldata /*viewingKeys*/ ) internal { uint256 numTxs = verifyProofAndUpdateState(proofData); processDepositsAndWithdrawals(proofData, numTxs, signatures); } /** * @dev Verify the zk proof and update the contract state variables with those provided by the rollup. * @param proofData - cryptographic zk proof data. Passed to the verifier for verification. */ function verifyProofAndUpdateState(bytes memory proofData) internal returns (uint256) { ( bytes32 newDataRoot, bytes32 newNullRoot, uint256 rollupId, uint256 rollupSize, bytes32 newRootRoot, uint256 numTxs, uint256 newDataSize ) = validateMerkleRoots(proofData); // Verify the rollup proof. // // We manually call the verifier contract via assembly. This is to prevent a // redundant copy of `proofData` into memory, which costs between 100,000 to 1,000,000 gas // depending on the rollup size! bool proof_verified = false; address verifierAddress; uint256 temp1; uint256 temp2; uint256 temp3; assembly { // Step 1: we need to insert 68 bytes of verifier 'calldata' just prior to proofData // Start by defining the start of our 'calldata'. Also grab the verifier contract address from storage let inputPtr := sub(proofData, 0x44) verifierAddress := sload(verifier_slot) // Step 2: we need to overwrite the memory between `inputPtr` and `inputPtr + 68` // we load the existing 68 bytes of memory into stack variables temp1, temp2, temp3 // Once we have called the verifier contract, we will write this data back into memory temp1 := mload(inputPtr) temp2 := mload(add(inputPtr, 0x20)) temp3 := mload(add(inputPtr, 0x40)) // Step 3: insert our calldata into memory // We call the function `verify(bytes,uint256)` // The function signature is 0xac318c5d // Calldata map is: // 0x00 - 0x04 : 0xac318c5d // 0x04 - 0x24 : 0x40 (number of bytes between 0x04 and the start of the `proofData` array at 0x44) // 0x24 - 0x44 : rollupSize // 0x44 - .... : proofData (already present in memory) mstore8(inputPtr, 0xac) mstore8(add(inputPtr, 0x01), 0x31) mstore8(add(inputPtr, 0x02), 0x8c) mstore8(add(inputPtr, 0x03), 0x5d) mstore(add(inputPtr, 0x04), 0x40) mstore(add(inputPtr, 0x24), rollupSize) // Total calldata size is proofData.length + 96 bytes (the 66 bytes we just wrote, plus the 32 byte 'length' field of proofData) let callSize := add(mload(proofData), 0x64) // Step 4: Call our verifier contract. If does not return any values, but will throw an error if the proof is not valid // i.e. verified == false if proof is not valid proof_verified := staticcall(gas(), verifierAddress, inputPtr, callSize, 0x00, 0x00) // Step 5: Restore the memory we overwrote with our 'calldata' mstore(inputPtr, temp1) mstore(add(inputPtr, 0x20), temp2) mstore(add(inputPtr, 0x40), temp3) } // Check the proof is valid! require(proof_verified, 'proof verification failed'); // Update state variables. dataRoot = newDataRoot; nullRoot = newNullRoot; nextRollupId = rollupId.add(1); rootRoot = newRootRoot; dataSize = newDataSize; emit RollupProcessed(rollupId, dataRoot, nullRoot, rootRoot, dataSize); return numTxs; } /** * @dev Extract public inputs and validate they are inline with current contract state. * @param proofData - Rollup proof data. */ function validateMerkleRoots(bytes memory proofData) internal view returns ( bytes32, bytes32, uint256, uint256, bytes32, uint256, uint256 ) { ( // Stack to deep workaround: // 0: rollupId // 1: rollupSize // 2: dataStartIndex // 3: numTxs uint256[4] memory nums, bytes32 oldDataRoot, bytes32 newDataRoot, bytes32 oldNullRoot, bytes32 newNullRoot, bytes32 oldRootRoot, bytes32 newRootRoot ) = decodeProof(proofData, numberOfAssets); // Escape hatch denominated by a rollup size of 0, which means inserting 2 new entries. nums[3] = nums[1] == 0 ? 1 : nums[1]; // Ensure we are inserting at the next subtree boundary. uint256 toInsert = nums[3].mul(2); if (dataSize % toInsert == 0) { require(nums[2] == dataSize, 'Rollup Processor: INCORRECT_DATA_START_INDEX'); } else { uint256 expected = dataSize + toInsert - (dataSize % toInsert); require(nums[2] == expected, 'Rollup Processor: INCORRECT_DATA_START_INDEX'); } // Data validation checks. require(oldDataRoot == dataRoot, 'Rollup Processor: INCORRECT_DATA_ROOT'); require(oldNullRoot == nullRoot, 'Rollup Processor: INCORRECT_NULL_ROOT'); require(oldRootRoot == rootRoot, 'Rollup Processor: INCORRECT_ROOT_ROOT'); require(nums[0] == nextRollupId, 'Rollup Processor: ID_NOT_SEQUENTIAL'); return (newDataRoot, newNullRoot, nums[0], nums[1], newRootRoot, nums[3], nums[2] + toInsert); } /** * @dev Process deposits and withdrawls. * @param proofData - the proof data * @param numTxs - number of transactions rolled up in the proof * @param signatures - bytes array of secp256k1 ECDSA signatures, authorising a transfer of tokens */ function processDepositsAndWithdrawals( bytes memory proofData, uint256 numTxs, bytes memory signatures ) internal { uint256 sigIndex = 0x00; uint256 proofDataPtr; assembly { proofDataPtr := add(proofData, 0x20) // add 0x20 to skip over 1st field in bytes array (the length field) } proofDataPtr += rollupPubInputLength; // update pointer to skip over rollup public inputs and point to inner tx public inputs uint256 end = proofDataPtr + (numTxs * txPubInputLength); uint256 stepSize = txPubInputLength; // This is a bit of a hot loop, we iterate over every tx to determine whether to process deposits or withdrawals. while (proofDataPtr < end) { // extract the minimum information we need to determine whether to skip this iteration uint256 proofId; uint256 publicInput; uint256 publicOutput; bool txNeedsProcessing; assembly { proofId := mload(proofDataPtr) publicInput := mload(add(proofDataPtr, 0x20)) publicOutput := mload(add(proofDataPtr, 0x40)) // only process deposits and withdrawals iff // the proofId == 0 (not an account proof) and publicInput > 0 OR publicOutput > 0 txNeedsProcessing := and(iszero(proofId), or(not(iszero(publicInput)), not(iszero(publicOutput)))) } if (txNeedsProcessing) { // extract asset Id uint256 assetId; assembly { assetId := mload(add(proofDataPtr, 0x60)) } if (publicInput > 0) { // validate user has approved deposit address inputOwner; bytes32 digest; assembly { inputOwner := mload(add(proofDataPtr, 0x140)) // compute the message digest to check if user has approved tx digest := keccak256(proofDataPtr, stepSize) } if (!depositProofApprovals[inputOwner][digest]) { // extract and validate signature // we can create a bytes memory container for the signature without allocating new memory, // by overwriting the previous 32 bytes in the `signatures` array with the 'length' of our synthetic byte array (92) // we store the memory we overwrite in `temp`, so that we can restore it bytes memory signature; uint256 temp; assembly { // set `signature` to point to 32 bytes less than the desired `r, s, v` values in `signatures` signature := add(signatures, sigIndex) // cache the memory we're about to overwrite temp := mload(signature) // write in a 92-byte 'length' parameter into the `signature` bytes array mstore(signature, 0x60) } // validate the signature RollupProcessorLibrary.validateUnpackedSignature(digest, signature, inputOwner); // restore the memory we overwrote assembly { mstore(signature, temp) sigIndex := add(sigIndex, 0x60) } } decreasePendingDepositBalance(assetId, inputOwner, publicInput); } if (publicOutput > 0) { address outputOwner; assembly { outputOwner := mload(add(proofDataPtr, 0x160)) } withdraw(publicOutput, outputOwner, assetId); } } proofDataPtr += txPubInputLength; } } function transferFee(bytes memory proofData) internal { for (uint256 i = 0; i < numberOfAssets; ++i) { uint256 txFee = extractTotalTxFee(proofData, i); if (txFee > 0) { bool success; if (i == ethAssetId) { (success, ) = payable(feeDistributor).call{value: txFee}(''); } else { address assetAddress = getSupportedAsset(i); IERC20(assetAddress).approve(feeDistributor, txFee); (success, ) = feeDistributor.call(abi.encodeWithSignature('deposit(uint256,uint256)', i, txFee)); } require(success, 'Rollup Processor: DEPOSIT_TX_FEE_FAILED'); totalFees[i] = totalFees[i].add(txFee); } } } /** * @dev Internal utility function to withdraw funds from the contract to a receiver address * @param withdrawValue - value being withdrawn from the contract * @param receiverAddress - address receiving public ERC20 tokens * @param assetId - ID of the asset for which a withdrawl is being performed */ function withdraw( uint256 withdrawValue, address receiverAddress, uint256 assetId ) internal { require(receiverAddress != address(0), 'Rollup Processor: ZERO_ADDRESS'); if (assetId == 0) { // We explicitly do not throw if this call fails, as this opens up the possiblity of // griefing attacks, as engineering a failed withdrawal will invalidate an entire rollup block payable(receiverAddress).call{gas: 30000, value: withdrawValue}(''); } else { address assetAddress = getSupportedAsset(assetId); IERC20(assetAddress).transfer(receiverAddress, withdrawValue); } totalWithdrawn[assetId] = totalWithdrawn[assetId].add(withdrawValue); } }
// 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 "../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; import "./Context.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 Pausable is Context { /** * @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. */ constructor () internal { _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()); } }
// 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: GPL-2.0-only // Copyright 2020 Spilsbury Holdings Ltd pragma solidity >=0.6.10 <0.8.0; interface IVerifier { function verify(bytes memory serialized_proof, uint256 _keyId) external; }
// SPDX-License-Identifier: GPL-2.0-only // Copyright 2020 Spilsbury Holdings Ltd pragma solidity >=0.6.10 <0.8.0; interface IRollupProcessor { function feeDistributor() external view returns (address); function escapeHatch( bytes calldata proofData, bytes calldata signatures, bytes calldata viewingKeys ) external; function processRollup( bytes calldata proofData, bytes calldata signatures, bytes calldata viewingKeys, bytes calldata providerSignature, address provider, address payable feeReceiver, uint256 feeLimit ) external; function depositPendingFunds( uint256 assetId, uint256 amount, address owner ) external payable; function depositPendingFundsPermit( uint256 assetId, uint256 amount, address owner, address spender, uint256 permitApprovalAmount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; function setRollupProvider(address provderAddress, bool valid) external; function approveProof(bytes32 _proofHash) external; function setFeeDistributor(address feeDistributorAddress) external; function setVerifier(address verifierAddress) external; function setSupportedAsset(address linkedToken, bool supportsPermit) external; function setAssetPermitSupport(uint256 assetId, bool supportsPermit) external; function getSupportedAsset(uint256 assetId) external view returns (address); function getSupportedAssets() external view returns (address[] memory); function getTotalDeposited() external view returns (uint256[] memory); function getTotalWithdrawn() external view returns (uint256[] memory); function getTotalPendingDeposit() external view returns (uint256[] memory); function getTotalFees() external view returns (uint256[] memory); function getAssetPermitSupport(uint256 assetId) external view returns (bool); function getEscapeHatchStatus() external view returns (bool, uint256); function getUserPendingDeposit(uint256 assetId, address userAddress) external view returns (uint256); }
// SPDX-License-Identifier: GPL-2.0-only // Copyright 2020 Spilsbury Holdings Ltd pragma solidity >=0.6.10 <0.8.0; interface IFeeDistributor { event FeeReimbursed(address receiver, uint256 amount); function txFeeBalance(uint256 assetId) external view returns (uint256); function deposit(uint256 assetId, uint256 amount) external payable returns (uint256 depositedAmount); function reimburseGas( uint256 gasUsed, uint256 feeLimit, address payable feeReceiver ) external returns (uint256 reimbursement); }
// SPDX-License-Identifier: GPL-2.0-only // Copyright 2020 Spilsbury Holdings Ltd pragma solidity >=0.6.10 <0.8.0; import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; interface IERC20Permit is IERC20 { function nonces(address user) external view returns (uint256); function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; }
// SPDX-License-Identifier: GPL-2.0-only // Copyright 2020 Spilsbury Holdings Ltd pragma solidity >=0.6.10 <0.8.0; import {SafeMath} from '@openzeppelin/contracts/math/SafeMath.sol'; import {Types} from './verifier/cryptography/Types.sol'; import {Bn254Crypto} from './verifier/cryptography/Bn254Crypto.sol'; contract Decoder { using SafeMath for uint256; /** * @dev Decode the public inputs component of proofData. Required to update state variables * @param proofData - cryptographic proofData associated with a rollup */ function decodeProof(bytes memory proofData, uint256 numberOfAssets) internal pure returns ( uint256[4] memory nums, bytes32 oldDataRoot, bytes32 newDataRoot, bytes32 oldNullRoot, bytes32 newNullRoot, bytes32 oldRootRoot, bytes32 newRootRoot ) { uint256 rollupId; uint256 rollupSize; uint256 dataStartIndex; uint256 numTxs; assembly { let dataStart := add(proofData, 0x20) // jump over first word, it's length of data rollupId := mload(dataStart) rollupSize := mload(add(dataStart, 0x20)) dataStartIndex := mload(add(dataStart, 0x40)) oldDataRoot := mload(add(dataStart, 0x60)) newDataRoot := mload(add(dataStart, 0x80)) oldNullRoot := mload(add(dataStart, 0xa0)) newNullRoot := mload(add(dataStart, 0xc0)) oldRootRoot := mload(add(dataStart, 0xe0)) newRootRoot := mload(add(dataStart, 0x100)) numTxs := mload(add(add(dataStart, 0x120), mul(0x20, numberOfAssets))) } return ( [rollupId, rollupSize, dataStartIndex, numTxs], oldDataRoot, newDataRoot, oldNullRoot, newNullRoot, oldRootRoot, newRootRoot ); } function extractTotalTxFee(bytes memory proofData, uint256 assetId) internal pure returns (uint256) { uint256 totalTxFee; assembly { totalTxFee := mload(add(add(proofData, 0x140), mul(0x20, assetId))) } return totalTxFee; } }
// SPDX-License-Identifier: GPL-2.0-only // Copyright 2020 Spilsbury Holdings Ltd pragma solidity >=0.6.10 <0.8.0; library RollupProcessorLibrary { /** * Extracts the address of the signer with ECDSA. Performs checks on `s` and `v` to * to prevent signature malleability based attacks * @param digest - Hashed data being signed over. * @param signature - ECDSA signature over the secp256k1 elliptic curve. * @param signer - Address that signs the signature. */ function validateSignature( bytes32 digest, bytes memory signature, address signer ) internal view { bool result; address recoveredSigner = address(0x0); require(signer != address(0x0), 'validateSignature: ZERO_ADDRESS'); // prepend "\x19Ethereum Signed Message:\n32" to the digest to create the signed message bytes32 message; assembly { mstore(0, "\x19Ethereum Signed Message:\n32") mstore(add(0, 28), digest) message := keccak256(0, 60) } assembly { let mPtr := mload(0x40) let byteLength := mload(signature) // store the signature digest mstore(mPtr, message) // load 'v' - we need it for a condition check // add 0x60 to jump over 3 words - length of bytes array, r and s let v := shr(248, mload(add(signature, 0x60))) // bitshifting, to resemble padLeft let s := mload(add(signature, 0x40)) /** * Original memory map for input to precompile * * signature : signature + 0x20 message * signature + 0x20 : signature + 0x40 r * signature + 0x40 : signature + 0x60 s * signature + 0x60 : signature + 0x80 v * Desired memory map for input to precompile * * signature : signature + 0x20 message * signature + 0x20 : signature + 0x40 v * signature + 0x40 : signature + 0x60 r * signature + 0x60 : signature + 0x80 s */ // store s mstore(add(mPtr, 0x60), s) // store r mstore(add(mPtr, 0x40), mload(add(signature, 0x20))) // store v mstore(add(mPtr, 0x20), v) result := and( and( // validate s is in lower half order lt(s, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A1), and( // validate signature length == 0x41 eq(byteLength, 0x41), // validate v == 27 or v == 28 or(eq(v, 27), eq(v, 28)) ) ), // validate call to ecrecover precompile succeeds staticcall(gas(), 0x01, mPtr, 0x80, mPtr, 0x20) ) // save the recoveredSigner only if the first word in signature is not `message` anymore switch eq(message, mload(mPtr)) case 0 { recoveredSigner := mload(mPtr) } mstore(mPtr, byteLength) // and put the byte length back where it belongs // validate that recoveredSigner is not address(0x00) result := and(result, not(iszero(recoveredSigner))) } require(result, 'validateSignature: signature recovery failed'); require(recoveredSigner == signer, 'validateSignature: INVALID_SIGNATURE'); } /** * Extracts the address of the signer with ECDSA. Performs checks on `s` and `v` to * to prevent signature malleability based attacks * This 'Unpacked' version expects 'signature' to be a 92-byte array. * i.e. the `v` parameter occupies a full 32 bytes of memory, not 1 byte * @param digest - Hashed data being signed over. * @param signature - ECDSA signature over the secp256k1 elliptic curve. * @param signer - Address that signs the signature. */ function validateUnpackedSignature( bytes32 digest, bytes memory signature, address signer ) internal view { bool result; address recoveredSigner = address(0x0); require(signer != address(0x0), 'validateSignature: ZERO_ADDRESS'); // prepend "\x19Ethereum Signed Message:\n32" to the digest to create the signed message bytes32 message; assembly { mstore(0, "\x19Ethereum Signed Message:\n32") mstore(28, digest) message := keccak256(0, 60) } assembly { // There's a little trick we can pull. We expect `signature` to be a byte array, of length 0x60, with // 'v', 'r' and 's' located linearly in memory. Preceeding this is the length parameter of `signature`. // We *replace* the length param with the signature msg to get a memory block formatted for the precompile // load length as a temporary variable // N.B. we mutate the signature by re-ordering r, s, and v! let byteLength := mload(signature) // store the signature digest mstore(signature, message) // load 'v' - we need it for a condition check // add 0x60 to jump over 3 words - length of bytes array, r and s let v := mload(add(signature, 0x60)) let s := mload(add(signature, 0x40)) /** * Original memory map for input to precompile * * signature : signature + 0x20 message * signature + 0x20 : signature + 0x40 r * signature + 0x40 : signature + 0x60 s * signature + 0x60 : signature + 0x80 v * Desired memory map for input to precompile * * signature : signature + 0x20 message * signature + 0x20 : signature + 0x40 v * signature + 0x40 : signature + 0x60 r * signature + 0x60 : signature + 0x80 s */ // move s to v position mstore(add(signature, 0x60), s) // move r to s position mstore(add(signature, 0x40), mload(add(signature, 0x20))) // move v to r position mstore(add(signature, 0x20), v) result := and( and( // validate s is in lower half order lt(s, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A1), and( // validate signature length == 0x60 (unpacked) eq(byteLength, 0x60), // validate v == 27 or v == 28 or(eq(v, 27), eq(v, 28)) ) ), // validate call to ecrecover precompile succeeds staticcall(gas(), 0x01, signature, 0x80, signature, 0x20) ) // save the recoveredSigner only if the first word in signature is not `message` anymore switch eq(message, mload(signature)) case 0 { recoveredSigner := mload(signature) } mstore(signature, byteLength) // and put the byte length back where it belongs // validate that recoveredSigner is not address(0x00) result := and(result, not(iszero(recoveredSigner))) } require(result, 'validateSignature: signature recovery failed'); require(recoveredSigner == signer, 'validateSignature: INVALID_SIGNATURE'); } }
// 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: GPL-2.0-only // Copyright 2020 Spilsbury Holdings Ltd pragma solidity >=0.6.0 <0.8.0; pragma experimental ABIEncoderV2; /** * @title Bn254Crypto library used for the fr, g1 and g2 point types * @dev Used to manipulate fr, g1, g2 types, perform modular arithmetic on them and call * the precompiles add, scalar mul and pairing * * Notes on optimisations * 1) Perform addmod, mulmod etc. in assembly - removes the check that Solidity performs to confirm that * the supplied modulus is not 0. This is safe as the modulus's used (r_mod, q_mod) are hard coded * inside the contract and not supplied by the user */ library Types { uint256 constant PROGRAM_WIDTH = 4; uint256 constant NUM_NU_CHALLENGES = 11; uint256 constant coset_generator0 = 0x0000000000000000000000000000000000000000000000000000000000000005; uint256 constant coset_generator1 = 0x0000000000000000000000000000000000000000000000000000000000000006; uint256 constant coset_generator2 = 0x0000000000000000000000000000000000000000000000000000000000000007; // TODO: add external_coset_generator() method to compute this uint256 constant coset_generator7 = 0x000000000000000000000000000000000000000000000000000000000000000c; struct G1Point { uint256 x; uint256 y; } // G2 group element where x \in Fq2 = x0 * z + x1 struct G2Point { uint256 x0; uint256 x1; uint256 y0; uint256 y1; } // N>B. Do not re-order these fields! They must appear in the same order as they // appear in the proof data struct Proof { G1Point W1; G1Point W2; G1Point W3; G1Point W4; G1Point Z; G1Point T1; G1Point T2; G1Point T3; G1Point T4; uint256 w1; uint256 w2; uint256 w3; uint256 w4; uint256 sigma1; uint256 sigma2; uint256 sigma3; uint256 q_arith; uint256 q_ecc; uint256 q_c; uint256 linearization_polynomial; uint256 grand_product_at_z_omega; uint256 w1_omega; uint256 w2_omega; uint256 w3_omega; uint256 w4_omega; G1Point PI_Z; G1Point PI_Z_OMEGA; G1Point recursive_P1; G1Point recursive_P2; uint256 quotient_polynomial_eval; } struct ChallengeTranscript { uint256 alpha_base; uint256 alpha; uint256 zeta; uint256 beta; uint256 gamma; uint256 u; uint256 v0; uint256 v1; uint256 v2; uint256 v3; uint256 v4; uint256 v5; uint256 v6; uint256 v7; uint256 v8; uint256 v9; uint256 v10; } struct VerificationKey { uint256 circuit_size; uint256 num_inputs; uint256 work_root; uint256 domain_inverse; uint256 work_root_inverse; G1Point Q1; G1Point Q2; G1Point Q3; G1Point Q4; G1Point Q5; G1Point QM; G1Point QC; G1Point QARITH; G1Point QECC; G1Point QRANGE; G1Point QLOGIC; G1Point SIGMA1; G1Point SIGMA2; G1Point SIGMA3; G1Point SIGMA4; bool contains_recursive_proof; uint256 recursive_proof_indices; G2Point g2_x; // zeta challenge raised to the power of the circuit size. // Not actually part of the verification key, but we put it here to prevent stack depth errors uint256 zeta_pow_n; } }
// SPDX-License-Identifier: GPL-2.0-only // Copyright 2020 Spilsbury Holdings Ltd pragma solidity >=0.6.0 <0.8.0; pragma experimental ABIEncoderV2; import {Types} from "./Types.sol"; /** * @title Bn254 elliptic curve crypto * @dev Provides some basic methods to compute bilinear pairings, construct group elements and misc numerical methods */ library Bn254Crypto { uint256 constant p_mod = 21888242871839275222246405745257275088696311157297823662689037894645226208583; uint256 constant r_mod = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Perform a modular exponentiation. This method is ideal for small exponents (~64 bits or less), as // it is cheaper than using the pow precompile function pow_small( uint256 base, uint256 exponent, uint256 modulus ) internal pure returns (uint256) { uint256 result = 1; uint256 input = base; uint256 count = 1; assembly { let endpoint := add(exponent, 0x01) for {} lt(count, endpoint) { count := add(count, count) } { if and(exponent, count) { result := mulmod(result, input, modulus) } input := mulmod(input, input, modulus) } } return result; } function invert(uint256 fr) internal view returns (uint256) { uint256 output; bool success; uint256 p = r_mod; assembly { let mPtr := mload(0x40) mstore(mPtr, 0x20) mstore(add(mPtr, 0x20), 0x20) mstore(add(mPtr, 0x40), 0x20) mstore(add(mPtr, 0x60), fr) mstore(add(mPtr, 0x80), sub(p, 2)) mstore(add(mPtr, 0xa0), p) success := staticcall(gas(), 0x05, mPtr, 0xc0, 0x00, 0x20) output := mload(0x00) } require(success, "pow precompile call failed!"); return output; } function new_g1(uint256 x, uint256 y) internal pure returns (Types.G1Point memory) { uint256 xValue; uint256 yValue; assembly { xValue := mod(x, r_mod) yValue := mod(y, r_mod) } return Types.G1Point(xValue, yValue); } function new_g2(uint256 x0, uint256 x1, uint256 y0, uint256 y1) internal pure returns (Types.G2Point memory) { return Types.G2Point(x0, x1, y0, y1); } function P1() internal pure returns (Types.G1Point memory) { return Types.G1Point(1, 2); } function P2() internal pure returns (Types.G2Point memory) { return Types.G2Point({ x0: 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2, x1: 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed, y0: 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b, y1: 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa }); } /// Evaluate the following pairing product: /// e(a1, a2).e(-b1, b2) == 1 function pairingProd2( Types.G1Point memory a1, Types.G2Point memory a2, Types.G1Point memory b1, Types.G2Point memory b2 ) internal view returns (bool) { validateG1Point(a1); validateG1Point(b1); bool success; uint256 out; assembly { let mPtr := mload(0x40) mstore(mPtr, mload(a1)) mstore(add(mPtr, 0x20), mload(add(a1, 0x20))) mstore(add(mPtr, 0x40), mload(a2)) mstore(add(mPtr, 0x60), mload(add(a2, 0x20))) mstore(add(mPtr, 0x80), mload(add(a2, 0x40))) mstore(add(mPtr, 0xa0), mload(add(a2, 0x60))) mstore(add(mPtr, 0xc0), mload(b1)) mstore(add(mPtr, 0xe0), mload(add(b1, 0x20))) mstore(add(mPtr, 0x100), mload(b2)) mstore(add(mPtr, 0x120), mload(add(b2, 0x20))) mstore(add(mPtr, 0x140), mload(add(b2, 0x40))) mstore(add(mPtr, 0x160), mload(add(b2, 0x60))) success := staticcall( gas(), 8, mPtr, 0x180, 0x00, 0x20 ) out := mload(0x00) } require(success, "Pairing check failed!"); return (out != 0); } /** * validate the following: * x != 0 * y != 0 * x < p * y < p * y^2 = x^3 + 3 mod p */ function validateG1Point(Types.G1Point memory point) internal pure { bool is_well_formed; uint256 p = p_mod; assembly { let x := mload(point) let y := mload(add(point, 0x20)) is_well_formed := and( and( and(lt(x, p), lt(y, p)), not(or(iszero(x), iszero(y))) ), eq(mulmod(y, y, p), addmod(mulmod(x, mulmod(x, x, p), p), 3, p)) ) } require(is_well_formed, "Bn254: G1 point not on curve, or is malformed"); } }
[{"inputs":[{"internalType":"address","name":"_verifierAddress","type":"address"},{"internalType":"uint256","name":"_escapeBlockLowerBound","type":"uint256"},{"internalType":"uint256","name":"_escapeBlockUpperBound","type":"uint256"},{"internalType":"address","name":"_contractOwner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"assetId","type":"uint256"},{"indexed":true,"internalType":"address","name":"assetAddress","type":"address"}],"name":"AssetAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"assetId","type":"uint256"},{"indexed":false,"internalType":"address","name":"depositorAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"depositValue","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"rollupId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"dataRoot","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"nullRoot","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"rootRoot","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"dataSize","type":"uint256"}],"name":"RollupProcessed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"providerAddress","type":"address"},{"indexed":false,"internalType":"bool","name":"valid","type":"bool"}],"name":"RollupProviderUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"verifierAddress","type":"address"}],"name":"VerifierUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"assetId","type":"uint256"},{"indexed":false,"internalType":"address","name":"withdrawAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"withdrawValue","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes","name":"errorReason","type":"bytes"}],"name":"WithdrawError","type":"event"},{"inputs":[{"internalType":"bytes32","name":"_proofHash","type":"bytes32"}],"name":"approveProof","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"dataRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dataSize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"depositorAddress","type":"address"}],"name":"depositPendingFunds","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"depositorAddress","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"permitApprovalAmount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"depositPendingFundsPermit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"depositProofApprovals","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"escapeBlockLowerBound","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"escapeBlockUpperBound","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"proofData","type":"bytes"},{"internalType":"bytes","name":"signatures","type":"bytes"},{"internalType":"bytes","name":"viewingKeys","type":"bytes"}],"name":"escapeHatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"ethAssetId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeDistributor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetId","type":"uint256"}],"name":"getAssetPermitSupport","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getEscapeHatchStatus","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetId","type":"uint256"}],"name":"getSupportedAsset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSupportedAssets","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalDeposited","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalFees","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalPendingDeposit","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalWithdrawn","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetId","type":"uint256"},{"internalType":"address","name":"userAddress","type":"address"}],"name":"getUserPendingDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextRollupId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nullRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numberOfAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"proofData","type":"bytes"},{"internalType":"bytes","name":"signatures","type":"bytes"},{"internalType":"bytes","name":"viewingKeys","type":"bytes"},{"internalType":"bytes","name":"providerSignature","type":"bytes"},{"internalType":"address","name":"provider","type":"address"},{"internalType":"address payable","name":"feeReceiver","type":"address"},{"internalType":"uint256","name":"feeLimit","type":"uint256"}],"name":"processRollup","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rollupNumPubInputs","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rollupProviders","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rollupPubInputLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rootRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetId","type":"uint256"},{"internalType":"bool","name":"supportsPermit","type":"bool"}],"name":"setAssetPermitSupport","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"feeDistributorAddress","type":"address"}],"name":"setFeeDistributor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"providerAddress","type":"address"},{"internalType":"bool","name":"valid","type":"bool"}],"name":"setRollupProvider","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"linkedToken","type":"address"},{"internalType":"bool","name":"supportsPermit","type":"bool"}],"name":"setSupportedAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_verifierAddress","type":"address"}],"name":"setVerifier","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"supportedAssets","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"totalDeposited","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"totalFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"totalPendingDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"totalWithdrawn","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"txNumPubInputs","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"txPubInputLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"userPendingDeposits","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"verifier","outputs":[{"internalType":"contract IVerifier","name":"","type":"address"}],"stateMutability":"view","type":"function"}]
000000000000000000000000dcc80db987bf63f01b7bafced6230de5002ef87400000000000000000000000000000000000000000000000000000000000011d000000000000000000000000000000000000000000000000000000000000012c0000000000000000000000000fcf75295f242c4e87203abb5d7c9bbeda90a8895 ----Decode View------------Arg [0] : _verifierAddress (address): 0xDCC80dB987bf63f01b7bafCED6230DE5002eF874Arg [1] : _escapeBlockLowerBound (uint256): 4560Arg [2] : _escapeBlockUpperBound (uint256): 4800Arg [3] : _contractOwner (address): 0xFcF75295f242C4E87203Abb5d7C9BbEda90a8895-----Encoded View--------------- 4 Constructor Arguments found :Arg [0] : 000000000000000000000000dcc80db987bf63f01b7bafced6230de5002ef874Arg [1] : 00000000000000000000000000000000000000000000000000000000000011d0Arg [2] : 00000000000000000000000000000000000000000000000000000000000012c0Arg [3] : 000000000000000000000000fcf75295f242c4e87203abb5d7c9bbeda90a8895