# 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>
  )
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://wiki.gen6.life/developer-resources/sdk-and-tooling/g6-mw-and-chain-api-guide/06.-finance.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
