From d555b1c05962ff1549f8ffa8b99a6ecc9404de24 Mon Sep 17 00:00:00 2001 From: Vladyslav Dalechyn Date: Wed, 13 Mar 2024 04:27:49 +0200 Subject: [PATCH] feat: add address to the `messageToFrameData` (#138) * feat: add address to the `messageVerifyBody` * chore: regenerate protobufs * fix: type * chore: changesets * fix: have `address` to be defined only in `TransactionContext` * nit: add docs * nit: add the missing `transactionId` docs * fix: hoist `address` from `frameData` to `c` * tweaks --------- Co-authored-by: moxey.eth --- .changeset/gentle-dots-talk.md | 5 +++ protobufs/schemas/message.proto | 1 + site/pages/reference/frog-frame-context.mdx | 43 ++++++++++++++++++- .../reference/frog-transaction-context.mdx | 22 +++++++++- src/protobufs/generated/message_pb.ts | 8 ++++ src/types/context.ts | 10 +++++ src/types/frame.ts | 1 + src/utils/getTransactionContext.ts | 1 + src/utils/verifyFrame.ts | 3 ++ 9 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 .changeset/gentle-dots-talk.md 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!),