From 7673fc5a8b6e50f81a6f9ef1d649e3cc0e1d9e97 Mon Sep 17 00:00:00 2001 From: gilmeir-arnac <106956961+gilmeir-arnac@users.noreply.github.com> Date: Tue, 21 May 2024 18:48:36 +0300 Subject: [PATCH] feat: add fordefi signer to aa-signers (#665) * feat: add fordefi signer to aa-signers * fix: use @fordefi/web3-provider v0.2.0 * fix: remove user info from docs * fix: apply adjustments pushed to the PR that targeted `development` * docs: fix wrong markdown, casing and code block closing * docs: add Fordefi to examples of MPC wallets * fix: remove new viem version from lock file * docs: add jsdoc and remove void type aliases --------- Co-authored-by: Gil Meir --- packages/signers/package.json | 7 + .../src/fordefi/__tests__/signer.test.ts | 127 ++++++++++++++++++ packages/signers/src/fordefi/index.ts | 1 + packages/signers/src/fordefi/signer.ts | 121 +++++++++++++++++ packages/signers/src/index.ts | 1 + site/.vitepress/sidebar/index.ts | 1 + .../.vitepress/sidebar/packages/aa-signers.ts | 14 ++ .../aa-signers/fordefi/authenticate.md | 39 ++++++ .../aa-signers/fordefi/constructor.md | 57 ++++++++ .../packages/aa-signers/fordefi/getAddress.md | 40 ++++++ .../aa-signers/fordefi/getAuthDetails.md | 41 ++++++ .../aa-signers/fordefi/introduction.md | 77 +++++++++++ .../aa-signers/fordefi/signMessage.md | 44 ++++++ .../aa-signers/fordefi/signTypedData.md | 79 +++++++++++ site/resources/terms.md | 2 +- site/signers/choosing-a-signer.md | 2 +- site/signers/guides/fordefi.md | 88 ++++++++++++ site/snippets/signers/fordefi.ts | 17 +++ yarn.lock | 7 + 19 files changed, 763 insertions(+), 2 deletions(-) create mode 100644 packages/signers/src/fordefi/__tests__/signer.test.ts create mode 100644 packages/signers/src/fordefi/index.ts create mode 100644 packages/signers/src/fordefi/signer.ts create mode 100644 site/packages/aa-signers/fordefi/authenticate.md create mode 100644 site/packages/aa-signers/fordefi/constructor.md create mode 100644 site/packages/aa-signers/fordefi/getAddress.md create mode 100644 site/packages/aa-signers/fordefi/getAuthDetails.md create mode 100644 site/packages/aa-signers/fordefi/introduction.md create mode 100644 site/packages/aa-signers/fordefi/signMessage.md create mode 100644 site/packages/aa-signers/fordefi/signTypedData.md create mode 100644 site/signers/guides/fordefi.md create mode 100644 site/snippets/signers/fordefi.ts diff --git a/packages/signers/package.json b/packages/signers/package.json index 45521eef05..78d6203e51 100644 --- a/packages/signers/package.json +++ b/packages/signers/package.json @@ -71,6 +71,11 @@ "import": "./dist/esm/passport/index.js", "default": "./dist/cjs/passport/index.js" }, + "./fordefi": { + "types": "./dist/types/fordefi/index.d.ts", + "import": "./dist/esm/fordefi/index.js", + "default": "./dist/cjs/fordefi/index.js" + }, "./package.json": "./package.json" }, "scripts": { @@ -90,6 +95,7 @@ "@0xpass/webauthn-signer": "^1.0.0", "@arcana/auth": "^1.0.8", "@fireblocks/fireblocks-web3-provider": "^1.2.6", + "@fordefi/web3-provider": "^0.2.0", "@lit-protocol/crypto": "3.0.24", "@lit-protocol/lit-node-client": "3.0.24", "@lit-protocol/pkp-ethers": "3.0.24", @@ -132,6 +138,7 @@ "optionalDependencies": { "@arcana/auth": "^1.0.8", "@fireblocks/fireblocks-web3-provider": "^1.2.6", + "@fordefi/web3-provider": "^0.2.0", "@lit-protocol/crypto": "3.0.24", "@lit-protocol/lit-node-client": "3.0.24", "@lit-protocol/pkp-ethers": "3.0.24", diff --git a/packages/signers/src/fordefi/__tests__/signer.test.ts b/packages/signers/src/fordefi/__tests__/signer.test.ts new file mode 100644 index 0000000000..ce4c467c57 --- /dev/null +++ b/packages/signers/src/fordefi/__tests__/signer.test.ts @@ -0,0 +1,127 @@ +import { + type FordefiMethodName, + type FordefiRpcSchema, + FordefiWeb3Provider, + type MethodReturnType, + type RequestArgs, +} from "@fordefi/web3-provider"; +import { numberToHex } from "viem"; +import { FordefiSigner } from "../signer.js"; + +const fixtures = { + address: "0x1234567890123456789012345678901234567890", + chainId: 11155111, + message: "test", + signedMessage: "0xtest", + apiUserToken: "123-456", +} as const; + +describe("Fordefi Signer Tests", () => { + it("should correctly get address", async () => { + const signer = await givenSigner(); + + const address = await signer.getAddress(); + expect(address).toMatchInlineSnapshot(`"${fixtures.address}"`); + }); + + 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", async () => { + const signer = await givenSigner(); + + const details = await signer.getAuthDetails(); + expect(details).toBeUndefined(); + }); + + 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(fixtures.message); + expect(signMessage).toMatchInlineSnapshot(`"${fixtures.signedMessage}"`); + }); + + it("should correctly fail to sign message if unauthenticated", async () => { + const signer = await givenSigner(false); + + const signMessage = signer.signMessage(fixtures.message); + 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(`"${fixtures.signedMessage}"`); + }); +}); + +const givenSigner = async (auth = true) => { + FordefiWeb3Provider.prototype.request = vi.fn((async < + M extends FordefiMethodName + >( + args: RequestArgs + ) => { + switch (args.method) { + case "eth_accounts": + return Promise.resolve([fixtures.address]) as Promise< + MethodReturnType + >; + case "eth_chainId": + return Promise.resolve(numberToHex(fixtures.chainId)) as Promise< + MethodReturnType + >; + case "personal_sign": + return Promise.resolve(fixtures.signedMessage) as Promise< + MethodReturnType + >; + case "eth_signTypedData_v4": + return Promise.resolve(fixtures.signedMessage) as Promise< + MethodReturnType + >; + default: + return Promise.reject(new Error("Method not found")); + } + }) as FordefiWeb3Provider["request"]); + + const inner = new FordefiWeb3Provider({ + chainId: fixtures.chainId, + address: fixtures.address, + apiUserToken: fixtures.apiUserToken, + apiPayloadSignKey: "fakeApiKey", + }); + + const signer = new FordefiSigner({ inner }); + + if (auth) { + await signer.authenticate(); + } + + return signer; +}; diff --git a/packages/signers/src/fordefi/index.ts b/packages/signers/src/fordefi/index.ts new file mode 100644 index 0000000000..240ef27037 --- /dev/null +++ b/packages/signers/src/fordefi/index.ts @@ -0,0 +1 @@ +export { FordefiSigner } from "./signer.js"; diff --git a/packages/signers/src/fordefi/signer.ts b/packages/signers/src/fordefi/signer.ts new file mode 100644 index 0000000000..4156f211fc --- /dev/null +++ b/packages/signers/src/fordefi/signer.ts @@ -0,0 +1,121 @@ +import { + WalletClientSigner, + type SmartAccountAuthenticator, +} from "@alchemy/aa-core"; +import { + type FordefiProviderConfig, + FordefiWeb3Provider, +} from "@fordefi/web3-provider"; +import { + createWalletClient, + custom, + type Hash, + type SignableMessage, + type TypedData, + type TypedDataDefinition, +} from "viem"; +import { signerTypePrefix } from "../constants.js"; + +/** + * This class requires the `@fordefi/web3-provider` dependency. + * `@alchemy/aa-signers` lists it as optional dependency. + * + * @see https://github.com/FordefiHQ/web3-provider + */ +export class FordefiSigner + implements SmartAccountAuthenticator +{ + inner: FordefiWeb3Provider; + private signer: WalletClientSigner | undefined; + + constructor(params: FordefiProviderConfig | { inner: FordefiWeb3Provider }) { + if ("inner" in params) { + this.inner = params.inner; + return; + } + + this.inner = new FordefiWeb3Provider(params); + } + + readonly signerType = `${signerTypePrefix}fordefi`; + + /** + * Returns the address managed by this signer. + * + * @returns the address managed by this signer + * @throws if the provider is not authenticated, or if the address was not found + */ + 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 satisfies Hash; + }; + + /** + * Signs a message with the authenticated account. + * + * @param msg the message to sign + * @returns the address of the authenticated account + * @throws if the provider is not authenticated + */ + signMessage = async (msg: SignableMessage) => { + if (!this.signer) throw new Error("Not authenticated"); + + return this.signer.signMessage(msg); + }; + + /** + * Signs a typed data object with the authenticated account. + * + * @param params the data object to sign + * @returns the signed data as a hex string + * @throws if the provider is not authenticated + */ + signTypedData = async < + const TTypedData extends TypedData | { [key: string]: unknown }, + TPrimaryType extends string = string + >( + params: TypedDataDefinition + ) => { + if (!this.signer) throw new Error("Not authenticated"); + + return this.signer.signTypedData(params); + }; + + /** + * Authenticates with the Fordefi platform and verifies that this client + * is authorized to manage the account. + * This step is required before any signing operations can be performed. + * + * @returns void + * @throws if no provider was found, or if authentication failed + */ + authenticate = async (): Promise => { + if (this.inner == null) throw new Error("No provider found"); + + await this.inner.connect(); + + this.signer = new WalletClientSigner( + createWalletClient({ + transport: custom(this.inner), + }), + this.signerType + ); + + return this.getAuthDetails(); + }; + + /** + * Verifies that this signer is authenticated, and throws an error otherwise. + * Authentication details are not available. + * + * @returns void + * @throws Error if this signer is not authenticated + */ + getAuthDetails = async (): Promise => { + if (!this.signer) throw new Error("Not authenticated"); + }; +} diff --git a/packages/signers/src/index.ts b/packages/signers/src/index.ts index e8e6d222c0..7cb8563c6a 100644 --- a/packages/signers/src/index.ts +++ b/packages/signers/src/index.ts @@ -33,3 +33,4 @@ export { ArcanaAuthSigner, type ArcanaAuthAuthenticationParams, } from "./arcana-auth/index.js"; +export { FordefiSigner } from "./fordefi/index.js"; diff --git a/site/.vitepress/sidebar/index.ts b/site/.vitepress/sidebar/index.ts index 8fa3e96f39..f7758fd08c 100644 --- a/site/.vitepress/sidebar/index.ts +++ b/site/.vitepress/sidebar/index.ts @@ -239,6 +239,7 @@ export const sidebar: DefaultTheme.Sidebar = [ { text: "Dfns", link: "/dfns" }, { text: "WalletKit", link: "/walletkit" }, { text: "Passport", link: "/passport" }, + { text: "Fordefi", link: "/fordefi" }, ], }, { text: "EOA signer", link: "/eoa" }, diff --git a/site/.vitepress/sidebar/packages/aa-signers.ts b/site/.vitepress/sidebar/packages/aa-signers.ts index 92718aad66..05bb4e5cf3 100644 --- a/site/.vitepress/sidebar/packages/aa-signers.ts +++ b/site/.vitepress/sidebar/packages/aa-signers.ts @@ -149,6 +149,20 @@ export const aaSignersSidebar: DefaultTheme.SidebarItem = { { text: "getAuthDetails", link: "/getAuthDetails" }, ], }, + { + text: "Fordefi Signer", + collapsed: true, + base: "/packages/aa-signers/fordefi", + 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: "Contributing", link: "/contributing" }, ], }; diff --git a/site/packages/aa-signers/fordefi/authenticate.md b/site/packages/aa-signers/fordefi/authenticate.md new file mode 100644 index 0000000000..fab100ad98 --- /dev/null +++ b/site/packages/aa-signers/fordefi/authenticate.md @@ -0,0 +1,39 @@ +--- +outline: deep +head: + - - meta + - property: og:title + content: FordefiSigner • authenticate + - - meta + - name: description + content: Overview of the authenticate method on FordefiSigner + - - meta + - property: og:description + content: Overview of the authenticate method on FordefiSigner +--- + +# authenticate + +`authenticate` is a method on the `FordefiSigner` which leverages the `Fordefi` provider to authenticate a user. + +You must call this method before accessing the other methods available on the `FordefiSigner`, such as signing messages or typed data or accessing user details. + +## Usage + +::: code-group + +```ts [example.ts] +// [!code focus:99] +import { FordefiSigner } from "@alchemy/aa-signers/fordefi"; + +const fordefiSigner = new FordefiSigner({ + chainId: 11155111, + address: "0x1234567890123456789012345678901234567890", + apiUserToken: process.env.FORDEFI_API_USER_TOKEN!, + apiPayloadSignKey: process.env.FORDEFI_API_PAYLOAD_SIGNING_KEY!, +}); + +await fordefiSigner.authenticate(); +``` + +::: diff --git a/site/packages/aa-signers/fordefi/constructor.md b/site/packages/aa-signers/fordefi/constructor.md new file mode 100644 index 0000000000..c1199c50db --- /dev/null +++ b/site/packages/aa-signers/fordefi/constructor.md @@ -0,0 +1,57 @@ +--- +outline: deep +head: + - - meta + - property: og:title + content: FordefiSigner • constructor + - - meta + - name: description + content: Overview of the constructor method on FordefiSigner in aa-signers + - - meta + - property: og:description + content: Overview of the constructor method on FordefiSigner in aa-signers +--- + +# constructor + +To initialize a `FordefiSigner`, you must provide a set of parameters detailed below. + +## Usage + +::: code-group + +```ts [example.ts] +import { FordefiSigner } from "@alchemy/aa-signers/fordefi"; + +// instantiates using every possible parameter, as a reference +const fordefiSigner = new FordefiSigner({ + chainId: 11155111, + address: "0x1234567890123456789012345678901234567890", + apiUserToken: process.env.FORDEFI_API_USER_TOKEN!, + apiPayloadSignKey: process.env.FORDEFI_API_PAYLOAD_SIGNING_KEY!, +}); +``` + +::: + +## Returns + +### `FordefiSigner` + +A new instance of a `FordefiSigner`. + +## Parameters + +### `params: FordefiProviderConfig | { inner: FordefiWeb3Provider }` + +You can either pass in a constructed `FordefiWeb3Provider` object, or directly pass into the `FordefiSigner` the `FordefiProviderConfig` used to construct a `FordefiWeb3Provider` object. These parameters are listed on the [Fordefi repo](https://github.com/FordefiHQ/web3-provider/blob/main/src/types/config.ts) as well. + +`FordefiProviderConfig` takes in the following parameters: + +- `chainId: EvmChainId | EvmChainUniqueId` -- Chain on which the signer acts for the given address. + +- `address: Address` -- EVM address. + +- `apiUserToken: string` -- Fordefi API User token issued via the [Fordefi Web Console](https://app.fordefi.com). + +- `apiPayloadSignKey: string` -- Private key in PEM format used to sign the body of requests sent to the Fordefi API - the content of the private key `.pem`, see [Create a public/private signature key pair for the API client](https://docs.fordefi.com/reference/pair-an-api-client-with-the-api-signer). diff --git a/site/packages/aa-signers/fordefi/getAddress.md b/site/packages/aa-signers/fordefi/getAddress.md new file mode 100644 index 0000000000..60d9014f41 --- /dev/null +++ b/site/packages/aa-signers/fordefi/getAddress.md @@ -0,0 +1,40 @@ +--- +outline: deep +head: + - - meta + - property: og:title + content: FordefiSigner • getAddress + - - meta + - name: description + content: Overview of the getAddress method on FordefiSigner + - - meta + - property: og:description + content: Overview of the getAddress method on FordefiSigner +--- + +# getAddress + +`getAddress` returns the EOA address of the Signer. + +This method must be called after [`authenticate`](/packages/aa-signers/fordefi/authenticate). Otherwise, this method will throw an error with the message `Not Authenticated`. + +## Usage + +::: code-group + +```ts [example.ts] +import { createFordefiSigner } from "./fordefi"; +// [!code focus:99] +const fordefiSigner = await createFordefiSigner(); + +const address = await fordefiSigner.getAddress(); +``` + +<<< @/snippets/signers/fordefi.ts +::: + +## Returns + +### `Promise
` + +A Promise containing the EOA address accessible via the Signer. diff --git a/site/packages/aa-signers/fordefi/getAuthDetails.md b/site/packages/aa-signers/fordefi/getAuthDetails.md new file mode 100644 index 0000000000..e174037d7e --- /dev/null +++ b/site/packages/aa-signers/fordefi/getAuthDetails.md @@ -0,0 +1,41 @@ +--- +outline: deep +head: + - - meta + - property: og:title + content: FordefiSigner • getAuthDetails + - - meta + - name: description + content: Overview of the getAuthDetails method on FordefiSigner + - - meta + - property: og:description + content: Overview of the getAuthDetails method on FordefiSigner +--- + +# getAuthDetails + +`getAuthDetails` currently does not have a return value. + +This method must be called after [`authenticate`](/packages/aa-signers/fordefi/authenticate). Otherwise, this method will throw an error with the message `Not Authenticated`. + +## Usage + +::: code-group + +```ts [example.ts] +import { createFordefiSigner } from "./fordefi"; +// [!code focus:99] +const fordefiSigner = await createFordefiSigner(); + +const details = await fordefiSigner.getAuthDetails(); +``` + +<<< @/snippets/signers/fordefi.ts +::: + +## Returns + +### `Promise` + +Verifies that this signer is authenticated, and throws an error otherwise. +Authentication details are not available. diff --git a/site/packages/aa-signers/fordefi/introduction.md b/site/packages/aa-signers/fordefi/introduction.md new file mode 100644 index 0000000000..3d94b69351 --- /dev/null +++ b/site/packages/aa-signers/fordefi/introduction.md @@ -0,0 +1,77 @@ +--- +outline: deep +head: + - - meta + - property: og:title + content: FordefiSigner + - - meta + - name: description + content: Overview of the FordefiSigner class in aa-signers + - - meta + - property: og:description + content: Overview of the FordefiSigner class in aa-signers +--- + +# Fordefi Signer + +`FordefiSigner` is a signer implementation which extends `SmartAccountAuthenticator` to leverage the [Fordefi Web3 Provider](https://github.com/FordefiHQ/web3-provider). It supports features such as authentication, message and typed data signing, and authentication details retrieval. + +`FordefiSigner` provides implementations for all methods on `SmartAccountAuthenticator`: + +1. [`authenticate`](/packages/aa-signers/fordefi/authenticate) -- supports user authentication. +2. [`getAddress`](/packages/aa-signers/fordefi/getAddress) -- gets the address of the smart contract account's connected EOA signer account. +3. [`signMessage`](/packages/aa-signers/fordefi/signMessage) -- supports message signatures. +4. [`signTypedData`](/packages/aa-signers/fordefi/signTypedData) -- supports typed data signatures. +5. [`getAuthDetails`](/packages/aa-signers/fordefi/getAuthDetails) -- verifies that this signer is authenticated, it does not return any details. + +## Install Dependencies + +`FordefiSigner` requires installation of the [`@fordefi/web3-provider`](https://github.com/FordefiHQ/web3-provider) SDK. `aa-signers` lists it as an optional dependency. + +::: code-group + +```bash [npm] +npm i -s @fordefi/web3-provider +``` + +```bash [yarn] +yarn add @fordefi/web3-provider +``` + +::: + +## Usage + +::: code-group + +```ts [example.ts] +import { createFordefiSigner } from "./fordefi"; + +const fordefiSigner = await createFordefiSigner(); + +const address = await fordefiSigner.getAddress(); + +const details = await fordefiSigner.getAuthDetails(); + +const signedMessage = await fordefiSigner.signMessage("test"); + +const typedData = { + types: { + Request: [{ name: "hello", type: "string" }], + }, + primaryType: "Request", + message: { + hello: "world", + }, +}; +const signTypedData = await fordefiSigner.signTypedData(typedData); +``` + +<<< @/snippets/signers/fordefi.ts +::: + +## Developer Links + +- [Fordefi Web3 Provider](https://github.com/FordefiHQ/web3-provider) +- [Fordefi Web3 Provider Docs](https://web3provider-docs.fordefi.com/) +- [FordefiSigner Tests](https://github.com/alchemyplatform/aa-sdk/blob/main/packages/signers/src/fordefi/__tests__/signer.test.ts) diff --git a/site/packages/aa-signers/fordefi/signMessage.md b/site/packages/aa-signers/fordefi/signMessage.md new file mode 100644 index 0000000000..87cbd82217 --- /dev/null +++ b/site/packages/aa-signers/fordefi/signMessage.md @@ -0,0 +1,44 @@ +--- +outline: deep +head: + - - meta + - property: og:title + content: FordefiSigner • signMessage + - - meta + - name: description + content: Overview of the signMessage method on FordefiSigner + - - meta + - property: og:description + content: Overview of the signMessage method on FordefiSigner +--- + +# signMessage + +`signMessage` supports signing messages from the `FordefiSigner`. + +This method must be called after [`authenticate`](/packages/aa-signers/fordefi/authenticate). Otherwise, this method will throw an error with the message `Not Authenticated`. + +## Usage + +::: code-group + +```ts [example.ts] +import { createFordefiSigner } from "./fordefi"; +// [!code focus:99] +const fordefiSigner = await createFordefiSigner(); + +const signedMessage = await fordefiSigner.signMessage("test"); +``` + +<<< @/snippets/signers/fordefi.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/fordefi/signTypedData.md b/site/packages/aa-signers/fordefi/signTypedData.md new file mode 100644 index 0000000000..6b17f96ba1 --- /dev/null +++ b/site/packages/aa-signers/fordefi/signTypedData.md @@ -0,0 +1,79 @@ +--- +outline: deep +head: + - - meta + - property: og:title + content: FordefiSigner • signTypedData + - - meta + - name: description + content: Overview of the signTypedData method on FordefiSigner + - - meta + - property: og:description + content: Overview of the signTypedData method on FordefiSigner +--- + +# signTypedData + +`signTypedData` supports signing typed data from the `FordefiSigner`. + +This method must be called after [`authenticate`](/packages/aa-signers/fordefi/authenticate). Otherwise, this method will throw an error with the message `Not Authenticated`. + +## Usage + +::: code-group + +```ts [example.ts] +import { createFordefiSigner } from "./fordefi"; +// [!code focus:99] +const fordefiSigner = await createFordefiSigner(); + +const signedTypedData = await fordefiSigner.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/signers/fordefi.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/resources/terms.md b/site/resources/terms.md index 951d175b9c..cd163bb07a 100644 --- a/site/resources/terms.md +++ b/site/resources/terms.md @@ -84,7 +84,7 @@ A service or application that manages the private key and signs either arbitrary - **Custodial**: Managed by a third party, it holds and autonomously uses the private key for transactions, necessitating complete user trust. - **Non-custodial**: While a third party manages the private key, user involvement is required for signing transactions. Examples: Metamask. -- **MPC (Multi-Party Computation)**: Partial or complete key shares are managed by third parties, but user participation is needed for transaction signatures. Examples: Privy, Portal, Fireblocks. +- **MPC (Multi-Party Computation)**: Partial or complete key shares are managed by third parties, but user participation is needed for transaction signatures. Examples: Privy, Portal, Fireblocks, Fordefi. - **Decentralized MPC**: Operated by a decentralized network, it manages key shares and requires node consensus for transaction signatures. Examples: Lit, Web3auth, 0xpass. ## Smart Contract Account diff --git a/site/signers/choosing-a-signer.md b/site/signers/choosing-a-signer.md index 2305956b30..616aab07b2 100644 --- a/site/signers/choosing-a-signer.md +++ b/site/signers/choosing-a-signer.md @@ -80,7 +80,7 @@ Valid signatures do not always require all shares to sign a transaction. MPC Sig Some MPC signers provide recovery services in which key share(s) are backed up in the service provider’s cloud, on the end user’s device, or in the end user’s cloud (e.g. iCloud or Google Drive). When evaluating an MPC provider, it’s important to under where each key share is stored. -**Example**: Privy, Fireblocks MPC, Portal, Capsule, WalletKit +**Example**: Privy, Fireblocks MPC, Portal, Capsule, WalletKit, Fordefi ::: details TSS vs SSSS diff --git a/site/signers/guides/fordefi.md b/site/signers/guides/fordefi.md new file mode 100644 index 0000000000..2ab14fb424 --- /dev/null +++ b/site/signers/guides/fordefi.md @@ -0,0 +1,88 @@ +--- +outline: deep +head: + - - meta + - property: og:title + content: Fordefi Integration Guide + - - meta + - name: description + content: Follow this integration guide to use Fordefi as a Signer with Account Kit, a vertically integrated stack for building apps that support ERC-4337 and ERC-6900. + - - meta + - property: og:description + content: Follow this integration guide to use Fordefi as a Signer with Account Kit, a vertically integrated stack for building apps that support ERC-4337 and ERC-6900. + - - meta + - name: twitter:title + content: Fordefi Integration Guide + - - meta + - name: twitter:description + content: Follow this integration guide to use Fordefi as a Signer with Account Kit, a vertically integrated stack for building apps that support ERC-4337 and ERC-6900. +--- + +# Fordefi Integration Guide + +[Fordefi](https://fordefi.com) is a blockchain security company that provides an **institutional-grade MPC (Multi-Party Computation) +non-custodial wallet** specifically built for decentralized finance (DeFi). + +Fordefi focuses on enhancing the security and efficiency of transactions in the DeFi space through the innovative use of MPC technology +for key management and transaction signing, +and by providing a secure and user-friendly interface through various clients: + +1. [Fordefi Browser Extension](https://chromewebstore.google.com/detail/fordefi/hcmehenccjdmfbojapcbcofkgdpbnlle) for interaction with dApps. +2. [Fordefi Web Console](https://app.fordefi.com) for securely performing administrative operations such as setting up transaction policy + rules and user management, which require approvals by a quorum of administrators. +3. [Fordefi API](https://docs.fordefi.com/reference/api-overview) for developers to interact with the Fordefi infrastructure. + +Read more about Fordefi on the [site](https://fordefi.com) and [docs](https://docs.fordefi.com). + +Combining Fordefi with Account Kit allows you to get the best of both worlds. +You can use Fordefi via the [`aa-signers`](/packages/aa-signers/index) package to generate a wallet scoped to your application, +and then use [`aa-alchemy`](/packages/aa-alchemy/index) to create smart accounts for your users. + +## Integration + +### Install the Fordefi Web3 Provider + +Using `FordefiSigner` in the `aa-signers` package requires installation of the [`@fordefi/web3-provider`](https://github.com/FordefiHQ/web3-provider) SDK. `aa-signers` lists it as optional dependency. + +::: code-group + +```bash [npm] +npm i -s @fordefi/web3-provider +``` + +```bash [yarn] +yarn add @fordefi/web3-provider +``` + +::: + +### Create a SmartAccountSigner + +Next, setup the Fordefi Web3 Provider and create a `SmartAccountSigner` using the `aa-signers` package: + +<<< @/snippets/signers/fordefi.ts + +### Use it with Modular Account + +Let's see it in action with `aa-alchemy`: + +::: code-group + +```ts [example.ts] +import { createModularAccountAlchemyClient } from "@alchemy/aa-alchemy"; +import { sepolia } from "@alchemy/aa-core"; +import { createFordefiSigner } from "./fordefi"; + +const chain = sepolia; + +const signer = await createFordefiSigner(); +const provider = await createModularAccountAlchemyClient({ + apiKey: "ALCHEMY_API_KEY", + chain, + signer, +}); +``` + +<<< @/snippets/signers/fordefi.ts + +::: diff --git a/site/snippets/signers/fordefi.ts b/site/snippets/signers/fordefi.ts new file mode 100644 index 0000000000..77a310eadc --- /dev/null +++ b/site/snippets/signers/fordefi.ts @@ -0,0 +1,17 @@ +import { FordefiSigner } from "@alchemy/aa-signers/fordefi"; +import { FordefiWeb3Provider } from "@fordefi/web3-provider"; + +const fordefi = new FordefiWeb3Provider({ + address: "0x1234567890123456789012345678901234567890", + chainId: 11155111, + apiUserToken: process.env.FORDEFI_API_USER_TOKEN!, + apiPayloadSignKey: process.env.FORDEFI_API_PAYLOAD_SIGNING_KEY!, +}); + +export const createFordefiSigner = async () => { + const fordefiSigner = new FordefiSigner(fordefi); + + await fordefiSigner.authenticate(); + + return fordefiSigner; +}; diff --git a/yarn.lock b/yarn.lock index c66569ce4e..8522d4a6b9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4178,6 +4178,13 @@ fireblocks-sdk "^3.1.4" web3-providers-http "1.8.0" +"@fordefi/web3-provider@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@fordefi/web3-provider/-/web3-provider-0.2.0.tgz#c48da777410081e29e6ccdae6e40c8dc1558f395" + integrity sha512-7zBzg/F8fJgspmjeKbI3QjNKLYmycCtWOuPf225Ye0O+8zRtQr6PgaCh2tDhmFMa3gmAJ+BAhuPqCyYyNw4Y+w== + dependencies: + viem "^2.8.6" + "@github/webauthn-json@^2.1.1": version "2.1.1" resolved "https://registry.npmjs.org/@github/webauthn-json/-/webauthn-json-2.1.1.tgz"