Saltar al contenido principal

Requisitos previos

Preparación del contrato

El siguiente contrato de ejemplo se usará a lo largo de esta guía:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

contract SimpleStorage {
    string private storedData;
    address public owner;
    
    event DataStored(string data, address indexed by);
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    
    constructor(string memory initialData) {
        storedData = initialData;
        owner = msg.sender;
        emit DataStored(initialData, msg.sender);
    }
    
    modifier onlyOwner() {
        require(msg.sender == owner, "Only owner can perform this action");
        _;
    }
    
    function setData(string memory data) public onlyOwner {
        storedData = data;
        emit DataStored(data, msg.sender);
    }
    
    function getData() public view returns (string memory) {
        return storedData;
    }
    
    function transferOwnership(address newOwner) public onlyOwner {
        require(newOwner != address(0), "New owner cannot be zero address");
        emit OwnershipTransferred(owner, newOwner);
        owner = newOwner;
    }
}

Despliega con Foundry

Foundry es un toolkit rápido basado en Rust para desarrollo en Ethereum.
  1. Instala Foundry:
    curl -L https://foundry.paradigm.xyz | bash
    
    Puede que tengas que ejecutar source a tu archivo .bashrc o .zshrc
  2. Configura un nuevo proyecto Foundry:
    foundryup
    forge init my-plasma-project
    cd my-plasma-project
    
  3. Actualiza foundry.toml con la configuración de la testnet de Plasma:
    [profile.default]
    src = "src"
    out = "out"
    libs = ["lib"]
    solc_version = "0.8.28"
    optimizer = true
    optimizer_runs = 200
    
    [rpc_endpoints]
    plasma_testnet = "https://testnet-rpc.plasma.to"
    
  4. Crea un archivo .env en la raíz de tu proyecto:
    PRIVATE_KEY=your_private_key_here
    RPC_URL=https://testnet-rpc.plasma.to
    
  5. Carga las variables de entorno:
    source .env
    
  6. Guarda el código del contrato como src/SimpleStorage.sol y luego despliégalo:
    forge create src/SimpleStorage.sol:SimpleStorage \
        --rpc-url $RPC_URL \
        --private-key $PRIVATE_KEY \
        --constructor-args "Hello, Plasma!"
    
    Foundry generará el hash de la transacción de despliegue y la dirección del contrato:
    [⠊] Compiling...
    [⠢] Compiling 1 files with Solc 0.8.28
    [⠆] Solc 0.8.28 finished in 124.81ms
    Compiler run successful!
    Warning: Dry run enabled, not broadcasting transaction
    
    Contract: SimpleStorage
    Transaction: {
      "from": "0xbd828f7679656f8f830b89611c933017442f2ebf",
      "to": null,
      "maxFeePerGas": "0xf",
      "maxPriorityFeePerGas": "0x1",
      "gas": "0x69f18",
    
    [...]
    
  7. Prueba tu contrato desplegado usando la herramienta cast de Foundry. Primero, lee los datos almacenados:
    cast call 0x742d35Cc6610C7532C8582d4C371Acb1D5f44D7F \
        "getData()" \
        --rpc-url $RPC_URL
    
  8. Luego actualiza los datos y léelos nuevamente:
    # Update the stored data.
    cast send 0x742d35Cc6610C7532C8582d4C371Acb1D5f44D7F \
        "setData(string)" "Updated from Foundry" \
        --private-key $PRIVATE_KEY \
        --rpc-url $RPC_URL
    
    # Read the stored data again.
    cast call 0x742d35Cc6610C7532C8582d4C371Acb1D5f44D7F \
        "getData()" \
        --rpc-url $RPC_URL
    
    Esto debería generar algo como:
    blockHash            0x68fbd2aaf1de9c577869056ca634f2103fa1695673a94d8c049d0b78d3733aac
    blockNumber          1200423
    contractAddress
    cumulativeGasUsed    21712
    effectiveGasPrice    8
    from                 0xBd828F7679656F8f830b89611C933017442F2EbF
    gasUsed              21712
    logs                 []
    logsBloom            0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
    root
    status               1 (success)
    transactionHash      0xcf5b1fcc8d19f7cf7992f6e6a8b3ade74e07afbd0da0b9fce26bda4c9503a12b
    transactionIndex     0
    type                 2
    blobGasPrice
    blobGasUsed
    to                   0x742D35Cc6610c7532C8582D4C371aCb1D5F44D7F
    0x
    

Despliega con Hardhat

Hardhat es un framework de desarrollo completo con amplio soporte de plugins.
  1. Asegúrate de tener lo siguiente:
  • Node.js 20+ (requerido para compatibilidad con Hardhat moderno)
  • Familiaridad con el desarrollo en Ethereum
  • Una wallet con tokens de testnet (consulta los detalles de la cadena testnet)
Node.js 18 e inferiores ya no son soportados por Hardhat y pueden causar problemas de compatibilidad con módulos ES.
  1. Verifica la versión de Node.js:
    node --version
    
    Si estás usando Node.js 18 o inferior, actualiza a Node.js 20+:
    # Using nvm (recommended)
    nvm install 20
    nvm use 20
    
    # Or using NodeSource repository (Ubuntu/Debian)
    curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
    sudo apt-get install -y nodejs
    
  2. Inicializa un nuevo proyecto Hardhat:
    mkdir my-plasma-hardhat-project
    cd my-plasma-hardhat-project
    npm init -y
    npm install --save-dev hardhat@latest @nomicfoundation/hardhat-toolbox@latest
    npm install dotenv@latest ethers@latest
    npx hardhat init
    
    Si encuentras problemas de compatibilidad, revisa la documentación de Hardhat para las últimas versiones soportadas.
    Elige Create a JavaScript project cuando se te solicite.
  3. Actualiza hardhat.config.js:
    require("@nomicfoundation/hardhat-toolbox");
    require("dotenv").config();
    
    /** @type import('hardhat/config').HardhatUserConfig */
    module.exports = {
      solidity: {
        version: "0.8.28",
        settings: {
          optimizer: {
            enabled: true,
            runs: 200
          }
        }
      },
      networks: {
        plasmaTestnet: {
          url: process.env.RPC_URL,
          chainId: 9746,
          accounts: [process.env.PRIVATE_KEY],
          gasPrice: 1000000000, // 1 gwei
        }
      },
      etherscan: {
        apiKey: {
          plasmaTestnet: process.env.ETHERSCAN_API_KEY
        },
        customChains: [
          {
            network: "plasmaTestnet",
            chainId: 9746,
            urls: {
              apiURL: "https://testnet.plasmascan.to/api",
              browserURL: "https://testnet.plasmascan.to/"
            }
          }
        ]
      }
    };
    
  4. Crea un archivo .env en la raíz de tu proyecto:
    PRIVATE_KEY=your_private_key_here
    RPC_URL=https://testnet-rpc.plasma.to
    ETHERSCAN_API_KEY=your_api_key_for_verification
    
  5. Crea el directorio scripts y el script de despliegue:
    mkdir scripts
    
  6. Crea scripts/deploy.js:
    const { ethers } = require("hardhat");
    
    async function main() {
      // Get the contract factory
      const SimpleStorage = await ethers.getContractFactory("SimpleStorage");
    
      console.log("Deploying SimpleStorage contract...");
    
      // Deploy the contract with constructor arguments
      const simpleStorage = await SimpleStorage.deploy("Hello, Plasma from Hardhat!");
    
      // Wait for deployment to complete
      await simpleStorage.waitForDeployment();
    
      const contractAddress = await simpleStorage.getAddress();
      console.log("SimpleStorage deployed to:", contractAddress);
      console.log("Transaction hash:", simpleStorage.deploymentTransaction().hash);
    
      // Wait a few blocks for the transaction to be included
      console.log("Waiting for block confirmations...");
      await simpleStorage.deploymentTransaction().wait(5);
    
      console.log("✅ Deployment completed successfully!");
    }
    
    main()
      .then(() => process.exit(0))
      .catch((error) => {
        console.error(error);
        process.exit(1);
      });
    
  7. Guarda el código del contrato como contracts/SimpleStorage.sol y luego despliégalo:
    npx hardhat run scripts/deploy.js --network plasmaTestnet
    
  8. ¡Listo!

Despliega con Ethers.js

Ethers.js provee un flujo de despliegue programático y mínimo.
  1. Crea un nuevo proyecto:
    mkdir my-plasma-ethers-project
    cd my-plasma-ethers-project
    npm init -y
    npm install ethers dotenv solc
    
  2. Crea un archivo .env en la raíz de tu proyecto:
    PRIVATE_KEY=<your_private_key_here>
    RPC_URL=https://testnet-rpc.plasma.to
    ETHERSCAN_API_KEY=your_api_key_for_verification
    
  3. Crea compile.js para compilar tu contrato Solidity:
    const fs = require('fs');
    const solc = require('solc');
    require('dotenv').config();
    
    // Read the contract source code.
    const contractSource = fs.readFileSync('SimpleStorage.sol', 'utf8');
    
    // Prepare the input for the Solidity compiler.
    const input = {
      language: 'Solidity',
      sources: {
        'SimpleStorage.sol': {
          content: contractSource,
        },
      },
      settings: {
        outputSelection: {
          '*': {
            '*': ['*'],
          },
        },
        optimizer: {
          enabled: true,
          runs: 200,
        },
      },
    };
    
    // Compile the contract.
    const output = JSON.parse(solc.compile(JSON.stringify(input)));
    
    // Check for compilation errors.
    if (output.errors) {
      output.errors.forEach((error) => {
        console.error(error.formattedMessage);
      });
    }
    
    // Extract the contract data.
    const contract = output.contracts['SimpleStorage.sol']['SimpleStorage'];
    const abi = contract.abi;
    const bytecode = contract.evm.bytecode.object;
    
    // Save compilation artifacts.
    fs.writeFileSync('SimpleStorage.json', JSON.stringify({
      abi: abi,
      bytecode: bytecode
    }, null, 2));
    
    console.log('Contract compiled successfully!');
    
  4. Guarda tu contrato como SimpleStorage.sol y luego compílalo:
    node compile.js
    
    Esto debería generar:
    Contract compiled successfully!
    
  5. Crea deploy.js:
    const { ethers } = require('ethers');
    const fs = require('fs');
    require('dotenv').config();
    
    async function deploy() {
      // Set up provider and wallet.
      const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
      const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
      
      console.log('Deploying from account:', wallet.address);
      
      // Check account balance.
      const balance = await provider.getBalance(wallet.address);
      console.log('Account balance:', ethers.formatEther(balance), 'XPL');
      
      // Load compiled contract.
      const contractData = JSON.parse(fs.readFileSync('SimpleStorage.json', 'utf8'));
      
      // Create contract factory.
      const factory = new ethers.ContractFactory(
        contractData.abi,
        contractData.bytecode,
        wallet
      );
      
      // Deploy the contract.
      console.log('Deploying contract...');
      const contract = await factory.deploy("Hello, Plasma from Ethers.js!", {
        gasLimit: 500000, // Set a reasonable gas limit.
        gasPrice: ethers.parseUnits('1', 'gwei'), // 1 gwei gas price.
      });
      
      // Wait for deployment.
      await contract.waitForDeployment();
      
      const contractAddress = await contract.getAddress();
      console.log('Contract deployed to:', contractAddress);
      console.log('Transaction hash:', contract.deploymentTransaction().hash);
      
      // Save deployment info.
      const deploymentInfo = {
        contractAddress: contractAddress,
        transactionHash: contract.deploymentTransaction().hash,
        deployer: wallet.address,
        network: 'plasmaTestnet',
        timestamp: new Date().toISOString()
      };
      
      fs.writeFileSync('deployment.json', JSON.stringify(deploymentInfo, null, 2));
      
      return contract;
    }
    
    async function interact(contract) {
      console.log('\nInteracting with deployed contract...');
      
      // Read initial data.
      const initialData = await contract.getData();
      console.log('Initial data:', initialData);
      
      // Update data.
      const updateTx = await contract.setData("Updated via Ethers.js");
      await updateTx.wait();
      console.log('Data updated. Transaction hash:', updateTx.hash);
      
      // Read updated data.
      const updatedData = await contract.getData();
      console.log('Updated data:', updatedData);
      
      // Get owner.
      const owner = await contract.owner();
      console.log('Contract owner:', owner);
    }
    
    // Main execution.
    deploy()
      .then(async (contract) => {
        await interact(contract);
        console.log('\nDeployment and interaction completed successfully!');
      })
      .catch((error) => {
        console.error('Error:', error);
        process.exit(1);
      });
    
  6. Ejecuta el script de despliegue:
    node deploy.js
    
    Esto debería generar algo como:
    Deploying from account: 0xBd828F7679656F8f830b89611C933017442F2EbF
    Account balance: 98539.897261933991888304 XPL
    Deploying contract...
    Contract deployed to: 0xf298A2A7BC526F9228B8C422D38f3c2E0D15449F
    Transaction hash: 0x3d16cc55b9148bac9ac20981d9748a0fc89861c6beb92a2f869bb09b75f685b2
    
    Interacting with deployed contract...
    Initial data: Hello, Plasma from Ethers.js!
    Data updated. Transaction hash: 0xe3328862ece5d0ca4ca84d25c4130d6749ec872c24bf76eea23dfa8c50c22505
    Updated data: Updated via Ethers.js
    Contract owner: 0xBd828F7679656F8f830b89611C933017442F2EbF
    
    Deployment and interaction completed successfully!
    

Verifica el despliegue

Después de desplegar con cualquiera de los métodos anteriores, verifica el despliegue de tu contrato.

Solución de problemas

Problemas de compatibilidad con módulos ES

Si encuentras errores como Error [ERR_REQUIRE_ESM]: require() of ES Module, esto generalmente significa:
  1. La versión de Node.js es demasiado antigua: Actualiza a Node.js 20+ como se menciona en los requisitos previos
  2. Desajuste en la versión de la dependencia: Si no es posible actualizar Node.js, puedes degradar la dependencia problemática:
    npm install micro-eth-signer@0.10.0
    

Advertencias de versión de Node.js

Si ves advertencias sobre versiones no soportadas de Node.js:
WARNING: You are currently using Node.js v18.19.1, which is not supported by Hardhat
Actualiza a Node.js 20+ siguiendo los pasos en la sección de requisitos previos.

Fondos insuficientes

Error: insufficient funds for intrinsic transaction cost
Asegúrate de que tu wallet tenga suficientes tokens de testnet. Visita un faucet de testnet.

Configuración de red incorrecta

Error: network with chainId "1" doesn't match the configured chainId "9746"
Verifica que tu configuración de red coincida con los detalles de la cadena testnet.

Falla en la estimación de gas

Error: cannot estimate gas
Establece límites de gas explícitos en tu configuración de despliegue:
// In your deploy script
const simpleStorage = await SimpleStorage.deploy("Hello, Plasma from Hardhat!", {
  gasLimit: 500000
});