---
# System prepended metadata

title: FLP Token HyperBeam Patches

---

# FLP Token HyperBeam Patches

In order to be able for frontends to get the token states regarding Whitelist Module and Batch Transfer functionality, token processes are required to PATCH details about those.

# Step 1: Configure Your State Message
This is required for all tokens to enable balances and details resolution from HyperBeam nodes.

To get started, you must login to your AOS process. Once the connection is established, we must create new submessages in our process state:

1. Save the snippet into a file called `patch_state.lua`:
```lua
Send({ 
    device="patch@1.0", 
    balances = { device = "trie@1.0" }, 
    ["token-info"] = { 
        name = Name,
        ticker = Ticker,
        logo = Logo,
        denomination = tostring(Denomination),
        supply = TotalSupply
    } 
})
```
2. Connect via AOS to your FLP token process:
`aos --wallet OWNER_WALLET PROCESS_ID`
3. Load the patch snippet into the process (inside the AOS console): 
`.load ./patch_state.lua`


You may see a `**WARN: No target specified for message...` notification in response. This warning is harmless and can be safely ignored.

# Step 2: Configure Patch for token transfer

1. Save the snippet into a file called `patch_transfers.lua`:
```lua
local handlerExists = function(handlerName)
    if not Handlers or not Handlers.list then return false end
    for _, handler in ipairs(Handlers.list) do
        if handler.name == handlerName then return true end
    end
    return false
end

-- 1. ensure we don't duplicate
while handlerExists('Patch-Transfers') do
    Handlers.remove('Patch-Transfers')
end

-- 2. add handler
Handlers.after("transfer").add("Patch-Transfers",
    function(msg)
        if msg.Tags.Action == "Transfer" then
            return "continue"
        end

        return 0
    end,
    function(msg)
        local patchMsg = {
            device = "patch@1.0",
            balances = {}
        }

        patchMsg.balances[msg.Sender] = Balances[msg.Sender]
        patchMsg.balances[msg.Recipient] = Balances[msg.Recipient]

        Send(patchMsg)
    end
)
```

2. Connect via AOS to your FLP token process:
`aos --wallet OWNER_WALLET PROCESS_ID`
3. Load the patch snippet into the process (inside the AOS console): 
`.load ./patch_transfers.lua`

# Batch Transfer
To expose Batch Transfer functionality `one-time patching` is required on the token process:

1. Save the following snippet to a file called `patch_bt.lua`:

