Aller au contenu

Système de permission d'actifs

Le Système de Permission d'Actifs (APS) est l'une des fonctionnalités uniques de Ralph. Il stipule explicitement le flux des actifs au niveau du code, offrant ainsi confiance aux développeurs et aux utilisateurs des contrats intelligents que tous les transferts d'actifs se déroulent comme prévu. Associé au modèle UTXOs, il offre également une expérience utilisateur plus simple et plus sécurisée en éliminant les risques d'approbation de jetons dans des systèmes tels que l'EVM.

Alephium utilise le modèle sUTXO où les actifs, y compris le jeton natif ALPH et d'autres jetons, sont gérés par des UTXOs tandis que les contrats intelligents et leurs états sont gérés à l'aide d'un modèle basé sur les comptes.

Cela a quelques implications :

  1. Les transferts d'actifs simples entre utilisateurs ne nécessitent que des UTXOs, qui ont fait leurs preuves en matière de sécurité dans la gestion des actifs. Aucun contrat intelligent n'est impliqué ici.
  2. Lorsque les contrats intelligents ont besoin de transférer des actifs au nom des propriétaires, aucune transaction d'approbation séparée n'est requise. L'approbation est implicite dans le modèle UTXO: si l'entrée contenant un jeton particulier est autorisée à être dépensée dans la transaction, alors le propriétaire a déjà donné son consentement à l'utilisation de ce jeton dans le contexte de cette transaction, ce qui signifie que les contrats intelligents invoqués dans la même transaction peuvent potentiellement transférer le jeton.

Maintenant, la question est : dans la deuxième situation, comment pouvons-nous nous assurer que les actifs implicitement approuvés dans la transaction en utilisant le modèle UTXO peuvent être gérés en toute sécurité par les contrats intelligents ? La réponse est le Système de Permission d'Actifs (APS) de Ralph.

Flux des Actifs

Pour interagir avec les contrats intelligents dans Alephium, une transaction doit exécuter un TxScript. Dans l'exemple de transaction suivant, il y a deux entrées, une sortie fixe et un TxScript:

Text Only
                  ----------------
                  |              |
                  |              |
   1 Jeton A      |              |   1 ALPH (sortie fixe)
================> |              | ========================>
   6.1 ALPHs      |  <TxScript>  |   ??? (sortie générée)
================> |              | ========================>
                  |              | 
                  |              | 
                  |              |
                  ----------------

Deux choses valent la peine d'être notées ici :

  1. Bien qu'il n'y ait qu'une seule sortie fixe, il y aura plus de sorties générées pour cette transaction. Les sorties générées dépendent du résultat de l'exécution du TxScript.
  2. Les actifs totaux disponibles pour le TxScript (y compris les contrats intelligents qu'il invoque) sont de 5.1 ALPHs et de 1 Token A, car nous devons soustraire le 1 ALPH en sortie fixe.

Disons que le TxScript ressemble à ceci :

Rust
TxScript ListNFT(
    tokenAId: ByteVec,
    price: U256,
    marketPlace: NFTMarketPlace
) {
    let listingFee = marketPlace.getListingFee()
    let minimalAlphInContract = 1 alph
    let approvedAlphAmount = listingFee + minimalAlphInContract

    marketPlace.listNFT{callerAddress!() -> ALPH: approvedAlphAmount, tokenAId: 1}(tokenAId, price)
}

Comme vous l'avez peut-être deviné, Token A est un jeton NFT et le but du TxScript ci-dessus est de le mettre en vente via un contrat intelligent de place de marché.

La ligne de code suivante est particulièrement intéressante:

Rust
marketPlace.listNFT{callerAddress!() -> ALPH: approvedAlphAmount, tokenAId: 1}(tokenAId, price)

Le code à l'intérieur des accolades approuve explicitement que approvedAlphAmount ALPH et 1 token A peuvent être dépensés dans la fonction marketPlace.listNFT, même si le montant total des actifs disponibles pour le TxScript est de 5.1 et 1 pour ALPH et token A respectivement.

Les scénarios suivants pourraient se produire:

  1. Si approvedAlphAmount s'avère être plus de 5.1 ALPH, alors la transaction échoue avec une erreur NotEnoughBalance.
  2. Si approvedAlphAmount est inférieur à 5.1 ALPH, disons 1.1 ALPH, alors le montant maximum d'actifs que marketPlace.listNFT peut gérer est de 1.1 ALPHs et 1 token A. marketPlace.listNFT n'a pas accès au reste des 4 ALPHs.
  3. Si marketPlace.listNFT n'a pas dépensé la totalité des actifs approuvés, les actifs restants seront retournés à leur propriétaire lorsque marketPlace.listNFT retournera.

Jetons un coup d'œil de plus près à la fonction marketPlace.listNFT:

Rust
Contract NFTMarketPlace(
    nftListingTemplateId: ByteVec
) {
    // Other code are omitted for brevity

    pub fn getListingFee() -> U256 {
        return 0.1 ALPH
    }

    @using(preapprovedAssets = true, assetsInContract = true, updateFields = false)
    pub fn listNFT(
        tokenId: ByteVec,
        price: U256
    ) -> (Address) {
        assert!(price > 0, ErrorCodes.NFTPriceIsZero)

        // Only owner can list the NFT
        let tokenOwner = callerAddress!()

        let (encodeImmutableFields, encodeMutableFields) = NFTListing.encodeFields!(tokenId, tokenOwner, selfAddress!(), commissionRate, price)
        // Create the listing contract
        let nftListingContractId = copyCreateSubContract!{tokenOwner -> ALPH: 1 alph, tokenId: 1}(
            tokenId, nftListingTemplateId, encodeImmutableFields, encodeMutableFields
        )

        // Charge the listing fee
        transferTokenToSelf!(tokenOwner, ALPH, listingFee)

        return contractIdToAddress!(nftListingContractId)
    }
}

La première chose à remarquer est l'annotation pour la méthode listNFT:

Rust
@using(preapprovedAssets = true, assetsInContract = true, updateFields = false)

preapprovedAssets = true indique au VM que la méthod listNFT a l'intention d'utiliser certains actifs et que l'appelant est censé approuver un ensemble d'actifs nécessaires, sinon une erreur de compilation sera signalée. La compilation échouera également si l'appelant essaie d'approuver des actifs pour une méthode où preapprovedAssets = false.

assetsInContract = true indique au VM que la méthode listNFT souhaite mettre à jour l'actif du contrat NFTMarketPlace. Le compilateur veillera à ce que la méthode listNFT fasse effectivement cela, sinon une erreur de compilation sera signalée. Dans ce cas, listNFT met à jour l'actif du contrat NFTMarketPlace en transférant le listingFee à celui-ci :

Rust
// Charge the listing fee
transferTokenToSelf!(tokenOwner, ALPH, listingFee)

L'annotation updateFields est hors de portée pour cette documentation.

La méthode marketPlace.listNFT est invoquée par le TxScript ListNFT, comme indiqué ci-dessous :

Rust
marketPlace.listNFT{callerAddress!() -> ALPH: approvedAlphAmount, tokenAId: 1}(tokenAId, price)

Lorsque marketPlace.listNFT est exécutée par le VM, elle est autorisée à dépenser 1.1 ALPH et 1 jeton par l'appelant du script. Si marketPlace.listNFT appelle à son tour d'autres méthodes, elle peut approuver un sous-ensemble de ces actifs approuvés pour cette méthode également. Par exemple, dans marketPlace.listNFT, nous avons le code suivant pour créer une liste NFT:

Rust
let nftListingContractId = copyCreateSubContract!{tokenOwner -> ALPH: 1 alph, tokenId: 1}(
    tokenId, nftListingTemplateId, encodeImmutableFields, encodeMutableFields
)

Comme nous pouvons le voir, la méthode marketPlace.listNFT approuve 1 ALPH et 1 jeton A à la fonction intégrée copyCreateSubContract! à partir de son propre pool d'actifs approuvés (1.1 ALPH et 1 jeton A), avant d'envoyer le listingFee au contrat NFTMarketPlace lui-même. Le flux des actifs est illustré ci-dessous :

Text Only
  Appelant du TxScript
  (6.1 ALPH; 1 Jeton A)
           ||
           ||
           || Soustrait les actifs dans les 
           || sorties fixes
           ||
           ||                    Approuve                         Approuve
           \/               (1.1 ALPH; 1 Jeton A)              (1 ALPH; 1 Jeton A)
  (5.1 ALPH; 1 Jeton A)  ========================> listNFT ========================> copyCreateSubContract!
                                                     ||
                                                     ||
                                                     || À lui-même
                                                     ||
                                                     \/
                                                  (0.1 ALPH)

Comme nous pouvons l'imaginer, si nous avons un arbre de méthodes d'appel plus grand, le fonds approuvé se propagera de la racine de l'arbre jusqu'aux feuilles comme de l'eau. Le Système de Permission d'Actifs rend ce flux du fonds tout au long des appels de méthode explicite et impose des contraintes à chacune des méthodes quant aux jetons et à la quantité pouvant être dépensée.

En revenant à la transaction, après l'exécution du TxScript, les sorties générées devraient ressembler à quelque chose comme ceci :

Text Only
                        ----------------
                        |              |
                        |              |   1 ALPH (sortie fixe)
  1 Jeton A             |              | =========================================>
======================> |              |   1 ALPH, 1 Token A (NFTListing contrat)
  6.1 ALPHs             |  <TxScript>  | =========================================>
======================> |              |   0.1 ALPH (NFTMarketPlace contrat)
                        |              | =========================================>
                        |              |   4 ALPH - gaz (changer la sortie)
                        |              | =========================================>
                        |              |
                        ----------------

Résumé

Le Système de Permission d'Actifs (APS) dicte le flux des actifs dans les contrats intelligents. L'approbation explicite des actifs pour chaque invocation de méthode garantit que les méthodes ne peuvent jamais dépenser plus que ce pour quoi elles sont autorisées. Associé au modèle UTXO, il offre une solution de gestion des actifs plus simple, plus fiable et plus sécurisée.