Compilación y despliegue de contratos inteligentes con Truffle y Ganache

0
2008

Índice

  1. Introducción
  2. Instalando Visual Studio Code y diversos plugins
  3. Iniciando el contrato
  4. Conectando el contrato con Ganache
  5. Interactuando con el contrato desde Truffle
  6. Conclusión

Introducción

En tutoriales anteriores ya hemos visto un montón de cosas sobre criptomonedas: comprar (Binance), guardarlas en un monedero (MetaMask), crear una red de pruebas (Ganache), construir un contrato inteligente (con Solidity), desplegarlo en Remix, etc. 

Os invito a repasar los tutoriales anteriores antes de continuar:

Cómo comprar e invertir criptomonedas en Binance: https://adictosaltrabajo.com/2022/01/14/como-comprar-e-invertir-criptomonedas-en-binance/

Entendiendo las criptomonedas: https://adictosaltrabajo.com/2022/01/24/entendiendo-las-criptomonedas/

Votación con contratos inteligentes en Remix: https://adictosaltrabajo.com/2022/01/31/votacion-con-contratos-inteligentes-en-remix/

Usando la wallet MetaMask y la red local Ganache: https://adictosaltrabajo.com/2022/02/09/usando-la-wallet-metamask-y-la-red-local-ganache/

Creando tokens Ethereum: https://adictosaltrabajo.com/2022/03/14/creando-tokens-ethereum/

Ahora vamos a ver cómo utilizar Visual Studio Code para editar un contrato (y no depender de una plataforma Web como Remix), compilar el contrato en Truffle en local, enganchar el contrato con Ganache e interactuar con el contrato desde la consola de desarrollo de Truffle.

Instalando Visual Studio Code y diversos plugins

Podemos descargar e instalar MS Visual Studio Code desde: https://code.visualstudio.com.

Captura de pantalla de la página de descarga de Visual Studio Code.

Una vez instalado y abierto vamos a la zona de plugins e instalamos: Blockchain Development kit for Ethereum.

Captura del plugin Blockchain Development kit for Ethereum.

Ahora vamos a la paleta de comandos y empezamos a escribir Blockchain. Aparecerán las opciones y seleccionaremos New Solidity Project.

Captura de pantalla de la paleta de comando con la selección de un proyecto Solidity.

Ahora elegimos la opción de crear un nuevo proyecto básico.

Captura de pantalla de la paleta de comando con la selección proyecto básico.

El plugin nos genera una estructura de ficheros mínima con un ejemplo de proyecto Solidity. 

Captura de pantalla visualizando los ficheros creados por el plugin.

Podemos pulsar la opción de construir contrato y nos dirá que es necesario instalar algunas aplicaciones requeridas: Node, Git, npm, Truffle y Ganache.

Captura de pantalla con la opción de compilar en MS Visual Studio Code y el error de las dependencias.

Pulsamos secuencialmente la opción de node, git y npm.

No vamos a utilizar las opciones de Truffle o Ganache porque en Mac me han dado problemas con la última versión y lo vamos a hacer a mano.

Captura de pantalla de las dependencias necesarias.

Ahora abrimos un terminal en MS Visual Studio Code y tratamos de instalar la última versión de Truffle. A mí no me ha funcionado pero es posible que funcione cuando leas el tutorial.

Vamos a demostrar cómo no funciona y los pasos para conseguir que funcione.

Usamos el comando estándar de instalación con la última versión.

npm install -g truffle

Nos dice que no tenemos permisos para instalar. Aquí ya empezamos con que “la abuela fuma” instalando con privilegios de administrador (no os creáis que me acaba de convencer), poniendo delante sudo.

Captura de pantalla al instalar con el comando estándar de Truffle.

Vamos a instalar como administrador con sudo. 

sudo npm install -g truffle

Tampoco el resultado es satisfactorio. La última versión disponible parece que da errores. 

Captura de pantalla de la instalación de Truffle como administrador, con errores.

Vamos a buscar versiones anteriores a ver si tenemos más suerte con la instalación. Revisamos las versiones disponibles en https://www.npmjs.com/package/truffle.

