Aller au contenu

Interagir avec les contrats

Alephium fournit un ensemble d'APIs via son nœud complet pour interagir avec les contrats déployés. Ces interactions peuvent être divisées en deux catégories selon qu'elles ont l'intention de mettre à jour l'état de la blockchain: les appels de contrat sont des interactions en lecture seule qui sont sans frais de gaz, exécutées immédiatement avec le résultat renvoyé immédiatement à l'appelant. Les transactions TxScript, quant à elles, mettent à jour l'état de la blockchain, nécessitent du gaz, sont exécutées lorsque les transactions sont minées avec seulement les identifiants de transaction renvoyés à l'appelant.

Interagir directement avec les contrats via l'API du nœud complet peut être fastidieux. Le Web3 SDK d'Alephium abstrait de nombreux détails en générant du code d'enrobage pour les contrats et les scripts de transaction. Illustrons son utilité en utilisant le contrat TokenFaucet discuté dans le guide de démarrage:

token_faucet.ral
Rust
import "std/fungible_token_interface"

// Définit un contrat nommé TokenFaucet.
// Un contrat est une collection de champs (son état) et de fonctions.
// Une fois déployé, un contrat réside à une adresse spécifique sur la blockchain Alephium.
// Les champs du contrat sont stockés de manière permanente dans le stockage du contrat.
// Un contrat peut émettre une quantité initiale de jetons lors de son déploiement.
Contract TokenFaucet(
    symbol: ByteVec,
    name: ByteVec,
    decimals: U256,
    supply: U256,
    mut balance: U256
) implements IFungibleToken {

    // Les événements permettent de consigner les activités sur la blockchain.
    // Les clients Alephium peuvent écouter les événements afin de réagir aux changements d'état des contrats.
    event Withdraw(to: Address, amount: U256)

    enum ErrorCodes {
        InvalidWithdrawAmount = 0
    }

    // Une fonction publique qui renvoie l'approvisionnement initial du jeton du contrat.
    // Remarquez que le champ doit être initialisé avec la quantité du jeton émis.
    pub fn getTotalSupply() -> U256 {
        return supply
    }

    // Une fonction publique qui renvoie le symbole du jeton.
    pub fn getSymbol() -> ByteVec {
        return symbol
    }

    // Une fonction publique qui renvoie le nom du jeton.
    pub fn getName() -> ByteVec {
        return name
    }

    // Une fonction publique qui renvoie le nombre de décimales du jeton.
    pub fn getDecimals() -> U256 {
        return decimals
    }

    // Une fonction publique qui renvoie le solde actuel du contrat.
    pub fn balance() -> U256 {
        return balance
    }

    // Une fonction publique qui transfère des jetons à quiconque l'appelle.
    // La fonction est annotée avec `updateFields = true` car elle modifie les champs du contrat.
    // La fonction est annotée comme utilisant les actifs du contrat car elle le fait.
    @using(assetsInContract = true, updateFields = true, checkExternalCaller = false)
    pub fn withdraw(amount: U256) -> () {
        // Les événements de débogage peuvent être utiles pour l'analyse des erreurs.
        emit Debug(`The current balance is ${balance}`)

        // Assurez-vous que le montant est valide.
        assert!(amount <= 2, ErrorCodes.InvalidWithdrawAmount)
        //  Les fonctions suffixées par `!` sont des fonctions intégrées.
        transferTokenFromSelf!(callerAddress!(), selfTokenId!(), amount)
        // Ralph n'autorise pas le débordement négatif.
        balance = balance - amount

        // Émettre l'événement défini précédemment.
        emit Withdraw(callerAddress!(), amount)
    }
}

Le contrat TokenFaucet dispose de 5 onctions publiques qui ne font que lire les états du contrat: getTotalSupply, getSymbol, getName, getDecimals et balance. Il dispose également d'une fonction appelée withdraw, qui non seulement met à jour l'état du contrat, mais transfère également des actifs.

Après la compilation du contrat TokenFaucet, une classe Typescript correspondante est générée. Nous pouvons obtenir une instance de cette classe après l'avoir déployée sur devnet:

TypeScript
import { DUST_AMOUNT } from '@alephium/web3'
import { getSigner } from '@alephium/web3-test'
import { TokenFaucet, TokenFaucetTypes, Withdraw } from '../artifacts/ts'

const signer = await getSigner()
const deployResult = await TokenFaucet.deploy(signer, {
  initialFields: {
    symbol: stringToHex('TF'),
    name: stringToHex('TokenFaucet'),
    decimals: 18n,
    supply: 10n ** 18n,
    balance: 10n
  }
})

const tokenFaucet = deployResult.contractInstance

Appel de contrat

À l'intérieur de la classe typescript TokenFaucet e SDK d'Alephium génère des méthodes pour toutes les fonctions pures dans le contrat TokenFaucet. Elles peuvent être appelées comme des fonctions typescript régulières:

TypeScript
const getNameResult = await tokenFaucet.methods.getName()
console.log(`name: ${hexToString(getNameResult.returns)}`)  // name: TokenFaucet

const getDecimalsResult = await tokenFaucet.methods.getDecimals()
console.log(`decimals: ${getDecimalsResult.returns)}`)      // decimals: 18

Notez que les résultats des appels de contrat sont retournés immédiatement et qu'aucun gaz ou signature n'est requis.

Le SDK d'Alephium génère également du code pour appeler plusieurs fonctions pures en même temps, réduisant ainsi le nombre de requêtes réseau:

TypeScript
const result = await tokenFaucet.multicall({
  getSymbol: {},
  getName: {},
  getDecimals: {},
  getTotalSupply: {}
})

console.log(`name: ${hexToString(result.getName.returns)}`)       // name: TokenFaucet
console.log(`symbole: ${hexToString(result.getSymbol.returns)}`)  // symbole: TF
console.log(`décimales: ${result.getDecimals.returns}`)           // décimales: 18
console.log(`offre totale: ${result.getTotalSupply.returns}`)     // offre totale: 10

TxScript Transactions

Le contrat TokenFaucet dispose également d'une fonction appelée withdraw, qui transfère le jeton du robinet au demandeur et met à jour le solde. Lors de l'appel de la fonction withdraw, nous devrons l'exécuter en tant que transaction à l'aide de TxScript:

Rust
TxScript Withdraw(token: TokenFaucet, amount: U256) {
    // Appeler la fonction de retrait du contrat de jeton.
    token.withdraw(amount)
}

Le SDK d'Alephium génère également une classe typescript correspondante pour le script de transaction Withdraw après la compilation, que nous pouvons utiliser pour exécuter la transaction:

TypeScript
const signer = await getSigner()
const withdrawResult = await Withdraw.execute(signer, {
  initialFields: { token: tokenFaucet.contractId, amount: 2n },
  attoAlphAmount: DUST_AMOUNT * 2n
})
console.log(`tx id: ${withdrawResult.txId}`)  // tx id: xxxx

Nous passons les initialFields comme défini dans le script de transaction Withdraw, et suffisamment attoAlphAmount pour couvrir le gaz ainsi que le montant minimal pour recevoir les jetons.

Notez qu'un signer est requis pour signer la transaction. Le résultat de l'exécution du script Withdraw contient un identifiant de transaction, qui peut être utilisé pour interroger ultérieurement l'état de la transaction. Certaines autres informations utiles dans le résultat d'exécution incluent:

  • unsignedTx: Version sérialisée de la transaction non signée
  • signature: Signature du signataire pour la transaction
  • gasAmount: Coût en gaz de la transaction

Les fonctions exécutées à partir de TxScript ne peuvent pas renvoyer de valeur directement à l'appelant car la transaction sera traitée ultérieurement et le résultat est non déterministe en fonction de l'état futur de la blockchain lorsque la transaction est minée. Les Événements sont plutôt souvent utilisés pour obtenir des informations sur les activités du contrat.


Abonnement aux événements

The withdraw function for TokenFaucet contract emits a Withdraw event, which contains the recipient and amount of the withdrawn token.

Rust
emit Withdraw(callerAddress!(), amount)

Des événements comme celui-ci sont très utiles pour les dApps afin de suivre les activités des contrats. Le Web3 SDK d'Alephium fournit un ensemble de fonctions pour interagir avec les événements des contrats. Voici comment nous pouvons nous abonner à l'événement Withdraw émis par la fonction de retrait dans le contrat TokenFaucet:

TypeScript
// Subscribe to the `Withdraw` event in
tokenFaucet.subscribeWithdrawEvent({
  pollingInterval: 500,
  messageCallback: (event: TokenFaucetTypes.WithdrawEvent): Promise<void> => {
    console.log('got withdraw event:', event)
    return Promise.resolve()
  },
  errorCallback: (error: any, subscription): Promise<void> => {
    console.log(error)
    subscription.unsubscribe()
    return Promise.resolve()
  }
})

Si plusieurs événements sont émis par le contrat TokenFaucet, une fonction appelée subscribeAllEvents sera également générée pour s'abonner à tous les événements émis par le contrat.


Pour aller plus loin

Le Web3 SDK d'Alephium construit des abstractions conviviales pour les développeurs au-dessus des APIs du nœud complet d'Alephium. Pour plus de détails sur les APIs, veuillez consulter la documentation OpenAPI.

Veuillez lire une explication plus détaillée sur TxScript ici, une fonctionnalité unique dans Alephium qui est un moyen plus flexible et efficace de créer des transactions qui interagissent avec les contrats intelligents.

Les événements sont cruciaux pour construire des dApps, plus d'informations peuvent être trouvées ici.