跳转至

使用Hardhat部署至Moonbeam

概览

Hardhat是一个灵活可拓展的以太坊开发环境,它能帮助开发者简化智能合约开发过程。 由于Moonbeam与以太坊兼容,您可以在Moonbeam上使用Hardhat开发和部署智能合约。

Hardhat采用基于任务的开发方式,开发者可以定义和执行任务以执行特定操作。这些操作包括编译和部署合约、运行测试等等。这些任务的可配置性很高,您可以创建、自定义和执行不同任务来满足您的需求。

您还可以通过使用插件来扩展Hardhat的功能。插件是外部扩展应用,它们可与Hardhat集成以提供额外的功能与工具来简化工作流程。有些插件包括了常见的以太坊库,例如Ethers.jsviem和为Chai Assertion库添加以太坊功能的插件等等。 所有这些插件都可用于在Moonbeam上扩展您的Hardhat项目。

本指南将简要介绍Hardhat,并向您展示如何使用Hardhat在Moonbase Alpha测试网上编译、部署和调试以太坊智能合约。本指南还适用于Moonbeam、Moonriver或 Moonbeam开发节点。

请注意,尽管Hardhat带有一个Hardhat Network组件,它能提供一个本地开发环境,但您应该使用本地Moonbeam开发节点来代替。您可以像连接任何其他网络一样将 Moonbeam开发节点与Hardhat相连。

查看先决条件

在开始之前,您将需要准备以下内容:

创建Hardhat项目

如果您还未有Hardhat项目,您将需要创建一个。为此,您可以执行以下步骤:

  1. 为您的项目创建一个目录

    mkdir hardhat && cd hardhat
    
  2. 初始化将创建package.json文件的项目

    npm init -y
    
  3. 安装Hardhat

    npm install hardhat
    
  4. 创建Hardhat项目

    npx hardhat init
    

    注意事项

    npx用于运行安装在项目中的本地可执行文件。虽然Hardhat可以全网安装,但是我们建议在每个项目中进行本地安装,这样您可以按项目控制项目版本。

  5. 系统将会显示菜单,允许您创建新的项目或使用范本项目。在本示例中,您可以选择Create an empty hardhat.config.js,这会为您的项目创建一个Hardhat配置文件。

npx hardhat init 888    888                      888 888               888 888    888                      888 888               888 888    888                      888 888               888 8888888888  8888b.  888d888 .d88888 88888b.   8888b.  888888 888    888     "88b 888P"  d88" 888 888 "88b     "88b 888 888    888 .d888888 888    888  888 888  888 .d888888 888 888    888 888  888 888    Y88b 888 888  888 888  888 Y88b. 888    888 "Y888888 888     "Y88888 888  888 "Y888888  "Y888
👷 Welcome to Hardhat v2.22.2 👷‍
 What do you want to do? …   Create a JavaScript project   Create a TypeScript project   Create a TypeScript project (with Viem)   Quit

Hardhat配置文件

设置Hardhat配置文件是您Hardhat项目的开始。它定义了您Hardhat项目的不同设定以及可选项。比如使用的Solidity编译器版本以及您将部署智能合约的目标网络。

第一步,您的 hardhat.config.js 应包含以下内容:

/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
  solidity: '0.8.20',
};

在这个例子中,您会使用 0.8.20 版本的Solidity编译器版本;但是如果您的其他智能合约需要更新的版本,请记得在这里更改版本号。

下一步,您需要修改您的配置文件,在其中依据您部署智能合约的目标网络来添加网络配置。部署在Moonbeam网络需要配置以下这些选项:

  • url - 节点的RPC 端口
  • chainId - 链ID,来验证网络是否正确
  • accounts - 用来部署智能合约及与其交互的账号。您可以填入一组账户私钥的array,或使用HD钱包

这个例子中的网络为Moonbase Alpha,您也可以修改这个配置以适配其他Moonbeam网络:

module.exports = {
  solidity: '0.8.20',
  networks: {
    moonbeam: {
      url: 'INSERT_RPC_API_ENDPOINT', // 输入您的RPC URL
      chainId: 1284, // (hex: 0x504),
      accounts: ['INSERT_PRIVATE_KEY'],
    },
  },
};
module.exports = {
  solidity: '0.8.20',
  networks: {
    moonriver: {
      url: 'INSERT_RPC_API_ENDPOINT', // 输入您的RPC URL
      chainId: 1285, // (hex: 0x505),
      accounts: ['INSERT_PRIVATE_KEY'],
    },
  },
};
module.exports = {
  solidity: '0.8.20',
  networks: {
    moonbase: {
      url: 'https://rpc.api.moonbase.moonbeam.network', // 输入您的RPC URL
      chainId: 1287, // (hex: 0x507),
      accounts: ['INSERT_PRIVATE_KEY'],
    },
  },
};
module.exports = {
  solidity: '0.8.20',
  networks: {
    dev: {
      url: 'http://127.0.0.1:9944', // Insert your RPC URL here
      chainId: 1281, // (hex: 0x501),
      accounts: ['INSERT_PRIVATE_KEY'],
    },
  },
};

请记住

以上代码只是示例,请千万不要在您的Javascript文件中储存私钥

如果您想要在项目中使用插件,您需要安装插件并将通过hardhat.config.js文件将其导入。当一个插件导入后,它会成为Hardhat Runtime Environment的一部分,您可以在任务,脚本或别的地方使用该插件。

在这个范例中,您可以安装 hardhat-ethers 插件并且将其导入配置文件,这个插件为Ethers.js代码库提供了一个方便的封装,用于网络交互。

npm install @nomicfoundation/hardhat-ethers ethers

您也需要另外安装 hardhat-ignition-ethers 插件以使用Hardhat Ignition部署智能合约。您可以使用以下命令安装插件:

npm install --save-dev @nomicfoundation/hardhat-ignition-ethers

To import both plugins, add the following require statements to the top of the Hardhat configuration file:

/** @type import('hardhat/config').HardhatUserConfig */
require('@nomicfoundation/hardhat-ethers');
require('@nomicfoundation/hardhat-ignition-ethers');

const privateKey = 'INSERT_PRIVATE_KEY';

module.exports = {
  solidity: '0.8.20',
  networks: {
     moonbase: {
      url: 'https://rpc.api.moonbase.moonbeam.network',
      chainId: 1287, // 0x507 in hex,
      accounts: [privateKey]
    }
  }
};

关于其他配置选项的信息,请阅读Hardhat文档中的Configuration部分。

合约文件

现在您已经完成项目的配置,可以开始创建您的智能合约了。接下来要部署的合约并不复杂,它被命名为Box,它能储存一个数值并且读取这个数值。

使用以下步骤添加合约:

  1. 创建contracts目录

    mkdir contracts
    
  2. 创建Box.sol文件

    touch contracts/Box.sol
    
  3. 打开文件,并为其添加以下合约内容:

    // contracts/Box.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.1;
    contract Box {
        uint256 private value;
    
        // 在数值变化时被调用
        event ValueChanged(uint256 newValue);
        // 在合约中储存新数值
        function store(uint256 newValue) public {
            value = newValue;
            emit ValueChanged(newValue);
        }
        // 读取最后储存的数值
        function retrieve() public view returns (uint256) {
            return value;
        }
    }
    

编译Solidity

下一步是编译Box.sol智能合约,您可以使用内置的compile任务。这个任务会在contracts目录中寻找Solidity文件并且根据hardhat.config.js中定义的版本与设置来编译这些文件。

您可以简单运行以下命令编译合约:

npx hardhat compile
npx hardhat compile Compiled 1 Solidity files successfully (evm target: paris). ls -l artifacts cache contracts hardhat.config.js node_modules package.json package-lock.json

编译后,将会创建一个artifacts目录:这保存了合约的字节码和元数据,为.json文件。您可以将此目录添加至您的.gitignore

如果您在编译后修改了合约内容,您可以用以上命令再次编译合约。Hardhat会自动识别更改内容并且重编译合约。如果没有更新内容,那将不会进行编译。如果有需求,您可以使用clean任务来强制重新编译,缓存文件会被清理且旧的artifect文件会被删除。

部署合约

您将使用Hardhat Ignition来部署合约,Hardhat Ignition是一个用于部署智能合约的声明式框架。它能够简化智能合约部署与测试中重复任务的管理。详细信息请参照Hardhat Ignition文档

设置Ignition模块的正确文件架构,请先创建一个名为ignition的文件夹和一个名为modules的子目录。然后在其中添加一个名为Box.js的新文件。您可以使用以下命令一次性完成这三个步骤:

mkdir ignition ignition/modules && touch ignition/modules/Box.js
接下来,您可以使用以下步骤来编写Hardhat Ignition模块:

从Hardhat Ignition模块导入buildModule函数 使用buildModule导出一个模块 使用getAccount方法选择部署者账户 部署Box合约 从模块返回一个对象。这使得Box合约可以在Hardhat测试和脚本中进行交互

  1. 从 Hardhat Ignition 模块导入 buildModule 函数
  2. 使用 buildModule 导出模块
  3. 使用 getAccount 方法选择部署账户
  4. 部署 Box 合约
  5. 从模块返回一个对象。这使得Box合约能够在Hardhat测试和脚本中交互
// 1.  Import the `buildModule` function from the Hardhat Ignition module
const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules");

// 2. Export a module using `buildModule`
module.exports = buildModule("BoxModule", (m) => {

  // 3. Use the `getAccount` method to select the deployer account
  const deployer = m.getAccount(0);

  // 4. Deploy the `Box` contract
  const box = m.contract("Box", [], {
    from: deployer,
  });
  // 5. Return an object from the module 
  return { box };
});

使用以下命令来运行脚本,部署Box.sol合约;您需要给这个命令定义一个部署合约的网络名称,这个名称已在hardhat.config.js中定义。如果您没有定义网络,hardhat将会默认在本地网络上部署合约

npx hardhat ignition deploy ./ignition/modules/Box.js --network moonbase

注意事项

如果您正在使用另一个Moonbeam网络,请确保您已指定正确的网络。网络名称需要与hardhat.config.js中所定义的网络相匹配。

您将会收到提示确认要部署到的网络。在您确认后几秒钟合约就会被部署,您将在终端中看到合约地址。

npx hardhat ignition deploy ./ignition/modules/Box.js --network moonbase
✅ Confirm deploy to network moonbase (1287)? … yes Hardhat Ignition 🚀
Deploying [ BoxModule ]
Batch #1 Executed BoxModule#Box
[ BoxModule ] successfully deployed 🚀
Deployed Addresses
BoxModule#Box - 0xfBD78CE8C9E1169851119754C4Ea2f70AB159289

恭喜您,您的合约已完成!请保存地址,用于后续与合约实例的交互。

与合约交互

使用Hardhat与新合约交互的方式有几种:您可以使用console任务来启用一个可交互的JavaScript控制台;或者您也可以创建一个脚本并用run任务来执行它。

使用Hardhat控制台

Hardhat控制台与任务和脚本使用同样的执行环境,因此它也能自动使用hardhat.config.js中定义的参数与插件。

执行以下命令开启Hardhat console

npx hardhat console --network moonbase

接下来,您可以执行以下步骤(一次输入一行):

  1. 创建一个Box.sol合约的本地实例

    const Box = await ethers.getContractFactory('Box');
    
  2. 使用合约地址,将本地实例连接至已部署的合约

    const box = await Box.attach('0xfBD78CE8C9E1169851119754C4Ea2f70AB159289');
    
  3. 与此合约交互。在本示例中,您可以调用store方法并存储一个简单的值

    await box.store(5);
    

交易将通过您在hardhat.config.js中定义的账户进行签署并传送至网络。后台输出将如下所示:

npx hardhat console --network moonbase
Welcome to Node.js v20.9.0. Type ".help" for more information. const Box = await ethers.getContractFactory('Box'); undefined
const box = await Box.attach('0xfBD78CE8C9E1169851119754C4Ea2f70AB159289'); undefined
await box.store(5); ContractTransactionResponse {
provider: HardhatEthersProvider { ... },
blockNumber: null,
blockHash: null,
index: undefined,
hash: '0x1c49a64a601fc5dd184f0a368a91130cb49203ec0f533c6fcf20445c68e20264',
type: 2,
to: '0xa84caB60db6541573a091e5C622fB79e175E17be',
from: '0x3B939FeaD1557C741Ff06492FD0127bd287A421e',
nonce: 87,
gasLimit: 45881n,
gasPrice: 1107421875n,
maxPriorityFeePerGas: 1n,
maxFeePerGas: 1107421875n,
data: '0x6057361d0000000000000000000000000000000000000000000000000000000000000005',
value: 0n,
chainId: 5678n,
signature: Signature { r: "0x9233b9cc4ae6879b7e08b9f1a4bfb175c8216eee0099966eca4a305c7f369ecc", s: "0x7663688633006b5a449d02cb08311569fadf2f9696bd7fe65417860a3b5fc57d", yParity: 0, networkV: null },
accessList: [],
blobVersionedHashes: null
}
await box.retrieve(); 5n

请注意您的地址将被标记为from,即合约地址,以及正在传送的data。现在,您可以通过运行以下命令来检索数值:

await box.retrieve();

您将看到5或者您初始存储的数值。

使用脚本

与部署脚本相同,您也可以创建一个脚本来与合约交互。您可以将脚本存入scripts目录,并使用内置的run来运行它。

首先,在scripts目录下创建一个set-value.js文件:

touch scripts/set-value.js

然后将以下代码复制到set-value.js文件中:

// scripts/set-value.js
async function main() {
  // Create instance of the Box contract
  const Box = await ethers.getContractFactory('Box');
  // Connect the instance to the deployed contract
  const box = await Box.attach('0xfBD78CE8C9E1169851119754C4Ea2f70AB159289');
  // Store a new value
  await box.store(2);
  // Retrieve the value
  const value = await box.retrieve();
  console.log(`The new value is: ${value}`);
}
main()
  .then(() => process.exit(0))
  .catch(error => {
    console.error(error);
    process.exit(1);
  });

运行以下命令来执行脚本:

npx hardhat run --network moonbase scripts/set-value.js

这个脚本应当返回2这个数值。

npx hardhat run --network moonbase scripts/set-value.js
The new value is: 2

使用Hardhat进行分叉

您可以使用Hardhat分叉(fork) 包括Moonbeam在内的任何EVM兼容链。分叉是在本地模拟实时Moonbeam网络,使您可以在本地测试环境中与已部署在Moonbeam上的合约交互。因为Hardhat的分叉是基于EVM实现,您可以通过MoonbeamHardhat支持的标准以太坊JSON-RPC方法与分叉网络交互。

您需要了解一些使用Hardhat进行分叉的注意事项。您无法与任何Moonbeam预编译合约及其函数交互。预编译是Substrate实现的一部分,因此无法在模拟的EVM环境中复制。这将阻止您与Moonbeam上的跨链资产和基于Substrate的功能(例如质押和治理)进行交互。

当前分叉Moonbeam存在一个问题,为了解决此问题,您需要先手动修补Hardhat。您可以通过GitHub上的问题和相关PR获取更多信息。

修补Hardhat

在开始之前,您需要应用一个临时补丁来解决RPC错误,直到Hardhat修复了根本问题。错误如下所示:

Error HH604: Error running JSON-RPC server: Invalid JSON-RPC response's result.

Errors: Invalid value null supplied to : RpcBlockWithTransactions | null/transactions: RpcTransaction Array/0: RpcTransaction/accessList: Array<{ address: DATA, storageKeys: Array<DATA> | null }> | undefined, Invalid value null supplied to : RpcBlockWithTransactions | null/transactions: RpcTransaction Array/1: RpcTransaction/accessList: Array<{ address: DATA, storageKeys: Array<DATA> | null }> | undefined, Invalid value null supplied to : RpcBlockWithTransactions | null/transactions: RpcTransaction Array/2: RpcTransaction/accessList: Array<{ address: DATA, storageKeys: Array<DATA> | null }> | undefined

要修补Hardhat,您需要打开您项目中的node_modules/hardhat/internal/hardhat-network/jsonrpc/client.js文件。接下来,添加addAccessList函数并更新_perform_performBatch函数。

