Introdução
As plataformas de seguros agrícolas desempenham um papel vital ao ajudar os agricultores a gerir os riscos associados a condições climáticas adversas, infestações de pragas e outras circunstâncias imprevistas.
Este tutorial irá orientá-lo na criação de uma plataforma descentralizada de seguro agrícola na blockchain Celo, usando Solidity, Hardhat e JavaScript. Essa plataforma utilizará o Celo Dollar (cUSD) como meio de troca (exchange), e apenas os seguradores autorizados poderão segurar os agricultores.
Pré-requisitos
Antes de começarmos, certifique-se de ter o seguinte instalado:
Node.js e npm
Hardhat
Solidity versão 0.8.0 ou superior
Uma carteira Celo
Configurando o Ambiente de Desenvolvimento
Crie um novo diretório e inicialize o npm:
$ mkdir cropInsurance && cd cropInsurance
$ npm init -y
Em seguida, instale o Hardhat e outras dependências necessárias:
$ npm install --save-dev hardhat
$ npm install --save-dev @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers @openzeppelin/contracts
$ npm install dotenv --save
Inicialize o Hardhat:
$ npx hardhat
Siga os prompts para criar um projeto de amostra básico.
Escrevendo o Contrato Inteligente
Crie um novo arquivo na pasta de contratos, nomeie-o como CropInsurance.sol e adicione o seguinte código:
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/interfaces/IERC20.sol";
contract Insurance is ERC20 {
using SafeMath for uint256;
IERC20 public immutable cUSD;
uint256 constant minimumPremium = 50 ether;
uint256 constant minimumClaimInsurance = 1000 ether;
uint256 constant tokenRation = 20;
struct Members {
uint256 usdValue;
uint256 citValue;
uint256 premi;
uint256 registerDate;
uint256 lastPayment;
uint256 nextPayment;
}
mapping(address => Members) private _addressToMembers;
mapping(address => bool) private _addressStatus;
mapping (address => uint256[]) private _addressClaimHistory;
mapping (address => uint256[]) private _addressPaymentHistory;
event NewRegistration(address indexed members, uint256 timestamp);
constructor(address _cUSD)ERC20("CropInsuranceToken", "CIT") {
cUSD = IERC20(_cUSD);
}
function register(uint256 _usdAmount) external {
require(_usdAmount >= minimumPremium, "Insurance: Minimum premium is 50 cUSD");
require(!_addressStatus[msg.sender], "Insurance: You are already registered");
cUSD.transferFrom(msg.sender, address(this), _usdAmount);
uint256 _citAmount = _usdAmount.mul(tokenRation);
_addressToMembers[msg.sender] = Members(
_usdAmount,
_citAmount,
_usdAmount,
block.timestamp,
block.timestamp,
block.timestamp.add(30 days)
);
_addressStatus[msg.sender] = true;
_addressPaymentHistory[msg.sender].push(block.timestamp);
_mint(msg.sender, _citAmount);
emit NewRegistration(msg.sender, block.timestamp);
}
function claim(uint256 _citAmount) external {
require(_citAmount >= minimumClaimInsurance, "Insurance: Minimum claim is 1000 CIT");
require(_addressStatus[msg.sender], "Insurance: You are not registered");
uint256 _usdAmount = _citAmount.div(tokenRation);
Members storage members = _addressToMembers[msg.sender];
members.usdValue = members.usdValue.sub(_usdAmount);
members.citValue = members.citValue.sub(_citAmount);
_addressClaimHistory[msg.sender].push(block.timestamp);
_burn(msg.sender, _citAmount);
cUSD.transfer(msg.sender, _usdAmount);
}
function pay(uint256 _usdAmount) external {
require(_addressStatus[msg.sender], "Insurance: You are not registered");
Members storage members = _addressToMembers[msg.sender];
require(_usdAmount >= members.premi, "Insurance: Payment amount is less than the premium");
require(block.timestamp >= members.nextPayment, "Insurance: Payment is not due");
cUSD.transferFrom(msg.sender, address(this), _usdAmount);
uint256 _citAmount = _usdAmount.mul(tokenRation);
members.usdValue = members.usdValue.add(_usdAmount);
members.citValue = members.citValue.add(_citAmount);
members.lastPayment = block.timestamp;
members.nextPayment = block.timestamp.add(30 days);
_addressPaymentHistory[msg.sender].push(block.timestamp)
_mint(msg.sender, _citAmount);
}
function getInsurance() external view returns (Members memory) {
return _addressToMembers[msg.sender];
}
}
Este é um contrato Solidity chamado Insurance para um sistema de seguros onde os usuários podem se registrar, fazer pedidos de indenização e pagamentos em um token chamado cUSD (presumivelmente uma stablecoin atrelada ao dólar americano) e receber tokens chamados CropInsuranceToken (CIT) em troca. Esse sistema utiliza o padrão ERC20 para o CropInsuranceToken.
Vamos examinar cada seção:
- Pragma e Importações:
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/interfaces/IERC20.sol";
Esta seção define a versão do compilador Solidity a ser usada (0.8.15) e importa diversas bibliotecas do OpenZeppelin: SafeMath para operações matemáticas seguras, ERC20 para a funcionalidade básica do padrão ERC20 e IERC20 que é a interface para o padrão ERC20.
- Declaração do contrato:
contract Insurance is ERC20 {
Esta linha declara o contrato Insurence (Seguro) que estende o contrato ERC20. Isso dá ao Insurance todas as funcionalidades de um token ERC20.
- Variáveis globais:
using SafeMath for uint256;
IERC20 public immutable cUSD;
uint256 constant minimumPremium = 50 ether;
uint256 constant minimumClaimInsurance = 1000 ether;
uint256 constant tokenRation = 20;
A biblioteca SafeMath é usada para realizar operações matemáticas com segurança (evitando overflows e underflows). cUSD é uma instância pública e imutável de um token IERC20, que será inicializado no construtor e não poderá ser alterado posteriormente. O contrato também define três variáveis constantes: minimumPremium (50 ethers), minimumClaimInsurance (1000 ethers) e tokenRation (20 ethers).
- Structs e mapeamentos:
struct Members {
uint256 usdValue;
uint256 citValue;
uint256 premi;
uint256 registerDate;
uint256 lastPayment;
uint256 nextPayment;
}
mapping(address => Members) private _addressToMembers;
mapping(address => bool) private _addressStatus;
mapping (address => uint256[]) private _addressClaimHistory;
mapping (address => uint256[]) private _addressPaymentHistory;
A struct Members (membros) armazena informações sobre cada segurado, incluindo o valor de cUSD que eles contribuíram (usdValue), o valor do CIT que receberam (citValue), o valor do prêmio que devem pagar (premi), a data de registro (registerDate), a hora do último pagamento (lastPayment) e a hora do próximo pagamento (nextPayment).
Quatro mapeamentos são declarados: _addressToMembers mapeia um endereço para uma struct Members, _addressStatus mapeia um endereço para um bool indicando se o endereço está registrado ou não, _addressClaimHistory mapeia um endereço para um array de marcas temporais indicando quando eles fizeram reivindicações e _addressPaymentHistory mapeia um endereço a uma série de marcas temporais indicando quando eles fizeram pagamentos.
- Eventos:
event NewRegistration(address indexed members, uint256 timestamp);
Este evento é emitido sempre que ocorre um novo cadastro, informando o endereço do associado e a data e hora do evento.
- Construtor:
constructor(address _cUSD)ERC20("CropInsuranceToken", "CIT") {
cUSD = IERC20(_cUSD);
}
O construtor aceita um endereço _cUSD como parâmetro e o atribui a cUSD (que é um token IERC20). Esse endereço é o endereço do contrato do token cUSD. O contrato também chama o construtor do contrato ERC20, definindo o nome do token como “CropInsuranceToken” e o símbolo como “CIT”.
- Função de registro:
function register(uint256 _usdAmount) external {
// ... omitido por razões de brevidade
}
Esta função permite que um usuário se registre no seguro enviando _usdAmount de cUSD para o contrato. O usuário recebe _usdAmount vezes tokenRation de tokens CIT em troca. Várias condições devem ser atendidas para que a transação seja bem-sucedida: o valor enviado deve ser igual ou superior ao prêmio mínimo e o usuário ainda não deve estar cadastrado.
- Função de reivindicação:
function claim(uint256 _citAmount) external {
// ... omitido por razões de brevidade
}
Esta função permite que um usuário registrado faça uma reivindicação, queimando seus tokens CIT e recebendo cUSD em troca. O valor da reinvindicação no CIT deverá ser igual ou superior ao minimumClaimInsurance e o usuário deverá estar cadastrado.
- Função de pagamento:
function pay(uint256 _usdAmount) external {
// ... omitido por razões de brevidade
}
Esta função permite que um usuário registrado faça um pagamento em cUSD, o que aumenta seu usdValue e citValue nos valores apropriados. O valor do pagamento deve ser pelo menos o valor do prêmio e o pagamento deve ser devido (ou seja, a marca temporal atual deve ser maior ou igual ao nextPayment).
- Função getInsurance:
function getInsurance() external view returns (Members memory) {
return _addressToMembers[msg.sender];
}
Esta função permite que um usuário visualize os detalhes do seu seguro, retornando a struct Members associada ao seu endereço.
Observe que todas as funções deste contrato são externas, o que significa que só podem ser chamadas de fora do contrato. Além disso, este contrato pressupõe que a aprovação para transferências de tokens foi feita fora deste contrato, ou seja, os usuários aprovaram este contrato para gastar seus tokens cUSD.
Compilando o Contrato Inteligente
Compile o contrato usando Hardhat:
$ npx hardhat compile
Escrevendo testes
Em seguida, escreva testes para o contrato. Crie um novo arquivo no diretório de teste chamado CropInsuranceTest.js e adicione o seguinte código:
const { expect } = require("chai");
const { ethers } = require("hardhat");
const {
time,
loadFixture
} = require("@nomicfoundation/hardhat-network-helpers");
describe("Insurance", function(){
async function setup() {
const [ deployer, otherAccount ] = await ethers.getSigners();
// Carregar contrato
const Insurance = await ethers.getContractFactory("Insurance");
const Token = await ethers.getContractFactory("TestToken");
// Implantar contrato
const cUSD = await Token.deploy();
const insurance = await Insurance.deploy(await cUSD.getAddress());
// Solicitação de torneira OtherAccount
await cUSD.connect(otherAccount).faucet();
return { deployer, otherAccount, cUSD, insurance };
}
it("Register", async function(){
const { otherAccount, cUSD, insurance } = await loadFixture(setup);
const amount = ethers.parseEther("60");
// Registrar seguro
await cUSD.connect(otherAccount).approve(await insurance.getAddress(), amount);
await insurance.connect(otherAccount).register(amount);
// Verificar dados
const insuranceData = await insurance.connect(otherAccount).getInsurance();
expect(insuranceData[0]).to.be.equal(amount);
expect(insuranceData[1]).to.be.equal(ethers.parseEther("1200"));
expect(insuranceData[2]).to.be.equal(amount);
expect(await insurance.balanceOf(otherAccount.address)).to.be.equal(ethers.parseEther("1200"));
expect(await cUSD.balanceOf(await insurance.getAddress())).to.be.equal(ethers.parseEther("60"));
});
it("Claim", async function(){
const { otherAccount, cUSD, insurance } = await loadFixture(setup);
const amount = ethers.parseEther("60");
// Registrar seguro
await cUSD.connect(otherAccount).approve(await insurance.getAddress(), amount);
await insurance.connect(otherAccount).register(amount);
// Obter dados de seguro
const citAmount = await insurance.balanceOf(otherAccount.address);
const insuranceData = await insurance.connect(otherAccount).getInsurance();
// Aumentar o tempo
time.increaseTo(insuranceData[5]);
// Reivindicar
await insurance.connect(otherAccount).claim(citAmount);
const newInsuranceData = await insurance.connect(otherAccount).getInsurance();
expect(newInsuranceData[0]).to.be.equal(ethers.parseEther("0"));
expect(newInsuranceData[1]).to.be.equal(ethers.parseEther("0"));
expect(await insurance.balanceOf(otherAccount.address)).to.be.equal(ethers.parseEther("0"));
expect(await cUSD.balanceOf(await otherAccount.address)).to.be.equal(ethers.parseEther("200"));
});
it("Pay Insurance", async function(){
const { otherAccount, cUSD, insurance } = await loadFixture(setup);
const amount = ethers.parseEther("60");
// Registrar seguro
await cUSD.connect(otherAccount).approve(await insurance.getAddress(), amount);
await insurance.connect(otherAccount).register(amount);
// Obter dados de seguro
const citAmount = await insurance.balanceOf(otherAccount.address);
const insuranceData = await insurance.connect(otherAccount).getInsurance();
// Aumentar tempo
time.increaseTo(insuranceData[5]);
// Pagar seguro
await cUSD.connect(otherAccount).approve(await insurance.getAddress(), amount);
await insurance.connect(otherAccount).pay(amount);
const newInsuranceData = await insurance.connect(otherAccount).getInsurance();
expect(newInsuranceData[0]).to.be.equal(ethers.parseEther("120"));
expect(newInsuranceData[1]).to.be.equal(ethers.parseEther("2400"));
expect(await cUSD.balanceOf(await insurance.getAddress())).to.be.equal(ethers.parseEther("120"));
expect(await insurance.balanceOf(otherAccount.address)).to.be.equal(ethers.parseEther("2400"));
})
})
Este conjunto de testes verifica se o contrato segura corretamente um agricultor e permite que ele solicite o seguro. Substitua “0xcUSDCoinAddress” pelo endereço do contrato do token cUSD e “0xYourAddress” por um endereço real.
Você pode executar estes testes usando Hardhat:
$ npx hardhat test
Implantando o Contrato Inteligente
Após o teste, implante o contrato na rede Celo. Você precisa configurar uma rede na configuração do Hardhat e depositar em sua conta com cUSD.
No arquivo hardhat.config.js, adicione:
require("@nomicfoundation/hardhat-toolbox");
require("dotenv").config({ path: ".env" });
/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
solidity: {
version: "0.8.15",
settings: {
optimizer: {
enabled: true,
runs: 200,
},
},
},
networks:{
alfajores:{
url: process.env.RPC_URL,
chainId: 44787,
accounts: {
mnemonic: process.env.MNEMONIC,
path: "m/44'/60'/0'/0",
}
}
}
};
Por fim, crie um script de implantação em scripts/deploy.js:
const hre = require("hardhat")
async function main() {
const CropInsurance = await hre.ethers.getContractFactory("CropInsurance");
const cropInsurance = await CropInsurance.deploy();
await cropInsurance.deployed("<CUSD_ADDRESS>");
console.log("CropInsurance deployed to:", cropInsurance.address);
}
main()
.then(() => process.exit(0))
.catch(error => {
console.error(error);
process.exit(1);
});
Execute o comando abaixo para implantar o contrato:
npx hardhat run --network celo scripts/deploy.js
Agora você tem uma plataforma de seguro agrícola na blockchain Celo. Essa plataforma permite que um funcionário designado assegure as colheitas dos agricultores em troca de um prémio pago em cUSD. Os agricultores podem reivindicar o seu seguro em caso de perda, com todas as transações registradas de forma transparente na blockchain.
Aprimoramentos futuros podem incluir links para um oráculo meteorológico para reinvindicações automáticas com base em condições climáticas adversas.
Sobre o autor
Aborode Olusegun Isaac é um profissional de marketing em crescimento e analista de dados. Ele tem um grande interesse na Web3 e na descentralização que ela oferece.
Referência
Este artigo foi escrito por Olusegun e traduzido por Isabela Curado Nehme. Seu original pode ser lido aqui.
Latest comments (0)