Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- RailgunSmartWallet
- Optimization enabled
- true
- Compiler version
- v0.8.17+commit.8df45f5f
- Optimization runs
- 200
- EVM Version
- default
- Verified at
- 2025-04-30T20:36:09.068524Z
contracts/logic/RailgunSmartWallet.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
import { TokenBlocklist } from "./TokenBlocklist.sol";
import { Commitments } from "./Commitments.sol";
import { RailgunLogic } from "./RailgunLogic.sol";
import { SNARK_SCALAR_FIELD, CommitmentPreimage, CommitmentCiphertext, ShieldCiphertext, TokenType, UnshieldType, Transaction, ShieldRequest } from "./Globals.sol";
/**
* @title Railgun Smart Wallet
* @author Railgun Contributors
* @notice Railgun private smart wallet
* @dev Entry point for processing private meta-transactions
*/
contract RailgunSmartWallet is RailgunLogic {
/**
* @notice Shields requested amount and token, creates a commitment hash from supplied values and adds to tree
* @param _shieldRequests - list of commitments to shield
*/
function shield(ShieldRequest[] calldata _shieldRequests) external {
// Insertion and event arrays
bytes32[] memory insertionLeaves = new bytes32[](_shieldRequests.length);
CommitmentPreimage[] memory commitments = new CommitmentPreimage[](_shieldRequests.length);
ShieldCiphertext[] memory shieldCiphertext = new ShieldCiphertext[](_shieldRequests.length);
uint256[] memory fees = new uint256[](_shieldRequests.length);
// Loop through each note and process
for (uint256 notesIter = 0; notesIter < _shieldRequests.length; notesIter += 1) {
// Check note is valid
(bool valid, string memory reason) = RailgunLogic.validateCommitmentPreimage(
_shieldRequests[notesIter].preimage
);
require(valid, string.concat("RailgunSmartWallet: ", reason));
// Process shield request and store adjusted note
(commitments[notesIter], fees[notesIter]) = RailgunLogic.transferTokenIn(
_shieldRequests[notesIter].preimage
);
// Hash note for merkle tree insertion
insertionLeaves[notesIter] = RailgunLogic.hashCommitment(commitments[notesIter]);
// Push shield ciphertext
shieldCiphertext[notesIter] = _shieldRequests[notesIter].ciphertext;
}
// Emit Shield events (for wallets) for the commitments
emit Shield(
Commitments.treeNumber,
Commitments.nextLeafIndex,
commitments,
shieldCiphertext,
fees
);
// Push new commitments to merkle tree
Commitments.insertLeaves(insertionLeaves);
// Store block number of last event for easier sync
RailgunLogic.lastEventBlock = block.number;
}
/**
* @notice Execute batch of Railgun snark transactions
* @param _transactions - Transactions to execute
*/
function transact(Transaction[] calldata _transactions) external {
uint256 commitmentsCount = RailgunLogic.sumCommitments(_transactions);
// Create accumulators
bytes32[] memory commitments = new bytes32[](commitmentsCount);
uint256 commitmentsStartOffset = 0;
CommitmentCiphertext[] memory ciphertext = new CommitmentCiphertext[](commitmentsCount);
// Loop through each transaction, validate, and nullify
for (
uint256 transactionIter = 0;
transactionIter < _transactions.length;
transactionIter += 1
) {
// Validate transaction
(bool valid, string memory reason) = RailgunLogic.validateTransaction(
_transactions[transactionIter]
);
require(valid, string.concat("RailgunSmartWallet: ", reason));
// Nullify, accumulate, and update offset
commitmentsStartOffset = RailgunLogic.accumulateAndNullifyTransaction(
_transactions[transactionIter],
commitments,
commitmentsStartOffset,
ciphertext
);
}
// Loop through each transaction and process unshields
for (
uint256 transactionIter = 0;
transactionIter < _transactions.length;
transactionIter += 1
) {
// If unshield is specified, process
if (_transactions[transactionIter].boundParams.unshield != UnshieldType.NONE) {
RailgunLogic.transferTokenOut(_transactions[transactionIter].unshieldPreimage);
}
}
// Get insertion parameters
(
uint256 insertionTreeNumber,
uint256 insertionStartIndex
) = getInsertionTreeNumberAndStartingIndex(commitments.length);
// Emit commitment state update
if (commitments.length > 0) {
emit Transact(insertionTreeNumber, insertionStartIndex, commitments, ciphertext);
}
// Push commitments to tree after events due to insertLeaves causing side effects
Commitments.insertLeaves(commitments);
// Store block number of last event for easier sync
RailgunLogic.lastEventBlock = block.number;
}
}
@openzeppelin/contracts/utils/Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
pragma solidity ^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 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) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
contracts/governance/arbitrum/Executor.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
import { AddressAliasHelper } from "@arbitrum/nitro-contracts/src/libraries/AddressAliasHelper.sol";
import { ArbRetryableTx } from "@arbitrum/nitro-contracts/src/precompiles/ArbRetryableTx.sol";
import { Delegator } from "../Delegator.sol";
import { IL2Executor } from "../IL2Executor.sol";
/**
* @title Executor
* @author Railgun Contributors
* @notice Stores instructions to execute after L1 sender confirms
*/
contract ArbitrumExecutor is IL2Executor {
// Addresses
ArbRetryableTx public constant ARB_RETRYABLE_TX =
ArbRetryableTx(0x000000000000000000000000000000000000006E);
// solhint-disable-next-line var-name-mixedcase
address public immutable SENDER_L1; // Voting contract on L1
// solhint-disable-next-line var-name-mixedcase
Delegator public immutable DELEGATOR; // Delegator contract
uint256 public constant EXPIRY_TIME = 40 days;
enum ExecutionState {
Created,
AwaitingExecution,
Executed
}
// Task structure
struct Task {
uint256 creationTime; // Creation time of task
ExecutionState state; // Execution state of task
Action[] actions; // Calls to execute
}
// Task queue
Task[] public tasks;
// Task events
event TaskCreated(uint256 id);
event TaskReady(uint256 id);
event TaskExecuted(uint256 id);
// Errors event
error ExecutionFailed(uint256 index, bytes data);
/**
* @notice Sets contract addresses
* @param _senderL1 - sender contract on L1
* @param _delegator - delegator contract
*/
constructor(address _senderL1, Delegator _delegator) {
SENDER_L1 = _senderL1;
DELEGATOR = _delegator;
}
/**
* @notice Creates new task
* @param _actions - list of calls to execute for this task
*/
function createTask(Action[] calldata _actions) external returns (uint256) {
uint256 taskID = tasks.length;
// Get new task
Task storage task = tasks.push();
// Set task creation time
task.creationTime = block.timestamp;
// Set task execution state
task.state = ExecutionState.Created;
// Set call list
// Loop over actions and copy manually as solidity doesn't support copying struct arrays from calldata
for (uint256 i = 0; i < _actions.length; i += 1) {
task.actions.push(Action(_actions[i].callContract, _actions[i].data, _actions[i].value));
}
// Emit event
emit TaskCreated(taskID);
return taskID;
}
/**
* @notice Gets actions for a task
* @param _tasks - task to get actions for
*/
function getActions(uint256 _tasks) external view returns (Action[] memory) {
return tasks[_tasks].actions;
}
/**
* @notice Convenience function to get minimum time newly created tickets will be redeemable
*/
function newTicketTimeout() external view returns (uint256) {
return ARB_RETRYABLE_TX.getLifetime();
}
/**
* @notice Convenience function to get time left for ticket redemption
* @param _ticket - ticket ID to redeem
*/
function ticketTimeLeft(uint256 _ticket) external view returns (uint256) {
return ARB_RETRYABLE_TX.getTimeout(bytes32(_ticket));
}
/**
* @notice Convenience function to execute retryable ticket redeem
* @param _ticket - ticket ID to redeem
*/
function redeem(uint256 _ticket) external returns (bytes32) {
return ARB_RETRYABLE_TX.redeem(bytes32(_ticket));
}
/**
* @notice Executes task
* @param _task - task ID to execute
*/
function readyTask(uint256 _task) external {
// Check cross chain call
require(
msg.sender == AddressAliasHelper.applyL1ToL2Alias(SENDER_L1),
"ArbitrumExecutor: Caller is not L1 sender contract"
);
// Get task
Task storage task = tasks[_task];
// Check task hasn't already been executed
require(
task.state == ExecutionState.Created,
"ArbitrumExecutor: Task has already been executed"
);
// Set task can execute
task.state = ExecutionState.AwaitingExecution;
// Emit event
emit TaskReady(_task);
}
/**
* @notice Executes task
* @param _task - task ID to execute
*/
function executeTask(uint256 _task) external {
// Get task
Task storage task = tasks[_task];
// Check task can be executed
require(
task.state == ExecutionState.AwaitingExecution,
"ArbitrumExecutor: Task not marked as executable"
);
// Check task hasn't expired
require(
block.timestamp < task.creationTime + EXPIRY_TIME,
"ArbitrumExecutor: Task has expired"
);
// Mark task as executed
task.state = ExecutionState.Executed;
// Loop over actions and execute
for (uint256 i = 0; i < task.actions.length; i += 1) {
// Execute action
(bool successful, bytes memory returnData) = DELEGATOR.callContract(
task.actions[i].callContract,
task.actions[i].data,
task.actions[i].value
);
// If an action fails to execute, catch and bubble up reason with revert
if (!successful) {
revert ExecutionFailed(i, returnData);
}
}
// Emit event
emit TaskExecuted(_task);
}
}
contracts/teststubs/logic/VerifierStub.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
//OpenZeppelin v4
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { Verifier } from "../../logic/Verifier.sol";
contract VerifierStub is Verifier {
constructor() {
initializeVerifierStub();
}
function initializeVerifierStub() internal initializer {
OwnableUpgradeable.__Ownable_init();
}
}
contracts/logic/Snark.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
import { G1Point, G2Point, VerifyingKey, SnarkProof, SNARK_SCALAR_FIELD } from "./Globals.sol";
library Snark {
uint256 private constant PRIME_Q =
21888242871839275222246405745257275088696311157297823662689037894645226208583;
uint256 private constant PAIRING_INPUT_SIZE = 24;
uint256 private constant PAIRING_INPUT_WIDTH = 768; // PAIRING_INPUT_SIZE * 32
/**
* @notice Computes the negation of point p
* @dev The negation of p, i.e. p.plus(p.negate()) should be zero.
* @return result
*/
function negate(G1Point memory p) internal pure returns (G1Point memory) {
if (p.x == 0 && p.y == 0) return G1Point(0, 0);
// check for valid points y^2 = x^3 +3 % PRIME_Q
uint256 rh = mulmod(p.x, p.x, PRIME_Q); //x^2
rh = mulmod(rh, p.x, PRIME_Q); //x^3
rh = addmod(rh, 3, PRIME_Q); //x^3 + 3
uint256 lh = mulmod(p.y, p.y, PRIME_Q); //y^2
require(lh == rh, "Snark: Invalid negation");
return G1Point(p.x, PRIME_Q - (p.y % PRIME_Q));
}
/**
* @notice Adds 2 G1 points
* @return result
*/
function add(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory) {
// Format inputs
uint256[4] memory input;
input[0] = p1.x;
input[1] = p1.y;
input[2] = p2.x;
input[3] = p2.y;
// Setup output variables
bool success;
G1Point memory result;
// Add points
// solhint-disable-next-line no-inline-assembly
assembly {
success := staticcall(sub(gas(), 2000), 6, input, 0x80, result, 0x40)
}
// Check if operation succeeded
require(success, "Snark: Add Failed");
return result;
}
/**
* @notice Scalar multiplies two G1 points p, s
* @dev The product of a point on G1 and a scalar, i.e.
* p == p.scalar_mul(1) and p.plus(p) == p.scalar_mul(2) for all
* points p.
* @return r - result
*/
function scalarMul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) {
uint256[3] memory input;
input[0] = p.x;
input[1] = p.y;
input[2] = s;
bool success;
// solhint-disable-next-line no-inline-assembly
assembly {
success := staticcall(sub(gas(), 2000), 7, input, 0x60, r, 0x40)
}
// Check multiplication succeeded
require(success, "Snark: Scalar Multiplication Failed");
}
/**
* @notice Performs pairing check on points
* @dev The result of computing the pairing check
* e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1
* For example,
* pairing([P1(), P1().negate()], [P2(), P2()]) should return true.
* @return if pairing check passed
*/
function pairing(
G1Point memory _a1,
G2Point memory _a2,
G1Point memory _b1,
G2Point memory _b2,
G1Point memory _c1,
G2Point memory _c2,
G1Point memory _d1,
G2Point memory _d2
) internal view returns (bool) {
uint256[PAIRING_INPUT_SIZE] memory input = [
_a1.x,
_a1.y,
_a2.x[0],
_a2.x[1],
_a2.y[0],
_a2.y[1],
_b1.x,
_b1.y,
_b2.x[0],
_b2.x[1],
_b2.y[0],
_b2.y[1],
_c1.x,
_c1.y,
_c2.x[0],
_c2.x[1],
_c2.y[0],
_c2.y[1],
_d1.x,
_d1.y,
_d2.x[0],
_d2.x[1],
_d2.y[0],
_d2.y[1]
];
uint256[1] memory out;
bool success;
// solhint-disable-next-line no-inline-assembly
assembly {
success := staticcall(sub(gas(), 2000), 8, input, PAIRING_INPUT_WIDTH, out, 0x20)
}
// Check if operation succeeded
require(success, "Snark: Pairing Verification Failed");
return out[0] != 0;
}
/**
* @notice Verifies snark proof against proving key
* @param _vk - Verification Key
* @param _proof - snark proof
* @param _inputs - inputs
*/
function verify(
VerifyingKey memory _vk,
SnarkProof memory _proof,
uint256[] memory _inputs
) internal view returns (bool) {
// Compute the linear combination vkX
G1Point memory vkX = G1Point(0, 0);
// Loop through every input
for (uint256 i = 0; i < _inputs.length; i += 1) {
// Make sure inputs are less than SNARK_SCALAR_FIELD
require(_inputs[i] < SNARK_SCALAR_FIELD, "Snark: Input > SNARK_SCALAR_FIELD");
// Add to vkX point
vkX = add(vkX, scalarMul(_vk.ic[i + 1], _inputs[i]));
}
// Compute final vkX point
vkX = add(vkX, _vk.ic[0]);
// Verify pairing and return
return
pairing(
negate(_proof.a),
_proof.b,
_vk.alpha1,
_vk.beta2,
vkX,
_vk.gamma2,
_proof.c,
_vk.delta2
);
}
}
@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library AddressUpgradeable {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 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://consensys.net/diligence/blog/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.8.0/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");
(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 functionCallWithValue(target, data, 0, "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");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// 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
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
@openzeppelin/contracts-upgradeable/utils/math/SignedMathUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMathUpgradeable {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}
contracts/logic/Globals.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
// Constants
uint256 constant SNARK_SCALAR_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
// Verification bypass address, can't be address(0) as many burn prevention mechanisms will disallow transfers to 0
// Use 0x000000000000000000000000000000000000dEaD as an alternative known burn address
// https://etherscan.io/address/0x000000000000000000000000000000000000dEaD
address constant VERIFICATION_BYPASS = 0x000000000000000000000000000000000000dEaD;
bytes32 constant ACCEPT_RAILGUN_RESPONSE = keccak256(abi.encodePacked("Accept Railgun Session"));
struct ShieldRequest {
CommitmentPreimage preimage;
ShieldCiphertext ciphertext;
}
enum TokenType {
ERC20,
ERC721,
ERC1155
}
struct TokenData {
TokenType tokenType;
address tokenAddress;
uint256 tokenSubID;
}
struct CommitmentCiphertext {
bytes32[4] ciphertext; // Ciphertext order: IV & tag (16 bytes each), encodedMPK (senderMPK XOR receiverMPK), random & amount (16 bytes each), token
bytes32 blindedSenderViewingKey;
bytes32 blindedReceiverViewingKey;
bytes annotationData; // Only for sender to decrypt
bytes memo; // Added to note ciphertext for decryption
}
struct ShieldCiphertext {
bytes32[3] encryptedBundle; // IV shared (16 bytes), tag (16 bytes), random (16 bytes), IV sender (16 bytes), receiver viewing public key (32 bytes)
bytes32 shieldKey; // Public key to generate shared key from
}
enum UnshieldType {
NONE,
NORMAL,
REDIRECT
}
struct BoundParams {
uint16 treeNumber;
uint72 minGasPrice; // Only for type 0 transactions
UnshieldType unshield;
uint64 chainID;
address adaptContract;
bytes32 adaptParams;
// For unshields do not include an element in ciphertext array
// Ciphertext array length = commitments - unshields
CommitmentCiphertext[] commitmentCiphertext;
}
struct Transaction {
SnarkProof proof;
bytes32 merkleRoot;
bytes32[] nullifiers;
bytes32[] commitments;
BoundParams boundParams;
CommitmentPreimage unshieldPreimage;
}
struct CommitmentPreimage {
bytes32 npk; // Poseidon(Poseidon(spending public key, nullifying key), random)
TokenData token; // Token field
uint120 value; // Note value
}
struct G1Point {
uint256 x;
uint256 y;
}
// Encoding of field elements is: X[0] * z + X[1]
struct G2Point {
uint256[2] x;
uint256[2] y;
}
struct VerifyingKey {
string artifactsIPFSHash;
G1Point alpha1;
G2Point beta2;
G2Point gamma2;
G2Point delta2;
G1Point[] ic;
}
struct SnarkProof {
G1Point a;
G2Point b;
G1Point c;
}
@openzeppelin/contracts/utils/Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
import "./math/Math.sol";
import "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toString(int256 value) internal pure returns (string memory) {
return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return keccak256(bytes(a)) == keccak256(bytes(b));
}
}
@openzeppelin/contracts/utils/math/Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}
@arbitrum/nitro-contracts/src/precompiles/ArbRetryableTx.sol
// Copyright 2021-2022, Offchain Labs, Inc.
// For license information, see https://github.com/nitro/blob/master/LICENSE
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.4.21 <0.9.0;
/**
* @title Methods for managing retryables.
* @notice Precompiled contract in every Arbitrum chain for retryable transaction related data retrieval and interactions. Exists at 0x000000000000000000000000000000000000006e
*/
interface ArbRetryableTx {
/**
* @notice Schedule an attempt to redeem a redeemable tx, donating all of the call's gas to the redeem.
* Revert if ticketId does not exist.
* @param ticketId unique identifier of retryable message: keccak256(keccak256(ArbchainId, inbox-sequence-number), uint(0) )
* @return txId that the redeem attempt will have
*/
function redeem(bytes32 ticketId) external returns (bytes32);
/**
* @notice Return the minimum lifetime of redeemable txn.
* @return lifetime in seconds
*/
function getLifetime() external view returns (uint256);
/**
* @notice Return the timestamp when ticketId will age out, reverting if it does not exist
* @param ticketId unique ticket identifier
* @return timestamp for ticket's deadline
*/
function getTimeout(bytes32 ticketId) external view returns (uint256);
/**
* @notice Adds one lifetime period to the life of ticketId.
* Donate gas to pay for the lifetime extension.
* If successful, emits LifetimeExtended event.
* Revert if ticketId does not exist, or if the timeout of ticketId is already at least one lifetime period in the future.
* @param ticketId unique ticket identifier
* @return new timeout of ticketId
*/
function keepalive(bytes32 ticketId) external returns (uint256);
/**
* @notice Return the beneficiary of ticketId.
* Revert if ticketId doesn't exist.
* @param ticketId unique ticket identifier
* @return address of beneficiary for ticket
*/
function getBeneficiary(bytes32 ticketId) external view returns (address);
/**
* @notice Cancel ticketId and refund its callvalue to its beneficiary.
* Revert if ticketId doesn't exist, or if called by anyone other than ticketId's beneficiary.
* @param ticketId unique ticket identifier
*/
function cancel(bytes32 ticketId) external;
/**
* @notice Gets the redeemer of the current retryable redeem attempt.
* Returns the zero address if the current transaction is not a retryable redeem attempt.
* If this is an auto-redeem, returns the fee refund address of the retryable.
*/
function getCurrentRedeemer() external view returns (address);
/**
* @notice Do not call. This method represents a retryable submission to aid explorers.
* Calling it will always revert.
*/
function submitRetryable(
bytes32 requestId,
uint256 l1BaseFee,
uint256 deposit,
uint256 callvalue,
uint256 gasFeeCap,
uint64 gasLimit,
uint256 maxSubmissionFee,
address feeRefundAddress,
address beneficiary,
address retryTo,
bytes calldata retryData
) external;
event TicketCreated(bytes32 indexed ticketId);
event LifetimeExtended(bytes32 indexed ticketId, uint256 newTimeout);
event RedeemScheduled(
bytes32 indexed ticketId,
bytes32 indexed retryTxHash,
uint64 indexed sequenceNum,
uint64 donatedGas,
address gasDonor,
uint256 maxRefund,
uint256 submissionFeeRefund
);
event Canceled(bytes32 indexed ticketId);
/// @dev DEPRECATED in favour of new RedeemScheduled event after the nitro upgrade
event Redeemed(bytes32 indexed userTxHash);
error NoTicketWithID();
error NotCallable();
}
contracts/logic/RailgunLogic.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
// OpenZeppelin v4
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import { StorageSlot } from "@openzeppelin/contracts/utils/StorageSlot.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { SNARK_SCALAR_FIELD, TokenType, UnshieldType, TokenData, ShieldCiphertext, CommitmentCiphertext, CommitmentPreimage, Transaction } from "./Globals.sol";
import { Verifier } from "./Verifier.sol";
import { Commitments } from "./Commitments.sol";
import { TokenBlocklist } from "./TokenBlocklist.sol";
import { PoseidonT4 } from "./Poseidon.sol";
// Core validation logic should remain here
/**
* @title Railgun Logic
* @author Railgun Contributors
* @notice Logic to process transactions
*/
contract RailgunLogic is Initializable, OwnableUpgradeable, Commitments, TokenBlocklist, Verifier {
using SafeERC20 for IERC20;
// NOTE: The order of instantiation MUST stay the same across upgrades
// add new variables to the bottom of the list
// See https://docs.openzeppelin.com/learn/upgrading-smart-contracts#upgrading
// Treasury variables
address payable public treasury; // Treasury contract
uint120 private constant BASIS_POINTS = 10000; // Number of basis points that equal 100%
// % fee in 100ths of a %. 100 = 1%.
uint120 public shieldFee; // Previously called as depositFee
uint120 public unshieldFee; // Previously called withdrawFee
// Flat fee in wei that applies to NFT transactions
// LOGIC IS NOT IMPLEMENTED
// TODO: Revisit adapt module structure if we want to implement this
uint256 public nftFee; // Previously called transferFee
// Safety vectors
mapping(uint256 => bool) public snarkSafetyVector;
// Token ID mapping
mapping(bytes32 => TokenData) public tokenIDMapping;
// Last event block - to assist with scanning
uint256 public lastEventBlock;
// Treasury events
event TreasuryChange(address treasury);
event FeeChange(uint256 shieldFee, uint256 unshieldFee, uint256 nftFee);
// Transaction events
event Transact(
uint256 treeNumber,
uint256 startPosition,
bytes32[] hash,
CommitmentCiphertext[] ciphertext
);
event Shield(
uint256 treeNumber,
uint256 startPosition,
CommitmentPreimage[] commitments,
ShieldCiphertext[] shieldCiphertext,
uint256[] fees
);
event Unshield(address to, TokenData token, uint256 amount, uint256 fee);
event Nullified(uint16 treeNumber, bytes32[] nullifier);
/**
* @notice Initialize Railgun contract
* @dev OpenZeppelin initializer ensures this can only be called once
* This function also calls initializers on inherited contracts
* @param _treasury - address to send usage fees to
* @param _shieldFee - Shield fee
* @param _unshieldFee - Unshield fee
* @param _nftFee - Flat fee in wei that applies to NFT transactions
* @param _owner - governance contract
*/
function initializeRailgunLogic(
address payable _treasury,
uint120 _shieldFee,
uint120 _unshieldFee,
uint256 _nftFee,
address _owner
) public initializer {
// Call initializers
OwnableUpgradeable.__Ownable_init();
Commitments.initializeCommitments();
// Set treasury and fee
changeTreasury(_treasury);
changeFee(_shieldFee, _unshieldFee, _nftFee);
// Change Owner
OwnableUpgradeable.transferOwnership(_owner);
// Set safety vectors
snarkSafetyVector[11991246288605609459798790887503763024866871101] = true;
snarkSafetyVector[135932600361240492381964832893378343190771392134] = true;
snarkSafetyVector[1165567609304106638376634163822860648671860889162] = true;
}
/**
* @notice Change treasury address, only callable by owner (governance contract)
* @dev This will change the address of the contract we're sending the fees to in the future
* it won't transfer tokens already in the treasury
* @param _treasury - Address of new treasury contract
*/
function changeTreasury(address payable _treasury) public onlyOwner {
// Do nothing if the new treasury address is same as the old
if (treasury != _treasury) {
// Change treasury
treasury = _treasury;
// Emit treasury change event
emit TreasuryChange(_treasury);
}
}
/**
* @notice Change fee rate for future transactions
* @param _shieldFee - Shield fee
* @param _unshieldFee - Unshield fee
* @param _nftFee - Flat fee in wei that applies to NFT transactions
*/
function changeFee(uint120 _shieldFee, uint120 _unshieldFee, uint256 _nftFee) public onlyOwner {
if (_shieldFee != shieldFee || _unshieldFee != unshieldFee || _nftFee != nftFee) {
require(_shieldFee <= BASIS_POINTS / 2, "RailgunLogic: Shield Fee exceeds 50%");
require(_unshieldFee <= BASIS_POINTS / 2, "RailgunLogic: Unshield Fee exceeds 50%");
// Change fee
shieldFee = _shieldFee;
unshieldFee = _unshieldFee;
nftFee = _nftFee;
// Emit fee change event
emit FeeChange(_shieldFee, _unshieldFee, _nftFee);
}
}
/**
* @notice Get base and fee amount
* @param _amount - Amount to calculate for
* @param _isInclusive - Whether the amount passed in is inclusive of the fee
* @param _feeBP - Fee basis points
*/
function getFee(
uint136 _amount,
bool _isInclusive,
uint120 _feeBP
) public pure returns (uint120, uint120) {
// Expand width of amount to uint136 to accommodate full size of (2**120-1)*BASIS_POINTS
// Base is the amount sent into the railgun contract or sent to the target eth address
// for shields and unshields respectively
uint136 base;
// Fee is the amount sent to the treasury
uint136 fee;
if (_isInclusive) {
base = _amount - (_amount * _feeBP) / BASIS_POINTS;
fee = _amount - base;
} else {
base = _amount;
fee = (BASIS_POINTS * base) / (BASIS_POINTS - _feeBP) - base;
}
return (uint120(base), uint120(fee));
}
/**
* @notice Gets token ID value from tokenData
*/
function getTokenID(TokenData memory _tokenData) public pure returns (bytes32) {
// ERC20 tokenID is just the address
if (_tokenData.tokenType == TokenType.ERC20) {
return bytes32(uint256(uint160(_tokenData.tokenAddress)));
}
// Other token types are the keccak256 hash of the token data
return bytes32(uint256(keccak256(abi.encode(_tokenData))) % SNARK_SCALAR_FIELD);
}
/**
* @notice Hashes a commitment
*/
function hashCommitment(
CommitmentPreimage memory _commitmentPreimage
) public pure returns (bytes32) {
return
PoseidonT4.poseidon(
[
_commitmentPreimage.npk,
getTokenID(_commitmentPreimage.token),
bytes32(uint256(_commitmentPreimage.value))
]
);
}
/**
* @notice Checks commitment ranges for validity
* @return valid, reason
*/
function validateCommitmentPreimage(
CommitmentPreimage calldata _note
) public view returns (bool, string memory) {
// Note must be more than 0
if (_note.value == 0) return (false, "Invalid Note Value");
// Note token must not be blocklisted
if (TokenBlocklist.tokenBlocklist[_note.token.tokenAddress])
return (false, "Unsupported Token");
// Note NPK must be in field
if (uint256(_note.npk) >= SNARK_SCALAR_FIELD) return (false, "Invalid Note NPK");
// ERC721 notes should have a value of 1
if (_note.token.tokenType == TokenType.ERC721 && _note.value != 1)
return (false, "Invalid NFT Note Value");
return (true, "");
}
/**
* @notice Transfers tokens to contract and adjusts preimage with fee values
* @param _note - note to process
* @return adjusted note
*/
function transferTokenIn(
CommitmentPreimage calldata _note
) internal returns (CommitmentPreimage memory, uint256) {
// validateTransaction and accumulateAndNullifyTransaction functions MUST be called
// in that order BEFORE invoking this function to process an unshield on a transaction
// else reentrancy attacks are possible
CommitmentPreimage memory adjustedNote;
uint256 treasuryFee;
// Process shield request
if (_note.token.tokenType == TokenType.ERC20) {
// ERC20
// Get ERC20 interface
IERC20 token = IERC20(address(uint160(_note.token.tokenAddress)));
// Get base and fee amounts
(uint120 base, uint120 fee) = getFee(_note.value, true, RailgunLogic.shieldFee);
// Store treasury fee
treasuryFee = fee;
// Set adjusted preimage
adjustedNote = CommitmentPreimage({ npk: _note.npk, value: base, token: _note.token });
// Get balance before
uint256 balanceBefore = token.balanceOf(address(this));
// Transfer base to contract address
token.safeTransferFrom(address(msg.sender), address(this), base);
// Get balance after
uint256 balanceAfter = token.balanceOf(address(this));
// Check ERC20 tokens transferred
require(balanceAfter - balanceBefore == base, "RailgunLogic: ERC20 transfer failed");
// Transfer fee to treasury
token.safeTransferFrom(address(msg.sender), treasury, fee);
} else if (_note.token.tokenType == TokenType.ERC721) {
// ERC721 token
// Get ERC721 interface
IERC721 token = IERC721(address(uint160(_note.token.tokenAddress)));
// Treasury fee will be 0
treasuryFee = 0;
// No need to adjust note
adjustedNote = _note;
// Set tokenID mapping
tokenIDMapping[getTokenID(_note.token)] = _note.token;
// Transfer NFT to contract address
token.transferFrom(address(msg.sender), address(this), _note.token.tokenSubID);
// Check ERC721 transferred
require(
token.ownerOf(_note.token.tokenSubID) == address(this),
"RailgunLogic: ERC721 didn't transfer"
);
} else {
// ERC1155 token
revert("RailgunLogic: ERC1155 not yet supported");
}
return (adjustedNote, treasuryFee);
}
/**
* @notice Transfers tokens to contract and adjusts preimage with fee values
* @param _note - note to process
*/
function transferTokenOut(CommitmentPreimage calldata _note) internal {
// validateTransaction and accumulateAndNullifyTransaction functions MUST be called
// in that order BEFORE invoking this function to process an unshield on a transaction
// else reentrancy attacks are possible
// Process unshield request
if (_note.token.tokenType == TokenType.ERC20) {
// ERC20
// Get ERC20 interface
IERC20 token = IERC20(address(uint160(_note.token.tokenAddress)));
// Get base and fee amounts
(uint120 base, uint120 fee) = getFee(_note.value, true, unshieldFee);
// Transfer base to output address
token.safeTransfer(address(uint160(uint256(_note.npk))), base);
// Transfer fee to treasury
token.safeTransfer(treasury, fee);
// Emit unshield event
emit Unshield(address(uint160(uint256(_note.npk))), _note.token, base, fee);
} else if (_note.token.tokenType == TokenType.ERC721) {
// ERC721 token
// Get ERC721 interface
IERC721 token = IERC721(address(uint160(_note.token.tokenAddress)));
// Transfer NFT to output address
token.transferFrom(
address(this),
address(uint160(uint256(_note.npk))),
_note.token.tokenSubID
);
// Emit unshield event
emit Unshield(address(uint160(uint256(_note.npk))), _note.token, 1, 0);
} else {
// ERC1155 token
revert("RailgunLogic: ERC1155 not yet supported");
}
}
/**
* @notice Safety check for badly behaving code
*/
function checkSafetyVectors() external {
// Set safety bit
StorageSlot
.getBooleanSlot(0x8dea8703c3cf94703383ce38a9c894669dccd4ca8e65ddb43267aa0248711450)
.value = true;
// Setup behavior check
bool result = false;
// Execute behavior check
// solhint-disable-next-line no-inline-assembly
assembly {
mstore(0, caller())
mstore(32, snarkSafetyVector.slot)
let hash := keccak256(0, 64)
result := sload(hash)
}
require(result, "RailgunLogic: Unsafe vectors");
}
/**
* @notice Adds safety vector
*/
function addVector(uint256 vector) external onlyOwner {
snarkSafetyVector[vector] = true;
}
/**
* @notice Removes safety vector
*/
function removeVector(uint256 vector) external onlyOwner {
snarkSafetyVector[vector] = false;
}
/**
* @notice Sums number commitments in transaction batch
*/
function sumCommitments(Transaction[] calldata _transactions) public pure returns (uint256) {
uint256 commitments = 0;
for (
uint256 transactionIter = 0;
transactionIter < _transactions.length;
transactionIter += 1
) {
// The last commitment should NOT be counted if transaction includes unshield
// The ciphertext length is validated in the transaction validity function to reflect this
commitments += _transactions[transactionIter].boundParams.commitmentCiphertext.length;
}
return commitments;
}
/**
* @notice Verifies transaction validity
* @return valid, reason
*/
function validateTransaction(
Transaction calldata _transaction
) public view returns (bool, string memory) {
// Gas price of eth transaction should be equal or greater than railgun transaction specified min gas price
// This will only work correctly for type 0 transactions, set to 0 for EIP-1559 transactions
if (tx.gasprice < _transaction.boundParams.minGasPrice) return (false, "Gas price too low");
// Adapt contract must either equal 0 or msg.sender
if (
_transaction.boundParams.adaptContract != address(0) &&
_transaction.boundParams.adaptContract != msg.sender
) return (false, "Invalid Adapt Contract as Sender");
// ChainID should match the current EVM chainID
if (_transaction.boundParams.chainID != block.chainid) return (false, "ChainID mismatch");
// Merkle root must be a seen historical root
if (!Commitments.rootHistory[_transaction.boundParams.treeNumber][_transaction.merkleRoot])
return (false, "Invalid Merkle Root");
if (_transaction.boundParams.unshield != UnshieldType.NONE) {
// Ensure ciphertext length matches the commitments length (minus 1 for unshield output)
if (
_transaction.boundParams.commitmentCiphertext.length != _transaction.commitments.length - 1
) return (false, "Invalid Note Ciphertext Array Length");
// Check unshield preimage hash is correct
bytes32 hash;
if (_transaction.boundParams.unshield == UnshieldType.REDIRECT) {
// If redirect is allowed unshield MUST be submitted by original recipient
hash = hashCommitment(
CommitmentPreimage({
npk: bytes32(uint256(uint160(msg.sender))),
token: _transaction.unshieldPreimage.token,
value: _transaction.unshieldPreimage.value
})
);
} else {
hash = hashCommitment(_transaction.unshieldPreimage);
}
// Check hash equals the last commitment in array
if (hash != _transaction.commitments[_transaction.commitments.length - 1])
return (false, "Invalid Withdraw Note");
} else {
// Ensure ciphertext length matches the commitments length
if (_transaction.boundParams.commitmentCiphertext.length != _transaction.commitments.length)
return (false, "Invalid Note Ciphertext Array Length");
}
// Verify SNARK proof
if (!Verifier.verify(_transaction)) return (false, "Invalid Snark Proof");
return (true, "");
}
/**
* @notice Accumulates transaction fields and nullifies nullifiers
* @param _transaction - transaction to process
* @param _commitments - commitments accumulator
* @param _commitmentsStartOffset - number of commitments already in the accumulator
* @param _ciphertext - commitment ciphertext accumulator, count will be identical to commitments accumulator
* @return New nullifier start offset, new commitments start offset
*/
function accumulateAndNullifyTransaction(
Transaction calldata _transaction,
bytes32[] memory _commitments,
uint256 _commitmentsStartOffset,
CommitmentCiphertext[] memory _ciphertext
) internal returns (uint256) {
// Loop through each nullifier
for (
uint256 nullifierIter = 0;
nullifierIter < _transaction.nullifiers.length;
nullifierIter += 1
) {
// If nullifier has been seen before revert
require(
!Commitments.nullifiers[_transaction.boundParams.treeNumber][
_transaction.nullifiers[nullifierIter]
],
"RailgunLogic: Note already spent"
);
// Set nullifier to seen
Commitments.nullifiers[_transaction.boundParams.treeNumber][
_transaction.nullifiers[nullifierIter]
] = true;
}
// Emit nullifier event
emit Nullified(_transaction.boundParams.treeNumber, _transaction.nullifiers);
// Loop through each commitment
for (
uint256 commitmentsIter = 0;
// The last commitment should NOT be accumulated if transaction includes unshield
// The ciphertext length is validated in the transaction validity function to reflect this
commitmentsIter < _transaction.boundParams.commitmentCiphertext.length;
commitmentsIter += 1
) {
// Push commitment to commitments accumulator
_commitments[_commitmentsStartOffset + commitmentsIter] = _transaction.commitments[
commitmentsIter
];
// Push ciphertext to ciphertext accumulator
_ciphertext[_commitmentsStartOffset + commitmentsIter] = _transaction
.boundParams
.commitmentCiphertext[commitmentsIter];
}
// Return new starting offset
return _commitmentsStartOffset + _transaction.boundParams.commitmentCiphertext.length;
}
uint256[43] private __gap;
}
@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC721.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional metadata extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Metadata is IERC721 {
/**
* @dev Returns the token collection name.
*/
function name() external view returns (string memory);
/**
* @dev Returns the token collection symbol.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
*/
function tokenURI(uint256 tokenId) external view returns (string memory);
}
contracts/teststubs/logic/SnarkStub.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
import { VerifyingKey, SnarkProof } from "../../logic/Globals.sol";
import { Snark, G1Point } from "../../logic/Snark.sol";
contract SnarkStub {
function negate(G1Point memory p) external pure returns (G1Point memory) {
return Snark.negate(p);
}
function verify(
VerifyingKey memory _verifyingKey,
SnarkProof calldata _proof,
uint256[] memory _inputs
) external view returns (bool) {
return Snark.verify(_verifyingKey, _proof, _inputs);
}
}
@arbitrum/nitro-contracts/src/bridge/IOwnable.sol
// Copyright 2021-2022, Offchain Labs, Inc.
// For license information, see https://github.com/nitro/blob/master/LICENSE
// SPDX-License-Identifier: BUSL-1.1
// solhint-disable-next-line compiler-version
pragma solidity >=0.4.21 <0.9.0;
interface IOwnable {
function owner() external view returns (address);
}
@arbitrum/nitro-contracts/src/bridge/IDelayedMessageProvider.sol
// Copyright 2021-2022, Offchain Labs, Inc.
// For license information, see https://github.com/nitro/blob/master/LICENSE
// SPDX-License-Identifier: BUSL-1.1
// solhint-disable-next-line compiler-version
pragma solidity >=0.6.9 <0.9.0;
interface IDelayedMessageProvider {
/// @dev event emitted when a inbox message is added to the Bridge's delayed accumulator
event InboxMessageDelivered(uint256 indexed messageNum, bytes data);
/// @dev event emitted when a inbox message is added to the Bridge's delayed accumulator
/// same as InboxMessageDelivered but the batch data is available in tx.input
event InboxMessageDeliveredFromOrigin(uint256 indexed messageNum);
}
contracts/logic/TokenBlocklist.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
// OpenZeppelin v4
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
/**
* @title Token Blocklist
* @author Railgun Contributors
* @notice Blocklist of tokens that are incompatible with the protocol
* @dev Tokens on this blocklist can't be shielded to railgun.
* Tokens on this blocklist will still be transferrable
* internally (as internal transactions have a shielded token ID) and
* unshieldable (to prevent user funds from being locked)
* THIS WILL ALWAYS BE A NON-EXHAUSTIVE LIST, DO NOT RELY ON IT BLOCKING ALL
* INCOMPATIBLE TOKENS
*/
contract TokenBlocklist is OwnableUpgradeable {
// Events for offchain building of blocklist index
event AddToBlocklist(address indexed token);
event RemoveFromBlocklist(address indexed token);
// NOTE: The order of instantiation MUST stay the same across upgrades
// add new variables to the bottom of the list and decrement the __gap
// variable at the end of this file
// See https://docs.openzeppelin.com/learn/upgrading-smart-contracts#upgrading
mapping(address => bool) public tokenBlocklist;
/**
* @notice Adds tokens to Blocklist, only callable by owner (governance contract)
* @dev This function will ignore tokens that are already in the Blocklist
* no events will be emitted in this case
* @param _tokens - List of tokens to add to Blocklist
*/
function addToBlocklist(address[] calldata _tokens) external onlyOwner {
// Loop through token array
for (uint256 i = 0; i < _tokens.length; i += 1) {
// Don't do anything if the token is already blocklisted
if (!tokenBlocklist[_tokens[i]]) {
// Set token address in blocklist map to true
tokenBlocklist[_tokens[i]] = true;
// Emit event for building index of blocklisted tokens offchain
emit AddToBlocklist(_tokens[i]);
}
}
}
/**
* @notice Removes token from blocklist, only callable by owner (governance contract)
* @dev This function will ignore tokens that aren't in the blocklist
* no events will be emitted in this case
* @param _tokens - List of tokens to remove from blocklist
*/
function removeFromBlocklist(address[] calldata _tokens) external onlyOwner {
// Loop through token array
for (uint256 i = 0; i < _tokens.length; i += 1) {
// Don't do anything if the token isn't blocklisted
if (tokenBlocklist[_tokens[i]]) {
// Set token address in blocklisted map to false (default value)
delete tokenBlocklist[_tokens[i]];
// Emit event for building index of blocklisted tokens off chain
emit RemoveFromBlocklist(_tokens[i]);
}
}
}
uint256[49] private __gap;
}
contracts/teststubs/adapt/SimpleSwap.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
// OpenZeppelin v4
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
contract SimpleSwap {
using SafeERC20 for IERC20;
uint256 private constant BASIS_POINTS = 10000; // Number of basis points that equal 100%
function swap(IERC20 _from, IERC20 _to, uint256 _amount, uint256 _rateBP) external {
_from.safeTransferFrom(msg.sender, address(this), _amount);
_to.safeTransfer(msg.sender, (_amount * _rateBP) / BASIS_POINTS);
}
}
contracts/teststubs/logic/TokenBlocklistStub.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
//OpenZeppelin v4
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { TokenBlocklist } from "../../logic/TokenBlocklist.sol";
contract TokenBlocklistStub is TokenBlocklist {
constructor() {
initializeTokenBlocklistStub();
}
function initializeTokenBlocklistStub() internal initializer {
OwnableUpgradeable.__Ownable_init();
}
}
@openzeppelin/contracts-upgradeable/access/IAccessControlUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
pragma solidity ^0.8.0;
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControlUpgradeable {
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*
* _Available since v3.1._
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) external;
}
contracts/governance/IL2Executor.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
import { Delegator } from "./Delegator.sol";
interface IL2Executor {
// Action structure
struct Action {
address callContract;
bytes data;
uint256 value;
}
/**
* @notice Delegator address
* @return Governance delegator to execute actions via
*/
// solhint-disable-next-line func-name-mixedcase
function DELEGATOR() external returns (Delegator);
/**
* @notice Creates new task
* @param _actions - list of calls to execute for this task
*/
function createTask(Action[] calldata _actions) external returns (uint256);
/**
* @notice Gets actions for a task
* @param _tasks - task to get actions for
* @return List of actions
*/
function getActions(uint256 _tasks) external view returns (Action[] memory);
/**
* @notice Executes task
* @dev Should execute via Governance Delegator
* @param _task - task ID to execute
*/
function executeTask(uint256 _task) external;
}
contracts/token/RailDaoMintable.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
// OpenZeppelin v4
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
/**
* @title RailTokenDAOMintable
* @author Railgun Contributors
* @notice ERC20 Railgun Governance Token
*/
contract RailTokenDAOMintable is Ownable, ERC20 {
// Minting cap
uint256 public cap;
/**
* @notice Mints initial token supply
*/
constructor(
address _initialHolder,
uint256 _initialSupply,
uint256 _cap,
address _owner,
string memory name,
string memory symbol
) ERC20(name, symbol) {
// Save cap
cap = _cap;
// Mint initial tokens
_mint(_initialHolder, _initialSupply);
// Transfer ownership
Ownable.transferOwnership(_owner);
}
/**
* @notice See ERC20._mint
* @dev Overrides ERC20 mint to add hard cap check
* @param _account - account to mint to
* @param _amount - amount to mint
*/
function _mint(address _account, uint256 _amount) internal override {
require(
ERC20.totalSupply() + _amount <= cap,
"RailTokenDAOMintable: Can't mint more than hard cap"
);
super._mint(_account, _amount);
}
/**
* @notice Mints new coins if governance contract requests
* @dev ONLY MINTABLE IF GOVERNANCE PROCESS PASSES, CANNOT MINT MORE THAN HARD CAP (cap())
* @param _account - account to mint to
* @param _amount - amount to mint
* @return success
*/
function governanceMint(
address _account,
uint256 _amount
) external onlyOwner returns (bool success) {
_mint(_account, _amount);
return true;
}
}
@openzeppelin/contracts/token/ERC1155/ERC1155.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC1155/ERC1155.sol)
pragma solidity ^0.8.0;
import "./IERC1155.sol";
import "./IERC1155Receiver.sol";
import "./extensions/IERC1155MetadataURI.sol";
import "../../utils/Address.sol";
import "../../utils/Context.sol";
import "../../utils/introspection/ERC165.sol";
/**
* @dev Implementation of the basic standard multi-token.
* See https://eips.ethereum.org/EIPS/eip-1155
* Originally based on code by Enjin: https://github.com/enjin/erc-1155
*
* _Available since v3.1._
*/
contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
using Address for address;
// Mapping from token ID to account balances
mapping(uint256 => mapping(address => uint256)) private _balances;
// Mapping from account to operator approvals
mapping(address => mapping(address => bool)) private _operatorApprovals;
// Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json
string private _uri;
/**
* @dev See {_setURI}.
*/
constructor(string memory uri_) {
_setURI(uri_);
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return
interfaceId == type(IERC1155).interfaceId ||
interfaceId == type(IERC1155MetadataURI).interfaceId ||
super.supportsInterface(interfaceId);
}
/**
* @dev See {IERC1155MetadataURI-uri}.
*
* This implementation returns the same URI for *all* token types. It relies
* on the token type ID substitution mechanism
* https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
*
* Clients calling this function must replace the `\{id\}` substring with the
* actual token type ID.
*/
function uri(uint256) public view virtual override returns (string memory) {
return _uri;
}
/**
* @dev See {IERC1155-balanceOf}.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function balanceOf(address account, uint256 id) public view virtual override returns (uint256) {
require(account != address(0), "ERC1155: address zero is not a valid owner");
return _balances[id][account];
}
/**
* @dev See {IERC1155-balanceOfBatch}.
*
* Requirements:
*
* - `accounts` and `ids` must have the same length.
*/
function balanceOfBatch(
address[] memory accounts,
uint256[] memory ids
) public view virtual override returns (uint256[] memory) {
require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch");
uint256[] memory batchBalances = new uint256[](accounts.length);
for (uint256 i = 0; i < accounts.length; ++i) {
batchBalances[i] = balanceOf(accounts[i], ids[i]);
}
return batchBalances;
}
/**
* @dev See {IERC1155-setApprovalForAll}.
*/
function setApprovalForAll(address operator, bool approved) public virtual override {
_setApprovalForAll(_msgSender(), operator, approved);
}
/**
* @dev See {IERC1155-isApprovedForAll}.
*/
function isApprovedForAll(address account, address operator) public view virtual override returns (bool) {
return _operatorApprovals[account][operator];
}
/**
* @dev See {IERC1155-safeTransferFrom}.
*/
function safeTransferFrom(
address from,
address to,
uint256 id,
uint256 amount,
bytes memory data
) public virtual override {
require(
from == _msgSender() || isApprovedForAll(from, _msgSender()),
"ERC1155: caller is not token owner or approved"
);
_safeTransferFrom(from, to, id, amount, data);
}
/**
* @dev See {IERC1155-safeBatchTransferFrom}.
*/
function safeBatchTransferFrom(
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) public virtual override {
require(
from == _msgSender() || isApprovedForAll(from, _msgSender()),
"ERC1155: caller is not token owner or approved"
);
_safeBatchTransferFrom(from, to, ids, amounts, data);
}
/**
* @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `from` must have a balance of tokens of type `id` of at least `amount`.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function _safeTransferFrom(
address from,
address to,
uint256 id,
uint256 amount,
bytes memory data
) internal virtual {
require(to != address(0), "ERC1155: transfer to the zero address");
address operator = _msgSender();
uint256[] memory ids = _asSingletonArray(id);
uint256[] memory amounts = _asSingletonArray(amount);
_beforeTokenTransfer(operator, from, to, ids, amounts, data);
uint256 fromBalance = _balances[id][from];
require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
unchecked {
_balances[id][from] = fromBalance - amount;
}
_balances[id][to] += amount;
emit TransferSingle(operator, from, to, id, amount);
_afterTokenTransfer(operator, from, to, ids, amounts, data);
_doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);
}
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}.
*
* Emits a {TransferBatch} event.
*
* Requirements:
*
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
* acceptance magic value.
*/
function _safeBatchTransferFrom(
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) internal virtual {
require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
require(to != address(0), "ERC1155: transfer to the zero address");
address operator = _msgSender();
_beforeTokenTransfer(operator, from, to, ids, amounts, data);
for (uint256 i = 0; i < ids.length; ++i) {
uint256 id = ids[i];
uint256 amount = amounts[i];
uint256 fromBalance = _balances[id][from];
require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
unchecked {
_balances[id][from] = fromBalance - amount;
}
_balances[id][to] += amount;
}
emit TransferBatch(operator, from, to, ids, amounts);
_afterTokenTransfer(operator, from, to, ids, amounts, data);
_doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);
}
/**
* @dev Sets a new URI for all token types, by relying on the token type ID
* substitution mechanism
* https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
*
* By this mechanism, any occurrence of the `\{id\}` substring in either the
* URI or any of the amounts in the JSON file at said URI will be replaced by
* clients with the token type ID.
*
* For example, the `https://token-cdn-domain/\{id\}.json` URI would be
* interpreted by clients as
* `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json`
* for token type ID 0x4cce0.
*
* See {uri}.
*
* Because these URIs cannot be meaningfully represented by the {URI} event,
* this function emits no events.
*/
function _setURI(string memory newuri) internal virtual {
_uri = newuri;
}
/**
* @dev Creates `amount` tokens of token type `id`, and assigns them to `to`.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function _mint(address to, uint256 id, uint256 amount, bytes memory data) internal virtual {
require(to != address(0), "ERC1155: mint to the zero address");
address operator = _msgSender();
uint256[] memory ids = _asSingletonArray(id);
uint256[] memory amounts = _asSingletonArray(amount);
_beforeTokenTransfer(operator, address(0), to, ids, amounts, data);
_balances[id][to] += amount;
emit TransferSingle(operator, address(0), to, id, amount);
_afterTokenTransfer(operator, address(0), to, ids, amounts, data);
_doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data);
}
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
*
* Emits a {TransferBatch} event.
*
* Requirements:
*
* - `ids` and `amounts` must have the same length.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
* acceptance magic value.
*/
function _mintBatch(
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) internal virtual {
require(to != address(0), "ERC1155: mint to the zero address");
require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
address operator = _msgSender();
_beforeTokenTransfer(operator, address(0), to, ids, amounts, data);
for (uint256 i = 0; i < ids.length; i++) {
_balances[ids[i]][to] += amounts[i];
}
emit TransferBatch(operator, address(0), to, ids, amounts);
_afterTokenTransfer(operator, address(0), to, ids, amounts, data);
_doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
}
/**
* @dev Destroys `amount` tokens of token type `id` from `from`
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `from` must have at least `amount` tokens of token type `id`.
*/
function _burn(address from, uint256 id, uint256 amount) internal virtual {
require(from != address(0), "ERC1155: burn from the zero address");
address operator = _msgSender();
uint256[] memory ids = _asSingletonArray(id);
uint256[] memory amounts = _asSingletonArray(amount);
_beforeTokenTransfer(operator, from, address(0), ids, amounts, "");
uint256 fromBalance = _balances[id][from];
require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
unchecked {
_balances[id][from] = fromBalance - amount;
}
emit TransferSingle(operator, from, address(0), id, amount);
_afterTokenTransfer(operator, from, address(0), ids, amounts, "");
}
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.
*
* Emits a {TransferBatch} event.
*
* Requirements:
*
* - `ids` and `amounts` must have the same length.
*/
function _burnBatch(address from, uint256[] memory ids, uint256[] memory amounts) internal virtual {
require(from != address(0), "ERC1155: burn from the zero address");
require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
address operator = _msgSender();
_beforeTokenTransfer(operator, from, address(0), ids, amounts, "");
for (uint256 i = 0; i < ids.length; i++) {
uint256 id = ids[i];
uint256 amount = amounts[i];
uint256 fromBalance = _balances[id][from];
require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
unchecked {
_balances[id][from] = fromBalance - amount;
}
}
emit TransferBatch(operator, from, address(0), ids, amounts);
_afterTokenTransfer(operator, from, address(0), ids, amounts, "");
}
/**
* @dev Approve `operator` to operate on all of `owner` tokens
*
* Emits an {ApprovalForAll} event.
*/
function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
require(owner != operator, "ERC1155: setting approval status for self");
_operatorApprovals[owner][operator] = approved;
emit ApprovalForAll(owner, operator, approved);
}
/**
* @dev Hook that is called before any token transfer. This includes minting
* and burning, as well as batched variants.
*
* The same hook is called on both single and batched variants. For single
* transfers, the length of the `ids` and `amounts` arrays will be 1.
*
* Calling conditions (for each `id` and `amount` pair):
*
* - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* of token type `id` will be transferred to `to`.
* - When `from` is zero, `amount` tokens of token type `id` will be minted
* for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
* will be burned.
* - `from` and `to` are never both zero.
* - `ids` and `amounts` have the same, non-zero length.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(
address operator,
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) internal virtual {}
/**
* @dev Hook that is called after any token transfer. This includes minting
* and burning, as well as batched variants.
*
* The same hook is called on both single and batched variants. For single
* transfers, the length of the `id` and `amount` arrays will be 1.
*
* Calling conditions (for each `id` and `amount` pair):
*
* - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* of token type `id` will be transferred to `to`.
* - When `from` is zero, `amount` tokens of token type `id` will be minted
* for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
* will be burned.
* - `from` and `to` are never both zero.
* - `ids` and `amounts` have the same, non-zero length.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(
address operator,
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) internal virtual {}
function _doSafeTransferAcceptanceCheck(
address operator,
address from,
address to,
uint256 id,
uint256 amount,
bytes memory data
) private {
if (to.isContract()) {
try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) {
if (response != IERC1155Receiver.onERC1155Received.selector) {
revert("ERC1155: ERC1155Receiver rejected tokens");
}
} catch Error(string memory reason) {
revert(reason);
} catch {
revert("ERC1155: transfer to non-ERC1155Receiver implementer");
}
}
}
function _doSafeBatchTransferAcceptanceCheck(
address operator,
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) private {
if (to.isContract()) {
try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (
bytes4 response
) {
if (response != IERC1155Receiver.onERC1155BatchReceived.selector) {
revert("ERC1155: ERC1155Receiver rejected tokens");
}
} catch Error(string memory reason) {
revert(reason);
} catch {
revert("ERC1155: transfer to non-ERC1155Receiver implementer");
}
}
}
function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) {
uint256[] memory array = new uint256[](1);
array[0] = element;
return array;
}
}
contracts/teststubs/governance/StakingStub.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
import { Staking } from "../../governance/Staking.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract StakingStub is Staking {
// solhint-disable-next-line no-empty-blocks
constructor(IERC20 _stakingToken) Staking(_stakingToken) {}
function snapshotStub(address _account) public {
Staking.snapshot(_account);
}
}
contracts/governance/arbitrum/Sender.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
// OpenZeppelin v4
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { ArbitrumExecutor } from "./Executor.sol";
import { IL2Sender } from "../IL2Sender.sol";
import { IInboxPatched } from "./IInboxPatched.sol";
/**
* @title Sender
* @author Railgun Contributors
* @notice Sets tasks on Arbitrum sender to executable
*/
contract ArbitrumSender is Ownable, IL2Sender {
// solhint-disable-next-line var-name-mixedcase
IInboxPatched public immutable ARBITRUM_INBOX; // Arbitrum Inbox
address public executorL2; // Sender contract on L2
/**
* @notice Sets contract addresses
* @param _admin - delegator contract
* @param _executorL2 - sender contract on L1
* @param _arbitrumInbox - arbitrum inbox address
*/
constructor(address _admin, address _executorL2, IInboxPatched _arbitrumInbox) {
ARBITRUM_INBOX = _arbitrumInbox;
setExecutorL2(_executorL2);
Ownable.transferOwnership(_admin);
}
/**
* @notice Sends ready task instruction to arbitrum executor
* @param _task - task ID to ready
*/
function readyTask(uint256 _task) external onlyOwner returns (uint256) {
// Calculate data
bytes memory data = abi.encodeWithSelector(ArbitrumExecutor.readyTask.selector, _task);
// Get submission fee
uint256 submissionFee = ARBITRUM_INBOX.calculateRetryableSubmissionFee(
data.length,
block.basefee
);
// Create retryable ticket on arbitrum to set execution for governance task to true
return
ARBITRUM_INBOX.createRetryableTicket{ value: submissionFee }(
executorL2,
0,
submissionFee,
// solhint-disable-next-line avoid-tx-origin
tx.origin,
// solhint-disable-next-line avoid-tx-origin
tx.origin,
0,
0,
data
);
}
/**
* @notice Sets L2 executor address
* @param _executorL2 - new executor address
*/
function setExecutorL2(address _executorL2) public onlyOwner {
require(_executorL2 != address(0), "ArbitrumSender: Executor address is 0");
executorL2 = _executorL2;
}
// Allow receiving ETH
receive() external payable {}
}
contracts/proxy/ProxyAdmin.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
// OpenZeppelin v4
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { PausableUpgradableProxy } from "./Proxy.sol";
/**
* @title ProxyAdmin
* @author Railgun Contributors
* @notice Admin interface for PausableUpgradableProxy
* @dev All non-proxied calls must go through this contract
*/
contract ProxyAdmin is Ownable {
/**
* @notice Sets initial admin
*/
constructor(address _admin) {
Ownable.transferOwnership(_admin);
}
/**
* @notice Transfers ownership of proxy to new address
* @param _proxy - proxy to administrate
* @param _newOwner - Address to transfer ownership to
*/
function transferProxyOwnership(
PausableUpgradableProxy _proxy,
address _newOwner
) external onlyOwner {
_proxy.transferOwnership(_newOwner);
}
/**
* @notice Upgrades implementation
* @param _proxy - Proxy to upgrade
* @param _newImplementation - Address of the new implementation
*/
function upgrade(PausableUpgradableProxy _proxy, address _newImplementation) external onlyOwner {
_proxy.upgrade(_newImplementation);
}
/**
* @notice Pauses contract
* @param _proxy - Proxy to pause
*/
function pause(PausableUpgradableProxy _proxy) external onlyOwner {
_proxy.pause();
}
/**
* @notice Unpauses contract
* @param _proxy - Proxy to pause
*/
function unpause(PausableUpgradableProxy _proxy) external onlyOwner {
_proxy.unpause();
}
}
@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.2;
import "../../utils/AddressUpgradeable.sol";
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
* @custom:oz-retyped-from bool
*/
uint8 private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint8 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
* constructor.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
bool isTopLevelCall = !_initializing;
require(
(isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
"Initializable: contract is already initialized"
);
_initialized = 1;
if (isTopLevelCall) {
_initializing = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: setting the version to 255 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint8 version) {
require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
_initialized = version;
_initializing = true;
_;
_initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized != type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint8) {
return _initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _initializing;
}
}
@openzeppelin/contracts/token/ERC721/IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the caller.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)
pragma solidity ^0.8.0;
/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/
interface IERC721Receiver {
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
*
* The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
*/
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
import "./math/MathUpgradeable.sol";
import "./math/SignedMathUpgradeable.sol";
/**
* @dev String operations.
*/
library StringsUpgradeable {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = MathUpgradeable.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toString(int256 value) internal pure returns (string memory) {
return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMathUpgradeable.abs(value))));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, MathUpgradeable.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return keccak256(bytes(a)) == keccak256(bytes(b));
}
}
contracts/teststubs/adapt/MaliciousReentrant.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
import { TokenType, TokenData, CommitmentPreimage, ShieldCiphertext, ShieldRequest, Transaction } from "../../logic/Globals.sol";
import { RelayAdapt } from "../../adapt/Relay.sol";
contract MaliciousReentrant {
function attack() external {
RelayAdapt relayAdapt = RelayAdapt(payable(msg.sender));
ShieldRequest memory shieldRequest = ShieldRequest({
preimage: CommitmentPreimage({
npk: bytes32(uint256(1)),
token: TokenData({ tokenType: TokenType.ERC20, tokenAddress: address(0), tokenSubID: 0 }),
value: 0
}),
ciphertext: ShieldCiphertext({
encryptedBundle: [bytes32(uint256(1)), bytes32(uint256(1)), bytes32(uint256(1))],
shieldKey: bytes32(uint256(1))
})
});
bool success = false;
// solhint-disable-next-line avoid-low-level-calls
(success, ) = address(relayAdapt).call(
abi.encodeWithSelector(relayAdapt.shield.selector, [shieldRequest])
);
require(!success, "Reentry was successful");
RelayAdapt.TokenTransfer memory tokenTransfer = RelayAdapt.TokenTransfer({
to: address(this),
token: TokenData({ tokenType: TokenType.ERC20, tokenAddress: address(0), tokenSubID: 0 }),
value: 100
});
// solhint-disable-next-line avoid-low-level-calls
(success, ) = address(relayAdapt).call(
abi.encodeWithSelector(relayAdapt.transfer.selector, [tokenTransfer])
);
require(!success, "Reentry was successful");
// solhint-disable-next-line avoid-low-level-calls
(success, ) = address(relayAdapt).call(
abi.encodeWithSelector(relayAdapt.wrapBase.selector, uint256(0))
);
require(!success, "Reentry was successful");
// solhint-disable-next-line avoid-low-level-calls
(success, ) = address(relayAdapt).call(
abi.encodeWithSelector(relayAdapt.unwrapBase.selector, uint256(0))
);
require(!success, "Reentry was successful");
RelayAdapt.Call[] memory calls = new RelayAdapt.Call[](0);
// solhint-disable-next-line avoid-low-level-calls
(success, ) = address(relayAdapt).call(
abi.encodeWithSelector(relayAdapt.multicall.selector, false, calls)
);
require(!success, "Reentry was successful");
Transaction[] memory transactions = new Transaction[](0);
RelayAdapt.ActionData memory actionData = RelayAdapt.ActionData({
random: bytes31(0),
requireSuccess: false,
minGasLimit: 0,
calls: new RelayAdapt.Call[](0)
});
// solhint-disable-next-line avoid-low-level-calls
(success, ) = address(relayAdapt).call(
abi.encodeWithSelector(relayAdapt.relay.selector, transactions, actionData)
);
require(!success, "Reentry was successful");
}
}
contracts/teststubs/governance/GovernanceTarget.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
contract GovernanceTargetAlphaStub {
function a() external pure returns (string memory) {
return "a";
}
function b() external pure returns (string memory) {
return "b";
}
function add(uint256 _left, uint256 _right) external pure returns (uint256) {
return _left + _right;
}
function willRevert() external pure returns (bool success) {
require(1 == 2, "1 is not equal to 2");
return true;
}
receive() external payable {
require(1 == 2, "1 is not equal to 2");
}
}
contract GovernanceTargetNumberStub {
function a() external pure returns (uint256) {
return 1;
}
function b() external pure returns (uint256) {
return 2;
}
function add(uint256 _left, uint256 _right) external pure returns (uint256) {
return _left + _right;
}
function willRevert() external pure returns (bool success) {
require(1 == 2, "1 is not equal to 2");
return true;
}
receive() external payable {
require(1 == 2, "1 is not equal to 2");
}
}
contract GovernanceTargetConstructorArgumentStub {
string public greeting;
constructor(string memory _greeting) {
greeting = _greeting;
}
function willRevert() external pure returns (bool success) {
require(1 == 2, "1 is not equal to 2");
return true;
}
receive() external payable {
require(1 == 2, "1 is not equal to 2");
}
}
contract GovernanceStateChangeTargetStub {
string public greeting;
constructor(string memory _greeting) {
greeting = _greeting;
}
function changeGreeting(string calldata _newGreeting) external {
greeting = _newGreeting;
}
function willRevert() external pure returns (bool success) {
require(1 == 2, "1 is not equal to 2");
return true;
}
receive() external payable {
require(1 == 2, "1 is not equal to 2");
}
}
@openzeppelin/contracts/utils/introspection/IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
@openzeppelin/contracts/utils/StorageSlot.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.0;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```solidity
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*
* _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._
* _Available since v4.9 for `string`, `bytes`._
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
/**
* @dev Returns an `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
}
contracts/governance/Staking.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
// OpenZeppelin v4
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
/**
* @title Snapshot
* @author Railgun Contributors
* @notice Governance contract for railgun, handles staking, voting power, and snapshotting
* @dev Snapshots cannot be taken during interval 0
* wait till interval 1 before utilizing snapshots
*/
contract Staking {
using SafeERC20 for IERC20;
// Constants
uint256 public constant STAKE_LOCKTIME = 30 days;
uint256 public constant SNAPSHOT_INTERVAL = 1 days;
// Staking token
IERC20 public stakingToken;
// Time of deployment
// solhint-disable-next-line var-name-mixedcase
uint256 public immutable DEPLOY_TIME = block.timestamp;
// New stake created
event Stake(address indexed account, uint256 indexed stakeID, uint256 amount);
// Stake unlocked (coins removed from voting pool, 30 day delay before claiming is allowed)
event Unlock(address indexed account, uint256 indexed stakeID);
// Stake claimed
event Claim(address indexed account, uint256 indexed stakeID);
// Delegate claimed
event Delegate(
address indexed owner,
address indexed _from,
address indexed to,
uint256 stakeID,
uint256 amount
);
// Total staked
uint256 public totalStaked = 0;
// Snapshots for globals
struct GlobalsSnapshot {
uint256 interval;
uint256 totalVotingPower;
uint256 totalStaked;
}
GlobalsSnapshot[] private globalsSnapshots;
// Stake
struct StakeStruct {
address delegate; // Address stake voting power is delegated to
uint256 amount; // Amount of tokens on this stake
uint256 staketime; // Time this stake was created
uint256 locktime; // Time this stake can be claimed (if 0, unlock hasn't been initiated)
uint256 claimedTime; // Time this stake was claimed (if 0, stake hasn't been claimed)
}
// Stake mapping
// address => stakeID => stake
mapping(address => StakeStruct[]) public stakes;
// Voting power for each account
mapping(address => uint256) public votingPower;
// Snapshots for accounts
struct AccountSnapshot {
uint256 interval;
uint256 votingPower;
}
mapping(address => AccountSnapshot[]) private accountSnapshots;
/**
* @notice Sets staking token
* @param _stakingToken - time to get interval of
*/
constructor(IERC20 _stakingToken) {
stakingToken = _stakingToken;
// Use address 0 to store inverted totalVotingPower
votingPower[address(0)] = type(uint256).max;
}
/**
* @notice Gets total voting power in system
* @return totalVotingPower
*/
function totalVotingPower() public view returns (uint256) {
return ~votingPower[address(0)];
}
/**
* @notice Gets length of stakes array for address
* @param _account - address to retrieve stakes array of
* @return length
*/
function stakesLength(address _account) external view returns (uint256) {
return stakes[_account].length;
}
/**
* @notice Gets interval at time
* @param _time - time to get interval of
* @return interval
*/
function intervalAtTime(uint256 _time) public view returns (uint256) {
require(_time >= DEPLOY_TIME, "Staking: Requested time is before contract was deployed");
return (_time - DEPLOY_TIME) / SNAPSHOT_INTERVAL;
}
/**
* @notice Gets current interval
* @return interval
*/
function currentInterval() public view returns (uint256) {
return intervalAtTime(block.timestamp);
}
/**
* @notice Returns interval of latest global snapshot
* @return Latest global snapshot interval
*/
function latestGlobalsSnapshotInterval() public view returns (uint256) {
if (globalsSnapshots.length > 0) {
// If a snapshot exists return the interval it was taken
return globalsSnapshots[globalsSnapshots.length - 1].interval;
} else {
// Else default to 0
return 0;
}
}
/**
* @notice Returns interval of latest account snapshot
* @param _account - account to get latest snapshot of
* @return Latest account snapshot interval
*/
function latestAccountSnapshotInterval(address _account) public view returns (uint256) {
if (accountSnapshots[_account].length > 0) {
// If a snapshot exists return the interval it was taken
return accountSnapshots[_account][accountSnapshots[_account].length - 1].interval;
} else {
// Else default to 0
return 0;
}
}
/**
* @notice Returns length of snapshot array
* @param _account - account to get snapshot array length of
* @return Snapshot array length
*/
function accountSnapshotLength(address _account) external view returns (uint256) {
return accountSnapshots[_account].length;
}
/**
* @notice Returns length of snapshot array
* @return Snapshot array length
*/
function globalsSnapshotLength() external view returns (uint256) {
return globalsSnapshots.length;
}
/**
* @notice Returns global snapshot at index
* @param _index - account to get latest snapshot of
* @return Globals snapshot
*/
function globalsSnapshot(uint256 _index) external view returns (GlobalsSnapshot memory) {
return globalsSnapshots[_index];
}
/**
* @notice Returns account snapshot at index
* @param _account - account to get snapshot of
* @param _index - index to get snapshot at
* @return Account snapshot
*/
function accountSnapshot(
address _account,
uint256 _index
) external view returns (AccountSnapshot memory) {
return accountSnapshots[_account][_index];
}
/**
* @notice Checks if account and globals snapshots need updating and updates
* @param _account - Account to take snapshot for
*/
function snapshot(address _account) internal {
uint256 _currentInterval = currentInterval();
// If latest global snapshot is less than current interval, push new snapshot
if (latestGlobalsSnapshotInterval() < _currentInterval) {
globalsSnapshots.push(GlobalsSnapshot(_currentInterval, totalVotingPower(), totalStaked));
}
// If latest account snapshot is less than current interval, push new snapshot
// Skip if account is 0 address
if (_account != address(0) && latestAccountSnapshotInterval(_account) < _currentInterval) {
accountSnapshots[_account].push(AccountSnapshot(_currentInterval, votingPower[_account]));
}
}
/**
* @notice Moves voting power in response to delegation or stake/unstake
* @param _from - account to move voting power fom
* @param _to - account to move voting power to
* @param _amount - amount of voting power to move
*/
function moveVotingPower(address _from, address _to, uint256 _amount) internal {
votingPower[_from] -= _amount;
votingPower[_to] += _amount;
}
/**
* @notice Updates vote delegation
* @param _stakeID - stake to delegate
* @param _to - address to delegate to
*/
function delegate(uint256 _stakeID, address _to) public {
StakeStruct storage _stake = stakes[msg.sender][_stakeID];
require(_stake.locktime == 0, "Staking: Stake unlocked");
require(_to != address(0), "Staking: Can't delegate to 0 address");
if (_stake.delegate != _to) {
// Check if snapshot needs to be taken
snapshot(_stake.delegate); // From
snapshot(_to); // To
// Move voting power to delegatee
moveVotingPower(_stake.delegate, _to, _stake.amount);
// Emit event
emit Delegate(msg.sender, _stake.delegate, _to, _stakeID, _stake.amount);
// Update delegation
_stake.delegate = _to;
}
}
/**
* @notice Delegates voting power of stake back to self
* @param _stakeID - stake to delegate back to self
*/
function undelegate(uint256 _stakeID) external {
delegate(_stakeID, msg.sender);
}
/**
* @notice Gets global state at interval
* @param _interval - interval to get state at
* @return state
*/
function globalsSnapshotAtSearch(
uint256 _interval
) internal view returns (GlobalsSnapshot memory) {
// Index of element
uint256 index;
// High/low for binary search to find index
// https://en.wikipedia.org/wiki/Binary_search_algorithm
uint256 low = 0;
uint256 high = globalsSnapshots.length;
while (low < high) {
uint256 mid = Math.average(low, high);
// Note that mid will always be strictly less than high (i.e. it will be a valid array index)
// because Math.average rounds down (it does integer division with truncation).
if (globalsSnapshots[mid].interval > _interval) {
high = mid;
} else {
low = mid + 1;
}
}
// At this point `low` is the exclusive upper bound. Find the inclusive upper bounds and set to index
if (low > 0 && globalsSnapshots[low - 1].interval == _interval) {
return globalsSnapshots[low - 1];
} else {
index = low;
}
// If index is equal to snapshot array length, then no update was made after the requested
// snapshot interval. This means the latest value is the right one.
if (index == globalsSnapshots.length) {
return GlobalsSnapshot(_interval, totalVotingPower(), totalStaked);
} else {
return globalsSnapshots[index];
}
}
/**
* @notice Gets global state at interval
* @param _interval - interval to get state at
* @param _hint - off-chain computed index of interval
* @return state
*/
function globalsSnapshotAt(
uint256 _interval,
uint256 _hint
) external view returns (GlobalsSnapshot memory) {
require(_interval <= currentInterval(), "Staking: Interval out of bounds");
// Check if hint is correct, else fall back to binary search
if (
_hint <= globalsSnapshots.length &&
(_hint == 0 || globalsSnapshots[_hint - 1].interval < _interval) &&
(_hint == globalsSnapshots.length || globalsSnapshots[_hint].interval >= _interval)
) {
// The hint is correct
if (_hint < globalsSnapshots.length) return globalsSnapshots[_hint];
else return GlobalsSnapshot(_interval, totalVotingPower(), totalStaked);
} else return globalsSnapshotAtSearch(_interval);
}
/**
* @notice Gets account state at interval
* @param _account - account to get state for
* @param _interval - interval to get state at
* @return state
*/
function accountSnapshotAtSearch(
address _account,
uint256 _interval
) internal view returns (AccountSnapshot memory) {
// Get account snapshots array
AccountSnapshot[] storage snapshots = accountSnapshots[_account];
// Index of element
uint256 index;
// High/low for binary search to find index
// https://en.wikipedia.org/wiki/Binary_search_algorithm
uint256 low = 0;
uint256 high = snapshots.length;
while (low < high) {
uint256 mid = Math.average(low, high);
// Note that mid will always be strictly less than high (i.e. it will be a valid array index)
// because Math.average rounds down (it does integer division with truncation).
if (snapshots[mid].interval > _interval) {
high = mid;
} else {
low = mid + 1;
}
}
// At this point `low` is the exclusive upper bound. Find the inclusive upper bounds and set to index
if (low > 0 && snapshots[low - 1].interval == _interval) {
return snapshots[low - 1];
} else {
index = low;
}
// If index is equal to snapshot array length, then no update was made after the requested
// snapshot interval. This means the latest value is the right one.
if (index == snapshots.length) {
return AccountSnapshot(_interval, votingPower[_account]);
} else {
return snapshots[index];
}
}
/**
* @notice Gets account state at interval
* @param _account - account to get state for
* @param _interval - interval to get state at
* @param _hint - off-chain computed index of interval
* @return state
*/
function accountSnapshotAt(
address _account,
uint256 _interval,
uint256 _hint
) external view returns (AccountSnapshot memory) {
require(_interval <= currentInterval(), "Staking: Interval out of bounds");
// Get account snapshots array
AccountSnapshot[] storage snapshots = accountSnapshots[_account];
// Check if hint is correct, else fall back to binary search
if (
_hint <= snapshots.length &&
(_hint == 0 || snapshots[_hint - 1].interval < _interval) &&
(_hint == snapshots.length || snapshots[_hint].interval >= _interval)
) {
// The hint is correct
if (_hint < snapshots.length) return snapshots[_hint];
else return AccountSnapshot(_interval, votingPower[_account]);
} else return accountSnapshotAtSearch(_account, _interval);
}
/**
* @notice Stake tokens
* @dev This contract should be approve()'d for _amount
* @param _amount - Amount to stake
* @return stake ID
*/
function stake(uint256 _amount) public returns (uint256) {
// Check if amount is not 0
require(_amount > 0, "Staking: Amount not set");
// Check if snapshot needs to be taken
snapshot(msg.sender);
// Get stakeID
uint256 stakeID = stakes[msg.sender].length;
// Set stake values
stakes[msg.sender].push(StakeStruct(msg.sender, _amount, block.timestamp, 0, 0));
// Increment global staked
totalStaked += _amount;
// Add voting power
moveVotingPower(address(0), msg.sender, _amount);
// Transfer tokens
stakingToken.safeTransferFrom(msg.sender, address(this), _amount);
// Emit event
emit Stake(msg.sender, stakeID, _amount);
return stakeID;
}
/**
* @notice Unlock stake tokens
* @param _stakeID - Stake to unlock
*/
function unlock(uint256 _stakeID) public {
require(stakes[msg.sender][_stakeID].locktime == 0, "Staking: Stake already unlocked");
// Check if snapshot needs to be taken
snapshot(msg.sender);
// Set stake locktime
stakes[msg.sender][_stakeID].locktime = block.timestamp + STAKE_LOCKTIME;
// Remove voting power
moveVotingPower(
stakes[msg.sender][_stakeID].delegate,
address(0),
stakes[msg.sender][_stakeID].amount
);
// Emit event
emit Unlock(msg.sender, _stakeID);
}
/**
* @notice Claim stake token
* @param _stakeID - Stake to claim
*/
function claim(uint256 _stakeID) public {
require(
stakes[msg.sender][_stakeID].locktime != 0 &&
stakes[msg.sender][_stakeID].locktime < block.timestamp,
"Staking: Stake not unlocked"
);
require(stakes[msg.sender][_stakeID].claimedTime == 0, "Staking: Stake already claimed");
// Check if snapshot needs to be taken
snapshot(msg.sender);
// Set stake claimed time
stakes[msg.sender][_stakeID].claimedTime = block.timestamp;
// Decrement global staked
totalStaked -= stakes[msg.sender][_stakeID].amount;
// Transfer tokens
stakingToken.safeTransfer(msg.sender, stakes[msg.sender][_stakeID].amount);
// Emit event
emit Claim(msg.sender, _stakeID);
}
}
@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165Upgradeable {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
contracts/adapt/IRailgunAdapt.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
interface IRailgunAdapt {
// Function should return keccak256(abi.encodePacked("Accept Railgun Session"))
// = 0x32608f8fdf2c6d4f0ed2d88de1e681933b74955f1b66faaa8e1157bac3c90086
// 'magic value' to accept session, anything else to reject it
// gas supplied is 30,000 as in EIP165
function acceptRailgunSession() external view returns (bytes32);
}
contracts/teststubs/logic/RailgunSmartWalletStub.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
import { RailgunSmartWallet, Commitments } from "../../logic/RailgunSmartWallet.sol";
contract RailgunSmartWalletStub is RailgunSmartWallet {
function newTreeStub() external {
Commitments.newTree();
}
function setMerkleRoot(uint256 _treeNumber, bytes32 _root, bool _setting) external {
Commitments.rootHistory[_treeNumber][_root] = _setting;
}
function setNullifier(uint256 _treeNumber, bytes32 _nullifier, bool _setting) external {
Commitments.nullifiers[_treeNumber][_nullifier] = _setting;
}
}
contracts/teststubs/logic/RailgunLogicStub.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
import { CommitmentPreimage, CommitmentCiphertext, Transaction } from "../../logic/Globals.sol";
import { Commitments } from "../../logic/Commitments.sol";
import { RailgunLogic } from "../../logic/RailgunLogic.sol";
contract RailgunLogicStub is RailgunLogic {
function forceNewTree() external {
Commitments.newTree();
}
function doubleInit(
address payable _treasury,
uint120 _shieldFee,
uint120 _unshieldFee,
uint256 _nftFee,
address _owner
) external {
RailgunLogic.initializeRailgunLogic(_treasury, _shieldFee, _unshieldFee, _nftFee, _owner);
}
function setMerkleRoot(uint256 _treeNumber, bytes32 _root, bool _setting) external {
Commitments.rootHistory[_treeNumber][_root] = _setting;
}
function setNullifier(uint256 _treeNumber, bytes32 _nullifier, bool _setting) external {
Commitments.nullifiers[_treeNumber][_nullifier] = _setting;
}
function transferTokenInStub(
CommitmentPreimage calldata _note
) external returns (CommitmentPreimage memory, uint256) {
return RailgunLogic.transferTokenIn(_note);
}
function transferTokenOutStub(CommitmentPreimage calldata _note) external {
RailgunLogic.transferTokenOut(_note);
}
function accumulateAndNullifyTransactionStub(
Transaction calldata _transaction,
uint256 _initialArrayLengths,
uint256 _commitmentsStartOffset
) external returns (uint256, bytes32[] memory, CommitmentCiphertext[] memory) {
bytes32[] memory _commitments = new bytes32[](_initialArrayLengths);
CommitmentCiphertext[] memory _ciphertext = new CommitmentCiphertext[](_initialArrayLengths);
return (
accumulateAndNullifyTransaction(
_transaction,
_commitments,
_commitmentsStartOffset,
_ciphertext
),
_commitments,
_ciphertext
);
}
}
contracts/governance/Delegator.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
// OpenZeppelin v4
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
/**
* @title Delegator
* @author Railgun Contributors
* @notice 'Owner' contract for all railgun contracts
* delegates permissions to other contracts (voter, role)
*/
contract Delegator is Ownable {
/*
Mapping structure is calling address => contract => function signature
0 is used as a wildcard, so permission for contract 0 is permission for
any contract, and permission for function signature 0 is permission for
any function.
Comments below use * to signify wildcard and . notation to separate address/contract/function.
caller.*.* allows caller to call any function on any contract
caller.X.* allows caller to call any function on contract X
caller.*.Y allows caller to call function Y on any contract
*/
mapping(address => mapping(address => mapping(bytes4 => bool))) public permissions;
event GrantPermission(
address indexed caller,
address indexed contractAddress,
bytes4 indexed selector
);
event RevokePermission(
address indexed caller,
address indexed contractAddress,
bytes4 indexed selector
);
/**
* @notice Sets initial admin
*/
constructor(address _admin) {
Ownable.transferOwnership(_admin);
}
/**
* @notice Sets permission bit
* @dev See comment on permissions mapping for wildcard format
* @param _caller - caller to set permissions for
* @param _contract - contract to set permissions for
* @param _selector - selector to set permissions for
* @param _permission - permission bit to set
*/
function setPermission(
address _caller,
address _contract,
bytes4 _selector,
bool _permission
) public onlyOwner {
// If permission set is different to new permission then we execute, otherwise skip
if (permissions[_caller][_contract][_selector] != _permission) {
// Set permission bit
permissions[_caller][_contract][_selector] = _permission;
// Emit event
if (_permission) {
emit GrantPermission(_caller, _contract, _selector);
} else {
emit RevokePermission(_caller, _contract, _selector);
}
}
}
/**
* @notice Checks if caller has permission to execute function
* @param _caller - caller to check permissions for
* @param _contract - contract to check
* @param _selector - function signature to check
* @return if caller has permission
*/
function checkPermission(
address _caller,
address _contract,
bytes4 _selector
) public view returns (bool) {
/*
See comment on permissions mapping for structure
Comments below use * to signify wildcard and . notation to separate contract/function
*/
return (_caller == Ownable.owner() ||
permissions[_caller][_contract][_selector] || // Owner always has global permissions
permissions[_caller][_contract][0x0] || // Permission for function is given
permissions[_caller][address(0)][_selector] || // Permission for _contract.* is given
permissions[_caller][address(0)][0x0]); // Global permission is given
}
/**
* @notice Calls function
* @dev calls to functions on this contract are intercepted and run directly
* this is so the voting contract doesn't need to have special cases for calling
* functions other than this one.
* @param _contract - contract to call
* @param _data - calldata to pass to contract
* @return success - whether call succeeded
* @return returnData - return data from function call
*/
function callContract(
address _contract,
bytes calldata _data,
uint256 _value
) public returns (bool success, bytes memory returnData) {
// Get selector
bytes4 selector = bytes4(_data);
// Intercept calls to this contract
if (_contract == address(this)) {
if (selector == this.setPermission.selector) {
// Decode call data
(address caller, address calledContract, bytes4 _permissionSelector, bool permission) = abi
.decode(abi.encodePacked(_data[4:]), (address, address, bytes4, bool));
// Call setPermission
setPermission(caller, calledContract, _permissionSelector, permission);
// Return success with empty ReturnData bytes
bytes memory empty;
return (true, empty);
} else if (selector == this.transferOwnership.selector) {
// Decode call data
address newOwner = abi.decode(abi.encodePacked(_data[4:]), (address));
// Call transferOwnership
Ownable.transferOwnership(newOwner);
// Return success with empty ReturnData bytes
bytes memory empty;
return (true, empty);
} else if (selector == this.renounceOwnership.selector) {
// Call renounceOwnership
Ownable.renounceOwnership();
// Return success with empty ReturnData bytes
bytes memory empty;
return (true, empty);
} else {
// Return failed with empty ReturnData bytes
bytes memory empty;
return (false, empty);
}
}
// Check permissions
require(
checkPermission(msg.sender, _contract, selector),
"Delegator: Caller doesn't have permission"
);
// Call external contract and return
// solhint-disable-next-line avoid-low-level-calls
return _contract.call{ value: _value }(_data);
}
}
@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol)
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
/**
* @dev _Available since v3.1._
*/
interface IERC1155Receiver is IERC165 {
/**
* @dev Handles the receipt of a single ERC1155 token type. This function is
* called at the end of a `safeTransferFrom` after the balance has been updated.
*
* NOTE: To accept the transfer, this must return
* `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
* (i.e. 0xf23a6e61, or its own function selector).
*
* @param operator The address which initiated the transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param id The ID of the token being transferred
* @param value The amount of tokens being transferred
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
*/
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 value,
bytes calldata data
) external returns (bytes4);
/**
* @dev Handles the receipt of a multiple ERC1155 token types. This function
* is called at the end of a `safeBatchTransferFrom` after the balances have
* been updated.
*
* NOTE: To accept the transfer(s), this must return
* `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
* (i.e. 0xbc197c81, or its own function selector).
*
* @param operator The address which initiated the batch transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param ids An array containing ids of each token being transferred (order and length must match values array)
* @param values An array containing amounts of each token being transferred (order and length must match ids array)
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
*/
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external returns (bytes4);
}
contracts/treasury/Sweeper.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
// OpenZeppelin v4
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/**
* @title Sweeper
* @author Railgun Contributors
* @notice Sweeps funds into another address
*/
contract Sweeper {
address public immutable receiver;
constructor(address _receiver) {
receiver = _receiver;
}
/**
* @notice Transfers ETH to receiver address
*/
function transferETH() external {
// solhint-disable-next-line avoid-low-level-calls
(bool success, ) = receiver.call{ value: address(this).balance }("");
if (!success) revert("ETH transfer failed");
}
/**
* @notice Transfers ERC20 to receiver address
* @param _token - ERC20 token address to transfer
*/
function transferERC20(IERC20 _token) external {
_token.transfer(receiver, _token.balanceOf(address(this)));
}
}
@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
function __Ownable_init() internal onlyInitializing {
__Ownable_init_unchained();
}
function __Ownable_init_unchained() internal onlyInitializing {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(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");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}
contracts/governance/VkeySetter.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
// OpenZeppelin v4
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { Delegator } from "./Delegator.sol";
import { Verifier, VerifyingKey } from "../logic/Verifier.sol";
/**
* @title VKeySetter
* @author Railgun Contributors
* @notice
*/
contract VKeySetter is Ownable {
Delegator public delegator;
Verifier public verifier;
// Lock adding new vKeys once this boolean is flipped
enum VKeySetterState {
SETTING,
WAITING,
COMMITTING
}
VKeySetterState public state;
// Nullifiers => Commitments => Verification Key
mapping(uint256 => mapping(uint256 => VerifyingKey)) private verificationKeys;
// Owner can set vKeys in setting state
// Owner can always change contract to waiting state
// Governance is required to change state to committing state
// Owner can only change contract to setting state when in committing state
modifier onlySetting() {
require(state == VKeySetterState.SETTING, "VKeySetter: Contract is not in setting state");
_;
}
// modifier onlyWaiting() {
// require(state == VKeySetterState.WAITING, "VKeySetter: Contract is not in waiting state");
// _;
// }
modifier onlyCommitting() {
require(state == VKeySetterState.COMMITTING, "VKeySetter: Contract is not in committing state");
_;
}
modifier onlyDelegator() {
require(msg.sender == address(delegator), "VKeySetter: Caller isn't governance");
_;
}
/**
* @notice Sets initial admin and delegator and verifier contract addresses
*/
constructor(address _admin, Delegator _delegator, Verifier _verifier) {
Ownable.transferOwnership(_admin);
delegator = _delegator;
verifier = _verifier;
}
/**
* @notice Sets verification key
* @param _nullifiers - number of nullifiers this verification key is for
* @param _commitments - number of commitments out this verification key is for
* @param _verifyingKey - verifyingKey to set
*/
function setVerificationKey(
uint256 _nullifiers,
uint256 _commitments,
VerifyingKey calldata _verifyingKey
) public onlyOwner onlySetting {
verificationKeys[_nullifiers][_commitments] = _verifyingKey;
}
/**
* @notice Gets verification key
* @param _nullifiers - number of nullifiers this verification key is for
* @param _commitments - number of commitments out this verification key is for
*/
function getVerificationKey(
uint256 _nullifiers,
uint256 _commitments
) external view returns (VerifyingKey memory) {
// Manually add getter so dynamic IC array is included in response
return verificationKeys[_nullifiers][_commitments];
}
/**
* @notice Sets verification key
* @param _nullifiers - array of nullifier values of keys
* @param _commitments - array of commitment values of keys
* @param _verifyingKey - array of keys
*/
function batchSetVerificationKey(
uint256[] calldata _nullifiers,
uint256[] calldata _commitments,
VerifyingKey[] calldata _verifyingKey
) external {
for (uint256 i = 0; i < _nullifiers.length; i += 1) {
setVerificationKey(_nullifiers[i], _commitments[i], _verifyingKey[i]);
}
}
/**
* @notice Commits verification keys to contract
* @param _nullifiers - number of nullifiers this verification key is for
* @param _commitments - number of commitments out this verification key is for
*/
function commitVerificationKey(
uint256 _nullifiers,
uint256 _commitments
) public onlyOwner onlyCommitting {
// NOTE: The vkey configuration must EXACTLY match the desired vkey configuration on the verifier contract
// Leaving a vkey empty on this contract can be used to delete a vkey on the verifier contract by setting
// the values to 0
delegator.callContract(
address(verifier),
abi.encodeWithSelector(
Verifier.setVerificationKey.selector,
_nullifiers,
_commitments,
verificationKeys[_nullifiers][_commitments]
),
0
);
}
/**
* @notice Commits verification keys to contract as batch
* @param _nullifiers - array of nullifier values of keys
* @param _commitments - array of commitment values of keys
*/
function batchCommitVerificationKey(
uint256[] calldata _nullifiers,
uint256[] calldata _commitments
) external {
for (uint256 i = 0; i < _nullifiers.length; i += 1) {
commitVerificationKey(_nullifiers[i], _commitments[i]);
}
}
/**
* @notice Set state to 'setting'
*/
function stateToSetting() external onlyOwner onlyCommitting {
state = VKeySetterState.SETTING;
}
/**
* @notice Set state to 'waiting'
*/
function stateToWaiting() external onlyOwner {
state = VKeySetterState.WAITING;
}
/**
* @notice Set state to 'committing'
*/
function stateToCommitting() external onlyDelegator {
state = VKeySetterState.COMMITTING;
}
}
@openzeppelin/contracts/token/ERC721/ERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/ERC721.sol)
pragma solidity ^0.8.0;
import "./IERC721.sol";
import "./IERC721Receiver.sol";
import "./extensions/IERC721Metadata.sol";
import "../../utils/Address.sol";
import "../../utils/Context.sol";
import "../../utils/Strings.sol";
import "../../utils/introspection/ERC165.sol";
/**
* @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
* the Metadata extension, but not including the Enumerable extension, which is available separately as
* {ERC721Enumerable}.
*/
contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
using Address for address;
using Strings for uint256;
// Token name
string private _name;
// Token symbol
string private _symbol;
// Mapping from token ID to owner address
mapping(uint256 => address) private _owners;
// Mapping owner address to token count
mapping(address => uint256) private _balances;
// Mapping from token ID to approved address
mapping(uint256 => address) private _tokenApprovals;
// Mapping from owner to operator approvals
mapping(address => mapping(address => bool)) private _operatorApprovals;
/**
* @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return
interfaceId == type(IERC721).interfaceId ||
interfaceId == type(IERC721Metadata).interfaceId ||
super.supportsInterface(interfaceId);
}
/**
* @dev See {IERC721-balanceOf}.
*/
function balanceOf(address owner) public view virtual override returns (uint256) {
require(owner != address(0), "ERC721: address zero is not a valid owner");
return _balances[owner];
}
/**
* @dev See {IERC721-ownerOf}.
*/
function ownerOf(uint256 tokenId) public view virtual override returns (address) {
address owner = _ownerOf(tokenId);
require(owner != address(0), "ERC721: invalid token ID");
return owner;
}
/**
* @dev See {IERC721Metadata-name}.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev See {IERC721Metadata-symbol}.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev See {IERC721Metadata-tokenURI}.
*/
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
_requireMinted(tokenId);
string memory baseURI = _baseURI();
return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
}
/**
* @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
* token will be the concatenation of the `baseURI` and the `tokenId`. Empty
* by default, can be overridden in child contracts.
*/
function _baseURI() internal view virtual returns (string memory) {
return "";
}
/**
* @dev See {IERC721-approve}.
*/
function approve(address to, uint256 tokenId) public virtual override {
address owner = ERC721.ownerOf(tokenId);
require(to != owner, "ERC721: approval to current owner");
require(
_msgSender() == owner || isApprovedForAll(owner, _msgSender()),
"ERC721: approve caller is not token owner or approved for all"
);
_approve(to, tokenId);
}
/**
* @dev See {IERC721-getApproved}.
*/
function getApproved(uint256 tokenId) public view virtual override returns (address) {
_requireMinted(tokenId);
return _tokenApprovals[tokenId];
}
/**
* @dev See {IERC721-setApprovalForAll}.
*/
function setApprovalForAll(address operator, bool approved) public virtual override {
_setApprovalForAll(_msgSender(), operator, approved);
}
/**
* @dev See {IERC721-isApprovedForAll}.
*/
function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
return _operatorApprovals[owner][operator];
}
/**
* @dev See {IERC721-transferFrom}.
*/
function transferFrom(address from, address to, uint256 tokenId) public virtual override {
//solhint-disable-next-line max-line-length
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
_transfer(from, to, tokenId);
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {
safeTransferFrom(from, to, tokenId, "");
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual override {
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
_safeTransfer(from, to, tokenId, data);
}
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* `data` is additional data, it has no specified format and it is sent in call to `to`.
*
* This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
* implement alternative mechanisms to perform token transfer, such as signature-based.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual {
_transfer(from, to, tokenId);
require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer");
}
/**
* @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
*/
function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
return _owners[tokenId];
}
/**
* @dev Returns whether `tokenId` exists.
*
* Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
*
* Tokens start existing when they are minted (`_mint`),
* and stop existing when they are burned (`_burn`).
*/
function _exists(uint256 tokenId) internal view virtual returns (bool) {
return _ownerOf(tokenId) != address(0);
}
/**
* @dev Returns whether `spender` is allowed to manage `tokenId`.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
address owner = ERC721.ownerOf(tokenId);
return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
}
/**
* @dev Safely mints `tokenId` and transfers it to `to`.
*
* Requirements:
*
* - `tokenId` must not exist.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function _safeMint(address to, uint256 tokenId) internal virtual {
_safeMint(to, tokenId, "");
}
/**
* @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
* forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
*/
function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {
_mint(to, tokenId);
require(
_checkOnERC721Received(address(0), to, tokenId, data),
"ERC721: transfer to non ERC721Receiver implementer"
);
}
/**
* @dev Mints `tokenId` and transfers it to `to`.
*
* WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
*
* Requirements:
*
* - `tokenId` must not exist.
* - `to` cannot be the zero address.
*
* Emits a {Transfer} event.
*/
function _mint(address to, uint256 tokenId) internal virtual {
require(to != address(0), "ERC721: mint to the zero address");
require(!_exists(tokenId), "ERC721: token already minted");
_beforeTokenTransfer(address(0), to, tokenId, 1);
// Check that tokenId was not minted by `_beforeTokenTransfer` hook
require(!_exists(tokenId), "ERC721: token already minted");
unchecked {
// Will not overflow unless all 2**256 token ids are minted to the same owner.
// Given that tokens are minted one by one, it is impossible in practice that
// this ever happens. Might change if we allow batch minting.
// The ERC fails to describe this case.
_balances[to] += 1;
}
_owners[tokenId] = to;
emit Transfer(address(0), to, tokenId);
_afterTokenTransfer(address(0), to, tokenId, 1);
}
/**
* @dev Destroys `tokenId`.
* The approval is cleared when the token is burned.
* This is an internal function that does not check if the sender is authorized to operate on the token.
*
* Requirements:
*
* - `tokenId` must exist.
*
* Emits a {Transfer} event.
*/
function _burn(uint256 tokenId) internal virtual {
address owner = ERC721.ownerOf(tokenId);
_beforeTokenTransfer(owner, address(0), tokenId, 1);
// Update ownership in case tokenId was transferred by `_beforeTokenTransfer` hook
owner = ERC721.ownerOf(tokenId);
// Clear approvals
delete _tokenApprovals[tokenId];
unchecked {
// Cannot overflow, as that would require more tokens to be burned/transferred
// out than the owner initially received through minting and transferring in.
_balances[owner] -= 1;
}
delete _owners[tokenId];
emit Transfer(owner, address(0), tokenId);
_afterTokenTransfer(owner, address(0), tokenId, 1);
}
/**
* @dev Transfers `tokenId` from `from` to `to`.
* As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
*
* Emits a {Transfer} event.
*/
function _transfer(address from, address to, uint256 tokenId) internal virtual {
require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
require(to != address(0), "ERC721: transfer to the zero address");
_beforeTokenTransfer(from, to, tokenId, 1);
// Check that tokenId was not transferred by `_beforeTokenTransfer` hook
require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
// Clear approvals from the previous owner
delete _tokenApprovals[tokenId];
unchecked {
// `_balances[from]` cannot overflow for the same reason as described in `_burn`:
// `from`'s balance is the number of token held, which is at least one before the current
// transfer.
// `_balances[to]` could overflow in the conditions described in `_mint`. That would require
// all 2**256 token ids to be minted, which in practice is impossible.
_balances[from] -= 1;
_balances[to] += 1;
}
_owners[tokenId] = to;
emit Transfer(from, to, tokenId);
_afterTokenTransfer(from, to, tokenId, 1);
}
/**
* @dev Approve `to` to operate on `tokenId`
*
* Emits an {Approval} event.
*/
function _approve(address to, uint256 tokenId) internal virtual {
_tokenApprovals[tokenId] = to;
emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
}
/**
* @dev Approve `operator` to operate on all of `owner` tokens
*
* Emits an {ApprovalForAll} event.
*/
function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
require(owner != operator, "ERC721: approve to caller");
_operatorApprovals[owner][operator] = approved;
emit ApprovalForAll(owner, operator, approved);
}
/**
* @dev Reverts if the `tokenId` has not been minted yet.
*/
function _requireMinted(uint256 tokenId) internal view virtual {
require(_exists(tokenId), "ERC721: invalid token ID");
}
/**
* @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
* The call is not executed if the target address is not a contract.
*
* @param from address representing the previous owner of the given token ID
* @param to target address that will receive the tokens
* @param tokenId uint256 ID of the token to be transferred
* @param data bytes optional data to send along with the call
* @return bool whether the call correctly returned the expected magic value
*/
function _checkOnERC721Received(
address from,
address to,
uint256 tokenId,
bytes memory data
) private returns (bool) {
if (to.isContract()) {
try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
return retval == IERC721Receiver.onERC721Received.selector;
} catch (bytes memory reason) {
if (reason.length == 0) {
revert("ERC721: transfer to non ERC721Receiver implementer");
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
} else {
return true;
}
}
/**
* @dev Hook that is called before any token transfer. This includes minting and burning. If {ERC721Consecutive} is
* used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, ``from``'s tokens will be transferred to `to`.
* - When `from` is zero, the tokens will be minted for `to`.
* - When `to` is zero, ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
* - `batchSize` is non-zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal virtual {}
/**
* @dev Hook that is called after any token transfer. This includes minting and burning. If {ERC721Consecutive} is
* used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, ``from``'s tokens were transferred to `to`.
* - When `from` is zero, the tokens were minted for `to`.
* - When `to` is zero, ``from``'s tokens were burned.
* - `from` and `to` are never both zero.
* - `batchSize` is non-zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal virtual {}
/**
* @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override.
*
* WARNING: Anyone calling this MUST ensure that the balances remain consistent with the ownership. The invariant
* being that for any address `a` the value returned by `balanceOf(a)` must be equal to the number of tokens such
* that `ownerOf(tokenId)` is `a`.
*/
// solhint-disable-next-line func-name-mixedcase
function __unsafe_increaseBalance(address account, uint256 amount) internal {
_balances[account] += amount;
}
}
@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
contracts/proxy/Proxy.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
// OpenZeppelin v4
import { StorageSlot } from "@openzeppelin/contracts/utils/StorageSlot.sol";
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
/**
* @title PausableUpgradableProxy
* @author Railgun Contributors
* @notice Delegates calls to implementation address
* @dev Calls are reverted if the contract is paused
*/
contract PausableUpgradableProxy {
// Storage slot locations
bytes32 private constant IMPLEMENTATION_SLOT =
bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1);
bytes32 private constant ADMIN_SLOT = bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1);
bytes32 private constant PAUSED_SLOT = bytes32(uint256(keccak256("eip1967.proxy.paused")) - 1);
// Events
event ProxyUpgrade(address previousImplementation, address newImplementation);
event ProxyOwnershipTransfer(address previousOwner, address newOwner);
event ProxyPause();
event ProxyUnpause();
/**
* @notice Sets initial specified admin value
* Implementation is set as 0x0 and contract is created as paused
* @dev Implementation must be set before unpausing
*/
constructor(address _admin) {
// Set initial value for admin
StorageSlot.getAddressSlot(ADMIN_SLOT).value = _admin;
// Explicitly initialize implementation as 0
StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = address(0);
// Explicitly initialize as paused
StorageSlot.getBooleanSlot(PAUSED_SLOT).value = true;
}
/**
* @notice Reverts if proxy is paused
*/
modifier notPaused() {
// Revert if the contract is paused
require(!StorageSlot.getBooleanSlot(PAUSED_SLOT).value, "Proxy: Contract is paused");
_;
}
/**
* @notice Delegates call to implementation
*/
function delegate() internal notPaused {
// Get implementation
address implementation = StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
// Check that implementation exists
require(Address.isContract(implementation), "Proxy: Implementation doesn't exist");
// solhint-disable-next-line no-inline-assembly
assembly {
// Copy msg.data. We take full control of memory in this inline assembly
// block because it will not return to Solidity code. We overwrite the
// Solidity scratch pad at memory position 0.
calldatacopy(0, 0, calldatasize())
// Call the implementation.
// out and outsize are 0 because we don't know the size yet.
let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
// Copy the returned data.
returndatacopy(0, 0, returndatasize())
switch result
// delegatecall returns 0 on error.
case 0 {
revert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}
/**
* @notice Prevents calls unless caller is owner
* @dev This should be on all external/public functions that aren't the fallback
*/
modifier onlyOwner() {
if (msg.sender == StorageSlot.getAddressSlot(ADMIN_SLOT).value) {
_;
} else {
// Redirect to delegate if caller isn't owner
delegate();
}
}
/**
* @notice fallback function that delegates calls with calldata
*/
fallback() external payable {
delegate();
}
/**
* @notice fallback function that delegates calls with no calldata
*/
receive() external payable {
delegate();
}
/**
* @notice Transfers ownership to new address
* @param _newOwner - Address to transfer ownership to
*/
function transferOwnership(address _newOwner) external onlyOwner {
require(_newOwner != address(0), "Proxy: Preventing potential accidental burn");
// Get admin slot
StorageSlot.AddressSlot storage admin = StorageSlot.getAddressSlot(ADMIN_SLOT);
// Emit event
emit ProxyOwnershipTransfer(admin.value, _newOwner);
// Store new admin
admin.value = _newOwner;
}
/**
* @notice Upgrades implementation
* @param _newImplementation - Address of the new implementation
*/
function upgrade(address _newImplementation) external onlyOwner {
// Get implementation slot
StorageSlot.AddressSlot storage implementation = StorageSlot.getAddressSlot(
IMPLEMENTATION_SLOT
);
// If new implementation is identical to old, skip
if (implementation.value != _newImplementation) {
// Emit event
emit ProxyUpgrade(implementation.value, _newImplementation);
// Store new implementation
implementation.value = _newImplementation;
}
}
/**
* @notice Pauses contract
*/
function pause() external onlyOwner {
// Get paused slot
StorageSlot.BooleanSlot storage paused = StorageSlot.getBooleanSlot(PAUSED_SLOT);
// If not already paused, pause
if (!paused.value) {
// Set paused to true
paused.value = true;
// Emit paused event
emit ProxyPause();
}
}
/**
* @notice Unpauses contract
*/
function unpause() external onlyOwner {
// Get paused slot
StorageSlot.BooleanSlot storage paused = StorageSlot.getBooleanSlot(PAUSED_SLOT);
// If already unpaused, do nothing
if (paused.value) {
// Set paused to true
paused.value = false;
// Emit paused event
emit ProxyUnpause();
}
}
}
@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
pragma solidity ^0.8.0;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}
@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.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 Address for address;
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
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'
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));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @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");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
/**
* @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).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// 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 cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}
@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library MathUpgradeable {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}
contracts/governance/Voting.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
// OpenZeppelin v4
import { Staking } from "./Staking.sol";
import { Delegator } from "./Delegator.sol";
/**
* @title Voting
* @author Railgun Contributors
* @notice Governance contract for railgun, handles voting.
*/
contract Voting {
// Time offsets from publish time, offset times are relative to voteCallTime
uint256 public constant SPONSOR_WINDOW = 30 days;
uint256 public constant VOTING_START_OFFSET = 2 days; // Should be > interval size of staking snapshots
uint256 public constant VOTING_YAY_END_OFFSET = 5 days;
uint256 public constant VOTING_NAY_END_OFFSET = 6 days;
uint256 public constant EXECUTION_START_OFFSET = 7 days;
uint256 public constant EXECUTION_END_OFFSET = 14 days;
uint256 public constant SPONSOR_LOCKOUT_TIME = 7 days;
// Threshold constants
uint256 public constant QUORUM = 2000000e18; // 2 million, 18 decimal places
uint256 public constant PROPOSAL_SPONSOR_THRESHOLD = 500000e18; // 500 thousand, 18 decimal places
// Proposal has been created
event Proposal(uint256 indexed id, address indexed proposer);
// Proposal has been sponsored
event Sponsorship(uint256 indexed id, address indexed sponsor, uint256 amount);
// Proposal has been unsponsored
event SponsorshipRevocation(uint256 indexed id, address indexed sponsor, uint256 amount);
// Proposal vote called
event VoteCall(uint256 indexed id);
// Vote cast on proposal
event VoteCast(uint256 indexed id, address indexed voter, bool affirmative, uint256 votes);
// Proposal executed
event Execution(uint256 indexed id);
// Proposal executed
event VoteKeySet(address indexed account, address votingKey);
// Errors event
error ExecutionFailed(uint256 index, bytes data);
// Function call
struct Call {
address callContract;
bytes data;
uint256 value;
}
// Governance proposals
struct ProposalStruct {
// Execution status
bool executed;
// Proposal Data
address proposer;
string proposalDocument; // IPFS hash
Call[] actions;
// Event timestamps
uint256 publishTime;
uint256 voteCallTime; // If vote call time is 0, proposal hasn't gone to vote
// Sponsorship info
uint256 sponsorship;
mapping(address => uint256) sponsors;
// Vote data
// Amount of voting power used for accounts, used for fractional voting from contracts
mapping(address => uint256) voted;
uint256 yayVotes;
uint256 nayVotes;
// Staking snapshots
uint256 sponsorInterval;
uint256 votingInterval;
}
// Proposals id => proposal data
ProposalStruct[] public proposals;
// Voting keys
mapping(address => address) public votingKey;
// Last sponsored proposal data
struct LastSponsored {
uint256 lastSponsorTime;
uint256 proposalID;
}
mapping(address => LastSponsored) public lastSponsored;
/* solhint-disable var-name-mixedcase */
Staking public immutable STAKING_CONTRACT;
Delegator public immutable DELEGATOR_CONTRACT;
/* solhint-enable var-name-mixedcase */
// Only voting key modifier
modifier onlyVotingKey(address _account) {
// Only voting key or main key can call
require(
msg.sender == _account || msg.sender == votingKey[_account],
"Voting: Caller not authorized"
);
_;
}
/**
* @notice Sets governance token ID and delegator contract
*/
constructor(Staking _stakingContract, Delegator _delegator) {
STAKING_CONTRACT = _stakingContract;
DELEGATOR_CONTRACT = _delegator;
}
/**
* @notice Gets length of proposals array
* @return length
*/
function proposalsLength() external view returns (uint256) {
return proposals.length;
}
/**
* @notice Gets actions from proposal document
* @dev Gets actions from proposal as nested arrays won't be returned on public getter
* @param _id - Proposal to get actions of
* @return actions
*/
function getActions(uint256 _id) external view returns (Call[] memory) {
return proposals[_id].actions;
}
/**
* @notice Gets sponsor amount an account has given to a proposal
* @dev Gets actions from proposal as mappings wont be returned on public getter
* @param _id - Proposal to get sponsor amount of
* @param _account - Account to get sponsor amount for
* @return sponsor amount
*/
function getSponsored(uint256 _id, address _account) external view returns (uint256) {
return proposals[_id].sponsors[_account];
}
/**
* @notice Gets votes cast by an account on a particular proposal
* @dev Gets votes from proposal as mappings wont be returned on public getter
* @param _id - Proposal to get votes for
* @param _account - Account to get votes for
* @return votes amount
*/
function getVotes(uint256 _id, address _account) external view returns (uint256) {
return proposals[_id].voted[_account];
}
/**
* @notice Sets voting key for account
* @param _votingKey - voting key address
*/
function setVotingKey(address _votingKey) external {
votingKey[msg.sender] = _votingKey;
emit VoteKeySet(msg.sender, _votingKey);
}
/**
* @notice Creates governance proposal
* @param _proposalDocument - IPFS multihash of proposal document
* @param _actions - actions to take
*/
function createProposal(
string calldata _proposalDocument,
Call[] calldata _actions
) external returns (uint256) {
// Don't allow proposals with no actions
require(_actions.length > 0, "Voting: No actions specified");
uint256 proposalID = proposals.length;
ProposalStruct storage proposal = proposals.push();
// Store proposer
proposal.proposer = msg.sender;
// Store proposal document
proposal.proposalDocument = _proposalDocument;
// Store published time
proposal.publishTime = block.timestamp;
// Store sponsor voting snapshot interval
proposal.sponsorInterval = STAKING_CONTRACT.currentInterval();
// Loop over actions and copy manually as solidity doesn't support copying struct arrays from calldata
for (uint256 i = 0; i < _actions.length; i += 1) {
proposal.actions.push(Call(_actions[i].callContract, _actions[i].data, _actions[i].value));
}
// Emit event
emit Proposal(proposalID, msg.sender);
return proposalID;
}
/**
* @notice Sponsor proposal
* @param _id - id of proposal to sponsor
* @param _amount - amount to sponsor with
* @param _account - account to vote with
* @param _hint - hint for snapshot search
*/
function sponsorProposal(
uint256 _id,
uint256 _amount,
address _account,
uint256 _hint
) external onlyVotingKey(_account) {
// Prevent proposal spam
require(
lastSponsored[_account].proposalID == _id ||
block.timestamp - lastSponsored[_account].lastSponsorTime > 7 days,
"Voting: Can only sponsor one proposal per week"
);
ProposalStruct storage proposal = proposals[_id];
// Check proposal hasn't already gone to vote
require(proposal.voteCallTime == 0, "Voting: Gone to vote");
// Check proposal is still in sponsor window
require(
block.timestamp < proposal.publishTime + SPONSOR_WINDOW,
"Voting: Sponsoring window passed"
);
// Set last sponsored info
lastSponsored[_account].proposalID = _id;
lastSponsored[_account].lastSponsorTime = block.timestamp;
// Get address sponsor voting power
Staking.AccountSnapshot memory snapshot = STAKING_CONTRACT.accountSnapshotAt(
_account,
proposal.sponsorInterval,
_hint
);
// Can't sponsor with more than voting power
require(
proposal.sponsors[_account] + _amount <= snapshot.votingPower,
"Voting: Not enough voting power"
);
// Update address sponsorship amount on proposal
proposal.sponsors[_account] += _amount;
// Update sponsor total
proposal.sponsorship += _amount;
// Emit event
emit Sponsorship(_id, _account, _amount);
}
/**
* @notice Unsponsor proposal
* @param _id - id of proposal to sponsor
* @param _account - account to vote with
* @param _amount - amount to sponsor with
*/
function unsponsorProposal(
uint256 _id,
uint256 _amount,
address _account
) external onlyVotingKey(_account) {
ProposalStruct storage proposal = proposals[_id];
// Check proposal hasn't already gone to vote
require(proposal.voteCallTime == 0, "Voting: Gone to vote");
// Check proposal is still in sponsor window
require(
block.timestamp < proposal.publishTime + SPONSOR_WINDOW,
"Voting: Sponsoring window passed"
);
// Can't unsponsor more than sponsored
require(_amount <= proposal.sponsors[_account], "Voting: Amount greater than sponsored");
// Update address sponsorship amount on proposal
proposal.sponsors[_account] -= _amount;
// Update sponsor total
proposal.sponsorship -= _amount;
// Emit event
emit SponsorshipRevocation(_id, _account, _amount);
}
/**
* @notice Call vote
* @param _id - id of proposal to call to vote
*/
function callVote(uint256 _id) external {
ProposalStruct storage proposal = proposals[_id];
// Check proposal hasn't exceeded sponsor window
require(
block.timestamp < proposal.publishTime + SPONSOR_WINDOW,
"Voting: Sponsoring window passed"
);
// Check proposal hasn't already gone to vote
require(proposal.voteCallTime == 0, "Voting: Proposal already gone to vote");
// Proposal must meet sponsorship threshold
require(
proposal.sponsorship >= PROPOSAL_SPONSOR_THRESHOLD,
"Voting: Sponsor threshold not met"
);
// Log vote time (also marks proposal as ready to vote)
proposal.voteCallTime = block.timestamp;
// Log governance token snapshot interval
// VOTING_START_OFFSET must be greater than snapshot interval of governance token for this to work correctly
proposal.votingInterval = STAKING_CONTRACT.currentInterval();
// Emit event
emit VoteCall(_id);
}
/**
* @notice Vote on proposal
* @param _id - id of proposal to call to vote
* @param _amount - amount of voting power to allocate
* @param _affirmative - whether to vote yay (true) or nay (false) on this proposal
* @param _account - account to vote with
* @param _hint - hint for snapshot search
*/
function vote(
uint256 _id,
uint256 _amount,
bool _affirmative,
address _account,
uint256 _hint
) external onlyVotingKey(_account) {
ProposalStruct storage proposal = proposals[_id];
// Check vote has been called
require(proposal.voteCallTime > 0, "Voting: Vote hasn't been called for this proposal");
// Check Voting window has opened
require(
block.timestamp > proposal.voteCallTime + VOTING_START_OFFSET,
"Voting: Voting window hasn't opened"
);
// Check voting window hasn't closed (voting window length conditional on )
if (_affirmative) {
require(
block.timestamp < proposal.voteCallTime + VOTING_YAY_END_OFFSET,
"Voting: Affirmative voting window has closed"
);
} else {
require(
block.timestamp < proposal.voteCallTime + VOTING_NAY_END_OFFSET,
"Voting: Negative voting window has closed"
);
}
// Get address voting power
Staking.AccountSnapshot memory snapshot = STAKING_CONTRACT.accountSnapshotAt(
_account,
proposal.votingInterval,
_hint
);
// Check address isn't voting with more voting power than it has
require(
proposal.voted[_account] + _amount <= snapshot.votingPower,
"Voting: Not enough voting power to cast this vote"
);
// Update account voted amount
proposal.voted[_account] += _amount;
// Update voting totals
if (_affirmative) {
proposal.yayVotes += _amount;
} else {
proposal.nayVotes += _amount;
}
// Emit event
emit VoteCast(_id, _account, _affirmative, _amount);
}
/**
* @notice Execute proposal
* @param _id - id of proposal to execute
*/
function executeProposal(uint256 _id) external {
ProposalStruct storage proposal = proposals[_id];
// Check proposal has been called to vote
require(proposal.voteCallTime > 0, "Voting: Vote hasn't been called for this proposal");
// Check quorum has been reached
require(proposal.yayVotes >= QUORUM, "Voting: Quorum hasn't been reached");
// Check vote passed
require(proposal.yayVotes > proposal.nayVotes, "Voting: Proposal hasn't passed vote");
// Check we're in execution window
require(
block.timestamp > proposal.voteCallTime + EXECUTION_START_OFFSET,
"Voting: Execution window hasn't opened"
);
require(
block.timestamp < proposal.voteCallTime + EXECUTION_END_OFFSET,
"Voting: Execution window has closed"
);
// Check proposal hasn't been executed before
require(!proposal.executed, "Voting: Proposal has already been executed");
// Mark proposal as executed
proposal.executed = true;
Call[] memory actions = proposal.actions;
// Loop over actions and execute
for (uint256 i = 0; i < actions.length; i += 1) {
// Execute action
(bool successful, bytes memory returnData) = DELEGATOR_CONTRACT.callContract(
actions[i].callContract,
actions[i].data,
actions[i].value
);
// If an action fails to execute, catch and bubble up reason with revert
if (!successful) {
revert ExecutionFailed(i, returnData);
}
}
// Emit event
emit Execution(_id);
}
}
contracts/adapt/Relay.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
// OpenZeppelin v4
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import { IWBase } from "./IWBase.sol";
import { VERIFICATION_BYPASS, Transaction, ShieldRequest, TokenData, TokenType } from "../logic/Globals.sol";
import { RailgunSmartWallet } from "../logic/RailgunSmartWallet.sol";
/**
* @title Relay Adapt
* @author Railgun Contributors
* @notice Multicall adapt contract for Railgun with relayer support
*/
contract RelayAdapt {
using SafeERC20 for IERC20;
// Set to true if contract is executing
bool private isExecuting = false;
struct Call {
address to;
bytes data;
uint256 value;
}
struct ActionData {
bytes31 random; // Random value (shouldn't be reused if resubmitting the same transaction
// through another relayer or resubmitting on failed transaction - i.e. the same
// nullifier:random combination should never be reused)
bool requireSuccess; // If the transaction should require success on all sub calls
uint256 minGasLimit; // Minimum gas that should be supplied to this transaction
Call[] calls; // Array of calls to execute during transaction
}
struct TokenTransfer {
TokenData token;
address to;
uint256 value; // 0 to send entire balance
}
// Custom errors
error CallFailed(uint256 callIndex, bytes revertReason);
// Events
event CallError(uint256 callIndex, bytes revertReason);
// External contract addresses
RailgunSmartWallet public railgun;
IWBase public wBase;
/**
* @notice only allows self calls to these contracts if contract is executing
*/
modifier onlySelfIfExecuting() {
require(
!isExecuting || msg.sender == address(this),
"RelayAdapt: External call to onlySelf function"
);
isExecuting = true;
_;
isExecuting = false;
}
/**
* @notice Sets Railgun contract and wBase address
*/
constructor(RailgunSmartWallet _railgun, IWBase _wBase) {
railgun = _railgun;
wBase = _wBase;
}
/**
* @notice Executes a batch of Railgun shields
* @param _shieldRequests - Tokens to shield
*/
function shield(ShieldRequest[] calldata _shieldRequests) external onlySelfIfExecuting {
// Loop through each token specified for shield and shield requested balance
uint256 numValidTokens = 0;
uint120[] memory values = new uint120[](_shieldRequests.length);
for (uint256 i = 0; i < _shieldRequests.length; i += 1) {
if (_shieldRequests[i].preimage.token.tokenType == TokenType.ERC20) {
// ERC20
IERC20 token = IERC20(_shieldRequests[i].preimage.token.tokenAddress);
if (_shieldRequests[i].preimage.value == 0) {
// If balance is 0 then shield the entire token balance
// Set values to balance of this contract, capped at
// type(uint120).max to fit Railgun's note max value
values[i] = uint120(token.balanceOf(address(this)));
} else {
values[i] = _shieldRequests[i].preimage.value;
}
// Approve the balance for shield
// Set to 0 first for the following reasons:
// https://github.com/Uniswap/interface/issues/1034
// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
token.safeApprove(address(railgun), 0);
token.safeApprove(address(railgun), values[i]);
// Increment number of valid tokens if we have a balance to deposit
if (values[i] > 0) {
numValidTokens += 1;
}
} else if (_shieldRequests[i].preimage.token.tokenType == TokenType.ERC721) {
// ERC721 token
IERC721 token = IERC721(_shieldRequests[i].preimage.token.tokenAddress);
// Approve NFT for shield
token.approve(address(railgun), _shieldRequests[i].preimage.token.tokenSubID);
// Set value to 1
values[i] = 1;
// Increment number of valid tokens
numValidTokens += 1;
} else {
// ERC1155 token
revert("RelayAdapt: ERC1155 not yet supported");
}
}
// Noop if all tokens requested to shield are 0 balance
if (numValidTokens == 0) {
return;
}
// Filter commitmentPreImages for != 0 (remove 0 balance tokens).
// Initialize filtered array for length valid tokens
ShieldRequest[] memory filteredShieldRequests = new ShieldRequest[](numValidTokens);
uint256 filteredIndex = 0;
// Loop through shields and push non-0 values to filtered array
for (uint256 i = 0; i < _shieldRequests.length; i += 1) {
if (values[i] != 0) {
// Push to filtered array
filteredShieldRequests[filteredIndex] = _shieldRequests[i];
// Set value to adjusted value (if adjusted)
filteredShieldRequests[filteredIndex].preimage.value = values[i];
// Increment index of filtered arrays
filteredIndex += 1;
}
}
// Shield to railgun
railgun.shield(filteredShieldRequests);
}
/**
* @notice Sends tokens to particular address
* @param _transfers - tokens to send (0x0 - ERC20 is base)
*/
function transfer(TokenTransfer[] calldata _transfers) external onlySelfIfExecuting {
for (uint256 i = 0; i < _transfers.length; i += 1) {
if (
_transfers[i].token.tokenType == TokenType.ERC20 &&
_transfers[i].token.tokenAddress == address(0)
) {
// BASE
// Fetch balance
uint256 amount = _transfers[i].value == 0 ? address(this).balance : _transfers[i].value;
// Transfer base tokens
// solhint-disable-next-line avoid-low-level-calls
(bool success, ) = _transfers[i].to.call{ value: amount }("");
// Check transfer succeeded
require(success, "RelayAdapt: ETH transfer failed");
} else if (_transfers[i].token.tokenType == TokenType.ERC20) {
// ERC20
IERC20 token = IERC20(_transfers[i].token.tokenAddress);
// Fetch balance
uint256 amount = _transfers[i].value == 0
? token.balanceOf(address(this))
: _transfers[i].value;
// Transfer token
token.safeTransfer(_transfers[i].to, amount);
} else if (_transfers[i].token.tokenType == TokenType.ERC721) {
// ERC721 token
IERC721 token = IERC721(_transfers[i].token.tokenAddress);
// Transfer token
token.transferFrom(address(this), _transfers[i].to, _transfers[i].token.tokenSubID);
} else {
// ERC1155 token
revert("RelayAdapt: ERC1155 not yet supported");
}
}
}
/**
* @notice Wraps base tokens in contract
* @param _amount - amount to wrap (0 = wrap all)
*/
function wrapBase(uint256 _amount) external onlySelfIfExecuting {
// Fetch balance
uint256 balance = _amount == 0 ? address(this).balance : _amount;
// Wrap
wBase.deposit{ value: balance }();
}
/**
* @notice Unwraps wrapped base tokens in contract
* @param _amount - amount to unwrap (0 = unwrap all)
*/
function unwrapBase(uint256 _amount) external onlySelfIfExecuting {
// Fetch balance
uint256 balance = _amount == 0 ? wBase.balanceOf(address(this)) : _amount;
// Unwrap
wBase.withdraw(balance);
}
/**
* @notice Executes multicall batch
* @param _requireSuccess - Whether transaction should throw on call failure
* @param _calls - multicall array
*/
function _multicall(bool _requireSuccess, Call[] calldata _calls) internal {
// Loop through each call
for (uint256 i = 0; i < _calls.length; i += 1) {
// Retrieve call
Call calldata call = _calls[i];
// Don't allow calls to Railgun contract in multicall
if (call.to == address(railgun)) {
revert CallFailed(i, bytes("RelayAdapt: Refusing to call Railgun contract"));
}
// Execute call
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returned) = call.to.call{ value: call.value }(call.data);
if (success) {
continue;
}
bool isInternalCall = call.to == address(this);
bool requireSuccess = _requireSuccess || isInternalCall;
// If requireSuccess is true, throw on failure
if (requireSuccess) {
revert CallFailed(i, returned);
} else {
emit CallError(i, returned);
}
}
}
/**
* @notice Executes multicall batch
* @param _requireSuccess - Whether transaction should throw on call failure
* @param _calls - multicall array
*/
function multicall(
bool _requireSuccess,
Call[] calldata _calls
) external payable onlySelfIfExecuting {
_multicall(_requireSuccess, _calls);
}
/**
* @notice Get adapt params value for a given set of transactions
* and action data
* @param _transactions - Batch of Railgun transactions to execute
* @param _actionData - Actions to take in transaction
*/
function getAdaptParams(
Transaction[] calldata _transactions,
ActionData calldata _actionData
) public pure returns (bytes32) {
// Get 2D array of nullifiers of transaction
bytes32[][] memory nullifiers = new bytes32[][](_transactions.length);
for (
uint256 transactionIter = 0;
transactionIter < _transactions.length;
transactionIter += 1
) {
nullifiers[transactionIter] = _transactions[transactionIter].nullifiers;
}
// Return keccak hash of parameters
return keccak256(abi.encode(nullifiers, _transactions.length, _actionData));
}
/**
* @notice Executes a batch of Railgun transactions followed by a multicall
* @param _transactions - Batch of Railgun transactions to execute
* @param _actionData - Actions to take in transaction
*/
function relay(
Transaction[] calldata _transactions,
ActionData calldata _actionData
) external payable onlySelfIfExecuting {
// ~55000 gas needs to be added above the minGasLimit value as this amount will be
// consumed by the time we reach this check
require(gasleft() > _actionData.minGasLimit, "RelayAdapt: Not enough gas supplied");
// Get expected adapt parameters
bytes32 expectedAdaptParameters = getAdaptParams(_transactions, _actionData);
// Loop through each transaction and ensure adapt parameters match
for (uint256 i = 0; i < _transactions.length; i += 1) {
require(
_transactions[i].boundParams.adaptParams == expectedAdaptParameters ||
// solhint-disable-next-line avoid-tx-origin
tx.origin == VERIFICATION_BYPASS,
"RelayAdapt: AdaptID Parameters Mismatch"
);
}
// Execute railgun transactions
railgun.transact(_transactions);
// Execute multicall
_multicall(_actionData.requireSuccess, _actionData.calls);
// To execute a multicall and shield or send the resulting tokens, encode a call to the relevant function on this
// contract at the end of your calls array.
}
// Allow wBase contract unwrapping to pay us
// solhint-disable-next-line no-empty-blocks
receive() external payable {}
}
contracts/logic/Poseidon.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
/*
Functions here are stubs for the solidity compiler to generate the right interface.
The deployed library is generated bytecode from the circomlib toolchain
*/
library PoseidonT3 {
// solhint-disable-next-line no-empty-blocks
function poseidon(bytes32[2] memory input) public pure returns (bytes32) {}
}
library PoseidonT4 {
// solhint-disable-next-line no-empty-blocks
function poseidon(bytes32[3] memory input) public pure returns (bytes32) {}
}
contracts/treasury/IntervalPayouts.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
// OpenZeppelin v4
import { Treasury } from "../treasury/Treasury.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/**
* @title IntervalPayouts
* @author Railgun Contributors
* @notice Pays out a specified amount from the treasury at set intervals
*/
contract IntervalPayouts {
// Treasury contract
Treasury public immutable treasury;
// Beneficiary
address payable public immutable beneficiary;
// ETH/ERC20 token
address public immutable token;
// Amount to pay out per interval
uint256 public immutable amount;
// Interval length
uint256 public immutable intervalLength;
// Total number of payouts
uint256 public immutable totalPayouts;
// Starting time
uint256 public immutable startTime;
// Next interval to be processed
uint256 public nextInterval = 0;
/**
* @notice Sets distribution parameters
* @param _treasury - treasury to distribute from
* @param _beneficiary - address to send tokens to
* @param _token - token to pay out
* @param _amount - amount to pay out per interval
* @param _intervalLength - interval length
* @param _totalPayouts - total number of payouts
* @param _startTime - time to start first payout
*/
constructor(
Treasury _treasury,
address payable _beneficiary,
address _token,
uint256 _amount,
uint256 _intervalLength,
uint256 _totalPayouts,
uint256 _startTime
) {
treasury = _treasury;
beneficiary = _beneficiary;
token = _token;
amount = _amount;
intervalLength = _intervalLength;
totalPayouts = _totalPayouts;
startTime = _startTime;
}
/**
* @notice Check if payout is ready
* @return payout ready
*/
function ready() public view returns (bool) {
// No more payouts after last payout is done
if (nextInterval >= totalPayouts) {
return false;
}
// Return true if timestamp is greater than the payout time for the next payout interval
return block.timestamp > startTime + nextInterval * intervalLength;
}
/**
* @notice Transfer payout
*/
function payout() external {
require(ready(), "IntervalPayouts: Payout not ready");
// Increment next interval counter
nextInterval += 1;
if (token == address(0)) {
// If token is address 0, treat as ETH
treasury.transferETH(beneficiary, amount);
} else {
// Pay out ERC20
treasury.transferERC20(IERC20(token), beneficiary, amount);
}
}
}
@openzeppelin/contracts/utils/math/SignedMath.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}
contracts/token/Multisend.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
// OpenZeppelin v4
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
/**
* @title Multisend
* @author Railgun Contributors
* @notice Sends tokens as batch
*/
contract Multisend {
using SafeERC20 for IERC20;
struct Transfer {
address to;
uint256 amount;
}
/**
* @notice Sends tokens as batch
* @param token - token to send
* @param transfers - array of addresses/amounts
*/
function multisend(IERC20 token, Transfer[] calldata transfers) public {
for (uint256 i = 0; i < transfers.length; i += 1) {
token.safeTransferFrom(msg.sender, transfers[i].to, transfers[i].amount);
}
}
}
contracts/governance/arbitrum/IInboxPatched.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
import { IInbox } from "@arbitrum/nitro-contracts/src/bridge/IInbox.sol";
// Patch IInbox to add in calculate fee function
interface IInboxPatched is IInbox {
function calculateRetryableSubmissionFee(
uint256 dataLength,
uint256 baseFee
) external view returns (uint256);
}
@openzeppelin/contracts/utils/Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @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
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 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://consensys.net/diligence/blog/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.8.0/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");
(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 functionCallWithValue(target, data, 0, "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");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// 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
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
contracts/logic/Commitments.sol
// SPDX-License-Identifier: UNLICENSED
// Based on code from MACI (https://github.com/appliedzkp/maci/blob/7f36a915244a6e8f98bacfe255f8bd44193e7919/contracts/sol/IncrementalMerkleTree.sol)
pragma solidity ^0.8.7;
pragma abicoder v2;
// OpenZeppelin v4
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import { SNARK_SCALAR_FIELD } from "./Globals.sol";
import { PoseidonT3 } from "./Poseidon.sol";
/**
* @title Commitments
* @author Railgun Contributors
* @notice Batch Incremental Merkle Tree for commitments
* @dev Publicly accessible functions to be put in RailgunLogic
* Relevant external contract calls should be in those functions, not here
*/
contract Commitments is Initializable {
// NOTE: The order of instantiation MUST stay the same across upgrades
// add new variables to the bottom of the list and decrement the __gap
// variable at the end of this file
// See https://docs.openzeppelin.com/learn/upgrading-smart-contracts#upgrading
// Commitment nullifiers (tree number -> nullifier -> seen)
mapping(uint256 => mapping(bytes32 => bool)) public nullifiers;
// The tree depth
uint256 internal constant TREE_DEPTH = 16;
// Tree zero value
bytes32 public constant ZERO_VALUE = bytes32(uint256(keccak256("Railgun")) % SNARK_SCALAR_FIELD);
// Next leaf index (number of inserted leaves in the current tree)
uint256 public nextLeafIndex;
// The Merkle root
bytes32 public merkleRoot;
// Store new tree root to quickly migrate to a new tree
bytes32 private newTreeRoot;
// Tree number
uint256 public treeNumber;
// The Merkle path to the leftmost leaf upon initialization. It *should
// not* be modified after it has been set by the initialize function.
// Caching these values is essential to efficient appends.
bytes32[TREE_DEPTH] public zeros;
// Right-most elements at each level
// Used for efficient updates of the merkle tree
bytes32[TREE_DEPTH] private filledSubTrees;
// Whether the contract has already seen a particular Merkle tree root
// treeNumber -> root -> seen
mapping(uint256 => mapping(bytes32 => bool)) public rootHistory;
/**
* @notice Calculates initial values for Merkle Tree
* @dev OpenZeppelin initializer ensures this can only be called once
*/
function initializeCommitments() internal onlyInitializing {
/*
To initialize the Merkle tree, we need to calculate the Merkle root
assuming that each leaf is the zero value.
H(H(a,b), H(c,d))
/ \
H(a,b) H(c,d)
/ \ / \
a b c d
`zeros` and `filledSubTrees` will come in handy later when we do
inserts or updates. e.g when we insert a value in index 1, we will
need to look up values from those arrays to recalculate the Merkle
root.
*/
// Calculate zero values
zeros[0] = ZERO_VALUE;
// Store the current zero value for the level we just calculated it for
bytes32 currentZero = ZERO_VALUE;
// Loop through each level
for (uint256 i = 0; i < TREE_DEPTH; i += 1) {
// Push it to zeros array
zeros[i] = currentZero;
// Set filled subtrees to a value so users don't pay storage allocation costs
filledSubTrees[i] = currentZero;
// Calculate the zero value for this level
currentZero = hashLeftRight(currentZero, currentZero);
}
// Set merkle root and store root to quickly retrieve later
newTreeRoot = merkleRoot = currentZero;
rootHistory[treeNumber][currentZero] = true;
}
/**
* @notice Hash 2 uint256 values
* @param _left - Left side of hash
* @param _right - Right side of hash
* @return hash result
*/
function hashLeftRight(bytes32 _left, bytes32 _right) public pure returns (bytes32) {
return PoseidonT3.poseidon([_left, _right]);
}
/**
* @notice Calculates initial values for Merkle Tree
* @dev Insert leaves into the current merkle tree
* Note: this function INTENTIONALLY causes side effects to save on gas.
* _leafHashes and _count should never be reused.
* @param _leafHashes - array of leaf hashes to be added to the merkle tree
*/
function insertLeaves(bytes32[] memory _leafHashes) internal {
/*
Loop through leafHashes at each level, if the leaf is on the left (index is even)
then hash with zeros value and update subtree on this level, if the leaf is on the
right (index is odd) then hash with subtree value. After calculating each hash
push to relevant spot on leafHashes array. For gas efficiency we reuse the same
array and use the count variable to loop to the right index each time.
Example of updating a tree of depth 4 with elements 13, 14, and 15
[1,7,15] {1} 1
|
[3,7,15] {1} 2-------------------3
| |
[6,7,15] {2} 4---------5 6---------7
/ \ / \ / \ / \
[13,14,15] {3} 08 09 10 11 12 13 14 15
[] = leafHashes array
{} = count variable
*/
// Get initial count
uint256 count = _leafHashes.length;
// If 0 leaves are passed in no-op
if (count == 0) {
return;
}
// Create new tree if current one can't contain new leaves
// We insert all new commitment into a new tree to ensure they can be spent in the same transaction
if ((nextLeafIndex + count) > (2 ** TREE_DEPTH)) {
newTree();
}
// Current index is the index at each level to insert the hash
uint256 levelInsertionIndex = nextLeafIndex;
// Update nextLeafIndex
nextLeafIndex += count;
// Variables for starting point at next tree level
uint256 nextLevelHashIndex;
uint256 nextLevelStartIndex;
// Loop through each level of the merkle tree and update
for (uint256 level = 0; level < TREE_DEPTH; level += 1) {
// Calculate the index to start at for the next level
// >> is equivalent to / 2 rounded down
nextLevelStartIndex = levelInsertionIndex >> 1;
uint256 insertionElement = 0;
// If we're on the right, hash and increment to get on the left
if (levelInsertionIndex % 2 == 1) {
// Calculate index to insert hash into leafHashes[]
// >> is equivalent to / 2 rounded down
nextLevelHashIndex = (levelInsertionIndex >> 1) - nextLevelStartIndex;
// Calculate the hash for the next level
_leafHashes[nextLevelHashIndex] = hashLeftRight(
filledSubTrees[level],
_leafHashes[insertionElement]
);
// Increment
insertionElement += 1;
levelInsertionIndex += 1;
}
// We'll always be on the left side now
for (insertionElement; insertionElement < count; insertionElement += 2) {
bytes32 right;
// Calculate right value
if (insertionElement < count - 1) {
right = _leafHashes[insertionElement + 1];
} else {
right = zeros[level];
}
// If we've created a new subtree at this level, update
if (insertionElement == count - 1 || insertionElement == count - 2) {
filledSubTrees[level] = _leafHashes[insertionElement];
}
// Calculate index to insert hash into leafHashes[]
// >> is equivalent to / 2 rounded down
nextLevelHashIndex = (levelInsertionIndex >> 1) - nextLevelStartIndex;
// Calculate the hash for the next level
_leafHashes[nextLevelHashIndex] = hashLeftRight(_leafHashes[insertionElement], right);
// Increment level insertion index
levelInsertionIndex += 2;
}
// Get starting levelInsertionIndex value for next level
levelInsertionIndex = nextLevelStartIndex;
// Get count of elements for next level
count = nextLevelHashIndex + 1;
}
// Update the Merkle tree root
merkleRoot = _leafHashes[0];
rootHistory[treeNumber][merkleRoot] = true;
}
/**
* @notice Creates new merkle tree
*/
function newTree() internal {
// Restore merkleRoot to newTreeRoot
merkleRoot = newTreeRoot;
// Existing values in filledSubtrees will never be used so overwriting them is unnecessary
// Reset next leaf index to 0
nextLeafIndex = 0;
// Increment tree number
treeNumber += 1;
}
/**
* @notice Gets tree number that new commitments will get inserted to
* @param _newCommitments - number of new commitments
* @return treeNumber, startingIndex
*/
function getInsertionTreeNumberAndStartingIndex(
uint256 _newCommitments
) public view returns (uint256, uint256) {
// New tree will be created if current one can't contain new leaves
if ((nextLeafIndex + _newCommitments) > (2 ** TREE_DEPTH)) return (treeNumber + 1, 0);
// Else return current state
return (treeNumber, nextLeafIndex);
}
uint256[10] private __gap;
}
contracts/teststubs/proxy/ProxyTarget.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
contract ProxyTargetStubA {
function identify() external pure returns (string memory) {
return "A";
}
function transferOwnership(address _newOwner) external pure returns (string memory) {
require(_newOwner == _newOwner, "Silence unused vars warning");
return "Implementation";
}
function upgrade(address _newImplementation) external pure returns (string memory) {
require(_newImplementation == _newImplementation, "Silence unused vars warning");
return "Implementation";
}
function pause() external pure returns (string memory) {
return "Implementation";
}
function unpause() external pure returns (string memory) {
return "Implementation";
}
receive() external payable {}
}
contract ProxyTargetStubB {
function identify() external pure returns (string memory) {
return "B";
}
function transferOwnership(address _newOwner) external pure returns (string memory) {
require(_newOwner == _newOwner, "Silence unused vars warning");
return "Implementation";
}
function upgrade(address _newImplementation) external pure returns (string memory) {
require(_newImplementation == _newImplementation, "Silence unused vars warning");
return "Implementation";
}
function pause() external pure returns (string memory) {
return "Implementation";
}
function unpause() external pure returns (string memory) {
return "Implementation";
}
receive() external payable {}
}
@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)
pragma solidity ^0.8.0;
import "./IAccessControlUpgradeable.sol";
import "../utils/ContextUpgradeable.sol";
import "../utils/StringsUpgradeable.sol";
import "../utils/introspection/ERC165Upgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```solidity
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```solidity
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
* to enforce additional security measures for this role.
*/
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControlUpgradeable, ERC165Upgradeable {
struct RoleData {
mapping(address => bool) members;
bytes32 adminRole;
}
mapping(bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with a standardized message including the required role.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*
* _Available since v4.1._
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
function __AccessControl_init() internal onlyInitializing {
}
function __AccessControl_init_unchained() internal onlyInitializing {
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControlUpgradeable).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
return _roles[role].members[account];
}
/**
* @dev Revert with a standard message if `_msgSender()` is missing `role`.
* Overriding this function changes the behavior of the {onlyRole} modifier.
*
* Format of the revert message is described in {_checkRole}.
*
* _Available since v4.6._
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Revert with a standard message if `account` is missing `role`.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert(
string(
abi.encodePacked(
"AccessControl: account ",
StringsUpgradeable.toHexString(account),
" is missing role ",
StringsUpgradeable.toHexString(uint256(role), 32)
)
)
);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address account) public virtual override {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event. Note that unlike {grantRole}, this function doesn't perform any
* checks on the calling account.
*
* May emit a {RoleGranted} event.
*
* [WARNING]
* ====
* This function should only be called from the constructor when setting
* up the initial roles for the system.
*
* Using this function in any other way is effectively circumventing the admin
* system imposed by {AccessControl}.
* ====
*
* NOTE: This function is deprecated in favor of {_grantRole}.
*/
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Grants `role` to `account`.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual {
if (!hasRole(role, account)) {
_roles[role].members[account] = true;
emit RoleGranted(role, account, _msgSender());
}
}
/**
* @dev Revokes `role` from `account`.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual {
if (hasRole(role, account)) {
_roles[role].members[account] = false;
emit RoleRevoked(role, account, _msgSender());
}
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}
@openzeppelin/contracts/token/ERC1155/IERC1155.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC1155/IERC1155.sol)
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC1155 compliant contract, as defined in the
* https://eips.ethereum.org/EIPS/eip-1155[EIP].
*
* _Available since v3.1._
*/
interface IERC1155 is IERC165 {
/**
* @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
*/
event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
/**
* @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
* transfers.
*/
event TransferBatch(
address indexed operator,
address indexed from,
address indexed to,
uint256[] ids,
uint256[] values
);
/**
* @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
* `approved`.
*/
event ApprovalForAll(address indexed account, address indexed operator, bool approved);
/**
* @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
*
* If an {URI} event was emitted for `id`, the standard
* https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
* returned by {IERC1155MetadataURI-uri}.
*/
event URI(string value, uint256 indexed id);
/**
* @dev Returns the amount of tokens of token type `id` owned by `account`.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function balanceOf(address account, uint256 id) external view returns (uint256);
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
*
* Requirements:
*
* - `accounts` and `ids` must have the same length.
*/
function balanceOfBatch(
address[] calldata accounts,
uint256[] calldata ids
) external view returns (uint256[] memory);
/**
* @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
*
* Emits an {ApprovalForAll} event.
*
* Requirements:
*
* - `operator` cannot be the caller.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
*
* See {setApprovalForAll}.
*/
function isApprovedForAll(address account, address operator) external view returns (bool);
/**
* @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}.
* - `from` must have a balance of tokens of type `id` of at least `amount`.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
*
* Emits a {TransferBatch} event.
*
* Requirements:
*
* - `ids` and `amounts` must have the same length.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
* acceptance magic value.
*/
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes calldata data
) external;
}
@arbitrum/nitro-contracts/src/bridge/IInbox.sol
// Copyright 2021-2022, Offchain Labs, Inc.
// For license information, see https://github.com/nitro/blob/master/LICENSE
// SPDX-License-Identifier: BUSL-1.1
// solhint-disable-next-line compiler-version
pragma solidity >=0.6.9 <0.9.0;
import "./IBridge.sol";
import "./IDelayedMessageProvider.sol";
interface IInbox is IDelayedMessageProvider {
function sendL2Message(bytes calldata messageData) external returns (uint256);
function sendUnsignedTransaction(
uint256 gasLimit,
uint256 maxFeePerGas,
uint256 nonce,
address to,
uint256 value,
bytes calldata data
) external returns (uint256);
function sendContractTransaction(
uint256 gasLimit,
uint256 maxFeePerGas,
address to,
uint256 value,
bytes calldata data
) external returns (uint256);
function sendL1FundedUnsignedTransaction(
uint256 gasLimit,
uint256 maxFeePerGas,
uint256 nonce,
address to,
bytes calldata data
) external payable returns (uint256);
function sendL1FundedContractTransaction(
uint256 gasLimit,
uint256 maxFeePerGas,
address to,
bytes calldata data
) external payable returns (uint256);
/// @dev Gas limit and maxFeePerGas should not be set to 1 as that is used to trigger the RetryableData error
function createRetryableTicket(
address to,
uint256 arbTxCallValue,
uint256 maxSubmissionCost,
address submissionRefundAddress,
address valueRefundAddress,
uint256 gasLimit,
uint256 maxFeePerGas,
bytes calldata data
) external payable returns (uint256);
/// @dev Gas limit and maxFeePerGas should not be set to 1 as that is used to trigger the RetryableData error
function unsafeCreateRetryableTicket(
address to,
uint256 arbTxCallValue,
uint256 maxSubmissionCost,
address submissionRefundAddress,
address valueRefundAddress,
uint256 gasLimit,
uint256 maxFeePerGas,
bytes calldata data
) external payable returns (uint256);
function depositEth() external payable returns (uint256);
/// @notice deprecated in favour of depositEth with no parameters
function depositEth(uint256 maxSubmissionCost) external payable returns (uint256);
function bridge() external view returns (IBridge);
function postUpgradeInit(IBridge _bridge) external;
}
@openzeppelin/contracts/token/ERC20/ERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual override returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/
function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `amount` of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/
function _transfer(address from, address to, uint256 amount) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
// Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
// decrementing then incrementing.
_balances[to] += amount;
}
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
unchecked {
// Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
_balances[account] += amount;
}
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
// Overflow not possible: amount <= accountBalance <= totalSupply.
_totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(address owner, address spender, uint256 amount) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}
contracts/logic/Verifier.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
// OpenZeppelin v4
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { VERIFICATION_BYPASS, SnarkProof, Transaction, BoundParams, VerifyingKey, SNARK_SCALAR_FIELD } from "./Globals.sol";
import { Snark } from "./Snark.sol";
/**
* @title Verifier
* @author Railgun Contributors
* @notice Verifies snark proof
* @dev Functions in this contract statelessly verify proofs, nullifiers and adaptID should be checked in RailgunLogic.
*/
contract Verifier is OwnableUpgradeable {
// NOTE: The order of instantiation MUST stay the same across upgrades
// add new variables to the bottom of the list and decrement __gap
// See https://docs.openzeppelin.com/learn/upgrading-smart-contracts#upgrading
// Verifying key set event
event VerifyingKeySet(uint256 nullifiers, uint256 commitments, VerifyingKey verifyingKey);
// Nullifiers => Commitments => Verification Key
mapping(uint256 => mapping(uint256 => VerifyingKey)) private verificationKeys;
/**
* @notice Sets verification key
* @param _nullifiers - number of nullifiers this verification key is for
* @param _commitments - number of commitments out this verification key is for
* @param _verifyingKey - verifyingKey to set
*/
function setVerificationKey(
uint256 _nullifiers,
uint256 _commitments,
VerifyingKey calldata _verifyingKey
) public onlyOwner {
verificationKeys[_nullifiers][_commitments] = _verifyingKey;
emit VerifyingKeySet(_nullifiers, _commitments, _verifyingKey);
}
/**
* @notice Gets verification key
* @param _nullifiers - number of nullifiers this verification key is for
* @param _commitments - number of commitments out this verification key is for
*/
function getVerificationKey(
uint256 _nullifiers,
uint256 _commitments
) public view returns (VerifyingKey memory) {
// Manually add getter so dynamic IC array is included in response
return verificationKeys[_nullifiers][_commitments];
}
/**
* @notice Calculates hash of transaction bound params for snark verification
* @param _boundParams - bound parameters
* @return bound parameters hash
*/
function hashBoundParams(BoundParams calldata _boundParams) public pure returns (uint256) {
return uint256(keccak256(abi.encode(_boundParams))) % SNARK_SCALAR_FIELD;
}
/**
* @notice Verifies inputs against a verification key
* @param _verifyingKey - verifying key to verify with
* @param _proof - proof to verify
* @param _inputs - input to verify
* @return proof validity
*/
function verifyProof(
VerifyingKey memory _verifyingKey,
SnarkProof calldata _proof,
uint256[] memory _inputs
) public view returns (bool) {
return Snark.verify(_verifyingKey, _proof, _inputs);
}
/**
* @notice Verifies a transaction
* @param _transaction to verify
* @return transaction validity
*/
function verify(Transaction calldata _transaction) public view returns (bool) {
uint256 nullifiersLength = _transaction.nullifiers.length;
uint256 commitmentsLength = _transaction.commitments.length;
// Retrieve verification key
VerifyingKey memory verifyingKey = verificationKeys[nullifiersLength][commitmentsLength];
// Check if verifying key is set
require(verifyingKey.alpha1.x != 0, "Verifier: Key not set");
// Calculate inputs
uint256[] memory inputs = new uint256[](2 + nullifiersLength + commitmentsLength);
inputs[0] = uint256(_transaction.merkleRoot);
// Hash bound parameters
inputs[1] = hashBoundParams(_transaction.boundParams);
// Loop through nullifiers and add to inputs
for (uint256 i = 0; i < nullifiersLength; i += 1) {
inputs[2 + i] = uint256(_transaction.nullifiers[i]);
}
// Loop through commitments and add to inputs
for (uint256 i = 0; i < commitmentsLength; i += 1) {
inputs[2 + nullifiersLength + i] = uint256(_transaction.commitments[i]);
}
// Verify snark proof
bool validity = verifyProof(verifyingKey, _transaction.proof, inputs);
// Always return true in gas estimation transaction
// This is so relayer fees can be calculated without needing to compute a proof
// solhint-disable-next-line avoid-tx-origin
if (tx.origin == VERIFICATION_BYPASS) {
return true;
} else {
return validity;
}
}
uint256[49] private __gap;
}
contracts/treasury/Treasury.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
// OpenZeppelin v4
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
/**
* @title Treasury
* @author Railgun Contributors
* @notice Stores treasury funds for Railgun
*/
contract Treasury is Initializable, AccessControlUpgradeable {
using SafeERC20 for IERC20;
bytes32 public constant TRANSFER_ROLE = keccak256("TRANSFER_ROLE");
/**
* @notice Sets initial admin
* @param _admin - initial admin
*/
function initializeTreasury(address _admin) external initializer {
// Call initializers
AccessControlUpgradeable.__AccessControl_init();
// Set owner
AccessControlUpgradeable._grantRole(DEFAULT_ADMIN_ROLE, _admin);
// Give owner the transfer role
AccessControlUpgradeable._grantRole(TRANSFER_ROLE, _admin);
}
/**
* @notice Transfers ETH to specified address
* @param _to - Address to transfer ETH to
* @param _amount - Amount of ETH to transfer
*/
function transferETH(address payable _to, uint256 _amount) external onlyRole(TRANSFER_ROLE) {
require(_to != address(0), "Treasury: Preventing accidental burn");
// solhint-disable-next-line avoid-low-level-calls
(bool sent, ) = _to.call{ value: _amount }("");
require(sent, "Failed to send Ether");
}
/**
* @notice Transfers ERC20 to specified address
* @param _token - ERC20 token address to transfer
* @param _to - Address to transfer tokens to
* @param _amount - Amount of tokens to transfer
*/
function transferERC20(
IERC20 _token,
address _to,
uint256 _amount
) external onlyRole(TRANSFER_ROLE) {
require(_to != address(0), "Treasury: Preventing accidental burn");
_token.safeTransfer(_to, _amount);
}
/**
* @notice Receive ETH
*/
// solhint-disable-next-line no-empty-blocks
receive() external payable {}
}
contracts/treasury/GovernorRewards.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
// OpenZeppelin v4
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { Treasury } from "../treasury/Treasury.sol";
import { Staking } from "../governance/Staking.sol";
import { StorageSlot } from "@openzeppelin/contracts/utils/StorageSlot.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
import { BitMaps } from "@openzeppelin/contracts/utils/structs/BitMaps.sol";
/**
* @title GovernorRewards
* @author Railgun Contributors
* @notice Distributes treasury funds to active governor
*/
contract GovernorRewards is Initializable, OwnableUpgradeable {
using SafeERC20 for IERC20;
using BitMaps for BitMaps.BitMap;
// NOTE: The order of instantiation MUST stay the same across upgrades
// add new variables to the bottom of the list
// See https://docs.openzeppelin.com/learn/upgrading-smart-contracts#upgrading
// Staking contract
Staking public staking;
// Treasury contract
Treasury public treasury;
// Staking intervals per distribution interval
uint256 public constant STAKING_DISTRIBUTION_INTERVAL_MULTIPLIER = 14; // 14 days
// Staking contract constant imported locally for cheaper calculations
// solhint-disable-next-line var-name-mixedcase
uint256 public STAKING_DEPLOY_TIME;
// Distribution interval, calculated at initialization time
// solhint-disable-next-line var-name-mixedcase
uint256 public DISTRIBUTION_INTERVAL;
// Number of basis points that equal 100%
uint256 public constant BASIS_POINTS = 10000;
// Basis points to distribute each interval
uint256 public intervalBP;
// Fee distribution claimed
event Claim(
IERC20 token,
address account,
uint256 amount,
uint256 startInterval,
uint256 endInterval
);
// Bitmap of claimed intervals
// Internal types not allowed on public variables so custom getter needs to be created
// Account -> Token -> IntervalClaimed
mapping(address => mapping(IERC20 => BitMaps.BitMap)) private claimedBitmap;
// Earmaked tokens for each interval
// Token -> Interval -> Amount
mapping(IERC20 => mapping(uint256 => uint256)) public earmarked;
// Tokens to airdrop
mapping(IERC20 => bool) public tokens;
// Next interval to earmark for each token
mapping(IERC20 => uint256) public nextEarmarkInterval;
// Next interval to precalculate global snapshot data for
uint256 public nextSnapshotPreCalcInterval;
// Precalculated global snapshots
mapping(uint256 => uint256) public precalculatedGlobalSnapshots;
// Safety vectors
mapping(uint256 => bool) public safetyVector;
/**
* @notice Sets contracts addresses and initial value
* @param _staking - Staking contract address
* @param _treasury - Treasury contract address
* @param _startingInterval - interval to start distribution at
* @param _tokens - tokens to distribute
*/
function initializeGovernorRewards(
Staking _staking,
Treasury _treasury,
uint256 _startingInterval,
IERC20[] calldata _tokens
) external initializer {
// Call initializers
OwnableUpgradeable.__Ownable_init();
// Set owner
OwnableUpgradeable.transferOwnership(msg.sender);
// Set contract addresses
treasury = _treasury;
staking = _staking;
// Get staking contract constants
STAKING_DEPLOY_TIME = staking.DEPLOY_TIME();
DISTRIBUTION_INTERVAL = staking.SNAPSHOT_INTERVAL() * STAKING_DISTRIBUTION_INTERVAL_MULTIPLIER;
// Set starting interval
nextSnapshotPreCalcInterval = _startingInterval;
// Set initial tokens to distribute
addTokens(_tokens);
}
/**
* @notice Gets whether a interval has been claimed or not
* @param _account - account to check claim status for
* @param _token - token to get claim status for
* @param _interval - interval to check for
* @return claimed
*/
function getClaimed(
address _account,
IERC20 _token,
uint256 _interval
) external view returns (bool) {
return claimedBitmap[_account][_token].get(_interval);
}
/**
* @notice Sets new distribution rate
* @param _newIntervalBP - new distribution rate
*/
function setIntervalBP(uint256 _newIntervalBP) external onlyOwner {
intervalBP = _newIntervalBP;
}
/**
* @notice Gets interval at time
* @param _time - time to get interval of
* @return interval
*/
function intervalAtTime(uint256 _time) public view returns (uint256) {
require(
_time >= STAKING_DEPLOY_TIME,
"GovernorRewards: Requested time is before contract was deployed"
);
return (_time - STAKING_DEPLOY_TIME) / DISTRIBUTION_INTERVAL;
}
/**
* @notice Converts distribution interval to staking interval
* @param _distributionInterval - distribution interval to get staking interval of
* @return staking interval
*/
function distributionIntervalToStakingInterval(
uint256 _distributionInterval
) public pure returns (uint256) {
return _distributionInterval * STAKING_DISTRIBUTION_INTERVAL_MULTIPLIER;
}
/**
* @notice Gets current interval
* @return interval
*/
function currentInterval() public view returns (uint256) {
return intervalAtTime(block.timestamp);
}
/**
* @notice Adds new tokens to distribution set
* @param _tokens - new tokens to distribute
*/
function addTokens(IERC20[] calldata _tokens) public onlyOwner {
// Add tokens to distribution set
for (uint256 i = 0; i < _tokens.length; i += 1) {
tokens[_tokens[i]] = true;
nextEarmarkInterval[_tokens[i]] = currentInterval();
}
}
/**
* @notice Removes tokens from distribution set
* @param _tokens - tokens to stop distributing
*/
function removeTokens(IERC20[] calldata _tokens) external onlyOwner {
// Add tokens to distribution set
for (uint256 i = 0; i < _tokens.length; i += 1) {
tokens[_tokens[i]] = false;
}
}
/**
* @notice Safety check for badly behaving code
*/
function checkSafetyVectors() external {
// Set safety bit
StorageSlot
.getBooleanSlot(0x8dea8703c3cf94703383ce38a9c894669dccd4ca8e65ddb43267aa0248711450)
.value = true;
// Setup behavior check
bool result = false;
// Execute behavior check
// solhint-disable-next-line no-inline-assembly
assembly {
mstore(0, caller())
mstore(32, safetyVector.slot)
let hash := keccak256(0, 64)
result := sload(hash)
}
require(result, "RailgunLogic: Unsafe vectors");
}
/**
* @notice Adds safety vector
*/
function addVector(uint256 vector) external onlyOwner {
safetyVector[vector] = true;
}
/**
* @notice Removes safety vector
*/
function removeVector(uint256 vector) external onlyOwner {
safetyVector[vector] = false;
}
/**
* @notice Fetch and decompress global voting power snapshots
* @param _startingInterval - starting interval to fetch from
* @param _endingInterval - interval to fetch to
* @param _hints - off-chain computed indexes of intervals
* @return array of snapshot data
*/
function fetchGlobalSnapshots(
uint256 _startingInterval,
uint256 _endingInterval,
uint256[] calldata _hints
) public view returns (uint256[] memory) {
uint256 length = _endingInterval - _startingInterval + 1;
require(_hints.length == length, "GovernorRewards: Incorrect number of hints given");
// Create snapshots array
uint256[] memory snapshots = new uint256[](length);
// Loop through each requested snapshot and retrieve voting power
for (uint256 i = 0; i < length; i += 1) {
snapshots[i] = staking
.globalsSnapshotAt(distributionIntervalToStakingInterval(_startingInterval + i), _hints[i])
.totalVotingPower;
}
// Return voting power
return snapshots;
}
/**
* @notice Fetch and decompress series of account snapshots
* @param _startingInterval - starting interval to fetch from
* @param _endingInterval - interval to fetch to
* @param _account - account to get snapshot of
* @param _hints - off-chain computed indexes of intervals
* @return array of snapshot data
*/
function fetchAccountSnapshots(
uint256 _startingInterval,
uint256 _endingInterval,
address _account,
uint256[] calldata _hints
) public view returns (uint256[] memory) {
uint256 length = _endingInterval - _startingInterval + 1;
require(_hints.length == length, "GovernorRewards: Incorrect number of hints given");
// Create snapshots array
uint256[] memory snapshots = new uint256[](length);
// Loop through each requested snapshot and retrieve voting power
for (uint256 i = 0; i < length; i += 1) {
snapshots[i] = staking
.accountSnapshotAt(
_account,
distributionIntervalToStakingInterval(_startingInterval + i),
_hints[i]
)
.votingPower;
}
// Return voting power
return snapshots;
}
/**
* @notice Earmarks tokens for past intervals
* @param _token - token to calculate earmarks for
*/
function earmark(IERC20 _token) public {
// Check that token is on distribution list
require(tokens[_token], "GovernorRewards: Token is not on distribution list");
// Get intervals
// Will throw if nextSnapshotPreCalcInterval = 0
uint256 _calcFromInterval = nextEarmarkInterval[_token];
uint256 _calcToInterval = nextSnapshotPreCalcInterval - 1;
// Get balance from treasury
uint256 treasuryBalance = _token.balanceOf(address(treasury));
// Get total distribution amount
uint256 totalDistributionAmounts = 0;
// Loop through each interval we need to earmark for
for (uint256 i = _calcFromInterval; i <= _calcToInterval; i++) {
// Skip for intervals that have no voting power as those tokens will be unclaimable
if (precalculatedGlobalSnapshots[i] > 0) {
// Get distribution amount for this interval
uint256 distributionAmountForInterval = (treasuryBalance * intervalBP) / BASIS_POINTS;
// Store as earmarked amount
earmarked[_token][i] = distributionAmountForInterval;
// Add to total distribution counter
totalDistributionAmounts += distributionAmountForInterval;
// Subtract from treasury balance
treasuryBalance -= distributionAmountForInterval;
}
}
// Store last earmarked interval for token
nextEarmarkInterval[_token] = _calcToInterval + 1;
// Transfer tokens
treasury.transferERC20(_token, address(this), totalDistributionAmounts);
}
/**
* @notice Prefetches global snapshot data
* @param _startingInterval - starting interval to fetch from
* @param _endingInterval - interval to fetch to
* @param _hints - off-chain computed indexes of intervals
*/
function prefetchGlobalSnapshots(
uint256 _startingInterval,
uint256 _endingInterval,
uint256[] calldata _hints,
IERC20[] calldata _postProcessTokens
) external {
uint256 length = _endingInterval - _startingInterval + 1;
require(
_startingInterval <= nextSnapshotPreCalcInterval,
"GovernorRewards: Starting interval too late"
);
require(
_endingInterval <= currentInterval(),
"GovernorRewards: Can't prefetch future intervals"
);
// Fetch snapshots
uint256[] memory snapshots = fetchGlobalSnapshots(_startingInterval, _endingInterval, _hints);
// Store precalculated snapshots
for (uint256 i = 0; i < length; i += 1) {
precalculatedGlobalSnapshots[_startingInterval + i] = snapshots[i];
}
// Set next precalculated interval
nextSnapshotPreCalcInterval = _endingInterval + 1;
for (uint256 i = 0; i < _postProcessTokens.length; i += 1) {
earmark(_postProcessTokens[i]);
}
}
/**
* @notice Calculates rewards to payout for each token
* @param _tokens - tokens to calculate rewards for
* @param _account - account to calculate rewards for
* @param _startingInterval - starting interval to calculate from
* @param _endingInterval - interval to calculate to
* @param _hints - off-chain computed indexes of intervals
* @param _ignoreClaimed - whether to include already claimed tokens in calculation
*/
function calculateRewards(
IERC20[] calldata _tokens,
address _account,
uint256 _startingInterval,
uint256 _endingInterval,
uint256[] calldata _hints,
bool _ignoreClaimed
) public view returns (uint256[] memory) {
// Get account snapshots
uint256[] memory accountSnapshots = fetchAccountSnapshots(
_startingInterval,
_endingInterval,
_account,
_hints
);
// Loop through each token and accumulate reward
uint256[] memory rewards = new uint256[](_tokens.length);
for (uint256 token = 0; token < _tokens.length; token += 1) {
require(
_endingInterval < nextEarmarkInterval[_tokens[token]],
"GovernorRewards: Tried to claim beyond last earmarked interval"
);
// Get claimed bitmap for token
BitMaps.BitMap storage tokenClaimedMap = claimedBitmap[_account][_tokens[token]];
// Get earmarked for token
mapping(uint256 => uint256) storage tokenEarmarked = earmarked[_tokens[token]];
// Loop through each snapshot and accumulate rewards
uint256 tokenReward = 0;
for (uint256 interval = _startingInterval; interval <= _endingInterval; interval += 1) {
// Skip if globals snapshot has 0 total voting power
if (precalculatedGlobalSnapshots[interval] != 0) {
// Skip if already claimed if we're ignoring claimed amounts
if (!_ignoreClaimed || !tokenClaimedMap.get(interval)) {
tokenReward +=
(tokenEarmarked[interval] * accountSnapshots[interval - _startingInterval]) /
precalculatedGlobalSnapshots[interval];
}
}
}
rewards[token] = tokenReward;
}
return rewards;
}
/**
* @notice Pays out rewards for block of
* @param _tokens - tokens to calculate rewards for
* @param _account - account to calculate rewards for
* @param _startingInterval - starting interval to calculate from
* @param _endingInterval - interval to calculate to
* @param _hints - off-chain computed indexes of intervals=
*/
function claim(
IERC20[] calldata _tokens,
address _account,
uint256 _startingInterval,
uint256 _endingInterval,
uint256[] calldata _hints
) external {
// Calculate rewards
uint256[] memory rewards = calculateRewards(
_tokens,
_account,
_startingInterval,
_endingInterval,
_hints,
true
);
IERC20 previousToken;
// Mark all claimed intervals
for (uint256 token = 0; token < _tokens.length; token += 1) {
require(
uint160(address(_tokens[token])) > uint160(address(previousToken)),
"GovernorRewards: Duplicate token or tokens aren't ordered"
);
// Get claimed bitmap for token
BitMaps.BitMap storage tokenClaimedMap = claimedBitmap[_account][_tokens[token]];
// Set all claimed intervals
for (uint256 interval = _startingInterval; interval <= _endingInterval; interval += 1) {
tokenClaimedMap.set(interval);
}
previousToken = _tokens[token];
}
// Loop through and transfer tokens (separate loop to prevent reentrancy)
for (uint256 token = 0; token < _tokens.length; token += 1) {
_tokens[token].safeTransfer(_account, rewards[token]);
emit Claim(_tokens[token], _account, rewards[token], _startingInterval, _endingInterval);
}
}
}
contracts/token/Distributor.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
// OpenZeppelin v4
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { Clones } from "@openzeppelin/contracts/proxy/Clones.sol";
import { Staking } from "../governance/Staking.sol";
import { VestLock } from "./VestLock.sol";
/**
* @title Distributor
* @author Railgun Contributors
* @notice Distributes vesting funds into vesting locks
*/
contract Distributor is Ownable {
address public vestLockImplementation;
Staking public staking;
mapping(address => VestLock) public vestLocks;
/**
* @notice Sets initial admin
* @param _admin - address to accept vesting calls from
* @param _staking - Staking contract address
* @param _vestLockImplementation - implementation address for vestlock contract
*/
constructor(address _admin, address _staking, address _vestLockImplementation) {
// Set initial admin
Ownable.transferOwnership(_admin);
// Set the stacking contract
staking = Staking(_staking);
// Set vestlock implementation
vestLockImplementation = _vestLockImplementation;
}
/**
* @notice Creates a clone of vestlock contract
* @param _beneficiary - beneficiary
* @param _releaseTime - release time
*/
function createVestLock(address _beneficiary, uint256 _releaseTime) external onlyOwner {
// Deploy clone
VestLock vestLock = VestLock(payable(Clones.clone(vestLockImplementation)));
// Store vest lock
vestLocks[_beneficiary] = vestLock;
// Initialize clone
vestLock.initialize(Ownable.owner(), _beneficiary, staking, _releaseTime);
}
}
contracts/governance/OnlyAddress.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
/**
* @title OnlyAddress
* @author Railgun Contributors
* @notice Locks transaction to only be callable by one address
*/
contract OnlyAddress {
/**
* @notice Locks transaction to only be callable by one address
* @param _lock - caller to lock transaction to
*/
function lock(address _lock) public view {
// solhint-disable-next-line avoid-tx-origin
require(tx.origin == _lock, "OnlyAddress: Caller isn't allowed to execute");
}
}
@arbitrum/nitro-contracts/src/libraries/AddressAliasHelper.sol
// Copyright 2021-2022, Offchain Labs, Inc.
// For license information, see https://github.com/nitro/blob/master/LICENSE
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
library AddressAliasHelper {
uint160 internal constant OFFSET = uint160(0x1111000000000000000000000000000000001111);
/// @notice Utility function that converts the address in the L1 that submitted a tx to
/// the inbox to the msg.sender viewed in the L2
/// @param l1Address the address in the L1 that triggered the tx to L2
/// @return l2Address L2 address as viewed in msg.sender
function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) {
unchecked {
l2Address = address(uint160(l1Address) + OFFSET);
}
}
/// @notice Utility function that converts the msg.sender viewed in the L2 to the
/// address in the L1 that submitted a tx to the inbox
/// @param l2Address L2 address as viewed in msg.sender
/// @return l1Address the address in the L1 that triggered the tx to L2
function undoL1ToL2Alias(address l2Address) internal pure returns (address l1Address) {
unchecked {
l1Address = address(uint160(l2Address) - OFFSET);
}
}
}
contracts/governance/IL2Sender.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
interface IL2Sender {
/**
* @notice Sends ready task instruction to L2 executor
* @param _task - task ID to ready
*/
function readyTask(uint256 _task) external returns (uint256);
}
contracts/teststubs/TokenStubs.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import { ERC1155 } from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
contract TestERC20 is ERC20 {
constructor() ERC20("testERC20", "testERC20") {}
function mint(address _account, uint256 _amount) external {
_mint(_account, _amount);
}
}
contract TestERC721 is ERC721 {
constructor() ERC721("testERC721", "testERC721") {}
function mint(address _account, uint256 _tokenId) external {
_mint(_account, _tokenId);
}
}
contract TestERC1155 is ERC1155 {
constructor() ERC1155("testERC1155") {}
function mint(
address _account,
uint256 _tokenId,
uint256 _amount,
bytes calldata _data
) external {
_mint(_account, _tokenId, _amount, _data);
}
}
contract AdminERC20 is ERC20, Ownable {
constructor(string memory name, string memory symbol) ERC20(name, symbol) {
// Transfer ownership
Ownable.transferOwnership(msg.sender);
}
function adminMint(address _to, uint256 _amount) external onlyOwner {
_mint(_to, _amount);
}
function adminBurn(address _from, uint256 _amount) external onlyOwner {
_burn(_from, _amount);
}
}
contract NonTransferringERC20 is TestERC20 {
function transfer(address to, uint256 amount) public virtual override returns (bool) {
to;
amount;
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual override returns (bool) {
from;
to;
amount;
return true;
}
}
contract NonTransferringERC721 is TestERC721 {
function transferFrom(address from, address to, uint256 tokenId) public override {}
}
contracts/teststubs/governance/arbitrum/ArbRetryableTxStub.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
contract ArbRetryableTxStub {
function getLifetime() external pure returns (uint256) {
return 14 days;
}
function getTimeout(bytes32 _id) external pure returns (uint256) {
return uint256(keccak256(abi.encodePacked(_id)));
}
function redeem(bytes32 _id) external pure returns (bytes32) {
return _id;
}
}
contracts/adapt/IWBase.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IWBase is IERC20 {
function deposit() external payable;
function withdraw(uint256) external;
}
@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
@openzeppelin/contracts/access/Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^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() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(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");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
@openzeppelin/contracts/token/ERC1155/extensions/IERC1155MetadataURI.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/IERC1155MetadataURI.sol)
pragma solidity ^0.8.0;
import "../IERC1155.sol";
/**
* @dev Interface of the optional ERC1155MetadataExtension interface, as defined
* in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP].
*
* _Available since v3.1._
*/
interface IERC1155MetadataURI is IERC1155 {
/**
* @dev Returns the URI for token type `id`.
*
* If the `\{id\}` substring is present in the URI, it must be replaced by
* clients with the actual token type ID.
*/
function uri(uint256 id) external view returns (string memory);
}
contracts/token/VestLock.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
// OpenZeppelin v4
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { Staking } from "../governance/Staking.sol";
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
/**
* @title VestLock
* @author Railgun Contributors
* @notice Escrows vested tokens
* @dev Designed to be used behind lightweight clones proxies
*/
contract VestLock is Initializable, OwnableUpgradeable {
using SafeERC20 for IERC20;
// Time to release tokens
uint256 public releaseTime;
// Staking contract
Staking public staking;
// Override key
address public admin;
// Lock functions until after releaseTime
modifier locked() {
require(block.timestamp > releaseTime, "VestLock: Vesting hasn't matured yet");
_;
}
// Lock function unless called by admin
modifier onlyAdmin() {
require(msg.sender == admin, "VestLock: Caller not admin");
_;
}
/**
* @notice Initializes escrow contract
* @dev Token must be railgun token, requires delegate function on token contract
* @param _beneficiary - address to send tokens to at release time
* @param _staking - Staking contract address
* @param _releaseTime - time to release tokens
*/
function initialize(
address _admin,
address _beneficiary,
Staking _staking,
uint256 _releaseTime
) external initializer {
// Set admin
admin = _admin;
// Init OwnableUpgradeable
OwnableUpgradeable.__Ownable_init();
// Set beneficiary as owner
OwnableUpgradeable.transferOwnership(_beneficiary);
// Set the stacking contract
staking = _staking;
// Set release time
releaseTime = _releaseTime;
}
/**
* @notice Delegates stake
* @param _id - id of stake to claim
* @param _delegatee - address to delegate to
*/
function delegate(uint256 _id, address _delegatee) public onlyOwner {
staking.delegate(_id, _delegatee);
}
/**
* @notice Stakes tokens
* @param _token - address of the rail token
* @param _amount - amount to stake
*/
function stake(IERC20 _token, uint256 _amount) external onlyOwner {
_token.safeApprove(address(staking), _amount);
uint256 stakeID = staking.stake(_amount);
staking.delegate(stakeID, OwnableUpgradeable.owner());
}
/**
* @notice Unlocks tokens
* @param _id - id of stake to unstake
*/
function unlock(uint256 _id) external onlyOwner {
staking.unlock(_id);
}
/**
* @notice Claims tokens
* @param _id - id of stake to claim
*/
function claim(uint256 _id) external onlyOwner {
staking.claim(_id);
}
/**
* @notice Transfers ETH to specified address
* @param _to - Address to transfer ETH to
* @param _amount - Amount of ETH to transfer
*/
function transferETH(address payable _to, uint256 _amount) external locked onlyOwner {
// solhint-disable-next-line avoid-low-level-calls
(bool sent, ) = _to.call{ value: _amount }("");
require(sent, "Failed to send Ether");
}
/**
* @notice Transfers ERC20 to specified address
* @param _token - ERC20 token address to transfer
* @param _to - Address to transfer tokens to
* @param _amount - Amount of tokens to transfer
*/
function transferERC20(IERC20 _token, address _to, uint256 _amount) external locked onlyOwner {
_token.safeTransfer(_to, _amount);
}
/**
* @notice Calls function
* @dev calls to functions on arbitrary contracts
* @param _contract - contract to call
* @param _data - calldata to pass to contract
* @param _value - ETH value to include in call
*/
function callContract(
address _contract,
bytes calldata _data,
uint256 _value
) external locked onlyOwner {
// Call external contract and return
// solhint-disable-next-line avoid-low-level-calls
(bool success, ) = _contract.call{ value: _value }(_data);
require(success, "VestLock: failure on external contract call");
}
/**
* @notice Overrides vesting lock
* @dev can only override to a lower value
* @param _newLocktime - new lock time
*/
function overrideLock(uint256 _newLocktime) external onlyAdmin {
require(_newLocktime < releaseTime, "VestLock: new lock time must be less than old lock time");
releaseTime = _newLocktime;
}
/**
* @notice Receive ETH
*/
// solhint-disable-next-line no-empty-blocks
receive() external payable {}
}
@openzeppelin/contracts/proxy/Clones.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/Clones.sol)
pragma solidity ^0.8.0;
/**
* @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
* deploying minimal proxy contracts, also known as "clones".
*
* > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
* > a minimal bytecode implementation that delegates all calls to a known, fixed address.
*
* The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
* (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
* deterministic method.
*
* _Available since v3.4._
*/
library Clones {
/**
* @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
*
* This function uses the create opcode, which should never revert.
*/
function clone(address implementation) internal returns (address instance) {
/// @solidity memory-safe-assembly
assembly {
// Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
// of the `implementation` address with the bytecode before the address.
mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
// Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
instance := create(0, 0x09, 0x37)
}
require(instance != address(0), "ERC1167: create failed");
}
/**
* @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
*
* This function uses the create2 opcode and a `salt` to deterministically deploy
* the clone. Using the same `implementation` and `salt` multiple time will revert, since
* the clones cannot be deployed twice at the same address.
*/
function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
/// @solidity memory-safe-assembly
assembly {
// Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
// of the `implementation` address with the bytecode before the address.
mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
// Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
instance := create2(0, 0x09, 0x37, salt)
}
require(instance != address(0), "ERC1167: create2 failed");
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
*/
function predictDeterministicAddress(
address implementation,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(add(ptr, 0x38), deployer)
mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
mstore(add(ptr, 0x14), implementation)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
mstore(add(ptr, 0x58), salt)
mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
predicted := keccak256(add(ptr, 0x43), 0x55)
}
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
*/
function predictDeterministicAddress(
address implementation,
bytes32 salt
) internal view returns (address predicted) {
return predictDeterministicAddress(implementation, salt, address(this));
}
}
contracts/governance/Getters.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
// OpenZeppelin v4
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { Staking } from "./Staking.sol";
import { GovernorRewards } from "../treasury/GovernorRewards.sol";
/**
* @title Governance Getters
* @author Railgun Contributors
* @notice Convenience functions to quickly get data from governance contracts
*/
contract Getters {
Staking public staking;
GovernorRewards public governorRewards;
/**
* @notice Sets external contract addresses
* @param _staking - staking contract
* @param _governorRewards - governor rewards contract
*/
constructor(Staking _staking, GovernorRewards _governorRewards) {
staking = _staking;
governorRewards = _governorRewards;
}
/**
* @notice Gets all snapshots for account
* @param _account - account to get snapshots for
* @return snapshots
*/
function getAccountSnapshots(
address _account
) external view returns (Staking.AccountSnapshot[] memory) {
// Get number of snapshots
uint256 length = staking.accountSnapshotLength(_account);
// Retrieve snapshots
Staking.AccountSnapshot[] memory snapshots = new Staking.AccountSnapshot[](length);
for (uint256 i = 0; i < length; i++) {
snapshots[i] = staking.accountSnapshot(_account, i);
}
// Return
return snapshots;
}
/**
* @notice Gets all snapshots for globals
* @return snapshots
*/
function getGlobalsSnapshots() external view returns (Staking.GlobalsSnapshot[] memory) {
// Get number of snapshots
uint256 length = staking.globalsSnapshotLength();
// Retrieve snapshots
Staking.GlobalsSnapshot[] memory snapshots = new Staking.GlobalsSnapshot[](length);
for (uint256 i = 0; i < length; i++) {
snapshots[i] = staking.globalsSnapshot(i);
}
// Return
return snapshots;
}
/**
* @notice Gets all claimed for account and interval for token
* @dev this will return a flattened bool array
* @param _account - account to get claims for
* @param _token - token to get earned amount for
* @param _startingInterval - fetch starting interval
* @param _endingInterval - fetch ending interval
* @return flattened claims array
*/
function getEarnedTokensPerInterval(
address _account,
IERC20 _token,
uint256 _startingInterval,
uint256 _endingInterval
) external view returns (uint256[] memory) {
// Create bitmap array
uint256 intervalsLength = _endingInterval - _startingInterval + 1;
uint256[] memory earned = new uint256[](intervalsLength);
// Loop through each interval
for (uint256 intervalIter = 0; intervalIter < intervalsLength; intervalIter++) {
// Calculate hint parameter
uint256[] memory hintParam = new uint256[](1);
hintParam[0] = 0;
// Calculate tokens parameter
IERC20[] memory tokenParam = new IERC20[](1);
tokenParam[0] = _token;
// Get earned amounts for token
uint256[] memory earnedForInterval = governorRewards.calculateRewards(
tokenParam,
_account,
intervalIter + _startingInterval,
intervalIter + _startingInterval,
hintParam,
false
);
earned[intervalIter] = earnedForInterval[0];
}
// Return
return earned;
}
/**
* @notice Gets all claimed for account and interval for token
* @dev this will return a flattened bool array
* @param _account - account to get claims for
* @param _tokens - tokens to get claims bitmap for
* @param _startingInterval - fetch starting interval
* @param _endingInterval - fetch ending interval
* @return flattened claims array
*/
function getClaimed(
address _account,
IERC20[] calldata _tokens,
uint256 _startingInterval,
uint256 _endingInterval
) external view returns (bool[] memory) {
// Create bitmap array
uint256 intervalsLength = _endingInterval - _startingInterval + 1;
bool[] memory claimed = new bool[](_tokens.length * intervalsLength);
// Loop through each token
for (uint256 tokensIter = 0; tokensIter < _tokens.length; tokensIter++) {
for (uint256 intervalIter = 0; intervalIter < intervalsLength; intervalIter++) {
claimed[tokensIter * intervalsLength + intervalIter] = governorRewards.getClaimed(
_account,
_tokens[tokensIter],
intervalIter + _startingInterval
);
}
}
// Return
return claimed;
}
}
@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
pragma solidity ^0.8.0;
import "./IERC165Upgradeable.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {
function __ERC165_init() internal onlyInitializing {
}
function __ERC165_init_unchained() internal onlyInitializing {
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165Upgradeable).interfaceId;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}
contracts/token/RailFixed.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
// OpenZeppelin v4
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
/**
* @title RailTokenDAOMintable
* @author Railgun Contributors
* @notice ERC20 Railgun Governance Token
*/
contract RailTokenFixedSupply is ERC20 {
/**
* @notice Mints initial token supply
*/
constructor(
address _initialHolder,
uint256 _initialSupply,
string memory name,
string memory symbol
) ERC20(name, symbol) {
// Mint initial tokens
_mint(_initialHolder, _initialSupply);
}
}
@arbitrum/nitro-contracts/src/bridge/IBridge.sol
// Copyright 2021-2022, Offchain Labs, Inc.
// For license information, see https://github.com/nitro/blob/master/LICENSE
// SPDX-License-Identifier: BUSL-1.1
// solhint-disable-next-line compiler-version
pragma solidity >=0.6.9 <0.9.0;
import "./IOwnable.sol";
interface IBridge {
event MessageDelivered(
uint256 indexed messageIndex,
bytes32 indexed beforeInboxAcc,
address inbox,
uint8 kind,
address sender,
bytes32 messageDataHash,
uint256 baseFeeL1,
uint64 timestamp
);
event BridgeCallTriggered(
address indexed outbox,
address indexed to,
uint256 value,
bytes data
);
event InboxToggle(address indexed inbox, bool enabled);
event OutboxToggle(address indexed outbox, bool enabled);
event SequencerInboxUpdated(address newSequencerInbox);
function enqueueDelayedMessage(
uint8 kind,
address sender,
bytes32 messageDataHash
) external payable returns (uint256);
function enqueueSequencerMessage(bytes32 dataHash, uint256 afterDelayedMessagesRead)
external
returns (
uint256 seqMessageIndex,
bytes32 beforeAcc,
bytes32 delayedAcc,
bytes32 acc
);
function submitBatchSpendingReport(address batchPoster, bytes32 dataHash)
external
returns (uint256 msgNum);
function executeCall(
address to,
uint256 value,
bytes calldata data
) external returns (bool success, bytes memory returnData);
// These are only callable by the admin
function setDelayedInbox(address inbox, bool enabled) external;
function setOutbox(address inbox, bool enabled) external;
function setSequencerInbox(address _sequencerInbox) external;
// View functions
function sequencerInbox() external view returns (address);
function activeOutbox() external view returns (address);
function allowedDelayedInboxes(address inbox) external view returns (bool);
function allowedOutboxes(address outbox) external view returns (bool);
function delayedInboxAccs(uint256 index) external view returns (bytes32);
function sequencerInboxAccs(uint256 index) external view returns (bytes32);
function delayedMessageCount() external view returns (uint256);
function sequencerMessageCount() external view returns (uint256);
function rollup() external view returns (IOwnable);
function acceptFundsFromOldBridge() external payable;
}
contracts/teststubs/logic/CommitmentsStub.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
import { Commitments } from "../../logic/Commitments.sol";
contract CommitmentsStub is Commitments {
constructor() {
initializeCommitmentsStub();
}
function doubleInit() external {
Commitments.initializeCommitments();
}
function initializeCommitmentsStub() internal initializer {
Commitments.initializeCommitments();
}
function insertLeavesStub(bytes32[] memory _leafHashes) external {
Commitments.insertLeaves(_leafHashes);
}
function setNextLeafIndex(uint256 _index) external {
Commitments.nextLeafIndex = _index;
}
}
@openzeppelin/contracts/utils/introspection/ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
pragma solidity ^0.8.0;
import "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
@openzeppelin/contracts/utils/structs/BitMaps.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/BitMaps.sol)
pragma solidity ^0.8.0;
/**
* @dev Library for managing uint256 to bool mapping in a compact and efficient way, providing the keys are sequential.
* Largely inspired by Uniswap's https://github.com/Uniswap/merkle-distributor/blob/master/contracts/MerkleDistributor.sol[merkle-distributor].
*/
library BitMaps {
struct BitMap {
mapping(uint256 => uint256) _data;
}
/**
* @dev Returns whether the bit at `index` is set.
*/
function get(BitMap storage bitmap, uint256 index) internal view returns (bool) {
uint256 bucket = index >> 8;
uint256 mask = 1 << (index & 0xff);
return bitmap._data[bucket] & mask != 0;
}
/**
* @dev Sets the bit at `index` to the boolean `value`.
*/
function setTo(BitMap storage bitmap, uint256 index, bool value) internal {
if (value) {
set(bitmap, index);
} else {
unset(bitmap, index);
}
}
/**
* @dev Sets the bit at `index`.
*/
function set(BitMap storage bitmap, uint256 index) internal {
uint256 bucket = index >> 8;
uint256 mask = 1 << (index & 0xff);
bitmap._data[bucket] |= mask;
}
/**
* @dev Unsets the bit at `index`.
*/
function unset(BitMap storage bitmap, uint256 index) internal {
uint256 bucket = index >> 8;
uint256 mask = 1 << (index & 0xff);
bitmap._data[bucket] &= ~mask;
}
}
contracts/teststubs/governance/arbitrum/ArbInboxStub.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
pragma abicoder v2;
contract ArbInboxStub {
address public to;
uint256 public arbTxCallValue;
uint256 public maxSubmissionCost;
address public submissionRefundAddress;
address public valueRefundAddress;
uint256 public gasLimit;
uint256 public maxFeePerGas;
bytes public data;
uint256 public ticketID;
function calculateRetryableSubmissionFee(
uint256 dataLength,
uint256 baseFee
) public pure returns (uint256) {
return baseFee * 2 + dataLength * 0; // multiply by 0 to silence unused variable warning
}
function createRetryableTicket(
address _to,
uint256 _arbTxCallValue,
uint256 _maxSubmissionCost,
address _submissionRefundAddress,
address _valueRefundAddress,
uint256 _gasLimit,
uint256 _maxFeePerGas,
bytes calldata _data
) external payable returns (uint256) {
require(
_maxSubmissionCost >= calculateRetryableSubmissionFee(_data.length, block.basefee),
"_maxSubmissionCost too low"
);
require(msg.value == _maxSubmissionCost, "msg.value wrong");
to = _to;
arbTxCallValue = _arbTxCallValue;
maxSubmissionCost = _maxSubmissionCost;
submissionRefundAddress = _submissionRefundAddress;
valueRefundAddress = _valueRefundAddress;
gasLimit = _gasLimit;
maxFeePerGas = _maxFeePerGas;
data = _data;
return ticketID;
}
function getData()
external
view
returns (address, uint256, uint256, address, address, uint256, uint256, bytes memory)
{
return (
to,
arbTxCallValue,
maxSubmissionCost,
submissionRefundAddress,
valueRefundAddress,
gasLimit,
maxFeePerGas,
data
);
}
function setTicketID(uint256 _newID) external {
ticketID = _newID;
}
}
@openzeppelin/contracts/token/ERC20/IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @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);
/**
* @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 `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, 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 `from` to `to` 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 from, address to, uint256 amount) external returns (bool);
}
Compiler Settings
{"outputSelection":{"*":{"*":["storageLayout","abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"],"":["ast"]}},"optimizer":{"runs":200,"enabled":true},"libraries":{"contracts/logic/Poseidon.sol":{"PoseidonT4":"0x2428d0ce4d50be67a7c461fe363faa2e66332b95","PoseidonT3":"0x11cb5badfa4bbf360661602a02227fb5fd315ec9"}}}
Contract ABI
[{"type":"event","name":"AddToBlocklist","inputs":[{"type":"address","name":"token","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"FeeChange","inputs":[{"type":"uint256","name":"shieldFee","internalType":"uint256","indexed":false},{"type":"uint256","name":"unshieldFee","internalType":"uint256","indexed":false},{"type":"uint256","name":"nftFee","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Initialized","inputs":[{"type":"uint8","name":"version","internalType":"uint8","indexed":false}],"anonymous":false},{"type":"event","name":"Nullified","inputs":[{"type":"uint16","name":"treeNumber","internalType":"uint16","indexed":false},{"type":"bytes32[]","name":"nullifier","internalType":"bytes32[]","indexed":false}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"RemoveFromBlocklist","inputs":[{"type":"address","name":"token","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"Shield","inputs":[{"type":"uint256","name":"treeNumber","internalType":"uint256","indexed":false},{"type":"uint256","name":"startPosition","internalType":"uint256","indexed":false},{"type":"tuple[]","name":"commitments","internalType":"struct CommitmentPreimage[]","indexed":false,"components":[{"type":"bytes32","name":"npk","internalType":"bytes32"},{"type":"tuple","name":"token","internalType":"struct TokenData","components":[{"type":"uint8","name":"tokenType","internalType":"enum TokenType"},{"type":"address","name":"tokenAddress","internalType":"address"},{"type":"uint256","name":"tokenSubID","internalType":"uint256"}]},{"type":"uint120","name":"value","internalType":"uint120"}]},{"type":"tuple[]","name":"shieldCiphertext","internalType":"struct ShieldCiphertext[]","indexed":false,"components":[{"type":"bytes32[3]","name":"encryptedBundle","internalType":"bytes32[3]"},{"type":"bytes32","name":"shieldKey","internalType":"bytes32"}]},{"type":"uint256[]","name":"fees","internalType":"uint256[]","indexed":false}],"anonymous":false},{"type":"event","name":"Transact","inputs":[{"type":"uint256","name":"treeNumber","internalType":"uint256","indexed":false},{"type":"uint256","name":"startPosition","internalType":"uint256","indexed":false},{"type":"bytes32[]","name":"hash","internalType":"bytes32[]","indexed":false},{"type":"tuple[]","name":"ciphertext","internalType":"struct CommitmentCiphertext[]","indexed":false,"components":[{"type":"bytes32[4]","name":"ciphertext","internalType":"bytes32[4]"},{"type":"bytes32","name":"blindedSenderViewingKey","internalType":"bytes32"},{"type":"bytes32","name":"blindedReceiverViewingKey","internalType":"bytes32"},{"type":"bytes","name":"annotationData","internalType":"bytes"},{"type":"bytes","name":"memo","internalType":"bytes"}]}],"anonymous":false},{"type":"event","name":"TreasuryChange","inputs":[{"type":"address","name":"treasury","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"Unshield","inputs":[{"type":"address","name":"to","internalType":"address","indexed":false},{"type":"tuple","name":"token","internalType":"struct TokenData","indexed":false,"components":[{"type":"uint8","name":"tokenType","internalType":"enum TokenType"},{"type":"address","name":"tokenAddress","internalType":"address"},{"type":"uint256","name":"tokenSubID","internalType":"uint256"}]},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false},{"type":"uint256","name":"fee","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"VerifyingKeySet","inputs":[{"type":"uint256","name":"nullifiers","internalType":"uint256","indexed":false},{"type":"uint256","name":"commitments","internalType":"uint256","indexed":false},{"type":"tuple","name":"verifyingKey","internalType":"struct VerifyingKey","indexed":false,"components":[{"type":"string","name":"artifactsIPFSHash","internalType":"string"},{"type":"tuple","name":"alpha1","internalType":"struct G1Point","components":[{"type":"uint256","name":"x","internalType":"uint256"},{"type":"uint256","name":"y","internalType":"uint256"}]},{"type":"tuple","name":"beta2","internalType":"struct G2Point","components":[{"type":"uint256[2]","name":"x","internalType":"uint256[2]"},{"type":"uint256[2]","name":"y","internalType":"uint256[2]"}]},{"type":"tuple","name":"gamma2","internalType":"struct G2Point","components":[{"type":"uint256[2]","name":"x","internalType":"uint256[2]"},{"type":"uint256[2]","name":"y","internalType":"uint256[2]"}]},{"type":"tuple","name":"delta2","internalType":"struct G2Point","components":[{"type":"uint256[2]","name":"x","internalType":"uint256[2]"},{"type":"uint256[2]","name":"y","internalType":"uint256[2]"}]},{"type":"tuple[]","name":"ic","internalType":"struct G1Point[]","components":[{"type":"uint256","name":"x","internalType":"uint256"},{"type":"uint256","name":"y","internalType":"uint256"}]}]}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"ZERO_VALUE","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"addToBlocklist","inputs":[{"type":"address[]","name":"_tokens","internalType":"address[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"addVector","inputs":[{"type":"uint256","name":"vector","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"changeFee","inputs":[{"type":"uint120","name":"_shieldFee","internalType":"uint120"},{"type":"uint120","name":"_unshieldFee","internalType":"uint120"},{"type":"uint256","name":"_nftFee","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"changeTreasury","inputs":[{"type":"address","name":"_treasury","internalType":"address payable"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"checkSafetyVectors","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint120","name":"","internalType":"uint120"},{"type":"uint120","name":"","internalType":"uint120"}],"name":"getFee","inputs":[{"type":"uint136","name":"_amount","internalType":"uint136"},{"type":"bool","name":"_isInclusive","internalType":"bool"},{"type":"uint120","name":"_feeBP","internalType":"uint120"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"}],"name":"getInsertionTreeNumberAndStartingIndex","inputs":[{"type":"uint256","name":"_newCommitments","internalType":"uint256"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"getTokenID","inputs":[{"type":"tuple","name":"_tokenData","internalType":"struct TokenData","components":[{"type":"uint8","name":"tokenType","internalType":"enum TokenType"},{"type":"address","name":"tokenAddress","internalType":"address"},{"type":"uint256","name":"tokenSubID","internalType":"uint256"}]}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"","internalType":"struct VerifyingKey","components":[{"type":"string","name":"artifactsIPFSHash","internalType":"string"},{"type":"tuple","name":"alpha1","internalType":"struct G1Point","components":[{"type":"uint256","name":"x","internalType":"uint256"},{"type":"uint256","name":"y","internalType":"uint256"}]},{"type":"tuple","name":"beta2","internalType":"struct G2Point","components":[{"type":"uint256[2]","name":"x","internalType":"uint256[2]"},{"type":"uint256[2]","name":"y","internalType":"uint256[2]"}]},{"type":"tuple","name":"gamma2","internalType":"struct G2Point","components":[{"type":"uint256[2]","name":"x","internalType":"uint256[2]"},{"type":"uint256[2]","name":"y","internalType":"uint256[2]"}]},{"type":"tuple","name":"delta2","internalType":"struct G2Point","components":[{"type":"uint256[2]","name":"x","internalType":"uint256[2]"},{"type":"uint256[2]","name":"y","internalType":"uint256[2]"}]},{"type":"tuple[]","name":"ic","internalType":"struct G1Point[]","components":[{"type":"uint256","name":"x","internalType":"uint256"},{"type":"uint256","name":"y","internalType":"uint256"}]}]}],"name":"getVerificationKey","inputs":[{"type":"uint256","name":"_nullifiers","internalType":"uint256"},{"type":"uint256","name":"_commitments","internalType":"uint256"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"hashBoundParams","inputs":[{"type":"tuple","name":"_boundParams","internalType":"struct BoundParams","components":[{"type":"uint16","name":"treeNumber","internalType":"uint16"},{"type":"uint72","name":"minGasPrice","internalType":"uint72"},{"type":"uint8","name":"unshield","internalType":"enum UnshieldType"},{"type":"uint64","name":"chainID","internalType":"uint64"},{"type":"address","name":"adaptContract","internalType":"address"},{"type":"bytes32","name":"adaptParams","internalType":"bytes32"},{"type":"tuple[]","name":"commitmentCiphertext","internalType":"struct CommitmentCiphertext[]","components":[{"type":"bytes32[4]","name":"ciphertext","internalType":"bytes32[4]"},{"type":"bytes32","name":"blindedSenderViewingKey","internalType":"bytes32"},{"type":"bytes32","name":"blindedReceiverViewingKey","internalType":"bytes32"},{"type":"bytes","name":"annotationData","internalType":"bytes"},{"type":"bytes","name":"memo","internalType":"bytes"}]}]}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"hashCommitment","inputs":[{"type":"tuple","name":"_commitmentPreimage","internalType":"struct CommitmentPreimage","components":[{"type":"bytes32","name":"npk","internalType":"bytes32"},{"type":"tuple","name":"token","internalType":"struct TokenData","components":[{"type":"uint8","name":"tokenType","internalType":"enum TokenType"},{"type":"address","name":"tokenAddress","internalType":"address"},{"type":"uint256","name":"tokenSubID","internalType":"uint256"}]},{"type":"uint120","name":"value","internalType":"uint120"}]}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"hashLeftRight","inputs":[{"type":"bytes32","name":"_left","internalType":"bytes32"},{"type":"bytes32","name":"_right","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initializeRailgunLogic","inputs":[{"type":"address","name":"_treasury","internalType":"address payable"},{"type":"uint120","name":"_shieldFee","internalType":"uint120"},{"type":"uint120","name":"_unshieldFee","internalType":"uint120"},{"type":"uint256","name":"_nftFee","internalType":"uint256"},{"type":"address","name":"_owner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"lastEventBlock","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"merkleRoot","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"nextLeafIndex","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"nftFee","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"nullifiers","inputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"bytes32","name":"","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"removeFromBlocklist","inputs":[{"type":"address[]","name":"_tokens","internalType":"address[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"removeVector","inputs":[{"type":"uint256","name":"vector","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"rootHistory","inputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"bytes32","name":"","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setVerificationKey","inputs":[{"type":"uint256","name":"_nullifiers","internalType":"uint256"},{"type":"uint256","name":"_commitments","internalType":"uint256"},{"type":"tuple","name":"_verifyingKey","internalType":"struct VerifyingKey","components":[{"type":"string","name":"artifactsIPFSHash","internalType":"string"},{"type":"tuple","name":"alpha1","internalType":"struct G1Point","components":[{"type":"uint256","name":"x","internalType":"uint256"},{"type":"uint256","name":"y","internalType":"uint256"}]},{"type":"tuple","name":"beta2","internalType":"struct G2Point","components":[{"type":"uint256[2]","name":"x","internalType":"uint256[2]"},{"type":"uint256[2]","name":"y","internalType":"uint256[2]"}]},{"type":"tuple","name":"gamma2","internalType":"struct G2Point","components":[{"type":"uint256[2]","name":"x","internalType":"uint256[2]"},{"type":"uint256[2]","name":"y","internalType":"uint256[2]"}]},{"type":"tuple","name":"delta2","internalType":"struct G2Point","components":[{"type":"uint256[2]","name":"x","internalType":"uint256[2]"},{"type":"uint256[2]","name":"y","internalType":"uint256[2]"}]},{"type":"tuple[]","name":"ic","internalType":"struct G1Point[]","components":[{"type":"uint256","name":"x","internalType":"uint256"},{"type":"uint256","name":"y","internalType":"uint256"}]}]}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"shield","inputs":[{"type":"tuple[]","name":"_shieldRequests","internalType":"struct ShieldRequest[]","components":[{"type":"tuple","name":"preimage","internalType":"struct CommitmentPreimage","components":[{"type":"bytes32","name":"npk","internalType":"bytes32"},{"type":"tuple","name":"token","internalType":"struct TokenData","components":[{"type":"uint8","name":"tokenType","internalType":"enum TokenType"},{"type":"address","name":"tokenAddress","internalType":"address"},{"type":"uint256","name":"tokenSubID","internalType":"uint256"}]},{"type":"uint120","name":"value","internalType":"uint120"}]},{"type":"tuple","name":"ciphertext","internalType":"struct ShieldCiphertext","components":[{"type":"bytes32[3]","name":"encryptedBundle","internalType":"bytes32[3]"},{"type":"bytes32","name":"shieldKey","internalType":"bytes32"}]}]}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint120","name":"","internalType":"uint120"}],"name":"shieldFee","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"snarkSafetyVector","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"sumCommitments","inputs":[{"type":"tuple[]","name":"_transactions","internalType":"struct Transaction[]","components":[{"type":"tuple","name":"proof","internalType":"struct SnarkProof","components":[{"type":"tuple","name":"a","internalType":"struct G1Point","components":[{"type":"uint256","name":"x","internalType":"uint256"},{"type":"uint256","name":"y","internalType":"uint256"}]},{"type":"tuple","name":"b","internalType":"struct G2Point","components":[{"type":"uint256[2]","name":"x","internalType":"uint256[2]"},{"type":"uint256[2]","name":"y","internalType":"uint256[2]"}]},{"type":"tuple","name":"c","internalType":"struct G1Point","components":[{"type":"uint256","name":"x","internalType":"uint256"},{"type":"uint256","name":"y","internalType":"uint256"}]}]},{"type":"bytes32","name":"merkleRoot","internalType":"bytes32"},{"type":"bytes32[]","name":"nullifiers","internalType":"bytes32[]"},{"type":"bytes32[]","name":"commitments","internalType":"bytes32[]"},{"type":"tuple","name":"boundParams","internalType":"struct BoundParams","components":[{"type":"uint16","name":"treeNumber","internalType":"uint16"},{"type":"uint72","name":"minGasPrice","internalType":"uint72"},{"type":"uint8","name":"unshield","internalType":"enum UnshieldType"},{"type":"uint64","name":"chainID","internalType":"uint64"},{"type":"address","name":"adaptContract","internalType":"address"},{"type":"bytes32","name":"adaptParams","internalType":"bytes32"},{"type":"tuple[]","name":"commitmentCiphertext","internalType":"struct CommitmentCiphertext[]","components":[{"type":"bytes32[4]","name":"ciphertext","internalType":"bytes32[4]"},{"type":"bytes32","name":"blindedSenderViewingKey","internalType":"bytes32"},{"type":"bytes32","name":"blindedReceiverViewingKey","internalType":"bytes32"},{"type":"bytes","name":"annotationData","internalType":"bytes"},{"type":"bytes","name":"memo","internalType":"bytes"}]}]},{"type":"tuple","name":"unshieldPreimage","internalType":"struct CommitmentPreimage","components":[{"type":"bytes32","name":"npk","internalType":"bytes32"},{"type":"tuple","name":"token","internalType":"struct TokenData","components":[{"type":"uint8","name":"tokenType","internalType":"enum TokenType"},{"type":"address","name":"tokenAddress","internalType":"address"},{"type":"uint256","name":"tokenSubID","internalType":"uint256"}]},{"type":"uint120","name":"value","internalType":"uint120"}]}]}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"tokenBlocklist","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"tokenType","internalType":"enum TokenType"},{"type":"address","name":"tokenAddress","internalType":"address"},{"type":"uint256","name":"tokenSubID","internalType":"uint256"}],"name":"tokenIDMapping","inputs":[{"type":"bytes32","name":"","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transact","inputs":[{"type":"tuple[]","name":"_transactions","internalType":"struct Transaction[]","components":[{"type":"tuple","name":"proof","internalType":"struct SnarkProof","components":[{"type":"tuple","name":"a","internalType":"struct G1Point","components":[{"type":"uint256","name":"x","internalType":"uint256"},{"type":"uint256","name":"y","internalType":"uint256"}]},{"type":"tuple","name":"b","internalType":"struct G2Point","components":[{"type":"uint256[2]","name":"x","internalType":"uint256[2]"},{"type":"uint256[2]","name":"y","internalType":"uint256[2]"}]},{"type":"tuple","name":"c","internalType":"struct G1Point","components":[{"type":"uint256","name":"x","internalType":"uint256"},{"type":"uint256","name":"y","internalType":"uint256"}]}]},{"type":"bytes32","name":"merkleRoot","internalType":"bytes32"},{"type":"bytes32[]","name":"nullifiers","internalType":"bytes32[]"},{"type":"bytes32[]","name":"commitments","internalType":"bytes32[]"},{"type":"tuple","name":"boundParams","internalType":"struct BoundParams","components":[{"type":"uint16","name":"treeNumber","internalType":"uint16"},{"type":"uint72","name":"minGasPrice","internalType":"uint72"},{"type":"uint8","name":"unshield","internalType":"enum UnshieldType"},{"type":"uint64","name":"chainID","internalType":"uint64"},{"type":"address","name":"adaptContract","internalType":"address"},{"type":"bytes32","name":"adaptParams","internalType":"bytes32"},{"type":"tuple[]","name":"commitmentCiphertext","internalType":"struct CommitmentCiphertext[]","components":[{"type":"bytes32[4]","name":"ciphertext","internalType":"bytes32[4]"},{"type":"bytes32","name":"blindedSenderViewingKey","internalType":"bytes32"},{"type":"bytes32","name":"blindedReceiverViewingKey","internalType":"bytes32"},{"type":"bytes","name":"annotationData","internalType":"bytes"},{"type":"bytes","name":"memo","internalType":"bytes"}]}]},{"type":"tuple","name":"unshieldPreimage","internalType":"struct CommitmentPreimage","components":[{"type":"bytes32","name":"npk","internalType":"bytes32"},{"type":"tuple","name":"token","internalType":"struct TokenData","components":[{"type":"uint8","name":"tokenType","internalType":"enum TokenType"},{"type":"address","name":"tokenAddress","internalType":"address"},{"type":"uint256","name":"tokenSubID","internalType":"uint256"}]},{"type":"uint120","name":"value","internalType":"uint120"}]}]}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address payable"}],"name":"treasury","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"treeNumber","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint120","name":"","internalType":"uint120"}],"name":"unshieldFee","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"},{"type":"string","name":"","internalType":"string"}],"name":"validateCommitmentPreimage","inputs":[{"type":"tuple","name":"_note","internalType":"struct CommitmentPreimage","components":[{"type":"bytes32","name":"npk","internalType":"bytes32"},{"type":"tuple","name":"token","internalType":"struct TokenData","components":[{"type":"uint8","name":"tokenType","internalType":"enum TokenType"},{"type":"address","name":"tokenAddress","internalType":"address"},{"type":"uint256","name":"tokenSubID","internalType":"uint256"}]},{"type":"uint120","name":"value","internalType":"uint120"}]}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"},{"type":"string","name":"","internalType":"string"}],"name":"validateTransaction","inputs":[{"type":"tuple","name":"_transaction","internalType":"struct Transaction","components":[{"type":"tuple","name":"proof","internalType":"struct SnarkProof","components":[{"type":"tuple","name":"a","internalType":"struct G1Point","components":[{"type":"uint256","name":"x","internalType":"uint256"},{"type":"uint256","name":"y","internalType":"uint256"}]},{"type":"tuple","name":"b","internalType":"struct G2Point","components":[{"type":"uint256[2]","name":"x","internalType":"uint256[2]"},{"type":"uint256[2]","name":"y","internalType":"uint256[2]"}]},{"type":"tuple","name":"c","internalType":"struct G1Point","components":[{"type":"uint256","name":"x","internalType":"uint256"},{"type":"uint256","name":"y","internalType":"uint256"}]}]},{"type":"bytes32","name":"merkleRoot","internalType":"bytes32"},{"type":"bytes32[]","name":"nullifiers","internalType":"bytes32[]"},{"type":"bytes32[]","name":"commitments","internalType":"bytes32[]"},{"type":"tuple","name":"boundParams","internalType":"struct BoundParams","components":[{"type":"uint16","name":"treeNumber","internalType":"uint16"},{"type":"uint72","name":"minGasPrice","internalType":"uint72"},{"type":"uint8","name":"unshield","internalType":"enum UnshieldType"},{"type":"uint64","name":"chainID","internalType":"uint64"},{"type":"address","name":"adaptContract","internalType":"address"},{"type":"bytes32","name":"adaptParams","internalType":"bytes32"},{"type":"tuple[]","name":"commitmentCiphertext","internalType":"struct CommitmentCiphertext[]","components":[{"type":"bytes32[4]","name":"ciphertext","internalType":"bytes32[4]"},{"type":"bytes32","name":"blindedSenderViewingKey","internalType":"bytes32"},{"type":"bytes32","name":"blindedReceiverViewingKey","internalType":"bytes32"},{"type":"bytes","name":"annotationData","internalType":"bytes"},{"type":"bytes","name":"memo","internalType":"bytes"}]}]},{"type":"tuple","name":"unshieldPreimage","internalType":"struct CommitmentPreimage","components":[{"type":"bytes32","name":"npk","internalType":"bytes32"},{"type":"tuple","name":"token","internalType":"struct TokenData","components":[{"type":"uint8","name":"tokenType","internalType":"enum TokenType"},{"type":"address","name":"tokenAddress","internalType":"address"},{"type":"uint256","name":"tokenSubID","internalType":"uint256"}]},{"type":"uint120","name":"value","internalType":"uint120"}]}]}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"verify","inputs":[{"type":"tuple","name":"_transaction","internalType":"struct Transaction","components":[{"type":"tuple","name":"proof","internalType":"struct SnarkProof","components":[{"type":"tuple","name":"a","internalType":"struct G1Point","components":[{"type":"uint256","name":"x","internalType":"uint256"},{"type":"uint256","name":"y","internalType":"uint256"}]},{"type":"tuple","name":"b","internalType":"struct G2Point","components":[{"type":"uint256[2]","name":"x","internalType":"uint256[2]"},{"type":"uint256[2]","name":"y","internalType":"uint256[2]"}]},{"type":"tuple","name":"c","internalType":"struct G1Point","components":[{"type":"uint256","name":"x","internalType":"uint256"},{"type":"uint256","name":"y","internalType":"uint256"}]}]},{"type":"bytes32","name":"merkleRoot","internalType":"bytes32"},{"type":"bytes32[]","name":"nullifiers","internalType":"bytes32[]"},{"type":"bytes32[]","name":"commitments","internalType":"bytes32[]"},{"type":"tuple","name":"boundParams","internalType":"struct BoundParams","components":[{"type":"uint16","name":"treeNumber","internalType":"uint16"},{"type":"uint72","name":"minGasPrice","internalType":"uint72"},{"type":"uint8","name":"unshield","internalType":"enum UnshieldType"},{"type":"uint64","name":"chainID","internalType":"uint64"},{"type":"address","name":"adaptContract","internalType":"address"},{"type":"bytes32","name":"adaptParams","internalType":"bytes32"},{"type":"tuple[]","name":"commitmentCiphertext","internalType":"struct CommitmentCiphertext[]","components":[{"type":"bytes32[4]","name":"ciphertext","internalType":"bytes32[4]"},{"type":"bytes32","name":"blindedSenderViewingKey","internalType":"bytes32"},{"type":"bytes32","name":"blindedReceiverViewingKey","internalType":"bytes32"},{"type":"bytes","name":"annotationData","internalType":"bytes"},{"type":"bytes","name":"memo","internalType":"bytes"}]}]},{"type":"tuple","name":"unshieldPreimage","internalType":"struct CommitmentPreimage","components":[{"type":"bytes32","name":"npk","internalType":"bytes32"},{"type":"tuple","name":"token","internalType":"struct TokenData","components":[{"type":"uint8","name":"tokenType","internalType":"enum TokenType"},{"type":"address","name":"tokenAddress","internalType":"address"},{"type":"uint256","name":"tokenSubID","internalType":"uint256"}]},{"type":"uint120","name":"value","internalType":"uint120"}]}]}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"verifyProof","inputs":[{"type":"tuple","name":"_verifyingKey","internalType":"struct VerifyingKey","components":[{"type":"string","name":"artifactsIPFSHash","internalType":"string"},{"type":"tuple","name":"alpha1","internalType":"struct G1Point","components":[{"type":"uint256","name":"x","internalType":"uint256"},{"type":"uint256","name":"y","internalType":"uint256"}]},{"type":"tuple","name":"beta2","internalType":"struct G2Point","components":[{"type":"uint256[2]","name":"x","internalType":"uint256[2]"},{"type":"uint256[2]","name":"y","internalType":"uint256[2]"}]},{"type":"tuple","name":"gamma2","internalType":"struct G2Point","components":[{"type":"uint256[2]","name":"x","internalType":"uint256[2]"},{"type":"uint256[2]","name":"y","internalType":"uint256[2]"}]},{"type":"tuple","name":"delta2","internalType":"struct G2Point","components":[{"type":"uint256[2]","name":"x","internalType":"uint256[2]"},{"type":"uint256[2]","name":"y","internalType":"uint256[2]"}]},{"type":"tuple[]","name":"ic","internalType":"struct G1Point[]","components":[{"type":"uint256","name":"x","internalType":"uint256"},{"type":"uint256","name":"y","internalType":"uint256"}]}]},{"type":"tuple","name":"_proof","internalType":"struct SnarkProof","components":[{"type":"tuple","name":"a","internalType":"struct G1Point","components":[{"type":"uint256","name":"x","internalType":"uint256"},{"type":"uint256","name":"y","internalType":"uint256"}]},{"type":"tuple","name":"b","internalType":"struct G2Point","components":[{"type":"uint256[2]","name":"x","internalType":"uint256[2]"},{"type":"uint256[2]","name":"y","internalType":"uint256[2]"}]},{"type":"tuple","name":"c","internalType":"struct G1Point","components":[{"type":"uint256","name":"x","internalType":"uint256"},{"type":"uint256","name":"y","internalType":"uint256"}]}]},{"type":"uint256[]","name":"_inputs","internalType":"uint256[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"zeros","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]}]
Contract Creation Code
0x608060405234801561001057600080fd5b5061591c80620000216000396000f3fe608060405234801561001057600080fd5b50600436106102535760003560e01c806371c5345511610146578063c718dbda116100c3578063e829558811610087578063e8295588146105c5578063ec732959146105d8578063ee990783146105e0578063f19ea903146105f3578063f2fde38b14610621578063f71a55f81461063457600080fd5b8063c718dbda14610555578063cc1f73fd14610583578063cd3efe0614610596578063d8ae136a1461059f578063dc7c9a1f146105b257600080fd5b806392d3c7a41161010a57806392d3c7a4146104bf578063a33952a9146104d2578063ab63e69c146104e5578063b14f2a39146104f8578063b8ceaef21461050b57600080fd5b806371c534551461044f5780637b12ae8314610462578063815b534f146104825780638da5cb5b146104a55780639071adc0146104b657600080fd5b80632ebfe12a116101d4578063617e1b1711610198578063617e1b17146103ed57806361d027b314610400578063665033151461042b5780636cefafaa14610434578063715018a61461044757600080fd5b80632ebfe12a1461036e5780632ec0f3591461038157806338bf282e1461039457806343c88730146103a757806359bb6199146103da57600080fd5b80630c9c0c8d1161021b5780630c9c0c8d146102d6578063129458f4146102fe578063174768911461033157806328f89c3a146103525780632eb4a7ab1461036557600080fd5b806301c3779014610258578063044a40c314610288578063053ed12a1461029d5780630804a4ee146102b75780630be4f422146102bf575b600080fd5b60fa5461026b906001600160781b031681565b6040516001600160781b0390911681526020015b60405180910390f35b61029b610296366004613d11565b610647565b005b60fa5461026b90600160781b90046001600160781b031681565b61029b610955565b6102c860665481565b60405190815260200161027f565b6102e96102e4366004613d86565b6109e1565b6040805192835260208301919091520161027f565b61032161030c366004613d86565b60fc6020526000908152604090205460ff1681565b604051901515815260200161027f565b61034461033f366004613d9f565b610a2b565b60405161027f929190613e2a565b6102c8610360366004613e45565b610e90565b6102c860675481565b61032161037c3660046141ce565b610edb565b61029b61038f3660046142e1565b610efe565b6102c86103a2366004614338565b610f6e565b6103ba6103b5366004614384565b611000565b604080516001600160781b0393841681529290911660208301520161027f565b6102c86103e8366004614422565b611091565b6103446103fb366004614457565b611106565b60f954610413906001600160a01b031681565b6040516001600160a01b03909116815260200161027f565b6102c860695481565b61029b610442366004613d86565b611291565b61029b6112b4565b6102c861045d3660046144e4565b6112c8565b610475610470366004614338565b61137c565b60405161027f919061456c565b610321610490366004614648565b60956020526000908152604090205460ff1681565b6033546001600160a01b0316610413565b6102c860fb5481565b6102c86104cd366004614665565b611641565b61029b6104e0366004614681565b61168d565b61029b6104f3366004614422565b611869565b61029b610506366004614648565b611989565b610546610519366004613d86565b60fd602052600090815260409020805460019091015460ff82169161010090046001600160a01b03169083565b60405161027f93929190614719565b610321610563366004614338565b608a60209081526000928352604080842090915290825290205460ff1681565b61029b610591366004614743565b6119fa565b6102c860fe5481565b61029b6105ad366004614422565b611bad565b61029b6105c0366004613d86565b611e31565b6102c86105d3366004613d86565b611e51565b6102c8611e68565b6103216105ee366004613d9f565b611ea3565b610321610601366004614338565b606560209081526000928352604080842090915290825290205460ff1681565b61029b61062f366004614648565b61239a565b61029b610642366004614422565b612410565b6000816001600160401b0381111561066157610661613e7f565b60405190808252806020026020018201604052801561068a578160200160208202803683370190505b5090506000826001600160401b038111156106a7576106a7613e7f565b6040519080825280602002602001820160405280156106e057816020015b6106cd613b91565b8152602001906001900390816106c55790505b5090506000836001600160401b038111156106fd576106fd613e7f565b60405190808252806020026020018201604052801561073657816020015b610723613bca565b81526020019060019003908161071b5790505b5090506000846001600160401b0381111561075357610753613e7f565b60405190808252806020026020018201604052801561077c578160200160208202803683370190505b50905060005b858110156108fc576000806107b18989858181106107a2576107a261477f565b90506101200201600001611106565b9150915081816040516020016107c79190614795565b604051602081830303815290604052906107fd5760405162461bcd60e51b81526004016107f491906147d1565b60405180910390fd5b506108228989858181106108135761081361477f565b90506101200201600001612533565b8785815181106108345761083461477f565b6020026020010186868151811061084d5761084d61477f565b6020026020010182815250829052505061087f8684815181106108725761087261477f565b60200260200101516112c8565b8784815181106108915761089161477f565b6020026020010181815250508888848181106108af576108af61477f565b9050610120020160a0018036038101906108c991906147e4565b8584815181106108db576108db61477f565b602002602001018190525050506001816108f59190614886565b9050610782565b507f3a5b9dc26075a3801a6ddccf95fec485bb7500a91b44cec1add984c21ee6db3b606954606654858585604051610938959493929190614969565b60405180910390a1610949846129ba565b50504360fe5550505050565b7f8dea8703c3cf94703383ce38a9c894669dccd4ca8e65ddb43267aa0248711450805460ff1916600117905533600090815260fc6020526040902054806109de5760405162461bcd60e51b815260206004820152601c60248201527f5261696c67756e4c6f6769633a20556e7361666520766563746f72730000000060448201526064016107f4565b50565b6000806109f060106002614af1565b836066546109fe9190614886565b1115610a1c57606954610a12906001614886565b9360009350915050565b60695460665491509150915091565b60006060610a3d610160840184614afd565b610a4e906040810190602001614b36565b68ffffffffffffffffff163a1015610a9257505060408051808201909152601181527047617320707269636520746f6f206c6f7760781b6020820152600092909150565b6000610aa2610160850185614afd565b610ab39060a0810190608001614648565b6001600160a01b031614158015610af1575033610ad4610160850185614afd565b610ae59060a0810190608001614648565b6001600160a01b031614155b15610b375760006040518060400160405280602081526020017f496e76616c696420416461707420436f6e74726163742061732053656e64657281525091509150915091565b46610b46610160850185614afd565b610b57906080810190606001614b68565b6001600160401b031614610b9657505060408051808201909152601081526f086d0c2d2dc928840dad2e6dac2e8c6d60831b6020820152600092909150565b608a6000610ba8610160860186614afd565b610bb6906020810190614b95565b61ffff16815260208082019290925260409081016000908120610100870135825290925290205460ff16610c18575050604080518082019091526013815272125b9d985b1a590813595c9adb1948149bdbdd606a1b6020820152600092909150565b6000610c28610160850185614afd565b610c39906060810190604001614bb0565b6002811115610c4a57610c4a6146e5565b14610ddf576001610c5f610140850185614bcd565b610c6a929150614c16565b610c78610160850185614afd565b610c869060c0810190614bcd565b905014610cb15760006040518060600160405280602481526020016158c36024913991509150915091565b60006002610cc3610160860186614afd565b610cd4906060810190604001614bb0565b6002811115610ce557610ce56146e5565b03610d41576040805160608101909152338152610d3a9060208101610d133688900388016101a08901614665565b8152602001610d2a61022088016102008901614c29565b6001600160781b031690526112c8565b9050610d5a565b610d5761045d36869003860161018087016144e4565b90505b610d68610140850185614bcd565b6001610d78610140880188614bcd565b610d83929150614c16565b818110610d9257610d9261477f565b905060200201358114610dd957600060405180604001604052806015815260200174496e76616c6964205769746864726177204e6f746560581b8152509250925050915091565b50610e36565b610ded610140840184614bcd565b9050610dfd610160850185614afd565b610e0b9060c0810190614bcd565b905014610e365760006040518060600160405280602481526020016158c36024913991509150915091565b610e3f83611ea3565b610e7757505060408051808201909152601381527224b73b30b634b21029b730b93590283937b7b360691b6020820152600092909150565b5050604080516020810190915260008152600192909150565b60006000805160206158a383398151915282604051602001610eb29190614dbd565b6040516020818303038152906040528051906020012060001c610ed59190614e87565b92915050565b6000610ef684610ef036869003860186614e9b565b84612c4f565b949350505050565b610f06612dc2565b600083815260c76020908152604080832085845290915290208190610f2b8282615174565b9050507f3d09e10d1c966d01c4a2c14d0ac9af253486aa5e99b6cffe9019c4d43eb1fb23838383604051610f6193929190615277565b60405180910390a1505050565b60408051808201825283815260208101839052905163014cf2b360e51b81526000917311cb5badfa4bbf360661602a02227fb5fd315ec99163299e566091610fb891600401615358565b602060405180830381865af4158015610fd5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ff99190615389565b9392505050565b6000806000808515611049576127106110226001600160781b038716896153a2565b61102c91906153cb565b61103690886153f1565b915061104282886153f1565b9050611085565b8691508161105986612710615418565b6001600160781b031661106e846127106153a2565b61107891906153cb565b61108291906153f1565b90505b90969095509350505050565b600080805b838110156110fe578484828181106110b0576110b061477f565b90506020028101906110c29190615438565b6110d190610160810190614afd565b6110df9060c0810190614bcd565b6110ea915083614886565b91506110f7600182614886565b9050611096565b509392505050565b6000606061111a60a0840160808501614c29565b6001600160781b031660000361115d575050604080518082019091526012815271496e76616c6964204e6f74652056616c756560701b6020820152600092909150565b609560006111716060860160408701614648565b6001600160a01b0316815260208101919091526040016000205460ff16156111c55750506040805180820190915260118152702ab739bab83837b93a32b2102a37b5b2b760791b6020820152600092909150565b6000805160206158a383398151915283351061120c57505060408051808201909152601081526f496e76616c6964204e6f7465204e504b60801b6020820152600092909150565b600161121e6040850160208601614bb0565b600281111561122f5761122f6146e5565b148015611255575061124760a0840160808501614c29565b6001600160781b0316600114155b15610e77575050604080518082019091526016815275496e76616c6964204e4654204e6f74652056616c756560501b6020820152600092909150565b611299612dc2565b600090815260fc60205260409020805460ff19166001179055565b6112bc612dc2565b6112c66000612e1c565b565b6000732428d0ce4d50be67a7c461fe363faa2e66332b95635a53025d6040518060600160405280856000015181526020016113068660200151611641565b815260200185604001516001600160781b031660001b8152506040518263ffffffff1660e01b815260040161133b919061544f565b602060405180830381865af4158015611358573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ed59190615389565b611384613bdd565b600083815260c76020908152604080832085845290915290819020815160c081019092528054829082906113b790614ee1565b80601f01602080910402602001604051908101604052809291908181526020018280546113e390614ee1565b80156114305780601f1061140557610100808354040283529160200191611430565b820191906000526020600020905b81548152906001019060200180831161141357829003601f168201915b505050918352505060408051808201825260018401548152600280850154602080840191909152840191909152815160808101808452938301939092600386019284929183019184919082845b81548152602001906001019080831161147d57505050918352505060408051808201918290526020909201919060028481019182845b8154815260200190600101908083116114b3575050509190925250505081526040805160808101808352602090930192909160078501918391820190839060029082845b8154815260200190600101908083116114f757505050918352505060408051808201918290526020909201919060028481019182845b81548152602001906001019080831161152d5750505091909252505050815260408051608081018083526020909301929091600b8501918391820190839060029082845b81548152602001906001019080831161157157505050918352505060408051808201918290526020909201919060028481019182845b8154815260200190600101908083116115a7575050505050815250508152602001600f8201805480602002602001604051908101604052809291908181526020016000905b82821015611632578382906000526020600020906002020160405180604001604052908160008201548152602001600182015481525050815260200190600101906115ec565b50505091525090949350505050565b60008082516002811115611657576116576146e5565b0361166d5750602001516001600160a01b031690565b6000805160206158a383398151915282604051602001610eb29190615477565b600054610100900460ff16158080156116ad5750600054600160ff909116105b806116c75750303b1580156116c7575060005460ff166001145b61172a5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084016107f4565b6000805460ff19166001179055801561174d576000805461ff0019166101001790555b611755612e6e565b61175d612e9d565b61176686611989565b6117718585856119fa565b61177a8261239a565b60fc6020527f18a261e1efd282dd636becf59b85995514ab34d1c1c9a3e1128aac786b5472de8054600160ff1991821681179092557f35cc25be27a7e44e9e6eb23f103c8980fe6d01e7b6e981c2c07b23911ac79e8b805482168317905573cc29dbf69896278a0c6b886ef8cc30a56fa6164a6000527f7e1bfa3bd01233beed03b737790a265ae14e11664e22f82316c6f65a13e15c8c805490911690911790558015611861576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050505050565b611871612dc2565b60005b8181101561198457609560008484848181106118925761189261477f565b90506020020160208101906118a79190614648565b6001600160a01b0316815260208101919091526040016000205460ff161561197257609560008484848181106118df576118df61477f565b90506020020160208101906118f49190614648565b6001600160a01b031681526020810191909152604001600020805460ff191690558282828181106119275761192761477f565b905060200201602081019061193c9190614648565b6001600160a01b03167f2ef13bd1aff17b0f9c85afaf228e84266c8394d9d381735e83fe23f607113e2e60405160405180910390a25b61197d600182614886565b9050611874565b505050565b611991612dc2565b60f9546001600160a01b038281169116146109de5760f980546001600160a01b0319166001600160a01b0383169081179091556040519081527f1ac54af2084ba0e4b86b7bc54b978ce7dd4ca046d3dec89d7c8963873820c0bb9060200160405180910390a150565b611a02612dc2565b60fa546001600160781b038481169116141580611a34575060fa546001600160781b03838116600160781b9092041614155b80611a41575060fb548114155b1561198457611a536002612710615485565b6001600160781b0316836001600160781b03161115611ac05760405162461bcd60e51b8152602060048201526024808201527f5261696c67756e4c6f6769633a20536869656c642046656520657863656564736044820152632035302560e01b60648201526084016107f4565b611acd6002612710615485565b6001600160781b0316826001600160781b03161115611b3d5760405162461bcd60e51b815260206004820152602660248201527f5261696c67756e4c6f6769633a20556e736869656c642046656520657863656560448201526564732035302560d01b60648201526084016107f4565b60fa80546001600160781b038581166001600160f01b03199092168217600160781b9186169182021790925560fb8390556040805191825260208201929092529081018290527f472cfc031d19bcc54db01976ce486cc12dc3d489e6adced1eb5a782cd55cfdf290606001610f61565b6000611bb98383611091565b90506000816001600160401b03811115611bd557611bd5613e7f565b604051908082528060200260200182016040528015611bfe578160200160208202803683370190505b509050600080836001600160401b03811115611c1c57611c1c613e7f565b604051908082528060200260200182016040528015611c5557816020015b611c42613c40565b815260200190600190039081611c3a5790505b50905060005b85811015611d1857600080611c8d898985818110611c7b57611c7b61477f565b905060200281019061033f9190615438565b915091508181604051602001611ca39190614795565b60405160208183030381529060405290611cd05760405162461bcd60e51b81526004016107f491906147d1565b50611d00898985818110611ce657611ce661477f565b9050602002810190611cf89190615438565b878787612fc4565b94505050600181611d119190614886565b9050611c5b565b5060005b85811015611dc2576000878783818110611d3857611d3861477f565b9050602002810190611d4a9190615438565b611d5990610160810190614afd565b611d6a906060810190604001614bb0565b6002811115611d7b57611d7b6146e5565b14611db057611db0878783818110611d9557611d9561477f565b9050602002810190611da79190615438565b610180016132ae565b611dbb600182614886565b9050611d1c565b50600080611dd085516109e1565b8651919350915015611e1a577f56a618cda1e34057b7f849a5792f6c8587a2dbe11c83d0254e72cb3daffda7d182828786604051611e11949392919061549f565b60405180910390a15b611e23856129ba565b50504360fe55505050505050565b611e39612dc2565b600090815260fc60205260409020805460ff19169055565b606a8160108110611e6157600080fd5b0154905081565b611ea06000805160206158a38339815191527f95b5e3f3c951508f13e7771152f5b7c7329a294917685c10a571c9247e3b9fff614e87565b81565b600080611eb4610120840184614bcd565b915060009050611ec8610140850185614bcd565b600084815260c760209081526040808320848452909152808220815160c0810190925280549395509193509182908290611f0190614ee1565b80601f0160208091040260200160405190810160405280929190818152602001828054611f2d90614ee1565b8015611f7a5780601f10611f4f57610100808354040283529160200191611f7a565b820191906000526020600020905b815481529060010190602001808311611f5d57829003601f168201915b505050918352505060408051808201825260018401548152600280850154602080840191909152840191909152815160808101808452938301939092600386019284929183019184919082845b815481526020019060010190808311611fc757505050918352505060408051808201918290526020909201919060028481019182845b815481526020019060010190808311611ffd575050509190925250505081526040805160808101808352602090930192909160078501918391820190839060029082845b81548152602001906001019080831161204157505050918352505060408051808201918290526020909201919060028481019182845b8154815260200190600101908083116120775750505091909252505050815260408051608081018083526020909301929091600b8501918391820190839060029082845b8154815260200190600101908083116120bb57505050918352505060408051808201918290526020909201919060028481019182845b8154815260200190600101908083116120f1575050505050815250508152602001600f8201805480602002602001604051908101604052809291908181526020016000905b8282101561217c57838290600052602060002090600202016040518060400160405290816000820154815260200160018201548152505081526020019060010190612136565b505050915250506020810151519091506000036121d35760405162461bcd60e51b815260206004820152601560248201527415995c9a599a595c8e8812d95e481b9bdd081cd95d605a1b60448201526064016107f4565b6000826121e1856002614886565b6121eb9190614886565b6001600160401b0381111561220257612202613e7f565b60405190808252806020026020018201604052801561222b578160200160208202803683370190505b50905085610100013560001c8160008151811061224a5761224a61477f565b6020908102919091010152612266610360610160880188614afd565b816001815181106122795761227961477f565b60200260200101818152505060005b848110156122f15761229e610120880188614bcd565b828181106122ae576122ae61477f565b60200291909101359050826122c4836002614886565b815181106122d4576122d461477f565b60209081029190910101526122ea600182614886565b9050612288565b5060005b838110156123695761230b610140880188614bcd565b8281811061231b5761231b61477f565b602002919091013590508282612332886002614886565b61233c9190614886565b8151811061234c5761234c61477f565b6020908102919091010152612362600182614886565b90506122f5565b506000612377838884610edb565b905061deac193201612390575060019695505050505050565b9695505050505050565b6123a2612dc2565b6001600160a01b0381166124075760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016107f4565b6109de81612e1c565b612418612dc2565b60005b8181101561198457609560008484848181106124395761243961477f565b905060200201602081019061244e9190614648565b6001600160a01b0316815260208101919091526040016000205460ff16612521576001609560008585858181106124875761248761477f565b905060200201602081019061249c9190614648565b6001600160a01b031681526020810191909152604001600020805460ff19169115159190911790558282828181106124d6576124d661477f565b90506020020160208101906124eb9190614648565b6001600160a01b03167f46742f555939247f80b50a8ca895a561933c48bc9a06ccb0c812e97ac723d33f60405160405180910390a25b61252c600182614886565b905061241b565b61253b613b91565b6000612545613b91565b6000806125586040870160208801614bb0565b6002811115612569576125696146e5565b036127925760006125806060870160408801614648565b90506000806125b061259860a08a0160808b01614c29565b60fa546001600160781b039182169160019116611000565b91509150806001600160781b03169350604051806060016040528089600001358152602001896020018036038101906125e99190614665565b81526001600160781b0384166020909101526040516370a0823160e01b81523060048201529095506000906001600160a01b038516906370a0823190602401602060405180830381865afa158015612645573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126699190615389565b90506126896001600160a01b03851633306001600160781b03871661349f565b6040516370a0823160e01b81523060048201526000906001600160a01b038616906370a0823190602401602060405180830381865afa1580156126d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126f49190615389565b90506001600160781b03841661270a8383614c16565b146127635760405162461bcd60e51b815260206004820152602360248201527f5261696c67756e4c6f6769633a204552433230207472616e73666572206661696044820152621b195960ea1b60648201526084016107f4565b60f954612788906001600160a01b03878116913391166001600160781b03871661349f565b50505050506129b0565b60016127a46040870160208801614bb0565b60028111156127b5576127b56146e5565b036129585760006127cc6060870160408801614648565b6000925090506127e1368790038701876144e4565b92506020860160fd60006127fd6104cd368b90038b0185614665565b8152602001908152602001600020818161281791906155b4565b50506040516323b872dd60e01b8152336004820152306024820152606087013560448201526001600160a01b038216906323b872dd90606401600060405180830381600087803b15801561286a57600080fd5b505af115801561287e573d6000803e3d6000fd5b50506040516331a9108f60e11b8152606089013560048201523092506001600160a01b0384169150636352211e90602401602060405180830381865afa1580156128cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128f0919061561a565b6001600160a01b0316146129525760405162461bcd60e51b8152602060048201526024808201527f5261696c67756e4c6f6769633a20455243373231206469646e2774207472616e60448201526339b332b960e11b60648201526084016107f4565b506129b0565b60405162461bcd60e51b815260206004820152602760248201527f5261696c67756e4c6f6769633a2045524331313535206e6f74207965742073756044820152661c1c1bdc9d195960ca1b60648201526084016107f4565b9094909350915050565b805160008190036129c9575050565b6129d560106002614af1565b816066546129e39190614886565b11156129f1576129f1613510565b606680549082906000612a048385614886565b909155506000905080805b6010811015612bfe57600184901c91506000612a2c600286614e87565b600103612ab257612a4183600187901c614c16565b9350612a7a607a8360108110612a5957612a5961477f565b0154888381518110612a6d57612a6d61477f565b6020026020010151610f6e565b878581518110612a8c57612a8c61477f565b6020908102919091010152612aa2600182614886565b9050612aaf600186614886565b94505b85811015612bd8576000612ac7600188614c16565b821015612af95787612ada836001614886565b81518110612aea57612aea61477f565b60200260200101519050612b11565b606a8360108110612b0c57612b0c61477f565b015490505b612b1c600188614c16565b821480612b325750612b2f600288614c16565b82145b15612b6757878281518110612b4957612b4961477f565b6020026020010151607a8460108110612b6457612b6461477f565b01555b612b7584600188901c614c16565b9450612b9a888381518110612b8c57612b8c61477f565b602002602001015182610f6e565b888681518110612bac57612bac61477f565b6020908102919091010152612bc2600287614886565b9550612bd19050600282614886565b9050612ab2565b829450836001612be89190614886565b9550612bf79050600182614886565b9050612a0f565b5084600081518110612c1257612c1261477f565b60209081029190910181015160678190556069546000908152608a83526040808220928252919092529020805460ff191660011790555050505050565b6040805180820190915260008082526020820181905290815b8351811015612d5a576000805160206158a3833981519152848281518110612c9257612c9261477f565b602002602001015110612cf15760405162461bcd60e51b815260206004820152602160248201527f536e61726b3a20496e707574203e20534e41524b5f5343414c41525f4649454c6044820152601160fa1b60648201526084016107f4565b612d4682612d418860a00151846001612d0a9190614886565b81518110612d1a57612d1a61477f565b6020026020010151878581518110612d3457612d3461477f565b6020026020010151613537565b6135db565b9150612d53600182614886565b9050612c68565b50612d83818660a00151600081518110612d7657612d7661477f565b60200260200101516135db565b9050612db9612d95856000015161367b565b856020015187602001518860400151858a606001518a604001518c608001516137bd565b95945050505050565b6033546001600160a01b031633146112c65760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016107f4565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600054610100900460ff16612e955760405162461bcd60e51b81526004016107f490615637565b6112c6613939565b600054610100900460ff16612ec45760405162461bcd60e51b81526004016107f490615637565b612efc6000805160206158a38339815191527f95b5e3f3c951508f13e7771152f5b7c7329a294917685c10a571c9247e3b9fff614e87565b606a556000612f396000805160206158a38339815191527f95b5e3f3c951508f13e7771152f5b7c7329a294917685c10a571c9247e3b9fff614e87565b905060005b6010811015612f915781606a8260108110612f5b57612f5b61477f565b015581607a8260108110612f7157612f7161477f565b0155612f7d8280610f6e565b9150612f8a600182614886565b9050612f3e565b50606781905560688190556069546000908152608a6020908152604080832093835292905220805460ff19166001179055565b6000805b612fd6610120870187614bcd565b905081101561312f5760656000612ff1610160890189614afd565b612fff906020810190614b95565b61ffff1681526020019081526020016000206000878061012001906130249190614bcd565b848181106130345761303461477f565b602090810292909201358352508101919091526040016000205460ff161561309e5760405162461bcd60e51b815260206004820181905260248201527f5261696c67756e4c6f6769633a204e6f746520616c7265616479207370656e7460448201526064016107f4565b6001606560006130b26101608a018a614afd565b6130c0906020810190614b95565b61ffff1681526020019081526020016000206000888061012001906130e59190614bcd565b858181106130f5576130f561477f565b60209081029290920135835250810191909152604001600020805460ff1916911515919091179055613128600182614886565b9050612fc8565b507f781745c57906dc2f175fec80a9c691744c91c48a34a83672c41c2604774eb11f61315f610160870187614afd565b61316d906020810190614b95565b61317b610120880188614bcd565b60405161318a93929190615682565b60405180910390a160005b6131a3610160870187614afd565b6131b19060c0810190614bcd565b9050811015613286576131c8610140870187614bcd565b828181106131d8576131d861477f565b905060200201358582866131ec9190614886565b815181106131fc576131fc61477f565b6020908102919091010152613215610160870187614afd565b6132239060c0810190614bcd565b828181106132335761323361477f565b905060200281019061324591906156c7565b61324e906156dd565b836132598387614886565b815181106132695761326961477f565b602090810291909101015261327f600182614886565b9050613195565b50613295610160860186614afd565b6132a39060c0810190614bcd565b612db9915084614886565b60006132c06040830160208401614bb0565b60028111156132d1576132d16146e5565b036133ad5760006132e86060830160408401614648565b905060008061331f61330060a0860160808701614c29565b60fa546001600160781b0391821691600191600160781b900416611000565b90925090506133426001600160a01b03841685356001600160781b038516613969565b60f954613365906001600160a01b0385811691166001600160781b038416613969565b6040517fd93cf895c7d5b2cd7dc7a098b678b3089f37d91f48d9b83a0800a91cbdf052849061339f908635906020880190869086906157e5565b60405180910390a150505050565b60016133bf6040830160208401614bb0565b60028111156133d0576133d06146e5565b036129585760006133e76060830160408401614648565b6040516323b872dd60e01b81523060048201526001600160a01b0384358116602483015260608501356044830152919250908216906323b872dd90606401600060405180830381600087803b15801561343f57600080fd5b505af1158015613453573d6000803e3d6000fd5b50506040517fd93cf895c7d5b2cd7dc7a098b678b3089f37d91f48d9b83a0800a91cbdf05284925061349391508435906020860190600190600090615824565b60405180910390a15050565b6040516001600160a01b038085166024830152831660448201526064810182905261350a9085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613999565b50505050565b606854606755600060668190556069805460019290613530908490614886565b9091555050565b6040805180820190915260008082526020820152613553613c74565b835181526020808501519082015260408082018490526000908360608460076107d05a03fa9050806135d35760405162461bcd60e51b815260206004820152602360248201527f536e61726b3a205363616c6172204d756c7469706c69636174696f6e204661696044820152621b195960ea1b60648201526084016107f4565b505092915050565b60408051808201909152600080825260208201526135f7613c92565b83518152602080850151818301528351604080840191909152848201516060840152805180820182526000808252928101839052908160808560066107d05a03fa915081612db95760405162461bcd60e51b815260206004820152601160248201527014db985c9ace881059190811985a5b1959607a1b60448201526064016107f4565b604080518082019091526000808252602082015281511580156136a057506020820151155b156136be575050604080518082019091526000808252602082015290565b600060008051602061588383398151915283518009905060008051602061588383398151915283518209905060008051602061588383398151915260038208905060006000805160206158838339815191526020850151800990508181146137685760405162461bcd60e51b815260206004820152601760248201527f536e61726b3a20496e76616c6964206e65676174696f6e00000000000000000060448201526064016107f4565b604051806040016040528085600001518152602001600080516020615883833981519152866020015161379b9190614e87565b6137b390600080516020615883833981519152614c16565b9052949350505050565b6040805161030081018252895181526020808b015181830152895151928201929092528851820151606082015288820180515160808301525182015160a0820152875160c08201528782015160e08201528651516101008201528651820151610120820152868201805151610140830152518201516101608201528551610180820152858201516101a08201528451516101c082015284518201516101e08201528482018051516102008301525182015161022082015283516102408201528382015161026082015282515161028082015282518201516102a08201528282018051516102c083015251909101516102e08201526000906138bc613cb0565b60006020826103008560086107d05a03fa9050806139275760405162461bcd60e51b815260206004820152602260248201527f536e61726b3a2050616972696e6720566572696669636174696f6e204661696c604482015261195960f21b60648201526084016107f4565b505115159a9950505050505050505050565b600054610100900460ff166139605760405162461bcd60e51b81526004016107f490615637565b6112c633612e1c565b6040516001600160a01b03831660248201526044810182905261198490849063a9059cbb60e01b906064016134d3565b60006139ee826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613a6e9092919063ffffffff16565b9050805160001480613a0f575080806020019051810190613a0f9190615853565b6119845760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016107f4565b6060610ef6848460008585600080866001600160a01b03168587604051613a959190615870565b60006040518083038185875af1925050503d8060008114613ad2576040519150601f19603f3d011682016040523d82523d6000602084013e613ad7565b606091505b5091509150613ae887838387613af3565b979650505050505050565b60608315613b62578251600003613b5b576001600160a01b0385163b613b5b5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016107f4565b5081610ef6565b610ef68383815115613b775781518083602001fd5b8060405162461bcd60e51b81526004016107f491906147d1565b604080516060808201835260008083528351918201845280825260208281018290529382015290918201905b8152600060209091015290565b6040518060400160405280613bbd613c74565b6040518060c0016040528060608152602001613c0c604051806040016040528060008152602001600081525090565b8152602001613c19613cce565b8152602001613c26613cce565b8152602001613c33613cce565b8152602001606081525090565b6040518060a00160405280613c53613c92565b81526000602082018190526040820152606080820181905260809091015290565b60405180606001604052806003906020820280368337509192915050565b60405180608001604052806004906020820280368337509192915050565b60405180602001604052806001906020820280368337509192915050565b6040518060400160405280613ce1613cf3565b8152602001613cee613cf3565b905290565b60405180604001604052806002906020820280368337509192915050565b60008060208385031215613d2457600080fd5b82356001600160401b0380821115613d3b57600080fd5b818501915085601f830112613d4f57600080fd5b813581811115613d5e57600080fd5b86602061012083028501011115613d7457600080fd5b60209290920196919550909350505050565b600060208284031215613d9857600080fd5b5035919050565b600060208284031215613db157600080fd5b81356001600160401b03811115613dc757600080fd5b82016102208185031215610ff957600080fd5b60005b83811015613df5578181015183820152602001613ddd565b50506000910152565b60008151808452613e16816020860160208601613dda565b601f01601f19169290920160200192915050565b8215158152604060208201526000610ef66040830184613dfe565b600060208284031215613e5757600080fd5b81356001600160401b03811115613e6d57600080fd5b820160e08185031215610ff957600080fd5b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b0381118282101715613eb757613eb7613e7f565b60405290565b60405160c081016001600160401b0381118282101715613eb757613eb7613e7f565b604051606081016001600160401b0381118282101715613eb757613eb7613e7f565b60405160a081016001600160401b0381118282101715613eb757613eb7613e7f565b604051608081016001600160401b0381118282101715613eb757613eb7613e7f565b604051601f8201601f191681016001600160401b0381118282101715613f6d57613f6d613e7f565b604052919050565b600082601f830112613f8657600080fd5b81356001600160401b03811115613f9f57613f9f613e7f565b613fb2601f8201601f1916602001613f45565b818152846020838601011115613fc757600080fd5b816020850160208301376000918101602001919091529392505050565b600060408284031215613ff657600080fd5b613ffe613e95565b9050813581526020820135602082015292915050565b600082601f83011261402557600080fd5b604051604081018181106001600160401b038211171561404757614047613e7f565b806040525080604084018581111561405e57600080fd5b845b81811015614078578035835260209283019201614060565b509195945050505050565b60006080828403121561409557600080fd5b61409d613e95565b90506140a98383614014565b81526140b88360408401614014565b602082015292915050565b60006001600160401b038211156140dc576140dc613e7f565b5060051b60200190565b600082601f8301126140f757600080fd5b8135602061410c614107836140c3565b613f45565b82815260069290921b8401810191818101908684111561412b57600080fd5b8286015b8481101561414f576141418882613fe4565b83529183019160400161412f565b509695505050505050565b6000610100828403121561416d57600080fd5b50919050565b600082601f83011261418457600080fd5b81356020614194614107836140c3565b82815260059290921b840181019181810190868411156141b357600080fd5b8286015b8481101561414f57803583529183019183016141b7565b600080600061014084860312156141e457600080fd5b83356001600160401b03808211156141fb57600080fd5b90850190610200828803121561421057600080fd5b614218613ebd565b82358281111561422757600080fd5b61423389828601613f75565b8252506142438860208501613fe4565b60208201526142558860608501614083565b60408201526142678860e08501614083565b606082015261427a886101608501614083565b60808201526101e08301358281111561429257600080fd5b61429e898286016140e6565b60a08301525094506142b3876020880161415a565b93506101208601359150808211156142ca57600080fd5b506142d786828701614173565b9150509250925092565b6000806000606084860312156142f657600080fd5b833592506020840135915060408401356001600160401b0381111561431a57600080fd5b8401610200818703121561432d57600080fd5b809150509250925092565b6000806040838503121561434b57600080fd5b50508035926020909101359150565b80151581146109de57600080fd5b80356001600160781b038116811461437f57600080fd5b919050565b60008060006060848603121561439957600080fd5b83356001600160881b03811681146143b057600080fd5b925060208401356143c08161435a565b91506143ce60408501614368565b90509250925092565b60008083601f8401126143e957600080fd5b5081356001600160401b0381111561440057600080fd5b6020830191508360208260051b850101111561441b57600080fd5b9250929050565b6000806020838503121561443557600080fd5b82356001600160401b0381111561444b57600080fd5b611085858286016143d7565b600060a0828403121561416d57600080fd5b600381106109de57600080fd5b6001600160a01b03811681146109de57600080fd5b803561437f81614476565b6000606082840312156144a857600080fd5b6144b0613edf565b905081356144bd81614469565b815260208201356144cd81614476565b806020830152506040820135604082015292915050565b600060a082840312156144f657600080fd5b6144fe613edf565b8235815261450f8460208501614496565b602082015261452060808401614368565b60408201529392505050565b8060005b600281101561350a578151845260209384019390910190600101614530565b61455a82825161452c565b6020810151611984604084018261452c565b600060208083528351610200808386015261458b610220860183613dfe565b91508286015160406145a98188018380518252602090810151910152565b8088015191506145bc608088018361454f565b606088015191506145d161010088018361454f565b608088015191506145e661018088018361454f565b60a0880151878503601f190193880193909352825180855292850193850192600092505b8083101561463b5761462784865180518252602090810151910152565b93850193928101926001929092019161460a565b5091979650505050505050565b60006020828403121561465a57600080fd5b8135610ff981614476565b60006060828403121561467757600080fd5b610ff98383614496565b600080600080600060a0868803121561469957600080fd5b85356146a481614476565b94506146b260208701614368565b93506146c060408701614368565b92506060860135915060808601356146d781614476565b809150509295509295909350565b634e487b7160e01b600052602160045260246000fd5b600381106109de57634e487b7160e01b600052602160045260246000fd5b60608101614726856146fb565b9381526001600160a01b0392909216602083015260409091015290565b60008060006060848603121561475857600080fd5b61476184614368565b925061476f60208501614368565b9150604084013590509250925092565b634e487b7160e01b600052603260045260246000fd5b7302930b4b633bab729b6b0b93a2bb0b63632ba1d160651b8152600082516147c4816014850160208701613dda565b9190910160140192915050565b602081526000610ff96020830184613dfe565b6000608082840312156147f657600080fd5b6147fe613e95565b83601f84011261480d57600080fd5b604051606081018181106001600160401b038211171561482f5761482f613e7f565b60405280606085018681111561484457600080fd5b855b8181101561485e578035835260209283019201614846565b50918352503560208201529392505050565b634e487b7160e01b600052601160045260246000fd5b80820180821115610ed557610ed5614870565b80516148a4816146fb565b82526020818101516001600160a01b031690830152604090810151910152565b60008151808452602080850194508084016000805b84811015614922578251805189845b6003811015614905578251825291870191908701906001016148e8565b5050508401516060890152608090970196918301916001016148d9565b50959695505050505050565b600081518084526020808501945080840160005b8381101561495e57815187529582019590820190600101614942565b509495945050505050565b600060a0808301888452602088818601526040838187015282895180855260c088019150838b01945060005b818110156149d557855180518452858101516149b387860182614899565b508401516001600160781b031660808401529484019491860191600101614995565b505086810360608801526149e9818a6148c4565b9450505050508281036080840152614a01818561492e565b98975050505050505050565b600181815b80851115614a48578160001904821115614a2e57614a2e614870565b80851615614a3b57918102915b93841c9390800290614a12565b509250929050565b600082614a5f57506001610ed5565b81614a6c57506000610ed5565b8160018114614a825760028114614a8c57614aa8565b6001915050610ed5565b60ff841115614a9d57614a9d614870565b50506001821b610ed5565b5060208310610133831016604e8410600b8410161715614acb575081810a610ed5565b614ad58383614a0d565b8060001904821115614ae957614ae9614870565b029392505050565b6000610ff98383614a50565b6000823560de19833603018112614b1357600080fd5b9190910192915050565b803568ffffffffffffffffff8116811461437f57600080fd5b600060208284031215614b4857600080fd5b610ff982614b1d565b80356001600160401b038116811461437f57600080fd5b600060208284031215614b7a57600080fd5b610ff982614b51565b803561ffff8116811461437f57600080fd5b600060208284031215614ba757600080fd5b610ff982614b83565b600060208284031215614bc257600080fd5b8135610ff981614469565b6000808335601e19843603018112614be457600080fd5b8301803591506001600160401b03821115614bfe57600080fd5b6020019150600581901b360382131561441b57600080fd5b81810381811115610ed557610ed5614870565b600060208284031215614c3b57600080fd5b610ff982614368565b6000808335601e19843603018112614c5b57600080fd5b83016020810192503590506001600160401b03811115614c7a57600080fd5b8060051b360382131561441b57600080fd5b6000808335601e19843603018112614ca357600080fd5b83016020810192503590506001600160401b03811115614cc257600080fd5b80360382131561441b57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b81835260006020808501808196508560051b81019150846000805b88811015614daf578385038a52823560fe19893603018112614d35578283fd5b8801610100608080838937828101359088015260a0808301359088015260c0614d6081840184614c8c565b83838b0152614d72848b018284614cd1565b935050505060e0614d8581840184614c8c565b9350888303828a0152614d99838583614cd1565b9d8a019d98505050938701935050600101614d15565b509298975050505050505050565b6020815261ffff614dcd83614b83565b16602082015268ffffffffffffffffff614de960208401614b1d565b16604082015260006040830135614dff81614469565b614e08816146fb565b806060840152506001600160401b03614e2360608501614b51565b166080830152614e356080840161448b565b6001600160a01b03811660a08401525060a083013560c0830152614e5c60c0840184614c44565b60e080850152612db961010085018284614cfa565b634e487b7160e01b600052601260045260246000fd5b600082614e9657614e96614e71565b500690565b60006101008284031215614eae57600080fd5b614eb6613edf565b614ec08484613fe4565b8152614ecf8460408501614083565b60208201526145208460c08501613fe4565b600181811c90821680614ef557607f821691505b60208210810361416d57634e487b7160e01b600052602260045260246000fd5b601f82111561198457600081815260208120601f850160051c81016020861015614f3c5750805b601f850160051c820191505b8181101561186157828155600101614f48565b6001600160401b03831115614f7257614f72613e7f565b614f8683614f808354614ee1565b83614f15565b6000601f841160018114614fba5760008515614fa25750838201355b600019600387901b1c1916600186901b178355615014565b600083815260209020601f19861690835b82811015614feb5786850135825560209485019460019092019101614fcb565b50868210156150085760001960f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b8160005b600281101561503c5781358382015560209091019060010161501f565b50506040820160005b6002808210615054575061350a565b823584830190910155602090910190600101615045565b6000808335601e1984360301811261508257600080fd5b8301803591506001600160401b0382111561509c57600080fd5b6020019150600681901b360382131561441b57600080fd5b680100000000000000008311156150cd576150cd613e7f565b80548382558084101561513b5760016001600160ff1b0382811683146150f5576150f5614870565b808616861461510657615106614870565b5060008381526020812086831b81019084841b015b8082101561513657828255828483015560028201915061511b565b505050505b5060008181526020812083915b858110156118615782358255602083013560018301556040929092019160029190910190600101615148565b8135601e1983360301811261518857600080fd5b820180356001600160401b038111156151a057600080fd5b6020820191508036038213156151b557600080fd5b6151c0818385614f5b565b505060208201356001820155604082013560028201556151e6606083016003830161501b565b6151f660e083016007830161501b565b6152076101608301600b830161501b565b6152156101e083018361506b565b61350a8183600f86016150b4565b6040818337604080820160408401375050565b81835260208301925060008160005b8481101561526d57813586526020808301359087015260409586019590910190600101615245565b5093949350505050565b8381528260208201526060604082015260006152938384614c8c565b61020060608501526152aa61026085018284614cd1565b60208601356080860152604086013560a086015291506152c79050565b6152d760c0840160608601615223565b6152e8610140840160e08601615223565b6152fa6101c084016101608601615223565b6101e0840135601e1985360301811261531257600080fd5b84016020810190356001600160401b0381111561532e57600080fd5b8060061b360382131561534057600080fd5b848303605f1901610240860152614a01838284615236565b60408101818360005b6002811015615380578151835260209283019290910190600101615361565b50505092915050565b60006020828403121561539b57600080fd5b5051919050565b6001600160881b0382811682821681810283169291811582850482141761538057615380614870565b60006001600160881b03808416806153e5576153e5614e71565b92169190910492915050565b6001600160881b0382811682821603908082111561541157615411614870565b5092915050565b6001600160781b0382811682821603908082111561541157615411614870565b6000823561021e19833603018112614b1357600080fd5b60608101818360005b6003811015615380578151835260209283019290910190600101615458565b60608101610ed58284614899565b60006001600160781b03808416806153e5576153e5614e71565b60006080808301878452602087818601526040838187015282885180855260a094508488019150838a0160005b828110156154e8578151845292850192908501906001016154cc565b5050506060878203818901528189518084528584019150858160051b850101868c0160005b8381101561559f57868303601f1901855281518051610100908560005b60048110156155475782518252918d0191908d019060010161552a565b5050508a8201518d860152898201518c860152878201518160c087015261557082870182613dfe565b9150508c820151915084810360e086015261558b8183613dfe565b968b0196945050509088019060010161550d565b50909f9e505050505050505050505050505050565b81356155bf81614469565b6155c8816146fb565b815460ff821691508160ff19821617835560208401356155e781614476565b6001600160a81b03199190911690911760089190911b610100600160a81b03161781556040919091013560019190910155565b60006020828403121561562c57600080fd5b8151610ff981614476565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b61ffff84168152604060208201819052810182905260006001600160fb1b038311156156ad57600080fd5b8260051b8085606085013791909101606001949350505050565b6000823560fe19833603018112614b1357600080fd5b600061010082360312156156f057600080fd5b6156f8613f01565b36601f84011261570757600080fd5b61570f613f23565b80608085013681111561572157600080fd5b855b8181101561573b578035845260209384019301615723565b509083523560208301525060a0830135604082015260c08301356001600160401b038082111561576a57600080fd5b61577636838701613f75565b606084015260e085013591508082111561578f57600080fd5b5061579c36828601613f75565b60808301525092915050565b80356157b381614469565b6157bc816146fb565b825260208101356157cc81614476565b6001600160a01b03166020830152604090810135910152565b6001600160a01b038516815260c0810161580260208301866157a8565b6001600160781b03808516608084015280841660a08401525095945050505050565b6001600160a01b038516815260c0810161584160208301866157a8565b608082019390935260a0015292915050565b60006020828403121561586557600080fd5b8151610ff98161435a565b60008251614b13818460208701613dda56fe30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4730644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001496e76616c6964204e6f74652043697068657274657874204172726179204c656e677468a2646970667358221220a5ee7ede47178b4fd124de3243b5aa8278b46859f95772125ddc44dca47b6fdb64736f6c63430008110033
Deployed ByteCode
0x608060405234801561001057600080fd5b50600436106102535760003560e01c806371c5345511610146578063c718dbda116100c3578063e829558811610087578063e8295588146105c5578063ec732959146105d8578063ee990783146105e0578063f19ea903146105f3578063f2fde38b14610621578063f71a55f81461063457600080fd5b8063c718dbda14610555578063cc1f73fd14610583578063cd3efe0614610596578063d8ae136a1461059f578063dc7c9a1f146105b257600080fd5b806392d3c7a41161010a57806392d3c7a4146104bf578063a33952a9146104d2578063ab63e69c146104e5578063b14f2a39146104f8578063b8ceaef21461050b57600080fd5b806371c534551461044f5780637b12ae8314610462578063815b534f146104825780638da5cb5b146104a55780639071adc0146104b657600080fd5b80632ebfe12a116101d4578063617e1b1711610198578063617e1b17146103ed57806361d027b314610400578063665033151461042b5780636cefafaa14610434578063715018a61461044757600080fd5b80632ebfe12a1461036e5780632ec0f3591461038157806338bf282e1461039457806343c88730146103a757806359bb6199146103da57600080fd5b80630c9c0c8d1161021b5780630c9c0c8d146102d6578063129458f4146102fe578063174768911461033157806328f89c3a146103525780632eb4a7ab1461036557600080fd5b806301c3779014610258578063044a40c314610288578063053ed12a1461029d5780630804a4ee146102b75780630be4f422146102bf575b600080fd5b60fa5461026b906001600160781b031681565b6040516001600160781b0390911681526020015b60405180910390f35b61029b610296366004613d11565b610647565b005b60fa5461026b90600160781b90046001600160781b031681565b61029b610955565b6102c860665481565b60405190815260200161027f565b6102e96102e4366004613d86565b6109e1565b6040805192835260208301919091520161027f565b61032161030c366004613d86565b60fc6020526000908152604090205460ff1681565b604051901515815260200161027f565b61034461033f366004613d9f565b610a2b565b60405161027f929190613e2a565b6102c8610360366004613e45565b610e90565b6102c860675481565b61032161037c3660046141ce565b610edb565b61029b61038f3660046142e1565b610efe565b6102c86103a2366004614338565b610f6e565b6103ba6103b5366004614384565b611000565b604080516001600160781b0393841681529290911660208301520161027f565b6102c86103e8366004614422565b611091565b6103446103fb366004614457565b611106565b60f954610413906001600160a01b031681565b6040516001600160a01b03909116815260200161027f565b6102c860695481565b61029b610442366004613d86565b611291565b61029b6112b4565b6102c861045d3660046144e4565b6112c8565b610475610470366004614338565b61137c565b60405161027f919061456c565b610321610490366004614648565b60956020526000908152604090205460ff1681565b6033546001600160a01b0316610413565b6102c860fb5481565b6102c86104cd366004614665565b611641565b61029b6104e0366004614681565b61168d565b61029b6104f3366004614422565b611869565b61029b610506366004614648565b611989565b610546610519366004613d86565b60fd602052600090815260409020805460019091015460ff82169161010090046001600160a01b03169083565b60405161027f93929190614719565b610321610563366004614338565b608a60209081526000928352604080842090915290825290205460ff1681565b61029b610591366004614743565b6119fa565b6102c860fe5481565b61029b6105ad366004614422565b611bad565b61029b6105c0366004613d86565b611e31565b6102c86105d3366004613d86565b611e51565b6102c8611e68565b6103216105ee366004613d9f565b611ea3565b610321610601366004614338565b606560209081526000928352604080842090915290825290205460ff1681565b61029b61062f366004614648565b61239a565b61029b610642366004614422565b612410565b6000816001600160401b0381111561066157610661613e7f565b60405190808252806020026020018201604052801561068a578160200160208202803683370190505b5090506000826001600160401b038111156106a7576106a7613e7f565b6040519080825280602002602001820160405280156106e057816020015b6106cd613b91565b8152602001906001900390816106c55790505b5090506000836001600160401b038111156106fd576106fd613e7f565b60405190808252806020026020018201604052801561073657816020015b610723613bca565b81526020019060019003908161071b5790505b5090506000846001600160401b0381111561075357610753613e7f565b60405190808252806020026020018201604052801561077c578160200160208202803683370190505b50905060005b858110156108fc576000806107b18989858181106107a2576107a261477f565b90506101200201600001611106565b9150915081816040516020016107c79190614795565b604051602081830303815290604052906107fd5760405162461bcd60e51b81526004016107f491906147d1565b60405180910390fd5b506108228989858181106108135761081361477f565b90506101200201600001612533565b8785815181106108345761083461477f565b6020026020010186868151811061084d5761084d61477f565b6020026020010182815250829052505061087f8684815181106108725761087261477f565b60200260200101516112c8565b8784815181106108915761089161477f565b6020026020010181815250508888848181106108af576108af61477f565b9050610120020160a0018036038101906108c991906147e4565b8584815181106108db576108db61477f565b602002602001018190525050506001816108f59190614886565b9050610782565b507f3a5b9dc26075a3801a6ddccf95fec485bb7500a91b44cec1add984c21ee6db3b606954606654858585604051610938959493929190614969565b60405180910390a1610949846129ba565b50504360fe5550505050565b7f8dea8703c3cf94703383ce38a9c894669dccd4ca8e65ddb43267aa0248711450805460ff1916600117905533600090815260fc6020526040902054806109de5760405162461bcd60e51b815260206004820152601c60248201527f5261696c67756e4c6f6769633a20556e7361666520766563746f72730000000060448201526064016107f4565b50565b6000806109f060106002614af1565b836066546109fe9190614886565b1115610a1c57606954610a12906001614886565b9360009350915050565b60695460665491509150915091565b60006060610a3d610160840184614afd565b610a4e906040810190602001614b36565b68ffffffffffffffffff163a1015610a9257505060408051808201909152601181527047617320707269636520746f6f206c6f7760781b6020820152600092909150565b6000610aa2610160850185614afd565b610ab39060a0810190608001614648565b6001600160a01b031614158015610af1575033610ad4610160850185614afd565b610ae59060a0810190608001614648565b6001600160a01b031614155b15610b375760006040518060400160405280602081526020017f496e76616c696420416461707420436f6e74726163742061732053656e64657281525091509150915091565b46610b46610160850185614afd565b610b57906080810190606001614b68565b6001600160401b031614610b9657505060408051808201909152601081526f086d0c2d2dc928840dad2e6dac2e8c6d60831b6020820152600092909150565b608a6000610ba8610160860186614afd565b610bb6906020810190614b95565b61ffff16815260208082019290925260409081016000908120610100870135825290925290205460ff16610c18575050604080518082019091526013815272125b9d985b1a590813595c9adb1948149bdbdd606a1b6020820152600092909150565b6000610c28610160850185614afd565b610c39906060810190604001614bb0565b6002811115610c4a57610c4a6146e5565b14610ddf576001610c5f610140850185614bcd565b610c6a929150614c16565b610c78610160850185614afd565b610c869060c0810190614bcd565b905014610cb15760006040518060600160405280602481526020016158c36024913991509150915091565b60006002610cc3610160860186614afd565b610cd4906060810190604001614bb0565b6002811115610ce557610ce56146e5565b03610d41576040805160608101909152338152610d3a9060208101610d133688900388016101a08901614665565b8152602001610d2a61022088016102008901614c29565b6001600160781b031690526112c8565b9050610d5a565b610d5761045d36869003860161018087016144e4565b90505b610d68610140850185614bcd565b6001610d78610140880188614bcd565b610d83929150614c16565b818110610d9257610d9261477f565b905060200201358114610dd957600060405180604001604052806015815260200174496e76616c6964205769746864726177204e6f746560581b8152509250925050915091565b50610e36565b610ded610140840184614bcd565b9050610dfd610160850185614afd565b610e0b9060c0810190614bcd565b905014610e365760006040518060600160405280602481526020016158c36024913991509150915091565b610e3f83611ea3565b610e7757505060408051808201909152601381527224b73b30b634b21029b730b93590283937b7b360691b6020820152600092909150565b5050604080516020810190915260008152600192909150565b60006000805160206158a383398151915282604051602001610eb29190614dbd565b6040516020818303038152906040528051906020012060001c610ed59190614e87565b92915050565b6000610ef684610ef036869003860186614e9b565b84612c4f565b949350505050565b610f06612dc2565b600083815260c76020908152604080832085845290915290208190610f2b8282615174565b9050507f3d09e10d1c966d01c4a2c14d0ac9af253486aa5e99b6cffe9019c4d43eb1fb23838383604051610f6193929190615277565b60405180910390a1505050565b60408051808201825283815260208101839052905163014cf2b360e51b81526000917311cb5badfa4bbf360661602a02227fb5fd315ec99163299e566091610fb891600401615358565b602060405180830381865af4158015610fd5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ff99190615389565b9392505050565b6000806000808515611049576127106110226001600160781b038716896153a2565b61102c91906153cb565b61103690886153f1565b915061104282886153f1565b9050611085565b8691508161105986612710615418565b6001600160781b031661106e846127106153a2565b61107891906153cb565b61108291906153f1565b90505b90969095509350505050565b600080805b838110156110fe578484828181106110b0576110b061477f565b90506020028101906110c29190615438565b6110d190610160810190614afd565b6110df9060c0810190614bcd565b6110ea915083614886565b91506110f7600182614886565b9050611096565b509392505050565b6000606061111a60a0840160808501614c29565b6001600160781b031660000361115d575050604080518082019091526012815271496e76616c6964204e6f74652056616c756560701b6020820152600092909150565b609560006111716060860160408701614648565b6001600160a01b0316815260208101919091526040016000205460ff16156111c55750506040805180820190915260118152702ab739bab83837b93a32b2102a37b5b2b760791b6020820152600092909150565b6000805160206158a383398151915283351061120c57505060408051808201909152601081526f496e76616c6964204e6f7465204e504b60801b6020820152600092909150565b600161121e6040850160208601614bb0565b600281111561122f5761122f6146e5565b148015611255575061124760a0840160808501614c29565b6001600160781b0316600114155b15610e77575050604080518082019091526016815275496e76616c6964204e4654204e6f74652056616c756560501b6020820152600092909150565b611299612dc2565b600090815260fc60205260409020805460ff19166001179055565b6112bc612dc2565b6112c66000612e1c565b565b6000732428d0ce4d50be67a7c461fe363faa2e66332b95635a53025d6040518060600160405280856000015181526020016113068660200151611641565b815260200185604001516001600160781b031660001b8152506040518263ffffffff1660e01b815260040161133b919061544f565b602060405180830381865af4158015611358573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ed59190615389565b611384613bdd565b600083815260c76020908152604080832085845290915290819020815160c081019092528054829082906113b790614ee1565b80601f01602080910402602001604051908101604052809291908181526020018280546113e390614ee1565b80156114305780601f1061140557610100808354040283529160200191611430565b820191906000526020600020905b81548152906001019060200180831161141357829003601f168201915b505050918352505060408051808201825260018401548152600280850154602080840191909152840191909152815160808101808452938301939092600386019284929183019184919082845b81548152602001906001019080831161147d57505050918352505060408051808201918290526020909201919060028481019182845b8154815260200190600101908083116114b3575050509190925250505081526040805160808101808352602090930192909160078501918391820190839060029082845b8154815260200190600101908083116114f757505050918352505060408051808201918290526020909201919060028481019182845b81548152602001906001019080831161152d5750505091909252505050815260408051608081018083526020909301929091600b8501918391820190839060029082845b81548152602001906001019080831161157157505050918352505060408051808201918290526020909201919060028481019182845b8154815260200190600101908083116115a7575050505050815250508152602001600f8201805480602002602001604051908101604052809291908181526020016000905b82821015611632578382906000526020600020906002020160405180604001604052908160008201548152602001600182015481525050815260200190600101906115ec565b50505091525090949350505050565b60008082516002811115611657576116576146e5565b0361166d5750602001516001600160a01b031690565b6000805160206158a383398151915282604051602001610eb29190615477565b600054610100900460ff16158080156116ad5750600054600160ff909116105b806116c75750303b1580156116c7575060005460ff166001145b61172a5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084016107f4565b6000805460ff19166001179055801561174d576000805461ff0019166101001790555b611755612e6e565b61175d612e9d565b61176686611989565b6117718585856119fa565b61177a8261239a565b60fc6020527f18a261e1efd282dd636becf59b85995514ab34d1c1c9a3e1128aac786b5472de8054600160ff1991821681179092557f35cc25be27a7e44e9e6eb23f103c8980fe6d01e7b6e981c2c07b23911ac79e8b805482168317905573cc29dbf69896278a0c6b886ef8cc30a56fa6164a6000527f7e1bfa3bd01233beed03b737790a265ae14e11664e22f82316c6f65a13e15c8c805490911690911790558015611861576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050505050565b611871612dc2565b60005b8181101561198457609560008484848181106118925761189261477f565b90506020020160208101906118a79190614648565b6001600160a01b0316815260208101919091526040016000205460ff161561197257609560008484848181106118df576118df61477f565b90506020020160208101906118f49190614648565b6001600160a01b031681526020810191909152604001600020805460ff191690558282828181106119275761192761477f565b905060200201602081019061193c9190614648565b6001600160a01b03167f2ef13bd1aff17b0f9c85afaf228e84266c8394d9d381735e83fe23f607113e2e60405160405180910390a25b61197d600182614886565b9050611874565b505050565b611991612dc2565b60f9546001600160a01b038281169116146109de5760f980546001600160a01b0319166001600160a01b0383169081179091556040519081527f1ac54af2084ba0e4b86b7bc54b978ce7dd4ca046d3dec89d7c8963873820c0bb9060200160405180910390a150565b611a02612dc2565b60fa546001600160781b038481169116141580611a34575060fa546001600160781b03838116600160781b9092041614155b80611a41575060fb548114155b1561198457611a536002612710615485565b6001600160781b0316836001600160781b03161115611ac05760405162461bcd60e51b8152602060048201526024808201527f5261696c67756e4c6f6769633a20536869656c642046656520657863656564736044820152632035302560e01b60648201526084016107f4565b611acd6002612710615485565b6001600160781b0316826001600160781b03161115611b3d5760405162461bcd60e51b815260206004820152602660248201527f5261696c67756e4c6f6769633a20556e736869656c642046656520657863656560448201526564732035302560d01b60648201526084016107f4565b60fa80546001600160781b038581166001600160f01b03199092168217600160781b9186169182021790925560fb8390556040805191825260208201929092529081018290527f472cfc031d19bcc54db01976ce486cc12dc3d489e6adced1eb5a782cd55cfdf290606001610f61565b6000611bb98383611091565b90506000816001600160401b03811115611bd557611bd5613e7f565b604051908082528060200260200182016040528015611bfe578160200160208202803683370190505b509050600080836001600160401b03811115611c1c57611c1c613e7f565b604051908082528060200260200182016040528015611c5557816020015b611c42613c40565b815260200190600190039081611c3a5790505b50905060005b85811015611d1857600080611c8d898985818110611c7b57611c7b61477f565b905060200281019061033f9190615438565b915091508181604051602001611ca39190614795565b60405160208183030381529060405290611cd05760405162461bcd60e51b81526004016107f491906147d1565b50611d00898985818110611ce657611ce661477f565b9050602002810190611cf89190615438565b878787612fc4565b94505050600181611d119190614886565b9050611c5b565b5060005b85811015611dc2576000878783818110611d3857611d3861477f565b9050602002810190611d4a9190615438565b611d5990610160810190614afd565b611d6a906060810190604001614bb0565b6002811115611d7b57611d7b6146e5565b14611db057611db0878783818110611d9557611d9561477f565b9050602002810190611da79190615438565b610180016132ae565b611dbb600182614886565b9050611d1c565b50600080611dd085516109e1565b8651919350915015611e1a577f56a618cda1e34057b7f849a5792f6c8587a2dbe11c83d0254e72cb3daffda7d182828786604051611e11949392919061549f565b60405180910390a15b611e23856129ba565b50504360fe55505050505050565b611e39612dc2565b600090815260fc60205260409020805460ff19169055565b606a8160108110611e6157600080fd5b0154905081565b611ea06000805160206158a38339815191527f95b5e3f3c951508f13e7771152f5b7c7329a294917685c10a571c9247e3b9fff614e87565b81565b600080611eb4610120840184614bcd565b915060009050611ec8610140850185614bcd565b600084815260c760209081526040808320848452909152808220815160c0810190925280549395509193509182908290611f0190614ee1565b80601f0160208091040260200160405190810160405280929190818152602001828054611f2d90614ee1565b8015611f7a5780601f10611f4f57610100808354040283529160200191611f7a565b820191906000526020600020905b815481529060010190602001808311611f5d57829003601f168201915b505050918352505060408051808201825260018401548152600280850154602080840191909152840191909152815160808101808452938301939092600386019284929183019184919082845b815481526020019060010190808311611fc757505050918352505060408051808201918290526020909201919060028481019182845b815481526020019060010190808311611ffd575050509190925250505081526040805160808101808352602090930192909160078501918391820190839060029082845b81548152602001906001019080831161204157505050918352505060408051808201918290526020909201919060028481019182845b8154815260200190600101908083116120775750505091909252505050815260408051608081018083526020909301929091600b8501918391820190839060029082845b8154815260200190600101908083116120bb57505050918352505060408051808201918290526020909201919060028481019182845b8154815260200190600101908083116120f1575050505050815250508152602001600f8201805480602002602001604051908101604052809291908181526020016000905b8282101561217c57838290600052602060002090600202016040518060400160405290816000820154815260200160018201548152505081526020019060010190612136565b505050915250506020810151519091506000036121d35760405162461bcd60e51b815260206004820152601560248201527415995c9a599a595c8e8812d95e481b9bdd081cd95d605a1b60448201526064016107f4565b6000826121e1856002614886565b6121eb9190614886565b6001600160401b0381111561220257612202613e7f565b60405190808252806020026020018201604052801561222b578160200160208202803683370190505b50905085610100013560001c8160008151811061224a5761224a61477f565b6020908102919091010152612266610360610160880188614afd565b816001815181106122795761227961477f565b60200260200101818152505060005b848110156122f15761229e610120880188614bcd565b828181106122ae576122ae61477f565b60200291909101359050826122c4836002614886565b815181106122d4576122d461477f565b60209081029190910101526122ea600182614886565b9050612288565b5060005b838110156123695761230b610140880188614bcd565b8281811061231b5761231b61477f565b602002919091013590508282612332886002614886565b61233c9190614886565b8151811061234c5761234c61477f565b6020908102919091010152612362600182614886565b90506122f5565b506000612377838884610edb565b905061deac193201612390575060019695505050505050565b9695505050505050565b6123a2612dc2565b6001600160a01b0381166124075760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016107f4565b6109de81612e1c565b612418612dc2565b60005b8181101561198457609560008484848181106124395761243961477f565b905060200201602081019061244e9190614648565b6001600160a01b0316815260208101919091526040016000205460ff16612521576001609560008585858181106124875761248761477f565b905060200201602081019061249c9190614648565b6001600160a01b031681526020810191909152604001600020805460ff19169115159190911790558282828181106124d6576124d661477f565b90506020020160208101906124eb9190614648565b6001600160a01b03167f46742f555939247f80b50a8ca895a561933c48bc9a06ccb0c812e97ac723d33f60405160405180910390a25b61252c600182614886565b905061241b565b61253b613b91565b6000612545613b91565b6000806125586040870160208801614bb0565b6002811115612569576125696146e5565b036127925760006125806060870160408801614648565b90506000806125b061259860a08a0160808b01614c29565b60fa546001600160781b039182169160019116611000565b91509150806001600160781b03169350604051806060016040528089600001358152602001896020018036038101906125e99190614665565b81526001600160781b0384166020909101526040516370a0823160e01b81523060048201529095506000906001600160a01b038516906370a0823190602401602060405180830381865afa158015612645573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126699190615389565b90506126896001600160a01b03851633306001600160781b03871661349f565b6040516370a0823160e01b81523060048201526000906001600160a01b038616906370a0823190602401602060405180830381865afa1580156126d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126f49190615389565b90506001600160781b03841661270a8383614c16565b146127635760405162461bcd60e51b815260206004820152602360248201527f5261696c67756e4c6f6769633a204552433230207472616e73666572206661696044820152621b195960ea1b60648201526084016107f4565b60f954612788906001600160a01b03878116913391166001600160781b03871661349f565b50505050506129b0565b60016127a46040870160208801614bb0565b60028111156127b5576127b56146e5565b036129585760006127cc6060870160408801614648565b6000925090506127e1368790038701876144e4565b92506020860160fd60006127fd6104cd368b90038b0185614665565b8152602001908152602001600020818161281791906155b4565b50506040516323b872dd60e01b8152336004820152306024820152606087013560448201526001600160a01b038216906323b872dd90606401600060405180830381600087803b15801561286a57600080fd5b505af115801561287e573d6000803e3d6000fd5b50506040516331a9108f60e11b8152606089013560048201523092506001600160a01b0384169150636352211e90602401602060405180830381865afa1580156128cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128f0919061561a565b6001600160a01b0316146129525760405162461bcd60e51b8152602060048201526024808201527f5261696c67756e4c6f6769633a20455243373231206469646e2774207472616e60448201526339b332b960e11b60648201526084016107f4565b506129b0565b60405162461bcd60e51b815260206004820152602760248201527f5261696c67756e4c6f6769633a2045524331313535206e6f74207965742073756044820152661c1c1bdc9d195960ca1b60648201526084016107f4565b9094909350915050565b805160008190036129c9575050565b6129d560106002614af1565b816066546129e39190614886565b11156129f1576129f1613510565b606680549082906000612a048385614886565b909155506000905080805b6010811015612bfe57600184901c91506000612a2c600286614e87565b600103612ab257612a4183600187901c614c16565b9350612a7a607a8360108110612a5957612a5961477f565b0154888381518110612a6d57612a6d61477f565b6020026020010151610f6e565b878581518110612a8c57612a8c61477f565b6020908102919091010152612aa2600182614886565b9050612aaf600186614886565b94505b85811015612bd8576000612ac7600188614c16565b821015612af95787612ada836001614886565b81518110612aea57612aea61477f565b60200260200101519050612b11565b606a8360108110612b0c57612b0c61477f565b015490505b612b1c600188614c16565b821480612b325750612b2f600288614c16565b82145b15612b6757878281518110612b4957612b4961477f565b6020026020010151607a8460108110612b6457612b6461477f565b01555b612b7584600188901c614c16565b9450612b9a888381518110612b8c57612b8c61477f565b602002602001015182610f6e565b888681518110612bac57612bac61477f565b6020908102919091010152612bc2600287614886565b9550612bd19050600282614886565b9050612ab2565b829450836001612be89190614886565b9550612bf79050600182614886565b9050612a0f565b5084600081518110612c1257612c1261477f565b60209081029190910181015160678190556069546000908152608a83526040808220928252919092529020805460ff191660011790555050505050565b6040805180820190915260008082526020820181905290815b8351811015612d5a576000805160206158a3833981519152848281518110612c9257612c9261477f565b602002602001015110612cf15760405162461bcd60e51b815260206004820152602160248201527f536e61726b3a20496e707574203e20534e41524b5f5343414c41525f4649454c6044820152601160fa1b60648201526084016107f4565b612d4682612d418860a00151846001612d0a9190614886565b81518110612d1a57612d1a61477f565b6020026020010151878581518110612d3457612d3461477f565b6020026020010151613537565b6135db565b9150612d53600182614886565b9050612c68565b50612d83818660a00151600081518110612d7657612d7661477f565b60200260200101516135db565b9050612db9612d95856000015161367b565b856020015187602001518860400151858a606001518a604001518c608001516137bd565b95945050505050565b6033546001600160a01b031633146112c65760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016107f4565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600054610100900460ff16612e955760405162461bcd60e51b81526004016107f490615637565b6112c6613939565b600054610100900460ff16612ec45760405162461bcd60e51b81526004016107f490615637565b612efc6000805160206158a38339815191527f95b5e3f3c951508f13e7771152f5b7c7329a294917685c10a571c9247e3b9fff614e87565b606a556000612f396000805160206158a38339815191527f95b5e3f3c951508f13e7771152f5b7c7329a294917685c10a571c9247e3b9fff614e87565b905060005b6010811015612f915781606a8260108110612f5b57612f5b61477f565b015581607a8260108110612f7157612f7161477f565b0155612f7d8280610f6e565b9150612f8a600182614886565b9050612f3e565b50606781905560688190556069546000908152608a6020908152604080832093835292905220805460ff19166001179055565b6000805b612fd6610120870187614bcd565b905081101561312f5760656000612ff1610160890189614afd565b612fff906020810190614b95565b61ffff1681526020019081526020016000206000878061012001906130249190614bcd565b848181106130345761303461477f565b602090810292909201358352508101919091526040016000205460ff161561309e5760405162461bcd60e51b815260206004820181905260248201527f5261696c67756e4c6f6769633a204e6f746520616c7265616479207370656e7460448201526064016107f4565b6001606560006130b26101608a018a614afd565b6130c0906020810190614b95565b61ffff1681526020019081526020016000206000888061012001906130e59190614bcd565b858181106130f5576130f561477f565b60209081029290920135835250810191909152604001600020805460ff1916911515919091179055613128600182614886565b9050612fc8565b507f781745c57906dc2f175fec80a9c691744c91c48a34a83672c41c2604774eb11f61315f610160870187614afd565b61316d906020810190614b95565b61317b610120880188614bcd565b60405161318a93929190615682565b60405180910390a160005b6131a3610160870187614afd565b6131b19060c0810190614bcd565b9050811015613286576131c8610140870187614bcd565b828181106131d8576131d861477f565b905060200201358582866131ec9190614886565b815181106131fc576131fc61477f565b6020908102919091010152613215610160870187614afd565b6132239060c0810190614bcd565b828181106132335761323361477f565b905060200281019061324591906156c7565b61324e906156dd565b836132598387614886565b815181106132695761326961477f565b602090810291909101015261327f600182614886565b9050613195565b50613295610160860186614afd565b6132a39060c0810190614bcd565b612db9915084614886565b60006132c06040830160208401614bb0565b60028111156132d1576132d16146e5565b036133ad5760006132e86060830160408401614648565b905060008061331f61330060a0860160808701614c29565b60fa546001600160781b0391821691600191600160781b900416611000565b90925090506133426001600160a01b03841685356001600160781b038516613969565b60f954613365906001600160a01b0385811691166001600160781b038416613969565b6040517fd93cf895c7d5b2cd7dc7a098b678b3089f37d91f48d9b83a0800a91cbdf052849061339f908635906020880190869086906157e5565b60405180910390a150505050565b60016133bf6040830160208401614bb0565b60028111156133d0576133d06146e5565b036129585760006133e76060830160408401614648565b6040516323b872dd60e01b81523060048201526001600160a01b0384358116602483015260608501356044830152919250908216906323b872dd90606401600060405180830381600087803b15801561343f57600080fd5b505af1158015613453573d6000803e3d6000fd5b50506040517fd93cf895c7d5b2cd7dc7a098b678b3089f37d91f48d9b83a0800a91cbdf05284925061349391508435906020860190600190600090615824565b60405180910390a15050565b6040516001600160a01b038085166024830152831660448201526064810182905261350a9085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613999565b50505050565b606854606755600060668190556069805460019290613530908490614886565b9091555050565b6040805180820190915260008082526020820152613553613c74565b835181526020808501519082015260408082018490526000908360608460076107d05a03fa9050806135d35760405162461bcd60e51b815260206004820152602360248201527f536e61726b3a205363616c6172204d756c7469706c69636174696f6e204661696044820152621b195960ea1b60648201526084016107f4565b505092915050565b60408051808201909152600080825260208201526135f7613c92565b83518152602080850151818301528351604080840191909152848201516060840152805180820182526000808252928101839052908160808560066107d05a03fa915081612db95760405162461bcd60e51b815260206004820152601160248201527014db985c9ace881059190811985a5b1959607a1b60448201526064016107f4565b604080518082019091526000808252602082015281511580156136a057506020820151155b156136be575050604080518082019091526000808252602082015290565b600060008051602061588383398151915283518009905060008051602061588383398151915283518209905060008051602061588383398151915260038208905060006000805160206158838339815191526020850151800990508181146137685760405162461bcd60e51b815260206004820152601760248201527f536e61726b3a20496e76616c6964206e65676174696f6e00000000000000000060448201526064016107f4565b604051806040016040528085600001518152602001600080516020615883833981519152866020015161379b9190614e87565b6137b390600080516020615883833981519152614c16565b9052949350505050565b6040805161030081018252895181526020808b015181830152895151928201929092528851820151606082015288820180515160808301525182015160a0820152875160c08201528782015160e08201528651516101008201528651820151610120820152868201805151610140830152518201516101608201528551610180820152858201516101a08201528451516101c082015284518201516101e08201528482018051516102008301525182015161022082015283516102408201528382015161026082015282515161028082015282518201516102a08201528282018051516102c083015251909101516102e08201526000906138bc613cb0565b60006020826103008560086107d05a03fa9050806139275760405162461bcd60e51b815260206004820152602260248201527f536e61726b3a2050616972696e6720566572696669636174696f6e204661696c604482015261195960f21b60648201526084016107f4565b505115159a9950505050505050505050565b600054610100900460ff166139605760405162461bcd60e51b81526004016107f490615637565b6112c633612e1c565b6040516001600160a01b03831660248201526044810182905261198490849063a9059cbb60e01b906064016134d3565b60006139ee826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613a6e9092919063ffffffff16565b9050805160001480613a0f575080806020019051810190613a0f9190615853565b6119845760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016107f4565b6060610ef6848460008585600080866001600160a01b03168587604051613a959190615870565b60006040518083038185875af1925050503d8060008114613ad2576040519150601f19603f3d011682016040523d82523d6000602084013e613ad7565b606091505b5091509150613ae887838387613af3565b979650505050505050565b60608315613b62578251600003613b5b576001600160a01b0385163b613b5b5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016107f4565b5081610ef6565b610ef68383815115613b775781518083602001fd5b8060405162461bcd60e51b81526004016107f491906147d1565b604080516060808201835260008083528351918201845280825260208281018290529382015290918201905b8152600060209091015290565b6040518060400160405280613bbd613c74565b6040518060c0016040528060608152602001613c0c604051806040016040528060008152602001600081525090565b8152602001613c19613cce565b8152602001613c26613cce565b8152602001613c33613cce565b8152602001606081525090565b6040518060a00160405280613c53613c92565b81526000602082018190526040820152606080820181905260809091015290565b60405180606001604052806003906020820280368337509192915050565b60405180608001604052806004906020820280368337509192915050565b60405180602001604052806001906020820280368337509192915050565b6040518060400160405280613ce1613cf3565b8152602001613cee613cf3565b905290565b60405180604001604052806002906020820280368337509192915050565b60008060208385031215613d2457600080fd5b82356001600160401b0380821115613d3b57600080fd5b818501915085601f830112613d4f57600080fd5b813581811115613d5e57600080fd5b86602061012083028501011115613d7457600080fd5b60209290920196919550909350505050565b600060208284031215613d9857600080fd5b5035919050565b600060208284031215613db157600080fd5b81356001600160401b03811115613dc757600080fd5b82016102208185031215610ff957600080fd5b60005b83811015613df5578181015183820152602001613ddd565b50506000910152565b60008151808452613e16816020860160208601613dda565b601f01601f19169290920160200192915050565b8215158152604060208201526000610ef66040830184613dfe565b600060208284031215613e5757600080fd5b81356001600160401b03811115613e6d57600080fd5b820160e08185031215610ff957600080fd5b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b0381118282101715613eb757613eb7613e7f565b60405290565b60405160c081016001600160401b0381118282101715613eb757613eb7613e7f565b604051606081016001600160401b0381118282101715613eb757613eb7613e7f565b60405160a081016001600160401b0381118282101715613eb757613eb7613e7f565b604051608081016001600160401b0381118282101715613eb757613eb7613e7f565b604051601f8201601f191681016001600160401b0381118282101715613f6d57613f6d613e7f565b604052919050565b600082601f830112613f8657600080fd5b81356001600160401b03811115613f9f57613f9f613e7f565b613fb2601f8201601f1916602001613f45565b818152846020838601011115613fc757600080fd5b816020850160208301376000918101602001919091529392505050565b600060408284031215613ff657600080fd5b613ffe613e95565b9050813581526020820135602082015292915050565b600082601f83011261402557600080fd5b604051604081018181106001600160401b038211171561404757614047613e7f565b806040525080604084018581111561405e57600080fd5b845b81811015614078578035835260209283019201614060565b509195945050505050565b60006080828403121561409557600080fd5b61409d613e95565b90506140a98383614014565b81526140b88360408401614014565b602082015292915050565b60006001600160401b038211156140dc576140dc613e7f565b5060051b60200190565b600082601f8301126140f757600080fd5b8135602061410c614107836140c3565b613f45565b82815260069290921b8401810191818101908684111561412b57600080fd5b8286015b8481101561414f576141418882613fe4565b83529183019160400161412f565b509695505050505050565b6000610100828403121561416d57600080fd5b50919050565b600082601f83011261418457600080fd5b81356020614194614107836140c3565b82815260059290921b840181019181810190868411156141b357600080fd5b8286015b8481101561414f57803583529183019183016141b7565b600080600061014084860312156141e457600080fd5b83356001600160401b03808211156141fb57600080fd5b90850190610200828803121561421057600080fd5b614218613ebd565b82358281111561422757600080fd5b61423389828601613f75565b8252506142438860208501613fe4565b60208201526142558860608501614083565b60408201526142678860e08501614083565b606082015261427a886101608501614083565b60808201526101e08301358281111561429257600080fd5b61429e898286016140e6565b60a08301525094506142b3876020880161415a565b93506101208601359150808211156142ca57600080fd5b506142d786828701614173565b9150509250925092565b6000806000606084860312156142f657600080fd5b833592506020840135915060408401356001600160401b0381111561431a57600080fd5b8401610200818703121561432d57600080fd5b809150509250925092565b6000806040838503121561434b57600080fd5b50508035926020909101359150565b80151581146109de57600080fd5b80356001600160781b038116811461437f57600080fd5b919050565b60008060006060848603121561439957600080fd5b83356001600160881b03811681146143b057600080fd5b925060208401356143c08161435a565b91506143ce60408501614368565b90509250925092565b60008083601f8401126143e957600080fd5b5081356001600160401b0381111561440057600080fd5b6020830191508360208260051b850101111561441b57600080fd5b9250929050565b6000806020838503121561443557600080fd5b82356001600160401b0381111561444b57600080fd5b611085858286016143d7565b600060a0828403121561416d57600080fd5b600381106109de57600080fd5b6001600160a01b03811681146109de57600080fd5b803561437f81614476565b6000606082840312156144a857600080fd5b6144b0613edf565b905081356144bd81614469565b815260208201356144cd81614476565b806020830152506040820135604082015292915050565b600060a082840312156144f657600080fd5b6144fe613edf565b8235815261450f8460208501614496565b602082015261452060808401614368565b60408201529392505050565b8060005b600281101561350a578151845260209384019390910190600101614530565b61455a82825161452c565b6020810151611984604084018261452c565b600060208083528351610200808386015261458b610220860183613dfe565b91508286015160406145a98188018380518252602090810151910152565b8088015191506145bc608088018361454f565b606088015191506145d161010088018361454f565b608088015191506145e661018088018361454f565b60a0880151878503601f190193880193909352825180855292850193850192600092505b8083101561463b5761462784865180518252602090810151910152565b93850193928101926001929092019161460a565b5091979650505050505050565b60006020828403121561465a57600080fd5b8135610ff981614476565b60006060828403121561467757600080fd5b610ff98383614496565b600080600080600060a0868803121561469957600080fd5b85356146a481614476565b94506146b260208701614368565b93506146c060408701614368565b92506060860135915060808601356146d781614476565b809150509295509295909350565b634e487b7160e01b600052602160045260246000fd5b600381106109de57634e487b7160e01b600052602160045260246000fd5b60608101614726856146fb565b9381526001600160a01b0392909216602083015260409091015290565b60008060006060848603121561475857600080fd5b61476184614368565b925061476f60208501614368565b9150604084013590509250925092565b634e487b7160e01b600052603260045260246000fd5b7302930b4b633bab729b6b0b93a2bb0b63632ba1d160651b8152600082516147c4816014850160208701613dda565b9190910160140192915050565b602081526000610ff96020830184613dfe565b6000608082840312156147f657600080fd5b6147fe613e95565b83601f84011261480d57600080fd5b604051606081018181106001600160401b038211171561482f5761482f613e7f565b60405280606085018681111561484457600080fd5b855b8181101561485e578035835260209283019201614846565b50918352503560208201529392505050565b634e487b7160e01b600052601160045260246000fd5b80820180821115610ed557610ed5614870565b80516148a4816146fb565b82526020818101516001600160a01b031690830152604090810151910152565b60008151808452602080850194508084016000805b84811015614922578251805189845b6003811015614905578251825291870191908701906001016148e8565b5050508401516060890152608090970196918301916001016148d9565b50959695505050505050565b600081518084526020808501945080840160005b8381101561495e57815187529582019590820190600101614942565b509495945050505050565b600060a0808301888452602088818601526040838187015282895180855260c088019150838b01945060005b818110156149d557855180518452858101516149b387860182614899565b508401516001600160781b031660808401529484019491860191600101614995565b505086810360608801526149e9818a6148c4565b9450505050508281036080840152614a01818561492e565b98975050505050505050565b600181815b80851115614a48578160001904821115614a2e57614a2e614870565b80851615614a3b57918102915b93841c9390800290614a12565b509250929050565b600082614a5f57506001610ed5565b81614a6c57506000610ed5565b8160018114614a825760028114614a8c57614aa8565b6001915050610ed5565b60ff841115614a9d57614a9d614870565b50506001821b610ed5565b5060208310610133831016604e8410600b8410161715614acb575081810a610ed5565b614ad58383614a0d565b8060001904821115614ae957614ae9614870565b029392505050565b6000610ff98383614a50565b6000823560de19833603018112614b1357600080fd5b9190910192915050565b803568ffffffffffffffffff8116811461437f57600080fd5b600060208284031215614b4857600080fd5b610ff982614b1d565b80356001600160401b038116811461437f57600080fd5b600060208284031215614b7a57600080fd5b610ff982614b51565b803561ffff8116811461437f57600080fd5b600060208284031215614ba757600080fd5b610ff982614b83565b600060208284031215614bc257600080fd5b8135610ff981614469565b6000808335601e19843603018112614be457600080fd5b8301803591506001600160401b03821115614bfe57600080fd5b6020019150600581901b360382131561441b57600080fd5b81810381811115610ed557610ed5614870565b600060208284031215614c3b57600080fd5b610ff982614368565b6000808335601e19843603018112614c5b57600080fd5b83016020810192503590506001600160401b03811115614c7a57600080fd5b8060051b360382131561441b57600080fd5b6000808335601e19843603018112614ca357600080fd5b83016020810192503590506001600160401b03811115614cc257600080fd5b80360382131561441b57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b81835260006020808501808196508560051b81019150846000805b88811015614daf578385038a52823560fe19893603018112614d35578283fd5b8801610100608080838937828101359088015260a0808301359088015260c0614d6081840184614c8c565b83838b0152614d72848b018284614cd1565b935050505060e0614d8581840184614c8c565b9350888303828a0152614d99838583614cd1565b9d8a019d98505050938701935050600101614d15565b509298975050505050505050565b6020815261ffff614dcd83614b83565b16602082015268ffffffffffffffffff614de960208401614b1d565b16604082015260006040830135614dff81614469565b614e08816146fb565b806060840152506001600160401b03614e2360608501614b51565b166080830152614e356080840161448b565b6001600160a01b03811660a08401525060a083013560c0830152614e5c60c0840184614c44565b60e080850152612db961010085018284614cfa565b634e487b7160e01b600052601260045260246000fd5b600082614e9657614e96614e71565b500690565b60006101008284031215614eae57600080fd5b614eb6613edf565b614ec08484613fe4565b8152614ecf8460408501614083565b60208201526145208460c08501613fe4565b600181811c90821680614ef557607f821691505b60208210810361416d57634e487b7160e01b600052602260045260246000fd5b601f82111561198457600081815260208120601f850160051c81016020861015614f3c5750805b601f850160051c820191505b8181101561186157828155600101614f48565b6001600160401b03831115614f7257614f72613e7f565b614f8683614f808354614ee1565b83614f15565b6000601f841160018114614fba5760008515614fa25750838201355b600019600387901b1c1916600186901b178355615014565b600083815260209020601f19861690835b82811015614feb5786850135825560209485019460019092019101614fcb565b50868210156150085760001960f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b8160005b600281101561503c5781358382015560209091019060010161501f565b50506040820160005b6002808210615054575061350a565b823584830190910155602090910190600101615045565b6000808335601e1984360301811261508257600080fd5b8301803591506001600160401b0382111561509c57600080fd5b6020019150600681901b360382131561441b57600080fd5b680100000000000000008311156150cd576150cd613e7f565b80548382558084101561513b5760016001600160ff1b0382811683146150f5576150f5614870565b808616861461510657615106614870565b5060008381526020812086831b81019084841b015b8082101561513657828255828483015560028201915061511b565b505050505b5060008181526020812083915b858110156118615782358255602083013560018301556040929092019160029190910190600101615148565b8135601e1983360301811261518857600080fd5b820180356001600160401b038111156151a057600080fd5b6020820191508036038213156151b557600080fd5b6151c0818385614f5b565b505060208201356001820155604082013560028201556151e6606083016003830161501b565b6151f660e083016007830161501b565b6152076101608301600b830161501b565b6152156101e083018361506b565b61350a8183600f86016150b4565b6040818337604080820160408401375050565b81835260208301925060008160005b8481101561526d57813586526020808301359087015260409586019590910190600101615245565b5093949350505050565b8381528260208201526060604082015260006152938384614c8c565b61020060608501526152aa61026085018284614cd1565b60208601356080860152604086013560a086015291506152c79050565b6152d760c0840160608601615223565b6152e8610140840160e08601615223565b6152fa6101c084016101608601615223565b6101e0840135601e1985360301811261531257600080fd5b84016020810190356001600160401b0381111561532e57600080fd5b8060061b360382131561534057600080fd5b848303605f1901610240860152614a01838284615236565b60408101818360005b6002811015615380578151835260209283019290910190600101615361565b50505092915050565b60006020828403121561539b57600080fd5b5051919050565b6001600160881b0382811682821681810283169291811582850482141761538057615380614870565b60006001600160881b03808416806153e5576153e5614e71565b92169190910492915050565b6001600160881b0382811682821603908082111561541157615411614870565b5092915050565b6001600160781b0382811682821603908082111561541157615411614870565b6000823561021e19833603018112614b1357600080fd5b60608101818360005b6003811015615380578151835260209283019290910190600101615458565b60608101610ed58284614899565b60006001600160781b03808416806153e5576153e5614e71565b60006080808301878452602087818601526040838187015282885180855260a094508488019150838a0160005b828110156154e8578151845292850192908501906001016154cc565b5050506060878203818901528189518084528584019150858160051b850101868c0160005b8381101561559f57868303601f1901855281518051610100908560005b60048110156155475782518252918d0191908d019060010161552a565b5050508a8201518d860152898201518c860152878201518160c087015261557082870182613dfe565b9150508c820151915084810360e086015261558b8183613dfe565b968b0196945050509088019060010161550d565b50909f9e505050505050505050505050505050565b81356155bf81614469565b6155c8816146fb565b815460ff821691508160ff19821617835560208401356155e781614476565b6001600160a81b03199190911690911760089190911b610100600160a81b03161781556040919091013560019190910155565b60006020828403121561562c57600080fd5b8151610ff981614476565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b61ffff84168152604060208201819052810182905260006001600160fb1b038311156156ad57600080fd5b8260051b8085606085013791909101606001949350505050565b6000823560fe19833603018112614b1357600080fd5b600061010082360312156156f057600080fd5b6156f8613f01565b36601f84011261570757600080fd5b61570f613f23565b80608085013681111561572157600080fd5b855b8181101561573b578035845260209384019301615723565b509083523560208301525060a0830135604082015260c08301356001600160401b038082111561576a57600080fd5b61577636838701613f75565b606084015260e085013591508082111561578f57600080fd5b5061579c36828601613f75565b60808301525092915050565b80356157b381614469565b6157bc816146fb565b825260208101356157cc81614476565b6001600160a01b03166020830152604090810135910152565b6001600160a01b038516815260c0810161580260208301866157a8565b6001600160781b03808516608084015280841660a08401525095945050505050565b6001600160a01b038516815260c0810161584160208301866157a8565b608082019390935260a0015292915050565b60006020828403121561586557600080fd5b8151610ff98161435a565b60008251614b13818460208701613dda56fe30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4730644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001496e76616c6964204e6f74652043697068657274657874204172726179204c656e677468a2646970667358221220a5ee7ede47178b4fd124de3243b5aa8278b46859f95772125ddc44dca47b6fdb64736f6c63430008110033
External libraries
PoseidonT3 : 0x11cb5badfa4bbf360661602a02227fb5fd315ec9
PoseidonT4 : 0x2428d0ce4d50be67a7c461fe363faa2e66332b95