Skip to content

Commit

Permalink
client: add frames module
Browse files Browse the repository at this point in the history
  • Loading branch information
krzysu committed Mar 21, 2024
1 parent d094fd4 commit cf9a7ee
Show file tree
Hide file tree
Showing 10 changed files with 961 additions and 1 deletion.
26 changes: 26 additions & 0 deletions examples/node/scripts/frames/createFrameTypedData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { FramesEip721TypedDataSpec, LensClient, development, staging } from '@lens-protocol/client';

Check failure on line 1 in examples/node/scripts/frames/createFrameTypedData.ts

View workflow job for this annotation

GitHub Actions / Lint

'development' is defined but never used. Allowed unused vars must match /^_/u

async function main() {
const client = new LensClient({
environment: staging,
});

const deadline = new Date();
deadline.setMinutes(deadline.getMinutes() + 5);

const result = await client.frames.createFrameTypedData({
actionResponse: '0x0000000000000000000000000000000000000000',
buttonIndex: 2,
deadline: deadline.getTime(),
inputText: 'Hello, World!',
profileId: '0x01',
pubId: '0x01-0x01',
specVersion: FramesEip721TypedDataSpec.OnePointOnePointOne,
state: '{"counter":1,"idempotency_key":"431b8b38-eb4d-455b"}',
url: 'https://mylensframe.xyz',
});

console.log(`Result: `, result);
}

main();
31 changes: 31 additions & 0 deletions examples/node/scripts/frames/signFrameAction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { FramesEip721TypedDataSpec } from '@lens-protocol/client';

import { getAuthenticatedClient } from '../shared/getAuthenticatedClient';
import { setupWallet } from '../shared/setupWallet';

async function main() {
const wallet = setupWallet();
const client = await getAuthenticatedClient(wallet, '0x58');

const result = await client.frames.signFrameAction({
actionResponse: '0x0000000000000000000000000000000000000000',
buttonIndex: 2,
inputText: 'Hello, World!',
profileId: '0x01',
pubId: '0x01-0x01',
specVersion: FramesEip721TypedDataSpec.OnePointOnePointOne,
state: '{"counter":1,"idempotency_key":"431b8b38-eb4d-455b"}',
url: 'https://mylensframe.xyz',
});

if (result.isFailure()) {
console.error(result.error); // CredentialsExpiredError or NotAuthenticatedError
process.exit(1);
}

const data = result.value;

console.log(`Result: `, data);
}

