BasketToken API
The complete external interface of the basket contract.
One BasketToken instance per basket. ERC-20 (OpenZeppelin v5 base, 18
decimals) plus the interface below. Solidity 0.8.24, no proxy, no
upgradeability, nonReentrant on state-changing entry points. Source is
verified on Blockscout for every deployment
(addresses).
Constructor
constructor(
string name_,
string symbol_,
address[] tokens_, // 2–20 constituents, no zero/duplicate, must be contracts
uint256[] unitsPerBasket_, // raw constituent wei per 1e18 basket wei, all > 0
uint16 mintFeeBps_, // ≤ MAX_FEE_BPS (50)
address feeRecipient_, // non-zero
address guardian_, // non-zero, immutable
uint256 maxSupplyCap_, // > 0, immutable ceiling
uint256 initialSupplyCap_ // 0 < x ≤ maxSupplyCap
)Each validation failure reverts with its own custom error (see Errors).
Core functions
mint
function mint(uint256 basketAmount, address to) external nonReentrantMints basketAmount (gross) by pulling
ceil(basketAmount × units[i] / 1e18) of every constituent via
safeTransferFrom; the caller must have approved each constituent. The
contract verifies actual balance deltas (delta ≥ required) to reject
non-standard transfer behavior. to receives basketAmount − fee; the fee
(basketAmount × mintFeeBps / 10_000) is minted to feeRecipient. Reverts
with MintingPaused, SupplyCapExceeded, or InsufficientDeposit(token).
Emits Minted.
redeem
function redeem(uint256 basketAmount, address to) external nonReentrantBurns basketAmount from the caller and transfers
floor(basketAmount × units[i] / 1e18) of each constituent to to. No
pause, cap or guardian power gates this function; the only possible
external failure is a constituent token itself reverting transfers. No fee.
Emits Redeemed.
Guardian functions
Callable only by the immutable guardian (reverts NotGuardian
otherwise). This is the complete privileged surface:
function setMintPaused(bool paused) external onlyGuardian // minting only
function setSupplyCap(uint256 newCap) external onlyGuardian // ≤ maxSupplyCap
function setFeeRecipient(address newRecipient) external onlyGuardianViews
function constituents() external view returns (address[] memory)
function units() external view returns (uint256[] memory) // raw wei per 1e18 basket weigetRequiredUnits
function getRequiredUnits(uint256 basketAmount)
external view returns (address[] memory tokens, uint256[] memory amounts)Exact deposit per constituent for a mint:
amounts[i] = ceil(basketAmount × units[i] / 1e18). It rounds up
(protocol-favoring). mint pulls exactly these amounts, so approvals set
from this view return to zero.
backingOf
function backingOf(uint256 basketAmount)
external view returns (address[] memory tokens, uint256[] memory amounts)The floor variant: what a redemption of basketAmount pays out.
isFullyBacked
function isFullyBacked() external view returns (bool)Checks balanceOf(vault) ≥ totalSupply × units[i] / 1e18 for every
constituent. Should always return true; it exists so anyone can monitor
the invariant live on Blockscout.
Public state
| Member | Type | Notes |
|---|---|---|
guardian | address immutable | The Safe; see Guardian |
mintFeeBps | uint16 immutable | Fixed at deploy, ≤ 50 |
maxSupplyCap | uint256 immutable | Ceiling the cap can never exceed |
feeRecipient | address | Guardian-settable |
supplyCap | uint256 | Guardian-settable, ≤ maxSupplyCap |
mintPaused | bool | Guardian-settable, never affects redeem |
MAX_FEE_BPS | uint256 constant | 50 |
MIN_CONSTITUENTS / MAX_CONSTITUENTS | uint256 constant | 2 / 20 |
Events
event Minted(address indexed sender, address indexed to, uint256 basketAmount, uint256 fee);
event Redeemed(address indexed sender, address indexed to, uint256 basketAmount);
event MintPausedSet(bool paused);
event SupplyCapSet(uint256 newCap);
event FeeRecipientSet(address indexed newRecipient);Minted/Redeemed are the events to index for TVL, volume and fee-revenue
analytics.
Errors
| Error | Thrown when |
|---|---|
LengthMismatch | tokens.length != units.length at construction |
InvalidConstituentCount | Fewer than 2 or more than 20 constituents |
DuplicateToken | Same constituent twice |
ZeroAddress | Zero token, fee recipient or guardian |
NotAContract(token) | Constituent address has no code |
ZeroUnits | A unit of 0 |
FeeTooHigh | mintFeeBps > 50 |
ZeroSupplyCap | Zero max or initial cap |
CapExceedsMax | Initial or new cap above maxSupplyCap |
ZeroAmount | Mint/redeem of 0 |
MintingPaused | Mint while paused |
SupplyCapExceeded | Mint above supplyCap |
InsufficientDeposit(token) | Balance delta below required (e.g. fee-on-transfer token) |
NotGuardian | Guardian function from another address |