以太坊Solidity迁移Flow Cadence指南7-ERC20/TOKEN迁移

Flow作为专门为NFT设计的区块链,其合约语言主要侧重于NFT功能,当然,实现基本的ERC20 token 更不在话下。

由于ERC20合约相对比较简单,迁移也比较容易,因此,本节在完成迁移讲解的同时,重点对比下ERC20 token和Flow token合约的差异,并探讨下Flow的token 空投问题。

标准ERC20合约分析

以太坊ERC20 Token核心功能大致是这样的:

contract ERC20Basic {

mapping(address => uint256) balances; // 余额

function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {

    balances[_from] = balances[_from].sub(_value); 

balances[_to] = balances[_to].add(_value);

return true;

  }

}

如果完整对应的改成Flow Cadence合约的形式,可以是这样的:

contract ERC20Basic {

    access(contract) let balances: {Address: UFix64}

    pub fun transfer(from: Address, to: Address, amount: UFix64) {

        balances[from] = balances[from] - amount

        balances[to] = balances[to] + amount

    }

}

这样的实现是完全可以的,但就无法享受flow Cadence编程的各种自带好处了。举个简单的例子,solidity使用中会经常担心,把token发送到未知/错误地址,直接就”烧纸”了,但这在Cadence中是不会出现的。因为Cadence中钱包必须先执行金库的初始化操作,也就是先开户,才能转账,一个未知/错误的地址肯定是无法开户的,即使转错了,系统就会报错回滚,不用担心转错账的问题。这个就是面向资源编程的优势体现。

合约迁移

按照程序分析的一般套路,还是先分析数据结构,然后再看对应的增删改查功能函数。

ERC20和Flow推荐token标准合约数据结构对应关系如下图所示:

 

1 ERC20和Flow Token数据结构对应图

ERC20的核心数据结构就是一个字典,key为用户钱包地址,而value则是对应钱包的余额,所有转账查询等操作都是基于这个字典展开的,这样的方式类似于银行账号,其实是一种中心化的实现。而Cadence则只需定义了余额,token是作为一个资源类型,存放在对应用户的存储空间内的,也就是vault金库路径里面。也就是说Flow Cadence是一种真正的“去中心化”,用户的token都存储在自己的账号下,而以太坊Solidity的token则是存放在合约账号上的。

举个例子,如果合约不开源,Solidity完全可以随意操纵用户账号,类似某些城乡银行的高端操作,而Flow Cadence合约中,即使铸造token的合约都消失了、重写了,用户的token也不会受到任何影响,更不会存在合约账号暗箱操纵用户token的问题。

ERC20和Flow推荐的Token标准合约的功能函数对应关系如下图所示:

 

2 ERC20和Flow Token功能结构对应图

Flow token提供了token相关的铸造、转账、查询功能,但并不提供Approve相关的三方托管功能。具体迁移的时候,只需迁移核心的mint和transfer功能即可,approve的功能可以忽略,保留核心功能就行。Flow相关的approve托管合约,可通过ERC20类似的中心化实现试试,也可以利用“能力”相关的功能实现,具体可参考NFT市场一节。

