From 8910c7a32766807f517ee3610146bb9db552bb1a Mon Sep 17 00:00:00 2001 From: Pedro Nauck Date: Wed, 27 Sep 2023 17:38:44 -0300 Subject: [PATCH 01/15] chore: improve graphql package --- packages/graphql/.babelrc | 4 - packages/graphql/package.json | 8 +- .../metadata => }/data/accounts.json | 0 .../{services/metadata => }/data/tokens.json | 0 packages/graphql/src/domains/Account.ts | 44 ++++ packages/graphql/src/domains/Input.ts | 51 ++++ packages/graphql/src/domains/Token.ts | 46 ++++ packages/graphql/src/domains/Transaction.ts | 147 +++++++++++ .../src/domains/TransactionConnection.ts | 67 +++++ packages/graphql/src/schema.ts | 2 +- .../extender/delegators/QueryAccounts.ts | 21 -- .../extender/delegators/QueryTokens.ts | 21 -- .../src/services/extender/extender.graphql | 23 +- .../graphql/src/services/extender/index.ts | 8 +- .../extender/resolvers/Transaction.ts | 158 ----------- .../resolvers/TransactionConnection.ts | 30 --- .../src/services/extender/resolvers/index.ts | 2 - .../graphql/src/services/metadata/index.ts | 17 +- .../resolvers/Account/QueryAccounts.ts | 20 -- .../metadata/resolvers/Token/QueryTokens.ts | 20 -- .../src/services/metadata/resolvers/index.ts | 2 - .../graphql/src/services/metadata/schema.ts | 14 - packages/graphql/tsconfig.json | 4 +- packages/graphql/tsup.config.mjs | 16 ++ pnpm-lock.yaml | 248 +++++++++++++++++- 25 files changed, 672 insertions(+), 301 deletions(-) delete mode 100644 packages/graphql/.babelrc rename packages/graphql/src/{services/metadata => }/data/accounts.json (100%) rename packages/graphql/src/{services/metadata => }/data/tokens.json (100%) create mode 100644 packages/graphql/src/domains/Account.ts create mode 100644 packages/graphql/src/domains/Input.ts create mode 100644 packages/graphql/src/domains/Token.ts create mode 100644 packages/graphql/src/domains/Transaction.ts create mode 100644 packages/graphql/src/domains/TransactionConnection.ts delete mode 100644 packages/graphql/src/services/extender/delegators/QueryAccounts.ts delete mode 100644 packages/graphql/src/services/extender/delegators/QueryTokens.ts delete mode 100644 packages/graphql/src/services/extender/resolvers/Transaction.ts delete mode 100644 packages/graphql/src/services/extender/resolvers/TransactionConnection.ts delete mode 100644 packages/graphql/src/services/extender/resolvers/index.ts delete mode 100644 packages/graphql/src/services/metadata/resolvers/Account/QueryAccounts.ts delete mode 100644 packages/graphql/src/services/metadata/resolvers/Token/QueryTokens.ts delete mode 100644 packages/graphql/src/services/metadata/resolvers/index.ts delete mode 100644 packages/graphql/src/services/metadata/schema.ts create mode 100644 packages/graphql/tsup.config.mjs diff --git a/packages/graphql/.babelrc b/packages/graphql/.babelrc deleted file mode 100644 index d8cf9c8f5..000000000 --- a/packages/graphql/.babelrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "presets": ["@babel/preset-env", "@babel/preset-typescript"], - "plugins": ["import-graphql"] -} diff --git a/packages/graphql/package.json b/packages/graphql/package.json index 333687d30..cb9d20ed7 100644 --- a/packages/graphql/package.json +++ b/packages/graphql/package.json @@ -9,11 +9,13 @@ "types": "./src/index.ts", "typings": "./src/index.ts", "scripts": { - "dev": "node -r babel-register-ts src/bin.ts", + "build": "tsup --dts", + "dev": "pnpm build && node dist/index.js", "start": "pnpm dev", "codegen": "gql-gen --config codegen.ts", "codegen:schema": "gql-gen --config codegen.schema.ts", "fix:generated": "node ./scripts/fix-generated.mjs", + "ts:check": "tsc --noEmit", "prepare": "pnpm codegen" }, "dependencies": { @@ -24,11 +26,13 @@ "@graphql-tools/stitch": "^9.0.3", "@graphql-tools/url-loader": "^8.0.0", "@graphql-tools/utils": "^10.0.6", + "@luckycatfactory/esbuild-graphql-loader": "3.8.1", "cors": "^2.8.5", "dayjs": "1.11.10", "dotenv": "16.3.1", + "esbuild": "0.19.3", "express": "^4.18.2", - "graphql": "^16.8.1", + "graphql": "16.8.1", "graphql-http": "^1.22.0", "graphql-playground-middleware-express": "^1.7.23", "graphql-request": "6.1.0", diff --git a/packages/graphql/src/services/metadata/data/accounts.json b/packages/graphql/src/data/accounts.json similarity index 100% rename from packages/graphql/src/services/metadata/data/accounts.json rename to packages/graphql/src/data/accounts.json diff --git a/packages/graphql/src/services/metadata/data/tokens.json b/packages/graphql/src/data/tokens.json similarity index 100% rename from packages/graphql/src/services/metadata/data/tokens.json rename to packages/graphql/src/data/tokens.json diff --git a/packages/graphql/src/domains/Account.ts b/packages/graphql/src/domains/Account.ts new file mode 100644 index 000000000..1530ecc3b --- /dev/null +++ b/packages/graphql/src/domains/Account.ts @@ -0,0 +1,44 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { delegateToSchema } from '@graphql-tools/delegate'; +import type { GraphQLResolveInfo } from 'graphql'; +import { OperationTypeNode } from 'graphql'; + +import accountsData from '../data/accounts.json'; +import { metadataSchema } from '../services/metadata'; + +export class AccountDomain { + constructor() {} + + delegateQuery( + addresses: Array, + context: any, + info: GraphQLResolveInfo, + ) { + return delegateToSchema({ + schema: metadataSchema, + operation: OperationTypeNode.QUERY, + fieldName: 'accounts', + args: { addresses }, + context, + info, + }); + } + + static queryAccounts(addresses: Array) { + return addresses + .map((addres) => + accountsData.find( + (account) => account.address.toLowerCase() === addres.toLowerCase(), + ), + ) + .filter((i) => !!i); + } +} + +export function createAccountsResolver( + _source: any, + _args: any, + { addresses }: { addresses: string[] }, +) { + return AccountDomain.queryAccounts(addresses); +} diff --git a/packages/graphql/src/domains/Input.ts b/packages/graphql/src/domains/Input.ts new file mode 100644 index 000000000..515be0359 --- /dev/null +++ b/packages/graphql/src/domains/Input.ts @@ -0,0 +1,51 @@ +import { bn } from '@fuel-ts/math'; +import { groupBy } from 'lodash'; + +import type { + InputCoin, + InputContract, + InputMessage, + TransactionItemFragment, +} from '../generated/types'; + +type Inputs = TransactionItemFragment['inputs']; + +export class InputDomain { + constructor(private inputs: Inputs) {} + + get groupedInputs() { + return [...this.assetInputs, ...this.contractInputs, ...this.messageInputs]; + } + + get assetInputs() { + const inputs = this._filterByTypename('InputCoin'); + const entries = Object.entries(groupBy(inputs, (i) => i.assetId)); + return entries.map(([assetId, inputs]) => { + const type = inputs[0].__typename; + const owner = inputs[0].owner; + const totalAmount = this._getTotalAmount(inputs); + return { owner, assetId, type, totalAmount, inputs }; + }); + } + + get contractInputs() { + const inputs = this._filterByTypename('InputContract'); + const entries = Object.entries(groupBy(inputs, (i) => i.contract.id)); + return entries.map(([contractId, inputs]) => { + const type = inputs[0].__typename; + return { contractId, type, inputs }; + }); + } + + get messageInputs() { + return this._filterByTypename('InputMessage'); + } + + private _filterByTypename(typename: string) { + return this.inputs?.filter((i) => i.__typename === typename) as T[]; + } + + private _getTotalAmount(inputs: InputCoin[]) { + return inputs.reduce((acc, input) => acc.add(bn(input.amount)), bn(0)); + } +} diff --git a/packages/graphql/src/domains/Token.ts b/packages/graphql/src/domains/Token.ts new file mode 100644 index 000000000..3ded5dbf8 --- /dev/null +++ b/packages/graphql/src/domains/Token.ts @@ -0,0 +1,46 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { delegateToSchema } from '@graphql-tools/delegate'; +import type { GraphQLResolveInfo } from 'graphql'; +import { OperationTypeNode } from 'graphql'; + +import tokensData from '../data/tokens.json'; +import { metadataSchema } from '../services/metadata'; + +export class TokenDomain { + constructor() {} + + delegateQuery( + assetsId: Array, + context: any, + info: GraphQLResolveInfo, + ) { + return delegateToSchema({ + schema: metadataSchema, + operation: OperationTypeNode.QUERY, + fieldName: 'tokens', + args: { assetsId }, + context, + info, + }); + } + + static queryTokens(assetsId: string[]) { + return assetsId + .map((id) => + tokensData.find( + (token) => token.assetId.toLowerCase() === id.toLowerCase(), + ), + ) + .filter((i) => !!i); + } +} + +export function createTokensResolver() { + return function ( + _source: any, + _args: any, + { assetsId }: { assetsId: Array }, + ) { + return TokenDomain.queryTokens(assetsId); + }; +} diff --git a/packages/graphql/src/domains/Transaction.ts b/packages/graphql/src/domains/Transaction.ts new file mode 100644 index 000000000..88072f8c1 --- /dev/null +++ b/packages/graphql/src/domains/Transaction.ts @@ -0,0 +1,147 @@ +import { bn } from '@fuel-ts/math'; +import { uniqBy } from 'lodash'; + +import type { TransactionItemFragment } from '../generated/types'; +import { tai64toDate } from '../utils/dayjs'; + +import { InputDomain } from './Input'; + +export class TransactionDomain { + constructor(private transaction: TransactionItemFragment) {} + + static createResolver(key: string) { + return { + [key]: { + resolve(transaction: TransactionItemFragment) { + const domain = new TransactionDomain(transaction); + return domain[key] ?? null; + }, + }, + }; + } + + static createResolvers() { + return { + ...TransactionDomain.createResolver('title'), + ...TransactionDomain.createResolver('time'), + ...TransactionDomain.createResolver('blockHeight'), + ...TransactionDomain.createResolver('statusType'), + ...TransactionDomain.createResolver('totalAssets'), + ...TransactionDomain.createResolver('totalOperations'), + ...TransactionDomain.createResolver('totalAccounts'), + ...TransactionDomain.createResolver('gasUsed'), + ...TransactionDomain.createResolver('groupedInputs'), + ...TransactionDomain.createResolver('accountsInvolved'), + }; + } + + get title() { + const { transaction } = this; + if (transaction.isMint) return 'Mint'; + if (transaction.isCreate) return 'Contract Created'; + return 'ContractCall'; + } + + get time() { + const { transaction } = this; + const status = transaction.status; + if (status?.__typename === 'SqueezedOutStatus') return null; + const time = status?.time ?? null; + const date = tai64toDate(time); + return { + fromNow: date.fromNow(), + full: date.format('DD MMM YYYY - HH:mm:ss A'), + rawTai64: time.toString(), + rawUnix: date.unix().toString(), + }; + } + + get blockHeight() { + const { transaction } = this; + const status = transaction.status; + if (status?.__typename === 'SuccessStatus') { + return status?.block?.header?.daHeight ?? null; + } + return null; + } + + get statusType() { + const { transaction } = this; + const typename = transaction.status?.__typename; + if (typename === 'SuccessStatus') { + return 'Success'; + } + if (typename === 'FailureStatus') { + return 'Failure'; + } + return 'Submitted'; + } + + get totalAssets() { + const { transaction } = this; + if (transaction.isMint) return 1; + return transaction.inputAssetIds?.length ?? 0; + } + + get totalOperations() { + const { transaction } = this; + if (transaction.isMint) return 1; + return transaction.inputs?.length ?? 0; + } + + get totalAccounts() { + const { transaction } = this; + if (transaction.isMint) return 1; + return this._getAccounts().length; + } + + get gasUsed() { + const { transaction } = this; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const receipts = (transaction.receipts ?? []) as any[]; + const gasUsed = receipts.reduce((acc, receipt) => { + return acc.add(bn(receipt.gasUsed)); + }, bn(0)); + return gasUsed.toString(); + } + + get accountsInvolved() { + const { transaction } = this; + if (transaction.isMint) return []; + return this._getAccounts() ?? []; + } + + get groupedInputs() { + const { transaction } = this; + const domain = new InputDomain(transaction.inputs ?? []); + return domain.groupedInputs; + } + + private _getAccounts() { + const { transaction } = this; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const ids = transaction.inputs?.flatMap((input: any) => { + const typename = input?.__typename; + if (typename === 'InputCoin') { + return { + type: 'Contract', + id: input.owner, + }; + } + if (typename === 'InputMessage') { + return [ + { type: 'Wallet', id: input.sender }, + { type: 'Wallet', id: input.sender }, + ]; + } + if (typename === 'InputContract') { + return { + type: 'Contract', + id: input.contract.id, + }; + } + }); + + return uniqBy(ids, 'id'); + } +} diff --git a/packages/graphql/src/domains/TransactionConnection.ts b/packages/graphql/src/domains/TransactionConnection.ts new file mode 100644 index 000000000..c69bea101 --- /dev/null +++ b/packages/graphql/src/domains/TransactionConnection.ts @@ -0,0 +1,67 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import type { GraphQLResolveInfo } from 'graphql'; + +import type { TransactionConnection } from '../generated/types'; +import { removeDuplicates, getFieldsValues } from '../utils'; + +import { AccountDomain } from './Account'; +import { TokenDomain } from './Token'; + +export class TransactionConnectionDomain { + constructor( + private connection: TransactionConnection, + private context: any, + private info: GraphQLResolveInfo, + ) {} + + static createResolver(key: string) { + return { + [key]: { + resolve( + connection: TransactionConnection, + _args: any, + context: any, + info: GraphQLResolveInfo, + ) { + const domain = new TransactionConnectionDomain( + connection, + context, + info, + ); + return domain[key] ?? null; + }, + }, + }; + } + + static createResolvers() { + return { + ...TransactionConnectionDomain.createResolver('tokens'), + ...TransactionConnectionDomain.createResolver('accounts'), + }; + } + + get tokens() { + const { connection, context, info } = this; + const assetsId = removeDuplicates( + getFieldsValues(connection.nodes, ['assetId']), + ); + const domain = new TokenDomain(); + return domain.delegateQuery(assetsId, context, info); + } + + get accounts() { + const { connection, context, info } = this; + const assetsId = removeDuplicates( + getFieldsValues(connection.nodes, [ + 'to', + 'owner', + 'recipient', + 'sender', + 'toAddress', + ]), + ); + const domain = new AccountDomain(); + return domain.delegateQuery(assetsId, context, info); + } +} diff --git a/packages/graphql/src/schema.ts b/packages/graphql/src/schema.ts index 73c5d3006..34069ab20 100644 --- a/packages/graphql/src/schema.ts +++ b/packages/graphql/src/schema.ts @@ -5,7 +5,7 @@ import type { Application } from 'express'; import { createHandler } from 'graphql-http/lib/use/express'; import { ExtenderResolvers, ExtenderTypeDefs } from './services/extender'; -import { metadataSchema } from './services/metadata/schema'; +import { metadataSchema } from './services/metadata'; import { createGraphqlFetch } from './utils'; export async function createSchema(fuelCoreGraphql: string) { diff --git a/packages/graphql/src/services/extender/delegators/QueryAccounts.ts b/packages/graphql/src/services/extender/delegators/QueryAccounts.ts deleted file mode 100644 index 5b31db09f..000000000 --- a/packages/graphql/src/services/extender/delegators/QueryAccounts.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { delegateToSchema } from '@graphql-tools/delegate'; -import type { GraphQLResolveInfo } from 'graphql'; -import { OperationTypeNode } from 'graphql'; - -import { metadataSchema } from '../../metadata'; - -export function delegateQueryAccounts( - addresses: Array, - context: any, - info: GraphQLResolveInfo, -) { - return delegateToSchema({ - schema: metadataSchema, - operation: OperationTypeNode.QUERY, - fieldName: 'accounts', - args: { addresses }, - context, - info, - }); -} diff --git a/packages/graphql/src/services/extender/delegators/QueryTokens.ts b/packages/graphql/src/services/extender/delegators/QueryTokens.ts deleted file mode 100644 index e5b225bad..000000000 --- a/packages/graphql/src/services/extender/delegators/QueryTokens.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { delegateToSchema } from '@graphql-tools/delegate'; -import type { GraphQLResolveInfo } from 'graphql'; -import { OperationTypeNode } from 'graphql'; - -import { metadataSchema } from '../../metadata'; - -export function delegateQueryTokens( - assetsId: Array, - context: any, - info: GraphQLResolveInfo, -) { - return delegateToSchema({ - schema: metadataSchema, - operation: OperationTypeNode.QUERY, - fieldName: 'tokens', - args: { assetsId }, - context, - info, - }); -} diff --git a/packages/graphql/src/services/extender/extender.graphql b/packages/graphql/src/services/extender/extender.graphql index b5fd57096..4e4e3f7f5 100644 --- a/packages/graphql/src/services/extender/extender.graphql +++ b/packages/graphql/src/services/extender/extender.graphql @@ -42,6 +42,26 @@ type GroupedInput { owner: Address } +enum GroupedOutputType { + OutputCoin + ContractOutput + MessageOutput + ChangeOutput + VariableOutput + ContractCreated +} + +type GroupedOutput { + type: GroupedOutputType + totalAmount: U64 + outputs: [Output] + to: Address + assetId: AssetId + inputIndex: Int + recipient: Address + contract: Contract +} + enum TransactionAccountType { Contract Predicate @@ -62,6 +82,7 @@ extend type Transaction { totalAssets: Int totalOperations: Int gasUsed: U64 - groupedInputs: [GroupedInput] accountsInvolved: [TransactionAccount] + groupedInputs: [GroupedInput] + groupedOutputs: [GroupedOutput] } diff --git a/packages/graphql/src/services/extender/index.ts b/packages/graphql/src/services/extender/index.ts index 1d2ca8277..136269b34 100644 --- a/packages/graphql/src/services/extender/index.ts +++ b/packages/graphql/src/services/extender/index.ts @@ -1,4 +1,10 @@ +import { TransactionDomain } from '../../domains/Transaction'; +import { TransactionConnectionDomain } from '../../domains/TransactionConnection'; + import typeDefs from './extender.graphql'; -export * as ExtenderResolvers from './resolvers'; export const ExtenderTypeDefs = typeDefs; +export const ExtenderResolvers = { + Transaction: TransactionDomain.createResolvers(), + TransactionConnection: TransactionConnectionDomain.createResolvers(), +}; diff --git a/packages/graphql/src/services/extender/resolvers/Transaction.ts b/packages/graphql/src/services/extender/resolvers/Transaction.ts deleted file mode 100644 index d9e0e42e2..000000000 --- a/packages/graphql/src/services/extender/resolvers/Transaction.ts +++ /dev/null @@ -1,158 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { bn } from '@fuel-ts/math'; -import type { IResolvers } from '@graphql-tools/utils'; -import { groupBy, uniqBy } from 'lodash'; - -import type { - InputCoin, - InputContract, - InputMessage, - TransactionItemFragment, -} from '../../../'; -import { tai64toDate } from '../../../utils/dayjs'; - -export const Transaction: IResolvers = { - title: { - resolve(transaction, _args, _context, _info) { - if (transaction.isMint) return 'Mint'; - if (transaction.isCreate) return 'Contract Created'; - return 'ContractCall'; - }, - }, - time: { - resolve(transaction, _args, _context, _info) { - const status = transaction.status; - if (status?.__typename === 'SqueezedOutStatus') return null; - const time = status?.time ?? null; - const date = tai64toDate(time); - return { - fromNow: date.fromNow(), - full: date.format('DD MMM YYYY - HH:mm:ss A'), - rawTai64: time.toString(), - rawUnix: date.unix().toString(), - }; - }, - }, - blockHeight: { - resolve(transaction, _args, _context, _info) { - const status = transaction.status; - if (status?.__typename === 'SuccessStatus') { - return status?.block?.header?.daHeight ?? null; - } - return null; - }, - }, - statusType: { - resolve(transaction, _args, _context, _info) { - const typename = transaction.status?.__typename; - if (typename === 'SuccessStatus') { - return 'Success'; - } - if (typename === 'FailureStatus') { - return 'Failure'; - } - return 'Submitted'; - }, - }, - totalAssets: { - resolve(transaction, _args, _context, _info) { - if (transaction.isMint) return 1; - return transaction.inputAssetIds?.length ?? 0; - }, - }, - totalOperations: { - resolve(transaction, _args, _context, _info) { - if (transaction.isMint) return 1; - return transaction.inputs?.length ?? 0; - }, - }, - totalAccounts: { - resolve(transaction, _args, _context, _info) { - if (transaction.isMint) return 1; - return getAccounts(transaction).length; - }, - }, - gasUsed: { - resolve(transaction, _args, _context, _info) { - const receipts = (transaction.receipts ?? []) as any[]; - const gasUsed = receipts.reduce((acc, receipt) => { - return acc.add(bn(receipt.gasUsed)); - }, bn(0)); - return gasUsed.toString(); - }, - }, - groupedInputs: { - resolve(transaction, _args, _context, _info) { - const inputs = transaction.inputs ?? []; - const assetsInputs = getAssetsInput(inputs); - const contractInputs = getContractInputs(inputs); - const messageInputs = getMessageInputs(inputs); - return [...assetsInputs, ...contractInputs, ...messageInputs]; - }, - }, - accountsInvolved: { - resolve(transaction, _args, _context, _info) { - if (transaction.isMint) return []; - return getAccounts(transaction) ?? []; - }, - }, -}; - -function getAssetsInput(inputs: TransactionItemFragment['inputs']) { - const assetsInputs = inputs?.filter( - (i) => i.__typename === 'InputCoin', - ) as InputCoin[]; - const entries = Object.entries(groupBy(assetsInputs, (i) => i.assetId)); - return entries.map(([assetId, inputs]) => { - const type = inputs[0].__typename; - const owner = inputs[0].owner; - const totalAmount = inputs.reduce( - (acc, input: any) => acc.add(bn(input.amount)), - bn(0), - ); - return { owner, assetId, type, totalAmount, inputs }; - }); -} - -function getContractInputs(inputs: TransactionItemFragment['inputs']) { - const contractInputs = inputs?.filter( - (i) => i.__typename === 'InputContract', - ) as InputContract[]; - const entries = Object.entries(groupBy(contractInputs, (i) => i.contract.id)); - return entries.map(([contractId, inputs]) => { - const type = inputs[0].__typename; - return { contractId, type, inputs }; - }); -} - -function getMessageInputs(inputs: TransactionItemFragment['inputs']) { - return inputs?.filter( - (i) => i.__typename === 'InputMessage', - ) as InputMessage[]; -} - -function getAccounts(transaction: TransactionItemFragment) { - const ids = transaction.inputs?.flatMap((input: any) => { - const typename = input?.__typename; - if (typename === 'InputCoin') { - return { - type: 'Contract', - id: input.owner, - }; - } - if (typename === 'InputMessage') { - return [ - { type: 'Wallet', id: input.sender }, - { type: 'Wallet', id: input.sender }, - ]; - } - if (typename === 'InputContract') { - return { - type: 'Contract', - id: input.contract.id, - }; - } - }); - - return uniqBy(ids, 'id'); -} diff --git a/packages/graphql/src/services/extender/resolvers/TransactionConnection.ts b/packages/graphql/src/services/extender/resolvers/TransactionConnection.ts deleted file mode 100644 index f44c50628..000000000 --- a/packages/graphql/src/services/extender/resolvers/TransactionConnection.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { IResolvers } from '@graphql-tools/utils'; - -import { getFieldsValues, removeDuplicates } from '../../../utils'; -import { delegateQueryAccounts } from '../delegators/QueryAccounts'; -import { delegateQueryTokens } from '../delegators/QueryTokens'; - -export const TransactionConnection: IResolvers = { - tokens: { - resolve(transactionConnection, args, context, info) { - const assetsId = removeDuplicates( - getFieldsValues(transactionConnection.nodes, ['assetId']), - ); - return delegateQueryTokens(assetsId, context, info); - }, - }, - accounts: { - resolve(transactionConnection, args, context, info) { - const assetsId = removeDuplicates( - getFieldsValues(transactionConnection.nodes, [ - 'to', - 'owner', - 'recipient', - 'sender', - 'toAddress', - ]), - ); - return delegateQueryAccounts(assetsId, context, info); - }, - }, -}; diff --git a/packages/graphql/src/services/extender/resolvers/index.ts b/packages/graphql/src/services/extender/resolvers/index.ts deleted file mode 100644 index 36f3ac9bd..000000000 --- a/packages/graphql/src/services/extender/resolvers/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './TransactionConnection'; -export * from './Transaction'; diff --git a/packages/graphql/src/services/metadata/index.ts b/packages/graphql/src/services/metadata/index.ts index e27a6e2f5..2f441e0d4 100644 --- a/packages/graphql/src/services/metadata/index.ts +++ b/packages/graphql/src/services/metadata/index.ts @@ -1 +1,16 @@ -export * from './schema'; +import { makeExecutableSchema } from '@graphql-tools/schema'; + +import { createAccountsResolver } from '../../domains/Account'; +import { createTokensResolver } from '../../domains/Token'; + +import typeDefs from './custom.graphql'; + +export const metadataSchema = makeExecutableSchema({ + typeDefs, + resolvers: { + Query: { + tokens: createTokensResolver, + accounts: createAccountsResolver, + }, + }, +}); diff --git a/packages/graphql/src/services/metadata/resolvers/Account/QueryAccounts.ts b/packages/graphql/src/services/metadata/resolvers/Account/QueryAccounts.ts deleted file mode 100644 index b01fb9fbc..000000000 --- a/packages/graphql/src/services/metadata/resolvers/Account/QueryAccounts.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import type { GraphQLFieldResolver } from 'graphql'; - -import accountsData from '../../data/accounts.json'; - -export const QueryAccounts: GraphQLFieldResolver< - any, - any, - { - addresses: Array; - } -> = (_, { addresses }) => { - return addresses - .map((addres) => - accountsData.find( - (account) => account.address.toLowerCase() === addres.toLowerCase(), - ), - ) - .filter((i) => !!i); -}; diff --git a/packages/graphql/src/services/metadata/resolvers/Token/QueryTokens.ts b/packages/graphql/src/services/metadata/resolvers/Token/QueryTokens.ts deleted file mode 100644 index 67f194c30..000000000 --- a/packages/graphql/src/services/metadata/resolvers/Token/QueryTokens.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import type { GraphQLFieldResolver } from 'graphql'; - -import tokensData from '../../data/tokens.json'; - -export const QueryTokens: GraphQLFieldResolver< - any, - any, - { - assetsId: Array; - } -> = (_, { assetsId }) => { - return assetsId - .map((id) => - tokensData.find( - (token) => token.assetId.toLowerCase() === id.toLowerCase(), - ), - ) - .filter((i) => !!i); -}; diff --git a/packages/graphql/src/services/metadata/resolvers/index.ts b/packages/graphql/src/services/metadata/resolvers/index.ts deleted file mode 100644 index bef7cb52c..000000000 --- a/packages/graphql/src/services/metadata/resolvers/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './Account/QueryAccounts'; -export * from './Token/QueryTokens'; diff --git a/packages/graphql/src/services/metadata/schema.ts b/packages/graphql/src/services/metadata/schema.ts deleted file mode 100644 index 8f9d11d40..000000000 --- a/packages/graphql/src/services/metadata/schema.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { makeExecutableSchema } from '@graphql-tools/schema'; - -import typeDefs from './custom.graphql'; -import { QueryTokens, QueryAccounts } from './resolvers'; - -export const metadataSchema = makeExecutableSchema({ - typeDefs, - resolvers: { - Query: { - tokens: QueryTokens, - accounts: QueryAccounts, - }, - }, -}); diff --git a/packages/graphql/tsconfig.json b/packages/graphql/tsconfig.json index eeea6dfbc..eb02c2fe4 100644 --- a/packages/graphql/tsconfig.json +++ b/packages/graphql/tsconfig.json @@ -1,9 +1,9 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { + "isolatedModules": true, "outDir": "./dist", - "module": "commonjs", - "lib": ["dom", "es6"] + "module": "commonjs" }, "include": ["src", "*.config.*"] } diff --git a/packages/graphql/tsup.config.mjs b/packages/graphql/tsup.config.mjs new file mode 100644 index 000000000..0924d045b --- /dev/null +++ b/packages/graphql/tsup.config.mjs @@ -0,0 +1,16 @@ +import graphqlLoaderPluginPkg from '@luckycatfactory/esbuild-graphql-loader'; + +import tsconfig from './tsconfig.json'; + +const graphqlLoaderPlugin = graphqlLoaderPluginPkg.default; +const defConfig = { + outDir: 'dist', + splitting: true, + format: ['cjs'], + sourcemap: true, + clean: true, + target: tsconfig.compilerOptions.target, + esbuildPlugins: [graphqlLoaderPlugin()], +}; + +export default [{ ...defConfig, entry: { index: 'src/bin.ts' } }]; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 97f8059b6..f10b39696 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -326,6 +326,9 @@ importers: '@graphql-tools/utils': specifier: ^10.0.6 version: 10.0.6(graphql@16.8.1) + '@luckycatfactory/esbuild-graphql-loader': + specifier: 3.8.1 + version: 3.8.1(esbuild@0.19.3)(graphql-tag@2.12.6)(graphql@16.8.1) cors: specifier: ^2.8.5 version: 2.8.5 @@ -335,11 +338,14 @@ importers: dotenv: specifier: 16.3.1 version: 16.3.1 + esbuild: + specifier: 0.19.3 + version: 0.19.3 express: specifier: ^4.18.2 version: 4.18.2 graphql: - specifier: '>=16.8.1' + specifier: 16.8.1 version: 16.8.1 graphql-http: specifier: ^1.22.0 @@ -2269,6 +2275,15 @@ packages: dev: true optional: true + /@esbuild/android-arm64@0.19.3: + resolution: {integrity: sha512-w+Akc0vv5leog550kjJV9Ru+MXMR2VuMrui3C61mnysim0gkFCPOUTAfzTP0qX+HpN9Syu3YA3p1hf3EPqObRw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: false + optional: true + /@esbuild/android-arm@0.18.20: resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} engines: {node: '>=12'} @@ -2278,6 +2293,15 @@ packages: dev: true optional: true + /@esbuild/android-arm@0.19.3: + resolution: {integrity: sha512-Lemgw4io4VZl9GHJmjiBGzQ7ONXRfRPHcUEerndjwiSkbxzrpq0Uggku5MxxrXdwJ+pTj1qyw4jwTu7hkPsgIA==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: false + optional: true + /@esbuild/android-x64@0.18.20: resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} engines: {node: '>=12'} @@ -2287,6 +2311,15 @@ packages: dev: true optional: true + /@esbuild/android-x64@0.19.3: + resolution: {integrity: sha512-FKQJKkK5MXcBHoNZMDNUAg1+WcZlV/cuXrWCoGF/TvdRiYS4znA0m5Il5idUwfxrE20bG/vU1Cr5e1AD6IEIjQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: false + optional: true + /@esbuild/darwin-arm64@0.18.20: resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} engines: {node: '>=12'} @@ -2296,6 +2329,15 @@ packages: dev: true optional: true + /@esbuild/darwin-arm64@0.19.3: + resolution: {integrity: sha512-kw7e3FXU+VsJSSSl2nMKvACYlwtvZB8RUIeVShIEY6PVnuZ3c9+L9lWB2nWeeKWNNYDdtL19foCQ0ZyUL7nqGw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + /@esbuild/darwin-x64@0.18.20: resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} engines: {node: '>=12'} @@ -2305,6 +2347,15 @@ packages: dev: true optional: true + /@esbuild/darwin-x64@0.19.3: + resolution: {integrity: sha512-tPfZiwF9rO0jW6Jh9ipi58N5ZLoSjdxXeSrAYypy4psA2Yl1dAMhM71KxVfmjZhJmxRjSnb29YlRXXhh3GqzYw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + /@esbuild/freebsd-arm64@0.18.20: resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} engines: {node: '>=12'} @@ -2314,6 +2365,15 @@ packages: dev: true optional: true + /@esbuild/freebsd-arm64@0.19.3: + resolution: {integrity: sha512-ERDyjOgYeKe0Vrlr1iLrqTByB026YLPzTytDTz1DRCYM+JI92Dw2dbpRHYmdqn6VBnQ9Bor6J8ZlNwdZdxjlSg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: false + optional: true + /@esbuild/freebsd-x64@0.18.20: resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} engines: {node: '>=12'} @@ -2323,6 +2383,15 @@ packages: dev: true optional: true + /@esbuild/freebsd-x64@0.19.3: + resolution: {integrity: sha512-nXesBZ2Ad1qL+Rm3crN7NmEVJ5uvfLFPLJev3x1j3feCQXfAhoYrojC681RhpdOph8NsvKBBwpYZHR7W0ifTTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: false + optional: true + /@esbuild/linux-arm64@0.18.20: resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} engines: {node: '>=12'} @@ -2332,6 +2401,15 @@ packages: dev: true optional: true + /@esbuild/linux-arm64@0.19.3: + resolution: {integrity: sha512-qXvYKmXj8GcJgWq3aGvxL/JG1ZM3UR272SdPU4QSTzD0eymrM7leiZH77pvY3UetCy0k1xuXZ+VPvoJNdtrsWQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + /@esbuild/linux-arm@0.18.20: resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} engines: {node: '>=12'} @@ -2341,6 +2419,15 @@ packages: dev: true optional: true + /@esbuild/linux-arm@0.19.3: + resolution: {integrity: sha512-zr48Cg/8zkzZCzDHNxXO/89bf9e+r4HtzNUPoz4GmgAkF1gFAFmfgOdCbR8zMbzFDGb1FqBBhdXUpcTQRYS1cQ==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: false + optional: true + /@esbuild/linux-ia32@0.18.20: resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} engines: {node: '>=12'} @@ -2350,6 +2437,15 @@ packages: dev: true optional: true + /@esbuild/linux-ia32@0.19.3: + resolution: {integrity: sha512-7XlCKCA0nWcbvYpusARWkFjRQNWNGlt45S+Q18UeS///K6Aw8bB2FKYe9mhVWy/XLShvCweOLZPrnMswIaDXQA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: false + optional: true + /@esbuild/linux-loong64@0.18.20: resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} engines: {node: '>=12'} @@ -2359,6 +2455,15 @@ packages: dev: true optional: true + /@esbuild/linux-loong64@0.19.3: + resolution: {integrity: sha512-qGTgjweER5xqweiWtUIDl9OKz338EQqCwbS9c2Bh5jgEH19xQ1yhgGPNesugmDFq+UUSDtWgZ264st26b3de8A==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: false + optional: true + /@esbuild/linux-mips64el@0.18.20: resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} engines: {node: '>=12'} @@ -2368,6 +2473,15 @@ packages: dev: true optional: true + /@esbuild/linux-mips64el@0.19.3: + resolution: {integrity: sha512-gy1bFskwEyxVMFRNYSvBauDIWNggD6pyxUksc0MV9UOBD138dKTzr8XnM2R4mBsHwVzeuIH8X5JhmNs2Pzrx+A==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: false + optional: true + /@esbuild/linux-ppc64@0.18.20: resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} engines: {node: '>=12'} @@ -2377,6 +2491,15 @@ packages: dev: true optional: true + /@esbuild/linux-ppc64@0.19.3: + resolution: {integrity: sha512-UrYLFu62x1MmmIe85rpR3qou92wB9lEXluwMB/STDzPF9k8mi/9UvNsG07Tt9AqwPQXluMQ6bZbTzYt01+Ue5g==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: false + optional: true + /@esbuild/linux-riscv64@0.18.20: resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} engines: {node: '>=12'} @@ -2386,6 +2509,15 @@ packages: dev: true optional: true + /@esbuild/linux-riscv64@0.19.3: + resolution: {integrity: sha512-9E73TfyMCbE+1AwFOg3glnzZ5fBAFK4aawssvuMgCRqCYzE0ylVxxzjEfut8xjmKkR320BEoMui4o/t9KA96gA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: false + optional: true + /@esbuild/linux-s390x@0.18.20: resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} engines: {node: '>=12'} @@ -2395,6 +2527,15 @@ packages: dev: true optional: true + /@esbuild/linux-s390x@0.19.3: + resolution: {integrity: sha512-LlmsbuBdm1/D66TJ3HW6URY8wO6IlYHf+ChOUz8SUAjVTuaisfuwCOAgcxo3Zsu3BZGxmI7yt//yGOxV+lHcEA==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: false + optional: true + /@esbuild/linux-x64@0.18.20: resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} engines: {node: '>=12'} @@ -2404,6 +2545,15 @@ packages: dev: true optional: true + /@esbuild/linux-x64@0.19.3: + resolution: {integrity: sha512-ogV0+GwEmvwg/8ZbsyfkYGaLACBQWDvO0Kkh8LKBGKj9Ru8VM39zssrnu9Sxn1wbapA2qNS6BiLdwJZGouyCwQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + /@esbuild/netbsd-x64@0.18.20: resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} engines: {node: '>=12'} @@ -2413,6 +2563,15 @@ packages: dev: true optional: true + /@esbuild/netbsd-x64@0.19.3: + resolution: {integrity: sha512-o1jLNe4uzQv2DKXMlmEzf66Wd8MoIhLNO2nlQBHLtWyh2MitDG7sMpfCO3NTcoTMuqHjfufgUQDFRI5C+xsXQw==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: false + optional: true + /@esbuild/openbsd-x64@0.18.20: resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} engines: {node: '>=12'} @@ -2422,6 +2581,15 @@ packages: dev: true optional: true + /@esbuild/openbsd-x64@0.19.3: + resolution: {integrity: sha512-AZJCnr5CZgZOdhouLcfRdnk9Zv6HbaBxjcyhq0StNcvAdVZJSKIdOiPB9az2zc06ywl0ePYJz60CjdKsQacp5Q==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: false + optional: true + /@esbuild/sunos-x64@0.18.20: resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} engines: {node: '>=12'} @@ -2431,6 +2599,15 @@ packages: dev: true optional: true + /@esbuild/sunos-x64@0.19.3: + resolution: {integrity: sha512-Acsujgeqg9InR4glTRvLKGZ+1HMtDm94ehTIHKhJjFpgVzZG9/pIcWW/HA/DoMfEyXmANLDuDZ2sNrWcjq1lxw==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: false + optional: true + /@esbuild/win32-arm64@0.18.20: resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} engines: {node: '>=12'} @@ -2440,6 +2617,15 @@ packages: dev: true optional: true + /@esbuild/win32-arm64@0.19.3: + resolution: {integrity: sha512-FSrAfjVVy7TifFgYgliiJOyYynhQmqgPj15pzLyJk8BUsnlWNwP/IAy6GAiB1LqtoivowRgidZsfpoYLZH586A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: false + optional: true + /@esbuild/win32-ia32@0.18.20: resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} engines: {node: '>=12'} @@ -2449,6 +2635,15 @@ packages: dev: true optional: true + /@esbuild/win32-ia32@0.19.3: + resolution: {integrity: sha512-xTScXYi12xLOWZ/sc5RBmMN99BcXp/eEf7scUC0oeiRoiT5Vvo9AycuqCp+xdpDyAU+LkrCqEpUS9fCSZF8J3Q==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: false + optional: true + /@esbuild/win32-x64@0.18.20: resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} engines: {node: '>=12'} @@ -2458,6 +2653,15 @@ packages: dev: true optional: true + /@esbuild/win32-x64@0.19.3: + resolution: {integrity: sha512-FbUN+0ZRXsypPyWE2IwIkVjDkDnJoMJARWOcFZn4KPPli+QnKqF0z1anvfaYe3ev5HFCpRDLLBDHyOALLppWHw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + /@eslint-community/eslint-utils@4.4.0(eslint@8.50.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -4230,6 +4434,18 @@ packages: /@juggle/resize-observer@3.4.0: resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==} + /@luckycatfactory/esbuild-graphql-loader@3.8.1(esbuild@0.19.3)(graphql-tag@2.12.6)(graphql@16.8.1): + resolution: {integrity: sha512-ovONIUSW6NAlCpiPMaVw4PpdFoO3Kqi8TGQ2hTtjKTQTdPpSOdekPI1ZRnwciTeUn0yCAQk7M2xdrbIZeTh6pw==} + peerDependencies: + esbuild: '>=0.8.26' + graphql: '>=16.8.1' + graphql-tag: ^2.11.0 + dependencies: + esbuild: 0.19.3 + graphql: 16.8.1 + graphql-tag: 2.12.6(graphql@16.8.1) + dev: false + /@mdx-js/react@2.3.0(react@18.2.0): resolution: {integrity: sha512-zQH//gdOmuu7nt2oJR29vFhDv88oGPmVw6BggmrHeMI+xgEkp1B2dX9/bMBSYtK0dyLX/aOmesKS09g222K1/g==} peerDependencies: @@ -12358,6 +12574,36 @@ packages: '@esbuild/win32-x64': 0.18.20 dev: true + /esbuild@0.19.3: + resolution: {integrity: sha512-UlJ1qUUA2jL2nNib1JTSkifQTcYTroFqRjwCFW4QYEKEsixXD5Tik9xML7zh2gTxkYTBKGHNH9y7txMwVyPbjw==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.19.3 + '@esbuild/android-arm64': 0.19.3 + '@esbuild/android-x64': 0.19.3 + '@esbuild/darwin-arm64': 0.19.3 + '@esbuild/darwin-x64': 0.19.3 + '@esbuild/freebsd-arm64': 0.19.3 + '@esbuild/freebsd-x64': 0.19.3 + '@esbuild/linux-arm': 0.19.3 + '@esbuild/linux-arm64': 0.19.3 + '@esbuild/linux-ia32': 0.19.3 + '@esbuild/linux-loong64': 0.19.3 + '@esbuild/linux-mips64el': 0.19.3 + '@esbuild/linux-ppc64': 0.19.3 + '@esbuild/linux-riscv64': 0.19.3 + '@esbuild/linux-s390x': 0.19.3 + '@esbuild/linux-x64': 0.19.3 + '@esbuild/netbsd-x64': 0.19.3 + '@esbuild/openbsd-x64': 0.19.3 + '@esbuild/sunos-x64': 0.19.3 + '@esbuild/win32-arm64': 0.19.3 + '@esbuild/win32-ia32': 0.19.3 + '@esbuild/win32-x64': 0.19.3 + dev: false + /escalade@3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} From 029433c5090c9d0abdb9dc375b411e3c0c575fb4 Mon Sep 17 00:00:00 2001 From: Pedro Nauck Date: Wed, 27 Sep 2023 19:21:19 -0300 Subject: [PATCH 02/15] chore: fix graphql package output --- packages/app/.storybook/main.ts | 13 + packages/app/package.json | 5 + packages/graphql/codegen.schema.ts | 11 +- packages/graphql/codegen.ts | 49 +- packages/graphql/package.json | 2 - packages/graphql/src/bin.ts | 4 +- packages/graphql/src/domains/Output.ts | 99 +++ packages/graphql/src/domains/Transaction.ts | 10 +- .../graphql/src/queries/tx-fragments.graphql | 7 + packages/graphql/src/schema.ts | 21 +- packages/graphql/src/schemas/fuelcore.graphql | 620 ++++++++++++++++++ .../graphql/src/schemas/fullschema.graphql | 21 + packages/graphql/src/server.ts | 7 +- .../src/services/extender/extender.graphql | 2 +- packages/graphql/tsconfig.build.json | 11 - packages/graphql/tsup.config.mjs | 5 +- pnpm-lock.yaml | 21 +- 17 files changed, 852 insertions(+), 56 deletions(-) create mode 100644 packages/graphql/src/domains/Output.ts create mode 100644 packages/graphql/src/schemas/fuelcore.graphql delete mode 100644 packages/graphql/tsconfig.build.json diff --git a/packages/app/.storybook/main.ts b/packages/app/.storybook/main.ts index c6bc1aa49..4cb9e24d0 100644 --- a/packages/app/.storybook/main.ts +++ b/packages/app/.storybook/main.ts @@ -20,6 +20,19 @@ const config: StorybookConfig = { check: false, reactDocgen: 'react-docgen', }, + webpack: (config) => { + let rules = config.module?.rules || []; + rules.push({ + test: /\.(graphql|gql)/, + exclude: /node_modules/, + loader: 'graphql-tag/loader', + }); + + return { + ...config, + module: { ...config.module, rules }, + }; + }, }; export default config; diff --git a/packages/app/package.json b/packages/app/package.json index 465b00cc3..2250b4ddf 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -73,5 +73,10 @@ "typescript": "5.2.2", "vite": "^4.4.9", "vite-tsconfig-paths": "^4.2.1" + }, + "browser": { + "fs": false, + "path": false, + "module": false } } diff --git a/packages/graphql/codegen.schema.ts b/packages/graphql/codegen.schema.ts index ee67f3970..1b50fcc3b 100644 --- a/packages/graphql/codegen.schema.ts +++ b/packages/graphql/codegen.schema.ts @@ -1,9 +1,18 @@ import type { CodegenConfig } from '@graphql-codegen/cli'; +import { config as configDotenv } from 'dotenv'; +configDotenv(); const config: CodegenConfig = { - schema: 'http://localhost:4444/graphql', generates: { './src/schemas/fullschema.graphql': { + schema: 'http://localhost:4444/graphql', + plugins: ['schema-ast'], + config: { + includeDirectives: true, + }, + }, + './src/schemas/fuelcore.graphql': { + schema: process.env.FUEL_PROVIDER_URL, plugins: ['schema-ast'], config: { includeDirectives: true, diff --git a/packages/graphql/codegen.ts b/packages/graphql/codegen.ts index 1d53c27c6..9fd5e0e6f 100644 --- a/packages/graphql/codegen.ts +++ b/packages/graphql/codegen.ts @@ -18,15 +18,48 @@ const config: CodegenConfig = { }, }, 'src/generated/mocks.ts': { - plugins: ['typescript-mock-data'], - config: { - addTypename: true, - typesFile: './types.ts', - typesNames: 'keep', - scalars: { - Tai64Timestamp: 'unix_time', + plugins: [ + { + 'typescript-mock-data': { + addTypename: true, + typesFile: './types.ts', + typesNames: 'keep', + generateLibrary: 'faker', + fieldGeneration: { + _all: { + totalAmount: { + generator: 'datatype.hexadecimal', + }, + }, + }, + scalars: { + Address: { + generator: 'random.alphaNumeric', + arguments: [40], + }, + AssetId: { + generator: 'random.alphaNumeric', + arguments: [32], + }, + ContractId: { + generator: 'random.alphaNumeric', + arguments: [32], + }, + TransactionId: { + generator: 'random.alphaNumeric', + arguments: [32], + }, + UtxoId: { + generator: 'random.alphaNumeric', + arguments: [32], + }, + U64: { + generator: 'datatype.hexadecimal', + }, + }, + }, }, - }, + ], }, }, hooks: { diff --git a/packages/graphql/package.json b/packages/graphql/package.json index cb9d20ed7..63bff89d0 100644 --- a/packages/graphql/package.json +++ b/packages/graphql/package.json @@ -21,10 +21,8 @@ "dependencies": { "@fuel-ts/math": "0.60.0", "@graphql-tools/delegate": "^10.0.3", - "@graphql-tools/load": "^8.0.0", "@graphql-tools/schema": "^10.0.0", "@graphql-tools/stitch": "^9.0.3", - "@graphql-tools/url-loader": "^8.0.0", "@graphql-tools/utils": "^10.0.6", "@luckycatfactory/esbuild-graphql-loader": "3.8.1", "cors": "^2.8.5", diff --git a/packages/graphql/src/bin.ts b/packages/graphql/src/bin.ts index f1fc08c74..7b7f24145 100644 --- a/packages/graphql/src/bin.ts +++ b/packages/graphql/src/bin.ts @@ -1,5 +1,5 @@ -import dotenv from 'dotenv'; -dotenv.config(); +import { config } from 'dotenv'; +config(); import app from './server'; diff --git a/packages/graphql/src/domains/Output.ts b/packages/graphql/src/domains/Output.ts new file mode 100644 index 000000000..535b339c0 --- /dev/null +++ b/packages/graphql/src/domains/Output.ts @@ -0,0 +1,99 @@ +import { bn } from '@fuel-ts/math'; +import { groupBy } from 'lodash'; + +import type { + ChangeOutput, + CoinOutput, + ContractCreated, + ContractOutput, + MessageOutput, + TransactionItemFragment, + VariableOutput, +} from '../generated/types'; + +type Outputs = TransactionItemFragment['outputs']; + +export class OutputDomain { + constructor(private outputs: Outputs) {} + + get groupedOutputs() { + return [ + ...this.coinOutputs, + ...this.contractOutputs, + ...this.messageOutputs, + ...this.changeOutputs, + ...this.variableOutputs, + ...this.contractCreatedOutputs, + ]; + } + + get coinOutputs() { + const outputs = this._filterByTypename('OutputCoin'); + const entries = Object.entries(groupBy(outputs, (i) => i.assetId)); + return entries.map(([assetId, outputs]) => { + const type = outputs[0].__typename; + const to = outputs[0].to; + const totalAmount = this._getTotalAmount(outputs); + return { to, assetId, type, totalAmount, outputs }; + }); + } + + get contractOutputs() { + const outputs = this._filterByTypename('ContractOutput'); + const entries = Object.entries(groupBy(outputs, (i) => i.inputIndex)); + return entries.map(([inputIndex, outputs]) => { + const type = outputs[0].__typename; + return { inputIndex, type, outputs }; + }); + } + + get messageOutputs() { + const outputs = this._filterByTypename('MessageOutput'); + const entries = Object.entries(groupBy(outputs, (i) => i.recipient)); + return entries.map(([recipient, outputs]) => { + const type = outputs[0].__typename; + const totalAmount = this._getTotalAmount(outputs); + return { recipient, type, outputs, totalAmount }; + }); + } + + get changeOutputs() { + const outputs = this._filterByTypename('ChangeOutput'); + const entries = Object.entries(groupBy(outputs, (i) => i.assetId)); + return entries.map(([assetId, outputs]) => { + const type = outputs[0].__typename; + const to = outputs[0].to; + const totalAmount = this._getTotalAmount(outputs); + return { to, assetId, type, outputs, totalAmount }; + }); + } + + get variableOutputs() { + const outputs = this._filterByTypename('VariableOutput'); + const entries = Object.entries(groupBy(outputs, (i) => i.assetId)); + return entries.map(([assetId, outputs]) => { + const type = outputs[0].__typename; + const to = outputs[0].to; + const totalAmount = this._getTotalAmount(outputs); + return { to, assetId, type, outputs, totalAmount }; + }); + } + + get contractCreatedOutputs() { + const outputs = this._filterByTypename('ContractCreated'); + const entries = Object.entries(groupBy(outputs, (i) => i.contract.id)); + return entries.map(([_, outputs]) => { + const type = outputs[0].__typename; + const contract = outputs[0].contract; + return { contract, type, outputs }; + }); + } + + private _filterByTypename(typename: string) { + return this.outputs?.filter((i) => i.__typename === typename) as T[]; + } + + private _getTotalAmount(inputs: T[]) { + return inputs.reduce((acc, input) => acc.add(bn(input.amount)), bn(0)); + } +} diff --git a/packages/graphql/src/domains/Transaction.ts b/packages/graphql/src/domains/Transaction.ts index 88072f8c1..b2170546d 100644 --- a/packages/graphql/src/domains/Transaction.ts +++ b/packages/graphql/src/domains/Transaction.ts @@ -5,6 +5,7 @@ import type { TransactionItemFragment } from '../generated/types'; import { tai64toDate } from '../utils/dayjs'; import { InputDomain } from './Input'; +import { OutputDomain } from './Output'; export class TransactionDomain { constructor(private transaction: TransactionItemFragment) {} @@ -30,8 +31,9 @@ export class TransactionDomain { ...TransactionDomain.createResolver('totalOperations'), ...TransactionDomain.createResolver('totalAccounts'), ...TransactionDomain.createResolver('gasUsed'), - ...TransactionDomain.createResolver('groupedInputs'), ...TransactionDomain.createResolver('accountsInvolved'), + ...TransactionDomain.createResolver('groupedInputs'), + ...TransactionDomain.createResolver('groupedOutputs'), }; } @@ -117,6 +119,12 @@ export class TransactionDomain { return domain.groupedInputs; } + get groupedOutputs() { + const { transaction } = this; + const domain = new OutputDomain(transaction.outputs ?? []); + return domain.groupedOutputs; + } + private _getAccounts() { const { transaction } = this; // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/packages/graphql/src/queries/tx-fragments.graphql b/packages/graphql/src/queries/tx-fragments.graphql index deab8dccf..4a553999f 100644 --- a/packages/graphql/src/queries/tx-fragments.graphql +++ b/packages/graphql/src/queries/tx-fragments.graphql @@ -60,6 +60,13 @@ fragment TransactionOutput on Output { amount assetId } + ... on ContractOutput { + inputIndex + } + ... on MessageOutput { + recipient + amount + } ... on ChangeOutput { to amount diff --git a/packages/graphql/src/schema.ts b/packages/graphql/src/schema.ts index 34069ab20..9c40d778f 100644 --- a/packages/graphql/src/schema.ts +++ b/packages/graphql/src/schema.ts @@ -1,34 +1,23 @@ -import { loadSchema } from '@graphql-tools/load'; +import { makeExecutableSchema } from '@graphql-tools/schema'; import { stitchSchemas } from '@graphql-tools/stitch'; -import { UrlLoader } from '@graphql-tools/url-loader'; -import type { Application } from 'express'; -import { createHandler } from 'graphql-http/lib/use/express'; +import fuelSchema from './schemas/fuelcore.graphql'; import { ExtenderResolvers, ExtenderTypeDefs } from './services/extender'; import { metadataSchema } from './services/metadata'; import { createGraphqlFetch } from './utils'; -export async function createSchema(fuelCoreGraphql: string) { +export function createSchema(fuelCoreGraphql: string) { return stitchSchemas({ subschemas: [ { // TODO: delete this once we start using local database or indexer // Load remote schame from Fuel Core - schema: await loadSchema(fuelCoreGraphql, { - loaders: [new UrlLoader()], - }), + schema: makeExecutableSchema({ typeDefs: fuelSchema }), executor: createGraphqlFetch(fuelCoreGraphql), }, - { - schema: metadataSchema, - }, + { schema: metadataSchema }, ], typeDefs: ExtenderTypeDefs, resolvers: ExtenderResolvers, }); } - -export async function startGraphql(fuelCoreGraphql: string, app: Application) { - const schema = await createSchema(fuelCoreGraphql); - app.post('/graphql', createHandler({ schema })); -} diff --git a/packages/graphql/src/schemas/fuelcore.graphql b/packages/graphql/src/schemas/fuelcore.graphql new file mode 100644 index 000000000..7c9e93eb4 --- /dev/null +++ b/packages/graphql/src/schemas/fuelcore.graphql @@ -0,0 +1,620 @@ +schema { + query: Query + mutation: Mutation + subscription: Subscription +} + +scalar Address + +scalar AssetId + +type Balance { + amount: U64! + assetId: AssetId! + owner: Address! +} + +type BalanceConnection { + """A list of edges.""" + edges: [BalanceEdge!]! + """A list of nodes.""" + nodes: [Balance!]! + """Information to aid in pagination.""" + pageInfo: PageInfo! +} + +"""An edge in a connection.""" +type BalanceEdge { + """A cursor for use in pagination""" + cursor: String! + """The item at the end of the edge""" + node: Balance! +} + +input BalanceFilterInput { + """Filter coins based on the `owner` field""" + owner: Address! +} + +type Block { + consensus: Consensus! + header: Header! + id: BlockId! + transactions: [Transaction!]! +} + +type BlockConnection { + """A list of edges.""" + edges: [BlockEdge!]! + """A list of nodes.""" + nodes: [Block!]! + """Information to aid in pagination.""" + pageInfo: PageInfo! +} + +"""An edge in a connection.""" +type BlockEdge { + """A cursor for use in pagination""" + cursor: String! + """The item at the end of the edge""" + node: Block! +} + +scalar BlockId + +scalar Bytes32 + +type ChainInfo { + baseChainHeight: U64! + consensusParameters: ConsensusParameters! + latestBlock: Block! + name: String! + peerCount: Int! +} + +type ChangeOutput { + amount: U64! + assetId: AssetId! + to: Address! +} + +type Coin { + amount: U64! + assetId: AssetId! + blockCreated: U64! + maturity: U64! + owner: Address! + status: CoinStatus! + utxoId: UtxoId! +} + +type CoinConnection { + """A list of edges.""" + edges: [CoinEdge!]! + """A list of nodes.""" + nodes: [Coin!]! + """Information to aid in pagination.""" + pageInfo: PageInfo! +} + +"""An edge in a connection.""" +type CoinEdge { + """A cursor for use in pagination""" + cursor: String! + """The item at the end of the edge""" + node: Coin! +} + +input CoinFilterInput { + """Returns coins only with `asset_id`.""" + assetId: AssetId + """Returns coins owned by the `owner`.""" + owner: Address! +} + +type CoinOutput { + amount: U64! + assetId: AssetId! + to: Address! +} + +enum CoinStatus { + SPENT + UNSPENT +} + +union Consensus = Genesis | PoAConsensus + +type ConsensusParameters { + contractMaxSize: U64! + gasPerByte: U64! + gasPriceFactor: U64! + maxGasPerTx: U64! + maxInputs: U64! + maxMessageDataLength: U64! + maxOutputs: U64! + maxPredicateDataLength: U64! + maxPredicateLength: U64! + maxScriptDataLength: U64! + maxScriptLength: U64! + maxStorageSlots: U64! + maxWitnesses: U64! +} + +type Contract { + bytecode: HexString! + id: ContractId! + salt: Salt! +} + +type ContractBalance { + amount: U64! + assetId: AssetId! + contract: ContractId! +} + +type ContractBalanceConnection { + """A list of edges.""" + edges: [ContractBalanceEdge!]! + """A list of nodes.""" + nodes: [ContractBalance!]! + """Information to aid in pagination.""" + pageInfo: PageInfo! +} + +"""An edge in a connection.""" +type ContractBalanceEdge { + """A cursor for use in pagination""" + cursor: String! + """The item at the end of the edge""" + node: ContractBalance! +} + +input ContractBalanceFilterInput { + """Filter assets based on the `contractId` field""" + contract: ContractId! +} + +type ContractCreated { + contract: Contract! + stateRoot: Bytes32! +} + +scalar ContractId + +type ContractOutput { + balanceRoot: Bytes32! + inputIndex: Int! + stateRoot: Bytes32! +} + +input ExcludeInput { + """Messages to exclude from the selection.""" + messages: [MessageId!]! + """Utxos to exclude from the selection.""" + utxos: [UtxoId!]! +} + +type FailureStatus { + block: Block! + programState: ProgramState + reason: String! + time: Tai64Timestamp! +} + +type Genesis { + """ + The chain configs define what consensus type to use, what settlement layer to use, + rules of block validity, etc. + """ + chainConfigHash: Bytes32! + """The Binary Merkle Tree root of all genesis coins.""" + coinsRoot: Bytes32! + """ + The Binary Merkle Tree root of state, balances, contracts code hash of each contract. + """ + contractsRoot: Bytes32! + """The Binary Merkle Tree root of all genesis messages.""" + messagesRoot: Bytes32! +} + +type Header { + """Hash of the application header.""" + applicationHash: Bytes32! + """ + The layer 1 height of messages and events to include since the last layer 1 block number. + """ + daHeight: U64! + """Fuel block height.""" + height: U64! + """Hash of the header""" + id: BlockId! + """Number of output messages in this block.""" + outputMessagesCount: U64! + """Merkle root of messages in this block.""" + outputMessagesRoot: Bytes32! + """Merkle root of all previous block header hashes.""" + prevRoot: Bytes32! + """The block producer time.""" + time: Tai64Timestamp! + """Number of transactions in this block.""" + transactionsCount: U64! + """Merkle root of transactions.""" + transactionsRoot: Bytes32! +} + +scalar HexString + +union Input = InputCoin | InputContract | InputMessage + +type InputCoin { + amount: U64! + assetId: AssetId! + maturity: U64! + owner: Address! + predicate: HexString! + predicateData: HexString! + txPointer: TxPointer! + utxoId: UtxoId! + witnessIndex: Int! +} + +type InputContract { + balanceRoot: Bytes32! + contract: Contract! + stateRoot: Bytes32! + txPointer: TxPointer! + utxoId: UtxoId! +} + +type InputMessage { + amount: U64! + data: HexString! + messageId: MessageId! + nonce: U64! + predicate: HexString! + predicateData: HexString! + recipient: Address! + sender: Address! + witnessIndex: Int! +} + +type Message { + amount: U64! + daHeight: U64! + data: HexString! + messageId: MessageId! + nonce: U64! + recipient: Address! + sender: Address! + status: MessageStatus! +} + +type MessageConnection { + """A list of edges.""" + edges: [MessageEdge!]! + """A list of nodes.""" + nodes: [Message!]! + """Information to aid in pagination.""" + pageInfo: PageInfo! +} + +"""An edge in a connection.""" +type MessageEdge { + """A cursor for use in pagination""" + cursor: String! + """The item at the end of the edge""" + node: Message! +} + +scalar MessageId + +type MessageOutput { + amount: U64! + recipient: Address! +} + +type MessageProof { + amount: U64! + data: HexString! + header: Header! + nonce: Bytes32! + proofIndex: U64! + proofSet: [Bytes32!]! + recipient: Address! + sender: Address! + signature: Signature! +} + +enum MessageStatus { + SPENT + UNSPENT +} + +type Mutation { + """ + Execute a dry-run of the transaction using a fork of current state, no changes are committed. + """ + dryRun(tx: HexString!, utxoValidation: Boolean): [Receipt!]! + produceBlocks(blocksToProduce: U64!, time: TimeParameters): U64! + """Submits transaction to the txpool""" + submit(tx: HexString!): Transaction! +} + +type NodeInfo { + maxDepth: U64! + maxTx: U64! + minGasPrice: U64! + nodeVersion: String! + utxoValidation: Boolean! + vmBacktrace: Boolean! +} + +union Output = ChangeOutput | CoinOutput | ContractCreated | ContractOutput | MessageOutput | VariableOutput + +"""Information about pagination in a connection""" +type PageInfo { + """When paginating forwards, the cursor to continue.""" + endCursor: String + """When paginating forwards, are there more items?""" + hasNextPage: Boolean! + """When paginating backwards, are there more items?""" + hasPreviousPage: Boolean! + """When paginating backwards, the cursor to continue.""" + startCursor: String +} + +type PoAConsensus { + """Gets the signature of the block produced by `PoA` consensus.""" + signature: Signature! +} + +type ProgramState { + data: HexString! + returnType: ReturnType! +} + +type Query { + balance( + """asset_id of the coin""" + assetId: AssetId! + """address of the owner""" + owner: Address! + ): Balance! + balances(after: String, before: String, filter: BalanceFilterInput!, first: Int, last: Int): BalanceConnection! + block( + """Height of the block""" + height: U64 + """ID of the block""" + id: BlockId + ): Block + blocks(after: String, before: String, first: Int, last: Int): BlockConnection! + chain: ChainInfo! + """Gets the coin by `utxo_id`.""" + coin( + """The ID of the coin""" + utxoId: UtxoId! + ): Coin + """ + Gets all coins of some `owner` maybe filtered with by `asset_id` per page. + It includes `CoinStatus::Spent` and `CoinStatus::Unspent` coins. + """ + coins(after: String, before: String, filter: CoinFilterInput!, first: Int, last: Int): CoinConnection! + contract( + """ID of the Contract""" + id: ContractId! + ): Contract + contractBalance(asset: AssetId!, contract: ContractId!): ContractBalance! + contractBalances(after: String, before: String, filter: ContractBalanceFilterInput!, first: Int, last: Int): ContractBalanceConnection! + """Returns true when the GraphQL API is serving requests.""" + health: Boolean! + messageProof(messageId: MessageId!, transactionId: TransactionId!): MessageProof + messages( + after: String + before: String + first: Int + last: Int + """address of the owner""" + owner: Address + ): MessageConnection! + nodeInfo: NodeInfo! + """ + For each `query_per_asset`, get some spendable resources(of asset specified by the query) owned by + `owner` that add up at least the query amount. The returned resources are actual resources + that can be spent. The number of resources is optimized to prevent dust accumulation. + Max number of resources and excluded resources can also be specified. + + Returns: + The list of spendable resources per asset from the query. The length of the result is + the same as the length of `query_per_asset`. The ordering of assets and `query_per_asset` + is the same. + """ + resourcesToSpend( + """The excluded resources from the selection.""" + excludedIds: ExcludeInput + """The `Address` of the resources owner.""" + owner: Address! + """ + The list of requested assets` resources with asset ids, `target` amount the user wants to reach, and the `max` number of resources in the selection. Several entries with the same asset id are not allowed. + """ + queryPerAsset: [SpendQueryElementInput!]! + ): [[Resource!]!]! + transaction( + """The ID of the transaction""" + id: TransactionId! + ): Transaction + transactions(after: String, before: String, first: Int, last: Int): TransactionConnection! + transactionsByOwner(after: String, before: String, first: Int, last: Int, owner: Address!): TransactionConnection! +} + +type Receipt { + amount: U64 + assetId: AssetId + contract: Contract + contractId: ContractId + data: HexString + digest: Bytes32 + gas: U64 + gasUsed: U64 + is: U64 + len: U64 + messageId: MessageId + nonce: Bytes32 + param1: U64 + param2: U64 + pc: U64 + ptr: U64 + ra: U64 + rawPayload: HexString! + rb: U64 + rc: U64 + rd: U64 + reason: U64 + receiptType: ReceiptType! + recipient: Address + result: U64 + sender: Address + to: Contract + toAddress: Address + val: U64 +} + +enum ReceiptType { + CALL + LOG + LOG_DATA + MESSAGE_OUT + PANIC + RETURN + RETURN_DATA + REVERT + SCRIPT_RESULT + TRANSFER + TRANSFER_OUT +} + +"""The schema analog of the [`resource::Resource`].""" +union Resource = Coin | Message + +enum ReturnType { + RETURN + RETURN_DATA + REVERT +} + +scalar Salt + +scalar Signature + +input SpendQueryElementInput { + """Target amount for the query.""" + amount: U64! + """Identifier of the asset to spend.""" + assetId: AssetId! + """The maximum number of currencies for selection.""" + max: U64 +} + +type SqueezedOutStatus { + reason: String! +} + +type SubmittedStatus { + time: Tai64Timestamp! +} + +type Subscription { + """ + Returns a stream of status updates for the given transaction id. + If the current status is [`TransactionStatus::Success`], [`TransactionStatus::SqueezedOut`] + or [`TransactionStatus::Failed`] the stream will return that and end immediately. + If the current status is [`TransactionStatus::Submitted`] this will be returned + and the stream will wait for a future update. + + This stream will wait forever so it's advised to use within a timeout. + + It is possible for the stream to miss an update if it is polled slower + then the updates arrive. In such a case the stream will close without + a status. If this occurs the stream can simply be restarted to return + the latest status. + """ + statusChange( + """The ID of the transaction""" + id: TransactionId! + ): TransactionStatus! +} + +type SuccessStatus { + block: Block! + programState: ProgramState + time: Tai64Timestamp! +} + +scalar Tai64Timestamp + +input TimeParameters { + """The time interval between subsequent blocks""" + blockTimeInterval: U64! + """The time to set on the first block""" + startTime: U64! +} + +type Transaction { + bytecodeLength: U64 + bytecodeWitnessIndex: Int + gasLimit: U64 + gasPrice: U64 + id: TransactionId! + inputAssetIds: [AssetId!] + inputContracts: [Contract!] + inputs: [Input!] + isCreate: Boolean! + isMint: Boolean! + isScript: Boolean! + maturity: U64 + outputs: [Output!]! + """Return the transaction bytes using canonical encoding""" + rawPayload: HexString! + receipts: [Receipt!] + receiptsRoot: Bytes32 + salt: Salt + script: HexString + scriptData: HexString + status: TransactionStatus + storageSlots: [HexString!] + txPointer: TxPointer + witnesses: [HexString!] +} + +type TransactionConnection { + """A list of edges.""" + edges: [TransactionEdge!]! + """A list of nodes.""" + nodes: [Transaction!]! + """Information to aid in pagination.""" + pageInfo: PageInfo! +} + +"""An edge in a connection.""" +type TransactionEdge { + """A cursor for use in pagination""" + cursor: String! + """The item at the end of the edge""" + node: Transaction! +} + +scalar TransactionId + +union TransactionStatus = FailureStatus | SqueezedOutStatus | SubmittedStatus | SuccessStatus + +scalar TxPointer + +scalar U64 + +scalar UtxoId + +type VariableOutput { + amount: U64! + assetId: AssetId! + to: Address! +} \ No newline at end of file diff --git a/packages/graphql/src/schemas/fullschema.graphql b/packages/graphql/src/schemas/fullschema.graphql index ae3880e10..17876aac8 100644 --- a/packages/graphql/src/schemas/fullschema.graphql +++ b/packages/graphql/src/schemas/fullschema.graphql @@ -242,6 +242,26 @@ type GroupedInput { type: GroupInputType } +type GroupedOutput { + assetId: AssetId + contract: Contract + inputIndex: Int + outputs: [Output] + recipient: Address + to: Address + totalAmount: U64 + type: GroupedOutputType +} + +enum GroupedOutputType { + ChangeOutput + CoinOutput + ContractCreated + ContractOutput + MessageOutput + VariableOutput +} + type Header { """Hash of the application header.""" applicationHash: Bytes32! @@ -611,6 +631,7 @@ type Transaction { gasPrice: U64 gasUsed: U64 groupedInputs: [GroupedInput] + groupedOutputs: [GroupedOutput] id: TransactionId! inputAssetIds: [AssetId!] inputContracts: [Contract!] diff --git a/packages/graphql/src/server.ts b/packages/graphql/src/server.ts index c2eb00205..15e611d9f 100644 --- a/packages/graphql/src/server.ts +++ b/packages/graphql/src/server.ts @@ -1,8 +1,9 @@ import cors from 'cors'; import express from 'express'; +import { createHandler } from 'graphql-http/lib/use/express'; import expressPlayground from 'graphql-playground-middleware-express'; -import { startGraphql } from './schema'; +import { createSchema } from './schema'; import { requireEnv } from './utils/requireEnv'; const { FUEL_PROVIDER_URL } = requireEnv(['FUEL_PROVIDER_URL']); @@ -23,7 +24,7 @@ app.get( }), ); -// Start graphql server -startGraphql(FUEL_PROVIDER_URL, app); +const schema = createSchema(FUEL_PROVIDER_URL); +app.post('/graphql', createHandler({ schema })); export default app; diff --git a/packages/graphql/src/services/extender/extender.graphql b/packages/graphql/src/services/extender/extender.graphql index 4e4e3f7f5..a11f9fe8c 100644 --- a/packages/graphql/src/services/extender/extender.graphql +++ b/packages/graphql/src/services/extender/extender.graphql @@ -43,7 +43,7 @@ type GroupedInput { } enum GroupedOutputType { - OutputCoin + CoinOutput ContractOutput MessageOutput ChangeOutput diff --git a/packages/graphql/tsconfig.build.json b/packages/graphql/tsconfig.build.json deleted file mode 100644 index 4490e84c8..000000000 --- a/packages/graphql/tsconfig.build.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "declaration": true, - "declarationDir": "dist", - "outDir": "./dist", - "module": "commonjs", - "lib": ["dom", "es6"] - }, - "include": ["src", "*.config.*"] -} diff --git a/packages/graphql/tsup.config.mjs b/packages/graphql/tsup.config.mjs index 0924d045b..f6b61cb5c 100644 --- a/packages/graphql/tsup.config.mjs +++ b/packages/graphql/tsup.config.mjs @@ -1,15 +1,12 @@ import graphqlLoaderPluginPkg from '@luckycatfactory/esbuild-graphql-loader'; -import tsconfig from './tsconfig.json'; - const graphqlLoaderPlugin = graphqlLoaderPluginPkg.default; const defConfig = { outDir: 'dist', splitting: true, - format: ['cjs'], + format: ['esm', 'cjs'], sourcemap: true, clean: true, - target: tsconfig.compilerOptions.target, esbuildPlugins: [graphqlLoaderPlugin()], }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f10b39696..c155faa3d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -311,18 +311,12 @@ importers: '@graphql-tools/delegate': specifier: ^10.0.3 version: 10.0.3(graphql@16.8.1) - '@graphql-tools/load': - specifier: ^8.0.0 - version: 8.0.0(graphql@16.8.1) '@graphql-tools/schema': specifier: ^10.0.0 version: 10.0.0(graphql@16.8.1) '@graphql-tools/stitch': specifier: ^9.0.3 version: 9.0.3(graphql@16.8.1) - '@graphql-tools/url-loader': - specifier: ^8.0.0 - version: 8.0.0(@types/node@20.7.0)(graphql@16.8.1) '@graphql-tools/utils': specifier: ^10.0.6 version: 10.0.6(graphql@16.8.1) @@ -345,7 +339,7 @@ importers: specifier: ^4.18.2 version: 4.18.2 graphql: - specifier: 16.8.1 + specifier: '>=16.8.1' version: 16.8.1 graphql-http: specifier: ^1.22.0 @@ -698,6 +692,7 @@ packages: node-fetch: 2.7.0 transitivePeerDependencies: - encoding + dev: true /@ariakit/core@0.3.2: resolution: {integrity: sha512-hTiYCMsezqOH1y1KNNbRv58oQvbxAZlbYPhFG0XbTHd+tJQkHgtpezLBbJc4U/YjnhA95EAS6Zfa2xwj+EEbKg==} @@ -3716,6 +3711,7 @@ packages: transitivePeerDependencies: - bufferutil - utf-8-validate + dev: true /@graphql-tools/executor-http@1.0.2(@types/node@20.7.0)(graphql@16.8.1): resolution: {integrity: sha512-JKTB4E3kdQM2/1NEcyrVPyQ8057ZVthCV5dFJiKktqY9IdmF00M8gupFcW3jlbM/Udn78ickeUBsUzA3EouqpA==} @@ -3733,6 +3729,7 @@ packages: value-or-promise: 1.0.12 transitivePeerDependencies: - '@types/node' + dev: true /@graphql-tools/executor-legacy-ws@1.0.3(graphql@16.8.1): resolution: {integrity: sha512-rr3IDeO9Dh+8u8KIro++5kzJJYPHkcrIAWzqXtN663nhInC85iW7Ko91yOYwf7ovBci/7s+4Rqe4ZRyca1LGjQ==} @@ -3749,6 +3746,7 @@ packages: transitivePeerDependencies: - bufferutil - utf-8-validate + dev: true /@graphql-tools/executor@1.2.0(graphql@16.8.1): resolution: {integrity: sha512-SKlIcMA71Dha5JnEWlw4XxcaJ+YupuXg0QCZgl2TOLFz4SkGCwU/geAsJvUJFwK2RbVLpQv/UMq67lOaBuwDtg==} @@ -3868,6 +3866,7 @@ packages: graphql: 16.8.1 p-limit: 3.1.0 tslib: 2.6.2 + dev: true /@graphql-tools/merge@9.0.0(graphql@16.8.1): resolution: {integrity: sha512-J7/xqjkGTTwOJmaJQJ2C+VDBDOWJL3lKrHJN4yMaRLAJH3PosB7GiPRaSDZdErs0+F77sH2MKs2haMMkywzx7Q==} @@ -4015,6 +4014,7 @@ packages: - bufferutil - encoding - utf-8-validate + dev: true /@graphql-tools/utils@10.0.6(graphql@16.8.1): resolution: {integrity: sha512-hZMjl/BbX10iagovakgf3IiqArx8TPsotq5pwBld37uIX1JiZoSbgbCIFol7u55bh32o6cfDEiiJgfAD5fbeyQ==} @@ -9728,6 +9728,7 @@ packages: resolution: {integrity: sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==} dependencies: '@types/node': 20.7.0 + dev: true /@types/yargs-parser@21.0.0: resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} @@ -13092,6 +13093,7 @@ packages: /extract-files@11.0.0: resolution: {integrity: sha512-FuoE1qtbJ4bBVvv94CC7s0oTnKUGvQs+Rjf1L2SJFfS+HTVVjhPFtehPdQ0JiGPqVNfSSZvL5yzHHQq2Z4WNhQ==} engines: {node: ^12.20 || >= 14.13} + dev: true /extract-files@9.0.0: resolution: {integrity: sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ==} @@ -13982,6 +13984,7 @@ packages: graphql: '>=16.8.1' dependencies: graphql: 16.8.1 + dev: true /graphql-yoga@4.0.4(graphql@16.8.1): resolution: {integrity: sha512-MvCLhFecYNIKuxAZisPjpIL9lxRYbpgPSNKENDO/8CV3oiFlsLJHZb5dp2sVAeLafXHeZ9TgkijLthUBc1+Jag==} @@ -14846,6 +14849,7 @@ packages: ws: '*' dependencies: ws: 8.14.1 + dev: true /isomorphic-ws@5.0.0(ws@8.14.2): resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==} @@ -14853,6 +14857,7 @@ packages: ws: '*' dependencies: ws: 8.14.2 + dev: true /isstream@0.1.2: resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==} @@ -16040,6 +16045,7 @@ packages: optional: true dependencies: '@types/node': 20.7.0 + dev: true /mersenne-twister@1.1.0: resolution: {integrity: sha512-mUYWsMKNrm4lfygPkL3OfGzOPTR2DBlTkBNHM//F6hGp8cLThY897crAlk3/Jo17LEOOjQUrNAx6DvgO77QJkA==} @@ -20277,6 +20283,7 @@ packages: optional: true utf-8-validate: optional: true + dev: true /ws@8.14.2: resolution: {integrity: sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==} From 70e7d22474df107f681d4d19d05b81da178dc8a4 Mon Sep 17 00:00:00 2001 From: Pedro Nauck Date: Wed, 27 Sep 2023 19:28:32 -0300 Subject: [PATCH 03/15] chore: improve mocks --- .../src/systems/Transaction/__mocks__/tx.ts | 36 +++++-------------- packages/graphql/codegen.ts | 4 +++ 2 files changed, 12 insertions(+), 28 deletions(-) diff --git a/packages/app/src/systems/Transaction/__mocks__/tx.ts b/packages/app/src/systems/Transaction/__mocks__/tx.ts index d9dc84885..0c772fb9c 100644 --- a/packages/app/src/systems/Transaction/__mocks__/tx.ts +++ b/packages/app/src/systems/Transaction/__mocks__/tx.ts @@ -1,7 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { faker } from '@faker-js/faker'; import { GroupInputType, mocks } from '@fuel-explorer/graphql'; -import { bn } from '@fuel-ts/math'; import { assets } from '@fuels/assets'; import { dayjs } from '~/systems/Core/utils/dayjs'; @@ -13,53 +11,36 @@ const status = mocks.aSuccessStatus({ }), }); -const genInput = (typename: any) => - mocks.anInputCoin({ - __typename: typename, - amount: bn(1), - utxoId: faker.string.uuid(), - }); +function input(typename: any) { + return mocks.anInputCoin({ __typename: typename }); +} export const GROUPED_INPUT_ASSET = mocks.aGroupedInput({ - __typename: 'GroupedInput', type: GroupInputType.InputCoin, - inputs: [genInput('InputCoin'), genInput('InputCoin'), genInput('InputCoin')], + inputs: [input('InputCoin'), input('InputCoin'), input('InputCoin')], assetId: assets[0].assetId, - totalAmount: bn(3), - owner: `0x${faker.string.alpha(32)}`, }); export const GROUPED_INPUT_ASSET_UNKNOWN = mocks.aGroupedInput({ - __typename: 'GroupedInput', type: GroupInputType.InputCoin, - inputs: [genInput('InputCoin'), genInput('InputCoin'), genInput('InputCoin')], - owner: `0x${faker.string.alpha(32)}`, - assetId: `0x${faker.string.alpha(32)}`, - totalAmount: bn(3), + inputs: [input('InputCoin'), input('InputCoin'), input('InputCoin')], }); export const GROUPED_INPUT_CONTRACT = mocks.aGroupedInput({ - __typename: 'GroupedInput', type: GroupInputType.InputContract, inputs: [ - genInput('InputContract'), - genInput('InputContract'), - genInput('InputContract'), + input('InputContract'), + input('InputContract'), + input('InputContract'), ], - contractId: assets[0].assetId, - totalAmount: bn(3), }); export const GROUPED_INPUT_MESSAGE = mocks.aGroupedInput({ __typename: 'GroupedInput', type: GroupInputType.InputMessage, - sender: `0x${faker.string.alpha(40)}`, - recipient: `0x${faker.string.alpha(40)}`, - data: `0x${faker.string.alpha(160)}`, }); export const TX_MOCK = mocks.aTransaction({ - id: '0x78d13f111bf301324f34f2a7eaffc546d39598d156af38e7c4ef9fe61ea2c46a', time: { __typename: 'ParsedTime', fromNow: date.fromNow(), @@ -68,7 +49,6 @@ export const TX_MOCK = mocks.aTransaction({ totalAccounts: 2, totalAssets: 3, totalOperations: 4, - gasUsed: bn(1), status, groupedInputs: [ GROUPED_INPUT_ASSET, diff --git a/packages/graphql/codegen.ts b/packages/graphql/codegen.ts index 9fd5e0e6f..b9615484b 100644 --- a/packages/graphql/codegen.ts +++ b/packages/graphql/codegen.ts @@ -56,6 +56,10 @@ const config: CodegenConfig = { U64: { generator: 'datatype.hexadecimal', }, + HexString: { + generator: 'datatype.hexadecimal', + arguments: [160], + }, }, }, }, From 54a1b8837ba79ffa18dcdca48c23158ea602d523 Mon Sep 17 00:00:00 2001 From: Pedro Nauck Date: Wed, 27 Sep 2023 20:30:13 -0300 Subject: [PATCH 04/15] feat: add output component --- .../src/systems/Transaction/__mocks__/tx.ts | 78 ++++++- .../component/TxOutput/TxOutput.stories.tsx | 75 +++++++ .../component/TxOutput/TxOutput.tsx | 207 ++++++++++++++++++ .../graphql/src/schemas/fullschema.graphql | 4 +- .../src/services/extender/extender.graphql | 4 +- 5 files changed, 352 insertions(+), 16 deletions(-) create mode 100644 packages/app/src/systems/Transaction/component/TxOutput/TxOutput.stories.tsx create mode 100644 packages/app/src/systems/Transaction/component/TxOutput/TxOutput.tsx diff --git a/packages/app/src/systems/Transaction/__mocks__/tx.ts b/packages/app/src/systems/Transaction/__mocks__/tx.ts index 0c772fb9c..f4ae252a1 100644 --- a/packages/app/src/systems/Transaction/__mocks__/tx.ts +++ b/packages/app/src/systems/Transaction/__mocks__/tx.ts @@ -1,5 +1,9 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { GroupInputType, mocks } from '@fuel-explorer/graphql'; +import { + GroupedInputType, + GroupedOutputType, + mocks, +} from '@fuel-explorer/graphql'; import { assets } from '@fuels/assets'; import { dayjs } from '~/systems/Core/utils/dayjs'; @@ -16,28 +20,71 @@ function input(typename: any) { } export const GROUPED_INPUT_ASSET = mocks.aGroupedInput({ - type: GroupInputType.InputCoin, - inputs: [input('InputCoin'), input('InputCoin'), input('InputCoin')], + type: GroupedInputType.InputCoin, assetId: assets[0].assetId, + inputs: [input('InputCoin'), input('InputCoin'), input('InputCoin')], }); export const GROUPED_INPUT_ASSET_UNKNOWN = mocks.aGroupedInput({ - type: GroupInputType.InputCoin, + type: GroupedInputType.InputCoin, inputs: [input('InputCoin'), input('InputCoin'), input('InputCoin')], }); export const GROUPED_INPUT_CONTRACT = mocks.aGroupedInput({ - type: GroupInputType.InputContract, - inputs: [ - input('InputContract'), - input('InputContract'), - input('InputContract'), - ], + type: GroupedInputType.InputContract, }); export const GROUPED_INPUT_MESSAGE = mocks.aGroupedInput({ - __typename: 'GroupedInput', - type: GroupInputType.InputMessage, + type: GroupedInputType.InputMessage, +}); + +function output(typename: any) { + return mocks.aCoinOutput({ __typename: typename }); +} + +export const GROUPED_OUTPUT_ASSET = mocks.aGroupedOutput({ + type: GroupedOutputType.CoinOutput, + outputs: [output('OutputCoin'), output('OutputCoin'), output('OutputCoin')], + assetId: assets[0].assetId, +}); + +export const GROUPED_OUTPUT_ASSET_UNKNOWN = mocks.aGroupedOutput({ + type: GroupedOutputType.CoinOutput, + outputs: [output('outputCoin'), output('outputCoin'), output('outputCoin')], +}); + +export const GROUPED_OUTPUT_VARIABLE_OUTPUT = mocks.aGroupedOutput({ + type: GroupedOutputType.VariableOutput, + outputs: [output('outputCoin'), output('outputCoin'), output('outputCoin')], + assetId: assets[0].assetId, +}); + +export const GROUPED_OUTPUT_VARIABLE_OUTPUT_UNKNOWN = mocks.aGroupedOutput({ + type: GroupedOutputType.VariableOutput, + outputs: [output('outputCoin'), output('outputCoin'), output('outputCoin')], +}); + +export const GROUPED_OUTPUT_CHANGE_OUTPUT = mocks.aGroupedOutput({ + type: GroupedOutputType.ChangeOutput, + outputs: [output('outputCoin'), output('outputCoin'), output('outputCoin')], + assetId: assets[0].assetId, +}); + +export const GROUPED_OUTPUT_CHANGE_OUTPUT_UNKNOWN = mocks.aGroupedOutput({ + type: GroupedOutputType.ChangeOutput, + outputs: [output('outputCoin'), output('outputCoin'), output('outputCoin')], +}); + +export const GROUPED_OUTPUT_CONTRACT_OUTPUT = mocks.aGroupedOutput({ + type: GroupedOutputType.ContractOutput, +}); + +export const GROUPED_OUTPUT_CONTRACT_CREATED = mocks.aGroupedOutput({ + type: GroupedOutputType.ContractCreated, +}); + +export const GROUPED_OUTPUT_MESSAGE = mocks.aGroupedOutput({ + type: GroupedOutputType.MessageOutput, }); export const TX_MOCK = mocks.aTransaction({ @@ -56,4 +103,11 @@ export const TX_MOCK = mocks.aTransaction({ GROUPED_INPUT_ASSET, GROUPED_INPUT_MESSAGE, ], + groupedOutputs: [ + GROUPED_OUTPUT_ASSET, + GROUPED_OUTPUT_ASSET_UNKNOWN, + GROUPED_OUTPUT_CONTRACT_OUTPUT, + GROUPED_OUTPUT_CONTRACT_CREATED, + GROUPED_OUTPUT_MESSAGE, + ], }); diff --git a/packages/app/src/systems/Transaction/component/TxOutput/TxOutput.stories.tsx b/packages/app/src/systems/Transaction/component/TxOutput/TxOutput.stories.tsx new file mode 100644 index 000000000..a164e176b --- /dev/null +++ b/packages/app/src/systems/Transaction/component/TxOutput/TxOutput.stories.tsx @@ -0,0 +1,75 @@ +import { VStack } from '@fuels/ui'; +import type { Meta, StoryObj } from '@storybook/react'; + +import { + GROUPED_OUTPUT_ASSET, + GROUPED_OUTPUT_ASSET_UNKNOWN, + GROUPED_OUTPUT_CHANGE_OUTPUT, + GROUPED_OUTPUT_CHANGE_OUTPUT_UNKNOWN, + GROUPED_OUTPUT_CONTRACT_CREATED, + GROUPED_OUTPUT_CONTRACT_OUTPUT, + GROUPED_OUTPUT_MESSAGE, + GROUPED_OUTPUT_VARIABLE_OUTPUT, + GROUPED_OUTPUT_VARIABLE_OUTPUT_UNKNOWN, +} from '../../__mocks__/tx'; + +import { TxOutput } from './TxOutput'; + +const meta: Meta = { + title: 'Transaction/TxOutput', + component: TxOutput, +}; + +export default meta; +type Story = StoryObj; + +export const Asset: Story = { + render: () => ( + + + + + ), +}; + +export const VariableOutput: Story = { + render: () => ( + + + + + ), +}; + +export const ChangeOutput: Story = { + render: () => ( + + + + + ), +}; + +export const ContractOutput: Story = { + render: () => ( + + ), +}; + +export const ContractCreated: Story = { + render: () => ( + + ), +}; + +export const Message: Story = { + render: () => ( + + ), +}; diff --git a/packages/app/src/systems/Transaction/component/TxOutput/TxOutput.tsx b/packages/app/src/systems/Transaction/component/TxOutput/TxOutput.tsx new file mode 100644 index 000000000..6248e4788 --- /dev/null +++ b/packages/app/src/systems/Transaction/component/TxOutput/TxOutput.tsx @@ -0,0 +1,207 @@ +import { GroupedOutputType } from '@fuel-explorer/graphql'; +import type { GroupedOutput } from '@fuel-explorer/graphql'; +import { assets, resolveIconPath } from '@fuels/assets'; +import { + Card, + Copyable, + HStack, + Text, + VStack, + createComponent, + cx, + shortAddress, +} from '@fuels/ui'; +import type { CardProps } from '@fuels/ui'; +import { bn } from 'fuels'; +import Image from 'next/image'; +import { useMemo } from 'react'; +import { tv } from 'tailwind-variants'; + +import { TxIcon } from '../TxIcon/TxIcon'; + +const ASSET_LIST = resolveIconPath('/assets', assets); +const ICON_SIZE = 36; + +export type TxOutputProps = CardProps & { + output: GroupedOutput; + title?: string; +}; + +const TxOutputCoin = createComponent({ + id: 'TxOutputCoin', + render: (_, { output, title, ...props }) => { + const classes = styles(); + const assetId = output.assetId; + const amount = output.totalAmount; + const asset = useMemo(() => { + const found = ASSET_LIST.find((asset) => asset.assetId === assetId); + return { + assetId, + name: found?.name ?? 'Unknown Asset', + symbol: found?.symbol ?? null, + icon: found?.icon ?? null, + }; + }, [assetId]); + + if (!asset) return null; + return ( + + + + {asset.icon ? ( + {asset.name} + ) : ( + + )} + + + {title || asset.name} + {asset.symbol && ( + + ({asset.symbol}) + + )} + + + To: {shortAddress(output.to)} + + + + + {amount && ( + + {bn(amount).format({ precision: 3 })} {asset.symbol} + + )} + + + + ); + }, +}); + +const TxOutputContract = createComponent({ + id: 'TxOutputContract', + render: (_, { output, ...props }) => { + const classes = styles(); + + return ( + + + + + + Contract Output + + Input Index: {output.inputIndex} + + + + + + ); + }, +}); + +const TxOutputContractCreated = createComponent({ + id: 'TxOutputContractCreated', + render: (_, { output, ...props }) => { + const classes = styles(); + const contractId = output.contract?.id as string; + + return ( + + + + + + Contract Created + + Id: {shortAddress(contractId)} + + + + + + ); + }, +}); + +const TxOutputMessage = createComponent({ + id: 'TxOutputMessage', + render: (_, { output, ...props }) => { + const classes = styles(); + const { recipient } = output; + + return ( + + + + + Message + + + From: + + {shortAddress(recipient)} + + + + To: + + {shortAddress(recipient)} + + + + + + + ); + }, +}); + +export function TxOutput({ output, ...props }: TxOutputProps) { + if (output.type === GroupedOutputType.CoinOutput) { + return ; + } + if (output.type === GroupedOutputType.VariableOutput) { + return ; + } + if (output.type === GroupedOutputType.ChangeOutput) { + return ; + } + if (output.type === GroupedOutputType.ContractOutput) { + return ; + } + if (output.type === GroupedOutputType.ContractCreated) { + return ; + } + if (output.type === GroupedOutputType.MessageOutput) { + return ; + } +} + +const styles = tv({ + slots: { + header: 'group flex flex-row gap-4 justify-between items-center', + icon: 'transition-transform group-data-[state=closed]:hover:rotate-180 group-data-[state=open]:rotate-180', + utxos: 'bg-gray-2 mx-4 py-3 px-4 rounded', + }, +}); diff --git a/packages/graphql/src/schemas/fullschema.graphql b/packages/graphql/src/schemas/fullschema.graphql index 17876aac8..480044211 100644 --- a/packages/graphql/src/schemas/fullschema.graphql +++ b/packages/graphql/src/schemas/fullschema.graphql @@ -224,7 +224,7 @@ type Genesis { messagesRoot: Bytes32! } -enum GroupInputType { +enum GroupedInputType { InputCoin InputContract InputMessage @@ -239,7 +239,7 @@ type GroupedInput { recipient: Address sender: Address totalAmount: U64 - type: GroupInputType + type: GroupedInputType } type GroupedOutput { diff --git a/packages/graphql/src/services/extender/extender.graphql b/packages/graphql/src/services/extender/extender.graphql index a11f9fe8c..a44f81918 100644 --- a/packages/graphql/src/services/extender/extender.graphql +++ b/packages/graphql/src/services/extender/extender.graphql @@ -24,14 +24,14 @@ type ParsedTime { rawUnix: String } -enum GroupInputType { +enum GroupedInputType { InputCoin InputContract InputMessage } type GroupedInput { - type: GroupInputType + type: GroupedInputType totalAmount: U64 inputs: [Input] contractId: ContractId From 3ee3d7fb88b2494c6c1471effb8fd7e638d8aefd Mon Sep 17 00:00:00 2001 From: Pedro Nauck Date: Wed, 27 Sep 2023 20:39:09 -0300 Subject: [PATCH 05/15] feat: add TxOutput component --- .../component/TxInput/TxInput.stories.tsx | 10 +++---- .../Transaction/component/__mocks__/tx.ts | 26 ------------------- .../Transaction/screens/TxScreen/TxScreen.tsx | 26 ++++++++++++++++++- .../graphql/src/queries/tx-fragments.graphql | 14 ++++++++++ .../graphql/src/schemas/fullschema.graphql | 12 ++++----- 5 files changed, 50 insertions(+), 38 deletions(-) delete mode 100644 packages/app/src/systems/Transaction/component/__mocks__/tx.ts diff --git a/packages/app/src/systems/Transaction/component/TxInput/TxInput.stories.tsx b/packages/app/src/systems/Transaction/component/TxInput/TxInput.stories.tsx index b18ffa992..3c5964ed0 100644 --- a/packages/app/src/systems/Transaction/component/TxInput/TxInput.stories.tsx +++ b/packages/app/src/systems/Transaction/component/TxInput/TxInput.stories.tsx @@ -1,3 +1,4 @@ +import { VStack } from '@fuels/ui'; import type { Meta, StoryObj } from '@storybook/react'; import { @@ -18,12 +19,11 @@ export default meta; type Story = StoryObj; export const Asset: Story = { - render: () => , -}; - -export const AssetUnknown: Story = { render: () => ( - + + + + ), }; diff --git a/packages/app/src/systems/Transaction/component/__mocks__/tx.ts b/packages/app/src/systems/Transaction/component/__mocks__/tx.ts deleted file mode 100644 index 47b0e298e..000000000 --- a/packages/app/src/systems/Transaction/component/__mocks__/tx.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { mocks } from '@fuel-explorer/graphql'; -import { bn } from '@fuel-ts/math'; -import { dayjs } from '~/systems/Core/utils/dayjs'; - -const status = mocks.aSuccessStatus({ - __typename: 'SuccessStatus', - block: mocks.aBlock({ - transactions: [], - }), -}); - -const date = dayjs().subtract(1, 'day'); - -export const TX_MOCK = mocks.aTransaction({ - id: '0x78d13f111bf301324f34f2a7eaffc546d39598d156af38e7c4ef9fe61ea2c46a', - time: { - __typename: 'ParsedTime', - fromNow: date.fromNow(), - full: dayjs().format('DD MMM YYYY - HH:mm:ss A'), - }, - totalAccounts: 2, - totalAssets: 3, - totalOperations: 4, - gasUsed: bn(1), - status, -}); diff --git a/packages/app/src/systems/Transaction/screens/TxScreen/TxScreen.tsx b/packages/app/src/systems/Transaction/screens/TxScreen/TxScreen.tsx index 90e06cdee..35e01e5e3 100644 --- a/packages/app/src/systems/Transaction/screens/TxScreen/TxScreen.tsx +++ b/packages/app/src/systems/Transaction/screens/TxScreen/TxScreen.tsx @@ -1,6 +1,10 @@ 'use client'; -import type { GroupedInput, Maybe } from '@fuel-explorer/graphql'; +import type { + GroupedInput, + GroupedOutput, + Maybe, +} from '@fuel-explorer/graphql'; import { bn } from '@fuel-ts/math'; import { Grid, Heading, VStack } from '@fuels/ui'; @@ -8,6 +12,7 @@ import { TxAccountItem } from '../../component/TxAccountItem/TxAccountItem'; import { TxAssetItem } from '../../component/TxAssetItem/TxAssetItem'; import { TxBreadcrumb } from '../../component/TxBreadcrumb/TxBreadcrumb'; import { TxInput } from '../../component/TxInput/TxInput'; +import { TxOutput } from '../../component/TxOutput/TxOutput'; import { TxSummary } from '../../component/TxSummary/TxSummary'; import type { TransactionNode, TxAccountType } from '../../types'; @@ -67,6 +72,17 @@ export function TxScreen({ transaction: tx }: TxScreenProps) { /> ))} + + + Outputs + + {tx.groupedOutputs?.map((output) => ( + + ))} + ); @@ -78,3 +94,11 @@ function getInputId(input?: Maybe) { if (input.type === 'InputContract') return input.contractId; return input.sender; } + +function getOutputId(output?: Maybe) { + if (!output) return 0; + if (output.type === 'ContractOutput') return output.inputIndex; + if (output.type === 'ContractCreated') return output.contract?.id ?? 0; + if (output.type === 'MessageOutput') return output.recipient; + return output.assetId; +} diff --git a/packages/graphql/src/queries/tx-fragments.graphql b/packages/graphql/src/queries/tx-fragments.graphql index 4a553999f..97e8ef543 100644 --- a/packages/graphql/src/queries/tx-fragments.graphql +++ b/packages/graphql/src/queries/tx-fragments.graphql @@ -160,4 +160,18 @@ fragment TransactionItem on Transaction { data owner } + groupedOutputs { + type + totalAmount + outputs { + ...TransactionOutput + } + contract { + id + } + to + assetId + inputIndex + recipient + } } diff --git a/packages/graphql/src/schemas/fullschema.graphql b/packages/graphql/src/schemas/fullschema.graphql index 480044211..3f1ffe0e2 100644 --- a/packages/graphql/src/schemas/fullschema.graphql +++ b/packages/graphql/src/schemas/fullschema.graphql @@ -224,12 +224,6 @@ type Genesis { messagesRoot: Bytes32! } -enum GroupedInputType { - InputCoin - InputContract - InputMessage -} - type GroupedInput { assetId: AssetId contractId: ContractId @@ -242,6 +236,12 @@ type GroupedInput { type: GroupedInputType } +enum GroupedInputType { + InputCoin + InputContract + InputMessage +} + type GroupedOutput { assetId: AssetId contract: Contract From 0c8f5709032b2eff67ec6ae576be1877f8d1ad6d Mon Sep 17 00:00:00 2001 From: Pedro Nauck Date: Wed, 27 Sep 2023 22:23:06 -0300 Subject: [PATCH 06/15] feat: add EmptyCard --- packages/app/.storybook/main.ts | 17 +- packages/app/next.config.mjs | 28 +- packages/app/package.json | 1 + packages/app/src/svg.d.ts | 5 + .../EmptyCard/EmptyCard.stories.tsx | 40 +++ .../Core/components/EmptyCard/EmptyCard.tsx | 65 ++++ .../Core/components/EmptyCard/empty.svg | 142 +++++++++ .../Transaction/screens/TxScreen/TxScreen.tsx | 105 +++++-- packages/graphql/src/domains/Output.ts | 2 +- .../graphql/src/queries/tx-fragments.graphql | 2 +- pnpm-lock.yaml | 280 +++++++++++++++++- 11 files changed, 644 insertions(+), 43 deletions(-) create mode 100644 packages/app/src/svg.d.ts create mode 100644 packages/app/src/systems/Core/components/EmptyCard/EmptyCard.stories.tsx create mode 100644 packages/app/src/systems/Core/components/EmptyCard/EmptyCard.tsx create mode 100644 packages/app/src/systems/Core/components/EmptyCard/empty.svg diff --git a/packages/app/.storybook/main.ts b/packages/app/.storybook/main.ts index 4cb9e24d0..16278c9d7 100644 --- a/packages/app/.storybook/main.ts +++ b/packages/app/.storybook/main.ts @@ -20,18 +20,21 @@ const config: StorybookConfig = { check: false, reactDocgen: 'react-docgen', }, - webpack: (config) => { - let rules = config.module?.rules || []; - rules.push({ + webpack: (config: any) => { + config.module.rules.push({ test: /\.(graphql|gql)/, exclude: /node_modules/, loader: 'graphql-tag/loader', }); - return { - ...config, - module: { ...config.module, rules }, - }; + config.module.rules.push({ + test: /\.svg$/i, + issuer: /\.[jt]sx?$/, + resourceQuery: { not: /url/ }, // exclude if *.svg?url + use: ['@svgr/webpack'], + }); + + return config; }, }; diff --git a/packages/app/next.config.mjs b/packages/app/next.config.mjs index 91f737a83..f98889b9e 100644 --- a/packages/app/next.config.mjs +++ b/packages/app/next.config.mjs @@ -61,13 +61,39 @@ const config = { encoding: 'commonjs encoding', module: 'commonjs module', }); - config.module.rules.push({ test: /\.(graphql|gql)/, exclude: /node_modules/, loader: 'graphql-tag/loader', }); + // Grab the existing rule that handles SVG imports + const fileLoaderRule = config.module.rules.find((rule) => + rule.test?.test?.('.svg'), + ); + config.module.rules.push( + { + ...fileLoaderRule, + test: /\.svg$/i, + resourceQuery: /url/, // *.svg?url + }, + { + test: /\.svg$/i, + issuer: /\.[jt]sx?$/, + resourceQuery: { not: /url/ }, // exclude if *.svg?url + use: [ + { + loader: '@svgr/webpack', + options: { + exportType: 'named', + }, + }, + ], + }, + ); + // Modify the file loader rule to ignore *.svg, since we have it handled now. + fileLoaderRule.exclude = /\.svg$/i; + return config; }, }; diff --git a/packages/app/package.json b/packages/app/package.json index 2250b4ddf..37ce570a7 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -54,6 +54,7 @@ "@storybook/react": "^7.4.5", "@storybook/testing-library": "^0.2.1", "@storybook/types": "^7.4.5", + "@svgr/webpack": "8.1.0", "@testing-library/dom": "9.3.3", "@testing-library/jest-dom": "6.1.3", "@types/node": "20.7.0", diff --git a/packages/app/src/svg.d.ts b/packages/app/src/svg.d.ts new file mode 100644 index 000000000..73674f001 --- /dev/null +++ b/packages/app/src/svg.d.ts @@ -0,0 +1,5 @@ +declare module '*.svg' { + export const ReactComponent: React.FunctionComponent< + React.SVGProps + >; +} diff --git a/packages/app/src/systems/Core/components/EmptyCard/EmptyCard.stories.tsx b/packages/app/src/systems/Core/components/EmptyCard/EmptyCard.stories.tsx new file mode 100644 index 000000000..7d775d0c7 --- /dev/null +++ b/packages/app/src/systems/Core/components/EmptyCard/EmptyCard.stories.tsx @@ -0,0 +1,40 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { EmptyCard } from './EmptyCard'; + +const meta: Meta = { + title: 'Core/EmptyCard', + component: EmptyCard, +}; + +export default meta; +type Story = StoryObj; + +export const Usage: Story = { + render: () => ( + + Empty Card + + Use this card when there is no data to show. + + + ), +}; + +export const JustTitle: Story = { + render: () => ( + + Empty Card + + ), +}; + +export const JustDescription: Story = { + render: () => ( + + + Use this card when there is no data to show. + + + ), +}; diff --git a/packages/app/src/systems/Core/components/EmptyCard/EmptyCard.tsx b/packages/app/src/systems/Core/components/EmptyCard/EmptyCard.tsx new file mode 100644 index 000000000..5127772d1 --- /dev/null +++ b/packages/app/src/systems/Core/components/EmptyCard/EmptyCard.tsx @@ -0,0 +1,65 @@ +import type { CardProps, HeadingProps, TextProps } from '@fuels/ui'; +import { Card, Heading, createComponent, Text, withNamespace } from '@fuels/ui'; +import { tv } from 'tailwind-variants'; + +import { ReactComponent as EmptySvg } from './empty.svg'; + +export type EmptyCardProps = CardProps; +export type EmptyCardTitleProps = HeadingProps; +export type EmptyCardDescriptionProps = TextProps; + +export const EmptyCardRoot = createComponent({ + id: 'EmptyCard', + render: (_, { children, className, ...props }) => { + const classes = styles({ className }); + return ( + + + {children} + + ); + }, +}); + +export const EmptyCardTitle = createComponent< + EmptyCardTitleProps, + typeof Card.Title +>({ + id: 'EmptyCardTitle', + render: (_, { className, ...props }) => { + const classes = styles({ className }); + return ; + }, +}); + +export const EmptyCardDescription = createComponent< + EmptyCardDescriptionProps, + typeof Text +>({ + id: 'EmptyCardDescription', + render: (_, { className, ...props }) => { + const classes = styles({ className }); + return ; + }, +}); + +export const EmptyCard = withNamespace(EmptyCardRoot, { + Title: EmptyCardTitle, + Description: EmptyCardDescription, +}); + +const styles = tv({ + slots: { + root: 'p-6 text-center flex flex-col items-center gap-0', + image: 'mb-6', + title: 'font-semibold text-heading', + description: 'text-sm text-secondary mt-2', + }, +}); diff --git a/packages/app/src/systems/Core/components/EmptyCard/empty.svg b/packages/app/src/systems/Core/components/EmptyCard/empty.svg new file mode 100644 index 000000000..57fd47c25 --- /dev/null +++ b/packages/app/src/systems/Core/components/EmptyCard/empty.svg @@ -0,0 +1,142 @@ + + + + diff --git a/packages/app/src/systems/Transaction/screens/TxScreen/TxScreen.tsx b/packages/app/src/systems/Transaction/screens/TxScreen/TxScreen.tsx index 35e01e5e3..b2069a11a 100644 --- a/packages/app/src/systems/Transaction/screens/TxScreen/TxScreen.tsx +++ b/packages/app/src/systems/Transaction/screens/TxScreen/TxScreen.tsx @@ -7,6 +7,7 @@ import type { } from '@fuel-explorer/graphql'; import { bn } from '@fuel-ts/math'; import { Grid, Heading, VStack } from '@fuels/ui'; +import { EmptyCard } from '~/systems/Core/components/EmptyCard/EmptyCard'; import { TxAccountItem } from '../../component/TxAccountItem/TxAccountItem'; import { TxAssetItem } from '../../component/TxAssetItem/TxAssetItem'; @@ -22,6 +23,10 @@ type TxScreenProps = { export function TxScreen({ transaction: tx }: TxScreenProps) { if (!tx) return null; + const hasInputs = tx.groupedInputs?.length ?? 0 > 0; + const hasOutputs = tx.groupedOutputs?.length ?? 0 > 0; + const hasAssets = tx.inputAssetIds?.length ?? 0 > 0; + const hasAccounts = tx.accountsInvolved?.length ?? 0 > 0; return ( @@ -34,54 +39,90 @@ export function TxScreen({ transaction: tx }: TxScreenProps) { Assets - - {tx.inputAssetIds?.map((assetId) => ( - - ))} - + {hasAssets ? ( + + {tx.inputAssetIds?.map((assetId) => ( + + ))} + + ) : ( + + No Assets + + This transaction does not have any assets. + + + )} Accounts - - {tx.accountsInvolved?.map((acc) => ( - - ))} - + {hasAccounts ? ( + + {tx.accountsInvolved?.map((acc) => ( + + ))} + + ) : ( + + No Accounts + + This transaction does not have any accounts. + + + )} Inputs - {tx.groupedInputs?.map((input) => ( - - ))} + {hasInputs ? ( + tx.groupedInputs?.map((input) => ( + + )) + ) : ( + + No Inputs + + This transaction does not have any inputs. + + + )} Outputs - {tx.groupedOutputs?.map((output) => ( - - ))} + {hasOutputs ? ( + tx.groupedOutputs?.map((output) => ( + + )) + ) : ( + + No Outputs + + This transaction does not have any outputs. + + + )} diff --git a/packages/graphql/src/domains/Output.ts b/packages/graphql/src/domains/Output.ts index 535b339c0..821c46db3 100644 --- a/packages/graphql/src/domains/Output.ts +++ b/packages/graphql/src/domains/Output.ts @@ -28,7 +28,7 @@ export class OutputDomain { } get coinOutputs() { - const outputs = this._filterByTypename('OutputCoin'); + const outputs = this._filterByTypename('CoinOutput'); const entries = Object.entries(groupBy(outputs, (i) => i.assetId)); return entries.map(([assetId, outputs]) => { const type = outputs[0].__typename; diff --git a/packages/graphql/src/queries/tx-fragments.graphql b/packages/graphql/src/queries/tx-fragments.graphql index 97e8ef543..d624aca8b 100644 --- a/packages/graphql/src/queries/tx-fragments.graphql +++ b/packages/graphql/src/queries/tx-fragments.graphql @@ -161,6 +161,7 @@ fragment TransactionItem on Transaction { owner } groupedOutputs { + to type totalAmount outputs { @@ -169,7 +170,6 @@ fragment TransactionItem on Transaction { contract { id } - to assetId inputIndex recipient diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c155faa3d..1de7e0610 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -245,6 +245,9 @@ importers: '@storybook/types': specifier: ^7.4.5 version: 7.4.5 + '@svgr/webpack': + specifier: 8.1.0 + version: 8.1.0(typescript@5.2.2) '@testing-library/dom': specifier: 9.3.3 version: 9.3.3 @@ -296,6 +299,9 @@ importers: typescript: specifier: 5.2.2 version: 5.2.2 + url-loader: + specifier: 4.1.1 + version: 4.1.1 vite: specifier: ^4.4.9 version: 4.4.9(@types/node@20.7.0) @@ -1736,6 +1742,16 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true + /@babel/plugin-transform-react-constant-elements@7.22.5(@babel/core@7.23.0): + resolution: {integrity: sha512-BF5SXoO+nX3h5OhlN78XbbDrBOffv+AxPP2ENaJOVqjWCgBDeOY3WcaUcddutGSfoap+5NEQ/q/4I3WZIvgkXA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.0 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + /@babel/plugin-transform-react-display-name@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-PVk3WPYudRF5z4GKMEYUrLjPl38fJSKNaEOkFuoprioowGuWN6w2RKznuFNSlJx7pzzXXStPUnNSOEO0jL5EVw==} engines: {node: '>=6.9.0'} @@ -9102,6 +9118,163 @@ packages: '@types/express': 4.17.18 file-system-cache: 2.3.0 + /@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.23.0): + resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.0 + dev: true + + /@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.23.0): + resolution: {integrity: sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.0 + dev: true + + /@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.23.0): + resolution: {integrity: sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.0 + dev: true + + /@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.23.0): + resolution: {integrity: sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.0 + dev: true + + /@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.23.0): + resolution: {integrity: sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.0 + dev: true + + /@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.23.0): + resolution: {integrity: sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.0 + dev: true + + /@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.23.0): + resolution: {integrity: sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.0 + dev: true + + /@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.23.0): + resolution: {integrity: sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==} + engines: {node: '>=12'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.0 + dev: true + + /@svgr/babel-preset@8.1.0(@babel/core@7.23.0): + resolution: {integrity: sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.0 + '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.23.0) + '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.23.0) + '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.23.0) + '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.23.0) + '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.23.0) + '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.23.0) + '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.23.0) + '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.23.0) + dev: true + + /@svgr/core@8.1.0(typescript@5.2.2): + resolution: {integrity: sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==} + engines: {node: '>=14'} + dependencies: + '@babel/core': 7.23.0 + '@svgr/babel-preset': 8.1.0(@babel/core@7.23.0) + camelcase: 6.3.0 + cosmiconfig: 8.3.6(typescript@5.2.2) + snake-case: 3.0.4 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@svgr/hast-util-to-babel-ast@8.0.0: + resolution: {integrity: sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==} + engines: {node: '>=14'} + dependencies: + '@babel/types': 7.23.0 + entities: 4.5.0 + dev: true + + /@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0): + resolution: {integrity: sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==} + engines: {node: '>=14'} + peerDependencies: + '@svgr/core': '*' + dependencies: + '@babel/core': 7.23.0 + '@svgr/babel-preset': 8.1.0(@babel/core@7.23.0) + '@svgr/core': 8.1.0(typescript@5.2.2) + '@svgr/hast-util-to-babel-ast': 8.0.0 + svg-parser: 2.0.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@svgr/plugin-svgo@8.1.0(@svgr/core@8.1.0)(typescript@5.2.2): + resolution: {integrity: sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==} + engines: {node: '>=14'} + peerDependencies: + '@svgr/core': '*' + dependencies: + '@svgr/core': 8.1.0(typescript@5.2.2) + cosmiconfig: 8.3.6(typescript@5.2.2) + deepmerge: 4.3.1 + svgo: 3.0.2 + transitivePeerDependencies: + - typescript + dev: true + + /@svgr/webpack@8.1.0(typescript@5.2.2): + resolution: {integrity: sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==} + engines: {node: '>=14'} + dependencies: + '@babel/core': 7.23.0 + '@babel/plugin-transform-react-constant-elements': 7.22.5(@babel/core@7.23.0) + '@babel/preset-env': 7.22.20(@babel/core@7.23.0) + '@babel/preset-react': 7.22.15(@babel/core@7.23.0) + '@babel/preset-typescript': 7.23.0(@babel/core@7.23.0) + '@svgr/core': 8.1.0(typescript@5.2.2) + '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0) + '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0)(typescript@5.2.2) + transitivePeerDependencies: + - supports-color + - typescript + dev: true + /@swc/core-darwin-arm64@1.3.90: resolution: {integrity: sha512-he0w74HvcoufE6CZrB/U/VGVbc7021IQvYrn1geMACnq/OqMBqjdczNtdNfJAy87LZ4AOUjHDKEIjsZZu7o8nQ==} engines: {node: '>=10'} @@ -9378,6 +9551,11 @@ packages: engines: {node: '>= 10'} dev: false + /@trysound/sax@0.2.0: + resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} + engines: {node: '>=10.13.0'} + dev: true + /@tsconfig/node10@1.0.9: resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} @@ -11577,6 +11755,11 @@ packages: engines: {node: '>= 6'} dev: true + /commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + dev: true + /commander@8.3.0: resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} engines: {node: '>= 12'} @@ -11881,6 +12064,16 @@ packages: nth-check: 2.1.1 dev: true + /css-select@5.1.0: + resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 5.0.3 + domutils: 3.1.0 + nth-check: 2.1.1 + dev: true + /css-tree@1.1.3: resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} engines: {node: '>=8.0.0'} @@ -11889,6 +12082,22 @@ packages: source-map: 0.6.1 dev: false + /css-tree@2.2.1: + resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + dependencies: + mdn-data: 2.0.28 + source-map-js: 1.0.2 + dev: true + + /css-tree@2.3.1: + resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + dependencies: + mdn-data: 2.0.30 + source-map-js: 1.0.2 + dev: true + /css-what@6.1.0: resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} engines: {node: '>= 6'} @@ -11906,6 +12115,13 @@ packages: resolution: {integrity: sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==} dev: false + /csso@5.0.5: + resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + dependencies: + css-tree: 2.2.1 + dev: true + /cssom@0.3.8: resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==} dev: false @@ -12244,6 +12460,14 @@ packages: entities: 2.2.0 dev: true + /dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + dev: true + /domain-browser@4.22.0: resolution: {integrity: sha512-IGBwjF7tNk3cwypFNH/7bfzBcgSCbaMOD3GsaY1AU/JRrnHnYgEM0+9kQt52iZxjNsjBtJYtao146V+f8jFZNw==} engines: {node: '>=10'} @@ -12267,6 +12491,13 @@ packages: domelementtype: 2.3.0 dev: true + /domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + dependencies: + domelementtype: 2.3.0 + dev: true + /domutils@2.8.0: resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} dependencies: @@ -12275,6 +12506,14 @@ packages: domhandler: 4.3.1 dev: true + /domutils@3.1.0: + resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + dev: true + /dot-case@2.1.1: resolution: {integrity: sha512-HnM6ZlFqcajLsyudHq7LeeLDr2rFAVYtDv/hV5qchQEidSck8j9OPUsXY9KwJv/lHMtYlX4DjRQqwFYa+0r8Ug==} dependencies: @@ -12404,7 +12643,6 @@ packages: /entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} - dev: false /env-variable@0.0.6: resolution: {integrity: sha512-bHz59NlBbtS0NhftmR8+ExBEekE7br0e01jw+kk0NDro7TtZzBYZ5ScGPs3OmwnpyfHTHOtr1Y6uedCdrIldtg==} @@ -16004,6 +16242,14 @@ packages: resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} dev: false + /mdn-data@2.0.28: + resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} + dev: true + + /mdn-data@2.0.30: + resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + dev: true + /media-typer@0.3.0: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} @@ -18855,6 +19101,23 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + /svg-parser@2.0.4: + resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==} + dev: true + + /svgo@3.0.2: + resolution: {integrity: sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ==} + engines: {node: '>=14.0.0'} + hasBin: true + dependencies: + '@trysound/sax': 0.2.0 + commander: 7.2.0 + css-select: 5.1.0 + css-tree: 2.3.1 + csso: 5.0.5 + picocolors: 1.0.0 + dev: true + /swap-case@1.1.2: resolution: {integrity: sha512-BAmWG6/bx8syfc6qXPprof3Mn5vQgf5dwdUNJhsNqU9WdPt5P+ES/wQ5bxfijy8zwZgZZHslC3iAsxsuQMCzJQ==} dependencies: @@ -19759,6 +20022,21 @@ packages: dependencies: punycode: 2.3.0 + /url-loader@4.1.1: + resolution: {integrity: sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==} + engines: {node: '>= 10.13.0'} + peerDependencies: + file-loader: '*' + webpack: ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + file-loader: + optional: true + dependencies: + loader-utils: 3.2.1 + mime-types: 2.1.35 + schema-utils: 3.3.0 + dev: true + /url-parse@1.5.10: resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} dependencies: From d275e21b1f8083f280ea80d67ff92eddb4950dbe Mon Sep 17 00:00:00 2001 From: Pedro Nauck Date: Wed, 27 Sep 2023 22:27:52 -0300 Subject: [PATCH 07/15] chore: remove fuelcore schema from git --- packages/graphql/.gitignore | 1 + packages/graphql/codegen.fuel.ts | 16 + packages/graphql/codegen.schema.ts | 7 - packages/graphql/package.json | 3 +- packages/graphql/src/schemas/fuelcore.graphql | 620 ------------------ pnpm-lock.yaml | 18 - 6 files changed, 19 insertions(+), 646 deletions(-) create mode 100644 packages/graphql/codegen.fuel.ts delete mode 100644 packages/graphql/src/schemas/fuelcore.graphql diff --git a/packages/graphql/.gitignore b/packages/graphql/.gitignore index c83f90a2b..dffa62723 100644 --- a/packages/graphql/.gitignore +++ b/packages/graphql/.gitignore @@ -1 +1,2 @@ src/generated +src/schemas/fuelcore.graphql diff --git a/packages/graphql/codegen.fuel.ts b/packages/graphql/codegen.fuel.ts new file mode 100644 index 000000000..5f5891feb --- /dev/null +++ b/packages/graphql/codegen.fuel.ts @@ -0,0 +1,16 @@ +import type { CodegenConfig } from '@graphql-codegen/cli'; +import { config as configDotenv } from 'dotenv'; +configDotenv(); + +const config: CodegenConfig = { + generates: { + './src/schemas/fuelcore.graphql': { + schema: process.env.FUEL_PROVIDER_URL, + plugins: ['schema-ast'], + config: { + includeDirectives: true, + }, + }, + }, +}; +export default config; diff --git a/packages/graphql/codegen.schema.ts b/packages/graphql/codegen.schema.ts index 1b50fcc3b..59d309392 100644 --- a/packages/graphql/codegen.schema.ts +++ b/packages/graphql/codegen.schema.ts @@ -11,13 +11,6 @@ const config: CodegenConfig = { includeDirectives: true, }, }, - './src/schemas/fuelcore.graphql': { - schema: process.env.FUEL_PROVIDER_URL, - plugins: ['schema-ast'], - config: { - includeDirectives: true, - }, - }, }, }; export default config; diff --git a/packages/graphql/package.json b/packages/graphql/package.json index 63bff89d0..c0aa25cbc 100644 --- a/packages/graphql/package.json +++ b/packages/graphql/package.json @@ -14,9 +14,10 @@ "start": "pnpm dev", "codegen": "gql-gen --config codegen.ts", "codegen:schema": "gql-gen --config codegen.schema.ts", + "codegen:fuel": "gql-gen --config codegen.fuel.ts", "fix:generated": "node ./scripts/fix-generated.mjs", "ts:check": "tsc --noEmit", - "prepare": "pnpm codegen" + "prepare": "run-s codegen:fuel codegen build" }, "dependencies": { "@fuel-ts/math": "0.60.0", diff --git a/packages/graphql/src/schemas/fuelcore.graphql b/packages/graphql/src/schemas/fuelcore.graphql deleted file mode 100644 index 7c9e93eb4..000000000 --- a/packages/graphql/src/schemas/fuelcore.graphql +++ /dev/null @@ -1,620 +0,0 @@ -schema { - query: Query - mutation: Mutation - subscription: Subscription -} - -scalar Address - -scalar AssetId - -type Balance { - amount: U64! - assetId: AssetId! - owner: Address! -} - -type BalanceConnection { - """A list of edges.""" - edges: [BalanceEdge!]! - """A list of nodes.""" - nodes: [Balance!]! - """Information to aid in pagination.""" - pageInfo: PageInfo! -} - -"""An edge in a connection.""" -type BalanceEdge { - """A cursor for use in pagination""" - cursor: String! - """The item at the end of the edge""" - node: Balance! -} - -input BalanceFilterInput { - """Filter coins based on the `owner` field""" - owner: Address! -} - -type Block { - consensus: Consensus! - header: Header! - id: BlockId! - transactions: [Transaction!]! -} - -type BlockConnection { - """A list of edges.""" - edges: [BlockEdge!]! - """A list of nodes.""" - nodes: [Block!]! - """Information to aid in pagination.""" - pageInfo: PageInfo! -} - -"""An edge in a connection.""" -type BlockEdge { - """A cursor for use in pagination""" - cursor: String! - """The item at the end of the edge""" - node: Block! -} - -scalar BlockId - -scalar Bytes32 - -type ChainInfo { - baseChainHeight: U64! - consensusParameters: ConsensusParameters! - latestBlock: Block! - name: String! - peerCount: Int! -} - -type ChangeOutput { - amount: U64! - assetId: AssetId! - to: Address! -} - -type Coin { - amount: U64! - assetId: AssetId! - blockCreated: U64! - maturity: U64! - owner: Address! - status: CoinStatus! - utxoId: UtxoId! -} - -type CoinConnection { - """A list of edges.""" - edges: [CoinEdge!]! - """A list of nodes.""" - nodes: [Coin!]! - """Information to aid in pagination.""" - pageInfo: PageInfo! -} - -"""An edge in a connection.""" -type CoinEdge { - """A cursor for use in pagination""" - cursor: String! - """The item at the end of the edge""" - node: Coin! -} - -input CoinFilterInput { - """Returns coins only with `asset_id`.""" - assetId: AssetId - """Returns coins owned by the `owner`.""" - owner: Address! -} - -type CoinOutput { - amount: U64! - assetId: AssetId! - to: Address! -} - -enum CoinStatus { - SPENT - UNSPENT -} - -union Consensus = Genesis | PoAConsensus - -type ConsensusParameters { - contractMaxSize: U64! - gasPerByte: U64! - gasPriceFactor: U64! - maxGasPerTx: U64! - maxInputs: U64! - maxMessageDataLength: U64! - maxOutputs: U64! - maxPredicateDataLength: U64! - maxPredicateLength: U64! - maxScriptDataLength: U64! - maxScriptLength: U64! - maxStorageSlots: U64! - maxWitnesses: U64! -} - -type Contract { - bytecode: HexString! - id: ContractId! - salt: Salt! -} - -type ContractBalance { - amount: U64! - assetId: AssetId! - contract: ContractId! -} - -type ContractBalanceConnection { - """A list of edges.""" - edges: [ContractBalanceEdge!]! - """A list of nodes.""" - nodes: [ContractBalance!]! - """Information to aid in pagination.""" - pageInfo: PageInfo! -} - -"""An edge in a connection.""" -type ContractBalanceEdge { - """A cursor for use in pagination""" - cursor: String! - """The item at the end of the edge""" - node: ContractBalance! -} - -input ContractBalanceFilterInput { - """Filter assets based on the `contractId` field""" - contract: ContractId! -} - -type ContractCreated { - contract: Contract! - stateRoot: Bytes32! -} - -scalar ContractId - -type ContractOutput { - balanceRoot: Bytes32! - inputIndex: Int! - stateRoot: Bytes32! -} - -input ExcludeInput { - """Messages to exclude from the selection.""" - messages: [MessageId!]! - """Utxos to exclude from the selection.""" - utxos: [UtxoId!]! -} - -type FailureStatus { - block: Block! - programState: ProgramState - reason: String! - time: Tai64Timestamp! -} - -type Genesis { - """ - The chain configs define what consensus type to use, what settlement layer to use, - rules of block validity, etc. - """ - chainConfigHash: Bytes32! - """The Binary Merkle Tree root of all genesis coins.""" - coinsRoot: Bytes32! - """ - The Binary Merkle Tree root of state, balances, contracts code hash of each contract. - """ - contractsRoot: Bytes32! - """The Binary Merkle Tree root of all genesis messages.""" - messagesRoot: Bytes32! -} - -type Header { - """Hash of the application header.""" - applicationHash: Bytes32! - """ - The layer 1 height of messages and events to include since the last layer 1 block number. - """ - daHeight: U64! - """Fuel block height.""" - height: U64! - """Hash of the header""" - id: BlockId! - """Number of output messages in this block.""" - outputMessagesCount: U64! - """Merkle root of messages in this block.""" - outputMessagesRoot: Bytes32! - """Merkle root of all previous block header hashes.""" - prevRoot: Bytes32! - """The block producer time.""" - time: Tai64Timestamp! - """Number of transactions in this block.""" - transactionsCount: U64! - """Merkle root of transactions.""" - transactionsRoot: Bytes32! -} - -scalar HexString - -union Input = InputCoin | InputContract | InputMessage - -type InputCoin { - amount: U64! - assetId: AssetId! - maturity: U64! - owner: Address! - predicate: HexString! - predicateData: HexString! - txPointer: TxPointer! - utxoId: UtxoId! - witnessIndex: Int! -} - -type InputContract { - balanceRoot: Bytes32! - contract: Contract! - stateRoot: Bytes32! - txPointer: TxPointer! - utxoId: UtxoId! -} - -type InputMessage { - amount: U64! - data: HexString! - messageId: MessageId! - nonce: U64! - predicate: HexString! - predicateData: HexString! - recipient: Address! - sender: Address! - witnessIndex: Int! -} - -type Message { - amount: U64! - daHeight: U64! - data: HexString! - messageId: MessageId! - nonce: U64! - recipient: Address! - sender: Address! - status: MessageStatus! -} - -type MessageConnection { - """A list of edges.""" - edges: [MessageEdge!]! - """A list of nodes.""" - nodes: [Message!]! - """Information to aid in pagination.""" - pageInfo: PageInfo! -} - -"""An edge in a connection.""" -type MessageEdge { - """A cursor for use in pagination""" - cursor: String! - """The item at the end of the edge""" - node: Message! -} - -scalar MessageId - -type MessageOutput { - amount: U64! - recipient: Address! -} - -type MessageProof { - amount: U64! - data: HexString! - header: Header! - nonce: Bytes32! - proofIndex: U64! - proofSet: [Bytes32!]! - recipient: Address! - sender: Address! - signature: Signature! -} - -enum MessageStatus { - SPENT - UNSPENT -} - -type Mutation { - """ - Execute a dry-run of the transaction using a fork of current state, no changes are committed. - """ - dryRun(tx: HexString!, utxoValidation: Boolean): [Receipt!]! - produceBlocks(blocksToProduce: U64!, time: TimeParameters): U64! - """Submits transaction to the txpool""" - submit(tx: HexString!): Transaction! -} - -type NodeInfo { - maxDepth: U64! - maxTx: U64! - minGasPrice: U64! - nodeVersion: String! - utxoValidation: Boolean! - vmBacktrace: Boolean! -} - -union Output = ChangeOutput | CoinOutput | ContractCreated | ContractOutput | MessageOutput | VariableOutput - -"""Information about pagination in a connection""" -type PageInfo { - """When paginating forwards, the cursor to continue.""" - endCursor: String - """When paginating forwards, are there more items?""" - hasNextPage: Boolean! - """When paginating backwards, are there more items?""" - hasPreviousPage: Boolean! - """When paginating backwards, the cursor to continue.""" - startCursor: String -} - -type PoAConsensus { - """Gets the signature of the block produced by `PoA` consensus.""" - signature: Signature! -} - -type ProgramState { - data: HexString! - returnType: ReturnType! -} - -type Query { - balance( - """asset_id of the coin""" - assetId: AssetId! - """address of the owner""" - owner: Address! - ): Balance! - balances(after: String, before: String, filter: BalanceFilterInput!, first: Int, last: Int): BalanceConnection! - block( - """Height of the block""" - height: U64 - """ID of the block""" - id: BlockId - ): Block - blocks(after: String, before: String, first: Int, last: Int): BlockConnection! - chain: ChainInfo! - """Gets the coin by `utxo_id`.""" - coin( - """The ID of the coin""" - utxoId: UtxoId! - ): Coin - """ - Gets all coins of some `owner` maybe filtered with by `asset_id` per page. - It includes `CoinStatus::Spent` and `CoinStatus::Unspent` coins. - """ - coins(after: String, before: String, filter: CoinFilterInput!, first: Int, last: Int): CoinConnection! - contract( - """ID of the Contract""" - id: ContractId! - ): Contract - contractBalance(asset: AssetId!, contract: ContractId!): ContractBalance! - contractBalances(after: String, before: String, filter: ContractBalanceFilterInput!, first: Int, last: Int): ContractBalanceConnection! - """Returns true when the GraphQL API is serving requests.""" - health: Boolean! - messageProof(messageId: MessageId!, transactionId: TransactionId!): MessageProof - messages( - after: String - before: String - first: Int - last: Int - """address of the owner""" - owner: Address - ): MessageConnection! - nodeInfo: NodeInfo! - """ - For each `query_per_asset`, get some spendable resources(of asset specified by the query) owned by - `owner` that add up at least the query amount. The returned resources are actual resources - that can be spent. The number of resources is optimized to prevent dust accumulation. - Max number of resources and excluded resources can also be specified. - - Returns: - The list of spendable resources per asset from the query. The length of the result is - the same as the length of `query_per_asset`. The ordering of assets and `query_per_asset` - is the same. - """ - resourcesToSpend( - """The excluded resources from the selection.""" - excludedIds: ExcludeInput - """The `Address` of the resources owner.""" - owner: Address! - """ - The list of requested assets` resources with asset ids, `target` amount the user wants to reach, and the `max` number of resources in the selection. Several entries with the same asset id are not allowed. - """ - queryPerAsset: [SpendQueryElementInput!]! - ): [[Resource!]!]! - transaction( - """The ID of the transaction""" - id: TransactionId! - ): Transaction - transactions(after: String, before: String, first: Int, last: Int): TransactionConnection! - transactionsByOwner(after: String, before: String, first: Int, last: Int, owner: Address!): TransactionConnection! -} - -type Receipt { - amount: U64 - assetId: AssetId - contract: Contract - contractId: ContractId - data: HexString - digest: Bytes32 - gas: U64 - gasUsed: U64 - is: U64 - len: U64 - messageId: MessageId - nonce: Bytes32 - param1: U64 - param2: U64 - pc: U64 - ptr: U64 - ra: U64 - rawPayload: HexString! - rb: U64 - rc: U64 - rd: U64 - reason: U64 - receiptType: ReceiptType! - recipient: Address - result: U64 - sender: Address - to: Contract - toAddress: Address - val: U64 -} - -enum ReceiptType { - CALL - LOG - LOG_DATA - MESSAGE_OUT - PANIC - RETURN - RETURN_DATA - REVERT - SCRIPT_RESULT - TRANSFER - TRANSFER_OUT -} - -"""The schema analog of the [`resource::Resource`].""" -union Resource = Coin | Message - -enum ReturnType { - RETURN - RETURN_DATA - REVERT -} - -scalar Salt - -scalar Signature - -input SpendQueryElementInput { - """Target amount for the query.""" - amount: U64! - """Identifier of the asset to spend.""" - assetId: AssetId! - """The maximum number of currencies for selection.""" - max: U64 -} - -type SqueezedOutStatus { - reason: String! -} - -type SubmittedStatus { - time: Tai64Timestamp! -} - -type Subscription { - """ - Returns a stream of status updates for the given transaction id. - If the current status is [`TransactionStatus::Success`], [`TransactionStatus::SqueezedOut`] - or [`TransactionStatus::Failed`] the stream will return that and end immediately. - If the current status is [`TransactionStatus::Submitted`] this will be returned - and the stream will wait for a future update. - - This stream will wait forever so it's advised to use within a timeout. - - It is possible for the stream to miss an update if it is polled slower - then the updates arrive. In such a case the stream will close without - a status. If this occurs the stream can simply be restarted to return - the latest status. - """ - statusChange( - """The ID of the transaction""" - id: TransactionId! - ): TransactionStatus! -} - -type SuccessStatus { - block: Block! - programState: ProgramState - time: Tai64Timestamp! -} - -scalar Tai64Timestamp - -input TimeParameters { - """The time interval between subsequent blocks""" - blockTimeInterval: U64! - """The time to set on the first block""" - startTime: U64! -} - -type Transaction { - bytecodeLength: U64 - bytecodeWitnessIndex: Int - gasLimit: U64 - gasPrice: U64 - id: TransactionId! - inputAssetIds: [AssetId!] - inputContracts: [Contract!] - inputs: [Input!] - isCreate: Boolean! - isMint: Boolean! - isScript: Boolean! - maturity: U64 - outputs: [Output!]! - """Return the transaction bytes using canonical encoding""" - rawPayload: HexString! - receipts: [Receipt!] - receiptsRoot: Bytes32 - salt: Salt - script: HexString - scriptData: HexString - status: TransactionStatus - storageSlots: [HexString!] - txPointer: TxPointer - witnesses: [HexString!] -} - -type TransactionConnection { - """A list of edges.""" - edges: [TransactionEdge!]! - """A list of nodes.""" - nodes: [Transaction!]! - """Information to aid in pagination.""" - pageInfo: PageInfo! -} - -"""An edge in a connection.""" -type TransactionEdge { - """A cursor for use in pagination""" - cursor: String! - """The item at the end of the edge""" - node: Transaction! -} - -scalar TransactionId - -union TransactionStatus = FailureStatus | SqueezedOutStatus | SubmittedStatus | SuccessStatus - -scalar TxPointer - -scalar U64 - -scalar UtxoId - -type VariableOutput { - amount: U64! - assetId: AssetId! - to: Address! -} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1de7e0610..0ce9f4bac 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -299,9 +299,6 @@ importers: typescript: specifier: 5.2.2 version: 5.2.2 - url-loader: - specifier: 4.1.1 - version: 4.1.1 vite: specifier: ^4.4.9 version: 4.4.9(@types/node@20.7.0) @@ -20022,21 +20019,6 @@ packages: dependencies: punycode: 2.3.0 - /url-loader@4.1.1: - resolution: {integrity: sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==} - engines: {node: '>= 10.13.0'} - peerDependencies: - file-loader: '*' - webpack: ^4.0.0 || ^5.0.0 - peerDependenciesMeta: - file-loader: - optional: true - dependencies: - loader-utils: 3.2.1 - mime-types: 2.1.35 - schema-utils: 3.3.0 - dev: true - /url-parse@1.5.10: resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} dependencies: From 808230a65979249f9e51f14119560935af468d5e Mon Sep 17 00:00:00 2001 From: Pedro Nauck Date: Wed, 27 Sep 2023 22:32:01 -0300 Subject: [PATCH 08/15] fix: build --- packages/graphql/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/graphql/package.json b/packages/graphql/package.json index c0aa25cbc..6b0ed168c 100644 --- a/packages/graphql/package.json +++ b/packages/graphql/package.json @@ -17,7 +17,7 @@ "codegen:fuel": "gql-gen --config codegen.fuel.ts", "fix:generated": "node ./scripts/fix-generated.mjs", "ts:check": "tsc --noEmit", - "prepare": "run-s codegen:fuel codegen build" + "prepare": "pnpm codegen:fuel && pnpm codegen && pnpm build" }, "dependencies": { "@fuel-ts/math": "0.60.0", From 089706964bcc1c01f549b28fd7f86c1efb137b13 Mon Sep 17 00:00:00 2001 From: Pedro Nauck Date: Wed, 27 Sep 2023 22:33:58 -0300 Subject: [PATCH 09/15] fix: build --- vercel.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vercel.json b/vercel.json index 7c6abc99a..78a13cd25 100644 --- a/vercel.json +++ b/vercel.json @@ -1,5 +1,5 @@ { - "buildCommand": "pnpm -w build:preview", + "buildCommand": "cd ../../ && pnpm build:preview", "github": { "silent": true } From 88b5c7c4358ef9cc06cfa6116b820a7cb29db63c Mon Sep 17 00:00:00 2001 From: Pedro Nauck Date: Wed, 27 Sep 2023 22:35:52 -0300 Subject: [PATCH 10/15] fix: build --- packages/graphql/package.json | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/graphql/package.json b/packages/graphql/package.json index 6b0ed168c..49950dee7 100644 --- a/packages/graphql/package.json +++ b/packages/graphql/package.json @@ -9,15 +9,16 @@ "types": "./src/index.ts", "typings": "./src/index.ts", "scripts": { - "build": "tsup --dts", - "dev": "pnpm build && node dist/index.js", + "build:lib": "tsup --dts", + "build": "run-s codegen:fuel codegen build:lib", + "dev": "pnpm build && node ./dist/index.js", "start": "pnpm dev", - "codegen": "gql-gen --config codegen.ts", - "codegen:schema": "gql-gen --config codegen.schema.ts", - "codegen:fuel": "gql-gen --config codegen.fuel.ts", + "codegen": "gql-gen --config ./codegen.ts", + "codegen:schema": "gql-gen --config ./codegen.schema.ts", + "codegen:fuel": "gql-gen --config ./codegen.fuel.ts", "fix:generated": "node ./scripts/fix-generated.mjs", "ts:check": "tsc --noEmit", - "prepare": "pnpm codegen:fuel && pnpm codegen && pnpm build" + "prepare": "pnpm build" }, "dependencies": { "@fuel-ts/math": "0.60.0", From 68e3497ec6dcc1db662056cd34792de0ebf464e7 Mon Sep 17 00:00:00 2001 From: Pedro Nauck Date: Wed, 27 Sep 2023 22:38:24 -0300 Subject: [PATCH 11/15] fix: build --- packages/graphql/.env.production | 2 ++ packages/graphql/package.json | 10 +++++----- vercel.json | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) create mode 100644 packages/graphql/.env.production diff --git a/packages/graphql/.env.production b/packages/graphql/.env.production new file mode 100644 index 000000000..6453c6456 --- /dev/null +++ b/packages/graphql/.env.production @@ -0,0 +1,2 @@ +FUEL_PROVIDER_URL=http://beta-3.fuel.network/graphql + diff --git a/packages/graphql/package.json b/packages/graphql/package.json index 49950dee7..da5675445 100644 --- a/packages/graphql/package.json +++ b/packages/graphql/package.json @@ -9,13 +9,13 @@ "types": "./src/index.ts", "typings": "./src/index.ts", "scripts": { - "build:lib": "tsup --dts", "build": "run-s codegen:fuel codegen build:lib", - "dev": "pnpm build && node ./dist/index.js", + "build:lib": "tsup --dts", + "dev": "pnpm build && node dist/index.js", "start": "pnpm dev", - "codegen": "gql-gen --config ./codegen.ts", - "codegen:schema": "gql-gen --config ./codegen.schema.ts", - "codegen:fuel": "gql-gen --config ./codegen.fuel.ts", + "codegen": "gql-gen --config codegen.ts", + "codegen:schema": "gql-gen --config codegen.schema.ts", + "codegen:fuel": "gql-gen --config codegen.fuel.ts", "fix:generated": "node ./scripts/fix-generated.mjs", "ts:check": "tsc --noEmit", "prepare": "pnpm build" diff --git a/vercel.json b/vercel.json index 78a13cd25..7c6abc99a 100644 --- a/vercel.json +++ b/vercel.json @@ -1,5 +1,5 @@ { - "buildCommand": "cd ../../ && pnpm build:preview", + "buildCommand": "pnpm -w build:preview", "github": { "silent": true } From d86636c05866ae0dd8e39964f263a42ea0396868 Mon Sep 17 00:00:00 2001 From: Pedro Nauck Date: Wed, 27 Sep 2023 22:40:12 -0300 Subject: [PATCH 12/15] fix: build --- packages/graphql/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/graphql/package.json b/packages/graphql/package.json index da5675445..028237017 100644 --- a/packages/graphql/package.json +++ b/packages/graphql/package.json @@ -13,9 +13,9 @@ "build:lib": "tsup --dts", "dev": "pnpm build && node dist/index.js", "start": "pnpm dev", - "codegen": "gql-gen --config codegen.ts", - "codegen:schema": "gql-gen --config codegen.schema.ts", - "codegen:fuel": "gql-gen --config codegen.fuel.ts", + "codegen": "gql-gen -r dotenv/config --config codegen.ts", + "codegen:schema": "gql-gen -r dotenv/config --config codegen.schema.ts", + "codegen:fuel": "gql-gen -r dotenv/config --config codegen.fuel.ts", "fix:generated": "node ./scripts/fix-generated.mjs", "ts:check": "tsc --noEmit", "prepare": "pnpm build" From e16608079183caf532fbdc44ea45d20f652d0b1b Mon Sep 17 00:00:00 2001 From: Pedro Nauck Date: Wed, 27 Sep 2023 22:42:52 -0300 Subject: [PATCH 13/15] fix: build --- packages/graphql/codegen.fuel.ts | 2 -- packages/graphql/codegen.schema.ts | 2 -- 2 files changed, 4 deletions(-) diff --git a/packages/graphql/codegen.fuel.ts b/packages/graphql/codegen.fuel.ts index 5f5891feb..d7d5f9f50 100644 --- a/packages/graphql/codegen.fuel.ts +++ b/packages/graphql/codegen.fuel.ts @@ -1,6 +1,4 @@ import type { CodegenConfig } from '@graphql-codegen/cli'; -import { config as configDotenv } from 'dotenv'; -configDotenv(); const config: CodegenConfig = { generates: { diff --git a/packages/graphql/codegen.schema.ts b/packages/graphql/codegen.schema.ts index 59d309392..f3979a569 100644 --- a/packages/graphql/codegen.schema.ts +++ b/packages/graphql/codegen.schema.ts @@ -1,6 +1,4 @@ import type { CodegenConfig } from '@graphql-codegen/cli'; -import { config as configDotenv } from 'dotenv'; -configDotenv(); const config: CodegenConfig = { generates: { From 5941cac80142381202d6e15d4a067c36ed1e4962 Mon Sep 17 00:00:00 2001 From: Pedro Nauck Date: Wed, 27 Sep 2023 22:44:28 -0300 Subject: [PATCH 14/15] fix: build --- packages/graphql/codegen.fuel.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/graphql/codegen.fuel.ts b/packages/graphql/codegen.fuel.ts index d7d5f9f50..28faf1b99 100644 --- a/packages/graphql/codegen.fuel.ts +++ b/packages/graphql/codegen.fuel.ts @@ -3,7 +3,8 @@ import type { CodegenConfig } from '@graphql-codegen/cli'; const config: CodegenConfig = { generates: { './src/schemas/fuelcore.graphql': { - schema: process.env.FUEL_PROVIDER_URL, + schema: + process.env.FUEL_PROVIDER_URL || 'http://beta-3.fuel.network/graphql', plugins: ['schema-ast'], config: { includeDirectives: true, From e7735d70bc2ad941572aa122142bb2b55fe54aea Mon Sep 17 00:00:00 2001 From: Pedro Nauck Date: Wed, 27 Sep 2023 23:12:11 -0300 Subject: [PATCH 15/15] fix: color theme --- packages/app/src/app/favicon.ico | Bin 39535 -> 0 bytes packages/app/src/app/layout.tsx | 19 +++++-- .../app/src/systems/Core/actions/setTheme.ts | 13 +++++ .../systems/Core/components/Layout/Layout.tsx | 12 +++-- .../Core/components/Provider/Provider.tsx | 11 +++- .../component/TxBreadcrumb/TxBreadcrumb.tsx | 3 +- packages/ui/src/components/Nav/Nav.tsx | 48 ++++++------------ 7 files changed, 61 insertions(+), 45 deletions(-) delete mode 100644 packages/app/src/app/favicon.ico create mode 100644 packages/app/src/systems/Core/actions/setTheme.ts diff --git a/packages/app/src/app/favicon.ico b/packages/app/src/app/favicon.ico deleted file mode 100644 index 4570eb8d9269ad58b17fecbec6d630cded56f507..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39535 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 diff --git a/packages/app/src/app/layout.tsx b/packages/app/src/app/layout.tsx index 23c4ed859..f3ae189f4 100644 --- a/packages/app/src/app/layout.tsx +++ b/packages/app/src/app/layout.tsx @@ -4,7 +4,9 @@ import './globals.css'; import type { Metadata } from 'next'; import { Inter } from 'next/font/google'; +import { cookies } from 'next/headers'; import { Provider } from '~/systems/Core/components/Provider'; +import { cx } from '~/systems/Core/utils/cx'; const inter = Inter({ subsets: ['latin'], @@ -12,8 +14,8 @@ const inter = Inter({ }); export const metadata: Metadata = { - title: 'Create Next App', - description: 'Generated by create next app', + title: 'Fuel Explorer', + description: 'Explorer of the Fastest execution layer', }; export default function RootLayout({ @@ -21,10 +23,19 @@ export default function RootLayout({ }: { children: React.ReactNode; }) { + const { value: theme } = cookies().get('fuel-theme') ?? { value: 'dark' }; return ( - + + + + - {children} + {children} ); diff --git a/packages/app/src/systems/Core/actions/setTheme.ts b/packages/app/src/systems/Core/actions/setTheme.ts new file mode 100644 index 000000000..81eea8183 --- /dev/null +++ b/packages/app/src/systems/Core/actions/setTheme.ts @@ -0,0 +1,13 @@ +'use server'; + +import { cookies } from 'next/headers'; +import { z } from 'zod'; +import { act } from '~/systems/Core/utils/act-server'; + +const schema = z.object({ + theme: z.string(), +}); + +export const setTheme = act(schema, async (input) => { + cookies().set('fuel-theme', input.theme, { path: '/' }); +}); diff --git a/packages/app/src/systems/Core/components/Layout/Layout.tsx b/packages/app/src/systems/Core/components/Layout/Layout.tsx index 2a7850e6b..01f871cc1 100644 --- a/packages/app/src/systems/Core/components/Layout/Layout.tsx +++ b/packages/app/src/systems/Core/components/Layout/Layout.tsx @@ -1,8 +1,10 @@ 'use client'; import { Container, VStack, Nav } from '@fuels/ui'; import type { BaseProps } from '@fuels/ui'; +import Link from 'next/link'; import { Hero } from '~/systems/Home/components/Hero/Hero'; +import { setTheme } from '../../actions/setTheme'; import { Footer } from '../Footer/Footer'; export type LayoutProps = BaseProps<{ @@ -14,7 +16,9 @@ export function Layout({ children, hero }: LayoutProps) { {hero && } diff --git a/packages/app/src/systems/Core/components/Provider/Provider.tsx b/packages/app/src/systems/Core/components/Provider/Provider.tsx index 3dfacdcd1..29856cd10 100644 --- a/packages/app/src/systems/Core/components/Provider/Provider.tsx +++ b/packages/app/src/systems/Core/components/Provider/Provider.tsx @@ -2,9 +2,16 @@ import { Theme, Toaster } from '@fuels/ui'; -export function Provider({ children }: { children: React.ReactNode }) { +export function Provider({ + children, + theme, +}: { + children: React.ReactNode; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + theme: any; +}) { return ( - + {children} diff --git a/packages/app/src/systems/Transaction/component/TxBreadcrumb/TxBreadcrumb.tsx b/packages/app/src/systems/Transaction/component/TxBreadcrumb/TxBreadcrumb.tsx index 4d7ee05ef..932f0853d 100644 --- a/packages/app/src/systems/Transaction/component/TxBreadcrumb/TxBreadcrumb.tsx +++ b/packages/app/src/systems/Transaction/component/TxBreadcrumb/TxBreadcrumb.tsx @@ -7,7 +7,6 @@ import { BreadcrumbLink, Copyable, Icon, - shortAddress, } from '@fuels/ui'; import { IconHome } from '@tabler/icons-react'; import Link from 'next/link'; @@ -27,7 +26,7 @@ export function TxBreadcrumb({ transaction: tx, ...props }: TxBreadcrumbProps) { - {shortAddress(tx.id)} + {tx.id} ); diff --git a/packages/ui/src/components/Nav/Nav.tsx b/packages/ui/src/components/Nav/Nav.tsx index 1bbf0b831..6c76b5e5f 100644 --- a/packages/ui/src/components/Nav/Nav.tsx +++ b/packages/ui/src/components/Nav/Nav.tsx @@ -55,7 +55,10 @@ export type NavConnectionProps = HStackProps & { }; export type NavThemeToggleProps = AsChildProp & - PropsOf<'span'> & { whenOpened?: 'hide' | 'show' | 'no-effect' }; + PropsOf<'span'> & { + whenOpened?: 'hide' | 'show' | 'no-effect'; + onToggle?: (theme: string) => void; + }; export type NavMobileProps = WithAsProps & PropsOf<'nav'> & { @@ -87,26 +90,12 @@ export const NavRoot = createComponent({ * NavDesktop */ -const DESKTOP_CHILD_ITEMS = [ - 'NavLogo', - 'NavMenu', - 'NavSpacer', - 'NavConnection', - 'NavThemeToggle', -]; - export const NavDesktop = createComponent({ id: 'NavDesktop', baseElement: 'nav', - render: (Root, { className, ...props }) => { + render: (Root, { className, children, ...props }) => { const classes = styles(); const { width } = useWindowSize(); - const children = useStrictedChildren( - 'NavDesktop', - DESKTOP_CHILD_ITEMS, - props.children, - ); - if (width < 1024) return null; return (
@@ -126,27 +115,13 @@ export const NavDesktop = createComponent({ * NavMobile */ -const MOBILE_CHILD_ITEMS = [ - 'NavLogo', - 'NavMenu', - 'NavSpacer', - 'NavConnection', - 'NavThemeToggle', - 'NavMobileContent', -]; - export const NavMobile = createComponent({ id: 'NavMobile', baseElement: 'nav', className: () => styles().mobile(), - render: (Root, { isOpen, onOpenChange, ...props }) => { + render: (Root, { isOpen, onOpenChange, children, ...props }) => { const { width } = useWindowSize(); const [open, setOpen] = useState(() => Boolean(isOpen)); - const children = useStrictedChildren( - 'NavMobile', - MOBILE_CHILD_ITEMS, - props.children, - ); useEffect(() => { onOpenChange?.(Boolean(open)); @@ -369,10 +344,17 @@ export const NavConnection = createComponent( export const NavThemeToggle = createComponent({ id: 'NavThemeToggle', baseElement: 'span', - render: (Root, { className, whenOpened = 'hide', ...props }) => { + render: (Root, { className, whenOpened = 'hide', onToggle, ...props }) => { const { theme: current, toggleTheme } = useTheme(); const mobileProps = useNavMobileContext(); const classes = styles(); + + function handleToggle() { + toggleTheme(); + const next = current === 'light' ? 'dark' : 'light'; + onToggle?.(next); + } + const content = ( ({ data-theme={current} role="button" tabIndex={0} - onClick={toggleTheme} + onClick={handleToggle} >