Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b23cf1c
fix: catch unhandled RPC rejections from internal API calls (#5636)
Khizr97 Apr 13, 2026
4640c69
fix: guard setDefaultChain calls against undefined provider on stale …
Khizr97 Apr 13, 2026
a77f5aa
fix: pass required account param in Bitcoin getAccountAddresses (#5630)
Khizr97 Apr 13, 2026
3b3d256
fix(swap): auto-focus input field when swap view opens (#5619)
Khizr97 Apr 13, 2026
13f225f
fix: serialize BigInt values in social login RPC requests (#5632)
Khizr97 Apr 13, 2026
12755cf
fix: handle undefined address in wagmi toChecksummedAddress (#5635)
Khizr97 Apr 13, 2026
dc4067e
fix: reject malformed CAIP-10 addresses in setCaipAddress (#5634)
Khizr97 Apr 13, 2026
2f3408b
fix: include projectId in API requests when value is empty string (#5…
Khizr97 Apr 13, 2026
9553f86
fix: resolve Lit dev warnings from AppKit web components (#5629)
Khizr97 Apr 13, 2026
fdf31f5
fix(send): use string type for sendTokenAmount to preserve precision …
Khizr97 Apr 13, 2026
9082d4e
fix: resolve TypeScript errors in wallet send view getMessage()
svenvoskamp Apr 14, 2026
ce3c8dc
fix(swap): defer input focus to after browser paint using requestAnim…
Khizr97 Apr 15, 2026
e8744a7
fix(send/swap): clamp max amount toFixed to token decimals to prevent…
Khizr97 Apr 15, 2026
696d94f
fix(send/swap): use ROUND_DOWN when truncating max amount to token de…
Khizr97 Apr 15, 2026
17fbc61
fix(send/swap): deduct estimated gas from max native token amount
Khizr97 Apr 16, 2026
0bb57bc
fix(swap): re-request calldata with gas-adjusted amount when value ov…
Khizr97 Apr 16, 2026
e8dcc4e
Merge branch 'main' into chore/khizr-fixes
0xmkh May 13, 2026
0034294
fix: lint issues and prettier format
0xmkh May 13, 2026
cc089cf
fix: lint issue
0xmkh May 13, 2026
73cf814
fix: prevent empty search/exclude/include params from being sent to w…
Khizr97 May 13, 2026
90c69bf
chore: bump @reown/appkit/react size limit to 236 KB and simplify exc…
Khizr97 May 13, 2026
87e0edd
Merge branch 'main' into chore/khizr-fixes
Khizr97 May 13, 2026
73f626b
fix(tests): check inner shadow DOM button for disabled state in expec…
Khizr97 May 13, 2026
c053c9f
fix(tests): use waitForFunction for cross-browser shadow DOM disabled…
Khizr97 May 13, 2026
7d7f457
fix(tests): use locator.evaluate + expect.poll for cross-browser disa…
Khizr97 May 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .size-limit.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default [
{
name: '@reown/appkit/react',
path: 'packages/appkit/dist/esm/exports/react.js',
limit: '235 KB', // Current: ~222 KB (5% buffer)
limit: '236 KB', // Current: ~235 KB
gzip: true
},
{
Expand Down
21 changes: 16 additions & 5 deletions apps/laboratory/tests/shared/validators/ModalValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,11 +310,22 @@ export class ModalValidator {

async expectOpenButton({ disabled }: { disabled: boolean }) {
const secondaryButton = this.page.getByTestId('w3m-connecting-widget-secondary-button')
if (disabled) {
await expect(secondaryButton).toHaveAttribute('disabled')
} else {
await expect(secondaryButton).not.toHaveAttribute('disabled')
}

await expect
.poll(
() =>
secondaryButton.evaluate((el: Element) => {
const btn = el.shadowRoot?.querySelector('button') as HTMLButtonElement | null
if (btn) {
return btn.disabled
}
const attr = el.getAttribute('disabled')

return attr !== null && attr !== 'false'
}),
{ timeout: 60000 }
)
.toBe(disabled)
}

async expectTryAgainButton() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,11 @@ export class BitcoinWalletConnectConnector

public async getAccountAddresses(): Promise<BitcoinConnector.AccountAddress[]> {
this.checkIfMethodIsSupported('getAccountAddresses')
const account = this.getAccount(true)

const addresses = await this.internalRequest({
method: 'getAccountAddresses',
params: undefined
params: { account }
})

return addresses.map(address => ({ address, purpose: AddressPurpose.Payment }))
Expand Down Expand Up @@ -119,7 +120,7 @@ export class BitcoinWalletConnectConnector
}

public setDefaultChain(chainId: string) {
this.provider.setDefaultChain(chainId)
this.provider?.setDefaultChain(chainId)
}

// -- Private ------------------------------------------ //
Expand Down Expand Up @@ -210,6 +211,11 @@ export namespace WalletConnectProvider {
memo?: string
}

export type WCGetAccountAddressesParams = {
account: string
intentions?: string[]
}

export type WCGetAccountAddressesResponse = {
address: string
publicKey: Uint8Array
Expand Down Expand Up @@ -240,7 +246,7 @@ export namespace WalletConnectProvider {
export type RequestMethods = {
signMessage: Request<WCSignMessageParams, WCSignMessageResponse>
sendTransfer: Request<WCSendTransferParams, WCSendTransferResponse>
getAccountAddresses: Request<undefined, string[]>
getAccountAddresses: Request<WCGetAccountAddressesParams, string[]>
signPsbt: Request<WCSignPSBTParams, WCSignPSBTResponse>
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,10 @@ describe('LeatherConnector', () => {
describe('getAccountAddresses', () => {
beforeEach(() => {
universalProvider.session = mockUniversalProvider.mockSession()
vi.spyOn(ChainController, 'getAccountData').mockReturnValue({
caipAddress: `${bitcoin.caipNetworkId}:address`,
address: 'address'
} as unknown as AccountState)
})

it('should get the account addresses and parse response', async () => {
Expand All @@ -208,7 +212,7 @@ describe('LeatherConnector', () => {
expect(requestSpy).toHaveBeenCalledWith(
{
method: 'getAccountAddresses',
params: undefined
params: { account: 'address' }
},
bitcoin.caipNetworkId
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ export class SolanaWalletConnectProvider
}

public setDefaultChain(chainId: string) {
this.provider.setDefaultChain(chainId)
this.provider?.setDefaultChain(chainId)
}

// -- Private ------------------------------------------ //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export class TonWalletConnectConnector
}

public setDefaultChain(chainId: string) {
this.provider.setDefaultChain(chainId)
this.provider?.setDefaultChain(chainId)
}

// -- Internals ----------------------------------------------------- //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ export class TronWalletConnectConnector
}

public setDefaultChain(chainId: string) {
this.provider.setDefaultChain(chainId)
this.provider?.setDefaultChain(chainId)
}

// -- Internals ----------------------------------------------------- //
Expand Down
10 changes: 9 additions & 1 deletion packages/adapters/wagmi/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1086,6 +1086,14 @@ export class WagmiAdapter extends AdapterBlueprint {
}

private toChecksummedAddress(address: string) {
return checksumAddress(address.toLowerCase() as `0x${string}`)
if (!address) {
return address as `0x${string}`
}

try {
return checksumAddress(address.toLowerCase() as `0x${string}`)
} catch {
return address as `0x${string}`
}
}
}
15 changes: 12 additions & 3 deletions packages/appkit/src/client/appkit-base-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1277,10 +1277,10 @@ export abstract class AppKitBaseClient {
address,
chainId: syncAccountChainId,
chainNamespace
})
}).catch(() => null)
} else if (!isActiveChain && syncAccountChainId) {
this.syncAccountInfo(address, syncAccountChainId, chainNamespace)
this.syncBalance({ address, chainId: syncAccountChainId, chainNamespace })
this.syncBalance({ address, chainId: syncAccountChainId, chainNamespace }).catch(() => null)
} else {
this.syncAccountInfo(address, chainId, chainNamespace)
}
Expand Down Expand Up @@ -1667,7 +1667,7 @@ export abstract class AppKitBaseClient {
address,
chainId,
chainNamespace
})
}).catch(() => null)
}
}

Expand Down Expand Up @@ -2132,6 +2132,15 @@ export abstract class AppKitBaseClient {
chain: ChainNamespace,
shouldRefresh = false
) => {
if (caipAddress !== null) {
const parts = caipAddress.split(':')
if (parts.length !== 3 || parts.some(p => !p)) {
console.warn(`[AppKit] setCaipAddress: invalid CAIP-10 address rejected: "${caipAddress}"`)

return
}
}

ChainController.setAccountProp('caipAddress', caipAddress, chain, shouldRefresh)
ChainController.setAccountProp(
'address',
Expand Down
2 changes: 1 addition & 1 deletion packages/appkit/src/universal-adapter/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ export class UniversalAdapter extends AdapterBlueprint {
}
}
}
connector.provider.setDefaultChain(caipNetwork.caipNetworkId)
connector.provider?.setDefaultChain(caipNetwork.caipNetworkId)
}

public getWalletConnectProvider() {
Expand Down
6 changes: 3 additions & 3 deletions packages/controllers/src/controllers/ApiController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,8 @@ export const ApiController = {
...params,
page: String(params.page),
entries: String(params.entries),
include: params.include?.join(','),
exclude: exclude.join(',')
include: params.include?.join(',') || undefined,
exclude: exclude.join(',') || undefined
}
})

Expand Down Expand Up @@ -468,7 +468,7 @@ export const ApiController = {
const params = {
page: pageOverride ?? 1,
entries: entriesOverride ?? 100,
search: search?.trim(),
search: search?.trim() || undefined,
badge_type: badge,
include: includeOverride ?? includeWalletIds,
exclude: excludeOverride ?? excludeWalletIds,
Expand Down
20 changes: 10 additions & 10 deletions packages/controllers/src/controllers/SendController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import { SnackController } from './SnackController.js'

export interface TxParams {
receiverAddress: string
sendTokenAmount: number
sendTokenAmount: string
decimals: string
}
export interface SendInputArguments {
Expand All @@ -47,14 +47,14 @@ export interface SendInputArguments {
export interface ContractWriteParams {
receiverAddress: string
tokenAddress: string
sendTokenAmount: number
sendTokenAmount: string
decimals: string
}
export interface SendControllerState {
tokenBalances: Balance[]
token?: Balance
hash?: string
sendTokenAmount?: number
sendTokenAmount?: string
receiverAddress?: string
receiverProfileName?: string
receiverProfileImageUrl?: string
Expand Down Expand Up @@ -122,7 +122,7 @@ const controller = {
getPreferredAccountType(ChainController.state.activeChain) ===
W3mFrameRpcConstants.ACCOUNT_TYPES.SMART_ACCOUNT,
token: state.token?.symbol || '',
amount: state.sendTokenAmount ?? 0,
amount: Number(state.sendTokenAmount ?? '0'),
network: ChainController.state.activeCaipNetwork?.caipNetworkId || ''
}
},
Expand Down Expand Up @@ -177,7 +177,7 @@ const controller = {
properties: {
isSmartAccount: activeAccountType === W3mFrameRpcConstants.ACCOUNT_TYPES.SMART_ACCOUNT,
token: SendController.state.token.address,
amount: SendController.state.sendTokenAmount,
amount: Number(SendController.state.sendTokenAmount),
network: ChainController.state.activeCaipNetwork?.caipNetworkId || ''
}
})
Expand All @@ -198,7 +198,7 @@ const controller = {
properties: {
isSmartAccount: activeAccountType === W3mFrameRpcConstants.ACCOUNT_TYPES.SMART_ACCOUNT,
token: SendController.state.token.symbol || '',
amount: SendController.state.sendTokenAmount,
amount: Number(SendController.state.sendTokenAmount),
network: ChainController.state.activeCaipNetwork?.caipNetworkId || ''
}
})
Expand Down Expand Up @@ -302,7 +302,7 @@ const controller = {
isSmartAccount:
getPreferredAccountType('eip155') === W3mFrameRpcConstants.ACCOUNT_TYPES.SMART_ACCOUNT,
token: SendController.state.token?.symbol || '',
amount: params.sendTokenAmount,
amount: Number(params.sendTokenAmount),
network: ChainController.state.activeCaipNetwork?.caipNetworkId || '',
hash: hash || ''
}
Expand Down Expand Up @@ -350,7 +350,7 @@ const controller = {
isSmartAccount:
getPreferredAccountType('eip155') === W3mFrameRpcConstants.ACCOUNT_TYPES.SMART_ACCOUNT,
token: SendController.state.token?.symbol || '',
amount: params.sendTokenAmount,
amount: Number(params.sendTokenAmount),
network: ChainController.state.activeCaipNetwork?.caipNetworkId || '',
hash: hash || ''
}
Expand Down Expand Up @@ -392,7 +392,7 @@ const controller = {
chainNamespace: 'solana',
tokenMint,
to: SendController.state.receiverAddress,
value: SendController.state.sendTokenAmount
value: Number(SendController.state.sendTokenAmount)
})

if (hash) {
Expand All @@ -407,7 +407,7 @@ const controller = {
properties: {
isSmartAccount: false,
token: SendController.state.token?.symbol || '',
amount: SendController.state.sendTokenAmount,
amount: Number(SendController.state.sendTokenAmount),
network: ChainController.state.activeCaipNetwork?.caipNetworkId || '',
hash: hash || ''
}
Expand Down
50 changes: 45 additions & 5 deletions packages/controllers/src/controllers/SwapController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -727,24 +727,64 @@ const controller = {
return undefined
}

const amount = ConnectionController.parseUnits(
let amount = ConnectionController.parseUnits(
sourceTokenAmount,
sourceToken.decimals
)?.toString()

try {
const response = await BlockchainApiController.generateSwapCalldata({
const isSourceTokenIsNetworkToken = sourceToken.address === networkAddress

let response = await BlockchainApiController.generateSwapCalldata({
userAddress: fromCaipAddress,
from: sourceToken.address,
to: toToken.address,
amount: amount as string,
disableEstimate: true
})

const isSourceTokenIsNetworkToken = sourceToken.address === networkAddress
let gas = BigInt(response.tx.eip155.gas)
let gasPrice = BigInt(response.tx.eip155.gasPrice)

/*
* For native-token source swaps (e.g. ETH → USDC), the transaction
* value IS the swap input amount, so the total ETH deducted from the
* wallet is value + gas×gasPrice. If that exceeds the balance the
* wallet's eth_estimateGas call will revert.
*
* The UI's Max button uses a rough constant to pre-subtract gas, but
* the API may return a different gas estimate. We correct here using
* the actual numbers from the first calldata response.
*/
if (isSourceTokenIsNetworkToken && amount) {
const nativeToken = state.myTokensWithBalance?.find(t => t.address === networkAddress)
if (nativeToken) {
const decimals = parseInt(nativeToken.quantity.decimals, 10)
const rawBalance =
ConnectionController.parseUnits(nativeToken.quantity.numeric, decimals) ?? 0n
const gasCost = gas * gasPrice
const value = BigInt(amount)

if (value + gasCost > rawBalance) {
// Re-request calldata with an amount that leaves room for gas
const adjustedAmount = rawBalance > gasCost ? rawBalance - gasCost : 0n
// eslint-disable-next-line max-depth
if (adjustedAmount > 0n) {
response = await BlockchainApiController.generateSwapCalldata({
userAddress: fromCaipAddress,
from: sourceToken.address,
to: toToken.address,
amount: adjustedAmount.toString(),
disableEstimate: true
})
amount = adjustedAmount.toString()
gas = BigInt(response.tx.eip155.gas)
gasPrice = BigInt(response.tx.eip155.gasPrice)
}
}
}
}

const gas = BigInt(response.tx.eip155.gas)
const gasPrice = BigInt(response.tx.eip155.gasPrice)
const address = CoreHelperUtil.getPlainAddress(response.tx.to)

if (!address) {
Expand Down
2 changes: 1 addition & 1 deletion packages/controllers/src/utils/FetchUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export class FetchUtil {
const url = new URL(path, this.baseUrl)
if (params) {
Object.entries(params).forEach(([key, value]) => {
if (value) {
if (value !== undefined && value !== null) {
url.searchParams.append(key, value)
}
})
Expand Down
Loading
Loading