Pressione ESC para fechar

Vulnerabilidades ERC: Guia de Arquitetura de Smart Contracts

Salve, dev! Se você caiu aqui, ou você é fissurado em segurança de smart contracts ou tá desenhando a arquitetura de um protocolo DeFi novo agora mesmo e tá com o uc na mão pensando que amanhã tudo pode ir de ralo por causa de uma vulnerabilidade boba.

Cara, eu já tô nessa estrada faz tempo. Vim lá dos hackathons raiz, onde a gente buildava exploit de madrugada na base de pizza e energético, até sentar na cadeira de CTO de uma grande exchange. E papo reto? A maioria dos hacks bizarros que eu já vi e investiguei (e vários que consegui travar na fase de auditoria) não aconteceram porque a criptografia quebrou ou porque o compilador do Solidity endoidou. Eles rolaram por pura falta de noção de como os padrões ERC se comportam quando começam a interagir entre si.

A gente cresce achando que se tá no padrão, é lei e tá seguro. Mas o diabo mora nos detalhes da implementação e nos efeitos colaterais que ninguém vê. Vamos destrinchar onde os arquitetos mais quebram a cara e como estruturar o seu sistema para você conseguir dormir em paz.

1. A Ameaça Fantasma do ERC-20: O Clássico que Não Morre

Parece que todo mundo já tá careca de saber como o ERC-20 funciona, né? O que poderia dar errado? Tudo, se você começar a integrar tokens de terceiros no seu protocolo sem uma validação absurdamente rígida.

O Dilema do Retorno Inexistente (The No-Return Dilemma)

Pela especificação oficial, as funções transfer e transferFrom deveriam retornar um bool. Só que, no mundo real, uma porrada de tokens antigos e gigantes (alô, USDT e BNB em alguns de seus contratos de primeira geração) simplesmente cagam para isso. Eles não retornam porra nenhuma quando dão bom.

Se o seu contrato estiver esperando um bool usando a interface padrão:

// Nunca faça isso se estiver lidando com tokens genéricos!
IERC20(token).transferFrom(msg.sender, address(this), amount);

A transação vai simplesmente estourar um revert quando interagir com USDT, porque a EVM vai caçar o valor de retorno na stack e não vai achar nada. Ou pior: se você não estiver checando o resultado (usando só token.transfer(...) em vez de require(token.transfer(...))), alguns tokens retornam false em vez de dar revert quando dá merda. Aí seu contrato continua executando como se tudo estivesse lindo. O resultado? O usuário acabou de farmar saldo do além.

A Solução: Esqueça de uma vez por todas a chamada direta para transfer e transferFrom. Use a biblioteca SafeERC20 da OpenZeppelin com os métodos safeTransfer e safeTransferFrom. Ela faz o de-para do retorno em low-level por baixo dos panos e lida perfeitamente com esses contratos "mutantes".

Weird ERC-20 Tokens: Quando o Padrão Vira Abóbora

Segue uma colinha rápida dos tokens que se comportam de um jeito totalmente bizarro se comparado ao que está nos manuais. Como arquiteto, você tem a obrigação de prever isso na lógica das suas liquidity pools.

Tipo de Token (Weird ERC-20)Qual é a pegadinha?Qual o risco para a arquitetura?
Deflationary / Fee-on-Transfer (ex: STA, PAXG)Eles cobram uma taxa direto na transação durante o transfer.Você achou que o contrato recebeu 100 tokens, mas na real só entraram 99 no saldo. A contabilidade interna da pool quebra e a conta não fecha.
Upgradable Proxies (ex: USDC, USDT)A lógica do token pode ser alterada a qualquer momento pelos admins.Risco de Blacklists. Se o endereço do seu contrato for banido, toda a liquidez interna fica travada lá dentro para sempre. F no chat.
Rebasing Tokens (ex: AMPL)O saldo nas carteiras muda dinamicamente (o supply oscila para estabilizar o preço).O saldo do seu contrato pode derreter ou valorizar do nada, sem que nenhuma função de transferência tenha sido chamada.

2. ERC-721 e ERC-1155: A Cilada do onERC721Received e o Reentrancy

Ah, esse aqui é o meu assunto favorito. O que eu já vi de marketplace de NFT e protocolo de lending sangrar por causa dessas funções de transfer dita "seguras" não tá escrito.

Quando você roda um safeTransferFrom no ERC-721 ou ERC-1155, o contrato do token vai checar se quem tá recebendo é um smart contract. Se for, ele trigga um hook no destino chamado onERC721Received ou onERC1155Received.

Para que isso serve? Para garantir que o contrato receptor sabe lidar com NFTs e que eles não vão ficar mofando lá dentro.

Onde tá a pegadinha? Esse bendito hook passa o controle da execução para um código externo totalmente desconhecido bem no meio da sua transação, antes mesmo de você atualizar o estado interno do seu próprio contrato!

Código de um Mint / Marketplace Vulnerável

Dá uma olhada nesse trecho. Escrevi ele de um jeito bem didático para expor o clássico erro de arquitetura: a violação descarada do padrão Checks-Effects-Interactions.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
contract VulnerableNFCLending {
    // Guarda os colaterais: Usuário => ID do Token => Colateral Ativo?
    mapping(address => mapping(uint256 => bool)) public hasCollateral;
    IERC721 public nftToken;
    constructor(address _nft) {
        nftToken = IERC721(_nft);
    }
    // Usuário quer pegar empréstimo deixando NFT de garantia
    function depositCollateral(uint256 tokenId) external {
        // 1. Interactions: Transfere o NFT para o contrato
        // O safeTransferFrom vai disparar o hook onERC721Received no receptor?
        // Pera, o receptor aqui somos NÓS. Mas se o contrato chamar safeMint...
        // Vamos ajustar o contexto para quando estamos devolvendo o NFT ou quando o atacante intercepta o fluxo.
        
        // Ajustando o cenário: O contrato está devolvendo o NFT (tipo num saque de garantia)
        // ou é um contrato de mint que primeiro transfere e só depois atualiza o estado.
    }
}

Para ficar mais visual, deixa eu te mostrar um exemplo limpo com uma função de mint vulnerável. Imagina um contrato que deixa a galera cunhar 1 NFT de graça por carteira (Free Mint).

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
contract VulnerableMint is ERC721 {
    mapping(address => bool) public hasMinted;
    uint256 public currentTokenId;
    constructor() ERC721("DangerNFT", "DNFT") {}
    function freeMint() external {
        // Checks
        require(!hasMinted[msg.sender], "Brother, you already have one!");
        // Interactions (Dentro do _safeMint tem uma chamada externa camuflada!)
        _safeMint(msg.sender, currentTokenId);
        currentTokenId++;
        // Effects (A atualização do estado acontece TARDE DEMAIS)
        hasMinted[msg.sender] = true;
    }
}

Agora olha o contrato do atacante. Ele simplesmente intercepta esse hook, saca que a flag hasMinted no contrato principal ainda está como false, e fica chamando freeMint em loop até drenar o limite inteiro ou acabar o gas.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface IVulnerableMint {
    function freeMint() external;
}
contract Attacker {
    IVulnerableMint public target;
    uint256 count;
    constructor(address _target) {
        target = IVulnerableMint(_target);
    }
    function attack() external {
        target.freeMint();
    }
    // O maldito hook que o padrão ERC-721 dispara
    function onERC721Received(
        address,
        address,
        uint256,
        bytes calldata
    ) external returns (bytes4) {
        if (count < 5) {
            count++;
            // Reentrancy na veia! O estado interno da vítima ainda não atualizou
            target.freeMint();
        }
        return this.onERC721Received.selector;
    }
}

Putz, cansei de ver essa presepada em sessões de auditoria. Os devs pensam: "Ah, é só um transfer de NFT, não tô enviando ETH nativo, o que de mal pode acontecer?". O que acontece é que seu contrato é dropado e limpo em minutos.

Regra de Ouro do Arquiteto: Primeiro atualize o estado (hasMinted[msg.sender] = true;), e só depois invoque qualquer método de mint ou transferência. E sempre, escuta bem, SEMPRE jogue o modificador nonReentrant da OpenZeppelin em funções que lidam com movimentação de NFT.

Bora dar sequência. Já que a gente destrinchou o Reentrancy via hooks, vamos cavar um problema mais recente e bem mais sutil. É o tipo de treta que quase ninguém bota o neurônio para pensar na fase de desenho da arquitetura, até que o pior aconteça em mainnet.

3. ERC-2612 (Permit): Phantom Approvals e Ataques de Front-running

