Skip to content

Metadata

Every NFT and on-chain account on Sails.to carries structured metadata — the fields that tell the protocol what a token represents, who holds it, and what rules apply.

Overview

Metadata is not decoration. On Sails.to, metadata is the mechanism by which smart contracts enforce compliance, authorize actions, and maintain audit trails. Every NFT — whether it represents a KYC credential, an operator role, or an offering configuration — carries a structured set of fields that the Solana runtime reads and validates on every instruction.

There are no optional fields in a "nice to have" sense. Each field exists because a specific compliance check, authorization gate, or audit requirement demands it. Remove a field and a smart contract check breaks. Add a field without purpose and you waste on-chain storage that every validator must replicate. The metadata schemas documented here are the product of that discipline.

This page covers the six metadata categories on the platform: KYC Credential NFTs, Role NFTs, OfferingState PDAs, ComplianceConfig PDAs, Program Events, and the privacy architecture that binds them together.

KYC Credential NFT Metadata

Every investor on the platform carries a KYC Credential NFT — an on-chain attestation that a licensed verification provider has confirmed the investor's identity, classification, and regulatory status. This NFT contains zero personally identifiable information. What it does contain is everything the Transfer Hook needs to enforce compliance rules at the protocol level.

KYC NFT Metadata (on-chain, no PII):
├── investor_class:      enum { Accredited, Professional, QualifiedPurchaser, Retail }
├── jurisdiction_hash:   [u8; 32]   // SHA-256 of ISO country code
├── reg_exemption:       enum { RegD506b, RegD506c, RegS, RegA, RegCF }
├── verification_level:  enum { Basic, Enhanced, InstitutionalEDD }
├── issued_by:           Pubkey     // KYC provider's license NFT
├── issued_at:           i64        // Unix timestamp
├── expires_at:          i64        // Credential expiration
├── aml_clear:           bool       // Anti-money laundering clearance
└── pep_clear:           bool       // Politically exposed person check

Field-by-Field Breakdown

Field Type Purpose
investor_class Enum Determines which offerings the investor can access. An Accredited Investor can participate in Reg D 506(b) and 506(c) offerings. A Professional Investor can access institutional tranches. A QualifiedPurchaser meets the higher threshold for certain private fund exemptions. Retail investors are limited to Reg A and Reg CF offerings. The smart contract checks this classification on every mint, every transfer, and every distribution claim.
jurisdiction_hash [u8; 32] A SHA-256 hash of the investor's ISO 3166-1 country code. The blockchain never sees the raw country code — only its hash. But the Transfer Hook can still enforce jurisdiction whitelists by comparing the investor's hash against the offering's list of allowed jurisdiction hashes. A Reg S offering restricted to non-US investors will reject any transfer to a wallet whose jurisdiction hash matches the US hash.
reg_exemption Enum Records which regulatory exemption the investor was verified under. This matters because different exemptions carry different rules — Reg D 506(c) requires verified accreditation, while 506(b) allows up to 35 non-accredited investors. The offering's ComplianceConfig checks that the investor's exemption is compatible with the offering's exemption before allowing any operation.
verification_level Enum Indicates the depth of due diligence performed. Basic covers standard identity verification. Enhanced adds source-of-funds checks and additional document review. InstitutionalEDD is full Enhanced Due Diligence for institutional investors — beneficial ownership, corporate structure analysis, and sanctions screening at every level. Higher-value offerings can require a minimum verification level.
issued_by Pubkey The public key of the licensed KYC provider's license NFT that issued this credential. This creates a verifiable chain of authority: the platform can trace any credential back to the provider who issued it, and if a provider's license is revoked, every credential they issued can be flagged for re-verification.
issued_at i64 Unix timestamp recording when the credential was issued. Used for audit trail purposes and to determine credential age — some compliance policies require re-verification after a certain period regardless of the expires_at date.
expires_at i64 Unix timestamp after which this credential is no longer valid. When expires_at passes, the smart contract rejects any new minting or transfer operations for this investor until a fresh credential is issued. The platform tracks expiration windows proactively and triggers re-verification workflows before credentials expire.
aml_clear bool Anti-money laundering clearance. If false, the investor has not passed AML screening and cannot participate in any offering. The Transfer Hook checks this flag on every transaction — a cleared investor whose AML status is later revoked is immediately locked out of all transfers.
pep_clear bool Politically exposed person clearance. PEP status requires enhanced monitoring under most regulatory frameworks. If false, the investor is flagged as a PEP or has not been screened, and the platform applies additional transaction scrutiny. Both aml_clear and pep_clear must be true for unrestricted participation.

