WEB3DEV

Cover image for Tutorial de Solidity Intermedi√°rio | Construindo em Blocos DeFi Lego com Hardhat ūüĎ∑
Paulo Gio
Paulo Gio

Posted on • Atualizado em

Tutorial de Solidity Intermedi√°rio | Construindo em Blocos DeFi Lego com Hardhat ūüĎ∑

Neste tutorial de Solidity intermediário, estarei construindo, testando e implantando um contrato inteligente para reequilibrar um portfólio de ativos digitais. A ideia é ver como podemos trabalhar com contratos inteligentes externos para começar a construir nossos próprios produtos nos blocos DeFi Lego.

  • O Desafio
  • Tutorial de Solidity Intermedi√°rio [V√≠deo]
  • O Ambiente de Desenvolvimento
  • Guia de In√≠cio R√°pido
  • Tutorial do C√≥digo Solidity
  • Testando Contratos Inteligentes Solidity
  • Implantando e Utilizando o Hardhat
  • Verifica√ß√Ķes de Seguran√ßa do Solidity

O Desafio

Criar um contrato inteligente Solidity para manter e reequilibrar um portfólio de ativos digitais semelhante a um portfólio de criptomoedas 60/40 sobre o qual falei no passado.

Isso permitirá que os fundos sejam enviados ao contrato pelo proprietário, momento em que podem ser reequilibrados, chamando uma função do contrato. Isso executará uma transação em uma exchange descentralizada, especificamente Uniswap v3.

Precisaremos de alguns feeds de preços de oráculos externos e uma maneira de retirar fundos do cofre.

Este é um tutorial de Solidity intermediário que fluirá relativamente rápido e cobrirá muitos dos conceitos básicos de Solidity abordados neste tutorial introdutório: https://jamesbachini.com/solidity-tutorial/

Vídeo Tutorial de Solidity Intermediário

O Ambiente de Desenvolvimento

Decidi usar o Hardhat em vez do Truffle neste tutorial, pois sinto que ele fornece uma estrutura rica em recursos e não fiz um tutorial com ele antes. Você também precisará de NodeJS, Metamask e uma chave da API Alchemy.

npm install hardhat
Enter fullscreen mode Exit fullscreen mode

Meu arquivo de configuração do Hardhat se parece com isso:

require("@nomiclabs/hardhat-waffle");
require("@nomiclabs/hardhat-etherscan");

const ethers = require('ethers');
const credentials = require('./credentials.js');

/**
 * @type import('hardhat/config').HardhatUserConfig
 */
module.exports = {
  solidity: "0.8.4",
  networks: {
    kovan: {
      url: `https://eth-kovan.alchemyapi.io/v2/${credentials.alchemy}`,
      accounts: [credentials.privateKey],
    },
    local: {
      url: `http://127.0.0.1:8545/`,
      accounts: [credentials.privateKey],
    },
  },
  etherscan: {
    apiKey: credentials.etherscan
  }
};
Enter fullscreen mode Exit fullscreen mode

Tudo bem padrão, observe que estou armazenando a chave privada da testnet (com fundos) e as chaves da API Alchemy em um arquivo chamado credenciais.js que não está incluído no repositório GitHub.

Falando nisso, o código completo pelo qual passaremos está aqui:- https://github.com/jamesbachini/myVault

Podemos clonar este repositório usando o seguinte comando:

git clone https://github.com/jamesbachini/myVault.git
Enter fullscreen mode Exit fullscreen mode

Guia de Início Rápido

Os comandos a seguir criar√£o, testar√£o e implantar√£o os contratos.

git clone https://github.com/jamesbachini/myVault.git
cd myVault
mv credentials-example.js credentials.js
code credentials.js (Insira o endereço da carteira da testnet Kovan com fundos ETH e chaves API Alchemy/Etherscan)
npm install
npx hardhat compile
npx hardhat node --fork https://eth-kovan.alchemyapi.io/v2/YourAlchemyAPIKeyHere
npx hardhat test --network local
npx hardhat run scripts/deploy.js --network kovan
Enter fullscreen mode Exit fullscreen mode

