Skip to content

Commit

Permalink
Merge pull request #124 from fei-protocol/feat/IndexOTC
Browse files Browse the repository at this point in the history
INDEX OTC Proposal
  • Loading branch information
Joeysantoro committed Aug 14, 2021
2 parents 2e0f4d7 + 64d0684 commit 207e060
Show file tree
Hide file tree
Showing 15 changed files with 699 additions and 52 deletions.
14 changes: 12 additions & 2 deletions contract-addresses/mainnetAddresses.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,18 @@
"tribeReserveStabilizerAddress" : { "artifact": "TribeReserveStabilizer", "address": "0xa08A721dFB595753FFf335636674D76C455B275C"},
"feiRewardsDistributorAddress" : { "address": "0xEf1a94AF192A88859EAF3F3D8C1B9705542174C5"},
"multisigAddress": { "address": "0xB8f482539F2d3Ae2C9ea6076894df36D1f632775"},
"wethAddress": { "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"},
"stethAddress" : { "address": "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84"},
"wethAddress": { "artifact": "IWETH", "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"},
"wethERC20Address": { "artifact": "IERC20", "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"},
"stethAddress" : { "artifact": "IERC20", "address": "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84"},
"indexAddress" : { "artifact": "IERC20", "address" : "0x0954906da0Bf32d5479e25f46056d22f08464cab" },
"snapshotDelegateRegistry" : { "artifact": "DelegateRegistry", "address" : "0x469788fE6E9E9681C6ebF3bF78e7Fd26Fc015446" },
"ethOTCEscrow" : { "artifact": "OtcEscrow", "address": "0x6Cfed416f0729d5754f13fDDf297789079208E2e"},
"tribeOTCEscrow" : { "artifact": "OtcEscrow", "address": "0xe2fE8041429e4bd51c40F92C6cDb699527171298"},
"feiOTCEscrow" : { "artifact": "OtcEscrow", "address": "0x9B9fE1b732839a53948B02E5164c0A50fdf11e06"},
"indexDelegatorAddress" : { "artifact": "SnapshotDelegatorPCVDeposit", "address": "0x0ee81df08B20e4f9E0F534e50da437D24491c4ee" },
"defiPulseOTCAddress" : { "address" : "0x673d140eed36385cb784e279f8759f495c97cf03" },
"compoundEthAddress" : { "address" : "0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5" },
"compoundEthPCVDepositAddress" : { "artifact" : "EthCompoundPCVDeposit", "address" : "0x4fCB1435fD42CE7ce7Af3cB2e98289F79d2962b3" },
"communalFarmAddress" : { "address": "0x0639076265e9f88542C91DCdEda65127974A5CA5"},
"uniswapRouterAddress": { "address": "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D"},
"sushiswapRouterAddress" : { "address": "0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F"},
Expand Down
8 changes: 8 additions & 0 deletions contracts/pcv/PCVDeposit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ abstract contract PCVDeposit is IPCVDeposit, CoreRef {
address to,
uint256 amount
) public override onlyPCVController {
_withdrawERC20(token, to, amount);
}

function _withdrawERC20(
address token,
address to,
uint256 amount
) internal {
IERC20(token).safeTransfer(to, amount);
emit WithdrawERC20(msg.sender, token, to, amount);
}
Expand Down
2 changes: 1 addition & 1 deletion contracts/pcv/compound/EthCompoundPCVDeposit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ contract EthCompoundPCVDeposit is CompoundPCVDepositBase {
address _core,
address _cToken
) CompoundPCVDepositBase(_core, _cToken) {
require(cToken.isCEther(), "EthCompoundPCVDeposit: Not a CEther");
// require(cToken.isCEther(), "EthCompoundPCVDeposit: Not a CEther");
}

receive() external payable {}
Expand Down
88 changes: 88 additions & 0 deletions contracts/pcv/snapshot/SnapshotDelegatorPCVDeposit.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

import "../PCVDeposit.sol";

interface DelegateRegistry {
function setDelegate(bytes32 id, address delegate) external;

function clearDelegate(bytes32 id) external;

function delegation(address delegator, bytes32 id) external view returns(address delegatee);
}