Captura de pantalla de https://www.npmjs.com/package/truffle donde vemos las versiones disponibles de Truffle.

Probamos con alguna versión anterior. 

sudo npm install –g truffle@5.4.29

Esta sí parece funcionar. También es algo preocupante que dice que los paquetes instalados tienen 89 vulnerabilidades. Si ejecutas sudo ppm Audit fix —force es posible que te las corrija (dependerá de la versión).

Captura de pantalla de las vulnerabilidades en paquetes.

Una de las primeras cosas que tendríamos que hacer es ejecutar “truffle version” para ver las versiones de Solidity que podemos utilizar dentro del código. En este caso la versión 0.5.16.

Captura de pantalla de la salida del comando truffle version.

Esto es ya un problema, porque si revisamos el tutorial de votaciones (ballot) desde https://adictosaltrabajo.com/2022/01/31/votacion-con-contratos-inteligentes-en-remix/ nos dice que funcionará para versiones de la 0.7 a la 0.9.

Al tratar de compilar el contrato inteligente ballot me ha dado errores en el constructor. Mas adelante podemos arreglarlo y adaptarlo a la versión del compilador (con convertirlo en un método normal nos valdría, obligando a inicializar de otro modo).

De todos modos ahora no nos importa demasiado porque vamos a utilizar en ejemplo más sencillo porque para el caso es lo mismo.

 Esta es la directiva que indica las versiones de compilación de Solidity (la versión más reciente es 0.8.1).

pragma solidity >=0.7.0 <0.9.0;
Captura de pantalla del código de ballot.

Iniciando el contrato

Una vez instalado Truffle ejecutamos el comando truffle init. Esto inicializará el proyecto para trabajar con Truffle y nos preguntará si queremos reescribir los ficheros. 

Sobre la estructura creada vamos a crear un nuevo fichero llamado ejemplo y copiamos sobre este el contenido del ejemplo básico tomado de Remix. 

En él disponemos simplemente de un número y dos funciones: store y retrieve.

pragma solidity >=0.7.0 <0.9.0;

/**
 * @title Storage
 * @dev Store & retrieve value in a variable
 */
contract Storage {

    uint256 number;

    /**
     * @dev Store value in variable
     * @param num value to store
     */
    function store(uint256 num) public {
        number = num;
    }

    /**
     * @dev Return value 
     * @return value of 'number'
     */
    function retrieve() public view returns (uint256){
        return number;
    }
}

Podemos ver aquí como queda.

Captura de pantallas de MS Visual Studio Code con el aspecto del proyecto básico.

Para conocer los siguientes pasos os recomiendo visitar el Truffle Quickstart en https://trufflesuite.com/docs/truffle/quickstart

Ahora tenemos que asegurarnos que los ficheros truffle-config.js y el de deploy son los correctos.

En el fichero truffe-config.js activamos la parte de redes de desarrollo (networks: { development: )

/**
 * Use this file to configure your truffle project. It's seeded with some
 * common settings for different networks and features like migrations,
 * compilation and testing. Uncomment the ones you need or modify
 * them to suit your project as necessary.
 *
 * More information about configuration can be found at:
 *
 * truffleframework.com/docs/advanced/configuration
 *
 * To deploy via Infura you'll need a wallet provider (like @truffle/hdwallet-provider)
 * to sign your transactions before they're sent to a remote public node. Infura accounts
 * are available for free at: infura.io/register.
 *
 * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate
 * public/private key pairs. If you're publishing your code to GitHub make sure you load this
 * phrase from a file you've .gitignored so it doesn't accidentally become public.
 *
 */

// const HDWalletProvider = require('@truffle/hdwallet-provider');
// const infuraKey = "fj4jll3k.....";
//
// const fs = require('fs');
// const mnemonic = fs.readFileSync(".secret").toString().trim();

module.exports = {
  /**
   * Networks define how you connect to your ethereum client and let you set the
   * defaults web3 uses to send transactions. If you don't specify one truffle
   * will spin up a development blockchain for you on port 9545 when you
   * run `develop` or `test`. You can ask a truffle command to use a specific
   * network from the command line, e.g
   *
   * $ truffle test --network 
   */

  networks: {
    // Useful for testing. The `development` name is special - truffle uses it by default
    // if it's defined here and no other network is specified at the command line.
    // You should run a client (like ganache-cli, geth or parity) in a separate terminal
    // tab if you use this network and you must also set the `host`, `port` and `network_id`
    // options below to some value.
    //
    development: {
      host: "127.0.0.1", // Localhost (default: none)
      port: 7545, // Standard Ethereum port (default: none)
      network_id: "*", // Any network (default: none)
    },
    // Another network with more advanced options...
    // advanced: {
    // port: 8777,             // Custom port
    // network_id: 1342,       // Custom network
    // gas: 8500000,           // Gas sent with each transaction (default: ~6700000)
    // gasPrice: 20000000000,  // 20 gwei (in wei) (default: 100 gwei)
    // from: <Adress>,        // Account to send txs from (default: accounts[0])
    // websockets: true        // Enable EventEmitter interface for web3 (default: false)
    // },
    // Useful for deploying to a public network.
    // NB: It's important to wrap the provider as a function.
    // ropsten: {
    // provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`),
    // network_id: 3,       // Ropsten's id
    // gas: 5500000,        // Ropsten has a lower block limit than mainnet
    // confirmations: 2,    // # of confs to wait between deployments. (default: 0)
    // timeoutBlocks: 200,  // # of blocks before a deployment times out  (minimum/default: 50)
    // skipDryRun: true     // Skip dry run before migrations? (default: false for public nets )
    // },
    // Useful for private networks
    // private: {
    // provider: () => new HDWalletProvider(mnemonic, `https://network.io`),
    // network_id: 2111,   // This network is yours, in the cloud.
    // production: true    // Treats this network as if it was a public net. (default: false)
    // }
  },

  // Set default mocha options here, use special reporters etc.
  mocha: {
    // timeout: 100000
  },

  // Configure your compilers
  compilers: {
    solc: {
      // version: "0.5.1",    // Fetch exact version from solc-bin (default: truffle's version)
      // docker: true,        // Use "0.5.1" you've installed locally with docker (default: false)
      // settings: {          // See the solidity docs for advice about optimization and evmVersion
      //  optimizer: {
      //    enabled: false,
      //    runs: 200
      //  },
      //  evmVersion: "byzantium"
      // }
    },
  },
};

He reciclado el fichero con la carpeta de migraciones para decir que despliegue el contrato “ejemplo”.

const ejemplo = artifacts.require("ejemplo");
module.exports = function (deployer) {
  deployer.deploy(ejemplo);
};

Con esta estructura, ejecutamos “truffle compile” y comprobamos que funciona. Os recomiendo que si os falla algo, hagáis un cambio en el fichero del contrato “.sol” y guardéis y compiléis.

Conectando el contrato con Ganache

Ahora vamos tratar de conectar el contrato que hemos compilado con truffle para que se despliegue en Ganache.

Aunque ya contamos cómo se instalaba en el tutorial anterior, repetimos para tener la secuencia completa.

Vamos a la página de Truffle Suite y descargamos e instalamos.

Arrastramos Ganache a la carpeta de aplicaciones.

Captura de pantalla de la instalación de Ganache.

Ejecutamos Quickstart y comprobamos las cuentas y la dirección del servidor. Verificamos que se encuentra en http://localhost:7545 y coincide con los datos de truffle-config.js

Captura de pantalla de creación de la creación del espacio de trabajo.
Captura de la pantalla principal de Ganache.

Ahora vamos a la sección de contratos de Ganache. Pulsamos el botón de enlazar al proyecto Truffle.

Captura de pantalla de contratos en Ganache.

Ahora hay que pulsar la opción de Add Projects. Introducimos el trayecto del fichero truffle-config.js que hemos previamente editado activando el servidor local.

Captura de pantalla con el fichero truffle-config.js seleccionado.

Podemos a ver los contratos que maneja truffle.

Captura de pantalla de los contratos disponibles.

Ahora volvemos al terminal y ejecutamos truffle deploy que desplegará nuestro ejemplo (migración).

Captura de pantalla del resultado de ejecutar en la consola truffle deploy.

Y veremos que ya está disponible nuestro ejemplo desplegado (no hagáis caso al de Migrations porque invoqué el deploy sin modificar previamente el fichero de la carpeta Migrations).

Captura de pantalla de Ganache con el contrato ejemplo desplegado.

Interactuando con el contrato desde Truffle

Ahora, si queremos interactuar con el contrato desde truffle podemos seguir las instrucciones de https://trufflesuite.com/guides/debugging-an-example-smart-contract/index.html.

Ojo que no es exactamente igual el código pero nos vale para hacernos una idea del proceso.

Ejecutamos el comando truffle develop y aparecerá la consola de desarrollo de Truffle.

Ahora escribimos en el terminal el nombre del contrato “ejemplo” y nos aparecerá la estructura de almacenamiento con un montón de información relevante.

Captura de pantalla del entorno de desarrollo de truffle.

Obtenemos la siguiente respuesta:

truffle(develop)> ejemplo
[Function: TruffleContract] {
  _constructorMethods: {
    configureNetwork: [Function: configureNetwork],
    setProvider: [Function: setProvider],
    new: [Function: new],
    at: [AsyncFunction: at],
    deployed: [AsyncFunction: deployed],
    defaults: [Function: defaults],
    hasNetwork: [Function: hasNetwork],
    isDeployed: [Function: isDeployed],
    detectNetwork: [AsyncFunction: detectNetwork],
    setNetwork: [Function: setNetwork],
    setNetworkType: [Function: setNetworkType],
    setWallet: [Function: setWallet],
    resetAddress: [Function: resetAddress],
    link: [Function: link],
    clone: [Function: clone],
    addProp: [Function: addProp],
    toJSON: [Function: toJSON],
    decodeLogs: [Function: decodeLogs]
  },
  _properties: {
    contract_name: { get: [Function: get], set: [Function: set] },
    contractName: { get: [Function: get], set: [Function: set] },
    gasMultiplier: { get: [Function: get], set: [Function: set] },
    timeoutBlocks: { get: [Function: get], set: [Function: set] },
    autoGas: { get: [Function: get], set: [Function: set] },
    numberFormat: { get: [Function: get], set: [Function: set] },
    abi: { get: [Function: get], set: [Function: set] },
    metadata: [Function: metadata],
    network: [Function: network],
    networks: [Function: networks],
    address: { get: [Function: get], set: [Function: set] },
    transactionHash: { get: [Function: get], set: [Function: set] },
    links: [Function: links],
    events: [Function: events],
    binary: [Function: binary],
    deployedBinary: [Function: deployedBinary],
    unlinked_binary: { get: [Function: get], set: [Function: set] },
    bytecode: { get: [Function: get], set: [Function: set] },
    deployedBytecode: { get: [Function: get], set: [Function: set] },
    sourceMap: { get: [Function: get], set: [Function: set] },
    deployedSourceMap: { get: [Function: get], set: [Function: set] },
    source: { get: [Function: get], set: [Function: set] },
    sourcePath: { get: [Function: get], set: [Function: set] },
    legacyAST: { get: [Function: get], set: [Function: set] },
    ast: { get: [Function: get], set: [Function: set] },
    compiler: { get: [Function: get], set: [Function: set] },
    schema_version: [Function: schema_version],
    schemaVersion: [Function: schemaVersion],
    updated_at: [Function: updated_at],
    updatedAt: [Function: updatedAt],
    userdoc: [Function: userdoc],
    devdoc: [Function: devdoc],
    networkType: { get: [Function: get], set: [Function: set] },
    immutableReferences: { get: [Function: get], set: [Function: set] },
    generatedSources: { get: [Function: get], set: [Function: set] },
    deployedGeneratedSources: { get: [Function: get], set: [Function: set] },
    db: { get: [Function: get], set: [Function: set] }
  },
  _property_values: {},
  _json: {
    contractName: 'ejemplo',
    abi: [ [Object], [Object] ],
    metadata: `{"compiler":{"version":"0.5.16+commit.9c3226ce"},"language":"Solidity","output":{"abi":[{"constant":true,"inputs":[],"name":"retrieve","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"num","type":"uint256"}],"name":"store","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}],"devdoc":{"details":"Almacenar y recuperar una variable","methods":{"retrieve()":{"details":"Return value","return":"value of 'number'"},"store(uint256)":{"details":"Store value in variable","params":{"num":"value to store"}}},"title":"Ejemplo con Truffle"},"userdoc":{"methods":{}}},"settings":{"compilationTarget":{"project:/contracts/ejemplo.sol":"ejemplo"},"evmVersion":"istanbul","libraries":{},"optimizer":{"enabled":false,"runs":200},"remappings":[]},"sources":{"project:/contracts/ejemplo.sol":{"keccak256":"0xbced53232932c32afc1ada366ec764573b55b2519c57bbb4286d6502b10c51f1","urls":["bzz-raw://87de1b3c2f835122b1720cd72e522b2319d7dd44dc93e1424b4f027e16cdb9ae","dweb:/ipfs/QmasF3uSvyETpaxem9f5ZmYWQgQnpjB6DeLZ3vUQUBy6FP"]}},"version":1}`,
    bytecode: '0x608060405234801561001057600080fd5b5060c68061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c80632e64cec11460375780636057361d146053575b600080fd5b603d607e565b6040518082815260200191505060405180910390f35b607c60048036036020811015606757600080fd5b81019080803590602001909291905050506087565b005b60008054905090565b806000819055505056fea265627a7a7231582033f5ed74314021bebb159a47a80758dde34c51d0e0a3e273838038781befc63564736f6c63430005100032',
    deployedBytecode: '0x6080604052348015600f57600080fd5b506004361060325760003560e01c80632e64cec11460375780636057361d146053575b600080fd5b603d607e565b6040518082815260200191505060405180910390f35b607c60048036036020811015606757600080fd5b81019080803590602001909291905050506087565b005b60008054905090565b806000819055505056fea265627a7a7231582033f5ed74314021bebb159a47a80758dde34c51d0e0a3e273838038781befc63564736f6c63430005100032',
    immutableReferences: undefined,
    generatedSources: undefined,
    deployedGeneratedSources: undefined,
    sourceMap: '151:389:0:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;151:389:0;;;;;;;',
    deployedSourceMap: '151:389:0:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;151:389:0;;;;;;;;;;;;;;;;;;;;;;;;458:80;;;:::i;:::-;;;;;;;;;;;;;;;;;;;314:64;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;314:64:0;;;;;;;;;;;;;;;;;:::i;:::-;;458:80;499:7;525:6;;518:13;;458:80;:::o;314:64::-;368:3;359:6;:12;;;;314:64;:::o',
    source: '// SPDX-License-Identifier: GPL-3.0\n' +
      '\n' +
      'pragma solidity >=0.5.0 <0.9.0;\n' + '\n' + 
      '\n' +
      '/**\n' +
      ' * @title Ejemplo con Truffle\n' +
      ' * @dev Almacenar y recuperar una variable\n' +
      ' */\n' +
      'contract ejemplo {\n' +
      '    // primera verion de contrato\n' +
      '    uint256 number;\n' +
      '\n' +
      '    /**\n' +
      '     * @dev Store value in variable\n' +
      '     * @param num value to store\n' +
      '     */\n' +
      '    function store(uint256 num) public {\n' +
      '        number = num;\n' +
      '    }\n' +
      '\n' +
      '    /**\n' +
      '     * @dev Return value\n' +
      "     * @return value of 'number'\n" +
      '     */\n' +
      '    function retrieve() public view returns (uint256) {\n' +
      '        return number;\n' +
      '    }\n' +
      '}\n',
    sourcePath: '/Users/rcanales/votaciones/contracts/ejemplo.sol',
    ast: {
      absolutePath: 'project:/contracts/ejemplo.sol',
      exportedSymbols: [Object],
      id: 23,
      nodeType: 'SourceUnit',
      nodes: [Array],
      src: '37:504:0'
    },
    legacyAST: {
      attributes: [Object],
      children: [Array],
      id: 23,
      name: 'SourceUnit',
      src: '37:504:0'
    },
    compiler: {
      name: 'solc',
      version: '0.5.16+commit.9c3226ce.Emscripten.clang'
    },
    networks: { '5777': [Object] },
    schemaVersion: '3.4.4',
    updatedAt: '2022-02-01T21:33:54.820Z',
    networkType: 'ethereum',
    devdoc: {
      details: 'Almacenar y recuperar una variable',
      methods: [Object],
      title: 'Ejemplo con Truffle'
    },
    userdoc: { methods: {} },
    db: undefined,
    autoGas: true,
    gasMultiplier: 1.25
  },
  configureNetwork: [Function: bound configureNetwork],
  setProvider: [Function: bound setProvider],
  new: [Function: bound new] {
    estimateGas: [Function: bound estimateDeployment],
    request: [Function: bound requestDeployment]
  },
  at: [Function: bound at] AsyncFunction,
  deployed: [Function: bound deployed] AsyncFunction,
  defaults: [Function: bound defaults],
  hasNetwork: [Function: bound hasNetwork],
  isDeployed: [Function: bound isDeployed],
  detectNetwork: [Function: bound detectNetwork] AsyncFunction,
  setNetwork: [Function: bound setNetwork],
  setNetworkType: [Function: bound setNetworkType],
  setWallet: [Function: bound setWallet],
  resetAddress: [Function: bound resetAddress],
  link: [Function: bound link],
  clone: [Function: bound clone],
  addProp: [Function: bound addProp],
  toJSON: [Function: bound toJSON],
  decodeLogs: [Function: bound decodeLogs],
  enums: {},
  class_defaults: { from: '0x5A31C7C78930a61aBe0d71C63438D9FEc64a2010' },
  interfaceAdapter: Web3InterfaceAdapter {
    web3: Web3Shim {
      currentProvider: [Getter/Setter],
      _requestManager: [RequestManager],
      givenProvider: null,
      providers: [Object],
      _provider: [HttpProvider],
      setProvider: [Function (anonymous)],
      setRequestManager: [Function (anonymous)],
      BatchRequest: [Function: bound Batch],
      extend: [Function],
      version: '1.5.3',
      utils: [Object],
      eth: [Eth],
      shh: [Shh],
      bzz: [Bzz],
      networkType: 'ethereum'
    }
  },
  web3: Web3Shim {
    currentProvider: [Getter/Setter],
    _requestManager: RequestManager {
      provider: [HttpProvider],
      providers: [Object],
      subscriptions: Map(0) {}
    },
    givenProvider: null,
    providers: {
      WebsocketProvider: [Function: WebsocketProvider],
      HttpProvider: [Function: HttpProvider],
      IpcProvider: [Function: IpcProvider]
    },
    _provider: HttpProvider {
      withCredentials: false,
      timeout: 0,
      headers: undefined,
      agent: undefined,
      connected: true,
      host: 'http://127.0.0.1:9545/',
      httpAgent: [Agent],
      send: [Function (anonymous)],
      _alreadyWrapped: true
    },
    setProvider: [Function (anonymous)],
    setRequestManager: [Function (anonymous)],
    BatchRequest: [Function: bound Batch],
    extend: [Function: ex] {
      formatters: [Object],
      utils: [Object],
      Method: [Function: Method]
    },
    version: '1.5.3',
    utils: {
      _fireError: [Function: _fireError],
      _jsonInterfaceMethodToString: [Function: _jsonInterfaceMethodToString],
      _flattenTypes: [Function: _flattenTypes],
      randomHex: [Function: randomHex],
      BN: [Function],
      isBN: [Function: isBN],
      isBigNumber: [Function: isBigNumber],
      isHex: [Function: isHex],
      isHexStrict: [Function: isHexStrict],
      sha3: [Function],
      sha3Raw: [Function: sha3Raw],
      keccak256: [Function],
      soliditySha3: [Function: soliditySha3],
      soliditySha3Raw: [Function: soliditySha3Raw],
      encodePacked: [Function: encodePacked],
      isAddress: [Function: isAddress],
      checkAddressChecksum: [Function: checkAddressChecksum],
      toChecksumAddress: [Function: toChecksumAddress],
      toHex: [Function: toHex],
      toBN: [Function: toBN],
      bytesToHex: [Function: bytesToHex],
      hexToBytes: [Function: hexToBytes],
      hexToNumberString: [Function: hexToNumberString],
      hexToNumber: [Function: hexToNumber],
      toDecimal: [Function: hexToNumber],
      numberToHex: [Function: numberToHex],
      fromDecimal: [Function: numberToHex],
      hexToUtf8: [Function: hexToUtf8],
      hexToString: [Function: hexToUtf8],
      toUtf8: [Function: hexToUtf8],
      stripHexPrefix: [Function: stripHexPrefix],
      utf8ToHex: [Function: utf8ToHex],
      stringToHex: [Function: utf8ToHex],
      fromUtf8: [Function: utf8ToHex],
      hexToAscii: [Function: hexToAscii],
      toAscii: [Function: hexToAscii],
      asciiToHex: [Function: asciiToHex],
      fromAscii: [Function: asciiToHex],
      unitMap: [Object],
      toWei: [Function: toWei],
      fromWei: [Function: fromWei],
      padLeft: [Function: leftPad],
      leftPad: [Function: leftPad],
      padRight: [Function: rightPad],
      rightPad: [Function: rightPad],
      toTwosComplement: [Function: toTwosComplement],
      isBloom: [Function: isBloom],
      isUserEthereumAddressInBloom: [Function: isUserEthereumAddressInBloom],
      isContractAddressInBloom: [Function: isContractAddressInBloom],
      isTopic: [Function: isTopic],
      isTopicInBloom: [Function: isTopicInBloom],
      isInBloom: [Function: isInBloom],
      compareBlockNumbers: [Function: compareBlockNumbers],
      toNumber: [Function: toNumber]
    },
    eth: Eth {
      currentProvider: [Getter/Setter],
      _requestManager: [RequestManager],
      givenProvider: null,
      providers: [Object],
      _provider: [HttpProvider],
      setProvider: [Function (anonymous)],
      setRequestManager: [Function (anonymous)],
      BatchRequest: [Function: bound Batch],
      extend: [Function],
      handleRevert: [Getter/Setter],
      defaultCommon: [Getter/Setter],
      defaultHardfork: [Getter/Setter],
      defaultChain: [Getter/Setter],
      transactionPollingTimeout: [Getter/Setter],
      transactionConfirmationBlocks: [Getter/Setter],
      transactionBlockTimeout: [Getter/Setter],
      defaultAccount: [Getter/Setter],
      defaultBlock: [Getter/Setter],
      maxListenersWarningThreshold: [Getter/Setter],
      clearSubscriptions: [Function: bound ],
      removeSubscriptionById: [Function: bound ],
      net: [Net],
      accounts: [Accounts],
      personal: [Personal],
      Contract: [Function],
      Iban: [class Iban],
      abi: ABICoder {},
      ens: [ENS],
      getNodeInfo: [Function],
      getProtocolVersion: [Function],
      getCoinbase: [Function],
      isMining: [Function],
      getHashrate: [Function],
      isSyncing: [Function],
      getGasPrice: [Function],
      getFeeHistory: [Function],
      getAccounts: [Function],
      getBlockNumber: [Function],
      getBalance: [Function],
      getStorageAt: [Function],
      getCode: [Function],
      getBlock: [Function],
      getUncle: [Function],
      getBlockTransactionCount: [Function],
      getBlockUncleCount: [Function],
      getTransaction: [Function],
      getTransactionFromBlock: [Function],
      getTransactionReceipt: [Function],
      getTransactionCount: [Function],
      sendSignedTransaction: [Function],
      signTransaction: [Function],
      sendTransaction: [Function],
      sign: [Function],
      call: [Function],
      estimateGas: [Function],
      submitWork: [Function],
      getWork: [Function],
      getPastLogs: [Function],
      getChainId: [Function],
      requestAccounts: [Function],
      getProof: [Function],
      getPendingTransactions: [Function],
      subscribe: [Function (anonymous)]
    },
    shh: Shh {
      currentProvider: [Getter/Setter],
      _requestManager: [RequestManager],
      givenProvider: null,
      providers: [Object],
      _provider: [HttpProvider],
      setProvider: [Function (anonymous)],
      setRequestManager: [Function (anonymous)],
      BatchRequest: [Function: bound Batch],
      extend: [Function],
      net: [Net],
      subscribe: [Function (anonymous)],
      getVersion: [Function],
      getInfo: [Function],
      setMaxMessageSize: [Function],
      setMinPoW: [Function],
      markTrustedPeer: [Function],
      newKeyPair: [Function],
      addPrivateKey: [Function],
      deleteKeyPair: [Function],
      hasKeyPair: [Function],
      getPublicKey: [Function],
      getPrivateKey: [Function],
      newSymKey: [Function],
      addSymKey: [Function],
      generateSymKeyFromPassword: [Function],
      hasSymKey: [Function],
      getSymKey: [Function],
      deleteSymKey: [Function],
      newMessageFilter: [Function],
      getFilterMessages: [Function],
      deleteMessageFilter: [Function],
      post: [Function],
      unsubscribe: [Function]
    },
    bzz: Bzz {
      givenProvider: null,
      currentProvider: null,
      isAvailable: [Function (anonymous)],
      upload: [Function (anonymous)],
      download: [Function (anonymous)]
    },
    networkType: 'ethereum'
  },
  currentProvider: HttpProvider {
    withCredentials: false,
    timeout: 0,
    headers: undefined,
    agent: undefined,
    connected: true,
    host: 'http://127.0.0.1:9545/',
    httpAgent: Agent {
      _events: [Object: null prototype],
      _eventsCount: 2,
      _maxListeners: undefined,
      defaultPort: 80,
      protocol: 'http:',
      options: [Object: null prototype],
      requests: [Object: null prototype] {},
      sockets: [Object: null prototype] {},
      freeSockets: [Object: null prototype] {},
      keepAliveMsecs: 1000,
      keepAlive: false,
      maxSockets: Infinity,
      maxFreeSockets: 256,
      scheduling: 'lifo',
      maxTotalSockets: Infinity,
      totalSocketCount: 0,
      [Symbol(kCapture)]: false
    },
    send: [Function (anonymous)],
    _alreadyWrapped: true
  },
  network_id: '5777',
  disableConfirmationListener: undefined,
  ens: { enabled: false, registryAddress: null }
}
truffle(develop)>

Ahora, desde el entorno de desarrollo, vamos a interactuar con el contrato ejecutando código Java Script: ejemplo.deployed().then(function (instance) { return instance.store(4); });

truffle(develop)> ejemplo.deployed().then(function (instance) { return instance.store(4); });
{
  tx: '0x38bff639b3d28e023560bbfe9e6979203e43fa73ecb430b776cc31cf0f90a342',
  receipt: {
    transactionHash: '0x38bff639b3d28e023560bbfe9e6979203e43fa73ecb430b776cc31cf0f90a342',
    transactionIndex: 0,
    blockHash: '0xe4832410c193f71c2dd2eb3feb5db571f3e93fa913854ecd7592f3c8a62f9ae9',
    blockNumber: 1,
    from: '0x5a31c7c78930a61abe0d71c63438d9fec64a2010',
    to: '0xb41e6d09639ebb4bd6b251747b041a14b4fea7bb',
    gasUsed: 21204,
    cumulativeGasUsed: 21204,
    contractAddress: null,
    logs: [],
    status: true,
    logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
    rawLogs: []
  },
  logs: []
}
truffle(develop)>

Conclusión

Con ello ya hemos completado el círculo. Editar en MS Visual Studio Code, compilar con Truffle, desplegar en Ganache e interactuar con el contrato desde la consola de desarrollo de Truffle.

La gracia es que entendidos estos conceptos, ya solamente tenemos que elegir los productos que más nos interesen para hacer el desarrollo de aplicaciones distribuidas (DApp) y contratos inteligentes (smart contracts).

No acabo de estar muy contento porque hay desajustes de versiones entre la versión más moderna de Solidity y la que podemos usar en Truffle, por lo que podemos usar Remix para aprender las novedades del lenguaje, hacer algunos tutoriales y ver otras pilas de productos.

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad