@windingtree/wt-js-libs
Version:
Javascript libraries to interact with the Winding Tree contracts
182 lines (169 loc) • 7.02 kB
JavaScript
import { WTLibsError } from '../errors';
import { InputDataError, OrganizationNotFoundError, OrganizationNotInstantiableError } from './errors';
import OnChainOrganization from './organization';
/**
* Wrapper class for a SegmentDirectory smart contract. Allows you to
* add and remove organizations from this directory.
*/
export class SegmentDirectory {
static createInstance (address, web3Utils, web3Contracts) {
return new SegmentDirectory(address, web3Utils, web3Contracts);
}
constructor (address, web3Utils, web3Contracts) {
this.address = address;
this.web3Utils = web3Utils;
this.web3Contracts = web3Contracts;
}
async _getDeployedDirectory () {
if (!this.deployedDirectory) {
this.deployedDirectory = await this.web3Contracts.getSegmentDirectoryInstance(this.address);
}
return this.deployedDirectory;
}
/**
* Generates transaction data required for adding an organization
* and more metadata required for sucessful mining of that transaction.
* Does not sign or send the transaction.
*
* @throws {InputDataError} When orgData does not contain address property.
* @throws {InputDataError} When orgData does not contain a owner property.
* @throws {WTLibsError} When anything goes wrong during data preparation phase.
*/
async add (orgData) {
if (!orgData.address) {
throw new InputDataError('Cannot add Organization without address.');
}
const orgOwner = await orgData.owner;
if (!orgOwner) {
throw new InputDataError('Cannot add Organization without owner.');
}
try {
const directory = await this._getDeployedDirectory();
const data = directory.methods.add(orgData.address).encodeABI();
const estimate = directory.methods.add(orgData.address).estimateGas({ from: orgOwner });
const transactionData = {
nonce: await this.web3Utils.determineCurrentAddressNonce(orgOwner),
data: data,
from: orgOwner,
to: this.address,
gas: this.web3Utils.applyGasModifier(await estimate),
};
return {
transactionData: transactionData,
eventCallbacks: {
onReceipt: () => {}, // use empty callback to ensure consistent behaviour of all tx methods
},
};
} catch (err) {
throw new WTLibsError(`Cannot add Organization: ${err.message}`, err);
}
}
/**
* Generates transaction data required for removing an organization
* and more metadata required for successful mining of that transaction.
* Does not sign or send the transaction.
*
* @throws {InputDataError} When organization does not contain address property.
* @throws {InputDataError} When organization does not contain a owner property.
* @throws {WTLibsError} When anything goes wrong during data preparation phase.
*/
async remove (orgData) {
if (!orgData.address) {
throw new InputDataError('Cannot remove Organization without address.');
}
const orgOwner = await orgData.owner;
if (!orgOwner) {
throw new InputDataError('Cannot remove Organization without owner.');
}
try {
const directory = await this._getDeployedDirectory();
const data = directory.methods.remove(orgData.address).encodeABI();
const estimate = directory.methods.remove(orgData.address).estimateGas({ from: orgOwner });
const transactionData = {
nonce: await this.web3Utils.determineCurrentAddressNonce(orgOwner),
data: data,
from: orgOwner,
to: this.address,
gas: this.web3Utils.applyGasModifier(await estimate),
};
return {
transactionData: transactionData,
eventCallbacks: {
onReceipt: () => {}, // use empty callback to ensure consistent behaviour of all tx methods
},
};
} catch (err) {
throw new WTLibsError(`Cannot remove Organization: ${err.message}`, err);
}
}
/**
* Gets organization representation of a organization on a given address. If organization
* on such address is not registered through this Winding Tree index
* instance, the method throws immediately.
*
* @throws {OrganizationNotFoundError} When organization does not exist.
* @throws {OrganizationNotInstantiableError} When the organization class cannot be constructed.
* @throws {WTLibsError} When something breaks in the network communication.
*/
async getOrganization (address) {
let organizationIndex;
try {
// This returns strings
organizationIndex = await this.getOrganizationIndex(address);
} catch (err) {
throw new WTLibsError(`Cannot find Organization at ${address}: ${err.message}`, err);
}
// Zeroeth position is reserved as empty during index deployment
if (!organizationIndex) {
throw new OrganizationNotFoundError(`Cannot find Organization at ${address}: Not found in Organization list`);
} else {
try {
return OnChainOrganization.createInstance(this.web3Utils, this.web3Contracts, address);
} catch (err) {
throw new OrganizationNotInstantiableError(`Cannot instantiate Organization at ${address}: ${err.message}`, err);
}
}
}
async getOrganizationIndex (address) {
const directory = await this._getDeployedDirectory();
return parseInt(await directory.methods.organizationsIndex(address).call(), 10);
}
async getOrganizationByIndex (organizationIndex) {
const directory = await this._getDeployedDirectory();
const address = await directory.methods.organizations(organizationIndex).call();
return this.getOrganization(address);
}
/**
* Returns a list of all organizations. It will filter out
* every organization that is inaccessible for any reason.
*
* Currently any inaccessible organization is silently ignored.
* Subject to change.
*/
async getOrganizations () {
const directory = await this._getDeployedDirectory();
const orgAddressList = await directory.methods.getOrganizations().call();
const getOrgDetails = orgAddressList
// Filtering null addresses beforehand improves efficiency
.filter((addr) => !this.web3Utils.isZeroAddress(addr))
.map((addr) => {
return this.getOrganization(addr) // eslint-disable-line promise/no-nesting
// We don't really care why the organization is inaccessible
// and we need to catch exceptions here on each individual organization
.catch((err) => { // eslint-disable-line
return null;
});
});
const orgDetails = await (Promise.all(getOrgDetails));
return (orgDetails.filter(a => a != null));
}
async getLifTokenAddress () {
const index = await this._getDeployedDirectory();
return index.methods.getLifToken().call();
}
async getSegment (transactionOptions = {}) {
const directory = await this._getDeployedDirectory();
return directory.methods.getSegment().call(transactionOptions);
}
}
export default SegmentDirectory;