> ## Documentation Index
> Fetch the complete documentation index at: https://docs.incentiv.io/llms.txt
> Use this file to discover all available pages before exploring further.

# SDK Guide

> A comprehensive guide for integrating the Incentiv DApp SDK into your application. This guide covers installation, wallet connection, transaction signing, payments, and best practices.

## Overview

The Incentiv SDK (`@incentiv/dapp-sdk`) provides a seamless way to connect your decentralized application (dApp) to the Incentiv blockchain network. It includes:

* **IncentivResolver**: For wallet connection and account management
* **IncentivSigner**: For signing transactions and interacting with smart contracts

The SDK supports Account Abstraction (AA) wallets, enabling gasless transactions and improved user experience.

## Prerequisites

Before you begin, ensure you have:

* **Node.js** (v16 or higher)
* **npm** or **yarn** package manager
* A modern browser with **popups** enabled for your application
* Basic knowledge of:
  * React/JavaScript
  * Ethereum/Web3 concepts
  * Smart contracts

## Installation

### Step 1: Install the SDK

Install the Incentiv DApp SDK using npm:

```bash theme={null}
npm install @incentiv/dapp-sdk ethers@^6.13.0
```

Or using yarn:

```bash theme={null}
yarn add @incentiv/dapp-sdk ethers
```

### Step 2: Install Additional Dependencies

The SDK requires `buffer` for Node.js compatibility in browser environments:

```bash theme={null}
npm install buffer
```

## Project Setup

### Vite Configuration

If you're using Vite, configure it to support the SDK:

**`vite.config.js:`**

```javascript theme={null}
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  define: {
    global: 'globalThis',
  },
  resolve: {
    alias: {
      buffer: 'buffer',
    },
  },
  optimizeDeps: {
    include: ['buffer'],
  },
});
```

### Environment Variables

Create a configuration file for your environment settings:

**`src/config.js:`**

```javascript theme={null}
const Config = {
  SC: {
    address: "0xC3A195E....." // Your contract address
  },
  Environments: {
    Mainnet: {
      Portal: "https://portal.incentiv.io", 
      RPC: "https://rpc.incentiv.io" 
    }
  },
  API: {
    baseURL: import.meta.env.VITE_API_URL || "https://your-api.com/api"
  }
};

export default Config;
```

### Environment Variables Template

Create a `.env` file (or use environment variables):

```env theme={null}
VITE_API_URL=https://your-api.com/api
VITE_ENVIRONMENT=mainnet
VITE_CONTRACT_ADDRESS=0xC3A195E.....
```

## Connecting to Wallet

### Step 1: Import Required Modules

```javascript theme={null}
import { IncentivResolver, IncentivSigner } from "@incentiv/dapp-sdk";
import { ethers } from "ethers";
import Config from "./config";
```

### Step 2: Connect to Wallet

The `IncentivResolver` is used to connect to the user's wallet and get their account address:

```javascript theme={null}
const handleConnect = async () => {
  try {
    // Get the user's wallet address
    const address = await IncentivResolver.getAccountAddress(
      Config.Environments.Mainnet.Portal
    );

    // Validate the address
    if (!address || address === "null" || address === null) {
      throw new Error(
        "No wallet address received. The popup may have been closed or the connection was rejected."
      );
    }

    console.log("Connected address:", address);
    setAddress(address);

    // Initialize contract connection (see next section)
    await fetchContractData(address);
  } catch (err) {
    console.error("Connection error:", err);
    // Possible causes:
    // - Popup blocked by the browser
    // - User closed the popup
    // - User rejected the connection
  }
};
```

### Complete Wallet Connection Example

```javascript theme={null}
import { useState } from "react";
import { IncentivResolver } from "@incentiv/dapp-sdk";
import Config from "./config";

function WalletConnection() {
  const [address, setAddress] = useState("");
  const [isConnecting, setIsConnecting] = useState(false);
  const [error, setError] = useState("");

  const handleConnect = async () => {
    setIsConnecting(true);
    setError("");

    try {
      // Connect to wallet
      const userAddress = await IncentivResolver.getAccountAddress(
        Config.Environments.Mainnet.Portal
      );

      // Validate address
      if (!userAddress || userAddress === "null" || userAddress === null) {
        throw new Error(
          "No wallet address received. The popup may have been closed or the connection was rejected."
        );
      }

      setAddress(userAddress);
      console.log("Wallet connected:", userAddress);
    } catch (err) {
      console.error("Connection error:", err);
      setError(
        err.message ||
          "Failed to connect. Please allow popups and try again."
      );
    } finally {
      setIsConnecting(false);
    }
  };

  return (
    <div>
      {!address ? (
        <button onClick={handleConnect} disabled={isConnecting}>
          {isConnecting ? "Connecting..." : "Connect Wallet"}
        </button>
      ) : (
        <div>
          <p>Connected: {address}</p>
        </div>
      )}
      {error && <p style={{ color: "red" }}>{error}</p>}
    </div>
  );
}
```

***

## Initializing the Signer

After connecting to the wallet, you need to initialize the `IncentivSigner` to interact with smart contracts. The signer uses Account Abstraction (AA) for gasless transactions.

### Step 1: Create Provider

```javascript theme={null}
import { parseEther, formatEther } from "ethers";

const provider = new ethers.JsonRpcProvider(
  Config.Environments.Mainnet.RPC
);
```

### Step 2: Initialize IncentivSigner

```javascript theme={null}
import { IncentivSigner } from "@incentiv/dapp-sdk";

const signer = new IncentivSigner({
  address: userAddress, // From wallet connection
  provider: provider,
  environment: Config.Environments.Mainnet.Portal,
  entryPoint: "0x3eC61c5633BBD7Afa9144C6610930489736a72d4", // EntryPoint contract address
});
```

### Step 3: Prepare Contract Interface

```javascript theme={null}
import { Interface } from "ethers";
import ABI from "./YourContractABI.json";

const contractAddress = Config.SC.address;
const contractInterface = new Interface(ABI);
```

### Complete Signer Initialization Example

```javascript theme={null}
import { useRef, useState } from "react";
import { IncentivResolver, IncentivSigner } from "@incentiv/dapp-sdk";
import { ethers, Interface } from "ethers";
import ABI from "./YourContractABI.json";
import Config from "./config";

function App() {
  const [address, setAddress] = useState("");
  const [isReady, setIsReady] = useState(false);
  const signerRef = useRef(null);
  const contractInterfaceRef = useRef(null);

  async function fetchContractData(userAddress) {
    try {
      const provider = new ethers.JsonRpcProvider(
        Config.Environments.Mainnet.RPC
      );

      const signer = new IncentivSigner({
        address: userAddress,
        provider,
        environment: Config.Environments.Mainnet.Portal,
        entryPoint: "0x3eC61c5633BBD7Afa9144C6610930489736a72d4",
      });

      signerRef.current = signer;
      contractInterfaceRef.current = new Interface(ABI);

      setIsReady(true);
      console.log("Signer and contract interface initialized");
    } catch (err) {
      console.error("Failed to initialize signer:", err);
      signerRef.current = null;
      contractInterfaceRef.current = null;
      setIsReady(false);
    }
  }

  const handleConnect = async () => {
    try {
      const userAddress = await IncentivResolver.getAccountAddress(
        Config.Environments.Mainnet.Portal
      );

      setAddress(userAddress);
      await fetchContractData(userAddress);
    } catch (err) {
      console.error("Connection error:", err);
    }
  };

  return (
    <div>
      {!address ? (
        <button onClick={handleConnect}>Connect Wallet</button>
      ) : (
        <div>
          <p>Address: {address}</p>
          <p>Signer Status: {isReady ? "Ready" : "Not initialized"}</p>
        </div>
      )}
    </div>
  );
}
```

