Wallet Connect and Argent

How to make sure your Dapp works seamlessly with Argent!

WalletConnect is an open source protocol for connecting decentralized applications ("Dapps") to mobile wallets, via QR code scanning or deep linking. A user can interact securely with any Dapp from their mobile phone, making WalletConnect enabled wallets a safer choice compared to desktop or browser extension wallets.

As a mobile wallet, Argent fully supports the WalletConnect protocol. Since Argent is a smart contract based wallet, you need to pay attention to some specific features. We have wrapped them in a small utility library available here: @argent/smartwallet-utils

Message signature

Externally Owned Accounts (EOA) can sign messages with their associated private keys, however, smart contracts cannot. EIP-1271 outlines a standard way for contracts to verify if a provided signature is valid when an account is a contract.

The Argent wallet implements theisValidSignature() method, as per EIP-1271. A Dapp that wants to verify the signature of an Argent user should therefore call isValidSignature() on the Argent wallet instead of ecrecover() (as would be used to verify signatures from EOA accounts).

contract ERC1271 {
// bytes4(keccak256("isValidSignature(bytes32,bytes)")
bytes4 constant internal MAGICVALUE = 0x1626ba7e;
/**
* @dev Should return whether the signature provided is valid for the provided data
* @param _hash EIP-191 compliant hash of the message
* @param _signature Signature byte array associated with _data
*
* MUST return the bytes4 magic value 0x1626ba7e when function passes.
* MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc > 0.5)
* MUST allow external calls
*/
function isValidSignature(
bytes32 _hash,
bytes memory _signature
)
public
view
returns (bytes4 magicValue);
}

The parameter _hash should be EIP-191 compliant and could be computed using:

// ethers.js
ethers.utils.hashMessage(message)
// web3js
web3.eth.accounts.hashMessage(message)

Wallet detection

We have developed and deployed a simple contract to detect if a given address corresponds to an Argent wallet. The contract exposes a single isArgentWallet(address) method that returns true if the code deployed at the input address matches a deployed version of the Argent wallet.

contract ArgentWalletDetector {
/**
* @notice Checks if an address is an Argent wallet
* @param _wallet The target wallet
*/
function isArgentWallet(
)
external
view
returns (bool);
}

The detector contract is deployed on Ropsten testnet and Mainnet:

Ropsten: 0xB5F49f8899D61b89DC8093e646F327d4E10e1277
Mainnet: 0xeca4B0bDBf7c55E9b7925919d03CbF8Dc82537E8

Approve ERC20 token and call contract

Our smart wallet contract wraps in a single transaction an approve ERC20 token and an arbitrary call contract. It is more user-friendly and better practice to approve only the amount required for the contract call.

/**
* @notice Lets the owner do an ERC20 approve followed by a call to a contract.
* We assume that the contract will pull the tokens and does not require ETH.
* @param _wallet The target wallet.
* @param _token The token to approve.
* @param _proxy The address to approve, which may be different from the contract being called.
* @param _amount The amount of ERC20 tokens to approve.
* @param _contract The address of the contract.
* @param _data The encoded method data
*/
function approveTokenAndCallContract(
uint256 _amount,
bytes calldata _data
) external;

Daily limit

Argent wallets are protected by a daily limit (rolling for 24 hours). The owner can spend up to the daily limit in a given 24 hours period.

Transfers and ERC20 token approvals exceeding the daily limit can be executed immediately by the owner, provided that they obtain approval from their guardians. This mechanism can be bypassed via a set of trusted addresses. Transfers to trusted addresses are immediate and allow an unlimited amount.

Note that adding an address to the whitelist is an action triggered by the wallet owner and takes 24 hours to be effective. For more information about Argent's security model, read the full specifications of the wallet.

Any transfer or contract call exceeding the user's daily limit may require some extra time before the transaction is submitted (due to a user's guardians also being required to sign).

Meta transactions and relayers

