diff --git a/package.json b/package.json index 2c8e4267a..69ada0ec7 100644 --- a/package.json +++ b/package.json @@ -113,6 +113,7 @@ }, "resolutions": { "@solana/web3.js": "1.98.0", - "rpc-websockets": "9.0.4" + "rpc-websockets": "9.0.4", + "next": "file:./etc/noop" } } diff --git a/packages/exceptions/src/exceptions/types/codes.ts b/packages/exceptions/src/exceptions/types/codes.ts index 8d79f59e4..f81fbcfb1 100644 --- a/packages/exceptions/src/exceptions/types/codes.ts +++ b/packages/exceptions/src/exceptions/types/codes.ts @@ -817,4 +817,5 @@ export type ErrorContextCode = | ChainStakingErrorCodes | ChainWasmErrorCodes | ErrorCode + | number | typeof UnspecifiedErrorCode diff --git a/packages/sdk-ts/src/utils/address.spec.ts b/packages/sdk-ts/src/utils/address.spec.ts index 1a9f5e95a..d22e0fc9f 100644 --- a/packages/sdk-ts/src/utils/address.spec.ts +++ b/packages/sdk-ts/src/utils/address.spec.ts @@ -2,6 +2,7 @@ import { getEthereumAddress, getInjectiveAddress, getDefaultSubaccountId, + getChecksumAddress, } from '../../src/utils/index.js' describe('address helper functions', () => { @@ -31,4 +32,13 @@ describe('address helper functions', () => { '0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000', ) }) + + it('getChecksumAddress returns correct value', () => { + const ethereumAddress = + '0xaf79152ac5df276d9a8e1e2e22822f9713474902'.toLowerCase() + + expect(getChecksumAddress(ethereumAddress)).toMatch( + '0xAF79152AC5dF276D9A8e1E2E22822f9713474902', + ) + }) }) diff --git a/packages/utils/src/classes/HttpRestClient.ts b/packages/utils/src/classes/HttpRestClient.ts index 863a3f896..225270818 100644 --- a/packages/utils/src/classes/HttpRestClient.ts +++ b/packages/utils/src/classes/HttpRestClient.ts @@ -13,7 +13,9 @@ const getErrorMessage = (error: any, endpoint: string): string => { } return error.response.data - ? error.response.data.message || error.response.data + ? error.response.data.message || + error.response.data.statusMessage || + error.response.data : error.response.statusText } diff --git a/packages/wallets/wallet-base/src/base.ts b/packages/wallets/wallet-base/src/base.ts index fad3cfe6b..b2fe8a42a 100644 --- a/packages/wallets/wallet-base/src/base.ts +++ b/packages/wallets/wallet-base/src/base.ts @@ -1,5 +1,10 @@ -import { ChainId, CosmosChainId, EthereumChainId } from '@injectivelabs/ts-types' import { + ChainId, + CosmosChainId, + EthereumChainId, +} from '@injectivelabs/ts-types' +import { + WalletMetadata, WalletEventListener, ConcreteWalletStrategyArgs, ConcreteCosmosWalletStrategyArgs, @@ -13,10 +18,19 @@ export default abstract class BaseConcreteStrategy { protected listeners: Partial> = {} - protected constructor(args: ConcreteWalletStrategyArgs | ConcreteEthereumWalletStrategyArgs | ConcreteCosmosWalletStrategyArgs) { - this.ethereumChainId = 'ethereumOptions' in args && args.ethereumOptions - ? args.ethereumOptions.ethereumChainId - : undefined + public metadata?: WalletMetadata + + public constructor( + args: + | ConcreteWalletStrategyArgs + | ConcreteEthereumWalletStrategyArgs + | ConcreteCosmosWalletStrategyArgs, + ) { + this.ethereumChainId = + 'ethereumOptions' in args && args.ethereumOptions + ? args.ethereumOptions.ethereumChainId + : undefined this.chainId = args.chainId + this.metadata = args.metadata } } diff --git a/packages/wallets/wallet-base/src/types/enums.ts b/packages/wallets/wallet-base/src/types/enums.ts index 641dd2222..c675d4dbf 100644 --- a/packages/wallets/wallet-base/src/types/enums.ts +++ b/packages/wallets/wallet-base/src/types/enums.ts @@ -11,6 +11,8 @@ export enum Wallet { Phantom = 'phantom', Metamask = 'metamask', OkxWallet = 'okx-wallet', + TurnkeyOtp = 'turnkey-otp', + TurnkeyOauth = 'turnkey-oauth', TrustWallet = 'trust-wallet', PrivateKey = 'private-key', TrezorBip32 = 'trezor-bip32', diff --git a/packages/wallets/wallet-base/src/types/strategy.ts b/packages/wallets/wallet-base/src/types/strategy.ts index 1a0eeb328..e32ca8a3b 100644 --- a/packages/wallets/wallet-base/src/types/strategy.ts +++ b/packages/wallets/wallet-base/src/types/strategy.ts @@ -1,6 +1,5 @@ import { ChainId, - CosmosChainId, AccountAddress, EthereumChainId, } from '@injectivelabs/ts-types' @@ -30,10 +29,13 @@ export type MagicMetadata = { rpcEndpoint?: string } -export type WalletConnectMetadata = Record< - string, - string | Record | Record -> +export type PrivateKeyMetadata = { + privateKey: string +} + +export type WalletConnectMetadata = { + projectId?: string +} export interface WalletStrategyEthereumOptions { ethereumChainId: EthereumChainId @@ -51,20 +53,57 @@ export interface SendTransactionOptions { } } -export interface ConcreteWalletStrategyOptions { - privateKey?: string - metadata?: Record> +export enum TurnkeyProvider { + Email = 'email', + Google = 'google', + Apple = 'apple', +} + +export type TurnkeySession = { + sessionType: any + userId: string + organizationId: string + expiry: number + token: string +} + +export interface TurnkeyMetadata { + defaultOrganizationId: string + apiBaseUrl: string + apiServerEndpoint: string + iframeUrl?: string + email?: string + session?: TurnkeySession + otpId?: string + otpCode?: string + oidcToken?: string + iframeElementId?: string + iframeContainerId: string + credentialBundle?: string + organizationId?: string + provider?: TurnkeyProvider + otpInitPath?: string + otpVerifyPath?: string + oauthLoginPath?: string + googleClientId?: string + googleRedirectUri?: string +} + +export interface WalletMetadata { + magic?: MagicMetadata + turnkey?: Partial + walletConnect?: WalletConnectMetadata + privateKey?: PrivateKeyMetadata } export interface ConcreteWalletStrategyArgs { chainId: ChainId - options?: ConcreteWalletStrategyOptions + metadata?: WalletMetadata } -export interface ConcreteCosmosWalletStrategyArgs { - chainId: CosmosChainId | ChainId +export interface ConcreteCosmosWalletStrategyArgs + extends ConcreteWalletStrategyArgs { wallet?: Wallet - options?: ConcreteWalletStrategyOptions } export interface ConcreteEthereumWalletStrategyArgs @@ -73,6 +112,9 @@ export interface ConcreteEthereumWalletStrategyArgs } export interface ConcreteCosmosWalletStrategy { + metadata?: WalletMetadata + + setMetadata?(metadata?: WalletMetadata): void /** * The accounts from the wallet (addresses) */ @@ -125,7 +167,7 @@ export type ConcreteStrategiesArg = { export interface WalletStrategyArguments { chainId: ChainId - options?: ConcreteWalletStrategyOptions + metadata?: WalletMetadata ethereumOptions?: WalletStrategyEthereumOptions disabledWallets?: Wallet[] wallet?: Wallet @@ -135,9 +177,7 @@ export interface WalletStrategyArguments { export interface ConcreteWalletStrategy extends Omit< ConcreteCosmosWalletStrategy, - | 'sendTransaction' - | 'isChainIdSupported' - | 'signAminoTransaction' + 'sendTransaction' | 'isChainIdSupported' | 'signAminoTransaction' > { /** * Sends Cosmos transaction. Returns a transaction hash @@ -224,10 +264,11 @@ export interface WalletStrategy { strategies: ConcreteStrategiesArg wallet: Wallet args: WalletStrategyArguments + metadata?: WalletMetadata getWallet(): Wallet setWallet(wallet: Wallet): void - setOptions(options?: ConcreteWalletStrategyOptions): void + setMetadata(metadata?: WalletMetadata): void getStrategy(): ConcreteWalletStrategy getAddresses(args?: unknown): Promise getWalletDeviceType(): Promise @@ -269,4 +310,4 @@ export interface WalletStrategy { getCosmosWallet?(chainId: ChainId): CosmosWalletAbstraction } -export { StdSignDoc} +export { StdSignDoc } diff --git a/packages/wallets/wallet-base/src/utils/wallet.ts b/packages/wallets/wallet-base/src/utils/wallet.ts index c9f7186d0..19c00f4f0 100644 --- a/packages/wallets/wallet-base/src/utils/wallet.ts +++ b/packages/wallets/wallet-base/src/utils/wallet.ts @@ -5,12 +5,14 @@ export const isEvmWallet = (wallet: Wallet): boolean => Wallet.Magic, Wallet.BitGet, Wallet.Ledger, - Wallet.TrezorBip32, - Wallet.TrezorBip44, Wallet.Phantom, Wallet.Metamask, Wallet.OkxWallet, Wallet.PrivateKey, + Wallet.TrezorBip32, + Wallet.TurnkeyOtp, + Wallet.TurnkeyOauth, + Wallet.TrezorBip44, Wallet.TrustWallet, Wallet.LedgerLegacy, Wallet.WalletConnect, @@ -19,14 +21,16 @@ export const isEvmWallet = (wallet: Wallet): boolean => export const isCosmosWallet = (wallet: Wallet): boolean => !isEvmWallet(wallet) -export const isEvmBrowserWallet = (wallet: Wallet) => [ - Wallet.BitGet, - Wallet.Phantom, - Wallet.Metamask, - Wallet.OkxWallet, - Wallet.TrustWallet, -].includes(wallet) - +export const isEvmBrowserWallet = (wallet: Wallet) => + [ + Wallet.BitGet, + Wallet.Phantom, + Wallet.Metamask, + Wallet.OkxWallet, + Wallet.TrustWallet, + Wallet.TurnkeyOtp, + Wallet.TurnkeyOauth, + ].includes(wallet) export const isCosmosBrowserWallet = (wallet: Wallet): boolean => [ diff --git a/packages/wallets/wallet-core/src/strategy/BaseWalletStrategy.ts b/packages/wallets/wallet-core/src/strategy/BaseWalletStrategy.ts index 002864bf3..d4956c065 100644 --- a/packages/wallets/wallet-core/src/strategy/BaseWalletStrategy.ts +++ b/packages/wallets/wallet-core/src/strategy/BaseWalletStrategy.ts @@ -18,11 +18,11 @@ import { ConcreteStrategiesArg, SendTransactionOptions, ConcreteWalletStrategy, + type WalletMetadata, onAccountChangeCallback, onChainIdChangeCallback, WalletStrategyArguments, CosmosWalletAbstraction, - ConcreteWalletStrategyOptions, WalletStrategy as WalletStrategyInterface, } from '@injectivelabs/wallet-base' import { StdSignDoc } from '@keplr-wallet/types' @@ -58,12 +58,15 @@ export default class BaseWalletStrategy implements WalletStrategyInterface { public args: WalletStrategyArguments + public metadata?: WalletMetadata + public wallets?: Wallet[] constructor(args: WalletStrategyArguments) { this.args = args this.strategies = args.strategies this.wallet = getInitialWallet(args) + this.metadata = args.metadata } public getWallet(): Wallet { @@ -74,8 +77,13 @@ export default class BaseWalletStrategy implements WalletStrategyInterface { this.wallet = wallet } - public setOptions(_options?: ConcreteWalletStrategyOptions) { - // + /** + * When we use setMetadata, we are usually updating the metadata of the + * existing strategy. + */ + public setMetadata(metadata?: WalletMetadata) { + this.metadata = metadata + this.getStrategy().setMetadata?.(metadata) } public getStrategy(): ConcreteWalletStrategy { diff --git a/packages/wallets/wallet-cosmos/src/strategy/strategy.ts b/packages/wallets/wallet-cosmos/src/strategy/strategy.ts index 69885b4b0..7afcc25e7 100644 --- a/packages/wallets/wallet-cosmos/src/strategy/strategy.ts +++ b/packages/wallets/wallet-cosmos/src/strategy/strategy.ts @@ -41,6 +41,7 @@ export class CosmosWalletStrategy implements ConcreteWalletStrategy { public wallet: Wallet + private cosmosWallet: CosmosWallet constructor( @@ -49,7 +50,7 @@ export class CosmosWalletStrategy endpoints?: { rest: string; rpc: string } } & { wallet: Wallet }, ) { - super(args) + super({ ...args, chainId: args.chainId as ChainId }) if (!cosmosWallets.includes(args.wallet)) { throw new CosmosWalletException( @@ -60,7 +61,7 @@ export class CosmosWalletStrategy } this.wallet = args.wallet - this.chainId = args.chainId || CosmosChainId.Injective + this.chainId = args.chainId as ChainId this.cosmosWallet = new CosmosWallet({ wallet: args.wallet, chainId: args.chainId, diff --git a/packages/wallets/wallet-cosmostation/src/strategy/strategy.ts b/packages/wallets/wallet-cosmostation/src/strategy/strategy.ts index 6f3b724b3..3821e1bad 100644 --- a/packages/wallets/wallet-cosmostation/src/strategy/strategy.ts +++ b/packages/wallets/wallet-cosmostation/src/strategy/strategy.ts @@ -25,6 +25,7 @@ import { WalletDeviceType, BaseConcreteStrategy, ConcreteWalletStrategy, + type ConcreteCosmosWalletStrategyArgs, } from '@injectivelabs/wallet-base' import { CosmosTxV1Beta1Tx } from '@injectivelabs/sdk-ts' import { InstallError, Cosmos } from '@cosmostation/extension-client' @@ -62,7 +63,8 @@ export class Cosmostation public chainName: string constructor(args: { chainId: ChainId | CosmosChainId }) { - super(args) + super(args as ConcreteCosmosWalletStrategyArgs) + this.chainId = args.chainId || CosmosChainId.Injective this.chainName = getChainNameFromChainId(this.chainId) } @@ -84,12 +86,13 @@ export class Cosmostation return [accounts.address] } catch (e: unknown) { if ((e as any).code === 4001) { - throw new CosmosWalletException(new Error('The user rejected the request'), - { - code: UnspecifiedErrorCode, - context: WalletAction.GetAccounts, - }, - ) + throw new CosmosWalletException( + new Error('The user rejected the request'), + { + code: UnspecifiedErrorCode, + context: WalletAction.GetAccounts, + }, + ) } throw new CosmosWalletException(new Error((e as any).message), { @@ -113,7 +116,9 @@ export class Cosmostation _options: { address: AccountAddress; ethereumChainId: EthereumChainId }, ): Promise { throw new CosmosWalletException( - new Error('sendEthereumTransaction is not supported. Cosmostation only supports sending cosmos transactions'), + new Error( + 'sendEthereumTransaction is not supported. Cosmostation only supports sending cosmos transactions', + ), { code: UnspecifiedErrorCode, context: WalletAction.SendEthereumTransaction, @@ -222,10 +227,13 @@ export class Cosmostation return Buffer.from(account.publicKey).toString('base64') } catch (e: unknown) { if ((e as any).code === 4001) { - throw new CosmosWalletException(new Error('The user rejected the request'), { - code: UnspecifiedErrorCode, - context: WalletAction.GetAccounts, - }) + throw new CosmosWalletException( + new Error('The user rejected the request'), + { + code: UnspecifiedErrorCode, + context: WalletAction.GetAccounts, + }, + ) } throw new CosmosWalletException(new Error((e as any).message), { @@ -282,7 +290,9 @@ export class Cosmostation async getEthereumTransactionReceipt(_txHash: string): Promise { throw new CosmosWalletException( - new Error('getEthereumTransactionReceipt is not supported on Cosmostation'), + new Error( + 'getEthereumTransactionReceipt is not supported on Cosmostation', + ), { code: UnspecifiedErrorCode, type: ErrorType.WalletError, diff --git a/packages/wallets/wallet-magic/src/strategy/strategy.ts b/packages/wallets/wallet-magic/src/strategy/strategy.ts index bf1d5df48..984c38d7f 100644 --- a/packages/wallets/wallet-magic/src/strategy/strategy.ts +++ b/packages/wallets/wallet-magic/src/strategy/strategy.ts @@ -19,55 +19,21 @@ import { AccountAddress, EthereumChainId } from '@injectivelabs/ts-types' import { StdSignDoc, WalletAction, - MagicMetadata, MagicProvider, WalletDeviceType, BaseConcreteStrategy, BrowserEip1993Provider, ConcreteWalletStrategy, SendTransactionOptions, - WalletStrategyArguments, } from '@injectivelabs/wallet-base' -interface MagicConnectArgs extends WalletStrategyArguments { - metadata?: MagicMetadata -} - -export class Magic extends BaseConcreteStrategy implements ConcreteWalletStrategy { +export class Magic + extends BaseConcreteStrategy + implements ConcreteWalletStrategy +{ public provider: BrowserEip1993Provider | undefined - public metadata?: MagicMetadata - private magicWallet: MagicWallet - - constructor(args: MagicConnectArgs) { - if (!args.metadata?.apiKey) { - throw new WalletException( - new Error( - 'You have to pass the apiKey within metadata to use Magic wallet', - ), - ) - } - if (!args.metadata.rpcEndpoint) { - throw new WalletException( - new Error( - 'You have to pass the rpc url endpoint within metadata to use Magic wallet', - ), - ) - } - - super(args) - - this.metadata = args.metadata - this.magicWallet = new MagicWallet(args.metadata.apiKey, { - extensions: [ - new OAuthExtension(), - new CosmosExtension({ - rpcUrl: args.metadata.rpcEndpoint, - chain: 'inj', - }), - ], - }) as unknown as MagicWallet - } + private magicWallet: MagicWallet | undefined async getWalletDeviceType(): Promise { return Promise.resolve(WalletDeviceType.Browser) @@ -104,30 +70,36 @@ export class Magic extends BaseConcreteStrategy implements ConcreteWalletStrateg } async connectViaEmail(email?: string) { + const magicWallet = await this.getMagicWallet() + if (!email) { throw new WalletException( new Error('You have to pass the email for using Magic wallet'), ) } - return this.magicWallet.auth.loginWithMagicLink({ email }) + return magicWallet.auth.loginWithMagicLink({ email }) } async connectViaOauth(provider: MagicProvider) { - return (this.magicWallet.oauth2 as any).loginWithRedirect({ + const magicWallet = await this.getMagicWallet() + + return (magicWallet.oauth2 as any).loginWithRedirect({ provider: provider, redirectURI: window.location.origin, }) } public async disconnect() { - const isUserLoggedIn = await this.magicWallet.user.isLoggedIn() + const magicWallet = await this.getMagicWallet() + + const isUserLoggedIn = await magicWallet.user.isLoggedIn() if (!isUserLoggedIn) { return } - await this.magicWallet.user.logout() + await magicWallet.user.logout() } async getAddresses({ @@ -135,21 +107,21 @@ export class Magic extends BaseConcreteStrategy implements ConcreteWalletStrateg }: { provider: MagicProvider }): Promise { + const magicWallet = await this.getMagicWallet() + if (!provider) { try { - await (this.magicWallet.oauth2 as any).getRedirectResult() + await (magicWallet.oauth2 as any).getRedirectResult() } catch { // fail silently } } try { - const { publicAddress } = await this.magicWallet.user.getInfo() + const { publicAddress } = await magicWallet.user.getInfo() if (!publicAddress?.startsWith('inj')) { - const address = await (this.magicWallet.cosmos as any).changeAddress( - 'inj', - ) + const address = await (magicWallet.cosmos as any).changeAddress('inj') return [address || ''] } @@ -220,7 +192,9 @@ export class Magic extends BaseConcreteStrategy implements ConcreteWalletStrateg eip712json: string, _address: AccountAddress, ): Promise { - const signature = await (this.magicWallet.cosmos as any).signTypedData( + const magicWallet = await this.getMagicWallet() + + const signature = await (magicWallet.cosmos as any).signTypedData( eip712json, ) @@ -303,11 +277,12 @@ export class Magic extends BaseConcreteStrategy implements ConcreteWalletStrateg } private async pollUserLoggedInState(timeout = 60 * 1000): Promise { + const magicWallet = await this.getMagicWallet() const POLL_INTERVAL = 3 * 1000 for (let i = 0; i <= timeout / POLL_INTERVAL; i += 1) { try { - const result = await this.magicWallet.user.isLoggedIn() + const result = await magicWallet.user.isLoggedIn() if (result) { return result @@ -331,4 +306,38 @@ export class Magic extends BaseConcreteStrategy implements ConcreteWalletStrateg }, ) } + + private async getMagicWallet(): Promise { + const { metadata } = this + + if (!this.magicWallet) { + if (!metadata?.magic?.apiKey) { + throw new WalletException( + new Error( + 'You have to pass the apiKey within metadata to use Magic wallet', + ), + ) + } + + if (!metadata?.magic?.rpcEndpoint) { + throw new WalletException( + new Error( + 'You have to pass the rpc url endpoint within metadata to use Magic wallet', + ), + ) + } + + this.magicWallet = new MagicWallet(metadata.magic.apiKey, { + extensions: [ + new OAuthExtension(), + new CosmosExtension({ + rpcUrl: metadata.magic.rpcEndpoint, + chain: 'inj', + }), + ], + }) as unknown as MagicWallet + } + + return this.magicWallet + } } diff --git a/packages/wallets/wallet-private-key/src/strategy/strategy.ts b/packages/wallets/wallet-private-key/src/strategy/strategy.ts index 730a8c2ce..b60c8beb9 100644 --- a/packages/wallets/wallet-private-key/src/strategy/strategy.ts +++ b/packages/wallets/wallet-private-key/src/strategy/strategy.ts @@ -24,28 +24,15 @@ import { BaseConcreteStrategy, ConcreteWalletStrategy, SendTransactionOptions, - ConcreteEthereumWalletStrategyArgs, } from '@injectivelabs/wallet-base' import { TxRaw, toUtf8, TxGrpcApi, TxResponse } from '@injectivelabs/sdk-ts' -interface PrivateKeyArgs extends ConcreteEthereumWalletStrategyArgs { - privateKey?: string -} - export class PrivateKeyWallet extends BaseConcreteStrategy implements ConcreteWalletStrategy { private privateKey?: PrivateKeySigner | undefined - constructor(args: PrivateKeyArgs) { - super(args) - - this.privateKey = args.privateKey - ? PrivateKeySigner.fromHex(args.privateKey) - : undefined - } - async getWalletDeviceType(): Promise { return Promise.resolve(WalletDeviceType.Other) } @@ -254,13 +241,19 @@ export class PrivateKeyWallet private getPrivateKey(): PrivateKeySigner { if (!this.privateKey) { - throw new WalletException( - new Error('Please provide private key in the constructor'), - { - code: UnspecifiedErrorCode, - type: ErrorType.WalletNotInstalledError, - contextModule: WalletAction.GetAccounts, - }, + if (!this.metadata?.privateKey?.privateKey) { + throw new WalletException( + new Error('Please provide private key in the constructor'), + { + code: UnspecifiedErrorCode, + type: ErrorType.WalletNotInstalledError, + contextModule: WalletAction.GetAccounts, + }, + ) + } + + this.privateKey = PrivateKeySigner.fromHex( + this.metadata.privateKey.privateKey, ) } diff --git a/packages/wallets/wallet-strategy/src/strategy/index.ts b/packages/wallets/wallet-strategy/src/strategy/index.ts index 5fe77be4b..b221d4db2 100644 --- a/packages/wallets/wallet-strategy/src/strategy/index.ts +++ b/packages/wallets/wallet-strategy/src/strategy/index.ts @@ -1,11 +1,10 @@ import { Wallet, isEvmWallet, - MagicMetadata, + type WalletMetadata, ConcreteStrategiesArg, ConcreteWalletStrategy, WalletStrategyArguments, - ConcreteWalletStrategyOptions, WalletStrategyEthereumOptions, ConcreteEthereumWalletStrategyArgs, } from '@injectivelabs/wallet-base' @@ -17,7 +16,14 @@ import { MagicStrategy } from '@injectivelabs/wallet-magic' import { EvmWalletStrategy } from '@injectivelabs/wallet-evm' import { BaseWalletStrategy } from '@injectivelabs/wallet-core' import { CosmosWalletStrategy } from '@injectivelabs/wallet-cosmos' -import { TrezorBip32Strategy, TrezorBip44Strategy } from '@injectivelabs/wallet-trezor' +import { + TurnkeyOtpWalletStrategy, + TurnkeyOauthWalletStrategy, +} from '@injectivelabs/wallet-turnkey' +import { + TrezorBip32Strategy, + TrezorBip44Strategy, +} from '@injectivelabs/wallet-trezor' import { WalletConnectStrategy } from '@injectivelabs/wallet-wallet-connect' import { PrivateKeyWalletStrategy } from '@injectivelabs/wallet-private-key' import { CosmostationWalletStrategy } from '@injectivelabs/wallet-cosmostation' @@ -54,6 +60,7 @@ const createStrategy = ({ } const ethWalletArgs = { + ...args, chainId: args.chainId, ethereumOptions: args.ethereumOptions as WalletStrategyEthereumOptions, } as ConcreteEthereumWalletStrategyArgs @@ -93,15 +100,13 @@ const createStrategy = ({ wallet: Wallet.BitGet, }) case Wallet.WalletConnect: - return new WalletConnectStrategy({ - ...ethWalletArgs, - metadata: args.options?.metadata, - }) + if (!args.metadata?.walletConnect?.projectId) { + return undefined + } + + return new WalletConnectStrategy(ethWalletArgs) case Wallet.PrivateKey: - return new PrivateKeyWalletStrategy({ - ...ethWalletArgs, - privateKey: args.options?.privateKey, - }) + return new PrivateKeyWalletStrategy(ethWalletArgs) case Wallet.Keplr: return new CosmosWalletStrategy({ ...args, wallet: Wallet.Keplr }) case Wallet.Cosmostation: @@ -113,18 +118,27 @@ const createStrategy = ({ case Wallet.OWallet: return new CosmosWalletStrategy({ ...args, wallet: Wallet.OWallet }) case Wallet.Magic: + if (!args.metadata?.magic?.apiKey || !args.metadata?.magic?.rpcEndpoint) { + return undefined + } + + return new MagicStrategy(args) + case Wallet.TurnkeyOtp: if ( - !args.options?.metadata?.magic || - !(args.options?.metadata.magic as MagicMetadata)?.apiKey || - !(args.options?.metadata.magic as MagicMetadata)?.rpcEndpoint + !args.metadata?.turnkey?.defaultOrganizationId || + !args.metadata?.turnkey?.iframeContainerId ) { return undefined } - - return new MagicStrategy({ - ...args, - metadata: args.options.metadata.magic as MagicMetadata, - }) + return new TurnkeyOtpWalletStrategy(ethWalletArgs) + case Wallet.TurnkeyOauth: + if ( + !args.metadata?.turnkey?.defaultOrganizationId || + !args.metadata?.turnkey?.iframeContainerId + ) { + return undefined + } + return new TurnkeyOauthWalletStrategy(ethWalletArgs) default: return undefined } @@ -133,18 +147,15 @@ const createStrategy = ({ const createAllStrategies = ( args: WalletStrategyArguments, ): ConcreteStrategiesArg => { - return Object.values(Wallet).reduce( - (strategies, wallet) => { - if (strategies[wallet]) { - return strategies - } + return Object.values(Wallet).reduce((strategies, wallet) => { + if (strategies[wallet]) { + return strategies + } - strategies[wallet] = createStrategy({ args, wallet: wallet as Wallet }) + strategies[wallet] = createStrategy({ args, wallet: wallet as Wallet }) - return strategies - }, - {} as ConcreteStrategiesArg, - ) + return strategies + }, {} as ConcreteStrategiesArg) } export class WalletStrategy extends BaseWalletStrategy { @@ -158,26 +169,41 @@ export class WalletStrategy extends BaseWalletStrategy { } /** + * This method is used to set the metadata for the wallet strategies. + * In some cases we are going to set the metadata dynamically on the fly, and in + * some cases we are recreating the wallet strategies from scratch using the new + * metadata + * * Case 1: Private Key is set dynamically * If we have a dynamically set private key, * we are creating a new PrivateKey strategy - * with the specified private key + * with the specified private key (passed as metadata) + * + * Case 2: Similar to Case 1, but for Wallet Connect Metadata * - * Case 2: Wallet Connect Metadata set dynamically */ - public setOptions(options?: ConcreteWalletStrategyOptions) { - if (options?.privateKey) { - this.strategies[Wallet.PrivateKey] = createStrategy({ - args: { ...this.args, options: { privateKey: options.privateKey } }, - wallet: Wallet.PrivateKey, - }) - } + public setMetadata(metadata?: WalletMetadata) { + const shouldRecreateStrategyOnMetadataChange = [ + Wallet.PrivateKey, + Wallet.WalletConnect, + ] + + for (const wallet of Object.keys(this.strategies)) { + const walletEnum = wallet as Wallet + + if (shouldRecreateStrategyOnMetadataChange.includes(walletEnum)) { + this.strategies[walletEnum] = createStrategy({ + args: { + ...this.args, + metadata: { ...this.args.metadata, ...metadata }, + }, + wallet: walletEnum, + }) + + continue + } - if (options?.metadata) { - this.strategies[Wallet.WalletConnect] = createStrategy({ - args: { ...this.args, options: { metadata: options.metadata } }, - wallet: Wallet.WalletConnect, - }) + this.strategies[walletEnum]?.setMetadata?.(metadata) } } } diff --git a/packages/wallets/wallet-turnkey/CHANGELOG.md b/packages/wallets/wallet-turnkey/CHANGELOG.md new file mode 100644 index 000000000..e69de29bb diff --git a/packages/wallets/wallet-turnkey/README.md b/packages/wallets/wallet-turnkey/README.md new file mode 100644 index 000000000..c1fda73ea --- /dev/null +++ b/packages/wallets/wallet-turnkey/README.md @@ -0,0 +1,120 @@ +# 🌟 Injective Protocol - Turnkey Wallet Strategy + +[![downloads](https://img.shields.io/npm/dm/@injectivelabs/wallet-turnkey.svg)](https://www.npmjs.com/package/@injectivelabs/wallet-turnkey) +[![npm-version](https://img.shields.io/npm/v/@injectivelabs/wallet-turnkey.svg)](https://www.npmjs.com/package/@injectivelabs/wallet-turnkey) +[![license](https://img.shields.io/npm/l/express.svg)]() + +_Package to use Turnkey Wallet on Injective via the wallet strategy._ + +--- + +## 📚 Installation + +```bash +yarn add @injectivelabs/wallet-turnkey +``` + +--- + +## 📖 Documentation + +Injective's wallet packages are intended to make it easy for developers to choose exactly what wallets - and subsequent dependencies - they +want to include in their projects. + +Regardless of which wallet package(s) you choose to use you must also have `@injectivelabs/wallet-core` and `@injectivelabs/wallet-base` +installed. These contain all of the types and core wallet functionality, with the separate wallet packages only providing the necessary +dependencies and implementations for their specific wallets. + +Here's a brief example of how to use this package to send 1 INJ.: + +```typescript +import { Wallet } from '@injectivelabs/wallet-base'; +import { BaseWalletStrategy, MsgBroadcaster } from '@injectivelabs/wallet-core'; +import { TurnkeyWallet } from '@injectivelabs/wallet-turnkey'; + + +const strategyArgs: WalletStrategyArguments = { + chainId: ChainId.Mainnet, + wallet: Wallet.Turnkey, + strategies: { + [Wallet.Turnkey]: new TurnkeyWallet({ + onStatusChange(status) { + turnkeyStatus.value = status + }, + chainId: injectiveClients.chainId, + ethereumOptions: { + ethereumChainId: injectiveClients.ethereumChainId!, + }, + metadata: { + turnkeyAuthIframeContainerId, + defaultOrganizationId: import.meta.env + .VITE_TURNKEY_DEFAULT_ORGANIZATION_ID, + apiBaseUrl: 'https://api.turnkey.com', + }, + }) + }, +} +const walletStrategy = new BaseWalletStrategy(strategyArgs) + +const msgBroadcaster = new MsgBroadcaster({ + walletStrategy, + simulateTx: true, + network: Network.Mainnet, + ethereumChainId: injectiveClients.ethereumChainId!, + endpoints: injectiveClients.endpoints, +}) + +const sendTX = async () => { + const injectiveAddress = 'someInjectiveAddress' + + const message = MsgSend.fromJSON({ + srcInjectiveAddress: injectiveAddress, + dstInjectiveAddress: injectiveAddress, + amount: { + amount: '1', + denom: 'inj', + }, + }) + + return await msgBroadcaster.broadcast({ msgs: message }) + } + + const result = await sendTX() +``` + +Read more and find example usages on our [WalletStrategy Docs](https://docs.ts.injective.network/wallet/wallet-wallet-strategy) + +--- + +## 📜 Contribution + +**Contribution guides and practices will be available once there is a stable foundation of the whole package set within the `injective-ts` repo.** + +--- + +## ⛑ Support + +Reach out to us at one of the following places! + +- Website at `injective.com` +- Twitter at `@Injective` +- Discord at `Discord` +- Telegram at `Telegram` + +--- + +## 🔓 License + +Copyright © 2021 - 2022 Injective Labs Inc. (https://injectivelabs.org/) + + + +Originally released by Injective Labs Inc. under:
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/ + +

 

+
+ Powering the future of decentralized finance. +
diff --git a/packages/wallets/wallet-turnkey/package.json b/packages/wallets/wallet-turnkey/package.json new file mode 100644 index 000000000..d5d09e75d --- /dev/null +++ b/packages/wallets/wallet-turnkey/package.json @@ -0,0 +1,78 @@ +{ + "name": "@injectivelabs/wallet-turnkey", + "description": "Turnkey wallet strategy for use with @injectivelabs/wallet-core.", + "version": "1.15.3", + "sideEffects": false, + "type": "module", + "author": { + "name": "InjectiveLabs", + "email": "admin@injectivelabs.org" + }, + "license": "Apache-2.0", + "types": "dist/cjs/index.d.ts", + "main": "dist/cjs/index.js", + "module": "dist/esm/index.js", + "files": [ + "dist" + ], + "_moduleAliases": { + "~wallet-turnkey": "dist" + }, + "exports": { + ".": { + "react-native": { + "import": "./dist/esm/index.js", + "require": "./dist/cjs/index.js", + "types": "./dist/cjs/index.d.ts", + "default": "./dist/cjs/index.js" + }, + "require": { + "types": "./dist/cjs/index.d.ts", + "default": "./dist/cjs/index.js" + }, + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "default": { + "types": "./dist/cjs/index.d.ts", + "default": "./dist/cjs/index.js" + } + } + }, + "scripts": { + "build": "yarn build:esm && yarn build:cjs && yarn build:post", + "build:cjs": "tsc --build --force tsconfig.build.json", + "build:esm": "tsc --build --force tsconfig.build.esm.json", + "build:watch": "tsc --build -w tsconfig.build.json && tsc -w --build tsconfig.build.esm.json && yarn build:post", + "build:post": "shx cp ../../../etc/stub/package.json.stub dist/cjs/package.json && shx cp ../../../etc/stub/package.esm.json.stub dist/esm/package.json", + "clean": "tsc --build tsconfig.build.json --clean && tsc --build tsconfig.build.esm.json --clean && shx rm -rf coverage *.log junit.xml dist && jest --clearCache && shx mkdir -p dist", + "test": "jest", + "test:watch": "jest --watch", + "test:ci": "jest --coverage --ci --reporters='jest-junit'", + "coverage": "jest --coverage", + "coverage:show": "live-server coverage", + "dev": "ts-node -r tsconfig-paths/register src/index.ts", + "start": "node dist/index.js" + }, + "dependencies": { + "@injectivelabs/exceptions": "^1.15.2", + "@injectivelabs/sdk-ts": "^1.15.5", + "@injectivelabs/ts-types": "^1.15.3", + "@injectivelabs/utils": "^1.15.3", + "@injectivelabs/wallet-base": "^1.15.5", + "@turnkey/sdk-browser": "^4.1.0", + "@turnkey/viem": "^0.9.0", + "viem": "^2.28.1" + }, + "devDependencies": { + "jest": "^29.0.0", + "live-server": "^1.2.1", + "shx": "^0.3.4", + "ts-jest": "^29.0.0", + "ts-node": "^10.9.1", + "tsconfig-paths": "^4.2.0", + "typescript": "^5.0.0" + }, + "gitHead": "6442ae377bbfb3459d2fb3a44c650630a5b7f445" +} diff --git a/packages/wallets/wallet-turnkey/src/index.ts b/packages/wallets/wallet-turnkey/src/index.ts new file mode 100644 index 000000000..6ca7910da --- /dev/null +++ b/packages/wallets/wallet-turnkey/src/index.ts @@ -0,0 +1,5 @@ +export { TurnkeyOtpWalletStrategy } from './strategy/strategy/otp.js' +export { TurnkeyOauthWalletStrategy } from './strategy/strategy/oauth.js' + +export * from './strategy/strategy/base.js' +export * from './strategy/turnkey/turnkey.js' diff --git a/packages/wallets/wallet-turnkey/src/strategy/consts.ts b/packages/wallets/wallet-turnkey/src/strategy/consts.ts new file mode 100644 index 000000000..76a0f212e --- /dev/null +++ b/packages/wallets/wallet-turnkey/src/strategy/consts.ts @@ -0,0 +1,4 @@ +export const TURNKEY_OAUTH_PATH = 'turnkey/oauth' +export const TURNKEY_OTP_PATH = 'turnkey/otp' +export const TURNKEY_OTP_INIT_PATH = `${TURNKEY_OTP_PATH}/init` +export const TURNKEY_OTP_VERIFY_PATH = `${TURNKEY_OTP_PATH}/verify` diff --git a/packages/wallets/wallet-turnkey/src/strategy/strategy/base.ts b/packages/wallets/wallet-turnkey/src/strategy/strategy/base.ts new file mode 100644 index 000000000..c3e580b5c --- /dev/null +++ b/packages/wallets/wallet-turnkey/src/strategy/strategy/base.ts @@ -0,0 +1,497 @@ +/* eslint-disable class-methods-use-this */ +import { + ErrorType, + WalletException, + UnspecifiedErrorCode, + TransactionException, + CosmosWalletException, +} from '@injectivelabs/exceptions' +import { + TxRaw, + TxGrpcApi, + AminoSignResponse, + DirectSignResponse, +} from '@injectivelabs/sdk-ts' +import { HttpRestClient } from '@injectivelabs/utils' +import { AccountAddress, EthereumChainId } from '@injectivelabs/ts-types' +import { + StdSignDoc, + WalletAction, + TurnkeyProvider, + WalletDeviceType, + type WalletMetadata, + BaseConcreteStrategy, + ConcreteWalletStrategy, + SendTransactionOptions, + ConcreteEthereumWalletStrategyArgs, + TurnkeyMetadata, +} from '@injectivelabs/wallet-base' +import { TurnkeyWallet } from '../turnkey/turnkey.js' +import { TurnkeyErrorCodes } from '../types.js' +import { TurnkeyOtpWallet } from '../turnkey/otp.js' +import { TurnkeyOauthWallet } from '../turnkey/oauth.js' +import { SessionType } from '@turnkey/sdk-browser' +import { TurnkeyIframeClient } from '@turnkey/sdk-browser' +import { getAddress } from 'viem' +import { generateGoogleUrl } from '../../utils.js' + +export class BaseTurnkeyWalletStrategy + extends BaseConcreteStrategy + implements ConcreteWalletStrategy +{ + public turnkeyWallet: TurnkeyWallet | undefined + + public turnkeyProvider: TurnkeyProvider + + public client: HttpRestClient + + constructor( + args: ConcreteEthereumWalletStrategyArgs & { + provider: TurnkeyProvider + apiServerEndpoint?: string + }, + ) { + super(args) + + this.turnkeyProvider = args.provider + + const endpoint = + args.apiServerEndpoint || this.metadata?.turnkey?.apiServerEndpoint + + if (!endpoint) { + throw new WalletException(new Error('apiServerEndpoint is required')) + } + + this.client = new HttpRestClient(endpoint) + } + + async getWalletDeviceType(): Promise { + return Promise.resolve(WalletDeviceType.Browser) + } + + setMetadata(metadata?: { turnkey?: Partial }) { + if (metadata?.turnkey) { + this.metadata = { + ...this.metadata, + turnkey: { + ...this.metadata?.turnkey, + ...metadata.turnkey, + } as TurnkeyMetadata, + } + } + } + + //? This is here specifically because we have to ensure we set the organizationId on TurnkeyWallet as well + private setOrganizationId(organizationId: string) { + this.setMetadata({ turnkey: { organizationId } }) + this.turnkeyWallet?.setMetadata({ organizationId }) + } + + async enable(): Promise { + const turnkeyWallet = await this.getTurnkeyWallet() + + try { + const session = await turnkeyWallet.getSession() + + if (session.session) { + // User is already logged in, we don't need to do anything in the next steps + if (this.metadata?.turnkey) { + this.metadata.turnkey.session = session.session + } + + return true + } + + return !!(await turnkeyWallet.getIframeClient()) + } catch (e) { + return false + } + } + + public async disconnect() { + const turnkeyWallet = await this.getTurnkeyWallet() + const turnkey = await turnkeyWallet.getTurnkey() + + const isUserLoggedIn = await turnkey.getSession() + + if (!isUserLoggedIn) { + return + } + + await turnkey.logout() + } + + async getAddresses(): Promise { + const turnkeyWallet = await this.getTurnkeyWallet() + const organizationId = await this.getOrganizationId() + + // CHeck if the user is already connected + const session = await turnkeyWallet.getSession() + + if (!session.session) { + const iframeClient = await turnkeyWallet.getIframeClient() + // Check the provider type and perform auth step accordingly + if (this.turnkeyProvider === TurnkeyProvider.Email) { + if (!this.metadata?.turnkey?.otpId) { + throw new WalletException(new Error('OTP ID is required')) + } + + if (!this.metadata?.turnkey?.otpCode) { + throw new WalletException(new Error('OTP code is required')) + } + + const result = await TurnkeyOtpWallet.confirmEmailOTP({ + iframeClient, + organizationId, + client: this.client, + otpCode: this.metadata?.turnkey?.otpCode, + emailOTPId: this.metadata?.turnkey?.otpId, + }) + + if (result?.credentialBundle) { + this.metadata.turnkey.credentialBundle = result.credentialBundle + await turnkeyWallet.injectAndRefresh(result.credentialBundle) + } + } else { + if (!this.metadata?.turnkey?.oidcToken) { + throw new WalletException(new Error('Oidc token is required')) + } + + const result = await TurnkeyOauthWallet.oauthLogin({ + client: this.client, + iframeClient, + oidcToken: this.metadata?.turnkey?.oidcToken, + providerName: this.turnkeyProvider.toString() as 'google' | 'apple', + }) + + if (result?.credentialBundle) { + this.metadata.turnkey.organizationId = result.organizationId + this.metadata.turnkey.credentialBundle = result.credentialBundle + await turnkeyWallet.injectAndRefresh(result.credentialBundle) + } + } + } + + try { + return await turnkeyWallet.getAccounts() + } catch (e: any) { + if (e.contextCode === TurnkeyErrorCodes.UserLoggedOut) { + await this.disconnect() + + throw e + } + + throw new WalletException(new Error(e.message), { + code: UnspecifiedErrorCode, + type: ErrorType.WalletError, + contextModule: WalletAction.GetAccounts, + }) + } + } + + async getSessionOrConfirm(): Promise { + const { turnkeyProvider, metadata, client } = this + + const turnkeyWallet = await this.getTurnkeyWallet() + const iframeClient = await turnkeyWallet.getIframeClient() + + const session = await turnkeyWallet.getSession() + + if ( + // If either of these values exist on the metadata, then we want to proceed with the login flow + !this.metadata?.turnkey?.email && + !this.metadata?.turnkey?.oidcToken && + session.session?.token + ) { + await iframeClient.injectCredentialBundle(session.session?.token) + this.setOrganizationId(session.organizationId) + + await iframeClient.refreshSession({ + sessionType: SessionType.READ_WRITE, + targetPublicKey: iframeClient.iframePublicKey, + expirationSeconds: '900', + }) + + return session.session.token + } + + if (turnkeyProvider === TurnkeyProvider.Email) { + if (!metadata?.turnkey?.email) { + throw new WalletException(new Error('Email is required')) + } + + const result = await TurnkeyOtpWallet.initEmailOTP({ + client, + iframeClient, + email: metadata.turnkey.email, + otpInitPath: metadata.turnkey.otpInitPath, + }) + + if (result?.organizationId && this.metadata?.turnkey) { + this.metadata.turnkey.organizationId = result.organizationId + } + + if (result?.otpId && this.metadata?.turnkey) { + this.metadata.turnkey.otpId = result.otpId + } + + return result?.otpId || '' + } + + if ( + [TurnkeyProvider.Google, TurnkeyProvider.Apple].includes(turnkeyProvider) + ) { + if (metadata?.turnkey?.oidcToken) { + const oauthResult = await TurnkeyOauthWallet.oauthLogin({ + client, + iframeClient, + oidcToken: metadata.turnkey.oidcToken, + providerName: turnkeyProvider.toString() as 'google' | 'apple', + oauthLoginPath: metadata.turnkey.oauthLoginPath, + }) + + if (oauthResult?.credentialBundle) { + await turnkeyWallet.injectAndRefresh(oauthResult.credentialBundle) + } + + if (oauthResult?.credentialBundle) { + const session = await turnkeyWallet.getSession() + + if (this.metadata?.turnkey && session.organizationId) { + this.metadata.turnkey.organizationId = session.organizationId + } + return oauthResult.credentialBundle + } + + throw new WalletException(new Error('Oauth result not found')) + } else { + const nonce = await TurnkeyOauthWallet.generateOAuthNonce(iframeClient) + + if ( + !metadata?.turnkey?.googleClientId || + !metadata?.turnkey?.googleRedirectUri + ) { + throw new WalletException( + new Error('googleClientId and googleRedirectUri are required'), + ) + } + + if (turnkeyProvider === TurnkeyProvider.Google) { + return generateGoogleUrl({ + nonce, + clientId: metadata.turnkey.googleClientId, + redirectUri: metadata.turnkey.googleRedirectUri, + }) + } + //? When we add Apple support, we might also want to return the URL as well + return nonce + } + } + + return '' + } + + async sendEthereumTransaction( + _transaction: unknown, + _options: { address: AccountAddress; ethereumChainId: EthereumChainId }, + ): Promise { + throw new WalletException( + new Error( + 'sendEthereumTransaction is not supported. Turnkey only supports sending cosmos transactions', + ), + { + code: UnspecifiedErrorCode, + context: WalletAction.SendEthereumTransaction, + }, + ) + } + + async sendTransaction( + transaction: TxRaw, + options: SendTransactionOptions, + ): Promise { + const { endpoints, txTimeout } = options + + if (!endpoints) { + throw new WalletException( + new Error( + 'You have to pass endpoints.grpc within the options for using Turnkey wallet', + ), + ) + } + + const txApi = new TxGrpcApi(endpoints.grpc) + const response = await txApi.broadcast(transaction, { txTimeout }) + + if (response.code !== 0) { + throw new TransactionException(new Error(response.rawLog), { + code: UnspecifiedErrorCode, + contextCode: response.code, + contextModule: response.codespace, + }) + } + + return response + } + + async signEip712TypedData( + eip712json: string, + address: AccountAddress, + ): Promise { + const turnkeyWallet = await this.getTurnkeyWallet() + const organizationId = await this.getOrganizationId() + + //? Turnkey expects the case sensitive address and the current impl of getChecksumAddress from sdk-ts doesn't play nice with browser envs + const checksumAddress = getAddress(address) + + const account = await turnkeyWallet.getOrCreateAndGetAccount( + checksumAddress, + organizationId, + ) + + if (!account) { + throw new WalletException(new Error('Turnkey account not found')) + } + + let parsedData + try { + parsedData = JSON.parse(eip712json) + } catch (e) { + throw new WalletException( + new Error('Failed to parse EIP-712 data: Invalid JSON format'), + { + code: UnspecifiedErrorCode, + type: ErrorType.WalletError, + contextModule: WalletAction.SignTransaction, + }, + ) + } + + const signature = await account.signTypedData(parsedData) + + return signature + } + + // eslint-disable-next-line class-methods-use-this + async signCosmosTransaction(_transaction: { + txRaw: TxRaw + accountNumber: number + chainId: string + address: string + }): Promise { + throw new WalletException( + new Error('This wallet does not support signing Cosmos transactions'), + { + code: UnspecifiedErrorCode, + type: ErrorType.WalletError, + contextModule: WalletAction.SignTransaction, + }, + ) + } + + async signAminoCosmosTransaction(_transaction: { + address: string + signDoc: StdSignDoc + }): Promise { + throw new WalletException( + new Error('This wallet does not support signAminoCosmosTransaction'), + { + code: UnspecifiedErrorCode, + type: ErrorType.WalletError, + contextModule: WalletAction.SignTransaction, + }, + ) + } + + async signArbitrary( + _signer: AccountAddress, + _data: string | Uint8Array, + ): Promise { + throw new WalletException( + new Error('This wallet does not support signArbitrary'), + { + code: UnspecifiedErrorCode, + type: ErrorType.WalletError, + contextModule: WalletAction.SignTransaction, + }, + ) + } + + async getEthereumChainId(): Promise { + throw new CosmosWalletException( + new Error('getEthereumChainId is not supported on Turnkey wallet'), + { + code: UnspecifiedErrorCode, + context: WalletAction.GetChainId, + }, + ) + } + + async getEthereumTransactionReceipt(_txHash: string): Promise { + throw new CosmosWalletException( + new Error('getEthereumTransactionReceipt is not supported on Turnkey'), + { + code: UnspecifiedErrorCode, + type: ErrorType.WalletError, + context: WalletAction.GetEthereumTransactionReceipt, + }, + ) + } + + // eslint-disable-next-line class-methods-use-this + async getPubKey(): Promise { + throw new WalletException( + new Error('You can only fetch PubKey from Cosmos native wallets'), + ) + } + + async getIframeClient(): Promise { + const turnkeyWallet = await this.getTurnkeyWallet() + return turnkeyWallet.getIframeClient() + } + + private async getTurnkeyWallet(): Promise { + const { metadata } = this + + if (!this.turnkeyWallet) { + if (!metadata?.turnkey) { + throw new WalletException(new Error('Turnkey metadata is required')) + } + + if (!metadata.turnkey.apiBaseUrl) { + throw new WalletException(new Error('Turnkey apiBaseUrl is required')) + } + + if (!metadata.turnkey.apiServerEndpoint) { + throw new WalletException( + new Error('Turnkey apiServerEndpoint is required'), + ) + } + + if (!metadata.turnkey.defaultOrganizationId) { + throw new WalletException( + new Error('Turnkey defaultOrganizationId is required'), + ) + } + + this.turnkeyWallet = new TurnkeyWallet( + metadata.turnkey as TurnkeyMetadata, + ) + } + + return this.turnkeyWallet + } + + private async getOrganizationId(): Promise { + const { metadata } = this + const organizationId = + metadata?.turnkey?.organizationId || + metadata?.turnkey?.defaultOrganizationId + + if (!organizationId) { + throw new WalletException(new Error('Organization ID is required')) + } + + return organizationId + } +} diff --git a/packages/wallets/wallet-turnkey/src/strategy/strategy/oauth.ts b/packages/wallets/wallet-turnkey/src/strategy/strategy/oauth.ts new file mode 100644 index 000000000..d0069bd6c --- /dev/null +++ b/packages/wallets/wallet-turnkey/src/strategy/strategy/oauth.ts @@ -0,0 +1,14 @@ +import { BaseTurnkeyWalletStrategy } from './base.js' +import { + TurnkeyProvider, + ConcreteEthereumWalletStrategyArgs, +} from '@injectivelabs/wallet-base' + +export class TurnkeyOauthWalletStrategy extends BaseTurnkeyWalletStrategy { + constructor(args: ConcreteEthereumWalletStrategyArgs) { + super({ + ...args, + provider: TurnkeyProvider.Google, + }) + } +} diff --git a/packages/wallets/wallet-turnkey/src/strategy/strategy/otp.ts b/packages/wallets/wallet-turnkey/src/strategy/strategy/otp.ts new file mode 100644 index 000000000..650e09dfc --- /dev/null +++ b/packages/wallets/wallet-turnkey/src/strategy/strategy/otp.ts @@ -0,0 +1,11 @@ +import { BaseTurnkeyWalletStrategy } from './base.js' +import { ConcreteEthereumWalletStrategyArgs, TurnkeyProvider } from '@injectivelabs/wallet-base' + +export class TurnkeyOtpWalletStrategy extends BaseTurnkeyWalletStrategy { + constructor(args: ConcreteEthereumWalletStrategyArgs) { + super({ + ...args, + provider: TurnkeyProvider.Email, + }) + } +} diff --git a/packages/wallets/wallet-turnkey/src/strategy/turnkey/oauth.ts b/packages/wallets/wallet-turnkey/src/strategy/turnkey/oauth.ts new file mode 100644 index 000000000..ef2d68cf4 --- /dev/null +++ b/packages/wallets/wallet-turnkey/src/strategy/turnkey/oauth.ts @@ -0,0 +1,72 @@ +import { + ErrorType, + WalletException, + UnspecifiedErrorCode, +} from '@injectivelabs/exceptions' +import { sha256 } from '@injectivelabs/sdk-ts' +import type { TurnkeyOauthLoginResponse } from '../types.js' +import type { TurnkeyIframeClient } from '@turnkey/sdk-browser' +import { type HttpRestClient } from '@injectivelabs/utils' +import { TURNKEY_OAUTH_PATH } from '../consts.js' + +export class TurnkeyOauthWallet { + static async generateOAuthNonce(iframeClient: TurnkeyIframeClient) { + try { + const targetPublicKey = iframeClient.iframePublicKey + + if (!targetPublicKey) { + throw new WalletException(new Error('Target public key not found')) + } + + return Array.from(sha256(new TextEncoder().encode(targetPublicKey))) + .map((b) => b.toString(16).padStart(2, '0')) + .join('') + } catch (e: any) { + throw new WalletException(new Error(e.message), { + code: UnspecifiedErrorCode, + type: ErrorType.WalletError, + contextModule: 'turnkey-generate-oauth-nonce', + }) + } + } + + static async oauthLogin(args: { + oidcToken: string + client: HttpRestClient + oauthLoginPath?: string + expirationSeconds?: number + providerName: 'google' | 'apple' + iframeClient: TurnkeyIframeClient + }): Promise< + { organizationId: string; credentialBundle: string } | undefined + > { + const { client, iframeClient } = args + + const path = args.oauthLoginPath || TURNKEY_OAUTH_PATH + + try { + const targetPublicKey = iframeClient.iframePublicKey + + if (!targetPublicKey) { + throw new WalletException(new Error('Target public key not found')) + } + + // client.$post is undefined, resorting to this for now + const response = await client.post<{ + data: TurnkeyOauthLoginResponse + }>(path, { + targetPublicKey, + oidcToken: args.oidcToken, + providerName: args.providerName, + }) + + return response.data + } catch (e: any) { + throw new WalletException(new Error(e.message), { + code: UnspecifiedErrorCode, + type: ErrorType.WalletError, + contextModule: 'turnkey-oauth-login', + }) + } + } +} diff --git a/packages/wallets/wallet-turnkey/src/strategy/turnkey/otp.ts b/packages/wallets/wallet-turnkey/src/strategy/turnkey/otp.ts new file mode 100644 index 000000000..d0093229f --- /dev/null +++ b/packages/wallets/wallet-turnkey/src/strategy/turnkey/otp.ts @@ -0,0 +1,98 @@ +import { type TurnkeyIframeClient } from '@turnkey/sdk-browser' +import { + ErrorType, + WalletException, + UnspecifiedErrorCode, +} from '@injectivelabs/exceptions' +import { + type TurnkeyConfirmEmailOTPResponse, + type TurnkeyOTPCredentialsResponse, +} from './../types.js' +import { type HttpRestClient } from '@injectivelabs/utils' +import { TURNKEY_OTP_INIT_PATH, TURNKEY_OTP_VERIFY_PATH } from '../consts.js' + +export class TurnkeyOtpWallet { + static async initEmailOTP(args: { + email: string + subOrgId?: string + otpInitPath?: string + client: HttpRestClient + iframeClient: TurnkeyIframeClient + invalidateExistingSessions?: boolean + }) { + const { client, iframeClient } = args + + try { + const targetPublicKey = iframeClient.iframePublicKey + + if (!targetPublicKey) { + throw new WalletException(new Error('Target public key not found')) + } + + // client.$post is undefined, resorting to this for now + const response = await client.post<{ + data?: TurnkeyOTPCredentialsResponse + }>(args.otpInitPath || TURNKEY_OTP_INIT_PATH, { + targetPublicKey, + email: args.email, + suborgId: args.subOrgId, + invalidateExistingSessions: args.invalidateExistingSessions, + }) + + return response?.data + } catch (e: any) { + throw new WalletException(new Error(e.message), { + code: UnspecifiedErrorCode, + type: ErrorType.WalletError, + contextModule: 'turnkey-init-email-otp', + }) + } + } + + static async confirmEmailOTP(args: { + otpCode: string + emailOTPId?: string + client: HttpRestClient + organizationId?: string + iframeClient: TurnkeyIframeClient + otpVerifyPath?: string + }) { + const { client, iframeClient } = args + + try { + const organizationId = args.organizationId + const emailOTPId = args.emailOTPId + const targetPublicKey = iframeClient.iframePublicKey + const otpVerifyPath = args.otpVerifyPath || TURNKEY_OTP_VERIFY_PATH + + if (!targetPublicKey) { + throw new WalletException(new Error('Target public key not found')) + } + + if (!emailOTPId) { + throw new WalletException(new Error('Email OTP ID is required')) + } + + if (!organizationId) { + throw new WalletException(new Error('Organization ID is required')) + } + + const response = await client.post<{ + data?: TurnkeyConfirmEmailOTPResponse + }>(otpVerifyPath, { + targetPublicKey, + otpId: emailOTPId, + otpCode: args.otpCode, + suborgID: organizationId, + }) + + return response?.data + } catch (e: any) { + throw new WalletException(new Error(e.message), { + code: UnspecifiedErrorCode, + type: ErrorType.WalletError, + contextModule: 'turnkey-confirm-email-otp', + }) + } + } +} diff --git a/packages/wallets/wallet-turnkey/src/strategy/turnkey/turnkey.ts b/packages/wallets/wallet-turnkey/src/strategy/turnkey/turnkey.ts new file mode 100644 index 000000000..ad26c5321 --- /dev/null +++ b/packages/wallets/wallet-turnkey/src/strategy/turnkey/turnkey.ts @@ -0,0 +1,275 @@ +import { Turnkey, SessionType, TurnkeyIframeClient } from '@turnkey/sdk-browser' +import { + ErrorType, + WalletException, + GeneralException, + UnspecifiedErrorCode, +} from '@injectivelabs/exceptions' +import { WalletAction, TurnkeyMetadata } from '@injectivelabs/wallet-base' +import { getInjectiveAddress } from '@injectivelabs/sdk-ts' +import { HttpRestClient } from '@injectivelabs/utils' +import { createAccount } from '@turnkey/viem' +import { TurnkeyErrorCodes } from '../types.js' + +export class TurnkeyWallet { + protected iframeClient: TurnkeyIframeClient | undefined + + protected turnkey: Turnkey | undefined + + protected client: HttpRestClient + + private metadata: TurnkeyMetadata + + private accountMap: Record< + string, + Awaited> + > = {} + + public setMetadata(metadata: Partial) { + this.metadata = { ...this.metadata, ...metadata } + } + + constructor(metadata: TurnkeyMetadata) { + this.metadata = metadata + + this.client = new HttpRestClient(metadata.apiServerEndpoint) + } + + public static async getTurnkeyInstance(metadata: TurnkeyMetadata) { + const { turnkey, iframeClient } = await createTurnkeyIFrame(metadata) + + return { + turnkey, + iframeClient, + } + } + + public async getTurnkey(): Promise { + if (!this.iframeClient) { + await this.initFrame() + } + + if (!this.turnkey) { + this.turnkey = new Turnkey(this.metadata) + } + + return this.turnkey as Turnkey + } + + public async getIframeClient(): Promise { + if (!this.iframeClient) { + await this.initFrame() + } + + if (!this.iframeClient) { + throw new WalletException(new Error('Iframe client not initialized')) + } + + return this.iframeClient as TurnkeyIframeClient + } + + public async getSession(existingCredentialBundle?: string) { + const { metadata } = this + + const iframeClient = await this.getIframeClient() + const turnkey = await this.getTurnkey() + + const currentSession = await turnkey.getSession() + const organizationId = + currentSession?.organizationId || metadata.defaultOrganizationId + const credentialBundle = existingCredentialBundle || currentSession?.token + + if (!credentialBundle) { + return { + session: undefined, + organizationId, + } + } + + try { + const loginResult = await iframeClient.injectCredentialBundle( + credentialBundle, + ) + + // If there is no session, we want to force a refresh to enable to browser SDK to handle key storage and proper session management. + await iframeClient.refreshSession({ + sessionType: SessionType.READ_WRITE, + targetPublicKey: iframeClient.iframePublicKey, + expirationSeconds: '900', + }) + + const [session, user] = await Promise.all([ + turnkey.getSession(), + iframeClient.getWhoami(), + ]) + + const actualOrganizationId = + user?.organizationId || session?.organizationId || organizationId + + if (!loginResult) { + return { + session: undefined, + organizationId: actualOrganizationId, + } + } + + return { + session, + organizationId: actualOrganizationId, + } + } catch (e: any) { + throw new WalletException(new Error(e.message), { + code: UnspecifiedErrorCode, + type: ErrorType.WalletError, + contextModule: 'turnkey-wallet-get-session', + }) + } + } + + public async getAccounts() { + const iframeClient = await this.getIframeClient() + + if (!this.metadata.organizationId) { + return [] + } + + try { + const response = await iframeClient.getWallets({ + organizationId: this.metadata.organizationId, + }) + + const accounts = await Promise.allSettled( + response.wallets.map((wallet) => + iframeClient.getWalletAccounts({ + walletId: wallet.walletId, + organizationId: this.metadata.organizationId, + }), + ), + ) + + const filteredAccounts = accounts + .filter((account) => account.status === 'fulfilled') + .flatMap((result) => result.value?.accounts) + .filter( + (wa): wa is NonNullable => + !!wa && + wa.addressFormat === 'ADDRESS_FORMAT_ETHEREUM' && + !!wa.address, + ) + + return filteredAccounts.map((account) => + getInjectiveAddress(account.address), + ) + } catch (e: any) { + if (e.code === TurnkeyErrorCodes.UserLoggedOut) { + throw new WalletException(new Error('User is not logged in'), { + code: UnspecifiedErrorCode, + type: ErrorType.WalletError, + contextModule: WalletAction.GetAccounts, + contextCode: TurnkeyErrorCodes.UserLoggedOut, + }) + } + + throw new WalletException(new Error(e.message), { + code: UnspecifiedErrorCode, + type: ErrorType.WalletError, + contextModule: 'turnkey-wallet-get-accounts', + }) + } + } + + public async getOrCreateAndGetAccount( + address: string, + organizationId?: string, + ) { + const { accountMap } = this + const iframeClient = await this.getIframeClient() + + if (accountMap[address] || accountMap[address.toLowerCase()]) { + return accountMap[address] || accountMap[address.toLowerCase()] + } + + if (!organizationId) { + throw new WalletException(new Error('Organization ID is required')) + } + + iframeClient.config.organizationId = organizationId + + if (!address) { + throw new WalletException(new Error('Account address not found')) + } + + const turnkeyAccount = await createAccount({ + organizationId, + signWith: address, + client: iframeClient, + }) + + this.accountMap[address] = turnkeyAccount + + return turnkeyAccount + } + + public async injectAndRefresh(credentialBundle: string) { + const iframeClient = await this.getIframeClient() + + await iframeClient.injectCredentialBundle(credentialBundle) + await iframeClient.refreshSession({ + sessionType: SessionType.READ_WRITE, + targetPublicKey: iframeClient.iframePublicKey, + expirationSeconds: '900', + }) + + return + } + + private async initFrame(): Promise { + const { metadata } = this + const { turnkey, iframeClient } = await createTurnkeyIFrame(metadata) + + this.turnkey = turnkey + this.iframeClient = iframeClient + } +} + +async function createTurnkeyIFrame(metadata: TurnkeyMetadata) { + const turnkey = new Turnkey(metadata) + + const turnkeyAuthIframeElementId = + metadata.iframeElementId || 'turnkey-auth-iframe-element-id' + + if (!metadata.iframeContainerId) { + throw new GeneralException(new Error('iframeContainerId is required')) + } + + if (!turnkey) { + throw new GeneralException(new Error('Turnkey is not initialized')) + } + + const iframe = document.getElementById( + metadata.iframeContainerId, + ) as HTMLIFrameElement + + if (!iframe) { + throw new GeneralException(new Error('iframe is null')) + } + + const existingIframeClient = document.getElementById( + turnkeyAuthIframeElementId, + ) + + if (existingIframeClient) { + existingIframeClient.remove() + } + + const iframeClient = await turnkey.iframeClient({ + iframeContainer: iframe, + iframeElementId: turnkeyAuthIframeElementId, + iframeUrl: metadata?.iframeUrl || 'https://auth.turnkey.com', + }) + + return { + turnkey, + iframeClient, + } +} diff --git a/packages/wallets/wallet-turnkey/src/strategy/types.ts b/packages/wallets/wallet-turnkey/src/strategy/types.ts new file mode 100644 index 000000000..8a8c027a2 --- /dev/null +++ b/packages/wallets/wallet-turnkey/src/strategy/types.ts @@ -0,0 +1,32 @@ +export enum TurnkeyErrorCodes { + UserLoggedOut = 7, +} + +export type TurnkeyOAuthArgs = { + provider: 'google' + oidcToken: string + oauthLoginEndpoint: string +} + +export type TurnkeyEmailArgs = { + provider: 'email' + email: string + initEmailOTPEndpoint: string +} + +export type TurnkeyEnableArgs = TurnkeyOAuthArgs | TurnkeyEmailArgs + +export type TurnkeyOTPCredentialsResponse = { + otpId: string + organizationId: string +} + +export type TurnkeyConfirmEmailOTPResponse = { + credentialBundle: string +} + +export type TurnkeyOauthLoginResponse = { + organizationId: string + credentialBundle: string + message: string +} diff --git a/packages/wallets/wallet-turnkey/src/utils.ts b/packages/wallets/wallet-turnkey/src/utils.ts new file mode 100644 index 000000000..eded06192 --- /dev/null +++ b/packages/wallets/wallet-turnkey/src/utils.ts @@ -0,0 +1,21 @@ +export function generateGoogleUrl({ + nonce, + clientId, + redirectUri, + scope = 'openid profile email', + prompt = 'consent', +}: { + nonce: string + clientId: string + redirectUri: string + scope?: string + prompt?: string +}) { + if (!clientId) { + throw new Error('Google client ID not found') + } + + const responseType = 'id_token' + + return `https://accounts.google.com/o/oauth2/v2/auth?prompt=${prompt}&client_id=${clientId}&redirect_uri=${redirectUri}&response_type=${responseType}&scope=${scope}&nonce=${nonce}` +} diff --git a/packages/wallets/wallet-turnkey/tsconfig.build.esm.json b/packages/wallets/wallet-turnkey/tsconfig.build.esm.json new file mode 100644 index 000000000..37490362e --- /dev/null +++ b/packages/wallets/wallet-turnkey/tsconfig.build.esm.json @@ -0,0 +1,13 @@ +{ + "extends": "./tsconfig.build.json", + "compilerOptions": { + "module": "NodeNext", + "outDir": "./dist/esm", + "rootDir": "./src", + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "target": "ESNext", + "moduleResolution": "NodeNext", + "tsBuildInfoFile": "../../../.build-cache/wallet-turnkey.esm.tsbuildinfo" + } +} diff --git a/packages/wallets/wallet-turnkey/tsconfig.build.json b/packages/wallets/wallet-turnkey/tsconfig.build.json new file mode 100644 index 000000000..5ca6e5009 --- /dev/null +++ b/packages/wallets/wallet-turnkey/tsconfig.build.json @@ -0,0 +1,12 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "rootDir": "./src", + "outDir": "./dist/cjs", + "module": "commonjs", + "moduleResolution": "node", + "tsBuildInfoFile": "../../../.build-cache/wallet-turnkey.tsbuildinfo" + }, + "include": ["./src/**/*.ts"], + "exclude": ["./src/**/*.spec.ts", "./src/**/*.test.ts"] +} diff --git a/packages/wallets/wallet-turnkey/tsconfig.json b/packages/wallets/wallet-turnkey/tsconfig.json new file mode 100644 index 000000000..618c6c3e9 --- /dev/null +++ b/packages/wallets/wallet-turnkey/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../../tsconfig.json" +} diff --git a/packages/wallets/wallet-wallet-connect/src/strategy/strategy.ts b/packages/wallets/wallet-wallet-connect/src/strategy/strategy.ts index 139143109..033dc6041 100644 --- a/packages/wallets/wallet-wallet-connect/src/strategy/strategy.ts +++ b/packages/wallets/wallet-wallet-connect/src/strategy/strategy.ts @@ -12,10 +12,8 @@ import { WalletDeviceType, WalletEventListener, BaseConcreteStrategy, - WalletConnectMetadata, ConcreteWalletStrategy, SendTransactionOptions, - ConcreteEthereumWalletStrategyArgs, } from '@injectivelabs/wallet-base' import { Provider, @@ -37,24 +35,12 @@ const WalletConnectIds = { '5864e2ced7c293ed18ac35e0db085c09ed567d67346ccb6f58a0327a75137489', } -interface WalletConnectArgs extends ConcreteEthereumWalletStrategyArgs { - metadata?: WalletConnectMetadata -} - export class WalletConnect extends BaseConcreteStrategy implements ConcreteWalletStrategy { public provider: Provider | undefined - public metadata?: WalletConnectMetadata - - constructor(args: WalletConnectArgs) { - super(args) - - this.metadata = args.metadata - } - async getWalletDeviceType(): Promise { return Promise.resolve(WalletDeviceType.Browser) } @@ -294,7 +280,7 @@ export class WalletConnect return this.provider } - if (!this.metadata) { + if (!this.metadata?.walletConnect) { throw new WalletException( new Error('Please provide metadata for WalletConnect'), { @@ -305,7 +291,7 @@ export class WalletConnect ) } - if (!this.metadata.projectId) { + if (!this.metadata.walletConnect.projectId) { throw new WalletException( new Error( 'Please provide projectId alongside the metadata for WalletConnect', @@ -320,8 +306,8 @@ export class WalletConnect try { this.provider = await EthereumProvider.init({ - projectId: this.metadata.projectId as string, - metadata: this.metadata + projectId: this.metadata.walletConnect.projectId as string, + metadata: this .metadata as unknown as EthereumProviderOptions['metadata'], showQrModal: true, optionalChains: this.ethereumChainId diff --git a/yarn.lock b/yarn.lock index b780b0f45..29b31216b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,7 +7,7 @@ resolved "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz#63430d04bd8c5e74f8d7d049338f1cd9d4f02069" integrity sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw== -"@adraffy/ens-normalize@^1.8.8": +"@adraffy/ens-normalize@^1.10.1", "@adraffy/ens-normalize@^1.8.8": version "1.11.0" resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.11.0.tgz#42cc67c5baa407ac25059fcd7d405cc5ecdb0c33" integrity sha512-/3DDPKHqqIqxUULp8yP4zODUY1i+2xvVWsv8A79xGWdCAG+8sb0hRh0Rk2QyOJUnnbyPUAZYcpBuRe3nS2OIUg== @@ -1320,7 +1320,7 @@ "@ledgerhq/hw-transport" "npm:@bangjelkoski/ledgerhq-hw-transport@^6.31.4-beta.0" "@ledgerhq/logs" "^6.12.0" -"@bangjelkoski/ledgerhq-hw-transport@6.31.4-beta.0", "@ledgerhq/hw-transport@npm:@bangjelkoski/ledgerhq-hw-transport@^6.31.4-beta.0": +"@bangjelkoski/ledgerhq-hw-transport@6.31.4-beta.0": version "6.31.4-beta.0" resolved "https://registry.yarnpkg.com/@bangjelkoski/ledgerhq-hw-transport/-/ledgerhq-hw-transport-6.31.4-beta.0.tgz#c5a3b49f6b7cc16a9243285a4dd18d8b3a53e94f" integrity sha512-AIRuxu8s83mv0cBooO5v+pirZyIEUobl8L9QmXvBlTJybmMmyBgigLuoIcZDlSp4Tc7jnoOlHSpFQkASc+5sNA== @@ -2452,6 +2452,44 @@ resolved "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.1.1.tgz#076d78ce99822258cf813ecc1e7fa460fa74d052" integrity sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg== +"@hpke/chacha20poly1305@^1.6.2": + version "1.6.2" + resolved "https://registry.yarnpkg.com/@hpke/chacha20poly1305/-/chacha20poly1305-1.6.2.tgz#34f9e242c2693518afe07bcf64ef5ab92c681d00" + integrity sha512-LAzcHlX+GfrVqwjx+EqqHmEdkzE5YYIlzZz4Q1uN2Keq81iOB9IveJpufhsbyB1zw7W5/Y4gJ6dfAZq4gFO/sA== + dependencies: + "@hpke/common" "^1.7.2" + "@noble/ciphers" "^1.2.1" + +"@hpke/common@^1.7.2": + version "1.7.2" + resolved "https://registry.yarnpkg.com/@hpke/common/-/common-1.7.2.tgz#64415e0465ea6d639883a06dbdd40168c5d39f0e" + integrity sha512-x8wGc1S3ANyQpbTXxhDuzQwzZn9IDaOARpEq/QzDyWFBEhOFyN8mJd6oMnvFkxe0H96JFgIU6eI4G2aaNxgFJQ== + +"@hpke/core@^1.7.2": + version "1.7.2" + resolved "https://registry.yarnpkg.com/@hpke/core/-/core-1.7.2.tgz#6f9377dd4da659bd2011e1bf89bff31db6efeec2" + integrity sha512-WPsy/Wp1oF+47EVfQdXG55TGS+rOKAAZJ4W/4BFnTENGGq/EAJeX1h0ooCarkqWrJsREsrpa4EiIZkz1m8hMOA== + dependencies: + "@hpke/common" "^1.7.2" + +"@hpke/dhkem-x25519@^1.6.2": + version "1.6.2" + resolved "https://registry.yarnpkg.com/@hpke/dhkem-x25519/-/dhkem-x25519-1.6.2.tgz#77a23046e61e462047453498642a6374685ebc4f" + integrity sha512-xL//FDIY0ai3/JZyr3UI/jaw8eLEcs+SU7Z9K5uxO8R4xzvwOfGajI4VE9GH+QXGMrHncQDLIDPX9pcdqkGSvQ== + dependencies: + "@hpke/common" "^1.7.2" + "@noble/curves" "^1.8.1" + "@noble/hashes" "^1.7.1" + +"@hpke/dhkem-x448@^1.6.2": + version "1.6.2" + resolved "https://registry.yarnpkg.com/@hpke/dhkem-x448/-/dhkem-x448-1.6.2.tgz#64f818def201cb7af284193225cf458f80b5ad4f" + integrity sha512-uA5DJczlkjpvfjHUvLTk9Ux7ET5ERnkFR0KxvdRHtArN72Bzf4MKVSB/9hqKB/rD+zx8oWIy9KHrlYxj+0DS7g== + dependencies: + "@hpke/common" "^1.7.2" + "@noble/curves" "^1.8.1" + "@noble/hashes" "^1.7.1" + "@humanwhocodes/config-array@^0.5.0": version "0.5.0" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" @@ -2980,6 +3018,16 @@ "@ledgerhq/logs" "^6.12.0" events "^3.3.0" +"@ledgerhq/hw-transport@npm:@bangjelkoski/ledgerhq-hw-transport@^6.31.4-beta.0": + version "6.31.4-beta.0" + resolved "https://registry.yarnpkg.com/@bangjelkoski/ledgerhq-hw-transport/-/ledgerhq-hw-transport-6.31.4-beta.0.tgz#c5a3b49f6b7cc16a9243285a4dd18d8b3a53e94f" + integrity sha512-AIRuxu8s83mv0cBooO5v+pirZyIEUobl8L9QmXvBlTJybmMmyBgigLuoIcZDlSp4Tc7jnoOlHSpFQkASc+5sNA== + dependencies: + "@ledgerhq/devices" "8.4.4" + "@ledgerhq/errors" "^6.19.1" + "@ledgerhq/logs" "^6.12.0" + events "^3.3.0" + "@ledgerhq/live-env@^2.5.0": version "2.5.0" resolved "https://registry.yarnpkg.com/@ledgerhq/live-env/-/live-env-2.5.0.tgz#53f85ad6e09a9b71692df59b8acd5962c2bee8e9" @@ -3245,11 +3293,21 @@ "@emnapi/runtime" "^1.1.0" "@tybys/wasm-util" "^0.9.0" +"@noble/ciphers@0.5.3": + version "0.5.3" + resolved "https://registry.yarnpkg.com/@noble/ciphers/-/ciphers-0.5.3.tgz#48b536311587125e0d0c1535f73ec8375cd76b23" + integrity sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w== + "@noble/ciphers@1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@noble/ciphers/-/ciphers-1.2.1.tgz#3812b72c057a28b44ff0ad4aff5ca846e5b9cdc9" integrity sha512-rONPWMC7PeExE077uLE4oqWrZ1IvAfz3oH9LibVAcVCopJiA9R62uavnbEzdkVmJYI6M6Zgkbeb07+tWjlq2XA== +"@noble/ciphers@^1.2.1": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@noble/ciphers/-/ciphers-1.3.0.tgz#f64b8ff886c240e644e5573c097f86e5b43676dc" + integrity sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw== + "@noble/curves@1.2.0": version "1.2.0" resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35" @@ -3257,6 +3315,13 @@ dependencies: "@noble/hashes" "1.3.2" +"@noble/curves@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.4.0.tgz#f05771ef64da724997f69ee1261b2417a49522d6" + integrity sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg== + dependencies: + "@noble/hashes" "1.4.0" + "@noble/curves@1.4.2", "@noble/curves@~1.4.0": version "1.4.2" resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz#40309198c76ed71bc6dbf7ba24e81ceb4d0d1fe9" @@ -3278,6 +3343,20 @@ dependencies: "@noble/hashes" "1.7.1" +"@noble/curves@1.8.2", "@noble/curves@~1.8.1": + version "1.8.2" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.8.2.tgz#8f24c037795e22b90ae29e222a856294c1d9ffc7" + integrity sha512-vnI7V6lFNe0tLAuJMu+2sX+FcL14TaCWy1qiczg1VwRmPrpQCdq5ESXQMqUc2tluRNf6irBXrWbl1mGN8uaU/g== + dependencies: + "@noble/hashes" "1.7.2" + +"@noble/curves@^1.3.0", "@noble/curves@^1.6.0", "@noble/curves@~1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.9.0.tgz#13e0ca8be4a0ce66c113693a94514e5599f40cfc" + integrity sha512-7YDlXiNMdO1YZeH6t/kvopHHbIZzlxrCV9WLqCY6QhcXOoXiNCMDqJIglZ9Yjx5+w7Dz30TITFrlTjnRg7sKEg== + dependencies: + "@noble/hashes" "1.8.0" + "@noble/curves@^1.4.2": version "1.7.0" resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.7.0.tgz#0512360622439256df892f21d25b388f52505e45" @@ -3315,6 +3394,16 @@ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.7.1.tgz#5738f6d765710921e7a751e00c20ae091ed8db0f" integrity sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ== +"@noble/hashes@1.7.2": + version "1.7.2" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.7.2.tgz#d53c65a21658fb02f3303e7ee3ba89d6754c64b4" + integrity sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ== + +"@noble/hashes@1.8.0", "@noble/hashes@^1.5.0", "@noble/hashes@~1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.8.0.tgz#cee43d801fcef9644b11b8194857695acd5f815a" + integrity sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A== + "@noble/hashes@^1", "@noble/hashes@^1.0.0": version "1.1.5" resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.5.tgz#1a0377f3b9020efe2fae03290bd2a12140c95c11" @@ -3896,6 +3985,11 @@ resolved "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz#e5e142fbbfe251091f9c5f1dd4c834ac04c3dbd1" integrity sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg== +"@scure/base@~1.2.2", "@scure/base@~1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.2.5.tgz#f9d1b232425b367d0dcb81c96611dcc651d58671" + integrity sha512-9rE6EOVeIQzt5TSu4v+K523F8u6DhBsoZWPGKlnCshhlDhy0kJzUX4V+tr2dWmzF1GdekvThABoEQBGBQI7xZw== + "@scure/base@~1.2.4": version "1.2.4" resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.2.4.tgz#002eb571a35d69bdb4c214d0995dff76a8dcd2a9" @@ -3919,6 +4013,24 @@ "@noble/hashes" "~1.4.0" "@scure/base" "~1.1.6" +"@scure/bip32@1.6.2": + version "1.6.2" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.6.2.tgz#093caa94961619927659ed0e711a6e4bf35bffd0" + integrity sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw== + dependencies: + "@noble/curves" "~1.8.1" + "@noble/hashes" "~1.7.1" + "@scure/base" "~1.2.2" + +"@scure/bip32@^1.5.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.7.0.tgz#b8683bab172369f988f1589640e53c4606984219" + integrity sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw== + dependencies: + "@noble/curves" "~1.9.0" + "@noble/hashes" "~1.8.0" + "@scure/base" "~1.2.5" + "@scure/bip39@1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.1.1.tgz#b54557b2e86214319405db819c4b6a370cf340c5" @@ -3935,7 +4047,7 @@ "@noble/hashes" "~1.4.0" "@scure/base" "~1.1.6" -"@scure/bip39@^1.5.1": +"@scure/bip39@1.5.4", "@scure/bip39@^1.5.1": version "1.5.4" resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.5.4.tgz#07fd920423aa671be4540d59bdd344cc1461db51" integrity sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA== @@ -3943,6 +4055,14 @@ "@noble/hashes" "~1.7.1" "@scure/base" "~1.2.4" +"@scure/bip39@^1.4.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.6.0.tgz#475970ace440d7be87a6086cbee77cb8f1a684f9" + integrity sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A== + dependencies: + "@noble/hashes" "~1.8.0" + "@scure/base" "~1.2.5" + "@sigstore/bundle@^2.3.2": version "2.3.2" resolved "https://registry.npmjs.org/@sigstore/bundle/-/bundle-2.3.2.tgz#ad4dbb95d665405fd4a7a02c8a073dbd01e4e95e" @@ -4445,6 +4565,105 @@ "@tufjs/canonical-json" "2.0.0" minimatch "^9.0.4" +"@turnkey/api-key-stamper@0.4.5": + version "0.4.5" + resolved "https://registry.yarnpkg.com/@turnkey/api-key-stamper/-/api-key-stamper-0.4.5.tgz#a5b75297d38ef20ac5d0891732ee342955ec64b5" + integrity sha512-8UeYt/2WtMrK2uSFzjiRXdCVc9SmKlMVuA4f1Z+SlxcsW0wlEpNM/7bd8N4VVVAjoE8Yy50Vhk3ylfQFtlaKsQ== + dependencies: + "@noble/curves" "^1.3.0" + "@turnkey/encoding" "0.4.0" + sha256-uint8array "^0.10.7" + +"@turnkey/crypto@2.3.1": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@turnkey/crypto/-/crypto-2.3.1.tgz#22d3d263cf67daba71d72f73206a3024291e2028" + integrity sha512-fHKCw0inuThEKIpZnC8pvz16egZHf08wES0LdSkM9XrGDcI7p0eqF7nAHgfAMW/0qNVZD8AKW+g/5O97HBwZ4w== + dependencies: + "@noble/ciphers" "0.5.3" + "@noble/curves" "1.4.0" + "@noble/hashes" "1.4.0" + "@turnkey/encoding" "0.4.0" + bs58 "^5.0.0" + bs58check "3.0.1" + +"@turnkey/encoding@0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@turnkey/encoding/-/encoding-0.4.0.tgz#9d06efe6447322bcc9eb4d51b48961054a3c102d" + integrity sha512-ptLgcpWVt34KTPx0omF2QLJrosW6I//clCJ4G2+yngYFCzrdR0yBchV/BOcfME67mK1v3MmauyXl9AAnQTmB4Q== + +"@turnkey/http@3.3.0": + version "3.3.0" + resolved "https://registry.yarnpkg.com/@turnkey/http/-/http-3.3.0.tgz#b7f6b54ef0f81f89bf80fe42cc55c1128b8459dc" + integrity sha512-m1fP8aqQcI0U4j7Gw89UbavavWP7AWGmEwoJl5nBhQz88mtep6AaZTsUoDDU4HXg0ch+aQ2/lfl0KFPp8s2u5Q== + dependencies: + "@turnkey/api-key-stamper" "0.4.5" + "@turnkey/encoding" "0.4.0" + "@turnkey/webauthn-stamper" "0.5.0" + cross-fetch "^3.1.5" + +"@turnkey/iframe-stamper@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@turnkey/iframe-stamper/-/iframe-stamper-2.5.0.tgz#4543458a3ac380c96f239c6c2a997c03bac0bd7b" + integrity sha512-XjntbA5CNjxGRH+loceAlVLL9PG9Q4Y7p5zjBm4DeKclhD6lpUl9h8INArMEXIFbfLwLjjS6Q+SmQG4BHvNY6A== + +"@turnkey/sdk-browser@4.3.0", "@turnkey/sdk-browser@^4.1.0": + version "4.3.0" + resolved "https://registry.yarnpkg.com/@turnkey/sdk-browser/-/sdk-browser-4.3.0.tgz#90bdaa4f7a6f2c9f3ef5eaad53dbd87ec9509515" + integrity sha512-TBAWpA28PnqFOeD45Ljzq5RfAabj4Qlnvo1toekt+yf6Hk13Nsm6R5FEiivHUMSNcvKBAwf5uQg+MPbBndymlQ== + dependencies: + "@turnkey/api-key-stamper" "0.4.5" + "@turnkey/crypto" "2.3.1" + "@turnkey/encoding" "0.4.0" + "@turnkey/http" "3.3.0" + "@turnkey/iframe-stamper" "2.5.0" + "@turnkey/wallet-stamper" "1.0.3" + "@turnkey/webauthn-stamper" "0.5.0" + bs58check "^3.0.1" + buffer "^6.0.3" + cross-fetch "^3.1.5" + hpke-js "^1.2.7" + +"@turnkey/sdk-server@3.3.0": + version "3.3.0" + resolved "https://registry.yarnpkg.com/@turnkey/sdk-server/-/sdk-server-3.3.0.tgz#d53cbaec3c7a63f338f9a326ac0d78797bc8de2f" + integrity sha512-qaREccylb1XNwFzK05hOoKMNdxP0VG/bOtttrgxNCrhdlwg/J6k8g9//1CthFqEfc7OvWag54oWe9Y/rVpbg+g== + dependencies: + "@turnkey/api-key-stamper" "0.4.5" + "@turnkey/http" "3.3.0" + "@turnkey/wallet-stamper" "1.0.3" + buffer "^6.0.3" + cross-fetch "^3.1.5" + next "^15.2.3" + +"@turnkey/viem@^0.9.0": + version "0.9.3" + resolved "https://registry.yarnpkg.com/@turnkey/viem/-/viem-0.9.3.tgz#0f827639a3fbd532d9ff4cee2c99f576127add0c" + integrity sha512-f2wIAxU4qajbBmrUTInz4d/fqDo5krdnFELmBiGm2mHIALnyJL5Awi2chl7pT1uEFhTdLvIpDcr2KIddHQj63Q== + dependencies: + "@noble/curves" "1.8.0" + "@turnkey/api-key-stamper" "0.4.5" + "@turnkey/http" "3.3.0" + "@turnkey/sdk-browser" "4.3.0" + "@turnkey/sdk-server" "3.3.0" + cross-fetch "^4.0.0" + +"@turnkey/wallet-stamper@1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@turnkey/wallet-stamper/-/wallet-stamper-1.0.3.tgz#9e0a006dffd671fd5d4f9d84aa21b21ceb42add8" + integrity sha512-8DMuVPo/u8oIzQ9Snsa24ZQO3nXVMfpd8VnlPLfZiW2jjZHPFyBGCJOYSArfp+W2xoudpYVu//JElBAVxk/u/g== + dependencies: + "@turnkey/crypto" "2.3.1" + "@turnkey/encoding" "0.4.0" + optionalDependencies: + viem "^2.21.35" + +"@turnkey/webauthn-stamper@0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@turnkey/webauthn-stamper/-/webauthn-stamper-0.5.0.tgz#014b8c20b1732af49dacb04f396edf010d3b7f47" + integrity sha512-iUbTUwD4f4ibdLy5PWWb7ITEz4S4VAP9/mNjFhoRY3cKVVTDfmykrVTKjPOIHWzDgAmLtgrLvySIIC9ZBVENBw== + dependencies: + sha256-uint8array "^0.10.7" + "@tybys/wasm-util@^0.9.0": version "0.9.0" resolved "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz#3e75eb00604c8d6db470bf18c37b7d984a0e3355" @@ -5386,6 +5605,11 @@ abitype@0.7.1: resolved "https://registry.yarnpkg.com/abitype/-/abitype-0.7.1.tgz#16db20abe67de80f6183cf75f3de1ff86453b745" integrity sha512-VBkRHTDZf9Myaek/dO3yMmOzB/y2s3Zo6nVU7yaw1G+TvCHAjwaJzNGN9yo4K5D8bU/VZXKP1EJpRhFr862PlQ== +abitype@1.0.8, abitype@^1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.0.8.tgz#3554f28b2e9d6e9f35eb59878193eabd1b9f46ba" + integrity sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg== + accepts@~1.3.4, accepts@~1.3.5: version "1.3.8" resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" @@ -5943,6 +6167,11 @@ base-x@3.0.9, base-x@^3.0.2: dependencies: safe-buffer "^5.0.1" +base-x@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-4.0.1.tgz#817fb7b57143c501f649805cb247617ad016a885" + integrity sha512-uAZ8x6r6S3aUM9rbHGVOIsR15U/ZSc82b3ymnCPsT45Gk1DDvhDPdIgB5MrhirZWt+5K0EEPQH985kNqZgNPFw== + base-x@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/base-x/-/base-x-5.0.0.tgz#6d835ceae379130e1a4cb846a70ac4746f28ea9b" @@ -6258,6 +6487,13 @@ bs58@^4.0.0, bs58@^4.0.1: dependencies: base-x "^3.0.2" +bs58@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-5.0.0.tgz#865575b4d13c09ea2a84622df6c8cbeb54ffc279" + integrity sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ== + dependencies: + base-x "^4.0.0" + bs58@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/bs58/-/bs58-6.0.0.tgz#a2cda0130558535dd281a2f8697df79caaf425d8" @@ -6274,6 +6510,14 @@ bs58check@2.1.2, bs58check@^2.1.2: create-hash "^1.1.0" safe-buffer "^5.1.2" +bs58check@3.0.1, bs58check@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-3.0.1.tgz#2094d13720a28593de1cba1d8c4e48602fdd841c" + integrity sha512-hjuuJvoWEybo7Hn/0xOrczQKKEKD63WguEjlhLExYs2wUBcebDC1jDNK17eEAD2lYfw82d5ASC1d7K3SWszjaQ== + dependencies: + "@noble/hashes" "^1.2.0" + bs58 "^5.0.0" + bs58check@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/bs58check/-/bs58check-4.0.0.tgz#46cda52a5713b7542dcb78ec2efdf78f5bf1d23c" @@ -7218,6 +7462,13 @@ cross-fetch@^3.1.4: dependencies: node-fetch "^2.6.12" +cross-fetch@^3.1.5: + version "3.2.0" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.2.0.tgz#34e9192f53bc757d6614304d9e5e6fb4edb782e3" + integrity sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q== + dependencies: + node-fetch "^2.7.0" + cross-fetch@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz#f037aef1580bb3a1a35164ea2a848ba81b445983" @@ -8493,16 +8744,16 @@ eventemitter3@4.0.4: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.4.tgz#b5463ace635a083d018bdc7c917b4c5f10a85384" integrity sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ== +eventemitter3@5.0.1, eventemitter3@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== + eventemitter3@^4.0.4: version "4.0.7" resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== -eventemitter3@^5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" - integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== - events@3.3.0, events@^3.3.0: version "3.3.0" resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" @@ -9682,6 +9933,18 @@ hosted-git-info@^7.0.0, hosted-git-info@^7.0.2: dependencies: lru-cache "^10.0.1" +hpke-js@^1.2.7: + version "1.6.2" + resolved "https://registry.yarnpkg.com/hpke-js/-/hpke-js-1.6.2.tgz#4a3f132138ae80740adb49015123ebcaeca45546" + integrity sha512-HllaSHiEEd9bZ9L0F0hFERSu2iAWDptkzVUqxra3lTapSPP8Bnyc6J9Viwn/oktPLBXOJpX57lfXSHYsh4HLag== + dependencies: + "@hpke/chacha20poly1305" "^1.6.2" + "@hpke/common" "^1.7.2" + "@hpke/core" "^1.7.2" + "@hpke/dhkem-x25519" "^1.6.2" + "@hpke/dhkem-x448" "^1.6.2" + "@noble/hashes" "^1.7.1" + html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" @@ -10575,6 +10838,11 @@ isomorphic-ws@^5.0.0: resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz#e5529148912ecb9b451b46ed44d53dae1ce04bbf" integrity sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw== +isows@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.6.tgz#0da29d706fa51551c663c627ace42769850f86e7" + integrity sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw== + istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: version "3.2.0" resolved "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" @@ -11084,7 +11352,7 @@ jest-worker@^29.7.0: merge-stream "^2.0.0" supports-color "^8.0.0" -jest@^29.7.0: +jest@^29.0.0, jest@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== @@ -12450,6 +12718,9 @@ next-tick@^1.1.0: resolved "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== +next@^15.2.3, "next@file:./etc/noop": + version "0.0.1" + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -13034,6 +13305,19 @@ own-keys@^1.0.1: object-keys "^1.1.1" safe-push-apply "^1.0.0" +ox@0.6.9: + version "0.6.9" + resolved "https://registry.yarnpkg.com/ox/-/ox-0.6.9.tgz#da1ee04fa10de30c8d04c15bfb80fe58b1f554bd" + integrity sha512-wi5ShvzE4eOcTwQVsIPdFr+8ycyX+5le/96iAJutaZAvCes1J0+RvpEPg5QDPDiaR0XQQAvZVl7AwqQcINuUug== + dependencies: + "@adraffy/ens-normalize" "^1.10.1" + "@noble/curves" "^1.6.0" + "@noble/hashes" "^1.5.0" + "@scure/bip32" "^1.5.0" + "@scure/bip39" "^1.4.0" + abitype "^1.0.6" + eventemitter3 "5.0.1" + p-finally@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" @@ -14652,6 +14936,11 @@ sha.js@^2.4.0, sha.js@^2.4.11, sha.js@^2.4.8: inherits "^2.0.1" safe-buffer "^5.0.1" +sha256-uint8array@^0.10.7: + version "0.10.7" + resolved "https://registry.yarnpkg.com/sha256-uint8array/-/sha256-uint8array-0.10.7.tgz#c751fc914f4227b26d996980562065fa4eadcf99" + integrity sha512-1Q6JQU4tX9NqsDGodej6pkrUVQVNapLZnvkwIhddH/JqzBZF1fSaxSWNY6sziXBE8aEa2twtGkXUrwzGeZCMpQ== + shallow-clone@^3.0.0: version "3.0.1" resolved "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" @@ -15560,6 +15849,22 @@ ts-invariant@^0.10.3: dependencies: tslib "^2.1.0" +ts-jest@^29.0.0: + version "29.3.2" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.3.2.tgz#0576cdf0a507f811fe73dcd16d135ce89f8156cb" + integrity sha512-bJJkrWc6PjFVz5g2DGCNUo8z7oFEYaz1xP1NpeDU7KNLMWPpEyV8Chbpkn8xjzgRDpQhnGMyvyldoL7h8JXyug== + dependencies: + bs-logger "^0.2.6" + ejs "^3.1.10" + fast-json-stable-stringify "^2.1.0" + jest-util "^29.0.0" + json5 "^2.2.3" + lodash.memoize "^4.1.2" + make-error "^1.3.6" + semver "^7.7.1" + type-fest "^4.39.1" + yargs-parser "^21.1.1" + ts-jest@^29.2.5: version "29.2.6" resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.2.6.tgz#df53edf8b72fb89de032cfa310abf37582851d9a" @@ -15615,7 +15920,7 @@ ts-node@^10.4.0, ts-node@^10.8.1: v8-compile-cache-lib "^3.0.1" yn "3.1.1" -ts-node@^10.9.2: +ts-node@^10.9.1, ts-node@^10.9.2: version "10.9.2" resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== @@ -15802,6 +16107,11 @@ type-fest@^2.5.2: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== +type-fest@^4.39.1: + version "4.41.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.41.0.tgz#6ae1c8e5731273c2bf1f58ad39cbae2c91a46c58" + integrity sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA== + type-flag@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/type-flag/-/type-flag-3.0.0.tgz#2caef2f20f2c71e960fe1d3b6f57bae8c8246459" @@ -15909,6 +16219,11 @@ typescript@^4.6.4: resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz#a2a3d2756c079abda241d75f149df9d561091e78" integrity sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg== +typescript@^5.0.0: + version "5.8.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e" + integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== + typescript@^5.7.2: version "5.8.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.2.tgz#8170b3702f74b79db2e5a96207c15e65807999e4" @@ -16289,6 +16604,20 @@ vary@^1, vary@~1.1.2: resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== +viem@^2.21.35, viem@^2.28.1: + version "2.29.2" + resolved "https://registry.yarnpkg.com/viem/-/viem-2.29.2.tgz#e3033f68d6baf95c412593b213865e91634ac35e" + integrity sha512-cukRxab90jvQ+TDD84sU3qB3UmejYqgCw4cX8SfWzvh7JPfZXI3kAMUaT5OSR2As1Mgvx1EJawccwPjGqkSSwA== + dependencies: + "@noble/curves" "1.8.2" + "@noble/hashes" "1.7.2" + "@scure/bip32" "1.6.2" + "@scure/bip39" "1.5.4" + abitype "1.0.8" + isows "1.0.6" + ox "0.6.9" + ws "8.18.1" + vscode-oniguruma@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz#439bfad8fe71abd7798338d1cd3dc53a8beea94b" @@ -16924,6 +17253,11 @@ ws@8.17.1: resolved "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== +ws@8.18.1: + version "8.18.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.1.tgz#ea131d3784e1dfdff91adb0a4a116b127515e3cb" + integrity sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w== + ws@^7, ws@^7.2.0, ws@^7.4.0, ws@^7.5.1: version "7.5.9" resolved "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591"