O padrão ERC-2612 trouxe um baita alívio para o ecossistema Web3: a função permit. Ela permite que os usuários assinem uma mensagem offline (EIP-712) para aprovar o gasto de tokens (allowance), jogando o custo do gás no colo de um relayer ou do próprio protocolo. O salto em UX foi bizarro: em vez de travar o usuário em duas transações separadas (approve + transferFrom), ele resolve tudo numa única tacada.

Só que os arquitetos costumam esquecer como essa assinatura roda de verdade debaixo do capô, e acabam cometendo erros fatais de lógica.

Front-running de Assinatura (Signature Front-running)

Imagina uma função clássica de depósito num smart contract usando permit:

function depositWithPermit(
    uint256 amount,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
) external {
    // Primeiro executa o permit com a assinatura passada pelo usuário
    IERC20Permit(token).permit(msg.sender, address(this), amount, deadline, v, r, s);
    
    // Depois puxa os tokens
    IERC20(token).transferFrom(msg.sender, address(this), amount);
    
    // Minta as shares do pool ou pontos internos
    _mintShares(msg.sender, amount);
}

Onde tá o vetor de ataque aqui? Qualquer bot de MEV que fica monitorando o mempool público consegue ver essa transação pendente. O bot simplesmente extrai a assinatura válida (v, r, s) e os parâmetros da sua TX, monta uma transação própria chamando token.permit(...) direto no contrato do token e passa a perna no usuário enfiando um Gas Price bem mais alto (front-running).

A transação do bot roda primeiro. A assinatura é executada com sucesso e o allowance é definido. Logo em seguida, vem a transação do usuário legítimo. Só que, como a assinatura já foi gasta, o nonce do usuário no contrato do token incrementou! O chamado do permit dentro de depositWithPermit vai dar revert na hora, porque a assinatura virou fumaça.

O resultado? A transação do usuário quebra, ele queima gás à toa, a UX vai pro espaço e, se isso fosse um aporte crítico de margem para salvar uma posição long alavancada, a posição é liquidada sem dó por causa do delay.

Como blindar a sua arquitetura?

Envolva o chamado do permit num bloco try/catch. Se a assinatura já tiver sido injetada na rede por um front-runner, o contrato do token já vai estar com o allowance que você precisa configurado. O seu contrato só precisa ignorar o erro de assinatura duplicada e seguir o baile para rodar o transferFrom.

try IERC20Permit(token).permit(msg.sender, address(this), amount, deadline, v, r, s) {} 
catch {
    // Se falhou, há grandes chances de a assinatura ter sofrido front-running.
    // Validamos se o allowance atual já dá conta do recado para a operação.
    require(IERC20(token).allowance(msg.sender, address(this)) >= amount, "Permit failed and allowance insufficient");
}

O fantasma do "Phantom Permit"

Isso aqui já é aquela dor de quem trampa no ecossistema e que você quase não vê documentada por aí. O que acontece se o seu protocolo DeFi aceita tokens arbitrários e o usuário tenta meter um depositWithPermit usando um token que NÃO suporta o padrão ERC-2612?

Você vai pensar: "Ah, o contrato vai dar revert, afinal a função permit nem existe lá". Mas não é bem assim que a banda toca!

Se o contrato do token tiver uma função genérica de fallback() ou receive() que não dá revert ao receber um selector desconhecido (comportamento bem comum em algumas arquiteturas de proxy ou tokens antigos tipo versões velhas de WETH), o chamado do permit vai retornar sucesso (success = true), só que nenhum allowance vai ser aprovado de verdade.

Na sequência, o seu contrato vai tentar rodar o transferFrom, que vai quebrar feio, a menos que já existisse algum approve antigo mofando ali. Mas se você cruzar essa vulnerabilidade com alguma lógica onde o allowance é verificado por outras rotas, o seu protocolo vai tomar um rekt histórico. Sempre verifique se o token alvo realmente implementa IERC20Permit via ERC-165, ou controle isso de forma estrita usando uma whitelist de tokens.

4. ERC-3156 (Flash Loans): Perigo de Manipulação de Saldo na Mesma Transação

Flash loans são ferramentas absurdas de poderosas, mas elas quebram todas as premissas de timing com as quais os arquitetos de software tradicionais estão acostumados. Dentro de uma única transação atômica, o atacante consegue pegar milhões de dólares emprestados, fazer a limpa nos estados do contrato e devolver a grana logo em seguida.

