Hoje, falaremos sobre outra linguagem, a linguagem Solidity. Quando estudei essa linguagem pela primeira vez, fiquei um pouco receoso em relação a ela. Mas quanto mais me aprofundo, mais me divirto.
Seu sistema de Array é um pouco diferente de outras linguagens. Em outras linguagens (como PHP e JavaScript), você pode adicionar quantos elementos quiser na matriz, mas o Solidity tem algumas barreiras.
Não se preocupe, hoje não falarei sobre Array. Estamos criando um contrato de ingresso de evento. Vamos falar sobre a história do ingresso e depois trabalharemos.
Nesse contrato, qualquer usuário pode criar um ingresso. Quando o usuário cria um ingresso, o proprietário do contrato recebe uma comissão. Suponhamos que Jone crie um ingresso e que o limite de ingressos seja 15 e que o preço de cada ingresso seja de 2 Eth. Se ele vender 15 ingressos, receberá uma receita de 15*2 = 30 Eth. Mas aqui adicionamos alguma complexidade, como, por exemplo, quando outros usuários comprarem esse ingresso, o proprietário do contrato receberá alguma comissão.
Observação: Não estamos criando o aplicativo inteiro. Estamos apenas criando um contrato.
Este é o código completo do contrato. Vamos falar sobre essa função passo a passo.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
contract Ticket is ERC721URIStorage, Ownable {
using Counters for Counters.Counter;
Counters.Counter private tokenIdCounter;
struct TicketInfo {
uint256 tokenId;
uint256 totalTickets;
uint256 ticketsSold;
uint256 ticketPrice;
uint256 ticketStartDate;
uint256 ticketEndDate;
address creator;
bool ticketSold;
}
struct PurchaseInfo {
address buyer;
uint256 ticketsBought;
uint256 totalPrice;
uint256 ticketId;
uint256 purchaseId;
uint256 purchaseTimestamp;
}
uint256 public creationFeePercentage; // Percentual da taxa para criação do ingresso
uint256 public purchaseFeePercentage; // Percentual da taxa para compra do ingresso
mapping(uint256 => TicketInfo) public tickets;
mapping(address => uint256[]) public userTickets;
mapping(uint256 => PurchaseInfo[]) public ticketPurchases; // Mapeamento para armazenar informações de compra de cada ingresso.
event TicketCreated(
uint256 indexed tokenId,
uint256 totalTickets,
uint256 ticketPrice,
uint256 ticketStartDate,
uint256 ticketEndDate
);
event TicketPurchased(
uint256 indexed tokenId,
address buyer,
uint256 ticketsBought
);
constructor(uint256 _creationFeePercentage, uint256 _purchaseFeePercentage) ERC721("Ticket", "TICKET") {
creationFeePercentage = _creationFeePercentage;
purchaseFeePercentage = _purchaseFeePercentage;
}
function createTicket(
string calldata tokenURI,
uint256 _totalTickets,
uint256 _ticketPrice,
uint256 _ticketEndDate
) external payable {
require(_totalTickets > 0, "Total tickets must be greater than 0");
require(_ticketPrice > 0, "Ticket price must be greater than 0");
require(_ticketEndDate > block.timestamp, "Ticket end date must be in the future");
uint256 currentID = tokenIdCounter.current();
tokenIdCounter.increment();
_safeMint(msg.sender, currentID);
_setTokenURI(currentID, tokenURI);
uint256 ticketStartDate = block.timestamp;
tickets[currentID] = TicketInfo({
tokenId: currentID,
totalTickets: _totalTickets,
ticketsSold: 0,
ticketPrice: _ticketPrice,
ticketStartDate: ticketStartDate,
ticketEndDate: _ticketEndDate,
creator: msg.sender,
ticketSold: false
});
// Calcula a taxa de criação e transfere para o proprietário do contrato uint256 creationFee = creationFeePercentage;
require(msg.value == creationFee, "Incorrect creation fee sent");
// Transfere a taxa de criação para o proprietário do contrato
payable(owner()).transfer(creationFee);
emit TicketCreated(currentID, _totalTickets, _ticketPrice, ticketStartDate, _ticketEndDate);
}
function purchaseTicket(uint256 tokenID, uint256 ticketsToBuy) external payable {
TicketInfo storage ticket = tickets[tokenID];
require(!ticket.ticketSold, "Ticket has already been sold");
require(ticketsToBuy > 0 && ticketsToBuy <= ticket.totalTickets - ticket.ticketsSold, "Invalid number of tickets");
uint256 totalPrice = ticket.ticketPrice * ticketsToBuy;
uint256 purchaseFee = purchaseFeePercentage;
uint256 totalPriceWithFee = totalPrice + purchaseFee;
require(msg.value == totalPriceWithFee, "Incorrect amount sent");
// Transfere o preço do ingresso diretamente para o criador do ingresso
payable(ticket.creator).transfer(totalPrice);
// Transfere a taxa de compra para o proprietário do contrato payable(owner()).transfer(purchaseFee);
//Cunha os ingressos e registra as compras
for (uint256 i = 0; i < ticketsToBuy; i++) {
uint256 newTokenId = tokenIdCounter.current();
tokenIdCounter.increment();
_safeMint(msg.sender, newTokenId);
_setTokenURI(newTokenId, tokenURI(tokenID));
// Armazena o ingresso comprado para o usuário
userTickets[msg.sender].push(newTokenId);
// Armazena as informações de compra do ingresso
ticketPurchases[newTokenId].push(PurchaseInfo({
buyer: msg.sender,
ticketsBought: 1,
totalPrice: ticket.ticketPrice,
ticketId:tokenID,
purchaseId:newTokenId,
purchaseTimestamp: block.timestamp
}));
ticket.ticketsSold++;
emit TicketPurchased(newTokenId, msg.sender, ticketsToBuy);
}
// Marca o ingresso como vendido quando todos os ingressos são vendidos
if (ticket.ticketsSold == ticket.totalTickets) {
ticket.ticketSold = true;
}
}
function getUserTickets(address user) external view returns (uint256[] memory) {
return userTickets[user];
}
function getTicketInfo(uint256 tokenID) external view returns (TicketInfo memory) {
return tickets[tokenID];
}
function getPurchaseInfo(uint256 tokenID) external view returns (PurchaseInfo[] memory) {
return ticketPurchases[tokenID];
}
function updateCreationFeePercentage(uint256 _creationFeePercentage) external onlyOwner {
creationFeePercentage = _creationFeePercentage;
}
function updatePurchaseFeePercentage(uint256 _purchaseFeePercentage) external onlyOwner {
purchaseFeePercentage = _purchaseFeePercentage;
}
function getCreationFeePercentage() external view returns (uint256) {
return creationFeePercentage;
}
function getPurchaseFeePercentage() external view returns (uint256) {
return purchaseFeePercentage;
}
}
Passo-1:
Primeiro, importamos a classe de outro contrato necessário ou útil.
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
Passo-2:
Em seguida, declaramos nosso contrato, cujo nome é Ticket e usamos outros contratos de importação.
Passo-3:
Em seguida, definimos Counters e tokenIdCounter.
using Counters for Counters.Counter;
Counters.Counter private tokenIdCounter;
Passo-4:
Declaramos Struct ou Object
struct TicketInfo {
uint256 tokenId;
uint256 totalTickets;
uint256 ticketsSold;
uint256 ticketPrice;
uint256 ticketStartDate;
uint256 ticketEndDate;
address creator;
bool ticketSold;
}
struct PurchaseInfo {
address buyer;
uint256 ticketsBought;
uint256 totalPrice;
uint256 ticketId;
uint256 purchaseId;
uint256 purchaseTimestamp;
}
Passo-5:
A seguir, chame map para ler essa Struct ou Object
mapping(uint256 => TicketInfo) public tickets;
mapping(uint256 => PurchaseInfo[]) public ticketPurchases;
mapping(address => uint256[]) public userTickets;
Aqui você vê que estamos usando 3 mapas. Os dois primeiros mapas são lidos para a Struct TicketInfo e PurchaseInfo. E o terceiro mapa é um pouco complicado, pois quando o usuário compra esse ingresso, colocamos o endereço desse usuário nesse mapa. No futuro, poderemos descobrir facilmente quantos ingressos esse usuário comprou.
Passo-6:
Em seguida, declaramos e criamos o Ticket Fee e compramos o Ticket Fee.
uint256 public creationFeePercentage;
uint256 public purchaseFeePercentage;
Passo-7:
Em seguida, declaramos o evento para registro na blockchain.
event TicketCreated(
uint256 indexed tokenId,
uint256 totalTickets,
uint256 ticketPrice,
uint256 ticketStartDate,
uint256 ticketEndDate
);
event TicketPurchased(
uint256 indexed tokenId,
address buyer,
uint256 ticketsBought
);
Passo-8:
Aqui aplicamos o método construct. Quando o contrato é implantado, esse contrato recebe dois parâmetros.
constructor(uint256 _creationFeePercentage, uint256 _purchaseFeePercentage) ERC721("Ticket", "TICKET") {
creationFeePercentage = _creationFeePercentage;
purchaseFeePercentage = _purchaseFeePercentage;
}
Passo-9:
Desta vez, estamos trabalhando com o método createTicket
function createTicket(
string calldata tokenURI,
uint256 _totalTickets,
uint256 _ticketPrice,
uint256 _ticketEndDate
) external payable {
require(_totalTickets > 0, "Total tickets must be greater than 0");
require(_ticketPrice > 0, "Ticket price must be greater than 0");
require(_ticketEndDate > block.timestamp, "Ticket end date must be in the future");
uint256 currentID = tokenIdCounter.current();
tokenIdCounter.increment();
_safeMint(msg.sender, currentID);
_setTokenURI(currentID, tokenURI);
uint256 ticketStartDate = block.timestamp;
tickets[currentID] = TicketInfo({
tokenId: currentID,
totalTickets: _totalTickets,
ticketsSold: 0,
ticketPrice: _ticketPrice,
ticketStartDate: ticketStartDate,
ticketEndDate: _ticketEndDate,
creator: msg.sender,
ticketSold: false
});
// Calcula a taxa de criação e transfere para o proprietário do contrato
uint256 creationFee = creationFeePercentage;
require(msg.value == creationFee, "Incorrect creation fee sent");
// Transfer a taxa de criação para o proprietário do contrato
payable(owner()).transfer(creationFee);e
emit TicketCreated(currentID, _totalTickets, _ticketPrice, ticketStartDate, _ticketEndDate);
}
Este método precisa de três parâmetros.
tokenURI
_totalTickets
_ticketPrice
_ticketEndDate
Aqui, tokenURI é um link de url. Normalmente, quando usamos metadados, como nome do ingresso, categoria, etc., e imagem, usamos o IPFS. E outros parâmetros que já conhecemos.
Nessa função, criamos algumas condições antes de criar um ingresso.
require(_totalTickets > 0, "Total tickets must be greater than 0");
require(_ticketPrice > 0, "Ticket price must be greater than 0");
require(_ticketEndDate > block.timestamp, "Ticket end date must be in the future");
Em seguida, pegamos o currentID e aumentamos esse currentID. Depois disso, vamos cunhar; então usamos _safeMint e _setTokenURI.
uint256 currentID = tokenIdCounter.current();
tokenIdCounter.increment();
_safeMint(msg.sender, currentID);
_setTokenURI(currentID, tokenURI);
uint256 ticketStartDate = block.timestamp;
tickets[currentID] = TicketInfo({
tokenId: currentID,
totalTickets: _totalTickets,
ticketsSold: 0,
ticketPrice: _ticketPrice,
ticketStartDate: ticketStartDate,
ticketEndDate: _ticketEndDate,
creator: msg.sender,
ticketSold: false
});
Em seguida, enviamos uma pequena quantia para o proprietário do contrato como uma taxa. No final, criamos um evento para a blockchain de registro.
// Calcula a taxa de criação e transfere para o proprietário do contrato
uint256 creationFee = creationFeePercentage;
require(msg.value == creationFee, "Incorrect creation fee sent");
// Transfere a taxa de criação para o proprietário do contrato
payable(owner()).transfer(creationFee);
emit TicketCreated(currentID, _totalTickets, _ticketPrice, ticketStartDate, _ticketEndDate);
Passo-10:
Agora estamos trabalhando com o método purchaseTicket.
function purchaseTicket(uint256 tokenID, uint256 ticketsToBuy) external payable {
TicketInfo storage ticket = tickets[tokenID];
require(!ticket.ticketSold, "Ticket has already been sold");
require(ticketsToBuy > 0 && ticketsToBuy <= ticket.totalTickets - ticket.ticketsSold, "Invalid number of tickets");
uint256 totalPrice = ticket.ticketPrice * ticketsToBuy;
uint256 purchaseFee = purchaseFeePercentage;
uint256 totalPriceWithFee = totalPrice + purchaseFee;
require(msg.value == totalPriceWithFee, "Incorrect amount sent");
// Transfere o preço do ingresso diretamente para o criador do ingresso
payable(ticket.creator).transfer(totalPrice);
// Transfere a taxa de compra para o proprietário do ingresso
payable(owner()).transfer(purchaseFee);
// Cunha ingressos e registra compras
for (uint256 i = 0; i < ticketsToBuy; i++) {
uint256 newTokenId = tokenIdCounter.current();
tokenIdCounter.increment();
_safeMint(msg.sender, newTokenId);
_setTokenURI(newTokenId, tokenURI(tokenID));
// Armazena o ingresso comprado para o usuário
userTickets[msg.sender].push(newTokenId);
// Armazena a informação da compra para o ingresso
ticketPurchases[newTokenId].push(PurchaseInfo({
buyer: msg.sender,
ticketsBought: 1,
totalPrice: ticket.ticketPrice,
ticketId:tokenID,
purchaseId:newTokenId,
purchaseTimestamp: block.timestamp
}));
ticket.ticketsSold++;
emit TicketPurchased(newTokenId, msg.sender, ticketsToBuy);
}
// Marca o ingresso como vendido quando todos os ingressos são vendidos
if (ticket.ticketsSold == ticket.totalTickets) {
ticket.ticketSold = true;
}
}
Este método aceita dois parâmetros.
tokenID
ticketsToBuy (quantos ingressos)
Primeiro, encontre as informações e armazene como TicketInfo. Em seguida, criamos algumas condições antes de comprar os ingressos.
TicketInfo storage ticket = tickets[tokenID];
require(!ticket.ticketSold, "Ticket has already been sold");
require(ticketsToBuy > 0 && ticketsToBuy <= ticket.totalTickets - ticket.ticketsSold, "Invalid number of tickets");
Em seguida, fazemos alguns cálculos e novamente criamos condições.
uint256 totalPrice = ticket.ticketPrice * ticketsToBuy;
uint256 purchaseFee = purchaseFeePercentage;
uint256 totalPriceWithFee = totalPrice + purchaseFee;
require(msg.value == totalPriceWithFee, "Incorrect amount sent");
Então, o proprietário do ingresso e o proprietário do contrato recebem uma taxa pela compra dos ingressos.
// Transfere o preço do ingresso diretamente para o criador do ingresso
payable(ticket.creator).transfer(totalPrice);
// Transfere a taxa de compra para o proprietário do contrato
payable(owner()).transfer(purchaseFee);
Em seguida, cunhamos esse ingresso de compra e registramos todos os dados necessários.
// Cunha ingressos e registra compras
for (uint256 i = 0; i < ticketsToBuy; i++) {
uint256 newTokenId = tokenIdCounter.current();
tokenIdCounter.increment();
_safeMint(msg.sender, newTokenId);
_setTokenURI(newTokenId, tokenURI(tokenID));
// Armazena o ingresso comprado para o usuário
userTickets[msg.sender].push(newTokenId);
// Armazena a informação da compra para o ingresso
ticketPurchases[newTokenId].push(PurchaseInfo({
buyer: msg.sender,
ticketsBought: 1,
totalPrice: ticket.ticketPrice,
ticketId:tokenID,
purchaseId:newTokenId,
purchaseTimestamp: block.timestamp
}));
ticket.ticketsSold++;
emit TicketPurchased(newTokenId, msg.sender, ticketsToBuy);
}
// Marca o ingresso como vendido quando todos os ingressos são vendidos
if (ticket.ticketsSold == ticket.totalTickets) {
ticket.ticketSold = true;
}
Passo-11:
Crie uma função getter.
function getUserTickets(address user) external view returns (uint256[] memory) {
return userTickets[user];
}
function getTicketInfo(uint256 tokenID) external view returns (TicketInfo memory) {
return tickets[tokenID];
}
function getPurchaseInfo(uint256 tokenID) external view returns (PurchaseInfo[] memory) {
return ticketPurchases[tokenID];
}
function getCreationFeePercentage() external view returns (uint256) {
return creationFeePercentage;
}
function getPurchaseFeePercentage() external view returns (uint256) {
return purchaseFeePercentage;
}
Passo-12:
Agora criamos algumas funções para futura atualização de cobranças.
function getCreationFeePercentage() external view returns (uint256) {
return creationFeePercentage;
}
function getPurchaseFeePercentage() external view returns (uint256) {
return purchaseFeePercentage;
}
Parabéns, concluímos nosso contrato simples. Agora estamos fazendo alguns testes. Para testar, estamos usando o editor remix on-line. Aqui está o link
Passo-13:
Testando….
Esta imagem de uma pequena caixa vermelha no lado esquerdo é o botão de implantação. Ao clicar nesse botão, você verá o seguinte. Agora, clique na caixa vermelha grande dentro do botão de seta.
Em seguida, depositamos um valor. Aqui eu coloquei 1000000000000000000 Wei.
Nota: 1000000000000000000 Wei = 1 Eth.
Em seguida, clique novamente no botão de seta. Agora podemos ver o botão Deploy. Clique nesse botão para implantar o contrato.
Ao implantar o contrato, vemos alguns métodos de teste como este.
Agora mudamos o usuário
Aqui vemos 15 usuários para testar o contrato. O primeiro usuário é o proprietário do contrato. Quando o contrato é implantado, o sistema recebe algum gas. Por isso, o saldo do primeiro usuário não é de 100 ether, mas os outros usuários têm 100 ether. Agora mudamos o segundo usuário que cria um ingresso para seu evento.
Aqui você vê o botão createTicket [2ª caixa vermelha]. Nesse botão, no lado direito, você vê um botão de seta. Clique nesse botão e você verá um campo de entrada. Preencha esse campo e, em seguida, preencha o campo de valor [1ª caixa vermelha] e altere o tipo de moeda. Esse campo de valor é o campo de cobrança. Quando o usuário cria um ingresso, o proprietário do contrato recebe uma comissão. Agora clique no botão createTicket
Criamos o ingresso com sucesso. Nosso primeiro tokenId de ingresso é 0.
Você já deve ter percebido que o saldo do segundo usuário está diminuindo por criar ingressos e o saldo do primeiro usuário está aumentando por receber uma comissão.
Agora estamos mudando o usuário. Aqui, o terceiro usuário está comprando um ingresso. Vamos fazer isso.
Aqui, a terceira caixa é o botão do método purchaseTicket. Com esse botão, no lado direito, você verá uma seta. Clique nessa seta e verá dois campos de entrada. Preencha esse campo de entrada. Aqui compramos 2 ingressos e o tokenID é 0.
Na segunda caixa vermelha, definimos o valor 3. Porque compramos 2 ingressos e o preço de cada um é 1 eth e a comissão é 1 eth. Portanto, o preço total é 3 eth. Agora clique no botão purchaseTicket.
Agora, podemos ver as informações do ingresso de compra.
Aqui, o primeiro usuário é o proprietário do contrato, o segundo usuário é o proprietário do ingresso e o terceiro usuário está comprando um ingresso. Quando um ingresso é criado ou vendido, o primeiro usuário recebe uma comissão. O segundo usuário é o proprietário do evento e, quando um ingresso é vendido, o segundo usuário recebe o valor de venda.
Agora é hora de implantá-lo em uma rede Ethereum ao vivo ou em uma rede Testnet e compartilhá-lo com o mundo!
Bom trabalho! 🚀
Esse artigo foi escrito por syed kamruzzaman e traduzido por Fátima Lima. O original pode ser lido aqui.
Latest comments (0)