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

hollow #109

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
70 changes: 70 additions & 0 deletions lua/hollow/hollow-db.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
local sqlite3 = require("lsqlite3")

function ExecuteStatement(stmt, name)
local step_result, step_err = stmt:step()
if step_result ~= sqlite3.DONE then
stmt:finalize()
error(name .. " error executing statement: " .. (step_err or "Unknown error") .. " step_result: " .. step_result)
end
end

function InsertOrder(wallet_address, timestamp, item_type, item_qty, payment)
local stmt = db:prepare([[
insert into orders(wallet_address, timestamp, item_type, item_qty, payment)
values (?, ?, ?, ?, ?);
]])
if not stmt then
error("Preparing insert order failure: " .. db:errcode() .. " " .. db:errmsg())
end
local stmtBind = stmt:bind_values(wallet_address, timestamp, item_type, item_qty, payment)
if not stmtBind then
error("Binding insert order failure: " .. db:errcode() .. " " .. db:errmsg())
end

ExecuteStatement(stmt, "Inserting order")
end

function ReadOrders(limit, offset, wallet_address)
local readOrdersResult = {}

local sql = [[
SELECT *
FROM orders
]]

local valuesToBind = {}

if wallet_address then
sql = sql .. ' WHERE wallet_address = ?'
table.insert(valuesToBind, wallet_address)
end

sql = sql .. ' LIMIT ? OFFSET ?;'
table.insert(valuesToBind, limit)
table.insert(valuesToBind, offset)
local stmt, err_parse = db:prepare(sql)
if not stmt then
error("Error parsing SQL statement: " .. (err_parse or "Unknown error"))
end

local ok, err_bind = pcall(function ()
stmt:bind_values(table.unpack(valuesToBind))
end)
if not ok then
stmt:finalize()
error("Error binding values: " .. (err_bind or "Unknown error"))
end

local exec_ok, err_exec = pcall(function ()
for row in stmt:nrows("") do
table.insert(readOrdersResult, row)
end
end)
if not exec_ok then
stmt:finalize()
error("Error executing query: " .. (err_exec or "Unknown error"))
end

stmt:finalize()
return readOrdersResult
end
44 changes: 44 additions & 0 deletions lua/hollow/hollow-utils.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
local bint = require('.bint')(256)

function CheckValidAddress(address)
if not address or type(address) ~= 'string' then
return false
end

return string.match(address, '^[%w%-_]+$') ~= nil and #address == 43
Copy link
Contributor

Choose a reason for hiding this comment

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

eth wallets?

end

function CheckValidAmount(data)
return bint(data) > bint(0)
end

function Add(a,b)
return tostring(bint(a) + bint(b))
end

function Multiply(a,b)
return tostring(bint(a) * bint(b))
end

function Subtract(a,b)
return tostring(bint(a) - bint(b))
end

function ToNumber(a)
Copy link
Contributor

Choose a reason for hiding this comment

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

what is the added value of this fn?

return tonumber(a)
end

function HandleError(args) -- Target, TransferToken, Quantity
-- If there is a valid quantity then return the funds
if args.TransferToken and args.Quantity and CheckValidAmount(args.Quantity) then
ao.send({
Target = args.TransferToken,
Action = 'Transfer',
Tags = {
Recipient = args.Target,
Quantity = tostring(args.Quantity)
}
})
end
ao.send({ Target = args.Target, Action = args.Action, Tags = { Status = 'Error', Message = args.Message } })
end
282 changes: 282 additions & 0 deletions lua/hollow/hollow.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,282 @@
local bint = require('.bint')(256)
local sqlite3 = require("lsqlite3")

Owner = Owner or ao.env.Process.Owner

Items = Items or {}
Balances = Balances or {}

ao.authorities = {"fcoN_xJeisVsPXA-trzVAuIiqO3ydLQxM-L4XbrQKzY", "jmGGoJaDYDTx4OCM7MP-7l-VLIM4ZEGCS0cHPsSmiNE"}

--
-- WRITE
-- 1. Add-Item - add new item to the hollow or change price/qty
-- 3. Credit-Notice - create order of the items
-- READ
-- 1. Read-Item - read item with specific name
-- 2. Read-Hollow - read all the items in the hollow
-- 3. Read-Balance - read player's items balance
-- 4. Read-Orders - read orders from db
--

