Nash is a stablecoin which is designed to maintain its price stability by using an algorithmic mechanism, without the requirement for central control on supply and demand, as is the case with most famous stablecoins such as Tether, USDC, etc.
In addition to ordinary users who can buy Nash as a stable-value crypto-asset, arbitrageurs can earn money from the price fluctuations whilst helping maintain the peg. In addition, there is a decentralized reserve whereby investors can lock their crypto-assets and make a profit, (for example, using LSK). In the future, we will use this reserve to make the system sustainable under critical conditions.
Finally, I am currently working on this project as part of my internship at Rastak Media Sepehr.
The somewhat volatile price swings of Bitcoin and other cryptocurrencies are one of the biggest entry barriers to deploying them in the mainstream financial markets. As without price stability, it is difficult for credit and debt markets to justifiably utilize a cryptocurrency.
On the other hand, stablecoins have been gaining a lot of traction, as they offer the security and relative price stability that other cryptos are unable to match. Today’s stablecoin solutions seek to combine the benefits of blockchain technology with the stability associated with certain fiat currencies or specific commodities.
It should be noted that there are many stablecoins existing in the blockchain market, however they can be classified into four general types based on the kind of collateral they use:
Through this project my aim is to implement one of the main ideas on algorithmic stablecoins and seigniorage shares, using the Lisk SDK so that it can be tested and simulated. I started by creating a simple PoC version with minimum functionalities, and will now extend, and build the required features to bring this to fruition.
In this project we have 3 types of tokens: Nash, Bond and Share. These tokens are each described below, including clarification of the price-stability mechanism deployed.
Nash: The core token of the system which is pegged directly to the US Dollar.
Bond: Users of the PoC version of this application can feed the system by a desired price and test the application. If this price is lower than $1, new bonds will be created and initialized, then they will be added to the bond market, hence all users will be able to buy them with a 20% reduction, only if exchanged for Nash. This is how the supply can be reduced at low prices. In addition, when the price of Nash rises over $1 some bonds will be converted to Nash automatically in a FIFO order to expand the total Nash supply, and subsequently return the price to $1. Each bond is equivalent to 100 Nash and also each bond is an
account
with a custom asset object consisting of properties such as ownerId, price, status (sold, unsold, expired).Share: With this token, the users are like the shareholders of our system. This token is required to keep the system stable in critical conditions by having a decentralized reserve of Lisk. For example, if you wish to buy a ‘share’, the only way is to transfer some LSK with a value of 10LSK/share to the reserve. For real-world use cases we need to implement a DAO, therefore users of this DAO will be shareholders. In critical conditions whereby the algorithmic expansion or contraction is not sufficient, shareholders can vote on possible reactions to handle the situation using the reserve.
When the system wants to expand the supply of Nash at high prices, at most 80% of new tokens can be supported by bonds. The remaining tokens will be distributed to the shareholders. In this manner it is possible to encourage people to buy shares and invest in the Reserve.
Please note that in the PoC version we ignore implementing the DAO.
The list of implemented custom transactions can be seen below:
1. Nash Transfer: This is similar to the balance transfer as a default transaction in the SDK, but instead of the balance, nash property of accounts is changed.
//update recipient's account
const new_recipient_nash = (!recipient.asset.nash)? this.asset.amount
: new BigNum(recipient.asset.nash).add(this.asset.amount).toString();
const updated_recipient = { ...recipient, asset: { ...recipient.asset, nash: new_recipient_nash} };
store.account.set(updated_recipient.address, updated_recipient);
//update sender's account
const new_sender_nash = new BigNum(sender.asset.nash).sub(this.asset.amount).toString();
const updated_sender = { ...sender, asset: { ...sender.asset, nash: new_sender_nash} };
store.account.set(updated_sender.address, updated_sender);
2. Bond Transfer: Users can transfer the ownership of bonds between each other. The only requirement is to change the ownerId property of the corresponding bond account.
//update bond's account
const updated_bond = {...bond, asset: {...bond.asset, ownerId: this.asset.newOwnerId}};
store.account.set(updated_bond.address, updated_bond);
3. Buy Share: With this transaction the users can send 10 LSK/share to the reserve account, and they will receive an equivalent share corresponding to the value of 10LSK. Furthermore, it is necessary to maintain a list of shareholders, together with the amount of shares they own. This information is kept in the manager account as the holders field.
//update sender's account
const new_sender_balance = new BigNum(sender.balance).sub(lisk_amount).toString();
const new_sender_share = (!sender.asset.share)? this.asset.amount
: new BigNum(sender.asset.share).add(this.asset.amount).toString();
const updated_sender = { ...sender, balance: new_sender_balance, asset: { ...sender.asset, share: new_sender_share}};
store.account.set(sender.address, updated_sender);
//update manager's account
const new_manager_balance = new BigNum(Manager.balance).add(lisk_amount).toString();
var new_holders = (!Manager.asset.holders) ? {} : Manager.asset.holders;
new_holders[sender.address] = new_sender_share;
const new_share_supply = (!Manager.asset.shareSupply) ? this.asset.amount
: new BigNum(Manager.asset.shareSupply).add(this.asset.amount).toString();
const updated_manager = {...Manager, balance : new_manager_balance,
asset:{...Manager.asset, holders: new_holders, shareSupply: new_share_supply}};
store.account.set(updated_manager.address, updated_manager);
4. Sell Share: This is based on the same logic as covered in the previous transaction, only this time the users sell their share to the reserve and receive their LSK back.
//update sender's account
const new_sender_balance = new BigNum(sender.balance).add(lisk_amount).toString();
const new_sender_share = (!sender.asset.share)? this.asset.amount:
new BigNum(sender.asset.share).sub(this.asset.amount).toString();
const updated_sender = { ...sender, balance: new_sender_balance,
asset: { ...sender.asset, share: new_sender_share} };
store.account.set(updated_sender.address, updated_sender);
//update Manager's account
const new_recipient_balance = new BigNum(recipient.balance).sub(lisk_amount).toString();
var new_holders = recipient.asset.holders;
new_holders[sender.address] = new_sender_share;
if(new_sender_share === '0'){
delete new_holders[sender.address];
}
const new_share_supply = new BigNum(recipient.asset.shareSupply).sub(this.asset.amount).toString();
const updated_recipient = {...recipient, balance : new_recipient_balance,
asset:{...recipient.asset, holders: new_holders, shareSupply: new_share_supply}};
store.account.set(updated_recipient.address, updated_recipient);
5. Bond2Nash: This particular transaction is used in the expanding mechanism whereby a number of Bonds need to be converted to Nash. To implement this, the ownerId field of the corresponding bond account is cleared, the status is changed to “expired”, and 100 Nash is added to the owner's Nash balance.
Finally the address of the bond is deleted from the bonds list that is located in the manager’s account. In addition, the manager’s nashSupply field should also be updated.
//update bond's account
const updated_bond = { ...bond, asset: { ...bond.asset, ownerId: '', status: 'expired'} };
store.account.set(updated_bond.address, updated_bond);
//update owner's account
const new_owner_nash = (!owner.asset.nash)? '100' : new BigNum(owner.asset.nash).add('100').toString();
const updated_owner = { ...owner, asset: { ...owner.asset, nash : new_owner_nash } };
store.account.set(updated_owner.address, updated_owner);
//update Manager's account
const new_supply = (!Manager.asset.nashSupply) ? '100' : new BigNum(Manager.asset.nashSupply).add('100').toString();
const new_bondsList = Manager.asset.bondsList.filter( item => item !== bond.address );
const updated_Manager = {...Manager,asset : {...Manager.asset, bondsList : new_bondsList, nashSupply: new_supply}};
store.account.set(updated_Manager.address, updated_Manager);
6. Initialization: This transaction is deployed in two cases, firstly whereby it is necessary to initialize new bonds that are created in a contract mechanism. At this time their price is set to a value of 20% less than (current price of nash*100), status field is set to “unsold”, and the owner is set to manager’s account, and then they will be available on the bond market. Secondly, the other use case of this transaction is initializing the manager's account when starting the application.
if( this.asset.type === 'manager'){
const Manager = store.account.get(manager.address);
const updated_Manager = {...Manager,
asset: {...Manager.asset, type:'manager', nash: '10000', nashSupply: '10000'}} ;
store.account.set(updated_Manager.address,updated_Manager);
}
else if ( this.asset.type === 'bond'){
if(!this.asset.price){
errors.push(new TransactionError(
'invalid transaction asset',
this.id,
'.asset',
this.asset,
'price must be provided'));
}
else{
const bond = store.account.get(this.senderId);
const updated_bond = {...bond, asset: { price: this.asset.price,
status: 'not sold', type: 'bond', ownerId: manager.address}} ;
store.account.set(updated_bond.address,updated_bond);
}
}
7. Buy Bond: Here the users can buy “unsold” bonds from the market. Through this transaction, owner and status properties of bond account, buyer nash balance and manager’s nash supply should be updated. In addition, the address of the bond is placed in the sold bonds list.
//update sender's account
const new_sender_nash = new BigNum(sender.asset.nash).sub(bond.asset.price).toString() ;
const updated_sender = {...sender, asset: {...sender.asset, nash: new_sender_nash} };
store.account.set(updated_sender.address, updated_sender);
//update bond's account
const updated_bond = {...bond, asset: {...bond.asset, ownerId:sender.address, status: 'sold'}};
store.account.set(updated_bond.address, updated_bond);
//update Manager's account
const new_supply = new BigNum(Manager.asset.nashSupply).sub(bond.asset.price).toString();
var new_bondsList = (!Manager.asset.bondsList) ? [] : Manager.asset.bondsList;
new_bondsList.push(bond.address);
const updated_Manager = {...Manager,asset : {...Manager.asset, bondsList : new_bondsList , nashSupply: new_supply}};
store.account.set(updated_Manager.address, updated_Manager);
8. New Nash: During the supply expansion, some of the new Nashs should be distributed amongst the shareholders. This is managed through the NewNash transactions that updates the Nash balance of shareholders, and also the manager’s nashSupply property.
//update recipient's account
const new_recipient_nash = (!recipient.asset.nash)? this.asset.amount
: new BigNum(recipient.asset.nash).add(this.asset.amount).toString();
const updated_recipient = { ...recipient, asset: { ...recipient.asset, nash: new_recipient_nash} };
store.account.set(updated_recipient.address, updated_recipient);
//update Manager's account
const new_supply = (!Manager.asset.nashSupply) ? this.asset.amount
: new BigNum(Manager.asset.nashSupply).add(this.asset.amount).toString();
const updated_Manager = {...Manager,asset : {...Manager.asset, nashSupply: new_supply}};
store.account.set(updated_Manager.address, updated_Manager);
As I am a beginner working with JavaScript, and especially in front-end development it will take some time for me to build an appropriate UI. I started with a very simple front-end to test the basic functionalities, and I will continue to work on and improve it further to higher versions as soon as possible. To further complement my work, l have included some of the relevant screenshots below:
It was a great experience to build this PoC, and I will continue working on improving the application further, both in the custom transactions and client interface; and finally to extend the features and functionalities in the fullness of time. I hope this blog gave you some insight regarding how we can build our DeFi ideas with Lisk.
If you have any questions or feedback, feel free to reach out on Discord (user `Kasra99ab#0990`).
If you feel inspired and want to build your own proof of concept blockchain application, check out the Lisk Builders program. More information about the program and the application procedure can be found on the Lisk webpage for the Lisk Builders program.
Disclaimer: This blog post was written by our community member, Kasra (LinkedIn profile) as part of his participation in the Lisk Builders program.