packages/oo7-parity/src/index.js
// (C) Copyright 2016-2017 Parity Technologies (UK) Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/* eslint-disable no-return-assign */
/* eslint-disable no-proto */
// TODO [Document auxilary types]
const oo7 = require('oo7');
const ParityApi = require('@parity/api');
const {
asciiToHex,
bytesToHex,
hexToAscii,
isAddressValid,
toChecksumAddress,
sha3,
capitalizeFirstLetter,
singleton,
denominations,
denominationMultiplier,
interpretRender,
combineValue,
defDenom,
formatValue,
formatValueNoDenom,
formatToExponential,
interpretQuantity,
splitValue,
formatBalance,
formatBlockNumber,
isNullData,
splitSignature,
removeSigningPrefix,
cleanup
} = require('./utils');
const {
abiPolyfill,
RegistryABI,
RegistryExtras,
GitHubHintABI,
OperationsABI,
BadgeRegABI,
TokenRegABI,
BadgeABI,
TokenABI
} = require('./abis');
function defaultProvider () {
if (typeof window !== 'undefined' && window.ethereum) {
return window.ethereum;
}
try {
if (typeof window !== 'undefined' && window.parent && window.parent.ethereum) {
return window.parent.ethereum;
}
} catch (e) {}
return new ParityApi.Provider.Http('http://localhost:8545');
}
class Bonds {
/**
* Creates a new oo7-parity bonds aggregate object with given ethereum provider.
*
* Additional documentation can be found at https://wiki.parity.io/oo7-Parity-Reference.html
*
* @param {?Provider} provider Web3-compatible transport Provider (i.e. `window.ethereum`). Uses a sane default if not provided.
* @returns {Bonds}
*/
constructor (provider = defaultProvider()) {
if (!this) {
return createBonds({ api: new ParityApi(provider) });
}
/**
*
* A {@link Bond} representing latest time. Updated every second.
*
* @type {TimeBond}
*
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .time
* .tie(console.log) // prints time periodically
*/
this.time = null;
/**
* A {@link Bond} representing latest block number.
* Alias for {@link Bonds.blockNumber}
*
* @type {Bond.<Number>}
*/
this.height = null;
/**
* A {@link Bond} representing latest block number.
*
* @type {Bond.<Number>}
*
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .blockNumber
* .tie(console.log) // prints latest block number when it changes
*/
this.blockNumber = null;
/**
* A function returning bond that represents given block content.
*
* @param {string|number|Bond} number block number
* @returns {Bond.<Block>} block bond
*
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .blockByNumber(bonds.height)
* .tie(console.log) // prints latest block
*/
this.blockByNumber = null;
/**
* A function returning bond that represents given block content.
*
* @param {string|number|Bond} hash block hash
* @returns {Bond.<Block>} block bond
*
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .blockByHash('0x2b23d04567313fa141ca396f1e2620b62ab0c5d69f8c77157118f8d7671e1f4d')
* .tie(console.log) // prints block with given hash
*/
this.blockByHash = null;
/**
* Similar to {@link Bonds.blockByNumber} and {@link Bonds.blockByHash},
* but accepts both hashes and numbers as arguments.
*
* @param {string|number|Bond} hashOrNumber block hash or block number
* @returns {Bond.<Block>} block bond
*
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .findBlock('0x2b23d04567313fa141ca396f1e2620b62ab0c5d69f8c77157118f8d7671e1f4d')
* .tie(console.log) // prints block with given hash
*/
this.findBlock = null;
/**
* A subscriptable version of {@link Bonds.findBlock}
*
* You can retrieve bonds given block numbers or hashes or other Bonds.
*
* @type {Object.<string|number|Bond, Bond>}
*
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .blocks['0x2b23d04567313fa141ca396f1e2620b62ab0c5d69f8c77157118f8d7671e1f4d']
* .tie(console.log) // prints block with given hash
*
* bonds
* .blocks[bonds.height]
* .tie(console.log) // prints latest block every time it changes
*/
this.blocks = null;
/**
* A {@link Bond} for latest block.
*
* @type {Bond.<Block>}
*
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .head
* .tie(console.log) // prints latest block every time it changes
*
*/
this.head = null;
/**
* A {@link Bond} for currently set block author.
* Represents a result of `eth_coinbase` RPC call.
*
* @type {Bond.<Address>}
*
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .author
* .tie(console.log) // prints currently set block author (coinbase/miner) every time it changes
*
*/
this.author = null;
/**
* List of accounts managed by the node.
*
* @type {Bond.<Address[]>}
*
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .accounts
* .tie(console.log) // prints accounts list every time it changes
*
*/
this.accounts = null;
/**
* User-selected default account for this dapp.
*
* @type {Bond.<Address>}
*
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .defaultAccount
* .tie(console.log) // prints default account every time it changes
*
*/
this.defaultAccount = null;
/**
* Alias for {@link Bonds.defaultAccount}
*
* @type {Bond.<Address>}
*
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .me
* .tie(console.log) // prints default account every time it changes
*
*/
this.me = null;
/**
* Posts a transaction to the network.
*
* @param {TransactionRequest} tx Transaction details
* @returns {ReactivePromise.<TransactionStatus>}
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .post({ to: bonds.me, value: 0 })
* .tie(console.log) // Reports transaction progress
*/
this.post = null;
/**
* Returns a signature of given message
*
* @param {Hash|Bond} hash Hash to sign
* @param {?Address|Bond} from Optional account that should be used for signing.
* @returns {ReactivePromise.<SignStatus>}
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .sign('0x2ea2e504d09c458dbadc703112125564d53ca03c27a5b28e7b3e2b5804289c45')
* .tie(console.log) // Reports signing progress
*/
this.sign = null;
/**
* Returns balance of given address.
*
* @param {string|Bond.<Address>} address
* @returns {Bond.<BigNumber>}
*
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .balance(bonds.me)
* .tie(console.log) // prints default account balance every time any of them changes
*
*/
this.balance = null;
/**
* Returns code of given address.
*
* @param {string|Bond.<Address>} address
* @returns {Bond.<Bytes>}
*
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .code(bonds.me)
* .tie(console.log) // prints default account code every time any of them changes
*
*/
this.code = null;
/**
* Returns the nonce of given address.
*
* @param {string|Bond.<Address>} address
* @returns {Bond.<BigNumber>}
*
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .nonce(bonds.me)
* .tie(console.log) // prints default account nonce every time any of them changes
*
*/
this.nonce = null;
/**
* Returns storage at given index of an address.
*
* @param {string|Bond.<Address>} address Contract address
* @param {string|number|Bond.<H256>} storageIdx Contract storage index
* @returns {Bond.<BigNumber>}
*
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .storageAt(bonds.me, 0)
* .tie(console.log) // prints default account storage at position 0 every time any of them changes
*
*/
this.storageAt = null;
/**
* Returns node's syncing status.
* If the node is fully synced this will return `false`.
*
* @type {Bond.<bool>}
*
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .syncing
* .tie(console.log) // prints sync status every time it changes
*
*/
this.syncing = null;
/**
* Returns node's authoring status.
* If the node is not authoring blocks this will return `false`.
*
* @type {Bond.<bool>}
*
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .authoring
* .tie(console.log) // prints authoring status every time it changes
*
*/
this.authoring = null;
/**
* Reported hashrate.
* If there is an external miner connected to the node it will return reported values.
*
* @type {Bond.<BigNumber>}
*
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .hashrate
* .tie(console.log) // prints current average hashrate
*
*/
this.hashrate = null;
this.ethProtocolVersion = null;
/**
* Suggested gas price value. (Gas Price Oracle)
* This returns a suggested gas price for next transaction. The estimation is based on statistics from last blocks.
*
* @type {Bond.<BigNumber>}
*
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .gasPrice
* .tie(console.log) // prints current gas price suggestion
*
*/
this.gasPrice = null;
/**
* Estimates gas required to execute given transaction
*
* @param {{ from: ?Address, to: ?Address, data: ?Bytes }} call Transaction request
* @returns {Bond.<BigNumber>} gas estimate
*
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .estimateGas({ from: bonds.me, to: '0x00D6Cc1BA9cf89BD2e58009741f4F7325BAdc0ED' })
* .tie(console.log) // prints current gas estimate
*
*/
this.estimateGas = null;
/**
* Returns block transaction count given block number or hash.
*
* @param {string|number|Bond} block block number or hash
* @returns {Bond.<Number>} number of transactions in block
*
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .blockTransactionCount(bonds.blockNumber)
* .tie(console.log) // prints number of transactions in latest block
*
*/
this.blockTransactionCount = null;
/**
* Returns uncle count given block number or hash.
*
* @param {string|number|Bond} block block number or hash
* @returns {Bond.<Number>} number of uncles in a block
*
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .uncleCount(bonds.blockNumber)
* .tie(console.log) // prints number of uncles in latest block
*
*/
this.uncleCount = null;
/**
* Returns uncle given block number or hash and uncle index
*
* @param {string|number|Bond} block block number or hash
* @param {string|number|Bond} index index of an uncle within a block
* @returns {Bond.<Header>} uncle header at that index
*
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .uncle(bonds.blockNumber, 0)
* .tie(console.log) // prints the first uncle in latest block
*
*/
this.uncle = null;
/**
* Returns transaction given block number or hash and transaction index
*
* @param {string|number|Bond} block block number or hash
* @param {string|number|Bond} index index of a transaction within a block
* @returns {Bond.<Transaction>} transaction at that index
*
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .transaction(bonds.blockNumber, 0)
* .tie(console.log) // prints the first uncle in latest block
*
*/
this.transaction = null;
/**
* Returns receipt given transaction hash.
*
* @param {string|number|Bond} hash transaction hash
* @returns {Bond.<TransactionReceipt>} transaction at that index
*
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .receipt(bonds.transaction(bonds.height, 0).map(x => x ? x.hash : undefined))
* .tie(console.log) // prints receipt of first transaction in latest block
*
*/
this.receipt = null;
/**
* Returns client version string. (`web3_clientVersion`).
*
* @type {Bond.<String>}
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .clientVersion
* .tie(console.log)
*
*/
this.clientVersion = null;
/**
* Returns current peer count. (`net_peerCount`).
*
* @type {Bond.<Number>}
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .peerCount
* .tie(console.log)
*
*/
this.peerCount = null;
/**
* Returns true if the node is actively listening for network connections.
*
* @type {Bond.<bool>}
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .listening
* .tie(console.log)
*
*/
this.listening = null;
/**
* Returns chain id (used for chain replay protection).
* NOTE: It's _not_ network id.
*
* @type {Bond.<Number>}
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .chainId
* .tie(console.log)
*
*/
this.chainId = null;
/**
* Returns a hash of content under given URL.
*
* @param {string|Bond} url URL of the content
* @returns {Bond.<string>} hash of the content
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .hashContent('https://google.com')
* .tie(console.log)
*
*/
this.hashContent = null;
this.gasPriceHistogram = null;
this.accountsInfo = null;
this.allAccountsInfo = null;
this.hardwareAccountsInfo = null;
this.mode = null;
this.defaultExtraData = null;
this.extraData = null;
this.gasCeilTarget = null;
this.gasFloorTarget = null;
this.minGasPrice = null;
this.transactionsLimit = null;
/**
* Returns a string name of currently connected chain.
*
* @type {Bond.<string>}
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .chainName
* .tie(console.log)
*/
this.chainName = null;
/**
* Returns a status of currently connected chain.
*
* @type {Bond.<object>}
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .chainStatus
* .tie(console.log)
*/
this.chainStatus = null;
this.peers = null;
this.enode = null;
this.nodePort = null;
this.nodeName = null;
this.signerPort = null;
this.dappsPort = null;
this.dappsInterface = null;
this.nextNonce = null;
this.pending = null;
this.local = null;
this.future = null;
this.pendingStats = null;
this.unsignedCount = null;
this.releaseInfo = null;
this.versionInfo = null;
this.consensusCapability = null;
this.upgradeReady = null;
/**
* Replays (re-executes) a transaction. Returns requested traces of execution.
*
* @param {string} hash Transaction hash
* @param {String[]} traces Any subset of `trace`,`vmTrace`,`stateDiff`.
* @returns {Bond.<object>}
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .replayTx('0x2ea2e504d09c458dbadc703112125564d53ca03c27a5b28e7b3e2b5804289c45', ['trace'])
* .tie(console.log)
*/
this.replayTx = null;
/**
* Executs a transaction and collects traces.
*
* @param {TransactionRequest} transaction Transaction request
* @param {String[]} traces Any subset of `trace`,`vmTrace`,`stateDiff`.
* @param {string|number|Bond} block Block number or hash
* @returns {Bond.<object>}
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .callTx({
* from: bonds.me,
* to: bonds.registry.address
* }, ['trace'], 'latest')
* .tie(console.log)
*/
this.callTx = null;
/**
* Deploys a new contract
*
* @param {string|Bytes} init Initialization bytecode
* @param {ABI} abi Contract ABI
* @param {{from: ?Address, gas: ?BigNumber, gasPrice: ?BigNumber, nonce: ?BigNumber}} options Deployment options
* @returns {ReactivePromise.<DeployStatus>}
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .deployContract('0x1234', abi, {})
* .tie(console.log) // Reports deployment progress
*/
this.deployContract = null;
/**
* Creates bond-enabled contract object for existing contract.
*
* @param {string|Bond} address Contract address
* @param {ABI} abi Contract ABI
* @param {?ABI} extras Additional methods not defined in the ABI.
* @returns {Contract}
* @example
* const { bonds } = require('oo7-parity')
*
* bonds
* .makeContract(bonds.me, abi)
* .someMethod()
* .tie(console.log) // returns a result of someMethod call
*/
this.makeContract = null;
/**
* Parity registry contract instance.
* @type {Contract.<Registry>}
*/
this.registry = null;
/**
* Parity registry contract instance.
* @type {Contract.<GithubHint>}
*/
this.githubhint = null;
/**
* Parity registry contract instance.
* @type {Contract.<Operations>}
*/
this.operations = null;
/**
* Parity registry contract instance.
* @type {Contract.<BadgeReg>}
*/
this.badgereg = null;
/**
* Parity registry contract instance.
* @type {Contract.<TokenReg>}
*/
this.tokenreg = null;
/**
* A {@link Bond} representing all currently registered badges from BadgeReg.
*
* @type {Bond.<{id:string,name:string,img:string,caption:string,badge:Contract}[]>}
*/
this.badges = null;
/**
* Returns a list of badges for given address.
*
* @param {Address} address
* @returns {Bond.<Badge[]>} see {@link Bonds.badges}
*/
this.badgesOf = null;
/**
* A {@link Bond} representing all currently registered tokens from TokenReg.
*
* @type {Bond.<{id:string,tla:string,base:string,name:string,owner:address,img:string,caption:string}[]>}
*/
this.tokens = null;
/**
* Returns a list of tokens with a non-empty balance for given address.
*
* @param {Address} address
* @returns {Bond.<Token[]>} see {@link Bonds.tokens}
*/
this.tokensOf = null;
return this;
}
}
function isNumber (n) {
return typeof (n) === 'number' || (typeof (n) === 'string' && n.match(/^[0-9]+$/));
}
function memoized (f) {
var memo;
return function () {
if (memo === undefined) { memo = f(); }
return memo;
};
}
function overlay (base, top) {
Object.keys(top).forEach(k => {
base[k] = top[k];
});
return base;
}
function transactionPromise (api, tx, progress, f) {
progress({ initialising: null });
let condition = tx.condition || null;
Promise.all([api().eth.accounts(), api().eth.gasPrice()])
.then(([a, p]) => {
progress({ estimating: null });
tx.from = tx.from || a[0];
tx.gasPrice = tx.gasPrice || p;
return tx.gas || api().eth.estimateGas(tx);
})
.then(g => {
progress({ estimated: g });
tx.gas = tx.gas || g;
return api().parity.postTransaction(tx);
})
.then(signerRequestId => {
progress({ requested: signerRequestId });
return api().pollMethod('parity_checkRequest', signerRequestId);
})
.then(transactionHash => {
if (condition) {
progress(f({ signed: transactionHash, scheduled: condition }));
return { signed: transactionHash, scheduled: condition };
} else {
progress({ signed: transactionHash });
return api()
.pollMethod('eth_getTransactionReceipt', transactionHash, (receipt) => receipt && receipt.blockNumber && !receipt.blockNumber.eq(0))
.then(receipt => {
progress(f({ confirmed: receipt }));
return receipt;
});
}
})
.catch(error => {
progress({ failed: error });
});
}
class DeployContract extends oo7.ReactivePromise {
constructor (initBond, abiBond, optionsBond, api) {
super([initBond, abiBond, optionsBond, bonds.registry], [], ([init, abi, options, registry]) => {
options.data = init;
delete options.to;
let progress = this.trigger.bind(this);
transactionPromise(api, options, progress, status => {
if (status.confirmed) {
status.deployed = bonds.makeContract(status.confirmed.contractAddress, abi, options.extras || []);
}
return status;
});
// TODO: consider allowing registry of the contract here.
}, false);
this.then(_ => null);
}
isDone (s) {
return !!(s.failed || s.confirmed);
}
}
class Transaction extends oo7.ReactivePromise {
constructor (tx, api) {
super([tx], [], ([tx]) => {
let progress = this.trigger.bind(this);
transactionPromise(api, tx, progress, _ => _);
}, false);
this.then(_ => null);
}
isDone (s) {
return !!(s.failed || s.confirmed);
}
}
/**
* @param {{api: ParityApi}} Options object
* @returns {Bonds}
*/
function createBonds (options) {
const bonds = new Bonds();
// We only ever use api() at call-time of this function; this allows the
// options (particularly the transport option) to be changed dynamically
// and the datastructure to be reused.
const api = () => options.api;
const util = ParityApi.util;
class TransformBond extends oo7.TransformBond {
constructor (f, a = [], d = [], outResolveDepth = 0, resolveDepth = 1, latched = true, mayBeNull = true) {
super(f, a, d, outResolveDepth, resolveDepth, latched, mayBeNull, api());
}
map (f, outResolveDepth = 0, resolveDepth = 1) {
return new TransformBond(f, [this], [], outResolveDepth, resolveDepth);
}
sub (name, outResolveDepth = 0, resolveDepth = 1) {
return new TransformBond((r, n) => r[n], [this, name], [], outResolveDepth, resolveDepth);
}
static all (list) {
return new TransformBond((...args) => args, list);
}
}
class SubscriptionBond extends oo7.Bond {
constructor (module, rpcName, options = []) {
super();
this.module = module;
this.rpcName = rpcName;
this.options = [(_, n) => this.trigger(n), ...options];
}
initialise () {
// promise instead of id because if a dependency triggers finalise() before id's promise is resolved the unsubscribing would call with undefined
this.subscription = api().pubsub[this.module][this.rpcName](...this.options);
}
finalise () {
this.subscription.then(id => api().pubsub.unsubscribe([id]));
}
map (f, outResolveDepth = 0, resolveDepth = 1) {
return new TransformBond(f, [this], [], outResolveDepth, resolveDepth);
}
sub (name, outResolveDepth = 0, resolveDepth = 1) {
return new TransformBond((r, n) => r[n], [this, name], [], outResolveDepth, resolveDepth);
}
static all (list) {
return new TransformBond((...args) => args, list);
}
}
class Signature extends oo7.ReactivePromise {
constructor (message, from) {
super([message, from], [], ([message, from]) => {
api().parity.postSign(from, asciiToHex(message))
.then(signerRequestId => {
this.trigger({ requested: signerRequestId });
return api().pollMethod('parity_checkRequest', signerRequestId);
})
.then(signature => {
this.trigger({
signed: splitSignature(signature)
});
})
.catch(error => {
console.error(error);
this.trigger({ failed: error });
});
}, false);
this.then(_ => null);
}
isDone (s) {
return !!s.failed || !!s.signed;
}
}
function call (addr, method, args, options) {
let data = util.abiEncode(method.name, method.inputs.map(f => f.type), args);
let decode = d => util.abiDecode(method.outputs.map(f => f.type), d);
return api().eth.call(overlay({ to: addr, data: data }, options)).then(decode);
}
function post (addr, method, args, options) {
let toOptions = (addr, method, options, ...args) => {
return overlay({ to: addr, data: util.abiEncode(method.name, method.inputs.map(f => f.type), args) }, options);
};
// inResolveDepth is 2 to allow for Bonded `condition`values which are
// object values in `options`.
return new Transaction(new TransformBond(toOptions, [addr, method, options, ...args], [], 0, 2), api);
}
function presub (f) {
return new Proxy(f, {
get (receiver, name) {
if (typeof (name) === 'string' || typeof (name) === 'number') {
return typeof (receiver[name]) !== 'undefined' ? receiver[name] : receiver(name);
} else if (typeof (name) === 'symbol' && oo7.Bond.knowSymbol(name)) {
return receiver(oo7.Bond.fromSymbol(name));
} else {
throw new Error(`Weird value type to be subscripted by: ${typeof (name)}: ${JSON.stringify(name)}`);
}
}
});
}
let useSubs = false;
bonds.time = new oo7.TimeBond();
if (!useSubs) {
bonds.height = new TransformBond(() => api().eth.blockNumber().then(_ => +_), [], [bonds.time]);
let onAccountsChanged = bonds.time; // TODO: more accurate notification
let onHardwareAccountsChanged = bonds.time; // TODO: more accurate notification
let onHeadChanged = bonds.height; // TODO: more accurate notification
// let onReorg = undefined; // TODO make more accurate.
let onSyncingChanged = bonds.time;
let onAuthoringDetailsChanged = bonds.time;
let onPeerNetChanged = bonds.time; // TODO: more accurate notification
let onPendingChanged = bonds.time; // TODO: more accurate notification
let onUnsignedChanged = bonds.time; // TODO: more accurate notification
let onAutoUpdateChanged = bonds.height;
// eth_
bonds.blockNumber = bonds.height;
bonds.blockByNumber = x => new TransformBond(x => api().eth.getBlockByNumber(x), [x], []).subscriptable();// TODO: chain reorg that includes number x
bonds.blockByHash = x => new TransformBond(x => api().eth.getBlockByHash(x), [x]).subscriptable();
bonds.findBlock = hashOrNumberBond => new TransformBond(hashOrNumber => isNumber(hashOrNumber)
? api().eth.getBlockByNumber(hashOrNumber)
: api().eth.getBlockByHash(hashOrNumber),
[hashOrNumberBond], [/* onReorg */]).subscriptable();// TODO: chain reorg that includes number x, if x is a number
bonds.blocks = presub(bonds.findBlock);
bonds.block = bonds.blockByNumber(bonds.height); // TODO: DEPRECATE AND REMOVE
bonds.head = new TransformBond(() => api().eth.getBlockByNumber('latest'), [], [onHeadChanged]).subscriptable();// TODO: chain reorgs
bonds.author = new TransformBond(() => api().eth.coinbase(), [], [onAccountsChanged]);
bonds.accounts = new TransformBond(a => a.map(util.toChecksumAddress), [new TransformBond(() => api().eth.accounts(), [], [onAccountsChanged])]).subscriptable();
bonds.defaultAccount = bonds.accounts[0]; // TODO: make this use its subscription
bonds.me = bonds.accounts[0];
// TODO [ToDr] document (Post & Sign)
bonds.post = tx => new Transaction(tx, api);
bonds.sign = (message, from = bonds.me) => new Signature(message, from);
bonds.balance = x => new TransformBond(x => api().eth.getBalance(x), [x], [onHeadChanged]);
bonds.code = x => new TransformBond(x => api().eth.getCode(x), [x], [onHeadChanged]);
bonds.nonce = x => new TransformBond(x => api().eth.getTransactionCount(x).then(_ => +_), [x], [onHeadChanged]);
bonds.storageAt = (x, y) => new TransformBond((x, y) => api().eth.getStorageAt(x, y), [x, y], [onHeadChanged]);
bonds.syncing = new TransformBond(() => api().eth.syncing(), [], [onSyncingChanged]);
bonds.hashrate = new TransformBond(() => api().eth.hashrate(), [], [onAuthoringDetailsChanged]);
bonds.authoring = new TransformBond(() => api().eth.mining(), [], [onAuthoringDetailsChanged]);
bonds.ethProtocolVersion = new TransformBond(() => api().eth.protocolVersion(), [], []);
bonds.gasPrice = new TransformBond(() => api().eth.gasPrice(), [], [onHeadChanged]);
bonds.estimateGas = x => new TransformBond(x => api().eth.estimateGas(x), [x], [onHeadChanged, onPendingChanged]);
bonds.blockTransactionCount = hashOrNumberBond => new TransformBond(
hashOrNumber => isNumber(hashOrNumber)
? api().eth.getBlockTransactionCountByNumber(hashOrNumber).then(_ => +_)
: api().eth.getBlockTransactionCountByHash(hashOrNumber).then(_ => +_),
[hashOrNumberBond], [/* onReorg */]);
bonds.uncleCount = hashOrNumberBond => new TransformBond(
hashOrNumber => isNumber(hashOrNumber)
? api().eth.getUncleCountByBlockNumber(hashOrNumber).then(_ => +_)
: api().eth.getUncleCountByBlockHash(hashOrNumber).then(_ => +_),
[hashOrNumberBond], [/* onReorg */]).subscriptable();
bonds.uncle = (hashOrNumberBond, indexBond) => new TransformBond(
(hashOrNumber, index) => isNumber(hashOrNumber)
? api().eth.getUncleByBlockNumber(hashOrNumber, index)
: api().eth.getUncleByBlockHash(hashOrNumber, index),
[hashOrNumberBond, indexBond], [/* onReorg */]).subscriptable();
bonds.transaction = (hashOrNumberBond, indexOrNullBond) => new TransformBond(
(hashOrNumber, indexOrNull) =>
indexOrNull === undefined || indexOrNull === null
? api().eth.getTransactionByHash(hashOrNumber)
: isNumber(hashOrNumber)
? api().eth.getTransactionByBlockNumberAndIndex(hashOrNumber, indexOrNull)
: api().eth.getTransactionByBlockHashAndIndex(hashOrNumber, indexOrNull),
[hashOrNumberBond, indexOrNullBond], [/* onReorg */]).subscriptable();
bonds.receipt = hashBond => new TransformBond(x => api().eth.getTransactionReceipt(x), [hashBond], []).subscriptable();
// web3_
bonds.clientVersion = new TransformBond(() => api().web3.clientVersion(), [], []);
// net_
bonds.peerCount = new TransformBond(() => api().net.peerCount().then(_ => +_), [], [onPeerNetChanged]);
bonds.listening = new TransformBond(() => api().net.listening(), [], [onPeerNetChanged]);
bonds.chainId = new TransformBond(() => api().net.version(), [], []);
// parity_
bonds.hashContent = u => new TransformBond(x => api().parity.hashContent(x), [u], [], false);
bonds.gasPriceHistogram = new TransformBond(() => api().parity.gasPriceHistogram(), [], [onHeadChanged]).subscriptable();
bonds.accountsInfo = new TransformBond(() => api().parity.accountsInfo(), [], [onAccountsChanged]).subscriptable(2);
bonds.allAccountsInfo = new TransformBond(() => api().parity.allAccountsInfo(), [], [onAccountsChanged]).subscriptable(2);
bonds.hardwareAccountsInfo = new TransformBond(() => api().parity.hardwareAccountsInfo(), [], [onHardwareAccountsChanged]).subscriptable(2);
bonds.mode = new TransformBond(() => api().parity.mode(), [], [bonds.height]);
// ...authoring
bonds.defaultExtraData = new TransformBond(() => api().parity.defaultExtraData(), [], [onAuthoringDetailsChanged]);
bonds.extraData = new TransformBond(() => api().parity.extraData(), [], [onAuthoringDetailsChanged]);
bonds.gasCeilTarget = new TransformBond(() => api().parity.gasCeilTarget(), [], [onAuthoringDetailsChanged]);
bonds.gasFloorTarget = new TransformBond(() => api().parity.gasFloorTarget(), [], [onAuthoringDetailsChanged]);
bonds.minGasPrice = new TransformBond(() => api().parity.minGasPrice(), [], [onAuthoringDetailsChanged]);
bonds.transactionsLimit = new TransformBond(() => api().parity.transactionsLimit(), [], [onAuthoringDetailsChanged]);
// ...chain info
bonds.chainName = new TransformBond(() => api().parity.netChain(), [], []);
bonds.chainStatus = new TransformBond(() => api().parity.chainStatus(), [], [onSyncingChanged]).subscriptable();
// ...networking
bonds.peers = new TransformBond(() => api().parity.netPeers(), [], [onPeerNetChanged]).subscriptable(2);
bonds.enode = new TransformBond(() => api().parity.enode(), [], []);
bonds.nodePort = new TransformBond(() => api().parity.netPort().then(_ => +_), [], []);
bonds.nodeName = new TransformBond(() => api().parity.nodeName(), [], []);
bonds.signerPort = new TransformBond(() => api().parity.signerPort().then(_ => +_), [], []);
bonds.dappsPort = new TransformBond(() => api().parity.dappsPort().then(_ => +_), [], []);
bonds.dappsInterface = new TransformBond(() => api().parity.dappsInterface(), [], []);
// ...transaction queue
bonds.nextNonce = new TransformBond(() => api().parity.nextNonce().then(_ => +_), [], [onPendingChanged]);
bonds.pending = new TransformBond(() => api().parity.pendingTransactions(), [], [onPendingChanged]);
bonds.local = new TransformBond(() => api().parity.localTransactions(), [], [onPendingChanged]).subscriptable(3);
bonds.future = new TransformBond(() => api().parity.futureTransactions(), [], [onPendingChanged]).subscriptable(2);
bonds.pendingStats = new TransformBond(() => api().parity.pendingTransactionsStats(), [], [onPendingChanged]).subscriptable(2);
bonds.unsignedCount = new TransformBond(() => api().parity.parity_unsignedTransactionsCount().then(_ => +_), [], [onUnsignedChanged]);
// ...auto-update
bonds.releasesInfo = new TransformBond(() => api().parity.releasesInfo(), [], [onAutoUpdateChanged]).subscriptable();
bonds.versionInfo = new TransformBond(() => api().parity.versionInfo(), [], [onAutoUpdateChanged]).subscriptable();
bonds.consensusCapability = new TransformBond(() => api().parity.consensusCapability(), [], [onAutoUpdateChanged]);
bonds.upgradeReady = new TransformBond(() => api().parity.upgradeReady(), [], [onAutoUpdateChanged]).subscriptable();
} else {
bonds.height = new TransformBond(_ => +_, [new SubscriptionBond('eth', 'blockNumber')]).subscriptable();
let onAutoUpdateChanged = bonds.height;
// eth_
bonds.blockNumber = bonds.height;
bonds.blockByNumber = numberBond => new TransformBond(number => new SubscriptionBond('eth', 'getBlockByNumber', [number]), [numberBond]).subscriptable();
bonds.blockByHash = x => new TransformBond(x => new SubscriptionBond('eth', 'getBlockByHash', [x]), [x]).subscriptable();
bonds.findBlock = hashOrNumberBond => new TransformBond(hashOrNumber => isNumber(hashOrNumber)
? new SubscriptionBond('eth', 'getBlockByNumber', [hashOrNumber])
: new SubscriptionBond('eth', 'getBlockByHash', [hashOrNumber]),
[hashOrNumberBond]).subscriptable();
bonds.blocks = presub(bonds.findBlock);
bonds.block = bonds.blockByNumber(bonds.height); // TODO: DEPRECATE AND REMOVE
bonds.head = new SubscriptionBond('eth', 'getBlockByNumber', ['latest']).subscriptable();
bonds.author = new SubscriptionBond('eth', 'coinbase');
bonds.me = new SubscriptionBond('parity', 'defaultAccount');
bonds.defaultAccount = bonds.me; // TODO: DEPRECATE
bonds.accounts = new SubscriptionBond('eth', 'accounts').subscriptable();
bonds.post = tx => new Transaction(tx, api);
bonds.sign = (message, from = bonds.me) => new Signature(message, from);
bonds.balance = x => new TransformBond(x => new SubscriptionBond('eth', 'getBalance', [x]), [x]);
bonds.code = x => new TransformBond(x => new SubscriptionBond('eth', 'getCode', [x]), [x]);
bonds.nonce = x => new TransformBond(x => new SubscriptionBond('eth', 'getTransactionCount', [x]), [x]); // TODO: then(_ => +_) Depth 2 if second TransformBond or apply to result
bonds.storageAt = (x, y) => new TransformBond((x, y) => new SubscriptionBond('eth', 'getStorageAt', [x, y]), [x, y]);
bonds.syncing = new SubscriptionBond('eth', 'syncing');
bonds.hashrate = new SubscriptionBond('eth', 'hashrate');
bonds.authoring = new SubscriptionBond('eth', 'mining');
bonds.ethProtocolVersion = new SubscriptionBond('eth', 'protocolVersion');
bonds.gasPrice = new SubscriptionBond('eth', 'gasPrice');
bonds.estimateGas = x => new TransformBond(x => new SubscriptionBond('eth', 'estimateGas', [x]), [x]);
bonds.blockTransactionCount = hashOrNumberBond => new TransformBond(
hashOrNumber => isNumber(hashOrNumber)
? new TransformBond(_ => +_, [new SubscriptionBond('eth', 'getBlockTransactionCountByNumber', [hashOrNumber])])
: new TransformBond(_ => +_, [new SubscriptionBond('eth', 'getBlockTransactionCountByHash', [hashOrNumber])]),
[hashOrNumberBond]);
bonds.uncleCount = hashOrNumberBond => new TransformBond(
hashOrNumber => isNumber(hashOrNumber)
? new TransformBond(_ => +_, [new SubscriptionBond('eth', 'getUncleCountByBlockNumber', [hashOrNumber])])
: new TransformBond(_ => +_, [new SubscriptionBond('eth', 'getUncleCountByBlockHash', [hashOrNumber])]),
[hashOrNumberBond]).subscriptable();
bonds.uncle = (hashOrNumberBond, indexBond) => new TransformBond(
(hashOrNumber, index) => isNumber(hashOrNumber)
? new SubscriptionBond('eth', 'getUncleByBlockNumberAndIndex', [hashOrNumber, index])
: new SubscriptionBond('eth', 'getUncleByBlockHashAndIndex', [hashOrNumber, index]),
[hashOrNumberBond, indexBond]).subscriptable();
bonds.transaction = (hashOrNumberBond, indexOrNullBond) => new TransformBond(
(hashOrNumber, indexOrNull) =>
indexOrNull === undefined || indexOrNull === null
? new SubscriptionBond('eth', 'getTransactionByHash', [hashOrNumber])
: isNumber(hashOrNumber)
? new SubscriptionBond('eth', 'getTransactionByBlockNumberAndIndex', [hashOrNumber, indexOrNull])
: new SubscriptionBond('eth', 'getTransactionByBlockHashAndIndex', [hashOrNumber, indexOrNull]),
[hashOrNumberBond, indexOrNullBond]).subscriptable();
bonds.receipt = hashBond => new TransformBond(x => new SubscriptionBond('eth', 'getTransactionReceipt', [x]), [hashBond]).subscriptable();
// web3_
bonds.clientVersion = new TransformBond(() => api().web3.clientVersion(), [], []);
// net_
bonds.peerCount = new TransformBond(_ => +_, [new SubscriptionBond('net', 'peerCount')]);
bonds.listening = new SubscriptionBond('net', 'listening');
bonds.chainId = new SubscriptionBond('net', 'version');
// parity_
bonds.hashContent = u => new TransformBond(x => api().parity.hashContent(x), [u], [], false);
bonds.gasPriceHistogram = new SubscriptionBond('parity', 'gasPriceHistogram').subscriptable();
bonds.mode = new SubscriptionBond('parity', 'mode');
bonds.accountsInfo = new SubscriptionBond('parity', 'accountsInfo').subscriptable(2);
bonds.allAccountsInfo = new SubscriptionBond('parity', 'allAccountsInfo').subscriptable(2);
bonds.hardwareAccountsInfo = new SubscriptionBond('parity', 'hardwareAccountsInfo').subscriptable(2);
// ...authoring
bonds.defaultExtraData = new SubscriptionBond('parity', 'defaultExtraData');
bonds.extraData = new SubscriptionBond('parity', 'extraData');
bonds.gasCeilTarget = new SubscriptionBond('parity', 'gasCeilTarget');
bonds.gasFloorTarget = new SubscriptionBond('parity', 'gasFloorTarget');
bonds.minGasPrice = new SubscriptionBond('parity', 'minGasPrice');
bonds.transactionsLimit = new SubscriptionBond('parity', 'transactionsLimit');
// ...chain info
bonds.chainName = new SubscriptionBond('parity', 'netChain');
bonds.chainStatus = new SubscriptionBond('parity', 'chainStatus').subscriptable();
// ...networking
bonds.peers = new SubscriptionBond('parity', 'netPeers').subscriptable(2);
bonds.enode = new SubscriptionBond('parity', 'enode');
bonds.nodePort = new TransformBond(_ => +_, [new SubscriptionBond('parity', 'netPort')]);
bonds.nodeName = new SubscriptionBond('parity', 'nodeName');
// Where defined ?
bonds.signerPort = new TransformBond(() => api().parity.signerPort().then(_ => +_), [], []);
bonds.dappsPort = new TransformBond(() => api().parity.dappsPort().then(_ => +_), [], []);
bonds.dappsInterface = new TransformBond(() => api().parity.dappsInterface(), [], []);
// ...transaction queue
bonds.nextNonce = new TransformBond(_ => +_, [new SubscriptionBond('parity', 'nextNonce')]);
bonds.pending = new SubscriptionBond('parity', 'pendingTransactions').subscriptable();
bonds.local = new SubscriptionBond('parity', 'localTransactions').subscriptable(3);
bonds.future = new SubscriptionBond('parity', 'futureTransactions').subscriptable(2);
bonds.pendingStats = new SubscriptionBond('parity', 'pendingTransactionsStats').subscriptable(2);
bonds.unsignedCount = new TransformBond(_ => +_, [new SubscriptionBond('parity', 'unsignedTransactionsCount')]);
bonds.requestsToConfirm = new SubscriptionBond('signer', 'requestsToConfirm');
// ...auto-update
bonds.releasesInfo = new SubscriptionBond('parity', 'releasesInfo').subscriptable();
bonds.versionInfo = new SubscriptionBond('parity', 'versionInfo').subscriptable();
bonds.consensusCapability = new SubscriptionBond('parity', 'consensusCapability').subscriptable();
bonds.upgradeReady = new TransformBond(() => api().parity.upgradeReady(), [], [onAutoUpdateChanged]).subscriptable();
}
// trace TODO: Implement contract object with new trace_many feature
bonds.replayTx = (x, whatTrace) => new TransformBond((x, whatTrace) => api().trace.replayTransaction(x, whatTrace), [x, whatTrace], []).subscriptable();
bonds.callTx = (x, whatTrace, blockNumber) => new TransformBond((x, whatTrace, blockNumber) => api().trace.call(x, whatTrace, blockNumber), [x, whatTrace, blockNumber], []).subscriptable();
function traceCall (addr, method, args, options) {
let data = util.abiEncode(method.name, method.inputs.map(f => f.type), args);
let decode = d => util.abiDecode(method.outputs.map(f => f.type), d);
let traceMode = options.traceMode;
delete options.traceMode;
return api().trace.call(overlay({ to: addr, data: data }, options), traceMode, 'latest').then(decode);
}
bonds.deployContract = function (init, abi, options = {}) {
return new DeployContract(init, abi, options, api);
};
bonds.makeContract = function (address, abi, extras = [], debug = false) {
var r = { address: address };
let unwrapIfOne = a => a.length === 1 ? a[0] : a;
abi.forEach(i => {
if (i.type === 'function' && i.constant) {
let f = function (...args) {
var options = args.length === i.inputs.length + 1 ? args.pop() : {};
if (args.length !== i.inputs.length) {
throw new Error(`Invalid number of arguments to ${i.name}. Expected ${i.inputs.length}, got ${args.length}.`);
}
let f = (addr, ...fargs) => debug
? traceCall(address, i, args, options)
: call(addr, i, fargs, options)
.then(rets => rets.map((r, o) => cleanup(r, i.outputs[o].type, api)))
.then(unwrapIfOne);
return new TransformBond(f, [address, ...args], [bonds.height]).subscriptable(); // TODO: should be subscription on contract events
};
r[i.name] = (i.inputs.length === 0) ? memoized(f) : (i.inputs.length === 1) ? presub(f) : f;
r[i.name].args = i.inputs;
}
});
extras.forEach(i => {
let f = function (...args) {
let expectedInputs = (i.numInputs || i.args.length);
var options = args.length === expectedInputs + 1 ? args.pop() : {};
if (args.length !== expectedInputs) {
throw new Error(`Invalid number of arguments to ${i.name}. Expected ${expectedInputs}, got ${args.length}. ${args}`);
}
let c = abi.find(j => j.name === i.method);
let f = (addr, ...fargs) => {
let args = i.args.map((v, index) => v === null ? fargs[index] : typeof (v) === 'function' ? v(fargs[index]) : v);
return debug
? traceCall(address, i, args, options)
: call(addr, c, args, options).then(unwrapIfOne);
};
return new TransformBond(f, [address, ...args], [bonds.height]).subscriptable(); // TODO: should be subscription on contract events
};
r[i.name] = (i.args.length === 1) ? presub(f) : f;
r[i.name].args = i.args;
});
abi.forEach(i => {
if (i.type === 'function' && !i.constant) {
r[i.name] = function (...args) {
var options = args.length === i.inputs.length + 1 ? args.pop() : {};
if (args.length !== i.inputs.length) { throw new Error(`Invalid number of arguments to ${i.name}. Expected ${i.inputs.length}, got ${args.length}. ${args}`); }
return debug
? traceCall(address, i, args, options)
: post(address, i, args, options).subscriptable();
};
r[i.name].args = i.inputs;
}
});
var eventLookup = {};
abi.filter(i => i.type === 'event').forEach(i => {
eventLookup[util.abiSignature(i.name, i.inputs.map(f => f.type))] = i.name;
});
function prepareIndexEncode (v, t, top = true) {
if (v instanceof Array) {
if (top) {
return v.map(x => prepareIndexEncode(x, t, false));
} else {
throw new Error('Invalid type');
}
}
var val;
if (t === 'string' || t === 'bytes') {
val = util.sha3(v);
} else {
val = util.abiEncode(null, [t], [v]);
}
if (val.length !== 66) {
throw new Error('Invalid length');
}
return val;
}
abi.forEach(i => {
if (i.type === 'event') {
r[i.name] = function (indexed = {}, params = {}) {
return new TransformBond((addr, indexed) => {
var topics = [util.abiSignature(i.name, i.inputs.map(f => f.type))];
i.inputs.filter(f => f.indexed).forEach(f => {
try {
topics.push(indexed[f.name] ? prepareIndexEncode(indexed[f.name], f.type) : null);
} catch (e) {
throw new Error(`Couldn't encode indexed parameter ${f.name} of type ${f.type} with value ${indexed[f.name]}`);
}
});
return api().eth.getLogs({
address: addr,
fromBlock: params.fromBlock || 0,
toBlock: params.toBlock || 'pending',
limit: params.limit || 10,
topics: topics
}).then(logs => logs.map(l => {
l.blockNumber = +l.blockNumber;
l.transactionIndex = +l.transactionIndex;
l.logIndex = +l.logIndex;
l.transactionLogIndex = +l.transactionLogIndex;
var e = {};
let unins = i.inputs.filter(f => !f.indexed);
util.abiDecode(unins.map(f => f.type), l.data).forEach((v, j) => {
let f = unins[j];
if (v instanceof Array && !f.type.endsWith(']')) {
v = util.bytesToHex(v);
}
if (f.type.substr(0, 4) === 'uint' && +f.type.substr(4) <= 48) {
v = +v;
}
e[f.name] = v;
});
i.inputs.filter(f => f.indexed).forEach((f, j) => {
if (f.type === 'string' || f.type === 'bytes') {
e[f.name] = l.topics[1 + j];
} else {
var v = util.abiDecode([f.type], l.topics[1 + j])[0];
if (v instanceof Array) {
v = util.bytesToHex(v);
}
if (f.type.substr(0, 4) === 'uint' && +f.type.substr(4) <= 48) {
v = +v;
}
e[f.name] = v;
}
});
e.event = eventLookup[l.topics[0]];
e.log = l;
return e;
}));
}, [address, indexed], [bonds.height]).subscriptable();
};
r[i.name].args = i.inputs;
}
});
return r;
};
if (useSubs) {
bonds.registry = bonds.makeContract(new SubscriptionBond('parity', 'registryAddress'), RegistryABI, RegistryExtras);
} else {
bonds.registry = bonds.makeContract(new TransformBond(() => api().parity.registryAddress(), [], [bonds.time]), RegistryABI, RegistryExtras);
}
bonds.githubhint = bonds.makeContract(bonds.registry.lookupAddress('githubhint', 'A'), GitHubHintABI);
bonds.operations = bonds.makeContract(bonds.registry.lookupAddress('operations', 'A'), OperationsABI);
bonds.badgereg = bonds.makeContract(bonds.registry.lookupAddress('badgereg', 'A'), BadgeRegABI);
bonds.tokenreg = bonds.makeContract(bonds.registry.lookupAddress('tokenreg', 'A'), TokenRegABI);
bonds.badges = new TransformBond(n => {
var ret = [];
for (var i = 0; i < +n; ++i) {
let id = i;
ret.push(oo7.Bond.all([
bonds.badgereg.badge(id),
bonds.badgereg.meta(id, 'IMG'),
bonds.badgereg.meta(id, 'CAPTION')
]).map(([[addr, name, owner], img, caption]) => ({
id,
name,
img,
caption,
badge: bonds.makeContract(addr, BadgeABI)
}))
);
}
return ret;
}, [bonds.badgereg.badgeCount()], [], 1);
bonds.badgesOf = address => new TransformBond(
(addr, bads) => bads.map(b => ({
certified: b.badge.certified(addr),
badge: b.badge,
id: b.id,
img: b.img,
caption: b.caption,
name: b.name
})),
[address, bonds.badges], [], 2
).map(all => all.filter(_ => _.certified));
bonds.tokens = new TransformBond(n => {
var ret = [];
for (var i = 0; i < +n; ++i) {
let id = i;
ret.push(oo7.Bond.all([
bonds.tokenreg.token(id),
bonds.tokenreg.meta(id, 'IMG'),
bonds.tokenreg.meta(id, 'CAPTION')
]).map(([[addr, tla, base, name, owner], img, caption]) => ({
id,
tla,
base,
name,
img,
caption,
token: bonds.makeContract(addr, TokenABI)
}))
);
}
return ret;
}, [bonds.tokenreg.tokenCount()], [], 1);
bonds.tokensOf = address => new TransformBond(
(addr, bads) => bads.map(b => ({
balance: b.token.balanceOf(addr),
token: b.token,
id: b.id,
name: b.name,
tla: b.tla,
base: b.base,
img: b.img,
caption: b.caption
})),
[address, bonds.tokens], [], 2
).map(all => all.filter(_ => _.balance.gt(0)));
bonds.namesOf = address => new TransformBond((reg, addr, accs) => ({
owned: accs[addr] ? accs[addr].name : null,
registry: reg || null
}), [bonds.registry.reverse(address), address, bonds.accountsInfo]);
bonds.registry.names = oo7.Bond.mapAll([bonds.registry.ReverseConfirmed({}, { limit: 100 }), bonds.accountsInfo],
(reg, info) => {
let r = {};
Object.keys(info).forEach(k => r[k] = info[k].name);
reg.forEach(a => r[a.reverse] = bonds.registry.reverse(a.reverse));
return r;
}, 1);
return bonds;
}
const t = defaultProvider();
const options = t ? { api: new ParityApi(t) } : null;
/** @type {Bonds} */
const bonds = options ? createBonds(options) : null;
const isOwned = addr => oo7.Bond.mapAll([addr, bonds.accounts], (a, as) => as.indexOf(a) !== -1);
const isNotOwned = addr => oo7.Bond.mapAll([addr, bonds.accounts], (a, as) => as.indexOf(a) === -1);
module.exports = {
// Bonds stuff
// abiPolyfill,
options,
bonds,
Bonds,
createBonds,
// Util functions
isOwned,
isNotOwned,
asciiToHex,
bytesToHex,
hexToAscii,
isAddressValid,
toChecksumAddress,
sha3,
capitalizeFirstLetter,
singleton,
denominations,
denominationMultiplier,
interpretRender,
combineValue,
defDenom,
formatValue,
formatValueNoDenom,
formatToExponential,
interpretQuantity,
splitValue,
formatBalance,
formatBlockNumber,
isNullData,
splitSignature,
removeSigningPrefix,
cleanup,
// ABIs
abiPolyfill,
RegistryABI,
RegistryExtras,
GitHubHintABI,
OperationsABI,
BadgeRegABI,
TokenRegABI,
BadgeABI,
TokenABI
};