The critical design constraint: none of these fields contain PII. No names, no addresses, no document images, no phone numbers. The investor's actual identity documents are encrypted and stored off-chain in the KYC grain's journal with 7-year retention. The on-chain credential carries only the classification flags and cryptographic proofs that the smart contract needs to make compliance decisions.

Role NFT Metadata

The platform uses NFT-based authorization for every privileged action. There are no API keys, no admin passwords, no shared secrets. If you want to mint security tokens, you need an Issuer NFT in your wallet. If you want to authenticate a CrossConversion, you need a Trustee NFT. The smart contract checks the caller's wallet for the required NFT on every instruction — no NFT, no authorization.

Each Role NFT is a print edition from the Sails Master NFT, carrying role-specific metadata and optional expiration. All Role NFTs are recallable — if a participant's authorization is revoked, their NFT is burned and every instruction that checks for it will immediately fail.

Role NFT Capabilities Issued By Recallable
Platform Operator Deploy offerings, manage platform configuration, approve issuers and brokers, emergency pause Master NFT (3-of-5 keyholder threshold) Yes
White-Label Operator Operate a branded instance, manage own issuers and offerings, configure white-label UI Platform Operator Yes
Trustee Authenticate CrossConversions, validate distributions, sign reconciliation reports, emergency freeze Platform Operator Yes
KYC Issuer Issue KYC Credential NFTs to verified investors, revoke credentials, update verification status Platform Operator Yes
Broker Place investors into offerings, execute secondary trades, manage client portfolios, earn placement commissions Platform Operator Yes
Issuer Create offerings within their Series, mint security tokens, manage investor whitelist, configure compliance parameters Platform Operator Yes
Paying Agent Execute distributions, manage revenue waterfall, process investor claims, handle tax withholding Trustee Yes

The hierarchy matters. A Platform Operator is issued by the Master NFT holders — the 3-of-5 keyholder set that controls the root of the authority chain. A Paying Agent is issued by a Trustee, not directly by the Platform Operator, because paying agent authority is scoped to the trust relationship. If a Trustee's NFT is recalled, every Paying Agent they issued is also invalidated. Authority flows downward; revocation propagates upward.

Offering State Metadata

Every offering on the platform is represented by an OfferingState PDA — a Program Derived Address account seeded by ["offering", series_id]. This PDA is the canonical source of truth for the offering's current state, and every instruction that touches the offering reads from it.

OfferingState PDA Fields:
├── series_id:            Pubkey   // Links to the DAO Series LLC
├── max_supply:           u64      // Maximum tokens that can ever be minted
├── minted:               u64      // Tokens minted so far
├── locked_in_crossconv:  u64      // Tokens locked in CrossConversion lockbox
├── nominal_value:        u64      // Face value per token (in cents)
├── status:               enum     // Active, Paused, Closed
└── version:              u8       // PDA version for migration support
Field Purpose
series_id Links the offering to its parent DAO Series LLC. Every offering belongs to exactly one Series, and the Series controls the legal structure — operating agreement, member rights, and regulatory filings. The series_id is how the smart contract traces an offering back to its legal entity.
max_supply The hard cap on token issuance. Once minted reaches max_supply, the mint_security_token instruction rejects all further minting. This is not a soft limit — it is enforced at the protocol level and cannot be changed without a program upgrade approved by the 3-of-5 keyholder set.
minted Running count of tokens minted to date. Incremented by the mint_security_token instruction. Combined with max_supply, this gives real-time visibility into how much of an offering has been subscribed — critical for both compliance reporting and investor transparency.
locked_in_crossconv Tokens currently locked in the CrossConversion lockbox. The supply invariant locked_in_crossconv == isin_outstanding must hold at all times — if on-chain lockbox state ever diverges from Clearstream positions, the reconciliation engine flags it immediately. This counter is incremented by burn_for_crossconversion and decremented by redeem_from_crossconversion.
nominal_value The face value of each token in cents. A token with nominal value of 10000 represents $100.00. This is used by the distribution waterfall to calculate per-token payouts and by the cap table to report ownership in dollar terms, not just token counts.
status The offering lifecycle state. Active means minting, transfers, and distributions are permitted. Paused freezes all operations — triggered by the emergency pause bit in ComplianceConfig or by a Security Admin NFT action. Closed is terminal: remaining tokens are burned, outstanding CrossConversions settled, and the PDA is sealed. There is no transition back from Closed.
version PDA schema version for data migration support. When the program is upgraded, migration instructions read the version field to determine which migration path to apply. This ensures backward compatibility without breaking existing on-chain state.

