Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(primary-names): add patch for adding demand factor data to primar… #408

Merged
merged 5 commits into from
Mar 10, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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 package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"test:unit:debug": "rm -rf coverage && mkdir -p coverage && DEBUG=true busted . && luacov",
"test:coverage": "rm -rf luacov-html && yarn test:unit && luacov --reporter html && open luacov-html/index.html",
"test:integration": "yarn build && node --test --experimental-wasm-memory64 **/*.test.mjs",
"patch:new": "node patch.mjs $1",
"monitor:down": "docker compose -f tests/monitor/docker-compose.test.yml down",
"monitor": "yarn monitor:down && node --test tests/monitor/monitor.test.mjs",
"monitor:devnet": "yarn monitor:down && ARIO_NETWORK_PROCESS_ID=GaQrvEMKBpkjofgnBi_B3IgIDmY_XYelVLB6GcRGrHc node --test tests/monitor/monitor.test.mjs",
Expand Down
26 changes: 26 additions & 0 deletions patch.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
*
* Script to create a new patch file.
*
* Usage: node patch.mjs <patch-name>
*
* Example: node patch.mjs add-demand-factor-data
*
*
*/
import fs from 'fs';

const date = new Date().toISOString().split('T')[0];

const patchName = process.argv[2];

if (!patchName) {
console.error('Patch name is required');
process.exit(1);
}

fs.mkdirSync('patches', { recursive: true });
fs.writeFileSync(
`patches/${date}-${patchName.replace(/ /g, '-').toLowerCase().trim()}.lua`,
'--[[\n\tPLACEHOLDER FOR PATCH DESCRIPTION\n\n\n\tReviewers: [PLACEHOLDER FOR REVIEWERS]\n]]--',
);
239 changes: 239 additions & 0 deletions patches/2025-03-10-create-primary-name-request-demand-factor.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
--[[
Adds demand factor data to the ioEvent for the requestPrimaryName handler.
Reviewers: Dylan, Ariel, Atticus, Jon, Phil, Derek
]]
--
local utils = require(".src.utils")
local primaryNames = require(".src.primary_names")
local arns = require(".src.arns")
local gar = require(".src.gar")
local balances = require(".src.balances")
local demand = require(".src.demand")

-- Update the primaryNames global function to return the demand factor data
primaryNames.createPrimaryNameRequest = function(name, initiator, timestamp, msgId, fundFrom)
fundFrom = fundFrom or "balance"

primaryNames.assertValidPrimaryName(name)

name = string.lower(name)
local baseName = utils.baseNameForName(name)

--- check the primary name request for the initiator does not already exist for the same name
--- this allows the caller to create a new request and pay the fee again, so long as it is for a different name
local existingRequest = primaryNames.getPrimaryNameRequest(initiator)
assert(
not existingRequest or existingRequest.name ~= name,
"Primary name request by '" .. initiator .. "' for '" .. name .. "' already exists"
)

--- check the primary name is not already owned
local primaryNameOwner = primaryNames.getAddressForPrimaryName(name)
assert(not primaryNameOwner, "Primary name is already owned")

local record = arns.getRecord(baseName)
assert(record, "ArNS record '" .. baseName .. "' does not exist")
assert(arns.recordIsActive(record, timestamp), "ArNS record '" .. baseName .. "' is not active")

local requestCost = arns.getTokenCost({
intent = "Primary-Name-Request",
name = name,
currentTimestamp = timestamp,
record = record,
})

local fundingPlan = gar.getFundingPlan(initiator, requestCost.tokenCost, fundFrom)
assert(fundingPlan and fundingPlan.shortfall == 0, "Insufficient balances")
local fundingResult = gar.applyFundingPlan(fundingPlan, msgId, timestamp)
assert(fundingResult.totalFunded == requestCost.tokenCost, "Funding plan application failed")

--- transfer the primary name cost from the initiator to the protocol balance
balances.increaseBalance(ao.id, requestCost.tokenCost)
demand.tallyNamePurchase(requestCost.tokenCost)

local request = {
name = name,
startTimestamp = timestamp,
endTimestamp = timestamp + constants.PRIMARY_NAME_REQUEST_DURATION_MS,
}

--- if the initiator is base name owner, then just set the primary name and return
local newPrimaryName
if record.processId == initiator then
newPrimaryName = primaryNames.setPrimaryNameFromRequest(initiator, request, timestamp)
else
-- otherwise store the request for asynchronous approval
PrimaryNames.requests[initiator] = request
primaryNames.scheduleNextPrimaryNamesPruning(request.endTimestamp)
end

