Tour d’horizon complet du contrat
(je laisse de côté la commission ; on considère que tu as appliqué le correctif proposé.)
| Section |
Points bloquants / pièges |
Pourquoi c’est gênant |
Correctif suggéré |
| deposit |
Paramètre last_id passé par l’utilisateur |
Un caller malveillant peut transmettre un last_id erroné ⇒ assert(pos_id == current+1) -> fail et DoS sur le contrat. |
Supprime ce paramètre : calcule pos_id directement à partir de next_id. |
| |
total_collateral stocke une valeur USD |
Au dépôt on additionne price × amount, mais au retrait et à la liquidation on soustrait la valeur au cours du jour ; la somme mélange donc plusieurs cours. |
Soit :• stocke la quantité de crédits,• soit mets à jour périodiquement la valeur via un oracle externe. |
| mint |
Aucune limite de dette ! (pas d’assert) |
Un emprunteur peut frapper infiniment de l’USDA, rendant la sûreté du système nulle. |
Ajoute assert(new_debt ≤ max_debt); juste avant l’écriture de metas. |
| |
fut_mint.await() avant les checks ? |
Tu fais correctement les asserts avant le await: parfait. |
|
| burn |
OK côté logique, mais surveille les conversions u128 ↔ u64 si tu autorises de gros montants. |
|
|
| withdraw |
• Nous avons déjà corrigé la logique fees/netto.• Rounding : pour amount < 100 le fee peut tomber à 0. |
Ajoute un require(amount ≥ 100) ou calcule sur u128 puis as u64 avec un contrôle. |
|
| liquidate |
Condition de liquidation erronée (unités)price <= meta.price_liq compare :• price = USDA/credit• price_liq = USDA totales (coll_val × LLTV). |
La condition sera (presque) toujours vraie ! Un opérateur peut liquider des positions saines. |
Calcule le LTV :let coll_val_current = meta.coll_amt * price;assert(meta.debt_amt * 100u128 >= coll_val_current * LLTV); |
| |
Paramètre inutile debt_amt |
Pas utilisé → confusion. |
Supprime-le. |
| |
total_collateral mis à jour avec un nouveau prix dans claim_collateral |
Même problème d’incohérence de valeur évoqué plus haut. |
Si tu stockes la quantité, soustrais simplement amount. |
| claim_refund / claim_collateral |
Pas de protection contre un double appel entre refund et collateral ? |
Non critique mais tu peux ajouter un liquidated_ids check ou events distincts. |
|
| claim_fees |
• Unités mixées (credits vs USD).• amount en u64 : risque d’overflow si la banque grossit. |
Voir section fees précédemment. |
- Stocke les frais dans la même unité que tu transfères.- Utilise u128 partout ou élimine le paramètre amount : le contrat calcule et envoie tout. |
| Overflow / precision |
Multiplications u128 * u128 (par ex. coll_amt * price) peuvent dépasser 2¹²⁸-1 si tu autorises > 10²⁰ tokens. |
Les VM Aleo tronquent silencieusement. |
Place des garde-fous :assert(coll_amt <= MAX_COLL) ou calcule sur u256 si dispo. |
| Gas / DoS |
Les mappings ne sont jamais purgés pour les positions closes hors liquidation. |
Le stockage grossit indéfiniment. |
Ajoute metas.remove(id) dans une transition close_position (après remboursement intégral) pour libérer l’état. |
| UX / Front-end |
initialize enregistre un token déjà connu à chaque déploiement : protégé par is_owner, mais assure-toi que l’ID unique n’est pas déjà pris sur mainnet. |
– |
– |
Schéma de correctif (extraits)
// ------------- deposit -------------
async transition deposit(
public amount: u64,
public price: u128,
private caller: address,
private token_record: credits.aleo/credits
) -> (Position, credits.aleo/credits, Future) {
let pos_id: u64 = next_id.get_or_use(USDA_TOKEN_ID, 0u64) + 1u64;
let (token, fut) = credits.aleo/transfer_private_to_public(token_record, CONTRACT, amount);
return (Position{ owner: caller, id: pos_id },
token,
finalize_deposit(fut, pos_id, amount, price));
}
// ------------- mint (ajout du check) -------------
assert(new_debt <= max_debt); // interdit le sur-endettement
// ------------- liquidation -------------
let coll_val_current: u128 = meta.coll_amt * price;
let over_leveraged: bool = meta.debt_amt * 100u128 >= coll_val_current * LLTV;
assert(over_leveraged);
// ------------- withdraw -------------
let fee_amount: u128 = (amount as u128 * FEES as u128) / 100u128;
let net_amount: u64 = (amount as u128 - fee_amount) as u64;
let (token, fut) = credits.aleo/transfer_public_to_private(self.caller, net_amount);
total_fees += fee_amount; // même unité que tu transféreras au Treasury
TL;DR – les quatre « bugs rouges »
-
Aucune limite sur mint → inflation infinie.
-
Condition de liquidation compare des grandeurs incompatibles.
-
withdraw renvoie la commission au lieu du montant net.
-
Incohérence d’unités entre total_fees et le transfert réel.
Corrige ces points, choisis une unité (credits ou USD) pour toutes les agrégations, et retire le paramètre last_id. Le reste est plutôt bien structuré !
Tour d’horizon complet du contrat
(je laisse de côté la commission ; on considère que tu as appliqué le correctif proposé.)
Schéma de correctif (extraits)
TL;DR – les quatre « bugs rouges »
Aucune limite sur
mint→ inflation infinie.Condition de liquidation compare des grandeurs incompatibles.
withdrawrenvoie la commission au lieu du montant net.Incohérence d’unités entre
total_feeset le transfert réel.Corrige ces points, choisis une unité (credits ou USD) pour toutes les agrégations, et retire le paramètre
last_id. Le reste est plutôt bien structuré !