Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Fetch Contributable Worlds #146

Merged
merged 2 commits into from
Oct 9, 2024
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
221 changes: 221 additions & 0 deletions packages/renderer/src/lib/ens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
const BATCH_SIZE = 1000;

export type Domain = { name: string };
export type DomainsQueryResult = { data: { domains: Domain[] } } | { errors: any };

export type OwnerByENSTuple = {
name: string;
wrappedOwner: {
id: string;
};
};
export type OwnerByENSQueryResult =
| {
data: {
domains: OwnerByENSTuple[];
};
}
| { errors: any };

export class ENS {
private subgraph = 'https://subgraph.decentraland.org/ens';
constructor(isDev: boolean) {
if (isDev) {
this.subgraph = 'https://subgraph.decentraland.org/ens-sepolia';
}
}

public async fetchNames(address: string) {
const response: Response = await fetch(this.subgraph, {
method: 'POST',
body: JSON.stringify({
query: `{
domains(
where: {or: [
{ wrappedOwner: "${address.toLowerCase()}" },
{ registrant: "${address.toLowerCase()}" }
]}
) {
name
}
}`,
}),
});

if (!response.ok) {
throw new Error(response.status.toString());
}

const queryResult: DomainsQueryResult = await response.json();

if ('errors' in queryResult) {
throw new Error(JSON.stringify(queryResult.errors));
}

return queryResult.data.domains.map(domain => domain.name);
}

public async fetchNamesOwners(domains: string[]): Promise<Record<string, string>> {
if (!domains) {
return {};
}

const response: Response = await fetch(this.subgraph, {
method: 'POST',
body: JSON.stringify({
query: `query getOwners($domains: [String]) {
domains(where: { name_in: $domains }) {
name
wrappedOwner {
id
}
}
}`,
variables: { domains },
}),
});

if (!response.ok) {
throw new Error(response.status.toString());
}

const queryResult: OwnerByENSQueryResult = await response.json();

if ('errors' in queryResult) {
throw new Error(JSON.stringify(queryResult.errors));
}

const results: Record<string, string> = {};
queryResult.data.domains.forEach(({ wrappedOwner, name }) => {
results[name] = wrappedOwner.id;
});
return results;
}
}

export type DCLDomainsQueryResult =
| { data: { nfts: { ens: { subdomain: string } }[] } }
| { errors: any };

export type DCLOwnerByNameTuple = {
owner: {
address: string;
};
ens: {
subdomain: string;
};
};
export type DCLOwnerByNameQueryResult = {
data: {
nfts: DCLOwnerByNameTuple[];
};
};

export class DCLNames {
private subgraph = 'https://subgraph.decentraland.org/marketplace';
constructor(isDev: boolean) {
if (isDev) {
this.subgraph = 'https://subgraph.decentraland.org/marketplace-sepolia';
}
}

public async fetchNames(address: string) {
let results: string[] = [];
let offset = 0;
let nextPage = true;

while (nextPage) {
const response: Response = await fetch(this.subgraph, {
method: 'POST',
body: JSON.stringify({
query: `{
nfts(
first: ${BATCH_SIZE},
skip: ${offset},
where: {
owner_: { id: "${address.toLowerCase()}" },
category: ens
}
) {
ens {
subdomain
}
}
}`,
}),
});

if (!response.ok) {
throw new Error(response.status.toString());
}

const queryResult: DCLDomainsQueryResult = await response.json();

if ('errors' in queryResult) {
throw new Error(JSON.stringify(queryResult.errors));
}
const domains: string[] = queryResult.data.nfts.map(
nft => `${nft.ens.subdomain.toString()}.dcl.eth`,
);
results = results.concat(domains);

if (domains.length === BATCH_SIZE) {
offset += BATCH_SIZE;
} else {
nextPage = false;
}
}

return results;
}

public async fetchNamesOwners(domains: string[]) {
if (!domains) {
return {};
}

const results: Record<string, string> = {};
let offset = 0;
let nextPage = true;

while (nextPage) {
const response: Response = await fetch(this.subgraph, {
method: 'POST',
body: JSON.stringify({
query: `query getOwners($domains: [String!], $offset: Int) {
nfts(first: ${BATCH_SIZE}, skip: $offset, where: { name_in: $domains, category: ens }) {
owner {
address
}
ens {
subdomain
}
}
}`,
variables: { domains, offset },
}),
});

if (!response.ok) {
throw new Error(response.status.toString());
}

const queryResult: DCLOwnerByNameQueryResult = await response.json();

if ('errors' in queryResult) {
throw new Error(JSON.stringify(queryResult.errors));
}
queryResult.data.nfts.forEach(({ ens, owner }) => {
results[ens.subdomain] = owner.address;
});

if (queryResult.data.nfts.length === BATCH_SIZE) {
offset += BATCH_SIZE;
} else {
nextPage = false;
}
}

return results;
}
}
27 changes: 17 additions & 10 deletions packages/renderer/src/lib/worlds.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import fetch from 'decentraland-crypto-fetch';
import type { AuthIdentity } from '@dcl/crypto';
import { localStorageGetIdentity } from '@dcl/single-sign-on-client';
import { DEPLOY_URLS } from '/shared/types/deploy';

import type { ContributableDomain } from '../modules/store/ens/types';
Expand Down Expand Up @@ -145,6 +146,14 @@ export class Worlds {
}
}

private withIdentity(address: string): AuthIdentity {
const identity = localStorageGetIdentity(address);
if (!identity) {
throw new Error('No identity found');
}
return identity;
}

public async fetchWorld(name: string) {
try {
const result = await fetch(`${this.url}/entities/active`, {
Expand Down Expand Up @@ -187,7 +196,7 @@ export class Worlds {
};

public postPermissionType = async (
identity: AuthIdentity,
address: string,
worldName: string,
worldPermissionNames: WorldPermissionNames,
worldPermissionType: WorldPermissionType,
Expand All @@ -199,48 +208,46 @@ export class Worlds {
metadata: {
type: worldPermissionType,
},
identity,
identity: this.withIdentity(address),
},
);
return result.status === 204;
};

public putPermissionType = async (
identity: AuthIdentity,
address: string,
worldName: string,
worldPermissionNames: WorldPermissionNames,
address: string,
) => {
const result = await fetch(
`${this.url}/world/${worldName}/permissions/${worldPermissionNames}/${address}`,
{
method: 'PUT',
identity,
identity: this.withIdentity(address),
},
);
return result.status === 204;
};

public deletePermissionType = async (
identity: AuthIdentity,
address: string,
worldName: string,
worldPermissionNames: WorldPermissionNames,
address: string,
) => {
const result = await fetch(
`${this.url}/world/${worldName}/permissions/${worldPermissionNames}/${address}`,
{
method: 'DELETE',
identity,
identity: this.withIdentity(address),
},
);
return result.status === 204;
};

public fetchContributableDomains = async (identity: AuthIdentity) => {
public fetchContributableDomains = async (address: string) => {
const result = await fetch(`${this.url}/wallet/contribute`, {
method: 'GET',
identity,
identity: this.withIdentity(address),
});

if (result.ok) {
Expand Down
Loading
Loading