diff --git a/.github/workflows/helm-publish.yml b/.github/workflows/helm-publish.yml index 52b84c569..87702ea77 100644 --- a/.github/workflows/helm-publish.yml +++ b/.github/workflows/helm-publish.yml @@ -5,7 +5,7 @@ on: branches: - main paths: - - "helm/fuel-explorer/Chart.yaml" + - 'helm/fuel-explorer/Chart.yaml' permissions: contents: read @@ -33,4 +33,4 @@ jobs: username: ${{ github.repository_owner }} access-token: ${{ secrets.GITHUB_TOKEN }} force: true - chart-folder: ./helm/fuel-explorer \ No newline at end of file + chart-folder: ./helm/fuel-explorer diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 8d6e258a6..0ade2f458 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -30,7 +30,8 @@ jobs: - uses: FuelLabs/github-actions/setups/node@master - name: Run Audit - run: pnpm audit --prod + # temporally avoid low severity vulnerabilities + run: pnpm audit --prod --audit-level high lint-and-test: name: Lint diff --git a/.prettierignore b/.prettierignore index 907ed9ebd..b11a5bb10 100644 --- a/.prettierignore +++ b/.prettierignore @@ -15,8 +15,11 @@ storybook **/storybook/** storybook-static **/storybook-static/** +**/public/ui/** +**/public/storybook/** .turbo pnpm-lock.yaml .next graphql.schema.json -heml +helm +packages/graphql/src/schemas/*.graphql \ No newline at end of file diff --git a/packages/app/.env.example b/packages/app/.env.example index 037950fa5..0d3b781fe 100644 --- a/packages/app/.env.example +++ b/packages/app/.env.example @@ -1,3 +1 @@ -FUEL_PROVIDER_URL=http://beta-4.fuel.network/graphql - - +FUEL_PROVIDER_URL=https://beta-4.fuel.network/graphql diff --git a/packages/app/.env.production b/packages/app/.env.production index 6453c6456..0d3b781fe 100644 --- a/packages/app/.env.production +++ b/packages/app/.env.production @@ -1,2 +1 @@ -FUEL_PROVIDER_URL=http://beta-3.fuel.network/graphql - +FUEL_PROVIDER_URL=https://beta-4.fuel.network/graphql diff --git a/packages/graphql/.env.production b/packages/graphql/.env.production index 6453c6456..0d3b781fe 100644 --- a/packages/graphql/.env.production +++ b/packages/graphql/.env.production @@ -1,2 +1 @@ -FUEL_PROVIDER_URL=http://beta-3.fuel.network/graphql - +FUEL_PROVIDER_URL=https://beta-4.fuel.network/graphql diff --git a/packages/graphql/codegen.fuel.ts b/packages/graphql/codegen.fuel.ts index 28faf1b99..a7f4ea8be 100644 --- a/packages/graphql/codegen.fuel.ts +++ b/packages/graphql/codegen.fuel.ts @@ -4,7 +4,7 @@ const config: CodegenConfig = { generates: { './src/schemas/fuelcore.graphql': { schema: - process.env.FUEL_PROVIDER_URL || 'http://beta-3.fuel.network/graphql', + process.env.FUEL_PROVIDER_URL || 'http://beta-4.fuel.network/graphql', plugins: ['schema-ast'], config: { includeDirectives: true, diff --git a/packages/graphql/src/domains/Output.ts b/packages/graphql/src/domains/Output.ts index 821c46db3..d964439c8 100644 --- a/packages/graphql/src/domains/Output.ts +++ b/packages/graphql/src/domains/Output.ts @@ -6,7 +6,6 @@ import type { CoinOutput, ContractCreated, ContractOutput, - MessageOutput, TransactionItemFragment, VariableOutput, } from '../generated/types'; @@ -20,7 +19,6 @@ export class OutputDomain { return [ ...this.coinOutputs, ...this.contractOutputs, - ...this.messageOutputs, ...this.changeOutputs, ...this.variableOutputs, ...this.contractCreatedOutputs, @@ -47,16 +45,6 @@ export class OutputDomain { }); } - 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)); diff --git a/packages/graphql/src/queries/tx-fragments.graphql b/packages/graphql/src/queries/tx-fragments.graphql index d624aca8b..0d4c93191 100644 --- a/packages/graphql/src/queries/tx-fragments.graphql +++ b/packages/graphql/src/queries/tx-fragments.graphql @@ -63,10 +63,6 @@ fragment TransactionOutput on Output { ... on ContractOutput { inputIndex } - ... on MessageOutput { - recipient - amount - } ... on ChangeOutput { to amount diff --git a/packages/graphql/src/schemas/fullschema.graphql b/packages/graphql/src/schemas/fullschema.graphql index 3f1ffe0e2..82f88d0ae 100644 --- a/packages/graphql/src/schemas/fullschema.graphql +++ b/packages/graphql/src/schemas/fullschema.graphql @@ -71,8 +71,9 @@ scalar BlockId scalar Bytes32 type ChainInfo { - baseChainHeight: U64! + baseChainHeight: U32! consensusParameters: ConsensusParameters! + gasCosts: GasCosts! latestBlock: Block! name: String! peerCount: Int! @@ -87,10 +88,12 @@ type ChangeOutput { type Coin { amount: U64! assetId: AssetId! - blockCreated: U64! - maturity: U64! + """TxPointer - the height of the block this coin was created in""" + blockCreated: U32! + maturity: U32! owner: Address! - status: CoinStatus! + """TxPointer - the index of the transaction that created this coin""" + txCreatedIdx: U64! utxoId: UtxoId! } @@ -124,17 +127,17 @@ type CoinOutput { to: Address! } -enum CoinStatus { - SPENT - UNSPENT -} +"""The schema analog of the [`coins::CoinType`].""" +union CoinType = Coin | MessageCoin union Consensus = Genesis | PoAConsensus type ConsensusParameters { + chainId: U64! contractMaxSize: U64! gasPerByte: U64! gasPriceFactor: U64! + maxGasPerPredicate: U64! maxGasPerTx: U64! maxInputs: U64! maxMessageDataLength: U64! @@ -194,9 +197,14 @@ type ContractOutput { stateRoot: Bytes32! } +type DependentCost { + base: U64! + depPerUnit: U64! +} + input ExcludeInput { """Messages to exclude from the selection.""" - messages: [MessageId!]! + messages: [Nonce!]! """Utxos to exclude from the selection.""" utxos: [UtxoId!]! } @@ -208,6 +216,111 @@ type FailureStatus { time: Tai64Timestamp! } +type GasCosts { + add: U64! + addi: U64! + aloc: U64! + and: U64! + andi: U64! + bal: U64! + bhei: U64! + bhsh: U64! + burn: U64! + call: DependentCost! + cb: U64! + ccp: DependentCost! + cfei: U64! + cfsi: U64! + croo: U64! + csiz: DependentCost! + div: U64! + divi: U64! + eck1: U64! + ecr1: U64! + ed19: U64! + eq: U64! + exp: U64! + expi: U64! + flag: U64! + gm: U64! + gt: U64! + gtf: U64! + ji: U64! + jmp: U64! + jmpb: U64! + jmpf: U64! + jne: U64! + jneb: U64! + jnef: U64! + jnei: U64! + jnzb: U64! + jnzf: U64! + jnzi: U64! + k256: U64! + lb: U64! + ldc: DependentCost! + log: U64! + logd: DependentCost! + lt: U64! + lw: U64! + mcl: DependentCost! + mcli: DependentCost! + mcp: DependentCost! + mcpi: U64! + meq: DependentCost! + mint: U64! + mldv: U64! + mlog: U64! + modOp: U64! + modi: U64! + moveOp: U64! + movi: U64! + mroo: U64! + mul: U64! + muli: U64! + noop: U64! + not: U64! + or: U64! + ori: U64! + ret: U64! + retd: DependentCost! + rvrt: U64! + s256: U64! + sb: U64! + scwq: U64! + sll: U64! + slli: U64! + smo: DependentCost! + srl: U64! + srli: U64! + srw: U64! + srwq: DependentCost! + sub: U64! + subi: U64! + sw: U64! + sww: U64! + swwq: U64! + time: U64! + tr: U64! + tro: U64! + wdam: U64! + wdcm: U64! + wddv: U64! + wdmd: U64! + wdml: U64! + wdmm: U64! + wdop: U64! + wqam: U64! + wqcm: U64! + wqdv: U64! + wqmd: U64! + wqml: U64! + wqmm: U64! + wqop: U64! + xor: U64! + xori: U64! +} + type Genesis { """ The chain configs define what consensus type to use, what settlement layer to use, @@ -270,13 +383,13 @@ type Header { """ daHeight: U64! """Fuel block height.""" - height: U64! + height: U32! """Hash of the header""" id: BlockId! - """Number of output messages in this block.""" - outputMessagesCount: U64! - """Merkle root of messages in this block.""" - outputMessagesRoot: Bytes32! + """Number of message receipts in this block.""" + messageReceiptCount: U64! + """Merkle root of message receipts in this block.""" + messageReceiptRoot: Bytes32! """Merkle root of all previous block header hashes.""" prevRoot: Bytes32! """The block producer time.""" @@ -294,10 +407,11 @@ union Input = InputCoin | InputContract | InputMessage type InputCoin { amount: U64! assetId: AssetId! - maturity: U64! + maturity: U32! owner: Address! predicate: HexString! predicateData: HexString! + predicateGasUsed: U64! txPointer: TxPointer! utxoId: UtxoId! witnessIndex: Int! @@ -314,24 +428,36 @@ type InputContract { type InputMessage { amount: U64! data: HexString! - messageId: MessageId! - nonce: U64! + nonce: Nonce! predicate: HexString! predicateData: HexString! + predicateGasUsed: U64! recipient: Address! sender: Address! witnessIndex: Int! } +type MerkleProof { + proofIndex: U64! + proofSet: [Bytes32!]! +} + type Message { amount: U64! daHeight: U64! data: HexString! - messageId: MessageId! - nonce: U64! + nonce: Nonce! + recipient: Address! + sender: Address! +} + +type MessageCoin { + amount: U64! + assetId: AssetId! + daHeight: U64! + nonce: Nonce! recipient: Address! sender: Address! - status: MessageStatus! } type MessageConnection { @@ -353,35 +479,45 @@ type MessageEdge { scalar MessageId -type MessageOutput { - amount: U64! - recipient: Address! -} - type MessageProof { amount: U64! + blockProof: MerkleProof! + commitBlockHeader: Header! data: HexString! - header: Header! - nonce: Bytes32! - proofIndex: U64! - proofSet: [Bytes32!]! + messageBlockHeader: Header! + messageProof: MerkleProof! + nonce: Nonce! recipient: Address! sender: Address! - signature: Signature! } -enum MessageStatus { +enum MessageState { + NOT_FOUND SPENT UNSPENT } +type MessageStatus { + state: MessageState! +} + 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""" + """ + Sequentially produces `blocks_to_produce` blocks. The first block starts with + `start_timestamp`. If the block production in the [`crate::service::Config`] is + `Trigger::Interval { block_time }`, produces blocks with `block_time ` intervals between + them. The `start_timestamp` is the timestamp in seconds. + """ + produceBlocks(blocksToProduce: U64!, startTimestamp: Tai64Timestamp): U32! + """ + Submits transaction to the `TxPool`. + + Returns submitted transaction if the transaction is included in the `TxPool` without problems. + """ submit(tx: HexString!): Transaction! } @@ -394,7 +530,9 @@ type NodeInfo { vmBacktrace: Boolean! } -union Output = ChangeOutput | CoinOutput | ContractCreated | ContractOutput | MessageOutput | VariableOutput +scalar Nonce + +union Output = ChangeOutput | CoinOutput | ContractCreated | ContractOutput | VariableOutput """Information about pagination in a connection""" type PageInfo { @@ -448,19 +586,43 @@ type Query { 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. + Gets all unspent coins of some `owner` maybe filtered with by `asset_id` per page. """ coins(after: String, before: String, filter: CoinFilterInput!, first: Int, last: Int): CoinConnection! + """ + For each `query_per_asset`, get some spendable coins(of asset specified by the query) owned by + `owner` that add up at least the query amount. The returned coins can be spent. + The number of coins is optimized to prevent dust accumulation. + + The query supports excluding and maximum the number of coins. + + Returns: + The list of spendable coins 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. + """ + coinsToSpend( + """The excluded coins from the selection.""" + excludedIds: ExcludeInput + """The `Address` of the coins owner.""" + owner: Address! + """ + The list of requested assets` coins with asset ids, `target` amount the user wants to reach, and the `max` number of coins in the selection. Several entries with the same asset id are not allowed. + """ + queryPerAsset: [SpendQueryElementInput!]! + ): [[CoinType!]!]! 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! + """Estimate the predicate gas for the provided transaction""" + estimatePredicates(tx: HexString!): Transaction! """Returns true when the GraphQL API is serving requests.""" health: Boolean! - messageProof(messageId: MessageId!, transactionId: TransactionId!): MessageProof + messageProof(commitBlockHeight: U32, commitBlockId: BlockId, messageId: MessageId!, transactionId: TransactionId!): MessageProof + messageStatus(nonce: Nonce!): MessageStatus! messages( after: String before: String @@ -470,27 +632,6 @@ type Query { 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!]!]! tokens(assetsId: [String]!): [Token!]! transaction( """The ID of the transaction""" @@ -511,14 +652,12 @@ type Receipt { gasUsed: U64 is: U64 len: U64 - messageId: MessageId - nonce: Bytes32 + nonce: Nonce param1: U64 param2: U64 pc: U64 ptr: U64 ra: U64 - rawPayload: HexString! rb: U64 rc: U64 rd: U64 @@ -527,16 +666,19 @@ type Receipt { recipient: Address result: U64 sender: Address + subId: Bytes32 to: Contract toAddress: Address val: U64 } enum ReceiptType { + BURN CALL LOG LOG_DATA MESSAGE_OUT + MINT PANIC RETURN RETURN_DATA @@ -546,9 +688,6 @@ enum ReceiptType { TRANSFER_OUT } -"""The schema analog of the [`resource::Resource`].""" -union Resource = Coin | Message - enum ReturnType { RETURN RETURN_DATA @@ -595,6 +734,10 @@ type Subscription { """The ID of the transaction""" id: TransactionId! ): TransactionStatus! + """ + Submits transaction to the `TxPool` and await either confirmation or failure. + """ + submitAndAwait(tx: HexString!): TransactionStatus! } type SuccessStatus { @@ -605,13 +748,6 @@ type SuccessStatus { scalar Tai64Timestamp -input TimeParameters { - """The time interval between subsequent blocks""" - blockTimeInterval: U64! - """The time to set on the first block""" - startTime: U64! -} - type Token { assetId: String! decimals: U64! @@ -639,7 +775,7 @@ type Transaction { isCreate: Boolean! isMint: Boolean! isScript: Boolean! - maturity: U64 + maturity: U32 outputs: [Output!]! """Return the transaction bytes using canonical encoding""" rawPayload: HexString! @@ -708,6 +844,8 @@ enum TransactionTitle { scalar TxPointer +scalar U32 + scalar U64 scalar UtxoId