智能合约

智能合约与平时的代码其实没有什么区别,只是运行于一个以太坊这样的分布式平台上。这个运行的平台,赋予了这些代码不可变,确定性,分布式和可自校验状态等特点。代码运行过程中状态的存储,是不可变的。

在以太坊中,每个合约都有一个唯一的地址来标识它自己(由创建者的哈希地址和曾经发送过的交易的数量推算出来)。客户端可以与这个地址进行交互,可以发送ether,调用函数,查询当前的状态等。

智能合约,本质上就是代码,以及代码运行后存储到区块链上的状态两个元素组成。比如,你用来收发ETH的钱包,本质上就是一个智能合约,只是外面套了一个界面。

Gas

因为执行计算要花钱,而要执行的运算量与代码直接相关。所以,每个在网络运行的底层操作都需要一定量的gas。gas只是一个名字,它代表的是执行所需要花费的成本(译者注:由于以太坊是图灵完备的,随便一个死循环就将导致网络不可用,所以引入了gas的概念)。

整个分布式网络引入了强制限制,来避免停机问题。因此如果你写一个死循环,当gas耗尽后,网络就会拒绝执行接下来的操作,并且回滚你之前的所有操作。

gas的价格由市场决定,类似于比特币的交易费机制。如果你的gas价格高,节点则将优先因为利益问题打包你的交易。

一般来说,在以太坊网上读取状态是免费的,只有写入状态是收费的。下面这个文章是gas概念的一些深度解析

GWEI & gasPrice & gasLimit

gasPrice 是给矿工的费用,GWEI 越高 即 gasPrice越高,打包速度越快。而 gasLimit 用于限制本次交易 gas 费用上限,防止无限制烧 gas。

合约交互可入参overrides.gasPrice 修改,示例如下:

// 使用 ethers 设置 GWEI:
const baseGasPrice = await provider.getGasPrice(); // 基础gasPrice { BigNumber: "23610503242" }
const limit = await contract.estimateGas.takeChance(num); // 预估 gaslimt
const options = {
	gasPrice: baseGasPrice,
	gasLimit: (Number(limit) * 1.5).toFixed(0)
}

const res = await this.contract.takeChance(num, options);// 最后一项override
const reward = await res.wait(); 

getGasPrice: 返回交易中使用的 gasPrice 的最佳猜测。

estimateGas: 返回执行带有 args 和 overrides 的 METHOD_NAME 所需的估计 gas 单位。

PS:主链币价格的设置方式

overrides.value 主链币(比如BNB),价格的传参方式不能通过参数传入,而要放在最后一项对象的value属性中

// 使用 ethers 设置 主链币价格:
const options = {
    value: 0 // 主链币的数量 bignumber
}

const resp = await contractObj.swap(cloneAmountOut, cloneAmountInMax, options); // 最后一项位对象

MetaMask

Metamask 是一个Google浏览器扩展,把Chrome变成了一个 DApp 浏览器。它的核心特性是注入以太坊提供的 js 客户端库web3,到每一个界面,来让 DApp 连接到 MetaMask 提供的以太坊节点服务。不过这个 Chrome 扩展,可以允许你管理你的钱包,以及连接到不同的以太坊网络。

常见的 MetaMask 交互

是否安装metamsk

export function isMetaMask() {
 const { ethereum } = window;
 return Boolean(ethereum && ethereum.isMetaMask);
}

获取chainid

async function getChainId() {
 const { ethereum } = window;
 try {
   const chainId = await ethereum.request({
     method: "eth_chainId"
   });
   handleNewChain(chainId);
 } catch (err) {
   console.error(err);
 }
}

主动切换到以太坊网络

async function switchToEthereum() {
 try {
   await window.ethereum.request({
     method: "wallet_switchEthereumChain",
     params: [
       {
         chainId: "0x1"
       }
     ]
   });
 } catch (error) {
   console.log(error);
 }
}

主动切换到其余链配置

async function switchToOtherNetwork(findChain) {
 const data = [];
 data.push(findChain);
 console.log(findChain, "switchNetwork");
 try {
   await window.ethereum.request({
     method: "wallet_addEthereumChain",
     params: data 
   });
 } catch (error) {
   console.log(error);
 }
}

监听链上钱包配置

function handelConnectInfo(info) {
 console.log(info, "handelConnectInfo");
}

function handleDisConnect(disconnect) {
 console.log(disconnect, "handleDisConnect");
}

function handleNewAccount(account) {
 updateAccount(account[0]);
}

function handelNewMessage(msg) {
 console.log(msg, "handelNewMessage");
}

function _listeningMetamsk() {
 const { ethereum } = window;

 ethereum.on("chainChanged", handleNewChain);

 ethereum.on("accountsChanged", handleNewAccount);

 ethereum.on("message", handelNewMessage);

 ethereum.on("connect", throttle(handelConnectInfo, 1000));

 ethereum.on("disconnect", throttle(handleDisConnect, 1000));
}

添加自定义代币到metamask

async function addToken() {
     let contractAddress = getContractAddress();
     window.ethereum
       .request({
         method: "wallet_watchAsset",
         params: {
           type: "ERC20",
           options: {
             address: contractAddress.IPISTR,
             symbol: "IPISTR",
             decimals: 18,
             image: "https://i.loli.net/2021/08/12/BNYGAD9RZUl6nch.png"
           }
         }
       })
       .then(success => {
         if (success) {
           Message.success("IPISTR Token added successfully!");
         } else {
           Message.error("Something went wrong.");
         }
       })
       .catch(console.error);
   }

综合实例-连接钱包

export async function getMetamskConnect() {
  if (!isMetaMask()) {
    openUrl("https://metamask.io/", "install metamsk");
  }
  if (window.ethereum) {
    window.provider = window.ethereum;
    try {
      let accounts = await window.ethereum.request({
        method: "eth_requestAccounts"
      });
      await updateAccount(accounts[0]);
    } catch (error) {
      console.warn("Please authorize to access tour account");
    }
  }
  await getChainId(); 
  _listeningMetamsk();
}

内容参考: Getting Up to Speed on Ethereum

21个基于ethers的Dapp常用工具函数 | 登链社区 | 区块链技术社区

Last Updated: