Transactions
Token Transfers
Tokens
Internal Transactions
Coin Balance History
Logs
Code
Read Contract
Write Contract
Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB 0x207288d32a108dfa89733b6e7354da0a8e768973.
All metadata displayed below is from that contract. In order to verify current contract, click Verify & Publish button
Verify & Publish
All metadata displayed below is from that contract. In order to verify current contract, click Verify & Publish button
- Contract name:
- SparkSwapPair
- Optimization enabled
- true
- Compiler version
- v0.8.19+commit.7dd6d404
- Optimization runs
- 200
- Verified at
- 2023-11-03T15:25:35.849673Z
contracts/SparkSwapPair.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.19;
import "./SparkSwapERC20.sol";
import "./abstracts/Constants.sol";
import "./interfaces/IERC20.sol";
import "./interfaces/ISparkSwapPair.sol";
import "./interfaces/ISparkSwapFactory.sol";
import "./interfaces/ISparkSwapCallee.sol";
import "./libraries/Math.sol";
import "./libraries/UQ112x112.sol";
contract SparkSwapPair is ISparkSwapPair, SparkSwapERC20, Constants {
using UQ112x112 for uint224;
bytes4 private constant SELECTOR = bytes4(keccak256(bytes("transfer(address,uint256)")));
uint256 public constant MINIMUM_LIQUIDITY = 10 ** 3;
uint256 public constant MAX_FEE = 100;
uint256 public constant MAX_PROTOCOL_SHARE = 100;
uint256 public fee;
uint256 public protocolShare;
address public factory;
address public token0;
address public token1;
uint256 public price0CumulativeLast;
uint256 public price1CumulativeLast;
uint256 public kLast;
uint256 internal decimals0;
uint256 internal decimals1;
uint256 private blockTimestampLast;
uint112 private reserve0;
uint112 private reserve1;
uint256 private unlocked = 1;
function getAmountIn(uint256 amountOut, address tokenIn, address caller) public view returns (uint256 amountIn) {
(uint256 _reserve0, uint256 _reserve1, ) = getReserves();
require(amountOut > 0, "SparkSwapPair: INSUFFICIENT_INPUT_AMOUNT");
require(_reserve0 > 0 && _reserve1 > 0, "SparkSwapPair: INSUFFICIENT_LIQUIDITY");
if (tokenIn == token1) (_reserve0, _reserve1) = (_reserve1, _reserve0);
uint256 fee_ = ISparkSwapFactory(factory).feeWhitelistContains(caller) ? 0 : fee;
uint256 numerator = _reserve0 * amountOut * DIVIDER;
uint256 denominator = (_reserve1 - amountOut) * (DIVIDER - fee_);
amountIn = (numerator / denominator) + 1;
}
function getAmountOut(uint256 amountIn, address tokenIn, address caller) public view returns (uint256 amountOut) {
(uint256 _reserve0, uint256 _reserve1, ) = getReserves();
require(amountIn > 0, "SparkSwapPair: INSUFFICIENT_INPUT_AMOUNT");
require(_reserve0 > 0 && _reserve1 > 0, "SparkSwapPair: INSUFFICIENT_LIQUIDITY");
uint256 fee_ = ISparkSwapFactory(factory).feeWhitelistContains(caller) ? 0 : fee;
if (tokenIn == token1) (_reserve0, _reserve1) = (_reserve1, _reserve0);
uint amountInWithFee = amountIn * (DIVIDER - fee_);
uint numerator = amountInWithFee * _reserve1;
uint denominator = (_reserve0 * DIVIDER) + amountInWithFee;
amountOut = numerator / denominator;
}
function getReserves() public view returns (uint112 _reserve0, uint112 _reserve1, uint256 _blockTimestampLast) {
_reserve0 = reserve0;
_reserve1 = reserve1;
_blockTimestampLast = blockTimestampLast;
}
constructor() {
factory = msg.sender;
}
function burn(address to) external lock returns (uint256 amount0, uint256 amount1) {
(uint112 _reserve0, uint112 _reserve1, ) = getReserves();
address _token0 = token0;
address _token1 = token1;
uint256 balance0 = IERC20(_token0).balanceOf(address(this));
uint256 balance1 = IERC20(_token1).balanceOf(address(this));
uint256 liquidity = balanceOf[address(this)];
bool feeOn = _mintFee(_reserve0, _reserve1);
uint256 _totalSupply = totalSupply;
amount0 = (liquidity * balance0) / _totalSupply;
amount1 = (liquidity * balance1) / _totalSupply;
require(amount0 > 0 && amount1 > 0, "SparkSwapPair: INSUFFICIENT_LIQUIDITY_BURNED");
_burn(address(this), liquidity);
_safeTransfer(_token0, to, amount0);
_safeTransfer(_token1, to, amount1);
balance0 = IERC20(_token0).balanceOf(address(this));
balance1 = IERC20(_token1).balanceOf(address(this));
_update(balance0, balance1, _reserve0, _reserve1);
if (feeOn) kLast = uint256(reserve0) * reserve1;
emit Burn(msg.sender, amount0, amount1, to);
}
function initialize(address _token0, address _token1) external onlyFactory {
token0 = _token0;
token1 = _token1;
decimals0 = 10 ** IERC20(_token0).decimals();
decimals1 = 10 ** IERC20(_token1).decimals();
}
function mint(address to) external lock returns (uint256 liquidity) {
(uint112 _reserve0, uint112 _reserve1, ) = getReserves();
uint256 balance0 = IERC20(token0).balanceOf(address(this));
uint256 balance1 = IERC20(token1).balanceOf(address(this));
uint256 amount0 = balance0 - _reserve0;
uint256 amount1 = balance1 - _reserve1;
bool feeOn = _mintFee(_reserve0, _reserve1);
uint256 _totalSupply = totalSupply;
if (_totalSupply == 0) {
liquidity = Math.sqrt(amount0 * amount1) - MINIMUM_LIQUIDITY;
_mint(address(0), MINIMUM_LIQUIDITY);
} else {
liquidity = Math.min((amount0 * _totalSupply) / _reserve0, (amount1 * _totalSupply) / _reserve1);
}
require(liquidity > 0, "SparkSwapPair: INSUFFICIENT_LIQUIDITY_MINTED");
_mint(to, liquidity);
_update(balance0, balance1, _reserve0, _reserve1);
if (feeOn) kLast = uint256(reserve0) * reserve1;
emit Mint(msg.sender, amount0, amount1);
}
function skim(address to) external onlyFactory lock {
address _token0 = token0;
address _token1 = token1;
_safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)) - reserve0);
_safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)) - reserve1);
}
function sync() external lock {
_update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)), reserve0, reserve1);
}
function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) external {
_swap(amount0Out, amount1Out, to, address(0), data);
}
function swapFromPeriphery(
uint256 amount0Out,
uint256 amount1Out,
address to,
address caller,
bytes calldata data
) external {
require(
ISparkSwapFactory(factory).peripheryWhitelistContains(msg.sender),
"SparkSwapPair: Caller is not periphery"
);
_swap(amount0Out, amount1Out, to, caller, data);
}
function updateFee(uint256 fee_) external onlyFactory returns (bool) {
require(fee_ <= MAX_FEE, "SparkSwapFactory: Fee gt MAX_FEE");
fee = fee_;
emit FeeUpdated(fee_);
return true;
}
function updateProtocolShare(uint256 share) external onlyFactory returns (bool) {
require(share <= MAX_PROTOCOL_SHARE, "SparkSwapFactory: Share gt MAX_PROTOCOL_SHARE");
protocolShare = share;
emit ProtocolShareUpdated(share);
return true;
}
function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn) {
address feeTo = ISparkSwapFactory(factory).feeTo();
feeOn = feeTo != address(0) && protocolShare > 0;
uint256 _kLast = kLast;
if (feeOn) {
if (_kLast != 0) {
uint256 rootK = Math.sqrt(uint256(_reserve0) * _reserve1);
uint256 rootKLast = Math.sqrt(_kLast);
if (rootK > rootKLast) {
uint256 numerator = (totalSupply * (rootK - rootKLast)) * protocolShare;
uint256 denominator = (rootK * (MAX_PROTOCOL_SHARE - protocolShare)) + (rootKLast * protocolShare);
uint256 liquidity = numerator / denominator;
if (liquidity > 0) _mint(feeTo, liquidity);
}
}
} else if (_kLast != 0) {
kLast = 0;
}
}
function _safeTransfer(address token, address to, uint256 value) private {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), "SparkSwapPair: TRANSFER_FAILED");
}
function _swap(
uint256 amount0Out,
uint256 amount1Out,
address to,
address caller,
bytes calldata data
) private lock {
ISparkSwapFactory factory_ = ISparkSwapFactory(factory);
require(
factory_.contractsWhitelistContains(address(0)) ||
msg.sender == tx.origin ||
factory_.contractsWhitelistContains(msg.sender),
"SparkSwapPair: Caller is invalid"
);
require(amount0Out > 0 || amount1Out > 0, "SparkSwapPair: INSUFFICIENT_OUTPUT_AMOUNT");
(uint112 _reserve0, uint112 _reserve1, ) = getReserves();
require(amount0Out < _reserve0 && amount1Out < _reserve1, "SparkSwapPair: INSUFFICIENT_LIQUIDITY");
uint256 balance0;
uint256 balance1;
{
address _token0 = token0;
address _token1 = token1;
require(to != _token0 && to != _token1, "SparkSwapPair: INVALID_TO");
if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out);
if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out);
if (data.length > 0) ISparkSwapCallee(to).SparkSwapCall(msg.sender, amount0Out, amount1Out, data);
balance0 = IERC20(_token0).balanceOf(address(this));
balance1 = IERC20(_token1).balanceOf(address(this));
}
uint256 amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;
uint256 amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;
require(amount0In > 0 || amount1In > 0, "SparkSwapPair: INSUFFICIENT_INPUT_AMOUNT");
{
uint256 fee_ = (caller != address(0) && factory_.feeWhitelistContains(caller)) ? 0 : fee;
uint256 balance0Adjusted = (balance0 * DIVIDER) - (amount0In * fee_);
uint256 balance1Adjusted = (balance1 * DIVIDER) - (amount1In * fee_);
require(
balance0Adjusted * balance1Adjusted >= (uint256(_reserve0) * _reserve1) * DIVIDER ** 2,
"SparkSwapPair: K"
);
}
_update(balance0, balance1, _reserve0, _reserve1);
emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
}
function _update(uint256 balance0, uint256 balance1, uint112 _reserve0, uint112 _reserve1) private {
require(balance0 <= type(uint112).max && balance1 <= type(uint112).max, "SparkSwapPair: OVERFLOW");
uint256 blockTimestamp = block.timestamp % 2 ** 32;
uint256 timeElapsed = blockTimestamp - blockTimestampLast;
if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;
price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;
}
reserve0 = uint112(balance0);
reserve1 = uint112(balance1);
blockTimestampLast = blockTimestamp;
emit Sync(reserve0, reserve1);
}
modifier lock() {
require(unlocked == 1, "SparkSwapPair: LOCKED");
unlocked = 0;
_;
unlocked = 1;
}
modifier onlyFactory() {
require(msg.sender == factory, "SparkSwapPair: Caller is not factory");
_;
}
}
contracts/interfaces/ISparkSwapPair.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.19;
import "./ISparkSwapERC20.sol";
interface ISparkSwapPair is ISparkSwapERC20 {
event Mint(address indexed sender, uint256 amount0, uint256 amount1);
event Burn(address indexed sender, uint256 amount0, uint256 amount1, address indexed to);
event FeeUpdated(uint256 fee);
event ProtocolShareUpdated(uint256 share);
event Swap(
address indexed sender,
uint256 amount0In,
uint256 amount1In,
uint256 amount0Out,
uint256 amount1Out,
address indexed to
);
event Sync(uint112 reserve0, uint112 reserve1);
function MINIMUM_LIQUIDITY() external view returns (uint256);
function MAX_FEE() external view returns (uint256);
function MAX_PROTOCOL_SHARE() external view returns (uint256);
function factory() external view returns (address);
function fee() external view returns (uint256);
function protocolShare() external view returns (uint256);
function token0() external view returns (address);
function token1() external view returns (address);
function getAmountOut(uint256 amountIn, address tokenIn, address caller) external view returns (uint256 amountOut);
function getAmountIn(uint256 amountOut, address tokenIn, address caller) external view returns (uint256 amountIn);
function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint256 blockTimestampLast);
function price0CumulativeLast() external view returns (uint256);
function price1CumulativeLast() external view returns (uint256);
function kLast() external view returns (uint256);
function mint(address to) external returns (uint256 liquidity);
function burn(address to) external returns (uint256 amount0, uint256 amount1);
function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) external;
function swapFromPeriphery(
uint256 amount0Out,
uint256 amount1Out,
address to,
address caller,
bytes calldata data
) external;
function skim(address to) external;
function sync() external;
function initialize(address, address) external;
function updateFee(uint256 fee_) external returns (bool);
function updateProtocolShare(uint256 share) external returns (bool);
}
contracts/interfaces/ISparkSwapRouter01.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.19;
interface ISparkSwapRouter01 {
function factory() external view returns (address);
function WETH() external view returns (address);
function addLiquidity(
address tokenA,
address tokenB,
uint256 amountADesired,
uint256 amountBDesired,
uint256 amountAMin,
uint256 amountBMin,
address to,
uint256 deadline
) external returns (uint256 amountA, uint256 amountB, uint256 liquidity);
function addLiquidityETH(
address token,
uint256 amountTokenDesired,
uint256 amountTokenMin,
uint256 amountETHMin,
address to,
uint256 deadline
) external payable returns (uint256 amountToken, uint256 amountETH, uint256 liquidity);
function removeLiquidity(
address tokenA,
address tokenB,
uint256 liquidity,
uint256 amountAMin,
uint256 amountBMin,
address to,
uint256 deadline
) external returns (uint256 amountA, uint256 amountB);
function removeLiquidityETH(
address token,
uint256 liquidity,
uint256 amountTokenMin,
uint256 amountETHMin,
address to,
uint256 deadline
) external returns (uint256 amountToken, uint256 amountETH);
function removeLiquidityWithPermit(
address tokenA,
address tokenB,
uint256 liquidity,
uint256 amountAMin,
uint256 amountBMin,
address to,
uint256 deadline,
bool approveMax,
uint8 v,
bytes32 r,
bytes32 s
) external returns (uint256 amountA, uint256 amountB);
function removeLiquidityETHWithPermit(
address token,
uint256 liquidity,
uint256 amountTokenMin,
uint256 amountETHMin,
address to,
uint256 deadline,
bool approveMax,
uint8 v,
bytes32 r,
bytes32 s
) external returns (uint256 amountToken, uint256 amountETH);
function swapExactTokensForTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external returns (uint256[] memory amounts);
function swapTokensForExactTokens(
uint256 amountOut,
uint256 amountInMax,
address[] calldata path,
address to,
uint256 deadline
) external returns (uint256[] memory amounts);
function swapExactETHForTokens(
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external payable returns (uint256[] memory amounts);
function swapTokensForExactETH(
uint256 amountOut,
uint256 amountInMax,
address[] calldata path,
address to,
uint256 deadline
) external returns (uint256[] memory amounts);
function swapExactTokensForETH(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external returns (uint256[] memory amounts);
function swapETHForExactTokens(
uint256 amountOut,
address[] calldata path,
address to,
uint256 deadline
) external payable returns (uint256[] memory amounts);
function quote(uint256 amountA, uint256 reserveA, uint256 reserveB) external pure returns (uint256 amountB);
function getAmountOut(
uint256 amountIn,
address tokenIn,
address tokenOut,
address caller
) external view returns (uint256 amountOut);
function getAmountIn(
uint256 amountOut,
address tokenIn,
address tokenOut,
address caller
) external view returns (uint256 amountIn);
function getAmountsOut(
uint256 amountIn,
address[] calldata path,
address caller
) external view returns (uint256[] memory amounts);
function getAmountsIn(
uint256 amountOut,
address[] calldata path,
address caller
) external view returns (uint256[] memory amounts);
}
@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/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 {}
}
@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);
}
@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/utils/Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (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;
}
}
@openzeppelin/contracts/utils/structs/EnumerableSet.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping(bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastValue;
// Update the index for the moved value
set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}
contracts/SparkSwapERC20.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.19;
import "./interfaces/ISparkSwapERC20.sol";
contract SparkSwapERC20 is ISparkSwapERC20 {
// keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
string public constant name = "SparkSwap LPs";
string public constant symbol = "SparkSwap-LP";
uint8 public constant decimals = 18;
bytes32 public override DOMAIN_SEPARATOR;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
mapping(address => uint256) public nonces;
constructor() {
uint256 chainId;
assembly {
chainId := chainid()
}
DOMAIN_SEPARATOR = keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256(bytes("1")),
chainId,
address(this)
)
);
}
function _mint(address to, uint256 value) internal {
totalSupply = totalSupply + value;
balanceOf[to] = balanceOf[to] + value;
emit Transfer(address(0), to, value);
}
function _burn(address from, uint256 value) internal {
balanceOf[from] = balanceOf[from] - value;
totalSupply = totalSupply - value;
emit Transfer(from, address(0), value);
}
function _approve(address owner, address spender, uint256 value) private {
allowance[owner][spender] = value;
emit Approval(owner, spender, value);
}
function _transfer(address from, address to, uint256 value) private {
balanceOf[from] = balanceOf[from] - value;
balanceOf[to] = balanceOf[to] + value;
emit Transfer(from, to, value);
}
function approve(address spender, uint256 value) external returns (bool) {
_approve(msg.sender, spender, value);
return true;
}
function transfer(address to, uint256 value) external returns (bool) {
_transfer(msg.sender, to, value);
return true;
}
function transferFrom(address from, address to, uint256 value) external returns (bool) {
if (allowance[from][msg.sender] != type(uint256).max) {
allowance[from][msg.sender] = allowance[from][msg.sender] - value;
}
_transfer(from, to, value);
return true;
}
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external {
require(deadline >= block.timestamp, "SparkSwapERC20: EXPIRED");
bytes32 digest = keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR,
keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
)
);
address recoveredAddress = ecrecover(digest, v, r, s);
require(recoveredAddress != address(0) && recoveredAddress == owner, "SparkSwapERC20: INVALID_SIGNATURE");
_approve(owner, spender, value);
}
}
contracts/SparkSwapFactory.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.19;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "./interfaces/ISparkSwapFactory.sol";
import "./SparkSwapPair.sol";
contract SparkSwapFactory is ISparkSwapFactory, Ownable {
using EnumerableSet for EnumerableSet.AddressSet;
EnumerableSet.AddressSet private _feeWhitelist;
EnumerableSet.AddressSet private _peripheryWhitelist;
EnumerableSet.AddressSet private _contractsWhitelist;
bytes32 public constant INIT_CODE_PAIR_HASH = keccak256(abi.encodePacked(type(SparkSwapPair).creationCode));
uint256 public fee;
address public feeTo;
address public feeToSetter;
uint256 public protocolShare;
address[] public allPairs;
mapping(address => mapping(address => address)) public getPair;
function allPairsLength() external view returns (uint256) {
return allPairs.length;
}
function contractsWhitelistList(uint256 offset, uint256 limit) external view returns (address[] memory output) {
uint256 contractsWhitelistLength = _contractsWhitelist.length();
if (offset >= contractsWhitelistLength) return new address[](0);
uint256 to = offset + limit;
if (contractsWhitelistLength < to) to = contractsWhitelistLength;
output = new address[](to - offset);
for (uint256 i = 0; i < output.length; i++) output[i] = _contractsWhitelist.at(offset + i);
}
function contractsWhitelist(uint256 index) external view returns (address) {
return _contractsWhitelist.at(index);
}
function contractsWhitelistContains(address contract_) external view returns (bool) {
return _contractsWhitelist.contains(contract_);
}
function contractsWhitelistCount() external view returns (uint256) {
return _contractsWhitelist.length();
}
function feeWhitelistList(uint256 offset, uint256 limit) external view returns (address[] memory output) {
uint256 feeWhitelistLength = _feeWhitelist.length();
if (offset >= feeWhitelistLength) return new address[](0);
uint256 to = offset + limit;
if (feeWhitelistLength < to) to = feeWhitelistLength;
output = new address[](to - offset);
for (uint256 i = 0; i < output.length; i++) output[i] = _feeWhitelist.at(offset + i);
}
function feeWhitelist(uint256 index) external view returns (address) {
return _feeWhitelist.at(index);
}
function feeWhitelistContains(address account) external view returns (bool) {
return _feeWhitelist.contains(account);
}
function feeWhitelistCount() external view returns (uint256) {
return _feeWhitelist.length();
}
function peripheryWhitelistList(uint256 offset, uint256 limit) external view returns (address[] memory output) {
uint256 peripheryWhitelistLength = _peripheryWhitelist.length();
if (offset >= peripheryWhitelistLength) return new address[](0);
uint256 to = offset + limit;
if (peripheryWhitelistLength < to) to = peripheryWhitelistLength;
output = new address[](to - offset);
for (uint256 i = 0; i < output.length; i++) output[i] = _peripheryWhitelist.at(offset + i);
}
function peripheryWhitelist(uint256 index) external view returns (address) {
return _peripheryWhitelist.at(index);
}
function peripheryWhitelistContains(address account) external view returns (bool) {
return _peripheryWhitelist.contains(account);
}
function peripheryWhitelistCount() external view returns (uint256) {
return _peripheryWhitelist.length();
}
constructor(address _feeToSetter) {
feeToSetter = _feeToSetter;
}
function createPair(address tokenA, address tokenB) external onlyOwner returns (address pair) {
require(tokenA != tokenB, "SparkSwapFactory: IDENTICAL_ADDRESSES");
(address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
require(token0 != address(0), "SparkSwapFactory: ZERO_ADDRESS");
require(getPair[token0][token1] == address(0), "SparkSwapFactory: PAIR_EXISTS");
bytes memory bytecode = type(SparkSwapPair).creationCode;
bytes32 salt = keccak256(abi.encodePacked(token0, token1));
assembly {
pair := create2(0, add(bytecode, 32), mload(bytecode), salt)
}
ISparkSwapPair pair_ = ISparkSwapPair(pair);
pair_.initialize(token0, token1);
pair_.updateFee(fee);
pair_.updateProtocolShare(protocolShare);
getPair[token0][token1] = pair;
getPair[token1][token0] = pair;
allPairs.push(pair);
emit PairCreated(token0, token1, pair, allPairs.length);
}
function addContractsWhitelist(address[] memory contracts) external onlyOwner returns (bool) {
for (uint256 i = 0; i < contracts.length; i++) {
require(contracts[i] != address(0), "SparkSwapFactory: Contract is zero address");
_contractsWhitelist.add(contracts[i]);
}
emit ContractsWhitelistAdded(contracts);
return true;
}
function addFeeWhitelist(address[] memory accounts) external onlyOwner returns (bool) {
for (uint256 i = 0; i < accounts.length; i++) {
require(accounts[i] != address(0), "SparkSwapFactory: Account is zero address");
_feeWhitelist.add(accounts[i]);
}
emit FeeWhitelistAdded(accounts);
return true;
}
function addPeripheryWhitelist(address[] memory periphery) external onlyOwner returns (bool) {
for (uint256 i = 0; i < periphery.length; i++) {
require(periphery[i] != address(0), "SparkSwapFactory: Periphery is zero address");
_peripheryWhitelist.add(periphery[i]);
}
emit PeripheryWhitelistAdded(periphery);
return true;
}
function removeContractsWhitelist(address[] memory contracts) external onlyOwner returns (bool) {
for (uint256 i = 0; i < contracts.length; i++) {
_contractsWhitelist.remove(contracts[i]);
}
emit ContractsWhitelistRemoved(contracts);
return true;
}
function removeFeeWhitelist(address[] memory accounts) external onlyOwner returns (bool) {
for (uint256 i = 0; i < accounts.length; i++) {
_feeWhitelist.remove(accounts[i]);
}
emit FeeWhitelistRemoved(accounts);
return true;
}
function removePeripheryWhitelist(address[] memory periphery) external onlyOwner returns (bool) {
for (uint256 i = 0; i < periphery.length; i++) {
_peripheryWhitelist.remove(periphery[i]);
}
emit PeripheryWhitelistRemoved(periphery);
return true;
}
function skim(address token0, address token1, address to) external onlyOwner returns (bool) {
require(to != address(0), "SparkSwapFactory: Recipient is zero address");
ISparkSwapPair(getPair[token0][token1]).skim(to);
emit Skimmed(token0, token1, to);
return true;
}
function updateFee(uint256 fee_) external onlyOwner returns (bool) {
fee = fee_;
emit FeeUpdated(fee_);
return true;
}
function updateProtocolShare(uint256 share) external onlyOwner returns (bool) {
protocolShare = share;
emit ProtocolShareUpdated(share);
return true;
}
function updateFeePair(address token0, address token1, uint256 fee_) external onlyOwner returns (bool) {
ISparkSwapPair(getPair[token0][token1]).updateFee(fee_);
emit FeePairUpdated(token0, token1, fee_);
return true;
}
function updateProtocolSharePair(address token0, address token1, uint256 share) external onlyOwner returns (bool) {
ISparkSwapPair(getPair[token0][token1]).updateProtocolShare(share);
emit ProtocolSharePairUpdated(token0, token1, share);
return true;
}
function setFeeTo(address _feeTo) external {
require(msg.sender == feeToSetter, "SparkSwapFactory: FORBIDDEN");
feeTo = _feeTo;
}
function setFeeToSetter(address _feeToSetter) external {
require(msg.sender == feeToSetter, "SparkSwapFactory: FORBIDDEN");
feeToSetter = _feeToSetter;
}
}
contracts/SparkSwapMigrator.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.19;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./interfaces/ISparkSwapRouter01.sol";
import "./interfaces/ISparkSwapFactory.sol";
import "./interfaces/ISparkSwapPair.sol";
import "./libraries/TransferHelper.sol";
contract SparkSwapMigrator {
ISparkSwapRouter01 public previousRouter;
ISparkSwapFactory public previousFactory;
ISparkSwapRouter01 public nextRouter;
ISparkSwapFactory public nextFactory;
constructor(address previousRouter_, address nextRouter_) {
require(previousRouter_ != address(0), "SparkSwapMigrator: Previous router zero");
require(nextRouter_ != address(0), "SparkSwapMigrator: Next router zero");
previousRouter = ISparkSwapRouter01(previousRouter_);
previousFactory = ISparkSwapFactory(previousRouter.factory());
nextRouter = ISparkSwapRouter01(nextRouter_);
nextFactory = ISparkSwapFactory(nextRouter.factory());
}
function migrate(address token0, address token1) external returns (bool) {
address previousPairAddress = previousFactory.getPair(token0, token1);
require(previousPairAddress != address(0), "SparkSwapMigrator: Invalid previous pair");
uint256 availableAmount = ISparkSwapPair(previousPairAddress).balanceOf(msg.sender);
require(availableAmount > 0, "SparkSwapMigrator: Available amount zero");
TransferHelper.safeTransferFrom(previousPairAddress, msg.sender, address(this), availableAmount);
TransferHelper.safeApprove(previousPairAddress, address(previousRouter), availableAmount);
previousRouter.removeLiquidity(token0, token1, availableAmount, 1, 1, address(this), block.timestamp);
uint256 token0Balance = 0;
uint256 token1Balance = 0;
(token0, token1, token0Balance, token1Balance) = _correctiveSwap(token0, token1);
TransferHelper.safeApprove(token0, address(nextRouter), token0Balance);
TransferHelper.safeApprove(token1, address(nextRouter), token1Balance);
nextRouter.addLiquidity(token0, token1, token0Balance, token1Balance, 1, 1, msg.sender, block.timestamp);
_returnAsset(token0);
_returnAsset(token1);
return true;
}
function _correctiveSwap(
address token0_,
address token1_
) private returns (address token0, address token1, uint256 token0Balance, uint256 token1Balance) {
address nextPairAddress = nextFactory.getPair(token0, token1);
require(nextPairAddress != address(0), "SparSwapMigrator: Invalid next pair");
(token0, token1) = token0_ < token1_ ? (token0_, token1_) : (token1_, token0_);
IERC20 token0ERC = IERC20(token0);
IERC20 token1ERC = IERC20(token1);
token0Balance = token0ERC.balanceOf(address(this));
token1Balance = token1ERC.balanceOf(address(this));
(uint256 reserve0, uint256 reserve1, ) = ISparkSwapPair(nextPairAddress).getReserves();
uint256 swapAmount;
address[] memory swapPath = new address[](2);
if (reserve1 > (token1Balance * reserve0) / token0Balance) {
swapPath[0] = token0;
swapPath[1] = token1;
swapAmount = uint256(
(int256(reserve0) * int256(token1Balance * reserve0 - token0Balance * reserve1) * int256(-1)) /
int256(token1Balance * reserve0 + token0Balance * reserve1 + 2 * reserve1 * reserve0)
);
} else {
swapPath[0] = token1;
swapPath[1] = token0;
swapAmount = uint256(
(int256(reserve1) * int256(token0Balance * reserve1 - token1Balance * reserve0)) /
int256(token1Balance * reserve0 + token0Balance * reserve1 + 2 * reserve1 * reserve0)
);
}
TransferHelper.safeApprove(swapPath[0], address(nextRouter), swapAmount);
nextRouter.swapExactTokensForTokens(swapAmount, 1, swapPath, address(this), block.timestamp);
token0Balance = token0ERC.balanceOf(address(this));
token1Balance = token1ERC.balanceOf(address(this));
}
function _returnAsset(address token) private {
uint256 balance = IERC20(token).balanceOf(address(this));
if (balance > 0) TransferHelper.safeTransfer(token, msg.sender, balance);
}
}
contracts/SparkSwapRouter.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.19;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./interfaces/IWETH.sol";
import "./interfaces/ISparkSwapPair.sol";
import "./interfaces/ISparkSwapFactory.sol";
import "./interfaces/ISparkSwapRouter02.sol";
import "./abstracts/Constants.sol";
import "./libraries/TransferHelper.sol";
contract SparkSwapRouter is ISparkSwapRouter02, Constants {
address public immutable override factory;
address public immutable override WETH;
modifier ensure(uint256 deadline) {
require(deadline >= block.timestamp, "SparkSwapRouter: EXPIRED");
_;
}
function quote(uint256 amountA, uint256 reserveA, uint256 reserveB) public pure returns (uint256 amountB) {
require(amountA > 0, "SparkSwapRouter: INSUFFICIENT_AMOUNT");
require(reserveA > 0 && reserveB > 0, "SparkSwapRouter: INSUFFICIENT_LIQUIDITY");
amountB = (amountA * reserveB) / reserveA;
}
function getAmountOut(
uint256 amountIn,
address tokenIn,
address tokenOut,
address caller
) public view returns (uint256 amountOut) {
ISparkSwapPair pair = ISparkSwapPair(pairFor(tokenIn, tokenOut));
return pair.getAmountOut(amountIn, tokenIn, caller);
}
function getAmountIn(
uint256 amountOut,
address tokenIn,
address tokenOut,
address caller
) public view returns (uint256 amountIn) {
ISparkSwapPair pair = ISparkSwapPair(pairFor(tokenIn, tokenOut));
return pair.getAmountIn(amountOut, tokenIn, caller);
}
function getAmountsOut(
uint256 amountIn,
address[] memory path,
address caller
) public view returns (uint256[] memory amounts) {
require(path.length >= 2, "SparkSwapRouter: INVALID_PATH");
amounts = new uint256[](path.length);
amounts[0] = amountIn;
for (uint256 i; i < path.length - 1; i++) {
amounts[i + 1] = getAmountOut(amounts[i], path[i], path[i + 1], caller);
}
}
function getAmountsIn(
uint256 amountOut,
address[] memory path,
address caller
) public view returns (uint256[] memory amounts) {
require(path.length >= 2, "SparkSwapRouter: INVALID_PATH");
amounts = new uint256[](path.length);
amounts[amounts.length - 1] = amountOut;
for (uint256 i = path.length - 1; i > 0; i--) {
amounts[i - 1] = getAmountIn(amounts[i], path[i - 1], path[i], caller);
}
}
function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
require(tokenA != tokenB, "SparkSwapRouter: IDENTICAL_ADDRESSES");
(token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
require(token0 != address(0), "SparkSwapRouter: ZERO_ADDRESS");
}
function pairFor(address tokenA, address tokenB) internal view returns (address pair) {
(address token0, address token1) = sortTokens(tokenA, tokenB);
pair = address(
uint160(
uint256(
keccak256(
abi.encodePacked(
hex"ff",
factory,
keccak256(abi.encodePacked(token0, token1)),
hex"d98d5db929ea0eea7a1e5d6b95fe5a6bca2f96da961c8c025a31487e3354fd80"
)
)
)
)
);
}
function getReserves(address tokenA, address tokenB) internal view returns (uint256 reserveA, uint256 reserveB) {
(address token0, ) = sortTokens(tokenA, tokenB);
(uint256 reserve0, uint256 reserve1, ) = ISparkSwapPair(pairFor(tokenA, tokenB)).getReserves();
(reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
}
constructor(address factory_, address WETH_) {
factory = factory_;
WETH = WETH_;
}
receive() external payable {
assert(msg.sender == WETH);
}
function _addLiquidity(
address tokenA,
address tokenB,
uint256 amountADesired,
uint256 amountBDesired,
uint256 amountAMin,
uint256 amountBMin
) internal virtual returns (uint256 amountA, uint256 amountB) {
require(
ISparkSwapFactory(factory).getPair(tokenA, tokenB) != address(0),
"SparkSwapRouter: Pair is zero address"
);
(uint256 reserveA, uint256 reserveB) = getReserves(tokenA, tokenB);
if (reserveA == 0 && reserveB == 0) {
(amountA, amountB) = (amountADesired, amountBDesired);
} else {
uint256 amountBOptimal = quote(amountADesired, reserveA, reserveB);
if (amountBOptimal <= amountBDesired) {
require(amountBOptimal >= amountBMin, "SparkSwapRouter: INSUFFICIENT_B_AMOUNT");
(amountA, amountB) = (amountADesired, amountBOptimal);
} else {
uint256 amountAOptimal = quote(amountBDesired, reserveB, reserveA);
assert(amountAOptimal <= amountADesired);
require(amountAOptimal >= amountAMin, "SparkSwapRouter: INSUFFICIENT_A_AMOUNT");
(amountA, amountB) = (amountAOptimal, amountBDesired);
}
}
}
function addLiquidity(
address tokenA,
address tokenB,
uint256 amountADesired,
uint256 amountBDesired,
uint256 amountAMin,
uint256 amountBMin,
address to,
uint256 deadline
) external virtual override ensure(deadline) returns (uint256 amountA, uint256 amountB, uint256 liquidity) {
(amountA, amountB) = _addLiquidity(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin);
address pair = pairFor(tokenA, tokenB);
TransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA);
TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB);
liquidity = ISparkSwapPair(pair).mint(to);
}
function addLiquidityETH(
address token,
uint256 amountTokenDesired,
uint256 amountTokenMin,
uint256 amountETHMin,
address to,
uint256 deadline
)
external
payable
virtual
override
ensure(deadline)
returns (uint256 amountToken, uint256 amountETH, uint256 liquidity)
{
(amountToken, amountETH) = _addLiquidity(
token,
WETH,
amountTokenDesired,
msg.value,
amountTokenMin,
amountETHMin
);
address pair = pairFor(token, WETH);
TransferHelper.safeTransferFrom(token, msg.sender, pair, amountToken);
IWETH(WETH).deposit{value: amountETH}();
assert(IWETH(WETH).transfer(pair, amountETH));
liquidity = ISparkSwapPair(pair).mint(to);
if (msg.value > amountETH) TransferHelper.safeTransferETH(msg.sender, msg.value - amountETH);
}
function removeLiquidity(
address tokenA,
address tokenB,
uint256 liquidity,
uint256 amountAMin,
uint256 amountBMin,
address to,
uint256 deadline
) public virtual override ensure(deadline) returns (uint256 amountA, uint256 amountB) {
address pair = pairFor(tokenA, tokenB);
ISparkSwapPair(pair).transferFrom(msg.sender, pair, liquidity);
(uint256 amount0, uint256 amount1) = ISparkSwapPair(pair).burn(to);
(address token0, ) = sortTokens(tokenA, tokenB);
(amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0);
require(amountA >= amountAMin, "SparkSwapRouter: INSUFFICIENT_A_AMOUNT");
require(amountB >= amountBMin, "SparkSwapRouter: INSUFFICIENT_B_AMOUNT");
}
function removeLiquidityETH(
address token,
uint256 liquidity,
uint256 amountTokenMin,
uint256 amountETHMin,
address to,
uint256 deadline
) public virtual override ensure(deadline) returns (uint256 amountToken, uint256 amountETH) {
(amountToken, amountETH) = removeLiquidity(
token,
WETH,
liquidity,
amountTokenMin,
amountETHMin,
address(this),
deadline
);
TransferHelper.safeTransfer(token, to, amountToken);
IWETH(WETH).withdraw(amountETH);
TransferHelper.safeTransferETH(to, amountETH);
}
function removeLiquidityWithPermit(
address tokenA,
address tokenB,
uint256 liquidity,
uint256 amountAMin,
uint256 amountBMin,
address to,
uint256 deadline,
bool approveMax,
uint8 v,
bytes32 r,
bytes32 s
) external virtual override returns (uint256 amountA, uint256 amountB) {
address pair = pairFor(tokenA, tokenB);
uint256 value = approveMax ? type(uint256).max : liquidity;
ISparkSwapPair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);
(amountA, amountB) = removeLiquidity(tokenA, tokenB, liquidity, amountAMin, amountBMin, to, deadline);
}
function removeLiquidityETHWithPermit(
address token,
uint256 liquidity,
uint256 amountTokenMin,
uint256 amountETHMin,
address to,
uint256 deadline,
bool approveMax,
uint8 v,
bytes32 r,
bytes32 s
) external virtual override returns (uint256 amountToken, uint256 amountETH) {
address pair = pairFor(token, WETH);
uint256 value = approveMax ? type(uint256).max : liquidity;
ISparkSwapPair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);
(amountToken, amountETH) = removeLiquidityETH(token, liquidity, amountTokenMin, amountETHMin, to, deadline);
}
function removeLiquidityETHSupportingFeeOnTransferTokens(
address token,
uint256 liquidity,
uint256 amountTokenMin,
uint256 amountETHMin,
address to,
uint256 deadline
) public virtual override ensure(deadline) returns (uint256 amountETH) {
(, amountETH) = removeLiquidity(token, WETH, liquidity, amountTokenMin, amountETHMin, address(this), deadline);
TransferHelper.safeTransfer(token, to, IERC20(token).balanceOf(address(this)));
IWETH(WETH).withdraw(amountETH);
TransferHelper.safeTransferETH(to, amountETH);
}
function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
address token,
uint256 liquidity,
uint256 amountTokenMin,
uint256 amountETHMin,
address to,
uint256 deadline,
bool approveMax,
uint8 v,
bytes32 r,
bytes32 s
) external virtual override returns (uint256 amountETH) {
address pair = pairFor(token, WETH);
uint256 value = approveMax ? type(uint256).max : liquidity;
ISparkSwapPair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);
amountETH = removeLiquidityETHSupportingFeeOnTransferTokens(
token,
liquidity,
amountTokenMin,
amountETHMin,
to,
deadline
);
}
function _swap(
uint256[] memory amounts,
address[] memory path,
address _to
) internal virtual onlyContractsWhitelist {
for (uint256 i; i < path.length - 1; i++) {
(address input, address output) = (path[i], path[i + 1]);
(address token0, ) = sortTokens(input, output);
uint256 amountOut = amounts[i + 1];
(uint256 amount0Out, uint256 amount1Out) = input == token0
? (uint256(0), amountOut)
: (amountOut, uint256(0));
address to = i < path.length - 2 ? pairFor(output, path[i + 2]) : _to;
ISparkSwapPair(pairFor(input, output)).swapFromPeriphery(
amount0Out,
amount1Out,
to,
msg.sender,
new bytes(0)
);
}
}
function swapExactTokensForTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external virtual override ensure(deadline) returns (uint256[] memory amounts) {
amounts = getAmountsOut(amountIn, path, msg.sender);
require(amounts[amounts.length - 1] >= amountOutMin, "SparkSwapRouter: INSUFFICIENT_OUTPUT_AMOUNT");
TransferHelper.safeTransferFrom(path[0], msg.sender, pairFor(path[0], path[1]), amounts[0]);
_swap(amounts, path, to);
}
function swapTokensForExactTokens(
uint256 amountOut,
uint256 amountInMax,
address[] calldata path,
address to,
uint256 deadline
) external virtual override ensure(deadline) returns (uint256[] memory amounts) {
amounts = getAmountsIn(amountOut, path, msg.sender);
require(amounts[0] <= amountInMax, "SparkSwapRouter: EXCESSIVE_INPUT_AMOUNT");
TransferHelper.safeTransferFrom(path[0], msg.sender, pairFor(path[0], path[1]), amounts[0]);
_swap(amounts, path, to);
}
function swapExactETHForTokens(
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external payable virtual override ensure(deadline) returns (uint256[] memory amounts) {
require(path[0] == WETH, "SparkSwapRouter: INVALID_PATH");
amounts = getAmountsOut(msg.value, path, msg.sender);
require(amounts[amounts.length - 1] >= amountOutMin, "SparkSwapRouter: INSUFFICIENT_OUTPUT_AMOUNT");
IWETH(WETH).deposit{value: amounts[0]}();
assert(IWETH(WETH).transfer(pairFor(path[0], path[1]), amounts[0]));
_swap(amounts, path, to);
}
function swapTokensForExactETH(
uint256 amountOut,
uint256 amountInMax,
address[] calldata path,
address to,
uint256 deadline
) external virtual override ensure(deadline) returns (uint256[] memory amounts) {
require(path[path.length - 1] == WETH, "SparkSwapRouter: INVALID_PATH");
amounts = getAmountsIn(amountOut, path, msg.sender);
require(amounts[0] <= amountInMax, "SparkSwapRouter: EXCESSIVE_INPUT_AMOUNT");
TransferHelper.safeTransferFrom(path[0], msg.sender, pairFor(path[0], path[1]), amounts[0]);
_swap(amounts, path, address(this));
IWETH(WETH).withdraw(amounts[amounts.length - 1]);
TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);
}
function swapExactTokensForETH(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external virtual override ensure(deadline) returns (uint256[] memory amounts) {
require(path[path.length - 1] == WETH, "SparkSwapRouter: INVALID_PATH");
amounts = getAmountsOut(amountIn, path, msg.sender);
require(amounts[amounts.length - 1] >= amountOutMin, "SparkSwapRouter: INSUFFICIENT_OUTPUT_AMOUNT");
TransferHelper.safeTransferFrom(path[0], msg.sender, pairFor(path[0], path[1]), amounts[0]);
_swap(amounts, path, address(this));
IWETH(WETH).withdraw(amounts[amounts.length - 1]);
TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);
}
function swapETHForExactTokens(
uint256 amountOut,
address[] calldata path,
address to,
uint256 deadline
) external payable virtual override ensure(deadline) returns (uint256[] memory amounts) {
require(path[0] == WETH, "SparkSwapRouter: INVALID_PATH");
amounts = getAmountsIn(amountOut, path, msg.sender);
require(amounts[0] <= msg.value, "SparkSwapRouter: EXCESSIVE_INPUT_AMOUNT");
IWETH(WETH).deposit{value: amounts[0]}();
assert(IWETH(WETH).transfer(pairFor(path[0], path[1]), amounts[0]));
_swap(amounts, path, to);
if (msg.value > amounts[0]) TransferHelper.safeTransferETH(msg.sender, msg.value - amounts[0]);
}
function _swapSupportingFeeOnTransferTokens(
address[] memory path,
address _to
) internal virtual onlyContractsWhitelist {
for (uint256 i; i < path.length - 1; i++) {
(address input, address output) = (path[i], path[i + 1]);
(address token0, ) = sortTokens(input, output);
ISparkSwapPair pair = ISparkSwapPair(pairFor(input, output));
uint256 amountInput;
uint256 amountOutput;
{
(uint256 reserve0, uint256 reserve1, ) = pair.getReserves();
(uint256 reserveInput, ) = input == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
amountInput = IERC20(input).balanceOf(address(pair)) - reserveInput;
amountOutput = getAmountOut(amountInput, path[i], path[i + 1], msg.sender);
}
(uint256 amount0Out, uint256 amount1Out) = input == token0
? (uint256(0), amountOutput)
: (amountOutput, uint256(0));
address to = i < path.length - 2 ? pairFor(output, path[i + 2]) : _to;
pair.swapFromPeriphery(amount0Out, amount1Out, to, msg.sender, new bytes(0));
}
}
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external virtual override ensure(deadline) {
TransferHelper.safeTransferFrom(path[0], msg.sender, pairFor(path[0], path[1]), amountIn);
uint256 balanceBefore = IERC20(path[path.length - 1]).balanceOf(to);
_swapSupportingFeeOnTransferTokens(path, to);
require(
IERC20(path[path.length - 1]).balanceOf(to) - balanceBefore >= amountOutMin,
"SparkSwapRouter: INSUFFICIENT_OUTPUT_AMOUNT"
);
}
function swapExactETHForTokensSupportingFeeOnTransferTokens(
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external payable virtual override ensure(deadline) {
require(path[0] == WETH, "SparkSwapRouter: INVALID_PATH");
uint256 amountIn = msg.value;
IWETH(WETH).deposit{value: amountIn}();
assert(IWETH(WETH).transfer(pairFor(path[0], path[1]), amountIn));
uint256 balanceBefore = IERC20(path[path.length - 1]).balanceOf(to);
_swapSupportingFeeOnTransferTokens(path, to);
require(
IERC20(path[path.length - 1]).balanceOf(to) - balanceBefore >= amountOutMin,
"SparkSwapRouter: INSUFFICIENT_OUTPUT_AMOUNT"
);
}
function swapExactTokensForETHSupportingFeeOnTransferTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external virtual override ensure(deadline) {
require(path[path.length - 1] == WETH, "SparkSwapRouter: INVALID_PATH");
TransferHelper.safeTransferFrom(path[0], msg.sender, pairFor(path[0], path[1]), amountIn);
_swapSupportingFeeOnTransferTokens(path, address(this));
uint256 amountOut = IERC20(WETH).balanceOf(address(this));
require(amountOut >= amountOutMin, "SparkSwapRouter: INSUFFICIENT_OUTPUT_AMOUNT");
IWETH(WETH).withdraw(amountOut);
TransferHelper.safeTransferETH(to, amountOut);
}
modifier onlyContractsWhitelist() {
ISparkSwapFactory factory_ = ISparkSwapFactory(factory);
require(
factory_.contractsWhitelistContains(address(0)) ||
msg.sender == tx.origin ||
factory_.contractsWhitelistContains(msg.sender),
"SparkSwapRouter: Caller is invalid"
);
_;
}
}
contracts/abstracts/Constants.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.19;
abstract contract Constants {
uint256 public constant DIVIDER = 10000;
}
contracts/interfaces/IERC20.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.19;
interface IERC20 {
event Approval(address indexed owner, address indexed spender, uint256 value);
event Transfer(address indexed from, address indexed to, uint256 value);
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint256);
function balanceOf(address owner) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transfer(address to, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
contracts/interfaces/ISparkSwapCallee.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.19;
interface ISparkSwapCallee {
function SparkSwapCall(address sender, uint256 amount0, uint256 amount1, bytes calldata data) external;
}
contracts/interfaces/ISparkSwapERC20.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.19;
interface ISparkSwapERC20 {
event Approval(address indexed owner, address indexed spender, uint256 value);
event Transfer(address indexed from, address indexed to, uint256 value);
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint256);
function balanceOf(address owner) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transfer(address to, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
function DOMAIN_SEPARATOR() external view returns (bytes32);
function PERMIT_TYPEHASH() external view returns (bytes32);
function nonces(address owner) external view returns (uint256);
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
}
contracts/interfaces/ISparkSwapFactory.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.19;
interface ISparkSwapFactory {
event ContractsWhitelistAdded(address[] contracts);
event ContractsWhitelistRemoved(address[] contracts);
event FeeUpdated(uint256 fee);
event ProtocolShareUpdated(uint256 share);
event FeePairUpdated(address indexed token0, address indexed token1, uint256 fee);
event ProtocolSharePairUpdated(address indexed token0, address indexed token1, uint256 share);
event FeeWhitelistAdded(address[] accounts);
event FeeWhitelistRemoved(address[] accounts);
event PairCreated(address indexed token0, address indexed token1, address pair, uint256);
event PeripheryWhitelistAdded(address[] periphery);
event PeripheryWhitelistRemoved(address[] periphery);
event Skimmed(address indexed token0, address indexed token1, address to);
function INIT_CODE_PAIR_HASH() external view returns (bytes32);
function contractsWhitelistList(uint256 offset, uint256 limit) external view returns (address[] memory output);
function contractsWhitelist(uint256 index) external view returns (address);
function contractsWhitelistContains(address contract_) external view returns (bool);
function contractsWhitelistCount() external view returns (uint256);
function protocolShare() external view returns (uint256);
function fee() external view returns (uint256);
function feeWhitelistList(uint256 offset, uint256 limit) external view returns (address[] memory output);
function feeWhitelist(uint256 index) external view returns (address);
function feeWhitelistContains(address account) external view returns (bool);
function feeWhitelistCount() external view returns (uint256);
function feeTo() external view returns (address);
function feeToSetter() external view returns (address);
function peripheryWhitelistList(uint256 offset, uint256 limit) external view returns (address[] memory output);
function peripheryWhitelist(uint256 index) external view returns (address);
function peripheryWhitelistContains(address account) external view returns (bool);
function peripheryWhitelistCount() external view returns (uint256);
function getPair(address tokenA, address tokenB) external view returns (address pair);
function allPairs(uint256) external view returns (address pair);
function allPairsLength() external view returns (uint256);
function addContractsWhitelist(address[] memory contracts) external returns (bool);
function addFeeWhitelist(address[] memory accounts) external returns (bool);
function addPeripheryWhitelist(address[] memory periphery) external returns (bool);
function removeContractsWhitelist(address[] memory contracts) external returns (bool);
function removeFeeWhitelist(address[] memory accounts) external returns (bool);
function removePeripheryWhitelist(address[] memory periphery) external returns (bool);
function createPair(address tokenA, address tokenB) external returns (address pair);
function updateFee(uint256 fee_) external returns (bool);
function updateProtocolShare(uint256 share) external returns (bool);
function updateFeePair(address token0, address token1, uint256 fee_) external returns (bool);
function updateProtocolSharePair(address token0, address token1, uint256 share) external returns (bool);
function setFeeTo(address) external;
function setFeeToSetter(address) external;
function skim(address token0, address token1, address to) external returns (bool);
}
contracts/interfaces/ISparkSwapRouter02.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.19;
import "./ISparkSwapRouter01.sol";
interface ISparkSwapRouter02 is ISparkSwapRouter01 {
function removeLiquidityETHSupportingFeeOnTransferTokens(
address token,
uint256 liquidity,
uint256 amountTokenMin,
uint256 amountETHMin,
address to,
uint256 deadline
) external returns (uint256 amountETH);
function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
address token,
uint256 liquidity,
uint256 amountTokenMin,
uint256 amountETHMin,
address to,
uint256 deadline,
bool approveMax,
uint8 v,
bytes32 r,
bytes32 s
) external returns (uint256 amountETH);
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external;
function swapExactETHForTokensSupportingFeeOnTransferTokens(
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external payable;
function swapExactTokensForETHSupportingFeeOnTransferTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external;
}
contracts/interfaces/IWETH.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.19;
interface IWETH {
function deposit() external payable;
function transfer(address to, uint256 value) external returns (bool);
function withdraw(uint256) external;
}
contracts/libraries/Math.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.19;
// a library for performing various math operations
library Math {
function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = x < y ? x : y;
}
// babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
function sqrt(uint256 y) internal pure returns (uint256 z) {
if (y > 3) {
z = y;
uint256 x = y / 2 + 1;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
}
}
}
contracts/libraries/TransferHelper.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.19;
library TransferHelper {
function safeApprove(address token, address to, uint256 value) internal {
// bytes4(keccak256(bytes('approve(address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
require(
success && (data.length == 0 || abi.decode(data, (bool))),
"TransferHelper::safeApprove: approve failed"
);
}
function safeTransfer(address token, address to, uint256 value) internal {
// bytes4(keccak256(bytes('transfer(address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
require(
success && (data.length == 0 || abi.decode(data, (bool))),
"TransferHelper::safeTransfer: transfer failed"
);
}
function safeTransferFrom(address token, address from, address to, uint256 value) internal {
// bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
require(
success && (data.length == 0 || abi.decode(data, (bool))),
"TransferHelper::transferFrom: transferFrom failed"
);
}
function safeTransferETH(address to, uint256 value) internal {
(bool success, ) = to.call{value: value}(new bytes(0));
require(success, "TransferHelper::safeTransferETH: ETH transfer failed");
}
}
contracts/libraries/UQ112x112.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.19;
// a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))
// range: [0, 2**112 - 1]
// resolution: 1 / 2**112
library UQ112x112 {
uint224 constant Q112 = 2 ** 112;
// encode a uint112 as a UQ112x112
function encode(uint112 y) internal pure returns (uint224 z) {
z = uint224(y) * Q112; // never overflows
}
// divide a UQ112x112 by a uint112, returning a UQ112x112
function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) {
z = x / uint224(y);
}
}
contracts/libraries/WBNB.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.19;
contract WBNB {
string public name = "Wrapped BNB";
string public symbol = "WBNB";
uint8 public decimals = 18;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
event Approval(address indexed src, address indexed guy, uint256 wad);
event Transfer(address indexed src, address indexed dst, uint256 wad);
event Deposit(address indexed dst, uint256 wad);
event Withdrawal(address indexed src, uint256 wad);
receive() external payable {
deposit();
}
function deposit() public payable {
balanceOf[msg.sender] += msg.value;
emit Deposit(msg.sender, msg.value);
}
function withdraw(uint256 wad) public {
require(balanceOf[msg.sender] >= wad);
balanceOf[msg.sender] -= wad;
payable(msg.sender).transfer(wad);
emit Withdrawal(msg.sender, wad);
}
function totalSupply() public view returns (uint256) {
return address(this).balance;
}
function approve(address guy, uint256 wad) public returns (bool) {
allowance[msg.sender][guy] = wad;
emit Approval(msg.sender, guy, wad);
return true;
}
function transfer(address dst, uint256 wad) public returns (bool) {
return transferFrom(msg.sender, dst, wad);
}
function transferFrom(address src, address dst, uint256 wad) public returns (bool) {
require(balanceOf[src] >= wad);
if (src != msg.sender && allowance[src][msg.sender] != type(uint256).max) {
require(allowance[src][msg.sender] >= wad);
allowance[src][msg.sender] -= wad;
}
balanceOf[src] -= wad;
balanceOf[dst] += wad;
emit Transfer(src, dst, wad);
return true;
}
}
contracts/mocks/MockToken.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "../interfaces/IWETH.sol";
contract MockToken is ERC20, IWETH {
event Deposit(address indexed dst, uint256 wad);
event Withdrawal(address indexed src, uint256 wad);
constructor(string memory name_, string memory symbol_, uint256 supply_) ERC20(name_, symbol_) {
_mint(msg.sender, supply_);
}
receive() external payable {
deposit();
}
function deposit() public payable {
mint(msg.value);
emit Deposit(msg.sender, msg.value);
}
function mint(uint256 amount) public returns (bool) {
_mint(msg.sender, amount);
return true;
}
function transfer(address to, uint256 amount) public override(ERC20, IWETH) returns (bool) {
return super.transfer(to, amount);
}
function withdraw(uint256 wad) public {
_burn(msg.sender, wad);
payable(msg.sender).transfer(wad);
emit Withdrawal(msg.sender, wad);
}
}
Compiler Settings
{"viaIR":true,"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers"]}},"optimizer":{"runs":200,"enabled":true},"metadata":{"useLiteralContent":true},"libraries":{}}
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[]},{"type":"event","name":"Approval","inputs":[{"type":"address","name":"owner","internalType":"address","indexed":true},{"type":"address","name":"spender","internalType":"address","indexed":true},{"type":"uint256","name":"value","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Burn","inputs":[{"type":"address","name":"sender","internalType":"address","indexed":true},{"type":"uint256","name":"amount0","internalType":"uint256","indexed":false},{"type":"uint256","name":"amount1","internalType":"uint256","indexed":false},{"type":"address","name":"to","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"FeeUpdated","inputs":[{"type":"uint256","name":"fee","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Mint","inputs":[{"type":"address","name":"sender","internalType":"address","indexed":true},{"type":"uint256","name":"amount0","internalType":"uint256","indexed":false},{"type":"uint256","name":"amount1","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"ProtocolShareUpdated","inputs":[{"type":"uint256","name":"share","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Swap","inputs":[{"type":"address","name":"sender","internalType":"address","indexed":true},{"type":"uint256","name":"amount0In","internalType":"uint256","indexed":false},{"type":"uint256","name":"amount1In","internalType":"uint256","indexed":false},{"type":"uint256","name":"amount0Out","internalType":"uint256","indexed":false},{"type":"uint256","name":"amount1Out","internalType":"uint256","indexed":false},{"type":"address","name":"to","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"Sync","inputs":[{"type":"uint112","name":"reserve0","internalType":"uint112","indexed":false},{"type":"uint112","name":"reserve1","internalType":"uint112","indexed":false}],"anonymous":false},{"type":"event","name":"Transfer","inputs":[{"type":"address","name":"from","internalType":"address","indexed":true},{"type":"address","name":"to","internalType":"address","indexed":true},{"type":"uint256","name":"value","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"DIVIDER","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"DOMAIN_SEPARATOR","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MAX_FEE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MAX_PROTOCOL_SHARE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MINIMUM_LIQUIDITY","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"PERMIT_TYPEHASH","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"allowance","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"approve","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"value","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"balanceOf","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"amount0","internalType":"uint256"},{"type":"uint256","name":"amount1","internalType":"uint256"}],"name":"burn","inputs":[{"type":"address","name":"to","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"","internalType":"uint8"}],"name":"decimals","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"factory","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"fee","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"}],"name":"getAmountIn","inputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"},{"type":"address","name":"tokenIn","internalType":"address"},{"type":"address","name":"caller","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"}],"name":"getAmountOut","inputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"address","name":"tokenIn","internalType":"address"},{"type":"address","name":"caller","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint112","name":"_reserve0","internalType":"uint112"},{"type":"uint112","name":"_reserve1","internalType":"uint112"},{"type":"uint256","name":"_blockTimestampLast","internalType":"uint256"}],"name":"getReserves","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initialize","inputs":[{"type":"address","name":"_token0","internalType":"address"},{"type":"address","name":"_token1","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"kLast","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"liquidity","internalType":"uint256"}],"name":"mint","inputs":[{"type":"address","name":"to","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"name","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"nonces","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"permit","inputs":[{"type":"address","name":"owner","internalType":"address"},{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"value","internalType":"uint256"},{"type":"uint256","name":"deadline","internalType":"uint256"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"price0CumulativeLast","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"price1CumulativeLast","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"protocolShare","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"skim","inputs":[{"type":"address","name":"to","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"swap","inputs":[{"type":"uint256","name":"amount0Out","internalType":"uint256"},{"type":"uint256","name":"amount1Out","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"},{"type":"bytes","name":"data","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"swapFromPeriphery","inputs":[{"type":"uint256","name":"amount0Out","internalType":"uint256"},{"type":"uint256","name":"amount1Out","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"},{"type":"address","name":"caller","internalType":"address"},{"type":"bytes","name":"data","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"symbol","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"sync","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"token0","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"token1","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalSupply","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transfer","inputs":[{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"value","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transferFrom","inputs":[{"type":"address","name":"from","internalType":"address"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"value","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"updateFee","inputs":[{"type":"uint256","name":"fee_","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"updateProtocolShare","inputs":[{"type":"uint256","name":"share","internalType":"uint256"}]}]
Contract Creation Code
0x60808060405234620000f4576020816200001b600d93620000f9565b828152016c537061726b53776170204c507360981b815220600160206040516200004581620000f9565b82815201603160f81b815220906040519160208301917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8352604084015260608301524660808301523060a083015260a0825260c082019180831060018060401b03841117620000de5760408390525190206000556001601155600780546001600160a01b03191633179055612c5f9081620001168239f35b634e487b7160e01b600052604160045260246000fd5b600080fd5b604081019081106001600160401b03821117620000de5760405256fe6080604052600436101561001257600080fd5b6000803560e01c8063022c0d9f14611de757806306fdde0314611da15780630902f1ac14611d50578063095ea7b314611d295780630a9a2b7214611bd45780630dfe168114611bab5780631103f31514611b8d57806318160ddd14611b6f57806323b872dd14611ace57806330adf81f14611a93578063313ce56714611a775780633644e51514611a5a578063485cc955146119475780635909c0d5146119295780635a3d54931461190b5780635e1e6325146117ba57806362043bd81461179d5780636a6278421461148557806370a082311461144c5780637464fc3d1461142e5780637ecebe00146113f557806389afcb44146110905780639012c4a814610fe057806395d89b4114610f97578063a9059cbb14610f65578063a931208f146107e7578063b239683214610809578063ba9a7a56146107ec578063bc063e1a146107e7578063bc25cf7714610691578063c45a015514610668578063c98ec7a4146105a1578063d21220a714610578578063d505accf14610355578063dd62ed3e14610306578063ddca3f43146102e85763fff6cae9146101b457600080fd5b346102e557806003193601126102e5576101d26001601154146125e3565b60118190556008546040516370a0823160e01b808252306004830152916020916001600160a01b039183908290602490829086165afa9182156102da57839186936102a9575b50600954169360246040518096819382523060048301525afa91821561029e578492610267575b5061025f9250601054916001600160701b03808460701c169316916129f6565b600160115580f35b90915082813d8311610297575b61027e818361234a565b810103126102925761025f9151903861023f565b600080fd5b503d610274565b6040513d86823e3d90fd5b8281939294503d83116102d3575b6102c1818361234a565b81010312610292578290519138610218565b503d6102b7565b6040513d87823e3d90fd5b80fd5b50346102e557806003193601126102e5576020600554604051908152f35b50346102e55760403660031901126102e5576103206122a4565b604061032a6122ba565b9260018060a01b03809316815260036020522091166000526020526020604060002054604051908152f35b50346102e55760e03660031901126102e55761036f6122a4565b6103776122ba565b604435906064356084359060ff82168092036105745742811061052f5785546001600160a01b038681168089526004602052604089208054919592949290600019831461051b576001830190556040519260208401927f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98452876040860152868916606086015289608086015260a085015260c084015260c0835260e083019383851067ffffffffffffffff86111761050557848b94610122608095602098604052825190209161010081019461190160f01b86526101028201520152604281526104618161232e565b519020906040519182528482015260a435604082015260c435606082015282805260015afa156102da578551169081151591826104fb575b5050156104ac576104a992612406565b80f35b60405162461bcd60e51b815260206004820152602160248201527f537061726b5377617045524332303a20494e56414c49445f5349474e415455526044820152604560f81b6064820152608490fd5b1490503880610499565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b8b52601160045260248bfd5b60405162461bcd60e51b815260206004820152601760248201527f537061726b5377617045524332303a20455850495245440000000000000000006044820152606490fd5b8580fd5b50346102e557806003193601126102e5576009546040516001600160a01b039091168152602090f35b50346102e55760203660031901126102e5576004356105cb60018060a01b03600754163314612627565b6064811161060d576020817fd82bf55fab0a06d972b148305a6b58062e33e048f53695742fc3e9f07585259292600655604051908152a1602060405160018152f35b60405162461bcd60e51b815260206004820152602d60248201527f537061726b53776170466163746f72793a205368617265206774204d41585f5060448201526c524f544f434f4c5f534841524560981b6064820152608490fd5b50346102e557806003193601126102e5576007546040516001600160a01b039091168152602090f35b50346102e5576020806003193601126107e3576106ac6122a4565b6007546001600160a01b039291906106c79084163314612627565b6106d56001601154146125e3565b836011558260085416926009541691604051936370a0823160e01b908186523060048701528286602481845afa9586156107d85787966107a7575b50610733908461072d6001600160701b039889601054169061245d565b9161285a565b6040519081523060048201528181602481875afa91821561079c57869261076c575b505061072d9061025f9460105460701c169061245d565b90809250813d8311610795575b610783818361234a565b8101031261029257518361072d610755565b503d610779565b6040513d88823e3d90fd5b9095508281813d83116107d1575b6107bf818361234a565b81010312610292575194610733610710565b503d6107b5565b6040513d89823e3d90fd5b5080fd5b6123ea565b50346102e557806003193601126102e55760206040516103e88152f35b50346102e55760a03660031901126102e55761082361228e565b606435906001600160a01b03821682036102925760843567ffffffffffffffff8111610f61576108579036906004016122d0565b60075460405163effe8ce160e01b81523360048201526001600160a01b039091169290602081602481875afa9081156107d8578791610f42575b5015610eee576108a56001601154146125e3565b856011556040516304ad971560e21b808252876004830152602082602481885afa918215610c5b578892610ecd575b508115610ec3575b8115610e78575b5015610e34576004351580158091610e29575b15610dd2576010546001600160701b03607082901c811693911691600435831180610dbe575b6109259061253e565b6008546009546001600160a01b0391821696908216939091891687141580610dab575b15610d66578a92610d54575b602435610d42575b81610c9b575b505050604051936020856024816370a0823160e01b948582523060048301525afa948515610c07578995610c66575b5060209060246040518094819382523060048301525afa908115610c5b578891610c29575b506109cc6004356001600160701b03841661245d565b841115610c22576109f16109eb6004356001600160701b03851661245d565b8561245d565b965b610a086024356001600160701b03861661245d565b821115610c1b57610a2d610a276024356001600160701b03871661245d565b8361245d565b955b8815801590610c12575b610a42906124e1565b6001600160a01b03821615159182610b97575b505015610b8f57875b846127108102046127101485151715610b7b57610a88610a7e828a6125b0565b612710870261245d565b90612710830290838204612710148415171561051b57610abc9291610ab0610ab6928a6125b0565b9061245d565b906125b0565b610ae3610adb6001600160701b0386166001600160701b0386166125b0565b610ab66129ac565b11610b4357610af1936129f6565b60405192835260208301526004356040830152602435606083015260018060a01b0316907fd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d82260803392a3600160115580f35b60405162461bcd60e51b815260206004820152601060248201526f537061726b53776170506169723a204b60801b6044820152606490fd5b634e487b7160e01b89526011600452602489fd5b600554610a5e565b604051631e45990d60e31b81526001600160a01b0390911660048201529150602090829060249082905afa908115610c07578991610bd8575b503880610a55565b610bfa915060203d602011610c00575b610bf2818361234a565b810190612598565b38610bd0565b503d610be8565b6040513d8b823e3d90fd5b50861515610a39565b8895610a2f565b87966109f3565b90506020813d602011610c53575b81610c446020938361234a565b810103126102925751386109b6565b3d9150610c37565b6040513d8a823e3d90fd5b9094506020813d602011610c93575b81610c826020938361234a565b810103126102925751936020610991565b3d9150610c75565b6001600160a01b0389163b15610d3e578160a484926040519485938492630da4f2fd60e21b84523360048501526004356024850152602435604485015260806064850152816084850152848401378181018301859052601f01601f19168101030181836001600160a01b038d165af18015610d3357610d1b575b80610962565b610d24906122fe565b610d2f578738610d15565b8780fd5b6040513d84823e3d90fd5b8280fd5b610d4f6024358a8661285a565b61095c565b610d616004358a8961285a565b610954565b60405162461bcd60e51b815260206004820152601960248201527f537061726b53776170506169723a20494e56414c49445f544f000000000000006044820152606490fd5b506001600160a01b038916841415610948565b506001600160701b0384166024351061091c565b60405162461bcd60e51b815260206004820152602960248201527f537061726b53776170506169723a20494e53554646494349454e545f4f555450604482015268155517d05353d5539560ba1b6064820152608490fd5b5060243515156108f6565b606460405162461bcd60e51b815260206004820152602060248201527f537061726b53776170506169723a2043616c6c657220697320696e76616c69646044820152fd5b9050604051908152336004820152602081602481875afa9081156107d8578791610ea4575b50386108e3565b610ebd915060203d602011610c0057610bf2818361234a565b38610e9d565b33321491506108dc565b610ee791925060203d602011610c0057610bf2818361234a565b90386108d4565b60405162461bcd60e51b815260206004820152602660248201527f537061726b53776170506169723a2043616c6c6572206973206e6f742070657260448201526569706865727960d01b6064820152608490fd5b610f5b915060203d602011610c0057610bf2818361234a565b38610891565b8380fd5b50346102e55760403660031901126102e557610f8c610f826122a4565b6024359033612477565b602060405160018152f35b50346102e557806003193601126102e557610fdc604051610fb781612312565b600c81526b0537061726b537761702d4c560a41b60208201526040519182918261236c565b0390f35b50346102e55760203660031901126102e55760043561100a60018060a01b03600754163314612627565b6064811161104c576020817f8c4d35e54a3f2ef1134138fd8ea3daee6a3c89e10d2665996babdf70261e2c7692600555604051908152a1602060405160018152f35b606460405162461bcd60e51b815260206004820152602060248201527f537061726b53776170466163746f72793a20466565206774204d41585f4645456044820152fd5b50346102e557602090816003193601126102e5576110ac6122a4565b906110bb6001601154146125e3565b6011819055601054600f546001600160701b038083169260701c1690506008546009546040516370a0823160e01b8082523060048301529794966001600160a01b039384169690949192841686846024818b5afa938415610d335782946113c6575b5060405199808b523060048c0152878b602481855afa9889156113bb578a9b849b9a611388575b503084526002895261118061115e8660408720549c6126ff565b966111746111798d6111746001549485926125b0565b6125c3565b9d8d6125b0565b998b15158061137f575b15611325576024939291858b92308252600284526111ac81604084205461245d565b3083526002855260408320556111c48160015461245d565b600155604051908152600080516020612c0a833981519152843092a36111eb8d8b8361285a565b6111f68c8b8661285a565b604051948580928582523060048301525afa92831561029e5790899185946112f4575b5060246040518094819382523060048301525afa9283156112e857926112b2575b5098611249929160409a6129f6565b61128f575b855191858352848484015216907fdccd412f0b1252819cb1fd330b93224ca42612892bb3f4f789976e6d81936496863392a360016011558351928352820152f35b6112aa6010546001600160701b03808260701c1691166125b0565b600c5561124e565b929150988683813d83116112e1575b6112cb818361234a565b810103126102925791519198909190604061123a565b503d6112c1565b604051903d90823e3d90fd5b8281939295503d831161131e575b61130c818361234a565b81010312610292578890519238611219565b503d611302565b60405162461bcd60e51b8152600481018b9052602c60248201527f537061726b53776170506169723a20494e53554646494349454e545f4c49515560448201526b125112551657d0955493915160a21b6064820152608490fd5b508a151561118a565b8980929c50819b503d83116113b4575b6113a2818361234a565b81010312610292578a98519938611144565b503d611398565b6040513d85823e3d90fd5b9093508681813d83116113ee575b6113de818361234a565b810103126102925751923861111d565b503d6113d4565b50346102e55760203660031901126102e5576020906040906001600160a01b0361141d6122a4565b168152600483522054604051908152f35b50346102e557806003193601126102e5576020600c54604051908152f35b50346102e55760203660031901126102e5576020906040906001600160a01b036114746122a4565b168152600283522054604051908152f35b50346102e55760203660031901126102e55761149f6122a4565b906114ae6001601154146125e3565b60118190556010546008546040516370a0823160e01b80825230600483015290946001600160701b03607085901c811695941693926001600160a01b03906020908890602490829085165afa96871561029e578497611768575b50602090600954169260246040518095819382523060048301525afa9182156113bb578392611734575b506001600160701b03841694611548868861245d565b946001600160701b0382169361155e858261245d565b9561156984846126ff565b95600154998a156000146116fd57505061158b611586888a6125b0565b612b5b565b6103e7198101919082116116e95750976103e8810181116116d3576103e8016001556000805260026020526040600020546103e8810181116116d3576103e89060008052600260205201604060002055600080600080516020612c0a83398151915260206040516103e88152a35b87156116795760209861160f89611614966126a9565b6129f6565b611656575b604051918252838201527f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f60403392a26001601155604051908152f35b6116716010546001600160701b03808260701c1691166125b0565b600c55611619565b60405162461bcd60e51b815260206004820152602c60248201527f537061726b53776170506169723a20494e53554646494349454e545f4c49515560448201526b125112551657d3525395115160a21b6064820152608490fd5b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b81526011600452602490fd5b9061117461171461171b936111748e979e8e6125b0565b948a6125b0565b90508082101561172d57505b966115f9565b9050611727565b9091506020813d602011611760575b816117506020938361234a565b8101031261029257519038611532565b3d9150611743565b9096506020813d602011611795575b816117846020938361234a565b810103126102925751956020611508565b3d9150611777565b50346102e557806003193601126102e55760206040516127108152f35b50346102e5576117c9366123b5565b9091926117ea601054906001600160701b038083169260701c1690600f5490565b506001600160701b039182169591169384906118078315156124e1565b86151580611902575b6118199061253e565b600754604051631e45990d60e31b81526001600160a01b03968716600482015290602090829060249082908a165afa9081156102da5785916118e4575b50156118db5783945b80600954169116146118d2575b50612710928303908382116118be5761188f91611888916125b0565b93846125b0565b938281029281840414901517156116e95760206118b6856118b0868661246a565b906125c3565b604051908152f35b634e487b7160e01b83526011600452602483fd5b9493503861186c565b6005549461185f565b6118fc915060203d8111610c0057610bf2818361234a565b38611856565b50851515611810565b50346102e557806003193601126102e5576020600b54604051908152f35b50346102e557806003193601126102e5576020600a54604051908152f35b50346102e55760403660031901126102e5576119616122a4565b6119696122ba565b6007546001600160a01b039283916119849083163314612627565b16916bffffffffffffffffffffffff60a01b91838360085416176008551680916009541617600955604051809263313ce56760e01b9081835282600460209687935afa9182156102da5784926119e1918791611a3d575b50612698565b600d5560046040518094819382525afa9081156113bb57611a0a928492611a10575b5050612698565b600e5580f35b611a2f9250803d10611a36575b611a27818361234a565b81019061267f565b3880611a03565b503d611a1d565b611a549150843d8611611a3657611a27818361234a565b386119db565b50346102e557806003193601126102e55760209054604051908152f35b50346102e557806003193601126102e557602060405160128152f35b50346102e557806003193601126102e55760206040517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98152f35b50346102e55760603660031901126102e557611b2a611aeb6122a4565b611af36122ba565b6044359160018060a01b038116808652602095600387526040812033600052875260001960406000205403611b33575b5050612477565b60405160018152f35b80826040925260038852818120336000528852611b5486836000205461245d565b92815260038852203360005286526040600020553880611b23565b50346102e557806003193601126102e5576020600154604051908152f35b50346102e557806003193601126102e5576020600654604051908152f35b50346102e557806003193601126102e5576008546040516001600160a01b039091168152602090f35b50346102e557611be3366123b5565b6010546001600160701b03607082901c8116939116919083611c068615156124e1565b83151580611d20575b611c189061253e565b6009546001600160a01b0392831690831614611d16575b50600754604051631e45990d60e31b8152928216600484015260209183916024918391165afa9081156102da578591611cf8575b5015611ceb57611c748385926125b0565b926127109283850294808604851490151715611cd75790611c949161245d565b908203918211611cc357611cac92916118b0916125b0565b90600182018092116116e957602082604051908152f35b634e487b7160e01b84526011600452602484fd5b634e487b7160e01b86526011600452602486fd5b611c7483600554926125b0565b611d10915060203d8111610c0057610bf2818361234a565b38611c63565b9293506020611c2f565b50841515611c0f565b50346102e55760403660031901126102e557610f8c611d466122a4565b6024359033612406565b50346102e557806003193601126102e5576060611d81601054906001600160701b038083169260701c1690600f5490565b9091604051926001600160701b0380921684521660208301526040820152f35b50346102e557806003193601126102e557610fdc604051611dc181612312565b600d81526c537061726b53776170204c507360981b60208201526040519182918261236c565b50346102e55760803660031901126102e557611e0161228e565b60643567ffffffffffffffff8111610d3e57611e219036906004016122d0565b91611e306001601154146125e3565b60118490556007546040516304ad971560e21b808252600482018790526001600160a01b039492851690602083602481855afa928315610c5b57889361226d575b508215612263575b8215612216575b505015610e3457600435158015809161220b575b15610dd2576010546001600160701b038082169660709290921c16906004358711806121f7575b611ec49061253e565b856008541692866009541694848888161415806121eb575b15610d665789916121d9575b6024356121c7575b82612142575b505050604051916020836024816370a0823160e01b948582523060048301525afa928315610c5b57889361210d575b5060209060246040518096819382523060048301525afa9283156107d85787936120d9575b50611f606004356001600160701b03881661245d565b8211156120d257611f7f610a276004356001600160701b03891661245d565b955b611f966024356001600160701b03841661245d565b8411156120cb57611fb56109eb6024356001600160701b03851661245d565b935b87158015906120c2575b611fca906124e1565b88156120ba57885b8461271081020461271014851517156120a657611ff2610a7e828b6125b0565b9061271083029083820461271014841517156120925761201a9291610ab0610ab6928a6125b0565b612039610adb6001600160701b0386166001600160701b0386166125b0565b11610b4357612047936129f6565b60405193845260208401526004356040840152602435606084015216907fd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d82260803392a3600160115580f35b634e487b7160e01b8c52601160045260248cfd5b634e487b7160e01b8a52601160045260248afd5b600554611fd2565b50841515611fc1565b8793611fb7565b8695611f81565b9092506020813d602011612105575b816120f56020938361234a565b8101031261029257519138611f4a565b3d91506120e8565b9092506020813d60201161213a575b816121296020938361234a565b810103126102925751916020611f25565b3d915061211c565b8787163b156107e35760a483916040519485938492630da4f2fd60e21b84523360048501526004356024850152602435604485015260806064850152816084850152848401378181018301859052601f01601f1916810103018183898b165af18015610c5b576121b4575b8781611ef6565b6121c0909791976122fe565b95386121ad565b6121d4602435888861285a565b611ef0565b6121e6600435888761285a565b611ee8565b50858888161415611edc565b506001600160701b03821660243510611ebb565b506024351515611e94565b602091925060246040518094819382523360048301525afa90811561079c578691612244575b503880611e80565b61225d915060203d602011610c0057610bf2818361234a565b3861223c565b3332149250611e79565b61228791935060203d602011610c0057610bf2818361234a565b9138611e71565b604435906001600160a01b038216820361029257565b600435906001600160a01b038216820361029257565b602435906001600160a01b038216820361029257565b9181601f840112156102925782359167ffffffffffffffff8311610292576020838186019501011161029257565b67ffffffffffffffff811161050557604052565b6040810190811067ffffffffffffffff82111761050557604052565b6080810190811067ffffffffffffffff82111761050557604052565b90601f8019910116810190811067ffffffffffffffff82111761050557604052565b6020808252825181830181905290939260005b8281106123a157505060409293506000838284010152601f8019910116010190565b81810186015184820160400152850161237f565b606090600319011261029257600435906001600160a01b03906024358281168103610292579160443590811681036102925790565b3461029257600036600319011261029257602060405160648152f35b909160207f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259160018060a01b03809416938460005260038352604060002095169485600052825280604060002055604051908152a3565b919082039182116116d357565b919082018092116116d357565b90916020600080516020612c0a8339815191529160018060a01b0380941693600095858752600284526124ae83604089205461245d565b868852600285526040882055169485815260406124ce838284205461246a565b91878152600285522055604051908152a3565b156124e857565b60405162461bcd60e51b815260206004820152602860248201527f537061726b53776170506169723a20494e53554646494349454e545f494e50556044820152671517d05353d5539560c21b6064820152608490fd5b1561254557565b60405162461bcd60e51b815260206004820152602560248201527f537061726b53776170506169723a20494e53554646494349454e545f4c495155604482015264494449545960d81b6064820152608490fd5b90816020910312610292575180151581036102925790565b818102929181159184041417156116d357565b81156125cd570490565b634e487b7160e01b600052601260045260246000fd5b156125ea57565b60405162461bcd60e51b815260206004820152601560248201527414dc185c9ad4ddd85c14185a5c8e881313d0d2d151605a1b6044820152606490fd5b1561262e57565b60405162461bcd60e51b8152602060048201526024808201527f537061726b53776170506169723a2043616c6c6572206973206e6f7420666163604482015263746f727960e01b6064820152608490fd5b90816020910312610292575160ff811681036102925790565b60ff16604d81116116d357600a0a90565b600080516020612c0a83398151915260206000926126c98560015461246a565b60015560018060a01b031693848452600282526126ea81604086205461246a565b858552600283526040852055604051908152a3565b600754604051622fcfcb60e31b8152919392906001600160a01b03906020908490600490829085165afa92831561284e57600093612810575b50821615159081612804575b600c54919485156127f3578261275b575b50505050565b6115866127789161277e936001600160701b0380911691166125b0565b91612b5b565b80821161278c575b80612755565b61279c600154610ab6838561245d565b916127aa60065480946125b0565b9183606403606481116116d3576127d7946127cb6127d1926118b0956125b0565b926125b0565b9061246a565b806127e3575b80612786565b6127ec916126a9565b38806127dd565b505090506127fd57565b6000600c55565b60065415159150612744565b6020939193813d8211612846575b8161282b6020938361234a565b810103126107e357519083821682036102e557509138612738565b3d915061281e565b6040513d6000823e3d90fd5b906040519261286884612312565b601984527f7472616e7366657228616464726573732c75696e74323536290000000000000060209485015260405163a9059cbb60e01b8186019081526001600160a01b0390931660248201526044808201929092529081526128c98161232e565b600092839283809351925af1903d156129a4573d67ffffffffffffffff81116129905760405190612903601f8201601f191686018361234a565b81528091843d92013e5b81612960575b501561291c5750565b6064906040519062461bcd60e51b82526004820152601e60248201527f537061726b53776170506169723a205452414e534645525f4641494c454400006044820152fd5b80518015925083908315612978575b50505038612913565b6129889350820181019101612598565b38828161296f565b634e487b7160e01b82526041600452602482fd5b50606061290d565b61271060016002815b8082116129cd575050816000190481116116d3570290565b9092806000190481116116d3578184166129ed575b800292811c906129b5565b809202916129e2565b91926001600160701b03908184111580612b51575b15612b0c57816040947f1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad19663ffffffff421693612a4a600f548661245d565b80151580612b01575b80612af6575b612a9d575b505050169283601054916001600160701b0360701b9060701b169163ffffffff60e01b1617179081601055600f55835192835260701c166020820152a1565b612aeb92612ae3926001600160e01b0391612acd9190612ac890612adb612ad3878787858789612bb5565b612beb565b166125b0565b600a5461246a565b600a55612bb5565b600b5461246a565b600b55388080612a5e565b508482161515612a59565b508483161515612a53565b60405162461bcd60e51b815260206004820152601760248201527f537061726b53776170506169723a204f564552464c4f570000000000000000006044820152606490fd5b5081831115612a0b565b9060006003831115612ba857508160019080821c8281018091116116d35791905b848310612b8857505050565b90919350612b9f84612b9a81846125c3565b61246a565b821c9190612b7c565b91612baf57565b60019150565b6dffffffffffffffffffffffffffff60701b607082901b16906001600160701b0316808204600160701b14901517156116d35790565b906001600160701b03169081156125cd576001600160e01b0316049056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122006280758518c180494fe710957cf10bd3f48348fea2a289b77823eb8356759ca64736f6c63430008130033
Deployed ByteCode
0x6080604052600436101561001257600080fd5b6000803560e01c8063022c0d9f14611de757806306fdde0314611da15780630902f1ac14611d50578063095ea7b314611d295780630a9a2b7214611bd45780630dfe168114611bab5780631103f31514611b8d57806318160ddd14611b6f57806323b872dd14611ace57806330adf81f14611a93578063313ce56714611a775780633644e51514611a5a578063485cc955146119475780635909c0d5146119295780635a3d54931461190b5780635e1e6325146117ba57806362043bd81461179d5780636a6278421461148557806370a082311461144c5780637464fc3d1461142e5780637ecebe00146113f557806389afcb44146110905780639012c4a814610fe057806395d89b4114610f97578063a9059cbb14610f65578063a931208f146107e7578063b239683214610809578063ba9a7a56146107ec578063bc063e1a146107e7578063bc25cf7714610691578063c45a015514610668578063c98ec7a4146105a1578063d21220a714610578578063d505accf14610355578063dd62ed3e14610306578063ddca3f43146102e85763fff6cae9146101b457600080fd5b346102e557806003193601126102e5576101d26001601154146125e3565b60118190556008546040516370a0823160e01b808252306004830152916020916001600160a01b039183908290602490829086165afa9182156102da57839186936102a9575b50600954169360246040518096819382523060048301525afa91821561029e578492610267575b5061025f9250601054916001600160701b03808460701c169316916129f6565b600160115580f35b90915082813d8311610297575b61027e818361234a565b810103126102925761025f9151903861023f565b600080fd5b503d610274565b6040513d86823e3d90fd5b8281939294503d83116102d3575b6102c1818361234a565b81010312610292578290519138610218565b503d6102b7565b6040513d87823e3d90fd5b80fd5b50346102e557806003193601126102e5576020600554604051908152f35b50346102e55760403660031901126102e5576103206122a4565b604061032a6122ba565b9260018060a01b03809316815260036020522091166000526020526020604060002054604051908152f35b50346102e55760e03660031901126102e55761036f6122a4565b6103776122ba565b604435906064356084359060ff82168092036105745742811061052f5785546001600160a01b038681168089526004602052604089208054919592949290600019831461051b576001830190556040519260208401927f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98452876040860152868916606086015289608086015260a085015260c084015260c0835260e083019383851067ffffffffffffffff86111761050557848b94610122608095602098604052825190209161010081019461190160f01b86526101028201520152604281526104618161232e565b519020906040519182528482015260a435604082015260c435606082015282805260015afa156102da578551169081151591826104fb575b5050156104ac576104a992612406565b80f35b60405162461bcd60e51b815260206004820152602160248201527f537061726b5377617045524332303a20494e56414c49445f5349474e415455526044820152604560f81b6064820152608490fd5b1490503880610499565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b8b52601160045260248bfd5b60405162461bcd60e51b815260206004820152601760248201527f537061726b5377617045524332303a20455850495245440000000000000000006044820152606490fd5b8580fd5b50346102e557806003193601126102e5576009546040516001600160a01b039091168152602090f35b50346102e55760203660031901126102e5576004356105cb60018060a01b03600754163314612627565b6064811161060d576020817fd82bf55fab0a06d972b148305a6b58062e33e048f53695742fc3e9f07585259292600655604051908152a1602060405160018152f35b60405162461bcd60e51b815260206004820152602d60248201527f537061726b53776170466163746f72793a205368617265206774204d41585f5060448201526c524f544f434f4c5f534841524560981b6064820152608490fd5b50346102e557806003193601126102e5576007546040516001600160a01b039091168152602090f35b50346102e5576020806003193601126107e3576106ac6122a4565b6007546001600160a01b039291906106c79084163314612627565b6106d56001601154146125e3565b836011558260085416926009541691604051936370a0823160e01b908186523060048701528286602481845afa9586156107d85787966107a7575b50610733908461072d6001600160701b039889601054169061245d565b9161285a565b6040519081523060048201528181602481875afa91821561079c57869261076c575b505061072d9061025f9460105460701c169061245d565b90809250813d8311610795575b610783818361234a565b8101031261029257518361072d610755565b503d610779565b6040513d88823e3d90fd5b9095508281813d83116107d1575b6107bf818361234a565b81010312610292575194610733610710565b503d6107b5565b6040513d89823e3d90fd5b5080fd5b6123ea565b50346102e557806003193601126102e55760206040516103e88152f35b50346102e55760a03660031901126102e55761082361228e565b606435906001600160a01b03821682036102925760843567ffffffffffffffff8111610f61576108579036906004016122d0565b60075460405163effe8ce160e01b81523360048201526001600160a01b039091169290602081602481875afa9081156107d8578791610f42575b5015610eee576108a56001601154146125e3565b856011556040516304ad971560e21b808252876004830152602082602481885afa918215610c5b578892610ecd575b508115610ec3575b8115610e78575b5015610e34576004351580158091610e29575b15610dd2576010546001600160701b03607082901c811693911691600435831180610dbe575b6109259061253e565b6008546009546001600160a01b0391821696908216939091891687141580610dab575b15610d66578a92610d54575b602435610d42575b81610c9b575b505050604051936020856024816370a0823160e01b948582523060048301525afa948515610c07578995610c66575b5060209060246040518094819382523060048301525afa908115610c5b578891610c29575b506109cc6004356001600160701b03841661245d565b841115610c22576109f16109eb6004356001600160701b03851661245d565b8561245d565b965b610a086024356001600160701b03861661245d565b821115610c1b57610a2d610a276024356001600160701b03871661245d565b8361245d565b955b8815801590610c12575b610a42906124e1565b6001600160a01b03821615159182610b97575b505015610b8f57875b846127108102046127101485151715610b7b57610a88610a7e828a6125b0565b612710870261245d565b90612710830290838204612710148415171561051b57610abc9291610ab0610ab6928a6125b0565b9061245d565b906125b0565b610ae3610adb6001600160701b0386166001600160701b0386166125b0565b610ab66129ac565b11610b4357610af1936129f6565b60405192835260208301526004356040830152602435606083015260018060a01b0316907fd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d82260803392a3600160115580f35b60405162461bcd60e51b815260206004820152601060248201526f537061726b53776170506169723a204b60801b6044820152606490fd5b634e487b7160e01b89526011600452602489fd5b600554610a5e565b604051631e45990d60e31b81526001600160a01b0390911660048201529150602090829060249082905afa908115610c07578991610bd8575b503880610a55565b610bfa915060203d602011610c00575b610bf2818361234a565b810190612598565b38610bd0565b503d610be8565b6040513d8b823e3d90fd5b50861515610a39565b8895610a2f565b87966109f3565b90506020813d602011610c53575b81610c446020938361234a565b810103126102925751386109b6565b3d9150610c37565b6040513d8a823e3d90fd5b9094506020813d602011610c93575b81610c826020938361234a565b810103126102925751936020610991565b3d9150610c75565b6001600160a01b0389163b15610d3e578160a484926040519485938492630da4f2fd60e21b84523360048501526004356024850152602435604485015260806064850152816084850152848401378181018301859052601f01601f19168101030181836001600160a01b038d165af18015610d3357610d1b575b80610962565b610d24906122fe565b610d2f578738610d15565b8780fd5b6040513d84823e3d90fd5b8280fd5b610d4f6024358a8661285a565b61095c565b610d616004358a8961285a565b610954565b60405162461bcd60e51b815260206004820152601960248201527f537061726b53776170506169723a20494e56414c49445f544f000000000000006044820152606490fd5b506001600160a01b038916841415610948565b506001600160701b0384166024351061091c565b60405162461bcd60e51b815260206004820152602960248201527f537061726b53776170506169723a20494e53554646494349454e545f4f555450604482015268155517d05353d5539560ba1b6064820152608490fd5b5060243515156108f6565b606460405162461bcd60e51b815260206004820152602060248201527f537061726b53776170506169723a2043616c6c657220697320696e76616c69646044820152fd5b9050604051908152336004820152602081602481875afa9081156107d8578791610ea4575b50386108e3565b610ebd915060203d602011610c0057610bf2818361234a565b38610e9d565b33321491506108dc565b610ee791925060203d602011610c0057610bf2818361234a565b90386108d4565b60405162461bcd60e51b815260206004820152602660248201527f537061726b53776170506169723a2043616c6c6572206973206e6f742070657260448201526569706865727960d01b6064820152608490fd5b610f5b915060203d602011610c0057610bf2818361234a565b38610891565b8380fd5b50346102e55760403660031901126102e557610f8c610f826122a4565b6024359033612477565b602060405160018152f35b50346102e557806003193601126102e557610fdc604051610fb781612312565b600c81526b0537061726b537761702d4c560a41b60208201526040519182918261236c565b0390f35b50346102e55760203660031901126102e55760043561100a60018060a01b03600754163314612627565b6064811161104c576020817f8c4d35e54a3f2ef1134138fd8ea3daee6a3c89e10d2665996babdf70261e2c7692600555604051908152a1602060405160018152f35b606460405162461bcd60e51b815260206004820152602060248201527f537061726b53776170466163746f72793a20466565206774204d41585f4645456044820152fd5b50346102e557602090816003193601126102e5576110ac6122a4565b906110bb6001601154146125e3565b6011819055601054600f546001600160701b038083169260701c1690506008546009546040516370a0823160e01b8082523060048301529794966001600160a01b039384169690949192841686846024818b5afa938415610d335782946113c6575b5060405199808b523060048c0152878b602481855afa9889156113bb578a9b849b9a611388575b503084526002895261118061115e8660408720549c6126ff565b966111746111798d6111746001549485926125b0565b6125c3565b9d8d6125b0565b998b15158061137f575b15611325576024939291858b92308252600284526111ac81604084205461245d565b3083526002855260408320556111c48160015461245d565b600155604051908152600080516020612c0a833981519152843092a36111eb8d8b8361285a565b6111f68c8b8661285a565b604051948580928582523060048301525afa92831561029e5790899185946112f4575b5060246040518094819382523060048301525afa9283156112e857926112b2575b5098611249929160409a6129f6565b61128f575b855191858352848484015216907fdccd412f0b1252819cb1fd330b93224ca42612892bb3f4f789976e6d81936496863392a360016011558351928352820152f35b6112aa6010546001600160701b03808260701c1691166125b0565b600c5561124e565b929150988683813d83116112e1575b6112cb818361234a565b810103126102925791519198909190604061123a565b503d6112c1565b604051903d90823e3d90fd5b8281939295503d831161131e575b61130c818361234a565b81010312610292578890519238611219565b503d611302565b60405162461bcd60e51b8152600481018b9052602c60248201527f537061726b53776170506169723a20494e53554646494349454e545f4c49515560448201526b125112551657d0955493915160a21b6064820152608490fd5b508a151561118a565b8980929c50819b503d83116113b4575b6113a2818361234a565b81010312610292578a98519938611144565b503d611398565b6040513d85823e3d90fd5b9093508681813d83116113ee575b6113de818361234a565b810103126102925751923861111d565b503d6113d4565b50346102e55760203660031901126102e5576020906040906001600160a01b0361141d6122a4565b168152600483522054604051908152f35b50346102e557806003193601126102e5576020600c54604051908152f35b50346102e55760203660031901126102e5576020906040906001600160a01b036114746122a4565b168152600283522054604051908152f35b50346102e55760203660031901126102e55761149f6122a4565b906114ae6001601154146125e3565b60118190556010546008546040516370a0823160e01b80825230600483015290946001600160701b03607085901c811695941693926001600160a01b03906020908890602490829085165afa96871561029e578497611768575b50602090600954169260246040518095819382523060048301525afa9182156113bb578392611734575b506001600160701b03841694611548868861245d565b946001600160701b0382169361155e858261245d565b9561156984846126ff565b95600154998a156000146116fd57505061158b611586888a6125b0565b612b5b565b6103e7198101919082116116e95750976103e8810181116116d3576103e8016001556000805260026020526040600020546103e8810181116116d3576103e89060008052600260205201604060002055600080600080516020612c0a83398151915260206040516103e88152a35b87156116795760209861160f89611614966126a9565b6129f6565b611656575b604051918252838201527f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f60403392a26001601155604051908152f35b6116716010546001600160701b03808260701c1691166125b0565b600c55611619565b60405162461bcd60e51b815260206004820152602c60248201527f537061726b53776170506169723a20494e53554646494349454e545f4c49515560448201526b125112551657d3525395115160a21b6064820152608490fd5b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b81526011600452602490fd5b9061117461171461171b936111748e979e8e6125b0565b948a6125b0565b90508082101561172d57505b966115f9565b9050611727565b9091506020813d602011611760575b816117506020938361234a565b8101031261029257519038611532565b3d9150611743565b9096506020813d602011611795575b816117846020938361234a565b810103126102925751956020611508565b3d9150611777565b50346102e557806003193601126102e55760206040516127108152f35b50346102e5576117c9366123b5565b9091926117ea601054906001600160701b038083169260701c1690600f5490565b506001600160701b039182169591169384906118078315156124e1565b86151580611902575b6118199061253e565b600754604051631e45990d60e31b81526001600160a01b03968716600482015290602090829060249082908a165afa9081156102da5785916118e4575b50156118db5783945b80600954169116146118d2575b50612710928303908382116118be5761188f91611888916125b0565b93846125b0565b938281029281840414901517156116e95760206118b6856118b0868661246a565b906125c3565b604051908152f35b634e487b7160e01b83526011600452602483fd5b9493503861186c565b6005549461185f565b6118fc915060203d8111610c0057610bf2818361234a565b38611856565b50851515611810565b50346102e557806003193601126102e5576020600b54604051908152f35b50346102e557806003193601126102e5576020600a54604051908152f35b50346102e55760403660031901126102e5576119616122a4565b6119696122ba565b6007546001600160a01b039283916119849083163314612627565b16916bffffffffffffffffffffffff60a01b91838360085416176008551680916009541617600955604051809263313ce56760e01b9081835282600460209687935afa9182156102da5784926119e1918791611a3d575b50612698565b600d5560046040518094819382525afa9081156113bb57611a0a928492611a10575b5050612698565b600e5580f35b611a2f9250803d10611a36575b611a27818361234a565b81019061267f565b3880611a03565b503d611a1d565b611a549150843d8611611a3657611a27818361234a565b386119db565b50346102e557806003193601126102e55760209054604051908152f35b50346102e557806003193601126102e557602060405160128152f35b50346102e557806003193601126102e55760206040517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98152f35b50346102e55760603660031901126102e557611b2a611aeb6122a4565b611af36122ba565b6044359160018060a01b038116808652602095600387526040812033600052875260001960406000205403611b33575b5050612477565b60405160018152f35b80826040925260038852818120336000528852611b5486836000205461245d565b92815260038852203360005286526040600020553880611b23565b50346102e557806003193601126102e5576020600154604051908152f35b50346102e557806003193601126102e5576020600654604051908152f35b50346102e557806003193601126102e5576008546040516001600160a01b039091168152602090f35b50346102e557611be3366123b5565b6010546001600160701b03607082901c8116939116919083611c068615156124e1565b83151580611d20575b611c189061253e565b6009546001600160a01b0392831690831614611d16575b50600754604051631e45990d60e31b8152928216600484015260209183916024918391165afa9081156102da578591611cf8575b5015611ceb57611c748385926125b0565b926127109283850294808604851490151715611cd75790611c949161245d565b908203918211611cc357611cac92916118b0916125b0565b90600182018092116116e957602082604051908152f35b634e487b7160e01b84526011600452602484fd5b634e487b7160e01b86526011600452602486fd5b611c7483600554926125b0565b611d10915060203d8111610c0057610bf2818361234a565b38611c63565b9293506020611c2f565b50841515611c0f565b50346102e55760403660031901126102e557610f8c611d466122a4565b6024359033612406565b50346102e557806003193601126102e5576060611d81601054906001600160701b038083169260701c1690600f5490565b9091604051926001600160701b0380921684521660208301526040820152f35b50346102e557806003193601126102e557610fdc604051611dc181612312565b600d81526c537061726b53776170204c507360981b60208201526040519182918261236c565b50346102e55760803660031901126102e557611e0161228e565b60643567ffffffffffffffff8111610d3e57611e219036906004016122d0565b91611e306001601154146125e3565b60118490556007546040516304ad971560e21b808252600482018790526001600160a01b039492851690602083602481855afa928315610c5b57889361226d575b508215612263575b8215612216575b505015610e3457600435158015809161220b575b15610dd2576010546001600160701b038082169660709290921c16906004358711806121f7575b611ec49061253e565b856008541692866009541694848888161415806121eb575b15610d665789916121d9575b6024356121c7575b82612142575b505050604051916020836024816370a0823160e01b948582523060048301525afa928315610c5b57889361210d575b5060209060246040518096819382523060048301525afa9283156107d85787936120d9575b50611f606004356001600160701b03881661245d565b8211156120d257611f7f610a276004356001600160701b03891661245d565b955b611f966024356001600160701b03841661245d565b8411156120cb57611fb56109eb6024356001600160701b03851661245d565b935b87158015906120c2575b611fca906124e1565b88156120ba57885b8461271081020461271014851517156120a657611ff2610a7e828b6125b0565b9061271083029083820461271014841517156120925761201a9291610ab0610ab6928a6125b0565b612039610adb6001600160701b0386166001600160701b0386166125b0565b11610b4357612047936129f6565b60405193845260208401526004356040840152602435606084015216907fd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d82260803392a3600160115580f35b634e487b7160e01b8c52601160045260248cfd5b634e487b7160e01b8a52601160045260248afd5b600554611fd2565b50841515611fc1565b8793611fb7565b8695611f81565b9092506020813d602011612105575b816120f56020938361234a565b8101031261029257519138611f4a565b3d91506120e8565b9092506020813d60201161213a575b816121296020938361234a565b810103126102925751916020611f25565b3d915061211c565b8787163b156107e35760a483916040519485938492630da4f2fd60e21b84523360048501526004356024850152602435604485015260806064850152816084850152848401378181018301859052601f01601f1916810103018183898b165af18015610c5b576121b4575b8781611ef6565b6121c0909791976122fe565b95386121ad565b6121d4602435888861285a565b611ef0565b6121e6600435888761285a565b611ee8565b50858888161415611edc565b506001600160701b03821660243510611ebb565b506024351515611e94565b602091925060246040518094819382523360048301525afa90811561079c578691612244575b503880611e80565b61225d915060203d602011610c0057610bf2818361234a565b3861223c565b3332149250611e79565b61228791935060203d602011610c0057610bf2818361234a565b9138611e71565b604435906001600160a01b038216820361029257565b600435906001600160a01b038216820361029257565b602435906001600160a01b038216820361029257565b9181601f840112156102925782359167ffffffffffffffff8311610292576020838186019501011161029257565b67ffffffffffffffff811161050557604052565b6040810190811067ffffffffffffffff82111761050557604052565b6080810190811067ffffffffffffffff82111761050557604052565b90601f8019910116810190811067ffffffffffffffff82111761050557604052565b6020808252825181830181905290939260005b8281106123a157505060409293506000838284010152601f8019910116010190565b81810186015184820160400152850161237f565b606090600319011261029257600435906001600160a01b03906024358281168103610292579160443590811681036102925790565b3461029257600036600319011261029257602060405160648152f35b909160207f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259160018060a01b03809416938460005260038352604060002095169485600052825280604060002055604051908152a3565b919082039182116116d357565b919082018092116116d357565b90916020600080516020612c0a8339815191529160018060a01b0380941693600095858752600284526124ae83604089205461245d565b868852600285526040882055169485815260406124ce838284205461246a565b91878152600285522055604051908152a3565b156124e857565b60405162461bcd60e51b815260206004820152602860248201527f537061726b53776170506169723a20494e53554646494349454e545f494e50556044820152671517d05353d5539560c21b6064820152608490fd5b1561254557565b60405162461bcd60e51b815260206004820152602560248201527f537061726b53776170506169723a20494e53554646494349454e545f4c495155604482015264494449545960d81b6064820152608490fd5b90816020910312610292575180151581036102925790565b818102929181159184041417156116d357565b81156125cd570490565b634e487b7160e01b600052601260045260246000fd5b156125ea57565b60405162461bcd60e51b815260206004820152601560248201527414dc185c9ad4ddd85c14185a5c8e881313d0d2d151605a1b6044820152606490fd5b1561262e57565b60405162461bcd60e51b8152602060048201526024808201527f537061726b53776170506169723a2043616c6c6572206973206e6f7420666163604482015263746f727960e01b6064820152608490fd5b90816020910312610292575160ff811681036102925790565b60ff16604d81116116d357600a0a90565b600080516020612c0a83398151915260206000926126c98560015461246a565b60015560018060a01b031693848452600282526126ea81604086205461246a565b858552600283526040852055604051908152a3565b600754604051622fcfcb60e31b8152919392906001600160a01b03906020908490600490829085165afa92831561284e57600093612810575b50821615159081612804575b600c54919485156127f3578261275b575b50505050565b6115866127789161277e936001600160701b0380911691166125b0565b91612b5b565b80821161278c575b80612755565b61279c600154610ab6838561245d565b916127aa60065480946125b0565b9183606403606481116116d3576127d7946127cb6127d1926118b0956125b0565b926125b0565b9061246a565b806127e3575b80612786565b6127ec916126a9565b38806127dd565b505090506127fd57565b6000600c55565b60065415159150612744565b6020939193813d8211612846575b8161282b6020938361234a565b810103126107e357519083821682036102e557509138612738565b3d915061281e565b6040513d6000823e3d90fd5b906040519261286884612312565b601984527f7472616e7366657228616464726573732c75696e74323536290000000000000060209485015260405163a9059cbb60e01b8186019081526001600160a01b0390931660248201526044808201929092529081526128c98161232e565b600092839283809351925af1903d156129a4573d67ffffffffffffffff81116129905760405190612903601f8201601f191686018361234a565b81528091843d92013e5b81612960575b501561291c5750565b6064906040519062461bcd60e51b82526004820152601e60248201527f537061726b53776170506169723a205452414e534645525f4641494c454400006044820152fd5b80518015925083908315612978575b50505038612913565b6129889350820181019101612598565b38828161296f565b634e487b7160e01b82526041600452602482fd5b50606061290d565b61271060016002815b8082116129cd575050816000190481116116d3570290565b9092806000190481116116d3578184166129ed575b800292811c906129b5565b809202916129e2565b91926001600160701b03908184111580612b51575b15612b0c57816040947f1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad19663ffffffff421693612a4a600f548661245d565b80151580612b01575b80612af6575b612a9d575b505050169283601054916001600160701b0360701b9060701b169163ffffffff60e01b1617179081601055600f55835192835260701c166020820152a1565b612aeb92612ae3926001600160e01b0391612acd9190612ac890612adb612ad3878787858789612bb5565b612beb565b166125b0565b600a5461246a565b600a55612bb5565b600b5461246a565b600b55388080612a5e565b508482161515612a59565b508483161515612a53565b60405162461bcd60e51b815260206004820152601760248201527f537061726b53776170506169723a204f564552464c4f570000000000000000006044820152606490fd5b5081831115612a0b565b9060006003831115612ba857508160019080821c8281018091116116d35791905b848310612b8857505050565b90919350612b9f84612b9a81846125c3565b61246a565b821c9190612b7c565b91612baf57565b60019150565b6dffffffffffffffffffffffffffff60701b607082901b16906001600160701b0316808204600160701b14901517156116d35790565b906001600160701b03169081156125cd576001600160e01b0316049056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122006280758518c180494fe710957cf10bd3f48348fea2a289b77823eb8356759ca64736f6c63430008130033