NAV Navbar
  • Dai.js
  • Getting Started
  • Maker
  • CDP
  • Price Service
  • ETH CDP Service
  • Exchange Service
  • System Status
  • Events
  • Transactions
  • Using Multiple Accounts
  • Using DSProxy
  • Developing
  • Adding a New Service
  • Dai.js

    tests coverage

    Dai.js is a JavaScript library that makes it easy to build applications on top of MakerDAO's platform of smart contracts. You can use Maker's contracts to open Collateralized Debt Positions (CDPs), withdraw loans in Dai, trade tokens on OasisDEX, and more.

    The library features a pluggable, service-based architecture, which allows users maximal control when integrating the Maker functionality into existing infrastructures. It also includes convenient configuration presets for out-of-the-box usability and support for both front-end and back-end applications.

    Maker's entire suite of contracts will eventually be accessible through this library—including the DAO governance and the upcoming multi-collateral release—but functionality is limited in the current alpha version to the following areas:

    For example code that consumes the library, check out this repository.

    Getting Started


    import Maker from '@makerdao/dai';
    // or:
    const Maker = require('@makerdao/dai');

    Install the package with npm:

    npm install @makerdao/dai

    Once it's installed, import the module into your project as shown on the right.


    <script src="./dai.js" />
        privateKey: YOUR_PRIVATE_KEY,
        url: ''
        .then(maker => { window.maker = maker })
        .then(() => maker.authenticate())
      .then(() => maker.openCdp())
      .then(cdp => console.log(;

    This library is also accessible as a UMD module.

    Quick Example

    import Maker from '@makerdao/dai';
    async function openLockDraw() {
        const maker = await Maker.create("http", {
            privateKey: YOUR_PRIVATE_KEY,
            url: ''
      await maker.authenticate();
      const cdp = await maker.openCdp();
      await cdp.lockEth(0.25);
      await cdp.drawDai(50);
      const debt = await cdp.getDebtValue();
      console.log(debt.toString); // '50.00 DAI'

    Using the Maker and Cdp APIs, the code shown on the right opens a cdp, locks 0.25 ETH into it, draws out 50 Dai, and then logs information about the newly opened position to the console.



    const makerBrowser = await Maker.create('browser');
    const makerHttp = await Maker.create('http', {
      privateKey: YOUR_PRIVATE_KEY,
      url: ''
    const makerTest = await Maker.create('test');

    When instantiating a Maker object, you pass in the name of a configuration preset and an options hash.

    Available presets are listed below, with the required options for each shown on the right.


    const maker = await Maker.create('http', {
      privateKey: YOUR_PRIVATE_KEY, // '0xabc...'
      url: '',
      provider: {
        type: 'HTTP', // or 'TEST'
        network: 'kovan'
      web3: {
        statusTimerDelay: 2000,
        confirmedBlockCount: 8
        transactionSettings: {
          gasPrice: 12000000000
      log: false


    await maker.authenticate();

    After creating your Maker instance, and before using any other methods, run this. It makes sure all services are initialized, connected to any remote API's, and properly authenticated.


    const priceService = maker.service('price');

    The service function can be used to access services that were injected into your instance of maker. See the service documentation sections below.


    const newCdp = await maker.openCdp();

    maker.openCdp() will create a new CDP, and then return the CDP object, which can be used to access other CDP functionality.

    The promise will resolve when the transaction is mined.


    const cdp = await maker.getCdp(614);

    maker.getCdp(id) creates a CDP object for an existing CDP. The CDP object can then be used to interact with your CDP.


    import Maker from '@makerdao/dai';
    const {
    } = Maker;
    // These are all identical:
    // each method has a default type
    // you can pass in a currency unit instance
    // you can pass the unit as an options argument
    cdp.lockEth(0.25, { unit: ETH });
    cdp.lockEth(250000000000000000, { unit: ETH.wei });
    const eth = ETH(5);
    eth.toString() == '5.00 ETH';
    const price = USD_ETH(500);
    price.toString() == '500.00 USD/ETH';
    // multiplication handles units
    const usd = eth.times(price);
    usd.toString() == '2500.00 USD';
    // division does too
    const eth2 = usd.div(eth);

    Methods that take numerical values as input can also take instances of token classes that the library provides. These are useful for managing precision, keeping track of units, and passing in wei values.

    Most methods that return numerical values return them wrapped in one of these classes.

    There are two types:

    The classes that begin with USD are price units; e.g. USD_ETH represents the price of ETH in USD.

    Useful instance methods:


    Now that you've used maker to either open a new CDP or retrieve an existing one, you can use the returned cdp object to call functions on it.



    This is the ID of the CDP object. You can pass this ID to Maker.getCdp.


    const daiDebt = await cdp.getDebtValue();
    const usdDebt = await cdp.getDebtValue(Maker.USD);

    cdp.getDebtValue() returns the amount of debt that has been borrowed against the collateral in the CDP. By default it returns the amount of Dai as a currency unit, but can return the equivalent in USD if the first argument is Maker.USD.


    const mkrFee = await cdp.getGovernanceFee();
    const usdFee = await cdp.getGovernanceFee(Maker.USD);

    cdp.getGovernanceFee() returns the value of the accrued governance fee. By default it returns the amount of MKR as a currency unit, but can return the equivalent in USD if the first argument is Maker.USD.

    Note: this is often referred to as the Stability Fee, even though technically the Stability Fee is the fee that is paid in Dai, and the Governance Fee is the fee that is paid in MKR. But since fees are only paid in MKR in Single-Collateral Dai, and only paid in Dai in Multi-Collateral Dai, the fee in Single-Collateral Dai is often referred to as the Stability Fee to be consistent with the term that will be used in Multi-Collateral Dai and to avoid unduly confusing regular users.


    const ratio = await cdp.getCollateralizationRatio();

    cdp.getCollateralizationRatio() returns the USD value of the collateral in the CDP divided by the USD value of the Dai debt for the CDP, e.g. 2.5.


    const ratio = await cdp.getLiquidationPrice();

    cdp.getLiquidationPrice() returns the price of Ether in USD that causes the CDP to become unsafe (able to be liquidated), all other factors constant. It returns a USD_ETH price unit.


    const ethCollateral = await cdp.getCollateralValue();
    const pethCollateral = await cdp.getCollateralValue(Maker.PETH);
    const usdCollateral = await cdp.getCollateralValue(Maker.USD);

    cdp.getCollateralValue() returns the value of the collateral in the CDP. By default it returns the amount of ETH as a currency unit, but can return the equivalent in PETH or USD depending on the first argument.


    const ratio = await cdp.isSafe();

    cdp.isSafe() returns true if the cdp is safe, that is, if the USD value of its collateral is greater than or equal to USD value of the its debt multiplied by the liquidation ratio.


    const enoughMkrToWipe = await cdp.enoughMkrToWipe(10000000000000000000, DAI.wei);

    cdp.enoughMkrToWipe(dai) returns true if the current account owns enough MKR to wipe the specified amount of Dai from the CDP.


    return await cdp.lockEth(10000000000000000000, ETH.wei);
    // or equivalently
    return await cdp.lockEth(100, ETH);

    cdp.lockEth(eth) abstracts the token conversions needed to lock collateral in a CDP. It first converts the ETH to WETH, then converts the WETH to PETH, then locks the PETH in the CDP.

    Note: this process is not atomic, so it's possible for some of the transactions to succeed but not all three. See Using DsProxy for executing multiple transactions atomically.


    return await cdp.drawDai(10000000000000000000, DAI.wei);
    // or equivalently
    return await cdp.drawDai(100, DAI);

    cdp.drawDai(dai) withdraws the specified amount of Dai as a loan against the collateral in the CDP. As such, it will fail if the CDP doesn't have enough PETH locked in it to remain at least 150% collateralized.


    return await cdp.wipeDai(10000000000000000000, DAI.wei);
    // or equivalently
    return await cdp.wipeDai(100, DAI);

    cdp.wipeDai(dai) sends Dai back to the CDP in order to repay some (or all) of its outstanding debt.

    Note: CDPs accumulate MKR governance debt over their lifetime. This must be paid when wiping dai debt, and thus MKR must be acquired before calling this method.


    return await cdp.freePeth(100, PETH);
    // or equivalently
    return await cdp.freePeth(10000000000000000000, PETH.wei);

    cdp.freePeth(peth) withdraws the specified amount of PETH and returns it to the owner's address. As such, the contract will only allow you to free PETH that's locked in excess of 150% of the CDP's outstanding debt.


    return await cdp.give('0x046ce6b8ecb159645d3a605051ee37ba93b6efcc');

    cdp.give(address) transfers ownership of the CDP from the current owner to the address you provide as an argument.


    return await cdp.shut();

    cdp.shut() wipes all remaining dai, frees all remaining collateral, and deletes the CDP. This will fail if the caller does not have enough DAI and MKR to wipe all debt.


    return await cdp.bite();

    cdp.bite() will initiate the liquidation process of an undercollateralized CDP

    Price Service

    const price = maker.service('price');

    Retrieve the PriceService through Maker.service('price').

    The PriceService exposes the collateral and governance tokens' price information that is reported by the oracles in the Maker system.


    const ethPrice = await price.getEthPrice();

    Get the current USD price of ETH, as a USD_ETH price unit.


    const mkrPrice = await price.getMkrPrice();

    Get the current USD price of the governance token MKR, as a USD_MKR price unit.


    await pethPrice = price.getPethPrice();

    Get the current USD price of PETH (pooled ethereum), as a USD_PETH price unit.


    await price.setEthPrice(475);

    Set the current USD price of ETH.

    This requires the necessary permissions and will only be useful in a testing environment.


    await price.setMkrPrice(950.00);

    Set the current USD price of the governance token MKR.

    This requires the necessary permissions and will only be useful in a testing environment.


    await price.getWethToPethRatio();

    Returns the current W-ETH to PETH ratio.

    ETH CDP Service

    const ethCdp = maker.service('cdp');

    Retrieve the ETH CDP Service through Maker.service('cdp').

    The ETH CDP Service exposes the risk parameter information for the Ether CDP type (in single-collateral Dai, this is the only CDP Type)


    const ratio = await ethCdp.getLiquidationRatio();

    getLiquidationRatio() returns a decimal representation of the liquidation ratio, e.g. 1.5


    const penalty = await ethCdp.getLiquidationPenalty();

    getLiquidationPenalty() returns a decimal representation of the liquidation penalty, e.g. 0.13


    const fee = await ethCdp.getAnnualGovernanceFee();

    getAnnualGovernanceFee() returns a decimal representation of the annual governance fee, e.g. 0.005.

    Note: this is often referred to as the Stability Fee, even though technically the Stability Fee is the fee that is paid in Dai, and the Governance Fee is the fee that is paid in MKR. But since fees are only paid in MKR in Single-Collateral Dai, and only paid in Dai in Multi-Collateral Dai, the fee in Single-Collateral Dai is often referred to as the Stability Fee to be consistent with the term that will be used in Multi-Collateral Dai and to avoid unduly confusing regular users.

    Exchange Service

    const exchange = maker.service('exchange');

    Retrieve the OasisExchangeService (or alternative implementation) through Maker.service('exchange').

    The exchange service allows to buy and sell DAI, MKR, and other tokens. The default OasisExchangeService implementation uses the OasisDEX OTC market for this.


    // Sell 100.00 DAI for 0.30 WETH or more.
    const sellOrder = await exchange.sellDai('100.0', 'WETH', '0.30');

    Sell a set amount of DAI and receive another token in return.


    // Buy 100.00 DAI for 0.30 WETH or less.
    const buyOrder = await exchange.buyDai('100.0', 'WETH', '0.35');

    Buy a set amount of DAI and give another token in return.


    const buyOrder = await exchange.buyDai('100.0', 'WETH', '0.35');
    const fillAmount = buyOrder.fillAmount();
    const gasPaid = buyOrder.fees();
    const created = buyOrder.created();

    OasisOrders have a few methods: * fillAmount: amount of token received in exchange * fees(): amount of ether spent on gas * created(): timestamp of when transaction was mined

    System Status

    const ethCdp = maker.service('cdp');

    To access system status information, retrieve the ETH CDP Service through Maker.service('cdp').


    const systemRatio = await ethCdp.getSystemCollateralization();

    getSystemCollateralization() returns the collateralization ratio for the entire system, e.g. 2.75


    const targetPrice = await ethCdp.getTargetPrice();

    getTargetPrice() returns the target price of Dai in USD, that is, the value to which Dai is soft-pegged, e.g. 1.0


    The event pipeline allows developers to easily create real-time applications by letting them listen for important state changes and lifecycle events.


    An event name passed to any event emitter method can contain a wildcard (the * character). A wildcard may appear as foo/*, foo/bar/*, or simply *.

    * matches one sub-level.

    e.g. price/* will trigger on both price/USD_ETH and price/MKR_USD but not price/MKR_USD/foo.

    ** matches all sub-levels.

    e.g. price/** will trigger on price/USD_ETH, price/MKR_USD, and price/MKR_USD/foo.

    Event Object

        type: <event_type>,
        payload: <event_payload>, /* if applicable */
        index: <event_sequence_number>,
        block: <latest_block_when_emitted>

    Triggered events will receive the object shown on the right.

    Maker Object


    maker.on('price/ETH_USD', eventObj => {
        const { price } = eventObj.payload;
        console.log('ETH price changed to', price);
    Event Name Payload
    price/ETH_USD { price }
    price/MKR_USD { price }
    price/WETH_PETH { ratio }


    maker.on('web3/AUTHENTICATED', eventObj => {
        const { account } = eventObj.payload;
        console.log('web3 authenticated with account', account);
    Event Name Payload
    web3/INITIALIZED { provider: { type, url } }
    web3/CONNECTED { api, network, node }
    web3/AUTHENTICATED { account }
    web3/DISCONNECTED { }

    CDP Object

    cdp.on('DEBT', eventObj => {
        const { dai } = eventObj.payload;
        console.log('Your cdp now has a dai debt of', dai);
    Event Name Payload
    DEBT { dai }


    TransactionObject Lifecycle Hooks

    const txMgr = maker.service('transactionManager');
    // instance of transactionManager
    const open = maker.service('cdp').openCdp();
    // open is a transactionObject when promise resolves

    The transactionManager service is used to access and monitor TransactionObjects which can be used to track a transaction's status as it propagates through the blockchain. TransactionObjects have methods which trigger callbacks in the event of a transaction's status being pending, mined, confirmed or error.


    txMgr.listen(open, {
      pending: tx => {
        // do something when tx is pending
      mined: tx => {
        // do something when tx is mined
      confirmed: tx => {
        // do something when tx is confirmed       
      error: tx => {
        // do someting when tx fails
    await txMgr.confirm(open); // confirmed will fire after 5 blocks if default

    Accessing these status callbacks is done through the listen function from the transactionManager service. The listen function takes a promise and a callback object with the transaction status as a key.

    One caveat is that the confirmed event will not fire unless the transactionManager confirms the promise. This confirm() function waits on a number of blocks after the transaction has been mined, of which the default is 5, to resolve. To change this, the confirmedBlockCount attribute in the options object can be modified.

    Transaction Metadata

    const lock = cdp.lockEth(1);
    txMgr.listen(lock, {
      pending: tx => {
        const {contract, method} = tx.metadata;
        if(contract === 'WETH' && method === 'deposit') {
          console.log(tx.hash); // print hash for WETH.deposit

    There are functions such as lockEth() which are composed of several internal transactions. These can be more accurately tracked by accessing the tx.metadata in the callback which contains both the contract and the method the internal transactions were created from.

    TransactionObject Methods

    A TransactionObject also has a few methods to provide further details on the transaction:

    Using Multiple Accounts

    const maker = await Maker.create({
      url: 'http://localhost:2000',
      accounts: {
        other: {type: privateKey, key: someOtherKey},
        default: {type: privateKey, key: myKey}
    await maker.authenticate();
    await maker.addAccount('yetAnother', {type: privateKey, key: thirdKey});
    const cdp1 = await maker.openCdp(); // owned by "default"
    const cdp2 = await maker.openCdp(); // owned by "other"
    const cdp3 = await maker.openCdp(); // owned by "yetAnother"
    await maker.addAccount({type: privateKey, key: fourthAccount.key}); // the name argument is optional
    const cdp4 = await maker.openCdp(); //owned by the fourth account

    The library supports the use of multiple accounts (i.e. private keys) within a single Maker instance. Accounts can be specified in the constructor options or with the addAccount method.

    Call useAccount to switch to using an account by its name, or useAccountWithAddress to switch to using an account by its address, and subsequent calls will use that account as the transaction signer.

    When the Maker instance is first created, it will use the account named default if it exists, or the first account in the list otherwise.

    Account types

    const maker = await Maker.create({
      url: 'http://localhost:2000',
      accounts: {
        // this will be the first account from the provider at
        // localhost:2000
        first: {type: 'provider'},
        // this will be the current account in MetaMask, and it
        // will send its transactions through MetaMask
        second: {type: 'browser'},
        // this account will send its transactions through the
        // provider at localhost:2000
        third: {type: 'privateKey', key: myPrivateKey}

    In addition to the privateKey account type, there are two other built-in types:


    import TrezorPlugin from '@makerdao/dai-plugin-trezor-web';
    import LedgerPlugin from '@makerdao/dai-plugin-ledger-web';
    const maker = await Maker.create({
      plugins: [
      accounts: {
        // default derivation path is "44'/60'/0'/0/0"
        myTrezor: {type: 'trezor', path: derivationPath1},
        myLedger: {type: 'ledger', path: derivationPath2}

    Plugins can add additional account types. There are currently two such plugins for hardware wallet support:


    Install the multiple accounts demo app to see this functionality in action.

    Using DSProxy

    The DSProxyService includes all the functionality necessary for interacting with both types of proxies found in Maker products: profile proxies and forwarding proxies.

    Forwarding proxies are simple contracts that aggregate function calls in the body of a single method. These are used in the CDP Portal and Oasis Direct in order to allow users to execute multiple transactions atomically, which is both safer and more user-friendly than implementing several steps as discrete transactions.

    // Forwarding proxy
    function lockAndDraw(address tub_, bytes32 cup, uint wad) public payable {
      lock(tub_, cup);
      draw(tub_, cup, wad);

    Forwarding proxies are meant to be as simple as possible, so they lack some features that could be important if they are to be used as interfaces for more complex smart contract logic. This problem can be solved by using profile proxies (i.e., copies of DSProxy) to execute the functionality defined in the forwarding proxies.

    The first time an account is used to interact with any Maker application, the user will be prompted to deploy a profile proxy. This copy of DSProxy can be used in any product, including dai.js, by way of a universal proxy registry. Then, the calldata from any function in the forwarding proxy can be passed to DSProxy's execute() method, which runs the provided code in the context of the profile proxy.

    // Calling the forwarding proxy with dai.js
    function lockAndDraw(tubContractAddress, cdpId, daiAmount, ethAmount) {
      const saiProxy = maker.service('smartContract').getContractByName('SAI_PROXY');
      return saiProxy.lockAndDraw(
          value: ethAmount,
          dsProxy: true

    This makes it possible for users' token allowances to persist from one Maker application to another, and it allows users to recover any funds mistakenly sent to the proxy's address.

    Many of the functions in DSProxyService will only be relevant to power users. All that is strictly required to automatically generate a function's calldata and find the correct profile proxy is the inclusion of { dsProxy: true } in the options object for any transaction — provided the user has already deployed a profile proxy. If that's not certain, it may also be necessary to query the registry to determine if a user already owns a proxy, and to build one if they do not.


    function getProxy() {
      return maker.service('proxy').currentProxy();

    If the currentAccount (according the Web3Service) has already deployed a DSProxy, currentProxy() returns its address. If not, it returns null. It will update automatically in the event that the active account is changed.

    This function should be used to check whether a user has a proxy before attempting to build one.


    async function buildProxy() {
      const proxyService = maker.service('proxy');
      if (!proxyService.currentProxy()) {
        return await;

    build will deploy a copy of DSProxy owned by the current account. This transaction will revert if the current account already owns a profile proxy.

    By default, build() returns after the transaction is mined.


    function lockAndDraw(tubContractAddress, cdpId, daiAmount, ethAmount) {
      const saiProxy = maker.service('smartContract').getContractByName('SAI_PROXY');
      return saiProxy.lockAndDraw(
          value: ethAmount,
          dsProxy: true

    This function is designed to be called by the TransactionManager, not accessed directly. As long as the target contract is defined by the SmartContractService, the inclusion of the dsProxy key in the options for any transaction will automatically forward the transaction to the DSProxyService.

    The value of dsProxy can either be true or an explicitly provided DSProxy address.


    const proxy = maker.service('proxy').getProxyAddress('0x...');

    getProxyAddress will query the proxy registry for the profile proxy address associated with a given account. If no address is provided as a parameter, the function will return the address of the proxy owned by the currentAccount.


    const owner = maker.service('proxy').getOwner('0x...');

    getOwner will query the proxy registry for the owner of a provided instance of DSProxy.


    async function giveProxy(newOwner, proxyAddress) {
      return await maker.service('proxy').setOwner(newOwner, proxyAddress);

    setOwner can be used to give a profile proxy to a new owner. The address of the recipient account must be specified, but the DSProxy address will default to currentProxy if the second parameter is excluded.


    1. git clone
    2. npm install

    Running the tests

    The test suite is configured to run on a Ganache test chain.

    If you run the tests with npm test, it will start a test chain and deploy all the smart contracts for the Dai stablecoin system. This takes a few minutes.

    To keep the test chain running and re-run the tests when changes are made to the code, use the command npm run test:watch.

    If you want to deploy the contracts to a test chain without running the test suite, use npm run test:net.

    Inspect contract state

    Start the dev server using npm start, then open http://localhost:9000.


    Adding a New Service

    Note: This section is only for advanced users that are willing to modify the dai.js source code. In the future, you will be able to add custom services without needing to modify the source code.

    You can take advantage of the pluggable architecture of this library by choosing different implementations for services, and/or adding new service roles altogether. A service is just a Javascript class that inherits from either PublicService, PrivateService, or LocalService, and contains public method(s). It can depend on other services through a built-in dependency injection framework, and can also be configured through the Maker config file / config options.

    Steps to add a new service

    Here are the steps to add a new service called ExampleService to MakerJS:

    //example code in ExampleService.js for steps 3-6
    import PublicService from '../core/PublicService';
    export default class ExampleService extends PublicService {
        constructor (name='example') {
            super(name, ['log']);
    //example code in ExampleService.spec.js for step 8
    import Maker from '../../src/index';
    //step 8: a new service role ('example') is used
    test('test 1', async () => {
      const maker = await Maker.create('http', {example: "ExampleService"});
      const exampleService = customMaker.service('example');
      exampleService.test(); //logs "test"
    //step 8: a custom service replaces a default service (Web3)
    test('test 2', async () => {
      const maker = await Maker.create('http', {web3: "MyCustomWeb3Service"});
      const mycustomWeb3Service = maker.service('web3');
    //step 10: in ExampleService.spec.js
    const maker = await Maker.create('http', {
        example: ["ExampleService", {
        exampleSetting: "this is a configuration setting"
    //step 10: accessing configuration settings in ExampleService.js
    initialize(settings) {

    Service Lifecycle

    The three kinds of services mentioned in step 4 above follow the following state machine diagrams in the picture below.

    //example initialize() function in ExampleService.js
      initialize(settings) {
        this.get('log').info('ExampleService is initializing...');

    To specify what initializing, connecting and authenticating entails, implement the initialize(), connect(), and authenticate() functions in the service itself. This will be called while the service's manager brings the service to the corresponding state.

    alt text

    const maker = await Maker.create('http', {example: "ExampleService"});
    const exampleService = customMaker.service('example');
    //wait for example service and its dependencies to initialize
    await exampleService.manager().initialize();
    //wait for example service and its dependencies to connect
    await exampleService.manager().connect();
    //wait for example service and its dependencies to authenticate
    await exampleService.manager().authenticate();
    //can also use callback syntax
        /*executed after connected*/
    //wait for all services used by the maker object to authenticate

    A service will not finish initializing/connecting/authenticating until all of its dependent services have completed the same state (if applicable - for example a LocalService is considered authenticated/connected in addition to initialized, if it has finished initializing). The example code here shows how to wait for the service to be in a certain state.

    Adding Custom Events

    //in PriceService.js
          'price/ETH_USD': {
            price: () => this.getEthPrice()

    One way to add an event is to “register” a function that gets called on each new block, using the event service's registerPollEvents() function. For example, here is some code from the price service. this.getEthPrice() will be called on each new block, and if the state has changed from the last call, a price/ETH_USD event will be emitted with the payload { price: [new_price] }.

    //in Web3Service.js
    this.get('event').emit('web3/INITIALIZED', {
      provider: { ...settings.provider }

    Another way to an add an event is to manually emit an event using the event service's emit function. For example, when the Web3Service initializes, it emits an event that contains info about the provider.

    //in the constructor in the Cdp.js
    this._emitterInstance = this._cdpService.get('event').buildEmitter();
    this.on = this._emitterInstance.on;
        USD: () => this.getCollateralValueInUSD(),
        ETH: () => this.getCollateralValueInEth()
      DEBT: {
        dai: () => this.getDebtValueInDai()

    Note that calling registerPollEvents and emit() directly on the event service like in the previous two examples will register events on the "default" event emitter instance. However, you can create a new event emitter instance for your new service. For example, the CDP object defines it's own event emitter, as can be seen here, by calling the event service's buildEmitter() function.