En la entrada anterior repasamos el funcionamiento del bridge.
En este artículo se explica lo que hay que hacer para integrar los contratos del token con el bridge, de modo que los propietarios de tokens puedan transferirlos entre las redes Palm y Ethereum.
Contratos Originales vs Contratos Sintéticos
En el contexto del puente de la red Palm, un contrato original se sitúa donde se acuñan principalmente los tokens. Un contrato sintético se despliega en la cadena donde se transferirán los tokens a través del puente.
El despliegue de contratos originales y sintéticos garantiza que los tokens puedan transferirse entre las cadenas original y de destino.
Puede que tengas que hacer algunos cambios en tus contratos para que sean compatibles con el puente:
Las siguientes especificaciones se aplican a los contratos ERC-721. En el futuro se ofrecerán tutoriales para los contratos ERC-1155 o ERC-20.
Ejemplo de Contrato | Contratos Originales y Sintéticos
ERC-721 contract example
|
// SPDX-License-Identifier: MIT
pragma solidity 0.8.6;import"@openzeppelin/contracts/access/AccessControl.sol";import"@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";import"@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol";import"@openzeppelin/contracts/token/ERC721/extensions/ERC721Pausable.sol";import"./ERC2981.sol";
contract NFT is AccessControl,ERC2981, ERC721Enumerable, ERC721Burnable, ERC721Pausable {
event RoyaltyWalletChanged(address indexed previousWallet, address indexed newWallet);
event RoyaltyFeeChanged(uint256 previousFee, uint256 newFee);
event BaseURIChanged(string previousURI, string newURI);
bytes32 public constant OWNER_ROLE=keccak256("OWNER_ROLE");
bytes32 public constant MINTER_ROLE=keccak256("MINTER_ROLE");
uint256 public constant ROYALTY_FEE_DENOMINATOR=100000;
uint256 public royaltyFee;
address public royaltyWallet;
string private _baseTokenURI;/**
* @param _name ERC721 token name
* @param _symbol ERC721 token symbol
* @param _uri Base token URI
* @param _royaltyWallet Wallet where royalties should be sent
* @param _royaltyFee Fee numerator to be used for fees
*/constructor(
string memory _name,
string memory _symbol,
string memory _uri,
address _royaltyWallet,
uint256 _royaltyFee
)ERC721(_name, _symbol){_setBaseTokenURI(_uri);_setRoyaltyWallet(_royaltyWallet);_setRoyaltyFee(_royaltyFee);_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);_setupRole(OWNER_ROLE, msg.sender);_setupRole(MINTER_ROLE, msg.sender);}/**
* @dev Throws an exception if called by any account other than the owners.
* Implemented using the underlying AccessControl methods.
*/
modifier onlyOwners(){require(hasRole(OWNER_ROLE,_msgSender()),"Caller does not have the OWNER_ROLE");
_;}/**
* @dev Throws an exception if called by any account other than minters.
* Implemented using the underlying AccessControl methods.
*/
modifier onlyMinters(){require(hasRole(MINTER_ROLE,_msgSender()),"Caller does not have the MINTER_ROLE");
_;}/**
* @dev Mints the specified token IDs to the recipient addresses
* @param recipient Address that will receive the tokens
* @param tokenIds Array of tokenIds to be minted
*/functionmint(address recipient, uint256[] calldata tokenIds) external onlyMinters {for(uint256 i =0; i < tokenIds.length; i++){_mint(recipient, tokenIds[i]);}}/**
* @dev Mints the specified token ID to the recipient addresses
* @dev The unused string parameter exists to support the API used by ChainBridge.
* @param recipient Address that will receive the tokens
* @param tokenId tokenId to be minted
*/functionmint(address recipient, uint256 tokenId, string calldata) external onlyMinters {_mint(recipient, tokenId);}/**
* @dev Pauses token transfers
*/functionpause() external onlyOwners {_pause();}/**
* @dev Unpauses token transfers
*/functionunpause() external onlyOwners {_unpause();}/**
* @dev Sets the base token URI
* @param uri Base token URI
*/functionsetBaseTokenURI(string calldata uri) external onlyOwners {_setBaseTokenURI(uri);}/**
* @dev Sets the wallet to which royalties should be sent
* @param _royaltyWallet Address that should receive the royalties
*/functionsetRoyaltyWallet(address _royaltyWallet) external onlyOwners {_setRoyaltyWallet(_royaltyWallet);}/**
* @dev Sets the fee percentage for royalties
* @param _royaltyFee Basis points to compute royalty percentage
*/functionsetRoyaltyFee(uint256 _royaltyFee) external onlyOwners {_setRoyaltyFee(_royaltyFee);}/**
* @dev Function defined by ERC2981, which provides information about fees.
* @param value Price being paid for the token (in base units)
*/functionroyaltyInfo(
uint256,// tokenId is not used in this case as all tokens take the same fee
uint256 value
)
external
view
override
returns(
address,// receiver
uint256 // royaltyAmount){return(royaltyWallet,(value * royaltyFee)/ROYALTY_FEE_DENOMINATOR);}/**
* @dev For each existing tokenId, it returns the URI where metadata is stored
* @param tokenId Token ID
*/functiontokenURI(uint256 tokenId)public view override returns(string memory){
string memory uri =super.tokenURI(tokenId);returnbytes(uri).length >0?string(abi.encodePacked(uri,".json")):"";}functionsupportsInterface(bytes4 interfaceId)public
view
override(AccessControl,ERC2981,ERC721, ERC721Enumerable)returns(bool){returnsuper.supportsInterface(interfaceId);}function_beforeTokenTransfer(address from,
address to,
uint256 tokenId) internal override(ERC721, ERC721Enumerable, ERC721Pausable){super._beforeTokenTransfer(from, to, tokenId);}function_setBaseTokenURI(string memory newURI) internal {
emit BaseURIChanged(_baseTokenURI, newURI);
_baseTokenURI = newURI;}function_setRoyaltyWallet(address _royaltyWallet) internal {require(_royaltyWallet !=address(0),"INVALID_WALLET");
emit RoyaltyWalletChanged(royaltyWallet, _royaltyWallet);
royaltyWallet = _royaltyWallet;}function_setRoyaltyFee(uint256 _royaltyFee) internal {require(_royaltyFee <=ROYALTY_FEE_DENOMINATOR,"INVALID_FEE");
emit RoyaltyFeeChanged(royaltyFee, _royaltyFee);
royaltyFee = _royaltyFee;}function_baseURI() internal view override returns(string memory){return _baseTokenURI;}}
Comprueba tus contratos
Puedes utilizar el puente de la red de pruebas de Palm para probar en vivo la integración de tus contratos con el puente. Todo lo que necesitas es desplegarlos en la red de pruebas Palm y en Goerli (la red de pruebas de Ethereum).
Para los contratos ERC-721, tendrás que inicializarlo utilizando el constructor, y conceder acceso al contrato del manejador ERC-721 del bridge a MINTER_ROLE. Consulta Direcciones Componentes del Puente para conocer las direcciones de contrato relevantes.
El original y el sintético pueden ser exactamente iguales que en la producción.
Cómo Utilizar el Puente
Una vez que hayas preparado tus contratos para el puente, no dudes en ponerte en contacto con nosotros a través de discord para validar la compatibilidad de tus contratos. Nuestro equipo realizará las pruebas en la red de pruebas y las pondrá en producción.
Si todo parece correcto, concederás funciones de minero y/o quemador a la dirección de contrato del gestor ERC721 del puente y, a continuación, registraremos los contratos con el puente para que los propietarios puedan transferir los tokens.
Te recomendamos encarecidamente que pruebes tus contratos con el puente de la red de pruebas antes de configurarlos con el puente de la red principal.