Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
92fc25c
chore(patch balances): add patch balances work
atticusofsparta Oct 7, 2025
0d0f527
fix(patch): patch balances at end of handler wrapper
atticusofsparta Oct 7, 2025
19d4361
fix(git): ignore mac pointer files
atticusofsparta Oct 7, 2025
43a7a28
fix(patch): simplify balance patching
atticusofsparta Oct 7, 2025
271d965
fix(notes): remove handler balance modification notes
atticusofsparta Oct 7, 2025
4d3c53a
fix(patch): add patch balances hydration handler
atticusofsparta Oct 7, 2025
ceb7a21
fix(events): dont add affected address to the event
atticusofsparta Oct 7, 2025
4d915e0
fix(types): update luadoc types
atticusofsparta Oct 7, 2025
6af1b0d
fix(balances): use a save size checking method
atticusofsparta Oct 7, 2025
8aea00b
fix(patch): add patch file and move patchBalances to its own file to …
atticusofsparta Oct 7, 2025
3fd06b6
fix(balances): revert change to walletHasSufficientQUantity
atticusofsparta Oct 7, 2025
1ff76a3
fix(patching): remove token supply from patch message and fix tests
atticusofsparta Oct 7, 2025
4d2c7b1
fix(tests): add live memory test
atticusofsparta Oct 8, 2025
69350a2
fix(tests): auth issue
atticusofsparta Oct 8, 2025
6b11edd
fix(tests): add configuration for live process testing
atticusofsparta Oct 8, 2025
26b4e44
fix(comments): remove extraneous type
atticusofsparta Oct 8, 2025
68b0fc7
fix(tests): update tests
atticusofsparta Oct 8, 2025
e098651
fix(tests): update integration tests with new patch messages that off…
atticusofsparta Oct 8, 2025
271b6cb
fix(hb): update patchBalances to directly use Balances
atticusofsparta Oct 8, 2025
660739c
fix(test): cleanup unused variables
atticusofsparta Oct 8, 2025
f744578
fix(hb patch): fix pattern function on PatchHyperbeamBalances handler
atticusofsparta Oct 8, 2025
2bcb16c
fix(tests): add tests for patch hyperbeam balances
atticusofsparta Oct 8, 2025
122ffa8
fix(patch file): update hb patch device patch file
atticusofsparta Oct 8, 2025
16e05a5
fix(tests): adjust error handling in patch test
atticusofsparta Oct 8, 2025
377dd8b
fix(tests): add comment on different module id for live memory testing
atticusofsparta Oct 8, 2025
2cd0b34
fix(patch): update patch file to hydrate balances on hb at end of eval
atticusofsparta Oct 8, 2025
faa95de
fix(patch): stringify balances on egress to hb patch device
atticusofsparta Oct 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ coverage