```lua filename=patch_bt.lua
local bint = require ".bint" (256)
local token_lib = require "token.token_lib"

local utils     = {
  add = function(a, b)
    return tostring(bint(a) + bint(b))
  end,
  subtract = function(a, b)
    return tostring(bint(a) - bint(b))
  end,
  toBalanceValue = function(a)
    return tostring(bint(a))
  end,
  toNumber = function(a)
    return tonumber(a)
  end
}

-- Send Patch message for info token and discovery
local patchMsg = {
  device = 'patch@1.0',
  ['token-info'] = {
    ['batch-transfer'] = true,
  },
  balances = Balances
}

Send(patchMsg)

local handlerExists = function(handlerName)
    if not Handlers or not Handlers.list then return false end
    for _, handler in ipairs(Handlers.list) do
        if handler.name == handlerName then return true end
    end
    return false
end

-- 1. ensure we don't duplicate
while handlerExists('batchTransfer') do
    Handlers.remove('batchTransfer')
end

Handlers.add('batchTransfer',
  Handlers.utils.hasMatchingTag("Action", "Batch-Transfer"),
  function(msg)
    --[[
      Simple CSV parser that splits input by newlines and commas
      to create a 2D table of values.
    ]]
    local function parseCSV(csvText)
      local result = {}
      -- Split by newlines and process each line
      for line in csvText:gmatch("[^\r\n]+") do
        local row = {}
        -- Split line by commas and add each value to the row
        for value in line:gmatch("[^,]+") do
          table.insert(row, value)
        end
        table.insert(result, row)
      end
      return result
    end

    -- Parse CSV data and validate entries
    local rawRecords = parseCSV(msg.Data)
    assert(rawRecords and #rawRecords > 0, 'No transfer entries found in CSV')

    local transferEntries = {}
    local totalQuantity = "0"

    -- Validate each entry and calculate total transfer amount
    for i, record in ipairs(rawRecords) do
      local recipient = record[1]
      local quantity = record[2]

      assert(recipient and quantity, 'Invalid entry at line ' .. i .. ': recipient and quantity required')
      assert(string.match(quantity, "^%d+$"), 'Invalid quantity format at line ' .. i .. ': must contain only digits')
      assert(bint.ispos(bint(quantity)), 'Quantity must be greater than 0 at line ' .. i)

      table.insert(transferEntries, {
        Recipient = recipient,
        Quantity = quantity
      })

      totalQuantity = utils.add(totalQuantity, quantity)
    end

    -- Step 2: Check if sender has sufficient balance
    if not Balances[msg.From] then Balances[msg.From] = "0" end

    local balance = token_lib.calculateAvailableBalance(msg.From, msg.Timestamp)

    if not (bint(totalQuantity) <= bint(balance.Available)) then
      msg.reply({
        Action = 'Transfer-Error',
        ['Message-Id'] = msg.Id,
        Error = 'Insufficient Balance!'
      })
      return
    end

    -- Step 3: Prepare the balance updates
    local balanceUpdates = {}
    local patchMsg = {
      device = 'patch@1.0',
      balances = {}
    }

    for _, entry in ipairs(transferEntries) do
      local recipient = entry.Recipient
      local quantity = entry.Quantity

      if not Balances[recipient] then Balances[recipient] = "0" end

      -- Aggregate multiple transfers to the same recipient
      if not balanceUpdates[recipient] then
        balanceUpdates[recipient] = utils.add(Balances[recipient], quantity)
      else
        balanceUpdates[recipient] = utils.add(balanceUpdates[recipient], quantity)
      end
    end

    -- Step 4: Apply the balance changes atomically
    Balances[msg.From] = utils.subtract(Balances[msg.From], totalQuantity)
    for recipient, newBalance in pairs(balanceUpdates) do
      Balances[recipient] = newBalance
      patchMsg.balances[recipient] = Balances[recipient]
    end

    patchMsg.balances[msg.From] = Balances[msg.From]

    -- Step 5: Always send a batch debit notice to the sender
    local batchDebitNotice = {
      Action = 'Batch-Debit-Notice',
      Count = tostring(#transferEntries),
      Total = totalQuantity,
      ['Batch-Transfer-Init-Id'] = msg.Id,
    }

    -- Forward any X- tags to the debit notice
    for tagName, tagValue in pairs(msg.Tags) do
      if string.sub(tagName, 1, 2) == "X-" then
        batchDebitNotice[tagName] = tagValue
      end
    end

    msg.reply(batchDebitNotice)

    ao.send(patchMsg)

    -- Step 6: Send individual credit notices if Cast tag is not set
    if not msg.Cast then
      for _, entry in ipairs(transferEntries) do
        local creditNotice = {
          Target = entry.Recipient,
          Action = 'Credit-Notice',
          Sender = msg.From,
          Quantity = entry.Quantity,
          Data = "You received " .. entry.Quantity .. " from " .. msg.From
        }

        -- Forward any X- tags to the credit notices
        for tagName, tagValue in pairs(msg.Tags) do
          if string.sub(tagName, 1, 2) == "X-" then
            creditNotice[tagName] = tagValue
          end
        end

        ao.send(creditNotice)
      end
    end
  end
)
```

2. Connect via AOS to your FLP token process:
`aos --wallet OWNER_WALLET PROCESS_ID`
3. Load the patch snippet into the process (inside the AOS console): 
`.load ./patch_bt.lua`


# Whitelist Module
To expose Whitelist Module functionality `one-time patching` is required on the token process:

1. Save the following snippet to a file called `patch_whitelist.lua`:

```lua
-- Send Patch message for info token and discovery
local patchMsg = {
  device = 'patch@1.0',
  ['token-info'] = {
    ['whitelist-module'] = WhitelistModule and true or false
  }
}

Send(patchMsg)
```

2. Connect via AOS to your FLP token process:
`aos --wallet OWNER_WALLET PROCESS_ID`
3. Load the patch snippet into the process (inside the AOS console): 
`.load ./patch_whitelist.lua`