TAVOLA DEI CONTENUTI
cos’è RTFKT
la funzionalità inventata da RTFKT per sostituire un NFT che viene bruciato con un’altro NFT
analisi dei contratti
Cos’è RTFKT?
RTFKT Studios (pronunciato "Artefact") è un'azienda di moda digitale & oggetti da collezione fondata nel gennaio 2020. Hanno creato articoli digitali, da sneakers ad avatar collezionabili. Questi elementi, come gli NFT vivono sulla blockchain e sono progettati per l'uso nei giochi e nella realtà aumentata.
RTFKT ha collaborato con l’artista Takashi Murakami, il più influente rappresentante della cultura giapponese contemporanea. Takashi Murakami si è unito a RTFKT in un progetto di cripto-arte chiamato Clone X.
Un’altro evento significativo nella vita dell’azienda è stata la sua acquisizione da parte di Nike. Nell'annuncio, il CEO di Nike ha affermato che questa acquisizione sarà utilizzata per offrire "agli atleti e creatori l’intersezione tra sport, creatività, gioco e cultura".
Ora che sappiamo cos’è RTFKT, analizzerò una collezione realizzata in collaborazione con Nike.
In questo articolo analizzeremo il metodo inventato da RTFKT per permettere di riscattare oggetti fisici partendo dal possesso di un NFT e i contratti che permettono questo processo.
In particolare analizzeremo i contratti della collezione RTFKT x NIKE AR HOODIE.
La funzionalità inventata da RTFKT per sostituire un NFT che viene bruciato con un’altro NFT
RTFKT ha creato una funzionalità chiamata "forging" che consente ai titolari di riscattare una versione fisica dell'NFT di loro proprietà.
Il processo:
in partenza viene creata una collezione NFT che sblocca un qualcosa di fisico nel mondo reale
quando l’acquirente dell’NFT vuole ottenere quel qualcosa di fisico, effettua un’operazione che farà si che il suo attuale NFT venga bruciato e sostituito da un’altro
che certifica il possesso del oggetto fisico.
Analisi dei contratti
La funzionalità che ha scelto RTFKT per questa collezione è suddiviso tra 3 contratti
ERC1155 il contratto che rappresenta l’NFT non ancora forgiato (background blu)
ERC1155 il contratto con cui l’utente interagisce per coniare l’NFT
ERC721 il contratto che rappresenta l’NFT già forgiato (background arancione)
Analisi del contratto 1
// SPDX-License-Identifier: MIT pragma solidity ^0.8.15; import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Burnable.sol"; // chiama la funzione OwnerOf del contratto ERC721 della collezione NFT background arancione abstract contract ERC721 { function ownerOf(uint256 tokenId) public view virtual returns (address); } // chiama la funzione forgeToken del contratto ERC721 della collezione NFT background arancione abstract contract ForgeTokenContract { function forgeToken(uint256 amount, uint256 tokenId, address owner) public virtual; } contract RedeemableToken is ERC1155, Ownable, ERC1155Burnable { constructor() ERC1155("") { tokenURIs[1] = "https://rtfkt.mypinata.cloud/ipfs/QmRbUSDAFLf5iEuU9v49Vt7YppyCjGsJQMxqrt6PWF5RF6/1"; } // address del contratto che permette il riscatto del NFT ERC721 address redemptionMiddlewareContract = 0x8E5474cA17499626EE88E2A533bD369EBe72099A; mapping (uint256 => uint256) public currentSupply; mapping (uint256 => uint256) public supplyLimit; mapping (uint256 => mapping (address => mapping(uint256 => address))) redeemedToken; mapping (uint256 => address) public forgingContractAddresses; mapping (address => uint256) public authorizedCollections; // Imposta il tokenID di conio associato alla raccolta mapping (uint256 => string) public tokenURIs; // Mint function function redeem(address owner, address initialCollection, uint256 tokenId) public payable returns(uint256) { // questa funzione viene chiamata dal redeem contract ovvero gli utenti interagiscono // con la funzione del mint attraverso il contratto RTFKTRedemption (0x8E547) require(msg.sender == redemptionMiddlewareContract, "Not authorized"); // se il contratto è autorizzato quindi è il contratto di Clone X o Dunks require(authorizedCollections[initialCollection] != 0, "Collection not authorized"); // viene effettuato il controllo della supply, in modo che non venga superato il massimo di token disponibili require(currentSupply[authorizedCollections[initialCollection]] + 1 <= supplyLimit[authorizedCollections[initialCollection]], "Limit reached"); // initialCollection sta per Clone X o Dunks e l'address del contratto ERC721 // il suo valore è attribuito alla variabile collectionRedeem ERC721 collectionRedeem = ERC721(initialCollection); // viene fatto il controllo per verificare la proprietà dei // token che fanno parte della collezione Clone X o Dunks require(collectionRedeem.ownerOf(tokenId) == owner, "Don't own that token"); // viene fatto il controllo per verificare se il token è stato già riscatatto require(redeemedToken[authorizedCollections[initialCollection]][initialCollection][tokenId] == 0x0000000000000000000000000000000000000000, "Token redeemd already"); // la currentSupply aumenta di 1 currentSupply[authorizedCollections[initialCollection]] = currentSupply[authorizedCollections[initialCollection]] + 1; // viene reggistratto il token appena riscatato dal mitente nella mapping dei token già riscatatti redeemedToken[authorizedCollections[initialCollection]][initialCollection][tokenId] = owner; // viene mintato un NFT ERC1155 con il background blu _mint(owner, authorizedCollections[initialCollection], 1, ""); return 1; } // puoi riscattare più token contemporaneamente soltanto se fanno parte della stessa collezione // vengono fatti i stessi controlli della funzione commentata sopra function redeemBatch(address owner, address initialCollection, uint256[] calldata tokenIds) public payable { require(msg.sender == redemptionMiddlewareContract, "Not authorized"); require(tokenIds.length > 0, "Mismatch of length"); require(authorizedCollections[initialCollection] != 0, "Collection not authorized"); require(currentSupply[authorizedCollections[initialCollection]] + tokenIds.length <= supplyLimit[authorizedCollections[initialCollection]], "Limit reached"); ERC721 collectionRedeem = ERC721(initialCollection); for(uint256 i = 0; i < tokenIds.length; ++i) { require(redeemedToken[authorizedCollections[initialCollection]][initialCollection][tokenIds[i]] == 0x0000000000000000000000000000000000000000, "Token redeemd already"); require(collectionRedeem.ownerOf(tokenIds[i]) == owner, "Don't own that token"); redeemedToken[authorizedCollections[initialCollection]][initialCollection][tokenIds[i]] = owner; } currentSupply[authorizedCollections[initialCollection]] = currentSupply[authorizedCollections[initialCollection]] + tokenIds.length; _mint(owner, authorizedCollections[initialCollection], tokenIds.length, ""); } /* Funzione di forgiatura arrivato il momento in cui gli NFT ERC1155 possono essere forgiati per una felpa con cappuccio e un NFT ERC721 che ne rappresenta il possesso fisico della felpa l'utente dovrà inserire nell'input l'id del NFT ERC1155 che intende forgiare e la quantità */ function forgeToken(uint256 tokenId, uint256 amount) public { // verifica se il tokenId dato in input fa parte dei token che possono essere forgiati require(forgingContractAddresses[tokenId] != 0x0000000000000000000000000000000000000000, "No forging address set for this token"); // verifica la proprietà della quantità di token data in input require(balanceOf(msg.sender, tokenId) >= amount, "Doesn't own the token"); // Check if the user own one of the ERC-1155 // il token ERC1155 viene bruciato burn(msg.sender, tokenId, amount); ForgeTokenContract forgingContract = ForgeTokenContract(forgingContractAddresses[tokenId]); // viene coniato il token NFT ERC-721 (background arancione) forgingContract.forgeToken(amount, tokenId, msg.sender); } // funzione Airdrop di NFT ERC1155 function airdropTokens(uint256[] calldata tokenIds, uint256[] calldata amount, address[] calldata owners) public onlyOwner { for(uint256 i = 0; i < tokenIds.length; ++i) { _mint(owners[i], tokenIds[i], amount[i], ""); } } // -------- // Getter // -------- // restituisce l'address dell'utente che ha riscatatto un determinato NFT ERC1155 function hasBeenRedeem(address initialCollection, uint256 tokenId) public view returns(address) { return redeemedToken[authorizedCollections[initialCollection]][initialCollection][tokenId]; } // restituisce l'uri dei metadata del NFT function uri(uint256 tokenId) public view virtual override returns (string memory) { return tokenURIs[tokenId]; } // -------- // Setter // -------- function setTokenURIs(uint256 tokenId, string calldata newUri) public onlyOwner { tokenURIs[tokenId] = newUri; } function setSupply(uint256 tokenId, uint256 newSupply) public onlyOwner { supplyLimit[tokenId] = newSupply; } function setForgingAddress(uint256 tokenId, address forgingAddress) public onlyOwner { forgingContractAddresses[tokenId] = forgingAddress; } function setAuthorizedCollection(address authorizedCollection, uint256 tokenId) public onlyOwner { // se il tokenId è 0 allora l'autorizzazione alla raccolta verrà annullata authorizedCollections[authorizedCollection] = tokenId; } function setMiddleware(address newContractAddress) public onlyOwner { redemptionMiddlewareContract = newContractAddress; } // Nel caso qualcuno invii denaro al contratto per errore function withdrawFunds() public onlyOwner { payable(msg.sender).transfer(address(this).balance); } }
All’interno di questo contratto abbiamo:
ereditato la funzione forgeToken dal contratto 3 per bruciare l’NFT ERC1155 scambiandolo per uno NFT ERC721
la funzione di conio del NFT ERC1155 che verrà ereditata dal contratto 2 dal quale l’utente dovrà coniare l’NFT
la verifica del possesso di almeno uno dei NFT di una delle due collezioni autorizzate per coniare, queste due collezioni sono Clone X e Dunks
Nel contratto 2 abbiamo:
la funzione che permette di inserire l’address del contratto autorizzato (Clone X o Dunks) riservata al proprietario della collezione
eredita dal contratto 1 la funzione reddemBatch attraverso la quale l’utente può coniare l’NFT ERC1155
Nel contratto 3 abbiamo:
la funzione di conio del NFT ERC721
la funzione per inserire l’URI dei metadata del NFT
Ho realizzato uno schema per dimostrare quali funzioni vengono ereditate dal quale contratto e all’interno del quale.
Per ereditare una funzione s’intende riscrivere lo scheletro della funzione appartenente a un contratto diverso rispetto a quello che sto scrivendo.
esempio: function forgeToken(uint256 amount, uint256 tokenId, address owner) public virtual;
Conclusione:
RTFKT ha inventato questa funzionalità per un’esperienza specifica, quella di riscattare un oggetto fisico grazie al possesso di un NFT.
Man mano i brand di abbigliamento, gioielleria, calzature entrano nel mondo della blockchain e vorranno associare un loro prodotto a un NFT, questo ci suggerisce che questo metodo o altri simili diventeranno molto richiesto.
La funzionalità inventata da RTFKT può risultare confusionaria per un utente nuovo nel mondo della blockchain, in quanto dovrà interagire due volte con il contratto, questo richiede il doppio pagamento del gas fee + il prezzo del NFT.
C’è un’altra collezione che ha usato un metodo simile per riscattare un oggetto fisico partendo dall’acquisto di un NFT. Parlo della collezione di Tiffany, soltanto il proprietario di un punk e di un NFTiff (NFT di Tiffany) può riscattare un ciondolo. L’intero processo è incorporato in un singolo contratto ed avviene sempre attraverso due interazioni con il contratto, in quanto il conio e la verifica della proprietà per riscattare l’oggetto fisico avviene sulla blockchain.
Una soluzione più economica dal punto di vista del gas sarebbe quella di effettuare la verifica della proprietà fuori catena.
Interesting!