Smart contract d’une garantie autonome au sens de l’article 2321 du code civil
Une blockchain peut être définie comme une base de données permanente, distribuée globalement et fonctionnant de manière décentralisée.
Pour se représenter facilement son concept, on peut imaginer un très grand cahier, que tout le monde peut lire librement et gratuitement, sur lequel tout le monde peut écrire, mais qui est impossible à effacer et indestructible ¹.
Un des cas d’applications les plus populaires et prometteurs de cette technologie est le mal nommé smart contract ² correspondant à du code informatique exécuté sur une blockchain. La particularité d’un tel code est que son exécution emprunte les caractéristiques de la blockchain : une fois déployé, il ne peut être ni altéré ni effacé et sera exécuté immuablement.
Comme le suggère son appellation, il a été judicieusement pensé qu’une telle fonctionnalité pourrait servir à garantir l’exécution d’obligations contractuelles.
L’exécution certaine et automatisée de tout ou partie d’accords juridiques permettrait, entre autres, de les faire gagner en efficacité, sans perdre en confiance, tout en économisant les coûts des traditionnels intermédiaires parfois nécessaires à leur exécution.
Les potentialités des smart contract et leurs limites font l’objet de beaucoup de discussions. Le but de cet article est d’y contribuer en proposant un smart contract simplifié d’une garantie autonome.
Description du contrat.
L’article 2321 du code civil définit la garantie autonome comme « un engagement par lequel le garant s’oblige, en considération d’une obligation souscrite par un tiers, à verser une somme soit à première demande, soit suivant des modalités convenues. »
Une garantie autonome fonctionne similairement au mécanisme connu du cautionnement.
Imaginons un créancier prêtant une somme d’argent à un débiteur; pour se prémunir de son éventuelle défaillance, le créancier décide de contracter avec un garant qui s’engage à lui payer le montant de la somme prêtée, dès qu’il lui en fera la demande.
A la différence d’une caution, l’engagement du garant de la garantie autonome est indépendant juridiquement de celui existant entre le créancier et le débiteur. Le garant paye sa propre dette, celle à laquelle il s’est engagé envers le créancier. Il ne peut opposer au créancier les exceptions tenant à au prêt garanti.
Dans notre exemple de garantie autonome, un créancier a donc prêté une somme d’argent à un débiteur. Le créancier, souhaitant garantir ce prêt, contracte avec un garant qui s’engage à lui concéder, à première demande, l’équivalent du montant de la somme d’argent prêtée en ether (la monnaie virtuelle de la blockchain Ethereum sur laquelle serait déployé notre smart contract). La seule condition toutefois est que le créancier fasse sa demande dans un délai de 15 jours calendaires suivant le dépôt de l’ether.
Par un heureux hasard, dans notre exemple, le créancier a prêté 180 euros au débiteur, soit à peu près la valeur d’un ether au moment où est écrit cet article.
Le contrat ressemblerait alors à ceci :
Avant de rentrer dans le vif du sujet, présentons le mécanisme général de notre smart contract:
1 — Une fois que le contrat est signé, le garant envoie un ether au smart contract qui le stockera en attendant d’être retiré.
2 — Cet ether ne pourra être retiré que par le créancier, dans un délai de 15 jours suivant son dépôt.
3 — A défaut d’être retiré dans le délai imparti, le garant pourra récupérer l’ether.
Pour écrire ce smart contract, nous utiliserons le langage de programmation Solidity qui a l’avantage d’être le plus populaire et d’être relativement accessible.
Sans plus tarder voici le code du smart contract:
pragma solidity ^0.4.19;contract garantieAutonome {
uint public etherGaranti;
uint public dureeDuContrat = 15 days;
uint public delai;
address public creancier=0xe0f5206bbd039e7b0592d8918820024e2a7437b8;
address public garant=0xe0f5206bbd039e7b0592d8918820024e2a7437b1;
modifier onlyCreancier () {
require(msg.sender == creancier);
_;
}
modifier onlyGarant () {
require (msg.sender == garant);
_;
}
function internal commencerDelai (){
delai = now + dureeDuContrat;
}
function deposerEther() public payable onlyGarant {
require (msg.value == 1 ether);
etherGaranti = msg.value;
commencerDelai();
}
function retirerEther() public onlyCreancier {
require(now < delai);
creancier.transfer(etherGaranti);
etherGaranti = 0;
}
function finirContrat() public onlyGarant {
require(now > delai);
garant.transfer(etherGaranti);
etherGaranti = 0;}
}
Description du code
Création du smart contract et déclaration des variables
En toute première ligne de notre programme, nous avons indiqué la version du langage Solidity utilisé. Ici il s’agit de la version 0.4.19.
pragma solidity ^0.4.19;
Ensuite, nous avons créé le smart contract de la manière suivante :
garantieAutonome contract {}
C’est entre les accolades que doit être écrit le contenu du programme.
Puis, à la manière d’un contrat ordinaire où l’on définit les termes avant de les utiliser dans le corpus, nous avons défini des « variables » qui vont être utilisées dans le smart contract. Elles sont classées par type.
Il y a d’abord les variables de type numérique , contenant des nombres. Elles s’écrivent de la sorte:
uint public nomDeLaVariable = [valeur de la variable]
“Uint” est une abréviation pour unsigned integer qui signifie un entier non négatif et “public” signifie que la variable est accessible par tout le monde.
On peut d’emblée assigner une valeur à une variable ou le faire plus tard dans le programme.
Dans notre smart contract, nous avons les variables numériques suivantes:
- une variable qui stocke l’ether déposé en garantie, que nous avons appelé
etherGaranti
,
uint public etherGaranti;
- une autre correspondant à la durée du contrat, appelée
dureeDuContrat
et valant 15 jours,
uint public dureeDuContrat = 15 days;
- et une dernière correspondant au délai (sur laquelle nous reviendrons plus en détail plus tard).
uint public delai;
Il y a ensuite les variables de type adresse: ce sont les clefs publiques du créancier et du garant, qui identifient les deux protagonistes de notre smart contract.
address public creancier=0xe0f5206bbd039e7b0592d8918820024e2a7437b9;
address public garant=0xe0f5206bbd039e7b0592d8918820024e2a7437b1;
Ici les clefs publiques sont fictives.
Création des « modifier » : les vérificateurs de fonctions
Un smart contract est un programme interactif. Le garant interagit avec le programme lorsqu’il dépose l’ether en garantie, tout comme le créancier interagit avec le programme lorsqu’il le retire.
Ces interactions sont représentées par des fonctions. Il y en a deux dans notre smart contract avec lesquelles les protagonistes interagiront: une fonction deposerEther
et une fonction retirerEther
.
Celles-ci doivent pouvoir être appelées uniquement par les personnes concernées : seul le garant doit pouvoir déposer l’ether en garantie et seul le créancier doit pouvoir le retirer.
Nous avons donc incorporé des fonctions dites “vérificatrices“ à nos fonctions deposerEther
et retirerEther
pour s’assurer qu’elles soient bien appelées par les personnes concernées.
C’est le rôle des fonctions “modifier” que nous avons dénommées onlyCreancier
et onlyGarant
.
On créé une telle fonction de la manière suivante:
modifier nomDuModifier (){
// on écrit le code entre les accolades
_;}
La première fonction modifier vérifie que la personne interagissant avec le smart contract ( désigné “msg. sender”) est bien le créancier. Autrement dit, nous vérifions que l’adresse de la personne interagissant avec le programme est celle du créancier. On utilise alors la formule suivante:
require(msg.sender == creancier);
La seconde fonction réalise le même mécanisme avec le garant.
require(msg.sender == garant);
Création des fonctions
Attardons-nous maintenant sur la variable delai
définie en tête du smart contract.
Dans le contrat, il est stipulé que la condition pour que le créancier obtienne l’ether est de le réclamer dans un délai de 15 jours calendaires.
Afin de traduire cette stipulation en code, nous avons d’abord créé une variable appelée dureeDuContrat
égale à 15 jours: plus exactement la variable contient l’équivalent en secondes de 15 jours, soit 1 296 000 secondes.
Quand l’ether est déposé, il nous faut faire courir le délai. Pour cela, nous avons fait usage de la variable spéciale appelée now
qui donne l’équivalent, en secondes, du temps écoulé depuis le 01/01/1970 jusqu’à maintenant.
Ainsi now + 15 days
est égal au temps, en secondes, écoulé jusqu’à maintenant + la valeur en secondes de 15 jours.
Si nous créons une fonction commencerDelai
contenant l’expression now + 15 days
, qui “s’active” dès l’instant où est déposé l’ether , cela revient à dire :
« Donne moi, en secondes, le temps écoulé depuis le 01/01/1970 jusqu’à ce que la somme ait été déposée en garantie + l’équivalent en secondes de 15 jours. »
Le résultat de l’expression est alors stocké dans la variable delai
.
La fonction deposerEther
est alors créée de la sorte:
Puisqu’il s’agit d’une fonction spéciale par laquelle le garant “paye” un ether, elle contient le mot clef payable.
Et comme nous souhaitons qu’elle puisse être appelée que par le garant, nous y avons également inclus le nom de la fonction modifier onlyGarant
.
Dans cette fonction, l’ether envoyé doit être égal à une unité, ni plus, ni moins. On lui indique donc de vérifier que la valeur qu’elle va recevoir ( appelée msg.value
) soit égale à 1 ether
. Cet ether est stocké dans la variable etherGaranti
en attendant d’être retiré. Une fois cette opération effectuée, on peut faire courir le délai en appelant la fonction commencerDelai()
.
function deposerEther() public payable onlyGarant {
require (msg.value == 1 ether);
etherGaranti = msg.value;
commencerDelai();
}
La fonction retirerEther
, quand à elle, doit ne pouvoir être invoquée seulement par le créancier. Nous avons donc inclus le nom du modifier onlyCreancier
.
Le créancier ne peut retirer cette somme que si le délai de 15 jours n’est pas écoulé.
Autrement dit, nous devons vérifier si la valeur en secondes du 01/01/1970 jusqu’au moment où est appelée la fonction
retirerEther
, est inférieure à la valeur en secondes du 01/01/1970 jusqu’au moment a été appelée la fonctiondeposerEther
+ la valeur en secondes de 15 jours.
Ce qui revient à écrire: require(now <delai);
Si ce n’est pas le cas, la condition est remplie: le reste de fonction peut s’exécuter, à savoir, transférer l’ether au créancier et vider la variable etherGaranti
en la rendant égale à 0.
function retirerEther() public onlyCreancier {
require(now < delai);
creancier.transfer(etherGaranti);
etherGaranti = 0;
}
Enfin, la dernière fonction finirContrat
est une fonction que seul le garant peut appeler.
Si le délai est dépassé:
require(now > delai);
et que la somme n’a pas été retirée:
Ou, dit autrement, que le montant d’ether que contient variable
etherGaranti
n’est pas nul.
require (etherGaranti != 0);
Alors le garant peut récupérer son dépôt:
garant.transfer(etherGaranti);
Voilà pour pour une présentation succincte d’un smart contract très sommaire.
[1] Citation du mathématicien Jean-Paul Delahaye
[2] Dans un récent tweet Vitalik Buterin le créateur de la plateforme Ethereum dit regretter avoir utilisé le terme de “smart contract” pour quelque chose de plus ennuyant comme “code permanent”.