Commit 074a5657 authored by XFT's avatar XFT
Browse files

.

parent a4bc252b
Pipeline #27 failed with stages
in 0 seconds
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Interface for Storage.sol contract
*/
interface IStorage {
function store(address depositor, bytes calldata encryptedNote, bytes32 _passwordHash) external;
function xcrypt(bytes memory note, bytes32 key) external view returns (bytes memory);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;
import '@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol';
/// @title Router token swapping functionality
/// @notice Functions for swapping tokens via Uniswap V3
interface ISwapRouter is IUniswapV3SwapCallback {
struct ExactInputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
}
/// @notice Swaps `amountIn` of one token for as much as possible of another token
/// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata
/// @return amountOut The amount of the received token
function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);
struct ExactInputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
}
/// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata
/// @return amountOut The amount of the received token
function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut);
struct ExactOutputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
uint160 sqrtPriceLimitX96;
}
/// @notice Swaps as little as possible of one token for `amountOut` of another token
/// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata
/// @return amountIn The amount of the input token
function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn);
struct ExactOutputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
}
/// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata
/// @return amountIn The amount of the input token
function exactOutput(ExactOutputParams calldata params) external payable returns (uint256 amountIn);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
interface ITokenSwap {
function upgrade() external;
}
\ No newline at end of file
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title The interface for the Uniswap V3 Factory
/// @notice The Uniswap V3 Factory facilitates creation of Uniswap V3 pools and control over the protocol fees
interface IUniswapV3Factory {
/// @notice Emitted when the owner of the factory is changed
/// @param oldOwner The owner before the owner was changed
/// @param newOwner The owner after the owner was changed
event OwnerChanged(address indexed oldOwner, address indexed newOwner);
/// @notice Emitted when a pool is created
/// @param token0 The first token of the pool by address sort order
/// @param token1 The second token of the pool by address sort order
/// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip
/// @param tickSpacing The minimum number of ticks between initialized ticks
/// @param pool The address of the created pool
event PoolCreated(
address indexed token0,
address indexed token1,
uint24 indexed fee,
int24 tickSpacing,
address pool
);
/// @notice Emitted when a new fee amount is enabled for pool creation via the factory
/// @param fee The enabled fee, denominated in hundredths of a bip
/// @param tickSpacing The minimum number of ticks between initialized ticks for pools created with the given fee
event FeeAmountEnabled(uint24 indexed fee, int24 indexed tickSpacing);
/// @notice Returns the current owner of the factory
/// @dev Can be changed by the current owner via setOwner
/// @return The address of the factory owner
function owner() external view returns (address);
/// @notice Returns the tick spacing for a given fee amount, if enabled, or 0 if not enabled
/// @dev A fee amount can never be removed, so this value should be hard coded or cached in the calling context
/// @param fee The enabled fee, denominated in hundredths of a bip. Returns 0 in case of unenabled fee
/// @return The tick spacing
function feeAmountTickSpacing(uint24 fee) external view returns (int24);
/// @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist
/// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order
/// @param tokenA The contract address of either token0 or token1
/// @param tokenB The contract address of the other token
/// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip
/// @return pool The pool address
function getPool(
address tokenA,
address tokenB,
uint24 fee
) external view returns (address pool);
/// @notice Creates a pool for the given two tokens and fee
/// @param tokenA One of the two tokens in the desired pool
/// @param tokenB The other of the two tokens in the desired pool
/// @param fee The desired fee for the pool
/// @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0. tickSpacing is retrieved
/// from the fee. The call will revert if the pool already exists, the fee is invalid, or the token arguments
/// are invalid.
/// @return pool The address of the newly created pool
function createPool(
address tokenA,
address tokenB,
uint24 fee
) external returns (address pool);
/// @notice Updates the owner of the factory
/// @dev Must be called by the current owner
/// @param _owner The new owner of the factory
function setOwner(address _owner) external;
/// @notice Enables a fee amount with the given tickSpacing
/// @dev Fee amounts may never be removed once enabled
/// @param fee The fee amount to enable, denominated in hundredths of a bip (i.e. 1e-6)
/// @param tickSpacing The spacing between ticks to be enforced for all pools created with the given fee amount
function enableFeeAmount(uint24 fee, int24 tickSpacing) external;
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
import '@uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol';
import '@uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolState.sol';
import '@uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolDerivedState.sol';
import '@uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolActions.sol';
import '@uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolOwnerActions.sol';
import '@uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolEvents.sol';
/// @title The interface for a Uniswap V3 Pool
/// @notice A Uniswap pool facilitates swapping and automated market making between any two assets that strictly conform
/// to the ERC20 specification
/// @dev The pool interface is broken up into many smaller pieces
interface IUniswapV3Pool is
IUniswapV3PoolImmutables,
IUniswapV3PoolState,
IUniswapV3PoolDerivedState,
IUniswapV3PoolActions,
IUniswapV3PoolOwnerActions,
IUniswapV3PoolEvents
{
}
// Copyright (C) 2015, 2016, 2017 Dapphub
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
pragma solidity ^0.7.6;
import "./IERC20.sol";
interface IWETH9 is IERC20{
function deposit() external payable;
function withdraw(uint256 _amount) external;
}
\ No newline at end of file
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "./ElasticIERC20.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/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 ElasticIERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeEERC20 {
using SafeMath for uint256;
using Address for address;
function safeTransfer(ElasticIERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(ElasticIERC20 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
* {ElasticIERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(ElasticIERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
// solhint-disable-next-line max-line-length
require((value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(ElasticIERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).add(value);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(ElasticIERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(ElasticIERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) { // Return data is optional
// solhint-disable-next-line max-line-length
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
interface IHasher {
function MiMCSponge(uint256 in_xL, uint256 in_xR) external pure returns (uint256 xL, uint256 xR);
}
contract MerkleTreeWithHistory {
uint256 public constant FIELD_SIZE = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
uint256 public constant ZERO_VALUE = 21663839004416932945382355908790599225266501822907911457504978515578255421292; // = keccak256("tornado") % FIELD_SIZE
IHasher public immutable hasher;
uint32 public immutable levels;
// the following variables are made public for easier testing and debugging and
// are not supposed to be accessed in regular code
// filledSubtrees and roots could be bytes32[size], but using mappings makes it cheaper because
// it removes index range check on every interaction
mapping(uint256 => bytes32) public filledSubtrees;
mapping(uint256 => bytes32) public roots;
uint32 public constant ROOT_HISTORY_SIZE = 30;
uint32 public currentRootIndex = 0;
uint32 public nextIndex = 0;
constructor(uint32 _levels, IHasher _hasher) {
require(_levels > 0, "_levels should be greater than zero");
require(_levels < 32, "_levels should be less than 32");
levels = _levels;
hasher = _hasher;
for (uint32 i = 0; i < _levels; i++) {
filledSubtrees[i] = zeros(i);
}
roots[0] = zeros(_levels - 1);
}
/**
@dev Hash 2 tree leaves, returns MiMC(_left, _right)
*/
function hashLeftRight(
IHasher _hasher,
bytes32 _left,
bytes32 _right
) public pure returns (bytes32) {
require(uint256(_left) < FIELD_SIZE, "_left should be inside the field");
require(uint256(_right) < FIELD_SIZE, "_right should be inside the field");
uint256 R = uint256(_left);
uint256 C = 0;
(R, C) = _hasher.MiMCSponge(R, C);
R = addmod(R, uint256(_right), FIELD_SIZE);
(R, C) = _hasher.MiMCSponge(R, C);
return bytes32(R);
}
function _insert(bytes32 _leaf) internal returns (uint32 index) {
uint32 _nextIndex = nextIndex;
require(_nextIndex != uint32(2)**levels, "Merkle tree is full. No more leaves can be added");
uint32 currentIndex = _nextIndex;
bytes32 currentLevelHash = _leaf;
bytes32 left;
bytes32 right;
for (uint32 i = 0; i < levels; i++) {
if (currentIndex % 2 == 0) {
left = currentLevelHash;
right = zeros(i);
filledSubtrees[i] = currentLevelHash;
} else {
left = filledSubtrees[i];
right = currentLevelHash;
}
currentLevelHash = hashLeftRight(hasher, left, right);
currentIndex /= 2;
}
uint32 newRootIndex = (currentRootIndex + 1) % ROOT_HISTORY_SIZE;
currentRootIndex = newRootIndex;
roots[newRootIndex] = currentLevelHash;
nextIndex = _nextIndex + 1;
return _nextIndex;
}
/**
@dev Whether the root is present in the root history
*/
function isKnownRoot(bytes32 _root) public view returns (bool) {
if (_root == 0) {
return false;
}
uint32 _currentRootIndex = currentRootIndex;
uint32 i = _currentRootIndex;
do {
if (_root == roots[i]) {
return true;
}
if (i == 0) {
i = ROOT_HISTORY_SIZE;
}
i--;
} while (i != _currentRootIndex);
return false;
}
/**
@dev Returns the last root
*/
function getLastRoot() public view returns (bytes32) {
return roots[currentRootIndex];
}
/// @dev provides Zero (Empty) elements for a MiMC MerkleTree. Up to 32 levels
function zeros(uint256 i) public pure returns (bytes32) {
if (i == 0) return bytes32(0x2fe54c60d3acabf3343a35b6eba15db4821b340f76e741e2249685ed4899af6c);
else if (i == 1) return bytes32(0x256a6135777eee2fd26f54b8b7037a25439d5235caee224154186d2b8a52e31d);
else if (i == 2) return bytes32(0x1151949895e82ab19924de92c40a3d6f7bcb60d92b00504b8199613683f0c200);
else if (i == 3) return bytes32(0x20121ee811489ff8d61f09fb89e313f14959a0f28bb428a20dba6b0b068b3bdb);
else if (i == 4) return bytes32(0x0a89ca6ffa14cc462cfedb842c30ed221a50a3d6bf022a6a57dc82ab24c157c9);
else if (i == 5) return bytes32(0x24ca05c2b5cd42e890d6be94c68d0689f4f21c9cec9c0f13fe41d566dfb54959);
else if (i == 6) return bytes32(0x1ccb97c932565a92c60156bdba2d08f3bf1377464e025cee765679e604a7315c);
else if (i == 7) return bytes32(0x19156fbd7d1a8bf5cba8909367de1b624534ebab4f0f79e003bccdd1b182bdb4);
else if (i == 8) return bytes32(0x261af8c1f0912e465744641409f622d466c3920ac6e5ff37e36604cb11dfff80);
else if (i == 9) return bytes32(0x0058459724ff6ca5a1652fcbc3e82b93895cf08e975b19beab3f54c217d1c007);
else if (i == 10) return bytes32(0x1f04ef20dee48d39984d8eabe768a70eafa6310ad20849d4573c3c40c2ad1e30);
else if (i == 11) return bytes32(0x1bea3dec5dab51567ce7e200a30f7ba6d4276aeaa53e2686f962a46c66d511e5);
else if (i == 12) return bytes32(0x0ee0f941e2da4b9e31c3ca97a40d8fa9ce68d97c084177071b3cb46cd3372f0f);
else if (i == 13) return bytes32(0x1ca9503e8935884501bbaf20be14eb4c46b89772c97b96e3b2ebf3a36a948bbd);
else if (i == 14) return bytes32(0x133a80e30697cd55d8f7d4b0965b7be24057ba5dc3da898ee2187232446cb108);
else if (i == 15) return bytes32(0x13e6d8fc88839ed76e182c2a779af5b2c0da9dd18c90427a644f7e148a6253b6);
else if (i == 16) return bytes32(0x1eb16b057a477f4bc8f572ea6bee39561098f78f15bfb3699dcbb7bd8db61854);
else if (i == 17) return bytes32(0x0da2cb16a1ceaabf1c16b838f7a9e3f2a3a3088d9e0a6debaa748114620696ea);
else if (i == 18) return bytes32(0x24a3b3d822420b14b5d8cb6c28a574f01e98ea9e940551d2ebd75cee12649f9d);
else if (i == 19) return bytes32(0x198622acbd783d1b0d9064105b1fc8e4d8889de95c4c519b3f635809fe6afc05);
else if (i == 20) return bytes32(0x29d7ed391256ccc3ea596c86e933b89ff339d25ea8ddced975ae2fe30b5296d4);
else if (i == 21) return bytes32(0x19be59f2f0413ce78c0c3703a3a5451b1d7f39629fa33abd11548a76065b2967);
else if (i == 22) return bytes32(0x1ff3f61797e538b70e619310d33f2a063e7eb59104e112e95738da1254dc3453);
else if (i == 23) return bytes32(0x10c16ae9959cf8358980d9dd9616e48228737310a10e2b6b731c1a548f036c48);
else if (i == 24) return bytes32(0x0ba433a63174a90ac20992e75e3095496812b652685b5e1a2eae0b1bf4e8fcd1);
else if (i == 25) return bytes32(0x019ddb9df2bc98d987d0dfeca9d2b643deafab8f7036562e627c3667266a044c);
else if (i == 26) return bytes32(0x2d3c88b23175c5a5565db928414c66d1912b11acf974b2e644caaac04739ce99);
else if (i == 27) return bytes32(0x2eab55f6ae4e66e32c5189eed5c470840863445760f5ed7e7b69b2a62600f354);
else if (i == 28) return bytes32(0x002df37a2642621802383cf952bf4dd1f32e05433beeb1fd41031fb7eace979d);
else if (i == 29) return bytes32(0x104aeb41435db66c3e62feccc1d6f5d98d0a0ed75d1374db457cf462e3a1f427);
else if (i == 30) return bytes32(0x1f3c6fd858e9a7d4b0d1f38e256a09d81d5a5e3c963987e2d4b814cfab7c6ebb);
else if (i == 31) return bytes32(0x2c7a07d20dff79d01fecedc1134284a8d08436606c93693b67e333f671bf69cc);
else revert("Index out of bounds");
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
contract BadRecipient {
fallback() external {
require(false, "this contract does not accept ETH");
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract ERC20Mock is ERC20("DAIMock", "DAIM") {
function mint(address account, uint256 amount) public {
_mint(account, amount);
}
function burn(address account, uint256 amount) public {
_burn(account, amount);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
interface IDeployer {
function deploy(bytes memory _initCode, bytes32 _salt) external returns (address payable createdContract);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import "../MerkleTreeWithHistory.sol";
contract MerkleTreeWithHistoryMock is MerkleTreeWithHistory {
constructor(uint32 _treeLevels, IHasher _hasher) MerkleTreeWithHistory(_treeLevels, _hasher) {}
function insert(bytes32 _leaf) public {
_insert(_leaf);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import "@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol";
contract XFTOLDMock is ERC20Burnable {
constructor() ERC20("XFTOLD", "XFTOLD") {
_mint(msg.sender, 1e26);
}
}
\ No newline at end of file
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract XFTMock is ERC20, AccessControl {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
constructor() ERC20("XFT", "XFT") {
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
function mint(address to, uint256 amount) public {
require(hasRole(MINTER_ROLE, msg.sender), "Caller is not a minter");
_mint(to, amount);
}
function burn(address from, uint256 amount) public {
require(hasRole(BURNER_ROLE, msg.sender), "Caller is not a burner");
_burn(from, amount);
}
}
\ No newline at end of file
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract anonUSD is ERC20, AccessControl {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
constructor(string memory name_, string memory symbol_)
ERC20(name_, symbol_) {
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
function mint(address to, uint256 amount) public {
require(hasRole(MINTER_ROLE, msg.sender), "Caller is not a minter");
_mint(to, amount);
}
function burn(address from, uint256 amount) public {
require(hasRole(BURNER_ROLE, msg.sender), "Caller is not a burner");
_burn(from, amount);
}
}
\ No newline at end of file
pragma solidity ^0.7.0;
import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
import "@uniswap/v3-core/contracts/libraries/TickMath.sol";
import "@uniswap/v3-core/contracts/libraries/FixedPoint96.sol";
import "@uniswap/v3-core/contracts/libraries/FullMath.sol";
import "@chainlink/contracts/src/v0.7/interfaces/AggregatorV2V3Interface.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract Oracle is Ownable {
address public weth9;
uint32 public intervalShort;
uint32 public intervalLong;
uint256 public flexFeeThreshold;
constructor(
address _weth9,
uint32 _intervalShort,
uint32 _intervalLong,
uint256 _flexFeeThreshold
) {
weth9 = _weth9;
intervalShort = _intervalShort;
intervalLong = _intervalLong;
flexFeeThreshold = _flexFeeThreshold;
}
event IntervalShort(uint32 _intervalShort);
event IntervalLong(uint32 _intervalLong);
event FlexFeeThreshold(uint256 _flexFeeThreshold);
function setIntervalShort(uint32 _interval) external onlyOwner {
require(_interval > 0, "Interval must be nonzero");
intervalShort = _interval;
emit IntervalShort(intervalShort);
}
function setIntervalLong(uint32 _interval) external onlyOwner {
require(_interval > intervalShort, "intervalLong must be greater than intervalShort");
intervalLong = _interval;
emit IntervalLong(intervalLong);
}
function setFlexFeeThreshold(uint256 _flexFeeThreshold) external onlyOwner {
require(_flexFeeThreshold <= 1e18, "Flex fee threshold too high");
flexFeeThreshold = _flexFeeThreshold;
emit FlexFeeThreshold(flexFeeThreshold);
}
//Helper function to fetch average price over an interval
//Will revert if interval is older than oldest pool observation
function getV3SqrtPriceAvg (address uniswapV3Pool, uint32 _interval) public view returns (uint160 sqrtPriceX96) {
require(_interval > 0, "interval cannot be zero");
//Returns TWAP prices for short and long intervals
uint32[] memory secondsAgo = new uint32[](2);
secondsAgo[0] = _interval; // from (before)
secondsAgo[1] = 0; // to (now)
(int56[] memory tickCumulatives, ) = IUniswapV3Pool(uniswapV3Pool).observe(secondsAgo);
// tick(imprecise as it's an integer) to sqrtPriceX96
return TickMath.getSqrtRatioAtTick(int24((tickCumulatives[1] - tickCumulatives[0]) / _interval));
}
//Helper function to filter invalid Chainlink feeds ie 0 timestamp, invalid round IDs
function chainlinkPrice(address _chainlinkFeed) public view returns (uint256) {
(uint80 roundID, int256 price, , uint256 timeStamp, uint80 answeredInRound) = AggregatorV3Interface(_chainlinkFeed).latestRoundData();
require(answeredInRound >= roundID, "Answer given before round");
require(timeStamp != 0, "Invalid timestamp");
require(price > 0, "Price must be greater than 0");
return uint256(price);
}
function getV3SqrtPrice(address uniswapV3Pool, uint32 _intervalShort, uint32 _intervalLong) public view returns (uint160 sqrtPriceX96) {
require(_intervalLong > intervalShort, "intervalLong must be longer than intervalShort");
//Spot price of the token
(uint160 sqrtPriceX96Spot, , , , , , ) = IUniswapV3Pool(uniswapV3Pool).slot0();
//TWAP prices for short and long intervals
uint160 sqrtPriceX96Short = getV3SqrtPriceAvg(uniswapV3Pool, _intervalShort);
uint160 sqrtPriceX96Long = getV3SqrtPriceAvg(uniswapV3Pool, _intervalLong);
//Takes the lowest token price denominated in WETH
//Condition checks to see if WETH is in denominator of pair, ie: token1/token0
if (IUniswapV3Pool(uniswapV3Pool).token0() == weth9) {
sqrtPriceX96 = (sqrtPriceX96Long >= sqrtPriceX96Short ? sqrtPriceX96Long : sqrtPriceX96Short);
sqrtPriceX96 = (sqrtPriceX96 >= sqrtPriceX96Spot ? sqrtPriceX96 : sqrtPriceX96Spot);
} else {
sqrtPriceX96 = (sqrtPriceX96Long <= sqrtPriceX96Short ? sqrtPriceX96Long : sqrtPriceX96Short);
sqrtPriceX96 = (sqrtPriceX96 <= sqrtPriceX96Spot ? sqrtPriceX96 : sqrtPriceX96Spot);
}
return sqrtPriceX96;
}
function getV3SqrtPriceSimpleShift(address uniswapV3Pool, uint32 _intervalShort, uint32 _intervalLong) public view returns (uint160 sqrtPriceX96) {
require(_intervalLong > intervalShort, "intervalLong must be longer than intervalShort");
//Spot price of the token
(uint160 sqrtPriceX96Spot, , , , , , ) = IUniswapV3Pool(uniswapV3Pool).slot0();
//TWAP prices for short and long intervals
uint160 sqrtPriceX96Short = getV3SqrtPriceAvg(uniswapV3Pool, _intervalShort);
uint160 sqrtPriceX96Long = getV3SqrtPriceAvg(uniswapV3Pool, _intervalLong);
//Takes the lowest token price denominated in WETH
//Condition checks to see if WETH is in denominator of pair, ie: token1/token0
if (IUniswapV3Pool(uniswapV3Pool).token0() == weth9) {
sqrtPriceX96 = (sqrtPriceX96Long <= sqrtPriceX96Short ? sqrtPriceX96Long : sqrtPriceX96Short);
sqrtPriceX96 = (sqrtPriceX96 <= sqrtPriceX96Spot ? sqrtPriceX96 : sqrtPriceX96Spot);
} else {
sqrtPriceX96 = (sqrtPriceX96Long >= sqrtPriceX96Short ? sqrtPriceX96Long : sqrtPriceX96Short);
sqrtPriceX96 = (sqrtPriceX96 >= sqrtPriceX96Spot ? sqrtPriceX96 : sqrtPriceX96Spot);
}
return sqrtPriceX96;
}
function getTokensForAmount(address _pool, uint32 _intervalShort, uint32 _intervalLong, address _chainlinkFeed, uint256 _amount, address _weth9) public view returns (uint256) {
uint160 sqrtPriceX96 = getV3SqrtPrice(_pool, _intervalShort, _intervalLong);
uint256 numerator = (IUniswapV3Pool(_pool).token0() == _weth9 ? sqrtPriceX96 : FixedPoint96.Q96);
uint256 denominator = (numerator == sqrtPriceX96 ? FixedPoint96.Q96 : sqrtPriceX96);
//XFT per ETH, times _amount
uint256 xftPerETH = FullMath.mulDiv(FullMath.mulDiv(numerator, numerator, denominator), _amount, denominator);
if (_chainlinkFeed == address(0)) return xftPerETH;
uint256 linkFeed = chainlinkPrice(_chainlinkFeed);
uint256 decimalPrecision = AggregatorV3Interface(_chainlinkFeed).decimals();
uint256 tokensForAmount;
//if ETH is on the left side of the fraction in the price feed
if (ethLeftSide(_chainlinkFeed)) {
tokensForAmount = FullMath.mulDiv(xftPerETH, 10**decimalPrecision, linkFeed);
} else {
tokensForAmount = FullMath.mulDiv(xftPerETH, linkFeed, 10**decimalPrecision);
}
return tokensForAmount;
}
function getTokensForAmountSimpleShift(address _pool, uint32 _intervalShort, uint32 _intervalLong, address _chainlinkFeed, uint256 _amount, address _weth9) public view returns (uint256) {
uint160 sqrtPriceX96 = getV3SqrtPriceSimpleShift(_pool, _intervalShort, _intervalLong);
uint256 numerator = (IUniswapV3Pool(_pool).token0() == _weth9 ? sqrtPriceX96 : FixedPoint96.Q96);
uint256 denominator = (numerator == sqrtPriceX96 ? FixedPoint96.Q96 : sqrtPriceX96);
//XFT per ETH, times _amount
uint256 xftPerETH = FullMath.mulDiv(FullMath.mulDiv(numerator, numerator, denominator), _amount, denominator);
if (_chainlinkFeed == address(0)) return xftPerETH;
uint256 linkFeed = chainlinkPrice(_chainlinkFeed);
uint256 decimalPrecision = AggregatorV3Interface(_chainlinkFeed).decimals();
uint256 tokensForAmount;
//if ETH is on the left side of the fraction in the price feed
if (ethLeftSide(_chainlinkFeed)) {
tokensForAmount = FullMath.mulDiv(xftPerETH, 10**decimalPrecision, linkFeed);
} else {
tokensForAmount = FullMath.mulDiv(xftPerETH, linkFeed, 10**decimalPrecision);
}
return tokensForAmount;
}
// Uses mulDivRoundingUp instead of mulDiv. Will round up number of XFT to be burned.
function getTokensForAmountCeiling(address _pool, uint32 _intervalShort, uint32 _intervalLong, address _chainlinkFeed, uint256 _amount, address _weth9) public view returns (uint256) {
uint160 sqrtPriceX96 = getV3SqrtPrice(_pool, _intervalShort, _intervalLong);
uint256 numerator = (IUniswapV3Pool(_pool).token0() == _weth9 ? sqrtPriceX96 : FixedPoint96.Q96);
uint256 denominator = (numerator == sqrtPriceX96 ? FixedPoint96.Q96 : sqrtPriceX96);
//XFT per ETH, times _amount
uint256 xftPerETH = FullMath.mulDivRoundingUp(FullMath.mulDivRoundingUp(numerator, numerator, denominator), _amount, denominator);
if (_chainlinkFeed == address(0)) return xftPerETH;
uint256 linkFeed = chainlinkPrice(_chainlinkFeed);
uint256 decimalPrecision = AggregatorV3Interface(_chainlinkFeed).decimals();
uint256 tokensForAmount;
//if ETH is on the left side of the fraction in the price feed
if (ethLeftSide(_chainlinkFeed)) {
tokensForAmount = FullMath.mulDivRoundingUp(xftPerETH, 10**decimalPrecision, linkFeed);
} else {
tokensForAmount = FullMath.mulDivRoundingUp(xftPerETH, linkFeed, 10**decimalPrecision);
}
return tokensForAmount;
}
function getTokensRaw(address _xftPool, address _tokenPool, uint32 _intervalShort, uint32 _intervalLong, uint256 _amount, address _weth9) public view returns (uint256) {
uint160 xftSqrtPriceX96 = getV3SqrtPriceSimpleShift(_xftPool, _intervalShort, _intervalLong);
uint160 tokenSqrtPriceX96 = getV3SqrtPrice(_tokenPool, _intervalShort, _intervalLong);
uint256 xftA;
uint256 xftPrice;
uint256 tokenA;
uint256 tokenPrice;
// xftPrice is XFT/ETH
if (IUniswapV3Pool(_xftPool).token0() == _weth9) {
xftA = FullMath.mulDiv(xftSqrtPriceX96, xftSqrtPriceX96, FixedPoint96.Q96);
xftPrice = FullMath.mulDiv(xftA, 1e18, FixedPoint96.Q96);
} else {
xftA = FullMath.mulDiv(FixedPoint96.Q96, FixedPoint96.Q96, xftSqrtPriceX96);
xftPrice = FullMath.mulDiv(xftA, 1e18, xftSqrtPriceX96);
}
// tokenPrice is ETH/Token
if (IUniswapV3Pool(_tokenPool).token1() == _weth9) {
tokenA = FullMath.mulDiv(tokenSqrtPriceX96, tokenSqrtPriceX96, FixedPoint96.Q96);
tokenPrice = FullMath.mulDiv(tokenA, _amount, FixedPoint96.Q96);
} else {
tokenA = FullMath.mulDiv(FixedPoint96.Q96, FixedPoint96.Q96, tokenSqrtPriceX96);
tokenPrice = FullMath.mulDiv(tokenA, _amount, tokenSqrtPriceX96);
}
//Multiplying xftPrice by tokenPrice and dividing by 1e18
//In other words, XFT * amount / Tokens -> Number of XFT to mint for a given amount
return FullMath.mulDiv(xftPrice, tokenPrice, 1e18);
}
function isTokenBelowThreshold(uint256 _threshold, address _pool, uint32 _intervalShort, uint32 _intervalLong, address _chainlinkFeed, address _weth9) public view returns (bool) {
uint160 sqrtPriceX96 = getV3SqrtPrice(_pool, _intervalShort, _intervalLong);
//We want _weth9 as our price feed's numerator for the multiplications below
uint256 numerator = (IUniswapV3Pool(_pool).token1() == _weth9 ? sqrtPriceX96 : FixedPoint96.Q96);
uint256 denominator = (numerator == sqrtPriceX96 ? FixedPoint96.Q96 : sqrtPriceX96);
//ETH per Token, times 1e18
uint256 ethPerToken = FullMath.mulDiv(FullMath.mulDiv(numerator, numerator, denominator), 1e18, denominator);
if (_chainlinkFeed == address(0)) return ethPerToken < _threshold;
uint256 linkFeed = chainlinkPrice(_chainlinkFeed);
uint256 decimalPrecision = AggregatorV3Interface(_chainlinkFeed).decimals();
uint256 tokensForAmount;
//if ETH is on the left side of the fraction in the price feed
if (ethLeftSide(_chainlinkFeed)) {
tokensForAmount = FullMath.mulDiv(ethPerToken, linkFeed, 10**decimalPrecision);
} else {
tokensForAmount = FullMath.mulDiv(ethPerToken, 10**decimalPrecision, linkFeed);
}
return tokensForAmount < _threshold;
}
//Uses getTokensForAmountCeiling to round up number of XFT burned
function getCost(uint256 _amount, address _chainlinkFeed, address _xftPool) public view returns (uint256) {
return getTokensForAmountCeiling(_xftPool, intervalShort, intervalLong, _chainlinkFeed, _amount, weth9);
}
function getCostSimpleShift(uint256 _amount, address _chainlinkFeed, address _xftPool, address _tokenPool) public view returns (uint256) {
uint256 _output;
if (isTokenBelowThreshold(flexFeeThreshold, _tokenPool, intervalShort, intervalLong, _chainlinkFeed, weth9)) {
_output = getTokensRaw(_xftPool, _tokenPool, intervalShort, intervalLong, _amount, weth9);
} else {
_output = getTokensForAmountSimpleShift(_xftPool, intervalShort, intervalLong, _chainlinkFeed, _amount, weth9);
}
return _output;
}
// Check if _chainlinkFeed.description() starts with "ETH"
function ethLeftSide(address _chainlinkFeed) internal view returns (bool) {
if (_chainlinkFeed == address(0)) return true;
string memory description = AggregatorV3Interface(_chainlinkFeed).description();
bytes memory descriptionBytes = bytes(description);
bytes memory ethBytes = bytes("ETH");
for (uint i = 0; i < 3; i++) if (descriptionBytes[i] != ethBytes[i]) return false;
return true;
}
}
\ No newline at end of file
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import "./Interfaces/IStorage.sol";
import "./MerkleTreeWithHistory.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
interface IVerifier {
function verifyProof(bytes memory _proof, uint256[6] memory _input) external returns (bool);
}
abstract contract Shifter is MerkleTreeWithHistory, ReentrancyGuard {
IVerifier public immutable verifier;
IStorage public immutable noteStorage;
uint256 public immutable denomination;
uint256 public immutable ethDenomination;
bytes32[] public allCommitments;
uint256 public allCommitmentsLength = allCommitments.length;
mapping(bytes32 => bool) public nullifierHashes;
// we store all commitments just to prevent accidental deposits with the same commitment
mapping(bytes32 => bool) public commitments;
event Deposit(bytes32 indexed commitment, uint32 leafIndex, uint256 timestamp, address indexed depositor, uint256 ethDenomination, uint256 denomination);
event Withdrawal(address to, bytes32 nullifierHash, address indexed relayer, uint256 fee);
/**
@dev The constructor
@param _verifier the address of SNARK verifier for this contract
@param _hasher the address of MiMC hash contract
@param _storage the address of the storage contract
@param _denomination transfer amount for each deposit
@param _merkleTreeHeight the height of deposits' Merkle Tree
@param _ethDenomination the amount of ETH to be deposted
*/
constructor(
IVerifier _verifier,
IHasher _hasher,
IStorage _storage,
uint256 _denomination,
uint256 _ethDenomination,
uint32 _merkleTreeHeight
) MerkleTreeWithHistory(_merkleTreeHeight, _hasher) {
require(_denomination > 0, "denomination should be greater than 0");
noteStorage = _storage;
verifier = _verifier;
denomination = _denomination;
ethDenomination = _ethDenomination;
}
/**
@dev Return the entire commitment array
*/
function commitmentList() public view returns (bytes32[] memory) {
return allCommitments;
}
/**
@dev Deposit funds into the contract. The caller must send (for ETH) or approve (for ERC20) value equal to or `denomination` of this instance.
@param _commitment the note commitment, which is PedersenHash(nullifier + secret)
@param _encryptedNote encrypted preimage, use xcrypt view for symmetrical encryption (unchecked)
@param _passwordHash the hash of the password (unchecked)
*/
function deposit(bytes32 _commitment, bytes calldata _encryptedNote, bytes32 _passwordHash) external payable nonReentrant {
require(msg.value == ethDenomination, "Incorrect deposit value"); // Require fee for gas
require(!commitments[_commitment], "The commitment has been submitted");
uint32 insertedIndex = _insert(_commitment);
allCommitments.push(_commitment);
noteStorage.store(msg.sender, _encryptedNote, _passwordHash);
commitments[_commitment] = true;
_processDeposit();
emit Deposit(_commitment, insertedIndex, block.timestamp, msg.sender, msg.value, denomination);
}
/** @dev this function is defined in a child contract */
function _processDeposit() internal virtual;
/**
@dev Withdraw a deposit from the contract. `proof` is a zkSNARK proof data, and input is an array of circuit public inputs
`input` array consists of:
- merkle root of all deposits in the contract
- hash of unique deposit nullifier to prevent double spends
- the recipient of funds
- optional fee that goes to the transaction sender (usually a relay)
*/
function withdraw(
bytes calldata _proof,
bytes32 _root,
bytes32 _nullifierHash,
address payable _recipient,
address payable _relayer,
uint256 _fee,
uint256 _refund
) external payable nonReentrant {
require(_fee <= denomination, "Fee exceeds transfer value");
require(!nullifierHashes[_nullifierHash], "The note has been already spent");
require(isKnownRoot(_root), "Cannot find your merkle root"); // Make sure to use a recent one
require(
verifier.verifyProof(
_proof,
[uint256(_root), uint256(_nullifierHash), uint256(_recipient), uint256(_relayer), _fee, _refund]
),
"Invalid withdraw proof"
);
nullifierHashes[_nullifierHash] = true;
_processWithdraw(_recipient, _relayer, _fee, _refund);
emit Withdrawal(_recipient, _nullifierHash, _relayer, _fee);
}
/** @dev this function is defined in a child contract */
function _processWithdraw(
address payable _recipient,
address payable _relayer,
uint256 _fee,
uint256 _refund
) internal virtual;
/** @dev whether a note is already spent */
function isSpent(bytes32 _nullifierHash) public view returns (bool) {
return nullifierHashes[_nullifierHash];
}
/** @dev whether an array of notes is already spent */
function isSpentArray(bytes32[] calldata _nullifierHashes) external view returns (bool[] memory spent) {
uint256 nullifierHashesLength = _nullifierHashes.length;
spent = new bool[](nullifierHashesLength);
for (uint256 i; i < nullifierHashesLength; i++) {
if (isSpent(_nullifierHashes[i])) {
spent[i] = true;
}
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
pragma abicoder v2;
import "@openzeppelin/contracts/utils/EnumerableSet.sol";
contract Storage {
using EnumerableSet for EnumerableSet.AddressSet;
// Shifter -> Depositor -> PasswordHash -> EncryptedNote
mapping(address => mapping(address => mapping(bytes32 => bytes[]))) public notes;
EnumerableSet.AddressSet internal shifterContracts;
// Owner cannot be changed
address public immutable owner;
event ShifterContract(address shifterContract);
event Store(address depositor, bytes encryptedNote, bytes32 passwordHash);
constructor(address _shifterContract) {
shifterContracts.add(_shifterContract);
owner = msg.sender;
}
modifier onlyOwner {
require(msg.sender == owner, "Message sender is not the owner");
_;
}
function addShifter(address _shifterContract) external onlyOwner {
shifterContracts.add(_shifterContract);
emit ShifterContract(_shifterContract);
}
function store(address _depositor, bytes calldata _encryptedNote, bytes32 _passwordHash) external {
require(shifterContracts.contains(msg.sender), "Only verified shifters can store notes.");
require(_encryptedNote.length == 62, "Note size is incorrect.");
notes[msg.sender][_depositor][_passwordHash].push(_encryptedNote);
emit Store(_depositor, _encryptedNote, _passwordHash);
}
function getLatestDeposit(address _shifter, address _depositor, bytes32 _passwordHash) public view returns (bytes memory result) {
require(notes[_shifter][_depositor][_passwordHash].length > 0, "Address has no deposits");
return notes[_shifter][_depositor][_passwordHash][notes[_shifter][_depositor][_passwordHash].length - 1];
}
function getDeposits(address _shifter, address _depositor, bytes32 _passwordHash) external view returns (bytes[] memory result) {
return notes[_shifter][_depositor][_passwordHash];
}
function getDepositsLength(address _shifter, address _depositor, bytes32 _passwordHash) external view returns (uint256 length) {
return notes[_shifter][_depositor][_passwordHash].length;
}
function getDepositByIndex(address _shifter, address _depositor, bytes32 _passwordHash, uint256 _index) external view returns (bytes memory result) {
return notes[_shifter][_depositor][_passwordHash][_index];
}
function getShifterCount() public view returns (uint256 count) {
return shifterContracts.length();
}
function getShifterAtIndex(uint256 index) public view returns (address shifter) {
return shifterContracts.at(index);
}
function isShifter(address _shifterContract) public view returns (bool isShifter) {
return shifterContracts.contains(_shifterContract);
}
function getAllShifters() public view returns (address[] memory shifters) {
shifters = new address[](getShifterCount());
for (uint256 i = 0; i < getShifterCount(); i++) {
shifters[i] = getShifterAtIndex(i);
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import "@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "./Interfaces/ElasticIERC20.sol";
contract TokenSwap is Ownable {
ERC20Burnable public immutable xftOld;
ElasticIERC20 public immutable xftNew;
bool public active;
address public contract_address;
constructor(ERC20Burnable _xftOld, ElasticIERC20 _xftNew) {
xftOld = _xftOld;
xftNew = _xftNew;
active = false;
contract_address = address(0x0);
}
event XFTUpgraded(address user, uint256 amount);
function upgrade() public {
if (!active) {
require(msg.sender == contract_address, "You can't upgrade yet");
}
uint256 amount = xftOld.balanceOf(msg.sender);
require(amount > 0, "User has no tokens");
require(xftOld.allowance(msg.sender, address(this)) >= amount, "User must approve tokens first");
xftOld.transferFrom(msg.sender, address(this), amount); //Tokens need to be transferred
require(xftOld.balanceOf(address(this)) >= amount, "Insufficient balance");
xftOld.burn(amount);
xftNew.mint(msg.sender, amount);
emit XFTUpgraded(msg.sender, amount);
}
function setActive() onlyOwner public{
require(!active, "switch already triggered");
active = true;
}
function setContract(address _contract) onlyOwner public {
require(!active, "switch already triggered");
contract_address = _contract;
}
}
\ No newline at end of file
// SPDX-License-Identifier: MIT
// Copyright 2017 Christian Reitwiessner
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
// 2019 OKIMS
pragma solidity ^0.7.0;
library Pairing {
uint256 constant PRIME_Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
struct G1Point {
uint256 X;
uint256 Y;
}
// Encoding of field elements is: X[0] * z + X[1]
struct G2Point {
uint256[2] X;
uint256[2] Y;
}
/*
* @return The negation of p, i.e. p.plus(p.negate()) should be zero.
*/
function negate(G1Point memory p) internal pure returns (G1Point memory) {
// The prime q in the base field F_q for G1
if (p.X == 0 && p.Y == 0) {
return G1Point(0, 0);
} else {
return G1Point(p.X, PRIME_Q - (p.Y % PRIME_Q));
}
}
/*
* @return r the sum of two points of G1
*/
function plus(
G1Point memory p1,
G1Point memory p2
) internal view returns (G1Point memory r) {
uint256[4] memory input;
input[0] = p1.X;
input[1] = p1.Y;
input[2] = p2.X;
input[3] = p2.Y;
bool success;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60)
// Use "invalid" to make gas estimation work
switch success case 0 { invalid() }
}
require(success, "pairing-add-failed");
}
/*
* @return r 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.
*/
function scalar_mul(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;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60)
// Use "invalid" to make gas estimation work
switch success case 0 { invalid() }
}
require(success, "pairing-mul-failed");
}
/* @return 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.
*/
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) {
G1Point[4] memory p1 = [a1, b1, c1, d1];
G2Point[4] memory p2 = [a2, b2, c2, d2];
uint256 inputSize = 24;
uint256[] memory input = new uint256[](inputSize);
for (uint256 i = 0; i < 4; i++) {
uint256 j = i * 6;
input[j + 0] = p1[i].X;
input[j + 1] = p1[i].Y;
input[j + 2] = p2[i].X[0];
input[j + 3] = p2[i].X[1];
input[j + 4] = p2[i].Y[0];
input[j + 5] = p2[i].Y[1];
}
uint256[1] memory out;
bool success;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
// Use "invalid" to make gas estimation work
switch success case 0 { invalid() }
}
require(success, "pairing-opcode-failed");
return out[0] != 0;
}
}
contract Verifier {
uint256 constant SNARK_SCALAR_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
uint256 constant PRIME_Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
using Pairing for *;
struct VerifyingKey {
Pairing.G1Point alfa1;
Pairing.G2Point beta2;
Pairing.G2Point gamma2;
Pairing.G2Point delta2;
Pairing.G1Point[7] IC;
}
struct Proof {
Pairing.G1Point A;
Pairing.G2Point B;
Pairing.G1Point C;
}
function verifyingKey() internal pure returns (VerifyingKey memory vk) {
vk.alfa1 = Pairing.G1Point(uint256(20692898189092739278193869274495556617788530808486270118371701516666252877969), uint256(11713062878292653967971378194351968039596396853904572879488166084231740557279));
vk.beta2 = Pairing.G2Point([uint256(12168528810181263706895252315640534818222943348193302139358377162645029937006), uint256(281120578337195720357474965979947690431622127986816839208576358024608803542)], [uint256(16129176515713072042442734839012966563817890688785805090011011570989315559913), uint256(9011703453772030375124466642203641636825223906145908770308724549646909480510)]);
vk.gamma2 = Pairing.G2Point([uint256(11559732032986387107991004021392285783925812861821192530917403151452391805634), uint256(10857046999023057135944570762232829481370756359578518086990519993285655852781)], [uint256(4082367875863433681332203403145435568316851327593401208105741076214120093531), uint256(8495653923123431417604973247489272438418190587263600148770280649306958101930)]);
vk.delta2 = Pairing.G2Point([uint256(15208606214570874334261746446200199033462678344026185572194543686340588455376), uint256(4201549448713475441700620943818443847551216333428073482030847716518222239220)], [uint256(12504569504683876560222890118098094502189148489042391982838401316936649733743), uint256(21452163545381790963880582203990389540905251758607075975866466960090453123328)]);
vk.IC[0] = Pairing.G1Point(uint256(16225148364316337376768119297456868908427925829817748684139175309620217098814), uint256(5167268689450204162046084442581051565997733233062478317813755636162413164690));
vk.IC[1] = Pairing.G1Point(uint256(12882377842072682264979317445365303375159828272423495088911985689463022094260), uint256(19488215856665173565526758360510125932214252767275816329232454875804474844786));
vk.IC[2] = Pairing.G1Point(uint256(13083492661683431044045992285476184182144099829507350352128615182516530014777), uint256(602051281796153692392523702676782023472744522032670801091617246498551238913));
vk.IC[3] = Pairing.G1Point(uint256(9732465972180335629969421513785602934706096902316483580882842789662669212890), uint256(2776526698606888434074200384264824461688198384989521091253289776235602495678));
vk.IC[4] = Pairing.G1Point(uint256(8586364274534577154894611080234048648883781955345622578531233113180532234842), uint256(21276134929883121123323359450658320820075698490666870487450985603988214349407));
vk.IC[5] = Pairing.G1Point(uint256(4910628533171597675018724709631788948355422829499855033965018665300386637884), uint256(20532468890024084510431799098097081600480376127870299142189696620752500664302));
vk.IC[6] = Pairing.G1Point(uint256(15335858102289947642505450692012116222827233918185150176888641903531542034017), uint256(5311597067667671581646709998171703828965875677637292315055030353779531404812));
}
/*
* @returns Whether the proof is valid given the hardcoded verifying key
* above and the public inputs
*/
function verifyProof(
bytes memory proof,
uint256[6] memory input
) public view returns (bool) {
uint256[8] memory p = abi.decode(proof, (uint256[8]));
// Make sure that each element in the proof is less than the prime q
for (uint8 i = 0; i < p.length; i++) {
require(p[i] < PRIME_Q, "verifier-proof-element-gte-prime-q");
}
Proof memory _proof;
_proof.A = Pairing.G1Point(p[0], p[1]);
_proof.B = Pairing.G2Point([p[2], p[3]], [p[4], p[5]]);
_proof.C = Pairing.G1Point(p[6], p[7]);
VerifyingKey memory vk = verifyingKey();
// Compute the linear combination vk_x
Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0);
vk_x = Pairing.plus(vk_x, vk.IC[0]);
// Make sure that every input is less than the snark scalar field
for (uint256 i = 0; i < input.length; i++) {
require(input[i] < SNARK_SCALAR_FIELD, "verifier-gte-snark-scalar-field");
vk_x = Pairing.plus(vk_x, Pairing.scalar_mul(vk.IC[i + 1], input[i]));
}
return Pairing.pairing(
Pairing.negate(_proof.A),
_proof.B,
vk.alfa1,
vk.beta2,
vk_x,
vk.gamma2,
_proof.C,
vk.delta2
);
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment