Understand how the DropFi mobile app injects the window.xrpl provider into web views. This script enables DApps to communicate with the DropFi mobile wallet through React Native WebView.
How window.xrpl is injected into mobile web views
Uses ReactNativeWebView.postMessage for native communication
Checks for existing provider to prevent multiple injections
Implements event listeners for wallet state changes
All methods return promises for async operations
(function () {
if (!window.ReactNativeWebView) return;
if (window.xrpl && window.xrpl.isDropFi) { true; return; }
console.log('[DropFi] Injected XRPL provider');
const XRPLProvider = {
isDropFi: true,
selectedAddress: null,
selectedNetwork: null,
connectedAccounts: [],
network: null,
endpoint: null,
listeners: {},
resolvers: {},
_emit(event, payload) {
(this.listeners[event] || []).forEach((fn) => fn(payload));
},
on(event, cb) {
this.listeners[event] = this.listeners[event] || [];
this.listeners[event].push(cb);
},
off(event, cb) {
if (!this.listeners[event]) return;
this.listeners[event] = this.listeners[event].filter((fn) => fn !== cb);
},
createXRPLPromise(method, data = {}) {
return new Promise((resolve) => {
this.resolvers[method] = (result) => {
if (method === 'xrpl_stateRequest' && result) {
this.selectedAddress = result.selectedAddress;
this.connectedAccounts = result.connectedAccounts;
this.network = result.network;
this.endpoint = result.endpoint;
this._emit('xrpl_selectedAddress', result.selectedAddress);
this._emit('xrpl_connectedAccounts', result.connectedAccounts);
this._emit('xrpl_selectedNetwork', result.network);
} else if (method === 'xrpl_connect' && result) {
this.selectedAddress = result;
this.connectedAccounts = [result];
this._emit('xrpl_selectedAddress', result);
this._emit('xrpl_connectedAccounts', [result]);
} else if (method === 'xrpl_disconnect') {
const old = this.selectedAddress;
this.selectedAddress = null;
this.connectedAccounts = [];
this._emit('xrpl_selectedAddress', null);
this._emit('xrpl_connectedAccounts', []);
this._emit('xrpl_disconnect', old);
} else if (method === 'xrpl_switchNetwork' && result) {
this.network = result.network;
this.endpoint = result.endpoint;
this._emit('xrpl_selectedNetwork', result.network);
}
resolve(result);
};
window.ReactNativeWebView.postMessage(
JSON.stringify({ method, params: data })
);
});
},
handleHostResponse(message) {
try {
const { method, payload } = JSON.parse(message?.data || '{}');
if (this.resolvers[method]) {
this.resolvers[method](payload);
}
} catch (err) {
console.warn('[DropFi] Failed to handle host response', err);
}
},
connect: (data) => XRPLProvider.createXRPLPromise('xrpl_connect', { data }),
disconnect: () => XRPLProvider.createXRPLPromise('xrpl_disconnect'),
signMessage: (message) => XRPLProvider.createXRPLPromise('xrpl_signMessage', { message }),
sendTransaction: (tx) => XRPLProvider.createXRPLPromise('xrpl_sendTransaction', { tx }),
switchNetwork: (networkId) => XRPLProvider.createXRPLPromise('xrpl_switchNetwork', { networkId }),
changeAccount: (account) => XRPLProvider.createXRPLPromise('xrpl_changeAccount', { account }),
initialize: () => XRPLProvider.createXRPLPromise('xrpl_stateRequest'),
isConnected: () => !!XRPLProvider.selectedAddress,
};
window.addEventListener('message', (event) => {
XRPLProvider.handleHostResponse(event);
});
window.xrpl = XRPLProvider;
})();First checks if window.ReactNativeWebView exists before injecting
Checks if window.xrpl already exists and is a DropFi provider to prevent double injection
Uses JSON.stringify for postMessage instead of direct object passing
Parses message.data as JSON in handleHostResponse, with error handling
Verifies ReactNativeWebView exists and prevents duplicate injection
Only injects if running in a React Native WebView and no existing DropFi provider is found
The main provider object that implements the window.xrpl API
Contains state properties (selectedAddress, network, etc.) and methods for wallet interaction
Implements on/off/_emit for event-driven communication
Allows DApps to listen for wallet state changes like address changes or network switches
createXRPLPromise creates promises that resolve when the app responds
Each method (connect, disconnect, etc.) uses this pattern to handle async communication
Uses ReactNativeWebView.postMessage to communicate with the native app
Messages are JSON stringified and sent to the native layer for processing
Automatically updates internal state when responses are received
Updates selectedAddress, connectedAccounts, network, and endpoint based on app responses
DApp calls window.xrpl.connect() → createXRPLPromise → ReactNativeWebView.postMessage → Native app receives message
Native app sends response → window.addEventListener receives → handleHostResponse → JSON.parse → resolver called → Promise resolves
handleHostResponse includes try-catch to gracefully handle JSON parsing errors
connect(data)Initiates connection to DropFi wallet
Parameters: data: object (optional) - returns Promise<string>
disconnect()Disconnects from the wallet
Parameters: None - returns Promise<void>
signMessage(message)Signs a message using the connected wallet
Parameters: message: string - returns Promise<string>
sendTransaction(tx)Sends a transaction to the XRP Ledger
Parameters: tx: object - returns Promise<string>
switchNetwork(networkId)Switches the active network
Parameters: networkId: string - returns Promise<object>
changeAccount(account)Changes the active account
Parameters: account: string - returns Promise<void>
initialize()Initializes and requests current wallet state
Parameters: None - returns Promise<object>
isConnected()Checks if wallet is currently connected
Parameters: None - returns boolean
// Listen for address changes
window.xrpl.on('xrpl_selectedAddress', (address) => {
console.log('Address changed:', address);
});
// Listen for network changes
window.xrpl.on('xrpl_selectedNetwork', (network) => {
console.log('Network changed:', network);
});
// Listen for disconnect events
window.xrpl.on('xrpl_disconnect', (oldAddress) => {
console.log('Disconnected from:', oldAddress);
});
// Remove event listener
const handler = (address) => console.log(address);
window.xrpl.on('xrpl_selectedAddress', handler);
window.xrpl.off('xrpl_selectedAddress', handler);