🎯 Рекомендуемые коллекции

Балансированные коллекции примеров кода из различных категорий, которые вы можете исследовать

Умные контракты Ethereum на Solidity

Комплексные примеры разработки умных контрактов Ethereum включая ERC-20, ERC-721, протоколы DeFi и интеграцию с аппаратными кошельками

💻 Основы Solidity и умные контракты solidity

🟢 simple ⭐⭐

Основные концепции Solidity, типы данных, функции, модификаторы, события и полный пример простого умного контракта

⏱️ 30 min 🏷️ solidity, basics, smart-contracts, ethereum
Prerequisites: Basic programming concepts, Blockchain fundamentals
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

// Basic Solidity Concepts and Data Types
contract SolidityBasics {

    // State Variables
    uint256 public myNumber = 42;
    string public myString = "Hello, Solidity!";
    bool public myBool = true;
    address public owner;

    // Integer Types
    int256 public myInt = -100;
    uint8 public smallUint = 255;
    uint256 public largeUint;

    // Fixed Size Arrays
    uint256[5] public fixedArray = [1, 2, 3, 4, 5];
    address[3] public addressArray;

    // Dynamic Arrays
    uint256[] public dynamicArray;
    string[] public stringArray;

    // Mappings (Key-Value Pairs)
    mapping(address => uint256) public balances;
    mapping(string => address) public names;

    // Structs (Custom Data Types)
    struct Person {
        string name;
        uint256 age;
        bool isStudent;
    }

    Person public defaultPerson;
    mapping(address => Person) public people;

    // Enums (Limited Set of Values)
    enum State { Created, Locked, Inactive }
    State public currentState;

    // Constants and Immutables
    uint256 public constant MAX_SUPPLY = 1000000;
    address public immutable CREATOR;

    // Events
    event NumberChanged(uint256 oldValue, uint256 newValue);
    event PersonAdded(address indexed personAddress, string name, uint256 age);
    event StateChanged(State oldState, State newState);
    event EtherReceived(address indexed from, uint256 amount);

    // Modifiers
    modifier onlyOwner() {
        require(msg.sender == owner, "Only owner can call this function");
        _;
    }

    modifier validAddress(address _addr) {
        require(_addr != address(0), "Invalid address");
        _;
    }

    modifier costs(uint256 amount) {
        require(msg.value >= amount, "Not enough ether sent");
        _;
    }

    // Constructor (runs once during deployment)
    constructor() {
        owner = msg.sender;
        CREATOR = msg.sender;
        defaultPerson = Person("Default", 25, false);
        currentState = State.Created;
    }

    // Basic Functions
    function setNumber(uint256 _newNumber) public {
        uint256 oldValue = myNumber;
        myNumber = _newNumber;
        emit NumberChanged(oldValue, _newNumber);
    }

    function getNumber() public view returns (uint256) {
        return myNumber;
    }

    function addPerson(string memory _name, uint256 _age) public validAddress(msg.sender) {
        people[msg.sender] = Person(_name, _age, false);
        emit PersonAdded(msg.sender, _name, _age);
    }

    function getPerson(address _addr) public view returns (string memory name, uint256 age, bool isStudent) {
        Person memory person = people[_addr];
        return (person.name, person.age, person.isStudent);
    }

    // Array Operations
    function addToDynamicArray(uint256 _value) public {
        dynamicArray.push(_value);
    }

    function getDynamicArrayLength() public view returns (uint256) {
        return dynamicArray.length;
    }

    function getDynamicArrayElement(uint256 _index) public view returns (uint256) {
        require(_index < dynamicArray.length, "Index out of bounds");
        return dynamicArray[_index];
    }

    // Mapping Operations
    function setBalance(address _addr, uint256 _amount) public {
        balances[_addr] = _amount;
    }

    function getBalance(address _addr) public view returns (uint256) {
        return balances[_addr];
    }

    // State Management
    function changeState(State _newState) public onlyOwner {
        State oldState = currentState;
        currentState = _newState;
        emit StateChanged(oldState, _newState);
    }

    // Ether Handling
    function deposit() public payable {
        require(msg.value > 0, "Must send ether");
        emit EtherReceived(msg.sender, msg.value);
    }

    function withdraw(uint256 _amount) public onlyOwner {
        require(address(this).balance >= _amount, "Insufficient contract balance");
        payable(owner).transfer(_amount);
    }

    // Get contract balance
    function getBalance() public view returns (uint256) {
        return address(this).balance;
    }

    // Fallback function to receive ether
    receive() external payable {
        emit EtherReceived(msg.sender, msg.value);
    }

    // Fallback function for unknown function calls
    fallback() external payable {
        emit EtherReceived(msg.sender, msg.value);
    }
}

// Advanced Example: Simple Voting Contract
contract SimpleVoting {

    struct Candidate {
        string name;
        uint256 voteCount;
        bool exists;
    }

    mapping(string => Candidate) public candidates;
    mapping(address => bool) public hasVoted;

    string[] public candidateNames;
    uint256 public votingEndTime;
    bool public votingActive;

    event VoteCast(address indexed voter, string candidateName);
    event CandidateAdded(string candidateName);
    event VotingStarted(uint256 endTime);
    event VotingEnded();

    modifier onlyWhileActive() {
        require(votingActive && block.timestamp < votingEndTime, "Voting is not active");
        _;
    }

    modifier onlyOnce() {
        require(!hasVoted[msg.sender], "You have already voted");
        _;
    }

    constructor() {
        votingActive = false;
    }

    function addCandidate(string memory _name) public {
        require(!candidates[_name].exists, "Candidate already exists");
        require(bytes(_name).length > 0, "Candidate name cannot be empty");

        candidates[_name] = Candidate({
            name: _name,
            voteCount: 0,
            exists: true
        });

        candidateNames.push(_name);
        emit CandidateAdded(_name);
    }

    function startVoting(uint256 _durationInMinutes) public {
        require(!votingActive, "Voting is already active");
        require(candidateNames.length >= 2, "Need at least 2 candidates");

        votingActive = true;
        votingEndTime = block.timestamp + (_durationInMinutes * 60);
        emit VotingStarted(votingEndTime);
    }

    function vote(string memory _candidateName) public onlyWhileActive onlyOnce {
        require(candidates[_candidateName].exists, "Candidate does not exist");

        candidates[_candidateName].voteCount++;
        hasVoted[msg.sender] = true;

        emit VoteCast(msg.sender, _candidateName);
    }

    function endVoting() public {
        require(votingActive, "Voting is not active");
        require(block.timestamp >= votingEndTime, "Voting period has not ended");

        votingActive = false;
        emit VotingEnded();
    }

    function getWinner() public view returns (string memory winnerName, uint256 winnerVotes) {
        require(!votingActive, "Voting is still active");

        uint256 maxVotes = 0;
        winnerName = "No candidates";

        for (uint256 i = 0; i < candidateNames.length; i++) {
            string memory name = candidateNames[i];
            if (candidates[name].voteCount > maxVotes) {
                maxVotes = candidates[name].voteCount;
                winnerName = name;
            }
        }

        winnerVotes = maxVotes;
    }

    function getCandidateInfo(string memory _name) public view returns (string memory, uint256, bool) {
        return (
            candidates[_name].name,
            candidates[_name].voteCount,
            candidates[_name].exists
        );
    }

    function getTotalCandidates() public view returns (uint256) {
        return candidateNames.length;
    }
}

// Library Example: SafeMath
library SafeMath {
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");
        return c;
    }

    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "SafeMath: subtraction overflow");
        uint256 c = a - b;
        return c;
    }

    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }
        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");
        return c;
    }

    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: division by zero");
        uint256 c = a / b;
        return c;
    }
}

// Example using the SafeMath library
contract BankAccount {
    using SafeMath for uint256;

    mapping(address => uint256) public balances;

    event Deposit(address indexed from, uint256 amount);
    event Withdrawal(address indexed to, uint256 amount);

    function deposit() public payable {
        require(msg.value > 0, "Must deposit ether");
        balances[msg.sender] = balances[msg.sender].add(msg.value);
        emit Deposit(msg.sender, msg.value);
    }

    function withdraw(uint256 _amount) public {
        require(_amount <= balances[msg.sender], "Insufficient balance");
        balances[msg.sender] = balances[msg.sender].sub(_amount);
        payable(msg.sender).transfer(_amount);
        emit Withdrawal(msg.sender, _amount);
    }

    function getBalance() public view returns (uint256) {
        return balances[msg.sender];
    }
}

💻 Реализация стандарта ERC-20 solidity

🟡 intermediate ⭐⭐⭐⭐

Полный контракт ERC-20 с функциями чеканки, сжигания, паузы и расширенными возможностями как права голоса и временные блокировки переводов

⏱️ 45 min 🏷️ erc20, token, solidity, ethereum, smart-contracts
Prerequisites: Solidity basics, ERC-20 standard, Token economics
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

// Interface for ERC-20 Token Standard
interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address to, uint256 amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);
    function transferFrom(address from, address to, uint256 amount) external returns (bool);

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

// OpenZeppelin's SafeMath library for safe arithmetic operations
library SafeMath {
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");
        return c;
    }

    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "SafeMath: subtraction overflow");
        uint256 c = a - b;
        return c;
    }

    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }
        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");
        return c;
    }

    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: division by zero");
        uint256 c = a / b;
        return c;
    }
}

