Skip to content
Open
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
97 changes: 97 additions & 0 deletions packages/dweb-api-resolver/src/nameservice/BasenamesService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { JsonRpcProvider } from "ethers";
import { ILoggerService } from "dweb-api-types/dist/logger";
import { IRequestContext } from "dweb-api-types/dist/request-context";
import { INameService } from "dweb-api-types/dist/name-service";
import { IConfigurationBase } from "dweb-api-types/dist/config";
import { getContentHashFallback } from "./utils.js";
import { namehash } from "ethers";

const L2_RESOLVER_ADDRESS = "0xC6d566A56A1aFf6508b41f6c90ff131615583BCD";
const L2_RESOLVER_ABI = [
"function contenthash(bytes32 node) view returns (bytes)",
];

export class BasenamesService implements INameService {
_configurationService: IConfigurationBase;
provider: JsonRpcProvider;
_logger: ILoggerService;

constructor(
configurationService: IConfigurationBase,
logger: ILoggerService,
) {
this._configurationService = configurationService;
const baseConfig = this._configurationService.getConfigBaseBackend();
const rpc = baseConfig.getBackend();

this.provider = new JsonRpcProvider(rpc, undefined, {
staticNetwork: true,
});
this._logger = logger;
}

async getContentHash(
request: IRequestContext,
name: string,
): Promise<string | null> {
this._logger.debug("BasenamesService: resolving contenthash", {
...request,
origin: "BasenamesService",
context: { name },
});

try {
const node = namehash(name);

this._logger.debug("BasenamesService: querying L2 resolver", {
...request,
origin: "BasenamesService",
context: { name, node, resolver: L2_RESOLVER_ADDRESS },
});

const { Contract } = await import("ethers");
const contract = new Contract(
L2_RESOLVER_ADDRESS,
L2_RESOLVER_ABI,
this.provider,
);

const contenthashBytes = await contract.contenthash(node);

if (!contenthashBytes || contenthashBytes === "0x") {
this._logger.debug("BasenamesService: no contenthash set", {
...request,
origin: "BasenamesService",
context: { name },
});
return null;
}

const decoded = getContentHashFallback(
request,
this._logger,
contenthashBytes,
name,
"BasenamesService",
);

this._logger.debug("BasenamesService: contenthash resolved", {
...request,
origin: "BasenamesService",
context: { name, contenthash: decoded },
});

return decoded;
} catch (error: any) {
this._logger.error("BasenamesService: error resolving contenthash", {
...request,
origin: "BasenamesService",
context: {
name,
error: error.message || error,
},
});
return null;
}
}
}
10 changes: 10 additions & 0 deletions packages/dweb-api-resolver/src/nameservice/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,18 @@ export class NameServiceFactory implements INameServiceFactory {
_logger: ILoggerService;
_ensService: INameService;
_web3NameSdkService: INameService;
_basenamesService: INameService | null;

constructor(
logger: ILoggerService,
ensService: INameService,
web3NameSdkService: INameService,
basenamesService?: INameService,
) {
this._logger = logger;
this._ensService = ensService;
this._web3NameSdkService = web3NameSdkService;
this._basenamesService = basenamesService || null;
}

getNameServiceForDomain(
Expand All @@ -33,6 +36,13 @@ export class NameServiceFactory implements INameServiceFactory {
});
return this._web3NameSdkService;
}
if (domain.endsWith(".base.eth") && this._basenamesService) {
this._logger.debug("Using BasenamesService for domain " + domain, {
...request,
origin: "NameServiceFactory",
});
return this._basenamesService;
}
this._logger.debug("Using EnsService for domain " + domain, {
...request,
origin: "NameServiceFactory",
Expand Down
24 changes: 24 additions & 0 deletions packages/dweb-api-server/src/configuration/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
IConfigurationEthereum,
IConfigurationEthereumFailover,
IConfigurationGnosis,
IConfigurationBase,
ICacheConfig,
IConfigurationIpfs,
IConfigurationServerAsk,
Expand Down Expand Up @@ -37,6 +38,9 @@ const configuration = {
gnosis: {
rpc: process.env.GNO_RPC_ENDPOINT || "https://rpc.gnosischain.com",
},
base: {
rpc: process.env.BASE_RPC_ENDPOINT || "https://mainnet.base.org",
},
// Storage backends
ipfs: {
backend: process.env.IPFS_TARGET || "http://localhost:8080",
Expand Down Expand Up @@ -195,6 +199,10 @@ export class TestConfigurationService implements ServerConfiguration {
return this.getServerConfiguration().getConfigGnosisBackend();
};

getConfigBaseBackend = () => {
return this.getServerConfiguration().getConfigBaseBackend();
};

getCacheConfig = () => {
return this.getServerConfiguration().getCacheConfig();
};
Expand Down Expand Up @@ -362,6 +370,20 @@ export const configurationToIConfigurationGnosis = (config: {
};
};

export const configurationToIConfigurationBase = (config: {
base: {
rpc: string;
};
}): IConfigurationBase => {
return {
getConfigBaseBackend: () => {
return {
getBackend: () => config.base.rpc,
};
},
};
};

export const configurationToICacheConfig = (config: {
cache: {
ttl: number;
Expand Down Expand Up @@ -547,6 +569,7 @@ export type ServerConfiguration = IConfigurationServerRouter &
IConfigurationEthereum &
IConfigurationEthereumFailover &
IConfigurationGnosis &
IConfigurationBase &
ICacheConfig &
IConfigurationServerDnsquery &
IDomainQueryConfig &
Expand All @@ -567,6 +590,7 @@ export const configurationToServerConfiguration = (
...configurationToIConfigurationEthereum(config),
...configurationToIConfigurationEthereumFailover(config),
...configurationToIConfigurationGnosis(config),
...configurationToIConfigurationBase(config),
...configurationToICacheConfig(config),
...configurationToIDomainQueryConfig(config),
...configurationToIRedisConfig(config),
Expand Down
10 changes: 10 additions & 0 deletions packages/dweb-api-server/src/dependencies/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import { NameServiceFactory } from "dweb-api-resolver/dist/nameservice/index";
import {} from "dweb-api-resolver/dist/resolver/index";
import { Web3NameSdkService } from "dweb-api-resolver/dist/nameservice/Web3NameSdkService";
import { EnsService } from "dweb-api-resolver/dist/nameservice/EnsService";
import { BasenamesService } from "dweb-api-resolver/dist/nameservice/BasenamesService";

export const createApplicationConfigurationBindingsManager = () => {
const configuration = new EnvironmentBinding<ServerConfiguration>({
Expand Down Expand Up @@ -167,6 +168,12 @@ export const createApplicationConfigurationBindingsManager = () => {
[EnvironmentConfiguration.Development]: (_env) => new TestResolverService(),
});

const basenamesService = new EnvironmentBinding<INameService>({
[EnvironmentConfiguration.Production]: (env) =>
new BasenamesService(configuration.getBinding(env), logger.getBinding(env)),
[EnvironmentConfiguration.Development]: (_env) => new TestResolverService(),
});

const domainRateLimit = new EnvironmentBinding<IDomainRateLimitService>({
[EnvironmentConfiguration.Production]: (env) =>
new DomainRateLimitService(
Expand All @@ -192,12 +199,14 @@ export const createApplicationConfigurationBindingsManager = () => {
logger.getBinding(env),
ensService.getBinding(env),
web3NameSdk.getBinding(env),
basenamesService.getBinding(env),
),
[EnvironmentConfiguration.Development]: (env) =>
new NameServiceFactory(
logger.getBinding(env),
ensService.getBinding(env),
web3NameSdk.getBinding(env),
basenamesService.getBinding(env),
),
});

Expand Down Expand Up @@ -249,6 +258,7 @@ export const createApplicationConfigurationBindingsManager = () => {
kuboApi,
web3NameSdk,
ensService,
basenamesService,
domainRateLimit,
arweaveResolver,
nameServiceFactory,
Expand Down
6 changes: 6 additions & 0 deletions packages/dweb-api-types/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ export interface IConfigurationGnosis {
};
}

export interface IConfigurationBase {
getConfigBaseBackend: () => {
getBackend: () => string;
};
}

export type IConfigurationLogger = {
getLoggerConfig: () => {
getLevel: () => "warn" | "error" | "info" | "debug";
Expand Down