Contract Name:
EscrowRegistry
Contract Source Code:
// SPDX-License-Identifier: GPL-3.0-or-later
// Smart Contract for Trustless Escrow at https://trustlessescrow.com
pragma solidity ^0.6.0;
import "./SafeMath.sol";
contract EscrowRegistry {
using SafeMath for uint;
//////////////////////////////////////////////////////////
// Modifiers
modifier onlyOwner() {
require(
msg.sender == owner,
"Only owner can call this."
);
_;
}
//////////////////////////////////////////////////////////
// Custom data types
/*
* 0. Init
* 1. BuyerLocked or SellerLocked
* 2. Locked
* 3. Inactive
*/
enum State {
Init,
BuyerLocked,
SellerLocked,
Locked,
Inactive
}
struct Escrow {
State state;
uint value;
uint buyerDeposit;
uint sellerDeposit;
uint expireAt;
address payable buyer;
address payable seller;
}
//////////////////////////////////////////////////////////
// Constants
uint constant one_hour = 60 * 60;
uint constant one_day = 24 * one_hour;
uint constant one_year = 365 * one_day;
uint constant min_expiration_time = 3 * one_day;
uint constant max_expiration_time = 3 * one_year;
//////////////////////////////////////////////////////////
// Data
address payable owner;
mapping(bytes24 => Escrow) escrows;
uint feePct; // in 0.01% i.e fee = value * fee_pct / 10000
uint feeStore;
//////////////////////////////////////////////////////////
// Events
event EscrowCreated(bytes24 indexed escrowId, bool buyerCreated, address indexed buyer, address indexed seller);
event BuyerAborted(bytes24 indexed escrowId);
event SellerAborted(bytes24 indexed escrowId);
event BuyerConfirmed(bytes24 indexed escrowId);
event SellerConfirmed(bytes24 indexed escrowId);
event Completed(bytes24 indexed escrowId);
event Expired(bytes24 indexed escrowId);
//////////////////////////////////////////////////////////
// Functions
function getUniqueKey() internal view returns (bytes24) {
bytes24 uniqId = bytes24(keccak256(abi.encodePacked(msg.sender, blockhash(block.number - 1))));
while (escrows[uniqId].value != 0) {
uniqId = bytes24(keccak256(abi.encodePacked(uniqId)));
}
return uniqId;
}
constructor() public {
owner = msg.sender;
feePct = 10;
feeStore = 0;
}
function changeOwner(address payable newOwner) public onlyOwner {
owner = newOwner;
}
function setFee(uint newFeePct) public onlyOwner {
feePct = newFeePct;
}
function emptyFeeStore() public onlyOwner {
require(feeStore > 0, "Fee store is empty");
owner.transfer(feeStore);
feeStore = 0;
}
function buyerCreate(uint value, address payable seller, uint sellerDeposit, uint expireIn) public payable {
require(value > 0 && value <= msg.value, "Invalid value");
require(expireIn >= min_expiration_time && expireIn <= max_expiration_time, "Invalid expiration time");
bytes24 escrowId = getUniqueKey();
emit EscrowCreated(escrowId, true, msg.sender, seller);
escrows[escrowId] = Escrow({
expireAt: now + expireIn,
value: value,
buyerDeposit: msg.value - value,
sellerDeposit: sellerDeposit,
buyer: msg.sender,
seller: seller,
state: State.BuyerLocked
});
}
function sellerCreate(uint value, address payable buyer, uint buyerDeposit, uint expireIn) public payable {
require(value > 0, "Invalid value");
require(expireIn >= min_expiration_time && expireIn <= max_expiration_time, "Invalid expiration time");
bytes24 escrowId = getUniqueKey();
emit EscrowCreated(escrowId, false, buyer, msg.sender);
escrows[escrowId] = Escrow({
expireAt: now + expireIn,
value: value,
buyerDeposit: buyerDeposit,
sellerDeposit: msg.value,
buyer: buyer,
seller: msg.sender,
state: State.SellerLocked
});
}
function buyerConfirm(bytes24 escrowId) public payable {
emit BuyerConfirmed(escrowId);
Escrow storage e = escrows[escrowId];
require(e.state == State.SellerLocked, "Invalid state");
require(msg.sender == e.buyer, "Not allowed");
require(msg.value == e.value + e.buyerDeposit, "Invalid amount sent");
e.state = State.Locked;
}
function sellerConfirm(bytes24 escrowId) public payable {
emit SellerConfirmed(escrowId);
Escrow storage e = escrows[escrowId];
require(e.state == State.BuyerLocked, "Invalid state");
require(msg.sender == e.seller, "Not allowed");
require(msg.value == e.sellerDeposit, "Invalid amount sent");
e.state = State.Locked;
}
function buyerAbort(bytes24 escrowId) public {
emit BuyerAborted(escrowId);
Escrow storage e = escrows[escrowId];
require(e.state == State.BuyerLocked, "Invalid state");
require(msg.sender == e.buyer, "Not allowed");
e.state = State.Inactive;
e.buyer.transfer(e.value + e.buyerDeposit);
}
function sellerAbort(bytes24 escrowId) public {
emit SellerAborted(escrowId);
Escrow storage e = escrows[escrowId];
require(e.state == State.SellerLocked, "Invalid state");
require(msg.sender == e.seller, "Not allowed");
e.state = State.Inactive;
e.seller.transfer(e.sellerDeposit);
}
function complete(bytes24 escrowId) public {
emit Completed(escrowId);
Escrow storage e = escrows[escrowId];
require(e.state == State.Locked, "Invalid state");
require(msg.sender == e.buyer, "Not allowed");
uint fee = e.value * feePct / 10000;
e.state = State.Inactive;
feeStore += fee;
e.buyer.transfer(e.buyerDeposit);
e.seller.transfer(e.value - fee + e.sellerDeposit);
}
function expire(bytes24 escrowId) public onlyOwner {
emit Expired(escrowId);
Escrow storage e = escrows[escrowId];
require(e.state == State.Locked, "Invalid state");
require(now > e.expireAt, "Not expired");
e.state = State.Inactive;
owner.transfer(e.value + e.buyerDeposit + e.sellerDeposit);
}
function getEscrow(bytes24 escrowId) public view returns (State state, uint value, uint buyerDeposit,
uint sellerDeposit, uint expireAt, address buyer, address seller) {
Escrow storage e = escrows[escrowId];
require(e.state != State.Init, "Not found");
return (e.state, e.value, e.buyerDeposit, e.sellerDeposit, e.expireAt, e.buyer, e.seller);
}
function isEscrowExpired(bytes24 escrowId) public view returns (bool) {
Escrow storage e = escrows[escrowId];
require(e.state != State.Init, "Not found");
return now > e.expireAt;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}