trufflesuite/truffle

View on GitHub
packages/environment/environment.js

Summary

Maintainability
A
3 hrs
Test Coverage
const Web3 = require("web3");
const { createInterfaceAdapter } = require("@truffle/interface-adapter");
const expect = require("@truffle/expect");
const TruffleError = require("@truffle/error");
const { Resolver } = require("@truffle/resolver");
const Artifactor = require("@truffle/artifactor");
const Ganache = require("ganache");
const Provider = require("@truffle/provider");

const Environment = {
  // It's important config is a Config object and not a vanilla object
  detect: async function (config) {
    expect.options(config, ["networks"]);

    helpers.setUpConfig(config);
    helpers.validateNetworkConfig(config);

    const interfaceAdapter = createInterfaceAdapter({
      provider: config.provider,
      networkType: config.network_config.type
    });

    await Provider.testConnection(config);
    await helpers.detectAndSetNetworkId(config, interfaceAdapter);
    await helpers.setFromOnConfig(config, interfaceAdapter);
  },

  // Ensure you call Environment.detect() first.
  fork: async function (config, ganacheOptions) {
    expect.options(config, ["from", "provider", "networks", "network"]);

    const interfaceAdapter = createInterfaceAdapter({
      provider: config.provider,
      networkType: config.network_config.type
    });

    let accounts;
    try {
      accounts = await interfaceAdapter.getAccounts();
    } catch {
      // don't prevent Truffle from working if user doesn't provide some way
      // to sign transactions (e.g. no reason to disallow debugging)
      accounts = [];
    }
    const block = await interfaceAdapter.getBlock("latest");

    const upstreamNetwork = config.network;
    const upstreamConfig = config.networks[upstreamNetwork];
    const forkedNetwork = config.network + "-fork";
    ganacheOptions = {
      ...ganacheOptions,
      fork: config.provider,
      miner: {
        ...ganacheOptions.miner,
        blockGasLimit: block.gasLimit
      }
    };
    if (accounts.length > 0) ganacheOptions.unlocked_accounts = accounts;

    config.networks[forkedNetwork] = {
      network_id: config.network_id,
      provider: Ganache.provider(ganacheOptions),
      from: config.from,
      gas: upstreamConfig.gas,
      gasPrice: upstreamConfig.gasPrice
    };
    config.network = forkedNetwork;
  },

  develop: async (config, ganacheOptions) => {
    expect.options(config, ["networks"]);

    const network = config.network || "develop";
    const url = `http://${ganacheOptions.host}:${ganacheOptions.port}/`;

    config.networks[network] = {
      ...config.networks[network],
      network_id: ganacheOptions.network_id,
      provider: function () {
        return new Web3.providers.HttpProvider(url, { keepAlive: false });
      }
    };

    config.network = network;

    return await Environment.detect(config);
  }
};

const helpers = {
  setFromOnConfig: async (config, interfaceAdapter) => {
    if (config.from) return;

    try {
      const accounts = await interfaceAdapter.getAccounts();
      config.networks[config.network].from = accounts[0];
    } catch {
      // don't prevent Truffle from working if user doesn't provide some way
      // to sign transactions (e.g. no reason to disallow debugging)
    }
  },

  detectAndSetNetworkId: async (config, interfaceAdapter) => {
    const configNetworkId = config.networks[config.network].network_id;
    const providerNetworkId = await interfaceAdapter.getNetworkId();
    if (configNetworkId !== "*") {
      // Ensure the network id matches the one in the config for safety
      if (providerNetworkId.toString() !== configNetworkId.toString()) {
        const error =
          `The network id specified in the truffle config ` +
          `(${configNetworkId}) does not match the one returned by the network ` +
          `(${providerNetworkId}).  Ensure that both the network and the ` +
          `provider are properly configured.`;
        throw new Error(error);
      }
    } else {
      // We have a "*" network. Get the current network and replace it with the real one.
      // TODO: Should we replace this with the blockchain uri?
      config.networks[config.network].network_id = providerNetworkId;
    }
  },

  validateNetworkConfig: config => {
    const networkConfig = config.network_config;

    if (!networkConfig) {
      throw new TruffleError(
        `Unknown network "${config.network}` +
          `". See your Truffle configuration file for available networks.`
      );
    }

    const configNetworkId = config.network_config.network_id;

    if (configNetworkId == null) {
      throw new Error(
        `You must specify a network_id in your '` +
          `${config.network}' configuration in order to use this network.`
      );
    }
  },

  setUpConfig: config => {
    if (!config.resolver) {
      config.resolver = new Resolver(config);
    }

    if (!config.artifactor) {
      config.artifactor = new Artifactor(config.contracts_build_directory);
    }

    if (!config.network) {
      if (config.networks["development"]) {
        config.network = "development";
      } else {
        config.network = "ganache";
        config.networks[config.network] = {
          host: "127.0.0.1",
          port: 7545,
          network_id: 5777
        };
      }
    }

    const currentNetworkSettings = config.networks[config.network];
    if (
      currentNetworkSettings &&
      currentNetworkSettings.ens &&
      currentNetworkSettings.ens.registry
    ) {
      config.ens.registryAddress = currentNetworkSettings.ens.registry.address;
    }
  }
};

module.exports = Environment;