Transaction Hash:
Block:
7660533 at Apr-29-2019 05:38:15 AM +UTC
Transaction Fee:
0.00021191999994702 ETH
$0.46
Gas Used:
52,980 Gas / 3.999999999 Gwei
Account State Difference:
| Address | Before | After | State Difference | ||
|---|---|---|---|---|---|
|
0x52bc44d5...b7d7bE3b5
Miner
| (Nanopool) | 7,352.285836580816663587 Eth | 7,352.286048500816610607 Eth | 0.00021191999994702 | |
| 0xDE139153...32E23126a |
0.103163362680713272 Eth
Nonce: 2496
|
0.102951442680766252 Eth
Nonce: 2497
| 0.00021191999994702 |
Execution Trace
0x778fdaf34bb5993d69968f541152451241fab7e3.e42a91ef( )
Vyper_contract.getEthToTokenInputPrice( eth_sold=23250000000000000000 ) => ( out=3633213997708571361260 )
Vyper_contract.getEthToTokenInputPrice( eth_sold=23250000000000000000 ) => ( out=3633213997708571361260 )
-
DSToken.balanceOf( src=0x09cabEC1eAd1c0Ba254B09efb3EE13841712bE14 ) => ( 1299182604383572327888648 )
-
-
DSToken.CALL( )
KyberOasisReserve.getConversionRate( src=0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359, dest=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, srcQty=3633213997708571361260, blockNumber=7660533 ) => ( 6341385886840431 )-
MatchingMarket.getBestOffer( sell_gem=0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, buy_gem=0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359 ) => ( 91726 ) -
MatchingMarket.getOffer( id=91726 ) => ( 233311690360448352691, 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, 36699928893698525878296, 0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359 ) -
DSToken.balanceOf( src=0x04A487aFd662c4F9DEAcC07A7B10cFb686B682A4 ) => ( 7185707144979830293449 )
-
File 1 of 5: Vyper_contract
File 2 of 5: Vyper_contract
File 3 of 5: DSToken
File 4 of 5: KyberOasisReserve
File 5 of 5: MatchingMarket
# @title Uniswap Exchange Interface V1
# @notice Source code found at https://github.com/uniswap
# @notice Use at your own risk
contract Factory():
def getExchange(token_addr: address) -> address: constant
contract Exchange():
def getEthToTokenOutputPrice(tokens_bought: uint256) -> uint256(wei): constant
def ethToTokenTransferInput(min_tokens: uint256, deadline: timestamp, recipient: address) -> uint256: modifying
def ethToTokenTransferOutput(tokens_bought: uint256, deadline: timestamp, recipient: address) -> uint256(wei): modifying
TokenPurchase: event({buyer: indexed(address), eth_sold: indexed(uint256(wei)), tokens_bought: indexed(uint256)})
EthPurchase: event({buyer: indexed(address), tokens_sold: indexed(uint256), eth_bought: indexed(uint256(wei))})
AddLiquidity: event({provider: indexed(address), eth_amount: indexed(uint256(wei)), token_amount: indexed(uint256)})
RemoveLiquidity: event({provider: indexed(address), eth_amount: indexed(uint256(wei)), token_amount: indexed(uint256)})
Transfer: event({_from: indexed(address), _to: indexed(address), _value: uint256})
Approval: event({_owner: indexed(address), _spender: indexed(address), _value: uint256})
name: public(bytes32) # Uniswap V1
symbol: public(bytes32) # UNI-V1
decimals: public(uint256) # 18
totalSupply: public(uint256) # total number of UNI in existence
balances: uint256[address] # UNI balance of an address
allowances: (uint256[address])[address] # UNI allowance of one address on another
token: address(ERC20) # address of the ERC20 token traded on this contract
factory: Factory # interface for the factory that created this contract
# @dev This function acts as a contract constructor which is not currently supported in contracts deployed
# using create_with_code_of(). It is called once by the factory during contract creation.
@public
def setup(token_addr: address):
assert (self.factory == ZERO_ADDRESS and self.token == ZERO_ADDRESS) and token_addr != ZERO_ADDRESS
self.factory = msg.sender
self.token = token_addr
self.name = 0x556e697377617020563100000000000000000000000000000000000000000000
self.symbol = 0x554e492d56310000000000000000000000000000000000000000000000000000
self.decimals = 18
# @notice Deposit ETH and Tokens (self.token) at current ratio to mint UNI tokens.
# @dev min_liquidity does nothing when total UNI supply is 0.
# @param min_liquidity Minimum number of UNI sender will mint if total UNI supply is greater than 0.
# @param max_tokens Maximum number of tokens deposited. Deposits max amount if total UNI supply is 0.
# @param deadline Time after which this transaction can no longer be executed.
# @return The amount of UNI minted.
@public
@payable
def addLiquidity(min_liquidity: uint256, max_tokens: uint256, deadline: timestamp) -> uint256:
assert deadline > block.timestamp and (max_tokens > 0 and msg.value > 0)
total_liquidity: uint256 = self.totalSupply
if total_liquidity > 0:
assert min_liquidity > 0
eth_reserve: uint256(wei) = self.balance - msg.value
token_reserve: uint256 = self.token.balanceOf(self)
token_amount: uint256 = msg.value * token_reserve / eth_reserve + 1
liquidity_minted: uint256 = msg.value * total_liquidity / eth_reserve
assert max_tokens >= token_amount and liquidity_minted >= min_liquidity
self.balances[msg.sender] += liquidity_minted
self.totalSupply = total_liquidity + liquidity_minted
assert self.token.transferFrom(msg.sender, self, token_amount)
log.AddLiquidity(msg.sender, msg.value, token_amount)
log.Transfer(ZERO_ADDRESS, msg.sender, liquidity_minted)
return liquidity_minted
else:
assert (self.factory != ZERO_ADDRESS and self.token != ZERO_ADDRESS) and msg.value >= 1000000000
assert self.factory.getExchange(self.token) == self
token_amount: uint256 = max_tokens
initial_liquidity: uint256 = as_unitless_number(self.balance)
self.totalSupply = initial_liquidity
self.balances[msg.sender] = initial_liquidity
assert self.token.transferFrom(msg.sender, self, token_amount)
log.AddLiquidity(msg.sender, msg.value, token_amount)
log.Transfer(ZERO_ADDRESS, msg.sender, initial_liquidity)
return initial_liquidity
# @dev Burn UNI tokens to withdraw ETH and Tokens at current ratio.
# @param amount Amount of UNI burned.
# @param min_eth Minimum ETH withdrawn.
# @param min_tokens Minimum Tokens withdrawn.
# @param deadline Time after which this transaction can no longer be executed.
# @return The amount of ETH and Tokens withdrawn.
@public
def removeLiquidity(amount: uint256, min_eth: uint256(wei), min_tokens: uint256, deadline: timestamp) -> (uint256(wei), uint256):
assert (amount > 0 and deadline > block.timestamp) and (min_eth > 0 and min_tokens > 0)
total_liquidity: uint256 = self.totalSupply
assert total_liquidity > 0
token_reserve: uint256 = self.token.balanceOf(self)
eth_amount: uint256(wei) = amount * self.balance / total_liquidity
token_amount: uint256 = amount * token_reserve / total_liquidity
assert eth_amount >= min_eth and token_amount >= min_tokens
self.balances[msg.sender] -= amount
self.totalSupply = total_liquidity - amount
send(msg.sender, eth_amount)
assert self.token.transfer(msg.sender, token_amount)
log.RemoveLiquidity(msg.sender, eth_amount, token_amount)
log.Transfer(msg.sender, ZERO_ADDRESS, amount)
return eth_amount, token_amount
# @dev Pricing function for converting between ETH and Tokens.
# @param input_amount Amount of ETH or Tokens being sold.
# @param input_reserve Amount of ETH or Tokens (input type) in exchange reserves.
# @param output_reserve Amount of ETH or Tokens (output type) in exchange reserves.
# @return Amount of ETH or Tokens bought.
@private
@constant
def getInputPrice(input_amount: uint256, input_reserve: uint256, output_reserve: uint256) -> uint256:
assert input_reserve > 0 and output_reserve > 0
input_amount_with_fee: uint256 = input_amount * 997
numerator: uint256 = input_amount_with_fee * output_reserve
denominator: uint256 = (input_reserve * 1000) + input_amount_with_fee
return numerator / denominator
# @dev Pricing function for converting between ETH and Tokens.
# @param output_amount Amount of ETH or Tokens being bought.
# @param input_reserve Amount of ETH or Tokens (input type) in exchange reserves.
# @param output_reserve Amount of ETH or Tokens (output type) in exchange reserves.
# @return Amount of ETH or Tokens sold.
@private
@constant
def getOutputPrice(output_amount: uint256, input_reserve: uint256, output_reserve: uint256) -> uint256:
assert input_reserve > 0 and output_reserve > 0
numerator: uint256 = input_reserve * output_amount * 1000
denominator: uint256 = (output_reserve - output_amount) * 997
return numerator / denominator + 1
@private
def ethToTokenInput(eth_sold: uint256(wei), min_tokens: uint256, deadline: timestamp, buyer: address, recipient: address) -> uint256:
assert deadline >= block.timestamp and (eth_sold > 0 and min_tokens > 0)
token_reserve: uint256 = self.token.balanceOf(self)
tokens_bought: uint256 = self.getInputPrice(as_unitless_number(eth_sold), as_unitless_number(self.balance - eth_sold), token_reserve)
assert tokens_bought >= min_tokens
assert self.token.transfer(recipient, tokens_bought)
log.TokenPurchase(buyer, eth_sold, tokens_bought)
return tokens_bought
# @notice Convert ETH to Tokens.
# @dev User specifies exact input (msg.value).
# @dev User cannot specify minimum output or deadline.
@public
@payable
def __default__():
self.ethToTokenInput(msg.value, 1, block.timestamp, msg.sender, msg.sender)
# @notice Convert ETH to Tokens.
# @dev User specifies exact input (msg.value) and minimum output.
# @param min_tokens Minimum Tokens bought.
# @param deadline Time after which this transaction can no longer be executed.
# @return Amount of Tokens bought.
@public
@payable
def ethToTokenSwapInput(min_tokens: uint256, deadline: timestamp) -> uint256:
return self.ethToTokenInput(msg.value, min_tokens, deadline, msg.sender, msg.sender)
# @notice Convert ETH to Tokens and transfers Tokens to recipient.
# @dev User specifies exact input (msg.value) and minimum output
# @param min_tokens Minimum Tokens bought.
# @param deadline Time after which this transaction can no longer be executed.
# @param recipient The address that receives output Tokens.
# @return Amount of Tokens bought.
@public
@payable
def ethToTokenTransferInput(min_tokens: uint256, deadline: timestamp, recipient: address) -> uint256:
assert recipient != self and recipient != ZERO_ADDRESS
return self.ethToTokenInput(msg.value, min_tokens, deadline, msg.sender, recipient)
@private
def ethToTokenOutput(tokens_bought: uint256, max_eth: uint256(wei), deadline: timestamp, buyer: address, recipient: address) -> uint256(wei):
assert deadline >= block.timestamp and (tokens_bought > 0 and max_eth > 0)
token_reserve: uint256 = self.token.balanceOf(self)
eth_sold: uint256 = self.getOutputPrice(tokens_bought, as_unitless_number(self.balance - max_eth), token_reserve)
# Throws if eth_sold > max_eth
eth_refund: uint256(wei) = max_eth - as_wei_value(eth_sold, 'wei')
if eth_refund > 0:
send(buyer, eth_refund)
assert self.token.transfer(recipient, tokens_bought)
log.TokenPurchase(buyer, as_wei_value(eth_sold, 'wei'), tokens_bought)
return as_wei_value(eth_sold, 'wei')
# @notice Convert ETH to Tokens.
# @dev User specifies maximum input (msg.value) and exact output.
# @param tokens_bought Amount of tokens bought.
# @param deadline Time after which this transaction can no longer be executed.
# @return Amount of ETH sold.
@public
@payable
def ethToTokenSwapOutput(tokens_bought: uint256, deadline: timestamp) -> uint256(wei):
return self.ethToTokenOutput(tokens_bought, msg.value, deadline, msg.sender, msg.sender)
# @notice Convert ETH to Tokens and transfers Tokens to recipient.
# @dev User specifies maximum input (msg.value) and exact output.
# @param tokens_bought Amount of tokens bought.
# @param deadline Time after which this transaction can no longer be executed.
# @param recipient The address that receives output Tokens.
# @return Amount of ETH sold.
@public
@payable
def ethToTokenTransferOutput(tokens_bought: uint256, deadline: timestamp, recipient: address) -> uint256(wei):
assert recipient != self and recipient != ZERO_ADDRESS
return self.ethToTokenOutput(tokens_bought, msg.value, deadline, msg.sender, recipient)
@private
def tokenToEthInput(tokens_sold: uint256, min_eth: uint256(wei), deadline: timestamp, buyer: address, recipient: address) -> uint256(wei):
assert deadline >= block.timestamp and (tokens_sold > 0 and min_eth > 0)
token_reserve: uint256 = self.token.balanceOf(self)
eth_bought: uint256 = self.getInputPrice(tokens_sold, token_reserve, as_unitless_number(self.balance))
wei_bought: uint256(wei) = as_wei_value(eth_bought, 'wei')
assert wei_bought >= min_eth
send(recipient, wei_bought)
assert self.token.transferFrom(buyer, self, tokens_sold)
log.EthPurchase(buyer, tokens_sold, wei_bought)
return wei_bought
# @notice Convert Tokens to ETH.
# @dev User specifies exact input and minimum output.
# @param tokens_sold Amount of Tokens sold.
# @param min_eth Minimum ETH purchased.
# @param deadline Time after which this transaction can no longer be executed.
# @return Amount of ETH bought.
@public
def tokenToEthSwapInput(tokens_sold: uint256, min_eth: uint256(wei), deadline: timestamp) -> uint256(wei):
return self.tokenToEthInput(tokens_sold, min_eth, deadline, msg.sender, msg.sender)
# @notice Convert Tokens to ETH and transfers ETH to recipient.
# @dev User specifies exact input and minimum output.
# @param tokens_sold Amount of Tokens sold.
# @param min_eth Minimum ETH purchased.
# @param deadline Time after which this transaction can no longer be executed.
# @param recipient The address that receives output ETH.
# @return Amount of ETH bought.
@public
def tokenToEthTransferInput(tokens_sold: uint256, min_eth: uint256(wei), deadline: timestamp, recipient: address) -> uint256(wei):
assert recipient != self and recipient != ZERO_ADDRESS
return self.tokenToEthInput(tokens_sold, min_eth, deadline, msg.sender, recipient)
@private
def tokenToEthOutput(eth_bought: uint256(wei), max_tokens: uint256, deadline: timestamp, buyer: address, recipient: address) -> uint256:
assert deadline >= block.timestamp and eth_bought > 0
token_reserve: uint256 = self.token.balanceOf(self)
tokens_sold: uint256 = self.getOutputPrice(as_unitless_number(eth_bought), token_reserve, as_unitless_number(self.balance))
# tokens sold is always > 0
assert max_tokens >= tokens_sold
send(recipient, eth_bought)
assert self.token.transferFrom(buyer, self, tokens_sold)
log.EthPurchase(buyer, tokens_sold, eth_bought)
return tokens_sold
# @notice Convert Tokens to ETH.
# @dev User specifies maximum input and exact output.
# @param eth_bought Amount of ETH purchased.
# @param max_tokens Maximum Tokens sold.
# @param deadline Time after which this transaction can no longer be executed.
# @return Amount of Tokens sold.
@public
def tokenToEthSwapOutput(eth_bought: uint256(wei), max_tokens: uint256, deadline: timestamp) -> uint256:
return self.tokenToEthOutput(eth_bought, max_tokens, deadline, msg.sender, msg.sender)
# @notice Convert Tokens to ETH and transfers ETH to recipient.
# @dev User specifies maximum input and exact output.
# @param eth_bought Amount of ETH purchased.
# @param max_tokens Maximum Tokens sold.
# @param deadline Time after which this transaction can no longer be executed.
# @param recipient The address that receives output ETH.
# @return Amount of Tokens sold.
@public
def tokenToEthTransferOutput(eth_bought: uint256(wei), max_tokens: uint256, deadline: timestamp, recipient: address) -> uint256:
assert recipient != self and recipient != ZERO_ADDRESS
return self.tokenToEthOutput(eth_bought, max_tokens, deadline, msg.sender, recipient)
@private
def tokenToTokenInput(tokens_sold: uint256, min_tokens_bought: uint256, min_eth_bought: uint256(wei), deadline: timestamp, buyer: address, recipient: address, exchange_addr: address) -> uint256:
assert (deadline >= block.timestamp and tokens_sold > 0) and (min_tokens_bought > 0 and min_eth_bought > 0)
assert exchange_addr != self and exchange_addr != ZERO_ADDRESS
token_reserve: uint256 = self.token.balanceOf(self)
eth_bought: uint256 = self.getInputPrice(tokens_sold, token_reserve, as_unitless_number(self.balance))
wei_bought: uint256(wei) = as_wei_value(eth_bought, 'wei')
assert wei_bought >= min_eth_bought
assert self.token.transferFrom(buyer, self, tokens_sold)
tokens_bought: uint256 = Exchange(exchange_addr).ethToTokenTransferInput(min_tokens_bought, deadline, recipient, value=wei_bought)
log.EthPurchase(buyer, tokens_sold, wei_bought)
return tokens_bought
# @notice Convert Tokens (self.token) to Tokens (token_addr).
# @dev User specifies exact input and minimum output.
# @param tokens_sold Amount of Tokens sold.
# @param min_tokens_bought Minimum Tokens (token_addr) purchased.
# @param min_eth_bought Minimum ETH purchased as intermediary.
# @param deadline Time after which this transaction can no longer be executed.
# @param token_addr The address of the token being purchased.
# @return Amount of Tokens (token_addr) bought.
@public
def tokenToTokenSwapInput(tokens_sold: uint256, min_tokens_bought: uint256, min_eth_bought: uint256(wei), deadline: timestamp, token_addr: address) -> uint256:
exchange_addr: address = self.factory.getExchange(token_addr)
return self.tokenToTokenInput(tokens_sold, min_tokens_bought, min_eth_bought, deadline, msg.sender, msg.sender, exchange_addr)
# @notice Convert Tokens (self.token) to Tokens (token_addr) and transfers
# Tokens (token_addr) to recipient.
# @dev User specifies exact input and minimum output.
# @param tokens_sold Amount of Tokens sold.
# @param min_tokens_bought Minimum Tokens (token_addr) purchased.
# @param min_eth_bought Minimum ETH purchased as intermediary.
# @param deadline Time after which this transaction can no longer be executed.
# @param recipient The address that receives output ETH.
# @param token_addr The address of the token being purchased.
# @return Amount of Tokens (token_addr) bought.
@public
def tokenToTokenTransferInput(tokens_sold: uint256, min_tokens_bought: uint256, min_eth_bought: uint256(wei), deadline: timestamp, recipient: address, token_addr: address) -> uint256:
exchange_addr: address = self.factory.getExchange(token_addr)
return self.tokenToTokenInput(tokens_sold, min_tokens_bought, min_eth_bought, deadline, msg.sender, recipient, exchange_addr)
@private
def tokenToTokenOutput(tokens_bought: uint256, max_tokens_sold: uint256, max_eth_sold: uint256(wei), deadline: timestamp, buyer: address, recipient: address, exchange_addr: address) -> uint256:
assert deadline >= block.timestamp and (tokens_bought > 0 and max_eth_sold > 0)
assert exchange_addr != self and exchange_addr != ZERO_ADDRESS
eth_bought: uint256(wei) = Exchange(exchange_addr).getEthToTokenOutputPrice(tokens_bought)
token_reserve: uint256 = self.token.balanceOf(self)
tokens_sold: uint256 = self.getOutputPrice(as_unitless_number(eth_bought), token_reserve, as_unitless_number(self.balance))
# tokens sold is always > 0
assert max_tokens_sold >= tokens_sold and max_eth_sold >= eth_bought
assert self.token.transferFrom(buyer, self, tokens_sold)
eth_sold: uint256(wei) = Exchange(exchange_addr).ethToTokenTransferOutput(tokens_bought, deadline, recipient, value=eth_bought)
log.EthPurchase(buyer, tokens_sold, eth_bought)
return tokens_sold
# @notice Convert Tokens (self.token) to Tokens (token_addr).
# @dev User specifies maximum input and exact output.
# @param tokens_bought Amount of Tokens (token_addr) bought.
# @param max_tokens_sold Maximum Tokens (self.token) sold.
# @param max_eth_sold Maximum ETH purchased as intermediary.
# @param deadline Time after which this transaction can no longer be executed.
# @param token_addr The address of the token being purchased.
# @return Amount of Tokens (self.token) sold.
@public
def tokenToTokenSwapOutput(tokens_bought: uint256, max_tokens_sold: uint256, max_eth_sold: uint256(wei), deadline: timestamp, token_addr: address) -> uint256:
exchange_addr: address = self.factory.getExchange(token_addr)
return self.tokenToTokenOutput(tokens_bought, max_tokens_sold, max_eth_sold, deadline, msg.sender, msg.sender, exchange_addr)
# @notice Convert Tokens (self.token) to Tokens (token_addr) and transfers
# Tokens (token_addr) to recipient.
# @dev User specifies maximum input and exact output.
# @param tokens_bought Amount of Tokens (token_addr) bought.
# @param max_tokens_sold Maximum Tokens (self.token) sold.
# @param max_eth_sold Maximum ETH purchased as intermediary.
# @param deadline Time after which this transaction can no longer be executed.
# @param recipient The address that receives output ETH.
# @param token_addr The address of the token being purchased.
# @return Amount of Tokens (self.token) sold.
@public
def tokenToTokenTransferOutput(tokens_bought: uint256, max_tokens_sold: uint256, max_eth_sold: uint256(wei), deadline: timestamp, recipient: address, token_addr: address) -> uint256:
exchange_addr: address = self.factory.getExchange(token_addr)
return self.tokenToTokenOutput(tokens_bought, max_tokens_sold, max_eth_sold, deadline, msg.sender, recipient, exchange_addr)
# @notice Convert Tokens (self.token) to Tokens (exchange_addr.token).
# @dev Allows trades through contracts that were not deployed from the same factory.
# @dev User specifies exact input and minimum output.
# @param tokens_sold Amount of Tokens sold.
# @param min_tokens_bought Minimum Tokens (token_addr) purchased.
# @param min_eth_bought Minimum ETH purchased as intermediary.
# @param deadline Time after which this transaction can no longer be executed.
# @param exchange_addr The address of the exchange for the token being purchased.
# @return Amount of Tokens (exchange_addr.token) bought.
@public
def tokenToExchangeSwapInput(tokens_sold: uint256, min_tokens_bought: uint256, min_eth_bought: uint256(wei), deadline: timestamp, exchange_addr: address) -> uint256:
return self.tokenToTokenInput(tokens_sold, min_tokens_bought, min_eth_bought, deadline, msg.sender, msg.sender, exchange_addr)
# @notice Convert Tokens (self.token) to Tokens (exchange_addr.token) and transfers
# Tokens (exchange_addr.token) to recipient.
# @dev Allows trades through contracts that were not deployed from the same factory.
# @dev User specifies exact input and minimum output.
# @param tokens_sold Amount of Tokens sold.
# @param min_tokens_bought Minimum Tokens (token_addr) purchased.
# @param min_eth_bought Minimum ETH purchased as intermediary.
# @param deadline Time after which this transaction can no longer be executed.
# @param recipient The address that receives output ETH.
# @param exchange_addr The address of the exchange for the token being purchased.
# @return Amount of Tokens (exchange_addr.token) bought.
@public
def tokenToExchangeTransferInput(tokens_sold: uint256, min_tokens_bought: uint256, min_eth_bought: uint256(wei), deadline: timestamp, recipient: address, exchange_addr: address) -> uint256:
assert recipient != self
return self.tokenToTokenInput(tokens_sold, min_tokens_bought, min_eth_bought, deadline, msg.sender, recipient, exchange_addr)
# @notice Convert Tokens (self.token) to Tokens (exchange_addr.token).
# @dev Allows trades through contracts that were not deployed from the same factory.
# @dev User specifies maximum input and exact output.
# @param tokens_bought Amount of Tokens (token_addr) bought.
# @param max_tokens_sold Maximum Tokens (self.token) sold.
# @param max_eth_sold Maximum ETH purchased as intermediary.
# @param deadline Time after which this transaction can no longer be executed.
# @param exchange_addr The address of the exchange for the token being purchased.
# @return Amount of Tokens (self.token) sold.
@public
def tokenToExchangeSwapOutput(tokens_bought: uint256, max_tokens_sold: uint256, max_eth_sold: uint256(wei), deadline: timestamp, exchange_addr: address) -> uint256:
return self.tokenToTokenOutput(tokens_bought, max_tokens_sold, max_eth_sold, deadline, msg.sender, msg.sender, exchange_addr)
# @notice Convert Tokens (self.token) to Tokens (exchange_addr.token) and transfers
# Tokens (exchange_addr.token) to recipient.
# @dev Allows trades through contracts that were not deployed from the same factory.
# @dev User specifies maximum input and exact output.
# @param tokens_bought Amount of Tokens (token_addr) bought.
# @param max_tokens_sold Maximum Tokens (self.token) sold.
# @param max_eth_sold Maximum ETH purchased as intermediary.
# @param deadline Time after which this transaction can no longer be executed.
# @param recipient The address that receives output ETH.
# @param token_addr The address of the token being purchased.
# @return Amount of Tokens (self.token) sold.
@public
def tokenToExchangeTransferOutput(tokens_bought: uint256, max_tokens_sold: uint256, max_eth_sold: uint256(wei), deadline: timestamp, recipient: address, exchange_addr: address) -> uint256:
assert recipient != self
return self.tokenToTokenOutput(tokens_bought, max_tokens_sold, max_eth_sold, deadline, msg.sender, recipient, exchange_addr)
# @notice Public price function for ETH to Token trades with an exact input.
# @param eth_sold Amount of ETH sold.
# @return Amount of Tokens that can be bought with input ETH.
@public
@constant
def getEthToTokenInputPrice(eth_sold: uint256(wei)) -> uint256:
assert eth_sold > 0
token_reserve: uint256 = self.token.balanceOf(self)
return self.getInputPrice(as_unitless_number(eth_sold), as_unitless_number(self.balance), token_reserve)
# @notice Public price function for ETH to Token trades with an exact output.
# @param tokens_bought Amount of Tokens bought.
# @return Amount of ETH needed to buy output Tokens.
@public
@constant
def getEthToTokenOutputPrice(tokens_bought: uint256) -> uint256(wei):
assert tokens_bought > 0
token_reserve: uint256 = self.token.balanceOf(self)
eth_sold: uint256 = self.getOutputPrice(tokens_bought, as_unitless_number(self.balance), token_reserve)
return as_wei_value(eth_sold, 'wei')
# @notice Public price function for Token to ETH trades with an exact input.
# @param tokens_sold Amount of Tokens sold.
# @return Amount of ETH that can be bought with input Tokens.
@public
@constant
def getTokenToEthInputPrice(tokens_sold: uint256) -> uint256(wei):
assert tokens_sold > 0
token_reserve: uint256 = self.token.balanceOf(self)
eth_bought: uint256 = self.getInputPrice(tokens_sold, token_reserve, as_unitless_number(self.balance))
return as_wei_value(eth_bought, 'wei')
# @notice Public price function for Token to ETH trades with an exact output.
# @param eth_bought Amount of output ETH.
# @return Amount of Tokens needed to buy output ETH.
@public
@constant
def getTokenToEthOutputPrice(eth_bought: uint256(wei)) -> uint256:
assert eth_bought > 0
token_reserve: uint256 = self.token.balanceOf(self)
return self.getOutputPrice(as_unitless_number(eth_bought), token_reserve, as_unitless_number(self.balance))
# @return Address of Token that is sold on this exchange.
@public
@constant
def tokenAddress() -> address:
return self.token
# @return Address of factory that created this exchange.
@public
@constant
def factoryAddress() -> address(Factory):
return self.factory
# ERC20 compatibility for exchange liquidity modified from
# https://github.com/ethereum/vyper/blob/master/examples/tokens/ERC20.vy
@public
@constant
def balanceOf(_owner : address) -> uint256:
return self.balances[_owner]
@public
def transfer(_to : address, _value : uint256) -> bool:
self.balances[msg.sender] -= _value
self.balances[_to] += _value
log.Transfer(msg.sender, _to, _value)
return True
@public
def transferFrom(_from : address, _to : address, _value : uint256) -> bool:
self.balances[_from] -= _value
self.balances[_to] += _value
self.allowances[_from][msg.sender] -= _value
log.Transfer(_from, _to, _value)
return True
@public
def approve(_spender : address, _value : uint256) -> bool:
self.allowances[msg.sender][_spender] = _value
log.Approval(msg.sender, _spender, _value)
return True
@public
@constant
def allowance(_owner : address, _spender : address) -> uint256:
return self.allowances[_owner][_spender]File 2 of 5: Vyper_contract
# @title Uniswap Exchange Interface V1
# @notice Source code found at https://github.com/uniswap
# @notice Use at your own risk
contract Factory():
def getExchange(token_addr: address) -> address: constant
contract Exchange():
def getEthToTokenOutputPrice(tokens_bought: uint256) -> uint256(wei): constant
def ethToTokenTransferInput(min_tokens: uint256, deadline: timestamp, recipient: address) -> uint256: modifying
def ethToTokenTransferOutput(tokens_bought: uint256, deadline: timestamp, recipient: address) -> uint256(wei): modifying
TokenPurchase: event({buyer: indexed(address), eth_sold: indexed(uint256(wei)), tokens_bought: indexed(uint256)})
EthPurchase: event({buyer: indexed(address), tokens_sold: indexed(uint256), eth_bought: indexed(uint256(wei))})
AddLiquidity: event({provider: indexed(address), eth_amount: indexed(uint256(wei)), token_amount: indexed(uint256)})
RemoveLiquidity: event({provider: indexed(address), eth_amount: indexed(uint256(wei)), token_amount: indexed(uint256)})
Transfer: event({_from: indexed(address), _to: indexed(address), _value: uint256})
Approval: event({_owner: indexed(address), _spender: indexed(address), _value: uint256})
name: public(bytes32) # Uniswap V1
symbol: public(bytes32) # UNI-V1
decimals: public(uint256) # 18
totalSupply: public(uint256) # total number of UNI in existence
balances: uint256[address] # UNI balance of an address
allowances: (uint256[address])[address] # UNI allowance of one address on another
token: address(ERC20) # address of the ERC20 token traded on this contract
factory: Factory # interface for the factory that created this contract
# @dev This function acts as a contract constructor which is not currently supported in contracts deployed
# using create_with_code_of(). It is called once by the factory during contract creation.
@public
def setup(token_addr: address):
assert (self.factory == ZERO_ADDRESS and self.token == ZERO_ADDRESS) and token_addr != ZERO_ADDRESS
self.factory = msg.sender
self.token = token_addr
self.name = 0x556e697377617020563100000000000000000000000000000000000000000000
self.symbol = 0x554e492d56310000000000000000000000000000000000000000000000000000
self.decimals = 18
# @notice Deposit ETH and Tokens (self.token) at current ratio to mint UNI tokens.
# @dev min_liquidity does nothing when total UNI supply is 0.
# @param min_liquidity Minimum number of UNI sender will mint if total UNI supply is greater than 0.
# @param max_tokens Maximum number of tokens deposited. Deposits max amount if total UNI supply is 0.
# @param deadline Time after which this transaction can no longer be executed.
# @return The amount of UNI minted.
@public
@payable
def addLiquidity(min_liquidity: uint256, max_tokens: uint256, deadline: timestamp) -> uint256:
assert deadline > block.timestamp and (max_tokens > 0 and msg.value > 0)
total_liquidity: uint256 = self.totalSupply
if total_liquidity > 0:
assert min_liquidity > 0
eth_reserve: uint256(wei) = self.balance - msg.value
token_reserve: uint256 = self.token.balanceOf(self)
token_amount: uint256 = msg.value * token_reserve / eth_reserve + 1
liquidity_minted: uint256 = msg.value * total_liquidity / eth_reserve
assert max_tokens >= token_amount and liquidity_minted >= min_liquidity
self.balances[msg.sender] += liquidity_minted
self.totalSupply = total_liquidity + liquidity_minted
assert self.token.transferFrom(msg.sender, self, token_amount)
log.AddLiquidity(msg.sender, msg.value, token_amount)
log.Transfer(ZERO_ADDRESS, msg.sender, liquidity_minted)
return liquidity_minted
else:
assert (self.factory != ZERO_ADDRESS and self.token != ZERO_ADDRESS) and msg.value >= 1000000000
assert self.factory.getExchange(self.token) == self
token_amount: uint256 = max_tokens
initial_liquidity: uint256 = as_unitless_number(self.balance)
self.totalSupply = initial_liquidity
self.balances[msg.sender] = initial_liquidity
assert self.token.transferFrom(msg.sender, self, token_amount)
log.AddLiquidity(msg.sender, msg.value, token_amount)
log.Transfer(ZERO_ADDRESS, msg.sender, initial_liquidity)
return initial_liquidity
# @dev Burn UNI tokens to withdraw ETH and Tokens at current ratio.
# @param amount Amount of UNI burned.
# @param min_eth Minimum ETH withdrawn.
# @param min_tokens Minimum Tokens withdrawn.
# @param deadline Time after which this transaction can no longer be executed.
# @return The amount of ETH and Tokens withdrawn.
@public
def removeLiquidity(amount: uint256, min_eth: uint256(wei), min_tokens: uint256, deadline: timestamp) -> (uint256(wei), uint256):
assert (amount > 0 and deadline > block.timestamp) and (min_eth > 0 and min_tokens > 0)
total_liquidity: uint256 = self.totalSupply
assert total_liquidity > 0
token_reserve: uint256 = self.token.balanceOf(self)
eth_amount: uint256(wei) = amount * self.balance / total_liquidity
token_amount: uint256 = amount * token_reserve / total_liquidity
assert eth_amount >= min_eth and token_amount >= min_tokens
self.balances[msg.sender] -= amount
self.totalSupply = total_liquidity - amount
send(msg.sender, eth_amount)
assert self.token.transfer(msg.sender, token_amount)
log.RemoveLiquidity(msg.sender, eth_amount, token_amount)
log.Transfer(msg.sender, ZERO_ADDRESS, amount)
return eth_amount, token_amount
# @dev Pricing function for converting between ETH and Tokens.
# @param input_amount Amount of ETH or Tokens being sold.
# @param input_reserve Amount of ETH or Tokens (input type) in exchange reserves.
# @param output_reserve Amount of ETH or Tokens (output type) in exchange reserves.
# @return Amount of ETH or Tokens bought.
@private
@constant
def getInputPrice(input_amount: uint256, input_reserve: uint256, output_reserve: uint256) -> uint256:
assert input_reserve > 0 and output_reserve > 0
input_amount_with_fee: uint256 = input_amount * 997
numerator: uint256 = input_amount_with_fee * output_reserve
denominator: uint256 = (input_reserve * 1000) + input_amount_with_fee
return numerator / denominator
# @dev Pricing function for converting between ETH and Tokens.
# @param output_amount Amount of ETH or Tokens being bought.
# @param input_reserve Amount of ETH or Tokens (input type) in exchange reserves.
# @param output_reserve Amount of ETH or Tokens (output type) in exchange reserves.
# @return Amount of ETH or Tokens sold.
@private
@constant
def getOutputPrice(output_amount: uint256, input_reserve: uint256, output_reserve: uint256) -> uint256:
assert input_reserve > 0 and output_reserve > 0
numerator: uint256 = input_reserve * output_amount * 1000
denominator: uint256 = (output_reserve - output_amount) * 997
return numerator / denominator + 1
@private
def ethToTokenInput(eth_sold: uint256(wei), min_tokens: uint256, deadline: timestamp, buyer: address, recipient: address) -> uint256:
assert deadline >= block.timestamp and (eth_sold > 0 and min_tokens > 0)
token_reserve: uint256 = self.token.balanceOf(self)
tokens_bought: uint256 = self.getInputPrice(as_unitless_number(eth_sold), as_unitless_number(self.balance - eth_sold), token_reserve)
assert tokens_bought >= min_tokens
assert self.token.transfer(recipient, tokens_bought)
log.TokenPurchase(buyer, eth_sold, tokens_bought)
return tokens_bought
# @notice Convert ETH to Tokens.
# @dev User specifies exact input (msg.value).
# @dev User cannot specify minimum output or deadline.
@public
@payable
def __default__():
self.ethToTokenInput(msg.value, 1, block.timestamp, msg.sender, msg.sender)
# @notice Convert ETH to Tokens.
# @dev User specifies exact input (msg.value) and minimum output.
# @param min_tokens Minimum Tokens bought.
# @param deadline Time after which this transaction can no longer be executed.
# @return Amount of Tokens bought.
@public
@payable
def ethToTokenSwapInput(min_tokens: uint256, deadline: timestamp) -> uint256:
return self.ethToTokenInput(msg.value, min_tokens, deadline, msg.sender, msg.sender)
# @notice Convert ETH to Tokens and transfers Tokens to recipient.
# @dev User specifies exact input (msg.value) and minimum output
# @param min_tokens Minimum Tokens bought.
# @param deadline Time after which this transaction can no longer be executed.
# @param recipient The address that receives output Tokens.
# @return Amount of Tokens bought.
@public
@payable
def ethToTokenTransferInput(min_tokens: uint256, deadline: timestamp, recipient: address) -> uint256:
assert recipient != self and recipient != ZERO_ADDRESS
return self.ethToTokenInput(msg.value, min_tokens, deadline, msg.sender, recipient)
@private
def ethToTokenOutput(tokens_bought: uint256, max_eth: uint256(wei), deadline: timestamp, buyer: address, recipient: address) -> uint256(wei):
assert deadline >= block.timestamp and (tokens_bought > 0 and max_eth > 0)
token_reserve: uint256 = self.token.balanceOf(self)
eth_sold: uint256 = self.getOutputPrice(tokens_bought, as_unitless_number(self.balance - max_eth), token_reserve)
# Throws if eth_sold > max_eth
eth_refund: uint256(wei) = max_eth - as_wei_value(eth_sold, 'wei')
if eth_refund > 0:
send(buyer, eth_refund)
assert self.token.transfer(recipient, tokens_bought)
log.TokenPurchase(buyer, as_wei_value(eth_sold, 'wei'), tokens_bought)
return as_wei_value(eth_sold, 'wei')
# @notice Convert ETH to Tokens.
# @dev User specifies maximum input (msg.value) and exact output.
# @param tokens_bought Amount of tokens bought.
# @param deadline Time after which this transaction can no longer be executed.
# @return Amount of ETH sold.
@public
@payable
def ethToTokenSwapOutput(tokens_bought: uint256, deadline: timestamp) -> uint256(wei):
return self.ethToTokenOutput(tokens_bought, msg.value, deadline, msg.sender, msg.sender)
# @notice Convert ETH to Tokens and transfers Tokens to recipient.
# @dev User specifies maximum input (msg.value) and exact output.
# @param tokens_bought Amount of tokens bought.
# @param deadline Time after which this transaction can no longer be executed.
# @param recipient The address that receives output Tokens.
# @return Amount of ETH sold.
@public
@payable
def ethToTokenTransferOutput(tokens_bought: uint256, deadline: timestamp, recipient: address) -> uint256(wei):
assert recipient != self and recipient != ZERO_ADDRESS
return self.ethToTokenOutput(tokens_bought, msg.value, deadline, msg.sender, recipient)
@private
def tokenToEthInput(tokens_sold: uint256, min_eth: uint256(wei), deadline: timestamp, buyer: address, recipient: address) -> uint256(wei):
assert deadline >= block.timestamp and (tokens_sold > 0 and min_eth > 0)
token_reserve: uint256 = self.token.balanceOf(self)
eth_bought: uint256 = self.getInputPrice(tokens_sold, token_reserve, as_unitless_number(self.balance))
wei_bought: uint256(wei) = as_wei_value(eth_bought, 'wei')
assert wei_bought >= min_eth
send(recipient, wei_bought)
assert self.token.transferFrom(buyer, self, tokens_sold)
log.EthPurchase(buyer, tokens_sold, wei_bought)
return wei_bought
# @notice Convert Tokens to ETH.
# @dev User specifies exact input and minimum output.
# @param tokens_sold Amount of Tokens sold.
# @param min_eth Minimum ETH purchased.
# @param deadline Time after which this transaction can no longer be executed.
# @return Amount of ETH bought.
@public
def tokenToEthSwapInput(tokens_sold: uint256, min_eth: uint256(wei), deadline: timestamp) -> uint256(wei):
return self.tokenToEthInput(tokens_sold, min_eth, deadline, msg.sender, msg.sender)
# @notice Convert Tokens to ETH and transfers ETH to recipient.
# @dev User specifies exact input and minimum output.
# @param tokens_sold Amount of Tokens sold.
# @param min_eth Minimum ETH purchased.
# @param deadline Time after which this transaction can no longer be executed.
# @param recipient The address that receives output ETH.
# @return Amount of ETH bought.
@public
def tokenToEthTransferInput(tokens_sold: uint256, min_eth: uint256(wei), deadline: timestamp, recipient: address) -> uint256(wei):
assert recipient != self and recipient != ZERO_ADDRESS
return self.tokenToEthInput(tokens_sold, min_eth, deadline, msg.sender, recipient)
@private
def tokenToEthOutput(eth_bought: uint256(wei), max_tokens: uint256, deadline: timestamp, buyer: address, recipient: address) -> uint256:
assert deadline >= block.timestamp and eth_bought > 0
token_reserve: uint256 = self.token.balanceOf(self)
tokens_sold: uint256 = self.getOutputPrice(as_unitless_number(eth_bought), token_reserve, as_unitless_number(self.balance))
# tokens sold is always > 0
assert max_tokens >= tokens_sold
send(recipient, eth_bought)
assert self.token.transferFrom(buyer, self, tokens_sold)
log.EthPurchase(buyer, tokens_sold, eth_bought)
return tokens_sold
# @notice Convert Tokens to ETH.
# @dev User specifies maximum input and exact output.
# @param eth_bought Amount of ETH purchased.
# @param max_tokens Maximum Tokens sold.
# @param deadline Time after which this transaction can no longer be executed.
# @return Amount of Tokens sold.
@public
def tokenToEthSwapOutput(eth_bought: uint256(wei), max_tokens: uint256, deadline: timestamp) -> uint256:
return self.tokenToEthOutput(eth_bought, max_tokens, deadline, msg.sender, msg.sender)
# @notice Convert Tokens to ETH and transfers ETH to recipient.
# @dev User specifies maximum input and exact output.
# @param eth_bought Amount of ETH purchased.
# @param max_tokens Maximum Tokens sold.
# @param deadline Time after which this transaction can no longer be executed.
# @param recipient The address that receives output ETH.
# @return Amount of Tokens sold.
@public
def tokenToEthTransferOutput(eth_bought: uint256(wei), max_tokens: uint256, deadline: timestamp, recipient: address) -> uint256:
assert recipient != self and recipient != ZERO_ADDRESS
return self.tokenToEthOutput(eth_bought, max_tokens, deadline, msg.sender, recipient)
@private
def tokenToTokenInput(tokens_sold: uint256, min_tokens_bought: uint256, min_eth_bought: uint256(wei), deadline: timestamp, buyer: address, recipient: address, exchange_addr: address) -> uint256:
assert (deadline >= block.timestamp and tokens_sold > 0) and (min_tokens_bought > 0 and min_eth_bought > 0)
assert exchange_addr != self and exchange_addr != ZERO_ADDRESS
token_reserve: uint256 = self.token.balanceOf(self)
eth_bought: uint256 = self.getInputPrice(tokens_sold, token_reserve, as_unitless_number(self.balance))
wei_bought: uint256(wei) = as_wei_value(eth_bought, 'wei')
assert wei_bought >= min_eth_bought
assert self.token.transferFrom(buyer, self, tokens_sold)
tokens_bought: uint256 = Exchange(exchange_addr).ethToTokenTransferInput(min_tokens_bought, deadline, recipient, value=wei_bought)
log.EthPurchase(buyer, tokens_sold, wei_bought)
return tokens_bought
# @notice Convert Tokens (self.token) to Tokens (token_addr).
# @dev User specifies exact input and minimum output.
# @param tokens_sold Amount of Tokens sold.
# @param min_tokens_bought Minimum Tokens (token_addr) purchased.
# @param min_eth_bought Minimum ETH purchased as intermediary.
# @param deadline Time after which this transaction can no longer be executed.
# @param token_addr The address of the token being purchased.
# @return Amount of Tokens (token_addr) bought.
@public
def tokenToTokenSwapInput(tokens_sold: uint256, min_tokens_bought: uint256, min_eth_bought: uint256(wei), deadline: timestamp, token_addr: address) -> uint256:
exchange_addr: address = self.factory.getExchange(token_addr)
return self.tokenToTokenInput(tokens_sold, min_tokens_bought, min_eth_bought, deadline, msg.sender, msg.sender, exchange_addr)
# @notice Convert Tokens (self.token) to Tokens (token_addr) and transfers
# Tokens (token_addr) to recipient.
# @dev User specifies exact input and minimum output.
# @param tokens_sold Amount of Tokens sold.
# @param min_tokens_bought Minimum Tokens (token_addr) purchased.
# @param min_eth_bought Minimum ETH purchased as intermediary.
# @param deadline Time after which this transaction can no longer be executed.
# @param recipient The address that receives output ETH.
# @param token_addr The address of the token being purchased.
# @return Amount of Tokens (token_addr) bought.
@public
def tokenToTokenTransferInput(tokens_sold: uint256, min_tokens_bought: uint256, min_eth_bought: uint256(wei), deadline: timestamp, recipient: address, token_addr: address) -> uint256:
exchange_addr: address = self.factory.getExchange(token_addr)
return self.tokenToTokenInput(tokens_sold, min_tokens_bought, min_eth_bought, deadline, msg.sender, recipient, exchange_addr)
@private
def tokenToTokenOutput(tokens_bought: uint256, max_tokens_sold: uint256, max_eth_sold: uint256(wei), deadline: timestamp, buyer: address, recipient: address, exchange_addr: address) -> uint256:
assert deadline >= block.timestamp and (tokens_bought > 0 and max_eth_sold > 0)
assert exchange_addr != self and exchange_addr != ZERO_ADDRESS
eth_bought: uint256(wei) = Exchange(exchange_addr).getEthToTokenOutputPrice(tokens_bought)
token_reserve: uint256 = self.token.balanceOf(self)
tokens_sold: uint256 = self.getOutputPrice(as_unitless_number(eth_bought), token_reserve, as_unitless_number(self.balance))
# tokens sold is always > 0
assert max_tokens_sold >= tokens_sold and max_eth_sold >= eth_bought
assert self.token.transferFrom(buyer, self, tokens_sold)
eth_sold: uint256(wei) = Exchange(exchange_addr).ethToTokenTransferOutput(tokens_bought, deadline, recipient, value=eth_bought)
log.EthPurchase(buyer, tokens_sold, eth_bought)
return tokens_sold
# @notice Convert Tokens (self.token) to Tokens (token_addr).
# @dev User specifies maximum input and exact output.
# @param tokens_bought Amount of Tokens (token_addr) bought.
# @param max_tokens_sold Maximum Tokens (self.token) sold.
# @param max_eth_sold Maximum ETH purchased as intermediary.
# @param deadline Time after which this transaction can no longer be executed.
# @param token_addr The address of the token being purchased.
# @return Amount of Tokens (self.token) sold.
@public
def tokenToTokenSwapOutput(tokens_bought: uint256, max_tokens_sold: uint256, max_eth_sold: uint256(wei), deadline: timestamp, token_addr: address) -> uint256:
exchange_addr: address = self.factory.getExchange(token_addr)
return self.tokenToTokenOutput(tokens_bought, max_tokens_sold, max_eth_sold, deadline, msg.sender, msg.sender, exchange_addr)
# @notice Convert Tokens (self.token) to Tokens (token_addr) and transfers
# Tokens (token_addr) to recipient.
# @dev User specifies maximum input and exact output.
# @param tokens_bought Amount of Tokens (token_addr) bought.
# @param max_tokens_sold Maximum Tokens (self.token) sold.
# @param max_eth_sold Maximum ETH purchased as intermediary.
# @param deadline Time after which this transaction can no longer be executed.
# @param recipient The address that receives output ETH.
# @param token_addr The address of the token being purchased.
# @return Amount of Tokens (self.token) sold.
@public
def tokenToTokenTransferOutput(tokens_bought: uint256, max_tokens_sold: uint256, max_eth_sold: uint256(wei), deadline: timestamp, recipient: address, token_addr: address) -> uint256:
exchange_addr: address = self.factory.getExchange(token_addr)
return self.tokenToTokenOutput(tokens_bought, max_tokens_sold, max_eth_sold, deadline, msg.sender, recipient, exchange_addr)
# @notice Convert Tokens (self.token) to Tokens (exchange_addr.token).
# @dev Allows trades through contracts that were not deployed from the same factory.
# @dev User specifies exact input and minimum output.
# @param tokens_sold Amount of Tokens sold.
# @param min_tokens_bought Minimum Tokens (token_addr) purchased.
# @param min_eth_bought Minimum ETH purchased as intermediary.
# @param deadline Time after which this transaction can no longer be executed.
# @param exchange_addr The address of the exchange for the token being purchased.
# @return Amount of Tokens (exchange_addr.token) bought.
@public
def tokenToExchangeSwapInput(tokens_sold: uint256, min_tokens_bought: uint256, min_eth_bought: uint256(wei), deadline: timestamp, exchange_addr: address) -> uint256:
return self.tokenToTokenInput(tokens_sold, min_tokens_bought, min_eth_bought, deadline, msg.sender, msg.sender, exchange_addr)
# @notice Convert Tokens (self.token) to Tokens (exchange_addr.token) and transfers
# Tokens (exchange_addr.token) to recipient.
# @dev Allows trades through contracts that were not deployed from the same factory.
# @dev User specifies exact input and minimum output.
# @param tokens_sold Amount of Tokens sold.
# @param min_tokens_bought Minimum Tokens (token_addr) purchased.
# @param min_eth_bought Minimum ETH purchased as intermediary.
# @param deadline Time after which this transaction can no longer be executed.
# @param recipient The address that receives output ETH.
# @param exchange_addr The address of the exchange for the token being purchased.
# @return Amount of Tokens (exchange_addr.token) bought.
@public
def tokenToExchangeTransferInput(tokens_sold: uint256, min_tokens_bought: uint256, min_eth_bought: uint256(wei), deadline: timestamp, recipient: address, exchange_addr: address) -> uint256:
assert recipient != self
return self.tokenToTokenInput(tokens_sold, min_tokens_bought, min_eth_bought, deadline, msg.sender, recipient, exchange_addr)
# @notice Convert Tokens (self.token) to Tokens (exchange_addr.token).
# @dev Allows trades through contracts that were not deployed from the same factory.
# @dev User specifies maximum input and exact output.
# @param tokens_bought Amount of Tokens (token_addr) bought.
# @param max_tokens_sold Maximum Tokens (self.token) sold.
# @param max_eth_sold Maximum ETH purchased as intermediary.
# @param deadline Time after which this transaction can no longer be executed.
# @param exchange_addr The address of the exchange for the token being purchased.
# @return Amount of Tokens (self.token) sold.
@public
def tokenToExchangeSwapOutput(tokens_bought: uint256, max_tokens_sold: uint256, max_eth_sold: uint256(wei), deadline: timestamp, exchange_addr: address) -> uint256:
return self.tokenToTokenOutput(tokens_bought, max_tokens_sold, max_eth_sold, deadline, msg.sender, msg.sender, exchange_addr)
# @notice Convert Tokens (self.token) to Tokens (exchange_addr.token) and transfers
# Tokens (exchange_addr.token) to recipient.
# @dev Allows trades through contracts that were not deployed from the same factory.
# @dev User specifies maximum input and exact output.
# @param tokens_bought Amount of Tokens (token_addr) bought.
# @param max_tokens_sold Maximum Tokens (self.token) sold.
# @param max_eth_sold Maximum ETH purchased as intermediary.
# @param deadline Time after which this transaction can no longer be executed.
# @param recipient The address that receives output ETH.
# @param token_addr The address of the token being purchased.
# @return Amount of Tokens (self.token) sold.
@public
def tokenToExchangeTransferOutput(tokens_bought: uint256, max_tokens_sold: uint256, max_eth_sold: uint256(wei), deadline: timestamp, recipient: address, exchange_addr: address) -> uint256:
assert recipient != self
return self.tokenToTokenOutput(tokens_bought, max_tokens_sold, max_eth_sold, deadline, msg.sender, recipient, exchange_addr)
# @notice Public price function for ETH to Token trades with an exact input.
# @param eth_sold Amount of ETH sold.
# @return Amount of Tokens that can be bought with input ETH.
@public
@constant
def getEthToTokenInputPrice(eth_sold: uint256(wei)) -> uint256:
assert eth_sold > 0
token_reserve: uint256 = self.token.balanceOf(self)
return self.getInputPrice(as_unitless_number(eth_sold), as_unitless_number(self.balance), token_reserve)
# @notice Public price function for ETH to Token trades with an exact output.
# @param tokens_bought Amount of Tokens bought.
# @return Amount of ETH needed to buy output Tokens.
@public
@constant
def getEthToTokenOutputPrice(tokens_bought: uint256) -> uint256(wei):
assert tokens_bought > 0
token_reserve: uint256 = self.token.balanceOf(self)
eth_sold: uint256 = self.getOutputPrice(tokens_bought, as_unitless_number(self.balance), token_reserve)
return as_wei_value(eth_sold, 'wei')
# @notice Public price function for Token to ETH trades with an exact input.
# @param tokens_sold Amount of Tokens sold.
# @return Amount of ETH that can be bought with input Tokens.
@public
@constant
def getTokenToEthInputPrice(tokens_sold: uint256) -> uint256(wei):
assert tokens_sold > 0
token_reserve: uint256 = self.token.balanceOf(self)
eth_bought: uint256 = self.getInputPrice(tokens_sold, token_reserve, as_unitless_number(self.balance))
return as_wei_value(eth_bought, 'wei')
# @notice Public price function for Token to ETH trades with an exact output.
# @param eth_bought Amount of output ETH.
# @return Amount of Tokens needed to buy output ETH.
@public
@constant
def getTokenToEthOutputPrice(eth_bought: uint256(wei)) -> uint256:
assert eth_bought > 0
token_reserve: uint256 = self.token.balanceOf(self)
return self.getOutputPrice(as_unitless_number(eth_bought), token_reserve, as_unitless_number(self.balance))
# @return Address of Token that is sold on this exchange.
@public
@constant
def tokenAddress() -> address:
return self.token
# @return Address of factory that created this exchange.
@public
@constant
def factoryAddress() -> address(Factory):
return self.factory
# ERC20 compatibility for exchange liquidity modified from
# https://github.com/ethereum/vyper/blob/master/examples/tokens/ERC20.vy
@public
@constant
def balanceOf(_owner : address) -> uint256:
return self.balances[_owner]
@public
def transfer(_to : address, _value : uint256) -> bool:
self.balances[msg.sender] -= _value
self.balances[_to] += _value
log.Transfer(msg.sender, _to, _value)
return True
@public
def transferFrom(_from : address, _to : address, _value : uint256) -> bool:
self.balances[_from] -= _value
self.balances[_to] += _value
self.allowances[_from][msg.sender] -= _value
log.Transfer(_from, _to, _value)
return True
@public
def approve(_spender : address, _value : uint256) -> bool:
self.allowances[msg.sender][_spender] = _value
log.Approval(msg.sender, _spender, _value)
return True
@public
@constant
def allowance(_owner : address, _spender : address) -> uint256:
return self.allowances[_owner][_spender]File 3 of 5: DSToken
pragma solidity ^0.4.13;
////// lib/ds-math/src/math.sol
/// math.sol -- mixin for inline numerical wizardry
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/* pragma solidity ^0.4.13; */
contract DSMath {
function add(uint x, uint y) internal pure returns (uint z) {
require((z = x + y) >= x);
}
function sub(uint x, uint y) internal pure returns (uint z) {
require((z = x - y) <= x);
}
function mul(uint x, uint y) internal pure returns (uint z) {
require(y == 0 || (z = x * y) / y == x);
}
function min(uint x, uint y) internal pure returns (uint z) {
return x <= y ? x : y;
}
function max(uint x, uint y) internal pure returns (uint z) {
return x >= y ? x : y;
}
function imin(int x, int y) internal pure returns (int z) {
return x <= y ? x : y;
}
function imax(int x, int y) internal pure returns (int z) {
return x >= y ? x : y;
}
uint constant WAD = 10 ** 18;
uint constant RAY = 10 ** 27;
function wmul(uint x, uint y) internal pure returns (uint z) {
z = add(mul(x, y), WAD / 2) / WAD;
}
function rmul(uint x, uint y) internal pure returns (uint z) {
z = add(mul(x, y), RAY / 2) / RAY;
}
function wdiv(uint x, uint y) internal pure returns (uint z) {
z = add(mul(x, WAD), y / 2) / y;
}
function rdiv(uint x, uint y) internal pure returns (uint z) {
z = add(mul(x, RAY), y / 2) / y;
}
// This famous algorithm is called "exponentiation by squaring"
// and calculates x^n with x as fixed-point and n as regular unsigned.
//
// It's O(log n), instead of O(n) for naive repeated multiplication.
//
// These facts are why it works:
//
// If n is even, then x^n = (x^2)^(n/2).
// If n is odd, then x^n = x * x^(n-1),
// and applying the equation for even x gives
// x^n = x * (x^2)^((n-1) / 2).
//
// Also, EVM division is flooring and
// floor[(n-1) / 2] = floor[n / 2].
//
function rpow(uint x, uint n) internal pure returns (uint z) {
z = n % 2 != 0 ? x : RAY;
for (n /= 2; n != 0; n /= 2) {
x = rmul(x, x);
if (n % 2 != 0) {
z = rmul(z, x);
}
}
}
}
////// lib/ds-stop/lib/ds-auth/src/auth.sol
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/* pragma solidity ^0.4.13; */
contract DSAuthority {
function canCall(
address src, address dst, bytes4 sig
) public view returns (bool);
}
contract DSAuthEvents {
event LogSetAuthority (address indexed authority);
event LogSetOwner (address indexed owner);
}
contract DSAuth is DSAuthEvents {
DSAuthority public authority;
address public owner;
function DSAuth() public {
owner = msg.sender;
LogSetOwner(msg.sender);
}
function setOwner(address owner_)
public
auth
{
owner = owner_;
LogSetOwner(owner);
}
function setAuthority(DSAuthority authority_)
public
auth
{
authority = authority_;
LogSetAuthority(authority);
}
modifier auth {
require(isAuthorized(msg.sender, msg.sig));
_;
}
function isAuthorized(address src, bytes4 sig) internal view returns (bool) {
if (src == address(this)) {
return true;
} else if (src == owner) {
return true;
} else if (authority == DSAuthority(0)) {
return false;
} else {
return authority.canCall(src, this, sig);
}
}
}
////// lib/ds-stop/lib/ds-note/src/note.sol
/// note.sol -- the `note' modifier, for logging calls as events
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/* pragma solidity ^0.4.13; */
contract DSNote {
event LogNote(
bytes4 indexed sig,
address indexed guy,
bytes32 indexed foo,
bytes32 indexed bar,
uint wad,
bytes fax
) anonymous;
modifier note {
bytes32 foo;
bytes32 bar;
assembly {
foo := calldataload(4)
bar := calldataload(36)
}
LogNote(msg.sig, msg.sender, foo, bar, msg.value, msg.data);
_;
}
}
////// lib/ds-stop/src/stop.sol
/// stop.sol -- mixin for enable/disable functionality
// Copyright (C) 2017 DappHub, LLC
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/* pragma solidity ^0.4.13; */
/* import "ds-auth/auth.sol"; */
/* import "ds-note/note.sol"; */
contract DSStop is DSNote, DSAuth {
bool public stopped;
modifier stoppable {
require(!stopped);
_;
}
function stop() public auth note {
stopped = true;
}
function start() public auth note {
stopped = false;
}
}
////// lib/erc20/src/erc20.sol
/// erc20.sol -- API for the ERC20 token standard
// See <https://github.com/ethereum/EIPs/issues/20>.
// This file likely does not meet the threshold of originality
// required for copyright to apply. As a result, this is free and
// unencumbered software belonging to the public domain.
/* pragma solidity ^0.4.8; */
contract ERC20Events {
event Approval(address indexed src, address indexed guy, uint wad);
event Transfer(address indexed src, address indexed dst, uint wad);
}
contract ERC20 is ERC20Events {
function totalSupply() public view returns (uint);
function balanceOf(address guy) public view returns (uint);
function allowance(address src, address guy) public view returns (uint);
function approve(address guy, uint wad) public returns (bool);
function transfer(address dst, uint wad) public returns (bool);
function transferFrom(
address src, address dst, uint wad
) public returns (bool);
}
////// src/base.sol
/// base.sol -- basic ERC20 implementation
// Copyright (C) 2015, 2016, 2017 DappHub, LLC
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/* pragma solidity ^0.4.13; */
/* import "erc20/erc20.sol"; */
/* import "ds-math/math.sol"; */
contract DSTokenBase is ERC20, DSMath {
uint256 _supply;
mapping (address => uint256) _balances;
mapping (address => mapping (address => uint256)) _approvals;
function DSTokenBase(uint supply) public {
_balances[msg.sender] = supply;
_supply = supply;
}
function totalSupply() public view returns (uint) {
return _supply;
}
function balanceOf(address src) public view returns (uint) {
return _balances[src];
}
function allowance(address src, address guy) public view returns (uint) {
return _approvals[src][guy];
}
function transfer(address dst, uint wad) public returns (bool) {
return transferFrom(msg.sender, dst, wad);
}
function transferFrom(address src, address dst, uint wad)
public
returns (bool)
{
if (src != msg.sender) {
_approvals[src][msg.sender] = sub(_approvals[src][msg.sender], wad);
}
_balances[src] = sub(_balances[src], wad);
_balances[dst] = add(_balances[dst], wad);
Transfer(src, dst, wad);
return true;
}
function approve(address guy, uint wad) public returns (bool) {
_approvals[msg.sender][guy] = wad;
Approval(msg.sender, guy, wad);
return true;
}
}
////// src/token.sol
/// token.sol -- ERC20 implementation with minting and burning
// Copyright (C) 2015, 2016, 2017 DappHub, LLC
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/* pragma solidity ^0.4.13; */
/* import "ds-stop/stop.sol"; */
/* import "./base.sol"; */
contract DSToken is DSTokenBase(0), DSStop {
bytes32 public symbol;
uint256 public decimals = 18; // standard token precision. override to customize
function DSToken(bytes32 symbol_) public {
symbol = symbol_;
}
event Mint(address indexed guy, uint wad);
event Burn(address indexed guy, uint wad);
function approve(address guy) public stoppable returns (bool) {
return super.approve(guy, uint(-1));
}
function approve(address guy, uint wad) public stoppable returns (bool) {
return super.approve(guy, wad);
}
function transferFrom(address src, address dst, uint wad)
public
stoppable
returns (bool)
{
if (src != msg.sender && _approvals[src][msg.sender] != uint(-1)) {
_approvals[src][msg.sender] = sub(_approvals[src][msg.sender], wad);
}
_balances[src] = sub(_balances[src], wad);
_balances[dst] = add(_balances[dst], wad);
Transfer(src, dst, wad);
return true;
}
function push(address dst, uint wad) public {
transferFrom(msg.sender, dst, wad);
}
function pull(address src, uint wad) public {
transferFrom(src, msg.sender, wad);
}
function move(address src, address dst, uint wad) public {
transferFrom(src, dst, wad);
}
function mint(uint wad) public {
mint(msg.sender, wad);
}
function burn(uint wad) public {
burn(msg.sender, wad);
}
function mint(address guy, uint wad) public auth stoppable {
_balances[guy] = add(_balances[guy], wad);
_supply = add(_supply, wad);
Mint(guy, wad);
}
function burn(address guy, uint wad) public auth stoppable {
if (guy != msg.sender && _approvals[guy][msg.sender] != uint(-1)) {
_approvals[guy][msg.sender] = sub(_approvals[guy][msg.sender], wad);
}
_balances[guy] = sub(_balances[guy], wad);
_supply = sub(_supply, wad);
Burn(guy, wad);
}
// Optional token name
bytes32 public name = "";
function setName(bytes32 name_) public auth {
name = name_;
}
}File 4 of 5: KyberOasisReserve
pragma solidity 0.4.18;
// File: /home/yaron/kyber/oasis_set_otc/smart-contracts/contracts/ERC20Interface.sol
// https://github.com/ethereum/EIPs/issues/20
interface ERC20 {
function totalSupply() public view returns (uint supply);
function balanceOf(address _owner) public view returns (uint balance);
function transfer(address _to, uint _value) public returns (bool success);
function transferFrom(address _from, address _to, uint _value) public returns (bool success);
function approve(address _spender, uint _value) public returns (bool success);
function allowance(address _owner, address _spender) public view returns (uint remaining);
function decimals() public view returns(uint digits);
event Approval(address indexed _owner, address indexed _spender, uint _value);
}
// File: /home/yaron/kyber/oasis_set_otc/smart-contracts/contracts/Utils.sol
/// @title Kyber constants contract
contract Utils {
ERC20 constant internal ETH_TOKEN_ADDRESS = ERC20(0x00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee);
uint constant internal PRECISION = (10**18);
uint constant internal MAX_QTY = (10**28); // 10B tokens
uint constant internal MAX_RATE = (PRECISION * 10**6); // up to 1M tokens per ETH
uint constant internal MAX_DECIMALS = 18;
uint constant internal ETH_DECIMALS = 18;
mapping(address=>uint) internal decimals;
function setDecimals(ERC20 token) internal {
if (token == ETH_TOKEN_ADDRESS) decimals[token] = ETH_DECIMALS;
else decimals[token] = token.decimals();
}
function getDecimals(ERC20 token) internal view returns(uint) {
if (token == ETH_TOKEN_ADDRESS) return ETH_DECIMALS; // save storage access
uint tokenDecimals = decimals[token];
// technically, there might be token with decimals 0
// moreover, very possible that old tokens have decimals 0
// these tokens will just have higher gas fees.
if(tokenDecimals == 0) return token.decimals();
return tokenDecimals;
}
function calcDstQty(uint srcQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {
require(srcQty <= MAX_QTY);
require(rate <= MAX_RATE);
if (dstDecimals >= srcDecimals) {
require((dstDecimals - srcDecimals) <= MAX_DECIMALS);
return (srcQty * rate * (10**(dstDecimals - srcDecimals))) / PRECISION;
} else {
require((srcDecimals - dstDecimals) <= MAX_DECIMALS);
return (srcQty * rate) / (PRECISION * (10**(srcDecimals - dstDecimals)));
}
}
function calcSrcQty(uint dstQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {
require(dstQty <= MAX_QTY);
require(rate <= MAX_RATE);
//source quantity is rounded up. to avoid dest quantity being too low.
uint numerator;
uint denominator;
if (srcDecimals >= dstDecimals) {
require((srcDecimals - dstDecimals) <= MAX_DECIMALS);
numerator = (PRECISION * dstQty * (10**(srcDecimals - dstDecimals)));
denominator = rate;
} else {
require((dstDecimals - srcDecimals) <= MAX_DECIMALS);
numerator = (PRECISION * dstQty);
denominator = (rate * (10**(dstDecimals - srcDecimals)));
}
return (numerator + denominator - 1) / denominator; //avoid rounding down errors
}
}
// File: /home/yaron/kyber/oasis_set_otc/smart-contracts/contracts/Utils2.sol
contract Utils2 is Utils {
/// @dev get the balance of a user.
/// @param token The token type
/// @return The balance
function getBalance(ERC20 token, address user) public view returns(uint) {
if (token == ETH_TOKEN_ADDRESS)
return user.balance;
else
return token.balanceOf(user);
}
function getDecimalsSafe(ERC20 token) internal returns(uint) {
if (decimals[token] == 0) {
setDecimals(token);
}
return decimals[token];
}
function calcDestAmount(ERC20 src, ERC20 dest, uint srcAmount, uint rate) internal view returns(uint) {
return calcDstQty(srcAmount, getDecimals(src), getDecimals(dest), rate);
}
function calcSrcAmount(ERC20 src, ERC20 dest, uint destAmount, uint rate) internal view returns(uint) {
return calcSrcQty(destAmount, getDecimals(src), getDecimals(dest), rate);
}
function calcRateFromQty(uint srcAmount, uint destAmount, uint srcDecimals, uint dstDecimals)
internal pure returns(uint)
{
require(srcAmount <= MAX_QTY);
require(destAmount <= MAX_QTY);
if (dstDecimals >= srcDecimals) {
require((dstDecimals - srcDecimals) <= MAX_DECIMALS);
return (destAmount * PRECISION / ((10 ** (dstDecimals - srcDecimals)) * srcAmount));
} else {
require((srcDecimals - dstDecimals) <= MAX_DECIMALS);
return (destAmount * PRECISION * (10 ** (srcDecimals - dstDecimals)) / srcAmount);
}
}
}
// File: /home/yaron/kyber/oasis_set_otc/smart-contracts/contracts/PermissionGroups.sol
contract PermissionGroups {
address public admin;
address public pendingAdmin;
mapping(address=>bool) internal operators;
mapping(address=>bool) internal alerters;
address[] internal operatorsGroup;
address[] internal alertersGroup;
uint constant internal MAX_GROUP_SIZE = 50;
function PermissionGroups() public {
admin = msg.sender;
}
modifier onlyAdmin() {
require(msg.sender == admin);
_;
}
modifier onlyOperator() {
require(operators[msg.sender]);
_;
}
modifier onlyAlerter() {
require(alerters[msg.sender]);
_;
}
function getOperators () external view returns(address[]) {
return operatorsGroup;
}
function getAlerters () external view returns(address[]) {
return alertersGroup;
}
event TransferAdminPending(address pendingAdmin);
/**
* @dev Allows the current admin to set the pendingAdmin address.
* @param newAdmin The address to transfer ownership to.
*/
function transferAdmin(address newAdmin) public onlyAdmin {
require(newAdmin != address(0));
TransferAdminPending(pendingAdmin);
pendingAdmin = newAdmin;
}
/**
* @dev Allows the current admin to set the admin in one tx. Useful initial deployment.
* @param newAdmin The address to transfer ownership to.
*/
function transferAdminQuickly(address newAdmin) public onlyAdmin {
require(newAdmin != address(0));
TransferAdminPending(newAdmin);
AdminClaimed(newAdmin, admin);
admin = newAdmin;
}
event AdminClaimed( address newAdmin, address previousAdmin);
/**
* @dev Allows the pendingAdmin address to finalize the change admin process.
*/
function claimAdmin() public {
require(pendingAdmin == msg.sender);
AdminClaimed(pendingAdmin, admin);
admin = pendingAdmin;
pendingAdmin = address(0);
}
event AlerterAdded (address newAlerter, bool isAdd);
function addAlerter(address newAlerter) public onlyAdmin {
require(!alerters[newAlerter]); // prevent duplicates.
require(alertersGroup.length < MAX_GROUP_SIZE);
AlerterAdded(newAlerter, true);
alerters[newAlerter] = true;
alertersGroup.push(newAlerter);
}
function removeAlerter (address alerter) public onlyAdmin {
require(alerters[alerter]);
alerters[alerter] = false;
for (uint i = 0; i < alertersGroup.length; ++i) {
if (alertersGroup[i] == alerter) {
alertersGroup[i] = alertersGroup[alertersGroup.length - 1];
alertersGroup.length--;
AlerterAdded(alerter, false);
break;
}
}
}
event OperatorAdded(address newOperator, bool isAdd);
function addOperator(address newOperator) public onlyAdmin {
require(!operators[newOperator]); // prevent duplicates.
require(operatorsGroup.length < MAX_GROUP_SIZE);
OperatorAdded(newOperator, true);
operators[newOperator] = true;
operatorsGroup.push(newOperator);
}
function removeOperator (address operator) public onlyAdmin {
require(operators[operator]);
operators[operator] = false;
for (uint i = 0; i < operatorsGroup.length; ++i) {
if (operatorsGroup[i] == operator) {
operatorsGroup[i] = operatorsGroup[operatorsGroup.length - 1];
operatorsGroup.length -= 1;
OperatorAdded(operator, false);
break;
}
}
}
}
// File: /home/yaron/kyber/oasis_set_otc/smart-contracts/contracts/Withdrawable.sol
/**
* @title Contracts that should be able to recover tokens or ethers
* @author Ilan Doron
* @dev This allows to recover any tokens or Ethers received in a contract.
* This will prevent any accidental loss of tokens.
*/
contract Withdrawable is PermissionGroups {
event TokenWithdraw(ERC20 token, uint amount, address sendTo);
/**
* @dev Withdraw all ERC20 compatible tokens
* @param token ERC20 The address of the token contract
*/
function withdrawToken(ERC20 token, uint amount, address sendTo) external onlyAdmin {
require(token.transfer(sendTo, amount));
TokenWithdraw(token, amount, sendTo);
}
event EtherWithdraw(uint amount, address sendTo);
/**
* @dev Withdraw Ethers
*/
function withdrawEther(uint amount, address sendTo) external onlyAdmin {
sendTo.transfer(amount);
EtherWithdraw(amount, sendTo);
}
}
// File: /home/yaron/kyber/oasis_set_otc/smart-contracts/contracts/KyberReserveInterface.sol
/// @title Kyber Reserve contract
interface KyberReserveInterface {
function trade(
ERC20 srcToken,
uint srcAmount,
ERC20 destToken,
address destAddress,
uint conversionRate,
bool validate
)
public
payable
returns(bool);
function getConversionRate(ERC20 src, ERC20 dest, uint srcQty, uint blockNumber) public view returns(uint);
}
// File: contracts/oasisContracts/KyberOasisReserve.sol
contract OtcInterface {
function getOffer(uint id) public constant returns (uint, ERC20, uint, ERC20);
function getBestOffer(ERC20 sellGem, ERC20 buyGem) public constant returns(uint);
function getWorseOffer(uint id) public constant returns(uint);
function take(bytes32 id, uint128 maxTakeAmount) public;
}
contract WethInterface is ERC20 {
function deposit() public payable;
function withdraw(uint) public;
}
contract KyberOasisReserve is KyberReserveInterface, Withdrawable, Utils2 {
uint constant internal COMMON_DECIMALS = 18;
address public sanityRatesContract = 0;
address public kyberNetwork;
OtcInterface public otc;
WethInterface public wethToken;
mapping(address=>bool) public isTokenListed;
mapping(address=>uint) public tokenMinSrcAmount;
mapping(address=>uint) public minTokenBalance;
mapping(address=>uint) public maxTokenBalance;
mapping(address=>uint) public internalPricePremiumBps;
mapping(address=>uint) public minOasisSpreadForinternalPricingBps;
bool public tradeEnabled;
uint public feeBps;
function KyberOasisReserve(
address _kyberNetwork,
OtcInterface _otc,
WethInterface _wethToken,
address _admin,
uint _feeBps
)
public
{
require(_admin != address(0));
require(_kyberNetwork != address(0));
require(_otc != address(0));
require(_wethToken != address(0));
require(_feeBps < 10000);
require(getDecimals(_wethToken) == COMMON_DECIMALS);
kyberNetwork = _kyberNetwork;
otc = _otc;
wethToken = _wethToken;
admin = _admin;
feeBps = _feeBps;
tradeEnabled = true;
require(wethToken.approve(otc, 2**255));
}
function() public payable {
// anyone can deposit ether
}
function listToken(ERC20 token, uint minSrcAmount) public onlyAdmin {
require(token != address(0));
require(!isTokenListed[token]);
require(getDecimals(token) == COMMON_DECIMALS);
require(token.approve(otc, 2**255));
isTokenListed[token] = true;
tokenMinSrcAmount[token] = minSrcAmount;
minTokenBalance[token] = 2 ** 255; // disable by default
maxTokenBalance[token] = 0; // disable by default;
internalPricePremiumBps[token] = 0; // NA by default
minOasisSpreadForinternalPricingBps[token] = 0; // NA by default
}
function delistToken(ERC20 token) public onlyAdmin {
require(isTokenListed[token]);
require(token.approve(otc, 0));
delete isTokenListed[token];
delete tokenMinSrcAmount[token];
delete minTokenBalance[token];
delete maxTokenBalance[token];
delete internalPricePremiumBps[token];
delete minOasisSpreadForinternalPricingBps[token];
}
function setInternalPriceAdminParams(ERC20 token,
uint minSpreadBps,
uint premiumBps) public onlyAdmin {
require(isTokenListed[token]);
require(premiumBps <= 500); // premium <= 5%
require(minSpreadBps <= 1000); // min spread <= 10%
internalPricePremiumBps[token] = premiumBps;
minOasisSpreadForinternalPricingBps[token] = minSpreadBps;
}
function setInternalInventoryMinMax(ERC20 token,
uint minBalance,
uint maxBalance) public onlyOperator {
require(isTokenListed[token]);
// don't require anything on min and max balance as it might be used for disable
minTokenBalance[token] = minBalance;
maxTokenBalance[token] = maxBalance;
}
event TradeExecute(
address indexed sender,
address src,
uint srcAmount,
address destToken,
uint destAmount,
address destAddress
);
function trade(
ERC20 srcToken,
uint srcAmount,
ERC20 destToken,
address destAddress,
uint conversionRate,
bool validate
)
public
payable
returns(bool)
{
require(tradeEnabled);
require(msg.sender == kyberNetwork);
require(doTrade(srcToken, srcAmount, destToken, destAddress, conversionRate, validate));
return true;
}
event TradeEnabled(bool enable);
function enableTrade() public onlyAdmin returns(bool) {
tradeEnabled = true;
TradeEnabled(true);
return true;
}
function disableTrade() public onlyAlerter returns(bool) {
tradeEnabled = false;
TradeEnabled(false);
return true;
}
event KyberNetworkSet(address kyberNetwork);
function setKyberNetwork(address _kyberNetwork) public onlyAdmin {
require(_kyberNetwork != address(0));
kyberNetwork = _kyberNetwork;
KyberNetworkSet(kyberNetwork);
}
event FeeBpsSet(uint feeBps);
function setFeeBps(uint _feeBps) public onlyAdmin {
require(_feeBps < 10000);
feeBps = _feeBps;
FeeBpsSet(feeBps);
}
function valueAfterReducingFee(uint val) public view returns(uint) {
require(val <= MAX_QTY);
return ((10000 - feeBps) * val) / 10000;
}
function valueBeforeFeesWereReduced(uint val) public view returns(uint) {
require(val <= MAX_QTY);
return val * 10000 / (10000 - feeBps);
}
function valueAfterAddingPremium(ERC20 token, uint val) public view returns(uint) {
require(val <= MAX_QTY);
uint premium = internalPricePremiumBps[token];
return val * (10000 + premium) / 10000;
}
function shouldUseInternalInventory(ERC20 token,
uint tokenVal,
uint ethVal,
bool ethToToken) public view returns(bool) {
require(tokenVal <= MAX_QTY);
uint tokenBalance = token.balanceOf(this);
if (ethToToken) {
if (tokenBalance < tokenVal) return false;
if (tokenBalance - tokenVal < minTokenBalance[token]) return false;
}
else {
if (this.balance < ethVal) return false;
if (tokenBalance + tokenVal > maxTokenBalance[token]) return false;
}
// check that spread in first level makes sense
uint x1; uint y1; uint x2; uint y2;
(,x1,y1) = getMatchingOffer(token, wethToken, 0);
(,y2,x2) = getMatchingOffer(wethToken, token, 0);
require(x1 <= MAX_QTY && x2 <= MAX_QTY && y1 <= MAX_QTY && y2 <= MAX_QTY);
// check if there is an arbitrage
if (x1*y2 > x2*y1) return false;
// spread is (x1/y1 - x2/y2) / (x1/y1)
if (10000 * (x2*y1 - x1*y2) < x1*y2*minOasisSpreadForinternalPricingBps[token]) return false;
return true;
}
function getConversionRate(ERC20 src, ERC20 dest, uint srcQty, uint blockNumber) public view returns(uint) {
uint rate;
uint actualSrcQty;
ERC20 actualSrc;
ERC20 actualDest;
uint offerPayAmt;
uint offerBuyAmt;
blockNumber;
if (!tradeEnabled) return 0;
if (!validTokens(src, dest)) return 0;
if (src == ETH_TOKEN_ADDRESS) {
actualSrc = wethToken;
actualDest = dest;
actualSrcQty = srcQty;
} else if (dest == ETH_TOKEN_ADDRESS) {
actualSrc = src;
actualDest = wethToken;
if (srcQty < tokenMinSrcAmount[src]) {
/* remove rounding errors and present rate for 0 src amount. */
actualSrcQty = tokenMinSrcAmount[src];
} else {
actualSrcQty = srcQty;
}
} else {
return 0;
}
// otc's terminology is of offer maker, so their sellGem is the taker's dest token.
(, offerPayAmt, offerBuyAmt) = getMatchingOffer(actualDest, actualSrc, actualSrcQty);
// make sure to take only one level of order book to avoid gas inflation.
if (actualSrcQty > offerBuyAmt) return 0;
bool tradeFromInventory = false;
uint valueWithPremium = valueAfterAddingPremium(token, offerPayAmt);
ERC20 token;
if (src == ETH_TOKEN_ADDRESS) {
token = dest;
tradeFromInventory = shouldUseInternalInventory(token,
valueWithPremium,
offerBuyAmt,
true);
}
else {
token = src;
tradeFromInventory = shouldUseInternalInventory(token,
offerBuyAmt,
valueWithPremium,
false);
}
rate = calcRateFromQty(offerBuyAmt, offerPayAmt, COMMON_DECIMALS, COMMON_DECIMALS);
if (tradeFromInventory) return valueAfterAddingPremium(token,rate);
else return valueAfterReducingFee(rate);
}
function doTrade(
ERC20 srcToken,
uint srcAmount,
ERC20 destToken,
address destAddress,
uint conversionRate,
bool validate
)
internal
returns(bool)
{
uint actualDestAmount;
require(validTokens(srcToken, destToken));
// can skip validation if done at kyber network level
if (validate) {
require(conversionRate > 0);
if (srcToken == ETH_TOKEN_ADDRESS)
require(msg.value == srcAmount);
else
require(msg.value == 0);
}
uint userExpectedDestAmount = calcDstQty(srcAmount, COMMON_DECIMALS, COMMON_DECIMALS, conversionRate);
require(userExpectedDestAmount > 0); // sanity check
uint destAmountIncludingFees = valueBeforeFeesWereReduced(userExpectedDestAmount);
if (srcToken == ETH_TOKEN_ADDRESS) {
if(!shouldUseInternalInventory(destToken,
userExpectedDestAmount,
0,
true)) {
wethToken.deposit.value(msg.value)();
actualDestAmount = takeMatchingOffer(wethToken, destToken, srcAmount);
require(actualDestAmount >= destAmountIncludingFees);
}
// transfer back only requested dest amount.
require(destToken.transfer(destAddress, userExpectedDestAmount));
} else {
require(srcToken.transferFrom(msg.sender, this, srcAmount));
if(!shouldUseInternalInventory(srcToken,
0,
userExpectedDestAmount,
false)) {
actualDestAmount = takeMatchingOffer(srcToken, wethToken, srcAmount);
require(actualDestAmount >= destAmountIncludingFees);
wethToken.withdraw(actualDestAmount);
}
// transfer back only requested dest amount.
destAddress.transfer(userExpectedDestAmount);
}
TradeExecute(msg.sender, srcToken, srcAmount, destToken, userExpectedDestAmount, destAddress);
return true;
}
function takeMatchingOffer(
ERC20 srcToken,
ERC20 destToken,
uint srcAmount
)
internal
returns(uint actualDestAmount)
{
uint offerId;
uint offerPayAmt;
uint offerBuyAmt;
// otc's terminology is of offer maker, so their sellGem is our (the taker's) dest token.
(offerId, offerPayAmt, offerBuyAmt) = getMatchingOffer(destToken, srcToken, srcAmount);
require(srcAmount <= MAX_QTY);
require(offerPayAmt <= MAX_QTY);
actualDestAmount = srcAmount * offerPayAmt / offerBuyAmt;
require(uint128(actualDestAmount) == actualDestAmount);
otc.take(bytes32(offerId), uint128(actualDestAmount)); // Take the portion of the offer that we need
return;
}
function getMatchingOffer(
ERC20 offerSellGem,
ERC20 offerBuyGem,
uint payAmount
)
internal
view
returns(
uint offerId,
uint offerPayAmount,
uint offerBuyAmount
)
{
offerId = otc.getBestOffer(offerSellGem, offerBuyGem);
(offerPayAmount, , offerBuyAmount, ) = otc.getOffer(offerId);
uint depth = 1;
while (payAmount > offerBuyAmount) {
offerId = otc.getWorseOffer(offerId); // We look for the next best offer
if (offerId == 0 || ++depth > 7) {
offerId = 0;
offerPayAmount = 0;
offerBuyAmount = 0;
break;
}
(offerPayAmount, , offerBuyAmount, ) = otc.getOffer(offerId);
}
return;
}
function validTokens(ERC20 src, ERC20 dest) internal view returns (bool valid) {
return ((isTokenListed[src] && ETH_TOKEN_ADDRESS == dest) ||
(isTokenListed[dest] && ETH_TOKEN_ADDRESS == src));
}
}File 5 of 5: MatchingMarket
/// matching_market.sol
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
pragma solidity ^0.4.18;
/// expiring_market.sol
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
pragma solidity ^0.4.18;
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
pragma solidity ^0.4.13;
contract DSAuthority {
function canCall(
address src, address dst, bytes4 sig
) public view returns (bool);
}
contract DSAuthEvents {
event LogSetAuthority (address indexed authority);
event LogSetOwner (address indexed owner);
}
contract DSAuth is DSAuthEvents {
DSAuthority public authority;
address public owner;
function DSAuth() public {
owner = msg.sender;
LogSetOwner(msg.sender);
}
function setOwner(address owner_)
public
auth
{
owner = owner_;
LogSetOwner(owner);
}
function setAuthority(DSAuthority authority_)
public
auth
{
authority = authority_;
LogSetAuthority(authority);
}
modifier auth {
require(isAuthorized(msg.sender, msg.sig));
_;
}
function isAuthorized(address src, bytes4 sig) internal view returns (bool) {
if (src == address(this)) {
return true;
} else if (src == owner) {
return true;
} else if (authority == DSAuthority(0)) {
return false;
} else {
return authority.canCall(src, this, sig);
}
}
}
/// simple_market.sol
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
pragma solidity ^0.4.18;
/// math.sol -- mixin for inline numerical wizardry
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
pragma solidity ^0.4.13;
contract DSMath {
function add(uint x, uint y) internal pure returns (uint z) {
require((z = x + y) >= x);
}
function sub(uint x, uint y) internal pure returns (uint z) {
require((z = x - y) <= x);
}
function mul(uint x, uint y) internal pure returns (uint z) {
require(y == 0 || (z = x * y) / y == x);
}
function min(uint x, uint y) internal pure returns (uint z) {
return x <= y ? x : y;
}
function max(uint x, uint y) internal pure returns (uint z) {
return x >= y ? x : y;
}
function imin(int x, int y) internal pure returns (int z) {
return x <= y ? x : y;
}
function imax(int x, int y) internal pure returns (int z) {
return x >= y ? x : y;
}
uint constant WAD = 10 ** 18;
uint constant RAY = 10 ** 27;
function wmul(uint x, uint y) internal pure returns (uint z) {
z = add(mul(x, y), WAD / 2) / WAD;
}
function rmul(uint x, uint y) internal pure returns (uint z) {
z = add(mul(x, y), RAY / 2) / RAY;
}
function wdiv(uint x, uint y) internal pure returns (uint z) {
z = add(mul(x, WAD), y / 2) / y;
}
function rdiv(uint x, uint y) internal pure returns (uint z) {
z = add(mul(x, RAY), y / 2) / y;
}
// This famous algorithm is called "exponentiation by squaring"
// and calculates x^n with x as fixed-point and n as regular unsigned.
//
// It's O(log n), instead of O(n) for naive repeated multiplication.
//
// These facts are why it works:
//
// If n is even, then x^n = (x^2)^(n/2).
// If n is odd, then x^n = x * x^(n-1),
// and applying the equation for even x gives
// x^n = x * (x^2)^((n-1) / 2).
//
// Also, EVM division is flooring and
// floor[(n-1) / 2] = floor[n / 2].
//
function rpow(uint x, uint n) internal pure returns (uint z) {
z = n % 2 != 0 ? x : RAY;
for (n /= 2; n != 0; n /= 2) {
x = rmul(x, x);
if (n % 2 != 0) {
z = rmul(z, x);
}
}
}
}
/// erc20.sol -- API for the ERC20 token standard
// See <https://github.com/ethereum/EIPs/issues/20>.
// This file likely does not meet the threshold of originality
// required for copyright to apply. As a result, this is free and
// unencumbered software belonging to the public domain.
pragma solidity ^0.4.8;
contract ERC20Events {
event Approval(address indexed src, address indexed guy, uint wad);
event Transfer(address indexed src, address indexed dst, uint wad);
}
contract ERC20 is ERC20Events {
function totalSupply() public view returns (uint);
function balanceOf(address guy) public view returns (uint);
function allowance(address src, address guy) public view returns (uint);
function approve(address guy, uint wad) public returns (bool);
function transfer(address dst, uint wad) public returns (bool);
function transferFrom(
address src, address dst, uint wad
) public returns (bool);
}
contract EventfulMarket {
event LogItemUpdate(uint id);
event LogTrade(uint pay_amt, address indexed pay_gem,
uint buy_amt, address indexed buy_gem);
event LogMake(
bytes32 indexed id,
bytes32 indexed pair,
address indexed maker,
ERC20 pay_gem,
ERC20 buy_gem,
uint128 pay_amt,
uint128 buy_amt,
uint64 timestamp
);
event LogBump(
bytes32 indexed id,
bytes32 indexed pair,
address indexed maker,
ERC20 pay_gem,
ERC20 buy_gem,
uint128 pay_amt,
uint128 buy_amt,
uint64 timestamp
);
event LogTake(
bytes32 id,
bytes32 indexed pair,
address indexed maker,
ERC20 pay_gem,
ERC20 buy_gem,
address indexed taker,
uint128 take_amt,
uint128 give_amt,
uint64 timestamp
);
event LogKill(
bytes32 indexed id,
bytes32 indexed pair,
address indexed maker,
ERC20 pay_gem,
ERC20 buy_gem,
uint128 pay_amt,
uint128 buy_amt,
uint64 timestamp
);
}
contract SimpleMarket is EventfulMarket, DSMath {
uint public last_offer_id;
mapping (uint => OfferInfo) public offers;
bool locked;
struct OfferInfo {
uint pay_amt;
ERC20 pay_gem;
uint buy_amt;
ERC20 buy_gem;
address owner;
uint64 timestamp;
}
modifier can_buy(uint id) {
require(isActive(id));
_;
}
modifier can_cancel(uint id) {
require(isActive(id));
require(getOwner(id) == msg.sender);
_;
}
modifier can_offer {
_;
}
modifier synchronized {
require(!locked);
locked = true;
_;
locked = false;
}
function isActive(uint id) public constant returns (bool active) {
return offers[id].timestamp > 0;
}
function getOwner(uint id) public constant returns (address owner) {
return offers[id].owner;
}
function getOffer(uint id) public constant returns (uint, ERC20, uint, ERC20) {
var offer = offers[id];
return (offer.pay_amt, offer.pay_gem,
offer.buy_amt, offer.buy_gem);
}
// ---- Public entrypoints ---- //
function bump(bytes32 id_)
public
can_buy(uint256(id_))
{
var id = uint256(id_);
LogBump(
id_,
keccak256(offers[id].pay_gem, offers[id].buy_gem),
offers[id].owner,
offers[id].pay_gem,
offers[id].buy_gem,
uint128(offers[id].pay_amt),
uint128(offers[id].buy_amt),
offers[id].timestamp
);
}
// Accept given `quantity` of an offer. Transfers funds from caller to
// offer maker, and from market to caller.
function buy(uint id, uint quantity)
public
can_buy(id)
synchronized
returns (bool)
{
OfferInfo memory offer = offers[id];
uint spend = mul(quantity, offer.buy_amt) / offer.pay_amt;
require(uint128(spend) == spend);
require(uint128(quantity) == quantity);
// For backwards semantic compatibility.
if (quantity == 0 || spend == 0 ||
quantity > offer.pay_amt || spend > offer.buy_amt)
{
return false;
}
offers[id].pay_amt = sub(offer.pay_amt, quantity);
offers[id].buy_amt = sub(offer.buy_amt, spend);
require( offer.buy_gem.transferFrom(msg.sender, offer.owner, spend) );
require( offer.pay_gem.transfer(msg.sender, quantity) );
LogItemUpdate(id);
LogTake(
bytes32(id),
keccak256(offer.pay_gem, offer.buy_gem),
offer.owner,
offer.pay_gem,
offer.buy_gem,
msg.sender,
uint128(quantity),
uint128(spend),
uint64(now)
);
LogTrade(quantity, offer.pay_gem, spend, offer.buy_gem);
if (offers[id].pay_amt == 0) {
delete offers[id];
}
return true;
}
// Cancel an offer. Refunds offer maker.
function cancel(uint id)
public
can_cancel(id)
synchronized
returns (bool success)
{
// read-only offer. Modify an offer by directly accessing offers[id]
OfferInfo memory offer = offers[id];
delete offers[id];
require( offer.pay_gem.transfer(offer.owner, offer.pay_amt) );
LogItemUpdate(id);
LogKill(
bytes32(id),
keccak256(offer.pay_gem, offer.buy_gem),
offer.owner,
offer.pay_gem,
offer.buy_gem,
uint128(offer.pay_amt),
uint128(offer.buy_amt),
uint64(now)
);
success = true;
}
function kill(bytes32 id)
public
{
require(cancel(uint256(id)));
}
function make(
ERC20 pay_gem,
ERC20 buy_gem,
uint128 pay_amt,
uint128 buy_amt
)
public
returns (bytes32 id)
{
return bytes32(offer(pay_amt, pay_gem, buy_amt, buy_gem));
}
// Make a new offer. Takes funds from the caller into market escrow.
function offer(uint pay_amt, ERC20 pay_gem, uint buy_amt, ERC20 buy_gem)
public
can_offer
synchronized
returns (uint id)
{
require(uint128(pay_amt) == pay_amt);
require(uint128(buy_amt) == buy_amt);
require(pay_amt > 0);
require(pay_gem != ERC20(0x0));
require(buy_amt > 0);
require(buy_gem != ERC20(0x0));
require(pay_gem != buy_gem);
OfferInfo memory info;
info.pay_amt = pay_amt;
info.pay_gem = pay_gem;
info.buy_amt = buy_amt;
info.buy_gem = buy_gem;
info.owner = msg.sender;
info.timestamp = uint64(now);
id = _next_id();
offers[id] = info;
require( pay_gem.transferFrom(msg.sender, this, pay_amt) );
LogItemUpdate(id);
LogMake(
bytes32(id),
keccak256(pay_gem, buy_gem),
msg.sender,
pay_gem,
buy_gem,
uint128(pay_amt),
uint128(buy_amt),
uint64(now)
);
}
function take(bytes32 id, uint128 maxTakeAmount)
public
{
require(buy(uint256(id), maxTakeAmount));
}
function _next_id()
internal
returns (uint)
{
last_offer_id++; return last_offer_id;
}
}
// Simple Market with a market lifetime. When the close_time has been reached,
// offers can only be cancelled (offer and buy will throw).
contract ExpiringMarket is DSAuth, SimpleMarket {
uint64 public close_time;
bool public stopped;
// after close_time has been reached, no new offers are allowed
modifier can_offer {
require(!isClosed());
_;
}
// after close, no new buys are allowed
modifier can_buy(uint id) {
require(isActive(id));
require(!isClosed());
_;
}
// after close, anyone can cancel an offer
modifier can_cancel(uint id) {
require(isActive(id));
require((msg.sender == getOwner(id)) || isClosed());
_;
}
function ExpiringMarket(uint64 _close_time)
public
{
close_time = _close_time;
}
function isClosed() public constant returns (bool closed) {
return stopped || getTime() > close_time;
}
function getTime() public constant returns (uint64) {
return uint64(now);
}
function stop() public auth {
stopped = true;
}
}
/// note.sol -- the `note' modifier, for logging calls as events
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
pragma solidity ^0.4.13;
contract DSNote {
event LogNote(
bytes4 indexed sig,
address indexed guy,
bytes32 indexed foo,
bytes32 indexed bar,
uint wad,
bytes fax
) anonymous;
modifier note {
bytes32 foo;
bytes32 bar;
assembly {
foo := calldataload(4)
bar := calldataload(36)
}
LogNote(msg.sig, msg.sender, foo, bar, msg.value, msg.data);
_;
}
}
contract MatchingEvents {
event LogBuyEnabled(bool isEnabled);
event LogMinSell(address pay_gem, uint min_amount);
event LogMatchingEnabled(bool isEnabled);
event LogUnsortedOffer(uint id);
event LogSortedOffer(uint id);
event LogInsert(address keeper, uint id);
event LogDelete(address keeper, uint id);
}
contract MatchingMarket is MatchingEvents, ExpiringMarket, DSNote {
bool public buyEnabled = true; //buy enabled
bool public matchingEnabled = true; //true: enable matching,
//false: revert to expiring market
struct sortInfo {
uint next; //points to id of next higher offer
uint prev; //points to id of previous lower offer
uint delb; //the blocknumber where this entry was marked for delete
}
mapping(uint => sortInfo) public _rank; //doubly linked lists of sorted offer ids
mapping(address => mapping(address => uint)) public _best; //id of the highest offer for a token pair
mapping(address => mapping(address => uint)) public _span; //number of offers stored for token pair in sorted orderbook
mapping(address => uint) public _dust; //minimum sell amount for a token to avoid dust offers
mapping(uint => uint) public _near; //next unsorted offer id
uint _head; //first unsorted offer id
uint public dustId; // id of the latest offer marked as dust
function MatchingMarket(uint64 close_time) ExpiringMarket(close_time) public {
}
// After close, anyone can cancel an offer
modifier can_cancel(uint id) {
require(isActive(id), "Offer was deleted or taken, or never existed.");
require(
isClosed() || msg.sender == getOwner(id) || id == dustId,
"Offer can not be cancelled because user is not owner, and market is open, and offer sells required amount of tokens."
);
_;
}
// ---- Public entrypoints ---- //
function make(
ERC20 pay_gem,
ERC20 buy_gem,
uint128 pay_amt,
uint128 buy_amt
)
public
returns (bytes32)
{
return bytes32(offer(pay_amt, pay_gem, buy_amt, buy_gem));
}
function take(bytes32 id, uint128 maxTakeAmount) public {
require(buy(uint256(id), maxTakeAmount));
}
function kill(bytes32 id) public {
require(cancel(uint256(id)));
}
// Make a new offer. Takes funds from the caller into market escrow.
//
// If matching is enabled:
// * creates new offer without putting it in
// the sorted list.
// * available to authorized contracts only!
// * keepers should call insert(id,pos)
// to put offer in the sorted list.
//
// If matching is disabled:
// * calls expiring market's offer().
// * available to everyone without authorization.
// * no sorting is done.
//
function offer(
uint pay_amt, //maker (ask) sell how much
ERC20 pay_gem, //maker (ask) sell which token
uint buy_amt, //taker (ask) buy how much
ERC20 buy_gem //taker (ask) buy which token
)
public
returns (uint)
{
require(!locked, "Reentrancy attempt");
var fn = matchingEnabled ? _offeru : super.offer;
return fn(pay_amt, pay_gem, buy_amt, buy_gem);
}
// Make a new offer. Takes funds from the caller into market escrow.
function offer(
uint pay_amt, //maker (ask) sell how much
ERC20 pay_gem, //maker (ask) sell which token
uint buy_amt, //maker (ask) buy how much
ERC20 buy_gem, //maker (ask) buy which token
uint pos //position to insert offer, 0 should be used if unknown
)
public
can_offer
returns (uint)
{
return offer(pay_amt, pay_gem, buy_amt, buy_gem, pos, true);
}
function offer(
uint pay_amt, //maker (ask) sell how much
ERC20 pay_gem, //maker (ask) sell which token
uint buy_amt, //maker (ask) buy how much
ERC20 buy_gem, //maker (ask) buy which token
uint pos, //position to insert offer, 0 should be used if unknown
bool rounding //match "close enough" orders?
)
public
can_offer
returns (uint)
{
require(!locked, "Reentrancy attempt");
require(_dust[pay_gem] <= pay_amt);
if (matchingEnabled) {
return _matcho(pay_amt, pay_gem, buy_amt, buy_gem, pos, rounding);
}
return super.offer(pay_amt, pay_gem, buy_amt, buy_gem);
}
//Transfers funds from caller to offer maker, and from market to caller.
function buy(uint id, uint amount)
public
can_buy(id)
returns (bool)
{
require(!locked, "Reentrancy attempt");
var fn = matchingEnabled ? _buys : super.buy;
return fn(id, amount);
}
// Cancel an offer. Refunds offer maker.
function cancel(uint id)
public
can_cancel(id)
returns (bool success)
{
require(!locked, "Reentrancy attempt");
if (matchingEnabled) {
if (isOfferSorted(id)) {
require(_unsort(id));
} else {
require(_hide(id));
}
}
return super.cancel(id); //delete the offer.
}
//insert offer into the sorted list
//keepers need to use this function
function insert(
uint id, //maker (ask) id
uint pos //position to insert into
)
public
returns (bool)
{
require(!locked, "Reentrancy attempt");
require(!isOfferSorted(id)); //make sure offers[id] is not yet sorted
require(isActive(id)); //make sure offers[id] is active
_hide(id); //remove offer from unsorted offers list
_sort(id, pos); //put offer into the sorted offers list
LogInsert(msg.sender, id);
return true;
}
//deletes _rank [id]
// Function should be called by keepers.
function del_rank(uint id)
public
returns (bool)
{
require(!locked, "Reentrancy attempt");
require(!isActive(id) && _rank[id].delb != 0 && _rank[id].delb < block.number - 10);
delete _rank[id];
LogDelete(msg.sender, id);
return true;
}
//set the minimum sell amount for a token
// Function is used to avoid "dust offers" that have
// very small amount of tokens to sell, and it would
// cost more gas to accept the offer, than the value
// of tokens received.
function setMinSell(
ERC20 pay_gem, //token to assign minimum sell amount to
uint dust //maker (ask) minimum sell amount
)
public
auth
note
returns (bool)
{
_dust[pay_gem] = dust;
LogMinSell(pay_gem, dust);
return true;
}
//returns the minimum sell amount for an offer
function getMinSell(
ERC20 pay_gem //token for which minimum sell amount is queried
)
public
constant
returns (uint)
{
return _dust[pay_gem];
}
//set buy functionality enabled/disabled
function setBuyEnabled(bool buyEnabled_) public auth returns (bool) {
buyEnabled = buyEnabled_;
LogBuyEnabled(buyEnabled);
return true;
}
//set matching enabled/disabled
// If matchingEnabled true(default), then inserted offers are matched.
// Except the ones inserted by contracts, because those end up
// in the unsorted list of offers, that must be later sorted by
// keepers using insert().
// If matchingEnabled is false then MatchingMarket is reverted to ExpiringMarket,
// and matching is not done, and sorted lists are disabled.
function setMatchingEnabled(bool matchingEnabled_) public auth returns (bool) {
matchingEnabled = matchingEnabled_;
LogMatchingEnabled(matchingEnabled);
return true;
}
//return the best offer for a token pair
// the best offer is the lowest one if it's an ask,
// and highest one if it's a bid offer
function getBestOffer(ERC20 sell_gem, ERC20 buy_gem) public constant returns(uint) {
return _best[sell_gem][buy_gem];
}
//return the next worse offer in the sorted list
// the worse offer is the higher one if its an ask,
// a lower one if its a bid offer,
// and in both cases the newer one if they're equal.
function getWorseOffer(uint id) public constant returns(uint) {
return _rank[id].prev;
}
//return the next better offer in the sorted list
// the better offer is in the lower priced one if its an ask,
// the next higher priced one if its a bid offer
// and in both cases the older one if they're equal.
function getBetterOffer(uint id) public constant returns(uint) {
return _rank[id].next;
}
//return the amount of better offers for a token pair
function getOfferCount(ERC20 sell_gem, ERC20 buy_gem) public constant returns(uint) {
return _span[sell_gem][buy_gem];
}
//get the first unsorted offer that was inserted by a contract
// Contracts can't calculate the insertion position of their offer because it is not an O(1) operation.
// Their offers get put in the unsorted list of offers.
// Keepers can calculate the insertion position offchain and pass it to the insert() function to insert
// the unsorted offer into the sorted list. Unsorted offers will not be matched, but can be bought with buy().
function getFirstUnsortedOffer() public constant returns(uint) {
return _head;
}
//get the next unsorted offer
// Can be used to cycle through all the unsorted offers.
function getNextUnsortedOffer(uint id) public constant returns(uint) {
return _near[id];
}
function isOfferSorted(uint id) public constant returns(bool) {
return _rank[id].next != 0
|| _rank[id].prev != 0
|| _best[offers[id].pay_gem][offers[id].buy_gem] == id;
}
function sellAllAmount(ERC20 pay_gem, uint pay_amt, ERC20 buy_gem, uint min_fill_amount)
public
returns (uint fill_amt)
{
require(!locked, "Reentrancy attempt");
uint offerId;
while (pay_amt > 0) { //while there is amount to sell
offerId = getBestOffer(buy_gem, pay_gem); //Get the best offer for the token pair
require(offerId != 0); //Fails if there are not more offers
// There is a chance that pay_amt is smaller than 1 wei of the other token
if (pay_amt * 1 ether < wdiv(offers[offerId].buy_amt, offers[offerId].pay_amt)) {
break; //We consider that all amount is sold
}
if (pay_amt >= offers[offerId].buy_amt) { //If amount to sell is higher or equal than current offer amount to buy
fill_amt = add(fill_amt, offers[offerId].pay_amt); //Add amount bought to acumulator
pay_amt = sub(pay_amt, offers[offerId].buy_amt); //Decrease amount to sell
take(bytes32(offerId), uint128(offers[offerId].pay_amt)); //We take the whole offer
} else { // if lower
var baux = rmul(pay_amt * 10 ** 9, rdiv(offers[offerId].pay_amt, offers[offerId].buy_amt)) / 10 ** 9;
fill_amt = add(fill_amt, baux); //Add amount bought to acumulator
take(bytes32(offerId), uint128(baux)); //We take the portion of the offer that we need
pay_amt = 0; //All amount is sold
}
}
require(fill_amt >= min_fill_amount);
}
function buyAllAmount(ERC20 buy_gem, uint buy_amt, ERC20 pay_gem, uint max_fill_amount)
public
returns (uint fill_amt)
{
require(!locked, "Reentrancy attempt");
uint offerId;
while (buy_amt > 0) { //Meanwhile there is amount to buy
offerId = getBestOffer(buy_gem, pay_gem); //Get the best offer for the token pair
require(offerId != 0);
// There is a chance that buy_amt is smaller than 1 wei of the other token
if (buy_amt * 1 ether < wdiv(offers[offerId].pay_amt, offers[offerId].buy_amt)) {
break; //We consider that all amount is sold
}
if (buy_amt >= offers[offerId].pay_amt) { //If amount to buy is higher or equal than current offer amount to sell
fill_amt = add(fill_amt, offers[offerId].buy_amt); //Add amount sold to acumulator
buy_amt = sub(buy_amt, offers[offerId].pay_amt); //Decrease amount to buy
take(bytes32(offerId), uint128(offers[offerId].pay_amt)); //We take the whole offer
} else { //if lower
fill_amt = add(fill_amt, rmul(buy_amt * 10 ** 9, rdiv(offers[offerId].buy_amt, offers[offerId].pay_amt)) / 10 ** 9); //Add amount sold to acumulator
take(bytes32(offerId), uint128(buy_amt)); //We take the portion of the offer that we need
buy_amt = 0; //All amount is bought
}
}
require(fill_amt <= max_fill_amount);
}
function getBuyAmount(ERC20 buy_gem, ERC20 pay_gem, uint pay_amt) public constant returns (uint fill_amt) {
var offerId = getBestOffer(buy_gem, pay_gem); //Get best offer for the token pair
while (pay_amt > offers[offerId].buy_amt) {
fill_amt = add(fill_amt, offers[offerId].pay_amt); //Add amount to buy accumulator
pay_amt = sub(pay_amt, offers[offerId].buy_amt); //Decrease amount to pay
if (pay_amt > 0) { //If we still need more offers
offerId = getWorseOffer(offerId); //We look for the next best offer
require(offerId != 0); //Fails if there are not enough offers to complete
}
}
fill_amt = add(fill_amt, rmul(pay_amt * 10 ** 9, rdiv(offers[offerId].pay_amt, offers[offerId].buy_amt)) / 10 ** 9); //Add proportional amount of last offer to buy accumulator
}
function getPayAmount(ERC20 pay_gem, ERC20 buy_gem, uint buy_amt) public constant returns (uint fill_amt) {
var offerId = getBestOffer(buy_gem, pay_gem); //Get best offer for the token pair
while (buy_amt > offers[offerId].pay_amt) {
fill_amt = add(fill_amt, offers[offerId].buy_amt); //Add amount to pay accumulator
buy_amt = sub(buy_amt, offers[offerId].pay_amt); //Decrease amount to buy
if (buy_amt > 0) { //If we still need more offers
offerId = getWorseOffer(offerId); //We look for the next best offer
require(offerId != 0); //Fails if there are not enough offers to complete
}
}
fill_amt = add(fill_amt, rmul(buy_amt * 10 ** 9, rdiv(offers[offerId].buy_amt, offers[offerId].pay_amt)) / 10 ** 9); //Add proportional amount of last offer to pay accumulator
}
// ---- Internal Functions ---- //
function _buys(uint id, uint amount)
internal
returns (bool)
{
require(buyEnabled);
if (amount == offers[id].pay_amt) {
if (isOfferSorted(id)) {
//offers[id] must be removed from sorted list because all of it is bought
_unsort(id);
}else{
_hide(id);
}
}
require(super.buy(id, amount));
// If offer has become dust during buy, we cancel it
if (isActive(id) && offers[id].pay_amt < _dust[offers[id].pay_gem]) {
dustId = id; //enable current msg.sender to call cancel(id)
cancel(id);
}
return true;
}
//find the id of the next higher offer after offers[id]
function _find(uint id)
internal
view
returns (uint)
{
require( id > 0 );
address buy_gem = address(offers[id].buy_gem);
address pay_gem = address(offers[id].pay_gem);
uint top = _best[pay_gem][buy_gem];
uint old_top = 0;
// Find the larger-than-id order whose successor is less-than-id.
while (top != 0 && _isPricedLtOrEq(id, top)) {
old_top = top;
top = _rank[top].prev;
}
return old_top;
}
//find the id of the next higher offer after offers[id]
function _findpos(uint id, uint pos)
internal
view
returns (uint)
{
require(id > 0);
// Look for an active order.
while (pos != 0 && !isActive(pos)) {
pos = _rank[pos].prev;
}
if (pos == 0) {
//if we got to the end of list without a single active offer
return _find(id);
} else {
// if we did find a nearby active offer
// Walk the order book down from there...
if(_isPricedLtOrEq(id, pos)) {
uint old_pos;
// Guaranteed to run at least once because of
// the prior if statements.
while (pos != 0 && _isPricedLtOrEq(id, pos)) {
old_pos = pos;
pos = _rank[pos].prev;
}
return old_pos;
// ...or walk it up.
} else {
while (pos != 0 && !_isPricedLtOrEq(id, pos)) {
pos = _rank[pos].next;
}
return pos;
}
}
}
//return true if offers[low] priced less than or equal to offers[high]
function _isPricedLtOrEq(
uint low, //lower priced offer's id
uint high //higher priced offer's id
)
internal
view
returns (bool)
{
return mul(offers[low].buy_amt, offers[high].pay_amt)
>= mul(offers[high].buy_amt, offers[low].pay_amt);
}
//these variables are global only because of solidity local variable limit
//match offers with taker offer, and execute token transactions
function _matcho(
uint t_pay_amt, //taker sell how much
ERC20 t_pay_gem, //taker sell which token
uint t_buy_amt, //taker buy how much
ERC20 t_buy_gem, //taker buy which token
uint pos, //position id
bool rounding //match "close enough" orders?
)
internal
returns (uint id)
{
uint best_maker_id; //highest maker id
uint t_buy_amt_old; //taker buy how much saved
uint m_buy_amt; //maker offer wants to buy this much token
uint m_pay_amt; //maker offer wants to sell this much token
// there is at least one offer stored for token pair
while (_best[t_buy_gem][t_pay_gem] > 0) {
best_maker_id = _best[t_buy_gem][t_pay_gem];
m_buy_amt = offers[best_maker_id].buy_amt;
m_pay_amt = offers[best_maker_id].pay_amt;
// Ugly hack to work around rounding errors. Based on the idea that
// the furthest the amounts can stray from their "true" values is 1.
// Ergo the worst case has t_pay_amt and m_pay_amt at +1 away from
// their "correct" values and m_buy_amt and t_buy_amt at -1.
// Since (c - 1) * (d - 1) > (a + 1) * (b + 1) is equivalent to
// c * d > a * b + a + b + c + d, we write...
if (mul(m_buy_amt, t_buy_amt) > mul(t_pay_amt, m_pay_amt) +
(rounding ? m_buy_amt + t_buy_amt + t_pay_amt + m_pay_amt : 0))
{
break;
}
// ^ The `rounding` parameter is a compromise borne of a couple days
// of discussion.
buy(best_maker_id, min(m_pay_amt, t_buy_amt));
t_buy_amt_old = t_buy_amt;
t_buy_amt = sub(t_buy_amt, min(m_pay_amt, t_buy_amt));
t_pay_amt = mul(t_buy_amt, t_pay_amt) / t_buy_amt_old;
if (t_pay_amt == 0 || t_buy_amt == 0) {
break;
}
}
if (t_buy_amt > 0 && t_pay_amt > 0 && t_pay_amt >= _dust[t_pay_gem]) {
//new offer should be created
id = super.offer(t_pay_amt, t_pay_gem, t_buy_amt, t_buy_gem);
//insert offer into the sorted list
_sort(id, pos);
}
}
// Make a new offer without putting it in the sorted list.
// Takes funds from the caller into market escrow.
// ****Available to authorized contracts only!**********
// Keepers should call insert(id,pos) to put offer in the sorted list.
function _offeru(
uint pay_amt, //maker (ask) sell how much
ERC20 pay_gem, //maker (ask) sell which token
uint buy_amt, //maker (ask) buy how much
ERC20 buy_gem //maker (ask) buy which token
)
internal
returns (uint id)
{
require(_dust[pay_gem] <= pay_amt);
id = super.offer(pay_amt, pay_gem, buy_amt, buy_gem);
_near[id] = _head;
_head = id;
LogUnsortedOffer(id);
}
//put offer into the sorted list
function _sort(
uint id, //maker (ask) id
uint pos //position to insert into
)
internal
{
require(isActive(id));
address buy_gem = address(offers[id].buy_gem);
address pay_gem = address(offers[id].pay_gem);
uint prev_id; //maker (ask) id
pos = pos == 0 || offers[pos].pay_gem != pay_gem || offers[pos].buy_gem != buy_gem || !isOfferSorted(pos)
?
_find(id)
:
_findpos(id, pos);
if (pos != 0) { //offers[id] is not the highest offer
//requirement below is satisfied by statements above
//require(_isPricedLtOrEq(id, pos));
prev_id = _rank[pos].prev;
_rank[pos].prev = id;
_rank[id].next = pos;
} else { //offers[id] is the highest offer
prev_id = _best[pay_gem][buy_gem];
_best[pay_gem][buy_gem] = id;
}
if (prev_id != 0) { //if lower offer does exist
//requirement below is satisfied by statements above
//require(!_isPricedLtOrEq(id, prev_id));
_rank[prev_id].next = id;
_rank[id].prev = prev_id;
}
_span[pay_gem][buy_gem]++;
LogSortedOffer(id);
}
// Remove offer from the sorted list (does not cancel offer)
function _unsort(
uint id //id of maker (ask) offer to remove from sorted list
)
internal
returns (bool)
{
address buy_gem = address(offers[id].buy_gem);
address pay_gem = address(offers[id].pay_gem);
require(_span[pay_gem][buy_gem] > 0);
require(_rank[id].delb == 0 && //assert id is in the sorted list
isOfferSorted(id));
if (id != _best[pay_gem][buy_gem]) { // offers[id] is not the highest offer
require(_rank[_rank[id].next].prev == id);
_rank[_rank[id].next].prev = _rank[id].prev;
} else { //offers[id] is the highest offer
_best[pay_gem][buy_gem] = _rank[id].prev;
}
if (_rank[id].prev != 0) { //offers[id] is not the lowest offer
require(_rank[_rank[id].prev].next == id);
_rank[_rank[id].prev].next = _rank[id].next;
}
_span[pay_gem][buy_gem]--;
_rank[id].delb = block.number; //mark _rank[id] for deletion
return true;
}
//Hide offer from the unsorted order book (does not cancel offer)
function _hide(
uint id //id of maker offer to remove from unsorted list
)
internal
returns (bool)
{
uint uid = _head; //id of an offer in unsorted offers list
uint pre = uid; //id of previous offer in unsorted offers list
require(!isOfferSorted(id)); //make sure offer id is not in sorted offers list
if (_head == id) { //check if offer is first offer in unsorted offers list
_head = _near[id]; //set head to new first unsorted offer
_near[id] = 0; //delete order from unsorted order list
return true;
}
while (uid > 0 && uid != id) { //find offer in unsorted order list
pre = uid;
uid = _near[uid];
}
if (uid != id) { //did not find offer id in unsorted offers list
return false;
}
_near[pre] = _near[id]; //set previous unsorted offer to point to offer after offer id
_near[id] = 0; //delete order from unsorted order list
return true;
}
}