【前言】
嗨嗨大家好,今天的主题延续昨天的检测是否已经安装插件後,紧接着而来的是 MetaMask 的弹出页面以及检测登入者是否更换帐户。今天的内容大部份都参考来自 MetaMask 的使用文件!
【Pop Up & Loading Message】
这里会跳出 MetaMask 的登入介面。并且在使用者登入时(输入密码时)呈现 Loading Message。
使用 useState
初始化 isLoging
查看使用者是否正在输入密码,也就是说如果使用者正在登入并且尚未完成登入,都可以归类在正在输入的状态,也就会呈现 Loading 图示。
export function OnboardingButton() {
const [isLoging, setIsLoging] = useState(false);
// 当 USER 正在 MetaMask 输入密码时要呈现 Loading Message
...
return (
<Wrapper>
{ !isLoging && <StyledButton disabled={isDisabled} onClick={onClick}>{buttonText}</StyledButton>}
{ isLoging && <StyledLink to="/login">Welcome Back! Direct to Dino LogIn</StyledLink>}
</Wrapper>
); // 和前面的前端实作一样都使用逻辑运算子来决定呈现的物件
}
【Ethereum Provider API - ethereum.request
】
在 Ethereum Provider API 中可以使用 ethereum.request
来透过 MetaMask 传送 RPC 需求给以太坊。在 param
中会利用传入的各种资讯来调用各种 RPC 方法。而 promise
会储存所有函式 calling 之後的结果。
interface RequestArguments {
method: string;
params?: unknown[] | object;
}
ethereum.request(args: RequestArguments): Promise<unknown>;
在分散式计算,远端程序呼叫(英语:Remote Procedure Call,缩写为 RPC)是一个电脑通信协定。该协定允许执行於一台电脑的程序呼叫另一个位址空间(通常为一个开放网路的一台电脑)的子程序,而程序设计师就像呼叫本地程序一样,无需额外地为这个互动作用编程(无需关注细节)。RPC是一种服务器-客户端(Client/Server)模式,经典实现是一个通过传送请求-接受回应进行资讯互动的系统。
节录自维基百科《远端程序呼叫》- https://zh.wikipedia.org/zh-tw/远程过程调用
先来看看第一个使用例子,是我们等下实作会用到的 eth_requestAccounts
,主要可以弹出 MetaMask 的登入介面。在 MetaMask 的使用介绍中有特别强调如果这个需求已经被送出,要使按钮暂时 disable
,这我们待会也会做到。并且尽量建议使用者每次使用时都需要重新登入。
/* eth_requestAccounts -------------------------------------------- */
ethereum
.request({ method: 'eth_requestAccounts' })
.then(handleAccountsChanged)
.catch((error) => {
if (error.code === 4001) {
// EIP-1193 userRejectedRequest error
// The request was rejected by the user
console.log('Please connect to MetaMask.');
} else {
console.error(error);
}
});
在 eth_requestAccounts
这个需求使用後,其 promise
会储存一个含有以太坊地址 String
的 array
。也就是使用的的以太坊地址。
来看一下第二个使用例子,eth_sendTransaction
可作为创建一个新的调用交易讯息或是用来创建合约。
/* eth_sendTransaction -------------------------------------------- */
params: [
{
from: '0xb60e8dd61c5d32be8058bb8eb970870f07233155', // 发送交易的地址
to: '0xd46e8dd67c5d32be8058bb8eb970870f07244567', // 交易的目标地址
gas: '0x76c0', // 30400 // gas 可用量,预设是 90000
gasPrice: '0x9184e72a000', // 10000000000000 // gas 价格,预设是 To-Be-Determined
value: '0x9184e72a', // 2441406250 // 交易发送的金额
data: // 合约的编译代码
'0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675',
},
];
ethereum
.request({
method: 'eth_sendTransaction',
params,
})
.then((result) => {
// The result varies by by RPC method.
// For example, this method will return a transaction hash hexadecimal string on success.
})
.catch((error) => {
// If the request fails, the Promise will reject with an error.
});
如果当前送出的 request 因为各种原因失败的话,将会回传一个 Ethereum RPC Error。
interface ProviderRpcError extends Error {
message: string;
code: number;
data?: unknown;
}
/* Error Message
- 4001
The request was rejected by the user
- 32602
The parameters were invalid
- 32603
Internal error
*/
【Ethereum Provider API - ethereum.on
】
MetaMask 提供了 Node.js EventEmitter
的 API,并且可以利用 listener
是否被加入来决定是否要进行之後的动作。
Events | Node.js v16.7.0 Documentation
第一个使用例子是 connect
。这个事件将会递交一个 RPC 要求给链,来确认 provider 是否已经连接到链上。可以搭配 ethereum.isConnected()
这个方法来检测当前链的连接状况。
interface ConnectInfo {
chainId: string;
}
ethereum.on('connect', handler: (connectInfo: ConnectInfo) => void);
第二个例子是 disconnect
。这个事件会在无法递交任何 RPC 要求给任何链时出现,在 MetaMask 的使用文件中表示这通常只会出现在网路连接有问题,或其他不可见的错误时。当 disconnect
这个事件被递交後,provider 将不能在接受新的 request
直到 connection
被重新建立。
ethereum.on('disconnect', handler: (error: ProviderRpcError) => void);
要重新建立连接可能需要使用者重新整理页面,同样的也可以同时利用 ethereum.isConnected()
这个方法来检测使用者是否是连接状态。
第三个例子是 accountsChange
。当 eth_accounts
的 RPC 回传值改变时这个事件就会被递交。由於 eth_accounts
回传的是一个阵列,可能是空或一个地址字串。如果这个地址跟最近使用的 Callers 帐户不同则会被判断为改变。在 MetaMask 的使用文件中表示 Callers 的异同是由他们的 URL origin
来判断,也就是说所有端点都会拥有相同的 origin 并分享同样的 permissions。
ethereum.on('accountsChanged', handler: (accounts: Array<string>) => {
// Handle the new accounts, or lack thereof.
// "accounts" will always be an array, but it can be empty.
});
Understanding "same-site" and "same-origin"
【连动 MetaMask By React.js】
这里回到我们的 Project 主轴,利用我们刚刚介绍到的 ethereum.request
弹出 MetaMask 的登入介面,来得到使用者的以太坊帐户资讯。
export function OnboardingButton() {
...
const onClick = () => {
if (MetaMaskOnboarding.isMetaMaskInstalled()) {
window.ethereum
.request({ method: 'eth_requestAccounts' })
.then((newAccounts) => setAccounts(newAccounts));
} else {
onboarding.current.startOnboarding();
}
};
return (
...
);
}
使用 useEffect
随时检测是否更换帐户以及如果有的话就更新地址资料。
export function OnboardingButton() {
...
useEffect(() => {
function handleNewAccounts(newAccounts) {
setAccounts(newAccounts);
}
if (MetaMaskOnboarding.isMetaMaskInstalled()) {
window.ethereum
.request({ method: 'eth_requestAccounts' })
.then(handleNewAccounts);
window.ethereum.on('accountsChanged', handleNewAccounts);
return () => {
window.ethereum.off('accountsChanged', handleNewAccounts);
};
}
}, []);
...
return (
...
);
}
而我们现在就可以成功地透过 accounts[0]
来取用登入者的地址!
ethereum.on('accountsChanged', function (accounts) {
// Time to reload your interface with accounts[0]!
console.log(accounts[0])
});
【小结】
今天的内容有一点多,但大部分都是 MetaMask 使用文件有提到的说明,所以只是加以翻译并且套用,并没有非常困难,吗。
【参考资料】
JSON RPC · ethereum/wiki Wiki
json-rpc
MetaMask-GitHub
前言 这篇将介绍 boxenn 与 DAL 层的依赖关系和介面。 简易 Class Diagram ...
WordPress.org 与 WordPress.com,试用时完全不知道有分这两个 在前几天的文...
前言 这篇文章主要来介绍 Service 元件 内容会谈到 Service 的功能与不同种 Serv...
前言 在介绍JDK有哪些工具时,第二大列应该是『故障排查、分析、监控和管理工具』,但我想先从监控工具...
昨天我们已经能把单一地点的天气资讯显示到手机App上面了,接着我们会利用UIButton让我们可以选...