08. Mint a position to an investor
Minting creates new units of the asset and credits them to an investor. It is how primary issuance lands on the register: once the Issuer has settled a subscription under its own process, an agent mints the corresponding position. The token applies the same eligibility gate and the same compliance gate as for a transfer, with the difference that there is no sender to check.
At a glance
Triggered by
Token Agent
Actors
Issuer, Token Agent, Investor, Platform
Contracts
Token, TREXRegistry, ModularCompliance, compliance modules, the ONCHAINID of the Investor and its Claim Issuers (read only, during verification)
Preconditions
The token suite is deployed and commissioned (see 01 and 25); the caller holds AGENT_MINTER in the asset's domain; the Investor's identity holds a valid claim from a trusted Claim Issuer for every required topic (see 05); the receiving address is the Investor's ONCHAINID, a wallet linked to it (see 07), or an address registered to it for the asset (see 06); the subscription is settled under the Issuer's process
Result
The balance of the receiving address, the position of the Investor's identity and the total supply have each increased by amount
Actors and roles
Issuer
Decides that the position is due: the subscription is accepted and paid. Does not transact in this flow.
The roles it delegated to its agents
Token Agent
Submits the mint.
AGENT_MINTER on the Token
Investor
Receives the position. Does not transact.
An ONCHAINID with valid claims. Optionally a linked wallet
Platform
Runs the pre-checks and submits with the Token Agent's key.
The Token Agent's signing key, or a key authorized for it
Sequence
Step by step
Step 1. Confirm the position can be created
Who: Platform, on behalf of the Token Agent
Call: Off-chain, using read calls
Before the call: The subscription is accepted and settled under the Issuer's process. The Platform knows the receiving address to and the number of units amount, in the token's smallest unit.
What happens: The Platform reads TREXRegistry.isVerified(to) and ModularCompliance.canTransfer(0x0, to, amount). Both must return true. If isVerified is false, the Investor is not eligible and the mint would revert: the fix is on the identity side, not on the token. If canTransfer is false, a rule refuses this amount for this investor at this moment, for example a cap on what one identity may own.
What changes: Nothing on-chain.
Step 2. Mint
Who: Token Agent (AGENT_MINTER)
Call: Token.mint(to, amount)
Before the call: Step 1 passed.
What the contract checks:
The caller holds
AGENT_MINTERin the asset's domain. Otherwise the call reverts withAccessManagedUnauthorized.TREXRegistry.isVerified(to)returns true. The registry resolvestoto an identity: a linked wallet resolves to the identity it is linked to, and an ONCHAINID resolves to itself. It refuses a wallet whose link was revoked. It then requires, for each topic required of that identity's type, a valid claim from a Claim Issuer trusted for that topic. OtherwiseUnverifiedIdentity.ModularCompliance.canTransfer(0x0, to, amount)returns true. EachRULEmodule answers the largest amount it allows for this recipient, andamountmust not exceed the smallest answer. OtherwiseComplianceNotFollowed.The amount is not zero. The compliance does not record a movement of zero. Otherwise
ZeroValue.
The pause is not checked: a mint is allowed while the token is paused. A freeze on the receiving address is not checked either.
What changes: The token credits amount to the free balance of to, increases totalSupply by amount, and emits Transfer(0x0, to, amount). It then calls ModularCompliance.created(to, amount). The compliance adds amount to the position of the recipient's identity and calls afterTransfer on every TRACKER module.
State after the flow
Token.balanceOf(to)has increased byamount. The increase is in the free balance.Token.totalSupply()has increased byamount.ModularCompliance.positionOf(identity)has increased byamountfor the recipient's identity.One
Transferevent withfrom = 0x0was emitted. An indexer treats it as issuance.The pause state of the token is unchanged.
Failure modes
Reverts with AccessManagedUnauthorized
The calling key does not hold AGENT_MINTER in the asset's domain (step 2, check 1)
Grant the role: 25. Holding AGENT is not enough
Reverts with UnverifiedIdentity
to resolves to no identity, its link was revoked, the identity lacks a valid claim for a required topic, or the claim's issuer is not trusted for that topic (step 2, check 2)
Reverts with ComplianceNotFollowed
A rule refuses this amount for this recipient, counting everything its identity already owns and what open validations promise to it (step 2, check 3)
Reduce amount, or have the Compliance Owner change the rule: 11
Reverts with ZeroValue
The amount is zero (step 2, check 4)
Mint a positive amount. In a batch, remove the entries of zero
The mint succeeds while the token is paused
Mints are allowed during a pause
Intended. The initial distribution can precede the opening of the asset
The mint succeeds on a frozen address
A freeze blocks transfers to and from an address, not mints
Intended. Unfreeze, or mint to another address of the investor
The transaction succeeds and the Platform shows no balance
The indexer ignores Transfer events whose from is the zero address
Index mints as Transfer from the zero address
Common misunderstandings
Wrong: "Minting is an issuer action, so it bypasses compliance."
Right:
mintruns the same eligibility gate and the same compliance gate as a transfer. It skips only the sender-side checks, because there is no sender. If an investor could not receive a transfer, it cannot receive a mint.
Wrong: "The wallet is whitelisted, so we can mint to it."
Right: A link or a registration says whose the address is. Eligibility is evaluated on the identity's claims: one valid claim from a trusted Claim Issuer for every required topic. A linked wallet whose identity has no claims is not verified.
Wrong: "Tokens are minted to a wallet."
Right: The recipient is any address of the investor: its ONCHAINID itself, which is a smart account, a wallet linked to it, or an address the Issuer's agent registered to it. All are checked the same way.
Wrong: "Mint is a function of the owner key."
Right: Mint is gated by
AGENT_MINTER, one of seven separate agent roles. No other role includes it: an account that holdsOWNERorAGENTcannot mint unlessAGENT_MINTERis granted to it. Keeping issuance apart from the other powers is what lets an Issuer delegate it alone.
Integration notes
Run
isVerified(to)andcanTransfer(0x0, to, amount)as reads before submitting. They are the checks the contract runs.For an issuance to many investors, use
Token.batchMint(toList, amounts). It is gated by the same role. The batch reverts as a whole if one entry fails, so run the reads on every entry first.Index
Transfer(0x0, to, amount)as issuance. There is no separate mint event.Store the link between the subscription record and the transaction hash. The on-chain position is the register. The subscription record is the audit trail of why it was created.
A rule that caps an identity counts its whole position, on every address and every chain. Read
ModularCompliance.positionOf(identity)to anticipate it.If the Token Agent operates through an ONCHAINID, the mint can be a sponsored user operation. The role is held by the identity that makes the call, not by the key that instructed it.
This guide mints to an address on the reference chain. A mint directly to an investor's wallet on a satellite, with no action from the investor, is part of the design.
[[TBC: the function for a mint to a wallet on a satellite]]See Movement types.
Related flows
05 Issue a claim to an investor : the eligibility precondition
07 Link a wallet to an ONCHAINID : when the investor receives in a wallet
09 Burn a position : the inverse operation
10 Transfer between investors : the same gates, with a sender
11 Add a compliance rule : where a refusal by the compliance comes from
16 Pause and unpause a token : opening the asset after the first mints
Last updated