Sign Transaction

Sign EVM Transaction

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

This function is compliant with the official Ethereum sign transaction API https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_signtransaction

await sdk.signTransaction(
    // Eth transaction object
    {
        type: 2,
        from: userEthAddress,
        to: "0x97706Df14A769E28EC897dAc5Ba7bCfa5AA9C444",
        value: "0x2710",
        nonce: "0x1",
        data: "0x0123456789",
        gas: 23,
        maxFeePerGas: 1000,
        maxPriorityFeePerGas: 999,
        chainId: 137,
    },
    // signing reason
    "reason",
);

Here's a reference implementation of this code: https://jsfiddle.net/passbird/d1s30g8b/

Sign Solana Transaction

Once you have built a Solana transaction object using @solana/web3.js, you can ask the user to sign it by using thesignTransaction function in the MetaKeep SDK. The function expects a non-empty reason which is shown to the user at the time of transaction signing.

If you are not using @solana/web3.js, you need to serialize the transaction message that needs to be signed before sending it to the MetaKeep SDK.

👍

Solana versioned transactions are supported

// Build the solana transfer transaction
const tx = new Transaction().add(
  SystemProgram.transfer({
    fromPubkey: fromPublicKey,
    toPubkey: toPublicKey,
    lamports: 1 * LAMPORTS_PER_SOL
  })
);

await sdk.signTransaction(
  // Transaction object
  tx,
  // Signing reason
  "transfer 1 SOL"
);
// Build the solana transfer versioned message
const messageV0WithLookupTable = new TransactionMessage({
  payerKey: payerPublicKey,
  recentBlockhash: (await connection.getRecentBlockhash()).blockhash,
  instructions: [
    SystemProgram.transfer({
      fromPubkey: fromPublicKey,
      toPubkey: toPublicKey,
      lamports: amountToTransfer * LAMPORTS_PER_SOL
    })
  ]
}).compileToV0Message([lookupTableAccount]);

// Build the versioned transaction
const tx = new VersionedTransaction(messageV0WithLookupTable);

await sdk.signTransaction(
  // Transaction object
  tx,
  // Signing reason
  "transfer 1 SOL"
);
// Serialized transaction message in hex format.
// This is the data that needs to be signed
const serializedTransactionMessage = "0xabcd.........."

await sdk.signTransaction(
  // Serialized Solana transaction message
  {
    serializedTransactionMessage: serializedTransactionMessage
  },
  // signing reason
  "transfer 1 SOL"
);

Here's a reference implementation of this code:

  1. Legacy transaction: https://codesandbox.io/s/metakeep-sign-solana-transaction-qu5rqu?file=/src/App.js
  2. Versioned transaction: https://codesandbox.io/s/metakeep-sign-solana-versioned-transaction-hpclk4?file=/src/App.js

📘

The latest @solana/web3.js supported version is 1.75.0

Let us know if you would like us to support a more recent version :)

Sign EOS Transaction

Once you have built a raw EOS transaction, you can ask the user to sign it by using thesignTransaction function in the MetaKeep SDK. If the signature generation also requires a chainId, you can send it in the extraSigningData field. Note that the action data needs to be sent as a serialized hex string.

The function also expects a non-empty reason which is shown to the user at the time of transaction signing.

await sdk.signTransaction(
    // Transaction object
    {
        // Raw EOS transaction
        "rawTransaction": {
            "expiration": "2023-05-06T01:23:45",
            "ref_block_num": 12345,
            "ref_block_prefix": 67890,
            "max_net_usage_words": 0,
            "max_cpu_usage_ms": 0,
            "delay_sec": 0,
            "context_free_actions": [],
            "actions": [{
                "account": "eosio.token",
                "name": "transfer",
                "authorization": [{
                    "actor": "myaccount",
                    "permission": "active"
                }],
                // Send action data as a serialized hex string.
                "data": "0000000000ea305500000000487a2b000000000000000004454f530000000000"
            }],
            "transaction_extensions": []
        },
        "extraSigningData": {
            // If chainId is part of the signature generation,
            // send it inside extraSigningData field.
            "chainId": "b20901380af44ef59c5918439a1f9a41d83669020319a80574b804a5f95cbd7e"
        }
    },
    // signing reason
    "reason",
);

Here's a reference implementation of this code: https://jsfiddle.net/passbird/xpLwjn53/

Response

signTransaction returns a promise which succeeds when the sign operation is successful. The response looks like this-

{
  status: "SUCCESS",
  //The RLP encoded transaction, ready to be sent using web3.eth.sendSignedTransaction.
  signedRawTransaction: "0x02f86c8189018203e78203e8179497706df14a769e28ec897dac5ba7bcfa5aa9c444822710850123456789c080a01a84678f385553358386051464a252fa25d019335deb20262eb231d4ec146730a00e6f1944e872430b7e04af6dd68ece90e2a9995a7abd827b50588602523b5256",
  // The transaction hash for the RLP encoded transaction.
  transactionHash: "0xe02497f6d7d4b52197a35db6115633b34802f90eaa98e894791999ca0d6b11f8",
  // Signature of the signed transaction. 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"
}
{
  "status": "SUCCESS",
  // Signature in hex format
  "signature": "0x023d9a5700b77971b8de539936143e0d82808809b856cc9ab9bcc41a02a3794ec516a44db6ed666f3e4f7549ae91939062ce076a9ef419febc21c41eebb98f0a"
}
{
  "status": "SUCCESS",
  "signature": "SIG_K1_K1YY3maW1vHjQmmtciiQkkzPaUcpPdCQ631oMqayz4Z3TG9xhjNQwzqL3VCQPV1ZfYTQ14hkoRBkFaUwwrAngLedAxhog6"
}

Error status

signTransaction returns a promise which throws an error when the sign operation is unsuccessful. The error object looks like so-

{
  status: "USER_CONSENT_DENIED"
}

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

StatusDescription
USER_REQUEST_DENIEDUser 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.
MISSING_NONCEThe nonce is missing. Please specify nonce for the transaction.
MISSING_GASGas is missing. Please specify gas for the transaction.
INVALID_GAS_FEE_PARAMSThe transaction contains gasPrice maxFeePerGas and maxPriorityFeePerGas. Please specify either gasPrice or maxFeePerGas and maxPriorityFeePerGas
MISSING_GAS_PRICEGas price is missing for type 1 transactions. Please specify gasPrice.
MISSING_MAX_FEE_PER_GASThe max fee per gas is missing for type 2 transactions. Please specify both maxFeePerGas and maxPriorityFeePerGas
MISSING_MAX_PRIORITY_FEE_PER_GASThe max priority fee per gas is missing for type 2 transactions. Please specify both maxFeePerGas and maxPriorityFeePerGas
APP_ID_REQUIREDNo app id provided when initializing the SDK.
You can find the app id in MetaKeep Developer Console.
APP_NOT_FOUNDProvided app id is invalid.
You can find the correct app id in MetaKeep Developer Console.
SOMETHING_WENT_WRONGUnknown error happened.
Please get in touch with us if you continue seeing this error.

© Copyright 2024, Passbird Research Inc.