-
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.
feat: add alchemy accounts context (#539)
* feat: add alchemy accounts context * style: address PR feedback Co-authored-by: Ajay Vasisht <43521356+avasisht23@users.noreply.github.com> * fix: address a few PR comments * docs(hooks): add initial react hooks docs (#561) * docs(hooks): add initial react hooks docs * refactor: apply suggestions from code review Co-authored-by: Ajay Vasisht <43521356+avasisht23@users.noreply.github.com> --------- Co-authored-by: Ajay Vasisht <43521356+avasisht23@users.noreply.github.com> --------- Co-authored-by: Ajay Vasisht <43521356+avasisht23@users.noreply.github.com>
- Loading branch information
1 parent
12c02e9
commit f92469e
Showing
58 changed files
with
1,782 additions
and
8 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
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
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,107 @@ | ||
import { | ||
createLightAccount, | ||
createMultiOwnerModularAccount, | ||
type CreateLightAccountParams, | ||
type CreateMultiOwnerModularAccountParams, | ||
} from "@alchemy/aa-accounts"; | ||
import { custom } from "viem"; | ||
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; | ||
accountParams?: AccountConfig<TAccount>; | ||
}; | ||
|
||
export async function createAccount<TAccount extends SupportedAccountTypes>( | ||
{ type, accountParams: 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.isConnected) { | ||
throw new Error("Signer not connected"); | ||
} | ||
|
||
const cachedAccount = clientStore.getState().accounts[type]; | ||
if (cachedAccount?.account) { | ||
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, | ||
}, | ||
}, | ||
})); | ||
|
||
try { | ||
const account = await accountPromise; | ||
clientStore.setState((state) => ({ | ||
accounts: { | ||
...state.accounts, | ||
[type]: { | ||
status: "READY", | ||
account, | ||
}, | ||
}, | ||
})); | ||
} catch (error) { | ||
clientStore.setState((state) => ({ | ||
accounts: { | ||
...state.accounts, | ||
[type]: { | ||
status: "ERROR", | ||
error, | ||
}, | ||
}, | ||
})); | ||
} | ||
|
||
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,22 @@ | ||
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>; | ||
|
||
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,10 @@ | ||
import { ClientOnlyPropertyError } from "../errors.js"; | ||
import type { AlchemyAccountsConfig } from "../types"; | ||
|
||
export const getUser = (config: AlchemyAccountsConfig) => { | ||
if (config.clientStore == null) { | ||
throw new ClientOnlyPropertyError("user"); | ||
} | ||
|
||
return config.clientStore.getState().user; | ||
}; |
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,27 @@ | ||
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 === "READY" && b?.status === "READY" | ||
? a.account.address === b.account.address | ||
: a?.status === b?.status; | ||
}, | ||
} | ||
); | ||
}; |
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 { ClientOnlyPropertyError } from "../errors.js"; | ||
import type { SignerStatus } from "../store/types.js"; | ||
import type { AlchemyAccountsConfig } from "../types"; | ||
|
||
export const watchSignerStatus = | ||
(config: AlchemyAccountsConfig) => | ||
(onChange: (status: SignerStatus) => 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, | ||
}); | ||
}; |
Oops, something went wrong.