ETH Price: $3,271.60 (-0.48%)

Contract Diff Checker

Contract Name:
Vyper_contract

Contract Source Code:

File 1 of 1 : Vyper_contract

contract ERC20():
    def transfer(_to: address, _value: uint256) -> bool: modifying
    def transferFrom(_from: address, _to: address, _value: uint256) -> bool: modifying
    def balanceOf(_owner: address) -> uint256: constant
    def allowance(_owner: address, _spender: address) -> uint256: constant
    def decimals() -> uint256: constant

contract PriceOracle():
    def poolSize(contract_address: address) -> uint256: constant
    def normalized_token_prices(token_address: address) -> uint256: constant

TOKEN_PRICE_MULTIPLIER: constant(uint256) = 100000000
FEE_MULTIPLIER: constant(uint256) = 100000
EXCHANGE_RATE_MULTIPLIER: constant(uint256) = 10000000000000000000000 #10**22

# ERC20 events
Transfer: event({_from: indexed(address), _to: indexed(address), _value: uint256})
Approval: event({_owner: indexed(address), _spender: indexed(address), _value: uint256})

OwnershipTransferred: event({previous_owner: indexed(address), new_owner: indexed(address)})
LiquidityAdded: event({provider: indexed(address), amount: indexed(uint256)})
LiquidityRemoved: event({provider: indexed(address), amount: indexed(uint256)})
Trade: event({input_token: indexed(address), output_token: indexed(address), input_amount: indexed(uint256)})
PermissionUpdated: event({name: indexed(string[32]), value: indexed(bool)})
FeeUpdated: event({name: indexed(string[32]), value: indexed(decimal)})
PriceOracleAddressUpdated: event({new_address: indexed(address)})

name: public(string[32])                                 # Stablecoinswap
symbol: public(string[6])                                # STL
owner: public(address)                                   # contract owner
decimals: public(uint256)                                # 18
totalSupply: public(uint256)                             # total number of contract tokens in existence
balanceOf: public(map(address, uint256))                 # balance of an address
allowance: public(map(address, map(address, uint256)))   # allowance of one address on another
inputTokens: public(map(address, bool))                  # addresses of the ERC20 tokens allowed to transfer into this contract
outputTokens: public(map(address, bool))                 # addresses of the ERC20 tokens allowed to transfer out of this contract
permissions: public(map(string[32], bool))               # pause / resume contract functions
feesInt: map(string[32], uint256)                        # trade / pool fees multiplied by FEE_MULTIPLIER
priceOracleAddress: public(address)                      # address of price oracle

@public
def __init__(token_addresses: address[3], price_oracle_addr: address):
    assert price_oracle_addr != ZERO_ADDRESS

    self.owner = msg.sender
    self.name = "Stablecoinswap"
    self.symbol = "STL"
    self.decimals = 18
    self.permissions["tradingAllowed"] = True
    self.permissions["liquidityAddingAllowed"] = True

    for i in range(3):
        assert token_addresses[i] != ZERO_ADDRESS
        self.inputTokens[token_addresses[i]] = True
        self.outputTokens[token_addresses[i]] = True

    self.feesInt['tradeFee'] = 200
    self.feesInt['ownerFee'] = 100

    self.priceOracleAddress = price_oracle_addr

@private
@constant
def tokenPrice(token_address: address) -> uint256:
    token_price: uint256 = PriceOracle(self.priceOracleAddress).normalized_token_prices(token_address)
    assert token_price > 0

    return token_price

# Required to support tokens (such s USDT) which don't provide a return value for transfer functions
@private
def transferAndCheck(token: address, recipient: address, amount: uint256) -> bool:
    self_balance: uint256 = ERC20(token).balanceOf(self)
    recipient_balance: uint256 = ERC20(token).balanceOf(recipient)
    ERC20(token).transfer(recipient, amount)
    new_self_balance: uint256 = ERC20(token).balanceOf(self)
    new_recipient_balance: uint256 = ERC20(token).balanceOf(recipient)

    assert new_recipient_balance == (recipient_balance + amount)
    assert new_self_balance == (self_balance - amount)

    return True

# Required to support tokens (such s USDT) which don't provide a return value for transfer functions
@private
def transferFromAndCheck(token: address, sender: address, recipient: address, amount: uint256) -> bool:
    recipient_balance: uint256 = ERC20(token).balanceOf(recipient)
    sender_balance: uint256 = ERC20(token).balanceOf(sender)
    ERC20(token).transferFrom(sender, recipient, amount)
    new_recipient_balance: uint256 = ERC20(token).balanceOf(recipient)
    new_sender_balance: uint256 = ERC20(token).balanceOf(sender)

    assert new_sender_balance == (sender_balance - amount)
    assert new_recipient_balance == (recipient_balance + amount)

    return True

# Deposit erc20 token
@public
@nonreentrant('lock')
def addLiquidity(token_address: address, erc20_token_amount: uint256, deadline: timestamp) -> uint256:
    assert self.inputTokens[token_address]
    assert deadline > block.timestamp and erc20_token_amount > 0
    assert self.permissions["liquidityAddingAllowed"]

    token_price: uint256 = self.tokenPrice(token_address)
    # It's better to divide at the very end for a higher precision
    new_liquidity: uint256 = token_price * erc20_token_amount / TOKEN_PRICE_MULTIPLIER
    if self.totalSupply > 0:
        new_liquidity = new_liquidity * self.totalSupply / PriceOracle(self.priceOracleAddress).poolSize(self)
    else:
        assert new_liquidity >= 1000000000

    self.balanceOf[msg.sender] += new_liquidity
    self.totalSupply += new_liquidity

    # Can't assert the result directly: https://github.com/ethereum/vyper/issues/1468
    transfer_from_result: bool = self.transferFromAndCheck(token_address, msg.sender, self, erc20_token_amount)
    assert transfer_from_result

    log.LiquidityAdded(msg.sender, new_liquidity)

    return new_liquidity

# Withdraw erc20 token
@public
@nonreentrant('lock')
def removeLiquidity(token_address: address, stableswap_token_amount: uint256, erc20_min_output_amount: uint256, deadline: timestamp) -> uint256:
    assert self.outputTokens[token_address]
    assert stableswap_token_amount > 0 and deadline > block.timestamp
    assert self.balanceOf[msg.sender] >= stableswap_token_amount
    assert self.totalSupply > 0

    token_price: uint256 = self.tokenPrice(token_address)
    pool_size: uint256 = PriceOracle(self.priceOracleAddress).poolSize(self)
    # erc20_token_amount = stableswap_token_amount * pool_size / totalSupply / token_price * TOKEN_PRICE_MULTIPLIER
    # It's better to divide at the very end for a higher precision
    erc20_token_amount: uint256 = stableswap_token_amount * pool_size / self.totalSupply

    ownerFee: uint256 = 0

    if msg.sender != self.owner:
        ownerFee = stableswap_token_amount * self.feesInt['ownerFee'] / FEE_MULTIPLIER
        multiplier_after_fees: uint256 = FEE_MULTIPLIER - self.feesInt['ownerFee'] - self.feesInt['tradeFee']
        erc20_token_amount = erc20_token_amount * multiplier_after_fees / FEE_MULTIPLIER

    erc20_token_amount = erc20_token_amount * TOKEN_PRICE_MULTIPLIER / token_price

    self.balanceOf[msg.sender] -= stableswap_token_amount
    self.balanceOf[self.owner] += ownerFee
    self.totalSupply -= stableswap_token_amount - ownerFee

    assert erc20_token_amount >= erc20_min_output_amount

    # Can't assert the result directly: https://github.com/ethereum/vyper/issues/1468
    transfer_result: bool = self.transferAndCheck(token_address, msg.sender, erc20_token_amount)
    assert transfer_result

    log.LiquidityRemoved(msg.sender, stableswap_token_amount)

    return erc20_token_amount

# Note that due to rounding, the fees could be slightly higher for the tokens with smaller decimal precision.
@public
@constant
def tokenExchangeRateAfterFees(input_token_address: address, output_token_address: address) -> uint256:
    input_token_price: uint256 = self.tokenPrice(input_token_address)
    output_token_price: uint256 = self.tokenPrice(output_token_address)

    multiplier_after_fees: uint256 = FEE_MULTIPLIER - self.feesInt['ownerFee'] - self.feesInt['tradeFee']
    exchange_rate: uint256 = EXCHANGE_RATE_MULTIPLIER * input_token_price * multiplier_after_fees / FEE_MULTIPLIER / output_token_price

    return exchange_rate

@public
@constant
def tokenOutputAmountAfterFees(input_token_amount: uint256, input_token_address: address, output_token_address: address) -> uint256:
    output_token_amount: uint256 = input_token_amount * self.tokenExchangeRateAfterFees(input_token_address, output_token_address) / EXCHANGE_RATE_MULTIPLIER
    return output_token_amount

