Signing message using Metamask in Flutter
Tags: flutter, blockchain, tutorial, metamask
Published: 2022-06-28 23:29:16

When it comes to decentralized applications, signed messages provide a way to authenticate a user or specific data. It plays a crucial role when it comes to meta-transactions (off-chain transactions) and helps in preventing [Replay Attacks](https://medium.com/cypher-core/replay-attack-vulnerability-in-ethereum-smart-contracts-introduced-by-transferproxy-124bf3694e25).

## What we are going to build?

This blog is a continuation of our [Building with Flutter and Metamask](https://dev.to/bhaskardutta/building-with-flutter-and-metamask-8h5) series where we learn how to build an app that can directly interact with the blockchain using Metamask. In this blog, we will learn how we can use Metamask to sign a particular message. After this tutorial, there will have an additional “Sign Message” step in the login process.


![Signing Message GIF](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kg84gsxge6appteuauss.gif)

The user signing the message represents the user agrees to the Terms and Conditions provided by our app. While building this feature, we will also learn about how these signatures are verified by the t contracts.

## Why sign a message?

A signature is the most common form to denote authorization. For example, in the real world, signing any legal document implies that the signer agrees to all the terms and conditions mentioned in the signed document. In the blockchain ecosystem, providing a signature implies a similar meaning. The user is asked to sign a message, and this signature can be later verified by the smart contract for performing several actions. [The Magic of Digital Signature on Ethereum](https://morten.dev/posts/the-magic-of-digital-signatures-on-ethereum) provides an in-depth view of digital signature and signature verification.

[EIP-191](https://eips.ethereum.org/EIPS/eip-191) and [EIP-712](https://eips.ethereum.org/EIPS/eip-712) are two of the most popular proposed standardization for signing messages in the Ethereum blockchain ecosystem. EIP-712 provides a more human-readable format of data while signing it, whereas EIP-191 is much more simple in implementing but users are asked to sign a hash, that is difficult to comprehend. Although EIP-712 provides a far superior user experience, it is still in its early stages of development.

For this tutorial, we will keep it very simple and provide our users with a hash of a simple text message for signing. We can also provide the raw text for signing, but this would make the process of signature verification more difficult as we must pass the length of the message signed for verification. We use `keccak256` for hashing the message before signing it. Using `keccak256` for hashing the message ensures that the hash will always be 256-bits long, regardless of the actual length of the message. It makes the process of signature verification significantly easier as the length of the signed message will always be 256-bit long. This will be more clear as we proceed with our application development and testing.

## Pre-requisites

- Readers are highly encouraged to complete the [first part](https://dev.to/bhaskardutta/building-with-flutter-and-metamask-8h5) of this series.
- Have all the pre-requisites mentioned in the first part.
- You can continue with your code, however, in this tutorial, we will start from `login-with-metamask` branch of the [FlutterAppWithMetamask](https://github.com/BhaskarDutta2209/FlutterAppWithMetamask) repo.
- Remix-ide for writing and testing smart contracts.

## How to use signatures

Before we proceed with our application development, it is important to understand how messages are signed and verified using a smart contract. Let’s assume we want to sign the message “Hello Developers!”, the steps followed for this are shown in the following diagram:


![Flow Diagram for signing message](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fntnph22em2kj4uz5lcy.png)

The steps followed here are as follows:

1. We start with the Raw data, in this example, it is the string “Hello Developers!”. In more advanced situations, the raw data can be something like serialized JSON.
2. We use the keccak256 hashing algorithm to generate the hash of the Raw message. It is important to note that the generated hash is in form of a hexadecimal number.
3. Before signing, the passed message is structured in the following format: 
    
    ```
    "\x19Ethereum Signed Message:\n" + length(message) + message
    ```
    
    The prefix denotes that the signed message is intended to be used in the Ethereum blockchain network. Since we are already hashing the raw message before passing it for signing, we know that the size of the `message` will be `32 bytes`.
    
    Hence if we follow the above format of hashing the raw message before sending it to get signed, we can deterministically say that the prefix will always be `“\xEthereum Signed Message:\n32”`.
    
4. Finally, the formatted message is signed using the private key and a signature is produced in hexadecimal number format.

Using solidity, we can write a function that will take in the generated signature and the raw message to compute the account that signed the message. This forms a basis to verify whether the concerned account has provided the required signatures.

### Signature verification using solidity

Although writing a smart contract for signature verification is a complex topic and demands a tutorial of its own, the following YouTube video proves to be very helpful:

[https://www.youtube.com/watch?v=vYwYe-Gv_XI&t=647s](https://www.youtube.com/watch?v=vYwYe-Gv_XI&t=647s)

I will use a smart contract similar to the one shown in the tutorial for demonstrating the above-explained idea and testing the generated signature in a later phase of this blog. The smart contract I am using is:

```solidity
//SPDX-License-Identifier: Unlicensed

pragma solidity ^0.8.4;

contract SignaturesInEth {

    function _split(bytes memory signature) internal pure returns(bytes32 r, bytes32 s, uint8 v) {
        require(signature.length == 65, "Signature must be 65 bytes long");
        assembly {
            r := mload(add(signature, 32))
            s := mload(add(signature, 64))
            v := byte(0, mload(add(signature, 96)))
        }
    }

    function getMessageHash(string memory message) public pure returns(bytes32) {
        return keccak256(abi.encodePacked(message));
    }

    function getEthSignedMessageHash(bytes32 messageHash) public pure returns(bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", messageHash));
    }

    function recover(bytes32 ethSignedMessageHash, bytes memory signature) public pure returns(address) {
        (bytes32 r, bytes32 s, uint8 v) = _split(signature);
        return ecrecover(ethSignedMessageHash, v, r, s);
    }

    function verifySignature(string memory message, bytes memory signature) public pure returns(address) {
        bytes32 messageHash = getMessageHash(message);
        bytes32 ethSignedMessageHash = getEthSignedMessageHash(messageHash);

        return recover(ethSignedMessageHash, signature);
    }

}
```

### Sample verification

Let’s take a small tangent and verify the details shown in the above flow diagram. The details shown in the flow diagram are as follows:

```
Raw Message: ****Hello Developers!
Hash of Raw Message: 0x8a67fef1b0a61b7d4e8f0921c7c93372ab0e38f1317b2e9431cdca324c041792
Eth-Signed Message: 0x21d8c6f7bbaa104e61dfba68f68eabdfe47968b3f9b3fea6d9146214743cb01d
Signature: 0xa01b7764293ccab794ef74b7d3d21f88f62eeac7acc2959c422d254a3aadb60f08f7c4afd25b088af870772d20df8482d85f0b675441bbe61474d75c5c2663761b
Signer: ****0xcDb333BCE61C1E4a3Da9F4a2831e23b155f3949B
```

We can use the above smart contract to verify this details. Paste the contract into [Remix](https://remix.ethereum.org/#optimize=false&runs=200&evmVersion=null&version=soljson-v0.8.7+commit.e28d00a7.js) and deploy it to the `Javascript VM`.

To start with, we can use the `verify` function passing in the *Raw Message* and *Signature* to obtain the signing account. We can then verify manually that the returned signer is the correct account signing the message.


![Verification Example](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i65pv9bfnj0kefmx0051.png)

Similarly, we can also use the other functions to calculate the other values specified in the flow diagram. We will again use this contract after we have completed our app but for now, let's back to our main objective of app development.

## Developing the App

If you want to continue with your code, that is highly encouraged, however, if you want to have a common starting point with this tutorial, clone [this](https://github.com/BhaskarDutta2209/FlutterAppWithMetamask) repo and create a new branch, called `signing-message` from the `login-with-metamask` branch.

```bash
git clone https://github.com/BhaskarDutta2209/FlutterAppWithMetamask
cd FlutterAppWithMetamask
git checkout login-with-metamask
git checkout -b signing-message
```

Open the project folder in your code editor of choice and we are *Ready To Go!* 

### Installing dependencies

For this tutorial, we will be using `keccak256` for hashing our message. For doing so, we will need the [web3dart](https://pub.dev/packages/web3dart) package. For this blog we will be limiting ourselves to only the [crypto](https://pub.dev/documentation/web3dart/latest/crypto/crypto-library.html) library, however, in future blogs, this same package will also be used for interacting with the smart contracts. To add the package, type the following command:

```bash
flutter pub add web3dart
```

### Creating a  function for signing message

Open the `login_page.dart` file inside the `_LoginPageState` class where we have previously defined `_session` and `_uri` variables, define a new variable called `_signature` for storing the signed data.

```dart
var _session, _uri, _signature;
```

Now inside the same class we define a new function called `signMessageWithMetamask` as shown below:

```dart
signMessageWithMetamask(BuildContext context, String message) async {}
```

Besides the `BuildContext`, this function takes in a `String` parameter that represents the message to be signed. In the later section of this blog, we will see that the `message` is nothing but the `keccak256` hash of the data we want to be signed.

### Writing the function body

The main content of the function body is as follows:

```dart
signMessageWithMetamask(BuildContext context, String message) async {
  if (connector.connected) {
    try {
      print("Message received");
      print(message);

      EthereumWalletConnectProvider provider =
          EthereumWalletConnectProvider(connector);
      launchUrlString(_uri, mode: LaunchMode.externalApplication);
      var signature = await provider.personalSign(
          message: message, address: _session.accounts[0], password: "");
      print(signature);
      setState(() {
        _signature = signature;
      });
    } catch (exp) {
      print("Error while signing transaction");
      print(exp);
    }
  }
}
```

Here, we are doing the following:

- First, we check whether we are already connected with an external wallet using the `connect` variable defined in the previous tutorial.
- If we are connected, we proceed with signing our message. We are using `try-catch` block for error handling and are printing the error to the console. In a production-ready app, we should properly notify the user about any caused error. The most common cause where an error can be generated is if the user rejects signing the message, after the metamask popup.
- Before signing the message, we must define a `provider`. This step may seem familiar if you have used `web3.js` or `ethers.js` before. Since our targeted blockchain is `Polygon`, which is a fork of the `Ethereum` blockchain network, the provider type is `EthereumWalletConnectProvider` and we pass the `connector` as an argument.
- We want the Metamask app to pop up for signing the message. To open the Metamask app, we use the `launchUrlString` function passing in the `_uri`. This is similar to how we opened Metamask during login.
    
    ***Note:*** Here we are not using `await` keyword, because we want to open Metamask and continue with the rest of our function execution and not wait for Metamask to close. After a user signs the message, metamask will close automatically.
    
- Now we use the `personalSign` method in the `provider` object defined for signing the message passed as a function parameter. This method also takes the signing account as a parameter. We use `_seesion.accounts[0]` to pass the current metamask account connected with the app.
    
    Here we are using `await` keyword because we want the function execution to wait until the user signs the data.
    
- Finally, we use `setState` method to update the value of `_signature` variable with the latest obtained signature. Using the `setState` function will also re-render the app screen and will display the updated information.

### Adding the button

We want to add a simple `Sign Message` button before the `Slide to login` slider. For showing the button we will use a similar `ternary-operator` syntax used in our previous tutorial. We check whether the `_signature` variable is `null` as a `null` value would signify that the message is not signed. If the value of `_signature` is set, aka the message is signed by the user, we display the signature on the app screen. 

Inside our `login_page.dart` file, we replace the `SliderButton()` section with the following code:

```dart
...
(_signature == null)
  ? Container(
      alignment: Alignment.center,
      child: ElevatedButton(
        onPressed: () =>
          signMessageWithMetamask(
            context,
            generateSessionMessage(
              _session.accounts[0])),
        child: const Text('Sign Message')),
    )
  : Column(
      crossAxisAlignment:
        CrossAxisAlignment.center,
      children: [
        Row(
          children: [
            Text(
              "Signature: ",
              style: GoogleFonts.merriweather(
                fontWeight: FontWeight.bold,
                fontSize: 16),
            ),
            Text(
              truncateString(
                _signature.toString(), 4, 2),
              style: GoogleFonts.inconsolata(
                fontSize: 16))
          ],
        ),
        const SizedBox(height: 20),
        SliderButton(
          action: () async {
            // TODO: Navigate to main page
          },
          label: const Text('Slide to login'),
          icon: const Icon(Icons.check),
        )
      ],
    )
...
```

Sorry for the mess that this Dart code is, but if you look closely into the code, it only does the following things:

- If the value of `_signature` is `null`, we create an `ElevatedButton` with the text `Sign Message`. On clicking this button, the `signMessageWithMetamask` function is called passing in the current context and the message to be signed as a parameter. To obtain the message to be signed we are using the `generateSessionMessage` function that takes in the currently connected account as a parameter. We will define this function in the next section.
- If the value of `_signature` is not `null`, we show the signature on the app screen. Since the signature is a very long string, we use a function called `truncateString` to truncate it in the `AAAA…BB` format. We will define this function also in the next section.

### Creating the helper functions

Inside the `utils` folder, we create a new file named `helperfunctions.dart`. In this file, we will specify the two helper functions used above. We start with the `truncateString` function, which is the easier of the two to implement.

```dart
String truncateString(String text, int front, int end) {
  int size = front + end;

  if (text.length > size) {
    String finalString =
        "${text.substring(0, front)}...${text.substring(text.length - end)}";
    return finalString;
  }

  return text;
}
```

In this function:

- `front` and `end` represent the number of characters to be displayed before and after the truncation symbol (`…`) respectively.
- We check if it’s possible to truncate the string based on the passed parameters, i.e. the passed `text` has a length greater than `front` + `end`. If so, we use the `substring` method to truncate the string to our requirement.
- Finally, we return the truncated string or the original string if truncating is not possible.

Now we define the `generateSessionMessage` function. Since in this function we will need to implement `keccak256` hashing, we import the `web3dart/crypto.dart` package by adding the following line at the top of our script:

```dart
import 'package:web3dart/crypto.dart';
```

This will give us access to a function called `keccak256Utf8` which uses the keccak256 hashing algorithm. This will return the data in form of an array of `bytes`. We will then use another function called `bytesToHex` to convert this array to hexadecimal string format. We prefix `“0x”` with the generated string to denote that the string is in hexadecimal format. For simplicity, we have initialized the raw message as a string containing the account address of the user. However, it is possible and encouraged to use a more complex data structure for the raw message e.g. serialized JSON. The complete function looks like this:

```dart
String generateSessionMessage(String accountAddress) {
  String message = 'Hello $accountAddress, welcome to our app. By signing this message you agree to learn and have fun with blockchain';
	print(message);

  var hash = keccakUtf8(message);
  final hashString = '0x${bytesToHex(hash).toString()}';

  return hashString;
}
```

Finally, we import our helper functions in our `login_page.dart` file using the following import statement:

```dart
import 'package:my_app/utils/helperfunctions.dart';
```

## Running our app

Finally, we are ready to run our app 🤞🏾🤞🏾🤞🏾!

If everything was done correctly, on running the app, we will be presented with a flow similar to our previous tutorial. However, when we are connected with the correct network, we will be presented with an additional `“Sign Message”` button. On clicking on it, we are treated with a Metamask popup that looks something like the following:


![Sign message popup](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zmg8q6atz020juddi1qt.png)

Here you can notice one of the challenges of using the Metamask app, that is the message shown is not human readable. Due to some internal bug in the app, the message section is not able to show the hexadecimal hash properly.

Nevertheless, when we click on the `Sign` button, we are returned to our app screen. Now we can see the truncated version of the signature along with our old `Slide to login` slider.


![Details in App Screen](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uyj4sgfys6mt3ung12l8.png)

You can get the complete signature from the console. In my case the details are as follows:

```
Account: 0x11Ea3D9B50116986C77E17Fa177D56fae0b73631
Signature: 0xd4e8b7997d5e244981965a5f301658f780242ff51226e0fc6af21564867a0d560292b1aa17e41ca836111b7413bbbd3a0a430c0715ae511dd206396c8bed7abe1b
```

### Verifying the signature using a smart contract

Now we get back to our smart contract and try to verify this signature. Depending on the account you used, the message and the signature will be different. In my case:

```
Message: "Hello 0x11ea3d9b50116986c77e17fa177d56fae0b73631, welcome to our app. By signing this message you agree to learn and have fun with blockchain"
```

We call the `verifySignature` function with the above parameters to obtain the signing account.


![Verify](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hh6fpswc7vcud2a3zyce.png)

We can cross-check the returned address with the address used for signing. This proves that by using the signature we can determine the account that provided the signature. ***Mission Accomplished*** 😎🎉!

## Wrapping up

In this tutorial, we learned about signing messages using Ethereum wallets like Metamask. We also learned how we can use Metamask in a Flutter app for generating the signature of a `String` message. Finally, we verified our signature using a simple smart contract.

The code for this project is uploaded to Github [here](https://github.com/BhaskarDutta2209/FlutterAppWithMetamask). The updated code for this tutorial will be present in the `signing-message` branch.

## About me

I am working in the domain of blockchain technology for more than 2years now. I aspire to learn new technologies targeted at solving real-world problems and write tutorials with unique approaches. Kindly follow me to stay updated as I write new tutorials and show you support through reactions and comments.

I am also looking forward to writing sponsored articles and creating YouTube videos based on my articles. If you want to connect with me or provide your feedback, you can find me on [LinkedIn](https://www.linkedin.com/in/itsbhaskardutta/), [Twitter](https://twitter.com/Bhaskar2209), or through my [mail](mailto:bhaskar@bhaskardutta.tech).

We will meet again with another new tutorial or blog, till then stay safe, spend time with your family and ***KEEP BUIDLING!***