Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .changeset/omnigraph-resolution-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
Changes related to **Omnigraph**:

- add `Domain.resolve { records, trace, acceleration, profile? }` for forward resolution driven by the GraphQL selection set
- add `Account.resolve { primaryName(by: ...), primaryNames(where: ...) }` for reverse (ENSIP-19 primary name) resolution with `@oneOf` inputs (`coinType`/`chain`, `coinTypes`/`chains`)
- add `Account.resolve { primaryName(by: ...), primaryNames(where: ...) }` for reverse (ENSIP-19 primary name) resolution with `@oneOf` inputs (`coinType`/`chainName`, `coinTypes`/`chainNames`)
- add `PrimaryNameRecord.resolve { records, ... }` for forward resolution of the resolved primary name
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { AccountId, Node } from "enssdk";
import type { AccountId, Hex, Node } from "enssdk";

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

Expand Down Expand Up @@ -50,7 +50,7 @@ function resolveOperationWithIndex(op: Operation, records: IndexedRecords): Oper
case "addr": {
const coinType = op.args[1];
const found = records?.addressRecords.find((r) => r.coinType === coinType);
return { ...op, result: found?.value ?? null };
return { ...op, result: (found?.value ?? null) as Hex | null };
Comment thread
sevenzing marked this conversation as resolved.
Comment thread
sevenzing marked this conversation as resolved.
}
case "text": {
const key = op.args[1];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ describe("makeRecordsResponse", () => {

it("writes resolved address records keyed by CoinType", () => {
const operations = [
{ functionName: "addr", args: [node, 60n], result: "0x123" },
{ functionName: "addr", args: [node, 1001n], result: "0x456" },
{ functionName: "addr", args: [node, 60n], result: "0x1234" as Hex },
{ functionName: "addr", args: [node, 1001n], result: "0x5678" as Hex },
] satisfies Operation[];
expect(makeRecordsResponse(operations)).toEqual({
addresses: { 60: "0x123", 1001: "0x456" },
addresses: { 60: "0x1234", 1001: "0x5678" },
});
});

Expand Down
2 changes: 1 addition & 1 deletion apps/ensapi/src/lib/resolution/operations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import type { ResolverRecordsSelection } from "@ensnode/ensnode-sdk";
*/
type OperationMap = {
name: { args: readonly [Node]; result: InterpretedName | null };
addr: { args: readonly [Node, bigint]; result: string | null };
addr: { args: readonly [Node, bigint]; result: Hex | null };
text: { args: readonly [Node, string]; result: string | null };
contenthash: { args: readonly [Node]; result: Hex | null };
pubkey: { args: readonly [Node]; result: { x: Hex; y: Hex } | null };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ const PrimaryNameByInputType = new GraphQLInputObjectType({
name: "PrimaryNameByInput",
fields: {
coinType: { type: GraphQLInt },
chain: { type: GraphQLString },
chainName: { type: GraphQLString },
},
});

const PrimaryNamesWhereInputType = new GraphQLInputObjectType({
name: "PrimaryNamesWhereInput",
fields: {
coinTypes: { type: new GraphQLList(GraphQLInt) },
chains: { type: new GraphQLList(GraphQLString) },
chainNames: { type: new GraphQLList(GraphQLString) },
},
});

Expand Down Expand Up @@ -85,9 +85,9 @@ describe("buildAccountPrimaryNamesSelection", () => {
expect(buildAccountPrimaryNamesSelection(info)).toEqual([60, 0]);
});

it("extracts coin type from primaryName(by: { chain: ETHEREUM })", () => {
it("extracts coin type from primaryName(by: { chainName: ETHEREUM })", () => {
const info = resolveInfoForAccountResolveSubselection(
'primaryName(by: { chain: "ETHEREUM" }) { name }',
'primaryName(by: { chainName: "ETHEREUM" }) { name }',
);
expect(buildAccountPrimaryNamesSelection(info)).toEqual([coinNameToTypeMap.eth]);
});
Expand All @@ -105,14 +105,13 @@ describe("buildAccountPrimaryNamesSelection", () => {
one: primaryName(by: { coinType: ${coinNameToTypeMap.btc} }) { name }
two: primaryName(by: { coinType: ${coinNameToTypeMap.ltc} }) { name }
three: primaryNames(where: { coinTypes: [${coinNameToTypeMap.doge}, ${coinNameToTypeMap.sol}] }) { name }
four: primaryNames(where: { chains: ["DEFAULT", "ETHEREUM", "ARBITRUM_ONE"] }) { name }
five: primaryName(by: { chain: "BASE" }) { name }
four: primaryNames(where: { chainNames: ["ETHEREUM", "ARBITRUM_ONE"] }) { name }
five: primaryName(by: { chainName: "BASE" }) { name }
`);

expect(buildAccountPrimaryNamesSelection(info)).toEqual([
coinNameToTypeMap.doge,
coinNameToTypeMap.sol,
coinNameToTypeMap.default,
coinNameToTypeMap.eth,
coinNameToTypeMap.arb1,
coinNameToTypeMap.btc,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import type { CoinType } from "enssdk";
* GraphQL `ChainName` enum values.
*/
export const CHAIN_NAME_ENTRIES = [
["default", "DEFAULT"],
["eth", "ETHEREUM"],
["base", "BASE"],
["op", "OPTIMISM"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import type {
*/
export const normalizePrimaryNameByInput = (by: PrimaryNameByInputValue): CoinType => {
if (by.coinType != null) return by.coinType;
if (by.chain != null) return chainNameToCoinType(by.chain);
if (by.chainName != null) return chainNameToCoinType(by.chainName);
// this should never happen as the schema with `@oneOf` prevents it
throw new Error("PrimaryNameByInput must specify exactly one of coinType or chain.");
throw new Error("PrimaryNameByInput must specify exactly one of coinType or chainName.");
};

/**
Expand All @@ -23,7 +23,7 @@ export const normalizeAccountPrimaryNamesWhereInput = (
where: PrimaryNamesWhereInputValue,
): CoinType[] => {
if (where.coinTypes != null) return where.coinTypes;
if (where.chains != null) return where.chains.map(chainNameToCoinType);
if (where.chainNames != null) return where.chainNames.map(chainNameToCoinType);
// this should never happen as the schema with `@oneOf` prevents it
throw new Error("PrimaryNamesWhereInput must specify exactly one of coinTypes or chains.");
throw new Error("PrimaryNamesWhereInput must specify exactly one of coinTypes or chainNames.");
};
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const toPrimaryNameRecord = (
): PrimaryNameRecordModel => ({
address,
coinType,
chain: coinTypeToChainName(coinType),
chainName: coinTypeToChainName(coinType),
name,
});

Expand Down
119 changes: 24 additions & 95 deletions apps/ensapi/src/omnigraph-api/schema/account.integration.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import {
DEFAULT_EVM_COIN_TYPE,
ETH_COIN_TYPE,
evmChainIdToCoinType,
type InterpretedName,
} from "enssdk";
import { ETH_COIN_TYPE, evmChainIdToCoinType, type Hex, type InterpretedName } from "enssdk";
import { base } from "viem/chains";
import { beforeAll, describe, expect, it } from "vitest";

Expand Down Expand Up @@ -329,10 +324,10 @@ describe("Account.primaryName and Account.primaryNames", () => {

type PrimaryNameRecordResult = {
coinType: number;
chain: string | null;
chainName: string | null;
name: CanonicalNameResult;
resolve?: {
records?: { addresses: Array<{ coinType: number; address: string | null }> } | null;
records?: { addresses: Array<{ coinType: number; address: Hex | null }> } | null;
} | null;
};

Expand Down Expand Up @@ -363,7 +358,7 @@ describe("Account.primaryName and Account.primaryNames", () => {
resolve {
primaryName(by: { coinType: $coinType }) {
coinType
chain
chainName
name { interpreted beautified }
}
}
Expand All @@ -375,37 +370,9 @@ describe("Account.primaryName and Account.primaryNames", () => {
query AccountPrimaryNameByChain($address: Address!) {
account(by: { address: $address }) {
resolve {
primaryName(by: { chain: ETHEREUM }) {
primaryName(by: { chainName: ETHEREUM }) {
coinType
chain
name { interpreted beautified }
}
}
}
}
`;

const AccountPrimaryNameByDefaultChain = gql`
query AccountPrimaryNameByDefaultChain($address: Address!) {
account(by: { address: $address }) {
resolve {
primaryName(by: { chain: DEFAULT }) {
coinType
chain
name { interpreted beautified }
}
}
}
}
`;

const AccountPrimaryNamesByDefaultChain = gql`
query AccountPrimaryNamesByDefaultChain($address: Address!) {
account(by: { address: $address }) {
resolve {
primaryNames(where: { chains: [DEFAULT] }) {
coinType
chain
chainName
name { interpreted beautified }
}
}
Expand All @@ -419,7 +386,7 @@ describe("Account.primaryName and Account.primaryNames", () => {
resolve {
primaryNames(where: { coinTypes: $coinTypes }) {
coinType
chain
chainName
name { interpreted beautified }
}
}
Expand All @@ -431,9 +398,9 @@ describe("Account.primaryName and Account.primaryNames", () => {
query AccountPrimaryNamesByChains($address: Address!) {
account(by: { address: $address }) {
resolve {
primaryNames(where: { chains: [ETHEREUM, BASE] }) {
primaryNames(where: { chainNames: [ETHEREUM, BASE] }) {
coinType
chain
chainName
name { interpreted beautified }
}
}
Expand All @@ -447,7 +414,7 @@ describe("Account.primaryName and Account.primaryNames", () => {
resolve {
primaryName(by: { coinType: 0 }) {
coinType
chain
chainName
name { interpreted beautified }
resolve {
records {
Expand Down Expand Up @@ -486,59 +453,21 @@ describe("Account.primaryName and Account.primaryNames", () => {
).resolves.toEqual({
account: {
resolve: {
primaryName: { coinType: ETH_COIN_TYPE, chain: "ETHEREUM", name: TEST_ETH_NAME },
primaryName: { coinType: ETH_COIN_TYPE, chainName: "ETHEREUM", name: TEST_ETH_NAME },
},
},
});
});

it("resolves the same primary name by chain as by coinType", async () => {
it("resolves the same primary name by chainName as by coinType", async () => {
await expect(
request<AccountPrimaryNameResult>(AccountPrimaryNameByChain, {
address: accounts.owner.address,
}),
).resolves.toEqual({
account: {
resolve: {
primaryName: { coinType: ETH_COIN_TYPE, chain: "ETHEREUM", name: TEST_ETH_NAME },
},
},
});
});

it("accepts DEFAULT and maps it to the ENSIP-19 default EVM coin type", async () => {
await expect(
request<AccountPrimaryNameResult>(AccountPrimaryNameByDefaultChain, {
address: accounts.owner.address,
}),
).resolves.toEqual({
account: {
resolve: {
primaryName: {
coinType: DEFAULT_EVM_COIN_TYPE,
chain: "DEFAULT",
name: null,
},
},
},
});
});

it("resolves primary names for DEFAULT", async () => {
await expect(
request<AccountPrimaryNamesResult>(AccountPrimaryNamesByDefaultChain, {
address: accounts.owner.address,
}),
).resolves.toEqual({
account: {
resolve: {
primaryNames: [
{
coinType: DEFAULT_EVM_COIN_TYPE,
chain: "DEFAULT",
name: null,
},
],
primaryName: { coinType: ETH_COIN_TYPE, chainName: "ETHEREUM", name: TEST_ETH_NAME },
},
},
});
Expand All @@ -553,7 +482,7 @@ describe("Account.primaryName and Account.primaryNames", () => {
).resolves.toEqual({
account: {
resolve: {
primaryName: { coinType: ETH_COIN_TYPE, chain: "ETHEREUM", name: null },
primaryName: { coinType: ETH_COIN_TYPE, chainName: "ETHEREUM", name: null },
},
},
});
Expand All @@ -569,15 +498,15 @@ describe("Account.primaryName and Account.primaryNames", () => {
account: {
resolve: {
primaryNames: [
{ coinType: ETH_COIN_TYPE, chain: "ETHEREUM", name: TEST_ETH_NAME },
{ coinType: BASE_COIN_TYPE, chain: "BASE", name: null },
{ coinType: ETH_COIN_TYPE, chainName: "ETHEREUM", name: TEST_ETH_NAME },
{ coinType: BASE_COIN_TYPE, chainName: "BASE", name: null },
],
},
},
});
});

it("resolves primary names for requested chains", async () => {
it("resolves primary names for requested chainNames", async () => {
await expect(
request<AccountPrimaryNamesResult>(AccountPrimaryNamesByChains, {
address: accounts.owner.address,
Expand All @@ -586,15 +515,15 @@ describe("Account.primaryName and Account.primaryNames", () => {
account: {
resolve: {
primaryNames: [
{ coinType: ETH_COIN_TYPE, chain: "ETHEREUM", name: TEST_ETH_NAME },
{ coinType: BASE_COIN_TYPE, chain: "BASE", name: null },
{ coinType: ETH_COIN_TYPE, chainName: "ETHEREUM", name: TEST_ETH_NAME },
{ coinType: BASE_COIN_TYPE, chainName: "BASE", name: null },
],
},
},
});
});

it("returns null name and chain for non-ENSIP-19 coin types", async () => {
it("returns null name and chainName for non-ENSIP-19 coin types", async () => {
await expect(
request<AccountPrimaryNameResult>(AccountPrimaryNameNonEnsip19, {
address: accounts.owner.address,
Expand All @@ -604,7 +533,7 @@ describe("Account.primaryName and Account.primaryNames", () => {
resolve: {
primaryName: {
coinType: 0,
chain: null,
chainName: null,
name: null,
resolve: {
records: null,
Expand Down Expand Up @@ -645,14 +574,14 @@ describe("Account.primaryName and Account.primaryNames", () => {
).rejects.toThrow();
});

it("rejects empty chains at GraphQL validation", async () => {
it("rejects empty chainNames at GraphQL validation", async () => {
await expect(
request(
gql`
query AccountPrimaryNamesEmptyChains($address: Address!) {
query AccountPrimaryNamesEmptyChainNames($address: Address!) {
account(by: { address: $address }) {
resolve {
primaryNames(where: { chains: [] }) { coinType }
primaryNames(where: { chainNames: [] }) { coinType }
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,6 @@ describe("Domain.records", () => {
version: string | null;
abi: { contentType: ContentType; data: string } | null;
interfaces: Array<{ interfaceId: string; implementer: string | null }>;
// address is string (not Hex/NormalizedAddress) because non-EVM records may use non-Ethereum formats; matches GraphQL String field
addresses: Array<{ coinType: CoinType; address: Hex | null }>;
texts: Array<{ key: string; value: string | null }>;
} | null;
Expand Down
Loading
Loading