-
Notifications
You must be signed in to change notification settings - Fork 133
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
34 changed files
with
875 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import { | ||
createLightAccount, | ||
createMultiOwnerModularAccount, | ||
type CreateLightAccountParams, | ||
type CreateMultiOwnerModularAccountParams, | ||
} from "@alchemy/aa-accounts"; | ||
import { custom } from "viem"; | ||
import { AlchemySignerStatus } from "../../signer/index.js"; | ||
import { ClientOnlyPropertyError } from "../errors.js"; | ||
import type { | ||
AlchemyAccountsConfig, | ||
SupportedAccountTypes, | ||
SupportedAccounts, | ||
} from "../types"; | ||
import { getSignerStatus } from "./getSignerStatus.js"; | ||
|
||
export type AccountConfig<TAccount extends SupportedAccountTypes> = | ||
TAccount extends "LightAccount" | ||
? Omit<CreateLightAccountParams, "signer" | "transport" | "chain"> | ||
: Omit< | ||
CreateMultiOwnerModularAccountParams, | ||
"signer" | "transport" | "chain" | ||
>; | ||
|
||
export type CreateAccountParams<TAccount extends SupportedAccountTypes> = { | ||
type: TAccount; | ||
params?: AccountConfig<TAccount>; | ||
}; | ||
|
||
export async function createAccount<TAccount extends SupportedAccountTypes>( | ||
{ type, params }: CreateAccountParams<TAccount>, | ||
config: AlchemyAccountsConfig | ||
): Promise<SupportedAccounts> { | ||
const clientStore = config.clientStore; | ||
if (!clientStore) { | ||
throw new ClientOnlyPropertyError("account"); | ||
} | ||
|
||
const transport = custom(config.bundlerClient); | ||
const chain = config.bundlerClient.chain; | ||
const signer = config.signer; | ||
const signerStatus = getSignerStatus(config); | ||
|
||
if (signerStatus !== AlchemySignerStatus.CONNECTED) { | ||
throw new Error("Signer not connected"); | ||
} | ||
|
||
const cachedAccount = clientStore.getState().accounts[type]; | ||
if (cachedAccount) { | ||
return cachedAccount.account; | ||
} | ||
|
||
const accountPromise = (() => { | ||
switch (type) { | ||
case "LightAccount": | ||
return createLightAccount({ | ||
...params, | ||
signer, | ||
transport: (opts) => transport({ ...opts, retryCount: 0 }), | ||
chain, | ||
}); | ||
case "MultiOwnerModularAccount": | ||
return createMultiOwnerModularAccount({ | ||
...params, | ||
signer, | ||
transport: (opts) => transport({ ...opts, retryCount: 0 }), | ||
chain, | ||
}); | ||
default: | ||
throw new Error("Unsupported account type"); | ||
} | ||
})(); | ||
|
||
clientStore.setState((state) => ({ | ||
accounts: { | ||
...state.accounts, | ||
[type]: { | ||
status: "INITIALIZING", | ||
account: accountPromise, | ||
}, | ||
}, | ||
})); | ||
|
||
const account = await accountPromise; | ||
clientStore.setState((state) => ({ | ||
accounts: { | ||
...state.accounts, | ||
[type]: { | ||
status: "READY", | ||
account, | ||
}, | ||
}, | ||
})); | ||
|
||
return accountPromise; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { ClientOnlyPropertyError } from "../errors.js"; | ||
import type { AccountState } from "../store/types.js"; | ||
import type { AlchemyAccountsConfig, SupportedAccountTypes } from "../types"; | ||
import { type CreateAccountParams } from "./createAccount.js"; | ||
|
||
export type GetAccountResult<TAccount extends SupportedAccountTypes> = | ||
| AccountState<TAccount> | ||
| undefined; | ||
|
||
export type GetAccountParams<TAccount extends SupportedAccountTypes> = | ||
CreateAccountParams<TAccount>; | ||
|
||
export const getAccount = <TAccount extends SupportedAccountTypes>( | ||
{ type }: GetAccountParams<TAccount>, | ||
config: AlchemyAccountsConfig | ||
): GetAccountResult<TAccount> => { | ||
const clientStore = config.clientStore; | ||
if (!clientStore) { | ||
throw new ClientOnlyPropertyError("account"); | ||
} | ||
|
||
return clientStore.getState().accounts[type]; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import type { ClientWithAlchemyMethods } from "../../client/types"; | ||
import type { AlchemyAccountsConfig } from "../types"; | ||
|
||
export const getBundlerClient = ( | ||
config: AlchemyAccountsConfig | ||
): ClientWithAlchemyMethods => { | ||
return config.coreStore.getState().bundlerClient; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import type { AlchemySigner } from "../../signer/signer.js"; | ||
import { ClientOnlyPropertyError } from "../errors.js"; | ||
import type { AlchemyAccountsConfig } from "../types"; | ||
|
||
export const getSigner = (config: AlchemyAccountsConfig): AlchemySigner => { | ||
if (config.clientStore == null) { | ||
throw new ClientOnlyPropertyError("signer"); | ||
} | ||
|
||
return config.clientStore.getState().signer; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { ClientOnlyPropertyError } from "../errors.js"; | ||
import type { AlchemyAccountsConfig } from "../types"; | ||
|
||
export const getSignerStatus = (config: AlchemyAccountsConfig) => { | ||
if (config.clientStore == null) { | ||
throw new ClientOnlyPropertyError("signerStatus"); | ||
} | ||
|
||
return config.clientStore.getState().signerStatus; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { ClientOnlyPropertyError } from "../errors.js"; | ||
import type { AlchemyAccountsConfig, SupportedAccountTypes } from "../types"; | ||
import type { GetAccountResult } from "./getAccount"; | ||
|
||
export const watchAccount = | ||
<TAccount extends SupportedAccountTypes>( | ||
type: TAccount, | ||
config: AlchemyAccountsConfig | ||
) => | ||
(onChange: (account: GetAccountResult<TAccount>) => void) => { | ||
if (config.clientStore == null) { | ||
throw new ClientOnlyPropertyError("account"); | ||
} | ||
|
||
return config.clientStore.subscribe( | ||
({ accounts }) => accounts[type], | ||
onChange, | ||
{ | ||
fireImmediately: true, | ||
equalityFn(a, b) { | ||
return ( | ||
a?.status === b?.status || | ||
(a?.status === "READY" && | ||
b?.status === "READY" && | ||
a.account.address === b.account.address) | ||
); | ||
}, | ||
} | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import type { ClientWithAlchemyMethods } from "../../client/types"; | ||
import type { AlchemyAccountsConfig } from "../types"; | ||
|
||
export const watchBundlerClient = | ||
(config: AlchemyAccountsConfig) => | ||
(onChange: (bundlerClient: ClientWithAlchemyMethods) => void) => { | ||
return config.coreStore.subscribe( | ||
({ bundlerClient }) => bundlerClient, | ||
onChange, | ||
{ fireImmediately: true } | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import type { AlchemySigner } from "../../signer"; | ||
import { ClientOnlyPropertyError } from "../errors.js"; | ||
import type { AlchemyAccountsConfig } from "../types"; | ||
|
||
export const watchSigner = | ||
(config: AlchemyAccountsConfig) => | ||
(onChange: (signer: AlchemySigner) => void) => { | ||
if (config.clientStore == null) { | ||
throw new ClientOnlyPropertyError("signer"); | ||
} | ||
|
||
return config.clientStore.subscribe(({ signer }) => signer, onChange, { | ||
fireImmediately: true, | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import type { AlchemySignerStatus } from "../../signer/types.js"; | ||
import { ClientOnlyPropertyError } from "../errors.js"; | ||
import type { AlchemyAccountsConfig } from "../types"; | ||
|
||
export const watchSignerStatus = | ||
(config: AlchemyAccountsConfig) => | ||
(onChange: (status: AlchemySignerStatus) => void) => { | ||
if (config.clientStore == null) { | ||
throw new ClientOnlyPropertyError("signerStatus"); | ||
} | ||
|
||
return config.clientStore.subscribe( | ||
({ signerStatus }) => signerStatus, | ||
onChange, | ||
{ fireImmediately: true } | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import type { User } from "../../signer"; | ||
import { ClientOnlyPropertyError } from "../errors.js"; | ||
import type { AlchemyAccountsConfig } from "../types"; | ||
|
||
export const watchUser = | ||
(config: AlchemyAccountsConfig) => (onChange: (user?: User) => void) => { | ||
if (config.clientStore == null) { | ||
throw new ClientOnlyPropertyError("user"); | ||
} | ||
|
||
return config.clientStore.subscribe(({ user }) => user, onChange, { | ||
fireImmediately: true, | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
"use client"; | ||
|
||
import { ConnectionConfigSchema } from "@alchemy/aa-core"; | ||
import { getBundlerClient } from "./actions/getBundlerClient.js"; | ||
import { getSigner } from "./actions/getSigner.js"; | ||
import { createClientStore } from "./store/client.js"; | ||
import { createCoreStore } from "./store/core.js"; | ||
import type { AlchemyAccountsConfig, CreateConfigProps } from "./types"; | ||
|
||
export const DEFAULT_IFRAME_CONTAINER_ID = "alchemy-signer-iframe-container"; | ||
|
||
export const createConfig = ({ | ||
chain, | ||
iframeConfig, | ||
rootOrgId, | ||
rpId, | ||
sessionConfig, | ||
signerConnection, | ||
...connectionConfig | ||
}: CreateConfigProps): AlchemyAccountsConfig => { | ||
const connection = ConnectionConfigSchema.parse(connectionConfig); | ||
|
||
const config: AlchemyAccountsConfig = { | ||
coreStore: createCoreStore({ connection, chain }), | ||
clientStore: createClientStore({ | ||
client: { | ||
connection: signerConnection ?? connection, | ||
iframeConfig, | ||
rootOrgId, | ||
rpId, | ||
}, | ||
sessionConfig, | ||
}), | ||
// these are just here for convenience right now, but you can do all of this with actions on the stores as well | ||
get bundlerClient() { | ||
return getBundlerClient(config); | ||
}, | ||
get signer() { | ||
return getSigner(config); | ||
}, | ||
}; | ||
|
||
return config; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { BaseError } from "../errors/base.js"; | ||
|
||
export class ClientOnlyPropertyError extends BaseError { | ||
name: string = "ClientOnlyPropertyError"; | ||
|
||
constructor(property: string) { | ||
super(`${property} is only available on the client`); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { DEFAULT_IFRAME_CONTAINER_ID, createConfig } from "./createConfig.js"; | ||
export type * from "./types.js"; |
Oops, something went wrong.