This guide explains how reward tokens are issued and how tokens are spent across the PERS platform. It complements the Terminology Dictionary with the economic model that governs every token movement.
Tokenomics in PERS answers two questions for every transaction:
- Issuance — when a user earns a reward, who creates or provides the token, and at whose cost?
- Spend — when a user spends a token, is it transferred to a business or destroyed?
| Type | Standard | Typical use | Supply model |
|---|---|---|---|
| Credits / Points | ERC20 | Everyday earning & spending balance | Fungible, divisible |
| Rewards | ERC1155 | Real-world experiences, products, multi-copy rewards sharing one design | Semi-fungible |
| Status Tokens | ERC721 | Unique, personalised vouchers, tickets, stamps & memberships — each carries its own metadata (recipient, seat, artwork, expiry) | Non-fungible, 1-of-1 |
Names match the Terminology Dictionary. "Ticket", "stamp", and "voucher" below are use cases of these types, not separate token types. Because Status Tokens (ERC721) are unique, each one can hold individual metadata — which is what makes them ideal for personalised, single-use vouchers and event tickets.
Two flags on the issuing Business drive the economics:
canMintToken— whether the platform mints new ERC20 supply on the business's behalf (delegated), or the business funds rewards from its own balance.canChargeToken— whether the business is allowed to pull (charge) tokens from a user at the point of sale.
Whenever a user earns a reward (via a campaign claim or a redemption reward), the platform records how it was economically issued on the transaction as issuanceMode. This value is resolved automatically at claim/redeem time — integrators never set it directly.
| Token | Condition | issuanceMode | On-chain executor | Web3 type |
|---|---|---|---|---|
| ERC20 | Business canMintToken = true | TENANT_DELEGATED_MINT | Tenant master wallet | MINT |
| ERC20 | Business canMintToken = false | BUSINESS_BALANCE_TRANSFER | Business custodial wallet | TRANSFER |
| NFT (721/1155) | metadata.ownerBusinessId matches the engaging business | BUSINESS_OWNED_MINT | Business custodial wallet | MINT |
| NFT (721/1155) | No business-ownership match | TENANT_DELEGATED_MINT | Tenant master wallet | MINT |
| Any | Non-reward transaction (plain transfer/burn) | null | — | — |
TENANT_DELEGATED_MINT (default)
- The tenant (platform) master wallet mints new supply. The platform absorbs the gas/minting cost.
- Applies to all NFTs without a business owner, and to ERC20 tokens where the business is allowed delegated minting.
BUSINESS_OWNED_MINT
- The business owns the NFT template (
TokenMetadata.ownerBusinessId). Its custodial smart wallet is the on-chainmsg.senderfor the mint. MINTER_ROLEis granted to the business wallet eagerly, at the moment metadata ownership is assigned (see §4), so minting works instantly at claim time.
BUSINESS_BALANCE_TRANSFER
- No new supply is created. The business transfers ERC20 tokens from its own on-chain balance to the user.
- The cap is the business wallet's balance; a balance pre-check is enforced before submission.
- Used when the business is not permitted delegated minting (
canMintToken = false).
Wallet routing: For
BUSINESS_OWNED_MINTandBUSINESS_BALANCE_TRANSFER, the business custodial smart wallet signs via the EIP-712 meta-transaction path. Non-custodial business wallets fall back to the tenant master wallet (EIP-1559) for backward compatibility.
Spending semantics depend on the token type and who is spending.
When a business pulls tokens from a user (sender = user, recipient = business, requires canChargeToken = true), the caller decides what happens to the token via web3TransactionType. If it isn't set explicitly, the platform infers it from whether a recipient is present:
recipientpresent → TRANSFER (to the business wallet)recipientabsent → BURN (destroyed on-chain)
| Token type | Recommended behavior | When to use |
|---|---|---|
| ERC20 (credit/points) | TRANSFER to business wallet | Always — points are fungible value the business reuses |
| ERC721 / ERC1155 (ticket/stamp — consumed) | BURN — omit recipient | User presents a single-use ticket, stamp, or voucher; it is destroyed on-chain |
| ERC721 / ERC1155 (NFT — custody transfer) | TRANSFER to business wallet | Business takes custody (trade, deposit, secondary market) |
Both paths are valid and fully supported. The canChargeToken guard is the only authorization check — what happens to the token on-chain is your design decision.
When a user redeems (pays credit tokens for a reward), the spend flows USER → BUSINESS:
- Recipient priority:
redemption.ownerBusinessId→dto.businessId→ tenant fallback. - Business-owned redemptions always carry
ownerBusinessId, so the spend always reaches the owning business. - The reward the user receives in return is issued using the
TokenIssuanceModematrix above.
For BUSINESS_OWNED_MINT to work, the business's custodial smart wallet must hold MINTER_ROLE on the NFT contract.
- The role is granted automatically and eagerly when an NFT
TokenMetadata.ownerBusinessIdis set (on create) or changed (on update). - The grant runs on both testnet and mainnet contracts if both are configured.
- It is retried up to 3 times with exponential backoff (1s / 2s / 4s). A persistent failure surfaces to the API caller so the operation can be retried, rather than silently leaving the business unable to mint.
- The grant is idempotent on-chain — re-granting an already-held role is a no-op.
Every transaction exposes the following tokenomics-relevant fields via TransactionDTO:
| Field | Meaning |
|---|---|
type | MINT | TRANSFER | BURN — the on-chain operation |
issuanceMode | TENANT_DELEGATED_MINT | BUSINESS_OWNED_MINT | BUSINESS_BALANCE_TRANSFER | null |
engagedBusinessId | The business economically attributed to the transaction (for analytics) |
triggerProcessType | e.g. CAMPAIGN_USER_CLAIM, REDEMPTION_SPEND, REDEMPTION_RECEIVE, SPEND, TRANSFER |
issuanceMode is intended for audit, analytics, and AML reporting — it records economic intent/attribution independent of the on-chain executor.
A. Café gives loyalty points (delegated ERC20 mint)
- Business
canMintToken = true, token is ERC20. - User claims →
issuanceMode = TENANT_DELEGATED_MINT,type = MINT, minted by tenant master wallet.
B. Brand funds cashback from its own treasury (ERC20 transfer)
- Business
canMintToken = false, token is ERC20. - User claims →
issuanceMode = BUSINESS_BALANCE_TRANSFER,type = TRANSFER, sent from the business wallet balance.
C. Museum issues its own NFT ticket (business-owned mint)
- NFT metadata
ownerBusinessId = museum. - User claims →
issuanceMode = BUSINESS_OWNED_MINT,type = MINT, minted by the museum's custodial wallet (MINTER_ROLE pre-granted).
D. User redeems points for a coffee
- User pays ERC20 credits →
TRANSFERUSER → BUSINESS (REDEMPTION_SPEND). - User receives a reward token issued per the matrix (
REDEMPTION_RECEIVE).
E. User presents an NFT ticket at the gate (consumed)
- Business charges the NFT → caller passes
web3TransactionType: "BURN", norecipient→ ticket destroyed on-chain.
F. User deposits an NFT to a business (custody transfer)
- Business charges the NFT → caller passes
web3TransactionType: "TRANSFER",recipient = business→ NFT moves to the business wallet.
All of this is resolved automatically by the platform — integrators create campaigns, redemptions, and transactions using the standard endpoints, and PERS records the correct issuance mode and spend semantics.