Skip to content

Commit

Permalink
Merge branch 'master' into test/chainlink-pricer
Browse files Browse the repository at this point in the history
  • Loading branch information
antoncoding committed Sep 7, 2020
2 parents bee218e + 8b28ed1 commit a25bcd2
Show file tree
Hide file tree
Showing 21 changed files with 1,937 additions and 576 deletions.
3 changes: 2 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ jobs:
key: gamma-{{ .Environment.CIRCLE_SHA1 }}
paths:
- ~/gammaprotocol

lint:
working_directory: ~/gammaprotocol
docker:
Expand Down Expand Up @@ -96,7 +97,7 @@ jobs:
docker:
- image: circleci/node:10.18.0
- image: trufflesuite/ganache-cli:v6.10.1
command: ganache-cli -d -l 4294967295 --allowUnlimitedContractSize
command: ganache-cli -d -l 4294967295 --defaultBalanceEther 500
steps:
- restore_cache:
key: gamma-contracts-build-{{ .Environment.CIRCLE_SHA1 }}
Expand Down
1 change: 1 addition & 0 deletions .solcover.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module.exports = {
providerOptions: {
mnemonic: "candy maple cake sugar pudding cream honey rich smooth crumble sweet treat"
},
silent: true,
skipFiles: [
'Migrations.sol',
'mocks/',
Expand Down
10 changes: 5 additions & 5 deletions contracts/AddressBook.sol
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ contract AddressBook is Ownable {
* @param _marginCalculator margin calculator address
*/
function setMarginCalculator(address _marginCalculator) external onlyOwner {
updateImpl(MARGIN_CALCULATOR, _marginCalculator);
setAddress(MARGIN_CALCULATOR, _marginCalculator);
}

/**
Expand All @@ -170,7 +170,7 @@ contract AddressBook is Ownable {
* @param _liquidationManager liquidation manager address
*/
function setLiquidationManager(address _liquidationManager) external onlyOwner {
updateImpl(LIQUIDATION_MANAGER, _liquidationManager);
setAddress(LIQUIDATION_MANAGER, _liquidationManager);
}

/**
Expand All @@ -179,7 +179,7 @@ contract AddressBook is Ownable {
* @param _oracle oracle address
*/
function setOracle(address _oracle) external onlyOwner {
updateImpl(ORACLE, _oracle);
setAddress(ORACLE, _oracle);
}

/**
Expand Down Expand Up @@ -220,7 +220,7 @@ contract AddressBook is Ownable {
function updateImpl(bytes32 _id, address _newAddress) public onlyOwner {
address payable proxyAddress = address(uint160(getAddress(_id)));

bytes memory params = abi.encodeWithSignature("initialize(address)", address(this));
bytes memory params = abi.encodeWithSignature("initialize(address,address)", address(this), owner());

if (proxyAddress == address(0)) {
OwnedUpgradeabilityProxy proxy = new OwnedUpgradeabilityProxy();
Expand All @@ -229,7 +229,7 @@ contract AddressBook is Ownable {
proxy.upgradeToAndCall(_newAddress, params);
} else {
OwnedUpgradeabilityProxy proxy = OwnedUpgradeabilityProxy(proxyAddress);
proxy.upgradeToAndCall(_newAddress, params);
proxy.upgradeTo(_newAddress);
}
}
}
124 changes: 109 additions & 15 deletions contracts/Controller.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ pragma solidity =0.6.10;

pragma experimental ABIEncoderV2;

import {Ownable} from "./packages/oz/Ownable.sol";
import {OwnableUpgradeSafe} from "./packages/oz/upgradeability/OwnableUpgradeSafe.sol";
import {ReentrancyGuardUpgradeSafe} from "./packages/oz/upgradeability/ReentrancyGuardUpgradeSafe.sol";
import {Initializable} from "./packages/oz/upgradeability/Initializable.sol";
import {SafeMath} from "./packages/oz/SafeMath.sol";
import {ReentrancyGuard} from "./packages/oz/ReentrancyGuard.sol";
import {MarginAccount} from "./libs/MarginAccount.sol";
import {Actions} from "./libs/Actions.sol";
import {AddressBookInterface} from "./interfaces/AddressBookInterface.sol";
Expand All @@ -22,7 +23,7 @@ import {MarginPoolInterface} from "./interfaces/MarginPoolInterface.sol";
* @title Controller
* @notice contract that
*/
contract Controller is ReentrancyGuard, Ownable {
contract Controller is Initializable, OwnableUpgradeSafe, ReentrancyGuardUpgradeSafe {
using MarginAccount for MarginAccount.Vault;
using SafeMath for uint256;

Expand All @@ -40,11 +41,13 @@ contract Controller is ReentrancyGuard, Ownable {
mapping(address => mapping(address => bool)) internal operators;

/**
* @notice contructor
* @notice initalize deployed contract
* @param _addressBook adressbook module
*/
constructor(address _addressBook) public {
require(_addressBook != address(0), "Controller: Invalid address book");
function initialize(address _addressBook, address _owner) public initializer {
__Context_init_unchained();
__Ownable_init_unchained(_owner);
__ReentrancyGuard_init_unchained();

addressBook = _addressBook;
}
Expand Down Expand Up @@ -93,14 +96,31 @@ contract Controller is ReentrancyGuard, Ownable {
uint256 vaultId,
uint256 amount
);
/// @notice emits an event when a short otoken get burned from a vaukt
/// @notice emits an event when a short otoken get burned from a vault
event ShortOtokenBurned(
address indexed otoken,
address indexed AccountOwner,
address indexed from,
uint256 vaultId,
uint256 amount
);
/// @notice emits an event when a exercise action execute
event Exercise(
address indexed otoken,
address indexed exerciser,
address indexed receiver,
address collateralAsset,
uint256 otokenBurned,
uint256 payout
);
/// @notice emits an event when a vault is settlted
event VaultSettled(
address indexed otoken,
address indexed AccountOwner,
address indexed to,
uint256 vaultId,
uint256 payout
);

/**
* @notice modifier check if protocol is not paused
Expand Down Expand Up @@ -200,7 +220,7 @@ contract Controller is ReentrancyGuard, Ownable {
* @param _otoken The address of the relevant oToken.
* @return A boolean which is true if and only if the price is finalized.
*/
function isPriceFinalized(address _otoken) external view returns (bool) {
function isPriceFinalized(address _otoken) public view returns (bool) {
address oracleModule = AddressBookInterface(addressBook).getOracle();
OracleInterface oracle = OracleInterface(oracleModule);

Expand Down Expand Up @@ -259,8 +279,8 @@ contract Controller is ReentrancyGuard, Ownable {
Actions.ActionType actionType = action.actionType;

if (
(actionType != Actions.ActionType.SettleVault) ||
(actionType != Actions.ActionType.Exercise) ||
(actionType != Actions.ActionType.SettleVault) &&
(actionType != Actions.ActionType.Exercise) &&
(actionType != Actions.ActionType.Call)
) {
// check if this action is manipulating the same vault as all other actions, other than SettleVault
Expand All @@ -285,6 +305,10 @@ contract Controller is ReentrancyGuard, Ownable {
vault = _mintOtoken(Actions._parseMintArgs(action));
} else if (actionType == Actions.ActionType.BurnShortOption) {
vault = _burnOtoken(Actions._parseBurnArgs(action));
} else if (actionType == Actions.ActionType.Exercise) {
_exercise(Actions._parseExerciseArgs(action));
} else if (actionType == Actions.ActionType.SettleVault) {
_settleVault(Actions._parseSettleVaultArgs(action));
}
}

Expand Down Expand Up @@ -440,7 +464,7 @@ contract Controller is ReentrancyGuard, Ownable {
require(checkVaultId(_args.owner, _args.vaultId), "Controller: invalid vault id");

MarginAccount.Vault memory vault = vaults[_args.owner][_args.vaultId];
if (isNotEmpty(vault.shortOtokens)) {
if (_isNotEmpty(vault.shortOtokens)) {
OtokenInterface otoken = OtokenInterface(vault.shortOtokens[0]);

require(
Expand Down Expand Up @@ -522,13 +546,68 @@ contract Controller is ReentrancyGuard, Ownable {
* @notice exercise option
* @param _args ExerciseArgs structure
*/
// function _exercise(Actions.ExerciseArgs memory _args) internal {}
function _exercise(Actions.ExerciseArgs memory _args) internal {
OtokenInterface otoken = OtokenInterface(_args.otoken);

require(now > otoken.expiryTimestamp(), "Controller: can not exercise un-expired otoken");

require(isPriceFinalized(_args.otoken), "Controller: otoken underlying asset price is not finalized yet");

uint256 payout = _getPayout(_args.otoken, _args.amount);

otoken.burnOtoken(msg.sender, _args.amount);

address marginPoolModule = AddressBookInterface(addressBook).getMarginPool();
MarginPoolInterface marginPool = MarginPoolInterface(marginPoolModule);

marginPool.transferToUser(otoken.collateralAsset(), _args.receiver, payout);

emit Exercise(_args.otoken, msg.sender, _args.receiver, otoken.collateralAsset(), _args.amount, payout);
}

/**
* @notice settle vault option
* @notice settle vault after expiry
* @param _args SettleVaultArgs structure
*/
// function _settleVault(Actions.SettleVaultArgs memory _args) internal {}
function _settleVault(Actions.SettleVaultArgs memory _args)
internal
isAuthorized(msg.sender, _args.owner)
returns (MarginAccount.Vault memory)
{
require(checkVaultId(_args.owner, _args.vaultId), "Controller: invalid vault id");

MarginAccount.Vault memory vault = vaults[_args.owner][_args.vaultId];

require(_isNotEmpty(vault.shortOtokens), "Controller: can not settle a vault with no otoken minted");

OtokenInterface shortOtoken = OtokenInterface(vault.shortOtokens[0]);

require(now > shortOtoken.expiryTimestamp(), "Controller: can not settle vault with un-expired otoken");
require(
isPriceFinalized(address(shortOtoken)),
"Controller: otoken underlying asset price is not finalized yet"
);

address calculatorModule = AddressBookInterface(addressBook).getMarginCalculator();
MarginCalculatorInterface calculator = MarginCalculatorInterface(calculatorModule);

(uint256 payout, ) = calculator.getExcessCollateral(vault);

address marginPoolModule = AddressBookInterface(addressBook).getMarginPool();
MarginPoolInterface marginPool = MarginPoolInterface(marginPoolModule);

if (_isNotEmpty(vault.longOtokens)) {
OtokenInterface longOtoken = OtokenInterface(vault.longOtokens[0]);

longOtoken.burnOtoken(marginPoolModule, vault.longAmounts[0]);
}

vaults[_args.owner][_args.vaultId]._clearVault();

marginPool.transferToUser(shortOtoken.collateralAsset(), _args.to, payout);

emit VaultSettled(address(shortOtoken), _args.owner, _args.to, _args.vaultId, payout);
}

//High Level: call arbitrary smart contract
//function _call(Actions.CallArgs args) internal {
Expand All @@ -545,7 +624,22 @@ contract Controller is ReentrancyGuard, Ownable {
return ((_vaultId > 0) && (_vaultId <= accountVaultCounter[_accountOwner]));
}

function isNotEmpty(address[] memory _array) internal pure returns (bool) {
function _isNotEmpty(address[] memory _array) internal pure returns (bool) {
return (_array.length > 0) && (_array[0] != address(0));
}

/**
* @notice get Otoken payout after expiry
* @param _otoken Otoken address
* @param _amount amount of Otoken
* @return payout = cashValue * amount
*/
function _getPayout(address _otoken, uint256 _amount) internal view returns (uint256) {
address calculatorModule = AddressBookInterface(addressBook).getMarginCalculator();
MarginCalculatorInterface calculator = MarginCalculatorInterface(calculatorModule);

uint256 cashValue = calculator.getExpiredCashValue(_otoken);

return cashValue.mul(_amount).div(1e18);
}
}

0 comments on commit a25bcd2

Please sign in to comment.