# Message signatures

Using the Protocol Kit, this guide explains how to generate and sign messages from a Safe account, including plain string messages and EIP-712 JSON messages.

<Callout type='info' emoji='ℹ️'>
  Before starting, check this guide's [setup](../signatures.md).
</Callout>

## Prerequisites

- [Node.js and npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)

## Steps

<Steps>

  ### Install dependencies

  ```bash
  yarn install @safe-global/protocol-kit
  ```

  ### Create a message

  Messages can be plain strings or valid EIP-712 typed data structures.

  ```typescript
  // An example of a string message
  const STRING_MESSAGE = "I'm the owner of this Safe account"
  ```

  ```typescript
  // An example of a typed data message
  const TYPED_MESSAGE = {
    types: {
      EIP712Domain: [
        { name: 'name', type: 'string' },
        { name: 'version', type: 'string' },
        { name: 'chainId', type: 'uint256' },
        { name: 'verifyingContract', type: 'address' }
      ],
      Person: [
        { name: 'name', type: 'string' },
        { name: 'wallets', type: 'address[]' }
      ],
      Mail: [
        { name: 'from', type: 'Person' },
        { name: 'to', type: 'Person[]' },
        { name: 'contents', type: 'string' }
      ]
    },
    domain: {
      name: 'Ether Mail',
      version: '1',
      chainId: Number(chainId),
      verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC'
    },
    primaryType: 'Mail',
    message: {
      from: {
        name: 'Cow',
        wallets: [
          '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826',
          '0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF'
        ]
      },
      to: [
        {
          name: 'Bob',
          wallets: [
            '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB',
            '0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57',
            '0xB0B0b0b0b0b0B000000000000000000000000000'
          ]
        }
      ],
      contents: 'Hello, Bob!'
    }
  }
  ```

  The `createMessage` method in the Protocol Kit allows for creating new messages and returns an instance of the `EthSafeMessage` class. Here, we are passing `TYPED_MESSAGE`, but `STRING_MESSAGE` could also be passed.

  ```typescript
  let safeMessage = protocolKit.createMessage(TYPED_MESSAGE)
  ```

  The returned `safeMessage` object contains the message data (`safeMessage.data`) and a map of owner-signature pairs (`safeMessage.signatures`). The structure is similar to the `EthSafeTransaction` class but applied for messages instead of transactions.

  We use `let` to initialize the `safeMessage` variable because we will add the signatures later.

  ```typescript
  class EthSafeMessage implements SafeMessage {
    data: EIP712TypedData | string
    signatures: Map<string, SafeSignature> = new Map()
    ...
    // Other props and methods
  }
  ```

  ### Sign the message

  Once the `safeMessage` object is created, we need to collect the signatures from the signers who will sign it.

  Following our [setup](../signatures.md), we will sign a message with `SAFE_3_4_ADDRESS`, the main Safe account in this guide. To do that, we first need to sign the same message with its owners: `OWNER_1_ADDRESS`, `OWNER_2_ADDRESS`, `SAFE_1_1_ADDRESS`, and `SAFE_2_3_ADDRESS`.

  #### ECDSA signatures

  This applies to `OWNER_1_ADDRESS` and `OWNER_2_ADDRESS` accounts, as both are EOAs.

  The `signMessage` method takes the `safeMessage` together with a `SigningMethod` and adds the new signature to the `signMessage.signatures` map. Depending on the type of message, the `SigningMethod` can take these values:
  - `SigningMethod.ETH_SIGN`
  - `SigningMethod.ETH_SIGN_TYPED_DATA_V4`

  ```typescript
  // Connect OWNER_1_ADDRESS
  protocolKit = await protocolKit.connect({
    provider: RPC_URL
    signer: OWNER_1_PRIVATE_KEY
  })

  // Sign the safeMessage with OWNER_1_ADDRESS
  // After this, the safeMessage contains the signature from OWNER_1_ADDRESS
  safeMessage = await protocolKit.signMessage(
    safeMessage,
    SigningMethod.ETH_SIGN_TYPED_DATA_V4
  )

  // Connect OWNER_2_ADDRESS
  protocolKit = await protocolKit.connect({
    provider: RPC_URL
    signer: OWNER_2_PRIVATE_KEY
  })

  // Sign the safeMessage with OWNER_2_ADDRESS
  // After this, the safeMessage contains the signature from OWNER_1_ADDRESS and OWNER_2_ADDRESS
  safeMessage = await protocolKit.signMessage(
    safeMessage,
    SigningMethod.ETH_SIGN_TYPED_DATA_V4
  )
  ```

  #### Smart contract signatures

  When signing with a Safe account, the `SigningMethod` will take the value `SigningMethod.SAFE_SIGNATURE`.

  ##### 1/1 Safe account

  This applies to the `SAFE_1_1_ADDRESS` account, another owner of `SAFE_3_4_ADDRESS`.

  We need to connect the Protocol Kit to `SAFE_1_1_ADDRESS` and the `OWNER_3_ADDRESS` account (the only owner of `SAFE_1_1_ADDRESS`) and sign the message.

  ```typescript
  // Create a new message object
  let messageSafe1_1 = await createMessage(TYPED_MESSAGE)

  // Connect OWNER_3_ADDRESS and SAFE_1_1_ADDRESS
  protocolKit = await protocolKit.connect({
    provider: RPC_URL
    signer: OWNER_3_PRIVATE_KEY,
    safeAddress: SAFE_1_1_ADDRESS
  })

  // Sign the messageSafe1_1 with OWNER_3_ADDRESS
  // After this, the messageSafe1_1 contains the signature from OWNER_3_ADDRESS
  messageSafe1_1 = await signMessage(
    messageSafe1_1,
    SigningMethod.SAFE_SIGNATURE,
    SAFE_3_4_ADDRESS // Parent Safe address
  )

  // Build the contract signature of SAFE_1_1_ADDRESS
  const signatureSafe1_1 = await buildContractSignature(
    Array.from(messageSafe1_1.signatures.values()),
    SAFE_1_1_ADDRESS
  )

  // Add the signatureSafe1_1 to safeMessage
  // After this, the safeMessage contains the signature from OWNER_1_ADDRESS, OWNER_2_ADDRESS and SAFE_1_1_ADDRESS
  safeMessage.addSignature(signatureSafe1_1)
  ```

  When signing with a child Safe account, we need to specify the parent Safe address to generate the signature based on the version of the contract.

  ##### 2/3 Safe account

  This applies to the `SAFE_2_3_ADDRESS` account, another owner of `SAFE_3_4_ADDRESS`.

  We need to connect the Protocol Kit to `SAFE_2_3_ADDRESS` and the `OWNER_4_ADDRESS` and `OWNER_5_ADDRESS` accounts (owners of `SAFE_2_3_ADDRESS`) and sign the message.

  ```typescript
  // Create a new message object
  let messageSafe2_3 = await createMessage(TYPED_MESSAGE)

  // Connect OWNER_4_ADDRESS and SAFE_2_3_ADDRESS
  protocolKit = await protocolKit.connect({
    provider: RPC_URL,
    signer: OWNER_4_PRIVATE_KEY,
    safeAddress: SAFE_2_3_ADDRESS
  })

  // Sign the messageSafe2_3 with OWNER_4_ADDRESS
  // After this, the messageSafe2_3 contains the signature from OWNER_4_ADDRESS
  messageSafe2_3 = await protocolKit.signMessage(
    messageSafe2_3,
    SigningMethod.SAFE_SIGNATURE,
    SAFE_3_4_ADDRESS // Parent Safe address
  )

  // Connect OWNER_5_ADDRESS
  protocolKit = await protocolKit.connect({
    provider: RPC_URL,
    signer: OWNER_5_PRIVATE_KEY
  })

  // Sign the messageSafe2_3 with OWNER_5_ADDRESS
  // After this, the messageSafe2_3 contains the signature from OWNER_5_ADDRESS
  messageSafe2_3 = await protocolKit.signMessage(
    messageSafe2_3,
    SigningMethod.SAFE_SIGNATURE,
    SAFE_3_4_ADDRESS // Parent Safe address
  )

  // Build the contract signature of SAFE_2_3_ADDRESS
  const signatureSafe2_3 = await buildContractSignature(
    Array.from(messageSafe2_3.signatures.values()),
    SAFE_2_3_ADDRESS
  )

  // Add the signatureSafe2_3 to safeMessage
  // After this, the safeMessage contains the signature from OWNER_1_ADDRESS, OWNER_2_ADDRESS, SAFE_1_1_ADDRESS and SAFE_2_3_ADDRESS
  safeMessage.addSignature(signatureSafe2_3)
  ```

  After following all the steps above, the `safeMessage` now contains all the signatures from the owners of the Safe.

  ### Publish the signed message

  As messages aren't stored in the blockchain, we must make them public and available to others by storing them elsewhere.

  Safe messages can be stored on-chain and off-chain:

  - **Off-chain**: Messages are stored in the Safe Transaction Service. This is the default option and doesn't require any on-chain interaction.
  - **On-chain**: Messages are [stored](https://github.com/safe-global/safe-smart-account/blob/f03dfae65fd1d085224b00a10755c509a4eaacfe/contracts/Safe.sol#L68-L69) in the Safe contract.

  Safe supports signing [EIP-191](https://eips.ethereum.org/EIPS/eip-191) messages and [EIP-712](https://eips.ethereum.org/EIPS/eip-712) typed data messages all together with off-chain [EIP-1271](https://eips.ethereum.org/EIPS/eip-1271) validation for signatures.

  #### Off-chain messages

  To use off-chain messages, we need to use the functionality from this guide and call the Safe Transaction Service API to store the messages and signatures.

  We mentioned the utility of storing messages in the contract. Off-chain messages have the same purpose, but they're stored in the Safe Transaction Service. It stores the messages and signatures in a database. It's a centralized service, but it's open-source and can be deployed by anyone.

  The Safe Transaction Service is used by [Safe\{Wallet\}](https:/app.safe.global) to store messages and signatures by default.

  ##### Propose the message

  To store a new message, we need to call the `addMessage` from the API Kit, passing the Safe address, an object with the message, and a signature from one owner.

  ```typescript
  // Get the signature from OWNER_1_ADDRESS
  const signatureOwner1 = safeMessage.getSignature(OWNER_1_PRIVATE_KEY) as EthSafeSignature

  // Instantiate the API Kit
  // Use the chainId where you have the Safe account deployed
  // How to get an Api key => http://docs.safe.global/core-api/how-to-use-api-keys
  const apiKit = new SafeApiKit({ 
    chainId, 
    apiKey: 'YOUR_API_KEY' 
  })

  // Propose the message
  apiKit.addMessage(SAFE_3_4_ADDRESS, {
    message: TYPED_MESSAGE, // or STRING_MESSAGE
    signature: buildSignatureBytes([signatureOwner1])
  })
  ```

  The message is now publicly available in the Safe Transaction Service with the signature of the owner who submitted it.

  ##### Confirm the message

  To add the signatures from the remaining owners, we need to call the `addMessageSignature`, passing the `safeMessageHash` and a signature from the owner.

  ```typescript
  // Get the safeMessageHash
  const safeMessageHash = await protocolKit.getSafeMessageHash(
    hashSafeMessage(TYPED_MESSAGE) // or STRING_MESSAGE
  )

  // Get the signature from OWNER_2_ADDRESS
  const signatureOwner2 = safeMessage.getSignature(OWNER_2_ADDRESS) as EthSafeSignature

  // Add signature from OWNER_2_ADDRESS
  await apiKit.addMessageSignature(
    safeMessageHash,
    buildSignatureBytes([signatureOwner2])
  )

  // Add signature from the owner SAFE_1_1_ADDRESS
  await apiKit.addMessageSignature(
    safeMessageHash,
    buildSignatureBytes([signatureSafe1_1])
  )

  // Add signature from the owner SAFE_2_3_ADDRESS
  await apiKit.addMessageSignature(
    safeMessageHash,
    buildSignatureBytes([signatureSafe2_3])
  )
  ```

  At this point, the message stored in the Safe Transaction Service contains all the required signatures from the owners of the Safe.

  The `getMessage` method returns the status of a message.

  ```typescript
  const confirmedMessage = await apiKit.getMessage(safeMessageHash)
  ```

  [Safe\{Wallet\}](https://app.safe.global) exposes to its users the list of off-chain messages signed by a Safe account.

  ```
  https://app.safe.global/transactions/messages?safe=<NETWORK_PREFIX>:<SAFE_ADDRESS>
  ```

  #### On-chain messages

  Storing messages on-chain is less efficient than doing it off-chain because it requires executing a transaction to store the message hash in the contract, resulting in additional gas costs. To do this on-chain, we use the `SignMessageLib` contract.

  ```typescript
  // Get the contract with the correct version
  const signMessageLibContract = await getSignMessageLibContract({
    safeVersion: '1.4.1'
  })
  ```

  We need to calculate the `messageHash`, encode the call to the `signMessage` function in the `SignMessageLib` contract and create the transaction that will store the message hash in that contract.

  ```typescript
  const messageHash = hashSafeMessage(MESSAGE)
  const txData = signMessageLibContract.encode('signMessage', [messageHash])

  const safeTransactionData: SafeTransactionDataPartial = {
    to: signMessageLibContract.address,
    value: '0',
    data: txData,
    operation: OperationType.DelegateCall
  }

  const signMessageTx = await protocolKit.createTransaction({
    transactions: [safeTransactionData]
  })
  ```

  Once the transaction object is instantiated, the owners must sign and execute it.

  ```typescript
  // Collect the signatures using the signTransaction method

  // Execute the transaction to store the messageHash
  await protocolKit.executeTransaction(signMessageTx)
  ```

  Once the transaction is executed, the message hash will be stored in the contract.

  ### Validate the signature

  #### On-chain

  When a message is stored on-chain, the `isValidSignature` method in the Protocol Kit needs to be called with the parameters `messageHash` and `0x`. The method will check the stored hashes in the Safe contract to validate the signature.

  ```typescript
  import { hashSafeMessage } from '@safe-global/protocol-kit'

  const messageHash = hashSafeMessage(MESSAGE)

  const isValid = await protocolKit.isValidSignature(messageHash, '0x')
  ```

  #### Off-chain

  When a message is stored off-chain, the `isValidSignature` method in the Protocol Kit must be called with the `messageHash` and the `encodedSignatures` parameters. The method will check the `isValidSignature` function defined in the `CompatibilityFallbackHandler` [contract](https://github.com/safe-global/safe-smart-account/blob/f03dfae65fd1d085224b00a10755c509a4eaacfe/contracts/handler/CompatibilityFallbackHandler.sol#L51-L68) to validate the signature.

  ```typescript
  const encodedSignatures = safeMessage.encodedSignatures()

  const isValid = await protocolKit.isValidSignature(
    messageHash,
    encodedSignatures
  )
  ```

</Steps>
