My Name Tag:
Not Available, login to update
[ Download CSV Export ]
Latest 2 internal transactions
Parent Txn Hash | Block | From | To | Value | |||
---|---|---|---|---|---|---|---|
0x0e475e4e609fa74e3af0a5f65dcadf812e104059b1fb4459b187c73f00428692 | 396452 | 420 days 2 hrs ago | Cronos Stable Swap: Swap | Contract Creation | 0 CRO | ||
0x0e475e4e609fa74e3af0a5f65dcadf812e104059b1fb4459b187c73f00428692 | 396452 | 420 days 2 hrs ago | 0xdc9cde9750cf3062c43dcf31a5321477627e07d2 | Contract Creation | 0 CRO |
[ Download CSV Export ]
Minimal Proxy Contract for 0xacebab1b289d11fa3d1deea034d2a57ea17ece03
Contract Name:
SwapFlashLoan
Compiler Version
v0.6.12+commit.27d51765
Contract Source Code (Solidity)
/** *Submitted for verification at cronoscan.com on 2022-01-28 */ // SPDX-License-Identifier: MIT pragma solidity 0.6.12; library SafeMath { function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b > a) return (false, 0); return (true, a - b); } function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a / b); } function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a % b); } function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a, "SafeMath: subtraction overflow"); return a - b; } function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) return 0; uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } function div(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: division by zero"); return a / b; } function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: modulo by zero"); return a % b; } function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); return a - b; } function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); return a / b; } function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); return a % b; } } interface IERC20 { function totalSupply() external view returns (uint256); function balanceOf(address account) external view returns (uint256); function transfer(address recipient, 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 sender, address recipient, 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); } library Address { function isContract(address account) internal view returns (bool) { uint256 size; assembly { size := extcodesize(account) } return size > 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) private 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); } } } } library SafeERC20 { using SafeMath for uint256; 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).add(value); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } 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"); } } } library Clones { function clone(address master) internal returns (address instance) { assembly { let ptr := mload(0x40) mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) mstore(add(ptr, 0x14), shl(0x60, master)) mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000) instance := create(0, ptr, 0x37) } require(instance != address(0), "ERC1167: create failed"); } function cloneDeterministic(address master, bytes32 salt) internal returns (address instance) { assembly { let ptr := mload(0x40) mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) mstore(add(ptr, 0x14), shl(0x60, master)) mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000) instance := create2(0, ptr, 0x37, salt) } require(instance != address(0), "ERC1167: create2 failed"); } function predictDeterministicAddress(address master, bytes32 salt, address deployer) internal pure returns (address predicted) { assembly { let ptr := mload(0x40) mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) mstore(add(ptr, 0x14), shl(0x60, master)) mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000) mstore(add(ptr, 0x38), shl(0x60, deployer)) mstore(add(ptr, 0x4c), salt) mstore(add(ptr, 0x6c), keccak256(ptr, 0x37)) predicted := keccak256(add(ptr, 0x37), 0x55) } } function predictDeterministicAddress(address master, bytes32 salt) internal view returns (address predicted) { return predictDeterministicAddress(master, salt, address(this)); } } library AddressUpgradeable { function isContract(address account) internal view returns (bool) { uint256 size; assembly { size := extcodesize(account) } return size > 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 _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private 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); } } } } abstract contract Initializable { bool private _initialized; bool private _initializing; modifier initializer() { require(_initializing || _isConstructor() || !_initialized, "Initializable: contract is already initialized"); bool isTopLevelCall = !_initializing; if (isTopLevelCall) { _initializing = true; _initialized = true; } _; if (isTopLevelCall) { _initializing = false; } } function _isConstructor() private view returns (bool) { return !AddressUpgradeable.isContract(address(this)); } } abstract contract ReentrancyGuardUpgradeable is Initializable { uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; function __ReentrancyGuard_init() internal initializer { __ReentrancyGuard_init_unchained(); } function __ReentrancyGuard_init_unchained() internal initializer { _status = _NOT_ENTERED; } modifier nonReentrant() { require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); _status = _ENTERED; _; _status = _NOT_ENTERED; } uint256[49] private __gap; } abstract contract ContextUpgradeable is Initializable { function __Context_init() internal initializer { __Context_init_unchained(); } function __Context_init_unchained() internal initializer { } function _msgSender() internal view virtual returns (address payable) { return msg.sender; } function _msgData() internal view virtual returns (bytes memory) { this; return msg.data; } uint256[50] private __gap; } abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); function __Ownable_init() internal initializer { __Context_init_unchained(); __Ownable_init_unchained(); } function __Ownable_init_unchained() internal initializer { address msgSender = _msgSender(); _owner = msgSender; emit OwnershipTransferred(address(0), 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 { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } uint256[49] private __gap; } abstract contract PausableUpgradeable is Initializable, ContextUpgradeable { event Paused(address account); event Unpaused(address account); bool private _paused; function __Pausable_init() internal initializer { __Context_init_unchained(); __Pausable_init_unchained(); } function __Pausable_init_unchained() internal initializer { _paused = false; } function paused() public view virtual returns (bool) { return _paused; } modifier whenNotPaused() { require(!paused(), "Pausable: paused"); _; } modifier whenPaused() { require(paused(), "Pausable: not paused"); _; } function _pause() internal virtual whenNotPaused { _paused = true; emit Paused(_msgSender()); } function _unpause() internal virtual whenPaused { _paused = false; emit Unpaused(_msgSender()); } uint256[49] private __gap; } abstract contract OwnerPausableUpgradeable is OwnableUpgradeable, PausableUpgradeable { function __OwnerPausable_init() internal initializer { __Context_init_unchained(); __Ownable_init_unchained(); __Pausable_init_unchained(); } function pause() external onlyOwner { PausableUpgradeable._pause(); } function unpause() external onlyOwner { PausableUpgradeable._unpause(); } } library AmplificationUtils { using SafeMath for uint256; event RampA( uint256 oldA, uint256 newA, uint256 initialTime, uint256 futureTime ); event StopRampA(uint256 currentA, uint256 time); uint256 public constant A_PRECISION = 100; uint256 public constant MAX_A = 10**6; uint256 private constant MAX_A_CHANGE = 2; uint256 private constant MIN_RAMP_TIME = 14 days; function getA(SwapUtils.Swap storage self) external view returns (uint256) { return _getAPrecise(self).div(A_PRECISION); } function getAPrecise(SwapUtils.Swap storage self) external view returns (uint256) { return _getAPrecise(self); } function _getAPrecise(SwapUtils.Swap storage self) internal view returns (uint256) { uint256 t1 = self.futureATime; uint256 a1 = self.futureA; if (block.timestamp < t1) { uint256 t0 = self.initialATime; uint256 a0 = self.initialA; if (a1 > a0) { return a0.add( a1.sub(a0).mul(block.timestamp.sub(t0)).div(t1.sub(t0)) ); } else { return a0.sub( a0.sub(a1).mul(block.timestamp.sub(t0)).div(t1.sub(t0)) ); } } else { return a1; } } function rampA( SwapUtils.Swap storage self, uint256 futureA_, uint256 futureTime_ ) external { require( block.timestamp >= self.initialATime.add(1 days), "Wait 1 day before starting ramp" ); require( futureTime_ >= block.timestamp.add(MIN_RAMP_TIME), "Insufficient ramp time" ); require( futureA_ > 0 && futureA_ < MAX_A, "futureA_ must be > 0 and < MAX_A" ); uint256 initialAPrecise = _getAPrecise(self); uint256 futureAPrecise = futureA_.mul(A_PRECISION); if (futureAPrecise < initialAPrecise) { require( futureAPrecise.mul(MAX_A_CHANGE) >= initialAPrecise, "futureA_ is too small" ); } else { require( futureAPrecise <= initialAPrecise.mul(MAX_A_CHANGE), "futureA_ is too large" ); } self.initialA = initialAPrecise; self.futureA = futureAPrecise; self.initialATime = block.timestamp; self.futureATime = futureTime_; emit RampA( initialAPrecise, futureAPrecise, block.timestamp, futureTime_ ); } function stopRampA(SwapUtils.Swap storage self) external { require(self.futureATime > block.timestamp, "Ramp is already stopped"); uint256 currentA = _getAPrecise(self); self.initialA = currentA; self.futureA = currentA; self.initialATime = block.timestamp; self.futureATime = block.timestamp; emit StopRampA(currentA, block.timestamp); } } interface IERC20Upgradeable { function totalSupply() external view returns (uint256); function balanceOf(address account) external view returns (uint256); function transfer(address recipient, 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 sender, address recipient, 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); } library SafeMathUpgradeable { function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b > a) return (false, 0); return (true, a - b); } function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a / b); } function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a % b); } function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a, "SafeMath: subtraction overflow"); return a - b; } function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) return 0; uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } function div(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: division by zero"); return a / b; } function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: modulo by zero"); return a % b; } function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); return a - b; } function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); return a / b; } function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); return a % b; } } contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable { using SafeMathUpgradeable for uint256; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; uint8 private _decimals; function __ERC20_init(string memory name_, string memory symbol_) internal initializer { __Context_init_unchained(); __ERC20_init_unchained(name_, symbol_); } function __ERC20_init_unchained(string memory name_, string memory symbol_) internal initializer { _name = name_; _symbol = symbol_; _decimals = 18; } function name() public view virtual returns (string memory) { return _name; } function symbol() public view virtual returns (string memory) { return _symbol; } function decimals() public view virtual returns (uint8) { return _decimals; } 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 recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, 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) { _approve(_msgSender(), spender, amount); return true; } function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); return true; } function _transfer(address sender, address recipient, uint256 amount) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, 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 = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(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); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(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 _setupDecimals(uint8 decimals_) internal virtual { _decimals = decimals_; } function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } uint256[44] private __gap; } abstract contract ERC20BurnableUpgradeable is Initializable, ContextUpgradeable, ERC20Upgradeable { function __ERC20Burnable_init() internal initializer { __Context_init_unchained(); __ERC20Burnable_init_unchained(); } function __ERC20Burnable_init_unchained() internal initializer { } using SafeMathUpgradeable for uint256; function burn(uint256 amount) public virtual { _burn(_msgSender(), amount); } function burnFrom(address account, uint256 amount) public virtual { uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, "ERC20: burn amount exceeds allowance"); _approve(account, _msgSender(), decreasedAllowance); _burn(account, amount); } uint256[50] private __gap; } abstract contract Context { function _msgSender() internal view virtual returns (address payable) { return msg.sender; } function _msgData() internal view virtual returns (bytes memory) { this; return msg.data; } } contract ERC20 is Context, IERC20 { using SafeMath for uint256; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; uint8 private _decimals; constructor (string memory name_, string memory symbol_) public { _name = name_; _symbol = symbol_; _decimals = 18; } function name() public view virtual returns (string memory) { return _name; } function symbol() public view virtual returns (string memory) { return _symbol; } function decimals() public view virtual returns (uint8) { return _decimals; } 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 recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, 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) { _approve(_msgSender(), spender, amount); return true; } function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); return true; } function _transfer(address sender, address recipient, uint256 amount) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, 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 = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(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); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(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 _setupDecimals(uint8 decimals_) internal virtual { _decimals = decimals_; } function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } } interface IAllowlist { function getPoolAccountLimit(address poolAddress) external view returns (uint256); function getPoolCap(address poolAddress) external view returns (uint256); function verifyAddress(address account, bytes32[] calldata merkleProof) external returns (bool); } interface ISwap { function getA() external view returns (uint256); function getAllowlist() external view returns (IAllowlist); function getToken(uint8 index) external view returns (IERC20); function getTokenIndex(address tokenAddress) external view returns (uint8); function getTokenBalance(uint8 index) external view returns (uint256); function getVirtualPrice() external view returns (uint256); function isGuarded() external view returns (bool); function calculateSwap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) external view returns (uint256); function calculateTokenAmount(uint256[] calldata amounts, bool deposit) external view returns (uint256); function calculateRemoveLiquidity(uint256 amount) external view returns (uint256[] memory); function calculateRemoveLiquidityOneToken( uint256 tokenAmount, uint8 tokenIndex ) external view returns (uint256 availableTokenAmount); function initialize( IERC20[] memory pooledTokens, uint8[] memory decimals, string memory lpTokenName, string memory lpTokenSymbol, uint256 a, uint256 fee, uint256 adminFee, address lpTokenTargetAddress ) external; function swap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy, uint256 deadline ) external returns (uint256); function addLiquidity( uint256[] calldata amounts, uint256 minToMint, uint256 deadline ) external returns (uint256); function removeLiquidity( uint256 amount, uint256[] calldata minAmounts, uint256 deadline ) external returns (uint256[] memory); function removeLiquidityOneToken( uint256 tokenAmount, uint8 tokenIndex, uint256 minAmount, uint256 deadline ) external returns (uint256); function removeLiquidityImbalance( uint256[] calldata amounts, uint256 maxBurnAmount, uint256 deadline ) external returns (uint256); } contract LPToken is ERC20BurnableUpgradeable, OwnableUpgradeable { using SafeMathUpgradeable for uint256; function initialize(string memory name, string memory symbol) external initializer returns (bool) { __Context_init_unchained(); __ERC20_init_unchained(name, symbol); __Ownable_init_unchained(); return true; } function mint(address recipient, uint256 amount) external onlyOwner { require(amount != 0, "LPToken: cannot mint 0"); _mint(recipient, amount); } function _beforeTokenTransfer( address from, address to, uint256 amount ) internal virtual override(ERC20Upgradeable) { super._beforeTokenTransfer(from, to, amount); require(to != address(this), "LPToken: cannot send to itself"); } } library MathUtils { function within1(uint256 a, uint256 b) internal pure returns (bool) { return (difference(a, b) <= 1); } function difference(uint256 a, uint256 b) internal pure returns (uint256) { if (a > b) { return a - b; } return b - a; } } library SwapUtils { using SafeERC20 for IERC20; using SafeMath for uint256; using MathUtils for uint256; event TokenSwap( address indexed buyer, uint256 tokensSold, uint256 tokensBought, uint128 soldId, uint128 boughtId ); event AddLiquidity( address indexed provider, uint256[] tokenAmounts, uint256[] fees, uint256 invariant, uint256 lpTokenSupply ); event RemoveLiquidity( address indexed provider, uint256[] tokenAmounts, uint256 lpTokenSupply ); event RemoveLiquidityOne( address indexed provider, uint256 lpTokenAmount, uint256 lpTokenSupply, uint256 boughtId, uint256 tokensBought ); event RemoveLiquidityImbalance( address indexed provider, uint256[] tokenAmounts, uint256[] fees, uint256 invariant, uint256 lpTokenSupply ); event NewAdminFee(uint256 newAdminFee); event NewSwapFee(uint256 newSwapFee); struct Swap { uint256 initialA; uint256 futureA; uint256 initialATime; uint256 futureATime; uint256 swapFee; uint256 adminFee; LPToken lpToken; IERC20[] pooledTokens; uint256[] tokenPrecisionMultipliers; uint256[] balances; } struct CalculateWithdrawOneTokenDYInfo { uint256 d0; uint256 d1; uint256 newY; uint256 feePerToken; uint256 preciseA; } struct ManageLiquidityInfo { uint256 d0; uint256 d1; uint256 d2; uint256 preciseA; LPToken lpToken; uint256 totalSupply; uint256[] balances; uint256[] multipliers; } uint8 public constant POOL_PRECISION_DECIMALS = 18; uint256 private constant FEE_DENOMINATOR = 10**10; uint256 public constant MAX_SWAP_FEE = 10**8; uint256 public constant MAX_ADMIN_FEE = 10**10; uint256 private constant MAX_LOOP_LIMIT = 256; function _getAPrecise(Swap storage self) internal view returns (uint256) { return AmplificationUtils._getAPrecise(self); } function calculateWithdrawOneToken( Swap storage self, uint256 tokenAmount, uint8 tokenIndex ) external view returns (uint256) { (uint256 availableTokenAmount, ) = _calculateWithdrawOneToken( self, tokenAmount, tokenIndex, self.lpToken.totalSupply() ); return availableTokenAmount; } function _calculateWithdrawOneToken( Swap storage self, uint256 tokenAmount, uint8 tokenIndex, uint256 totalSupply ) internal view returns (uint256, uint256) { uint256 dy; uint256 newY; uint256 currentY; (dy, newY, currentY) = calculateWithdrawOneTokenDY( self, tokenIndex, tokenAmount, totalSupply ); uint256 dySwapFee = currentY .sub(newY) .div(self.tokenPrecisionMultipliers[tokenIndex]) .sub(dy); return (dy, dySwapFee); } function calculateWithdrawOneTokenDY( Swap storage self, uint8 tokenIndex, uint256 tokenAmount, uint256 totalSupply ) internal view returns ( uint256, uint256, uint256 ) { uint256[] memory xp = _xp(self); require(tokenIndex < xp.length, "Token index out of range"); CalculateWithdrawOneTokenDYInfo memory v = CalculateWithdrawOneTokenDYInfo(0, 0, 0, 0, 0); v.preciseA = _getAPrecise(self); v.d0 = getD(xp, v.preciseA); v.d1 = v.d0.sub(tokenAmount.mul(v.d0).div(totalSupply)); require(tokenAmount <= xp[tokenIndex], "Withdraw exceeds available"); v.newY = getYD(v.preciseA, tokenIndex, xp, v.d1); uint256[] memory xpReduced = new uint256[](xp.length); v.feePerToken = _feePerToken(self.swapFee, xp.length); for (uint256 i = 0; i < xp.length; i++) { uint256 xpi = xp[i]; xpReduced[i] = xpi.sub( ( (i == tokenIndex) ? xpi.mul(v.d1).div(v.d0).sub(v.newY) : xpi.sub(xpi.mul(v.d1).div(v.d0)) ).mul(v.feePerToken).div(FEE_DENOMINATOR) ); } uint256 dy = xpReduced[tokenIndex].sub( getYD(v.preciseA, tokenIndex, xpReduced, v.d1) ); dy = dy.sub(1).div(self.tokenPrecisionMultipliers[tokenIndex]); return (dy, v.newY, xp[tokenIndex]); } function getYD( uint256 a, uint8 tokenIndex, uint256[] memory xp, uint256 d ) internal pure returns (uint256) { uint256 numTokens = xp.length; require(tokenIndex < numTokens, "Token not found"); uint256 c = d; uint256 s; uint256 nA = a.mul(numTokens); for (uint256 i = 0; i < numTokens; i++) { if (i != tokenIndex) { s = s.add(xp[i]); c = c.mul(d).div(xp[i].mul(numTokens)); } } c = c.mul(d).mul(AmplificationUtils.A_PRECISION).div(nA.mul(numTokens)); uint256 b = s.add(d.mul(AmplificationUtils.A_PRECISION).div(nA)); uint256 yPrev; uint256 y = d; for (uint256 i = 0; i < MAX_LOOP_LIMIT; i++) { yPrev = y; y = y.mul(y).add(c).div(y.mul(2).add(b).sub(d)); if (y.within1(yPrev)) { return y; } } revert("Approximation did not converge"); } function getD(uint256[] memory xp, uint256 a) internal pure returns (uint256) { uint256 numTokens = xp.length; uint256 s; for (uint256 i = 0; i < numTokens; i++) { s = s.add(xp[i]); } if (s == 0) { return 0; } uint256 prevD; uint256 d = s; uint256 nA = a.mul(numTokens); for (uint256 i = 0; i < MAX_LOOP_LIMIT; i++) { uint256 dP = d; for (uint256 j = 0; j < numTokens; j++) { dP = dP.mul(d).div(xp[j].mul(numTokens)); } prevD = d; d = nA .mul(s) .div(AmplificationUtils.A_PRECISION) .add(dP.mul(numTokens)) .mul(d) .div( nA .sub(AmplificationUtils.A_PRECISION) .mul(d) .div(AmplificationUtils.A_PRECISION) .add(numTokens.add(1).mul(dP)) ); if (d.within1(prevD)) { return d; } } revert("D does not converge"); } function _xp( uint256[] memory balances, uint256[] memory precisionMultipliers ) internal pure returns (uint256[] memory) { uint256 numTokens = balances.length; require( numTokens == precisionMultipliers.length, "Balances must match multipliers" ); uint256[] memory xp = new uint256[](numTokens); for (uint256 i = 0; i < numTokens; i++) { xp[i] = balances[i].mul(precisionMultipliers[i]); } return xp; } function _xp(Swap storage self) internal view returns (uint256[] memory) { return _xp(self.balances, self.tokenPrecisionMultipliers); } function getVirtualPrice(Swap storage self) external view returns (uint256) { uint256 d = getD(_xp(self), _getAPrecise(self)); LPToken lpToken = self.lpToken; uint256 supply = lpToken.totalSupply(); if (supply > 0) { return d.mul(10**uint256(POOL_PRECISION_DECIMALS)).div(supply); } return 0; } function getY( uint256 preciseA, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 x, uint256[] memory xp ) internal pure returns (uint256) { uint256 numTokens = xp.length; require( tokenIndexFrom != tokenIndexTo, "Can't compare token to itself" ); require( tokenIndexFrom < numTokens && tokenIndexTo < numTokens, "Tokens must be in pool" ); uint256 d = getD(xp, preciseA); uint256 c = d; uint256 s; uint256 nA = numTokens.mul(preciseA); uint256 _x; for (uint256 i = 0; i < numTokens; i++) { if (i == tokenIndexFrom) { _x = x; } else if (i != tokenIndexTo) { _x = xp[i]; } else { continue; } s = s.add(_x); c = c.mul(d).div(_x.mul(numTokens)); } c = c.mul(d).mul(AmplificationUtils.A_PRECISION).div(nA.mul(numTokens)); uint256 b = s.add(d.mul(AmplificationUtils.A_PRECISION).div(nA)); uint256 yPrev; uint256 y = d; for (uint256 i = 0; i < MAX_LOOP_LIMIT; i++) { yPrev = y; y = y.mul(y).add(c).div(y.mul(2).add(b).sub(d)); if (y.within1(yPrev)) { return y; } } revert("Approximation did not converge"); } function calculateSwap( Swap storage self, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) external view returns (uint256 dy) { (dy, ) = _calculateSwap( self, tokenIndexFrom, tokenIndexTo, dx, self.balances ); } function _calculateSwap( Swap storage self, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256[] memory balances ) internal view returns (uint256 dy, uint256 dyFee) { uint256[] memory multipliers = self.tokenPrecisionMultipliers; uint256[] memory xp = _xp(balances, multipliers); require( tokenIndexFrom < xp.length && tokenIndexTo < xp.length, "Token index out of range" ); uint256 x = dx.mul(multipliers[tokenIndexFrom]).add(xp[tokenIndexFrom]); uint256 y = getY( _getAPrecise(self), tokenIndexFrom, tokenIndexTo, x, xp ); dy = xp[tokenIndexTo].sub(y).sub(1); dyFee = dy.mul(self.swapFee).div(FEE_DENOMINATOR); dy = dy.sub(dyFee).div(multipliers[tokenIndexTo]); } function calculateRemoveLiquidity(Swap storage self, uint256 amount) external view returns (uint256[] memory) { return _calculateRemoveLiquidity( self.balances, amount, self.lpToken.totalSupply() ); } function _calculateRemoveLiquidity( uint256[] memory balances, uint256 amount, uint256 totalSupply ) internal pure returns (uint256[] memory) { require(amount <= totalSupply, "Cannot exceed total supply"); uint256[] memory amounts = new uint256[](balances.length); for (uint256 i = 0; i < balances.length; i++) { amounts[i] = balances[i].mul(amount).div(totalSupply); } return amounts; } function calculateTokenAmount( Swap storage self, uint256[] calldata amounts, bool deposit ) external view returns (uint256) { uint256 a = _getAPrecise(self); uint256[] memory balances = self.balances; uint256[] memory multipliers = self.tokenPrecisionMultipliers; uint256 d0 = getD(_xp(balances, multipliers), a); for (uint256 i = 0; i < balances.length; i++) { if (deposit) { balances[i] = balances[i].add(amounts[i]); } else { balances[i] = balances[i].sub( amounts[i], "Cannot withdraw more than available" ); } } uint256 d1 = getD(_xp(balances, multipliers), a); uint256 totalSupply = self.lpToken.totalSupply(); if (deposit) { return d1.sub(d0).mul(totalSupply).div(d0); } else { return d0.sub(d1).mul(totalSupply).div(d0); } } function getAdminBalance(Swap storage self, uint256 index) external view returns (uint256) { require(index < self.pooledTokens.length, "Token index out of range"); return self.pooledTokens[index].balanceOf(address(this)).sub( self.balances[index] ); } function _feePerToken(uint256 swapFee, uint256 numTokens) internal pure returns (uint256) { return swapFee.mul(numTokens).div(numTokens.sub(1).mul(4)); } function swap( Swap storage self, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy ) external returns (uint256) { { IERC20 tokenFrom = self.pooledTokens[tokenIndexFrom]; require( dx <= tokenFrom.balanceOf(msg.sender), "Cannot swap more than you own" ); uint256 beforeBalance = tokenFrom.balanceOf(address(this)); tokenFrom.safeTransferFrom(msg.sender, address(this), dx); dx = tokenFrom.balanceOf(address(this)).sub(beforeBalance); } uint256 dy; uint256 dyFee; uint256[] memory balances = self.balances; (dy, dyFee) = _calculateSwap( self, tokenIndexFrom, tokenIndexTo, dx, balances ); require(dy >= minDy, "Swap didn't result in min tokens"); uint256 dyAdminFee = dyFee.mul(self.adminFee).div(FEE_DENOMINATOR).div( self.tokenPrecisionMultipliers[tokenIndexTo] ); self.balances[tokenIndexFrom] = balances[tokenIndexFrom].add(dx); self.balances[tokenIndexTo] = balances[tokenIndexTo].sub(dy).sub( dyAdminFee ); self.pooledTokens[tokenIndexTo].safeTransfer(msg.sender, dy); emit TokenSwap(msg.sender, dx, dy, tokenIndexFrom, tokenIndexTo); return dy; } function addLiquidity( Swap storage self, uint256[] memory amounts, uint256 minToMint ) external returns (uint256) { IERC20[] memory pooledTokens = self.pooledTokens; require( amounts.length == pooledTokens.length, "Amounts must match pooled tokens" ); ManageLiquidityInfo memory v = ManageLiquidityInfo( 0, 0, 0, _getAPrecise(self), self.lpToken, 0, self.balances, self.tokenPrecisionMultipliers ); v.totalSupply = v.lpToken.totalSupply(); if (v.totalSupply != 0) { v.d0 = getD(_xp(v.balances, v.multipliers), v.preciseA); } uint256[] memory newBalances = new uint256[](pooledTokens.length); for (uint256 i = 0; i < pooledTokens.length; i++) { require( v.totalSupply != 0 || amounts[i] > 0, "Must supply all tokens in pool" ); if (amounts[i] != 0) { uint256 beforeBalance = pooledTokens[i].balanceOf( address(this) ); pooledTokens[i].safeTransferFrom( msg.sender, address(this), amounts[i] ); amounts[i] = pooledTokens[i].balanceOf(address(this)).sub( beforeBalance ); } newBalances[i] = v.balances[i].add(amounts[i]); } v.d1 = getD(_xp(newBalances, v.multipliers), v.preciseA); require(v.d1 > v.d0, "D should increase"); v.d2 = v.d1; uint256[] memory fees = new uint256[](pooledTokens.length); if (v.totalSupply != 0) { uint256 feePerToken = _feePerToken( self.swapFee, pooledTokens.length ); for (uint256 i = 0; i < pooledTokens.length; i++) { uint256 idealBalance = v.d1.mul(v.balances[i]).div(v.d0); fees[i] = feePerToken .mul(idealBalance.difference(newBalances[i])) .div(FEE_DENOMINATOR); self.balances[i] = newBalances[i].sub( fees[i].mul(self.adminFee).div(FEE_DENOMINATOR) ); newBalances[i] = newBalances[i].sub(fees[i]); } v.d2 = getD(_xp(newBalances, v.multipliers), v.preciseA); } else { self.balances = newBalances; } uint256 toMint; if (v.totalSupply == 0) { toMint = v.d1; } else { toMint = v.d2.sub(v.d0).mul(v.totalSupply).div(v.d0); } require(toMint >= minToMint, "Couldn't mint min requested"); v.lpToken.mint(msg.sender, toMint); emit AddLiquidity( msg.sender, amounts, fees, v.d1, v.totalSupply.add(toMint) ); return toMint; } function removeLiquidity( Swap storage self, uint256 amount, uint256[] calldata minAmounts ) external returns (uint256[] memory) { LPToken lpToken = self.lpToken; IERC20[] memory pooledTokens = self.pooledTokens; require(amount <= lpToken.balanceOf(msg.sender), ">LP.balanceOf"); require( minAmounts.length == pooledTokens.length, "minAmounts must match poolTokens" ); uint256[] memory balances = self.balances; uint256 totalSupply = lpToken.totalSupply(); uint256[] memory amounts = _calculateRemoveLiquidity( balances, amount, totalSupply ); for (uint256 i = 0; i < amounts.length; i++) { require(amounts[i] >= minAmounts[i], "amounts[i] < minAmounts[i]"); self.balances[i] = balances[i].sub(amounts[i]); pooledTokens[i].safeTransfer(msg.sender, amounts[i]); } lpToken.burnFrom(msg.sender, amount); emit RemoveLiquidity(msg.sender, amounts, totalSupply.sub(amount)); return amounts; } function removeLiquidityOneToken( Swap storage self, uint256 tokenAmount, uint8 tokenIndex, uint256 minAmount ) external returns (uint256) { LPToken lpToken = self.lpToken; IERC20[] memory pooledTokens = self.pooledTokens; require(tokenAmount <= lpToken.balanceOf(msg.sender), ">LP.balanceOf"); require(tokenIndex < pooledTokens.length, "Token not found"); uint256 totalSupply = lpToken.totalSupply(); (uint256 dy, uint256 dyFee) = _calculateWithdrawOneToken( self, tokenAmount, tokenIndex, totalSupply ); require(dy >= minAmount, "dy < minAmount"); self.balances[tokenIndex] = self.balances[tokenIndex].sub( dy.add(dyFee.mul(self.adminFee).div(FEE_DENOMINATOR)) ); lpToken.burnFrom(msg.sender, tokenAmount); pooledTokens[tokenIndex].safeTransfer(msg.sender, dy); emit RemoveLiquidityOne( msg.sender, tokenAmount, totalSupply, tokenIndex, dy ); return dy; } function removeLiquidityImbalance( Swap storage self, uint256[] memory amounts, uint256 maxBurnAmount ) public returns (uint256) { ManageLiquidityInfo memory v = ManageLiquidityInfo( 0, 0, 0, _getAPrecise(self), self.lpToken, 0, self.balances, self.tokenPrecisionMultipliers ); v.totalSupply = v.lpToken.totalSupply(); IERC20[] memory pooledTokens = self.pooledTokens; require( amounts.length == pooledTokens.length, "Amounts should match pool tokens" ); require( maxBurnAmount <= v.lpToken.balanceOf(msg.sender) && maxBurnAmount != 0, ">LP.balanceOf" ); uint256 feePerToken = _feePerToken(self.swapFee, pooledTokens.length); uint256[] memory fees = new uint256[](pooledTokens.length); { uint256[] memory balances1 = new uint256[](pooledTokens.length); v.d0 = getD(_xp(v.balances, v.multipliers), v.preciseA); for (uint256 i = 0; i < pooledTokens.length; i++) { balances1[i] = v.balances[i].sub( amounts[i], "Cannot withdraw more than available" ); } v.d1 = getD(_xp(balances1, v.multipliers), v.preciseA); for (uint256 i = 0; i < pooledTokens.length; i++) { uint256 idealBalance = v.d1.mul(v.balances[i]).div(v.d0); uint256 difference = idealBalance.difference(balances1[i]); fees[i] = feePerToken.mul(difference).div(FEE_DENOMINATOR); self.balances[i] = balances1[i].sub( fees[i].mul(self.adminFee).div(FEE_DENOMINATOR) ); balances1[i] = balances1[i].sub(fees[i]); } v.d2 = getD(_xp(balances1, v.multipliers), v.preciseA); } uint256 tokenAmount = v.d0.sub(v.d2).mul(v.totalSupply).div(v.d0); require(tokenAmount != 0, "Burnt amount cannot be zero"); tokenAmount = tokenAmount.add(1); require(tokenAmount <= maxBurnAmount, "tokenAmount > maxBurnAmount"); v.lpToken.burnFrom(msg.sender, tokenAmount); for (uint256 i = 0; i < pooledTokens.length; i++) { pooledTokens[i].safeTransfer(msg.sender, amounts[i]); } emit RemoveLiquidityImbalance( msg.sender, amounts, fees, v.d1, v.totalSupply.sub(tokenAmount) ); return tokenAmount; } function withdrawAdminFees(Swap storage self, address to) external { IERC20[] memory pooledTokens = self.pooledTokens; for (uint256 i = 0; i < pooledTokens.length; i++) { IERC20 token = pooledTokens[i]; uint256 balance = token.balanceOf(address(this)).sub( self.balances[i] ); if (balance != 0) { token.safeTransfer(to, balance); } } } function setAdminFee(Swap storage self, uint256 newAdminFee) external { require(newAdminFee <= MAX_ADMIN_FEE, "Fee is too high"); self.adminFee = newAdminFee; emit NewAdminFee(newAdminFee); } function setSwapFee(Swap storage self, uint256 newSwapFee) external { require(newSwapFee <= MAX_SWAP_FEE, "Fee is too high"); self.swapFee = newSwapFee; emit NewSwapFee(newSwapFee); } } contract Swap is OwnerPausableUpgradeable, ReentrancyGuardUpgradeable { using SafeERC20 for IERC20; using SafeMath for uint256; using SwapUtils for SwapUtils.Swap; using AmplificationUtils for SwapUtils.Swap; SwapUtils.Swap public swapStorage; mapping(address => uint8) private tokenIndexes; event TokenSwap( address indexed buyer, uint256 tokensSold, uint256 tokensBought, uint128 soldId, uint128 boughtId ); event AddLiquidity( address indexed provider, uint256[] tokenAmounts, uint256[] fees, uint256 invariant, uint256 lpTokenSupply ); event RemoveLiquidity( address indexed provider, uint256[] tokenAmounts, uint256 lpTokenSupply ); event RemoveLiquidityOne( address indexed provider, uint256 lpTokenAmount, uint256 lpTokenSupply, uint256 boughtId, uint256 tokensBought ); event RemoveLiquidityImbalance( address indexed provider, uint256[] tokenAmounts, uint256[] fees, uint256 invariant, uint256 lpTokenSupply ); event NewAdminFee(uint256 newAdminFee); event NewSwapFee(uint256 newSwapFee); event NewWithdrawFee(uint256 newWithdrawFee); event RampA( uint256 oldA, uint256 newA, uint256 initialTime, uint256 futureTime ); event StopRampA(uint256 currentA, uint256 time); function initialize( IERC20[] memory _pooledTokens, uint8[] memory decimals, string memory lpTokenName, string memory lpTokenSymbol, uint256 _a, uint256 _fee, uint256 _adminFee, address lpTokenTargetAddress ) public virtual initializer { __OwnerPausable_init(); __ReentrancyGuard_init(); require(_pooledTokens.length > 1, "_pooledTokens.length <= 1"); require(_pooledTokens.length <= 32, "_pooledTokens.length > 32"); require( _pooledTokens.length == decimals.length, "_pooledTokens decimals mismatch" ); uint256[] memory precisionMultipliers = new uint256[](decimals.length); for (uint8 i = 0; i < _pooledTokens.length; i++) { if (i > 0) { require( tokenIndexes[address(_pooledTokens[i])] == 0 && _pooledTokens[0] != _pooledTokens[i], "Duplicate tokens" ); } require( address(_pooledTokens[i]) != address(0), "The 0 address isn't an ERC-20" ); require( decimals[i] <= SwapUtils.POOL_PRECISION_DECIMALS, "Token decimals exceeds max" ); precisionMultipliers[i] = 10 ** uint256(SwapUtils.POOL_PRECISION_DECIMALS).sub( uint256(decimals[i]) ); tokenIndexes[address(_pooledTokens[i])] = i; } require(_a < AmplificationUtils.MAX_A, "_a exceeds maximum"); require(_fee < SwapUtils.MAX_SWAP_FEE, "_fee exceeds maximum"); require( _adminFee < SwapUtils.MAX_ADMIN_FEE, "_adminFee exceeds maximum" ); LPToken lpToken = LPToken(Clones.clone(lpTokenTargetAddress)); require( lpToken.initialize(lpTokenName, lpTokenSymbol), "could not init lpToken clone" ); swapStorage.lpToken = lpToken; swapStorage.pooledTokens = _pooledTokens; swapStorage.tokenPrecisionMultipliers = precisionMultipliers; swapStorage.balances = new uint256[](_pooledTokens.length); swapStorage.initialA = _a.mul(AmplificationUtils.A_PRECISION); swapStorage.futureA = _a.mul(AmplificationUtils.A_PRECISION); swapStorage.swapFee = _fee; swapStorage.adminFee = _adminFee; } modifier deadlineCheck(uint256 deadline) { require(block.timestamp <= deadline, "Deadline not met"); _; } function getA() external view virtual returns (uint256) { return swapStorage.getA(); } function getAPrecise() external view virtual returns (uint256) { return swapStorage.getAPrecise(); } function getToken(uint8 index) public view virtual returns (IERC20) { require(index < swapStorage.pooledTokens.length, "Out of range"); return swapStorage.pooledTokens[index]; } function getTokenIndex(address tokenAddress) public view virtual returns (uint8) { uint8 index = tokenIndexes[tokenAddress]; require( address(getToken(index)) == tokenAddress, "Token does not exist" ); return index; } function getTokenBalance(uint8 index) external view virtual returns (uint256) { require(index < swapStorage.pooledTokens.length, "Index out of range"); return swapStorage.balances[index]; } function getVirtualPrice() external view virtual returns (uint256) { return swapStorage.getVirtualPrice(); } function calculateSwap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) external view virtual returns (uint256) { return swapStorage.calculateSwap(tokenIndexFrom, tokenIndexTo, dx); } function calculateTokenAmount(uint256[] calldata amounts, bool deposit) external view virtual returns (uint256) { return swapStorage.calculateTokenAmount(amounts, deposit); } function calculateRemoveLiquidity(uint256 amount) external view virtual returns (uint256[] memory) { return swapStorage.calculateRemoveLiquidity(amount); } function calculateRemoveLiquidityOneToken( uint256 tokenAmount, uint8 tokenIndex ) external view virtual returns (uint256 availableTokenAmount) { return swapStorage.calculateWithdrawOneToken(tokenAmount, tokenIndex); } function getAdminBalance(uint256 index) external view virtual returns (uint256) { return swapStorage.getAdminBalance(index); } function swap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy, uint256 deadline ) external virtual nonReentrant whenNotPaused deadlineCheck(deadline) returns (uint256) { return swapStorage.swap(tokenIndexFrom, tokenIndexTo, dx, minDy); } function addLiquidity( uint256[] calldata amounts, uint256 minToMint, uint256 deadline ) external virtual nonReentrant whenNotPaused deadlineCheck(deadline) returns (uint256) { return swapStorage.addLiquidity(amounts, minToMint); } function removeLiquidity( uint256 amount, uint256[] calldata minAmounts, uint256 deadline ) external virtual nonReentrant deadlineCheck(deadline) returns (uint256[] memory) { return swapStorage.removeLiquidity(amount, minAmounts); } function removeLiquidityOneToken( uint256 tokenAmount, uint8 tokenIndex, uint256 minAmount, uint256 deadline ) external virtual nonReentrant whenNotPaused deadlineCheck(deadline) returns (uint256) { return swapStorage.removeLiquidityOneToken( tokenAmount, tokenIndex, minAmount ); } function removeLiquidityImbalance( uint256[] calldata amounts, uint256 maxBurnAmount, uint256 deadline ) external virtual nonReentrant whenNotPaused deadlineCheck(deadline) returns (uint256) { return swapStorage.removeLiquidityImbalance(amounts, maxBurnAmount); } function withdrawAdminFees() external onlyOwner { swapStorage.withdrawAdminFees(owner()); } function setAdminFee(uint256 newAdminFee) external onlyOwner { swapStorage.setAdminFee(newAdminFee); } function setSwapFee(uint256 newSwapFee) external onlyOwner { swapStorage.setSwapFee(newSwapFee); } function rampA(uint256 futureA, uint256 futureTime) external onlyOwner { swapStorage.rampA(futureA, futureTime); } function stopRampA() external onlyOwner { swapStorage.stopRampA(); } } interface IFlashLoanReceiver { function executeOperation( address pool, address token, uint256 amount, uint256 fee, bytes calldata params ) external; } contract SwapFlashLoan is Swap { uint256 public flashLoanFeeBPS; uint256 public protocolFeeShareBPS; uint256 public constant MAX_BPS = 10000; event FlashLoan( address indexed receiver, uint8 tokenIndex, uint256 amount, uint256 amountFee, uint256 protocolFee ); function initialize( IERC20[] memory _pooledTokens, uint8[] memory decimals, string memory lpTokenName, string memory lpTokenSymbol, uint256 _a, uint256 _fee, uint256 _adminFee, address lpTokenTargetAddress ) public virtual override initializer { Swap.initialize( _pooledTokens, decimals, lpTokenName, lpTokenSymbol, _a, _fee, _adminFee, lpTokenTargetAddress ); flashLoanFeeBPS = 8; protocolFeeShareBPS = 0; } function flashLoan( address receiver, IERC20 token, uint256 amount, bytes memory params ) external nonReentrant { uint8 tokenIndex = getTokenIndex(address(token)); uint256 availableLiquidityBefore = token.balanceOf(address(this)); uint256 protocolBalanceBefore = availableLiquidityBefore.sub( swapStorage.balances[tokenIndex] ); require( amount > 0 && availableLiquidityBefore >= amount, "invalid amount" ); uint256 amountFee = amount.mul(flashLoanFeeBPS).div(10000); uint256 protocolFee = amountFee.mul(protocolFeeShareBPS).div(10000); require(amountFee > 0, "amount is small for a flashLoan"); token.safeTransfer(receiver, amount); IFlashLoanReceiver(receiver).executeOperation( address(this), address(token), amount, amountFee, params ); uint256 availableLiquidityAfter = token.balanceOf(address(this)); require( availableLiquidityAfter >= availableLiquidityBefore.add(amountFee), "flashLoan fee is not met" ); swapStorage.balances[tokenIndex] = availableLiquidityAfter .sub(protocolBalanceBefore) .sub(protocolFee); emit FlashLoan(receiver, tokenIndex, amount, amountFee, protocolFee); } function setFlashLoanFees( uint256 newFlashLoanFeeBPS, uint256 newProtocolFeeShareBPS ) external onlyOwner { require( newFlashLoanFeeBPS > 0 && newFlashLoanFeeBPS <= MAX_BPS && newProtocolFeeShareBPS <= MAX_BPS, "fees are not in valid range" ); flashLoanFeeBPS = newFlashLoanFeeBPS; protocolFeeShareBPS = newProtocolFeeShareBPS; } }
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenAmounts","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"fees","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"invariant","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lpTokenSupply","type":"uint256"}],"name":"AddLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint8","name":"tokenIndex","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"protocolFee","type":"uint256"}],"name":"FlashLoan","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newAdminFee","type":"uint256"}],"name":"NewAdminFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newSwapFee","type":"uint256"}],"name":"NewSwapFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newWithdrawFee","type":"uint256"}],"name":"NewWithdrawFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldA","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newA","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"initialTime","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"futureTime","type":"uint256"}],"name":"RampA","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenAmounts","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"lpTokenSupply","type":"uint256"}],"name":"RemoveLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenAmounts","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"fees","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"invariant","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lpTokenSupply","type":"uint256"}],"name":"RemoveLiquidityImbalance","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256","name":"lpTokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lpTokenSupply","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"boughtId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokensBought","type":"uint256"}],"name":"RemoveLiquidityOne","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"currentA","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"time","type":"uint256"}],"name":"StopRampA","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokensSold","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokensBought","type":"uint256"},{"indexed":false,"internalType":"uint128","name":"soldId","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"boughtId","type":"uint128"}],"name":"TokenSwap","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"MAX_BPS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"uint256","name":"minToMint","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"addLiquidity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"calculateRemoveLiquidity","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"uint8","name":"tokenIndex","type":"uint8"}],"name":"calculateRemoveLiquidityOneToken","outputs":[{"internalType":"uint256","name":"availableTokenAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"tokenIndexFrom","type":"uint8"},{"internalType":"uint8","name":"tokenIndexTo","type":"uint8"},{"internalType":"uint256","name":"dx","type":"uint256"}],"name":"calculateSwap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"bool","name":"deposit","type":"bool"}],"name":"calculateTokenAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"params","type":"bytes"}],"name":"flashLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"flashLoanFeeBPS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getA","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAPrecise","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getAdminBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"index","type":"uint8"}],"name":"getToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"index","type":"uint8"}],"name":"getTokenBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"getTokenIndex","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVirtualPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20[]","name":"_pooledTokens","type":"address[]"},{"internalType":"uint8[]","name":"decimals","type":"uint8[]"},{"internalType":"string","name":"lpTokenName","type":"string"},{"internalType":"string","name":"lpTokenSymbol","type":"string"},{"internalType":"uint256","name":"_a","type":"uint256"},{"internalType":"uint256","name":"_fee","type":"uint256"},{"internalType":"uint256","name":"_adminFee","type":"uint256"},{"internalType":"address","name":"lpTokenTargetAddress","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolFeeShareBPS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"futureA","type":"uint256"},{"internalType":"uint256","name":"futureTime","type":"uint256"}],"name":"rampA","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256[]","name":"minAmounts","type":"uint256[]"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidity","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"uint256","name":"maxBurnAmount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidityImbalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"uint8","name":"tokenIndex","type":"uint8"},{"internalType":"uint256","name":"minAmount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidityOneToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newAdminFee","type":"uint256"}],"name":"setAdminFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newFlashLoanFeeBPS","type":"uint256"},{"internalType":"uint256","name":"newProtocolFeeShareBPS","type":"uint256"}],"name":"setFlashLoanFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newSwapFee","type":"uint256"}],"name":"setSwapFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stopRampA","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"tokenIndexFrom","type":"uint8"},{"internalType":"uint8","name":"tokenIndexTo","type":"uint8"},{"internalType":"uint256","name":"dx","type":"uint256"},{"internalType":"uint256","name":"minDy","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"swapStorage","outputs":[{"internalType":"uint256","name":"initialA","type":"uint256"},{"internalType":"uint256","name":"futureA","type":"uint256"},{"internalType":"uint256","name":"initialATime","type":"uint256"},{"internalType":"uint256","name":"futureATime","type":"uint256"},{"internalType":"uint256","name":"swapFee","type":"uint256"},{"internalType":"uint256","name":"adminFee","type":"uint256"},{"internalType":"contract LPToken","name":"lpToken","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawAdminFees","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Age | Block | Fee Address | BC Fee Address | Voting Power | Jailed | Incoming |
---|
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.