From 340c5c843a515a934ee276428bdb312d2a773194 Mon Sep 17 00:00:00 2001 From: Michael Moldoveanu Date: Tue, 13 Feb 2024 18:07:50 -0500 Subject: [PATCH] docs: add docs for using the alchemy signer (#445) --- packages/alchemy/src/signer/client/index.ts | 2 - site/.vitepress/sidebar/index.ts | 1 + .../.vitepress/sidebar/packages/aa-alchemy.ts | 48 +++++++++++++ site/packages/aa-alchemy/signer/addPasskey.md | 49 +++++++++++++ .../aa-alchemy/signer/authenticate.md | 71 ++++++++++++++++++ site/packages/aa-alchemy/signer/disconnect.md | 37 ++++++++++ .../aa-alchemy/signer/exportWallet.md | 57 +++++++++++++++ site/packages/aa-alchemy/signer/getAddress.md | 45 ++++++++++++ .../aa-alchemy/signer/getAuthDetails.md | 45 ++++++++++++ site/packages/aa-alchemy/signer/getUser.md | 41 +++++++++++ site/packages/aa-alchemy/signer/overview.md | 51 +++++++++++++ .../packages/aa-alchemy/signer/signMessage.md | 50 +++++++++++++ .../aa-alchemy/signer/signTypedData.md | 49 +++++++++++++ site/signers/alchemy-signer.md | 72 +++++++++++++++++++ site/signers/choosing-a-signer.md | 7 +- .../signers/alchemy/SignupLoginComponent.tsx | 56 +++++++++++++++ site/snippets/signers/alchemy/signer.ts | 14 ++++ site/snippets/signers/alchemy/usePromise.ts | 20 ++++++ 18 files changed, 707 insertions(+), 8 deletions(-) create mode 100644 site/packages/aa-alchemy/signer/addPasskey.md create mode 100644 site/packages/aa-alchemy/signer/authenticate.md create mode 100644 site/packages/aa-alchemy/signer/disconnect.md create mode 100644 site/packages/aa-alchemy/signer/exportWallet.md create mode 100644 site/packages/aa-alchemy/signer/getAddress.md create mode 100644 site/packages/aa-alchemy/signer/getAuthDetails.md create mode 100644 site/packages/aa-alchemy/signer/getUser.md create mode 100644 site/packages/aa-alchemy/signer/overview.md create mode 100644 site/packages/aa-alchemy/signer/signMessage.md create mode 100644 site/packages/aa-alchemy/signer/signTypedData.md create mode 100644 site/signers/alchemy-signer.md create mode 100644 site/snippets/signers/alchemy/SignupLoginComponent.tsx create mode 100644 site/snippets/signers/alchemy/signer.ts create mode 100644 site/snippets/signers/alchemy/usePromise.ts diff --git a/packages/alchemy/src/signer/client/index.ts b/packages/alchemy/src/signer/client/index.ts index 9a647ffd41..a94b79b1bf 100644 --- a/packages/alchemy/src/signer/client/index.ts +++ b/packages/alchemy/src/signer/client/index.ts @@ -7,7 +7,6 @@ import { IframeStamper } from "@turnkey/iframe-stamper"; import { WebauthnStamper } from "@turnkey/webauthn-stamper"; import { type Hex } from "viem"; import { z } from "zod"; -import { SessionManagerParamsSchema } from "../session/manager.js"; import { base64UrlEncode } from "../utils/base64UrlEncode.js"; import { generateRandomBuffer } from "../utils/generateRandomBuffer.js"; import type { @@ -26,7 +25,6 @@ export const AlchemySignerClientParamsSchema = z.object({ iframeElementId: z.string().default("turnkey-iframe"), iframeContainerId: z.string(), }), - sessionConfig: SessionManagerParamsSchema.optional(), }); export type AlchemySignerClientParams = z.input< diff --git a/site/.vitepress/sidebar/index.ts b/site/.vitepress/sidebar/index.ts index 4f36f21a16..6b40af6999 100644 --- a/site/.vitepress/sidebar/index.ts +++ b/site/.vitepress/sidebar/index.ts @@ -51,6 +51,7 @@ export const sidebar: DefaultTheme.Sidebar = [ base: "/signers", items: [ { text: "Introduction", link: "/choosing-a-signer" }, + { text: "Alchemy Signer", link: "/alchemy-signer" }, { text: "Signer guides", base: "/signers/guides", diff --git a/site/.vitepress/sidebar/packages/aa-alchemy.ts b/site/.vitepress/sidebar/packages/aa-alchemy.ts index cb5f63a3bb..c2da0f37cc 100644 --- a/site/.vitepress/sidebar/packages/aa-alchemy.ts +++ b/site/.vitepress/sidebar/packages/aa-alchemy.ts @@ -51,6 +51,54 @@ export const aaAlchemySidebar: DefaultTheme.SidebarItem = { }, ], }, + { + text: "Alchemy Signer", + base: "/packages/aa-alchemy/signer", + collapsed: true, + items: [ + { + text: "Overview", + link: "/overview", + }, + { + text: "authenticate", + link: "/authenticate", + }, + { + text: "disconnect", + link: "/disconnect", + }, + { + text: "getAuthDetails", + link: "/getAuthDetails", + }, + + { + text: "getAddress", + link: "/getAddress", + }, + { + text: "signMessage", + link: "/signMessage", + }, + { + text: "signTypedData", + link: "/signTypedData", + }, + { + text: "getUser", + link: "/getUser", + }, + { + text: "addPasskey", + link: "/addPasskey", + }, + { + text: "exportWallet", + link: "/exportWallet", + }, + ], + }, { text: "Utils", collapsed: true, diff --git a/site/packages/aa-alchemy/signer/addPasskey.md b/site/packages/aa-alchemy/signer/addPasskey.md new file mode 100644 index 0000000000..34457a7140 --- /dev/null +++ b/site/packages/aa-alchemy/signer/addPasskey.md @@ -0,0 +1,49 @@ +--- +outline: deep +head: + - - meta + - property: og:title + content: Alchemy Signer • addPasskey + - - meta + - name: description + content: Learn how to use the AlchemySigner.addPasskey method + - - meta + - property: og:description + content: Learn how to use the AlchemySigner.addPasskey method + - - meta + - name: twitter:title + content: Alchemy Signer • addPasskey + - - meta + - name: twitter:description + content: Learn how to use the AlchemySigner.addPasskey method +--- + +# addPasskey + +The `addPasskey` method is used to add a passkey as an auth method to an already logged in user. + +::: warning +This method throws if there is no authenticated user. +::: + +## Usage + +::: code-group + +```ts +import { signer } from "./signer"; + +await signer.addPasskey(); +``` + +<<< @/snippets/signers/alchemy/signer.ts + +::: + +## Returns + +`Promise` -- on success returns an array of credential ids + +## Parameters + +`params?: CredentialCreationOptions` -- overrides for the WebAuthn credential creation options. For more info on the `CredentialCreationOptions` interface, see [here](https://microsoft.github.io/PowerBI-JavaScript/interfaces/_node_modules_typedoc_node_modules_typescript_lib_lib_dom_d_.credentialcreationoptions.html). diff --git a/site/packages/aa-alchemy/signer/authenticate.md b/site/packages/aa-alchemy/signer/authenticate.md new file mode 100644 index 0000000000..4889eaa262 --- /dev/null +++ b/site/packages/aa-alchemy/signer/authenticate.md @@ -0,0 +1,71 @@ +--- +outline: deep +head: + - - meta + - property: og:title + content: Alchemy Signer • authenticate + - - meta + - name: description + content: Learn how to use the AlchemySigner.authenticate method + - - meta + - property: og:description + content: Learn how to use the AlchemySigner.authenticate method + - - meta + - name: twitter:title + content: Alchemy Signer • authenticate + - - meta + - name: twitter:description + content: Learn how to use the AlchemySigner.authenticate method +--- + +# authenticate + +The `authenticate` method is used to authenticate a user with the Alchemy Signer. + +## Usage + +::: code-group + +```ts +import { signer } from "./signer"; + +const bundlePromise = new Promise(async (resolve) => { + // up to you define how you collect the OTP from the user + const otpFromUser = await getOtpFromUser(); + resolve(otpFromUser); +}); + +const user = await signer.authenticate({ + type: "email", + email: "user@email.com", + // the bundle is the OTP that the user will input from their email + bundle: bundlePromise, +}); +``` + +<<< @/snippets/signers/alchemy/signer.ts + +::: + +## Returns + +`Promise` -- on success returns a `User` object representing the authenticated user. + +## Parameters + +`AuthParams` -- an object that contains the following properties: + +```ts +export type AuthParams = + | { type: "email"; email: string; bundle: Promise } + | { + type: "passkey"; + createNew: false; + } + | { + type: "passkey"; + createNew: true; + username: string; + creationOpts?: CredentialCreationOptionOverrides; + }; +``` diff --git a/site/packages/aa-alchemy/signer/disconnect.md b/site/packages/aa-alchemy/signer/disconnect.md new file mode 100644 index 0000000000..d480b4d2de --- /dev/null +++ b/site/packages/aa-alchemy/signer/disconnect.md @@ -0,0 +1,37 @@ +--- +outline: deep +head: + - - meta + - property: og:title + content: Alchemy Signer • disconnect + - - meta + - name: description + content: Learn how to use the AlchemySigner.disconnect method + - - meta + - property: og:description + content: Learn how to use the AlchemySigner.disconnect method + - - meta + - name: twitter:title + content: Alchemy Signer • disconnect + - - meta + - name: twitter:description + content: Learn how to use the AlchemySigner.disconnect method +--- + +# disconnect + +The `disconnect` method is used to disconnect a user from the Alchemy Signer and clear the local session. + +## Usage + +::: code-group + +```ts +import { signer } from "./signer"; + +await signer.disconnect(); +``` + +<<< @/snippets/signers/alchemy/signer.ts + +::: diff --git a/site/packages/aa-alchemy/signer/exportWallet.md b/site/packages/aa-alchemy/signer/exportWallet.md new file mode 100644 index 0000000000..9aa8ad2feb --- /dev/null +++ b/site/packages/aa-alchemy/signer/exportWallet.md @@ -0,0 +1,57 @@ +--- +outline: deep +head: + - - meta + - property: og:title + content: Alchemy Signer • exportWallet + - - meta + - name: description + content: Learn how to use the AlchemySigner.exportWallet method + - - meta + - property: og:description + content: Learn how to use the AlchemySigner.exportWallet method + - - meta + - name: twitter:title + content: Alchemy Signer • exportWallet + - - meta + - name: twitter:description + content: Learn how to use the AlchemySigner.exportWallet method +--- + +# exportWallet + +The `exportWallet` method is used to export the user's private key or seed phrase. + +If the user is authenticated with an Email, this will return a seed phrase +If the user is authenticated with a Passkey, this will return a private key + +::: warning +This method throws if there is no authenticated user. +::: + +## Usage + +::: code-group + +```ts +import { signer } from "./signer"; + +await signer.exportWallet({ + iframeContainerId: "my-export-wallet-container", +}); +``` + +<<< @/snippets/signers/alchemy/signer.ts + +::: + +## Returns + +`Promise` -- returns a boolean indicating the success of the export + +## Parameters + +`params: ExportWalletParams` + +- `iframeContainerId: string` -- the id of the container to render the export wallet iframe in +- `iframeElementId?: string` -- the id given to the iframe element that will be injected into the DOM diff --git a/site/packages/aa-alchemy/signer/getAddress.md b/site/packages/aa-alchemy/signer/getAddress.md new file mode 100644 index 0000000000..0e59069f2c --- /dev/null +++ b/site/packages/aa-alchemy/signer/getAddress.md @@ -0,0 +1,45 @@ +--- +outline: deep +head: + - - meta + - property: og:title + content: Alchemy Signer • getAddress + - - meta + - name: description + content: Learn how to use the AlchemySigner.getAddress method + - - meta + - property: og:description + content: Learn how to use the AlchemySigner.getAddress method + - - meta + - name: twitter:title + content: Alchemy Signer • getAddress + - - meta + - name: twitter:description + content: Learn how to use the AlchemySigner.getAddress method +--- + +# getAddress + +Returns the signer's public address. + +::: warning +This method throws if there is no authenticated user. +::: + +## Usage + +::: code-group + +```ts +import { signer } from "./signer"; + +const address = await signer.getAddress(); +``` + +<<< @/snippets/signers/alchemy/signer.ts + +::: + +## Returns + +`Promise
` -- on success returns the signer's public address. diff --git a/site/packages/aa-alchemy/signer/getAuthDetails.md b/site/packages/aa-alchemy/signer/getAuthDetails.md new file mode 100644 index 0000000000..041eb382c2 --- /dev/null +++ b/site/packages/aa-alchemy/signer/getAuthDetails.md @@ -0,0 +1,45 @@ +--- +outline: deep +head: + - - meta + - property: og:title + content: Alchemy Signer • getAuthDetails + - - meta + - name: description + content: Learn how to use the AlchemySigner.getAuthDetails method + - - meta + - property: og:description + content: Learn how to use the AlchemySigner.getAuthDetails method + - - meta + - name: twitter:title + content: Alchemy Signer • getAuthDetails + - - meta + - name: twitter:description + content: Learn how to use the AlchemySigner.getAuthDetails method +--- + +# getAuthDetails + +The `getAuthDetails` method is used to get the details of the currently authenticated user. This method will also use session storage to get the user's details if they are already authenticated. + +::: warning +This method throws if there is no authenticated user. +::: + +## Usage + +::: code-group + +```ts +import { signer } from "./signer"; + +const user = await signer.getAuthDetails(); +``` + +<<< @/snippets/signers/alchemy/signer.ts + +::: + +## Returns + +`Promise` -- on success returns a `User` object representing the authenticated user. diff --git a/site/packages/aa-alchemy/signer/getUser.md b/site/packages/aa-alchemy/signer/getUser.md new file mode 100644 index 0000000000..b129a5b69c --- /dev/null +++ b/site/packages/aa-alchemy/signer/getUser.md @@ -0,0 +1,41 @@ +--- +outline: deep +head: + - - meta + - property: og:title + content: Alchemy Signer • getUser + - - meta + - name: description + content: Learn how to use the AlchemySigner.getUser method + - - meta + - property: og:description + content: Learn how to use the AlchemySigner.getUser method + - - meta + - name: twitter:title + content: Alchemy Signer • getUser + - - meta + - name: twitter:description + content: Learn how to use the AlchemySigner.getUser method +--- + +# getUser + +The `getUser` method is used to look up if a user already exists for a given email address + +## Usage + +::: code-group + +```ts +import { signer } from "./signer"; + +const result = await signer.getUser("moldy@email.com"); +``` + +<<< @/snippets/signers/alchemy/signer.ts + +::: + +## Returns + +`Promise<{orgId: string} | null>` -- if a user exists, this will return an object with the orgId of the user. If no user exists, this will return `null`. diff --git a/site/packages/aa-alchemy/signer/overview.md b/site/packages/aa-alchemy/signer/overview.md new file mode 100644 index 0000000000..44f6789173 --- /dev/null +++ b/site/packages/aa-alchemy/signer/overview.md @@ -0,0 +1,51 @@ +--- +outline: deep +head: + - - meta + - property: og:title + content: Alchemy Signer • Overview + - - meta + - name: description + content: Learn how to get started with the Alchemy Signer + - - meta + - property: og:description + content: Learn how to get started with the Alchemy Signer + - - meta + - name: twitter:title + content: Alchemy Signer + - - meta + - name: twitter:description + content: Learn how to get started with the Alchemy Signer +--- + +# AlchemySigner + +The Alchemy Signer is a `SmartAccountSigner` that is powered by Alchemy's Signer Infrastructure. Using the Alchemy Signer, you can get started building embedded accounts with just an Alchemy API key! + +::: warning +The Alchemy Signer is currently still under development and is not yet available for public use. If you are interested in using the Alchemy Signer, please reach out to us at [account-abstraction@alchemy.com](mailto:account-abstraction@alchemy.com). +::: + +## Usage + +Once you've been granted access to the Alchemy Signer, getting started is really simple. Install the `@alchemy/aa-alchemy` package and initialize your signer: + +<<< @/snippets/signers/alchemy/signer.ts + +## Returns + +`AlchemySigner` -- an instance of the AlchemySigner that can be used as a signer on `SmartContractAccount` instances + +## Parameters + +`AlchemySignerParams` -- an object that contains the following properties: + +- `client: AlchemySignerClient | AlchemySignerClientParams` -- the underlying client to use for the signer. The `AlchemySignerClientParams` are defined as follows: + - `connection: ConnectionConfig` -- the api config to use for calling Alchemy's APIs. + - `iframeConfig: IframeConfig` -- the config to use for the iframe that will be used to interact with the signer. + - `iframeElementId?: string` -- the id of the iframe element that will be injected into the DOM [default: "turnkey-iframe"] + - `iframeContainerId: string` -- the id of the iframe container that you have injected into your DOM +- `sessionConfig?: SessionConfig` -- optional parameter used to configure user sessions + - `sessionKey?: string` -- the key that the session will be stored to in your chosen storage [default: "alchemy-signer-session"] + - `storage?: "localStorage" | "sessionStorage"` -- the storage to use for the session [default: "localStorage"] + - `expirationTimeMs?: number` -- the time in milliseconds that the session will be valid for [default: 15 minutes] diff --git a/site/packages/aa-alchemy/signer/signMessage.md b/site/packages/aa-alchemy/signer/signMessage.md new file mode 100644 index 0000000000..f6cbf372a5 --- /dev/null +++ b/site/packages/aa-alchemy/signer/signMessage.md @@ -0,0 +1,50 @@ +--- +outline: deep +head: + - - meta + - property: og:title + content: Alchemy Signer • signMessage + - - meta + - name: description + content: Learn how to use the AlchemySigner.signMessage method + - - meta + - property: og:description + content: Learn how to use the AlchemySigner.signMessage method + - - meta + - name: twitter:title + content: Alchemy Signer • signMessage + - - meta + - name: twitter:description + content: Learn how to use the AlchemySigner.signMessage method +--- + +# signMessage + +The `signMessage` method is used to sign a message with the Alchemy Signer. + +::: warning +This method throws if there is no authenticated user. +::: + +## Usage + +::: code-group + +```ts +import { signer } from "./signer"; + +const message = "Hello, world!"; +const signature = await signer.signMessage(message); +``` + +<<< @/snippets/signers/alchemy/signer.ts + +::: + +## Returns + +`Promise` -- on success returns the signature of the message. + +## Parameters + +`message: string | Uint8Array` -- the message to sign diff --git a/site/packages/aa-alchemy/signer/signTypedData.md b/site/packages/aa-alchemy/signer/signTypedData.md new file mode 100644 index 0000000000..8a45e8bdbb --- /dev/null +++ b/site/packages/aa-alchemy/signer/signTypedData.md @@ -0,0 +1,49 @@ +--- +outline: deep +head: + - - meta + - property: og:title + content: Alchemy Signer • signTypedData + - - meta + - name: description + content: Learn how to use the AlchemySigner.signTypedData method + - - meta + - property: og:description + content: Learn how to use the AlchemySigner.signTypedData method + - - meta + - name: twitter:title + content: Alchemy Signer • signTypedData + - - meta + - name: twitter:description + content: Learn how to use the AlchemySigner.signTypedData method +--- + +# signTypedData + +The `signTypedData` method is used to sign typed data with the Alchemy Signer. + +::: warning +This method throws if there is no authenticated user. +::: + +## Usage + +::: code-group + +```ts +import { signer } from "./signer"; + +const signature = await signer.signTypedData(typedDataParams); +``` + +<<< @/snippets/signers/alchemy/signer.ts + +::: + +## Returns + +`Promise` -- on success returns the signature of the message. + +## Parameters + +`params: TypedDataDefinition` -- the typed data definition of the message you want to sign diff --git a/site/signers/alchemy-signer.md b/site/signers/alchemy-signer.md new file mode 100644 index 0000000000..79536276bb --- /dev/null +++ b/site/signers/alchemy-signer.md @@ -0,0 +1,72 @@ +--- +outline: deep +head: + - - meta + - property: og:title + content: Alchemy Signer + - - meta + - name: description + content: Learn how to get started with the Alchemy Signer + - - meta + - property: og:description + content: Learn how to get started with the Alchemy Signer + - - meta + - name: twitter:title + content: Alchemy Signer + - - meta + - name: twitter:description + content: Learn how to get started with the Alchemy Signer +--- + +# Alchemy Signer + +The Alchemy Signer is a `SmartAccountSigner` that is powered by Alchemy's Signer Infrastructure. Using the Alchemy Signer, you can get started building embedded accounts with just an Alchemy API key! + +::: warning +The Alchemy Signer is currently still under development and is not yet available for public use. If you are interested in using the Alchemy Signer, please reach out to us at [account-abstraction@alchemy.com](mailto:account-abstraction@alchemy.com). +::: + +## Usage + +Once you've been granted access to the Alchemy Signer, getting started is really simple. Install the `@alchemy/aa-alchemy` package and initialize your signer: + +<<< @/snippets/signers/alchemy/signer.ts + +For other configuration options, see the [Alchemy Signer API Reference](/packages/aa-alchemy/signer/overview). + +## Logging Users in + +Once you've initialized your signer, you can now enable your users to create an account or login to their existing account. + +::: warning +In the coming weeks, the OTP based email auth flow will be replaced with a magic link flow that will make this easier +::: + +::: code-group + +<<< @/snippets/signers/alchemy/SignupLoginComponent.tsx + +<<< @/snippets/signers/alchemy/usePromise.ts +::: + +Once your signer is authenticated with a user, you can use it to sign User Operations by creating a `SmartContractAccount` and passing the signer to it. + +## Leveraging Persistent Sessions + +By default the `AlchemySigner` leverages `localStorage` to cache user sessions for 15 minutes. This can be configured by passing in a `sessionConfig` to your `AlchemySigner` constructor. + +You can check if a session exists by doing the following: +::: code-group + +```ts +import { signer } from "./signer"; + +// NOTE: this method throws if there is no authenticated user +// so we return null in the case of an error +const user = await signer.getAuthDetails().catch(() => null); +``` + +<<< @/snippets/signers/alchemy/signer.ts +::: + +If there is an existing session, then your signer is ready for use! If not, see the section above for logging users in. diff --git a/site/signers/choosing-a-signer.md b/site/signers/choosing-a-signer.md index 5224a4c5d0..1eef583a9a 100644 --- a/site/signers/choosing-a-signer.md +++ b/site/signers/choosing-a-signer.md @@ -16,12 +16,6 @@ head: - - meta - name: twitter:description content: Explore Account Kit integration guides for signers including Magic.Link, Privy, Web3Auth, EOAs, and many more! -prev: - text: Smart Accounts - link: /smart-accounts/ -next: - text: How to send a User Operation - link: /using-smart-accounts/send-user-operations --- # What is a Signer? @@ -98,6 +92,7 @@ An improvement on SSSS is Threshold Signature Scheme (TSS). In this model, the k TSS is safer than SSSS because is possible to create the initial shares without ever constructing the original key on any one device. However, the tradeoff is that signing requires a Peer-to-Peer exchange which introduces latency. +You can read more about the difference between TSS and SSSS [here](https://www.dynamic.xyz/blog/the-evolution-of-multi-signature-and-multi-party-computation). You can read more about the difference between TSS and SSSS [here](https://www.dynamic.xyz/blog/the-evolution-of-multi-signature-and-multi-party-computation). ::: diff --git a/site/snippets/signers/alchemy/SignupLoginComponent.tsx b/site/snippets/signers/alchemy/SignupLoginComponent.tsx new file mode 100644 index 0000000000..1bb8896926 --- /dev/null +++ b/site/snippets/signers/alchemy/SignupLoginComponent.tsx @@ -0,0 +1,56 @@ +import { AlchemySigner } from "@alchemy/aa-alchemy"; +import { useMutation } from "@tanstack/react-query"; +import { useState } from "react"; +import { usePromise } from "./usePromise.js"; + +export const SignupLoginComponent = () => { + // The usePromise hook is a helpful utility that makes it easier to resolve user's input after + // a request has already been initiated + const { promise: bundle, resolve } = usePromise(); + const [email, setEmail] = useState(""); + const [bundleInput, setBundleInput] = useState(""); + + // It's recommended you wrap this in React Context or other state management + const signer = new AlchemySigner({ + client: { + connection: { + jwt: "alcht_", + }, + iframeConfig: { + iframeContainerId: "turnkey-iframe-container", + }, + }, + }); + + // we're using react-query to handle loading states more easily, but feel free to use w/e state management library you prefer + const { mutate: loginOrSignup, isLoading } = useMutation({ + mutationFn: (email: string) => + signer.authenticate({ type: "email", email, bundle }), + }); + + // The below view allows you to collect the email from the user and then the OTP bundle that they are emailed + return ( + <> + {!isLoading ? ( +
+ setEmail(e.target.value)} + /> + +
+ ) : ( +
+ setBundleInput(e.target.value)} + /> + +
+ )} +
+ + ); +}; diff --git a/site/snippets/signers/alchemy/signer.ts b/site/snippets/signers/alchemy/signer.ts new file mode 100644 index 0000000000..92a8aabfd0 --- /dev/null +++ b/site/snippets/signers/alchemy/signer.ts @@ -0,0 +1,14 @@ +import { AlchemySigner } from "@alchemy/aa-alchemy"; + +export const signer = new AlchemySigner({ + client: { + // This is created in your dashboard under `https://dashboard.alchemy.com/settings/access-keys` + // NOTE: it's not recommended to expose your API key on the client, instead proxy requests to your backend and set the `rpcUrl` + // here to point to your backend. + connection: { apiKey: "alcht_" }, + iframeConfig: { + // you will need to render a container with this id in your DOM + iframeContainerId: "turnkey-iframe-container", + }, + }, +}); diff --git a/site/snippets/signers/alchemy/usePromise.ts b/site/snippets/signers/alchemy/usePromise.ts new file mode 100644 index 0000000000..50cf49ea89 --- /dev/null +++ b/site/snippets/signers/alchemy/usePromise.ts @@ -0,0 +1,20 @@ +import { useState } from "react"; + +export const usePromise = () => { + const [resolve, setResolve] = useState<(value: T) => void>(); + const [reject, setReject] = useState<(reason?: any) => void>(); + + const [promise] = useState( + () => + new Promise((resolve, reject) => { + setResolve(() => resolve); + setReject(() => reject); + }) + ); + + return { + promise, + resolve: resolve!, + reject: reject!, + }; +};