Lithe targets the same EVM and the same ABI as Solidity, and its storage layout is slot-for-slot compatible. Most concepts carry over, with different syntax and a few deliberate omissions.
The same contract, twice
contract Vault { error Insufficient(uint256 available, uint256 required); event Withdrawn(address indexed who, uint256 amount); mapping(address => uint256) public deposits; function deposit() external payable { deposits[msg.sender] += msg.value; } function withdraw(uint256 amount) external { uint256 have = deposits[msg.sender]; if (have < amount) revert Insufficient(have, amount); deposits[msg.sender] = have - amount; emit Withdrawn(msg.sender, amount); (bool ok, ) = msg.sender.call{value: amount}(""); require(ok); }}
import "std/evm";error Insufficient of record { available : wei; required : wei;}event Withdrawn of record { indexed who : address; amount : wei;}storage is record { deposits : map(address, wei);}external view function deposits(const who : address) : wei is storage.deposits[who]external payable function deposit() is { storage.deposits[Evm.sender()] := storage.deposits[Evm.sender()] + Evm.value();}external function withdraw(const amount : wei) raises Insufficientis { const have : wei := storage.deposits[Evm.sender()]; assert(have >= amount, Insufficient { available = have; required = amount; }); storage.deposits[Evm.sender()] := have - amount; emit Withdrawn { who = Evm.sender(); amount = amount; }; defer Evm.transfer(Evm.sender(), amount);}
Both contracts have the same ABI, the same error selector, the same event topic, and the same storage slots.
Evm.code(a), defer create(Clones.code(impl)), defer create(SSTORE2.code(data)). See Utilities
token.call(...) accepting empty return data
a result of option(T): None when nothing comes back. See Optional results
to.call{value: v}(data)
defer Evm.call(to, data) with value = v then (result : bytes) { ... }. See Raw calls
to.staticcall(data)
query Evm.staticCall(to, data)
to.delegatecall(data)
defer Evm.delegateCall(to, data). For a contract's own upgrades, use upgradeable
Inheritance and libraries
Solidity
Lithe
contract A is B, C
contract A is { use B; use C; ... }
inherited public functions
a module's externals, exposed with use B exposing I, { ... }
ERC165 and supportsInterface overrides
supports I, J;, and the compiler writes supportsInterface. See ERC-165
an abstract _canSetX() hook the contract overrides
a module guard, which each contract that exposes the guarded externals writes. See Guards
a virtual function the contract overrides, such as _beforeTokenTransfer, _update, or tokenURI
a module hook, which the module's code calls and each contract that reaches it implements, or skips with is skip. Only the contract implements hooks, so it composes extensions in the hook's body. See Hooks, and the standard library's tokens' beforeTransfer and afterTransfer, where an _update override's code before and after super._update goes: Transfer hooks