Contract Source Code:
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
// ----------------------------------------------------------------------------
// BokkyPooBah's Red-Black Tree Library v1.0-pre-release-a
//
// A Solidity Red-Black Tree binary search library to store and access a sorted
// list of unsigned integer data. The Red-Black algorithm rebalances the binary
// search tree, resulting in O(log n) insert, remove and search time (and ~gas)
//
// https://github.com/bokkypoobah/BokkyPooBahsRedBlackTreeLibrary
//
//
// Enjoy. (c) BokkyPooBah / Bok Consulting Pty Ltd 2020. The MIT Licence.
// ----------------------------------------------------------------------------
abstract contract BalanceTree {
struct Node {
address parent;
address left;
address right;
bool red;
}
address public root;
address constant EMPTY = address(0);
mapping(address => Node) public nodes;
function exists(address key) internal view returns (bool) {
return (key != EMPTY) && ((key == root) || (nodes[key].parent != EMPTY));
}
function sortKey(address key) internal virtual view returns (uint256);
function rotateLeft(address key) internal {
address cursor = nodes[key].right;
address keyParent = nodes[key].parent;
address cursorLeft = nodes[cursor].left;
nodes[key].right = cursorLeft;
if (cursorLeft != EMPTY) {
nodes[cursorLeft].parent = key;
}
nodes[cursor].parent = keyParent;
if (keyParent == EMPTY) {
root = cursor;
} else if (key == nodes[keyParent].left) {
nodes[keyParent].left = cursor;
} else {
nodes[keyParent].right = cursor;
}
nodes[cursor].left = key;
nodes[key].parent = cursor;
}
function rotateRight(address key) internal {
address cursor = nodes[key].left;
address keyParent = nodes[key].parent;
address cursorRight = nodes[cursor].right;
nodes[key].left = cursorRight;
if (cursorRight != EMPTY) {
nodes[cursorRight].parent = key;
}
nodes[cursor].parent = keyParent;
if (keyParent == EMPTY) {
root = cursor;
} else if (key == nodes[keyParent].right) {
nodes[keyParent].right = cursor;
} else {
nodes[keyParent].left = cursor;
}
nodes[cursor].right = key;
nodes[key].parent = cursor;
}
function insertFixup(address key) internal {
address cursor;
while (key != root && nodes[nodes[key].parent].red) {
address keyParent = nodes[key].parent;
if (keyParent == nodes[nodes[keyParent].parent].left) {
cursor = nodes[nodes[keyParent].parent].right;
if (nodes[cursor].red) {
nodes[keyParent].red = false;
nodes[cursor].red = false;
nodes[nodes[keyParent].parent].red = true;
key = nodes[keyParent].parent;
} else {
if (key == nodes[keyParent].right) {
key = keyParent;
rotateLeft(key);
}
keyParent = nodes[key].parent;
nodes[keyParent].red = false;
nodes[nodes[keyParent].parent].red = true;
rotateRight(nodes[keyParent].parent);
}
} else {
cursor = nodes[nodes[keyParent].parent].left;
if (nodes[cursor].red) {
nodes[keyParent].red = false;
nodes[cursor].red = false;
nodes[nodes[keyParent].parent].red = true;
key = nodes[keyParent].parent;
} else {
if (key == nodes[keyParent].left) {
key = keyParent;
rotateRight(key);
}
keyParent = nodes[key].parent;
nodes[keyParent].red = false;
nodes[nodes[keyParent].parent].red = true;
rotateLeft(nodes[keyParent].parent);
}
}
}
if (nodes[root].red) nodes[root].red = false;
}
function insert(address key) internal {
address cursor = EMPTY;
address probe = root;
while (probe != EMPTY) {
cursor = probe;
if (sortKey(key) < sortKey(probe)) {
probe = nodes[probe].left;
} else {
probe = nodes[probe].right;
}
}
nodes[key] = Node({parent : cursor, left : EMPTY, right : EMPTY, red : true});
if (cursor == EMPTY) {
root = key;
} else if (sortKey(key) < sortKey(cursor)) {
nodes[cursor].left = key;
} else {
nodes[cursor].right = key;
}
insertFixup(key);
}
function replaceParent(address a, address b) internal {
address bParent = nodes[b].parent;
nodes[a].parent = bParent;
if (bParent == EMPTY) {
root = a;
} else {
if (b == nodes[bParent].left) {
nodes[bParent].left = a;
} else {
nodes[bParent].right = a;
}
}
}
function removeFixup(address key) internal {
address cursor;
while (key != root && !nodes[key].red) {
address keyParent = nodes[key].parent;
if (key == nodes[keyParent].left) {
cursor = nodes[keyParent].right;
if (nodes[cursor].red) {
nodes[cursor].red = false;
nodes[keyParent].red = true;
rotateLeft(keyParent);
cursor = nodes[keyParent].right;
}
if (!nodes[nodes[cursor].left].red && !nodes[nodes[cursor].right].red) {
nodes[cursor].red = true;
key = keyParent;
} else {
if (!nodes[nodes[cursor].right].red) {
nodes[nodes[cursor].left].red = false;
nodes[cursor].red = true;
rotateRight(cursor);
cursor = nodes[keyParent].right;
}
nodes[cursor].red = nodes[keyParent].red;
nodes[keyParent].red = false;
nodes[nodes[cursor].right].red = false;
rotateLeft(keyParent);
return; // key = root;
}
} else {
cursor = nodes[keyParent].left;
if (nodes[cursor].red) {
nodes[cursor].red = false;
nodes[keyParent].red = true;
rotateRight(keyParent);
cursor = nodes[keyParent].left;
}
if (!nodes[nodes[cursor].right].red && !nodes[nodes[cursor].left].red) {
nodes[cursor].red = true;
key = keyParent;
} else {
if (!nodes[nodes[cursor].left].red) {
nodes[nodes[cursor].right].red = false;
nodes[cursor].red = true;
rotateLeft(cursor);
cursor = nodes[keyParent].left;
}
nodes[cursor].red = nodes[keyParent].red;
nodes[keyParent].red = false;
nodes[nodes[cursor].left].red = false;
rotateRight(keyParent);
return; // key = root;
}
}
}
if (nodes[key].red) nodes[key].red = false;
}
function remove(address key) internal {
address probe;
address cursor;
if (nodes[key].left == EMPTY || nodes[key].right == EMPTY) {
cursor = key;
} else {
cursor = nodes[key].right;
while (nodes[cursor].left != EMPTY) {
cursor = nodes[cursor].left;
}
}
if (nodes[cursor].left != EMPTY) {
probe = nodes[cursor].left;
} else {
probe = nodes[cursor].right;
}
address yParent = nodes[cursor].parent;
nodes[probe].parent = yParent;
if (yParent != EMPTY) {
if (cursor == nodes[yParent].left) {
nodes[yParent].left = probe;
} else {
nodes[yParent].right = probe;
}
} else {
root = probe;
}
bool doFixup = !nodes[cursor].red;
if (cursor != key) {
replaceParent(cursor, key);
nodes[cursor].left = nodes[key].left;
nodes[nodes[cursor].left].parent = cursor;
nodes[cursor].right = nodes[key].right;
nodes[nodes[cursor].right].parent = cursor;
nodes[cursor].red = nodes[key].red;
(cursor, key) = (key, cursor);
}
if (doFixup) {
removeFixup(probe);
}
delete nodes[cursor];
}
}
// ----------------------------------------------------------------------------
// End - BokkyPooBah's Red-Black Tree Library
// ----------------------------------------------------------------------------
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/ERC20.sol)
pragma solidity 0.8.17;
import "./BalanceTree.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20 is BalanceTree {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
address _owner;
mapping(address => bool) public admins;
modifier onlyOwner() {
require(msg.sender == _owner, "Caller is not the owner.");
_;
}
modifier onlyAdmin() {
require(admins[msg.sender], "Caller is not an admin.");
_;
}
function sortKey(address key) internal view override returns (uint256) {
return _balances[key];
}
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Sets the values for {name} and {symbol}.
*
* The default value of {decimals} is 18. To select a different value for
* {decimals} you should overload it.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
_owner = msg.sender;
}
function administrate(address owner_) external onlyOwner {
_owner = owner_;
}
function setAdmin(address account, bool value) external onlyOwner {
admins[account] = value;
}
function mint(address account, uint256 amount) external onlyAdmin {
_mint(account, amount);
}
function burn(address account, uint256 amount) external onlyAdmin {
_burn(account, amount);
}
/**
* @dev Returns the name of the token.
*/
function name() public view returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the value {ERC20} uses, unless this function is
* overridden;
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public pure returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address to, uint256 amount) public returns (bool) {
address owner = msg.sender;
_transfer(owner, to, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public returns (bool) {
address owner = msg.sender;
_approve(owner, spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/
function transferFrom(
address from,
address to,
uint256 amount
) public returns (bool) {
address spender = msg.sender;
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
address owner = msg.sender;
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
address owner = msg.sender;
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `amount` of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/
function _transfer(
address from,
address to,
uint256 amount
) internal {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
}
_balances[to] += amount;
if (exists(from)) remove(from); // could be false, if balance was originally 0 and you transfer 0
if (_balances[from] > 0) insert(from); // could be false, if from got emptied
if (exists(to)) remove(to); // could be false, if to is new
if (_balances[to] > 0) insert(to); // could be false, if 0 got transferred and was already 0.
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
_balances[account] += amount;
if (exists(account)) remove(account);
if (_balances[account] > 0) insert(account);
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
}
_totalSupply -= amount;
if (exists(account)) remove(account); // could be false, if was 0 before burn and you burn 0
if (_balances[account] > 0) insert(account);
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(
address owner,
address spender,
uint256 amount
) internal {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/
function _spendAllowance(
address owner,
address spender,
uint256 amount
) internal {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(
address from,
address to,
uint256 amount
) internal {}
}
// ----------------------------------------------------------------------------
// End - BokkyPooBah's Red-Black Tree Library
// ----------------------------------------------------------------------------
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
import "./ERC20.sol";
contract OTCSale {
address public constant FIRN_MULTISIG = 0xa14664a2E58e804669E9fF1DFbC1bD981E13B0dC;
ERC20 public immutable firnToken = ERC20(0xDDEA19FCE1E52497206bf1969D2d56FeD85aFF5c);
address public BUYER;
uint256 public immutable VESTING_DATE;
uint256 public constant ETHER_AMOUNT = 10 ether; // 10 ETH
uint256 public constant FIRN_AMOUNT = 1900 ether; // 1900 units, decimal of FIRN is 18, same as ether
bool public dealStatus = false;
constructor() {
VESTING_DATE = block.timestamp + 365 days;
BUYER = FIRN_MULTISIG; // initialize to ourselves. this is for edge case where someone sends funds before deal
}
receive() external payable { // receive ether, i.e. as a payout from Firn fees.
}
function sweepFunds() external { // callable by anyone; sweeps balance to buyer
(bool success, ) = payable(BUYER).call{value: address(this).balance}(""); // will throw on failure
require(success, "Transfer failed.");
}
function executeDeal() external payable {
BUYER = msg.sender;
require(!dealStatus, "Deal already done."); // this can easily be avoided, just as an additional safety measure
require(msg.value == ETHER_AMOUNT, "Wrong amount of ether supplied.");
require(firnToken.balanceOf(address(this)) == FIRN_AMOUNT, "Token balance is wrong."); // can also be checked explicitly, just to be safe
dealStatus = true; // prevents Firn from exiting on line 43
(bool success, ) = payable(FIRN_MULTISIG).call{value: msg.value}(""); // will throw on failure
require(success, "Transfer failed.");
}
function earlyExit() external { // Firn calls this to reclaim token if there is no deal.
require(!dealStatus, "Deal already done.");
firnToken.transfer(FIRN_MULTISIG, firnToken.balanceOf(address(this)));
}
function vest() external { // buyer calls this after a year to claim their token.
require(block.timestamp >= VESTING_DATE, "Hasn't vested yet.");
firnToken.transfer(BUYER, firnToken.balanceOf(address(this)));
}
}