修改合作伙伴nft holder claim规则

This commit is contained in:
CounterFire2023 2024-04-16 13:37:10 +08:00
parent 8a7b036bd9
commit 9513fab283
8 changed files with 188 additions and 27 deletions

View File

@ -5,7 +5,9 @@
"contract": "0x20577896ea6113ed8c94b2f08f3893bdc08eba22",
"collection": "l3e7 worlds",
"remarks": "600 collection",
"chain": 1
"chain": 1,
"guild": "1222509817411665920",
"role": "1229658972793999391"
},
{
"projectName": "Ultiverse",

View File

@ -22,7 +22,7 @@ export const STEP_SCORE_MIN = 40
// 每一步能获得的最大分数
export const STEP_SCORE_MAX = 50
// 每一步能获得的宝箱的概率
export const STEP_CHEST_RATE = 0.3
export const STEP_CHEST_RATE = 1
// 邀请用户得到额外分数
export const INVITE_REBATE = 0.1

View File

@ -2,6 +2,7 @@ import { ChestStatusEnum } from 'models/ActivityChest'
import { NFTHolderRecord } from 'models/NFTHodlerRecord'
import { queryNftBalance } from 'services/chain.svr'
import { generateChestLevel, generateNewChest } from 'services/game.svr'
import { checkDiscordRole } from 'services/oauth.svr'
import { SyncLocker, BaseController, router, role, ROLE_ANON, ZError } from 'zutils'
const nftList = require('../../configs/partner_nft_list.json')
@ -33,9 +34,9 @@ class NftController extends BaseController {
return list
}
/**
* nft holder奖励
* nft holder奖励, NFT
*/
@router('post /api/partner/claim')
// @router('post /api/partner/claim')
async claimNftHolderReward(req) {
new SyncLocker().checkLock(req)
const user = req.user
@ -73,4 +74,43 @@ class NftController extends BaseController {
await recordNew.save()
return { chests: [chest.toJson()] }
}
@router('post /api/partner/claim')
async claimNftHolderRewardDC(req) {
new SyncLocker().checkLock(req)
const user = req.user
let { contract } = req.params
if (!contract) {
throw new ZError(11, 'contract not found')
}
contract = contract.toLowerCase()
if (!nftMap.has(contract)) {
throw new ZError(12, 'contract not found')
}
let record = await NFTHolderRecord.findOne({ user: user.id, contract })
if (record) {
throw new ZError(13, 'already claimed')
}
const cfg = nftMap.get(contract)
let rpcRes = await checkDiscordRole(user.address.toLowerCase(), cfg.guild, cfg.role)
console.log('check result:', rpcRes)
if (rpcRes.errcode) {
throw new ZError(20, `check error: ${rpcRes.errmsg}`)
}
if (!rpcRes.data.result) {
throw new ZError(14, 'not match claim condition')
}
let randomLevel = generateChestLevel()
let chest = generateNewChest(user.id, user.activity, randomLevel, ChestStatusEnum.NORMAL)
await chest.save()
let recordNew = new NFTHolderRecord({
user: user.id,
contract,
chain: nftMap.get(contract).chain,
holderNum: 1,
rewards: [chest.id],
})
await recordNew.save()
return { chests: [chest.toJson()] }
}
}

View File

@ -146,7 +146,7 @@ class SignController extends BaseController {
discordName: user.discordName,
scoreToday: formatNumShow(todayScore ? parseInt(todayScore + '') / RANK_SCORE_SCALE : 0),
scoreTotal: formatNumShow(totalScore ? parseInt(totalScore + '') / RANK_SCORE_SCALE : 0),
rankTotal: totalRank ? totalRank : '-',
rankTotal: totalRank != undefined ? totalRank : '-',
invite,
inviteCount: records.length,
inviteScore,

View File

@ -8,6 +8,7 @@ import { BASE_TASK_TICKET } from 'common/Constants'
import { BASE_TASK_REWARD, SIGN_SEQ, TicketRecord } from 'models/TicketRecord'
import { ActivityGame } from 'models/ActivityGame'
import { formatNumShow } from 'common/Utils'
import { fetchClaimStatus } from 'services/chain.svr'
const fs = require('fs')
const prod = process.env.NODE_ENV === 'production'
@ -205,6 +206,10 @@ export default class TasksController extends BaseController {
if (!chainRecord) {
throw new ZError(14, 'waiting for chain confirm')
}
// let result = await fetchClaimStatus(user.address.toLowerCase(), task)
// if (!result) {
// throw new ZError(15, 'waiting for chain confirm')
// }
}
const Task = require('../tasks/' + currentTask.task)

View File

@ -2,6 +2,10 @@ import { CheckIn } from 'models/chain/CheckIn'
import { NftHolder } from 'models/chain/NftHolder'
import { NftStake } from 'models/chain/NftStake'
import { getMonthBegin, getNDayAgo } from 'utils/utcdate.util'
import { timeoutFetch } from 'zutils/utils/net.util'
import { numberToBN } from 'zutils/utils/number.util'
const DEFAULT_TIMEOUT = 30000
const ACTIVITY_RPC_URL = process.env.ACTIVITY_RPC_URL
export const queryCheckInList = async (address: string, days: string | number | string[], limit: number = 0) => {
let query: any = { from: address.toLowerCase() }
@ -69,23 +73,94 @@ export const queryStakeList = async (userAddress: string) => {
return records
}
const requestChain = async (rpc: string, method: string, params: any) => {
const data = {
id: Date.now(),
jsonrpc: '2.0',
method,
params,
}
const options: any = {
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
body: JSON.stringify(data),
}
return timeoutFetch(rpc, options, DEFAULT_TIMEOUT).then(res => res.json())
}
export const queryNftBalance = async (contract: string, address: string) => {
const rpc = 'https://mainnet.infura.io/v3/b6bf7d3508c941499b10025c0776eaf8'
const data = {
id: (Date.now() / 1000) | 0,
jsonrpc: '2.0',
method: 'eth_call',
params: [
{
data: `0x70a08231000000000000000000000000${address.replace('0x', '')}`,
from: address,
to: contract,
},
'latest',
],
}
return fetch(rpc, {
body: JSON.stringify(data),
method: 'POST',
}).then(res => res.json())
const params = [
{
data: `0x70a08231000000000000000000000000${address.replace('0x', '')}`,
from: address,
to: contract,
},
'latest',
]
return requestChain(rpc, 'eth_call', params)
}
export const fetchChainStatus = async (address: string, data: string) => {
console.log(data)
const params = [
{
data: data,
from: address,
to: process.env.ACTIVITY_CONTRACT,
},
'latest',
]
return requestChain(ACTIVITY_RPC_URL, 'eth_call', params)
}
export const fetchCheckInStatus = async (address: string) => {
const days = ((Date.now() / 1000 / 60 / 60 / 24) | 0) - 1
const valStr = days.toString(16).padStart(64, '0')
const addressStr = address.replace('0x', '').padStart(64, '0')
const method = '86cd4926'
return fetchChainStatus(address, `0x${method}${addressStr}${valStr}`)
}
export const fetchExploreStatus = async (address: string, exploreId: string) => {
const valStr = exploreId.toString().padStart(64, '0')
const addressStr = address.replace('0x', '').padStart(64, '0')
const method = '36028275'
return fetchChainStatus(address, `0x${method}${addressStr}${valStr}`)
}
export const fetchOpenBoxtatus = async (address: string, boxId: string) => {
const valStr = boxId.padStart(64, '0')
const addressStr = address.replace('0x', '').padStart(64, '0')
const method = 'd869bb29'
return fetchChainStatus(address, `0x${method}${addressStr}${valStr}`)
}
export const fetchEnhanceStatus = async (address: string, shareCode: string) => {
const shareCodeHex = shareCode
.split('')
.map(c => c.charCodeAt(0).toString(16).padStart(2, '0'))
.join('')
const valStr = shareCodeHex.padStart(64, '0')
const addressStr = address.replace('0x', '').padStart(64, '0')
const method = '9b68ea4c'
let res = await fetchChainStatus(address, `0x${method}${addressStr}${valStr}`)
if (res.error) {
throw new Error(res.error.message)
}
let result = parseInt(res.result)
return !!result
}
export const fetchClaimStatus = async (address: string, taskId: string) => {
const taskIdHex = taskId
.split('')
.map(c => c.charCodeAt(0).toString(16).padStart(2, '0'))
.join('')
const valStr = taskIdHex.padStart(64, '0')
const addressStr = address.replace('0x', '').padStart(64, '0')
const method = '4902f7e0'
return fetchChainStatus(address, `0x${method}${addressStr}${valStr}`)
}

View File

@ -1,7 +1,8 @@
import { hmacSha256 } from 'zutils/utils/security.util'
import { handleFetch } from 'zutils/utils/net.util'
import { handleFetch, timeoutFetch } from 'zutils/utils/net.util'
const SECRET_KEY = process.env.HASH_SALT
const DEFAULT_TIMEOUT = 30000
function createSign(address: string) {
// address = address.toLowerCase();
@ -9,14 +10,51 @@ function createSign(address: string) {
return signCheck
}
export function checkTwitter(address: string) {
export async function checkTwitter(address: string) {
let sign = createSign(address)
const url = `${process.env.OAUTH_SVR_URL}/activity/twitter/${address}?sign=${sign}`
return handleFetch(url)
}
export function checkDiscord(address: string) {
export async function checkDiscord(address: string) {
let sign = createSign(address)
const url = `${process.env.OAUTH_SVR_URL}/activity/discord/${address}?sign=${sign}`
return handleFetch(url)
}
export async function checkDiscordGuid(address: string, gid: string) {
let sign = createSign(address)
const data = {
gid,
address,
sign,
}
const url = `${process.env.OAUTH_SVR_URL}/activity/discord/guild`
const options: any = {
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
body: JSON.stringify(data),
}
return timeoutFetch(url, options, DEFAULT_TIMEOUT).then(res => res.json())
}
export async function checkDiscordRole(address: string, gid: string, rid: string) {
let sign = createSign(address)
const data = {
gid,
address,
rid,
sign,
}
const url = `${process.env.OAUTH_SVR_URL}/activity/discord/role`
const options: any = {
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
body: JSON.stringify(data),
}
return timeoutFetch(url, options, DEFAULT_TIMEOUT).then(res => res.json())
}

View File

@ -91,7 +91,8 @@ export const rankKey = (activity: string, date?: Date) => {
return `${activity}:score:${dateTag}`
}
export const rankLevel = (rank: number) => {
export const rankLevel = (rank: number | string) => {
rank = parseInt(rank + '')
const data = rankLevels.find(o => rank >= o.rankMin && rank <= o.rankMax)
return data.level
return data ? data.level : '-'
}