본문 바로가기

BlockChain Developer/Public Blockchain

Hashed Time Lock Contract (HTLC)

[HTLC 정의]
Hashed TimeLock Contract(HTLC) 란, 계약에 일정 시간의 제한을 거는 Time Lock과 일정한 해시 값 제시가 필요한 Hash Lock을 결합한 형태의 Smart Contract로 주로 On-Chain Atomic Swap에서 채택할 수 있는 이기종 네트워크 간 토큰 스왑 방식의 컨트랙트다.
 

[Example]

Hashed Timelock Contract Example

 

 

[Security Risk]
1. BOB이 Secret을 노출시켰을 때 수령인이 지정된 상태의 컨트랙트이기에 안전하다.

2. LockedTime이내에 TokenSwap이 이루어지지 않을 경우 → 비상출금함수로 CA에 토큰이 묶이지 않는다.

3. BOB이 ALICE의 토큰을 lockedTime 직전에 출금해 Alice는 BOB의 토큰을 출금할 수 없는 경우
→ BOB의 HTLC Contract Locked Time은 항시 Alice의 HTLC Contract Locked TIme보다 짧게 설정 필요.
 
[Hashed Time Lock Contract Example]

// SPDX-License-Identifier: Unlicenced;

pragma solidity ^0.8.0;

interface IERC20 {
    function totalSupply() external view returns (uint);
    function balanceOf(address account) external view returns (uint);
    function transfer(address recipient, uint amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint);
    function approve(address spender, uint amount) external returns (bool);
    function transferFrom(
        address sender,
        address recipient,
        uint amount
    ) external returns (bool);
    event Transfer(address indexed from, address indexed to, uint value);
    event Approval(address indexed owner, address indexed spender, uint value);
}

contract HTLC {
    IERC20 public token;
    address public owner;
    address public recipient;
    uint public amount;
    uint public startTime;
    uint public lockTime = 259200 seconds; // 3일
    string public secret;
    bytes32 hashed;

    constructor(address _recipient, bytes32 _hashed, address _token, uint _amount) {
        hashed = _hashed;;
        owner = msg.sender;
        recipient = _recipient;
        amount = _amount;
        token = IERC20(_token);
        startTime = block.timestamp;
    }

    // 토큰 예치 함수; (msg.sender == owner);
    function deposit() public {
        token.transferFrom(msg.sender, address(this), amount);
        token.approve(address(this), amount);
    }

    // 인출; (msg.sender == receipient 호출 함수)
    function withdraw(string memory _secretMsg) external {
        require(keccak256(abi.encode(_secretMsg)) == hashed, "Wrong Secret");
        secret = _secretMsg;
        token.transferFrom(address(this), recipient, amount);
    }

    // 교환 실패시, 비상출금함수; (msg.sender == owner)
    function emergencyRefund() external {
        require(block.timestamp > startTime + lockTime, "Too Early");
        token.transferFrom(address(this), owner, amount);
    }
}