Argent wallet uses the concept of meta transactions. These are a particular type of transaction which are signed by one or more key pairs (in our case the wallet's owner and potentially their guardians) but are submitted to the Ethereum network by a relayer. The relayer pays the gas fee (in ETH) and the wallet will refund the relayer (in ETH or ERC20 tokens) up to an amount signed by the wallet's owner.

From a Dapp perspective, this is managed by the Argent mobile application. The Dapp will submit a regular { to, value, data } transaction to the web3 provider. This transaction will be transmitted to the Argent mobile application through Wallet Connect. The mobile wallet will transform the data into a meta transaction:

• to will be the Argent RelayerManager contract address

• data will be the encoded data of the call to the execute() method with the relevant parameters

The Dapp will receive the transaction hash in order to monitor the status of the transaction and events will be emitted as usual.

Our relayer has the ability to replay a transaction with a higher gas price due to fluctuating network conditions. The transaction hash is modified and the Dapp will not be aware of the new transaction hash.

One solution could be for the Dapp to observe a specific event being emitted instead of transaction status. We are working on defining a standard for a Dapp to be notified when a transaction is replaced and the transaction hash changes (contact us if you are interested in this).

SmartWallet Utils

As mentioned, we have wrapped wallet detection, message signature and "approve token and call contract" in a small utility library available here: @argent/smartwallet-utils. Then it becomes as easy as:

const swu = new SmartWalletUtils(web3Provider, address);
const walletHelper = await swu.getWalletHelper();
// Check if a message signature is valid
const isValid = await walletHelper.isValidSignature(hexMessage, signature);
// Trigger an Approve ERC20 tokens and call contract in one single transaction, if supported by the wallet
if (walletHelper.supportApproveAndCall) {
const txHash = await walletHelper.approveAndCall(erc20Contract, amount, spender, contract, data, gasLimit);
}

To improve the login experience for Argent users, we recommend showing an Argent branded login button at the top level. This ensures people less familiar with WalletConnect can log in to your app. When clicking on the Argent login, you then present the WalletConnect QR code.

You can find an example of this implementation on https://app.flexa.network/connect

FAQ

Can I use the ERC20 permit function?

It depends on how the ERC20 smart contract implements the permit() method. If you look below (source Etherscan) at the Maker DAI implementation, you will notice at line 24 the use of ecrecover() and not EIP-1271 to verify the signature. Here, the call will fail with Argent wallets.

function permit(
uint256 nonce,
uint256 expiry,
bool allowed,
uint8 v,
bytes32 r,
bytes32 s
) external {
bytes32 digest =
keccak256(abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR,
keccak256(abi.encode(PERMIT_TYPEHASH,
holder,
spender,
nonce,
expiry,
allowed))
));
require(holder == ecrecover(digest, v, r, s), "Dai/invalid-permit");
require(expiry == 0 || now <= expiry, "Dai/permit-expired");
require(nonce == nonces[holder]++, "Dai/invalid-nonce");
uint wad = allowed ? uint(-1) : 0;
}

I'm asking for ERC20 approval of an amount of$10^{53}$but it doesn't seem to work

Argent may override the "infinite value" and ask a user to input a smaller amount due to the additional security risk of allowing large persistent approvals. When checking the allowance in your Dapp you need to check for the exact amount the user wants to spend. A better option is to use our approveTokenAndCallContract() method where you ask for the exact amount required.

Argent require guardians whenever I use my Dapp

When calling a method on an ERC20 contract other than approve() or transfer(), guardians are required for security to avoid bypassing the daily limit.

Can I use 3rd party relayers with Argent

Yes, but the relayer needs to implement a call to the execute() method of our RelayerManager smart contract. You will have to get the wallet owner signing the correct payload which is not straightforward. Please contact us if you're interested in this topic.

Do you estimate gas?

Currently no, we use the gas limit provided by the Dapp through the Wallet Connect gas parameter.

Yes, Wallet Connect will work on mobile browsers (or native mobile apps) and will deep link to the Argent wallet

How can I test the integration

You can test Argent on Ropsten on both iOS and Android. Contact [email protected] to receive a test build.