npm package: @jim4565/dapp-wrapper
Description: An abstraction layer over the Incentiv dApp SDK for simplified dApp development
Author: Incentiv dApp-Wrapper Team
Latest version: 1.5.0

# Incentiv dApp-Wrapper

A minimized, focused wrapper around the Incentiv dApp SDK for core blockchain interactions.

## Features

- **Core Blockchain Functions**: `connect`, `getProvider`, `getSigner`, `getUserAddress`, `isConnected`
- **Smart Contract Integration**: Register contracts and call methods with simplified API
- **Single Instance**: One wrapper instance across your entire frontend application
- **TypeScript Support**: Full type safety with comprehensive interfaces
- **Error Handling**: Specific error codes and user-friendly messages

## Installation

```bash
npm install @jim4565/dapp-wrapper
```

### Peer Dependencies

```bash
npm install @incentiv/dapp-sdk ethers
```

## Quick Start

### Basic Setup

```typescript
import { IncentivWrapper } from '@jim4565/dapp-wrapper';

// 1. Create wrapper (network configuration is built-in)
const wrapper = new IncentivWrapper();

// 2. Connect to wallet (triggers Incentiv Portal popup)
const userAddress = await wrapper.connect();

// 3. Register contracts (only way to add contracts)
wrapper.registerContract("SOLO", "0x1234...", yourABI);
```

### Perfect API Usage

```typescript
// Your preferred API - exactly as you described!
// Directly call contract methods - transactions are sent automatically!
const receipt = await wrapper.getContract("SOLO").playGame(true);

// View/pure functions return values directly
const gameDetails = await wrapper.getContract("SOLO").getGameDetails(1);
const balance = await wrapper.getContract("SOLO").getBalance();

// State-changing functions return transaction receipt
const txReceipt = await wrapper.getContract("SOLO").deposit(100);
console.log('Transaction hash:', txReceipt.hash);

// Payable functions with ETH value (only works with payable functions!)
const gameReceipt = await wrapper.getContract("SOLO").playGame(true).withValue("0.1");
const depositReceipt = await wrapper.getContract("SOLO").deposit(100).withValue("3");

// Wait for confirmation
const confirmedReceipt = await txReceipt.wait();
console.log('Confirmed in block:', confirmedReceipt.blockNumber);

// Core functions
const provider = wrapper.getProvider();
const signer = wrapper.getSigner();
const address = wrapper.getUserAddress();
const connected = await wrapper.isConnected();
```

## Global Usage Pattern

Use one wrapper instance across your entire frontend:

```typescript
// services/blockchain.ts
export class BlockchainService {
  private static instance: IncentivWrapper;

  static getInstance(): IncentivWrapper {
    if (!BlockchainService.instance) {
      BlockchainService.instance = new IncentivWrapper();
      
      // Register contracts after creation
      BlockchainService.instance.registerContracts([
        { name: "Game", address: "0x123...", abi: gameABI },
        { name: "Token", address: "0x456...", abi: tokenABI }
      ]);
    }
    return BlockchainService.instance;
  }
}

// In any component
const wrapper = BlockchainService.getInstance();
```

## Wallet Connection

The wrapper provides multiple ways to connect to the Incentiv wallet:

### Standard Connection
```typescript
try {
  const userAddress = await wrapper.connect();
  console.log('Connected as:', userAddress);
} catch (error) {
  if (error.code === 'USER_REJECTED') {
    console.log('User rejected wallet connection');
  }
}
```

### Alternative Connection Methods
```typescript
// Force fresh connection (clears existing session)
const address = await wrapper.connectFresh();
```

### Connection Status
```typescript
// Check if already connected
const isConnected = await wrapper.isConnected();

// Get current user address
const userAddress = wrapper.getUserAddress();

```

## API Reference

### Core Methods

- **`connect()`**: Connect to wallet (triggers Incentiv Portal popup)
- **`connectFresh()`**: Force fresh wallet connection
- **`getProvider()`**: Get ethers provider
- **`getSigner()`**: Get current signer
- **`getUserAddress()`**: Get connected user address
- **`isConnected()`**: Check connection status
- **`createSigner(address)`**: Create new signer instance

### Contract Management

- **`registerContract(name, address, abi)`**: Register a single contract
- **`registerContracts(contracts[])`**: Register multiple contracts
- **`getRegisteredContracts()`**: Get list of all registered contract names
- **`hasContract(name)`**: Check if a contract is registered
- **`getContractAddress(name)`**: Get contract address by name
- **`getContractMethods(name)`**: Get all function names for a contract
- **`getContract(name)`**: Get contract instance by name

### Contract Access

Contracts are accessed through the `getContract()` method for better encapsulation:

```typescript
// Get contract instance
const soloContract = wrapper.getContract("SOLO");

// Check if contract exists
if (!soloContract) {
  console.log("Contract not found!");
  return;
}

// Access contract properties
console.log(soloContract.address);
console.log(soloContract.abi);

// Direct method calls - automatically sends transactions or reads state
const txReceipt = await wrapper.getContract("SOLO").playGame(true);
console.log('Transaction sent:', txReceipt.hash);

// With ETH value for payable functions
const payableReceipt = await wrapper.getContract("SOLO").playGame(true).withValue("0.1");
console.log('Payable transaction sent:', payableReceipt.hash);

// Alternative: Store contract reference for multiple uses
const game = wrapper.getContract("SOLO");
const gameReceipt = await game.playGame(true);
const gameWithValue = await game.deposit(100).withValue("3");
const gameStatus = await game.getGameStatus(); // Returns value directly for view functions
```

## Validation Rules

### withValue() Usage

The `withValue()` method has strict validation rules:

```typescript
// ✅ Correct usage - only on payable functions
const receipt = await wrapper.getContract("Game").deposit(100).withValue("1.0");

// ❌ This will throw an error - view functions cannot receive ETH
try {
  await wrapper.getContract("Game").getBalance().withValue("1.0");
} catch (error) {
  console.error(error.message); 
  // "Cannot use withValue() on function 'getBalance' with stateMutability 'view'. Only 'payable' functions accept ETH value."
}

// ❌ This will throw an error - nonpayable functions cannot receive ETH
try {
  await wrapper.getContract("Game").updateSettings().withValue("1.0");
} catch (error) {
  console.error(error.message);
  // "Cannot use withValue() on function 'updateSettings' with stateMutability 'nonpayable'. Only 'payable' functions accept ETH value."
}

// ❌ This will throw an error - invalid ETH value
try {
  await wrapper.getContract("Game").deposit(100).withValue("-1.0");
} catch (error) {
  console.error(error.message);
  // "Invalid ETH value '-1.0'. Value must be a positive number."
}
```

## Error Handling

```typescript
try {
  // Direct method call - automatically sends transaction
  const result = await wrapper.getContract("Game").playGame(player, bet);
  console.log('Game transaction:', result.hash);
  
  // With ETH value for payable functions only
  const payableResult = await wrapper.getContract("Game").deposit(100).withValue("1.5");
  console.log('Payable transaction:', payableResult.hash);
  
  // This will throw an error - withdraw is not payable!
  // const errorResult = await wrapper.getContract("Game").withdraw().withValue("1.0");
  
  // Wait for confirmation
  const receipt = await result.wait();
  console.log('Confirmed in block:', receipt.blockNumber);
} catch (error) {
  if (error.code === 'WALLET_NOT_CONNECTED') {
    // Handle not connected
  } else if (error.code === 'USER_REJECTED') {
    // Handle user rejection
  } else if (error.code === 'INSUFFICIENT_FUNDS') {
    // Handle insufficient funds
  } else if (error.message.includes('Only \'payable\' functions')) {
    // Handle incorrect withValue() usage
    console.error('Cannot send ETH to non-payable function');
  } else if (error.message.includes('Invalid ETH value')) {
    // Handle invalid ETH value
    console.error('ETH value must be a positive number');
  }
}
```

## TypeScript Support

Full TypeScript support with interfaces:

```typescript
import { 
  IncentivWrapper, 
  TransactionResponse 
} from '@jim4565/dapp-wrapper';

// Simple creation - network is pre-configured
const wrapper = new IncentivWrapper();
```

## License

MIT License - see [LICENSE](LICENSE) file for details.

## Support

- **GitHub Issues**: [Repository Issues](https://github.com/jim4565/dApp-Wrapper/issues)
- **NPM Package**: [@jim4565/dapp-wrapper](https://www.npmjs.com/package/@jim4565/dapp-wrapper)