/// @title Snapshot Delegator PCV Deposit
/// @author Fei Protocol
contract SnapshotDelegatorPCVDeposit is PCVDeposit {

event DelegateUpdate(address indexed oldDelegate, address indexed newDelegate);

/// @notice the Gnosis delegate registry used by snapshot
DelegateRegistry public constant DELEGATE_REGISTRY = DelegateRegistry(0x469788fE6E9E9681C6ebF3bF78e7Fd26Fc015446);

/// @notice the token that is being used for snapshot
IERC20 public immutable token;

/// @notice the keccak encoded spaceId of the snapshot space
bytes32 public spaceId;

/// @notice the snapshot delegate for the deposit
address public delegate;

/// @notice Snapshot Delegator PCV Deposit constructor
/// @param _core Fei Core for reference
/// @param _token snapshot token
/// @param _spaceId the id (or ENS name) of the snapshot space
constructor(
address _core,
IERC20 _token,
bytes32 _spaceId,
address _initialDelegate
) CoreRef(_core) {
token = _token;
spaceId = _spaceId;
_delegate(_initialDelegate);
}

/// @notice withdraw tokens from the PCV allocation
/// @param amountUnderlying of tokens withdrawn
/// @param to the address to send PCV to
function withdraw(address to, uint256 amountUnderlying)
external
override
onlyPCVController
{
_withdrawERC20(address(token), to, amountUnderlying);
}

/// @notice no-op
function deposit() external override {}

/// @notice returns total balance of PCV in the Deposit
function balance() public view override returns (uint256) {
return token.balanceOf(address(this));
}

/// @notice sets the snapshot delegate
/// @dev callable by governor or admin
function setDelegate(address newDelegate) external onlyGovernorOrAdmin {
_delegate(newDelegate);
}

/// @notice clears the delegate from snapshot
/// @dev callable by governor or guardian
function clearDelegate() external onlyGuardianOrGovernor {
address oldDelegate = delegate;
DELEGATE_REGISTRY.clearDelegate(spaceId);

emit DelegateUpdate(oldDelegate, address(0));
}

function _delegate(address newDelegate) internal {
address oldDelegate = delegate;
DELEGATE_REGISTRY.setDelegate(spaceId, newDelegate);
delegate = newDelegate;

emit DelegateUpdate(oldDelegate, newDelegate);
}
}
68 changes: 68 additions & 0 deletions contracts/utils/OtcEscrow.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

/*
Simple OTC Escrow contract to transfer tokens OTC
Inspired and forked from BadgerDAO
https://github.com/Badger-Finance/badger-system/blob/develop/contracts/badger-timelock/OtcEscrow.sol
*/
contract OtcEscrow {
using SafeERC20 for IERC20;

address public receivedToken;
address public sentToken;
address public recipient;

address public beneficiary;
uint256 public receivedAmount;
uint256 public sentAmount;

constructor(
address beneficiary_,
address recipient_,
address receivedToken_,
address sentToken_,
uint256 receivedAmount_,
uint256 sentAmount_
) {
beneficiary = beneficiary_;
recipient = recipient_;

receivedToken = receivedToken_;
sentToken = sentToken_;

receivedAmount = receivedAmount_;
sentAmount = sentAmount_;
}

modifier onlyApprovedParties() {
require(msg.sender == recipient || msg.sender == beneficiary);
_;
}

/// @dev Atomically trade specified amount of receivedToken for control over sentToken in vesting contract
/// @dev Either counterparty may execute swap if sufficient token approval is given by recipient
function swap() public onlyApprovedParties {
// Transfer expected receivedToken from beneficiary
IERC20(receivedToken).safeTransferFrom(beneficiary, recipient, receivedAmount);

// Transfer sentToken to beneficiary
IERC20(sentToken).safeTransfer(address(beneficiary), sentAmount);
}

/// @dev Return sentToken to Fei Protocol to revoke escrow deal
function revoke() external {
require(msg.sender == recipient, "onlyRecipient");
uint256 sentTokenBalance = IERC20(sentToken).balanceOf(address(this));
IERC20(sentToken).safeTransfer(recipient, sentTokenBalance);
}

function revokeReceivedToken() external onlyApprovedParties {
uint256 receivedTokenBalance = IERC20(receivedToken).balanceOf(address(this));
IERC20(receivedToken).safeTransfer(beneficiary, receivedTokenBalance);
}
}
12 changes: 6 additions & 6 deletions deploy/compoundPCVDeposit.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ async function deploy(deployAddress, addresses, logging = false) {
coreAddress,
feiAddress,
rariPool8FeiAddress,
rariPool8EthAddress
compoundEthAddress,
} = addresses;

