Verified Network
  • What is the Verified Network
  • How to use it
  • Reference
    • Verified SDK
      • Using the SDK
      • Wallet and Contracts
      • Know your Customer
        • Using the KYC plugin
      • Delegated permissions
      • Investment products
        • Product lifecycle
        • Example of a Product
      • Security tokens
        • Issuing functions
      • Liquidity pools
        • Buy and Sell Orders workflow
        • Buy and Sell Order Complete workflow example
      • Secondary issues
      • Primary issues
        • Primary offer function
      • Margin traded issues
        • Offering collateral
      • Adding money to wallet
        • Paying in supported tokens
      • Issuing cash tokens
        • Issuing and Exchange functions
      • Making payments
        • Transferring cash tokens
      • Withdrawals
        • Redeeming cash tokens
      • Custody of Assets
        • Custody functions
      • Staking to invest
        • Liquidity functions
        • Market maker functions
      • Staking to borrow
        • Bond issuing function
      • Lending
        • Bond purchase function
      • Repayments
        • Bond redemption function
      • Claiming collateral
        • Defaults and Unsold bonds
      • Returns on Investment
        • Manager and Platform returns
    • Verified REST API
      • Market data
        • Get all assets
        • Get Tradable Asset Pairs
        • Get Ticker Information
        • Get Orderbook
      • Order data
        • Get Account Balance
        • Get Trade Balance
        • Get Open Orders
        • Get Closed Orders
        • Query Orders Info
        • Query Trades Info
        • Get Trades History
      • Order Management
        • Add Order
        • Edit Order
        • Cancel Order
    • Verified Subgraphs
    • Verified Applications
      • Doing KYC
        • Creating wallet
        • Using the Verified Dapp
        • Doing KYC
      • Primary issues
        • Creating a new issue
        • Subscribing to issue
        • Closing issue
      • Secondary trading
        • Market orders
        • Limit orders
        • Edit, Cancel, Settle orders
      • Margin trading
        • Post Margin collateral
        • Swaps
        • Edit, Cancel, Settle orders
      • Portfolio
        • Account statement
        • Corporate actions
        • Underwriting liquidity
Powered by GitBook
On this page
  1. Reference
  2. Verified SDK
  3. Investment products

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"
      }
    ]
  }
}
PreviousProduct lifecycleNextSecurity tokens

Last updated 1 year ago