RUPIA: Synthetic rupee for yield farming

Vijay Lakshminarayanan
8 min readNov 3, 2020

Rupia was my project submission to Ethonline a hackathon organised by Ethglobal. Following the spirit of in person hackathons which range between 24–48 hours, I completed my “hack” around 3 days. This project was awarded the sponsor prize from Chainlink .

Problem:

Recent judgement passed by Supreme court of India overturning the 2018 ban on cryptocurrencies paved the way for strong DeFi adoption in India. It comes as there is a strong move away from cash transactions in many of the use cases which have strong potential for DeFi. With increasing penetration of android phones which are expected to go upto 760 million users in 2021, there is a tremendous potential for new set of users to on-board into the DeFi ecosystem. Use cases broadly include insurance claim handling, trading, sports betting markets. Thus a stable-coin which can act as a fuel for these use-cases has strong potential.

Emerging DeFi opportunity in India

Currently many of the problems can be solved simply with digital tech i.e. Paytm, PhonePe (launched by Walmart in India) and other digital currencies. They have strong UX, and provide cashback initiatives capturing the market to provide an alternative method of payment which is hassle free. Thus there needs to be a strong incentive for the users to switch to stable-coin.

So the requirement in India for stable coins is that they must have good UX, and earn an interest rate atleast 4%. The stablecoins must also support the IMPS gateway which will enable a fiat-crypto gateway with low transaction fees.

With this in mind for the hackathon, a solution for this problem in the form of an MVP Rupia was envisaged.

Rupia MVP Architecture:

This is a stablecoin project which aims to act as a building block for using the Chainlink Middleware, and also working on the mechanics of yield farming.

Better UX, and low transaction cost is a necessity so the Matic network was chosen instead of deploying this project on Ethereum Main-net which suffers from significant delays and gas costs due to increase in transaction volume.

The MVP can be split into 3 parts as shown below:

  1. Fiat-Crypto Gateway: An easy to use Fiat crypto gateway which incorporates the IMPS payment method is necessary. For this purpose an integration with Transak was done.
  2. A DAI INR price-feed powered by Chainlink oracles will enable the precise, reliable calculation of the amount of Rupia tokens which will be minted for the user.
  3. ERC-20 Token: DAI was used to provide liquidity i.e. the deposit in the form of DAI can be minted to Rupias which can be used either to stake or un-stake earning an interest rate.

Figure below demonstrates the Rupia architecture bringing it all together:

Rupia Architecture

The following sections will describe the above 3 parts individually to build this project.

Fiat-Crypto Gateway:

To build the fiat crypto gateway, Transak was used. Transak enables direct conversion from INR to Matic DAI which will suit the purpose of this MVP/Project.

Transak is a fiat on/off ramp, and upon registration an API key will be provided for integration/use with testnet.

React app is used as a front end. A detailed instruction to create and get started with react app is found here. The template create react app was modified to use the Transak API, and configure it with Indian Rupee as the base currency, and Matic DAI as output.

As suggested in the tutorial:

npx create-react-app my-app
cd my-app
npm start

Additionally install the transak SDK dependency

npm install @transak/transak-sdk

Next edit App.js file and configure the settings file for the base Fiat currency which is INR in this case, and the default cryptocurrency as Matic DAI. Ensure that wallet address is the Mumbai test net wallet address, and enable connection via metamask to the Mumbai testnet address.

const settings = {    
apiKey: '', // Your API Key requested from Transak
environment: 'STAGING', // STAGING/PRODUCTION defaultCryptoCurrency: 'DAI',
walletAddress: '', // Your customer's wallet address
themeColor: '000000', // App theme color
fiatCurrency: 'INR',
hostURL: window.location.origin,
widgetHeight: '550px',
widgetWidth: '450px'
}

After setting it up with the API key, run

node App.js

Based on the widget configuration the following screen is arrived at: Link to the Matic testnet wallet address, and selection of matic network. This will enable the user to buy DAI directly into the Matic testnet. KYC aspects have been skipped below.

Rupia Transak SDK react screenshot

In main-net implementation the purchase of DAI will require the user to transfer the money from his bank account via IMPS. As discussed at the start IMPS or bank transfer has the lowest transaction cost.

Click made transfer, and then a confirmation email will be sent to the user that the DAI was successfully sent to the Address.

This achieves the goal for the first part of this project.

DAI-INR Price Feed

The next step after receiving the DAI will be to mint the corresponding amount in Rupia tokens. To achieve this a chainlink price feed which gets accurate information on DAI-Indian rupee exchange rate is necessary. There are a few readily available APIs such as Coingecko, Cryptocompare with host addresses.

Chainlink’s Request and Receive cycle enables smart contracts to make a request to any external API and consume the response. To implement it the contract needs to define a request and receive functionality.

Let us have a look at the mechanics of building an API and connecting it with the smart contract. The code below does the following:

→ Import the chainlink client contract

→ Configure the contract to receive the DAI to INR conversion price into rupees and paisa. (similar to dollars and cents).

→ Set the fee for the node at 0.1 Link, and set the chainlink token contract address deployed in Matic network.

→ Set the job ID, oracle address for the Matic network. This information can be found on the Matic docs here.

