diff --git a/.changeset/gentle-dots-talk.md b/.changeset/gentle-dots-talk.md
new file mode 100644
index 00000000..612815cf
--- /dev/null
+++ b/.changeset/gentle-dots-talk.md
@@ -0,0 +1,5 @@
+---
+"frog": patch
+---
+
+Added `address` to `FrameData`. Read more at https://warpcast.com/horsefacts.eth/0xb98e17d8.
diff --git a/protobufs/schemas/message.proto b/protobufs/schemas/message.proto
index 3f5d7f26..2f08489f 100644
--- a/protobufs/schemas/message.proto
+++ b/protobufs/schemas/message.proto
@@ -181,4 +181,5 @@ message FrameActionBody {
bytes input_text = 4; // Text input from the user, if present
bytes state = 5; // Serialized frame state value
bytes transaction_id = 6; // Chain-specific transaction ID for tx actions
+ bytes address = 7; // Chain-specific address for tx actions
}
diff --git a/site/pages/reference/frog-frame-context.mdx b/site/pages/reference/frog-frame-context.mdx
index 9cd37438..cbdef30e 100644
--- a/site/pages/reference/frog-frame-context.mdx
+++ b/site/pages/reference/frog-frame-context.mdx
@@ -352,6 +352,47 @@ app.frame('/', (c) => {
})
```
+## transactionId
+
+- **Type**: `string | undefined`
+
+The chain-specific transaction ID. Defined at the `action` frame of `transaction` route.
+
+```tsx twoslash
+// @noErrors
+/** @jsxImportSource frog/jsx */
+// ---cut---
+import { Button, Frog } from 'frog'
+
+export const app = new Frog()
+
+app.frame('/', (c) => {
+ return c.res({
+ action: '/finish',
+ image: (
+
+ Perform a transaction
+
+ ),
+ intents: [
+ ,
+ Send Ether,
+ ]
+ })
+})
+
+app.frame('/finish', (c) => {
+ const { transactionId } = c
+ return c.res({
+ image: (
+
+ Transaction ID: {transactionId}
+
+ )
+ })
+})
+```
+
## var
- **Type**: `HonoContext['var']`
@@ -415,4 +456,4 @@ app.frame('/', (c) => {
const { url } = c // [!code focus]
return c.res({/* ... */})
})
-```
\ No newline at end of file
+```
diff --git a/site/pages/reference/frog-transaction-context.mdx b/site/pages/reference/frog-transaction-context.mdx
index a3494870..ca9f6e88 100644
--- a/site/pages/reference/frog-transaction-context.mdx
+++ b/site/pages/reference/frog-transaction-context.mdx
@@ -17,6 +17,26 @@ app.transaction('/send-ether', (c) => { // [!code focus]
A transaction handler can also be asynchronous (ie. `async (c) => { ... }{:js}`).
:::
+## address
+
+- **Type**: `string | undefined`
+
+The address of a wallet that sends a transaction.
+
+```tsx twoslash
+// @noErrors
+/** @jsxImportSource frog/jsx */
+// ---cut---
+import { Button, Frog } from 'frog'
+
+export const app = new Frog()
+
+app.transaction('/send-ether', (c) => {
+ const { address } = c // [!code focus]
+ return c.send({/* ... */})
+})
+```
+
## buttonIndex
- **Type**: `number`
@@ -421,4 +441,4 @@ app.transaction('/send-ether', (c) => {
const { url } = c // [!code focus]
return c.send({/* ... */})
})
-```
\ No newline at end of file
+```
diff --git a/src/protobufs/generated/message_pb.ts b/src/protobufs/generated/message_pb.ts
index c97d4320..1d97570a 100644
--- a/src/protobufs/generated/message_pb.ts
+++ b/src/protobufs/generated/message_pb.ts
@@ -1457,6 +1457,13 @@ export class FrameActionBody extends Message$1 {
*/
transactionId = new Uint8Array(0)
+ /**
+ * Chain-specific address for tx actions
+ *
+ * @generated from field: bytes address = 7;
+ */
+ address = new Uint8Array(0)
+
constructor(data?: PartialMessage) {
super()
proto3.util.initPartial(data, this)
@@ -1481,6 +1488,7 @@ export class FrameActionBody extends Message$1 {
kind: 'scalar',
T: 12 /* ScalarType.BYTES */,
},
+ { no: 7, name: 'address', kind: 'scalar', T: 12 /* ScalarType.BYTES */ },
])
static fromBinary(
diff --git a/src/types/context.ts b/src/types/context.ts
index 0e8052b3..7bf2203e 100644
--- a/src/types/context.ts
+++ b/src/types/context.ts
@@ -125,6 +125,16 @@ export type TransactionContext<
//
_state = env['State'],
> = Context & {
+ /**
+ * Address of the account that is executing a transaction (if any). Maps to:
+ * - Ethereum: 20-byte address string.
+ */
+ address: string
+ /**
+ * Data from the frame that was passed via the POST body.
+ * The {@link Context`verified`} flag indicates whether the data is trusted or not.
+ */
+ frameData?: Pretty
/**
* Contract transaction request.
*
diff --git a/src/types/frame.ts b/src/types/frame.ts
index fa9f1ae0..496f1cf5 100644
--- a/src/types/frame.ts
+++ b/src/types/frame.ts
@@ -117,6 +117,7 @@ export type FrameResponseFn = (
) => TypedResponse
export type FrameData = {
+ address?: string | undefined
buttonIndex?: 1 | 2 | 3 | 4 | undefined
castId: { fid: number; hash: string }
fid: number
diff --git a/src/utils/getTransactionContext.ts b/src/utils/getTransactionContext.ts
index de790ac9..c82afd16 100644
--- a/src/utils/getTransactionContext.ts
+++ b/src/utils/getTransactionContext.ts
@@ -59,6 +59,7 @@ export function getTransactionContext<
return {
context: {
+ address: frameData?.address!,
buttonIndex: frameData?.buttonIndex,
buttonValue,
contract(parameters) {
diff --git a/src/utils/verifyFrame.ts b/src/utils/verifyFrame.ts
index 30ce6efd..b7c6f2ee 100644
--- a/src/utils/verifyFrame.ts
+++ b/src/utils/verifyFrame.ts
@@ -49,6 +49,9 @@ export async function verifyFrame({
export function messageToFrameData(message: Message): FrameData {
const frameActionBody = message.data?.body.value as FrameActionBody
const frameData: FrameData = {
+ address: frameActionBody.address
+ ? bytesToHex(frameActionBody.address)
+ : undefined,
castId: {
fid: Number(frameActionBody.castId?.fid),
hash: bytesToHex(frameActionBody.castId?.hash!),