Skip to content

Blobert Arises #121

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

Merged
merged 5 commits into from
Feb 22, 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
1 change: 1 addition & 0 deletions apps/nextjs/public/collections/blobert.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1,046 changes: 1,046 additions & 0 deletions apps/nextjs/src/abi/L2/Blobert.json

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions apps/nextjs/src/app/collection/CollectionsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ export default async function CollectionsList() {
link: "goldentoken",
image: "/collections/goldentoken.svg",
},
{
name: "Blobert",
link: "blobert",
image: "/collections/blobert.svg",
},
];

return (
Expand Down
134 changes: 97 additions & 37 deletions apps/nextjs/src/inngest/fetch_metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,20 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { NextResponse } from "next/server";
import { SUPPORTED_L2_CHAIN_ID } from "@/constants/env";
import { hash, shortString, uint256 } from "starknet";
import {
constants,
Contract,
hash,
RpcProvider,
shortString,
uint256,
} from "starknet";

import type { SQL } from "@realms-world/db";
import { Collections, getCollectionAddresses } from "@realms-world/constants";
import { and, db, eq, inArray, schema, sql } from "@realms-world/db";

import blobertABI from "../abi/L2/Blobert.json";
//import { Client } from "https://esm.sh/ts-postgres";

import { inngest } from "./client";
Expand All @@ -23,6 +31,7 @@ function eventKey(name: string) {

export const tokenURI = eventKey("tokenURI");
export const token_uri = eventKey("token_uri");
export const svg_image = eventKey("svg_image");

export const fetchMetadata = inngest.createFunction(
{
Expand All @@ -31,16 +40,18 @@ export const fetchMetadata = inngest.createFunction(
concurrency: {
limit: 3,
},
retries: 1,
},
{ event: "nft/mint" },
async ({ event, step }) => {
const metadata = await step.run("Fetch metadata", async () => {
const tokenId = uint256.bnToUint256(
BigInt(event.data.tokenId.toString()),
);
const fetchUrl = `https://starknet-${!process.env.NEXT_PUBLIC_IS_TESTNET || process.env.NEXT_PUBLIC_IS_TESTNET == "false" ? "mainnet" : "sepolia"}.blastapi.io/${process.env.NEXT_PUBLIC_BLAST_API}`;
const providerUrl = /*`https://starknet-${!process.env.NEXT_PUBLIC_IS_TESTNET || process.env.NEXT_PUBLIC_IS_TESTNET == "false" ? "mainnet" : "sepolia"}.blastapi.io/${process.env.NEXT_PUBLIC_BLAST_API}`;*/ `https://starknet-mainnet.blastapi.io/${process.env.NEXT_PUBLIC_BLAST_API}`;
const provider = new RpcProvider({
nodeUrl: providerUrl,
});
const tokenId = uint256.bnToUint256(BigInt(event.data.tokenId.toString()));

const response = await fetch(fetchUrl, {
const metadata = await step.run("Fetch metadata", async () => {
const response = await fetch(providerUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
Expand All @@ -50,14 +61,14 @@ export const fetchMetadata = inngest.createFunction(
method: "starknet_call",
params: [
{
contract_address: event.data.contract_address,
entry_point_selector:
event.data.contract_address ==
getCollectionAddresses(Collections.BEASTS)[
SUPPORTED_L2_CHAIN_ID
]!
? tokenURI
: token_uri, // Token URI
contract_address:
/*"0x00539f522b29ae9251dbf7443c7a950cf260372e69efab3710a11bf17a9599f1",*/ event
.data.contract_address,
entry_point_selector: getCollectionAddresses(Collections.BEASTS)[
SUPPORTED_L2_CHAIN_ID
]!
? tokenURI
: token_uri, // Token URI
calldata: [tokenId.low, tokenId.high],
},
"pending",
Expand All @@ -73,35 +84,86 @@ export const fetchMetadata = inngest.createFunction(
await step.sleep("fetch sleep", "20s");
throw new Error("Failed to fetch item from Blast API");
}

const value: any = [];
for (let i = 2; i < metadata.result.length; i++) {
const result = shortString.decodeShortString(metadata.result[i]);
value.push(result);
}
let jsonString: string;
let parsedJson: any = {};
const flattenedAttributes: Record<string, string> = {};

const jsonString = value.join("");
// eslint-disable-next-line no-control-regex
const regex = new RegExp("\\u0015", "g");
const modifiedJsonString = jsonString
.replace(
/"name":"(.*?)",/g,
(match: any, name: any) => `"name":"${name.replaceAll('"', '\\"')}",`,
)
.replace(regex, "");
if (
event.data.contract_address ==
getCollectionAddresses(Collections.BLOBERT)[SUPPORTED_L2_CHAIN_ID]!
) {
metadata.result.forEach((result: any) => {
value.push(shortString.decodeShortString(result));
});
value.push(metadata.result.pending_word);
jsonString = atob(value.join("").substring(29));
parsedJson = JSON.parse(jsonString);
parsedJson.attributes = parsedJson.attributes.map((attribute: any) => {
if (attribute.trait) {
return {
trait_type: attribute.trait,
value: attribute.value,
};
}
});
if (parsedJson.attributes[0] == undefined) {
parsedJson.attributes = [
{ trait_type: "Honorary", value: parsedJson.name },
];
}
//console.log(flattenedAttributes);

const parsedJson = JSON.parse(modifiedJsonString);
/*const blobertContract = new Contract(
blobertABI,
"0x00539f522b29ae9251dbf7443c7a950cf260372e69efab3710a11bf17a9599f1", //event.data.contract_address,
provider,
);

const flattenedAttributes: Record<string, string> = {};
const traitsResponse = await blobertContract.traits(tokenId);
console.log(traitsResponse);

if (parsedJson.attributes) {
for (const attribute of parsedJson.attributes) {
flattenedAttributes[attribute.trait_type] = attribute.value;
if (traitsResponse.variant.Custom != undefined) {
parsedJson.attributes.push({
trait_type: "Honorary",
value: traitsResponse.variant.Custom,
});
} else {
for (const [key, value] of Object.entries(
traitsResponse.variant.Regular,
)) {
parsedJson.attributes.push({
trait_type: Object.keys(value)[0],
value: value[Object.keys(value)[0]],
});
}
}*/
} else {
for (let i = 2; i < metadata.result.length; i++) {
const result = shortString.decodeShortString(metadata.result[i]);
value.push(result);
}
jsonString = value.join("");

// eslint-disable-next-line no-control-regex
const regex = new RegExp("\\u0015", "g");
jsonString = jsonString
.replace(
/"name":"(.*?)",/g,
(match: any, name: any) => `"name":"${name.replaceAll('"', '\\"')}",`,
)
.replace(regex, "");
//}
parsedJson = JSON.parse(jsonString);
if (parsedJson.attributes) {
for (const attribute of parsedJson.attributes) {
flattenedAttributes[attribute.trait_type] = attribute.value;
}
}
}

const dbRes = await step.run(
"Insert Beast Metadata to Postgres",
"Insert Token Metadata to Postgres",
async () => {
const tokenKey = event.data.contract_address + ":" + event.data.tokenId;
const query: { updatedId: string }[] = await db
Expand Down Expand Up @@ -236,8 +298,6 @@ export const fetchMetadata = inngest.createFunction(
});
}
}
console.log(addedTokenAttributes);
console.log(attributesCountMap);

tokenAttributeResult = await db
.insert(schema.erc721TokenAttributes)
Expand Down
8 changes: 5 additions & 3 deletions packages/apibara/local.env
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
STREAM_URL='https://mainnet.starknet.a5a.ch'
STREAM_URL='https://sepolia.starknet.a5a.ch'

BRIDGE_STARTING_BLOCK=88589
BRIDGE_CONTRACT='0x7c76a71952ce3acd1f953fd2a3fda8564408b821ff367041c89f44526076633'

ERC721_STARTING_BLOCK=366310
ERC721_STARTING_BLOCK=37300
BEASTS_CONTRACT='0x0158160018d590d93528995b340260e65aedd76d28a686e9daa5c4e8fad0c5dd'
GOLDEN_TOKEN_CONTRACT='0x04f5e296c805126637552cf3930e857f380e7c078e8f00696de4fc8545356b1d'
BLOBERT_CONTRACT='0x007075083c7f643a2009cf1dfa28dfec9366f7d374743c2e378e03c01e16c3af'

MARKET_CONTRACT=0x07bb44fd93f79c3529acc8b03fdd749f60e8ed4e0e57364eef6a971550cccae4
MARKET_STARTING_BLOCK=506960

INNGEST_EVENT_KEY='Zr3NCNM9fqzCgAO1Cf_BnQhoBKaf20wYYqfssY-_Zl7DzkSUACjYUJAt-nthVsgB7qmw1iiJcvBKm9ARMlPxVg'
POSTGRES_CONNECTION_STRING="postgres://RedBeardEth:[email protected]/production?sslmode=require"
POSTGRES_CONNECTION_STRING="postgres://RedBeardEth:[email protected]/sepolia?sslmode=require"
INNGEST_ENV='testnet'
4 changes: 3 additions & 1 deletion packages/apibara/mainnet.env
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ BRIDGE_STARTING_BLOCK=88589
BRIDGE_CONTRACT='0x7c76a71952ce3acd1f953fd2a3fda8564408b821ff367041c89f44526076633'

ERC721_STARTING_BLOCK=366310

BEASTS_CONTRACT='0x0158160018d590d93528995b340260e65aedd76d28a686e9daa5c4e8fad0c5dd'
GOLDEN_TOKEN_CONTRACT='0x04f5e296c805126637552cf3930e857f380e7c078e8f00696de4fc8545356b1d'
BLOBERT_CONTRACT='0x00539f522b29ae9251dbf7443c7a950cf260372e69efab3710a11bf17a9599f1'

MARKET_CONTRACT=0x07bb44fd93f79c3529acc8b03fdd749f60e8ed4e0e57364eef6a971550cccae4
MARKET_STARTING_BLOCK=506960

INNGEST_EVENT_KEY='Zr3NCNM9fqzCgAO1Cf_BnQhoBKaf20wYYqfssY-_Zl7DzkSUACjYUJAt-nthVsgB7qmw1iiJcvBKm9ARMlPxVg'
INNGEST_EVENT_KEY='Zr3NCNM9fqzCgAO1Cf_BnQhoBKaf20wYYqfssY-_Zl7DzkSUACjYUJAt-nthVsgB7qmw1iiJcvBKm9ARMlPxVg'
1 change: 1 addition & 0 deletions packages/apibara/sepolia.env
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ BRIDGE_CONTRACT='0x024f21982680442892d2f7ac4cee98c7d62708b04fdf9f8a0453415baca4b
ERC721_STARTING_BLOCK=17282
BEASTS_CONTRACT='0x03065c1db93be057c40fe92c9cba7f898de8d3622693d128e4e97fdc957808a3'
GOLDEN_TOKEN_CONTRACT='0x024f21982680442892d2f7ac4cee98c7d62708b04fdf9f8a0453415baca4b16f'
BLOBERT_CONTRACT='0x007075083c7f643a2009cf1dfa28dfec9366f7d374743c2e378e03c01e16c3af'

MARKET_CONTRACT=0x0297e088cd7777bebda7024e2dde81e9b745f41e5de0589c91de5caa885d9c32
MARKET_STARTING_BLOCK=17287
Expand Down
53 changes: 38 additions & 15 deletions packages/apibara/src/erc721_metadata.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Config } from "https://esm.sh/@apibara/indexer";
import type { Console } from "https://esm.sh/@apibara/indexer/sink/console";
import type { Webhook } from "https://esm.sh/@apibara/indexer/sink/webhook";
import type {
Block,
Expand Down Expand Up @@ -37,20 +38,42 @@ export default function transform({ header, events }: Block) {
}

function transferToTask(_header: BlockHeader, { event }: EventWithTransaction) {
const from = BigInt(event.data[0]);
if (from !== 0n) {
return [];
}
const tokenId = uint256
.uint256ToBN({ low: event.data[2], high: event.data[3] })
.toString();
return [
{
name: "nft/mint",
data: {
contract_address: event.fromAddress,
tokenId,
if (event.data) {
const from = BigInt(event.data[0]);
if (from !== 0n) {
return [];
}
const tokenId = uint256
.uint256ToBN({ low: event.data[2], high: event.data[3] })
.toString();
return [
{
name: "nft/mint",
data: {
contract_address: event.fromAddress,
tokenId,
},
},
},
];
];
} else if (event.keys?.length) {
const from = BigInt(event.keys[1]);
if (from !== 0n) {
return [];
}
const tokenId = uint256
.uint256ToBN({ low: event.keys[3], high: event.keys[4] })
.toString();

//if (tokenId == 6) {
return [
{
name: "nft/mint",
data: {
contract_address: event.fromAddress,
tokenId,
},
},
];
//}
}
}
12 changes: 6 additions & 6 deletions packages/apibara/src/erc721_tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,19 @@ export default function transform({ header, events }: Block) {
function transferToTask(_header: BlockHeader, { event }: EventWithTransaction) {
const isMainnet =
Deno.env.get("STREAM_URL") == "https://mainnet.starknet.a5a.ch";

switch (event.keys[0]) {
const isData = event.data != undefined;
switch (event.keys?.[0]) {
case TRANSFER_EVENT: {
const from = BigInt(isMainnet ? event.data[0] : event.keys[1]);
const from = BigInt(isData ? event.data[0] : event.keys[1]);
const token_id = parseInt(
uint256
.uint256ToBN({
low: isMainnet ? event.data[2] : event.keys[3],
high: isMainnet ? event.data[3] : event.keys[4],
low: isData ? event.data[2] : event.keys[3],
high: isData ? event.data[3] : event.keys[4],
})
.toString(),
);
const owner = isMainnet ? event.data[1] : event.keys[2];
const owner = isData ? event.data[1] : event.keys[2];

if (from == 0n) {
return {
Expand Down
1 change: 1 addition & 0 deletions packages/apibara/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { hash } from "https://esm.sh/starknet";
export const whitelistedContracts = [
Deno.env.get("GOLDEN_TOKEN_CONTRACT"),
Deno.env.get("BEASTS_CONTRACT"),
Deno.env.get("BLOBERT_CONTRACT"),
];

function eventKey(name: string) {
Expand Down
9 changes: 9 additions & 0 deletions packages/constants/src/Collections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export enum Collections {
REALMS = "realms",
BEASTS = "beasts",
GOLDEN_TOKEN = "goldentoken",
BLOBERT = "blobert",
}

export const CollectionAddresses: {
Expand All @@ -25,18 +26,26 @@ export const CollectionAddresses: {
[ChainId.SN_SEPOLIA]:
"0x024f21982680442892d2f7ac4cee98c7d62708b04fdf9f8a0453415baca4b16f",
},
[Collections.BLOBERT]: {
[ChainId.SN_MAIN]:
"0x00539f522b29ae9251dbf7443c7a950cf260372e69efab3710a11bf17a9599f1",
[ChainId.SN_SEPOLIA]:
"0x007075083c7f643a2009cf1dfa28dfec9366f7d374743c2e378e03c01e16c3af",
},
};
export const CollectionRoyalties: {
readonly [key in Collections]: number;
} = {
[Collections.REALMS]: 0,
[Collections.BEASTS]: 500,
[Collections.GOLDEN_TOKEN]: 500,
[Collections.BLOBERT]: 500,
};
export const CollectionDisplayName = {
[Collections.REALMS]: "Realms",
[Collections.BEASTS]: "Beasts",
[Collections.GOLDEN_TOKEN]: "Golden Token",
[Collections.BLOBERT]: "Blobert",
};

export function getCollectionAddresses(
Expand Down
4 changes: 3 additions & 1 deletion packages/constants/src/Marketplace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ import { ChainId } from "./Chains";
import { Collections } from "./Collections";

export interface MarketplaceCollectionIdsType {
[Collections.REALMS]?: number;
[Collections.BEASTS]: number;
[Collections.BLOBERT]: number;
[Collections.GOLDEN_TOKEN]: number;
[Collections.REALMS]?: number;
}

export const MarketplaceCollectionIds: MarketplaceCollectionIdsType = {
[Collections.BEASTS]: 2,
[Collections.BLOBERT]: 3,
[Collections.GOLDEN_TOKEN]: 1,
};

Expand Down