import "@chainlink/contracts/src/v0.6/ChainlinkClient.sol";contract Pricefeed is ChainlinkClient {uint256 public rupeePrice;uint256 public times = 100;address private oracle;bytes32 private jobId;uint256 private fee;/*** Network: Kovan* Oracle: Chainlink — 0x2f90A6D021db21e1B2A077c5a37B3C7E75D15b7e* Job ID: Chainlink — 29fa9aa13bf1468788b7cc4a500a45b8* Fee: 0.1 LINK*/constructor() public { setChainlinkToken(0x70d1F773A9f81C852087B77F6Ae6d3032B02D2AB);oracle = 0x1cf7D49BE7e0c6AC30dEd720623490B64F572E17;jobId = “d8fcf41ee8984d3b8b0eae7b74eca7dd”;fee = 0.1 * 10**18; // 0.1 LINK}

Once this configuration is setup as discussed earlier the request, receive cycle needs to be setup. In this case the Cryptocompare API has a ready made requestRupeePrice() which just needs the json string to be configured for making the DAI to rupee conversion as shown in the code below.

Fulfill rupee function when called after a response is received simply records the price value which can be used by the front end.

//Request rupee price
function requestRupeePrice() public returns (bytes32 requestId) {
Chainlink.Request memory request = buildChainlinkRequest(jobId, address(this), this.fulfillRupee.selector);request.add("get","https://min-api.cryptocompare.com/data/price?fsym=DAI&tsyms=INR");request.add("path", "INR");
request.addInt("times", int256(times));
return sendChainlinkRequestTo(oracle, request, fee);
}//Fulfill rupeefunction fulfillRupee(bytes32 _requestId, uint256 _price)public recordChainlinkFulfillment(_requestId){
rupeePrice = _price;
}

Before integrating this with the token farm contract, the price-feed can be tested directly with a react front end.

Configure truffle.js with your Infura endpoint and development key. This part is left open since it’s specific to the implementation. Similar to section 1 configure a standard create react app, and then make a call to the price feed request function.

const tx = price_feed.methods.requestAll()var arr = []console.log(["CHAIN LINK NODE REQUESTS", sol_address])const operation = async () => {await tx.send({from: signer.address,gas: await tx.estimateGas(),}).once('transactionHash', txhash => {console.log(txhash)price_feed.methods.rupeePrice().call().then(res => {arr.push(res)console.log(res)})})return arr}operation()

The current DAI INR price, contract where it’s deployed and transaction hash are shown below. For Mumbai testnet, the contract hash needs to be entered here.

DAI <-> INR Price: 105.91

ERC-20 token:

The last section of this project is creating an ERC-20 token. Now this might seem daunting but it’s actually easy with OpenZeppelin. Basically the openzeppelin template can be used, and the relay network which uses GSN needs to be modified.

Openzeppelin was installed, and imported to mint tokens using the constructor method. Note that the units are expressed in 10e18, so this would be about 1 million tokens. Congratulations on being a millionaire ;)

npm install @openzeppelin/contractsimport "@openzeppelin/contracts/token/ERC20/ERC20.sol";contract RUPIA is ERC20 {constructor() public ERC20("RUPIA", "INRR") {_mint(msg.sender, 10000000000000000000000000);}}

Deploy this token on Mumbai testnet, and copy the address. This address will be used in adapting the tokenfarm example in the chainlink tutorial.

First step would be to issue holders of DAI who had converted their Indian rupees to DAI an equivalent in Rupia tokens, and this could be used for staking or unstaking earning interest.

As shown in the solidity code below which brings it all together. Rupias are minted based on the price-feed value for DAI-INR, and this is reflected in the balance which can be updated.

function stakeTokens(uint256 _amount, address token) public {require(_amount > 0, "amount cannot be 0");updateUniqueTokensStaked(msg.sender, token);IERC20(token).transferFrom(msg.sender, address(this), _amount);stakingBalance[token][msg.sender] = stakingBalance[token][msg.sender] + _amount;if (uniqueTokensStaked[msg.sender] == 1) {stakers.push(msg.sender);}issueTokens(msg.sender, _amount);}function unstakeTokens(address token) public {uint256 balance = stakingBalance[token][msg.sender];require(balance > 0, "staking balance cannot be 0");IERC20(token).transfer(msg.sender, balance);stakingBalance[token][msg.sender] = 0;uniqueTokensStaked[msg.sender] = uniqueTokensStaked[msg.sender] - 1;uint256 balanceRupia = RUPIA.balanceOf(address(msg.sender));RUPIA.transferFrom(msg.sender, address(this), balanceRupia);}function issueTokens(address recipient, uint256 amount) internal {RUPIA.transfer(recipient, uint256(requestRupeePrice())*amount);}

To make this work in Matic, a few more steps have to be followed. Truffle config needs to be configured to work. Follow the steps shown here.

It’s important to note that the mnemonic words are used in the truffle config file. Please ensure that these are stored safely in a .secret file. Don’t use the values stored in metamask since the same mnemonic can be used for generating the private keys and can be easily guessed by a hacker. The mnemonic values i.e. 12 words can be autogenerated by following the steps here for BIP39.

I hope this overview of Rupia project was useful as a template for token creation integrating chainlink oracles. Please see the demo here.

--

--