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

# Integration Best Practices

# Best Practices for Entity Integration

Recommended patterns, security considerations, and optimization strategies for working with Endaoment entities.

## Security Best Practices

### Always Verify Entity Status

Before any transaction, confirm the entity is active:

```typescript theme={null}
async function safeEntityInteraction(entityAddress: string): Promise<boolean> {
  const provider = new ethers.providers.JsonRpcProvider(RPC_URL);
  const registry = new ethers.Contract(REGISTRY_ADDRESS, REGISTRY_ABI, provider);
  
  // 1. Check if entity exists and is active
  const isActive = await registry.isActiveEntity(entityAddress);
  
  if (!isActive) {
    throw new Error(`Entity ${entityAddress} is not active or does not exist`);
  }
  
  // 2. Verify contract has code
  const code = await provider.getCode(entityAddress);
  if (code === '0x') {
    throw new Error('No contract at this address');
  }
  
  // 3. Optional: Verify it's the correct entity type
  const ENTITY_ABI = ['function entityType() external pure returns (uint8)'];
  const entity = new ethers.Contract(entityAddress, ENTITY_ABI, provider);
  const entityType = await entity.entityType();
  
  console.log(`✓ Entity verified: ${entityType === 0 ? 'Org' : 'Fund'}`);
  return true;
}
```

### Validate Amounts

Always validate amounts before transactions:

```typescript theme={null}
function validateAmount(amount: string, decimals: number = 6): ethers.BigNumber {
  // 1. Check it's a valid number
  if (isNaN(parseFloat(amount)) || parseFloat(amount) <= 0) {
    throw new Error('Invalid amount: must be a positive number');
  }
  
  // 2. Check precision
  const decimalPlaces = amount.split('.')[1]?.length || 0;
  if (decimalPlaces > decimals) {
    throw new Error(`Too many decimal places (max ${decimals})`);
  }
  
  // 3. Convert to Wei
  const amountWei = ethers.utils.parseUnits(amount, decimals);
  
  // 4. Sanity check (e.g., not more than $1B)
  const maxAmount = ethers.utils.parseUnits('1000000000', decimals);
  if (amountWei.gt(maxAmount)) {
    throw new Error('Amount too large');
  }
  
  return amountWei;
}

// Usage
try {
  const amount = validateAmount('1000.50');
  await entity.donate(amount);
} catch (error) {
  console.error(`Validation failed: ${error.message}`);
}
```

### Check Permissions Before Operations

```typescript theme={null}
async function verifyPermissions(
  entityAddress: string,
  walletAddress: string,
  operation: 'donate' | 'transfer' | 'manager'
): Promise<boolean> {
  const provider = new ethers.providers.JsonRpcProvider(RPC_URL);
  const entity = new ethers.Contract(entityAddress, ENTITY_ABI, provider);
  
  if (operation === 'donate') {
    // Anyone can donate
    return true;
  }
  
  if (operation === 'transfer' || operation === 'manager') {
    // Check if wallet is manager
    const manager = await entity.manager();
    if (manager.toLowerCase() === walletAddress.toLowerCase()) {
      return true;
    }
    
    console.log('✗ Not the entity manager');
    return false;
  }
  
  return false;
}
```

## Error Handling

### Comprehensive Error Handler

```typescript theme={null}
class EntityOperationError extends Error {
  constructor(
    message: string,
    public code: string,
    public details?: any
  ) {
    super(message);
    this.name = 'EntityOperationError';
  }
}

async function safeDonation(
  entityAddress: string,
  amount: string
): Promise<ethers.ContractReceipt> {
  const provider = new ethers.providers.JsonRpcProvider(RPC_URL);
  const signer = new ethers.Wallet(PRIVATE_KEY, provider);
  
  try {
    // 1. Validate inputs
    const amountWei = validateAmount(amount);
    
    // 2. Verify entity
    await safeEntityInteraction(entityAddress);
    
    // 3. Check USDC balance
    const usdc = new ethers.Contract(USDC_ADDRESS, USDC_ABI, signer);
    const balance = await usdc.balanceOf(signer.address);
    
    if (balance.lt(amountWei)) {
      throw new EntityOperationError(
        'Insufficient USDC balance',
        'INSUFFICIENT_BALANCE',
        {
          required: ethers.utils.formatUnits(amountWei, 6),
          available: ethers.utils.formatUnits(balance, 6)
        }
      );
    }
    
    // 4. Check allowance
    const allowance = await usdc.allowance(signer.address, entityAddress);
    
    if (allowance.lt(amountWei)) {
      console.log('Approving USDC...');
      const approveTx = await usdc.approve(entityAddress, amountWei);
      await approveTx.wait();
    }
    
    // 5. Execute donation
    const entity = new ethers.Contract(entityAddress, ENTITY_ABI, signer);
    const tx = await entity.donate(amountWei, {
      gasLimit: 200000
    });
    
    const receipt = await tx.wait();
    console.log(`✓ Donation successful: ${receipt.transactionHash}`);
    
    return receipt;
    
  } catch (error) {
    // Handle different error types
    if (error instanceof EntityOperationError) {
      console.error(`❌ ${error.message}`);
      console.error(`Code: ${error.code}`);
      if (error.details) {
        console.error('Details:', error.details);
      }
    } else if (error.code === 'INSUFFICIENT_FUNDS') {
      console.error('❌ Insufficient ETH for gas');
    } else if (error.code === 'UNPREDICTABLE_GAS_LIMIT') {
      console.error('❌ Transaction would fail. Check entity status and parameters.');
    } else {
      console.error('❌ Unexpected error:', error.message);
    }
    
    throw error;
  }
}
```

### Retry Logic

```typescript theme={null}
async function withRetry<T>(
  fn: () => Promise<T>,
  maxRetries: number = 3,
  delayMs: number = 1000
): Promise<T> {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (i === maxRetries - 1) {
        throw error;
      }
      
      console.log(`Attempt ${i + 1} failed, retrying in ${delayMs}ms...`);
      await new Promise(resolve => setTimeout(resolve, delayMs));
      delayMs *= 2; // Exponential backoff
    }
  }
  
  throw new Error('Should not reach here');
}

// Usage
const receipt = await withRetry(() => 
  entity.donate(ethers.utils.parseUnits('100', 6))
);
```

## Gas Optimization

### Batch Operations

Use multicall pattern for reading multiple entities:

```typescript theme={null}
import { ethers } from 'ethers';

interface EntityData {
  address: string;
  balance: string;
  manager: string;
  entityType: 'org' | 'fund';
}

async function batchReadEntities(addresses: string[]): Promise<EntityData[]> {
  const provider = new ethers.providers.JsonRpcProvider(RPC_URL);
  
  // Batch all calls
  const calls = addresses.flatMap(address => {
    const entity = new ethers.Contract(address, ENTITY_ABI, provider);
    return [
      entity.balance(),
      entity.manager(),
      entity.entityType()
    ];
  });
  
  const results = await Promise.all(calls);
  
  // Parse results
  const entities: EntityData[] = [];
  for (let i = 0; i < addresses.length; i++) {
    const offset = i * 3;
    entities.push({
      address: addresses[i],
      balance: ethers.utils.formatUnits(results[offset], 6),
      manager: results[offset + 1],
      entityType: results[offset + 2] === 0 ? 'org' : 'fund'
    });
  }
  
  return entities;
}
```

### Optimize Approvals

```typescript theme={null}
async function optimizedApproval(
  tokenAddress: string,
  spenderAddress: string,
  amount: ethers.BigNumber
): Promise<void> {
  const signer = new ethers.Wallet(PRIVATE_KEY, provider);
  const token = new ethers.Contract(tokenAddress, ERC20_ABI, signer);
  
  // Check current allowance
  const currentAllowance = await token.allowance(signer.address, spenderAddress);
  
  // Only approve if needed
  if (currentAllowance.gte(amount)) {
    console.log('✓ Sufficient allowance already exists');
    return;
  }
  
  // If there's a non-zero allowance, reset to 0 first (for some tokens like USDT)
  if (currentAllowance.gt(0)) {
    console.log('Resetting allowance to 0...');
    await (await token.approve(spenderAddress, 0)).wait();
  }
  
  // Set new allowance
  console.log('Setting new allowance...');
  await (await token.approve(spenderAddress, amount)).wait();
  console.log('✓ Approval complete');
}
```

## Integration Patterns

### Pattern 1: Donation Widget

Complete donation flow with user feedback:

```typescript theme={null}
interface DonationResult {
  success: boolean;
  txHash?: string;
  error?: string;
  amountDonated?: string;
  amountReceived?: string;
  fee?: string;
}

async function processDonation(
  entityAddress: string,
  amount: string,
  onProgress?: (step: string) => void
): Promise<DonationResult> {
  try {
    onProgress?.('Validating...');
    await safeEntityInteraction(entityAddress);
    const amountWei = validateAmount(amount);
    
    onProgress?.('Checking balance...');
    const signer = new ethers.Wallet(PRIVATE_KEY, provider);
    const usdc = new ethers.Contract(USDC_ADDRESS, USDC_ABI, signer);
    const balance = await usdc.balanceOf(signer.address);
    
    if (balance.lt(amountWei)) {
      return {
        success: false,
        error: 'Insufficient USDC balance'
      };
    }
    
    onProgress?.('Approving USDC...');
    await optimizedApproval(USDC_ADDRESS, entityAddress, amountWei);
    
    onProgress?.('Processing donation...');
    const entity = new ethers.Contract(entityAddress, ENTITY_ABI, signer);
    const tx = await entity.donate(amountWei);
    
    onProgress?.('Confirming transaction...');
    const receipt = await tx.wait();
    
    // Parse event for fee info
    const event = receipt.events?.find(e => e.event === 'EntityDonationReceived');
    
    return {
      success: true,
      txHash: receipt.transactionHash,
      amountDonated: amount,
      amountReceived: event?.args?.amountReceived 
        ? ethers.utils.formatUnits(event.args.amountReceived, 6)
        : undefined,
      fee: event?.args?.amountFee
        ? ethers.utils.formatUnits(event.args.amountFee, 6)
        : undefined
    };
    
  } catch (error) {
    return {
      success: false,
      error: error.message
    };
  }
}

// Usage
const result = await processDonation('0x...', '1000', (step) => {
  console.log(`Status: ${step}`);
});

if (result.success) {
  console.log(`✓ Donation successful!`);
  console.log(`  Transaction: ${result.txHash}`);
  console.log(`  Net to entity: ${result.amountReceived} USDC`);
  console.log(`  Fee: ${result.fee} USDC`);
} else {
  console.log(`✗ Donation failed: ${result.error}`);
}
```

### Pattern 2: Grant Workflow

Structured grant recommendation and execution:

```typescript theme={null}
enum GrantStatus {
  PENDING = 'pending',
  APPROVED = 'approved',
  EXECUTED = 'executed',
  FAILED = 'failed'
}

interface Grant {
  id: string;
  fundAddress: string;
  orgAddress: string;
  orgName: string;
  amount: string;
  reason: string;
  status: GrantStatus;
  createdAt: number;
  executedAt?: number;
  txHash?: string;
  error?: string;
}

class GrantWorkflow {
  private grants: Map<string, Grant> = new Map();
  
  async createGrant(
    fundAddress: string,
    orgAddress: string,
    orgName: string,
    amount: string,
    reason: string
  ): Promise<string> {
    const grantId = `grant-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
    
    const grant: Grant = {
      id: grantId,
      fundAddress,
      orgAddress,
      orgName,
      amount,
      reason,
      status: GrantStatus.PENDING,
      createdAt: Date.now()
    };
    
    // Validate grant
    await this.validateGrant(grant);
    
    this.grants.set(grantId, grant);
    console.log(`✓ Grant ${grantId} created`);
    
    return grantId;
  }
  
  private async validateGrant(grant: Grant): Promise<void> {
    const provider = new ethers.providers.JsonRpcProvider(RPC_URL);
    const fund = new ethers.Contract(grant.fundAddress, ENTITY_ABI, provider);
    const registry = new ethers.Contract(REGISTRY_ADDRESS, REGISTRY_ABI, provider);
    
    // Check org is active
    const isActive = await registry.isActiveEntity(grant.orgAddress);
    if (!isActive) {
      throw new Error('Recipient organization is not active');
    }
    
    // Check fund balance
    const balance = await fund.balance();
    const amountWei = ethers.utils.parseUnits(grant.amount, 6);
    
    if (balance.lt(amountWei)) {
      throw new Error('Insufficient fund balance');
    }
  }
  
  async approveGrant(grantId: string): Promise<void> {
    const grant = this.grants.get(grantId);
    if (!grant) throw new Error('Grant not found');
    
    grant.status = GrantStatus.APPROVED;
    console.log(`✓ Grant ${grantId} approved`);
  }
  
  async executeGrant(grantId: string): Promise<ethers.ContractReceipt> {
    const grant = this.grants.get(grantId);
    if (!grant) throw new Error('Grant not found');
    if (grant.status !== GrantStatus.APPROVED) {
      throw new Error('Grant must be approved before execution');
    }
    
    try {
      const signer = new ethers.Wallet(PRIVATE_KEY, provider);
      const fund = new ethers.Contract(grant.fundAddress, ENTITY_ABI, signer);
      const amountWei = ethers.utils.parseUnits(grant.amount, 6);
      
      console.log(`Executing grant ${grantId}...`);
      const tx = await fund.transferToEntity(grant.orgAddress, amountWei);
      const receipt = await tx.wait();
      
      grant.status = GrantStatus.EXECUTED;
      grant.executedAt = Date.now();
      grant.txHash = receipt.transactionHash;
      
      console.log(`✓ Grant ${grantId} executed: ${receipt.transactionHash}`);
      return receipt;
      
    } catch (error) {
      grant.status = GrantStatus.FAILED;
      grant.error = error.message;
      console.error(`✗ Grant ${grantId} failed: ${error.message}`);
      throw error;
    }
  }
  
  getGrant(grantId: string): Grant | undefined {
    return this.grants.get(grantId);
  }
  
  listGrants(status?: GrantStatus): Grant[] {
    const grants = Array.from(this.grants.values());
    return status ? grants.filter(g => g.status === status) : grants;
  }
}

