
ERC-3525 标准是以太坊社区批准通过的半匀质化通证(Semifungible Token, 亦称为半同质化通证,简称 SFT)标准,由 Solv Protocol 提出。
ERC-3525 标准定义了一类新型的数字资产,具有以下突出优势:
与 ERC-721 标准兼容,具有唯一 ID 和可视化外观,可复用现有的大量 NFT 基础设施;
可拆分、可合并、可计算;
具有账户特征,可以容纳其他数字资产,如 ERC-20 通证、NFT 等,并支持在若干 SFT 之间的转账操作;
可以对外观、功能、资产存储、锁定、转账等各方面进行编程,并且为元数据的结构化进行了特别的优化,以支持动态变化、复杂金融逻辑等高级功能的开发。
由于具有以上的优势,ERC-3525 特别适合用来描述金融工具、数字票证、数字合同等高级数字资产,同时也正在被试用于 Web3 虚拟物品、动态 NFT 艺术品、虚拟装备、真实世界资产(RWA)通证化等领域,得到了非常广泛的关注。
Solv Protocol 已经将 ERC-3525 参考实现开源,为有兴趣了解和快速尝试这一全新通证技术的开发者提供有力的支持。这一参考实现同时以开源代码库和 NPM 模块包的形式提供,开发者可以以此为起点,在这个参考实现代码的基础之上通过改写和扩展,开发自己的 ERC-3525 应用。
本文档引导读者安装、配置和部署 ERC-3525 官方参考实现,并引导读者在此基础上开发一个简单的 ERC-3525 通证合约。这个合约没有任何特别的功能,但开发、测试和部署这个合约的过程却是通用的。开发者如果了解和熟练掌握了这个开发过程,就能够在此基础上开发复杂的、具有业务功能的 ERC-3525 通证合约了。
本文档内容基于 ERC-3525 参考实现 1.1.0 版(2022 年 12 月发布)。
ERC-3525 参考实现是基于 Hardhat 框架、以 Solidity 语言为主开发的。我们推荐读者在学习 ERC-3525 开发之前,首先掌握以下知识和技能:
Solidity 语言和 EVM 智能合约开发的基本知识
Hardhat 智能合约开发框架的基本实用技能
当然,要使用 Hardhat 框架,也必须对于 JavaScript 或者 TypeScript 语言有基本的了解。本文档使用 TypeScript 进行介绍,但对于有经验的开发者来说,基于本文档介绍的内容,很容易可以用 JavaScript 完成相同的工作。
我们推荐对于 Hardhat 不熟悉的读者首先通过 Hardhat 的官方文档 (https://hardhat.org/docs) 来熟悉这一流行的智能合约开发框架。
建议读者在 macOS 或 Linux 的命令行环境下进行 ERC-3525 开发。如果读者使用 Windows,我们强烈建议读者首先安装 Windows Subsystem for Linux (WSL),然后在 WSL 环境中进行如下操作。
读者可以选择自己喜欢的任何一种代码编辑工具来编写代码,但我们推荐使用 Visual Studio Code,因为 Hardhat 的开发者 Nomic Foundation 为 Visual Studio Code 开发了一款 Solidity 插件,可以帮助提升 Solidity 和 Hardhat 开发效率。

此外,Hardhat 开发中大量使用 JavaScript 或者 TypeScript 编写测试用例,Visual Studio Code 本身对于 JavaScript 和 TypeScript 就提供了良好的支持。
首先在命令行环境下通过如下命令准备项目目录。本示例项目名称为 erc3525-getting-started。
mkdir erc3525-getting-started |
在命令行输入以下命令(以MacOSX为例)
npx hardhat |
将看到以下界面

选择“Create a TypeScript project”后,Hardhat 会提示若干问题,读者直接通过回车选择缺省选项即可。

全部选择完毕后,系统自动执行一系列安装和准备工作。结束后,使用 Visual Studio Code打开目录,你可以看到如下项目结构:

下面,通过 npm 命令在当前目录安装 ERC-3525 参考实现
npm install @solvprotocol/erc-3525@latest |
由于我们需要用到 OpenZeppelin 的 String 库,因此需要使用以下命令安装 OpenZepplin:
npm install @openzeppelin/contracts@latest |
安装完毕之后,可打开 package.json 文件,应该能够看到 @solvprotocol/erc-3525 相关信息,表明已经成功安装。

为了简单起见,我们规避复杂的业务逻辑,以一个最简单的应用案例来讲解 ERC-3525 的代码开发过程。这个案例中,我们创建一个最简单的 ERC-3525 通证,它只具备 ERC-3525 的基本功能,没有额外的功能。不过我们将为它创建一个“外表”,使它可以用 SVG 动态图像来显示内部的状态。
在 Hardhat 项目创建过程中,自动添加了一个实例代码文件 Lock.sol。本范例中不需要这文件,因此首先请删除 contracts/Lock.sol,并在 contracts 目录中新建文件 ERC3525GettingStarted.sol,代码如下:
// SPDX-License-Identifier: MIT contract ERC3525GettingStarted is ERC3525 { constructor(address owner_) ERC3525("ERC3525GettingStarted", "ERC3525GS", 18) { } function mint(address to_, uint256 slot_, uint256 amount_) external { require(msg.sender == owner, "ERC3525GettingStarted: only owner can mint"); _mint(to_, slot_, amount_); } |
在以上代码中,我们创建了一个新的合约 ERC3525GettingStarted。这个合约从 ERC3525 参考实现合约中派生,其构造函数直接调用 ERC3525 合约的构造函数,传入合约的全名、符号和小数位数,并为 owner 赋值。我们并且添加了一个 mint() 函数,确保只有 owner 能够铸造这个 ERC-3525 通证。具体的铸造过程,是通过调用 ERC3525 合约当中的 _mint() 实现的,这样我们就复用了 ERC3525 合约的参考实现,得到了一个最简单的 ERC-3525 通证合约。
有了 ERC-3525 的参考实现,很多基本功能都可以直接调用相应的函数实现,开发者可以只聚焦于业务逻辑和创新功能,这样就大大简化了相关的开发。
代码编写完毕之后,在命令行执行以下命令进行编译:
npx hardhat compile |
编译成功结果如下:

使用 Hardhat 框架开发智能合约的主要好处之一是可以进行自动化测试。下面我们介绍如何使用 Hardhat 的测试框架对 ERC3525GettingStarted 合约进行自动化测试。
测试代码集中在 test 目录下。同样,我们首先删除 test/Lock.ts,然后在 test 目录下新建ERC3525GettingStarted.ts,代码如下:
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; "ERC3525GettingStarted"); deployGettingStartedFixture); await loadFixture(deployGettingStartedFixture); gettingStarted.connect(otherAccount) .mint(owner.address, slot, value)) .to.be.revertedWith( await loadFixture(deployGettingStartedFixture); .to.eq(otherAccount.address); |
在上面的测试代码中,我们编写了一个测试夹具和三个测试用例,分别测试了 owner 的正确性、mint 的操作权限和 mint 操作的功能。这些用例遵循了 Hardhat 中编写智能合约测试代码的标准方式,读者可通过 Hardhat 官方文档学习,此处不再赘述。
下面实际运行测试。方法是在项目主目录执行如下命令:
npx hardhat test |
执行结果如下:

这表明我们的智能合约成功通过了所有三个测试用例。
ERC-3525 最初的设计目标是表达复杂的金融资产,特别是数字票据。既然是数字资产,就必须支持可拆分、可合并,能够像 ERC-20 通证一样进行各种数学计算。另一方面,ERC-3525 超越 ERC-20 的重要一点,就是具有可视化的外在形象,唯有如此才能够向用户传达丰富的信息,使复杂数字资产的复杂性能够被表达出来。这是 ERC-3525 选择兼容 ERC-721 的主要动机。因此,ERC-3525 支持元数据,并且通过从 IERC721Metadata 接口继承而来的 tokenURI 函数返回资源的 URL,或者直接返回图片的内容数据。在 NFT 当中,普遍的做法是将图片放在链外的存储上,然后让 tokenURI 函数返回其 URL。这种设计其实有一个非常大的风险,就是在 NFT 出售以后,控制存储的人可以更换 URL 上的图片,这样买家手里的 NFT 实际上就被篡改了。为了解决这个问题,大多数 NFT 采用了 IPFS 存储,通过哈希值来确保图片资源的唯一性。即使如此,也难以防范一些破坏,比如将 IPFS 上存储的图片资源删除。
ERC-3525 的设计初衷是为了表达金融资产,金融资产的信息非常敏感和重要,决不能被更换和删除。因此,Solv 建议直接将展现层用 SVG 表达,并直接放在链上。具体方法就是让 tokenURI 函数直接返回 SVG 代码片段,而不是指向图像资源的链接。
在 ERC3525GettingStarted 合约中加入以下函数:
function tokenURI(uint256 tokenId_) public view virtual override returns (string memory) { |
这将生成一张黑色背景的 SVG 图像,显示如下:

注意,其中 Slot、TokenId 和 Balance 的数值都是直接从 ERC-3525 通证的当前状态中提取的。
Hardhat 框架自带一个以太坊本地节点的实现,特别针对开发过程中的需求做了不少优化。我们推荐在开发调试过程中将合约部署到这个节点上。
在deploy目录修改deploy.ts如以下内容:
import { ethers } from "hardhat"; |
打开一个新的 Terminal,运行hardhat 内建节点
npx hardhat node |
运行结果如下(为节约篇幅,省略其它账号):

在项目主目录执行以下命令:
npx hardhat run --network localhost scripts/deploy.ts |
执行成功后将看到如下结果。注意红框的地址部分,后面的交互会用到。

智能合约部署之后,可以通过 hardhat console 命令与之进行交互,这是 Hardhat 节点的一个重要优势,能够大大简化测试和调试阶段的工作。输入以下命令:
npx hardhat console --network localhost |
交互指令及结果如下:
~/Sources/erc3525-getting-started$ npx hardhat console --network localhost
... |
读者可以自行输入一些 TypeScript 代码来尝试与智能合约进行交互。
在开发环境下测试和调试完毕之后,就需要部署到测试链上了。测试链提供了基本等同于主链的运行环境,但在上面进行测试和调试无需缴纳高昂的 gas 费用。另一方面,有些智能合约的功能必须在测试链上才能运行,比如与 Oracle 的交互,在开发用的虚拟节点上是不支持的。我们这个案例非常简单,用不到 Oracle,但是作为一个原则,一个智能合约在上主链之前,一定是要在测试链上运行测试无误才可以。
以太坊已经于 2022 年 9 月 15 日升级到 POS,因此之前几个流行的测试链,如 Ropsten, Rinkeby, Kovan 等已经被废弃。现在主要的两个测试链是 Goerli 和 Sepolia。其中 Goerli 历史较长,完全开放,比较适合于测试复杂的智能合约,而 Sepolia 较新,由一组确定的验证者节点组成,不能随意加入,是当前进行 DApp 开发测试的首选测试链。在这个例子中,我们选择 Sepolia 测试链。
为了部署在 Sepolia 测试链,读者需要通过 https://www.infura.io/ 申请 infura API KEY。我们假定读者已经完成这项工作,下面直接介绍部署的过程。
修改 hardhat.config.ts 如下:
import { HardhatUserConfig } from "hardhat/config"; |
然后在 Terminal 命令行环境中执行以下命令,设置 infura api key 和 private key:
export INFURA_KEY=<YOUR_INFURA_KEY>; export PRIVATE_KEY=<YOUR_PRIVATE_KEY>; |
请注意,将<YOUR_INFURA_KEY> 替换成你申请的 infura API KEY,将 <YOUR_PRIVATE_KEY> 替换成私钥。强烈建议测试网和主网的私钥隔离,不要使用已有的主网私钥。
在 Sepolia 测试网中进行测试需要准备一些测试币,即 Sepolia FaucETH。读者可以到 https://faucet.sepolia.dev/ 去申领一些 FaucETH 以供测试之用。
这些准备工作做好之后,就可以执行脚本进行部署了:
npx hardhat run --network sepolia scripts/deploy.ts |
执行成功后,结果如下。请注意红框中的地址,我们将在下一步中使用到。

下面我们来铸造一个 ERC3525GettingStarted 通证。我们采用的方法是使用 TypeScript 调用合约功能进行通证铸造,这与在 Web3 DApp 开发中的模式是一致的。
首先在 scripts 目录下新建文件 mint.ts ,代码如下:
import { ethers } from "hardhat"; |
请注意,将代码中 <部署合约地址> 替换成上一节红框中的地址。
最后,执行以下命令:
npx hardhat run --network sepolia scripts/mint.ts |
这样,我们就成功的铸造了一张 ERC3525GettingStarted 通证。
怎么确认这一点呢?可以到 Sepolia Etherscan (https://sepolia.etherscan.io/) 上去查看铸造出来的 token。在浏览器地址栏里输入:
https://sepolia.etherscan.io/address/<部署合约地址>
请注意,将<部署合约地址> 替换成上一节红框中的地址。
恭喜你!如果一切顺利,你就成功的开发和部署第一个 ERC-3525 通证了,可以对它进行各种新的操作了,比如拆分、合并、在两个通证之间转账,等等,赶快尝试一下吧!
本文完整的示例代码参见 GitHub (https://github.com/solv-finance/erc3525-getting-started)。
本教程对于 ERC-3525 半匀质化通证(SFT)应用开发的过程进行了简明扼要的阐述。读者可以由此出发,开发具有丰富功能和高级外观的 SFT。当然,如果想要深入学习 ERC-3525 的知识和开发技术,这只是一个起点,我们推荐您从以下几个方面入手深入学习:
阅读 ERC-3525 白皮书(https://whitepaper.sftlabs.io/SFT%20Whitepaper.pdf)
研究 ERC-3525 参考实现 (https://github.com/solv-finance/erc-3525)
研究 SFTLabs 官方提供的 Showroom 案例 (https://showroom.sftlabs.io/showroom/)
研究 ERC-3525 技术专家开发的以太币现金钞案例 Crypto Notes (https://cryptonotes.fun/)
我们也将继续发表一系列文章和教程来帮助开发者掌握 ERC-3525 技术。
我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当
我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm
我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI
这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub
我正在玩HTML5视频并且在ERB中有以下片段:mp4视频从在我的开发环境中运行的服务器很好地流式传输到chrome。然而firefox显示带有海报图像的视频播放器,但带有一个大X。问题似乎是mongrel不确定ogv扩展的mime类型,并且只返回text/plain,如curl所示:$curl-Ihttp://0.0.0.0:3000/pr6.ogvHTTP/1.1200OKConnection:closeDate:Mon,19Apr201012:33:50GMTLast-Modified:Sun,18Apr201012:46:07GMTContent-Type:text/plain
无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD
在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList()Obt
目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称
@作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors 1、什么是behaviors 2、behaviors的工作方式 3、创建behavior 4、导入并使用behavior 5、behavior中所有可用的节点 6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors 1、什么是behaviorsbehaviors是小程序中,用于实现
遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg