Bond Implementation

I am looking to create a smart contract for bond payments. The idea is that a bond holder will get an asset representing the bond, receive periodic coupon payments and then at maturity can exchange the bond for the principal.

I am unsure whether to use a non-fungible or fungible asset to represent the bond. The challenge I am trying to solve is knowing whether a specific coupon payment has or has not been collected for a bond. This is made harder by the fact that I want people to be able to transfer the bond between themselves.

If a non-fungible asset is used then the smart contract would need to know about every single asset ID which I don’t think is possible with limitted global state.

If a fungible asset is used then there is no obvious way of distinguishing between an asset that has collected a coupon payment and one that has not. One option would be to create x number of assets for the x different coupon payments, although again the smart contract would need to know the x asset IDs.

Alternatively with a fungible token maybe you can track the asset somehow in the smart contract? You could record in the local state of the bond owner how many coupon payments they have collected. When the asset is transfered you would have to copy over this local state to the new owner.

Would love some feedback and ideas on my proposed approaches. I am new to Algorand so there is probably a solution that I am completely ignoring. Maybe there is a way to collectively pay all bond owners at the same time which would avoid my problem entirely?

These are really good questions.

I don’t think there is an easy way to securely pay all bond owners at the same time, except if there are fewer than 16 of them and you can do a group transaction.

I am thinking of two potential solutions (that can definitely be refined):

  1. Do not use at all a token and instead store in local storage the details about the bond. To transfer the bond to someone else, you need to do a smart contract call (instead of an ASA transfer). This has the advantage of being quite easy to implement but you lose the advantage of ASA transfer.
  2. Each time a bond is issued, you create a new smart contract address (stateless TEAL) that will be the creator of the bond NFT and you store the bond parameters in the local storage of this new contract. Note that the stateful TEAL application can generate itself the smart contract address as it consists of the hash of the smart contract (that can be reconstructed as the concatenation of several strings). There is an additional trick to reduce size of the stateful application described at the bottom of this post. Note also that the stateful TEAL application can access the creator of any NFT so can check it is an NFT mapping to a real bond and retrieve the bond parameters from there.

Trick to reduce the size of the stateful application: The stateless smart contract will consist of three parts (once compiled): prefix, some value x to distinguish each bond, suffix.
The prefix is short (most likely just the version number and the bytecblock instruction) but the suffix can be long. What you can do is hardcode the hash of the suffix instead of the suffix in the application and require one argument of the application to be the suffix itself.
This way you moved the long suffix from the program (very constrained in size) to the arguments (much less constrained in size).

I’d like to throw in an additional idea to what @fabrice mentioned. Since making changes to many accounts might not be feasable, what about making a change to a global account that would render additional deferred value to each of the individual account ?

Concretly, i’m referring to the same model the node is using in order to distribute rewards. Instead of updating millions of accounts, the node add a global “reward level” variable, and each account contain its own reward level. When an account is being modified, it’s rewards are being rendered by diffing the global reward level over the local account reward level.

Thank you for your suggestions.

I don’t quite understand the second solution. My understanding is that you are referring to a ‘Contract Account - Stateless Smart Contract’. Would every bond have its own Contract Account and how would you store any parameters in a stateless contract? Also how would these Contract Accounts be created initially and then facilitate bond transfers?

Is there somewhere I can read more about the model the node is using in order to distrubute rewards?

The model used by the node is described in the protocol specification ( specs/ledger.md at master · algorandfoundation/specs · GitHub ).

Regarding the way Algorand distributes rewards, see https://www.algorand.com/resources/blog/rewards-technical-overview

Regarding the second solution, here is how I was thinking of it.
You create a stateless smart contract C_x that does the following:

accept any transaction as long as it’s called in a group with an application call to your application App. The application App will be responsible to only allow the transactions described below.

The stateless smart contract also contains an ID x that it just discards, just to distinguish between each bond. The application App contains a counter X in global storage used to initialize x.
Instead of using a counter, you may use other values such as the hash of the calling account A and a byte chosen by A.

Then, to issue the bond to an account A, you do the following:

  1. First, you need to create the new smart contract account C_x and store in local storage of A that it should receive this NFT from C_x. You do that using the following group of transactions:
    a. Opt-in application call of App by A that will make A opt-in to App and store in A’s local storage that it is allowed to get the bond with ID X. (X is then incremented.)
    b. Payment from A to buy the bond
    c. Payment from A to the stateless smart contract C_x of 1 Algo to be able to issue transactions from C_x
    d. Creation of a bond NFT by C_x
    e. Opt-in application call of App by C_x to store in C_x’s local state the bond parameters.
  2. Second, the account A can opt-in to the NFT created by C_x
  3. Third, the account A can get the NFT by doing the following group of transactions:
    a. Close-out application call of App by A that will see that A is still owed the NFT created by C_x.
    b. Asset transfer from C_x to A

The reason that you need to do the process in 3 steps rather than a big transaction is that you cannot know the asset ID created by C_x until the transaction is executed. So you cannot do the asset transfer of the NFT bond immediately. Instead, you need to store in local storage of A that A is meant to receive this NFT. And then make the asset transfer.

I propose another possibility, inspired by the original meaning of a bond coupon. From Coupon Definition

Coupon Bonds

The term “coupon” originally refers to actual detachable coupons affixed to bond certificates. Bonds with coupons, known as coupon bonds or bearer bonds, are not registered, meaning that possession of them constitutes ownership. To collect an interest payment, the investor has to present the physical coupon.

The idea

Create an ASA corresponding to each payment deadline, and the the final principal. When you buy a “bond”, you get one of each. When you wish to get paid for a coupon, you send it to a smart contract that pays you out (assuming the deadline associated with the coupon has passed). Same for the principal.

Now the coupons are individually tradable which seems like an advantage. And the “tracking” is only on the number of payment deadlines, rather than number of bonds issued.