Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

INDEX OTC Proposal #124

Merged
merged 13 commits into from
Aug 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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