Skip to content

RAPPORT - O3 #4

@herissondev

Description

@herissondev

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 »

  1. Aucune limite sur mint → inflation infinie.

  2. Condition de liquidation compare des grandeurs incompatibles.

  3. withdraw renvoie la commission au lieu du montant net.

  4. 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é !

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions