Este documento fornece informações abrangentes para implantar smart contracts na Polkadot Hub TestNet (Paseo) usando Claude Code. Inclui configurações verificadas, problemas comuns, soluções e estratégias de otimização.
CRÍTICO: Sempre inicie novos projetos com kitdot@latest init para configuração adequada de rede e gerenciamento de dependências.
Status: PolkaVM Preview Release (estágio inicial de desenvolvimento)
Características Principais
Compatibilidade EVM: Implantação de smart contracts compatível com Ethereum
PolkaVM: Requer configuração específica para compatibilidade
Limite de Bytecode: ~100KB tamanho máximo do contrato
Modelo de Gas: Mecânica padrão de gas EVM
Aviso de Versão Node: Funciona com Node.js v21+ apesar dos avisos
DEPENDÊNCIAS OBRIGATÓRIAS
Dependências Principais
Requisitos do Package.json
CRÍTICO: Use a flag --force ao instalar hardhat-toolbox para resolver conflitos de dependência.
CONFIGURAÇÃO DO HARDHAT
hardhat.config.js Completo e Funcional
Requisitos de Configuração
Deve usar formato string para versão do Solidity: solidity: "0.8.28"
Deve incluir configuração resolc: Necessário para compatibilidade PolkaVM
Deve definir polkavm: true: Em todas as configurações de rede
Deve usar hardhat vars: Para gerenciamento de chave privada
Nome da rede: Use passetHub (não paseo ou outros nomes)
PROCESSO DE CONFIGURAÇÃO
Passo 1: Inicializar Projeto com kitdot@latest (Recomendado)
Configuração Manual Alternativa:
Por que kitdot@latest? Configura automaticamente configurações de rede, dependências e estrutura de projeto adequadas. Elimina erros comuns de configuração.
Passo 2: Instalar Dependências
Se usando kitdot@latest: Dependências são instaladas automaticamente.
Instalação manual:
Passo 3: Inicializar Plugin Polkadot
Se usando kitdot@latest: Já configurado.
Configuração manual:
Passo 4: Configurar Chave Privada
Passo 5: Obter Tokens de Teste
Visite https://faucet.polkadot.io/?parachain=1111
Digite o endereço da sua carteira
Solicite tokens PAS
Verifique o saldo na carteira ou block explorer
Passo 6: Criar Configuração Hardhat
Se usando kitdot@latest: Arquivo de configuração já criado com configurações adequadas.
Configuração manual: Copie a configuração exata acima para hardhat.config.js
DESENVOLVIMENTO DE CONTRATOS
Requisitos da Versão Solidity
Versão Obrigatória: ^0.8.28
Target EVM: paris (padrão)
Optimizer: Habilitado por padrão
Exemplo de Contrato Simples
Limitações de Tamanho do Contrato
Bytecode Máximo: ~100KB
Impacto OpenZeppelin: Imports padrão frequentemente excedem o limite
Certifique-se de ter polkavm: true na configuração de rede
Adicione bloco de configuração resolc
Use formato exato do hardhat.config.js acima
2. Erro "initcode is too big"
Erro: initcode is too big: 125282 (ou número similar)
Causa: Bytecode do contrato excede limite de ~100KB
Soluções:
Remover dependências OpenZeppelin
Usar implementações mínimas de contratos
Dividir contratos grandes em componentes menores
Implementar versões customizadas e leves
3. Erros de Configuração
Erro: Cannot read properties of undefined (reading 'settings')
Solução: Use formato string para versão do Solidity: solidity: "0.8.28"
4. Problemas de Dependência
Erro: Cannot find module 'run-container' ou similar
Soluções:
Instalar dependências com flag --force
Limpar node_modules e reinstalar
Verificar compatibilidade da versão @parity/hardhat-polkadot
5. Problemas de Chave Privada
Erro: No signers found ou falhas de autenticação
Soluções:
Definir chave privada via npx hardhat vars set PRIVATE_KEY
Certificar-se de que a conta tem tokens PAS
Verificar formato da chave privada (sem prefixo 0x nas vars)
OTIMIZAÇÃO DE CONTRATOS
Removendo Dependências OpenZeppelin
Em vez de OpenZeppelin Ownable:
Em vez de OpenZeppelin ReentrancyGuard:
Implementação ERC721 Mínima:
INTEGRAÇÃO FRONTEND
Problemas de Transações Frontend (Problemas de Estimação de Gas/Legacy)
CRÍTICO PARA AGENTES: Aplicações frontend frequentemente encontram problemas de estimação de gas ao enviar transações para redes Polkadot. Sempre implemente essas estratégias:
Abrir settings.json:Cmd/Ctrl + Shift + P → "Preferences: Open Settings (JSON)"
Adicionar configuração MCP: Inserir a configuração acima
Substituir placeholders: Atualizar credenciais API
Reiniciar Cursor: Necessário para que as mudanças MCP tenham efeito
Verificar conexão: Verificar status MCP na paleta de comandos do Cursor
Passo 2: Instalar CLI do Apillon (Método Alternativo)
Passo 3: Configurar Autenticação
Passo 4: Configurar MCP para Implantação Automatizada
Criar .apillon.json na raiz do projeto:
Passo 5: Script de Implantação MCP
Passo 6: Variáveis de Ambiente
Melhores Práticas para Agentes
Configurar MCP primeiro: Configurar servidor MCP do Apillon no seu IDE (Claude Desktop ou Cursor) antes de começar a implantação.
Sempre usar CLI Apillon mais recente:npm install -g @apillon/cli@latest
Credenciais seguras: Armazenar API keys e UUIDs como variáveis de ambiente, nunca no código
Guiar usuário na configuração da conta: Instruir claramente usuários sobre criação de conta Apillon e geração de API key
Verificar implantações: Sempre verificar status da implantação após upload
Usar ambiente produção: Para entregas finais de hackathon
Monitorar custos: Apillon usa modelo pay-per-use
Testar localmente primeiro: Sempre testar builds antes de implantar
Reiniciar seu IDE: Após mudanças de configuração MCP (Claude Desktop ou Cursor)
Verificar conexão MCP: Verificar se servidor MCP do Apillon está conectado corretamente antes da implantação
DIRETRIZES DE ESCRITA PARA LLMs
Ao criar documentação ou ajudar desenvolvedores:
CHECKLIST DE SOLUÇÃO DE PROBLEMAS
Primeiro: Tente kitdot@latest init com projeto novo e copie código existente
Quando a implantação falhar, verifique:
LIMITAÇÕES E SOLUÇÕES ALTERNATIVAS
Limitações Atuais
Tamanho do Contrato: Limite de 100KB de bytecode
OpenZeppelin: A maioria das bibliotecas são muito grandes
Estabilidade da Rede: Preview release, possível tempo de inatividade
Ferramentas de Depuração: Limitadas comparadas à mainnet
Documentação: Escassa, soluções dirigidas pela comunidade
Soluções Alternativas Recomendadas
Problemas de Tamanho: Use implementações customizadas mínimas
Lógica Complexa: Divida entre múltiplos contratos
Gerenciamento de Estado: Use eventos para dados off-chain
Experiência do Usuário: Forneça mensagens de erro claras
Testes: Testes locais extensivos antes da implantação
Este guia fornece informações abrangentes para implantação bem-sucedida de smart contracts na Paseo TestNet usando Claude Code, incluindo todas as configurações críticas, problemas comuns e estratégias de otimização.
contract MinimalERC721 {
mapping(uint256 => address) private _owners;
mapping(address => uint256) private _balances;
mapping(uint256 => address) private _tokenApprovals;
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
function ownerOf(uint256 tokenId) public view returns (address) {
return _owners[tokenId];
}
function balanceOf(address owner) public view returns (uint256) {
return _balances[owner];
}
function approve(address to, uint256 tokenId) public {
address owner = ownerOf(tokenId);
require(msg.sender == owner, "Not authorized");
_tokenApprovals[tokenId] = to;
emit Approval(owner, to, tokenId);
}
function transferFrom(address from, address to, uint256 tokenId) public {
require(_isApprovedOrOwner(msg.sender, tokenId), "Not authorized");
_transfer(from, to, tokenId);
}
function _mint(address to, uint256 tokenId) internal {
require(to != address(0), "Invalid address");
require(_owners[tokenId] == address(0), "Already minted");
_balances[to] += 1;
_owners[tokenId] = to;
emit Transfer(address(0), to, tokenId);
}
function _transfer(address from, address to, uint256 tokenId) internal {
require(ownerOf(tokenId) == from, "Not owner");
require(to != address(0), "Invalid address");
delete _tokenApprovals[tokenId];
_balances[from] -= 1;
_balances[to] += 1;
_owners[tokenId] = to;
emit Transfer(from, to, tokenId);
}
function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {
address owner = ownerOf(tokenId);
return (spender == owner || _tokenApprovals[tokenId] == spender);
}
}
// Use estimação de gas legacy com buffer de segurança
const gasLimit = await provider.estimateGas({
to: contractAddress,
data: contractInterface.encodeFunctionData("functionName", [args]),
});
// Adicione buffer de 10-20% para segurança
const adjustedGasLimit = gasLimit.mul(120).div(100);
// Envie transação com gas explícito e tipo legacy
const tx = await contract.functionName(args, {
gasLimit: adjustedGasLimit,
type: 0, // Use tipo de transação legacy
});
// Use limites de gas fixos para operações previsíveis
const tx = await contract.functionName(args, {
gasLimit: 100000, // Ajuste baseado na complexidade da função
type: 0, // Tipo de transação legacy
gasPrice: ethers.utils.parseUnits("20", "gwei"), // Opcional: definir preço do gas
});
async function sendTransactionWithRetry(
contract,
functionName,
args,
retries = 3
) {
for (let i = 0; i < retries; i++) {
try {
// Tente com gas estimado primeiro
const estimatedGas = await contract.estimateGas[functionName](...args);
const tx = await contract[functionName](...args, {
gasLimit: estimatedGas.mul(120).div(100),
type: 0,
});
return tx;
} catch (error) {
if (i === retries - 1) throw error;
// Fallback para limite de gas fixo
try {
const tx = await contract[functionName](...args, {
gasLimit: 200000, // Limite fixo mais alto
type: 0,
});
return tx;
} catch (fallbackError) {
if (i === retries - 1) throw fallbackError;
}
}
}
}