mainnet/**/outputs/*
mainnet/outputs/*
._*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this one all about?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

macos pointer file generation when working on an external harddrive

35 changes: 35 additions & 0 deletions src/balances.lua
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,39 @@ function balances.walletHasSufficientBalance(wallet, quantity)
return Balances[wallet] ~= nil and Balances[wallet] >= quantity
end

---@param oldBalances table<string, number> A table of addresses and their balances
---@param newBalances table<string, number> A table of addresses and their balances
---@return table<string, boolean> affectedBalancesAddresses table of addresses that have had balance changes
function balances.patchBalances(oldBalances, newBalances)
assert(type(oldBalances) == "table", "Old balances must be a table")
assert(type(newBalances) == "table", "New balances must be a table")
local affectedBalancesAddresses = {}
for address, _ in pairs(oldBalances) do
if Balances[address] ~= oldBalances[address] then
affectedBalancesAddresses[address] = true
end
end
for address, _ in pairs(newBalances) do
if oldBalances[address] ~= newBalances[address] then
affectedBalancesAddresses[address] = true
end
end

--- For simplicity we always include the protocol balance in the patch message
--- this also prevents us from sending an empty patch message and deleting the entire hyperbeam balances table
local patchMessage = { device = "[email protected]", balances = { [ao.id] = Balances[ao.id] or 0 } }
for address, _ in pairs(affectedBalancesAddresses) do
patchMessage.balances[address] = Balances[address] or 0
end

-- only send the patch message if there are affected balances, otherwise we'll end up deleting the entire hyperbeam balances table
if next(patchMessage.balances) == nil then
return {}
else
ao.send(patchMessage)
end

return affectedBalancesAddresses
end

return balances
36 changes: 26 additions & 10 deletions src/main.lua
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ local ActionMap = {
ApprovePrimaryNameRequest = "Approve-Primary-Name-Request",
PrimaryNames = "Primary-Names",
PrimaryName = "Primary-Name",
-- Hyperbeam Patch Balances
PatchHyperbeamBalances = "Patch-Hyperbeam-Balances",
}

--- @param msg ParsedMessage
Expand Down Expand Up @@ -471,6 +473,8 @@ local function addEventingHandler(handlerName, pattern, handleFn, critical, prin
critical = critical or false
printEvent = printEvent == nil and true or printEvent
Handlers.add(handlerName, pattern, function(msg)
-- Store the old balances to compare after the handler has run for patching state
local oldBalances = utils.deepCopy(Balances)
-- add an ARIOEvent to the message if it doesn't exist
msg.ioEvent = msg.ioEvent or ARIOEvent(msg)
-- global handler for all eventing errors, so we can log them and send a notice to the sender for non critical errors and discard the memory on critical errors
Expand All @@ -494,6 +498,9 @@ local function addEventingHandler(handlerName, pattern, handleFn, critical, prin
error(errorWithEvent, 0) -- 0 ensures not to include this line number in the error message
end

-- Send patch message to HB
balances.patchBalances(oldBalances, Balances)

msg.ioEvent:addField("Handler-Memory-KiB-Used", collectgarbage("count"), false)
collectgarbage("collect")
msg.ioEvent:addField("Final-Memory-KiB-Used", collectgarbage("count"), false)
Expand Down Expand Up @@ -2137,17 +2144,12 @@ end)
-- Pagination handlers

addEventingHandler("paginatedRecords", function(msg)
return msg.Action == "Paginated-Records" or msg.Action == ActionMap.Records
return msg.Action == "Paginated-Records" or msg.Action == ActionMap.Records
end, function(msg)
local page = utils.parsePaginationTags(msg)
local result = arns.getPaginatedRecords(
page.cursor,
page.limit,
page.sortBy or "startTimestamp",
page.sortOrder,
page.filters
)
Send(msg, { Target = msg.From, Action = "Records-Notice", Data = json.encode(result) })
local page = utils.parsePaginationTags(msg)
local result =
arns.getPaginatedRecords(page.cursor, page.limit, page.sortBy or "startTimestamp", page.sortOrder, page.filters)
Send(msg, { Target = msg.From, Action = "Records-Notice", Data = json.encode(result) })
end)

addEventingHandler("paginatedGateways", function(msg)
Expand Down Expand Up @@ -2691,4 +2693,18 @@ addEventingHandler("allPaginatedGatewayVaults", utils.hasMatchingTag("Action", "
Send(msg, { Target = msg.From, Action = "All-Gateway-Vaults-Notice", Data = json.encode(result) })
end)

addEventingHandler(
ActionMap.PatchHyperbeamBalances,
utils.hasMatchingTag("Action", ActionMap.PatchHyperbeamBalances),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrap this handler with Handlers.continue

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried Handlers.utils.continue but it didnt perform as expected, so i just did a manual pattern function. Our tests cover it well.

function(msg)
assert(msg.From == Owner, "Only the owner can trigger " .. ActionMap.PatchHyperbeamBalances)
local patchMessage = { device = "[email protected]", balances = utils.deepCopy(Balances) }
ao.send(patchMessage)
return Send(msg, {
Target = msg.From,
Action = ActionMap.PatchHyperbeamBalances .. "-Notice",
})
end
)
Copy link

@coderabbitai coderabbitai bot Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Verify: Should the manual patch include supply information?

The automatic patch in hb.patchBalances includes info.supply (line 30 in src/hb.lua), but this manual owner-triggered patch on line 2702 does not. Confirm whether the manual patch should also include the total supply information for consistency with the automatic patching mechanism.

Run the following script to check if there are any other patch message constructions in the codebase:


🏁 Script executed:

#!/bin/bash
# Search for other patch message constructions to verify consistency
rg -n "device.*patch" --type lua -C 2

Length of output: 2241


Include total supply in manual patch
The owner-triggered patch in src/main.lua omits the info.supply field that hb.patchBalances sets via token.calculateTotalSupply(). Add info = { supply = token.calculateTotalSupply() } to the manual patchMessage for consistency.

🤖 Prompt for AI Agents
In src/main.lua around lines 2697 to 2709, the owner-triggered manual
patchMessage omits the info.supply field that hb.patchBalances would set; update
the construction of patchMessage to include an info table with supply =
token.calculateTotalSupply() so the message mirrors hb.patchBalances (e.g., set
patchMessage = { device = "[email protected]", balances = utils.deepCopy(Balances), info
= { supply = token.calculateTotalSupply() } }). Ensure
token.calculateTotalSupply() is available in scope and keep deepCopy(Balances)
as-is.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is deliberate, we set it once in the patch message to initialize hb state

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@atticusofsparta, thank you for clarifying! I understand now—the manual Patch-Hyperbeam-Balances handler is intended for one-time initialization of the Hyperbeam state, while the automatic patches (via hb.patchBalances()) handle ongoing state synchronization and include the supply information. That distinction makes sense.


✏️ Learnings added
Learnt from: atticusofsparta
PR: ar-io/ar-io-network-process#435
File: src/main.lua:0-0
Timestamp: 2025-10-08T16:45:02.913Z
Learning: In `src/main.lua`, the owner-triggered manual `Patch-Hyperbeam-Balances` handler deliberately omits the `info.supply` field that `hb.patchBalances()` includes. The manual patch is for one-time initialization of Hyperbeam state, while automatic patches handle ongoing state synchronization with supply information.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: atticusofsparta
PR: ar-io/ar-io-network-process#435
File: src/hb.lua:0-0
Timestamp: 2025-10-08T16:44:07.775Z
Learning: In `src/hb.lua`, the `hb.patchBalances()` function intentionally uses the global `Balances` variable directly and only accepts `oldBalances` as a parameter, rather than accepting both old and new balances as parameters.


return main
Loading