Llegó el momento de poner a prueba todo lo aprendido hasta aquí sobre los diferentes tipos de tokens.
Desafío de tokens y extensiones
El reto para ti consiste en crear un Token ERC20 que permita quemar los mismos para controlar su emisión y pausar el contrato en el caso de descubrir una vulnerabilidad de seguridad.
La quema de tokens puedes realizarla con la extensión ERC20Burnable, mientras que para la pausa en el funcionamiento del contrato puedes utilizar la extensión ERC20Pausable.
Además, poner en pausa un contrato inteligente debería ser tarea del administrador del mismo. No estaría mal que tu contrato implemente Ownable o AccessControl.
Es tu momento, implementa tu primer token ERC20 cumpliendo con estas funcionalidades. Comparte tu solución y compárala con la del resto de estudiantes de Platzi.
// SPDX-License-Identifier: MIT// Compatible with OpenZeppelin Contracts ^5.0.0pragma solidity ^0.8.22;import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";import {ERC20Burnable} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";import {ERC20Pausable} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol";contract MyToken is ERC20, ERC20Burnable, ERC20Pausable, AccessControl { bytes32 public constant PAUSER_ROLE =keccak256("PAUSER_ROLE"); bytes32 public constant OWNER =keccak256("OWNER");constructor()ERC20("MyToken","MTK"){_grantRole(OWNER, msg.sender);_grantRole(PAUSER_ROLE, msg.sender);} function pause() public onlyRole(PAUSER_ROLE){_pause();} function unpause() public onlyRole(PAUSER_ROLE){_unpause();} function burn(uint256 value) public virtual override onlyRole(OWNER){_burn(_msgSender(), value);} function burnFrom(address account, uint256 value) public virtual override onlyRole(OWNER){_spendAllowance(account,_msgSender(), value);_burn(account, value);} function _update(address from, address to, uint256 value) internal
override(ERC20, ERC20Pausable){ super._update(from, to, value);}}```// SPDX-License-Identifier: MIT// Compatible with OpenZeppelin Contracts ^5.0.0pragma solidity ^0.8.22;import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";import {ERC20Burnable} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";import {ERC20Pausable} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol";contract MyToken is ERC20, ERC20Burnable, ERC20Pausable, AccessControl { bytes32 public constant PAUSER\_ROLE =keccak256("PAUSER\_ROLE"); bytes32 public constant OWNER =keccak256("OWNER"); constructor()ERC20("MyToken","MTK"){ \_grantRole(OWNER, msg.sender); \_grantRole(PAUSER\_ROLE, msg.sender);}  function pause() public onlyRole(PAUSER\_ROLE){ \_pause();}  function unpause() public onlyRole(PAUSER\_ROLE){ \_unpause();}  function burn(uint256 value) public virtual override onlyRole(OWNER){ \_burn(\_msgSender(), value);}  function burnFrom(address account, uint256 value) public virtual override onlyRole(OWNER){ \_spendAllowance(account, \_msgSender(), value); \_burn(account, value);}  function \_update(address from, address to, uint256 value) internal override(ERC20, ERC20Pausable){ super.\_update(from, to, value);}}
Cada vez que intento importar la extension ERC20Burnable, al momento de compilar me sale este error del mismo contracto de la extensión y no se que estoy haciendo mal, por que ya revise las configuraciones del contrato y del compilador con respecto al contrato de la extensión y todo esta bien, no se que hacer :(
A simple viste no veo el error, tuviste algun progreso ?
Tengo el mismo problema, lo pudiste solucionar?
Reto
// SPDX-License-Identifier: GPL-3.0pragma solidity >=0.7.0<0.9.0;// Accessimport"@openzeppelin/contracts/access/AccessControl.sol";// Tokens import"@openzeppelin/contracts/token/ERC20/ERC20.sol";import"@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";import"@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol";contract TokenFungible is ERC20("TokenFungible","TF"),ERC20Burnable,ERC20Pausable,AccessControl{ bytes32 public constant ROL_ADMIN=keccak256("ROL_ADMIN");constructor(){_grantRole(ROL_ADMIN, msg.sender);_mint(msg.sender,1);} modifier onlyAdmin{require(hasRole(ROL_ADMIN, msg.sender),"You dont have role admin"); _;}/**
* Override Pausable Funtion
*/function_beforeTokenTransfer(address from, address to, uint256 amount) internal override(ERC20,ERC20Pausable) onlyAdmin {ERC20Pausable._beforeTokenTransfer(from, to, amount);}/**
* Use modifier Pausable Funtion
*/functiontokenPause()public onlyAdmin {_pause();}/**
* Use modifier Pausable Funtion
*/functiontokenUnpause()public onlyAdmin {_unpause();}/**
* Override Burnable Funtion
*/functionburn(uint256 amount)public override onlyAdmin {super.burn(amount);}/**
* Override Burnable Funtion
*/functionburnFrom(address account, uint256 amount)public override onlyAdmin {super.burnFrom(account,amount);}}
Excelente desafio, puedes encontrar todos los desafios usando hardhat
// SPDX-License-Identifier: MITpragma solidity ^0.8.0;import"@openzeppelin/contracts/token/ERC20/ERC20.sol";import"@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";import"@openzeppelin/contracts/security/Pausable.sol";import"@openzeppelin/contracts/access/AccessControl.sol";contract PinaCoin is ERC20,ERC20Burnable,Pausable,AccessControl{ bytes32 public constant Owner=keccak256("Owner");constructor()ERC20("Pina Coin","Pina"){_grantRole(Owner, msg.sender);}functionpause()publiconlyRole(Owner){_pause();}functionunpause()publiconlyRole(Owner){_unpause();}function_beforeTokenTransfer(address from, address to, uint256 amount) internal
whenNotPaused
override
{super._beforeTokenTransfer(from, to, amount);}}
Al encontrar una vulnerabilidad pauso el contrato y luego cual sería el siguiente paso ? actualizar el código del contrato y volver hacer deploy no es posible... Como se debe manejar esta situación ?
Si pausas el contrato y era una falsa alarma o se solucionó el problema puede que alcance con quitarle la pausa. En caso de no poder solucionar debes recurrir a una nueva versión del contrato. Para esto existen patrones que te ayudan a hacer esta migración como el patrón "diamante" que puedes ver aquí: https://eips.ethereum.org/EIPS/eip-2535
Pregunta: ahora modificaste el contrato y lo implementaste nuevamente obviamente en la interfase de pruebas, que sucede si ya estando publicado el contrato en la Blockchain nos damos cuenta de un error o queremos mejorar nuestro contrato? que sucede cuando hacen los hackatones para buscar fallas en un contrato y consiguen una falla? como se corrige la falla si segun entiendo el contrato es inmutable? se crea otro contrato y se anula el primero?
Willy es un tema bastante complejo. En principio no puedes modificar el contrato, por lo tanto antes de publicarlo deberías pensar en alguna estrategia de acción por si llega a suceder. Por ejemplo, se podría pensar en bloquear el contrato y hacer una migración a otro contrato con el código actualizado.
Que significa esto?
The transaction has been reverted to the initial state.Note:The called function should be payable if you send value and the value you send should be less than your current balance.Debug the transaction to get more information.
Hola Mateo, lo que indica el mensaje tiene que ver con enviarle ethers en una cantidad incorrecta a una función. Si estás usando Remix, hay veces que muestra ese mensaje como un error genérico porque no puede detectar específicamente dónde está fallando, por eso te recomienda hacer Debug
Cumpliendo el reto
Como es de esperar, hago uso de las extensiones y las funciones de la implementación ERC20 de Openzeppelin. Creo una variable _ownerContract para almacenar la dirección que dueña del contrato, el modificador onlyOwner para validar que el caller sea el _ownerContract y aplico este modificador a las funciones transfer, approve y transferFrom.
El contrato ERC20Pausable.sol hereda de Pausable entonces con incluirlo al reto ya cuenta con las funciones y modifier para verificar si el contrato está pausado por ejemplo (whenNotPaused())
MI solución fue:
// SPDX-License-Identifier: GPL-3.0pragma solidity >=0.7.0<0.9.0;import"@openzeppelin/contracts/token/ERC20/ERC20.sol";import"@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";import"@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol";import"@openzeppelin/contracts/access/AccessControl.sol";contract TokenFungible is ERC20("TokenNeo","TKN"),AccessControl,ERC20Burnable,Pausable{ bytes32 public constant ROL_ADMIN=keccak256("ROL_ADMIN"); bytes32 public constant ROL_USUARIO=keccak256("ROL_USUARIO");constructor(){_mint(msg.sender,1000);_grantRole(ROL_ADMIN, msg.sender);} modifier esAdmin(){require(hasRole(ROL_ADMIN, msg.sender),"Esta funcion solo puede ser utilizada por el ADMIN"); _;}functionquemarTokens(uint256 _amount)publicwhenNotPaused(){burn(_amount);}functionpausarContrato()publicesAdmin(){_pause();}functiondespausarContrato()public{_unpause();}}```
Antes de la explicacion:
// SPDX-License-Identifier: GPL-3.0pragma solidity >=0.7.0<0.9.0;// Accessimport"@openzeppelin/contracts/access/AccessControl.sol";// Tokens import"@openzeppelin/contracts/token/ERC20/ERC20.sol";import"@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";import"@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol";import"@openzeppelin/contracts/access/Ownable.sol";contract Reto is ERC20,ERC20Burnable,Pausable,Ownable{constructor(uint256 initialSupply)ERC20("Gold","GLD"){_mint(msg.sender, initialSupply);}functionpasueDestroy()public{_pause();}functionunpauseDestroy()public{_unpause();}functiondestroyTokes(uint256 amount)public{require(!paused(),"the contract is paused");_burn(_msgSender(), amount);}}
Después:
// SPDX-License-Identifier: GPL-3.0pragma solidity >=0.7.0<0.9.0;// Accessimport"@openzeppelin/contracts/access/AccessControl.sol";// Tokens import"@openzeppelin/contracts/token/ERC20/ERC20.sol";import"@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";import"@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol";import"@openzeppelin/contracts/access/Ownable.sol";contract Reto is ERC20,ERC20Burnable,Pausable,Ownable{constructor(uint256 initialSupply)ERC20Burnable()ERC20("Gold","GLD"){_mint(msg.sender, initialSupply);}functionpasueDestroy()public{_pause();}functionunpauseDestroy()public{_unpause();}functiondestroyTokes(uint256 amount)public{require(!paused(),"the contract is paused");_burn(_msgSender(), amount);}}
Voy ganando
3 - 0 Ganando
Aqui va mi solución
// SPDX-License-Identifier: SEE LICENSE IN LICENSEpragma solidity >0.8.0;import"@openzeppelin/contracts/token/ERC20/ERC20.sol";import"@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";import"@openzeppelin/contracts/security/Pausable.sol";import"@openzeppelin/contracts/access/Ownable.sol";contract TokenBurnPausable is ERC20,ERC20Burnable,Pausable,Ownable{constructor()ERC20("MyToken","MTK"){_mint(msg.sender,1000000*10**18);}functionburn(uint256 amount)public override {_burn(_msgSender(), amount);}functionpause()public onlyOwner {_pause();}functionunpause()public onlyOwner {_unpause();}function_beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override {super._beforeTokenTransfer(from, to, amount);require(!paused(),"ERC20Pausable: token transfer while paused");}}
Mi código:
// SPDX-License-Identifier: GPL-3.0pragma solidity >=0.7.0<0.9.0;import"@openzeppelin/contracts/access/AccessControl.sol";import"@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";import"@openzeppelin/contracts/security/Pausable.sol";contract MyFungibleToken is ERC20Burnable,Pausable,AccessControl{ bytes32 public constant ROLE_ADMIN=keccak256("ROLE_ADMIN");constructor(uint amount)ERC20Burnable()ERC20("MyToken","TKN"){require(amount>0,"The amount must be greater than 0");_mint(msg.sender, amount);_grantRole(ROLE_ADMIN, msg.sender);} modifier onlyAdmin(){require(hasRole(ROLE_ADMIN, msg.sender)); _;}functionBurn(uint256 amount)public onlyAdmin {require(!paused(),"The contract is paused");burn(amount);}functionPauseBurns()public onlyAdmin {_pause();}functionUnPauseBurns()public onlyAdmin {_unpause();}}
// SPDX-License-Identifier: MITpragma solidity >=0.7.0<0.9.0;import"@openzeppelin/contracts/token/ERC20/ERC20.sol";import"@openzeppelin/contracts/access/AccessControl.sol";import"@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";import"@openzeppelin/contracts/security/Pausable.sol";contract India is ERC20("India Cartagena Token","ICT"),Pausable,ERC20Burnable,AccessControl{ bytes32 public constant ICT_ADMIN=keccak256("ICT_ADMIN"); bytes32 public constant ICT_CLIENT=keccak256("ICT_CLIENT");constructor(uint256 initialSupply)ERC20Burnable(){_mint(msg.sender, initialSupply);}functionburn(uint256 amount)public override whenNotPaused {require(hasRole(ICT_ADMIN, msg.sender));super._burn(msg.sender, amount);}functionpause()public whenNotPaused {require(hasRole(ICT_ADMIN, msg.sender));_pause();}functionunpause()public whenPaused {require(hasRole(ICT_ADMIN, msg.sender));_unpause();}}
Mi código:
// SPDX-License-Identifier: GPL-3.0pragma solidity >=0.7.0<0.9.0;import"@openzeppelin/contracts/access/Ownable.sol";import"@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";import"@openzeppelin/contracts//security/Pausable.sol";contract DesafioERC20 is ERC20Burnable,Pausable,Ownable{constructor()ERC20Burnable()ERC20("Desafio Extensiones","DE"){_mint(msg.sender,1000);}functionquemaToken(uint cantidad)public{require(paused()!=true,"El contrato esta pausado");burn(cantidad);}functionpausaToken()public onlyOwner {_pause();}functiondespausaToken()public onlyOwner {_unpause();}}
Este es mi contrato en el constructor creo 1000 tokens
// SPDX-License-Identifier: MITpragma solidity ^0.8.4;import"@openzeppelin/contracts/token/ERC20/ERC20.sol";import"@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";import"@openzeppelin/contracts/security/Pausable.sol";import"@openzeppelin/contracts/access/Ownable.sol";contract ORCoin is ERC20,ERC20Burnable,Pausable,Ownable{constructor()ERC20("ORCoin","ORC"){_mint(msg.sender,1000*10**decimals());}functionpause()public onlyOwner {_pause();}functionunpause()public onlyOwner {_unpause();}function_beforeTokenTransfer(address from, address to, uint256 amount) internal
whenNotPaused
override
{super._beforeTokenTransfer(from, to, amount);}}