Interface | Params |
---|---|
claim | [
"token_symbol"
] |
createMortgageAmountNFT | [] |
init | [] |
mortgage | [
"token_symbol",
"mortgage_amount"
] |
redeem | [
"token_symbol"
] |
requestShoot | [
"project_name"
] |
requestUnshoot | [
"project_name"
] |
unshootCallback | [
"account",
"token_symbol",
"shoot_amount"
] |
Method | Caller | Call time | Transaction |
---|---|---|---|
redeem |
2020-10-12 12:35:44
| ||
redeem |
2020-10-11 13:01:24
| ||
redeem |
2020-10-11 12:53:04
| ||
redeem |
2020-10-10 22:04:24
| ||
redeem |
2020-10-10 17:54:02
| ||
redeem |
2020-10-10 17:15:02
| ||
redeem |
2020-10-10 17:14:56
| ||
redeem |
2020-10-10 17:14:52
| ||
redeem |
2020-10-10 15:27:16
| ||
redeem |
2020-10-10 15:23:20
| ||
redeem |
2020-10-10 15:21:56
| ||
redeem |
2020-10-10 15:21:08
| ||
redeem |
2020-10-10 14:13:44
| ||
claim |
2020-10-10 14:13:30
| ||
redeem |
2020-10-10 13:00:56
| ||
claim |
2020-10-10 12:30:34
| ||
redeem |
2020-10-10 12:29:22
| ||
claim |
2020-10-10 12:29:14
| ||
claim |
2020-10-10 12:22:50
| ||
claim |
2020-10-10 12:22:40
|
local MAIN_TOKEN_SYMBOL = "COCOS"
local MAIN_TOKEN_ACCURACY = 100000
local MINING_TOKEN_SYMBOL = "SHOOT"
local MINING_TOKEN_ACCURACY = 100000
local MORTGAGE_TOKEN = {
COCOS = {accuracy = 100000, min = 10000,
duration = 60 * 60 * 160, total_mine = 5000 * MINING_TOKEN_ACCURACY},
LWT = {accuracy = 10000, min = 300,
duration = 60 * 60 * 160, total_mine = 5000 * MINING_TOKEN_ACCURACY},
CFS = {accuracy = 100000, min = 30,
duration = 60 * 60 * 160, total_mine = 5000 * MINING_TOKEN_ACCURACY},
KKKK = {accuracy = 100000, min = 15,
duration = 60 * 60 * 160, total_mine = 5000 * MINING_TOKEN_ACCURACY},
}
local NFT_WORLDVIEW = "SHOOT"
local NFT_TYPE = "MORTGAGE"
local NFT_ICON = "http://www.shoot.finance/shoot.png"
local MAX_TRANSFER_AMOUNT = 1000000000000000 --限制最大转账数量
local MORTGAGE_AMOUNT_NFT_ID = "" --TODO
local CONTRACT_BIGINTEGER = "contract.crosbiginteger" -- 大整数计算库
local CONTRACT_SHOOTMINING_ID = "" --TODO
local PROJECT_TOKEN = {
["CFS_CFS"] = "CFS",
["CFS_COCOS"] = "COCOS",
["36K_COCOS"] = "COCOS",
["36K_KKKK"] = "KKKK",
}
local ACTION_SHOOT = "shoot"
local ACTION_UNSHOOT = "unshoot"
--private 检查整数(字符串)
local function isInteger(str)
local num = tonumber(str)
return num and (num - math.floor(num) == 0)
end
--private 读取链上数据
local function readAll()
read_list = {
public_data = {
_account_set = true,
_token_info_set = true,
}
}
chainhelper:read_chain()
end
--private 写入链上数据
local function writeAll()
write_list = {
public_data = {
_account_set = true,
_token_info_set = true,
}
}
chainhelper:write_chain()
end
local function encodeValueType(value)
local tp = type(value)
return tp == "number" and 1
or tp == "string" and 2
or tp == "boolean" and 3
or tp == "table" and 4
or tp == "function" and 5
or 2
end
-- 调用其它合约的方法
local function invokeContractFunction(contract_name, function_name, ...)
local args = {...}
local value_list = {}
for i = 1, #args do
local value = args[i]
if type(value) == "table" then
value = cjson.encode(value)
end
table.insert(value_list, {
encodeValueType(value),
{v = value},
})
end
value_list = cjson.encode(value_list)
chainhelper:invoke_contract_function(contract_name, function_name, value_list)
end
--private 创建用户数据NFT
local function createNFT(account)
local base_describe = {
type = NFT_TYPE,
name = account.."-MORTGAGE",
icon = NFT_ICON,
}
--创建NFT,必须是合约owner创建
local nft_id = chainhelper:create_nft_asset(contract_base_info.owner,
NFT_WORLDVIEW, cjson.encode(base_describe), true, true)
return nft_id
end
--private 读取用户数据NFT
local function readNFT(nft_id, token_symbol)
local nft = cjson.decode(chainhelper:get_nft_asset(nft_id))
assert(nft, "nft not found")
assert(nft.world_view == NFT_WORLDVIEW, "invalid nft worldview")
assert(nft.nh_asset_creator == contract_base_info.owner, "invalid nft creator")
local token_info
for _, contract in pairs(nft.describe_with_contract) do
if contract[1] == contract_base_info.id then
for _, describe in pairs(contract[2]) do
if describe[1] == token_symbol then
token_info = cjson.decode(describe[2])
break
end
end
break
end
end
return token_info
end
--private 写入用户数据NFT
local function writeNFT(nft_id, token_symbol, token_info)
--转移NFT
if contract_base_info.caller ~= contract_base_info.owner then
chainhelper:change_nht_active_by_owner(contract_base_info.caller, nft_id, true)
end
--记录数据
chainhelper:nht_describe_change(nft_id, token_symbol, cjson.encode(token_info), true)
--转移NFT
if contract_base_info.caller ~= contract_base_info.owner then
chainhelper:change_nht_active_by_owner(contract_base_info.owner, nft_id, true)
end
end
--private 更新质押总量NFT
local function updateMortgageAmountNFT()
read_list = {
public_data = {
_token_info_set = true,
}
}
chainhelper:read_chain()
--转移NFT
if contract_base_info.caller ~= contract_base_info.owner then
chainhelper:change_nht_active_by_owner(contract_base_info.caller, MORTGAGE_AMOUNT_NFT_ID, true)
end
--记录数据
local mortgage_amount_set = {}
for token_symbol, info in pairs(public_data._token_info_set) do
mortgage_amount_set[token_symbol] = info.mortgage
end
chainhelper:nht_describe_change(MORTGAGE_AMOUNT_NFT_ID, "info", cjson.encode(mortgage_amount_set), true)
--转移NFT
if contract_base_info.caller ~= contract_base_info.owner then
chainhelper:change_nht_active_by_owner(contract_base_info.owner, MORTGAGE_AMOUNT_NFT_ID, true)
end
end
--private 提取矿币
local function localClaim(bi, token_symbol)
local token_setting = MORTGAGE_TOKEN[token_symbol]
assert(token_setting, token_symbol.." not supported")
local account = contract_base_info.caller
local now = chainhelper:time()
read_list = {
public_data = {
_account_set = {
[account] = true,
},
_token_info_set = {
[token_symbol] = true,
},
}
}
chainhelper:read_chain()
local token_total_info = public_data._token_info_set[token_symbol]
assert(token_total_info.start_time > 0, "mining not started")
assert(public_data._account_set and public_data._account_set[account], "account not found")
local nft_id = public_data._account_set[account]
local token_info = readNFT(nft_id, token_symbol)
local released_time = math.min(now - token_total_info.start_time, token_setting.duration)
local released_mine = math.min(math.ceil((token_setting.total_mine / token_setting.duration) * released_time),
token_setting.total_mine)
local account_amount = bi.div(bi.mul(token_info.mortgage, bi.add(released_mine - token_total_info.claimed, token_total_info.mask)), token_total_info.mortgage)
local claim_amount = tonumber(bi.sub(bi.sub(account_amount, token_info.claimed), token_info.mask))
assert(claim_amount > 0, "no mine to claim")
token_info.claimed = token_info.claimed + claim_amount
writeNFT(nft_id, token_symbol, token_info)
--转账
if account ~= contract_base_info.owner then
assert(claim_amount <= MAX_TRANSFER_AMOUNT, "transfer too large amount")
chainhelper:transfer_from_owner(account, claim_amount, MINING_TOKEN_SYMBOL, true)
end
end
--public 初始化 owner only
function init()
assert(contract_base_info.invoker_contract_id == "1.16.0", "Not to be called by any other contracts!")
assert(chainhelper:is_owner(), "Unauthorized!")
readAll()
public_data._account_set = public_data._account_set or {}
if not public_data._token_info_set then
public_data._token_info_set = {}
for token_symbol, _ in pairs(MORTGAGE_TOKEN) do
local info = {
mortgage = 0,
mask = "0",
start_time = 0,
claimed = 0,
}
public_data._token_info_set[token_symbol] = info
end
updateMortgageAmountNFT()
end
writeAll()
end
--public 抵押资产
function mortgage(token_symbol, mortgage_amount)
local token_setting = MORTGAGE_TOKEN[token_symbol]
assert(token_setting, token_symbol.." not supported")
assert(isInteger(mortgage_amount), "mortgage_amount should be integer!")
mortgage_amount = tonumber(mortgage_amount)
assert(mortgage_amount <= chainhelper:integer_max(), "mortgage_amount should be <= "..chainhelper:integer_max())
assert(mortgage_amount >= token_setting.min * token_setting.accuracy, "mortgage at least "..(token_setting.min * token_setting.accuracy).." "..token_symbol)
local account = contract_base_info.caller
local bi = import_contract(CONTRACT_BIGINTEGER)
read_list = {
public_data = {
_account_set = {
[account] = true,
},
_token_info_set = {
[token_symbol] = true,
},
}
}
chainhelper:read_chain()
local token_total_info = public_data._token_info_set[token_symbol]
local now = chainhelper:time()
if token_total_info.start_time > 0 then
assert(now - token_total_info.start_time <= token_setting.duration, "out of mining time")
else
token_total_info.start_time = now --第一个抵押开始挖矿
end
--检查账号信息是否已存在
local nft_id
if public_data._account_set and public_data._account_set[account] then
nft_id = public_data._account_set[account]
else
nft_id = createNFT(account)
public_data._account_set = {}
public_data._account_set[account] = nft_id
end
--检查该币种信息是否已存在
local token_info = readNFT(nft_id, token_symbol) or {
mortgage = 0,
claimed = 0,
mask = "0",
shoot = 0,
}
token_info.mortgage = token_info.mortgage + mortgage_amount
local released_time = now - token_total_info.start_time
local released_mine = math.min(token_setting.total_mine,
math.ceil((token_setting.total_mine / token_setting.duration) * released_time))
local mask = token_total_info.mortgage <= 0 and "0" or --向下取整 + 1
bi.add(bi.div(bi.mul(mortgage_amount, bi.add(released_mine - token_total_info.claimed, token_total_info.mask)), token_total_info.mortgage), 1)
token_info.mask = bi.add(token_info.mask, mask)
writeNFT(nft_id, token_symbol, token_info)
--转账
if account ~= contract_base_info.owner then
assert(mortgage_amount <= MAX_TRANSFER_AMOUNT, "transfer too large amount")
chainhelper:transfer_from_caller(contract_base_info.owner, mortgage_amount, token_symbol, true)
end
--chainhelper:adjust_lock_asset(token_symbol, mortgage_amount)
token_total_info.mortgage = token_total_info.mortgage + mortgage_amount
token_total_info.mask = bi.add(token_total_info.mask, mask)
write_list = {
public_data = {
_account_set = {
[account] = true,
},
_token_info_set = {
[token_symbol] = true,
},
}
}
chainhelper:write_chain()
--更新质押总量NFT
updateMortgageAmountNFT()
end
--public 提取矿币
function claim(token_symbol)
local bi = import_contract(CONTRACT_BIGINTEGER)
localClaim(bi, token_symbol)
end
--public 提取矿币并赎回抵押资产
function redeem(token_symbol)
local token_setting = MORTGAGE_TOKEN[token_symbol]
assert(token_setting, token_symbol.." not supported")
local account = contract_base_info.caller
local bi = import_contract(CONTRACT_BIGINTEGER)
localClaim(bi, token_symbol)
read_list = {
public_data = {
_account_set = {
[account] = true,
},
_token_info_set = {
[token_symbol] = true,
},
}
}
chainhelper:read_chain()
local token_total_info = public_data._token_info_set[token_symbol]
assert(token_total_info.start_time > 0, "mining not started")
assert(public_data._account_set and public_data._account_set[account], "account not found")
local nft_id = public_data._account_set[account]
local token_info = readNFT(nft_id, token_symbol)
assert(token_info.mortgage > token_info.shoot, "no remained mortgage to redeem")
local redeem_amount = 0
if token_info.shoot > 0 then
redeem_amount = token_info.mortgage - token_info.shoot
local redeem_claimed = bi.div(bi.mul(redeem_amount, token_info.claimed), token_total_info.mortgage) --向下取整
local redeem_mask = bi.div(bi.mul(redeem_amount, token_info.mask), token_total_info.mortgage) --向下取整
token_info.mortgage = token_info.mortgage - redeem_amount
token_info.claimed = token_info.claimed - redeem_claimed
token_info.mask = bi.sub(token_info.mask, redeem_mask)
token_total_info.mortgage = token_total_info.mortgage - redeem_amount
token_total_info.mask = bi.sub(token_total_info.mask, redeem_mask)
token_total_info.claimed = token_total_info.claimed + redeem_claimed + 1 --向下取整 + 1
else --防止极小值
redeem_amount = token_info.mortgage
token_total_info.mortgage = token_total_info.mortgage - token_info.mortgage
token_total_info.mask = bi.sub(token_total_info.mask, token_info.mask)
token_total_info.claimed = token_total_info.claimed + token_info.claimed
token_info = {
mortgage = 0,
claimed = 0,
mask = "0",
shoot = 0,
}
end
writeNFT(nft_id, token_symbol, token_info)
--chainhelper:adjust_lock_asset(token_symbol, -redeem_amount)
--转账
if account ~= contract_base_info.owner then
assert(redeem_amount <= MAX_TRANSFER_AMOUNT, "transfer too large amount")
chainhelper:transfer_from_owner(account, redeem_amount, token_symbol, true)
end
write_list = {
public_data = {
_token_info_set = {
[token_symbol] = true,
},
}
}
chainhelper:write_chain()
--更新质押总量NFT
updateMortgageAmountNFT()
end
--创建质押总量NFT
function createMortgageAmountNFT()
assert(contract_base_info.invoker_contract_id == "1.16.0", "Not to be called by any other contracts!")
assert(chainhelper:is_owner(), "Unauthorized!")
local base_describe = {
type = "MORTGAGE_INFO",
name = "SHOOT-MORTGAGE",
icon = NFT_ICON,
}
--创建NFT,必须是合约owner创建
local nft_id = chainhelper:create_nft_asset(contract_base_info.owner,
NFT_WORLDVIEW, cjson.encode(base_describe), true, true)
chainhelper:log("nft_id: "..nft_id)
end
--请求shoot
function requestShoot(project_name)
assert(type(project_name) == "string", "project_name should be string")
local token_symbol = PROJECT_TOKEN[project_name]
assert(token_symbol, project_name .. " not supported")
local account = contract_base_info.caller
read_list = {
public_data = {
_account_set = {
[account] = true,
},
}
}
chainhelper:read_chain()
assert(public_data._account_set and public_data._account_set[account], "account not found")
local nft_id = public_data._account_set[account]
local token_info = readNFT(nft_id, token_symbol)
assert(token_info.mortgage > token_info.shoot, "no remained mortgage to shoot")
local shoot_amount = token_info.mortgage - token_info.shoot
token_info.shoot = token_info.shoot + shoot_amount
writeNFT(nft_id, token_symbol, token_info)
invokeContractFunction(CONTRACT_SHOOTMINING_ID, "postRequest", account, project_name, ACTION_SHOOT, shoot_amount)
end
--请求unshoot
function requestUnshoot(project_name)
assert(type(project_name) == "string", "project_name should be string")
local token_symbol = PROJECT_TOKEN[project_name]
assert(token_symbol, project_name.."not supported")
local account = contract_base_info.caller
read_list = {
public_data = {
_account_set = {
[account] = true,
},
}
}
chainhelper:read_chain()
assert(public_data._account_set and public_data._account_set[account], "account not found")
local nft_id = public_data._account_set[account]
local token_info = readNFT(nft_id, token_symbol)
assert(token_info.shoot > 0, "no shoot to unshoot")
assert(token_info.mortgage >= token_info.shoot, "data error")
invokeContractFunction(CONTRACT_SHOOTMINING_ID, "postRequest", account, project_name, ACTION_UNSHOOT, 0)
end
--unshoot回调 only by shootmining contract
function unshootCallback(account, token_symbol, shoot_amount)
assert(contract_base_info.invoker_contract_id == CONTRACT_SHOOTMINING_ID,
"only called by shootmining contract")
read_list = {
public_data = {
_account_set = {
[account] = true,
},
}
}
chainhelper:read_chain()
assert(public_data._account_set and public_data._account_set[account], "account not found")
local nft_id = public_data._account_set[account]
local token_info = readNFT(nft_id, token_symbol)
token_info.shoot = token_info.shoot - shoot_amount
writeNFT(nft_id, token_symbol, token_info)
end