> ## Documentation Index
> Fetch the complete documentation index at: https://docs.train.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# Core

> The core HTLC contract interface of the TRAIN protocol

<Tabs>
  <Tab title="Overview">
    The core of the protocol relies on the HTLC contract (`Train`). This contract is responsible for locking and unlocking funds based on specific conditions. All locks are keyed by the **hashlock** — `sha256(secret)` — and every network implements the same lifecycle.

    <Frame>
      <img width="300" src="https://mintcdn.com/trainprotocol/7R9mnlp6I7r6fs1j/images/htlc.png?fit=max&auto=format&n=7R9mnlp6I7r6fs1j&q=85&s=82d08a18211af771efb4823a1dd12f5f" data-path="images/htlc.png" />
    </Frame>

    ### Core Functions

    <Card title="userLock ()" icon="lock" horizontal="true">
      User creates an HTLC on the source chain with a hashlock derived from the recoverable secret generation mechanism. This locks the user's funds for the specified Solver. A gasless variant, `userLockFor`, lets a relayer fund and submit the lock attributed to the user (see [Gasless Flows](/protocol/gasless)).
    </Card>

    <Card title="solverLock ()" icon="lock-keyhole" horizontal="true">
      Solver locks matching funds on the destination chain with the same hashlock, plus a reward secured by a `rewardTimelock`. Solver locks are keyed by **(hashlock, solver address)** — each solver gets exactly one permanent slot per hashlock (`SolverLockAlreadyExists` guard), so a blind retry can never double-fund the same swap, and no one can squat another solver's slot.
    </Card>

    <Card title="redeemUser () / redeemSolver ()" icon="lock-open" horizontal="true">
      Anyone holding the secret can redeem: `redeemSolver` releases the Solver's destination lock to the user (paying the reward), and `redeemUser` releases the user's source lock to the Solver. Redeeming reveals the secret on-chain, which atomically unlocks both sides.
    </Card>

    <Card title="refundUser () / refundSolver ()" icon="arrow-down-to-line" horizontal="true">
      Return locked funds to the `refundTo` address after the timelock expires if the exchange wasn't completed. The user lock's recipient may refund at any time. A solver whose lock was refunded cannot re-lock the same hashlock from the same address — a retry requires a different solver address.
    </Card>
  </Tab>

  <Tab title="Code">
    Implementation of the HTLC core contracts can be found in the [TRAIN contracts repo](https://github.com/trainprotocol/contracts). `main` carries every production-track network: [EVM](https://github.com/trainprotocol/contracts/tree/main/chains/evm), [Starknet](https://github.com/trainprotocol/contracts/tree/main/chains/starknet), [Fuel](https://github.com/trainprotocol/contracts/tree/main/chains/fuel), [Solana](https://github.com/trainprotocol/contracts/tree/main/chains/solana), and [Aztec](https://github.com/trainprotocol/contracts/tree/main/chains/aztec). Early-stage networks live on their own `main-add-<network>` branch, e.g. [Bitcoin](https://github.com/trainprotocol/contracts/tree/main-add-bitcoin) and [Sui](https://github.com/trainprotocol/contracts/tree/main-add-sui).

    The condensed EVM interface (see [`Train.sol`](https://github.com/TrainProtocol/contracts/blob/main/chains/evm/solidity/src/Train.sol) for the full implementation):

    ```solidity theme={null}
    contract Train {

      /// @dev The hashlock is the lock identifier: sha256(abi.encodePacked(secret)).
      /// User locks are keyed by hashlock; solver locks are keyed by (hashlock, solver) —
      /// at most ONE lock per solver per hashlock, ever (the retry / double-funding guard).

      enum LockStatus { Empty, Pending, Refunded, Redeemed }

      /// @notice Parameters for creating a user lock.
      struct UserLockParams {
        bytes32 hashlock;
        uint256 amount;
        uint256 rewardAmount;        // reward expected on the destination side (informational)
        uint48 timelockDelta;        // refund becomes possible at startTime + timelockDelta
        uint48 rewardTimelockDelta;  // echoed for the solver (informational)
        uint48 quoteExpiry;          // lock creation reverts after this timestamp (QuoteExpired)
        address recipient;           // receives the payout on redeem
        address refundTo;            // receives the funds on refund
        address token;               // address(0) for native
        address payoutCurve;         // optional payout curve, address(0) for full payout
        bytes payoutCurveData;
        string rewardToken;
        string rewardRecipient;
        string srcChain;
      }

      /// @notice Parameters for creating a solver lock.
      struct SolverLockParams {
        bytes32 hashlock;
        uint256 amount;
        uint256 reward;              // escrowed alongside the amount
        uint48 timelockDelta;
        uint48 rewardTimelockDelta;  // reward routes to rewardRecipient before this, to the redeemer after
        address recipient;
        address rewardRecipient;
        address refundTo;
        address token;
        address rewardToken;
        address payoutCurve;
        bytes payoutCurveData;
        string srcChain;
      }

      /// @notice Cross-chain destination details (logged only, not stored).
      struct DestinationInfo {
        string dstChain;
        string dstAddress;
        uint256 dstAmount;
        string dstToken;
      }

      /// @dev Emitted when a user creates a lock.
      event UserLocked(
        bytes32 indexed hashlock,
        address indexed sender,
        address indexed recipient,
        string srcChain,
        address token,
        uint256 amount,
        uint48 timelock,
        address payoutCurve,
        string dstChain,
        string dstAddress,
        uint256 dstAmount,
        string dstToken,
        uint256 rewardAmount,
        string rewardToken,
        string rewardRecipient,
        uint48 rewardTimelockDelta,
        uint48 quoteExpiry,
        bytes userData,
        bytes solverData
      );

      /// @dev Emitted when a solver creates a lock. `sender` is the solver —
      /// at most one lock per (hashlock, solver).
      event SolverLocked(
        bytes32 indexed hashlock,
        address indexed sender,
        address indexed recipient,
        string srcChain,
        address token,
        uint256 amount,
        uint256 reward,
        address rewardToken,
        address rewardRecipient,
        uint48 timelock,
        uint48 rewardTimelock,
        address payoutCurve,
        string dstChain,
        string dstAddress,
        uint256 dstAmount,
        string dstToken,
        bytes data
      );

      /// @dev Emitted on redemption. `rewardTo` is the rewardRecipient before
      /// rewardTimelock, and the redeemer after it.
      event UserRedeemed(bytes32 indexed hashlock, address redeemer, uint256 secret, uint256 payout, uint256 excess);
      event SolverRedeemed(
        bytes32 indexed hashlock,
        address indexed solver,
        address redeemer,
        uint256 secret,
        uint256 payout,
        uint256 excess,
        address rewardTo,
        uint256 reward
      );

      /// @dev Emitted on refund (full amount — and for solver locks, the reward — returns to refundTo).
      event UserRefunded(bytes32 indexed hashlock, address refundTo, uint256 amount);
      event SolverRefunded(bytes32 indexed hashlock, address indexed solver, address refundTo, uint256 amount, uint256 reward);

      /// @notice Create a user lock to initiate a cross-chain swap (caller funds the lock).
      /// @dev Payable: send `params.amount` as msg.value for native locks; 0 for ERC20 locks.
      function userLock(UserLockParams calldata params, DestinationInfo calldata dst, bytes calldata userData, bytes calldata solverData) external payable { ... }

      /// @notice Permissionless, ERC20-only variant: funds are pulled from msg.sender but the
      /// lock is attributed to `user`. This is the gasless TrainRouter's forwarding target.
      function userLockFor(address user, UserLockParams calldata params, DestinationInfo calldata dst, bytes calldata userData, bytes calldata solverData) external { ... }

      /// @notice Create a solver lock (amount + reward escrowed). Reverts with
      /// SolverLockAlreadyExists if this solver already used this hashlock.
      function solverLock(SolverLockParams calldata params, DestinationInfo calldata dst, bytes calldata data) external payable { ... }

      /// @notice Redeem a user lock with the secret. Recipient gets the payout
      /// (curve-computed, full amount when no curve); any excess goes to refundTo.
      function redeemUser(bytes32 hashlock, uint256 secret) external { ... }

      /// @notice Redeem a specific solver's lock with the secret. The reward goes to
      /// rewardRecipient before rewardTimelock, and to the caller after it.
      function redeemSolver(bytes32 hashlock, address solver, uint256 secret) external { ... }

      /// @notice Refund a user lock: recipient may refund anytime, others after the timelock.
      function refundUser(bytes32 hashlock) external { ... }

      /// @notice Refund a solver's lock after its timelock (amount + reward to refundTo).
      function refundSolver(bytes32 hashlock, address solver) external { ... }

      /// @notice Lock lookups.
      function getUserLock(bytes32 hashlock) external view returns (UserLock memory) { ... }
      function getSolverLock(bytes32 hashlock, address solver) external view returns (SolverLock memory) { ... }

      /// @notice Windowed enumeration of a user's historical locks (scale-safe).
      function getUserLockHashes(address user, uint256 offset, uint256 limit) external view returns (bytes32[] memory, uint256 total) { ... }
      function getUserLocks(address user, uint256 offset, uint256 limit) external view returns (UserLock[] memory, uint256 total) { ... }

    }
    ```
  </Tab>
</Tabs>
