Skip to content
LogoLogo

Standard library

The standard library ships inside the compiler. Import a module with a path that starts with std/:

import "std/interfaces/erc20";
import "std/evm";
import "std/token/erc20";
import "std/access/ownable";
 
contract Token is {
  use ERC20 exposing {
    view function balanceOf(account : address) : uint;
    function transfer(to : address; value : uint) : bool
      raises ERC20InvalidSender | ERC20InvalidReceiver | ERC20InsufficientBalance;
  }
 
  use Ownable;
 
  hook ERC20.beforeTransfer is skip;
  hook ERC20.afterTransfer is skip;
 
  storage is record {
    minted : uint;
  }
 
  deploy()
    raises OwnableInvalidOwner
  is {
    ERC20.init("Token", "TKN");
    Ownable.init(Evm.sender());
  }
 
  external function mint(const to : address; const value : uint)
    raises OwnableUnauthorizedAccount | ERC20InvalidReceiver | ERC20InsufficientBalance
  is {
    Ownable.checkOwner();
    ERC20.mint(to, value);
    storage.minted := storage.minted + value;
  }
}

Every module follows the ordinary rules for modules:

  • Modules declare the standard interfaces; contracts choose what to expose. A module's externals carry the standard's ABI names, and no internal function shares one. A view that only reads storage has no internal twin: a contract reads the storage, ERC20.erc20.balances[account]. balanceOf is an external of IERC20, which a token exposes with use ERC20 exposing IERC20, and announces through ERC-165 with supports IERC20. A contract that needs different behavior writes its own entrypoint instead, and exposes the rest with the interfaces the module's file declares for its parts, such as IERC20Views, IOwnable, and IPausable; only whole standards are in std/interfaces. lithe stubs file.lithe ERC20 prints a module's whole block.
  • Each module owns its storage, at an ERC-7201 root named lithe.std.<module>: lithe.std.erc20, lithe.std.ownable, and so on. Contracts don't redeclare a module's schema, and two modules never collide.
  • Extensions add behavior, and never change a base module silently. A pause, a cap, or a fee is written in the contract's own transfer, around the module's pieces.
  • Errors and events match OpenZeppelin's, or Solady's where OpenZeppelin has no such module, so wallets, explorers, and indexers decode them as they already do.

Modules

PathModules
std/access/ownable.litheOwnableAccess control
std/access/ownable_two_step.litheOwnable2Step
std/access/ownable_roles.litheOwnableRoles
std/access/access_control.litheAccessControl
std/security/pausable.lithePausable
std/token/erc20.litheERC20Tokens
std/token/erc20_burnable.litheERC20Burnable
std/token/erc20_permit.litheERC20Permit
std/token/erc20_capped.litheERC20Capped
std/token/erc20_votes.litheERC20Votes
std/token/erc20_flash_mint.litheERC20FlashMint
std/token/safe_erc20.litheSafeERC20
std/token/erc4626.litheERC4626
std/token/erc721.litheERC721
std/token/erc721_enumerable.litheERC721Enumerable
std/token/erc721_uri_storage.litheERC721URIStorage
std/token/erc2981.litheERC2981
std/token/erc1155.litheERC1155
std/token/erc1155_supply.litheERC1155Supply
std/token/erc6909.litheERC6909
std/token/erc6909_token_supply.litheERC6909TokenSupply
std/token/erc6909_metadata.litheERC6909Metadata
std/token/erc6909_content_uri.litheERC6909ContentURI
std/collections/set.litheSet(T)Collections
std/collections/set_enumerable.litheSetEnumerable(T)
std/collections/map_enumerable.litheMapEnumerable(K, V)
std/collections/bitmap.litheBitmap
std/collections/queue.litheQueue(T)
std/math/math.litheMathMath
std/math/safe_cast.litheSafeCast
std/math/fixed_point.litheFixedPoint
std/crypto/ecdsa.litheECDSACryptography
std/crypto/eip712.litheEIP712
std/crypto/merkle.litheMerkle
std/crypto/webauthn.litheWebAuthn
std/crypto/signature_checker.litheSignatureChecker
std/utils/create.litheCreateUtilities
std/utils/create2.litheCreate2
std/utils/clones.litheClones
std/utils/sstore2.litheSSTORE2
std/utils/create3.litheCreate3
std/utils/nonce.litheNonces
std/utils/checkpoint.litheCheckpoints
std/utils/block_hash.litheBlockHash
std/utils/strings.litheStrings
std/utils/date_time.litheDateTime
std/accounts/erc7821.litheERC7821
std/interfaces/*.litheIERC165, IERC20, IERC20Metadata, IERC20Permit, IERC721, IERC721Metadata, IERC721Enumerable, IERC721Receiver, IERC1155, IERC1155MetadataURI, IERC1155Receiver, IERC6909, IERC6909Metadata, IERC6909ContentURI, IERC6909TokenSupply, IERC1271, IERC2981, IERC3156FlashLender, IERC3156FlashBorrower, IERC4626, IERC5267, IERC6372, IVotes, IERC7821Interfaces

What isn't here

  • A reentrancy guard. nonreentrant is a language feature, and deferred calls already run after a transition's own effects.
  • Byte and arithmetic primitives. Bytes.concat, Bytes.word, String.lower, String.escapeJson, Base64.encode, Uint.mulDiv, and the rest are built-ins, because the compiler does them better than a library could. Anything that builds a string a byte at a time is one: a library can only join slices.
  • Applications, such as AMMs, lending markets, or auctions. They are too opinionated for a foundation.