练习 1:房地产通证化

完整代码链接: https://github.com/andrejrakic/rwa-primerarrow-up-right

调用Zillow API

在创建我们的通证之前,让我们看看我们将从哪里获取数据。

本练习中, 我们将使用 Zillow's API arrow-up-right

circle-info

这是一个测试用的API接口。如果想要了解更多关于此API的详细信息,请参考 Zillow 官方文档arrow-up-right

让我们先从编写 JavaScript 函数开始,这个函数能帮助我们获取创建NFT所需的信息。

为了构建NFT的元数据,我们需要获取如下数据:

  • 房产地址

  • 建造年份

  • 土地面积

  • 居住面积

  • 卧室数量

我们可以利用 API 来获取这些信息。在将其添加进智能合约前,我们会使用 Functions Playgroundarrow-up-right 来测试这段 JavaScript 代码。

// Import the ethers library from npm
const { ethers } = await import("npm:ethers@6.10.0");
const Hash = await import("npm:ipfs-only-hash@4.0.0");

// Make an HTTP request to fetch real estate data
const apiResponse = await Functions.makeHttpRequest({
  url: `https://api.bridgedataoutput.com/api/v2/OData/test/Property('P_5dba1fb94aa4055b9f29696f')?access_token=6baca547742c6f96a6ff71b138424f21`,
});

// Extract relevant data from the API response
const realEstateAddress = apiResponse.data.UnparsedAddress;
const yearBuilt = Number(apiResponse.data.YearBuilt);
const lotSizeSquareFeet = Number(apiResponse.data.LotSizeSquareFeet);
const livingArea = Number(apiResponse.data.LivingArea);
const bedroomsTotal = Number(apiResponse.data.BedroomsTotal);

const metadata = {
  name: "Real Estate Token",
  attributes: [
    { trait_type: "realEstateAddress", value: realEstateAddress },
    { trait_type: "yearBuilt", value: yearBuilt },
    { trait_type: "lotSizeSquareFeet", value: lotSizeSquareFeet },
    { trait_type: "livingArea", value: livingArea },
    { trait_type: "bedroomsTotal", value: bedroomsTotal }
  ]
};

// Stringify the JSON object
const metadataString = JSON.stringify(metadata);

const ipfsCid = await Hash.of(metadataString);
console.log(ipfsCid);

return Functions.encodeString(`ipfs://${ipfsCid}`);

定价信息

我们还将通过单独调用同一个API来收集定价信息arrow-up-right

将代码存储于链上

我们的JavaScript代码已经准备就绪。接下来,我们将创建首个智能合约以将代码存储在区块链上。 创建一个名为FunctionsSource.sol的合约。

circle-info

在智能合约中存储 JavaScript 代码可能会有些棘手。请注意以下几点:

  • 用一对双引号包裹每一行代码

  • 在代码中使用单引号和反引号来转义字符串

  • 如果你想将所有 JavaScript 代码存储在一行中,那么请确保分号的正确位置

运用工厂模式发行 ERC-1155 通证

我们的通证化资产项目由多个不同的智能合约组成,这些合约需要我们在本次训练营期间开发。下图展示了整个架构,其中我们将在每个部分使用的 Chainlink 服务用蓝色字母标记出来。

创建 ERC1155Core.sol

首先,我们需要创建 ERC1155Core.sol文件。这个智能合约包含了所有与 ERC-1155 标准相关的逻辑,此外还有我们用于初始发行以及后续通过 CCIP 进行跨链转账时会用到的自定义 mintmintBatch 函数。

创建 CrossChainBurnAndMintERC1155.sol

该合约将继承 ERC1155Core.sol,并在其基本的 ERC-1155 功能基础上进行扩展以支持通过Chainlink CCIParrow-up-right 实现的跨链转账。

任何 NFT 都是通过智能合约实现的,而这些智能合约本质上与单一区块链相连。这意味着任何跨链 NFT 的实现至少需要两个区块链上的两个智能合约并在它们之间的互联。

考虑到以上内容,跨链 NFT 可以通过三种方式进行实施:

  • 销毁与铸造: NFT 持有者将其 NFT 放入源链上的智能合约中并将其销毁,实际上是从这条区块链上删除该 NFT。一旦完成这一动作,目标区块链上将从相应的智能合约中创造一个等价的 NFT。这一过程可以在两个方向上发生。

  • 锁定与铸造: NFT 持有者在源链上的智能合约中锁定他们的 NFT,同时在目标区块链上创造一个等效的 NFT。当持有者想要将NFT移回时,他们需销毁目标链上的 NFT,从而解锁原始区块链上的 NFT。

  • 锁定与解锁: 同一 NFT 集合在多个区块链上进行铸造。NFT 持有者可在源区块链上锁定自己的 NFT,以在目标区块链上解锁等效的 NFT。这意味着任何时候只有一个 NFT 的实例能处于活跃状态,即使这个 NFT 在同一时间存在跨区块链的多个副本。

我们将要实现的是销毁与铸造机制。

每种情形都需要一个位于中间层的跨链消息传递协议,用于从一条区块链向另一条区块链发送数据指令。

