从工作分工上来看,前端工程师完全可以把智能合约看做是一个后端工程师给你的接口,毕竟solidty的主要工作也是处理和返回数据的,和普通的后端工作内容相差不大,只不过代码是部署在区块链上的。
因为代码是部署在区块链上的,所以在发送请求时会与传统的请求有所不同,但本质是一样的,接下来我们要搞清楚以下三点,就能明白它的本质为什么是一样的:
1.什么是abi,我们为什么要用到它
2.为什么要借助第三方包,而不是直接请求服务器,它都有哪些作用
3.请求智能合约的原理是什么,区块是如何执行的
先看代码,因为我对ether比较熟悉,这里用ether做演示:
//区块链本质是由多个服务器跑相同的程序,存储相同的数据,然后不停地相互同步来形成的信任网络
//我们只需要链接其中一个,然后请求这个服务器即可,至于数据同步的事情不用我们处理
//provider我们称之为节点提供者(每个组成区块的服务器我们称之为节点),存储节点信息和地址信息
//我这里模拟的是DAPP的流程,节点和用户的相关信息存在window.ethereum中,可以直接获取
let provider = new ethers.providers.Web3Provider(window.ethereum)
//signer(签名者),该信息存在节点提供者中并自动与节点关联
let signer = provider.getSigner();
//这里将合约地址,abi,还有签名者之间做关联,生成一个合约对象,之后就可以调用合约对象的方法了
let contract= new ethers.Contract('合约地址', erc20abi, signer);
//这里调用了合约代币的转账方法
// ethers.utils.parseEther()是ether.js工具包中的一个方法,可以将数字字符串乘上10**16,没有精度损耗
let data = await contract.transfer("收币地址", ethers.utils.parseEther("1"))
.catch(function(err) {
console.log(err)
})
//data是本次调用生成的链上数据,不是代码执行完后的返回值
console.log(data)
//监听data中的交易hash,来获取交易结果
let data2=await provider.waitForTransaction(data.hash);
console.log(data2)
data返回值:
{
"hash": "0xe632c016cea540b54ccde8343378e615174040fbaae68fce44ab1ea44a89a531",
"type": 0,
"accessList": null,
"blockHash": null,
"blockNumber": null,
"transactionIndex": null,
"confirmations": 0,
"from": "0x173f8Ce8356dD214813f4874C316739F560D8022",
"gasPrice": {
"type": "BigNumber",
"hex": "0x1ff973cafa8000"
},
"gasLimit": {
"type": "BigNumber",
"hex": "0x760e"
},
"to": "0x2c4eb3c76D7115E210Fadf3cBFe8E0a3d5b8448F",
"value": {
"type": "BigNumber",
"hex": "0x00"
},
"nonce": 207,
"data": "0xa9059cbb000000000000000000000000173f8ce8356dd214813f4874c316739f560d80220000000000000000000000000000000000000000000000000de0b6b3a7640000",
"r": "0xd8c09aab22974fc057010152929fcb4b134a431c707c2f212f619f18297eb485",
"s": "0x69f6e8d9f3581625797bc20cfdd2951ef225e5f24366d8f4128e8e6fdfd70316",
"v": 2093,
"creates": null,
"chainId": 1029
}
data2返回值:
{
"to": "0x2c4eb3c76D7115E210Fadf3cBFe8E0a3d5b8448F",
"from": "0x173f8Ce8356dD214813f4874C316739F560D8022",
"contractAddress": null,
"transactionIndex": 0,
"gasUsed": {
"type": "BigNumber",
"hex": "0x6a09"
},
"logsBloom": "0x04000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000008000000800000000000000000000100000000000000000000000020000000000000000000000010000000000080000010000000000000000000040000000000000000000000004000000000000000000000000000200000000000000000000000000400000000000000000000000000000000004000000002000000000001000000000000000000000000000000100000000000400000000000000000000000000000000000000000000000000100000000100000",
"blockHash": "0x7c85a7911b5e9c8fb3bf40898167873e4beccd535407fdae3aea777246014936",
"transactionHash": "0xe632c016cea540b54ccde8343378e615174040fbaae68fce44ab1ea44a89a531",
"logs": [
{
"transactionIndex": 0,
"blockNumber": 20938930,
"transactionHash": "0xe632c016cea540b54ccde8343378e615174040fbaae68fce44ab1ea44a89a531",
"address": "0x2c4eb3c76D7115E210Fadf3cBFe8E0a3d5b8448F",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x000000000000000000000000173f8ce8356dd214813f4874c316739f560d8022",
"0x000000000000000000000000173f8ce8356dd214813f4874c316739f560d8022"
],
"data": "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000",
"logIndex": 0,
"blockHash": "0x7c85a7911b5e9c8fb3bf40898167873e4beccd535407fdae3aea777246014936"
},
{
"transactionIndex": 0,
"blockNumber": 20938930,
"transactionHash": "0xe632c016cea540b54ccde8343378e615174040fbaae68fce44ab1ea44a89a531",
"address": "0x0000000000000000000000000000000000001010",
"topics": [
"0x4dfe1bbbcf077ddc3e01291eea2d5c70c2b422b415d95645b9adcfd678cb1d63",
"0x0000000000000000000000000000000000000000000000000000000000001010",
"0x000000000000000000000000173f8ce8356dd214813f4874c316739f560d8022",
"0x000000000000000000000000f4c2f1d772488cc6d3ec3c703b9710fa2c0e227e"
],
"data": "0x00000000000000000000000000000000000000000000000d3e69b71ddbce800000000000000000000000000000000000000000000010708542e952a17a361000000000000000000000000000000000000000000000ff9c1d5a421812e455d3b8000000000000000000000000000000000000000000107078047f9b839e679000000000000000000000000000000000000000000000ff9c2a98abcf30c02453b8",
"logIndex": 1,
"blockHash": "0x7c85a7911b5e9c8fb3bf40898167873e4beccd535407fdae3aea777246014936"
}
],
"blockNumber": 20938930,
"confirmations": 2,
"cumulativeGasUsed": {
"type": "BigNumber",
"hex": "0x6a09"
},
"effectiveGasPrice": {
"type": "BigNumber",
"hex": "0x1ff973cafa8000"
},
"status": 1,
"type": 0,
"byzantium": true
}
大家可以看到abi只在构建合约的时候用到过一次,其他地方并没有使用到
let contract= new ethers.Contract('合约地址', erc20abi, signer);
下面是abi的样子
[
{
"inputs": [
{
"internalType": "string",
"name": "_name",
"type": "string"
},
{
"internalType": "string",
"name": "_symbol",
"type": "string"
},
{
"internalType": "uint256",
"name": "_decimals",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_totalSupply",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "_owner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "_spender",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "_value",
"type": "uint256"
}
],
"name": "Approval",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "_from",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "_to",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "_value",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
},
{
"constant": true,
"inputs": [
{
"internalType": "address",
"name": "_owner",
"type": "address"
},
{
"internalType": "address",
"name": "_spender",
"type": "address"
}
],
"name": "allowance",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "_spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "_value",
"type": "uint256"
}
],
"name": "approve",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "address",
"name": "_owner",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "decimals",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "name",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "symbol",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "totalSupply",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "_to",
"type": "address"
},
{
"internalType": "uint256",
"name": "_value",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "_from",
"type": "address"
},
{
"internalType": "address",
"name": "_to",
"type": "address"
},
{
"internalType": "uint256",
"name": "_value",
"type": "uint256"
}
],
"name": "transferFrom",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
}
]
abi就是一个json文件,在合约发布后生成,由solidity工程师导出,前端使用它的主要作用就是构建合约。里面存了这个合约里的所有方法,事件以及请求参数的限制,返回值的类型等等,可以看作是一个接口文档,只不过没有注释。
我们在调用合约的时候会先经过abi的检测,不存在的方法,错误的参数都会直接在这里被拦截,经过abi验证的请求才有可能正常发送给区块。
这里为什么说有可能那,是因为abi只能做到数据类型和方法的判断,至于说方法能不能正常执行下去还是要在合约上过一遍的。
1.构建交易中的对象:从上面的例子中来看,我们通过ether创建了节点,签名者,合约三个对象,然后才能开始和区块上的合约进行交互,如果不引用第三方包我们其实也能构建这三个对象,但效率就不言而喻了。
2.成熟的工具包:区块链上的数据和我们平时给用户看到的实际上相差很大,需要通过一些工具包进行转换,就比如说上面代码中的
ethers.utils.parseEther("1")
他将1变成了1*10**18,如果我们自己计算会有精度损耗不说,当量大时会自动变成科学计数法,给我们带来很多麻烦。
3.交易签名并发送:这点也是最重要的一点,不借助第三方工具包,签名这部分的工作量极大,靠我们个人是完成不了的,而如果使用了ether,我们只需要在构建合约对象时传进去即可,剩下的包ether会帮我们签名并发送,能省下很多步骤。
总的来说使用了ether之后与合约进行交互,写代码的速度甚至要比正常的请求后端要快。
下面我们讲一下区块链的执行原理,我们看一下下面这块代码
let data = await contract.transfer("收币地址", ethers.utils.parseEther("1"))
.catch(function(err) {
console.log(err)
})
//data是本次调用生成的链上数据,不是代码执行完后的返回值
console.log(data)
//监听data中的交易hash,来获取交易结果
let data2=await provider.waitForTransaction(data.hash);
console.log(data2)
其中的data只有交易hash和交易数据,连块hash都没有,更不用说请求结果了,这是为什么?这里我们需要理解一下区块链的运行原理,主要就是矿工费机制的影响。
矿工机制是为了鼓励用户部署节点,完善生态。地址在发起交易的时候需要为这次交易提供矿工费,矿工费由广播这次交易节点获得。
所以用户发起交易后这笔交易只会生成一个待执行的签名信息,其中包括一个唯一的交易hash和要广播的交易数据以及矿工费,然后在队列中等待打包。矿工每次可以打包一个块,一个块收取的矿工费总数是固定的,会优先打包给的矿工费高的交易,打包完后整个块会一起执行,所以如果你发现你的一笔交易迟迟没有反馈结果,就要考虑提高矿工费了。交易被打包成块执行完以后才会给反馈结果。
综上所述,区块链的交易发起和反馈结果在设计上是两部分,需要对应处理。data就是发起部分的数据,data2就是监听这次交易hash获取到的数据。文章来源:https://www.toymoban.com/news/detail-799374.html
我一直在使用ether,web3用来写了dom感觉过于繁琐,在实际项目中几乎没有用到,但有些大佬说web3才是厉害的,在此希望懂的朋友给点建议,应该深耕哪个框架,并说下原因文章来源地址https://www.toymoban.com/news/detail-799374.html
到了这里,关于前端请求智能合约的思路的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!