// Usage
const workflow = new GrantWorkflow();

// Create grant recommendation
const grantId = await workflow.createGrant(
  '0x...', // fund
  '0x...', // org
  'Example Charity',
  '5000',
  'Annual education program support'
);

// Review and approve
await workflow.approveGrant(grantId);

// Execute
await workflow.executeGrant(grantId);
```

## Testing

### Local Testing with Hardhat

```typescript theme={null}
import { ethers } from 'hardhat';
import { expect } from 'chai';

describe('Entity Integration Tests', () => {
  let entity, usdc, signer;
  
  beforeEach(async () => {
    // Fork mainnet
    await network.provider.request({
      method: 'hardhat_reset',
      params: [{
        forking: {
          jsonRpcUrl: RPC_URL,
          blockNumber: 18000000
        }
      }]
    });
    
    [signer] = await ethers.getSigners();
    
    // Get contracts
    entity = await ethers.getContractAt('Entity', ENTITY_ADDRESS);
    usdc = await ethers.getContractAt('ERC20', USDC_ADDRESS);
    
    // Get USDC from whale
    await network.provider.request({
      method: 'hardhat_impersonateAccount',
      params: [USDC_WHALE]
    });
    
    const whale = await ethers.getSigner(USDC_WHALE);
    await usdc.connect(whale).transfer(
      signer.address,
      ethers.utils.parseUnits('10000', 6)
    );
  });
  
  it('should donate USDC successfully', async () => {
    const amount = ethers.utils.parseUnits('100', 6);
    
    await usdc.approve(entity.address, amount);
    
    const balanceBefore = await entity.balance();
    await entity.donate(amount);
    const balanceAfter = await entity.balance();
    
    expect(balanceAfter.sub(balanceBefore)).to.be.gt(0);
  });
  
  it('should handle insufficient balance', async () => {
    const amount = ethers.utils.parseUnits('100000', 6); // More than we have
    
    await usdc.approve(entity.address, amount);
    
    await expect(entity.donate(amount)).to.be.reverted;
  });
});
```

## Monitoring & Maintenance

### Health Check Script

```typescript theme={null}
async function healthCheck(entityAddress: string): Promise<{
  healthy: boolean;
  issues: string[];
}> {
  const issues: string[] = [];
  const provider = new ethers.providers.JsonRpcProvider(RPC_URL);
  
  try {
    // 1. Check entity is active
    const registry = new ethers.Contract(REGISTRY_ADDRESS, REGISTRY_ABI, provider);
    const isActive = await registry.isActiveEntity(entityAddress);
    if (!isActive) {
      issues.push('Entity is not active');
    }
    
    // 2. Check balance is reasonable
    const entity = new ethers.Contract(entityAddress, ENTITY_ABI, provider);
    const balance = await entity.balance();
    if (balance.isZero()) {
      issues.push('Entity balance is zero');
    }
    
    // 3. Check manager is set
    const manager = await entity.manager();
    if (manager === ethers.constants.AddressZero) {
      issues.push('No manager set');
    }
    
    // 4. Check contract code hasn't changed
    const code = await provider.getCode(entityAddress);
    if (code === '0x') {
      issues.push('No contract code at address');
    }
    
    console.log(`\n🏥 Health Check for ${entityAddress}:`);
    if (issues.length === 0) {
      console.log('✅ All checks passed');
      return { healthy: true, issues: [] };
    } else {
      console.log('⚠️  Issues found:');
      issues.forEach(issue => console.log(`  - ${issue}`));
      return { healthy: false, issues };
    }
    
  } catch (error) {
    issues.push(`Health check failed: ${error.message}`);
    return { healthy: false, issues };
  }
}

// Run health check
await healthCheck('0x...');
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Entity State" href="/developers/contracts/entity-state" icon="chart-simple">
    Query entity information
  </Card>

  <Card title="Donations" href="/developers/contracts/donations" icon="hand-holding-heart">
    Accept donations
  </Card>

  <Card title="Entity Events" href="/developers/contracts/entity-events" icon="bell">
    Monitor entity activity
  </Card>

  <Card title="Entity Deployment" href="/developers/contracts/entity-deployment" icon="rocket">
    Deploy new entities
  </Card>
</CardGroup>
