franzueto
Victor Inojosa
Daniel Ramirez-Chiquillo
Leandro Gavidia Santamaria
guillermo sanchez
Metaskins Studios
Sebastian Perez
ProfesorEzequiel Savedra
Rafael Livise Larico
José Manuel Piña Rodríguez
Andrés Muñoz
Sebastian Perez
ProfesorWilly Gonzalez
Sebastian Perez
ProfesorMateo Santiago Zapata Maldonado
Sebastian Perez
ProfesorEber Laurente Lliuyacc
Erick Carvajal Rodríguez
Jorge David Diaz Cordero
Sebastian Aristizabal Castañeda
Dany R
Alejo Lemos
Mateo Santiago Zapata Maldonado
Arturo Mena
Oscar Riojas
La extensión Pausable de Open Zeppelin cuenta con modifiers que podemos utilizar en lugar del require que se implementó en la clase:
modifier whenNotPaused() { require(!paused(), "Pausable: paused"); _; } modifier whenPaused() { require(paused(), "Pausable: not paused"); _; }
Completé el challenge y además le agregué pruebas unitarias:
Cosas que hice diferente con la solucion del profesor:
mi solución:
// SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.10; import "oz-tokens/ERC20/ERC20.sol"; import "oz-tokens/ERC20/extensions/ERC20Burnable.sol"; import "openzeppelin-contracts/contracts/security/Pausable.sol"; import "oz-access/Ownable.sol"; contract Token is ERC20("Burnable and Pausable", "BaP"), ERC20Burnable, Pausable, Ownable { constructor ( ) { _mint(msg.sender, 1000); } function pauseBurns() public onlyOwner { _pause(); } function unPauseBurns() public onlyOwner { _unpause(); } function burn(uint256 amount) public virtual override onlyOwner whenNotPaused { _burn(msg.sender, amount); } function burnFrom(address account, uint256 amount) public virtual override onlyOwner whenNotPaused { _spendAllowance(account, _msgSender(), amount); _burn(account, amount); } }
Tests (usando DS-test):
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.10; import "ds-test/test.sol"; import "../Token.sol"; interface CheatCodes { function prank(address) external; } contract TokenTest is DSTest { Token tokenTest; CheatCodes cheats = CheatCodes(HEVM_ADDRESS); function setUp() public { tokenTest = new Token(); } function testMint() public { assertEq(tokenTest.balanceOf(address(this)), 1000); } function testBurn() public { tokenTest.burn(500); assertEq(tokenTest.balanceOf(address(this)), 500); } function testFailBurnWhenPaused() public { tokenTest.pauseBurns(); tokenTest.burn(500); assertEq(tokenTest.balanceOf(address(this)), 500); } function testPauseBurnUnPause() public { tokenTest.pauseBurns(); tokenTest.unPauseBurns(); tokenTest.burn(500); assertEq(tokenTest.balanceOf(address(this)), 500); } function testFailPauseNotOwner() public{ cheats.prank(address(0)); tokenTest.pauseBurns(); } function testFailBurnMoreThanBalance() public { tokenTest.burn(1500); } function testBurnAllowance() public { tokenTest.transfer(address(1), 10); address tokenTestAddress = address(this); cheats.prank(address(1)); tokenTest.increaseAllowance(tokenTestAddress, 10); tokenTest.burnFrom(address(1), 10); } function testFailBurnAllowanceWhenPaused() public { tokenTest.pauseBurns(); tokenTest.transfer(address(1), 10); address tokenTestAddress = address(this); cheats.prank(address(1)); tokenTest.increaseAllowance(tokenTestAddress, 10); tokenTest.burnFrom(address(1), 10); } }
// SPDX-License-Identifier: GPT-3.0 pragma solidity >=0.7.0 <0.9.0; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/security/Pausable.sol"; contract LGSToken is ERC20("LGS Token", "LGS"), Ownable, Pausable { constructor() { _mint(msg.sender, 10000); } function burnTokens(address _account, uint256 _amount) public onlyOwner whenNotPaused { _burn(_account, _amount); } function pauseContract() public onlyOwner whenNotPaused { _pause(); } function unpauseContract() public onlyOwner whenPaused { _unpause(); } }
// SPDX-License-Identifier: MIT // Compatible with OpenZeppelin Contracts ^5.0.0 pragma 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 :(
DeclarationError: Undeclared identifier. --> @openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol:36:9: | 36 | _spendAllowance(account, _msgSender(), amount); |
A simple viste no veo el error, tuviste algun progreso ?
Tengo el mismo problema, lo pudiste solucionar?
Reto
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; // Access import "@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 */ function tokenPause() public onlyAdmin { _pause(); } /** * Use modifier Pausable Funtion */ function tokenUnpause() public onlyAdmin { _unpause(); } /** * Override Burnable Funtion */ function burn(uint256 amount) public override onlyAdmin { super.burn(amount); } /** * Override Burnable Funtion */ function burnFrom(address account, uint256 amount) public override onlyAdmin { super.burnFrom(account,amount); } }
Excelente desafio, puedes encontrar todos los desafios usando hardhat
// SPDX-License-Identifier: MIT pragma 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); } function pause() public onlyRole(Owner) { _pause(); } function unpause() public onlyRole(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.
// SPDX-License-Identifier: MIT pragma 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"; contract TokenBurnablePausable is ERC20("3b3r Token","3b"), ERC20Burnable, Pausable{ address _ownerContract; constructor(){ _mint(msg.sender, 100000000000); _ownerContract = _msgSender(); } modifier onlyOwner(){ require(msg.sender == _ownerContract, "Caller isn't owner"); _; } function BurnToken(address owner, uint256 amount) public onlyOwner{ _burn(owner, amount); } function PauseContrat() public onlyOwner{ _pause(); } function UnPauseContrat() public onlyOwner{ _unpause(); } function transfer(address to, uint256 amount) public virtual override whenNotPaused returns (bool){ address owner = _msgSender(); _transfer(owner, to, amount); return true; } function approve(address spender, uint256 amount) public virtual override whenNotPaused returns (bool) { address owner = _msgSender(); _approve(owner, spender, amount); return true; } function transferFrom(address from, address to, uint256 amount) public virtual override whenNotPaused returns (bool) { address spender = _msgSender(); _spendAllowance(from, spender, amount); _transfer(from, to, amount); return true; } }
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.0 pragma 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"); _; } function quemarTokens(uint256 _amount) public whenNotPaused(){ burn(_amount); } function pausarContrato() public esAdmin() { _pause(); } function despausarContrato() public { _unpause(); } }```
Antes de la explicacion:
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; // Access import "@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); } function pasueDestroy() public { _pause(); } function unpauseDestroy() public { _unpause(); } function destroyTokes(uint256 amount) public{ require(!paused(), "the contract is paused"); _burn(_msgSender(), amount); } }
Después:
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; // Access import "@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); } function pasueDestroy() public { _pause(); } function unpauseDestroy() public { _unpause(); } function destroyTokes(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 LICENSE pragma 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); } function burn(uint256 amount) public override { _burn(_msgSender(), amount); } function pause() public onlyOwner { _pause(); } function unpause() 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.0 pragma 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)); _; } function Burn(uint256 amount) public onlyAdmin { require(!paused(), "The contract is paused"); burn(amount); } function PauseBurns() public onlyAdmin { _pause(); } function UnPauseBurns() public onlyAdmin { _unpause(); } }
// SPDX-License-Identifier: MIT pragma 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); } function burn(uint256 amount) public override whenNotPaused { require(hasRole(ICT_ADMIN, msg.sender)); super._burn(msg.sender, amount); } function pause () public whenNotPaused { require(hasRole(ICT_ADMIN, msg.sender)); _pause(); } function unpause () public whenPaused { require(hasRole(ICT_ADMIN, msg.sender)); _unpause(); } }
Mi código:
// SPDX-License-Identifier: GPL-3.0 pragma 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); } function quemaToken (uint cantidad) public { require (paused()!=true,"El contrato esta pausado"); burn (cantidad); } function pausaToken () public onlyOwner { _pause(); } function despausaToken () public onlyOwner { _unpause(); } }
Este es mi contrato en el constructor creo 1000 tokens
// SPDX-License-Identifier: MIT pragma 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()); } function pause() public onlyOwner { _pause(); } function unpause() public onlyOwner { _unpause(); } function _beforeTokenTransfer(address from, address to, uint256 amount) internal whenNotPaused override { super._beforeTokenTransfer(from, to, amount); } }