Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ DISCORD_BOT_TOKEN=
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=

HUMAN_PASSPORT_API_URL=https://api.passport.xyz
HUMAN_PASSPORT_API_KEY=
HUMAN_PASSPORT_SCORER_ID=

# OmniProtocol TCP Server (optional - disabled by default)
OMNI_ENABLED=false
OMNI_PORT=3001
Expand Down
180 changes: 168 additions & 12 deletions src/features/incentive/PointSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@ const pointValues = {
LINK_DISCORD: 1,
LINK_UD_DOMAIN_DEMOS: 3,
LINK_UD_DOMAIN: 1,
LINK_HUMAN_PASSPORT: 1,
}

export class PointSystem {
private static instance: PointSystem

private constructor() {}
private constructor() { }

public static getInstance(): PointSystem {
if (!PointSystem.instance) {
Expand All @@ -45,6 +46,7 @@ export class PointSystem {
[network: string]: string[]
}
linkedNomis: NomisWalletIdentity[]
linkedHumanPassport: { address: string; score: number; passingScore: boolean }[]
}> {
const identities = await IdentityManager.getIdentities(userId)
const twitterIdentities = await IdentityManager.getWeb2Identities(
Expand Down Expand Up @@ -159,7 +161,14 @@ export class PointSystem {
}
}

return { linkedWallets, linkedSocials, linkedUDDomains, linkedNomis }
const humanPassportIdentities: any[] = (await IdentityManager.getIdentities(userId, "humanpassport")) || []
const linkedHumanPassport = humanPassportIdentities.map(hp => ({
address: hp.address,
score: hp.score || 0,
passingScore: hp.passingScore || false,
}))

return { linkedWallets, linkedSocials, linkedUDDomains, linkedNomis, linkedHumanPassport }
}

/**
Expand All @@ -175,7 +184,7 @@ export class PointSystem {
const gcrMainRepository = db.getDataSource().getRepository(GCRMain)
let account = await gcrMainRepository.findOneBy({ pubkey: userIdStr })

const { linkedWallets, linkedSocials, linkedUDDomains, linkedNomis } =
const { linkedWallets, linkedSocials, linkedUDDomains, linkedNomis, linkedHumanPassport } =
await this.getUserIdentitiesFromGCR(userIdStr)

if (!account) {
Expand Down Expand Up @@ -212,13 +221,15 @@ export class PointSystem {
},
udDomains: account.points.breakdown?.udDomains || {},
nomisScores: account.points.breakdown?.nomisScores || {},
humanPassport: account.points.breakdown?.humanPassport || 0,
referrals: account.points.breakdown?.referrals || 0,
demosFollow: account.points.breakdown?.demosFollow || 0,
},
linkedWallets,
linkedSocials,
linkedUDDomains,
linkedNomisIdentities: linkedNomis,
linkedHumanPassport,
lastUpdated: account.points.lastUpdated || new Date(),
flagged: account.flagged || null,
flaggedReason: account.flaggedReason || null,
Expand All @@ -236,7 +247,8 @@ export class PointSystem {
| "socialAccounts"
| "udDomains"
| "nomisScores"
| "demosFollow",
| "demosFollow"
| "humanPassport",
platform: string,
referralCode?: string,
twitterUserId?: string,
Expand Down Expand Up @@ -311,6 +323,15 @@ export class PointSystem {
)
appliedDelta = newDemosFollowPoints - oldDemosFollowPoints
account.points.breakdown.demosFollow = newDemosFollowPoints
} else if (type === "humanPassport") {
const oldHumanPassportPoints =
account.points.breakdown.humanPassport || 0
const newHumanPassportPoints = Math.max(
0,
oldHumanPassportPoints + points,
)
appliedDelta = newHumanPassportPoints - oldHumanPassportPoints
account.points.breakdown.humanPassport = newHumanPassportPoints
}

if (appliedDelta !== 0) {
Expand Down Expand Up @@ -466,8 +487,8 @@ export class PointSystem {
message: walletIsAlreadyLinked
? walletIsAlreadyLinkedMessage
: hasExistingWalletOnChain
? hasExistingWalletOnChainMessage
: "Points awarded for linking wallet",
? hasExistingWalletOnChainMessage
: "Points awarded for linking wallet",
},
require_reply: false,
extra: {},
Expand Down Expand Up @@ -1237,9 +1258,8 @@ export class PointSystem {
response: {
pointsAwarded: pointValue,
totalPoints: updatedPoints.totalPoints,
message: `Points awarded for linking ${
isDemosDomain ? ".demos" : "UD"
} domain`,
message: `Points awarded for linking ${isDemosDomain ? ".demos" : "UD"
} domain`,
},
require_reply: false,
extra: {},
Expand Down Expand Up @@ -1320,9 +1340,8 @@ export class PointSystem {
response: {
pointsDeducted: pointValue,
totalPoints: updatedPoints.totalPoints,
message: `Points deducted for unlinking ${
isDemosDomain ? ".demos" : "UD"
} domain`,
message: `Points deducted for unlinking ${isDemosDomain ? ".demos" : "UD"
} domain`,
},
require_reply: false,
extra: {},
Expand Down Expand Up @@ -1549,6 +1568,143 @@ export class PointSystem {
}
}

/**
* Award points for linking a Human Passport
* @param userId The user's Demos address
* @param referralCode Optional referral code
* @returns RPCResponse
*/
async awardHumanPassportPoints(
userId: string,
referralCode?: string,
): Promise<RPCResponse> {
try {
// Verify the Human Passport identity is actually linked
const account = await ensureGCRForUser(userId)
const hpIdentities = account.identities.humanpassport || []

if (hpIdentities.length === 0) {
return {
result: 400,
response: {
pointsAwarded: 0,
totalPoints: account.points.totalPoints || 0,
message: "Human Passport not linked",
},
require_reply: false,
extra: {},
}
}

const userPointsWithIdentities = await this.getUserPointsInternal(
userId,
)

// Check if user already has Human Passport points
if ((userPointsWithIdentities.breakdown.humanPassport || 0) > 0) {
return {
result: 200,
response: {
pointsAwarded: 0,
totalPoints: userPointsWithIdentities.totalPoints,
message: "Human Passport points already awarded",
},
require_reply: false,
extra: {},
}
}

await this.addPointsToGCR(
userId,
pointValues.LINK_HUMAN_PASSPORT,
"humanPassport",
"humanPassport",
referralCode,
)

const updatedPoints = await this.getUserPointsInternal(userId)

return {
result: 200,
response: {
pointsAwarded: pointValues.LINK_HUMAN_PASSPORT,
totalPoints: updatedPoints.totalPoints,
message: "Points awarded for linking Human Passport",
},
require_reply: false,
extra: {},
}
} catch (error) {
return {
result: 500,
response: "Error awarding Human Passport points",
require_reply: false,
extra: {
error:
error instanceof Error ? error.message : String(error),
},
}
}
}

/**
* Deduct points for unlinking a Human Passport
* @param userId The user's Demos address
* @returns RPCResponse
*/
async deductHumanPassportPoints(userId: string): Promise<RPCResponse> {
try {
const userPointsWithIdentities = await this.getUserPointsInternal(
userId,
)

const currentHumanPassport =
userPointsWithIdentities.breakdown.humanPassport || 0
if (currentHumanPassport <= 0) {
return {
result: 200,
response: {
pointsDeducted: 0,
totalPoints: userPointsWithIdentities.totalPoints,
message: "No Human Passport points to deduct",
},
require_reply: false,
extra: {},
}
}

await this.addPointsToGCR(
userId,
-pointValues.LINK_HUMAN_PASSPORT,
"humanPassport",
"humanPassport",
)

const updatedPoints = await this.getUserPointsInternal(userId)

return {
result: 200,
response: {
pointsDeducted: pointValues.LINK_HUMAN_PASSPORT,
totalPoints: updatedPoints.totalPoints,
message: "Points deducted for unlinking Human Passport",
},
require_reply: false,
extra: {},
}
} catch (error) {
return {
result: 500,
response: "Error deducting Human Passport points",
require_reply: false,
extra: {
error:
error instanceof Error ? error.message : String(error),
},
}
}
}

private getNomisPointsByScore(score: number): number {
const formattedScore = Number((score * 100).toFixed(0))
if (formattedScore >= 80) return 5
Expand Down
Loading