O erro arquitetural mais bizarro aqui é usar a função balanceOf(address(this)) para calcular o preço das shares de um pool ou o valor de um ativo.

// ERRO CATASTRÓFICO DE ARQUITETURA
function getSharePrice() public view returns (uint256) {
    // O preço da share depende diretamente do saldo atual de tokens no contrato
    return token.balanceOf(address(this)) / totalShares;
}

Se o seu contrato abre as portas para Flash Loans desse mesmo token, no momento em que o tomador saca os fundos, o saldo do contrato derrete para quase zero. Se exatamente nessa fração de segundo (dentro do callback onFlashLoan) o seu protocolo permitir outras operações colaterais — tipo liquidações ou cálculo de recompensas —, o preço da share calculado vai estar completamente distorcido.

O hacker puxa o flash loan -> o saldo do pool desaba -> o preço da share vai pro chão -> o hacker usa outra wallet para rapar as shares do pool a preço de banana -> o flash loan é pago -> o saldo do pool volta ao normal -> o hacker despeja as shares no preço cheio. Pronto, pool drenado.

Regra de ouro: Nunca, em hipótese alguma, dependa de balanceOf(address(this)) para contas matemáticas ou econômicas críticas se esse saldo puder ser manipulado temporariamente sem alterar o estado lógico real do sistema. Use contabilidade interna (internal accounting) através de uma state variable como uint256 internalReserve, atualizada única e exclusivamente durante fluxos controlados de depósitos e saques oficiais.

Beleza, vamos direto para os assuntos que me dão um frio na espinha de verdade como responsável pela segurança da infra de uma exchange. Vamos falar sobre uns padrões novos e relativamente recentes, onde os bugs clássicos ainda não foram totalmente pisoteados pelos pés de centenas de desenvolvedores.

5. ERC-4337 (Account Abstraction): Armadilhas em Transações em Lote (Batches) e Paymaster

Account Abstraction é animal, sem discussão. Estamos finalmente saindo das EOAs (carteiras comuns) para usar smart contracts como carteiras dos usuários. Acabou esse negócio de seed phrase, dá para meter um social recovery e pagar o gas em stablecoin usando os Paymasters.

Só que do ponto de vista de um arquiteto de protocolo que vai integrar com ERC-4337, abre-se um abismo de vetores de ataque bizarros e super específicos.

Vulnerabilidade de assinatura no validateUserOp

No ERC-4337, o ponto central da validação customizada da carteira é o método validateUserOp. Ele tem que checar a assinatura da transação e retornar um status específico.

// Exemplo ultra-simplificado da lógica de validação em uma smart wallet
function validateUserOp(
    UserOperation calldata userOp,
    bytes32 userOpHash,
    uint256 missingAccountFunds
) external returns (uint256 validationData) {
    // Erro crítico: Estamos confiando em uma chamada vinda de QUALQUER endereço?
    // Não, o Bundler chama isso através do EntryPoint. Mas se esquecermos a verificação...
    require(msg.sender == entryPoint, "Only EntryPoint can trigger validation");
    
    // Validação própria da assinatura
    if (_verifySignature(userOp, userOpHash)) {
        // Retorna 0 se a validação der bom
        return 0; 
    }
    
    // Retorna SIG_VALIDATION_FAILED (geralmente 1) se der ruim
    return 1; 
}

Peraí, sacou a pegadinha aqui? Pela especificação do ERC-4337, se a validação da assinatura falhar, a função NÃO PODE dar um revert. Ela precisa retornar um valor compactado específico (uma constante de erro) para que o EntryPoint (o contrato maestro) entenda que a transação é inválida. Assim, ele não desconta o gas da carteira e simplesmente dropa a operação direto no nível do Bundler.

Se você, como arquiteto, meter ali por puro hábito um require(isValid, "Invalid signature");, vai dar ruim demais. Quando mandarem um lote (batch) enorme de transações, um único revert seco de uma transação que falhou vai quebrar o lote inteiro do Bundler. No final das contas, a sua carteira ou o seu paymaster vai levar bloco (ban) dos bundlers, e os seus usuários não vão conseguir mandar transação nenhuma. A lógica de validação precisa ser atômica e seguir estritamente a matemática de retorno do ERC-4337, e não os padrões clássicos do Solidity.

Ataque ao Paymaster (Gas Drain)

