HackathonPLUS-MetaPort之Ethereum智能合约迁移到PlatON教程

Ethereum智能合约迁移到PlatON教程

        Ethereum生态的不断壮大,导致交易数量急剧增加,用户不得不在交易速度和手续费之间做出艰难的选择。随着DeFi和NFT项目持续火爆,产生了高额利润催生了大量的“套利”交易,导致用户对区块链数据隐私的需求不断增加。PlatON结合区块链、人工智能和隐私计算技术,建立了一个去中心化的协作式隐私人工智能区块链网络。相较于以太坊在交易速度、交易成本和数据隐私方面有巨大的优势。为提高开发效率,PlatON 1.1.1版本和Alaya0.16.1版本将开始全面兼容Ethereum生态工具。本文将以ENS为例,讲解如何将Ethereum智能合约迁移到PlatON中。

        ENS(Ethereum Name Service)是一个基于Ethereum的分布式、开放和可扩展的命名系统。ENS是一个层次结构的域名系统,层次的名称叫做域,不同域之间以“点“作为分隔符,一个域的所有者能够完全控制其子域。ENS主要由注册表和解析器组成。其中,注册表用于维护所有域名和子域名列表,解析器负责将域名转换为地址。通过分析ENS智能合约,ENSRegistry.sol合约实现了注册表功能;ensdomains/resolver/PublicResolver合约实现了解析器功能。为实现子域名自动化注册,需要部署FIFSRegistrar.sol合约。此外,启用ENS反向解析,需部署ReverseRegistrar.sol合约。

        在Ethereum生态中,用于智能合约部署的工具主要有Truffle和Remix两种,首先介绍如何使用Truffle部署合约。

一、使用Truffle迁移ENS合约

1.1安装Truffle

首先需要利用高于v8.9.4的nodejs安装Truffle

npm install -g truffle

Truffle使用文档

1.2 下载编译ENS合约

Step1 下载ENS合约

git clone https://github.com/ensdomains/ens.git && cd ens

Step2 使用truffle初始化一个工程

truffle init

在操作完成之后,就有如下项目结构:

  • contracts/: Solidity合约目录
  • migrations/: 部署脚本文件目录
  • test/: 测试脚本目录
  • truffle-config.js: platon-truffle 配置文件
    Step3 修改platon-truffle 配置文件truffle-config.js
    PlatON测试网RPC信息:
  • chainid为210309
  • RPC为http://35.247.155.162:6789、"https://devnetopenapi.platon.network/rpc" 以及 ws://35.247.155.162:6790
    前往PlatON测试网水龙头,在水龙头中输入的地址可以是LAT或0x格式的地址.
vim truffle-config.js

truffle-config.js 修改部分内容如下:

development: {
      host: "35.247.155.162",     // RPC(default: none)
      port: 6789,            // Standard PlatON port (default: none)
      network_id: "*",       // Any network (default: none)
      from: "地址",        // Account to send txs from (default: accounts[0]) 这个地址格式可以是0x或LAT,建议使用0x格式
},

...

compilers: {
      solc: {
            version: "^0.5.17",    // 此版本号与合约声明的版本号保持一致
      }
}

Step4 编译合约

truffle compile

在操作完成之后,生成如下目录结构:

  • build/: Solidity合约编译后的目录
  • build/contracts/: 对应的编译文件

1.3 部署ENS合约

Step1 新增合约部署脚本文件

cd migrations/ && vim 2_initial_ens.js

部署脚本文件名建议使用合约名称便于后面维护,如ENS合约对应的部署脚本文件为2_initial_ens.js,内容如下所示:

const ENS = artifacts.require("@ensdomains/ens/ENSRegistry");
const FIFSRegistrar = artifacts.require("@ensdomains/ens/FIFSRegistrar");
const ReverseRegistrar = artifacts.require("@ensdomains/ens/ReverseRegistrar");
const PublicResolver = artifacts.require("@ensdomains/resolver/PublicResolver");

const utils = require('web3-utils');
const namehash = require('eth-ens-namehash');

const tld = "test";

module.exports = function(deployer, network, accounts) {
  let ens;
  let resolver;
  let registrar;

  // Registry
  deployer.deploy(ENS)
  // Resolver
  .then(function(ensInstance) {
    ens = ensInstance;
    return deployer.deploy(PublicResolver, ens.address);
  })
  .then(function(resolverInstance) {
    resolver = resolverInstance;
    return setupResolver(ens, resolver, accounts);
  })
  // Registrar
  .then(function() {
    return deployer.deploy(FIFSRegistrar, ens.address, namehash.hash(tld));
  })
  .then(function(registrarInstance) {
    registrar = registrarInstance;
    return setupRegistrar(ens, registrar);
  })
  // Reverse Registrar
  .then(function() {
    return deployer.deploy(ReverseRegistrar, ens.address, resolver.address);
  })
  .then(function(reverseRegistrarInstance) {
    return setupReverseRegistrar(ens, resolver, reverseRegistrarInstance, accounts);
  })
};

async function setupResolver(ens, resolver, accounts) {
  const resolverNode = namehash.hash("resolver");
  const resolverLabel = utils.sha3("resolver");

  await ens.setSubnodeOwner("0x0000000000000000000000000000000000000000", resolverLabel, accounts[0]);
  await ens.setResolver(resolverNode, resolver.address);
  await resolver.setAddr(resolverNode, resolver.address);
}

async function setupRegistrar(ens, registrar) {
  await ens.setSubnodeOwner("0x0000000000000000000000000000000000000000", utils.sha3(tld), registrar.address);
}

async function setupReverseRegistrar(ens, resolver, reverseRegistrar, accounts) {
  await ens.setSubnodeOwner("0x0000000000000000000000000000000000000000", utils.sha3("reverse"), accounts[0]);
  await ens.setSubnodeOwner(namehash.hash("reverse"), utils.sha3("addr"), reverseRegistrar.address);
}

Step2 解锁账户
进入Truffle控制台

truffle console

导入账户

web3.eth.personal.importRawKey('私钥','密码')   	// 私钥没有0x前缀

解锁账户

web3.eth.personal.unlockAccount('地址', '密码', 999999)

Step3 合约部署

truffle migrate

当看到以下内容代表合约已经部署成功。Gas实际上使用LAT结算,因此用于部署合约的地址中需要有一定的LAT。

Replacing 'ENSRegistry'
   -----------------------
   > transaction hash:    0x7f166ce0d1287202e0583c739cacb77d0166fb8c5c459b9aecd2491e44bfc5ba
   > Blocks: 4            Seconds: 4
   > contract address:    0x5215c9F58E1Bf2c4BF6c7B5F80AD612CE0623AA9
   > block number:        6157441
   > block timestamp:     1637306960253
   > account:             0x54AFd12E9e5678F3Fc6f528120e1a13cb9c3c2AF
   > balance:             198.00101596
   > gas used:            584642 (0x8ebc2)
   > gas price:           500 gwei
   > value sent:          0 ETH
   > total cost:          0.292321 ETH


   Replacing 'PublicResolver'
   --------------------------
   > transaction hash:    0x78b33dc7ca8df8f72c0da17fd8d125207bc7903039c2987578500a150e32499d
   > Blocks: 2            Seconds: 4
   > contract address:    0xA447FDd6c8BF9Ce3Ce80f52b7075d5651D301369
   > block number:        6157448
   > block timestamp:     1637306967968
   > account:             0x54AFd12E9e5678F3Fc6f528120e1a13cb9c3c2AF
   > balance:             196.30776246
   > gas used:            3386507 (0x33ac8b)
   > gas price:           500 gwei
   > value sent:          0 ETH
   > total cost:          1.6932535 ETH


   Replacing 'FIFSRegistrar'
   -------------------------
   > transaction hash:    0xcc61e6a21a9ec5b1f4678becbde438846396fbee59616f58898d05d5c8faa9e8
   > Blocks: 2            Seconds: 4
   > contract address:    0xd72eF5Af584bc13799361505207B3508bc317D93
   > block number:        6157470
   > block timestamp:     1637306992188
   > account:             0x54AFd12E9e5678F3Fc6f528120e1a13cb9c3c2AF
   > balance:             196.13476646
   > gas used:            202670 (0x317ae)
   > gas price:           500 gwei
   > value sent:          0 ETH
   > total cost:          0.101335 ETH


   Replacing 'ReverseRegistrar'
   ----------------------------
   > transaction hash:    0x80a6bb520f60849a385691e84600518a98308bd6e1cc2ecfbca66d79a1c36b3b
   > Blocks: 3            Seconds: 4
   > contract address:    0x7431eB4F2f8B55c76017b8e26fc0B7205709F08F
   > block number:        6157481
   > block timestamp:     1637307004348
   > account:             0x54AFd12E9e5678F3Fc6f528120e1a13cb9c3c2AF
   > balance:             195.85967096
   > gas used:            505078 (0x7b4f6)
   > gas price:           500 gwei
   > value sent:          0 ETH
   > total cost:          0.252539 ETH

   > Saving artifacts
   -------------------------------------
   > Total cost:           2.3394485 ETH


Summary
=======
> Total deployments:   4
> Final cost:          2.3394485 ETH

二、使用Remix迁移ENS合约

        使用Truffle迁移智能合约并非是最佳选择,主要原因包括三点:一是,需部署额外的Migrations.sol合约,增加了部署成本;二是,若部署合约所需时间较长,无法及时根据网络拥堵情况,灵活调整Gas;三是,部署过程中需全程保证网络链接,一旦失去网络链接需从头开始部署合约。而使用Remix可以较好的解决上述问题。

2.1 将合约导入Remix

        通过分析可知,迁移ENS需要部署四个智能合约。这四个合约分别位于https://github.com/ensdomains/ens/tree/master/contractshttps://github.com/ensdomains/resolvers内。首先进入https://remix.ethereum.org/将所有合约导入Remix中,如图1所示。

图1 ENS涉及到的合约

2.2 部署合约

        根据合约依赖关系,需要第一个部署的是注册表合约,即ENSRegistry.sol。首先,选择所需部署的合约;其次,根据合约声明选择solidity版本;最后,点击Compile ContractName编译合约,如图2、3所示。

图2 选择所需部署的合约
 

图3 编译所需部署的合约
 

        若编译成功,则进入Remix的部署界面,完成ENSRegistry合约的部署,如图4所示。部署时,首先将PlatON测试网RPC添加到MateMask中,通过私钥将LAT地址导入到MateMask中;其次,在Remix的deploy界面中将ENVIRONMENT设置为Injected Web3 ,并将CONTRACT设置为所需部署的合约;然后,点击Deploy可以设置该笔交易的Gas Price,如图5所示;最后,在MateMask中点击确认,便成功部署该合约。

图4 合约部署界面
 

图5 调整交易Gas Price价格
 

        同理,使用Remix部署其余三个合约与部署ENSRegistry.sol方法一致。需额外注意的有三点。一是,如果合约通过import "@PATH/contractName.sol"导入其他合约,需要统一修改为import "https://github.com/PATH/contractName.sol";二是,若导入合约所使用solidity版本与项目版本不一致,需修改solidity版本和因此导致的编译错误;三是,构造函数需要传参时,需在Deploy后面传入参数。入参大于一时,使用“,”分割,如图6所示。

图6 构造函数传参
 

三、合约测试

        如果使用Truffle部署合约,可以使用Web3对部署的合约进行调试。首先,通过npm install web3安装web3包。由于项目需要调试的内容比较多,本文只介绍测试程序的主要框架。

const Web3 = require('web3')
const HttpProvider = ${RPC}
const web3 = new Web3(new Web3.providers.HttpProvider(HttpProvider))

const fs = require("fs");
const ABIString= fs.readFileSync("./build/contracts/fileName.json", "utf-8");
const ABIJSON= JSON.parse(ABIString);

const privateKey = '私钥'
web3.eth.accounts.wallet.add(privateKey)
const myAddr = web3.eth.accounts.wallet[0].address
var ContractName = new web3.eth.Contract(ABIJSON.abi,'ContractAddr');

async function test() {
    // 修改合约状态的方法
    var renturnsContent = await ContractName.methods.methodsName(param).send({
        from:myAddr,
        gas:2000000,
        gasPrice:"999999"
    },function(error,txHash){
        console.log("error:",error);
        console.log("txHash:",txHash);
    })
    console.log("renturnsContent:",renturnsContent);
    
    // 查询合约状态
    var renturnsContent= await ContractName.methods.methodsName(param).call()
    console.log("renturnsContent:",renturnsContent);
}

        如果使用Remix部署合约,合约部署成功之后在Deployed Contract下面会显示合约名称,合约地址和合约的对外方法,如图7所示。

图7 Remix合约调试界面

        至此,将ENS迁移到PlatON的整个流程介绍完毕。整个流程与在Ethereum上部署合约的体验一致,说明PlatON在兼容Ethereum生态方面已经足够成熟,将一步降低了Ethereum开发者在PlatON上的开发成本。期待,PlatON借助自身的隐私计算、AI方面的优势以及Ethereum强悍的生态,打造一更加强悍的生态,更好的服务于实体经济的发展。

2 Likes