Home Reference Source

src/deployer.js

import Web3 from 'web3'

import Addresses from './addresses.js'
import conversionRatesByteCode from './contracts/ConversionRatesContract.js'
import conversionRatesABI from '../abi/ConversionRatesContract.abi.json'
import kyberReserveContractABI from '../abi/KyberReserveContract.abi.json'
import kyberReserveContractByteCode from './contracts/KyberReserveContract.js'
import sanityRatesContractABI from '../abi/SanityRatesContract.abi.json'
import sanityRatesContractByteCode from './contracts/SanityRatesContract.js'

/**
 * KyberNetworkAddress is the smart contract address of KyberNetwork.</br>
 * *Last updated*: 2018, August 31
 * @type {string}
 */
export const KyberNetworkAddress = '0x91a502C678605fbCe581eae053319747482276b9'

/**
 * Deployer is used for deploying new KyberNetwork reserve contracts.
 * It deployed all requires smart contracts for running a reserve:
 * <ul>
 *   <li>reserve</li>
 *   <li>conversionRates</li>
 *   <li>sanityRates (optional)</li>
 * </ul>
 */
export default class Deployer {
  /**
   * Create a deployer instance with given account parameter.
   * @param {object} web3 - Web3 instance
   */
  constructor (web3) {
    if (web3.currentProvider == null) {
      throw new Error('web3 is not provided')
    }
    this.web3 = web3
  }

  /**
   * Deploy new reserve and pricing contracts.
   * @param {object} adminAddress - Web3 account to create the smart contracts. This account is also set to be admin of the contracts
   * @param {string} [network] - Address of KyberNetwork smart contract.
   * @param {boolean} [sanityRates=false] - If true, sanityRates contract will be deployed.
   * @param {number} gasPrice (optional) - the gasPrice desired for the tx
   * @return {Addresses} - Deployed reserve addresses set.
   */
  async deploy (
    adminAddress,
    network,
    sanityRates = false,
    gasPrice
  ) {
    if (!adminAddress) {
      throw new Error('missing admin address')
    }
    if (!network) {
      throw new Error('missing network address')
    }

    const deployContract = async (adminAddress, jsonInterface, byteCode, args) => {
      const dpl = new this.web3.eth.Contract(jsonInterface).deploy({
        data: `0x${byteCode}`,
        arguments: args
      })
      return dpl.send({
        from: adminAddress,
        gas: await dpl.estimateGas({
          from: adminAddress
        }),
        gasPrice: gasPrice
      })
    }

    const deployConversionRates = adminAddress => {
      console.log(
        'Deploying conversion ... This might take a while for the tx to be mined'
      )
      return deployContract(
        adminAddress,
        conversionRatesABI,
        conversionRatesByteCode,
        [adminAddress]
      )
    }

    const deployReserve = (adminAddress, network, conversionAddress) => {
      console.log(
        'Deploying reserve ... This might take a while for the tx to be mined'
      )
      const args = [network, conversionAddress, adminAddress]
      return deployContract(
        adminAddress,
        kyberReserveContractABI,
        kyberReserveContractByteCode,
        args
      )
    }

    const deploySanityRates = adminAddress => {
      console.log(
        'Deploying sanity ...This might take a while for the tx to be mined'
      )

      return deployContract(
        adminAddress,
        sanityRatesContractABI,
        sanityRatesContractByteCode,
        [adminAddress]
      )
    }

    // All the contract must be deployed sequentially
    const conversionRatesContract = await deployConversionRates(adminAddress)
    const reserveContract = await deployReserve(
      adminAddress,
      network,
      conversionRatesContract.options.address
    )
    let sanityRatesContract
    if (sanityRates) {
      sanityRatesContract = await deploySanityRates(adminAddress)
    }

    const setReserveAddressForConversionRates = async (
      conversionRatesContract,
      reserveAddress
    ) => {
      console.log('linking reserveContractAddress to conversionRateContract...')
      const setReserveAddressTx = await conversionRatesContract.methods.setReserveAddress(
        reserveAddress
      )
      return setReserveAddressTx.send({
        from: adminAddress,
        gas: await setReserveAddressTx.estimateGas({
          from: adminAddress
        }),
        gasPrice: gasPrice
      })
    }

    const setReserveAddressTxResult = await setReserveAddressForConversionRates(
      conversionRatesContract,
      reserveContract.options.address
    )
    console.log(
      'setReserveAddressTx: ',
      setReserveAddressTxResult.transactionHash
    )

    const setContractAddressesForReserve = async (
      reserveContract,
      networkAddress,
      rateAddress,
      sanityAddress
    ) => {
      console.log(
        'linking network, rate and sanity contract address to reserveContract...'
      )
      const setContractsTx = await reserveContract.methods.setContracts(
        networkAddress,
        rateAddress,
        sanityAddress
      )
      return setContractsTx.send({
        from: adminAddress,
        gas: await setContractsTx.estimateGas({
          from: adminAddress
        }),
        gasPrice: gasPrice
      })
    }
    const setContractAddressesTxResult = await setContractAddressesForReserve(
      reserveContract,
      network,
      conversionRatesContract.options.address,
      !sanityRatesContract
        ? '0x0000000000000000000000000000000000000000'
        : sanityRatesContract.options.address
    )
    console.log(
      'setContractAddressesTx: ',
      setContractAddressesTxResult.transactionHash
    )

    return new Addresses(
      reserveContract.options.address,
      conversionRatesContract.options.address,
      !sanityRatesContract ? undefined : sanityRatesContract.options.address
    )
  }
  async deploySanityRates (
    adminAddress,
    gasPrice
  ) {
    if (!adminAddress) {
      throw new Error('missing admin address')
    }

    const deployContract = async (adminAddress, jsonInterface, byteCode, args) => {
      const dpl = new this.web3.eth.Contract(jsonInterface).deploy({
        data: `0x${byteCode}`,
        arguments: args
      })
      return dpl.send({
        from: adminAddress,
        gas: await dpl.estimateGas({
          from: adminAddress
        }),
        gasPrice: gasPrice
      })
    }

    const deploySanityRates = adminAddress => {
      console.log(
        'Deploying sanity ...This might take a while for the tx to be mined'
      )

      return deployContract(
        adminAddress,
        sanityRatesContractABI,
        sanityRatesContractByteCode,
        [adminAddress]
      )
    }

    let sanityRatesContract = await deploySanityRates(adminAddress)
    return sanityRatesContract.options.address
    
  }
}