return {
request = request,
newPrimaryName = newPrimaryName,
baseNameOwner = record.processId,
fundingPlan = fundingPlan,
fundingResult = fundingResult,
df = demand.getDemandFactorInfo(),
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

includes the df data

}
end

-- Now update main.lua to use the new function and add the demand factor data
local createPrimaryNameRequestHandlerIndex = utils.findInArray(Handlers.list, function(handler)
return handler.name == "requestPrimaryName"
end)

if not createPrimaryNameRequestHandlerIndex then
error("Failed to find requestPrimaryName handler")
end

local createPrimaryNameRequestHandler = Handlers.list[createPrimaryNameRequestHandlerIndex]

local previousHandlerCopy = string.dump(createPrimaryNameRequestHandler.handler)
local previousHandler = load(previousHandlerCopy)

if not previousHandler then
error("Failed to load previous handler")
end

local function Send(msg, response)
if msg.reply then
--- Reference: https://github.com/permaweb/aos/blob/main/blueprints/patch-legacy-reply.lua
msg.reply(response)
else
ao.send(response)
end
end

local function assertValidFundFrom(fundFrom)
if fundFrom == nil then
return
end
local validFundFrom = utils.createLookupTable({ "any", "balance", "stakes" })
assert(validFundFrom[fundFrom], "Invalid fund from type. Must be one of: any, balance, stakes")
end

local function addPrimaryNameCounts(ioEvent)
ioEvent:addField("Total-Primary-Names", utils.lengthOfTable(primaryNames.getUnsafePrimaryNames()))
ioEvent:addField("Total-Primary-Name-Requests", utils.lengthOfTable(primaryNames.getUnsafePrimaryNameRequests()))
end

local function adjustSuppliesForFundingPlan(fundingPlan, rewardForInitiator)
if not fundingPlan then
return
end
rewardForInitiator = rewardForInitiator or 0
local totalActiveStakesUsed = utils.reduce(fundingPlan.stakes, function(acc, _, stakeSpendingPlan)
return acc + stakeSpendingPlan.delegatedStake
end, 0)
local totalWithdrawStakesUsed = utils.reduce(fundingPlan.stakes, function(acc, _, stakeSpendingPlan)
return acc
+ utils.reduce(stakeSpendingPlan.vaults, function(acc2, _, vaultBalance)
return acc2 + vaultBalance
end, 0)
end, 0)
LastKnownStakedSupply = LastKnownStakedSupply - totalActiveStakesUsed
LastKnownWithdrawSupply = LastKnownWithdrawSupply - totalWithdrawStakesUsed
LastKnownCirculatingSupply = LastKnownCirculatingSupply - fundingPlan.balance + rewardForInitiator
end

local function addResultFundingPlanFields(ioEvent, result)
ioEvent:addFieldsWithPrefixIfExist(result.fundingPlan, "FP-", { "balance" })
local fundingPlanVaultsCount = 0
local fundingPlanStakesAmount = utils.reduce(
result.fundingPlan and result.fundingPlan.stakes or {},
function(acc, _, delegation)
return acc
+ delegation.delegatedStake
+ utils.reduce(delegation.vaults, function(acc2, _, vaultAmount)
fundingPlanVaultsCount = fundingPlanVaultsCount + 1
return acc2 + vaultAmount
end, 0)
end,
0
)
if fundingPlanStakesAmount > 0 then
ioEvent:addField("FP-Stakes-Amount", fundingPlanStakesAmount)
end
if fundingPlanVaultsCount > 0 then
ioEvent:addField("FP-Vaults-Count", fundingPlanVaultsCount)
end
local newWithdrawVaultsTallies = utils.reduce(
result.fundingResult and result.fundingResult.newWithdrawVaults or {},
function(acc, _, newWithdrawVault)
acc.totalBalance = acc.totalBalance
+ utils.reduce(newWithdrawVault, function(acc2, _, vault)
acc.count = acc.count + 1
return acc2 + vault.balance
end, 0)
return acc
end,
{ count = 0, totalBalance = 0 }
)
if newWithdrawVaultsTallies.count > 0 then
ioEvent:addField("New-Withdraw-Vaults-Count", newWithdrawVaultsTallies.count)
ioEvent:addField("New-Withdraw-Vaults-Total-Balance", newWithdrawVaultsTallies.totalBalance)
end
adjustSuppliesForFundingPlan(result.fundingPlan, result.returnedName and result.returnedName.rewardForInitiator)
end

--- @param ioEvent ARIOEvent
--- @param primaryNameResult CreatePrimaryNameResult|PrimaryNameRequestApproval
local function addPrimaryNameRequestData(ioEvent, primaryNameResult)
Comment on lines +97 to +181
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

unfortunately, these all need to be added as they are not available in the global scope

ioEvent:addFieldsIfExist(primaryNameResult, { "baseNameOwner" })
ioEvent:addFieldsIfExist(primaryNameResult.newPrimaryName, { "owner", "startTimestamp" })
ioEvent:addFieldsWithPrefixIfExist(primaryNameResult.request, "Request-", { "startTimestamp", "endTimestamp" })
addResultFundingPlanFields(ioEvent, primaryNameResult)
addPrimaryNameCounts(ioEvent)

-- add the demand factor data to the ioEvent
if primaryNameResult.df and type(primaryNameResult.df) == "table" then
ioEvent:addField("DF-Trailing-Period-Purchases", (primaryNameResult.df.trailingPeriodPurchases or {}))
ioEvent:addField("DF-Trailing-Period-Revenues", (primaryNameResult.df.trailingPeriodRevenues or {}))
ioEvent:addFieldsWithPrefixIfExist(primaryNameResult.df, "DF-", {
"currentPeriod",
"currentDemandFactor",
"consecutivePeriodsWithMinDemandFactor",
"revenueThisPeriod",
"purchasesThisPeriod",
})
end
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

the DF data is now included in the event

end

-- Update the handler to use the new function and add the demand factor data
createPrimaryNameRequestHandler.handler = function(msg)
local fundFrom = msg.Tags["Fund-From"]
local name = msg.Tags.Name and string.lower(msg.Tags.Name) or nil
local initiator = msg.From
assert(name, "Name is required")
assert(initiator, "Initiator is required")
assertValidFundFrom(fundFrom)

local primaryNameResult = primaryNames.createPrimaryNameRequest(name, initiator, msg.Timestamp, msg.Id, fundFrom)

addPrimaryNameRequestData(msg.ioEvent, primaryNameResult)

--- if the from is the new owner, then send an approved notice to the from
if primaryNameResult.newPrimaryName then
Send(msg, {
Target = msg.From,
Action = ActionMap.ApprovePrimaryNameRequest .. "-Notice",
Data = json.encode(primaryNameResult),
})
return
end

if primaryNameResult.request then
--- send a notice to the msg.From, and the base name owner
Send(msg, {
Target = msg.From,
Action = ActionMap.PrimaryNameRequest .. "-Notice",
Data = json.encode(primaryNameResult),
})
Send(msg, {
Target = primaryNameResult.baseNameOwner,
Action = ActionMap.PrimaryNameRequest .. "-Notice",
Data = json.encode(primaryNameResult),
})
end
end
13 changes: 13 additions & 0 deletions src/main.lua
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,19 @@ local function addPrimaryNameRequestData(ioEvent, primaryNameResult)
ioEvent:addFieldsWithPrefixIfExist(primaryNameResult.request, "Request-", { "startTimestamp", "endTimestamp" })
addResultFundingPlanFields(ioEvent, primaryNameResult)
addPrimaryNameCounts(ioEvent)

-- demand factor data
if primaryNameResult.df and type(primaryNameResult.df) == "table" then
ioEvent:addField("DF-Trailing-Period-Purchases", (primaryNameResult.df.trailingPeriodPurchases or {}))
ioEvent:addField("DF-Trailing-Period-Revenues", (primaryNameResult.df.trailingPeriodRevenues or {}))
ioEvent:addFieldsWithPrefixIfExist(primaryNameResult.df, "DF-", {
"currentPeriod",
"currentDemandFactor",
"consecutivePeriodsWithMinDemandFactor",
"revenueThisPeriod",
"purchasesThisPeriod",
})
end
end

local function assertValueBytesLowerThan(value, remainingBytes, tablesSeen)
Expand Down
4 changes: 3 additions & 1 deletion src/primary_names.lua
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ local primaryNames = {}
--- @field baseNameOwner WalletAddress
--- @field fundingPlan table
--- @field fundingResult table

--- @field df table
---
-- NOTE: lua 5.3 has limited regex support, particularly for lookaheads and negative lookaheads or use of {n}
---@param name string
---@description Asserts that the provided name is a valid undername
Expand Down Expand Up @@ -154,6 +155,7 @@ function primaryNames.createPrimaryNameRequest(name, initiator, timestamp, msgId
baseNameOwner = record.processId,
fundingPlan = fundingPlan,
fundingResult = fundingResult,
df = demand.getDemandFactorInfo(),
}
end

Expand Down
Loading