# Trade one erc20 token for another
@public
@nonreentrant('lock')
def swapTokens(input_token: address, output_token: address, erc20_input_amount: uint256, erc20_min_output_amount: uint256, deadline: timestamp) -> uint256:
    assert self.inputTokens[input_token] and self.outputTokens[output_token]
    assert erc20_input_amount > 0 and erc20_min_output_amount > 0
    assert deadline > block.timestamp
    assert self.permissions["tradingAllowed"]

    input_token_price: uint256 = self.tokenPrice(input_token)
    # input_usd_value is a value of input multiplied by TOKEN_PRICE_MULTIPLIER
    input_usd_value: uint256 = erc20_input_amount * input_token_price
    tradeFee: uint256 = input_usd_value * self.feesInt['tradeFee'] / FEE_MULTIPLIER / TOKEN_PRICE_MULTIPLIER
    ownerFee: uint256 = input_usd_value * self.feesInt['ownerFee'] / FEE_MULTIPLIER / TOKEN_PRICE_MULTIPLIER

    pool_size_after_swap: uint256 = PriceOracle(self.priceOracleAddress).poolSize(self) + tradeFee
    new_owner_shares: uint256 = self.totalSupply * ownerFee / pool_size_after_swap

    erc20_output_amount: uint256 = self.tokenOutputAmountAfterFees(erc20_input_amount, input_token, output_token)
    assert erc20_output_amount >= erc20_min_output_amount

    self.balanceOf[self.owner] += new_owner_shares
    self.totalSupply += new_owner_shares

    # Can't assert the result directly: https://github.com/ethereum/vyper/issues/1468
    transfer_from_result: bool = self.transferFromAndCheck(input_token, msg.sender, self, erc20_input_amount)
    assert transfer_from_result

    transfer_result: bool = self.transferAndCheck(output_token, msg.sender, erc20_output_amount)
    assert transfer_result

    log.Trade(input_token, output_token, erc20_input_amount)

    return erc20_output_amount

@public
def updateInputToken(token_address: address, allowed: bool) -> bool:
    assert msg.sender == self.owner
    assert not self.inputTokens[token_address] == allowed
    assert ERC20(token_address).decimals() >= 2
    self.inputTokens[token_address] = allowed
    return True

@public
def updateOutputToken(token_address: address, allowed: bool) -> bool:
    assert msg.sender == self.owner
    assert not self.outputTokens[token_address] == allowed
    assert ERC20(token_address).decimals() >= 2
    self.outputTokens[token_address] = allowed
    return True

@public
def updatePermission(permission_name: string[32], value: bool) -> bool:
    assert msg.sender == self.owner
    self.permissions[permission_name] = value
    log.PermissionUpdated(permission_name, value)
    return True

# Return share of total liquidity belonging to the user
@public
@constant
def poolOwnership(user_address: address) -> decimal:
    user_balance: decimal = convert(self.balanceOf[user_address], decimal)
    total_liquidity: decimal = convert(self.totalSupply, decimal)
    share: decimal = user_balance / total_liquidity
    return share

@public
def transferOwnership(new_owner: address) -> bool:
    assert new_owner != ZERO_ADDRESS
    assert msg.sender == self.owner
    self.owner = new_owner
    log.OwnershipTransferred(self.owner, new_owner)
    return True

@public
def updateFee(fee_name: string[32], value: decimal) -> bool:
    assert msg.sender == self.owner
    self.feesInt[fee_name] = convert(floor(value * convert(FEE_MULTIPLIER, decimal)), uint256)
    log.FeeUpdated(fee_name, value)
    return True

@public
@constant
def fees(fee_name: string[32]) -> decimal:
    return convert(self.feesInt[fee_name], decimal) / convert(FEE_MULTIPLIER, decimal)

@public
def updatePriceOracleAddress(new_address: address) -> bool:
    assert msg.sender == self.owner
    self.priceOracleAddress = new_address
    log.PriceOracleAddressUpdated(new_address)
    return True

# ERC-20 functions

@public
def transfer(_to: address, _value: uint256) -> bool:
    self.balanceOf[msg.sender] -= _value
    self.balanceOf[_to] += _value
    log.Transfer(msg.sender, _to, _value)
    return True

@public
def transferFrom(_from: address, _to: address, _value: uint256) -> bool:
    self.balanceOf[_from] -= _value
    self.balanceOf[_to] += _value
    self.allowance[_from][msg.sender] -= _value
    log.Transfer(_from, _to, _value)
    return True

@public
def approve(_spender: address, _value: uint256) -> bool:
    self.allowance[msg.sender][_spender] = _value
    log.Approval(msg.sender, msg.sender, _value)
    return True

Please enter a contract address above to load the contract details and source code.

Context size (optional):