Skip to content

Commit

Permalink
Merge pull request #29 from argentlabs/chore/housekeeping
Browse files Browse the repository at this point in the history
Chore/housekeeping
  • Loading branch information
bluecco authored Nov 13, 2023
2 parents 0bbd516 + 504a6a7 commit c5c424a
Show file tree
Hide file tree
Showing 15 changed files with 118 additions and 71 deletions.
16 changes: 3 additions & 13 deletions src/connectors/argentMobile/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,26 +103,16 @@ export class ArgentMobileConnector extends Connector {
this._wallet = null
}

async account(): Promise<AccountInterface | null> {
/*
Don't throw an exception if the wallet is not connected.
This is needed because when argentMobile and webwallet connectors are used together with starknet-react,
it would always try to retrieve the account since the connectors are always available (and throw an exception since the value is null)
https://github.com/apibara/starknet-react/blob/226e4cb1d8e9b478dc57d45a98a59a57733572bb/packages/core/src/hooks/useAccount.ts#L92
*/
async account(): Promise<AccountInterface> {
if (!this._wallet || !this._wallet.account) {
return null
throw new ConnectorNotConnectedError()
}

return this._wallet.account as AccountInterface
}

async chainId(): Promise<bigint> {
this.ensureWallet()

if (!this._wallet) {
if (!this._wallet || !this.wallet.account || !this._wallet.provider) {
throw new ConnectorNotConnectedError()
}

Expand Down
2 changes: 1 addition & 1 deletion src/connectors/argentMobile/modal/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export abstract class NamespaceAdapter {
}: SessionTypes.Struct) => {
const chain = this.formatChainId(this.chainId)
if (requiredNamespaces) {
return !!requiredNamespaces[this.namespace]?.chains.includes(chain)
return !!requiredNamespaces[this.namespace]?.chains?.includes(chain)
}
return !!namespaces?.[this.namespace]?.accounts.some((account) =>
account.startsWith(chain),
Expand Down
3 changes: 2 additions & 1 deletion src/connectors/argentMobile/modal/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ export type { StarknetWindowObject, IArgentLoginOptions }

export const getStarknetWindowObject = async (
options: IArgentLoginOptions,
): Promise<ConnectedStarknetWindowObject> => login(options, StarknetAdapter)
): Promise<ConnectedStarknetWindowObject | null> =>
login(options, StarknetAdapter)
10 changes: 9 additions & 1 deletion src/connectors/argentMobile/modal/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,14 @@ export const login = async <TAdapter extends NamespaceAdapter>(
walletConnect,
}: IArgentLoginOptions,
Adapter: new (options: NamespaceAdapterOptions) => TAdapter,
): Promise<TAdapter> => {
): Promise<TAdapter | null> => {
if (!bridgeUrl) {
throw new Error("bridgeUrl is required")
}

if (!mobileUrl) {
throw new Error("mobileUrl is required")
}
argentModal.bridgeUrl = bridgeUrl
argentModal.mobileUrl = mobileUrl
argentModal.type = modalType
Expand Down Expand Up @@ -101,6 +108,7 @@ export const login = async <TAdapter extends NamespaceAdapter>(
} catch (error) {
console.error("@argent/login::error")
argentModal.closeModal()
return null
}
}

Expand Down
6 changes: 2 additions & 4 deletions src/connectors/injected/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,5 @@ export const ARGENT_X_ICON = `data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhl

// Icons used when the injected wallet is not found and no icon is provided.
// question-mark-circle from heroicons with color changed to black/white.
export const WALLET_NOT_FOUND_ICON_LIGHT =
"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCAyNCAyNCIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZT0iYmxhY2siPgogIDxwYXRoIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgZD0iTTkuODc5IDcuNTE5YzEuMTcxLTEuMDI1IDMuMDcxLTEuMDI1IDQuMjQyIDAgMS4xNzIgMS4wMjUgMS4xNzIgMi42ODcgMCAzLjcxMi0uMjAzLjE3OS0uNDMuMzI2LS42Ny40NDItLjc0NS4zNjEtMS40NS45OTktMS40NSAxLjgyN3YuNzVNMjEgMTJhOSA5IDAgMTEtMTggMCA5IDkgMCAwMTE4IDB6bS05IDUuMjVoLjAwOHYuMDA4SDEydi0uMDA4eiIgLz4KPC9zdmc+"
export const WALLET_NOT_FOUND_ICON_DARK =
"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCAyNCAyNCIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZT0id2hpdGUiPgogIDxwYXRoIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgZD0iTTkuODc5IDcuNTE5YzEuMTcxLTEuMDI1IDMuMDcxLTEuMDI1IDQuMjQyIDAgMS4xNzIgMS4wMjUgMS4xNzIgMi42ODcgMCAzLjcxMi0uMjAzLjE3OS0uNDMuMzI2LS42Ny40NDItLjc0NS4zNjEtMS40NS45OTktMS40NSAxLjgyN3YuNzVNMjEgMTJhOSA5IDAgMTEtMTggMCA5IDkgMCAwMTE4IDB6bS05IDUuMjVoLjAwOHYuMDA4SDEydi0uMDA4eiIgLz4KPC9zdmc+Cg=="
export const WALLET_NOT_FOUND_ICON_LIGHT = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCAyNCAyNCIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZT0iYmxhY2siPgogIDxwYXRoIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgZD0iTTkuODc5IDcuNTE5YzEuMTcxLTEuMDI1IDMuMDcxLTEuMDI1IDQuMjQyIDAgMS4xNzIgMS4wMjUgMS4xNzIgMi42ODcgMCAzLjcxMi0uMjAzLjE3OS0uNDMuMzI2LS42Ny40NDItLjc0NS4zNjEtMS40NS45OTktMS40NSAxLjgyN3YuNzVNMjEgMTJhOSA5IDAgMTEtMTggMCA5IDkgMCAwMTE4IDB6bS05IDUuMjVoLjAwOHYuMDA4SDEydi0uMDA4eiIgLz4KPC9zdmc+`
export const WALLET_NOT_FOUND_ICON_DARK = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCAyNCAyNCIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZT0id2hpdGUiPgogIDxwYXRoIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgZD0iTTkuODc5IDcuNTE5YzEuMTcxLTEuMDI1IDMuMDcxLTEuMDI1IDQuMjQyIDAgMS4xNzIgMS4wMjUgMS4xNzIgMi42ODcgMCAzLjcxMi0uMjAzLjE3OS0uNDMuMzI2LS42Ny40NDItLjc0NS4zNjEtMS40NS45OTktMS40NSAxLjgyN3YuNzVNMjEgMTJhOSA5IDAgMTEtMTggMCA5IDkgMCAwMTE4IDB6bS05IDUuMjVoLjAwOHYuMDA4SDEydi0uMDA4eiIgLz4KPC9zdmc+Cg==`
65 changes: 52 additions & 13 deletions src/connectors/injected/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { StarknetWindowObject } from "get-starknet-core"
import { getStarknet } from "get-starknet-core"
import { AccountInterface, constants } from "starknet"
import {
ConnectorNotConnectedError,
Expand Down Expand Up @@ -36,12 +35,14 @@ export class InjectedConnector extends Connector {
}

available(): boolean {
// This should be awaited ideally but it would break compatibility with
// starknet-react. Do we need to make this async? Is ensureWallet needed?
this.ensureWallet()
return this._wallet !== undefined
}

async ready(): Promise<boolean> {
await this.ensureWallet()
this.ensureWallet()

if (!this._wallet) {
return false
Expand Down Expand Up @@ -149,7 +150,7 @@ export class InjectedConnector extends Connector {
}

async disconnect(): Promise<void> {
await this.ensureWallet()
this.ensureWallet()

if (!this.available()) {
throw new ConnectorNotFoundError()
Expand All @@ -160,17 +161,13 @@ export class InjectedConnector extends Connector {
}
}

async account(): Promise<AccountInterface | null> {
await this.ensureWallet()
async account(): Promise<AccountInterface> {
this.ensureWallet()

if (!this._wallet) {
if (!this._wallet || !this._wallet.account) {
throw new ConnectorNotConnectedError()
}

if (!this._wallet.account) {
return null
}

return this._wallet.account
}

Expand Down Expand Up @@ -210,12 +207,54 @@ export class InjectedConnector extends Connector {
return this._wallet
}

private async ensureWallet() {
const starknet = getStarknet()
const installed = await starknet.getAvailableWallets()
private ensureWallet() {
const installed = getAvailableWallets(globalThis)
const wallet = installed.filter((w) => w.id === this._options.id)[0]
if (wallet) {
this._wallet = wallet
}
}
}

function getAvailableWallets(obj: Record<string, any>): StarknetWindowObject[] {
return Object.values(
Object.getOwnPropertyNames(obj).reduce<
Record<string, StarknetWindowObject>
>((wallets, key) => {
if (key.startsWith("starknet")) {
const wallet = obj[key]

if (isWalletObject(wallet) && !wallets[wallet.id]) {
wallets[wallet.id] = wallet as StarknetWindowObject
}
}
return wallets
}, {}),
)
}

// biome-ignore lint: wallet could be anything
function isWalletObject(wallet: any): boolean {
try {
return (
wallet &&
[
// wallet's must have methods/members, see IStarknetWindowObject
"request",
"isConnected",
"provider",
"enable",
"isPreauthorized",
"on",
"off",
"version",
"id",
"name",
"icon",
].every((key) => key in wallet)
)
} catch (err) {
/* empty */
}
return false
}
2 changes: 1 addition & 1 deletion src/connectors/webwallet/helpers/trpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const setPopupOptions = ({
: parentTop + parentHeight / 2 - height / 2

popupOrigin = origin ?? popupOrigin
popupLocation = location ?? location
popupLocation = location ?? popupLocation
popupParams = `width=${width},height=${height},top=${y},left=${x},toolbar=no,menubar=no,scrollbars=no,location=no,status=no,popup=1`
}

Expand Down
22 changes: 6 additions & 16 deletions src/connectors/webwallet/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
UserNotConnectedError,
UserRejectedRequestError,
} from "../../errors"
import { DEFAULT_WEBWALLET_URL } from "./constants"
import { DEFAULT_WEBWALLET_ICON, DEFAULT_WEBWALLET_URL } from "./constants"
import { getWebWalletStarknetObject } from "./starknetWindowObject/getWebWalletStarknetObject"

let _wallet: StarknetWindowObject | null = null
Expand Down Expand Up @@ -60,8 +60,8 @@ export class WebWalletConnector extends Connector {

get icon(): ConnectorIcons {
return {
light: DEFAULT_WEBWALLET_URL,
dark: DEFAULT_WEBWALLET_URL,
light: DEFAULT_WEBWALLET_ICON,
dark: DEFAULT_WEBWALLET_ICON,
}
}

Expand Down Expand Up @@ -120,28 +120,18 @@ export class WebWalletConnector extends Connector {
this._wallet = _wallet
}

/*
Don't throw an exception if the wallet is not connected.
This is needed because when argentMobile and webwallet connectors are used together with starknet-react,
it would always try to retrieve the account since the connectors are always available (and throw an exception since the value is null)
https://github.com/apibara/starknet-react/blob/226e4cb1d8e9b478dc57d45a98a59a57733572bb/packages/core/src/hooks/useAccount.ts#L92
*/
async account(): Promise<AccountInterface | null> {
async account(): Promise<AccountInterface> {
this._wallet = _wallet

if (!this._wallet || !this._wallet.account) {
return null
throw new ConnectorNotConnectedError()
}

return this._wallet.account as unknown as AccountInterface
}

async chainId(): Promise<bigint> {
this.ensureWallet()

if (!this._wallet) {
if (!this._wallet || !this.wallet.account || !this._wallet.provider) {
throw new ConnectorNotConnectedError()
}

Expand Down
6 changes: 5 additions & 1 deletion src/connectors/webwallet/starknetWindowObject/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,11 @@ export class MessageAccount extends Account implements AccountInterface {
height: EXECUTE_POPUP_HEIGHT,
location: "/review",
})
if (calls[0] && calls[0].entrypoint === "use_offchain_session") {
if (
Array.isArray(calls) &&
calls[0] &&
calls[0].entrypoint === "use_offchain_session"
) {
setPopupOptions({
width: 1,
height: 1,
Expand Down
19 changes: 13 additions & 6 deletions src/helpers/mapModalWallets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { StarknetWindowObject, WalletProvider } from "get-starknet-core"
import { Connector } from "../connectors/connector"
import { ARGENT_X_ICON } from "../connectors/injected/constants"
import type { ModalWallet, StoreVersion } from "../types/modal"
import { isString } from "lodash-es"

interface SetConnectorsExpandedParams {
availableConnectors: Connector[]
Expand All @@ -25,13 +26,15 @@ export const mapModalWallets = ({
}

return availableConnectors
.map((c) => {
.map<ModalWallet | null>((c) => {
const installed = installedWallets.find((w) => w.id === c.id)
if (installed) {
const installedIcon =
installed.id === "argentX" ? ARGENT_X_ICON : installed.icon
return {
name: installed.name,
id: installed.id,
icon: installed.id === "argentX" ? ARGENT_X_ICON : installed.icon,
icon: { light: installedIcon, dark: installedIcon },
connector: c,
}
}
Expand All @@ -44,10 +47,13 @@ export const mapModalWallets = ({

if (discovery) {
const { downloads } = discovery

const discoveryIcon =
discovery.id === "argentX" ? ARGENT_X_ICON : discovery.icon
return {
name: discovery.name,
id: discovery.id,
icon: discovery.id === "argentX" ? ARGENT_X_ICON : discovery.icon,
icon: { light: discoveryIcon, dark: discoveryIcon },
connector: c,
download: downloads[storeVersion as keyof typeof downloads],
}
Expand All @@ -62,9 +68,10 @@ export const mapModalWallets = ({
id: c.id,
icon: c.icon,
connector: c,
title: "title" in c ? c.title : undefined,
subtitle: "subtitle" in c ? c.subtitle : undefined,
title: "title" in c && isString(c.title) ? c.title : undefined,
subtitle:
"subtitle" in c && isString(c.subtitle) ? c.subtitle : undefined,
}
})
.filter((c) => c !== null) as ModalWallet[]
.filter((c): c is ModalWallet => c !== null)
}
14 changes: 10 additions & 4 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ export const connect = async ({
if (wallet) {
const connector = availableConnectors.find((c) => c.id === lastWalletId)
await connector?.connect()
selectedConnector = connector
if (connector) {
selectedConnector = connector
}
return wallet
} // otherwise fallback to modal
}
Expand All @@ -93,11 +95,13 @@ export const connect = async ({
dappName,
callback: async (value: StarknetWindowObject | null) => {
try {
if (value.id !== "argentWebWallet") {
if (value !== null && value.id !== "argentWebWallet") {
setStarknetLastConnectedWallet(value.id)
}
selectedConnector =
availableConnectors.find((c) => c.id === value.id) ?? null
availableConnectors.find(
(c) => value !== null && c.id === value.id,
) ?? null
resolve(value)
} finally {
setTimeout(() => modal.$destroy())
Expand All @@ -116,7 +120,9 @@ export const getSelectedConnectorWallet = () =>

export const disconnect = async (options: DisconnectOptions = {}) => {
removeStarknetLastConnectedWallet()
await selectedConnector.disconnect()
if (selectedConnector) {
await selectedConnector.disconnect()
}
selectedConnector = null

return sn.disconnect(options)
Expand Down
10 changes: 6 additions & 4 deletions src/modal/ConnectorButton.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
import type { Connector } from "../connectors/connector"
export let wallet: ModalWallet
export let theme: "light" | "dark" | null = null
export let cb: (value: Connector | null) => Promise<void> = async () => {}
export let loadingItem: string | false = false
const isSvg = wallet.icon.startsWith("<svg")
const icon = theme === "dark" ? wallet.icon.dark : wallet.icon.light
const isSvg = icon?.startsWith("<svg")
</script>

{#if wallet.download}
Expand Down Expand Up @@ -36,7 +38,7 @@
<p class="font-semibold text-base p">
Install {wallet.name}
</p>
<img alt={wallet.name} src={wallet.icon} class="w-8 h-8 rounded-full" />
<img alt={wallet.name} src={icon} class="w-8 h-8 rounded-full" />
</li>
</a>
{:else}
Expand Down Expand Up @@ -91,9 +93,9 @@
<span class="sr-only">Loading...</span>
</div>
{:else if isSvg}
<div style="position: relative;">{@html wallet.icon}</div>
<div style="position: relative;">{@html icon}</div>
{:else}
<img alt={wallet?.name} src={wallet?.icon} class="w-8 h-8 rounded" />
<img alt={wallet?.name} src={icon} class="w-8 h-8 rounded" />
{/if}
</li>
{/if}
Loading

0 comments on commit c5c424a

Please sign in to comment.