现在,您可以移除预先存在的_perform_performBatch函数,并在其中添加以下代码片段:

  addAccessList(method, rawResult) {
    if (
      method.startsWith('eth_getBlock') &&
      rawResult &&
      rawResult.transactions?.length
    ) {
      rawResult.transactions.forEach((t) => {
        if (t.accessList == null) t.accessList = [];
      });
    }
  }
  async _perform(method, params, tType, getMaxAffectedBlockNumber) {
    const cacheKey = this._getCacheKey(method, params);
    const cachedResult = this._getFromCache(cacheKey);
    if (cachedResult !== undefined) {
      return cachedResult;
    }
    if (this._forkCachePath !== undefined) {
      const diskCachedResult = await this._getFromDiskCache(
        this._forkCachePath,
        cacheKey,
        tType
      );
      if (diskCachedResult !== undefined) {
        this._storeInCache(cacheKey, diskCachedResult);
        return diskCachedResult;
      }
    }
    const rawResult = await this._send(method, params);
    this.addAccessList(method, rawResult);
    const decodedResult = (0, decodeJsonRpcResponse_1.decodeJsonRpcResponse)(
      rawResult,
      tType
    );
    const blockNumber = getMaxAffectedBlockNumber(decodedResult);
    if (this._canBeCached(blockNumber)) {
      this._storeInCache(cacheKey, decodedResult);
      if (this._forkCachePath !== undefined) {
        await this._storeInDiskCache(this._forkCachePath, cacheKey, rawResult);
      }
    }
    return decodedResult;
  }
  async _performBatch(batch, getMaxAffectedBlockNumber) {
    // Perform Batch caches the entire batch at once.
    // It could implement something more clever, like caching per request
    // but it's only used in one place, and those other requests aren't
    // used anywhere else.
    const cacheKey = this._getBatchCacheKey(batch);
    const cachedResult = this._getFromCache(cacheKey);
    if (cachedResult !== undefined) {
      return cachedResult;
    }
    if (this._forkCachePath !== undefined) {
      const diskCachedResult = await this._getBatchFromDiskCache(
        this._forkCachePath,
        cacheKey,
        batch.map((b) => b.tType)
      );
      if (diskCachedResult !== undefined) {
        this._storeInCache(cacheKey, diskCachedResult);
        return diskCachedResult;
      }
    }
    const rawResults = await this._sendBatch(batch);
    const decodedResults = rawResults.map((result, i) => {
      this.addAccessList(batch[i].method, result);
      return (0, decodeJsonRpcResponse_1.decodeJsonRpcResponse)(
        result,
        batch[i].tType
      );
    });
    const blockNumber = getMaxAffectedBlockNumber(decodedResults);
    if (this._canBeCached(blockNumber)) {
      this._storeInCache(cacheKey, decodedResults);
      if (this._forkCachePath !== undefined) {
        await this._storeInDiskCache(this._forkCachePath, cacheKey, rawResults);
      }
    }
    return decodedResults;
  }

然后,您可以通过运行以下命令使用patch-package自动应用代码包:

npx patch-package hardhat

随即会创建一个patches目录,现在您可以分叉Moonbeam且在运行时不会遇到任何错误了。

分叉Moonbeam

您可以从命令行分叉Moonbeam或配置您的Hardhat项目以始终从您的hardhat.config.js文件运行此分叉。要分叉Moonbeam或Moonriver,需要用到您自己的端点和API密钥,您可以从端点提供商所支持的列表中获取。

要从命令行分叉Moonbeam,您可以从您的Hardhat项目目录中运行以下命令:

npx hardhat node --fork INSERT_RPC_API_ENDPOINT
npx hardhat node --fork INSERT_RPC_API_ENDPOINT
npx hardhat node --fork https://rpc.api.moonbase.moonbeam.network

如果您想要配置您的Hardhat项目,您可以使用以下配置更新您的hardhat.config.js文件:

...
networks: {
  hardhat: {
    forking: {
      url: 'INSERT_RPC_API_ENDPOINT',
    },
  },
},
...
...
networks: {
  hardhat: {
    forking: {
      url: 'INSERT_RPC_API_ENDPOINT',
    },
  },
},
...
...
networks: {
  hardhat: {
    forking: {
      url: 'https://rpc.api.moonbase.moonbeam.network',
    },
  },
},
...

当您启动Hardhat分叉时,您会有20个预先注资10,000个测试Token的开发账户。分叉好的实例位于http://127.0.0.1:8545/。在您的终端中,将会显示类似以下输出:

Private Key: Oxdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97 Account #9: Oxa0Ee7A142d267C1f36714E4a8F75612F20a79720 (10000 ETH) Private Key: 0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6 Account #10: OxBcd4042DE499D14e55001CcbB24a551F3b954096 (10000 ETH) Private Key: Oxf214f2b2cd398c806f84e317254e0f0b801d0643303237d97a22a48e01628897 Account #11: 0x71bE63f3384f5fb98995898A86B02Fb2426c5788 (10000 ETH) Private Key: 0x701b615bbdfb9de65240bc28bd21bbc0d996645a3dd57e7b12bc2bdf6f192c82 Account #12: OxFABBOac9d68B0B445fB7357272F202C5651694a (10000 ETH) Private Key: Oxa267530f49f8280200edf313ee7af6b827f2a8bce2897751d06a843f644967b1 Account #13: 0x1CBd3b2770909D4e10f157cABC84C7264073C9Ec (10000 ETH) Private Key: 0x47c99abed3324a2707c28affff1267e45918ec8c3f20b8aa892e8b065d2942dd Account #14: OxdF3e18d64BC6A983f673Ab319CCaE4f1a5707097 (10000 ETH) Private Key: Oxc526ee95bf44d8fc405a158bb884d9d1238d990612e9f33d006bb0789009aaa Account #15: Oxcd3B766CCDd6AE721141F452C550Ca635964ce71 (10000 ETH) Private Key: 0x8166f546bab6da521a8369cab06c5d2b9e46670292d85c875ee9ec20e84ffb61 Account #16: 0×2546BcD3c84621e976D8185a91A922aE77ECEc30 (10000 ETH) Private Key: Oxea6c44ac03bff858b476bba40716402b03e41b8e97e276d1baec7c37d42484a0 Account #17: OxbDA5747bFD65F08deb54cb465eB87D40e51B197E (10000 ETH) Private Key: 0x689af8efa8c651a91ad287602527f3af2fe9f6501a7ac4b06166765a93e037fd Account #18: OxdD2FD4581271e230360230F9337D5c0430Bf44C0 (10000 ETH) Private Key: Oxde9be858da4a475276426320d5e9262ecfc3ba460bfac56360bfa6c4c28b4ee0 Account #19: 0×8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199 (10000 ETH) Private Key: Oxdf57089febbacf7ba0bc227dafbffa9fc08a93fdc68e1e42411a14efcf23656e WARNING: These accounts, and their private keys, are publicly known.
Any funds sent to them on Mainnet or any other live network WILL BE LOST.

要验证您是否已经分叉好网络,您可以查询最新区块号:

curl --data '{"method":"eth_blockNumber","params":[],"id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST localhost:8545 

如果您已经将result从16进制转换成十进制,您应该在分叉网络时获得了最新区块号。您可以使用区块浏览器交叉查询区块号。

在这里,您可以部署新的合约到您的Moonbeam分叉实例,或者通过创建已部署合约的本地实例与已部署合约交互。

要与已部署合约交互,您可以使用ethersscripts目录中创建新的脚本。因为您将使用Hardhat运行此脚本,因此您无需导入任何库。在脚本中,您可以使用以下代码片段访问网络上的实时合约。

const hre = require('hardhat');

async function main() {
  const provider = new ethers.JsonRpcProvider(
    'http://127.0.0.1:8545/'
  );
  const contract = new ethers.Contract(
    'INSERT_CONTRACT_ADDRESS',
    'INSERT_CONTRACT_ABI',
    provider
  );
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});
本网站的所有信息由第三方提供,仅供参考之用。Moonbeam文档网站(https://docs.moonbeam.network/)上列出和描述的任何项目与Moonbeam立场无关。Moonbeam Foundation不保证网站信息的准确性、完整性或真实性。如使用或依赖本网站信息,需自行承担相关风险,Moonbeam Foundation不承担任何责任和义务。这些材料的所有陈述和/或意见由提供方个人或实体负责,与Moonbeam Foundation立场无关,概不构成任何投资建议。对于任何特定事项或情况,应寻求专业权威人士的建议。此处的信息可能会包含或链接至第三方提供的信息与/或第三方服务(包括任何第三方网站等)。这类链接网站不受Moonbeam Foundation控制。Moonbeam Foundation对此类链接网站的内容(包括此类链接网站上包含的任何信息或资料)概不负责也不认可。这些链接内容仅为方便访客而提供,Moonbeam Foundation对因您使用此信息或任何第三方网站或服务提供的信息而产生的所有责任概不负责。
最后更新: August 5, 2024