@farcaster/frame-core 0.0.34 → 0.0.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/esm/types.d.ts CHANGED
@@ -1,6 +1,7 @@
1
- import type { AddFrame, ComposeCast, Ready, SendToken, SignIn, SwapToken, ViewProfile, ViewToken } from './actions';
1
+ import type { AddMiniApp, ComposeCast, Ready, SendToken, SignIn, SwapToken, ViewProfile, ViewToken } from './actions';
2
2
  import type { FrameContext } from './context';
3
3
  import type { EventFrameAdded, EventFrameRemoved, EventNotificationsDisabled, EventNotificationsEnabled } from './schemas';
4
+ import type { SolanaRequestFn, SolanaWireRequestFn } from './solana';
4
5
  import type { Ethereum } from './wallet';
5
6
  export type SetPrimaryButtonOptions = {
6
7
  text: string;
@@ -12,6 +13,8 @@ export * from './wallet/ethereum';
12
13
  export { DEFAULT_READY_OPTIONS, ReadyOptions } from './actions/Ready';
13
14
  export type SignInOptions = SignIn.SignInOptions;
14
15
  export type SetPrimaryButton = (options: SetPrimaryButtonOptions) => void;
16
+ export type MiniAppHostCapability = 'wallet.getEvmProvider' | 'wallet.getSolanaProvider' | 'actions.ready' | 'actions.openUrl' | 'actions.close' | 'actions.setPrimaryButton' | 'actions.addMiniApp' | 'actions.signIn' | 'actions.viewProfile' | 'actions.composeCast' | 'actions.viewToken' | 'actions.sendToken' | 'actions.swapToken';
17
+ export type GetCapabilities = () => Promise<MiniAppHostCapability[]>;
15
18
  export type WireFrameHost = {
16
19
  context: FrameContext;
17
20
  close: () => void;
@@ -22,12 +25,14 @@ export type WireFrameHost = {
22
25
  ethProviderRequest: Ethereum.EthProvideRequest;
23
26
  ethProviderRequestV2: Ethereum.RpcTransport;
24
27
  eip6963RequestProvider: () => void;
25
- addFrame: AddFrame.WireAddFrame;
28
+ solanaProviderRequest?: SolanaWireRequestFn;
29
+ addFrame: AddMiniApp.WireAddMiniApp;
26
30
  viewProfile: ViewProfile.ViewProfile;
27
31
  viewToken: ViewToken.ViewToken;
28
32
  sendToken: SendToken.SendToken;
29
33
  swapToken: SwapToken.SwapToken;
30
34
  composeCast: <close extends boolean | undefined = undefined>(options: ComposeCast.Options<close>) => Promise<ComposeCast.Result<close>>;
35
+ getCapabilities: GetCapabilities;
31
36
  };
32
37
  export type FrameHost = {
33
38
  context: FrameContext;
@@ -43,16 +48,18 @@ export type FrameHost = {
43
48
  * Hosts must emit an EventEip6963AnnounceProvider in response.
44
49
  */
45
50
  eip6963RequestProvider: () => void;
46
- addFrame: AddFrame.AddFrame;
51
+ solanaProviderRequest?: SolanaRequestFn;
52
+ addFrame: AddMiniApp.AddMiniApp;
47
53
  viewProfile: ViewProfile.ViewProfile;
48
54
  viewToken: ViewToken.ViewToken;
49
55
  sendToken: SendToken.SendToken;
50
56
  swapToken: SwapToken.SwapToken;
51
57
  composeCast: <close extends boolean | undefined = undefined>(options: ComposeCast.Options<close>) => Promise<ComposeCast.Result<close>>;
58
+ getCapabilities: GetCapabilities;
52
59
  };
53
60
  export type EventFrameAddRejected = {
54
61
  event: 'frame_add_rejected';
55
- reason: AddFrame.AddFrameRejectedReason;
62
+ reason: AddMiniApp.AddMiniAppRejectedReason;
56
63
  };
57
64
  export type EventPrimaryButtonClicked = {
58
65
  event: 'primary_button_clicked';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farcaster/frame-core",
3
- "version": "0.0.34",
3
+ "version": "0.0.36",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -21,6 +21,7 @@
21
21
  "@farcaster/tsconfig": "0.0.2"
22
22
  },
23
23
  "dependencies": {
24
+ "@solana/web3.js": "^1.98.2",
24
25
  "ox": "^0.4.4",
25
26
  "zod": "^3.24.1"
26
27
  },
@@ -2,11 +2,11 @@ import * as Errors from '../errors'
2
2
  import type { OneOf } from '../internal/types'
3
3
  import type { FrameNotificationDetails } from '../schemas'
4
4
 
5
- export type AddFrameResult = {
5
+ export type AddMiniAppResult = {
6
6
  notificationDetails?: FrameNotificationDetails
7
7
  }
8
8
 
9
- export type AddFrame = () => Promise<AddFrameResult>
9
+ export type AddMiniApp = () => Promise<AddMiniAppResult>
10
10
 
11
11
  type InvalidDomainManifestJsonError = {
12
12
  type: 'invalid_domain_manifest'
@@ -16,23 +16,23 @@ type RejectedByUserJsonError = {
16
16
  type: 'rejected_by_user'
17
17
  }
18
18
 
19
- export type AddFrameJsonError =
19
+ export type AddMiniAppJsonError =
20
20
  | InvalidDomainManifestJsonError
21
21
  | RejectedByUserJsonError
22
22
 
23
- export type AddFrameRejectedReason = AddFrameJsonError['type']
23
+ export type AddMiniAppRejectedReason = AddMiniAppJsonError['type']
24
24
 
25
- export type AddFrameJsonResult = OneOf<
26
- { result: AddFrameResult } | { error: AddFrameJsonError }
25
+ export type AddMiniAppJsonResult = OneOf<
26
+ { result: AddMiniAppResult } | { error: AddMiniAppJsonError }
27
27
  >
28
28
 
29
- export type WireAddFrame = () => Promise<AddFrameJsonResult>
29
+ export type WireAddMiniApp = () => Promise<AddMiniAppJsonResult>
30
30
 
31
31
  /**
32
32
  * Thrown when the frame does not have a valid domain manifest.
33
33
  */
34
34
  export class InvalidDomainManifest extends Errors.BaseError {
35
- override readonly name = 'AddFrame.InvalidDomainManifest'
35
+ override readonly name = 'AddMiniApp.InvalidDomainManifest'
36
36
 
37
37
  constructor() {
38
38
  super('Invalid domain manifest')
@@ -43,7 +43,7 @@ export class InvalidDomainManifest extends Errors.BaseError {
43
43
  * Thrown when add frame action was rejected by the user.
44
44
  */
45
45
  export class RejectedByUser extends Errors.BaseError {
46
- override readonly name = 'AddFrame.RejectedByUser'
46
+ override readonly name = 'AddMiniApp.RejectedByUser'
47
47
 
48
48
  constructor() {
49
49
  super('Add frame rejected by user')
@@ -2,7 +2,7 @@ export type Options<close extends boolean | undefined = undefined> = {
2
2
  /**
3
3
  * Suggested text for the body of the cast.
4
4
  *
5
- * Mentions can be included using the human-writeable form (e.g. @farcaster).
5
+ * Mentions can be included using the human-writable form (e.g. @farcaster).
6
6
  **/
7
7
  text?: string
8
8
 
@@ -18,11 +18,27 @@ export type SignInOptions = {
18
18
  * ISO 8601 datetime.
19
19
  */
20
20
  expirationTime?: string
21
+
22
+ /**
23
+ * Whether an [Auth
24
+ * Address](https://github.com/farcasterxyz/protocol/discussions/225) signed
25
+ * message is acceptable. Defaults to `false` to maintain backwards
26
+ * compatibility, though applications should set this to `true` for the best
27
+ * user experience assuming their verification method supports it.
28
+ *
29
+ * @default false
30
+ */
31
+ acceptAuthAddress?: boolean
21
32
  }
22
33
 
23
34
  export type SignInResult = {
24
35
  signature: string
25
36
  message: string
37
+
38
+ /**
39
+ * Indicates if the signature was produced by a custody or auth address.
40
+ */
41
+ authMethod: 'custody' | 'authAddress'
26
42
  }
27
43
 
28
44
  export type SignIn = (options: SignInOptions) => Promise<SignInResult>
@@ -14,7 +14,7 @@ export type SwapTokenOptions = {
14
14
 
15
15
  /**
16
16
  * Sell token amount, as numeric string.
17
- * For example, 10 USDC: 1000000
17
+ * For example, 1 USDC: 1000000
18
18
  */
19
19
  sellAmount?: string
20
20
  }
@@ -1,4 +1,4 @@
1
- export * as AddFrame from './AddFrame'
1
+ export * as AddMiniApp from './AddMiniApp'
2
2
  export * as ComposeCast from './ComposeCast'
3
3
  export * as Ready from './Ready'
4
4
  export * as SignIn from './SignIn'
package/src/index.ts CHANGED
@@ -5,3 +5,5 @@ export * as Context from './context'
5
5
  export * as Manifest from './manifest'
6
6
  export * from './types'
7
7
  export * from './schemas'
8
+ export * from './solana'
9
+ export * from './solanaWire'
package/src/solana.ts ADDED
@@ -0,0 +1,108 @@
1
+ import {
2
+ Connection as SolanaConnection,
3
+ type SendOptions as SolanaSendOptions,
4
+ type Transaction as SolanaTransaction,
5
+ type VersionedTransaction as SolanaVersionedTransaction,
6
+ } from '@solana/web3.js'
7
+
8
+ export { SolanaConnection }
9
+ export type { SolanaSendOptions }
10
+
11
+ export type SolanaCombinedTransaction =
12
+ | SolanaTransaction
13
+ | SolanaVersionedTransaction
14
+
15
+ export type SolanaConnectRequestArguments = {
16
+ method: 'connect'
17
+ }
18
+ export type SolanaSignMessageRequestArguments = {
19
+ method: 'signMessage'
20
+ params: {
21
+ message: string
22
+ }
23
+ }
24
+ export type SolanaSignAndSendTransactionRequestArguments = {
25
+ method: 'signAndSendTransaction'
26
+ params: {
27
+ transaction: SolanaCombinedTransaction
28
+ options?: SolanaSendOptions
29
+ }
30
+ }
31
+ export type SolanaSignTransactionRequestArguments<
32
+ T extends SolanaCombinedTransaction = SolanaTransaction,
33
+ > = {
34
+ method: 'signTransaction'
35
+ params: {
36
+ transaction: T
37
+ }
38
+ }
39
+
40
+ export type SolanaRequestFn = ((
41
+ request: SolanaConnectRequestArguments,
42
+ ) => Promise<{ publicKey: string }>) &
43
+ ((request: SolanaSignMessageRequestArguments) => Promise<{
44
+ signature: string
45
+ }>) &
46
+ ((request: SolanaSignAndSendTransactionRequestArguments) => Promise<{
47
+ signature: string
48
+ }>) &
49
+ (<T extends SolanaCombinedTransaction>(
50
+ request: SolanaSignTransactionRequestArguments<T>,
51
+ ) => Promise<{ signedTransaction: T }>)
52
+
53
+ export interface SolanaWalletProvider {
54
+ request: SolanaRequestFn
55
+
56
+ signMessage(message: string): Promise<{ signature: string }>
57
+ signTransaction<T extends SolanaCombinedTransaction>(
58
+ transaction: T,
59
+ ): Promise<{ signedTransaction: T }>
60
+ signAndSendTransaction(input: {
61
+ transaction: SolanaCombinedTransaction
62
+ }): Promise<{ signature: string }>
63
+ }
64
+
65
+ export const createSolanaWalletProvider = (
66
+ request: SolanaRequestFn,
67
+ ): SolanaWalletProvider => ({
68
+ request,
69
+ signMessage: (msg: string) =>
70
+ request({ method: 'signMessage', params: { message: msg } }),
71
+ signTransaction: <T extends SolanaCombinedTransaction>(transaction: T) =>
72
+ request({ method: 'signTransaction', params: { transaction } }),
73
+ signAndSendTransaction: (input: {
74
+ transaction: SolanaCombinedTransaction
75
+ }) =>
76
+ request({
77
+ method: 'signAndSendTransaction',
78
+ params: input,
79
+ }),
80
+ })
81
+
82
+ export type SolanaWireSignAndSendTransactionRequestArguments = {
83
+ method: 'signAndSendTransaction'
84
+ params: {
85
+ transaction: string
86
+ options?: SolanaSendOptions
87
+ }
88
+ }
89
+
90
+ export type SolanaWireSignTransactionRequestArguments = {
91
+ method: 'signTransaction'
92
+ params: {
93
+ transaction: string
94
+ }
95
+ }
96
+
97
+ export type SolanaWireRequestFn = ((
98
+ request: SolanaConnectRequestArguments,
99
+ ) => Promise<{ publicKey: string }>) &
100
+ ((request: SolanaSignMessageRequestArguments) => Promise<{
101
+ signature: string
102
+ }>) &
103
+ ((request: SolanaWireSignAndSendTransactionRequestArguments) => Promise<{
104
+ signature: string
105
+ }>) &
106
+ ((
107
+ request: SolanaWireSignTransactionRequestArguments,
108
+ ) => Promise<{ signedTransaction: string }>)
@@ -0,0 +1,118 @@
1
+ import {
2
+ Transaction as SolanaTransaction,
3
+ VersionedTransaction as SolanaVersionedTransaction,
4
+ } from '@solana/web3.js'
5
+
6
+ import type {
7
+ SolanaCombinedTransaction,
8
+ SolanaConnectRequestArguments,
9
+ SolanaRequestFn,
10
+ SolanaSignAndSendTransactionRequestArguments,
11
+ SolanaSignMessageRequestArguments,
12
+ SolanaSignTransactionRequestArguments,
13
+ SolanaWireRequestFn,
14
+ SolanaWireSignAndSendTransactionRequestArguments,
15
+ SolanaWireSignTransactionRequestArguments,
16
+ } from './solana'
17
+
18
+ function serializeTransaction(transaction: SolanaCombinedTransaction): string {
19
+ return Buffer.from(
20
+ transaction.serialize({
21
+ verifySignatures: false,
22
+ }),
23
+ ).toString('base64')
24
+ }
25
+
26
+ function unserializeTransaction(
27
+ transaction: string,
28
+ ): SolanaCombinedTransaction {
29
+ const bytes = Uint8Array.from(Buffer.from(transaction, 'base64'))
30
+ return (bytes[0] & 0x80) !== 0
31
+ ? SolanaVersionedTransaction.deserialize(bytes)
32
+ : SolanaTransaction.from(bytes)
33
+ }
34
+
35
+ export function wrapSolanaProviderRequest(
36
+ requestFn: SolanaRequestFn,
37
+ ): SolanaWireRequestFn {
38
+ const wrappedFn = async (
39
+ request:
40
+ | SolanaConnectRequestArguments
41
+ | SolanaSignMessageRequestArguments
42
+ | SolanaWireSignAndSendTransactionRequestArguments
43
+ | SolanaWireSignTransactionRequestArguments,
44
+ ) => {
45
+ if (request.method === 'connect') {
46
+ return await requestFn(request)
47
+ }
48
+ if (request.method === 'signMessage') {
49
+ return await requestFn(request)
50
+ }
51
+ if (request.method === 'signAndSendTransaction') {
52
+ const { transaction, options } = request.params
53
+ const params = {
54
+ transaction: unserializeTransaction(transaction),
55
+ options,
56
+ }
57
+ return await requestFn({
58
+ method: 'signAndSendTransaction',
59
+ params,
60
+ })
61
+ }
62
+ if (request.method === 'signTransaction') {
63
+ const { transaction } = request.params
64
+ const params = {
65
+ transaction: unserializeTransaction(transaction),
66
+ }
67
+ const { signedTransaction } = await requestFn({
68
+ method: 'signTransaction',
69
+ params,
70
+ })
71
+ return {
72
+ signedTransaction: serializeTransaction(signedTransaction),
73
+ }
74
+ }
75
+ }
76
+ return wrappedFn as SolanaWireRequestFn
77
+ }
78
+
79
+ export function unwrapSolanaProviderRequest(
80
+ wrappedRequestFn: SolanaWireRequestFn,
81
+ ): SolanaRequestFn {
82
+ const unwrappedFn = async <T extends SolanaCombinedTransaction>(
83
+ request:
84
+ | SolanaConnectRequestArguments
85
+ | SolanaSignMessageRequestArguments
86
+ | SolanaSignAndSendTransactionRequestArguments
87
+ | SolanaSignTransactionRequestArguments<T>,
88
+ ) => {
89
+ if (request.method === 'connect') {
90
+ return await wrappedRequestFn(request)
91
+ }
92
+ if (request.method === 'signMessage') {
93
+ return await wrappedRequestFn(request)
94
+ }
95
+ if (request.method === 'signAndSendTransaction') {
96
+ const { transaction, options } = request.params
97
+ const params = {
98
+ transaction: serializeTransaction(transaction),
99
+ }
100
+ return await wrappedRequestFn({
101
+ method: 'signAndSendTransaction',
102
+ params,
103
+ })
104
+ }
105
+ if (request.method === 'signTransaction') {
106
+ const { transaction } = request.params
107
+ const params = {
108
+ transaction: serializeTransaction(transaction),
109
+ }
110
+ const { signedTransaction } = await wrappedRequestFn({
111
+ method: 'signTransaction',
112
+ params,
113
+ })
114
+ return { signedTransaction: unserializeTransaction(signedTransaction) }
115
+ }
116
+ }
117
+ return unwrappedFn as SolanaRequestFn
118
+ }
package/src/types.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type {
2
- AddFrame,
2
+ AddMiniApp,
3
3
  ComposeCast,
4
4
  Ready,
5
5
  SendToken,
@@ -15,6 +15,7 @@ import type {
15
15
  EventNotificationsDisabled,
16
16
  EventNotificationsEnabled,
17
17
  } from './schemas'
18
+ import type { SolanaRequestFn, SolanaWireRequestFn } from './solana'
18
19
  import type { Ethereum } from './wallet'
19
20
 
20
21
  export type SetPrimaryButtonOptions = {
@@ -32,6 +33,23 @@ export type SignInOptions = SignIn.SignInOptions
32
33
 
33
34
  export type SetPrimaryButton = (options: SetPrimaryButtonOptions) => void
34
35
 
36
+ export type MiniAppHostCapability =
37
+ | 'wallet.getEvmProvider'
38
+ | 'wallet.getSolanaProvider'
39
+ | 'actions.ready'
40
+ | 'actions.openUrl'
41
+ | 'actions.close'
42
+ | 'actions.setPrimaryButton'
43
+ | 'actions.addMiniApp'
44
+ | 'actions.signIn'
45
+ | 'actions.viewProfile'
46
+ | 'actions.composeCast'
47
+ | 'actions.viewToken'
48
+ | 'actions.sendToken'
49
+ | 'actions.swapToken'
50
+
51
+ export type GetCapabilities = () => Promise<MiniAppHostCapability[]>
52
+
35
53
  export type WireFrameHost = {
36
54
  context: FrameContext
37
55
  close: () => void
@@ -42,7 +60,8 @@ export type WireFrameHost = {
42
60
  ethProviderRequest: Ethereum.EthProvideRequest
43
61
  ethProviderRequestV2: Ethereum.RpcTransport
44
62
  eip6963RequestProvider: () => void
45
- addFrame: AddFrame.WireAddFrame
63
+ solanaProviderRequest?: SolanaWireRequestFn
64
+ addFrame: AddMiniApp.WireAddMiniApp
46
65
  viewProfile: ViewProfile.ViewProfile
47
66
  viewToken: ViewToken.ViewToken
48
67
  sendToken: SendToken.SendToken
@@ -50,6 +69,7 @@ export type WireFrameHost = {
50
69
  composeCast: <close extends boolean | undefined = undefined>(
51
70
  options: ComposeCast.Options<close>,
52
71
  ) => Promise<ComposeCast.Result<close>>
72
+ getCapabilities: GetCapabilities
53
73
  }
54
74
 
55
75
  export type FrameHost = {
@@ -66,7 +86,8 @@ export type FrameHost = {
66
86
  * Hosts must emit an EventEip6963AnnounceProvider in response.
67
87
  */
68
88
  eip6963RequestProvider: () => void
69
- addFrame: AddFrame.AddFrame
89
+ solanaProviderRequest?: SolanaRequestFn
90
+ addFrame: AddMiniApp.AddMiniApp
70
91
  viewProfile: ViewProfile.ViewProfile
71
92
  viewToken: ViewToken.ViewToken
72
93
  sendToken: SendToken.SendToken
@@ -74,11 +95,12 @@ export type FrameHost = {
74
95
  composeCast: <close extends boolean | undefined = undefined>(
75
96
  options: ComposeCast.Options<close>,
76
97
  ) => Promise<ComposeCast.Result<close>>
98
+ getCapabilities: GetCapabilities
77
99
  }
78
100
 
79
101
  export type EventFrameAddRejected = {
80
102
  event: 'frame_add_rejected'
81
- reason: AddFrame.AddFrameRejectedReason
103
+ reason: AddMiniApp.AddMiniAppRejectedReason
82
104
  }
83
105
 
84
106
  export type EventPrimaryButtonClicked = {