> For the complete documentation index, see [llms.txt](https://developers.polymath.network/developers/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developers.polymath.network/developers/tutorials-1/minting.md).

# Minting

{% embed url="<https://github.com/PolymathNetwork/minting-app>" %}

## :eyes: Overview

This tutorial application allows token owners to issue their security token to whitelisted investors. You may also burn tokens, if needed. You should be able to:

1. Import and Export CSV with investor addresses and balances.
2. Mint tokens to investors.
3. Burn tokens.

## :ledger: Import and Export CSV

The CSV that represents issuance has the following format:

```
address, balance
0x0439...8fj4 1200
0xjf73...hf73 400
```

### :point\_up: Import

```typescript
// Processes imported text into tokenholder objects, then sends blockchain transaction to
// issue tokens to uploaded tokenholders.
const importData = (data) => {
  // Split file content into lines
  data = data.split('\r\n')
    // Trim lines 
    .map(record => record.trim())
    // Remove empty lines
    .filter(record => record.length)
    // Convert string amounts to BigNumber.
    .map(record => {
      let [address, amount] = record.split(',')
      return {address, amount: new BigNumber(amount)}
    })
  asyncAction(dispatch, () => issueTokens(data), 'Issuing tokens')
}
```

### :writing\_hand: Issuance

To issue tokens, simply call the SDK's `token.issuance.issue` method.

```typescript
const issueTokens = async (records) => {
  const q = await token.issuance.issue({issuanceData: records})
  await q.run()
  await new Promise(resolve => setTimeout(resolve, 1000))
  dispatch({type: 'RELOAD_TOKENHOLDERS'})
}
```

### :point\_down: Export

You will have the data needed in state store (i.e., `records`). You simply need to concatenate that data into a CSV file.

```typescript
const exportData = () => {
  const csvContent = records.map(({address, balance}) => {
    return [address, balance].join(',')
  }).join('\r\n')
  console.log('csvContent', csvContent)
  const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8' })
  FileSaver.saveAs(blob, 'whitelist.csv')
}
```

## :fire: Burning

Burning tokens is as intuitive as specifying the `amount`, `address`, and `reason` for burning in each call.

```typescript
// Async action
const burnTokens = async (amount, from, reason = '') => {
  asyncAction(
    dispatch,
    () => controllerRedeem(amount, from, reason),
    `Burning ${amount} tokens from ${from}`
  )
}

const controllerRedeem = async (amount, from, reason) => {
  reason = web3Utils.numberToHex(reason)
  amount = new BigNumber(amount)
  while (true) {
    // Try to redeem tokens...
    try {
      const q = await token.controller.redeem({ amount, from, reason })
      await q.run()
      dispatch({type: 'RELOAD_TOKENHOLDERS'})
      return
    }
    // ...unless current user is not a controller. In that case, add them as a controller first.
    catch (error) {
      if (error.message.includes('You must be the controller')) {
        console.log(`Add ${walletAddress} as controller`)
        const addControllerQ = await token.controller.modifyController({controller: walletAddress})
        await addControllerQ.run()
      }
      else {
        dispatch({type: 'ERROR', error: error.message})
        return
      }
    }
  }
}
```
