UNPKG

@parity/light.js

Version:

A high-level reactive JS library optimized for light clients

134 lines (120 loc) 4.29 kB
// Copyright 2015-2019 Parity Technologies (UK) Ltd. // This file is part of Parity. // // SPDX-License-Identifier: MIT import Abi from '@parity/abi'; import { abiEncode } from '@parity/api/lib/util/encode'; import * as memoizee from 'memoizee'; import { Address } from '../../types'; import { createApiFromProvider, getApi } from '../../api'; import createRpc from '../utils/createRpc'; import { switchMapPromise } from '../../utils/operators'; import frequency from '../../frequency'; import { post$ } from './post'; interface MakeContract { abi: any; // use types from @parity/abi address: string; readonly contractObject: any; // TODO from @parity/api [index: string]: any | string | ((...args: any[]) => any); // use types from @parity/abi } /** * Cache contracts, so that they are: * - only created after the first call/transaction to a contract has been made * - further calls/transactions to the same contract doesn't recreate the * contract * * @ignore * @param address - The contract address. * @param abiJson - The contract abi. * @param api - The api Object. * @return - The contract object as defined in @parity/api. */ const getContract = memoizee( (address: Address, abiJson: any[], api: any) => api.newContract(abiJson, address) // use types from @parity/abi ); /** * Create a contract, given an api object. * Pure function version of {@link makeContract}. * * @ignore * @param address - The contract address. * @param abiJson - The contract abi. * @param api - The api Object. * @return - An object whose keys are all the functions of the * contract, and each function returns an Observable which will fire when the * function resolves. */ const makeContractWithApi = memoizee( (address: Address, abiJson: any[], api: any) => { const abi = new Abi(abiJson); // use types from @parity/abi // Variable result will hold the final object to return const result: MakeContract = { abi, address, get contractObject () { return getContract(address, abiJson, api); } }; // We then copy every key inside contract.instance into our `result` object, // replacing each the value by an Observable instead of a Promise. abi.functions.forEach(({ name }: any) => { // use types from @parity/abi result[`${name}$`] = (...args: any[]) => { // We only get the contract when the function is called for the 1st // time. Note: getContract is memoized, won't create contract on each // call. const contract = getContract(address, abiJson, api); const method = contract.instance[name]; // Hold the method from the Abi // The last arguments in args can be an options object const options = args.length === method.inputs.length + 1 ? args.pop() : {}; if (method.constant) { return createRpc({ frequency: [frequency.onEveryBlock$], name: `${address}:${name}`, pipes: () => [ switchMapPromise(() => contract.instance[name].call(options, args) ) ] })({ provider: api.provider })(...args); } else { const { estimate, passphrase, ...txFields } = options; return post$( { to: address, data: abiEncode( method.name, method.inputs.map(({ kind: { type } }: any) => type), // TODO Use @parity/api types args ), ...txFields }, { estimate, passphrase } ); } }; }); return result; } ); /** * Create a contract. * * @param address - The contract address. * @param abiJson - The contract abi. * @param options - The options to pass in when creating the contract. * @return - An object whose keys are all the functions of the * contract, and each function return an Observable which will fire when the * function resolves. */ export const makeContract = ( address: Address, abiJson: any[], options: { provider?: any } = {} ) => { const { provider } = options; const api = provider ? createApiFromProvider(provider) : getApi(); return makeContractWithApi(address, abiJson, api); };