-
Notifications
You must be signed in to change notification settings - Fork 440
Add metamask extension web3 #566
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 30 commits
4c8b1c8
a98b814
a956347
2d44686
f8c01a2
f056a53
42bf1fd
966c6db
b3ff173
6303215
a1f12b9
e275ad3
6ff55de
e8dc31c
83b02de
e40bcce
02c1543
034ce08
0ca3c18
40cc504
2f590e6
786cdb9
ba312be
6081cef
1314d64
26f14eb
7bbb18b
74da34f
3ba0757
e91483f
d9822ed
58188be
a68bc59
a312bf4
b8ee32c
5d315b3
88b707c
bbf0627
3e046a9
846d274
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,15 @@ | ||
// Copyright 2019-2021 @polkadot/extension-dapp authors & contributors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
import initMetaMaskSource from './metaMaskSource'; | ||
import singleSource from './singleSource'; | ||
|
||
// initialize all the compatibility engines | ||
export default function initCompat (): Promise<boolean> { | ||
return Promise.all([ | ||
singleSource() | ||
]).then((): boolean => true); | ||
singleSource(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note-to-self: After this can drop the singleSource. It was always great as a reference, but now that we have a proper reference available here, not needed anymore. |
||
initMetaMaskSource() | ||
]).then((): boolean => { | ||
return true; | ||
}); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
// Copyright 2019-2020 @polkadot/extension-dapp authors & contributors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
import type { Injected, InjectedAccount, InjectedWindow } from '@polkadot/extension-inject/types'; | ||
|
||
import detectEthereumProvider from '@metamask/detect-provider'; | ||
import Web3 from 'web3'; | ||
|
||
import { SignerPayloadRaw, SignerResult } from '@polkadot/types/types'; | ||
|
||
interface RequestArguments { | ||
method: string; | ||
params?: unknown[]; | ||
} | ||
|
||
interface EthRpcSubscription { | ||
unsubscribe: () => void | ||
} | ||
|
||
interface EthereumProvider { | ||
request: (args: RequestArguments) => Promise<any>; | ||
isMetaMask: boolean; | ||
on: (name: string, cb: any) => EthRpcSubscription; | ||
} | ||
|
||
interface Web3Window extends InjectedWindow { | ||
// this is injected by metaMask | ||
ethereum: any; | ||
} | ||
|
||
function isMetaMaskProvider (prov: unknown): EthereumProvider { | ||
if (prov !== null) { | ||
return (prov as EthereumProvider); | ||
} else { | ||
throw new Error('Injected provider is not MetaMask'); | ||
} | ||
} | ||
|
||
// transfor the Web3 accounts into a simple address/name array | ||
function transformAccounts (accounts: string[]): InjectedAccount[] { | ||
return accounts.map((acc, i) => { | ||
return { address: acc, name: 'MetaMask Address #' + i.toString(), type: 'ethereum' }; | ||
}); | ||
} | ||
|
||
// add a compat interface of metaMaskSource to window.injectedWeb3 | ||
function injectMetaMaskWeb3 (win: Web3Window): void { | ||
// decorate the compat interface | ||
win.injectedWeb3.Web3Source = { | ||
enable: async (): Promise<Injected> => { | ||
// win.web3 = new Web3(win.ethereum); | ||
|
||
const providerRaw: unknown = await detectEthereumProvider({ mustBeMetaMask: true }); | ||
joelamouche marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const provider: EthereumProvider = isMetaMaskProvider(providerRaw); | ||
|
||
await provider.request({ method: 'eth_requestAccounts' }); | ||
|
||
return { | ||
accounts: { | ||
get: async (): Promise<InjectedAccount[]> => { | ||
return transformAccounts(await provider.request({ method: 'eth_requestAccounts' })); | ||
}, | ||
subscribe: (cb: (accounts: InjectedAccount[]) => void): (() => void) => { | ||
const sub = provider.on('accountsChanged', function (accounts: string[]) { | ||
cb(transformAccounts(accounts)); | ||
}); | ||
// TODO: add onchainchanged | ||
|
||
return (): void => { | ||
sub.unsubscribe(); | ||
}; | ||
} | ||
}, | ||
signer: { | ||
signRaw: async (raw: SignerPayloadRaw): Promise<SignerResult> => { | ||
joelamouche marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const signature = (await provider.request({ method: 'eth_sign', params: [raw.address, Web3.utils.sha3(raw.data)] })as string); | ||
joelamouche marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
return { id: 0, signature }; | ||
} | ||
} | ||
}; | ||
}, | ||
version: '0' // TODO: win.ethereum.version | ||
}; | ||
} | ||
|
||
// returns the MetaMask source instance, as per | ||
// https://github.com/cennznet/singlesource-extension/blob/f7cb35b54e820bf46339f6b88ffede1b8e140de0/react-example/src/App.js#L19 | ||
export default function initMetaMaskSource (): Promise<boolean> { | ||
return new Promise((resolve): void => { | ||
const win = window as Window & Web3Window; | ||
|
||
if (win.ethereum) { | ||
injectMetaMaskWeb3(win); | ||
resolve(true); | ||
} else { | ||
resolve(false); | ||
} | ||
}); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,7 +27,7 @@ interface SingleWindow extends InjectedWindow { | |
SingleSource: SingleSource; | ||
} | ||
|
||
// transfor the SingleSource accounts into a simple address/name array | ||
// transform the SingleSource accounts into a simple address/name array | ||
function transformAccounts (accounts: SingleSourceAccount[]): InjectedAccount[] { | ||
return accounts.map(({ address, name }): InjectedAccount => ({ | ||
address, | ||
|
@@ -73,15 +73,13 @@ function injectSingleSource (win: SingleWindow): void { | |
// https://github.com/cennznet/singlesource-extension/blob/f7cb35b54e820bf46339f6b88ffede1b8e140de0/react-example/src/App.js#L19 | ||
export default function initSingleSource (): Promise<boolean> { | ||
return new Promise((resolve): void => { | ||
window.addEventListener('load', (): void => { | ||
const win = window as Window & SingleWindow; | ||
const win = window as Window & SingleWindow; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Believe I commented elsewhere - the original is correct. (All ok, since this will be dropped anyway) |
||
|
||
if (win.SingleSource) { | ||
injectSingleSource(win); | ||
resolve(true); | ||
} else { | ||
resolve(false); | ||
} | ||
}); | ||
if (win.SingleSource) { | ||
injectSingleSource(win); | ||
resolve(true); | ||
} else { | ||
resolve(false); | ||
} | ||
}); | ||
} |
Uh oh!
There was an error while loading. Please reload this page.