ComplianceConfig Metadata

Every offering carries a companion ComplianceConfig PDA, seeded by ["compliance", offering_id]. This is where the regulatory rules live — not in documentation, not in terms of service, but in on-chain data that the Transfer Hook reads on every single token transfer.

ComplianceConfig PDA Fields:
├── offering_id:              Pubkey              // The offering this config governs
├── allowed_jurisdictions:    Vec<[u8; 32]>       // SHA-256 hashes of allowed ISO codes
├── min_investment:           u64                 // Minimum investment amount (in cents)
├── lock_up_days:             u32                 // Mandatory holding period
├── max_investors:            u32                 // Maximum investor count
├── accreditation_required:   bool                // Whether accredited status is mandatory
├── reg_exemption:            enum                // Which regulatory exemption applies
├── features:                 u64                 // Bitmask for feature flags
└── version:                  u8                  // PDA version for migration
Field Purpose
offering_id Links this compliance configuration to its parent offering. One offering, one ComplianceConfig — the relationship is 1:1 and enforced by the PDA seed derivation.
allowed_jurisdictions A list of SHA-256 hashes of ISO 3166-1 country codes representing the jurisdictions from which investors are permitted. The Transfer Hook compares the investor's jurisdiction_hash from their KYC Credential NFT against this list. If the hash is not present, the transfer is rejected. This is how a Reg S offering excludes US investors without ever storing a country name on-chain.
min_investment The minimum investment amount in cents. The mint_security_token instruction checks that the minted amount multiplied by the nominal value meets or exceeds this threshold. Prevents micro-positions that would create compliance overhead disproportionate to their value.
lock_up_days Mandatory holding period in days before an investor can transfer their tokens. For Reg D securities, this is typically 6–12 months. The transfer_with_compliance instruction compares the investor's locked_until timestamp against the current slot time — if the lock-up has not elapsed, the transfer is rejected at the protocol level. No exceptions, no overrides.
max_investors The maximum number of investors allowed in the offering. Critical for Reg D compliance — 506(b) limits non-accredited investors to 35, and total investor counts must remain under SEC thresholds. The smart contract tracks the current investor count in the OfferingState PDA and rejects any mint that would exceed this limit.
accreditation_required When true, only investors whose KYC Credential NFT carries an investor_class of Accredited, Professional, or QualifiedPurchaser can participate. Retail investors are blocked. This flag is the on-chain enforcement of the accreditation requirement for Reg D 506(c) and similar exemptions.
reg_exemption The specific regulatory exemption under which the offering operates. This field is cross-checked against the investor's reg_exemption in their KYC Credential NFT to ensure compatibility. An investor verified under Reg A cannot be minted tokens in a Reg D 506(c) offering.
features A 64-bit bitmask for feature flags. The most critical bit is the emergency pause — when set (gated by Security Admin NFT), the Transfer Hook rejects all transfers for the offering. This is the circuit breaker for regulatory emergencies. Other bits control optional features like distribution auto-claiming, secondary trading eligibility, and CrossConversion availability — all toggleable without a program upgrade.
version Schema version for forward-compatible data migration, identical in purpose to the OfferingState version field.

The ComplianceConfig is set at offering creation by the Issuer with Platform Operator approval. Modifications after creation require the same dual authorization. This is compliance as data — stored on-chain, enforced by the runtime, auditable by anyone.

