Aún no tienes acceso a esta clase

Crea una cuenta y continúa viendo este curso

Curso de OpenZeppelin

Curso de OpenZeppelin

Sebastián Leonardo Perez

Sebastián Leonardo Perez

Reto: tokens

10/19
Recursos

Aportes 12

Preguntas 2

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad? Crea una cuenta o inicia sesión.

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");
        _;
    }

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);
    }
}

Completé el challenge y además le agregué pruebas unitarias:

Cosas que hice diferente con la solucion del profesor:

  • override de burn para poder aplicarle el modificador onlyOwner
  • override de _beforeTokenTransfer porque era obligatorio por la herencia
  • pruebas unitarias para probar lo siguiente:
    • Constract was created successfully
    • Contract token supply
    • Deployer is the owner
    • Transfer tokens to another account
    • Burn token
    • non-owner can’t burn
    • Pause Contract
    • Unpause Contract
    • non-owner can’t Pause Contract
    • non-owner can’t Unpause Contract

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);
    }
}

RETO #2 -SOLUCIONADO. Hay error en compilación si el mensaje usa “está”

// SPDX-License-Identifier: GPL-3

// Challenge #2: ERC20 Token Burnable and Pausable Token by the owner*

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract ERC20Challenge_2 is ERC20Burnable, Pausable, Ownable {

    constructor() ERC20("Platzi Token", "PLTK") {
        _mint(msg.sender, 1000);
    }

    function burn_token( address _sender, uint256 _amount) onlyOwner()  public {
        require(paused() != true, "No realizable ya que el contrato esta pausado.");
        _burn(_sender,_amount);
    }

    function pausar() onlyOwner() public {
        _pause();
    }

     function noPausar() onlyOwner() public {
        _unpause();
    }
}

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);
    }
}

muy buen Desafío, aprendí más a fondo las funcionalidades de los contratos.

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract TokenBurnablePausable is ERC20Burnable, Pausable, Ownable {

    constructor(string memory _name, string memory _symbol) ERC20Burnable()  ERC20(_name, _symbol) {
        _mint(msg.sender,1000);
    }

    function  pausar() public onlyOwner whenNotPaused {
        _pause();
    }

    function  reanudar() public onlyOwner whenPaused {
        _unpause();
    }

    function quemar(uint256 _cantidad) public whenNotPaused {
       burn(_cantidad);
    }
    
}

La función de quemar tokens me gustaría que se pudiera implementar cuado se hace una transferencia para poder controlar la emisión pero no encontré una manera de hacerlo por lo menos con la función _afterTokenTransfer

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";


contract MyToken is ERC20, Ownable, Pausable{

   constructor() ERC20("MyToken", "MTK") {
       _mint(msg.sender, 10000 * 10**18);
   }

   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);
        require(amount > 0, "amount is not allowed");
    }

    function burn(uint256 value) public onlyOwner whenNotPaused {
        _burn(msg.sender, value);
    }
    
}

No lo terminé. No voy a mentir

Estuve probando las funcionalidades y todo bien, es un gran reto, lo que no supe validar es que pausaba el contrato pero no parecía pasar absolutamente nada y todo lo que hice funcionó (por ejemplo transferirle fondos a una wallet que no era owner)

Dejo mi reto esperando que todo bien.

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";

contract NuevoToken is ERC20, Ownable, ERC20Burnable, Pausable{

     constructor() ERC20( "Platzicoin", "PLZ" ) {
        _mint( msg.sender, 1000 );
    }

    function generateTokens( address _reciver, uint256 _amount ) public onlyOwner{
        _mint( _reciver, _amount );
    }
    
    function burnTokens( uint256 _amountToBurn ) public onlyOwner{
        burn( _amountToBurn );
    }
    
    function pauseContract() public onlyOwner{
        _pause();
    }
    
    function unPauseContract() public onlyOwner{
        _unpause();
    }

}

Fue bastante dificil para mi este desafio

cuando fui directo al github de openzeppelin y busque el ownable y demás, no tenian el @openzeppelin/contracts etc, solo tenian import y el nombre.sol, eso me llamo la atencion porque no me dejaba seguir, ya que no estaba haciendo uso de los paquetes