Example of a Product

A Tracker certificate

This product reflects an open-ended performance certificate on an exchange-traded, underlying asset such a Treasury bill. The product offers the investor a bi-monthly redemption option.

The Settlement token in this example can be any cash token such as USDC which is supported by the Verified Network. The Security token in this example is the fund distribution token which is used to parameterize the investment product. Holders of the Security token can withdraw funds from the investment product, which in this case is a Certificate.

import Web3 from 'web3';
import BigNumber from 'bignumber.js'; 
import fs from 'fs';

import { AP, APTypes } from '@verified-network/protocol';
import ADDRESS_BOOK from '@verified-network/protocol/ap-chain/addresses.json';

import CERTFTerms from './CERTFTerms.json';
import { keys, rpcURL } from './secret.json';

import SettlementTokenArtifact from '@verified-network/protocol/build/contracts/contracts/tokens/SettlementToken.sol/SettlementToken.json';

(async () => {

    const web3 = new Web3(new Web3.providers.HttpProvider(rpcURL));
    const SecurityTokenArtifact = JSON.parse(fs.readFileSync('./abi/Security.json', 'utf8'));
    const fdt = new web3.eth.Contract(SecurityTokenArtifact.abi, '0xd74109546668c7Fb97a093D9E888D6BF65eadfF0');

    // setup accounts
    keys.forEach((pk: string) => web3.eth.accounts.wallet.add(web3.eth.accounts.privateKeyToAccount(pk)));
    
    const creator = (await web3.eth.getAccounts())[5]; // creator of the asset
    const manager = (await web3.eth.getAccounts())[6]; // manager of the asset
    const anyone = (await web3.eth.getAccounts())[2]; // used to make calls that could be made by any address
    const holder = (await web3.eth.getAccounts())[3]; // future holder of FDTs created by asset manager

    // initialize AP with web3 object and addressBook
    const ap = await AP.init(web3, ADDRESS_BOOK);

    // Deploy Settlement Token
    // @ts-ignore
    const settlementToken = await (new web3.eth.Contract(SettlementTokenArtifact.abi)).deploy(
        { data: SettlementTokenArtifact.bytecode }
    ).send({ from: creator, gas: 2000000 });


    // create the term sheet by setting the currency to the Settlement Token contract we just deployed
    const terms = { ...CERTFTerms };

    // set up ownership
    const ownership = {
        creatorObligor: manager, // account which has to fulfill investor obligations (such as the initial exchange)
        creatorBeneficiary: manager, // account which receives positive cashflows for the investor (such as redemptions)
        counterpartyObligor: creator, // account which has to fulfill issuer obligations (such as paying redemptions)
        counterpartyBeneficiary: creator, //account which receives positive cashflows for the issuer (such as the principal)
    };

    // create new CERTF asset
    const initializeAssetTx = await ap.contracts.certfActor.methods.initialize(
        terms, 
        [], // optionally pass custom schedule, 
        ownership, 
        ap.contracts.certfEngine.options.address, 
        ap.utils.constants.ZERO_ADDRESS,
        ap.utils.constants.ZERO_ADDRESS
    ).send({ from: creator, gas: 2000000 });

    // retrieve the AssetId from the transaction event logs
    const assetId = initializeAssetTx.events.InitializedAsset.returnValues.assetId;
    console.log('AssetId: ' + assetId);

    // get asset terms
    console.log('Terms: ', ap.utils.conversion.parseWeb3Response<APTypes.UTerms>(
        await ap.contracts.certfRegistry.methods.getTerms(assetId).call())
    );

    // get asset state
    console.log('State: ', ap.utils.conversion.parseWeb3Response<APTypes.UState>(
        await ap.contracts.certfRegistry.methods.getState(assetId).call())
    );

    // get next scheduled event for the asset from the CERTF Registry and decode it
    const nextScheduledEvent = await ap.contracts.certfRegistry.methods.getNextScheduledEvent(assetId).call();
    const decodedEvent = ap.utils.schedule.decodeEvent(nextScheduledEvent);
    // in our case that the IED (Initial Exchange event for transferring the principal from the investor to the issuer)
    console.log('Next scheduled event: ', decodedEvent);
    
    //compute pay off using engine
    const payoff = 
        await ap.contracts.certfEngine.methods.computePayoffForEvent(
            await ap.contracts.certfRegistry.methods.getTerms(assetId).call(),
            await ap.contracts.certfRegistry.methods.getState(assetId).call(),
            nextScheduledEvent,
            web3.eth.abi.encodeParameter('uint256', decodedEvent.scheduleTime)
        ).call();
      
    console.log("Pay off: ", payoff);

    // approve actor to execute settlement payment (must be called before progressing the asset)
    // manager has to give allowance to the actor to transfer the principal to the issuer
    await ap.contracts.erc20(terms.currency).methods.approve(
        ap.contracts.certfActor.options.address,
        payoff
    ).send({ from: manager, gas: 2000000 });

    // progress the asset - can be called by any account
    await ap.contracts.certfActor.methods.progress(assetId).send({ from: manager, gas: 2000000 });

    const isEventSettled = await ap.contracts.certfRegistry.methods.isEventSettled(
        web3.utils.toHex(assetId), nextScheduledEvent
    ).call();

    console.log("Is event settled: ", isEventSettled);

    const projectedNextState = 
        await ap.contracts.certfEngine.methods.computeStateForEvent(
            await ap.contracts.certfRegistry.methods.getTerms(assetId).call(),
            await ap.contracts.certfRegistry.methods.getState(assetId).call(),
            nextScheduledEvent,
            web3.utils.toHex(0)
        ).call();
    console.log("Projected next state: ", projectedNextState);
    
    // creator sells 50% of his FDTs to a third party
    await fdt.methods.transfer(
        holder,
        new BigNumber(await fdt.methods.balanceOf(manager).call()).dividedBy(2).toString()
    ).send({ from: manager, gas: 1000000 });

    // set FDT contract as new beneficiary for asset
    // in our case the FDT will receive and distribute all future payments
    await ap.contracts.certfRegistry.methods.setCreatorBeneficiary(
        assetId,
        fdt.options.address
    ).send({ from: manager, gas: 2000000 });
    
    // approve actor to execute settlement payment
    // debtor has to give allowance to the Actor to transfer the first payment
    await settlementToken.methods.approve(
        ap.contracts.certfActor.options.address,
        '25479452054794518000'
    ).send({ from: creator, gas: 2000000 });
    
    // update internal balances in the FDT (can be called by any account)
    await fdt.methods.updateFundsReceived().send({ from: holder, gas: 2000000 })

    // check withdrawable funds for fractional owner after calling updateFundsReceived
    const withdrawableAmount = await fdt.methods.withdrawableFundsOf(holder).call()
    // Holder received 50% of the first interst payment
    console.log('Withdrawable Balance of Holder: ' + withdrawableAmount.toString());
    
})();

