Skip to content

Instantly share code, notes, and snippets.

@andytudhope
Last active March 5, 2024 17:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andytudhope/5728a5acb9cb288ec6f61f4daa09d14c to your computer and use it in GitHub Desktop.
Save andytudhope/5728a5acb9cb288ec6f61f4daa09d14c to your computer and use it in GitHub Desktop.
A memecoin for ao which illustrates a different way of using the token.lua blueprint with hopefully more prosocial outcomes
-- import the necessaries
local json = require('json')
CRED = "Sa0iBLPNyJQrwpTTG-tWLQU-1QeUAJA73DdxGGiKoJc"
if not Balances then Balances = { [ao.id] = 0 } end
if not CredSent then CredSent = { [ao.id] = 0 } end
if Name ~= 'aoWifHat' then Name = 'aoWifHat' end
if Ticker ~= 'WHAT' then Ticker = 'WHAT' end
if Denomination ~= 1000 then Denomination = 1000 end
if not Logo then Logo = 'OVJ2EyD3dKFctzANd0KX_PCgg8IQvk0zYqkWIj-aeaU' end
-- This function may seem strange at first, but it's worth thinking about.
-- We want a community meme coin, which is used to vote on what appears in an iframe
-- on a "community" web property. Therefore, it is likely that we want to
-- incentivise lots of small holders, with roughly equal amounts, such that we
-- have a greater chance of creating a vibrant ecosystem of regular voters,
-- rather than a stale one which tends towards a few whales over time.
-- How do we achieve such a thing?
-- We mint 1WHAT:1CRED for the first CRED you send in, and then implement
-- a linear decay function such that you get 0 WHAT for the 10000th CRED you send.
-- This actively disincentivises people sending in lots of CRED in order to get
-- more power over what appears on the community website, and allows us to go
-- along with the rather simple schemes in the voting + staking blueprints without
-- causing too many power imbalances (hopefully).
local function calcCoin(quantity, process)
local maxCred = 10000 * 1000
local whatToMint
if CredSent[process] == nil then
-- this is the first time this process has sent CRED
CredSent[process] = quantity
if quantity <= maxCred then
-- Linear decay formula: starts with 1 at first CRED and linearly decreases to 0 at 10000th CRED
whatToMint = (maxCred - CredSent[process] + 1000) / maxCred
else
-- they've sent more than 10000 CRED immediately.
-- Mint the max WHAT (which is 5000 (1/2b x h) and send it to them)
whatToMint = 5000 * 1000
end
else
local whatMinted = (maxCred - CredSent[process]) / maxCred
local totalWhat = (maxCred - CredSent[process] + quantity) / maxCred
whatToMint = totalWhat - whatMinted
end
-- Ensure the returned value is a string and does not exceed 1000 units (for the first token)
return tostring(math.floor(whatToMint))
end
-- Handler for incoming messages
Handlers.add('info', Handlers.utils.hasMatchingTag('Action', 'Info'), function(m)
ao.send(
{ Target = m.From, Tags = { Name = Name, Ticker = Ticker, Logo = Logo, Denomination = tostring(Denomination) } })
end)
-- Handlers for token balances and info
Handlers.add('balance', Handlers.utils.hasMatchingTag('Action', 'Balance'), function(m)
local bal = '0'
-- If not Target is provided, then return the Senders balance
if (m.Tags.Target and Balances[m.Tags.Target]) then
bal = tostring(Balances[m.Tags.Target])
elseif Balances[m.From] then
bal = tostring(Balances[m.From])
end
ao.send({
Target = m.From,
Tags = { Target = m.From, Balance = bal, Ticker = Ticker, Data = json.encode(tonumber(bal)) }
})
end)
Handlers.add('balances', Handlers.utils.hasMatchingTag('Action', 'Balances'),
function(m) ao.send({ Target = m.From, Data = json.encode(Balances) }) end)
-- Handler for transfers
Handlers.add('transfer', Handlers.utils.hasMatchingTag('Action', 'Transfer'), function(m)
assert(type(m.Tags.Recipient) == 'string', 'Recipient is required!')
assert(type(m.Tags.Quantity) == 'string', 'Quantity is required!')
if not Balances[m.From] then Balances[m.From] = 0 end
if not Balances[m.Tags.Recipient] then Balances[m.Tags.Recipient] = 0 end
local qty = tonumber(m.Tags.Quantity)
assert(type(qty) == 'number', 'qty must be number')
if Balances[m.From] >= qty then
Balances[m.From] = Balances[m.From] - qty
Balances[m.Tags.Recipient] = Balances[m.Tags.Recipient] + qty
--[[
Only Send the notifications to the Sender and Recipient
if the Cast tag is not set on the Transfer message
]] --
if not m.Tags.Cast then
-- Send Debit-Notice to the Sender
ao.send({
Target = m.From,
Tags = { Action = 'Debit-Notice', Recipient = m.Tags.Recipient, Quantity = tostring(qty) }
})
-- Send Credit-Notice to the Recipient
ao.send({
Target = m.Tags.Recipient,
Tags = { Action = 'Credit-Notice', Sender = m.From, Quantity = tostring(qty) }
})
end
else
ao.send({
Target = m.Tags.From,
Tags = { Action = 'Transfer-Error', ['Message-Id'] = m.Id, Error = 'Insufficient Balance!' }
})
end
end)
-- Handler for processes that want to buy WHAT
Handlers.add(
"buy",
function(m)
return
m.Tags.Action == "Credit-Notice" and
m.From == CRED and
m.Tags.Quantity >= "1000" and "continue" -- 1 CRED == 1000 CRED Units
end,
function(m)
local memes = calcCoin(tonumber(m.Tags.Quantity), m.Tags.Sender)
-- rather that mint a massive balance using the token blueprint, we just mint here as needed.
-- again, this is in service of a provably fair community system.
Balances[ao.id] = tonumber(memes)
ao.send({Target = ao.id, Action = "Transfer", Recipient = m.Tags.Sender, Quantity = memes })
end
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment