用智能合约实现质押分红

stirlingx 发布于 2021-10-30 阅读 9121

这两年DeFi的兴起,让世界看到了金融的另一种形式。越来越多的团队开始思考如何把传统的金融业务搬进区块链世界。

这两年DeFi的兴起,让世界看到了金融的另一种形式。越来越多的团队开始思考如何把传统的金融业务搬进区块链世界。区块链因其公开透明,不可篡改的性质,优势是显而易见的。然而一旦进来后才会发现区块链性能非常低效,同时与区块链交互又是非常昂贵的。所有区块链为了保证自身的正常运转,都设计了一套负反馈经济模型,典型的如以太坊的gas模型。许多看似简单的逻辑,用智能合约却很难实现。以前学过的算法和数据结构,在这里突然发现用不了了。也许我们需要新的思维,一种去中心化的思考方式。

废话不多说,我们这里就来分析一下常见的质押分红,这个功能怎么用智能合约实现。

质押分红是什么?

假设你开了一个网店,并发行了一种积分。用户购买商品的时候可以获得一定数量的积分。假设比例为1:100,1块钱的商品送100积分,积累的积分可以用来兑换商品。但是你不希望用户用积分来兑换你的商品,这是显然的。于是你设计了一个程序(智能合约),让用户自愿把他的积分质押起来,当然用户也可以在任何时候从智能合约中赎回属于自己的积分,就像我们在银行存钱一样。为了让用户自愿去质押,你修改了规则,将原来买1块钱商品可以获得100积分,现在改成了50积分,另外再将50积分按每个用户在智能合约中质押的积分比例分发给这些用。质押的积分越多,获得的积分也越多。购买的人越多,获得的积分也越多。积分有了利息,于是那些积分大户,纷纷把积分质押起来,赚取利息,并且还主动帮你推广网店。因为他们实际上已经成了网店的股东。

这个智能合约该怎么实现呢?

为了用智能合约实现,我们先把发行的积分token化,一个遵循ERC20标准的token。按照正常的思维,显然这个智能合约必须实现三个方法,也就是质押、赎回、分红。用户调用质押方法,将自己的token存入合约,合约中记录每个用户质押的token数量。用户通过赎回方法赎回之前质押的token。网店店主通过调用分红方法,传入用于分红的token,循环遍历所有质押的用户,按照质押数量比例分红。这里就有个问题,也就是前面提到的,区块链是昂贵的,里面不应该有大循环。因为gas费太高,甚至可能失败。所以我们应该思考一种没有循环的分红方式。

买过基金的童鞋们可能会发现,这里的质押跟买基金非常像。我们买入基金时,它会返回给你一定的份额。这个份额数量怎么来,就是用你买入的钱除以当前这只基金每份的价格。举个例子,假设有一只基金每份2元,你买入1000元,你将得到500份。过了一段时间,这只基金价格涨到了3元,你把手里的那500份卖掉得到1500元,赚了500。当然基金价格也有跌的时候,这时你卖掉就亏了。我们分析一下里面的逻辑,基金的价格怎么来。显然价格等于总价值除以总份额,价格是算出来的,只需记录基金的总价值和份额就可以了。我们买入基金时,总价值增加了,为了保证价格不变,份额也等比例增加了,这个比例就是价格。卖出时同理,份额减少,总价值等比例减少。为什么基金会有涨和跌,因为基金经理拿着基金的钱去投资,当然有赚有亏,也就是总价值变化 了,份额还是没变。

回到我们的质押分红合约,实际上就是一只基金,区别在于基金的钱是要拿去投资的,可能会亏损,而我们质押的token是躺在合约里面的。质押分红合约相当于一只只涨不跌的基金,质押相当于买入基金,赎回相当于卖出,分红就是把token转入合约,也就是总价值变大了。所以分红函数是不需要实现的,只需要往合约地址转账即可。

更进一步,我们还可以把份额设计成ERC20的token,命名为LP,这样LP也是可以转账的。所以核心的功能只剩下质押和赎回两个函数了。质押相当于铸造新的LP token,代码如下:

function mint(uint tokenAmount) public {
	 require(tokenAmount > 0, "invalid tokenAmount");
	
	 uint lpAmount = 0;
	 if (m_totalSupply == 0 || totalToken() == 0) {
		 lpAmount = tokenAmount;
		 m_balances[msg.sender] = lpAmount;
		 m_totalSupply = lpAmount;
		
	 }else {
		 lpAmount = m_totalSupply.mul(tokenAmount).div(totalToken());
		 m_balances[msg.sender] = m_balances[msg.sender].add(lpAmount);
		 m_totalSupply = m_totalSupply.add(lpAmount);
	 }
	 
	 require(IERC20(m_token).transferFrom(msg.sender, address(this), tokenAmount), "failed to Transfer token");
         emit Transfer(address(0), msg.sender, lpAmount);
	 emit Mint(msg.sender, lpAmount, tokenAmount);
}

第一次质押时,发行的LP数量和传入的token一样,之后按等比例发行。

赎回相当于销毁LP token,代码如下:

function burn(uint lpAmount) public {
	 require(lpAmount > 0, "invalid lpAmount");
	 require(lpAmount <= m_balances[msg.sender], "lpAmount exceed range");
	   
	 uint tokenAmount = totalToken().mul(lpAmount).div(m_totalSupply);
	 
	 m_balances[msg.sender] = m_balances[msg.sender].sub(lpAmount);
	 m_totalSupply = m_totalSupply.sub(lpAmount);
	 
	 require(IERC20(m_token).transfer(msg.sender, tokenAmount), "failed to Transfer token");
	 emit Transfer(msg.sender, address(0), lpAmount);
	 emit Burn(msg.sender, lpAmount, tokenAmount);
}

赎回的时候按等比例销毁LP即可。

总结一下

传统金融很多逻辑都是可以用区块链实现的,包括在电商行业流行的各种优惠券,积分券等等。相对于传统互联网方式,区块链的方式更透明,也更灵活。同时对于智能合约开发人员又提出了新的要求,不仅要会写代码,还有掌握一定的金融知识。用去中心化的思维,灵活运用。

完整的代码在github上面:

https://github.com/liyue201/stock-contract

更多内容请关注公众号

wx.jpg

相关文章

5 条评论

写的不错 ,点个赞

2021-11-09 12:26

请问分红那部分是在哪段代码体现的呢? 没看明白, 求指教。

2021-11-11 16:00
stirlingx 回复 J Phx

分红不需要代码实现,只需要往合约地址转入分红的token即可。之后这个函数的返回值会变大

function totalToken() public view returns(uint) { return IERC20(m_token).balanceOf(address(this)); }

2021-11-11 16:44
J Phx 回复 stirlingx

好吧,看你代码没有这部分。所以有些疑惑,谢谢解答。

2021-11-11 17:25

按说分红其实从现实世界来看,是投资的再回报,这部分利润是分红的基础。而这个质押分红,token从那里来的呢,除非是这个池子的未解锁的代币来分红。

2022-02-20 15:56