Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 12 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"presets": [
"@babel/preset-env",
"@babel/preset-react",
"@babel/preset-typescript"
],
"plugins": [
["@babel/plugin-transform-react-jsx", {
"runtime": "automatic"
}]
]
}
31 changes: 31 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Publish Package to NPM
on:
push:
tags:
- 'v*'

jobs:
build-and-publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18.x'
registry-url: 'https://registry.npmjs.org'

- name: Install dependencies
run: yarn install --frozen-lockfile

- name: Run type check
run: yarn typecheck

- name: Build
run: yarn build

- name: Publish to NPM
run: yarn publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
28 changes: 28 additions & 0 deletions .github/workflows/quality.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Code Quality

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18.x'
cache: 'yarn'

- name: Install dependencies
run: yarn install --frozen-lockfile

- name: Run type check
run: yarn typecheck

- name: Run lint
run: yarn lint
28 changes: 28 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Development
.github/
.vscode/
example/
scripts/
src/__tests__/
*.test.ts
*.test.tsx
jest.config.js
tsconfig.json
tsconfig.build.json
.eslintrc
.prettierrc
.npmignore

# Dependencies
node_modules/

# Build artifacts
*.tgz
.DS_Store
.env
coverage/
.nyc_output/

# Documentation
CONTRIBUTING.md
CODE_OF_CONDUCT.md
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Changelog

## [0.1.0] - 2025-04-15

### Added
- Initial release
- Web3View component with EVM and Solana support
- MetaMask provider compatibility
- Phantom wallet compatibility
- Domain allowlist support
- TypeScript definitions
- Basic error handling and error boundary
- Example implementations for various wallet types
197 changes: 175 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,46 +1,199 @@
# react-native-dapp-explorer (⚠️Beta)
# React Native DApp Explorer

web3 dapp wallet injector webview for react-native

This package is still under development! CONTRIBUTIONS ARE HIGHLY APPRECIATED!
A lightweight Web3-enabled WebView component for React Native that supports both EVM (Ethereum) and Solana wallets. Drop-in replacement for react-native-webview with built-in wallet provider injection.

## Installation

```sh
### React Native CLI
```bash
npm install react-native-dapp-explorer
# or
yarn add react-native-dapp-explorer
```

### Expo
```bash
npx expo install react-native-dapp-explorer react-native-webview
```

## Usage
## Usage Examples

### 1. Using with ethers.js Private Key Wallet
```typescript
import { Web3View } from 'react-native-dapp-explorer';
import { ethers } from 'ethers';

const wallet = new ethers.Wallet(
'YOUR_PRIVATE_KEY',
new ethers.JsonRpcProvider('YOUR_RPC_URL')
);

const ethProvider = {
getAddress: async () => wallet.address,
getChainId: async () => 1, // mainnet
signMessage: async (message) => wallet.signMessage(message),
sendTransaction: async (tx) => {
const response = await wallet.sendTransaction(tx);
return response.hash;
}
};

export const DAppBrowser = () => (
<Web3View
url="https://app.uniswap.org"
ethProvider={ethProvider}
trustedDomains={['app.uniswap.org']}
/>
);
```

```js
import provider from ./web3wallet';
### 2. Using with wagmi
```typescript
import { Web3View } from 'react-native-dapp-explorer';
import { useAccount, useNetwork, useSignMessage, useSendTransaction } from 'wagmi';

const WagmiExample = () => {
const { address } = useAccount();
const { chain } = useNetwork();
const { signMessage } = useSignMessage();
const { sendTransaction } = useSendTransaction();

const ethProvider = {
getAddress: async () => address || '',
getChainId: async () => chain?.id || 1,
signMessage: async (message) => {
const result = await signMessage({ message });
return result;
},
sendTransaction: async (tx) => {
const result = await sendTransaction({ ...tx });
return result.hash;
}
};

return (
<Web3View
url="https://app.uniswap.org"
ethProvider={ethProvider}
trustedDomains={['app.uniswap.org']}
/>
);
};
```

function MyApp() {
### 3. Using with Solana Private Key Wallet
```typescript
import { Web3View } from 'react-native-dapp-explorer';
import { Keypair, Transaction } from '@solana/web3.js';
import * as bs58 from 'bs58';

const keypair = Keypair.fromSecretKey(bs58.decode('YOUR_PRIVATE_KEY'));

const solanaProvider = {
getPublicKey: async () => keypair.publicKey.toBase58(),
getCluster: async () => 'mainnet-beta',
signTransaction: async (serializedTx) => {
const tx = Transaction.from(Buffer.from(serializedTx, 'base64'));
tx.partialSign(keypair);
return Buffer.from(tx.serialize()).toString('base64');
},
signMessage: async (message) => {
const messageBytes = typeof message === 'string'
? new TextEncoder().encode(message)
: message;
const signature = keypair.sign(messageBytes);
return Buffer.from(signature).toString('base64');
}
};

export const SolanaDAppBrowser = () => (
<Web3View
url="https://jupiter.exchange"
solanaProvider={solanaProvider}
trustedDomains={['jupiter.exchange']}
/>
);
```

const ref = useRef();
### 4. Using with Solana Wallet Adapter
```typescript
import { Web3View } from 'react-native-dapp-explorer';
import { useWallet } from '@solana/wallet-adapter-react';
import { Transaction } from '@solana/web3.js';

const SolanaWalletKitExample = () => {
const wallet = useWallet();

const solanaProvider = {
getPublicKey: async () => wallet.publicKey?.toBase58() || '',
getCluster: async () => 'mainnet-beta',
signTransaction: async (serializedTx) => {
const tx = Transaction.from(Buffer.from(serializedTx, 'base64'));
const signedTx = await wallet.signTransaction(tx);
return Buffer.from(signedTx.serialize()).toString('base64');
},
signMessage: async (message) => {
const messageBytes = typeof message === 'string'
? new TextEncoder().encode(message)
: message;
const signature = await wallet.signMessage(messageBytes);
return Buffer.from(signature).toString('base64');
}
};

return (
<Web3InjectedWalletView
provider={provider}
chainId={1}
url="https://your-dapp-url.com"
style={{ flex: 1, width: '100%' }}
webviewRef={ref}
<Web3View
url="https://jupiter.exchange"
solanaProvider={solanaProvider}
trustedDomains={['jupiter.exchange']}
/>
);
};
```

## Props

| Prop | Type | Description |
|------|------|-------------|
| url | string | The URL to load in the WebView |
| ethProvider? | EthereumProvider | Ethereum wallet provider implementation |
| solanaProvider? | SolanaProvider | Solana wallet provider implementation |
| trustedDomains? | string[] | List of allowed domains |
| onError? | (error: Error) => void | Error handler callback |
| ...props | WebViewProps | Any valid react-native-webview props |

## Provider Interfaces

### EthereumProvider
```typescript
interface EthereumProvider {
getAddress(): Promise<string>;
getChainId(): Promise<number>;
signMessage(message: string): Promise<string>;
signTypedData?(data: any): Promise<string>;
sendTransaction(tx: any): Promise<string>;
}
```

### SolanaProvider
```typescript
interface SolanaProvider {
getPublicKey(): Promise<string>;
getCluster(): Promise<'mainnet-beta' | 'testnet' | 'devnet'>;
signTransaction(transaction: string): Promise<string>;
signAllTransactions(transactions: string[]): Promise<string[]>;
signMessage(message: Uint8Array | string): Promise<string>;
}
```

## Contributing
## Security

See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.
The Web3View component includes built-in security features:
- Domain allowlist through trustedDomains prop
- Transaction validation before signing
- Base64 transaction serialization
- No auto-injection on untrusted domains

## License

MIT

---

Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
12 changes: 12 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: [
[
'module-resolver',
{
root: ['./src'],
extensions: ['.ios.js', '.android.js', '.js', '.ts', '.tsx', '.json'],
alias: {
'@': './src'
}
}
]
]
};
43 changes: 43 additions & 0 deletions example/EthersWalletExample.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React, { useState } from 'react';
import { View, StyleSheet } from 'react-native';
import { Web3View } from 'react-native-dapp-explorer';
import { ethers } from 'ethers';

const EthersWalletExample = () => {
const [url] = useState('https://app.uniswap.org');

// Create a wallet from private key
const wallet = new ethers.Wallet(
'YOUR_PRIVATE_KEY', // Replace with your private key
new ethers.JsonRpcProvider('https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY')
);

const ethProvider = {
getAddress: async () => wallet.address,
getChainId: async () => 1, // mainnet
signMessage: async (message: string) => wallet.signMessage(message),
sendTransaction: async (tx: any) => {
const response = await wallet.sendTransaction(tx);
return response.hash;
}
};

return (
<View style={styles.container}>
<Web3View
url={url}
ethProvider={ethProvider}
trustedDomains={['app.uniswap.org']}
onError={console.error}
/>
</View>
);
};

const styles = StyleSheet.create({
container: {
flex: 1,
},
});

export default EthersWalletExample;
Loading
Loading