Skip to content
This repository was archived by the owner on Aug 5, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from 4 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
7 changes: 4 additions & 3 deletions ensawards.org/data/awards/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import type { App } from "data/apps/types.ts";
import type { EntityMetadata } from "data/entity-metadata/types.ts";
import type { IncentiveProgram, IncentiveProgramSlug } from "data/incentive-programs/types.ts";
import type { Protocol } from "data/protocols/types.ts";
import { type EnsTokens } from "data/shared/ensTokens.ts";
import type { TransactionRef } from "data/shared/transactionRef.ts";
import type { AccountId, UnixTimestamp } from "enssdk";

import type { Price } from "@ensnode/ensnode-sdk";

export const AwardTypes = {
FinancialAward: "financial-award",
RecognitionAward: "recognition-award",
Expand Down Expand Up @@ -53,11 +54,11 @@ export interface AwardAbstract<AwardTypeT extends AwardType> {
export interface AwardRecognition extends AwardAbstract<typeof AwardTypes.RecognitionAward> {}

export interface AwardFinancial extends AwardAbstract<typeof AwardTypes.FinancialAward> {
/** Amount of the award in $ENS.
/** Financial award in any currency supported by {@link Price}.
*
* @invariant Award amount must be finite and greater than 0.
Comment thread
Y3drk marked this conversation as resolved.
*/
price: EnsTokens; // should be replaced with `Price` from enssdk when Issue#1941 is completed.
price: Price;

/**
* Details of the transaction associated with the distribution of this award.
Expand Down
49 changes: 36 additions & 13 deletions ensawards.org/data/awards/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,33 @@ import type { ChainId } from "enssdk";
import { zeroAddress, zeroHash } from "viem";
import { describe, expect, it } from "vitest";

import { CurrencyIds, parseEnsTokens, parseUsdc } from "@ensnode/ensnode-sdk";

import { type AwardFinancial, AwardTypes } from "./types.ts";
import { sortFinancialAwardsByPrice } from "./utils.ts";
import { isValidAwardValue, sortFinancialAwardsByPrice } from "./utils.ts";

describe("awards utils", () => {
describe("isValidAwardValue", () => {
it("should return true for valid award values", () => {
expect(
isValidAwardValue(parseUsdc("1000")),
"Should return true for positive finite amounts",
).toBe(true);
});

it("should return false for invalid award values", () => {
expect(isValidAwardValue(parseUsdc("0")), "Should return false for zero amount").toBe(false);

// Not possible to have negative amounts with parseX function (it will throw instead),
// but we want to ensure the validation function
// behaves correctly if it receives such input
expect(
isValidAwardValue({ currency: CurrencyIds.USDC, amount: -10n }),
"Should return false for negative amount",
).toBe(false);
});
});

describe("sortFinancialAwardsByPrice", () => {
const placeholderChainId: ChainId = 1;
it("should sort awards by award value in descending order", () => {
Expand All @@ -17,7 +40,7 @@ describe("awards utils", () => {
chainId: placeholderChainId,
address: zeroAddress,
},
price: 100,
price: parseEnsTokens("100"),
awardedAt: 1,
transaction: {
chainId: placeholderChainId,
Expand All @@ -31,7 +54,7 @@ describe("awards utils", () => {
chainId: placeholderChainId,
address: zeroAddress,
},
price: 200,
price: parseEnsTokens("200"),
awardedAt: 2,
transaction: {
chainId: placeholderChainId,
Expand All @@ -45,7 +68,7 @@ describe("awards utils", () => {
chainId: placeholderChainId,
address: zeroAddress,
},
price: 150,
price: parseEnsTokens("150"),
awardedAt: 0,
transaction: {
chainId: placeholderChainId,
Expand All @@ -62,7 +85,7 @@ describe("awards utils", () => {
chainId: placeholderChainId,
address: zeroAddress,
},
price: 200,
price: parseEnsTokens("200"),
awardedAt: 2,
transaction: {
chainId: placeholderChainId,
Expand All @@ -76,7 +99,7 @@ describe("awards utils", () => {
chainId: placeholderChainId,
address: zeroAddress,
},
price: 150,
price: parseEnsTokens("150"),
awardedAt: 0,
transaction: {
chainId: placeholderChainId,
Expand All @@ -90,7 +113,7 @@ describe("awards utils", () => {
chainId: placeholderChainId,
address: zeroAddress,
},
price: 100,
price: parseEnsTokens("100"),
awardedAt: 1,
transaction: {
chainId: placeholderChainId,
Expand All @@ -114,7 +137,7 @@ describe("awards utils", () => {
chainId: placeholderChainId,
address: zeroAddress,
},
price: 100,
price: parseEnsTokens("100"),
awardedAt: 2,
transaction: {
chainId: placeholderChainId,
Expand All @@ -128,7 +151,7 @@ describe("awards utils", () => {
chainId: placeholderChainId,
address: zeroAddress,
},
price: 100,
price: parseEnsTokens("100"),
awardedAt: 1,
transaction: {
chainId: placeholderChainId,
Expand All @@ -142,7 +165,7 @@ describe("awards utils", () => {
chainId: placeholderChainId,
address: zeroAddress,
},
price: 100,
price: parseEnsTokens("100"),
awardedAt: 3,
transaction: {
chainId: placeholderChainId,
Expand All @@ -159,7 +182,7 @@ describe("awards utils", () => {
chainId: placeholderChainId,
address: zeroAddress,
},
price: 100,
price: parseEnsTokens("100"),
awardedAt: 1,
transaction: {
chainId: placeholderChainId,
Expand All @@ -173,7 +196,7 @@ describe("awards utils", () => {
chainId: placeholderChainId,
address: zeroAddress,
},
price: 100,
price: parseEnsTokens("100"),
awardedAt: 2,
transaction: {
chainId: placeholderChainId,
Expand All @@ -187,7 +210,7 @@ describe("awards utils", () => {
chainId: placeholderChainId,
address: zeroAddress,
},
price: 100,
price: parseEnsTokens("100"),
awardedAt: 3,
transaction: {
chainId: placeholderChainId,
Expand Down
34 changes: 26 additions & 8 deletions ensawards.org/data/awards/utils.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,27 @@
import { type EnsTokens } from "data/shared/ensTokens";
import { interpretCurrency } from "data/shared/currencies";

import { type Award, type AwardFinancial } from "./types";
import type { Price } from "@ensnode/ensnode-sdk";

import { type AwardFinancial } from "./types";

/**
* Sorts {@link Award}s of {@link AwardFinancial} type.
* Sorts {@link AwardFinancial}s.
*
* Prioritizes awards with higher {@link AwardFinancial.price} and,
* Prioritizes awards with higher {@link AwardFinancial.price.amount} and,
* in case of a tie, earlier {@link AwardFinancial.awardedAt} date.
*/
export const sortFinancialAwardsByPrice = (a: AwardFinancial, b: AwardFinancial): number => {
if (a.price > b.price) return -1;
if (a.price < b.price) return 1;
if (a.price.currency !== b.price.currency) {
throw new Error(
`Cannot compare awards with \`price\` in different currencies: ${a.price.currency} vs ${b.price.currency}`,
);
Comment thread
Y3drk marked this conversation as resolved.
Comment thread
Y3drk marked this conversation as resolved.
}

const aPriceAmount = a.price.amount;
const bPriceAmount = b.price.amount;

if (aPriceAmount > bPriceAmount) return -1;
if (aPriceAmount < bPriceAmount) return 1;
Comment thread
Y3drk marked this conversation as resolved.
Outdated

return a.awardedAt - b.awardedAt;
};
Expand All @@ -19,6 +30,13 @@ export const sortFinancialAwardsByPrice = (a: AwardFinancial, b: AwardFinancial)
* Checks if a given award value is valid
* according to the invariants defined in {@link AwardFinancial}.
*/
export const isValidAwardValue = (awardValue: EnsTokens): boolean => {
return Number.isFinite(awardValue) && awardValue > 0;
export const isValidAwardValue = (awardPrice: Price): boolean => {
// Check the raw `Price.amount` value first
if (awardPrice.amount <= 0n) {
return false;
}

// Then check the user-facing interpreted value
const interpretedAmount = interpretCurrency(awardPrice);
return Number.isFinite(interpretedAmount) && interpretedAmount > 0;
};
Comment thread
Y3drk marked this conversation as resolved.
Comment thread
Y3drk marked this conversation as resolved.
Loading
Loading