diff --git a/docs/developer-guides/troubleshooting-guide.md b/docs/developer-guides/troubleshooting-guide.md new file mode 100644 index 0000000..b0a853a --- /dev/null +++ b/docs/developer-guides/troubleshooting-guide.md @@ -0,0 +1,559 @@ +--- +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.4.3-dev" # 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/" +] +``` + +## 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 create --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 $RPC_URL +``` + +#### 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 \ + --verifier blockscout \ + --verifier-url https://testnet.fluentscan.xyz/api/ \ + --constructor-args + +# Verify WASM contract +gblend verify-contract
YourContract.wasm \ + --wasm \ + --verifier blockscout \ + --verifier-url https://testnet.fluentscan.xyz/api/ +``` + +**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 + + + +## 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 the `solidity_storage!` macro for proper storage management: + +```rust +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 { + Owner::get(&self.sdk) +} + +fn set_owner(&mut self, new_owner: Address) { + Owner::set(&mut self.sdk, new_owner); +} +``` + +#### 2. Storage Type Mismatches + +**Problem**: Reading wrong data type from storage. + +**Solution**: Use the generated storage methods for type safety: + +```rust +// Store and retrieve with same type using generated methods +fn set_balance(&mut self, user: Address, amount: U256) { + Balance::set(&mut self.sdk, user, amount); +} + +fn get_balance(&self, user: Address) -> U256 { + Balance::get(&self.sdk, user) +} + +// The macro ensures type safety - this won't compile if types don't match +``` + +## Performance Issues + +### High Gas Consumption + +#### 1. Inefficient Storage Operations + +**Problem**: Excessive gas usage for storage operations. + +**Solution**: Use the `solidity_storage!` macro for optimized storage access: + +```rust +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); + } +} + +// 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 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 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()); + } +} +``` + +## 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) 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 + +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. diff --git a/docs/fluentbase-sdk/common-patterns.md b/docs/fluentbase-sdk/common-patterns.md new file mode 100644 index 0000000..786e4aa --- /dev/null +++ b/docs/fluentbase-sdk/common-patterns.md @@ -0,0 +1,523 @@ +--- +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](/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 + +::: + +## Table of Contents + +- [Error Handling Patterns](#error-handling-patterns) +- [Security Best Practices](#security-best-practices) +- [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) +} +``` + +## Security Best Practices + +### 1. Access Control + +#### 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, +} + +pub trait SecurityAPI { + fn only_owner_function(&self) -> String; +e 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 using proper storage access + let caller = self.sdk.context().contract_caller(); + let owner = Owner::get(&self.sdk); + + if caller != owner { + 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 using proper storage access + let paused = Paused::get(&self.sdk); + + if paused { + panic!("Contract is paused"); + } + + "Function executed".to_string() + } + + #[function_id("reentrancyProtected()")] + fn reentrancy_protected(&mut self) -> U256 { + // Simple reentrancy protection using proper storage access + let lock_value = ReentrancyLock::get(&self.sdk); + + if lock_value { + panic!("Reentrancy detected"); + } + + // Set lock + ReentrancyLock::set(&mut self.sdk, true); + + // Perform operation + let result = U256::from(42); + + // Clear lock + ReentrancyLock::set(&mut self.sdk, false); + + 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 +} +``` + + + +## 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 +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 cached_result = Cache::get(&self.sdk, input); + + 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) + Cache::set(&mut self.sdk, input, 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](/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) +- [Example Projects](https://github.com/fluentlabs-xyz/examples) + +:::