
Research
Malicious npm Packages Impersonate Flashbots SDKs, Targeting Ethereum Wallet Credentials
Four npm packages disguised as cryptographic tools steal developer credentials and send them to attacker-controlled Telegram infrastructure.
@avasapp/react-native-otp-autofill
Advanced tools
Automatic SMS Verification with the SMS Retriever API
An Expo module for automatic SMS verification using Android SMS Retriever API.
This module is available as an npm package. To use it in your Expo/React Native app:
npm install @avasapp/react-native-otp-autofill
bun add @avasapp/react-native-otp-autofill
yarn add @avasapp/react-native-otp-autofill
After installation, you can use the React hooks for the simplest integration:
import { useGetHash, useOtpListener } from '@avasapp/react-native-otp-autofill'
// In your component
const { hash } = useGetHash()
const { startListener, receivedOtp } = useOtpListener()
The module provides React hooks for easy integration with modern React apps:
import React from 'react'
import { useGetHash } from '@avasapp/react-native-otp-autofill'
const AppHashComponent = () => {
const { hash, loading, error, refetch } = useGetHash({
onSuccess: (hash) => {
console.log('App hash loaded:', hash)
},
onError: (error) => {
console.error('Failed to get app hash:', error)
},
})
if (loading) return <p>Loading app hash...</p>
if (error) return <p>Error: {error.message}</p>
return (
<div>
<p>App Hash: {hash}</p>
<button onClick={refetch}>Refresh Hash</button>
</div>
)
}
import React from 'react'
import { useOtpListener } from '@avasapp/react-native-otp-autofill'
const SmsVerificationComponent = () => {
const {
isListening,
loading,
receivedOtp,
receivedMessage,
error,
startListener,
stopListener,
} = useOtpListener({
onOtpReceived: (otp, message) => {
console.log('OTP received:', otp)
console.log('Full message:', message)
// Process the OTP
},
onTimeout: (message) => {
console.log('SMS timeout:', message)
},
onError: (error, code) => {
console.error('SMS error:', error, 'Code:', code)
},
})
return (
<div>
<button onClick={startListener} disabled={isListening || loading}>
{loading
? 'Starting...'
: isListening
? 'Listening...'
: 'Start SMS Listener'}
</button>
<button onClick={stopListener} disabled={!isListening}>
Stop Listener
</button>
{receivedOtp && <p>OTP: {receivedOtp}</p>}
{error && <p>Error: {error}</p>}
</div>
)
}
import React, { useState } from 'react'
import { useGetHash, useOtpListener } from '@avasapp/react-native-otp-autofill'
const SmsVerificationFlow = () => {
const [step, setStep] = useState<'hash' | 'sms' | 'complete'>('hash')
const [phoneNumber, setPhoneNumber] = useState('')
// Get app hash first
const {
hash,
loading: hashLoading,
error: hashError,
} = useGetHash({
onSuccess: (hash) => {
console.log('Ready to send SMS with hash:', hash)
setStep('sms')
},
})
// Listen for SMS
const { isListening, receivedOtp, startListener, stopListener } =
useOtpListener({
onOtpReceived: (otp) => {
console.log('Verification complete:', otp)
setStep('complete')
stopListener()
},
})
const sendSms = async () => {
if (!hash) return
// Send SMS with your backend API
await fetch('/api/send-sms', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
phoneNumber,
appHash: hash,
}),
})
// Start listening for SMS
startListener()
}
if (step === 'hash') {
return (
<div>
<p>Preparing SMS verification...</p>
{hashLoading && <p>Loading...</p>}
{hashError && <p>Error: {hashError.message}</p>}
{hash && <p>Ready! Hash: {hash}</p>}
</div>
)
}
if (step === 'sms') {
return (
<div>
<input
type="tel"
placeholder="Phone number"
value={phoneNumber}
onChange={(e) => setPhoneNumber(e.target.value)}
/>
<button onClick={sendSms} disabled={!phoneNumber}>
Send SMS
</button>
{isListening && <p>Waiting for SMS...</p>}
</div>
)
}
return (
<div>
<p>✅ Verification complete!</p>
<p>OTP: {receivedOtp}</p>
</div>
)
}
If you prefer direct control without hooks, use the module methods and event listeners:
import { AvasOtpAutofillModule } from '@avasapp/react-native-otp-autofill'
// Get app signature hash (use the first one)
const getAppHash = async (): Promise<string | undefined> => {
try {
const hashes = await AvasOtpAutofillModule.getHash()
console.log('App signature hashes:', hashes)
return hashes[0]
} catch (error) {
console.error('Error getting app hash:', error)
}
}
// Start listening for SMS and wire up events
const startSmsListener = async () => {
const subs = [] as import('expo-modules-core').EventSubscription[]
subs.push(
AvasOtpAutofillModule.addListener('onSmsReceived', ({ otp, message }) => {
console.log('OTP received:', otp)
console.log('Full message:', message)
// ...verify with your backend
}),
)
subs.push(
AvasOtpAutofillModule.addListener('onTimeout', ({ message }) => {
console.log('SMS timeout:', message)
}),
)
subs.push(
AvasOtpAutofillModule.addListener('onError', ({ message, code }) => {
console.error('SMS error:', message, 'code:', code)
}),
)
await AvasOtpAutofillModule.startOtpListener()
// Return a cleanup function
return () => {
subs.forEach((s) => s.remove())
AvasOtpAutofillModule.stopSmsRetriever()
}
}
import React, { useRef, useState } from 'react'
import { Button, Text, View } from 'react-native'
import { AvasOtpAutofillModule } from '@avasapp/react-native-otp-autofill'
export const ManualSmsVerification = () => {
const [otp, setOtp] = useState<string | null>(null)
const [message, setMessage] = useState<string | null>(null)
const [isListening, setIsListening] = useState(false)
const cleanupRef = useRef<null | (() => void)>(null)
const start = async () => {
if (isListening) return
setIsListening(true)
setOtp(null)
setMessage(null)
// Register listeners first
const subs = [
AvasOtpAutofillModule.addListener('onSmsReceived', ({ otp, message }) => {
setOtp(otp ?? null)
setMessage(message)
setIsListening(false)
cleanup()
}),
AvasOtpAutofillModule.addListener('onTimeout', () => {
setIsListening(false)
cleanup()
}),
AvasOtpAutofillModule.addListener('onError', () => {
setIsListening(false)
cleanup()
}),
]
const cleanup = () => {
subs.forEach((s) => s.remove())
AvasOtpAutofillModule.stopSmsRetriever()
cleanupRef.current = null
}
cleanupRef.current = cleanup
await AvasOtpAutofillModule.startOtpListener()
}
const stop = () => {
cleanupRef.current?.()
setIsListening(false)
}
return (
<View>
<Button title={isListening ? 'Listening…' : 'Start SMS Listener'} onPress={start} disabled={isListening} />
{isListening && <Button title="Stop" onPress={stop} />}
{otp && <Text>OTP: {otp}</Text>}
{message && <Text>Message: {message}</Text>}
</View>
)
}
useGetHash(options?: UseGetHashOptions): UseGetHashReturn
A React hook for managing app signature hash retrieval with automatic loading states and error handling.
Options:
interface UseGetHashOptions {
onSuccess?: (value: string) => void
onError?: (error: Error) => void
}
Returns:
interface UseGetHashReturn {
hash: string | null // The app signature hash
loading: boolean // Whether hash is being fetched
error: Error | null // Any error that occurred
refetch: () => Promise<void> // Function to refetch the hash
}
Example:
const { hash, loading, error, refetch } = useGetHash({
onSuccess: (hash) => console.log('Hash:', hash),
onError: (error) => console.error('Error:', error),
})
useOtpListener(options?: UseOtpListenerOptions): UseOtpListenerReturn
A React hook for managing SMS OTP listening with automatic cleanup and state management.
Options:
interface UseOtpListenerOptions {
onOtpReceived?: (otp: string, message: string) => void
onTimeout?: (message: string) => void
onError?: (error: string, code: number) => void
}
Returns:
interface UseOtpListenerReturn {
isListening: boolean // Whether actively listening for SMS
loading: boolean // Whether starting/stopping listener
receivedOtp: string | null // Last received OTP
receivedMessage: string | null // Full SMS message
error: string | null // Any error message
startListener: () => Promise<void> // Start listening for SMS
stopListener: () => void // Stop listening and cleanup
}
Example:
const { isListening, receivedOtp, startListener, stopListener } =
useOtpListener({
onOtpReceived: (otp, message) => {
console.log('OTP:', otp, 'Message:', message)
},
})
getOtp(): Promise<boolean>
Starts the SMS retriever and returns whether it was successfully started.
const success = await AvasOtpAutofill.getOtp()
getHash(): Promise<string[]>
Returns the app signature hashes needed for SMS verification.
const hashes = await AvasOtpAutofill.getHash()
requestHint(): Promise<string>
Requests a phone number hint (placeholder implementation).
const hint = await AvasOtpAutofill.requestHint()
startOtpListener(): Promise<boolean>
Starts listening for SMS using the Android SMS Retriever API.
await AvasOtpAutofillModule.startOtpListener()
addListener(eventName, listener): EventSubscription
Adds a listener for module events. Call before startOtpListener()
.
const sub = AvasOtpAutofillModule.addListener('onSmsReceived', ({ otp, message }) => {
console.log('OTP:', otp, 'Message:', message)
})
Use the returned subscription to remove listeners, and stop the retriever when done.
sub.remove()
await AvasOtpAutofillModule.stopSmsRetriever()
The module emits the following events:
onSmsReceived
: When an SMS is received with OTPonTimeout
: When SMS retriever times out (after 5 minutes)onError
: When an error occursEvent payloads:
type SmsReceivedEventPayload = {
message: string
otp: string | null
}
type TimeoutEventPayload = {
message: string
}
type ErrorEventPayload = {
message: string
code: number
}
For the SMS Retriever API to work, the SMS message must:
Your verification code is: 123456
FA+9qCX9VSu
Where FA+9qCX9VSu
is your app's signature hash.
stopListener()
before starting a new listenerimport { useGetHash, useOtpListener } from '@avasapp/react-native-otp-autofill'
const DebugComponent = () => {
const { hash, loading, error } = useGetHash({
onSuccess: (hash) => console.log('✅ Hash loaded:', hash),
onError: (error) => console.error('❌ Hash error:', error),
})
const {
isListening,
receivedOtp,
receivedMessage,
error: smsError,
} = useOtpListener({
onOtpReceived: (otp, message) => {
console.log('📱 SMS received:', { otp, message })
},
onTimeout: (message) => {
console.log('⏰ SMS timeout:', message)
},
onError: (error, code) => {
console.error('❌ SMS error:', { error, code })
},
})
return (
<div>
<p>Hash: {hash || 'Loading...'}</p>
<p>Listening: {isListening ? 'Yes' : 'No'}</p>
<p>OTP: {receivedOtp || 'None'}</p>
{error && <p>Hash Error: {error.message}</p>}
{smsError && <p>SMS Error: {smsError}</p>}
</div>
)
}
import { AvasOtpAutofillModule } from '@avasapp/react-native-otp-autofill'
// Listen to all events for debugging
AvasOtpAutofillModule.addListener('onSmsReceived', (event) => {
console.log('📱 SMS received:', event)
})
AvasOtpAutofillModule.addListener('onTimeout', (event) => {
console.log('⏰ SMS timeout:', event)
})
AvasOtpAutofillModule.addListener('onError', (event) => {
console.log('❌ SMS error:', event)
})
stopListener()
when component unmountsrefetch()
from useGetHash
instead of creating new instances# Install dependencies
bun install
# Build the module
bun run build
# Clean build artifacts
bun run clean
# Run linting
bun run lint
# Run tests
bun run test
This package is published to the npm registry. To publish a new version:
package.json
bun run build
npm publish --access public
For local development and testing:
# Link the package locally
bun link
# In your test project
bun link @avasapp/react-native-otp-autofill
MIT
FAQs
Automatic SMS Verification with the SMS Retriever API
The npm package @avasapp/react-native-otp-autofill receives a total of 9 weekly downloads. As such, @avasapp/react-native-otp-autofill popularity was classified as not popular.
We found that @avasapp/react-native-otp-autofill demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Four npm packages disguised as cryptographic tools steal developer credentials and send them to attacker-controlled Telegram infrastructure.
Security News
Ruby maintainers from Bundler and rbenv teams are building rv to bring Python uv's speed and unified tooling approach to Ruby development.
Security News
Following last week’s supply chain attack, Nx published findings on the GitHub Actions exploit and moved npm publishing to Trusted Publishers.