STO实践分析:ERC1410标准从分析到代码实现
在数字化与区块链技术迅猛发展的今天,证券代币化(Security Token Offering,简称STO)正成为投资领域的热门话题。随着各国对数字资产监管的逐步完善,ERC1410标准作为一种新兴的代币标准,逐渐在市场上崭露头角。本文将深入探讨ERC1410标准的实践分析,从理论框架到代码实现,带领读者全面理解这一标准在STO中的应用与价值。
ERC1410标准的核心在于其灵活性和合规性,它不仅满足了证券代币的基本需求,还提供了更为复杂的功能,使得投资者能够在合规的前提下享受区块链技术的便捷。为了更好地理解ERC1410标准,我们首先需要从其设计理念和技术架构入手,分析该标准的优势及其在STO中的应用场景。
ERC1410标准的设计初衷是为了解决传统证券市场中存在的一些痛点,例如高昂的交易成本、复杂的合规流程以及信息不对称等。通过引入智能合约技术,ERC1410标准能够在链上实现资产的透明化和可追溯性,从而降低交易成本并提高交易效率。与传统的证券发行方式相比,STO通过代币化的方式,不仅能够吸引更多的投资者,还能实现全球范围内的资金募集。
在实际应用中,ERC1410标准的灵活性体现在其支持多种合规机制的能力。比如,发行方可以根据不同类型的投资者设定不同的投资限制,确保符合各国的法律法规。此外,ERC1410标准还允许在代币上附加多种权益,如股息分配、投票权等,从而为投资者提供更多的选择。这种灵活性使得ERC1410标准在实际操作中具备了很强的适应性,能够满足不同项目的需求。
接下来,我们将深入分析ERC1410标准的技术架构。ERC1410标准基于以太坊区块链,采用了与ERC20和ERC721类似的代币标准,但其在代币的操作和管理上进行了扩展。ERC1410标准通过引入“分片”机制,将代币分为多个部分,每个部分可以独立转移和管理,从而实现更为复杂的资产结构。这种分片机制不仅提高了代币的灵活性,也为资产的合规管理提供了便利。
在ERC1410标准的实现过程中,智能合约的设计至关重要。智能合约不仅负责管理代币的发行、转移和销毁,还需要根据不同的合规要求进行相应的设定。为了确保智能合约的安全性和可靠性,开发者需要遵循一定的编码规范,并进行充分的测试。此外,在代码实现过程中,开发者还需要考虑到与其他系统的兼容性,确保ERC1410标准能够在不同的区块链平台上顺利运行。
为了更好地理解ERC1410标准的代码实现,我们可以通过一个具体的示例来进行分析。假设我们要为某个初创企业发行基于ERC1410标准的证券代币,首先需要定义代币的基本属性,包括名称、符号、总供应量等。接下来,我们需要编写智能合约代码,定义代币的转移规则、合规要求以及投资者的权益等。
在代码实现中,开发者需要使用Solidity编程语言来编写智能合约。首先,我们需要引入ERC1410标准的接口,并实现相关的函数。例如,定义一个名为“SecurityToken”的合约,并实现“transfer”、“mint”、“burn”等基本功能。同时,为了实现合规管理,我们还需要定义投资者的身份验证机制,并在转移代币时进行相应的检查。
在实际操作中,开发者可能会遇到一些挑战,例如如何处理合规性问题、如何确保智能合约的安全性等。针对这些问题,开发者可以借助一些工具和框架来提高代码的质量和安全性。例如,使用Truffle框架进行智能合约的开发和测试,使用OpenZeppelin库提供的安全合约模板来减少安全风险。
在完成智能合约的编写后,开发者需要进行充分的测试,以确保合约的各项功能正常运行。此外,还需要进行安全审计,以发现潜在的安全漏洞。在确保合约安全可靠后,开发者可以将其部署到以太坊主网,并开始进行代币的发行和交易。
值得注意的是,ERC1410标准在实践过程中仍然面临一些挑战,尤其是在合规管理和市场接受度方面。虽然ERC1410标准具备了较强的灵活性和适应性,但在实际操作中,如何平衡合规性与市场需求仍然是一个亟待解决的问题。此外,随着市场的不断发展,ERC1410标准也需要不断进行迭代和优化,以适应新的市场环境和技术变化。
综上所述,ERC1410标准作为一种新兴的代币标准,为STO提供了新的解决方案。通过对ERC1410标准的深入分析,我们可以看到其在合规性、灵活性和技术架构等方面的优势。同时,随着数字资产市场的不断发展,ERC1410标准的应用前景也将更加广阔。在未来,我们期待看到更多基于ERC1410标准的创新项目,为投资者带来更好的投资体验。
在这个充满机遇与挑战的时代,ERC1410标准的实践与实现不仅是技术层面的探索,更是对传统金融体系的一次深刻反思。随着区块链技术的不断进步,我们有理由相信,证券代币化将为全球投资者带来更多的便利与机会。对于从事相关领域研究和开发的人员而言,掌握ERC1410标准的应用与实现,无疑将为他们的职业发展提供更多的可能性与空间。
最后,ERC1410标准的成功实现不仅依赖于技术的进步,更需要政策的支持和市场的认可。我们期待在不久的将来,看到更多基于ERC1410标准的成功案例,为数字资产的未来发展注入新的活力。
ERC1410 (等同ERC1411)将 ERC20/ERC777
中不存在解释属性的余额,附加额外的信息,从而划分成不同的部分,就可以做一些操作上的限制。而 ERC1400 (等同ERC1411)是对 ERC1410
标准的继承和改进。
1.摘要
ERC1410为STO环境中使用的一个以太坊协议标准。辉哥着眼于深度理解和编码实现,从以下几个方面阐述对ERC1410的理解。
1) ERC1410和ERC1411(ERC1400),ERC1404的区别
2)同质化通证,非同质化通证,部分同质化通证的区别
3)ERC1410标准的数据结构分析
4) ERC1410的接口函数分析
5) ERC1410的场景尝试
6) 代码部署和测试
2\. ERC1410和ERC1411(ERC1400),ERC1404的区别
ERC1410标准由 Gosselin, Adam Dossa, Pablo Ruiz 和 Fabian Vogelsteller 撰写,其中
Gosselin 和 Dossa 为 Polymath 工作,就是那个发布ST20.帮助资产实现证券化通证的平台公司。
ERC1410是一种以太坊上实现STO的技术方案,它兼容ERC20和ERC777标准的证券型通证发行标准,引入了部分同质化通证(Partially
Fungible Token Standard)的概念。
它在GITHUB的地址为ERC 1410: Partially Fungible Token Standard 1410.
ERC1400同ERC1411是同一个标准,现在在GITHUB上已经把他们合并了。ERC1411(ERC1400)则是继承ERC1410标准,增加了证券相关业务会使用到的函数:证券增发,相关法律文件存储等。
它在GITHUB的地址为ERC 1400: Security Token Standard 1411.
ERC1404是指简单的受限代币标准,ERC-1404则在ERC-20标准的基础上,添加了两种函数。
detectTransferRestriction:此函数是发行者强制执行通证传输的限制逻辑。例子包括,(1)检查通证接收者是否在白名单内,(2)检查发送者的通证是否在锁定期内被冻结等等。该函数实现仅面向发行者,另外,第三方可以公开调用该函数来检查转移的预期结果。因为这个函数会返回一个uint8代码,所以它允许函数调用者知道传输失败的原因,并将其报告给相关的对方。
messageForTransferRestriction:这个函数实际上是一个“消息”访问器,它负责以人类可阅读的方式解释一笔交易为什么会被限制。通过规范消息查找,开发者授权用户界面构建器,有效地向用户报告错误。
它在GITHUB的地址为
3.同质化通证(FT),非同质化通证(NFT),部分同质化通证(PFT)的区别
ERC1410引入了部分同质化通证(Partially Fungible Token
Standard)的概念,在此之前介绍下同质化通证(FT),非同质化通证(NFT)的概念。
所有的ERC20 Token都是“同质化通证”(Fungible
Token,简称FT),同一通证内两个相等单位的Token并无差别。这个类似于美元,作为一般等价物,假设都是真钞,你手里的美元和我手里的美元在购物能力上并无区别。
ERC721 Token都是“非同质化通证”(Non-Fungible
Token,简称NFT)。非同质代表独一无二,谜恋猫为例,每只猫都被赋予拥有基因,是独一无二的(一只猫就是一个NFTs),猫之间是不能直接替换的。
非同质性其实广泛存在于我们的生活中,如图书馆的每一本,宠物商店的每一只宠物,歌手所演唱的歌曲,花店里不同的花等等,因此ERC721合约必定有广泛的应用场景,例如房地产,画作等上链具有广泛的意义。
ERC1410 Token是部分可替代通证(Partially Fungible
Token,简称PFT)就是兼具了可替代性和不可替代性2种属性的通证形态。它可以把TOKEN跟证券对等起来,可以做security
token的一些功能。ERC1410标准引入了“Tranche”的名字,在证券行业中通常翻译为“分级”(例如AAA级、AA级、次级),目的是将不同的通证持有者的账户内Token分成不同的tranches级别。举个例子来说,辉哥发起主导了一个名为“BIG
STO
PROGRAMME”的项目,由辉哥,欧阳和ELLA小姐姐一起参与,并且已经经过了天使轮(A资本投资),A轮(I资本)参与,项目发展不错,正准备到美国做STO申请。那么,创始团队(创始股),A资本(天使轮),I资本(A轮)这些投资方对应的价格,基金到期时间都不相同,可以定义tranches级别分别为1级,2级,3级等。
这3级的TOKEN具有相同性和不同性,后面场景尝试的时候会深入分析。
TOKEN持仓情况
4.ERC1410标准的数据结构分析
1.TOKEN总供应量
uint256 public totalSupply;
2.TOKEN名称
string public name;
3.TOKEN标识
string public symbol;
4.覆盖所有投资人所持通证的每个账户地址下的通证余额总额
// Mapping from investor to aggregated balance across all investor token sets
mapping (address => uint256) balances;
5\. 投资人到分级份额Tranche[]的映射
// Mapping from investor to their tranches
mapping (address => Tranche[]) tranches;
// Represents a fungible set of tokens.
struct Tranche {
uint256 amount;
bytes32 tranche;
}
6\. trancheToIndex-分级份额索引
// Mapping from (investor, tranche) to index of corresponding tranche in
tranches
//三维数组,就是在分级份额Tranches映射(investor, tranche) 到对应分级份额的索引(index)
mapping (address => mapping (bytes32 => uint256)) trancheToIndex;
7.交易员针对tranche,的授权状态
// Mapping from (investor, tranche, operator) to approved status
//四维数组,就是三维数组(investor, tranche, operator)的授权状态
mapping (address => mapping (bytes32 => mapping (address => bool)))
trancheApprovals;
8.交易员针对投资人的授权状态
// Mapping from (investor, operator) to approved status (can be used against
any tranches)
//三维数组,就是 投资人对应的操作员(investor, operator)的授权状态
mapping (address => mapping (address => bool)) approvals;
5.ERC1410的接口函数分析
5.1 与tranche有关的查询接口
5.1.1 查询某个账户下指定 tranche 的余额:
function balanceOfByTranche(bytes32 _tranche,address _tokenHolder) external
view returns (uint256);
查询某个账户下所有的tranches:
function tranchesOf(address _tokenHolder) external view returns (bytes32[]);
5.2 与tranche有关的转账接口
5.2.1 从调用者的指定tranche转指定金额到目的账户
function sendByTranche(bytes32 _tranche,address _to,uint256_amount,bytes
_data) external returns (bytes32);
表示上述接口表示从调用者的指定tranche转指定金额到目的账户。
如果转账交易不能完成,函数必须revert;
如果转账成功,必须emit SentByTranche event;
如果转账成功但接收者的tranche与发送者的tranche不同,则必须emit ChangedTranche event;
返回值为目的账户接受此次转账的tranche;
最后一个参数_data,可以将目标tranche直接用此参数指定,或存放任何与该交易相关的数据,比如授权信息。
5.2.2 从指定的多个tranches往多个目标地址进行转账。
function sendByTranches(bytes32[] _tranches,address[] _tos,uint256[]
_amounts,bytes _data) external returns (bytes32);
上述接口是sendByTranche接口的升级版本,从指定的多个tranches中,往多个目标地址进行转账。
如果转账交易不能完成,函数必须revert;
如果转账成功,必须emit SentByTranche event;
如果转账成功但接收者的tranche与发送者的tranche不同,则必须emit ChangedTranche event
sendByTranche、sendByTranches接口均为交易发起者(msg.sender)对自有账户的操作。
5.2.3 交易员转账特定Tranche
ERC1410基于
ERC777继承了交易员(operator)的相关概念,允许某个交易员代表某个账户持有者基于tranche进行转账。operatorSendByTranche和operatorSendByTranches就是该类型接口。
function operatorSendByTranche(bytes32 _tranche,address _from,address
_to,uint256 _amount,bytes _data,bytes _operatorData) external returns
(bytes32);
如果一个未被授权(isOperatorForTranche)的地址调用,函数必须revert;
如果转账成功,则必须emit SentByTranche event;
如果转账成功且接收者的tranche与发送者的tranche不同,则必须emit ChangedTranche event。
5.2.4 交易员转账多个Tranches:
function operatorSendByTranches(bytes32[] _tranches,address _from,address
_to,uint256[] _amounts,bytes _data,bytes _operatorData) external returns
(bytes32[]);
如果一个未被授权(isOperatorForTranche)的地址调用,函数必须revert;
如果转账成功,则必须emit SentByTranche event;
如果转账成功且接收者的tranche与发送者的tranche不同,则必须emit ChangedTranche event。
5.3 默认tranche管理
5.3.1 设定默认Tranches(setDefaultTranches)
为了保证与ERC777、ERC20的兼容性,当调用send时,需要决定从哪个或哪几个tranches中转出。为解决这个问题,可以指定某一个或某几个tranches为默认。
function setDefaultTranche(bytes32[] _tranches) external;
设置默认tranches,这样在调用send时,将从default tranches中转账。
5.3.2 获取默认Tranches(getDefaultTranches)
function getDefaultTranches(address _tokenHolder) external view returns
(bytes32[]);
获得某个账户的默认tranches。如果返回值为空,则调用send会抛出异常。如果返回多个,则可以按照某种策略进行转出。
5.3.3 交易员(Operator)相关接口
交易员可以被授权操作:
所有账户的所有tranches
所有账户的某个特定的tranche
某个特定账户的所有tranches(包括现在与未来的)
某个特定账户的特定tranche
5.3.3.1 defaultOperatorsByTranche
function defaultOperatorsByTranche(bytes32 _tranche) external view returns
(address[]);
返回具有所有账户的某个特定tranche的默认操作员列表。
5.3.3.2 authorizeOperatorByTranche
function authorizeOperatorByTranche(bytes32 _tranche,address _operator)
external;
消息发送者授权给某个交易员某个特定tranche的操作权。每次被调用,必须emit AuthorizedOperatorByTranche event。
5.3.3.3 revokeOperatorByTranche
function revokeOperatorByTranche(bytes32 _tranche,address _operator) external;
消息发送者撤销某个交易员对某个特定tranche的操作权。每次被调用,必须emit RevokedOperatorByTranche event。
(注: 此Operator仍有可能通过defaultOperatorsByTranche或defaultOperators拥有对此tranche的操作权。)
5.3.3.4 isOperatorForTranche:
function isOperatorForTranche(bytes32 _tranche,address _operator,address
_tokenHolder) external view returns (bool);
查询_operator是否是某个账户特定tranche的操作员。
6.ERC1410的场景尝试
回到文章前面章节的故事,辉哥发起主导了一个名为“BIG STO
PROGRAMME”的项目,由辉哥,欧阳和ELLA小姐姐一起出资参与,并且已经完成了天使轮(A资本投资),A轮(I资本),项目发展不错,正准备到美国做STO申请。
我们假设以下为对应的合格投资人钱包地址:
辉哥地址:0xD1F7922e8b78cBEB182250753ade8379d1E09949
欧阳地址:0x17b1177E0491D25a13a8329a8D2C46621b6ca19F
ELLA地址:0x3D7DfB80E71096F2c4Ee63C42C4D849F2CBBE363
A资本地址:0x8fc02f03c15179f8C6D37C3a29FE7A338DC68192
I资本地址:0xcE689dBB962DbF45534Af13e4414cCB2dFC78c30
交易员: 0xDD55dA111fBfcc671966D138dE7DFA249a6e76cC
我们假设创始团队(创始股),A资本(天使轮),I资本(A轮)这些投资方对应的价格,基金到期时间都不相同,可以定义tranches级别分别为1级,2级,3级等。
tranche:
00000000000000000000000000000001 创始团队
00000000000000000000000000000002 天使轮
00000000000000000000000000000003 A轮
6.1 创始团队发起原始TOKEN
“ BIG STO PROJECT”项目,由辉哥/欧阳/ELLA创始发起,分配总额度和TRANCHE属性。
根据参与3人的投入的资金量,总发行量为1000万,由辉哥账号创建,分配份额如下:辉哥 400万,欧阳 300万,ELLA 300万。
1)首先创建PartialFungibleToken合约,设置初始属性如下:
totalSupply = 0;
name = “Big STO Project”;
symbol = “BST”;
2)调用接口增发1000万TOKEN。
mint("00000000000000000000000000000001",
"0xD1F7922e8b78cBEB182250753ade8379d1E09949", "10000000.000000000000000000",
"the founding team share tranche")
3) 辉哥转发给欧阳,ELLA对应份额
sendTranche("00000000000000000000000000000001",
"0x17b1177E0491D25a13a8329a8D2C46621b6ca19F","3000000.000000000000000000",
"创始团队欧阳份额,1级tranche") ;
sendTranche("00000000000000000000000000000001",
"0x3D7DfB80E71096F2c4Ee63C42C4D849F2CBBE363","3000000.000000000000000000",
"创始团ELLA份额,1级tranche") ;
6.2 A资本参与天使轮
A资本参与天使轮,再发行200万份额,其中辉哥占有100万,A占有100万,锁仓1年;跟辉哥签订对赌条约,公司营收在2018年达到1000万元,设置操作员账号质押辉哥TOKEN份额。
1) 增发200万
mint("00000000000000000000000000000002",
"0xD1F7922e8b78cBEB182250753ade8379d1E09949", "200000.000000000000000000",
"the spinel captital joined in.")
2)A占有100万; 辉哥占有100万,有业绩对赌;
sendTranche("00000000000000000000000000000002",
0x8fc02f03c15179f8C6D37C3a29FE7A338DC68192","1000000.000000000000000000",
"A资本参与天使轮,锁仓1年") ;
3)辉哥业绩对赌,TOKEN指定交易员授权;
authorizeOperator("0xDD55dA111fBfcc671966D138dE7DFA249a6e76cC")
4)该项目进展顺利,对赌失效
该项目进展顺利,对赌失效,辉哥要撤回授权。
revokeOperator("0xDD55dA111fBfcc671966D138dE7DFA249a6e76cC")
6.3 I资本参与A轮
该项目到2018年11月份就完成了全年目标,项目发展不错。I资本决定参与A轮。再发行200万TOKEN,其中辉哥占有50万,A占有50万,I占有100万。
1) 增发200万TOKEN
mint("00000000000000000000000000000001",
"0xD1F7922e8b78cBEB182250753ade8379d1E09949", "2000000.000000000000000000", "I
CAPITAL joined in!")
sendTranche("00000000000000000000000000000003",
0x8fc02f03c15179f8C6D37C3a29FE7A338DC68192","500000.000000000000000000",
"A资本参与A轮,锁仓1年") ;
sendTranche("00000000000000000000000000000003",
"0xcE689dBB962DbF45534Af13e4414cCB2dFC78c30","1000000.000000000000000000",
"I资本参与A轮,锁仓1年") ;
2)跟辉哥对赌2级股权100万
I资本跟辉哥有对赌条款,约定到2018年年底总营收能到1500万人民币,把辉哥天使轮对应的TOKEN质押,授权交易员。
authorizeOperatorTranche("00000000000000000000000000000002", "
0xDD55dA111fBfcc671966D138dE7DFA249a6e76cC")
结果项目进展又特别顺利,对赌失效,辉哥撤回授权。
revokeOperatorTranche("00000000000000000000000000000002", "
0xDD55dA111fBfcc671966D138dE7DFA249a6e76cC")
6.4 A质押50万2级TOKEN,找I资本融资5000万,后续转让100万给I资本,退出该项目
authorizeOperatorTranche("00000000000000000000000000000002", "
0xDD55dA111fBfcc671966D138dE7DFA249a6e76cC")
后来A资本和I资本代表人到交易所办理了转让交割手续。
operatorSendTranche("00000000000000000000000000000002",
"0x8fc02f03c15179f8C6D37C3a29FE7A338DC68192",
"0xcE689dBB962DbF45534Af13e4414cCB2dFC78c30", "500000.000000000000000000",
"A资本转让2级TOKEN给IDG资本", "由Polyman交易所交易员Mask办理交割")
6.5 ELLA退出运营,销毁对应代币
ELLA因为要跟男票一起去苏南农村做ELLA山庄,享受田园生活。所以申请退出“BIG STO PROJECT”运营。公司回购300万,销毁对应代币。
burn("00000000000000000000000000000001",
"0x3D7DfB80E71096F2c4Ee63C42C4D849F2CBBE363", "3000000.000000000000000000",
"ELLA退出运营,缩股销毁TOKEN。")
7.代码部署和测试
故事讲完了,接下来就是部署测试。
这部分的代码量有点大,辉哥就不再全部列出了。有需要的小伙伴可以去知识星球下载来运行。
该工程代码还未经过商用测试,只作为练习使用。