From fc7ad46c15c1a4b2d4b90183d781e18881ac7c7d Mon Sep 17 00:00:00 2001 From: MioGreen Date: Mon, 9 Nov 2020 19:52:54 +0200 Subject: [PATCH] feat: added deferred ramp A to y pool & static yearn like token wrapper --- contracts/pool-templates/y/SwapTemplateY.vy | 21 ++ contracts/pool-templates/y/yStaticWrapper.vy | 225 +++++++++++++++++++ 2 files changed, 246 insertions(+) create mode 100644 contracts/pool-templates/y/yStaticWrapper.vy diff --git a/contracts/pool-templates/y/SwapTemplateY.vy b/contracts/pool-templates/y/SwapTemplateY.vy index 6ec6fe91..6a5ceaaf 100644 --- a/contracts/pool-templates/y/SwapTemplateY.vy +++ b/contracts/pool-templates/y/SwapTemplateY.vy @@ -830,6 +830,27 @@ def ramp_A(_future_A: uint256, _future_time: uint256): log RampA(_initial_A, _future_A_p, block.timestamp, _future_time) +@external +def ramp_A_deferred(_future_A: uint256, _future_time: uint256, _initial_time: uint256): + assert msg.sender == self.owner # dev: only owner + assert _initial_time >= self.initial_A_time + MIN_RAMP_TIME + assert _future_time >= _initial_time + MIN_RAMP_TIME # dev: insufficient time + + _initial_A: uint256 = self._A() + _future_A_p: uint256 = _future_A * A_PRECISION + + assert _future_A > 0 and _future_A < MAX_A + if _future_A_p < _initial_A: + assert _future_A_p * MAX_A_CHANGE >= _initial_A + else: + assert _future_A_p <= _initial_A * MAX_A_CHANGE + + self.initial_A = _initial_A + self.future_A = _future_A_p + self.initial_A_time = _initial_time + self.future_A_time = _future_time + + log RampA(_initial_A, _future_A_p, _initial_time, _future_time) @external def stop_ramp_A(): diff --git a/contracts/pool-templates/y/yStaticWrapper.vy b/contracts/pool-templates/y/yStaticWrapper.vy new file mode 100644 index 00000000..e78847c9 --- /dev/null +++ b/contracts/pool-templates/y/yStaticWrapper.vy @@ -0,0 +1,225 @@ +# @version ^0.2.0 +""" +@title yERC20 token implementation with static conversion rate +@author Mikhail Zelenin +""" + +from vyper.interfaces import ERC20 + +# External Contracts +interface yERC20: + def deposit(depositAmount: uint256): nonpayable + def withdraw(withdrawTokens: uint256): nonpayable + def getPricePerFullShare() -> uint256: view + + +implements: ERC20 +#implements: yERC20 + +event Transfer: + sender: indexed(address) + receiver: indexed(address) + value: uint256 + +event Approval: + owner: indexed(address) + spender: indexed(address) + value: uint256 + +# fixed constants +PRECISION: constant(uint256) = 10 ** 18 # The precision to convert to + +name: public(String[64]) +symbol: public(String[32]) +decimals: public(uint256) + +# NOTE: By declaring `balanceOf` as public, vyper automatically generates a 'balanceOf()' getter +# method to allow access to account balances. +# The _KeyType will become a required parameter for the getter and it will return _ValueType. +# See: https://vyper.readthedocs.io/en/v0.1.0-beta.8/types.html?highlight=getter#mappings +balanceOf: public(HashMap[address, uint256]) +allowances: HashMap[address, HashMap[address, uint256]] +total_supply: uint256 +token: address +rate: uint256 + +@external +def __init__(_name: String[64], _symbol: String[32], _decimals: uint256, _token: address, _rate: uint256): + self.name = _name + self.symbol = _symbol + self.decimals = _decimals + self.token = _token + self.rate = _rate # multiplied by PRECISION + +@view +@external +def totalSupply() -> uint256: + """ + @dev Total number of tokens in existence. + """ + return self.total_supply + + +@view +@external +def allowance(_owner : address, _spender : address) -> uint256: + """ + @dev Function to check the amount of tokens that an owner allowed to a spender. + @param _owner The address which owns the funds. + @param _spender The address which will spend the funds. + @return An uint256 specifying the amount of tokens still available for the spender. + """ + return self.allowances[_owner][_spender] + + +@external +def transfer(_to : address, _value : uint256) -> bool: + """ + @dev Transfer token for a specified address + @param _to The address to transfer to. + @param _value The amount to be transferred. + """ + # NOTE: vyper does not allow underflows + # so the following subtraction would revert on insufficient balance + self.balanceOf[msg.sender] -= _value + self.balanceOf[_to] += _value + log Transfer(msg.sender, _to, _value) + return True + + +@external +def transferFrom(_from : address, _to : address, _value : uint256) -> bool: + """ + @dev Transfer tokens from one address to another. + @param _from address The address which you want to send tokens from + @param _to address The address which you want to transfer to + @param _value uint256 the amount of tokens to be transferred + """ + # NOTE: vyper does not allow underflows + # so the following subtraction would revert on insufficient balance + self.balanceOf[_from] -= _value + self.balanceOf[_to] += _value + # NOTE: vyper does not allow underflows + # so the following subtraction would revert on insufficient allowance + self.allowances[_from][msg.sender] -= _value + log Transfer(_from, _to, _value) + return True + + +@external +def approve(_spender : address, _value : uint256) -> bool: + """ + @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. + Beware that changing an allowance with this method brings the risk that someone may use both the old + and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this + race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: + https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + @param _spender The address which will spend the funds. + @param _value The amount of tokens to be spent. + """ + self.allowances[msg.sender][_spender] = _value + log Approval(msg.sender, _spender, _value) + return True + + +@internal +def _mint(_to: address, _value: uint256): + """ + @dev Mint an amount of the token and assigns it to an account. + This encapsulates the modification of balances such that the + proper events are emitted. + @param _to The account that will receive the created tokens. + @param _value The amount that will be created. + """ + assert _to != ZERO_ADDRESS + self.total_supply += _value + self.balanceOf[_to] += _value + log Transfer(ZERO_ADDRESS, _to, _value) + + +@internal +def _burn(_to: address, _value: uint256): + """ + @dev Internal function that burns an amount of the token of a given + account. + @param _to The account whose tokens will be burned. + @param _value The amount that will be burned. + """ + assert _to != ZERO_ADDRESS + self.total_supply -= _value + self.balanceOf[_to] -= _value + log Transfer(_to, ZERO_ADDRESS, _value) + + +@external +def burn(_value: uint256): + """ + @dev Burn an amount of the token of msg.sender. + @param _value The amount that will be burned. + """ + self._burn(msg.sender, _value) + + +@external +def burnFrom(_to: address, _value: uint256): + """ + @dev Burn an amount of the token from a given account. + @param _to The account whose tokens will be burned. + @param _value The amount that will be burned. + """ + self.allowances[_to][msg.sender] -= _value + self._burn(_to, _value) + +@external +def getPricePerFullShare() -> uint256: + return self.rate + +@internal +def safeTransfer(_token: address, _to: address, _value: uint256) -> bool: + _response: Bytes[32] = raw_call( + _token, + concat( + method_id("transfer(address,uint256)"), + convert(_to, bytes32), + convert(_value, bytes32) + ), + max_outsize=32 + ) + if len(_response) > 0: + assert convert(_response, bool), "Transfer failed!" + + return True + +@internal +def safeTransferFrom(_token: address, _from : address, _to : address, _value : uint256) -> bool: + _response: Bytes[32] = raw_call( + _token, + concat( + method_id("transferFrom(address,address,uint256)"), + convert(_from, bytes32), + convert(_to, bytes32), + convert(_value, bytes32) + ), + max_outsize=32 + ) + if len(_response) > 0: + assert convert(_response, bool), "Transfer failed!" + + return True + +@external +def deposit(_amount: uint256): + _before: uint256 = ERC20(self.token).balanceOf(self) + self.safeTransferFrom(self.token, msg.sender, self, _amount) + _after: uint256 = ERC20(self.token).balanceOf(self) + # NOTE: vyper does not allow underflows + # so the following subtraction would revert on insufficient allowance + _checked_amount: uint256 = _after - _before # Additional check for deflationary tokens + wrappers: uint256 = _checked_amount * self.rate / PRECISION + self._mint(msg.sender, wrappers) + +@external +def withdraw(_amount: uint256): + w: uint256 = _amount / self.rate * PRECISION + self._burn(msg.sender, _amount) + self.safeTransfer(self.token, msg.sender, w)