Transactions
Farcaster Frames have the ability to instruct an App to invoke and perform Ethereum transactions (see the spec).
Overview
At a glance:
- A Frame has a 
<Button.Transaction>element with a specified target.transactionroute. - When the user presses the button in the App, the App will make a 
POSTrequest to the.transactionroute. - The App uses the response to forward the transaction data to the user's wallet for signing and broadcasting.
 - Once the user has sent the transaction, the App will perform a 
POSTrequest to the frame. 
Walkthrough
Here is a trivial example on how to expose a transaction interface in a frame. We will break it down below.
import { Button, Frog, TextInput, parseEther } from 'frog'
import { abi } from './abi'
 
export const app = new Frog()
 
app.frame('/', (c) => {
  return c.res({
    action: '/finish',
    image: (
      <div style={{ color: 'white', display: 'flex', fontSize: 60 }}>
        Perform a transaction
      </div>
    ),
    intents: [
      <TextInput placeholder="Value (ETH)" />,
      <Button.Transaction target="/send-ether">Send Ether</Button.Transaction>,
      <Button.Transaction target="/mint">Mint</Button.Transaction>,
    ]
  })
})
 
app.frame('/finish', (c) => {
  const { transactionId } = c
  return c.res({
    image: (
      <div style={{ color: 'white', display: 'flex', fontSize: 60 }}>
        Transaction ID: {transactionId}
      </div>
    )
  })
})
 
app.transaction('/send-ether', (c) => {
  const { inputText } = c
  // Send transaction response.
  return c.send({
    chainId: 'eip155:10',
    to: '0xd2135CfB216b74109775236E36d4b433F1DF507B',
    value: parseEther(inputText),
  })
})
 
app.transaction('/mint', (c) => {
  const { inputText } = c
  // Contract transaction response.
  return c.contract({
    abi,
    chainId: 'eip155:10',
    functionName: 'mint',
    args: [69420n],
    to: '0xd2135CfB216b74109775236E36d4b433F1DF507B',
    value: parseEther(inputText)
  })
})1. Render Frame & Intents
In the example above, we are rendering three transaction intents:
- A text input to capture the amount of ether to send with the transaction.
 - A "Send Ether" button that will call the 
/send-etherroute, and expose a "send ethereum to an address" interface to the App. - A "Mint" button that will call the 
/mintroute, and expose a "mint NFT" interface to the App. 
app.frame('/', (c) => {
  return c.res({
    image: (
      <div style={{ color: 'white', display: 'flex', fontSize: 60 }}>
        Perform a transaction
      </div>
    ),
    intents: [
      <TextInput placeholder="Value (ETH)" />,
      <Button.Transaction target="/send-ether">Send Ether</Button.Transaction>,
      <Button.Transaction target="/mint">Mint</Button.Transaction>,
    ]
  })
})
 
// ...2. Handle /send-ether Requests
Without route handlers to handle these requests, these buttons will be meaningless.
Firstly, let's define a /send-ether route to handle the "Send Ether" button:
app.frame('/', (c) => {
  return c.res({
    image: (
      <div style={{ color: 'white', display: 'flex', fontSize: 60 }}>
        Perform a transaction
      </div>
    ),
    intents: [
      <TextInput placeholder="Value (ETH)" />,
      <Button.Transaction target="/send-ether">Send Ether</Button.Transaction>,
      <Button.Transaction target="/mint">Mint</Button.Transaction>,
    ]
  })
})
 
// ...
 
app.transaction('/send-ether', (c) => {
  const { inputText } = c
  // Send transaction response.
  return c.send({
    chainId: 'eip155:10',
    to: '0xd2135CfB216b74109775236E36d4b433F1DF507B',
    value: parseEther(inputText),
  })
})A breakdown of the /send-ether route handler:
- We are responding with a 
c.send("send transaction") response. - We are extracting user input from the previous frame via 
inputText. - Within 
c.send, we can specify a:chainId: CAIP-2 compliant chain ID. We are sending toeip155:10where1is Ethereum mainnet.to: a recipient.value: the amount of wei to send. We are usingparseEtherto convert ether → wei.data: optional calldata to include in the transaction.abi: optional ABI to include in the transaction.
 - The 
c.sendfunction constructs a well-formed JSON response to be consumed by the App. 
3. Handle /mint Requests
Secondly, let's define a /mint route to handle the "Mint" button:
import { abi } from './abi'
 
app.frame('/', (c) => {
  return c.res({
    image: (
      <div style={{ color: 'white', display: 'flex', fontSize: 60 }}>
        Perform a transaction
      </div>
    ),
    intents: [
      <TextInput placeholder="Value (ETH)" />,
      <Button.Transaction target="/send-ether">Send Ether</Button.Transaction>,
      <Button.Transaction target="/mint">Mint</Button.Transaction>,
    ]
  })
})
 
// ...
 
app.transaction('/mint', (c) => {
  const { inputText } = c
  // Contract transaction response.
  return c.contract({
    abi,
    functionName: 'mint',
    args: [69420n],
    chainId: 'eip155:10',
    to: '0xd2135CfB216b74109775236E36d4b433F1DF507B',
    value: parseEther(inputText)
  })
})A breakdown of the /mint route handler:
- We are responding with a 
c.contract("contract transaction") response. - We are extracting user input from the previous frame via 
inputText. - Within 
c.contract, we can specify a:abi: ABI for the contract.functionName: Function to call on the contract.args: Arguments to supply to the function.chainId: CAIP-2 compliant chain ID.to: Contract address.value: Optional amount of wei to send to the payable function.
 - The 
c.contractfunction constructs a well-formed JSON response to be consumed by the App. 
4. Handle Post-Transaction Execution
Once the user has sent the transaction, the App will perform a POST request to the frame.
We can extract the transaction ID from context via c.transactionId.
app.frame('/', (c) => {
  return c.res({
    action: '/finish',
    image: (
      <div style={{ color: 'white', display: 'flex', fontSize: 60 }}>
        Perform a transaction
      </div>
    ),
    intents: [
      <Button.Transaction target="/send-ether">Send Ether</Button.Transaction>,
      <Button.Transaction target="/mint">Mint</Button.Transaction>,
    ]
  })
})
 
app.frame('/finish', (c) => {
  const { transactionId } = c
  return c.res({
    image: (
      <div style={{ color: 'white', display: 'flex', fontSize: 60 }}>
        Transaction ID: {transactionId}
      </div>
    )
  })
})
 
app.transaction('/send-ether', (c) => {
  // Send transaction response.
  return c.send({
    chainId: 'eip155:10',
    to: '0xd2135CfB216b74109775236E36d4b433F1DF507B',
    value: parseEther('1'),
  })
})
 
app.transaction('/mint', (c) => {
  // Contract transaction response.
  return c.contract({
    abi,
    chainId: 'eip155:10',
    functionName: 'mint',
    args: [69420n],
    to: '0xd2135CfB216b74109775236E36d4b433F1DF507B'
  })
})5. Bonus: Learn the API
You can learn more about the transaction APIs here: