pragma solidity ^0.8.0; // Author: Louis Holbrook 0826EDA1702D1E87C6E2875121D2E7BB88C2A746 // SPDX-License-Identifier: AGPL-3.0-or-later // File-Version: 1 // Description: Voting contract using ERC20 tokens as shares contract SwapPool { // Implements EIP173 address public owner; address registry; address quoter; uint256 feePpm; address feeAddress; bytes32 declaration; string public name; string public symbol; uint256 public immutable decimals; uint256 public totalSupply; mapping ( address => uint256 ) fees; // Implements Seal uint256 public sealState; uint8 constant FEE_STATE = 1; uint8 constant FEEADDRESS_STATE = 2; uint256 constant public maxSealState = 3; // Implements Seal event SealStateChange(bool indexed _final, uint256 _sealState); // EIP173 event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); // EIP173 constructor(string memory _name, string memory _symbol, uint8 _decimals, bytes32 _declaration, address _tokenRegistry) { name = _name; symbol = _symbol; decimals = _decimals; registry = _tokenRegistry; declaration = _declaration; } function seal(uint256 _state) public returns(uint256) { require(_state <= maxSealState, 'ERR_INVALID_STATE'); require(_state & sealState == 0, 'ERR_ALREADY_LOCKED'); sealState |= _state; emit SealStateChange(sealState == maxSealState, sealState); return uint256(sealState); } function isSealed(uint256 _state) public view returns(bool) { require(_state < maxSealState); if (_state == 0) { return sealState == maxSealState; } return _state & sealState == _state; } // Change address for collecting fees function setFeeAddress(address _feeAddress) public { require(!isSealed(FEEADDRESS_STATE), "ERR_SEAL"); require(msg.sender == owner, "ERR_AXX"); feeAddress = _feeAddress; } // Change address for collecting fees function setFee(uint256 _fee) public { require(!isSealed(FEE_STATE), "ERR_SEAL"); require(msg.sender == owner, "ERR_AXX"); require(_fee < 1000000, "ERR_FEE_TOO_HIGH"); feePpm = _fee; } // Implements EIP173 function transferOwnership(address _newOwner) public returns (bool) { address oldOwner; require(msg.sender == owner); oldOwner = owner; owner = _newOwner; emit OwnershipTransferred(oldOwner, owner); return true; } function deposit(address _token, uint256 _value) public { bool r; bytes memory v; mustAllowedToken(_token, registry); (r, v) = _token.call(abi.encodeWithSignature('transferFrom(address,address,uint256)', msg.sender, this, _value)); require(r, "ERR_TOKEN"); r = abi.decode(v, (bool)); require(r, "ERR_TRANSFER"); totalSupply += _value; } function getFee(uint256 _value) private view returns (uint256) { uint256 fee; fee = _value * feePpm; fee /= 1000000; return fee; } function getQuote(address _outToken, address _inToken, uint256 _value) private returns (uint256) { bool r; bytes memory v; uint256 quote; if (quoter == address(0x0)) { return _value; } (r, v) = quoter.call(abi.encodeWithSignature('valueFor(address,address,uint256)', _outToken, _inToken, _value)); require(r, "ERR_QUOTER"); quote = abi.decode(v, (uint256)); return quote; } function withdraw(address _outToken, address _inToken, uint256 _value) public { bool r; bytes memory v; uint256 netValue; uint256 balance; uint256 fee; fee = getFee(_value); netValue = _value - fee; netValue = getQuote(_outToken, _inToken, netValue); (r, v) = _outToken.call(abi.encodeWithSignature("balanceOf(address)", this)); require(r, "ERR_TOKEN"); balance = abi.decode(v, (uint256)); require(balance >= netValue + fee, "ERR_BALANCE"); deposit(_inToken, _value); (r, v) = _outToken.call(abi.encodeWithSignature('transfer(address,uint256)', msg.sender, netValue)); require(r, "ERR_TOKEN"); r = abi.decode(v, (bool)); require(r, "ERR_TRANSFER"); if (feeAddress != address(0)) { fees[_outToken] += fee; } } // Withdraw token to fee address function withdraw(address _outToken) public returns (uint256) { uint256 balance; balance = fees[_outToken]; return withdraw(_outToken, balance); } function withdraw(address _outToken, uint256 _value) public returns (uint256) { bool r; bytes memory v; require(feeAddress != address(0), "ERR_AXX"); (r, v) = _outToken.call(abi.encodeWithSignature('transferFrom(address,address,uint256)', this, feeAddress, _value)); require(r, "ERR_TOKEN"); r = abi.decode(v, (bool)); require(r, "ERR_TRANSFER"); return _value; } function mustAllowedToken(address _token, address _registry) private { bool r; bytes memory v; if (_registry == address(0)) { return; } (r, v) = _registry.call(abi.encodeWithSignature('have(address)', _token)); require(r, "ERR_REGISTRY"); r = abi.decode(v, (bool)); require(r, "ERR_UNAUTH_TOKEN"); } // Implements EIP165 function supportsInterface(bytes4 _sum) public pure returns (bool) { if (_sum == 0x01ffc9a7) { // ERC165 return true; } if (_sum == 0x9493f8b2) { // ERC173 return true; } if (_sum == 0x0d7491f8) { // Seal return true; } return false; } }