// Advanced ERC-20 Token Contract with Additional Features
contract AdvancedERC20Token is IERC20 {
    using SafeMath for uint256;

    // Token Metadata
    string public name;
    string public symbol;
    uint8 public decimals;

    // Token State Variables
    uint256 private _totalSupply;
    mapping(address => uint256) private _balances;
    mapping(address => mapping(address => uint256)) private _allowances;

    // Ownership
    address public owner;
    address public minter;

    // Pausable Feature
    bool public paused = false;

    // Time-locked Transfers
    mapping(address => uint256) public lockedUntil;
    mapping(address => uint256) public lockAmount;

    // Blacklist
    mapping(address => bool) public blacklisted;

    // Vesting Schedule
    struct VestingSchedule {
        uint256 totalAmount;
        uint256 releasedAmount;
        uint256 startTime;
        uint256 duration;
        bool isActive;
    }

    mapping(address => VestingSchedule) public vestingSchedules;

    // Voting Rights
    mapping(address => uint256) public votingPower;
    uint256 public totalVotingPower;

    // Events
    event TokensMinted(address indexed to, uint256 amount);
    event TokensBurned(address indexed from, uint256 amount);
    event TokensLocked(address indexed account, uint256 amount, uint256 until);
    event TokensUnlocked(address indexed account, uint256 amount);
    event AccountBlacklisted(address indexed account);
    event AccountWhitelisted(address indexed account);
    event VestingCreated(address indexed beneficiary, uint256 amount, uint256 duration);
    event VestingClaimed(address indexed beneficiary, uint256 amount);
    event Pause();
    event Unpause();
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    event MinterChanged(address indexed previousMinter, address indexed newMinter);

    // Modifiers
    modifier onlyOwner() {
        require(msg.sender == owner, "Only owner can call this function");
        _;
    }

    modifier onlyMinter() {
        require(msg.sender == minter || msg.sender == owner, "Only minter can call this function");
        _;
    }

    modifier whenNotPaused() {
        require(!paused, "Token contract is paused");
        _;
    }

    modifier notBlacklisted(address _account) {
        require(!blacklisted[_account], "Account is blacklisted");
        _;
    }

    modifier validAddress(address _account) {
        require(_account != address(0), "Invalid address");
        _;
    }

    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals,
        uint256 _initialSupply
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;
        owner = msg.sender;
        minter = msg.sender;

        if (_initialSupply > 0) {
            _mint(msg.sender, _initialSupply);
        }
    }

    // ERC-20 Standard Functions

    function totalSupply() public view override returns (uint256) {
        return _totalSupply;
    }

    function balanceOf(address account) public view override returns (uint256) {
        return _balances[account];
    }

    function transfer(address to, uint256 amount) public override
        whenNotPaused
        notBlacklisted(msg.sender)
        notBlacklisted(to)
        validAddress(to)
        returns (bool) {

        _transfer(msg.sender, to, amount);
        return true;
    }

    function allowance(address owner, address spender) public view override returns (uint256) {
        return _allowances[owner][spender];
    }

    function approve(address spender, uint256 amount) public override
        whenNotPaused
        notBlacklisted(msg.sender)
        notBlacklisted(spender)
        validAddress(spender)
        returns (bool) {

        _approve(msg.sender, spender, amount);
        return true;
    }

    function transferFrom(address from, address to, uint256 amount) public override
        whenNotPaused
        notBlacklisted(from)
        notBlacklisted(to)
        notBlacklisted(msg.sender)
        validAddress(to)
        returns (bool) {

        uint256 currentAllowance = _allowances[from][msg.sender];
        require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");

        _transfer(from, to, amount);
        _approve(from, msg.sender, currentAllowance - amount);

        return true;
    }

    // Internal Functions

    function _transfer(address from, address to, uint256 amount) internal {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");

        // Check for locked tokens
        require(block.timestamp >= lockedUntil[from] || fromBalance - amount >= lockAmount[from],
               "ERC20: transfer of locked tokens");

        _balances[from] = fromBalance - amount;
        _balances[to] = _balances[to].add(amount);

        // Update voting power
        _updateVotingPower(from);
        _updateVotingPower(to);

        emit Transfer(from, to, amount);
    }

    function _approve(address owner, address spender, uint256 amount) internal {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    function _mint(address to, uint256 amount) internal {
        require(to != address(0), "ERC20: mint to the zero address");

        _totalSupply = _totalSupply.add(amount);
        _balances[to] = _balances[to].add(amount);

        _updateVotingPower(to);

        emit Transfer(address(0), to, amount);
        emit TokensMinted(to, amount);
    }

    function _burn(address from, uint256 amount) internal {
        require(from != address(0), "ERC20: burn from the zero address");

        uint256 accountBalance = _balances[from];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");

        _balances[from] = accountBalance - amount;
        _totalSupply = _totalSupply - amount;

        _updateVotingPower(from);

        emit Transfer(from, address(0), amount);
        emit TokensBurned(from, amount);
    }

    function _updateVotingPower(address account) internal {
        uint256 newPower = _balances[account];
        uint256 oldPower = votingPower[account];

        votingPower[account] = newPower;
        totalVotingPower = totalVotingPower.sub(oldPower).add(newPower);
    }

    // Minting and Burning

    function mint(address to, uint256 amount) external onlyMinter validAddress(to) {
        require(to != address(0), "Cannot mint to zero address");
        _mint(to, amount);
    }

    function burn(uint256 amount) external {
        _burn(msg.sender, amount);
    }

    function burnFrom(address from, uint256 amount) external {
        uint256 currentAllowance = _allowances[from][msg.sender];
        require(currentAllowance >= amount, "ERC20: burn amount exceeds allowance");

        _burn(from, amount);
        _approve(from, msg.sender, currentAllowance - amount);
    }

    // Pausable Functions

    function pause() external onlyOwner {
        require(!paused, "Contract is already paused");
        paused = true;
        emit Pause();
    }

    function unpause() external onlyOwner {
        require(paused, "Contract is not paused");
        paused = false;
        emit Unpause();
    }

    // Ownership Management

    function transferOwnership(address newOwner) external onlyOwner validAddress(newOwner) {
        address oldOwner = owner;
        owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }

    function setMinter(address newMinter) external onlyOwner validAddress(newMinter) {
        address oldMinter = minter;
        minter = newMinter;
        emit MinterChanged(oldMinter, newMinter);
    }

    // Token Locking

    function lockTokens(uint256 amount, uint256 duration) external {
        require(amount <= _balances[msg.sender], "Insufficient balance to lock");

        lockAmount[msg.sender] = lockAmount[msg.sender].add(amount);
        lockedUntil[msg.sender] = lockedUntil[msg.sender].add(duration);

        emit TokensLocked(msg.sender, amount, lockedUntil[msg.sender]);
    }

    function unlockTokens() external {
        require(block.timestamp >= lockedUntil[msg.sender], "Tokens are still locked");
        require(lockAmount[msg.sender] > 0, "No tokens to unlock");

        uint256 amount = lockAmount[msg.sender];
        lockAmount[msg.sender] = 0;

        emit TokensUnlocked(msg.sender, amount);
    }

    // Blacklist Management

    function blacklist(address account) external onlyOwner validAddress(account) {
        blacklisted[account] = true;
        emit AccountBlacklisted(account);
    }

    function whitelist(address account) external onlyOwner validAddress(account) {
        blacklisted[account] = false;
        emit AccountWhitelisted(account);
    }

    // Vesting Functions

    function createVesting(
        address beneficiary,
        uint256 amount,
        uint256 duration
    ) external onlyMinter validAddress(beneficiary) {
        require(amount > 0, "Vesting amount must be greater than 0");
        require(duration > 0, "Vesting duration must be greater than 0");

        vestingSchedules[beneficiary] = VestingSchedule({
            totalAmount: amount,
            releasedAmount: 0,
            startTime: block.timestamp,
            duration: duration,
            isActive: true
        });

        _mint(beneficiary, amount);
        emit VestingCreated(beneficiary, amount, duration);
    }

    function claimVesting() external {
        VestingSchedule storage schedule = vestingSchedules[msg.sender];
        require(schedule.isActive, "No active vesting schedule");

        uint256 timePassed = block.timestamp - schedule.startTime;
        uint256 vestedAmount;

        if (timePassed >= schedule.duration) {
            vestedAmount = schedule.totalAmount;
        } else {
            vestedAmount = (schedule.totalAmount * timePassed) / schedule.duration;
        }

        uint256 claimableAmount = vestedAmount - schedule.releasedAmount;
        require(claimableAmount > 0, "No tokens to claim");

        schedule.releasedAmount = vestedAmount;

        if (timePassed >= schedule.duration) {
            schedule.isActive = false;
        }

        emit VestingClaimed(msg.sender, claimableAmount);
    }

    // View Functions

    function getVestingInfo(address beneficiary) external view returns (
        uint256 totalAmount,
        uint256 releasedAmount,
        uint256 vestedAmount,
        uint256 claimableAmount,
        uint256 startTime,
        uint256 duration,
        bool isActive
    ) {
        VestingSchedule memory schedule = vestingSchedules[beneficiary];

        uint256 timePassed = block.timestamp - schedule.startTime;
        uint256 _vestedAmount;

        if (timePassed >= schedule.duration) {
            _vestedAmount = schedule.totalAmount;
        } else {
            _vestedAmount = (schedule.totalAmount * timePassed) / schedule.duration;
        }

        uint256 _claimableAmount = _vestedAmount - schedule.releasedAmount;

        return (
            schedule.totalAmount,
            schedule.releasedAmount,
            _vestedAmount,
            _claimableAmount,
            schedule.startTime,
            schedule.duration,
            schedule.isActive
        );
    }

    function getLockedTokens(address account) external view returns (uint256 amount, uint256 until) {
        return (lockAmount[account], lockedUntil[account]);
    }

    // Batch Operations

    function batchTransfer(address[] calldata recipients, uint256[] calldata amounts)
        external
        whenNotPaused
        returns (bool) {

        require(recipients.length == amounts.length, "Arrays must have same length");
        require(recipients.length > 0, "Arrays cannot be empty");

        for (uint256 i = 0; i < recipients.length; i++) {
            require(recipients[i] != address(0), "Invalid recipient address");
            require(!blacklisted[recipients[i]], "Recipient is blacklisted");

            _transfer(msg.sender, recipients[i], amounts[i]);
        }

        return true;
    }

    function batchApprove(address[] calldata spenders, uint256[] calldata amounts)
        external
        whenNotPaused
        returns (bool) {

        require(spenders.length == amounts.length, "Arrays must have same length");
        require(spenders.length > 0, "Arrays cannot be empty");

        for (uint256 i = 0; i < spenders.length; i++) {
            require(spenders[i] != address(0), "Invalid spender address");
            require(!blacklisted[spenders[i]], "Spender is blacklisted");

            _approve(msg.sender, spenders[i], amounts[i]);
        }

        return true;
    }
}

💻 Контракт NFT ERC-721 с метаданными solidity

🟡 intermediate ⭐⭐⭐⭐

Полная реализация ERC-721 с метаданными IPFS, системой роялти и интеграцией с маркетплейсом

⏱️ 50 min 🏷️ erc721, nft, solidity, ethereum, smart-contracts
Prerequisites: Solidity basics, ERC-721 standard, IPFS, Metadata standards
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

// ERC-721 Non-Fungible Token Standard
interface IERC721 {
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    function balanceOf(address owner) external view returns (uint256 balance);
    function ownerOf(uint256 tokenId) external view returns (address owner);
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
    function safeTransferFrom(address from, address to, uint256 tokenId) external;
    function transferFrom(address from, address to, uint256 tokenId) external;
    function approve(address to, uint256 tokenId) external;
    function setApprovalForAll(address operator, bool approved) external;
    function getApproved(uint256 tokenId) external view returns (address operator);
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

// ERC-721 Metadata Extension
interface IERC721Metadata is IERC721 {
    function name() external view returns (string memory);
    function symbol() external view returns (string memory);
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

// ERC-721 Enumerable Extension
interface IERC721Enumerable is IERC721 {
    function totalSupply() external view returns (uint256);
    function tokenByIndex(uint256 index) external view returns (uint256);
    function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
}

// OpenZeppelin libraries
library Counters {
    struct Counter {
        uint256 _value;
    }

    function current(Counter storage counter) internal view returns (uint256) {
        return counter._value;
    }

    function increment(Counter storage counter) internal {
        counter._value += 1;
    }

    function decrement(Counter storage counter) internal {
        counter._value = counter._value > 0 ? counter._value - 1 : 0;
    }
}

library Address {
    function isContract(address account) internal view returns (bool) {
        return account.code.length > 0;
    }
}

library Strings {
    function toString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }
}

// NFT Metadata Structure
struct NFTMetadata {
    string name;
    string description;
    string image;
    string external_url;
    string animation_url;
    string background_color;
    string youtube_url;
    Attribute[] attributes;
}

struct Attribute {
    string trait_type;
    string value;
    uint256 display_type; // 0: string, 1: number, 2: boost_percentage
}

// Royalty Structure
struct RoyaltyInfo {
    address recipient;
    uint256 percentage; // Percentage in basis points (10000 = 100%)
}

// Advanced ERC-721 NFT Contract
contract AdvancedNFT is IERC721, IERC721Metadata, IERC721Enumerable {
    using Counters for Counters.Counter;
    using Strings for uint256;
    using Address for address;

    // Token State Variables
    Counters.Counter private _tokenIdCounter;

    string private _name;
    string private _symbol;
    string private _baseTokenURI;

    // Mappings
    mapping(uint256 => address) private _owners;
    mapping(address => uint256) private _balances;
    mapping(uint256 => address) private _tokenApprovals;
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    // Owner mappings for enumeration
    mapping(address => uint256[]) private _ownedTokens;
    mapping(uint256 => uint256) private _ownedTokensIndex;
    mapping(uint256 => uint256) private _allTokensIndex;
    uint256[] private _allTokens;

    // NFT-specific data
    mapping(uint256 => NFTMetadata) private _tokenMetadata;
    mapping(uint256 => RoyaltyInfo) private _royalties;
    mapping(uint256 => bool) private _exists;
    mapping(uint256 => bool) private _burned;

    // Contract metadata
    address public owner;
    address public minter;
    uint256 public maxSupply;
    uint256 public mintPrice;
    bool public mintingActive;

    // Marketplace integration
    mapping(uint256 => bool) public _forSale;
    mapping(uint256 => uint256) public _salePrice;
    mapping(uint256 => address) public _saleCurrency; // address(0) for ETH

    // Events
    event TokenMinted(address indexed to, uint256 indexed tokenId);
    event TokenBurned(uint256 indexed tokenId);
    event MetadataUpdated(uint256 indexed tokenId);
    event RoyaltySet(uint256 indexed tokenId, address recipient, uint256 percentage);
    event TokenListed(uint256 indexed tokenId, uint256 price, address currency);
    event TokenSold(uint256 indexed tokenId, address indexed from, address indexed to, uint256 price);
    event MintingStateChanged(bool active);
    event MintPriceChanged(uint256 newPrice);

    // Modifiers
    modifier onlyOwner() {
        require(msg.sender == owner, "Only owner can call this function");
        _;
    }

    modifier onlyMinter() {
        require(msg.sender == minter || msg.sender == owner, "Only minter can call this function");
        _;
    }

    modifier whenMintingActive() {
        require(mintingActive, "Minting is not active");
        _;
    }

    modifier validToken(uint256 tokenId) {
        require(_exists[tokenId] && !_burned[tokenId], "Token does not exist");
        _;
    }

    modifier onlyTokenOwner(uint256 tokenId) {
        require(ownerOf(tokenId) == msg.sender, "Only token owner can call this function");
        _;
    }

    constructor(
        string memory name_,
        string memory symbol_,
        string memory baseTokenURI_,
        uint256 maxSupply_,
        uint256 mintPrice_
    ) {
        _name = name_;
        _symbol = symbol_;
        _baseTokenURI = baseTokenURI_;
        maxSupply = maxSupply_;
        mintPrice = mintPrice_;
        owner = msg.sender;
        minter = msg.sender;
        mintingActive = false;
    }

    // ERC-721 Standard Functions

    function name() external view override returns (string memory) {
        return _name;
    }

    function symbol() external view override returns (string memory) {
        return _symbol;
    }

    function tokenURI(uint256 tokenId) external view override validToken(tokenId) returns (string memory) {
        string memory baseURI = _baseTokenURI;
        return bytes(baseURI).length > 0
            ? string(abi.encodePacked(baseURI, tokenId.toString()))
            : "";
    }

    function balanceOf(address owner) external view override returns (uint256) {
        require(owner != address(0), "ERC721: balance query for the zero address");
        return _balances[owner];
    }

    function ownerOf(uint256 tokenId) public view override validToken(tokenId) returns (address) {
        return _owners[tokenId];
    }

    function approve(address to, uint256 tokenId) external override validToken(tokenId) {
        address owner = ownerOf(tokenId);
        require(to != owner, "ERC721: approval to current owner");

        require(msg.sender == owner || isApprovedForAll(owner, msg.sender),
               "ERC721: approve caller is not owner nor approved for all");

        _approve(to, tokenId);
    }

    function getApproved(uint256 tokenId) public view override validToken(tokenId) returns (address) {
        return _tokenApprovals[tokenId];
    }

    function setApprovalForAll(address operator, bool approved) external override {
        require(operator != msg.sender, "ERC721: approve to caller");
        _operatorApprovals[msg.sender][operator] = approved;
        emit ApprovalForAll(msg.sender, operator, approved);
    }

    function isApprovedForAll(address owner, address operator) public view override returns (bool) {
        return _operatorApprovals[owner][operator];
    }

    function transferFrom(address from, address to, uint256 tokenId) external override validToken(tokenId) {
        require(_isApprovedOrOwner(msg.sender, tokenId), "ERC721: transfer caller is not owner nor approved");
        _transfer(from, to, tokenId);
    }

    function safeTransferFrom(address from, address to, uint256 tokenId) external override {
        safeTransferFrom(from, to, tokenId, "");
    }

    function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public override validToken(tokenId) {
        require(_isApprovedOrOwner(msg.sender, tokenId), "ERC721: transfer caller is not owner nor approved");
        _safeTransfer(from, to, tokenId, data);
    }

    // ERC-721 Enumerable Functions

    function totalSupply() public view override returns (uint256) {
        return _allTokens.length;
    }

    function tokenByIndex(uint256 index) public view override returns (uint256) {
        require(index < totalSupply(), "ERC721Enumerable: global index out of bounds");
        return _allTokens[index];
    }

    function tokenOfOwnerByIndex(address owner, uint256 index) public view override returns (uint256) {
        require(index < balanceOf(owner), "ERC721Enumerable: owner index out of bounds");
        return _ownedTokens[owner][index];
    }

    // Internal Functions

    function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal {
        _transfer(from, to, tokenId);
        require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer");
    }

    function _transfer(address from, address to, uint256 tokenId) internal validToken(tokenId) {
        require(ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
        require(to != address(0), "ERC721: transfer to the zero address");

        _beforeTokenTransfer(from, to, tokenId);

        // Clear approvals
        _approve(address(0), tokenId);

        _balances[from] -= 1;
        _balances[to] += 1;
        _owners[tokenId] = to;

        // Update owned tokens list
        _removeTokenFromOwnerEnumeration(from, tokenId);
        _addTokenToOwnerEnumeration(to, tokenId);

        emit Transfer(from, to, tokenId);
    }

    function _mint(address to, uint256 tokenId) internal {
        require(to != address(0), "ERC721: mint to the zero address");
        require(!_exists[tokenId], "ERC721: token already minted");

        _beforeTokenTransfer(address(0), to, tokenId);

        _balances[to] += 1;
        _owners[tokenId] = to;
        _exists[tokenId] = true;

        _addTokenToOwnerEnumeration(to, tokenId);
        _addTokenToAllTokensEnumeration(tokenId);

        emit Transfer(address(0), to, tokenId);
    }

    function _burn(uint256 tokenId) internal validToken(tokenId) {
        address owner = ownerOf(tokenId);

        _beforeTokenTransfer(owner, address(0), tokenId);

        // Clear approvals
        _approve(address(0), tokenId);

        _balances[owner] -= 1;
        _burned[tokenId] = true;

        // Remove from owner enumeration
        _removeTokenFromOwnerEnumeration(owner, tokenId);
        // Note: Keep in _allTokens for totalSupply consistency

        emit Transfer(owner, address(0), tokenId);
        emit TokenBurned(tokenId);
    }

    function _approve(address to, uint256 tokenId) internal validToken(tokenId) {
        _tokenApprovals[tokenId] = to;
        emit Approval(ownerOf(tokenId), to, tokenId);
    }

    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {
        require(_exists[tokenId], "ERC721: operator query for nonexistent token");
        address owner = ownerOf(tokenId);
        return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
    }

    function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory data) private returns (bool) {
        if (to.isContract()) {
            try IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, data) returns (bytes4 retval) {
                return retval == IERC721Receiver.onERC721Received.selector;
            } catch {
                return false;
            }
        }
        return true;
    }

    function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal {
        // Can be overridden by child contracts
    }

    // Enumeration helpers
    function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
        uint256 length = balanceOf(to);
        _ownedTokens[to].push(tokenId);
        _ownedTokensIndex[tokenId] = length;
    }

    function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
        _allTokensIndex[tokenId] = _allTokens.length;
        _allTokens.push(tokenId);
    }

    function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
        uint256 lastTokenIndex = balanceOf(from) - 1;
        uint256 tokenIndex = _ownedTokensIndex[tokenId];

        if (tokenIndex != lastTokenIndex) {
            uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];
            _ownedTokens[from][tokenIndex] = lastTokenId;
            _ownedTokensIndex[lastTokenId] = tokenIndex;
        }

        delete _ownedTokensIndex[tokenId];
        _ownedTokens[from].pop();
    }

    // NFT-specific Functions

    function mintNFT(address to, NFTMetadata memory metadata) external onlyMinter whenMintingActive returns (uint256) {
        require(_tokenIdCounter.current() < maxSupply, "Max supply reached");

        uint256 tokenId = _tokenIdCounter.current();
        _tokenIdCounter.increment();

        _mint(to, tokenId);
        _setTokenMetadata(tokenId, metadata);

        emit TokenMinted(to, tokenId);
        return tokenId;
    }

    function mintBatch(address to, uint256 amount, NFTMetadata[] memory metadata) external onlyMinter whenMintingActive returns (uint256[] memory) {
        require(_tokenIdCounter.current() + amount <= maxSupply, "Max supply reached");
        require(amount == metadata.length, "Metadata array length mismatch");

        uint256[] memory tokenIds = new uint256[](amount);

        for (uint256 i = 0; i < amount; i++) {
            uint256 tokenId = _tokenIdCounter.current();
            _tokenIdCounter.increment();

            _mint(to, tokenId);
            _setTokenMetadata(tokenId, metadata[i]);

            tokenIds[i] = tokenId;
            emit TokenMinted(to, tokenId);
        }

        return tokenIds;
    }

    function burn(uint256 tokenId) external onlyTokenOwner(tokenId) {
        _burn(tokenId);
    }

    function setTokenMetadata(uint256 tokenId, NFTMetadata memory metadata) external onlyTokenOwner(tokenId) {
        _setTokenMetadata(tokenId, metadata);
        emit MetadataUpdated(tokenId);
    }

    function _setTokenMetadata(uint256 tokenId, NFTMetadata memory metadata) internal {
        _tokenMetadata[tokenId] = metadata;
    }

    function getTokenMetadata(uint256 tokenId) external view validToken(tokenId) returns (NFTMetadata memory) {
        return _tokenMetadata[tokenId];
    }

    function setRoyalty(uint256 tokenId, address recipient, uint256 percentage) external onlyTokenOwner(tokenId) {
        require(percentage <= 10000, "Royalty percentage cannot exceed 100%");
        _royalties[tokenId] = RoyaltyInfo(recipient, percentage);
        emit RoyaltySet(tokenId, recipient, percentage);
    }

    function getRoyalty(uint256 tokenId) external view validToken(tokenId) returns (address recipient, uint256 percentage) {
        RoyaltyInfo memory royalty = _royalties[tokenId];
        return (royalty.recipient, royalty.percentage);
    }

    // Marketplace Functions

    function listForSale(uint256 tokenId, uint256 price, address currency) external onlyTokenOwner(tokenId) {
        _forSale[tokenId] = true;
        _salePrice[tokenId] = price;
        _saleCurrency[tokenId] = currency;
        emit TokenListed(tokenId, price, currency);
    }

    function removeFromSale(uint256 tokenId) external onlyTokenOwner(tokenId) {
        _forSale[tokenId] = false;
        _salePrice[tokenId] = 0;
        _saleCurrency[tokenId] = address(0);
    }

    function buyToken(uint256 tokenId) external payable validToken(tokenId) {
        require(_forSale[tokenId], "Token not for sale");
        address seller = ownerOf(tokenId);
        require(msg.sender != seller, "Cannot buy your own token");
        address currency = _saleCurrency[tokenId];
        uint256 price = _salePrice[tokenId];

        if (currency == address(0)) {
            require(msg.value >= price, "Insufficient ETH sent");
            payable(seller).transfer(price);
        }

        // Handle royalty
        (address royaltyRecipient, uint256 royaltyPercentage) = getRoyalty(tokenId);
        if (royaltyRecipient != address(0) && royaltyPercentage > 0) {
            uint256 royaltyAmount = (price * royaltyPercentage) / 10000;
            if (currency == address(0)) {
                payable(royaltyRecipient).transfer(royaltyAmount);
            }
        }

        _transfer(seller, msg.sender, tokenId);

        // Remove from sale
        _forSale[tokenId] = false;
        _salePrice[tokenId] = 0;
        _saleCurrency[tokenId] = address(0);

        emit TokenSold(tokenId, seller, msg.sender, price);
    }

    // View Functions

    function getSaleInfo(uint256 tokenId) external view returns (bool forSale, uint256 price, address currency) {
        return (_forSale[tokenId], _salePrice[tokenId], _saleCurrency[tokenId]);
    }

    function tokensOfOwner(address owner) external view returns (uint256[] memory) {
        uint256 balance = balanceOf(owner);
        uint256[] memory result = new uint256[](balance);
        for (uint256 i = 0; i < balance; i++) {
            result[i] = tokenOfOwnerByIndex(owner, i);
        }
        return result;
    }

    function exists(uint256 tokenId) external view returns (bool) {
        return _exists[tokenId] && !_burned[tokenId];
    }

    // Admin Functions

    function setBaseURI(string memory baseURI) external onlyOwner {
        _baseTokenURI = baseURI;
    }

    function setMintingActive(bool active) external onlyOwner {
        mintingActive = active;
        emit MintingStateChanged(active);
    }

    function setMintPrice(uint256 newPrice) external onlyOwner {
        mintPrice = newPrice;
        emit MintPriceChanged(newPrice);
    }

    function setMinter(address newMinter) external onlyOwner {
        minter = newMinter;
    }

    function transferOwnership(address newOwner) external onlyOwner {
        owner = newOwner;
    }

    // Emergency Functions

    function emergencyWithdraw() external onlyOwner {
        payable(owner).transfer(address(this).balance);
    }
}

// Interface for ERC721 Receiver
interface IERC721Receiver {
    function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4);
}

💻 Протокол DeFi AMM (Автоматический Маркет-Мейкер) solidity

🔴 complex ⭐⭐⭐⭐⭐

Полная реализация AMM с пулами ликвидности, функцией обмена, фармингом ликвидности и токенами управления

