123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- --- Tokenomics.
- -- @module tokenomics
- local tokenomics = {
- name = "Tokenomics",
- slug = "tokenomics",
- desc = "A simple library for token economics",
- hide = true;
- }
- tokenomics.data = {
- result = nil
- }
- --- Config for module
- -- @field token_variables the data that goes into the token
- -- @field token_slug A no-spaces slug for the token
- -- @field initial_treasury Quantity in org treasury
- -- @field negative_spend Boolean: can users spend negative tokens? (for mutual credit)
- -- @field balances Table of user balances
- tokenomics.config = {
- approval_module = false,
- token_slug = "token",
- token_variables = {
- treasury = 0,
- negative_spend = true,
- balances = {}
- }
- }
- --- Initiate function: creates a token in an org.
- -- Set up the token data structure.
- -- Create an org treasury
- -- @function tokenomics:initiate
- -- @param result (optional) Callback if this module is embedded in other modules
- function tokenomics:initiate(result)
- -- TODO need to create a series of interactions to get the info from users
- self.data.result = result
- if self.org.tokens and self.org.tokens[slug] then
- modpol.interactions.message(
- self.initiator, "Token slug taken, aborting")
- self.org:delete_process(self.id)
- else
- self:call_module(
- self.config.approval_module,
- self.initiator,
- {
- prompt = "Create token " ..
- self.config.token_slug .. "?"
- },
- function()
- modpol.interactions.message_org(
- self.initiator, self.org.id,
- "Creating token " ..
- self.config.token_slug)
- self:create_token()
- end
- )
- end
- end
- --- Create token
- -- @function tokenomics:create_token
- function tokenomics:create_token()
- if not self.org.tokens then self.org.tokens = {} end
- self.org.tokens[self.config.token_slug] =
- self.config.token_variables
- self.org:record("Created token "..self.config.token_slug,
- self.slug)
- modpol.interactions.message_org(
- self.initiator, self.org.id,
- "Created token "..self.config.token_slug)
- if self.data.result then self.data.result() end
- -- call this wherever process might end:
- self.org:delete_process(self.id)
- end
- -----------------------------------------
- -- Utility functions
- -- all need to account for the fact that some users may not yet have balances
- -- all need to write to persistent data
- -- amount can be positive or negative (except transfer)
- --- Returns balance.
- -- If no user, get treasury balance
- -- @function tokenomics.balance
- -- @param org Name (string) or id (num)
- -- @param token Slug (string)
- -- @param user Name (string)
- function tokenomics.balance(org, token, user)
- local this_org = modpol.orgs.get_org(org)
- if not this_org[token] then return nil, "Token not found" end
- if not user then
- return this_org[token].treasury
- end
- if not this_org[user] then
- return nil, "User not found"
- else
- local user_balance = this_org[token].balances[user]
- if user_balance then
- return user_balance
- else
- return 0
- end
- end
- end
- --- Change balance
- -- @function tokenomics.change_balance
- -- @param org Org name (string) or id (number)
- -- @param token Token slug (string)
- -- @param user
- -- @param amount
- function tokenomics.change_balance(org, token, user, amount)
- local this_org = modpol.orgs.get_org(org)
- if not this_org then
- return nil, "Cannot change balance: Org not found"
- elseif not this_org.tokens then
- return nil, "Cannot change balance: no tokens found"
- elseif not this_org.tokens[token] then
- return nil, "Cannot change balance: token not found"
- elseif not this_org.members[user] then
- return nil, "Cannot change balance: user not in org"
- elseif not tonumber(amount) then
- return nil, "Cannot change balance: invalid amount"
- else
- local old_balance = this_org.tokens[token].balances[user]
- if not old_balance then old_balance = 0 end
- local new_balance = old_balance + amount
- this_org.tokens[token].balances[user] = new_balance
- local msg = "Your balance of token "..token..
- " changed from "..old_balance.." to "..new_balance
- modpol.interactions.message(user, msg)
- self.org:record(
- "Changed token balance for "..user,self.slug)
- end
- end
- --- Transfer tokens from a sender to recipient
- -- @function tokenomics.transfer
- -- @param org
- -- @param token
- -- @param sender
- -- @param recipient
- -- @param amount Positive number
- function tokenomics.transfer(org, token, sender, recipient, amount)
- local sender_balance = tokenomics.balance(org, token, sender)
- local recipient_balance = tokenomics.balance(org, token, recipient)
- if not sender_balance or recipient_balance then
- return nil, "Transfer failed, user not found"
- else
- sender_balance = sender_balance - amount
- recipient_balance = recipient_balance + amount
- local neg_spend = modpol.orgs.get_org(org).tokens[token].negative_spend
- if sender_balance < 0 and not neg_spend then
- return nil, "Transfer failed, negative spend not allowed"
- else
- tokenomics.change_balance(
- org, token, sender, sender_balance)
- tokenomics.change_balance(
- org, token, recipient, recipient_balance)
- return "Transfer complete"
- end
- end
- end
- --- Transfer from treasury
- -- @function tokenomics.treasury_transfer
- -- @param org
- -- @param token
- -- @param recipient
- -- @param amount Can be positive or negative, assumes flow from treasury to recipient
- function tokenomics.treasury_transfer(org, token, recipient, amount)
- local this_org = modpol.orgs.get_org(org)
- if not this_org then
- return nil, "Cannot transfer treasury: Org not found"
- elseif not this_org.tokens then
- return nil, "Cannot transfer treasury: no tokens found"
- elseif not this_org.tokens[token] then
- return nil, "Cannot transfer treasury: token not found"
- elseif not this_org.members[user] then
- return nil, "Cannot transfer treasury: user not in org"
- elseif not tonumber(amount) then
- return nil, "Cannot change balance: invalid amount"
- else
- local new_treasury = this_org.tokens[token].treasury - amount
- local new_recipient_balance = tokenomics.balance(org, token, recipient) + amount
- if new_treasury < 0 and not this_org.tokens[token].negative_spend then
- return nil, "Transfer failed, negative spend not allowed"
- elseif new_recipient_balance < 0 and not this_org.tokens[token].negative_spend then
- return nil, "Transfer failed, negative spend not allowed"
- else
- this_org.tokens[token].treasury = new_treasury
- self.org:record("Treasury payment",self.slug)
- tokenomics.change_balance(org, token, recipient, amount)
- end
- end
- end
- --- Creates new tokens in the org treasury
- -- @function tokenomics.issue
- -- @param org
- -- @param token
- -- @param amount
- function tokenomics.issue(org, token, amount)
- local this_org = modpol.orgs.get_org(org)
- if not this_org then
- return nil, "Cannot transfer treasury: Org not found"
- elseif not this_org.tokens then
- return nil, "Cannot transfer treasury: no tokens found"
- elseif not this_org.tokens[token] then
- return nil, "Cannot transfer treasury: token not found"
- elseif not tonumber(amount) then
- return nil, "Cannot change balance: invalid amount"
- else
- this_org.tokens[token].treasury =
- this_org.tokens[token].treasury + amount
- self.org:record("Issued tokes to treasury","tokenomics")
- end
- end
- ------------------------------------------
- modpol.modules.tokenomics = tokenomics
|