-- Open the database
db = db or sqlite3.open_memory()

-- init db
db:exec([[
create table if not exists orders(
id INTEGER PRIMARY KEY AUTOINCREMENT,
Copy link
Contributor

Choose a reason for hiding this comment

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

no indexes?

wallet_address TEXT NOT NULL,
timestamp INTEGER NOT NULL,
item_type TEXT NOT NULL,
item_qty INTEGER NOT NULL,
payment INTEGER NOT NULL
);
]])

Handlers.add('Credit-Notice', Handlers.utils.hasMatchingTag('Action', 'Credit-Notice'), function(msg)
Copy link
Contributor

Choose a reason for hiding this comment

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

local data = {
Sender = msg.Tags.Sender,
Quantity = msg.Tags.Quantity,
}

-- If msg.From is different than msg.Owner - authorities are taken into account
if msg.From == msg.Owner and msg.Owner ~= Owner then
HandleError({
Target = msg.From,
Action = 'Create-Order-Error',
Message = msg.Owner .. ' unauthorized.',
Quantity = data.Quantity,
TransferToken = data.Sender,
})
return
end

-- Check if all transfer fields are present
if not data.Sender or not data.Quantity then
HandleError({
Target = msg.From,
Action = 'Input-Error',
Message = 'Invalid arguments, required { Sender, Quantity }.',
Quantity = nil,
TransferToken = nil,
})
return
end

-- Check if sender is a valid address
if not CheckValidAddress(data.Sender) then
HandleError({
Target = msg.From,
Action = 'Validation-Error',
Message = 'Sender must be a valid address.',
Quantity = nil,
TransferToken = nil,
})
return
end

-- Check if quantity is a valid integer greater than zero
if not CheckValidAmount(data.Quantity) then
HandleError({
Target = msg.From,
Action = 'Validation-Error',
Message = 'Quantity must be an integer greater than zero.',
Quantity = nil,
TransferToken = nil,
})
return
end

-- If Order-Action then create the order
if (Handlers.utils.hasMatchingTag('Action', 'X-Order-Action') and msg.Tags['X-Order-Action'] == 'Create-Order') then
local orderArgs = {
orderId = msg.Id,
token = msg.From,
item = msg.Tags.Item,
itemQuantity = msg.Tags['Item-Quantity'],
sender = data.Sender,
quantity = data.Quantity,
timestamp = msg.Timestamp,
blockheight = msg['Block-Height']
}

-- check if item is passed
if not orderArgs.item or not orderArgs.itemQuantity then
HandleError({
Target = orderArgs.sender,
Action = 'Input-Error',
Message = 'Invalid arguments, required { Item, Item-Quantity }.',
Quantity = orderArgs.quantity,
TransferToken = msg.From,
})
return
end

-- check if item is present in the hollow
if not Items[orderArgs.item] then
HandleError({
Target = orderArgs.sender,
Action = 'Create-Order-Error',
Message = 'Item not found ' .. orderArgs.item,
Quantity = orderArgs.quantity,
TransferToken = msg.From,
})
return
end

-- check if there are enough items in the hollow
if ToNumber(Items[orderArgs.item].Quantity) < ToNumber(orderArgs.itemQuantity) then
HandleError({
Target = orderArgs.sender,
Action = 'Create-Order-Error',
Message = 'Not enough items in the hollow, required: ' .. orderArgs.itemQuantity .. ' had: ' .. Items[orderArgs.item].Quantity,
Quantity = orderArgs.quantity,
TransferToken = msg.From,
})
return
end

-- check if price of ordered items alligns with quantity of the Credit-Notice
local totalItemPrice = Multiply(orderArgs.itemQuantity, Items[orderArgs.item].Price)

if ToNumber(totalItemPrice) <= ToNumber(orderArgs.quantity) then
if not Balances[orderArgs.sender] then Balances[orderArgs.sender] = {} end
if not Balances[orderArgs.sender][orderArgs.item] then
Balances[orderArgs.sender][orderArgs.item] = '0'
end

Items[orderArgs.item].Quantity = Subtract(Items[orderArgs.item].Quantity, orderArgs.itemQuantity)
Balances[orderArgs.sender][orderArgs.item] = Add(Balances[orderArgs.sender][orderArgs.item], orderArgs.itemQuantity)
ao.send({
Target = msg.From,
Action = 'Create-Order-Success',
Data = 'Successfully created order for item: ' .. orderArgs.item .. ' Qty: ' .. orderArgs.itemQuantity .. ' for price: ' .. totalItemPrice
})


-- return remaining token quantity to the sender
if ToNumber(totalItemPrice) < ToNumber(orderArgs.quantity) then
ao.send({
Target = msg.From,
Action = 'Transfer',
Tags = {
Recipient = orderArgs.Sender,
Quantity = tostring(Subtract(orderArgs.quantity, totalItemPrice))
}
})
end
InsertOrder(orderArgs.sender, msg.Timestamp, orderArgs.item, orderArgs.itemQuantity, totalItemPrice)
return
else
HandleError({
Target = msg.From,
Action = 'Create-Order-Error',
Message = 'Not enough tokens transferred, required: ' .. totalItemPrice .. ' had: ' .. orderArgs.quantity,
Quantity = orderArgs.quantity,
TransferToken = msg.From,
})
return
end
end
end)

Handlers.add('Add-Item', Handlers.utils.hasMatchingTag('Action', 'Add-Item'), function(msg)
assert(type(msg.Tags.Quantity) == 'string', 'Quantity is required.')
assert(type(msg.Tags.Item) == 'string', 'Item is required.')

if not Items[msg.Tags.Item] then
assert(type(msg.Tags.Price) == 'string', 'Price is required for new item.')
end

if msg.Owner ~= Owner then
HandleError({
Target = msg.From,
Action = 'Add-Item-Error',
Message = 'Only the Owner can add new items..',
Quantity = nil,
TransferToken = nil,
})
return
end

if not Items[msg.Tags.Item] then
Items[msg.Tags.Item] = {
Price = "0",
Quantity = "0"
}
end

Items[msg.Tags.Item].Quantity = Add(Items[msg.Tags.Item].Quantity, bint(msg.Tags.Quantity))
if msg.Tags.Price then
Items[msg.Tags.Item].Price = msg.Tags.Price
end

ao.send({
Target = msg.From,
Action = 'Add-Item-Success',
Data = 'Successfully added item ' .. msg.Tags.Item .. ' Qty ' .. Items[msg.Item].Quantity .. ' Price ' .. Items[msg.Item].Price
})
end)

Handlers.add('Read-Item', Handlers.utils.hasMatchingTag('Action', 'Read-Item'), function(msg)
assert(type(msg.Tags.Item) == 'string', 'Item is required.')

if not Items[msg.Tags.Item] then
HandleError({
Target = msg.From,
Action = 'Item-Info-Error',
Message = 'Item not found.',
Quantity = nil,
TransferToken = nil,
})
return
end

ao.send({
Target = msg.From,
Action = 'Item-Info',
Data = Items[msg.Tags.Item],
})
end)

Handlers.add('Read-Balance', Handlers.utils.hasMatchingTag('Action', 'Read-Balance'), function(msg)
assert(type(msg.Tags['Wallet-Address']) == 'string', 'Wallet address is required.')

if not Balances[msg.Tags['Wallet-Address']] then
HandleError({
Target = msg.From,
Action = 'Read-Balance-Error',
Message = 'Wallet address not found in balances.',
Quantity = nil,
TransferToken = nil,
})
return
end

ao.send({
Target = msg.From,
Action = 'Read-Balance-Success',
Data = Balances[msg.Tags['Wallet-Address']],
})
end)

Handlers.add('Read-Hollow', Handlers.utils.hasMatchingTag('Action', 'Read-Hollow'), function(msg)
ao.send({
Target = msg.From,
Action = 'Read-Hollow-Success',
Data = Items,
})
end)

Handlers.add('Read-Orders', Handlers.utils.hasMatchingTag('Action', 'Read-Orders'), function(msg)
local limit = msg.Tags.Limit or 10
local offset = msg.Tags.Offset or 0
local wallet_address = msg.Tags['Wallet-Address'] or nil
local result = ReadOrders(limit, offset, wallet_address)
ao.send({
Target = msg.From,
Action = 'Read-Orders-Success',
Data = result,
})
end)
Loading