DApp开发入门

近一两年随着DeFi的火热,DApp开发也受到越来越多的关注,单纯从概念来说,DApp还是比较容易理解的,也就是App + decentralization(去中心化)。而去中心化我个人认为主要体现在两个方面,一是账户体系,二是服务或数据

账户体系也就是我们的钱包账户。

服务或数据,则对应我们的智能合约以及链上数据,获取这些数据非常简单,不需要Token认证,也不需要session,cookie,一切都是可以公开访问的,甚至你可以直接在一些区块链浏览器直接操作合约。

本文仅介绍以太坊系列的DApp开发,其他链原理差不太多。

目标

  1. 了解一些基础的区块链概念和操作
  2. 熟悉与MetaMask钱包的交互
  3. 了解ERC20 Token、并与ERC20合约进行交互
  4. 在Mdex的某个矿池中抵押LP Token(流动性证明),赎回LP Token

环境与工具

  1. 需要安装MetaMask钱包并创建或导入账号
  2. ethers.js (与以太坊区块链及其生态系统进行交互的JavaScript库),也可以用web3.js
  3. create-react-app(demo相关)
  4. Heco区块链浏览器

钱包操作

MetaMask安装完成并运行后,可以在Chrome控制台打印MetaMask注入的window.ethereum对象

截屏2021-06-04 上午10.55.39.png

关于ethereum对象,我们只需要关心ethereum.request就足够了,MetaMask 使用ethereum.request(args)方法来包装 RPC API。这些 API 基于所有以太坊客户端公开的接口。 简单来说钱包交互的大部分操作都是由request()方法实现,通过传入不同的方法名来区分。

⚠️即使ethereum对象中提供了chainId,isMetaMask,selectAddress属性,我们也不能完全相信这些属性,他们是不稳定或不标准,不建议使用。我们可以通过上面说的request方法,拿到可靠的数据

钱包通过method方法名,进行对应的实现 以获取钱包地址为例

调用ethereum.request({ method: "eth_requestAccounts" }),钱包实现了该方法,那么就可以拿到钱包的地址了。

case "eth_requestAccounts":
    return ["0xFD513D86a57BAFeAf46ddcC1bDDf629409A03cCf"]

常见的钱包操作

  1. 连接钱包

    const accounts = await ethereum.request({ method: "eth_requestAccounts" });
    const account = accounts[0];
    
  2. 监听网络和账号切换事件

    //监听账户变化
    ethereum.on("accountsChanged", (accounts) => {
      setAccount(accounts[0]);
    });
    //监听网络变化
    ethereum.on("chainChanged", (chainId) => {
      setChainId(chainId);
    });
    
  3. 添加自定义代币(添加自己的平台币到钱包账户,是个非常方便的功能。基于EIP-747)

    const Dog = {
      symbol: "Dog",
      decimals: 18,
      image: "https://hecoinfo.com/token/images/dogtoken_32.png",
      address: "0xb3863e02d6930762933f672ca134c1ccecd0d413",
    };
    
    ethereum.request({
      method: "wallet_watchAsset",
      params: {
        type: "ERC20", // Initially only supports ERC20, but eventually more!
        options: Dog,
      },
    });
    
  1. 添加自定义网络(像Mdex支持同时支持ETH、HECO、BSC,MetaMask只有ETH主网和测试网,可以直接通过此协议增加对应的网络到钱包,如果钱包支持的话,基于EIP-3085,仅支持非内置网络),社区目前有个EIP-3326提案,可以直接在以太坊网络进行切换。

    const Dog = {
      symbol: "Dog",
      decimals: 18,
      image: "https://hecoinfo.com/token/images/dogtoken_32.png",
      address: "0xb3863e02d6930762933f672ca134c1ccecd0d413",
    };
    
    ethereum.request({
      method: "wallet_watchAsset",
      params: {
        type: "ERC20", // Initially only supports ERC20, but eventually more!
        options: Dog,
      },
    });
    

Provider

Provider 是一个连接以太坊网络的抽象,用与查询以太坊网络状态或者发送更改状态的交易。

MetaMask注入的window.ethereum就是一个Provider,一个RPC节点也是一个Provider,通过Provider,我们有了访问区块链的能力。 在连接到钱包的情况下,通常使用钱包的Provider就可以了,ethers.providers.Web3Provider(ethereum)

如果只需要查询一些区块链数据,可以使用EtherscanProvider 和 InfuraProvider 连接公开的第三方节点服务提供商。JsonRpcProvider 和 IpcProvider 允许连接到我们控制或可以访问的以太坊节点。

获取当前账户余额

const balance = await provider.getBalance(account);

获取最新区块号

const blockNumber = await provider.getBlockNumber();

其他RPC操作,可以通过JSON-RPC查看。

ERC20 Token

ERC20 通证标准(ERC20 Token Standard)是通过以太坊创建通证时的一种规范。按照ERC20的规范可以编写一个智能合约,创建"可互换通证"。它并非强制要求,但遵循这个标准,所创建的通证可以与众多交易所,钱包等进行交互。目前的平台币基本都遵循ERC20标准。

contract ERC20 {
      function name() constant returns (string name)
      function symbol() constant returns (string symbol)
      function decimals() constant returns (uint8 decimals)
      function totalSupply() constant returns (uint totalSupply);
      function balanceOf(address _owner) constant returns (uint balance);
      function transfer(address _to, uint _value) returns (bool success);
      function transferFrom(address _from, address _to, uint _value) returns (bool success);
      function approve(address _spender, uint _value) returns (bool success);
      function allowance(address _owner, address _spender) constant returns (uint remaining);
      event Transfer(address indexed _from, address indexed _to, uint _value);
      event Approval(address indexed _owner, address indexed _spender, uint _value);
}

通过ethers.js可以连接ERC20的合约,合约编译后会生成ABI,合约部署后,会生成合约地址,开发者通过ABI和合约地址,对合约发送消息。

合约中的方法大致分为两种:视图方法(免费),非视图方法(消耗Gas),可以通过ABI查看方法类型。

截屏2021-06-08 上午10.10.11.png
  1. 初始化ERC20合约,如果是视图方法,只是进行数据的读取,传入Provider就可以,如果需要更改合约状态,需要用户签名,则需要传入Signer

    需要Token的合约地址和ERC20的合约ABI,可以通过浏览器查询到相关的信息,例如Dog

    new ethers Contract( addressOrName, abi,providerOrSigner)
    
  2. 查询合约的相关信息,这里以Heco的Dog为例,其他的Token都是一样的。

    const totalSupply = await contract.totalSupply();
    const balance = await contract.balanceOf(account);
    
  3. Transfer,转账的单位是Wei,可以通过etherjs提供的工具函数进行转换。

    const contract = new ethers.Contract(
          Dog.address,
          ERC20ABI,
          provider.getSigner()
        );
     const tx = await contract.transfer(
         "0xFD513D86a57BAFeAf46ddcC1bDDf629409A03cCf",
         parseEther("1.0")
      );
    

⚠️ERC20需要多加关注的是Approve()方法以及transfer()transferFrom()的区别,授权过的代币,被授权的那一方,可以通过调用transferFrom()方法,转走你授权数量内的代币,所以授权是一个很危险的操作,假设你授权了一个不良的合约,那你会面临授权的token被转走的风险,即使你没有泄露私钥助记词。

抵押和赎回

挖矿合约仅需要了解大概过程,挖矿合约并非标准,涉及到具体业务 这里选择Mdex DEP-HUSD矿池进行操作,也就是抵押DEP-HUSD LP Token 获取Mdex 。 此处已经提前添加了LP Token以及进行了Approve操作。

操作成功与否可以通过Mdex页面进行查看或在浏览器进行查询

  1. 从HecoInfo上获取到Mdex Pool的ABI,通过ABI连接Mdex Pool的合约

    const pool = new ethers.Contract(
          POOL_ADDRESS,
          POOLABI,
          provider.getSigner()
        );
    
  2. 抵押全部的LP

      const pid = await pool.LpOfPid(LP_ADDRESS);
    
      const lpToken = new ethers.Contract(LP_ADDRESS, ERC20ABI, provider.getSigner());
      //抵押
      const lpBanlance = await lpToken.balanceOf(account);
      const tx = await pool.deposit(pid, lpBanlance);
    
  3. 赎回全部的LP

    const pid = await pool.LpOfPid(LP_ADDRESS);
    const userInfo = await pool.userInfo(pid, account);
    const tx = await pool.withdraw(pid, userInfo[0]);
    

文档与三方库

便利三方库:web3-react use-wallet

文档:doc.metamask.io ethers

你可能感兴趣的:(DApp开发入门)