## Making Payments & Signing Transactions

### Depositing Funds (Native Token)

To deposit native tokens (CENT) to a smart contract:

```javascript theme={null}
async function depositFunds(amountInCENT) {
  if (!signer || !contractInterface) {
    throw new Error("Signer not initialized");
  }

  try {
    // Convert CENT to wei (1 CENT = 10^18 wei)
    const value = parseEther(amountInCENT.toString());

    // Call the deposit function with native value
    const data = contractInterface.encodeFunctionData("deposit", []);

    const tx = await signer.sendTransaction({
      to: Config.SC.address,
      data,
      value,
    });

    // Wait for transaction confirmation
    await tx.wait();

    console.log("Deposit successful:", tx.hash);
    return tx;
  } catch (err) {
    console.error("Deposit failed:", err);
    throw err;
  }
}
```

### Withdrawing Funds

To withdraw funds from a smart contract:

```javascript theme={null}
async function withdrawFunds(amountInCENT) {
  if (!signer || !contractInterface) {
    throw new Error("Signer not initialized");
  }

  try {
    // Convert CENT to wei
    const withdrawalAmountWei = parseEther(amountInCENT.toString());

    // Call the withdraw function
    const data = contractInterface.encodeFunctionData("withdraw", [
    withdrawalAmountWei,
  ]);
  
  const tx = await signer.sendTransaction({
    to: Config.SC.address,
    data,
    value: 0n,
  });

    // Show pending transaction notification
    console.log("Transaction pending:", tx.hash);

    // Wait for confirmation
    await tx.wait();

    console.log("Withdrawal successful:", tx.hash);
    return tx;
  } catch (err) {
    console.error("Withdrawal failed:", err);
    throw err;
  }
}
```

### Calling Contract Functions

For any contract function that requires signing:

```javascript theme={null}
async function callContractFunction(functionName, ...args) {
  if (!signer || !contractInterface) {
    throw new Error("Signer not initialized");
  }

  try {
    // Call the contract function
    const data = contractInterface.encodeFunctionData(functionName, args);

    const tx = await signer.sendTransaction({
      to: Config.SC.address,
      data,
      value: 0n,
    });

    // Wait for transaction
    await tx.wait();

    console.log("Transaction successful:", tx.hash);
    return tx;
  } catch (err) {
    console.error("Transaction failed:", err);
    throw err;
  }
}
```

### Reading Contract State (View Functions)

For read-only operations, you don't need to sign:

```javascript theme={null}
async function readContractData(functionName, ...args) {
  if (!signer || !contractInterface) {
    throw new Error("Signer not initialized");
  }

  try {
    // Call view function (no transaction needed)
    const result = await contract[functionName](...args);
    return result;
  } catch (err) {
    console.error("Read failed:", err);
    throw err;
  }
}
```

### Complete Payment Example

```javascript theme={null}
import { useState } from "react";
import { IncentivResolver, IncentivSigner } from "@incentiv/dapp-sdk";
import { parseEther, formatEther } from "ethers";
import ABI from "./YourContractABI.json";
import Config from "./config";

function PaymentExample() {
  const [address, setAddress] = useState("");
  const [contract, setContract] = useState(null);
  const [balance, setBalance] = useState(0);
  const [isProcessing, setIsProcessing] = useState(false);

  // Initialize contract (same as previous example)
  async function initializeContract(userAddress) {
    const provider = new ethers.JsonRpcProvider(
      Config.Environments.Mainnet.RPC
    );

    const signer = new IncentivSigner({
      address: userAddress,
      provider: provider,
      environment: Config.Environments.Mainnet.Portal,
      entryPoint: "0x3eC61c5633BBD7Afa9144C6610930489736a72d4",
    });

    const contractInstance = new ethers.Contract(
      Config.GamePot.address,
      ABI,
      signer
    );

    setContract(contractInstance);
  }

  // Deposit funds
  const handleDeposit = async (amount) => {
    if (!contract) return;

    setIsProcessing(true);
    try {
      const value = parseEtheramount.toString());
      const data = contractInterface.encodeFunctionData("deposit", []);
      const tx = await signer.sendTransaction({
        to: Config.SC.address,
        data,
        value,
      });
      await tx.wait();
      console.log("Deposit successful!");
      await updateBalance();
    } catch (err) {
      console.error("Deposit failed:", err);
    } finally {
      setIsProcessing(false);
    }
  };

  // Withdraw funds
  const handleWithdraw = async (amount) => {
    if (!contract) return;

    setIsProcessing(true);
    try {
      const amountWei = parseEtheramount.toString());
      const tx = await contract.withdraw(amountWei);
      await tx.wait();
      console.log("Withdrawal successful!");
      await updateBalance();
    } catch (err) {
      console.error("Withdrawal failed:", err);
    } finally {
      setIsProcessing(false);
    }
  };

  // Read balance
  const updateBalance = async () => {
    if (!contract || !address) return;

    try {
      const balanceWei = await contract.deposits(address);
      const balanceCENT = formatEther(balanceWei);
      setBalance(parseFloat(balanceCENT));
    } catch (err) {
      console.error("Failed to read balance:", err);
    }
  };

  return (
    <div>
      <button onClick={() => handleConnect()}>Connect Wallet</button>
      {contract && (
        <div>
          <p>Balance: {balance} CENT</p>
          <button onClick={() => handleDeposit(10)} disabled={isProcessing}>
            Deposit 10 CENT
          </button>
          <button onClick={() => handleWithdraw(5)} disabled={isProcessing}>
            Withdraw 5 CENT
          </button>
        </div>
      )}
    </div>
  );
}
```

***

## Complete Code Examples

### Full Integration Example

Here's a complete React component demonstrating the full integration:

```javascript theme={null}
import { useEffect, useState, useRef } from "react";
import { IncentivResolver, IncentivSigner } from "@incentiv/dapp-sdk";
import { parseEther, formatEther } from "ethers";
import ABI from "./YourContractABI.json";
import Config from "./config";

function FullIntegrationExample() {
  const [address, setAddress] = useState("");
  const [contract, setContract] = useState(null);
  const [balance, setBalance] = useState(0);
  const [isConnecting, setIsConnecting] = useState(false);
  const [isProcessing, setIsProcessing] = useState(false);
  const signerRef = useRef(null);

  // Initialize contract and signer
  async function fetchContractData(userAddress) {
    try {
      const provider = new ethers.JsonRpcProvider(
        Config.Environments.Mainnet.RPC
      );

      const signer = new IncentivSigner({
        address: userAddress,
        provider: provider,
        environment: Config.Environments.Mainnet.Portal,
        entryPoint: "0x3eC61c5633BBD7Afa9144C6610930489736a72d4",
      });

      signerRef.current = signer;

      const contractInstance = new ethers.Contract(
        Config.GamePot.address,
        ABI,
        signer
      );

      setContract(contractInstance);
      await updateBalance(userAddress, contractInstance);
    } catch (err) {
      console.error("Failed to initialize contract:", err);
      setContract(null);
      signerRef.current = null;
    }
  }

  // Connect wallet
  const handleConnect = async () => {
    setIsConnecting(true);
    try {
      const userAddress = await IncentivResolver.getAccountAddress(
        Config.Environments.Mainnet.Portal
      );

      if (!userAddress || userAddress === "null" || userAddress === null) {
        throw new Error(
          "No wallet address received. The popup may have been closed or the connection was rejected."
        );
      }

      setAddress(userAddress);
      await fetchContractData(userAddress);
    } catch (err) {
      console.error("Connection error:", err);
      alert(`Failed to connect: ${err.message}`);
    } finally {
      setIsConnecting(false);
    }
  };

  // Update balance
  async function updateBalance(userAddr, contractInstance) {
    if (!contractInstance || !userAddr) return;

    try {
      const balanceWei = await contractInstance.deposits(userAddr);
      const balanceCENT = formatEther(balanceWei);
      setBalance(parseFloat(balanceCENT));
    } catch (err) {
      console.error("Failed to read balance:", err);
    }
  }

  // Deposit
  const handleDeposit = async (amount) => {
    if (!contract) return;

    setIsProcessing(true);
    try {
      const value = parseEtheramount.toString());
      const data = contractInterface.encodeFunctionData("deposit", []);
      const tx = await signer.sendTransaction({
        to: Config.SC.address,
        data,
        value,
      });
      console.log("Transaction sent:", tx.hash);
      await tx.wait();
      console.log("Transaction confirmed");
      await updateBalance(address, contract);
    } catch (err) {
      console.error("Deposit failed:", err);
      alert(`Deposit failed: ${err.message}`);
    } finally {
      setIsProcessing(false);
    }
  };

  // Withdraw
  const handleWithdraw = async (amount) => {
    if (!contract) return;

    setIsProcessing(true);
    try {
      const amountWei = parseEtheramount.toString());
      const tx = await contract.withdraw(amountWei);
      console.log("Transaction sent:", tx.hash);
      await tx.wait();
      console.log("Transaction confirmed");
      await updateBalance(address, contract);
    } catch (err) {
      console.error("Withdrawal failed:", err);
      alert(`Withdrawal failed: ${err.message}`);
    } finally {
      setIsProcessing(false);
    }
  };

  return (
    <div style={{ padding: "20px" }}>
      <h1>Incentiv SDK Integration</h1>

      {!address ? (
        <button onClick={handleConnect} disabled={isConnecting}>
          {isConnecting ? "Connecting..." : "Connect Wallet"}
        </button>
      ) : (
        <div>
          <p><strong>Connected:</strong> {address}</p>
          <p><strong>Balance:</strong> {balance.toFixed(4)} CENT</p>
          <p><strong>Contract Status:</strong> {contract ? "Ready" : "Not initialized"}</p>

          <div style={{ marginTop: "20px" }}>
            <button
              onClick={() => handleDeposit(10)}
              disabled={isProcessing}
              style={{ marginRight: "10px" }}
            >
              Deposit 10 CENT
            </button>
            <button
              onClick={() => handleWithdraw(5)}
              disabled={isProcessing}
            >
              Withdraw 5 CENT
            </button>
          </div>
        </div>
      )}
    </div>
  );
}

export default FullIntegrationExample;
```

***

## Configuration

### Network Configuration

Update your configuration based on the network:

**Mainnet:**

```javascript theme={null}
const MainnetConfig = {
  Portal: "https://portal.incentiv.io/",
  RPC: "https://rpc.incentiv.io",
  EntryPoint: "0x3eC61c5633BBD7Afa9144C6610930489736a72d4",
  VerifierContract: "0xd44EbfDf4FFf3e367b7e07e47eA0e70F5277Bca0"
};
```

### Contract Addresses

Always use checksummed addresses:

```javascript theme={null}
const CONTRACT_ADDRESS = "0xC3A195E.....";
```

## Best Practices

### 1. Error Handling

Always wrap SDK calls in try-catch blocks:

```javascript theme={null}
try {
  const address = await IncentivResolver.getAccountAddress(portal);
} catch (err) {
  // Handle specific error types
  if (err.message.includes("extension not found")) {
    // Show install wallet message
  } else if (err.message.includes("user rejected")) {
    // User cancelled the connection
  } else {
    // Generic error handling
  }
}
```

### 2. Loading States

Provide user feedback during async operations:

```javascript theme={null}
const [isConnecting, setIsConnecting] = useState(false);
const [isProcessing, setIsProcessing] = useState(false);

// Use these states to disable buttons and show loading indicators
```

### 3. Transaction Confirmation

Always wait for transaction confirmation:

```javascript theme={null}
const data = contractInterface.encodeFunctionData("deposit", []);
const tx = await signer.sendTransaction({
  to: Config.SC.address,
  data,
  value,
});
await tx.wait(); // Wait for confirmation
```

### 4. Balance Updates

Update balances after transactions:

```javascript theme={null}
await tx.wait();
await updateBalance(); // Refresh balance
```

### 5. Address Validation

Validate addresses before using them:

```javascript theme={null}
if (!address || address === "null" || address === null) {
  throw new Error("Invalid address");
}
```

### 6. Contract Initialization Check

Always check if contract is initialized before use:

```javascript theme={null}
  if (!signer || !contractInterface) {
    throw new Error("Signer not initialized");
  }
```

### 7. Use Refs for Signers

Store signer in a ref to avoid re-initialization:

```javascript theme={null}
const signerRef = useRef(null);

// Initialize once
signerRef.current = new IncentivSigner({...});

// Use later
const contractInterfaceRef = useRef(null);

signerRef.current = new IncentivSigner({...});
contractInterfaceRef.current = new Interface(ABI);
```

## Troubleshooting

### Issue: Portal popup is blocked

**Solution:**

* Allow popups for your application.
* Trigger `getAccountAddress()` from a user action (such as clicking a button).
* Try the connection again.

### Issue: "No wallet address received"

**Solution:**

* Verify the Portal URL is correct.
* Ensure the popup was not blocked.
* Ensure the user completed the connection flow in the Portal.
* If the popup was closed or the user rejected the request, ask them to try again.

### Issue: Transaction fails with "insufficient funds"

**Solution:**

* Check the user's wallet balance
* Ensure enough funds for gas fees
* Verify the amount being sent is correct

### Issue: Contract calls fail

**Solution:**

* Verify the contract address is correct
* Check the ABI matches the contract
* Ensure the signer is properly initialized
* Verify the function name and parameters are correct

### Issue: "Provider not found"

**Solution:**

* Check the RPC URL is correct
* Verify network connectivity
* Test the RPC endpoint directly

### Issue: Buffer is not defined

**Solution:**

* Ensure `buffer` is installed: `npm install buffer`
* Configure Vite as shown in the Project Setup section
* Add buffer to optimizeDeps

***

## API Reference

### IncentivResolver

#### `getAccountAddress(portal: string): Promise<string>`

Opens the Incentiv Portal in a popup and returns the user's wallet address after the connection flow is completed.

**Parameters:**

* `portal`: The Portal URL (e.g., "[https://portal.incentiv.io](https://testnet.incentiv.io)")

**Returns:**

* `Promise<string>`: The user's wallet address

**Example:**

```javascript theme={null}
const address = await IncentivResolver.getAccountAddress(
  "https://portal.incentiv.io"
);
```

### IncentivSigner

#### Constructor

```javascript theme={null}
new IncentivSigner({
  address: string,
  provider: ethers.Provider,
  environment: string,
  entryPoint: string
})
```

**Parameters:**

* `address`: User's wallet address
* `provider`: Ethers.js provider instance
* `environment`: Portal URL
* `entryPoint`: EntryPoint contract address

**Example:**

```javascript theme={null}
const signer = new IncentivSigner({
  address: "0x...",
  provider: provider,
  environment: "https://portal.incentiv.io",
  entryPoint: "0x3eC61c5633BBD7Afa9144C6610930489736a72d4"
});
```

***

## Additional Resources

* **Incentiv Network**: [https://incentiv.io](https://incentiv.io)
* **Mainnet Portal**: [https://portal.incentiv.io](https://portal.incentiv.io)
* **Documentation**: Check official Incentiv documentation
* **Support**: Contact Incentiv support for SDK-specific issues

## License

This guide is provided as-is for educational purposes. Always refer to the official Incentiv SDK documentation for the most up-to-date information.

## Example

Follow this [example](/tools/dapps/example)

***
