In-Page Provider
Overview
The in-page provider exposes protocol-specific functionality through the web browser to dapps. A dapp is a web app that interacts with smart contracts through its front end code. The in-page provider lets dapps generate signatures with the user's keys.
We'll discuss in-page providers in the context of the Ethereum ecosystem (EIP-1193) in the following.
Signatures
Dapps can request to generate on- and off-chain signatures with the user's keys through the in-page provider.
On-Chain Signatures
An on-chain signature is a signature that can be submitted along with the signed
data through the
eth_sendRawTransaction
method. The on-chain signature is verified by the validator node before a
transaction is included in a block.
The data signed by on-chain signatures follows a standard format, and it can be used to simulate the on-chain changes caused by a signature.
Spender Approvals
Both the fungible (EIP-20) and non-fungible token standards (EIP-721, EIP-1155) support approving a spender other than the owner for tokens with a transaction. This mechanism is typically used to implement a clearing mechanism for exchanges.
Once a spender approval is granted to a third party, the user's token can be transferred by that third party without further involvement from the user. The application must keep track of approvals given by the user through the in-page provider and let the user revoke them later.
Off-Chain Signatures
An off-chain signature is a valid signature that validator nodes refuse to accept for a transaction.
Off-chain signatures are used for off-chain authorization purposes (e.g. sign-in with Ethereum) or for gasless transactions. There are two types of gasless transactions: meta-transactions and the approve-then-sign pattern. There are no standards for the payload that the user signs for either type. The payload cannot be used to simulate the on-chain changes caused by a signature.
Meta-Transactions
Meta-transactions are governed by the
EIP-2771 standard. Meta-transactions
require opt-in by the token implementor to trust an other smart contract to
verify that the transaction originates from the token owner (as opposed to
checking msg.sender
normally). OpenGSN is a popular
relayer.
There is no requirement in EIP-2771 that meta-transaction relayers verify the user's ownership of a token through off-chain signatures, but it seems to be common practice.
Approve-Then-Sign
In the approve-then-sign pattern, the user approves a spender through the standard token interface methods and then the spender collects an off-chain signature from the user to transfer the token without needing an additional on-chain transaction. A popular example is OpenSea's Seaport framework.
We should note that there is no standard that requires the spender to request an off-chain signature from the token owner before they'd spend the token.
Data Portability
Being able to port data across applications permissionlessly is the core value proposition of Web3. There are three means of data portability in Web3:
- token transfers
- copying keys
- performing signatures with the keys
The in-page provider plays a central role in data portability between web apps. It lets multiple web apps perform signatures with a key without granting the web apps access to the key material.
While the key material is safe in this setting, the security of the user's assets relies on the user's ability to detect malicious signature requests. This is fundamentally unsafe, since it is impossible for non-technical users to detect malicious signature requests and social engineering attacks can put even the most technically sophisticated user under enough pressure to make a mistake.
Dapp Keys And Cross-Connect
At SealVault, we automate what we can for users and when we cannot automate something, we reduce decisions to their simplest form. For this reason, we distinguish between two modes of operation for the provider. In the first mode, the provider generates signatures with a key specifically created for the dapp (dapp key). In the second mode, the provider generates signatures with a key not specifically created for the dapp (cross-connected key).
With dapp keys, we can completely automate the in-page provider for the user, but in order to support all aspects of data portability, we have to let users connect dapp keys to other dapps. As mentioned, this is fundamentally unsafe, and the Ethereum ecosystem is slowly moving away from this model with signature based delegation,1 but many dapps still rely on it.2 We support cross-connecting wallets and dapp keys and reduce the security decisions user have to make when a key is cross-connected to sign-in, payment approval or token pledge decisions.
New Dapp Flow
When a new dapp requests to connect for the first time, the in-page provider asks the user through a dialog if they want to add this new dapp. If the user wants to add the new dapp, they're asked to select an existing address (wallet or dapp key) to connect. Alternatively, the user can also choose to create a new dapp key.
Prompting the user whether they want to add a new dapp before letting them select which address to connect protects the user from phishing that relies on misidentifying the dapp the user interacts with.
flowchart TB
new_dapp([New dapp requests connect]) --> add_dapp{{Add new dapp?}}
add_dapp -- Yes --> select_or_create{{Select address or create dapp key?}}
select_or_create -- Select address --> select_address[\User selects address/]
select_address --> connect_selected([Connect selected address])
select_or_create -- Create dapp key --> create_key[App creates new dapp key]
create_key --> connect_new([Connect new dapp key])
add_dapp -- No --> reject([Reject connect request])
select_or_create -- Cancel --> reject
%% CSS-based class defs don't work
classDef green stroke:green;
classDef red stroke:red;
classDef yellow stroke:#e9c46a;
class connect_selected,connect_new green
class new_dapp yellow
class reject red
Added Dapp
When a dapp that was added before by the user requests to connect, if dapp key was last connected, then auto connect it, else let the user select user which key to connect. The default in the selection is the last connected address and the user can also choose to create a dapp key here.
flowchart TB
added_dapp([Added dapp requests connect]) --> last_connected{{Last connected dapp key?}}
last_connected -- Yes --> connect_dapp_key([Connect dapp key automatically])
last_connected -- No --> select_or_create{{Select address or create dapp key?}}
select_or_create -- Select address --> select_address[\User selects address/]
select_or_create -- Create dapp key --> create_key[App creates new dapp key]
create_key --> connect_new([Connect new dapp key])
select_address -- Yes --> connect_selected([Connect selected])
select_or_create -- Cancel --> reject([Reject connect request])
%% CSS-based class defs don't work
classDef green stroke:green;
classDef red stroke:red;
classDef yellow stroke:#e9c46a;
class connect_new,connect_selected,connect_dapp_key green
class added_dapp yellow
class reject red
-
E.g. Delegate Cash, Farcaster, Delegatable, MUD. ↩
-
E.g. Sunflower Land, Phi, Lens ecosystem. ↩