Skip to content

Commit

Permalink
chore: add clientId to BlockchainAPI requests (#2521)
Browse files Browse the repository at this point in the history
  • Loading branch information
tomiir authored Jul 18, 2024
1 parent daa678c commit 0ae4697
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 30 deletions.
2 changes: 1 addition & 1 deletion packages/core/src/controllers/ApiController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { OptionsController } from './OptionsController.js'

// -- Helpers ------------------------------------------- //
const baseUrl = CoreHelperUtil.getApiUrl()
export const api = new FetchUtil({ baseUrl })
export const api = new FetchUtil({ baseUrl, clientId: null })
const entries = '40'
const recommendedEntries = '4'

Expand Down
52 changes: 34 additions & 18 deletions packages/core/src/controllers/BlockchainApiController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import type {
BlockchainApiRegisterNameParams
} from '../utils/TypeUtil.js'
import { OptionsController } from './OptionsController.js'
import { proxy } from 'valtio/vanilla'

const DEFAULT_OPTIONS = {
purchaseCurrencies: [
Expand Down Expand Up @@ -108,15 +109,25 @@ const DEFAULT_OPTIONS = {
]
}

export interface BlockchainApiControllerState {
clientId: string | null
api: FetchUtil
}

// -- Helpers ------------------------------------------- //
const baseUrl = CoreHelperUtil.getBlockchainApiUrl()

const api = new FetchUtil({ baseUrl })
// -- State --------------------------------------------- //
const state = proxy<BlockchainApiControllerState>({
clientId: null,
api: new FetchUtil({ baseUrl, clientId: null })
})

// -- Controller ---------------------------------------- //
export const BlockchainApiController = {
state,
fetchIdentity({ address }: BlockchainApiIdentityRequest) {
return api.get<BlockchainApiIdentityResponse>({
return state.api.get<BlockchainApiIdentityResponse>({
path: `/v1/identity/${address}`,
params: {
projectId: OptionsController.state.projectId
Expand All @@ -134,7 +145,7 @@ export const BlockchainApiController = {
}: BlockchainApiTransactionsRequest) {
const queryParams = cursor ? { cursor } : {}

return api.get<BlockchainApiTransactionsResponse>({
return state.api.get<BlockchainApiTransactionsResponse>({
path: `/v1/account/${account}/history?projectId=${projectId}${
onramp ? `&onramp=${onramp}` : ''
}`,
Expand All @@ -152,7 +163,7 @@ export const BlockchainApiController = {
to,
gasPrice
}: BlockchainApiSwapQuoteRequest) {
return api.get<BlockchainApiSwapQuoteResponse>({
return state.api.get<BlockchainApiSwapQuoteResponse>({
path: `/v1/convert/quotes`,
headers: {
'Content-Type': 'application/json'
Expand All @@ -169,13 +180,13 @@ export const BlockchainApiController = {
},

fetchSwapTokens({ projectId, chainId }: BlockchainApiSwapTokensRequest) {
return api.get<BlockchainApiSwapTokensResponse>({
return state.api.get<BlockchainApiSwapTokensResponse>({
path: `/v1/convert/tokens?projectId=${projectId}&chainId=${chainId}`
})
},

fetchTokenPrice({ projectId, addresses }: BlockchainApiTokenPriceRequest) {
return api.post<BlockchainApiTokenPriceResponse>({
return state.api.post<BlockchainApiTokenPriceResponse>({
path: '/v1/fungible/price',
body: {
projectId,
Expand All @@ -191,7 +202,7 @@ export const BlockchainApiController = {
fetchSwapAllowance({ projectId, tokenAddress, userAddress }: BlockchainApiSwapAllowanceRequest) {
const { sdkType, sdkVersion } = OptionsController.state

return api.get<BlockchainApiSwapAllowanceResponse>({
return state.api.get<BlockchainApiSwapAllowanceResponse>({
path: `/v1/convert/allowance?projectId=${projectId}&tokenAddress=${tokenAddress}&userAddress=${userAddress}`,
headers: {
'Content-Type': 'application/json',
Expand All @@ -204,7 +215,7 @@ export const BlockchainApiController = {
fetchGasPrice({ projectId, chainId }: BlockchainApiGasPriceRequest) {
const { sdkType, sdkVersion } = OptionsController.state

return api.get<BlockchainApiGasPriceResponse>({
return state.api.get<BlockchainApiGasPriceResponse>({
path: `/v1/convert/gas-price`,
headers: {
'Content-Type': 'application/json',
Expand All @@ -225,7 +236,7 @@ export const BlockchainApiController = {
to,
userAddress
}: BlockchainApiGenerateSwapCalldataRequest) {
return api.post<BlockchainApiGenerateSwapCalldataResponse>({
return state.api.post<BlockchainApiGenerateSwapCalldataResponse>({
path: '/v1/convert/build-transaction',
headers: {
'Content-Type': 'application/json'
Expand All @@ -251,7 +262,7 @@ export const BlockchainApiController = {
}: BlockchainApiGenerateApproveCalldataRequest) {
const { sdkType, sdkVersion } = OptionsController.state

return api.get<BlockchainApiGenerateApproveCalldataResponse>({
return state.api.get<BlockchainApiGenerateApproveCalldataResponse>({
path: `/v1/convert/build-approve`,
headers: {
'Content-Type': 'application/json',
Expand All @@ -270,7 +281,7 @@ export const BlockchainApiController = {
async getBalance(address: string, chainId?: string, forceUpdate?: string) {
const { sdkType, sdkVersion } = OptionsController.state

return api.get<BlockchainApiBalanceResponse>({
return state.api.get<BlockchainApiBalanceResponse>({
path: `/v1/account/${address}/balance`,
headers: {
'x-sdk-type': sdkType,
Expand All @@ -286,19 +297,19 @@ export const BlockchainApiController = {
},

async lookupEnsName(name: string) {
return api.get<BlockchainApiLookupEnsName>({
return state.api.get<BlockchainApiLookupEnsName>({
path: `/v1/profile/account/${name}${CommonConstantsUtil.WC_NAME_SUFFIX}?projectId=${OptionsController.state.projectId}`
})
},

async reverseLookupEnsName({ address }: { address: string }) {
return api.get<BlockchainApiLookupEnsName[]>({
return state.api.get<BlockchainApiLookupEnsName[]>({
path: `/v1/profile/reverse/${address}?projectId=${OptionsController.state.projectId}`
})
},

async getEnsNameSuggestions(name: string) {
return api.get<BlockchainApiSuggestionResponse>({
return state.api.get<BlockchainApiSuggestionResponse>({
path: `/v1/profile/suggestions/${name}?projectId=${OptionsController.state.projectId}`
})
},
Expand All @@ -309,7 +320,7 @@ export const BlockchainApiController = {
message,
signature
}: BlockchainApiRegisterNameParams) {
return api.post({
return state.api.post({
path: `/v1/profile/account`,
body: { coin_type: coinType, address, message, signature },
headers: {
Expand All @@ -325,7 +336,7 @@ export const BlockchainApiController = {
purchaseAmount,
paymentAmount
}: GenerateOnRampUrlArgs) {
const response = await api.post<{ url: string }>({
const response = await state.api.post<{ url: string }>({
path: `/v1/generators/onrampurl?projectId=${OptionsController.state.projectId}`,
body: {
destinationWallets,
Expand All @@ -342,7 +353,7 @@ export const BlockchainApiController = {

async getOnrampOptions() {
try {
const response = await api.get<{
const response = await state.api.get<{
paymentCurrencies: PaymentCurrency[]
purchaseCurrencies: PurchaseCurrency[]
}>({
Expand All @@ -357,7 +368,7 @@ export const BlockchainApiController = {

async getOnrampQuote({ purchaseCurrency, paymentCurrency, amount, network }: GetQuoteArgs) {
try {
const response = await api.post<OnrampQuote>({
const response = await state.api.post<OnrampQuote>({
path: `/v1/onramp/quote?projectId=${OptionsController.state.projectId}`,
body: {
purchaseCurrency,
Expand All @@ -379,5 +390,10 @@ export const BlockchainApiController = {
quoteId: 'mocked-quote-id'
}
}
},

setClientId(clientId: string | null) {
state.clientId = clientId
state.api = new FetchUtil({ baseUrl, clientId })
}
}
2 changes: 1 addition & 1 deletion packages/core/src/controllers/EventsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { OptionsController } from './OptionsController.js'

// -- Helpers ------------------------------------------- //
const baseUrl = CoreHelperUtil.getAnalyticsUrl()
const api = new FetchUtil({ baseUrl })
const api = new FetchUtil({ baseUrl, clientId: null })
const excluded = ['MODAL_CREATED']

// -- Types --------------------------------------------- //
Expand Down
8 changes: 7 additions & 1 deletion packages/core/src/utils/FetchUtil.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// -- Types ----------------------------------------------------------------------
interface Options {
baseUrl: string
clientId: string | null
}

interface RequestArguments {
Expand Down Expand Up @@ -31,9 +32,11 @@ async function fetchData(...args: Parameters<typeof fetch>) {
// -- Utility --------------------------------------------------------------------
export class FetchUtil {
public baseUrl: Options['baseUrl']
public clientId: Options['clientId']

public constructor({ baseUrl }: Options) {
public constructor({ baseUrl, clientId }: Options) {
this.baseUrl = baseUrl
this.clientId = clientId
}

public async get<T>({ headers, signal, cache, ...args }: RequestArguments) {
Expand Down Expand Up @@ -95,6 +98,9 @@ export class FetchUtil {
}
})
}
if (this.clientId) {
url.searchParams.append('clientId', this.clientId)
}

return url
}
Expand Down
32 changes: 23 additions & 9 deletions packages/ethers/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,12 @@ export class Web3Modal extends Web3ModalScaffold {
onUri(uri)
})

// When connecting through walletconnect, we need to set the clientId in the store
const clientId = await WalletConnectProvider.signer?.client?.core?.crypto?.getClientId()
if (clientId) {
this.setClientId(clientId)
}

const params = await siweConfig?.getMessageParams?.()
// Must perform these checks to satify optional types
if (siweConfig?.options?.enabled && params && Object.keys(params || {}).length > 0) {
Expand Down Expand Up @@ -270,6 +276,8 @@ export class Web3Modal extends Web3ModalScaffold {
info: Info
provider: Provider
}) => {
// If connecting with something else than walletconnect, we need to clear the clientId in the store
this.setClientId(null)
if (id === ConstantsUtil.INJECTED_CONNECTOR_ID) {
const InjectedProvider = ethersConfig.injected
if (!InjectedProvider) {
Expand Down Expand Up @@ -324,7 +332,11 @@ export class Web3Modal extends Web3ModalScaffold {
},

disconnect: async () => {
const { provider, providerType } = EthersStoreUtil.state
const provider = EthersStoreUtil.state.provider
const providerType = EthersStoreUtil.state.providerType
localStorage.removeItem(EthersConstantsUtil.WALLET_ID)
EthersStoreUtil.reset()
this.setClientId(null)
if (siweConfig?.options?.signOutOnDisconnect) {
const { SIWEController } = await import('@web3modal/siwe')
await SIWEController.signOut()
Expand Down Expand Up @@ -632,7 +644,7 @@ export class Web3Modal extends Web3ModalScaffold {

localStorage.removeItem(EthersConstantsUtil.WALLET_ID)
EthersStoreUtil.reset()

this.setClientId(null)
if (providerType === ConstantsUtil.AUTH_CONNECTOR_ID) {
await this.authProvider?.disconnect()
} else if (provider && (providerType === 'injected' || providerType === 'eip6963')) {
Expand Down Expand Up @@ -661,16 +673,18 @@ export class Web3Modal extends Web3ModalScaffold {
}

private async initWalletConnectProvider() {
const rpcMap = this.chains
? this.chains.reduce<Record<number, string>>((map, chain) => {
map[chain.chainId] = chain.rpcUrl

return map
}, {})
: ({} as Record<number, string>)

const walletConnectProviderOptions: EthereumProviderOptions = {
projectId: this.projectId,
showQrModal: false,
rpcMap: this.chains
? this.chains.reduce<Record<number, string>>((map, chain) => {
map[chain.chainId] = chain.rpcUrl

return map
}, {})
: ({} as Record<number, string>),
rpcMap,
optionalChains: [...this.chains.map(chain => chain.chainId)] as [number],
metadata: {
name: this.metadata ? this.metadata.name : '',
Expand Down
4 changes: 4 additions & 0 deletions packages/ethers5/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,8 @@ export class Web3Modal extends Web3ModalScaffold {
info: Info
provider: Provider
}) => {
// If connecting with something else than walletconnect, we need to clear the clientId in the store
this.setClientId(null)
if (id === ConstantsUtil.INJECTED_CONNECTOR_ID) {
const InjectedProvider = ethersConfig.injected
if (!InjectedProvider) {
Expand Down Expand Up @@ -301,6 +303,7 @@ export class Web3Modal extends Web3ModalScaffold {
const providerType = EthersStoreUtil.state.providerType
localStorage.removeItem(EthersConstantsUtil.WALLET_ID)
EthersStoreUtil.reset()
this.setClientId(null)
if (siweConfig?.options?.signOutOnDisconnect) {
const { SIWEController } = await import('@web3modal/siwe')
await SIWEController.signOut()
Expand Down Expand Up @@ -497,6 +500,7 @@ export class Web3Modal extends Web3ModalScaffold {
localStorage.removeItem(EthersConstantsUtil.WALLET_ID)
EthersStoreUtil.reset()

this.setClientId(null)
if (providerType === 'injected' || providerType === 'eip6963') {
provider?.emit('disconnect')
} else {
Expand Down
4 changes: 4 additions & 0 deletions packages/scaffold/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,10 @@ export class Web3ModalScaffold {
OptionsController.setEIP6963Enabled(enabled)
}

protected setClientId: (typeof BlockchainApiController)['setClientId'] = clientId => {
BlockchainApiController.setClientId(clientId)
}

// -- Private ------------------------------------------------------------------
private async initControllers(options: ScaffoldOptions) {
ChainController.initialize([
Expand Down
7 changes: 7 additions & 0 deletions packages/wagmi/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,11 @@ export class Web3Modal extends Web3ModalScaffold {
onUri(data)
})

const clientId = await provider.signer?.client?.core?.crypto?.getClientId()
if (clientId) {
this.setClientId(clientId)
}

const chainId = NetworkUtil.caipNetworkIdToNumber(this.getCaipNetwork()?.id)
const siweParams = await siweConfig?.getMessageParams?.()
// Make sure client uses ethereum provider version that supports `authenticate`
Expand Down Expand Up @@ -214,6 +219,7 @@ export class Web3Modal extends Web3ModalScaffold {
if (!connector) {
throw new Error('connectionControllerClient:connectExternal - connector is undefined')
}
this.setClientId(null)
if (provider && info && connector.id === ConstantsUtil.EIP6963_CONNECTOR_ID) {
// @ts-expect-error Exists on EIP6963Connector
connector.setEip6963Wallet?.({ provider, info })
Expand Down Expand Up @@ -253,6 +259,7 @@ export class Web3Modal extends Web3ModalScaffold {

disconnect: async () => {
await disconnect(this.wagmiConfig)
this.setClientId(null)
if (siweConfig?.options?.signOutOnDisconnect) {
const { SIWEController } = await import('@web3modal/siwe')
await SIWEController.signOut()
Expand Down

0 comments on commit 0ae4697

Please sign in to comment.