"use strict" ; import BigNumber from "bignumber.js" ; import { AvatarService } from "./avatarService" ; import { Address , fnVoid , Hash } from "./commonTypes" ; import { DecodedLogEntryEvent , IContractWrapper } from "./iContractWrapperBase" ; import { TransactionService , TxGeneratingFunctionOptions } from "./transactionService" ; import { Utils } from "./utils" ; import { EntityFetcherFactory , EventFetcherFilterObject , Web3EventService } from "./web3EventService" ; import { DaoCreatorFactory , DaoCreatorWrapper } from "./wrappers/daoCreator" ; import { ForgeOrgConfig , InitialSchemesSetEventResult , SchemesConfig } from "./wrappers/daoCreator" ; import { DaoTokenWrapper } from "./wrappers/daoToken" ; import { ReputationMintEventResult , ReputationWrapper } from "./wrappers/reputation" ; import { WrapperService } from "./wrapperService" ; /**
 * Helper class and factory for DAOs.
 */ export class DAO { param { NewDaoConfig } options public static async new ( options ) { let daoCreator ; if ( options . daoCreatorAddress ) { daoCreator = await DaoCreatorFactory . at ( options . daoCreatorAddress ) ; } else { daoCreator = WrapperService . wrappers . DaoCreator ; } const payload = TransactionService . publishKickoffEvent ( "DAO.new" , options , daoCreator . forgeOrgTransactionsCount ( options ) + daoCreator . setSchemesTransactionsCount ( options ) ) ; /**
         * resend sub-events as Dao.new
         */ const eventContext = TransactionService . newTxEventContext ( "DAO.new" , payload , options ) ; options . txEventContext = eventContext ; const result = await ( await daoCreator . forgeOrg ( options ) ) . watchForTxMined ( ) ; const avatarAddress = TransactionService . getValueFromLogs ( result , "_avatar" , "NewOrg" ) ; if ( ! avatarAddress ) { throw new Error ( "avatar address is not defined" ) ; } options . txEventContext = eventContext ; await ( await daoCreator . setSchemes ( Object . assign ( { avatar : avatarAddress } , options ) ) ) . watchForTxMined ( ) ; return DAO . at ( avatarAddress ) ; } param avatarAddress public static async at ( avatarAddress ) { let dao ; const avatarService = new AvatarService ( avatarAddress ) ; const avatar = await avatarService . getAvatar ( ) ; if ( avatar ) { dao = new DAO ( ) ; dao . avatar = await avatarService . getAvatar ( ) ; dao . controller = await avatarService . getController ( ) ; dao . hasUController = await avatarService . getIsUController ( ) ; dao . token = await avatarService . getNativeToken ( ) ; dao . reputation = await avatarService . getNativeReputation ( ) ; dao . name = await dao . avatar . orgName ( ) ; } return dao ; } param options public static async getDaos ( options = { } ) { const daoEventFetcherFactory = await DAO . getDaoCreationEvents ( options ) ; return daoEventFetcherFactory ( { } , { fromBlock : 0 } ) . get ( ) ; } param options public static async getDaoCreationEvents ( options = { } ) { const web3EventService = new Web3EventService ( ) ; const daoCreator = options . daoCreatorAddress ? await WrapperService . factories . DaoCreator . at ( options . daoCreatorAddress ) : WrapperService . wrappers . DaoCreator ; return web3EventService . createEntityFetcherFactory ( daoCreator . InitialSchemesSet , async ( event ) : Promise < Address > => { return Promise . resolve ( event . args . _avatar ) ; } ) ; } /**
     * Truffle contract wrapper for the DAO's Avatar
     */ public avatar : any ; /**
     * Truffle contract wrapper for the DAO's controller (Controller or UController by default, see DAO.hasUController)
     */ public controller : any ; /**
     * `true` if the DAO is using Arc's universal controller
     */ public hasUController : boolean ; /**
     * Truffle contract wrapper for the DAO's native token (DAOToken by default)
     */ public token : DaoTokenWrapper ; /**
     * Truffle contract wrapper for the DAO's native reputation (Reputation)
     */ public reputation : ReputationWrapper ; /**
     * the name of the DAO
     */ public name : string ; param name public async getSchemes ( name ? ) { const schemes = await this . _getSchemes ( ) ; if ( name ) { return schemes . filter ( ( s ) => s . wrapper && s . wrapper . name && ( s . wrapper . name === name ) ) ; } else { return schemes ; } } param name public async getGlobalConstraints ( name ? ) { const constraints = await this . _getConstraints ( ) ; if ( name ) { return constraints . filter ( ( s ) => s . wrapper . name && ( s . wrapper . name === name ) ) ; } else { return constraints ; } } param options public async getParticipants ( options = { } as GetParticipantsOptions ) { const addresses = new Set < Address > ( ) ; let participants ; const fetcher = this . reputation . Mint ( options . participantAddress ? { _to : options . participantAddress } : undefined , Object . assign ( { fromBlock : 0 } , options ) ) ; const events = await fetcher . get ( ) ; events . forEach ( async ( event ) : Promise < void > => { addresses . add ( event . args . _to ) ; } ) ; if ( options . returnReputations ) { participants = new Array < Participant > ( ) ; for ( const account of addresses ) { const balance = await this . reputation . getBalanceOf ( account ) ; if ( balance . gt ( 0 ) ) { participants . push ( { address : account , reputation : balance } ) ; } } } else { participants = Array . from ( addresses . values ( ) ) . map ( ( address ) : Participant => ( { address } ) ) ; } return participants ; } /**
     * Returns whether the scheme with the given address is registered to this DAO's controller
     */ public async isSchemeRegistered ( schemeAddress ) { return await this . controller . isSchemeRegistered ( schemeAddress , this . avatar . address ) ; } /**
     * Returns whether the global constraint with the given address is registered to this DAO's controller
     */ public async isGlobalConstraintRegistered ( gc ) { return await this . controller . isGlobalConstraintRegistered ( gc , this . avatar . address ) ; } param agentAddress public async getTokenBalance ( agentAddress ) { return Utils . getTokenBalance ( agentAddress , this . token . address ) ; } return { Promise < string > } public async getTokenName ( ) { return await this . token . getTokenName ( ) ; } return { Promise < string > } public async getTokenSymbol ( ) { return await this . token . getTokenSymbol ( ) ; } /**
     * Returns promise of schemes currently in this DAO as Array<DaoSchemeInfo>
     */ private async _getSchemes ( ) { const foundSchemes = new Map < string , DaoSchemeInfo > ( ) ; const controller = this . controller ; const avatar = this . avatar ; const registerSchemeEvent = controller . RegisterScheme ( { } , { fromBlock : 0 , toBlock : "latest" } ) ; await new Promise ( ( resolve , reject ) : void => { registerSchemeEvent . get ( ( err , log ) => { if ( err ) { return reject ( err ) ; } this . _handleSchemeEvent ( log , foundSchemes ) . then ( ( ) : void => { resolve ( ) ; } ) ; } ) ; } ) ; const registeredSchemes = [ ] ; for ( const scheme of foundSchemes . values ( ) ) { if ( await controller . isSchemeRegistered ( scheme . address , avatar . address ) ) { registeredSchemes . push ( scheme ) ; } } return registeredSchemes ; } private async _handleSchemeEvent ( log , schemesMap ) { if ( ! Array . isArray ( log ) ) { log = [ log ] ; } const count = log . length ; for ( let i = 0 ; i < count ; i ++ ) { const address = log [ i ] . args . _scheme ; const wrapper = WrapperService . wrappersByAddress . get ( address ) ; const schemeInfo = { address , wrapper , } ; schemesMap . set ( address , schemeInfo ) ; } } /**
     * Returns promise of global constraints currently in this DAO, as DaoGlobalConstraintInfo
     */ private async _getConstraints ( ) { const foundConstraints = new Map < string , DaoGlobalConstraintInfo > ( ) ; const controller = this . controller ; const event = controller . AddGlobalConstraint ( { } , { fromBlock : 0 , toBlock : "latest" } ) ; await new Promise ( ( resolve , reject ) : void => { event . get ( ( err , log ) => { if ( err ) { return reject ( err ) ; } this . _handleConstraintEvent ( log , foundConstraints ) . then ( ( ) => { resolve ( ) ; } ) ; } ) ; } ) ; const registeredConstraints = [ ] ; for ( const gc of foundConstraints . values ( ) ) { if ( await this . isGlobalConstraintRegistered ( gc . address ) ) { registeredConstraints . push ( gc ) ; } } return registeredConstraints ; } private async _handleConstraintEvent ( log , constraintsMap ) { if ( ! Array . isArray ( log ) ) { log = [ log ] ; } const count = log . length ; for ( let i = 0 ; i < count ; i ++ ) { const address = log [ i ] . args . _globalConstraint ; const paramsHash = log [ i ] . args . _params ; const wrapper = WrapperService . wrappersByAddress . get ( address ) ; const info = { address , paramsHash , wrapper , } ; constraintsMap . set ( address , info ) ; } } } export interface NewDaoConfig extends ForgeOrgConfig , SchemesConfig { /**
     * Address of a DaoCreator to use.  Default is the Arc DaoCreator supplied in this release of Arc.js.
     * If given, the current Arc.js wrapper class must be compatible with Arc contract at the given address.
     */ daoCreatorAddress ? : Address ; } /**
 * Returned from DAO.getSchemes
 */ export interface DaoSchemeInfo { /**
     * Scheme address
     */ address : string ; /**
     * Wrapper class for the scheme if it was deployed by the running version of Arc.js
     */ wrapper ? : IContractWrapper ; } /********************************
 * Returned from DAO.getGlobalConstraints
 */ export interface DaoGlobalConstraintInfo { /**
     * Global constraint address
     */ address : string ; /**
     * hash of the constraint parameters
     */ paramsHash : string ; /**
     * Wrapper class for the constraint if it was deployed by the running version of Arc.js
     */ wrapper : IContractWrapper ; } export interface ControllerAddGlobalConstraintsEventLogEntry { _globalConstraint : Address ; _params : Hash ; } export interface ControllerRegisterSchemeEventLogEntry { _scheme : Address ; } export type PerDaoCallback = ( avatarAddress : Address ) => void | Promise < boolean > ; export interface GetDaosOptions { daoCreatorAddress ? : Address ; } export interface GetParticipantsOptions extends EventFetcherFilterObject { /**
     * optional address of a single participant to filter on
     */ participantAddress ? : Address ; /**
     * true to retrieve and return the current reputation of each participant
     */ returnReputations ? : boolean ; } export interface Participant { address : Address ; reputation ? : BigNumber ; }