From 9c24756f6ab05fefe3bd3b7af9f3e721d4046c8f Mon Sep 17 00:00:00 2001 From: ypszn Date: Sun, 24 Aug 2025 00:20:39 +0100 Subject: [PATCH 1/2] feat: add comprehensive developer guides and best practices --- docs/developer-guides/common-patterns.md | 691 ++++++++++++++++++ .../complete-blended-app-tutorial.md | 384 ++++++++++ .../developer-guides/troubleshooting-guide.md | 570 +++++++++++++++ 3 files changed, 1645 insertions(+) create mode 100644 docs/developer-guides/common-patterns.md create mode 100644 docs/developer-guides/complete-blended-app-tutorial.md create mode 100644 docs/developer-guides/troubleshooting-guide.md diff --git a/docs/developer-guides/common-patterns.md b/docs/developer-guides/common-patterns.md new file mode 100644 index 0000000..5cecaff --- /dev/null +++ b/docs/developer-guides/common-patterns.md @@ -0,0 +1,691 @@ +--- +title: Common Patterns & Best Practices +sidebar_position: 4 +--- + +Common Patterns & Best Practices +--- + +This guide covers common development patterns, best practices, and real-world examples for building on Fluent. Whether you're building simple contracts or complex blended applications, these patterns will help you write more efficient, secure, and maintainable code. + +:::prerequisite + +Before diving into these patterns, make sure you have: + +- Basic understanding of [Rust smart contracts](./smart-contracts/rust.mdx) +- Familiarity with [Solidity development](./smart-contracts/solidity.mdx) +- Experience with [blended applications](./building-a-blended-app/README.md) +- `gblend` tool installed and configured + +::: + +## Table of Contents + +- [Error Handling Patterns](#error-handling-patterns) +- [Gas Optimization](#gas-optimization) +- [Security Best Practices](#security-best-practices) +- [Testing Strategies](#testing-strategies) +- [Debugging Techniques](#debugging-techniques) +- [Performance Optimization](#performance-optimization) +- [Common Anti-Patterns](#common-anti-patterns) + +## Error Handling Patterns + +### Rust Contract Error Handling + +Proper error handling is crucial for robust smart contracts. Here are effective patterns for Rust contracts: + +#### 1. Custom Error Types + +```rust +#![cfg_attr(target_arch = "wasm32", no_std)] +extern crate alloc; + +use alloc::string::String; +use fluentbase_sdk::{ + basic_entrypoint, derive::{router, Contract}, SharedAPI, + U256, Address, address +}; + +#[derive(Contract)] +struct ErrorHandlingExample { + sdk: SDK, +} + +pub trait ErrorAPI { + fn safe_divide(&self, numerator: U256, denominator: U256) -> Result; + fn require_positive(&self, value: U256) -> Result; + fn validate_address(&self, addr: Address) -> Result; +} + +#[router(mode = "solidity")] +impl ErrorAPI for ErrorHandlingExample { + + #[function_id("safeDivide(uint256,uint256)")] + fn safe_divide(&self, numerator: U256, denominator: U256) -> Result { + if denominator.is_zero() { + return Err("Division by zero not allowed".to_string()); + } + Ok(numerator / denominator) + } + + #[function_id("requirePositive(uint256)")] + fn require_positive(&self, value: U256) -> Result { + if value.is_zero() { + return Err("Value must be greater than zero".to_string()); + } + Ok(true) + } + + #[function_id("validateAddress(address)")] + fn validate_address(&self, addr: Address) -> Result { + // Check for zero address + if addr == address!("0000000000000000000000000000000000000000") { + return Err("Invalid address: zero address not allowed".to_string()); + } + Ok(true) + } +} + +basic_entrypoint!(ErrorHandlingExample); +``` + +#### 2. Panic with Descriptive Messages + +For critical errors that should halt execution: + +```rust +#[function_id("criticalOperation(uint256)")] +fn critical_operation(&self, value: U256) -> U256 { + if value.is_zero() { + panic!("Critical operation failed: value cannot be zero"); + } + + if value > U256::from(1000) { + panic!("Critical operation failed: value exceeds maximum limit"); + } + + value * U256::from(2) +} +``` + +### Solidity Error Handling + +#### 1. Custom Errors (Gas Efficient) + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +contract ErrorHandlingExample { + // Custom errors are more gas efficient than require statements + error InsufficientBalance(uint256 available, uint256 required); + error InvalidAddress(address provided); + error ValueTooHigh(uint256 value, uint256 max); + + mapping(address => uint256) public balances; + + function withdraw(uint256 amount) external { + uint256 balance = balances[msg.sender]; + + if (balance < amount) { + revert InsufficientBalance(balance, amount); + } + + if (amount > 1000 ether) { + revert ValueTooHigh(amount, 1000 ether); + } + + balances[msg.sender] = balance - amount; + // Transfer logic here + } + + function setBalance(address user, uint256 amount) external { + if (user == address(0)) { + revert InvalidAddress(user); + } + + balances[user] = amount; + } +} +``` + +#### 2. Require Statements with Custom Messages + +```solidity +function transfer(address to, uint256 amount) external { + require(to != address(0), "Transfer to zero address"); + require(amount > 0, "Amount must be greater than zero"); + require(balances[msg.sender] >= amount, "Insufficient balance"); + + balances[msg.sender] -= amount; + balances[to] += amount; +} +``` + +## Gas Optimization + +### Rust Contract Optimization + +#### 1. Efficient Storage Patterns + +```rust +#[derive(Contract)] +struct OptimizedStorage { + sdk: SDK, +} + +pub trait StorageAPI { + fn set_value(&mut self, key: U256, value: U256); + fn get_value(&self, key: U256) -> U256; + fn batch_set(&mut self, keys: Vec, values: Vec); +} + +#[router(mode = "solidity")] +impl StorageAPI for OptimizedStorage { + + #[function_id("setValue(uint256,uint256)")] + fn set_value(&mut self, key: U256, value: U256) { + // Use efficient storage patterns + self.sdk.set_storage(key, value); + } + + #[function_id("getValue(uint256)")] + fn get_value(&self, key: U256) -> U256 { + self.sdk.get_storage(key) + } + + #[function_id("batchSet(uint256[],uint256[])")] + fn batch_set(&mut self, keys: Vec, values: Vec) { + // Batch operations reduce gas costs + for (key, value) in keys.iter().zip(values.iter()) { + self.sdk.set_storage(*key, *value); + } + } +} + +basic_entrypoint!(OptimizedStorage); +``` + +#### 2. Memory Management + +```rust +// Avoid unnecessary allocations +#[function_id("efficientString()")] +fn efficient_string(&self) -> String { + // Pre-allocate with known size when possible + let mut result = String::with_capacity(100); + result.push_str("Hello"); + result.push_str(" World"); + result +} + +// Use references when possible +#[function_id("processArray(uint256[])")] +fn process_array(&self, data: &[U256]) -> U256 { + let mut sum = U256::zero(); + for item in data { + sum += *item; + } + sum +} +``` + +### Solidity Gas Optimization + +#### 1. Storage Layout Optimization + +```solidity +contract GasOptimized { + // Pack related variables together + struct User { + uint128 balance; // 16 bytes + uint64 lastUpdate; // 8 bytes + uint64 userId; // 8 bytes + // Total: 32 bytes (one storage slot) + } + + // Use uint256 for single variables to avoid packing overhead + uint256 public totalSupply; + + // Use bytes32 for fixed-size data + mapping(address => bytes32) public userData; + + // Use uint8 for small enums + enum Status { Pending, Active, Inactive } + mapping(address => Status) public userStatus; +} +``` + +#### 2. Function Optimization + +```solidity +contract OptimizedFunctions { + // Use external for functions only called externally + function externalFunction() external pure returns (uint256) { + return 42; + } + + // Use public for functions that need internal access + function publicFunction() public pure returns (uint256) { + return externalFunction(); + } + + // Avoid unnecessary storage reads + function optimizedRead() external view returns (uint256) { + // Cache storage reads + uint256 value = storageValue; + return value + value; // Use cached value twice + } + + // Use unchecked for arithmetic that can't overflow + function uncheckedIncrement(uint256 x) external pure returns (uint256) { + unchecked { + return x + 1; + } + } +} +``` + +## Security Best Practices + +### 1. Access Control + +#### Rust Implementation + +```rust +#[derive(Contract)] +struct SecureContract { + sdk: SDK, +} + +pub trait SecurityAPI { + fn only_owner_function(&self) -> String; + fn pausable_function(&self) -> String; + fn reentrancy_protected(&mut self) -> U256; +} + +#[router(mode = "solidity")] +impl SecurityAPI for SecureContract { + + #[function_id("onlyOwnerFunction()")] + fn only_owner_function(&self) -> String { + // Check if caller is owner + let caller = self.sdk.get_caller(); + let owner = self.sdk.get_storage(U256::from(0)); // Owner stored at slot 0 + + if caller != Address::from_slice(&owner.to_be_bytes()) { + panic!("Only owner can call this function"); + } + + "Owner function executed".to_string() + } + + #[function_id("pausableFunction()")] + fn pausable_function(&self) -> String { + // Check if contract is paused + let paused = self.sdk.get_storage(U256::from(1)); // Paused flag at slot 1 + + if !paused.is_zero() { + panic!("Contract is paused"); + } + + "Function executed".to_string() + } + + #[function_id("reentrancyProtected()")] + fn reentrancy_protected(&mut self) -> U256 { + // Simple reentrancy protection + let lock_key = U256::from(2); + let lock_value = self.sdk.get_storage(lock_key); + + if !lock_value.is_zero() { + panic!("Reentrancy detected"); + } + + // Set lock + self.sdk.set_storage(lock_key, U256::from(1)); + + // Perform operation + let result = U256::from(42); + + // Clear lock + self.sdk.set_storage(lock_key, U256::zero()); + + result + } +} + +basic_entrypoint!(SecureContract); +``` + +#### Solidity Implementation + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin/contracts/security/Pausable.sol"; +import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; + +contract SecureContract is Ownable, Pausable, ReentrancyGuard { + + function onlyOwnerFunction() external onlyOwner returns (string memory) { + return "Owner function executed"; + } + + function pausableFunction() external whenNotPaused returns (string memory) { + return "Function executed"; + } + + function reentrancyProtected() external nonReentrant returns (uint256) { + // Perform operation + return 42; + } +} +``` + +### 2. Input Validation + +```rust +#[function_id("validateInputs(uint256,address,bytes)")] +fn validate_inputs(&self, amount: U256, recipient: Address, data: Bytes) -> bool { + // Validate amount + if amount.is_zero() { + panic!("Amount cannot be zero"); + } + + if amount > U256::from(1000000) { + panic!("Amount exceeds maximum limit"); + } + + // Validate address + if recipient == address!("0000000000000000000000000000000000000000") { + panic!("Invalid recipient address"); + } + + // Validate data length + if data.len() > 1024 { + panic!("Data too large"); + } + + true +} +``` + +## Testing Strategies + +### 1. Unit Testing in Rust + +```rust +#[cfg(test)] +mod tests { + use super::*; + use fluentbase_sdk::test_utils::MockSDK; + + #[test] + fn test_safe_divide() { + let contract = ErrorHandlingExample { sdk: MockSDK::new() }; + + // Test successful division + let result = contract.safe_divide(U256::from(10), U256::from(2)); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), U256::from(5)); + + // Test division by zero + let result = contract.safe_divide(U256::from(10), U256::zero()); + assert!(result.is_err()); + } + + #[test] + fn test_validate_address() { + let contract = ErrorHandlingExample { sdk: MockSDK::new() }; + + // Test valid address + let valid_addr = address!("d8da6bf26964af9d7eed9e03e53415d37aa96045"); + let result = contract.validate_address(valid_addr); + assert!(result.is_ok()); + + // Test zero address + let zero_addr = address!("0000000000000000000000000000000000000000"); + let result = contract.validate_address(zero_addr); + assert!(result.is_err()); + } +} +``` + +### 2. Integration Testing with Foundry + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import "forge-std/Test.sol"; +import "../src/YourContract.sol"; + +contract YourContractTest is Test { + YourContract public contract; + + function setUp() public { + contract = new YourContract(); + } + + function testBasicFunctionality() public { + uint256 result = contract.someFunction(42); + assertEq(result, 84); + } + + function testRevertOnInvalidInput() public { + vm.expectRevert("Invalid input"); + contract.someFunction(0); + } + + function testGasOptimization() public { + uint256 gasBefore = gasleft(); + contract.optimizedFunction(); + uint256 gasUsed = gasBefore - gasleft(); + + // Ensure gas usage is reasonable + assertLt(gasUsed, 100000); + } +} +``` + +## Debugging Techniques + +### 1. Logging and Events + +#### Rust Logging + +```rust +#[function_id("debugFunction(uint256)")] +fn debug_function(&self, input: U256) -> U256 { + // Log input for debugging + self.sdk.write(&format!("Debug: Input value is {}", input).as_bytes()); + + let result = input * U256::from(2); + + // Log result + self.sdk.write(&format!("Debug: Result is {}", result).as_bytes()); + + result +} +``` + +#### Solidity Events + +```solidity +contract DebuggableContract { + event DebugLog(string message, uint256 value); + event FunctionCalled(address caller, uint256 input, uint256 output); + + function debugFunction(uint256 input) external returns (uint256) { + emit DebugLog("Function called with input", input); + + uint256 result = input * 2; + + emit FunctionCalled(msg.sender, input, result); + return result; + } +} +``` + +### 2. Interactive Debugging + +```bash +# Use gblend for debugging +gblend test --verbosity 4 + +# Debug specific test +gblend test --match-test testFunctionName -vvv + +# Run with gas reporting +gblend test --gas-report +``` + +## Performance Optimization + +### 1. Batch Operations + +```rust +#[function_id("batchProcess(uint256[])")] +fn batch_process(&self, items: Vec) -> Vec { + let mut results = Vec::with_capacity(items.len()); + + for item in items { + // Process each item efficiently + let processed = item * U256::from(2); + results.push(processed); + } + + results +} +``` + +### 2. Caching Strategies + +```rust +#[function_id("cachedComputation(uint256)")] +fn cached_computation(&self, input: U256) -> U256 { + // Check cache first + let cache_key = input; + let cached_result = self.sdk.get_storage(cache_key); + + if !cached_result.is_zero() { + return cached_result; + } + + // Perform expensive computation + let result = expensive_computation(input); + + // Cache result (in real implementation, you'd want to limit cache size) + self.sdk.set_storage(cache_key, result); + + result +} + +fn expensive_computation(input: U256) -> U256 { + // Simulate expensive computation + input * input * input +} +``` + +## Common Anti-Patterns + +### 1. What to Avoid + +#### ❌ Unbounded Loops +```rust +// BAD: Unbounded loop can cause gas issues +#[function_id("badLoop()")] +fn bad_loop(&self) -> U256 { + let mut result = U256::zero(); + for i in 0..10000 { // Could be much larger + result += U256::from(i); + } + result +} +``` + +#### ❌ Unchecked External Calls +```solidity +// BAD: No error handling for external calls +function badExternalCall(address target) external { + target.call(""); // No error handling +} +``` + +#### ❌ Complex Storage Patterns +```solidity +// BAD: Inefficient storage usage +contract BadStorage { + uint8 public value1; // 1 byte + uint8 public value2; // 1 byte + uint8 public value3; // 1 byte + // Each takes a full storage slot (32 bytes) +} +``` + +### 2. Better Alternatives + +#### ✅ Bounded Operations +```rust +// GOOD: Bounded operations +#[function_id("goodLoop()")] +fn good_loop(&self, limit: U256) -> U256 { + let max_limit = U256::from(1000); + let actual_limit = if limit > max_limit { max_limit } else { limit }; + + let mut result = U256::zero(); + for i in 0..actual_limit.as_u32() { + result += U256::from(i); + } + result +} +``` + +#### ✅ Safe External Calls +```solidity +// GOOD: Safe external calls +function goodExternalCall(address target) external { + (bool success, bytes memory data) = target.call(""); + require(success, "External call failed"); +} +``` + +#### ✅ Efficient Storage +```solidity +// GOOD: Efficient storage usage +contract GoodStorage { + uint8 public value1; // 1 byte + uint8 public value2; // 1 byte + uint8 public value3; // 1 byte + uint8 public value4; // 1 byte + // All packed into one storage slot (32 bytes) +} +``` + +## Next Steps + +Now that you understand these patterns and best practices, you can: + +1. **Apply these patterns** to your existing contracts +2. **Review your code** for anti-patterns and optimize accordingly +3. **Implement comprehensive testing** using the strategies outlined +4. **Use debugging techniques** to troubleshoot issues +5. **Monitor gas usage** and optimize performance + +For more advanced topics, explore: +- [Rust Smart Contracts](./smart-contracts/rust.mdx) +- [Solidity Development](./smart-contracts/solidity.mdx) +- [Building Blended Apps](./building-a-blended-app/README.md) + +:::tip[Community Resources] + +Join the Fluent community for more tips and best practices: +- [Discord Developer Forum](https://discord.com/invite/fluentxyz) +- [GitHub Discussions](https://github.com/fluentlabs-xyz/docs-docusaurus/discussions) +- [Example Projects](https://github.com/fluentlabs-xyz/examples) + +::: diff --git a/docs/developer-guides/complete-blended-app-tutorial.md b/docs/developer-guides/complete-blended-app-tutorial.md new file mode 100644 index 0000000..8937152 --- /dev/null +++ b/docs/developer-guides/complete-blended-app-tutorial.md @@ -0,0 +1,384 @@ +--- +title: Complete Blended App Tutorial +sidebar_position: 6 +--- + +Complete Blended App Tutorial +--- + +This tutorial walks you through building a complete blended application on Fluent, combining Rust WASM contracts with Solidity contracts. You'll build a simple but functional token exchange system. + +:::prerequisite + +Before starting, ensure you have: +- [gblend installed](../gblend/installation.md) +- [Basic Rust knowledge](./smart-contracts/rust.mdx) +- [Basic Solidity knowledge](./smart-contracts/solidity.mdx) +- [Development environment setup](./building-a-blended-app/README.md) + +::: + +## Project Overview + +We'll build a **Hybrid Token Exchange** that: +- Uses Rust for complex mathematical calculations +- Uses Solidity for token management +- Demonstrates cross-contract communication +- Includes comprehensive testing + +## Step 1: Project Setup + +```bash +# Create new project +gblend init hybrid-exchange +cd hybrid-exchange + +# Clean default files +rm src/BlendedCounter.sol +rm script/BlendedCounter.s.sol +rm test/BlendedCounter.t.sol + +# Rename Rust contract +mv src/power-calculator src/exchange-engine +``` + +## Step 2: Rust Exchange Engine + +Create `src/exchange-engine/src/lib.rs`: + +```rust +#![cfg_attr(target_arch = "wasm32", no_std)] +extern crate alloc; + +use alloc::string::String; +use fluentbase_sdk::{ + basic_entrypoint, derive::{router, Contract}, SharedAPI, + U256, Address, address +}; + +#[derive(Contract)] +struct ExchangeEngine { + sdk: SDK, +} + +pub trait ExchangeAPI { + fn calculate_exchange_rate(&self, input_amount: U256, input_decimals: U256, output_decimals: U256) -> U256; + fn calculate_slippage(&self, amount: U256, slippage_bps: U256) -> U256; + fn validate_trade(&self, user: Address, amount: U256, min_output: U256) -> bool; +} + +#[router(mode = "solidity")] +impl ExchangeAPI for ExchangeEngine { + + #[function_id("calculateExchangeRate(uint256,uint256,uint256)")] + fn calculate_exchange_rate(&self, input_amount: U256, input_decimals: U256, output_decimals: U256) -> U256 { + // Calculate exchange rate with precision + let precision = U256::from(10).pow(U256::from(18)); + let rate = input_amount * precision / (U256::from(10).pow(input_decimals)); + rate * (U256::from(10).pow(output_decimals)) / precision + } + + #[function_id("calculateSlippage(uint256,uint256)")] + fn calculate_slippage(&self, amount: U256, slippage_bps: U256) -> U256 { + // Calculate minimum output with slippage (basis points) + let slippage_factor = U256::from(10000) - slippage_bps; + amount * slippage_factor / U256::from(10000) + } + + #[function_id("validateTrade(address,uint256,uint256)")] + fn validate_trade(&self, user: Address, amount: U256, min_output: U256) -> bool { + // Basic validation logic + !user.is_zero() && !amount.is_zero() && !min_output.is_zero() + } +} + +impl ExchangeEngine { + fn deploy(&mut self) { + // Initialize exchange parameters + self.sdk.set_storage(U256::from(0), U256::from(1); // Active + self.sdk.set_storage(U256::from(1), U256::from(300); // Default slippage 3% + } +} + +basic_entrypoint!(ExchangeEngine); +``` + +## Step 3: Solidity Token Contract + +Create `src/HybridToken.sol`: + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; + +contract HybridToken is ERC20, Ownable { + uint8 private _decimals; + + constructor( + string memory name, + string memory symbol, + uint8 decimals_, + uint256 initialSupply + ) ERC20(name, symbol) Ownable(msg.sender) { + _decimals = decimals_; + _mint(msg.sender, initialSupply); + } + + function decimals() public view virtual override returns (uint8) { + return _decimals; + } + + function mint(address to, uint256 amount) external onlyOwner { + _mint(to, amount); + } +} +``` + +## Step 4: Solidity Exchange Contract + +Create `src/HybridExchange.sol`: + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import "./HybridToken.sol"; +import "./IExchangeEngine.sol"; + +contract HybridExchange { + HybridToken public tokenA; + HybridToken public tokenB; + address public exchangeEngine; + + mapping(address => uint256) public userBalances; + + event TradeExecuted( + address indexed user, + address indexed tokenIn, + address indexed tokenOut, + uint256 amountIn, + uint256 amountOut + ); + + constructor( + address _tokenA, + address _tokenB, + address _exchangeEngine + ) { + tokenA = HybridToken(_tokenA); + tokenB = HybridToken(_tokenB); + exchangeEngine = _exchangeEngine; + } + + function executeTrade( + address tokenIn, + uint256 amountIn, + uint256 minAmountOut, + uint256 slippageBps + ) external { + require(tokenIn == address(tokenA) || tokenIn == address(tokenB), "Invalid token"); + + // Transfer tokens from user + HybridToken(tokenIn).transferFrom(msg.sender, address(this), amountIn); + + // Calculate exchange rate using Rust engine + IExchangeEngine engine = IExchangeEngine(exchangeEngine); + uint256 exchangeRate = engine.calculateExchangeRate( + amountIn, + HybridToken(tokenIn).decimals(), + HybridToken(tokenIn == address(tokenA) ? address(tokenB) : address(tokenA)).decimals() + ); + + // Calculate output with slippage protection + uint256 amountOut = engine.calculateSlippage(exchangeRate, slippageBps); + require(amountOut >= minAmountOut, "Insufficient output amount"); + + // Validate trade + require(engine.validateTrade(msg.sender, amountIn, minAmountOut), "Trade validation failed"); + + // Execute the trade + address tokenOut = tokenIn == address(tokenA) ? address(tokenB) : address(tokenA); + HybridToken(tokenOut).transfer(msg.sender, amountOut); + + emit TradeExecuted(msg.sender, tokenIn, tokenOut, amountIn, amountOut); + } +} +``` + +## Step 5: Interface Contract + +Create `src/IExchangeEngine.sol`: + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +interface IExchangeEngine { + function calculateExchangeRate( + uint256 inputAmount, + uint256 inputDecimals, + uint256 outputDecimals + ) external view returns (uint256); + + function calculateSlippage( + uint256 amount, + uint256 slippageBps + ) external view returns (uint256); + + function validateTrade( + address user, + uint256 amount, + uint256 minOutput + ) external view returns (bool); +} +``` + +## Step 6: Build and Deploy + +```bash +# Build the project +gblend build + +# Deploy contracts (you'll need testnet ETH) +gblend script Deploy --rpc-url https://rpc.dev.gblend.xyz --broadcast + +# Verify contracts +gblend verify --contract HybridToken +gblend verify --wasm --contract ExchangeEngine +gblend verify --contract HybridExchange +``` + +## Step 7: Testing + +Create comprehensive tests in `test/HybridExchange.t.sol`: + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import "forge-std/Test.sol"; +import "../src/HybridToken.sol"; +import "../src/HybridExchange.sol"; +import "../src/IExchangeEngine.sol"; + +contract HybridExchangeTest is Test { + HybridToken public tokenA; + HybridToken public tokenB; + HybridExchange public exchange; + address public exchangeEngine; + + address public user = address(0x1); + address public owner = address(0x2); + + function setUp() public { + // Deploy tokens + tokenA = new HybridToken("Token A", "TKA", 18, 1000000 * 10**18); + tokenB = new HybridToken("Token B", "TKB", 6, 1000000 * 10**6); + + // Deploy exchange engine (WASM) + // This would be deployed separately + + // Deploy exchange + exchange = new HybridExchange( + address(tokenA), + address(tokenB), + exchangeEngine + ); + + // Setup user with tokens + tokenA.transfer(user, 1000 * 10**18); + tokenB.transfer(user, 1000 * 10**6); + + vm.startPrank(user); + tokenA.approve(address(exchange), type(uint256).max); + tokenB.approve(address(exchange), type(uint256).max); + vm.stopPrank(); + } + + function testBasicTrade() public { + vm.startPrank(user); + + uint256 amountIn = 100 * 10**18; // 100 TKA + uint256 minAmountOut = 95 * 10**6; // 95 TKB + + exchange.executeTrade( + address(tokenA), + amountIn, + minAmountOut, + 500 // 5% slippage + ); + + vm.stopPrank(); + } + + function testInsufficientOutput() public { + vm.startPrank(user); + + uint256 amountIn = 100 * 10**18; + uint256 minAmountOut = 200 * 10**6; // Unrealistic expectation + + vm.expectRevert("Insufficient output amount"); + exchange.executeTrade( + address(tokenA), + amountIn, + minAmountOut, + 100 // 1% slippage + ); + + vm.stopPrank(); + } +} +``` + +## Step 8: Frontend Integration + +Create a simple frontend to interact with your contracts: + +```javascript +// Example using ethers.js +import { ethers } from 'ethers'; + +const provider = new ethers.providers.JsonRpcProvider('https://rpc.dev.gblend.xyz'); +const signer = provider.getSigner(); + +const exchangeContract = new ethers.Contract( + exchangeAddress, + exchangeABI, + signer +); + +async function executeTrade(tokenIn, amountIn, minAmountOut, slippageBps) { + try { + const tx = await exchangeContract.executeTrade( + tokenIn, + amountIn, + minAmountOut, + slippageBps + ); + await tx.wait(); + console.log('Trade executed successfully!'); + } catch (error) { + console.error('Trade failed:', error); + } +} +``` + +## Next Steps + +1. **Extend Functionality**: Add liquidity pools, order books, or advanced trading features +2. **Optimize Performance**: Implement caching and batch operations +3. **Add Monitoring**: Include events and analytics +4. **Security Audit**: Review for potential vulnerabilities +5. **Documentation**: Create user guides and API documentation + +## Common Issues & Solutions + +- **WASM Size**: Optimize by removing unused dependencies +- **Gas Costs**: Use batch operations and efficient storage patterns +- **Type Mismatches**: Ensure exact function signature matching +- **Deployment Failures**: Check network configuration and gas limits + +This tutorial provides a solid foundation for building complex blended applications on Fluent. The patterns demonstrated here can be applied to DeFi protocols, gaming applications, and other innovative use cases. diff --git a/docs/developer-guides/troubleshooting-guide.md b/docs/developer-guides/troubleshooting-guide.md new file mode 100644 index 0000000..7bc3647 --- /dev/null +++ b/docs/developer-guides/troubleshooting-guide.md @@ -0,0 +1,570 @@ +--- +title: Troubleshooting Guide +sidebar_position: 5 +--- + +Troubleshooting Guide +--- + +This guide helps you resolve common issues when developing on Fluent. Whether you're encountering build errors, deployment problems, or runtime issues, you'll find solutions and workarounds here. + +:::prerequisite + +Before troubleshooting, ensure you have: +- [gblend installed](../gblend/installation.md) +- [Proper development environment setup](./building-a-blended-app/README.md) +- [Basic understanding of Rust and Solidity](./smart-contracts/README.md) + +::: + +## Table of Contents + +- [Build Issues](#build-issues) +- [Deployment Problems](#deployment-problems) +- [Runtime Errors](#runtime-errors) +- [Performance Issues](#performance-issues) +- [Common Gotchas](#common-gotchas) +- [Getting Help](#getting-help) + +## Build Issues + +### Rust Contract Compilation Errors + +#### 1. "no_std" Environment Issues + +**Problem**: Compilation fails with standard library errors. + +```bash +error[E0433]: failed to resolve: could not find `std` in the list of imported crates +``` + +**Solution**: Ensure your contract has the proper `no_std` configuration: + +```rust +#![cfg_attr(target_arch = "wasm32", no_std)] +extern crate alloc; + +use alloc::string::String; +use fluentbase_sdk::{basic_entrypoint, derive::Contract, SharedAPI}; +``` + +**Common Fixes**: +- Add `#![cfg_attr(target_arch = "wasm32", no_std)]` at the top +- Replace `std::` imports with `alloc::` or `core::` +- Use `fluentbase_sdk` types instead of standard library types + +#### 2. Fluentbase SDK Version Mismatch + +**Problem**: Compilation fails with SDK compatibility errors. + +```bash +error[E0277]: the trait bound `fluentbase_sdk::SharedAPI` is not implemented +``` + +**Solution**: Update your `Cargo.toml` dependencies: + +```toml +[dependencies] +fluentbase-sdk = "0.3.6" # Use the latest compatible version +``` + +**Update Command**: +```bash +cd src/your-rust-contract +cargo update -p fluentbase-sdk +cargo clean +cargo build +``` + +#### 3. WASM Target Not Installed + +**Problem**: Can't compile to WASM target. + +```bash +error: target wasm32-unknown-unknown not found +``` + +**Solution**: Install the WASM target: + +```bash +rustup target add wasm32-unknown-unknown +``` + +**Verify Installation**: +```bash +rustup target list --installed | grep wasm32 +``` + +### Solidity Compilation Issues + +#### 1. Solidity Version Compatibility + +**Problem**: Compilation fails with version-specific syntax. + +```bash +Error: ParserError: Source file requires different compiler version +``` + +**Solution**: Check your `foundry.toml` configuration: + +```toml +[profile.default] +solc_version = "0.8.19" # Use compatible version +``` + +**Common Versions for Fluent**: +- Solidity: `0.8.19` or later +- Foundry: Latest stable version + +#### 2. Import Path Issues + +**Problem**: Can't resolve import paths. + +```bash +Error: Source file not found: @openzeppelin/contracts/... +``` + +**Solution**: Install dependencies and check paths: + +```bash +# Install OpenZeppelin contracts +forge install OpenZeppelin/openzeppelin-contracts + +# Update remappings in foundry.toml +remappings = [ + "@openzeppelin/=lib/openzeppelin-contracts/contracts/" +] +``` + +### gblend Build Issues + +#### 1. Docker Not Running + +**Problem**: WASM build fails with Docker errors. + +```bash +Error: failed to create container: Error response from daemon +``` + +**Solution**: Ensure Docker is running: + +```bash +# Check Docker status +docker info + +# Start Docker (macOS) +open -a Docker + +# Start Docker (Linux) +sudo systemctl start docker +``` + +#### 2. Build Cache Issues + +**Problem**: Stale build artifacts causing errors. + +**Solution**: Clean and rebuild: + +```bash +# Clean all build artifacts +gblend clean + +# Clean Rust cache +cd src/your-rust-contract +cargo clean + +# Rebuild +gblend build +``` + +## Deployment Problems + +### Contract Deployment Failures + +#### 1. Insufficient Gas + +**Problem**: Transaction fails due to gas limit. + +```bash +Error: gas required exceeds allowance +``` + +**Solution**: Increase gas limit and check gas estimation: + +```bash +# Estimate gas usage +gblend estimate-gas --contract YourContract + +# Deploy with higher gas limit +gblend deploy --gas-limit 5000000 +``` + +**Gas Optimization Tips**: +- Use batch operations when possible +- Optimize storage layout +- Avoid unbounded loops + +#### 2. Network Configuration Issues + +**Problem**: Wrong network or RPC endpoint. + +**Solution**: Verify network settings: + +```bash +# Check current network +gblend config --list + +# Set correct network +gblend config --network fluent-testnet + +# Verify RPC endpoint +gblend config --rpc-url https://rpc.dev.gblend.xyz +``` + +#### 3. Contract Verification Failures + +**Problem**: Contract verification fails on explorer. + +```bash +Error: Contract verification failed +``` + +**Solution**: Ensure proper verification: + +```bash +# Verify Solidity contract +gblend verify --contract YourContract + +# Verify WASM contract +gblend verify --wasm --contract YourWasmContract +``` + +**Verification Checklist**: +- Contract bytecode matches deployed version +- Constructor arguments are correct +- Source code is properly formatted +- All dependencies are available + +### WASM-Specific Deployment Issues + +#### 1. WASM Binary Too Large + +**Problem**: Contract exceeds size limits. + +```bash +Error: WASM binary exceeds maximum size +``` + +**Solution**: Optimize your WASM contract: + +```rust +// Use efficient data structures +use alloc::vec::Vec; + +// Avoid unnecessary allocations +let mut result = Vec::with_capacity(expected_size); + +// Use references when possible +fn process_data(&self, data: &[U256]) -> U256 { + // Process without cloning +} +``` + +**Size Optimization Tips**: +- Remove unused dependencies +- Use `no_std` compatible crates +- Minimize string allocations +- Optimize storage patterns + +#### 2. WASM Interface Generation Issues + +**Problem**: Solidity interface not generated correctly. + +**Solution**: Check your Rust contract structure: + +```rust +#[derive(Contract)] +struct YourContract { + sdk: SDK, +} + +pub trait YourAPI { + #[function_id("functionName(uint256)")] + fn function_name(&self, param: U256) -> U256; +} + +#[router(mode = "solidity")] +impl YourAPI for YourContract { + // Implementation +} + +basic_entrypoint!(YourContract); +``` + +## Runtime Errors + +### Contract Execution Failures + +#### 1. Function Selector Mismatch + +**Problem**: Function call fails with selector error. + +```bash +Error: Function selector not found +``` + +**Solution**: Verify function signatures match: + +```rust +// Rust function +#[function_id("calculate(uint256,uint256)")] +fn calculate(&self, a: U256, b: U256) -> U256 { + a + b +} +``` + +```solidity +// Solidity interface must match exactly +interface YourInterface { + function calculate(uint256 a, uint256 b) external view returns (uint256); +} +``` + +#### 2. Type Conversion Errors + +**Problem**: Data type mismatches between Rust and Solidity. + +**Solution**: Use proper type mappings: + +```rust +use fluentbase_sdk::{U256, Address, Bytes, B256}; + +// Correct type usage +fn process_data(&self, amount: U256, addr: Address) -> Bytes { + // U256 for uint256 + // Address for address + // Bytes for bytes +} +``` + +**Type Mapping Reference**: +- `uint256` → `U256` +- `address` → `Address` +- `bytes` → `Bytes` +- `bytes32` → `B256` +- `bool` → `bool` +- `string` → `String` + +### Storage Access Issues + +#### 1. Storage Slot Conflicts + +**Problem**: Data corruption due to storage conflicts. + +**Solution**: Use proper storage slot management: + +```rust +// Define storage layout explicitly +const OWNER_SLOT: U256 = U256::from(0); +const PAUSED_SLOT: U256 = U256::from(1); +const BALANCE_SLOT: U256 = U256::from(2); + +fn get_owner(&self) -> Address { + let owner_data = self.sdk.get_storage(OWNER_SLOT); + Address::from_slice(&owner_data.to_be_bytes()) +} +``` + +#### 2. Storage Type Mismatches + +**Problem**: Reading wrong data type from storage. + +**Solution**: Ensure consistent storage types: + +```rust +// Store and retrieve with same type +fn set_balance(&mut self, user: Address, amount: U256) { + let key = self.get_storage_key(user); + self.sdk.set_storage(key, amount); +} + +fn get_balance(&self, user: Address) -> U256 { + let key = self.get_storage_key(user); + self.sdk.get_storage(key) +} +``` + +## Performance Issues + +### High Gas Consumption + +#### 1. Inefficient Storage Operations + +**Problem**: Excessive gas usage for storage operations. + +**Solution**: Optimize storage patterns: + +```rust +// Batch storage operations +fn batch_update(&mut self, updates: Vec<(U256, U256)>) { + for (key, value) in updates { + self.sdk.set_storage(key, value); + } +} + +// Use efficient data structures +fn efficient_loop(&self, limit: U256) -> U256 { + let max_limit = U256::from(1000); + let actual_limit = if limit > max_limit { max_limit } else { limit }; + + let mut result = U256::zero(); + for i in 0..actual_limit.as_u32() { + result += U256::from(i); + } + result +} +``` + +#### 2. Unbounded Operations + +**Problem**: Functions that can consume unlimited gas. + +**Solution**: Implement bounds and limits: + +```rust +fn safe_operation(&self, items: Vec) -> Vec { + let max_items = 100; + let actual_items = if items.len() > max_items { + &items[..max_items] + } else { + &items + }; + + actual_items.iter().map(|&x| x * U256::from(2)).collect() +} +``` + +### Memory Management Issues + +#### 1. Excessive Allocations + +**Problem**: High memory usage and gas costs. + +**Solution**: Minimize allocations: + +```rust +// Pre-allocate when possible +fn efficient_string(&self) -> String { + let mut result = String::with_capacity(100); + result.push_str("Hello"); + result.push_str(" World"); + result +} + +// Use references to avoid cloning +fn process_array(&self, data: &[U256]) -> U256 { + data.iter().sum() +} +``` + +## Common Gotchas + +### 1. Function Visibility Issues + +**Problem**: Functions not accessible from Solidity. + +**Solution**: Ensure proper visibility and routing: + +```rust +pub trait YourAPI { + #[function_id("publicFunction()")] + fn public_function(&self) -> String; +} + +#[router(mode = "solidity")] +impl YourAPI for YourContract { + // Must be public + pub fn public_function(&self) -> String { + "Hello".to_string() + } +} +``` + +### 2. Missing Entry Point + +**Problem**: Contract doesn't respond to calls. + +**Solution**: Include the entry point macro: + +```rust +// Always include this at the end +basic_entrypoint!(YourContract); +``` + +### 3. Incorrect Function IDs + +**Problem**: Function calls don't match expected signatures. + +**Solution**: Use exact Solidity function signatures: + +```rust +// Correct function ID format +#[function_id("transfer(address,uint256)")] +fn transfer(&mut self, to: Address, amount: U256) -> bool { + // Implementation +} +``` + +**Common Function ID Patterns**: +- `"functionName()"` - No parameters +- `"functionName(uint256)"` - Single parameter +- `"functionName(address,uint256)"` - Multiple parameters +- `"functionName(uint256[])"` - Array parameter + +### 4. Storage Initialization + +**Problem**: Uninitialized storage causing unexpected behavior. + +**Solution**: Initialize storage properly: + +```rust +impl YourContract { + fn deploy(&mut self) { + // Initialize storage values + self.sdk.set_storage(U256::from(0), U256::from(1)); // Initial state + self.sdk.set_storage(U256::from(1), U256::zero()); // Paused = false + } +} +``` + +## Getting Help + +### When to Seek Help + +- **Build errors** that persist after trying solutions above +- **Runtime errors** that aren't covered in this guide +- **Performance issues** that affect production +- **Security concerns** about your implementation + +### Where to Get Help + +1. **Documentation**: Check existing guides first +2. **GitHub Issues**: Search for similar problems +3. **Discord Community**: Join the [Fluent Discord](https://discord.com/invite/fluentxyz) #developer-forum +4. **Example Projects**: Review [GitHub examples](https://github.com/fluentlabs-xyz/examples) + +### How to Ask for Help + +When seeking help, provide: + +1. **Clear description** of the problem +2. **Error messages** and stack traces +3. **Relevant code snippets** +4. **Steps to reproduce** +5. **What you've already tried** +6. **Environment details** (OS, versions, etc.) + + +--- + +**Still stuck?** Don't hesitate to reach out to the Fluent community. We're here to help you succeed! 🚀 From e7edbec3d13895411c6ffae96cb0d5beee387de1 Mon Sep 17 00:00:00 2001 From: ypszn Date: Mon, 8 Sep 2025 23:18:45 +0100 Subject: [PATCH 2/2] fix: update storage patterns & reorganize developer guides --- .../complete-blended-app-tutorial.md | 384 ------------------ .../developer-guides/troubleshooting-guide.md | 133 +++--- .../common-patterns.md | 244 ++--------- 3 files changed, 99 insertions(+), 662 deletions(-) delete mode 100644 docs/developer-guides/complete-blended-app-tutorial.md rename docs/{developer-guides => fluentbase-sdk}/common-patterns.md (66%) diff --git a/docs/developer-guides/complete-blended-app-tutorial.md b/docs/developer-guides/complete-blended-app-tutorial.md deleted file mode 100644 index 8937152..0000000 --- a/docs/developer-guides/complete-blended-app-tutorial.md +++ /dev/null @@ -1,384 +0,0 @@ ---- -title: Complete Blended App Tutorial -sidebar_position: 6 ---- - -Complete Blended App Tutorial ---- - -This tutorial walks you through building a complete blended application on Fluent, combining Rust WASM contracts with Solidity contracts. You'll build a simple but functional token exchange system. - -:::prerequisite - -Before starting, ensure you have: -- [gblend installed](../gblend/installation.md) -- [Basic Rust knowledge](./smart-contracts/rust.mdx) -- [Basic Solidity knowledge](./smart-contracts/solidity.mdx) -- [Development environment setup](./building-a-blended-app/README.md) - -::: - -## Project Overview - -We'll build a **Hybrid Token Exchange** that: -- Uses Rust for complex mathematical calculations -- Uses Solidity for token management -- Demonstrates cross-contract communication -- Includes comprehensive testing - -## Step 1: Project Setup - -```bash -# Create new project -gblend init hybrid-exchange -cd hybrid-exchange - -# Clean default files -rm src/BlendedCounter.sol -rm script/BlendedCounter.s.sol -rm test/BlendedCounter.t.sol - -# Rename Rust contract -mv src/power-calculator src/exchange-engine -``` - -## Step 2: Rust Exchange Engine - -Create `src/exchange-engine/src/lib.rs`: - -```rust -#![cfg_attr(target_arch = "wasm32", no_std)] -extern crate alloc; - -use alloc::string::String; -use fluentbase_sdk::{ - basic_entrypoint, derive::{router, Contract}, SharedAPI, - U256, Address, address -}; - -#[derive(Contract)] -struct ExchangeEngine { - sdk: SDK, -} - -pub trait ExchangeAPI { - fn calculate_exchange_rate(&self, input_amount: U256, input_decimals: U256, output_decimals: U256) -> U256; - fn calculate_slippage(&self, amount: U256, slippage_bps: U256) -> U256; - fn validate_trade(&self, user: Address, amount: U256, min_output: U256) -> bool; -} - -#[router(mode = "solidity")] -impl ExchangeAPI for ExchangeEngine { - - #[function_id("calculateExchangeRate(uint256,uint256,uint256)")] - fn calculate_exchange_rate(&self, input_amount: U256, input_decimals: U256, output_decimals: U256) -> U256 { - // Calculate exchange rate with precision - let precision = U256::from(10).pow(U256::from(18)); - let rate = input_amount * precision / (U256::from(10).pow(input_decimals)); - rate * (U256::from(10).pow(output_decimals)) / precision - } - - #[function_id("calculateSlippage(uint256,uint256)")] - fn calculate_slippage(&self, amount: U256, slippage_bps: U256) -> U256 { - // Calculate minimum output with slippage (basis points) - let slippage_factor = U256::from(10000) - slippage_bps; - amount * slippage_factor / U256::from(10000) - } - - #[function_id("validateTrade(address,uint256,uint256)")] - fn validate_trade(&self, user: Address, amount: U256, min_output: U256) -> bool { - // Basic validation logic - !user.is_zero() && !amount.is_zero() && !min_output.is_zero() - } -} - -impl ExchangeEngine { - fn deploy(&mut self) { - // Initialize exchange parameters - self.sdk.set_storage(U256::from(0), U256::from(1); // Active - self.sdk.set_storage(U256::from(1), U256::from(300); // Default slippage 3% - } -} - -basic_entrypoint!(ExchangeEngine); -``` - -## Step 3: Solidity Token Contract - -Create `src/HybridToken.sol`: - -```solidity -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import "@openzeppelin/contracts/access/Ownable.sol"; - -contract HybridToken is ERC20, Ownable { - uint8 private _decimals; - - constructor( - string memory name, - string memory symbol, - uint8 decimals_, - uint256 initialSupply - ) ERC20(name, symbol) Ownable(msg.sender) { - _decimals = decimals_; - _mint(msg.sender, initialSupply); - } - - function decimals() public view virtual override returns (uint8) { - return _decimals; - } - - function mint(address to, uint256 amount) external onlyOwner { - _mint(to, amount); - } -} -``` - -## Step 4: Solidity Exchange Contract - -Create `src/HybridExchange.sol`: - -```solidity -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import "./HybridToken.sol"; -import "./IExchangeEngine.sol"; - -contract HybridExchange { - HybridToken public tokenA; - HybridToken public tokenB; - address public exchangeEngine; - - mapping(address => uint256) public userBalances; - - event TradeExecuted( - address indexed user, - address indexed tokenIn, - address indexed tokenOut, - uint256 amountIn, - uint256 amountOut - ); - - constructor( - address _tokenA, - address _tokenB, - address _exchangeEngine - ) { - tokenA = HybridToken(_tokenA); - tokenB = HybridToken(_tokenB); - exchangeEngine = _exchangeEngine; - } - - function executeTrade( - address tokenIn, - uint256 amountIn, - uint256 minAmountOut, - uint256 slippageBps - ) external { - require(tokenIn == address(tokenA) || tokenIn == address(tokenB), "Invalid token"); - - // Transfer tokens from user - HybridToken(tokenIn).transferFrom(msg.sender, address(this), amountIn); - - // Calculate exchange rate using Rust engine - IExchangeEngine engine = IExchangeEngine(exchangeEngine); - uint256 exchangeRate = engine.calculateExchangeRate( - amountIn, - HybridToken(tokenIn).decimals(), - HybridToken(tokenIn == address(tokenA) ? address(tokenB) : address(tokenA)).decimals() - ); - - // Calculate output with slippage protection - uint256 amountOut = engine.calculateSlippage(exchangeRate, slippageBps); - require(amountOut >= minAmountOut, "Insufficient output amount"); - - // Validate trade - require(engine.validateTrade(msg.sender, amountIn, minAmountOut), "Trade validation failed"); - - // Execute the trade - address tokenOut = tokenIn == address(tokenA) ? address(tokenB) : address(tokenA); - HybridToken(tokenOut).transfer(msg.sender, amountOut); - - emit TradeExecuted(msg.sender, tokenIn, tokenOut, amountIn, amountOut); - } -} -``` - -## Step 5: Interface Contract - -Create `src/IExchangeEngine.sol`: - -```solidity -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -interface IExchangeEngine { - function calculateExchangeRate( - uint256 inputAmount, - uint256 inputDecimals, - uint256 outputDecimals - ) external view returns (uint256); - - function calculateSlippage( - uint256 amount, - uint256 slippageBps - ) external view returns (uint256); - - function validateTrade( - address user, - uint256 amount, - uint256 minOutput - ) external view returns (bool); -} -``` - -## Step 6: Build and Deploy - -```bash -# Build the project -gblend build - -# Deploy contracts (you'll need testnet ETH) -gblend script Deploy --rpc-url https://rpc.dev.gblend.xyz --broadcast - -# Verify contracts -gblend verify --contract HybridToken -gblend verify --wasm --contract ExchangeEngine -gblend verify --contract HybridExchange -``` - -## Step 7: Testing - -Create comprehensive tests in `test/HybridExchange.t.sol`: - -```solidity -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import "forge-std/Test.sol"; -import "../src/HybridToken.sol"; -import "../src/HybridExchange.sol"; -import "../src/IExchangeEngine.sol"; - -contract HybridExchangeTest is Test { - HybridToken public tokenA; - HybridToken public tokenB; - HybridExchange public exchange; - address public exchangeEngine; - - address public user = address(0x1); - address public owner = address(0x2); - - function setUp() public { - // Deploy tokens - tokenA = new HybridToken("Token A", "TKA", 18, 1000000 * 10**18); - tokenB = new HybridToken("Token B", "TKB", 6, 1000000 * 10**6); - - // Deploy exchange engine (WASM) - // This would be deployed separately - - // Deploy exchange - exchange = new HybridExchange( - address(tokenA), - address(tokenB), - exchangeEngine - ); - - // Setup user with tokens - tokenA.transfer(user, 1000 * 10**18); - tokenB.transfer(user, 1000 * 10**6); - - vm.startPrank(user); - tokenA.approve(address(exchange), type(uint256).max); - tokenB.approve(address(exchange), type(uint256).max); - vm.stopPrank(); - } - - function testBasicTrade() public { - vm.startPrank(user); - - uint256 amountIn = 100 * 10**18; // 100 TKA - uint256 minAmountOut = 95 * 10**6; // 95 TKB - - exchange.executeTrade( - address(tokenA), - amountIn, - minAmountOut, - 500 // 5% slippage - ); - - vm.stopPrank(); - } - - function testInsufficientOutput() public { - vm.startPrank(user); - - uint256 amountIn = 100 * 10**18; - uint256 minAmountOut = 200 * 10**6; // Unrealistic expectation - - vm.expectRevert("Insufficient output amount"); - exchange.executeTrade( - address(tokenA), - amountIn, - minAmountOut, - 100 // 1% slippage - ); - - vm.stopPrank(); - } -} -``` - -## Step 8: Frontend Integration - -Create a simple frontend to interact with your contracts: - -```javascript -// Example using ethers.js -import { ethers } from 'ethers'; - -const provider = new ethers.providers.JsonRpcProvider('https://rpc.dev.gblend.xyz'); -const signer = provider.getSigner(); - -const exchangeContract = new ethers.Contract( - exchangeAddress, - exchangeABI, - signer -); - -async function executeTrade(tokenIn, amountIn, minAmountOut, slippageBps) { - try { - const tx = await exchangeContract.executeTrade( - tokenIn, - amountIn, - minAmountOut, - slippageBps - ); - await tx.wait(); - console.log('Trade executed successfully!'); - } catch (error) { - console.error('Trade failed:', error); - } -} -``` - -## Next Steps - -1. **Extend Functionality**: Add liquidity pools, order books, or advanced trading features -2. **Optimize Performance**: Implement caching and batch operations -3. **Add Monitoring**: Include events and analytics -4. **Security Audit**: Review for potential vulnerabilities -5. **Documentation**: Create user guides and API documentation - -## Common Issues & Solutions - -- **WASM Size**: Optimize by removing unused dependencies -- **Gas Costs**: Use batch operations and efficient storage patterns -- **Type Mismatches**: Ensure exact function signature matching -- **Deployment Failures**: Check network configuration and gas limits - -This tutorial provides a solid foundation for building complex blended applications on Fluent. The patterns demonstrated here can be applied to DeFi protocols, gaming applications, and other innovative use cases. diff --git a/docs/developer-guides/troubleshooting-guide.md b/docs/developer-guides/troubleshooting-guide.md index 7bc3647..b0a853a 100644 --- a/docs/developer-guides/troubleshooting-guide.md +++ b/docs/developer-guides/troubleshooting-guide.md @@ -65,7 +65,7 @@ error[E0277]: the trait bound `fluentbase_sdk::SharedAPI` is not implemented ```toml [dependencies] -fluentbase-sdk = "0.3.6" # Use the latest compatible version +fluentbase-sdk = "0.4.3-dev" # Use the latest compatible version ``` **Update Command**: @@ -136,47 +136,6 @@ remappings = [ ] ``` -### gblend Build Issues - -#### 1. Docker Not Running - -**Problem**: WASM build fails with Docker errors. - -```bash -Error: failed to create container: Error response from daemon -``` - -**Solution**: Ensure Docker is running: - -```bash -# Check Docker status -docker info - -# Start Docker (macOS) -open -a Docker - -# Start Docker (Linux) -sudo systemctl start docker -``` - -#### 2. Build Cache Issues - -**Problem**: Stale build artifacts causing errors. - -**Solution**: Clean and rebuild: - -```bash -# Clean all build artifacts -gblend clean - -# Clean Rust cache -cd src/your-rust-contract -cargo clean - -# Rebuild -gblend build -``` - ## Deployment Problems ### Contract Deployment Failures @@ -196,7 +155,7 @@ Error: gas required exceeds allowance gblend estimate-gas --contract YourContract # Deploy with higher gas limit -gblend deploy --gas-limit 5000000 +gblend create --gas-limit 5000000 ``` **Gas Optimization Tips**: @@ -218,7 +177,7 @@ gblend config --list gblend config --network fluent-testnet # Verify RPC endpoint -gblend config --rpc-url https://rpc.dev.gblend.xyz +gblend config --rpc-url $RPC_URL ``` #### 3. Contract Verification Failures @@ -233,10 +192,16 @@ Error: Contract verification failed ```bash # Verify Solidity contract -gblend verify --contract YourContract +gblend verify-contract
YourContract \ + --verifier blockscout \ + --verifier-url https://testnet.fluentscan.xyz/api/ \ + --constructor-args # Verify WASM contract -gblend verify --wasm --contract YourWasmContract +gblend verify-contract
YourContract.wasm \ + --wasm \ + --verifier blockscout \ + --verifier-url https://testnet.fluentscan.xyz/api/ ``` **Verification Checklist**: @@ -276,7 +241,7 @@ fn process_data(&self, data: &[U256]) -> U256 { - Minimize string allocations - Optimize storage patterns -#### 2. WASM Interface Generation Issues + ## Runtime Errors @@ -361,17 +326,25 @@ fn process_data(&self, amount: U256, addr: Address) -> Bytes { **Problem**: Data corruption due to storage conflicts. -**Solution**: Use proper storage slot management: +**Solution**: Use the `solidity_storage!` macro for proper storage management: ```rust -// Define storage layout explicitly -const OWNER_SLOT: U256 = U256::from(0); -const PAUSED_SLOT: U256 = U256::from(1); -const BALANCE_SLOT: U256 = U256::from(2); +use fluentbase_sdk::derive::solidity_storage; + +// Define storage layout using the macro +solidity_storage! { + Address Owner; // Slot 0 + bool Paused; // Slot 1 + U256 TotalSupply; // Slot 2 + mapping(Address => U256) Balance; // Slot 3 +} fn get_owner(&self) -> Address { - let owner_data = self.sdk.get_storage(OWNER_SLOT); - Address::from_slice(&owner_data.to_be_bytes()) + Owner::get(&self.sdk) +} + +fn set_owner(&mut self, new_owner: Address) { + Owner::set(&mut self.sdk, new_owner); } ``` @@ -379,19 +352,19 @@ fn get_owner(&self) -> Address { **Problem**: Reading wrong data type from storage. -**Solution**: Ensure consistent storage types: +**Solution**: Use the generated storage methods for type safety: ```rust -// Store and retrieve with same type +// Store and retrieve with same type using generated methods fn set_balance(&mut self, user: Address, amount: U256) { - let key = self.get_storage_key(user); - self.sdk.set_storage(key, amount); + Balance::set(&mut self.sdk, user, amount); } fn get_balance(&self, user: Address) -> U256 { - let key = self.get_storage_key(user); - self.sdk.get_storage(key) + Balance::get(&self.sdk, user) } + +// The macro ensures type safety - this won't compile if types don't match ``` ## Performance Issues @@ -402,13 +375,20 @@ fn get_balance(&self, user: Address) -> U256 { **Problem**: Excessive gas usage for storage operations. -**Solution**: Optimize storage patterns: +**Solution**: Use the `solidity_storage!` macro for optimized storage access: ```rust -// Batch storage operations -fn batch_update(&mut self, updates: Vec<(U256, U256)>) { - for (key, value) in updates { - self.sdk.set_storage(key, value); +use fluentbase_sdk::derive::solidity_storage; + +solidity_storage! { + mapping(Address => U256) Balances; + U256 TotalSupply; +} + +// Batch storage operations using generated methods +fn batch_update(&mut self, updates: Vec<(Address, U256)>) { + for (user, amount) in updates { + Balances::set(&mut self.sdk, user, amount); } } @@ -525,14 +505,23 @@ fn transfer(&mut self, to: Address, amount: U256) -> bool { **Problem**: Uninitialized storage causing unexpected behavior. -**Solution**: Initialize storage properly: +**Solution**: Initialize storage properly using the `solidity_storage!` macro: ```rust +use fluentbase_sdk::derive::solidity_storage; + +solidity_storage! { + U256 InitialState; // Slot 0 + bool Paused; // Slot 1 + Address Owner; // Slot 2 +} + impl YourContract { fn deploy(&mut self) { - // Initialize storage values - self.sdk.set_storage(U256::from(0), U256::from(1)); // Initial state - self.sdk.set_storage(U256::from(1), U256::zero()); // Paused = false + // Initialize storage values using generated methods + InitialState::set(&mut self.sdk, U256::from(1)); + Paused::set(&mut self.sdk, false); + Owner::set(&mut self.sdk, self.sdk.context().contract_caller()); } } ``` @@ -550,7 +539,7 @@ impl YourContract { 1. **Documentation**: Check existing guides first 2. **GitHub Issues**: Search for similar problems -3. **Discord Community**: Join the [Fluent Discord](https://discord.com/invite/fluentxyz) #developer-forum +3. **Discord Community**: Join the [Fluent Discord](https://discord.com/invite/fluentxyz) and get support in the #devs-forum channel 4. **Example Projects**: Review [GitHub examples](https://github.com/fluentlabs-xyz/examples) ### How to Ask for Help @@ -567,4 +556,4 @@ When seeking help, provide: --- -**Still stuck?** Don't hesitate to reach out to the Fluent community. We're here to help you succeed! 🚀 +**Still stuck?** Don't hesitate to reach out to the Fluent community. diff --git a/docs/developer-guides/common-patterns.md b/docs/fluentbase-sdk/common-patterns.md similarity index 66% rename from docs/developer-guides/common-patterns.md rename to docs/fluentbase-sdk/common-patterns.md index 5cecaff..786e4aa 100644 --- a/docs/developer-guides/common-patterns.md +++ b/docs/fluentbase-sdk/common-patterns.md @@ -12,9 +12,9 @@ This guide covers common development patterns, best practices, and real-world ex Before diving into these patterns, make sure you have: -- Basic understanding of [Rust smart contracts](./smart-contracts/rust.mdx) -- Familiarity with [Solidity development](./smart-contracts/solidity.mdx) -- Experience with [blended applications](./building-a-blended-app/README.md) +- Basic understanding of [Rust smart contracts](/docs/developer-guides/smart-contracts/rust.mdx) +- Familiarity with [Solidity development](/docs/developer-guides/smart-contracts/solidity.mdx) +- Experience with [blended applications](/docs/developer-guides/building-a-blended-app/README.md) - `gblend` tool installed and configured ::: @@ -22,9 +22,7 @@ Before diving into these patterns, make sure you have: ## Table of Contents - [Error Handling Patterns](#error-handling-patterns) -- [Gas Optimization](#gas-optimization) - [Security Best Practices](#security-best-practices) -- [Testing Strategies](#testing-strategies) - [Debugging Techniques](#debugging-techniques) - [Performance Optimization](#performance-optimization) - [Common Anti-Patterns](#common-anti-patterns) @@ -109,184 +107,6 @@ fn critical_operation(&self, value: U256) -> U256 { } ``` -### Solidity Error Handling - -#### 1. Custom Errors (Gas Efficient) - -```solidity -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -contract ErrorHandlingExample { - // Custom errors are more gas efficient than require statements - error InsufficientBalance(uint256 available, uint256 required); - error InvalidAddress(address provided); - error ValueTooHigh(uint256 value, uint256 max); - - mapping(address => uint256) public balances; - - function withdraw(uint256 amount) external { - uint256 balance = balances[msg.sender]; - - if (balance < amount) { - revert InsufficientBalance(balance, amount); - } - - if (amount > 1000 ether) { - revert ValueTooHigh(amount, 1000 ether); - } - - balances[msg.sender] = balance - amount; - // Transfer logic here - } - - function setBalance(address user, uint256 amount) external { - if (user == address(0)) { - revert InvalidAddress(user); - } - - balances[user] = amount; - } -} -``` - -#### 2. Require Statements with Custom Messages - -```solidity -function transfer(address to, uint256 amount) external { - require(to != address(0), "Transfer to zero address"); - require(amount > 0, "Amount must be greater than zero"); - require(balances[msg.sender] >= amount, "Insufficient balance"); - - balances[msg.sender] -= amount; - balances[to] += amount; -} -``` - -## Gas Optimization - -### Rust Contract Optimization - -#### 1. Efficient Storage Patterns - -```rust -#[derive(Contract)] -struct OptimizedStorage { - sdk: SDK, -} - -pub trait StorageAPI { - fn set_value(&mut self, key: U256, value: U256); - fn get_value(&self, key: U256) -> U256; - fn batch_set(&mut self, keys: Vec, values: Vec); -} - -#[router(mode = "solidity")] -impl StorageAPI for OptimizedStorage { - - #[function_id("setValue(uint256,uint256)")] - fn set_value(&mut self, key: U256, value: U256) { - // Use efficient storage patterns - self.sdk.set_storage(key, value); - } - - #[function_id("getValue(uint256)")] - fn get_value(&self, key: U256) -> U256 { - self.sdk.get_storage(key) - } - - #[function_id("batchSet(uint256[],uint256[])")] - fn batch_set(&mut self, keys: Vec, values: Vec) { - // Batch operations reduce gas costs - for (key, value) in keys.iter().zip(values.iter()) { - self.sdk.set_storage(*key, *value); - } - } -} - -basic_entrypoint!(OptimizedStorage); -``` - -#### 2. Memory Management - -```rust -// Avoid unnecessary allocations -#[function_id("efficientString()")] -fn efficient_string(&self) -> String { - // Pre-allocate with known size when possible - let mut result = String::with_capacity(100); - result.push_str("Hello"); - result.push_str(" World"); - result -} - -// Use references when possible -#[function_id("processArray(uint256[])")] -fn process_array(&self, data: &[U256]) -> U256 { - let mut sum = U256::zero(); - for item in data { - sum += *item; - } - sum -} -``` - -### Solidity Gas Optimization - -#### 1. Storage Layout Optimization - -```solidity -contract GasOptimized { - // Pack related variables together - struct User { - uint128 balance; // 16 bytes - uint64 lastUpdate; // 8 bytes - uint64 userId; // 8 bytes - // Total: 32 bytes (one storage slot) - } - - // Use uint256 for single variables to avoid packing overhead - uint256 public totalSupply; - - // Use bytes32 for fixed-size data - mapping(address => bytes32) public userData; - - // Use uint8 for small enums - enum Status { Pending, Active, Inactive } - mapping(address => Status) public userStatus; -} -``` - -#### 2. Function Optimization - -```solidity -contract OptimizedFunctions { - // Use external for functions only called externally - function externalFunction() external pure returns (uint256) { - return 42; - } - - // Use public for functions that need internal access - function publicFunction() public pure returns (uint256) { - return externalFunction(); - } - - // Avoid unnecessary storage reads - function optimizedRead() external view returns (uint256) { - // Cache storage reads - uint256 value = storageValue; - return value + value; // Use cached value twice - } - - // Use unchecked for arithmetic that can't overflow - function uncheckedIncrement(uint256 x) external pure returns (uint256) { - unchecked { - return x + 1; - } - } -} -``` - ## Security Best Practices ### 1. Access Control @@ -294,6 +114,15 @@ contract OptimizedFunctions { #### Rust Implementation ```rust +use fluentbase_sdk::derive::solidity_storage; + +// Define storage layout +solidity_storage! { + Address Owner; // Slot 0 + bool Paused; // Slot 1 + bool ReentrancyLock; // Slot 2 +} + #[derive(Contract)] struct SecureContract { sdk: SDK, @@ -301,7 +130,7 @@ struct SecureContract { pub trait SecurityAPI { fn only_owner_function(&self) -> String; - fn pausable_function(&self) -> String; +e fn pausable_function(&self) -> String; fn reentrancy_protected(&mut self) -> U256; } @@ -310,11 +139,11 @@ impl SecurityAPI for SecureContract { #[function_id("onlyOwnerFunction()")] fn only_owner_function(&self) -> String { - // Check if caller is owner - let caller = self.sdk.get_caller(); - let owner = self.sdk.get_storage(U256::from(0)); // Owner stored at slot 0 + // Check if caller is owner using proper storage access + let caller = self.sdk.context().contract_caller(); + let owner = Owner::get(&self.sdk); - if caller != Address::from_slice(&owner.to_be_bytes()) { + if caller != owner { panic!("Only owner can call this function"); } @@ -323,10 +152,10 @@ impl SecurityAPI for SecureContract { #[function_id("pausableFunction()")] fn pausable_function(&self) -> String { - // Check if contract is paused - let paused = self.sdk.get_storage(U256::from(1)); // Paused flag at slot 1 + // Check if contract is paused using proper storage access + let paused = Paused::get(&self.sdk); - if !paused.is_zero() { + if paused { panic!("Contract is paused"); } @@ -335,22 +164,21 @@ impl SecurityAPI for SecureContract { #[function_id("reentrancyProtected()")] fn reentrancy_protected(&mut self) -> U256 { - // Simple reentrancy protection - let lock_key = U256::from(2); - let lock_value = self.sdk.get_storage(lock_key); + // Simple reentrancy protection using proper storage access + let lock_value = ReentrancyLock::get(&self.sdk); - if !lock_value.is_zero() { + if lock_value { panic!("Reentrancy detected"); } // Set lock - self.sdk.set_storage(lock_key, U256::from(1)); + ReentrancyLock::set(&mut self.sdk, true); // Perform operation let result = U256::from(42); // Clear lock - self.sdk.set_storage(lock_key, U256::zero()); + ReentrancyLock::set(&mut self.sdk, false); result } @@ -414,7 +242,7 @@ fn validate_inputs(&self, amount: U256, recipient: Address, data: Bytes) -> bool } ``` -## Testing Strategies + ## Debugging Techniques @@ -566,11 +394,16 @@ fn batch_process(&self, items: Vec) -> Vec { ### 2. Caching Strategies ```rust +use fluentbase_sdk::derive::solidity_storage; + +solidity_storage! { + mapping(U256 => U256) Cache; +} + #[function_id("cachedComputation(uint256)")] fn cached_computation(&self, input: U256) -> U256 { // Check cache first - let cache_key = input; - let cached_result = self.sdk.get_storage(cache_key); + let cached_result = Cache::get(&self.sdk, input); if !cached_result.is_zero() { return cached_result; @@ -580,7 +413,7 @@ fn cached_computation(&self, input: U256) -> U256 { let result = expensive_computation(input); // Cache result (in real implementation, you'd want to limit cache size) - self.sdk.set_storage(cache_key, result); + Cache::set(&mut self.sdk, input, result); result } @@ -677,15 +510,14 @@ Now that you understand these patterns and best practices, you can: 5. **Monitor gas usage** and optimize performance For more advanced topics, explore: -- [Rust Smart Contracts](./smart-contracts/rust.mdx) -- [Solidity Development](./smart-contracts/solidity.mdx) -- [Building Blended Apps](./building-a-blended-app/README.md) +- [Rust Smart Contracts](/docs/developer-guides/smart-contracts/rust.mdx) +- [Solidity Development](/docs/developer-guides/smart-contracts/solidity.mdx) +- [Building Blended Apps](/docs/developer-guides/building-a-blended-app/README.md) :::tip[Community Resources] Join the Fluent community for more tips and best practices: - [Discord Developer Forum](https://discord.com/invite/fluentxyz) -- [GitHub Discussions](https://github.com/fluentlabs-xyz/docs-docusaurus/discussions) - [Example Projects](https://github.com/fluentlabs-xyz/examples) :::