Como complemento al tema de los Arrays:
Los métodos push y pop solo funcionan en los arrays dinámicos. Para agregar/cambiar el valor de un elemento en un array fijo usaremos los corchetes y la posición del elemento a cambiar. 😃
¿Qué son los Smart Contracts?
Qué aprenderás de Smart Contracts y Blockchain
Proyecto del curso: contrato para crowdfunding
¿Qué son los Smart Contracts?
Ethereum: la blockchain para crear contratos inteligentes
EVM (Ethereum Virtual Machine)
Solidity
¿Qué es Solidity?
Estructura de un contrato en Solidity
Variables de estado y variables globales
Estructuras de control en Solidity
Declaración de funciones en Solidity
Tipos de almacenamiento de datos: memory, storage y call data
Modificadores de funciones
Envío de notificaciones utilizando eventos
Manejo de errores en Solidity
Establecer tipos de datos personalizados con Struct Types
Qué son los enum types
Qué son los array y mappings
Desafió para el contrato de CrowdFunding
Desplegando nuestro smart contract
Actualización sobre testnet en Ethereum
Cómo desplegar nuestro contrato en Ropsten
Conclusión y siguientes pasos
Conclusión
No tienes acceso a esta clase
¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera
Amazon Web Services (AWS)
Un contrato inteligente puede almacenar cientos o miles de datos a lo largo de su vida útil. En estos casos debemos implementar estructuras de datos que nos permitan manipular gran cantidad de datos de forma fácil y organizada.
La primera estructura de datos, típica de cualquier lenguaje de programación, son los Arrays. Los arrays almacenan de forma secuencial datos de un mismo tipo y los mismos pueden ser accedidos a través de su índice o posición dentro del mismo. Pueden ser de una longitud determinada o de longitud dinámica dependiendo la necesidad.
// Array de números de hasta 3 posiciones
uint[3] numbers = [1, 2, 3];
// Array de números de longitud variable
uint[] numbers;
En cualquier tipo de array, puedes agregar o quitar elementos utilizando los métodos push(x)
y pop()
.
push(x)
agrega un nuevo elemento pasado como argumento al final del array, mientras que pop()
remueve el último elemento.
El acceso a estos datos debe realizarse a través del índice del elemento comenzando por cero. Si tenemos el array string words = ["Bienvenido", "a", "Platzi"]
, el elemento 2 corresponde al string “Platzi” y accedes a este con la sintaxis words[2]
.
Finalmente, puedes conocer la longitud de un array a través de su método myArr.length
. El mismo devolverá un número entero que representa el tamaño del mismo.
Los arrays tiene sus limitaciones. Si tenemos un array con miles de datos, acceder a un valor en el medio de este puede ser costoso. Tener que recorrer todo el mismo para encontrar un valor requiere procesamiento y, por ende, consumo de gas. Los Mappings solucionan este problema permitiendo asignar valores a una clave única para acceder al dato.
Similar a un objeto donde acceder a sus propiedades a través de un nombre específico. Los mappings permiten utilizar cualquier tipo de clave para acceder a un valor y el mismo puede crecer y guardar tantos valores como necesitemos.
La declaración de un mapping requiere de especificar el tipo de dato de la clave, y el tipo de dato del valor que esta guardará.
mapping(string => uint) public myMapping;
Uno de los usos más habituales de los mappings es para guardar el balance económico de una cuenta en el contrato. Cada clave guarda una address
y el valor es el valor en ETH que esta posee.
mapping(address => uint) public balances;
El contenido del mapping puedes imaginarlo de la siguiente manera.
balances = {
"direccion123": 1000,
"direccion456": 2000,
"direccion789": 5000
}
A través de direccion123
, direccion456
y direccion789
puedes acceder rápidamente al balance de cada cuenta y su llamado se realiza de la forma balances["direccion123"]
.
Los mappings son completamente dinámicos, pudiendo almacenar un mapping dentro de otro.
mapping(address => mapping(uint => bool)) public nested;
Aumentando así su complejidad y las posibilidades de guardar información.
Una desventaja de los mapping es que no permiten ser recorridos como un array u obtener su longitud. Para esto puedes hacer uso de extensiones y utilidades para darle más funcionalidad a esta estructura de datos si necesitas resolver casos de uso complejos.
Tanto los Arrays como los Mappings tienen sus ventajas y desventajas y pueden ser implementados para almacenar información. Comprender estas diferentes estructuras de datos que Solidity implementa nos permitirá tener más herramientas para el desarrollo contratos inteligentes, organizar la información y el acceso a estos.
Contribución creada por: Kevin Fiorentino (Platzi Contributor).
Aportes 16
Preguntas 4
Como complemento al tema de los Arrays:
Los métodos push y pop solo funcionan en los arrays dinámicos. Para agregar/cambiar el valor de un elemento en un array fijo usaremos los corchetes y la posición del elemento a cambiar. 😃
Un reto por cada clase ha sido una buena técnica, ya que se va practicando lo aprendido, se revisa, se prueba y vamos adelantando el proyecto. Me ha gustado mucho.
Array
uint[3] steps = [1,2,3];
uint[] steps;
steps.push(1) // [1]
steps.push(2) // [1,2]
steps.pop() // [1]
steps[0] //1
.
Mapping
mapping (string -> string) countries;
countries['Felipe'] = 'Colombia';
countries['Laura']= 'Ecuador';
countries['Felipe'] // 'Colombia';
countries['Laura'] // 'Ecuador';
Excelente Profe, claro, preciso y me gusta mucho la dinámica de poner retos y revisión entre clases.
Los Arrays son una colección ordenada de datos de un solo tipo, mientras los mappings son Hashmaps, es decir, estructuras de clave valor. El correcto uso de estas estructuras de datos mejora el rendimiento de nuestro contrato, disminuyendo la carga de computo. Por ejemplo, buscar un address en un array tiene una complejidad de O(N) mientras que en un mapping con el address como clave tiene una complejidad de O(1). Me corrigen si no estoy en lo correcto jajaja. Saludos y buen contenido
Este es mi avance con los arrays y las funciones nuevas del reto #7:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract Proyecto_Platzi{
enum State {Open, Closed}
struct projectInfo{
string name;
string description;
string owner;
address payable ownerWallet;
State state;
uint goal;
uint currentFund;
}
projectInfo[] projects;
mapping(string => uint) projectsContributions;
modifier onlyOwner(uint index) {
require(
msg.sender == projects[index].ownerWallet,
"Only owner can change the projects state"
);
//la función es insertada en donde aparece este simbolo
_;
}
modifier differentToTheOwner(uint index) {
require(
msg.sender != projects[index].ownerWallet,
"The owner can't fund the projects"
);
//la función es insertada en donde aparece este simbolo
_;
}
event ChangeState(
State previousState,
State newState
);
event FundValueGiven(
uint fundGiven,
uint valueToGoal,
string greetingMessage
);
function viewProjects(uint index) public view returns(projectInfo memory){
return projects[index];
}
function viewProjectFund(string calldata projectName) public view returns(uint){
return projectsContributions[projectName];
}
function createProject(string calldata _name, string calldata _owner, string calldata _description, uint _goal, State _state) public {
projectInfo memory newProject = projectInfo(_name, _description, _owner, payable(msg.sender), _state, _goal, 0);
projects.push(newProject);
}
function fundProject(uint ProjectIndex) public payable differentToTheOwner(ProjectIndex){
require(projects[ProjectIndex].state != State.Closed, "Sorry, this projects is closed and cannot receive funds");
require(msg.value > 0, "Sorry, fund value must be greater than 0, try again");
projects[ProjectIndex].ownerWallet.transfer(msg.value);
projects[ProjectIndex].currentFund += msg.value;
projectsContributions[projects[ProjectIndex].name] = projects[ProjectIndex].currentFund;
emit FundValueGiven(msg.value, (projects[ProjectIndex].goal - msg.value), "Thanks for your contribution");
}
function changeProjectState(State newState, uint ProjectIndex) public onlyOwner(ProjectIndex){
require(projects[ProjectIndex].state != newState, "Sorry, to change the state you must put a different state from the current one");
emit ChangeState(projects[ProjectIndex].state, newState);
projects[ProjectIndex].state = newState;
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
contract CrowdFunding {
enum State { Active, Inactive }
struct Contributors {
address payable contributor;
uint256 amount;
uint256 timestamp;
}
struct CrowdFundingEntity {
string projectName;
State state;
address payable author;
uint256 balance;
Contributors[] contributors;
}
CrowdFundingEntity public project;
constructor(string memory _projectName, State _state) {
project.projectName = _projectName;
project.state = _state;
project.author = payable(msg.sender);
}
error InvalidAmount (string message);
event NewContributionReceived (
address contributor,
uint256 amount,
uint256 balance
);
modifier onlyOwner () {
require(msg.sender != project.author, "Que raro mandarte dinero a ti mismo");
_;
}
function fundMe() public onlyOwner payable {
require(project.state == State.Active, "El proyecto ya no esta activo, lo hubieses hecho antes BRO.");
if (msg.value <= 0) {
revert InvalidAmount("Estas aportando 0");
}
project.author.transfer(msg.value);
project.balance += msg.value;
emit NewContributionReceived(msg.sender, msg.value, project.balance);
Contributors memory newContributor;
newContributor.contributor = payable(msg.sender);
newContributor.amount = msg.value;
newContributor.timestamp = block.timestamp;
project.contributors.push(newContributor);
}
function getContributors() public view returns (Contributors[] memory) {
return project.contributors;
}
function getBalance() public view returns (uint256) {
return project.balance;
}
function getProjectName() public view returns (string memory) {
return project.projectName;
}
function changeProjectState (State _newState) public {
project.state = _newState;
}
function changeProjectName (string memory _newProjectName) public {
require(bytes(_newProjectName).length >= 3, 'El nombre del proyecto es demasiado corto');
project.projectName = _newProjectName;
}
}
Un array es un array de acá a la China. No hay mucho que explicar.
Los Mapping utilizan el concepto de clave/valor, como si fuese un JSON o un Objeto al cual accedemos a sus datos a partir del nombre de las propiedades.
A diferencia de un objeto clásico de JavaScript cuya correspondencia sería un Struct, un mapping da una forma de objeto a un conjunto de datos regulares, que siempre tendrán el mismo tipo de Key para el mismo tipo de Value.
Un poco de documentacion 😃
Array:
https://solidity-by-example.org/array/
Mapping:
https://solidity-by-example.org/mapping/
Mapping
RESUMEN CLASE 17:
ARRAYS Y MAPPINGS
I.- ARRAYS
Almacenan datos de un tipo.
Su tamaño puede ser dinámico o fijo.
Se accede al valor usando un índice.
II.- MAPPINGS
Asocian una “llave” con un valor.
El valor se accede usando la llave.
Solo se pueden almacenar en el storage.
RETO 7:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract CrowdFunding {
enum FundraisingState {
Opened,
Closed
}
struct Contribution {
address contributor;
uint256 value;
}
struct Project {
string id;
string name;
string description;
address payable author;
FundraisingState state;
uint256 funds;
uint256 fundraisingGoal;
}
Project[] public projects;
mapping(string => Contribution[]) public contributions;
event ProjectCreated(
string projectId,
string name,
string description,
uint256 fundraisingGoal
);
event ProjectFunded(string projectId, uint256 value);
event ProjectStateChanged(string id, FundraisingState state);
modifier isAuthor(uint256 projectIndex) {
require(
projects[projectIndex].author == msg.sender,
"You need to be the project author"
);
_;
}
modifier isNotAuthor(uint256 projectIndex) {
require(
projects[projectIndex].author != msg.sender,
"As author you can not fund your own project"
);
_;
}
function createProject(
string calldata id,
string calldata name,
string calldata description,
uint256 fundraisingGoal
) public {
require(fundraisingGoal > 0, "fundraising goal must be greater than 0");
Project memory project = Project(
id,
name,
description,
payable(msg.sender),
FundraisingState.Opened,
0,
fundraisingGoal
);
projects.push(project);
emit ProjectCreated(id, name, description, fundraisingGoal);
}
function fundProject(uint256 projectIndex)
public
payable
isNotAuthor(projectIndex)
{
Project memory project = projects[projectIndex];
require(
project.state != FundraisingState.Closed,
"The project can not receive funds"
);
require(msg.value > 0, "Fund value must be greater than 0");
project.author.transfer(msg.value);
project.funds += msg.value;
projects[projectIndex] = project;
contributions[project.id].push(Contribution(msg.sender, msg.value));
emit ProjectFunded(project.id, msg.value);
}
function changeProjectState(FundraisingState newState, uint256 projectIndex)
public
isAuthor(projectIndex)
{
Project memory project = projects[projectIndex];
require(project.state != newState, "New state must be different");
project.state = newState;
projects[projectIndex] = project;
emit ProjectStateChanged(project.id, newState);
}
}
on.Este cambio a array si me quemó las pestañas jaja, pero ahí se los dejo por si a alguno le sirve la forma en la que lo abordé. Haciendo este ejercicio me di cuenta que al igual que en Java no se puede comparar los strings con el ==, que los emit sirven para debuggear y que para ver el contenido de un array hay que crear una función.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Crowdfunding {
//Array of projects
CrowdfundingProject [] projects;
//Mapping of donations
mapping (string => uint256) donations;
//Crowdfunding Project structure
struct CrowdfundingProject{
address payable projectAddress;
string projectName;
uint256 fundraisingGoal;
uint256 moneyReceived;
ProjectState projectState;
}
//Enum - Project States
enum ProjectState {Opened, Closed}
//Modifier to validate the project with given name belong to sender address
modifier onlyOwner(string calldata _projectName){
bool isProjectOwner = false;
for(uint i = 0; i < projects.length; i++){
if(keccak256(bytes(projects[i].projectName)) == keccak256(bytes(_projectName)) && projects[i].projectAddress == msg.sender){
isProjectOwner = true;
}
}
require(isProjectOwner == true, "Only owner can do this action");
_;
}
//Modifier to validate the project with given name does not belong to sender address
modifier notOwnProject(string calldata _projectName) {
bool isOwnProject = false;
for(uint i = 0; i < projects.length; i++){
if(keccak256(bytes(projects[i].projectName)) == keccak256(bytes(_projectName)) && projects[i].projectAddress == msg.sender){
isOwnProject = true;
}
}
require(isOwnProject == false, "Owner cannot fund his own project");
_;
}
//Event when a project is funded
event FundedProject(
address contributor,
uint256 donation
);
//Event when the project state changed
event ProjectStateChanged(
address author,
ProjectState newState
);
//Function to create a project
function createProject (string calldata projectName, uint256 _fundraisingGoal) public {
CrowdfundingProject memory crowdfundingProject = CrowdfundingProject(payable(msg.sender), projectName, _fundraisingGoal, 0, ProjectState.Opened);
projects.push(crowdfundingProject);
}
//Function to fund a project
function fundProject (string calldata _projectName) public notOwnProject (_projectName) payable{
require(bytes(_projectName).length>0, "The project name is mandatory");
require(msg.value > 0, "The donation should be greater than zero");
uint index;
bool foundProject = false;
CrowdfundingProject memory currentProject;
for(uint i = 0; i < projects.length; i++){
if(keccak256(bytes(projects[i].projectName)) == keccak256(bytes(_projectName))){
currentProject = projects[i];
foundProject = true;
index = i;
}
}
emit ProjectStateChanged(msg.sender, currentProject.projectState);
require(foundProject == true, "The project was not found");
require(currentProject.projectState == ProjectState.Opened, "Crodwfunding is closed");
require (currentProject.moneyReceived + msg.value <= currentProject.fundraisingGoal, "Fundarising is almost full. Fund with less money.");
currentProject.projectAddress.transfer(msg.value);
currentProject.moneyReceived = currentProject.moneyReceived + msg.value;
//Update array and mapping
projects[index] = currentProject;
donations[_projectName] = currentProject.moneyReceived;
emit FundedProject(msg.sender, msg.value);
}
//Function to change the project state
function changeProjectState(string calldata _projectName) public onlyOwner (_projectName) {
uint index;
CrowdfundingProject memory currentProject;
for(uint i = 0; i < projects.length; i++){
if(keccak256(bytes(projects[i].projectName)) == keccak256(bytes(_projectName))){
currentProject = projects[i];
index = i;
}
}
if(currentProject.projectState == ProjectState.Opened){
currentProject.projectState = ProjectState.Closed;
projects[index] = currentProject;
}else{
currentProject.projectState = ProjectState.Opened;
projects[index] = currentProject;
}
emit ProjectStateChanged(msg.sender, currentProject.projectState);
}
//Function to get all projects
function getAllProjects() public view returns (CrowdfundingProject [] memory) {
return projects;
}
//Function to get donations for a specific project
function getDonationsForProject(string calldata _projectName) public view returns (uint256) {
return donations[_projectName];
}
}
Este modifier puede servir para que no se ponga el mismo proyecto varias veces (con el project name como identificador)
modifier distinctProjects(string _name) {
bool isDistinct = true;
for (uint i = 0; i < projects.length; i++) {
if (projects[i].name == _name) {
isDistinct = false;
}
}
require(isDistinct, "This project already exists");
_;
}
Arrays
los arrays son como contenedores de tipos especifico
¿Quieres ver más aportes, preguntas y respuestas de la comunidad?