Skip to main content

05 · Contract Interfaces and Events

The previous chapter described issuance, onboarding, minting, transfers, and burning. This chapter maps those processes to contract methods and serves as an implementation reference.

5.1 Find the correct contract first

An external application does not need to connect directly to the entire Suite. Start from the asset Token proxy address:

Recommended discovery order:

  1. Read the Token proxy address for the target environment from the public address table;
  2. Call Token.identityRegistry() for the asset registry;
  3. Call Token.compliance() for the asset compliance entry point;
  4. Query the wallet's ONCHAINID through IdFactory.getIdentity(wallet);
  5. Compare on-chain results with public configuration and never use an implementation address as the business entry point.

5.2 Permission reference

CallerCommon write methodsDescription
Any user or applicationAll view methodsRead-only calls send no transaction and consume no user gas
Token holdertransfer, approve, transferFromIdentity, pause, freeze, and compliance checks still apply
Investor identity-management walletdeployIdentityForWallet, addClaimCreate and maintain its own ONCHAINID
IR AgentregisterIdentity, updateIdentity, updateCountry, deleteIdentityManage investor registration for one asset
Token Agentmint, burn, pause, unpause, freeze, forced transfer, recoveryAsset-management operations
Contract OwnerChange registries, compliance modules, or governance configurationOutside ordinary application integration

Identify the current wallet role before showing operations. Hiding a button in the frontend does not replace on-chain permission control.

5.3 Token

Common read methods

MethodReturnPurpose
name() / symbol()stringDisplay asset name and symbol
decimals()uint8Amount conversion
totalSupply()uint256Current total supply
balanceOf(wallet)uint256Total wallet balance
identityRegistry()addressAsset eligibility registry
compliance()addressAsset compliance entry point
paused()boolWhether ordinary transfers are paused
isFrozen(wallet)boolWhether the wallet is fully frozen
getFrozenTokens(wallet)uint256Partially frozen amount

Calculate available balance as:

availableBalance = balanceOf(wallet) - getFrozenTokens(wallet)

Holder write methods

MethodPurposeMain prerequisites
transfer(to, amount)Direct transferToken active, wallet states allowed, recipient eligible, rules pass
approve(spender, amount)Set allowanceHolder signature
transferFrom(from, to, amount)Transfer using allowanceAllowance, balance, eligibility, and rules pass

Token Agent methods

MethodPurposeKey result
mint(to, amount)MintTransfer(0x0, to, amount)
burn(wallet, amount)BurnTransfer(wallet, 0x0, amount)
pause() / unpause()Pause or enable ordinary transfersPaused / Unpaused
setAddressFrozen(wallet, frozen)Fully freeze or unfreeze a walletAddressFrozen
freezePartialTokens(wallet, amount)Freeze part of a balanceTokensFrozen
unfreezePartialTokens(wallet, amount)Unfreeze part of a balanceTokensUnfrozen
forcedTransfer(from, to, amount)Administrative forced transferTransfer
recoveryAddress(lost, new, identity)Recover assets to a new walletRecoverySuccess and Transfer

Every amount is an integer in the smallest unit. Batch methods may hit the block gas limit, so split batches and track each transaction separately.

Token operations apply different checks:

OperationBlocked while pausedRecipient eligibilitycanTransferFrozen-balance handling
transfer / transferFromYesCheckedCheckedFrozen units cannot transfer
mintNoCheckedCheckedNot applicable
burnNoNot checkedNot checkedAgent may need to unfreeze
forcedTransferNoCheckedNot checkedAgent may need to unfreeze
recoveryAddressNoRecovery-flow rulesNot checkedFrozen state can migrate

Pause primarily restricts ordinary holder transfers. It does not stop Agent mint, burn, forced transfer, or wallet recovery.

5.4 IdentityRegistry

IdentityRegistry belongs to a specific asset. Obtain its address from the target Token's identityRegistry() before querying or registering an investor.

Common read methods

MethodMeaning
contains(wallet)Whether the wallet has a registration record
isVerified(wallet)Whether the wallet satisfies all current requirements
identity(wallet)Registered ONCHAINID address
investorCountry(wallet)Registered country or region code
topicsRegistry()Registry of required Claim Topics
issuersRegistry()Registry of recognized ClaimIssuers

contains == true only confirms a registration record and does not replace isVerified == true.

IR Agent methods

MethodPurposeEvent
registerIdentity(wallet, identity, country)First registrationIdentityRegistered
updateIdentity(wallet, identity)Replace associated identityIdentityUpdated
updateCountry(wallet, country)Update country or region codeCountryUpdated
deleteIdentity(wallet)Remove registration from the assetIdentityRemoved

One ONCHAINID can be referenced by multiple assets, but each asset uses its own IdentityRegistry and normally requires a separate registration.

5.5 ONCHAINID Gateway, IdFactory, and Identity

Create or query identity

ContractMethodCallerPurpose
ONCHAINID GatewaydeployIdentityForWallet(wallet)InvestorCreate deterministic ONCHAINID for wallet
IdFactorygetIdentity(wallet)Any applicationQuery associated identity

IdFactory emits Deployed and WalletLinked after creation. Query getIdentity(wallet) again and use the final on-chain mapping.

Claim methods

The investor calls on their own ONCHAINID:

addClaim(
uint256 topic,
uint256 scheme,
address issuer,
bytes signature,
bytes data,
string uri
) returns (bytes32 claimId)
MethodPurpose
getClaim(claimId)Read all fields of one Claim
getClaimIdsByTopic(topic)Query Claim IDs for a Topic
isClaimValid(identity, topic, signature, data)Validate ClaimIssuer signature

Key events:

  • ClaimAdded: first write;
  • ClaimChanged: update;
  • ClaimRemoved: removal.

ClaimSigner generates the off-chain signature. ClaimIssuer is the on-chain institution contract to which the signature belongs. They are distinct concepts.

5.6 ModularCompliance

canTransfer is defined on ModularCompliance, not Token:

canTransfer(
address from,
address to,
uint256 amount
) view returns (bool)
MethodPurpose
canTransfer(from, to, amount)Precheck whether current rules allow transfer
getModules()List bound rule modules
getTokenBound()Get bound Token
isModuleBound(module)Check whether a module is bound

canTransfer == true means rules pass at query time. Pause, freeze, balance, eligibility, or rules may change before the transaction, so the receipt remains authoritative.

transferred, created, and destroyed are called by the bound Token to notify compliance modules of state changes. Ordinary applications do not call them directly.

5.7 Event indexing

Index at least:

EventSourcePurpose
TransferTokenMint, burn, transfer
ApprovalTokenAllowance changes
Paused / UnpausedTokenTransfer-status changes
AddressFrozenTokenFull wallet freeze
TokensFrozen / TokensUnfrozenTokenPartial freeze changes
RecoverySuccessTokenWallet recovery
IdentityRegistered / IdentityRemovedIdentityRegistryRegistration changes
IdentityUpdated / CountryUpdatedIdentityRegistryIdentity or country changes
ClaimAdded / ClaimChanged / ClaimRemovedONCHAINIDEligibility-evidence changes
ModuleAdded / ModuleRemovedModularComplianceRule-module changes

Events reveal changes but are not current state by themselves. After a reorganization, backfill, or long offline period, re-read contract state for reconciliation.

5.8 Minimum ABI

Start with this minimum set when a full ABI is unnecessary:

const tokenAbi = [
'function name() view returns (string)',
'function symbol() view returns (string)',
'function decimals() view returns (uint8)',
'function totalSupply() view returns (uint256)',
'function balanceOf(address) view returns (uint256)',
'function identityRegistry() view returns (address)',
'function compliance() view returns (address)',
'function paused() view returns (bool)',
'function isFrozen(address) view returns (bool)',
'function getFrozenTokens(address) view returns (uint256)',
'function transfer(address,uint256) returns (bool)',
'event Transfer(address indexed from,address indexed to,uint256 value)',
];

const identityRegistryAbi = [
'function contains(address) view returns (bool)',
'function isVerified(address) view returns (bool)',
'function identity(address) view returns (address)',
'function investorCountry(address) view returns (uint16)',
'event IdentityRegistered(address indexed investorAddress,address indexed identity)',
];

const complianceAbi = [
'function canTransfer(address,address,uint256) view returns (bool)',
'function getModules() view returns (address[])',
];

Use the official ABI matching the deployed version for real write operations. Do not infer argument order from method names.

Before an investor transfer:

Validate Chain ID
→ Read Token.decimals
→ Read paused / isFrozen / getFrozenTokens
→ Obtain the correct registry through Token.identityRegistry
→ Query recipient contains / isVerified
→ Obtain compliance through Token.compliance
→ Call canTransfer
→ estimateGas or run a static simulation
→ Send transaction and wait for receipt
→ Update the application from events and latest state

The next chapter connects these interfaces to public environments and provides read, event-query, and acceptance steps:

Continue: 06 · Environment Configuration and Acceptance