Sign Typed Data

Calling signTypedData

Once you have built an Ethereum typed data object, you can ask the user to sign it by using signTypedData function in MetaKeep SDK. The function expects a non-empty reason which is shown to the user at the time of typed data signing.

The operation result is returned in the callback object passed to the signTypedData function or the Promise object returned by the signTypedData function.

This API is compliant with the latest EIP-712 spec and is a drop-in replacement for Metamask's signTypedData_v4 API.

val typedDataString =
    """
      {
        "types":{
          "EIP712Domain":[
            {
              "name":"name",
              "type":"string"
              },
            {
              "name":"version",
              "type":"string"
              },
            {
              "name":"chainId",
              "type":"uint256"
              },
            {
              "name":"verifyingContract",
              "type":"address"
              }
          ],
          "MetaTransaction":[
            {
              "name":"nonce",
              "type":"uint256"
              },
            {
              "name":"from",
              "type":"address"
              },
            {
              "name":"functionSignature",
              "type":"bytes"
              }
          ]
        },
        "primaryType":"MetaTransaction",
        "domain":{
          "name":"Ether Mail",
          "version":"1",
          "chainId":1,
          "verifyingContract":"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
          },
        "message":{
          "nonce":200,
          "from":"0x97706Df14A769E28EC897dAc5Ba7bCfa5AA9C444",
          "functionSignature":"0xd1a1beb40000"
          }
      }
     """

sdk.signTypedData(
    // Typed data as a JsonObject
    JsonRequest(txnString),
    // signing reason
    "reason",
    // Callback
    Callback(
        onSuccess = { response: JsonResponse ->
            Log.d("onSuccess", response.toString())
        },
        onFailure = { error: JsonResponse ->
            Log.d("onFailure", error.toString())
        },
    ),
)
let typedDataString =
  """
   {
     "types":{
       "EIP712Domain":[
         {
           "name":"name",
           "type":"string"
           },
         {
           "name":"version",
           "type":"string"
           },
         {
           "name":"chainId",
           "type":"uint256"
           },
         {
           "name":"verifyingContract",
           "type":"address"
           }
       ],
       "MetaTransaction":[
         {
           "name":"nonce",
           "type":"uint256"
           },
         {
           "name":"from",
           "type":"address"
           },
         {
           "name":"functionSignature",
           "type":"bytes"
           }
       ]
     },
     "primaryType":"MetaTransaction",
     "domain":{
       "name":"Ether Mail",
       "version":"1",
       "chainId":1,
       "verifyingContract":"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
       },
     "message":{
       "nonce":200,
       "from":"0x97706Df14A769E28EC897dAc5Ba7bCfa5AA9C444",
       "functionSignature":"0xd1a1beb40000"
       }
   }
  """

sdk.signTypedData(
  // Typed data as a JsonObject
  transaction: try JsonRequest(jsonString: txnString),
  // signing reason
  reason: reason,
  // Callback
  callback: Callback(
    onSuccess: { (result: JsonResponse) in
      print("onSuccess")
      print(result.description)
    },
    onFailure: { (error: JsonResponse) in
      print("onFailure")
      print(error.description)
    }
  )
)
await sdk.signTypedData(
    // Typed data
    {
        types: {
            EIP712Domain: [{
                    name: "name",
                    type: "string"
                },
                {
                    name: "version",
                    type: "string"
                },
                {
                    name: "chainId",
                    type: "uint256"
                },
                {
                    name: "verifyingContract",
                    type: "address"
                },
            ],
            MetaTransaction: [{
                    name: "nonce",
                    type: "uint256",
                },
                {
                    name: "from",
                    type: "address",
                },
                {
                    name: "functionSignature",
                    type: "bytes",
                },
            ],
        },
        primaryType: "MetaTransaction",
        domain: {
            name: "Ether Mail",
            version: "1",
            chainId: 1,
            verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
        },
        message: {
            nonce: 200,
            from: "0x97706Df14A769E28EC897dAc5Ba7bCfa5AA9C444",
            functionSignature: "0xd1a1beb40000",
        },
    },
    // signing reason
    "reason",
);
await sdk.signTypedData({
        "types": {
            "EIP712Domain": [{
                    "name": "name",
                    "type": "string"
                },
                {
                    "name": "version",
                    "type": "string"
                },
                {
                    "name": "chainId",
                    "type": "uint256"
                },
                {
                    "name": "verifyingContract",
                    "type": "address"
                }
            ],
            "MetaTransaction": [{
                    "name": "nonce",
                    "type": "uint256"
                },
                {
                    "name": "from",
                    "type": "address"
                },
                {
                    "name": "functionSignature",
                    "type": "bytes"
                }
            ]
        },
        "primaryType": "MetaTransaction",
        "domain": {
            "name": "Ether Mail",
            "version": "1",
            "chainId": 1,
            "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
        },
        "message": {
            "nonce": 200,
            "from": "0x97706Df14A769E28EC897dAc5Ba7bCfa5AA9C444",
            "functionSignature": "0xd1a1beb40000"
        }
    },
    // Signing reason
    "sign sample transaction from Flutter",
)

Response

On success, the callback onSuccess or the Promise resolve function is called with the signed message data as a JsonResponse object. This is what the data looks like:

{
  status: "SUCCESS",
  // Signature of the signed typed data. The signature encodes the r, s and v parameters 
  // from appendix F of the yellow paper in big-endian format. 
  // Bytes 0…32 contain the r parameter, bytes 32…64 the s parameter and the last byte the v parameter.
  signature: "0x1a84678f385553358386051464a252fa25d019335deb20262eb231d4ec1467300e6f1944e872430b7e04af6dd68ece90e2a9995a7abd827b50588602523b525600",
  r: "0x1a84678f385553358386051464a252fa25d019335deb20262eb231d4ec146730",
  s: "0x0e6f1944e872430b7e04af6dd68ece90e2a9995a7abd827b50588602523b5256",
  v: "0x0"
}

Error status

Callback onFailure or the Promise reject function is called when the user cancels the operation or if there's an error. The error object contains a status field which is a string indicating the status of the operation. Here's what the error object looks like:

{
  status: "USER_CONSENT_DENIED"
}

Here's a table of all possible error status returned by the SDK

StatusDescription
USER_REQUEST_DENIEDThe user has denied the sign operation.
In this case, you should ask the user if they want to try again.
INVALID_REQUESTInvalid data passed to sign operation.
Please get in touch with us if you are unable to fix this.
APP_ID_REQUIREDNo app id was provided when initializing the SDK.
You can find the app id in the MetaKeep Developer Console.
APP_NOT_FOUNDThe provided app id is invalid.
You can find the correct app id in the MetaKeep Developer Console.
SOMETHING_WENT_WRONGAn unknown error happened.
Please get in touch with us if you continue seeing this error.

© Copyright 2024, Passbird Research Inc.