José Manuel Piña Rodríguez
Pepe Sosa
Nicolas Lopez
Sebastian Perez
ProfesorNicolás Posa
Eber Laurente Lliuyacc
guillermo sanchez
Kevin Fiorentino
Luis Daniel Vieras
Metaskins Studios
Nicolas Barragan
Guillermo Leonardo Rossi
Sebastian Perez
ProfesorLeonardo David Martinez Hernandez
Sebastian Perez
ProfesorErick Carvajal Rodríguez
Sebastian Perez
ProfesorAndrés Muñoz
Sebastian Perez
ProfesorDaniel Vaca
Sebastian Perez
ProfesorNestor Mamani
Sebastian Aristizabal Castañeda
Alejo Lemos
Arturo Mena
Carlos Paez
Silvia Ayala
Interesante la mejora para facilitar la adición de los roles. Desafio completado en Hardhat
Si tienen duda de donde provienen las funciones como:
hasRole() _grantRole() _revokeRole()
Son funciones heradas del AccessControl.sol que pueden encontrar el contrato aqui que necesitarias para una parte del reto.
Reto:
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; import "@openzeppelin/contracts/access/AccessControl.sol"; contract Storage is AccessControl{ bytes32 public constant ROL_ADMIN = keccak256("ROL_ADMIN"); bytes32 public constant ROL_WRITER = keccak256("ROL_WRITER"); uint256 number; constructor(){ _grantRole( ROL_ADMIN, msg.sender ); } function store(uint256 num) public { require( hasRole(ROL_WRITER, msg.sender), "Esta funcion solo puede ser utilizada por el WRITER" ); number = num; } function retrieve() public view returns (uint256){ return number; } modifier onlyAdmin(){ require( hasRole( ROL_ADMIN, msg.sender ), "Esta funcion solo puede ser utilizada por el ADMIN" ); _; } function agregarRol( bytes32 role, address account ) public onlyAdmin() { _grantRole( role, account ); } function quitarRol( bytes32 role, address account ) public onlyAdmin() { _revokeRole( role, account ); } }
Como puedo usar openzeppelin en VSC?
VSC o BSC de Binance Smart Chain ?
Los contratos de oppen zeppelin no dejan de ser contratos en Solidity, simplemente incluyes el contrato en el código y lo puedes utilizar sin problemas.
Supongo que hablas de como utilizar openzeppelin en Visual Studio Code. En la raíz del proyecto ejecutas el siguiente comando:
npm install @openzeppelin/contracts
con eso ya vas a poder acceder a los contrato importandolos. Te dejo la documentación por si le queres echar un ojo
Desafío cumplido:
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.2 <0.9.0; import "@openzeppelin/contracts/access/AccessControl.sol"; /** * @title Storage * @dev Store & retrieve value in a variable * @custom:dev-run-script ./scripts/deploy_with_ethers.ts */ contract Storage is AccessControl { bytes32 public constant Admin = keccak256("Admin"); bytes32 public constant Writer = keccak256("Writer"); uint256 number; constructor() { _grantRole(Admin, msg.sender); _setRoleAdmin(Writer, Admin); } /** * @dev Store value in variable * @param num value to store */ function store(uint256 num) public onlyRole(Writer){ number = num; } /** * @dev Return value * @return value of 'number' */ function retrieve() public view returns (uint256){ return number; } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.2 <0.9.0; /** * @title Storage * @dev Store & retrieve value in a variable * @custom:dev-run-script ./scripts/deploy_with_ethers.ts */ import "@openzeppelin/contracts/access/AccessControl.sol"; contract Storage is AccessControl { bytes32 private ADMIN = keccak256("ADMIN"); bytes32 private WRITER = keccak256("WRITER"); uint256 number; constructor() { _grantRole(ADMIN, msg.sender); _grantRole(WRITER, msg.sender); } /** * @dev Store value in variable * @param num value to store */ function store(uint256 num) public { number = num; } /** * @dev Return value * @return value of 'number' */ function retrieve() public view onlyRole(WRITER) returns (uint256) { return number; } function addRoleWRITER(address account) public onlyRole(ADMIN) { _grantRole(WRITER, account); } function removeRoleWRITER( address account) public onlyRole(ADMIN) { _revokeRole(WRITER, account); } }
Pequeña satisfacción de ver que la solución que implementé fue exactamente igual que el profe. Dejaré mi código en GitHub.
Reto completado
He usado herencia y la librería de Access Control para el Ownable y Roles.
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.8 <0.9.0; import "@openzeppelin/contracts/access/AccessControl.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "../contracts/1_Storage.sol"; contract primerRetoOpenZeppelin is Ownable, AccessControl, Storage { // Constantes en bytes32 incluiendo encriptacion para la asignación de roles predefinidos. // Constants in bytes32, including encryption for predefined role assignment. bytes32 public constant ADMIN_ROL = keccak256("Admin_rol"); bytes32 public constant WRITER_ROL = keccak256("Writer_rol"); // Instancia de constructor, para asignar por defecto un rol administrativo al contrato. // Cuando se utiliza Ownable, el constructor predeterminado define que la cuenta utilizada es el propietario que implementa el contrato. // Constructor instance, to assign an administrative role to the default contract. // When Ownable is used, the default constructor defines that the account used is the owner that implements the contract. constructor(){ _grantRole(ADMIN_ROL, msg.sender); } //Modifiers belonging to the ADMIN and WRITER roles. //Modificadores pertenencientes a los roles ADMIN y ESCRITOR. modifier AdminRole(){ require(hasRole(ADMIN_ROL,msg.sender), "Function valid for the Contract Administrator"); _; } modifier WriterRole(){ require(hasRole(WRITER_ROL,msg.sender), "Function valid for the Writer of the contract"); _; } // Required feature for the challenge, add writer and remove writer. // Funcion requeridas para el reto, agregar escritor, y remover escritor. function addWriter(bytes32 role, address account) public AdminRole { _grantRole(role,account); } function RemoveWriter(bytes32 role, address account) public AdminRole { _revokeRole(role,account); } function store(uint256 num) public override WriterRole { number = num; } }
Los requerimientos del reto están en el contrato, añadiendo otros plus como:
Librerías: Access Control Ownable, AccessControl.
Convierte en el propietario del contrato a quien lo despliega.
Transferir a otro ownership y renunciar automaticamente.
Renunciar al contrato pone como owner 0 al contrato. Aunque se puede seguir teniendo rol de ADM.
El rol ADM se pone por defecto al desplegar el contrato.
El ADM puede agregar Role Writer y remover.
El Writer puede accionar a la función Store.
La función receive es accesible por todos.
Esta es mi solución al reto, usando un Enum con las acciones Grant y Revoke para tener una sola función al momento de agregar o quitar el rol Writer.
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; import "@openzeppelin/contracts/access/AccessControl.sol"; /** * @title Storage * @dev Store & retrieve value in a variable */ contract Storage is AccessControl{ enum RoleAction { Grant, Revoke} bytes32 public constant ROL_ADMIN = keccak256("ROL_ADMIN"); bytes32 public constant ROL_WRITER = keccak256("ROL_WRITER"); uint256 number; modifier onlyWriter() { require(hasRole(ROL_WRITER,msg.sender),"Only WRITER can call this function"); _; } modifier onlyAdmin() { require(hasRole(ROL_ADMIN,msg.sender),"Only ADMIN can call this function"); _; } constructor() { _grantRole(ROL_ADMIN, msg.sender); } /** * @dev Store value in variable * @param num value to store */ function store(uint256 num) public onlyWriter{ number = num; } /** * @dev Return value * @return value of 'number' */ function retrieve() public view returns (uint256){ return number; } function modifyWriterRole(address account, RoleAction actionToDo) public onlyAdmin{ if(actionToDo == RoleAction.Grant){ _grantRole(ROL_WRITER,account); } else if(actionToDo == RoleAction.Revoke){ _revokeRole(ROL_WRITER,account); } } }
Este es mi desafío, lo quise hacer también con herencia para aplicar los conceptos aprendidos en los anteriores cursos: Aclaración: Para esto tuve que modificar la función store del código de 1_Storage.sol, agregarle virtual para luego poder agregarle el modifier:
// Único cambio aplicado al contrato base: function store(uint256 num) public virtual { number = num; }
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; import "@openzeppelin/contracts/access/AccessControl.sol"; import "./1_Storage.sol"; contract StorageChallenge is AccessControl, Storage { bytes32 public constant ADMIN = keccak256("ADMIN"); bytes32 public constant WRITER = keccak256("WRITER"); constructor() { _grantRole(ADMIN, msg.sender); } modifier isAdmin() { require(hasRole(ADMIN, msg.sender), "Only the admin can access to this function"); _; } modifier isWriter() { require(hasRole(WRITER, msg.sender), "Only the WRITER can access to this function"); _; } function giveRole(address _writer) external isAdmin { _grantRole(WRITER, _writer); } function revokeRole(address _writer) external isAdmin { _revokeRole(WRITER, _writer); } function store(uint256 num) public override isWriter {} }
no me queda claro la diferencia entre grantRole(role, account) y _grantRole(role, account)
La diferencia es la visibilidad. _grantRole es internal y grantRole es pública. Igual si revisas el código verás que una llama a la otra, por lo tanto terminan haciendo lo mismo.
Por que al hacer click en el botón ROL_ADMIN en remix, la dirección o valor bytes32 no coincide con la dirección del wallet desde donde hice el deploy del contrato?
El ROL_ADMIN no es una dirección si no que es el resultado de generar un hash de la palabra "ROL_ADMIN", por eso lo ves diferente
En la función agregarRol si la defino con un parámetro "rol" se me hace imposible que funcione, así que al final lo dejé como. el profe quitando ese parámetro y solo enviando la cuenta address.
¿Alguien sabe por qué?
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; import "@openzeppelin/contracts/access/AccessControl.sol"; /** * @title Storage * @dev Store & retrieve value in a variable * @custom:dev-run-script ./scripts/deploy_with_ethers.ts */ contract Storage is AccessControl { uint256 number; bytes32 public constant ROL_ADMIN = keccak256("ADMIN"); bytes32 public constant ROL_WRITER = keccak256("WRITER"); constructor() { _grantRole(ROL_ADMIN, msg.sender); } modifier soloAdmin() { require(hasRole(ROL_ADMIN, msg.sender), "Esta funcion solo puede ser utilizada por el ADMIN"); _; } modifier soloWriter() { require(hasRole(ROL_WRITER, msg.sender), "Esta funcion solo puede ser utilizada por el WRITER"); _; } function agregarRol(address account) public soloAdmin() { _grantRole(ROL_WRITER, account); } function removerRol(address account) public soloAdmin() { _revokeRole(ROL_WRITER, account); } /** * @dev Store value in variable * @param num value to store */ function store(uint256 num) public soloWriter() { number = num; } /** * @dev Return value * @return value of 'number' */ function retrieve() public view returns (uint256){ return number; } }
Hola Erick, tal vez sea por el tipo de dato que tiene el rol. Si compartes el código es probable que lo encontremos :)
Tengo la duda de ¿Qué significa @dev y @title en los comentarios de los contratos ? veo que se usa esto en los contratos de OpenZeppelin y remix.
Hola Andrés, estos comentarios son una forma de documentar información del contrato. Esto corresponde a un formato llamado NatSpec y puedes encontrar el detalle aquí: https://docs.soliditylang.org/en/v0.8.11/natspec-format.html
¿Por qué al aejecutar la función "quitarWriter" en Remix me devuelve error de "The transaction ran out of gas. Please increase the Gas Limit."? He incrementado el Gas Limit en el IDE, pero sigue saliendo el mismo error.
hola Daniel, no debería tener problemas la función y de hecho lo que probaste de incrementar el gas limit está bien. Hay veces que Remix tiene problemas, cuentame si lo vuelves a probar y sigue fallando
En mi solución al reto utilicé el DEFAULT_ADMIN_ROLE para que asigne y quite ADMIN_ROLE
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; import "@openzeppelin/contracts/access/AccessControl.sol"; contract Storage is AccessControl { bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); bytes32 public constant WRITER_ROLE = keccak256("WRITER_ROLE"); uint256 number; constructor() { _setupRole(DEFAULT_ADMIN_ROLE, msg.sender); } modifier onlyAdmin() { require(hasRole(ADMIN_ROLE, msg.sender), "Caller is not an admin"); _; } modifier onlyWriter() { require(hasRole(WRITER_ROLE, msg.sender), "Caller is not a writer"); _; } modifier onlyDefaultAdmin() { require( hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Caller is not a default admin" ); _; } function grantWriterRole(address writer) public onlyAdmin { grantRole(WRITER_ROLE, writer); } function revokeWriterRole(address writer) public onlyAdmin { revokeRole(WRITER_ROLE, writer); } function grantAdminRole(address admin) public onlyDefaultAdmin { grantRole(ADMIN_ROLE, admin); } function revokeAdminRole(address admin) public onlyDefaultAdmin { revokeRole(ADMIN_ROLE, admin); } function store(uint256 num) public onlyWriter { number = num; } function retrieve() public view returns (uint256) { return number; } }
2 - 0 Ganando
Mi código:
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; import "@openzeppelin/contracts/access/AccessControl.sol"; contract Storage is AccessControl { bytes32 public constant ROLE_ADMIN = keccak256("ROLE_ADMIN"); bytes32 public constant ROLE_WRITER = keccak256("ROLE_WRITER"); constructor() { _grantRole(ROLE_ADMIN, msg.sender); } modifier onlyAdmin(){ require(hasRole(ROLE_ADMIN, msg.sender), "This function can only be executed by the admin"); _; } modifier onlyWriter() { require(hasRole(ROLE_WRITER, msg.sender), "This function can only be executed by a user"); _; } uint256 number; function grantWriter(address account) public onlyAdmin() { _grantRole(ROLE_WRITER,account); } function RevokeWriter(address account) public onlyAdmin() { _revokeRole(ROLE_WRITER,account); } function store(uint256 num) public onlyWriter { number = num; } function retrieve() public view returns (uint256){ return number; } }
Mi código:
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; import "@openzeppelin/contracts/access/AccessControl.sol"; contract Storage2 is AccessControl{ uint256 number; bytes32 public constant Admin = keccak256("Admin"); bytes32 public constant Writer = keccak256("Writer"); constructor() { _grantRole(Admin, msg.sender); } function store(uint256 num) public onlyWriter { number = num; } function retrieve() public view returns (uint256){ return number; } modifier onlyAdmin() { require(hasRole(Admin, msg.sender), "Esta funcion solo puede ser utilizada por el Admin"); _; } modifier onlyWriter() { require(hasRole(Writer, msg.sender), "Esta funcion solo puede ser utilizada por un Writer"); _; } function addWriter(address account) public onlyAdmin { _grantRole(Writer, account); } function popWriter(address account) public onlyAdmin { _revokeRole(Writer, account); } }
Mi pequeño aporte trate de practicar la herencia de paso
//SPDX-License-Identifier: GPL-3.0; pragma solidity >=0.7.0 < 0.9.0; import "@openzeppelin/contracts/access/AccessControl.sol"; import "./1_Storage.sol"; contract Desafio is AccessControl, Storage { bytes32 public constant ADMIN = keccak256("ADMIN"); bytes32 public constant WRITER = keccak256("WRITER"); constructor(){ _grantRole( ADMIN, msg.sender); } modifier OnlyAdmin(){ require(hasRole(ADMIN,msg.sender),"Solamente un administrador puede realizar esta accion"); _; } modifier OnlyWriter(){ require(hasRole(WRITER,msg.sender),"Solamente un writer puede realizar esta accion"); _; } function store(uint256 num) public OnlyWriter override { number = num; } function retrieve() public view override returns (uint256){ return number; } function addWriter(address user) OnlyAdmin public { _grantRole(WRITER,user); } function removeWriter(address user) OnlyAdmin public { _revokeRole(WRITER,user); } }
Me encantó esta clase! vamos por más retos!