如果你尝试编译这份合约,你会看到一个编译错误。这是因为合约中有对来自utils文件夹中的Withdraw.sol智能合约的导入语句。由于我们会将LINK代币充值到CrossChainBurnAndMintERC1155.sol名下以支付CCIP费用,因此Withdraw.sol将包含从本合约中提取任何ERC-20代币及原生代币的逻辑。

./utils文件夹中创建Withdraw.sol。

创建RealEstatePriceDetails.sol

该辅助智能合约将用于周期性地使用Chainlink Automation和Chainlink Functions服务来获取我们现实世界资产的价格详情。为了使其正常运作,我们已经创建了JavaScript脚本并将其内容放入到FunctionsSource.sol中。

创建 RealEstateToken.sol

最后来创建我们的主合约,它将继承上述所有的合约。

部署 RealEstateToken.sol 到 Avalanche Fuji 测试网

确保将 Solidity 编译器优化器设置为200次运行,并将EVM版本设置为 "Paris"。

为了将RealEstateToken.sol代币部署到 Avalanche Fuji 测试网,您需要向构造函数提供以下信息:

  • uri_: ""(这是ERC-1155代币的元数据基础URI,我们将其置为空)

  • ccipRouterAddress: 0xF694E193200268f9a4868e4Aa017A0118C9a8177

  • linkTokenAddress: 0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846

  • currentChainSelector: 14767482510784806043

  • functionsRouterAddress: 0xA9d587a00A31A52Ed70D6026794a8FC5E2F5dCb0

创建 Issuer.sol

正如我们之前所提到的,发行部分超出了本次训练营的范围。因此为了简化操作,我们将仅仅通过铸造一个模拟版本的房地产以 ERC-1155 通证的形式直接到 Alice 的地址,来满足这次练习的需要。

为了实现这一点,我们将使用Issuer.sol辅助合约。

部署 Issuer.sol 到 Avalanche Fuji 测试网

要将Issuer.sol部署到 Avalanche Fuji 上,我们需要向其构造函数提供以下信息:

  • realEstateToken: RealEstateToken.sol 合约的地址,这个合约我们之前已经部署过了。

  • functionsRouterAddress: 0xA9d587a00A31A52Ed70D6026794a8FC5E2F5dCb0

调用RealEstateToken.sol的setIssuer函数

在Avalanche Fuji上,调用 RealEstateToken.solsetIssuer函数,并提供以下信息:

  • _issuer: 先前部署的 Issuer.sol的地址

你使用Chainlink Functions的订阅来支付、管理和追踪Functions请求。

  1. 点击Connect wallet:

  2. 阅读并接受Chainlink基金会的服务条款。然后点击 MetaMask.

  3. 确保你的钱包已连接至 Avalanche Fuji 测试网。如果没有,点击页面右上角的网络名称,并选择Avalanche Fuji

  4. 点击 Create Subscription:

  1. 提供一个电子邮件地址和订阅名称

  2. 确认订阅的创建

  3. 在创建订阅后,Functions界面会提示你为订阅充值。点击 Add funds 并充值 10 个 LINK:

  4. 为订阅充值后,将Issuer.solRealEstateToken.sol合约添加为该订阅的消费者。

  5. 记住你的订阅ID

调用 Issuer.sol 的issue函数

为了发行ERC-1155代币给Alice,用你部署该合约所使用的地址调用Issuer.sol的issue函数,并提供以下信息:

  • to: Alice的钱包地址 (一个你的任意地址即可)

  • amount: 20

  • subscriptionId: 刚刚创建的Chainlink Functions的订阅ID

  • gasLimit: 300000

  • donID: 0x66756e2d6176616c616e6368652d66756a692d31000000000000000000000000 (fun-avalanche-fuji-1)

circle-exclamation
  1. Chainlink Automation Apparrow-up-right中,点击蓝色的 Register new Upkeep 按钮。

  2. 在 Trigger 选项中选择 Time-based

  3. Target contract address中输入RealEstateToken.sol合约地址

  4. 成功输入合约地址和ABI后,以CRON表达式arrow-up-right的形式指定您的时间安排。我们将每24小时调用一次updatePriceDetails函数。我们需要的CRON表达式是 0 0 * * * (这意味着该函数将在每天的00:00被触发)

  1. 提供你选择的"Upkeep name". 设置300000作为"Gas limit". 设置5 LINK作为 "Starting balance (LINK)"

  2. 点击Register upkeep并在MetaMask中确认交易

调用 RealEstateToken.sol 的 setAutomationForwarder函数

在 Chainlink Automation 网络中,每个已注册的 upkeep 都有其独特的Forwarder合约。你可以在你的 Chainlink Automation Upkeep仪表板上找到它的地址。

调用RealEstateToken.sol的setAutomationForwarder函数并提供:

  • automationForwarderAddressForwarder合约的地址

启用跨链转账

按照以下步骤启用跨链转账:

  1. 在新的区块链上部署RealEstateToken.sol。

  2. 调用enableChain函数,并为之前在其他区块链上部署的每一个 RealEstateToken.sol 提供chainSelectorxNftAddress ccipExtraArgs。这里仅指Avalanche Fuji的相关参数。

  3. 在之前部署了RealEstateToken.sol的所有其他区块链上(目前仅限 Ethereum Sepolia),调用enableChain函数,并提供新区块链的 chainSelectorxNftAddress ccipExtraArgs,即你刚部署了新的 RealEstateToken.sol 的区块链。

Last updated