编译器
0.8.13+commit.abaa5c0e
文件 1 的 14:Address.sol
pragma solidity ^0.8.1;
library Address {
function isContract(address account) internal view returns (bool) {
return account.code.length > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
文件 2 的 14:Context.sol
pragma solidity ^0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
文件 3 的 14:ERC20.sol
pragma solidity ^0.8.0;
import "IERC20.sol";
import "IERC20Metadata.sol";
import "Context.sol";
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;
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
function name() public view virtual override returns (string memory) {
return _name;
}
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
function decimals() public view virtual override returns (uint8) {
return 18;
}
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
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;
}
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, _allowances[owner][spender] + addedValue);
return true;
}
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = _allowances[owner][spender];
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
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;
}
_balances[to] += amount;
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
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;
_balances[account] += amount;
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
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;
}
_totalSupply -= amount;
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
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);
}
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);
}
}
}
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
function _afterTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
}
文件 4 的 14:ERC20Burnable.sol
pragma solidity ^0.8.0;
import "ERC20.sol";
import "Context.sol";
abstract contract ERC20Burnable is Context, ERC20 {
function burn(uint256 amount) public virtual {
_burn(_msgSender(), amount);
}
function burnFrom(address account, uint256 amount) public virtual {
_spendAllowance(account, _msgSender(), amount);
_burn(account, amount);
}
}
文件 5 的 14:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
文件 6 的 14:IERC20Metadata.sol
pragma solidity ^0.8.0;
import "IERC20.sol";
interface IERC20Metadata is IERC20 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
}
文件 7 的 14:IStablePlaza.sol
pragma solidity >=0.6.0;
import "IERC20.sol";
interface IStablePlaza {
error TokenNotFound();
error ExchangeLocked();
error InsufficientOutput();
error InvariantViolation();
error StakeIsStillLocked();
error AdminRightsRequired();
error TokenReserveNotEmpty();
error InsufficientLiquidity();
error ExcessiveLiquidityInput();
error InsufficientFlashloanRepayment();
error ZeroStakeAdditionIsNotSupported();
struct Config {
uint16 locked;
uint8 feeLevel;
uint8 flashLoanFeeLevel;
uint8 stakerFeeFraction;
uint8 maxLockingBonus;
uint16 maxLockingTime;
uint64 Delta;
uint64 unclaimedRewards;
uint64 totalSupply;
}
struct Token {
IERC20 token;
uint64 denormFactor;
}
struct SwapVariables {
Token token0;
Token token1;
uint256 balance0;
uint256 balance1;
uint256 inputAmount;
}
struct StakingState {
uint64 totalShares;
uint96 rewardsPerShare;
uint64 lastSyncedUnclaimedRewards;
}
struct StakerData {
uint64 stakedAmount;
uint64 sharesEquivalent;
uint96 rewardsPerShareWhenStaked;
uint32 unlockTime;
}
function getIndex(IERC20 token) external view returns (uint256 index);
function getTokenFromIndex(uint256 index) external view returns (IERC20 token);
function getOutFromIn(
uint256 inputIndex,
uint256 outputIndex,
uint256 inputAmount
) external view returns(uint256 maxOutputAmount);
function getInFromOut(
uint256 inputIndex,
uint256 outputIndex,
uint256 outputAmount
) external view returns(uint256 minInputAmount);
function getLPsFromInput(
uint256 tokenIndex,
uint256 inputAmount
) external view returns(uint256 maxLPamount);
function getInputFromLPs(
uint256 tokenIndex,
uint256 LPamount,
bool fromCallback
) external view returns(uint256 minInputAmount);
function getOutputFromLPs(
uint256 tokenIndex,
uint256 LPamount
) external view returns(uint256 maxOutputAmount);
function getLPsFromOutput(
uint256 tokenIndex,
uint256 outputAmount
) external view returns(uint256 minLPamount);
function easySwap(
uint256 pairSelector,
uint256 inputAmount,
uint256 minOutputAmount,
address destination
) external returns (uint256 actualOutput);
function swap(
uint256 pairSelector,
uint256 outputAmount,
address destination,
bytes calldata data
) external;
function easyAdd(
uint256 tokenIndex,
uint256 inputAmount,
uint256 minLP,
address destination
) external returns (uint256 actualLP);
function addLiquidity(
uint256 tokenIndex,
uint256 LPamount,
address destination,
bytes calldata data
) external;
function easyRemove(
uint256 tokenIndex,
uint256 LPamount,
uint256 minOutputAmount,
address destination
) external returns (uint256 actualOutput);
function removeLiquidity(
uint256 tokenIndex,
uint256 outputAmount,
address destination,
bytes calldata data
) external;
event Swap(
address sender,
IERC20 inputToken,
IERC20 outputToken,
uint256 inputAmount,
uint256 outputAmount,
address destination
);
event FlashLoan(
address lender,
IERC20 token,
uint256 amountLoaned,
uint256 amountRepayed
);
event LiquidityAdded(
address sender,
IERC20 token,
uint256 tokenAmount,
uint256 LPs
);
event LiquidityRemoved(
address creditor,
IERC20 token,
uint256 tokenAmount,
uint256 LPs
);
event ListingChange(
IERC20 removedToken,
IERC20 replacementToken
);
event AdminChanged(
address newAdmin
);
event LockChanged(
address exchangeAdmin,
uint256 newLockValue
);
event ConfigUpdated(
uint8 newFeeLevel,
uint8 newFlashLoanFeeLevel,
uint8 newStakerFeeFraction,
uint8 newMaxLockingBonus,
uint16 newMaxLockingTime
);
}
文件 8 的 14:IStablePlazaAddCallee.sol
pragma solidity >=0.6.0;
import "IERC20.sol";
interface IStablePlazaAddCallee {
function stablePlazaAddCall(uint256 LPamount, IERC20 tokenToPay, uint256 amountToPay, bytes calldata data) external;
}
文件 9 的 14:IStablePlazaRemoveCallee.sol
pragma solidity >=0.6.0;
import "IERC20.sol";
interface IStablePlazaRemoveCallee {
function stablePlazaRemoveCall(IERC20 outputToken, uint256 outputAmount, uint256 LPtoBurn, bytes calldata data) external;
}
文件 10 的 14:IStablePlazaSwapCallee.sol
pragma solidity >=0.6.0;
import "IERC20.sol";
interface IStablePlazaSwapCallee {
function stablePlazaSwapCall(IERC20 outputToken, uint256 outputAmount, IERC20 tokenToPay, uint256 amountToPay, bytes calldata data) external;
}
文件 11 的 14:IStakingContract.sol
pragma solidity >=0.6.0;
interface IStakingContract {
function stake(
uint256 amountToStake,
uint32 voluntaryLockupTime
) external;
function unstake(
uint256 amountToUnstake
) external;
event Staked(
address staker,
uint256 stakedAmount,
uint64 sharesEquivalent
);
event Unstaked(
address staker,
uint256 unstakedAmount,
uint64 sharesDestroyed,
uint256 rewards
);
}
文件 12 的 14:Ownable.sol
pragma solidity ^0.8.0;
import "Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor() {
_transferOwnership(_msgSender());
}
function owner() public view virtual returns (address) {
return _owner;
}
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 13 的 14:SafeERC20.sol
pragma solidity ^0.8.0;
import "IERC20.sol";
import "Address.sol";
library SafeERC20 {
using Address for address;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(
IERC20 token,
address spender,
uint256 value
) internal {
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
文件 14 的 14:StablePlaza.sol
pragma solidity 0.8.13;
import "IStablePlaza.sol";
import "IStakingContract.sol";
import "IStablePlazaAddCallee.sol";
import "IStablePlazaSwapCallee.sol";
import "IStablePlazaRemoveCallee.sol";
import "Ownable.sol";
import "ERC20.sol";
import "IERC20.sol";
import "SafeERC20.sol";
import "ERC20Burnable.sol";
import "IERC20Metadata.sol";
contract StablePlaza is IStablePlaza, IStakingContract, Ownable, ERC20Burnable {
using SafeERC20 for IERC20;
uint8 constant BASE_DECIMALS = 6;
uint256 constant NR_OF_TOKENS = 4;
uint16 constant TRADE_LOCK_MASK = 0x0001;
uint16 constant ADMIN_LOCK_MASK = 0x0002;
uint16 constant ADMIN_UNLOCK_MASK = 0xFFFD;
uint64 constant MIN_SHARES = 232830643653;
uint256 constant LP_FACTOR_ADD = 201_000_000;
uint256 constant LP_FACTOR_REMOVE = 202_493_812;
uint256 constant NORMALIZE_FACTOR = 1_000_000;
uint256 constant SECONDS_PER_DAY = 86400;
IERC20 public immutable stakingToken;
Token[NR_OF_TOKENS] public tokens;
uint64[NR_OF_TOKENS] public reserves;
uint64[NR_OF_TOKENS] public denormFactors;
mapping(address => StakerData) public stakerData;
mapping(IERC20 => uint256) private offsetIndex;
address public admin;
Config public SPconfig = Config({
locked: ADMIN_LOCK_MASK,
feeLevel: 3,
flashLoanFeeLevel: 3,
stakerFeeFraction: 85,
maxLockingBonus: 2,
maxLockingTime: 180,
Delta: 0,
unclaimedRewards: 1_000_000,
totalSupply: 0
});
StakingState public stakingState = StakingState({
totalShares: MIN_SHARES,
rewardsPerShare: 0,
lastSyncedUnclaimedRewards: 0
});
constructor(IERC20[] memory tokensToList, IERC20 stakingTokenAddress) ERC20("StablePlaza", "XSP") {
stakingToken = stakingTokenAddress;
uint64 d;
IERC20 previous;
IERC20 current;
if (tokensToList.length != NR_OF_TOKENS) { revert(); }
for (uint256 i; i < NR_OF_TOKENS; ) {
current = tokensToList[i];
if (previous >= current) { revert(); }
d = uint64(10**(IERC20Metadata(address(current)).decimals() - BASE_DECIMALS));
denormFactors[i] = d;
offsetIndex[current] = i + 1;
Token storage t = tokens[i];
t.token = current;
t.denormFactor = d;
previous = current;
unchecked { ++i; }
}
}
modifier onlyAdmin() {
if (msg.sender != admin && msg.sender != owner()) { revert AdminRightsRequired(); }
_;
}
function decimals() public view virtual override returns (uint8) { return BASE_DECIMALS; }
function getIndex(IERC20 token) external view override returns (uint256 index)
{
index = offsetIndex[token];
if (index == 0) { revert TokenNotFound(); }
--index;
}
function getTokenFromIndex(uint256 index) external view override returns (IERC20 token)
{
token = tokens[index].token;
}
function getOutFromIn(
uint256 inputIndex,
uint256 outputIndex,
uint256 inputAmount
)
public view override returns(uint256 maxOutputAmount)
{
(uint256 R0, uint256 R1, uint256 d0, uint256 d1, Config memory c) = _getPairReservesAndConfig(inputIndex, outputIndex);
uint256 oneMinusFee = 10_000 - c.feeLevel;
uint256 Delta = uint256(c.Delta);
inputAmount = inputAmount / d0;
maxOutputAmount = oneMinusFee * inputAmount * (R1 + Delta) / ((R0 + Delta) * 10_000 + oneMinusFee * inputAmount) * d1;
if (maxOutputAmount > R1 * d1) maxOutputAmount = R1 * d1;
}
function getInFromOut(
uint256 inputIndex,
uint256 outputIndex,
uint256 outputAmount
)
public view override returns(uint256 minInputAmount)
{
(uint256 R0, uint256 R1, uint256 d0, uint256 d1, Config memory c) = _getPairReservesAndConfig(inputIndex, outputIndex);
outputAmount = (outputAmount - 1) / d1 + 1;
if (outputAmount > R1) { revert InsufficientLiquidity(); }
minInputAmount = ((R0 + c.Delta) * outputAmount * 10_000 / (((R1 + c.Delta) - outputAmount) * (10_000 - c.feeLevel)) + 1) * d0;
}
function getLPsFromInput(
uint256 tokenIndex,
uint256 inputAmount
)
public view override returns(uint256 maxLPamount)
{
(uint256 R, uint256 d, Config memory c) = _getReservesAndConfig(tokenIndex);
inputAmount = inputAmount / d;
if (inputAmount >= R + c.Delta >> 5) { revert ExcessiveLiquidityInput(); }
uint256 X = (inputAmount << 128) / (R + c.Delta);
uint256 X_ = X * X >> 128;
uint256 R_ = (X >> 2) - (X_ * 3 >> 5);
X_ = X_ * X >> 128;
R_ = R_ + (X_ * 7 >> 7);
X_ = X_ * X >> 128;
R_ = R_ - (X_ * 77 >> 11);
X_ = X_ * X >> 128;
R_ = R_ + (X_ * 231 >> 13);
X_ = X_ * X >> 128;
R_ = R_ - (X_ * 1463 >> 16);
maxLPamount = (R_ * LP_FACTOR_ADD * (totalSupply() + c.unclaimedRewards) / NORMALIZE_FACTOR) >> 128;
}
function getInputFromLPs(
uint256 tokenIndex,
uint256 LPamount,
bool fromCallback
)
public view override returns(uint256 minInputAmount)
{
uint256 F_ = 1 << 120;
(uint256 R, uint256 d, Config memory c) = _getReservesAndConfig(tokenIndex);
uint256 correction = fromCallback ? LPamount : 0;
uint256 totalLPs = (totalSupply() - correction + c.unclaimedRewards) * LP_FACTOR_ADD / NORMALIZE_FACTOR;
if (LPamount > totalLPs >> 6) { revert ExcessiveLiquidityInput(); }
F_ += (LPamount << 120) / totalLPs;
F_ = F_ * F_ >> 120;
F_ = F_ * F_ >> 120;
minInputAmount = (((F_ - (1 << 120)) * (R + c.Delta) >> 120) + 1) * d;
}
function getOutputFromLPs(
uint256 tokenIndex,
uint256 LPamount
)
public view override returns(uint256 maxOutputAmount)
{
uint256 F_ = 1 << 128;
(uint256 R, uint256 d, Config memory c) = _getReservesAndConfig(tokenIndex);
F_ -= (LPamount << 128) * NORMALIZE_FACTOR / (LP_FACTOR_REMOVE * (totalSupply() + c.unclaimedRewards));
F_ = F_ * F_ >> 128;
F_ = F_ * F_ >> 128;
maxOutputAmount = (R + c.Delta) * ((1 << 128) - F_) >> 128;
maxOutputAmount = maxOutputAmount > R ? R : maxOutputAmount;
maxOutputAmount *= d;
}
function getLPsFromOutput(
uint256 tokenIndex,
uint256 outputAmount
)
public view override returns(uint256 minLPamount)
{
(uint256 R, uint256 d, Config memory c) = _getReservesAndConfig(tokenIndex);
outputAmount = (outputAmount - 1) / d + 1;
if (outputAmount > R) { revert InsufficientLiquidity(); }
uint256 X = (outputAmount << 128) / (R + c.Delta);
uint256 X_ = X * X >> 128;
uint256 R_ = (X >> 2) + (X_ * 3 >> 5);
X_ = X_ * X >> 128;
R_ = R_ + (X_ * 7 >> 7);
X_ = X_ * X >> 128;
R_ = R_ + (X_ * 77 >> 11);
X_ = X_ * X >> 128;
R_ = R_ + (X_ * 231 >> 13);
X_ = X_ * X >> 128;
R_ = R_ + (X_ * 1463 >> 16);
minLPamount = (R_ * LP_FACTOR_REMOVE * (totalSupply() + c.unclaimedRewards) / NORMALIZE_FACTOR >> 128) + 1;
}
function easySwap(
uint256 pairSelector,
uint256 inputAmount,
uint256 minOutputAmount,
address destination
)
external override returns (uint256 actualOutputAmount)
{
uint256 index0 = pairSelector & 0xFF;
uint256 index1 = pairSelector >> 8;
actualOutputAmount = getOutFromIn(index0, index1, inputAmount);
if (actualOutputAmount < minOutputAmount) { revert InsufficientOutput(); }
tokens[index0].token.safeTransferFrom(msg.sender, address(this), inputAmount);
swap(pairSelector, actualOutputAmount, destination, new bytes(0));
}
function swap(
uint256 pairSelector,
uint256 outputAmount,
address destination,
bytes memory data
)
public override
{
Config memory c = SPconfig;
if (c.locked != 0) { revert ExchangeLocked(); }
SPconfig.locked = TRADE_LOCK_MASK;
SwapVariables memory v;
uint256 index0 = pairSelector & 0xFF;
uint256 index1 = pairSelector >> 8;
v.token0 = tokens[index0];
v.token1 = tokens[index1];
uint256 allReserves;
uint256 d0 = uint256(v.token0.denormFactor);
assembly { allReserves := sload(reserves.slot) }
v.token1.token.safeTransfer(destination, outputAmount);
if (data.length != 0) {
uint256 amountToPay = (index0 != index1) ? getInFromOut(index0, index1, outputAmount) : (((outputAmount - 1) / d0 + 1) * (c.flashLoanFeeLevel + 10_000) / 10_000) * d0;
IStablePlazaSwapCallee(msg.sender).stablePlazaSwapCall(v.token1.token, outputAmount, v.token0.token, amountToPay, data);
}
{
uint256 R0 = ((allReserves >> index0 * 64) & 0xFFFFFFFFFFFFFFFF);
uint256 R1 = ((allReserves >> index1 * 64) & 0xFFFFFFFFFFFFFFFF);
v.balance0 = v.token0.token.balanceOf(address(this)) / d0;
if (index1 == index0) {
uint256 scaledOutputAmount = (outputAmount - 1) / d0 + 1;
v.inputAmount = v.balance0 - (R0 - scaledOutputAmount);
if (v.balance0 < R0 + scaledOutputAmount * c.flashLoanFeeLevel / 10_000) { revert InsufficientFlashloanRepayment(); }
}
else {
uint256 Delta = uint256(c.Delta);
v.inputAmount = v.balance0 - R0;
v.balance1 = R1 - ((outputAmount - 1) / v.token1.denormFactor + 1);
uint256 B0 = (v.balance0 + Delta) * 10_000 - v.inputAmount * c.feeLevel;
uint256 B1 = (v.balance1 + Delta);
if (B0 * B1 < (R0 + Delta) * (R1 + Delta) * 10_000) { revert InvariantViolation(); }
}
}
allReserves = (allReserves & (type(uint256).max - (0xFFFFFFFFFFFFFFFF << index1 * 64))) | ((v.balance1 & 0xFFFFFFFFFFFFFFFF) << index1 * 64);
allReserves = (allReserves & (type(uint256).max - (0xFFFFFFFFFFFFFFFF << index0 * 64))) | ((v.balance0 & 0xFFFFFFFFFFFFFFFF) << index0 * 64);
assembly { sstore(reserves.slot, allReserves) }
SPconfig.unclaimedRewards = c.unclaimedRewards + uint64(v.inputAmount * c.feeLevel / 10_000 * c.stakerFeeFraction / 256);
SPconfig.locked = 0;
if (index1 == index0) { emit FlashLoan(msg.sender, v.token0.token, outputAmount, v.inputAmount * d0); }
else { emit Swap(msg.sender, v.token0.token, v.token1.token, v.inputAmount * d0, outputAmount, destination); }
}
function easyAdd(
uint256 tokenIndex,
uint256 inputAmount,
uint256 minLP,
address destination
)
external override returns (uint256 actualLP)
{
actualLP = getLPsFromInput(tokenIndex, inputAmount);
if (actualLP < minLP) { revert InsufficientOutput(); }
tokens[tokenIndex].token.safeTransferFrom(msg.sender, address(this), inputAmount);
addLiquidity(tokenIndex, actualLP, destination, new bytes(0));
}
function addLiquidity(
uint256 tokenIndex,
uint256 LPamount,
address destination,
bytes memory data
)
public override
{
Config memory c = SPconfig;
if (c.locked != 0) { revert ExchangeLocked(); }
SPconfig.locked = TRADE_LOCK_MASK;
uint256 allReserves;
assembly { allReserves := sload(reserves.slot) }
Token memory token = tokens[tokenIndex];
uint256 R = ((allReserves >> tokenIndex * 64) & 0xFFFFFFFFFFFFFFFF);
uint256 d = uint256(token.denormFactor);
uint256 t = totalSupply();
_mint(destination, LPamount);
if (data.length != 0) {
uint256 amountToPay = getInputFromLPs(tokenIndex, LPamount, true);
IStablePlazaAddCallee(msg.sender).stablePlazaAddCall(LPamount, token.token, amountToPay, data);
}
uint256 B = token.token.balanceOf(address(this)) / d;
{
uint256 LP0_ = (t + c.unclaimedRewards) * LP_FACTOR_ADD / NORMALIZE_FACTOR;
uint256 LP1_ = LP0_ + LPamount;
LP0_ = LP0_ * LP0_;
LP0_ = LP0_ * LP0_ >> 128;
LP1_ = LP1_ * LP1_;
LP1_ = LP1_ * LP1_ >> 128;
if ((B + c.Delta) * LP0_ < (R + c.Delta) * LP1_) { revert InvariantViolation(); }
}
allReserves = (allReserves & (type(uint256).max - (0xFFFFFFFFFFFFFFFF << tokenIndex * 64))) | ((B & 0xFFFFFFFFFFFFFFFF) << tokenIndex * 64);
assembly { sstore(reserves.slot, allReserves) }
SPconfig.Delta = uint64(_calcDelta(allReserves));
SPconfig.totalSupply = uint64(t + LPamount);
SPconfig.locked = 0;
emit LiquidityAdded(destination, token.token, (B - R) * d, LPamount);
}
function easyRemove(
uint256 tokenIndex,
uint256 LPamount,
uint256 minOutputAmount,
address destination
)
external override returns (uint256 actualOutput)
{
actualOutput = getOutputFromLPs(tokenIndex, LPamount);
if (actualOutput < minOutputAmount) { revert InsufficientOutput(); }
_burn(msg.sender, LPamount);
removeLiquidity(tokenIndex, actualOutput, destination, new bytes(0));
}
function removeLiquidity(
uint256 tokenIndex,
uint256 outputAmount,
address destination,
bytes memory data
)
public override
{
Config memory c = SPconfig;
if (c.locked & TRADE_LOCK_MASK != 0) { revert ExchangeLocked(); }
SPconfig.locked = TRADE_LOCK_MASK;
Token memory token = tokens[tokenIndex];
token.token.safeTransfer(destination, outputAmount);
if (data.length != 0) {
uint256 LPtoBurn = getLPsFromOutput(tokenIndex, outputAmount);
IStablePlazaRemoveCallee(msg.sender).stablePlazaRemoveCall(token.token, outputAmount, LPtoBurn, data);
}
uint256 allReserves;
assembly { allReserves := sload(reserves.slot) }
uint256 R = ((allReserves >> tokenIndex * 64) & 0xFFFFFFFFFFFFFFFF);
uint256 d = uint256(token.denormFactor);
uint256 LPtokens = totalSupply();
uint256 previousSupply = uint256(c.totalSupply);
outputAmount = (outputAmount - 1) / d + 1;
{
uint256 Delta = uint256(c.Delta);
uint256 LP0_ = (previousSupply + c.unclaimedRewards) * LP_FACTOR_REMOVE / NORMALIZE_FACTOR;
uint256 LP1_ = LP0_ + LPtokens - previousSupply;
LP0_ = LP0_ * LP0_;
LP0_ = LP0_ * LP0_ >> 128;
LP1_ = LP1_ * LP1_;
LP1_ = LP1_ * LP1_ >> 128;
if ((R - outputAmount + Delta) * LP0_ < (R + Delta) * LP1_) { revert InvariantViolation(); }
}
allReserves = (allReserves & (type(uint256).max - (0xFFFFFFFFFFFFFFFF << tokenIndex * 64))) | (((R - outputAmount) & 0xFFFFFFFFFFFFFFFF) << tokenIndex * 64);
assembly { sstore(reserves.slot, allReserves) }
SPconfig.Delta = uint64(_calcDelta(allReserves));
SPconfig.totalSupply = uint64(LPtokens);
SPconfig.locked = 0;
emit LiquidityRemoved(msg.sender, token.token, outputAmount * d, previousSupply - LPtokens);
}
function stake(
uint256 amountToStake,
uint32 voluntaryLockupTime
)
external override
{
if (amountToStake == 0) { revert ZeroStakeAdditionIsNotSupported(); }
stakingToken.safeTransferFrom(msg.sender, address(this), amountToStake);
StakerData memory d = stakerData[msg.sender];
if (d.stakedAmount != 0) { unstake(0); }
Config memory c = SPconfig;
StakingState memory s = stakingState;
uint256 unsyncedRewards = uint256(c.unclaimedRewards) - s.lastSyncedUnclaimedRewards;
s.rewardsPerShare += uint96((unsyncedRewards << 80) / s.totalShares);
uint256 maxLockTime = uint256(c.maxLockingTime) * SECONDS_PER_DAY;
uint256 lockupTime = voluntaryLockupTime > maxLockTime ? maxLockTime : voluntaryLockupTime;
uint64 sharesEq = uint64((amountToStake >> 32) * ((1 << 32) + uint256(c.maxLockingBonus) * lockupTime * lockupTime * (1 << 32) / (maxLockTime * maxLockTime)) >> 32);
uint32 unlockTime = d.unlockTime > uint32(block.timestamp + lockupTime) ? d.unlockTime : uint32(block.timestamp + lockupTime);
StakerData storage D = stakerData[msg.sender];
D.stakedAmount = d.stakedAmount + uint64(amountToStake >> 32);
D.sharesEquivalent = d.sharesEquivalent + sharesEq;
D.rewardsPerShareWhenStaked = s.rewardsPerShare;
D.unlockTime = unlockTime;
s.totalShares += sharesEq;
s.lastSyncedUnclaimedRewards = c.unclaimedRewards;
stakingState = s;
emit Staked(msg.sender, amountToStake, sharesEq);
}
function unstake(
uint256 amountToUnstake
)
public override
{
Config memory c = SPconfig;
StakingState memory s = stakingState;
StakerData memory d = stakerData[msg.sender];
if (amountToUnstake != 0 && block.timestamp < d.unlockTime) { revert StakeIsStillLocked(); }
uint256 newRewards = uint256(c.unclaimedRewards) - s.lastSyncedUnclaimedRewards;
s.rewardsPerShare += uint96((newRewards << 80) / s.totalShares);
uint256 rewards = uint256(s.rewardsPerShare - d.rewardsPerShareWhenStaked) * d.sharesEquivalent >> 80;
d.rewardsPerShareWhenStaked = s.rewardsPerShare;
c.unclaimedRewards -= uint64(rewards);
s.lastSyncedUnclaimedRewards = c.unclaimedRewards;
SPconfig.unclaimedRewards = c.unclaimedRewards;
uint64 sharesDestroyed;
if (amountToUnstake != 0) {
uint256 bonus = (uint256(d.sharesEquivalent) << 64) / d.stakedAmount;
uint256 remainder = ((uint256(d.stakedAmount) << 32) - amountToUnstake) >> 32;
uint256 sharesRemainder = remainder * bonus >> 64;
sharesDestroyed = d.sharesEquivalent - uint64(sharesRemainder);
s.totalShares -= sharesDestroyed;
d.stakedAmount = uint64(remainder);
d.sharesEquivalent = uint64(sharesRemainder);
stakingToken.safeTransfer(msg.sender, amountToUnstake);
}
stakingState = s;
if (d.stakedAmount == 0) { delete stakerData[msg.sender]; }
else { stakerData[msg.sender] = d; }
_mint(msg.sender, rewards);
emit Unstaked(msg.sender, amountToUnstake, sharesDestroyed, rewards);
}
function lockExchange() external onlyAdmin() {
SPconfig.locked = SPconfig.locked | ADMIN_LOCK_MASK;
emit LockChanged(msg.sender, SPconfig.locked);
}
function unlockExchange() external onlyAdmin() {
SPconfig.locked = SPconfig.locked & ADMIN_UNLOCK_MASK;
emit LockChanged(msg.sender, SPconfig.locked);
}
function changeListedToken(uint8 outgoingIndex, IERC20 incomingAddress)
external onlyOwner()
{
if (reserves[outgoingIndex] != 0) { revert TokenReserveNotEmpty(); }
IERC20 outgoingAddress = tokens[outgoingIndex].token;
Token memory token;
token.token = incomingAddress;
token.denormFactor = uint64(10**(IERC20Metadata(address(incomingAddress)).decimals() - BASE_DECIMALS));
denormFactors[outgoingIndex] = token.denormFactor;
tokens[outgoingIndex] = token;
delete offsetIndex[outgoingAddress];
offsetIndex[incomingAddress] = outgoingIndex + 1;
emit ListingChange(outgoingAddress, incomingAddress);
}
function setAdmin(address adminAddress) external onlyOwner() {
admin = adminAddress;
emit AdminChanged(adminAddress);
}
function updateConfig(
uint8 newFeeLevel,
uint8 newFlashLoanFeeLevel,
uint8 newStakerFeeFraction,
uint8 newMaxLockingBonus,
uint16 newMaxLockingTime
) external onlyOwner() {
Config storage C = SPconfig;
C.feeLevel = newFeeLevel;
C.flashLoanFeeLevel = newFlashLoanFeeLevel;
C.stakerFeeFraction = newStakerFeeFraction;
C.maxLockingBonus = newMaxLockingBonus;
C.maxLockingTime = newMaxLockingTime;
emit ConfigUpdated(newFeeLevel, newFlashLoanFeeLevel, newStakerFeeFraction, newMaxLockingBonus, newMaxLockingTime);
}
function initialise() external onlyOwner() {
if (SPconfig.Delta != 0) { revert(); }
uint256 reserve;
uint256 allReserves;
uint256 toMint;
for (uint256 i; i < NR_OF_TOKENS; ) {
Token memory token = tokens[i];
reserve = token.token.balanceOf(address(this)) / token.denormFactor;
allReserves = (allReserves & (type(uint256).max - (0xFFFFFFFFFFFFFFFF << i * 64))) | ((reserve & 0xFFFFFFFFFFFFFFFF) << i * 64);
toMint += reserve;
unchecked { ++i; }
}
assembly { sstore(reserves.slot, allReserves) }
SPconfig.Delta = uint64(_calcDelta(allReserves));
SPconfig.totalSupply = uint64(toMint);
_mint(msg.sender, toMint);
}
function _calcDelta(uint256 allReserves) internal pure returns (uint256 Delta) {
for (uint256 i; i < NR_OF_TOKENS; ) {
Delta += (allReserves >> i * 64) & 0xFFFFFFFFFFFFFFFF;
unchecked { ++i; }
}
Delta = Delta * 50;
}
function _getPairReservesAndConfig(uint256 inputIndex, uint256 outputIndex)
internal view returns (
uint256 R0,
uint256 R1,
uint256 d0,
uint256 d1,
Config memory config )
{
config = SPconfig;
uint256 allReserves;
uint256 allDenormFactors;
assembly { allReserves := sload(reserves.slot) }
assembly { allDenormFactors := sload(denormFactors.slot) }
R0 = (allReserves >> inputIndex * 64) & 0xFFFFFFFFFFFFFFFF;
R1 = (allReserves >> outputIndex * 64) & 0xFFFFFFFFFFFFFFFF;
d0 = (allDenormFactors >> inputIndex * 64) & 0xFFFFFFFFFFFFFFFF;
d1 = (allDenormFactors >> outputIndex * 64) & 0xFFFFFFFFFFFFFFFF;
}
function _getReservesAndConfig(uint256 tokenIndex)
internal view returns (uint256 R, uint256 d, Config memory config) {
R = reserves[tokenIndex];
d = denormFactors[tokenIndex];
config = SPconfig;
}
}
{
"compilationTarget": {
"StablePlaza.sol": "StablePlaza"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"contract IERC20[]","name":"tokensToList","type":"address[]"},{"internalType":"contract IERC20","name":"stakingTokenAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AdminRightsRequired","type":"error"},{"inputs":[],"name":"ExcessiveLiquidityInput","type":"error"},{"inputs":[],"name":"ExchangeLocked","type":"error"},{"inputs":[],"name":"InsufficientFlashloanRepayment","type":"error"},{"inputs":[],"name":"InsufficientLiquidity","type":"error"},{"inputs":[],"name":"InsufficientOutput","type":"error"},{"inputs":[],"name":"InvariantViolation","type":"error"},{"inputs":[],"name":"StakeIsStillLocked","type":"error"},{"inputs":[],"name":"TokenNotFound","type":"error"},{"inputs":[],"name":"TokenReserveNotEmpty","type":"error"},{"inputs":[],"name":"ZeroStakeAdditionIsNotSupported","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"newFeeLevel","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"newFlashLoanFeeLevel","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"newStakerFeeFraction","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"newMaxLockingBonus","type":"uint8"},{"indexed":false,"internalType":"uint16","name":"newMaxLockingTime","type":"uint16"}],"name":"ConfigUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"lender","type":"address"},{"indexed":false,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountLoaned","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountRepayed","type":"uint256"}],"name":"FlashLoan","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"LPs","type":"uint256"}],"name":"LiquidityAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"creditor","type":"address"},{"indexed":false,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"LPs","type":"uint256"}],"name":"LiquidityRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IERC20","name":"removedToken","type":"address"},{"indexed":false,"internalType":"contract IERC20","name":"replacementToken","type":"address"}],"name":"ListingChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"exchangeAdmin","type":"address"},{"indexed":false,"internalType":"uint256","name":"newLockValue","type":"uint256"}],"name":"LockChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"uint256","name":"stakedAmount","type":"uint256"},{"indexed":false,"internalType":"uint64","name":"sharesEquivalent","type":"uint64"}],"name":"Staked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"contract IERC20","name":"inputToken","type":"address"},{"indexed":false,"internalType":"contract IERC20","name":"outputToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"inputAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"outputAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"destination","type":"address"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"uint256","name":"unstakedAmount","type":"uint256"},{"indexed":false,"internalType":"uint64","name":"sharesDestroyed","type":"uint64"},{"indexed":false,"internalType":"uint256","name":"rewards","type":"uint256"}],"name":"Unstaked","type":"event"},{"inputs":[],"name":"SPconfig","outputs":[{"internalType":"uint16","name":"locked","type":"uint16"},{"internalType":"uint8","name":"feeLevel","type":"uint8"},{"internalType":"uint8","name":"flashLoanFeeLevel","type":"uint8"},{"internalType":"uint8","name":"stakerFeeFraction","type":"uint8"},{"internalType":"uint8","name":"maxLockingBonus","type":"uint8"},{"internalType":"uint16","name":"maxLockingTime","type":"uint16"},{"internalType":"uint64","name":"Delta","type":"uint64"},{"internalType":"uint64","name":"unclaimedRewards","type":"uint64"},{"internalType":"uint64","name":"totalSupply","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenIndex","type":"uint256"},{"internalType":"uint256","name":"LPamount","type":"uint256"},{"internalType":"address","name":"destination","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"addLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burnFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"outgoingIndex","type":"uint8"},{"internalType":"contract IERC20","name":"incomingAddress","type":"address"}],"name":"changeListedToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"denormFactors","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenIndex","type":"uint256"},{"internalType":"uint256","name":"inputAmount","type":"uint256"},{"internalType":"uint256","name":"minLP","type":"uint256"},{"internalType":"address","name":"destination","type":"address"}],"name":"easyAdd","outputs":[{"internalType":"uint256","name":"actualLP","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenIndex","type":"uint256"},{"internalType":"uint256","name":"LPamount","type":"uint256"},{"internalType":"uint256","name":"minOutputAmount","type":"uint256"},{"internalType":"address","name":"destination","type":"address"}],"name":"easyRemove","outputs":[{"internalType":"uint256","name":"actualOutput","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"pairSelector","type":"uint256"},{"internalType":"uint256","name":"inputAmount","type":"uint256"},{"internalType":"uint256","name":"minOutputAmount","type":"uint256"},{"internalType":"address","name":"destination","type":"address"}],"name":"easySwap","outputs":[{"internalType":"uint256","name":"actualOutputAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"inputIndex","type":"uint256"},{"internalType":"uint256","name":"outputIndex","type":"uint256"},{"internalType":"uint256","name":"outputAmount","type":"uint256"}],"name":"getInFromOut","outputs":[{"internalType":"uint256","name":"minInputAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"getIndex","outputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenIndex","type":"uint256"},{"internalType":"uint256","name":"LPamount","type":"uint256"},{"internalType":"bool","name":"fromCallback","type":"bool"}],"name":"getInputFromLPs","outputs":[{"internalType":"uint256","name":"minInputAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenIndex","type":"uint256"},{"internalType":"uint256","name":"inputAmount","type":"uint256"}],"name":"getLPsFromInput","outputs":[{"internalType":"uint256","name":"maxLPamount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenIndex","type":"uint256"},{"internalType":"uint256","name":"outputAmount","type":"uint256"}],"name":"getLPsFromOutput","outputs":[{"internalType":"uint256","name":"minLPamount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"inputIndex","type":"uint256"},{"internalType":"uint256","name":"outputIndex","type":"uint256"},{"internalType":"uint256","name":"inputAmount","type":"uint256"}],"name":"getOutFromIn","outputs":[{"internalType":"uint256","name":"maxOutputAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenIndex","type":"uint256"},{"internalType":"uint256","name":"LPamount","type":"uint256"}],"name":"getOutputFromLPs","outputs":[{"internalType":"uint256","name":"maxOutputAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getTokenFromIndex","outputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"initialise","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lockExchange","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenIndex","type":"uint256"},{"internalType":"uint256","name":"outputAmount","type":"uint256"},{"internalType":"address","name":"destination","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"removeLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"reserves","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"adminAddress","type":"address"}],"name":"setAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountToStake","type":"uint256"},{"internalType":"uint32","name":"voluntaryLockupTime","type":"uint32"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"stakerData","outputs":[{"internalType":"uint64","name":"stakedAmount","type":"uint64"},{"internalType":"uint64","name":"sharesEquivalent","type":"uint64"},{"internalType":"uint96","name":"rewardsPerShareWhenStaked","type":"uint96"},{"internalType":"uint32","name":"unlockTime","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakingState","outputs":[{"internalType":"uint64","name":"totalShares","type":"uint64"},{"internalType":"uint96","name":"rewardsPerShare","type":"uint96"},{"internalType":"uint64","name":"lastSyncedUnclaimedRewards","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakingToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"pairSelector","type":"uint256"},{"internalType":"uint256","name":"outputAmount","type":"uint256"},{"internalType":"address","name":"destination","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"swap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tokens","outputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint64","name":"denormFactor","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unlockExchange","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountToUnstake","type":"uint256"}],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"newFeeLevel","type":"uint8"},{"internalType":"uint8","name":"newFlashLoanFeeLevel","type":"uint8"},{"internalType":"uint8","name":"newStakerFeeFraction","type":"uint8"},{"internalType":"uint8","name":"newMaxLockingBonus","type":"uint8"},{"internalType":"uint16","name":"newMaxLockingTime","type":"uint16"}],"name":"updateConfig","outputs":[],"stateMutability":"nonpayable","type":"function"}]