Problem

Composer actions and miniapps wish to request transactions from the user’s wallet, but they have no good mechanism to do so. It’s possible to use QR code based wallet linking inside the webview as a workaround, but this a very poor user experience, especially on mobile.

Goals

Proposal

Introduce a new miniapp-client message type, requestTransaction. Embedded apps may send a postMessage to the parent app to request a transaction:

window.parent.postMessage({
  type: "requestTransaction",
  data: {
    requestId: "01ef6570-5a51-48fa-910c-f419400a6d0d",
    tx: {
      method: "eth_sendTransaction",
      chainId: "eip155:10",
      params: {
        abi: [...], // JSON ABI of the function selector and any errors
        to: "0x00000000fcCe7f938e7aE6D3c335bD6a1a7c593D",
        data: "0x783a112b0000000000000000000000000000000000000000000000000000000000000e250000000000000000000000000000000000000000000000000000000000000001",
        value: "984316556204476",
      },
    }
  }
}, "*");

The message’s data.requestId must be a UUIDv4.

The message’s data.tx must follow the same format as frame transaction requests, either a transaction or typed signature:

type TransactionRequestMessage = {
  type: "requestTransaction";
  data: TransactionRequestBody;

type TransactionRequestBody = {
  requestId: string;
  tx: EthSendTransactionAction | EthSignTypedDataV4Action;
}

type EthSendTransactionAction = {
  chainId: string;
  method: 'eth_sendTransaction';
  attribution?: boolean;
  params: {
    abi: Abi | [];
    to: string;
    value?: string;
    data?: string;
  };
};

type EthSignTypedDataV4Action = {
  chainId: string;
  method: 'eth_signTypedData_v4';
  params: {
    domain: {
      name?: string;
      version?: string;
      chainId?: number;
      verifyingContract?: string;
    };
    types: Record<string, unknown>;
    primaryType: string;
    message: Record<string, unknown>;
  };
};

See the Frame Specification for details on transaction and signature request types.

Farcaster clients must listen for requests of this type and present the transaction request to the user. When the user completes the transaction action, clients must send back a message in the following format:

postMessage({
  type: "transactionResponse",
  data: {
    requestId: "01ef6570-5a51-48fa-910c-f419400a6d0d",
    success: true,
    receipt: {
      address: "0x075b108fC0a6426F9dEC9A5c18E87eB577D1346a"
      transactionId: "0x0e2b80fd7ecd4263de49d6979d68cc0d0e487a9b1ea8f95281c2d4e641318cd4"
    },
  }
}, "*");

Where address is the user’s connected wallet and transaction ID is the transaction hash or signature bytes. This is the same format as frame transaction responses.

Unlike frames, miniapps are more complex and may wish to recover from a failed or rejected transaction. In the event of a failed transaction, the client app must send back a failure message:

postMessage({
  type: "transactionResponse",
  data: {
    requestId: "01ef6570-5a51-48fa-910c-f419400a6d0d",
    success: false,
    message: "User rejected the request"
  }
}, "*");

Response types:

type TransactionResponseMessage = {
  type: "transactionResponse";
  data: TransactionSuccessBody | TransactionFailureBody
}

type TransactionSuccessBody = {
  requestId: string;
  success: true;
  receipt: {
    address: string;
    transactionId: string; 
  }
}

type TransactionFailureBody = {
  requestId: string;
  success: false;
  message: string;
}