> ## 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

> [tab] 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.
>
> ![](https://mintcdn.com/trainprotocol/7R9mnlp6I7r6fs1j/images/htlc.png?fit=max\&auto=format\&n=7R9mnlp6I7r6fs1j\&q=85\&s=82d08a18211af771efb4823a1dd12f5f)
>
> ### Core Functions
>
> > [card] userLock ()
> >
> > 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](https://docs.train.tech/protocol/gasless.md)).
>
> > [card] solverLock ()
> >
> > 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] redeemUser () / redeemSolver ()
> >
> > 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] refundUser () / refundSolver ()
> >
> > 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.

> [tab] 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) { ... }
>
> }
> ```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.