Smart1inch

An extension of 1inch Fusion+ to facilitate Tezos-EVM bidirectional cross-chain swaps, using SmartPy

Smart1inch

Created At

Unite Defi

Project Description

Cross-Chain Bridge Documentation

1. What We're Building

This project is a demonstration-grade bridge that lets two parties atomically swap assets between Sepolia (Ethereum) and Ghostnet (Tezos).

  • In the demo the maker sells Sepolia USDC and receives XTZ.
  • The opposite direction (XTZ → USDC) is fully wired but not shown live.

We kept every moving part identical to 1inch Fusion + wherever possible, then replaced anything heavy (Dutch auctions, Merkle leaves, WebSockets) with the lightest alternative that still shows the cross-chain logic.

2. Why the Architecture Looks Like This

| Need | Design choice | Rationale | |------|---------------|-----------| | Hard atomicity across L1 chains | Hash-timelocked escrows on both chains | Simple, formally-understood security; no reliance on optimistic messages or light clients. | | Low gas on Tezos | Hub pattern – single EscrowHub holds every deal | One contract deployment, per-escrow big-map rows. | | No contract-wide DoS | Per-escrow locks instead of a global mutex | If a buggy FA2 token hangs, every other escrow still works. | | Cross-chain identity | 32-byte hash sha256(pack(tz) ‖ SALT) | - Same fixed constant on both chains.<br>- bytes32 fits Solidity slots; Tezos re-derives to stop spoofing. | | Off-chain coordination | Relayer + Resolver pattern | 1inch's model: relayer matches orders, resolver pays gas and earns spread. | | No back-end servers for demo | Observer polling (cron) instead of WebSockets | Easiest to host: a CLI watcher checks EscrowHub / EscrowSrc events every N seconds and triggers the next call. | | Maker convenience | One ERC-20 approve only, no signature | We store the resolver address in allowedSender, so LOP skips maker sig verification. | | Judge-friendly codebase | Keep Solidity and SmartPy separate; no diagram assets | Reviewers can open each contract in a block-explorer and follow the README steps verbatim. |

3. Main Components

| Chain | Contract | Short role | |-------|----------|------------| | Sepolia | EscrowFactory.sol | Deploys minimal-proxy EscrowSrc with CREATE2; holds maker tokens + ETH deposit. | | | EscrowSrc.sol | HTLC escrow (source leg). | | | Resolver.sol | Helper that atomically sends ETH deposit, deploys source escrow and calls Limit-Order-Protocol (fillOrderArgs). | | | Limit-Order-Protocol v4 | Used only as a trampoline; we bypass signature with allowedSender. | | Ghostnet | EscrowHub.py | Single Tezos contract for every destination escrow; stores maker/taker Tezos addresses, hashlocks, timelocks, deposits. | | | TimelockLib.py | Calculates matching timestamp windows so dst_withdrawal < src_cancellation. | | | AddressConverter.py | View that converts tzbytes32 and back. | | | (Optional) DutchAuction.py, MerkleValidator.py | Present but disabled in MVP; ready for future public auction upgrade. |

4. End-to-End Flow (Sepolia USDC → Ghostnet XTZ)

1. Maker

  • Wraps nothing (USDC already ERC-20).
  • Calls USDC.approve(FACTORY, 5 USDC).
  • Shares secret0 with the relayer.

2. Resolver (single transaction)

  • Calls Resolver.deploySrc with
    • immutables (hashes, USDC address, 5 USDC, timelocks, deposits)
    • and dummy LOP order where allowedSender = Resolver.
  • Eth value = 0.1 ETH (safety deposit).
  • Contract sequence inside the tx:
    1. FACTORY.addressOfEscrowSrc predicts address.
    2. ETH deposit sent to that address.
    3. EscrowSrc deployed → pulls maker's 5 USDC using the approve.

3. Relayer (Ghostnet)

  • Calls EscrowHub.create_escrow (value = 0.1 ꜩ deposit).
  • Timelocks mirror Sepolia's but shifted by coordination buffer.

4. Reveal

  • Relayer posts secret0 to IPFS / Slack channel.
  • Resolver withdraws on Ghostnet (gets XTZ + ꜩ deposit), then on Sepolia (gets USDC + ETH deposit).

Fallbacks (publicWithdraw, cancel) work automatically after their timestamps; deposits reward the caller who finishes the swap.

How it's Made

For the MVP we replaced live push events with pollers:

  • UI (React hook) – every 15 s:

    • Reads EscrowHub.is_timelock_expired for each stage.
    • Reads Sepolia event logs for EscrowSrc.withdrawn.
    • Updates status badges in the browser.
  • Relayer cron – every 10 s:

    • Checks whether both escrows are confirmed (using block numbers).
    • When ready, writes secret0 to a pinned gist + calls Ghostnet withdraw if resolver stalled.
  • Resolver bot – every 10 s:

    • Watches IPFS CID list; when a new secret appears, triggers both withdrawals.

Polling is stateless, HTTP-only, so the demo runs fine on GitHub Pages + a free cron job without needing WebSocket infra.

background image mobile

Join the mailing list

Get the latest news and updates