From 5c285ebf99419e57eed65181cbf8e8ca4f2df79a Mon Sep 17 00:00:00 2001 From: Lucian Date: Mon, 13 Nov 2023 13:45:46 -0800 Subject: [PATCH] feat(platforms): updated GTC stake provider to use new API response (#1900) --- .../src/GtcStaking/Providers/GtcStaking.ts | 184 ++++-------------- .../GtcStaking/Providers/communityStaking.ts | 86 ++++++-- .../src/GtcStaking/Providers/selfStaking.ts | 40 ++-- .../__tests__/communityStaking.test.ts | 129 +++++++++--- .../GtcStaking/__tests__/selfStaking.test.ts | 41 +--- 5 files changed, 256 insertions(+), 224 deletions(-) diff --git a/platforms/src/GtcStaking/Providers/GtcStaking.ts b/platforms/src/GtcStaking/Providers/GtcStaking.ts index 1ec356b42b..db3ed22a83 100644 --- a/platforms/src/GtcStaking/Providers/GtcStaking.ts +++ b/platforms/src/GtcStaking/Providers/GtcStaking.ts @@ -1,5 +1,5 @@ // ----- Types -import { ProviderExternalVerificationError, type Provider } from "../../types"; +import { ProviderExternalVerificationError, ProviderInternalVerificationError, type Provider } from "../../types"; import type { ProviderContext, PROVIDER_ID, RequestPayload, VerifiedPayload } from "@gitcoin/passport-types"; // ----- Libs @@ -7,9 +7,6 @@ import axios from "axios"; import { handleProviderAxiosError } from "../../utils/handleProviderAxiosError"; import BigNumber from "bignumber.js"; -// ----- Utils -import { buildCID } from "../../utils/createCid"; - const gtcStakingEndpoint = `${process.env.PASSPORT_SCORER_BACKEND}registry/gtc-stake`; const apiKey = process.env.SCORER_API_KEY; @@ -31,11 +28,6 @@ export type Stake = { tx_hash: string; }; -type CommunityStakingCounts = { - bcs1gte5: number; - ecs2gte10: number; - tc5gte20: number; -}; export interface StakeResponse { data: { results: Stake[]; @@ -50,164 +42,66 @@ export type GtcStakingContext = ProviderContext & { export type GtcStakingProviderOptions = { type: PROVIDER_ID; - threshold?: BigNumber | number; - dataKey: keyof UserStake; - communityTypeCount?: number | undefined; - // Only needed for historic hashes, can be left - // off of any new providers - identifier?: string; + thresholdAmount: BigNumber; }; export class GtcStakingProvider implements Provider { type: PROVIDER_ID; - threshold?: BigNumber | number; - dataKey: keyof UserStake; - identifier: string; - communityTypeCount?: number | undefined; + thresholdAmount: BigNumber; // construct the provider instance with supplied options constructor(options: GtcStakingProviderOptions) { this.type = options.type; - this.threshold = options.threshold; - this.dataKey = options.dataKey; - this.identifier = options.identifier; - this.communityTypeCount = options.communityTypeCount; + this.thresholdAmount = options.thresholdAmount; } - // verify that the proof object contains valid === "true" - async verify(payload: RequestPayload, context: GtcStakingContext): Promise { - try { - const address = payload.address.toLowerCase(); - const errors: string[] = []; - let record = undefined, - valid = false, - stakeData; - - if (!address || address.substring(0, 2) !== "0x" || address.length !== 42) { - valid = false; - throw Error("Not a proper ethereum address"); - } - - try { - stakeData = await verifyStake(payload, context); - } catch (error: unknown) { - errors.push(String(error)); - } - - const selfStakeAmount = stakeData.selfStake; - const communityStakes = stakeData.communityStakes; - const commStakeCounts = await checkCommunityStakes(communityStakes, address); - - if (selfStakeAmount >= this.threshold) valid = true; - - for (const [key, val] of Object.entries(commStakeCounts)) { - if (val >= this.communityTypeCount && this.identifier === key) { - valid = true; - } - } - - if (valid) { - record = { - address: payload.address, - stakeAmount: this.identifier, - }; - } else if (!valid && selfStakeAmount < this.threshold) { - errors.push( - `Your current GTC self staking amount is ${selfStakeAmount.toString()} GTC, which is below the required ${this.threshold.toString()} GTC for this stamp.` - ); - } else { - errors.push( - "You are not staking enough on community members and/or community members are not staking enough on you 🥲" - ); - } - - return { - valid, - record, - errors, - }; - } catch (e: unknown) { - throw new ProviderExternalVerificationError(`${this.type} verifyStake: ${String(e)}.`); - } + verify(_payload: RequestPayload, _context: GtcStakingContext): Promise { + throw new Error("Method not implemented, this base class should not be used directly"); } -} - -async function checkCommunityStakes(communityStakes: Stake[], address: string): Promise { - const bcsMap = new Map(); - const ecsMap = new Map(); - const tcMap = new Map(); - const tcSet = new Set(); - - for (let i = 0; i < communityStakes.length; i++) { - const stake = communityStakes[i]; - const cid = await buildCID({ address: stake.address, staker: stake.staker, amount: stake.amount }); - const currentAmount = new BigNumber(stake.amount); - - if (stake.address === address || stake.staker === address) { - if (currentAmount.gte(5) && currentAmount.lt(10)) { - bcsMap.set(cid, (bcsMap.get(cid) || 0) + 1); - } - if (currentAmount.gte(10) && currentAmount.lt(20)) { - bcsMap.set(cid, (bcsMap.get(cid) || 0) + 1); - ecsMap.set(cid, (ecsMap.get(cid) || 0) + 1); - } - } - if (stake.address === address && currentAmount.gte(20)) { - if (!tcSet.has(stake.staker)) { - tcSet.add(stake.staker); - bcsMap.set(cid, (bcsMap.get(cid) || 0) + 1); - ecsMap.set(cid, (ecsMap.get(cid) || 0) + 1); - tcMap.set(cid, (tcMap.get(cid) || 0) + 1); - } + getAddress(payload: RequestPayload): string { + const address = payload.address.toLowerCase(); + if (!address || address.substring(0, 2) !== "0x" || address.length !== 42) { + throw new ProviderInternalVerificationError("Not a proper ethereum address"); } + return address; } - // Use the maps to find unpaired CIDs. - const bcs1gte5 = [...bcsMap].filter(([_, count]) => count === 1).map(([cid, _]) => cid).length; - const ecs2gte10 = [...ecsMap].filter(([_, count]) => count === 1).map(([cid, _]) => cid).length; - const tc5gte20 = [...tcMap].filter(([_, count]) => count === 1).map(([cid, _]) => cid).length; - - return { - bcs1gte5, - ecs2gte10, - tc5gte20, - }; -} - -async function verifyStake(payload: RequestPayload, context: GtcStakingContext): Promise { - try { - if (!context.gtcStaking?.userStake) { - const round = process.env.GTC_STAKING_ROUND || "1"; - const address = payload.address.toLowerCase(); + async getStakes(payload: RequestPayload, context: GtcStakingContext): Promise { + try { + if (!context.gtcStaking?.userStake) { + const round = process.env.GTC_STAKING_ROUND || "1"; + const address = payload.address.toLowerCase(); - const selfStakes: Stake[] = []; - const communityStakes: Stake[] = []; + const selfStakes: Stake[] = []; + const communityStakes: Stake[] = []; - const response: StakeResponse = await axios.get(`${gtcStakingEndpoint}/${address}/${round}`, { - headers: { Authorization: `Bearer ${apiKey}` }, - }); + const response: StakeResponse = await axios.get(`${gtcStakingEndpoint}/${address}/${round}`, { + headers: { Authorization: `Bearer ${apiKey}` }, + }); - const results: Stake[] = response.data.results; + const results: Stake[] = response?.data?.results; + if (!results) throw new ProviderExternalVerificationError("No results returned from the GTC Staking API"); - results.forEach((stake: Stake) => { - stake.event_type === "SelfStake" ? selfStakes.push(stake) : communityStakes.push(stake); - }); + results.forEach((stake: Stake) => { + stake.event_type === "SelfStake" ? selfStakes.push(stake) : communityStakes.push(stake); + }); - const selfStake: BigNumber = selfStakes.reduce((acc, curr) => { - if (curr.staked === true) { - return acc.plus(new BigNumber(curr.amount)); - } else { - return acc.minus(new BigNumber(curr.amount)); - } - }, new BigNumber(0)); + const selfStake: BigNumber = selfStakes.reduce((totalStake, currentStake) => { + if (currentStake.staked === true) { + return totalStake.plus(new BigNumber(currentStake.amount)); + } else { + return totalStake.minus(new BigNumber(currentStake.amount)); + } + }, new BigNumber(0)); - if (!context.gtcStaking) context.gtcStaking = {}; + if (!context.gtcStaking) context.gtcStaking = {}; - context.gtcStaking.userStake = { selfStake, communityStakes }; + context.gtcStaking.userStake = { selfStake, communityStakes }; + } + } catch (error) { + handleProviderAxiosError(error, "Verify GTC stake", [payload.address]); } - } catch (error) { - handleProviderAxiosError(error, "Verify GTC stake", [payload.address]); + return context.gtcStaking.userStake; } - return context.gtcStaking.userStake; } diff --git a/platforms/src/GtcStaking/Providers/communityStaking.ts b/platforms/src/GtcStaking/Providers/communityStaking.ts index f782c939f2..f15032ef03 100644 --- a/platforms/src/GtcStaking/Providers/communityStaking.ts +++ b/platforms/src/GtcStaking/Providers/communityStaking.ts @@ -1,19 +1,81 @@ -import { GtcStakingProvider, GtcStakingProviderOptions } from "./GtcStaking"; +import { RequestPayload, VerifiedPayload } from "@gitcoin/passport-types"; +import BigNumber from "bignumber.js"; +import { GtcStakingContext, GtcStakingProvider, GtcStakingProviderOptions, Stake } from "./GtcStaking"; class CommunityStakingBaseProvider extends GtcStakingProvider { - constructor(options: Omit) { - super({ - ...options, - dataKey: "communityStakes", - }); + minimumCountCommunityStakes: number; + + constructor(options: GtcStakingProviderOptions & { minimumCountCommunityStakes: number }) { + super(options); + this.minimumCountCommunityStakes = options.minimumCountCommunityStakes; + } + + async verify(payload: RequestPayload, context: GtcStakingContext): Promise { + const address = this.getAddress(payload); + const stakeData = await this.getStakes(payload, context); + const communityStakes = stakeData.communityStakes; + + const countRelevantStakes = this.getCountRelevantStakes(communityStakes, address); + + if (countRelevantStakes >= this.minimumCountCommunityStakes) { + return { + valid: true, + record: { address }, + }; + } else { + return { + valid: false, + errors: [ + `There are currently ${countRelevantStakes} 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`, + ], + }; + } + } + + getCountRelevantStakes(communityStakes: Stake[], address: string): number { + const stakesOnAddressByOthers: Record = {}; + const stakesByAddressOnOthers: Record = {}; + + for (let i = 0; i < communityStakes.length; i++) { + const stake = communityStakes[i]; + const stakeAmount = new BigNumber(stake.amount); + + if (stake.staker === address && stake.address !== address) { + stakesByAddressOnOthers[stake.address] ||= new BigNumber(0); + if (stake.staked) { + stakesByAddressOnOthers[stake.address] = stakesByAddressOnOthers[stake.address].plus(stakeAmount); + } else { + stakesByAddressOnOthers[stake.address] = stakesByAddressOnOthers[stake.address].sub(stakeAmount); + } + } else if (stake.address === address && stake.staker !== address) { + stakesOnAddressByOthers[stake.staker] ||= new BigNumber(0); + if (stake.staked) { + stakesOnAddressByOthers[stake.staker] = stakesOnAddressByOthers[stake.staker].plus(stakeAmount); + } else { + stakesOnAddressByOthers[stake.staker] = stakesOnAddressByOthers[stake.staker].sub(stakeAmount); + } + } + } + + 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 { constructor() { super({ type: "BeginnerCommunityStaker", - identifier: "bcs1gte5", - communityTypeCount: 1, + thresholdAmount: new BigNumber(5), + minimumCountCommunityStakes: 1, }); } } @@ -22,8 +84,8 @@ export class ExperiencedCommunityStakerProvider extends CommunityStakingBaseProv constructor() { super({ type: "ExperiencedCommunityStaker", - identifier: "ecs2gte10", - communityTypeCount: 2, + thresholdAmount: new BigNumber(10), + minimumCountCommunityStakes: 2, }); } } @@ -32,8 +94,8 @@ export class TrustedCitizenProvider extends CommunityStakingBaseProvider { constructor() { super({ type: "TrustedCitizen", - identifier: "tc5gte20", - communityTypeCount: 5, + thresholdAmount: new BigNumber(20), + minimumCountCommunityStakes: 5, }); } } diff --git a/platforms/src/GtcStaking/Providers/selfStaking.ts b/platforms/src/GtcStaking/Providers/selfStaking.ts index 60aa5f2cce..aa54743ee8 100644 --- a/platforms/src/GtcStaking/Providers/selfStaking.ts +++ b/platforms/src/GtcStaking/Providers/selfStaking.ts @@ -1,11 +1,30 @@ -import { GtcStakingProvider, GtcStakingProviderOptions } from "./GtcStaking"; +import { RequestPayload, VerifiedPayload } from "@gitcoin/passport-types"; +import BigNumber from "bignumber.js"; +import { GtcStakingContext, GtcStakingProvider, GtcStakingProviderOptions } from "./GtcStaking"; class SelfStakingBaseProvider extends GtcStakingProvider { - constructor(options: Omit) { - super({ - ...options, - dataKey: "selfStake", - }); + constructor(options: GtcStakingProviderOptions) { + super(options); + } + + async verify(payload: RequestPayload, context: GtcStakingContext): Promise { + const address = this.getAddress(payload); + const stakeData = await this.getStakes(payload, context); + const selfStakeAmount = stakeData.selfStake; + + if (selfStakeAmount.gte(this.thresholdAmount)) { + return { + valid: true, + record: { address }, + }; + } else { + return { + valid: false, + errors: [ + `Your current GTC self staking amount is ${selfStakeAmount.toString()} GTC, which is below the required ${this.thresholdAmount.toString()} GTC for this stamp.`, + ], + }; + } } } @@ -13,8 +32,7 @@ export class SelfStakingBronzeProvider extends SelfStakingBaseProvider { constructor() { super({ type: "SelfStakingBronze", - threshold: 5, - identifier: "ssgte5", + thresholdAmount: new BigNumber(5), }); } } @@ -23,8 +41,7 @@ export class SelfStakingSilverProvider extends SelfStakingBaseProvider { constructor() { super({ type: "SelfStakingSilver", - threshold: 20, - identifier: "ssgte20", + thresholdAmount: new BigNumber(20), }); } } @@ -33,8 +50,7 @@ export class SelfStakingGoldProvider extends SelfStakingBaseProvider { constructor() { super({ type: "SelfStakingGold", - threshold: 125, - identifier: "ssgte125", + thresholdAmount: new BigNumber(125), }); } } diff --git a/platforms/src/GtcStaking/__tests__/communityStaking.test.ts b/platforms/src/GtcStaking/__tests__/communityStaking.test.ts index 14ec4ac4e4..e6251c3437 100644 --- a/platforms/src/GtcStaking/__tests__/communityStaking.test.ts +++ b/platforms/src/GtcStaking/__tests__/communityStaking.test.ts @@ -124,9 +124,7 @@ describe("Attempt verification", function () { valid: true, record: { address: MOCK_ADDRESS_LOWER, - stakeAmount: "bcs1gte5", }, - errors: [], }); }); @@ -139,7 +137,7 @@ describe("Attempt verification", function () { } as unknown as RequestPayload, {} ); - }).rejects.toThrow("BeginnerCommunityStaker verifyStake: Error: Not a proper ethereum address."); + }).rejects.toThrow("Not a proper ethereum address"); }); it("handles invalid endpoint response", async () => { @@ -156,6 +154,55 @@ describe("Attempt verification", function () { ); }).rejects.toThrow(ProviderExternalVerificationError); }); + + it("should combine stakes where applicable", async () => { + jest.clearAllMocks(); + (axios.get as jest.Mock).mockImplementation(() => { + return Promise.resolve({ + data: { + results: [ + { + id: 1, + event_type: "Xstake", + round_id: 1, + staker: MOCK_ADDRESS_LOWER, + address: "0x6c5c1ce496c5164fef46c715c4a2d691bd9a1adb", + amount: "3", + staked: true, + block_number: 14124991, + tx_hash: "0x12345", + }, + { + id: 2, + event_type: "Xstake", + round_id: 1, + staker: MOCK_ADDRESS_LOWER, + address: "0x6c5c1ce496c5164fef46c715c4a2d691bd9a1adb", + amount: "3", + staked: true, + block_number: 14124992, + tx_hash: "0x12346", + }, + ], + }, + }); + }); + + const bcStaking = new BeginnerCommunityStakerProvider(); + const bcStakingPayload = await bcStaking.verify( + { + address: MOCK_ADDRESS_LOWER, + } as unknown as RequestPayload, + {} + ); + + expect(bcStakingPayload).toMatchObject({ + valid: true, + record: { + address: MOCK_ADDRESS_LOWER, + }, + }); + }); }); // All the negative case for thresholds are tested @@ -179,9 +226,8 @@ describe("should return invalid payload", function () { expect(bcStakingPayload).toMatchObject({ valid: false, - record: undefined, errors: [ - "You are not staking enough on community members and/or community members are not staking enough on you 🥲", + "There are currently 0 community stakes of at least 5 GTC on/by your address, you need a minimum of 1 relevant community stakes to claim this stamp", ], }); }); @@ -189,7 +235,7 @@ describe("should return invalid payload", function () { it("when a user is staking on 2 community members or is staked on by 2 community members below 10 GTC for ExperiencedCommunityStaker", async () => { jest.clearAllMocks(); (axios.get as jest.Mock).mockImplementation(() => { - return Promise.resolve(gtcStakingResponse("6", 1, "ExperiencedCommunityStaker")); + return Promise.resolve(gtcStakingResponse("4", 1, "ExperiencedCommunityStaker")); }); const ecsStaking = new ExperiencedCommunityStakerProvider(); @@ -202,16 +248,15 @@ describe("should return invalid payload", function () { expect(ecsStakingPayload).toMatchObject({ valid: false, - record: undefined, errors: [ - "You are not staking enough on community members and/or community members are not staking enough on you 🥲", + "There are currently 0 community stakes of at least 10 GTC on/by your address, you need a minimum of 2 relevant community stakes to claim this stamp", ], }); }); - it("when user is staked on by 5 community members with less that 20 GTC for TrustedCitizen", async () => { + it("when user is staked on by less than 5 community members with 20 GTC for TrustedCitizen", async () => { (axios.get as jest.Mock).mockImplementation(() => { - return Promise.resolve(gtcStakingResponse("18", 4, "TrustedCitizen")); + return Promise.resolve(gtcStakingResponse("25", 4, "TrustedCitizen")); }); const tcStaking = new TrustedCitizenProvider(); @@ -224,9 +269,57 @@ describe("should return invalid payload", function () { expect(tcStakingPayload).toMatchObject({ valid: false, - record: undefined, errors: [ - "You are not staking enough on community members and/or community members are not staking enough on you 🥲", + "There are currently 4 community stakes of at least 20 GTC on/by your address, you need a minimum of 5 relevant community stakes to claim this stamp", + ], + }); + }); + + it("should comprehend unstaking", async () => { + jest.clearAllMocks(); + (axios.get as jest.Mock).mockImplementation(() => { + return Promise.resolve({ + data: { + results: [ + { + id: 1, + event_type: "Xstake", + round_id: 1, + staker: MOCK_ADDRESS_LOWER, + address: "0x6c5c1ce496c5164fef46c715c4a2d691bd9a1adb", + amount: "10", + staked: true, + block_number: 14124991, + tx_hash: "0x12345", + }, + { + id: 2, + event_type: "Xstake", + round_id: 1, + staker: MOCK_ADDRESS_LOWER, + address: "0x6c5c1ce496c5164fef46c715c4a2d691bd9a1adb", + amount: "6", + staked: false, + block_number: 14124992, + tx_hash: "0x12346", + }, + ], + }, + }); + }); + + const bcStaking = new BeginnerCommunityStakerProvider(); + const bcStakingPayload = await bcStaking.verify( + { + address: MOCK_ADDRESS_LOWER, + } as unknown as RequestPayload, + {} + ); + + expect(bcStakingPayload).toMatchObject({ + valid: false, + errors: [ + "There are currently 0 community stakes of at least 5 GTC on/by your address, you need a minimum of 1 relevant community stakes to claim this stamp", ], }); }); @@ -255,9 +348,7 @@ describe("should return valid payload", function () { valid: true, record: { address: MOCK_ADDRESS_LOWER, - stakeAmount: "bcs1gte5", }, - errors: [], }); }); @@ -278,9 +369,7 @@ describe("should return valid payload", function () { valid: true, record: { address: MOCK_ADDRESS_LOWER, - stakeAmount: "ecs2gte10", }, - errors: [], }); }); @@ -301,9 +390,7 @@ describe("should return valid payload", function () { valid: true, record: { address: MOCK_ADDRESS_LOWER, - stakeAmount: "tc5gte20", }, - errors: [], }); }); // All values equal to tier amount @@ -324,9 +411,7 @@ describe("should return valid payload", function () { valid: true, record: { address: MOCK_ADDRESS_LOWER, - stakeAmount: "bcs1gte5", }, - errors: [], }); }); @@ -347,9 +432,7 @@ describe("should return valid payload", function () { valid: true, record: { address: MOCK_ADDRESS_LOWER, - stakeAmount: "ecs2gte10", }, - errors: [], }); }); @@ -370,9 +453,7 @@ describe("should return valid payload", function () { valid: true, record: { address: MOCK_ADDRESS_LOWER, - stakeAmount: "tc5gte20", }, - errors: [], }); }); }); diff --git a/platforms/src/GtcStaking/__tests__/selfStaking.test.ts b/platforms/src/GtcStaking/__tests__/selfStaking.test.ts index 961b705129..7865a5f329 100644 --- a/platforms/src/GtcStaking/__tests__/selfStaking.test.ts +++ b/platforms/src/GtcStaking/__tests__/selfStaking.test.ts @@ -65,9 +65,7 @@ describe("Attempt verification", function () { valid: true, record: { address: MOCK_ADDRESS_LOWER, - stakeAmount: "ssgte5", }, - errors: [], }); }); @@ -80,13 +78,11 @@ describe("Attempt verification", function () { } as unknown as RequestPayload, {} ); - }).rejects.toThrow( - new ProviderExternalVerificationError("SelfStakingBronze verifyStake: Error: Not a proper ethereum address.") - ); + }).rejects.toThrow(new ProviderExternalVerificationError("Not a proper ethereum address")); }); it("handles invalid subgraph response", async () => { - mockedAxios.get.mockRejectedValueOnce(invalidGtcStakingResponse); + mockedAxios.get.mockResolvedValueOnce(invalidGtcStakingResponse); const selfStakingProvider = new SelfStakingBronzeProvider(); await expect(async () => { @@ -96,13 +92,7 @@ describe("Attempt verification", function () { } as unknown as RequestPayload, {} ); - }).rejects.toThrow( - "SelfStakingBronze verifyStake: TypeError: Cannot read properties of undefined (reading 'selfStake')." - ); - // expect(axios.get).toHaveBeenCalledTimes(1); - // expect(mockedAxios.get).toBeCalledWith(`${gtcStakingEndpoint}/${MOCK_ADDRESS_LOWER}/${round}`, { - // headers: { Authorization: `Bearer ${apiKey}` }, - // }); + }).rejects.toThrow("No results returned from the GTC Staking API"); }); it("handles invalid verification attempt where an exception is thrown", async () => { @@ -117,9 +107,7 @@ describe("Attempt verification", function () { } as unknown as RequestPayload, {} ); - }).rejects.toThrow( - "SelfStakingBronze verifyStake: TypeError: Cannot read properties of undefined (reading 'selfStake')." - ); + }).rejects.toThrow("SelfStakingBronze verifyStake Error"); }); }); @@ -147,7 +135,6 @@ describe("should return invalid payload", function () { expect(selfstakingPayload).toMatchObject({ valid: false, - record: undefined, errors: [ `Your current GTC self staking amount is ${gtcStakeAmount} GTC, which is below the required 5 GTC for this stamp.`, ], @@ -172,7 +159,6 @@ describe("should return invalid payload", function () { expect(selfstakingPayload).toMatchObject({ valid: false, - record: undefined, errors: [ `Your current GTC self staking amount is ${gtcStakeAmount} GTC, which is below the required 20 GTC for this stamp.`, ], @@ -198,7 +184,6 @@ describe("should return invalid payload", function () { expect(selfstakingPayload).toMatchObject({ valid: false, - record: undefined, errors: [ `Your current GTC self staking amount is ${gtcStakeAmount} GTC, which is below the required 125 GTC for this stamp.`, ], @@ -224,8 +209,7 @@ describe("should return valid payload", function () { expect(selfstakingPayload).toMatchObject({ valid: true, - record: { address: MOCK_ADDRESS_LOWER, stakeAmount: "ssgte5" }, - errors: [], + record: { address: MOCK_ADDRESS_LOWER }, }); }); it("when stake amount above 20 GTC for Silver", async () => { @@ -244,8 +228,7 @@ describe("should return valid payload", function () { expect(selfstakingPayload).toMatchObject({ valid: true, - record: { address: MOCK_ADDRESS_LOWER, stakeAmount: "ssgte20" }, - errors: [], + record: { address: MOCK_ADDRESS_LOWER }, }); }); it("when stake amount above 125 GTC for Gold", async () => { @@ -264,8 +247,7 @@ describe("should return valid payload", function () { expect(selfstakingPayload).toMatchObject({ valid: true, - record: { address: MOCK_ADDRESS_LOWER, stakeAmount: "ssgte125" }, - errors: [], + record: { address: MOCK_ADDRESS_LOWER }, }); }); // All amounts equal to tier amount @@ -285,8 +267,7 @@ describe("should return valid payload", function () { expect(selfstakingPayload).toMatchObject({ valid: true, - record: { address: MOCK_ADDRESS_LOWER, stakeAmount: "ssgte5" }, - errors: [], + record: { address: MOCK_ADDRESS_LOWER }, }); }); it("when stake amount equal to 20 GTC for Silver", async () => { @@ -305,8 +286,7 @@ describe("should return valid payload", function () { expect(selfstakingPayload).toMatchObject({ valid: true, - record: { address: MOCK_ADDRESS_LOWER, stakeAmount: "ssgte20" }, - errors: [], + record: { address: MOCK_ADDRESS_LOWER }, }); }); it("when stake amount equal to 125 GTC for Gold", async () => { @@ -325,8 +305,7 @@ describe("should return valid payload", function () { expect(selfstakingPayload).toMatchObject({ valid: true, - record: { address: MOCK_ADDRESS_LOWER, stakeAmount: "ssgte125" }, - errors: [], + record: { address: MOCK_ADDRESS_LOWER }, }); }); });