Cadence上已经有类似ERC20的基础标准合约。我们就以openzeppelin对应实现的基础ERC20合约为准(https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol),对应的为Flow推荐的元数据标准合约实现(https://github.com/onflow/flow-ft),然后找到对齐关系,看需要修改的Cadence继承实现代码即可。这里暂时不考虑各种合约扩展功能。

从数据结构和功能函数来看, Solidity ERC721标准合约和flow cadence NFT合约都存在完整的对应关系,对于标准Token合约的迁移,我们只需要对flow合约进行“字符串替换”即可,具体如下图所示:

        

 

图3 ERC20合约迁移具体流程

如上图所示,核心就是修改合约名称,代码中对应合约名称的引用部分,也进行对应的修改即可。包括合约、交易、脚本都按此修改即可。合约部分有资源集接口,也进行对应的字符串修改即可。另外还有包含Token名称的资源路径和函数名称,替换成新的合约名称字符串即可。

熟悉linux shell脚本的同学,使用一句shell脚本即可完成上面所说的替换:

#!/bin/sh

nft_name="Wow" #token名称

find ./ -name "*.cdc"|xargs -i sed 's/Example/'''${nft_name}'''/g' {}

理解资源

分析Flow token和ERC20 token的转账功能,可以非常清晰的看到flow“资源”的使用方式:

transaction(amount: UFix64, to: Address) {

    let sentVault: @FungibleToken.Vault

    prepare(signer: AuthAccount) {

        let vaultRef = signer.borrow<&ExampleToken.Vault>(from: ExampleToken.VaultStoragePath)

?? panic("Could not borrow reference to the owner's Vault!")

        

self.sentVault <- vaultRef.withdraw(amount: amount) //取出token

    }

    execute {

        let recipient = getAccount(to)

        let receiverRef = recipient.getCapability(ExampleToken.ReceiverPublicPath)

            .borrow<&{FungibleToken.Receiver}>()

?? panic("Could not borrow receiver reference to the recipient's Vault")

        receiverRef.deposit(from: <-self.sentVault) //存放token

    }

}

ERC20的token,就是合约的字典数据中,先从源账号key中减去一个值,然后在目标账号key中加一个值。

Flow的token,则先从源账号的金库中withdraw取出一定的token,然后再deposit存储到模板账号的金库中。

具体对比如下图所示,flow这样的方式好处是啥,有啥不足,除了前面说的安全性之外,大家可以自己想一想。

 

图4 以太坊和Flow转账模式对比

Flow空投

Flow面向资源编程带来安全性无可比拟,但相应的,所得必有所失。在Flow的空投上,就会存在一定的问题。

因为Flow的账号转账,不管是NFT,还是一般的token,目标账号都必须先授权并初始化一个对应vault仓库,才能进行转账操作。类似于你用身份证当钱包地址,但如果在不同的银行存钱取钱,还是要用身份证去对应银行“开户”。

简而言之,Flow没法像以太坊那样,无需用户许可、骚扰式的发送token,这对很多应用而言,少了一个重要的推广方式,类似于不能发骚扰短信、不能发垃圾邮件、不能打骚扰电话了。。。。

目前flow社区暂时没有确定的方案,但有一些合约按照如下一些方式,间接的实现了flow空投功能:

最简单的方式,就是先发送,后领取。相当于一种中心化的实现,具体的数据结构设计方面,可以采用本文开始说的那种中心化的Cadence方式,先把发送的token存放到合约的字典数据里面,用户查看时,搜索字典,命中后会提示有token可以领取,然后再把token 转移到用户账号即可。

第二种,交易市场类实现。也就是做一个类似candy糖果类的NFT市场,有用户在这里领token,有开发者在这里发token,当然,如果是空投,设置价格为0即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Solidity,可以通过使用智能合约来实现ERC20代币的锁仓与释放。以下是一个简单的锁仓合约示例: ``` pragma solidity ^0.8.0; import "./IERC20.sol"; import "./SafeMath.sol"; contract TokenVesting { using SafeMath for uint256; address public beneficiary; uint256 public cliff; uint256 public start; uint256 public duration; uint256 public released; IERC20 public token; constructor( address _beneficiary, uint256 _cliff, uint256 _duration, address _token ) public { require(_beneficiary != address(0)); require(_cliff <= _duration); beneficiary = _beneficiary; cliff = _cliff; duration = _duration; start = block.timestamp; token = IERC20(_token); } function release() public { uint256 unreleased = releasableAmount(); require(unreleased > 0); released = released.add(unreleased); token.transfer(beneficiary, unreleased); } function releasableAmount() public view returns (uint256) { return vestedAmount().sub(released); } function vestedAmount() public view returns (uint256) { uint256 currentBalance = token.balanceOf(address(this)); uint256 totalBalance = currentBalance.add(released); if (block.timestamp < start.add(cliff)) { return 0; } else if (block.timestamp >= start.add(duration)) { return totalBalance; } else { return totalBalance.mul(block.timestamp.sub(start)).div(duration); } } } ``` 在这个合约,当创建合约时,需要传入受益人地址、锁仓期、释放期、代币地址等信息。锁仓期结束后,受益人可以通过调用 `release()` 函数来释放锁仓代币。如果释放函数被调用,但是当前时间还没有到达释放期,则会抛出异常。 为了保证代币不能被提前释放,合约还实现了 cliff 的概念,即在锁仓期结束之前,代币不能被释放。当 cliff 结束之后,代币将按照线性方式释放,直到释放期结束。 需要注意的是,以上示例只是一个简单的锁仓合约示例,实际生产环境需要更加严格地考虑各种情况和安全性问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wangliang_f

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值