diff --git a/packages/core-mobile/.env.example b/packages/core-mobile/.env.example
index 9663edbc84..5daa31064d 100644
--- a/packages/core-mobile/.env.example
+++ b/packages/core-mobile/.env.example
@@ -49,4 +49,7 @@ DD_SITE=
# public key and key id for analytics data encryption
ANALYTICS_ENCRYPTION_KEY=
-ANALYTICS_ENCRYPTION_KEY_ID=
\ No newline at end of file
+ANALYTICS_ENCRYPTION_KEY_ID=
+
+# required for gasless
+GAS_STATION_URL=
diff --git a/packages/core-mobile/__mocks__/react-native-config.js b/packages/core-mobile/__mocks__/react-native-config.js
index 46c7f84d1e..3bfc57d85c 100644
--- a/packages/core-mobile/__mocks__/react-native-config.js
+++ b/packages/core-mobile/__mocks__/react-native-config.js
@@ -17,5 +17,6 @@ export default {
ANALYTICS_ENCRYPTION_KEY: 'MOCK_ANALYTICS_ENCRYPTION_KEY',
ANALYTICS_ENCRYPTION_KEY_ID: 'MOCK_ANALYTICS_ENCRYPTION_KEY_ID',
GOOGLE_OAUTH_CLIENT_WEB_ID: 'MOCK_GOOGLE_OAUTH_CLIENT_WEB_ID',
- GOOGLE_OAUTH_CLIENT_IOS_ID: 'MOCK_GOOGLE_OAUTH_CLIENT_IOS_ID'
+ GOOGLE_OAUTH_CLIENT_IOS_ID: 'MOCK_GOOGLE_OAUTH_CLIENT_IOS_ID',
+ GAS_STATION_URL: 'MOCK_GAS_STATION_URL'
}
diff --git a/packages/core-mobile/app/screens/rpc/components/v2/AlertBanner.tsx b/packages/core-mobile/app/screens/rpc/components/v2/AlertBanner.tsx
index b3844fd3f0..1a5f610021 100644
--- a/packages/core-mobile/app/screens/rpc/components/v2/AlertBanner.tsx
+++ b/packages/core-mobile/app/screens/rpc/components/v2/AlertBanner.tsx
@@ -3,34 +3,54 @@ import { Alert, AlertType } from '@avalabs/vm-module-types'
import InfoSVG from 'components/svg/InfoSVG'
import React, { useMemo } from 'react'
-const AlertBanner = ({ alert }: { alert: Alert }): JSX.Element | null => {
+interface AlertBannerProps {
+ alert: Alert
+ customStyle?: {
+ borderColor?: string
+ backgroundColor?: string
+ iconColor?: string
+ }
+}
+
+const AlertBanner = ({
+ alert,
+ customStyle
+}: AlertBannerProps): JSX.Element | null => {
const {
theme: { colors }
} = useTheme()
const textStyle = { color: '$black', fontSize: 13, lineHeight: 16 }
const icon = useMemo(() => {
+ const iconColor = customStyle?.iconColor || colors.$black
+
if (alert.type === AlertType.DANGER) {
- return
+ return
}
if (alert.type === AlertType.WARNING) {
- return
+ return
}
- return
- }, [alert, colors])
+ return (
+
+ )
+ }, [alert, colors, customStyle?.iconColor])
if (alert.type === AlertType.INFO) {
return (
@@ -49,7 +69,8 @@ const AlertBanner = ({ alert }: { alert: Alert }): JSX.Element | null => {
padding: 16,
borderRadius: 8,
backgroundColor:
- alert.type === AlertType.DANGER ? '$dangerLight' : '$warningLight',
+ customStyle?.backgroundColor ||
+ (alert.type === AlertType.DANGER ? '$dangerLight' : '$warningLight'),
flexDirection: 'row',
alignItems: 'center',
gap: 12
diff --git a/packages/core-mobile/app/screens/rpc/components/v2/ApprovalPopup.tsx b/packages/core-mobile/app/screens/rpc/components/v2/ApprovalPopup.tsx
index af562a0e14..a3c07dfb3e 100644
--- a/packages/core-mobile/app/screens/rpc/components/v2/ApprovalPopup.tsx
+++ b/packages/core-mobile/app/screens/rpc/components/v2/ApprovalPopup.tsx
@@ -1,6 +1,11 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react'
import { ActivityIndicator, StyleSheet, ScrollView } from 'react-native'
-import { TokenType } from '@avalabs/vm-module-types'
+import {
+ Alert,
+ AlertType,
+ SigningData_EthSendTx,
+ TokenType
+} from '@avalabs/vm-module-types'
import { Space } from 'components/Space'
import { Row } from 'components/Row'
import NetworkFeeSelector from 'components/NetworkFeeSelector'
@@ -10,7 +15,10 @@ import { useNavigation, useRoute } from '@react-navigation/native'
import { useSelector } from 'react-redux'
import { NetworkLogo } from 'screens/network/NetworkLogo'
import { Button, Text, View, useTheme } from '@avalabs/k2-mobile'
-import { selectIsSeedlessSigningBlocked } from 'store/posthog'
+import {
+ selectIsGaslessBlocked,
+ selectIsSeedlessSigningBlocked
+} from 'store/posthog'
import FeatureBlocked from 'screens/posthog/FeatureBlocked'
import { Eip1559Fees } from 'utils/Utils'
import WalletConnectService from 'services/walletconnectv2/WalletConnectService'
@@ -20,7 +28,6 @@ import {
selectAccountByIndex,
selectActiveAccount
} from 'store/account/slice'
-import Logger from 'utils/Logger'
import TokenAddress from 'components/TokenAddress'
import { isInAppRequest } from 'store/rpc/utils/isInAppRequest'
import { isAccountApproved } from 'store/rpc/utils/isAccountApproved/isAccountApproved'
@@ -30,12 +37,19 @@ import GlobeSVG from 'components/svg/GlobeSVG'
import { useSpendLimits } from 'hooks/useSpendLimits'
import { isHex } from 'viem'
import { getChainIdFromCaip2 } from 'utils/caip2ChainIds'
+import Switch from 'components/Switch'
+import GaslessService from 'services/gasless/GaslessService'
+import Logger from 'utils/Logger'
+import { SendErrorMessage } from 'screens/send/utils/types'
+import { useNativeTokenWithBalance } from 'screens/send/hooks/useNativeTokenWithBalance'
+import { Tooltip } from 'components/Tooltip'
+import InfoSVG from 'components/svg/InfoSVG'
+import { validateFee } from 'screens/send/utils/evm/validate'
import RpcRequestBottomSheet from '../shared/RpcRequestBottomSheet'
import { DetailSectionView } from '../shared/DetailSectionView'
import BalanceChange from './BalanceChange'
import { SpendLimits } from './SpendLimits'
import AlertBanner from './AlertBanner'
-
type ApprovalPopupScreenProps = WalletScreenProps<
typeof AppNavigation.Modal.ApprovalPopup
>
@@ -50,6 +64,30 @@ const ApprovalPopup = (): JSX.Element => {
const caip2ChainId = request.chainId
const chainId = getChainIdFromCaip2(caip2ChainId)
const network = getNetwork(chainId)
+ const [isGaslessEligible, setIsGaslessEligible] = useState(false)
+ const [gaslessEnabled, setGaslessEnabled] = useState(false)
+ const isGaslessBlocked = useSelector(selectIsGaslessBlocked)
+ const [amountError, setAmountError] = useState()
+ const nativeToken = useNativeTokenWithBalance()
+ const [gaslessError, setGaslessError] = useState(null)
+
+ useEffect(() => {
+ const checkGaslessEligibility = async (): Promise => {
+ if (!chainId) {
+ setIsGaslessEligible(false)
+ return
+ }
+ const isEligible = await GaslessService.isEligibleForChain(
+ chainId.toString()
+ ).catch(err => {
+ Logger.error('Error checking gasless eligibility', err)
+ return false
+ })
+ Logger.info('ApprovalPopup: Gasless eligibility', isEligible)
+ setIsGaslessEligible(isEligible)
+ }
+ checkGaslessEligibility()
+ }, [chainId, request.chainId])
const accountSelector =
'account' in signingData
@@ -74,7 +112,8 @@ const ApprovalPopup = (): JSX.Element => {
!account ||
(displayData.networkFeeSelector && maxFeePerGas === undefined) ||
(displayData.networkFeeSelector && maxPriorityFeePerGas === undefined) ||
- submitting
+ submitting ||
+ amountError !== undefined
const showNetworkFeeSelector = displayData.networkFeeSelector
@@ -132,23 +171,73 @@ const ApprovalPopup = (): JSX.Element => {
setMaxPriorityFeePerGas(fees.maxPriorityFeePerGas)
}, [])
+ const handleGaslessTx = async (
+ addressFrom: string
+ ): Promise => {
+ let attempts = 0
+ const MAX_ATTEMPTS = 1
+
+ while (attempts <= MAX_ATTEMPTS) {
+ const result = await GaslessService.fundTx(
+ signingData as SigningData_EthSendTx,
+ addressFrom
+ )
+
+ if (result.txHash) {
+ setGaslessError(null)
+ return result.txHash
+ }
+
+ // Show error and stop if we get a DO_NOT_RETRY error or
+ // if we've hit max attempts with a RETRY_WITH_NEW_CHALLENGE error
+ if (
+ result.error?.category === 'DO_NOT_RETRY' ||
+ (result.error?.category === 'RETRY_WITH_NEW_CHALLENGE' &&
+ attempts === MAX_ATTEMPTS)
+ ) {
+ setGaslessError({
+ type: AlertType.INFO,
+ details: {
+ title: 'Free Gas Error',
+ description:
+ 'Core was unable to fund the gas. You will need to pay the gas fee to continue with this transaction. '
+ }
+ })
+ setSubmitting(false)
+ return undefined
+ }
+
+ attempts++
+ }
+ return undefined
+ }
+
const onHandleApprove = async (): Promise => {
if (approveDisabled) return
-
setSubmitting(true)
- onApprove({
- network,
- account,
- maxFeePerGas,
- maxPriorityFeePerGas,
- overrideData: hashedCustomSpend
- })
- .catch(Logger.error)
- .finally(() => {
+ if (gaslessEnabled) {
+ const txHash = await handleGaslessTx(account.addressC)
+ if (!txHash) {
setSubmitting(false)
- goBack()
+ return
+ }
+ }
+
+ try {
+ await onApprove({
+ network,
+ account,
+ maxFeePerGas,
+ maxPriorityFeePerGas,
+ overrideData: hashedCustomSpend
})
+ goBack()
+ } catch (error: unknown) {
+ Logger.error('Error approving transaction', error)
+ } finally {
+ setSubmitting(false)
+ }
}
const renderAlert = (): JSX.Element | null => {
@@ -161,6 +250,22 @@ const ApprovalPopup = (): JSX.Element => {
)
}
+ const renderGaslessAlert = (): JSX.Element | null => {
+ if (!gaslessError) return null
+ return (
+
+
+
+ )
+ }
+
const renderDappInfo = (): JSX.Element | null => {
if (!displayData.dAppInfo) return null
@@ -331,7 +436,37 @@ const ApprovalPopup = (): JSX.Element => {
return
}
+ const renderGasless = (): JSX.Element | null => {
+ if (!isGaslessEligible || isGaslessBlocked || gaslessError) {
+ return null
+ }
+ return (
+
+
+ Get Free Gas
+
+ }
+ />
+
+ setGaslessEnabled(prevState => !prevState)}
+ />
+
+ )
+ }
+
const renderNetworkFeeSelector = (): JSX.Element | null => {
+ if (gaslessEnabled && isGaslessEligible) return null
if (!showNetworkFeeSelector || !chainId) return null
let gasLimit: number | undefined
@@ -368,6 +503,46 @@ const ApprovalPopup = (): JSX.Element => {
)
}
+ const validateEthSendTransaction = useCallback(() => {
+ if (
+ !signingData ||
+ !network?.networkToken ||
+ !nativeToken ||
+ signingData.type !== 'eth_sendTransaction'
+ )
+ return
+ const ethSendTx = signingData.data
+
+ try {
+ const gasLimit = ethSendTx.gasLimit ? BigInt(ethSendTx.gasLimit) : 0n
+ const amount = ethSendTx.value ? BigInt(ethSendTx.value) : 0n
+
+ validateFee({
+ gasLimit,
+ maxFee: maxFeePerGas || 0n,
+ amount,
+ nativeToken,
+ token: nativeToken
+ })
+
+ setAmountError(undefined)
+ } catch (err) {
+ if (err instanceof Error) {
+ setAmountError(err.message)
+ } else {
+ setAmountError(SendErrorMessage.UNKNOWN_ERROR)
+ }
+ }
+ }, [signingData, network, maxFeePerGas, nativeToken])
+
+ useEffect(() => {
+ if (gaslessEnabled) {
+ setAmountError(undefined)
+ return
+ }
+ validateEthSendTransaction()
+ }, [validateEthSendTransaction, gaslessEnabled])
+
return (
<>
{
{displayData.title}
{renderAlert()}
+ {renderGaslessAlert()}
{renderDappInfo()}
{renderNetwork()}
@@ -387,8 +563,20 @@ const ApprovalPopup = (): JSX.Element => {
{renderDetails()}
{renderSpendLimits()}
{renderBalanceChange()}
+ {renderGasless()}
{renderNetworkFeeSelector()}
{renderDisclaimer()}
+ {amountError && (
+
+ {amountError}
+
+ )}
{renderApproveRejectButtons()}
diff --git a/packages/core-mobile/app/screens/send/hooks/useEVMSend.ts b/packages/core-mobile/app/screens/send/hooks/useEVMSend.ts
index 15db021d23..39cda4d435 100644
--- a/packages/core-mobile/app/screens/send/hooks/useEVMSend.ts
+++ b/packages/core-mobile/app/screens/send/hooks/useEVMSend.ts
@@ -6,6 +6,8 @@ import { assertNotUndefined } from 'utils/assertions'
import { useEVMProvider } from 'hooks/networks/networkProviderHooks'
import { bigIntToString } from '@avalabs/core-utils-sdk'
import Logger from 'utils/Logger'
+import { selectIsGaslessBlocked } from 'store/posthog'
+import { useSelector } from 'react-redux'
import { SendAdapterEVM, SendErrorMessage } from '../utils/types'
import { send as sendEVM } from '../utils/evm/send'
import { getGasLimit } from '../utils/evm/getGasLimit'
@@ -14,6 +16,7 @@ import {
validateERC1155,
validateERC721,
validateAmount,
+ validateFee,
validateGasLimit,
validateSupportedToken
} from '../utils/evm/validate'
@@ -36,6 +39,7 @@ const useEVMSend: SendAdapterEVM = ({
canValidate
} = useSendContext()
const provider = useEVMProvider(network)
+ const isGaslessBlocked = useSelector(selectIsGaslessBlocked)
const send = useCallback(async () => {
try {
@@ -71,12 +75,19 @@ const useEVMSend: SendAdapterEVM = ({
const handleError = useCallback(
(err: unknown) => {
if (err instanceof Error) {
+ if (
+ !isGaslessBlocked &&
+ err.message === SendErrorMessage.INSUFFICIENT_BALANCE_FOR_FEE
+ ) {
+ setError(undefined)
+ return
+ }
setError(err.message)
} else {
setError(SendErrorMessage.UNKNOWN_ERROR)
}
},
- [setError]
+ [setError, isGaslessBlocked]
)
const validate = useCallback(async () => {
@@ -109,11 +120,15 @@ const useEVMSend: SendAdapterEVM = ({
token.type === TokenType.ERC20
) {
validateAmount({
- gasLimit,
amount: amount?.bn,
- token,
+ token
+ })
+ validateFee({
+ gasLimit,
maxFee,
- nativeToken
+ amount: amount?.bn,
+ nativeToken,
+ token
})
}
diff --git a/packages/core-mobile/app/screens/send/utils/evm/validate.test.ts b/packages/core-mobile/app/screens/send/utils/evm/validate.test.ts
index 6eeb4200df..b42b4f5804 100644
--- a/packages/core-mobile/app/screens/send/utils/evm/validate.test.ts
+++ b/packages/core-mobile/app/screens/send/utils/evm/validate.test.ts
@@ -14,7 +14,8 @@ import {
validateBasicInputs,
validateERC1155,
validateERC721,
- validateSupportedToken
+ validateSupportedToken,
+ validateFee
} from './validate'
const tokenWithBalance: NativeTokenBalance = {
@@ -180,34 +181,35 @@ describe('validate evm send', () => {
it('should succeed when all requirements met', async () => {
validateAmount({
- gasLimit: 1n,
amount: 10n,
- token: mockNativeTokenWithBalance,
+ token: mockNativeTokenWithBalance
+ })
+ validateFee({
+ gasLimit: 1n,
maxFee: 1n,
- nativeToken: mockNativeTokenWithBalance
+ amount: 10n,
+ nativeToken: mockNativeTokenWithBalance,
+ token: mockNativeTokenWithBalance
})
})
it('should fail when amount is greater than token balance', async () => {
expect(() =>
validateAmount({
- gasLimit: 1n,
amount: 10000n,
- token: mockNativeTokenWithBalance,
- maxFee: 1n,
- nativeToken: mockNativeTokenWithBalance
+ token: mockNativeTokenWithBalance
})
).toThrow(SendErrorMessage.INSUFFICIENT_BALANCE)
})
it('should fail when totalFee is greater than remaining balance', async () => {
expect(() =>
- validateAmount({
+ validateFee({
gasLimit: 10n,
- amount: 1000n,
- token: mockNativeTokenWithBalance,
maxFee: 10n,
- nativeToken: mockNativeTokenWithBalance
+ amount: 1000n,
+ nativeToken: mockNativeTokenWithBalance,
+ token: mockNativeTokenWithBalance
})
).toThrow(SendErrorMessage.INSUFFICIENT_BALANCE_FOR_FEE)
})
@@ -215,11 +217,8 @@ describe('validate evm send', () => {
it('should fail when amount is 0', async () => {
expect(() =>
validateAmount({
- gasLimit: 1n,
amount: 0n,
- token: mockNativeTokenWithBalance,
- maxFee: 1n,
- nativeToken: mockNativeTokenWithBalance
+ token: mockNativeTokenWithBalance
})
).toThrow(SendErrorMessage.AMOUNT_REQUIRED)
})
@@ -237,22 +236,23 @@ describe('validate evm send', () => {
it('should succeed when all requirements met', async () => {
validateAmount({
- gasLimit: 1n,
amount: 10n,
- token: mockERC20TokenWithBalance,
+ token: mockERC20TokenWithBalance
+ })
+ validateFee({
+ gasLimit: 1n,
maxFee: 1n,
- nativeToken: mockNativeTokenWithBalance
+ amount: 10n,
+ nativeToken: mockNativeTokenWithBalance,
+ token: mockERC20TokenWithBalance
})
})
it('should fail when amount is greater than token balance', async () => {
expect(() =>
validateAmount({
- gasLimit: 1n,
amount: 10000n,
- token: mockERC20TokenWithBalance,
- maxFee: 1n,
- nativeToken: mockNativeTokenWithBalance
+ token: mockERC20TokenWithBalance
})
).toThrow(SendErrorMessage.INSUFFICIENT_BALANCE)
})
@@ -260,11 +260,8 @@ describe('validate evm send', () => {
it('should fail when amount is 0', async () => {
expect(() =>
validateAmount({
- gasLimit: 1n,
amount: 0n,
- token: mockERC20TokenWithBalance,
- maxFee: 1n,
- nativeToken: mockNativeTokenWithBalance
+ token: mockERC20TokenWithBalance
})
).toThrow(SendErrorMessage.AMOUNT_REQUIRED)
})
diff --git a/packages/core-mobile/app/screens/send/utils/evm/validate.ts b/packages/core-mobile/app/screens/send/utils/evm/validate.ts
index a559de2ef3..1566e4f226 100644
--- a/packages/core-mobile/app/screens/send/utils/evm/validate.ts
+++ b/packages/core-mobile/app/screens/send/utils/evm/validate.ts
@@ -40,32 +40,40 @@ export const validateERC1155 = (
}
export const validateAmount = ({
- gasLimit,
amount,
- token,
- maxFee,
- nativeToken
+ token
}: {
- gasLimit: bigint
amount: bigint | undefined
token: TokenWithBalanceERC20 | NetworkTokenWithBalance
- maxFee: bigint
- nativeToken: NetworkTokenWithBalance
}): void => {
if (amount && token.balance < amount) {
throw new Error(SendErrorMessage.INSUFFICIENT_BALANCE)
}
+ if (!amount || (amount && amount <= 0n)) {
+ throw new Error(SendErrorMessage.AMOUNT_REQUIRED)
+ }
+}
+
+export const validateFee = ({
+ gasLimit,
+ maxFee,
+ amount,
+ nativeToken,
+ token
+}: {
+ gasLimit: bigint
+ maxFee: bigint
+ amount: bigint | undefined
+ nativeToken: NetworkTokenWithBalance
+ token: TokenWithBalanceEVM
+}): void => {
const totalFee = gasLimit * maxFee
const remainingBalance = nativeToken.balance - (amount ?? 0n)
if (token.type === TokenType.NATIVE && remainingBalance < totalFee) {
throw new Error(SendErrorMessage.INSUFFICIENT_BALANCE_FOR_FEE)
}
-
- if (!amount || (amount && amount <= 0n)) {
- throw new Error(SendErrorMessage.AMOUNT_REQUIRED)
- }
}
export const validateGasLimit = (gasLimit: bigint): void => {
diff --git a/packages/core-mobile/app/services/gasless/GaslessService.ts b/packages/core-mobile/app/services/gasless/GaslessService.ts
new file mode 100644
index 0000000000..2cb486627f
--- /dev/null
+++ b/packages/core-mobile/app/services/gasless/GaslessService.ts
@@ -0,0 +1,67 @@
+import { FundResult, GaslessSdk } from '@avalabs/core-gasless-sdk'
+import { SigningData_EthSendTx } from '@avalabs/vm-module-types'
+import Config from 'react-native-config'
+import Logger from 'utils/Logger'
+import AppCheckService from 'services/fcm/AppCheckService'
+import { Transaction, TransactionLike } from 'ethers'
+
+if (!Config.GAS_STATION_URL) {
+ Logger.warn(
+ 'GAS_STATION_URL is missing. Gasless service may not work properly.'
+ )
+}
+
+class GaslessService {
+ private sdk: GaslessSdk | null = null
+
+ constructor() {
+ if (Config.GAS_STATION_URL) {
+ this.sdk = new GaslessSdk(Config.GAS_STATION_URL)
+ }
+ }
+
+ private getSdk = async (): Promise => {
+ if (!this.sdk) {
+ return null
+ }
+ const appCheckToken = (await AppCheckService.getToken()).token
+ this.sdk.setAppCheckToken(appCheckToken)
+ return this.sdk
+ }
+
+ isEligibleForChain = async (chainId: string): Promise => {
+ const sdk = await this.getSdk()
+ if (!sdk) return false
+ return await sdk.isEligibleForChain({ chainId })
+ }
+
+ fundTx = async (
+ signingData: SigningData_EthSendTx,
+ addressFrom: string
+ ): Promise => {
+ const sdk = await this.getSdk()
+ if (!sdk) {
+ return {
+ error: {
+ category: 'DO_NOT_RETRY',
+ message: 'INTERNAL_ERROR'
+ }
+ }
+ }
+
+ const { difficulty, challengeHex } = await sdk.fetchChallenge()
+ const { solutionHex } = await sdk.solveChallenge(challengeHex, difficulty)
+ const txHex = Transaction.from({
+ ...signingData.data,
+ from: null
+ } as TransactionLike).unsignedSerialized
+ return await sdk.fundTx({
+ challengeHex,
+ solutionHex,
+ txHex,
+ from: addressFrom
+ })
+ }
+}
+
+export default new GaslessService()
diff --git a/packages/core-mobile/app/services/posthog/types.ts b/packages/core-mobile/app/services/posthog/types.ts
index a53b0331db..28bb4a7a9c 100644
--- a/packages/core-mobile/app/services/posthog/types.ts
+++ b/packages/core-mobile/app/services/posthog/types.ts
@@ -33,7 +33,8 @@ export enum FeatureGates {
BLOCKAID_DAPP_SCAN = 'blockaid-dapp-scan',
ALL_NOTIFICATIONS = 'all-notifications',
ENABLE_NOTIFICATION_PROMPT = 'enable-notification-prompt',
- HALLIDAY_BRIDGE_BANNER = 'halliday-bridge-banner'
+ HALLIDAY_BRIDGE_BANNER = 'halliday-bridge-banner',
+ GASLESS = 'gasless-feature'
}
export enum FeatureVars {
diff --git a/packages/core-mobile/app/store/posthog/slice.ts b/packages/core-mobile/app/store/posthog/slice.ts
index bdda349fce..a5440c0bd8 100644
--- a/packages/core-mobile/app/store/posthog/slice.ts
+++ b/packages/core-mobile/app/store/posthog/slice.ts
@@ -334,6 +334,14 @@ export const selectIsHallidayBridgeBannerBlocked = (
)
}
+export const selectIsGaslessBlocked = (state: RootState): boolean => {
+ const { featureFlags } = state.posthog
+ return (
+ !featureFlags[FeatureGates.GASLESS] ||
+ !featureFlags[FeatureGates.EVERYTHING]
+ )
+}
+
// actions
export const { regenerateUserId, toggleAnalytics, setFeatureFlags } =
posthogSlice.actions
diff --git a/packages/core-mobile/app/store/posthog/types.ts b/packages/core-mobile/app/store/posthog/types.ts
index 9a839db8fd..f99131d231 100644
--- a/packages/core-mobile/app/store/posthog/types.ts
+++ b/packages/core-mobile/app/store/posthog/types.ts
@@ -37,7 +37,8 @@ export const DefaultFeatureFlagConfig = {
[FeatureGates.UNIFIED_BRIDGE_AB_EVM]: true,
[FeatureGates.UNIFIED_BRIDGE_AB_AVA_TO_BTC]: true,
[FeatureGates.UNIFIED_BRIDGE_AB_BTC_TO_AVA]: true,
- [FeatureGates.HALLIDAY_BRIDGE_BANNER]: true
+ [FeatureGates.HALLIDAY_BRIDGE_BANNER]: true,
+ [FeatureGates.GASLESS]: true
}
export const initialState = {
diff --git a/packages/core-mobile/docs/features.md b/packages/core-mobile/docs/features.md
index 7b27a2ee59..4370557e3a 100644
--- a/packages/core-mobile/docs/features.md
+++ b/packages/core-mobile/docs/features.md
@@ -21,7 +21,7 @@
- Transaction Validation + Simulation (Blockaid)
- PROXY_URL
- In app browser (Token purchase, Suggested dApp list)
- - PROXY_URL
+ - PROXY_URL
- COINBASE_APP_ID
- DeFi Asset Balances
- PROXY_URL
@@ -43,4 +43,5 @@
- C-Chain Balance notifications
- APPCHECK_DEBUG_TOKEN
- NOTIFICATION_SENDER_API_URL
-
+- Gasless
+ - GAS_STATION_URL
diff --git a/packages/core-mobile/package.json b/packages/core-mobile/package.json
index c6039f36a7..1211006fe7 100644
--- a/packages/core-mobile/package.json
+++ b/packages/core-mobile/package.json
@@ -1,325 +1,326 @@
{
- "name": "@avalabs/core-mobile",
- "private": true,
- "license": "Limited Ecosystem License",
- "scripts": {
- "setup": "yarn allow-scripts",
- "envs": "./scripts/getEnvs.sh && ./scripts/getGoogleServices.sh",
- "android": "ENVFILE=.env.development react-native run-android --mode=internalDebug",
- "podInstall": "bundle _2.1.4_ install && cd ios && bundle exec pod install",
- "ios": "ENVFILE=.env.development react-native run-ios",
- "start": "react-native start",
- "test": "jest",
- "tsc": "tsc -p tsconfig.test.json",
- "lint": "eslint .",
- "clean": "./scripts/clean",
- "postinstall": "node ${PROJECT_CWD}/scripts/generate-metro-monorepo-config.js; node_modules/.bin/patch-package; node_modules/.bin/rn-nodeify --install events,stream,vm,assert,https,http,os,zlib,path,fs --hack; yarn run gen:contracts",
- "storybook-generate": "sb-rn-get-stories --v6-store",
- "storybook-watch": "sb-rn-watcher",
- "gen:contracts": "typechain --target=ethers-v6 --out-dir app/contracts/openzeppelin ./node_modules/@openzeppelin/contracts/build/contracts/ERC20.json ./node_modules/@openzeppelin/contracts/build/contracts/ERC721.json ./node_modules/@openzeppelin/contracts/build/contracts/ERC1155.json",
- "gen:glacierApi": "npx openapi-zod-client 'https://glacier-api-dev.avax.network/api-json' -o './app/utils/network/glacierApi.client.ts'"
- },
- "dependencies": {
- "@avalabs/avalanche-module": "1.4.3",
- "@avalabs/avalanchejs": "4.2.0-alpha.1",
- "@avalabs/bitcoin-module": "1.4.3",
- "@avalabs/bridge-unified": "4.0.1",
- "@avalabs/core-bridge-sdk": "3.1.0-alpha.34",
- "@avalabs/core-chains-sdk": "3.1.0-alpha.34",
- "@avalabs/core-coingecko-sdk": "3.1.0-alpha.34",
- "@avalabs/core-utils-sdk": "3.1.0-alpha.34",
- "@avalabs/core-wallets-sdk": "3.1.0-alpha.34",
- "@avalabs/evm-module": "1.4.3",
- "@avalabs/glacier-sdk": "3.1.0-alpha.34",
- "@avalabs/k2-alpine": "workspace:*",
- "@avalabs/k2-mobile": "workspace:*",
- "@avalabs/types": "3.1.0-alpha.34",
- "@avalabs/vm-module-types": "1.4.3",
- "@blockaid/client": "0.27.4",
- "@coinbase/cbpay-js": "2.2.1",
- "@cubist-labs/cubesigner-sdk": "0.4.117-0",
- "@cubist-labs/cubesigner-sdk-ethers-v6": "0.4.117-0",
- "@datadog/mobile-react-native": "2.4.3",
- "@datadog/mobile-react-navigation": "2.4.3",
- "@date-fns/utc": "2.1.0",
- "@ethereumjs/common": "4.4.0",
- "@ethereumjs/tx": "5.4.0",
- "@gorhom/bottom-sheet": "4.6.4",
- "@hookform/resolvers": "3.9.0",
- "@invertase/react-native-apple-authentication": "2.4.0",
- "@lavamoat/preinstall-always-fail": "2.1.0",
- "@metamask/eth-sig-util": "7.0.3",
- "@metamask/rpc-errors": "6.3.0",
- "@noble/secp256k1": "2.1.0",
- "@notifee/react-native": "9.1.1",
- "@openzeppelin/contracts": "5.0.2",
- "@paraswap/sdk": "7.2.1",
- "@react-native-async-storage/async-storage": "2.0.0",
- "@react-native-clipboard/clipboard": "1.14.2",
- "@react-native-community/blur": "4.4.1",
- "@react-native-community/datetimepicker": "8.2.0",
- "@react-native-community/netinfo": "11.4.1",
- "@react-native-community/slider": "4.5.3",
- "@react-native-firebase/app": "21.0.0",
- "@react-native-firebase/app-check": "21.0.0",
- "@react-native-firebase/messaging": "21.0.0",
- "@react-native-google-signin/google-signin": "13.1.0",
- "@react-native-masked-view/masked-view": "0.3.0",
- "@react-native-menu/menu": "1.1.3",
- "@react-navigation/bottom-tabs": "6.5.20",
- "@react-navigation/drawer": "6.7.2",
- "@react-navigation/elements": "1.3.31",
- "@react-navigation/native": "6.1.18",
- "@react-navigation/native-stack": "6.11.0",
- "@react-navigation/stack": "6.4.1",
- "@reduxjs/toolkit": "1.8.1",
- "@reown/walletkit": "^1.0.0",
- "@sentry/react-native": "5.33.2",
- "@shopify/flash-list": "1.7.1",
- "@shopify/react-native-performance": "4.1.2",
- "@shopify/react-native-skia": "0.1.233",
- "@tanstack/query-sync-storage-persister": "5.59.6",
- "@tanstack/react-query": "5.59.8",
- "@tanstack/react-query-persist-client": "5.59.6",
- "@tradle/react-native-http": "2.0.1",
- "@walletconnect/react-native-compat": "2.11.0",
- "@walletconnect/types": "2.17.2",
- "@walletconnect/utils": "2.17.2",
- "@zodios/core": "10.9.6",
- "assert": "2.1.0",
- "asyncstorage-down": "4.2.0",
- "axios": "1.7.7",
- "base-64": "1.0.0",
- "big.js": "6.2.2",
- "bip174": "2.1.0",
- "bip39": "3.0.4",
- "bitcoinjs-lib": "5.2.0",
- "bn.js": "5.2.1",
- "browserify-zlib": "0.2.0",
- "camelcase-keys": "9.1.3",
- "coinselect": "3.1.13",
- "crypto-browserify": "3.12.0",
- "date-fns": "4.1.0",
- "deprecated-react-native-prop-types": "5.0.0",
- "es6-promise": "4.2.8",
- "ethers": "6.8.1",
- "events": "3.3.0",
- "expo": "50.0.21",
- "expo-blur": "12.9.2",
- "expo-constants": "15.4.6",
- "expo-image": "1.10.6",
- "expo-linear-gradient": "12.7.2",
- "expo-linking": "6.2.2",
- "expo-router": "3.4.10",
- "expo-status-bar": "1.11.1",
- "https-browserify": "1.0.0",
- "jail-monkey": "2.8.0",
- "jimp": "1.6.0",
- "jsc-android": "294992.0.0",
- "lodash.debounce": "4.0.8",
- "lodash.isempty": "4.4.0",
- "lodash.isstring": "4.0.1",
- "lodash.merge": "4.6.2",
- "lottie-react-native": "7.0.0",
- "moment": "2.30.1",
- "node-cache": "5.1.2",
- "path-browserify": "1.0.1",
- "qrcode-reader": "1.0.4",
- "react": "18.3.1",
- "react-content-loader": "6.2.0",
- "react-hook-form": "7.53.0",
- "react-native": "0.73.7",
- "react-native-aes-crypto": "1.3.10",
- "react-native-argon2": "2.0.1",
- "react-native-big-list": "1.6.1",
- "react-native-bootsplash": "6.1.4",
- "react-native-camera": "4.2.1",
- "react-native-chart-kit": "6.12.0",
- "react-native-circular-progress": "1.4.0",
- "react-native-collapsible": "1.6.2",
- "react-native-collapsible-tab-view": "8.0.0",
- "react-native-config": "1.5.3",
- "react-native-device-info": "13.0.0",
- "react-native-fs": "2.20.0",
- "react-native-gesture-handler": "2.20.0",
- "react-native-graph": "1.1.0",
- "react-native-haptic-feedback": "2.0.3",
- "react-native-inappbrowser-reborn": "3.7.0",
- "react-native-keychain": "8.1.1",
- "react-native-level-fs": "3.0.1",
- "react-native-localize": "3.2.1",
- "react-native-mmkv": "2.12.2",
- "react-native-modal-datetime-picker": "18.0.0",
- "react-native-os": "1.2.6",
- "react-native-pager-view": "6.4.1",
- "react-native-passkey": "3.1.0",
- "react-native-performance": "5.1.2",
- "react-native-permissions": "4.1.5",
- "react-native-popable": "0.4.3",
- "react-native-popover-view": "6.1.0",
- "react-native-qrcode-scanner": "1.5.5",
- "react-native-qrcode-svg": "6.3.2",
- "react-native-quick-base64": "2.1.2",
- "react-native-quick-crypto": "0.6.1",
- "react-native-reanimated": "3.6.2",
- "react-native-redash": "18.1.3",
- "react-native-restart": "0.0.27",
- "react-native-root-siblings": "5.0.1",
- "react-native-safe-area-context": "4.11.0",
- "react-native-screens": "3.34.0",
- "react-native-sensors": "7.3.6",
- "react-native-sound": "0.11.2",
- "react-native-svg": "15.7.1",
- "react-native-tab-view": "3.5.1",
- "react-native-toast-notifications": "3.4.0",
- "react-native-url-polyfill": "2.0.0",
- "react-native-view-shot": "3.8.0",
- "react-native-webview": "ava-labs/react-native-webview",
- "react-native-webview-crypto": "0.0.26",
- "react-redux": "9.1.2",
- "react-timer-hook": "3.0.7",
- "readable-stream": "4.5.2",
- "redux-persist": "6.0.0",
- "redux-persist-transform-encrypt": "4.0.0",
- "rn-dominant-color": "1.7.2",
- "rn-nodeify": "10.3.0",
- "rxjs": "7.8.1",
- "semver": "7.5.4",
- "stream-browserify": "3.0.0",
- "text-encoding": "0.7.0",
- "type-fest": "4.26.1",
- "url": "0.11.4",
- "viem": "2.21.21",
- "vm-browserify": "1.1.2",
- "web3": "1.7.5",
- "xss": "1.0.15",
- "zod": "3.23.8"
- },
- "devDependencies": {
- "@avalabs/tsconfig-mobile": "workspace:*",
- "@babel/core": "7.25.7",
- "@babel/plugin-proposal-nullish-coalescing-operator": "7.18.6",
- "@babel/plugin-syntax-object-rest-spread": "7.8.3",
- "@babel/plugin-transform-class-static-block": "7.25.7",
- "@babel/plugin-transform-export-namespace-from": "7.25.7",
- "@babel/plugin-transform-private-methods": "7.25.7",
- "@babel/preset-env": "7.25.7",
- "@babel/preset-typescript": "7.25.7",
- "@babel/runtime": "7.25.7",
- "@dlenroc/testrail": "1.9.1",
- "@lavamoat/allow-scripts": "3.2.1",
- "@playwright/test": "1.48.0",
- "@react-native/babel-preset": "0.73.21",
- "@react-native/metro-config": "0.73.5",
- "@rushstack/eslint-patch": "1.10.4",
- "@spotlightjs/spotlight": "2.5.0",
- "@storybook/addon-ondevice-actions": "7.6.20",
- "@storybook/addon-ondevice-backgrounds": "7.6.20",
- "@storybook/addon-ondevice-controls": "7.6.20",
- "@storybook/react-native": "7.6.20",
- "@typechain/ethers-v6": "0.5.1",
- "@types/big.js": "6.2.2",
- "@types/bn.js": "5.1.6",
- "@types/d3": "7.4.3",
- "@types/jest": "29.5.13",
- "@types/lodash.debounce": "4.0.9",
- "@types/lodash.isempty": "4.4.9",
- "@types/lodash.isstring": "4.0.9",
- "@types/numeral": "2.0.5",
- "@types/react": "18.3.11",
- "@types/react-dom": "18.3.0",
- "@types/react-test-renderer": "18.3.0",
- "@types/semver": "7.5.4",
- "@welldone-software/why-did-you-render": "8.0.3",
- "babel-jest": "29.7.0",
- "babel-loader": "9.2.1",
- "babel-plugin-inline-dotenv": "1.7.0",
- "babel-plugin-module-resolver": "5.0.2",
- "babel-plugin-react-require": "4.0.3",
- "detox": "20.27.2",
- "eslint": "8.50.0",
- "eslint-plugin-avalabs-mobile": "workspace:*",
- "jest": "29.7.0",
- "json-stringify-pretty-compact": "4.0.0",
- "msw": "1.3.2",
- "node-fetch": "3.3.2",
- "patch-package": "8.0.0",
- "playwright-extra": "4.3.6",
- "postinstall-postinstall": "2.1.0",
- "puppeteer-extra-plugin-stealth": "2.11.2",
- "react-dom": "18.3.1",
- "react-native-svg-transformer": "1.5.0",
- "react-test-renderer": "18.3.1",
- "reactotron-react-native": "5.1.8",
- "reactotron-react-native-mmkv": "0.2.7",
- "reactotron-redux": "3.1.10",
- "ts-jest": "29.2.5",
- "ts-node": "10.9.2",
- "typechain": "8.3.2",
- "typescript": "5.6.3"
- },
- "engines": {
- "node": ">=18.14.1",
- "yarn": ">=3.6.4"
- },
- "lavamoat": {
- "allowScripts": {
- "$root$": true,
- "@avalabs/core-wallets-sdk>hdkey>secp256k1": false,
- "@avalabs/evm-module": false,
- "@avalabs/k2-mobile": false,
- "@avalabs/k2-alpine": false,
- "@datadog/mobile-react-native": false,
- "@lavamoat/preinstall-always-fail": false,
- "@sentry/react-native>@sentry/cli": false,
- "@storybook/react-native>@storybook/core-common>esbuild": false,
- "bitcoinjs-lib>tiny-secp256k1": false,
- "detox": false,
- "detox>bunyan>dtrace-provider": false,
- "msw": false,
- "postinstall-postinstall": false,
- "react-native-bootsplash>sharp": false,
- "react-native-inappbrowser-reborn": false,
- "viem>ws>bufferutil": false,
- "viem>ws>utf-8-validate": false,
- "web3": false,
- "web3>web3-bzz": false,
- "web3>web3-core>web3-core-requestmanager>web3-providers-ws>websocket>es5-ext": false,
- "web3>web3-shh": false,
- "web3>web3-utils>ethereumjs-util>ethereum-cryptography>keccak": false,
- "@react-native-firebase/app>firebase>@firebase/firestore>@grpc/proto-loader>protobufjs": false,
- "@avalabs/vm-module-types>hypersdk-client>@metamask/sdk>eciesjs>secp256k1": false,
- "web3>web3-core>web3-core-requestmanager>web3-providers-ws>websocket>bufferutil": false
- }
- },
- "react-native": {
- "http": "@tradle/react-native-http",
- "https": "https-browserify",
- "os": "react-native-os",
- "_stream_transform": "readable-stream/transform",
- "_stream_readable": "readable-stream/readable",
- "_stream_writable": "readable-stream/writable",
- "_stream_duplex": "readable-stream/duplex",
- "_stream_passthrough": "readable-stream/passthrough",
- "stream": "stream-browserify",
- "vm": "vm-browserify",
- "zlib": "browserify-zlib",
- "path": "path-browserify",
- "fs": "react-native-level-fs"
- },
- "browser": {
- "_stream_duplex": "readable-stream/duplex",
- "_stream_passthrough": "readable-stream/passthrough",
- "_stream_readable": "readable-stream/readable",
- "_stream_transform": "readable-stream/transform",
- "_stream_writable": "readable-stream/writable",
- "fs": "react-native-level-fs",
- "http": "@tradle/react-native-http",
- "https": "https-browserify",
- "os": "react-native-os",
- "path": "path-browserify",
- "stream": "stream-browserify",
- "vm": "vm-browserify",
- "zlib": "browserify-zlib"
+ "name": "@avalabs/core-mobile",
+ "private": true,
+ "license": "Limited Ecosystem License",
+ "scripts": {
+ "setup": "yarn allow-scripts",
+ "envs": "./scripts/getEnvs.sh && ./scripts/getGoogleServices.sh",
+ "android": "ENVFILE=.env.development react-native run-android --mode=internalDebug",
+ "podInstall": "bundle _2.1.4_ install && cd ios && bundle exec pod install",
+ "ios": "ENVFILE=.env.development react-native run-ios",
+ "start": "react-native start",
+ "test": "jest",
+ "tsc": "tsc -p tsconfig.test.json",
+ "lint": "eslint .",
+ "clean": "./scripts/clean",
+ "postinstall": "node ${PROJECT_CWD}/scripts/generate-metro-monorepo-config.js; node_modules/.bin/patch-package; node_modules/.bin/rn-nodeify --install events,stream,vm,assert,https,http,os,zlib,path,fs --hack; yarn run gen:contracts",
+ "storybook-generate": "sb-rn-get-stories --v6-store",
+ "storybook-watch": "sb-rn-watcher",
+ "gen:contracts": "typechain --target=ethers-v6 --out-dir app/contracts/openzeppelin ./node_modules/@openzeppelin/contracts/build/contracts/ERC20.json ./node_modules/@openzeppelin/contracts/build/contracts/ERC721.json ./node_modules/@openzeppelin/contracts/build/contracts/ERC1155.json",
+ "gen:glacierApi": "npx openapi-zod-client 'https://glacier-api-dev.avax.network/api-json' -o './app/utils/network/glacierApi.client.ts'"
+ },
+ "dependencies": {
+ "@avalabs/avalanche-module": "1.4.3",
+ "@avalabs/avalanchejs": "4.2.0-alpha.1",
+ "@avalabs/bitcoin-module": "1.4.3",
+ "@avalabs/bridge-unified": "4.0.1",
+ "@avalabs/core-bridge-sdk": "3.1.0-alpha.34",
+ "@avalabs/core-chains-sdk": "3.1.0-alpha.34",
+ "@avalabs/core-coingecko-sdk": "3.1.0-alpha.34",
+ "@avalabs/core-gasless-sdk": "3.1.0-alpha.40",
+ "@avalabs/core-utils-sdk": "3.1.0-alpha.34",
+ "@avalabs/core-wallets-sdk": "3.1.0-alpha.34",
+ "@avalabs/evm-module": "1.4.3",
+ "@avalabs/glacier-sdk": "3.1.0-alpha.34",
+ "@avalabs/k2-alpine": "workspace:*",
+ "@avalabs/k2-mobile": "workspace:*",
+ "@avalabs/types": "3.1.0-alpha.34",
+ "@avalabs/vm-module-types": "1.4.3",
+ "@blockaid/client": "0.27.4",
+ "@coinbase/cbpay-js": "2.2.1",
+ "@cubist-labs/cubesigner-sdk": "0.4.117-0",
+ "@cubist-labs/cubesigner-sdk-ethers-v6": "0.4.117-0",
+ "@datadog/mobile-react-native": "2.4.3",
+ "@datadog/mobile-react-navigation": "2.4.3",
+ "@date-fns/utc": "2.1.0",
+ "@ethereumjs/common": "4.4.0",
+ "@ethereumjs/tx": "5.4.0",
+ "@gorhom/bottom-sheet": "4.6.4",
+ "@hookform/resolvers": "3.9.0",
+ "@invertase/react-native-apple-authentication": "2.4.0",
+ "@lavamoat/preinstall-always-fail": "2.1.0",
+ "@metamask/eth-sig-util": "7.0.3",
+ "@metamask/rpc-errors": "6.3.0",
+ "@noble/secp256k1": "2.1.0",
+ "@notifee/react-native": "9.1.1",
+ "@openzeppelin/contracts": "5.0.2",
+ "@paraswap/sdk": "7.2.1",
+ "@react-native-async-storage/async-storage": "2.0.0",
+ "@react-native-clipboard/clipboard": "1.14.2",
+ "@react-native-community/blur": "4.4.1",
+ "@react-native-community/datetimepicker": "8.2.0",
+ "@react-native-community/netinfo": "11.4.1",
+ "@react-native-community/slider": "4.5.3",
+ "@react-native-firebase/app": "21.0.0",
+ "@react-native-firebase/app-check": "21.0.0",
+ "@react-native-firebase/messaging": "21.0.0",
+ "@react-native-google-signin/google-signin": "13.1.0",
+ "@react-native-masked-view/masked-view": "0.3.0",
+ "@react-native-menu/menu": "1.1.3",
+ "@react-navigation/bottom-tabs": "6.5.20",
+ "@react-navigation/drawer": "6.7.2",
+ "@react-navigation/elements": "1.3.31",
+ "@react-navigation/native": "6.1.18",
+ "@react-navigation/native-stack": "6.11.0",
+ "@react-navigation/stack": "6.4.1",
+ "@reduxjs/toolkit": "1.8.1",
+ "@reown/walletkit": "^1.0.0",
+ "@sentry/react-native": "5.33.2",
+ "@shopify/flash-list": "1.7.1",
+ "@shopify/react-native-performance": "4.1.2",
+ "@shopify/react-native-skia": "0.1.233",
+ "@tanstack/query-sync-storage-persister": "5.59.6",
+ "@tanstack/react-query": "5.59.8",
+ "@tanstack/react-query-persist-client": "5.59.6",
+ "@tradle/react-native-http": "2.0.1",
+ "@walletconnect/react-native-compat": "2.11.0",
+ "@walletconnect/types": "2.17.2",
+ "@walletconnect/utils": "2.17.2",
+ "@zodios/core": "10.9.6",
+ "assert": "2.1.0",
+ "asyncstorage-down": "4.2.0",
+ "axios": "1.7.7",
+ "base-64": "1.0.0",
+ "big.js": "6.2.2",
+ "bip174": "2.1.0",
+ "bip39": "3.0.4",
+ "bitcoinjs-lib": "5.2.0",
+ "bn.js": "5.2.1",
+ "browserify-zlib": "0.2.0",
+ "camelcase-keys": "9.1.3",
+ "coinselect": "3.1.13",
+ "crypto-browserify": "3.12.0",
+ "date-fns": "4.1.0",
+ "deprecated-react-native-prop-types": "5.0.0",
+ "es6-promise": "4.2.8",
+ "ethers": "6.8.1",
+ "events": "3.3.0",
+ "expo": "50.0.21",
+ "expo-blur": "12.9.2",
+ "expo-constants": "15.4.6",
+ "expo-image": "1.10.6",
+ "expo-linear-gradient": "12.7.2",
+ "expo-linking": "6.2.2",
+ "expo-router": "3.4.10",
+ "expo-status-bar": "1.11.1",
+ "https-browserify": "1.0.0",
+ "jail-monkey": "2.8.0",
+ "jimp": "1.6.0",
+ "jsc-android": "294992.0.0",
+ "lodash.debounce": "4.0.8",
+ "lodash.isempty": "4.4.0",
+ "lodash.isstring": "4.0.1",
+ "lodash.merge": "4.6.2",
+ "lottie-react-native": "7.0.0",
+ "moment": "2.30.1",
+ "node-cache": "5.1.2",
+ "path-browserify": "1.0.1",
+ "qrcode-reader": "1.0.4",
+ "react": "18.3.1",
+ "react-content-loader": "6.2.0",
+ "react-hook-form": "7.53.0",
+ "react-native": "0.73.7",
+ "react-native-aes-crypto": "1.3.10",
+ "react-native-argon2": "2.0.1",
+ "react-native-big-list": "1.6.1",
+ "react-native-bootsplash": "6.1.4",
+ "react-native-camera": "4.2.1",
+ "react-native-chart-kit": "6.12.0",
+ "react-native-circular-progress": "1.4.0",
+ "react-native-collapsible": "1.6.2",
+ "react-native-collapsible-tab-view": "8.0.0",
+ "react-native-config": "1.5.3",
+ "react-native-device-info": "13.0.0",
+ "react-native-fs": "2.20.0",
+ "react-native-gesture-handler": "2.20.0",
+ "react-native-graph": "1.1.0",
+ "react-native-haptic-feedback": "2.0.3",
+ "react-native-inappbrowser-reborn": "3.7.0",
+ "react-native-keychain": "8.1.1",
+ "react-native-level-fs": "3.0.1",
+ "react-native-localize": "3.2.1",
+ "react-native-mmkv": "2.12.2",
+ "react-native-modal-datetime-picker": "18.0.0",
+ "react-native-os": "1.2.6",
+ "react-native-pager-view": "6.4.1",
+ "react-native-passkey": "3.1.0",
+ "react-native-performance": "5.1.2",
+ "react-native-permissions": "4.1.5",
+ "react-native-popable": "0.4.3",
+ "react-native-popover-view": "6.1.0",
+ "react-native-qrcode-scanner": "1.5.5",
+ "react-native-qrcode-svg": "6.3.2",
+ "react-native-quick-base64": "2.1.2",
+ "react-native-quick-crypto": "0.6.1",
+ "react-native-reanimated": "3.6.2",
+ "react-native-redash": "18.1.3",
+ "react-native-restart": "0.0.27",
+ "react-native-root-siblings": "5.0.1",
+ "react-native-safe-area-context": "4.11.0",
+ "react-native-screens": "3.34.0",
+ "react-native-sensors": "7.3.6",
+ "react-native-sound": "0.11.2",
+ "react-native-svg": "15.7.1",
+ "react-native-tab-view": "3.5.1",
+ "react-native-toast-notifications": "3.4.0",
+ "react-native-url-polyfill": "2.0.0",
+ "react-native-view-shot": "3.8.0",
+ "react-native-webview": "ava-labs/react-native-webview",
+ "react-native-webview-crypto": "0.0.26",
+ "react-redux": "9.1.2",
+ "react-timer-hook": "3.0.7",
+ "readable-stream": "4.5.2",
+ "redux-persist": "6.0.0",
+ "redux-persist-transform-encrypt": "4.0.0",
+ "rn-dominant-color": "1.7.2",
+ "rn-nodeify": "10.3.0",
+ "rxjs": "7.8.1",
+ "semver": "7.5.4",
+ "stream-browserify": "3.0.0",
+ "text-encoding": "0.7.0",
+ "type-fest": "4.26.1",
+ "url": "0.11.4",
+ "viem": "2.21.21",
+ "vm-browserify": "1.1.2",
+ "web3": "1.7.5",
+ "xss": "1.0.15",
+ "zod": "3.23.8"
+ },
+ "devDependencies": {
+ "@avalabs/tsconfig-mobile": "workspace:*",
+ "@babel/core": "7.25.7",
+ "@babel/plugin-proposal-nullish-coalescing-operator": "7.18.6",
+ "@babel/plugin-syntax-object-rest-spread": "7.8.3",
+ "@babel/plugin-transform-class-static-block": "7.25.7",
+ "@babel/plugin-transform-export-namespace-from": "7.25.7",
+ "@babel/plugin-transform-private-methods": "7.25.7",
+ "@babel/preset-env": "7.25.7",
+ "@babel/preset-typescript": "7.25.7",
+ "@babel/runtime": "7.25.7",
+ "@dlenroc/testrail": "1.9.1",
+ "@lavamoat/allow-scripts": "3.2.1",
+ "@playwright/test": "1.48.0",
+ "@react-native/babel-preset": "0.73.21",
+ "@react-native/metro-config": "0.73.5",
+ "@rushstack/eslint-patch": "1.10.4",
+ "@spotlightjs/spotlight": "2.5.0",
+ "@storybook/addon-ondevice-actions": "7.6.20",
+ "@storybook/addon-ondevice-backgrounds": "7.6.20",
+ "@storybook/addon-ondevice-controls": "7.6.20",
+ "@storybook/react-native": "7.6.20",
+ "@typechain/ethers-v6": "0.5.1",
+ "@types/big.js": "6.2.2",
+ "@types/bn.js": "5.1.6",
+ "@types/d3": "7.4.3",
+ "@types/jest": "29.5.13",
+ "@types/lodash.debounce": "4.0.9",
+ "@types/lodash.isempty": "4.4.9",
+ "@types/lodash.isstring": "4.0.9",
+ "@types/numeral": "2.0.5",
+ "@types/react": "18.3.11",
+ "@types/react-dom": "18.3.0",
+ "@types/react-test-renderer": "18.3.0",
+ "@types/semver": "7.5.4",
+ "@welldone-software/why-did-you-render": "8.0.3",
+ "babel-jest": "29.7.0",
+ "babel-loader": "9.2.1",
+ "babel-plugin-inline-dotenv": "1.7.0",
+ "babel-plugin-module-resolver": "5.0.2",
+ "babel-plugin-react-require": "4.0.3",
+ "detox": "20.27.2",
+ "eslint": "8.50.0",
+ "eslint-plugin-avalabs-mobile": "workspace:*",
+ "jest": "29.7.0",
+ "json-stringify-pretty-compact": "4.0.0",
+ "msw": "1.3.2",
+ "node-fetch": "3.3.2",
+ "patch-package": "8.0.0",
+ "playwright-extra": "4.3.6",
+ "postinstall-postinstall": "2.1.0",
+ "puppeteer-extra-plugin-stealth": "2.11.2",
+ "react-dom": "18.3.1",
+ "react-native-svg-transformer": "1.5.0",
+ "react-test-renderer": "18.3.1",
+ "reactotron-react-native": "5.1.8",
+ "reactotron-react-native-mmkv": "0.2.7",
+ "reactotron-redux": "3.1.10",
+ "ts-jest": "29.2.5",
+ "ts-node": "10.9.2",
+ "typechain": "8.3.2",
+ "typescript": "5.6.3"
+ },
+ "engines": {
+ "node": ">=18.14.1",
+ "yarn": ">=3.6.4"
+ },
+ "lavamoat": {
+ "allowScripts": {
+ "$root$": true,
+ "@avalabs/core-wallets-sdk>hdkey>secp256k1": false,
+ "@avalabs/evm-module": false,
+ "@avalabs/k2-mobile": false,
+ "@avalabs/k2-alpine": false,
+ "@datadog/mobile-react-native": false,
+ "@lavamoat/preinstall-always-fail": false,
+ "@sentry/react-native>@sentry/cli": false,
+ "@storybook/react-native>@storybook/core-common>esbuild": false,
+ "bitcoinjs-lib>tiny-secp256k1": false,
+ "detox": false,
+ "detox>bunyan>dtrace-provider": false,
+ "msw": false,
+ "postinstall-postinstall": false,
+ "react-native-bootsplash>sharp": false,
+ "react-native-inappbrowser-reborn": false,
+ "viem>ws>bufferutil": false,
+ "viem>ws>utf-8-validate": false,
+ "web3": false,
+ "web3>web3-bzz": false,
+ "web3>web3-core>web3-core-requestmanager>web3-providers-ws>websocket>es5-ext": false,
+ "web3>web3-shh": false,
+ "web3>web3-utils>ethereumjs-util>ethereum-cryptography>keccak": false,
+ "@react-native-firebase/app>firebase>@firebase/firestore>@grpc/proto-loader>protobufjs": false,
+ "@avalabs/vm-module-types>hypersdk-client>@metamask/sdk>eciesjs>secp256k1": false,
+ "web3>web3-core>web3-core-requestmanager>web3-providers-ws>websocket>bufferutil": false
}
+ },
+ "react-native": {
+ "http": "@tradle/react-native-http",
+ "https": "https-browserify",
+ "os": "react-native-os",
+ "_stream_transform": "readable-stream/transform",
+ "_stream_readable": "readable-stream/readable",
+ "_stream_writable": "readable-stream/writable",
+ "_stream_duplex": "readable-stream/duplex",
+ "_stream_passthrough": "readable-stream/passthrough",
+ "stream": "stream-browserify",
+ "vm": "vm-browserify",
+ "zlib": "browserify-zlib",
+ "path": "path-browserify",
+ "fs": "react-native-level-fs"
+ },
+ "browser": {
+ "_stream_duplex": "readable-stream/duplex",
+ "_stream_passthrough": "readable-stream/passthrough",
+ "_stream_readable": "readable-stream/readable",
+ "_stream_transform": "readable-stream/transform",
+ "_stream_writable": "readable-stream/writable",
+ "fs": "react-native-level-fs",
+ "http": "@tradle/react-native-http",
+ "https": "https-browserify",
+ "os": "react-native-os",
+ "path": "path-browserify",
+ "stream": "stream-browserify",
+ "vm": "vm-browserify",
+ "zlib": "browserify-zlib"
+ }
}
diff --git a/yarn.lock b/yarn.lock
index edf0f222ac..ec0d932250 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -159,6 +159,16 @@ __metadata:
languageName: node
linkType: hard
+"@avalabs/core-gasless-sdk@npm:3.1.0-alpha.40":
+ version: 3.1.0-alpha.40
+ resolution: "@avalabs/core-gasless-sdk@npm:3.1.0-alpha.40"
+ dependencies:
+ "@noble/hashes": 1.7.1
+ zod: 3.23.8
+ checksum: 4e571c8830adaad07076ed82e4a1abfaf7224f335b873879794b171151a2ddc4c5b7e3d95467d188938306b0d07ed8cdea54ba3897a0801710e49fd61a8f3183
+ languageName: node
+ linkType: hard
+
"@avalabs/core-mobile@workspace:packages/core-mobile":
version: 0.0.0-use.local
resolution: "@avalabs/core-mobile@workspace:packages/core-mobile"
@@ -170,6 +180,7 @@ __metadata:
"@avalabs/core-bridge-sdk": 3.1.0-alpha.34
"@avalabs/core-chains-sdk": 3.1.0-alpha.34
"@avalabs/core-coingecko-sdk": 3.1.0-alpha.34
+ "@avalabs/core-gasless-sdk": 3.1.0-alpha.40
"@avalabs/core-utils-sdk": 3.1.0-alpha.34
"@avalabs/core-wallets-sdk": 3.1.0-alpha.34
"@avalabs/evm-module": 1.4.3
@@ -27103,7 +27114,7 @@ react-native-webview@ava-labs/react-native-webview:
peerDependencies:
react: "*"
react-native: "*"
- checksum: ae658f8c94177d1b419ffb73d26bd70474b6c688af507daaabc11451f983f77a906b2aafcdfdd09c96cab846d9722dcaf26a7c22ee7d19fb4cf70578c8812ac5
+ checksum: 856a172d7a0046e6d93bf3900031f7743e67103ad7de882acf86db5a90c595a70cb05df54c44ae532a8b3c6e20f3d744771335803b84930e6110d75e323f974b
languageName: node
linkType: hard