UNPKG

@lukasrosario/account

Version:

Base Account SDK

189 lines (164 loc) 6.05 kB
import { AppMetadata, ConstructorOptions, Preference, ProviderInterface, SubAccountOptions, } from ':core/provider/interface.js'; import { AddSubAccountAccount } from ':core/rpc/wallet_addSubAccount.js'; import { WalletConnectResponse } from ':core/rpc/wallet_connect.js'; import { loadTelemetryScript } from ':core/telemetry/initCCA.js'; import { abi } from ':sign/base-account/utils/constants.js'; import { SubAccount, ToOwnerAccountFn, store } from ':store/store.js'; import { assertPresence } from ':util/assertPresence.js'; import { checkCrossOriginOpenerPolicy } from ':util/checkCrossOriginOpenerPolicy.js'; import { validatePreferences, validateSubAccount } from ':util/validatePreferences.js'; import { decodeAbiParameters, encodeFunctionData, toHex } from 'viem'; import { BaseAccountProvider } from './BaseAccountProvider.js'; import { getInjectedProvider } from './getInjectedProvider.js'; export type CreateProviderOptions = Partial<AppMetadata> & { preference?: Preference; subAccounts?: Omit<SubAccountOptions, 'enableAutoSubAccounts'>; paymasterUrls?: Record<number, string>; }; /** * Create Base AccountSDK instance with EIP-1193 compliant provider * @param params - Options to create a base account SDK instance. * @returns An SDK object with a getProvider method that returns an EIP-1193 compliant provider. */ export function createBaseAccountSDK(params: CreateProviderOptions) { const options: ConstructorOptions = { metadata: { appName: params.appName || 'App', appLogoUrl: params.appLogoUrl || '', appChainIds: params.appChainIds || [], }, preference: params.preference ?? {}, paymasterUrls: params.paymasterUrls, }; // ==================================================================== // If we have a toOwnerAccount function, set it in the non-persisted config // ==================================================================== if (params.subAccounts?.toOwnerAccount) { validateSubAccount(params.subAccounts.toOwnerAccount); } store.subAccountsConfig.set({ toOwnerAccount: params.subAccounts?.toOwnerAccount, // @ts-expect-error - enableSubAccounts is not officially supported yet enableAutoSubAccounts: params.subAccounts?.enableAutoSubAccounts, }); // ==================================================================== // Set the options in the store and rehydrate the store from storage // ==================================================================== store.config.set(options); void store.persist.rehydrate(); // ==================================================================== // Validation and telemetry // ==================================================================== void checkCrossOriginOpenerPolicy(); validatePreferences(options.preference); if (options.preference.telemetry !== false) { void loadTelemetryScript(); } // ==================================================================== // Return the provider // ==================================================================== let provider: ProviderInterface | null = null; const sdk = { getProvider: () => { if (!provider) { provider = getInjectedProvider() ?? new BaseAccountProvider(options); } return provider; }, subAccount: { async create(accountParam: AddSubAccountAccount): Promise<SubAccount> { return (await sdk.getProvider()?.request({ method: 'wallet_addSubAccount', params: [ { version: '1', account: accountParam, }, ], })) as SubAccount; }, async get(): Promise<SubAccount | null> { const subAccount = store.subAccounts.get(); if (subAccount?.address) { return subAccount; } const response = (await sdk.getProvider()?.request({ method: 'wallet_connect', params: [ { version: '1', capabilities: {}, }, ], })) as WalletConnectResponse; const subAccounts = response.accounts[0].capabilities?.subAccounts; if (!Array.isArray(subAccounts)) { return null; } return subAccounts[0] as SubAccount; }, addOwner: async ({ address, publicKey, chainId, }: { address?: `0x${string}`; publicKey?: `0x${string}`; chainId: number; }) => { const subAccount = store.subAccounts.get(); const account = store.account.get(); assertPresence(account, new Error('account does not exist')); assertPresence(subAccount?.address, new Error('subaccount does not exist')); const calls = []; if (publicKey) { const [x, y] = decodeAbiParameters([{ type: 'bytes32' }, { type: 'bytes32' }], publicKey); calls.push({ to: subAccount.address, data: encodeFunctionData({ abi, functionName: 'addOwnerPublicKey', args: [x, y] as const, }), value: toHex(0), }); } if (address) { calls.push({ to: subAccount.address, data: encodeFunctionData({ abi, functionName: 'addOwnerAddress', args: [address] as const, }), value: toHex(0), }); } return (await sdk.getProvider()?.request({ method: 'wallet_sendCalls', params: [ { calls, chainId: toHex(chainId), from: account.accounts?.[0], version: '1', }, ], })) as string; }, setToOwnerAccount(toSubAccountOwner: ToOwnerAccountFn): void { validateSubAccount(toSubAccountOwner); store.subAccountsConfig.set({ toOwnerAccount: toSubAccountOwner, }); }, }, }; return sdk; }