Se o seu protocolo DeFi serve como um Paymaster (por exemplo, se você subsidia o gas dos seus usuários para eles operarem com taxa zero), você tem a obrigação de isolar completamente a etapa de validação do resto do mundo.

No contrato do Paymaster existe o método validatePaymasterUserOp. Dentro dele é **estritamente proibido** usar estados dinâmicos que possam mudar entre o momento em que o bundler simula a transação e o momento em que ela entra no bloco. Tipo, você não pode chamar oráculos de preço (Chainlink) bem no meio da validação do paymaster para calcular quantos tokens vai cobrar do usuário pelo gas.

**Por quê?** Um atacante pode mandar uma transação onde, na simulação, o oráculo mostra um preço que passa liso na validação. Mas logo antes de entrar no bloco, o hacker manipula o preço do oráculo (com um flash loan ou um swap rápido). A validação on-chain vai começar a dar revert, mas o bundler já puxou a transação e gastou gas. O dinheiro vai ser sugado do seu paymaster, e o usuário não vai pagar nada. Vão torrar o seu saldo de gas em poucas horas.

6. ERC-4626 (Tokenized Vaults): Front-running no primeiro depósito (Inflation Attack)

O ERC-4626 é o padrão definitivo para cofres tokenizados (staking, pools de rendimento, lending). Ele padronizou os métodos deposit, mint, withdraw e redeem. Isso é genial, porque agora qualquer agregador de rendimento tipo a Yearn consegue integrar qualquer pool novo em 5 minutos.

Só que o próprio design matemático do padrão vem com uma bomba-relógio embutida, conhecida como **Inflation Attack** (Ataque de Inflação do Ativo). Esse ataque mira nos pools exatamente quando eles acabaram de ser spawnados na rede e o saldo deles está zerado.

A Mecânica do Ataque

A fórmula para calcular a quantidade de cotas (shares) que um usuário recebe ao depositar ativos (assets) geralmente é assim:

$$\text{shares} = \frac{\text{assets} \times \text{totalShares}}{\text{totalAssets}}$$

Se o pool estiver zerado (totalShares == 0), por padrão temos shares == assets. Ou seja, uma proporção limpa de 1 para 1.

Agora olha a malandragem do hacker:

  • Um usuário legítimo envia uma transação de deposit de 1.000 USDC num pool ERC-4626 novinho e totalmente vazio.
  • O hacker monitora isso no mempool e faz um front-run (bota o gas mais alto). Ele deposita míseros 1 wei de USDC no pool. O pool gera (mint) exatamente 1 wei de shares para ele. O cenário agora é: totalShares = 1, totalAssets = 1.
  • Em seguida, na mesma transação atômica, o hacker faz um envio direto (via transfer normal de ERC20, sem passar pela função de deposit) de uma bolada — tipo 10.000 USDC — direto para o endereço do contrato do pool.
  • O que aconteceu com a matemática do pool? O totalShares continua sendo 1, mas o totalAssets agora saltou para 10.001 USDC (o envio direto inflou o saldo do contrato, mas sem gerar novas shares). O preço de uma única cota do pool acabou de ir para a lua.
  • Finalmente, a transação do usuário honesto de 1.000 USDC é executada. O contrato calcula as shares usando a fórmula:

    $$\text{shares} = \frac{1000 \times 1}{10\,001} = 0$$

    Por causa do arredondamento para baixo do Solidity (divisão de inteiros), o usuário recebe exatamente 0 shares! Mas os 1.000 USDC dele entram bonitinho no saldo do pool.

  • O hacker só precisa dar um withdraw da sua única cota (1 wei de shares) e raspa absolutamente tudo do pool: os seus 10.000 USDC de volta, o seu 1 wei inicial e os 1.000 USDC roubados do usuário que tomou o rekt.

Solução de Arquitetura: Dá para se proteger disso de duas formas. A primeira é forçar o mint de "liquidez fantasma" (dead shares) para o endereço zero logo na criação do pool (travar os primeiros 1.000 wei de shares lá dentro, exatamente como o Uniswap V2 faz). A segunda é usar as bibliotecas atualizadas da OpenZeppelin, que já vêm com proteção embutida usando offsets virtuais (virtual assets e virtual shares). Isso impede que o denominador da fração vire zero ou um durante essas manipulações.

Mano, se liga: vamos subir o nível agora. A gente já falou sobre tokens específicos, NFTs, permits e vaults. Mas como é que você amarra tudo isso numa arquitetura única sem surtar na hora de integrar?

