工具库选用
用来和以太坊节点交互的库有许多选择,如web3.js,ether.js,而目前我们组内用到的是ether.js,它是一个轻量级的JavaScript库,可以用来替代Web3.js来构建javascript前端并与以太坊区块链进行交互。
它的用法可以具体查看它的官网https://learnblockchain.cn/docs/ethers.js/getting-started.html
核心概念
ehter.js主要包括以下4个模块:钱包、Providers、合约Contract以及工具包(utils)。
钱包Wallet 管理着一个公私钥对用于在以太坊网络上密码签名交易以及所有权证明。所有与链交互的操作需要有一个提供者(Provider),提供者Provider 是一个连接以太坊网络的抽象,用与查询以太坊网络状态
或者发送更改状态的交易。比如有EtherscanProvider、InfuraProvider 等…
而合约就是一段实现了某一个功能的一段代码,工具包是ether.js提供的一些我们写接口的时候可能用到的方法,比如字符串转Byte32格式。
Wallet
目前我们用的是现成的Metamask钱包,它相当于一个以太坊节点,通过它,我们可以直接和链上交互。
同时,它也提供了每个账户的钱包地址(公钥),这也解决了系统里的账户问题,不需要我们像以前一样去注册账户密码,而是通过MetaMask自动生成一个钱包账户,它的用户名就是它的地址,密码(私钥)也是随机生成的,钱包中可以导出。
Provider
目前项目中使用的是现有的web3提供者window.ethereum,或者也可以用MetaMask提供的Provider。
所以在一个接口lib文件中,起手式应该如下:
const ethereum = window.ethereum || ''
function condition (value) {
if (typeof window.ethereum !== 'undefined') {
return new ethers.providers.Web3Provider(value)
} else {
window.alert('Please install a crypto wallet before entering, we recommend to use Onekey and follow the manual(https://scf-evm.beetrust.com/static/guide.pdf) to install.')
}
}
const provider = condition(ethereum)
或者直接用metamask的provider
// 将自动检测网络;
// 如果在MetaMask中更改了网络,则会导致页面刷新。
let provider = new ethers.providers.Web3Provider(web3.currentProvider);
Contract
定义
合约是在以太坊区块链上的可执行程序的抽象。合约具有代码 (称为字节代码) 以及分配的长期存储 (storage)。每个已部署的合约都有一个地址, 用它连接到合约, 可以向其发送消息来调用合约方法。
类别
在合约上可以调用两种类型的方法:
- 视图方法 : 不能添加、移除或更改存储中的任何数据,也不能记录任何事件,并且只能调用其他合约上视图方法。 这些方法是免费的(不需要以太)调用。 结果也可以返回给调用者。
- 非视图方法: 需要支付费用(用Ether),但可以执行任何所需的状态更改操作,记录事件,发送ether并在其他合约上调用非视图方法。 这些方法不能将其结果返回给调用者。 这些方法必须由交易触发,由外部拥有的账户(EOA)直接或间接发送(如从另一个合约调用),并且只有在交易被打包(挖矿)后才会产生效果。 因此,这些操作所需的持续时间可能变化很大,并且取决于交易Gas价格、网络拥塞和矿工优先选择方法。
ABI
合约Contract对象是一个元类,它可以提供合约定义(称为应用程序二进制接口或ABI)方法供JS调用。
简单说,就是合约要提供出一个json格式的文件,我们再通过一些格式化方法将其变为指定格式的一段_abi内容,这样我们就可以调用这些合约里的方法了。
// 生成abi
function formAbi () {
return new Promise((resolve, reject) => {
axios.get('abi.json').then((res) => {
jsonAbi = res.data
const iface = new Interface(jsonAbi)
_abi = iface.format(FormatTypes.full)
resolve()
}).catch(() => {
reject()
})
})
}
举例
- 非视图方法
在调用合约里的mint方法时,它就是非视图方法,是需要用户去支付gas费用并且用钱包确认(其实也就是签名)的,所以它需要链接signer,具体如下:
const signer = provider.getSigner()
function getContractSigner (contractAddr) {
if (typeof window.ethereum !== 'undefined') {
const daiContract = new ethers.Contract(contractAddr, _abi, provider)
let contractSigner = daiContract.connect(signer)
return contractSigner
} else {
return new Error('Please install MetaMask!')
}
}
function mint (tokenUri, tokenType) {
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve, reject) => {
await formAbi()
const address = getContractAddress(tokenType)
const contract = getContractSigner(address)
contract.mint(window.account, tokenUri).then(res => {
console.log('mintres', res)
resolve(res)
}).catch(err => {
reject(err)
})
})
}
工具包
因为大部分合约里的方法要求的参数是Byte32格式的,所以有时会用到如下的方式
function setSale (v, r, s) {
const rr = ethers.utils.formatBytes32String(r)
const ss = ethers.utils.formatBytes32String(s)
.....
}
应用案例
这是制作NFT DEMO时写的接口文件,可以参考它来写:
https://git.hrlyit.com/lls-web3/nft-frontend-demo/-/blob/v0.4/src/utils/web3.js
具体要怎么和vue系统融合,也可以参考这个项目。