Constructor
initialSupply increases total supply during construction. If the
initial holder refuses the credit, the mint goes to ReceivePolicy quarantine.
Application-created tokens use the owner-managed MetalToken extension deployed
by MIP20Factory. Verify deployment and recipient credit separately.
Metadata and state getters
authorizationState == true means the nonce was consumed by a transfer or
cancellation. The transfer may have credited quarantine, so inspect the matching
events to determine the payment outcome.
Storage layout
Protocol gas accounting reads MIP20 balances at storage slot 0. Applications read balances, allowances, holds and authorizations through the public getters above. Verify MIP20 status through the configured factory’sisMIP20.
Transfers and allowances
transferFrom does not reduce an allowance equal to type(uint256).max. Finite
allowance consumption does not emit an additional Approval event. Read
allowance to establish the remaining permission.
A transfer rejects zero sender or recipient addresses and direct transfers to
ReceivePolicy. An accepted credit emits Transfer(from, to, amount). A refused
credit either reverts or emits a transfer to the ReceivePolicy escrow, according
to the refusal rules. Match the destination in
the emitted event to determine where the funds went.
Approve a spender
An allowance lets a specified account or contract spend up to a chosen amount of a holder’s tokens. The holder grants that permission by callingapprove on the
token contract. The approved spender can then call transferFrom to send tokens
or holdFrom to set them aside for a payment.
Use the public client from Connect to Metal
and a wallet client named wallet, configured with the holder’s signing account
as described in
Configure the test signer.
Both clients must use the same verified network. Install the public packages:
token address using
Read metadata. For a new
allowance, validate and convert the spending limit to the positive base-unit
amount using
Convert a payment amount.
Obtain spenderAddress from the account configuration or contract deployment
record. For testing, use a spender you control. Read its current allowance:
approvalAmount to the validated amount to grant permission, or 0n to
revoke it. Grant the amount required for the operation. A new approval replaces
the remaining allowance, including a nonzero allowance. If your application
requires revocation before replacement, submit and verify approve(spender, 0)
first.
Estimate each approval using
Estimate and record fees, with holder
as the sender, approve as the function, and [spender, approvalAmount] as its
arguments. Omit fee fields during estimation. Check that the signing account’s
balance in the gas token covers the maximum gas cost. The following call pays
gas in testnet *USDZ. Use the gasLimit and maxFeePerGas from that exact
call’s estimate:
approval.txHash and approval.raw before calling
await approval.send(). Require a successful receipt,
block finality, and an Approval event from the token
with the expected holder, spender, and amount. Read allowance(holder, spender)
again using the read above. Verify zero after a reset or revocation before
proceeding.
Process allowance changes sequentially. A spender can act until revocation
executes and may consume a new allowance immediately, so investigate an
unexpected lower value against its spending activity. If submission or receipt
polling fails, recover the saved hash before preparing another transaction. A
reverted approval leaves the allowance unchanged but can still incur a fee.
With a controlled test spender, verify that a grant sets the expected allowance,
a second nonzero approval replaces it, and revocation restores zero.
Holds
A MIP20 hold moves an owner’s funds out ofbalanceOf into escrow at the token
contract. The funds remain the owner’s, tracked by heldTotalOf(owner), while
only the holding spender can capture or release them.
Placement emits
Transfer(owner, token, amount) followed by HoldPlaced.
Release emits Transfer(token, owner, amount) followed by HoldReleased. These
transfers bypass receive policy screening because they preserve ownership.
Capture first emits the release events, then screens the owner-to-recipient
credit. A refusal always quarantines, even when the recipient chose REVERT mode.
The hold decreases as the funds enter quarantine. Retain the quarantine receipt
and recover through its recovery authority. A different execution failure, such
as an insufficient hold, reverts all changes. See
Payment from reserved funds.
ERC-3009 authorizations
The bytes-signature forms are:transferWithAuthorization permits any submitter. receiveWithAuthorization
requires the submitter to be to. Cancellation can be relayed by anyone holding
the valid cancellation signature. All three also have an overload replacing
bytes signature with uint8 v, bytes32 r, bytes32 s for an ECDSA signature.
For Unix-second validity bounds, an authorization is usable only while
validAfter < block.timestamp < validBefore. Use ordinary timestamps for the
payment procedures. The inherited interface also supports block-number bounds
when both bounds set bit 47, which must not be mixed into timestamp inputs.
The bytes form accepts the owner’s own ECDSA signature first. If that fails and
the owner has code, it checks ERC-1271. The (v,r,s) form checks ECDSA only. A
delegated EOA’s own key remains valid regardless of its delegate’s rules.
Mandate payments must use the account’s pull operation to apply its
accounting.
Successful authorization emits AuthorizationUsed(authorizer, nonce) from the
token. Cancellation emits AuthorizationCanceled(authorizer, nonce) instead. A
revert rolls back nonce consumption. Quarantine succeeds and consumes the nonce,
so retrying with a fresh authorization would create another payment.
Use Signing and Typed Data for the exact
domain and type strings. Keep application correlation and authenticated evidence
in your payment records or pass them to the send policy contract that checks them.
Receive policy and quarantine
The receive policy contract,ReceivePolicy, is at
0x0000000000000000000000000000000000000f4d. It screens
sender blocklists and allowlists, accepted tokens, minimum and maximum amounts,
and per-sender totals and arrival counts. Maximum amounts and per-sender limits
are not on Metal Testnet yet. The screened sender is the balance
owner, even when a spender or relayer submits. An
unconfigured account accepts all credits. A self-transfer is accepted.
A refused ordinary transfer reverts by default with CreditRefused(reason). A
recipient can select QUARANTINE mode instead. Refused mints, hold captures and
payouts from a token’s own balance always quarantine. The token emits
Transfer(originator, ReceivePolicy, amount), and ReceivePolicy emits
Quarantined(receiptId, receiver, originator, receipt, reason).
Store the full quarantine receipt, its ID, token, amount and transaction hash.
The recovery authority recorded in the receipt can submit
claim(receipt, destination). Verify Claimed and the exact token transfer
before recording recipient payment. The receipt retains its original recovery
authority after policy changes. The
Receive Policy reference defines enum values,
configuration, receipt fields, authority selection and claim screening.
Use a send policy contract to enforce outgoing budgets. To forward funds, call
a routing contract. See
Build a restricted account and
Route incoming payments.
Factory-deployed MetalToken
MIP20Factory.deployMIP20 deploys a MetalToken and adds its address to the
MIP20 allowlist. The caller becomes its owner.
The testnet default
*USDZ gas token is a fixed-supply base MIP20. It does not
expose these ownership, mint or burn functions.
The factory also supports yield-share tokens through
deployYieldMIP20(bytes creationCode, string calldata name_, string calldata symbol_, uint8 decimals_, uint256 initialShares, address initialHolder, uint256 ratePerSecond).
Obtain the exact approved creation artifact and yield-product configuration from
the issuer. Its rebasing view is a separate contract and is not a gas token.