Chats Lunaires: Le Périple, deuxième partie
Lors de notre précédante exploration, nous avions vu comment était généré un Chat Lunaire. A présent voyons comment fonctionne un wrapper, qui annonce emballer notre chat, le normaliser en NFT ERC721… Alors que la réalité est toute autre ! Il veut le cryogéniser et le cloner ! Voyez plus bas, la suite va vous étonner !
Le NFT ERC721
Avant d’entrer un peu plus dans les détails du wrapper, attardons-nous sur ce qu’est un ERC721, puisqu’il va créer un token de type ERC721 à partir d’un MoonCat.
Traduisons l’introduction de cette norme : “Le standard suivant permet l’implémentation d’une API standard au sein des smart contracts. Ce standard fournit des fonctionnalités de base pour traquer et transférer des NFT.”
Cette norme permet donc de définir des des NFT standardisés, simplifiant les échanges sur les places de marché telles que Opensea et Rarible, à travers la définition de méthodes et attributs génériques. Si l’objet est conforme, il est également visible dans le wallet depuis etherscan :
Mais revenons à notre wrapper, dont la tâche sera de “dresser” les Chats Lunaires selon la norme ERC721, tout en en conservant leurs attributs. Je mets le verbe “dresser” entre guillemets, car tout amateur de Chat qui se respecte est bien conscient qu’un Chat ne se laissera jamais “dresser”. Surtout s’il est issu d’un smart contract sauvage…
Le wrapper
Avant de commencer, rappelons-nous la procédure de JR_Kunz pour wrapper les MoonCats évoqué dans notre premier billet. Traduction libre :
- Activer dans le contrat sur la page etherscan de MoonCatsRescue 12. makeAdoptionOffer, avec comme paramètre le
catId
et l’adresse du contrat du wrapper, - Activer la fonction
wrap()
du wrapper lecatId
Étudions à présent un extrait de code de Smart Contract MoonCatsWrapped à partir de la ligne 1800, soit la ligne 1 de notre extrait.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
contract MoonCatsWrapped is ERC721 {
using Counters for Counters.Counter;
Counters.Counter private _tokenIdTracker;
MoonCatsRescue public _moonCats = MoonCatsRescue(0x60cd862c9C687A9dE49aecdC3A99b74A4fc54aB6);
mapping(bytes5 => uint) public _catIDToTokenID; // dictionnaire (hash) convertissant catID vers ID ERC721
mapping(uint => bytes5) public _tokenIDToCatID; // dictionnaire de ID ERC721 vers catID
string private _baseTokenURI;
address public _owner = 0xD2927a91570146218eD700566DF516d67C5ECFAB;
event Wrapped(bytes5 indexed catId, uint tokenID);
event Unwrapped(bytes5 indexed catId, uint tokenID);
constructor() ERC721("Wrapped MoonCatsRescue", "WMCR") {}
function setBaseURI(string memory _newBaseURI) public {
require(_msgSender() == _owner);
_baseTokenURI = _newBaseURI;
}
function renounceOwnership() public {
require(_msgSender() == _owner);
_owner = address(0x0);
}
function _baseURI() public view virtual returns (string memory) {
return _baseTokenURI;
}
function wrap(bytes5 catId) public {
MoonCatsRescue.AdoptionOffer memory offer = _moonCats.adoptionOffers(catId);
require(offer.seller == _msgSender()); //seul le propriétaire peut emballer son Chat
_moonCats.acceptAdoptionOffer(catId);
//Vérifie si le chat n'est pas déjà emballé
uint tokenID = _catIDToTokenID[catId];
uint tokenIDToAssign = tokenID;
if (tokenID == 0) {
tokenIDToAssign = _tokenIdTracker.current();
_tokenIdTracker.increment();
_catIDToTokenID[catId] = tokenIDToAssign;
_tokenIDToCatID[tokenIDToAssign] = catId;
}
_mint(_msgSender(), tokenIDToAssign);
emit Wrapped(catId, tokenIDToAssign);
}
function unwrap(uint256 tokenID) public {
bytes5 catId = _tokenIDToCatID[tokenID];
address owner = ownerOf(tokenID);
require(owner == _msgSender()); //seul le propriétaire peut déballer son chat
_moonCats.giveCat(catId, owner);
_burn(tokenID); // on brûle le chat ERC721 🔥😿🔥
emit Unwrapped(catId, tokenID); // on dégèle le Chat initial ❄️
}
}
wrap()
- Ligne 1 : la classe hérite de ERC721, classe de base décrite ligne 1321 et suivantes, héritant elle-même de plusieurs classes.
- Ligne 6 : récupération du contrat
MoonCatsRescue
dans_moonCats
, le Contrat Fondateur des Chats Lunaires, afin de pouvoir agir avec lui. - Ligne 35 :
function wrap(bytes5 catId) public
: c’est dans cette fonction que l’essentiel se passe !- Ligne 36 : récupère (l’innocente) offre d’adoption de l’autre contrat.
- Ligne 38 : Le Chat est adopté par le contrat wrapper et ne pourra en sortir uniquement lorsque la fonction
unwrap()
sera appelée. Le Chat est donc Freezed, ce qui justifie le chapeau accrocheur de cet article :-) - Lignes 46 à 48 : assignation d’un numéro unique au Chat à emballer (c’est un compteur incrémenté ligne 47). C’est donc ici qu’est construite une table de correspondance entre un
catID
et le tokenID qui lui est associé lors du wrap en ERC721. - Ligne 49 : on récupère l’ADN du chat (son
catId
). Ouvrons une parenthèse sur la question de l’ADN du Chat. La place nous manque pour tout analyser dans le détail, basons-nous sur ces deux sources ; le catID d’un Chat “normal” se décompose de la manière suivante :0x00ddrrvvbb
où :0x00
est le préfixe (héxadécimal, 00). Il en existe des différents, les genesis, sortes d’albinos qui devaient revenir aux créateurs mais dont beaucoup ont été perdus… Mais c’est une autre histoire.dd
l’aspect général du Chat :
(source)rrvvbb
la couleur de sa robe en rvb.
- Lignes 51 et 52, le token ERC721 est forgé, et l’information est envoyée sur la blockchain.
Exemple de transaction. L’onglet (Logs) permet de voir les paramètres utilisés, en particulier l’initiateur de la transaction, le catID
et le tokenID
correspondant qui lui assigné.
Dans le wallet du contrat de wrap, nous pouvons observer les Chats cryogénisés (càd gelés dans le compte du contrat jusqu’à l’appel d’un unwrap()
)
unwrap()
- Ligne 56 :
function unwrap(uint256 tokenID) public
: opération inverse, on décongèle le Chat. - Ligne 60 : Interaction avec le smart contract
MoonCatsRescue
pour remettre le Chat original à son propriétaire. - Ligne 61 :
_burn(tokenID)
: le token représentant le Wrapped MoonCatsRescue. La chaleur dégagée dégèle le Chat précédemment cryogénisé, le Chat original est prêt à être émis. - Ligne 62 : Émission sur la blockchain de l’ensemble la transaction d’unwrap.
Exemple de transaction. Pour retrouver l’appel à unwrap
et connaître les paramètres TokenID et catID, voir en bas de page de l’onglet Logs(5).
Remonter l’historique d’un Chat
A partir d’un wallet possédant un Chat (par exemple sur Rarible) ou Opensea, nous pouvons remonter l’historique, à l’aide d’etherscan. Nous pouvons également récupérer son TokenID et interroger le contrat ; fonctions 5 et 11 par exemple.
Ici le 7519
. Si à partir du TokenID nous pouvons obtenir le catId, nous pouvons aussi aller glaner des informations sur le contrat mooncatrescue (attention à la présentation des valeurs ; le TokenID est un uint256, donc un entier non signé, alors que le catId, un bytes5, doit être préfixé de 0x
)
Aller plus loin (en anglais)
- Un excellent article sur le frontrunning exploitant une faille dans le process d’adoption de Chat sur reddit.
- Analyse du smart contract pour les devs sur reddit
- Statistiques de types de Chats ayant été générés reddit
- The Non-Fungible Token Bible
- Un thread très complet à èrpèps des NFT sur twitter