Skip to content

vsnation/BeamDappConnector

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 

Repository files navigation

BEAM DApp Connector

A robust, production-ready library for connecting BEAM dApps to various BEAM wallet environments.

BEAM TypeScript No Dependencies

Why This Library?

The original dapp-utils library has several issues:

  • No connection timeout - Hangs forever if wallet extension isn't installed
  • No automatic reconnection - Users must manually refresh after disconnect
  • Memory leaks - Event listeners never cleaned up
  • Race conditions - Multiple reconnect attempts create duplicate listeners
  • Poor error handling - No distinction between "not installed" vs "user rejected"

This library solves all these issues with a modern, Promise-based API.

Features

  • Automatic Reconnection - Exponential backoff with configurable attempts
  • Connection State Machine - Clear states: disconnected → connecting → connected
  • Event-Driven Architecture - Subscribe to connection state changes
  • Promise-Based API - All methods return Promises with proper timeouts
  • Heartbeat Monitoring - Detect stale connections automatically
  • Memory Safe - Proper cleanup of event listeners and pending calls
  • TypeScript Ready - Full type declarations included
  • Zero Dependencies - Pure JavaScript, ~15KB minified

Quick Start

Script Tag (Browser)

<script src="BeamDappConnector.js"></script>
<script>
    const connector = new BeamDappConnector({
        appName: 'My BEAM DApp',
        debug: true
    });

    connector.on('connected', () => {
        console.log('Connected to BEAM wallet!');
    });

    connector.connect()
        .then(() => connector.getWalletStatus())
        .then(status => console.log('Balance:', status.available));
</script>

ES Modules

import { BeamDappConnector, ConnectionState } from './BeamDappConnector.js';

const connector = new BeamDappConnector({
    appName: 'My BEAM DApp',
    apiVersion: 'current',
    autoReconnect: true,
    debug: true
});

// Listen to events
connector.on('connected', () => console.log('Connected!'));
connector.on('disconnected', () => console.log('Disconnected!'));
connector.on('locked', () => console.log('Wallet is locked'));

// Connect
await connector.connect();

// Call contract
const result = await connector.invokeContract(
    'action=view_params,cid=729fe098d9fd2b57705db1a05a74103dd4b891f535aef2ae69b47bcfdeef9cbf'
);

Supported Environments

Environment How it Works Detection
Desktop Wallet Qt WebChannel injection QtWebEngine user agent
Web Extension postMessage API Chrome browser, not mobile
Mobile App Document events (Android) / Callback (iOS) Mobile user agent
Headless WebAssembly client Manual headless: true option

Configuration

const connector = new BeamDappConnector({
    // App identification
    appName: 'My DApp',           // Required: shown in wallet
    apiVersion: 'current',        // API version to request
    minApiVersion: '',            // Minimum API version

    // Timeouts
    connectionTimeout: 30000,     // Connection timeout (30s)
    callTimeout: 60000,           // API call timeout (60s)

    // Reconnection
    autoReconnect: true,          // Auto-reconnect on disconnect
    reconnectDelay: 1000,         // Initial reconnect delay (1s)
    maxReconnectDelay: 30000,     // Max reconnect delay (30s)
    maxReconnectAttempts: 10,     // Max attempts (0 = infinite)

    // Heartbeat
    heartbeatInterval: 15000,     // Check connection every 15s

    // Headless mode
    headlessNode: 'eu-node01.masternet.beam.mw:8200',

    // UI
    showLoader: true,             // Show connection overlay

    // Debug
    debug: false                  // Enable console logging
});

API Reference

Connection

// Connect to wallet (auto-detects environment)
await connector.connect();

// Connect in headless mode (WebAssembly)
await connector.connect({ headless: true });

// Disconnect
await connector.disconnect();

// Check connection state
connector.isConnected();  // boolean
connector.getState();     // 'connected' | 'disconnected' | 'connecting' | ...
connector.getEnvironment(); // 'desktop' | 'web' | 'mobile' | 'headless'

Events

// Connection events
connector.on('connected', () => {});
connector.on('disconnected', () => {});
connector.on('connecting', () => {});
connector.on('reconnecting', () => {});
connector.on('reconnected', () => {});
connector.on('reconnectFailed', () => {});

// Wallet state events
connector.on('locked', () => {});
connector.on('unlocked', () => {});

// Error events
connector.on('error', (err) => console.error(err));

// State change (all states)
connector.on('stateChange', (newState, oldState) => {
    console.log(`State: ${oldState}${newState}`);
});

// Unsubscribe
const unsubscribe = connector.on('connected', handler);
unsubscribe(); // Remove listener

Wallet API

// Get wallet status (balance, sync state)
const status = await connector.getWalletStatus();
console.log('Available BEAM:', status.available);
console.log('Height:', status.current_height);

// Get addresses
const addresses = await connector.getAddressList();

// Get UTXOs
const utxos = await connector.getUtxoList(0); // 0 = BEAM

// Get transactions
const txs = await connector.getTxList({ count: 10 });

// Get assets
const assets = await connector.getAssetsList();

Smart Contracts

// View contract state (no transaction)
const params = await connector.invokeContract(
    'action=view_params,cid=729fe098...'
);

// Call contract with transaction
const result = await connector.invokeContract(
    'action=trade,cid=729fe098...,aid1=0,aid2=174,amount1=1000000000',
    null,  // No bytecode needed
    true   // createTx = true
);

// Process transaction data (confirm)
if (result.raw_data) {
    await connector.processInvokeData(result.raw_data);
}

// Load shader bytecode
const shaderBytes = await connector.downloadShader('/shaders/my_contract.wasm');
const result = await connector.invokeContract(
    'action=my_action',
    shaderBytes
);

Generic API Call

// Any wallet API method
const result = await connector.callApi('tx_send', {
    address: 'recipient_address',
    value: 100000000,  // 1 BEAM in groth
    asset_id: 0
});

Static Utilities

// Environment detection
BeamDappConnector.isChrome();   // true/false
BeamDappConnector.isMobile();   // true/false
BeamDappConnector.isDesktop();  // true/false

// Amount conversion
BeamDappConnector.beamToGroth(1.5);     // 150000000
BeamDappConnector.grothToBeam(150000000); // "1.50000000"

// Validation
BeamDappConnector.validateAmount("1.23"); // true
BeamDappConnector.validateAmount("abc");  // false

// Formatting
BeamDappConnector.formatAmount(1234567890); // "1,234,567,890"

Connection States

DISCONNECTED ──connect()──→ CONNECTING
                                │
                   ┌────────────┼────────────┐
                   ↓            ↓            ↓
               CONNECTED     ERROR      RECONNECTING
                   │            │            │
                   │            └────────────┤
                   ↓                         ↓
                LOCKED ←───────────── (auto-reconnect)
State Description
disconnected Not connected to any wallet
connecting Connection in progress
connected Successfully connected
reconnecting Attempting to reconnect
locked Wallet is locked (needs password)
error Connection failed

Error Handling

try {
    await connector.connect();
} catch (err) {
    if (err.message.includes('timeout')) {
        console.log('Connection timed out - is wallet installed?');
    } else if (err.message.includes('rejected')) {
        console.log('User rejected connection');
    } else if (err.message.includes('Chrome')) {
        console.log('Please use Chrome browser');
    }
}

// Handle locked wallet
connector.on('locked', () => {
    alert('Please unlock your BEAM wallet');
});

// Handle API errors
try {
    await connector.invokeContract('action=invalid');
} catch (err) {
    console.error('Contract error:', err.message);
}

Migration from dapp-utils

// OLD (dapp-utils)
Utils.initialize({
    appname: 'My App',
    apiResultHandler: handleResult
}, (err) => {
    if (err) return console.error(err);
    Utils.invokeContract('action=view', (err, res) => {
        // callback-based
    });
});

// NEW (BeamDappConnector)
const connector = new BeamDappConnector({ appName: 'My App' });
await connector.connect();
const res = await connector.invokeContract('action=view');
// Promise-based!

TypeScript Usage

import {
    BeamDappConnector,
    ConnectionState,
    WalletEnvironment,
    WalletStatus,
    ConnectorConfig
} from './BeamDappConnector';

const config: ConnectorConfig = {
    appName: 'My TypeScript DApp',
    debug: true
};

const connector = new BeamDappConnector(config);

connector.on('stateChange', (newState, oldState) => {
    if (newState === ConnectionState.CONNECTED) {
        console.log('Ready!');
    }
});

const status: WalletStatus = await connector.getWalletStatus();

Browser Compatibility

  • Chrome/Chromium 80+ (required for Web Extension)
  • Safari 14+ (headless mode only)
  • Firefox 78+ (headless mode only)
  • Edge 80+

Files

DappConnector/
├── BeamDappConnector.js    # Main library (~15KB)
├── BeamDappConnector.d.ts  # TypeScript declarations
└── README.md               # This file

License

MIT License - Free for commercial and personal use.

Links

About

DappConnector is a gateway between Dapps and Beam Desktop/Web/Mobile wallets.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors