Compliance entity reference - rules, claims, and access control
Compliance entities enforce transfer restrictions through a modular architecture that validates every token transaction against regulatory requirements. The system combines identity claims, trusted issuers, and configurable rule modules to gate DvP settlements, vault operations, and yield distributions, ensuring that only eligible investors participate in regulated asset workflows.
Why compliance comes first
Every token transfer in ATK must pass compliance validation before execution. This compliance-first architecture prevents regulatory violations at the protocol level rather than relying on off-chain controls. When an investor attempts to transfer tokens, the system validates their identity claims against required topics (KYC, AML, accredited investor status) and evaluates all active compliance modules (transfer limits, country restrictions, balance caps) before allowing the transaction.
This design ensures that DvP settlements only complete when both parties meet eligibility requirements, vault withdrawals respect holding period restrictions, and yield distributions flow only to verified participants. The compliance layer acts as a regulatory gate across the entire DALP lifecycle.
Schema location:
kit/subgraph/schema.graphql
Compliance architecture overview
The compliance system connects three core components: the Compliance contract manages required claim topics and active modules for each token, ComplianceModule contracts enforce specific transfer rules, and IdentityClaim entities prove investor eligibility through cryptographically signed attestations from trusted issuers.
Compliance
The Compliance contract serves as the central enforcement point for a token's regulatory rules. It maintains a list of required claim topics that investors must possess and coordinates all active compliance modules that validate transfer conditions.
Schema definition
type Compliance @entity {
id: Bytes! # Compliance contract address
token: Token! # Associated token
modules: [ComplianceModule!]! # Active modules
requiredClaimTopics: [BigInt!]! # Required claim topics
}Required claim topics
The requiredClaimTopics array specifies which identity claims an investor must
hold to trade the token. These numeric topic IDs correspond to specific
attestation types:
| Topic ID | Claim Type | Regulatory Purpose |
|---|---|---|
1 | KYC verification | Customer identification per AML regulations |
2 | AML screening | Anti-money laundering compliance check |
3 | Accredited investor | Qualification for restricted securities (US SEC Reg D) |
4 | Country of residence | Geographic restriction enforcement (OFAC, sanctions) |
5 | Investor type | Retail vs institutional classification for product suitability |
A corporate bond offering restricted to accredited US investors would require
topics [1, 2, 3, 4], while a retail fund available to EU residents might only
require [1, 2, 4].
DALP integration points
Compliance validation gates critical DALP operations:
- DvP settlement: Both buyer and seller must pass compliance checks before the atomic swap executes, preventing regulatory violations in on-chain delivery-versus-payment transactions
- Vault withdrawals: Compliance modules can enforce lock-up periods or vesting schedules by rejecting transfers during restricted timeframes
- Yield distributions: Only addresses with valid identity claims receive dividend payments, ensuring yield flows only to verified participants
Querying compliance configuration
query GetTokenCompliance($tokenAddress: Bytes!) {
token(id: $tokenAddress) {
compliance {
id
requiredClaimTopics
modules {
id
name
moduleType
}
}
}
}This query retrieves the complete compliance setup for a token, showing which identity claims investors need and which rule modules the token enforces. Use this during investor onboarding to display eligibility requirements.
Related contracts:
Compliance.sol
ComplianceModule
Compliance modules implement specific transfer restrictions as isolated smart contracts. Each module evaluates one aspect of regulatory compliance (daily transfer limits, geographic restrictions, maximum holdings) and returns approval or rejection for proposed transfers. The modular design lets token issuers compose custom regulatory frameworks by activating multiple modules simultaneously.
Schema definition
type ComplianceModule @entity {
id: Bytes! # Module contract address
name: String! # Module type name
moduleType: String! # Classification category
isGlobal: Boolean! # System-wide vs token-specific
tokens: [TokenComplianceModule!]! # Tokens using this module
}Module type catalog
The kit includes pre-built modules covering common regulatory requirements:
Transfer volume controls
TimeTransfersLimitsModule: Enforces daily and monthly transfer volume caps to detect suspicious trading patterns and prevent market manipulation. Used by funds to comply with redemption notice periods and by bonds to limit secondary market liquidity.MaxBalanceModule: Restricts the maximum token balance any single address can hold, preventing concentration risk and ensuring broad investor distribution for funds and collective investment schemes.
Geographic restrictions
CountryRestrictModule: Blocks transfers to or from specific countries based on identity claim country codes, enforcing OFAC sanctions lists and geographic distribution restrictions in private placements.
Ownership limitations
MaxOwnershipModule: Caps the percentage of total supply any investor can own, satisfying fund diversification requirements and preventing single-entity control in tokenized voting securities.SupplyLimitModule: Restricts total circulating supply to a maximum value, useful for capped offerings and ensuring on-chain supply matches off-chain asset backing.
Time-based restrictions
TransferCooldownModule: Enforces minimum wait periods between transfers from the same address, reducing wash trading and supporting holding period requirements in certain jurisdictions.LockupModule: Implements hard lock-up periods where tokens cannot be transferred until a specified timestamp, supporting founder vesting schedules and SAFE note conversion restrictions.
Global vs token-specific modules
Modules deployed with isGlobal: true can be reused across multiple tokens,
reducing deployment costs and standardizing compliance logic. A global
CountryRestrictModule instance might serve all tokens issued by a platform.
Token-specific modules (isGlobal: false) contain custom logic or parameters
unique to one security offering.
Real-world compliance scenarios
Scenario: Private equity fund with quarterly redemptions
Configure TimeTransfersLimitsModule with monthlyLimit set to 25% of investor
holdings and dailyLimit set to zero except during redemption windows. Pair
with TransferCooldownModule requiring 90 days between transfers to enforce the
fund's liquidity terms.
Scenario: US Regulation D corporate bonds
Activate modules: CountryRestrictModule (block non-US transfers),
MaxOwnershipModule (prevent >10% ownership), and LockupModule (12-month
holding period). Set requiredClaimTopics to [1, 2, 3] for KYC, AML, and
accredited investor verification.
Scenario: EU MiFID II compliant retail fund
Use MaxBalanceModule to limit individual holdings below the product's risk
concentration threshold. Configure CountryRestrictModule to allow only EU/EEA
country codes. Set requiredClaimTopics to [1, 2, 5] to verify KYC, AML, and
retail investor classification.
Module discovery query
query GetComplianceModules {
complianceModules(where: { isGlobal: true }) {
id
name
moduleType
tokens(where: { isActive: true }) {
token {
name
symbol
}
}
}
}This query lists all reusable global modules and which tokens currently employ them, helping platform administrators understand compliance rule deployment across the system.
Related contracts:
ComplianceModule.sol
TokenComplianceModule
The TokenComplianceModule junction entity connects tokens to compliance modules with module-specific configuration parameters. This many-to-many relationship allows tokens to activate multiple rule modules simultaneously, and modules to serve multiple tokens.
Schema definition
type TokenComplianceModule @entity {
id: Bytes! # Composite ID (token + module)
token: Token! # Token reference
module: ComplianceModule! # Module reference
params: Bytes! # ABI-encoded configuration parameters
addedAt: BigInt! # Activation timestamp
removedAt: BigInt # Deactivation timestamp (null if active)
isActive: Boolean! # Currently enforced
}Module configuration parameters
The params field contains ABI-encoded configuration data specific to each
module type. These parameters customize the module's behavior for the token.
TimeTransfersLimitsModule configuration
struct Params {
uint256 dailyLimit; // Maximum tokens transferable per 24h period per address
uint256 monthlyLimit; // Maximum tokens transferable per 30-day rolling window
}Example: A fund with $100k monthly redemptions and $1M equivalent token price
sets dailyLimit: 3333e18 (roughly $10k/day) and monthlyLimit: 100000e18.
CountryRestrictModule configuration
struct Params {
uint16[] allowedCountries; // ISO 3166-1 numeric country codes permitted
uint16[] blockedCountries; // ISO 3166-1 numeric country codes forbidden
}Example: A US-only offering sets allowedCountries: [840] (840 = United
States). An EU fund might set allowedCountries to all EU member states' codes
and leave blockedCountries empty.
MaxBalanceModule configuration
struct Params {
uint256 maxBalance; // Maximum token balance per address
}Example: A retail investment product with €50k maximum individual exposure and
€10 token price sets maxBalance: 5000e18.
Module lifecycle tracking
Token issuers activate modules via
addModule()
and deactivate via
removeModule().
The subgraph tracks both events:
addedAt: Timestamp when module activated, useful for audit trailsremovedAt: Timestamp when module deactivated,nullif currently activeisActive: Boolean derived fromremovedAt, enables efficient filtering in queries
This history preserves the complete compliance configuration timeline for regulatory reporting.
Querying active module configurations
query GetTokenModules($tokenAddress: Bytes!) {
tokenComplianceModules(
where: { token: $tokenAddress, isActive: true }
orderBy: addedAt
orderDirection: asc
) {
module {
name
moduleType
}
params
addedAt
}
}This query retrieves all currently enforced compliance rules for a token with their configuration parameters, ordered by activation date. Use this to display compliance requirements on token detail pages.
IdentityClaim
IdentityClaim entities represent ERC-735 claims attached to investor identity contracts. Each claim is a cryptographically signed attestation from a trusted issuer asserting specific facts about the investor (their KYC status, country of residence, accredited investor qualification).
Schema definition
type IdentityClaim @entity {
id: Bytes! # Claim ID (hash of identity + topic + issuer)
identity: Identity! # Identity owning this claim
topic: BigInt! # Claim topic (matches requiredClaimTopics)
scheme: BigInt! # Signature scheme (1=ECDSA, 2=RSA, 3=other)
issuer: Account! # Claim issuer account
signature: Bytes! # Cryptographic signature proving authenticity
data: Bytes! # Claim payload data
uri: String! # URI to external claim documentation
addedAt: BigInt! # Claim issuance timestamp
removedAt: BigInt # Claim revocation timestamp (null if valid)
isActive: Boolean! # Currently valid and unrevoked
}Claim topic standard
Claim topics align with the requiredClaimTopics on Compliance contracts,
creating a matching system between investor attestations and token requirements:
| Topic | Attestation | Verification Method | Typical Issuer |
|---|---|---|---|
1 | KYC verification | Government ID validation, address proof | Identity verification service (Onfido, Jumio) |
2 | AML screening | Sanctions list check, PEP screening, adverse media | Compliance service provider (ComplyAdvantage, Chainalysis) |
3 | Accredited investor | Income/net worth verification per SEC standards | Registered broker-dealer, CPA firm, law firm |
4 | Country of residence | Tax residency documentation | Identity verification service with tax compliance |
5 | Investor type | Self-certification or regulatory classification | Platform administrator, financial advisor |
Signature schemes
The scheme field indicates the cryptographic algorithm used to sign the claim:
1(ECDSA): Elliptic curve signatures, standard for Ethereum private keys2(RSA): RSA signatures, common for traditional certificate authorities3(Other): Custom signature schemes for specialized use cases
Most claims use ECDSA signatures from issuer Ethereum addresses.
Claim lifecycle workflow
Step 1: Issue - Trusted issuer validates investor documentation off-chain
then calls
addClaim()
on the investor's identity contract, creating an on-chain attestation.
Step 2: Verify - During transfer validation, the Compliance contract retrieves claims by topic, verifies the signature matches the issuer's address, and confirms the issuer is in the TrustedIssuersRegistry.
Step 3: Revoke - If investor status changes (moves countries, loses
accredited status, fails AML re-screening), the issuer calls
removeClaim(),
setting removedAt and marking isActive: false.
Step 4: Expire - Some claims include expiration timestamps in their data
field, requiring periodic re-verification to maintain active trading
eligibility.
Pre-transfer claim validation query
query CheckInvestorCompliance(
$identityAddress: Bytes!
$requiredTopics: [BigInt!]!
) {
identity(id: $identityAddress) {
claims(where: { isActive: true, topic_in: $requiredTopics }) {
topic
issuer {
id
contractName
}
addedAt
data
}
}
}This query verifies whether an investor possesses all required claim topics before initiating a transfer. The dApp uses this to show eligibility status and prevent users from attempting transactions that will fail compliance validation.
Observability: claim verification metrics
Monitor claim verification performance and coverage using the observability stack:
- Claim verification rate: Percentage of transfer attempts where investors have all required claims vs. missing claims (tracked in Hasura analytics dashboard)
- Claim issuance latency: Time from investor document submission to claim issuance, indicates issuer processing efficiency
- Claim revocation frequency: Number of active claims revoked per time period, signals compliance risk events
- Topic coverage by token: Percentage of active investors holding each required claim topic for each token, identifies onboarding gaps
Related contracts:
Identity.sol,
ClaimTopicsRegistry.sol
Cross-reference: See Investor entities for Identity contract details and investor-claim relationships.
TrustedIssuersRegistry
The TrustedIssuersRegistry contract maintains the authoritative list of identity verification providers and attestation services whose claims the platform accepts. Only issuers registered in this contract can create claims that satisfy compliance requirements.
Schema definition
type TrustedIssuersRegistry @entity {
id: Bytes! # Registry contract address
system: System! # Owning platform system
issuers: [TrustedIssuer!]! # Registered claim issuers
}Registry purpose
This registry serves as the root of trust for the identity verification system.
Platform administrators with the claimIssuer role add reputable verification
services to the registry before those services can issue claims. The registry
pattern centralizes trust management, making it efficient to add or revoke
issuer authorization across all tokens simultaneously.
Querying trusted issuers
query GetTrustedIssuers($registryAddress: Bytes!) {
trustedIssuersRegistry(id: $registryAddress) {
issuers(where: { isActive: true }) {
issuer {
id
contractName
}
claimTopics
addedAt
}
}
}This query lists all currently trusted issuers with the claim topics they're authorized to certify. Use this to display available verification providers to investors during onboarding.
Related contracts:
TrustedIssuersRegistry.sol
TrustedIssuer
TrustedIssuer entities represent individual claim issuers registered in the TrustedIssuersRegistry with specific authorization scopes. Each issuer can only certify the claim topics explicitly granted to them.
Schema definition
type TrustedIssuer @entity {
id: Bytes! # Composite ID (registry + issuer address)
registry: TrustedIssuersRegistry! # Parent registry
issuer: Account! # Issuer identity contract or EOA
claimTopics: [BigInt!]! # Topics this issuer can certify
addedAt: BigInt! # Registration timestamp
removedAt: BigInt # Removal timestamp (null if active)
isActive: Boolean! # Currently authorized
}Issuer authorization model
The claimTopics array restricts which attestation types each issuer can
provide. This principle of least privilege prevents a KYC provider from issuing
accredited investor claims (topic 3) if they're only authorized for identity
verification (topic 1) and AML screening (topic 2).
Example authorization matrix:
| Issuer | Topics | Rationale |
|---|---|---|
| Onfido Identity Services | [1, 4] | Authorized for KYC verification and country residence based on ID documents |
| Chainalysis Compliance | [2] | Authorized for AML screening only, specializes in sanctions and PEP checks |
| VerifyInvestor Inc | [3] | Authorized for accredited investor status, licensed for income/asset verification |
| Platform Admin | [5] | Authorized for investor type classification based on account setup |
Issuer authorization validation
query CheckIssuerAuthorization($issuerAddress: Bytes!, $topic: BigInt!) {
trustedIssuers(
where: {
issuer: $issuerAddress
isActive: true
claimTopics_contains: [$topic]
}
) {
registry {
id
}
claimTopics
addedAt
}
}This query verifies whether a specific issuer is authorized to certify a claim topic. The Compliance contract executes this check during claim verification to ensure attestations come from authorized sources.
Related contracts:
TrustedIssuersRegistry.sol
AccessControl
The AccessControl entity manages role-based permissions across the entire platform. It defines which accounts can perform administrative actions (deploying tokens, adding compliance modules, issuing claims) and which smart contracts have system-level privileges.
Schema definition
type AccessControl @entity {
id: Bytes! # Access control contract address
system: System! # Owning platform system
roleAdmins: [AccessControlRoleAdmin!]! # Role hierarchy configuration
# Core roles
admin: [Account!]! # Full system control
# People roles (human operators)
systemManager: [Account!]! # System configuration
identityManager: [Account!]! # Identity registration
tokenManager: [Account!]! # Token deployment
complianceManager: [Account!]! # Compliance rule configuration
addonManager: [Account!]! # Addon deployment
claimPolicyManager: [Account!]! # Claim topic management
claimIssuer: [Account!]! # Identity claim issuance
auditor: [Account!]! # Read-only audit access
organisationIdentityManager: [Account!]! # Organization identity management
# System roles (smart contracts)
systemModule: [Account!]! # System contract modules
identityRegistryModule: [Account!]! # Identity registry contracts
tokenFactoryRegistryModule: [Account!]! # Token factory registry
tokenFactoryModule: [Account!]! # Token factory contracts
addonRegistryModule: [Account!]! # Addon registry
addonModule: [Account!]! # Addon contracts
# Asset roles (token-level operations)
governance: [Account!]! # Protocol governance votes
supplyManagement: [Account!]! # Mint and burn tokens
custodian: [Account!]! # Custody operations
emergency: [Account!]! # Emergency pause and recovery
# Addon roles (lifecycle operations)
fundsManager: [Account!]! # Funds addon operations
saleAdmin: [Account!]! # Sale addon administration
}Role categories and purposes
Core roles
The admin role has unrestricted access to all platform functions. This role
should be held by a multi-signature wallet or governance contract, never a
single externally owned account. Admins can grant and revoke all other roles.
People roles
These roles are assigned to human operators managing the platform:
systemManager: Configure platform-wide settings like fee structures and registry addressesidentityManager: Register new investor identity contracts in the IdentityRegistrytokenManager: Deploy new token contracts through the TokenFactorycomplianceManager: Add/remove compliance modules and configure transfer restrictionsclaimIssuer: Issue and revoke identity claims through TrustedIssuer contractsauditor: Read-only role for compliance auditors and regulators to inspect system state
System roles
These roles are assigned to smart contracts that need elevated privileges:
systemModule: Core protocol contracts (Compliance, IdentityRegistry) that modify system statetokenFactoryModule: Factory contracts authorized to deploy new tokensaddonModule: Addon contracts (DvP, Vault, Yield) that interact with tokens and compliance
Asset roles
These roles operate at the individual token level:
governance: Execute protocol governance decisions like parameter updatessupplyManagement: Mint new tokens for primary issuance and burn tokens for redemptionscustodian: Perform forced transfers for custody operations and legal complianceemergency: Pause trading and freeze accounts during security incidents
Addon roles
These roles manage DALP lifecycle operations:
fundsManager: Configure fund parameters, process subscriptions and redemptionssaleAdmin: Manage primary issuance campaigns, set pricing and allocation rules
Role-based compliance workflow example
Token deployment with compliance configuration
tokenManagercalls TokenFactory to deploy new security token- Token deployment automatically creates Compliance contract
complianceManageradds required compliance modules (TimeTransfersLimitsModule, CountryRestrictModule) to token's Compliance contractcomplianceManagersetsrequiredClaimTopicsto[1, 2, 3](KYC, AML, accredited)claimIssuerrole holders issue claims to investor identities as they complete verification- Investors can now transfer tokens if they have valid claims and pass module checks
This separation of duties prevents any single account from both deploying tokens and issuing the claims that authorize trading, reducing insider risk.
Querying account roles
query GetAccountRoles($accessControlAddress: Bytes!, $account: Bytes!) {
accessControl(id: $accessControlAddress) {
admin(where: { id: $account }) {
id
}
tokenManager(where: { id: $account }) {
id
}
complianceManager(where: { id: $account }) {
id
}
claimIssuer(where: { id: $account }) {
id
}
}
}This query checks which roles a specific account holds. The dApp uses this to show/hide administrative UI features based on user permissions.
Observability: role activity monitoring
Track role usage and detect anomalies using the observability stack:
- Role grant/revoke events: Monitor AccessControl events for unexpected permission changes, alerts on admin role modifications
- Action attribution: Log which role performed each administrative action (module addition, claim issuance) for audit trails
- Role usage frequency: Identify dormant accounts with elevated privileges that should have roles revoked
- Cross-role activity correlation: Detect suspicious patterns like an
account receiving
complianceManagerand immediately disabling all compliance modules
Related contracts:
AccessControl.sol
Transfer validation flow
This section details how Compliance, ComplianceModule, and IdentityClaim entities coordinate to evaluate every token transfer attempt.
Validation sequence
Step 1: Transfer initiation
Investor calls
transfer(recipient, amount)
or
transferFrom(sender, recipient, amount)
on the token contract.
Step 2: Compliance invocation
Token contract calls
compliance.canTransfer(sender, recipient, amount)
before executing the balance update.
Step 3: Identity claim verification
Compliance contract retrieves the Identity contracts for sender and recipient
from the IdentityRegistry, then checks that both identities have active claims
for all topics in requiredClaimTopics. For each required topic:
- Query identity contract for claims with that topic
- Verify claim issuer is in TrustedIssuersRegistry and authorized for that topic
- Validate claim signature matches issuer address
- Confirm claim is active (
isActive: true,removedAtis null)
If either identity is missing any required claim, validation fails immediately.
Step 4: Module rule evaluation
Compliance contract iterates through all active modules (where isActive: true
in TokenComplianceModule) and calls each module's
canTransfer(sender, recipient, amount)
function. Modules return boolean approval.
Each module applies its specific logic:
- TimeTransfersLimitsModule: Queries sender's transfer history, calculates
rolling daily and monthly totals, rejects if
amountwould exceed limits - CountryRestrictModule: Reads sender and recipient country claims (topic 4),
checks against allowed/blocked lists in module
params - MaxBalanceModule: Calculates
recipient.balance + amount, rejects if result exceedsmaxBalancefrom moduleparams
If any module returns false, validation fails.
Step 5: Transfer execution or rejection
If all claim checks pass and all modules approve, compliance.canTransfer()
returns true and the token contract executes the transfer. If any check fails,
the function returns false and the transfer transaction reverts with an error
message indicating the failure reason (missing claim topic, exceeded limit,
restricted country).
Observability: compliance monitoring
Track compliance system health and effectiveness using platform dashboards:
- Transfer rejection rate: Percentage of attempted transfers rejected by compliance, by failure reason (missing claim, module rejection), indicates investor education gaps or misconfigured modules
- Module evaluation latency: Gas cost and execution time per module type, identifies performance bottlenecks in complex compliance stacks
- Claim verification cache hit rate: Percentage of compliance checks served from cached claim data vs. on-chain lookups (if caching implemented), affects transaction cost
- Rule evaluation distribution: Heatmap showing which modules reject transfers most frequently, guides compliance tuning
The compliance metrics dashboard aggregates these statistics across all tokens, providing platform administrators with a real-time view of regulatory enforcement effectiveness.
Claim verification workflow
This section details how the system validates identity claims during compliance checks.
Verification steps
Step 1: Retrieve claim from identity
Compliance calls
identity.getClaim(topic)
to fetch the claim data. If no claim exists for the required topic, verification
fails immediately.
Step 2: Validate issuer authorization
System queries
trustedIssuersRegistry.isTrustedIssuer(claimIssuer, topic)
to confirm the claim issuer is registered and authorized to certify that
specific topic. Unauthorized issuers result in rejection.
Step 3: Verify cryptographic signature
System uses ecrecover on the claim signature to extract the signing address
and compares it to the claim issuer address. Signature mismatch indicates claim
tampering and fails verification.
Step 4: Check claim active status
System verifies claim.isActive == true and claim.removedAt is null. Revoked
or expired claims are rejected.
Only claims passing all four checks satisfy compliance requirements.
See also
- Token entities - Token contracts subject to compliance enforcement
- Investor entities - Identity contracts that hold claims
- System entities - System-level configuration including AccessControl
- Data model overview - Complete entity relationship architecture
- Smart contract reference - Compliance contract method documentation
- Identity architecture - Identity and compliance system design