/* eslint-disable no-underscore-dangle */
// eslint-disable-next-line no-unused-vars
import { network } from '../../interfaces';
import Numbers from '../../utils/Numbers';
import IContract from '../IContract';
import ERC20Contract from '../ERC20/ERC20Contract';
/**
* @typedef {Object} Network~Options
* @property {Boolean} test
* @property {Boolean} localtest ganache local blockchain
* @property {Web3Connection} [web3Connection=Web3Connection] created from params: 'test', 'localtest' and optional 'web3Connection' string and 'privateKey'
* @property {string} [contractAddress]
*/
/**
* Network Object
* @class Network
* @param {Network~Options} options
*/
class Network extends IContract {
constructor(params) {
super({ abi: network, ...params });
}
/**
* Asserts the 2 {@link ERC20Contract} on the current address
* @function
* @return {Promise<void>}
* @throws {Error} Contract is not deployed, first deploy it and provide a contract address
*/
__assert = async () => {
if (!this.getAddress()) {
throw new Error(
'Contract is not deployed, first deploy it and provide a contract address',
);
}
// Use ABI
this.params.contract.use(network, this.getAddress());
const transactionalAddress = await this.getTransactionTokenAddress();
const settlerAddresss = await this.getSettlerTokenAddress();
// Set Token Address Contract for easy access
this.params.transactionalToken = new ERC20Contract({
web3Connection: this.web3Connection,
contractAddress: transactionalAddress,
});
// Set Token Address Contract for easy access
this.params.settlerToken = new ERC20Contract({
web3Connection: this.web3Connection,
contractAddress: settlerAddresss,
});
// Assert Token Contract
await this.params.transactionalToken.start();
await this.params.transactionalToken.__assert();
// Assert Token Contract
await this.params.settlerToken.start();
await this.params.settlerToken.__assert();
};
/**
* Get Open Issues Available
* @param {Address} address
* @returns {number[]}
*/
async getIssuesByAddress(address) {
const res = await this.getContract()
.methods.getIssuesByAddress(address)
.call();
return res.map(r => parseInt(r, 10));
}
/**
* Get Amount of Issues Opened in the network
* @returns {Promise<number>}
*/
async getAmountofIssuesOpened() {
return parseInt(
await this.getContract().methods.incrementIssueID().call(),
10,
);
}
/**
* Get Amount of Issues Closed in the network
* @returns {Promise<number>}
*/
async getAmountofIssuesClosed() {
return parseInt(
await this.getContract().methods.closedIdsCount().call(),
10,
);
}
/**
* Get Amount of Disputers (people who locked BEPRO) in the network
* @returns {Promise<number>}
*/
async getAmountOfDisputers() {
return parseInt(
await this.getContract().methods.oraclersArray().call(),
10,
);
}
/**
* Get Amount of Needed for Approve
* @returns {Promise<number>}
*/
async percentageNeededForApprove() {
return parseInt(
await this.getContract()
.methods.percentageNeededForApprove()
.call(),
10,
);
}
/**
* @description Get Amount of % Needed for Dispute
* @returns {Promise<number>}
*/
async percentageNeededForDispute() {
return parseInt(
await this.getContract()
.methods.percentageNeededForDispute()
.call(),
10,
);
}
/**
* @description Get Amount of Merge Fee Share
* @returns {Promise<number>}
*/
async mergeCreatorFeeShare() {
return parseInt(
await this.getContract()
.methods.mergeCreatorFeeShare()
.call(),
10,
);
}
/**
* @description Get Time of disputableTime
* @returns {Promise<Date>}
*/
async disputableTime() {
return Numbers.fromSmartContractTimeToMinutes(
await this.getContract()
.methods.disputableTime()
.call(),
10,
);
}
/**
* @description Get Time of redeemTime
* @returns {Promise<Date>}
*/
async redeemTime() {
return Numbers.redeemTime(
await this.getContract()
.methods.redeemTime()
.call(),
10,
);
}
/**
* Get Amount of Needed for Merge
* @returns {Promise<number>}
*/
async percentageNeededForMerge() {
return parseInt(
await this.getContract()
.methods.percentageNeededForMerge()
.call(),
10,
);
}
/**
* Get Total Amount of Tokens Staked for Bounties in the Network
* @returns {Promise<number>}
*/
async getTokensStaked() {
return Numbers.fromDecimals(
await this.getContract().methods.totalStaked().call(),
18,
);
}
/**
* Get Total Amount of BEPRO Staked for Oracles
* @returns {Promise<number>}
*/
async getBEPROStaked() {
return Numbers.fromDecimals(
await this.getContract().methods.oraclesStaked().call(),
18,
);
}
async oraclesStaked() {
return Numbers.fromDecimalsToBN(
await this.getContract().methods.oraclesStaked().call(),
18,
);
}
/**
* Get Total Amount of Tokens Staked in the network
* @returns {Promise<number>}
*/
async votesStaked() {
return Numbers.fromDecimals(
await this.getContract()
.methods.votesStaked()
.call(),
18,
);
}
/**
* Get Transaction Token Address
* @returns {Promise<address>}
*/
getTransactionTokenAddress() {
return this.getContract()
.methods.transactionToken()
.call();
}
/**
* Verify if Address is Council
* @param {Object} params
* @param {number} params.address
* @returns {Promise<address>}
*/
async isCouncil({ address }) {
return await this.getOraclesByAddress({ address }) >= await this.COUNCIL_AMOUNT();
}
/**
* Get Settler Token Address
* @returns {Promise<address>}
*/
getSettlerTokenAddress() {
return this.getContract()
.methods.settlerToken()
.call();
}
/**
* Get Amount Needed for Council
* @returns {Promise<Integer>}
*/
async COUNCIL_AMOUNT() {
return Numbers.fromDecimals(
await this.getContract()
.methods.COUNCIL_AMOUNT()
.call(),
18,
);
}
/**
* Get Amount Needed for Council
* @returns {Promise<Integer>}
*/
/**
* Change amount needed for Council
* @param {Object} params
* @param {number} params.value
* @return {Promise<TransactionObject>}
*/
changeCouncilAmount({ value }, options) {
return this.__sendTx(
this.getContract().methods.changeCOUNCIL_AMOUNT(
Numbers.toSmartContractDecimals(value, this.getSettlerTokenContract().getDecimals()),
),
options,
);
}
/**
* Verify if Issue is still in Draft Mode (Available to use the redeemIssue Action)
* @param {Object} params
* @param {number} params.issueId
* @returns {Promise<boolean>}
*/
isIssueInDraft({ issueId }) {
return this.getContract()
.methods.isIssueInDraft(issueId)
.call();
}
/**
* Verify if Merge is disputed (i.e. was rejected by the network holders)
* @param {Object} params
* @param {number} params.issueId
* @param {number} params.mergeId
* @returns {Promise<boolean>}
*/
isMergeDisputed({ issueId, mergeId }) {
return this.getContract()
.methods.isMergeDisputed(issueId, mergeId)
.call();
}
/**
* Get Issue Id Info
* @param {Object} params
* @param {Address} params.address
* @returns {Promise<number>} Number of votes
*/
async getOraclesByAddress({ address }) {
const r = await this.getContract()
.methods.getOraclesByAddress(address)
.call();
return Numbers.fromDecimals(r, 18);
}
/**
* Get Oralces By Address
* @param {Object} params
* @param {Address} params.address
* @returns {Integer} oraclesDelegatedByOthers
* @returns {Array | Integer} amounts
* @returns {Array | Address} addresses
* @returns {Integer} tokensLocked
*/
async getOraclesSummary({ address }) {
const r = await this.getContract()
.methods.getOraclesSummary(address)
.call();
return {
oraclesDelegatedByOthers: Numbers.fromDecimals(r[0], 18),
amounts: r[1] ? r[1].map(a => Numbers.fromDecimals(a, 18)) : [],
addresses: r[2] ? r[2].map(a => a) : [],
tokensLocked: Numbers.fromDecimals(r[3], 18),
};
}
/**
* Get Issue By Id
* @param {Object} params
* @param {String} params.issueCID
* @returns {Promise<TokensNetwork~Issue>}
*/
async getIssueByCID({ issueCID }) {
const r = await this.__sendTx(
this.getContract().methods.getIssueByCID(issueCID),
{ call: true },
);
return {
_id: Numbers.fromHex(r[0]),
cid: r[1],
creationDate: Numbers.fromSmartContractTimeToMinutes(r[2]),
tokensStaked: Numbers.fromDecimals(r[3], 18),
issueGenerator: r[4],
mergeProposalsAmount: parseInt(r[5], 10),
finalized: r[6],
canceled: r[7],
recognizedAsFinished: r[8],
};
}
/**
* Get Issue By Id
* @param {Object} params
* @param {Integer} params.issueId
* @returns {Promise<TokensNetwork~Issue>}
*/
async getIssueById({ issueId }) {
const r = await this.__sendTx(
this.getContract().methods.getIssueById(issueId),
{ call: true },
);
return {
_id: Numbers.fromHex(r[0]),
cid: r[1],
creationDate: Numbers.fromSmartContractTimeToMinutes(r[2]),
tokensStaked: Numbers.fromDecimals(r[3], 18),
issueGenerator: r[4],
mergeProposalsAmount: parseInt(r[5], 10),
finalized: r[6],
canceled: r[7],
recognizedAsFinished: r[8],
};
}
/**
* Get votes, address and amounts for issue
* @param {Object} params
* @param {number} params.issue_id
* @param {number} params.merge_id
* @return {Promise<TokensNetwork~MergedIssue>}
*/
async getMergeById({ issue_id, merge_id }) {
const r = await this.__sendTx(
this.getContract()
.methods.getMergeById(issue_id, merge_id),
{ call: true },
);
return {
_id: Numbers.fromHex(r[0]),
votes: Numbers.fromDecimals(r[1], 18),
disputes: Numbers.fromDecimals(r[2], 18),
prAddresses: r[3],
prAmounts: r[4] ? r[4].map(a => Numbers.fromDecimals(a, 18)) : 0,
proposalAddress: r[5],
};
}
/**
* Approve ERC20 Allowance
* @function
* @return {Promise<number>}
*/
approveSettlerERC20Token = async () => {
const totalMaxAmount = await this.getSettlerTokenContract().totalSupply();
return this.getSettlerTokenContract().approve({
address: this.getAddress(),
amount: totalMaxAmount,
});
};
/**
* Approve ERC20 Allowance
* @function
* @return {Promise<number>}
*/
approveTransactionalERC20Token = async () => {
const totalMaxAmount = await this.getTransactionTokenContract().totalSupply();
return this.getTransactionTokenContract().approve({
address: this.getAddress(),
amount: totalMaxAmount,
});
};
/**
* Verify if Approved
* @function
* @param {Object} params
* @param {number} params.amount
* @param {Address} params.address
* @return {Promise<number>}
*/
isApprovedSettlerToken = ({ amount, address }) => this.getSettlerTokenContract().isApproved({
address,
amount,
spenderAddress: this.getAddress(),
});
/**
* Verify if Approved
* @function
* @param {Object} params
* @param {number} params.amount
* @param {Address} params.address
* @return {Promise<number>}
*/
isApprovedTransactionalToken = ({ amount, address }) => this.getTransactionTokenContract().isApproved({
address,
amount,
spenderAddress: this.getAddress(),
});
/**
* lock tokens for oracles
* @param {Object} params
* @params params.tokenAmount {number}
* @throws {Error} Tokens Amount has to be higher than 0
* @throws {Error} Tokens not approve for tx, first use 'approveERC20'
* @return {Promise<TransactionObject>}
*/
lock({ tokenAmount }, options) {
if (tokenAmount <= 0) {
throw new Error('Token Amount has to be higher than 0');
}
return this.__sendTx(
this.getContract().methods.lock(
Numbers.toSmartContractDecimals(tokenAmount, this.getSettlerTokenContract().getDecimals()),
),
options,
);
}
/**
* Unlock Tokens for oracles
* @param {Object} params
* @params params.tokenAmount {number}
* @params params.from {address}
* @throws {Error} Tokens Amount has to be higher than 0
* @return {Promise<TransactionObject>}
*/
unlock({ tokenAmount, from }, options) {
if (tokenAmount <= 0) {
throw new Error('Tokens Amount has to be higher than 0');
}
return this.__sendTx(
this.getContract().methods.unlock(
Numbers.toSmartContractDecimals(tokenAmount, this.getSettlerTokenContract().getDecimals()),
from,
),
options,
);
}
/**
* Delegated Oracles to others
* @param {Object} params
* @param {number} params.tokenAmount
* @param {Address} params.delegatedTo
* @return {Promise<TransactionObject>}
*/
delegateOracles({ tokenAmount, delegatedTo }, options) {
if (tokenAmount <= 0) {
throw new Error('Tokens Amount has to be higher than 0');
}
return this.__sendTx(
this.getContract()
.methods.delegateOracles(
Numbers.toSmartContractDecimals(tokenAmount, this.getTransactionTokenContract().getDecimals()),
delegatedTo,
),
options,
);
}
/**
* Recognize Issue as Resolved
* @param {Object} params
* @param {Number} params.issueId
* @return {Promise<TransactionObject>}
*/
recognizeAsFinished({ issueId }, options) {
return this.__sendTx(
this.getContract()
.methods.recognizeAsFinished(issueId),
options,
);
}
/**
* open Issue
* @param {Object} params
* @param {number} params.tokenAmount
* @param {String} params.cid
* @throws {Error} Tokens Amount has to be higher than 0
* @throws {Error} Tokens not approve for tx, first use 'approveERC20'
* @return {Promise<TransactionObject>}
*/
openIssue({ tokenAmount, cid }, options) {
if (tokenAmount < 0) {
throw new Error('Tokens Amount has to be higher than 0');
}
return this.__sendTx(
this.getContract().methods.openIssue(
cid,
Numbers.toSmartContractDecimals(tokenAmount, this.getTransactionTokenContract().getDecimals()),
),
options,
);
}
/**
* redeem Issue
* @param {Object} params
* @param {number} params.issueId
* @return {Promise<TransactionObject>}
*/
redeemIssue({ issueId }, options) {
return this.__sendTx(
this.getContract().methods.redeemIssue(issueId),
options,
);
}
/**
* open Issue
* @param {Object} params
* @param {number} params.issueID
* @param {number} params.tokenAmount
* @param {address} params.address
* @return {Promise<TransactionObject>}
*/
updateIssue({ issueID, tokenAmount }, options) {
if (tokenAmount < 0) {
throw new Error('Tokens Amount has to be higher than 0');
}
return this.__sendTx(
this.getContract()
.methods.updateIssue(
issueID,
Numbers.toSmartContractDecimals(tokenAmount, this.getTransactionTokenContract().getDecimals()),
),
options,
);
}
/**
* Propose Merge of Issue
* @param {Object} params
* @param {number} params.issueID
* @param {Address[]} params.prAddresses
* @param {number[]} params.prAmounts
* @return {Promise<TransactionObject>}
*/
proposeIssueMerge({ issueID, prAddresses, prAmounts }, options) {
if (prAddresses.length !== prAmounts.length) {
throw new Error('prAddresses dont match prAmounts size');
}
const prAmountsWithDecimals = prAmounts.map(
p => Numbers.toSmartContractDecimals(p, this.getTransactionTokenContract().getDecimals()),
);
return this.__sendTx(
this.getContract()
.methods.proposeIssueMerge(issueID, prAddresses, prAmountsWithDecimals),
options,
);
}
/**
* close Issue
* @param {Object} params
* @param {number} params.issueID
* @param {number} params.mergeID
* @return {Promise<TransactionObject>}
*/
closeIssue({ issueID, mergeID }, options) {
return this.__sendTx(
this.getContract().methods.closeIssue(issueID, mergeID),
options,
);
}
/**
* Dispute Merge
* @param {Object} params
* @param {number} params.issueID
* @param {number} params.mergeID
* @return {Promise<TransactionObject>}
*/
disputeMerge({ issueID, mergeID }, options) {
return this.__sendTx(
this.getContract().methods.disputeMerge(issueID, mergeID),
options,
);
}
/**
* Deploys current contract and awaits for {@link TokensNetwork#__assert}
* @function
* @param {Object} params
* @param {string} params.settlerTokenAddress
* @param {string} params.transactionTokenAddress
* @param {string} params.governanceAddress
* @param {IContract~TxOptions} options
* @return {Promise<*|undefined>}
*/
deploy = async (
{ settlerTokenAddress, transactionTokenAddress, governanceAddress },
options,
) => {
const params = [ settlerTokenAddress, transactionTokenAddress, governanceAddress ];
const res = await this.__deploy(params, options);
this.params.contractAddress = res.contractAddress;
/* Call to Backend API */
await this.__assert();
return res;
};
/**
* @function
* @return ERC20Contract|null
*/
getSettlerTokenContract() {
return this.params.settlerToken;
}
/**
* @function
* @return ERC20Contract|null
*/
getTransactionTokenContract() {
return this.params.transactionalToken;
}
}
export default Network;
Source