⏱️ 60 min 🏷️ defi, amm, liquidity, yield-farming, solidity
Prerequisites: Advanced Solidity, DeFi concepts, Mathematical modeling, Gas optimization
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

// Interfaces for DeFi protocols
interface IERC20 {
    function balanceOf(address account) external view returns (uint256);
    function transfer(address to, uint256 amount) external returns (bool);
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
    function approve(address spender, uint256 amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
}

interface IWETH is IERC20 {
    function deposit() external payable;
    function withdraw(uint256 amount) external;
}

// Library for safe math operations
library SafeMath {
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");
        return c;
    }

    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "SafeMath: subtraction overflow");
        uint256 c = a - b;
        return c;
    }

    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }
        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");
        return c;
    }

    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: division by zero");
        uint256 c = a / b;
        return c;
    }
}

// Library for Uniswap-style calculations
library UniswapV2Library {
    using SafeMath for uint256;

    // Minimum amount out calculation
    function getAmountOut(
        uint256 amountIn,
        uint256 reserveIn,
        uint256 reserveOut
    ) internal pure returns (uint256 amountOut) {
        require(amountIn > 0, "UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT");
        require(reserveIn > 0 && reserveOut > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY");

        uint256 numerator = amountIn.mul(reserveOut);
        uint256 denominator = reserveIn.add(amountIn);
        amountOut = numerator / denominator;
    }

    // Minimum amount in calculation
    function getAmountIn(
        uint256 amountOut,
        uint256 reserveIn,
        uint256 reserveOut
    ) internal pure returns (uint256 amountIn) {
        require(amountOut > 0, "UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT");
        require(reserveIn > 0 && reserveOut > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY");

        uint256 numerator = reserveIn.mul(amountOut);
        uint256 denominator = reserveOut.sub(amountOut);
        amountIn = numerator / denominator;
    }

    // K calculation (constant product)
    function quote(
        uint256 amountA,
        uint256 reserveA,
        uint256 reserveB
    ) internal pure returns (uint256 amountB) {
        require(amountA > 0, "UniswapV2Library: INSUFFICIENT_AMOUNT");
        require(reserveA > 0 && reserveB > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY");

        amountB = amountA.mul(reserveB) / reserveA;
    }
}

// Liquidity Pool structure
struct LiquidityPool {
    address token0;
    address token1;
    uint256 reserve0;
    uint256 reserve1;
    uint256 totalLiquidity;
    address feeTo;
    uint256 feeNumerator; // Fee numerator (0-10000 basis points)
    bool initialized;
}

// Position structure for liquidity providers
struct Position {
    uint256 liquidity;
    uint256 token0Amount;
    uint256 token1Amount;
    uint256 lastUpdateTime;
    uint256 unclaimedFees0;
    uint256 unclaimedFees1;
}

// Farming position structure
struct FarmingPosition {
    uint256 amount;
    uint256 startTime;
    uint256 rewardDebt;
}

// Advanced AMM Protocol with Yield Farming
class AdvancedAMM {
    using SafeMath for uint256;
    using UniswapV2Library for uint256;

    // State variables
    address public owner;
    address public governanceToken;
    address public WETH;

    // Pools and positions
    mapping(bytes32 => LiquidityPool) public pools;
    mapping(address => mapping(bytes32 => Position)) public positions;
    mapping(address => mapping(bytes32 => FarmingPosition)) public farmingPositions;

    // Global state
    uint256 public totalValueLocked;
    uint256 public governanceRate; // Governance tokens per second

    // Fee configuration
    uint256 public protocolFeeNumerator = 3; // 0.3% protocol fee
    uint256 public liquidityProviderFeeNumerator = 25; // 0.25% LP fee

    // Events
    event PoolCreated(bytes32 indexed poolId, address indexed token0, address indexed token1);
    event LiquidityAdded(bytes32 indexed poolId, address indexed provider, uint256 amount0, uint256 amount1, uint256 liquidity);
    event LiquidityRemoved(bytes32 indexed poolId, address indexed provider, uint256 amount0, uint256 amount1, uint256 liquidity);
    event Swap(bytes32 indexed poolId, address indexed user, address tokenIn, uint256 amountIn, address tokenOut, uint256 amountOut);
    event FarmingPositionCreated(bytes32 indexed poolId, address indexed user, uint256 amount);
    event Harvested(bytes32 indexed poolId, address indexed user, uint256 governanceReward);

    // Modifiers
    modifier onlyOwner() {
        require(msg.sender == owner, "Only owner can call this function");
        _;
    }

    modifier validPool(bytes32 poolId) {
        require(pools[poolId].initialized, "Pool does not exist");
        _;
    }

    modifier validTokens(address token0, address token1) {
        require(token0 != address(0) && token1 != address(0), "Invalid token addresses");
        require(token0 != token1, "Tokens must be different");
        _;
    }

    constructor(address _governanceToken, address _WETH) {
        owner = msg.sender;
        governanceToken = _governanceToken;
        WETH = _WETH;
        governanceRate = 1 ether / (365 days); // 1 token per day distributed across all farms
    }

    // Pool management
    function createPool(address token0, address token1) external validTokens(token0, token1) returns (bytes32 poolId) {
        // Ensure tokens are in correct order (address ordering)
        if (token0 > token1) {
            (token0, token1) = (token1, token0);
        }

        poolId = keccak256(abi.encodePacked(token0, token1));

        require(!pools[poolId].initialized, "Pool already exists");

        pools[poolId] = LiquidityPool({
            token0: token0,
            token1: token1,
            reserve0: 0,
            reserve1: 0,
            totalLiquidity: 0,
            feeTo: msg.sender,
            feeNumerator: 30, // 0.3%
            initialized: true
        });

        emit PoolCreated(poolId, token0, token1);
        return poolId;
    }

    // Liquidity management
    function addLiquidity(
        bytes32 poolId,
        uint256 amount0Desired,
        uint256 amount1Desired,
        uint256 amount0Min,
        uint256 amount1Min,
        address to
    ) external payable validPool(poolId) returns (uint256 amount0, uint256 amount1, uint256 liquidity) {
        LiquidityPool storage pool = pools[poolId];

        // Handle ETH input
        if (pool.token0 == WETH || pool.token1 == WETH) {
            require(msg.value > 0, "Must send ETH for ETH pool");

            if (pool.token0 == WETH) {
                amount0Desired = msg.value;
                IWETH(WETH).deposit{value: msg.value}();
                IWETH(WETH).transfer(address(this), msg.value);
            } else {
                amount1Desired = msg.value;
                IWETH(WETH).deposit{value: msg.value}();
                IWETH(WETH).transfer(address(this), msg.value);
            }
        }

        // Calculate optimal amount if pool has no liquidity
        if (pool.reserve0 == 0 && pool.reserve1 == 0) {
            amount0 = amount0Desired;
            amount1 = amount1Desired;
        } else {
            uint256 amount1Optimal = amount0Desired.mul(pool.reserve1) / pool.reserve0;

            if (amount1Optimal <= amount1Desired) {
                require(amount1Optimal >= amount1Min, "Insufficient amount1");
                amount1 = amount1Optimal;
                amount0 = amount0Desired;
            } else {
                uint256 amount0Optimal = amount1Desired.mul(pool.reserve0) / pool.reserve1;
                require(amount0Optimal <= amount0Desired && amount0Optimal >= amount0Min, "Insufficient amount0");
                amount0 = amount0Optimal;
                amount1 = amount1Desired;
            }
        }

        // Transfer tokens from user
        if (pool.token0 != WETH) {
            IERC20(pool.token0).transferFrom(msg.sender, address(this), amount0);
        }
        if (pool.token1 != WETH) {
            IERC20(pool.token1).transferFrom(msg.sender, address(this), amount1);
        }

        // Calculate liquidity to mint
        if (pool.totalLiquidity == 0) {
            liquidity = amount0.mul(amount1).sqrt();
        } else {
            liquidity = amount0.mul(pool.totalLiquidity) / pool.reserve0;
        }

        // Update pool state
        pool.reserve0 = pool.reserve0.add(amount0);
        pool.reserve1 = pool.reserve1.add(amount1);
        pool.totalLiquidity = pool.totalLiquidity.add(liquidity);

        // Update user position
        Position storage position = positions[to][poolId];
        position.liquidity = position.liquidity.add(liquidity);
        position.token0Amount = position.token0Amount.add(amount0);
        position.token1Amount = position.token1Amount.add(amount1);
        position.lastUpdateTime = block.timestamp;

        totalValueLocked = totalValueLocked.add(amount0).add(amount1);

        emit LiquidityAdded(poolId, to, amount0, amount1, liquidity);
    }

    function removeLiquidity(
        bytes32 poolId,
        uint256 liquidity,
        uint256 amount0Min,
        uint256 amount1Min,
        address to
    ) external validPool(poolId) returns (uint256 amount0, uint256 amount1) {
        LiquidityPool storage pool = pools[poolId];
        Position storage position = positions[msg.sender][poolId];

        require(position.liquidity >= liquidity, "Insufficient liquidity");
        require(pool.totalLiquidity > 0, "No liquidity in pool");

        // Calculate amounts to receive
        amount0 = liquidity.mul(pool.reserve0) / pool.totalLiquidity;
        amount1 = liquidity.mul(pool.reserve1) / pool.totalLiquidity;

        require(amount0 >= amount0Min && amount1 >= amount1Min, "Slippage exceeded");

        // Update pool state
        pool.reserve0 = pool.reserve0.sub(amount0);
        pool.reserve1 = pool.reserve1.sub(amount1);
        pool.totalLiquidity = pool.totalLiquidity.sub(liquidity);

        // Update user position
        position.liquidity = position.liquidity.sub(liquidity);
        position.token0Amount = position.token0Amount.sub(amount0);
        position.token1Amount = position.token1Amount.sub(amount1);

        // Add unclaimed fees
        if (position.unclaimedFees0 > 0 || position.unclaimedFees1 > 0) {
            amount0 = amount0.add(position.unclaimedFees0);
            amount1 = amount1.add(position.unclaimedFees1);
            position.unclaimedFees0 = 0;
            position.unclaimedFees1 = 0;
        }

        // Transfer tokens to user
        if (pool.token0 == WETH) {
            IWETH(WETH).withdraw(amount0);
            payable(to).transfer(amount0);
        } else {
            IERC20(pool.token0).transfer(to, amount0);
        }

        if (pool.token1 == WETH) {
            IWETH(WETH).withdraw(amount1);
            payable(to).transfer(amount1);
        } else {
            IERC20(pool.token1).transfer(to, amount1);
        }

        totalValueLocked = totalValueLocked.sub(amount0).sub(amount1);

        emit LiquidityRemoved(poolId, msg.sender, amount0, amount1, liquidity);
    }

    // Swapping
    function swap(
        bytes32 poolId,
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to
    ) external payable validPool(poolId) returns (uint256[] memory amounts) {
        LiquidityPool storage pool = pools[poolId];

        require(path.length >= 2, "Invalid path");

        // Handle ETH input
        uint256 ethAmount = 0;
        if (msg.value > 0) {
            require(pool.token0 == WETH || pool.token1 == WETH, "ETH not supported in this pool");
            ethAmount = msg.value;
            IWETH(WETH).deposit{value: ethAmount}();
            IWETH(WETH).transfer(address(this), ethAmount);
            amountIn = ethAmount;
        } else {
            // Transfer input token
            IERC20(path[0]).transferFrom(msg.sender, address(this), amountIn);
        }

        amounts = new uint256[](path.length);

        // Single pool swap
        if (path.length == 2) {
            amounts = _swapSinglePool(poolId, path[0], path[1], amountIn, to);
        } else {
            // Multi-hop swap (simplified)
            amounts = _swapMultiHop(path, amountIn, to);
        }

        require(amounts[amounts.length - 1] >= amountOutMin, "Slippage exceeded");

        // Refund unused ETH
        if (msg.value > 0 && ethAmount > 0) {
            uint256 refund = msg.value - ethAmount;
            if (refund > 0) {
                payable(msg.sender).transfer(refund);
            }
        }

        emit Swap(poolId, msg.sender, path[0], amounts[0], path[path.length - 1], amounts[amounts.length - 1]);
    }

    function _swapSinglePool(
        bytes32 poolId,
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        address to
    ) internal returns (uint256[] memory amounts) {
        LiquidityPool storage pool = pools[poolId];

        require(
            (tokenIn == pool.token0 && tokenOut == pool.token1) ||
            (tokenIn == pool.token1 && tokenOut == pool.token0),
            "Invalid token pair"
        );

        uint256 reserve0 = pool.reserve0;
        uint256 reserve1 = pool.reserve1;

        require(reserve0 > 0 && reserve1 > 0, "Insufficient liquidity");

        uint256 amountOut;
        bool isToken0In = tokenIn == pool.token0;

        if (isToken0In) {
            amountOut = amountIn.getAmountOut(reserve0, reserve1);
            require(amountOut < reserve1, "Insufficient liquidity");

            pool.reserve0 = reserve0.add(amountIn);
            pool.reserve1 = reserve1.sub(amountOut);
        } else {
            amountOut = amountIn.getAmountOut(reserve1, reserve0);
            require(amountOut < reserve0, "Insufficient liquidity");

            pool.reserve1 = reserve1.add(amountIn);
            pool.reserve0 = reserve0.sub(amountOut);
        }

        // Transfer output token to user
        if (tokenOut == WETH) {
            IWETH(WETH).withdraw(amountOut);
            payable(to).transfer(amountOut);
        } else {
            IERC20(tokenOut).transfer(to, amountOut);
        }

        amounts = new uint256[](2);
        amounts[0] = amountIn;
        amounts[1] = amountOut;

        return amounts;
    }

    function _swapMultiHop(address[] calldata path, uint256 amountIn, address to) internal returns (uint256[] memory amounts) {
        // Simplified multi-hop implementation
        // In production, you'd implement proper multi-hop routing

        amounts = new uint256[](path.length);
        amounts[0] = amountIn;

        for (uint256 i = 0; i < path.length - 1; i++) {
            bytes32 poolId = _getPoolId(path[i], path[i + 1]);
            address nextRecipient = (i == path.length - 2) ? to : address(this);

            uint256[] memory hopAmounts = _swapSinglePool(poolId, path[i], path[i + 1], amounts[i], nextRecipient);
            amounts[i + 1] = hopAmounts[1];
        }

        return amounts;
    }

    // Yield farming
    function createFarmingPosition(bytes32 poolId, uint256 amount) external validPool(poolId) {
        Position storage position = positions[msg.sender][poolId];
        FarmingPosition storage farmingPosition = farmingPositions[msg.sender][poolId];

        require(position.liquidity >= amount, "Insufficient liquidity");

        // Update existing farming position or create new one
        if (farmingPosition.amount > 0) {
            _harvest(poolId, msg.sender);
        }

        farmingPosition.amount = farmingPosition.amount.add(amount);
        farmingPosition.startTime = block.timestamp;
        farmingPosition.rewardDebt = _calculatePendingRewards(poolId, msg.sender);

        emit FarmingPositionCreated(poolId, msg.sender, amount);
    }

    function harvest(bytes32 poolId) external validPool(poolId) {
        _harvest(poolId, msg.sender);
    }

    function _harvest(bytes32 poolId, address user) internal {
        FarmingPosition storage farmingPosition = farmingPositions[user][poolId];

        if (farmingPosition.amount == 0) return;

        uint256 pendingRewards = _calculatePendingRewards(poolId, user);
        require(pendingRewards > 0, "No rewards to harvest");

        farmingPosition.rewardDebt = farmingPosition.rewardDebt.add(pendingRewards);

        // Transfer governance tokens
        IERC20(governanceToken).transfer(user, pendingRewards);

        emit Harvested(poolId, user, pendingRewards);
    }

    function _calculatePendingRewards(bytes32 poolId, address user) internal view returns (uint256) {
        LiquidityPool storage pool = pools[poolId];
        FarmingPosition storage farmingPosition = farmingPositions[user][poolId];

        if (farmingPosition.amount == 0) return 0;

        uint256 timePassed = block.timestamp - farmingPosition.startTime;
        uint256 totalPoolLiquidity = pool.totalLiquidity;

        if (totalPoolLiquidity == 0) return 0;

        // Calculate rewards based on position size and time
        uint256 rewards = (governanceRate * timePassed * farmingPosition.amount) / totalPoolLiquidity;

        return rewards - farmingPosition.rewardDebt;
    }

    // Utility functions
    function _getPoolId(address token0, address token1) internal pure returns (bytes32) {
        if (token0 > token1) {
            (token0, token1) = (token1, token0);
        }
        return keccak256(abi.encodePacked(token0, token1));
    }

    function getPoolInfo(bytes32 poolId) external view returns (
        address token0,
        address token1,
        uint256 reserve0,
        uint256 reserve1,
        uint256 totalLiquidity,
        uint256 feeNumerator
    ) {
        LiquidityPool storage pool = pools[poolId];
        return (
            pool.token0,
            pool.token1,
            pool.reserve0,
            pool.reserve1,
            pool.totalLiquidity,
            pool.feeNumerator
        );
    }

    function getPosition(bytes32 poolId, address user) external view returns (
        uint256 liquidity,
        uint256 token0Amount,
        uint256 token1Amount,
        uint256 unclaimedFees0,
        uint256 unclaimedFees1
    ) {
        Position storage position = positions[user][poolId];
        return (
            position.liquidity,
            position.token0Amount,
            position.token1Amount,
            position.unclaimedFees0,
            position.unclaimedFees1
        );
    }

    function getFarmingPosition(bytes32 poolId, address user) external view returns (
        uint256 amount,
        uint256 startTime,
        uint256 pendingRewards
    ) {
        FarmingPosition storage farmingPosition = farmingPositions[user][poolId];
        uint256 rewards = _calculatePendingRewards(poolId, user);

        return (
            farmingPosition.amount,
            farmingPosition.startTime,
            rewards
        );
    }

    // sqrt function for liquidity calculation
    function sqrt(uint256 y) internal pure returns (uint256 z) {
        if (y > 3) {
            z = y;
            uint256 x = y / 2 + 1;
            while (x < z) {
                z = x;
                x = (y / x + x) / 2;
            }
        } else if (y != 0) {
            z = 1;
        }
    }

    // Admin functions
    function setGovernanceRate(uint256 newRate) external onlyOwner {
        governanceRate = newRate;
    }

    function setProtocolFee(uint256 newFeeNumerator) external onlyOwner {
        require(newFeeNumerator <= 1000, "Fee too high"); // Max 10%
        protocolFeeNumerator = newFeeNumerator;
    }

    function setLiquidityProviderFee(uint256 newFeeNumerator) external onlyOwner {
        require(newFeeNumerator <= 1000, "Fee too high"); // Max 10%
        liquidityProviderFeeNumerator = newFeeNumerator;
    }

    function emergencyWithdraw(address token, uint256 amount) external onlyOwner {
        if (token == address(0)) {
            payable(owner).transfer(amount);
        } else {
            IERC20(token).transfer(owner, amount);
        }
    }
}

// Math utilities for sqrt calculation
library Math {
    function sqrt(uint256 y) internal pure returns (uint256 z) {
        if (y > 3) {
            z = y;
            uint256 x = y / 2 + 1;
            while (x < z) {
                z = x;
                x = (y / x + x) / 2;
            }
        } else if (y != 0) {
            z = 1;
        }
    }
}

💻 Интеграция аппаратных кошельков и Multi-Sig javascript

🔴 complex ⭐⭐⭐⭐⭐

Интеграция с аппаратными кошельками Ledger и Trezor, мульти-подписные кошельки и безопасное управление ключами

⏱️ 45 min 🏷️ hardware-wallet, ledger, trezor, multi-sig, ethereum
Prerequisites: JavaScript, Ethereum, Web3, Hardware wallet concepts
// Hardware Wallet Integration with Ethereum
// Supports Ledger, Trezor, and other hardware wallets
// Implements multi-signature schemes and secure key management

const { ethers } = require('ethers');
const TransportWebHID = require('@ledgerhq/hw-transport-webhid');
const AppEth = require('@ledgerhq/hw-app-eth');
const TransportWebUSB = require('@ledgerhq/hw-transport-webusb');

// Hardware Wallet Manager Class
class HardwareWalletManager {
    constructor() {
        this.connectedWallets = new Map();
        this.supportedWallets = ['ledger', 'trezor', 'metamask', 'walletconnect'];
        this.currentProvider = null;
    }

    // Initialize connection with hardware wallet
    async connectHardwareWallet(walletType) {
        try {
            let transport;
            let wallet;

            switch (walletType.toLowerCase()) {
                case 'ledger':
                    transport = await this._connectLedger();
                    wallet = new AppEth(transport);
                    break;

                case 'trezor':
                    wallet = await this._connectTrezor();
                    break;

                default:
                    throw new Error(`Unsupported wallet type: ${walletType}`);
            }

            const address = await wallet.getAddress(0, false, false);
            const walletInfo = {
                type: walletType,
                address: address.address,
                wallet: wallet,
                transport: transport,
                connectedAt: new Date()
            };

            this.connectedWallets.set(walletType, walletInfo);
            return walletInfo;

        } catch (error) {
            throw new Error(`Failed to connect ${walletType}: ${error.message}`);
        }
    }

    // Connect to Ledger hardware wallet
    async _connectLedger() {
        try {
            // Try WebHID first (more modern)
            let transport = await TransportWebHID.create();

            // Fallback to WebUSB if WebHID fails
            if (!transport) {
                transport = await TransportWebUSB.create();
            }

            if (!transport) {
                throw new Error('Ledger device not found. Please unlock your device and ensure it's connected.');
            }

            return transport;
        } catch (error) {
            throw new Error(`Ledger connection failed: ${error.message}`);
        }
    }

    // Connect to Trezor hardware wallet
    async _connectTrezor() {
        try {
            const TrezorConnect = require('@trezor/connect-web');

            await TrezorConnect.init({
                lazyLoad: true,
                manifest: {
                    email: '[email protected]',
                    appUrl: 'https://your-app-url.com'
                }
            });

            const result = await TrezorConnect.ethereumGetAddress({
                path: "m/44'/60'/0'/0/0",
                showOnDevice: true
            });

            if (!result.success) {
                throw new Error(result.payload.error);
            }

            return {
                type: 'trezor',
                address: result.payload.address,
                getBalance: this._getTrezorBalance.bind(this),
                signTransaction: this._signWithTrezor.bind(this),
                signMessage: this._signMessageWithTrezor.bind(this)
            };

        } catch (error) {
            throw new Error(`Trezor connection failed: ${error.message}`);
        }
    }

    // Get address from hardware wallet
    async getAddress(walletType, derivationPath = "m/44'/60'/0'/0/0") {
        const walletInfo = this.connectedWallets.get(walletType);
        if (!walletInfo) {
            throw new Error(`Wallet ${walletType} not connected`);
        }

        try {
            if (walletType === 'ledger') {
                const result = await walletInfo.wallet.getAddress(derivationPath, false, false);
                return result.address;
            } else if (walletType === 'trezor') {
                const TrezorConnect = require('@trezor/connect-web');
                const result = await TrezorConnect.ethereumGetAddress({
                    path: derivationPath,
                    showOnDevice: true
                });
                return result.payload.address;
            }
        } catch (error) {
            throw new Error(`Failed to get address from ${walletType}: ${error.message}`);
        }
    }

    // Sign transaction with hardware wallet
    async signTransaction(walletType, txData, chainId = 1) {
        const walletInfo = this.connectedWallets.get(walletType);
        if (!walletInfo) {
            throw new Error(`Wallet ${walletType} not connected`);
        }

        try {
            if (walletType === 'ledger') {
                return await this._signWithLedger(walletInfo.wallet, txData, chainId);
            } else if (walletType === 'trezor') {
                return await this._signWithTrezor(txData, chainId);
            }
        } catch (error) {
            throw new Error(`Failed to sign transaction with ${walletType}: ${error.message}`);
        }
    }

    // Sign with Ledger device
    async _signWithLedger(wallet, txData, chainId) {
        // Parse transaction data
        const ethersTx = ethers.utils.parseTransaction(txData);

        // Get derivation path
        const derivationPath = "m/44'/60'/0'/0/0";

        // Prepare signature data for Ledger
        const resolution = await wallet.resolveTransaction(
            ethersTx.raw,
            {
                erc20: true,
                nft: true
            }
        );

        // Sign transaction
        const signature = await wallet.signTransaction(
            derivationPath,
            ethersTx.raw,
            resolution
        );

        // Format signature for Ethereum
        const { v, r, s } = signature;

        // Create signed transaction
        const signedTx = ethers.utils.serializeTransaction({
            ...ethersTx,
            v: v,
            r: r,
            s: s
        });

        return signedTx;
    }

    // Sign with Trezor device
    async _signWithTrezor(txData, chainId) {
        const TrezorConnect = require('@trezor/connect-web');

        const ethersTx = ethers.utils.parseTransaction(txData);

        const result = await TrezorConnect.ethereumSignTransaction({
            path: "m/44'/60'/0'/0/0",
            transaction: {
                to: ethersTx.to,
                value: ethersTx.value.toString(),
                data: ethersTx.data || '',
                chainId: chainId,
                nonce: ethersTx.nonce,
                gasLimit: ethersTx.gasLimit.toString(),
                gasPrice: ethersTx.gasPrice?.toString() || '0',
                maxPriorityFeePerGas: ethersTx.maxPriorityFeePerGas?.toString() || null,
                maxFeePerGas: ethersTx.maxFeePerGas?.toString() || null
            }
        });

        if (!result.success) {
            throw new Error(result.payload.error);
        }

        // Combine signature components
        const signedTx = ethers.utils.serializeTransaction({
            ...ethersTx,
            v: parseInt(result.payload.v, 16),
            r: result.payload.r,
            s: result.payload.s
        });

        return signedTx;
    }

    // Sign message with hardware wallet
    async signMessage(walletType, message, derivationPath = "m/44'/60'/0'/0/0") {
        const walletInfo = this.connectedWallets.get(walletType);
        if (!walletInfo) {
            throw new Error(`Wallet ${walletType} not connected`);
        }

        try {
            if (walletType === 'ledger') {
                const messageHex = ethers.utils.hexlify(ethers.utils.toUtf8Bytes(message));
                const signature = await walletInfo.wallet.signPersonalMessage(
                    derivationPath,
                    messageHex
                );

                return {
                    signature: '0x' + signature.r + signature.s + signature.v.toString(16),
                    message: message,
                    address: await this.getAddress(walletType, derivationPath)
                };

            } else if (walletType === 'trezor') {
                const TrezorConnect = require('@trezor/connect-web');
                const result = await TrezorConnect.ethereumSignMessage({
                    path: derivationPath,
                    message: ethers.utils.hexlify(ethers.utils.toUtf8Bytes(message)),
                    hex: true
                });

                if (!result.success) {
                    throw new Error(result.payload.error);
                }

                return {
                    signature: result.payload.signature,
                    message: message,
                    address: result.payload.address
                };
            }
        } catch (error) {
            throw new Error(`Failed to sign message with ${walletType}: ${error.message}`);
        }
    }

    // Disconnect wallet
    async disconnectWallet(walletType) {
        const walletInfo = this.connectedWallets.get(walletType);
        if (walletInfo) {
            if (walletInfo.transport) {
                await walletInfo.transport.close();
            }
            this.connectedWallets.delete(walletType);
        }
    }

    // Get wallet info
    getWalletInfo(walletType) {
        const walletInfo = this.connectedWallets.get(walletType);
        if (walletInfo) {
            return {
                type: walletInfo.type,
                address: walletInfo.address,
                connectedAt: walletInfo.connectedAt
            };
        }
        return null;
    }

    // List all connected wallets
    listConnectedWallets() {
        const wallets = {};
        for (const [type, info] of this.connectedWallets) {
            wallets[type] = {
                type: info.type,
                address: info.address,
                connectedAt: info.connectedAt
            };
        }
        return wallets;
    }
}

// Multi-Signature Wallet Implementation
class MultiSigWallet {
    constructor(provider, contractAddress, owners, requiredSignatures = 2) {
        this.provider = provider;
        this.contractAddress = contractAddress;
        this.owners = owners;
        this.requiredSignatures = requiredSignatures;
        this.walletManager = new HardwareWalletManager();

        // Initialize contract interface
        this.contract = new ethers.Contract(
            contractAddress,
            MULTI_SIG_ABI,
            provider
        );
    }

    // Create transaction proposal
    async proposeTransaction(to, value, data, walletType) {
        try {
            // Get nonce for this wallet
            const nonce = await this.contract.nonce();

            // Create transaction data
            const txData = {
                to: to,
                value: value || '0',
                data: data || '0x',
                nonce: nonce
            };

            // Sign with hardware wallet
            const signature = await this.walletManager.signTransaction(walletType, txData);

            // Create proposal
            const proposal = {
                id: ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(
                    ['address', 'uint256', 'bytes', 'uint256'],
                    [to, value, data, nonce]
                )),
                to: to,
                value: value,
                data: data,
                nonce: nonce,
                signatures: [signature],
                proposers: [await this.walletManager.getAddress(walletType)]
            };

            return proposal;
        } catch (error) {
            throw new Error(`Failed to propose transaction: ${error.message}`);
        }
    }

    // Sign existing proposal
    async signProposal(proposal, walletType) {
        try {
            // Check if already signed
            const signerAddress = await this.walletManager.getAddress(walletType);
            if (proposal.signers && proposal.signers.includes(signerAddress)) {
                throw new Error('Already signed this proposal');
            }

            // Create signature
            const signature = await this.walletManager.signTransaction(walletType, {
                to: proposal.to,
                value: proposal.value,
                data: proposal.data,
                nonce: proposal.nonce
            });

            // Add signature to proposal
            proposal.signatures.push(signature);
            if (!proposal.signers) {
                proposal.signers = [];
            }
            proposal.signers.push(signerAddress);
            if (!proposal.proposers) {
                proposal.proposers = [];
            }
            proposal.proposers.push(signerAddress);

            return proposal;
        } catch (error) {
            throw new Error(`Failed to sign proposal: ${error.message}`);
        }
    }

    // Execute transaction if enough signatures collected
    async executeTransaction(proposal) {
        try {
            if (proposal.signatures.length < this.requiredSignatures) {
                throw new Error(`Need ${this.requiredSignatures} signatures, got ${proposal.signatures.length}`);
            }

            // Submit to multi-sig contract
            const tx = await this.contract.executeTransaction(
                proposal.to,
                proposal.value,
                proposal.data,
                proposal.signatures
            );

            return await tx.wait();
        } catch (error) {
            throw new Error(`Failed to execute transaction: ${error.message}`);
        }
    }
}

// Multi-Sig Contract ABI (simplified)
const MULTI_SIG_ABI = [
    "function executeTransaction(address to, uint256 value, bytes data, bytes[] signatures) external",
    "function nonce() view returns (uint256)",
    "function getOwners() view returns (address[])",
    "function getRequiredSignatures() view returns (uint256)"
];

// Example usage and testing functions
async function demonstrateHardwareWalletIntegration() {
    console.log('=== Hardware Wallet Integration Demo ===');

    const walletManager = new HardwareWalletManager();

    try {
        // Connect to Ledger
        console.log('\n1. Connecting to Ledger...');
        const ledgerInfo = await walletManager.connectHardwareWallet('ledger');
        console.log(`Connected to Ledger: ${ledgerInfo.address}`);

        // Connect to Trezor
        console.log('\n2. Connecting to Trezor...');
        const trezorInfo = await walletManager.connectHardwareWallet('trezor');
        console.log(`Connected to Trezor: ${trezorInfo.address}`);

        // Get balances
        console.log('\n3. Checking balances...');
        const provider = new ethers.providers.InfuraProvider('mainnet', process.env.INFURA_PROJECT_ID);

        const ledgerBalance = await provider.getBalance(ledgerInfo.address);
        const trezorBalance = await provider.getBalance(trezorInfo.address);

        console.log(`Ledger balance: ${ethers.utils.formatEther(ledgerBalance)} ETH`);
        console.log(`Trezor balance: ${ethers.utils.formatEther(trezorBalance)} ETH`);

        // Sign a message
        console.log('\n4. Signing messages...');
        const message = "Hello, Hardware Wallet!";

        const ledgerSignature = await walletManager.signMessage('ledger', message);
        console.log(`Ledger signature: ${ledgerSignature.signature}`);

        const trezorSignature = await walletManager.signMessage('trezor', message);
        console.log(`Trezor signature: ${trezorSignature.signature}`);

        // Create and sign a transaction (example)
        console.log('\n5. Creating and signing transactions...');
        const txData = {
            to: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6',
            value: ethers.utils.parseEther('0.001'),
            gasLimit: 21000,
            gasPrice: ethers.utils.parseUnits('20', 'gwei')
        };

        const ledgerTx = await walletManager.signTransaction('ledger', txData);
        console.log(`Ledger transaction signed: ${ledgerTx.substring(0, 50)}...`);

        // Multi-sig example
        console.log('\n6. Multi-signature wallet example...');
        const owners = [
            ledgerInfo.address,
            trezorInfo.address,
            '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6' // Third owner
        ];

        const multiSigContractAddress = '0x1234567890123456789012345678901234567890'; // Example address
        const multiSig = new MultiSigWallet(provider, multiSigContractAddress, owners, 2);

        // Create proposal
        const proposal = await multiSig.proposeTransaction(
            '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6',
            ethers.utils.parseEther('0.1'),
            '0x',
            'ledger'
        );

        console.log(`Multi-sig proposal created: ${proposal.id}`);
        console.log(`Signatures: ${proposal.signatures.length}/${multiSig.requiredSignatures}`);

        // Add second signature
        const signedProposal = await multiSig.signProposal(proposal, 'trezor');
        console.log(`Second signature added. Total: ${signedProposal.signatures.length}`);

        if (signedProposal.signatures.length >= multiSig.requiredSignatures) {
            console.log('Enough signatures collected - ready to execute');
            // const result = await multiSig.executeTransaction(signedProposal);
            // console.log(`Transaction executed: ${result.transactionHash}`);
        }

        // Disconnect wallets
        console.log('\n7. Disconnecting wallets...');
        await walletManager.disconnectWallet('ledger');
        await walletManager.disconnectWallet('trezor');

    } catch (error) {
        console.error('Error in demonstration:', error.message);
    }
}

// Key Management Utilities
class KeyManager {
    constructor() {
        this.encryptionKey = null;
        this.encryptedKeys = new Map();
    }

    // Generate secure random key
    generateSecureKey() {
        const array = new Uint8Array(32);
        crypto.getRandomValues(array);
        return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
    }

    // Encrypt private key
    async encryptPrivateKey(privateKey, password) {
        const encoder = new TextEncoder();
        const data = encoder.encode(privateKey);

        // Derive key from password
        const keyMaterial = await crypto.subtle.importKey(
            'raw',
            encoder.encode(password),
            'PBKDF2',
            false,
            ['deriveBits', 'deriveKey']
        );

        const key = await crypto.subtle.deriveKey(
            {
                name: 'PBKDF2',
                salt: encoder.encode('hardware-wallet-salt'),
                iterations: 100000,
                hash: 'SHA-256'
            },
            keyMaterial,
            { name: 'AES-GCM', length: 256 },
            false,
            ['encrypt', 'decrypt']
        );

        // Encrypt
        const iv = crypto.getRandomValues(new Uint8Array(12));
        const encrypted = await crypto.subtle.encrypt(
            { name: 'AES-GCM', iv: iv },
            key,
            data
        );

        return {
            encrypted: Array.from(new Uint8Array(encrypted)),
            iv: Array.from(iv)
        };
    }

    // Decrypt private key
    async decryptPrivateKey(encryptedData, password) {
        const encoder = new TextEncoder();
        const decoder = new TextDecoder();

        // Derive key from password
        const keyMaterial = await crypto.subtle.importKey(
            'raw',
            encoder.encode(password),
            'PBKDF2',
            false,
            ['deriveBits', 'deriveKey']
        );

        const key = await crypto.subtle.deriveKey(
            {
                name: 'PBKDF2',
                salt: encoder.encode('hardware-wallet-salt'),
                iterations: 100000,
                hash: 'SHA-256'
            },
            keyMaterial,
            { name: 'AES-GCM', length: 256 },
            false,
            ['encrypt', 'decrypt']
        );

        // Decrypt
        const decrypted = await crypto.subtle.decrypt(
            {
                name: 'AES-GCM',
                iv: new Uint8Array(encryptedData.iv)
            },
            key,
            new Uint8Array(encryptedData.encrypted)
        );

        return decoder.decode(decrypted);
    }
}

// Export classes for use in other modules
module.exports = {
    HardwareWalletManager,
    MultiSigWallet,
    KeyManager,
    demonstrateHardwareWalletIntegration
};

// Run demonstration if called directly
if (require.main === module) {
    demonstrateHardwareWalletIntegration();
}