Event Metadata

The sails_securities program emits five structured events for every significant state change. These are not optional log messages — they are the mechanism by which the off-chain application layer stays synchronized with on-chain state. The Solana Event Watcher grain subscribes to these events and routes them to the appropriate application grains for processing.

Event Fields Why It Matters
SecurityMinted offering, investor, amount, timestamp Triggers cap table updates in the Offering Grain and portfolio notifications in the Investor Grain. The timestamp field anchors the lock-up period calculation — locked_until = timestamp + lock_up_days.
CrossConversionRequested offering, amount, direction, isin Initiates the Clearstream workflow. The direction field indicates whether tokens are being locked (on-chain → bankable) or unlocked (bankable → on-chain). The isin field carries the ISIN code for the bankable security, linking the on-chain event to the traditional settlement system.
DistributionPaid offering, epoch, total_amount Notifies all investor grains that a distribution is available for claiming. The epoch field identifies the distribution period (e.g., Q1 2025), and total_amount represents the aggregate payout. Individual per-token amounts are calculated from the DistributionRecord PDA.
ComplianceViolation offering, investor, reason Alerts the DAO Manager Grain and Compliance Grain when a transaction is rejected for compliance reasons. The reason field is a structured enum — not a free-text string — covering cases like ExpiredKYC, JurisdictionMismatch, LockUpActive, AccreditationInsufficient, and InvestorLimitReached. Every violation is written to the regulatory audit log.
TransferCompleted offering, from, to, amount Updates the cap table in the Offering Grain and confirms trade settlement in the Broker Grain. This event fires only after all compliance checks have passed — if you see a TransferCompleted event, the transfer was fully compliant at the time of execution.

Every event is also written to the Solana Event Watcher's local event log for replay capability. If a grain misses an event — network partition, grain restart, temporary outage — the watcher replays the missed events in order. No event is ever lost. This is the foundation of the platform's eventual consistency model: on-chain state is the source of truth, events are the synchronization mechanism, and replay is the recovery path.

Privacy by Design

The metadata architecture is built on a single, non-negotiable principle: no personally identifiable information ever touches the blockchain. This is not a policy preference — it is a structural constraint enforced by the schema design itself.

  • Jurisdiction is hashed. The jurisdiction_hash field stores a SHA-256 hash of the ISO country code, not the code itself. An observer reading the blockchain sees a 32-byte hash — they cannot determine the investor's country without brute-forcing 249 possible ISO codes (trivial in theory, but the point is that jurisdiction is not stored in plaintext). The Transfer Hook compares hashes, not strings. The compliance check works without ever revealing the underlying data.
  • Verification level is categorical. The verification_level field is an enum — Basic, Enhanced, or InstitutionalEDD. It tells the smart contract how thoroughly the investor was vetted. It does not reveal what documents were submitted, what the documents contained, or who the investor is. The actual verification artifacts — passport scans, proof-of-address documents, selfie captures — are encrypted with AES-256 and stored in the KYC grain's append-only journal, never on-chain.
  • Identity is a public key. Investors are identified on-chain by their wallet address — a Solana public key. The mapping between public key and real-world identity exists only in the KYC grain's encrypted journal, protected by 7-year retention policies and GDPR-compliant cryptographic shredding (delete the encryption key to permanently erase the data).
  • Role NFTs carry authorization, not identity. A Trustee NFT proves that the holder is authorized to authenticate CrossConversions. It does not encode the trustee's name, their firm, or their license number. The mapping between NFT and real-world entity is maintained off-chain in the platform's grain journals.
  • Events carry addresses, not names. When SecurityMinted fires, the investor field is a wallet public key. When ComplianceViolation fires, the investor field is a wallet public key. At no point does any program event include a name, email, phone number, or any other PII.

This architecture satisfies both regulatory requirements and privacy expectations. Regulators can audit compliance enforcement through on-chain events and metadata — they can verify that every transfer was compliant, every mint checked KYC credentials, and every distribution followed the waterfall rules. But they access investor identity through the off-chain KYC records, not through the blockchain. The chain proves what happened. The grain journals prove who was involved. Neither system exposes more than it needs to.