MoveSwap is a comprehensive cross-chain atomic swap protocol enabling secure token exchanges between Base (Ethereum L2) and Sui networks. The protocol combines Hash Time Lock Contracts (HTLCs) with 1inch Limit Order Protocol integration for efficient, trustless cross-chain swaps. The project includes both CLI tools and a modern React frontend for seamless user experience.
An atomic swap is a cryptographic technique that allows the exchange of cryptocurrencies from different blockchains without requiring a trusted third party or centralized exchange.
HTLCs are smart contracts that use cryptographic hash functions and time-based conditions to create escrow-like functionality:
- Hash Lock: Funds can only be released by providing the correct secret (preimage)
- Time Lock: Funds are automatically refunded if not claimed within a specified timeframe
In our design, HTLC contracts act as escrow accounts:
- Ethereum HTLC: Holds ETH/ERC20 tokens in smart contract escrow
- Sui HTLC: Holds SUI/custom tokens in Move object escrow
- Conditional Release: Funds only released when cryptographic conditions are met
- Time Protection: Auto-refund prevents indefinite locking
MoveSwap/
βββ contracts/
β βββ ethereum/
β β βββ HTLC.sol # Ethereum Hash Time Lock Contracts contract
β βββ sui/
β βββ sources/
β β βββ htlc.move # Sui Move HTLC contract
β βββ Move.toml # Sui package configuration
β βββ build/ # Compiled Move bytecode
βββ frontend/ # React frontend application
β βββ src/
β β βββ App.jsx # Main application component
β β βββ components/ # UI components
β β βββ assets/ # Static assets
β βββ package.json # Frontend dependencies
β βββ vite.config.js # Vite configuration
βββ test/
β βββ atomic_swap_cli.ts # Interactive CLI for atomic swaps
β βββ test_cli.js # CLI test suite
βββ examples/
β βββ eth_sui_swap.ts # Complete atomic swap implementation
βββ scripts/
β βββ deploy.ts # Contract deployment scripts
βββ config.json # Multi-network configuration
βββ package.json # Main project dependencies
βββ hardhat.config.js # Hardhat configuration
βββ README.md # This documentation
- Cross-Chain Atomic Swaps: Secure token exchanges between Base and Sui
- 1inch Integration: Efficient DEX integration for Base chain swaps
- HTLC Security: Hash Time Lock Contracts ensure atomic execution
- Interactive CLI: Step-by-step swap execution with progress tracking
- Modern Frontend: React-based UI for user-friendly swap interface
- Multi-Network Support: Mainnet and testnet configurations
- Comprehensive Testing: CLI test suite and validation tools
- Node.js: v18 or higher
- npm/yarn: Package manager
- Sui CLI: For Sui blockchain interaction
- Git: Version control
- pnpm: For frontend dependencies (optional)
# Clone the repository
git clone https://github.com/codeakki/MoveSwap.git
cd MoveSwap
# Install main dependencies
npm install
# Install frontend dependencies (optional)
cd frontend
npm install
cd ..
# Copy environment template
cp env.example .env
Create a .env
file with the following variables:
# Base Configuration
BASE_PRIVATE_KEY=your_base_private_key_here
BASE_RPC_URL=https://base-mainnet.g.alchemy.com/v2/YOUR_KEY
# Sui Configuration
SUI_PRIVATE_KEY=your_sui_private_key_here
SUI_RPC_URL=https://sui-mainnet-rpc.allthatnode.com
# Network Selection
NETWORK=mainnet # or base-sepolia for testnet
# Optional: 1inch Integration
DEV_PORTAL_API_TOKEN=your_1inch_api_key
# Gas Configuration
MAX_FEE_PER_GAS=0.001
MAX_PRIORITY_FEE_PER_GAS=0.0001
GAS_LIMIT=300000
# Timelock Configuration (in seconds)
BASE_TIMELOCK_DURATION=7200 # 2 hours
SUI_TIMELOCK_DURATION=3600 # 1 hour
struct Lock {
bytes32 htlc_id; // Unique identifier
bytes32 hashlock; // SHA256 hash of secret
uint256 timelock; // Expiration timestamp
address sender; // Who created the HTLC
address receiver; // Who can claim the funds
uint256 amount; // Amount locked
bytes32 secret; // Revealed secret (initially 0)
bool withdrawn; // Has been claimed
bool refunded; // Has been refunded
uint256 created_at; // Creation timestamp
address token; // Token address (0x0 for ETH)
}
function createHTLC(
bytes32 _htlc_id, // Unique swap identifier
address _receiver, // Who can claim funds
bytes32 _hashlock, // SHA256(secret)
uint256 _timelock, // Expiration timestamp
address _token // Token address (0x0 for ETH)
) external payable
Purpose: Creates a new HTLC escrow account Process:
- Validates timelock is in future
- Locks ETH (if token = 0x0) or transfers ERC20 tokens to contract
- Creates Lock struct with provided parameters
- Emits
HTLCCreated
event
function claimHTLC(
bytes32 _htlc_id, // HTLC identifier
bytes32 _secret // Secret that hashes to hashlock
) external
Purpose: Claims funds from HTLC by revealing secret Process:
- Verifies caller is designated receiver
- Checks HTLC hasn't been withdrawn/refunded
- Verifies timelock hasn't expired
- Validates SHA256(secret) == hashlock
- Transfers funds to receiver
- Stores revealed secret
- Emits
HTLCClaimed
event
function refundHTLC(bytes32 _htlc_id) external
Purpose: Refunds locked funds after timelock expiry Process:
- Verifies caller is original sender
- Checks HTLC hasn't been withdrawn/refunded
- Verifies timelock has expired
- Returns funds to sender
- Emits
HTLCRefunded
event
View functions to query HTLC state and revealed secrets.
struct HTLC has key, store {
id: UID, // Sui object identifier
htlc_id: vector<u8>, // Unique swap identifier
hashlock: vector<u8>, // SHA256 hash of secret
timelock: u64, // Expiration timestamp
sender: address, // Who created the HTLC
receiver: address, // Who can claim funds
amount: u64, // Amount locked (in MIST)
secret: vector<u8>, // Revealed secret (initially empty)
withdrawn: bool, // Has been claimed
refunded: bool, // Has been refunded
created_at: u64, // Creation timestamp
coin: Option<Coin<SUI>> // Locked SUI coins
}
public entry fun create_htlc(
clock: &Clock, // System clock for timestamps
htlc_id: vector<u8>, // Unique identifier
receiver: address, // Who can claim funds
hashlock: vector<u8>, // SHA256 hash of secret
timelock: u64, // Expiration timestamp
payment: Coin<SUI>, // SUI coins to lock
ctx: &mut TxContext // Transaction context
)
Purpose: Creates new HTLC object with locked SUI Process:
- Creates HTLC object with provided parameters
- Stores payment coin in Option<Coin>
- Shares object publicly for access
- Emits
HTLCCreatedEvent
public entry fun claim_with_secret(
clock: &Clock, // System clock
htlc: &mut HTLC, // HTLC object to claim
secret: vector<u8>, // Secret to reveal
ctx: &mut TxContext // Transaction context
)
Purpose: Claims SUI by revealing secret Process:
- Verifies caller is designated receiver
- Checks timelock hasn't expired
- Validates SHA256(secret) == hashlock
- Extracts coin from Option and transfers to receiver
- Stores revealed secret
- Emits
HTLCClaimedEvent
public entry fun refund_htlc(
clock: &Clock, // System clock
htlc: &mut HTLC, // HTLC object to refund
ctx: &mut TxContext // Transaction context
)
Purpose: Refunds SUI after timelock expiry Process:
- Verifies caller is original sender
- Checks timelock has expired
- Returns coin to sender
- Emits
HTLCRefundedEvent
sequenceDiagram
participant Alice as Alice (Base)
participant OneInch as 1inch Limit Order
participant SuiHTLC as Sui HTLC
participant Bob as Bob (SUI)
Note over Alice,Bob: 1. Setup Phase
Alice->>Alice: Generate secret & hashlock
Alice->>Bob: Share hashlock (not secret)
Note over Alice,Bob: 2. Lock Phase
Alice->>OneInch: createBaseLimitOrder(WETHβUSDC, hashlock, timelock)
Bob->>SuiHTLC: create_htlc(id, Alice, hashlock, timelock, SUI)
Note over Alice,Bob: 3. Wait for Finalization
Alice->>Alice: waitForSuiFinalization()
Note over Alice: Sui HTLC transaction finalized
Note over Alice,Bob: 4. Claim Phase (Order matters!)
Alice->>SuiHTLC: claim_with_secret(secret)
Note over SuiHTLC: Secret now revealed on-chain
Alice->>OneInch: executeLimitOrder(secret) [using revealed secret]
Note over Alice,Bob: 5. Result
Note over Alice: Alice gets SUI from Sui HTLC
Note over Alice: Alice gets USDC from 1inch swap
Note over Bob: Bob gets WETH from 1inch swap
// examples/eth_sui_swap.ts
class AtomicSwap {
async performAtomicSwap(
fromChain: 'base' | 'sui',
fromTokenSymbol: string,
toTokenSymbol: string,
amount: string,
fromAddress: string,
toAddress: string
): Promise<boolean> {
// 1. Generate cryptographic materials
const { secret, hashlock, htlcId, baseTimelock, suiTimelock } =
this.generateSwapDetails();
let baseLimitOrder: { order: any, signature: string, orderHash: string } | null = null;
let suiHtlcResult: any = null;
if (fromChain === 'base') {
// 2. Create Base 1inch Limit Order (Alice creates order)
const orderParams: SwapParams = {
fromToken: this.getTokenInfo(fromTokenSymbol),
toToken: this.getTokenInfo(toTokenSymbol),
amount,
fromAddress: this.baseWallet.address,
toAddress,
secretHash: htlcId,
validUntil: baseTimelock
};
baseLimitOrder = await this.createBaseLimitOrder(orderParams);
// 3. Create Sui HTLC (Bob locks SUI)
suiHtlcResult = await this.createSuiHTLC(
htlcId, toAddress, hashlock, suiTimelock, amount
);
} else {
// 3. Create Sui HTLC first (Bob locks SUI)
suiHtlcResult = await this.createSuiHTLC(
htlcId, fromAddress, hashlock, suiTimelock, amount
);
// 2. Create Base 1inch Limit Order (Alice creates order)
const orderParams: SwapParams = {
fromToken: this.getTokenInfo(fromTokenSymbol),
toToken: this.getTokenInfo(toTokenSymbol),
amount,
fromAddress,
toAddress: this.baseWallet.address,
secretHash: htlcId,
validUntil: baseTimelock
};
baseLimitOrder = await this.createBaseLimitOrder(orderParams);
}
// 4. Wait for Sui transaction finalization
await this.suiClient.waitForTransaction({
digest: suiHtlcResult.digest
});
// 5. Claim Sui HTLC (Alice reveals secret)
const suiClaimResult = await this.claimSuiHTLC(
htlcId, secret, hashlock, suiHtlcResult.digest
);
// 6. Execute Base 1inch Limit Order (using revealed secret)
if (baseLimitOrder) {
await this.executeLimitOrder(
baseLimitOrder.order,
baseLimitOrder.signature,
secret
);
}
return true;
}
}
The project includes a comprehensive CLI for step-by-step atomic swap execution:
# Start the interactive CLI
npx ts-node test/atomic_swap_cli.ts
# Initialize a Base to SUI swap
init-swap base ETH USDC 0.001 0x_sender_address 0x_receiver_address
# Create Base 1inch Limit Order
create-base-order
# Create Sui HTLC
create-sui-htlc
# Wait for Sui finalization
wait-for-sui-finalization
# Claim Sui HTLC (reveals secret)
claim-sui-htlc
# Execute Base 1inch Limit Order
execute-base-order
Launch the React frontend for a user-friendly interface:
# Start the frontend development server
cd frontend
npm run dev
# Build for production
npm run build
import { AtomicSwap } from './examples/eth_sui_swap';
const swap = new AtomicSwap();
// Execute Base to SUI swap
await swap.performAtomicSwap(
'base', // from chain
'ETH', // from token (mapped to WETH)
'USDC', // to token
"0.001", // amount
"0x_base_sender_address", // Base sender
"0x_sui_receiver_address" // SUI recipient
);
// Execute SUI to Base swap
await swap.performAtomicSwap(
'sui', // from chain
'ETH', // from token (SUI amount)
'USDC', // to token on Base
"0.00001", // amount
"0x_sui_sender_address", // SUI sender
"0x_base_receiver_address" // Base recipient
);
# Run CLI tests
node test/test_cli.js
# Run Hardhat tests
npm run test:hardhat
# Run all tests
npm test
# Install dependencies
npm install
# Run tests
npm test
# Build TypeScript
npm run build
# Using Hardhat
npx hardhat deploy --network sepolia
# Verify contract
npx hardhat verify --network sepolia DEPLOYED_ADDRESS
# Build Move package
sui move build
# Deploy to testnet
sui client publish --gas-budget 100000000
# Update package ID in config.json
The project uses a comprehensive config.json
for multi-network support:
{
"base": {
"rpc": "https://base-mainnet.g.alchemy.com/v2/YOUR_KEY",
"htlcContract": "0x_deployed_htlc_address",
"limitOrderProtocol": "0x111111125421ca6dc452d289314280a0f8842a65",
"weth": "0x4200000000000000000000000000000000000006",
"usdc": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"privateKey": "YOUR_BASE_PRIVATE_KEY",
"chainId": 8453
},
"sui": {
"packageId": "0x_deployed_sui_package_id",
"privateKey": "YOUR_SUI_PRIVATE_KEY"
},
"tokens": {
"ETH": "0x0000000000000000000000000000000000000000",
"WETH": "0x4200000000000000000000000000000000000006",
"USDC": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
},
"networks": {
"mainnet": { /* mainnet configuration */ },
"base-sepolia": { /* testnet configuration */ }
}
}
The project includes a modern React frontend built with Vite and Tailwind CSS:
- Interactive Swap Interface: User-friendly token swap interface
- Real-time Progress Tracking: Visual progress indicators for swap steps
- Multi-Chain Support: Support for Base and Sui networks
- Responsive Design: Mobile-friendly interface
- Modern UI Components: Built with Radix UI and Tailwind CSS
frontend/
βββ src/
β βββ App.jsx # Main application component
β βββ components/ # Reusable UI components
β β βββ ui/ # Base UI components (Button, Card, etc.)
β βββ hooks/ # Custom React hooks
β βββ lib/ # Utility functions
β βββ assets/ # Static assets
βββ package.json # Frontend dependencies
βββ vite.config.js # Vite configuration
# Start development server
cd frontend
npm run dev
# Build for production
npm run build
# Preview production build
npm run preview
- Base timelock: Longer (2 hours) - gives time for Sui claim
- Sui timelock: Shorter (1 hour) - forces Alice to claim first
- Critical: Sui must be claimed before Base to reveal secret
- Test with small amounts first
- Monitor gas prices before execution
- Verify contract addresses before deploying
- Use proper error handling for network issues
- Implement retry mechanisms for failed transactions
- Timelock ordering: Sui timelock must be shorter than Base
- Gas estimation: Set explicit gas limits for reliable execution
- Secret management: Never share secrets before both HTLCs are created
- Network delays: Account for block confirmation times
- 1inch Integration: Ensure proper order validation and gas estimation
The implementation includes comprehensive logging with:
- π Transaction hashes for all operations
- π Block explorer links (BaseScan & Sui Explorer)
- β½ Gas usage tracking
- π° Amount and balance verification
- π― Step-by-step progress indicators
- π CLI progress tracking with detailed logs
- Fork the repository
- Create feature branch:
git checkout -b feature/new-feature
- Commit changes:
git commit -am 'Add new feature'
- Push to branch:
git push origin feature/new-feature
- Submit a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- 1inch Protocol for Limit Order Protocol and DEX integration
- Sui Foundation for Move language and blockchain infrastructure
- Base for Ethereum L2 infrastructure
- OpenZeppelin for secure contract libraries
- Radix UI for accessible UI components