从零开始的合约交互小知识(1)
65f1273e08074de9b1083d57cc16977c
智能合约
智能合约与平时的代码其实没有什么区别,只是运行于一个以太坊这样的分布式平台上。这个运行的平台,赋予了这些代码不可变,确定性,分布式和可自校验状态等特点。代码运行过程中状态的存储,是不可变的。
在以太坊中,每个合约都有一个唯一的地址来标识它自己(由创建者的哈希地址和曾经发送过的交易的数量推算出来)。客户端可以与这个地址进行交互,可以发送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 扩展,可以允许你管理你的钱包,以及连接到不同的以太坊网络。
是否安装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();
}
- 智能合约
- Gas
- GWEI & gasPrice & gasLimit