Skip to content

Commit

Permalink
feat: run blackadder, add vyper linting to CI
Browse files Browse the repository at this point in the history
  • Loading branch information
spinoch committed Nov 2, 2020
1 parent 5b68aa0 commit 07bb6ff
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 39 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ jobs:
- name: Install python dependencies
run: pip install -r requirements-dev.txt

- name: Run pre-commmit hooks
run: pre-commit run -a

- name: Run black
run: black --check --include "(tests|scripts)" .

Expand Down
6 changes: 6 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
repos:
- repo: https://github.com/spinoch/blackadder
rev: master
hooks:
- id: blackadder
language_version: python3
118 changes: 79 additions & 39 deletions contracts/Vault.vy
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#@version 0.2.7
# @version 0.2.7

API_VERSION: constant(String[28]) = "0.1.2"

Expand All @@ -8,22 +8,38 @@ from vyper.interfaces import ERC20

implements: ERC20


interface DetailedERC20:
def name() -> String[42]: view
def symbol() -> String[20]: view
def decimals() -> uint256: view
def name() -> String[42]:
view

def symbol() -> String[20]:
view

def decimals() -> uint256:
view


interface Strategy:
def strategist() -> address: view
def estimatedTotalAssets() -> uint256: view
def withdraw(_amount: uint256): nonpayable
def migrate(_newStrategy: address): nonpayable
def strategist() -> address:
view

def estimatedTotalAssets() -> uint256:
view

def withdraw(_amount: uint256):
nonpayable

def migrate(_newStrategy: address):
nonpayable


event Transfer:
sender: indexed(address)
receiver: indexed(address)
value: uint256


event Approval:
owner: indexed(address)
spender: indexed(address)
Expand All @@ -43,6 +59,7 @@ governance: public(address)
guardian: public(address)
pendingGovernance: address


struct StrategyParams:
performanceFee: uint256 # Strategist's fee (basis points)
activation: uint256 # Activation block.number
Expand All @@ -52,12 +69,14 @@ struct StrategyParams:
totalDebt: uint256 # Total outstanding debt that Strategy has
totalReturns: uint256 # Total returns that Strategy has realized for Vault


event StrategyAdded:
strategy: indexed(address)
debtLimit: uint256 # Maximum borrow amount
rateLimit: uint256 # Increase/decrease per block
performanceFee: uint256 # Strategist's fee (basis points)


event StrategyReported:
strategy: indexed(address)
returnAdded: uint256
Expand All @@ -66,6 +85,7 @@ event StrategyReported:
totalDebt: uint256
debtLimit: uint256


# NOTE: Track the total for overhead targeting purposes
strategies: public(HashMap[address, StrategyParams])
MAXIMUM_STRATEGIES: constant(uint256) = 20
Expand All @@ -88,18 +108,23 @@ totalDebt: public(uint256) # Amount of tokens that all strategies have borrowed
lastReport: public(uint256) # Number of blocks since last report

rewards: public(address) # Rewards contract where Governance fees are sent to
managementFee: public(uint256) # Governance Fee for management of Vault (given to `rewards`)
performanceFee: public(uint256) # Governance Fee for performance of Vault (given to `rewards`)
managementFee: public(
uint256
) # Governance Fee for management of Vault (given to `rewards`)
performanceFee: public(
uint256
) # Governance Fee for performance of Vault (given to `rewards`)
FEE_MAX: constant(uint256) = 10_000 # 100%, or 10k basis points
BLOCKS_PER_YEAR: constant(uint256) = 2_300_000


@external
def __init__(
_token: address,
_governance: address,
_rewards: address,
_nameOverride: String[64],
_symbolOverride: String[32]
_symbolOverride: String[32],
):
# TODO: Non-detailed Configuration?
self.token = ERC20(_token)
Expand Down Expand Up @@ -218,15 +243,17 @@ def transfer(_to: address, _value: uint256) -> bool:


@external
def transferFrom(_from : address, _to : address, _value : uint256) -> bool:
if self.allowance[_from][msg.sender] < MAX_UINT256: # Unlimited approval (saves an SSTORE)
self.allowance[_from][msg.sender] -= _value
def transferFrom(_from: address, _to: address, _value: uint256) -> bool:
if (
self.allowance[_from][msg.sender] < MAX_UINT256
): # Unlimited approval (saves an SSTORE)
self.allowance[_from][msg.sender] -= _value
self._transfer(_from, _to, _value)
return True


@external
def approve(_spender : address, _value : uint256) -> bool:
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
Expand Down Expand Up @@ -312,7 +339,9 @@ def _issueSharesForAmount(_to: address, _amount: uint256) -> uint256:


@external
def deposit(_amount: uint256 = MAX_UINT256, _recipient: address = msg.sender) -> uint256:
def deposit(
_amount: uint256 = MAX_UINT256, _recipient: address = msg.sender
) -> uint256:
assert not self.emergencyShutdown # Deposits are locked out

amount: uint256 = _amount
Expand Down Expand Up @@ -389,7 +418,9 @@ def maxAvailableShares() -> uint256:


@external
def withdraw(_shares: uint256 = MAX_UINT256, _recipient: address = msg.sender) -> uint256:
def withdraw(
_shares: uint256 = MAX_UINT256, _recipient: address = msg.sender
) -> uint256:
shares: uint256 = _shares # May reduce this number below

# If _shares not specified, transfer full share balance
Expand Down Expand Up @@ -488,7 +519,7 @@ def _organizeWithdrawalQueue():
if strategy == ZERO_ADDRESS:
offset += 1 # how many values we need to shift, always `<= idx`
elif offset > 0:
self.withdrawalQueue[idx-offset] = strategy
self.withdrawalQueue[idx - offset] = strategy
self.withdrawalQueue[idx] = ZERO_ADDRESS


Expand All @@ -501,21 +532,23 @@ def addStrategy(
):
assert msg.sender == self.governance
assert self.strategies[_strategy].activation == 0
self.strategies[_strategy] = StrategyParams({
performanceFee: _performanceFee,
activation: block.number,
debtLimit: _debtLimit,
rateLimit: _rateLimit,
lastReport: block.number,
totalDebt: 0,
totalReturns: 0,
})
self.strategies[_strategy] = StrategyParams(
{
performanceFee: _performanceFee,
activation: block.number,
debtLimit: _debtLimit,
rateLimit: _rateLimit,
lastReport: block.number,
totalDebt: 0,
totalReturns: 0,
}
)
self.debtLimit += _debtLimit
log StrategyAdded(_strategy, _debtLimit, _rateLimit, _performanceFee)

# queue is full
assert self.withdrawalQueue[MAXIMUM_STRATEGIES-1] == ZERO_ADDRESS
self.withdrawalQueue[MAXIMUM_STRATEGIES-1] = _strategy
assert self.withdrawalQueue[MAXIMUM_STRATEGIES - 1] == ZERO_ADDRESS
self.withdrawalQueue[MAXIMUM_STRATEGIES - 1] = _strategy
self._organizeWithdrawalQueue()


Expand Down Expand Up @@ -594,15 +627,18 @@ def revokeStrategy(_strategy: address = msg.sender):
def addStrategyToQueue(_strategy: address):
assert msg.sender == self.governance
# Must be a current strategy
assert self.strategies[_strategy].activation > 0 and self.strategies[_strategy].totalDebt > 0
assert (
self.strategies[_strategy].activation > 0
and self.strategies[_strategy].totalDebt > 0
)
# Check if queue is full
assert self.withdrawalQueue[MAXIMUM_STRATEGIES-1] == ZERO_ADDRESS
assert self.withdrawalQueue[MAXIMUM_STRATEGIES - 1] == ZERO_ADDRESS
# Can't already be in the queue
for strategy in self.withdrawalQueue:
if strategy == ZERO_ADDRESS:
break
assert strategy != _strategy
self.withdrawalQueue[MAXIMUM_STRATEGIES-1] = _strategy
self.withdrawalQueue[MAXIMUM_STRATEGIES - 1] = _strategy
self._organizeWithdrawalQueue()


Expand Down Expand Up @@ -692,9 +728,11 @@ def _expectedReturn(_strategy: address) -> uint256:
strategy_totalReturns: uint256 = self.strategies[_strategy].totalReturns
strategy_activation: uint256 = self.strategies[_strategy].activation

blockDelta: uint256 = (block.number - strategy_lastReport)
blockDelta: uint256 = block.number - strategy_lastReport
if blockDelta > 0:
return (strategy_totalReturns * blockDelta) / (block.number - strategy_activation)
return (strategy_totalReturns * blockDelta) / (
block.number - strategy_activation
)
else:
return 0 # Covers the scenario when block.number == strategy_activation

Expand Down Expand Up @@ -731,14 +769,16 @@ def report(_return: uint256) -> uint256:
# Issue new shares to cover fees
# NOTE: In effect, this reduces overall share price by the combined fee
governance_fee: uint256 = (
self._totalAssets() * (block.number - self.lastReport) * self.managementFee
) / FEE_MAX / BLOCKS_PER_YEAR
(self._totalAssets() * (block.number - self.lastReport) * self.managementFee)
/ FEE_MAX
/ BLOCKS_PER_YEAR
)
self.lastReport = block.number
strategist_fee: uint256 = 0 # Only applies in certain conditions

# NOTE: Applies if strategy is not shutting down, or it is but all debt paid off
# NOTE: No fee is taken when a strategy is unwinding it's position, until all debt is paid
if _return > debt:
if _return > debt:
strategist_fee = (
(_return - debt) * self.strategies[msg.sender].performanceFee
) / FEE_MAX
Expand Down Expand Up @@ -834,9 +874,9 @@ def erc20_safe_transfer(_token: address, _to: address, _value: uint256):
concat(
method_id("transfer(address,uint256)"),
convert(_to, bytes32),
convert(_value, bytes32)
convert(_value, bytes32),
),
max_outsize=32
max_outsize=32,
)
if len(_response) > 0:
assert convert(_response, bool), "Transfer failed!"
Expand Down
1 change: 1 addition & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
black==19.10b0
eth-brownie>=1.11.10,<2.0.0
pre-commit

0 comments on commit 07bb6ff

Please sign in to comment.