> For the complete documentation index, see [llms.txt](https://wiki.gen6.life/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://wiki.gen6.life/technology-core/developer-resources/sdk-and-tooling/gen6-mw-and-chain-api-guide/06.-finance.md).

# 06. Finance

## Token Transfers

The Finance package provides simple token transfer functionality between Gen6 accounts.

### Overview

**Purpose**: Transfer GSX between accounts.

**Location**: src/packages/finance/

**Note**: Finance uses only blockchain extrinsics.

***

### Blockchain Extrinsic

#### Transfer Tokens (Keep Alive)

Transfer tokens while ensuring the sender's account remains above the existential deposit.

**Extrinsic**: `api.tx.balances.transferKeepAlive()`

**Parameters**:

* `recipientAddress`: SS58-355 encoded Gen6 address
* `amount`: BN (BigNumber) - Amount in smallest unit

**Important**: `transferKeepAlive` ensures the sender account doesn't get reaped (deleted) by maintaining the minimum balance required to keep an account alive.

**Implementation**:

```typescript
import { BN } from 'bn.js'
import { useGen6 } from '@/contexts/gen6-context'
import { useSigner } from '@/hooks/useSigner'

export function useTransferMoney() {
  const { api, currentAccount } = useGen6()
  const { getSigner } = useSigner()

  const transferMoney = async (
    senderAddress: string,
    recipientAddress: string,
    amount: string
  ): Promise<void> => {
    // Convert to BigInt with chain decimals
    const decimals = api.registry.chainDecimals[0] || 12
    const amountBN = BigInt(
      Math.floor(parseFloat(amount) * Math.pow(10, decimals))
    )

    const signerResult = await getSigner()

    const extrinsic = api.tx.balances.transferKeepAlive(
      recipientAddress,
      amountBN.toString()
    )

    await new Promise<void>((resolve, reject) => {
      extrinsic
        .signAndSend(
          senderAddress,
          { signer: signerResult.signer },
          ({ status, dispatchError }) => {
            if (status.isFinalized) {
              if (dispatchError) {
                // Error handling logic
                const errorMessage = dispatchError.toString()
                reject(new Error(errorMessage))
              } else {
                resolve()
              }
            }
          }
        )
        .catch(reject)
    })
  }

  return { transferMoney }
}
```

**Usage**:

```typescript
import { useMutation } from '@tanstack/react-query'
import { toast } from 'sonner'

function TransferForm() {
  const { currentAccount } = useGen6()
  const { transferMoney } = useTransferMoney()
  const [recipient, setRecipient] = useState('')
  const [amount, setAmount] = useState('')

  const transferMutation = useMutation({
    mutationFn: ({ recipient, amount }: { recipient: string; amount: string }) =>
      transferMoney(currentAccount?.address || '', recipient, amount),
    onSuccess: (txHash) => {
      toast.success(`Transfer successful! Transaction: ${txHash}`)
      setRecipient('')
      setAmount('')
    },
    onError: (error: Error) => {
      toast.error(`Transfer failed: ${error.message}`)
    }
  })

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault()
    transferMutation.mutate({ recipient, amount })
  }

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label>Recipient Address</label>
        <Input
          type="text"
          value={recipient}
          onChange={(e) => setRecipient(e.target.value)}
          placeholder="g6x..."
          required
        />
      </div>

      <div>
        <label>Amount (GEN6)</label>
        <Input
          type="number"
          step="0.000001"
          value={amount}
          onChange={(e) => setAmount(e.target.value)}
          placeholder="0.00"
          required
        />
      </div>

      <Button type="submit" disabled={transferMutation.isPending}>
        {transferMutation.isPending ? 'Sending...' : 'Send Tokens'}
      </Button>
    </form>
  )
}
```

***

### Balance Queries

#### Get Account Balance

**Blockchain Query**: `api.query.system.account()`

**Parameters**:

* `address`: SS58-355 encoded address

**Implementation**:

```typescript
export function useBalance(address?: string) {
  const { api } = useGen6()

  const getBalance = async (addr: string) => {
    const account = await api.query.system.account(addr)
    return account.data
  }

  return useQuery({
    queryKey: ['balance', address],
    queryFn: () => getBalance(address!),
    enabled: !!address && !!api
  })
}
```

**Response**:

```typescript
interface BalanceInfo {
  free: string // Available balance
  reserved: string // Reserved/locked balance
  frozen: string // Frozen balance (e.g., staked)
  feeFrozen: string // Balance reserved for fees
}
```

**Usage**:

```typescript
function BalanceDisplay({ address }: { address: string }) {
  const { api } = useGen6()
  const { data: balance, isLoading } = useBalance(address)

  if (isLoading) return <div>Loading...</div>

  const decimals = api.registry.chainDecimals[0]
  const free = new BN(balance.free.toString())
    .div(new BN(10).pow(new BN(decimals)))

  return <div>Balance: {free.toString()} GEN6</div>
}
```

***

### Existential Deposit

The **existential deposit** is the minimum balance required to keep an account active on the blockchain.

#### Why It Matters

* Accounts below the existential deposit are **reaped** (deleted)
* All funds in a reaped account are lost
* `transferKeepAlive` prevents this by rejecting transfers that would drop the sender below the existential deposit

#### Get Existential Deposit

**Blockchain Constant**: `api.consts.balances.existentialDeposit`

**Implementation**:

```typescript
function useExistentialDeposit() {
  const { api } = useGen6()

  const existentialDeposit = api.consts.balances.existentialDeposit.toString()

  return existentialDeposit
}
```

**Usage**:

```typescript
function TransferWithValidation() {
  const { api, currentAccount } = useGen6()
  const { data: balance } = useBalance(currentAccount?.address)
  const existentialDeposit = useExistentialDeposit()

  const validateTransfer = (amount: string) => {
    const decimals = api.registry.chainDecimals[0]
    const amountBN = new BN(amount).mul(new BN(10).pow(new BN(decimals)))
    const freeBN = new BN(balance?.free.toString() || '0')
    const existentialBN = new BN(existentialDeposit)

    // Check if transfer would drop balance below existential deposit
    const remainingBalance = freeBN.sub(amountBN)

    if (remainingBalance.lt(existentialBN)) {
      throw new Error(
        `Transfer would drop balance below existential deposit (${existentialBN.toString()})`
      )
    }
  }

  return { validateTransfer }
}
```

***

### Transaction Status

Track the status of a transfer transaction.

**Status Types**:

* `Pending`: Transaction submitted, waiting for inclusion
* `InBlock`: Transaction included in a block (not final)
* `Finalized`: Transaction finalized (irreversible)
* `Failed`: Transaction failed with error

**Implementation**:

```typescript
function TransferWithStatus() {
  const { transferMoney } = useTransferMoney()
  const [status, setStatus] = useState<
    'idle' | 'pending' | 'inBlock' | 'finalized'
  >('idle')
  const [txHash, setTxHash] = useState<string>('')

  const transfer = async (recipient: string, amount: string) => {
    const { api, currentAccount } = useGen6()
    const { getSigner } = useSigner()

    const decimals = api.registry.chainDecimals[0]
    const amountBN = new BN(amount).mul(new BN(10).pow(new BN(decimals)))

    const signerResult = await getSigner()
    const extrinsic = api.tx.balances.transferKeepAlive(recipient, amountBN)

    setStatus('pending')

    extrinsic.signAndSend(
      currentAccount.address,
      { signer: signerResult.signer },
      ({ status, txHash, dispatchError }) => {
        setTxHash(txHash.toString())

        if (status.isInBlock) {
          setStatus('inBlock')
          toast.info('Transaction in block')
        }

        if (status.isFinalized) {
          if (dispatchError) {
            setStatus('idle')
            toast.error('Transaction failed')
          } else {
            setStatus('finalized')
            toast.success('Transaction finalized!')
          }
        }
      }
    )
  }

  return { transfer, status, txHash }
}
```

***

### Address Validation

Validate Gen6 addresses before attempting transfer.

**Implementation**:

```typescript
import { decodeAddress, encodeAddress } from '@polkadot/util-crypto'

export function isValidGen6Address(address: string): boolean {
  try {
    // Decode the address
    const decoded = decodeAddress(address)

    // Re-encode with Gen6 prefix (355)
    const encoded = encodeAddress(decoded, 355)

    // Check if it matches the input
    return encoded === address
  } catch {
    return false
  }
}
```

**Usage**:

```typescript
function TransferFormWithValidation() {
  const [recipient, setRecipient] = useState('')
  const [error, setError] = useState('')

  const handleRecipientChange = (value: string) => {
    setRecipient(value)

    if (value && !isValidGen6Address(value)) {
      setError('Invalid Gen6 address')
    } else {
      setError('')
    }
  }

  return (
    <div>
      <Input
        value={recipient}
        onChange={(e) => handleRecipientChange(e.target.value)}
        placeholder="g6x..."
      />
      {error && <p className="text-red-500">{error}</p>}
    </div>
  )
}
```

***

### Transaction Fees

Every blockchain transaction incurs a fee (gas).

#### Estimate Transaction Fee

**Implementation**:

```typescript
export function useEstimateFee() {
  const { api, currentAccount } = useGen6()

  const estimateFee = async (recipient: string, amount: string) => {
    const decimals = api.registry.chainDecimals[0]
    const amountBN = new BN(amount).mul(new BN(10).pow(new BN(decimals)))

    const extrinsic = api.tx.balances.transferKeepAlive(recipient, amountBN)

    const paymentInfo = await extrinsic.paymentInfo(currentAccount.address)

    return paymentInfo.partialFee.toString()
  }

  return { estimateFee }
}
```

**Usage**:

```typescript
function TransferWithFeeEstimate() {
  const { estimateFee } = useEstimateFee()
  const { api } = useGen6()
  const [fee, setFee] = useState<string>('')

  const handleAmountChange = async (amount: string, recipient: string) => {
    if (!amount || !recipient) return

    try {
      const estimatedFee = await estimateFee(recipient, amount)

      const decimals = api.registry.chainDecimals[0]
      const feeBN = new BN(estimatedFee).div(new BN(10).pow(new BN(decimals)))

      setFee(feeBN.toString())
    } catch (error) {
      console.error('Fee estimation failed:', error)
    }
  }

  return (
    <div>
      <Input onChange={(e) => handleAmountChange(e.target.value, recipient)} />
      {fee && <p>Estimated fee: {fee} GEN6</p>}
    </div>
  )
}
```

***

### Common Errors

#### "InsufficientBalance"

* **Cause**: Not enough tokens in sender account
* **Solution**: Check balance before allowing transfer

#### "InvalidRecipient"

* **Cause**: Recipient address is invalid or malformed
* **Solution**: Validate address format before submission

***

### Best Practices

1. **Validate addresses** - Always check address format before transfer
2. **Show fees** - Display estimated transaction fee
3. **Check balance** - Validate sufficient balance (amount + fee)
4. **Respect existential deposit** - Warn users about minimum balance
5. **Use BN for calculations** - Avoid JavaScript number precision issues

***

### Complete Transfer Example

```typescript
function CompleteTransferForm() {
  const { api, currentAccount } = useGen6()
  const { transferMoney } = useTransferMoney()
  const { data: balance } = useBalance(currentAccount?.address)
  const { estimateFee } = useEstimateFee()

  const [recipient, setRecipient] = useState('')
  const [amount, setAmount] = useState('')
  const [fee, setFee] = useState<string>('')

  const transferMutation = useMutation({
    mutationFn: ({ recipient, amount }: { recipient: string; amount: string }) => {
      // Validate address
      if (!isValidGen6Address(recipient)) {
        throw new Error('Invalid recipient address')
      }

      // Validate amount
      const decimals = api.registry.chainDecimals[0]
      const amountBN = new BN(amount).mul(new BN(10).pow(new BN(decimals)))
      const feeBN = new BN(fee || '0')
      const totalBN = amountBN.add(feeBN)
      const balanceBN = new BN(balance?.free.toString() || '0')

      if (totalBN.gt(balanceBN)) {
        throw new Error('Insufficient balance (including fees)')
      }

      return transferMoney(recipient, amount)
    },
    onSuccess: (txHash) => {
      toast.success(`Transfer successful! TX: ${txHash}`)
      queryClient.invalidateQueries({ queryKey: ['balance'] })
      setRecipient('')
      setAmount('')
      setFee('')
    },
    onError: (error: Error) => {
      toast.error(error.message)
    }
  })

  useEffect(() => {
    if (recipient && amount) {
      estimateFee(recipient, amount).then(setFee)
    }
  }, [recipient, amount])

  return (
    <form onSubmit={(e) => {
      e.preventDefault()
      transferMutation.mutate({ recipient, amount })
    }}>
      <Input
        placeholder="Recipient address"
        value={recipient}
        onChange={(e) => setRecipient(e.target.value)}
      />

      <Input
        type="number"
        placeholder="Amount"
        value={amount}
        onChange={(e) => setAmount(e.target.value)}
      />

      {fee && <p>Fee: ~{fee} GEN6</p>}

      <Button type="submit" disabled={transferMutation.isPending}>
        Send
      </Button>
    </form>
  )
}
```
