pragma solidity ^0.5.0;
pragma experimental ABIEncoderV2;
contract HashTimeLock {
mapping(bytes32 => LockContract) public contracts;
// / - WITHDRAWN
// INVALID - ACTIVE |
// \ - EXPIRED - REFUNDED
uint256 public constant INVALID = 0; // Uninitialized swap -> can go to ACTIVE
uint256 public constant ACTIVE = 1; // Active swap -> can go to WITHDRAWN or EXPIRED
uint256 public constant REFUNDED = 2; // Swap is refunded -> final state.
uint256 public constant WITHDRAWN = 3; // Swap is withdrawn -> final state.
uint256 public constant EXPIRED = 4; // Swap is expired -> can go to REFUNDED
struct LockContract {
uint256 inputAmount;
uint256 outputAmount;
uint256 expiration;
uint256 status;
bytes32 hashLock;
address payable sender;
address payable receiver;
string outputNetwork;
string outputAddress;
}
event Withdraw(
bytes32 indexed id,
bytes32 secret,
bytes32 hashLock,
address indexed sender,
address indexed receiver
);
event Refund(
bytes32 indexed id,
bytes32 hashLock,
address indexed sender,
address indexed receiver
);
event NewContract(
uint256 inputAmount,
uint256 outputAmount,
uint256 expiration,
bytes32 indexed id,
bytes32 hashLock,
address indexed sender,
address indexed receiver,
string outputNetwork,
string outputAddress
);
function newContract(
uint256 outputAmount,
uint256 expiration,
bytes32 hashLock,
address payable receiver,
string calldata outputNetwork,
string calldata outputAddress
) external payable {
address payable sender = msg.sender;
uint256 inputAmount = msg.value;
require(expiration > block.timestamp, 'INVALID_TIME');
require(inputAmount > 0, 'INVALID_AMOUNT');
bytes32 id = sha256(
abi.encodePacked(sender, receiver, inputAmount, hashLock, expiration)
);
require(contracts[id].status == INVALID, "SWAP_EXISTS");
contracts[id] = LockContract(
inputAmount,
outputAmount,
expiration,
ACTIVE,
hashLock,
sender,
receiver,
outputNetwork,
outputAddress
);
emit NewContract(
inputAmount,
outputAmount,
expiration,
id,
hashLock,
sender,
receiver,
outputNetwork,
outputAddress
);
}
function withdraw(bytes32 id, bytes32 secret) external {
LockContract storage c = contracts[id];
require(c.status == ACTIVE, "SWAP_NOT_ACTIVE");
require(c.expiration > block.timestamp, "INVALID_TIME");
require(c.hashLock == sha256(abi.encodePacked(secret)),"INVALID_SECRET");
c.status = WITHDRAWN;
c.receiver.transfer(c.inputAmount);
emit Withdraw(id, secret, c.hashLock, c.sender, c.receiver);
}
function refund(bytes32 id) external {
LockContract storage c = contracts[id];
require(c.status == ACTIVE, "SWAP_NOT_ACTIVE");
require(c.expiration <= block.timestamp, "INVALID_TIME");
c.status = REFUNDED;
c.sender.transfer(c.inputAmount);
emit Refund(id, c.hashLock, c.sender, c.receiver);
}
function getStatus(bytes32[] memory ids) public view returns (uint256[] memory) {
uint256[] memory result = new uint256[](ids.length);
for (uint256 index = 0; index < ids.length; index++) {
result[index] = getSingleStatus(ids[index]);
}
return result;
}
function getSingleStatus(bytes32 id) public view returns (uint256 result) {
LockContract memory tempContract = contracts[id];
if (
tempContract.status == ACTIVE &&
tempContract.expiration < block.timestamp
) {
result = EXPIRED;
} else {
result = tempContract.status;
}
}
}
{
"compilationTarget": {
"HashTimeLock.sol": "HashTimeLock"
},
"evmVersion": "istanbul",
"libraries": {},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"inputAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"outputAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"expiration","type":"uint256"},{"indexed":true,"internalType":"bytes32","name":"id","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"hashLock","type":"bytes32"},{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"string","name":"outputNetwork","type":"string"},{"indexed":false,"internalType":"string","name":"outputAddress","type":"string"}],"name":"NewContract","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"id","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"hashLock","type":"bytes32"},{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"}],"name":"Refund","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"id","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"secret","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"hashLock","type":"bytes32"},{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"}],"name":"Withdraw","type":"event"},{"constant":true,"inputs":[],"name":"ACTIVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"EXPIRED","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"INVALID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"REFUNDED","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"WITHDRAWN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"contracts","outputs":[{"internalType":"uint256","name":"inputAmount","type":"uint256"},{"internalType":"uint256","name":"outputAmount","type":"uint256"},{"internalType":"uint256","name":"expiration","type":"uint256"},{"internalType":"uint256","name":"status","type":"uint256"},{"internalType":"bytes32","name":"hashLock","type":"bytes32"},{"internalType":"address payable","name":"sender","type":"address"},{"internalType":"address payable","name":"receiver","type":"address"},{"internalType":"string","name":"outputNetwork","type":"string"},{"internalType":"string","name":"outputAddress","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"id","type":"bytes32"}],"name":"getSingleStatus","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32[]","name":"ids","type":"bytes32[]"}],"name":"getStatus","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"outputAmount","type":"uint256"},{"internalType":"uint256","name":"expiration","type":"uint256"},{"internalType":"bytes32","name":"hashLock","type":"bytes32"},{"internalType":"address payable","name":"receiver","type":"address"},{"internalType":"string","name":"outputNetwork","type":"string"},{"internalType":"string","name":"outputAddress","type":"string"}],"name":"newContract","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"id","type":"bytes32"}],"name":"refund","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"id","type":"bytes32"},{"internalType":"bytes32","name":"secret","type":"bytes32"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]