En este tutorial, crearemos un contrato Solidity ERC721 que admita regalías de ventas secundarias. Este contrato le permitirá crear tokens NFT, publicarlos para la venta en diferentes intercambios y permitir que el propietario del contrato reclame una regalía de creador en cada venta secundaria. Este contrato respalda las regalías de ventas secundarias en las plataformas comerciales Open Sea, Rarible y Mintable.
Este es un contrato avanzado y si es la primera vez que crea un NFT, le recomendaría leer «Cómo crear un contrato inteligente ERC721 Solidity». Luego regrese a este tutorial para aprender cómo implementar funciones avanzadas en un contrato ERC721.
Primeros pasos con un contrato de Solidity
En este momento, asegúrese de configurar su entorno de desarrollo. Puede utilizar cualquier IDE de Solidity con el que esté familiarizado. Para este tutorial usaremos remezclar ya que es simple, está basado en la web y hay mucho material de capacitación en la web para aprender cómo comenzar.
Dependencias del contrato ERC721
Cabe señalar que el contrato inteligente que crearemos requiere varios archivos de dependencia. Estos archivos de dependencia son fundamentales para que el contrato NFT respalde las regalías de ventas secundarias para el propietario del contrato. Las dependencias del contrato son las siguientes:
- Métodos de propiedad necesarios para Open Sea
- Métodos de regalías para Rarible
- Métodos de regalías ERC2981 NFT para Mintable
Agregaremos cada uno de los archivos de dependencia a Remix y los importaremos a nuestro contrato ERC721. Como alternativa, puede guardarlos en su repositorio de Github e importarlos a su contrato.
Configurar dependencias de regalías de ventas de contratos ERC721 en Remix
Primero cree los siguientes directorios en Remix en el explorador de archivos. Es importante que siga esta estructura de carpetas y archivos en Remix con las mismas convenciones de nomenclatura. Siempre puede cambiar la estructura y los nombres en una fecha posterior.
Cree los siguientes directorios:
- rarezas/regalías/contratos
- rarezas/royalties/contratos/impl
En estas dos carpetas pondremos 5 de los contratos de dependencia que necesitamos para este contrato NFT.
Directorio – objetos raros/regalías/contratos
En segundo lugar, coloque los siguientes archivos de dependencia en el directorio raribles/royalties/contracts
- LibPart
- LibRoyaltiesV2
- RegalíasV2
LibPart – Necesario para las regalías de ventas secundarias de Rarible
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
library LibPart {
bytes32 public constant TYPE_HASH = keccak256("Part(address account,uint96 value)");
struct Part {
address payable account;
uint96 value;
}
function hash(Part memory part) internal pure returns (bytes32) {
return keccak256(abi.encode(TYPE_HASH, part.account, part.value));
}
}
Pruébalo en remezclar
LibRoyaltiesV2 – Necesario para las regalías de ventas secundarias de Rarible
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
library LibRoyaltiesV2 {
/*
* bytes4(keccak256('getRaribleV2Royalties(uint256)')) == 0xcad96cca
*/
bytes4 constant _INTERFACE_ID_ROYALTIES = 0xcad96cca;
}
Pruébalo en remezclar
RegalíasV2 – Necesario para las regalías de ventas secundarias de Rarible
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./LibPart.sol";
interface RoyaltiesV2 {
event RoyaltiesSet(uint256 tokenId, LibPart.Part[] royalties);
function getRaribleV2Royalties(uint256 id) external view returns (LibPart.Part[] memory);
}
Pruébalo en remezclar
Directorio – rarezas/royalties/contratos/impl
Tercero, coloque los siguientes archivos de dependencia en el directorio raribles/royalties/contracts/impl
- ResumenRealezas
- RegalíasV2Impl
ResumenRealezas – Necesario para las regalías de ventas secundarias de Rarible
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../LibPart.sol";
abstract contract AbstractRoyalties {
mapping (uint256 => LibPart.Part[]) internal royalties;
function _saveRoyalties(uint256 id, LibPart.Part[] memory _royalties) internal {
uint256 totalValue;
for (uint i = 0; i < _royalties.length; i++) {
require(_royalties[i].account != address(0x0), "Recipient should be present");
require(_royalties[i].value != 0, "Royalty value should be positive");
totalValue += _royalties[i].value;
royalties[id].push(_royalties[i]);
}
require(totalValue < 10000, "Royalty total value should be < 10000");
_onRoyaltiesSet(id, _royalties);
}
function _updateAccount(uint256 _id, address _from, address _to) internal {
uint length = royalties[_id].length;
for(uint i = 0; i < length; i++) {
if (royalties[_id][i].account == _from) {
royalties[_id][i].account = payable(address(uint160(_to)));
}
}
}
function _onRoyaltiesSet(uint256 id, LibPart.Part[] memory _royalties) virtual internal;
}
Pruébalo en remezclar
RegalíasV2Impl – Necesario para las regalías de ventas secundarias de Rarible
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./AbstractRoyalties.sol";
import "../RoyaltiesV2.sol";
contract RoyaltiesV2Impl is AbstractRoyalties, RoyaltiesV2 {
function getRaribleV2Royalties(uint256 id) override external view returns (LibPart.Part[] memory) {
return royalties[id];
}
function _onRoyaltiesSet(uint256 id, LibPart.Part[] memory _royalties) override internal {
emit RoyaltiesSet(id, _royalties);
}
}
Pruébalo en remezclar
Crear un contrato inteligente ERC721
Ahora que todos nuestros archivos dependientes se han configurado y creado, creemos el contrato inteligente ERC721. En la raíz del explorador de archivos Remix, cree un nuevo archivo para nuestro contrato ERC721. Cabe señalar que si no se sigue la estructura de carpetas y archivos ilustrada arriba y abajo, los contratos dependientes no se importarán a su contrato inteligente correctamente.
Ahora copie el contrato a continuación y péguelo en Remix. Este contrato contiene las declaraciones de importación necesarias para Open Zeppelin y los archivos dependientes enumerados anteriormente. El contrato admite:
- acuñación de nuevos NFT
- revelando el proyecto NFT
- pausar la capacidad de acuñar NFT
- regalías de ventas secundarias en múltiples plataformas
- etc
Además, lea los comentarios en el código de contrato inteligente de Solidity para comprender cómo funcionan las diferentes funciones. Obtendrá una comprensión de todo el proceso de NFT, incluido cómo funcionan las regalías comerciales secundarias.
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
//Ownable is needed to setup sales royalties on Open Sea
//if you are the owner of the contract you can configure sales Royalties in the Open Sea website
import "@openzeppelin/contracts/access/Ownable.sol";
//the rarible dependency files are needed to setup sales royalties on Rarible
import "rarible/royalties/contracts/impl/RoyaltiesV2Impl.sol";
import "rarible/royalties/contracts/LibPart.sol";
import "rarible/royalties/contracts/LibRoyaltiesV2.sol";
//give your contract a name
contract NAMEYOURCONTRACT is ERC721Enumerable, Ownable, RoyaltiesV2Impl {
using Strings for uint256;
//configuration
string baseURI;
string public baseExtension = ".json";
//set the cost to mint each NFT
uint256 public cost = 0.00 ether;
//set the max supply of NFT's
uint256 public maxSupply = 100;
//set the maximum number an address can mint at a time
uint256 public maxMintAmount = 1;
//is the contract paused from minting an NFT
bool public paused = false;
//are the NFT's revealed (viewable)? If true users can see the NFTs.
//if false everyone sees a reveal picture
bool public revealed = true;
//the uri of the not revealed picture
string public notRevealedUri;
bytes4 private constant _INTERFACE_ID_ERC2981 = 0x2a55205a;
constructor(
string memory _name,
string memory _symbol,
string memory _initBaseURI,
string memory _initNotRevealedUri
) ERC721(_name, _symbol) {
setBaseURI(_initBaseURI);
setNotRevealedURI(_initNotRevealedUri);
}
//internal function for base uri
function _baseURI() internal view virtual override returns (string memory) {
return baseURI;
}
//function allows you to mint an NFT token
function mint(uint256 _mintAmount) public payable {
uint256 supply = totalSupply();
require(!paused);
require(_mintAmount > 0);
require(_mintAmount <= maxMintAmount);
require(supply + _mintAmount <= maxSupply);
if (msg.sender != owner()) {
require(msg.value >= cost * _mintAmount);
}
for (uint256 i = 1; i <= _mintAmount; i++) {
_safeMint(msg.sender, supply + i);
}
}
//function returns the owner
function walletOfOwner(address _owner)
public
view
returns (uint256[] memory)
{
uint256 ownerTokenCount = balanceOf(_owner);
uint256[] memory tokenIds = new uint256[](ownerTokenCount);
for (uint256 i; i < ownerTokenCount; i++) {
tokenIds[i] = tokenOfOwnerByIndex(_owner, i);
}
return tokenIds;
}
//input a NFT token ID and get the IPFS URI
function tokenURI(uint256 tokenId)
public
view
virtual
override
returns (string memory)
{
require(
_exists(tokenId),
"ERC721Metadata: URI query for nonexistent token"
);
if(revealed == false) {
return notRevealedUri;
}
string memory currentBaseURI = _baseURI();
return bytes(currentBaseURI).length > 0
? string(abi.encodePacked(currentBaseURI, tokenId.toString(), baseExtension))
: "";
}
//only owner
function reveal() public onlyOwner {
revealed = true;
}
//set the cost of an NFT
function setCost(uint256 _newCost) public onlyOwner {
cost = _newCost;
}
//set the max amount an address can mint
function setmaxMintAmount(uint256 _newmaxMintAmount) public onlyOwner {
maxMintAmount = _newmaxMintAmount;
}
//set the not revealed URI on IPFS
function setNotRevealedURI(string memory _notRevealedURI) public onlyOwner {
notRevealedUri = _notRevealedURI;
}
//set the base URI on IPFS
function setBaseURI(string memory _newBaseURI) public onlyOwner {
baseURI = _newBaseURI;
}
function setBaseExtension(string memory _newBaseExtension) public onlyOwner {
baseExtension = _newBaseExtension;
}
//pause the contract and do not allow any more minting
function pause(bool _state) public onlyOwner {
paused = _state;
}
function withdraw() public payable onlyOwner {
(bool success, ) = payable(msg.sender).call{value: address(this).balance}("");
require(success);
}
//configure royalties for Rariable
function setRoyalties(uint _tokenId, address payable _royaltiesRecipientAddress, uint96 _percentageBasisPoints) public onlyOwner {
LibPart.Part[] memory _royalties = new LibPart.Part[](1);
_royalties[0].value = _percentageBasisPoints;
_royalties[0].account = _royaltiesRecipientAddress;
_saveRoyalties(_tokenId, _royalties);
}
//configure royalties for Mintable using the ERC2981 standard
function royaltyInfo(uint256 _tokenId, uint256 _salePrice) external view returns (address receiver, uint256 royaltyAmount) {
//use the same royalties that were saved for Rar
LibPart.Part[] memory _royalties = royalties[_tokenId];
if(_royalties.length > 0) {
return (_royalties[0].account, (_salePrice * _royalties[0].value) / 10000);
}
return (address(0), 0);
}
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721Enumerable) returns (bool) {
if(interfaceId == LibRoyaltiesV2._INTERFACE_ID_ROYALTIES) {
return true;
}
if(interfaceId == _INTERFACE_ID_ERC2981) {
return true;
}
return super.supportsInterface(interfaceId);
}
}
Pruébalo en remezclar
Finalmente, modifique el contrato anterior para satisfacer sus necesidades de NFT. Puede usar el tutorial Cómo crear e implementar una colección NFT + el contrato de arriba para construir un gran proyecto NFT.
Después de crear un contrato ERC721, asegúrese de implementarlo en el entorno Goerli y probarlo con Open Sea, Rarible y Mintable. Asegúrese de probar la acuñación de tokens, cambiar los parámetros en el contrato y establecer regalías de ventas. El entorno de prueba de mar abierto en https://testnets.opensea.io/ permite a los propietarios de contratos conectar sus billeteras y establecer porcentajes de regalías de ventas.
Por último, pero no menos importante, no olvide revisar su código, auditar su contrato y escribir casos de prueba de unidad. Además, es importante realizar su propia investigación, auditoría y pruebas antes de implementar un contrato inteligente en la cadena de bloques. Este contrato ERC721 que respalda las regalías de ventas secundarias y otros contratos en este sitio son solo para fines educativos.
Si quiere puede hacernos una donación por el trabajo que hacemos, lo apreciaremos mucho.
Direcciones de Billetera:
- BTC: 14xsuQRtT3Abek4zgDWZxJXs9VRdwxyPUS
- USDT: TQmV9FyrcpeaZMro3M1yeEHnNjv7xKZDNe
- BNB: 0x2fdb9034507b6d505d351a6f59d877040d0edb0f
- DOGE: D5SZesmFQGYVkE5trYYLF8hNPBgXgYcmrx
También puede seguirnos en nuestras Redes sociales para mantenerse al tanto de los últimos post de la web:
- Telegram
Disclaimer: En Cryptoshitcompra.com no nos hacemos responsables de ninguna inversión de ningún visitante, nosotros simplemente damos información sobre Tokens, juegos NFT y criptomonedas, no recomendamos inversiones