For the complete documentation index, see llms.txt. This page is also available as Markdown.

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

Actor
Role in this flow
What they hold

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:

  1. The caller holds AGENT_MINTER in the asset's domain. Otherwise the call reverts with AccessManagedUnauthorized.

  2. TREXRegistry.isVerified(to) returns true. The registry resolves to to 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. Otherwise UnverifiedIdentity.

  3. ModularCompliance.canTransfer(0x0, to, amount) returns true. Each RULE module answers the largest amount it allows for this recipient, and amount must not exceed the smallest answer. Otherwise ComplianceNotFollowed.

  4. 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 by amount. The increase is in the free balance.

  • Token.totalSupply() has increased by amount.

  • ModularCompliance.positionOf(identity) has increased by amount for the recipient's identity.

  • One Transfer event with from = 0x0 was emitted. An indexer treats it as issuance.

  • The pause state of the token is unchanged.

Failure modes

Symptom
Cause
Fix

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)

Repair the identity side: 05 for claims, 07 for the wallet, 04 for the trusted issuer

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: mint runs 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 holds OWNER or AGENT cannot mint unless AGENT_MINTER is granted to it. Keeping issuance apart from the other powers is what lets an Issuer delegate it alone.

Integration notes

  • Run isVerified(to) and canTransfer(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.

Last updated