Home Reference Source

src/light.js

const ENS = require('eth-ens-namehash');
const Constants = require('./const');
const axios = require('axios');
const bs58 = require('bs58');
const sha256 = require('sha256');

// Lovely ABIs
const ResolverAbi = require('./smart_contracts/SV_ENS_Resolver.abi.json');
const IndexAbi = require('./smart_contracts/SVLightIndex.abi.json');
const BackendAbi = require('./smart_contracts/SVLightIndexBackend.abi.json');
const BBFarmAbi = require('./smart_contracts/BBFarm.abi.json');
const PaymentsAbi = require('./smart_contracts/SVPayments.abi.json');
const AuxAbi = require('./smart_contracts/AuxAbi.abi.json');
const AuctionAbi = require('./smart_contracts/CommAuctionIface.abi.json');
const ERC20Abi = require('./smart_contracts/ERC20.abi.json');

export const initializeSvLight = async svConfig => {
  const { indexContractName, ensResolver, httpProvider, auxContract } = svConfig;

  const Web3 = require('web3');
  const web3 = new Web3(new Web3.providers.HttpProvider(httpProvider));

  const resolver = new web3.eth.Contract(ResolverAbi, ensResolver);

  // const indexAddress =
  // console.log('indexAddress :', indexAddress);
  const index = new web3.eth.Contract(IndexAbi, await resolveEnsAddress({ resolver }, indexContractName));
  const backendAddress = await getBackendAddress({ index });
  const backend = new web3.eth.Contract(BackendAbi, backendAddress);
  const aux = new web3.eth.Contract(AuxAbi, auxContract);
  const payments = new web3.eth.Contract(PaymentsAbi, await index.methods.getPayments().call())

  return {
    svConfig,
    web3,
    resolver,
    index,
    backend,
    aux,
    payments
  };
};

export const resolveEnsAddress = async ({ resolver }, ensName) => {
  return await resolver.methods.addr(ENS.hash(ensName)).call();
};

export const getBackendAddress = async ({ index }) => {
  return await index.methods.getBackend().call();
};

export const getDemocInfo = async ({ backend, democHash }) => {
  return await backend.methods.getDInfo(democHash).call();
};

export const getDemocNthBallot = async ({ svNetwork }, democBallotInfo) => {
  // Destructure and set the variables that are needed
  const { index, backend, aux, svConfig } = svNetwork;
  const { democHash, nthBallot } = democBallotInfo;
  const indexAddress = index._address;
  const backendAddress = backend._address;
  const archiveUrl = { svConfig };

  const bbFarmAndBallotId = await aux.methods.getBBFarmAddressAndBallotId(backendAddress, indexAddress, democHash, nthBallot).call();
  // console.log('bbFarmAndBallotId :', bbFarmAndBallotId);

  const { id, bbFarmAddress } = bbFarmAndBallotId;
  const userEthAddress = '0x0000000000000000000000000000000000000000';
  const ethBallotDetails = await aux.methods.getBallotDetails(id, bbFarmAddress, userEthAddress).call();

  const ballotSpec = await getBallotSpec(archiveUrl, ethBallotDetails.specHash);
  // console.log('ballotSpec :', ballotSpec);
  // .then(x => console.log('Then called', x))
  // .catch(x => console.log('Caught error', x));

  const ballotObject = { ...bbFarmAndBallotId, ...ethBallotDetails, data: { ...ballotSpec.data } };

  return ballotObject;
};

export const getBallotSpec = async (archiveUrl, ballotSpecHash) => {
  return new Promise((res, rej) => {
    let done = false;
    const doRes = obj => {
      if (!done) {
        done = true;
        res(obj);
      }
    };
    getBallotObjectFromIpfs(ballotSpecHash).then(doRes);
    setTimeout(() => {
      if (!done) {
        getBallotObjectFromS3(archiveUrl, ballotSpecHash)
          .then(doRes)
          .catch(error => {
            rej(new Error(error));
          });
      }
    }, 3500);
  });
};

export const getBallotObjectFromS3 = async (archiveUrl, ballotSpecHash) => {
  return axios.get(archiveUrl + ballotSpecHash + '.json');
};

export const getBallotObjectFromIpfs = async ballotSpecHash => {
  const ipfsUrl = 'https://ipfs.infura.io/api/v0/block/get?arg=';
  const cidHex = '1220' + ballotSpecHash.substr(2);
  const bytes = Buffer.from(cidHex, 'hex');
  const cid = bs58.encode(bytes);
  return await axios.get(ipfsUrl + cid);
};

// Take the svNetwork object and a democHash, will return all of the ballots from the democracy in an array
export const getDemocBallots = async ({ svNetwork, democHash }) => {
  const { backend } = svNetwork;
  const democInfo = await getDemocInfo({ backend, democHash });

  // Throw an error if the democ info is not correct
  const {erc20, owner} = democInfo
  if (owner === '0x0000000000000000000000000000000000000000') {
    throw new Error('Democracy Hash does not resolve to a democracy')
  }

  // TODO - Work out where / how to push an errored ballot
  // Loop through and get all the ballots
  const numBallots = democInfo.nBallots;
  const ballotsArray = [];
  for (let i = 0; i < numBallots; i++) {
    ballotsArray[i] = await getDemocNthBallot({ svNetwork }, { democHash: democHash, nthBallot: i });
  }

  return ballotsArray;
};

/** Takes in the svNetwork object and returns all relevant addresses */
export const getContractAddresses = async ({svNetwork}) => {
  const {index, resolver, backend, aux, svConfig} = svNetwork
  const { delegationContractName, lookupAddress } = svConfig

  return {
    indexAddress: index._address,
    backendAddress: backend._address,
    auxAddress: aux._address,
    lookupAddress: lookupAddress,
    resolverAddress: resolver._address,
    communityAuctionAddress: await index.methods.getCommAuction().call(),
    delegationAddress: await resolveEnsAddress({ resolver }, delegationContractName),
    paymentsAddress: await index.methods.getPayments().call(),
  }
}

export const weiToCents = async ({payments}, wei) => {
  return await payments.methods.weiToCents(wei).call()
}

export const getCommunityBallotPrice = async ({payments}, democHash) => {
  return await payments.methods.getNextPrice(democHash).call()
}

export const checkIfAddressIsEditor = async ({ svNetwork }, { userAddress, democHash }) => {
  const {backend} = svNetwork
  return await backend.methods.isDEditor(democHash, userAddress).call();
};

// Checks the current ethereum gas price and returns a couple of values
export const getCurrentGasPrice = async () => {
  const gasStationInfo = await axios.get('https://ethgasstation.info/json/ethgasAPI.json')
  const {data} = gasStationInfo

  return {
    safeLow: data.safeLow / 10,
    average: data.average / 10,
    fast: data.fast / 10,
    fastest: data.fastest / 10,
  }
}
// Checks the ballot hash against the ballot content
export const checkBallotHashBSpec = (ballotSpec, assertSpecHash) => {
    let contentHash = '0x' + sha256(JSON.stringify(ballotSpec, null, 2));
    if (assertSpecHash === contentHash) {
      return true;
    } else {
      return false;
    }
}

// Checks the ballot hash against a ballot global ballot object
// Does this by destructuring the specHash and data out of it
export const checkBallotHashGBallot = (ballotObject) => {
  const {data, specHash} = ballotObject
  return checkBallotHashBSpec(data, specHash)
}