Skip to content

Commit

Permalink
2310 id staking v2 (passportxyz#2346)
Browse files Browse the repository at this point in the history
* WIP id staking v2

* update self staking tests

* add V1 & V2 tests for self staking

* update tests

* feat(GtcStamp): fix yarn lint error (passportxyz#2310)

* feat(GtcStaking): remove comment (passportxyz#2310)

* feat(GtcStaking): unrelevant test - new API returs the current total (passportxyz#2310)

* feat(GtcStaking): simplify the count of relevant stakes for V2 (passportxyz#2310)

* feat(GtcStaking): improve selfstake logic (passportxyz#2310)

* feat(GtcStaking): simply selfstake logic & typo fix community staking (passportxyz#2310)

* feat(GtcStaking): update the gtc stake V2 response  (passportxyz#2310)

* feat(GtcStaking): update the gtc stake V2 response  (passportxyz#2310)

* feat(GtcStaking): update gtc stake tests(passportxyz#2310)
  • Loading branch information
larisa17 authored Mar 28, 2024
1 parent 545b1ed commit e53440e
Show file tree
Hide file tree
Showing 4 changed files with 1,135 additions and 109 deletions.
52 changes: 48 additions & 4 deletions platforms/src/GtcStaking/Providers/GtcStaking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import axios from "axios";
import { handleProviderAxiosError } from "../../utils/handleProviderAxiosError";
import BigNumber from "bignumber.js";

const gtcStakingEndpoint = `${process.env.PASSPORT_SCORER_BACKEND}registry/gtc-stake`;
export const gtcStakingEndpoint = `${process.env.PASSPORT_SCORER_BACKEND}registry/gtc-stake`;
export const gtcStakingEndpointV2 = `${process.env.PASSPORT_SCORER_BACKEND}stake/gtc`;
const apiKey = process.env.SCORER_API_KEY;

type UserStake = {
selfStake: BigNumber;
communityStakes: Stake[];
communityStakesV2: StakeV2[];
error?: string;
};

Expand All @@ -28,12 +30,29 @@ export type Stake = {
tx_hash: string;
};

export type StakeV2 = {
id: number;
chain: number;
lock_time: string;
unlock_time: string;
staker: string;
stakee: string;
amount: string;
};

export interface StakeResponse {
data: {
results: Stake[];
};
}

export interface StakeV2Response {
status: number;
data: {
items: StakeV2[];
};
}

export type GtcStakingContext = ProviderContext & {
gtcStaking?: {
userStake?: UserStake;
Expand Down Expand Up @@ -88,18 +107,42 @@ export class GtcStakingProvider implements Provider {
const round = this.getCurrentRound();
const address = payload.address.toLowerCase();

// Verify id staking legacy
const selfStakes: Stake[] = [];
const communityStakes: Stake[] = [];

const response: StakeResponse = await axios.get(`${gtcStakingEndpoint}/${address}/${round}`);
const results: Stake[] = response?.data?.results || [];

// Verify id staking V2
const selfStakesV2: StakeV2[] = [];
const communityStakesV2: StakeV2[] = [];

const responseV2: StakeV2Response = await axios.get(`${gtcStakingEndpointV2}/${address}`, {
headers: { Authorization: process.env.CGRANTS_API_TOKEN },
});
const resultsV2: StakeV2[] = responseV2?.data?.items || [];

const results: Stake[] = response?.data?.results;
if (!results) throw new ProviderExternalVerificationError("No results returned from the GTC Staking API");
if (results.length == 0 && resultsV2.length == 0)
throw new ProviderExternalVerificationError("No results returned from the GTC Staking API");

// V0
results.forEach((stake: Stake) => {
stake.event_type === "SelfStake" ? selfStakes.push(stake) : communityStakes.push(stake);
});

// V2
let selfStakeV2 = new BigNumber(0);
resultsV2.forEach((stake: StakeV2) => {
if (stake.staker == stake.stakee) {
if (new Date(stake.unlock_time) > new Date()) {
selfStakeV2 = new BigNumber(stake.amount);
}
} else {
communityStakesV2.push(stake);
}
});

const selfStake: BigNumber = selfStakes.reduce((totalStake, currentStake) => {
if (currentStake.staked === true) {
return totalStake.plus(new BigNumber(currentStake.amount));
Expand All @@ -110,7 +153,8 @@ export class GtcStakingProvider implements Provider {

if (!context.gtcStaking) context.gtcStaking = {};

context.gtcStaking.userStake = { selfStake, communityStakes };
const totalSelfStaked = selfStake.plus(selfStakeV2); // Return total from legacy self staked & V2 self staked
context.gtcStaking.userStake = { selfStake: totalSelfStaked, communityStakes, communityStakesV2 };
}
} catch (error) {
handleProviderAxiosError(error, "Verify GTC stake", [payload.address]);
Expand Down
37 changes: 33 additions & 4 deletions platforms/src/GtcStaking/Providers/communityStaking.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { RequestPayload, VerifiedPayload } from "@gitcoin/passport-types";
import BigNumber from "bignumber.js";
import { GtcStakingContext, GtcStakingProvider, GtcStakingProviderOptions, Stake } from "./GtcStaking";
import { GtcStakingContext, GtcStakingProvider, GtcStakingProviderOptions, Stake, StakeV2 } from "./GtcStaking";

class CommunityStakingBaseProvider extends GtcStakingProvider {
minimumCountCommunityStakes: number;
Expand All @@ -14,10 +14,12 @@ class CommunityStakingBaseProvider extends GtcStakingProvider {
const address = this.getAddress(payload);
const stakeData = await this.getStakes(payload, context);
const communityStakes = stakeData.communityStakes;
const communityStakesV2 = stakeData.communityStakesV2;

const countRelevantStakes = this.getCountRelevantStakes(communityStakes, address);

if (countRelevantStakes >= this.minimumCountCommunityStakes) {
const countRelevantStakesV2 = this.getCountRelevantStakesV2(communityStakesV2, address);
const totalCountRelevantStakes = countRelevantStakes + countRelevantStakesV2;
if (totalCountRelevantStakes >= this.minimumCountCommunityStakes) {
return {
valid: true,
record: { address },
Expand All @@ -26,7 +28,7 @@ class CommunityStakingBaseProvider extends GtcStakingProvider {
return {
valid: false,
errors: [
`There are currently ${countRelevantStakes} community stakes of at least ${this.thresholdAmount.toString()} GTC on/by your address, ` +
`There are currently ${totalCountRelevantStakes} community stakes of at least ${this.thresholdAmount.toString()} GTC on/by your address, ` +
`you need a minimum of ${this.minimumCountCommunityStakes} relevant community stakes to claim this stamp`,
],
};
Expand Down Expand Up @@ -68,6 +70,33 @@ class CommunityStakingBaseProvider extends GtcStakingProvider {
0
);
}

getCountRelevantStakesV2(communityStakes: StakeV2[], address: string): number {
const stakesOnAddressByOthers: Record<string, BigNumber> = {};
const stakesByAddressOnOthers: Record<string, BigNumber> = {};

communityStakes.forEach((stake) => {
// if stake is not expired
if (new Date(stake.unlock_time) > new Date()) {
if (stake.staker === address && stake.stakee !== address) {
stakesByAddressOnOthers[stake.stakee] = new BigNumber(stake.amount);
}
if (stake.staker !== address && stake.stakee === address) {
stakesOnAddressByOthers[stake.staker] = new BigNumber(stake.amount);
}
}
});

return [...Object.entries(stakesByAddressOnOthers), ...Object.entries(stakesOnAddressByOthers)].reduce(
(count, [_address, amount]) => {
if (amount.gte(this.thresholdAmount)) {
return count + 1;
}
return count;
},
0
);
}
}

export class BeginnerCommunityStakerProvider extends CommunityStakingBaseProvider {
Expand Down
Loading

0 comments on commit e53440e

Please sign in to comment.