Cartographie des articles MiCA
Cartographie complète article par article du Règlement (UE) 2023/1114 — Marchés de crypto-actifs.
Source : Règlement (UE) 2023/1114|En vigueur: 29 juin 2023|Entièrement applicable: 30 décembre 2024
Référence rapide — Seuils et délais clés
Les chiffres essentiels de MiCA que tout implémenteur doit connaître
| Article | Seuil / Délai |
|---|---|
| Art. 4(2) | Exemption de livre blanc : ≤ 1 000 000 EUR sur 12 mois |
| Art. 4(2) | Exemption par État membre : < 150 personnes par État membre |
| Art. 8(5) | Notification du livre blanc : ≥ 20 JOURS OUVRABLES avant publication |
| Art. 10(1) | Publication du résultat de l’offre : dans les 20 JOURS OUVRABLES suivant la fin de la souscription |
| Art. 12(2) | Livre blanc modifié : notifié ≥ 7 JOURS OUVRABLES avant publication |
| Art. 13(1) | Délai de rétractation des détenteurs de détail : 14 JOURS CALENDAIRES |
| Art. 13(2) | Délai de remboursement : 14 JOURS à compter de la notification de la rétractation |
| Art. 14(3) | Offre annulée : fonds restitués dans les 25 JOURS CALENDAIRES |
| Art. 20(2) | Évaluation de l’agrément d’un émetteur d’ART : 60 JOURS OUVRABLES |
| Art. 35(1) | Plancher de fonds propres ART : 350 000 EUR |
| Art. 35(1) | Fonds propres ART : 2 % de la réserve moyenne sur 6 mois |
| Art. 45(5) | Fonds propres ART d’importance significative : 3 % de la réserve moyenne |
| Art. 36(4) | Dépôts ART auprès d’établissements de crédit : ≥ 30 % par monnaie de référence |
| Art. 45(7) | Dépôts ART d’importance significative : ≥ 60 % par monnaie de référence |
| Art. 43 | ART d’importance significative : ≥ 3 critères sur 7 |
| Art. 43 | Seuil de détenteurs : > 10 000 000 |
| Art. 43 | Seuil de valeur : > 5 000 000 000 EUR |
| Art. 49(4) | Remboursement EMT : à tout moment, au pair, sans frais |
| Art. 51(11) | Notification du livre blanc EMT : ≥ 20 JOURS OUVRABLES avant publication |
| Art. 54(a) | Fonds reçus pour des EMT : ≥ 30 % déposés auprès d’établissements de crédit |
| Art. 63(9) | Évaluation de l’agrément d’un PSCA : 40 JOURS OUVRABLES |
| Art. 67(1)(a), Annex IV | PSCA catégorie 1 : 50 000 EUR |
| Art. 67(1)(a), Annex IV | PSCA catégorie 2 : 125 000 EUR |
| Art. 67(1)(a), Annex IV | PSCA catégorie 3 : 150 000 EUR |
| Art. 68(9) | Conservation des enregistrements des PSCA : 5 ANS |
| Art. 70(3) | Fonds des clients placés auprès d’un établissement de crédit ou d’une banque centrale avant la fin du jour ouvrable suivant |
| Art. 75(5) | Relevé de position (conservation) : au moins tous les 3 MOIS |
| Art. 81(12) | Réexamen de l’adéquation : au moins tous les 2 ANS |
Cartographie article par article
Titre II — Crypto-actifs autres que les ART et les EMT — Art. 4–15
Offre au public de crypto-actifs
Une personne ne peut offrir au public dans l’Union des crypto-actifs (autres que des ART ou des EMT) que si elle est une personne morale, a rédigé, notifié et publié un livre blanc sur les crypto-actifs et respecte les obligations des offreurs — sauf exemption de l’art. 4(2) ou exclusion de l’art. 4(3).
Obligations
- L’offreur doit être une personne morale (art. 4(1)(a))
- Rédiger un livre blanc sur les crypto-actifs (art. 6)
- Notifier le livre blanc à l’autorité compétente au moins 20 JOURS OUVRABLES avant sa publication (art. 8(5))
- Publier le livre blanc sur le site internet de l’offreur (art. 9)
- Exemptions (art. 4(2)) : moins de 150 personnes par État membre, 1 000 000 EUR au plus sur 12 mois, ou investisseurs qualifiés uniquement
Seuils clés
Seuil d’exemption (art. 4(2)(b)): ≤ 1 000 000 EUR sur 12 mois
Exemption par État membre (art. 4(2)(a)): < 150 personnes par État membre
Notification à l’autorité compétente (art. 8(5)): ≥ 20 JOURS OUVRABLES avant publication
import { z } from "zod"
const TargetAudienceEnum = z.enum([
"general_public", // full Art. 4(1) requirements
"limited_distribution", // Art. 4(2)(a): < 150 persons per Member State
"qualified_investors", // Art. 4(2)(c): addressed to and holdable only by QIs
"free_distribution", // Art. 4(3)(a): Title II does not apply
"mining_reward", // Art. 4(3)(b)
"existing_utility", // Art. 4(3)(c): good or service exists or is in operation
"limited_network", // Art. 4(3)(d)
])
const OfferingDataSchema = z.object({
offerorIsLegalEntity: z.boolean(), // Art. 4(1)(a)
totalConsiderationEUR_12months: z.number().nonnegative(), // Art. 4(2)(b): ≤ EUR 1,000,000
targetAudience: TargetAudienceEnum,
personsPerMemberState: z.number().int().nonnegative(), // Art. 4(2)(a)
holdingRestrictedToQualifiedInvestors: z.boolean(), // Art. 4(2)(c)
freeOfferAgainstDataOrFees: z.boolean(), // Art. 4(3), second subparagraph: then not "free"
utilityGoodOrServiceExists: z.boolean(), // Art. 4(3)(c)
intendsAdmissionToTrading: z.boolean(), // Art. 4(4): no exemption applies
})
function checkOfferingEligibility(data: OfferingData, today: Date): {
offerPermitted: boolean // Art. 4(1)(a): legal persons only
whitepaperRequired: boolean // Art. 4(1)(b)–(d) unless Art. 4(2) or 4(3)
exemptionBasis: string | null // e.g. "Art. 4(2)(b)"
earliestPublicationDate?: string // notified today + 20 working days (Art. 8(5))
limitedNetworkNotificationRequired: boolean // Art. 4(3), third subparagraph
violations: string[]
}Contenu et forme du livre blanc sur les crypto-actifs (+ annexe I)
Le livre blanc doit contenir les informations énumérées à l’art. 6(1) et à l’annexe I : offreur, émetteur et exploitant de la plate-forme de négociation, projet, offre, crypto-actif, droits et obligations, technologie sous-jacente, risques et principales incidences négatives sur le climat et l’environnement.
Obligations
- Informations sur l’offreur ou la personne qui demande l’admission à la négociation, l’émetteur et l’exploitant de la plate-forme de négociation (art. 6(1)(a) à (c) ; annexe I, parties A à C)
- Informations sur le projet de crypto-actif (art. 6(1)(d) ; annexe I, partie D)
- Informations sur l’offre au public ou l’admission à la négociation (art. 6(1)(e) ; annexe I, partie E)
- Informations sur le crypto-actif, les droits et obligations, la technologie sous-jacente (art. 6(1)(f) à (h) ; annexe I, parties F à H)
- Risques (art. 6(1)(i) ; annexe I, partie I)
- Principales incidences négatives du mécanisme de consensus sur le climat et l’environnement (art. 6(1)(j))
- Déclarations obligatoires, notamment que le livre blanc n’a pas été approuvé par une autorité compétente (art. 6(3) à (6))
- Résumé bref, en langage non technique (art. 6(7))
import { z } from "zod"
const LEI_REGEX = /^[A-Z0-9]{18}[0-9]{2}$/
const IssuerSchema = z.object({
legalName: z.string().min(1),
legalForm: z.string().min(1),
lei: z.string().regex(LEI_REGEX).optional(),
registeredAddress: z.object({
street: z.string(),
city: z.string(),
country: z.string().length(2),
postcode: z.string(),
}),
ncaNotified: z.string().length(2), // MS where NCA notified
managementBody: z.array(z.object({
name: z.string(),
role: z.string(),
dob: z.string().date().optional(),
})).min(1),
websiteUrl: z.string().url(),
financialStatements: z.string().url().optional(),
})
const ConsensusMechanismEnum = z.enum([
"PoS", "PoW", "dPoS", "PoA", "DAG", "other"
])
const WhitePaperSchema = z.object({
// Metadata
version: z.string().default("1.0"),
language: z.string().default("en"),
publicationDate: z.string().datetime().optional(),
documentHash: z.string().length(64).optional(), // SHA-256 hex
// Art. 6(1)(a)–(b); Annex I, Parts A–B
offeror: IssuerSchema,
issuer: IssuerSchema.optional(), // if different from the offeror
// Art. 6(1)(d); Annex I, Part D — Project
reasonForOffer: z.string().min(50),
useOfProceeds: z.string().min(50),
technologyDescription: z.string().min(100),
// Art. 6(1)(f)–(h); Annex I, Parts F–H — Crypto-asset, rights, technology
tokenName: z.string().min(1),
tokenSymbol: z.string().max(10),
tokenType: z.enum(["utility", "payment", "other_crypto_asset"]),
totalSupply: z.number().positive(),
issuanceSchedule: z.string(),
distributionPlan: z.array(z.object({
recipient: z.string(),
amount: z.number(),
vestingMonths: z.number().int().nonnegative(),
percentage: z.number().min(0).max(100),
})),
holderRights: z.array(z.string()).min(1),
holderObligations: z.array(z.string()),
transferable: z.boolean(),
dltProtocol: z.string(),
smartContractAddress: z.string().optional(),
smartContractAuditUrl: z.string().url().optional(),
// Art. 6(1)(i); Annex I, Part I — Risks
risks: z.object({
offerRisks: z.array(z.string()).min(1), // Annex I, Part I, point 1
issuerRisks: z.array(z.string()), // point 2 (if issuer ≠ offeror)
cryptoAssetRisks: z.array(z.string()).min(1), // point 3
projectRisks: z.array(z.string()).min(1), // point 4
technologyRisks: z.array(z.string()).min(1), // point 5, with mitigation measures
}),
// Art. 6(1)(j) — Climate and environment-related adverse impacts
consensusMechanism: ConsensusMechanismEnum,
climateImpactDisclosure: z.string().optional(),
annualEnergyKWh: z.number().nonnegative().optional(),
carbonFootprintTons: z.number().nonnegative().optional(),
})
// Compliance validator
function validateWhitePaper(wp: WhitePaper): {
valid: boolean
missingFields: string[]
warnings: string[]
} {
const missing: string[] = []
const warnings: string[] = []
// Art. 6(1)(j) applies whatever the consensus mechanism
if (!wp.climateImpactDisclosure) {
missing.push("Principal adverse impacts on the climate of the consensus mechanism (Art. 6(1)(j))")
}
if (!wp.smartContractAuditUrl) {
warnings.push("Smart contract audit URL recommended")
}
if (wp.distributionPlan.reduce((s, d) => s + d.percentage, 0) !== 100) {
missing.push("Distribution plan percentages must sum to 100%")
}
return { valid: missing.length === 0, missingFields: missing, warnings }
}Droit de rétractation (détenteurs de détail)
Les détenteurs de détail qui achètent directement auprès de l’offreur, ou auprès d’un PSCA qui place pour son compte, peuvent se rétracter dans un délai de 14 jours calendaires à compter de leur accord d’achat ; tous les paiements sont remboursés dans les 14 jours suivant la notification de la rétractation.
Obligations
- Droit de rétractation dans un délai de 14 JOURS CALENDAIRES à compter de la date de l’accord d’achat (art. 13(1))
- Sans frais, sans coûts et sans avoir à motiver sa décision (art. 13(1))
- Rembourser tous les paiements, y compris les frais, dans les 14 jours suivant l’information (art. 13(2))
- Informer sur le droit de rétractation dans le livre blanc (art. 13(3))
- Inapplicable lorsque les crypto-actifs étaient admis à la négociation avant l’achat (art. 13(4))
Seuils clés
Délai de rétractation: 14 JOURS CALENDAIRES à compter de l’accord d’achat
Délai de remboursement: 14 JOURS à compter de la notification de la rétractation
import { z } from "zod"
import { addDays } from "date-fns"
const PurchaseSchema = z.object({
purchaseId: z.string().uuid(),
purchaserId: z.string(),
offeringId: z.string(),
purchaseDate: z.string().datetime(),
whitepaperPublicationDate: z.string().datetime(),
amountEUR: z.number().positive(),
tokenAmount: z.number().positive(),
transactionCostEUR: z.number().nonnegative(),
isRetailPurchaser: z.boolean(),
admittedToTradingBeforePurchase: z.boolean(), // Art. 13(4) exclusion
})
function getWithdrawalDeadline(purchase: Purchase): Date {
// Art. 13(1): 14 calendar days from the agreement to purchase
return addDays(new Date(purchase.purchaseDate), 14)
}
function getRefundDeadline(withdrawalNoticeDate: Date): Date {
// 14 calendar days from date offeror is informed (Art. 13(2))
return addDays(withdrawalNoticeDate, 14)
}
const WithdrawalSchema = z.object({
withdrawalId: z.string().uuid(),
purchaseId: z.string().uuid(),
noticeDate: z.string().datetime(),
deadline: z.string().datetime(),
refundDue: z.number(), // amountEUR + transactionCostEUR
refundDeadline: z.string().datetime(),
status: z.enum(["open", "withdrawn", "deadline_passed", "refunded"]),
})Titre III — Jetons se référant à un ou des actifs (ART) — Art. 16–47
Exigences de fonds propres des émetteurs d’ART
Les émetteurs d’ART disposent à tout moment de fonds propres au moins égaux au plus élevé des montants suivants : 350 000 EUR, 2 % du montant moyen de la réserve d’actifs, ou un quart des frais généraux fixes de l’année précédente. Les ART d’importance significative appliquent 3 % (art. 45(5)).
Obligations
- Fonds propres ≥ MAX(350 000 EUR, 2 % de la réserve moyenne, ¼ des frais généraux fixes) (art. 35(1))
- ART d’importance significative (art. 45(5)) : 3 % au lieu de 2 %
- Fonds propres constitués des éléments et instruments de fonds propres de base de catégorie 1 (art. 35(2))
- Réserve moyenne = montants en fin de journée calendaire sur les six mois précédents (art. 35(1), deuxième alinéa)
- L’autorité compétente peut exiger jusqu’à 20 % de plus que le montant de l’art. 35(1)(b) (art. 35(3))
Seuils clés
Plancher minimal: 350 000 EUR
Pourcentage standard: 2 % de la réserve moyenne (6 mois)
Pourcentage pour un ART d’importance significative: 3 % de la réserve moyenne
Frais généraux fixes (art. 35(1)(c)): ¼ des frais généraux fixes de l’année précédente
import { z } from "zod"
// Daily reserve snapshot for 6-month rolling average
const ReserveSnapshotSchema = z.object({
date: z.string().date(),
totalReserveEUR: z.number().nonnegative(),
})
function calculateRequiredOwnFunds(
dailySnapshots: ReserveSnapshot[], // end of each calendar day, preceding 6 months
isSignificant: boolean,
fixedOverheadsPrecedingYearEUR: number
): {
averageReserve: number
percentageRequirement: number // 2% (Art. 35(1)(b)) or 3% (Art. 45(5))
calculatedRequirement: number // avg * pct
overheadRequirement: number // ¼ of fixed overheads (Art. 35(1)(c))
requiredOwnFunds: number // highest of the three limbs
breachThreshold: number // POC early warning at 110% (not a MiCA value)
} {
const avg = dailySnapshots.reduce((s, d) => s + d.totalReserveEUR, 0)
/ dailySnapshots.length
const pct = isSignificant ? 0.03 : 0.02
const calculated = avg * pct
const overheads = fixedOverheadsPrecedingYearEUR / 4
// Art. 35(1): at least the highest of (a) EUR 350,000, (b) and (c)
const required = Math.max(350_000, calculated, overheads)
return {
averageReserve: avg,
percentageRequirement: pct,
calculatedRequirement: calculated,
overheadRequirement: overheads,
requiredOwnFunds: required,
breachThreshold: required * 1.1,
}
}Réserve d’actifs — composition, conservation et investissement
Les émetteurs d’ART maintiennent une réserve d’actifs juridiquement et opérationnellement séparée, évaluée aux prix de marché et au moins égale aux créances des détenteurs, conservée par un dépositaire et investie uniquement dans des instruments très liquides. Les niveaux minimaux de dépôts sont fixés par les normes techniques de réglementation de l’ABE, dans les limites prévues par le règlement.
Obligations
- Constituer et maintenir une réserve couvrant les actifs de référence et les risques de liquidité liés au remboursement (art. 36(1))
- Ségrégation juridique et opérationnelle par rapport au patrimoine de l’émetteur (art. 36(2) et (3))
- Dépôts auprès d’établissements de crédit ≥ 30 % du montant référencé dans chaque monnaie officielle (art. 36(4)(d)) ; ≥ 60 % pour les ART d’importance significative (art. 45(7)(b))
- Valeur totale de la réserve ≥ créances des détenteurs, aux prix de marché (art. 36(7))
- Conservation par un PSCA, un établissement de crédit ou une entreprise d’investissement dans les 5 jours ouvrables suivant l’émission (art. 37(3))
- Éviter la concentration des dépositaires et des actifs de réserve (art. 37(1)(d) et (e))
- Investir uniquement dans des instruments financiers très liquides à risque de marché, de crédit et de concentration minimal (art. 38(1))
- Audit indépendant tous les six mois (art. 36(9)) ; publication de la composition de la réserve au moins une fois par mois (art. 30(1))
Seuils clés
Dépôts minimaux (art. 36(4)(d)): ≥ 30 % par monnaie de référence
Dépôts minimaux, ART d’importance significative (art. 45(7)(b)): ≥ 60 % par monnaie de référence
Délai de conservation (art. 37(3)): 5 JOURS OUVRABLES après l’émission
Audit indépendant (art. 36(9)): Tous les 6 mois
import { z } from "zod"
// Asset categories modelled by the POC. Art. 38(1) only allows highly liquid
// financial instruments specified by EBA RTS; the list below is a POC assumption.
const AssetCategoryEnum = z.enum([
"deposit_credit_institution", // counts towards Art. 36(4)(d) / 45(7)(b)
"ucits_money_market_fund",
"eu_government_bond",
"central_bank_deposit", // not a deposit in a credit institution
])
const ReserveAssetSchema = z.object({
assetId: z.string().uuid(),
category: AssetCategoryEnum,
institution: z.string(),
currency: z.string().length(3), // ISO 4217
marketValueEUR: z.number().nonnegative(), // market prices (Art. 36(7))
custodian: z.string(), // Art. 37(3): CASP, credit institution or investment firm
verifiedAt: z.string().datetime(),
})
const ReserveStateSchema = z.object({
snapshotDate: z.string().datetime(),
referenceCurrency: z.string().length(3),
holderClaimsEUR: z.number().nonnegative(), // aggregate claims of holders (Art. 36(7))
assets: z.array(ReserveAssetSchema),
})
function validateReserveCompliance(state: ReserveState, isSignificant: boolean) {
const total = state.assets.reduce((s, a) => s + a.marketValueEUR, 0)
const deposits = state.assets
.filter(a => a.category === "deposit_credit_institution" && a.currency === state.referenceCurrency)
.reduce((s, a) => s + a.marketValueEUR, 0)
// Floors: ≥ 30% (Art. 36(4)(d)), ≥ 60% for significant ARTs (Art. 45(7)(b));
// the exact amounts are set by EBA RTS within these floors
const minDeposits = isSignificant ? 0.60 : 0.30
// Art. 37(1)(d)–(e): avoid concentration; 10% is an illustrative POC policy limit
const byInstitution: Record<string, number> = {}
state.assets.forEach(a => {
byInstitution[a.institution] = (byInstitution[a.institution] || 0) + a.marketValueEUR
})
const maxConcentration = Math.max(...Object.values(byInstitution)) / total
return {
deposits_min: { pass: deposits / total >= minDeposits, actual: deposits / total, required: minDeposits },
concentration: { pass: maxConcentration <= 0.10, maxSingleInstitution: maxConcentration },
// Art. 36(7): aggregate value at market prices ≥ aggregate claims of holders
full_coverage: { pass: total >= state.holderClaimsEUR, coverageRatio: total / state.holderClaimsEUR },
}
}ART — droit de remboursement et interdiction d’accorder des intérêts
Les détenteurs disposent à tout moment d’un droit de remboursement, payé en fonds correspondant à la valeur de marché des actifs de référence ou par la livraison de ces actifs, selon une politique de remboursement fixée par l’émetteur. Le remboursement est gratuit et aucun intérêt ne peut être accordé.
Obligations
- Droit de remboursement À TOUT MOMENT à l’égard de l’émetteur (art. 39(1))
- Remboursement en fonds à la valeur de marché des actifs de référence, ou par livraison des actifs (art. 39(2))
- La politique de remboursement fixe les conditions, périodes et délais (art. 39(2)(a)) — MiCA ne fixe aucun délai
- Le remboursement n’est soumis à aucuns frais (art. 39(3))
- Les émetteurs et les PSCA n’accordent pas d’intérêts, y compris sous forme d’avantages liés à la durée de détention (art. 40)
Seuils clés
Délai de remboursement: Fixé par la politique de remboursement de l’émetteur (art. 39(2)(a))
Frais de remboursement: Aucuns (art. 39(3))
Intérêts / rendement: INTERDITS (art. 40)
import { z } from "zod"
import { addBusinessDays } from "date-fns"
const ARTRedemptionSchema = z.object({
requestId: z.string().uuid(),
holderId: z.string(),
tokenAmount: z.number().positive(),
requestedAt: z.string().datetime(),
mode: z.enum(["reference_assets", "fiat_equivalent"]),
destinationAccount: z.string(),
marketValueEUR: z.number().optional(),
feeEUR: z.number().nonnegative(), // Art. 39(3): 0, except a recovery-plan
// liquidity fee under Art. 46(1)(a)
netAmountEUR: z.number().optional(),
workingDayDeadline: z.string().datetime(),
status: z.enum([
"pending",
"processing",
"completed",
"rejected",
"deadline_breached"
]),
})
function getARTRedemptionDeadline(requestDate: Date, policyWorkingDays: number): Date {
// Timeframe defined in the issuer's redemption policy (Art. 39(2)(a))
return addBusinessDays(requestDate, policyWorkingDays)
}
// Art. 40(3): any remuneration or benefit related to the holding period is interest,
// including net compensation or discounts with an equivalent effect
const InterestProhibitionSchema = z.object({
tokenId: z.string(),
holderAddress: z.string(),
interestPaid: z.literal(0),
holdingPeriodBenefits: z.literal(0),
equivalentDiscounts: z.literal(0),
})Titre IV — Jetons de monnaie électronique (EMT) — Art. 48–58
Émission et possibilité de remboursement de jetons de monnaie électronique
Les EMT sont émis au pair dès réception des fonds et doivent être remboursés à tout moment et au pair, en fonds autres que de la monnaie électronique, à la demande du détenteur. Le remboursement est gratuit.
Obligations
- Les détenteurs disposent d’une créance sur l’émetteur (art. 49(2))
- Émission AU PAIR dès réception des fonds (art. 49(3))
- Remboursement À TOUT MOMENT et AU PAIR, en fonds autres que de la monnaie électronique (art. 49(4))
- Indiquer clairement les conditions de remboursement dans le livre blanc (art. 49(5))
- Le remboursement n’est soumis à aucuns frais (art. 49(6))
- Au moins 30 % des fonds reçus déposés sur des comptes distincts auprès d’établissements de crédit (art. 54(a))
Seuils clés
Délai de remboursement: À tout moment (art. 49(4)) — MiCA ne fixe aucun délai de traitement
Taux de change: Au pair (1:1)
Frais: Aucuns (art. 49(6))
Dépôts auprès d’établissements de crédit (art. 54(a)): ≥ 30 % des fonds reçus
import { z } from "zod"
import { addBusinessDays, isAfter } from "date-fns"
const EMTRedemptionSchema = z.object({
requestId: z.string().uuid(),
holderId: z.string(),
tokenAmount: z.number().positive(),
referenceCurrency: z.string().length(3),
requestedAt: z.string().datetime(),
// PAR VALUE: 1 token = 1 unit of fiat
parValuePerToken: z.literal(1),
totalFiatAmount: z.number(), // = tokenAmount * 1
feeEUR: z.number().min(0).max(0), // Must be 0
netAmount: z.number(), // = totalFiatAmount (no deduction)
destinationIBAN: z.string().regex(/^[A-Z]{2}[0-9]{2}[A-Z0-9]{4,30}$/),
deadline: z.string().datetime(), // issuer SLA (MiCA: "at any time")
status: z.enum([
"pending",
"processing",
"completed",
"sla_breached"
]),
})
function createEMTRedemption(
tokenAmount: number,
currency: string,
requestDate: Date
): EMTRedemption {
const deadline = addBusinessDays(requestDate, 1) // issuer policy, not a MiCA figure
return {
requestId: crypto.randomUUID(),
tokenAmount,
referenceCurrency: currency,
parValuePerToken: 1,
totalFiatAmount: tokenAmount * 1, // Par value
feeEUR: 0,
netAmount: tokenAmount, // Full amount, no fees
deadline: deadline.toISOString(),
status: "pending",
}
}
function checkSLABreach(redemption: EMTRedemption): boolean {
return redemption.status === "pending" &&
isAfter(new Date(), new Date(redemption.deadline))
}Interdiction de verser des intérêts
Ni les émetteurs d’EMT ni les PSCA fournissant des services liés aux EMT ne peuvent accorder d’intérêts. Toute rémunération ou tout avantage lié à la durée de détention est considéré comme un intérêt.
Obligations
- INTERDIT : intérêts accordés par l’émetteur sur des EMT (art. 50(1))
- INTERDIT : intérêts accordés par un PSCA fournissant des services liés aux EMT (art. 50(2))
- Toute rémunération ou tout avantage lié à la durée de détention est traité comme un intérêt (art. 50(3))
- Y compris les compensations nettes ou remises ayant un effet équivalent, de l’émetteur ou de tiers (art. 50(3))
Seuils clés
Intérêts / rendement: INTERDITS
import { z } from "zod"
// Art. 50(3): any remuneration or other benefit related to the length of time
// a holder holds the EMT is interest (incl. equivalent net compensation or discounts)
const EMTYieldComplianceSchema = z.object({
tokenId: z.string(),
periodStart: z.string().date(),
periodEnd: z.string().date(),
interestPaid: z.literal(0), // Art. 50(1) issuer, Art. 50(2) CASP
holdingPeriodRewards: z.literal(0), // Art. 50(3)
equivalentCompensation: z.literal(0), // Art. 50(3): from the issuer or third parties
})
// Compliance check function
function validateArt50Compliance(
tokenId: string,
transactions: Transaction[]
): {
compliant: boolean
violations: string[]
} {
const violations: string[] = []
// Payments to holders computed from how long they held the EMT
const yieldTransactions = transactions.filter(tx =>
tx.type === "interest" ||
(tx.type === "reward" && tx.basedOnHoldingPeriod) ||
(tx.type === "discount" && tx.basedOnHoldingPeriod)
)
if (yieldTransactions.length > 0) {
violations.push(
`Art. 50 violation: ${yieldTransactions.length} yield-bearing transactions detected`
)
}
return {
compliant: violations.length === 0,
violations,
}
}Titre V — Prestataires de services sur crypto-actifs (PSCA) — Art. 59–85
Exigences prudentielles des PSCA
Les PSCA disposent à tout moment de garanties prudentielles au moins égales au plus élevé des montants suivants : le capital minimal permanent de l’annexe IV pour leurs services, ou un quart des frais généraux fixes de l’année précédente — sous forme de fonds propres, d’une police d’assurance, ou des deux.
Obligations
- Garanties prudentielles ≥ MAX(minimum de l’annexe IV, ¼ des frais généraux fixes de l’année précédente) (art. 67(1))
- Catégorie 1 (50 000 EUR) : conseil, gestion de portefeuille, réception et transmission, exécution, placement, transfert
- Catégorie 2 (125 000 EUR) : services de catégorie 1 + conservation, échange contre des fonds ou d’autres crypto-actifs
- Catégorie 3 (150 000 EUR) : services de catégorie 2 + exploitation d’une plate-forme de négociation
- Forme : fonds propres CET1, police d’assurance, ou une combinaison des deux (art. 67(4))
Seuils clés
Minimum catégorie 1 (annexe IV): 50 000 EUR
Minimum catégorie 2 (annexe IV): 125 000 EUR
Minimum catégorie 3 (annexe IV): 150 000 EUR
Calcul alternatif (art. 67(1)(b)): ¼ des frais généraux fixes de l’année précédente
import { z } from "zod"
const CASPServiceEnum = z.enum([
// Class 1 — EUR 50,000 (Annex IV)
"advice",
"portfolio_management",
"reception_transmission",
"execution",
"placing",
"transfer",
// Class 2 — EUR 125,000
"exchange_fiat",
"exchange_crypto",
"custody",
// Class 3 — EUR 150,000
"trading_platform",
])
// Annex IV — permanent minimum capital (Art. 67(1)(a))
const TIER_MINIMUMS = {
tier1: 50_000,
tier2: 125_000,
tier3: 150_000,
} as const
const SERVICE_TIERS: Record<z.infer<typeof CASPServiceEnum>, keyof typeof TIER_MINIMUMS> = {
advice: "tier1",
reception_transmission: "tier1",
execution: "tier1",
placing: "tier1",
transfer: "tier1",
portfolio_management: "tier1",
exchange_fiat: "tier2",
exchange_crypto: "tier2",
custody: "tier2",
trading_platform: "tier3",
}
function calculateCASPOwnFunds(
services: z.infer<typeof CASPServiceEnum>[],
annualFixedOverhead: number
): {
highestTier: keyof typeof TIER_MINIMUMS
tierMinimum: number
quarterOverhead: number
requiredOwnFunds: number
} {
// Determine highest tier from selected services
const tiers = services.map(s => SERVICE_TIERS[s])
const highestTier = tiers.includes("tier3") ? "tier3"
: tiers.includes("tier2") ? "tier2" : "tier1"
const tierMinimum = TIER_MINIMUMS[highestTier]
const quarterOverhead = annualFixedOverhead / 4 // Art. 67(1)(b)
return {
highestTier,
tierMinimum,
quarterOverhead,
requiredOwnFunds: Math.max(tierMinimum, quarterOverhead),
}
}Procédures de traitement des réclamations
Les PSCA mettent en place des procédures de réclamation efficaces, transparentes et gratuites, tiennent un registre de toutes les réclamations et les instruisent de manière diligente et équitable. MiCA ne fixe lui-même aucun délai ; les normes techniques de réglementation adoptées au titre de l’art. 71(5) le plafonnent à 2 mois à compter de la réception de la réclamation (art. 6(2) du règlement délégué (UE) 2025/294).
Obligations
- Établir et publier des procédures de réclamation efficaces et transparentes (art. 71(1))
- GRATUITES pour les clients (art. 71(2))
- Informer les clients, fournir un modèle de réclamation, enregistrer toutes les réclamations et les mesures prises (art. 71(3))
- Instruire de manière diligente et équitable ; communiquer le résultat dans un délai raisonnable (art. 71(4))
- Communiquer la décision dans les 2 MOIS suivant la réception de la réclamation (art. 6(2) du règlement délégué (UE) 2025/294)
- Dans les situations exceptionnelles où ce n’est pas possible, informer le client des raisons du retard et de la date de la décision (art. 6(3) du règlement délégué (UE) 2025/294)
- Conserver les enregistrements des services et activités pendant 5 ANS (art. 68(9))
Seuils clés
Délai de réponse: « Délai raisonnable » (art. 71(4)) ; aucun délai fixe dans MiCA lui-même
Délai maximal de décision (NTR réclamations): 2 MOIS à compter de la réception (art. 6(2) du règlement délégué (UE) 2025/294)
SLA indicatif (POC): 5 / 15 / 35 JOURS OUVRABLES
Conservation (art. 68(9)): 5 ANS
import { z } from "zod"
import { addBusinessDays, addMonths, differenceInBusinessDays, isAfter } from "date-fns"
const ComplaintSchema = z.object({
complaintId: z.string().uuid(),
clientId: z.string(),
submittedAt: z.string().datetime(),
subject: z.string().min(10),
description: z.string().min(50),
// SLA tracking (internal policy — MiCA requires "timely" handling)
ackDeadline: z.string().datetime(), // +5 business days
ackSentAt: z.string().datetime().optional(),
responseDeadline: z.string().datetime(), // +15 business days
extendedDeadline: z.string().datetime().optional(), // +35 if extended, capped at decisionDeadline
// Regulatory cap: decision within 2 months of receipt (RTS 2025/294)
decisionDeadline: z.string().datetime(),
extensionReason: z.string().optional(),
responseSentAt: z.string().datetime().optional(),
// Status
status: z.enum([
"received",
"acknowledged",
"under_review",
"extended",
"resolved",
"sla_breached"
]),
// Retention: 5 years (Art. 68(9))
retentionUntil: z.string().datetime().optional(),
})
function createComplaint(clientId: string, subject: string, description: string): Complaint {
const now = new Date()
return {
complaintId: crypto.randomUUID(),
clientId,
submittedAt: now.toISOString(),
subject,
description,
ackDeadline: addBusinessDays(now, 5).toISOString(),
responseDeadline: addBusinessDays(now, 15).toISOString(),
decisionDeadline: addMonths(now, 2).toISOString(),
status: "received",
}
}
function checkSLAStatus(complaint: Complaint): {
ackOverdue: boolean
responseOverdue: boolean
decisionOverdue: boolean // inform the client of the reasons and the decision date
daysUntilAck: number
daysUntilResponse: number
} {
const now = new Date()
const ackDeadline = new Date(complaint.ackDeadline)
const responseDeadline = new Date(
complaint.extendedDeadline || complaint.responseDeadline
)
return {
ackOverdue: !complaint.ackSentAt && isAfter(now, ackDeadline),
responseOverdue: !complaint.responseSentAt && isAfter(now, responseDeadline),
decisionOverdue:
!complaint.responseSentAt && isAfter(now, new Date(complaint.decisionDeadline)),
daysUntilAck: differenceInBusinessDays(ackDeadline, now),
daysUntilResponse: differenceInBusinessDays(responseDeadline, now),
}
}Évaluation de l’adéquation (conseil et gestion de portefeuille)
Les PSCA qui fournissent des conseils ou une gestion de portefeuille évaluent l’adéquation en fonction des connaissances et de l’expérience du client, de ses objectifs d’investissement, y compris sa tolérance au risque, et de sa situation financière, y compris sa capacité à supporter des pertes.
Obligations
- Obtenir les informations nécessaires avant le service (art. 81(1) et (8)) :
- (a) Connaissances et expérience en matière d’investissement, y compris en crypto-actifs
- (b) Objectifs d’investissement, y compris la tolérance au risque
- (c) Situation financière, y compris la capacité à supporter des pertes
- Ne pas recommander ni gérer lorsque le service n’est pas adéquat (art. 81(11))
- Réexaminer l’adéquation pour chaque client au moins tous les deux ans (art. 81(12))
- Fournir un rapport d’adéquation sous format électronique pour le conseil (art. 81(13))
import { z } from "zod"
const KnowledgeAssessmentSchema = z.object({
yearsOfExperience: z.number().int().min(0),
cryptoTypesTraded: z.array(z.enum([
"bitcoin", "ethereum", "stablecoins", "defi", "nfts", "other"
])),
understandsVolatility: z.boolean(),
understandsPrivateKeys: z.boolean(),
previousLosses: z.boolean(),
selfAssessedLevel: z.enum(["novice", "intermediate", "advanced"]),
})
const FinancialSituationSchema = z.object({
annualIncomeEUR: z.number().nonnegative(),
liquidAssetsEUR: z.number().nonnegative(),
totalLiabilitiesEUR: z.number().nonnegative(),
monthlyExpensesEUR: z.number().nonnegative(),
emergencyFundMonths: z.number().int().min(0),
dependents: z.number().int().min(0),
})
const InvestmentObjectivesSchema = z.object({
purpose: z.enum([
"capital_growth",
"income_generation",
"speculation",
"hedging",
"diversification"
]),
timeHorizon: z.enum([
"less_than_1_year",
"1_to_3_years",
"3_to_5_years",
"more_than_5_years"
]),
riskTolerance: z.enum(["low", "medium", "high"]),
maxPortfolioAllocation: z.number().min(0).max(100),
})
const LossCapacitySchema = z.object({
maxAcceptableLossPct: z.number().min(0).max(100),
canAffordTotalLoss: z.boolean(),
impactOnLifestyle: z.enum(["none", "minor", "significant", "severe"]),
})
const SuitabilityAssessmentSchema = z.object({
assessmentId: z.string().uuid(),
clientId: z.string(),
assessedAt: z.string().datetime(),
knowledge: KnowledgeAssessmentSchema,
financialSituation: FinancialSituationSchema,
objectives: InvestmentObjectivesSchema,
lossCapacity: LossCapacitySchema,
suitabilityScore: z.number().min(0).max(100),
recommendation: z.enum(["suitable", "potentially_suitable", "not_suitable"]),
warnings: z.array(z.string()),
nextReviewDue: z.string().datetime(), // At least every two years (Art. 81(12))
})
function calculateSuitabilityScore(assessment: SuitabilityAssessment): number {
let score = 50 // Base score
// Knowledge adjustments
if (assessment.knowledge.yearsOfExperience >= 3) score += 15
if (assessment.knowledge.understandsVolatility) score += 10
// Financial health adjustments
const netWorth = assessment.financialSituation.liquidAssetsEUR
- assessment.financialSituation.totalLiabilitiesEUR
if (netWorth > 100_000) score += 10
if (assessment.financialSituation.emergencyFundMonths >= 6) score += 10
// Risk tolerance alignment
if (assessment.objectives.riskTolerance === "low") score -= 15
if (assessment.lossCapacity.canAffordTotalLoss) score += 10
return Math.max(0, Math.min(100, score))
}Titre VI — Prévention et interdiction des abus de marché — Art. 86–92
Interdiction des opérations d’initiés
Nul ne peut utiliser une information privilégiée pour acquérir ou céder des crypto-actifs, ni recommander ou inciter autrui à le faire. L’information privilégiée est définie à l’art. 87 ; la divulgation illicite est interdite par l’art. 90.
Obligations
- INTERDIT : acquérir ou céder des crypto-actifs en utilisant une information privilégiée (art. 89)
- INTERDIT : recommander ou inciter autrui à effectuer une opération sur la base d’une information privilégiée (art. 89)
- INTERDIT : divulguer illicitement une information privilégiée hors de l’exercice normal de ses fonctions (art. 90)
- Information privilégiée = précise, non publique, susceptible d’influencer sensiblement les prix (art. 87)
- Les émetteurs rendent publiques les informations privilégiées dès que possible (art. 88)
import { z } from "zod"
const InsideInformationSchema = z.object({
informationId: z.string().uuid(),
cryptoAssetId: z.string(),
description: z.string().min(20),
// Criteria for "inside information"
isPrecise: z.boolean(), // Specific enough to conclude price effect
isNonPublic: z.boolean(), // Not yet disclosed to market
isPriceSensitive: z.boolean(), // Would likely affect price if disclosed
discoveredAt: z.string().datetime(),
disclosedAt: z.string().datetime().optional(),
source: z.enum([
"issuer_employee",
"casp_employee",
"advisor",
"third_party",
"technical_discovery"
]),
})
const InsiderTradeCheckSchema = z.object({
tradeId: z.string().uuid(),
traderId: z.string(),
cryptoAssetId: z.string(),
tradeTimestamp: z.string().datetime(),
direction: z.enum(["buy", "sell"]),
amount: z.number().positive(),
// Compliance fields
hadInsideInfo: z.boolean(),
insideInfoIds: z.array(z.string().uuid()),
complianceCheck: z.enum([
"clear", // No inside info
"flagged", // Potential violation
"confirmed_breach" // Verified Art. 89 breach
]),
})
function checkInsiderTrade(
trade: Trade,
activeInsideInfo: InsideInformation[]
): {
violation: boolean
matchingInfo: InsideInformation[]
recommendedAction: string
} {
const matching = activeInsideInfo.filter(info =>
info.cryptoAssetId === trade.cryptoAssetId &&
info.isPrecise && info.isNonPublic && info.isPriceSensitive &&
!info.disclosedAt // Still non-public
)
return {
violation: matching.length > 0,
matchingInfo: matching,
recommendedAction: matching.length > 0
? "HALT_TRADE_AND_REPORT_TO_NCA"
: "PROCEED",
}
}Interdiction des manipulations de marché
Nul ne peut effectuer ou tenter d’effectuer des manipulations de marché : signaux faux ou trompeurs, fixation des prix à un niveau artificiel, procédés fictifs ou trompeurs, diffusion de fausses informations (art. 91(2) et (3)).
Obligations
- INTERDIT : transactions ou ordres donnant des signaux faux ou trompeurs, p. ex. transactions fictives (wash trading) (art. 91(2)(a))
- INTERDIT : fixer les prix à un niveau anormal ou artificiel (art. 91(2)(a))
- INTERDIT : procédés fictifs ou toute autre forme de tromperie ou d’artifice (art. 91(2)(b))
- INTERDIT : passer, annuler ou modifier des ordres pour créer de faux signaux, p. ex. layering/spoofing (art. 91(3)(b))
- INTERDIT : diffuser des informations fausses ou trompeuses, y compris des rumeurs (art. 91(2)(c))
- Les personnes qui organisent ou exécutent à titre professionnel des transactions détectent et déclarent les soupçons sans délai (art. 92(1))
import { z } from "zod"
const ManipulationTypeEnum = z.enum([
"wash_trading",
"layering",
"spoofing",
"pump_and_dump",
"coordinated_trading",
"false_information",
])
const SuspiciousPatternSchema = z.object({
patternId: z.string().uuid(),
detectedAt: z.string().datetime(),
cryptoAssetId: z.string(),
manipulationType: ManipulationTypeEnum,
confidence: z.number().min(0).max(1),
indicators: z.array(z.string()),
involvedAddresses: z.array(z.string()),
volumeAffected: z.number().nonnegative(),
priceImpactPct: z.number(),
status: z.enum([
"detected",
"under_review",
"confirmed",
"dismissed",
"reported_to_nca"
]),
})
// Wash trading detection
function detectWashTrading(
trades: Trade[],
timeWindowMinutes: number = 60
): SuspiciousPattern[] {
const patterns: SuspiciousPattern[] = []
// Group trades by address pairs
const addressPairs = new Map<string, Trade[]>()
trades.forEach(trade => {
const key = [trade.fromAddress, trade.toAddress].sort().join("|")
if (!addressPairs.has(key)) addressPairs.set(key, [])
addressPairs.get(key)!.push(trade)
})
// Check for circular patterns
addressPairs.forEach((pairTrades, key) => {
if (pairTrades.length >= 3) {
const buys = pairTrades.filter(t => t.direction === "buy")
const sells = pairTrades.filter(t => t.direction === "sell")
if (buys.length > 0 && sells.length > 0) {
patterns.push({
patternId: crypto.randomUUID(),
detectedAt: new Date().toISOString(),
cryptoAssetId: pairTrades[0].cryptoAssetId,
manipulationType: "wash_trading",
confidence: 0.8,
indicators: [
"Circular trading between same addresses",
`${pairTrades.length} trades in ${timeWindowMinutes} minutes`,
],
involvedAddresses: key.split("|"),
volumeAffected: pairTrades.reduce((s, t) => s + t.amount, 0),
priceImpactPct: 0,
status: "detected",
})
}
}
})
return patterns
}