diff --git a/Cargo.lock b/Cargo.lock index c766d515b415e..4449deae1a1d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6716,12 +6716,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" -[[package]] -name = "markdown-gen" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8034621d7f1258317ca1dfb9205e3925d27ee4aa2a46620a09c567daf0310562" - [[package]] name = "match_opt" version = "0.1.2" @@ -13353,7 +13347,6 @@ dependencies = [ "insta", "itertools 0.10.5", "lru 0.10.0", - "markdown-gen", "move-binary-format", "move-bytecode-utils", "move-core-types", diff --git a/Cargo.toml b/Cargo.toml index 55f30a7447f6b..78b95b57b41e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -369,7 +369,6 @@ jsonrpsee = { git = "https://github.com/wlmyng/jsonrpsee.git", rev = "b1b3007847 json_to_table = { git = "https://github.com/zhiburt/tabled/", rev = "e449317a1c02eb6b29e409ad6617e5d9eb7b3bd4" } leb128 = "0.2.5" lru = "0.10" -markdown-gen = "1.2.1" match_opt = "0.1.2" miette = { version = "7", features = ["fancy"] } mime = "0.3" diff --git a/crates/sui-graphql-e2e-tests/tests/packages/versioning.exp b/crates/sui-graphql-e2e-tests/tests/packages/versioning.exp index 7a0e6ec3faeed..7f0e8a7153b98 100644 --- a/crates/sui-graphql-e2e-tests/tests/packages/versioning.exp +++ b/crates/sui-graphql-e2e-tests/tests/packages/versioning.exp @@ -1,4 +1,4 @@ -processed 15 tasks +processed 17 tasks init: A: object(0,0) @@ -13,7 +13,7 @@ task 2, line 11: //# create-checkpoint Checkpoint created: 1 -task 3, lines 13-28: +task 3, lines 13-50: //# run-graphql Response: { "data": { @@ -27,6 +27,35 @@ Response: { } ] } + }, + "packageVersions": { + "nodes": [ + { + "address": "0x175ae86f2df1eb652d57fbe9e44c7f2d67870d2b6776a4356f30930221b63b88", + "version": 1 + } + ] + } + }, + "firstPackage": { + "address": "0x175ae86f2df1eb652d57fbe9e44c7f2d67870d2b6776a4356f30930221b63b88", + "version": 1, + "module": { + "functions": { + "nodes": [ + { + "name": "f" + } + ] + } + }, + "packageVersions": { + "nodes": [ + { + "address": "0x175ae86f2df1eb652d57fbe9e44c7f2d67870d2b6776a4356f30930221b63b88", + "version": 1 + } + ] } }, "packages": { @@ -56,17 +85,17 @@ Response: { } } -task 4, lines 30-34: +task 4, lines 52-56: //# upgrade --package P0 --upgrade-capability 1,1 --sender A created: object(4,0) mutated: object(0,0), object(1,1) gas summary: computation_cost: 1000000, storage_cost: 5251600, storage_rebate: 2595780, non_refundable_storage_fee: 26220 -task 5, line 36: +task 5, line 58: //# create-checkpoint Checkpoint created: 2 -task 6, lines 38-53: +task 6, lines 60-97: //# run-graphql Response: { "data": { @@ -83,6 +112,43 @@ Response: { } ] } + }, + "packageVersions": { + "nodes": [ + { + "address": "0x175ae86f2df1eb652d57fbe9e44c7f2d67870d2b6776a4356f30930221b63b88", + "version": 1 + }, + { + "address": "0x351bc614b36f0f522a64334e4c278d4bfe200234958870c084e0a005f041d681", + "version": 2 + } + ] + } + }, + "firstPackage": { + "address": "0x175ae86f2df1eb652d57fbe9e44c7f2d67870d2b6776a4356f30930221b63b88", + "version": 1, + "module": { + "functions": { + "nodes": [ + { + "name": "f" + } + ] + } + }, + "packageVersions": { + "nodes": [ + { + "address": "0x175ae86f2df1eb652d57fbe9e44c7f2d67870d2b6776a4356f30930221b63b88", + "version": 1 + }, + { + "address": "0x351bc614b36f0f522a64334e4c278d4bfe200234958870c084e0a005f041d681", + "version": 2 + } + ] } }, "packages": { @@ -116,17 +182,17 @@ Response: { } } -task 7, lines 55-60: +task 7, lines 99-104: //# upgrade --package P1 --upgrade-capability 1,1 --sender A created: object(7,0) mutated: object(0,0), object(1,1) gas summary: computation_cost: 1000000, storage_cost: 5426400, storage_rebate: 2595780, non_refundable_storage_fee: 26220 -task 8, line 62: +task 8, line 106: //# create-checkpoint Checkpoint created: 3 -task 9, lines 64-79: +task 9, lines 108-145: //# run-graphql Response: { "data": { @@ -146,6 +212,51 @@ Response: { } ] } + }, + "packageVersions": { + "nodes": [ + { + "address": "0x175ae86f2df1eb652d57fbe9e44c7f2d67870d2b6776a4356f30930221b63b88", + "version": 1 + }, + { + "address": "0x351bc614b36f0f522a64334e4c278d4bfe200234958870c084e0a005f041d681", + "version": 2 + }, + { + "address": "0x0eae57b7a07b0548b1f6b0c309f0692828ff994e9159b541334b25582980631c", + "version": 3 + } + ] + } + }, + "firstPackage": { + "address": "0x175ae86f2df1eb652d57fbe9e44c7f2d67870d2b6776a4356f30930221b63b88", + "version": 1, + "module": { + "functions": { + "nodes": [ + { + "name": "f" + } + ] + } + }, + "packageVersions": { + "nodes": [ + { + "address": "0x175ae86f2df1eb652d57fbe9e44c7f2d67870d2b6776a4356f30930221b63b88", + "version": 1 + }, + { + "address": "0x351bc614b36f0f522a64334e4c278d4bfe200234958870c084e0a005f041d681", + "version": 2 + }, + { + "address": "0x0eae57b7a07b0548b1f6b0c309f0692828ff994e9159b541334b25582980631c", + "version": 3 + } + ] } }, "packages": { @@ -183,7 +294,7 @@ Response: { } } -task 10, lines 81-118: +task 10, lines 147-184: //# run-graphql Response: { "data": { @@ -283,7 +394,7 @@ Response: { } } -task 11, lines 120-157: +task 11, lines 186-223: //# run-graphql Response: { "data": { @@ -374,7 +485,7 @@ Response: { } } -task 12, lines 159-214: +task 12, lines 225-280: //# run-graphql Response: { "data": { @@ -513,7 +624,7 @@ Response: { } } -task 13, lines 216-244: +task 13, lines 282-310: //# run-graphql Response: { "data": { @@ -526,7 +637,7 @@ Response: { } } -task 14, lines 246-277: +task 14, lines 312-343: //# run-graphql Response: { "data": { @@ -621,3 +732,83 @@ Response: { } } } + +task 15, lines 345-380: +//# run-graphql +Response: { + "data": { + "packageVersions": { + "nodes": [ + { + "address": "0x175ae86f2df1eb652d57fbe9e44c7f2d67870d2b6776a4356f30930221b63b88", + "version": 1 + }, + { + "address": "0x351bc614b36f0f522a64334e4c278d4bfe200234958870c084e0a005f041d681", + "version": 2 + }, + { + "address": "0x0eae57b7a07b0548b1f6b0c309f0692828ff994e9159b541334b25582980631c", + "version": 3 + } + ] + }, + "after": { + "nodes": [ + { + "address": "0x351bc614b36f0f522a64334e4c278d4bfe200234958870c084e0a005f041d681", + "version": 2 + }, + { + "address": "0x0eae57b7a07b0548b1f6b0c309f0692828ff994e9159b541334b25582980631c", + "version": 3 + } + ] + }, + "before": { + "nodes": [ + { + "address": "0x175ae86f2df1eb652d57fbe9e44c7f2d67870d2b6776a4356f30930221b63b88", + "version": 1 + }, + { + "address": "0x351bc614b36f0f522a64334e4c278d4bfe200234958870c084e0a005f041d681", + "version": 2 + } + ] + }, + "between": { + "nodes": [ + { + "address": "0x351bc614b36f0f522a64334e4c278d4bfe200234958870c084e0a005f041d681", + "version": 2 + } + ] + } + } +} + +task 16, lines 382-400: +//# run-graphql +Response: { + "data": { + "packageVersions": { + "nodes": [ + { + "address": "0x0000000000000000000000000000000000000000000000000000000000000001", + "version": 1 + } + ] + }, + "package": { + "packageVersions": { + "nodes": [ + { + "address": "0x0000000000000000000000000000000000000000000000000000000000000001", + "version": 1 + } + ] + } + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/packages/versioning.move b/crates/sui-graphql-e2e-tests/tests/packages/versioning.move index 694072fb9c445..b0e0900bbcb59 100644 --- a/crates/sui-graphql-e2e-tests/tests/packages/versioning.move +++ b/crates/sui-graphql-e2e-tests/tests/packages/versioning.move @@ -17,6 +17,28 @@ module P0::m { module(name: "m") { functions { nodes { name } } } + + packageVersions { + nodes { + address + version + } + } + } + + firstPackage: package(address: "@{P0}", version: 1) { + address + version + module(name: "m") { + functions { nodes { name } } + } + + packageVersions { + nodes { + address + version + } + } } packages(first: 10) { @@ -42,6 +64,28 @@ module P1::m { module(name: "m") { functions { nodes { name } } } + + packageVersions { + nodes { + address + version + } + } + } + + firstPackage: package(address: "@{P1}", version: 1) { + address + version + module(name: "m") { + functions { nodes { name } } + } + + packageVersions { + nodes { + address + version + } + } } packages(first: 10) { @@ -68,6 +112,28 @@ module P2::m { module(name: "m") { functions { nodes { name } } } + + packageVersions { + nodes { + address + version + } + } + } + + firstPackage: package(address: "@{P2}", version: 1) { + address + version + module(name: "m") { + functions { nodes { name } } + } + + packageVersions { + nodes { + address + version + } + } } packages(first: 10) { @@ -275,3 +341,60 @@ module P2::m { } } } + +//# run-graphql +{ # Query for versions of a user package + packageVersions(address: "@{P0}") { + nodes { + address + version + } + } + + after: packageVersions(address: "@{P0}", filter: { afterVersion: 1 }) { + nodes { + address + version + } + } + + before: packageVersions(address: "@{P0}", filter: { beforeVersion: 3 }) { + nodes { + address + version + } + } + + between: packageVersions( + address: "@{P0}", + filter: { + afterVersion: 1, + beforeVersion: 3, + }, + ) { + nodes { + address + version + } + } +} + +//# run-graphql +{ # Query for versions of a system package (there will be only one because we + # don't have a way to upgrade system packages in these tests.) + packageVersions(address: "0x1") { + nodes { + address + version + } + } + + package(address: "0x1") { + packageVersions { + nodes { + address + version + } + } + } +} diff --git a/crates/sui-graphql-rpc/Cargo.toml b/crates/sui-graphql-rpc/Cargo.toml index 87c8a1571a9ef..12414fd4888c7 100644 --- a/crates/sui-graphql-rpc/Cargo.toml +++ b/crates/sui-graphql-rpc/Cargo.toml @@ -32,7 +32,6 @@ lru.workspace = true move-binary-format.workspace = true move-disassembler.workspace = true move-ir-types.workspace = true -markdown-gen.workspace = true mysten-metrics.workspace = true mysten-network.workspace = true move-core-types.workspace = true diff --git a/crates/sui-graphql-rpc/docs/examples.md b/crates/sui-graphql-rpc/docs/examples.md deleted file mode 100644 index b227665131112..0000000000000 --- a/crates/sui-graphql-rpc/docs/examples.md +++ /dev/null @@ -1,1700 +0,0 @@ -# Sui GraphQL Examples -### [Address](#0) -####   [Address](#0) -####   [Transaction Block Connection](#1) -### [Balance Connection](#1) -####   [Balance Connection](#65535) -### [Chain Id](#2) -####   [Chain Id](#131070) -### [Checkpoint](#3) -####   [At Digest](#196605) -####   [At Seq Num](#196606) -####   [First Two Tx Blocks For Checkpoint](#196607) -####   [Latest Checkpoint](#196608) -####   [Multiple Selections](#196609) -####   [With Timestamp Tx Block Live Objects](#196610) -####   [With Tx Sent Addr Filter](#196611) -### [Checkpoint Connection](#4) -####   [Ascending Fetch](#262140) -####   [First Ten After Checkpoint](#262141) -####   [Last Ten After Checkpoint](#262142) -### [Coin Connection](#5) -####   [Coin Connection](#327675) -### [Coin Metadata](#6) -####   [Coin Metadata](#393210) -### [Epoch](#7) -####   [Latest Epoch](#458745) -####   [Specific Epoch](#458746) -####   [With Checkpoint Connection](#458747) -####   [With Tx Block Connection](#458748) -####   [With Tx Block Connection Latest Epoch](#458749) -### [Event Connection](#8) -####   [Event Connection](#524280) -####   [Filter By Emitting Package Module And Event Type](#524281) -####   [Filter By Sender](#524282) -### [Name Service](#9) -####   [Name Service](#589815) -### [Object](#10) -####   [Object](#655350) -### [Object Connection](#11) -####   [Filter Object Ids](#720885) -####   [Filter On Generic Type](#720886) -####   [Filter On Type](#720887) -####   [Filter Owner](#720888) -####   [Object Connection](#720889) -### [Owner](#12) -####   [Dynamic Field](#786420) -####   [Dynamic Field Connection](#786421) -####   [Dynamic Object Field](#786422) -####   [Owner](#786423) -### [Protocol Configs](#13) -####   [Key Value](#851955) -####   [Key Value Feature Flag](#851956) -####   [Specific Config](#851957) -####   [Specific Feature Flag](#851958) -### [Service Config](#14) -####   [Service Config](#917490) -### [Stake Connection](#15) -####   [Stake Connection](#983025) -### [Sui System State Summary](#16) -####   [Sui System State Summary](#1048560) -### [Transaction Block](#17) -####   [Transaction Block](#1114095) -####   [Transaction Block Kind](#1114096) -### [Transaction Block Connection](#18) -####   [Before After Checkpoint](#1179630) -####   [Changed Object Filter](#1179631) -####   [Input Object Filter](#1179632) -####   [Input Object Sign Addr Filter](#1179633) -####   [Package Filter](#1179634) -####   [Package Module Filter](#1179635) -####   [Package Module Func Filter](#1179636) -####   [Recv Addr Filter](#1179637) -####   [Sign Addr Filter](#1179638) -####   [Tx Ids Filter](#1179639) -####   [Tx Kind Filter](#1179640) -####   [With Defaults Ascending](#1179641) -### [Transaction Block Effects](#19) -####   [Transaction Block Effects](#1245165) -## -## Address -### -### Address -#### Get the address' balance and its coins' id and type - ->
{
->  address(
->    address: "0x5094652429957619e6efa79a404a6714d1126e63f551f4b6c7fb76440f8118c9"
->  ) {
->    address
->    balance {
->      coinType {
->        repr
->      }
->      coinObjectCount
->      totalBalance
->    }
->    coins {
->      nodes {
->        contents {
->          type {
->            repr
->          }
->        }
->      }
->    }
->  }
->}
- -### -### Transaction Block Connection -#### See examples in Query::transactionBlocks as this is similar behavior -#### to the `transactionBlocks` in Query but supports additional -#### `AddressTransactionBlockRelationship` filter -#### Filtering on package where the signer of the TX is the current -#### address and displaying the transaction's sender and the gas price -#### and budget. - ->
# See examples in Query::transactionBlocks as this is similar behavior
-># to the `transactionBlocks` in Query but supports additional
-># `AddressTransactionBlockRelationship` filter
->
-># Filtering on package where the signer of the TX is the current
-># address and displaying the transaction's sender and the gas price
-># and budget.
->query transaction_block_with_relation_filter {
->  address(address: "0x2") {
->    transactionBlocks(relation: SIGN, filter: { function: "0x2" }) {
->      nodes {
->        sender {
->          address
->        }
->        gasInput {
->          gasPrice
->          gasBudget
->        }
->      }
->    }
->  }
->}
- -## -## Balance Connection -### -### Balance Connection -#### Query the balance for objects of type COIN and then for each coin -#### get the coin type, the number of objects, and the total balance - ->
{
->  address(
->    address: "0x5094652429957619e6efa79a404a6714d1126e63f551f4b6c7fb76440f8118c9"
->  ) {
->    balance(
->      type: "0xc060006111016b8a020ad5b33834984a437aaa7d3c74c18e09a95d48aceab08c::coin::COIN"
->    ) {
->      coinObjectCount
->      totalBalance
->    }
->    balances {
->      nodes {
->        coinType {
->          repr
->        }
->        coinObjectCount
->        totalBalance
->      }
->      pageInfo {
->        endCursor
->      }
->    }
->  }
->}
- -## -## Chain Id -### -### Chain Id -#### Returns the chain identifier for the chain that the server is tracking - ->
{
->  chainIdentifier
->}
- -## -## Checkpoint -### -### At Digest -#### Get the checkpoint's information at a particular digest - ->
{
->  checkpoint(id: { digest: "GaDeWEfbSQCQ8FBQHUHVdm4KjrnbgMqEZPuhStoq5njU" }) {
->    digest
->    sequenceNumber
->    validatorSignatures
->    previousCheckpointDigest
->    networkTotalTransactions
->    rollingGasSummary {
->      computationCost
->      storageCost
->      storageRebate
->      nonRefundableStorageFee
->    }
->    epoch {
->      epochId
->      referenceGasPrice
->      startTimestamp
->      endTimestamp
->    }
->  }
->}
- -### -### At Seq Num -#### Get the checkpoint's information at a particular sequence number - ->
{
->  checkpoint(id: { sequenceNumber: 10 }) {
->    digest
->    sequenceNumber
->    validatorSignatures
->    previousCheckpointDigest
->    networkTotalTransactions
->    rollingGasSummary {
->      computationCost
->      storageCost
->      storageRebate
->      nonRefundableStorageFee
->    }
->    epoch {
->      epochId
->      referenceGasPrice
->      startTimestamp
->      endTimestamp
->    }
->  }
->}
- -### -### First Two Tx Blocks For Checkpoint -#### Get data for the first two transaction blocks of checkpoint at sequence number 10 - ->
{
->  checkpoint(id: { sequenceNumber: 10 }) {
->    transactionBlocks(first: 2) {
->      edges {
->        node {
->          kind {
->            __typename
->          }
->          digest
->          sender {
->            address
->          }
->          expiration {
->            epochId
->          }
->        }
->      }
->      pageInfo {
->        startCursor
->        hasNextPage
->        hasPreviousPage
->        endCursor
->      }
->    }
->  }
->}
- -### -### Latest Checkpoint -#### Latest checkpoint's data - ->
{
->  checkpoint {
->    digest
->    sequenceNumber
->    validatorSignatures
->    previousCheckpointDigest
->    networkTotalTransactions
->    rollingGasSummary {
->      computationCost
->      storageCost
->      storageRebate
->      nonRefundableStorageFee
->    }
->    epoch {
->      epochId
->      referenceGasPrice
->      startTimestamp
->      endTimestamp
->    }
->  }
->}
- -### -### Multiple Selections -#### Get the checkpoint at sequence 9769 and show -#### its transactions - ->
{
->  checkpoint(id: { sequenceNumber: 9769 }) {
->    digest
->    sequenceNumber
->    timestamp
->    validatorSignatures
->    previousCheckpointDigest
->    networkTotalTransactions
->    rollingGasSummary {
->      computationCost
->      storageCost
->      storageRebate
->      nonRefundableStorageFee
->    }
->    epoch {
->      epochId
->      liveObjectSetDigest
->    }
->    transactionBlocks {
->      edges {
->        node {
->          digest
->          sender {
->            address
->          }
->          expiration {
->            epochId
->          }
->        }
->      }
->    }
->  }
->}
- -### -### With Timestamp Tx Block Live Objects -#### Latest checkpoint's timestamp, and transaction block data - ->
{
->  checkpoint {
->    digest
->    sequenceNumber
->    timestamp
->    transactionBlocks {
->      edges {
->        node {
->          digest
->          sender {
->            address
->          }
->          expiration {
->            epochId
->          }
->        }
->      }
->    }
->  }
->}
- -### -### With Tx Sent Addr Filter -#### Select checkpoint at sequence number 14830285 for transactions from signAddress - ->
{
->  checkpoint(id: { sequenceNumber: 14830285 }) {
->    digest
->    sequenceNumber
->    timestamp
->    transactionBlocks(
->      filter: {
->        signAddress: "0x0000000000000000000000000000000000000000000000000000000000000000"
->      }
->    ) {
->      edges {
->        node {
->          digest
->          sender {
->            address
->          }
->          expiration {
->            epochId
->          }
->        }
->      }
->    }
->  }
->}
- -## -## Checkpoint Connection -### -### Ascending Fetch -#### Use the checkpoint connection to fetch some default amount of checkpoints in an ascending order - ->
{
->  checkpoints {
->    nodes {
->      digest
->      sequenceNumber
->      validatorSignatures
->      previousCheckpointDigest
->      networkTotalTransactions
->      rollingGasSummary {
->        computationCost
->        storageCost
->        storageRebate
->        nonRefundableStorageFee
->      }
->      epoch {
->        epochId
->        referenceGasPrice
->        startTimestamp
->        endTimestamp
->      }
->    }
->  }
->}
- -### -### First Ten After Checkpoint -#### Fetch the digest and sequence number of the first 10 checkpoints after the cursor, which in this example is set to be checkpoint 0. Note that the cursor is opaque. - ->
{
->  checkpoints(first: 10, after: "eyJjIjoyMjgwMDU4MCwicyI6MH0") {
->    nodes {
->      sequenceNumber
->      digest
->    }
->  }
->}
- -### -### Last Ten After Checkpoint -#### Fetch the digest and the sequence number of the last 20 checkpoints before the cursor - ->
{
->  checkpoints(last: 20, before: "eyJjIjoyMjgwMDY1MSwicyI6MjI4MDA2MzJ9") {
->    nodes {
->      sequenceNumber
->      digest
->    }
->  }
->}
- -## -## Coin Connection -### -### Coin Connection -#### Get last 3 coins owned by `0x0`. - ->
{
->  address(
->    address: "0x0000000000000000000000000000000000000000000000000000000000000000"
->  ) {
->    coins(last: 3) {
->      nodes {
->        coinBalance
->      }
->      pageInfo {
->        endCursor
->        hasNextPage
->      }
->    }
->  }
->}
- -## -## Coin Metadata -### -### Coin Metadata - ->
query CoinMetadata {
->  coinMetadata(coinType: "0x2::sui::SUI") {
->    decimals
->    name
->    symbol
->    description
->    iconUrl
->    supply
->    hasPublicTransfer
->  }
->}
- -## -## Epoch -### -### Latest Epoch -#### Latest epoch, since epoch omitted - ->
{
->  epoch {
->    protocolConfigs {
->      protocolVersion
->    }
->    epochId
->    referenceGasPrice
->    startTimestamp
->    endTimestamp
->  }
->}
- -### -### Specific Epoch -#### Selecting all fields for epoch 100 - ->
{
->  epoch(id: 100) {
->    protocolConfigs {
->      protocolVersion
->    }
->    epochId
->    referenceGasPrice
->    startTimestamp
->    endTimestamp
->    validatorSet {
->      totalStake
->      pendingActiveValidatorsSize
->      stakingPoolMappingsSize
->      inactivePoolsSize
->      validatorCandidatesSize
->      activeValidators {
->        nodes {
->          name
->          description
->          imageUrl
->          projectUrl
->          exchangeRates {
->            storageRebate
->            bcs
->            hasPublicTransfer
->          }
->          exchangeRatesSize
->          stakingPoolActivationEpoch
->          stakingPoolSuiBalance
->          rewardsPool
->          poolTokenBalance
->          pendingStake
->          pendingTotalSuiWithdraw
->          pendingPoolTokenWithdraw
->          votingPower
->          gasPrice
->          commissionRate
->          nextEpochStake
->          nextEpochGasPrice
->          nextEpochCommissionRate
->          atRisk
->        }
->      }
->    }
->  }
->}
- -### -### With Checkpoint Connection - ->
{
->  epoch {
->    checkpoints {
->      nodes {
->        transactionBlocks(first: 10) {
->          pageInfo {
->            hasNextPage
->            endCursor
->          }
->          edges {
->            cursor
->            node {
->              sender {
->                address
->              }
->              effects {
->                gasEffects {
->                  gasObject {
->                    address
->                  }
->                }
->              }
->              gasInput {
->                gasPrice
->                gasBudget
->              }
->            }
->          }
->        }
->      }
->    }
->  }
->}
- -### -### With Tx Block Connection -#### Fetch the first 20 transactions after tx 231220153 (encoded as a -#### cursor) in epoch 97. - ->
{
->  epoch(id: 97) {
->    transactionBlocks(first: 20, after:"eyJjIjoyNjkzMzc3OCwidCI6MjMxMjIwMTUzLCJ0YyI6ODAxMDg4NH0") {
->      pageInfo {
->        hasNextPage
->        endCursor
->      }
->      edges {
->        cursor
->        node {
->          digest
->          sender {
->            address
->          }
->          effects {
->            gasEffects {
->              gasObject {
->                address
->              }
->            }
->          }
->          gasInput {
->            gasPrice
->            gasBudget
->          }
->        }
->      }
->    }
->  }
->}
- -### -### With Tx Block Connection Latest Epoch - ->
{
->  epoch {
->    transactionBlocks(first: 20, after: "eyJjIjoyNjkzMzMyNCwidCI6MTEwMTYxMDQ4MywidGMiOjI2ODUxMjQ4fQ") {
->      pageInfo {
->        hasNextPage
->        endCursor
->      }
->      edges {
->        cursor
->        node {
->          sender {
->            address
->          }
->          effects {
->            gasEffects {
->              gasObject {
->                address
->              }
->            }
->          }
->          gasInput {
->            gasPrice
->            gasBudget
->          }
->        }
->      }
->    }
->  }
->}
- -## -## Event Connection -### -### Event Connection - ->
{
->  events(
->    filter: {
->      eventType: "0x3164fcf73eb6b41ff3d2129346141bd68469964c2d95a5b1533e8d16e6ea6e13::Market::ChangePriceEvent<0x2::sui::SUI>"
->    }
->  ) {
->    nodes {
->      sendingModule {
->        name
->        package { digest }
->      }
->      type {
->        repr
->      }
->      sender {
->        address
->      }
->      timestamp
->      json
->      bcs
->    }
->  }
->}
- -### -### Filter By Emitting Package Module And Event Type - ->
query ByEmittingPackageModuleAndEventType {
->  events(
->    first: 1
->    after: "eyJ0eCI6Njc2MywiZSI6MCwiYyI6MjI4MDA3NDJ9"
->    filter: {
->      emittingModule: "0x3::sui_system",
->      eventType: "0x3::validator::StakingRequestEvent"
->    }
->  ) {
->    pageInfo {
->      hasNextPage
->      endCursor
->    }
->    nodes {
->      sendingModule {
->        name
->      }
->      type {
->        repr
->      }
->      sender {
->        address
->      }
->      timestamp
->      json
->      bcs
->    }
->  }
->}
- -### -### Filter By Sender - ->
query ByTxSender {
->  events(
->    first: 1
->    filter: {
->      sender: "0xdff57c401e125a7e0e06606380560b459a179aacd08ed396d0162d57dbbdadfb"
->    }
->  ) {
->    pageInfo {
->      hasNextPage
->      endCursor
->    }
->    nodes {
->      sendingModule {
->        name
->      }
->      type {
->        repr
->      }
->      sender {
->        address
->      }
->      timestamp
->      json
->      bcs
->    }
->  }
->}
- -## -## Name Service -### -### Name Service - ->
{
->  resolveSuinsAddress(domain: "example.sui") {
->    address
->  }
->  address(
->    address: "0x0b86be5d779fac217b41d484b8040ad5145dc9ba0cba099d083c6cbda50d983e"
->  ) {
->    address
->    balance(type: "0x2::sui::SUI") {
->      coinType {
->        repr
->      }
->      coinObjectCount
->      totalBalance
->    }
->    defaultSuinsName
->  }
->}
- -## -## Object -### -### Object - ->
{
->  object(
->    address: "0x04e20ddf36af412a4096f9014f4a565af9e812db9a05cc40254846cf6ed0ad91"
->  ) {
->    address
->    version
->    digest
->    storageRebate
->    owner {
->      __typename
->      ... on Shared {
->        initialSharedVersion
->      }
->      __typename
->      ... on Parent {
->        parent {
->          address
->        }
->      }
->      __typename
->      ... on AddressOwner {
->        owner {
->          address
->        }
->      }
->    }
->    previousTransactionBlock {
->      digest
->    }
->  }
->}
- -## -## Object Connection -### -### Filter Object Ids -#### Filter on objectIds - ->
{
->  objects(filter: { objectIds: [
->    "0x4bba2c7b9574129c272bca8f58594eba933af8001257aa6e0821ad716030f149"
->  ]}) {
->    edges {
->      node {
->        storageRebate
->        owner {
->          __typename
->          ... on Shared {
->            initialSharedVersion
->          }
->          __typename
->          ... on Parent {
->            parent {
->              address
->            }
->          }
->          __typename
->          ... on AddressOwner {
->            owner {
->              address
->            }
->          }
->        }
->      }
->    }
->  }
->}
- -### -### Filter On Generic Type - ->
{
->  objects(filter: {type: "0x2::coin::Coin"}) {
->    edges {
->      node {
->        asMoveObject {
->          contents {
->            type { repr }
->          }
->        }
->      }
->    }
->  }
->}
- -### -### Filter On Type - ->
{
->  objects(filter: {type: "0x3::staking_pool::StakedSui"}) {
->    edges {
->      node {
->        asMoveObject {
->          contents {
->            type {
->              repr
->            }
->          }
->        }
->      }
->    }
->  }
->}
- -### -### Filter Owner -#### Filter on owner - ->
{
->  objects(filter: {
->    owner: "0x23b7b0e2badb01581ba9b3ab55587d8d9fdae087e0cfc79f2c72af36f5059439"
->  }) {
->    edges {
->      node {
->        storageRebate
->        owner {
->          __typename
->          ... on Shared {
->            initialSharedVersion
->          }
->          __typename
->          ... on Parent {
->            parent {
->              address
->            }
->          }
->          __typename
->          ... on AddressOwner {
->            owner {
->              address
->            }
->          }
->        }
->      }
->    }
->  }
->}
- -### -### Object Connection - ->
{
->  objects {
->    nodes {
->      version
->      digest
->      storageRebate
->      previousTransactionBlock {
->        digest
->        sender { defaultSuinsName }
->        gasInput {
->          gasPrice
->          gasBudget
->        }
->      }
->    }
->    pageInfo {
->      endCursor
->    }
->  }
->}
- -## -## Owner -### -### Dynamic Field - ->
fragment DynamicFieldValueSelection on DynamicFieldValue {
->  ... on MoveValue {
->    type {
->      repr
->    }
->    data
->    __typename
->  }
->  ... on MoveObject {
->    hasPublicTransfer
->    contents {
->      type {
->        repr
->      }
->      data
->    }
->    __typename
->  }
->}
->
->fragment DynamicFieldNameSelection on MoveValue {
->  type {
->    repr
->  }
->  data
->  bcs
->}
->
->fragment DynamicFieldSelect on DynamicField {
->  name {
->    ...DynamicFieldNameSelection
->  }
->  value {
->    ...DynamicFieldValueSelection
->  }
->}
->
->query DynamicField {
->  object(
->    address: "0xb57fba584a700a5bcb40991e1b2e6bf68b0f3896d767a0da92e69de73de226ac"
->  ) {
->    dynamicField(
->      name: {
->        type: "0x2::kiosk::Listing",
->        bcs: "NLArx1UJguOUYmXgNG8Pv8KbKXLjWtCi6i0Yeq1VhfwA",
->      }
->    ) {
->      ...DynamicFieldSelect
->    }
->  }
->}
- -### -### Dynamic Field Connection - ->
fragment DynamicFieldValueSelection on DynamicFieldValue {
->  ... on MoveValue {
->    type {
->      repr
->    }
->    data
->  }
->  ... on MoveObject {
->    hasPublicTransfer
->    contents {
->      type {
->        repr
->      }
->      data
->    }
->  }
->}
->
->fragment DynamicFieldNameSelection on MoveValue {
->  type {
->    repr
->  }
->  data
->  bcs
->}
->
->fragment DynamicFieldSelect on DynamicField {
->  name {
->    ...DynamicFieldNameSelection
->  }
->  value {
->    ...DynamicFieldValueSelection
->  }
->}
->
->query DynamicFields {
->  object(
->    address: "0xb57fba584a700a5bcb40991e1b2e6bf68b0f3896d767a0da92e69de73de226ac"
->  ) {
->    dynamicFields {
->      pageInfo {
->        hasNextPage
->        endCursor
->      }
->      edges {
->        cursor
->        node {
->          ...DynamicFieldSelect
->        }
->      }
->    }
->  }
->}
- -### -### Dynamic Object Field - ->
fragment DynamicFieldValueSelection on DynamicFieldValue {
->  ... on MoveValue {
->    type {
->      repr
->    }
->    data
->    __typename
->  }
->  ... on MoveObject {
->    hasPublicTransfer
->    contents {
->      type {
->        repr
->      }
->      data
->    }
->    __typename
->  }
->}
->
->fragment DynamicFieldNameSelection on MoveValue {
->  type {
->    repr
->  }
->  data
->  bcs
->}
->
->fragment DynamicFieldSelect on DynamicField {
->  name {
->    ...DynamicFieldNameSelection
->  }
->  value {
->    ...DynamicFieldValueSelection
->  }
->}
->
->query DynamicObjectField {
->  object(
->    address: "0xb57fba584a700a5bcb40991e1b2e6bf68b0f3896d767a0da92e69de73de226ac"
->  ) {
->    dynamicObjectField(
->      name: {type: "0x2::kiosk::Item", bcs: "NLArx1UJguOUYmXgNG8Pv8KbKXLjWtCi6i0Yeq1Vhfw="}
->    ) {
->      ...DynamicFieldSelect
->    }
->  }
->}
- -### -### Owner - ->
{
->  owner(
->    address: "0x931f293ce7f65fd5ebe9542653e1fd92fafa03dda563e13b83be35da8a2eecbe"
->  ) {
->    address
->  }
->}
- -## -## Protocol Configs -### -### Key Value -#### Select the key and value of the protocol configuration - ->
{
->  protocolConfig {
->    configs {
->      key
->      value
->    }
->  }
->}
- -### -### Key Value Feature Flag -#### Select the key and value of the feature flag - ->
{
->  protocolConfig {
->    featureFlags {
->      key
->      value
->    }
->  }
->}
- -### -### Specific Config -#### Select the key and value of the specific protocol configuration, in this case `max_move_identifier_len` - ->
{
->  protocolConfig {
->    config(key: "max_move_identifier_len") {
->      key
->      value
->    }
->  }
->}
- -### -### Specific Feature Flag - ->
{
->  protocolConfig {
->    protocolVersion
->    featureFlag(key: "advance_epoch_start_time_in_safe_mode") {
->      value
->    }
->  }
->}
- -## -## Service Config -### -### Service Config -#### Get the configuration of the running service - ->
{
->  serviceConfig {
->    isEnabled(feature: ANALYTICS)
->    enabledFeatures
->    maxQueryDepth
->    maxQueryNodes
->    maxDbQueryCost
->    defaultPageSize
->    maxPageSize
->    requestTimeoutMs
->    maxQueryPayloadSize
->  }
->}
- -## -## Stake Connection -### -### Stake Connection -#### Get all the staked objects for this address and all the active validators at the epoch when the stake became active - ->
{
->  address(
->    address: "0xc0a5b916d0e406ddde11a29558cd91b29c49e644eef597b7424a622955280e1e"
->  ) {
->    address
->    balance(type: "0x2::sui::SUI") {
->      coinType {
->        repr
->      }
->      totalBalance
->    }
->    stakedSuis {
->      nodes {
->        status
->        principal
->        estimatedReward
->        activatedEpoch {
->          epochId
->          referenceGasPrice
->          validatorSet {
->            activeValidators {
->              nodes {
->                name
->                description
->                exchangeRatesSize
->              }
->            }
->            totalStake
->          }
->        }
->        requestedEpoch {
->          epochId
->        }
->      }
->    }
->  }
->}
- -## -## Sui System State Summary -### -### Sui System State Summary -#### Get the latest sui system state data - ->
{
->  epoch {
->    storageFund {
->      totalObjectStorageRebates
->      nonRefundableBalance
->    }
->    safeMode {
->      enabled
->      gasSummary {
->         computationCost
->         storageCost
->         storageRebate
->         nonRefundableStorageFee
->      }
->    }
->    systemStateVersion
->    systemParameters {
->      durationMs
->      stakeSubsidyStartEpoch
->      minValidatorCount
->      maxValidatorCount
->      minValidatorJoiningStake
->      validatorLowStakeThreshold
->      validatorVeryLowStakeThreshold
->      validatorLowStakeGracePeriod
->    }
->    systemStakeSubsidy {
->      balance
->      distributionCounter
->      currentDistributionAmount
->      periodLength
->      decreaseRate
->
->    }
->  }
->}
- -## -## Transaction Block -### -### Transaction Block -#### Get the data for a TransactionBlock by its digest - ->
{
->  transactionBlock(digest: "HvTjk3ELg8gRofmB1GgrpLHBFeA53QKmUKGEuhuypezg") {
->    sender {
->      address
->    }
->    gasInput {
->      gasSponsor {
->        address
->      }
->      gasPayment {
->        nodes {
->          address
->        }
->      }
->      gasPrice
->      gasBudget
->    }
->    kind {
->      __typename
->    }
->    signatures
->    digest
->    expiration {
->      epochId
->    }
->    effects {
->      timestamp
->    }
->  }
->}
- -### -### Transaction Block Kind - ->
{
->  object(
->    address: "0xd6b9c261ab53d636760a104e4ab5f46c2a3e9cda58bd392488fc4efa6e43728c"
->  ) {
->    previousTransactionBlock {
->      sender {
->        address
->      }
->      kind {
->        __typename
->        ... on ConsensusCommitPrologueTransaction {
->          epoch {
->            epochId
->            referenceGasPrice
->          }
->          round
->          commitTimestamp
->          consensusCommitDigest
->        }
->        ... on ChangeEpochTransaction {
->          computationCharge
->          storageCharge
->          startTimestamp
->          storageRebate
->        }
->        ... on GenesisTransaction {
->          objects {
->            nodes { address }
->          }
->        }
->      }
->    }
->  }
->}
- -## -## Transaction Block Connection -### -### Before After Checkpoint -#### Filter on before_ and after_checkpoint. If both are provided, before must be greater than after - ->
{
->  transactionBlocks(
->    filter: { afterCheckpoint: 10, beforeCheckpoint: 20 }
->  ) {
->    nodes {
->      sender {
->        address
->      }
->      gasInput {
->        gasPrice
->        gasBudget
->      }
->    }
->  }
->}
- -### -### Changed Object Filter -#### Filter on changedObject - ->
{
->  transactionBlocks(
->    filter: {
->      changedObject: "0x0000000000000000000000000000000000000000000000000000000000000006"
->    }
->  ) {
->    nodes {
->      sender {
->        address
->      }
->      gasInput {
->        gasPrice
->        gasBudget
->      }
->    }
->  }
->}
- -### -### Input Object Filter -#### Filter on inputObject - ->
{
->  transactionBlocks(
->    filter: {
->      inputObject: "0x0000000000000000000000000000000000000000000000000000000000000006"
->    }
->  ) {
->    nodes {
->      sender {
->        address
->      }
->      gasInput {
->        gasPrice
->        gasBudget
->      }
->    }
->  }
->}
- -### -### Input Object Sign Addr Filter -#### multiple filters - ->
{
->  transactionBlocks(
->    filter: {
->      inputObject: "0x0000000000000000000000000000000000000000000000000000000000000006"
->      signAddress: "0x0000000000000000000000000000000000000000000000000000000000000000"
->    }
->  ) {
->    nodes {
->      sender {
->        address
->      }
->      effects {
->        gasEffects {
->          gasObject {
->            address
->          }
->        }
->      }
->      gasInput {
->        gasPrice
->        gasBudget
->      }
->    }
->  }
->}
- -### -### Package Filter -#### Filtering on package - ->
{
->  transactionBlocks(filter: { function: "0x3" }) {
->    nodes {
->      sender {
->        address
->      }
->      gasInput {
->        gasPrice
->        gasBudget
->      }
->    }
->  }
->}
- -### -### Package Module Filter -#### Filtering on package and module - ->
{
->  transactionBlocks(
->    filter: {
->      function: "0x3::sui_system"
->    }
->  ) {
->    nodes {
->      sender {
->        address
->      }
->      gasInput {
->        gasPrice
->        gasBudget
->      }
->    }
->  }
->}
- -### -### Package Module Func Filter -#### Filtering on package, module and function - ->
{
->  transactionBlocks(
->    filter: {
->      function: "0x3::sui_system::request_withdraw_stake"
->    }
->  ) {
->    nodes {
->      sender {
->        address
->      }
->      gasInput {
->        gasPrice
->        gasBudget
->      }
->    }
->  }
->}
- -### -### Recv Addr Filter -#### Filter on recvAddress - ->
{
->  transactionBlocks(
->    filter: {
->      recvAddress: "0x0000000000000000000000000000000000000000000000000000000000000000"
->    }
->  ) {
->    nodes {
->      sender {
->        address
->      }
->      gasInput {
->        gasPrice
->        gasBudget
->      }
->    }
->  }
->}
- -### -### Sign Addr Filter -#### Filter on signing address - ->
{
->  transactionBlocks(
->    filter: {
->      signAddress: "0x0000000000000000000000000000000000000000000000000000000000000000"
->    }
->  ) {
->    nodes {
->      sender {
->        address
->      }
->      gasInput {
->        gasPrice
->        gasBudget
->      }
->    }
->  }
->}
- -### -### Tx Ids Filter -#### Filter on transactionIds - ->
{
->  transactionBlocks(
->    filter: { transactionIds: ["DtQ6v6iJW4wMLgadENPUCEUS5t8AP7qvdG5jX84T1akR"] }
->  ) {
->    nodes {
->      sender {
->        address
->      }
->      gasInput {
->        gasPrice
->        gasBudget
->      }
->    }
->  }
->}
- -### -### Tx Kind Filter -#### Filter on TransactionKind (only SYSTEM_TX or PROGRAMMABLE_TX) - ->
{
->  transactionBlocks(filter: { kind: SYSTEM_TX }) {
->    nodes {
->      sender {
->        address
->      }
->      gasInput {
->        gasPrice
->        gasBudget
->      }
->    }
->  }
->}
- -### -### With Defaults Ascending -#### Fetch some default amount of transactions, ascending - ->
{
->  transactionBlocks {
->    nodes {
->      digest
->      effects {
->        gasEffects {
->          gasObject {
->            version
->            digest
->          }
->          gasSummary {
->            computationCost
->            storageCost
->            storageRebate
->            nonRefundableStorageFee
->          }
->        }
->        errors
->      }
->      sender {
->        address
->      }
->      gasInput {
->        gasPrice
->        gasBudget
->      }
->    }
->    pageInfo {
->      endCursor
->    }
->  }
->}
- -## -## Transaction Block Effects -### -### Transaction Block Effects - ->
{
->  object(
->    address: "0x0bba1e7d907dc2832edfc3bf4468b6deacd9a2df435a35b17e640e135d2d5ddc"
->  ) {
->    version
->    owner {
->      __typename
->      ... on Shared {
->        initialSharedVersion
->      }
->      __typename
->      ... on Parent {
->        parent {
->          address
->        }
->      }
->      __typename
->      ... on AddressOwner {
->        owner {
->          address
->        }
->      }
->    }
->    previousTransactionBlock {
->      effects {
->        status
->        checkpoint {
->          sequenceNumber
->        }
->        lamportVersion
->        gasEffects {
->          gasSummary {
->            computationCost
->            storageCost
->            storageRebate
->            nonRefundableStorageFee
->          }
->        }
->        balanceChanges {
->          nodes {
->            owner {
->              address
->              balance(type: "0x2::sui::SUI") {
->                totalBalance
->              }
->            }
->            amount
->            coinType {
->              repr
->              signature
->              layout
->            }
->          }
->        }
->        dependencies {
->          nodes {
->            sender {
->              address
->            }
->          }
->        }
->      }
->    }
->  }
->}
- diff --git a/crates/sui-graphql-rpc/schema/current_progress_schema.graphql b/crates/sui-graphql-rpc/schema.graphql similarity index 99% rename from crates/sui-graphql-rpc/schema/current_progress_schema.graphql rename to crates/sui-graphql-rpc/schema.graphql index 95fca9ce535b8..9a134382a7a70 100644 --- a/crates/sui-graphql-rpc/schema/current_progress_schema.graphql +++ b/crates/sui-graphql-rpc/schema.graphql @@ -2173,6 +2173,12 @@ type MovePackage implements IObject & IOwner { """ packageAtVersion(version: Int!): MovePackage """ + Fetch all versions of this package (packages that share this package's original ID), + optionally bounding the versions exclusively from below with `afterVersion`, or from above + with `beforeVersion`. + """ + packageVersions(first: Int, after: String, last: Int, before: String, filter: MovePackageVersionFilter): MovePackageConnection! + """ Fetch the latest version of this package (the package with the highest `version` that shares this packages's original ID) """ @@ -2246,6 +2252,22 @@ type MovePackageEdge { cursor: String! } +""" +Filter for paginating versions of a given `MovePackage`. +""" +input MovePackageVersionFilter { + """ + Fetch versions of this package that are strictly newer than this version. Omitting this + fetches versions since the original version. + """ + afterVersion: UInt53 + """ + Fetch versions of this package that are strictly older than this version. Omitting this + fetches versions up to the latest version (inclusive). + """ + beforeVersion: UInt53 +} + """ Description of a struct type, defined in a Move module. """ @@ -3139,6 +3161,12 @@ type Query { """ packages(first: Int, after: String, last: Int, before: String, filter: MovePackageCheckpointFilter): MovePackageConnection! """ + Fetch all versions of package at `address` (packages that share this package's original ID), + optionally bounding the versions exclusively from below with `afterVersion`, or from above + with `beforeVersion`. + """ + packageVersions(first: Int, after: String, last: Int, before: String, address: SuiAddress!, filter: MovePackageVersionFilter): MovePackageConnection! + """ Fetch the protocol config by protocol version (defaults to the latest protocol version known to the GraphQL service). """ diff --git a/crates/sui-graphql-rpc/schema/draft_target_schema.graphql b/crates/sui-graphql-rpc/schema/draft_target_schema.graphql deleted file mode 100644 index 733f13eb57bf7..0000000000000 --- a/crates/sui-graphql-rpc/schema/draft_target_schema.graphql +++ /dev/null @@ -1,1588 +0,0 @@ -# Copyright (c) Mysten Labs, Inc. -# SPDX-License-Identifier: Apache-2.0 - -# GraphQL Schema Draft -# -------------------- -# -# This is a draft design of the schema used by the second iteration of -# the RPC service. Note that some elements may not be complete, and -# others may exist in this schema but may not appear in the production -# design initially, or ever. -# -# The source of truth for the actual schema is accessed by querying -# the GraphQL server for its `__schema`. - -schema { - query: Query - subscription: Subscription - mutation: Mutation -} - -type Query { - # First four bytes of the network's genesis checkpoint digest - # (uniquely identifies the network) - chainIdentifier: String! - - # Range of checkpoints that the RPC has data available for (for data - # that can be tied to a particular checkpoint). - availableRange: AvailableRange! - - # Configuration for this RPC service - serviceConfig: ServiceConfig! - - # Simulate running a transaction to inspect its effects without - # committing to them on-chain. - # - # `txBytes` either a `TransactionData` struct or a `TransactionKind` - # struct, BCS-encoded and then Base64-encoded. The expected - # type is controlled by the presence or absence of `txMeta`: If - # present, `txBytes` is assumed to be a `TransactionKind`, if - # absent, then `TransactionData`. - # - # `txMeta` the data that is missing from a `TransactionKind` to make - # a `TransactionData` (sender address and gas information). All - # its fields are nullable: `sender` defaults to `0x0`, if - # `gasObjects` is not present, or is an empty list, it is - # substituted with a mock Coin object, and `gasPrice` defaults to - # the reference gas price. - # - # `skipChecks` optional flag to disable the usual verification - # checks that prevent access to objects that are owned by - # addresses other than the sender, and calling non-public, - # non-entry functions. Defaults to false. - dryRunTransactionBlock( - txBytes: Base64!, - txMeta: TransactionMetadata, - skipChecks: Boolean, - ): DryRunResult - - owner(address: SuiAddress!): Owner - object(address: SuiAddress!, version: Int): Object - address(address: SuiAddress!): Address - type(type: String!): MoveType! - - # Fetch epoch information by ID (defaults to the latest epoch). - epoch(id: Int): Epoch - - # `protocolVersion` defaults to the latest protocol version. - protocolConfig(protocolVersion: Int): ProtocolConfigs - - # Fetch checkpoint information by sequence number or digest - # (defaults to the latest available checkpoint). - checkpoint(id: CheckpointId): Checkpoint - - # Fetch a transaction block by its transaction digest - transactionBlock(digest: String!): TransactionBlock - - coinMetadata(coinType: String!): CoinMetadata - - checkpoints( - first: Int, - after: String, - last: Int, - before: String, - ): CheckpointConnection! - - coins( - first: Int, - after: String, - last: Int, - before: String, - type: String, - ): CoinConnection! - - transactionBlocks( - first: Int, - after: String, - last: Int, - before: String, - filter: TransactionBlockFilter, - ): TransactionBlockConnection! - - events( - first: Int, - after: String, - last: Int, - before: String, - filter: EventFilter, - ): EventConnection! - - objects( - first: Int, - after: String, - last: Int, - before: String, - filter: ObjectFilter, - ): ObjectConnection! - - resolveSuinsAddress(name: String!): Address - - # NB. Will be moved into a private, explorer-specific extension. - networkMetrics: NetworkMetrics - moveCallMetrics: MoveCallMetrics - - allEpochAddressMetrics( - first: Int, - after: String, - last: Int, - before: String, - ): AddressMetricsConnection! -} - -# NB. Add after MVP has stabilised. -# -# Subscriptions use a "push-pull" system: Subscribers are notified -# when there is new data by being sent the cursor pointing after that -# new data. To actually fetch the data, a call must be made to the -# equivalent Connection API: -# -# e.g. When subscription `subscribe { events(filter: F) }` pushes -# cursor `E`. Then -# -# query { events(before: E, filter: F) } -# -# Will start paginating events up to the new data (multiple calls may -# be required if there are multiple pages of information between the -# start and the latest). If the client has already processed some -# prefix, up to cursor `P`, then they can resume with: -# -# query { events(after: P, before: E, filter: F) } -# -# The API for transactions is similar. -type Subscription { - events(filter: EventFilter): String! - transactions(filter: TransactionBlockFilter): String! -} - -type Mutation { - # Execute a transaction, committing its effects on chain. - # - # `txBytes` is a `TransactionData` struct that has been BCS-encoded - # and then Base64-encoded. - # `signatures` are a list of `flag || signature || pubkey` bytes, - # Base64-encoded. - # - # Waits until the transaction has been finalized on chain to return - # its transaction digest. If the transaction could not be - # finalized, returns the errors that prevented it, instead. - executeTransactionBlock( - txBytes: Base64!, - signatures: [Base64!]!, - ): ExecutionResult -} - -# String containing 32B hex-encoded address, with a leading "0x". -# Leading zeroes can be omitted on input but will always appear in -# outputs (SuiAddress in output is guaranteed to be 66 characters -# long). -scalar SuiAddress - -# String representation of an arbitrary width, possibly signed integer -scalar BigInt - -# String containing Base64-encoded binary data. -scalar Base64 - -# ISO-8601 Date and Time -scalar DateTime - -# Arbitrary JSON data -scalar JSON - -# Scalar representing the contents of a Move Value, corresponding to -# the following recursive type: -# -# type MoveData = -# { Number: BigInt } -# | { Bool: bool } -# | { Address: SuiAddress } -# | { UID: SuiAddress } -# | { ID: SuiAddress } -# | { String: string } -# | { Vector: [MoveData] } -# | { Option: MoveData? } -# | { Struct: [{ name: string, value: MoveData }] } -scalar MoveData - -# The signature of a concrete Move Type (a type with all its type -# parameters instantiated with concrete types, that contains no -# references), corresponding to the following recursive type: -# -# type MoveTypeSignature = -# "address" -# | "bool" -# | "u8" | "u16" | ... | "u256" -# | { vector: MoveTypeSignature } -# | { -# struct: { -# package: string, -# module: string, -# type: string, -# typeParameters: [MoveTypeSignature], -# } -# } -scalar MoveTypeSignature - -# The shape of a concrete Move Type (a type with all its type -# parameters instantiated with concrete types), corresponding to the -# following recursive type: -# -# type MoveTypeLayout = -# "address" -# | "bool" -# | "u8" | "u16" | ... | "u256" -# | { vector: MoveTypeLayout } -# | { -# struct: { -# type: string, -# fields: [{ name: string, layout: MoveTypeLayout }], -# } -# } -scalar MoveTypeLayout - -# The shape of an abstract Move Type (a type that can contain free -# type parameters, and can optionally be taken by reference), -# corresponding to the following recursive type: -# -# type OpenMoveTypeSignature = { -# ref: ("&" | "&mut")?, -# body: OpenMoveTypeSignatureBody, -# } -# -# type OpenMoveTypeSignatureBody = -# "address" -# | "bool" -# | "u8" | "u16" | ... | "u256" -# | { vector: OpenMoveTypeSignatureBody } -# | { -# struct: { -# package: string, -# module: string, -# type: string, -# typeParameters: [OpenMoveTypeSignatureBody]? -# } -# } -# | { typeParameter: number } -scalar OpenMoveTypeSignature - -# The extra data required to turn a `TransactionKind` into a -# `TransactionData` in a dry-run. -input TransactionMetadata { - sender: SuiAddress - gasPrice: Int - gasBudget: Int - gasObjects: [ObjectRef!] - gasSponsor: SuiAddress -} - -# A reference to a particular version of an object. -input ObjectRef { - address: SuiAddress! - version: Int! - digest: String! -} - -# Filter either by the digest, or the sequence number, or neither, to -# get the latest checkpoint. -input CheckpointId { - digest: String - sequenceNumber: Int -} - -input ObjectFilter { - # This field is used to specify the type of objects that should be - # include in the query results. - # - # Objects can be filtered by their type's package, package::module, - # or their fully qualified type name. - # - # Generic types can be queried by either the generic type name, e.g. - # `0x2::coin::Coin`, or by the full type name, such as - # `0x2::coin::Coin<0x2::sui::SUI>`. - type: String - - # Filter for live objects by their current owners. - owner: SuiAddress - - # Filter for live objects by their IDs. - objectIds: [SuiAddress!] - - # Filter for live or potentially historical objects by their ID and version. - objectKeys: [ObjectKey!] - - # Enhancement (post-MVP), compound filters. Compound filters are - # exclusive (must be the only filter set if they are used). - any: [ObjectFilter] - all: [ObjectFilter] - not: ObjectFilter -} - -input ObjectKey { - objectId: SuiAddress! - version: Int! -} - -input EventFilter { - sender: SuiAddress - transactionDigest: String - # Enhancement (post-MVP), requires compound filters to be useful. - afterCheckpoint: Int - beforeCheckpoint: Int - - # Events emitted by a particular module. An event is emitted by a - # particular module if some function in the module is called by a - # PTB and emits an event. - # - # Modules can be filtered by their package, or package::module. - emittingModule: String - - # This field is used to specify the type of event emitted. - # - # Events can be filtered by their type's package, package::module, - # or their fully qualified type name. - # - # Generic types can be queried by either the generic type name, e.g. - # `0x2::coin::Coin`, or by the full type name, such as - # `0x2::coin::Coin<0x2::sui::SUI>`. - eventType: String - - # Enhancement (post-MVP), requires compound filters to be useful. - startTime: DateTime - endTime: DateTime - - # Enhancement (post-MVP), compound filters. Compound filters are - # exclusive (must be the only filter set if they are used). - any: [EventFilter] - all: [EventFilter] - not: EventFilter -} - -input TransactionBlockFilter { - # Filter by the function called. Limited to an individual package, - # package::module, or package::module::function. - function: String - - kind: TransactionBlockKindInput - afterCheckpoint: Int - beforeCheckpoint: Int - - signAddress: SuiAddress - sentAddress: SuiAddress - recvAddress: SuiAddress - paidAddress: SuiAddress - - inputObject: SuiAddress - changedObject: SuiAddress - - transactionIDs: [String!] - - # Enhancement (post-MVP), consistency with EventFilter -- timestamp - # comes from checkpoint timestamp. - startTime: DateTime - endTime: DateTime - - # Enhancement (post-MVP), compound filters. Compound filters are - # exclusive (must be the only filter set if they are used). - any: [TransactionBlockFilter] - all: [TransactionBlockFilter] - not: TransactionBlockFilter -} - -input DynamicFieldFilter { - # Filter the type of dynamic field name. - # - # Names can be filtered by their type's package, package::module, or - # their fully qualified type name. - # - # Generic types can be queried by either the generic type name, e.g. - # `0x2::coin::Coin`, or by the full type name, such as - # `0x2::coin::Coin<0x2::sui::SUI>`. - nameType: String - - # Filter the type of dynamic field value. - # - # Values can be filtered by their type's package, package::module, - # or their fully qualified type name. - # - # Generic types can be queried by either the generic type name, e.g. - # `0x2::coin::Coin`, or by the full type name, such as - # `0x2::coin::Coin<0x2::sui::SUI>`. - valueType: String -} - -type AvailableRange { - first: Checkpoint - last: Checkpoint -} - -type ServiceConfig { - availableVersions: [String!] - enabledFeatures: [Feature!] - isEnabled(feature: Feature!): Boolean! - - maxQueryDepth: Int! - maxQueryNodes: Int! - maxOutputNodes: Int! - defaultPageSize: Int! - maxPageSize: Int! - requestTimeoutMs: Int! - maxQueryPayloadSize: Int! -} - -enum Feature { - ANALYTICS - COINS - DYNAMIC_FIELDS - NAME_SERVICE - SUBSCRIPTIONS - SYSTEM_STATE -} - -interface IOwner { - address: SuiAddress! - - objects( - first: Int, - after: String, - last: Int, - before: String, - # Enhancement (post-MVP) relies on compound filters. - filter: ObjectFilter, - ): MoveObjectConnection! - - balance(type: String!): Balance - balances( - first: Int, - after: String, - last: Int, - before: String, - ): BalanceConnection! - - # `type` defaults to `0x2::sui::SUI`. - coins( - first: Int, - after: String, - last: Int, - before: String, - type: String, - ): CoinConnection! - - stakedSuis( - first: Int, - after: String, - last: Int, - before: String, - ): StakedSuiConnection! - - dynamicField(dynamicFieldName: DynamicFieldName!): DynamicField - dynamicObjectField(dynamicFieldName: DynamicFieldName!): DynamicField - dynamicFields( - first: Int, - after: String, - last: Int, - before: String, - # Enhancement (post-MVP) to filter dynamic fields by type. - filter: DynamicFieldFilter, - ): DynamicFieldConnection! - - defaultSuinsName: String - suinsRegistrations( - first: Int, - after: String, - last: Int, - before: String, - ): SuinsRegistrationConnection! -} - -union ObjectOwner = Immutable | Shared | Parent | AddressOwner - -type Immutable { - # Dummy field - _: Boolean -} - -type Shared { - initialSharedVersion: Int! -} - -type Parent { - # Child objects are an implementation-detail of dynamic fields. Only - # another object can be a parent of a child object (not an address). - parent: Object -} - -type AddressOwner { - # The address that owns an object could be an Address, or an Object. - owner: Owner -} - -interface IObject { - version: Int! - digest: String! - owner: ObjectOwner - - previousTransactionBlock: TransactionBlock - storageRebate: BigInt - - display: [DisplayEntry!] - - # Transaction Blocks that sent objects to this object - receivedTransactionBlocks( - first: Int, - after: String, - last: Int, - before: String, - # Enhancement (post-MVP) relies on compound filters. - filter: TransactionBlockFilter, - ): TransactionBlockConnection! - - bcs: Base64 -} - -interface IMoveObject { - contents: MoveValue -} - -# Returned by Object.owner, where we can't disambiguate between -# Address and Object. -type Owner implements IOwner { - asAddress: Address - asObject: Object -} - -type Address implements IOwner { - transactionBlocks( - first: Int, - after: String, - last: Int, - before: String, - relation: AddressTransactionBlockRelationship, - # Enhancement (post-MVP) relies on compound filters. - filter: TransactionBlockFilter, - ): TransactionBlockConnection! -} - -enum AddressTransactionBlockRelationship { - SIGN # Transactions this address has signed - SENT # Transactions that transferred objects from this address - RECV # Transactions that received objects into this address - PAID # Transactions that were paid for by this address -} - -type Object implements IOwner & IObject { - asMoveObject: MoveObject - asMovePackage: MovePackage -} - -type DisplayEntry { - key: String! - value: String - error: String -} - -type Epoch { - epochId: Int! - protocolConfigs: ProtocolConfigs - referenceGasPrice: BigInt - validatorSet: ValidatorSet - - startTimestamp: DateTime! - endTimestamp: DateTime - - totalCheckpoints: BigInt - totalGasFees: BigInt - totalStakeRewards: BigInt - totalStakeSubsidies: BigInt - fundSize: BigInt - netInflow: BigInt - fundInflow: BigInt - fundOutflow: BigInt - - # SystemState fields - storageFund: StorageFund - safeMode: SafeMode - systemStateVersion: BigInt - systemParameters: SystemParameters - systemStakeSubsidy: StakeSubsidy - - checkpoints( - first: Int, - after: String, - last: Int, - before: String, - ): CheckpointConnection! - - transactionBlocks( - first: Int, - after: String, - last: Int, - before: String, - # Enhancement (post-MVP) relies on compound filters. - filter: TransactionBlockFilter, - ): TransactionBlockConnection! -} - -type ProtocolConfigs { - protocolVersion: Int! - featureFlags: [ProtocolConfigFeatureFlag!]! - configs: [ProtocolConfigAttr!]! - config(key: String!): ProtocolConfigAttr - featureFlag(key: String!): ProtocolConfigFeatureFlag -} - -type ProtocolConfigAttr { - key: String! - value: String! -} - -type ProtocolConfigFeatureFlag { - key: String! - value: Boolean! -} - -type SystemParameters { - durationMs: BigInt - stakeSubsidyStartEpoch: Int - - minValidatorCount: Int - maxValidatorCount: Int - - minValidatorJoiningStake: BigInt - validatorLowStakeThreshold: BigInt - validatorVeryLowStakeThreshold: BigInt - validatorLowStakeGracePeriod: Int -} - -type StakeSubsidy { - balance: BigInt - distributionCounter: Int - currentDistributionAmount: BigInt - periodLength: Int - decreaseRate: Int -} - -type ValidatorSet { - totalStake: BigInt - - activeValidators( - first: Int, - after: String, - last: Int, - before: String - ): ValidatorConnection! - - # Indices into `activeValidators` - pendingRemovals: [Int] - - pendingActiveValidators: MoveObject - pendingActiveValidatorsSize: Int - - stakePoolMappings: MoveObject - stakePoolMappingsSize: Int - - inactivePools: MoveObject - inactivePoolsSize: Int - - validatorCandidates: MoveObject - validatorCandidatesSize: Int -} - -type Validator { - address: Address! - - credentials: ValidatorCredentials - nextEpochCredentials: ValidatorCredentials - - name: String - description: String - imageUrl: String - projectUrl: String - - operationCap: MoveObject - stakingPool: MoveObject - - exchangeRates: MoveObject - exchangeRatesSize: Int - - stakingPoolActivationEpoch: Int - stakingPoolSuiBalance: BigInt - rewardsPool: BigInt - poolTokenBalance: BigInt - pendingStake: BigInt - pendingTotalSuiWithdraw: BigInt - pendingPoolTokenWithdraw: BigInt - - votingPower: Int - stakeUnits: Int - gasPrice: BigInt - commissionRate: Int - nextEpochStake: BigInt - nextEpochGasPrice: BigInt - nextEpochCommissionRate: Int - - # The number of epochs for which this validator has been below the - # low stake threshold. - atRisk: Int - - # The other validators this validator has reported - reportRecords: [SuiAddress!] - - apy: Int -} - -type ValidatorCredentials { - protocolPubKey: Base64 - networkPubKey: Base64 - workerPubKey: Base64 - proofOfPossession: Base64 - - netAddress: String - p2pAddreess: String - primaryAddress: String - workerAddress: String -} - -type StorageFund { - totalObjectStorageRebates: BigInt - nonRefundableBalance: BigInt -} - -type SafeMode { - enabled: Boolean - gasSummary: GasCostSummary -} - -type Checkpoint { - digest: String! - sequenceNumber: Int! - - timestamp: DateTime! - validatorSignatures: Base64 - - # Commitments - previousCheckpointDigest: String - liveObjectSetDigest: String - - networkTotalTransactions: Int - rollingGasSummary: GasCostSummary - - epoch: Epoch - - transactionBlocks( - first: Int, - after: String, - last: Int, - before: String, - # Enhancement (post-MVP) relies on compound filters. - filter: TransactionBlockFilter, - ): TransactionBlockConnection! - - # NB. Will be moved into a private, explorer-specific extension. - addressMetrics: AddressMetrics -} - -type TransactionBlock { - digest: String - - sender: Address - gasInput: GasInput - kind: TransactionBlockKind - signatures: [Base64!] - effects: TransactionBlockEffects - - expiration: Epoch - - bcs: Base64 -} - -enum TransactionBlockKindInput { - PROGRAMMABLE_TX - SYSTEM_TX -} - -union TransactionBlockKind = - ConsensusCommitPrologueTransaction - | GenesisTransaction - | ChangeEpochTransaction - | ProgrammableTransactionBlock - | AuthenticatorStateUpdateTransaction - | RandomnessStateUpdateTransaction - | EndOfEpochTransaction - -type ConsensusCommitPrologueTransaction { - epoch: Epoch! - round: Int! - commitTimestamp: DateTime! - consensusCommitDigest: String -} - -type GenesisTransaction { - objects( - first: Int, - after: String, - last: Int, - before: String, - ): ObjectConnection! -} - -type ChangeEpochTransaction { - epoch: Epoch - protocolVersion: Int! - startTimestamp: DateTime! - - storageCharge: BigInt! - computationCharge: BigInt! - storageRebate: BigInt! - nonRefundableStorageFee: BigInt! - - systemPackages( - first: Int, - after: String, - last: Int, - before: String, - ): MovePackageConnection! -} - -type ProgrammableTransactionBlock { - inputs( - first: Int, - after: String, - last: Int, - before: String, - ): TransactionInputConnection! - - transactions( - first: Int, - after: String, - last: Int, - before: String, - ): ProgrammableTransactionConnection! -} - -union TransactionInput = OwnedOrImmutable | SharedInput | Receiving | Pure - -type OwnedOrImmutable { - address: SuiAddress! - version: Int! - digest: String! - object: Object -} - -type SharedInput { - address: SuiAddress! - initialSharedVersion: Int! - mutable: Boolean! -} - -type Receiving { - address: SuiAddress! - version: Int! - digest: String! - object: Object -} - -type Pure { - bytes: Base64! -} - -union TransactionArgument = GasCoin | Input | Result - -type GasCoin { _: Boolean } -type Input { ix: Int! } -type Result { cmd: Int!, ix: Int } - -union ProgrammableTransaction = - MoveCallTransaction - | TransferObjectsTransaction - | SplitCoinTransaction - | MergeCoinsTransaction - | PublishTransaction - | UpgradeTransaction - | MakeMoveVecTransaction - -type MoveCallTransaction { - package: SuiAddress! - module: String! - functionName: String! - function: MoveFunction - typeArguments: [MoveType!]! - arguments: [TransactionArgument!]! -} - -type TransferObjectsTransaction { - objects: [TransactionArgument!]! - address: TransactionArgument! -} - -type SplitCoinsTransaction { - coin: TransactionArgument! - amounts: [TransactionArgument!]! -} - -type MergeCoinsTransaction { - coin: TransactionArgument! - coins: [TransactionArgument!]! -} - -type PublishTransaction { - modules: [Base64!]! - dependencies: [SuiAddress!]! -} - -type UpgradeTransaction { - modules: [Base64!]! - dependencies: [SuiAddress!]! - currentPackage: SuiAddress! - upgradeTicket: TransactionArgument! -} - -type MakeMoveVecTransaction { - type: MoveType - elements: [TransactionArgument!]! -} - -type TransactionBlockEffects { - transactionBlock: TransactionBlock! - status: ExecutionStatus - - errors: String - dependencies( - first: Int, - after: String, - last: Int, - before: String, - ): TransactionBlockConnection! - - lamportVersion: Int - gasEffects: GasEffects - - unchangedSharedObjects( - first: Int, - after: String, - last: Int, - before: String, - ): UnchangedSharedObjectConnection! - - objectChanges( - first: Int, - after: String, - last: Int, - before: String, - ): ObjectChangeConnection! - - balanceChanges( - first: Int, - after: String, - last: Int, - before: String, - ): BalanceChangeConnection! - - timestamp: DateTime - epoch: Epoch - checkpoint: Checkpoint - - events( - first: Int, - after: String, - last: Int, - before: String, - # Extension (post-MVP) relies on compound filters - filter: EventFilter, - ): EventConnection! - - bcs: Base64 -} - -enum ExecutionStatus { - SUCCESS - FAILURE -} - -type GasInput { - gasSponsor: Address - gasPayment( - first: Int, - after: String, - last: Int, - before: String, - ): ObjectConnection! - - gasPrice: BigInt - gasBudget: BigInt -} - -type GasEffects { - gasObject: Coin - gasSummary: GasCostSummary -} - -type GasCostSummary { - computationCost: BigInt - storageCost: BigInt - storageRebate: BigInt - nonRefundableStorageFee: BigInt -} - -union UnchangedSharedObject = SharedObjectRead | SharedObjectDelete - -type SharedObjectRead { - address: SuiAddress! - version: u64! - digest: String! - object: Object -} - -type SharedObjectDelete { - address: SuiAddress! - version: u64! - - # Whether this transaction intended to use this shared object - # mutably or not. - mutable: Boolean! -} - -type ObjectChange { - address: SuiAddress! - - inputState: Object - outputState: Object - - idCreated: Boolean - idDeleted: Boolean -} - -type BalanceChange { - owner: Owner - coinType: MoveType - amount: BigInt -} - -type Event { - # Module that the event was emitted by - sendingModule: MoveModule - - sender: Address - timestamp: DateTime - - type: MoveType! - bcs: Base64! - data: MoveData! - json: JSON! -} - -type Balance { - coinType: MoveType - coinObjectCount: Int - totalBalance: BigInt -} - -type Coin implements IOwner & IObject { - coinBalance: BigInt -} - -type StakedSui implements IOwner & IObject { - stakeStatus: StakeStatus! - requestEpoch: Epoch - activeEpoch: Epoch - principal: BigInt - - # Only available if status is `ACTIVE`. - estimatedReward: BigInt -} - -enum StakeStatus { - PENDING - ACTIVE - UNSTAKED -} - -type CoinMetadata implements IOwner & IObject { - decimals: Int - name: String - symbol: String - description: String - iconUrl: String - supply: BigInt -} - -input DynamicFieldName { - type: String! - bcs: Base64! -} - -type DynamicField { - name: MoveValue - value: DynamicFieldValue -} - -union DynamicFieldValue = MoveObject | MoveValue - -type MoveObject implements IOwner & IObject & IMoveObject { - asCoin: Coin - asStakedSui: StakedSui - asCoinMetadata: CoinMetadata - asSuinsRegistration: SuinsRegistration -} - -type MovePackage implements IOwner & IObject { - module(name: String!): MoveModule - modules( - first: Int, - after: String, - last: Int, - before: String, - ): MoveModuleConnection! - - linkage: [Linkage!] - typeOrigins: [TypeOrigin!] - - moduleBcs: Base64 -} - -type Linkage { - originalId: SuiAddress! - upgradedId: SuiAddress! - version: Int! -} - -type TypeOrigin { - module: String! - struct: String! - definingId: SuiAddress! -} - -enum MoveAbility { - COPY - DROP - STORE - KEY -} - -enum MoveVisibility { - PUBLIC - PRIVATE - FRIEND -} - -type MoveStructTypeParameter { - constraints: [MoveAbility!]! - isPhantom: Boolean! -} - -type MoveFunctionTypeParameter { - constraints: [MoveAbility!]! -} - -type MoveModule { - package: SuiAddress! - name: String! - - fileFormatVersion: Int! - - friends( - first: Int, - after: String, - last: Int, - before: String - ): MoveModuleConnection! - - struct(name: String!): MoveStruct - structs( - first: Int, - after: String, - last: Int, - before: String, - ): MoveStructConnection! - - function(name: String!): MoveFunction - functions( - first: Int, - after: String, - last: Int, - before: String, - ): MoveFunctionConnection! - - bytes: Base64 - disassembly: String -} - -type MoveStruct { - module: MoveModule! - name: String! - abilities: [MoveAbility!] - typeParameters: [MoveStructTypeParameter!] - fields: [MoveField!] -} - -type MoveField { - name: String! - type: OpenMoveType -} - -type MoveFunction { - module: MoveModule! - name: String! - - visibility: MoveVisibility - isEntry: Boolean - - typeParameters: [MoveFunctionTypeParameter!] - parameters: [OpenMoveType!] - return: [OpenMoveType!] -} - -type MoveValue { - type: MoveType! - data: MoveData! - json: JSON! - - bcs: Base64! -} - -# Represents concrete types (no type parameters, no references) -type MoveType { - # Flat representation of the type signature, as a displayable string. - repr: String! - # Structured representation of the type signature. - signature: MoveTypeSignature! - # Structured representation of the "shape" of values that match this type. - layout: MoveTypeLayout! - # The abilities this concrete type has. - abilities: [MoveAbility!]! -} - -# Represents types that could contain references or free type -# parameters. Such types can appear as function parameters, or fields -# in structs. -type OpenMoveType { - # Flat representation of the type signature, as a displayable string. - repr: String! - # Structured representation of the type signature. - signature: OpenMoveTypeSignature! -} - -# Metrics (omitted for brevity) -type NetworkMetrics -type MoveCallMetrics -type AddressMetrics - -# Execution - -# Either TransactionBlockEffects on success, or error on failure. -type ExecutionResult { - effects: TransactionBlockEffects - errors: [String!] -} - -type DryRunResult { - transaction: TransactionBlock - error: String - results: [DryRunEffect!] -} - -type DryRunEffect { - # Changes made to arguments that were mutably borrowed by this - # transaction - mutatedReferences: [DryRunMutation!] - - # Results of this transaction - returnValues: [DryRunReturn!] -} - -type DryRunMutation { - input: TransactionArgument - type: MoveType - bcs: Base64 -} - -type DryRunReturn { - type: MoveType - bcs: Base64 -} - -# Connections - -# Pagination -type PageInfo { - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - endCursor: String -} - -# Checkpoints -type CheckpointConnection { - edges: [CheckpointEdge!]! - nodes: [Checkpoint!]! - pageInfo: PageInfo! -} - -type CheckpointEdge { - cursor: String - node: Checkpoint! -} - -# Balance -type BalanceConnection { - edges: [BalanceEdge!]! - nodes: [Balance!]! - pageInfo: PageInfo! -} - -type BalanceEdge { - cursor: String - node: Balance! -} - -# Coin -type CoinConnection { - edges: [CoinEdge!]! - nodes: [Coin!]! - pageInfo: PageInfo! -} - -type CoinEdge { - cursor: String - node: Coin! -} - -# DynamicField -type DynamicFieldConnection { - edges: [DynamicFieldEdge!]! - nodes: [DynamicField!]! - pageInfo: PageInfo! -} - -type DynamicFieldEdge { - cursor: String - node: DynamicField! -} - -# Object -type ObjectConnection { - edges: [ObjectEdge!]! - nodes: [Object!]! - pageInfo: PageInfo! -} - -type ObjectEdge { - cursor: String - node: Object! -} - -# MoveObject -type MoveObjectConnection { - edges: [MoveObjectEdge!]! - nodes: [MoveObject!]! - pageInfo: PageInfo! -} - -type MoveObjectEdge { - cursor: String - node: MoveObject! -} - -# MovePackage -type MovePackageConnection { - edges: [MovePackageEdge!]! - nodes: [MovePackage!]! - pageInfo: PageInfo! -} - -type MovePackageEdge { - cursor: String - node: MovePackage! -} - -# Event -type EventConnection { - edges: [EventEdge!]! - nodes: [Event!]! - pageInfo: PageInfo! -} - -type EventEdge { - cursor: String - node: Event! -} - -# MoveFunction -type MoveFunctionConnection { - edges: [MoveFunctionEdge!]! - nodes: [MoveFunction!]! - pageInfo: PageInfo! -} - -type MoveFunctionEdge { - cursor: String - node: MoveFunction! -} - -# MoveModuleConnection -type MoveModuleConnection { - edges: [MoveModuleEdge] - nodes: [MoveModule] - pageInfo: PageInfo! -} - -type MoveModuleEdge { - cursor: String - node: MoveModule -} - -# MoveStructConnection -type MoveStructConnection { - edges: [MoveStructEdge!]! - nodes: [MoveStruct!]! - pageInfo: PageInfo! -} - -type MoveStructEdge { - cursor: String - node: MoveStruct! -} - -# TransactionBlockConnection -type TransactionBlockConnection { - totalTransactionBlocks: Int - edges: [TransactionBlockEdge!]! - nodes: [TransactionBlock!]! - pageInfo: PageInfo! -} - -type TransactionBlockEdge { - cursor: String - node: TransactionBlock! -} - -# TransactionInputConnection -type TransactionInputConnection { - edges: [TransactionInputEdge!]! - nodes: [TransactionInput!]! - pageInfo: PageInfo! -} - -type TransactionInputEdge { - cursor: String - node: TransactionInput! -} - -# ProgrammableTransactionConnection -type ProgrammableTransactionConnection { - edges: [ProgrammableTransactionEdge!]! - nodes: [ProgrammableTransaction!]! - pageInfo: PageInfo! -} - -type ProgrammableTransactionEdge { - cursor: String - node: ProgrammableTransaction! -} - -# UnchangedSharedObjectConnection - -type UnchangedSharedObjectConnection { - edges: [UnchangedSharedObjectEdge!]! - nodes: [UnchangedSharedObject!]! - pageInfo: PageInfo! -} - -type UnchangedSharedObjectEdge { - cursor: String - node: UnchangedSharedObject! -} - -# ObjectChangeConnection -type ObjectChangeConnection { - edges: [ObjectChangeEdge!]! - nodes: [ObjectChange!]! - pageInfo: PageInfo! -} - -type ObjectChangeEdge { - cursor: String - node: ObjectChange -} - -# BalanceChangeConnection -type BalanceChangeConnection { - edges: [BalanceChangeEdge!]! - nodes: [BalanceChange!]! - pageInfo: PageInfo! -} - -type BalanceChangeEdge { - cursor: String - node: BalanceChange -} - -# MoveModuleConnection -type MoveModuleConnection { - edges: [MoveModuleEdge!]! - nodes: [MoveModule!]! - pageInfo: PageInfo! -} - -type MoveModuleEdge { - cursor: String - node: MoveModule! -} - -# SuinsRegistrationConnection -type SuinsRegistrationConnection { - edges: [SuinsRegistrationEdge!]! - nodes: [SuinsRegistration!]! - pageInfo: PageInfo! -} - -type SuinsRegistrationEdge { - cursor: String - node: SuinsRegistration -} - -type SuinsRegistration { - """ - Domain name of the SuinsRegistration object - """ - domain: String! - """ - Convert the SuinsRegistration object into a Move object - """ - asMoveObject: MoveObject! -} - -# AddressMetricsConnection -type AddressMetricsConnection { - edges: [AddressMetricEdge!]! - nodes: [AddressMetric!]! - pageInfo: PageInfo! -} - -type AddressMetricEdge { - cursor: String - node: AddressMetrics! -} - -# StakedSuiConnection -type StakedSuiConnection { - edges: [StakedSuiEdge!]! - nodes: [StakedSui!]! - pageInfo: PageInfo! -} - -type StakedSuiEdge { - cursor: String - node: StakedSui! -} - -# ValidatorConnection -type ValidatorConnection { - edges: [ValidatorEdge!]! - nodes: [Validator!]! - pageInfo: PageInfo! -} - -type ValidatorEdge { - cursor: String - node: Validator! -} diff --git a/crates/sui-graphql-rpc/src/commands.rs b/crates/sui-graphql-rpc/src/commands.rs index f605efd735946..bda0c2f561fab 100644 --- a/crates/sui-graphql-rpc/src/commands.rs +++ b/crates/sui-graphql-rpc/src/commands.rs @@ -13,17 +13,6 @@ use std::path::PathBuf; version )] pub enum Command { - GenerateDocsExamples, - GenerateSchema { - /// Path to output GraphQL schema to, in SDL format. - #[clap(short, long)] - file: Option, - }, - GenerateExamples { - /// Path to output examples docs. - #[clap(short, long)] - file: Option, - }, StartServer { /// The title to display at the top of the page #[clap(short, long)] diff --git a/crates/sui-graphql-rpc/src/examples.rs b/crates/sui-graphql-rpc/src/examples.rs deleted file mode 100644 index 56f86ca428180..0000000000000 --- a/crates/sui-graphql-rpc/src/examples.rs +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use anyhow::anyhow; -use markdown_gen::markdown::{AsMarkdown, Markdown}; -use std::io::{BufWriter, Read}; -use std::path::PathBuf; - -#[derive(Debug)] -pub struct ExampleQuery { - pub name: String, - pub contents: String, - pub path: PathBuf, -} - -#[derive(Debug)] -pub struct ExampleQueryGroup { - pub name: String, - pub queries: Vec, - pub _path: PathBuf, -} - -const QUERY_EXT: &str = "graphql"; - -fn regularize_string(s: &str) -> String { - // Replace underscore with space and make every word first letter uppercase - s.replace('_', " ") - .split_whitespace() - .map(|word| { - let mut chars = word.chars(); - match chars.next() { - None => String::new(), - Some(f) => f.to_uppercase().chain(chars).collect(), - } - }) - .collect::>() - .join(" ") -} - -pub fn load_examples() -> anyhow::Result> { - let mut buf: PathBuf = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - buf.push("examples"); - - let mut groups = vec![]; - for entry in std::fs::read_dir(buf).map_err(|e| anyhow::anyhow!(e))? { - let entry = entry.map_err(|e| anyhow::anyhow!(e))?; - let path = entry.path(); - let group_name = path - .file_stem() - .ok_or(anyhow::anyhow!("File stem cannot be read"))? - .to_str() - .ok_or(anyhow::anyhow!("File stem cannot be read"))? - .to_string(); - - let mut group = ExampleQueryGroup { - name: group_name.clone(), - queries: vec![], - _path: path.clone(), - }; - - for file in std::fs::read_dir(path).map_err(|e| anyhow::anyhow!(e))? { - assert!(file.is_ok()); - let file = file.map_err(|e| anyhow::anyhow!(e))?; - assert!(file.path().extension().is_some()); - let ext = file - .path() - .extension() - .ok_or(anyhow!("File extension cannot be read"))? - .to_str() - .ok_or(anyhow!("File extension cannot be read to string"))? - .to_string(); - assert_eq!(ext, QUERY_EXT, "wrong file extension for example"); - - let file_path = file.path(); - let query_name = file_path - .file_stem() - .ok_or(anyhow!("File stem cannot be read"))? - .to_str() - .ok_or(anyhow!("File extension cannot be read to string"))? - .to_string(); - - let mut contents = String::new(); - let mut fp = std::fs::File::open(file_path.clone()).map_err(|e| anyhow!(e))?; - fp.read_to_string(&mut contents).map_err(|e| anyhow!(e))?; - group.queries.push(ExampleQuery { - name: query_name, - contents, - path: file_path, - }); - } - group.queries.sort_by(|x, y| x.name.cmp(&y.name)); - - groups.push(group); - } - - groups.sort_by(|x, y| x.name.cmp(&y.name)); - Ok(groups) -} - -/// This generates a markdown page with all the examples, to be used in the docs site -pub fn generate_examples_for_docs() -> anyhow::Result { - let groups = load_examples()?; - - let mut output = BufWriter::new(Vec::new()); - let mut md = Markdown::new(&mut output); - md.write( - r#"--- -title: Examples -description: Query examples for working with the Sui GraphQL RPC. ---- -"#, - )?; - md.write("This page showcases a number of queries to interact with the network. These examples can also be found in the [repository](https://github.com/MystenLabs/sui/tree/main/crates/sui-graphql-rpc/examples). You can use the [interactive online IDE](https://mainnet.sui.io/rpc/graphql) to run these examples.")?; - for group in groups.iter() { - let group_name = regularize_string(&group.name); - md.write(group_name.heading(2)) - .map_err(|e| anyhow::anyhow!(e))?; - for query in group.queries.iter() { - let name = regularize_string(&query.name); - md.write(name.heading(3)).map_err(|e| anyhow::anyhow!(e))?; - let query = query.contents.lines().collect::>().join("\n"); - let content = format!("```graphql\n{}\n```", query); - md.write(content.as_str()).map_err(|e| anyhow::anyhow!(e))?; - } - } - let bytes = output.into_inner().map_err(|e| anyhow::anyhow!(e))?; - Ok(String::from_utf8(bytes) - .map_err(|e| anyhow::anyhow!(e))? - .replace('\\', "")) -} - -pub fn generate_markdown() -> anyhow::Result { - let groups = load_examples()?; - - let mut output = BufWriter::new(Vec::new()); - let mut md = Markdown::new(&mut output); - - md.write("Sui GraphQL Examples".heading(1)) - .map_err(|e| anyhow!(e))?; - - // TODO: reduce multiple loops - // Generate the table of contents - for (id, group) in groups.iter().enumerate() { - let group_name = regularize_string(&group.name); - let group_name_toc = format!("[{}](#{})", group_name, id); - md.write(group_name_toc.heading(3)) - .map_err(|e| anyhow!(e))?; - - for (inner, query) in group.queries.iter().enumerate() { - let inner_id = inner + 0xFFFF * id; - let inner_name = regularize_string(&query.name); - let inner_name_toc = format!("  [{}](#{})", inner_name, inner_id); - md.write(inner_name_toc.heading(4)) - .map_err(|e| anyhow!(e))?; - } - } - - for (id, group) in groups.iter().enumerate() { - let group_name = regularize_string(&group.name); - - let id_tag = format!("", id); - md.write(id_tag.heading(2)) - .map_err(|e| anyhow::anyhow!(e))?; - md.write(group_name.heading(2)) - .map_err(|e| anyhow::anyhow!(e))?; - for (inner, query) in group.queries.iter().enumerate() { - let inner_id = inner + 0xFFFF * id; - let name = regularize_string(&query.name); - - let id_tag = format!("", inner_id); - md.write(id_tag.heading(3)) - .map_err(|e| anyhow::anyhow!(e))?; - md.write(name.heading(3)).map_err(|e| anyhow::anyhow!(e))?; - - // Extract all lines that start with `#` and use them as headers - let mut headers = vec![]; - let mut query_start = 0; - for (idx, line) in query.contents.lines().enumerate() { - let line = line.trim(); - if line.starts_with('#') { - headers.push(line.trim_start_matches('#')); - } else if line.starts_with('{') { - query_start = idx; - break; - } - } - - // Remove headers from query - let query = query - .contents - .lines() - .skip(query_start) - .collect::>() - .join("\n"); - - let content = format!("
{}
", query); - for header in headers { - md.write(header.heading(4)) - .map_err(|e| anyhow::anyhow!(e))?; - } - md.write(content.quote()).map_err(|e| anyhow::anyhow!(e))?; - } - } - let bytes = output.into_inner().map_err(|e| anyhow::anyhow!(e))?; - Ok(String::from_utf8(bytes) - .map_err(|e| anyhow::anyhow!(e))? - .replace('\\', "")) -} - -#[test] -fn test_generate_markdown() { - use similar::*; - use std::fs::File; - - let mut buf: PathBuf = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - buf.push("docs"); - buf.push("examples.md"); - let mut out_file: File = File::open(buf).expect("Could not open examples.md"); - - // Read the current content of `out_file` - let mut current_content = String::new(); - out_file - .read_to_string(&mut current_content) - .expect("Could not read examples.md"); - let new_content: String = generate_markdown().expect("Generating examples markdown failed"); - - if current_content != new_content { - let mut res = vec![]; - let diff = TextDiff::from_lines(¤t_content, &new_content); - for change in diff.iter_all_changes() { - let sign = match change.tag() { - ChangeTag::Delete => "---", - ChangeTag::Insert => "+++", - ChangeTag::Equal => " ", - }; - res.push(format!("{}{}", sign, change)); - } - panic!("Doc examples have changed. Please run `sui-graphql-rpc generate-examples` to update the docs. Diff: {}", res.join("")); - } -} diff --git a/crates/sui-graphql-rpc/src/lib.rs b/crates/sui-graphql-rpc/src/lib.rs index baea0d2ce2ce8..c2f7cd3f8687b 100644 --- a/crates/sui-graphql-rpc/src/lib.rs +++ b/crates/sui-graphql-rpc/src/lib.rs @@ -8,7 +8,6 @@ pub(crate) mod consistency; pub mod context_data; pub(crate) mod data; mod error; -pub mod examples; pub mod extensions; pub(crate) mod functional_group; mod metrics; diff --git a/crates/sui-graphql-rpc/src/main.rs b/crates/sui-graphql-rpc/src/main.rs index 6e552a09e92e8..349ef0f0f74a4 100644 --- a/crates/sui-graphql-rpc/src/main.rs +++ b/crates/sui-graphql-rpc/src/main.rs @@ -9,7 +9,6 @@ use sui_graphql_rpc::commands::Command; use sui_graphql_rpc::config::{ ConnectionConfig, Ide, ServerConfig, ServiceConfig, TxExecFullNodeConfig, Version, }; -use sui_graphql_rpc::server::builder::export_schema; use sui_graphql_rpc::server::graphiql_server::start_graphiql_server; use tokio_util::sync::CancellationToken; use tokio_util::task::TaskTracker; @@ -38,39 +37,6 @@ static VERSION: Version = Version { async fn main() { let cmd: Command = Command::parse(); match cmd { - Command::GenerateDocsExamples => { - let mut buf: PathBuf = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - // we are looking to put examples content in - // sui/docs/content/references/sui-graphql/examples.mdx - let filename = "docs/content/references/sui-graphql/examples.mdx"; - buf.pop(); - buf.pop(); - buf.push(filename); - let content = sui_graphql_rpc::examples::generate_examples_for_docs() - .expect("Generating examples markdown file for docs failed"); - std::fs::write(buf, content).expect("Writing examples markdown failed"); - println!("Generated the docs example.mdx file and copied it to {filename}."); - } - Command::GenerateSchema { file } => { - let out = export_schema(); - if let Some(file) = file { - println!("Write schema to file: {:?}", file); - std::fs::write(file, &out).unwrap(); - } else { - println!("{}", &out); - } - } - Command::GenerateExamples { file } => { - let new_content: String = sui_graphql_rpc::examples::generate_markdown() - .expect("Generating examples markdown failed"); - let mut buf: PathBuf = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - buf.push("docs"); - buf.push("examples.md"); - let file = file.unwrap_or(buf); - - std::fs::write(file.clone(), new_content).expect("Writing examples markdown failed"); - println!("Written examples to file: {:?}", file); - } Command::StartServer { ide_title, db_url, diff --git a/crates/sui-graphql-rpc/src/types/move_package.rs b/crates/sui-graphql-rpc/src/types/move_package.rs index d29612246c305..aba7259ff1a22 100644 --- a/crates/sui-graphql-rpc/src/types/move_package.rs +++ b/crates/sui-graphql-rpc/src/types/move_package.rs @@ -58,6 +58,18 @@ pub(crate) struct MovePackageCheckpointFilter { pub before_checkpoint: Option, } +/// Filter for paginating versions of a given `MovePackage`. +#[derive(InputObject, Debug, Default, Clone)] +pub(crate) struct MovePackageVersionFilter { + /// Fetch versions of this package that are strictly newer than this version. Omitting this + /// fetches versions since the original version. + pub after_version: Option, + + /// Fetch versions of this package that are strictly older than this version. Omitting this + /// fetches versions up to the latest version (inclusive). + pub before_version: Option, +} + /// Filter for a point query of a MovePackage, supporting querying different versions of a package /// by their version. Note that different versions of the same user package exist at different IDs /// to each other, so this is different from looking up the historical version of an object. @@ -355,6 +367,31 @@ impl MovePackage { .extend() } + /// Fetch all versions of this package (packages that share this package's original ID), + /// optionally bounding the versions exclusively from below with `afterVersion`, or from above + /// with `beforeVersion`. + async fn package_versions( + &self, + ctx: &Context<'_>, + first: Option, + after: Option, + last: Option, + before: Option, + filter: Option, + ) -> Result> { + let page = Page::from_params(ctx.data_unchecked(), first, after, last, before)?; + + MovePackage::paginate_by_version( + ctx.data_unchecked(), + page, + self.super_.address, + filter, + self.checkpoint_viewed_at_impl(), + ) + .await + .extend() + } + /// Fetch the latest version of this package (the package with the highest `version` that shares /// this packages's original ID) async fn latest_package(&self, ctx: &Context<'_>) -> Result { @@ -690,6 +727,55 @@ impl MovePackage { Ok(conn) } + /// Query the database for a `page` of Move packages. The Page uses the checkpoint sequence + /// number the package was created at, its original ID, and its version as the cursor. The query + /// is filtered by the ID of a package and will only return packages from the same family + /// (sharing the same original ID as the package whose ID was given), and can optionally be + /// filtered by an upper and lower bound on package version. + /// + /// The `checkpoint_viewed_at` parameter represents the checkpoint sequence number at which this + /// page was queried. Each entity returned in the connection will inherit this checkpoint, so + /// that when viewing that entity's state, it will be as if it is being viewed at this + /// checkpoint. + /// + /// The cursors in `page` may also include checkpoint viewed at fields. If these are set, they + /// take precedence over the checkpoint that pagination is being conducted in. + pub(crate) async fn paginate_by_version( + db: &Db, + page: Page, + package: SuiAddress, + filter: Option, + checkpoint_viewed_at: u64, + ) -> Result, Error> { + let cursor_viewed_at = page.validate_cursor_consistency()?; + let checkpoint_viewed_at = cursor_viewed_at.unwrap_or(checkpoint_viewed_at); + let (prev, next, results) = db + .execute(move |conn| { + page.paginate_raw_query::( + conn, + checkpoint_viewed_at, + if is_system_package(package) { + system_package_version_query(package, filter) + } else { + user_package_version_query(package, filter) + }, + ) + }) + .await?; + + let mut conn = Connection::new(prev, next); + + // The "checkpoint viewed at" sets a consistent upper bound for the nested queries. + for stored in results { + let cursor = stored.cursor(checkpoint_viewed_at).encode_cursor(); + let package = + MovePackage::try_from_stored_history_object(stored.object, checkpoint_viewed_at)?; + conn.edges.push(Edge::new(cursor, package)); + } + + Ok(conn) + } + /// `checkpoint_viewed_at` points to the checkpoint snapshot that this `MovePackage` came from. /// This is stored in the `MovePackage` so that related fields from the package are read from /// the same checkpoint (consistently). @@ -719,9 +805,9 @@ impl RawPaginated for StoredHistoryPackage { format!( "o.checkpoint_sequence_number > {cp} OR (\ o.checkpoint_sequence_number = {cp} AND - p.original_id > '\\x{id}'::bytea OR (\ - p.original_id = '\\x{id}'::bytea AND \ - p.package_version >= {pv}\ + original_id > '\\x{id}'::bytea OR (\ + original_id = '\\x{id}'::bytea AND \ + o.object_version >= {pv}\ ))", cp = cursor.checkpoint_sequence_number, id = hex::encode(&cursor.original_id), @@ -736,9 +822,9 @@ impl RawPaginated for StoredHistoryPackage { format!( "o.checkpoint_sequence_number < {cp} OR (\ o.checkpoint_sequence_number = {cp} AND - p.original_id < '\\x{id}'::bytea OR (\ - p.original_id = '\\x{id}'::bytea AND \ - p.package_version <= {pv}\ + original_id < '\\x{id}'::bytea OR (\ + original_id = '\\x{id}'::bytea AND \ + o.object_version <= {pv}\ ))", cp = cursor.checkpoint_sequence_number, id = hex::encode(&cursor.original_id), @@ -751,13 +837,13 @@ impl RawPaginated for StoredHistoryPackage { if asc { query .order_by("o.checkpoint_sequence_number ASC") - .order_by("p.original_id ASC") - .order_by("p.package_version ASC") + .order_by("original_id ASC") + .order_by("o.object_version ASC") } else { query .order_by("o.checkpoint_sequence_number DESC") - .order_by("p.original_id DESC") - .order_by("p.package_version DESC") + .order_by("original_id DESC") + .order_by("o.object_version DESC") } } } @@ -918,3 +1004,94 @@ impl TryFrom<&Object> for MovePackage { } } } + +/// Query for fetching all the versions of a system package (assumes that `package` has already been +/// verified as a system package). This is an `objects_history` query disguised as a package query. +fn system_package_version_query( + package: SuiAddress, + filter: Option, +) -> RawQuery { + // Query uses a left join to force the query planner to use `objects_version` in the outer loop. + let mut q = query!( + r#" + SELECT + o.object_id AS original_id, + o.* + FROM + objects_version v + LEFT JOIN + objects_history o + ON + v.object_id = o.object_id + AND v.object_version = o.object_version + AND v.cp_sequence_number = o.checkpoint_sequence_number + "# + ); + + q = filter!( + q, + format!( + "v.object_id = '\\x{}'::bytea", + hex::encode(package.into_vec()) + ) + ); + + if let Some(after) = filter.as_ref().and_then(|f| f.after_version) { + let a: u64 = after.into(); + q = filter!(q, format!("v.object_version > {a}")); + } + + if let Some(before) = filter.as_ref().and_then(|f| f.before_version) { + let b: u64 = before.into(); + q = filter!(q, format!("v.object_version < {b}")); + } + + q +} + +/// Query for fetching all the versions of a non-system package (assumes that `package` has already +/// been verified as a system package) +fn user_package_version_query( + package: SuiAddress, + filter: Option, +) -> RawQuery { + let mut q = query!( + r#" + SELECT + p.original_id, + o.* + FROM + packages q + INNER JOIN + packages p + ON + q.original_id = p.original_id + INNER JOIN + objects_history o + ON + p.package_id = o.object_id + AND p.package_version = o.object_version + AND p.checkpoint_sequence_number = o.checkpoint_sequence_number + "# + ); + + q = filter!( + q, + format!( + "q.package_id = '\\x{}'::bytea", + hex::encode(package.into_vec()) + ) + ); + + if let Some(after) = filter.as_ref().and_then(|f| f.after_version) { + let a: u64 = after.into(); + q = filter!(q, format!("p.package_version > {a}")); + } + + if let Some(before) = filter.as_ref().and_then(|f| f.before_version) { + let b: u64 = before.into(); + q = filter!(q, format!("p.package_version < {b}")); + } + + q +} diff --git a/crates/sui-graphql-rpc/src/types/query.rs b/crates/sui-graphql-rpc/src/types/query.rs index 0970a046749c3..1ff209d351a55 100644 --- a/crates/sui-graphql-rpc/src/types/query.rs +++ b/crates/sui-graphql-rpc/src/types/query.rs @@ -12,7 +12,9 @@ use sui_sdk::SuiClient; use sui_types::transaction::{TransactionData, TransactionKind}; use sui_types::{gas_coin::GAS, transaction::TransactionDataAPI, TypeTag}; -use super::move_package::{self, MovePackage, MovePackageCheckpointFilter}; +use super::move_package::{ + self, MovePackage, MovePackageCheckpointFilter, MovePackageVersionFilter, +}; use super::suins_registration::NameService; use super::uint53::UInt53; use super::{ @@ -457,6 +459,27 @@ impl Query { .extend() } + /// Fetch all versions of package at `address` (packages that share this package's original ID), + /// optionally bounding the versions exclusively from below with `afterVersion`, or from above + /// with `beforeVersion`. + async fn package_versions( + &self, + ctx: &Context<'_>, + first: Option, + after: Option, + last: Option, + before: Option, + address: SuiAddress, + filter: Option, + ) -> Result> { + let Watermark { checkpoint, .. } = *ctx.data()?; + + let page = Page::from_params(ctx.data_unchecked(), first, after, last, before)?; + MovePackage::paginate_by_version(ctx.data_unchecked(), page, address, filter, checkpoint) + .await + .extend() + } + /// Fetch the protocol config by protocol version (defaults to the latest protocol /// version known to the GraphQL service). async fn protocol_config( diff --git a/crates/sui-graphql-rpc/tests/examples_validation_tests.rs b/crates/sui-graphql-rpc/tests/examples_validation_tests.rs index fc2a95d21c90b..205c0e1407b5d 100644 --- a/crates/sui-graphql-rpc/tests/examples_validation_tests.rs +++ b/crates/sui-graphql-rpc/tests/examples_validation_tests.rs @@ -3,105 +3,147 @@ #[cfg(feature = "pg_integration")] mod tests { + use anyhow::{anyhow, Context, Result}; use rand::rngs::StdRng; use rand::SeedableRng; use serial_test::serial; use simulacrum::Simulacrum; use std::cmp::max; + use std::collections::BTreeMap; + use std::fs; use std::path::PathBuf; use std::sync::Arc; use sui_graphql_rpc::config::{ConnectionConfig, Limits}; - use sui_graphql_rpc::examples::{load_examples, ExampleQuery, ExampleQueryGroup}; use sui_graphql_rpc::test_infra::cluster::ExecutorCluster; use sui_graphql_rpc::test_infra::cluster::DEFAULT_INTERNAL_DATA_SOURCE_PORT; use tempfile::tempdir; - fn bad_examples() -> ExampleQueryGroup { - ExampleQueryGroup { - name: "bad_examples".to_string(), - queries: vec![ - ExampleQuery { - name: "multiple_queries".to_string(), + struct Example { + contents: String, + path: Option, + } + + fn good_examples() -> Result> { + let examples = PathBuf::from(&env!("CARGO_MANIFEST_DIR")).join("examples"); + + let mut dirs = vec![examples.clone()]; + let mut queries = BTreeMap::new(); + while let Some(dir) = dirs.pop() { + let entries = + fs::read_dir(&dir).with_context(|| format!("Looking in {}", dir.display()))?; + + for entry in entries { + let entry = entry.with_context(|| format!("Entry in {}", dir.display()))?; + let path = entry.path(); + let typ_ = entry + .file_type() + .with_context(|| format!("Metadata for {}", path.display()))?; + + if typ_.is_dir() { + dirs.push(entry.path()); + continue; + } + + if path.ends_with(".graphql") { + let contents = fs::read_to_string(&path) + .with_context(|| format!("Reading {}", path.display()))?; + + let rel_path = path + .strip_prefix(&examples) + .with_context(|| format!("Generating name from {}", path.display()))? + .with_extension(""); + + let name = rel_path + .to_str() + .ok_or_else(|| anyhow!("Generating name from {}", path.display()))?; + + queries.insert( + name.to_string(), + Example { + contents, + path: Some(path), + }, + ); + } + } + } + + Ok(queries) + } + + fn bad_examples() -> BTreeMap { + BTreeMap::from_iter([ + ( + "multiple_queries".to_string(), + Example { contents: "{ chainIdentifier } { chainIdentifier }".to_string(), - path: PathBuf::from("multiple_queries.graphql"), + path: None, }, - ExampleQuery { - name: "malformed".to_string(), + ), + ( + "malformed".to_string(), + Example { contents: "query { }}".to_string(), - path: PathBuf::from("malformed.graphql"), + path: None, }, - ExampleQuery { - name: "invalid".to_string(), + ), + ( + "invalid".to_string(), + Example { contents: "djewfbfo".to_string(), - path: PathBuf::from("invalid.graphql"), + path: None, }, - ExampleQuery { - name: "empty".to_string(), + ), + ( + "empty".to_string(), + Example { contents: " ".to_string(), - path: PathBuf::from("empty.graphql"), + path: None, }, - ], - _path: PathBuf::from("bad_examples"), - } + ), + ]) } - async fn validate_example_query_group( + async fn test_query( cluster: &ExecutorCluster, - group: &ExampleQueryGroup, + name: &str, + query: &Example, max_nodes: &mut u64, max_output_nodes: &mut u64, max_depth: &mut u64, max_payload: &mut u64, ) -> Vec { - let mut errors = vec![]; - for query in &group.queries { - let resp = cluster - .graphql_client - .execute_to_graphql(query.contents.clone(), true, vec![], vec![]) - .await - .unwrap(); - resp.errors().iter().for_each(|err| { - errors.push(format!( - "Query failed: {}: {} at: {}\nError: {}", - group.name, - query.name, - query.path.display(), - err - )) - }); - if resp.errors().is_empty() { - let usage = resp - .usage() - .expect("Usage fetch should succeed") - .unwrap_or_else(|| panic!("Usage should be present for query: {}", query.name)); - - let nodes = *usage.get("inputNodes").unwrap_or_else(|| { - panic!("Node usage should be present for query: {}", query.name) - }); - let output_nodes = *usage.get("outputNodes").unwrap_or_else(|| { - panic!( - "Output node usage should be present for query: {}", - query.name - ) - }); - let depth = *usage.get("depth").unwrap_or_else(|| { - panic!("Depth usage should be present for query: {}", query.name) - }); - let payload = *usage.get("queryPayload").unwrap_or_else(|| { - panic!("Payload usage should be present for query: {}", query.name) - }); - *max_nodes = max(*max_nodes, nodes); - *max_output_nodes = max(*max_output_nodes, output_nodes); - *max_depth = max(*max_depth, depth); - *max_payload = max(*max_payload, payload); - } + let resp = cluster + .graphql_client + .execute_to_graphql(query.contents.clone(), true, vec![], vec![]) + .await + .unwrap(); + + let errors = resp.errors(); + if errors.is_empty() { + let usage = resp + .usage() + .expect("Usage not found") + .expect("Usage not found"); + *max_nodes = max(*max_nodes, usage["inputNodes"]); + *max_output_nodes = max(*max_output_nodes, usage["outputNodes"]); + *max_depth = max(*max_depth, usage["depth"]); + *max_payload = max(*max_payload, usage["queryPayload"]); + return vec![]; } + errors + .into_iter() + .map(|e| match &query.path { + Some(p) => format!("Query {name:?} at {} failed: {e}", p.display()), + None => format!("Query {name:?} failed: {e}"), + }) + .collect() } #[tokio::test] #[serial] - async fn test_single_all_examples_structure_valid() { + async fn good_examples_within_limits() { let rng = StdRng::from_seed([12; 32]); let data_ingestion_path = tempdir().unwrap().into_path(); let mut sim = Simulacrum::new_with_rng(rng); @@ -119,20 +161,20 @@ mod tests { ) .await; - let groups = load_examples().expect("Could not load examples"); - let mut errors = vec![]; - for group in groups { - let group_errors = validate_example_query_group( - &cluster, - &group, - &mut max_nodes, - &mut max_output_nodes, - &mut max_depth, - &mut max_payload, - ) - .await; - errors.extend(group_errors); + for (name, example) in good_examples().expect("Could not load examples") { + errors.extend( + test_query( + &cluster, + &name, + &example, + &mut max_nodes, + &mut max_output_nodes, + &mut max_depth, + &mut max_payload, + ) + .await, + ); } // Check that our examples can run with our usage limits @@ -167,7 +209,7 @@ mod tests { #[tokio::test] #[serial] - async fn test_bad_examples_fail() { + async fn bad_examples_fail() { let rng = StdRng::from_seed([12; 32]); let data_ingestion_path = tempdir().unwrap().into_path(); let mut sim = Simulacrum::new_with_rng(rng); @@ -185,21 +227,19 @@ mod tests { ) .await; - let bad_examples = bad_examples(); - let errors = validate_example_query_group( - &cluster, - &bad_examples, - &mut max_nodes, - &mut max_output_nodes, - &mut max_depth, - &mut max_payload, - ) - .await; + for (name, example) in bad_examples() { + let errors = test_query( + &cluster, + &name, + &example, + &mut max_nodes, + &mut max_output_nodes, + &mut max_depth, + &mut max_payload, + ) + .await; - assert_eq!( - errors.len(), - bad_examples.queries.len(), - "all examples should fail" - ); + assert!(!errors.is_empty(), "Query {name:?} should have failed"); + } } } diff --git a/crates/sui-graphql-rpc/tests/snapshot_tests.rs b/crates/sui-graphql-rpc/tests/snapshot_tests.rs index 30a66934b5deb..dcefef844375d 100644 --- a/crates/sui-graphql-rpc/tests/snapshot_tests.rs +++ b/crates/sui-graphql-rpc/tests/snapshot_tests.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use insta::assert_snapshot; -use std::fs::write; +use std::fs; use std::path::PathBuf; use sui_graphql_rpc::server::builder::export_schema; @@ -11,9 +11,8 @@ fn test_schema_sdl_export() { let sdl = export_schema(); // update the current schema file - let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - path.extend(["schema", "current_progress_schema.graphql"]); - write(path, &sdl).unwrap(); + let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("schema.graphql"); + fs::write(path, &sdl).unwrap(); assert_snapshot!(sdl); } diff --git a/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap b/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap index 3499ea88329cc..87f5a27061f06 100644 --- a/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap +++ b/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap @@ -2177,6 +2177,12 @@ type MovePackage implements IObject & IOwner { """ packageAtVersion(version: Int!): MovePackage """ + Fetch all versions of this package (packages that share this package's original ID), + optionally bounding the versions exclusively from below with `afterVersion`, or from above + with `beforeVersion`. + """ + packageVersions(first: Int, after: String, last: Int, before: String, filter: MovePackageVersionFilter): MovePackageConnection! + """ Fetch the latest version of this package (the package with the highest `version` that shares this packages's original ID) """ @@ -2250,6 +2256,22 @@ type MovePackageEdge { cursor: String! } +""" +Filter for paginating versions of a given `MovePackage`. +""" +input MovePackageVersionFilter { + """ + Fetch versions of this package that are strictly newer than this version. Omitting this + fetches versions since the original version. + """ + afterVersion: UInt53 + """ + Fetch versions of this package that are strictly older than this version. Omitting this + fetches versions up to the latest version (inclusive). + """ + beforeVersion: UInt53 +} + """ Description of a struct type, defined in a Move module. """ @@ -3143,6 +3165,12 @@ type Query { """ packages(first: Int, after: String, last: Int, before: String, filter: MovePackageCheckpointFilter): MovePackageConnection! """ + Fetch all versions of package at `address` (packages that share this package's original ID), + optionally bounding the versions exclusively from below with `afterVersion`, or from above + with `beforeVersion`. + """ + packageVersions(first: Int, after: String, last: Int, before: String, address: SuiAddress!, filter: MovePackageVersionFilter): MovePackageConnection! + """ Fetch the protocol config by protocol version (defaults to the latest protocol version known to the GraphQL service). """ diff --git a/docs/site/docusaurus.config.js b/docs/site/docusaurus.config.js index e784d38744f15..5760ff48882b1 100644 --- a/docs/site/docusaurus.config.js +++ b/docs/site/docusaurus.config.js @@ -62,7 +62,7 @@ const config = { "@graphql-markdown/docusaurus", { schema: - "../../crates/sui-graphql-rpc/schema/current_progress_schema.graphql", + "../../crates/sui-graphql-rpc/schema.graphql", rootPath: "../content", // docs will be generated under rootPath/baseURL baseURL: "references/sui-api/sui-graphql/reference", loaders: { diff --git a/sdk/typescript/scripts/update-graphql-schemas.ts b/sdk/typescript/scripts/update-graphql-schemas.ts index a129e7dfe1024..4a3843ede5cbc 100644 --- a/sdk/typescript/scripts/update-graphql-schemas.ts +++ b/sdk/typescript/scripts/update-graphql-schemas.ts @@ -29,7 +29,7 @@ const result = execSync(`git branch --remote --list "origin/releases/sui-graphql minor, patch, branch, - schema: `https://raw.githubusercontent.com/MystenLabs/sui/${branch}/crates/sui-graphql-rpc/schema/current_progress_schema.graphql`, + schema: `https://raw.githubusercontent.com/MystenLabs/sui/${branch}/crates/sui-graphql-rpc/schema.graphql`, } : null; })