Skip to content

Commit

Permalink
fix(ant): update types and add index for easy enforcement
Browse files Browse the repository at this point in the history
  • Loading branch information
dtfiedler authored and dtfiedler committed Feb 4, 2025
1 parent 249c005 commit 3dd6df5
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 21 deletions.
6 changes: 5 additions & 1 deletion src/types/ant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ export const AntRecordSchema = z.object({
priority: z.number().optional(),
});
export type AoANTRecord = z.infer<typeof AntRecordSchema>;
export type ANTRecords = Record<string, AoANTRecord>;
export type SortedANTRecord = AoANTRecord & { index: number };
export type SortedANTRecords = Record<string, SortedANTRecord>;

export const AntRecordsSchema = z.record(z.string(), AntRecordSchema);
export const AntControllersSchema = z.array(
ArweaveTxIdSchema.describe('Controller address'),
Expand Down Expand Up @@ -189,7 +193,7 @@ export interface AoANTRead {
{ undername }: { undername: string },
opts?: AntReadOptions,
): Promise<AoANTRecord | undefined>;
getRecords(opts?: AntReadOptions): Promise<Record<string, AoANTRecord>>;
getRecords(opts?: AntReadOptions): Promise<ANTRecords>;
getOwner(opts?: AntReadOptions): Promise<WalletAddress>;
getControllers(): Promise<WalletAddress[]>;
getTicker(opts?: AntReadOptions): Promise<string>;
Expand Down
52 changes: 41 additions & 11 deletions src/utils/ant.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,61 @@ describe('sortANTRecordsByPriority', () => {
undername2: { priority: 2, transactionId: 'test', ttlSeconds: 1 },
undername3: { priority: 3, transactionId: 'test', ttlSeconds: 1 }, // colliding priorities default to lexicographic sorting
undername4: { priority: 3, transactionId: 'test', ttlSeconds: 1 },
undername5: { priority: 100, transactionId: 'test', ttlSeconds: 1 },
undername5: { priority: 100, transactionId: 'test', ttlSeconds: 1 }, // priority does not represent the index or position of the record, just the order of resolution relative to other records
noPriority: { transactionId: 'test', ttlSeconds: 1 },
'@': { transactionId: 'test', ttlSeconds: 1 }, // always first, even if no priority
};
const sorted = sortedANTRecords(records);
assert.deepStrictEqual(sorted, {
'@': { transactionId: 'test', ttlSeconds: 1 }, // always first, even if no priority
undername1: { priority: 1, transactionId: 'test', ttlSeconds: 1 },
undername2: { priority: 2, transactionId: 'test', ttlSeconds: 1 },
undername3: { priority: 3, transactionId: 'test', ttlSeconds: 1 },
undername4: { priority: 3, transactionId: 'test', ttlSeconds: 1 },
undername5: { priority: 100, transactionId: 'test', ttlSeconds: 1 },
noPriority: { transactionId: 'test', ttlSeconds: 1 },
'@': { transactionId: 'test', ttlSeconds: 1, index: 0 }, // always first, even if no priority
undername1: {
priority: 1,
transactionId: 'test',
ttlSeconds: 1,
index: 1,
},
undername2: {
priority: 2,
transactionId: 'test',
ttlSeconds: 1,
index: 2,
},
undername3: {
priority: 3,
transactionId: 'test',
ttlSeconds: 1,
index: 3,
},
undername4: {
priority: 3,
transactionId: 'test',
ttlSeconds: 1,
index: 4,
},
undername5: {
priority: 100,
transactionId: 'test',
ttlSeconds: 1,
index: 5,
},
noPriority: { transactionId: 'test', ttlSeconds: 1, index: 6 },
});
});

it('should always return @ as the first record, regardless of priority', () => {
const records = {
'@': { priority: 5, transactionId: 'test', ttlSeconds: 1 },
'@': { priority: 5, transactionId: 'test', ttlSeconds: 1 }, // priorities set on '@' are ignored, they are always first
undername1: { priority: 2, transactionId: 'test', ttlSeconds: 1 },
};
const sorted = sortedANTRecords(records);
assert.deepStrictEqual(sorted, {
'@': { priority: 5, transactionId: 'test', ttlSeconds: 1 },
undername1: { priority: 2, transactionId: 'test', ttlSeconds: 1 },
'@': { priority: 5, transactionId: 'test', ttlSeconds: 1, index: 0 },
undername1: {
priority: 2,
transactionId: 'test',
ttlSeconds: 1,
index: 1,
},
});
});
});
22 changes: 13 additions & 9 deletions src/utils/ant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { AoANTRecord } from '../types/ant.js';
import { ANTRecords, SortedANTRecords } from '../types/ant.js';

/**
* Sorts ANT records by priority and then lexicographically.
*
* Note: javascript guarantees that the order of objects in an object is persistent. Still, adding index to each record is useful for enforcing against undername limits.
*
* @param antRecords - The ANT records to sort.
*/
export const sortedANTRecords = (
antRecords: Record<string, AoANTRecord>,
): Record<string, AoANTRecord> => {
return Object.fromEntries(
Object.entries(antRecords).sort(([a, aRecord], [b, bRecord]) => {
export const sortedANTRecords = (antRecords: ANTRecords): SortedANTRecords => {
const sortedEntries = Object.entries(antRecords).sort(
([a, aRecord], [b, bRecord]) => {
// '@' is the root name and should be resolved first
if (a === '@') {
return -1;
Expand All @@ -39,15 +39,19 @@ export const sortedANTRecords = (
if (!('priority' in aRecord) && 'priority' in bRecord) {
return 1;
}
// sort by priority if both records have a priority
// if both records have a priority, sort by priority and fallback to lexicographic sorting
if (aRecord.priority !== undefined && bRecord.priority !== undefined) {
if (aRecord.priority === bRecord.priority) {
return a.localeCompare(b);
}
return aRecord.priority - bRecord.priority;
}
// if the records have no priority, sort lexicographically
// all other records are sorted lexicographically
return a.localeCompare(b);
}),
},
);
// now that they are sorted, add the index to each record - this is their position in the sorted list and is used to enforce undername limits
return Object.fromEntries(
sortedEntries.map(([a, aRecord], index) => [a, { ...aRecord, index }]),
);
};

0 comments on commit 3dd6df5

Please sign in to comment.