From c82dbf7ad76791e81525740dfe8820bd234c2863 Mon Sep 17 00:00:00 2001 From: mmjee Date: Wed, 20 Dec 2023 18:14:55 +0000 Subject: [PATCH] feat(aa-signers): implemented support for Arcana Auth in the AA signers package (#319) --- packages/signers/package.json | 9 +- .../src/arcana-auth/__tests__/signer.test.ts | 120 ++++++++++++++++++ packages/signers/src/arcana-auth/index.ts | 2 + packages/signers/src/arcana-auth/signer.ts | 97 ++++++++++++++ packages/signers/src/arcana-auth/types.ts | 4 + packages/signers/src/index.ts | 4 + site/.vitepress/config.ts | 14 ++ .../aa-signers/arcana-auth/authenticate.md | 65 ++++++++++ .../aa-signers/arcana-auth/constructor.md | 66 ++++++++++ .../aa-signers/arcana-auth/getAddress.md | 40 ++++++ .../aa-signers/arcana-auth/getAuthDetails.md | 51 ++++++++ .../aa-signers/arcana-auth/introduction.md | 92 ++++++++++++++ .../aa-signers/arcana-auth/signMessage.md | 46 +++++++ .../aa-signers/arcana-auth/signTypedData.md | 79 ++++++++++++ site/packages/aa-signers/contributing.md | 2 +- site/packages/aa-signers/index.md | 2 +- .../signers/guides/arcana-auth.md | 29 +++-- site/snippets/arcana-auth.ts | 29 ++--- yarn.lock | 32 ++++- 19 files changed, 750 insertions(+), 33 deletions(-) create mode 100644 packages/signers/src/arcana-auth/__tests__/signer.test.ts create mode 100644 packages/signers/src/arcana-auth/index.ts create mode 100644 packages/signers/src/arcana-auth/signer.ts create mode 100644 packages/signers/src/arcana-auth/types.ts create mode 100644 site/packages/aa-signers/arcana-auth/authenticate.md create mode 100644 site/packages/aa-signers/arcana-auth/constructor.md create mode 100644 site/packages/aa-signers/arcana-auth/getAddress.md create mode 100644 site/packages/aa-signers/arcana-auth/getAuthDetails.md create mode 100644 site/packages/aa-signers/arcana-auth/introduction.md create mode 100644 site/packages/aa-signers/arcana-auth/signMessage.md create mode 100644 site/packages/aa-signers/arcana-auth/signTypedData.md diff --git a/packages/signers/package.json b/packages/signers/package.json index d4a26b19fa..aa337ab3a6 100644 --- a/packages/signers/package.json +++ b/packages/signers/package.json @@ -61,6 +61,11 @@ "import": "./dist/esm/lit-protocol/index.js", "default": "./dist/cjs/lit-protocol/index.js" }, + "./arcana-auth": { + "types": "./dist/types/arcana-auth/index.d.ts", + "import": "./dist/esm/arcana-auth/index.js", + "default": "./dist/cjs/arcana-auth/index.js" + }, "./package.json": "./package.json" }, "scripts": { @@ -88,6 +93,7 @@ "@lit-protocol/pkp-ethers": "3.0.24", "@lit-protocol/types": "3.0.24", "@lit-protocol/crypto": "3.0.24", + "@arcana/auth": "^1.0.8", "jsdom": "^22.1.0", "magic-sdk": "^21.3.0", "typescript": "^5.0.4", @@ -125,6 +131,7 @@ "@lit-protocol/lit-node-client": "3.0.24", "@lit-protocol/pkp-ethers": "3.0.24", "@lit-protocol/types": "3.0.24", - "@lit-protocol/crypto": "3.0.24" + "@lit-protocol/crypto": "3.0.24", + "@arcana/auth": "^1.0.8" } } diff --git a/packages/signers/src/arcana-auth/__tests__/signer.test.ts b/packages/signers/src/arcana-auth/__tests__/signer.test.ts new file mode 100644 index 0000000000..dc3143155f --- /dev/null +++ b/packages/signers/src/arcana-auth/__tests__/signer.test.ts @@ -0,0 +1,120 @@ +import { AuthProvider } from "@arcana/auth"; +import { ArcanaAuthSigner } from "../signer.js"; + +describe("ArcanaAuth Signer Tests", () => { + it("should correctly get address if authenticated", async () => { + const signer = await givenSigner(); + + const address = await signer.getAddress(); + expect(address).toMatchInlineSnapshot( + '"0x1234567890123456789012345678901234567890"' + ); + }); + + it("should correctly fail to get address if unauthenticated", async () => { + const signer = await givenSigner(false); + + const address = signer.getAddress(); + await expect(address).rejects.toThrowErrorMatchingInlineSnapshot( + '"Not authenticated"' + ); + }); + + it("should correctly get auth details if authenticated", async () => { + const signer = await givenSigner(); + + const details = await signer.getAuthDetails(); + expect(details).toMatchInlineSnapshot(` + { + "email": "test", + "isMfaEnabled": false, + "issuer": null, + "phoneNumber": "1234567890", + "publicAddress": "0x1234567890123456789012345678901234567890", + "recoveryFactors": [], + } + `); + }); + + it("should correctly fail to get auth details if unauthenticated", async () => { + const signer = await givenSigner(false); + + const details = signer.getAuthDetails(); + await expect(details).rejects.toThrowErrorMatchingInlineSnapshot( + '"Not authenticated"' + ); + }); + + it("should correctly sign message if authenticated", async () => { + const signer = await givenSigner(); + + const signMessage = await signer.signMessage("test"); + expect(signMessage).toMatchInlineSnapshot('"0xtest"'); + }); + + it("should correctly fail to sign message if unauthenticated", async () => { + const signer = await givenSigner(false); + + const signMessage = signer.signMessage("test"); + await expect(signMessage).rejects.toThrowErrorMatchingInlineSnapshot( + '"Not authenticated"' + ); + }); + + it("should correctly sign typed data if authenticated", async () => { + const signer = await givenSigner(); + + const typedData = { + types: { + Request: [{ name: "hello", type: "string" }], + }, + primaryType: "Request", + message: { + hello: "world", + }, + }; + const signTypedData = await signer.signTypedData(typedData); + expect(signTypedData).toMatchInlineSnapshot('"0xtest"'); + }); +}); + +const givenSigner = async (auth = true) => { + const inner = new AuthProvider("EeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"); + + inner.getUser = vi.fn().mockResolvedValue({ + publicAddress: "0x1234567890123456789012345678901234567890", + issuer: null, + email: "test", + phoneNumber: "1234567890", + isMfaEnabled: false, + recoveryFactors: [], + }); + + vi.spyOn(inner, "provider", "get").mockReturnValue({ + request: ({ method }: { method: string; params: any[] }) => { + switch (method) { + case "eth_accounts": + return Promise.resolve([ + "0x1234567890123456789012345678901234567890", + ]); + case "personal_sign": + return Promise.resolve("0xtest"); + case "eth_signTypedData_v4": + return Promise.resolve("0xtest"); + default: + return Promise.reject(new Error("Method not found")); + } + }, + }); + + const signer = new ArcanaAuthSigner({ inner }); + + if (auth) { + await signer.authenticate({ + init: () => Promise.resolve(), + connect: () => Promise.resolve(), + }); + } + + return signer; +}; diff --git a/packages/signers/src/arcana-auth/index.ts b/packages/signers/src/arcana-auth/index.ts new file mode 100644 index 0000000000..953a520590 --- /dev/null +++ b/packages/signers/src/arcana-auth/index.ts @@ -0,0 +1,2 @@ +export { ArcanaAuthSigner } from "./signer.js"; +export type * from "./types"; diff --git a/packages/signers/src/arcana-auth/signer.ts b/packages/signers/src/arcana-auth/signer.ts new file mode 100644 index 0000000000..ed7d687d5f --- /dev/null +++ b/packages/signers/src/arcana-auth/signer.ts @@ -0,0 +1,97 @@ +import { + WalletClientSigner, + type SignTypedDataParams, + type SmartAccountAuthenticator, +} from "@alchemy/aa-core"; +import { + AuthProvider, + type ConstructorParams, + type UserInfo, +} from "@arcana/auth"; +import { createWalletClient, custom, type Hash } from "viem"; +import type { ArcanaAuthAuthenticationParams } from "./types"; + +/** + * This class requires the `@arcana/auth` package as a dependency. + * `@alchemy/aa-signers` lists it as optional dependencies. + * + * @see: https://github.com/arcana-network/auth + */ +export class ArcanaAuthSigner + implements + SmartAccountAuthenticator< + ArcanaAuthAuthenticationParams, + UserInfo, + AuthProvider + > +{ + inner: AuthProvider; + private signer: WalletClientSigner | undefined; + + constructor( + params: + | { clientId: string; params: Partial } + | { inner: AuthProvider } + ) { + if ("inner" in params) { + this.inner = params.inner; + return; + } + + this.inner = new AuthProvider(params.clientId, params.params); + } + + readonly signerType = "arcana-auth"; + + getAddress = async () => { + if (!this.signer) throw new Error("Not authenticated"); + + const address = await this.signer.getAddress(); + if (address == null) throw new Error("No address found"); + + return address as Hash; + }; + + signMessage = async (msg: Uint8Array | string) => { + if (!this.signer) throw new Error("Not authenticated"); + + return this.signer.signMessage(msg); + }; + + signTypedData = (params: SignTypedDataParams) => { + if (!this.signer) throw new Error("Not authenticated"); + + return this.signer.signTypedData(params); + }; + + authenticate = async ( + params: ArcanaAuthAuthenticationParams = { + init: async () => { + await this.inner.init(); + }, + connect: async () => { + await this.inner.connect(); + }, + } + ) => { + await params.init(); + await params.connect(); + + if (this.inner.provider == null) throw new Error("No provider found"); + + this.signer = new WalletClientSigner( + createWalletClient({ + transport: custom(this.inner.provider), + }), + this.signerType + ); + + return this.inner.getUser(); + }; + + getAuthDetails = async () => { + if (!this.signer) throw new Error("Not authenticated"); + + return this.inner.getUser(); + }; +} diff --git a/packages/signers/src/arcana-auth/types.ts b/packages/signers/src/arcana-auth/types.ts new file mode 100644 index 0000000000..133daec586 --- /dev/null +++ b/packages/signers/src/arcana-auth/types.ts @@ -0,0 +1,4 @@ +export interface ArcanaAuthAuthenticationParams { + init: () => Promise; + connect: () => Promise; +} diff --git a/packages/signers/src/index.ts b/packages/signers/src/index.ts index af237a6954..e8e6d222c0 100644 --- a/packages/signers/src/index.ts +++ b/packages/signers/src/index.ts @@ -29,3 +29,7 @@ export { type LitConfig, type LitAuthenticateProps, } from "./lit-protocol/index.js"; +export { + ArcanaAuthSigner, + type ArcanaAuthAuthenticationParams, +} from "./arcana-auth/index.js"; diff --git a/site/.vitepress/config.ts b/site/.vitepress/config.ts index 98d7e14026..6eacbbe3be 100644 --- a/site/.vitepress/config.ts +++ b/site/.vitepress/config.ts @@ -679,6 +679,20 @@ export default defineConfig({ text: "Getting Started", link: "/", }, + { + text: "Arcana Auth Signer", + collapsed: true, + base: "/packages/aa-signers/arcana-auth", + items: [ + { text: "Introduction", link: "/introduction" }, + { text: "constructor", link: "/constructor" }, + { text: "authenticate", link: "/authenticate" }, + { text: "getAddress", link: "/getAddress" }, + { text: "signMessage", link: "/signMessage" }, + { text: "signTypedData", link: "/signTypedData" }, + { text: "getAuthDetails", link: "/getAuthDetails" }, + ], + }, { text: "Magic Signer", collapsed: true, diff --git a/site/packages/aa-signers/arcana-auth/authenticate.md b/site/packages/aa-signers/arcana-auth/authenticate.md new file mode 100644 index 0000000000..89419def55 --- /dev/null +++ b/site/packages/aa-signers/arcana-auth/authenticate.md @@ -0,0 +1,65 @@ +--- +outline: deep +head: + - - meta + - property: og:title + content: ArcanaAuthSigner • authenticate + - - meta + - name: description + content: Overview of the authenticate method on ArcanaAuthSigner + - - meta + - property: og:description + content: Overview of the authenticate method on ArcanaAuthSigner +--- + +# authenticate + +`authenticate` is a method on the `ArcanaAuthSigner` that leverages the `Arcana` Auth Web SDK to authenticate a user. + +This method must be called before accessing the other methods available on the `ArcanaAuthSigner`, such as signing messages or typed data or accessing user details. + +## Usage + +::: code-group + +```ts [example.ts] +// [!code focus:99] +import { ArcanaAuthSigner } from "@alchemy/aa-signers"; +// Register app through Arcana Developer Dashboard to get clientId +// ARCANA_AUTH_CLIENTID = "xar_live_nnnnnnnnnn" +const newArcanaAuthSigner = new ArcanaAuthSigner({ + clientId: ARCANA_AUTH_CLIENTID, +}); +// or +// import { AuthProvider } from "@arcana/auth"; +// const inner = new AuthProvider ("xar_live_nnnn"); +// const newArcanaAuthSigner = new ArcanaAuthSigner({inner}); +const getUserInfo = await newArcanaAuthSigner.authenticate(); +``` + +::: + +## Returns + +### `Promise` + +A Promise containing the `UserInfo`, an object with the following fields: + +- `address: string | null` -- the EoA account address associated with the authenticated user's wallet. +- `email: string | null` -- email address of the authenticated user. +- `id: string | null` -- the decentralized user identifier. +- `loginToken: string` -- JWT token returned after the user authenticates. +- `loginType: Logins | passwordless` -- login provider type or passwordless used for authentication (ex. `[{ Logins: "google" | "github" | "discord" | "twitch" | "twitter" | "aws" | "firebase" | "steam" }]`). +- `name: string` -- user name associated with the email id +- `picture: string` -- url pointing to the user profile image +- `publicKey: string` -- public key associated with the user account + +See [Arcana Auth SDK Reference Guide](https://authsdk-ref-guide.netlify.app/interfaces/userinfo) for details. + +## Parameters + +### `clientId`: Unique app identifier assigned after app registration via the Arcana Developer Dashboard + +or + +### `inner`: An AuthProvifer object. For field details, see [AuthProvider constructor](https://authsdk-ref-guide.netlify.app/classes/authprovider#constructor). diff --git a/site/packages/aa-signers/arcana-auth/constructor.md b/site/packages/aa-signers/arcana-auth/constructor.md new file mode 100644 index 0000000000..9d9e657613 --- /dev/null +++ b/site/packages/aa-signers/arcana-auth/constructor.md @@ -0,0 +1,66 @@ +--- +outline: deep +head: + - - meta + - property: og:title + content: ArcanaAuthSigner • constructor + - - meta + - name: description + content: Overview of the constructor method on ArcanaAuthSigner in aa-signers + - - meta + - property: og:description + content: Overview of the constructor method on ArcanaAuthSigner in aa-signers +--- + +# constructor + +To initialize `ArcanaAuthSigner`, you must provide a set of parameters detailed below. + +## Usage + +::: code-group + +```ts [example.ts] +import { ArcanaAuthSigner } from "@alchemy/aa-signers"; + +// instantiates using every possible parameter, as a reference +const newArcanaAuthSigner = new ArcanaAuthSigner({ + clientId: "ARCANA_AUTH_CLIENT_ID", //Register app through dashboard and get clientId + params: { //See `AuthProvider` constructor params + network: 'testnet' | 'mainnet' | `NetworkConfig` + alwaysVisible: boolean // Wallet displayed always in the app or only when a transaction needs review + chainConfig: `ChainConfigInput` + redirectUrl: string // After authentication, window where the control returns + theme: 'light' | 'dark' + position: 'left' | 'right' // Wallet displayed on LHS or RHS in the app context + setWindowProvider: `boolean` // default is false + connectOptions: ConnectOptions + }, +}); +``` + +For details, see [`AuthProvider ConstructorParams`](https://authsdk-ref-guide.netlify.app/interfaces/constructorparams). + +::: + +## Returns + +### `ArcanaAuthSigner` + +A new instance of an `ArcanaAuthSigner`. + +## Parameters + +### `params: { clientId: string, params: ConstructorParams } | { inner: AuthProvider }` + +You can either pass in a constructed `AuthProvider` object, or directly pass into the `ArcanaAuthSigner` the `clientId` and the `AuthProvider` [`ConstructorParams`](https://authsdk-ref-guide.netlify.app/interfaces/constructorparams) used to construct an `AuthProvider` object. These parameters are listed on the [Arcana Auth SDK Reference Guide](https://authsdk-ref-guide.netlify.app/interfaces/constructorparams). You can refer to tutorials and the sample code for usage details in the [Arcana Docs](https://docs.arcana.network/tutorials/code-samples/web/) as well. + +`ArcanaAuthSigner` takes in the following parameters: + +- `inner`: an `AuthProvider` object + or +- `clientId: string` -- a unique app id assigned after app registration via the [Arcana Developer Dashboard](https://dashboard.arcana.network/). + +- `params: ConstructorParams` -- [optional] these are used to customize the Arcana Auth SDK usage. + +See [`ConstructorParams`](https://authsdk-ref-guide.netlify.app/interfaces/constructorparams) for details. diff --git a/site/packages/aa-signers/arcana-auth/getAddress.md b/site/packages/aa-signers/arcana-auth/getAddress.md new file mode 100644 index 0000000000..842a933aec --- /dev/null +++ b/site/packages/aa-signers/arcana-auth/getAddress.md @@ -0,0 +1,40 @@ +--- +outline: deep +head: + - - meta + - property: og:title + content: ArcanaAuthSigner • getAddress + - - meta + - name: description + content: Overview of the getAddress method on ArcanaAuthigner + - - meta + - property: og:description + content: Overview of the getAddress method on ArcanaAuthSigner +--- + +# getAddress + +`getAddress` returns the EOA address of the `ArcanaAuthSigner`. + +This method must be called after [`authenticate`](/packages/aa-signers/arcana-auth/authenticate). Otherwise, this method will throw an error with the message `Not Authenticated`. + +## Usage + +::: code-group + +```ts [example.ts] +import { createArcanaAuthSigner } from "./arcana-auth"; +// [!code focus:99] +const newArcanaAuthSigner = await createArcanaAuthSigner(); + +const address = await newArcanaAuthSigner.getAddress(); +``` + +<<< @/snippets/arcana-auth.ts +::: + +## Returns + +### `Promise
` + +A Promise containing the address of the `ArcanaAuthSigner`. diff --git a/site/packages/aa-signers/arcana-auth/getAuthDetails.md b/site/packages/aa-signers/arcana-auth/getAuthDetails.md new file mode 100644 index 0000000000..29bbc19e07 --- /dev/null +++ b/site/packages/aa-signers/arcana-auth/getAuthDetails.md @@ -0,0 +1,51 @@ +--- +outline: deep +head: + - - meta + - property: og:title + content: ArcanaAuthSigner • getAuthDetails + - - meta + - name: description + content: Overview of the getAuthDetails method on ArcanaAuthSigner + - - meta + - property: og:description + content: Overview of the getAuthDetails method on ArcanaAuthSigner +--- + +# getAuthDetails + +`getAuthDetails` returns the details about the authenticated user, per the `Arcana Auth` SDK's `getUser` [UserInfo specifications](https://authsdk-ref-guide.netlify.app/interfaces/userinfo. + +This method must be called after [`authenticate`](/packages/aa-signers/arcana-auth/authenticate). Otherwise, this method will throw an error with the message `Not Authenticated`. + +## Usage + +::: code-group + +```ts [example.ts] +import { createArcanaAuthSigner } from "./arcana-auth"; +// [!code focus:99] +const newArcanaAuthSigner = await createArcanaAuthSigner(); + +const details = await newArcanaAuthSigner.getAuthDetails(); +``` + +<<< @/snippets/arcana-auth.ts +::: + +## Returns + +### `Promise` + +A Promise containing the `UserInfo`, an object with the following fields: + +- `address: string | null` -- the EOA account address associated with the authenticated user's wallet. +- `email: string | null` -- email address of the authenticated user. +- `id: string | null` -- the decentralized user identifier. +- `loginToken: string` -- JWT token returned after the user authenticates. +- `loginType: Logins | passwordless` -- login provider type or passwordless used for authentication (ex. `[{ Logins: "google" | "github" | "discord" | "twitch" | "twitter" | "aws" | "firebase" | "steam" }]`). +- `name: string` -- user name associated with the email id +- `picture: string` -- url pointing to the user profile image +- `publicKey: string` -- public key associated with the user account + +See [Arcana Auth SDK Reference Guide](https://authsdk-ref-guide.netlify.app/interfaces/userinfo) for details. diff --git a/site/packages/aa-signers/arcana-auth/introduction.md b/site/packages/aa-signers/arcana-auth/introduction.md new file mode 100644 index 0000000000..cb600f914f --- /dev/null +++ b/site/packages/aa-signers/arcana-auth/introduction.md @@ -0,0 +1,92 @@ +--- +outline: deep +head: + - - meta + - property: og:title + content: ArcanaAuthSigner + - - meta + - name: description + content: Overview of the ArcanaAuthSigner class in aa-signers + - - meta + - property: og:description + content: Overview of the ArcanaAuthSigner class in aa-signers +--- + +# Arcana Auth Signer + +`ArcanaAuthSigner` is a signer implementation that extends `SmartAccountAuthenticator` to leverage the [Arcana Auth SDK](https://docs.arcana.network). It supports features such as authentication, message and typed data signing, and authentication details retrieval. + +`ArcanaAuthSigner` provides implementations for all methods on `SmartAccountAuthenticator`: + +1. [`authenticate`](/packages/aa-signers/arcana-auth/authenticate) -- supports user authentication. +2. [`getAddress`](/packages/aa-signers/arcana-auth/getAddress) -- supports typed data signatures from the smart contract account's owner address. +3. [`signMessage`](/packages/aa-signers/arcana-auth/signMessage) -- supports message signatures. +4. [`signTypedData`](/packages/aa-signers/arcana-auth/signTypedData) -- supports typed data signatures. +5. [`getAuthDetails`](/packages/aa-signers/arcana-auth/getAuthDetails) -- supports authentication details retrieval. + +## Install Dependencies + +`ArcanaAuthSigner` requires the installation of the [`Arcana Auth`](https://www.npmjs.com/package/@arcana/auth) SDK. `aa-signers` lists it as an optional dependency. + +::: code-group + +```bash [npm] +npm i -s @arcana/auth +``` + +```bash [yarn] +yarn add @arcana/auth +``` + +## Usage + +::: code-group + +```ts [example.ts] +import { ArcanaAuthSigner } from "@alchemy/aa-signers/arcana-auth"; + +const newArcanaAuthSigner = new ArcanaAuthSigner({ + clientId: "ARCANA_AUTH_CLIENT_ID", +}); +// or by using inner + +import { AuthProvider } from "@arcana/auth"; + +const authParams = { + theme: "light", + network: "testnet", + position: "left", +}; + +const inner = new AuthProvider("ARCANA_AUTH_CLIENT_ID", authParams); +const newArcanaAuthSigner2 = new ArcanaAuthSigner({ inner }); + +const getUserInfo = await newArcanaAuthSigner.authenticate(); + +const address = await newArcanaAuthSigner.getAddress(); + +const details = await newArcanaAuthSigner.getAuthDetails(); + +const signedMessage = await newArcanaAuthSigner.signMessage("test"); + +const typedData = { + types: { + Request: [{ name: "hello", type: "string" }], + }, + primaryType: "Request", + message: { + hello: "world", + }, +}; +const signTypedData = await newArcanaAuthSigner.signTypedData(typedData); +``` + +<<< @/snippets/arcana-auth.ts +::: + +## Developer Links + +- [authParams](https://authsdk-ref-guide.netlify.app/interfaces/constructorparams) +- [Arcana Auth SDK Reference Guide](https://authsdk-ref-guide.netlify.app/) +- [Arcana Auth Documentation](https://docs.arcana.network) +- [GitHub: Arcana Auth](https://github.com/arcana-network/auth) diff --git a/site/packages/aa-signers/arcana-auth/signMessage.md b/site/packages/aa-signers/arcana-auth/signMessage.md new file mode 100644 index 0000000000..932f2e638d --- /dev/null +++ b/site/packages/aa-signers/arcana-auth/signMessage.md @@ -0,0 +1,46 @@ +--- +outline: deep +head: + - - meta + - property: og:title + content: ArcanaAuthSigner • signMessage + - - meta + - name: description + content: Overview of the signMessage method on ArcanaAuthSigner + - - meta + - property: og:description + content: Overview of the signMessage method on ArcanaAuthSigner +--- + +# signMessage + +`signMessage` supports signing messages from the `ArcanaAuthSigner`. + +This method must be called after [`authenticate`](/packages/aa-signers/arcana-auth/authenticate). Otherwise, this method will throw an error with the message `Not Authenticated`. + +## Usage + +::: code-group + +```ts [example.ts] +import { createArcanaAuthSigner } from "./arcana-auth"; +// [!code focus:99] +const newArcanaAuthSigner = await createArcanaAuthSigner(); + +const signedMessage = await newArcanaAuthSigner.signMessage( + "testMessage signing with ArcanaAuthSigner" +); +``` + +<<< @/snippets/arcana-auth.ts +::: + +## Returns + +### `Promise` + +A Promise containing the signature of the message. + +## Parameters + +### `msg: string | Uint8Array)` -- the message to sign diff --git a/site/packages/aa-signers/arcana-auth/signTypedData.md b/site/packages/aa-signers/arcana-auth/signTypedData.md new file mode 100644 index 0000000000..104790b96c --- /dev/null +++ b/site/packages/aa-signers/arcana-auth/signTypedData.md @@ -0,0 +1,79 @@ +--- +outline: deep +head: + - - meta + - property: og:title + content: ArcanaAuthSigner • signTypedData + - - meta + - name: description + content: Overview of the signTypedData method on ArcanaAuthSigner + - - meta + - property: og:description + content: Overview of the signTypedData method on ArcanaAuthSigner +--- + +# signTypedData + +`signTypedData` supports signing typed data from the `ArcanaAuthSigner`. + +This method must be called after [`authenticate`](/packages/aa-signers/arcana-auth/authenticate). Otherwise, this method will throw an error with the message `Not Authenticated`. + +## Usage + +::: code-group + +```ts [example.ts] +import { createArcanaAuthSigner } from "./arcana-auth"; +// [!code focus:99] +const newArcanaAuthSigner = await createArcanaAuthSigner(); + +const signedTypedData = await newArcanaAuthSigner.signTypedData({ + domain: { + name: "Ether Mail", + version: "1", + chainId: 1, + verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", + }, + types: { + Person: [ + { name: "name", type: "string" }, + { name: "wallet", type: "address" }, + ], + Mail: [ + { name: "from", type: "Person" }, + { name: "to", type: "Person" }, + { name: "contents", type: "string" }, + ], + }, + primaryType: "Mail", + message: { + from: { + name: "Cow", + wallet: "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + }, + to: { + name: "Bob", + wallet: "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", + }, + contents: "Hello, Bob!", + }, +}); +``` + +<<< @/snippets/arcana-auth.ts +::: + +## Returns + +### `Promise` + +A Promise containing the signature of the typed data. + +## Parameters + +### `params: SignTypedDataParams` -- the typed data to sign + +- `domain: TypedDataDomain` -- The typed data domain +- `types: Object` -- the type definitions for the typed data +- `primaryType: inferred String` -- the primary type to extract from types and use in value +- `message: inferred from types & primaryType` -- the message, inferred from diff --git a/site/packages/aa-signers/contributing.md b/site/packages/aa-signers/contributing.md index 79a54a156a..03da1e134c 100644 --- a/site/packages/aa-signers/contributing.md +++ b/site/packages/aa-signers/contributing.md @@ -21,7 +21,7 @@ If you are looking to add a new Signer type, please follow the following structu 1. Create a new folder in `src` with the name of your Signer type in `kebab-case` (we're following kebab casing for files throughout the project). 2. If you require importing an external library for your implementation, add it as an `optionalDependency` 3. Create a new file in the folder you just created called `signer.ts` and add your implementation for `SmartAccountSigner` or `SmartAccountAuthenticator`. -4. If needed, create a file in your folder called `types.ts` and add any necesssary types required for authentication, details, or the inner SDK. +4. If needed, create a file in your folder called `types.ts` and add any necessary types required for authentication, details, or the inner SDK. 5. Add some tests for your Signer by creating a subfolder in your `signer/my-signer` called `__tests__` and make sure your files end with the `.test.ts` suffix. 6. Export the classes and types you've defined in `src/index.ts`. 7. Open a PR and we'll review it as soon as possible! diff --git a/site/packages/aa-signers/index.md b/site/packages/aa-signers/index.md index 9ebb156d33..98f69f6950 100644 --- a/site/packages/aa-signers/index.md +++ b/site/packages/aa-signers/index.md @@ -19,7 +19,7 @@ next: # `@alchemy/aa-signers` -This package contains various implementations of the `SmartAccountSigner` and `SmartAccountAuthenticator` classes defined in `aa-signer`. This repo is community maintained and we welcome contributions! +This package contains various implementations of the `SmartAccountSigner` and `SmartAccountAuthenticator` classes defined in `aa-signer`. This repo is community-maintained and we welcome contributions! ## Getting started diff --git a/site/smart-accounts/signers/guides/arcana-auth.md b/site/smart-accounts/signers/guides/arcana-auth.md index 6bf9f5cd42..ab9033c40a 100644 --- a/site/smart-accounts/signers/guides/arcana-auth.md +++ b/site/smart-accounts/signers/guides/arcana-auth.md @@ -63,22 +63,25 @@ import { getDefaultLightAccountFactoryAddress, } from "@alchemy/aa-accounts"; import { sepolia } from "viem/chains"; -import { arcanaAuthSigner } from "./arcana-auth"; +import { createArcanaAuthSigner } from "./arcana-auth"; const chain = sepolia; -const provider = new AlchemyProvider({ - apiKey: "ALCHEMY_API_KEY", - chain, -}).connect( - (rpcClient) => - new LightSmartContractAccount({ - chain, - owner: arcanaAuthSigner, - factoryAddress: getDefaultLightAccountFactoryAddress(chain), - rpcClient, - }) -); +export async function getProvider() { + const signer = await createArcanaAuthSigner(); + return new AlchemyProvider({ + apiKey: "ALCHEMY_API_KEY", + chain, + }).connect( + (rpcClient) => + new LightSmartContractAccount({ + chain, + owner: signer, + factoryAddress: getDefaultLightAccountFactoryAddress(chain), + rpcClient, + }) + ); +} ``` <<< @/snippets/arcana-auth.ts diff --git a/site/snippets/arcana-auth.ts b/site/snippets/arcana-auth.ts index 6ce2ce1533..58dd5dc893 100644 --- a/site/snippets/arcana-auth.ts +++ b/site/snippets/arcana-auth.ts @@ -1,20 +1,19 @@ -import { WalletClientSigner, type SmartAccountSigner } from "@alchemy/aa-core"; -import { AuthProvider } from "@arcana/auth"; -import { createWalletClient, custom } from "viem"; +import { ArcanaAuthSigner } from "@alchemy/aa-signers/arcana-auth"; // See https://docs.arcana.network/quick-start/vue-quick-start#step-3-integrate-app for details. -const provider = new AuthProvider("xar_test_..."); +const clientId = "xar_test_..."; -await provider.init(); -await provider.connect(); +export const createArcanaAuthSigner = async () => { + const arcanaAuthSigner = new ArcanaAuthSigner({ clientId, params: {} }); -// The Viem wallet client 'arcanaAuthClient' wraps the Auth SDK EIP-1193 provider -export const arcanaAuthClient = createWalletClient({ - transport: custom(provider.provider), -}); + await arcanaAuthSigner.authenticate({ + async init() { + await arcanaAuthSigner.inner.init(); + }, + async connect() { + await arcanaAuthSigner.inner.connect(); + }, + }); -// A smart account signer you can use as an owner on ISmartContractAccount -export const arcanaAuthSigner: SmartAccountSigner = new WalletClientSigner( - arcanaAuthClient, - "arcana-auth" // signerType -); + return arcanaAuthSigner; +}; diff --git a/yarn.lock b/yarn.lock index 0c24cf283c..3ba03e312a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -166,12 +166,30 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" +"@arcana/auth@^1.0.8": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@arcana/auth/-/auth-1.0.8.tgz#917dfc2cf01d20ca51b17d40468b9370335ec071" + integrity sha512-yoMszxaocffdtOLKX+CFCR4ZHL4nYmvi00M1JWr9qeOsfC3P4NOLQCqXGksr0qaoVu2u3Te3G9iFB5/6euNWLg== + dependencies: + "@metamask/safe-event-emitter" "^2.0.0" + eth-rpc-errors "^4.0.3" + penpal "^6.0.1" + validator "^13.11.0" + "@assemblyscript/loader@^0.9.4": version "0.9.4" resolved "https://registry.yarnpkg.com/@assemblyscript/loader/-/loader-0.9.4.tgz#a483c54c1253656bb33babd464e3154a173e1577" integrity sha512-HazVq9zwTVwGmqdwYzu7WyQ6FQVZ7SwET0KKQuKm55jD0IfUpZgN0OPIiZG3zV1iSrVYcN0bdwLRXI/VNCYsUA== -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.22.13", "@babel/code-frame@^7.23.5": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.22.13": + version "7.22.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" + integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== + dependencies: + "@babel/highlight" "^7.22.13" + chalk "^2.4.2" + +"@babel/code-frame@^7.23.5": version "7.23.5" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244" integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA== @@ -413,7 +431,7 @@ "@babel/traverse" "^7.23.6" "@babel/types" "^7.23.6" -"@babel/highlight@^7.23.4": +"@babel/highlight@^7.22.13", "@babel/highlight@^7.23.4": version "7.23.4" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A== @@ -15031,6 +15049,11 @@ pbkdf2@^3.0.17, pbkdf2@^3.0.3: safe-buffer "^5.0.1" sha.js "^2.4.8" +penpal@^6.0.1: + version "6.2.2" + resolved "https://registry.yarnpkg.com/penpal/-/penpal-6.2.2.tgz#20a0a0592881a959e8c87116d854f9528b1aa601" + integrity sha512-RQD7hTx14/LY7QoS3tQYO3/fzVtwvZI+JeS5udgsu7FPaEDjlvfK9HBcme9/ipzSPKnrxSgacI9PI7154W62YQ== + picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" @@ -17731,6 +17754,11 @@ validate-npm-package-name@^5.0.0: dependencies: builtins "^5.0.0" +validator@^13.11.0: + version "13.11.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-13.11.0.tgz#23ab3fd59290c61248364eabf4067f04955fbb1b" + integrity sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ== + valtio@1.11.0: version "1.11.0" resolved "https://registry.yarnpkg.com/valtio/-/valtio-1.11.0.tgz#c029dcd17a0f99d2fbec933721fe64cfd32a31ed"