Created
June 25, 2020 10:59
-
-
Save Marenz/6177898f2f839e9a7c584db9729951e2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
*Submitted for verification at Etherscan.io on 2020-01-22 | |
*/ | |
/** | |
* Copyright © 2017-2019 Ramp Network sp. z o.o. All rights reserved (MIT License). | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software | |
* and associated documentation files (the "Software"), to deal in the Software without restriction, | |
* including without limitation the rights to use, copy, modify, merge, publish, distribute, | |
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software | |
* is furnished to do so, subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be included in all copies | |
* or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING | |
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE | |
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | |
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
*/ | |
/** | |
* A standard, simple transferrable contract ownership. | |
*/ | |
contract Ownable { | |
address public owner; | |
event OwnerChanged(address oldOwner, address newOwner); | |
constructor() internal { | |
owner = msg.sender; | |
} | |
modifier onlyOwner() { | |
require(msg.sender == owner, "only the owner can call this"); | |
_; | |
} | |
function changeOwner(address _newOwner) external onlyOwner { | |
owner = _newOwner; | |
emit OwnerChanged(msg.sender, _newOwner); | |
} | |
} | |
/** | |
* A contract that can be stopped/restarted by its owner. | |
*/ | |
contract Stoppable is Ownable { | |
bool public isActive = true; | |
event IsActiveChanged(bool _isActive); | |
modifier onlyActive() { | |
require(isActive, "contract is stopped"); | |
_; | |
} | |
function setIsActive(bool _isActive) external onlyOwner { | |
if (_isActive == isActive) return; | |
isActive = _isActive; | |
emit IsActiveChanged(_isActive); | |
} | |
} | |
/** | |
* A simple interface used by the escrows contract (precisely AssetAdapters) to interact | |
* with the liquidity pools. | |
*/ | |
abstract contract RampInstantPoolInterface { | |
uint16 public ASSET_TYPE; | |
function sendFundsToSwap(uint256 _amount) | |
public virtual /*onlyActive onlySwapsContract isWithinLimits*/ returns(bool success); | |
} | |
abstract contract RampInstantTokenPoolInterface is RampInstantPoolInterface { | |
address public token; | |
} | |
/** | |
* An interface of the RampInstantEscrows functions that are used by the liquidity pool contracts. | |
* See RampInstantEscrows.sol for more comments. | |
*/ | |
abstract contract RampInstantEscrowsPoolInterface { | |
uint16 public ASSET_TYPE; | |
function release( | |
address _pool, | |
address payable _receiver, | |
address _oracle, | |
bytes calldata _assetData, | |
bytes32 _paymentDetailsHash | |
) | |
virtual external; /*statusAtLeast(Status.FINALIZE_ONLY) onlyOracleOrPool(_pool, _oracle)*/ | |
function returnFunds( | |
address payable _pool, | |
address _receiver, | |
address _oracle, | |
bytes calldata _assetData, | |
bytes32 _paymentDetailsHash | |
) | |
external virtual; /*statusAtLeast(Status.RETURN_ONLY) onlyOracleOrPool(_pool, _oracle)*/ | |
} | |
/** | |
* An abstract Ramp Instant Liquidity Pool. A liquidity provider deploys an instance of this | |
* contract, and sends his funds to it. The escrows contract later withdraws portions of these | |
* funds to be locked. The owner can withdraw any part of the funds at any time, or temporarily | |
* block creating new escrows by stopping the contract. | |
* | |
* The pool owner can set and update min/max swap amounts, with an upper limit of 2^240 wei/units | |
* (see `AssetAdapterWithFees` for more info). | |
* | |
* The paymentDetailsHash parameters works the same as in the `RampInstantEscrows` contract, only | |
* with 0 value and empty transfer title. It describes the bank account where the pool owner expects | |
* to be paid, and can be used to validate that a created swap indeed uses the same account. | |
* | |
* @author Ramp Network sp. z o.o. | |
*/ | |
abstract contract RampInstantPool is Ownable, Stoppable, RampInstantPoolInterface { | |
uint256 constant private MAX_SWAP_AMOUNT_LIMIT = 1 << 240; | |
address payable public swapsContract; | |
uint256 public minSwapAmount; | |
uint256 public maxSwapAmount; | |
bytes32 public paymentDetailsHash; | |
/** | |
* Triggered when the pool receives new funds, either a topup, or a returned escrow from an old | |
* swaps contract if it was changed. Avilable for ETH, ERC-223 and ERC-777 token pools. | |
* Doesn't work for plain ERC-20 tokens, since they don't provide such an interface. | |
*/ | |
event ReceivedFunds(address _from, uint256 _amount); | |
event LimitsChanged(uint256 _minAmount, uint256 _maxAmount); | |
event SwapsContractChanged(address _oldAddress, address _newAddress); | |
constructor( | |
address payable _swapsContract, | |
uint256 _minSwapAmount, | |
uint256 _maxSwapAmount, | |
bytes32 _paymentDetailsHash, | |
uint16 _assetType | |
) | |
public | |
validateLimits(_minSwapAmount, _maxSwapAmount) | |
validateSwapsContract(_swapsContract, _assetType) | |
{ | |
swapsContract = _swapsContract; | |
paymentDetailsHash = _paymentDetailsHash; | |
minSwapAmount = _minSwapAmount; | |
maxSwapAmount = _maxSwapAmount; | |
ASSET_TYPE = _assetType; | |
} | |
function availableFunds() public virtual view returns (uint256); | |
function withdrawFunds(address payable _to, uint256 _amount) | |
public virtual /*onlyOwner*/ returns (bool success); | |
function withdrawAllFunds(address payable _to) public onlyOwner returns (bool success) { | |
return withdrawFunds(_to, availableFunds()); | |
} | |
function setLimits( | |
uint256 _minAmount, | |
uint256 _maxAmount | |
) public onlyOwner validateLimits(_minAmount, _maxAmount) { | |
minSwapAmount = _minAmount; | |
maxSwapAmount = _maxAmount; | |
emit LimitsChanged(_minAmount, _maxAmount); | |
} | |
function setSwapsContract( | |
address payable _swapsContract | |
) public onlyOwner validateSwapsContract(_swapsContract, ASSET_TYPE) { | |
address oldSwapsContract = swapsContract; | |
swapsContract = _swapsContract; | |
emit SwapsContractChanged(oldSwapsContract, _swapsContract); | |
} | |
function sendFundsToSwap(uint256 _amount) | |
public override virtual /*onlyActive onlySwapsContract isWithinLimits*/ returns(bool success); | |
function releaseSwap( | |
address payable _receiver, | |
address _oracle, | |
bytes calldata _assetData, | |
bytes32 _paymentDetailsHash | |
) external onlyOwner { | |
RampInstantEscrowsPoolInterface(swapsContract).release( | |
address(this), | |
_receiver, | |
_oracle, | |
_assetData, | |
_paymentDetailsHash | |
); | |
} | |
function returnSwap( | |
address _receiver, | |
address _oracle, | |
bytes calldata _assetData, | |
bytes32 _paymentDetailsHash | |
) external onlyOwner { | |
RampInstantEscrowsPoolInterface(swapsContract).returnFunds( | |
address(this), | |
_receiver, | |
_oracle, | |
_assetData, | |
_paymentDetailsHash | |
); | |
} | |
/** | |
* Needed for address(this) to be payable in call to returnFunds. | |
* The Eth pool overrides this to not throw. | |
*/ | |
fallback() external payable { | |
revert("this pool cannot receive ether"); | |
} | |
modifier onlySwapsContract() { | |
require(msg.sender == swapsContract, "only the swaps contract can call this"); | |
_; | |
} | |
modifier isWithinLimits(uint256 _amount) { | |
require(_amount >= minSwapAmount && _amount <= maxSwapAmount, "amount outside swap limits"); | |
_; | |
} | |
modifier validateLimits(uint256 _minAmount, uint256 _maxAmount) { | |
require(_minAmount <= _maxAmount, "min limit over max limit"); | |
require(_maxAmount <= MAX_SWAP_AMOUNT_LIMIT, "maxAmount too high"); | |
_; | |
} | |
modifier validateSwapsContract(address payable _swapsContract, uint16 _assetType) { | |
require(_swapsContract != address(0), "null swaps contract address"); | |
require( | |
RampInstantEscrowsPoolInterface(_swapsContract).ASSET_TYPE() == _assetType, | |
"pool asset type doesn't match swap contract" | |
); | |
_; | |
} | |
} | |
/** | |
* @title partial ERC-20 Token interface according to official documentation: | |
* https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md | |
*/ | |
interface Erc20Token { | |
/** | |
* Send `_value` of tokens from `msg.sender` to `_to` | |
* | |
* @param _to The recipient address | |
* @param _value The amount of tokens to be transferred | |
* @return success Indication if the transfer was successful | |
*/ | |
function transfer(address _to, uint256 _value) external returns (bool success); | |
/** | |
* Approve `_spender` to withdraw from sender's account multiple times, up to `_value` | |
* amount. If this function is called again it overwrites the current allowance with _value. | |
* | |
* @param _spender The address allowed to operate on sender's tokens | |
* @param _value The amount of tokens allowed to be transferred | |
* @return success Indication if the approval was successful | |
*/ | |
function approve(address _spender, uint256 _value) external returns (bool success); | |
/** | |
* Transfer tokens on behalf of `_from`, provided it was previously approved. | |
* | |
* @param _from The transfer source address (tokens owner) | |
* @param _to The transfer destination address | |
* @param _value The amount of tokens to be transferred | |
* @return success Indication if the approval was successful | |
*/ | |
function transferFrom(address _from, address _to, uint256 _value) external returns (bool success); | |
/** | |
* Returns the account balance of another account with address `_owner`. | |
*/ | |
function balanceOf(address _owner) external view returns (uint256); | |
} | |
/** | |
* Partial ERC-1820 registry | |
* https://github.com/0xjac/ERC1820/blob/master/contracts/ERC1820Client.sol | |
*/ | |
abstract contract ERC1820Registry { | |
function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) virtual external; | |
} | |
/** | |
* https://github.com/0xjac/ERC777/blob/devel/contracts/examples/ExampleTokensRecipient.sol | |
*/ | |
abstract contract ERC777TokenRecipient { | |
ERC1820Registry internal constant ERC1820REGISTRY = ERC1820Registry( | |
0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24 | |
); | |
bytes32 internal constant ERC777TokenRecipientERC1820Hash = keccak256( | |
abi.encodePacked("ERC777TokensRecipient") | |
); | |
bytes32 constant internal ERC1820_ACCEPT_MAGIC = keccak256( | |
abi.encodePacked("ERC1820_ACCEPT_MAGIC") | |
); | |
constructor(bool _doSetErc1820Registry) internal { | |
if (_doSetErc1820Registry) { | |
ERC1820REGISTRY.setInterfaceImplementer( | |
address(this), | |
ERC777TokenRecipientERC1820Hash, | |
address(this) | |
); | |
} | |
} | |
function canImplementInterfaceForAddress( | |
bytes32 _interfaceHash, | |
address _addr | |
) external view returns(bytes32) { | |
if (_interfaceHash == ERC777TokenRecipientERC1820Hash && _addr == address(this)) { | |
return ERC1820_ACCEPT_MAGIC; | |
} | |
return 0; | |
} | |
function tokensReceived( | |
address _operator, | |
address _from, | |
address _to, | |
uint256 _amount, | |
bytes calldata _data, | |
bytes calldata _operatorData) virtual external; | |
} | |
/** | |
* A pool that implements handling of ERC-20-compatible token assets. See `RampInstantPool`. | |
* | |
* For ERC-777 tokens, enable `_doSetErc1820Registry` on deployment, if you want to receive the | |
* `ReceivedFunds` events. For ERC-223 tokens no actions are needed. For plain ERC-20 tokens, that | |
* event is not available. | |
* | |
* @author Ramp Network sp. z o.o. | |
*/ | |
contract RampInstantTokenPool is RampInstantPool, ERC777TokenRecipient { | |
uint16 internal constant TOKEN_TYPE_ID = 2; | |
Erc20Token public token; | |
constructor( | |
address payable _swapsContract, | |
uint256 _minSwapAmount, | |
uint256 _maxSwapAmount, | |
bytes32 _paymentDetailsHash, | |
address _tokenAddress, | |
bool _doSetErc1820Registry | |
) | |
public | |
RampInstantPool( | |
_swapsContract, _minSwapAmount, _maxSwapAmount, _paymentDetailsHash, TOKEN_TYPE_ID | |
) | |
ERC777TokenRecipient(_doSetErc1820Registry) | |
{ | |
token = Erc20Token(_tokenAddress); | |
} | |
function availableFunds() public override view returns(uint256) { | |
return token.balanceOf(address(this)); | |
} | |
function withdrawFunds( | |
address payable _to, | |
uint256 _amount | |
) public override onlyOwner returns (bool success) { | |
return token.transfer(_to, _amount); | |
} | |
function sendFundsToSwap( | |
uint256 _amount | |
) public override onlyActive onlySwapsContract isWithinLimits(_amount) returns(bool success) { | |
return token.transfer(swapsContract, _amount); | |
} | |
/** ERC-223 token fallback function */ | |
function tokenFallback(address _from, uint _value, bytes memory _data) public { | |
require(_data.length == 0, "tokens with data not supported"); | |
if (_from != swapsContract) { | |
emit ReceivedFunds(_from, _value); | |
} | |
} | |
/** ERC-777 token received hook */ | |
function tokensReceived( | |
address /*_operator*/, | |
address _from, | |
address /*_to*/, | |
uint256 _amount, | |
bytes calldata _data, | |
bytes calldata /*_operatorData*/ | |
) override external { | |
require(_data.length == 0, "tokens with data not supported"); | |
if (_from != swapsContract) { | |
emit ReceivedFunds(_from, _amount); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment