Skip to content

Instantly share code, notes, and snippets.

@dbeal-eth
Last active June 5, 2021 20:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dbeal-eth/9d38a097c4c58f42409f3f9874f3a5fe to your computer and use it in GitHub Desktop.
Save dbeal-eth/9d38a097c4c58f42409f3f9874f3a5fe to your computer and use it in GitHub Desktop.
Bonus Emission Escrow
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./interfaces/IERC20.sol";
import "./Owned.sol";
/**
* @title BonusEmissionEscrow
* @dev Escrow that holds funds for a beneficiary, deposited from a single party
* @dev The owner account (that is, the address that instantiates this
* contract) deposits the principal into the contract after construction, and after
* `startTime` for `period` seconds following, deposited funds will be emitted
* linerly. Beneficiary can claim curerntly emitted, claimable funds by calling `claim`.
* Owner may terminate emission and collect unemitted funds by calling `terminate`
*/
contract BonusEmissionEscrow is Owned {
event Claimed(address indexed beneficiary, uint claimed);
event Funded(address indexed beneficiary, uint funded);
address public immutable beneficiary;
address public immutable token;
uint public immutable principal;
uint public immutable startTime;
uint public immutable period;
uint public withdrawn;
/**
* @dev Constructor.
* @param beneficiary_ The beneficiary of the deposits.
* @param token_ The token supplied by the emitted bonus
* @param principal_ The amount of `token` supplied to the beneficiary by the end of `period_`
* @param startTime_ Unix time which beneficiary can start claiming funds
* @param period_ Unix time over which the funds will be emitted for claim
*/
constructor (address payable beneficiary_, address token_, uint principal_, uint startTime_, uint period_) {
require(beneficiary_ != address(0), "RefundEscrow: beneficiary is the zero address");
beneficiary = beneficiary_;
token = token_;
principal = principal_;
startTime = startTime_;
period = period_;
}
function tokenContract() internal view returns (IERC20) {
return IERC20(token);
}
/**
* @dev called after constructor to add funds to the contract
* will pull in needed ERC20 tokens in order to initialize the bonus emission
*/
function fund() public onlyOwner virtual {
require(tokenContract().balanceOf(address(this)) == 0);
tokenContract().transferFrom(owner, address(this), principal);
emit Funded(beneficiary, principal);
}
/**
* @dev terminate the bonus, sends availableToWithdraw to beneficiary, and refunds remaining tokens to owner
*/
function terminate() public onlyOwner virtual {
tokenContract().transfer(beneficiary, availableToClaim());
tokenContract().transfer(owner, IERC20(token).balanceOf(address(this)));
selfdestruct();
}
/**
* @dev reports number of tokens can be claimed if `claim` were called now.
*/
function availableToClaim() public view returns (uint256) {
uint remaining = principal - withdrawn;
uint curtime = block.timestamp;
int reltime = curtime - startTime;
if(reltime < 0) {
return 0;
}
if(reltime > period) {
return remaining;
}
uint available = principal * reltime / period - withdrawn;
require(available < remaining);
return available;
}
/**
* @dev Claims the beneficiary's currently available funds.
*/
function claim() public virtual {
uint claimable = availableToClaim();
withdrawn += claimable;
tokenContract().transfer(owner, beneficiary, claimable);
emit Claimed(beneficiary, claimable);
if(principal - withdrawn == 0) {
selfdestruct();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment