CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-openzeppelin-solidity

Secure Smart Contract library providing battle-tested implementations of industry-standard Solidity contracts including ERC20, ERC721, ERC1155 tokens, access control mechanisms, proxy patterns, and governance systems for Ethereum and EVM-compatible blockchains.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

finance.mddocs/

Financial Primitives

OpenZeppelin Contracts provides essential financial mechanisms for handling cryptocurrency payments, distributions, and vesting schedules in smart contracts.

Core Imports

Import financial contracts using Solidity import statements:

import "@openzeppelin/contracts/finance/PaymentSplitter.sol";
import "@openzeppelin/contracts/finance/VestingWallet.sol";

Capabilities

Payment Splitter

Splits incoming payments among multiple payees according to their share allocation. Supports both Ether and ERC20 token distributions.

contract PaymentSplitter is Context {
    constructor(address[] memory payees, uint256[] memory shares_);
    
    function totalShares() public view returns (uint256);
    function totalReleased() public view returns (uint256);
    function totalReleased(IERC20 token) public view returns (uint256);
    function shares(address account) public view returns (uint256);
    function released(address account) public view returns (uint256);
    function released(IERC20 token, address account) public view returns (uint256);
    function payee(uint256 index) public view returns (address);
    function releasable(address account) public view returns (uint256);
    function releasable(IERC20 token, address account) public view returns (uint256);
    function release(address payable account) public virtual;
    function release(IERC20 token, address account) public virtual;
}

Events

event PayeeAdded(address account, uint256 shares);
event PaymentReleased(address to, uint256 amount);
event ERC20PaymentReleased(IERC20 indexed token, address to, uint256 amount);
event PaymentReceived(address from, uint256 amount);

Usage Example

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/finance/PaymentSplitter.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract RevenueSharing is PaymentSplitter {
    constructor(
        address[] memory founders,
        uint256[] memory founderShares
    ) PaymentSplitter(founders, founderShares) {}
    
    // Contract automatically receives and splits payments
    receive() external payable {
        // PaymentSplitter handles the payment tracking
    }
    
    function releaseAll() external {
        for (uint256 i = 0; i < payee(i) != address(0); i++) {
            address payeeAddress = payee(i);
            if (releasable(payeeAddress) > 0) {
                release(payable(payeeAddress));
            }
        }
    }
    
    function releaseToken(IERC20 token) external {
        for (uint256 i = 0; i < payee(i) != address(0); i++) {
            address payeeAddress = payee(i);
            if (releasable(token, payeeAddress) > 0) {
                release(token, payeeAddress);
            }
        }
    }
}

Vesting Wallet

Implements token vesting functionality with linear release schedules over a specified duration.

contract VestingWallet is Context {
    constructor(address beneficiaryAddress, uint64 startTimestamp, uint64 durationSeconds);
    
    function beneficiary() public view virtual returns (address);
    function start() public view virtual returns (uint256);
    function duration() public view virtual returns (uint256);
    function released() public view virtual returns (uint256);
    function released(address token) public view virtual returns (uint256);
    function releasable() public view virtual returns (uint256);
    function releasable(address token) public view virtual returns (uint256);
    function release() public virtual;
    function release(address token) public virtual;
    function vestedAmount(uint64 timestamp) public view virtual returns (uint256);
    function vestedAmount(address token, uint64 timestamp) public view virtual returns (uint256);
}

Events

event EtherReleased(uint256 amount);
event ERC20Released(address indexed token, uint256 amount);

Usage Example

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/finance/VestingWallet.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract TeamVesting {
    mapping(address => VestingWallet) public vestingWallets;
    IERC20 public token;
    
    constructor(IERC20 _token) {
        token = _token;
    }
    
    function createVestingSchedule(
        address beneficiary,
        uint256 amount,
        uint64 startTime,
        uint64 duration
    ) external {
        // Create vesting wallet for team member
        VestingWallet vestingWallet = new VestingWallet(
            beneficiary,
            startTime,
            duration
        );
        
        vestingWallets[beneficiary] = vestingWallet;
        
        // Transfer tokens to vesting wallet
        token.transfer(address(vestingWallet), amount);
    }
    
    function releaseVestedTokens(address beneficiary) external {
        VestingWallet wallet = vestingWallets[beneficiary];
        require(address(wallet) != address(0), "No vesting schedule");
        
        wallet.release(address(token));
    }
    
    function getVestingInfo(address beneficiary) 
        external 
        view 
        returns (
            uint256 totalAmount,
            uint256 releasedAmount,
            uint256 releasableAmount,
            uint256 startTime,
            uint256 duration
        ) 
    {
        VestingWallet wallet = vestingWallets[beneficiary];
        require(address(wallet) != address(0), "No vesting schedule");
        
        totalAmount = token.balanceOf(address(wallet)) + wallet.released(address(token));
        releasedAmount = wallet.released(address(token));
        releasableAmount = wallet.releasable(address(token));
        startTime = wallet.start();
        duration = wallet.duration();
    }
}

Advanced Financial Patterns

Escrow Pattern with Payment Splitter

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/finance/PaymentSplitter.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract EscrowPaymentSplitter is PaymentSplitter, ReentrancyGuard {
    enum EscrowState { Pending, Released, Disputed }
    
    struct Escrow {
        uint256 amount;
        EscrowState state;
        uint256 releaseTime;
    }
    
    mapping(bytes32 => Escrow) public escrows;
    address public arbiter;
    
    constructor(
        address[] memory payees,
        uint256[] memory shares_,
        address _arbiter
    ) PaymentSplitter(payees, shares_) {
        arbiter = _arbiter;
    }
    
    function createEscrow(bytes32 escrowId, uint256 releaseTime) external payable {
        require(escrows[escrowId].amount == 0, "Escrow already exists");
        require(msg.value > 0, "Must send funds");
        
        escrows[escrowId] = Escrow({
            amount: msg.value,
            state: EscrowState.Pending,
            releaseTime: releaseTime
        });
    }
    
    function releaseEscrow(bytes32 escrowId) external nonReentrant {
        Escrow storage escrow = escrows[escrowId];
        require(escrow.state == EscrowState.Pending, "Escrow not pending");
        require(block.timestamp >= escrow.releaseTime, "Release time not reached");
        
        escrow.state = EscrowState.Released;
        
        // Funds automatically split according to PaymentSplitter shares
        (bool success, ) = address(this).call{value: escrow.amount}("");
        require(success, "Failed to release funds");
    }
}

Milestone-Based Vesting

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract MilestoneVesting is Ownable {
    struct Milestone {
        uint256 amount;
        bool completed;
        bool released;
    }
    
    struct VestingSchedule {
        address beneficiary;
        uint256 totalAmount;
        uint256 releasedAmount;
        Milestone[] milestones;
    }
    
    mapping(address => VestingSchedule) public vestingSchedules;
    IERC20 public token;
    
    event MilestoneCompleted(address indexed beneficiary, uint256 milestoneIndex);
    event TokensReleased(address indexed beneficiary, uint256 amount);
    
    constructor(IERC20 _token) {
        token = _token;
    }
    
    function createVestingSchedule(
        address beneficiary,
        uint256[] memory milestoneAmounts
    ) external onlyOwner {
        require(vestingSchedules[beneficiary].beneficiary == address(0), "Schedule exists");
        
        VestingSchedule storage schedule = vestingSchedules[beneficiary];
        schedule.beneficiary = beneficiary;
        
        uint256 totalAmount = 0;
        for (uint256 i = 0; i < milestoneAmounts.length; i++) {
            schedule.milestones.push(Milestone({
                amount: milestoneAmounts[i],
                completed: false,
                released: false
            }));
            totalAmount += milestoneAmounts[i];
        }
        
        schedule.totalAmount = totalAmount;
        token.transferFrom(msg.sender, address(this), totalAmount);
    }
    
    function completeMilestone(address beneficiary, uint256 milestoneIndex) external onlyOwner {
        VestingSchedule storage schedule = vestingSchedules[beneficiary];
        require(schedule.beneficiary != address(0), "No vesting schedule");
        require(milestoneIndex < schedule.milestones.length, "Invalid milestone");
        require(!schedule.milestones[milestoneIndex].completed, "Already completed");
        
        schedule.milestones[milestoneIndex].completed = true;
        emit MilestoneCompleted(beneficiary, milestoneIndex);
    }
    
    function releaseMilestone(uint256 milestoneIndex) external {
        VestingSchedule storage schedule = vestingSchedules[msg.sender];
        require(schedule.beneficiary == msg.sender, "Not beneficiary");
        require(milestoneIndex < schedule.milestones.length, "Invalid milestone");
        
        Milestone storage milestone = schedule.milestones[milestoneIndex];
        require(milestone.completed, "Milestone not completed");
        require(!milestone.released, "Already released");
        
        milestone.released = true;
        schedule.releasedAmount += milestone.amount;
        
        token.transfer(msg.sender, milestone.amount);
        emit TokensReleased(msg.sender, milestone.amount);
    }
}

Financial Best Practices

  1. Payment Splitting: Use PaymentSplitter for automatic revenue distribution
  2. Vesting Security: Implement proper access controls for vesting schedule modifications
  3. Reentrancy Protection: Use ReentrancyGuard for contracts handling payments
  4. Token Support: Design contracts to support both Ether and ERC20 tokens
  5. Release Mechanisms: Implement both automatic and manual release options
  6. Audit Considerations: Financial contracts require thorough security audits

Integration Patterns

With Governance

// Combine vesting with governance tokens
contract GovernanceVesting is VestingWallet {
    constructor(
        address beneficiary,
        uint64 startTimestamp,
        uint64 durationSeconds
    ) VestingWallet(beneficiary, startTimestamp, durationSeconds) {}
    
    // Override to delegate voting power while tokens are vesting
    function _afterTokenTransfer(address token, uint256 amount) internal {
        // Delegate voting power to beneficiary even while tokens are locked
        IVotes(token).delegate(beneficiary());
    }
}

With Access Control

// Multi-signature controlled payment splitter
contract MultiSigPaymentSplitter is PaymentSplitter, AccessControl {
    bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
    
    modifier onlyAdmin() {
        require(hasRole(ADMIN_ROLE, msg.sender), "Not admin");
        _;
    }
    
    function emergencyWithdraw(address token, uint256 amount) external onlyAdmin {
        // Emergency withdrawal mechanism
    }
}

Error Handling

Financial contracts may revert with various errors:

  • PaymentSplitter: PaymentSplitter__InvalidShares(), PaymentSplitter__NoPayeesProvided()
  • VestingWallet: No specific custom errors, uses standard transfer/balance checks
  • General: Standard ERC20 transfer errors, access control violations, reentrancy protection

Install with Tessl CLI

npx tessl i tessl/npm-openzeppelin-solidity

docs

access-control.md

crosschain.md

finance.md

governance.md

index.md

metatx.md

proxy.md

security.md

tokens.md

utilities.md

tile.json