Smart1inch
An extension of 1inch Fusion+ to facilitate Tezos-EVM bidirectional cross-chain swaps, using SmartPy
Problem Statement
Cross-Chain Bridge Documentation1. What We're BuildingThis project is ademonstration-grade bridgethat lets two parties atomically swap assets between Sepolia (Ethereum) and Ghostnet (Tezos).In the demo the maker sellsSepolia USDCand receivesXTZ.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 escrowson both chains | Simple, formally-understood security; no reliance on optimistic messages or light clients. | |Low gas on Tezos|Hub pattern– singleEscrowHubholds every deal | One contract deployment, per-escrow big-map rows. | |No contract-wide DoS|Per-escrow locksinstead of a global mutex | If a buggy FA2 token hangs, every other escrow still works. | |Cross-chain identity|32-byte hashsha256(pack(tz) ‖ SALT)| - Same fixed constant on both chains.<br>-bytes32fits 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 checksEscrowHub/EscrowSrcevents every N seconds and triggers the next call. | |Maker convenience| OneERC-20 approveonly, no signature | We store the resolver address inallowedSender, 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-proxyEscrowSrcwithCREATE2; holds maker tokens + ETH deposit. | | |EscrowSrc.sol| HTLC escrow (source leg). | | |Resolver.sol| Helper thatatomicallysends ETH deposit, deploys source escrow and calls Limit-Order-Protocol (fillOrderArgs). | | | Limit-Order-Protocol v4 | Used only as a trampoline; we bypass signature withallowedSender. | |Ghostnet|EscrowHub.py| Single Tezos contract for every destination escrow; stores maker/taker Tezos addresses, hashlocks, timelocks, deposits. | | |TimelockLib.py| Calculates matching timestamp windows sodst_withdrawal<src_cancellation. | | |AddressConverter.py| View that convertstz→bytes32and back. | | | (Optional)DutchAuction.py,MerkleValidator.py| Present butdisabledin MVP; ready for future public auction upgrade. |4. End-to-End Flow (Sepolia USDC → Ghostnet XTZ)1. MakerWraps nothing (USDC already ERC-20).CallsUSDC.approve(FACTORY, 5 USDC).Sharessecret0with the relayer.2. Resolver (single transaction)CallsResolver.deploySrcwithimmutables(hashes, USDC address, 5 USDC, timelocks, deposits)anddummyLOP order whereallowedSender = Resolver.Eth value = 0.1 ETH (safety deposit).Contract sequence inside the tx:FACTORY.addressOfEscrowSrcpredicts address.ETH deposit sent to that address.EscrowSrcdeployed → pulls maker's 5 USDC using the approve.3. Relayer (Ghostnet)CallsEscrowHub.create_escrow(value = 0.1 ꜩ deposit).Timelocks mirror Sepolia's but shifted by coordination buffer.4. RevealRelayer postssecret0to 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.
Solution
For the MVP we replaced live push events with pollers:UI (React hook)– every 15 s:ReadsEscrowHub.is_timelock_expiredfor each stage.Reads Sepolia event logs forEscrowSrc.withdrawn.Updates status badges in the browser.Relayer cron– every 10 s:Checks whether both escrows are confirmed (using block numbers).When ready, writessecret0to 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.
Hackathon
ETHGlobal Unite
2025
Contributors
- ananya-bangera
17 contributions
- saRvaGnyA
1 contributions