Se n√£o funcionar ou n√£o fizer sentido, continue lendo.

Tutorial do Código Solidity

Come√ßamos definindo a vers√£o da licen√ßa e do Solidity. Observe que estamos usando a vers√£o 8 ou superior. O Solidity v8 ou superior √© significativo do ponto de vista da seguran√ßa, pois a equipe introduziu uma s√©rie de verifica√ß√Ķes para evitar overflows de integers. Esta √© a raz√£o pela qual n√£o estou usando bibliotecas Safemath em todo o c√≥digo.

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
Enter fullscreen mode Exit fullscreen mode

Em seguida, importamos algumas bibliotecas do OpenZeppelin e UniswapV3.

‚Äč‚Äčimport "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import '@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol';
import '@uniswap/v3-periphery/contracts/interfaces/IQuoter.sol';
Enter fullscreen mode Exit fullscreen mode

Finalmente e por √ļltimo, antes de ficarmos presos no contrato, vamos precisar de algumas interfaces: 1. Uma para um or√°culo Chainlink obter o pre√ßo do ETH. 2. Uma para retornar qualquer ETH restante das transa√ß√Ķes Uniswap. 3. A interface final √© para adicionar uma fun√ß√£o de dep√≥sito √† interface ERC20 padr√£o a fim de converter ETH > WETH.

// EACAggregatorProxy é usado para oráculo Chainlink
interface EACAggregatorProxy {
  function latestAnswer() external view returns (int256);
}

// Interface Uniswap v3 
interface IUniswapRouter is ISwapRouter {
  function refundETH() external payable;
}

// Adiciona função de depósito para WETH
interface DepositableERC20 is IERC20 {
  function deposit() external payable;
}
Enter fullscreen mode Exit fullscreen mode

Observe que podemos descobrir como declarar fun√ß√Ķes para interfaces em contratos externos usando o c√≥digo verificado no Etherscan.

https://jamesbachini.com/wp-content/uploads/2021/09/Screenshot-2021-09-25-at-15-39-31-WETH9_-0xd0A1E359811322d97991E03f863a0C30C2cF029C-768x345.png

O contrato é então definido e alguns endereços são codificados rigidamente no contrato. Eles também podem ser fornecidos para a função constructor, o que seria mais limpo ao mover para a rede principal, mas neste exemplo o código não está chegando perto da rede principal, portanto, codificá-los rigidamente no contrato é aceitável.

contract myVault {
  uint public version = 1;

  /* Endereços Kovan*/
  address public daiAddress = 0x4F96Fe3b7A6Cf9725f59d353F723c1bDb64CA6Aa;
  address public wethAddress = 0xd0A1E359811322d97991E03f863a0C30C2cF029C;
  address public uinswapV3QuoterAddress = 0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6;
  address public uinswapV3RouterAddress = 0xE592427A0AEce92De3Edee1F18E0157C05861564;
  address public chainLinkETHUSDAddress = 0x9326BFA02ADD2366b30bacB125260Af641031331;
Enter fullscreen mode Exit fullscreen mode

Você pode usar a Metamask e o Etherscan para encontrar a maioria desses endereços. Basta realizar uma transação na versão testnet do Uniswap v3 e seguir os rastros da transação no Etherscan. Há uma grande lista de endereços de feeds de dados da Chainlink aqui: https://docs.chain.link/docs/ethereum-addresses/

Observe que estamos usando WETH aqui, que é uma versão encapsulada do ETH. Como o ETH é a moeda/token nativo da rede principal da Ethereum, ele não se comporta da mesma maneira que um token ERC20. Portanto, o ETH encapsulado é apenas um contrato de token ERC20 no qual você pode depositar ETH e obter WETH, um ERC20 compatível atrelado a 1:1 com ETH, em troca.

O próximo passo é definir algumas variáveis de estado.

uint public ethPrice = 0;
uint public usdTargetPercentage = 40;
uint public usdDividendPercentage = 25; // 25% de 40% = 10% de saque anual
uint private dividendFrequency = 5 minutes; // mude para 1 ano para produção
uint public nextDividendTS;
address public owner;
Enter fullscreen mode Exit fullscreen mode

E ent√£o algumas interfaces e um evento de log b√°sico.

using SafeERC20 for IERC20;
using SafeERC20 for DepositableERC20;

IERC20 daiToken = IERC20(daiAddress);
DepositableERC20 wethToken = DepositableERC20(wethAddress);
IQuoter quoter = IQuoter(uinswapV3QuoterAddress);
IUniswapRouter uniswapRouter = IUniswapRouter (uinswapV3RouterAddress);

event myVaultLog(string msg, uint ref);
Enter fullscreen mode Exit fullscreen mode

O código acima configura uma interface SafeERC20 para o daiToken e para o wethToken. Observe que a interface WETH é configurada usando a interface DepositableERC20 que criamos anteriormente com a função de depósito extra. Em seguida, configuramos uma interface de cotação para obter dados de preços do Uniswap e uma interface uniswapRouter para fazer as trocas de tokens.

Vamos agora criar uma função constructor que será acionada apenas uma vez enquanto o contrato estiver sendo implantado.

constructor() {
  console.log('Implantando myVault Vers√£o:', version);
  nextDividendTS = block.timestamp + dividendFrequency;
  owner = msg.sender;
}
Enter fullscreen mode Exit fullscreen mode

Observe que definimos duas variáveis na função constructor

  • A vari√°vel nextDividendTS ser√° a data futura em que uma retirada pode ocorrer. Medido como um timestamp Unix, que √© o n√ļmero de segundos desde 1¬ļ de janeiro de 1970
  • Com a vari√°vel owner definiremos o endere√ßo do propriet√°rio para o endere√ßo que implantou o contrato e pagou as taxas de gas. Este ser√° o mesmo endere√ßo que est√° em credentials.js

Eu quero poder obter o saldo da conta de WETH e DAI, ent√£o vamos criar fun√ß√Ķes para isso.

function getDaiBalance() public view returns(uint) {
  return daiToken.balanceOf(address(this));
}

function getWethBalance() public view returns(uint) {
  return wethToken.balanceOf(address(this));
}
Enter fullscreen mode Exit fullscreen mode

Eu também quero poder obter o valor total em USD da conta (o que exigirá um oráculo de preço ETH que ainda não foi configurado).

function getTotalBalance() public view returns(uint) {
  require(ethPrice > 0, 'Valor do ETH ainda n√£o foi definido');
  uint daiBalance = getDaiBalance();
  uint wethBalance = getWethBalance();
  uint wethUSD = wethBalance * ethPrice; // assume que ambos os ativos têm 18 decimais
  uint totalBalance = wethUSD + daiBalance;
  return totalBalance;
}
Enter fullscreen mode Exit fullscreen mode

Este é o primeiro exemplo de uma instrução require, que verifica se definimos o ethPrice usando um serviço de oráculo antes da getTotalBalance() ser chamada.

Ent√£o, vamos criar algumas fun√ß√Ķes diferentes para interagir com os feeds de dados do or√°culo. O primeiro usar√° a interface de cota√ß√£o da Uniswap para obter um pre√ßo diretamente da exchange descentralizada. O segundo usar√° o feed de dados de pre√ßos da Chainlink, que simplesmente cita um valor em d√≥lares para o ETH.

function updateEthPriceUniswap() public returns(uint) {
  uint ethPriceRaw = quoter.quoteExactOutputSingle(daiAddress,wethAddress,3000,100000,0);
  ethPrice = ethPriceRaw / 100000;
  return ethPrice;
}

function updateEthPriceChainlink() public returns(uint) {
  int256 chainLinkEthPrice = EACAggregatorProxy(chainLinkETHUSDAddress).latestAnswer();
  ethPrice = uint(chainLinkEthPrice / 100000000);
  return ethPrice;
}
Enter fullscreen mode Exit fullscreen mode

√Č bastante √ļtil na testnet ter dois or√°culos de pre√ßos diferentes porque a falta de arbitragem na testnet DEX significa que eles fornecer√£o respostas totalmente diferentes, o que √© √≥timo para testar os efeitos dos movimentos de pre√ßos.

Agora queremos criar uma função para trocar DAI por WETH usando a função exactInputSingle do Uniswap V3.

function buyWeth(uint amountUSD) internal {
  uint256 deadline = block.timestamp + 15;
  uint24 fee = 3000;
  address recipient = address(this);
  uint256 amountIn = amountUSD; // inclui 18 decimais
  uint256 amountOutMinimum = 0;
  uint160 sqrtPriceLimitX96 = 0;
  emit myVaultLog('amountIn', amountIn);
  require(daiToken.approve(address(uinswapV3RouterAddress), amountIn), 'Aprovação DAI falhou');
  ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams(
    daiAddress,
    wethAddress,
    fee,
    recipient,
    deadline,
    amountIn,
    amountOutMinimum,
    sqrtPriceLimitX96
  );
  uniswapRouter.exactInputSingle(params);
  uniswapRouter.refundETH();
}
Enter fullscreen mode Exit fullscreen mode

Deadline (prazo) é definido como 15 segundos a partir do timestamp do bloco. Fee (taxa) é definida para o pool padrão de 0,3%. AmountIn (Montante) inclui os 18 decimais para o DAI e é um montante de USD em ETH para comprar o WETH. Não há uma definição de retorno mínimo, o que abre para a possibilidade de derrapagem e front running. Novamente, isso não é um problema na rede de testes Kovan.

Em seguida, emitimos uma entrada ao log, para o valor amountIn, principalmente para depuração.

O código passa a aprovar o endereço do contrato do roteador Uniswap para gastar o valor exato de tokens DAI. Então, finalmente, executamos a troca dos tokens.

O próximo trecho de código faz a mesma coisa, mas vende WETH por DAI usando a função exactOutputSingle.

function sellWeth(uint amountUSD) internal {
  uint256 deadline = block.timestamp + 15;
  uint24 fee = 3000;
  address recipient = address(this);
  uint256 amountOut = amountUSD; // inclui 18 decimais
  uint256 amountInMaximum = 10 ** 28 ;
  uint160 sqrtPriceLimitX96 = 0;
  require(wethToken.approve(address(uinswapV3RouterAddress), amountOut), 'Aprovação WETH falhou');
  ISwapRouter.ExactOutputSingleParams memory params = ISwapRouter.ExactOutputSingleParams(
    wethAddress,
    daiAddress,
    fee,
    recipient,
    deadline,
    amountOut,
    amountInMaximum,
    sqrtPriceLimitX96
  );
  uniswapRouter.exactOutputSingle(params);
  uniswapRouter.refundETH();
}
Enter fullscreen mode Exit fullscreen mode

Agora que temos essas fun√ß√Ķes de compra/venda, podemos criar uma fun√ß√£o para reequilibrar o portf√≥lio para as porcentagens de 60/40.

function rebalance() public {
  require(msg.sender == owner, "Somente o propriet√°rio pode reequilibrar sua conta");
  uint usdBalance = getDaiBalance();
  uint totalBalance = getTotalBalance();
  uint usdBalancePercentage = 100 * usdBalance / totalBalance;
  emit myVaultLog('usdBalancePercentage', usdBalancePercentage);
  if (usdBalancePercentage < usdTargetPercentage) {
    uint amountToSell = totalBalance / 100 * (usdTargetPercentage - usdBalancePercentage);
    emit myVaultLog('amountToSell', amountToSell);
    require (amountToSell > 0, "Nada para vender");
    sellWeth(amountToSell);
  } else {
    uint amountToBuy = totalBalance / 100 * (usdBalancePercentage - usdTargetPercentage);
    emit myVaultLog('amountToBuy', amountToBuy);
    require (amountToBuy > 0, "Nada para comprar");
    buyWeth(amountToBuy);
  }
}
Enter fullscreen mode Exit fullscreen mode

Observe que ao calcular porcentagens, usamos uma f√≥rmula de tr√°s para frente para evitar decimais devido √† declara√ß√£o de tipo unsigned integer. Os integers s√≥ podem lidar com n√ļmeros inteiros.

Tamb√©m vou criar uma fun√ß√£o para sacar um dividendo anual de 10% da estrat√©gia. Isso seria √ļtil se estivesse sendo configurado como um fundo fiduci√°rio perp√©tuo ou portf√≥lio de contribui√ß√Ķes de caridade.

function annualDividend() public {
  require(msg.sender == owner, "Somente o propriet√°rio pode sacar da sua conta");
  require(block.timestamp > nextDividendTS, 'O dividendo ainda não é devido');
  uint balance = getDaiBalance();
  uint amount = (balance * usdDividendPercentage) / 100;
  daiToken.safeTransfer(owner, amount);
  nextDividendTS = block.timestamp + dividendFrequency;
}
Enter fullscreen mode Exit fullscreen mode

A função permite ao proprietário sacar 25% do saldo de DAI. O método daiToken.safeTransfer() é usado para enviar fundos ERC20 para a carteira do proprietário. Usamos a variável block.timestamp para atualizar quando o próximo dividendo vence.

Para fins de teste, também quero uma maneira de fechar a conta e remover todos os fundos.

function closeAccount() public {
  require(msg.sender == owner, "Somente o propriet√°rio pode encerrar sua conta");
  uint daiBalance = getDaiBalance();
  if (daiBalance > 0) {
    daiToken.safeTransfer(owner, daiBalance);
  }
  uint wethBalance = getWethBalance();
  if (wethBalance > 0) {
    wethToken.safeTransfer(owner, wethBalance);
  }
}
Enter fullscreen mode Exit fullscreen mode

Isso transfere os saldos de DAI e WETH para o propriet√°rio.

A √ļltima coisa que queremos fazer √© permitir que o ETH seja enviado ao contrato e convertido em WETH.

receive() external payable {

}

function wrapETH() public {
  require(msg.sender == owner, "Somente o propriet√°rio pode converter ETH para WETH");
  uint ethBalance = address(this).balance;
  require(ethBalance > 0, "Nenhum ETH disponível para encapsulamento");
  emit myVaultLog('wrapETH', ethBalance);
  wethToken.deposit{ value: ethBalance }();
}

}
Enter fullscreen mode Exit fullscreen mode