if (
!coreAddress || !feiAddress || !rariPool8FeiAddress || !rariPool8EthAddress
!coreAddress || !feiAddress || !rariPool8FeiAddress || !compoundEthAddress
) {
throw new Error('An environment variable contract address is not set');
}
Expand All @@ -23,16 +23,16 @@ async function deploy(deployAddress, addresses, logging = false) {
);
logging ? console.log('FEI Rari ERC20CompoundPCVDeposit deployed to: ', rariPool8FeiPCVDeposit.address) : undefined;

const rariPool8EthPCVDeposit = await EthCompoundPCVDeposit.new(
const compoundEthPCVDeposit = await EthCompoundPCVDeposit.new(
coreAddress,
rariPool8EthAddress,
compoundEthAddress,
{ from: deployAddress }
);
logging ? console.log('FEI Rari EthCompoundPCVDeposit deployed to: ', rariPool8EthAddress.address) : undefined;
logging ? console.log('EthCompoundPCVDeposit deployed to: ', compoundEthPCVDeposit.address) : undefined;

return {
rariPool8FeiPCVDeposit,
rariPool8EthPCVDeposit
compoundEthPCVDeposit
};
}

Expand Down
78 changes: 78 additions & 0 deletions deploy/indexOTC.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { formatBytes32String } from '@ethersproject/strings';

const OtcEscrow = artifacts.require('OtcEscrow');
const SnapshotDelegatorPCVDeposit = artifacts.require('SnapshotDelegatorPCVDeposit');

const e18 = '000000000000000000';
const e15 = '000000000000000';
const indexSpaceId = formatBytes32String('index-coop.eth');
const indexDelegate = '0xb647055A9915bF9c8021a684E175A353525b9890'; // Matthew Graham delegation

async function deploy(deployAddress, addresses, logging = false) {
const {
coreAddress,
feiAddress,
wethAddress,
tribeAddress,
indexAddress,
defiPulseOTCAddress,
timelockAddress
} = addresses;

if (
!coreAddress || !wethAddress || !feiAddress || !tribeAddress || !indexAddress || !defiPulseOTCAddress
) {
throw new Error('An environment variable contract address is not set');
}

const ethOTCEscrow = await OtcEscrow.new(
defiPulseOTCAddress,
timelockAddress,
indexAddress,
wethAddress,
`50000${e18}`, // 50k INDEX
`633150${e15}`, // 633.150 ETH
{ from: deployAddress }
);
logging ? console.log('ETH OTC Escrow deployed to: ', ethOTCEscrow.address) : undefined;

const tribeOTCEscrow = await OtcEscrow.new(
defiPulseOTCAddress,
timelockAddress,
indexAddress,
tribeAddress,
`25000${e18}`, // 25k INDEX
`1235325922${e15}`, // 1235325.922 TRIBE
{ from: deployAddress }
);
logging ? console.log('TRIBE OTC Escrow deployed to: ', tribeOTCEscrow.address) : undefined;

const feiOTCEscrow = await OtcEscrow.new(
defiPulseOTCAddress,
timelockAddress,
indexAddress,
feiAddress,
`25000${e18}`, // 25k INDEX
`991512900${e15}`, // 991512.900 FEI
{ from: deployAddress }
);
logging ? console.log('FEI OTC Escrow deployed to: ', feiOTCEscrow.address) : undefined;

const indexDelegator = await SnapshotDelegatorPCVDeposit.new(
coreAddress,
indexAddress,
indexSpaceId,
indexDelegate
);

logging ? console.log('Index Delegator deployed to: ', indexDelegator.address) : undefined;

return {
ethOTCEscrow,
tribeOTCEscrow,
feiOTCEscrow,
indexDelegator
};
}

module.exports = { deploy };
2 changes: 1 addition & 1 deletion end-to-end/e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ describe('e2e', function () {
})

it('should be able to deposit and withdraw ETH', async function () {
const ethCompoundPCVDeposit = contracts.rariPool8EthPCVDeposit;
const ethCompoundPCVDeposit = contracts.compoundEthPCVDeposit;
const amount = tenPow18.mul(toBN(200));
await web3.eth.sendTransaction({from: deployAddress, to: ethCompoundPCVDeposit.address, value: amount });

Expand Down
1 change: 1 addition & 0 deletions end-to-end/setup/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export type MainnetContracts = {
pcvDripController: typeof Contract,
rariPool8FeiPCVDeposit: typeof Contract,
rariPool8EthPCVDeposit: typeof Contract,
compoundEthPCVDeposit: typeof Contract,
dpiBondingCurve: typeof Contract,
dpi: typeof Contract,
chainlinkDpiUsdOracleWrapper: typeof Contract,
Expand Down
Loading

0 comments on commit 207e060

Please sign in to comment.