The Certificate's terms that need to be specified by an application user is in the following format.

{
  "contractType": "CERTF",
  "calendar": "MF",
  "contractRole": "BUY",
  "dayCountConvention": "AA",
  "businessDayConvention": "CSF",
  "endOfMonthConvention": "EOM",
  "couponType": "NOC",
  "currency": "0x1c36690810ad06fb15552657c7a8ff653eb46f76",
  "settlementCurrency": null,
  "issueDate": "2020-06-18T00:00:00.000Z",
  "statusDate": "2020-06-18T00:00:00.000Z",
  "initialExchangeDate": null,
  "maturityDate": null,
  "cycleAnchorDateOfRedemption": "2020-07-18T00:00:00.000Z",
  "cycleAnchorDateOfTermination": null,
  "cycleAnchorDateOfCoupon": null,
  "nominalPrice": "1000",
  "issuePrice": "987.14",
  "quantity": "1000",
  "denominationRatio": "1",
  "couponRate": "0",
  "gracePeriod": "10D",
  "delinquencyPeriod": "90D",
  "settlementPeriod": "2D",
  "fixingPeriod": "0D",
  "redemptionRecordPeriod": "1D",
  "cycleOfRedemption": "1M-",
  "cycleOfTermination": "0D-",
  "cycleOfCoupon": "0D-",
  "contractStructure": {
    "contractReference": [
      {
        "object": "0x43483232337276325f5246445f5241",
        "object2": "0x756e646566696e6564",
        "type": "MOC",
        "role": "UDL"
      },
      {
        "object": "0x43483232337276325f5246445f5154",
        "object2": "0x756e646566696e6564",
        "type": "MOC",
        "role": "UDL"
      }
    ]
  }
}

Last updated