Skip to content

Instantly share code, notes, and snippets.

@korrio
Created January 26, 2023 02:57
Show Gist options
  • Save korrio/8a13da14e9619df979b7eebd41275fd8 to your computer and use it in GitHub Desktop.
Save korrio/8a13da14e9619df979b7eebd41275fd8 to your computer and use it in GitHub Desktop.
EscrowJUTC.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.7;
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
contract EscrowJUTC is Ownable, AccessControl, Pausable {
using SafeERC20 for IERC20;
using ECDSA for bytes32;
struct CaseInfo {
address reporter;
bytes32[] proofs;
uint256 reward;
uint256 startAt;
uint256 endAt;
bool isClose;
}
struct ProofInfo {
uint256 caseId;
address owner;
uint256 amount;
}
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
bytes32 public constant WORKER_ROLE = keccak256("WORKER_ROLE");
address public admin;
address public wallet;
IERC20 public token;
mapping(uint256 => CaseInfo) public caseInfos;
mapping(bytes32 => ProofInfo) public proofInfos;
mapping(address => uint256) public rewards;
event CreateCase(
uint256 indexed caseId,
address indexed reporter,
uint256 reward,
uint256 startAt,
uint256 endAt
);
event AddProof(
uint256 indexed caseId,
uint256 indexed proofId,
address owner,
uint256 amount
);
event CloseCase(uint256 indexed caseId);
event AddReward(uint256[] proofIds, uint256[] amounts);
event ClaimReward(address user, uint256 amount);
constructor(
address _token,
address _admin,
address _wallet
) {
token = IERC20(_token);
admin = _admin;
wallet = _wallet;
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
function createCase(
uint256 _caseId,
address _reporter,
uint256 _reward,
uint256 _startAt,
uint256 _endAt
) external onlyRole(ADMIN_ROLE) {
require(_endAt > _startAt, "Escrow.sol: invalid timestamp.");
CaseInfo storage caseInfo = caseInfos[_caseId];
require(_startAt == 0, "Escrow.sol: duplicate caseId.");
caseInfo.reporter = _reporter;
caseInfo.reward = _reward;
caseInfo.startAt = _startAt;
caseInfo.endAt = _endAt;
emit CreateCase(_caseId, _reporter, _reward, _startAt, _endAt);
}
function addProof(
uint256 _caseId,
uint256 _proofId,
uint256 _amount,
bytes calldata _sign
) external whenNotPaused {
bytes32 message = keccak256(abi.encode(_caseId, _proofId, _amount));
require(
verifySignature(message, _sign),
"Escrow.sol: invalid signature."
);
token.safeTransferFrom(msg.sender, wallet, _amount);
bytes32 proofId = generateProofId(_caseId, _proofId);
ProofInfo storage proofInfo = proofInfos[proofId];
proofInfo.caseId = _caseId;
proofInfo.owner = msg.sender;
proofInfo.amount = _amount;
emit AddProof(_caseId, _proofId, msg.sender, _amount);
}
function closeCase(uint256 _caseId) external onlyRole(ADMIN_ROLE) {
CaseInfo storage caseInfo = caseInfos[_caseId];
require(!caseInfo.isClose, "Escrow.sol: already close.");
require(caseInfo.startAt != 0, "Escrow.sol: no exist case.");
require(caseInfo.endAt <= block.timestamp, "Escrow.sol: not ready.");
caseInfo.isClose = true;
emit CloseCase(_caseId);
}
function addReward(
uint256 _caseId,
uint256[] calldata _proofIds,
uint256[] calldata _amounts
) external onlyRole(WORKER_ROLE) {
require(caseInfos[_caseId].isClose, "Escrow.sol: case still open.");
require(
_proofIds.length == _amounts.length,
"Escrow.sol: invalid index"
);
for (uint256 i; i < _proofIds.length; i++) {
bytes32 proofId = generateProofId(_caseId, _proofIds[i]);
require(
proofInfos[proofId].caseId == _caseId,
"Escrow.sol: invalid proofId."
);
rewards[proofInfos[proofId].owner] += _amounts[i];
}
emit AddReward(_proofIds, _amounts);
}
function claimReward() external {
uint256 reward = rewards[msg.sender];
require(reward > 0, "Escrow.sol: no reward.");
rewards[msg.sender] = 0;
token.safeTransferFrom(wallet, msg.sender, reward);
emit ClaimReward(msg.sender, reward);
}
function setAdminAddress(address _admin) external onlyOwner {
admin = _admin;
}
function setWalletAddress(address _wallet) external onlyOwner {
wallet = _wallet;
}
function setToken(address _token) external onlyOwner {
token = IERC20(_token);
}
function verifySignature(bytes32 _message, bytes calldata _sign)
public
view
returns (bool)
{
bytes32 ethSignedHash = _message.toEthSignedMessageHash();
return (ethSignedHash.recover(_sign) == admin);
}
function generateProofId(uint256 _caseId, uint256 _proofId)
public
pure
returns (bytes32)
{
return keccak256(abi.encode(_caseId, _proofId));
}
function pause() external onlyOwner {
_pause();
}
function unpause() external onlyOwner {
_unpause();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment