Une DApp native est une application native qui interagit avec un Smart Contract déployé sur une BlockChain. Dans le post précédent nous avons défini des principes d’architecture qui permettent d’avoir un niveau acceptable de sécurité et de user experience. Dans cette partie, nous rentrerons dans le code Android qui permet à cette DApp native de fonctionner.
Le code source complet de l’application est disponible sur Github ici : https://github.com/omarbenhamid/android-dareth.
Dans ce post nous allons nous concentrer sur les parties du code concernant l’interaction avec la BlockChain. Il peut être utile de consulter le code solidity du Smart Contract pour comprendre certaines opérations.
Présentation générale de l’application
L’application offre 3 fonctionnalités :
- La consultation de l’état du smart contract.
-
L’inscription à un contrat Daret
-
Le paiement d’une mensualité Daret.
Coté Android il s’agit d’une seule activité qui permet de mener les 3 actions.
Consultation d’un Smart Contract
Comme discuté dans l’article sur les principes d’architecture nous avons choisi d’utiliser l’API JSON RPC Ethereum comme compromis entre utilisabilité et sécurité. La consultation d’attributs de Smart Contract étant gratuite (ne coute pas de gaz), nous n’avons pas besoin d’activer le Wallet.
La sous-classe RefreshInfoTask de MainActivity.java (77) implémente cette interaction.
Nous utilisons la librairie KEthereum pour ne pas ré-implementer les différents protocoles et formats :
1) Il faut initialiser le le client RPC KEthereum :
private static final NetworkDefinition network = new NetworkDefinition3();
private static final EthereumRPC rpc = new EthereumRPC(network.getRpcEndpoints().get(0), new OkHttpClient.Builder().build());
NetworkDefinition3() correspond au testnet Ropsten sur lequel nous avions déployé notre Smart Contract. L’objet rpc ainsi obtenu permet d’appeler les methodes de l’API Ethereum relativement simplement.
2) La BlockChain conserve tout l’historique de tous les états passés d’un contrat dans des blocs. Il faut donc indiquer un numéro de bloc lors de chaque appel pour préciser quelle version nous souhaitons consulter. Voulant le tout dernier état nous allons appeler l’api eth_blockNumber pour récupérer le dernier numéro de bloc (le dernier connu par le noeud Ethereum qui nous fournit le service RPC) :
String blockNumberString = rpc.blockNumber().getResult();
long blockNumber = Long.parseLong(blockNumberString.replaceAll(“0x”, “”), 16);
Vous noterez que le block number est fourni au format hexadecimal qu’il faut convertir en nombre.
3) Nous allons maintenant récupérer la valeur de l’attribut public moisCourrant du smart contract, cela revient invoquer la methode moisCourrent(). Cette methode est crée automatiquement par le compilateur solidity quand un attribut est marqué public. Pour appeler une methode il faut calculer sa signature : c’est une chaine qui identifie de façon unique cette methode. Le format précis est spécifié dans la documentation solidity mais KEthereum nous permet de le faire simplement:
SignatureFunKt.toHexSignature(new TextMethodSignature(“moisCourrent()”))
4) On utilise cette signature pour appeler l’api eth_call :
StringResultResponse ret = rpc.call(“{\”to\”:\”“ + contractAddr
+ “\”,\”data\”:\”0x” +
SignatureFunKt.toHexSignature(new TextMethodSignature(“moisCourrent()”))+ “\”}”,
blockNumberString);
5) Enfin, on convertit le resultat au format numérique en utilisant KEthereum :
moisCourrent = BigIntegerKt.hexToBigInteger(ret.getResult());
De la même manière on récupère les autres informations sur le smart contract.
Execution d’une operation : appel du Wallet
Comme discuté dans l’article sur les principes d’architecture nous allons appeler le Wallet par intent. Pour ce faire, nous appelons l’action ACTION_VIEW sur une URI Ethereum standard ERC681 qui permet de spécifier une transaction. KEthereum permet de créer ce type d’URIs grâce à la classe ERC681 :
public void onClickInscription(android.view.View v) {
ERC681 tx = new ERC681();
tx.setAddress(contractAddr);
tx.setChainId(network.getChain().getId());
tx.setValue(mensualite);
tx.setFunction(“inscription”);
tx.setGas(new BigInteger(“50000”));
startActivityForResult(new Intent(Intent.ACTION_VIEW, Uri.parse(ERC681GeneratorKt.generateURL(tx))), REQ_INSCRIPTION);
}
L’appel android standard startActivityForResult va donc démarrer toute application qui s’est déclarée capabe de gérer ce type d’URI. C’est le cas de WallETH. Assurez vous donc à ce moment là que:
- Vous avez installé la dernière version de WallETH, car pour pouvoir faire cette appel quelques modifications ont été nécessaires. (Voir ces merge requests pour détail: https://github.com/walleth/walleth/pull/345 et https://github.com/walleth/walleth/pull/347)
-
Que vous envoyez la transaction sur le testnet Ropsten car c’est celui sur lequel le smart contract est déployé.
Vérifier le succès du paiement
Pour vérifier le succès du paiement, nous allons utiliser l’API BlockScout, ceci dit nous aurions pu faire la même vérification en utilisant le client rpc avec l’api eth_getTransactionByHash.
Dans tous les cas, il nous faut obtenir le hash de la transaction que le Wallet a crée car c’est l’unique identifiant qui permet de retrouver la transaction sur la BlockChain. Il n’y a malheureusement pas de standard pour permettre a un Wallet Android de retransmettre cet identifiant à l’application appelante, le Wallet aue nous utilisons (WallETH) retrourne ce hash dans un extra en réponse sous la clé “TXHASH” . Dans onActivityResult on peut donc le lire :
data.getStringExtra(“TXHASH“)
Vérifier la transaction dans BlockScout revient à appeler en GET l’URL https://blockscout.com/eth/ropsten/api?module=transaction&action=gettxinfo&txhash=TXHASH en remplaçant TXHASH par le hash de transaction. La réponse est un JSON qui contient un objet “result” qui, quand le block est miné, contient un attribut “confirmations”.
Le code de cet appel / verificiation peut être trouvé dans BlockScoutCheckTransactionTask.java ligne 65 à 83.
En conclusion
Avec ces éléments nous avons réussi à écrire un application Android qui interagit avec un Smart Contract sur Ethereum. Elle execute à la fois des opérations “gratuites” et des transactions payantes en invoquant le Wallet choisi par l’utilisateur. Il se trouve que notre application ne manipule que des crypto-assets, l’utilisation d’un Smart Contract est donc suffisante pour garantir la sécurité du processus, si ce n’était pas le cas, on aurait eu à plus vérifier les transactions, ce sera peut être le sujet d’un nouveau post.