Automated trailing stop orders with Chainlink automation & 1inch integration
This is a sophisticated Trailing Stop Order System built for the ETH Global Delhi 2025 hackathon. The project implements an advanced DeFi trading mechanism that automatically adjusts stop-loss prices as market conditions move favorably, providing intelligent risk management and profit protection for cryptocurrency traders.
A trailing stop order is an advanced trading strategy that automatically adjusts your stop-loss price as the market moves in your favor. It "trails" behind the current market price, protecting your profits while allowing for continued upside potential.
Key Benefits:
For SELL Orders (Profit Protection):
Stop Price = Current Market Price - Trailing Distance
For BUY Orders (Entry Protection):
Stop Price = Current Market Price + Trailing Distance
TrailingStopOrder Contract (src/extensions/TrailingStopOrder.sol
)
TrailingStopKeeper Contract (src/helpers/TrailingStopKeeper.sol
)
LimitOrderProtocol Contract (src/LimitOrderProtocol.sol
)
Integration Layer
User Creates Order โ TrailingStopOrder Contract โ Chainlink Keeper Monitors โ Price Movement Detected โ Order Executed via 1inch
The system implements sophisticated trailing stop logic with the following key principles:
One-Way Movement Rule
Trailing Distance Calculation
// For SELL orders
newStopPrice = currentPrice - (currentPrice * trailingDistance / 10000)
// For BUY orders
newStopPrice = currentPrice + (currentPrice * trailingDistance / 10000)
Trigger Conditions
// For SELL orders
if (currentPrice <= stopPrice) {
executeOrder(); // Sell at current price
}
// For BUY orders
if (currentPrice >= stopPrice) {
executeOrder(); // Buy at current price
}
Dual Oracle System
TWAP Protection
Gas Optimization
Setup:
Price Movement Simulation:
Day 1: ETH rises to $2,100
Stop Price: $1,940 โ $2,037 โ
(moves UP)
Day 2: ETH rises to $2,200
Stop Price: $2,037 โ $2,134 โ
(moves UP)
Day 3: ETH rises to $2,300
Stop Price: $2,134 โ $2,231 โ
(moves UP)
Day 4: ETH drops to $2,150
Current Price ($2,150) < Stop Price ($2,231) โ ORDER TRIGGERS! ๐จ
Execution: Sell 5 ETH at ~$2,150 = $10,750 USDC
Result: Protected profit even though ETH dropped from $2,300 to $2,150.
Setup:
Price Movement Simulation:
Day 1: ETH drops to $1,900
Stop Price: $2,060 โ $1,957 โ
(moves DOWN)
Day 2: ETH drops to $1,800
Stop Price: $1,957 โ $1,854 โ
(moves DOWN)
Day 3: ETH drops to $1,700
Stop Price: $1,854 โ $1,751 โ
(moves DOWN)
Day 4: ETH rises to $1,800
Current Price ($1,800) > Stop Price ($1,751) โ ORDER TRIGGERS! ๐จ
Execution: Buy ETH at ~$1,800 with 10,000 USDC = ~5.56 ETH
Result: Ensured good entry price even though ETH rose from $1,700 to $1,800.
TrailingStopOrderComprehensiveTest.t.sol
OrderFillMainnetTest.t.sol
git clone <repository-url>
cd delhihackathon
npm install
forge install
forge script script/BuyOrderScenario.s.sol:BuyOrderScenarioScript \
--rpc-url https://eth-sepolia.g.alchemy.com/v2/YOUR_API_KEY \
--private-key $PRIVATE_KEY \
--broadcast \
--verify \
-vvvv
forge script script/SellOrderScenario.s.sol:SellOrderScenarioScript \
--rpc-url https://eth-sepolia.g.alchemy.com/v2/YOUR_API_KEY \
--private-key $PRIVATE_KEY \
--broadcast \
--verify \
-vvvv
forge script script/TrailingStopDemo.s.sol:TrailingStopDemoScript \
--rpc-url https://eth-sepolia.g.alchemy.com/v2/YOUR_API_KEY \
--private-key $PRIVATE_KEY \
--broadcast \
--verify \
-vvvv
# Run comprehensive tests
forge test -vv
# Run specific test suites
forge test --match-contract TrailingStopOrderComprehensiveTest
forge test --match-contract OrderFillMainnetTest
struct TrailingStopConfig {
address makerAssetOracle; // Price oracle for maker asset
address takerAssetOracle; // Price oracle for taker asset
uint256 initialStopPrice; // Initial stop price (in oracle decimals)
uint256 trailingDistance; // Trailing distance in basis points (e.g., 300 = 3%)
uint256 currentStopPrice; // Current stop price (updates dynamically)
uint256 configuredAt; // Timestamp when configured
uint256 lastUpdateAt; // Last update timestamp
uint256 updateFrequency; // Update frequency in seconds
uint256 maxSlippage; // Maximum slippage tolerance
uint256 maxPriceDeviation; // Maximum price deviation
uint256 twapWindow; // TWAP window in seconds
address keeper; // Keeper address
address orderMaker; // Order maker address
OrderType orderType; // BUY or SELL
uint8 makerAssetDecimals; // Maker asset decimals
uint8 takerAssetDecimals; // Taker asset decimals
}
delhihackathon/
โโโ src/
โ โโโ extensions/
โ โ โโโ TrailingStopOrder.sol # Main trailing stop contract
โ โโโ helpers/
โ โ โโโ TrailingStopKeeper.sol # Chainlink automation keeper
โ โโโ interfaces/
โ โ โโโ [9 interface files] # Contract interfaces
โ โโโ libraries/
โ โ โโโ [9 library files] # Utility libraries
โ โโโ LimitOrderProtocol.sol # 1inch protocol integration
โ โโโ OrderMixin.sol # Order management mixin
โโโ script/
โ โโโ BuyOrderScenario.s.sol # Buy order demonstration
โ โโโ SellOrderScenario.s.sol # Sell order demonstration
โ โโโ TrailingStopDemo.s.sol # Comprehensive demo
โ โโโ [2 additional scripts] # Utility scripts
โโโ test/
โ โโโ TrailingStopOrderComprehensiveTest.t.sol # Comprehensive test suite
โ โโโ OrderFillMainnetTest.t.sol # Mainnet integration tests
โโโ lib/
โ โโโ forge-std/ # Foundry standard library
โ โโโ openzeppelin-contracts/ # OpenZeppelin contracts
โ โโโ solidity-utils/ # 1inch solidity utilities
โโโ [configuration files] # Foundry, package, etc.
This project demonstrates several key achievements for ETH Global Delhi 2025:
This Trailing Stop Order System represents a sophisticated DeFi solution that combines advanced trading strategies with robust security measures. The project successfully integrates multiple protocols to create a comprehensive trading tool that provides automated risk management and profit protection for cryptocurrency traders.
The system's innovative approach to trailing stop implementation, combined with its comprehensive security measures and gas-efficient design, makes it a valuable contribution to the DeFi ecosystem and a strong contender for ETH Global Delhi 2025.
Built for ETH Global Delhi 2025 ๐
This project showcases the power of combining multiple DeFi protocols to create innovative trading solutions that provide real value to users while maintaining the highest standards of security and efficiency.
// Compiler Configuration (foundry.toml)
[profile.default]
solc_version = "0.8.23"
via_ir = true // Enable IR-based code generation for gas optimization
optimizer = true // Enable Solidity optimizer
optimizer_runs = 200 // Optimize for 200 function calls (balance between deployment and execution cost)
// package.json - Node.js dependencies
{
"dependencies": {
"@chainlink/contracts": "^1.4.0" // Chainlink price feeds and automation
}
}
# remappings.txt - Solidity import remappings
@1inch/solidity-utils/=lib/solidity-utils/contracts/
@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/
@chainlink/contracts/=node_modules/@chainlink/contracts/
contract TrailingStopOrder is
AmountGetterBase, // Custom amount calculation logic
Pausable, // Emergency pause functionality
Ownable, // Access control
ReentrancyGuard, // Reentrancy protection
IPreInteraction, // Pre-execution hooks
ITakerInteraction // Post-execution hooks
contract LimitOrderProtocol is
EIP712("1inch Limit Order Protocol", "4"), // EIP-712 signature verification
Ownable,
Pausable,
OrderMixin // Core order management logic
abstract contract OrderMixin is
IOrderMixin,
EIP712, // Signature verification
PredicateHelper, // Conditional execution logic
SeriesEpochManager, // Nonce management
Pausable,
OnlyWethReceiver, // ETH handling
PermitAndCall // Permit-based approvals
Why 1inch?
Implementation Details:
// Integration through AmountGetterBase
contract AmountGetterBase is IAmountGetter {
function _getMakingAmount(...) internal view virtual returns (uint256) {
if (extraData.length >= 20) {
// Call external amount getter (1inch integration)
return IAmountGetter(address(bytes20(extraData))).getMakingAmount(...);
} else {
// Use linear formula: makingAmount = (takingAmount * order.makingAmount) / order.takingAmount
return order.makingAmount.mulDiv(takingAmount, order.takingAmount);
}
}
}
Benefits to Our Project:
Why Chainlink Automation?
Implementation Details:
contract TrailingStopKeeper is IAutomationCompatible {
// Batch processing for gas efficiency
function performUpkeep(bytes calldata checkData) external override {
bytes32[] memory orderHashes = abi.decode(checkData, (bytes32[]));
uint256 ordersProcessed = 0;
uint256 ordersUpdated = 0;
for (uint256 i = 0; i < orderHashes.length; i++) {
// Skip if already processed in this batch
if (processedOrders[orderHashes[i]]) continue;
try this._processOrder(orderHashes[i]) {
processedOrders[orderHashes[i]] = true;
ordersUpdated++;
} catch {
// Continue processing other orders
}
}
// Clear processed orders mapping for next batch
for (uint256 i = 0; i < orderHashes.length; i++) {
delete processedOrders[orderHashes[i]];
}
}
}
Benefits to Our Project:
Why Chainlink Price Feeds?
Implementation Details:
function _getCurrentPriceSecure(
AggregatorV3Interface makerAssetOracle,
AggregatorV3Interface takerAssetOracle
) internal view returns (uint256) {
// Get maker asset price with validation
(, int256 makerPrice,, uint256 makerUpdatedAt,) = makerAssetOracle.latestRoundData();
if (makerPrice <= 0) revert InvalidOraclePrice();
// Stale price protection
uint256 makerHeartbeat = oracleHeartbeats[address(makerAssetOracle)];
if (makerHeartbeat == 0) makerHeartbeat = _DEFAULT_ORACLE_TTL;
if (makerUpdatedAt + makerHeartbeat < block.timestamp) revert StaleOraclePrice();
// Get taker asset price with validation
(, int256 takerPrice,, uint256 takerUpdatedAt,) = takerAssetOracle.latestRoundData();
if (takerPrice <= 0) revert InvalidOraclePrice();
// Calculate relative price (maker/taker) scaled to 18 decimals
uint256 price = Math.mulDiv(uint256(makerPrice), 10 ** _PRICE_DECIMALS, uint256(takerPrice));
return price;
}
Benefits to Our Project:
The Challenge: Prevent price manipulation through sophisticated averaging techniques.
Our Solution: Multi-layered TWAP with outlier detection and adaptive windows.
function _calculateSophisticatedTWAP(PriceHistory[] storage history, uint256 twapWindow)
internal view returns (uint256)
{
uint256 cutoffTime = block.timestamp - twapWindow;
// Collect valid prices within window
uint256[] memory validPrices = new uint256[](history.length);
uint256 validCount = 0;
for (uint256 i = 0; i < history.length; i++) {
if (history[i].timestamp >= cutoffTime) {
validPrices[validCount] = history[i].price;
validCount++;
}
}
// Apply outlier detection and median filtering
uint256[] memory filteredPrices = _filterOutliers(validPrices, validCount);
// Calculate median price for additional manipulation protection
uint256 medianPrice = _calculateMedian(filteredPrices, filteredPrices.length);
// Calculate time-weighted average of filtered prices
uint256 timeWeightedPrice = _calculateTimeWeightedAverage(history, cutoffTime, medianPrice);
// Return weighted average of median and time-weighted price for robustness
return (medianPrice + timeWeightedPrice) / 2;
}
Statistical Outlier Detection:
function _filterOutliers(uint256[] memory prices, uint256 count)
internal pure returns (uint256[] memory)
{
if (count <= 2) return prices; // Return original for small datasets
uint256 median = _calculateMedian(prices, count);
uint256[] memory filtered = new uint256[](count);
uint256 filteredCount = 0;
for (uint256 i = 0; i < count; i++) {
uint256 deviation = prices[i] > median
? (prices[i] - median) * _SLIPPAGE_DENOMINATOR / median
: (median - prices[i]) * _SLIPPAGE_DENOMINATOR / median;
// Keep prices within 15% of median
if (deviation <= 1500) {
filtered[filteredCount] = prices[i];
filteredCount++;
}
}
return filtered;
}
Adaptive Window Calculation:
function _updateTWAPMetrics(bytes32 orderHash, uint256 currentPrice) internal {
TWAPMetrics storage metrics = twapMetrics[orderHash];
if (block.timestamp - metrics.lastUpdateTime >= 300 || history.length % 10 == 0) {
if (history.length >= 3) {
// Calculate volatility and price range
uint256 totalDeviation = 0;
for (uint256 i = 0; i < history.length; i++) {
uint256 deviation = currentPrice > history[i].price
? (currentPrice - history[i].price) * _SLIPPAGE_DENOMINATOR / currentPrice
: (history[i].price - currentPrice) * _SLIPPAGE_DENOMINATOR / currentPrice;
totalDeviation += deviation;
}
metrics.volatility = totalDeviation / history.length;
// Calculate adaptive window based on volatility
if (metrics.volatility > 500) { // > 5% volatility
metrics.adaptiveWindow = _DEFAULT_TWAP_WINDOW * 2; // 30 minutes
} else if (metrics.volatility > 200) { // > 2% volatility
metrics.adaptiveWindow = _DEFAULT_TWAP_WINDOW * 3 / 2; // 22.5 minutes
} else {
metrics.adaptiveWindow = _DEFAULT_TWAP_WINDOW; // 15 minutes
}
}
}
}
The Challenge: Handle multiple token decimals (6, 8, 18) without precision loss.
Our Solution: Normalize everything to 18 decimals using OpenZeppelin's Math.mulDiv for precision.
function _calculateMakingAmountWithDecimals(
uint256 takingAmount,
uint256 currentPrice,
TrailingStopConfig memory config
) internal pure returns (uint256) {
// Normalize taking amount to 18 decimals
uint256 normalizedTakingAmount = _normalizeTo18Decimals(takingAmount, config.takerAssetDecimals);
// Calculate making amount in 18 decimals using Math.mulDiv for precision
// Formula: makingAmount = (takingAmount * 1e18) / currentPrice
uint256 makingAmount18 = Math.mulDiv(normalizedTakingAmount, 1e18, currentPrice);
// Convert back to maker asset decimals
return _convertFrom18Decimals(makingAmount18, config.makerAssetDecimals);
}
function _normalizeTo18Decimals(uint256 amount, uint8 decimals) internal pure returns (uint256) {
if (decimals == 18) {
return amount;
} else if (decimals < 18) {
uint256 scaleFactor = 10 ** (18 - decimals);
// Check for overflow before multiplication
if (amount > type(uint256).max / scaleFactor) {
revert InvalidTokenDecimals();
}
return amount * scaleFactor;
} else {
uint256 scaleFactor = 10 ** (decimals - 18);
return amount / scaleFactor;
}
}
The Core Algorithm: Dynamic stop price adjustment based on market movements.
function _calculateTrailingStopPrice(
uint256 currentPrice,
uint256 trailingDistance,
OrderType orderType
) internal pure returns (uint256) {
// Calculate the trailing amount: currentPrice * trailingDistance / 10000
uint256 trailingAmount = (currentPrice * trailingDistance) / _SLIPPAGE_DENOMINATOR;
if (orderType == OrderType.SELL) {
// For sell orders: stop price = current price - trailing amount
return currentPrice - trailingAmount;
} else {
// For buy orders: stop price = current price + trailing amount
return currentPrice + trailingAmount;
}
}
Packed Structs: Minimize storage slots by packing related data.
struct TrailingStopConfig {
AggregatorV3Interface makerAssetOracle; // 20 bytes
AggregatorV3Interface takerAssetOracle; // 20 bytes
uint256 initialStopPrice; // 32 bytes
uint256 trailingDistance; // 32 bytes
uint256 currentStopPrice; // 32 bytes
uint256 configuredAt; // 32 bytes
uint256 lastUpdateAt; // 32 bytes
uint256 updateFrequency; // 32 bytes
uint256 maxSlippage; // 32 bytes
uint256 maxPriceDeviation; // 32 bytes
uint256 twapWindow; // 32 bytes
address keeper; // 20 bytes
address orderMaker; // 20 bytes
OrderType orderType; // 1 byte
uint8 makerAssetDecimals; // 1 byte
uint8 takerAssetDecimals; // 1 byte
// Total: ~400 bytes (13 storage slots)
}
Keeper Batch Updates: Process multiple orders in single transaction.
function performUpkeep(bytes calldata checkData) external override {
bytes32[] memory orderHashes = abi.decode(checkData, (bytes32[]));
// Process multiple orders in single transaction
for (uint256 i = 0; i < orderHashes.length; i++) {
// Skip if already processed in this batch
if (processedOrders[orderHashes[i]]) continue;
try this._processOrder(orderHashes[i]) {
processedOrders[orderHashes[i]] = true;
ordersUpdated++;
} catch {
// Continue processing other orders
}
}
// Clear processed orders mapping for next batch
for (uint256 i = 0; i < orderHashes.length; i++) {
delete processedOrders[orderHashes[i]];
}
}
In-Place Array Cleanup: Remove old entries without creating new arrays.
function _updatePriceHistory(bytes32 orderHash, uint256 price) internal {
PriceHistory[] storage history = priceHistories[orderHash];
uint256 cutoffTime = block.timestamp - twapWindow;
// Enhanced cleanup: remove old entries more efficiently
uint256 writeIndex = 0;
for (uint256 i = 0; i < history.length; i++) {
if (history[i].timestamp >= cutoffTime) {
if (writeIndex != i) {
history[writeIndex] = history[i]; // Move valid entry
}
writeIndex++;
}
}
// Resize array by popping excess elements
while (history.length > writeIndex) {
history.pop();
}
// Add new price entry
history.push(PriceHistory({price: price, timestamp: block.timestamp}));
}
SeriesEpochManager: Efficient nonce management using bit operations.
contract SeriesEpochManager {
mapping(uint256 seriesId => uint256 epoch) private _epochs;
function epoch(address maker, uint96 series) public view returns (uint256) {
// Pack maker address and series into single storage slot
return _epochs[uint160(maker) | (uint256(series) << 160)];
}
function advanceEpoch(uint96 series, uint256 amount) public {
if (amount == 0 || amount > 255) revert AdvanceEpochFailed();
unchecked {
uint256 key = uint160(msg.sender) | (uint256(series) << 160);
uint256 newEpoch = _epochs[key] + amount;
_epochs[key] = newEpoch;
}
}
}
Role-Based Permissions: Different access levels for different functions.
modifier onlyKeeper(bytes32 orderHash) {
TrailingStopConfig memory config = trailingStopConfigs[orderHash];
if (config.keeper != address(0) && config.keeper != msg.sender) {
revert OnlyKeeper();
}
_;
}
// Restrict to 1inch Limit Order Protocol
if (msg.sender != limitOrderProtocol) {
revert OnlyLimitOrderProtocol();
}
Dual Oracle Validation: Cross-check prices from multiple sources.
function _getCurrentPriceSecure(
AggregatorV3Interface makerAssetOracle,
AggregatorV3Interface takerAssetOracle
) internal view returns (uint256) {
// Validate both oracles
(, int256 makerPrice,, uint256 makerUpdatedAt,) = makerAssetOracle.latestRoundData();
(, int256 takerPrice,, uint256 takerUpdatedAt,) = takerAssetOracle.latestRoundData();
// Check for positive prices
if (makerPrice <= 0 || takerPrice <= 0) revert InvalidOraclePrice();
// Check for stale prices
if (makerUpdatedAt + heartbeat < block.timestamp) revert StaleOraclePrice();
if (takerUpdatedAt + heartbeat < block.timestamp) revert StaleOraclePrice();
// Calculate relative price with precision
return Math.mulDiv(uint256(makerPrice), 10 ** _PRICE_DECIMALS, uint256(takerPrice));
}
Real-Time Slippage Calculation: Monitor slippage during execution.
function _calculateSlippage(uint256 expectedPrice, uint256 actualPrice)
internal pure returns (uint256)
{
if (expectedPrice == 0) return 0;
uint256 priceDifference = expectedPrice > actualPrice
? expectedPrice - actualPrice
: actualPrice - expectedPrice;
return (priceDifference * _SLIPPAGE_DENOMINATOR) / expectedPrice;
}
// Validate slippage before execution
uint256 slippage = _calculateSlippage(expectedPrice, currentPrice);
if (slippage > config.maxSlippage) {
revert SlippageExceeded();
}
NonReentrant Guards: Protect against reentrancy attacks.
function takerInteraction(...) external nonReentrant whenNotPaused {
// Critical execution logic protected by reentrancy guard
// and pause functionality
}
The Problem: Solidity doesn't allow try-catch on internal functions.
Our Hacky Solution: Use external self-calls with authorization checks.
function _processOrder(bytes32 orderHash) external returns (bool updated) {
// Only allow self-calls
if (msg.sender != address(this)) {
revert UnauthorizedCaller();
}
// Update trailing stop price with try-catch
try trailingStopOrder.updateTrailingStop(orderHash) {
updated = true;
} catch {
updated = false;
}
return updated;
}
// Call the external function from within the contract
try this._processOrder(orderHash) {
processedOrders[orderHash] = true;
ordersUpdated++;
} catch {
// Order processing failed, continue to next order
}
The Problem: Removing old price history entries efficiently.
Our Hacky Solution: In-place array manipulation with writeIndex.
function _updatePriceHistory(bytes32 orderHash, uint256 price) internal {
PriceHistory[] storage history = priceHistories[orderHash];
uint256 cutoffTime = block.timestamp - twapWindow;
// Use writeIndex to compact array in-place
uint256 writeIndex = 0;
for (uint256 i = 0; i < history.length; i++) {
if (history[i].timestamp >= cutoffTime) {
if (writeIndex != i) {
history[writeIndex] = history[i]; // Move valid entry
}
writeIndex++;
}
}
// Pop excess elements (gas efficient)
while (history.length > writeIndex) {
history.pop();
}
// Add new entry
history.push(PriceHistory({price: price, timestamp: block.timestamp}));
}
The Problem: Minimize storage slots for nonce management.
Our Hacky Solution: Pack address and series into single storage slot.
function epoch(address maker, uint96 series) public view returns (uint256) {
// Pack 160-bit address + 96-bit series into 256-bit key
return _epochs[uint160(maker) | (uint256(series) << 160)];
}
function advanceEpoch(uint96 series, uint256 amount) public {
unchecked {
// Create packed key
uint256 key = uint160(msg.sender) | (uint256(series) << 160);
uint256 newEpoch = _epochs[key] + amount;
_epochs[key] = newEpoch;
}
}
The Problem: Support both external amount getters and linear formulas.
Our Hacky Solution: Use extraData length to determine calculation method.
function _getMakingAmount(...) internal view virtual returns (uint256) {
if (extraData.length >= 20) {
// First 20 bytes contain external getter address
return IAmountGetter(address(bytes20(extraData))).getMakingAmount(
order, extension, orderHash, taker, takingAmount, remainingMakingAmount, extraData[20:]
);
} else {
// Use linear formula: makingAmount = (takingAmount * order.makingAmount) / order.takingAmount
return order.makingAmount.mulDiv(takingAmount, order.takingAmount);
}
}
Gas Snapshot Testing: Monitor gas usage across changes.
function testGasUsageUpdate() public {
// Arrange
bytes32 orderHash = createOrderHash("gas-update");
TrailingStopOrder.TrailingStopConfig memory config = createTrailingStopConfig(...);
vm.prank(maker);
trailingStopOrder.configureTrailingStop(orderHash, config);
// Act
vm.prank(keeper);
uint256 gasStart = gasleft();
trailingStopOrder.updateTrailingStop(orderHash);
uint256 gasUsed = gasStart - gasleft();
// Assert
assertTrue(gasUsed < 100000, "Gas usage should be reasonable");
}
Property-Based Testing: Test with random inputs.
function testFuzzTrailingStopCalculation(
uint256 currentPrice,
uint256 trailingDistance,
TrailingStopOrder.OrderType orderType
) public {
vm.assume(currentPrice > 0 && currentPrice < type(uint128).max);
vm.assume(trailingDistance >= 50 && trailingDistance <= 2000);
uint256 result = trailingStopOrder._calculateTrailingStopPrice(
currentPrice, trailingDistance, orderType
);
if (orderType == TrailingStopOrder.OrderType.SELL) {
assertTrue(result < currentPrice, "Sell stop price should be below current price");
} else {
assertTrue(result > currentPrice, "Buy stop price should be above current price");
}
}
Real-World Integration: Test with actual contracts and tokens.
function testMainnetIntegration() public {
// Fork mainnet at specific block
vm.createSelectFork("https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY");
// Use real contracts
address WBTC = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599;
address USDC = 0xA0b86a33E6441c8C06DDD5d8c4c1B3D4e8c4B3D4;
// Test with real tokens and oracles
deal(WBTC, maker, 1e8); // 1 WBTC
deal(USDC, taker, 120000e6); // 120k USDC
// Execute real order flow
// ...
}
// Network-specific configurations
address constant SEPOLIA_WETH = 0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14;
address constant MAINNET_WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
contract TrailingStopDemoScript is Script {
function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
address deployer = vm.addr(deployerPrivateKey);
vm.startBroadcast(deployerPrivateKey);
// Deploy contracts in correct order
_deployMockContracts();
_deployMainContracts();
vm.stopBroadcast();
// Run comprehensive demos
_demoSellOrder();
_demoBuyOrder();
_demoTriggerOperations();
_demoKeeperOperations();
}
}
# Compile contracts
forge build
# Run tests
forge test -vv
# Run specific test suites
forge test --match-contract TrailingStopOrderComprehensiveTest
# Deploy to testnet
forge script script/TrailingStopDemo.s.sol:TrailingStopDemoScript \
--rpc-url $RPC_URL \
--private-key $PRIVATE_KEY \
--broadcast \
--verify
This project represents a sophisticated fusion of multiple cutting-edge technologies:
The implementation showcases several particularly hacky but effective techniques:
The result is a production-ready DeFi protocol that combines advanced trading strategies with robust security measures, gas-efficient design, and comprehensive testing - making it a strong contender for ETH Global Delhi 2025.
Built with cutting-edge technology for ETH Global Delhi 2025 ๐
This project demonstrates how multiple protocols can be seamlessly integrated to create innovative DeFi solutions that provide real value while maintaining the highest standards of security, efficiency, and user experience.