Quando você projeta um sistema grande, tipo um agregador de yield ou uma bridge cross-chain, você é obrigado a lidar com todos esses padrões ao mesmo tempo. E é aí que nasce aquele efeito sinérgico de vulnerabilidades: duas features que, separadas, são super seguras, mas juntas criam uma brecha fatal.

7. Matriz de Riscos Arquiteturais para o seu sistema

Para você ter uma visão clara, montei essa tabela. É basicamente um checklist para o seu próximo Architecture Review. Salva isso no seu Notion ou imprime e cola na parede.

Padrão ERCA grande ameaça ocultaComo explode na lógicaComo mitigar no design?
ERC-20Sem retorno / Transferência fora do padrãoTransaction revert ou erro engolido silenciosamenteUse exclusivamente SafeERC20 (OpenZeppelin).
ERC-20 (Weird)Fee-on-Transfer / Mudança de balanço (Rebase)Divergência entre o seu accounting interno e o saldo real no contratoCalcule a diferença balanceAfter - balanceBefore em vez de confiar no argumento amount.
ERC-721 / 1155Sequestro de fluxo via hooks onERC...ReceivedReentrancy antes de atualizar o state internoSiga à risca o padrão Checks-Effects-Interactions + use o modificador nonReentrant.
ERC-2612Frontrunning de assinaturas no mempoolDenial-of-Service (DoS) para um usuário legítimoEnvolva as chamadas de permit em blocos try/catch.
ERC-3156Drenagem temporária do pool (Flash Loan)Manipulação de preços spot baseados em balanceOfUse variáveis de reserva internas (internal reserves) em vez do saldo direto.
ERC-4337revert bruto durante validação em loteContrato ou carteira banida pelos bundlersRetorne constantes de erro mágicas em vez de travar a tx com require.
ERC-4626Inflation Attack (Ataque no primeiro depósito)Arredondamento de shares para zero, roubo de fundos do primeiro depositanteMint "shares mortas" para o address(0) na inicialização ou use offsets virtuais.

8. Reflexões e as regras de ouro da arquitetura segura

Cara, depois de três anos sentado na cadeira de CTO, eu aprendi uma coisa: o código mais seguro é aquele que você não escreveu. Quanto mais complexa for a sua arquitetura, mais conexões ocultas ela tem e maior a chance de algum gênio do hackathon achar uma brecha que você nem sonhava que existia enquanto tomava seu café da manhã.

Se eu tivesse que te passar apenas três regras que vão salvar seu projeto de virar manchete no Rekt News, seriam estas:

  • Nunca confie em contratos externos. Mesmo que seja o token mais popular do mundo. Amanhã os admins atualizam o proxy, colocam blacklist e seu sistema trava. Escreva seu código contando que aquele token externo é o ator mais malandro e imprevisível da rede.
  • Primeiro state, depois transferências. Eu não vou cansar de repetir isso. É o básico do básico que ensinam em qualquer curso descente de smart contracts, mas o pessoal continua com a teimosia de mandar tokens antes de atualizar os números nos mappings. Primeiro você ajusta os direitos ou saldos internamente, registra isso na blockchain e, só no último passo, chama o transfer, safeMint ou call.
  • Isole sua matemática do saldo externo. O saldo do seu contrato na EVM é público e fácil de manipular. Qualquer um pode mandar milhões de dólares pra você via flash loan ou dar um "selfdestruct" em algum contrato pra forçar ETH no seu endereço. Se a sua lógica de rewards ou cálculo de preço de share depende de quanto token tem no contrato, você já perdeu. Seu accounting interno tem que ser isolado, tipo a cabine de um piloto.

Bom, por hoje passamos por todos os pontos críticos dos padrões que podem botar a perder o esforço de uma equipe monstra, caso o arquiteto não tenha feito aquela "gambiarra de qualidade" (no bom sentido) nos lugares certos.

Oleg Filatov

As the Chief Technology Officer at EXMON Exchange, I focus on building secure, scalable crypto infrastructure and developing systems that protect user assets and privacy.

With over 15 years in cybersecurity, blockchain, and DevOps, I specialize in smart contract analysis, threat modeling, and secure system architecture.

At EXMON Academy, I share practical insights from real-world...

...

Deixe seu parecer

O seu endereço de e-mail não será publicado. Campos obrigatórios estão marcados *