-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Brave Wallet developer information
The following information is mostly useful to Brave developers.
Please see https://github.com/brave/brave-browser/wiki/Brave-Wallet for general information about the Brave Wallet.
Please see https://github.com/brave/brave-browser/wiki/Ethereum-Provider-API for information about the global Ethereum provider object.
The Brave Wallet is natively implemented as part of brave-core. Front ends of the Brave Wallet include the Brave desktop WebUI page and panel, Android native UI, and iOS native UI. These front ends communicate with the Brave Wallet through the Brave Wallet mojo interface.
Most front end functionality is accomplished through one of the following singleton controllers:
-
KeyringController
: Implements the HD wallet, Ledger & Trezor integration, account management, and signing. -
EthJsonRpcController
: Deals with the ETH JSON RPC API, as well as things like the user's current network -
SwapController
: Implements swapping related functionality through the 0x API -
AssetRatioController
: Used for cryptocurrency prices over time -
ERCTokenRegistry
: List of tokens and coins that are supported (This is likely to get renamed in the future). -
EthTxController
: For transaction management such as creation, broadcasting, and storing transactions. -
BraveWalletService
: For basically everything else including the default wallet setting, user assets, permission checks, importing functionality and much more.
Each controller typically also has an Observer
named after it which allows the front end to update dynamically as changes are made in other places (such as settings).
For each renderer there is also a BraveWalletProvider
which is used by the renderer process for interaction from window.ethereum
.
You can debug Dapps that aren't compatible with our wallet but do work with MetaMask by using a Proxy object.
It may also be useful to add logging for the method name in components/brave_wallet/renderer/brave_wallet_js_handler.cc
.
Paste this into the console of the Dapp that isn't working:
const handler = {
get: function(target, prop, receiver) {
console.log('accessing prop: ', prop, ...arguments)
if (typeof target[prop] === 'function') {
return new Proxy(target[prop], {
apply: (targetFunc, thisArg, args) => {
console.log('args: ', ...args)
return targetFunc.call(target, ...args);
}
});
}
return Reflect.get(...arguments);
},
set(target, prop, val) { // to intercept property writing
console.log('Setting prop to', prop, val, ...arguments)
return Reflect.set(...arguments);
},
apply: function(target, thisArg, args) {
console.log('apply on target with args: ', target, ...args)
return target(...args)
}
};
const ethereumProxy = new Proxy(window.ethereum, handler);
window.ethereum = ethereumProxy
Note: This won't work if you have default wallet set to Brave Wallet
. It needs to be set to Brave Wallet (Prefer extensions)
because otherwise window.ethereum
is not writable.
Bitcoin and Ethereum generate addresses in different ways, but they share the same key derivation implementations as BIP-32 compatible wallets.
The Brave wallet implements BIP-32 (HD wallet), BIP-39 (Mnemonic keywords), BIP-43 (Multipurpose HD wallet structure), and BIP-44 (Multicurrency and multi account wallets).
- Generate mnemonic words from random 128-256bits (BIP39 https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki)
- Generate seed (512 bits) from the mnemonic words in step 1 with an empty passphrase (salt
mnemonic
) using PBKDF2 (BIP39). Note: The test vectors use passphrase TREZOR (saltmnemonicTREZOR
). - Use the seed to generate the master key / root key (256bits) and master chain code (256 bits) (BIP32 https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)
- Use the master private key above to generate a master public key using secp256k1
- Derive the extended private key and public key at least with 3 hardened derivations and 1 normal derivation from master keys before we can use the keys (BIP44 https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) m / purpose' / coin_type' / account' / change / address_index Where purpose’ is 44' coin_type’ is 60' (See https://github.com/satoshilabs/slips/blob/master/slip-0044.md#registered-coin-types for example Ethereum is 60’ and Ethereum Classic is 61’ Bitcoin is 0’, Binance Smart Contract is 519’, Binance is 714’). Note that MetaMask uses 60’ for the key path no matter which networks you add. We do the same for compatibility. account’ is : 0' change is: 0
Account 0 Private key: m/44'/60'/0'/0/0 Public key: M/44'/60'/0'/0/0
…
Account N Private key: m/44'/60'/0'/N/0 Public key: M/44'/60'/0'/N/0
The mnemonic words are encrypted with AES-GCM symmetric encryption and stored in preferences with the user passphrase.
Which account paths are created / derived are stored in preferences as well. We will support discovery of them when restoring too via https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#account-discovery
Bitcoin and Ethereum use an elliptic curve named secp256k1. We use Boring SSL in Brave, but that library does not support secp256k1. Instead we use libsecp256k1
from Bitcoin Core.
On Ethereum, both externally-owned accounts and contract accounts are 40-character hex encoded strings with a “0x” prefix. 0x + the last 20 bytes of the keccak
hash of the public key.
For testing: http://bip32.org/
Bitcoin addresses are encoded using bech32
base 58 encoding (base-58 encoding also from https://github.com/bitcoin/bitcoin).
https://en.bitcoin.it/wiki/BIP_0173#Segwit_address_format https://en.bitcoin.it/wiki/Segregated_Witness
It is common to work in very large numbers in Ethereum for both ETH and for tokens. For example eth = 1,000,000,000,000,000,000 wei. We use unsigned _ExtInt(256)
type for working with these large numbers. For mojo interfaces which allow communication across processes and languages, we support a second format of a hex encoded string, e.g. “0xDE0B6B3A7640000”. There are conversion functions to and from the hex string format and the uint256_t
function.
Hashing in Ethereum uses something close to SHA3 with slightly different parameters than SHA3. In particular it uses Keccak F1600
(ether version). Since we don’t have the ability to get the keccak hash in Chromium, we’ve added this third party dependency:
https://github.com/chfast/ethash.git
Apache License, Version 2.0.
For reference:
Keccak256("") =
c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470
SHA3("") =
a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a
The following JSON RPC API is exposed: https://eth.wiki/json-rpc/API#json-rpc-methods
This API will be used by the in page provider (window.ethereum
object) and also exposed to the Brave Wallet WebUI via mojo.
window.ethereum
and window.solana
are only available on secure contexts.
localhost
and 127.0.0.1
is considered secure even when over HTTP.
But if you want to test with some other host name, you can either use an HTTPS server locally, or use this flag: brave://flags/#unsafely-treat-insecure-origin-as-secure
For example if you add z.com as 127.0.0.1 in your hosts file and then start it on an HTTP server:
- Bump
package.json
in brave-core -
npm install
to get the bumped version in node_modules - Clone https://github.com/brave-experiments/Javascript-Secure-Plugin
- Copy
node_modules/@solana/web3.js/lib/index.iife.min.js
into the above repo assolana-web3.js
-
npm install
andnpm run build
in the plugin repo - Edit the
Build/test.js
to remove lines abovevar __webpack_exports__ = {};
and the last line/******/ })()
- Add Brave license and
/* Auto generated from @solana/web3.js 1.47.1 index.iife.min.js */
(replace 1.47.1 with the bumped version) - Copy
Build/test.js
to brave-core incomponents/brave_wallet/resources/solana_web3_script.js
- sync and build brave-core
- Go to https://github.com/darkdh/solana-provider-test to test
connect
andsend transaction
without any console error. - If there is no errors, then go ahead and open the PR, otherwise go to step 12.
- Trace the
solana_web3
error line number from console - Looking for something that begins with
$
and add missing builtins intocomponents/safe_builtins/renderer/safe_builtins.cc
.
- Let say if it is
$Array.from()
then add it to the the second array of$Array
block because it is a dynamic method. - If it is
Array.prototype.flat()
then addflat
to the first array of$Array
block as static method.
- Repeat step 12-14 until there is no errors and open a PR.