rchain-client-api
Version:
RChain client for node.js, browsers
104 lines (93 loc) • 3.02 kB
JavaScript
// @ts-check
// adapted from https://github.com/tgrospic/rnode-client-js/blob/e4d97ee436a9a35ed2fe7b6c3307d03e4e2e9612/src/eth/eth-wrapper.js
const { freeze } = Object;
/**
* "To notify sites of asynchronous injection,
* MetaMask dispatches the ethereum#initialized event
* on `window` immediately after the provider
* has been set as `window.ethereum`."
* -- https://github.com/MetaMask/detect-provider
*/
export const MetaMask = {
initialized: 'ethereum#initialized',
};
/**
* Metamask wrapper for Ethereum provider
* https://metamask.github.io/metamask-docs/guide/ethereum-provider.html#methods-new-api
*
* @param {{ window: {
* ethereum?: MetaMaskProvider,
* addEventListener: typeof window.addEventListener,
* removeEventListener: typeof window.removeEventListener,
* }}} io
*
* @typedef {{
* readonly isMetaMask: boolean,
* autoRefreshOnNetworkChange: boolean,
* request: (args: RequestArguments) => Promise<unknown>
* }} MetaMaskProvider
*
* EIP-1193: Ethereum Provider JavaScript API
* https://eips.ethereum.org/EIPS/eip-1193
* @typedef {{
* readonly method: string;
* readonly params?: readonly unknown[] | object;
* }} RequestArguments
*/
export function getEthProvider({ window }) {
return new Promise((resolve) => {
if (window.ethereum) {
resolve(window.ethereum);
}
// eslint-disable-next-line no-use-before-define
window.addEventListener(MetaMask.initialized, handle, { once: true });
function handle() {
window.removeEventListener(MetaMask.initialized, handle);
const { ethereum } = window;
if (!ethereum)
throw new Error(
`${MetaMask.initialized} event fired with window.ethereum not defined.`,
);
resolve(ethereum);
}
});
}
/**
* @param {MetaMaskProvider} ethereum
*/
export function MetaMaskAccount(ethereum) {
// https://docs.metamask.io/guide/ethereum-provider.html#properties
ethereum.autoRefreshOnNetworkChange = false;
return freeze({
/**
* Request an address selected in Metamask
* - the first request will ask the user for permission
* @returns { Promise<string> } ETH address in hex format
*/
async ethereumAddress() {
const accounts = await ethereum.request({
method: 'eth_requestAccounts',
});
if (!Array.isArray(accounts))
throw Error(
`Ethereum RPC response is not a list of accounts (${accounts}).`,
);
return accounts[0];
},
/**
* Ethereum personal signature
* https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_sign
* @param {Iterable<number>} bytes
* @param {string} ethAddr
* @returns { Promise<string> } signature in hex format
*/
async ethereumSign(bytes, ethAddr) {
const sig = await ethereum.request({
method: 'personal_sign',
params: [[...bytes], ethAddr],
});
if (typeof sig !== 'string') throw new TypeError(typeof sig);
return sig;
},
});
}