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