Loading module
Resolving locale, route permissions, and workspace projection.
Resolving locale, route permissions, and workspace projection.
Current scope: Guest
Category: 60_technical_specs | Version: v1.0.0
Owner: DOCUMENT_CUSTODIAN | Review cycle: 60 days
Approval authority: GOVERNANCE_ADMIN, COMPLIANCE_OFFICER
Documentation portal is read-only. Editing and mutation endpoints are disabled.
Kvary platformu aslen Gürcüce oluşturulmuştur. Gürcüce bir sürüm mevcut olduğunda platform arayüzü, dokümantasyon ve hukuki yorum bakımından yetkili dil Gürcücedir.
Diğer dillere yapılan çeviriler kolaylık sağlamak amacıyla sunulur. Bazı kayıtlar belirli bir akış için farklı bir kaynak veya hukuki yerel ayar taşıyabilir; ancak Gürcüce bir sürüm mevcut olduğunda platform düzeyindeki ifadeler ve yorum bakımından öncelik Gürcüce sürümdedir.
Metadata incomplete: Document ID, Version, Owner Role, Last Review Date, Next Review Date, Change Log
Status: DRAFT
Date: 2026-04-20
Scope: Spec/design batch only
Audit Basis: code-verified repository inspection
This specification proposes a governance-first Real Asset Token Issuance Wizard that fits the strongest existing product patterns in the repo instead of introducing a naive "mint token" form.
The design is grounded in these confirmed repo realities:
currentGate, missingRequirements, and prefilled payloads instead of optimistic direct publishing.The result is a wizard that should:
A direct mint form would be wrong for this repo for six reasons:
In short: the correct product is not "create token." The correct product is "structure an issuance against a verified asset basis and declare it only after governance gates are satisfied."
The wizard should be modeled in three layers:
asset_selectionsourceAssetTypeprimaryRefIdownerStakeholderIdrightsBasisregistryTruthStatusjurisdictioncapacitySnapshot.remainingEligibleQuantityparcelIdallocationIddeclarationIdinspectionIdreceivableIdinventoryLotIdwarehouseReceiptIdseasonlinked map/spatial referenceSourceAssetReferenceeligibility_checklistpolicyVersiondecisionEligibilityChecklistELIGIBLE or policy-allowed CONDITIONALLY_ELIGIBLEasset_structuringmaxIssuableAmountrisk_reserve_configurationrequirementLevelreserveTypeminimumReserveRatioconfiguredReserveRatiohaircutPercentReserveConfigurationinsurance_layerrequirementLevelInsuranceConfigurationfactoring_financing_layermodeNONE: financier, facility reference, advance rate, maximum finance amount, recourse type, priority rank, assignment evidenceFactoringConfigurationissuance_termsissuanceClassunitNameunitCurrencyfaceValuePerUnitunitCounttotalIssuanceAmountclaimRightsredemptionModelissuanceDateTokenIssuanceTermsreview_and_issueREADY_FOR_REVIEW, then APPROVED_FOR_ISSUE, then ISSUEDThese DTOs are intentionally chain-neutral. They describe governance, readiness, and issuance structure first, and leave live blockchain binding out of scope.
export type IssuanceWizardStepId =
| "asset_selection"
| "eligibility_checklist"
| "asset_structuring"
| "risk_reserve_configuration"
| "insurance_layer"
| "factoring_financing_layer"
| "issuance_terms"
| "review_and_issue";
export type IssuanceDraftState =
| "DRAFT"
| "STRUCTURING"
| "READY_FOR_REVIEW"
| "APPROVED_FOR_ISSUE"
| "ISSUED"
| "SUSPENDED"
| "MATURED"
| "REDEEMED"
| "CLOSED"
| "CANCELLED";
export type ReadinessSeverity = "BLOCKER" | "WARNING" | "INFO";
export type EvidenceReference = Readonly<{
kind: string;
refId: string;
label?: string;
uri?: string;
sha256?: string;
verified?: boolean;
uploadedAt?: string;
expiresAt?: string;
}>;
export type ReadinessFinding = Readonly<{
code: string;
severity: ReadinessSeverity;
stepId: IssuanceWizardStepId;
message: string;
path?: string;
sourceRefId?: string;
evidenceRefId?: string;
policyRef?: string;
expiresAt?: string;
suggestedAction?: string;
}>;
export type SourceAssetType =
| "PARCEL"
| "OUTPUT_ALLOCATION"
| "DECLARATION"
| "INSPECTION_CERTIFICATE"
| "RECEIVABLE"
| "INVENTORY_LOT";
export type RightsBasis =
| "OWNERSHIP"
| "LEASE"
| "ASSIGNMENT"
| "RECEIVABLE_ASSIGNMENT"
| "WAREHOUSE_CUSTODY";
export type RegistryTruthStatus =
| "VERIFIED"
| "PENDING_REVIEW"
| "DISPUTED"
| "EXTERNAL_ONLY";
export type SourceAssetCapacitySnapshot = Readonly<{
grossQuantity?: number;
unit?: string;
declaredQuantity?: number;
reservedQuantity?: number;
remainingEligibleQuantity?: number;
approvedLimitQuantity?: number;
requestedIssuanceQuantity?: number;
}>;
export type SourceAssetReference = Readonly<{
sourceAssetType: SourceAssetType;
primaryRefId: string;
parcelId?: string;
productiveAssetId?: string;
allocationId?: string;
declarationId?: string;
inspectionId?: string;
receivableId?: string;
inventoryLotId?: string;
ownerStakeholderId: string;
operatorStakeholderId?: string;
rightsBasis: RightsBasis;
registryTruthStatus: RegistryTruthStatus;
jurisdiction: string;
region?: string;
season?: string;
sourceState?: string;
assetSnapshot: Readonly<{
label?: string;
productCategory?: string;
quantity?: number;
unit?: string;
qualityGrade?: string;
originRegion?: string;
warehouseReceiptId?: string;
}>;
capacitySnapshot: SourceAssetCapacitySnapshot;
linkedEvidence: readonly EvidenceReference[];
}>;
export type EligibilityChecklistItem = Readonly<{
code: string;
label: string;
satisfied: boolean;
blocking: boolean;
detail?: string;
sourceDomain?:
| "PARCEL"
| "DECLARATION"
| "INSPECTION"
| "RESERVE"
| "INSURANCE"
| "FINANCING";
expiresAt?: string;
waiverAllowed?: boolean;
}>;
export type EligibilityDecision =
| "ELIGIBLE"
| "CONDITIONALLY_ELIGIBLE"
| "INELIGIBLE";
export type EligibilityChecklist = Readonly<{
policyVersion: string;
evaluatedAt?: string;
evaluatedBy?: string;
decision: EligibilityDecision;
linkedInspectionStatus?:
| "NOT_REQUIRED"
| "CURRENT"
| "EXPIRING"
| "EXPIRED"
| "FAILED";
linkedDeclarationStatus?: string;
evidenceCoverageStatus?: "COMPLETE" | "PARTIAL" | "MISSING";
items: readonly EligibilityChecklistItem[];
}>;
export type AssetStructuringSnapshot = Readonly<{
valuationBasis: Readonly<{
method:
| "REGISTRY_SNAPSHOT"
| "INSPECTION_VERIFIED"
| "RECEIVABLE_FACE_VALUE"
| "MARKET_REFERENCE"
| "INVENTORY_REPORT";
amount: number;
currency: string;
valuationDate: string;
providerStakeholderId?: string;
reportRefId?: string;
}>;
requestedIssuanceAmount: number;
requestedIssuanceCurrency: string;
requestedIssuanceQuantity?: number;
haircutPercent: number;
riskBufferPercent?: number;
overcollateralizationPercent?: number;
maxIssuableAmount: number;
claimBasis:
| "OWNERSHIP_BENEFICIAL_INTEREST"
| "OUTPUT_PROCEEDS"
| "RECEIVABLE_COLLECTION_RIGHT"
| "INVENTORY_RELEASE_RIGHT";
servicingPartyId?: string;
}>;
export type ReserveRequirementLevel = "NONE" | "OPTIONAL" | "REQUIRED";
export type ReserveConfiguration = Readonly<{
requirementLevel: ReserveRequirementLevel;
reserveType:
| "CASH_ESCROW"
| "COLLATERAL_BUFFER"
| "THIRD_PARTY_GUARANTEE"
| "HYBRID";
minimumReserveRatio: number;
configuredReserveRatio: number;
haircutPercent: number;
overcollateralizationPercent?: number;
reserveAssetKind?:
| "FIAT"
| "TOKENIZED_CASH"
| "SOURCE_ASSET_BUFFER"
| "GUARANTEE";
holderPartyId?: string;
evidenceRefs: readonly EvidenceReference[];
replenishmentPolicy?: string;
breachAction?: "BLOCK_ISSUANCE" | "SUSPEND" | "TOP_UP_REQUIRED";
}>;
export type InsuranceConfiguration = Readonly<{
requirementLevel: ReserveRequirementLevel;
coverageStatus:
| "NOT_SELECTED"
| "PENDING_BINDER"
| "ACTIVE"
| "EXPIRED";
providerStakeholderId?: string;
reinsurancePartnerStakeholderId?: string;
policyReference?: string;
coverageType?:
| "CROP"
| "PROPERTY_DAMAGE"
| "TITLE_RISK"
| "CREDIT"
| "PARAMETRIC"
| "MULTI_PERIL";
insuredValue?: number;
currency?: string;
deductiblePercent?: number;
coverageStartAt?: string;
coverageEndAt?: string;
claimBeneficiaryPartyId?: string;
exclusionsNote?: string;
evidenceRefs: readonly EvidenceReference[];
}>;
export type FactoringConfiguration = Readonly<{
mode:
| "NONE"
| "RECEIVABLE_FACTORING"
| "INVENTORY_FINANCE"
| "PREPAYMENT_ADVANCE";
financierStakeholderId?: string;
facilityReference?: string;
advanceRatePercent?: number;
maximumFinanceAmount?: number;
currency?: string;
recourseType?: "WITH_RECOURSE" | "WITHOUT_RECOURSE";
priorityRank?: "SENIOR" | "PARI_PASSU" | "SUBORDINATED";
settlementWaterfall?: readonly string[];
maturityDate?: string;
assignmentEvidenceRefs: readonly EvidenceReference[];
notes?: string;
}>;
export type TransferRestriction = Readonly<{
kind:
| "PERMISSIONED_ONLY"
| "JURISDICTION_LIMITED"
| "LOCKUP"
| "INVESTOR_TYPE_LIMIT";
value?: string;
until?: string;
}>;
export type TokenIssuanceTerms = Readonly<{
issuanceClass: string;
internalSymbol?: string;
unitName: string;
unitCurrency: string;
faceValuePerUnit: number;
unitCount: number;
totalIssuanceAmount: number;
claimRights: readonly (
| "PRINCIPAL"
| "PROCEEDS"
| "RESERVE_ACCESS"
| "INSURANCE_PROCEEDS"
| "RECOVERY_ACTION"
)[];
redemptionModel:
| "AT_MATURITY"
| "AMORTIZING"
| "REVOLVING_POOL"
| "EVENT_DRIVEN";
issuanceDate: string;
maturityDate?: string;
redemptionWindowStart?: string;
redemptionWindowEnd?: string;
transferRestrictions: readonly TransferRestriction[];
publicationMode:
| "INTERNAL_REGISTRY"
| "DECLARED_PUBLICLY"
| "PRIVATE_PLACEMENT";
servicingNotes?: string;
downstreamBindingMode: "DECLARATIVE_ONLY" | "FUTURE_CHAIN_BINDING";
}>;
export type ApprovalRecord = Readonly<{
stage:
| "READINESS_REVIEW"
| "ISSUANCE_APPROVAL"
| "ISSUE_EXECUTION"
| "SUSPENSION"
| "AMENDMENT";
actorId: string;
role: string;
decidedAt: string;
decision: "APPROVED" | "CHANGES_REQUIRED" | "REJECTED" | "EXECUTED";
note?: string;
}>;
export type IssuanceReadinessStepStatus = Readonly<{
stepId: IssuanceWizardStepId;
ready: boolean;
blockerCount: number;
warningCount: number;
infoCount: number;
lastEvaluatedAt?: string;
}>;
export type IssuanceReadinessReport = Readonly<{
draftId: string;
evaluatedAt: string;
evaluatedBy?: string;
policyVersion: string;
overallStatus:
| "NOT_READY"
| "READY_WITH_WARNINGS"
| "READY_FOR_REVIEW"
| "APPROVED_FOR_ISSUE";
hardBlockers: readonly ReadinessFinding[];
warnings: readonly ReadinessFinding[];
infoNotes: readonly ReadinessFinding[];
stepStatuses: readonly IssuanceReadinessStepStatus[];
immutableSnapshotHash?: string;
requiredApprovals: readonly (
| "READINESS_REVIEW"
| "ISSUANCE_APPROVAL"
| "ISSUE_EXECUTION"
)[];
}>;
export type RealAssetIssuanceDraft = Readonly<{
draftId: string;
state: IssuanceDraftState;
currentStep: IssuanceWizardStepId;
sourceAsset?: SourceAssetReference;
eligibility?: EligibilityChecklist;
structuring?: AssetStructuringSnapshot;
reserveConfiguration?: ReserveConfiguration;
insuranceConfiguration?: InsuranceConfiguration;
factoringConfiguration?: FactoringConfiguration;
issuanceTerms?: TokenIssuanceTerms;
readinessReport?: IssuanceReadinessReport;
evidenceCatalog: readonly EvidenceReference[];
governance: Readonly<{
createdBy: string;
lastEditedBy?: string;
rulesVersion: string;
sourceSystem: string;
amendmentOfDraftId?: string;
approvals: readonly ApprovalRecord[];
}>;
createdAt: string;
updatedAt: string;
submittedForReviewAt?: string;
approvedForIssueAt?: string;
issuedAt?: string;
suspendedAt?: string;
closedAt?: string;
}>;
READY_FOR_REVIEW| Condition | Default severity | Notes | | --- | --- | --- | | Ownership not verified | Hard blocker | Source truth failure | | Parcel/source record not registry-linked | Hard blocker | Unless policy explicitly permits external-only basis | | Remaining eligible quantity exhausted | Hard blocker | Prevents over-issuance | | Inspection expired | Hard blocker if required, warning otherwise | Policy-driven | | Inspection failed | Hard blocker | Eligibility failure | | Mandatory evidence missing | Hard blocker | Includes proof of title/assignment/reserve funding | | Insurance missing | Warning if optional, hard blocker if required | Policy-driven | | Reserve ratio below minimum | Hard blocker | Deterministic numeric gate | | Reserve evidence stale | Warning, escalates to blocker if policy requires current attestation | Policy-driven | | Factoring terms incomplete while financing selected | Hard blocker | Selected structure not fully defined | | Valuation stale | Warning or hard blocker depending on age threshold | Policy-driven | | Transfer restriction not yet implementable | Warning | Allowed only if declaration is still internal | | Snapshot drift after approval | Hard blocker | Must force re-review |
READY_FOR_REVIEW, but approval must explicitly accept or return those warnings.DRAFT: Draft exists, source asset may still be incomplete.STRUCTURING: Source asset chosen and structuring work is underway.READY_FOR_REVIEW: Draft frozen for governance review; no hard blockers remain.APPROVED_FOR_ISSUE: Governance approval granted; economics and claim terms frozen.ISSUED: Issuance event recorded.SUSPENDED: Issuance is paused due to reserve breach, evidence lapse, insurance lapse, or governance hold.MATURED: Economic maturity reached.REDEEMED: Redemption completed.CLOSED: Administrative close-out complete.CANCELLED: Withdrawn before issue.| From | To | Trigger | Mandatory constraints |
| --- | --- | --- | --- |
| DRAFT | STRUCTURING | Source asset basis selected | source asset snapshot exists |
| STRUCTURING | READY_FOR_REVIEW | Mark ready for review | zero hard blockers; snapshot frozen |
| READY_FOR_REVIEW | STRUCTURING | Changes requested | reviewer note recorded |
| READY_FOR_REVIEW | APPROVED_FOR_ISSUE | Governance approval | separation of duties satisfied |
| APPROVED_FOR_ISSUE | ISSUED | Issue action executed | immutable snapshot hash matches approved record |
| ISSUED | SUSPENDED | Breach/lapse/policy hold | suspension reason recorded |
| SUSPENDED | ISSUED | Reinstatement approved | amendment/reinstatement evidence recorded |
| ISSUED | MATURED | Maturity reached | maturity rule satisfied |
| MATURED | REDEEMED | Redemption recorded | redemption evidence recorded |
| REDEEMED | CLOSED | Closure completed | archival/close-out record present |
| DRAFT or STRUCTURING or READY_FOR_REVIEW | CANCELLED | Cancellation recorded | actor authority and reason recorded |
APPROVED_FOR_ISSUE or ISSUED.SUSPENDED, MATURED, REDEEMED, and CLOSED belong to later operational phases, but the states should exist now so issuance terms can be designed against them.Allowed initiators should be:
LAND_OWNER or other authorized asset originator when the asset basis belongs to themISSUANCE_OPERATOR acting on behalf of the originatorNot allowed:
Editable by:
ISSUANCE_OPERATORRISK_OFFICERFINANCE_OPERATORConstraints:
Minimum proposed separation of duties:
READINESS_REVIEWER verifies no blockers remainISSUANCE_APPROVER grants issue approvalISSUE_EXECUTOR records the issuance event against the approved snapshotAdditional constraint:
After ISSUED, these fields must be immutable:
These must use amendment flow, never direct edit:
These may be append-only operational events instead of amendments:
The wizard should eventually consume parcel/land-owner truth as a source-asset resolver, not as free-form user input.
Implications:
missingRequirements and gate logic should prefilter whether a parcel-origin issuance can even startInspection is not an afterthought. It belongs in eligibility gating.
Implications:
This wizard should reuse the declaration-backed mutation pattern already proven in the repo.
That means future implementation should follow:
This keeps issuance aligned with the current durable internal/admin write model rather than the repo's weaker public action surfaces.
Factoring and financing should be declared now but executed later.
Implications:
Reserve attestations and insurance binders should follow the same evidence-reference discipline already used in declaration flows:
This should launch first as an internal/admin governed wizard, not as a public self-service mint surface.
That matches the repo's strongest current truth:
packages/core or a new packages/core/issuance slice, using the DTOs above.blockingErrors, warnings, and readinessChecklist equivalents for issuance.apps/web by reusing current declaration wizard/readiness panel patterns.What should not be implemented first:
ISSUANCE WIZARD SPEC READY
The spec is ready to guide implementation because the required seams are now explicit:
Known implementation risks remain, but they do not block the spec itself:
Those are implementation-order constraints, not reasons to revert to a direct mint form.