Portanto, temos uma função de pagamento padrão que aceitará ETH no contrato, mas não fará nada com isso. A razão pela qual não chamamos a wrapETH diretamente é porque isso quebraria a maioria das transferências de ETH ao estourar o limite de gas. Ao enviar ETH via Metamask, por exemplo, um limite de gas bem apertado é definido por ela, o que não permite que o contrato faça muito na mesma transação.

No final da função wrapETH(), interagimos com a função de depósito personalizado do wethToken para encapsular todo o saldo ETH em WETH.

Finalmente fechamos os colchetes do contrato. Pronto!

Se você quiser revisar todo o código do contrato, ele está disponível aqui:

https://github.com/jamesbachini/myVault/blob/main/contracts/myVault.sol

Testando contratos inteligentes Solidity

Agora vamos escrever alguns testes usando hardhat e chai.

const hre = require('hardhat');
const assert = require('chai').assert;

describe('myVault', () => {
  let myVault;

  beforeEach(async function () {
    const contractName = 'myVault';
    await hre.run("compile");
    const smartContract = await hre.ethers.getContractFactory(contractName);
    myVault = await smartContract.deploy();
    await myVault.deployed();
    console.log(`${contractName} implantado em: ${myVault.address}`);
  });

  it('Deve retornar a vers√£o correta', async () => {
    const version = await myVault.version();
    assert.equal(version,1);
  });
});
Enter fullscreen mode Exit fullscreen mode