main();
8 changes: 8 additions & 0 deletions packages/client/src/LensClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { QueryParams } from './queryParams';
import {
Explore,
Feed,
Frames,
Handle,
Invites,
Modules,
Expand Down Expand Up @@ -108,6 +109,13 @@ export class LensClient {
return new Feed(this.context, this._authentication);
}

/**
* The Frames module
*/
get frames(): Frames {
return new Frames(this.context, this._authentication);
}

/**
* The Handle module
*/
Expand Down
1 change: 1 addition & 0 deletions packages/client/src/graphql/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ export {
ExplorePublicationType,
FeedEventItemType,
FollowModuleType,
FramesEip721TypedDataSpec,
HiddenCommentsType,
LensProfileManagerRelayErrorReasonType,
LensTransactionFailureType,
Expand Down
100 changes: 99 additions & 1 deletion packages/client/src/graphql/types.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,14 @@ export type ClaimProfileWithHandleRequest = {
id?: InputMaybe<Scalars['String']['input']>;
};

export type ClaimTokensRequest = {
for: ClaimableTokenType;
};

export enum ClaimableTokenType {
Bonsai = 'BONSAI',
}

export type CollectActionModuleInput = {
multirecipientCollectOpenAction?: InputMaybe<MultirecipientFeeCollectModuleInput>;
simpleCollectOpenAction?: InputMaybe<SimpleCollectOpenActionModuleInput>;
Expand Down Expand Up @@ -207,6 +215,31 @@ export enum ComparisonOperatorConditionType {
NotEqual = 'NOT_EQUAL',
}

export type CreateFrameEip712TypedDataInput = {
/** The typed data domain */
domain: Eip712TypedDataDomainInput;
/** The types */
types: CreateFrameEip712TypedDataTypesInput;
/** The values */
value: CreateFrameEip712TypedDataValueInput;
};

export type CreateFrameEip712TypedDataTypesInput = {
FrameData: Array<Eip712TypedDataFieldInput>;
};

export type CreateFrameEip712TypedDataValueInput = {
actionResponse: Scalars['String']['input'];
buttonIndex: Scalars['Int']['input'];
deadline: Scalars['UnixTimestamp']['input'];
inputText: Scalars['String']['input'];
profileId: Scalars['ProfileId']['input'];
pubId: Scalars['PublicationId']['input'];
specVersion: FramesEip721TypedDataSpec;
state: Scalars['String']['input'];
url: Scalars['URI']['input'];
};

export type CreateProfileRequest = {
followModule?: InputMaybe<FollowModuleInput>;
to: Scalars['EvmAddress']['input'];
Expand Down Expand Up @@ -261,6 +294,24 @@ export type DismissRecommendedProfilesRequest = {
dismiss: Array<Scalars['ProfileId']['input']>;
};

export type Eip712TypedDataDomainInput = {
/** The chainId */
chainId: Scalars['ChainId']['input'];
/** The name of the typed data domain */
name: Scalars['String']['input'];
/** The verifying contract */
verifyingContract: Scalars['EvmAddress']['input'];
/** The version */
version: Scalars['String']['input'];
};

export type Eip712TypedDataFieldInput = {
/** The name of the typed data field */
name: Scalars['String']['input'];
/** The type of the typed data field */
type: Scalars['String']['input'];
};

/** Possible sort criteria for exploring profiles */
export enum ExploreProfilesOrderByType {
CreatedOn = 'CREATED_ON',
Expand Down Expand Up @@ -430,6 +481,42 @@ export type FollowingRequest = {
orderBy?: InputMaybe<ProfilesOrderBy>;
};

export type FrameEip712Request = {
actionResponse: Scalars['String']['input'];
buttonIndex: Scalars['Int']['input'];
deadline: Scalars['UnixTimestamp']['input'];
inputText: Scalars['String']['input'];
profileId: Scalars['ProfileId']['input'];
pubId: Scalars['PublicationId']['input'];
specVersion: FramesEip721TypedDataSpec;
state: Scalars['String']['input'];
url: Scalars['URI']['input'];
};

export type FrameLensManagerEip712Request = {
actionResponse: Scalars['String']['input'];
buttonIndex: Scalars['Int']['input'];
inputText: Scalars['String']['input'];
profileId: Scalars['ProfileId']['input'];
pubId: Scalars['PublicationId']['input'];
specVersion: FramesEip721TypedDataSpec;
state: Scalars['String']['input'];
url: Scalars['URI']['input'];
};

export type FrameVerifySignature = {
/** The identity token */
identityToken: Scalars['Jwt']['input'];
/** The signature */
signature: Scalars['Signature']['input'];
/** The typed data signed */
signedTypedData: CreateFrameEip712TypedDataInput;
};

export enum FramesEip721TypedDataSpec {
OnePointOnePointOne = 'OnePointOnePointOne',
}

export type FraudReasonInput = {
reason: PublicationReportingReason;
subreason: PublicationReportingFraudSubreason;
Expand Down Expand Up @@ -581,6 +668,11 @@ export type InternalNftVerifyRequest = {
secret: Scalars['String']['input'];
};

export type InternalPaymentHandleInfoRequest = {
p: Scalars['String']['input'];
secret: Scalars['String']['input'];
};

export type InternalProfileStatusRequest = {
hhh: Scalars['String']['input'];
secret: Scalars['String']['input'];
Expand Down Expand Up @@ -620,6 +712,10 @@ export type LatestPaidActionsFilter = {
openActionPublicationMetadataFilters?: InputMaybe<PublicationMetadataFilters>;
};

export type LatestPaidActionsWhere = {
customFilters?: InputMaybe<Array<CustomFiltersType>>;
};

export type LegacyCollectRequest = {
on: Scalars['PublicationId']['input'];
referrer?: InputMaybe<Scalars['PublicationId']['input']>;
Expand Down Expand Up @@ -1843,7 +1939,9 @@ export type ValidatePublicationMetadataRequest = {

export type VerifyRequest = {
/** The access token to verify */
accessToken: Scalars['Jwt']['input'];
accessToken?: InputMaybe<Scalars['Jwt']['input']>;
/** The identity token to verify */
identityToken?: InputMaybe<Scalars['Jwt']['input']>;
};

export type WalletAuthenticationToProfileAuthenticationRequest = {
Expand Down
112 changes: 112 additions & 0 deletions packages/client/src/submodules/frames/Frames.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { PromiseResult } from '@lens-protocol/shared-kernel';

import type { Authentication } from '../../authentication';
import { LensContext } from '../../context';
import { CredentialsExpiredError, NotAuthenticatedError } from '../../errors';
import { FetchGraphQLClient } from '../../graphql/FetchGraphQLClient';
import {
FrameEip712Request,
FrameLensManagerEip712Request,
FrameVerifySignature,
} from '../../graphql/types.generated';
import { requireAuthHeaders, sdkAuthHeaderWrapper } from '../../helpers';
import {
CreateFrameEip712TypedDataFragment,
FrameLensManagerSignatureResultFragment,
Sdk,
getSdk,
} from './graphql/frames.generated';

/**
* Lens Frames
*
* @group LensClient Modules
*/
export class Frames {
private readonly sdk: Sdk;

/**
* @internal
*/
constructor(
context: LensContext,
private readonly authentication: Authentication,
) {
const client = new FetchGraphQLClient(context);

this.sdk = getSdk(client, sdkAuthHeaderWrapper(authentication));
}

/**
* Create a Lens Frame Typed Data to sign by the user wallet
*
* @param request - The request object
* @returns Typed data for Lens Frame request
*
* @example
* ```ts
* const result = await client.frames.createFrameTypedData({
* actionResponse: '0x0000000000000000000000000000000000000000',
* buttonIndex: 2,
* deadline: 1711038973,
* inputText: 'Hello, World!',
* profileId: '0x01',
* pubId: '0x01-0x01',
* specVersion: FramesEip721TypedDataSpec.OnePointOnePointOne,
* state: '{"counter":1,"idempotency_key":"431b8b38-eb4d-455b"}',
* url: 'https://mylensframe.xyz',
* });
* ```
*/
async createFrameTypedData(
request: FrameEip712Request,
): Promise<CreateFrameEip712TypedDataFragment> {
const response = await this.sdk.CreateFrameTypedData({ request });
return response.data.result;
}

async verifyFrameSignature(request: FrameVerifySignature): Promise<boolean> {
const response = await this.sdk.VerifyFrameSignature({ request });
return response.data.result;
}

/**
* Sign a Lens Frame Action Data
*
* ⚠️ Requires authenticated LensClient.
*
* @param request - The request object
* @returns Signature result
*
* @example
* ```ts
* const result = await client.frames.signFrameAction({
* actionResponse: '0x0000000000000000000000000000000000000000',
* buttonIndex: 2,
* inputText: 'Hello, World!',
* profileId: '0x01',
* pubId: '0x01-0x01',
* specVersion: FramesEip721TypedDataSpec.OnePointOnePointOne,
* state: '{"counter":1,"idempotency_key":"431b8b38-eb4d-455b"}',
* url: 'https://mylensframe.xyz',
* });
* ```
*/
async signFrameAction(
request: FrameLensManagerEip712Request,
): PromiseResult<
FrameLensManagerSignatureResultFragment,
CredentialsExpiredError | NotAuthenticatedError
> {
return requireAuthHeaders(this.authentication, async (headers) => {
const result = await this.sdk.SignFrameAction(
{
request,
},
headers,
);

return result.data.result;
});
}
}
Loading

0 comments on commit cf9a7ee

Please sign in to comment.