diff --git a/.changeset/mean-yaks-lay.md b/.changeset/mean-yaks-lay.md new file mode 100644 index 00000000..5e4ba644 --- /dev/null +++ b/.changeset/mean-yaks-lay.md @@ -0,0 +1,5 @@ +--- +"@fleet-sdk/blockchain-providers": patch +--- + +Fix `Header#votes` encoding diff --git a/.changeset/real-weeks-fix.md b/.changeset/real-weeks-fix.md new file mode 100644 index 00000000..04617b22 --- /dev/null +++ b/.changeset/real-weeks-fix.md @@ -0,0 +1,5 @@ +--- +"@fleet-sdk/blockchain-providers": patch +--- + +Export `BlockChainProviders` types diff --git a/.changeset/spotty-foxes-fetch.md b/.changeset/spotty-foxes-fetch.md new file mode 100644 index 00000000..9bd2219c --- /dev/null +++ b/.changeset/spotty-foxes-fetch.md @@ -0,0 +1,5 @@ +--- +"@fleet-sdk/core": patch +--- + +Set default token limit for a single box to `100` diff --git a/.changeset/unlucky-numbers-sort.md b/.changeset/unlucky-numbers-sort.md new file mode 100644 index 00000000..99bfb100 --- /dev/null +++ b/.changeset/unlucky-numbers-sort.md @@ -0,0 +1,5 @@ +--- +"@fleet-sdk/blockchain-providers": patch +--- + +[`ErgoGraphQLProvider`] Query Addresses/ErgoTrees by chunks of 20 diff --git a/.changeset/wicked-rice-mate.md b/.changeset/wicked-rice-mate.md new file mode 100644 index 00000000..5e78ca27 --- /dev/null +++ b/.changeset/wicked-rice-mate.md @@ -0,0 +1,5 @@ +--- +"@fleet-sdk/blockchain-providers": patch +--- + +Export `request` and `createGqlOperation` utility functions diff --git a/packages/blockchain-providers/src/ergo-graphql/ergoGraphQLProvider.spec.ts b/packages/blockchain-providers/src/ergo-graphql/ergoGraphQLProvider.spec.ts index 6f9b7d8e..89e07af0 100644 --- a/packages/blockchain-providers/src/ergo-graphql/ergoGraphQLProvider.spec.ts +++ b/packages/blockchain-providers/src/ergo-graphql/ergoGraphQLProvider.spec.ts @@ -63,7 +63,7 @@ describe("ergo-graphql provider", () => { await _client.getBoxes({ where: { - boxIds: ["boxId_1", "boxId_2"], + boxId: "boxId_1", ergoTrees: ["contract", "another_contract"] } }); @@ -71,7 +71,7 @@ describe("ergo-graphql provider", () => { call = JSON.parse(fetchSpy.mock.lastCall?.[1]?.body as string); expect(call.variables).to.be.deep.equal({ spent: false, - boxIds: ["boxId_1", "boxId_2"], + boxIds: ["boxId_1"], ergoTrees: ["contract", "another_contract"], skip: 0, take: 50 @@ -90,7 +90,6 @@ describe("ergo-graphql provider", () => { .setBigIntMapper((v) => Number(v)) .getBoxes({ where: { - boxIds: ["boxId_0", "boxId_1"], boxId: "boxId_0", ergoTrees: ["ergoTree_0", "ergoTree_1", "ergoTree_1"], ergoTree: "ergoTree_2" @@ -103,7 +102,7 @@ describe("ergo-graphql provider", () => { const call = JSON.parse(fetchSpy.mock.lastCall?.[1]?.body as string); expect(call.variables).to.be.deep.equal({ spent: false, - boxIds: ["boxId_0", "boxId_1"], + boxIds: ["boxId_0"], ergoTrees: ["ergoTree_0", "ergoTree_1", "ergoTree_2"], skip: 0, take: 50 @@ -451,7 +450,6 @@ describe("ergo-graphql provider", () => { const response = await _client.getConfirmedTransactions({ where: { transactionId: "txId", - transactionIds: ["txId_1", "txId_2", "txId_1"], address: _addresses[0].encode(), addresses: [_addresses[0], _addresses[1]], ergoTree: _addresses[1].ergoTree, @@ -469,7 +467,7 @@ describe("ergo-graphql provider", () => { let callBody = JSON.parse(fetchSpy.mock.lastCall?.[1]?.body as string); expect(callBody.query).to.be.equal(CONF_TX_QUERY); expect(callBody.variables).to.be.deep.equal({ - transactionIds: ["txId_1", "txId_2", "txId"], + transactionIds: ["txId"], addresses: [ _addresses[0].encode(), _addresses[1].encode(), @@ -565,8 +563,6 @@ describe("ergo-graphql provider", () => { const response = await _client.getUnconfirmedTransactions({ where: { - transactionId: "txId", - transactionIds: ["txId_1", "txId_2", "txId_1"], address: _addresses[0].encode(), addresses: [_addresses[0], _addresses[1]], ergoTree: _addresses[1].ergoTree, @@ -582,7 +578,6 @@ describe("ergo-graphql provider", () => { let callBody = JSON.parse(fetchSpy.mock.lastCall?.[1]?.body as string); expect(callBody.query).to.be.equal(UNCONF_TX_QUERY); expect(callBody.variables).to.be.deep.equal({ - transactionIds: ["txId_1", "txId_2", "txId"], addresses: [ _addresses[0].encode(), _addresses[1].encode(), @@ -679,7 +674,7 @@ describe("ergo-graphql provider", () => { id: header.headerId, nBits: Number(header.nBits), timestamp: Number(header.timestamp), - votes: header.votes.join("") + votes: "000000" })) ); expect(fetchSpy).toHaveBeenCalledOnce(); diff --git a/packages/blockchain-providers/src/ergo-graphql/ergoGraphQLProvider.ts b/packages/blockchain-providers/src/ergo-graphql/ergoGraphQLProvider.ts index 4c3546cf..b66eb32e 100644 --- a/packages/blockchain-providers/src/ergo-graphql/ergoGraphQLProvider.ts +++ b/packages/blockchain-providers/src/ergo-graphql/ergoGraphQLProvider.ts @@ -12,18 +12,20 @@ import type { import { type Base58String, type BlockHeader, - ensureDefaults, type HexString, + type SignedTransaction, + ensureDefaults, isEmpty, isUndefined, NotSupportedError, orderBy, - type SignedTransaction, some, uniq, - uniqBy + uniqBy, + chunk } from "@fleet-sdk/common"; import { ErgoAddress } from "@fleet-sdk/core"; +import { hex } from "@fleet-sdk/crypto"; import type { BoxQuery, BoxWhere, @@ -39,12 +41,11 @@ import type { UnconfirmedTransactionWhere } from "../types/blockchainProvider"; import { - createGqlOperation, type GraphQLOperation, type GraphQLRequestOptions, type GraphQLSuccessResponse, type GraphQLVariables, - isRequestParam + createGqlOperation } from "../utils"; import { ALL_BOXES_QUERY, @@ -57,14 +58,7 @@ import { UNCONF_TX_QUERY } from "./queries"; -type GraphQLThrowableOptions = GraphQLRequestOptions & { throwOnNonNetworkErrors: true }; -type OP = GraphQLOperation, V>; -type BiMapper = (value: string) => T; - export type GraphQLBoxWhere = BoxWhere & { - /** Base16-encoded BoxIds */ - boxIds?: HexString[]; - /** Base16-encoded ErgoTrees */ ergoTrees?: HexString[]; @@ -73,13 +67,11 @@ export type GraphQLBoxWhere = BoxWhere & { }; export type GraphQLConfirmedTransactionWhere = ConfirmedTransactionWhere & { - transactionIds?: HexString[]; addresses?: (Base58String | ErgoAddress)[]; ergoTrees?: HexString[]; }; export type GraphQLUnconfirmedTransactionWhere = UnconfirmedTransactionWhere & { - transactionIds?: HexString[]; addresses?: (Base58String | ErgoAddress)[]; ergoTrees?: HexString[]; }; @@ -87,7 +79,7 @@ export type GraphQLUnconfirmedTransactionWhere = UnconfirmedTransactionWhere & { export type GraphQLBoxQuery = BoxQuery; export type ErgoGraphQLRequestOptions = Omit< GraphQLRequestOptions, - "throwOnNonNetworkError" + "throwOnNonNetworkErrors" >; type ConfirmedBoxesResponse = { boxes: GQLBox[] }; @@ -100,7 +92,15 @@ type CheckTransactionResponse = { checkTransaction: string }; type TransactionSubmissionResponse = { submitTransaction: string }; type SignedTxArgsResp = { signedTransaction: SignedTransaction }; +type GraphQLThrowableOptions = ErgoGraphQLRequestOptions & { + throwOnNonNetworkErrors: true; +}; + +type OP = GraphQLOperation, V>; +type BiMapper = (value: string) => T; + const PAGE_SIZE = 50; +const MAX_ARGS = 20; export class ErgoGraphQLProvider implements IBlockchainProvider { #options: GraphQLThrowableOptions; @@ -116,15 +116,14 @@ export class ErgoGraphQLProvider implements IBlockchainProvider { #getHeaders!: OP; constructor(url: string); - constructor(url: ErgoGraphQLRequestOptions); + constructor(options: ErgoGraphQLRequestOptions); constructor(optOrUrl: ErgoGraphQLRequestOptions | string) { + this.#biMapper = (value) => BigInt(value) as I; this.#options = { ...(isRequestParam(optOrUrl) ? optOrUrl : { url: optOrUrl }), throwOnNonNetworkErrors: true }; - this.#biMapper = (value) => BigInt(value) as I; - this.#getConfirmedBoxes = this.createOperation(CONF_BOXES_QUERY); this.#getUnconfirmedBoxes = this.createOperation(UNCONF_BOXES_QUERY); this.#getAllBoxes = this.createOperation(ALL_BOXES_QUERY); @@ -137,10 +136,10 @@ export class ErgoGraphQLProvider implements IBlockchainProvider { #fetchBoxes(args: QueryBoxesArgs, inclConf: boolean, inclUnconf: boolean) { return inclConf && inclUnconf - ? this.#getAllBoxes(args, this.#options.url) + ? this.#getAllBoxes(args) : inclUnconf - ? this.#getUnconfirmedBoxes(args, this.#options.url) - : this.#getConfirmedBoxes(args, this.#options.url); + ? this.#getUnconfirmedBoxes(args) + : this.#getConfirmedBoxes(args); } setUrl(url: string): ErgoGraphQLProvider { @@ -161,55 +160,57 @@ export class ErgoGraphQLProvider implements IBlockchainProvider { const notBeingSpent = (box: GQLBox) => !box.beingSpent; const returnedBoxIds = new Set(); const { where, from } = query; - const args = buildGqlBoxQueryArgs(where); + const queries = buildGqlBoxQueries(where); + const isMempoolAware = from !== "blockchain"; - let inclChain = from !== "mempool"; - let inclPool = from !== "blockchain"; - const isMempoolAware = inclPool; + for (const query of queries) { + let inclChain = from !== "mempool"; + let inclPool = from !== "blockchain"; - do { - const { data } = await this.#fetchBoxes(args, inclChain, inclPool); - let boxes: ChainProviderBox[] = []; + while (inclChain || inclPool) { + const { data } = await this.#fetchBoxes(query, inclChain, inclPool); + let boxes: ChainProviderBox[] = []; - if (inclChain && hasConfirmed(data)) { - if (some(data.boxes)) { - const confirmedBoxes = ( - isMempoolAware ? data.boxes.filter(notBeingSpent) : data.boxes - ).map((b) => mapConfirmedBox(b, this.#biMapper)); + if (inclChain && hasConfirmed(data)) { + if (some(data.boxes)) { + const confirmedBoxes = ( + isMempoolAware ? data.boxes.filter(notBeingSpent) : data.boxes + ).map((b) => mapConfirmedBox(b, this.#biMapper)); - boxes = boxes.concat(confirmedBoxes); - } + boxes = boxes.concat(confirmedBoxes); + } - inclChain = data.boxes.length === PAGE_SIZE; - } - - if (isMempoolAware && hasMempool(data)) { - if (some(data.mempool.boxes)) { - const mempoolBoxes = data.mempool.boxes - .filter(notBeingSpent) - .map((b) => mapUnconfirmedBox(b, this.#biMapper)); - boxes = boxes.concat(mempoolBoxes); + inclChain = data.boxes.length === PAGE_SIZE; } - inclPool = data.mempool.boxes.length === PAGE_SIZE; - } + if (isMempoolAware && hasMempool(data)) { + if (some(data.mempool.boxes)) { + const mempoolBoxes = data.mempool.boxes + .filter(notBeingSpent) + .map((b) => mapUnconfirmedBox(b, this.#biMapper)); + boxes = boxes.concat(mempoolBoxes); + } - if (some(boxes)) { - // boxes can be moved from the mempool to the blockchain while streaming, - // so we need to filter out boxes that have already been returned. - if (boxes.some((box) => returnedBoxIds.has(box.boxId))) { - boxes = boxes.filter((b) => !returnedBoxIds.has(b.boxId)); + inclPool = data.mempool.boxes.length === PAGE_SIZE; } if (some(boxes)) { - boxes = uniqBy(boxes, (box) => box.boxId); - for (const box of boxes) returnedBoxIds.add(box.boxId); - yield boxes; + // boxes can be moved from the mempool to the blockchain while streaming, + // so we need to filter out boxes that have already been returned. + if (boxes.some((box) => returnedBoxIds.has(box.boxId))) { + boxes = boxes.filter((b) => !returnedBoxIds.has(b.boxId)); + } + + if (some(boxes)) { + boxes = uniqBy(boxes, (box) => box.boxId); + for (const box of boxes) returnedBoxIds.add(box.boxId); + yield boxes; + } } - } - if (inclChain || inclPool) args.skip += PAGE_SIZE; - } while (inclChain || inclPool); + if (inclChain || inclPool) query.skip += PAGE_SIZE; + } + } } async getBoxes(query: GraphQLBoxQuery): Promise[]> { @@ -221,19 +222,21 @@ export class ErgoGraphQLProvider implements IBlockchainProvider { async *streamUnconfirmedTransactions( query: TransactionQuery ): AsyncIterable[]> { - const args = buildGqlUnconfirmedTxQueryArgs(query.where); - - let keepFetching = true; - while (keepFetching) { - const response = await this.#getUnconfirmedTransactions(args); - if (some(response.data?.mempool?.transactions)) { - yield response.data.mempool.transactions.map((t) => - mapUnconfirmedTransaction(t, this.#biMapper) - ); - } + const queries = buildGqlUnconfirmedTxQueries(query.where); + + for (const query of queries) { + let keepFetching = true; + while (keepFetching) { + const response = await this.#getUnconfirmedTransactions(query); + if (some(response.data?.mempool?.transactions)) { + yield response.data.mempool.transactions.map((t) => + mapUnconfirmedTransaction(t, this.#biMapper) + ); + } - keepFetching = response.data?.mempool?.transactions?.length === PAGE_SIZE; - if (keepFetching) args.skip += PAGE_SIZE; + keepFetching = response.data?.mempool?.transactions?.length === PAGE_SIZE; + if (keepFetching) query.skip += PAGE_SIZE; + } } } @@ -251,19 +254,21 @@ export class ErgoGraphQLProvider implements IBlockchainProvider { async *streamConfirmedTransactions( query: TransactionQuery ): AsyncIterable[]> { - const args = buildGqlConfirmedTxQueryArgs(query.where); - - let keepFetching = true; - while (keepFetching) { - const response = await this.#getConfirmedTransactions(args); - if (some(response.data?.transactions)) { - yield response.data.transactions.map((t) => - mapConfirmedTransaction(t, this.#biMapper) - ); - } + const queries = buildGqlConfirmedTxQueries(query.where); + + for (const query of queries) { + let keepFetching = true; + while (keepFetching) { + const response = await this.#getConfirmedTransactions(query); + if (some(response.data?.transactions)) { + yield response.data.transactions.map((t) => + mapConfirmedTransaction(t, this.#biMapper) + ); + } - keepFetching = response.data?.transactions?.length === PAGE_SIZE; - if (keepFetching) args.skip += PAGE_SIZE; + keepFetching = response.data?.transactions?.length === PAGE_SIZE; + if (keepFetching) query.skip += PAGE_SIZE; + } } } @@ -279,15 +284,15 @@ export class ErgoGraphQLProvider implements IBlockchainProvider { } async getHeaders(query: HeaderQuery): Promise { - const response = await this.#getHeaders(query, this.#options.url); + const response = await this.#getHeaders(query); return ( - response.data?.blockHeaders.map((header) => ({ - ...header, - id: header.headerId, - timestamp: Number(header.timestamp), - nBits: Number(header.nBits), - votes: header.votes.join("") + response.data?.blockHeaders.map((h) => ({ + ...h, + id: h.headerId, + timestamp: Number(h.timestamp), + nBits: Number(h.nBits), + votes: hex.encode(Uint8Array.from(h.votes)) })) ?? [] ); } @@ -306,10 +311,7 @@ export class ErgoGraphQLProvider implements IBlockchainProvider { signedTransaction: SignedTransaction ): Promise { try { - const response = await this.#checkTransaction( - { signedTransaction }, - this.#options.url - ); + const response = await this.#checkTransaction({ signedTransaction }); return { success: true, transactionId: response.data.checkTransaction }; } catch (e) { return { success: false, message: (e as Error).message }; @@ -320,10 +322,7 @@ export class ErgoGraphQLProvider implements IBlockchainProvider { signedTransaction: SignedTransaction ): Promise { try { - const response = await this.#sendTransaction( - { signedTransaction }, - this.#options.url - ); + const response = await this.#sendTransaction({ signedTransaction }); return { success: true, transactionId: response.data.submitTransaction }; } catch (e) { return { success: false, message: (e as Error).message }; @@ -335,32 +334,28 @@ export class ErgoGraphQLProvider implements IBlockchainProvider { } } -function buildGqlBoxQueryArgs(where: GraphQLBoxWhere) { - const args = { +function buildGqlBoxQueries(where: GraphQLBoxWhere) { + const ergoTrees = uniq( + [ + merge(where.ergoTrees, where.ergoTree) ?? [], + merge(where.addresses, where.address)?.map((a) => + typeof a === "string" ? ErgoAddress.decode(a).ergoTree : a.ergoTree + ) ?? [] + ].flat() + ); + + return chunk(ergoTrees, MAX_ARGS).map((chunk) => ({ spent: false, - boxIds: merge(where.boxIds, where.boxId), - ergoTrees: merge(where.ergoTrees, where.ergoTree), + boxIds: where.boxId ? [where.boxId] : undefined, + ergoTrees: chunk, ergoTreeTemplateHash: where.templateHash, tokenId: where.tokenId, skip: 0, take: PAGE_SIZE - } satisfies QueryBoxesArgs; - - const addresses = merge(where.addresses, where.address); - if (some(addresses)) { - const trees = addresses.map((address) => - typeof address === "string" - ? ErgoAddress.decode(address).ergoTree - : address.ergoTree - ); - - args.ergoTrees = uniq(some(args.ergoTrees) ? args.ergoTrees.concat(trees) : trees); - } - - return args; + })); } -function buildGqlUnconfirmedTxQueryArgs(where: GraphQLConfirmedTransactionWhere) { +function buildGqlUnconfirmedTxQueries(where: GraphQLConfirmedTransactionWhere) { const addresses = uniq( [ merge(where.addresses, where.address)?.map((address): string => @@ -372,21 +367,21 @@ function buildGqlUnconfirmedTxQueryArgs(where: GraphQLConfirmedTransactionWhere) ].flat() ); - return { - addresses: addresses.length ? addresses : undefined, - transactionIds: merge(where.transactionIds, where.transactionId), + return chunk(addresses, MAX_ARGS).map((chunk) => ({ + addresses: chunk.length ? chunk : undefined, + transactionIds: where.transactionId ? [where.transactionId] : undefined, skip: 0, take: PAGE_SIZE - }; + })); } -function buildGqlConfirmedTxQueryArgs(where: GraphQLConfirmedTransactionWhere) { - return { - ...buildGqlUnconfirmedTxQueryArgs(where), +function buildGqlConfirmedTxQueries(where: GraphQLConfirmedTransactionWhere) { + return buildGqlUnconfirmedTxQueries(where).map((query) => ({ + ...query, headerId: where.headerId, minHeight: where.minHeight, onlyRelevantOutputs: where.onlyRelevantOutputs - }; + })); } function merge(array?: T[], el?: T) { @@ -481,3 +476,7 @@ function mapConfirmedTransaction( confirmed: true }; } + +export function isRequestParam(obj: unknown): obj is ErgoGraphQLRequestOptions { + return typeof obj === "object" && (obj as ErgoGraphQLRequestOptions).url !== undefined; +} diff --git a/packages/blockchain-providers/src/index.ts b/packages/blockchain-providers/src/index.ts index 3a169903..d7755a04 100644 --- a/packages/blockchain-providers/src/index.ts +++ b/packages/blockchain-providers/src/index.ts @@ -1 +1,4 @@ export * from "./ergo-graphql/ergoGraphQLProvider"; +export * from "./types/blockchainProvider"; +export * from "./utils/networking"; +export * from "./utils/graphql"; diff --git a/packages/blockchain-providers/src/utils/graphql.spec.ts b/packages/blockchain-providers/src/utils/graphql.spec.ts index 4f1f3da2..1a68ebd7 100644 --- a/packages/blockchain-providers/src/utils/graphql.spec.ts +++ b/packages/blockchain-providers/src/utils/graphql.spec.ts @@ -4,15 +4,18 @@ import { afterEach, describe, expect, expectTypeOf, it, vi } from "vitest"; import { resolveString } from "./_tests"; import { createGqlOperation, - DEFAULT_HEADERS, getOpName, gql, type GraphQLOperation, type GraphQLSuccessResponse, - type GraphQLVariables, - isRequestParam + type GraphQLVariables } from "./graphql"; +const DEFAULT_HEADERS = { + "content-type": "application/json; charset=utf-8", + accept: "application/graphql-response+json, application/json" +}; + describe("GraphQL query builder", () => { const parseSpy = vi.spyOn(JSON, "parse"); const stringifySpy = vi.spyOn(JSON, "stringify"); @@ -255,14 +258,3 @@ describe("Operation name extraction", () => { expect(getOpName(" query ($take: Int) { boxes { boxId } }")).to.be.undefined; }); }); - -describe("Request param handler", () => { - it("should return true for valid request params", () => { - expect(isRequestParam({ url: "https://gql.example.com/" })).to.be.true; - }); - - it("should return false for invalid request params", () => { - expect(isRequestParam({})).to.be.false; - expect(isRequestParam(3)).to.be.false; - }); -}); diff --git a/packages/blockchain-providers/src/utils/graphql.ts b/packages/blockchain-providers/src/utils/graphql.ts index bbe53a0c..e882708c 100644 --- a/packages/blockchain-providers/src/utils/graphql.ts +++ b/packages/blockchain-providers/src/utils/graphql.ts @@ -9,7 +9,7 @@ import type { FallbackRetryOptions, ParserLike } from "./networking"; import { request } from "./networking"; const OP_NAME_REGEX = /(query|mutation)\s?([\w\-_]+)?/; -export const DEFAULT_HEADERS = { +const DEFAULT_HEADERS = { "content-type": "application/json; charset=utf-8", accept: "application/graphql-response+json, application/json" }; @@ -118,7 +118,3 @@ export function gql(query: TemplateStringsArray): string { export function getOpName(query: string): string | undefined { return OP_NAME_REGEX.exec(query)?.at(2); } - -export function isRequestParam(obj: unknown): obj is GraphQLRequestOptions { - return typeof obj === "object" && (obj as GraphQLRequestOptions).url !== undefined; -} diff --git a/packages/blockchain-providers/src/utils/networking.ts b/packages/blockchain-providers/src/utils/networking.ts index 14724658..ba1cc179 100644 --- a/packages/blockchain-providers/src/utils/networking.ts +++ b/packages/blockchain-providers/src/utils/networking.ts @@ -1,5 +1,4 @@ import { some } from "@fleet-sdk/common"; -import { isEmpty } from "packages/common/src"; export interface ParserLike { parse(text: string): T; diff --git a/packages/core/src/builder/transactionBuilder.spec.ts b/packages/core/src/builder/transactionBuilder.spec.ts index 4b1ca291..93757b5c 100644 --- a/packages/core/src/builder/transactionBuilder.spec.ts +++ b/packages/core/src/builder/transactionBuilder.spec.ts @@ -743,7 +743,7 @@ describe("Building", () => { expect(change1.ergoTree).toBe(a1.ergoTree); expect(change1.creationHeight).toBe(height); - expect(change1.value).toBe(3595808n); + expect(change1.value).toBe(3358208n); expect(change1.assets).toHaveLength(MAX_TOKENS_PER_BOX); expect(change1.additionalRegisters).toEqual({}); @@ -756,7 +756,7 @@ describe("Building", () => { expect(change3.ergoTree).toBe(a1.ergoTree); expect(change3.creationHeight).toBe(height); expect(change3.value).toBe(_estimateBoxValue(change3)); - expect(change3.assets).toHaveLength(32); + expect(change3.assets).toHaveLength(72); expect(change3.additionalRegisters).toEqual({}); }); diff --git a/packages/core/src/models/collections/tokensCollection.ts b/packages/core/src/models/collections/tokensCollection.ts index 5ff8ea14..aed9f1a8 100644 --- a/packages/core/src/models/collections/tokensCollection.ts +++ b/packages/core/src/models/collections/tokensCollection.ts @@ -15,7 +15,7 @@ import { NotFoundError, UndefinedMintingContext } from "../../errors"; import { InsufficientTokenAmount } from "../../errors/insufficientTokenAmount"; import { MaxTokensOverflow } from "../../errors/maxTokensOverflow"; -export const MAX_TOKENS_PER_BOX = 120; +export const MAX_TOKENS_PER_BOX = 100; export type TokenAddOptions = CollectionAddOptions & { sum?: boolean }; export type OutputToken = {