Este √© o teste mais simples que podemos escrever, que verifica se a vers√£o da vari√°vel p√ļblica est√° definida como 1. Podemos execut√°-lo usando o seguinte comando.

npx hardhat test
Enter fullscreen mode Exit fullscreen mode

Muitas de nossas fun√ß√Ķes de contrato est√£o chamando fun√ß√Ķes externas de outros contratos. Para testar estas fun√ß√Ķes, precisaremos implantar na rede Kovan e testar l√° (processo lento) ou podemos fazer um fork do estado da rede Kovan localmente.

npx hardhat node --fork 
https://eth-kovan.alchemyapi.io/v2/AlchemyAPIkeyHere
Enter fullscreen mode Exit fullscreen mode

Isso irá configurar um nó local que está executando um fork independente da rede de teste kovan.

Agora podemos expandir os testes para chamar as fun√ß√Ķes externas de outros contratos.

it('Deve retornar saldo zero de DAI', async () => {
    const daiBalance = await myVault.getDaiBalance();
    assert.equal(daiBalance,0);
  });
Enter fullscreen mode Exit fullscreen mode

Este teste entrará em contato com o contrato daiToken externo em 0x4F96Fe3b7A6Cf9725f59d353F723c1bDb64CA6Aa e verificará o saldo do nosso endereço de contrato. Vamos testar isso usando o ambiente local que estruturamos na configuração do Hardhat, que se conecta ao nó local que estamos executando.

