From 31364734ce036648ea11a06110147c63c88f1940 Mon Sep 17 00:00:00 2001 From: ciphrd Date: Thu, 5 Oct 2023 20:26:03 +0200 Subject: [PATCH 01/61] onchfs-js > added resource resolution when preparing inscriptions --- .../test-project-next/src/pages/index.tsx | 9 +- examples/test-project/.gitignore | 2 + examples/test-project/package.json | 1 + examples/test-project/src/index.ts | 44 +++-- .../tests/{heelo.js => _heelo.js} | 0 examples/test-project/tests/{hello => _hello} | 0 .../onchfs-js/src/inscriptions/prepare.ts | 164 ++++++++++++++++-- packages/onchfs-js/src/types/inscriptions.ts | 5 +- packages/onchfs-js/src/utils/uint8.ts | 9 + 9 files changed, 191 insertions(+), 43 deletions(-) create mode 100644 examples/test-project/.gitignore rename examples/test-project/tests/{heelo.js => _heelo.js} (100%) rename examples/test-project/tests/{hello => _hello} (100%) diff --git a/examples/test-project-next/src/pages/index.tsx b/examples/test-project-next/src/pages/index.tsx index eefd977..5e4496a 100644 --- a/examples/test-project-next/src/pages/index.tsx +++ b/examples/test-project-next/src/pages/index.tsx @@ -93,7 +93,7 @@ export default function Home() { const op = await kt.methodsObject .create_file({ chunk_pointers: ins.chunks.map(buf => uint8hex(buf)), - metadata: ins.metadata.map(buf => uint8hex(buf)), + metadata: uint8hex(ins.metadata), }) .send() await op.confirmation(1) @@ -110,16 +110,15 @@ export default function Home() { } const enc = new TextEncoder() - const inode = await Onchfs.prepareDirectory( + const inode = Onchfs.files.prepare( files.map(pt => { return { path: pt.name, content: enc.encode(pt.content), } - }), - 2048 + }) ) - const inscrs = Onchfs.generateInscriptions(inode) + const inscrs = Onchfs.inscriptions.prepare(inode) console.log(inscrs) for (const ins of inscrs) { await writeInscription(ins) diff --git a/examples/test-project/.gitignore b/examples/test-project/.gitignore new file mode 100644 index 0000000..db4c6d9 --- /dev/null +++ b/examples/test-project/.gitignore @@ -0,0 +1,2 @@ +dist +node_modules \ No newline at end of file diff --git a/examples/test-project/package.json b/examples/test-project/package.json index 6aff780..eb3814f 100644 --- a/examples/test-project/package.json +++ b/examples/test-project/package.json @@ -17,6 +17,7 @@ "@taquito/signer": "17.3.0", "@taquito/taquito": "17.3.0", "@types/node-dir": "0.0.34", + "axios": "1.5.1", "node-dir": "0.1.17", "onchfs": "workspace:*", "tslib": "2.6.0" diff --git a/examples/test-project/src/index.ts b/examples/test-project/src/index.ts index 5cfa46e..ff8e6c0 100644 --- a/examples/test-project/src/index.ts +++ b/examples/test-project/src/index.ts @@ -1,3 +1,4 @@ +import axios from "axios" import Onchfs, { Inscription } from "onchfs" import fs from "fs" import path from "path" @@ -5,6 +6,10 @@ import dir from "node-dir" import { TezosToolkit, ContractAbstraction } from "@taquito/taquito" import { InMemorySigner } from "@taquito/signer" +async function sleep(time: number) { + return new Promise(resolve => setTimeout(resolve, time)) +} + // test with any folder/file at the root of the tests folder const files = fs.readdirSync("./tests") @@ -14,7 +19,9 @@ tezos.setProvider({ "edskSA3GU5AdocLoJtsE2cvrxPAGPZc8RouYhCDaJJ95amCVipeUHiQXiDM37RnKZXed4bobudR8QHmA3cxHNgYDpS5ZcH5XJA" ), }) +const KT_CHUNKS = "KT1TGsvdj2m3JA3RmMGekRYHnK7Ygkje7Xbt" const KT_FILES = "KT1FA8AGGcJha6S6MqfBUiibwTaYhK8u7s9Q" +const TZKT = "https://api.ghostnet.tzkt.io/v1" const kts: Record> = {} const KT = async (add: string) => { @@ -29,8 +36,6 @@ function uint8hex(uint8: Uint8Array): string { } async function writeInscription(ins: Inscription) { - console.log(`Inscription of ${ins.type}`) - console.log(ins) if (ins.type === "chunk") { const kt = await KT(KT_FILES) const op = await kt.methods.write_chunk(uint8hex(ins.content)).send() @@ -70,16 +75,10 @@ async function main() { if (!fs.lstatSync(root).isDirectory()) { const content = fs.readFileSync(path.join("tests", f)) - const inode = Onchfs.files.prepare( - { - path: f, - content: content, - }, - { - chunkSize: 10, - } - ) - console.log(inode) + const inode = Onchfs.files.prepare({ + path: f, + content: content, + }) const inscrs = Onchfs.inscriptions.prepare(inode) } // is durectory @@ -87,7 +86,7 @@ async function main() { dir.files(root, async (err, files) => { if (err) throw err // for each file, get the content - const inode = await Onchfs.files.prepare( + const inode = Onchfs.files.prepare( files.map(pt => { const pts = pt.split("/").slice(2).join("/") return { @@ -99,11 +98,22 @@ async function main() { chunkSize: 2048, } ) - const inscrs = Onchfs.inscriptions.prepare(inode) + const inscrs = await Onchfs.inscriptions.prepare(inode, { + async inodeExists(cid) { + const res = await axios.get( + `${TZKT}/contracts/${KT_FILES}/bigmaps/inodes/keys/${cid}` + ) + return res.status === 200 + }, + async chunkExists(cid) { + const res = await axios.get(`${TZKT}/bigmaps/354463/keys/${cid}`) + return res.status === 200 + }, + }) console.log(inscrs) - for (const ins of inscrs) { - await writeInscription(ins) - } + // for (const ins of inscrs) { + // await writeInscription(ins) + // } }) } } diff --git a/examples/test-project/tests/heelo.js b/examples/test-project/tests/_heelo.js similarity index 100% rename from examples/test-project/tests/heelo.js rename to examples/test-project/tests/_heelo.js diff --git a/examples/test-project/tests/hello b/examples/test-project/tests/_hello similarity index 100% rename from examples/test-project/tests/hello rename to examples/test-project/tests/_hello diff --git a/packages/onchfs-js/src/inscriptions/prepare.ts b/packages/onchfs-js/src/inscriptions/prepare.ts index 1ccb026..626f8f0 100644 --- a/packages/onchfs-js/src/inscriptions/prepare.ts +++ b/packages/onchfs-js/src/inscriptions/prepare.ts @@ -1,5 +1,11 @@ -import { INode } from "@/types/files" +import { FileChunk, INode } from "@/types/files" import { Inscription } from "@/types/inscriptions" +import { u8hex } from "@/utils/uint8" + +interface InsPrepResolver { + inodeExists: (cid: string) => Promise + chunkExists: (cid: string) => Promise +} /** * Traverse the inverted tree starting by the root, creating inscriptions as @@ -36,38 +42,158 @@ import { Inscription } from "@/types/inscriptions" * * @returns A list of inscription objects ready to be turned into operations */ -export function prepareInscriptions(root: INode): Inscription[] { +export function prepareInscriptions(root: INode): Inscription[] + +/** + * Traverse the inverted tree starting by the root, creating inscriptions as + * it's being traversed. Before inscriptions are added to the final list, they + * are checked against the provided resolution function to see if they are + * already inscribed. If so, there is not need to inscribe them. At the end of + * the flow the inscriptions will be reversed to ensure they are written to the + * store in the right order (as the onchfs will reject inodes pointing to + * inexisting resources; let it be file chunks or directory files). + * + * @example + * + * ```ts + * // first prepare the file(s) + * const F = onchfs.files.prepare({ + * content: [], + * path: "index.html" + * }) + * // ot + * const F = onchfs.files.prepare([ + * { + * content: [], + * path: "index.htmml" + * }, + * { + * content: [], + * path: "lib/main.js" + * } + * ]) + * + * // the prepare the inscriptions + * const inscriptions = await onchfs.inscriptions.prepare(F, { + * async inodeExists(cid) { + * // just an example, implementation will vary depending use-cases + * // this should return true|false depending on the existance of the inode + * return indexer.onchfs.hasInode(cid) + * }, + * async chunkExists(cid) { + * return indexer.onchfs.chunkExists(cid) + * } + * }) + * ``` + * + * @param root The root of the tree, can be either the root directory or a file + * + * @returns A list of inscription objects ready to be turned into operations + */ +export function prepareInscriptions( + root: INode, + resolver: InsPrepResolver +): Promise + +export function prepareInscriptions( + root: INode, + resolver?: InsPrepResolver +): Inscription[] | Promise { const inscriptions: Inscription[] = [] - const traverse = (node: INode) => { + + // no async resolution of the inodes/chunk; assume insert all + if (typeof resolver === "undefined") { + const traverse = (node: INode) => { + if (node.type === "directory") { + inscriptions.push(createInscription(node)) + // recursively traverse each inode of the directory + for (const name in node.files) { + traverse(node.files[name]) + } + } else if (node.type === "file") { + // create the file inscription first as it will be reversed in the end, + // so the chunk inscriptions will appear first + inscriptions.push(createInscription(node)) + for (const chunk of node.chunks) { + inscriptions.push(createInscription(chunk)) + } + } + } + traverse(root) + return inscriptions.reverse() + } + // resolver is defined, return a promise and check for inscriptions existing + else { + return new Promise(async resolve => { + const traverse = async (node: INode) => { + // check if inode exists, if so we are done with this segment of graph + if (node.type === "directory" || node.type === "file") { + let found = false + try { + found = await resolver.inodeExists(u8hex(node.cid)) + } catch (e) {} + if (found) return + } + + if (node.type === "directory") { + inscriptions.push(createInscription(node)) + // recursively traverse each inode of the directory + for (const name in node.files) { + await traverse(node.files[name]) + } + } else if (node.type === "file") { + inscriptions.push(createInscription(node)) + + // check all the chunks at once, then filter those which need insert + const results = await Promise.allSettled( + node.chunks.map(chunk => resolver.chunkExists(u8hex(chunk.hash))) + ) + console.log(results) + const chunksToInsert = node.chunks.filter((_, i) => { + const res = results[i] + return res.status === "fulfilled" && !res.value + }) + + for (const chunk of chunksToInsert) { + inscriptions.push(createInscription(chunk)) + } + } + } + await traverse(root) + resolve(inscriptions.reverse()) + }) + } +} + +/** + * Creates an inscription matching with the given node/chunk. + * @param node The node, either an INode or a file chunk + * @returns The inscription corresponding to the node + */ +function createInscription(node: INode | FileChunk): Inscription { + if ("type" in node) { if (node.type === "directory") { - inscriptions.push({ + return { type: "directory", files: Object.fromEntries( Object.keys(node.files).map(name => [name, node.files[name].cid]) ), cid: node.cid, - }) - // recursively traverse each inode of the directory - for (const name in node.files) { - traverse(node.files[name]) } } else if (node.type === "file") { - // create the file inscription first as it will be reversed in the end, - // so the chunk inscriptions will appear first - inscriptions.push({ + return { type: "file", chunks: node.chunks.map(chk => chk.hash), metadata: node.metadata, cid: node.cid, - }) - for (const chunk of node.chunks) { - inscriptions.push({ - type: "chunk", - content: chunk.bytes, - }) } } + } else { + return { + type: "chunk", + content: node.bytes, + hash: node.hash, + } } - traverse(root) - return inscriptions.reverse() + throw new Error("Unknown node type") } diff --git a/packages/onchfs-js/src/types/inscriptions.ts b/packages/onchfs-js/src/types/inscriptions.ts index cd7b2c7..fe0b650 100644 --- a/packages/onchfs-js/src/types/inscriptions.ts +++ b/packages/onchfs-js/src/types/inscriptions.ts @@ -1,13 +1,14 @@ export type InscriptionChunk = { type: "chunk" content: DataEncoding + hash: DataEncoding } export type InscriptionFile = { type: "file" metadata: DataEncoding chunks: DataEncoding[] - cid: Uint8Array + cid: DataEncoding } export type InscriptionDirectory = { @@ -15,7 +16,7 @@ export type InscriptionDirectory = { files: { [name: string]: DataEncoding } - cid: Uint8Array + cid: DataEncoding } export type Inscription = diff --git a/packages/onchfs-js/src/utils/uint8.ts b/packages/onchfs-js/src/utils/uint8.ts index bfe63c2..79cef10 100644 --- a/packages/onchfs-js/src/utils/uint8.ts +++ b/packages/onchfs-js/src/utils/uint8.ts @@ -68,3 +68,12 @@ export function areUint8ArrayEqual(a: Uint8Array, b: Uint8Array): boolean { } return true } + +/** + * Outputs the hex string representation of the uint8array + * @param uint8 The uint8 array + * @returns The hex string representation of the uint8array + */ +export function u8hex(uint8: Uint8Array): string { + return [...uint8].map(x => x.toString(16).padStart(2, "0")).join("") +} From 5b4b560992f6929b216f94606f12b59708d0f222 Mon Sep 17 00:00:00 2001 From: ciphrd Date: Sat, 7 Oct 2023 00:50:34 +0200 Subject: [PATCH 02/61] [wip] onchfs uri parsing for caip-10 spec --- examples/http-proxy/src/index.ts | 8 +- packages/onchfs-js/src/config.ts | 21 ++- packages/onchfs-js/src/types/uri.ts | 12 +- packages/onchfs-js/src/uri/parse.ts | 34 ++-- .../onchfs-js/test/resolution/uri.test.ts | 177 ++++++++---------- 5 files changed, 113 insertions(+), 139 deletions(-) diff --git a/examples/http-proxy/src/index.ts b/examples/http-proxy/src/index.ts index 7549a07..6900eda 100644 --- a/examples/http-proxy/src/index.ts +++ b/examples/http-proxy/src/index.ts @@ -17,11 +17,13 @@ async function main() { // executed during the resolution flow const resolve = Onchfs.resolver.create([ { - blockchain: "tezos:ghostnet", + // ghostnet + blockchain: "tezos:NetXnHfVqm9iesp", rpcs: ["https://ghostnet.ecadinfra.com"], }, { - blockchain: "tezos:mainnet", + // mainnet + blockchain: "tezos:NetXdQprcVkpaWU", rpcs: [ "https://mainnet.ecadinfra.com", "https://mainnet.smartpy.io", @@ -37,7 +39,7 @@ async function main() { // (speed-up demo): this implements a cache retrieval based on the req.path, // which is rather trivial. As explained at the top of this file, in-memory - // cache isn't ideal, although east to implement + // cache isn't ideal, although easy to implement app.use(async (req, res, next) => { if (inMemoryCache[req.path]) { console.log(`⚑️ cache hit for ${req.path}`) diff --git a/packages/onchfs-js/src/config.ts b/packages/onchfs-js/src/config.ts index e42b495..489b981 100644 --- a/packages/onchfs-js/src/config.ts +++ b/packages/onchfs-js/src/config.ts @@ -13,11 +13,24 @@ export const INODE_BYTE_IDENTIFIER = { // improve storage being shared as much as possible depending on the use cases export const DEFAULT_CHUNK_SIZE = 16384 +export const CHAIN_IDS = { + tezos: { + mainnet: "NetXdQprcVkpaWU", + ghostnet: "NetXnHfVqm9iesp", + }, + eip155: { + mainnet: "1", + goerli: "5", + }, +} as const + // TODO: insert true values here. // A naive map of the "official" onchfs Smart Contracts. export const DEFAULT_CONTRACTS: Record = { - "tezos:mainnet": "KT1WvzYHCNBvDSdwafTHv7nJ1dWmZ8GCYuuC", - "tezos:ghostnet": "KT1FA8AGGcJha6S6MqfBUiibwTaYhK8u7s9Q", - "ethereum:1": "b0e58801d1b4d69179b7bc23fe54a37cee999b09", - "ethereum:5": "fcfdfa971803e1cc201f80d8e74de71fddea6551", + // tezos mainnet + "tezos:NetXdQprcVkpaWU": "KT1WvzYHCNBvDSdwafTHv7nJ1dWmZ8GCYuuC", + // tezos ghostnet + "tezos:NetXnHfVqm9iesp": "KT1FA8AGGcJha6S6MqfBUiibwTaYhK8u7s9Q", + "eip155:1": "b0e58801d1b4d69179b7bc23fe54a37cee999b09", + "eip155:5": "fcfdfa971803e1cc201f80d8e74de71fddea6551", } diff --git a/packages/onchfs-js/src/types/uri.ts b/packages/onchfs-js/src/types/uri.ts index ea27a82..79402a0 100644 --- a/packages/onchfs-js/src/types/uri.ts +++ b/packages/onchfs-js/src/types/uri.ts @@ -1,19 +1,21 @@ +import { CHAIN_IDS } from "@/config" + /** * List of the blockchain supported officially. While the protocol can be * deployed anywhere, the URI resolution is more easily inferred from the * supported deployments. */ -export const blockchainNames = ["tezos", "ethereum"] as const +export const blockchainNames = ["tezos", "eip155"] as const export type BlockchainNames = (typeof blockchainNames)[number] /** * Each blockchain has a list of supported networks */ export const blockchainNetworks = [ - "tezos:mainnet", - "tezos:ghostnet", - "ethereum:1", - "ethereum:5", + `tezos:${CHAIN_IDS.tezos.mainnet}`, + `tezos:${CHAIN_IDS.tezos.ghostnet}`, + `eip155:${CHAIN_IDS.eip155.mainnet}`, + `eip155:${CHAIN_IDS.eip155.goerli}`, ] as const export type BlockchainNetwork = (typeof blockchainNetworks)[number] diff --git a/packages/onchfs-js/src/uri/parse.ts b/packages/onchfs-js/src/uri/parse.ts index 0222b24..a8a9aad 100644 --- a/packages/onchfs-js/src/uri/parse.ts +++ b/packages/onchfs-js/src/uri/parse.ts @@ -2,7 +2,7 @@ * Proper charsets tightly following the spec */ -import { DEFAULT_CONTRACTS } from "@/config" +import { CHAIN_IDS, DEFAULT_CONTRACTS } from "@/config" import { BlockchainNames, URIAuthority, @@ -120,7 +120,10 @@ export function parseSchema(uri: string): string { export function parseSchemaSpecificPart( uriPart: string ): URISchemaSpecificParts { - const authorityReg = `([${AUTHORITY_CHARSET}]*)\\/` + // CAIP-2 Blockchain ID Specification: https://chainagnostic.org/CAIPs/caip-2 + // CAIP-10: Account ID Specification: https://chainagnostic.org/CAIPs/caip-10 + const authorityReg = `([-a-z0-9]{3,8}(?::[-_a-zA-Z0-9]{1,32}(?::[-.%a-zA-Z0-9]{1,128})?)?)\\/` + const cidReg = `[${HEX_CHARSET}]{64}` const pathReg = `${SEG_CHARSET}*(?:\\/${SEG_CHARSET}*)*` const queryReg = `\\?(${QUERY_CHARSET}*)` @@ -154,26 +157,18 @@ export function parseSchemaSpecificPart( const blockchainAuthorityParsers: Record RegExp> = { tezos: () => new RegExp( - `^(?:(KT(?:1|2|3|4)[${B58_CHARSET}]{33})\\.)?(tezos|tez|xtz)(?::(ghostnet|mainnet))?$` + `^(tezos)(?::(?:(Net[${B58_CHARSET}]{12}))(?::(KT(?:1|2|3|4)[${B58_CHARSET}]{33}))?)?$` ), - ethereum: () => - new RegExp(`^(?:([${HEX_CHARSET}]{40})\\.)?(ethereum|eth)(?::([0-9]+))?$`), -} - -type BlockchainNameVariants = { - [K in BlockchainNames]: [K, ...string[]] -} -const blockchainNameVariants: BlockchainNameVariants = { - tezos: ["tezos", "tez", "xtz"], - ethereum: ["ethereum", "eth"], + eip155: () => + new RegExp(`^(eip155)(?::([0-9]{1,})(?::([${HEX_CHARSET}]{40}))?)?$`), } type BlockchainDefaultNetwork = { [K in BlockchainNames]: string } const blockchainDefaultNetwork: BlockchainDefaultNetwork = { - tezos: "mainnet", - ethereum: "1", + tezos: CHAIN_IDS.tezos.mainnet, + eip155: CHAIN_IDS.eip155.mainnet, } /** @@ -221,7 +216,7 @@ export function parseAuthority( // no result; move to next blockchain if (!res) continue // results are in slots [1;3] - assign to temp object being parsed - const [contract, blockchainName, blockchainId] = res.splice(1, 3) + const [blockchainName, blockchainId, contract] = res.splice(1, 3) contract && (tmp.contract = contract) blockchainName && (tmp.blockchainName = blockchainName) blockchainId && (tmp.blockchainId = blockchainId) @@ -236,13 +231,6 @@ export function parseAuthority( "the blockchain could not be inferred when parsing the URI, if the URI doesn't have an authority segment (onchfs:////...), a context should be provided based on where the URI was observed. The blockchain needs to be resolved either through the URI or using the context." ) } - // normalize blockchain name into its cleanest and most comprehensible form - for (const [name, values] of Object.entries(blockchainNameVariants)) { - if (values.includes(tmp.blockchainName)) { - tmp.blockchainName = name - break - } - } // if blockchain ID is missing, then assign the default blockchain ID // associated with the asset, which is mainnet diff --git a/packages/onchfs-js/test/resolution/uri.test.ts b/packages/onchfs-js/test/resolution/uri.test.ts index 133b50b..68192c5 100644 --- a/packages/onchfs-js/test/resolution/uri.test.ts +++ b/packages/onchfs-js/test/resolution/uri.test.ts @@ -12,6 +12,9 @@ import { } from "../../src/types/uri" import { DEFAULT_CONTRACTS } from "../../src/config" +const TEZOS_MAIN_ID = "NetXdQprcVkpaWU" +const TEZOS_GHOST_ID = "NetXnHfVqm9iesp" + const CHARSETS = (() => { const LOW_ALPHA = "abcdefghijklmnopqrstuvwxyz" const HI_ALPHA = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" @@ -192,7 +195,8 @@ describe("fragment segment MUST only accept certain characters", () => { describe("tezos pattern constrains", () => { const KT_BASE = (a: string, c: string) => `KT${a}WvzYHCNBvDSdwafTHv7nJ1dWmZ8GCYuu${c}` - const BASE = (a: string, c: string) => `${KT_BASE(a, c)}.tezos` + const BASE = (a: string, c: string) => + `tezos:${TEZOS_MAIN_ID}:${KT_BASE(a, c)}` test("sanity check with known valid address", () => { expect(parseAuthority(BASE("1", "a"))).toHaveProperty( @@ -240,12 +244,12 @@ describe("parse URI", () => { expect(() => parseURI("./some/relative/path.txt")).toThrow() expect(() => parseURI( - "abonchfs://ethereum:5/6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840" + "abonchfs://eip155:5/6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840" ) ).toThrow() expect(() => parseURI( - "onchfs://ethereum:5/6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840Β°de" + "onchfs://eip155:5/6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840Β°de" ) ).toThrow() }) @@ -256,13 +260,13 @@ describe("parse URI", () => { { uri: "onchfs://6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840", context: { - blockchainName: "ethereum", + blockchainName: "eip155", }, output: { cid: "6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840", authority: { contract: "b0e58801d1b4d69179b7bc23fe54a37cee999b09", - blockchainName: "ethereum", + blockchainName: "eip155", blockchainId: "1", }, }, @@ -270,61 +274,61 @@ describe("parse URI", () => { { uri: "onchfs://6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840/folder/index.html", context: { - blockchainName: "ethereum", + blockchainName: "eip155", }, output: { cid: "6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840", authority: { contract: "b0e58801d1b4d69179b7bc23fe54a37cee999b09", - blockchainName: "ethereum", + blockchainName: "eip155", blockchainId: "1", }, path: "folder/index.html", }, }, { - uri: "onchfs://ethereum:5/6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840", + uri: "onchfs://eip155:5/6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840", output: { cid: "6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840", authority: { contract: "fcfdfa971803e1cc201f80d8e74de71fddea6551", - blockchainName: "ethereum", + blockchainName: "eip155", blockchainId: "5", }, }, }, { - uri: "onchfs://68b75b4e8439a7099e53045bea850b3266e95906.eth/6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840", + uri: "onchfs://eip155:1:68b75b4e8439a7099e53045bea850b3266e95906/6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840", output: { cid: "6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840", authority: { contract: "68b75b4e8439a7099e53045bea850b3266e95906", - blockchainName: "ethereum", + blockchainName: "eip155", blockchainId: "1", }, }, }, { - uri: "onchfs://KT1WvzYHCNBvDSdwafTHv7nJ1dWmZ8GCYuuC.tezos/6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840", + uri: "onchfs://tezos:NetXdQprcVkpaWU:KT1WvzYHCNBvDSdwafTHv7nJ1dWmZ8GCYuuC/6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840", output: { cid: "6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840", authority: { contract: "KT1WvzYHCNBvDSdwafTHv7nJ1dWmZ8GCYuuC", blockchainName: "tezos", - blockchainId: "mainnet", + blockchainId: "NetXdQprcVkpaWU", }, }, }, { uri: "onchfs://6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840/folder/index.html?param1=4¶m2=heyheyhey#a-fragment", context: { - blockchainName: "ethereum", + blockchainName: "eip155", }, output: { cid: "6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840", authority: { contract: "b0e58801d1b4d69179b7bc23fe54a37cee999b09", - blockchainName: "ethereum", + blockchainName: "eip155", blockchainId: "1", }, path: "folder/index.html", @@ -342,7 +346,7 @@ describe("parse URI", () => { authority: { contract: "KT1WvzYHCNBvDSdwafTHv7nJ1dWmZ8GCYuuC", blockchainName: "tezos", - blockchainId: "mainnet", + blockchainId: "NetXdQprcVkpaWU", }, query: "a-param=1234", }, @@ -414,11 +418,11 @@ describe("parse URI", () => { it("should normalize hexadecimal points (lower/upper)-case", () => { expect( parseURI( - "onchfs://ethereum:5/6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840" + "onchfs://eip155/6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840" ) ).toEqual( parseURI( - "onchfs://ethereum:5/6db0ff44176C6f1E9f471DC0c3f15194827D1129af94628a3a753c747f726840" + "onchfs://eip155/6db0ff44176C6f1E9f471DC0c3f15194827D1129af94628a3a753c747f726840" ) ) }) @@ -428,10 +432,10 @@ describe("parseSchema", () => { it("should capture 2 groups for valid URIs", () => { expect( parseSchema( - "onchfs://ethereum:5/6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840" + "onchfs://eip155/6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840" ) ).toEqual( - "ethereum:5/6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840" + "eip155/6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840" ) expect( parseSchema( @@ -506,24 +510,25 @@ describe("parse schema-specific components", () => { }, }, { - uri: "ethereum:5/6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840", + uri: "eip155:5/6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840", output: { cid: "6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840", - authority: "ethereum:5", + authority: "eip155:5", }, }, { - uri: "68b75b4e8439a7099e53045bea850b3266e95906.eth/6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840", + uri: "eip155:342-45e:68b75b4e8439a7099e53045bea850b3266e95906/6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840", output: { cid: "6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840", - authority: "68b75b4e8439a7099e53045bea850b3266e95906.eth", + authority: "eip155:342-45e:68b75b4e8439a7099e53045bea850b3266e95906", }, }, { - uri: "KT1WvzYHCNBvDSdwafTHv7nJ1dWmZ8GCYuuC.tezos/6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840", + // weird chain id "bleblebleble" + uri: "tezos:blebleble:KT1WvzYHCNBvDSdwafTHv7nJ1dWmZ8GCYuuC/6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840", output: { cid: "6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840", - authority: "KT1WvzYHCNBvDSdwafTHv7nJ1dWmZ8GCYuuC.tezos", + authority: "tezos:blebleble:KT1WvzYHCNBvDSdwafTHv7nJ1dWmZ8GCYuuC", }, }, { @@ -545,24 +550,25 @@ describe("parse schema-specific components", () => { it("should still parse semi-invalid authority segment", () => { const set: { uri: string; output: URISchemaSpecificParts }[] = [ { - uri: "aaaaaaaaaaaaaaaaaa:5/6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840", + uri: "aaaaaaa:5/6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840", output: { cid: "6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840", - authority: "aaaaaaaaaaaaaaaaaa:5", + authority: "aaaaaaa:5", }, }, { - uri: "68b75b4e8439a7099e53045bea850b3266e959.eth/6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840", + // eth address has 38 characters + uri: "eip155:1:68b75b4e8439a7099e53045bea850b3266e959/6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840", output: { cid: "6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840", - authority: "68b75b4e8439a7099e53045bea850b3266e959.eth", + authority: "eip155:1:68b75b4e8439a7099e53045bea850b3266e959", }, }, { - uri: "KT1Wvz.tezos/6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840", + uri: "tezos:NetXdQprcVkpaWU:KT1Wvz/6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840", output: { cid: "6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840", - authority: "KT1Wvz.tezos", + authority: "tezos:NetXdQprcVkpaWU:KT1Wvz", }, }, ] @@ -632,83 +638,43 @@ describe("URI authority parser", () => { out: URIAuthority }[] = [ { - authority: "ethereum:5", + authority: "eip155:5", out: { - blockchainName: "ethereum", + blockchainName: "eip155", blockchainId: "5", - contract: DEFAULT_CONTRACTS["ethereum:5"], + contract: DEFAULT_CONTRACTS["eip155:5"], }, }, { - authority: "eth:5", + authority: "eip155:5", out: { - blockchainName: "ethereum", + blockchainName: "eip155", blockchainId: "5", - contract: DEFAULT_CONTRACTS["ethereum:5"], - }, - }, - { - authority: "eth", - out: { - blockchainName: "ethereum", - blockchainId: "1", - contract: DEFAULT_CONTRACTS["ethereum:1"], - }, - }, - { - authority: "eth:1", - out: { - blockchainName: "ethereum", - blockchainId: "1", - contract: DEFAULT_CONTRACTS["ethereum:1"], + contract: DEFAULT_CONTRACTS["eip155:5"], }, }, { - authority: "ethereum", + authority: "eip155", out: { - blockchainName: "ethereum", + blockchainName: "eip155", blockchainId: "1", - contract: DEFAULT_CONTRACTS["ethereum:1"], - }, - }, - { - authority: "tezos:ghostnet", - out: { - blockchainName: "tezos", - blockchainId: "ghostnet", - contract: DEFAULT_CONTRACTS["tezos:ghostnet"], - }, - }, - { - authority: "tez:ghostnet", - out: { - blockchainName: "tezos", - blockchainId: "ghostnet", - contract: DEFAULT_CONTRACTS["tezos:ghostnet"], + contract: DEFAULT_CONTRACTS["eip155:1"], }, }, { - authority: "tez", + authority: "tezos:NetXnHfVqm9iesp", out: { blockchainName: "tezos", - blockchainId: "mainnet", - contract: DEFAULT_CONTRACTS["tezos:mainnet"], - }, - }, - { - authority: "tez:mainnet", - out: { - blockchainName: "tezos", - blockchainId: "mainnet", - contract: DEFAULT_CONTRACTS["tezos:mainnet"], + blockchainId: "NetXnHfVqm9iesp", + contract: DEFAULT_CONTRACTS["tezos:NetXnHfVqm9iesp"], }, }, { authority: "tezos", out: { blockchainName: "tezos", - blockchainId: "mainnet", - contract: DEFAULT_CONTRACTS["tezos:mainnet"], + blockchainId: "NetXdQprcVkpaWU", + contract: DEFAULT_CONTRACTS["tezos:NetXdQprcVkpaWU"], }, }, ] @@ -722,46 +688,46 @@ describe("URI authority parser", () => { const goods: { context: URIContext; out: URIAuthority }[] = [ { context: { - blockchainName: "ethereum", + blockchainName: "eip155", }, out: { - blockchainName: "ethereum", + blockchainName: "eip155", blockchainId: "1", - contract: DEFAULT_CONTRACTS["ethereum:1"], + contract: DEFAULT_CONTRACTS["eip155:1"], }, }, { context: { - blockchainName: "ethereum", + blockchainName: "eip155", blockchainId: "1", - contract: DEFAULT_CONTRACTS["ethereum:1"], + contract: DEFAULT_CONTRACTS["eip155:1"], }, out: { - blockchainName: "ethereum", + blockchainName: "eip155", blockchainId: "1", - contract: DEFAULT_CONTRACTS["ethereum:1"], + contract: DEFAULT_CONTRACTS["eip155:1"], }, }, { context: { - blockchainName: "ethereum", + blockchainName: "eip155", blockchainId: "1", - contract: DEFAULT_CONTRACTS["ethereum:1"], + contract: DEFAULT_CONTRACTS["eip155:1"], }, out: { - blockchainName: "ethereum", + blockchainName: "eip155", blockchainId: "1", - contract: DEFAULT_CONTRACTS["ethereum:1"], + contract: DEFAULT_CONTRACTS["eip155:1"], }, }, { context: { - blockchainName: "ethereum", + blockchainName: "eip155", blockchainId: "1", contract: "abcde", }, out: { - blockchainName: "ethereum", + blockchainName: "eip155", blockchainId: "1", contract: "abcde", }, @@ -772,8 +738,8 @@ describe("URI authority parser", () => { }, out: { blockchainName: "tezos", - blockchainId: "mainnet", - contract: DEFAULT_CONTRACTS["tezos:mainnet"], + blockchainId: "NetXdQprcVkpaWU", + contract: DEFAULT_CONTRACTS["tezos:NetXdQprcVkpaWU"], }, }, ] @@ -784,12 +750,15 @@ describe("URI authority parser", () => { }) it("should fail if some segments are invalid", () => { - expect(() => parseAuthority("KT8LELE.tezos")).toThrow() + expect(() => parseAuthority("tezos:NetXdQprcVkpaWU:KT8LELE")).toThrow() + expect(() => + parseAuthority("eip155:1:1e9f471dc0c3f15194827d1129af94628a3a753K") + ).toThrow() expect(() => - parseAuthority("1e9f471dc0c3f15194827d1129af94628a3a753K.eth") + parseAuthority("eip155:1:1e9f471dc0c3f15194827d1129af94628a3a753f.ge") ).toThrow() expect(() => - parseAuthority("1e9f471dc0c3f15194827d1129af94628a3a753f.eth.ge") + parseAuthority("eip155:1:1e9f471dc0c3f15194827d1129af94628a3a753f:1a") ).toThrow() }) From 5171ce6251c46d8a715a566fdd08fbafe61f5573 Mon Sep 17 00:00:00 2001 From: ciphrd Date: Sat, 7 Oct 2023 13:51:55 +0200 Subject: [PATCH 03/61] onchfs set version to 0.0.0 in the monorepo (to match release) --- packages/onchfs-js/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/onchfs-js/package.json b/packages/onchfs-js/package.json index 804f043..d562a64 100644 --- a/packages/onchfs-js/package.json +++ b/packages/onchfs-js/package.json @@ -1,6 +1,6 @@ { "name": "onchfs", - "version": "1.0.0", + "version": "0.0.0", "exports": { ".": { "require": "./dist/index.js", From fc264f3d54625f99449cdce9d7073169dd92b6da Mon Sep 17 00:00:00 2001 From: maerzhase Date: Sat, 7 Oct 2023 14:58:43 +0200 Subject: [PATCH 04/61] [packages/onchfs-js] set version to 0.0.0 --- packages/onchfs-js/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/onchfs-js/package.json b/packages/onchfs-js/package.json index 804f043..d562a64 100644 --- a/packages/onchfs-js/package.json +++ b/packages/onchfs-js/package.json @@ -1,6 +1,6 @@ { "name": "onchfs", - "version": "1.0.0", + "version": "0.0.0", "exports": { ".": { "require": "./dist/index.js", From fd26231b1490127328e8030b25f532739d058dca Mon Sep 17 00:00:00 2001 From: ciphrd Date: Tue, 10 Oct 2023 01:52:49 +0200 Subject: [PATCH 05/61] update ABNF spec on the doc --- README.md | 24 +++++++++++++----------- doc/docs/concepts/uris.md | 38 +++++++++++++++++++++++--------------- 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index b5d54aa..ff7117a 100644 --- a/README.md +++ b/README.md @@ -271,26 +271,28 @@ The ABNF specification for onchfs URIs, as defined in [RFC 5234](https://datatra URI = "onchfs://" [ authority "/" ] cid [ "/" path ] [ "?" query ] [ "#" fragment ] -; while the authority is blockchain-specific as different -; blockchains will have different strategies to identify -; its resources with URI, this provides a generic pattern -; for the authority as reference: +; the authority is based on the CAIP-2 & CAIP-10 specifications +; for identifying blockchains & blockchain accounts; terms have +; been adapted to fit to onchfs context -generic-authority = [ contract-address "." ] blockchain-name - [ ":" chainid ] +generic-authority = namespace [ ":" chain-id ] + [ ":" contract-address] ; this defines how the authority is constructed for the ; ethereum and tezos blockchains, currently supported authority = authority-tez / authority-eth -authority-tez = [ tez-contract-addr "." ] - ( "tezos" / "tez" / "xtz" ) - [ ":" ( "mainnet" / "ghostnet" ) ] +authority-tez = "tezos" + [ ":" tez-chainid ] + [ ":" tez-contract-addr ] -authority-eth = [ eth-contract-addr "." ] - ( "ethereum" / "eth" ) +tez-chainid = "NetXdQprcVkpaWU" ; mainnet + / "NetXnHfVqm9iesp" ; ghostnet + +authority-eth = "eip155" [ ":" eth-chainid ] + [ ":" eth-contract-addr ] eth-chainid = 1*DIGIT ; ex: 1=mainnet, 5=goerli, 6=arbitrum diff --git a/doc/docs/concepts/uris.md b/doc/docs/concepts/uris.md index 0f45976..7239438 100644 --- a/doc/docs/concepts/uris.md +++ b/doc/docs/concepts/uris.md @@ -10,6 +10,12 @@ Simply put, URIs are constructed as following: onchfs://[ /][][? ][# ] ``` +- `authority`: the host of the resource (blockchain/contract). Optional and left out most often as implied by the context. Aligns on [CAIP-2](https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-2.md) & [CAIP-10](https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-10.md) specs for identifying blockchains & accounts. +- `cid`: unique identifier of the root of the resource +- `path`: eventualy path inside the folder if the resource identified by the cid is a directory +- `query`: some query parameters to pass to the document to load +- `fragment`: anchor/arbitraty data to pass to the document to load + See at the bottom of the document for the [ABNF definition of onchfs uris](#abnf). ## Outside the protocol @@ -64,7 +70,7 @@ Smart contracts provide a generic `get_inode_at(cid, paths[])` view which can re onchfs://6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840 ``` -Point file object at `6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840` , where the context in which the URI was found defines the blockchain/network (for instance if a smart contract references this address, the resources will be found on the main file object smart contract of the ethereum mainnet) +Point file object at `6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840` , where the context in which the URI was found defines the blockchain/network (for instance if an ethereum smart contract references this address, the resources will be found on the main file object smart contract of the ethereum mainnet) --- @@ -77,15 +83,15 @@ Point inode folder at `6db0...6840` , in its `folder` directory, in which `index --- ``` -onchfs://ethereum:5/6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840 +onchfs://eip155:5/6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840 ``` -Point file object at `6db0...6840` on the `ethereum` blockchain, goerli (`:5`) chain +Point file object at `6db0...6840` on the ethereum (`eip155`) blockchain, goerli (`:5`) chain --- ``` -onchfs://68b75b4e8439a7099e53045bea850b3266e95906.eth/6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840 +onchfs://eip155:1:68b75b4e8439a7099e53045bea850b3266e95906/6db0ff44176c6f1e9f471dc0c3f15194827d1129af94628a3a753c747f726840 ``` Point file object `6db0...6840` on the contract `68b75b4e8439a7099e53045bea850b3266e95906` of the ethereum mainnet @@ -98,26 +104,28 @@ The ABNF specification for onchfs URIs, as defined in [RFC 5234](https://datatra URI = "onchfs://" [ authority "/" ] cid [ "/" path ] [ "?" query ] [ "#" fragment ] -; while the authority is blockchain-specific as different -; blockchains will have different strategies to identify -; its resources with URI, this provides a generic pattern -; for the authority as reference: +; the authority is based on the CAIP-2 & CAIP-10 specifications +; for identifying blockchains & blockchain accounts; terms have +; been adapted to fit to onchfs context -generic-authority = [ contract-address "." ] blockchain-name - [ ":" chainid ] +generic-authority = namespace [ ":" chain-id ] + [ ":" contract-address] ; this defines how the authority is constructed for the ; ethereum and tezos blockchains, currently supported authority = authority-tez / authority-eth -authority-tez = [ tez-contract-addr "." ] - ( "tezos" / "tez" / "xtz" ) - [ ":" ( "mainnet" / "ghostnet" ) ] +authority-tez = "tezos" + [ ":" tez-chainid ] + [ ":" tez-contract-addr ] + +tez-chainid = "NetXdQprcVkpaWU" ; mainnet + / "NetXnHfVqm9iesp" ; ghostnet -authority-eth = [ eth-contract-addr "." ] - ( "ethereum" / "eth" ) +authority-eth = "eip155" [ ":" eth-chainid ] + [ ":" eth-contract-addr ] eth-chainid = 1*DIGIT ; ex: 1=mainnet, 5=goerli, 6=arbitrum From 4ce4ef4706f177433867bff9c90c717d3d2db83d Mon Sep 17 00:00:00 2001 From: ciphrd Date: Tue, 10 Oct 2023 13:42:37 +0200 Subject: [PATCH 06/61] add support for blockchain aliases for ease of use --- packages/onchfs-js/src/resolver/proxy.ts | 30 +++++++++++++++++++++--- packages/onchfs-js/src/types/resolver.ts | 19 +++++++++++++-- 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/packages/onchfs-js/src/resolver/proxy.ts b/packages/onchfs-js/src/resolver/proxy.ts index 7c8fbfe..4567c7d 100644 --- a/packages/onchfs-js/src/resolver/proxy.ts +++ b/packages/onchfs-js/src/resolver/proxy.ts @@ -15,8 +15,15 @@ import { ProxyResolutionStatusRedirect, ProxyResolutionStatusSuccess, Resolver, + chainAliases, } from "@/types/resolver" -import { URIAuthority, URISchemaSpecificParts } from "@/types/uri" +import { + BlockchainNetwork, + URIAuthority, + URISchemaSpecificParts, + blockchainNames, + blockchainNetworks, +} from "@/types/uri" import { parseAuthority, parseSchema, @@ -82,7 +89,24 @@ const ResolutionErrors: Record = { export function createProxyResolver(controllers: BlockchainResolverCtrl[]) { // add default cain contract if missing const blockchainResolvers: BlockchainResolver[] = controllers.map(h => { - const blockchain = h.blockchain.split(":")[0] as "tezos" | "ethereum" + // find the base chain id by resolving aliases if needed + let baseChainId: BlockchainNetwork + if ((blockchainNetworks as readonly string[]).includes(h.blockchain)) { + baseChainId = h.blockchain as BlockchainNetwork + } else { + for (const [base, aliases] of Object.entries(chainAliases)) { + if ((aliases as readonly string[]).includes(h.blockchain)) { + baseChainId = base as BlockchainNetwork + } + } + if (!baseChainId) { + throw new Error( + `The given blockchain identifier "${h.blockchain}" is unknown, it cannot be resvoled by this resolver` + ) + } + } + + const blockchain = baseChainId.split(":")[0] as "tezos" | "eip155" switch (blockchain) { case "tezos": { @@ -150,7 +174,7 @@ export function createProxyResolver(controllers: BlockchainResolverCtrl[]) { }, } } - case "ethereum": { + case "eip155": { throw new Error("Implement eth resolver!") } } diff --git a/packages/onchfs-js/src/types/resolver.ts b/packages/onchfs-js/src/types/resolver.ts index 2c5198d..720d48c 100644 --- a/packages/onchfs-js/src/types/resolver.ts +++ b/packages/onchfs-js/src/types/resolver.ts @@ -1,8 +1,23 @@ import { FileMetadataEntries } from "./metadata" import { BlockchainNetwork, URIAuthority } from "./uri" +/** + * For every base blockchain supported, also provide a list of aliases to make + * it easier to build apps using onchfs (otherwise for tezos for instance it + * could be hard to know which chain id is mainnet). + */ +export const chainAliases = { + "tezos:NetXdQprcVkpaWU": ["tezos:mainnet"] as const, + "tezos:NetXnHfVqm9iesp": ["tezos:ghostnet"] as const, + "eip155:1": ["ethereum:mainnet", "eth:mainnet"] as const, + "eip155:5": ["ethereum:goerli", "eth:goerli"] as const, +} as const +export type ChainAliases = + | BlockchainNetwork + | (typeof chainAliases)[BlockchainNetwork][number] + export interface BlockchainResolverCtrl { - blockchain: BlockchainNetwork + blockchain: ChainAliases rpcs: string[] contract?: string } @@ -10,7 +25,7 @@ export interface BlockchainResolverCtrl { export type ResolverContractDecorator = (address?: string) => Resolver export interface BlockchainResolver { - blockchain: BlockchainNetwork + blockchain: ChainAliases resolverWithContract: ResolverContractDecorator } From f0cef1449f9969da47a2d74d62062225e50d8c80 Mon Sep 17 00:00:00 2001 From: ciphrd Date: Tue, 10 Oct 2023 15:56:28 +0200 Subject: [PATCH 07/61] update onchfs proxy implementations w/ aliases --- examples/http-proxy/src/index.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/examples/http-proxy/src/index.ts b/examples/http-proxy/src/index.ts index 6900eda..7b7a2de 100644 --- a/examples/http-proxy/src/index.ts +++ b/examples/http-proxy/src/index.ts @@ -17,13 +17,11 @@ async function main() { // executed during the resolution flow const resolve = Onchfs.resolver.create([ { - // ghostnet - blockchain: "tezos:NetXnHfVqm9iesp", + blockchain: "tezos:ghostnet", rpcs: ["https://ghostnet.ecadinfra.com"], }, { - // mainnet - blockchain: "tezos:NetXdQprcVkpaWU", + blockchain: "tezos:mainnet", rpcs: [ "https://mainnet.ecadinfra.com", "https://mainnet.smartpy.io", From a5516c7f231d4c1be70dc970887b27a620ab77cf Mon Sep 17 00:00:00 2001 From: ciphrd Date: Tue, 10 Oct 2023 16:03:58 +0200 Subject: [PATCH 08/61] better readme to onchfs js --- packages/onchfs-js/README.md | 271 ++--------------------------------- 1 file changed, 8 insertions(+), 263 deletions(-) diff --git a/packages/onchfs-js/README.md b/packages/onchfs-js/README.md index 9208a80..461ba7b 100644 --- a/packages/onchfs-js/README.md +++ b/packages/onchfs-js/README.md @@ -1,270 +1,15 @@ -# Package API - -As we are aiming for long-term adoption of the ONCHFS, we are looking at building a cohesive programming API for the official packages. - -There are 3 different approaches: - -## Exporting nested objects for grouping similar APIs - -Rational: we can granuarly define how we nest the different properties of the API to guide users in their usage (hierarchy defines level of abstractions). - -```ts -import ONCHFS from "onchfs" - -// preparing a file -const file = ONCHFS.files.prepareFile(...) - -// preparing a directory -const dir = ONCHFS.files.prepareDirectory(...) - -// generate the inscriptions from a directory -const inscriptions = ONCHFS.files.generateInscriptions(...) - -// create a proxy-resolver -const resolver = ONCHFS.proxy.createResolver(...) - -// resolve an URI -const components = ONCHFS.uri.parse(...) -const components = ONCHFS.uri.resolve(...) - -// UNCOMMON OPERATIONS - -// chunk bytes -const chunks = ONCHFS.files.utils.chunkBytes(...) - -// encode/decode some metadata -const encoded = ONCHFS.files.utils.metadata.encode(...) -const decoded = ONCHFS.files.utils.metadata.decode(...) +# 🐌 onchfs js library +```sh +$ npm install onchfs ``` -Alternatively, we can use a similar approach and yet provide a way to import 1 level deep: - -```ts -// gives access to the same API as above -import * as ONCHFS from "onchfs" - -// possibility to import 1-deep nested APIs -import { files, uri, proxy } from "onchfs" - -// ex - -// preparing a file -const file = files.prepareFile(...) - -// preparing a directory -const dir = files.prepareDirectory(...) - -// generate the inscriptions from a directory -const inscriptions = files.generateInscriptions(...) - -// create a proxy-resolver -const resolver = proxy.createResolver(...) - -// resolve an URI -const components = uri.parse(...) -const components = uri.resolve(...) - -// UNCOMMON OPERATIONS - -// chunk bytes -const chunks = files.utils.chunkBytes(...) - -// encode/decode some metadata -const encoded = files.utils.metadata.encode(...) -const decoded = files.utils.metadata.decode(...) +```js +import onchfs from "onchfs" +onchfs.files.prepare(...) ``` -Pros: - -- we have full control over the API exposed -- the file structure doesn't have to reflect the API, usage logic does - -Cons: - -- uncommon operations are deeply nested, and we don't provide a way to import an atomic operation; the dot notation must be used to access "hidden" features -- on VSCode, with autocomplete, everything shows as a property: it's hard to differenciate regular static properties from methods; _maybe it can be solved with some import/export voodoo, didn't investigate_. -- need to bundle all the package even if we just want the URI resolution - -## Exposing everything at the top level - -I won't describe more this strategy as I think it's not great for the DX. While a manual can be used to understand the different functions, there's no way developers understand which functions they are supposed to used, resulting in a mess of an API. - -## Multi-package architecture - -We would divide ONCHFS into smaller packages which themselves give access to a subset of the features in a smaller scope. - -```ts -import { - prepareFile, - prepareDirectory, - generateInscriptions, - metadata, - utils -} from "@onchfs/files" -// alternatively -import * as ONCHFSFiles from "@onchfs/files" - -// preparing a file -const file = prepareFile(...) - -// preparing a directory -const dir = prepareDirectory(...) - -// generate the inscriptions from a directory -const inscriptions = generateInscriptions(...) - -// encode/decode some metadata -const encoded = metadata.encode(...) -const decoded = metadata.decode(...) - - -import { createProxyResolver } from "@onchfs/proxy" +[πŸ“‘ Extensive documentation](https://onchfs.com/docs/libraries/onchfs-js/overview) -// create a proxy resolver -const resolver = createProxyResolver(...) - - -import { parseURI } from "@onchfs/uri" - -const components = parseURI(...) - -``` - -Pros - -- clean API, it's easier to access the components we need in the app -- type friendly; functions are functions (and not object properties) -- bundle-optimized: consumers can only ship what they need for their app - -Cons - -- DX a bit tedious sometimes when building a fullstack single app (multiple onchfs packages have to be imported) -- harder to maintain for us: need to engineer finer solution for the deployment of the various modules (_can be mitigated with a strong strategy_) - -More ideas ? - -# TODOs - -## Improving the API of the onchfs package - -Consider various use-cases, readility, conciseness, etc.. - -Maybe divide into 2 APIs, accessible through a single core API ? If needed - -- files -- resolver -- - -## Publish strategy - -- from monorepo -- into different packages - -Improvements to the URI - -- Before the CID, any number of / is accepted, however the URI should be normalized so that there are no / before the CID - -# API Improvements - -```ts - -/** - * OUTPUT MODE - * Possibility to instanciate onchfs object instead of using the main one to - * get access to some top-level configuration, such as the output data type of - * the most common operations. - * - * * This is optional - * - * * tbd if good idea, not sure; maybe we just use hex everywhere as backend on - * node can easily work with it, and front-ends won't really manipulate bytes - * (if an application requires to do so they can use onchfs.utils) - */ -const onchfs = new Onchfs({ - outputsEncoding: "uint8array", // other: "hex" -}) - -/** - * PREPARING FILES - * Polymorphic API for preparing files & directories, makes it more - * straighforward and clear. - */ - -// preparing file (uint8array, string) -const file = onchfs.files.prepare(bytes, filename) - -// preparing a directory -const directory = onchfs.files.prepare([ - { path: "index.html", content: bytes0 }, - { path: "style.css", content: bytes1 }, - { path: "lib/main.js", content: bytes3 }, - { path: "lib/processing.min.js", content: bytes4 }, -]) - -/** - * Generating/optimizing inscriptions - */ - -// in any case the output of the prepare command can be fed into the -// inscriptions function -// this will create an optimised list of inscriptions -const inscriptions = await onchfs.inscriptions.prepare(file, { - // if an inode with such CID is found the optimizer will remove the relevant - // inscriptions. - getInode: async (cid) => { - return await blockchainNode.getInode(cid) - } -}) - -/** - * Working with metadata - */ -const encoded = onchfs.metadata.encode(...) -const decoded = onchfs.metadata.decode(...) - - -/** - * Writing a proxy - */ - -const resolver = onchfs.resolver.create( - // a list of resolver, order matters as if an URI without an authority has to - // be resolved, each network will be tested until the resource is found on one - [ - { - blockchain: "tezos:mainnet", - rpcs: ["https://rpc1.fxhash.xyz", "..."] - }, - { - blockchain: "tezos:ghostnet", - rpcs: ["https://rpc1.fxhash-dev.xyz", "..."], - // optional, the blockchain default one will be used by default - contract: "KT..." - }, - ] -) - -app.use(async (req, res, next) => { - const response = await resolver.resolve(req.path) - // ... -}) - -// also possible to create a custom resolver with low-level primitives -const resolver = onchfs.resolver.custom({ - getInode: async (cid, path) => { - // handle - }, - getFile: async (cid) => { - // handle - } -}) - -/** - * URI - */ - -const components = onchfs.uri.parse("onchfs://...") - -``` +_This readme was automatically generated by a lazy human_ From fb58e3a35e7c5ca9af9de18925d7b5f3ef261015 Mon Sep 17 00:00:00 2001 From: ciphrd Date: Wed, 11 Oct 2023 01:20:05 +0200 Subject: [PATCH 09/61] test-project-next for onchfs build error fix --- examples/test-project-next/src/pages/index.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/examples/test-project-next/src/pages/index.tsx b/examples/test-project-next/src/pages/index.tsx index eefd977..5e4496a 100644 --- a/examples/test-project-next/src/pages/index.tsx +++ b/examples/test-project-next/src/pages/index.tsx @@ -93,7 +93,7 @@ export default function Home() { const op = await kt.methodsObject .create_file({ chunk_pointers: ins.chunks.map(buf => uint8hex(buf)), - metadata: ins.metadata.map(buf => uint8hex(buf)), + metadata: uint8hex(ins.metadata), }) .send() await op.confirmation(1) @@ -110,16 +110,15 @@ export default function Home() { } const enc = new TextEncoder() - const inode = await Onchfs.prepareDirectory( + const inode = Onchfs.files.prepare( files.map(pt => { return { path: pt.name, content: enc.encode(pt.content), } - }), - 2048 + }) ) - const inscrs = Onchfs.generateInscriptions(inode) + const inscrs = Onchfs.inscriptions.prepare(inode) console.log(inscrs) for (const ins of inscrs) { await writeInscription(ins) From e95b4bc2b53eeb01dd1497bab5fbc1292d852013 Mon Sep 17 00:00:00 2001 From: ciphrd Date: Mon, 16 Oct 2023 16:32:25 +0200 Subject: [PATCH 10/61] updated contract addresses --- packages/onchfs-js/src/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/onchfs-js/src/config.ts b/packages/onchfs-js/src/config.ts index 489b981..0e34f12 100644 --- a/packages/onchfs-js/src/config.ts +++ b/packages/onchfs-js/src/config.ts @@ -28,7 +28,7 @@ export const CHAIN_IDS = { // A naive map of the "official" onchfs Smart Contracts. export const DEFAULT_CONTRACTS: Record = { // tezos mainnet - "tezos:NetXdQprcVkpaWU": "KT1WvzYHCNBvDSdwafTHv7nJ1dWmZ8GCYuuC", + "tezos:NetXdQprcVkpaWU": "KT1Ae7dT1gsLw2tRnUMXSCmEyF74KVkM6LUo", // tezos ghostnet "tezos:NetXnHfVqm9iesp": "KT1FA8AGGcJha6S6MqfBUiibwTaYhK8u7s9Q", "eip155:1": "b0e58801d1b4d69179b7bc23fe54a37cee999b09", From ac833937ede82b9e6499a54c4de647ba51184681 Mon Sep 17 00:00:00 2001 From: ciphrd Date: Mon, 16 Oct 2023 17:14:07 +0200 Subject: [PATCH 11/61] fix onchfs-js proxy wrong address blockchain check --- packages/onchfs-js/src/resolver/proxy.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/onchfs-js/src/resolver/proxy.ts b/packages/onchfs-js/src/resolver/proxy.ts index 4567c7d..73c2560 100644 --- a/packages/onchfs-js/src/resolver/proxy.ts +++ b/packages/onchfs-js/src/resolver/proxy.ts @@ -126,7 +126,7 @@ export function createProxyResolver(controllers: BlockchainResolverCtrl[]) { blockchain: h.blockchain, resolverWithContract: (address?: string) => { // default blockchain address if not specified - address = address || DEFAULT_CONTRACTS[h.blockchain] + address = address || DEFAULT_CONTRACTS[baseChainId] if (!address) { throw new Error( `no contract address was found; neither can it be inferred from the context (${h.blockchain}) nor has it been provided during resolution.` @@ -141,7 +141,7 @@ export function createProxyResolver(controllers: BlockchainResolverCtrl[]) { path, }) .executeView({ - viewCaller: "KT1Uktxf9dgGga6DRRNbGEDepxFGTwNtTg4y", + viewCaller: address, }) // if the contract has answered with a directory @@ -166,7 +166,7 @@ export function createProxyResolver(controllers: BlockchainResolverCtrl[]) { readFile: async cid => { const kt = await KT(address) const res = await kt.contractViews.read_file(cid).executeView({ - viewCaller: "KT1Uktxf9dgGga6DRRNbGEDepxFGTwNtTg4y", + viewCaller: address, }) return hexStringToBytes(res.content) }, From 400603b96389d231b7fe65dc9ecc773267a84902 Mon Sep 17 00:00:00 2001 From: ciphrd Date: Mon, 16 Oct 2023 19:45:56 +0200 Subject: [PATCH 12/61] added some logs to track the metadata onchfs generation --- packages/onchfs-js/src/files/file.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/packages/onchfs-js/src/files/file.ts b/packages/onchfs-js/src/files/file.ts index bbc1594..04b0c66 100644 --- a/packages/onchfs-js/src/files/file.ts +++ b/packages/onchfs-js/src/files/file.ts @@ -6,6 +6,8 @@ import { concatUint8Arrays, keccak } from "@/utils" import { FileMetadataEntries } from "@/types/metadata" import { encodeMetadata } from "@/metadata/encode" import { FileInode, IFile } from "@/types/files" +import { u8hex } from "@/utils/uint8" +import { decodeMetadata } from "@/metadata/decode" // import { fileTypeFromBuffer } from "file-type" /** @@ -32,6 +34,15 @@ export function prepareFile( let insertionBytes = content // we use file extension to get mime type let mime = lookupMime(name) + + console.log("--------------------------------------------------------------") + console.log("PREPARE FILE") + console.log("----------------------------------") + + console.log({ file }) + + console.log({ mime }) + // if no mime type can be mapped from filename, use magic number if (!mime) { // const magicMime = await fileTypeFromBuffer(content) @@ -46,6 +57,7 @@ export function prepareFile( } else { metadata["Content-Type"] = mime } + console.log({ metadata }) // compress into gzip using node zopfli, only keep if better const compressed = gzip(content) @@ -54,6 +66,8 @@ export function prepareFile( metadata["Content-Encoding"] = "gzip" } + console.log({ metadata }) + // chunk the file const chunks = chunkBytes(insertionBytes, chunkSize) // encode the metadata @@ -66,6 +80,17 @@ export function prepareFile( concatUint8Arrays(INODE_BYTE_IDENTIFIER.FILE, contentHash, metadataHash) ) + console.log({ + type: "file", + cid, + chunks, + metadata: metadataEncoded, + }) + + console.log("---DECODED METADATA") + console.log(u8hex(metadataEncoded)) + console.log(decodeMetadata(metadataEncoded)) + return { type: "file", cid, From 658885eaa8c0461485ef26deec9c40c42313e7d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Pradel?= Date: Mon, 6 Nov 2023 10:18:26 +0100 Subject: [PATCH 13/61] [onchfs/doc] feat: add new seo image for onchfs docs (#439) --- doc/docusaurus.config.js | 3 +-- doc/static/img/docusaurus-social-card.jpg | Bin 55746 -> 0 bytes doc/static/img/social-card.png | Bin 0 -> 83542 bytes 3 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 doc/static/img/docusaurus-social-card.jpg create mode 100644 doc/static/img/social-card.png diff --git a/doc/docusaurus.config.js b/doc/docusaurus.config.js index 1920dd2..8188c12 100644 --- a/doc/docusaurus.config.js +++ b/doc/docusaurus.config.js @@ -60,8 +60,7 @@ const config = { themeConfig: /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ ({ - // Replace with your project's social card - image: "img/docusaurus-social-card.jpg", + image: "img/social-card.png", navbar: { title: "ONCHFS", // logo: { diff --git a/doc/static/img/docusaurus-social-card.jpg b/doc/static/img/docusaurus-social-card.jpg deleted file mode 100644 index ffcb448210e1a456cb3588ae8b396a597501f187..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 55746 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 fgzs%Vr*-jYG(YMS<ttdtee# diff --git a/doc/static/img/social-card.png b/doc/static/img/social-card.png new file mode 100644 index 0000000000000000000000000000000000000000..adac191b3d3e6275fc9c3d2e7251a5c4229647aa GIT binary patch literal 83542 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

RKFH3mkdpX~VQt-aGejclDu%dHP zO&BHExhdtS^_8>g5y%M~i&+)Iu=Aj&?!BE48~Y&zRN*}^gbioam9bia09i}5eLwTs zM!ITVg{{}~5MQwD7VngGTCkv*eGNP4JR@La_^JK^&@P@8cCI>QOME^`my6vCS2m;x zOsu@cP3sV&jhUyTRC?J!^hm9h-X$nx_m79)7qs*K%=Wk<KfbaeZkokijkEJ#~sbUzQkXdZWM}j)pMwd zU9)rDaLFpwR>&jF{*a64Z-uQgNbfkKpZP%9kp5(3mJ>_!j#>a(;FHj9BcJviA;L&7 zNSiLW_BbV~;S+Ix{xa^nG}j>rTwG+eQ24N0$M0h3l)dpdB=|O{E5y^j53;3ZsE8Yb zckS(D;SS!9Erx=Rw?i;d%bq=Hd8jtWhgtD7*ubfmocvgttI zINsKVGs2?99X*pdl05F3#JSBai2pHO0FqNVh)W9h;hCc~g|jZ;&@FK7ejR%ho87gY z@L3g`EC8}?l>~WBa^S#~(#FI5?u>{|)t)I^vO3Cz(x-qzID5Y;S zId)s@X!{E>R^93}>5F4Mir`s%z-?;K@ryR*BK2{J|0u5DX~o20>==qXzT_g$-Zd$_ zwLsD8;9u}-(^ohVza9XjSzsB1v-^9}1UagZsEmHdYQ-@Z>@F- zFY9aF54m{duHdBD?0clniCbp8SZixNpyXa_cekBfeGk|bc)m*g*8Aq!?IHdXhORrx z2`#XLOwP}VXT!4Ag3z;h49)R08cgA4 zuWI0*rOL+>dNRbgKXvvH)(0+6$$vaB|H1+j<`Ej`>Dl0yaYUF(!@w*tVF9xp3KkL5{2(ou}=|ay*`oqUML$#V; z?jd5g-#PF>ABm+!^w4dla_oRCe2#`sRzS=5SG!*<#nq}7E(@>sTp5Qaor;I8bWSO6 zpK##ijZ8??6ORdW4*Ht<-#JaK?(A{8@(2(kXPMIQ6MG&kDo#g4Qo(vHaoicUc!^67 zYFTHi&B%bPIi^%OY1|r=_Ka|!I&AZPhR!XtcmKXcMOg+;t90_UhZ;B<8773BCa!N| zC*n>lc~BP9(R`GZ&N+6(sz};;s<0BVUb4RmOreyL*NG= zi}H&nu6LpTv$Z<|e<{-+xSmo5OMZMY1xDR^h!u!HdzH%H`!I_!?dl@%iKK}%o?NGO z%<(+*KGD#4s${R~Vcr#{7NENK@o9NNUp`22dPG=6la1%fml1yUG>mio`81~(l!~Xl z#CXmbY^jluL4s^Vp#GNiWSQQl!IQ{J4!ok0U`ngZnC=ud?lMR0-o;ELFQ!P7sNBUC z2uxwZ%qY=*8n}G1jG6`w7JgPbEB;sjDFrqP*A+$0yG{Pj$06Up>>&2}&m8!nA@tOH zEI;}nRbtS7ciQpJyZywU^D*wt;SH$i#o8&sbIs5tB799edg>0}Is3eYLMKH=n49Hz z$#> z2*9ZvEt-2J{KS}l^R2N3KDpOF1 z`g(3m4T^=KV0_8w!CoOOMu}CLR7Tw4JvgHa^1ox}Vc% z=N<7l{xs&$nF8q#p6suAPOXVKPB>KNC$HCBcc)^FB`FLooV%NhYCBwrEelvG>$`yq zaLlcQn##mq$pV6Inr92MSNa)=kxZ;G#R4JEMwDcizx?&iSq*zpH#qdY-{o3T{yW8| zZ^46gGDd6fSmh({V;FYm5W-^fOXJz*S{YRO@m`DD_$D}mKw8+aVnH2R4jS_>lwOen z&3*buv+|K`fAPqv(8}<;rDg51$tp5#*BZ+}hf5z5&Kg0|nT*-DQ!8L3BAM*Gc6=&E5$_vhD|neqJA@v+@S7TftjD^N0DW zhGkZ4nEwvjo{Ay!bN%k!QI7)^U};oOR#xw}+@Qal!&=h@@LuW})eA7nTy2m8q6U9O zLFk|!UXb;DW1lWdwmZAJJPIIs<6Y)R&0yQXwPi8(ZH7omPp6=Ym4{rljVSVus2NqZ> zBCWdu{UJZ_^4<13+Iv5+($U%Q&?L-AN{$`N?L=5+ek|V!=7pmU0!`25wfj+#z{K+K z>hX||`k5IVk4lsb`%$-`a*r-fdbp1(dy&S?8VRsamA^|4%kb@5!Zxt}AmpkZ;_v|0 z+80=_AZIDtJ$IE=`w4#6BtZu54QAONwAIwLgf9RX=s9bAw7tJdTcqf;DZkN2y};vZ z^Mg)0^#<}at+uSnTY}5-eW>^Nk}h=5RG9F>;w_@)k4{rf86#^;3^syIbvJWg2#%2g z@QUv}E?oNpuY9%_t+LkF$o&&FU=tM2Wl#~a8!TB*OiRDmxavK*R}VRzDrQ{I-O`Pg zy8YTyK1|mC0^?l{p8Jcwo6YCxUlQl+;hjOK)7|{Q>=P>3^3To6?AeBmT4H=$z>qFD z8vC4z(0mS%Oroqh){|A|X^w}K@|%1J#WPp^8$70=-`aII)!?+0!V}axmMi$AvxLCjed(A zdc86-JpR_+cO0re?xH9 z-?s=~u8{Dd``1bF21w^R>vH8pxP@4uQ_%?ykav%y&1QOo0EZ6hAVdOViE3NDf-6LZ zQM}_@6Z&%$Ujb_u(sq^F?ssY(wKUfP3d z&*U@fv;lK5jC#R;)L+zxs^h{gw*Qi@+AZH@hiw+KUg5dF>8oJecRN?Pbps4}fEQ(Q z_0~TE0Nz8)ChK>;Shp{Gu>)nPmIChwu>1k_bzGTDV}1KOMfYBZgI2tW%h7}M<~6Ll z!X=+{2~~pC(+@qVk;&NhSxL}F7x5V=XO1+!$j0Yhio{s<{kIw;y5 zOfn&6l7BlEp54KW3{nc|-9LEMAbW5d@Pu|l4tfxFVOj-P@wDJbuZ(r~Yt`<4e0RMu zhh)c6JmV(Vz=F1BR%aOM^pPb0PRyK}%VzD#cm@?$B6>GAanUj==Z}2I=eC(ykZ`Ya z;-iqGb)+6dW3wbwl|L*Foj1HemAa9@cuZ0ElW@zRL5uCHy)XixPutF1Iy7S5@1;uX zQEmw~;0jQ0Bk-hANEcvuXCDW8a#{^oqYc%m9bdu+MBp4_!4e*8NZkiRcl$>tS>xQs zUL=tPe1N*-;w|r#C9|oYIpooB^0_u zfnk^5gC@iTO7$8LgvCh$Nv<<6?$eRF`#XdA!{5^l257Q^-m^kI@;5Yp)G6dWf*{#LqhsRR z;!wYLD|oQIVyjyD(00_pA{Udg?3`&tc5+Z;pti6PFB7T1yZFwygcX%E1)pdkCE+#9 zr{wW&tQuwh18v-=jWuJ?-t_5IXaOeAjD%kkY=L!`iEPpD`X_;KF6 z))|uk%j)4n*~9oA=mB5)eTk*k)4K%FL679AFMNmg<)!nGpGoSw6UN#eliO@wx6 zc}LYiRnxgnZu7j0-3FPC{qDBzHggqZPGsu4z06DvtK-EJkD-TLqPl#MZQ(u46T;A` zYilEQSQ_5ek6)^VM~IynEq#6qr8%MarCT;z%v1$!Qs8*`6Tm=MFxK*gMjFbG0^z=T zzPZneJicc1^e)gqKLCwUmbTr z>!VHf5xiL9WZFZgBG^mLcFsnUaG&3tGfTn@TY)()C}X~^HCn}&i-T!}cE6Wte91Z5 z;H*<}n}sVu3g#)M;Npg#=Zh!wQQ~l)*pKftvHCMI?RoAVOs^z${%yCnZiNbjy`VX8 z8F(c!aaeYoAhg>naLT<%(b7{N8w5UiMF-WyWQ$_(Kv@R>Wr*M~pDA?h8O^?!k$?Kf z`fF(iAhCYzJBifc(CI3}_whd;>m(CVkGs=-uCABf{3-lu5!uFZ^iFnX&e+tV#K+GY zBJ>k}Sf$jOtF~$)EM{3KO5}dl{;S?k+CA9f=CI_(`ZM=oVvY5XBqQ#6My||Zn(p5R zp3We@+FYli8G;)Pl?u_-^4dSCb%JShP!Xh3MKbrQl$fxXPu*)2-*AK!&byHzo@?CI z2XUO{gP>M8-X}vLq?yOA06y$ykX^T(1ny~jJlhdIuS>LmohlDjMh?XY9ql}tlmJ6$ z5^ckjum@^vm(LJJ%E|rAOzN#?58v-|j%eKQ!D)r*vtU5sE^t+2$6rp{HQ&<|5hWV1ol+fsL(6{aq`8S`9({uxf} zOTzRN475C({Vv8_@J9{493`?PqC+qt@0!=TT@hpmA*urVb9Zx zq9~nd5nBRK7j)D`5DL`oAdxpJ+_GHt=HXs+mW0>k@q~_tU#J1oIuHdi&}8MUYWo?4 zj3*l?E^|g81-mB^0oIgm!4nS&4%0d2LlH}HzRV3zLiprPNKpJCG{UbXVzZ^r4oin* z^~t7^!WIoyjargjBL%8h_|t#;{VU&8o;NvX=>Ynf=Bu6}8}WgmW23ip3M__JlY(I( z2s)}<6@;i?wZOA4QAm8`9Iwb8*iyytrf0YR=GoC1&qPYe#JhG%h0VnMTvY>^ohE3gMcGaoF-+gT@M)vg z`-CSvD)i9_L8 z*mFsbN4WU!mZ(1-h9BRp4le#ISsCG|uFjY70Y|6dRxR2uj}w5S^L=>)ReQcMi#Enj z%tV^()PLCq6mYtGGc0VKD=LIfNX*~?f84%FO)pAnzUdWFQ0=g%o5jmE@?*IQi``{u z9M#!A=qYG!x@V&x%j82O;J2T#9=+x4zcY$0m*IVh>UyR_ z3wkffoqzZhTaCWmP~c7KbJg^|0`5Beg^}kaUp?3~H*K&)zCPjO zyxS~gfPFCtFK>v$b8i#`iy4k3mnUE;typTFb*#JgYj5h#SW+U4D_1H@_YnnAJlX7x zMD9EMwlwvukuL&ksH-6|Ue_p@`!~j7k0jUKih63~C?BzKheR&V@>a*ikoE?8Zzr{qK(m!TNC4|$CK(k-=6UJZ}pbwzb>ETr&cUnZRW1}RLot!8dMT+ zW;s{@*;N1c(#5_)17~_8hk2H$YmqwbARTdl8AG#?!flSS(;M$KVu$Tp-rHcNfSNJx z;ghGtdB$cuv*(CYIIR|gNy~BTi@X6SXf^gi0>AC1*W6_Cm$H`N+7n+>@Bx1c2S@y8t0wxXkn( zyL*^q=vYiS{+C|vSjV(OUvw9cIDiO;)%lFwd1Ym!C7?WW<-IwIZ#+Uu4DY9EdA#R| z(Hf-}GNqkIWg<+f1~aB!A&wIzT~>BhF*`%_Mm^cZNTqOvqA&cAQO)d1xwJi6yvU#XN;T6@>LGP@kmk_E(w|5G(SdpSXU+0$giacWzf(>~FF zxy75+O$z_r-OZ1AupWcN4@1&sj)+&Xdy@3HFXSsnU_Q!Cyf{L>XXz+f`O4(xe2%}H z0h4o~`8Eo@J89*I$7uXe*O)_Ei9>sS^H4tXCiq1ZUx?XyVIYT zGD*I^Gad6xmIF3fCJ4Rd1TOC-f4+0X=H0f9RaY^)VhwNGe^jaJ(odyD~pc)7f+w4;+0HtIAXF=eVO{`C~aK(j>>J?}1Akpnl{ep4AZh zUGy@+c9C0X$d&6=YMM6l^(^rb@w5C0dZv|Kx7N!;+?V3|+5oX^-yfD)r9$ z1!}5=W*pFcYBmK>MV&{d;d9BG-h4R{*azOz?;@E4ba;&fKr5K{65iX-_%EH8DHdX~ z$^Eb9QXo5Am+b)HQ*s9Xg-VD&0r277=A({9Zb1ESr$wcJ~5+^7<(Je}~ed}C@Ao?AN8n^Aek&?Z*m^Na- ze}B7sM*&5}Cp1Lhc_z$ITL`^7cT`f<%$?PZR}$Of!l&UPlOSQ#XBq% zPs$2A=0rY>F+5u16eekPnY z%cK+XhJ5a^Oz`Qbub_C~y6vH!UTn#S%`?G@Ui@>GH{EeRdyHRKK@qKSmG_qa=zElT z@ghbs)+C19-G4Mt&G)HV8sS7twt~5z>6B&Qsb`J0FC8->3lXrSJ9EL)5BJH;537#q z=Y9FWk&Qj=*2#U!YH{crZ&v%X)*_W}O(8l}9rbLm#dptplfSeGY`qAhR13(@sqOeG z9>I!cJ5Q{|I{(0uz7?;o-)yogobntT{=)aW9kM+%7h^;+xx2K1{#R%b2&eZ7b-YRI zXYyeSvial4m$3H}3j1>&?{CQKQ!<*#GA$xtj-SGOR;&18oPG-&&jwtdIJ~37^~ZVX zwSbEge0b|eBwvusW{QQaZ7uZiqSA)bvT4^yH(GN!Q>0VFYhEc|-T|A1S z7ZLRc8?I#kz`29cnuZGrkn$fyZtRUR0bayrZ<{|tZdToukj})*#GBqy;=kvBHf|4| z({_2~9@rWu($atwS7vRiR2Tcc6X_4a@yy(^9=hMr;wbe^;N@l_HkJ-95o$DW0MzE(?1qbc>>-?OZCxoZDdp7uOrBjCJJMKZ!YO@h8k zyM(c1yguzX_x((CU~r(EhzZUCeJ3PlhEN82>f=L^MYM{Hr5`&=xjGdN z4mrFzWPPWwMQ1;+_+Ank)^Kj$ns8n04yGR4dtM&VcO%q^+{zLpY1)16TO$tzh=(>! z@{WDQY8*2sso}jCuUKf4xZ27iG={9O%8!RSK1046}c9|hn1s1Txm3P( zJ!G?*mY+FWpiTX-=THxEG8in;>U2FClNTAiL3fm-Fy@fm_Kk+eU_;A6Uf@utTS1Y7 z>;93uaJi&=?p*6YOTCa?>NGl`;P4Q(`H=)zLS?ZuqIKNd!cU|WYZ6b6G*c9PHjp9$ zgCkUa`$|3s)y$H{eZE^7XYP5cu^T?|QuxQhbb)p>(ZG;PW>Wa;u$5ossO6yQZBnac zZuy1O%@)qwW3vKMFTac@PFu zvQJ&RV;H5;!1bl(aOxg3`mSC3Z1C5+L*36rbFuRx*kT6ts(c*ZeTIr1ogOD3st8P& z;}iw=gz}`qUu%WDd7m5F5{1qrM-uZSfa!14&AJ%l^**SPrR9=sJX~4w*T{Gw-x1t! zbCY~dT1<*QWK|``UF4UU*p#X~v)*3IQ367r#<~@*7jXfSvv^9Coj12saC!)tqjx!-)T@G_%+A0 z9&KasWvJZo9O19<21k~>zo*VEE+3(&USyAgLgj7X0Q*r^sCMp0(*UX;{TB$~mv1u5brd>NTEM z(O=_~*BUd?o*VsazV_cuRlUJu)?B!lwwugCSrsLQWbCJMzHt(aIOztI9=h`3-}dlo z_tnZ)@o?n_8-Lk120W;ti7Y{9cOj`2V38Mq>D}MHw}1*xS)Wy@Hz*4 z5a!*Fp_{!zRC>OMKe*u!iAyV!f^3zz^C_j8NM3T=n2R*h(Hu??^UYA;Y>RtDUNFG% za%f@8%V7LI5|KyHbyWL1j28PSKf(QeZ_G6m8kO8&Ik7+Jk1_W$cmK)XV$fupyL}Wr z;4u*pVB1Fm7{`=F;do*8m~t)(TTu*C*1@og|0(l90aKVV2Mq||h$*LH9udcshcJ)i z{l9x0Wmiw`h@n$c;-9&{!^R1Ur2dD&{p{Zi?$!SybpJ=HHvgv~`g|b3C5B3Ehv9bX zK>`1<>VGrjA8z}96pH@}0mcw#0B!_F7^_PJ;6AwYO;^6obl$@$49z?h6Qn$hU^wrJ zVi>_d<%p3X3|!+F!5CbE5e`iJ#4%!vLC_u}swdb0^NAopPnH-3;M?XH8NgNA3kzWW ze}4j_>VH?)HyOg3sYr?^Lb<_H5Zd&L@?pmFY^ik)D{a6CChH_I$@x#R>i$E2|L2bY z|M2lafIpa~|8JWQl2G@d>SAcyt>UrU^yOkChWzh^=^-mTP>X|QxNg4)Wtf2vjU-A$ z>X$ze#j2{3P-Sypm~#Zvb|)5B;qm&+f+2QK4hp-c2-8tMVB`SPq5dB(ER4X<(yO^; zFE4ve%%m}7e{YOmKQMCskFWp8WK{NgeAYtX-Bh5x3zY}8ltBT*$N_AHcepI%NW6P4N#NyoMNL!HU3SU=` z)!)aMKRCQ8J}MrOfPCJP;!PT*Mcw`Kn*4KisbG407&>|R1ATGy4q6Kg@OR$<&d+;= zAopBO@a|(!jZneJy?YL~eRHjGU+f$nXsz%lFT^82Q3VDfzi7V1ei@bB(#bA%k>js+ zK6zC4mqdpWltp*8G$M&Esg+8sKgXt^24!W<`}l!QuiHC?gTmDbfdZqRGt2(J0)Bzs zU{rjQ+(8sff!;)*x*ENt+C2Id0cyRXL5t)@V@4_xN*pGw8xf^RNe2{?bk=>4ft^lb z@0%XD{s!&-IwCo-s{@JGr4AmhDS|L;I;hNnR%N5Io2{1@9N1`m&VCSHTJ>q0`8Z6; zA-X#8-ejZFUe(@2Zh1&S{s&E89TrCq?Tx#;ON-m$UL1;3ph%%C4#lm&;_lj_MatWv zg(5|YE$+qLm&Ki3baC$9`+VOY^E{JGCdv8bNKPhmVyrgNEw{;D_4m3ihqJ}4_KOHy z44Le-ugh|DdJ^`|211el92@LwTpE(oxcQ!7-rxc>3OUD{WKFIH{!lNmg4v9)ImXc$ zf+84^kFS-SmFGAgX(K*R`IIyy6zk&!CWF527&6x+~ zdICWa2FQ{ec(CjK05?oY*-iH1U&tLMH^0M)k)>+GbU)Yn1{rs&fkvD<;n`&CdSc

%ZK2hlm1M?BR2gLO&FcOG<3F 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

ffH0L_FtVZiQ{gU+C}J=^q7 ze1ZBLll37~lh%?QSszhO(X8Z<J?2B*+PG<;@`M59W*d(;#gGN{V{9ifMSqSSz!Fu1Yt6BLNUH(!m;H31B-0p z1ZSXO`#vHJAv8P|PK#lUhBFBSFb2@DFuGAR8n#6@`agBhL;J;B?g<7)uwZ$R3)QoY z7vF+)pcwBtEijM+L>P8kIRDR*#$UYOV_^_u%M%|0K2skfESZHU7XLpSwEhRB|LY_f bhQtMCVoezlS|_Cq1O2EeYb(_%Scd*Dy2QrW literal 0 HcmV?d00001 From 7924d9a224d18c51551a56cec5a3a5a4436b474b Mon Sep 17 00:00:00 2001 From: ciphrd Date: Wed, 29 Nov 2023 20:35:57 +0100 Subject: [PATCH 14/61] progress --- examples/test-project/package.json | 5 +- examples/test-project/src/index.ts | 162 +++++++++++++++++++--- examples/test-project/tsconfig.json | 31 ++--- packages/onchfs-js/src/cid/index.ts | 49 +++++++ packages/onchfs-js/src/files/directory.ts | 5 +- packages/onchfs-js/src/files/file.ts | 24 ++-- packages/onchfs-js/src/files/prepare.ts | 5 +- packages/onchfs-js/src/types/files.ts | 19 +++ 8 files changed, 238 insertions(+), 62 deletions(-) create mode 100644 packages/onchfs-js/src/cid/index.ts diff --git a/examples/test-project/package.json b/examples/test-project/package.json index eb3814f..74b1fa7 100644 --- a/examples/test-project/package.json +++ b/examples/test-project/package.json @@ -14,13 +14,16 @@ "test": "echo \"Error: no test specified\" && exit 1" }, "dependencies": { + "@fxhash/config": "workspace:*", + "@fxhash/eth-sdk": "workspace:*", "@taquito/signer": "17.3.0", "@taquito/taquito": "17.3.0", "@types/node-dir": "0.0.34", "axios": "1.5.1", "node-dir": "0.1.17", "onchfs": "workspace:*", - "tslib": "2.6.0" + "tslib": "2.6.0", + "viem": "1.14.0" }, "devDependencies": { "@types/node": "18.7.13", diff --git a/examples/test-project/src/index.ts b/examples/test-project/src/index.ts index ff8e6c0..87ba373 100644 --- a/examples/test-project/src/index.ts +++ b/examples/test-project/src/index.ts @@ -1,15 +1,43 @@ import axios from "axios" import Onchfs, { Inscription } from "onchfs" -import fs from "fs" -import path from "path" +import fs from "node:fs" +import path from "node:path" import dir from "node-dir" import { TezosToolkit, ContractAbstraction } from "@taquito/taquito" import { InMemorySigner } from "@taquito/signer" +import { bytesToHex, createPublicClient, createWalletClient, http } from "viem" +import { privateKeyToAccount } from "viem/accounts" +import { goerli } from "viem/chains" +import { config } from "@fxhash/config" +import { ONCHFS_FILE_SYSTEM_ABI, ONCHFS_CONTENT_STORE } from "@fxhash/eth-sdk" async function sleep(time: number) { return new Promise(resolve => setTimeout(resolve, time)) } +var walk = function (dir, done) { + var results = [] + fs.readdir(dir, function (err, list) { + if (err) return done(err) + var pending = list.length + if (!pending) return done(null, results) + list.forEach(function (file) { + file = path.resolve(dir, file) + fs.stat(file, function (err, stat) { + if (stat && stat.isDirectory()) { + walk(file, function (err, res) { + results = results.concat(res) + if (!--pending) done(null, results) + }) + } else { + results.push(file) + if (!--pending) done(null, results) + } + }) + }) + }) +} + // test with any folder/file at the root of the tests folder const files = fs.readdirSync("./tests") @@ -30,6 +58,22 @@ const KT = async (add: string) => { } return kts[add] } +const ethWalletClient = createWalletClient({ + chain: goerli, + account: privateKeyToAccount( + "0xc6ce7bd0af8af4d72dec91fd78c44e5579aac9907a4e22b5424bd903fbd521fd" + ), + transport: http( + "https://eth-goerli.g.alchemy.com/v2/eGEGqTf0cBekTDv0Ghy1kXPKhdNSLmn7" + ), +}) + +const ethPublicClient = createPublicClient({ + transport: http( + "https://eth-goerli.g.alchemy.com/v2/eGEGqTf0cBekTDv0Ghy1kXPKhdNSLmn7" + ), + chain: goerli, +}) function uint8hex(uint8: Uint8Array): string { return [...uint8].map(x => x.toString(16).padStart(2, "0")).join("") @@ -62,11 +106,65 @@ async function writeInscription(ins: Inscription) { console.log("OK") } +async function writeInscriptionEth(ins: Inscription) { + if (ins.type === "chunk") { + console.log("-------------------") + console.log("writing chunk:") + console.log(uint8hex(ins.content)) + //@ts-ignore + const { request } = await ethPublicClient.simulateContract({ + account: ethWalletClient.account, + address: config.eth.contracts!.onchfs_content_store, + abi: ONCHFS_CONTENT_STORE, + functionName: "addContent", + args: [bytesToHex(ins.content)], + }) + //@ts-ignore + const transaction = await ethWalletClient.writeContract(request) + console.log(transaction) + } else if (ins.type === "file") { + //@ts-ignore + const { request } = await ethPublicClient.simulateContract({ + account: ethWalletClient.account, + address: config.eth.contracts!.onchfs_file_system, + abi: ONCHFS_FILE_SYSTEM_ABI, + functionName: "createFile", + args: [ + bytesToHex(ins.metadata), + ins.chunks.map(chunk => bytesToHex(chunk)), + ], + }) + //@ts-ignore + const hash = await ethWalletClient.writeContract(request) + console.log(hash) + } else { + //@ts-ignore + const { request } = await ethPublicClient.simulateContract({ + account: ethWalletClient.account, + address: config.eth.contracts!.onchfs_file_system, + abi: ONCHFS_FILE_SYSTEM_ABI, + functionName: "createDirectory", + args: Object.entries(ins.files).reduce( + (acc, [name, content]) => [ + [...acc[0], name], + [...acc[1], bytesToHex(content)], + ], + [[], []] + ), + }) + //@ts-ignore + const hash = await ethWalletClient.writeContract(request) + console.log(hash) + } +} + async function main() { for (const f of files) { // to avoid some files if we want if (f.startsWith("_")) continue + let inscrs + console.log( "---------------------------------------------------------------" ) @@ -75,15 +173,28 @@ async function main() { if (!fs.lstatSync(root).isDirectory()) { const content = fs.readFileSync(path.join("tests", f)) - const inode = Onchfs.files.prepare({ - path: f, - content: content, - }) - const inscrs = Onchfs.inscriptions.prepare(inode) + const inode = Onchfs.files.prepare( + { + path: f, + content: content, + }, + { + fileHashingStrategy: "cheap", + } + ) + inscrs = Onchfs.inscriptions.prepare(inode) } // is durectory else { + console.log("is dir !!") + console.log(root) + walk(root, (err, res) => { + console.log("doooooooooone") + console.log({ res }) + }) + console.log(files) dir.files(root, async (err, files) => { + console.log("file found") if (err) throw err // for each file, get the content const inode = Onchfs.files.prepare( @@ -96,25 +207,30 @@ async function main() { }), { chunkSize: 2048, + fileHashingStrategy: "cheap", } ) - const inscrs = await Onchfs.inscriptions.prepare(inode, { - async inodeExists(cid) { - const res = await axios.get( - `${TZKT}/contracts/${KT_FILES}/bigmaps/inodes/keys/${cid}` - ) - return res.status === 200 - }, - async chunkExists(cid) { - const res = await axios.get(`${TZKT}/bigmaps/354463/keys/${cid}`) - return res.status === 200 - }, - }) - console.log(inscrs) - // for (const ins of inscrs) { - // await writeInscription(ins) - // } + inscrs = Onchfs.inscriptions.prepare(inode) + // const inscrs = await Onchfs.inscriptions.prepare(inode, { + // async inodeExists(cid) { + // const res = await axios.get( + // `${TZKT}/contracts/${KT_FILES}/bigmaps/inodes/keys/${cid}` + // ) + // return res.status === 200 + // }, + // async chunkExists(cid) { + // const res = await axios.get(`${TZKT}/bigmaps/354463/keys/${cid}`) + // return res.status === 200 + // }, + // }) }) + console.log("end read") + } + console.log("inscruptions") + console.log(inscrs) + for (const ins of inscrs) { + console.log("yooo") + await writeInscriptionEth(ins) } } } diff --git a/examples/test-project/tsconfig.json b/examples/test-project/tsconfig.json index 670b1a1..59a31b6 100644 --- a/examples/test-project/tsconfig.json +++ b/examples/test-project/tsconfig.json @@ -1,25 +1,16 @@ { "compilerOptions": { - "target": "ES2022", - "module": "Node16", - "lib": ["ES2022"], - "moduleResolution": "Node16", - "rootDir": "src", - "baseUrl": "src", + "lib": ["es6"], + "target": "es6", + "module": "commonjs", + "moduleResolution": "node", "outDir": "dist", - "allowSyntheticDefaultImports": true, - "importHelpers": true, - "alwaysStrict": true, - "sourceMap": true, - "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true, - "noImplicitReturns": true, - "noUnusedLocals": false, - "noUnusedParameters": true, - "noImplicitAny": false, - "noImplicitThis": false, - "strictNullChecks": false + "resolveJsonModule": true, + "emitDecoratorMetadata": true, + "esModuleInterop": true, + "experimentalDecorators": true, + "sourceMap": true }, - "include": ["src/**/*", "__tests__/**/*"], - "exclude": ["src/static/**"] + "include": ["src/**/*.ts"], + "exclude": ["node_modules", "**/*.spec.ts"] } diff --git a/packages/onchfs-js/src/cid/index.ts b/packages/onchfs-js/src/cid/index.ts new file mode 100644 index 0000000..92782b5 --- /dev/null +++ b/packages/onchfs-js/src/cid/index.ts @@ -0,0 +1,49 @@ +import { concatUint8Arrays, keccak } from "@/utils" +import { FileChunk, FileHashingStrategy } from ".." +import { INODE_BYTE_IDENTIFIER } from "@/config" + +/** + * Computes the CID of a file given its chunk parts, metadata, and the + * strategy which should be used for the computation. + * + * @param strategy The strategy with which a file CID is computed. Based on the + * blockchain, such strategy can be different for optimization purposes. + * @param chunks The chunks of the file, in the right Order_By + * @param metadata The metadata associated with the file, already compressed in + * the right byte format + * + * @returns CID of the gile given the strategy + */ +export function computeFileCid( + chunks: FileChunk[], + metadata: Uint8Array, + strategy: FileHashingStrategy +): Uint8Array { + if (strategy === "consistent") { + // compute the file unique identifier, following the onchfs specifications: + // keccak( 0x01 , keccak( content ), keccak( metadata ) ) + const wholeBytes = concatUint8Arrays(...chunks.map(ch => ch.bytes)) + const contentHash = keccak(wholeBytes) + const metadataHash = keccak(metadata) + return keccak( + concatUint8Arrays(INODE_BYTE_IDENTIFIER.FILE, contentHash, metadataHash) + ) + } else if (strategy === "cheap") { + // compute the file unique identifier, following the onchfs specifications: + // keccak( 0x01 , keccak( checksums ), keccak( metadata ) ) + const chunksChecksumsHashed = keccak( + concatUint8Arrays(...chunks.map(chunk => chunk.hash)) + ) + const metadataHash = keccak(metadata) + + return keccak( + concatUint8Arrays( + INODE_BYTE_IDENTIFIER.FILE, + chunksChecksumsHashed, + metadataHash + ) + ) + } else { + throw new Error("Cannot compute file CID: strategy is missing") + } +} diff --git a/packages/onchfs-js/src/files/directory.ts b/packages/onchfs-js/src/files/directory.ts index ae8fbfd..6b7b4c0 100644 --- a/packages/onchfs-js/src/files/directory.ts +++ b/packages/onchfs-js/src/files/directory.ts @@ -4,6 +4,7 @@ import { DirectoryInode, IFile, INode, + OnchfsPrepareOptions, PrepareDirectoryDir, PrepareDirectoryFile, PrepareDirectoryNode, @@ -147,7 +148,7 @@ export function buildDirectoryGraph( */ export function prepareDirectory( files: IFile[], - chunkSize: number = DEFAULT_CHUNK_SIZE + options: Required ): DirectoryInode { const [graph, leaves] = buildDirectoryGraph(files) @@ -165,7 +166,7 @@ export function prepareDirectory( path: node.name, content: node.content, }, - chunkSize + options ) } else if (node.type === "directory") { // compute the inode associated with the directory diff --git a/packages/onchfs-js/src/files/file.ts b/packages/onchfs-js/src/files/file.ts index 04b0c66..f7d1008 100644 --- a/packages/onchfs-js/src/files/file.ts +++ b/packages/onchfs-js/src/files/file.ts @@ -1,14 +1,12 @@ import { gzip } from "pako" -import { DEFAULT_CHUNK_SIZE, INODE_BYTE_IDENTIFIER } from "@/config" import { lookup as lookupMime } from "mime-types" import { chunkBytes } from "./chunks" -import { concatUint8Arrays, keccak } from "@/utils" import { FileMetadataEntries } from "@/types/metadata" import { encodeMetadata } from "@/metadata/encode" -import { FileInode, IFile } from "@/types/files" +import { FileInode, IFile, OnchfsPrepareOptions } from "@/types/files" import { u8hex } from "@/utils/uint8" import { decodeMetadata } from "@/metadata/decode" -// import { fileTypeFromBuffer } from "file-type" +import { computeFileCid } from "@/cid" /** * Computes all the necessary data for the inscription of the file on-chain. @@ -27,7 +25,7 @@ import { decodeMetadata } from "@/metadata/decode" */ export function prepareFile( file: IFile, - chunkSize: number = DEFAULT_CHUNK_SIZE + options: Required ): FileInode { const { path: name, content } = file let metadata: FileMetadataEntries = {} @@ -68,16 +66,14 @@ export function prepareFile( console.log({ metadata }) - // chunk the file - const chunks = chunkBytes(insertionBytes, chunkSize) - // encode the metadata + // chunk the file, encode its metadata and compute its CID based on provided + // hashing strategy + const chunks = chunkBytes(insertionBytes, options.chunkSize) const metadataEncoded = encodeMetadata(metadata) - // compute the file unique identifier, following the onchfs specifications: - // keccak( 0x01 , keccak( content ), keccak( metadata ) ) - const contentHash = keccak(insertionBytes) - const metadataHash = keccak(metadataEncoded) - const cid = keccak( - concatUint8Arrays(INODE_BYTE_IDENTIFIER.FILE, contentHash, metadataHash) + const cid = computeFileCid( + chunks, + metadataEncoded, + options.fileHashingStrategy ) console.log({ diff --git a/packages/onchfs-js/src/files/prepare.ts b/packages/onchfs-js/src/files/prepare.ts index 6c30d96..8289e32 100644 --- a/packages/onchfs-js/src/files/prepare.ts +++ b/packages/onchfs-js/src/files/prepare.ts @@ -10,6 +10,7 @@ import { prepareFile } from "./file" const defaultPrepareOptions: Required = { chunkSize: DEFAULT_CHUNK_SIZE, + fileHashingStrategy: "consistent", } /** @@ -93,8 +94,8 @@ export function prepare( ...(options || {}), } if (Array.isArray(files)) { - return prepareDirectory(files, _options.chunkSize) + return prepareDirectory(files, _options) } else { - return prepareFile(files, _options.chunkSize) + return prepareFile(files, _options) } } diff --git a/packages/onchfs-js/src/types/files.ts b/packages/onchfs-js/src/types/files.ts index 0fce4b6..ead1f40 100644 --- a/packages/onchfs-js/src/types/files.ts +++ b/packages/onchfs-js/src/types/files.ts @@ -50,6 +50,25 @@ export type PrepareDirectoryDir = { export type PrepareDirectoryNode = PrepareDirectoryFile | PrepareDirectoryDir +/** + * How the File cids are constructed. Some blockchains are expensive and as + * such having a consistent cid generation on the input bytes might be quite + * expensive, as such the file system supports 2 ways of hashing files: + * - consistent: hash the whole file content + * - cheap: hash the checksums of the file chunks + */ +export type FileHashingStrategy = "consistent" | "cheap" + export interface OnchfsPrepareOptions { + /** + * The size of the chunks. Leaving is as default will fallback to onchfs + * default chunk size, which is meant to be optimized for most purposes. + */ chunkSize?: number + + /** + * The strategy which will be used for computing file cids from their + * chunks. + */ + fileHashingStrategy?: FileHashingStrategy } From 468b36fc7d32bc697b943ca886ce651ee0fc99da Mon Sep 17 00:00:00 2001 From: ciphrd Date: Thu, 30 Nov 2023 04:07:04 +0100 Subject: [PATCH 15/61] onchfs working!! --- packages/onchfs-js/package.json | 3 +- packages/onchfs-js/src/config.ts | 4 +- packages/onchfs-js/src/resolver/proxy.ts | 52 ++++++++- packages/onchfs-js/src/utils/abi.ts | 135 +++++++++++++++++++++++ 4 files changed, 189 insertions(+), 5 deletions(-) create mode 100644 packages/onchfs-js/src/utils/abi.ts diff --git a/packages/onchfs-js/package.json b/packages/onchfs-js/package.json index d562a64..6fb30e4 100644 --- a/packages/onchfs-js/package.json +++ b/packages/onchfs-js/package.json @@ -26,7 +26,8 @@ "hpack.js": "2.1.6", "js-sha3": "0.9.1", "mime-types": "2.1.35", - "pako": "2.1.0" + "pako": "2.1.0", + "viem": "1.14.0" }, "devDependencies": { "@babel/core": "7.22.19", diff --git a/packages/onchfs-js/src/config.ts b/packages/onchfs-js/src/config.ts index 0e34f12..28e126e 100644 --- a/packages/onchfs-js/src/config.ts +++ b/packages/onchfs-js/src/config.ts @@ -31,6 +31,6 @@ export const DEFAULT_CONTRACTS: Record = { "tezos:NetXdQprcVkpaWU": "KT1Ae7dT1gsLw2tRnUMXSCmEyF74KVkM6LUo", // tezos ghostnet "tezos:NetXnHfVqm9iesp": "KT1FA8AGGcJha6S6MqfBUiibwTaYhK8u7s9Q", - "eip155:1": "b0e58801d1b4d69179b7bc23fe54a37cee999b09", - "eip155:5": "fcfdfa971803e1cc201f80d8e74de71fddea6551", + "eip155:1": "0x1c958168beb52bed41bd0c2301ae7bf2cbd0af46", + "eip155:5": "0x1c958168beb52bed41bd0c2301ae7bf2cbd0af46", } diff --git a/packages/onchfs-js/src/resolver/proxy.ts b/packages/onchfs-js/src/resolver/proxy.ts index 73c2560..fabbaaf 100644 --- a/packages/onchfs-js/src/resolver/proxy.ts +++ b/packages/onchfs-js/src/resolver/proxy.ts @@ -21,7 +21,6 @@ import { BlockchainNetwork, URIAuthority, URISchemaSpecificParts, - blockchainNames, blockchainNetworks, } from "@/types/uri" import { @@ -35,6 +34,8 @@ import { ContractProvider, } from "@taquito/taquito" import { DEFAULT_CONTRACTS } from "@/config" +import { createPublicClient, fallback, hexToBytes, http } from "viem" +import { ONCHFS_FILE_SYSTEM_ABI } from "@/utils/abi" const ResolutionErrors: Record = { [ProxyResolutionStatusErrors.BAD_REQUEST]: "Bad Request", @@ -175,7 +176,54 @@ export function createProxyResolver(controllers: BlockchainResolverCtrl[]) { } } case "eip155": { - throw new Error("Implement eth resolver!") + const publicClient = createPublicClient({ + transport: fallback(h.rpcs.map(rpc => http(rpc))), + }) + return { + blockchain: h.blockchain, + resolverWithContract: (address?: string) => { + // default blockchain address if not specified + address = address || DEFAULT_CONTRACTS[baseChainId] + if (!address) { + throw new Error( + `no contract address was found; neither can it be inferred from the context (${h.blockchain}) nor has it been provided during resolution.` + ) + } + + return { + getInodeAtPath: async () => + // cid: string, + // path: string[], + // authority?: URIAuthority + { + // TODO: properly implement returning a File Object + if (true) { + return { + cid: "...", + files: {}, + } + } + // else { + // return { + // cid: "...", + // metadata: "...", + // chunkPointers: [], + // } + // } + }, + readFile: async (cid: string) => { + //@ts-ignore + const hexBytesString = await publicClient.readContract({ + address: address as `0x${string}`, + abi: ONCHFS_FILE_SYSTEM_ABI, + functionName: "readFile", + args: [`0x${cid}`], + }) + return hexToBytes(hexBytesString as any) + }, + } + }, + } } } }) diff --git a/packages/onchfs-js/src/utils/abi.ts b/packages/onchfs-js/src/utils/abi.ts new file mode 100644 index 0000000..e37c232 --- /dev/null +++ b/packages/onchfs-js/src/utils/abi.ts @@ -0,0 +1,135 @@ +export const ONCHFS_FILE_SYSTEM_ABI = [ + { + inputs: [ + { internalType: "address", name: "_contentStore", type: "address" }, + ], + stateMutability: "nonpayable", + type: "constructor", + }, + { inputs: [], name: "ChunkNotFound", type: "error" }, + { inputs: [], name: "DirectoryNotFound", type: "error" }, + { inputs: [], name: "FileNotFound", type: "error" }, + { inputs: [], name: "InodeAlreadyExists", type: "error" }, + { inputs: [], name: "InodeNotFound", type: "error" }, + { inputs: [], name: "InvalidCharacter", type: "error" }, + { + inputs: [ + { internalType: "uint256", name: "_size", type: "uint256" }, + { internalType: "uint256", name: "_start", type: "uint256" }, + { internalType: "uint256", name: "_end", type: "uint256" }, + ], + name: "InvalidCodeAtRange", + type: "error", + }, + { inputs: [], name: "LengthMismatch", type: "error" }, + { + inputs: [], + name: "CONTENT_STORE", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32[]", name: "_pointers", type: "bytes32[]" }, + ], + name: "concatenateChunks", + outputs: [{ internalType: "bytes", name: "fileContent", type: "bytes" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "string[]", name: "_fileNames", type: "string[]" }, + { internalType: "bytes32[]", name: "_inodeChecksums", type: "bytes32[]" }, + ], + name: "createDirectory", + outputs: [ + { internalType: "bytes32", name: "directoryChecksum", type: "bytes32" }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes", name: "_metadata", type: "bytes" }, + { internalType: "bytes32[]", name: "_chunkPointers", type: "bytes32[]" }, + ], + name: "createFile", + outputs: [ + { internalType: "bytes32", name: "fileChecksum", type: "bytes32" }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "string[]", name: "_fileNames", type: "string[]" }, + ], + name: "hashFileNames", + outputs: [ + { internalType: "bytes32[]", name: "hashedPaths", type: "bytes32[]" }, + ], + stateMutability: "pure", + type: "function", + }, + { + inputs: [{ internalType: "bytes32", name: "_checksum", type: "bytes32" }], + name: "inodeExists", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "bytes32", name: "checksum", type: "bytes32" }], + name: "inodes", + outputs: [ + { internalType: "enum InodeType", name: "inodeType", type: "uint8" }, + { + components: [ + { internalType: "bytes", name: "name", type: "bytes" }, + { + internalType: "bytes32[]", + name: "chunkChecksums", + type: "bytes32[]", + }, + ], + internalType: "struct File", + name: "file", + type: "tuple", + }, + { + components: [ + { internalType: "string[]", name: "paths", type: "string[]" }, + { + internalType: "bytes32[]", + name: "fileChecksums", + type: "bytes32[]", + }, + ], + internalType: "struct Directory", + name: "directory", + type: "tuple", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "bytes32", name: "_checksum", type: "bytes32" }], + name: "readDirectory", + outputs: [ + { internalType: "string[]", name: "", type: "string[]" }, + { internalType: "bytes32[]", name: "", type: "bytes32[]" }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "bytes32", name: "_checksum", type: "bytes32" }], + name: "readFile", + outputs: [{ internalType: "bytes", name: "", type: "bytes" }], + stateMutability: "view", + type: "function", + }, +] From bd7b3c885a889be8929dce7dfb56c3e23e03b762 Mon Sep 17 00:00:00 2001 From: ciphrd Date: Thu, 30 Nov 2023 17:12:30 +0100 Subject: [PATCH 16/61] dump --- packages/onchfs-js/src/config.ts | 4 +- packages/onchfs-js/src/files/file.ts | 22 ----- packages/onchfs-js/src/resolver/proxy.ts | 82 +++++++++++++---- packages/onchfs-js/src/types/eth.ts | 22 +++++ packages/onchfs-js/src/utils/abi.ts | 111 +++++++++++++++++++++-- 5 files changed, 191 insertions(+), 50 deletions(-) create mode 100644 packages/onchfs-js/src/types/eth.ts diff --git a/packages/onchfs-js/src/config.ts b/packages/onchfs-js/src/config.ts index 28e126e..ea185a0 100644 --- a/packages/onchfs-js/src/config.ts +++ b/packages/onchfs-js/src/config.ts @@ -31,6 +31,6 @@ export const DEFAULT_CONTRACTS: Record = { "tezos:NetXdQprcVkpaWU": "KT1Ae7dT1gsLw2tRnUMXSCmEyF74KVkM6LUo", // tezos ghostnet "tezos:NetXnHfVqm9iesp": "KT1FA8AGGcJha6S6MqfBUiibwTaYhK8u7s9Q", - "eip155:1": "0x1c958168beb52bed41bd0c2301ae7bf2cbd0af46", - "eip155:5": "0x1c958168beb52bed41bd0c2301ae7bf2cbd0af46", + "eip155:1": "0xc3f5ef1a0256b9ceb1452650db72344809bb3a85", + "eip155:5": "0xc3f5ef1a0256b9ceb1452650db72344809bb3a85", } diff --git a/packages/onchfs-js/src/files/file.ts b/packages/onchfs-js/src/files/file.ts index f7d1008..cbca616 100644 --- a/packages/onchfs-js/src/files/file.ts +++ b/packages/onchfs-js/src/files/file.ts @@ -33,14 +33,6 @@ export function prepareFile( // we use file extension to get mime type let mime = lookupMime(name) - console.log("--------------------------------------------------------------") - console.log("PREPARE FILE") - console.log("----------------------------------") - - console.log({ file }) - - console.log({ mime }) - // if no mime type can be mapped from filename, use magic number if (!mime) { // const magicMime = await fileTypeFromBuffer(content) @@ -55,7 +47,6 @@ export function prepareFile( } else { metadata["Content-Type"] = mime } - console.log({ metadata }) // compress into gzip using node zopfli, only keep if better const compressed = gzip(content) @@ -64,8 +55,6 @@ export function prepareFile( metadata["Content-Encoding"] = "gzip" } - console.log({ metadata }) - // chunk the file, encode its metadata and compute its CID based on provided // hashing strategy const chunks = chunkBytes(insertionBytes, options.chunkSize) @@ -76,17 +65,6 @@ export function prepareFile( options.fileHashingStrategy ) - console.log({ - type: "file", - cid, - chunks, - metadata: metadataEncoded, - }) - - console.log("---DECODED METADATA") - console.log(u8hex(metadataEncoded)) - console.log(decodeMetadata(metadataEncoded)) - return { type: "file", cid, diff --git a/packages/onchfs-js/src/resolver/proxy.ts b/packages/onchfs-js/src/resolver/proxy.ts index fabbaaf..0e60b55 100644 --- a/packages/onchfs-js/src/resolver/proxy.ts +++ b/packages/onchfs-js/src/resolver/proxy.ts @@ -34,8 +34,15 @@ import { ContractProvider, } from "@taquito/taquito" import { DEFAULT_CONTRACTS } from "@/config" -import { createPublicClient, fallback, hexToBytes, http } from "viem" +import { + createPublicClient, + encodeFunctionData, + fallback, + hexToBytes, + http, +} from "viem" import { ONCHFS_FILE_SYSTEM_ABI } from "@/utils/abi" +import { EthInode, EthInodeType } from "@/types/eth" const ResolutionErrors: Record = { [ProxyResolutionStatusErrors.BAD_REQUEST]: "Bad Request", @@ -191,26 +198,63 @@ export function createProxyResolver(controllers: BlockchainResolverCtrl[]) { } return { - getInodeAtPath: async () => - // cid: string, - // path: string[], - // authority?: URIAuthority - { - // TODO: properly implement returning a File Object - if (true) { - return { - cid: "...", - files: {}, + getInodeAtPath: async (cid: string, path: string[]) => { + try { + //@ts-ignore + const fdata = encodeFunctionData({ + abi: ONCHFS_FILE_SYSTEM_ABI, + functionName: "getInodeAt", + args: [`0x${cid}`, path], + }) + + console.log({ fdata }) + //@ts-ignore + const out: [`0x${string}`, EthInode] = await ( + publicClient as any + ).readContract({ + address: address as `0x${string}`, + abi: ONCHFS_FILE_SYSTEM_ABI, + functionName: "getInodeAt", + args: [`0x${cid}`, path], + }) + console.log({ out }) + if (out && (out as any)?.length === 2) { + const cid = out[0].replace("0x", "") + const inode = out[1] + + // if the contract has answered with a directory + if (inode.inodeType === EthInodeType.DIRECTORY) { + const dir = inode.directory + const files: Record = {} + for (let i = 0; i < dir.filenames.length; i++) { + files[dir.filenames[i]] = dir.fileChecksums[i].replace( + "0x", + "" + ) + } + return { + cid, + files, + } + } else { + const file = inode.file + // the contract has answered with a file + return { + cid, + chunkPointers: file.chunkChecksums.map(pt => + pt.replace("0x", "") + ), + metadata: file.metadata.replace("0x", ""), + } } + } else { + throw new Error("wrogn response from contract") } - // else { - // return { - // cid: "...", - // metadata: "...", - // chunkPointers: [], - // } - // } - }, + } catch (err) { + console.log(err) + return null + } + }, readFile: async (cid: string) => { //@ts-ignore const hexBytesString = await publicClient.readContract({ diff --git a/packages/onchfs-js/src/types/eth.ts b/packages/onchfs-js/src/types/eth.ts new file mode 100644 index 0000000..c01123a --- /dev/null +++ b/packages/onchfs-js/src/types/eth.ts @@ -0,0 +1,22 @@ +export enum EthInodeType { + DIRECTORY = 0, + FILE = 1, +} + +export type EthInodeFile = { + inodeType: EthInodeType.FILE + file: { + metadata: `0x${string}` + chunkChecksums: `0x${string}`[] + } +} + +export type EthInodeDirectory = { + inodeType: EthInodeType.DIRECTORY + directory: { + filenames: string[] + fileChecksums: `0x${string}`[] + } +} + +export type EthInode = EthInodeFile | EthInodeDirectory diff --git a/packages/onchfs-js/src/utils/abi.ts b/packages/onchfs-js/src/utils/abi.ts index e37c232..7a53762 100644 --- a/packages/onchfs-js/src/utils/abi.ts +++ b/packages/onchfs-js/src/utils/abi.ts @@ -9,7 +9,6 @@ export const ONCHFS_FILE_SYSTEM_ABI = [ { inputs: [], name: "ChunkNotFound", type: "error" }, { inputs: [], name: "DirectoryNotFound", type: "error" }, { inputs: [], name: "FileNotFound", type: "error" }, - { inputs: [], name: "InodeAlreadyExists", type: "error" }, { inputs: [], name: "InodeNotFound", type: "error" }, { inputs: [], name: "InvalidCharacter", type: "error" }, { @@ -21,7 +20,58 @@ export const ONCHFS_FILE_SYSTEM_ABI = [ name: "InvalidCodeAtRange", type: "error", }, + { inputs: [], name: "InvalidFileName", type: "error" }, { inputs: [], name: "LengthMismatch", type: "error" }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "bytes32", + name: "_checksum", + type: "bytes32", + }, + { + indexed: false, + internalType: "string[]", + name: "_names", + type: "string[]", + }, + { + indexed: false, + internalType: "bytes32[]", + name: "_inodeChecksums", + type: "bytes32[]", + }, + ], + name: "DirectoryCreated", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "bytes32", + name: "_checksum", + type: "bytes32", + }, + { + indexed: false, + internalType: "bytes", + name: "metadata", + type: "bytes", + }, + { + indexed: false, + internalType: "bytes32[]", + name: "_chunkPointers", + type: "bytes32[]", + }, + ], + name: "FileCreated", + type: "event", + }, { inputs: [], name: "CONTENT_STORE", @@ -38,6 +88,18 @@ export const ONCHFS_FILE_SYSTEM_ABI = [ stateMutability: "view", type: "function", }, + { + inputs: [ + { internalType: "string[]", name: "_fileNames", type: "string[]" }, + { internalType: "bytes32[]", name: "_filePointers", type: "bytes32[]" }, + ], + name: "concatenateFiles", + outputs: [ + { internalType: "bytes", name: "concatenatedFiles", type: "bytes" }, + ], + stateMutability: "pure", + type: "function", + }, { inputs: [ { internalType: "string[]", name: "_fileNames", type: "string[]" }, @@ -64,13 +126,48 @@ export const ONCHFS_FILE_SYSTEM_ABI = [ }, { inputs: [ - { internalType: "string[]", name: "_fileNames", type: "string[]" }, + { internalType: "bytes32", name: "_inodeChecksum", type: "bytes32" }, + { internalType: "string[]", name: "_pathSegments", type: "string[]" }, ], - name: "hashFileNames", + name: "getInodeAt", outputs: [ - { internalType: "bytes32[]", name: "hashedPaths", type: "bytes32[]" }, + { internalType: "bytes32", name: "", type: "bytes32" }, + { + components: [ + { internalType: "enum InodeType", name: "inodeType", type: "uint8" }, + { + components: [ + { internalType: "bytes", name: "metadata", type: "bytes" }, + { + internalType: "bytes32[]", + name: "chunkChecksums", + type: "bytes32[]", + }, + ], + internalType: "struct File", + name: "file", + type: "tuple", + }, + { + components: [ + { internalType: "string[]", name: "filenames", type: "string[]" }, + { + internalType: "bytes32[]", + name: "fileChecksums", + type: "bytes32[]", + }, + ], + internalType: "struct Directory", + name: "directory", + type: "tuple", + }, + ], + internalType: "struct Inode", + name: "", + type: "tuple", + }, ], - stateMutability: "pure", + stateMutability: "view", type: "function", }, { @@ -87,7 +184,7 @@ export const ONCHFS_FILE_SYSTEM_ABI = [ { internalType: "enum InodeType", name: "inodeType", type: "uint8" }, { components: [ - { internalType: "bytes", name: "name", type: "bytes" }, + { internalType: "bytes", name: "metadata", type: "bytes" }, { internalType: "bytes32[]", name: "chunkChecksums", @@ -100,7 +197,7 @@ export const ONCHFS_FILE_SYSTEM_ABI = [ }, { components: [ - { internalType: "string[]", name: "paths", type: "string[]" }, + { internalType: "string[]", name: "filenames", type: "string[]" }, { internalType: "bytes32[]", name: "fileChecksums", From 366e0497a20d41ef11ba58259a8d3c88ae1762c0 Mon Sep 17 00:00:00 2001 From: ciphrd Date: Thu, 30 Nov 2023 17:45:48 +0100 Subject: [PATCH 17/61] onchfs-proxy working --- packages/onchfs-js/src/resolver/proxy.ts | 66 +++++++++++------------- 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/packages/onchfs-js/src/resolver/proxy.ts b/packages/onchfs-js/src/resolver/proxy.ts index 0e60b55..b5886e9 100644 --- a/packages/onchfs-js/src/resolver/proxy.ts +++ b/packages/onchfs-js/src/resolver/proxy.ts @@ -200,14 +200,6 @@ export function createProxyResolver(controllers: BlockchainResolverCtrl[]) { return { getInodeAtPath: async (cid: string, path: string[]) => { try { - //@ts-ignore - const fdata = encodeFunctionData({ - abi: ONCHFS_FILE_SYSTEM_ABI, - functionName: "getInodeAt", - args: [`0x${cid}`, path], - }) - - console.log({ fdata }) //@ts-ignore const out: [`0x${string}`, EthInode] = await ( publicClient as any @@ -217,7 +209,6 @@ export function createProxyResolver(controllers: BlockchainResolverCtrl[]) { functionName: "getInodeAt", args: [`0x${cid}`, path], }) - console.log({ out }) if (out && (out as any)?.length === 2) { const cid = out[0].replace("0x", "") const inode = out[1] @@ -251,7 +242,6 @@ export function createProxyResolver(controllers: BlockchainResolverCtrl[]) { throw new Error("wrogn response from contract") } } catch (err) { - console.log(err) return null } }, @@ -302,43 +292,47 @@ export function createProxyResolver(controllers: BlockchainResolverCtrl[]) { const resolvers = orderedResolversFromAuthority(authority) // try finding the resource on every resolver let res: InodeNativeFS | null = null - for (const resolver of resolvers) { - try { - res = await resolver - .resolverWithContract(authority?.contract) - .getInodeAtPath(cid, path) - break - } catch (err) { - continue - } - } - - if (res) return res - else + try { + res = await Promise.any( + resolvers.map(async resolver => { + const resp = await resolver + .resolverWithContract(authority?.contract) + .getInodeAtPath(cid, path) + if (resp) return resp + throw Error("file not found") + }) + ) + if (res) return res + throw null + } catch (err) { + console.log(err) throw new Error( "searched all available blockchains, resource not found." ) + } }, async readFile(cid, chunkPointers, authority) { const resolvers = orderedResolversFromAuthority(authority) // try finding the resource on every resolver let res: string | Uint8Array | null = null - for (const resolver of resolvers) { - try { - res = await resolver - .resolverWithContract(authority?.contract) - .readFile(cid, chunkPointers) - break - } catch (err) { - continue - } - } - - if (res) return res - else + try { + res = await Promise.any( + resolvers.map(async resolver => { + const resp = await resolver + .resolverWithContract(authority?.contract) + .readFile(cid, chunkPointers) + if (resp) return resp + throw Error("file not found") + }) + ) + if (res) return res + throw null + } catch (err) { + console.log(err) throw new Error( "searched all available blockchains, resource not found." ) + } }, }) } From e1def3b6980b27e5d4d3be187844cb6f3f5981bc Mon Sep 17 00:00:00 2001 From: ciphrd Date: Thu, 30 Nov 2023 17:45:55 +0100 Subject: [PATCH 18/61] removed some logs --- packages/onchfs-js/src/inscriptions/prepare.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/onchfs-js/src/inscriptions/prepare.ts b/packages/onchfs-js/src/inscriptions/prepare.ts index 626f8f0..1de002c 100644 --- a/packages/onchfs-js/src/inscriptions/prepare.ts +++ b/packages/onchfs-js/src/inscriptions/prepare.ts @@ -148,7 +148,6 @@ export function prepareInscriptions( const results = await Promise.allSettled( node.chunks.map(chunk => resolver.chunkExists(u8hex(chunk.hash))) ) - console.log(results) const chunksToInsert = node.chunks.filter((_, i) => { const res = results[i] return res.status === "fulfilled" && !res.value From ee160c76aeab06e5068e9dd7aa5b28f40f9002a5 Mon Sep 17 00:00:00 2001 From: ciphrd Date: Thu, 30 Nov 2023 23:56:32 +0100 Subject: [PATCH 19/61] update onchfs file system address (final) --- packages/onchfs-js/src/config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/onchfs-js/src/config.ts b/packages/onchfs-js/src/config.ts index ea185a0..7a71f93 100644 --- a/packages/onchfs-js/src/config.ts +++ b/packages/onchfs-js/src/config.ts @@ -31,6 +31,6 @@ export const DEFAULT_CONTRACTS: Record = { "tezos:NetXdQprcVkpaWU": "KT1Ae7dT1gsLw2tRnUMXSCmEyF74KVkM6LUo", // tezos ghostnet "tezos:NetXnHfVqm9iesp": "KT1FA8AGGcJha6S6MqfBUiibwTaYhK8u7s9Q", - "eip155:1": "0xc3f5ef1a0256b9ceb1452650db72344809bb3a85", - "eip155:5": "0xc3f5ef1a0256b9ceb1452650db72344809bb3a85", + "eip155:1": "0x9e0f2864c6f125bbf599df6ca6e6c3774c5b2e04", + "eip155:5": "0x9e0f2864c6f125bbf599df6ca6e6c3774c5b2e04", } From 0a4eea3b0676744ccf478e99c4b27c253da06168 Mon Sep 17 00:00:00 2001 From: ciphrd Date: Tue, 5 Dec 2023 23:33:26 +0100 Subject: [PATCH 20/61] onchfs utils to compute upload summary --- packages/onchfs-js/src/files/index.ts | 1 + packages/onchfs-js/src/files/summary.ts | 133 ++++++++++++++++++ .../onchfs-js/src/inscriptions/estimate.ts | 6 +- packages/onchfs-js/src/types/files.ts | 55 ++++++++ 4 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 packages/onchfs-js/src/files/summary.ts diff --git a/packages/onchfs-js/src/files/index.ts b/packages/onchfs-js/src/files/index.ts index ef843b3..df9c241 100644 --- a/packages/onchfs-js/src/files/index.ts +++ b/packages/onchfs-js/src/files/index.ts @@ -5,6 +5,7 @@ import { encodeFilename, } from "./directory" +export { directoryUploadSummary as uploadSummary } from "./summary" export { prepare } from "./prepare" export const utils = { chunkBytes, diff --git a/packages/onchfs-js/src/files/summary.ts b/packages/onchfs-js/src/files/summary.ts new file mode 100644 index 0000000..35c3d8c --- /dev/null +++ b/packages/onchfs-js/src/files/summary.ts @@ -0,0 +1,133 @@ +import { + DirectoryInode, + FileChunk, + FileInode, + FileUploadProgress, + Inscription, + UploadProgress, + UploadSummary, +} from ".." + +/** + * Given a directory inode, computes an upload summary based on the inscriptions + * which are missing for the directory to be fully uploaded. + * @param node The Directory node for which the summary needs to be computed. + * @param missingInscriptions The inscriptions which need to be inscribed for + * the directory to be fully uploaded. + */ +export function directoryUploadSummary( + node: DirectoryInode, + missingInscriptions: Inscription[] +): UploadSummary { + const files: FileUploadProgress[] = [] + const extra: UploadProgress = { + total: 0, + left: 0, + } + const global: UploadProgress = { + total: 0, + left: 0, + } + + // recursively parse a directory (& sub-directories) while populating the + // upload summary as files are being traversed + function parse(node: DirectoryInode, path = "") { + for (const name in node.files) { + const N = node.files[name] + if (N.type === "file") { + const progress = fileUploadProgress(N, missingInscriptions) + files.push({ + path: path + name, + progress, + }) + global.total += progress.total + global.left += progress.left + } else if (N.type === "directory") { + parse(N, path + name + "/") + } else { + throw new Error("unsupported node type, this should never be reach!") + } + } + + if ( + missingInscriptions.find( + ins => ins.type === "directory" && ins.cid === node.cid + ) + ) { + //todo: this suppose node.files.name < 32 bytes, but it could be improved + // by computing the accurate size here + const dirSize = Object.keys(node.files).length * 32 * 2 + 32 + extra.total += dirSize + extra.left += dirSize + global.total += dirSize + global.left += dirSize + } + } + parse(node) + + return { + global, + files, + extraPayload: extra, + } +} + +/** + * Given a file node and some missing inscriptions, computes the progress of + * the upload on Onchfs. + * @param node File node + * @param missingInscriptions A list of inscriptions which are missing for the + * file to be fully uploaded. Inscriptions are not strictly constrained to being + * part of the file, they can be a bigger set of inscriptions wherein the file + * inscriptions are contained. + * @returns Progress details about the file upload. + */ +export function fileUploadProgress( + node: FileInode, + missingInscriptions: Inscription[] +): UploadProgress { + // chunk bytes, 2x the chunk hashes (for storing chunks + for referencing + // chunk in the file), the file hash + const total = + node.chunks.reduce((acc, chunk) => acc + chunk.bytes.length, 0) + + // add the size of storing 2x chunk hashes + node.chunks.length * 2 * 32 + + // the hash of the file + 32 + + // if the file exists in the inscriptions, we can just return full upload + if ( + !missingInscriptions.find( + ins => ins.type === "file" && ins.cid === node.cid + ) + ) { + return { + total, + left: 0, + } + } + + // find the chunks which are missing for the inscription + const missingChunks: FileChunk[] = [] + for (const chunk of node.chunks) { + if ( + missingInscriptions.find( + ins => ins.type === "chunk" && ins.hash === chunk.hash + ) + ) { + missingChunks.push(chunk) + } + } + + return { + total, + left: + missingChunks.reduce((acc, chunk) => acc + chunk.bytes.length, 0) + + // for each chunk, we need to store chunk hash + missingChunks.length * 32 + + // file node must reference all the chunks + node.chunks.length * 32 + + // file cid pointer + 32, + } +} diff --git a/packages/onchfs-js/src/inscriptions/estimate.ts b/packages/onchfs-js/src/inscriptions/estimate.ts index 5762a0d..2c1ed21 100644 --- a/packages/onchfs-js/src/inscriptions/estimate.ts +++ b/packages/onchfs-js/src/inscriptions/estimate.ts @@ -1,4 +1,8 @@ -import { Inscription } from "@/types/inscriptions" +import { + Inscription, + InscriptionDirectory, + InscriptionFile, +} from "@/types/inscriptions" /** * Compute the number of bytes an inscription will take on the storage. diff --git a/packages/onchfs-js/src/types/files.ts b/packages/onchfs-js/src/types/files.ts index ead1f40..25092ae 100644 --- a/packages/onchfs-js/src/types/files.ts +++ b/packages/onchfs-js/src/types/files.ts @@ -72,3 +72,58 @@ export interface OnchfsPrepareOptions { */ fileHashingStrategy?: FileHashingStrategy } + +/** + * Describes the state of an upload, in terms of size. Such an object can be + * attached to any entity which can be stored in order to represent its storage + * state. + */ +export interface UploadProgress { + /** + * The total size required for storing the full object from scratch, in bytes. + */ + total: number + /** + * The number of bytes left. + */ + left: number +} + +/** + * Progress related to a file upload. + */ +export interface FileUploadProgress { + /** + * **Absolute path** of the file from the root of the object uploaded. + */ + path: string + + /** + * Progress of the upload, includes the progress of the upload of the file + * chunks. + */ + progress: UploadProgress +} + +/** + * The summary of an upload of Inscriptions, defining the upload state of the + * various components of an upload on Onchfs + */ +export interface UploadSummary { + /** + * Global progress of the upload. It can be derived from the progress of the + * files & extra payload, but is given for convenience. + */ + global: UploadProgress + + /** + * A list of upload summary for all the files inside the directory, where + */ + files: FileUploadProgress[] + + /** + * Some extra payload, mainly used as a reference for non-visible objects + * such as directories which can have a significant print sometimes. + */ + extraPayload: UploadProgress +} From bf3e5b0308d2a72a95d96de738858b9e7c432f4d Mon Sep 17 00:00:00 2001 From: ciphrd Date: Wed, 6 Dec 2023 22:19:05 +0100 Subject: [PATCH 21/61] show modal for library issues --- packages/onchfs-js/src/files/file.ts | 5 +++-- packages/onchfs-js/src/files/summary.ts | 1 + packages/onchfs-js/src/types/files.ts | 8 ++++++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/onchfs-js/src/files/file.ts b/packages/onchfs-js/src/files/file.ts index cbca616..774692b 100644 --- a/packages/onchfs-js/src/files/file.ts +++ b/packages/onchfs-js/src/files/file.ts @@ -4,8 +4,6 @@ import { chunkBytes } from "./chunks" import { FileMetadataEntries } from "@/types/metadata" import { encodeMetadata } from "@/metadata/encode" import { FileInode, IFile, OnchfsPrepareOptions } from "@/types/files" -import { u8hex } from "@/utils/uint8" -import { decodeMetadata } from "@/metadata/decode" import { computeFileCid } from "@/cid" /** @@ -70,5 +68,8 @@ export function prepareFile( cid, chunks, metadata: metadataEncoded, + source: { + content: file.content, + }, } } diff --git a/packages/onchfs-js/src/files/summary.ts b/packages/onchfs-js/src/files/summary.ts index 35c3d8c..4f3ff50 100644 --- a/packages/onchfs-js/src/files/summary.ts +++ b/packages/onchfs-js/src/files/summary.ts @@ -38,6 +38,7 @@ export function directoryUploadSummary( const progress = fileUploadProgress(N, missingInscriptions) files.push({ path: path + name, + inode: N, progress, }) global.total += progress.total diff --git a/packages/onchfs-js/src/types/files.ts b/packages/onchfs-js/src/types/files.ts index 25092ae..1ceeb20 100644 --- a/packages/onchfs-js/src/types/files.ts +++ b/packages/onchfs-js/src/types/files.ts @@ -8,6 +8,9 @@ export type FileInode = { chunks: FileChunk[] cid: Uint8Array metadata: Uint8Array + source: { + content: Uint8Array + } } export type DirectoryInode = { @@ -98,6 +101,11 @@ export interface FileUploadProgress { */ path: string + /** + * The file Inode which is associated to the given file. + */ + inode: FileInode + /** * Progress of the upload, includes the progress of the upload of the file * chunks. From 284535ca398f8b75b9578a4254f22a77cd7b066e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Pradel?= Date: Wed, 17 Jan 2024 13:50:17 +0100 Subject: [PATCH 22/61] [repo] feat: move prettier config to the root of the repo (#853) --- examples/http-proxy/.eslintrc.json | 6 +----- examples/http-proxy/.prettierrc | 1 - examples/http-proxy/package.json | 1 - 3 files changed, 1 insertion(+), 7 deletions(-) delete mode 100644 examples/http-proxy/.prettierrc diff --git a/examples/http-proxy/.eslintrc.json b/examples/http-proxy/.eslintrc.json index e679ac7..cdafc82 100644 --- a/examples/http-proxy/.eslintrc.json +++ b/examples/http-proxy/.eslintrc.json @@ -1,7 +1,3 @@ { - "plugins": ["prettier"], - "extends": ["@fxhash/eslint-config"], - "rules": { - "prettier/prettier": "error" - } + "extends": ["@fxhash/eslint-config"] } diff --git a/examples/http-proxy/.prettierrc b/examples/http-proxy/.prettierrc deleted file mode 100644 index 15dc90f..0000000 --- a/examples/http-proxy/.prettierrc +++ /dev/null @@ -1 +0,0 @@ -"@fxhash/prettier-config" diff --git a/examples/http-proxy/package.json b/examples/http-proxy/package.json index e5e82ad..c6a95b3 100644 --- a/examples/http-proxy/package.json +++ b/examples/http-proxy/package.json @@ -23,7 +23,6 @@ }, "devDependencies": { "@fxhash/eslint-config": "workspace:*", - "@fxhash/prettier-config": "workspace:*", "@types/cors": "2.8.13", "@types/node": "16.18.38", "nodemon": "2.0.22", From cba78a63065362a65a5fe59cdc5e9b96a1b22dd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Pradel?= Date: Wed, 17 Jan 2024 14:35:15 +0100 Subject: [PATCH 23/61] [repo] feat: add prettier check on the CI (#854) --- doc/babel.config.js | 4 +- doc/src/components/HomepageFeatures/index.tsx | 36 ++-- doc/src/css/custom.css | 2 +- doc/tsconfig.json | 4 +- examples/http-proxy/tsconfig.json | 4 +- examples/test-project-next/tsconfig.json | 6 +- examples/test-project/tests/EM/index.html | 54 ++++-- examples/test-project/tests/EM/style.css | 2 +- examples/test-project/tests/_EM/index.html | 54 ++++-- examples/test-project/tests/_EM/style.css | 2 +- .../tests/_boilerplate/index.html | 183 ++++++++++-------- .../tests/_boilerplate/scripts/index.js | 22 +-- examples/test-project/tsconfig.json | 4 +- packages/onchfs-js/src/resolver/errors.ts | 5 +- packages/onchfs-js/tsconfig.json | 6 +- 15 files changed, 217 insertions(+), 171 deletions(-) diff --git a/doc/babel.config.js b/doc/babel.config.js index e00595d..cf4260b 100644 --- a/doc/babel.config.js +++ b/doc/babel.config.js @@ -1,3 +1,3 @@ module.exports = { - presets: [require.resolve('@docusaurus/core/lib/babel/preset')], -}; + presets: [require.resolve("@docusaurus/core/lib/babel/preset")], +} diff --git a/doc/src/components/HomepageFeatures/index.tsx b/doc/src/components/HomepageFeatures/index.tsx index 91ef460..5145cc3 100644 --- a/doc/src/components/HomepageFeatures/index.tsx +++ b/doc/src/components/HomepageFeatures/index.tsx @@ -1,17 +1,17 @@ -import React from 'react'; -import clsx from 'clsx'; -import styles from './styles.module.css'; +import React from "react" +import clsx from "clsx" +import styles from "./styles.module.css" type FeatureItem = { - title: string; - Svg: React.ComponentType>; - description: JSX.Element; -}; + title: string + Svg: React.ComponentType> + description: JSX.Element +} const FeatureList: FeatureItem[] = [ { - title: 'Easy to Use', - Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default, + title: "Easy to Use", + Svg: require("@site/static/img/undraw_docusaurus_mountain.svg").default, description: ( <> Docusaurus was designed from the ground up to be easily installed and @@ -20,8 +20,8 @@ const FeatureList: FeatureItem[] = [ ), }, { - title: 'Focus on What Matters', - Svg: require('@site/static/img/undraw_docusaurus_tree.svg').default, + title: "Focus on What Matters", + Svg: require("@site/static/img/undraw_docusaurus_tree.svg").default, description: ( <> Docusaurus lets you focus on your docs, and we'll do the chores. Go @@ -30,8 +30,8 @@ const FeatureList: FeatureItem[] = [ ), }, { - title: 'Powered by React', - Svg: require('@site/static/img/undraw_docusaurus_react.svg').default, + title: "Powered by React", + Svg: require("@site/static/img/undraw_docusaurus_react.svg").default, description: ( <> Extend or customize your website layout by reusing React. Docusaurus can @@ -39,11 +39,11 @@ const FeatureList: FeatureItem[] = [ ), }, -]; +] -function Feature({title, Svg, description}: FeatureItem) { +function Feature({ title, Svg, description }: FeatureItem) { return ( -

+
@@ -52,7 +52,7 @@ function Feature({title, Svg, description}: FeatureItem) {

{description}

- ); + ) } export default function HomepageFeatures(): JSX.Element { @@ -66,5 +66,5 @@ export default function HomepageFeatures(): JSX.Element { - ); + ) } diff --git a/doc/src/css/custom.css b/doc/src/css/custom.css index 58c28ef..fc06844 100644 --- a/doc/src/css/custom.css +++ b/doc/src/css/custom.css @@ -18,7 +18,7 @@ } /* For readability concerns, you should choose a lighter palette in dark mode. */ -[data-theme='dark'] { +[data-theme="dark"] { --ifm-color-primary: #25c2a0; --ifm-color-primary-dark: #21af90; --ifm-color-primary-darker: #1fa588; diff --git a/doc/tsconfig.json b/doc/tsconfig.json index 6f47569..b651133 100644 --- a/doc/tsconfig.json +++ b/doc/tsconfig.json @@ -2,6 +2,6 @@ // This file is not used in compilation. It is here just for a nice editor experience. "extends": "@tsconfig/docusaurus/tsconfig.json", "compilerOptions": { - "baseUrl": "." - } + "baseUrl": ".", + }, } diff --git a/examples/http-proxy/tsconfig.json b/examples/http-proxy/tsconfig.json index 670b1a1..2c19be9 100644 --- a/examples/http-proxy/tsconfig.json +++ b/examples/http-proxy/tsconfig.json @@ -18,8 +18,8 @@ "noUnusedParameters": true, "noImplicitAny": false, "noImplicitThis": false, - "strictNullChecks": false + "strictNullChecks": false, }, "include": ["src/**/*", "__tests__/**/*"], - "exclude": ["src/static/**"] + "exclude": ["src/static/**"], } diff --git a/examples/test-project-next/tsconfig.json b/examples/test-project-next/tsconfig.json index efe1b08..43821a0 100644 --- a/examples/test-project-next/tsconfig.json +++ b/examples/test-project-next/tsconfig.json @@ -14,9 +14,9 @@ "jsx": "preserve", "incremental": true, "paths": { - "@/*": ["./src/*"] - } + "@/*": ["./src/*"], + }, }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], - "exclude": ["node_modules"] + "exclude": ["node_modules"], } diff --git a/examples/test-project/tests/EM/index.html b/examples/test-project/tests/EM/index.html index e7922f9..1f571ee 100644 --- a/examples/test-project/tests/EM/index.html +++ b/examples/test-project/tests/EM/index.html @@ -1,47 +1,59 @@ - + Ethereal Microcosm - ciphrd - + - + - \ No newline at end of file + diff --git a/examples/test-project/tests/EM/style.css b/examples/test-project/tests/EM/style.css index 117f8c7..2cef9d1 100644 --- a/examples/test-project/tests/EM/style.css +++ b/examples/test-project/tests/EM/style.css @@ -13,4 +13,4 @@ body { canvas { max-height: min(100vh, 100vw); max-width: min(100vh, 100vw); -} \ No newline at end of file +} diff --git a/examples/test-project/tests/_EM/index.html b/examples/test-project/tests/_EM/index.html index e7922f9..1f571ee 100644 --- a/examples/test-project/tests/_EM/index.html +++ b/examples/test-project/tests/_EM/index.html @@ -1,47 +1,59 @@ - + Ethereal Microcosm - ciphrd - + - + - \ No newline at end of file + diff --git a/examples/test-project/tests/_EM/style.css b/examples/test-project/tests/_EM/style.css index 117f8c7..2cef9d1 100644 --- a/examples/test-project/tests/_EM/style.css +++ b/examples/test-project/tests/_EM/style.css @@ -13,4 +13,4 @@ body { canvas { max-height: min(100vh, 100vw); max-width: min(100vh, 100vw); -} \ No newline at end of file +} diff --git a/examples/test-project/tests/_boilerplate/index.html b/examples/test-project/tests/_boilerplate/index.html index 8bd9697..cf8e481 100644 --- a/examples/test-project/tests/_boilerplate/index.html +++ b/examples/test-project/tests/_boilerplate/index.html @@ -1,27 +1,43 @@ - + FXHASH project - + - + - + diff --git a/examples/test-project/tests/_boilerplate/scripts/index.js b/examples/test-project/tests/_boilerplate/scripts/index.js index 38be1c2..066af91 100644 --- a/examples/test-project/tests/_boilerplate/scripts/index.js +++ b/examples/test-project/tests/_boilerplate/scripts/index.js @@ -1,8 +1,8 @@ console.log(fxhash) console.log(fxrand()) -const sp = new URLSearchParams(window.location.search); -console.log(sp); +const sp = new URLSearchParams(window.location.search) +console.log(sp) // this is how to define parameters $fx.params([ @@ -24,7 +24,7 @@ $fx.params([ default: "pear", options: { options: ["apple", "orange", "pear"], - } + }, }, { id: "color_id", @@ -45,33 +45,33 @@ $fx.params([ default: "hello", options: { minLength: 1, - maxLength: 5 - } + maxLength: 5, + }, }, -]); +]) // this is how features can be defined $fx.features({ "A random feature": Math.floor($fx.rand() * 10), "A random boolean": $fx.rand() > 0.5, - "A random string": ["A", "B", "C", "D"].at(Math.floor($fx.rand()*4)), + "A random string": ["A", "B", "C", "D"].at(Math.floor($fx.rand() * 4)), "Feature from params, its a number": $fx.getParam("number_id"), }) // log the parameters, for debugging purposes, artists won't have to do that console.log("Current param values:") -// Raw deserialize param values +// Raw deserialize param values console.log($fx.getRawParams()) // Added addtional transformation to the parameter for easier usage -// e.g. color.hex.rgba, color.obj.rgba.r, color.arr.rgb[0] +// e.g. color.hex.rgba, color.obj.rgba.r, color.arr.rgb[0] console.log($fx.getParams()) // how to read a single raw parameter console.log("Single raw value:") -console.log($fx.getRawParam("color_id")); +console.log($fx.getRawParam("color_id")) // how to read a single transformed parameter console.log("Single transformed value:") -console.log($fx.getParam("color_id")); +console.log($fx.getParam("color_id")) // update the document based on the parameters document.body.style.background = $fx.getParam("color_id").hex.rgba diff --git a/examples/test-project/tsconfig.json b/examples/test-project/tsconfig.json index 59a31b6..48ee5ad 100644 --- a/examples/test-project/tsconfig.json +++ b/examples/test-project/tsconfig.json @@ -9,8 +9,8 @@ "emitDecoratorMetadata": true, "esModuleInterop": true, "experimentalDecorators": true, - "sourceMap": true + "sourceMap": true, }, "include": ["src/**/*.ts"], - "exclude": ["node_modules", "**/*.spec.ts"] + "exclude": ["node_modules", "**/*.spec.ts"], } diff --git a/packages/onchfs-js/src/resolver/errors.ts b/packages/onchfs-js/src/resolver/errors.ts index 6ca4f67..e94e4b0 100644 --- a/packages/onchfs-js/src/resolver/errors.ts +++ b/packages/onchfs-js/src/resolver/errors.ts @@ -4,7 +4,10 @@ import { ProxyResolutionStatusErrors } from "@/types/resolver" * Error thrown during the resolution of a relative URI by the proxy. */ export class OnchfsProxyResolutionError extends Error { - constructor(message: string, public status: ProxyResolutionStatusErrors) { + constructor( + message: string, + public status: ProxyResolutionStatusErrors + ) { super(message) this.name = "OnchfsProxyResolutionError" } diff --git a/packages/onchfs-js/tsconfig.json b/packages/onchfs-js/tsconfig.json index 5ca2688..b66d2c4 100644 --- a/packages/onchfs-js/tsconfig.json +++ b/packages/onchfs-js/tsconfig.json @@ -19,9 +19,9 @@ "noImplicitThis": false, "strictNullChecks": false, "paths": { - "@/*": ["./src/*"] - } + "@/*": ["./src/*"], + }, }, "include": ["src/**/*"], - "exclude": ["test/**/*"] + "exclude": ["test/**/*"], } From a7ace8cb1208c3ff8b33470df2ab87910675e051 Mon Sep 17 00:00:00 2001 From: Florian Date: Fri, 19 Jan 2024 16:20:37 +0100 Subject: [PATCH 24/61] dev -> staging (#872) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [core/website] fix overflow issue on large screen * [core/website] refactor generative metrics use maxDecimals 2 on sales * check s3 for signing state // update metadata in db when token already signed * disable website update * disable updating website in updateAccount resolver * disable updating website in account resolver * [core/website] dont randomise code-driven params add option to randmize all params * [core/website] lock and disbale code-driven params controllers * [core/website] dont create random variations with code-driven params * [repo] fix: fix reservoir Hasura env var usage (#820) * [repo] feat: fxhash sales bot discord (#796) * [repo] fix: fix sales bot workflow name * [core/sales-bot] chore: retrigger sales-bot deployment * force default lambda size to medium for tezos signer * [repo] fix: fix sales bot prod build config (#823) * take max of timeDiff and 0 to compute index (in case of artist mint before open) * fix extraction of signature from walletManager.signMessage * add inputBytes to mint operation handler * add /free-live-minting-token route * [dashboard/backend] fix typo in route path free-live-minting-token * [repo] feat: use turborepo to manage seed-authority prisma db (#90) * [repo] fix: fix DOCKERFILE_ARG_BUILD_PATH for seed db ctrl * [seed-authority/db-ctrl] fix: retrigger seed-auth db-ctrl deployment * tokenId already string in live minting explore-params route * [core/sales-bot] fix: fix sales bot Buyer and Seller fields (#827) * remove storage limit for free mint * add overwriteCaptureSettings to reassign route * Change β€œLaunch Calendar” > β€œRelease Calendar” * [core/website] copy rename launch -> release * [repo] fix: fix sales bot ipfs configuration (#836) * fix: fix floor warning (#834) * fix floor warning * remove obsolete warning * Ingore reservoir sales updates (#837) * ignore sales update events * add overwrite capture settings arg to indexer v1 reassign route * [repo] fix: fix missing onchfs deps in contracts package * fix missing onchfs deps in contracts package * remove launch from "launch partners" * hookup overwriteCaptureSettings to GentkTBA * [repo] feat: create reservoir db-ctrl and metadata autoapply for Hasura reservoir (#824) * [core/reservoir-sync-node] fix: fix reservoir node production docker build * [authentication/db-ctrl] feat: use turbo to manage authentication prisma db * [repo] prepare db-ctrl package * [core/api-v2] api-v2 use package * [repo] api-v2 package * [repo] add more fixed items to build * [authentication/db-ctrl] delete * [repo] use root folder * [repo] deploy config * [repo] Update package.json * [repo] fix turbo prisma docker image * [repo] fix local env * fix: fix reduce supply indexing (#839) * fix reduce supply indexing * fix processing of new supply * feat: improve eth data reset (#821) * add cascade delete for tokens * finish refactoring eth data reset * [core/website] fix eth mint button style (#807) * [core/website] fix eth mint button style * [core/website] smaller width increment button * fix: remove executec/canceled eth collab tx + allow updatable multisig tx (#838) * fix safe queries * remove using a new nonce for each tx for safe * feat: add collection offers on objkt iteration offer tab (#844) * add collection offers on objkt iteration offer tab * add collection offer ownership check when objkt defined --------- Co-authored-by: Louis Holley * handle non-checksummed eth addresses (#847) * use parseAddress where input may be an eth address * remove } from parsed addr * [core/api-v2] add randomIteration query * [core/website] add large hero section * fix description of randomIteration query * fix tags indexing (#848) * use accounts (not users) for search queries * [repo] feat: move prettier config to the root of the repo (#853) * use high res preview for opensea image * wip fix eth previews script * [repo] feat: add prettier check on the CI (#854) * use s3BucketName over hardcoded string * temp ignore missing input ts error for build * [core/api-v2] fix: fix prettier check after last merge * [core/website] feat: upgrade next.js to 14.X (#843) * [repo] fix: fix prettier generated files * still wip fix-eth-previews script * use metadata from api to obtain displayUri in preview fix script * thumbnailUri -> displayUri for contractURI metadata image * include preview fix for project metadata * format script * use preview for /thumbnail.png also * fix: fix market stats calculations + add fiat ranking (#831) * ignore inactive listings * add fiat to marketstats * fix stats processing * update api and website to align with new stats * also add crypto in rankings * increase size * pin version * rollback fail * update lock * [core/website] make size optional * remove comment * clean comments * fix build * ignore onchfs error preventing build * fix build * fix sync node build/dev * [core/reservoir-sync-node] fix build * update layouts * update lock * format * format * fix build --------- Co-authored-by: maerzhase Co-authored-by: LΓ©o Pradel * rollback changes for tags and ignore its indexing * [repo] Update pnpm-lock.yaml * fix tag indexing * fix format * ensure token_id is set for TICKET_CLAIMED actions * [core/website] update refetch strategoy for hero iteration * [core/website] fix display price styles (#859) - refactor priceDisplay -> size - remove color from display price - fix alignment of time selector for ranking * [core/api-v2] retrieve iterationsCount from supply - balance * [repo] fix: revert turbo upgrade * [repo] fix: fix prod deployment docker images for prisma (#862) * various fixes (#863) * [repo] feat: migrate og images to next 14 (#858) * [repo] feat: migrate og images to next 14 * [core/website] migrate grid * [core/website] Update og-grid.tsx * [core/website] fix * [core/website] add missing next config * fix: fix and optimize onchain filter (#846) * fix and optimize onchain filter * use MainDataSource for operation handler connection * [repo] fix: fix db-ctrl prod docker images * [repo] Update Dockerfile.k8s-prisma-db-turbo * [repo] chore: remove unused swc-core deps (#850) * [repo] chore: remove unused swc-core deps --------- Co-authored-by: Florian PAUTOT * [core/api] fix: fix api start script * Revert "[repo] chore: remove unused swc-core deps (#850)" This reverts commit 75f8f2fd6f188d760a11ccba45def193cadca24c. * fix seed authority entrypoint * [seed-authority/api] use relative paths (#871) * [seed-authority/api] use relative paths * format --------- Co-authored-by: Florian PAUTOT * [seed-authority/worker] use relative imports; fix build (#873) * [seed-authority/worker] use relative imports; fix build * format --------- Co-authored-by: Florian PAUTOT --------- Co-authored-by: maerzhase Co-authored-by: louis holley Co-authored-by: Markus Maerzhase Co-authored-by: LΓ©o Pradel --- doc/babel.config.js | 4 +- doc/src/components/HomepageFeatures/index.tsx | 36 ++-- doc/src/css/custom.css | 2 +- doc/tsconfig.json | 4 +- examples/http-proxy/.eslintrc.json | 6 +- examples/http-proxy/.prettierrc | 1 - examples/http-proxy/package.json | 1 - examples/http-proxy/tsconfig.json | 4 +- examples/test-project-next/tsconfig.json | 6 +- examples/test-project/tests/EM/index.html | 54 ++++-- examples/test-project/tests/EM/style.css | 2 +- examples/test-project/tests/_EM/index.html | 54 ++++-- examples/test-project/tests/_EM/style.css | 2 +- .../tests/_boilerplate/index.html | 183 ++++++++++-------- .../tests/_boilerplate/scripts/index.js | 22 +-- examples/test-project/tsconfig.json | 4 +- packages/onchfs-js/src/resolver/errors.ts | 5 +- packages/onchfs-js/tsconfig.json | 6 +- 18 files changed, 218 insertions(+), 178 deletions(-) delete mode 100644 examples/http-proxy/.prettierrc diff --git a/doc/babel.config.js b/doc/babel.config.js index e00595d..cf4260b 100644 --- a/doc/babel.config.js +++ b/doc/babel.config.js @@ -1,3 +1,3 @@ module.exports = { - presets: [require.resolve('@docusaurus/core/lib/babel/preset')], -}; + presets: [require.resolve("@docusaurus/core/lib/babel/preset")], +} diff --git a/doc/src/components/HomepageFeatures/index.tsx b/doc/src/components/HomepageFeatures/index.tsx index 91ef460..5145cc3 100644 --- a/doc/src/components/HomepageFeatures/index.tsx +++ b/doc/src/components/HomepageFeatures/index.tsx @@ -1,17 +1,17 @@ -import React from 'react'; -import clsx from 'clsx'; -import styles from './styles.module.css'; +import React from "react" +import clsx from "clsx" +import styles from "./styles.module.css" type FeatureItem = { - title: string; - Svg: React.ComponentType>; - description: JSX.Element; -}; + title: string + Svg: React.ComponentType> + description: JSX.Element +} const FeatureList: FeatureItem[] = [ { - title: 'Easy to Use', - Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default, + title: "Easy to Use", + Svg: require("@site/static/img/undraw_docusaurus_mountain.svg").default, description: ( <> Docusaurus was designed from the ground up to be easily installed and @@ -20,8 +20,8 @@ const FeatureList: FeatureItem[] = [ ), }, { - title: 'Focus on What Matters', - Svg: require('@site/static/img/undraw_docusaurus_tree.svg').default, + title: "Focus on What Matters", + Svg: require("@site/static/img/undraw_docusaurus_tree.svg").default, description: ( <> Docusaurus lets you focus on your docs, and we'll do the chores. Go @@ -30,8 +30,8 @@ const FeatureList: FeatureItem[] = [ ), }, { - title: 'Powered by React', - Svg: require('@site/static/img/undraw_docusaurus_react.svg').default, + title: "Powered by React", + Svg: require("@site/static/img/undraw_docusaurus_react.svg").default, description: ( <> Extend or customize your website layout by reusing React. Docusaurus can @@ -39,11 +39,11 @@ const FeatureList: FeatureItem[] = [ ), }, -]; +] -function Feature({title, Svg, description}: FeatureItem) { +function Feature({ title, Svg, description }: FeatureItem) { return ( -
+
@@ -52,7 +52,7 @@ function Feature({title, Svg, description}: FeatureItem) {

{description}

- ); + ) } export default function HomepageFeatures(): JSX.Element { @@ -66,5 +66,5 @@ export default function HomepageFeatures(): JSX.Element { - ); + ) } diff --git a/doc/src/css/custom.css b/doc/src/css/custom.css index 58c28ef..fc06844 100644 --- a/doc/src/css/custom.css +++ b/doc/src/css/custom.css @@ -18,7 +18,7 @@ } /* For readability concerns, you should choose a lighter palette in dark mode. */ -[data-theme='dark'] { +[data-theme="dark"] { --ifm-color-primary: #25c2a0; --ifm-color-primary-dark: #21af90; --ifm-color-primary-darker: #1fa588; diff --git a/doc/tsconfig.json b/doc/tsconfig.json index 6f47569..b651133 100644 --- a/doc/tsconfig.json +++ b/doc/tsconfig.json @@ -2,6 +2,6 @@ // This file is not used in compilation. It is here just for a nice editor experience. "extends": "@tsconfig/docusaurus/tsconfig.json", "compilerOptions": { - "baseUrl": "." - } + "baseUrl": ".", + }, } diff --git a/examples/http-proxy/.eslintrc.json b/examples/http-proxy/.eslintrc.json index e679ac7..cdafc82 100644 --- a/examples/http-proxy/.eslintrc.json +++ b/examples/http-proxy/.eslintrc.json @@ -1,7 +1,3 @@ { - "plugins": ["prettier"], - "extends": ["@fxhash/eslint-config"], - "rules": { - "prettier/prettier": "error" - } + "extends": ["@fxhash/eslint-config"] } diff --git a/examples/http-proxy/.prettierrc b/examples/http-proxy/.prettierrc deleted file mode 100644 index 15dc90f..0000000 --- a/examples/http-proxy/.prettierrc +++ /dev/null @@ -1 +0,0 @@ -"@fxhash/prettier-config" diff --git a/examples/http-proxy/package.json b/examples/http-proxy/package.json index e5e82ad..c6a95b3 100644 --- a/examples/http-proxy/package.json +++ b/examples/http-proxy/package.json @@ -23,7 +23,6 @@ }, "devDependencies": { "@fxhash/eslint-config": "workspace:*", - "@fxhash/prettier-config": "workspace:*", "@types/cors": "2.8.13", "@types/node": "16.18.38", "nodemon": "2.0.22", diff --git a/examples/http-proxy/tsconfig.json b/examples/http-proxy/tsconfig.json index 670b1a1..2c19be9 100644 --- a/examples/http-proxy/tsconfig.json +++ b/examples/http-proxy/tsconfig.json @@ -18,8 +18,8 @@ "noUnusedParameters": true, "noImplicitAny": false, "noImplicitThis": false, - "strictNullChecks": false + "strictNullChecks": false, }, "include": ["src/**/*", "__tests__/**/*"], - "exclude": ["src/static/**"] + "exclude": ["src/static/**"], } diff --git a/examples/test-project-next/tsconfig.json b/examples/test-project-next/tsconfig.json index efe1b08..43821a0 100644 --- a/examples/test-project-next/tsconfig.json +++ b/examples/test-project-next/tsconfig.json @@ -14,9 +14,9 @@ "jsx": "preserve", "incremental": true, "paths": { - "@/*": ["./src/*"] - } + "@/*": ["./src/*"], + }, }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], - "exclude": ["node_modules"] + "exclude": ["node_modules"], } diff --git a/examples/test-project/tests/EM/index.html b/examples/test-project/tests/EM/index.html index e7922f9..1f571ee 100644 --- a/examples/test-project/tests/EM/index.html +++ b/examples/test-project/tests/EM/index.html @@ -1,47 +1,59 @@ - + Ethereal Microcosm - ciphrd - + - + - \ No newline at end of file + diff --git a/examples/test-project/tests/EM/style.css b/examples/test-project/tests/EM/style.css index 117f8c7..2cef9d1 100644 --- a/examples/test-project/tests/EM/style.css +++ b/examples/test-project/tests/EM/style.css @@ -13,4 +13,4 @@ body { canvas { max-height: min(100vh, 100vw); max-width: min(100vh, 100vw); -} \ No newline at end of file +} diff --git a/examples/test-project/tests/_EM/index.html b/examples/test-project/tests/_EM/index.html index e7922f9..1f571ee 100644 --- a/examples/test-project/tests/_EM/index.html +++ b/examples/test-project/tests/_EM/index.html @@ -1,47 +1,59 @@ - + Ethereal Microcosm - ciphrd - + - + - \ No newline at end of file + diff --git a/examples/test-project/tests/_EM/style.css b/examples/test-project/tests/_EM/style.css index 117f8c7..2cef9d1 100644 --- a/examples/test-project/tests/_EM/style.css +++ b/examples/test-project/tests/_EM/style.css @@ -13,4 +13,4 @@ body { canvas { max-height: min(100vh, 100vw); max-width: min(100vh, 100vw); -} \ No newline at end of file +} diff --git a/examples/test-project/tests/_boilerplate/index.html b/examples/test-project/tests/_boilerplate/index.html index 8bd9697..cf8e481 100644 --- a/examples/test-project/tests/_boilerplate/index.html +++ b/examples/test-project/tests/_boilerplate/index.html @@ -1,27 +1,43 @@ - + FXHASH project - + - + - + diff --git a/examples/test-project/tests/_boilerplate/scripts/index.js b/examples/test-project/tests/_boilerplate/scripts/index.js index 38be1c2..066af91 100644 --- a/examples/test-project/tests/_boilerplate/scripts/index.js +++ b/examples/test-project/tests/_boilerplate/scripts/index.js @@ -1,8 +1,8 @@ console.log(fxhash) console.log(fxrand()) -const sp = new URLSearchParams(window.location.search); -console.log(sp); +const sp = new URLSearchParams(window.location.search) +console.log(sp) // this is how to define parameters $fx.params([ @@ -24,7 +24,7 @@ $fx.params([ default: "pear", options: { options: ["apple", "orange", "pear"], - } + }, }, { id: "color_id", @@ -45,33 +45,33 @@ $fx.params([ default: "hello", options: { minLength: 1, - maxLength: 5 - } + maxLength: 5, + }, }, -]); +]) // this is how features can be defined $fx.features({ "A random feature": Math.floor($fx.rand() * 10), "A random boolean": $fx.rand() > 0.5, - "A random string": ["A", "B", "C", "D"].at(Math.floor($fx.rand()*4)), + "A random string": ["A", "B", "C", "D"].at(Math.floor($fx.rand() * 4)), "Feature from params, its a number": $fx.getParam("number_id"), }) // log the parameters, for debugging purposes, artists won't have to do that console.log("Current param values:") -// Raw deserialize param values +// Raw deserialize param values console.log($fx.getRawParams()) // Added addtional transformation to the parameter for easier usage -// e.g. color.hex.rgba, color.obj.rgba.r, color.arr.rgb[0] +// e.g. color.hex.rgba, color.obj.rgba.r, color.arr.rgb[0] console.log($fx.getParams()) // how to read a single raw parameter console.log("Single raw value:") -console.log($fx.getRawParam("color_id")); +console.log($fx.getRawParam("color_id")) // how to read a single transformed parameter console.log("Single transformed value:") -console.log($fx.getParam("color_id")); +console.log($fx.getParam("color_id")) // update the document based on the parameters document.body.style.background = $fx.getParam("color_id").hex.rgba diff --git a/examples/test-project/tsconfig.json b/examples/test-project/tsconfig.json index 59a31b6..48ee5ad 100644 --- a/examples/test-project/tsconfig.json +++ b/examples/test-project/tsconfig.json @@ -9,8 +9,8 @@ "emitDecoratorMetadata": true, "esModuleInterop": true, "experimentalDecorators": true, - "sourceMap": true + "sourceMap": true, }, "include": ["src/**/*.ts"], - "exclude": ["node_modules", "**/*.spec.ts"] + "exclude": ["node_modules", "**/*.spec.ts"], } diff --git a/packages/onchfs-js/src/resolver/errors.ts b/packages/onchfs-js/src/resolver/errors.ts index 6ca4f67..e94e4b0 100644 --- a/packages/onchfs-js/src/resolver/errors.ts +++ b/packages/onchfs-js/src/resolver/errors.ts @@ -4,7 +4,10 @@ import { ProxyResolutionStatusErrors } from "@/types/resolver" * Error thrown during the resolution of a relative URI by the proxy. */ export class OnchfsProxyResolutionError extends Error { - constructor(message: string, public status: ProxyResolutionStatusErrors) { + constructor( + message: string, + public status: ProxyResolutionStatusErrors + ) { super(message) this.name = "OnchfsProxyResolutionError" } diff --git a/packages/onchfs-js/tsconfig.json b/packages/onchfs-js/tsconfig.json index 5ca2688..b66d2c4 100644 --- a/packages/onchfs-js/tsconfig.json +++ b/packages/onchfs-js/tsconfig.json @@ -19,9 +19,9 @@ "noImplicitThis": false, "strictNullChecks": false, "paths": { - "@/*": ["./src/*"] - } + "@/*": ["./src/*"], + }, }, "include": ["src/**/*"], - "exclude": ["test/**/*"] + "exclude": ["test/**/*"], } From f53a2f541aca584351fcc7d1c21d9ada1f39952b Mon Sep 17 00:00:00 2001 From: Louis Holley Date: Mon, 22 Jan 2024 12:40:11 +0000 Subject: [PATCH 25/61] add fallback mime lookup to onchfs file lib --- packages/onchfs-js/src/files/file.ts | 30 ++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/packages/onchfs-js/src/files/file.ts b/packages/onchfs-js/src/files/file.ts index 774692b..bb2dbc5 100644 --- a/packages/onchfs-js/src/files/file.ts +++ b/packages/onchfs-js/src/files/file.ts @@ -6,6 +6,29 @@ import { encodeMetadata } from "@/metadata/encode" import { FileInode, IFile, OnchfsPrepareOptions } from "@/types/files" import { computeFileCid } from "@/cid" +const MIME_LOOKUP = { + vert: "text/plain", +} + +/** + * Resolves the MIME type for a given filename. + * @param {string} filename - The name of the file. + * @returns {string|null} The determined MIME type or null if not found. + */ +function resolveMimeType(filename): string | null { + let mime = lookupMime(filename) + if (!mime) { + // fallback to extension lookup + const extension = filename.split(".").pop() + if (extension && MIME_LOOKUP[extension]) { + return MIME_LOOKUP[extension] + } + // return null if no MIME type is found + return null + } + return mime +} + /** * Computes all the necessary data for the inscription of the file on-chain. * Performs the following tasks in order: @@ -29,14 +52,9 @@ export function prepareFile( let metadata: FileMetadataEntries = {} let insertionBytes = content // we use file extension to get mime type - let mime = lookupMime(name) + const mime = resolveMimeType(name) - // if no mime type can be mapped from filename, use magic number if (!mime) { - // const magicMime = await fileTypeFromBuffer(content) - // if (magicMime) { - // metadata["Content-Type"] = magicMime.mime - // } // if still no mime, we simply do not set the Content-Type in the metadata, // and let the browser handle it. // We could set it to "application/octet-stream" as RFC2046 states, however From ceb028b3e58395b58633f81828a200546e16c447 Mon Sep 17 00:00:00 2001 From: maerzhase Date: Mon, 22 Jan 2024 17:24:44 +0100 Subject: [PATCH 26/61] [repo] rename fxhash.js -> fxhash.min.js --- doc/docs/use-cases/generative-art.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/docs/use-cases/generative-art.md b/doc/docs/use-cases/generative-art.md index e3e34d8..be60af0 100644 --- a/doc/docs/use-cases/generative-art.md +++ b/doc/docs/use-cases/generative-art.md @@ -121,7 +121,7 @@ While previous projects leave an opiniated footprint on how code data is handled β”œβ”€β”€ style.css β”œβ”€β”€ main.js └── libs/ - β”œβ”€β”€ fxhash.js + β”œβ”€β”€ fxhash.min.js β”œβ”€β”€ colors.js └── processing.min.js ``` @@ -131,7 +131,7 @@ While previous projects leave an opiniated footprint on how code data is handled ```html - + @@ -157,7 +157,7 @@ Onchfs also handles libraries elegantly, and so naturally by its design. Looking β”œβ”€β”€ style.css -> 0xaeaeaed2... β”œβ”€β”€ main.js -> 0xa2a2a2a9... └── libs/ -> 0xd5d5d5d5... - β”œβ”€β”€ fxhash.js -> 0xc6c6c6c6... + β”œβ”€β”€ fxhash.min.js -> 0xc6c6c6c6... β”œβ”€β”€ colors.js -> 0xabcdef12... └── processing.min.js -> 0x01010101... @@ -169,7 +169,7 @@ inscriptions: + ... + DIRECTORY libs (0xd5d5d5d5...) { - "fxhash.js": 0xc6c6c6c6..., + "fxhash.min.js": 0xc6c6c6c6..., "colors.js": 0xabcdef12..., "processing.min.js": 0x01010101..., } @@ -191,7 +191,7 @@ inscriptions: + ... + DIRECTORY libs (0xd5d5d5d5...) { - "fxhash.js": 0xc6c6c6c6..., + "fxhash.min.js": 0xc6c6c6c6..., "colors.js": 0xabcdef12..., "processing.min.js": 0x01010101..., <- points to existing resource } From 7bf6b1a77a9327ffa58a4e77586d3beb7f119b27 Mon Sep 17 00:00:00 2001 From: Florian Date: Tue, 23 Jan 2024 16:09:00 +0100 Subject: [PATCH 27/61] dev -> staging (#888) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [core/website] fix overflow issue on large screen * [core/website] refactor generative metrics use maxDecimals 2 on sales * check s3 for signing state // update metadata in db when token already signed * [core/website] dont randomise code-driven params add option to randmize all params * [core/website] lock and disbale code-driven params controllers * [core/website] dont create random variations with code-driven params * [repo] fix: fix reservoir Hasura env var usage (#820) * [repo] feat: fxhash sales bot discord (#796) * [repo] fix: fix sales bot workflow name * [core/sales-bot] chore: retrigger sales-bot deployment * [repo] fix: fix sales bot prod build config (#823) * take max of timeDiff and 0 to compute index (in case of artist mint before open) * add inputBytes to mint operation handler * add /free-live-minting-token route * [dashboard/backend] fix typo in route path free-live-minting-token * [repo] feat: use turborepo to manage seed-authority prisma db (#90) * [repo] fix: fix DOCKERFILE_ARG_BUILD_PATH for seed db ctrl * [seed-authority/db-ctrl] fix: retrigger seed-auth db-ctrl deployment * tokenId already string in live minting explore-params route * [core/sales-bot] fix: fix sales bot Buyer and Seller fields (#827) * remove storage limit for free mint * add overwriteCaptureSettings to reassign route * Change β€œLaunch Calendar” > β€œRelease Calendar” * [core/website] copy rename launch -> release * [repo] fix: fix sales bot ipfs configuration (#836) * fix: fix floor warning (#834) * fix floor warning * remove obsolete warning * Ingore reservoir sales updates (#837) * ignore sales update events * [repo] fix: fix missing onchfs deps in contracts package * remove launch from "launch partners" * [repo] feat: create reservoir db-ctrl and metadata autoapply for Hasura reservoir (#824) * [core/reservoir-sync-node] fix: fix reservoir node production docker build * [authentication/db-ctrl] feat: use turbo to manage authentication prisma db * [repo] prepare db-ctrl package * [core/api-v2] api-v2 use package * [repo] api-v2 package * [repo] add more fixed items to build * [authentication/db-ctrl] delete * [repo] use root folder * [repo] deploy config * [repo] Update package.json * [repo] fix turbo prisma docker image * [repo] fix local env * fix: fix reduce supply indexing (#839) * fix reduce supply indexing * fix processing of new supply * feat: improve eth data reset (#821) * add cascade delete for tokens * finish refactoring eth data reset * [core/website] fix eth mint button style (#807) * [core/website] fix eth mint button style * [core/website] smaller width increment button * fix: remove executec/canceled eth collab tx + allow updatable multisig tx (#838) * fix safe queries * remove using a new nonce for each tx for safe * feat: add collection offers on objkt iteration offer tab (#844) * add collection offers on objkt iteration offer tab * add collection offer ownership check when objkt defined --------- Co-authored-by: Louis Holley * handle non-checksummed eth addresses (#847) * use parseAddress where input may be an eth address * remove } from parsed addr * [core/api-v2] add randomIteration query * [core/website] add large hero section * fix description of randomIteration query * fix tags indexing (#848) * use accounts (not users) for search queries * [repo] feat: move prettier config to the root of the repo (#853) * use high res preview for opensea image * wip fix eth previews script * [repo] feat: add prettier check on the CI (#854) * use s3BucketName over hardcoded string * temp ignore missing input ts error for build * [core/api-v2] fix: fix prettier check after last merge * [core/website] feat: upgrade next.js to 14.X (#843) * [repo] fix: fix prettier generated files * still wip fix-eth-previews script * use metadata from api to obtain displayUri in preview fix script * thumbnailUri -> displayUri for contractURI metadata image * include preview fix for project metadata * format script * use preview for /thumbnail.png also * fix: fix market stats calculations + add fiat ranking (#831) * ignore inactive listings * add fiat to marketstats * fix stats processing * update api and website to align with new stats * also add crypto in rankings * increase size * pin version * rollback fail * update lock * [core/website] make size optional * remove comment * clean comments * fix build * ignore onchfs error preventing build * fix build * fix sync node build/dev * [core/reservoir-sync-node] fix build * update layouts * update lock * format * format * fix build --------- Co-authored-by: maerzhase Co-authored-by: LΓ©o Pradel * rollback changes for tags and ignore its indexing * [repo] Update pnpm-lock.yaml * fix tag indexing * fix format * ensure token_id is set for TICKET_CLAIMED actions * [core/website] update refetch strategoy for hero iteration * [core/website] fix display price styles (#859) - refactor priceDisplay -> size - remove color from display price - fix alignment of time selector for ranking * [core/api-v2] retrieve iterationsCount from supply - balance * [repo] fix: revert turbo upgrade * [repo] fix: fix prod deployment docker images for prisma (#862) * various fixes (#863) * [repo] feat: migrate og images to next 14 (#858) * [repo] feat: migrate og images to next 14 * [core/website] migrate grid * [core/website] Update og-grid.tsx * [core/website] fix * [core/website] add missing next config * fix: fix and optimize onchain filter (#846) * fix and optimize onchain filter * use MainDataSource for operation handler connection * return updated objkt from updateGenerationHash * [repo] fix: fix db-ctrl prod docker images * [core/api-v2] add excludeIteration arg to randomIteration * [repo] Update Dockerfile.k8s-prisma-db-turbo * [core/website] call randomIteration with current iteration excluded * [repo] chore: remove unused swc-core deps (#850) * [repo] chore: remove unused swc-core deps --------- Co-authored-by: Florian PAUTOT * [core/api] fix: fix api start script * Revert "[repo] chore: remove unused swc-core deps (#850)" This reverts commit 75f8f2fd6f188d760a11ccba45def193cadca24c. * fix seed authority entrypoint * [seed-authority/api] use relative paths (#871) * [seed-authority/api] use relative paths * format --------- Co-authored-by: Florian PAUTOT * [seed-authority/worker] use relative imports; fix build (#873) * [seed-authority/worker] use relative imports; fix build * format --------- Co-authored-by: Florian PAUTOT * [core/api-v2] better strategy for allow iteration exclusion * [core/api-v2] pnpm format * [core/website] implement QA for home hero large section * [core/website] increase large hero height * [core/website] button transparent = border transparent * [repo] feat: update connectkit to v1.6 to add support for EIP-6963. (#880) * enable free live minting for projects w/ no allow list * add fallback mime lookup to onchfs file lib * feat: auto force royalties + index external orders + batchable marketplace + updatable listing and offers (#783) * add metadata fields to orders * add metadata to created listings/offers * add auto royalties + make all marketeplace operations batchable * update reservoir env * fix reservoir indexer build * update hasura metadata for listing/offer metadata field * use new marketplace operation params * return early from handlers when objkt not found * expose metadata fields on api * add external listings to frontend * Feat/eth/aggregate all orders with royalties dynamic contracts (#812) * update active to filter to include inactive flag * remove log * fix checks for inactive orders * add further isInactive filtering * revert add additional filters * add queries to fetch individual listings/offers * Revert "add queries to fetch individual listings/offers" This reverts commit dd53140b6245f97820f8dbae2c5b9d2a02a993a2. * wip * skip mod lock for lockEnd === opensAt * rm log * [core/website] fix: fix tez label on gentk marketplace page * add rebate warning * show cancellable offers/listings in MarketplaceActions * add express server to eth-indexer * prefer account over user in MarketplaceActions * correct getGenerativeTokenPrice implementation for dutch auctions * checksum addresses used for whitelist * 401 -> 400 error * Warning component links to home page * add inactive offers/listings to queries * update types with inactive offers/listings * add InactiveBadge component * add InactiveBadge to user offers * add inactive badge to user listings * fix marketplace sales feed * use account names over user names in marketplace rankings * set max decimals to 2 on marketplace rankings * remove launch_timestamp check * return null instead of throwing error when finding available whitelist * parse reserve id as int before deleting * last ditch attempt * remove bunk fix * patch subsquid to remove sql typecast * force reserve id to int when removing * commit reserve script * copy patches from root in subsquid dockerfiler * manually copy patches into /app/out * revert subsquid dockerfile changes * revert bunk patch * patch subsquid to avoid unnecessary typecast * add eth donation addresses * pass chain prop to Donations components * fix rendering of donation users * add missing chain prop to article donations * fix mint progress filters for open editions * remove (tez) from marketplace metrics * hide inactive listings from activeListing_exist objkt filter * map raw dutch auction price db fields to camelCase * skip actions with no da pricing in refund button * remove hardcoded ids * set acceptedAt when listing accepted * remove ownership transfer from handleInsertSaleEvent (incorrect for credit card purchase) * fix issuer/target of action in reservoir sale * [fxhash-package/packages] feat: refactor eth tx processing + show tx id in error message * [repo] more * [packages/fxhash-eth-sdk] reservoir * remove duplicate actions/transactions for offers * add default transport to remove winston memory errors from prod * [packages/fxhash-eth-sdk] more * [packages/fxhash-eth-sdk] more * [packages/fxhash-eth-sdk] safe * [packages/fxhash-eth-sdk] tickets * [packages/fxhash-eth-sdk] last * [core/website] update docs links in footer * [core/website] better error message * [repo] all Tez txs are ONCHAIN * fix variations button for open editions * [core/website] make link clickable * change donations order * convert added da level to string * return receipt with contract operation data * handle transaction not found error from viem * get it compiling * rm unused * revert transaction receipt error * handle errors in waitForTransaction * fix contract feedback in minting components * rm spacing below feedback * fix display price for mint ticket modal * reduce min decrement duration to 1 minute * fix seed retrieval from seed authority * fix tezos splits update * min da tier to 3 minutes * [packages/fxhash-eth-sdk] fix: fix WithdrawAllEthV1Operation when nothing can be withdrawn * add TransactionCaughtError for better error messages * utilise TransactionUnknownError for custom error messages * [core/website] remove launch banner in calendar * temp commit * Chore/sepolia migration (#810) * start migrating config to Sepolia * update start block * update eth indexer env * add missing config * update contracts to align with new split contract * update queue type for reservoir indexer * fix rabbitmq startup script * rollback env file --------- Co-authored-by: Louis Holley Co-authored-by: LΓ©o Pradel Co-authored-by: Markus Maerzhase Co-authored-by: maerzhase * make listings and offers updatable * format --------- Co-authored-by: Louis Holley Co-authored-by: LΓ©o Pradel Co-authored-by: Markus Maerzhase Co-authored-by: maerzhase * add shouldEnqueue arg to GentkSigner * add signingEnabled util * pass shouldEnqueue arg to signingEnabled from indexer-v2 * enable overwrite capture settings on eth * fix reservoir db ctrl CI * pass null for live minting wallets when needed * remove throw when no live minting wallets * revert live minting wallets event form change * add extra safety check in getWalletConstraints * overwrite liveMintingWallets when list empty * add use reserve arg to OH_MINT * disconnect records when live minting wallets empty * fix reservoir image build * [repo] feat: create hasura metadata deployment pipelines (#828) * [repo] feat: create hasura metadata ctrl * [repo] create docker image * [repo] Create core_hasura_metadata.yml * [repo] test * [repo] trigger * [repo] add production * [repo] add reservoir * [repo] only run if branch * Delete hasura/Dockerfile.hasura-metadata * [repo] add missing check in the reservoir file * fix hasura metadata * fix marketstats columns (#885) * [repo] fix: fix db-ctrl config after latest fix (#886) * trigger CI + add retry for reservoir hasura * Revert "feat: auto force royalties + index external orders + batchable marketplace + updatable listing and offers (#783)" This reverts commit 06e6a56ad3a05e9d3f9e79aab853f46fbe76616d. * don't multiple by qty as price field already reflects total * initialise count with qty * add withdrawal caveat to EthWalletWithdraw * revert at withdrawal caveat * revert add withdrawal caveat --------- Co-authored-by: maerzhase Co-authored-by: Louis Holley Co-authored-by: LΓ©o Pradel Co-authored-by: Markus Maerzhase --- packages/onchfs-js/src/files/file.ts | 30 ++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/packages/onchfs-js/src/files/file.ts b/packages/onchfs-js/src/files/file.ts index 774692b..bb2dbc5 100644 --- a/packages/onchfs-js/src/files/file.ts +++ b/packages/onchfs-js/src/files/file.ts @@ -6,6 +6,29 @@ import { encodeMetadata } from "@/metadata/encode" import { FileInode, IFile, OnchfsPrepareOptions } from "@/types/files" import { computeFileCid } from "@/cid" +const MIME_LOOKUP = { + vert: "text/plain", +} + +/** + * Resolves the MIME type for a given filename. + * @param {string} filename - The name of the file. + * @returns {string|null} The determined MIME type or null if not found. + */ +function resolveMimeType(filename): string | null { + let mime = lookupMime(filename) + if (!mime) { + // fallback to extension lookup + const extension = filename.split(".").pop() + if (extension && MIME_LOOKUP[extension]) { + return MIME_LOOKUP[extension] + } + // return null if no MIME type is found + return null + } + return mime +} + /** * Computes all the necessary data for the inscription of the file on-chain. * Performs the following tasks in order: @@ -29,14 +52,9 @@ export function prepareFile( let metadata: FileMetadataEntries = {} let insertionBytes = content // we use file extension to get mime type - let mime = lookupMime(name) + const mime = resolveMimeType(name) - // if no mime type can be mapped from filename, use magic number if (!mime) { - // const magicMime = await fileTypeFromBuffer(content) - // if (magicMime) { - // metadata["Content-Type"] = magicMime.mime - // } // if still no mime, we simply do not set the Content-Type in the metadata, // and let the browser handle it. // We could set it to "application/octet-stream" as RFC2046 states, however From 83802fddfe14466886487289365a6ea8e3504f17 Mon Sep 17 00:00:00 2001 From: louis holley Date: Thu, 25 Jan 2024 17:30:23 +0000 Subject: [PATCH 28/61] Revert "staging -> main (#872)" --- doc/babel.config.js | 4 +- doc/src/components/HomepageFeatures/index.tsx | 36 ++-- doc/src/css/custom.css | 2 +- doc/tsconfig.json | 4 +- examples/http-proxy/.eslintrc.json | 6 +- examples/http-proxy/.prettierrc | 1 + examples/http-proxy/package.json | 1 + examples/http-proxy/tsconfig.json | 4 +- examples/test-project-next/tsconfig.json | 6 +- examples/test-project/tests/EM/index.html | 54 ++---- examples/test-project/tests/EM/style.css | 2 +- examples/test-project/tests/_EM/index.html | 54 ++---- examples/test-project/tests/_EM/style.css | 2 +- .../tests/_boilerplate/index.html | 183 ++++++++---------- .../tests/_boilerplate/scripts/index.js | 22 +-- examples/test-project/tsconfig.json | 4 +- packages/onchfs-js/src/files/file.ts | 30 +-- packages/onchfs-js/src/resolver/errors.ts | 5 +- packages/onchfs-js/tsconfig.json | 6 +- 19 files changed, 184 insertions(+), 242 deletions(-) create mode 100644 examples/http-proxy/.prettierrc diff --git a/doc/babel.config.js b/doc/babel.config.js index cf4260b..e00595d 100644 --- a/doc/babel.config.js +++ b/doc/babel.config.js @@ -1,3 +1,3 @@ module.exports = { - presets: [require.resolve("@docusaurus/core/lib/babel/preset")], -} + presets: [require.resolve('@docusaurus/core/lib/babel/preset')], +}; diff --git a/doc/src/components/HomepageFeatures/index.tsx b/doc/src/components/HomepageFeatures/index.tsx index 5145cc3..91ef460 100644 --- a/doc/src/components/HomepageFeatures/index.tsx +++ b/doc/src/components/HomepageFeatures/index.tsx @@ -1,17 +1,17 @@ -import React from "react" -import clsx from "clsx" -import styles from "./styles.module.css" +import React from 'react'; +import clsx from 'clsx'; +import styles from './styles.module.css'; type FeatureItem = { - title: string - Svg: React.ComponentType> - description: JSX.Element -} + title: string; + Svg: React.ComponentType>; + description: JSX.Element; +}; const FeatureList: FeatureItem[] = [ { - title: "Easy to Use", - Svg: require("@site/static/img/undraw_docusaurus_mountain.svg").default, + title: 'Easy to Use', + Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default, description: ( <> Docusaurus was designed from the ground up to be easily installed and @@ -20,8 +20,8 @@ const FeatureList: FeatureItem[] = [ ), }, { - title: "Focus on What Matters", - Svg: require("@site/static/img/undraw_docusaurus_tree.svg").default, + title: 'Focus on What Matters', + Svg: require('@site/static/img/undraw_docusaurus_tree.svg').default, description: ( <> Docusaurus lets you focus on your docs, and we'll do the chores. Go @@ -30,8 +30,8 @@ const FeatureList: FeatureItem[] = [ ), }, { - title: "Powered by React", - Svg: require("@site/static/img/undraw_docusaurus_react.svg").default, + title: 'Powered by React', + Svg: require('@site/static/img/undraw_docusaurus_react.svg').default, description: ( <> Extend or customize your website layout by reusing React. Docusaurus can @@ -39,11 +39,11 @@ const FeatureList: FeatureItem[] = [ ), }, -] +]; -function Feature({ title, Svg, description }: FeatureItem) { +function Feature({title, Svg, description}: FeatureItem) { return ( -
+
@@ -52,7 +52,7 @@ function Feature({ title, Svg, description }: FeatureItem) {

{description}

- ) + ); } export default function HomepageFeatures(): JSX.Element { @@ -66,5 +66,5 @@ export default function HomepageFeatures(): JSX.Element { - ) + ); } diff --git a/doc/src/css/custom.css b/doc/src/css/custom.css index fc06844..58c28ef 100644 --- a/doc/src/css/custom.css +++ b/doc/src/css/custom.css @@ -18,7 +18,7 @@ } /* For readability concerns, you should choose a lighter palette in dark mode. */ -[data-theme="dark"] { +[data-theme='dark'] { --ifm-color-primary: #25c2a0; --ifm-color-primary-dark: #21af90; --ifm-color-primary-darker: #1fa588; diff --git a/doc/tsconfig.json b/doc/tsconfig.json index b651133..6f47569 100644 --- a/doc/tsconfig.json +++ b/doc/tsconfig.json @@ -2,6 +2,6 @@ // This file is not used in compilation. It is here just for a nice editor experience. "extends": "@tsconfig/docusaurus/tsconfig.json", "compilerOptions": { - "baseUrl": ".", - }, + "baseUrl": "." + } } diff --git a/examples/http-proxy/.eslintrc.json b/examples/http-proxy/.eslintrc.json index cdafc82..e679ac7 100644 --- a/examples/http-proxy/.eslintrc.json +++ b/examples/http-proxy/.eslintrc.json @@ -1,3 +1,7 @@ { - "extends": ["@fxhash/eslint-config"] + "plugins": ["prettier"], + "extends": ["@fxhash/eslint-config"], + "rules": { + "prettier/prettier": "error" + } } diff --git a/examples/http-proxy/.prettierrc b/examples/http-proxy/.prettierrc new file mode 100644 index 0000000..15dc90f --- /dev/null +++ b/examples/http-proxy/.prettierrc @@ -0,0 +1 @@ +"@fxhash/prettier-config" diff --git a/examples/http-proxy/package.json b/examples/http-proxy/package.json index c6a95b3..e5e82ad 100644 --- a/examples/http-proxy/package.json +++ b/examples/http-proxy/package.json @@ -23,6 +23,7 @@ }, "devDependencies": { "@fxhash/eslint-config": "workspace:*", + "@fxhash/prettier-config": "workspace:*", "@types/cors": "2.8.13", "@types/node": "16.18.38", "nodemon": "2.0.22", diff --git a/examples/http-proxy/tsconfig.json b/examples/http-proxy/tsconfig.json index 2c19be9..670b1a1 100644 --- a/examples/http-proxy/tsconfig.json +++ b/examples/http-proxy/tsconfig.json @@ -18,8 +18,8 @@ "noUnusedParameters": true, "noImplicitAny": false, "noImplicitThis": false, - "strictNullChecks": false, + "strictNullChecks": false }, "include": ["src/**/*", "__tests__/**/*"], - "exclude": ["src/static/**"], + "exclude": ["src/static/**"] } diff --git a/examples/test-project-next/tsconfig.json b/examples/test-project-next/tsconfig.json index 43821a0..efe1b08 100644 --- a/examples/test-project-next/tsconfig.json +++ b/examples/test-project-next/tsconfig.json @@ -14,9 +14,9 @@ "jsx": "preserve", "incremental": true, "paths": { - "@/*": ["./src/*"], - }, + "@/*": ["./src/*"] + } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], - "exclude": ["node_modules"], + "exclude": ["node_modules"] } diff --git a/examples/test-project/tests/EM/index.html b/examples/test-project/tests/EM/index.html index 1f571ee..e7922f9 100644 --- a/examples/test-project/tests/EM/index.html +++ b/examples/test-project/tests/EM/index.html @@ -1,59 +1,47 @@ - + Ethereal Microcosm - ciphrd - + - + - + \ No newline at end of file diff --git a/examples/test-project/tests/EM/style.css b/examples/test-project/tests/EM/style.css index 2cef9d1..117f8c7 100644 --- a/examples/test-project/tests/EM/style.css +++ b/examples/test-project/tests/EM/style.css @@ -13,4 +13,4 @@ body { canvas { max-height: min(100vh, 100vw); max-width: min(100vh, 100vw); -} +} \ No newline at end of file diff --git a/examples/test-project/tests/_EM/index.html b/examples/test-project/tests/_EM/index.html index 1f571ee..e7922f9 100644 --- a/examples/test-project/tests/_EM/index.html +++ b/examples/test-project/tests/_EM/index.html @@ -1,59 +1,47 @@ - + Ethereal Microcosm - ciphrd - + - + - + \ No newline at end of file diff --git a/examples/test-project/tests/_EM/style.css b/examples/test-project/tests/_EM/style.css index 2cef9d1..117f8c7 100644 --- a/examples/test-project/tests/_EM/style.css +++ b/examples/test-project/tests/_EM/style.css @@ -13,4 +13,4 @@ body { canvas { max-height: min(100vh, 100vw); max-width: min(100vh, 100vw); -} +} \ No newline at end of file diff --git a/examples/test-project/tests/_boilerplate/index.html b/examples/test-project/tests/_boilerplate/index.html index cf8e481..8bd9697 100644 --- a/examples/test-project/tests/_boilerplate/index.html +++ b/examples/test-project/tests/_boilerplate/index.html @@ -1,43 +1,27 @@ - + FXHASH project - + - + - + diff --git a/examples/test-project/tests/_boilerplate/scripts/index.js b/examples/test-project/tests/_boilerplate/scripts/index.js index 066af91..38be1c2 100644 --- a/examples/test-project/tests/_boilerplate/scripts/index.js +++ b/examples/test-project/tests/_boilerplate/scripts/index.js @@ -1,8 +1,8 @@ console.log(fxhash) console.log(fxrand()) -const sp = new URLSearchParams(window.location.search) -console.log(sp) +const sp = new URLSearchParams(window.location.search); +console.log(sp); // this is how to define parameters $fx.params([ @@ -24,7 +24,7 @@ $fx.params([ default: "pear", options: { options: ["apple", "orange", "pear"], - }, + } }, { id: "color_id", @@ -45,33 +45,33 @@ $fx.params([ default: "hello", options: { minLength: 1, - maxLength: 5, - }, + maxLength: 5 + } }, -]) +]); // this is how features can be defined $fx.features({ "A random feature": Math.floor($fx.rand() * 10), "A random boolean": $fx.rand() > 0.5, - "A random string": ["A", "B", "C", "D"].at(Math.floor($fx.rand() * 4)), + "A random string": ["A", "B", "C", "D"].at(Math.floor($fx.rand()*4)), "Feature from params, its a number": $fx.getParam("number_id"), }) // log the parameters, for debugging purposes, artists won't have to do that console.log("Current param values:") -// Raw deserialize param values +// Raw deserialize param values console.log($fx.getRawParams()) // Added addtional transformation to the parameter for easier usage -// e.g. color.hex.rgba, color.obj.rgba.r, color.arr.rgb[0] +// e.g. color.hex.rgba, color.obj.rgba.r, color.arr.rgb[0] console.log($fx.getParams()) // how to read a single raw parameter console.log("Single raw value:") -console.log($fx.getRawParam("color_id")) +console.log($fx.getRawParam("color_id")); // how to read a single transformed parameter console.log("Single transformed value:") -console.log($fx.getParam("color_id")) +console.log($fx.getParam("color_id")); // update the document based on the parameters document.body.style.background = $fx.getParam("color_id").hex.rgba diff --git a/examples/test-project/tsconfig.json b/examples/test-project/tsconfig.json index 48ee5ad..59a31b6 100644 --- a/examples/test-project/tsconfig.json +++ b/examples/test-project/tsconfig.json @@ -9,8 +9,8 @@ "emitDecoratorMetadata": true, "esModuleInterop": true, "experimentalDecorators": true, - "sourceMap": true, + "sourceMap": true }, "include": ["src/**/*.ts"], - "exclude": ["node_modules", "**/*.spec.ts"], + "exclude": ["node_modules", "**/*.spec.ts"] } diff --git a/packages/onchfs-js/src/files/file.ts b/packages/onchfs-js/src/files/file.ts index bb2dbc5..774692b 100644 --- a/packages/onchfs-js/src/files/file.ts +++ b/packages/onchfs-js/src/files/file.ts @@ -6,29 +6,6 @@ import { encodeMetadata } from "@/metadata/encode" import { FileInode, IFile, OnchfsPrepareOptions } from "@/types/files" import { computeFileCid } from "@/cid" -const MIME_LOOKUP = { - vert: "text/plain", -} - -/** - * Resolves the MIME type for a given filename. - * @param {string} filename - The name of the file. - * @returns {string|null} The determined MIME type or null if not found. - */ -function resolveMimeType(filename): string | null { - let mime = lookupMime(filename) - if (!mime) { - // fallback to extension lookup - const extension = filename.split(".").pop() - if (extension && MIME_LOOKUP[extension]) { - return MIME_LOOKUP[extension] - } - // return null if no MIME type is found - return null - } - return mime -} - /** * Computes all the necessary data for the inscription of the file on-chain. * Performs the following tasks in order: @@ -52,9 +29,14 @@ export function prepareFile( let metadata: FileMetadataEntries = {} let insertionBytes = content // we use file extension to get mime type - const mime = resolveMimeType(name) + let mime = lookupMime(name) + // if no mime type can be mapped from filename, use magic number if (!mime) { + // const magicMime = await fileTypeFromBuffer(content) + // if (magicMime) { + // metadata["Content-Type"] = magicMime.mime + // } // if still no mime, we simply do not set the Content-Type in the metadata, // and let the browser handle it. // We could set it to "application/octet-stream" as RFC2046 states, however diff --git a/packages/onchfs-js/src/resolver/errors.ts b/packages/onchfs-js/src/resolver/errors.ts index e94e4b0..6ca4f67 100644 --- a/packages/onchfs-js/src/resolver/errors.ts +++ b/packages/onchfs-js/src/resolver/errors.ts @@ -4,10 +4,7 @@ import { ProxyResolutionStatusErrors } from "@/types/resolver" * Error thrown during the resolution of a relative URI by the proxy. */ export class OnchfsProxyResolutionError extends Error { - constructor( - message: string, - public status: ProxyResolutionStatusErrors - ) { + constructor(message: string, public status: ProxyResolutionStatusErrors) { super(message) this.name = "OnchfsProxyResolutionError" } diff --git a/packages/onchfs-js/tsconfig.json b/packages/onchfs-js/tsconfig.json index b66d2c4..5ca2688 100644 --- a/packages/onchfs-js/tsconfig.json +++ b/packages/onchfs-js/tsconfig.json @@ -19,9 +19,9 @@ "noImplicitThis": false, "strictNullChecks": false, "paths": { - "@/*": ["./src/*"], - }, + "@/*": ["./src/*"] + } }, "include": ["src/**/*"], - "exclude": ["test/**/*"], + "exclude": ["test/**/*"] } From b7478cad38def1945526aca164c6f7eea37d089b Mon Sep 17 00:00:00 2001 From: Markus Maerzhase Date: Mon, 29 Jan 2024 11:09:48 +0100 Subject: [PATCH 29/61] Revert "Fix/eth prng precision" --- doc/docs/use-cases/generative-art.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/docs/use-cases/generative-art.md b/doc/docs/use-cases/generative-art.md index be60af0..e3e34d8 100644 --- a/doc/docs/use-cases/generative-art.md +++ b/doc/docs/use-cases/generative-art.md @@ -121,7 +121,7 @@ While previous projects leave an opiniated footprint on how code data is handled β”œβ”€β”€ style.css β”œβ”€β”€ main.js └── libs/ - β”œβ”€β”€ fxhash.min.js + β”œβ”€β”€ fxhash.js β”œβ”€β”€ colors.js └── processing.min.js ``` @@ -131,7 +131,7 @@ While previous projects leave an opiniated footprint on how code data is handled ```html - + @@ -157,7 +157,7 @@ Onchfs also handles libraries elegantly, and so naturally by its design. Looking β”œβ”€β”€ style.css -> 0xaeaeaed2... β”œβ”€β”€ main.js -> 0xa2a2a2a9... └── libs/ -> 0xd5d5d5d5... - β”œβ”€β”€ fxhash.min.js -> 0xc6c6c6c6... + β”œβ”€β”€ fxhash.js -> 0xc6c6c6c6... β”œβ”€β”€ colors.js -> 0xabcdef12... └── processing.min.js -> 0x01010101... @@ -169,7 +169,7 @@ inscriptions: + ... + DIRECTORY libs (0xd5d5d5d5...) { - "fxhash.min.js": 0xc6c6c6c6..., + "fxhash.js": 0xc6c6c6c6..., "colors.js": 0xabcdef12..., "processing.min.js": 0x01010101..., } @@ -191,7 +191,7 @@ inscriptions: + ... + DIRECTORY libs (0xd5d5d5d5...) { - "fxhash.min.js": 0xc6c6c6c6..., + "fxhash.js": 0xc6c6c6c6..., "colors.js": 0xabcdef12..., "processing.min.js": 0x01010101..., <- points to existing resource } From 1040da38301b2874edffcb156e92c52c08191ed6 Mon Sep 17 00:00:00 2001 From: Markus Maerzhase Date: Mon, 29 Jan 2024 11:22:35 +0100 Subject: [PATCH 30/61] Revert "Revert "Fix/eth prng precision"" --- doc/docs/use-cases/generative-art.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/docs/use-cases/generative-art.md b/doc/docs/use-cases/generative-art.md index e3e34d8..be60af0 100644 --- a/doc/docs/use-cases/generative-art.md +++ b/doc/docs/use-cases/generative-art.md @@ -121,7 +121,7 @@ While previous projects leave an opiniated footprint on how code data is handled β”œβ”€β”€ style.css β”œβ”€β”€ main.js └── libs/ - β”œβ”€β”€ fxhash.js + β”œβ”€β”€ fxhash.min.js β”œβ”€β”€ colors.js └── processing.min.js ``` @@ -131,7 +131,7 @@ While previous projects leave an opiniated footprint on how code data is handled ```html - + @@ -157,7 +157,7 @@ Onchfs also handles libraries elegantly, and so naturally by its design. Looking β”œβ”€β”€ style.css -> 0xaeaeaed2... β”œβ”€β”€ main.js -> 0xa2a2a2a9... └── libs/ -> 0xd5d5d5d5... - β”œβ”€β”€ fxhash.js -> 0xc6c6c6c6... + β”œβ”€β”€ fxhash.min.js -> 0xc6c6c6c6... β”œβ”€β”€ colors.js -> 0xabcdef12... └── processing.min.js -> 0x01010101... @@ -169,7 +169,7 @@ inscriptions: + ... + DIRECTORY libs (0xd5d5d5d5...) { - "fxhash.js": 0xc6c6c6c6..., + "fxhash.min.js": 0xc6c6c6c6..., "colors.js": 0xabcdef12..., "processing.min.js": 0x01010101..., } @@ -191,7 +191,7 @@ inscriptions: + ... + DIRECTORY libs (0xd5d5d5d5...) { - "fxhash.js": 0xc6c6c6c6..., + "fxhash.min.js": 0xc6c6c6c6..., "colors.js": 0xabcdef12..., "processing.min.js": 0x01010101..., <- points to existing resource } From a55541c4341932eaecd620ed38219df7ca6e9ea5 Mon Sep 17 00:00:00 2001 From: Markus Maerzhase Date: Mon, 29 Jan 2024 11:37:39 +0100 Subject: [PATCH 31/61] Dev -> staging (#911) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [core/website] dont randomise code-driven params add option to randmize all params * [core/website] lock and disbale code-driven params controllers * [core/website] dont create random variations with code-driven params * [repo] fix: fix reservoir Hasura env var usage (#820) * [repo] feat: fxhash sales bot discord (#796) * [repo] fix: fix sales bot workflow name * [core/sales-bot] chore: retrigger sales-bot deployment * [repo] fix: fix sales bot prod build config (#823) * take max of timeDiff and 0 to compute index (in case of artist mint before open) * add inputBytes to mint operation handler * add /free-live-minting-token route * [dashboard/backend] fix typo in route path free-live-minting-token * [repo] feat: use turborepo to manage seed-authority prisma db (#90) * [repo] fix: fix DOCKERFILE_ARG_BUILD_PATH for seed db ctrl * [seed-authority/db-ctrl] fix: retrigger seed-auth db-ctrl deployment * tokenId already string in live minting explore-params route * [core/sales-bot] fix: fix sales bot Buyer and Seller fields (#827) * remove storage limit for free mint * add overwriteCaptureSettings to reassign route * Change β€œLaunch Calendar” > β€œRelease Calendar” * [core/website] copy rename launch -> release * [repo] fix: fix sales bot ipfs configuration (#836) * [scripts/fxrepo] add pc option to 'preview compose' file * [repo] use alpine3.18 for dependency builder * [repo] only start turbo dev without running dependency tasks * [scripts/fxrepo] add option to run docker with detach flag * [scripts/fxrepo] run containers detached default; add attach option * [scripts/fxrepo] show service status when --preview * [scripts/fxrepo] restart dependency-builder when starting additional services and merge dependencies of running and new services * [repo] tag depdency-builder to always overwrite if no tag is given we end up with a lot of images * [core/api-v2] include @fxhash node_modules with nodemon watch * [scripts/fxrepo] build fxrepo * [scripts/fxrepo] build dependency of dependencies in dependecy-builder * fix: fix floor warning (#834) * fix floor warning * remove obsolete warning * Ingore reservoir sales updates (#837) * ignore sales update events * [repo] fix: fix missing onchfs deps in contracts package * remove launch from "launch partners" * [repo] feat: create reservoir db-ctrl and metadata autoapply for Hasura reservoir (#824) * [core/reservoir-sync-node] fix: fix reservoir node production docker build * [authentication/db-ctrl] feat: use turbo to manage authentication prisma db * [repo] prepare db-ctrl package * [core/api-v2] api-v2 use package * [repo] api-v2 package * [repo] add more fixed items to build * [authentication/db-ctrl] delete * [repo] use root folder * [repo] deploy config * [repo] Update package.json * [repo] fix turbo prisma docker image * [repo] fix local env * fix: fix reduce supply indexing (#839) * fix reduce supply indexing * fix processing of new supply * feat: improve eth data reset (#821) * add cascade delete for tokens * finish refactoring eth data reset * [core/website] fix eth mint button style (#807) * [core/website] fix eth mint button style * [core/website] smaller width increment button * fix: remove executec/canceled eth collab tx + allow updatable multisig tx (#838) * fix safe queries * remove using a new nonce for each tx for safe * feat: add collection offers on objkt iteration offer tab (#844) * add collection offers on objkt iteration offer tab * add collection offer ownership check when objkt defined --------- Co-authored-by: Louis Holley * handle non-checksummed eth addresses (#847) * use parseAddress where input may be an eth address * remove } from parsed addr * [core/api-v2] add randomIteration query * [core/website] add large hero section * fix description of randomIteration query * fix tags indexing (#848) * use accounts (not users) for search queries * [repo] feat: move prettier config to the root of the repo (#853) * use high res preview for opensea image * wip fix eth previews script * [repo] feat: add prettier check on the CI (#854) * use s3BucketName over hardcoded string * temp ignore missing input ts error for build * [core/api-v2] fix: fix prettier check after last merge * [core/website] feat: upgrade next.js to 14.X (#843) * [repo] fix: fix prettier generated files * still wip fix-eth-previews script * [packages/utils] implement kolmogorov smirnov test for prng * use metadata from api to obtain displayUri in preview fix script * thumbnailUri -> displayUri for contractURI metadata image * include preview fix for project metadata * format script * use preview for /thumbnail.png also * fix: fix market stats calculations + add fiat ranking (#831) * ignore inactive listings * add fiat to marketstats * fix stats processing * update api and website to align with new stats * also add crypto in rankings * increase size * pin version * rollback fail * update lock * [core/website] make size optional * remove comment * clean comments * fix build * ignore onchfs error preventing build * fix build * fix sync node build/dev * [core/reservoir-sync-node] fix build * update layouts * update lock * format * format * fix build --------- Co-authored-by: maerzhase Co-authored-by: LΓ©o Pradel * rollback changes for tags and ignore its indexing * [repo] Update pnpm-lock.yaml * fix tag indexing * fix format * ensure token_id is set for TICKET_CLAIMED actions * [core/website] update refetch strategoy for hero iteration * [core/website] fix display price styles (#859) - refactor priceDisplay -> size - remove color from display price - fix alignment of time selector for ranking * [core/api-v2] retrieve iterationsCount from supply - balance * [repo] fix: revert turbo upgrade * [repo] fix: fix prod deployment docker images for prisma (#862) * various fixes (#863) * [repo] feat: migrate og images to next 14 (#858) * [repo] feat: migrate og images to next 14 * [core/website] migrate grid * [core/website] Update og-grid.tsx * [core/website] fix * [core/website] add missing next config * fix: fix and optimize onchain filter (#846) * fix and optimize onchain filter * use MainDataSource for operation handler connection * [core/website] feat: wallet management layout * [core/website] some changes * return updated objkt from updateGenerationHash * [repo] fix: fix db-ctrl prod docker images * [core/api-v2] add excludeIteration arg to randomIteration * [repo] Update Dockerfile.k8s-prisma-db-turbo * [core/website] call randomIteration with current iteration excluded * [repo] chore: remove unused swc-core deps (#850) * [repo] chore: remove unused swc-core deps --------- Co-authored-by: Florian PAUTOT * [core/api] fix: fix api start script * Revert "[repo] chore: remove unused swc-core deps (#850)" This reverts commit 75f8f2fd6f188d760a11ccba45def193cadca24c. * fix seed authority entrypoint * [seed-authority/api] use relative paths (#871) * [seed-authority/api] use relative paths * format --------- Co-authored-by: Florian PAUTOT * [seed-authority/worker] use relative imports; fix build (#873) * [seed-authority/worker] use relative imports; fix build * format --------- Co-authored-by: Florian PAUTOT * [core/api-v2] better strategy for allow iteration exclusion * [core/api-v2] pnpm format * [repo] turbo prune workspace for dependency builder * [core/api-v2] run pnpm format * [core/website] implement QA for home hero large section * [fxhash-package/packages] bump versions * [core/website] increase large hero height * [core/website] button transparent = border transparent * [repo] feat: update connectkit to v1.6 to add support for EIP-6963. (#880) * enable free live minting for projects w/ no allow list * add fallback mime lookup to onchfs file lib * feat: auto force royalties + index external orders + batchable marketplace + updatable listing and offers (#783) * add metadata fields to orders * add metadata to created listings/offers * add auto royalties + make all marketeplace operations batchable * update reservoir env * fix reservoir indexer build * update hasura metadata for listing/offer metadata field * use new marketplace operation params * return early from handlers when objkt not found * expose metadata fields on api * add external listings to frontend * Feat/eth/aggregate all orders with royalties dynamic contracts (#812) * update active to filter to include inactive flag * remove log * fix checks for inactive orders * add further isInactive filtering * revert add additional filters * add queries to fetch individual listings/offers * Revert "add queries to fetch individual listings/offers" This reverts commit dd53140b6245f97820f8dbae2c5b9d2a02a993a2. * wip * skip mod lock for lockEnd === opensAt * rm log * [core/website] fix: fix tez label on gentk marketplace page * add rebate warning * show cancellable offers/listings in MarketplaceActions * add express server to eth-indexer * prefer account over user in MarketplaceActions * correct getGenerativeTokenPrice implementation for dutch auctions * checksum addresses used for whitelist * 401 -> 400 error * Warning component links to home page * add inactive offers/listings to queries * update types with inactive offers/listings * add InactiveBadge component * add InactiveBadge to user offers * add inactive badge to user listings * fix marketplace sales feed * use account names over user names in marketplace rankings * set max decimals to 2 on marketplace rankings * remove launch_timestamp check * return null instead of throwing error when finding available whitelist * parse reserve id as int before deleting * last ditch attempt * remove bunk fix * patch subsquid to remove sql typecast * force reserve id to int when removing * commit reserve script * copy patches from root in subsquid dockerfiler * manually copy patches into /app/out * revert subsquid dockerfile changes * revert bunk patch * patch subsquid to avoid unnecessary typecast * add eth donation addresses * pass chain prop to Donations components * fix rendering of donation users * add missing chain prop to article donations * fix mint progress filters for open editions * remove (tez) from marketplace metrics * hide inactive listings from activeListing_exist objkt filter * map raw dutch auction price db fields to camelCase * skip actions with no da pricing in refund button * remove hardcoded ids * set acceptedAt when listing accepted * remove ownership transfer from handleInsertSaleEvent (incorrect for credit card purchase) * fix issuer/target of action in reservoir sale * [fxhash-package/packages] feat: refactor eth tx processing + show tx id in error message * [repo] more * [packages/fxhash-eth-sdk] reservoir * remove duplicate actions/transactions for offers * add default transport to remove winston memory errors from prod * [packages/fxhash-eth-sdk] more * [packages/fxhash-eth-sdk] more * [packages/fxhash-eth-sdk] safe * [packages/fxhash-eth-sdk] tickets * [packages/fxhash-eth-sdk] last * [core/website] update docs links in footer * [core/website] better error message * [repo] all Tez txs are ONCHAIN * fix variations button for open editions * [core/website] make link clickable * change donations order * convert added da level to string * return receipt with contract operation data * handle transaction not found error from viem * get it compiling * rm unused * revert transaction receipt error * handle errors in waitForTransaction * fix contract feedback in minting components * rm spacing below feedback * fix display price for mint ticket modal * reduce min decrement duration to 1 minute * fix seed retrieval from seed authority * fix tezos splits update * min da tier to 3 minutes * [packages/fxhash-eth-sdk] fix: fix WithdrawAllEthV1Operation when nothing can be withdrawn * add TransactionCaughtError for better error messages * utilise TransactionUnknownError for custom error messages * [core/website] remove launch banner in calendar * temp commit * Chore/sepolia migration (#810) * start migrating config to Sepolia * update start block * update eth indexer env * add missing config * update contracts to align with new split contract * update queue type for reservoir indexer * fix rabbitmq startup script * rollback env file --------- Co-authored-by: Louis Holley Co-authored-by: LΓ©o Pradel Co-authored-by: Markus Maerzhase Co-authored-by: maerzhase * make listings and offers updatable * format --------- Co-authored-by: Louis Holley Co-authored-by: LΓ©o Pradel Co-authored-by: Markus Maerzhase Co-authored-by: maerzhase * add shouldEnqueue arg to GentkSigner * add signingEnabled util * pass shouldEnqueue arg to signingEnabled from indexer-v2 * enable overwrite capture settings on eth * fix reservoir db ctrl CI * [repo] rename fxhash.js -> fxhash.min.js * pass null for live minting wallets when needed * remove throw when no live minting wallets * revert live minting wallets event form change * add extra safety check in getWalletConstraints * overwrite liveMintingWallets when list empty * add use reserve arg to OH_MINT * disconnect records when live minting wallets empty * fix reservoir image build * [packages/fxhash-cli] handle fxhash.min.js with cli * [repo] update lockfile * [repo] add fxhash.min.js to .prettierignore * [repo] pin zip-stream dep * [repo] feat: create hasura metadata deployment pipelines (#828) * [repo] feat: create hasura metadata ctrl * [repo] create docker image * [repo] Create core_hasura_metadata.yml * [repo] test * [repo] trigger * [repo] add production * [repo] add reservoir * [repo] only run if branch * Delete hasura/Dockerfile.hasura-metadata * [repo] add missing check in the reservoir file * fix hasura metadata * fix marketstats columns (#885) * [repo] fix: fix db-ctrl config after latest fix (#886) * trigger CI + add retry for reservoir hasura * [core/website] add missing aria-hidden * [core/website] remove console.log * use account name in ObjktDetails meta head * [core/website] don't parseInt artwork ids in articles * [core/website] allow ethereum projects in articles * [core/website] handle account usernames; fix article user mentions * [core/website] fix type error * don't multiple by qty as price field already reflects total * initialise count with qty * add withdrawal caveat to EthWalletWithdraw * revert at withdrawal caveat * revert add withdrawal caveat * [repo] feat: setup website-v2 starter * [repo] helper * [repo] update react types * [repo] prettier lib folder * [core/website] style add wallet button * [core/website] copy update; font styles; border styles * [repo/apps] delete local .prettierrc files * [repo/apps] run pnpm format * [events/live-viewer] explicitly use webpack-manifset-pluing@4.0.2 * [repo] update lockfile * [repo] Update pnpm-lock.yaml * [repo] remove concurrency flag * [repo] remove image tag from dependecy builder * [scripts/fxrepo] clarify addVolumeCached method * [repo] add theme provider * [repo] feat: use turborepo to manage dashboard prisma db (#146) * [core/website-v2] add TODO * improve reset eth data performance (#898) * [apps/dashboard] fix: fix dashboard start script (#899) * improve script (#901) * [core/website] strinigfy projectids for event fetch * [core/website] add thumbnail media to event metas * Revert "feat: improve eth data reset (#821)" (#902) * Revert "feat: improve eth data reset (#821)" This reverts commit 7bfa68cb128b941c152c037d7b3907063a5252da. * format * [repo] chore: cleanup after db-ctrl migration to turbo (#903) * add additional listing and offer filters * improve reset script * disable wipe * Revert "staging -> main (#872)" * add both addresses to holders payload * [core/website] fix display price size styles; add size big * [core/api-v2] run pnpm format * [repo] update v4 mock * Revert "Fix/eth prng precision" * Revert "Revert "Fix/eth prng precision"" * Revert "feat: auto force royalties + index external orders + batchable marketplace + updatable listing and offers (#783)" (#914) This reverts commit 06e6a56ad3a05e9d3f9e79aab853f46fbe76616d. --------- Co-authored-by: m3000 Co-authored-by: LΓ©o Pradel Co-authored-by: Louis Holley Co-authored-by: Florian PAUTOT --- doc/docs/use-cases/generative-art.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/docs/use-cases/generative-art.md b/doc/docs/use-cases/generative-art.md index e3e34d8..be60af0 100644 --- a/doc/docs/use-cases/generative-art.md +++ b/doc/docs/use-cases/generative-art.md @@ -121,7 +121,7 @@ While previous projects leave an opiniated footprint on how code data is handled β”œβ”€β”€ style.css β”œβ”€β”€ main.js └── libs/ - β”œβ”€β”€ fxhash.js + β”œβ”€β”€ fxhash.min.js β”œβ”€β”€ colors.js └── processing.min.js ``` @@ -131,7 +131,7 @@ While previous projects leave an opiniated footprint on how code data is handled ```html - + @@ -157,7 +157,7 @@ Onchfs also handles libraries elegantly, and so naturally by its design. Looking β”œβ”€β”€ style.css -> 0xaeaeaed2... β”œβ”€β”€ main.js -> 0xa2a2a2a9... └── libs/ -> 0xd5d5d5d5... - β”œβ”€β”€ fxhash.js -> 0xc6c6c6c6... + β”œβ”€β”€ fxhash.min.js -> 0xc6c6c6c6... β”œβ”€β”€ colors.js -> 0xabcdef12... └── processing.min.js -> 0x01010101... @@ -169,7 +169,7 @@ inscriptions: + ... + DIRECTORY libs (0xd5d5d5d5...) { - "fxhash.js": 0xc6c6c6c6..., + "fxhash.min.js": 0xc6c6c6c6..., "colors.js": 0xabcdef12..., "processing.min.js": 0x01010101..., } @@ -191,7 +191,7 @@ inscriptions: + ... + DIRECTORY libs (0xd5d5d5d5...) { - "fxhash.js": 0xc6c6c6c6..., + "fxhash.min.js": 0xc6c6c6c6..., "colors.js": 0xabcdef12..., "processing.min.js": 0x01010101..., <- points to existing resource } From 31f0807055cceea1df962b4c22fb0c6110aa3fb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Pradel?= Date: Thu, 8 Feb 2024 15:41:53 +0100 Subject: [PATCH 32/61] [repo] feat: update viem to 1.21.4 for base (#967) --- examples/test-project/package.json | 2 +- packages/onchfs-js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/test-project/package.json b/examples/test-project/package.json index 74b1fa7..3af0d22 100644 --- a/examples/test-project/package.json +++ b/examples/test-project/package.json @@ -23,7 +23,7 @@ "node-dir": "0.1.17", "onchfs": "workspace:*", "tslib": "2.6.0", - "viem": "1.14.0" + "viem": "1.21.4" }, "devDependencies": { "@types/node": "18.7.13", diff --git a/packages/onchfs-js/package.json b/packages/onchfs-js/package.json index 6fb30e4..d696594 100644 --- a/packages/onchfs-js/package.json +++ b/packages/onchfs-js/package.json @@ -27,7 +27,7 @@ "js-sha3": "0.9.1", "mime-types": "2.1.35", "pako": "2.1.0", - "viem": "1.14.0" + "viem": "1.21.4" }, "devDependencies": { "@babel/core": "7.22.19", From 054db1fbf8c3c9f3fc7f43a176273a6835d13e96 Mon Sep 17 00:00:00 2001 From: maerzhase Date: Fri, 9 Feb 2024 16:54:16 +0100 Subject: [PATCH 33/61] [packages/onchfs-js] migrate tsconfig to @fxhash/tsconfig --- packages/onchfs-js/package.json | 5 +++-- packages/onchfs-js/tsconfig.json | 27 +++++++++------------------ 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/packages/onchfs-js/package.json b/packages/onchfs-js/package.json index d696594..e62241b 100644 --- a/packages/onchfs-js/package.json +++ b/packages/onchfs-js/package.json @@ -30,6 +30,7 @@ "viem": "1.21.4" }, "devDependencies": { + "@fxhash/tsconfig": "workspace:*", "@babel/core": "7.22.19", "@babel/preset-env": "7.22.15", "@babel/preset-typescript": "7.22.15", @@ -43,7 +44,7 @@ "ts-jest": "29.1.1", "tsc-alias": "1.8.5", "tslib": "2.6.0", - "tsup": "6.6.0", - "typescript": "4.9.5" + "tsup": "8.0.1", + "typescript": "5.3.3" } } diff --git a/packages/onchfs-js/tsconfig.json b/packages/onchfs-js/tsconfig.json index b66d2c4..d58f806 100644 --- a/packages/onchfs-js/tsconfig.json +++ b/packages/onchfs-js/tsconfig.json @@ -1,27 +1,18 @@ { + "extends": "@fxhash/tsconfig/base", + "include": ["src/**/*"], + "exclude": ["node_modules", "test/**/*"], "compilerOptions": { - "target": "ES2022", - "module": "CommonJS", - "lib": ["ES2022", "DOM"], - "moduleResolution": "node", "rootDir": "src", "outDir": "dist", - "allowSyntheticDefaultImports": true, - "importHelpers": true, - "alwaysStrict": true, - "sourceMap": true, - "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true, - "noImplicitReturns": true, - "noUnusedLocals": false, - "noUnusedParameters": true, - "noImplicitAny": false, - "noImplicitThis": false, - "strictNullChecks": false, "paths": { "@/*": ["./src/*"], }, + "target": "es2022", + "lib": ["es2022", "DOM"], + // FIXME: resolve type errors when removing the following lines + "strict": false, + "noUnusedParameters": false, + "noUnusedLocals": false, }, - "include": ["src/**/*"], - "exclude": ["test/**/*"], } From e281d865ff1a830f9b1880908da0fdfa9edb8cf6 Mon Sep 17 00:00:00 2001 From: Florian Date: Mon, 12 Feb 2024 10:15:59 +0100 Subject: [PATCH 34/61] dev -> staging (#971) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [packages/tsconfig] add @fxhash/tsconfig * [packages/utils] use @fxhash/tsconfig * [packages/project-sdk] use @fxhash/tsconfig * [packages/libraries] use @fxhash/tsconfig * [packages/gql] use @fxhash/tsconfig * [packages/gql-client] use @fxhash/tsconfig * [packages/fxhash-snippet] use @fxhash/tsconfig * [packages/fxhash-react] use @fxhash/tsconfig * [packages/fxhash-params] use @fxhash/tsconfig * [packages/fxhash-eth-sdk] use @fxhash/tsconfig * [packages/fxhash-contracts] use @fxhash/tsconfig * [packages/fxhash-contracts-shared] use @fxhash/tsconfig * [packages/fxhash-config] use @fxhash/tsconfig * [packages/fxhash-cli] use @fxhash/tsconfig * [packages/auth] use @fxhash/tsconfig * [repo] update lockfile * [packages/project-sdk] overwrite project-sdk build * [repo] Update lockfile * [packages/project-sdk] change build script to build-sdk to prevent auto builds in ci * [packages/fxhash-cli] pnpm format * initialize main datasource in api-v2 * upgrade prisma in auth-db * update pnpm-lock * currency service for fiat exchange rates (#930) implement currency service for fiat pricing conversions * [core/website] upgrade typescript version * [private/fxhash-playground] upgrade typescript version * [repo] update lockfile * [packages/tsconfig] resort compiler options * [repo] remove unused private utils package * [private/otel-tracing] use @fxhash/tsconfig * [private/indexer-toolkit] use @fxhash/tsconfig * [private/indexer-toolkit-v2] use @fxhash/tsconfig * [private/gql-admin] use @fxhash/tsconfig * [private/danger] use @fxhash/tsconfig * [repo] update lockfile * [eth] implement batchable marketplace actions (#963) [eth] implement batchable marketplace actions * [repo] feat: update viem to 1.21.4 for base (#967) * [packages/tsconfig] use node18 instead of lts because our services use node 18 * add fix reservoir sync node * [packages/tsconfig] adjust shared compiler options * [packages/fxhash-params] fix build errors * [packages/utils] fix build errors * [packages/fxhash-eth-sdk] add FIXME tsconfig rules * [packages/fxhash-contracts] add FIXME tsconfig rules * [packages/auth] fix build errors * [private/indexer-toolkit] add FIXME tsconfig rules * [repo] update lockfile * [packages/fxhash-contracts] revert alias to relative * [private/indexer-toolkit-v2] add FIXME tsconfig rules * [repo] upgrade tsup@8.0.1 * [repo] update lockfile * [packages/tsconfig] remove rules set by strict * [packages/onchfs-js] migrate tsconfig to @fxhash/tsconfig * [repo/packages] remove fixme lines already set through strict false * [repo] update lockfile * feat: sales bot post high value sales on main chat (#961) --------- Co-authored-by: maerzhase Co-authored-by: Louis Holley Co-authored-by: LΓ©o Pradel Co-authored-by: Markus Maerzhase --- examples/test-project/package.json | 2 +- packages/onchfs-js/package.json | 7 ++++--- packages/onchfs-js/tsconfig.json | 27 +++++++++------------------ 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/examples/test-project/package.json b/examples/test-project/package.json index 74b1fa7..3af0d22 100644 --- a/examples/test-project/package.json +++ b/examples/test-project/package.json @@ -23,7 +23,7 @@ "node-dir": "0.1.17", "onchfs": "workspace:*", "tslib": "2.6.0", - "viem": "1.14.0" + "viem": "1.21.4" }, "devDependencies": { "@types/node": "18.7.13", diff --git a/packages/onchfs-js/package.json b/packages/onchfs-js/package.json index 6fb30e4..e62241b 100644 --- a/packages/onchfs-js/package.json +++ b/packages/onchfs-js/package.json @@ -27,9 +27,10 @@ "js-sha3": "0.9.1", "mime-types": "2.1.35", "pako": "2.1.0", - "viem": "1.14.0" + "viem": "1.21.4" }, "devDependencies": { + "@fxhash/tsconfig": "workspace:*", "@babel/core": "7.22.19", "@babel/preset-env": "7.22.15", "@babel/preset-typescript": "7.22.15", @@ -43,7 +44,7 @@ "ts-jest": "29.1.1", "tsc-alias": "1.8.5", "tslib": "2.6.0", - "tsup": "6.6.0", - "typescript": "4.9.5" + "tsup": "8.0.1", + "typescript": "5.3.3" } } diff --git a/packages/onchfs-js/tsconfig.json b/packages/onchfs-js/tsconfig.json index b66d2c4..d58f806 100644 --- a/packages/onchfs-js/tsconfig.json +++ b/packages/onchfs-js/tsconfig.json @@ -1,27 +1,18 @@ { + "extends": "@fxhash/tsconfig/base", + "include": ["src/**/*"], + "exclude": ["node_modules", "test/**/*"], "compilerOptions": { - "target": "ES2022", - "module": "CommonJS", - "lib": ["ES2022", "DOM"], - "moduleResolution": "node", "rootDir": "src", "outDir": "dist", - "allowSyntheticDefaultImports": true, - "importHelpers": true, - "alwaysStrict": true, - "sourceMap": true, - "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true, - "noImplicitReturns": true, - "noUnusedLocals": false, - "noUnusedParameters": true, - "noImplicitAny": false, - "noImplicitThis": false, - "strictNullChecks": false, "paths": { "@/*": ["./src/*"], }, + "target": "es2022", + "lib": ["es2022", "DOM"], + // FIXME: resolve type errors when removing the following lines + "strict": false, + "noUnusedParameters": false, + "noUnusedLocals": false, }, - "include": ["src/**/*"], - "exclude": ["test/**/*"], } From dec767c3764233de66b6932a73c6406e224ba8e2 Mon Sep 17 00:00:00 2001 From: Florian Date: Mon, 12 Feb 2024 10:40:00 +0100 Subject: [PATCH 35/61] staging -> main (#973) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix reservoir indexer * dev -> staging * ignore remove/reindex inactive listings * ignore aloglia reindeinx for newly re-active listings * dev -> staging (#957) * ignore remove/reindex inactive listings * ignore aloglia reindeinx for newly re-active listings * dev -> staging (#971) * [packages/tsconfig] add @fxhash/tsconfig * [packages/utils] use @fxhash/tsconfig * [packages/project-sdk] use @fxhash/tsconfig * [packages/libraries] use @fxhash/tsconfig * [packages/gql] use @fxhash/tsconfig * [packages/gql-client] use @fxhash/tsconfig * [packages/fxhash-snippet] use @fxhash/tsconfig * [packages/fxhash-react] use @fxhash/tsconfig * [packages/fxhash-params] use @fxhash/tsconfig * [packages/fxhash-eth-sdk] use @fxhash/tsconfig * [packages/fxhash-contracts] use @fxhash/tsconfig * [packages/fxhash-contracts-shared] use @fxhash/tsconfig * [packages/fxhash-config] use @fxhash/tsconfig * [packages/fxhash-cli] use @fxhash/tsconfig * [packages/auth] use @fxhash/tsconfig * [repo] update lockfile * [packages/project-sdk] overwrite project-sdk build * [repo] Update lockfile * [packages/project-sdk] change build script to build-sdk to prevent auto builds in ci * [packages/fxhash-cli] pnpm format * initialize main datasource in api-v2 * upgrade prisma in auth-db * update pnpm-lock * currency service for fiat exchange rates (#930) implement currency service for fiat pricing conversions * [core/website] upgrade typescript version * [private/fxhash-playground] upgrade typescript version * [repo] update lockfile * [packages/tsconfig] resort compiler options * [repo] remove unused private utils package * [private/otel-tracing] use @fxhash/tsconfig * [private/indexer-toolkit] use @fxhash/tsconfig * [private/indexer-toolkit-v2] use @fxhash/tsconfig * [private/gql-admin] use @fxhash/tsconfig * [private/danger] use @fxhash/tsconfig * [repo] update lockfile * [eth] implement batchable marketplace actions (#963) [eth] implement batchable marketplace actions * [repo] feat: update viem to 1.21.4 for base (#967) * [packages/tsconfig] use node18 instead of lts because our services use node 18 * add fix reservoir sync node * [packages/tsconfig] adjust shared compiler options * [packages/fxhash-params] fix build errors * [packages/utils] fix build errors * [packages/fxhash-eth-sdk] add FIXME tsconfig rules * [packages/fxhash-contracts] add FIXME tsconfig rules * [packages/auth] fix build errors * [private/indexer-toolkit] add FIXME tsconfig rules * [repo] update lockfile * [packages/fxhash-contracts] revert alias to relative * [private/indexer-toolkit-v2] add FIXME tsconfig rules * [repo] upgrade tsup@8.0.1 * [repo] update lockfile * [packages/tsconfig] remove rules set by strict * [packages/onchfs-js] migrate tsconfig to @fxhash/tsconfig * [repo/packages] remove fixme lines already set through strict false * [repo] update lockfile * feat: sales bot post high value sales on main chat (#961) --------- Co-authored-by: maerzhase Co-authored-by: Louis Holley Co-authored-by: LΓ©o Pradel Co-authored-by: Markus Maerzhase --------- Co-authored-by: maerzhase Co-authored-by: Louis Holley Co-authored-by: LΓ©o Pradel Co-authored-by: Markus Maerzhase --- examples/test-project/package.json | 2 +- packages/onchfs-js/package.json | 7 ++++--- packages/onchfs-js/tsconfig.json | 27 +++++++++------------------ 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/examples/test-project/package.json b/examples/test-project/package.json index 74b1fa7..3af0d22 100644 --- a/examples/test-project/package.json +++ b/examples/test-project/package.json @@ -23,7 +23,7 @@ "node-dir": "0.1.17", "onchfs": "workspace:*", "tslib": "2.6.0", - "viem": "1.14.0" + "viem": "1.21.4" }, "devDependencies": { "@types/node": "18.7.13", diff --git a/packages/onchfs-js/package.json b/packages/onchfs-js/package.json index 6fb30e4..e62241b 100644 --- a/packages/onchfs-js/package.json +++ b/packages/onchfs-js/package.json @@ -27,9 +27,10 @@ "js-sha3": "0.9.1", "mime-types": "2.1.35", "pako": "2.1.0", - "viem": "1.14.0" + "viem": "1.21.4" }, "devDependencies": { + "@fxhash/tsconfig": "workspace:*", "@babel/core": "7.22.19", "@babel/preset-env": "7.22.15", "@babel/preset-typescript": "7.22.15", @@ -43,7 +44,7 @@ "ts-jest": "29.1.1", "tsc-alias": "1.8.5", "tslib": "2.6.0", - "tsup": "6.6.0", - "typescript": "4.9.5" + "tsup": "8.0.1", + "typescript": "5.3.3" } } diff --git a/packages/onchfs-js/tsconfig.json b/packages/onchfs-js/tsconfig.json index b66d2c4..d58f806 100644 --- a/packages/onchfs-js/tsconfig.json +++ b/packages/onchfs-js/tsconfig.json @@ -1,27 +1,18 @@ { + "extends": "@fxhash/tsconfig/base", + "include": ["src/**/*"], + "exclude": ["node_modules", "test/**/*"], "compilerOptions": { - "target": "ES2022", - "module": "CommonJS", - "lib": ["ES2022", "DOM"], - "moduleResolution": "node", "rootDir": "src", "outDir": "dist", - "allowSyntheticDefaultImports": true, - "importHelpers": true, - "alwaysStrict": true, - "sourceMap": true, - "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true, - "noImplicitReturns": true, - "noUnusedLocals": false, - "noUnusedParameters": true, - "noImplicitAny": false, - "noImplicitThis": false, - "strictNullChecks": false, "paths": { "@/*": ["./src/*"], }, + "target": "es2022", + "lib": ["es2022", "DOM"], + // FIXME: resolve type errors when removing the following lines + "strict": false, + "noUnusedParameters": false, + "noUnusedLocals": false, }, - "include": ["src/**/*"], - "exclude": ["test/**/*"], } From 96b7217c091df35df610ebd04171b04c6c5f35bf Mon Sep 17 00:00:00 2001 From: louis holley Date: Tue, 20 Feb 2024 15:27:39 +0000 Subject: [PATCH 36/61] sepolia + base onchfs (#1006) --- packages/onchfs-js/src/config.ts | 14 ++++++++++++-- packages/onchfs-js/src/types/resolver.ts | 3 +++ packages/onchfs-js/src/types/uri.ts | 3 +++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/onchfs-js/src/config.ts b/packages/onchfs-js/src/config.ts index 7a71f93..7be6847 100644 --- a/packages/onchfs-js/src/config.ts +++ b/packages/onchfs-js/src/config.ts @@ -21,16 +21,26 @@ export const CHAIN_IDS = { eip155: { mainnet: "1", goerli: "5", + sepolia: "11155111", + baseMainnet: "8453", + baseSepolia: "84532", }, } as const -// TODO: insert true values here. // A naive map of the "official" onchfs Smart Contracts. export const DEFAULT_CONTRACTS: Record = { // tezos mainnet "tezos:NetXdQprcVkpaWU": "KT1Ae7dT1gsLw2tRnUMXSCmEyF74KVkM6LUo", // tezos ghostnet "tezos:NetXnHfVqm9iesp": "KT1FA8AGGcJha6S6MqfBUiibwTaYhK8u7s9Q", + // eth mainnet "eip155:1": "0x9e0f2864c6f125bbf599df6ca6e6c3774c5b2e04", - "eip155:5": "0x9e0f2864c6f125bbf599df6ca6e6c3774c5b2e04", + // eth goerli + "eip155:5": "0xc3f5ef1a0256b9ceb1452650db72344809bb3a85", + // eth sepolia + "eip155:11155111": "0x4f555d39e89f6d768f75831d610b3940fa94c6b1", + // base mainnet + "eip155:8453": "0x2983008f292a43f208bba0275afd7e9b3d39af3b", + // base sepolia + "eip155:84532": "0x3fb48e03291b2490f939c961a1ad088437129f71", } diff --git a/packages/onchfs-js/src/types/resolver.ts b/packages/onchfs-js/src/types/resolver.ts index 720d48c..4c234b9 100644 --- a/packages/onchfs-js/src/types/resolver.ts +++ b/packages/onchfs-js/src/types/resolver.ts @@ -11,6 +11,9 @@ export const chainAliases = { "tezos:NetXnHfVqm9iesp": ["tezos:ghostnet"] as const, "eip155:1": ["ethereum:mainnet", "eth:mainnet"] as const, "eip155:5": ["ethereum:goerli", "eth:goerli"] as const, + "eip155:11155111": ["ethereum:sepolia", "eth:sepolia"] as const, + "eip155:84532": ["ethereum:baseSepolia", "eth:baseSepolia"] as const, + "eip155:8453": ["ethereum:baseMainnet", "eth:baseMainnet"] as const, } as const export type ChainAliases = | BlockchainNetwork diff --git a/packages/onchfs-js/src/types/uri.ts b/packages/onchfs-js/src/types/uri.ts index 79402a0..f7d9eb9 100644 --- a/packages/onchfs-js/src/types/uri.ts +++ b/packages/onchfs-js/src/types/uri.ts @@ -16,6 +16,9 @@ export const blockchainNetworks = [ `tezos:${CHAIN_IDS.tezos.ghostnet}`, `eip155:${CHAIN_IDS.eip155.mainnet}`, `eip155:${CHAIN_IDS.eip155.goerli}`, + `eip155:${CHAIN_IDS.eip155.sepolia}`, + `eip155:${CHAIN_IDS.eip155.baseMainnet}`, + `eip155:${CHAIN_IDS.eip155.baseSepolia}`, ] as const export type BlockchainNetwork = (typeof blockchainNetworks)[number] From e23a49fc2882220edc0fa218db3b3b04d1e03522 Mon Sep 17 00:00:00 2001 From: Markus Maerzhase Date: Wed, 21 Feb 2024 16:19:27 +0100 Subject: [PATCH 37/61] Refactor/packages names and eliminate dead code (#1025) * [repo] rename folder fxhash-cli -> cli * [repo] rename folder fxhash-config -> config * [repo] rename @fxhash/contracts -> @fxhash/tez * [repo] rename @fxhash/eth-sdk -> @fxhash/eth * [repo] rename @fxhash/contracts-shared -> @fxhash/shared * [repo] rename folder fxhash-params -> params * [repo] rename folder fxhash-react -> react * [repo] rename folder fxhash-snippet -> snippet * [repo] run pnpm format * [repo] rename @fxhash/fxhash-snippet -> @fxhash/snippet * [repo] remove BlockchainType from @fxhash/tez, use @fxhash/shared instead * [repo] move shared types to @fxhash/shared; remove unsued code in @fxhash/tez * [packages/eth] remove unused code * [repo] derive blockchain from BlockchainType * [core/website] isEthOrL2 typeguard --- examples/test-project/package.json | 2 +- examples/test-project/src/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/test-project/package.json b/examples/test-project/package.json index 3af0d22..d8c0f39 100644 --- a/examples/test-project/package.json +++ b/examples/test-project/package.json @@ -15,7 +15,7 @@ }, "dependencies": { "@fxhash/config": "workspace:*", - "@fxhash/eth-sdk": "workspace:*", + "@fxhash/eth": "workspace:*", "@taquito/signer": "17.3.0", "@taquito/taquito": "17.3.0", "@types/node-dir": "0.0.34", diff --git a/examples/test-project/src/index.ts b/examples/test-project/src/index.ts index 87ba373..f079227 100644 --- a/examples/test-project/src/index.ts +++ b/examples/test-project/src/index.ts @@ -9,7 +9,7 @@ import { bytesToHex, createPublicClient, createWalletClient, http } from "viem" import { privateKeyToAccount } from "viem/accounts" import { goerli } from "viem/chains" import { config } from "@fxhash/config" -import { ONCHFS_FILE_SYSTEM_ABI, ONCHFS_CONTENT_STORE } from "@fxhash/eth-sdk" +import { ONCHFS_FILE_SYSTEM_ABI, ONCHFS_CONTENT_STORE } from "@fxhash/eth" async function sleep(time: number) { return new Promise(resolve => setTimeout(resolve, time)) From dbbf788211bdf5f04b0cc49ed84c21aee778624b Mon Sep 17 00:00:00 2001 From: louis holley Date: Thu, 22 Feb 2024 14:41:10 +0000 Subject: [PATCH 38/61] Dev -> Staging (#1041) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix generated imports * use secVolume for market stats chart * fix wallet moderation reason loader * Refactor/packages names and eliminate dead code (#1025) * [repo] rename folder fxhash-cli -> cli * [repo] rename folder fxhash-config -> config * [repo] rename @fxhash/contracts -> @fxhash/tez * [repo] rename @fxhash/eth-sdk -> @fxhash/eth * [repo] rename @fxhash/contracts-shared -> @fxhash/shared * [repo] rename folder fxhash-params -> params * [repo] rename folder fxhash-react -> react * [repo] rename folder fxhash-snippet -> snippet * [repo] run pnpm format * [repo] rename @fxhash/fxhash-snippet -> @fxhash/snippet * [repo] remove BlockchainType from @fxhash/tez, use @fxhash/shared instead * [repo] move shared types to @fxhash/shared; remove unsued code in @fxhash/tez * [packages/eth] remove unused code * [repo] derive blockchain from BlockchainType * [core/website] isEthOrL2 typeguard * [repo] feat: generative token card image (#1029) * Feat/fxhash package docs (#1013) * [repo] add docs to @fxhash/package * [fxhash-package/doc] set up docusaurus typedocs * [repo] use docusaurus typescript * [repo] use next version of docusaurus-markdown + typedocs plugins * [fxhash-package/doc] make sure md is detected and not all is interpreted as mdx * [repo] update lockfile * [repo] add build-docs task to turbo * [fxhash-package/doc] remove generated typedocs * [fxhash-package/doc] ignore typedos genenrated * [fxhash-package/doc] fix sidebar generation * [fxhash-package/doc] add readmes; mergeReadme true * [fxhash-package/doc] alphabetically sort package names * [fxhash-package/doc] set warn for broken links * [repo] index all folders in fxhash-package * [repo] rename build-docs -> build * [fxhash-package/doc] rename docs -> @fxhash/package-docs * [fxhash-package/doc] remove engines from package.json * [fxhash-package/doc] remove docusaurus images, add fxhash images * [packages/gql] remove relative links from readme * [fxhash-package/doc] throw on broken links * [fxhash-package/doc] pnpm format * [repo] add .docusaurus and generated docs to prettierignore * [fxhash-package/doc] update manifest * [packages/auth] add install to readme * [packages/react] add install to readme * [packages/shared] add install to readme * [packages/tez] add install to readme * [packages/utils] add jsdocs to hash utils * update dashboard prisma * fix dashboard build * new sort syntax after typegraphql prisma upgrade * Fix/dashboard migrations (#1038) * move orphaned migrations to db-ctrl * add api keys migration * pass correct chain for rebate * base minting flow changes (#1039) * base minting flow changes * remove log * prepend NEXT_PUBLIC to base env var --------- Co-authored-by: Markus Maerzhase Co-authored-by: LΓ©o Pradel --- examples/test-project/package.json | 2 +- examples/test-project/src/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/test-project/package.json b/examples/test-project/package.json index 3af0d22..d8c0f39 100644 --- a/examples/test-project/package.json +++ b/examples/test-project/package.json @@ -15,7 +15,7 @@ }, "dependencies": { "@fxhash/config": "workspace:*", - "@fxhash/eth-sdk": "workspace:*", + "@fxhash/eth": "workspace:*", "@taquito/signer": "17.3.0", "@taquito/taquito": "17.3.0", "@types/node-dir": "0.0.34", diff --git a/examples/test-project/src/index.ts b/examples/test-project/src/index.ts index 87ba373..f079227 100644 --- a/examples/test-project/src/index.ts +++ b/examples/test-project/src/index.ts @@ -9,7 +9,7 @@ import { bytesToHex, createPublicClient, createWalletClient, http } from "viem" import { privateKeyToAccount } from "viem/accounts" import { goerli } from "viem/chains" import { config } from "@fxhash/config" -import { ONCHFS_FILE_SYSTEM_ABI, ONCHFS_CONTENT_STORE } from "@fxhash/eth-sdk" +import { ONCHFS_FILE_SYSTEM_ABI, ONCHFS_CONTENT_STORE } from "@fxhash/eth" async function sleep(time: number) { return new Promise(resolve => setTimeout(resolve, time)) From f6263cfd8293b4a188b785e2027af3d2922eaf3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Pradel?= Date: Mon, 18 Mar 2024 11:41:41 +0100 Subject: [PATCH 39/61] [repo] fix: update prettier to 3.2.5 to produce valid JSON (#1115) --- doc/tsconfig.json | 4 ++-- examples/http-proxy/tsconfig.json | 4 ++-- examples/test-project-next/tsconfig.json | 6 +++--- examples/test-project/tsconfig.json | 4 ++-- packages/onchfs-js/tsconfig.json | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/doc/tsconfig.json b/doc/tsconfig.json index b651133..6f47569 100644 --- a/doc/tsconfig.json +++ b/doc/tsconfig.json @@ -2,6 +2,6 @@ // This file is not used in compilation. It is here just for a nice editor experience. "extends": "@tsconfig/docusaurus/tsconfig.json", "compilerOptions": { - "baseUrl": ".", - }, + "baseUrl": "." + } } diff --git a/examples/http-proxy/tsconfig.json b/examples/http-proxy/tsconfig.json index 2c19be9..670b1a1 100644 --- a/examples/http-proxy/tsconfig.json +++ b/examples/http-proxy/tsconfig.json @@ -18,8 +18,8 @@ "noUnusedParameters": true, "noImplicitAny": false, "noImplicitThis": false, - "strictNullChecks": false, + "strictNullChecks": false }, "include": ["src/**/*", "__tests__/**/*"], - "exclude": ["src/static/**"], + "exclude": ["src/static/**"] } diff --git a/examples/test-project-next/tsconfig.json b/examples/test-project-next/tsconfig.json index 43821a0..efe1b08 100644 --- a/examples/test-project-next/tsconfig.json +++ b/examples/test-project-next/tsconfig.json @@ -14,9 +14,9 @@ "jsx": "preserve", "incremental": true, "paths": { - "@/*": ["./src/*"], - }, + "@/*": ["./src/*"] + } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], - "exclude": ["node_modules"], + "exclude": ["node_modules"] } diff --git a/examples/test-project/tsconfig.json b/examples/test-project/tsconfig.json index 48ee5ad..59a31b6 100644 --- a/examples/test-project/tsconfig.json +++ b/examples/test-project/tsconfig.json @@ -9,8 +9,8 @@ "emitDecoratorMetadata": true, "esModuleInterop": true, "experimentalDecorators": true, - "sourceMap": true, + "sourceMap": true }, "include": ["src/**/*.ts"], - "exclude": ["node_modules", "**/*.spec.ts"], + "exclude": ["node_modules", "**/*.spec.ts"] } diff --git a/packages/onchfs-js/tsconfig.json b/packages/onchfs-js/tsconfig.json index d58f806..b44ba8d 100644 --- a/packages/onchfs-js/tsconfig.json +++ b/packages/onchfs-js/tsconfig.json @@ -6,13 +6,13 @@ "rootDir": "src", "outDir": "dist", "paths": { - "@/*": ["./src/*"], + "@/*": ["./src/*"] }, "target": "es2022", "lib": ["es2022", "DOM"], // FIXME: resolve type errors when removing the following lines "strict": false, "noUnusedParameters": false, - "noUnusedLocals": false, - }, + "noUnusedLocals": false + } } From e0e3bde19b4b8a0f92517aa366c20219993cbc1d Mon Sep 17 00:00:00 2001 From: louis holley Date: Tue, 26 Mar 2024 10:54:51 +0000 Subject: [PATCH 40/61] dev -> staging (#1138) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * set iterations_count field on pre v3 issuer handlers * better reservoir errors * [repo] update up-lib mock (#1075) * Staging (#1067) * add chain flag for actions and tokens (#1012) * [repo] feat: create typed router website v2 (#1045) * [core/website-v2] feat: gen card user info (#1047) * Redesign/header (#1043) * [core/website-v2] fix storybooks * [core/website-v2] [core/website-v2] add radix icons + navigation-menu * [core/website-v2] [core/website-v2] add tailwindcss-animate * [core/website-v2] [core/website-v2] add ui/Button * [core/website-v2] [core/website-v2] add ui/NavigationMenu * [core/website-v2] [core/website-v2] add MainNav component * [core/website-v2] [core/website-v2] add Header component * [repo] [repo] update lockfile * [core/website-v2] [core/website-v2] add button variants according to styleguide * [core/website-v2] add antialiased to preview container * [core/website-v2] [core/website-v2] add missing key prop * [core/website-v2] [core/website-v2] add tailwind black * [core/website-v2] [core/website-v2] add button styles according to design system * [core/website-v2] [core/website-v2] update button variants in header * [core/website-v2] [core/website-v2] add fxhash logo * [core/website-v2] [core/website-v2] use client for radix navigation menu primitive * [core/website-v2] [core/website-v2] implement navigation menu styles * [core/website-v2] [core/website-v2] move to dev dependencies * [repo] [repo] update lockfile * [core/website-v2] [core/website-v2] export NavigationMenu * [core/website-v2] [core/website-v2] refactor FxhashLogo into /images folder * [core/website-v2] [core/website-v2] add tailwind-merge to deps * [core/website-v2] [core/website-v2] move header + mainnav into /layout folder * [core/website-v2] [core/website-v2] add NavigationMenu story * [core/website-v2] [core/website-v2] refactor Button by adding ButtonBase; add IconButton * [core/website-v2] [core/website-v2] fix NavigationMenu styles * [core/website-v2] [core/website-v2] add href to sub navmenulink * [core/website-v2] [core/website-v2] add :focus-visible styles to button * [core/website-v2] [core/website-v2] use grey-900 instead of black * [repo] [repo] update lockfile * [core/website-v2] [core/website-v2] fix NavigationMenu Story * [core/website-v2] [core/website-v2] fxhash logo forward props + set color to darkest grey * [core/website-v2] [core/website-v2] fix import in header stories * [core/website-v2] [core/website-v2] export button * [core/website-v2] [core/website-v2] remove non existing style * [core/website-v2] [core/website-v2] remove variant from buttonbase * [core/website-v2] [core/website-v2] remove explicit disabled prop; add disabled controller * [core/website-v2] [core/website-v2] run pnpm next lint --fix * [core/website-v2] [core/website-v2] don't reuse button styles for navigation menu * [core/website-v2] [core/website-v2] move main styles to buttonbase * [core/website-v2] [core/website-v2] animate button gradient * [core/website-v2] [core/website-v2] fix gradient button transition and style * [core/website-v2] [core/website-v2] remove from via stop for gradient button * [core/website-v2] [core/website-v2] adjust button icon gap * [core/website-v2] [core/website-v2] adjust button px * [core/website-v2] [core/website-v2] add button with icon left/right stories * [repo] [repo] update lockfile * [core/website-v2] [core/website-v2] fix icon button styles * [core/website-v2] [core/website-v2] add tailwind-clip-path; fix button gradient with clip-path-padding * [repo] [repo] update lockfile * [core/website-v2] [core/website-v2] fix duplicate index file * [core/website-v2] [core/website-v2] Revert "[core/website-v2] add tailwind-clip-path; fix button gradient with clip-path-padding" This reverts commit c539bf105881d7008dd75f2380b5ab91411c11b5. * [repo] [repo] add overflow-hidden to buttonbase * [core/website-v2] [core/website-v2] remove black from tw config * [core/website-v2] [core/website-v2] use next/link * [core/website-v2] remove radix-ui icons * [core/website-v2] [core/website-v2] use grey-900 instead of black * [core/website-v2] [core/website-v2] update nav menu story * [core/website-v2] [core/website-v2] set cursor-pointer for nav items * [core/website-v2] [core/website-v2] remove unused nav menu indicator component * [core/website-v2] replace button color transparent with variant ghost * [core/website-v2] simplify buttonbase outline styles * [core/website-v2] add dark: style for icon button * [core/website-v2] add theme toggle * [core/website-v2] add header to layout * [core/website-v2] dont provide nav items as props * [core/website-v2] fix syntax error * [core/website-v2] use resolvedTheme instead of theme * [core/website-v2] optimize fxhash logo svg path * [core/website-v2] feat: gen tok card mint info (#1054) * generative token revealer should be chain-aware * [core/website-v2] feat: website v2 roadmap component (#1048) * [repo] fix: add user hasura persmission to select chain field (#1056) * [repo] fix: add user hasura persmission to select chain field * [repo] apply to other roles too * [repo] prettier * fix wallet connection * [core/website-v2] feat: add Docs section to homepage (#1051) * [repo] feat: GenerativeCard price info (#896) * format email/address in consumptions csv * [core/website-v2] feat: set app container layout (#1064) * fix address formatting * fix address formatting * [core/website-v2] feat: events section (#1065) * [repo] feat: sales bot add tezos support (#1063) * enable cc for base * [core/website] fi (#1071) x filter menu not disappearing * Redesign/header mobile (#1066) * [core/website-v2] add background design token * [core/website-v2] add data-orientation shortcuts * [core/website-v2] add styles for orientation vertical * [core/website-v2] remove test buttons * [core/website-v2] add Drawer + Collapsible * [core/website-v2] add MobileNav * [repo] update lockfile * [core/website-v2] fix * [core/website-v2] add separator * [core/website-v2] finalize mobile nav styles * [core/website-v2] prevent body scroll when drawer open * [core/website-v2] fix build * [core/website-v2] add zustand; add layout store * [core/website-v2] remove default padding from collabsible trigger * [core/website-v2] add padding to collabsile trigger in mobile nav * [core/website-v2] use layout store to open/close mobile nav * [core/website-v2] add animation to collapsible content * [repo] add lockfile * [core/website-v2] remove use client * [core/website-v2] make header sticky * [core/website-v2] remove rounded corners from drawer * [core/website-v2] prevent mobile nav closing when loosing focus * [core/website-v2] add separator variants; add inset variant * [core/website-v2] add inset to separator in mobile nav * [core/website-v2] add outline opacity button * [core/website-v2] fix button outline opacity active style * [core/website-v2] add icon button styles * [core/website-v2] use solid icon button for mobile nav toggle * [core/website-v2] load theme toggle with no ssr * [core/website-v2] add black tailwind var * [core/website-v2] remove color grey from iconbutton * [core/website-v2] decrease header padding * [core/website-v2] adjust NavigationMenu paddings * [core/website-v2] add margin-right to logo * [core/website-v2] crease button gap in header * [core/website-v2] increase button gap in mobile nav * [core/website-v2] responsve logo size * [core/website-v2] decrease icon button size to 20px * [core/website-v2] fix mobile nav submenu padding --------- Co-authored-by: LΓ©o Pradel * Feat/base cc (#1072) * [core/website] add analytics goals for home page visit hero or trendi… (#1073) * [core/website] add analytics goals for home page visit hero or trending, project page change sorting and marketplace change time frame * [core/website] set posthog distinct id for user identification * [core/website] move filter menu top (#1080) --------- Co-authored-by: Florian Co-authored-by: LΓ©o Pradel Co-authored-by: Markus Maerzhase * [core/website-v2] feat: footer layout (#1078) * Redesign/dark mode styles (#1083) * Redesign/fix mobile nav dark mode (#1085) * [core/website-v2] add inset 1-4 to separator * [core/website-v2] adjust separator inset on mobile nav * [core/website-v2] fix drawer darkmode * live minting fixes * [core/sales-bot] feat: send all sales > 0.01 ETH from base to fxchat (#1086) * add debug address for mint pass bypass * remove wert debugging * [core/website-v2] fix opacity button styles (#1092) * fix eth reserve checks * [packages/config] update base contracts (#1093) * fix eth reserves when not on primary list * [repo] feat: hasura query random object (#1082) * fix params minting for secondary tez address * Redesign/events carousel section (#1096) * [core/website-v2] add ui/Carousel * [core/website-v2] add ui/Tag * [core/website-v2] add events carousel section * [repo] update lockfile * [core/website-v2] fix build * [core/website-v2] fix Tag styles * [core/website-v2] use dashed tag for events carousel * [core/website-v2] export tag + carousel * [core/website-v2] feat: hero section curated (#1079) * [packages/config] update base testnet eth fee receiver (#1099) * [core/website-v2] feat: latest design changes (#1098) * [core/website] add secondary analytis goals (#1100) * [core/website] move legal from doc/legal to /legal and fix links (#1103) * [repo] feat: db-ctrl indexer toolkit (#835) * [repo] feat: hasura authentication flow (#1105) * remove ipfs reference when looking for content * allow failure in withdraw all eth op * filter withdrawable proceeds * add debug to prod for error injecting after minting * [core/reservoir-sync-node] fix: prettier config not working (#1114) * [repo] fix: update prettier to 3.2.5 to produce valid JSON (#1115) * [repo] feat: auto apply hasura metadata during local dev (#811) * [core/website-v2] feat: fix design system Tag style (#1110) * fix tezos sign in payload * card proxy contract + replace winter with wert (#1087) * wip card proxy listing_accept * working wip * refactor * more wip * remove winter related code * use storage for contract addresses * separate tests for clarity * format * split out marketplace_contract_address + format * add all the comments * initialize addresses on contract originate * hook up card proxy * remove todo * [packages/tez] add tsconfig resolveJsonModule * handle minting with input bytes * improve comments * remove admin/setters from card proxy * refactor for clarity * remove params checkout (url too large for wert) * deploy mainnet card proxy --------- Co-authored-by: maerzhase * [core/website-v2] fix button gradient outline darkmode (#1117) * [core/website-v2] fix button gradient outline darkmode * [core/website-v2] fix event carousel layout * rename & rearrange home sections * swap sales/listings in marketplace * [repo] remove plausible * add variable sort for trending projects * [core/website] comment posthog.capture beside * [core/website] add newsletter signup * [repo] Update lockfile * [core/website] remove console.log * [core/website] add validation * Fix/mutez rounding issues (#1121) * get chain from article where possible * add convertTezToMutez util to avoid listing/offer rounding errors * improve comment * manual assign (#1120) * add scaffold for gentk-metadata-manual route * add manual-assign routes to indexers * add indexer apis to config * add indexer service to file-api to call various chain indexers * refactor gentk-metadata routes with helpers * add type to prepareMetadata input * add ADMIN_SECRET to file api * wip objkt capture * wip capture proxy * hook up file-api route proxy * fix auth check ObjktCapture * update manualAssign logic * commit capture proxy project * add alert after successful capture * add already assigned check * review fixes * manual capture fixes * add debugging to file-api * remove debug logs * fix html canvas width/height in capture proxy * update capture proxy uri * add debug logs to indexer * [packages/config] add turnstile site keys to config * [core/website] add challenges.cloudflare.com to allowed domains * [core/website] add cloudflare turnstile captcha to newsletter signup * [repo] update lockfile * manual assignment fixes * reuse hasura admin secret for guarded file api route * [core/website] manually trigger captcha when input focused * fix validation for tez/eth metadata keys * [core/website-v2] feat: changes after feedback (#1123) * handle errors properly in manual-assign route * update eth manual-assign route * parse stringified features * add bgColor config to capture proxy * final test capture proxy uri * [core/capture-proxy] fix: prettify files * add debug to file-api * add html2canvas error handling to capture proxy * add confirmation step to objkt capture * update capture proxy uri * simplify waitPreview logic * update capture proxy uri * remove # from canvas selector * update capture proxy * add metadata-file route * amend gentk-metadata-manual route to be used without file upload * rm unused axios * refactor manual capture to two step upload + assign * properly handle preview aspect ratios * use querySelector for capture proxy * update capture proxy uri * add pointerEvents: none to capture iframe * add back already assigned check * [core/website] store optional account id with newsletter signup * [core/website-v2] update fxhash logo svg * [repo] feat: Hasura API limits (#1124) * add preview config to artwork url in objkt capture * unstyled iframe in capture proxy * artwork url should be hash param * update capture proxy uri * add isPreview to gentkLiveUrl util * Feature/website v2 posthog (#1104) * [core/website-v2] add posthog for analytics * [repo] update lockfile * [core/website-v2] set up manual pageview capture with posthog * [core/website-v2] setup posthog reverse proxy * [packages/config] add local website config * [core/website-v2] add .env type validation * [repo] update lockfile * [core/website-v2] add dummy .env * [core/website-v2] use suspense instead of dynamic import * [core/website-v2] add comment on .env file being dummy file * [core/website-v2] rely on fxhash config.envName rather than NODE_ENV * [packages/config] add type for fxhash config envName * [core/website-v2] define BASE_URL in next.config; remove explicit env validation env will be validated upon build within the components using the env * [repo] update lockfile * [core/website-v2] add NODE_ENV enum validation to .env validation * add onchfs config for capture proxy * allow messages from ipfs + onchfs gateways * update capture proxy to use dom-to-image-more * fallback to not copying styles for capture proxy * pass bgcolor param to capture proxy * Feat/website v2 homepage events queries (#1107) * [repo] add missing parcel/watcher dep * [packages/config] dont set config hasuraGql explicitly to localhost because needs to be docker internal in docker * [dashboard/db-ctrl] add migration to lift feature single row constraint * [packages/snippet] fix fxhash/snippet package.json * [dashboard/admin-panel] add UI to feature two events on admin panel * [repo] track event + featured event table in hasura * [repo] add NEXT_PUBLIC_API_ROOT to docker compose * [core/website-v2] use NEXT_PUBLIC_API_ROOT for codege and apollo client * [core/website-v2] WIP fetch featured events * [repo] update permission on event * [core/website-v2] add query for featured events and events carousel * [core/website-v2] remove fragments from queries * [repo] add missing env vars for dashboard/backend * [core/website-v2] add helper to retrieve media url from s3 assets * [core/website-v2] display event images; fetch only events gte now() * [repo] anon + user only fetch published events permission * [core/website-v2] only set placeholder when mediaPlaceholder * [core/website-v2] fix type error * [dashboard/admin-panel] display thumbnail in ferature event input * [core/website-v2] only render event section when two events are featured * [repo] run pnpm format * [repo] Revert "[repo] run pnpm format" This reverts commit 8fbfc948963b26da576db31030934276fce46310. * [repo] pnpm format * [packages/config] add localDocker configuration * [repo] get api endpoint from config instead of env * [packages/config] require fs only before using * [packages/config] fix TEnvName * [packages/config] update readme * [packages/config] fix TEnv type * [packages/config] fix isLocalDocker helper * update onchfs dev capture proxy * [repo] remove root dependencies; use @fxhash/tsconfig for tsconfig (#1133) * [repo] remove root dependencies; use @fxhash/tsconfig for tsconfig * [repo] add missing @types/node dev dep * [repo] keep old tsconfig to be sure * [repo] fix: fix hasura cli broken version (#1135) * add prod capture proxy uris * [core/sales-bot] feat: change ETH min to 0.025 (#1134) * fix eth manual assign call from file api * fix dev web build --------- Co-authored-by: Markus Maerzhase Co-authored-by: Florian Co-authored-by: LΓ©o Pradel Co-authored-by: maerzhase --- doc/tsconfig.json | 4 ++-- examples/http-proxy/tsconfig.json | 4 ++-- examples/test-project-next/tsconfig.json | 6 +++--- examples/test-project/tsconfig.json | 4 ++-- packages/onchfs-js/tsconfig.json | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/doc/tsconfig.json b/doc/tsconfig.json index b651133..6f47569 100644 --- a/doc/tsconfig.json +++ b/doc/tsconfig.json @@ -2,6 +2,6 @@ // This file is not used in compilation. It is here just for a nice editor experience. "extends": "@tsconfig/docusaurus/tsconfig.json", "compilerOptions": { - "baseUrl": ".", - }, + "baseUrl": "." + } } diff --git a/examples/http-proxy/tsconfig.json b/examples/http-proxy/tsconfig.json index 2c19be9..670b1a1 100644 --- a/examples/http-proxy/tsconfig.json +++ b/examples/http-proxy/tsconfig.json @@ -18,8 +18,8 @@ "noUnusedParameters": true, "noImplicitAny": false, "noImplicitThis": false, - "strictNullChecks": false, + "strictNullChecks": false }, "include": ["src/**/*", "__tests__/**/*"], - "exclude": ["src/static/**"], + "exclude": ["src/static/**"] } diff --git a/examples/test-project-next/tsconfig.json b/examples/test-project-next/tsconfig.json index 43821a0..efe1b08 100644 --- a/examples/test-project-next/tsconfig.json +++ b/examples/test-project-next/tsconfig.json @@ -14,9 +14,9 @@ "jsx": "preserve", "incremental": true, "paths": { - "@/*": ["./src/*"], - }, + "@/*": ["./src/*"] + } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], - "exclude": ["node_modules"], + "exclude": ["node_modules"] } diff --git a/examples/test-project/tsconfig.json b/examples/test-project/tsconfig.json index 48ee5ad..59a31b6 100644 --- a/examples/test-project/tsconfig.json +++ b/examples/test-project/tsconfig.json @@ -9,8 +9,8 @@ "emitDecoratorMetadata": true, "esModuleInterop": true, "experimentalDecorators": true, - "sourceMap": true, + "sourceMap": true }, "include": ["src/**/*.ts"], - "exclude": ["node_modules", "**/*.spec.ts"], + "exclude": ["node_modules", "**/*.spec.ts"] } diff --git a/packages/onchfs-js/tsconfig.json b/packages/onchfs-js/tsconfig.json index d58f806..b44ba8d 100644 --- a/packages/onchfs-js/tsconfig.json +++ b/packages/onchfs-js/tsconfig.json @@ -6,13 +6,13 @@ "rootDir": "src", "outDir": "dist", "paths": { - "@/*": ["./src/*"], + "@/*": ["./src/*"] }, "target": "es2022", "lib": ["es2022", "DOM"], // FIXME: resolve type errors when removing the following lines "strict": false, "noUnusedParameters": false, - "noUnusedLocals": false, - }, + "noUnusedLocals": false + } } From 494bb9ee383ec743ae695cd88d73ee2f45708df4 Mon Sep 17 00:00:00 2001 From: louis holley Date: Tue, 26 Mar 2024 11:01:35 +0000 Subject: [PATCH 41/61] staging -> main (#1139) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * set iterations_count field on pre v3 issuer handlers * add chain flag for actions and tokens (#1012) * [repo] feat: create typed router website v2 (#1045) * [core/website-v2] feat: gen card user info (#1047) * Redesign/header (#1043) * [core/website-v2] fix storybooks * [core/website-v2] [core/website-v2] add radix icons + navigation-menu * [core/website-v2] [core/website-v2] add tailwindcss-animate * [core/website-v2] [core/website-v2] add ui/Button * [core/website-v2] [core/website-v2] add ui/NavigationMenu * [core/website-v2] [core/website-v2] add MainNav component * [core/website-v2] [core/website-v2] add Header component * [repo] [repo] update lockfile * [core/website-v2] [core/website-v2] add button variants according to styleguide * [core/website-v2] add antialiased to preview container * [core/website-v2] [core/website-v2] add missing key prop * [core/website-v2] [core/website-v2] add tailwind black * [core/website-v2] [core/website-v2] add button styles according to design system * [core/website-v2] [core/website-v2] update button variants in header * [core/website-v2] [core/website-v2] add fxhash logo * [core/website-v2] [core/website-v2] use client for radix navigation menu primitive * [core/website-v2] [core/website-v2] implement navigation menu styles * [core/website-v2] [core/website-v2] move to dev dependencies * [repo] [repo] update lockfile * [core/website-v2] [core/website-v2] export NavigationMenu * [core/website-v2] [core/website-v2] refactor FxhashLogo into /images folder * [core/website-v2] [core/website-v2] add tailwind-merge to deps * [core/website-v2] [core/website-v2] move header + mainnav into /layout folder * [core/website-v2] [core/website-v2] add NavigationMenu story * [core/website-v2] [core/website-v2] refactor Button by adding ButtonBase; add IconButton * [core/website-v2] [core/website-v2] fix NavigationMenu styles * [core/website-v2] [core/website-v2] add href to sub navmenulink * [core/website-v2] [core/website-v2] add :focus-visible styles to button * [core/website-v2] [core/website-v2] use grey-900 instead of black * [repo] [repo] update lockfile * [core/website-v2] [core/website-v2] fix NavigationMenu Story * [core/website-v2] [core/website-v2] fxhash logo forward props + set color to darkest grey * [core/website-v2] [core/website-v2] fix import in header stories * [core/website-v2] [core/website-v2] export button * [core/website-v2] [core/website-v2] remove non existing style * [core/website-v2] [core/website-v2] remove variant from buttonbase * [core/website-v2] [core/website-v2] remove explicit disabled prop; add disabled controller * [core/website-v2] [core/website-v2] run pnpm next lint --fix * [core/website-v2] [core/website-v2] don't reuse button styles for navigation menu * [core/website-v2] [core/website-v2] move main styles to buttonbase * [core/website-v2] [core/website-v2] animate button gradient * [core/website-v2] [core/website-v2] fix gradient button transition and style * [core/website-v2] [core/website-v2] remove from via stop for gradient button * [core/website-v2] [core/website-v2] adjust button icon gap * [core/website-v2] [core/website-v2] adjust button px * [core/website-v2] [core/website-v2] add button with icon left/right stories * [repo] [repo] update lockfile * [core/website-v2] [core/website-v2] fix icon button styles * [core/website-v2] [core/website-v2] add tailwind-clip-path; fix button gradient with clip-path-padding * [repo] [repo] update lockfile * [core/website-v2] [core/website-v2] fix duplicate index file * [core/website-v2] [core/website-v2] Revert "[core/website-v2] add tailwind-clip-path; fix button gradient with clip-path-padding" This reverts commit c539bf105881d7008dd75f2380b5ab91411c11b5. * [repo] [repo] add overflow-hidden to buttonbase * [core/website-v2] [core/website-v2] remove black from tw config * [core/website-v2] [core/website-v2] use next/link * [core/website-v2] remove radix-ui icons * [core/website-v2] [core/website-v2] use grey-900 instead of black * [core/website-v2] [core/website-v2] update nav menu story * [core/website-v2] [core/website-v2] set cursor-pointer for nav items * [core/website-v2] [core/website-v2] remove unused nav menu indicator component * [core/website-v2] replace button color transparent with variant ghost * [core/website-v2] simplify buttonbase outline styles * [core/website-v2] add dark: style for icon button * [core/website-v2] add theme toggle * [core/website-v2] add header to layout * [core/website-v2] dont provide nav items as props * [core/website-v2] fix syntax error * [core/website-v2] use resolvedTheme instead of theme * [core/website-v2] optimize fxhash logo svg path * [core/website-v2] feat: gen tok card mint info (#1054) * generative token revealer should be chain-aware * [core/website-v2] feat: website v2 roadmap component (#1048) * [repo] fix: add user hasura persmission to select chain field (#1056) * [repo] fix: add user hasura persmission to select chain field * [repo] apply to other roles too * [repo] prettier * fix wallet connection * [core/website-v2] feat: add Docs section to homepage (#1051) * [repo] feat: GenerativeCard price info (#896) * format email/address in consumptions csv * [core/website-v2] feat: set app container layout (#1064) * fix address formatting * fix address formatting * [core/website-v2] feat: events section (#1065) * [repo] feat: sales bot add tezos support (#1063) * enable cc for base * [core/website] fi (#1071) x filter menu not disappearing * Redesign/header mobile (#1066) * [core/website-v2] add background design token * [core/website-v2] add data-orientation shortcuts * [core/website-v2] add styles for orientation vertical * [core/website-v2] remove test buttons * [core/website-v2] add Drawer + Collapsible * [core/website-v2] add MobileNav * [repo] update lockfile * [core/website-v2] fix * [core/website-v2] add separator * [core/website-v2] finalize mobile nav styles * [core/website-v2] prevent body scroll when drawer open * [core/website-v2] fix build * [core/website-v2] add zustand; add layout store * [core/website-v2] remove default padding from collabsible trigger * [core/website-v2] add padding to collabsile trigger in mobile nav * [core/website-v2] use layout store to open/close mobile nav * [core/website-v2] add animation to collapsible content * [repo] add lockfile * [core/website-v2] remove use client * [core/website-v2] make header sticky * [core/website-v2] remove rounded corners from drawer * [core/website-v2] prevent mobile nav closing when loosing focus * [core/website-v2] add separator variants; add inset variant * [core/website-v2] add inset to separator in mobile nav * [core/website-v2] add outline opacity button * [core/website-v2] fix button outline opacity active style * [core/website-v2] add icon button styles * [core/website-v2] use solid icon button for mobile nav toggle * [core/website-v2] load theme toggle with no ssr * [core/website-v2] add black tailwind var * [core/website-v2] remove color grey from iconbutton * [core/website-v2] decrease header padding * [core/website-v2] adjust NavigationMenu paddings * [core/website-v2] add margin-right to logo * [core/website-v2] crease button gap in header * [core/website-v2] increase button gap in mobile nav * [core/website-v2] responsve logo size * [core/website-v2] decrease icon button size to 20px * [core/website-v2] fix mobile nav submenu padding --------- Co-authored-by: LΓ©o Pradel * Feat/base cc (#1072) * [core/website] add analytics goals for home page visit hero or trendi… (#1073) * [core/website] add analytics goals for home page visit hero or trending, project page change sorting and marketplace change time frame * [core/website] set posthog distinct id for user identification * better reservoir errors * [core/website] move filter menu top (#1080) * [repo] update up-lib mock (#1075) * [core/website-v2] feat: footer layout (#1078) * Redesign/dark mode styles (#1083) * Redesign/fix mobile nav dark mode (#1085) * [core/website-v2] add inset 1-4 to separator * [core/website-v2] adjust separator inset on mobile nav * [core/website-v2] fix drawer darkmode * live minting fixes * add debug address for mint pass bypass * remove wert debugging * [core/website-v2] fix opacity button styles (#1092) * [packages/config] update base contracts (#1093) * [repo] feat: hasura query random object (#1082) * Redesign/events carousel section (#1096) * [core/website-v2] add ui/Carousel * [core/website-v2] add ui/Tag * [core/website-v2] add events carousel section * [repo] update lockfile * [core/website-v2] fix build * [core/website-v2] fix Tag styles * [core/website-v2] use dashed tag for events carousel * [core/website-v2] export tag + carousel * [core/website-v2] feat: hero section curated (#1079) * [packages/config] update base testnet eth fee receiver (#1099) * [core/website-v2] feat: latest design changes (#1098) * [repo] feat: db-ctrl indexer toolkit (#835) * [repo] feat: hasura authentication flow (#1105) * [core/reservoir-sync-node] fix: prettier config not working (#1114) * [repo] fix: update prettier to 3.2.5 to produce valid JSON (#1115) * [repo] feat: auto apply hasura metadata during local dev (#811) * [core/website-v2] feat: fix design system Tag style (#1110) * fix tezos sign in payload * card proxy contract + replace winter with wert (#1087) * wip card proxy listing_accept * working wip * refactor * more wip * remove winter related code * use storage for contract addresses * separate tests for clarity * format * split out marketplace_contract_address + format * add all the comments * initialize addresses on contract originate * hook up card proxy * remove todo * [packages/tez] add tsconfig resolveJsonModule * handle minting with input bytes * improve comments * remove admin/setters from card proxy * refactor for clarity * remove params checkout (url too large for wert) * deploy mainnet card proxy --------- Co-authored-by: maerzhase * [core/website-v2] fix button gradient outline darkmode (#1117) * [core/website-v2] fix button gradient outline darkmode * [core/website-v2] fix event carousel layout * manual assign (#1120) * add scaffold for gentk-metadata-manual route * add manual-assign routes to indexers * add indexer apis to config * add indexer service to file-api to call various chain indexers * refactor gentk-metadata routes with helpers * add type to prepareMetadata input * add ADMIN_SECRET to file api * wip objkt capture * wip capture proxy * hook up file-api route proxy * fix auth check ObjktCapture * update manualAssign logic * commit capture proxy project * add alert after successful capture * add already assigned check * review fixes * manual capture fixes * add debugging to file-api * remove debug logs * fix html canvas width/height in capture proxy * update capture proxy uri * add debug logs to indexer * manual assignment fixes * reuse hasura admin secret for guarded file api route * fix validation for tez/eth metadata keys * [core/website-v2] feat: changes after feedback (#1123) * handle errors properly in manual-assign route * update eth manual-assign route * parse stringified features * add bgColor config to capture proxy * final test capture proxy uri * [core/capture-proxy] fix: prettify files * add debug to file-api * add html2canvas error handling to capture proxy * add confirmation step to objkt capture * update capture proxy uri * simplify waitPreview logic * update capture proxy uri * remove # from canvas selector * update capture proxy * add metadata-file route * amend gentk-metadata-manual route to be used without file upload * rm unused axios * refactor manual capture to two step upload + assign * properly handle preview aspect ratios * use querySelector for capture proxy * update capture proxy uri * add pointerEvents: none to capture iframe * add back already assigned check * [core/website-v2] update fxhash logo svg * [repo] feat: Hasura API limits (#1124) * add preview config to artwork url in objkt capture * unstyled iframe in capture proxy * artwork url should be hash param * update capture proxy uri * add isPreview to gentkLiveUrl util * Feature/website v2 posthog (#1104) * [core/website-v2] add posthog for analytics * [repo] update lockfile * [core/website-v2] set up manual pageview capture with posthog * [core/website-v2] setup posthog reverse proxy * [packages/config] add local website config * [core/website-v2] add .env type validation * [repo] update lockfile * [core/website-v2] add dummy .env * [core/website-v2] use suspense instead of dynamic import * [core/website-v2] add comment on .env file being dummy file * [core/website-v2] rely on fxhash config.envName rather than NODE_ENV * [packages/config] add type for fxhash config envName * [core/website-v2] define BASE_URL in next.config; remove explicit env validation env will be validated upon build within the components using the env * [repo] update lockfile * [core/website-v2] add NODE_ENV enum validation to .env validation * add onchfs config for capture proxy * allow messages from ipfs + onchfs gateways * update capture proxy to use dom-to-image-more * fallback to not copying styles for capture proxy * pass bgcolor param to capture proxy * Feat/website v2 homepage events queries (#1107) * [repo] add missing parcel/watcher dep * [packages/config] dont set config hasuraGql explicitly to localhost because needs to be docker internal in docker * [dashboard/db-ctrl] add migration to lift feature single row constraint * [packages/snippet] fix fxhash/snippet package.json * [dashboard/admin-panel] add UI to feature two events on admin panel * [repo] track event + featured event table in hasura * [repo] add NEXT_PUBLIC_API_ROOT to docker compose * [core/website-v2] use NEXT_PUBLIC_API_ROOT for codege and apollo client * [core/website-v2] WIP fetch featured events * [repo] update permission on event * [core/website-v2] add query for featured events and events carousel * [core/website-v2] remove fragments from queries * [repo] add missing env vars for dashboard/backend * [core/website-v2] add helper to retrieve media url from s3 assets * [core/website-v2] display event images; fetch only events gte now() * [repo] anon + user only fetch published events permission * [core/website-v2] only set placeholder when mediaPlaceholder * [core/website-v2] fix type error * [dashboard/admin-panel] display thumbnail in ferature event input * [core/website-v2] only render event section when two events are featured * [repo] run pnpm format * [repo] Revert "[repo] run pnpm format" This reverts commit 8fbfc948963b26da576db31030934276fce46310. * [repo] pnpm format * [packages/config] add localDocker configuration * [repo] get api endpoint from config instead of env * [packages/config] require fs only before using * [packages/config] fix TEnvName * [packages/config] update readme * [packages/config] fix TEnv type * [packages/config] fix isLocalDocker helper * update onchfs dev capture proxy * [repo] remove root dependencies; use @fxhash/tsconfig for tsconfig (#1133) * [repo] remove root dependencies; use @fxhash/tsconfig for tsconfig * [repo] add missing @types/node dev dep * [repo] keep old tsconfig to be sure * [repo] fix: fix hasura cli broken version (#1135) * add prod capture proxy uris * fix eth manual assign call from file api * fix dev web build * dev -> staging (#1138) * set iterations_count field on pre v3 issuer handlers * better reservoir errors * [repo] update up-lib mock (#1075) * Staging (#1067) * add chain flag for actions and tokens (#1012) * [repo] feat: create typed router website v2 (#1045) * [core/website-v2] feat: gen card user info (#1047) * Redesign/header (#1043) * [core/website-v2] fix storybooks * [core/website-v2] [core/website-v2] add radix icons + navigation-menu * [core/website-v2] [core/website-v2] add tailwindcss-animate * [core/website-v2] [core/website-v2] add ui/Button * [core/website-v2] [core/website-v2] add ui/NavigationMenu * [core/website-v2] [core/website-v2] add MainNav component * [core/website-v2] [core/website-v2] add Header component * [repo] [repo] update lockfile * [core/website-v2] [core/website-v2] add button variants according to styleguide * [core/website-v2] add antialiased to preview container * [core/website-v2] [core/website-v2] add missing key prop * [core/website-v2] [core/website-v2] add tailwind black * [core/website-v2] [core/website-v2] add button styles according to design system * [core/website-v2] [core/website-v2] update button variants in header * [core/website-v2] [core/website-v2] add fxhash logo * [core/website-v2] [core/website-v2] use client for radix navigation menu primitive * [core/website-v2] [core/website-v2] implement navigation menu styles * [core/website-v2] [core/website-v2] move to dev dependencies * [repo] [repo] update lockfile * [core/website-v2] [core/website-v2] export NavigationMenu * [core/website-v2] [core/website-v2] refactor FxhashLogo into /images folder * [core/website-v2] [core/website-v2] add tailwind-merge to deps * [core/website-v2] [core/website-v2] move header + mainnav into /layout folder * [core/website-v2] [core/website-v2] add NavigationMenu story * [core/website-v2] [core/website-v2] refactor Button by adding ButtonBase; add IconButton * [core/website-v2] [core/website-v2] fix NavigationMenu styles * [core/website-v2] [core/website-v2] add href to sub navmenulink * [core/website-v2] [core/website-v2] add :focus-visible styles to button * [core/website-v2] [core/website-v2] use grey-900 instead of black * [repo] [repo] update lockfile * [core/website-v2] [core/website-v2] fix NavigationMenu Story * [core/website-v2] [core/website-v2] fxhash logo forward props + set color to darkest grey * [core/website-v2] [core/website-v2] fix import in header stories * [core/website-v2] [core/website-v2] export button * [core/website-v2] [core/website-v2] remove non existing style * [core/website-v2] [core/website-v2] remove variant from buttonbase * [core/website-v2] [core/website-v2] remove explicit disabled prop; add disabled controller * [core/website-v2] [core/website-v2] run pnpm next lint --fix * [core/website-v2] [core/website-v2] don't reuse button styles for navigation menu * [core/website-v2] [core/website-v2] move main styles to buttonbase * [core/website-v2] [core/website-v2] animate button gradient * [core/website-v2] [core/website-v2] fix gradient button transition and style * [core/website-v2] [core/website-v2] remove from via stop for gradient button * [core/website-v2] [core/website-v2] adjust button icon gap * [core/website-v2] [core/website-v2] adjust button px * [core/website-v2] [core/website-v2] add button with icon left/right stories * [repo] [repo] update lockfile * [core/website-v2] [core/website-v2] fix icon button styles * [core/website-v2] [core/website-v2] add tailwind-clip-path; fix button gradient with clip-path-padding * [repo] [repo] update lockfile * [core/website-v2] [core/website-v2] fix duplicate index file * [core/website-v2] [core/website-v2] Revert "[core/website-v2] add tailwind-clip-path; fix button gradient with clip-path-padding" This reverts commit c539bf105881d7008dd75f2380b5ab91411c11b5. * [repo] [repo] add overflow-hidden to buttonbase * [core/website-v2] [core/website-v2] remove black from tw config * [core/website-v2] [core/website-v2] use next/link * [core/website-v2] remove radix-ui icons * [core/website-v2] [core/website-v2] use grey-900 instead of black * [core/website-v2] [core/website-v2] update nav menu story * [core/website-v2] [core/website-v2] set cursor-pointer for nav items * [core/website-v2] [core/website-v2] remove unused nav menu indicator component * [core/website-v2] replace button color transparent with variant ghost * [core/website-v2] simplify buttonbase outline styles * [core/website-v2] add dark: style for icon button * [core/website-v2] add theme toggle * [core/website-v2] add header to layout * [core/website-v2] dont provide nav items as props * [core/website-v2] fix syntax error * [core/website-v2] use resolvedTheme instead of theme * [core/website-v2] optimize fxhash logo svg path * [core/website-v2] feat: gen tok card mint info (#1054) * generative token revealer should be chain-aware * [core/website-v2] feat: website v2 roadmap component (#1048) * [repo] fix: add user hasura persmission to select chain field (#1056) * [repo] fix: add user hasura persmission to select chain field * [repo] apply to other roles too * [repo] prettier * fix wallet connection * [core/website-v2] feat: add Docs section to homepage (#1051) * [repo] feat: GenerativeCard price info (#896) * format email/address in consumptions csv * [core/website-v2] feat: set app container layout (#1064) * fix address formatting * fix address formatting * [core/website-v2] feat: events section (#1065) * [repo] feat: sales bot add tezos support (#1063) * enable cc for base * [core/website] fi (#1071) x filter menu not disappearing * Redesign/header mobile (#1066) * [core/website-v2] add background design token * [core/website-v2] add data-orientation shortcuts * [core/website-v2] add styles for orientation vertical * [core/website-v2] remove test buttons * [core/website-v2] add Drawer + Collapsible * [core/website-v2] add MobileNav * [repo] update lockfile * [core/website-v2] fix * [core/website-v2] add separator * [core/website-v2] finalize mobile nav styles * [core/website-v2] prevent body scroll when drawer open * [core/website-v2] fix build * [core/website-v2] add zustand; add layout store * [core/website-v2] remove default padding from collabsible trigger * [core/website-v2] add padding to collabsile trigger in mobile nav * [core/website-v2] use layout store to open/close mobile nav * [core/website-v2] add animation to collapsible content * [repo] add lockfile * [core/website-v2] remove use client * [core/website-v2] make header sticky * [core/website-v2] remove rounded corners from drawer * [core/website-v2] prevent mobile nav closing when loosing focus * [core/website-v2] add separator variants; add inset variant * [core/website-v2] add inset to separator in mobile nav * [core/website-v2] add outline opacity button * [core/website-v2] fix button outline opacity active style * [core/website-v2] add icon button styles * [core/website-v2] use solid icon button for mobile nav toggle * [core/website-v2] load theme toggle with no ssr * [core/website-v2] add black tailwind var * [core/website-v2] remove color grey from iconbutton * [core/website-v2] decrease header padding * [core/website-v2] adjust NavigationMenu paddings * [core/website-v2] add margin-right to logo * [core/website-v2] crease button gap in header * [core/website-v2] increase button gap in mobile nav * [core/website-v2] responsve logo size * [core/website-v2] decrease icon button size to 20px * [core/website-v2] fix mobile nav submenu padding --------- Co-authored-by: LΓ©o Pradel * Feat/base cc (#1072) * [core/website] add analytics goals for home page visit hero or trendi… (#1073) * [core/website] add analytics goals for home page visit hero or trending, project page change sorting and marketplace change time frame * [core/website] set posthog distinct id for user identification * [core/website] move filter menu top (#1080) --------- Co-authored-by: Florian Co-authored-by: LΓ©o Pradel Co-authored-by: Markus Maerzhase * [core/website-v2] feat: footer layout (#1078) * Redesign/dark mode styles (#1083) * Redesign/fix mobile nav dark mode (#1085) * [core/website-v2] add inset 1-4 to separator * [core/website-v2] adjust separator inset on mobile nav * [core/website-v2] fix drawer darkmode * live minting fixes * [core/sales-bot] feat: send all sales > 0.01 ETH from base to fxchat (#1086) * add debug address for mint pass bypass * remove wert debugging * [core/website-v2] fix opacity button styles (#1092) * fix eth reserve checks * [packages/config] update base contracts (#1093) * fix eth reserves when not on primary list * [repo] feat: hasura query random object (#1082) * fix params minting for secondary tez address * Redesign/events carousel section (#1096) * [core/website-v2] add ui/Carousel * [core/website-v2] add ui/Tag * [core/website-v2] add events carousel section * [repo] update lockfile * [core/website-v2] fix build * [core/website-v2] fix Tag styles * [core/website-v2] use dashed tag for events carousel * [core/website-v2] export tag + carousel * [core/website-v2] feat: hero section curated (#1079) * [packages/config] update base testnet eth fee receiver (#1099) * [core/website-v2] feat: latest design changes (#1098) * [core/website] add secondary analytis goals (#1100) * [core/website] move legal from doc/legal to /legal and fix links (#1103) * [repo] feat: db-ctrl indexer toolkit (#835) * [repo] feat: hasura authentication flow (#1105) * remove ipfs reference when looking for content * allow failure in withdraw all eth op * filter withdrawable proceeds * add debug to prod for error injecting after minting * [core/reservoir-sync-node] fix: prettier config not working (#1114) * [repo] fix: update prettier to 3.2.5 to produce valid JSON (#1115) * [repo] feat: auto apply hasura metadata during local dev (#811) * [core/website-v2] feat: fix design system Tag style (#1110) * fix tezos sign in payload * card proxy contract + replace winter with wert (#1087) * wip card proxy listing_accept * working wip * refactor * more wip * remove winter related code * use storage for contract addresses * separate tests for clarity * format * split out marketplace_contract_address + format * add all the comments * initialize addresses on contract originate * hook up card proxy * remove todo * [packages/tez] add tsconfig resolveJsonModule * handle minting with input bytes * improve comments * remove admin/setters from card proxy * refactor for clarity * remove params checkout (url too large for wert) * deploy mainnet card proxy --------- Co-authored-by: maerzhase * [core/website-v2] fix button gradient outline darkmode (#1117) * [core/website-v2] fix button gradient outline darkmode * [core/website-v2] fix event carousel layout * rename & rearrange home sections * swap sales/listings in marketplace * [repo] remove plausible * add variable sort for trending projects * [core/website] comment posthog.capture beside * [core/website] add newsletter signup * [repo] Update lockfile * [core/website] remove console.log * [core/website] add validation * Fix/mutez rounding issues (#1121) * get chain from article where possible * add convertTezToMutez util to avoid listing/offer rounding errors * improve comment * manual assign (#1120) * add scaffold for gentk-metadata-manual route * add manual-assign routes to indexers * add indexer apis to config * add indexer service to file-api to call various chain indexers * refactor gentk-metadata routes with helpers * add type to prepareMetadata input * add ADMIN_SECRET to file api * wip objkt capture * wip capture proxy * hook up file-api route proxy * fix auth check ObjktCapture * update manualAssign logic * commit capture proxy project * add alert after successful capture * add already assigned check * review fixes * manual capture fixes * add debugging to file-api * remove debug logs * fix html canvas width/height in capture proxy * update capture proxy uri * add debug logs to indexer * [packages/config] add turnstile site keys to config * [core/website] add challenges.cloudflare.com to allowed domains * [core/website] add cloudflare turnstile captcha to newsletter signup * [repo] update lockfile * manual assignment fixes * reuse hasura admin secret for guarded file api route * [core/website] manually trigger captcha when input focused * fix validation for tez/eth metadata keys * [core/website-v2] feat: changes after feedback (#1123) * handle errors properly in manual-assign route * update eth manual-assign route * parse stringified features * add bgColor config to capture proxy * final test capture proxy uri * [core/capture-proxy] fix: prettify files * add debug to file-api * add html2canvas error handling to capture proxy * add confirmation step to objkt capture * update capture proxy uri * simplify waitPreview logic * update capture proxy uri * remove # from canvas selector * update capture proxy * add metadata-file route * amend gentk-metadata-manual route to be used without file upload * rm unused axios * refactor manual capture to two step upload + assign * properly handle preview aspect ratios * use querySelector for capture proxy * update capture proxy uri * add pointerEvents: none to capture iframe * add back already assigned check * [core/website] store optional account id with newsletter signup * [core/website-v2] update fxhash logo svg * [repo] feat: Hasura API limits (#1124) * add preview config to artwork url in objkt capture * unstyled iframe in capture proxy * artwork url should be hash param * update capture proxy uri * add isPreview to gentkLiveUrl util * Feature/website v2 posthog (#1104) * [core/website-v2] add posthog for analytics * [repo] update lockfile * [core/website-v2] set up manual pageview capture with posthog * [core/website-v2] setup posthog reverse proxy * [packages/config] add local website config * [core/website-v2] add .env type validation * [repo] update lockfile * [core/website-v2] add dummy .env * [core/website-v2] use suspense instead of dynamic import * [core/website-v2] add comment on .env file being dummy file * [core/website-v2] rely on fxhash config.envName rather than NODE_ENV * [packages/config] add type for fxhash config envName * [core/website-v2] define BASE_URL in next.config; remove explicit env validation env will be validated upon build within the components using the env * [repo] update lockfile * [core/website-v2] add NODE_ENV enum validation to .env validation * add onchfs config for capture proxy * allow messages from ipfs + onchfs gateways * update capture proxy to use dom-to-image-more * fallback to not copying styles for capture proxy * pass bgcolor param to capture proxy * Feat/website v2 homepage events queries (#1107) * [repo] add missing parcel/watcher dep * [packages/config] dont set config hasuraGql explicitly to localhost because needs to be docker internal in docker * [dashboard/db-ctrl] add migration to lift feature single row constraint * [packages/snippet] fix fxhash/snippet package.json * [dashboard/admin-panel] add UI to feature two events on admin panel * [repo] track event + featured event table in hasura * [repo] add NEXT_PUBLIC_API_ROOT to docker compose * [core/website-v2] use NEXT_PUBLIC_API_ROOT for codege and apollo client * [core/website-v2] WIP fetch featured events * [repo] update permission on event * [core/website-v2] add query for featured events and events carousel * [core/website-v2] remove fragments from queries * [repo] add missing env vars for dashboard/backend * [core/website-v2] add helper to retrieve media url from s3 assets * [core/website-v2] display event images; fetch only events gte now() * [repo] anon + user only fetch published events permission * [core/website-v2] only set placeholder when mediaPlaceholder * [core/website-v2] fix type error * [dashboard/admin-panel] display thumbnail in ferature event input * [core/website-v2] only render event section when two events are featured * [repo] run pnpm format * [repo] Revert "[repo] run pnpm format" This reverts commit 8fbfc948963b26da576db31030934276fce46310. * [repo] pnpm format * [packages/config] add localDocker configuration * [repo] get api endpoint from config instead of env * [packages/config] require fs only before using * [packages/config] fix TEnvName * [packages/config] update readme * [packages/config] fix TEnv type * [packages/config] fix isLocalDocker helper * update onchfs dev capture proxy * [repo] remove root dependencies; use @fxhash/tsconfig for tsconfig (#1133) * [repo] remove root dependencies; use @fxhash/tsconfig for tsconfig * [repo] add missing @types/node dev dep * [repo] keep old tsconfig to be sure * [repo] fix: fix hasura cli broken version (#1135) * add prod capture proxy uris * [core/sales-bot] feat: change ETH min to 0.025 (#1134) * fix eth manual assign call from file api * fix dev web build --------- Co-authored-by: Markus Maerzhase Co-authored-by: Florian Co-authored-by: LΓ©o Pradel Co-authored-by: maerzhase --------- Co-authored-by: Florian Co-authored-by: LΓ©o Pradel Co-authored-by: Markus Maerzhase Co-authored-by: maerzhase --- doc/tsconfig.json | 4 ++-- examples/http-proxy/tsconfig.json | 4 ++-- examples/test-project-next/tsconfig.json | 6 +++--- examples/test-project/tsconfig.json | 4 ++-- packages/onchfs-js/tsconfig.json | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/doc/tsconfig.json b/doc/tsconfig.json index b651133..6f47569 100644 --- a/doc/tsconfig.json +++ b/doc/tsconfig.json @@ -2,6 +2,6 @@ // This file is not used in compilation. It is here just for a nice editor experience. "extends": "@tsconfig/docusaurus/tsconfig.json", "compilerOptions": { - "baseUrl": ".", - }, + "baseUrl": "." + } } diff --git a/examples/http-proxy/tsconfig.json b/examples/http-proxy/tsconfig.json index 2c19be9..670b1a1 100644 --- a/examples/http-proxy/tsconfig.json +++ b/examples/http-proxy/tsconfig.json @@ -18,8 +18,8 @@ "noUnusedParameters": true, "noImplicitAny": false, "noImplicitThis": false, - "strictNullChecks": false, + "strictNullChecks": false }, "include": ["src/**/*", "__tests__/**/*"], - "exclude": ["src/static/**"], + "exclude": ["src/static/**"] } diff --git a/examples/test-project-next/tsconfig.json b/examples/test-project-next/tsconfig.json index 43821a0..efe1b08 100644 --- a/examples/test-project-next/tsconfig.json +++ b/examples/test-project-next/tsconfig.json @@ -14,9 +14,9 @@ "jsx": "preserve", "incremental": true, "paths": { - "@/*": ["./src/*"], - }, + "@/*": ["./src/*"] + } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], - "exclude": ["node_modules"], + "exclude": ["node_modules"] } diff --git a/examples/test-project/tsconfig.json b/examples/test-project/tsconfig.json index 48ee5ad..59a31b6 100644 --- a/examples/test-project/tsconfig.json +++ b/examples/test-project/tsconfig.json @@ -9,8 +9,8 @@ "emitDecoratorMetadata": true, "esModuleInterop": true, "experimentalDecorators": true, - "sourceMap": true, + "sourceMap": true }, "include": ["src/**/*.ts"], - "exclude": ["node_modules", "**/*.spec.ts"], + "exclude": ["node_modules", "**/*.spec.ts"] } diff --git a/packages/onchfs-js/tsconfig.json b/packages/onchfs-js/tsconfig.json index d58f806..b44ba8d 100644 --- a/packages/onchfs-js/tsconfig.json +++ b/packages/onchfs-js/tsconfig.json @@ -6,13 +6,13 @@ "rootDir": "src", "outDir": "dist", "paths": { - "@/*": ["./src/*"], + "@/*": ["./src/*"] }, "target": "es2022", "lib": ["es2022", "DOM"], // FIXME: resolve type errors when removing the following lines "strict": false, "noUnusedParameters": false, - "noUnusedLocals": false, - }, + "noUnusedLocals": false + } } From efc7cf382b1c63a022b3735dcf670f01ea1c6b07 Mon Sep 17 00:00:00 2001 From: Florian Date: Tue, 2 Apr 2024 11:52:20 +0200 Subject: [PATCH 42/61] chore: viem wagmi upgrade (#1140) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * upgrade viem and wagmi * fix imports * add missing config * format * ignore weird type error * fix build * fix query client * fix build * [core/website-v2] Update graphql.ts * [repo] Update pnpm-lock.yaml * [packages/react] Update SignerWagmi.ts * [repo] chore: changes to viem wagmi upgrade (#1151) * [packages/eth] fix * [packages/eth] Revert "[packages/eth] fix" This reverts commit ac208dcc8ccc6744ec5f8948f6e15d83e5eba7b4. * [packages/eth] fixes * [packages/eth] more * fix safe issue * [repo] Update pnpm-lock.yaml * fix safe error * [core/eth-indexer] fix * [packages/eth] last fix * [packages/eth] Update error.ts --------- Co-authored-by: LΓ©o Pradel --- examples/test-project/package.json | 2 +- packages/onchfs-js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/test-project/package.json b/examples/test-project/package.json index d8c0f39..09039f7 100644 --- a/examples/test-project/package.json +++ b/examples/test-project/package.json @@ -23,7 +23,7 @@ "node-dir": "0.1.17", "onchfs": "workspace:*", "tslib": "2.6.0", - "viem": "1.21.4" + "viem": "2.9.2" }, "devDependencies": { "@types/node": "18.7.13", diff --git a/packages/onchfs-js/package.json b/packages/onchfs-js/package.json index e62241b..8d9e2de 100644 --- a/packages/onchfs-js/package.json +++ b/packages/onchfs-js/package.json @@ -27,7 +27,7 @@ "js-sha3": "0.9.1", "mime-types": "2.1.35", "pako": "2.1.0", - "viem": "1.21.4" + "viem": "2.9.2" }, "devDependencies": { "@fxhash/tsconfig": "workspace:*", From f65e0b0a7b03d2058262b7aa1f1d822e64b8e465 Mon Sep 17 00:00:00 2001 From: Louis Holley Date: Tue, 2 Apr 2024 14:20:30 +0100 Subject: [PATCH 43/61] fix onchfs-tests package build --- examples/test-project/package.json | 9 +++++---- examples/test-project/tsup.config.ts | 13 +++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 examples/test-project/tsup.config.ts diff --git a/examples/test-project/package.json b/examples/test-project/package.json index d8c0f39..e4c8739 100644 --- a/examples/test-project/package.json +++ b/examples/test-project/package.json @@ -8,10 +8,10 @@ ], "license": "MIT", "scripts": { - "clean": "rm -rf dist", - "build": "npm run clean && tsc --declaration", - "dev": "nodemon --watch \"src/**/*.ts\" --exec \"ts-node --transpile-only\" src/index.ts", - "test": "echo \"Error: no test specified\" && exit 1" + "prod": "tsc && node dist/index.js", + "build": "tsup", + "dev": "tsup --watch --on-success 'node dist/index.js'", + "start": "nodemon --watch \"src/**/*.ts\" --exec \"ts-node --transpile-only\" src/index.ts" }, "dependencies": { "@fxhash/config": "workspace:*", @@ -30,6 +30,7 @@ "nodemon": "2.0.13", "ts-node": "10.9.1", "tsc-alias": "1.8.5", + "tsup": "7.2.0", "typescript": "4.9.5" } } diff --git a/examples/test-project/tsup.config.ts b/examples/test-project/tsup.config.ts new file mode 100644 index 0000000..b51d83e --- /dev/null +++ b/examples/test-project/tsup.config.ts @@ -0,0 +1,13 @@ +import { defineConfig, Options } from "tsup" + +export default defineConfig((options: Options) => ({ + entry: ["src/index.ts"], + outDir: "dist", + format: ["cjs"], + splitting: true, + sourcemap: true, + clean: !options.watch, + dts: true, + bundle: true, + cjsInterop: true, +})) From fc42ffa128a0718b219cbe73041052b6702893c4 Mon Sep 17 00:00:00 2001 From: Louis Holley Date: Tue, 16 Apr 2024 15:26:20 +0100 Subject: [PATCH 44/61] try all provided tezos rpcs before failing from the onchfs proxy resolver --- packages/onchfs-js/src/resolver/proxy.ts | 90 ++++++++++++++++-------- 1 file changed, 60 insertions(+), 30 deletions(-) diff --git a/packages/onchfs-js/src/resolver/proxy.ts b/packages/onchfs-js/src/resolver/proxy.ts index b5886e9..19dfc83 100644 --- a/packages/onchfs-js/src/resolver/proxy.ts +++ b/packages/onchfs-js/src/resolver/proxy.ts @@ -51,6 +51,15 @@ const ResolutionErrors: Record = { [ProxyResolutionStatusErrors.INTERNAL_SERVER_ERROR]: "Internal Server Error", } +function shuffle(array: T[]): T[] { + const out = [...array] + for (let i = out.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)) + ;[out[i], out[j]] = [out[j], out[i]] + } + return out +} + /** * Creates a basic proxy resolver using a declarative list of blockchain * resolvers (network, RPC addresses, etc...). This function is a wrapper @@ -142,41 +151,62 @@ export function createProxyResolver(controllers: BlockchainResolverCtrl[]) { } return { getInodeAtPath: async (cid, path) => { - const kt = await KT(address) - const out = await kt.contractViews - .get_inode_at({ - cid, - path, - }) - .executeView({ - viewCaller: address, - }) + for (const rpc of shuffle(h.rpcs)) { + Tezos.setRpcProvider(rpc) + try { + const kt = await KT(address) + const out = await kt.contractViews + .get_inode_at({ + cid, + path, + }) + .executeView({ + viewCaller: address, + }) - // if the contract has answered with a directory - if (out.inode.directory) { - const files: Record = {} - for (const [name, pointer] of out.inode.directory.entries()) { - files[name] = pointer - } - return { - cid: out.cid, - files, - } - } else { - // the contract has answered with a file - return { - cid: out.cid, - chunkPointers: out.inode.file.chunk_pointers, - metadata: out.inode.file.metadata, + // if the contract has answered with a directory + if (out.inode.directory) { + const files: Record = {} + for (const [ + name, + pointer, + ] of out.inode.directory.entries()) { + files[name] = pointer + } + return { + cid: out.cid, + files, + } + } else { + // the contract has answered with a file + return { + cid: out.cid, + chunkPointers: out.inode.file.chunk_pointers, + metadata: out.inode.file.metadata, + } + } + } catch (e) { + console.error(`RPC ${rpc} failed: ${e}, trying next...`) } } + throw new Error("all RPCs failed") }, readFile: async cid => { - const kt = await KT(address) - const res = await kt.contractViews.read_file(cid).executeView({ - viewCaller: address, - }) - return hexStringToBytes(res.content) + for (const rpc of shuffle(h.rpcs)) { + Tezos.setRpcProvider(rpc) + try { + const kt = await KT(address) + const res = await kt.contractViews + .read_file(cid) + .executeView({ + viewCaller: address, + }) + return hexStringToBytes(res.content) + } catch (e) { + console.error(`RPC ${rpc} failed: ${e}, trying next...`) + } + } + throw new Error("all RPCs failed") }, } }, From 69ce13dcd8fccec53a456d89809dfda1dc405279 Mon Sep 17 00:00:00 2001 From: Louis Holley Date: Wed, 17 Apr 2024 11:32:00 +0100 Subject: [PATCH 45/61] add tezos service abstraction to simplify rpc shuffle/retry --- packages/onchfs-js/src/resolver/proxy.ts | 101 ++++++------------ .../onchfs-js/src/services/tezos.service.ts | 66 ++++++++++++ 2 files changed, 100 insertions(+), 67 deletions(-) create mode 100644 packages/onchfs-js/src/services/tezos.service.ts diff --git a/packages/onchfs-js/src/resolver/proxy.ts b/packages/onchfs-js/src/resolver/proxy.ts index 19dfc83..34c2f58 100644 --- a/packages/onchfs-js/src/resolver/proxy.ts +++ b/packages/onchfs-js/src/resolver/proxy.ts @@ -28,11 +28,6 @@ import { parseSchema, parseSchemaSpecificPart, } from "@/uri/parse" -import { - TezosToolkit, - ContractAbstraction, - ContractProvider, -} from "@taquito/taquito" import { DEFAULT_CONTRACTS } from "@/config" import { createPublicClient, @@ -43,6 +38,7 @@ import { } from "viem" import { ONCHFS_FILE_SYSTEM_ABI } from "@/utils/abi" import { EthInode, EthInodeType } from "@/types/eth" +import { TezosService } from "@/services/tezos.service" const ResolutionErrors: Record = { [ProxyResolutionStatusErrors.BAD_REQUEST]: "Bad Request", @@ -127,17 +123,7 @@ export function createProxyResolver(controllers: BlockchainResolverCtrl[]) { switch (blockchain) { case "tezos": { - const Tezos = new TezosToolkit(h.rpcs[0]) - - const KTs: Record> = {} - - async function KT(address: string) { - if (KTs[address]) { - return KTs[address] - } - KTs[address] = await Tezos.contract.at(address) - return KTs[address] - } + const tezos = new TezosService(h.rpcs) return { blockchain: h.blockchain, @@ -151,62 +137,43 @@ export function createProxyResolver(controllers: BlockchainResolverCtrl[]) { } return { getInodeAtPath: async (cid, path) => { - for (const rpc of shuffle(h.rpcs)) { - Tezos.setRpcProvider(rpc) - try { - const kt = await KT(address) - const out = await kt.contractViews - .get_inode_at({ - cid, - path, - }) - .executeView({ - viewCaller: address, - }) + const out = await tezos.call(address, kt => + kt.contractViews + .get_inode_at({ + cid, + path, + }) + .executeView({ + viewCaller: address, + }) + ) - // if the contract has answered with a directory - if (out.inode.directory) { - const files: Record = {} - for (const [ - name, - pointer, - ] of out.inode.directory.entries()) { - files[name] = pointer - } - return { - cid: out.cid, - files, - } - } else { - // the contract has answered with a file - return { - cid: out.cid, - chunkPointers: out.inode.file.chunk_pointers, - metadata: out.inode.file.metadata, - } - } - } catch (e) { - console.error(`RPC ${rpc} failed: ${e}, trying next...`) + // if the contract has answered with a directory + if (out.inode.directory) { + const files: Record = {} + for (const [name, pointer] of out.inode.directory.entries()) { + files[name] = pointer + } + return { + cid: out.cid, + files, + } + } else { + // the contract has answered with a file + return { + cid: out.cid, + chunkPointers: out.inode.file.chunk_pointers, + metadata: out.inode.file.metadata, } } - throw new Error("all RPCs failed") }, readFile: async cid => { - for (const rpc of shuffle(h.rpcs)) { - Tezos.setRpcProvider(rpc) - try { - const kt = await KT(address) - const res = await kt.contractViews - .read_file(cid) - .executeView({ - viewCaller: address, - }) - return hexStringToBytes(res.content) - } catch (e) { - console.error(`RPC ${rpc} failed: ${e}, trying next...`) - } - } - throw new Error("all RPCs failed") + const res = await tezos.call(address, kt => + kt.contractViews.read_file(cid).executeView({ + viewCaller: address, + }) + ) + return hexStringToBytes(res.content) }, } }, diff --git a/packages/onchfs-js/src/services/tezos.service.ts b/packages/onchfs-js/src/services/tezos.service.ts new file mode 100644 index 0000000..f132ed0 --- /dev/null +++ b/packages/onchfs-js/src/services/tezos.service.ts @@ -0,0 +1,66 @@ +import { + ContractAbstraction, + ContractProvider, + TezosToolkit, +} from "@taquito/taquito" + +function shuffle(array: T[]): T[] { + const out = [...array] + for (let i = out.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)) + ;[out[i], out[j]] = [out[j], out[i]] + } + return out +} + +export class TezosService { + private tezosToolkit: TezosToolkit + private rpcNodes: string[] + private contractsCache: Record< + string, + ContractAbstraction + > = {} + + constructor(rpcs: string[]) { + this.tezosToolkit = new TezosToolkit(rpcs[0]) + this.rpcNodes = rpcs + } + + async call( + address: string, + callback: (contract: ContractAbstraction) => Promise + ): Promise { + for (const rpc of shuffle(this.rpcNodes)) { + this.tezosToolkit.setProvider({ rpc }) + try { + const contract = await this.getContract(address) + return await callback(contract) + } catch (err) { + if (!this.canErrorBeCycled(err)) throw err + console.error(`RPC ${rpc} failed: ${err}, trying next...`) + } + } + throw new Error("all RPCs failed") + } + + async getContract( + address: string + ): Promise> { + if (!this.contractsCache[address]) { + this.contractsCache[address] = + await this.tezosToolkit.contract.at(address) + } + return this.contractsCache[address] + } + + // given an error, returns true if request can be cycled to another RPC node + private canErrorBeCycled(err: any): boolean { + return ( + err && + (err.name === "HttpRequestFailed" || + err.status === 500 || + err.status === 408 || + err.status === 429) + ) + } +} From c0ed28d4ec672a902de0424dc58142cf516ce987 Mon Sep 17 00:00:00 2001 From: Louis Holley Date: Tue, 23 Apr 2024 11:09:21 +0100 Subject: [PATCH 46/61] import js-sha3 for compatibility with esm consumers --- packages/onchfs-js/src/utils/keccak.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/onchfs-js/src/utils/keccak.ts b/packages/onchfs-js/src/utils/keccak.ts index 1547f66..368ea4c 100644 --- a/packages/onchfs-js/src/utils/keccak.ts +++ b/packages/onchfs-js/src/utils/keccak.ts @@ -1,4 +1,5 @@ -import { keccak256 } from "js-sha3" +import sha3 from "js-sha3" +const { keccak256 } = sha3 /** * Hashes some bytes with keccak256. Simple typed wrapper to ease implementation From 9693d4b92e8ccd5fa3a210ead61c5bc5624a481b Mon Sep 17 00:00:00 2001 From: Louis Holley Date: Tue, 23 Apr 2024 12:20:30 +0100 Subject: [PATCH 47/61] remove duplicate shuffle definition --- packages/onchfs-js/src/resolver/proxy.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/packages/onchfs-js/src/resolver/proxy.ts b/packages/onchfs-js/src/resolver/proxy.ts index 34c2f58..5696239 100644 --- a/packages/onchfs-js/src/resolver/proxy.ts +++ b/packages/onchfs-js/src/resolver/proxy.ts @@ -47,15 +47,6 @@ const ResolutionErrors: Record = { [ProxyResolutionStatusErrors.INTERNAL_SERVER_ERROR]: "Internal Server Error", } -function shuffle(array: T[]): T[] { - const out = [...array] - for (let i = out.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)) - ;[out[i], out[j]] = [out[j], out[i]] - } - return out -} - /** * Creates a basic proxy resolver using a declarative list of blockchain * resolvers (network, RPC addresses, etc...). This function is a wrapper From 00b1880802a4c5e09e1636da88b3162fbe15ed38 Mon Sep 17 00:00:00 2001 From: Markus Maerzhase Date: Wed, 24 Apr 2024 11:15:00 +0200 Subject: [PATCH 48/61] dev -> staging (#1240) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add back subsquid/typeorm-store patch * add experimental decorators to tsconfig * remove ts nochecks * validate eth-indexer env vars * add check for RESERVOIR_API_KEY env * format: esm for @fxhash/eth * refactor frame flag to set it in token (#1180) * refactor frame flag to set it in token * add isFrame to api * fix build * update hasura metadata * migrating eth-sdk to esm * fix rarity worker for broken features * add default value for isFrame * upgrade multiformats for type safety * migrate eth sdk to esm * add safe types to collaboration manager * fix safe default imports * fix safe types * warn instead of throwing when env missing * add port to env * fix esm build * set initialized: true when walletClient status pending * add status to effect deps * use @fxhash/gql-client in eth sdk * remove @apollo/client dep * remove non-secret env from marketstats-v2 * migrate marketstats-v2 to esm * use marketstats boolean config * add typesafe env to marketstats-v2 * [repo] feat: create ToggleGroup component (#1174) * [repo] feat: create SegmentedControl component (#1175) * review fixes * remove accidental .js in strings * start migrating reservoir-indexer to esm * add localOrElse helper * utilise localOrElse * add getIndexerConfigForChain helper * migrate reservoir-indexer to esm * validate reservoir-indexer env * remove non-secret env from file-api * overwrite internal apis in local env * Feat/add active prop to button and icon button (#1181) * [core/website-v2] add shorthand for data-active & data-open * [core/website-v2] add data-active states to button * [core/website-v2] add active prop to button and icon button * [core/website-v2] feat: convert to esm (#1176) * migrate file-api to esm * validate file-api env * more file-api typed env * start refacotring frame * Update contract address in sendMintTransaction function * add aws bucket and otel config to global config * migrate extract-balancer to esm * typesafe env for extract balancer * [core/website-v2] feat: live section filter logic (#1125) * [core/website-v2] feat: live section filter logic * [core/website-v2] cleanup * [core/website-v2] use new component * Feature/website v2 newsletter signup (#1129) * [core/website-v2] add ui input component to design system * [core/website-v2] add newlsettersignupsection * [core/website-v2] add loops api * [repo] update lockfile * [core/website-v2] add startIcon + endIcon to Input component * [core/website-v2] add mini button size :) * [core/website-v2] add validation, captcha and feedback to newsletter signup * [core/website-v2] add TODOs * [repo] update lock file * [core/website-v2] use icon spinner * [core/website-v2] design feedback * [packages/config] add cloudflare turnstile sitekey v2 * [core/website-v2] add posthog api key to defaults * [core/website-v2] setup loops api-key and turnstile site-key and secret * update abis and model * update indexer * [core/website-v2] fix gradient of progress indeterminate (#1200) * [core/website-v2] add switch component (#1199) * [core/website-v2] add switch component * [core/website-v2] add tw state helper for checked + unchecked * [repo] update lockfile * [core/website-v2] export switch * [core/website-v2] add darkmode styles to switch component * [core/website-v2] implement pagination component (#1190) * [core/website-v2] implement pagination component * [core/website-v2] set active state on PaginationLink * [core/website-v2] add asChild prop to pagination link * [core/website-v2] add InnerWrapper prop to PaginationQuickLinks * [core/website-v2] add Shared/Pagination component * [core/website-v2] fix typo * [core/website-v2] add useUrlSearchParams hook * [core/website-v2] fix build * [core/website-v2] fix blood * [core/website-v2] fix typedoc * [core/website-v2] fix typedoc * Feat/toggle group variants (#1201) * [core/website-v2] add toggle group item radius variants * [core/website-v2] add wrap to ToggleGroup and whitespace-nowrap to ToggleGroupItem * [core/website-v2] add togglegroup context for variant styles from parent * update gql and hasura * use isGenerativeAuthor for objkt capture guard * use internal file api from indexer-v2 * [core/website] dont display zero as free * [core/website] update form for frame minting * set indexer-v2 signing enabled, tracing enabled for dev * copy deployed config to indexer-v2 settings * fix indexer-v2 build * update frame api logic * pnpm format * Feat/generalise blockchain filter (#1197) * [core/website-v2] add ChainFilter component * [core/website-v2] add missing key * [core/website-v2] fix typedoc * [core/website-v2] only return filter values from hook * [core/website-v2] dont use cleanChainFilters * add maxMintsPerFid to api model * remove experimentalSpecifierResolution * fix @fxhash/otel-tracing for esm services * migrate fs-emulator to esm * parse string port from env * migrate onchfs proxy to esm * use tsup for fs-emulator build * bump ts version * temp commit * migrate seed-authority to esm * revert root build change * upgrade axios * [core/website] fix frame form not working * [core/website] rename FreeFrame label to Frame minting * fix frame project minting * add working payable frame * improve check logic * clean public client * [core/website] fix copy remove 'free' * fix platform minting * format * fix event pick * clean * clean todo * normalise local and shared generative token types * fix web build * update farcaster minter address * try all provided tezos rpcs before failing from the onchfs proxy resolver * add logs to fetchExponentialBackoff implementation * reset contracts * [website-v2] feat: add sentry config (#1213) * [repo] feat: add sentry config * [core/website-v2] Update next.config.mjs * update backoff strategy for indexer-v2 * add tezos service abstraction to simplify rpc shuffle/retry * use last operation to obtain indexer status * hide rebate option from tezos projects * remove aws details from config package * remove aws from config package * add aws variables to env.ts * Update applications/core/file-api/src/utils/auth.ts Co-authored-by: LΓ©o Pradel * update env.ts * move aws config back to env variables * move aws config back to env vars * rm unused * pin deps * sepolia -> eth-sepolia * update type * add moderation-enabled flag to eth-indexer conffig * fix isFixed minter check * remove logs * remove debugger * clean console * rm module.exports syntax * fix safe types * env port should be string * fix eth signer private key env * fix eth-indexer esm runtime * add explicit types to safe sdk * upgrade nextjs * move service configs to service definitions * remove service config definitions from package * rm unused * Feat/add custom message to frame image error (#1219) * [core/website] text align center message of farcasterframe card image * [core/website] allow custom message on error frame image * [core/website] display custom error message on simulation; refactor simulation * [core/website] log error * [core/website] fix response setup * Feat/frame improvements (#1221) * properly return the check frame for payable frames * fix check label * remove gates and clean session update * fix session update * fix session update * [core/website] Revert "Feat/frame improvements (#1221)" This reverts commit 28f8e77341c5fae328763fbc9f114d4dba9b2a35. * [packages/gql] add query to retrieve objkts of token by wallet * [packages/gql] update generated * [core/website] add helper to fetch objets per wallets and latest minted iteration * [core/website] fix free + payed frame flow * [core/website] add more conditions to show minting button * [core/website] fix minted out condition --------- Co-authored-by: Florian * [core/website] use frame minting feature flag (#1225) * migrate to local config * upgrade marketstats tsup, typescript * fix reservoir-indexer node env * fix file-api env config * fix dotenv esm usage * fix ipfs internal url * fix file-api readFileSync usage * add missing config for frame indexing * fix fs emulator start script * fix FXHASH_ENV enum defs in env files * fix config definitions prod -> prd * fix fs-emu FXHASH_ENV variable * fix require.resolve usage for fs-emu * fix @fxhash/otel-tracing usage for extract-balancer * Revert "migrate fs-emulator to esm" * Revert "Revert "migrate fs-emulator to esm"" * remove fs-emu circ deps * downgrade fs-emu tsup * handle cjs aws package in fs-emu * import js-sha3 for compatibility with esm consumers * env.port should be string * [repo] feat: pnpm v9 (#1226) * @fxhash/seed-authority-db to esm * remove duplicate shuffle definition * fix seed-authority start scripts * remove extra slash when using tzkt api * use last operation for tezos indexer status in website-v2 * [repo] fix: fix docker to use the correct turbo / pnpm version (#1233) * add cjs version back to @fxhash/eth build * [website-v2] feat: add DropdownMenu component (#1216) * migrate api-v2 to esm * @fxhash/authentication-db to esm * migrate indexer to esm * esm import for lodash * api-v2 esm fixes * fix graphql json usage for reserve.data * revert typeorm upgrade and use specified BaseEntity import * api-v2 esm fixes * upgrade api-v2 typeorm * api-v2 sort fixes * fix esm api sorting * more api-v2 sort fixes --------- Co-authored-by: Louis Holley Co-authored-by: Florian Co-authored-by: LΓ©o Pradel --- examples/test-project/package.json | 11 ++-- examples/test-project/tsup.config.ts | 13 ++++ packages/onchfs-js/package.json | 2 +- packages/onchfs-js/src/resolver/proxy.ts | 46 +++++-------- .../onchfs-js/src/services/tezos.service.ts | 66 +++++++++++++++++++ packages/onchfs-js/src/utils/keccak.ts | 3 +- 6 files changed, 105 insertions(+), 36 deletions(-) create mode 100644 examples/test-project/tsup.config.ts create mode 100644 packages/onchfs-js/src/services/tezos.service.ts diff --git a/examples/test-project/package.json b/examples/test-project/package.json index d8c0f39..19ec32f 100644 --- a/examples/test-project/package.json +++ b/examples/test-project/package.json @@ -8,10 +8,10 @@ ], "license": "MIT", "scripts": { - "clean": "rm -rf dist", - "build": "npm run clean && tsc --declaration", - "dev": "nodemon --watch \"src/**/*.ts\" --exec \"ts-node --transpile-only\" src/index.ts", - "test": "echo \"Error: no test specified\" && exit 1" + "prod": "tsc && node dist/index.js", + "build": "tsup", + "dev": "tsup --watch --on-success 'node dist/index.js'", + "start": "nodemon --watch \"src/**/*.ts\" --exec \"ts-node --transpile-only\" src/index.ts" }, "dependencies": { "@fxhash/config": "workspace:*", @@ -23,13 +23,14 @@ "node-dir": "0.1.17", "onchfs": "workspace:*", "tslib": "2.6.0", - "viem": "1.21.4" + "viem": "2.9.2" }, "devDependencies": { "@types/node": "18.7.13", "nodemon": "2.0.13", "ts-node": "10.9.1", "tsc-alias": "1.8.5", + "tsup": "7.2.0", "typescript": "4.9.5" } } diff --git a/examples/test-project/tsup.config.ts b/examples/test-project/tsup.config.ts new file mode 100644 index 0000000..b51d83e --- /dev/null +++ b/examples/test-project/tsup.config.ts @@ -0,0 +1,13 @@ +import { defineConfig, Options } from "tsup" + +export default defineConfig((options: Options) => ({ + entry: ["src/index.ts"], + outDir: "dist", + format: ["cjs"], + splitting: true, + sourcemap: true, + clean: !options.watch, + dts: true, + bundle: true, + cjsInterop: true, +})) diff --git a/packages/onchfs-js/package.json b/packages/onchfs-js/package.json index e62241b..8d9e2de 100644 --- a/packages/onchfs-js/package.json +++ b/packages/onchfs-js/package.json @@ -27,7 +27,7 @@ "js-sha3": "0.9.1", "mime-types": "2.1.35", "pako": "2.1.0", - "viem": "1.21.4" + "viem": "2.9.2" }, "devDependencies": { "@fxhash/tsconfig": "workspace:*", diff --git a/packages/onchfs-js/src/resolver/proxy.ts b/packages/onchfs-js/src/resolver/proxy.ts index b5886e9..5696239 100644 --- a/packages/onchfs-js/src/resolver/proxy.ts +++ b/packages/onchfs-js/src/resolver/proxy.ts @@ -28,11 +28,6 @@ import { parseSchema, parseSchemaSpecificPart, } from "@/uri/parse" -import { - TezosToolkit, - ContractAbstraction, - ContractProvider, -} from "@taquito/taquito" import { DEFAULT_CONTRACTS } from "@/config" import { createPublicClient, @@ -43,6 +38,7 @@ import { } from "viem" import { ONCHFS_FILE_SYSTEM_ABI } from "@/utils/abi" import { EthInode, EthInodeType } from "@/types/eth" +import { TezosService } from "@/services/tezos.service" const ResolutionErrors: Record = { [ProxyResolutionStatusErrors.BAD_REQUEST]: "Bad Request", @@ -118,17 +114,7 @@ export function createProxyResolver(controllers: BlockchainResolverCtrl[]) { switch (blockchain) { case "tezos": { - const Tezos = new TezosToolkit(h.rpcs[0]) - - const KTs: Record> = {} - - async function KT(address: string) { - if (KTs[address]) { - return KTs[address] - } - KTs[address] = await Tezos.contract.at(address) - return KTs[address] - } + const tezos = new TezosService(h.rpcs) return { blockchain: h.blockchain, @@ -142,15 +128,16 @@ export function createProxyResolver(controllers: BlockchainResolverCtrl[]) { } return { getInodeAtPath: async (cid, path) => { - const kt = await KT(address) - const out = await kt.contractViews - .get_inode_at({ - cid, - path, - }) - .executeView({ - viewCaller: address, - }) + const out = await tezos.call(address, kt => + kt.contractViews + .get_inode_at({ + cid, + path, + }) + .executeView({ + viewCaller: address, + }) + ) // if the contract has answered with a directory if (out.inode.directory) { @@ -172,10 +159,11 @@ export function createProxyResolver(controllers: BlockchainResolverCtrl[]) { } }, readFile: async cid => { - const kt = await KT(address) - const res = await kt.contractViews.read_file(cid).executeView({ - viewCaller: address, - }) + const res = await tezos.call(address, kt => + kt.contractViews.read_file(cid).executeView({ + viewCaller: address, + }) + ) return hexStringToBytes(res.content) }, } diff --git a/packages/onchfs-js/src/services/tezos.service.ts b/packages/onchfs-js/src/services/tezos.service.ts new file mode 100644 index 0000000..f132ed0 --- /dev/null +++ b/packages/onchfs-js/src/services/tezos.service.ts @@ -0,0 +1,66 @@ +import { + ContractAbstraction, + ContractProvider, + TezosToolkit, +} from "@taquito/taquito" + +function shuffle(array: T[]): T[] { + const out = [...array] + for (let i = out.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)) + ;[out[i], out[j]] = [out[j], out[i]] + } + return out +} + +export class TezosService { + private tezosToolkit: TezosToolkit + private rpcNodes: string[] + private contractsCache: Record< + string, + ContractAbstraction + > = {} + + constructor(rpcs: string[]) { + this.tezosToolkit = new TezosToolkit(rpcs[0]) + this.rpcNodes = rpcs + } + + async call( + address: string, + callback: (contract: ContractAbstraction) => Promise + ): Promise { + for (const rpc of shuffle(this.rpcNodes)) { + this.tezosToolkit.setProvider({ rpc }) + try { + const contract = await this.getContract(address) + return await callback(contract) + } catch (err) { + if (!this.canErrorBeCycled(err)) throw err + console.error(`RPC ${rpc} failed: ${err}, trying next...`) + } + } + throw new Error("all RPCs failed") + } + + async getContract( + address: string + ): Promise> { + if (!this.contractsCache[address]) { + this.contractsCache[address] = + await this.tezosToolkit.contract.at(address) + } + return this.contractsCache[address] + } + + // given an error, returns true if request can be cycled to another RPC node + private canErrorBeCycled(err: any): boolean { + return ( + err && + (err.name === "HttpRequestFailed" || + err.status === 500 || + err.status === 408 || + err.status === 429) + ) + } +} diff --git a/packages/onchfs-js/src/utils/keccak.ts b/packages/onchfs-js/src/utils/keccak.ts index 1547f66..368ea4c 100644 --- a/packages/onchfs-js/src/utils/keccak.ts +++ b/packages/onchfs-js/src/utils/keccak.ts @@ -1,4 +1,5 @@ -import { keccak256 } from "js-sha3" +import sha3 from "js-sha3" +const { keccak256 } = sha3 /** * Hashes some bytes with keccak256. Simple typed wrapper to ease implementation From 429821c1fe983d06238d6b07e0f33f97d8b9019b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Pradel?= Date: Thu, 25 Apr 2024 13:46:33 +0200 Subject: [PATCH 49/61] [repo] fix --- doc/package.json | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/doc/package.json b/doc/package.json index 442b8db..eb6ff4f 100644 --- a/doc/package.json +++ b/doc/package.json @@ -15,20 +15,21 @@ "typecheck": "tsc" }, "dependencies": { - "@docusaurus/core": "2.4.1", - "@docusaurus/preset-classic": "2.4.1", - "@docusaurus/theme-mermaid": "2.4.1", - "@mdx-js/react": "^1.6.22", - "@types/node": "^20.6.2", - "clsx": "^1.2.1", - "prism-react-renderer": "^1.3.5", - "react": "^17.0.2", - "react-dom": "^17.0.2" + "@docusaurus/core": "3.2.1", + "@docusaurus/preset-classic": "3.2.1", + "@docusaurus/theme-mermaid": "3.2.1", + "@mdx-js/react": "3.0.1", + "@types/node": "20.6.2", + "clsx": "2.1.1", + "prism-react-renderer": "2.3.1", + "react": "18.2.0", + "react-dom": "18.2.0" }, "devDependencies": { - "@docusaurus/module-type-aliases": "2.4.1", - "@tsconfig/docusaurus": "^1.0.5", - "typescript": "^4.7.4" + "@docusaurus/module-type-aliases": "3.2.1", + "@docusaurus/types": "3.0.0", + "@docusaurus/tsconfig": "3.2.1", + "typescript": "5.4.5" }, "browserslist": { "production": [ @@ -43,6 +44,6 @@ ] }, "engines": { - "node": ">=16.14" + "node": ">=18" } } From a263324ce8b5da9bba236dcde72b79afcfb074af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Pradel?= Date: Thu, 25 Apr 2024 15:12:34 +0200 Subject: [PATCH 50/61] [repo] feat: upgrade onchfs to docusaurus v3 and react 18 (#1254) --- doc/README.md | 2 +- doc/docs/concepts/http-proxy.md | 2 +- doc/docusaurus.config.js | 153 ------------------ doc/docusaurus.config.ts | 143 ++++++++++++++++ doc/package.json | 32 ++-- doc/{sidebars.js => sidebars.ts} | 10 +- doc/src/components/HomepageFeatures/index.tsx | 70 -------- .../HomepageFeatures/styles.module.css | 11 -- doc/src/pages/index.tsx | 7 +- doc/src/pages/markdown-page.md | 7 - doc/tsconfig.json | 2 +- 11 files changed, 170 insertions(+), 269 deletions(-) delete mode 100644 doc/docusaurus.config.js create mode 100644 doc/docusaurus.config.ts rename doc/{sidebars.js => sidebars.ts} (96%) delete mode 100644 doc/src/components/HomepageFeatures/index.tsx delete mode 100644 doc/src/components/HomepageFeatures/styles.module.css delete mode 100644 doc/src/pages/markdown-page.md diff --git a/doc/README.md b/doc/README.md index aaba2fa..0c6c2c2 100644 --- a/doc/README.md +++ b/doc/README.md @@ -1,6 +1,6 @@ # Website -This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. +This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator. ### Installation diff --git a/doc/docs/concepts/http-proxy.md b/doc/docs/concepts/http-proxy.md index cea6ea4..23fef50 100644 --- a/doc/docs/concepts/http-proxy.md +++ b/doc/docs/concepts/http-proxy.md @@ -58,7 +58,7 @@ https://proxy-url.com/ ━━━━━┻━━━━━ ``` -:::info URI<->Proxy URL compatibility +:::info[URI Proxy to URL compatibility] Because of the design of its format, the _schema-specific part_ is fully compatible with URLs. Onchfs URIs also support URL path, search and fragment components, enabling as many use-cases as there are on the web for HTML documents. [Read more about the URI specification here](./uris) ::: diff --git a/doc/docusaurus.config.js b/doc/docusaurus.config.js deleted file mode 100644 index 8188c12..0000000 --- a/doc/docusaurus.config.js +++ /dev/null @@ -1,153 +0,0 @@ -// @ts-check -// Note: type annotations allow type checking and IDEs autocompletion - -const lightCodeTheme = require("prism-react-renderer/themes/dracula") -const darkCodeTheme = require("prism-react-renderer/themes/dracula") - -/** @type {import('@docusaurus/types').Config} */ -const config = { - title: "ONCHFS β€” On-Chain for Http File System", - tagline: "A file system for blockchains.", - favicon: "img/favicon.ico", - - // Set the production url of your site here - url: "https://onchfs.com", - // Set the // pathname under which your site is served - // For GitHub pages deployment, it is often '//' - baseUrl: "/", - - // GitHub pages deployment config. - // If you aren't using GitHub pages, you don't need these. - organizationName: "fxhash", - projectName: "onchfs", - - onBrokenLinks: "throw", - onBrokenMarkdownLinks: "warn", - - // Even if you don't use internalization, you can use this field to set useful - // metadata like html lang. For example, if your site is Chinese, you may want - // to replace "en" with "zh-Hans". - i18n: { - defaultLocale: "en", - locales: ["en"], - }, - - markdown: { - mermaid: true, - }, - themes: ["@docusaurus/theme-mermaid"], - - presets: [ - [ - "classic", - /** @type {import('@docusaurus/preset-classic').Options} */ - ({ - docs: { - sidebarPath: require.resolve("./sidebars.js"), - // Please change this to your repo. - // Remove this to remove the "edit this page" links. - editUrl: - "https://github.com/fxhash/onchfs/tree/main/doc/templates/shared/", - }, - theme: { - customCss: require.resolve("./src/css/custom.css"), - }, - blog: false, - }), - ], - ], - - themeConfig: - /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ - ({ - image: "img/social-card.png", - navbar: { - title: "ONCHFS", - // logo: { - // alt: "My Site Logo", - // src: "img/logo.svg", - // }, - items: [ - { - type: "docSidebar", - sidebarId: "getting-started", - position: "left", - label: "Getting started", - }, - { - type: "docSidebar", - sidebarId: "concepts", - position: "left", - label: "Concepts", - }, - { - type: "docSidebar", - sidebarId: "libraries", - position: "left", - label: "Libraries", - }, - { - href: "https://github.com/fxhash/onchfs", - label: "GitHub", - position: "right", - }, - ], - }, - footer: { - style: "dark", - links: [ - { - title: "Docs", - items: [ - { - label: "Getting started", - to: "/docs/intro", - }, - { - label: "Concepts", - to: "/docs/concepts/hashing", - }, - { - label: "Libraries", - to: "/docs/libraries/overview", - }, - ], - }, - { - title: "Community", - items: [ - { - label: "Stack Overflow", - href: "https://stackoverflow.com/questions/tagged/docusaurus", - }, - { - label: "Discord", - href: "https://discordapp.com/invite/docusaurus", - }, - { - label: "Twitter", - href: "https://twitter.com/docusaurus", - }, - ], - }, - { - title: "More", - items: [ - { - label: "GitHub", - href: "https://github.com/fxhash/onchfs", - }, - ], - }, - ], - copyright: `Copyright Β© ${new Date().getFullYear()} fxhash. Built with Docusaurus.`, - }, - prism: { - theme: lightCodeTheme, - darkTheme: darkCodeTheme, - additionalLanguages: ["solidity", "abnf"], - }, - }), -} - -module.exports = config diff --git a/doc/docusaurus.config.ts b/doc/docusaurus.config.ts new file mode 100644 index 0000000..c4f0ce5 --- /dev/null +++ b/doc/docusaurus.config.ts @@ -0,0 +1,143 @@ +import { themes as prismThemes } from "prism-react-renderer" +import type { Config } from "@docusaurus/types" +import type * as Preset from "@docusaurus/preset-classic" + +const config: Config = { + title: "ONCHFS β€” On-Chain for Http File System", + tagline: "A file system for blockchains.", + favicon: "img/favicon.ico", + + // Set the production url of your site here + url: "https://onchfs.com", + // Set the // pathname under which your site is served + // For GitHub pages deployment, it is often '//' + baseUrl: "/", + + // GitHub pages deployment config. + // If you aren't using GitHub pages, you don't need these. + organizationName: "fxhash", + projectName: "onchfs", + + onBrokenLinks: "throw", + onBrokenMarkdownLinks: "warn", + + // Even if you don't use internationalization, you can use this field to set + // useful metadata like html lang. For example, if your site is Chinese, you + // may want to replace "en" with "zh-Hans". + i18n: { + defaultLocale: "en", + locales: ["en"], + }, + + markdown: { + mermaid: true, + }, + themes: ["@docusaurus/theme-mermaid"], + + presets: [ + [ + "classic", + { + docs: { + sidebarPath: "./sidebars.ts", + // Please change this to your repo. + // Remove this to remove the "edit this page" links. + editUrl: + "https://github.com/fxhash/onchfs/tree/main/doc/templates/shared/", + }, + blog: false, + theme: { + customCss: "./src/css/custom.css", + }, + } satisfies Preset.Options, + ], + ], + + themeConfig: { + image: "img/social-card.png", + navbar: { + title: "ONCHFS", + items: [ + { + type: "docSidebar", + sidebarId: "getting-started", + position: "left", + label: "Getting started", + }, + { + type: "docSidebar", + sidebarId: "concepts", + position: "left", + label: "Concepts", + }, + { + type: "docSidebar", + sidebarId: "libraries", + position: "left", + label: "Libraries", + }, + { + href: "https://github.com/fxhash/onchfs", + label: "GitHub", + position: "right", + }, + ], + }, + footer: { + style: "dark", + links: [ + { + title: "Docs", + items: [ + { + label: "Getting started", + to: "/docs/intro", + }, + { + label: "Concepts", + to: "/docs/concepts/hashing", + }, + { + label: "Libraries", + to: "/docs/libraries/overview", + }, + ], + }, + { + title: "Community", + items: [ + { + label: "Stack Overflow", + href: "https://stackoverflow.com/questions/tagged/docusaurus", + }, + { + label: "Discord", + href: "https://discordapp.com/invite/docusaurus", + }, + { + label: "Twitter", + href: "https://twitter.com/docusaurus", + }, + ], + }, + { + title: "More", + items: [ + { + label: "GitHub", + href: "https://github.com/fxhash/onchfs", + }, + ], + }, + ], + copyright: `Copyright Β© ${new Date().getFullYear()} fxhash. Built with Docusaurus.`, + }, + prism: { + theme: prismThemes.dracula, + darkTheme: prismThemes.dracula, + additionalLanguages: ["solidity", "abnf"], + }, + } satisfies Preset.ThemeConfig, +} + +export default config diff --git a/doc/package.json b/doc/package.json index 442b8db..c5b1962 100644 --- a/doc/package.json +++ b/doc/package.json @@ -15,20 +15,20 @@ "typecheck": "tsc" }, "dependencies": { - "@docusaurus/core": "2.4.1", - "@docusaurus/preset-classic": "2.4.1", - "@docusaurus/theme-mermaid": "2.4.1", - "@mdx-js/react": "^1.6.22", - "@types/node": "^20.6.2", - "clsx": "^1.2.1", - "prism-react-renderer": "^1.3.5", - "react": "^17.0.2", - "react-dom": "^17.0.2" + "@docusaurus/core": "3.2.1", + "@docusaurus/preset-classic": "3.2.1", + "@docusaurus/theme-mermaid": "3.2.1", + "@mdx-js/react": "3.0.1", + "clsx": "2.1.1", + "prism-react-renderer": "2.3.1", + "react": "18.2.0", + "react-dom": "18.2.0" }, "devDependencies": { - "@docusaurus/module-type-aliases": "2.4.1", - "@tsconfig/docusaurus": "^1.0.5", - "typescript": "^4.7.4" + "@docusaurus/module-type-aliases": "3.2.1", + "@docusaurus/tsconfig": "3.2.1", + "@docusaurus/types": "3.2.1", + "typescript": "5.3.3" }, "browserslist": { "production": [ @@ -37,12 +37,12 @@ "not op_mini all" ], "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" + "last 3 chrome version", + "last 3 firefox version", + "last 5 safari version" ] }, "engines": { - "node": ">=16.14" + "node": ">=18.0" } } diff --git a/doc/sidebars.js b/doc/sidebars.ts similarity index 96% rename from doc/sidebars.js rename to doc/sidebars.ts index e01c627..8e62742 100644 --- a/doc/sidebars.js +++ b/doc/sidebars.ts @@ -1,3 +1,5 @@ +import type { SidebarsConfig } from "@docusaurus/plugin-content-docs" + /** * Creating a sidebar enables you to: - create an ordered group of docs @@ -8,11 +10,7 @@ Create as many sidebars as you want. */ - -// @ts-check - -/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const sidebars = { +const sidebars: SidebarsConfig = { "getting-started": [ { type: "doc", @@ -174,4 +172,4 @@ const sidebars = { ], } -module.exports = sidebars +export default sidebars diff --git a/doc/src/components/HomepageFeatures/index.tsx b/doc/src/components/HomepageFeatures/index.tsx deleted file mode 100644 index 5145cc3..0000000 --- a/doc/src/components/HomepageFeatures/index.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import React from "react" -import clsx from "clsx" -import styles from "./styles.module.css" - -type FeatureItem = { - title: string - Svg: React.ComponentType> - description: JSX.Element -} - -const FeatureList: FeatureItem[] = [ - { - title: "Easy to Use", - Svg: require("@site/static/img/undraw_docusaurus_mountain.svg").default, - description: ( - <> - Docusaurus was designed from the ground up to be easily installed and - used to get your website up and running quickly. - - ), - }, - { - title: "Focus on What Matters", - Svg: require("@site/static/img/undraw_docusaurus_tree.svg").default, - description: ( - <> - Docusaurus lets you focus on your docs, and we'll do the chores. Go - ahead and move your docs into the docs directory. - - ), - }, - { - title: "Powered by React", - Svg: require("@site/static/img/undraw_docusaurus_react.svg").default, - description: ( - <> - Extend or customize your website layout by reusing React. Docusaurus can - be extended while reusing the same header and footer. - - ), - }, -] - -function Feature({ title, Svg, description }: FeatureItem) { - return ( -
-
- -
-
-

{title}

-

{description}

-
-
- ) -} - -export default function HomepageFeatures(): JSX.Element { - return ( -
-
-
- {FeatureList.map((props, idx) => ( - - ))} -
-
-
- ) -} diff --git a/doc/src/components/HomepageFeatures/styles.module.css b/doc/src/components/HomepageFeatures/styles.module.css deleted file mode 100644 index b248eb2..0000000 --- a/doc/src/components/HomepageFeatures/styles.module.css +++ /dev/null @@ -1,11 +0,0 @@ -.features { - display: flex; - align-items: center; - padding: 2rem 0; - width: 100%; -} - -.featureSvg { - height: 200px; - width: 200px; -} diff --git a/doc/src/pages/index.tsx b/doc/src/pages/index.tsx index ebb3fce..428d8fd 100644 --- a/doc/src/pages/index.tsx +++ b/doc/src/pages/index.tsx @@ -1,9 +1,8 @@ -import React from "react" import clsx from "clsx" import Link from "@docusaurus/Link" import useDocusaurusContext from "@docusaurus/useDocusaurusContext" import Layout from "@theme/Layout" -import HomepageFeatures from "@site/src/components/HomepageFeatures" +import Heading from "@theme/Heading" import styles from "./index.module.css" @@ -12,7 +11,9 @@ function HomepageHeader() { return (
-

{siteConfig.title}

+ + {siteConfig.title} +

{siteConfig.tagline}

Date: Fri, 26 Apr 2024 11:01:21 +0200 Subject: [PATCH 51/61] [repo] some fixes --- examples/test-project/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/test-project/package.json b/examples/test-project/package.json index 19ec32f..b77d065 100644 --- a/examples/test-project/package.json +++ b/examples/test-project/package.json @@ -31,6 +31,6 @@ "ts-node": "10.9.1", "tsc-alias": "1.8.5", "tsup": "7.2.0", - "typescript": "4.9.5" + "typescript": "5.3.3" } } From dba16d0e53355665f2ced00cb6b99aa9efaefa6f Mon Sep 17 00:00:00 2001 From: maerzhase Date: Sat, 27 Jul 2024 21:50:14 +0200 Subject: [PATCH 52/61] fix versions --- examples/test-project/package.json | 2 +- packages/onchfs-js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/test-project/package.json b/examples/test-project/package.json index b77d065..4d401d1 100644 --- a/examples/test-project/package.json +++ b/examples/test-project/package.json @@ -23,7 +23,7 @@ "node-dir": "0.1.17", "onchfs": "workspace:*", "tslib": "2.6.0", - "viem": "2.9.2" + "viem": "2.13.10" }, "devDependencies": { "@types/node": "18.7.13", diff --git a/packages/onchfs-js/package.json b/packages/onchfs-js/package.json index 8d9e2de..b9829c8 100644 --- a/packages/onchfs-js/package.json +++ b/packages/onchfs-js/package.json @@ -27,7 +27,7 @@ "js-sha3": "0.9.1", "mime-types": "2.1.35", "pako": "2.1.0", - "viem": "2.9.2" + "viem": "2.13.10" }, "devDependencies": { "@fxhash/tsconfig": "workspace:*", From 271455059e38d00305d99cca396bc400eede9239 Mon Sep 17 00:00:00 2001 From: maerzhase Date: Mon, 29 Jul 2024 15:54:45 +0200 Subject: [PATCH 53/61] add syncpack; enfore exact in prod dependencies --- examples/test-project-next/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/test-project-next/package.json b/examples/test-project-next/package.json index c151d2a..3100eb7 100644 --- a/examples/test-project-next/package.json +++ b/examples/test-project-next/package.json @@ -9,8 +9,8 @@ "lint": "next lint" }, "dependencies": { - "@taquito/signer": "^17.3.0", - "@taquito/taquito": "^17.3.0", + "@taquito/signer": "17.3.0", + "@taquito/taquito": "17.3.0", "onchfs": "workspace:*", "@types/node": "20.5.9", "@types/react": "*", From 3a29930c83e12da2ba88497246eaa1cb53988b46 Mon Sep 17 00:00:00 2001 From: maerzhase Date: Mon, 29 Jul 2024 17:32:56 +0200 Subject: [PATCH 54/61] enforce same versions of: react, connectkit, @types/react + @types/react-dom --- examples/test-project-next/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/test-project-next/package.json b/examples/test-project-next/package.json index 3100eb7..5ef5ec5 100644 --- a/examples/test-project-next/package.json +++ b/examples/test-project-next/package.json @@ -13,8 +13,8 @@ "@taquito/taquito": "17.3.0", "onchfs": "workspace:*", "@types/node": "20.5.9", - "@types/react": "*", - "@types/react-dom": "*", + "@types/react": "18.2.48", + "@types/react-dom": "18.2.18", "next": "13.4.19", "react": "18.2.0", "react-dom": "18.2.0", From 85942c59eca289ca6afb4bd56961ed8637819d48 Mon Sep 17 00:00:00 2001 From: maerzhase Date: Mon, 29 Jul 2024 17:44:23 +0200 Subject: [PATCH 55/61] ensure @types packages are in dev dependencies --- examples/http-proxy/package.json | 4 ++-- examples/test-project-next/package.json | 8 +++++--- examples/test-project/package.json | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/examples/http-proxy/package.json b/examples/http-proxy/package.json index c6a95b3..d663ef0 100644 --- a/examples/http-proxy/package.json +++ b/examples/http-proxy/package.json @@ -13,8 +13,6 @@ }, "dependencies": { "@taquito/taquito": "17.3.0", - "@types/express": "4.17.17", - "@types/pako": "2.0.0", "axios": "1.5.0", "dotenv": "10.0.0", "express": "4.18.2", @@ -25,6 +23,8 @@ "@fxhash/eslint-config": "workspace:*", "@types/cors": "2.8.13", "@types/node": "16.18.38", + "@types/express": "4.17.17", + "@types/pako": "2.0.0", "nodemon": "2.0.22", "ts-node": "10.9.1", "typescript": "4.9.5" diff --git a/examples/test-project-next/package.json b/examples/test-project-next/package.json index 5ef5ec5..070c8e1 100644 --- a/examples/test-project-next/package.json +++ b/examples/test-project-next/package.json @@ -12,12 +12,14 @@ "@taquito/signer": "17.3.0", "@taquito/taquito": "17.3.0", "onchfs": "workspace:*", - "@types/node": "20.5.9", - "@types/react": "18.2.48", - "@types/react-dom": "18.2.18", "next": "13.4.19", "react": "18.2.0", "react-dom": "18.2.0", "typescript": "5.2.2" + }, + "devDependencies": { + "@types/react": "18.2.48", + "@types/react-dom": "18.2.18", + "@types/node": "20.5.9" } } diff --git a/examples/test-project/package.json b/examples/test-project/package.json index 4d401d1..08b17e1 100644 --- a/examples/test-project/package.json +++ b/examples/test-project/package.json @@ -18,7 +18,6 @@ "@fxhash/eth": "workspace:*", "@taquito/signer": "17.3.0", "@taquito/taquito": "17.3.0", - "@types/node-dir": "0.0.34", "axios": "1.5.1", "node-dir": "0.1.17", "onchfs": "workspace:*", @@ -27,6 +26,7 @@ }, "devDependencies": { "@types/node": "18.7.13", + "@types/node-dir": "0.0.34", "nodemon": "2.0.13", "ts-node": "10.9.1", "tsc-alias": "1.8.5", From 53247180cc6a6fec68b90a7f53f845def24bf869 Mon Sep 17 00:00:00 2001 From: maerzhase Date: Mon, 29 Jul 2024 18:21:36 +0200 Subject: [PATCH 56/61] lint package.json files --- doc/package.json | 48 ++++++++++++------------- examples/http-proxy/package.json | 20 +++++------ examples/test-project-next/package.json | 20 +++++------ examples/test-project/package.json | 26 +++++++------- packages/onchfs-js/package.json | 40 ++++++++++----------- 5 files changed, 77 insertions(+), 77 deletions(-) diff --git a/doc/package.json b/doc/package.json index c5b1962..df96a12 100644 --- a/doc/package.json +++ b/doc/package.json @@ -1,18 +1,17 @@ { "name": "onchfs-doc", "version": "0.0.0", - "private": true, - "scripts": { - "docusaurus": "docusaurus", - "start": "docusaurus start", - "build": "docusaurus build", - "swizzle": "docusaurus swizzle", - "deploy": "docusaurus deploy", - "clear": "docusaurus clear", - "serve": "docusaurus serve", - "write-translations": "docusaurus write-translations", - "write-heading-ids": "docusaurus write-heading-ids", - "typecheck": "tsc" + "browserslist": { + "production": [ + ">0.5%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 3 chrome version", + "last 3 firefox version", + "last 5 safari version" + ] }, "dependencies": { "@docusaurus/core": "3.2.1", @@ -30,19 +29,20 @@ "@docusaurus/types": "3.2.1", "typescript": "5.3.3" }, - "browserslist": { - "production": [ - ">0.5%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 3 chrome version", - "last 3 firefox version", - "last 5 safari version" - ] - }, "engines": { "node": ">=18.0" + }, + "private": true, + "scripts": { + "build": "docusaurus build", + "clear": "docusaurus clear", + "deploy": "docusaurus deploy", + "docusaurus": "docusaurus", + "serve": "docusaurus serve", + "start": "docusaurus start", + "swizzle": "docusaurus swizzle", + "typecheck": "tsc", + "write-heading-ids": "docusaurus write-heading-ids", + "write-translations": "docusaurus write-translations" } } diff --git a/examples/http-proxy/package.json b/examples/http-proxy/package.json index d663ef0..0ae916b 100644 --- a/examples/http-proxy/package.json +++ b/examples/http-proxy/package.json @@ -1,16 +1,7 @@ { "name": "onchfs-http-proxy-example", - "private": true, "version": "1.0.0", "author": "fxhash", - "license": "MIT", - "main": "index.js", - "scripts": { - "prod": "tsc && node dist/index.js", - "build": "tsc", - "start": "nodemon --watch \"src/**/*.ts\" --exec \"ts-node --transpile-only\" src/index.ts", - "dev": "nodemon --watch \"src/**/*.ts\" --exec \"ts-node --transpile-only\" src/index.ts" - }, "dependencies": { "@taquito/taquito": "17.3.0", "axios": "1.5.0", @@ -22,11 +13,20 @@ "devDependencies": { "@fxhash/eslint-config": "workspace:*", "@types/cors": "2.8.13", - "@types/node": "16.18.38", "@types/express": "4.17.17", + "@types/node": "16.18.38", "@types/pako": "2.0.0", "nodemon": "2.0.22", "ts-node": "10.9.1", "typescript": "4.9.5" + }, + "license": "MIT", + "main": "index.js", + "private": true, + "scripts": { + "build": "tsc", + "dev": "nodemon --watch \"src/**/*.ts\" --exec \"ts-node --transpile-only\" src/index.ts", + "prod": "tsc && node dist/index.js", + "start": "nodemon --watch \"src/**/*.ts\" --exec \"ts-node --transpile-only\" src/index.ts" } } diff --git a/examples/test-project-next/package.json b/examples/test-project-next/package.json index 070c8e1..a26eea3 100644 --- a/examples/test-project-next/package.json +++ b/examples/test-project-next/package.json @@ -1,25 +1,25 @@ { "name": "test-project-next", "version": "0.1.0", - "private": true, - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "lint": "next lint" - }, "dependencies": { "@taquito/signer": "17.3.0", "@taquito/taquito": "17.3.0", - "onchfs": "workspace:*", "next": "13.4.19", + "onchfs": "workspace:*", "react": "18.2.0", "react-dom": "18.2.0", "typescript": "5.2.2" }, "devDependencies": { + "@types/node": "20.5.9", "@types/react": "18.2.48", - "@types/react-dom": "18.2.18", - "@types/node": "20.5.9" + "@types/react-dom": "18.2.18" + }, + "private": true, + "scripts": { + "build": "next build", + "dev": "next dev", + "lint": "next lint", + "start": "next start" } } diff --git a/examples/test-project/package.json b/examples/test-project/package.json index 08b17e1..e587624 100644 --- a/examples/test-project/package.json +++ b/examples/test-project/package.json @@ -1,18 +1,6 @@ { "name": "onchfs-tests", "version": "1.0.0", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "files": [ - "dist/**/*" - ], - "license": "MIT", - "scripts": { - "prod": "tsc && node dist/index.js", - "build": "tsup", - "dev": "tsup --watch --on-success 'node dist/index.js'", - "start": "nodemon --watch \"src/**/*.ts\" --exec \"ts-node --transpile-only\" src/index.ts" - }, "dependencies": { "@fxhash/config": "workspace:*", "@fxhash/eth": "workspace:*", @@ -32,5 +20,17 @@ "tsc-alias": "1.8.5", "tsup": "7.2.0", "typescript": "5.3.3" - } + }, + "files": [ + "dist/**/*" + ], + "license": "MIT", + "main": "dist/index.js", + "scripts": { + "build": "tsup", + "dev": "tsup --watch --on-success 'node dist/index.js'", + "prod": "tsc && node dist/index.js", + "start": "nodemon --watch \"src/**/*.ts\" --exec \"ts-node --transpile-only\" src/index.ts" + }, + "types": "dist/index.d.ts" } diff --git a/packages/onchfs-js/package.json b/packages/onchfs-js/package.json index b9829c8..e93ea44 100644 --- a/packages/onchfs-js/package.json +++ b/packages/onchfs-js/package.json @@ -1,25 +1,7 @@ { "name": "onchfs", "version": "0.0.0", - "exports": { - ".": { - "require": "./dist/index.js", - "import": "./dist/index.mjs", - "types": "./dist/index.d.ts" - } - }, - "main": "./dist/index.mjs", "browser": "./dist/index.js", - "types": "./dist/index.d.ts", - "files": [ - "dist" - ], - "license": "MIT", - "scripts": { - "build": "tsup", - "dev": "tsup --watch", - "test": "jest" - }, "dependencies": { "@taquito/taquito": "17.3.1", "file-type": "18.5.0", @@ -30,11 +12,11 @@ "viem": "2.13.10" }, "devDependencies": { - "@fxhash/tsconfig": "workspace:*", "@babel/core": "7.22.19", "@babel/preset-env": "7.22.15", "@babel/preset-typescript": "7.22.15", "@esbuild-plugins/node-modules-polyfill": "0.2.2", + "@fxhash/tsconfig": "workspace:*", "@types/jest": "29.5.4", "@types/mime-types": "2.1.1", "@types/node": "20.6.3", @@ -46,5 +28,23 @@ "tslib": "2.6.0", "tsup": "8.0.1", "typescript": "5.3.3" - } + }, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs", + "require": "./dist/index.js" + } + }, + "files": [ + "dist" + ], + "license": "MIT", + "main": "./dist/index.mjs", + "scripts": { + "build": "tsup", + "dev": "tsup --watch", + "test": "jest" + }, + "types": "./dist/index.d.ts" } From 64a6a42397d948dea6af34642d981cc62440cc49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Pradel?= Date: Mon, 12 Aug 2024 12:03:50 +0200 Subject: [PATCH 57/61] [repo] up viem --- examples/test-project/package.json | 2 +- packages/onchfs-js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/test-project/package.json b/examples/test-project/package.json index b77d065..0876ed4 100644 --- a/examples/test-project/package.json +++ b/examples/test-project/package.json @@ -23,7 +23,7 @@ "node-dir": "0.1.17", "onchfs": "workspace:*", "tslib": "2.6.0", - "viem": "2.9.2" + "viem": "2.19.4" }, "devDependencies": { "@types/node": "18.7.13", diff --git a/packages/onchfs-js/package.json b/packages/onchfs-js/package.json index 8d9e2de..75f70a6 100644 --- a/packages/onchfs-js/package.json +++ b/packages/onchfs-js/package.json @@ -27,7 +27,7 @@ "js-sha3": "0.9.1", "mime-types": "2.1.35", "pako": "2.1.0", - "viem": "2.9.2" + "viem": "2.19.4" }, "devDependencies": { "@fxhash/tsconfig": "workspace:*", From 7cbd634e69ec9793fc5bfb9dc94f9afe5796125a Mon Sep 17 00:00:00 2001 From: maerzhase Date: Tue, 20 Aug 2024 17:27:47 +0200 Subject: [PATCH 58/61] bump @types/react to 18.3.3 --- examples/test-project-next/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/test-project-next/package.json b/examples/test-project-next/package.json index a26eea3..4ca822e 100644 --- a/examples/test-project-next/package.json +++ b/examples/test-project-next/package.json @@ -12,7 +12,7 @@ }, "devDependencies": { "@types/node": "20.5.9", - "@types/react": "18.2.48", + "@types/react": "18.3.3", "@types/react-dom": "18.2.18" }, "private": true, From 64183f62723f52e65227e83322aec96f35ff1c1b Mon Sep 17 00:00:00 2001 From: maerzhase Date: Tue, 20 Aug 2024 18:58:48 +0200 Subject: [PATCH 59/61] fix onchfs-js module resolution --- examples/http-proxy/package.json | 1 + packages/onchfs-js/package.json | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/examples/http-proxy/package.json b/examples/http-proxy/package.json index 0ae916b..525abdc 100644 --- a/examples/http-proxy/package.json +++ b/examples/http-proxy/package.json @@ -23,6 +23,7 @@ "license": "MIT", "main": "index.js", "private": true, + "type": "module", "scripts": { "build": "tsc", "dev": "nodemon --watch \"src/**/*.ts\" --exec \"ts-node --transpile-only\" src/index.ts", diff --git a/packages/onchfs-js/package.json b/packages/onchfs-js/package.json index 9cf833e..f4565e5 100644 --- a/packages/onchfs-js/package.json +++ b/packages/onchfs-js/package.json @@ -1,7 +1,6 @@ { "name": "onchfs", "version": "0.0.0", - "browser": "./dist/index.js", "dependencies": { "@taquito/taquito": "17.3.1", "file-type": "18.5.0", @@ -32,19 +31,21 @@ "exports": { ".": { "types": "./dist/index.d.ts", - "import": "./dist/index.mjs", - "require": "./dist/index.js" + "import": "./dist/index.js", + "require": "./dist/index.cjs" } }, "files": [ "dist" ], "license": "MIT", - "main": "./dist/index.mjs", + "main": "./dist/index.cjs", + "module": "./dist/index.js", "scripts": { "build": "tsup", "dev": "tsup --watch", "test": "jest" }, + "type": "module", "types": "./dist/index.d.ts" } From 7527b72d7240e50d9a6a347d47644d845cc5f801 Mon Sep 17 00:00:00 2001 From: maerzhase Date: Wed, 28 Aug 2024 10:37:57 +0200 Subject: [PATCH 60/61] syncpack format + fix mismatches --- examples/http-proxy/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/http-proxy/package.json b/examples/http-proxy/package.json index 525abdc..102e51b 100644 --- a/examples/http-proxy/package.json +++ b/examples/http-proxy/package.json @@ -23,11 +23,11 @@ "license": "MIT", "main": "index.js", "private": true, - "type": "module", "scripts": { "build": "tsc", "dev": "nodemon --watch \"src/**/*.ts\" --exec \"ts-node --transpile-only\" src/index.ts", "prod": "tsc && node dist/index.js", "start": "nodemon --watch \"src/**/*.ts\" --exec \"ts-node --transpile-only\" src/index.ts" - } + }, + "type": "module" } From b60668fb0b54b34182562266469d177899e41bce Mon Sep 17 00:00:00 2001 From: maerzhase Date: Fri, 6 Sep 2024 11:49:20 +0200 Subject: [PATCH 61/61] update package.json files --- doc/package.json | 2 +- examples/http-proxy/package.json | 14 +++++++------- examples/test-project-next/package.json | 6 +++--- examples/test-project/package.json | 14 +++++++------- packages/onchfs-js/package.json | 12 ++++++------ 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/doc/package.json b/doc/package.json index df96a12..cad8bb5 100644 --- a/doc/package.json +++ b/doc/package.json @@ -45,4 +45,4 @@ "write-heading-ids": "docusaurus write-heading-ids", "write-translations": "docusaurus write-translations" } -} +} \ No newline at end of file diff --git a/examples/http-proxy/package.json b/examples/http-proxy/package.json index 102e51b..7c6d508 100644 --- a/examples/http-proxy/package.json +++ b/examples/http-proxy/package.json @@ -7,27 +7,27 @@ "axios": "1.5.0", "dotenv": "10.0.0", "express": "4.18.2", - "onchfs": "workspace:*", - "pako": "2.1.0" + "pako": "2.1.0", + "onchfs": "0.0.0" }, "devDependencies": { - "@fxhash/eslint-config": "workspace:*", "@types/cors": "2.8.13", "@types/express": "4.17.17", "@types/node": "16.18.38", "@types/pako": "2.0.0", "nodemon": "2.0.22", "ts-node": "10.9.1", - "typescript": "4.9.5" + "typescript": "4.9.5", + "@fxhash/eslint-config": "1.0.2" }, "license": "MIT", "main": "index.js", "private": true, + "type": "module", "scripts": { "build": "tsc", "dev": "nodemon --watch \"src/**/*.ts\" --exec \"ts-node --transpile-only\" src/index.ts", "prod": "tsc && node dist/index.js", "start": "nodemon --watch \"src/**/*.ts\" --exec \"ts-node --transpile-only\" src/index.ts" - }, - "type": "module" -} + } +} \ No newline at end of file diff --git a/examples/test-project-next/package.json b/examples/test-project-next/package.json index 4ca822e..9959e6d 100644 --- a/examples/test-project-next/package.json +++ b/examples/test-project-next/package.json @@ -5,10 +5,10 @@ "@taquito/signer": "17.3.0", "@taquito/taquito": "17.3.0", "next": "13.4.19", - "onchfs": "workspace:*", "react": "18.2.0", "react-dom": "18.2.0", - "typescript": "5.2.2" + "typescript": "5.2.2", + "onchfs": "0.0.0" }, "devDependencies": { "@types/node": "20.5.9", @@ -22,4 +22,4 @@ "lint": "next lint", "start": "next start" } -} +} \ No newline at end of file diff --git a/examples/test-project/package.json b/examples/test-project/package.json index faedeec..ff67765 100644 --- a/examples/test-project/package.json +++ b/examples/test-project/package.json @@ -2,15 +2,15 @@ "name": "onchfs-tests", "version": "1.0.0", "dependencies": { - "@fxhash/config": "workspace:*", - "@fxhash/eth": "workspace:*", "@taquito/signer": "17.3.0", "@taquito/taquito": "17.3.0", "axios": "1.5.1", "node-dir": "0.1.17", - "onchfs": "workspace:*", "tslib": "2.6.0", - "viem": "2.19.4" + "viem": "2.19.4", + "onchfs": "0.0.0", + "@fxhash/config": "0.0.9", + "@fxhash/eth": "0.0.9" }, "devDependencies": { "@types/node": "18.7.13", @@ -26,11 +26,11 @@ ], "license": "MIT", "main": "dist/index.js", + "types": "dist/index.d.ts", "scripts": { "build": "tsup", "dev": "tsup --watch --on-success 'node dist/index.js'", "prod": "tsc && node dist/index.js", "start": "nodemon --watch \"src/**/*.ts\" --exec \"ts-node --transpile-only\" src/index.ts" - }, - "types": "dist/index.d.ts" -} + } +} \ No newline at end of file diff --git a/packages/onchfs-js/package.json b/packages/onchfs-js/package.json index f4565e5..722e2da 100644 --- a/packages/onchfs-js/package.json +++ b/packages/onchfs-js/package.json @@ -15,7 +15,6 @@ "@babel/preset-env": "7.22.15", "@babel/preset-typescript": "7.22.15", "@esbuild-plugins/node-modules-polyfill": "0.2.2", - "@fxhash/tsconfig": "workspace:*", "@types/jest": "29.5.4", "@types/mime-types": "2.1.1", "@types/node": "20.6.3", @@ -26,7 +25,8 @@ "tsc-alias": "1.8.5", "tslib": "2.6.0", "tsup": "8.0.1", - "typescript": "5.3.3" + "typescript": "5.3.3", + "@fxhash/tsconfig": "1.0.0" }, "exports": { ".": { @@ -41,11 +41,11 @@ "license": "MIT", "main": "./dist/index.cjs", "module": "./dist/index.js", + "type": "module", + "types": "./dist/index.d.ts", "scripts": { "build": "tsup", "dev": "tsup --watch", "test": "jest" - }, - "type": "module", - "types": "./dist/index.d.ts" -} + } +} \ No newline at end of file