Skip to content

Commit a43400c

Browse files
authored
Add escrow contracts for index sale (#46)
* add OtcEscrow contact * add OtcEscrow tests * add test for revoke method * optimize imports * PR review changes * code review changes * permission the swap function * improve tests * refactor * add onlyOnce modifier * add javadoc for onlyOnce * refactoring
1 parent 3bafb9e commit a43400c

File tree

5 files changed

+552
-2
lines changed

5 files changed

+552
-2
lines changed

contracts/token/OtcEscrow.sol

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
//SPDX-License-Identifier: Unlicense
2+
pragma solidity ^0.6.10;
3+
4+
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
5+
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
6+
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
7+
8+
import { Vesting } from "./Vesting.sol";
9+
10+
/**
11+
* @title OtcEscrow
12+
* @author Badger DAO (Modified by Set Protocol)
13+
*
14+
* A simple OTC swap contract allowing two users to set the parameters of an OTC
15+
* deal in the constructor arguments, and deposits the sold tokens into a vesting
16+
* contract when a swap is completed.
17+
*/
18+
contract OtcEscrow {
19+
20+
using SafeMath for uint256;
21+
using SafeERC20 for IERC20;
22+
23+
/* ========== Events =========== */
24+
25+
event VestingDeployed(address vesting);
26+
27+
/* ====== Modifiers ======== */
28+
29+
/**
30+
* Throws if the sender is not Index Gov
31+
*/
32+
modifier onlyIndexGov() {
33+
require(msg.sender == indexGov, "unauthorized");
34+
_;
35+
}
36+
37+
/**
38+
* Throws if run more than once
39+
*/
40+
modifier onlyOnce() {
41+
require(!hasRun, "swap already executed");
42+
hasRun = true;
43+
_;
44+
}
45+
46+
/* ======== State Variables ======= */
47+
48+
address public usdc;
49+
address public index;
50+
51+
address public indexGov;
52+
address public beneficiary;
53+
54+
uint256 public vestingStart;
55+
uint256 public vestingEnd;
56+
uint256 public vestingCliff;
57+
58+
uint256 public usdcAmount;
59+
uint256 public indexAmount;
60+
61+
bool hasRun;
62+
63+
64+
65+
/* ====== Constructor ======== */
66+
67+
/**
68+
* Sets the state variables that encode the terms of the OTC sale
69+
*
70+
* @param _beneficiary Address that will purchase INDEX
71+
* @param _indexGov Address that will receive USDC
72+
* @param _vestingStart Timestamp of vesting start
73+
* @param _vestingCliff Timestamp of vesting cliff
74+
* @param _vestingEnd Timestamp of vesting end
75+
* @param _usdcAmount Amount of USDC swapped for the sale
76+
* @param _indexAmount Amount of INDEX swapped for the sale
77+
* @param _usdcAddress Address of the USDC token
78+
* @param _indexAddress Address of the Index token
79+
*/
80+
constructor(
81+
address _beneficiary,
82+
address _indexGov,
83+
uint256 _vestingStart,
84+
uint256 _vestingCliff,
85+
uint256 _vestingEnd,
86+
uint256 _usdcAmount,
87+
uint256 _indexAmount,
88+
address _usdcAddress,
89+
address _indexAddress
90+
) public {
91+
beneficiary = _beneficiary;
92+
indexGov = _indexGov;
93+
94+
vestingStart = _vestingStart;
95+
vestingCliff = _vestingCliff;
96+
vestingEnd = _vestingEnd;
97+
98+
usdcAmount = _usdcAmount;
99+
indexAmount = _indexAmount;
100+
101+
usdc = _usdcAddress;
102+
index = _indexAddress;
103+
hasRun = false;
104+
}
105+
106+
/* ======= External Functions ======= */
107+
108+
/**
109+
* Executes the OTC deal. Sends the USDC from the beneficiary to Index Governance, and
110+
* locks the INDEX in the vesting contract. Can only be called once.
111+
*/
112+
function swap() external onlyOnce {
113+
114+
require(IERC20(index).balanceOf(address(this)) >= indexAmount, "insufficient INDEX");
115+
116+
// Transfer expected USDC from beneficiary
117+
IERC20(usdc).safeTransferFrom(beneficiary, address(this), usdcAmount);
118+
119+
// Create Vesting contract
120+
Vesting vesting = new Vesting(index, beneficiary, indexAmount, vestingStart, vestingCliff, vestingEnd);
121+
122+
// Transfer index to vesting contract
123+
IERC20(index).safeTransfer(address(vesting), indexAmount);
124+
125+
// Transfer USDC to index governance
126+
IERC20(usdc).safeTransfer(indexGov, usdcAmount);
127+
128+
emit VestingDeployed(address(vesting));
129+
}
130+
131+
/**
132+
* Return INDEX to Index Governance to revoke the deal
133+
*/
134+
function revoke() external onlyIndexGov {
135+
uint256 indexBalance = IERC20(index).balanceOf(address(this));
136+
IERC20(index).safeTransfer(indexGov, indexBalance);
137+
}
138+
139+
/**
140+
* Recovers USDC accidentally sent to the contract
141+
*/
142+
function recoverUsdc() external {
143+
uint256 usdcBalance = IERC20(usdc).balanceOf(address(this));
144+
IERC20(usdc).safeTransfer(beneficiary, usdcBalance);
145+
}
146+
}

0 commit comments

Comments
 (0)