npx hardhat test --network local
Enter fullscreen mode Exit fullscreen mode

https://jamesbachini.com/wp-content/uploads/2021/09/testComplete-768x198.png

Se f√īssemos implantar isso na rede principal, precisar√≠amos escrever testes de unidade para cada fun√ß√£o e criar alguns testes amplos de funcionalidade do contrato. O Hardhat se destaca em m√©todos avan√ßados de script, onde podemos movimentar fundos e acionar diferentes contratos, etc.

Aqui está um exemplo de teste mais avançado, onde podemos enviar 0,01 ETH da conta do proprietário para o contrato, depois fazer o encapsulamento para WETH, atualizar o preço do ETH usando o oráculo Uniswap e reequilibrar o portfólio antes de verificar se o saldo em DAI está acima de zero.

it('Deve reequilibrar o portfólio ', async () => {
  const accounts = await hre.ethers.getSigners();
  const owner = accounts[0];
  console.log('Transferindo ETH do endereço do proprietário', owner.address);
  await owner.sendTransaction({
    to: myVault.address,
    value: ethers.utils.parseEther('0.01'),
  });
  await myVault.wrapETH();
  await myVault.updateEthPriceUniswap();
  await myVault.rebalance();
  const daiBalance = await myVault.getDaiBalance();
  console.log('Saldo DAI reequilibrado',daiBalance);
  assert.isAbove(daiBalance,0);
});
Enter fullscreen mode Exit fullscreen mode

Implantando e Utilizando o Hardhat

Assim que tivermos alguns testes aceitáveis a postos, estaremos prontos para implantar na testnet externa Kovan e começar a mexer com ela no Etherscan. Um recurso muito bom do Hardhat é que podemos verificar o contrato no Etherscan a partir do script de implantação.

const hre = require('hardhat');
const fs = require('fs');

async function main() {
  const contractName = 'myVault';
  await hre.run("compile");
  const smartContract = await hre.ethers.getContractFactory(contractName);
  const myVault = await smartContract.deploy();
  await myVault.deployed();
  console.log(`${contractName} implantado em: ${myVault.address}`);

  const contractArtifacts = await artifacts.readArtifactSync(contractName);
  fs.writeFileSync('./artifacts/contractArtifacts.json',  JSON.stringify(contractArtifacts, null, 2));

  await hre.run("verify:verify", {
    address: myVault.address,
    //constructorArguments: [],
  });
}

main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });
Enter fullscreen mode Exit fullscreen mode

O script come√ßa compilando o contrato. Em seguida, ele o implanta na rede Kovan e registra o novo endere√ßo do contrato. Em seguida, gravamos os artefatos do contrato em um arquivo contractArtifacts.json que ser√° √ļtil ao fazer o trabalho de front-end. Finalmente, verificamos o contrato no Etherscan.

Podemos executar este script usando o seguinte comando:

npx hardhat run scripts/deploy.js --network kovan
Enter fullscreen mode Exit fullscreen mode

Podemos ent√£o usar e esperar para verificar o contrato no Etherscan e depois gerar um link para:

https://kovan.etherscan.io/address/0x9e87D719Ad4304731915C5bc5D2304D38E618b7D#code

Onde podemos interagir com o contrato usando a carteira do propriet√°rio na Metamask.

https://jamesbachini.com/wp-content/uploads/2021/09/Screenshot-2021-09-25-at-21-37-59-myVault-0x5046412b21D04f4B6410d58c5e156Ca8b6A82d9B-768x485.png

Para implantar na rede principal Ethereum ou em qualquer sidechain compatível com camada 2 ou EVM, podemos simplesmente adicionar uma rede na configuração do Hardhat e alterar os endereços. Observe que o endereço do contrato para o token DAI ou o roteador Uniswap será diferente em cada rede.

Verifica√ß√Ķes de Seguran√ßa do Solidity

Al√©m de tudo, gostar√≠amos de realizar intensas verifica√ß√Ķes de seguran√ßa e, idealmente, ter o c√≥digo auditado por terceiros antes de confiar nele para transa√ß√Ķes financeiras. Uma ferramenta chamada Slither pode ser bastante √ļtil para auditorias de vulnerabilidades simples. √Č um pouco como o ESlint √© para o Solidity.

Eu só consegui executá-lo no Linux e usar o Ubuntu no WSL, instalando através dos seguintes comandos em um linha de comando Linux:

sudo add-apt-repository ppa:ethereum/ethereum
sudo apt-get update
sudo apt install solc
sudo pip3 install slither-analyzer
slither
cd /mnt/c/shareddocs/jamesbachini/code/myVault/
sudo npm install
sudo slither .
Enter fullscreen mode Exit fullscreen mode

https://jamesbachini.com/wp-content/uploads/2021/09/rentrancyBugs-768x432.png

Aqui est√£o alguns dos muitos problemas destacados no contrato inteligente myVault do Slither. Muitos deles s√£o falsos positivos e problemas com contratos de terceiros importados, mas todos precisam ser verificados antes de serem implantados na rede principal.

Poder√≠amos levar isso adiante e configurar um fuzzer como o Echidna para usar de for√ßa bruta com dados incomuns nas fun√ß√Ķes do contrato. Idealmente, no entanto, se os or√ßamentos permitirem, √© ben√©fico obter um auditor de seguran√ßa terceirizado para examinar o c√≥digo e destacar problemas complexos que podem ter sido ignorados pelos desenvolvedores originais.

Espero que este tutorial intermediário de Solidity tenha se mostrado interessante e que você não tenha detectado muitos bugs no código myVault ao longo do caminho.

Interessado em aprender mais sobre desenvolvimento de blockchain, DeFi e manter-se atualizado com os mercados de criptomoedas? Confira o canal do YouTube e conecte-se comigo no Twitter para atualiza√ß√Ķes e novos conte√ļdos.

https://www.YouTube.com/c/JamesBachini

https://Twitter.com/james_bachini

Se voc√™ gostou desses recursos, por favor ajude a compartilhar este conte√ļdo nas m√≠dias sociais e envie para quem voc√™ acha que pode se beneficiar com ele.

Obrigado.

Isen√ß√£o de responsabilidade: O conte√ļdo que crio √© para documentar minha jornada e para fins de entretenimento. N√£o √© em hip√≥tese alguma um conselho de investimento. Eu n√£o sou um profissional de investimentos ou negocia√ß√Ķes e estou aprendendo sozinho enquanto ainda cometo muitos erros ao longo do caminho. Qualquer c√≥digo publicado √© experimental e n√£o est√° pronto para ser usado em transa√ß√Ķes financeiras. Fa√ßa suas pr√≥prias pesquisas e n√£o se arrisque com fundos que voc√™ n√£o quer perder.

Este artigo foi escrito por James Bachini, e traduzido por Paulinho Giovannini. Veja o artigo original aqui.

Top comments (0)