The ongoing AMA series on Reddit features guests from the Brave/BAT team. The most recent AMA took place on August 18th with Peter Snyder, Senior Privacy Researcher and Director of Privacy at Brave.Read this article →
Towards a safer signing experience on Brave Wallet
Written by Anirudha Bose
Signing DApp transactions for EVM chains is a security nightmare. It’s very common for users to blindly approve a signature request while being completely oblivious to what the transaction is going to do behind the scenes. This is commonly referred to as “blind signing” in the world of cryptocurrency (not to be confused with blind signatures in cryptography).
Blind signing is unfortunately a very common attack vector in crypto, where an attacker tricks the user into signing a transaction that drains their wallet (since there’s no way for the user to distinguish between a legitimate and a malicious transaction). Earlier this year, in a phishing attack on OpenSea users, an attacker stole hundreds of NFTs by simply tricking victims into signing a transaction to sell all their NFTs for 0 ETH. This story is far too common for the crypto ecosystem to ignore.
With Brave browser release 1.45, we are excited to debut a smarter signing experience on Brave Swap for all EVM transactions. With our latest updates to Brave Wallet, our signing experience is getting a redesign that aims to take the stress out of approving transactions. On any trading platform that’s powered by the 0x API (including Brave Wallet internal swaps), you’ll see clear and straightforward descriptions of how the transaction will impact your token balances.
The perils of DeFi
Using DeFi apps today is a lot like going to a shop and asking the shopkeeper to take whatever cash they need from your wallet for the purchase. The contracts you interact with already have an approval to spend part or all of your token balances, using the
approve() method of the ERC20 interface. Consider the following transaction approval popup on Metamask, which is a Uniswap trade for USDC-ETH on the Optimism network:
Approving this transaction requires a leap of faith, since none of the original swap parameters like maker and taker assets, quote amounts, etc. are visible on the approval screen. You’re essentially signing complete gibberish. It’s not just poor user experience; it’s unsafe. This info can be spoofed to execute almost anything without your awareness.
What the hex?
Transactions are to smart contracts what HTTP requests are to web services. DApps use a data segment in EVM transactions, known as calldata, to indicate which function to invoke in a smart contract and with what parameters. The calldata however is just a sequence of bytes that’s not human readable. You can view the calldata segment of a transaction on Etherscan under the “Input Data” section, as in the following example:
Anatomy of EVM calldata
The calldata is a read-only, byte-addressable space where the data parameter of a transaction or call is held. This calldata is encoded by wallets and Web3 clients following the Contract Application Binary Interface (ABI). Let’s look at a simple contract with a method to add two
uint256 numbers, and see how you’d construct the calldata to execute this method:
The first four bytes of the calldata, known as function selector, specify the function to be called. Method arguments are packed in chunks of 32 bytes, with types smaller than 32 bytes zero padded if necessary. Fixed-size types like
address, etc. are represented within the available 32 bytes. However, dynamic types like bytes and arrays follow head-tail encoding. In this scheme, the data is packaged at the tail-end of the transaction’s calldata. The arguments are references in the calldata to where the content is.
Let’s break down the calldata example above based on the encoding rules to have a better understanding:
We can now update our example contract logic with heavily-commented inline assembly (albeit a bit contrived), to demonstrate how EVM parses the calldata behind the scenes:
Parsing complex calldata
Here’s a more realistic example of how to parse the calldata of the 0x contract function
sellTokenForTokenToUniswapV3, involving dynamic types. Recall that dynamic types use the head-tail encoding scheme, unlike fixed-size types. Our example is a swap trade from COW to USDC, using the following path for the order: COW -> WETH -> USDC.
The function we’re trying to invoke is:
Here’s the same calldata, but broken down and grouped to indicate how we can parse it to extract meaningful insights about the swap trade.
Note that parsing the calldata is not enough—we also needed to figure out the information embedded in the
encodedPath argument, which is a Uniswap V3 order path. Thanks to our newfound ability to make sense of the entire calldata segment, we can now infer that the raw hex represented the swap trade 1421.74 COW -> 1000 USDC.
The end product
Technical improvements can only be appreciated if an impact is felt by users. So the final piece of the puzzle was to design a UX that gives users all the necessary details to confidently approve a swap, while keeping it generic enough that it can be extended to a vast majority of DEX transactions in the future. After brainstorming with our product designers, here’s what the end product looks like:
A few highlights of this design:
- It displays all the security-critical information about the swap, such as sender account, maker and taker assets along with network information, quote amounts, and exchange rate.
- It works both for swaps from Brave Wallet as well as third-party DApps integrating the 0x API, on all EVM-compatible networks.
- The UX could easily be extended to support cross-chain bridge transactions, where the networks for maker and taker assets would be different.
- The origin of the transaction (i.e. eTLD+1 and the site favicon) are displayed to prevent phishing attacks.
We implemented an ABI decoder in brave-core for EVM chains to bring safety and transparency to the transaction approval process. This allowed us to reveal insights about what a seemingly opaque transaction is supposed to do, and build a dedicated UX around it to make the signing process more user-friendly.
In the 1.45 release of the Brave browser, we are bringing this capability to 0x swaps on Brave Wallet, and will soon extend it to Uniswap, Curve, PancakeSwap, and Sushiswap, which should cover more than 70% of all DEX volume on Ethereum.
Building a secure and transparent signing experience in Brave Wallet remains a strong focus for us on the Wallet team. We intend to expand on this work to bring similar UX improvements beyond swaps, such as interacting with lending, cross-chain bridges, and staking DApps.
Not using Brave Wallet yet? Download Brave today and click in the toolbar to get started.
Our user base has grown by more than 10x these past years, recently passing 32 million monthly active users, and if you add the increasing number of platforms, channels, and architectures to the mix, then our build and release capacity had to scale 100x.Read this article →
We propose a new privacy-first framework to solve recommendation by integrating federated learning with differential privacy. This work on private federated recommendation is only one example of how we intend to leverage federated learning with privacy on the Brave browser in the future.Read this article →
Ready to Brave the new internet?
Brave is built by a team of privacy focused, performance oriented pioneers of the web. Help us fix browsing together.Download Brave