Skip to content

Commit

Permalink
chore: isolate more util functions
Browse files Browse the repository at this point in the history
  • Loading branch information
dominik-stumpf committed Jul 26, 2024
1 parent 0cc2356 commit 04201c3
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 112 deletions.
2 changes: 1 addition & 1 deletion src/hooks/useSubmit/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { default, sign, useSubmitWithSign } from "./useSubmit"
export { default, useSubmitWithSign } from "./useSubmit"
export type { SignedValidation, Validation } from "./types"
109 changes: 1 addition & 108 deletions src/hooks/useSubmit/useSubmit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,11 @@ import useLocalStorage from "hooks/useLocalStorage"
import useTimeInaccuracy from "hooks/useTimeInaccuracy"
import { useCallback, useState } from "react"
import useSWR from "swr"
import { ValidationMethod } from "types"
import { UnauthorizedProviderError, createPublicClient, trim } from "viem"
import { useChainId, usePublicClient, useWalletClient } from "wagmi"
import { wagmiConfig } from "wagmiConfig"
import { Chain, Chains, supportedChains } from "wagmiConfig/chains"
import { DEFAULT_MESSAGE, DEFAULT_SIGN_LOADING_TEXT } from "./constants"
import { fuelSign } from "./fuelSign"
import { SignProps, UseSubmitOptions, Validation } from "./types"
import { createMessageParams, getMessage, signWithKeyPair } from "./utils"
import { getMessage, sign } from "./utils"
import gnosisSafeSignCallback from "./utils/gnosisSafeSignCallback"

type FetcherFunction<ResponseType> = ({
Expand Down Expand Up @@ -208,108 +204,5 @@ const useSubmitWithSign = <ResponseType>(
})
}

const chainsOfAddressWithDeployedContract = async (
address: `0x${string}`
): Promise<Chain[]> => {
const LOCALSTORAGE_KEY = `chainsWithByteCode_${address.toLowerCase()}`
const chainsWithByteCodeFromLocalstorage = localStorage.getItem(LOCALSTORAGE_KEY)

if (chainsWithByteCodeFromLocalstorage) {
const parsed = JSON.parse(chainsWithByteCodeFromLocalstorage)

if (Array.isArray(parsed))
return parsed.filter((c) => supportedChains.includes(c))
}

const res = await Promise.all(
wagmiConfig.chains.map(async (chain) => {
const publicClient = createPublicClient({
chain,
transport: wagmiConfig._internal.transports[chain.id],
})

const bytecode = await publicClient
.getBytecode({
address,
})
.catch(() => null)

return [Chains[chain.id], bytecode && trim(bytecode) !== "0x"] as const
})
).then((results) => [
...new Set(
results
.filter(([, hasContract]) => !!hasContract)
.map(([chainName]) => chainName as Chain)
),
])

localStorage.setItem(LOCALSTORAGE_KEY, JSON.stringify(res))

return res
}

export const sign = async ({
walletClient,
address,
payload,
chainId,
keyPair,
forcePrompt,
msg = DEFAULT_MESSAGE,
ts,
getMessageToSign = getMessage,
}: SignProps): Promise<[string, Validation]> => {
const params = createMessageParams(address, ts ?? Date.now(), msg, payload)
let sig = null

if (!!keyPair && !forcePrompt) {
params.method = ValidationMethod.KEYPAIR
sig = await signWithKeyPair(keyPair, params)
} else {
const walletChains = await chainsOfAddressWithDeployedContract(address)
const walletChainId =
walletChains.length > 0 ? Chains[walletChains[0]] : undefined

if (walletChainId) {
if (walletClient.chain.id !== walletChainId) {
await walletClient.switchChain({ id: walletChainId })
}
params.chainId = `${walletChainId}`
}

const isSmartContract = walletChains.length > 0

params.method = isSmartContract
? ValidationMethod.EIP1271
: ValidationMethod.STANDARD

params.chainId ||= chainId || `${walletClient.chain.id}`

if (walletClient?.account?.type === "local") {
// For local accounts, such as CWaaS, we request the signature on the account. Otherwise it sends a personal_sign to the rpc
sig = await walletClient.account.signMessage({
message: getMessageToSign(params),
})
} else {
sig = await walletClient
.signMessage({
account: address,
message: getMessageToSign(params),
})
.catch((error) => {
if (error instanceof UnauthorizedProviderError) {
throw new Error(
"Your wallet is not connected. It might be because your browser locked it after a period of time."
)
}
throw error
})
}
}

return [payload, { params, sig }]
}

export default useSubmit
export { useSubmitWithSign, useSubmitWithSignWithParamKeyPair }
117 changes: 115 additions & 2 deletions src/hooks/useSubmit/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import randomBytes from "randombytes"
import { keccak256, stringToBytes } from "viem"
import { MessageParams } from "./types"
import { ValidationMethod } from "types"
import {
UnauthorizedProviderError,
createPublicClient,
keccak256,
stringToBytes,
trim,
} from "viem"
import { wagmiConfig } from "wagmiConfig"
import { Chain, Chains, supportedChains } from "wagmiConfig/chains"
import { DEFAULT_MESSAGE } from "./constants"
import { MessageParams, SignProps, Validation } from "./types"

export const signWithKeyPair = (keyPair: CryptoKeyPair, params: MessageParams) =>
window.crypto.subtle
Expand Down Expand Up @@ -38,3 +48,106 @@ export const createMessageParams = (
msg,
chainId: undefined,
})

export const sign = async ({
walletClient,
address,
payload,
chainId,
keyPair,
forcePrompt,
msg = DEFAULT_MESSAGE,
ts,
getMessageToSign = getMessage,
}: SignProps): Promise<[string, Validation]> => {
const params = createMessageParams(address, ts ?? Date.now(), msg, payload)
let sig = null

if (!!keyPair && !forcePrompt) {
params.method = ValidationMethod.KEYPAIR
sig = await signWithKeyPair(keyPair, params)
} else {
const walletChains = await chainsOfAddressWithDeployedContract(address)
const walletChainId =
walletChains.length > 0 ? Chains[walletChains[0]] : undefined

if (walletChainId) {
if (walletClient.chain.id !== walletChainId) {
await walletClient.switchChain({ id: walletChainId })
}
params.chainId = `${walletChainId}`
}

const isSmartContract = walletChains.length > 0

params.method = isSmartContract
? ValidationMethod.EIP1271
: ValidationMethod.STANDARD

params.chainId ||= chainId || `${walletClient.chain.id}`

if (walletClient?.account?.type === "local") {
// For local accounts, such as CWaaS, we request the signature on the account. Otherwise it sends a personal_sign to the rpc
sig = await walletClient.account.signMessage({
message: getMessageToSign(params),
})
} else {
sig = await walletClient
.signMessage({
account: address,
message: getMessageToSign(params),
})
.catch((error) => {
if (error instanceof UnauthorizedProviderError) {
throw new Error(
"Your wallet is not connected. It might be because your browser locked it after a period of time."
)
}
throw error
})
}
}

return [payload, { params, sig }]
}

export const chainsOfAddressWithDeployedContract = async (
address: `0x${string}`
): Promise<Chain[]> => {
const LOCALSTORAGE_KEY = `chainsWithByteCode_${address.toLowerCase()}`
const chainsWithByteCodeFromLocalstorage = localStorage.getItem(LOCALSTORAGE_KEY)

if (chainsWithByteCodeFromLocalstorage) {
const parsed = JSON.parse(chainsWithByteCodeFromLocalstorage)

if (Array.isArray(parsed))
return parsed.filter((c) => supportedChains.includes(c))
}

const res = await Promise.all(
wagmiConfig.chains.map(async (chain) => {
const publicClient = createPublicClient({
chain,
transport: wagmiConfig._internal.transports[chain.id],
})

const bytecode = await publicClient
.getBytecode({
address,
})
.catch(() => null)

return [Chains[chain.id], bytecode && trim(bytecode) !== "0x"] as const
})
).then((results) => [
...new Set(
results
.filter(([, hasContract]) => !!hasContract)
.map(([chainName]) => chainName as Chain)
),
])

localStorage.setItem(LOCALSTORAGE_KEY, JSON.stringify(res))

return res
}
2 changes: 1 addition & 1 deletion src/utils/fetcher.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { env } from "env"
import { fuelSign } from "hooks/useSubmit/fuelSign"
import { FuelSignProps, SignProps } from "hooks/useSubmit/types"
import { sign } from "hooks/useSubmit/useSubmit"
import { sign } from "hooks/useSubmit/utils"
import { pushToIntercomSetting } from "./intercom"

const SIG_HEADER_NAME = "x-guild-sig"
Expand Down

0 comments on commit 04201c3

Please sign in to comment.