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

Allow IPFS URLs to Contain Path Elements #12

Open
wants to merge 5 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 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 package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"version": "0.1.0",
"license": "GPL-3.0",
"devDependencies": {
"@graphprotocol/graph-cli": "0.71.1",
"@graphprotocol/graph-ts": "0.35.1",
"@graphprotocol/graph-cli": "^0.91.1",
"@graphprotocol/graph-ts": "^0.36.0",
Comment on lines +6 to +7
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated these trying to get the yarn deploy-local task to run.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This repo is using really old versions of graphprotocol as you can see, so lot of things might break as you upgrade since they often introduce some breaking changes

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If AssemblyScript doesn't support iterators, it seems like it never would have supported itertators. It'd be weird to remove support for something like that…

In any case, I replaced the iterators with a for loop & the AssemblyScript will now compile to WASM. I deployed the code to the Subgraph Studio & everything seems to work, if you'd like use it.

We're about to redeploy the contracts so the spurious entries will stop showing up in questchains.xyz.

"husky": "^9.0.11",
"lint-staged": "^15.2.2",
"mustache": "^4.2.0",
Expand Down Expand Up @@ -43,5 +43,6 @@
"*.{json,yml,yaml,md,ts}": [
"prettier --write"
]
}
},
"packageManager": "[email protected]+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}
2 changes: 1 addition & 1 deletion src/mappings/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export * from './constants';
export * from './ipfs';
export { Metadata } from './ipfs';
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Barrel files that don't use named exports will break tree-shaking in bundlers.

export * from './network';
export * from './strings';
export * from './schema';
Expand Down
111 changes: 54 additions & 57 deletions src/mappings/helpers/ipfs.ts
Original file line number Diff line number Diff line change
@@ -1,75 +1,72 @@
import { ipfs, json, log } from '@graphprotocol/graph-ts';
import { ipfs, json, JSONValue, log, TypedMap } from '@graphprotocol/graph-ts';

class Metadata {
name: string | null;
description: string | null;
imageUrl: string | null;
animationUrl: string | null;
externalUrl: string | null;
mimeType: string | null;
slug: string | null;
categories: string[] | null;
export class Metadata {
name: string | null = null;
description: string | null = null;
imageUrl: string | null = null;
animationUrl: string | null = null;
externalUrl: string | null = null;
mimeType: string | null = null;
slug: string | null = null;
categories: string[] | null = null;

constructor() {
this.name = null;
this.description = null;
this.imageUrl = null;
this.animationUrl = null;
this.externalUrl = null;
this.mimeType = null;
this.slug = null;
static from(detailsURL: string): Metadata {
const metadata = new Metadata();
const path = stripProtocol(detailsURL);
if (path != null) {
const data = fetchIPFSData(path);
if (data != null) {
metadata.name = getString(data, 'name');
metadata.description = getString(data, 'description');
metadata.imageUrl = getString(data, 'image_url');
metadata.animationUrl = getString(data, 'animation_url');
metadata.externalUrl = getString(data, 'external_url');
metadata.mimeType = getString(data, 'mime_type');
metadata.slug = getString(data, 'slug');
metadata.categories = getArray(data, 'categories');
}
}
return metadata;
}
}


function fetchHash(details: string): string {
let parts = details.split('/');
return parts.length > 0 ? parts[parts.length - 1] : '';
function stripProtocol(details: string): string {
if (details.toLowerCase().startsWith('ipfs://')) {
details = details.slice(7);
}
return details;
}

function fetchIpfsData(hash: string) {
let ipfsData = ipfs.cat(hash);
if (ipfsData) {
log.info('IPFS details from hash {}, data {}', [hash, ipfsData.toString()]);
return json.fromBytes(ipfsData).toObject();
} else {
log.warning('could not get IPFS details from hash {}', [hash]);
return null;
function fetchIPFSData(path: string): TypedMap<string, JSONValue> | null {
const raw = ipfs.cat(path);
if (raw) {
log.info(`Details from "${path}": ${raw}`, []);
return json.fromBytes(raw).toObject();
}
log.warning(`Couldn't get details from "${path}"`, []);
return null;
}

function assignMetadataValue(data: any, key: string): string | null {
let value = data.get(key);
function getString(
data: TypedMap<string, JSONValue>,
key: string,
): string | null {
const value = data.get(key);
return value != null && !value.isNull() ? value.toString() : null;
}

function assignMetadataCategories(data: any): string[] | null {
let categories = data.get('categories');
if (categories != null && !categories.isNull()) {
let categoryArray = categories.toArray();
return categoryArray.map(category => category.toString());
function getArray(
data: TypedMap<string, JSONValue>,
key: string,
): string[] | null {
const value = data.get(key);
if (value != null && !value.isNull()) {
const array = value.toArray();
return array.map<string>((elem: JSONValue) => elem.toString());
}
return null;
}

export function fetchMetadata(details: string): Metadata {
let metadata = new Metadata();
if (details == '') return metadata;

let hash = fetchHash(details);
if (hash != '') {
let data = fetchIpfsData(hash);
if (data) {
metadata.name = assignMetadataValue(data, 'name');
metadata.description = assignMetadataValue(data, 'description');
metadata.imageUrl = assignMetadataValue(data, 'image_url');
metadata.animationUrl = assignMetadataValue(data, 'animation_url');
metadata.externalUrl = assignMetadataValue(data, 'external_url');
metadata.mimeType = assignMetadataValue(data, 'mime_type');
metadata.slug = assignMetadataValue(data, 'slug');
metadata.categories = assignMetadataCategories(data);
}
}

return metadata;
function snakeToCamel(str: string): string {
return str.replace(/_([a-z])/gi, (_, char) => char.toUpperCase());
}
43 changes: 20 additions & 23 deletions src/mappings/helpers/network.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,24 @@
import { dataSource } from '@graphprotocol/graph-ts';
import { dataSource, TypedMap } from '@graphprotocol/graph-ts';

export function getNetwork(): string {
const network = dataSource.network();
const networkMap: { [key: string]: string } = {
'mainnet': '0x1',
'kovan': '0x2a',
'rinkeby': '0x4',
'ropsten': '0x3',
'goerli': '0x5',
'poa-core': '0x63',
'poa-sokol': '0x4d',
'gnosis': '0x64',
'matic': '0x89',
'mumbai': '0x13881',
'arbitrum-one': '0xa4b1',
'arbitrum-goerli': '0x66eed',
'optimism': '0xa',
'optimism-kovan': '0x45',
'aurora': '0x4e454152',
'aurora-testnet': '0x4e454153',
'sepolia': '0xaa36a7',
'holesky': '0x4268'
};

return networkMap[network] || 'unknown';
if (network === 'mainnet') return '0x1';
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It bitched alot about the eight different ways I tried to type the object literal & the AIs said they don't exist in AssemblyScript.

if (network === 'kovan') return '0x2a';
if (network === 'rinkeby') return '0x4';
if (network === 'ropsten') return '0x3';
if (network === 'goerli') return '0x5';
if (network === 'poa-core') return '0x63';
if (network === 'poa-sokol') return '0x4d';
if (network === 'gnosis') return '0x64';
if (network === 'matic') return '0x89';
if (network === 'mumbai') return '0x13881';
if (network === 'arbitrum-one') return '0xa4b1';
if (network === 'arbitrum-goerli') return '0x66eed';
if (network === 'optimism') return '0xa';
if (network === 'optimism-kovan') return '0x45';
if (network === 'aurora') return '0x4e454152';
if (network === 'aurora-testnet') return '0x4e454153';
if (network === 'sepolia') return '0xaa36a7';
if (network === 'holesky') return '0x4268';
return 'unknown';
}
4 changes: 2 additions & 2 deletions src/mappings/helpers/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
import { getNetwork } from './network';
import { ADDRESS_ZERO } from './constants';
import { createSearchString } from './strings';
import { fetchMetadata } from './ipfs';
import { Metadata } from './ipfs';

export function getUser(address: Address): User {
let user = User.load(address.toHexString());
Expand Down Expand Up @@ -101,7 +101,7 @@ export function createQuest(
): Quest {
let quest = getQuest(address, questIndex);

let metadata = fetchMetadata(details);
const metadata = Metadata.from(details);
quest.details = details;
quest.name = metadata.name;
quest.description = metadata.description;
Expand Down
14 changes: 7 additions & 7 deletions src/mappings/v0/questChain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
} from '../../types/templates/QuestChainV0/QuestChainV0';
import {
createSearchString,
fetchMetadata,
Metadata,
getUser,
removeFromArray,
updateQuestChainCompletions,
Expand All @@ -38,7 +38,7 @@ export function handleChainCreated(event: QuestChainCreatedEvent): void {
log.info('handleChainCreated {}', [event.address.toHexString()]);

let details = event.params.details;
let metadata = fetchMetadata(details);
const metadata = Metadata.from(details);
questChain.details = details;
questChain.name = metadata.name;
questChain.description = metadata.description;
Expand Down Expand Up @@ -83,7 +83,7 @@ export function handleChainEdited(event: QuestChainEditedEvent): void {
questChainEdit.save();

let details = event.params.details;
let metadata = fetchMetadata(details);
const metadata = Metadata.from(details);
questChain.paused = false;
questChain.details = details;
questChain.name = metadata.name;
Expand Down Expand Up @@ -193,7 +193,7 @@ export function handleQuestCreated(event: QuestCreatedEvent): void {
quest.questId = event.params.questId;

let details = event.params.details;
let metadata = fetchMetadata(details);
const metadata = Metadata.from(details);
quest.optional = false;
quest.skipReview = false;
quest.paused = false;
Expand Down Expand Up @@ -307,7 +307,7 @@ export function handleQuestEdited(event: QuestEditedEvent): void {
questEdit.save();

let details = event.params.details;
let metadata = fetchMetadata(details);
const metadata = Metadata.from(details);
quest.details = details;
quest.name = metadata.name;
quest.description = metadata.description;
Expand Down Expand Up @@ -399,7 +399,7 @@ export function handleQuestProofSubmitted(
.concat(event.logIndex.toHexString());
let proof = new ProofSubmission(proofId);
let details = event.params.proof;
let metadata = fetchMetadata(details);
const metadata = Metadata.from(details);
proof.details = details;
proof.name = metadata.name;
proof.description = metadata.description;
Expand Down Expand Up @@ -514,7 +514,7 @@ export function handleQuestProofReviewed(event: QuestProofReviewedEvent): void {
.concat(event.logIndex.toHexString());
let review = new ReviewSubmission(reviewId);
let details = event.params.details;
let metadata = fetchMetadata(details);
const metadata = Metadata.from(details);
review.details = details;
review.name = metadata.name;
review.description = metadata.description;
Expand Down
9 changes: 2 additions & 7 deletions src/mappings/v0/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,7 @@ import {
TransferSingle as TransferSingleEvent,
URI as URIEvent,
} from '../../types/templates/QuestChainTokenV0/QuestChainTokenV0';
import {
ADDRESS_ZERO,
removeFromArray,
fetchMetadata,
getUser,
} from '../helpers';
import { ADDRESS_ZERO, removeFromArray, Metadata, getUser } from '../helpers';
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prettier squished this line.


export function handleTransferSingle(event: TransferSingleEvent): void {
let tokenId = event.address
Expand Down Expand Up @@ -52,7 +47,7 @@ export function handleURIUpdated(event: URIEvent): void {
token.tokenAddress = event.address;

let details = event.params.value;
let metadata = fetchMetadata(details);
const metadata = Metadata.from(details);
token.details = details;
token.name = metadata.name;
token.description = metadata.description;
Expand Down
12 changes: 6 additions & 6 deletions src/mappings/v1/questChain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
import {
createQuest,
createSearchString,
fetchMetadata,
Metadata,
getQuest,
getQuestChain,
getUser,
Expand All @@ -37,7 +37,7 @@ export function handleChainInit(event: QuestChainInitEvent): void {
let questChain = getQuestChain(event.address);

let details = event.params.details;
let metadata = fetchMetadata(details);
const metadata = Metadata.from(details);
questChain.details = details;
questChain.name = metadata.name;
questChain.description = metadata.description;
Expand Down Expand Up @@ -100,7 +100,7 @@ export function handleChainEdited(event: QuestChainEditedEvent): void {
questChainEdit.save();

let details = event.params.details;
let metadata = fetchMetadata(details);
const metadata = Metadata.from(details);
questChain.details = details;
questChain.name = metadata.name;
questChain.description = metadata.description;
Expand Down Expand Up @@ -279,7 +279,7 @@ export function handleQuestsEdited(event: QuestsEditedEvent): void {
questEdit.editor = user.id;
questEdit.save();

let metadata = fetchMetadata(details);
const metadata = Metadata.from(details);
quest.details = details;
quest.name = metadata.name;
quest.description = metadata.description;
Expand Down Expand Up @@ -368,7 +368,7 @@ export function handleQuestProofsSubmitted(
.concat('-')
.concat(event.logIndex.toHexString());
let proof = new ProofSubmission(proofId);
let metadata = fetchMetadata(details);
const metadata = Metadata.from(details);
proof.details = details;
proof.name = metadata.name;
proof.description = metadata.description;
Expand Down Expand Up @@ -485,7 +485,7 @@ export function handleQuestProofsReviewed(
.concat('-')
.concat(event.logIndex.toHexString());
let review = new ReviewSubmission(reviewId);
let metadata = fetchMetadata(details);
const metadata = Metadata.from(details);
review.details = details;
review.name = metadata.name;
review.description = metadata.description;
Expand Down
9 changes: 2 additions & 7 deletions src/mappings/v1/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,7 @@ import {
TransferSingle as TransferSingleEvent,
URI as URIEvent,
} from '../../types/templates/QuestChainTokenV1/QuestChainTokenV1';
import {
ADDRESS_ZERO,
removeFromArray,
fetchMetadata,
getUser,
} from '../helpers';
import { ADDRESS_ZERO, removeFromArray, Metadata, getUser } from '../helpers';

export function handleTransferSingle(event: TransferSingleEvent): void {
let tokenId = event.address
Expand Down Expand Up @@ -52,7 +47,7 @@ export function handleURIUpdated(event: URIEvent): void {
token.tokenAddress = event.address;

let details = event.params.value;
let metadata = fetchMetadata(details);
const metadata = Metadata.from(details);
token.details = details;
token.name = metadata.name;
token.description = metadata.description;
Expand Down
Loading