Contract Source Code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./ownable.sol";
import "./IOmnify.sol";
import "./ierc20metadata.sol";
import "./ierc20.sol";
contract Escrow is Ownable {
event NewContract(string _id, address _owner, uint256 _blockNumber, uint256 _date);
event ContractDeleted(string _id, address _owner, uint256 _blockNumber, uint256 _date);
event BidAccepted(string _id, address _owner, uint256 _blockNumber, uint256 _date);
event NewBid(string _id, address _owner, uint256 _blockNumber, uint256 _date);
struct Bid {
address bidder;
address asset;
uint256 amount;
uint256 dateBid;
bool isAccepted;
bool isCancelled;
bool exists;
}
struct EscrowContract {
string id;
address asset;
uint256 assetAmount;
bool isComplete;
bool isDeleted;
bool exists;
uint256 bidCount;
mapping(uint256 => Bid) bids;
mapping(address => uint256) addressToBidCount;
address owner;
uint256 dateCreated;
uint256 dateDeleted;
}
struct EscrowProfile {
uint256 numOfCompleteContracts;
uint256 numOfDeletedContracts;
uint256 numOfBidsMade;
uint256 numOfBidsReceived;
uint256 contractCount;
mapping(uint256 => string) profileContracts;
}
constructor(address _paramOmnifyAddress, uint8 _paramNativeDecimals, uint256 _paramContractFee)
Ownable(msg.sender)
{
omnifyAddress = _paramOmnifyAddress;
nativeCoinDecimals = _paramNativeDecimals;
contractFee = _paramContractFee;
}
uint8 public nativeCoinDecimals;
uint256 internal MAXUINT = 2 ** 256 - 1;
uint256 public contractFee;
uint256 public numberContracts;
uint256 public amountAssetsInContracts;
uint256 public numberBids;
uint256 public amountAssetsInBids;
uint256 public numberCompletedContracts;
address public feeKeeperAddress;
address public omnifyAddress;
mapping(string => EscrowContract) public contracts;
mapping(address => EscrowProfile) public escrowProfiles;
function getMinAmount(uint8 _decimals) public pure returns (uint256) {
if (_decimals == 0) {
return 1;
}
if (_decimals == 1) {
return 0.1 * (10 ** 1);
}
if (_decimals == 2) {
return 0.01 * (10 ** 2);
}
if (_decimals >= 3) {
uint8 powa = _decimals - 3;
//0.001
return 1 * (10 ** powa);
}
return 1;
}
modifier onlyFeeKeeper(address _sender) {
require(_sender == feeKeeperAddress);
_;
}
function setOmnifyAddress(address _newaddress) external onlyOwner {
omnifyAddress = _newaddress;
}
function setFeeKeeperAddress(address _feeKeeper) external onlyOwner {
feeKeeperAddress = _feeKeeper;
}
function setContractFee(uint256 _fee) external onlyOwner {
contractFee = _fee;
}
function setContractFeeByFeeKeeper(uint256 _fee) external onlyFeeKeeper(msg.sender) {
contractFee = _fee;
}
function _addNumberContracts() internal {
numberContracts++;
}
function _addAmountAssetsInContracts(uint256 _amount) internal {
amountAssetsInContracts = safeAdd(amountAssetsInContracts, _amount);
}
function _addNumberBids() internal {
numberBids++;
}
function _addAmountAssetsInBids(uint256 _amount) internal {
amountAssetsInBids = safeAdd(amountAssetsInBids, _amount);
}
function _addCompletedContracts() internal {
numberCompletedContracts++;
}
function lookupEscrowProfileContractCount(address _profile) public view returns (uint256) {
return escrowProfiles[_profile].contractCount;
}
function lookupEscrowProfileCompleteContracts(address _profile) public view returns (uint256) {
return escrowProfiles[_profile].numOfCompleteContracts;
}
function lookupEscrowProfileDeletedContracts(address _profile) public view returns (uint256) {
return escrowProfiles[_profile].numOfDeletedContracts;
}
function lookupEscrowProfileBidsMade(address _profile) public view returns (uint256) {
return escrowProfiles[_profile].numOfBidsMade;
}
function lookupEscrowProfileBidsReceived(address _profile) public view returns (uint256) {
return escrowProfiles[_profile].numOfBidsReceived;
}
function lookupContractBids(string memory _id) public view returns (Bid[] memory) {
uint256 contractBidCount = contracts[_id].bidCount;
Bid[] memory bids = new Bid[](contractBidCount);
if (contractBidCount > 0) {
for (uint256 i = 0; i < contractBidCount; i++) {
bids[i] = contracts[_id].bids[i + 1];
}
}
return bids;
}
function lookupEscrowProfileContracts(address _profile) public view returns (string[] memory) {
uint256 profileContractCount = escrowProfiles[_profile].contractCount;
string[] memory profileContracts = new string[](profileContractCount);
if (profileContractCount > 0) {
for (uint256 i = 0; i < profileContractCount; i++) {
profileContracts[i] = escrowProfiles[_profile].profileContracts[i + 1];
}
}
return profileContracts;
}
function lookupContractAsset(string memory _id) public view returns (address) {
return contracts[_id].asset;
}
function lookupContractAssetAmount(string memory _id) public view returns (uint256) {
return contracts[_id].assetAmount;
}
function lookupContractIsComplete(string memory _id) public view returns (bool) {
return contracts[_id].isComplete;
}
function lookupContractIsDeleted(string memory _id) public view returns (bool) {
return contracts[_id].isDeleted;
}
function lookupContractExists(string memory _id) public view returns (bool) {
return contracts[_id].exists;
}
function lookupCountractBidCount(string memory _id) public view returns (uint256) {
return contracts[_id].bidCount;
}
function lookupContractOwner(string memory _id) public view returns (address) {
return contracts[_id].owner;
}
function lookupContractDateCreated(string memory _id) public view returns (uint256) {
return contracts[_id].dateCreated;
}
function lookupContractDateDeleted(string memory _id) public view returns (uint256) {
return contracts[_id].dateDeleted;
}
function newContract(string memory _id, address _asset, uint256 _amount) external payable {
require(!contracts[_id].exists);
if (_asset == address(0)) {
uint256 _minAmount = getMinAmount(nativeCoinDecimals);
require(_amount >= _minAmount);
uint256 totalAmount = _amount + contractFee;
require(msg.value == totalAmount);
escrowProfiles[msg.sender].contractCount++;
uint256 profileCount = escrowProfiles[msg.sender].contractCount;
escrowProfiles[msg.sender].profileContracts[profileCount] = _id;
_addNumberContracts();
_addAmountAssetsInContracts(_amount);
_addToContracts(_id, _asset, _amount);
IOmnify mainContract = IOmnify(omnifyAddress);
mainContract.addProfitsFromExternalContract{value: contractFee}();
emit NewContract(_id, msg.sender, block.number, block.timestamp);
} else {
require(msg.value == contractFee);
MYIERC20Metadata coin = MYIERC20Metadata(_asset);
uint8 _decimals = coin.decimals();
uint256 _minAmount = getMinAmount(_decimals);
require(_amount >= _minAmount);
bool success1 = coin.transferFrom(msg.sender, address(this), _amount);
require(success1);
escrowProfiles[msg.sender].contractCount++;
uint256 profileCount = escrowProfiles[msg.sender].contractCount;
escrowProfiles[msg.sender].profileContracts[profileCount] = _id;
_addNumberContracts();
_addAmountAssetsInContracts(_amount);
_addToContracts(_id, _asset, _amount);
IOmnify mainContract = IOmnify(omnifyAddress);
mainContract.addProfitsFromExternalContract{value: contractFee}();
emit NewContract(_id, msg.sender, block.number, block.timestamp);
}
}
function newBid(string memory _contractId, address _asset, uint256 _amount) external payable {
require(contracts[_contractId].exists);
require(!contracts[_contractId].isDeleted);
require(!contracts[_contractId].isComplete);
require(contracts[_contractId].addressToBidCount[msg.sender] == 0);
require(msg.sender != contracts[_contractId].owner);
if (_asset == address(0)) {
uint256 _minAmount = getMinAmount(nativeCoinDecimals);
require(_amount >= _minAmount);
require(msg.value >= _minAmount);
require(msg.value == _amount);
contracts[_contractId].bidCount++;
address contractOwner = contracts[_contractId].owner;
uint256 currentBidCount = contracts[_contractId].bidCount;
contracts[_contractId].bids[currentBidCount].bidder = msg.sender;
contracts[_contractId].bids[currentBidCount].amount = _amount;
contracts[_contractId].bids[currentBidCount].asset = _asset;
contracts[_contractId].bids[currentBidCount].dateBid = block.timestamp;
contracts[_contractId].bids[currentBidCount].exists = true;
contracts[_contractId].addressToBidCount[msg.sender] = currentBidCount;
escrowProfiles[contractOwner].numOfBidsReceived++;
escrowProfiles[msg.sender].numOfBidsMade++;
escrowProfiles[msg.sender].contractCount++;
escrowProfiles[msg.sender].profileContracts[escrowProfiles[msg.sender].contractCount] = _contractId;
_addNumberBids();
_addAmountAssetsInBids(_amount);
emit NewBid(_contractId, msg.sender, block.number, block.timestamp);
} else {
MYIERC20Metadata coin = MYIERC20Metadata(_asset);
uint8 _decimals = coin.decimals();
uint256 _minAmount = getMinAmount(_decimals);
require(_amount >= _minAmount);
bool success1 = coin.transferFrom(msg.sender, address(this), _amount);
require(success1);
contracts[_contractId].bidCount++;
address contractOwner = contracts[_contractId].owner;
uint256 currentBidCount = contracts[_contractId].bidCount;
contracts[_contractId].bids[currentBidCount].bidder = msg.sender;
contracts[_contractId].bids[currentBidCount].amount = _amount;
contracts[_contractId].bids[currentBidCount].asset = _asset;
contracts[_contractId].bids[currentBidCount].dateBid = block.timestamp;
contracts[_contractId].bids[currentBidCount].exists = true;
contracts[_contractId].addressToBidCount[msg.sender] = currentBidCount;
escrowProfiles[contractOwner].numOfBidsReceived++;
escrowProfiles[msg.sender].numOfBidsMade++;
escrowProfiles[msg.sender].contractCount++;
escrowProfiles[msg.sender].profileContracts[escrowProfiles[msg.sender].contractCount] = _contractId;
_addNumberBids();
_addAmountAssetsInBids(_amount);
emit NewBid(_contractId, msg.sender, block.number, block.timestamp);
}
}
function acceptBid(string memory _contractId, uint256 _acceptedBidCount) external {
require(contracts[_contractId].exists);
require(!contracts[_contractId].isDeleted);
require(!contracts[_contractId].isComplete);
require(!contracts[_contractId].bids[_acceptedBidCount].isAccepted);
require(!contracts[_contractId].bids[_acceptedBidCount].isCancelled);
require(contracts[_contractId].bids[_acceptedBidCount].exists);
require(msg.sender == contracts[_contractId].owner);
Bid memory theBid = contracts[_contractId].bids[_acceptedBidCount];
address contractAsset = contracts[_contractId].asset;
address bidAsset = theBid.asset;
address payable owner = payable(contracts[_contractId].owner);
address payable bidder = payable(theBid.bidder);
contracts[_contractId].isComplete = true;
contracts[_contractId].bids[_acceptedBidCount].isAccepted = true;
escrowProfiles[contracts[_contractId].owner].numOfCompleteContracts++;
_addCompletedContracts();
if (bidAsset == address(0)) {
(bool success,) = owner.call{value: theBid.amount}("");
require(success);
} else {
MYIERC20 bidCoin = MYIERC20(bidAsset);
bool success = bidCoin.transfer(contracts[_contractId].owner, theBid.amount);
require(success);
}
if (contractAsset == address(0)) {
(bool success,) = bidder.call{value: contracts[_contractId].assetAmount}("");
require(success);
} else {
MYIERC20 contractCoin = MYIERC20(contractAsset);
bool success = contractCoin.transfer(theBid.bidder, contracts[_contractId].assetAmount);
require(success);
}
emit BidAccepted(_contractId, msg.sender, block.number, block.timestamp);
}
function cancelBid(string memory _contractId, uint256 _bidCount) external {
require(contracts[_contractId].exists);
require(!contracts[_contractId].bids[_bidCount].isCancelled);
require(!contracts[_contractId].bids[_bidCount].isAccepted);
require(msg.sender == contracts[_contractId].bids[_bidCount].bidder);
require(contracts[_contractId].bids[_bidCount].exists);
address bidAsset = contracts[_contractId].bids[_bidCount].asset;
address bidder = contracts[_contractId].bids[_bidCount].bidder;
uint256 bidAmount = contracts[_contractId].bids[_bidCount].amount;
contracts[_contractId].bids[_bidCount].isCancelled = true;
address payable payBidder = payable(bidder);
if (bidAsset == address(0)) {
(bool success,) = payBidder.call{value: bidAmount}("");
require(success);
} else {
MYIERC20 bidCoin = MYIERC20(bidAsset);
bool success = bidCoin.transfer(bidder, bidAmount);
require(success);
}
}
function deleteContract(string memory _contractId) external {
require(contracts[_contractId].exists);
require(!contracts[_contractId].isDeleted);
require(!contracts[_contractId].isComplete);
require(msg.sender == contracts[_contractId].owner);
address payable owner = payable(contracts[_contractId].owner);
address contractAsset = contracts[_contractId].asset;
contracts[_contractId].isDeleted = true;
contracts[_contractId].dateDeleted = block.timestamp;
escrowProfiles[msg.sender].numOfDeletedContracts++;
if (contractAsset == address(0)) {
(bool success,) = owner.call{value: contracts[_contractId].assetAmount}("");
require(success);
} else {
MYIERC20 contractCoin = MYIERC20(contractAsset);
bool success = contractCoin.transfer(contracts[_contractId].owner, contracts[_contractId].assetAmount);
require(success);
}
emit ContractDeleted(_contractId, msg.sender, block.number, block.timestamp);
}
function _addToContracts(string memory _id, address _asset, uint256 _amount) private {
contracts[_id].id = _id;
contracts[_id].asset = _asset;
contracts[_id].assetAmount = _amount;
contracts[_id].dateCreated = block.timestamp;
contracts[_id].owner = msg.sender;
contracts[_id].exists = true;
}
function safeAdd(uint256 _currentAmount, uint256 _amountToBeAdded) internal view returns (uint256) {
uint256 _allowedAmount = MAXUINT - _currentAmount;
if (_amountToBeAdded <= _allowedAmount) {
return _currentAmount + _amountToBeAdded;
}
return _currentAmount;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
abstract contract MyContext {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is MyContext {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface IOmnify {
function addProfitsFromExternalContract() external payable;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./ierc20.sol";
interface MYIERC20Metadata is MYIERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface MYIERC20 {
/**
* @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 Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}