diff --git a/.changeset/big-pots-attack.md b/.changeset/big-pots-attack.md
new file mode 100644
index 0000000000..a260922ae2
--- /dev/null
+++ b/.changeset/big-pots-attack.md
@@ -0,0 +1,6 @@
+---
+'@kadena/graph': patch
+---
+
+Add policies to non fungible token; restructure the guards and create JsonString
+object
diff --git a/.changeset/three-rules-mate.md b/.changeset/three-rules-mate.md
new file mode 100644
index 0000000000..225e35aba4
--- /dev/null
+++ b/.changeset/three-rules-mate.md
@@ -0,0 +1,5 @@
+---
+'@kadena/graph-client': patch
+---
+
+Adjusted client to changes made in graph schema for Guards and Policies
diff --git a/packages/apps/graph-client/src/components/chain-fungible-account-table/chain-fungible-account-table.tsx b/packages/apps/graph-client/src/components/chain-fungible-account-table/chain-fungible-account-table.tsx
index 33dc8e10f9..e2cee4d5cd 100644
--- a/packages/apps/graph-client/src/components/chain-fungible-account-table/chain-fungible-account-table.tsx
+++ b/packages/apps/graph-client/src/components/chain-fungible-account-table/chain-fungible-account-table.tsx
@@ -51,8 +51,20 @@ export const FungibleChainAccountTable = (
{chainAccount.balance}
- | {chainAccount.guard.predicate} |
- {chainAccount.guard.keys.join(', ')} |
+
+ {chainAccount.guard.__typename === 'Keyset' ? (
+ chainAccount.guard.predicate
+ ) : (
+ N/A
+ )}
+ |
+
+ {chainAccount.guard.__typename === 'Keyset' ? (
+ chainAccount.guard.keys.join(', ')
+ ) : (
+ N/A
+ )}
+ |
))}
diff --git a/packages/apps/graph-client/src/components/token-table/token-table.tsx b/packages/apps/graph-client/src/components/token-table/token-table.tsx
index f6b6c55864..9cebef84e0 100644
--- a/packages/apps/graph-client/src/components/token-table/token-table.tsx
+++ b/packages/apps/graph-client/src/components/token-table/token-table.tsx
@@ -44,10 +44,21 @@ export const TokenTable = (props: ITokenTableProps): JSX.Element => {
{token.tokenId} |
{token.chainId} |
{token.balance} |
+
- Predicate: {token.guard.predicate}
-
- Keys: {token.guard.keys.join(', ')}
+ {token.guard.__typename === 'Keyset' ? (
+ <>
+ Predicate: {token.guard.predicate}
+
+ Keys: {token.guard.keys.join(', ')}
+ >
+ ) : token.guard.__typename === 'JsonString' ? (
+ <>
+ Guard: {token.guard.value}
+ >
+ ) : (
+ N/A
+ )}
|
);
diff --git a/packages/apps/graph-client/src/graphql/fields/fungible-chain-account.graph.ts b/packages/apps/graph-client/src/graphql/fields/fungible-chain-account.graph.ts
index cb554ae8bc..6ffe20b8d7 100644
--- a/packages/apps/graph-client/src/graphql/fields/fungible-chain-account.graph.ts
+++ b/packages/apps/graph-client/src/graphql/fields/fungible-chain-account.graph.ts
@@ -15,8 +15,10 @@ export const ALL_FUNGIBLE_CHAIN_ACCOUNT_FIELDS: DocumentNode = gql`
...CoreFungibleChainAccountFields
accountName
guard {
- keys
- predicate
+ ... on Keyset {
+ keys
+ predicate
+ }
}
fungibleName
diff --git a/packages/apps/graph-client/src/graphql/queries.graph.ts b/packages/apps/graph-client/src/graphql/queries.graph.ts
index ed60446cfc..a39d58b482 100644
--- a/packages/apps/graph-client/src/graphql/queries.graph.ts
+++ b/packages/apps/graph-client/src/graphql/queries.graph.ts
@@ -44,8 +44,10 @@ export const getBlockFromHash: DocumentNode = gql`
}
minerAccount {
guard {
- predicate
- keys
+ ... on Keyset {
+ predicate
+ keys
+ }
}
}
parent {
@@ -88,8 +90,10 @@ export const getFungibleAccount: DocumentNode = gql`
chainAccounts {
...CoreFungibleChainAccountFields
guard {
- keys
- predicate
+ ... on Keyset {
+ keys
+ predicate
+ }
}
}
transactions {
@@ -337,8 +341,10 @@ export const getNonFungibleAccount: DocumentNode = gql`
tokenId
chainId
guard {
- predicate
- keys
+ ... on Keyset {
+ predicate
+ keys
+ }
}
}
transactions {
@@ -364,8 +370,10 @@ export const getNonFungibleChainAccount: DocumentNode = gql`
tokenId
chainId
guard {
- predicate
- keys
+ ... on Keyset {
+ predicate
+ keys
+ }
}
}
transactions {
diff --git a/packages/apps/graph-client/src/pages/account/overview/[fungible]/[account]/[chain].tsx b/packages/apps/graph-client/src/pages/account/overview/[fungible]/[account]/[chain].tsx
index 8d6b74142c..9bfd613ec0 100644
--- a/packages/apps/graph-client/src/pages/account/overview/[fungible]/[account]/[chain].tsx
+++ b/packages/apps/graph-client/src/pages/account/overview/[fungible]/[account]/[chain].tsx
@@ -217,10 +217,13 @@ const ChainAccount: React.FC = () => {
Guard Predicate
- {
+ {fungibleChainAccountData.fungibleChainAccount.guard
+ .__typename === 'Keyset' ? (
fungibleChainAccountData.fungibleChainAccount.guard
.predicate
- }
+ ) : (
+ N/A
+ )}
|
@@ -228,7 +231,15 @@ const ChainAccount: React.FC = () => {
Guard Keys
- {fungibleChainAccountData.fungibleChainAccount.guard.keys}
+ |
+ {fungibleChainAccountData.fungibleChainAccount.guard
+ .__typename === 'Keyset' ? (
+ fungibleChainAccountData.fungibleChainAccount.guard
+ .keys
+ ) : (
+ N/A
+ )}
+ |
|
diff --git a/packages/apps/graph-client/src/pages/block/overview/[hash].tsx b/packages/apps/graph-client/src/pages/block/overview/[hash].tsx
index fbb4f2cb4d..f39c39ffef 100644
--- a/packages/apps/graph-client/src/pages/block/overview/[hash].tsx
+++ b/packages/apps/graph-client/src/pages/block/overview/[hash].tsx
@@ -202,12 +202,17 @@ const Block: React.FC = () => {
Value
- {data.block.minerAccount.guard.keys?.map(
- (minerKey, index) => (
-
- | {minerKey} |
-
- ),
+ {data.block.minerAccount.guard.__typename ===
+ 'Keyset' ? (
+ data.block.minerAccount.guard.keys?.map(
+ (minerKey, index) => (
+
+ | {minerKey} |
+
+ ),
+ )
+ ) : (
+ N/A
)}
@@ -217,7 +222,14 @@ const Block: React.FC = () => {
Predicate
|
- {data.block.minerAccount.guard.predicate} |
+
+ {data.block.minerAccount.guard.__typename ===
+ 'Keyset' ? (
+ data.block.minerAccount.guard.predicate
+ ) : (
+ N/A
+ )}
+ |
diff --git a/packages/apps/graph/generated-schema.graphql b/packages/apps/graph/generated-schema.graphql
index febb9132ff..cf17a9cb26 100644
--- a/packages/apps/graph/generated-schema.graphql
+++ b/packages/apps/graph/generated-schema.graphql
@@ -212,8 +212,17 @@ type GraphConfiguration {
minimumBlockHeight: BigInt
}
+"""A guard"""
+union Guard = JsonString | Keyset
+
+"""A JSON string."""
+type JsonString {
+ type: String!
+ value: String!
+}
+
"""Guard for an account."""
-type Guard {
+type Keyset {
keys: [String!]!
predicate: String!
}
@@ -226,6 +235,12 @@ type MinerKey implements Node {
key: String!
}
+"""A reference to a module."""
+type ModuleReference {
+ name: String!
+ namespace: String!
+}
+
"""Information about the network."""
type NetworkInfo {
"""The version of the API."""
@@ -302,13 +317,6 @@ type NonFungibleChainAccountTransactionsConnectionEdge {
node: Transaction!
}
-"""Information related to a token."""
-type NonFungibleToken {
- precision: Int!
- supply: Int!
- uri: String!
-}
-
"""The token identifier and its balance."""
type NonFungibleTokenBalance implements Node {
accountName: String!
@@ -316,11 +324,19 @@ type NonFungibleTokenBalance implements Node {
chainId: String!
guard: Guard!
id: ID!
- info: NonFungibleToken
+ info: NonFungibleTokenInfo
tokenId: String!
version: String!
}
+"""Information related to a token."""
+type NonFungibleTokenInfo {
+ policies: [Policy!]!
+ precision: Int!
+ supply: Int!
+ uri: String!
+}
+
input PactQuery {
chainId: String!
code: String!
@@ -348,6 +364,12 @@ type PageInfo {
startCursor: String
}
+"""A policy that defines the rules for a non-fungible token."""
+type Policy {
+ refName: ModuleReference!
+ refSpec: [ModuleReference!]!
+}
+
"""Floats that will have a value greater than 0."""
scalar PositiveFloat
diff --git a/packages/apps/graph/src/devnet/deployment/marmalade/deploy.ts b/packages/apps/graph/src/devnet/deployment/marmalade/deploy.ts
index 2b74c04640..eed9632c4d 100644
--- a/packages/apps/graph/src/devnet/deployment/marmalade/deploy.ts
+++ b/packages/apps/graph/src/devnet/deployment/marmalade/deploy.ts
@@ -37,11 +37,15 @@ export async function deployMarmaladeContracts(
nsDestinationPath: string = marmaladeLocalConfig.namespacePath,
): Promise {
logger.info('Validating repository data...');
- validateConfig(
- marmaladeRepository,
- marmaladeLocalConfig,
- marmaladeRemoteConfig,
- );
+ if (
+ !validateConfig(
+ marmaladeRepository,
+ marmaladeLocalConfig,
+ marmaladeRemoteConfig,
+ )
+ ) {
+ throw new Error('Invalid repository data');
+ }
logger.info('Preparing directories...');
await handleDirectorySetup(
@@ -362,8 +366,10 @@ export function validateConfig(
repositoryConfig: IMarmaladeRepository,
localConfig: IMarmaladeLocalConfig,
remoteConfig: IMarmaladeRemoteConfig,
-): void {
- validateObjectProperties(repositoryConfig, 'Repository');
- validateObjectProperties(localConfig, 'Local');
- validateObjectProperties(remoteConfig, 'Remote');
+): boolean {
+ return (
+ validateObjectProperties(repositoryConfig, 'Repository') &&
+ validateObjectProperties(localConfig, 'Local') &&
+ validateObjectProperties(remoteConfig, 'Remote')
+ );
}
diff --git a/packages/apps/graph/src/devnet/simulation/marmalade/get-token-info.ts b/packages/apps/graph/src/devnet/simulation/marmalade/get-token-info.ts
index 8986a91d54..93884d6ad4 100644
--- a/packages/apps/graph/src/devnet/simulation/marmalade/get-token-info.ts
+++ b/packages/apps/graph/src/devnet/simulation/marmalade/get-token-info.ts
@@ -9,6 +9,7 @@ interface ITokenInfo {
precision: number;
uri: string;
id: string;
+ policies: { moduleName: string }[];
}
export const getTokenInfo = async (
@@ -97,5 +98,15 @@ export const getTokenInfo = async (
}
}
+ if ('policies' in tokenInfo) {
+ if (!Array.isArray(tokenInfo.policies)) {
+ tokenInfo.policies = tokenInfo.policies.map((policy: string) => ({
+ moduleName: policy,
+ }));
+ }
+ } else if ('policy' in tokenInfo) {
+ tokenInfo.policies = { moduleName: tokenInfo.policy.toString() };
+ }
+
return tokenInfo as ITokenInfo;
};
diff --git a/packages/apps/graph/src/graph/builder.ts b/packages/apps/graph/src/graph/builder.ts
index 07c4482da9..d410d93a1c 100644
--- a/packages/apps/graph/src/graph/builder.ts
+++ b/packages/apps/graph/src/graph/builder.ts
@@ -25,13 +25,16 @@ import type {
IFungibleChainAccount,
IGasLimitEstimation,
IGraphConfiguration,
- IGuard,
+ IJsonString,
+ IKeyset,
+ IModuleReference,
INetworkInfo,
INonFungibleAccount,
INonFungibleChainAccount,
- INonFungibleToken,
INonFungibleTokenBalance,
+ INonFungibleTokenInfo,
IPactQueryResponse,
+ IPolicy,
ITransactionCapability,
ITransactionCommand,
ITransactionMempoolInfo,
@@ -80,11 +83,11 @@ export const builder = new SchemaBuilder<
FungibleChainAccount: IFungibleChainAccount;
GasLimitEstimation: IGasLimitEstimation;
GraphConfiguration: IGraphConfiguration;
- Guard: IGuard;
+ Keyset: IKeyset;
NonFungibleAccount: INonFungibleAccount;
NonFungibleChainAccount: INonFungibleChainAccount;
NonFungibleTokenBalance: INonFungibleTokenBalance;
- NonFungibleToken: INonFungibleToken;
+ NonFungibleTokenInfo: INonFungibleTokenInfo;
TransactionMeta: ITransactionMeta;
ExecutionPayload: IExecutionPayload;
ContinuationPayload: IContinuationPayload;
@@ -94,6 +97,9 @@ export const builder = new SchemaBuilder<
TransactionCapability: ITransactionCapability;
TransactionSignature: ITransactionSignature;
PactQueryResponse: IPactQueryResponse;
+ Policy: IPolicy;
+ ModuleReference: IModuleReference;
+ JsonString: IJsonString;
NetworkInfo: INetworkInfo;
};
}
diff --git a/packages/apps/graph/src/graph/data-loaders/fungible-account-details.ts b/packages/apps/graph/src/graph/data-loaders/fungible-account-details.ts
index 63954eb5c2..926ae5464c 100644
--- a/packages/apps/graph/src/graph/data-loaders/fungible-account-details.ts
+++ b/packages/apps/graph/src/graph/data-loaders/fungible-account-details.ts
@@ -1,5 +1,5 @@
import type { IFungibleChainAccountDetails } from '@services/chainweb-node/fungible-account-details';
-import { getFungibleAccountDetails } from '@services/chainweb-node/fungible-account-details';
+import { getFungibleAccountDetailsWithRetry } from '@services/chainweb-node/fungible-account-details';
import DataLoader from 'dataloader';
interface IFungibleAccountDetailsKey {
@@ -14,7 +14,7 @@ export const fungibleAccountDetailsLoader = new DataLoader<
>(async (keys: readonly IFungibleAccountDetailsKey[]) => {
const results = await Promise.all(
keys.map(({ fungibleName, accountName, chainId }) =>
- getFungibleAccountDetails(fungibleName, accountName, chainId),
+ getFungibleAccountDetailsWithRetry(fungibleName, accountName, chainId),
),
);
diff --git a/packages/apps/graph/src/graph/data-loaders/non-fungible-account-details.ts b/packages/apps/graph/src/graph/data-loaders/non-fungible-account-details.ts
index e4a34fadb4..e47a288c6e 100644
--- a/packages/apps/graph/src/graph/data-loaders/non-fungible-account-details.ts
+++ b/packages/apps/graph/src/graph/data-loaders/non-fungible-account-details.ts
@@ -1,11 +1,12 @@
import type { INonFungibleChainAccountDetails } from '@services/chainweb-node/non-fungible-account-details';
-import { getNonFungibleAccountDetails } from '@services/chainweb-node/non-fungible-account-details';
+import { getNonFungibleAccountDetailsWithRetry } from '@services/chainweb-node/non-fungible-account-details';
import DataLoader from 'dataloader';
interface INonFungibleAccountDetailsKey {
tokenId: string;
accountName: string;
chainId: string;
+ version: string;
}
export const nonFungibleAccountDetailsLoader = new DataLoader<
@@ -13,8 +14,13 @@ export const nonFungibleAccountDetailsLoader = new DataLoader<
INonFungibleChainAccountDetails | null
>(async (keys: readonly INonFungibleAccountDetailsKey[]) => {
const results = await Promise.all(
- keys.map(({ tokenId, accountName, chainId }) =>
- getNonFungibleAccountDetails(tokenId, accountName, chainId),
+ keys.map(({ tokenId, accountName, chainId, version }) =>
+ getNonFungibleAccountDetailsWithRetry(
+ tokenId,
+ accountName,
+ chainId,
+ version,
+ ),
),
);
diff --git a/packages/apps/graph/src/graph/data-loaders/non-fungible-token-balances.ts b/packages/apps/graph/src/graph/data-loaders/non-fungible-token-balances.ts
new file mode 100644
index 0000000000..dfa8654fa1
--- /dev/null
+++ b/packages/apps/graph/src/graph/data-loaders/non-fungible-token-balances.ts
@@ -0,0 +1,21 @@
+import { getNonFungibleTokenBalances } from '@services/token-service';
+import DataLoader from 'dataloader';
+import type { INonFungibleTokenBalance } from '../types/graphql-types';
+
+interface INonFungibleTokenBalancesKey {
+ accountName: string;
+ chainId?: string;
+}
+
+export const nonFungibleTokenBalancesLoader = new DataLoader<
+ INonFungibleTokenBalancesKey,
+ INonFungibleTokenBalance[]
+>(async (keys: readonly INonFungibleTokenBalancesKey[]) => {
+ const results = await Promise.all(
+ keys.map(({ accountName, chainId }) =>
+ getNonFungibleTokenBalances(accountName, chainId),
+ ),
+ );
+
+ return results;
+});
diff --git a/packages/apps/graph/src/graph/data-loaders/non-fungible-token-info.ts b/packages/apps/graph/src/graph/data-loaders/non-fungible-token-info.ts
new file mode 100644
index 0000000000..51f208dd5f
--- /dev/null
+++ b/packages/apps/graph/src/graph/data-loaders/non-fungible-token-info.ts
@@ -0,0 +1,25 @@
+import { getNonFungibleTokenInfoWithRetry } from '@services/token-service';
+import DataLoader from 'dataloader';
+import type { INonFungibleTokenInfo } from '../types/graphql-types';
+
+interface INonFungibleTokenInfoKey {
+ tokenId: string;
+ chainId: string;
+ version: string;
+}
+
+/**
+ * Get the info for a non-fungible token
+ */
+export const nonFungibleTokenInfoLoader = new DataLoader<
+ INonFungibleTokenInfoKey,
+ INonFungibleTokenInfo | null
+>(async (keys: readonly INonFungibleTokenInfoKey[]) => {
+ const results = await Promise.all(
+ keys.map(({ tokenId, chainId, version }) =>
+ getNonFungibleTokenInfoWithRetry(tokenId, chainId, version),
+ ),
+ );
+
+ return results;
+});
diff --git a/packages/apps/graph/src/graph/data-loaders/token-details.ts b/packages/apps/graph/src/graph/data-loaders/token-details.ts
deleted file mode 100644
index e5b336ef71..0000000000
--- a/packages/apps/graph/src/graph/data-loaders/token-details.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { getTokenDetails } from '@services/token-service';
-import DataLoader from 'dataloader';
-import type { INonFungibleTokenBalance } from '../types/graphql-types';
-
-interface ITokenDetailsKey {
- accountName: string;
- chainId?: string;
-}
-
-export const tokenDetailsLoader = new DataLoader<
- ITokenDetailsKey,
- INonFungibleTokenBalance[]
->(async (keys: readonly ITokenDetailsKey[]) => {
- const results = await Promise.all(
- keys.map(({ accountName, chainId }) =>
- getTokenDetails(accountName, chainId),
- ),
- );
-
- return results;
-});
diff --git a/packages/apps/graph/src/graph/index.ts b/packages/apps/graph/src/graph/index.ts
index 9aad9c181f..1f69c7d884 100644
--- a/packages/apps/graph/src/graph/index.ts
+++ b/packages/apps/graph/src/graph/index.ts
@@ -5,13 +5,13 @@ import './objects/fungible-account';
import './objects/fungible-chain-account';
import './objects/gas-limit-estimation';
import './objects/graph-configuration';
-import './objects/guard';
+import './objects/keyset';
import './objects/miner-key';
import './objects/network-info';
import './objects/non-fungible-account';
import './objects/non-fungible-chain-account';
-import './objects/non-fungible-token';
import './objects/non-fungible-token-balance';
+import './objects/non-fungible-token-info';
import './objects/pact-query-response';
import './objects/signer';
import './objects/transaction';
diff --git a/packages/apps/graph/src/graph/objects/block.ts b/packages/apps/graph/src/graph/objects/block.ts
index d790dec41f..4348755c16 100644
--- a/packages/apps/graph/src/graph/objects/block.ts
+++ b/packages/apps/graph/src/graph/objects/block.ts
@@ -7,7 +7,7 @@ import {
} from '@services/complexity';
import { normalizeError } from '@utils/errors';
import { builder } from '../builder';
-import type { IGuard } from '../types/graphql-types';
+import type { IKeyset } from '../types/graphql-types';
import { FungibleChainAccountName } from '../types/graphql-types';
import FungibleChainAccount from './fungible-chain-account';
@@ -56,7 +56,7 @@ export default builder.prismaNode(Prisma.ModelName.Block, {
},
})
)?.map((x) => x.key),
- predicate: parent.predicate as IGuard['predicate'],
+ predicate: parent.predicate as IKeyset['predicate'],
},
balance: 0,
transactions: [],
diff --git a/packages/apps/graph/src/graph/objects/fungible-chain-account.ts b/packages/apps/graph/src/graph/objects/fungible-chain-account.ts
index fb8268ab27..ed5ce08784 100644
--- a/packages/apps/graph/src/graph/objects/fungible-chain-account.ts
+++ b/packages/apps/graph/src/graph/objects/fungible-chain-account.ts
@@ -59,6 +59,7 @@ export default builder.node(
chainId: parent.chainId,
});
+ //If the account does not exist, the resolver will return null so we can safely use the non-null assertion operator
return {
keys: accountDetails!.guard.keys,
predicate: accountDetails!.guard.pred,
diff --git a/packages/apps/graph/src/graph/objects/guard.ts b/packages/apps/graph/src/graph/objects/guard.ts
index 98ed495c50..179459d786 100644
--- a/packages/apps/graph/src/graph/objects/guard.ts
+++ b/packages/apps/graph/src/graph/objects/guard.ts
@@ -1,9 +1,15 @@
import { builder } from '../builder';
+import JsonString from './json-string';
+import Keyset from './keyset';
-export default builder.objectType('Guard', {
- description: 'Guard for an account.',
- fields: (t) => ({
- keys: t.exposeStringList('keys'),
- predicate: t.exposeString('predicate'),
- }),
+export default builder.unionType('Guard', {
+ description: 'A guard',
+ types: [Keyset, JsonString],
+ resolveType(guard) {
+ if ('keys' in guard) {
+ return Keyset.name;
+ } else {
+ return JsonString.name;
+ }
+ },
});
diff --git a/packages/apps/graph/src/graph/objects/json-string.ts b/packages/apps/graph/src/graph/objects/json-string.ts
new file mode 100644
index 0000000000..1d14b7c27e
--- /dev/null
+++ b/packages/apps/graph/src/graph/objects/json-string.ts
@@ -0,0 +1,9 @@
+import { builder } from '../builder';
+
+export default builder.objectType('JsonString', {
+ description: 'A JSON string.',
+ fields: (t) => ({
+ type: t.exposeString('type'),
+ value: t.exposeString('value'),
+ }),
+});
diff --git a/packages/apps/graph/src/graph/objects/keyset.ts b/packages/apps/graph/src/graph/objects/keyset.ts
new file mode 100644
index 0000000000..e64fd8de31
--- /dev/null
+++ b/packages/apps/graph/src/graph/objects/keyset.ts
@@ -0,0 +1,9 @@
+import { builder } from '../builder';
+
+export default builder.objectType('Keyset', {
+ description: 'Guard for an account.',
+ fields: (t) => ({
+ keys: t.exposeStringList('keys'),
+ predicate: t.exposeString('predicate'),
+ }),
+});
diff --git a/packages/apps/graph/src/graph/objects/module-reference.ts b/packages/apps/graph/src/graph/objects/module-reference.ts
new file mode 100644
index 0000000000..24c5a10db8
--- /dev/null
+++ b/packages/apps/graph/src/graph/objects/module-reference.ts
@@ -0,0 +1,9 @@
+import { builder } from '../builder';
+
+export default builder.objectType('ModuleReference', {
+ description: 'A reference to a module.',
+ fields: (t) => ({
+ name: t.exposeString('name'),
+ namespace: t.exposeString('namespace'),
+ }),
+});
diff --git a/packages/apps/graph/src/graph/objects/non-fungible-account.ts b/packages/apps/graph/src/graph/objects/non-fungible-account.ts
index 9bd3ca0706..c7dfd2c976 100644
--- a/packages/apps/graph/src/graph/objects/non-fungible-account.ts
+++ b/packages/apps/graph/src/graph/objects/non-fungible-account.ts
@@ -9,7 +9,7 @@ import { dotenv } from '@utils/dotenv';
import { normalizeError } from '@utils/errors';
import { builder } from '../builder';
import { nonFungibleChainCheck } from '../data-loaders/non-fungible-chain-check';
-import { tokenDetailsLoader } from '../data-loaders/token-details';
+import { nonFungibleTokenBalancesLoader } from '../data-loaders/non-fungible-token-balances';
import type {
INonFungibleAccount,
INonFungibleChainAccount,
@@ -18,7 +18,7 @@ import {
NonFungibleAccountName,
NonFungibleChainAccountName,
} from '../types/graphql-types';
-import Token from './non-fungible-token-balance';
+import NonFungibleTokenBalance from './non-fungible-token-balance';
export default builder.node(
builder.objectRef(NonFungibleAccountName),
@@ -77,11 +77,11 @@ export default builder.node(
},
}),
nonFungibleTokenBalances: t.field({
- type: [Token],
+ type: [NonFungibleTokenBalance],
complexity: COMPLEXITY.FIELD.PRISMA_WITHOUT_RELATIONS,
async resolve(parent) {
try {
- const tokenDetails = await tokenDetailsLoader.load({
+ const tokenDetails = await nonFungibleTokenBalancesLoader.load({
accountName: parent.accountName,
});
diff --git a/packages/apps/graph/src/graph/objects/non-fungible-chain-account.ts b/packages/apps/graph/src/graph/objects/non-fungible-chain-account.ts
index 9b0f7d1878..b5e0d25200 100644
--- a/packages/apps/graph/src/graph/objects/non-fungible-chain-account.ts
+++ b/packages/apps/graph/src/graph/objects/non-fungible-chain-account.ts
@@ -7,7 +7,7 @@ import {
} from '@services/complexity';
import { normalizeError } from '@utils/errors';
import { builder } from '../builder';
-import { tokenDetailsLoader } from '../data-loaders/token-details';
+import { nonFungibleTokenBalancesLoader } from '../data-loaders/non-fungible-token-balances';
import type { INonFungibleChainAccount } from '../types/graphql-types';
import { NonFungibleChainAccountName } from '../types/graphql-types';
import NonFungibleTokenBalance from './non-fungible-token-balance';
@@ -45,7 +45,7 @@ export default builder.node(
complexity: COMPLEXITY.FIELD.PRISMA_WITHOUT_RELATIONS,
async resolve(parent) {
try {
- const tokenDetails = await tokenDetailsLoader.load({
+ const tokenDetails = await nonFungibleTokenBalancesLoader.load({
accountName: parent.accountName,
chainId: parent.chainId,
});
diff --git a/packages/apps/graph/src/graph/objects/non-fungible-token-balance.ts b/packages/apps/graph/src/graph/objects/non-fungible-token-balance.ts
index 4feac23618..ba8abc8169 100644
--- a/packages/apps/graph/src/graph/objects/non-fungible-token-balance.ts
+++ b/packages/apps/graph/src/graph/objects/non-fungible-token-balance.ts
@@ -1,11 +1,12 @@
-import { getTokenInfo } from '@devnet/simulation/marmalade/get-token-info';
-import type { ChainId } from '@kadena/types';
+import { COMPLEXITY } from '@services/complexity';
import { normalizeError } from '@utils/errors';
import { builder } from '../builder';
-import { tokenDetailsLoader } from '../data-loaders/token-details';
+import { nonFungibleTokenBalancesLoader } from '../data-loaders/non-fungible-token-balances';
+import { nonFungibleTokenInfoLoader } from '../data-loaders/non-fungible-token-info';
import type { INonFungibleTokenBalance } from '../types/graphql-types';
import { NonFungibleTokenBalanceName } from '../types/graphql-types';
-import NonFungibleToken from './non-fungible-token';
+import Guard from './guard';
+import NonFungibleTokenInfo from './non-fungible-token-info';
export default builder.node(
builder.objectRef(NonFungibleTokenBalanceName),
@@ -27,7 +28,7 @@ export default builder.node(
async loadOne({ tokenId, accountName, chainId }) {
try {
return (
- await tokenDetailsLoader.load({
+ await nonFungibleTokenBalancesLoader.load({
accountName,
chainId,
})
@@ -43,21 +44,24 @@ export default builder.node(
chainId: t.exposeString('chainId'),
version: t.exposeString('version'),
guard: t.field({
- type: 'Guard',
+ type: Guard,
resolve(parent) {
return parent.guard;
},
}),
info: t.field({
- type: NonFungibleToken,
+ type: NonFungibleTokenInfo,
+ complexity: COMPLEXITY.FIELD.CHAINWEB_NODE,
nullable: true,
async resolve(parent) {
try {
- return await getTokenInfo(
- parent.tokenId,
- parent.chainId.toString() as ChainId,
- parent.version,
- );
+ const tokenInfo = await nonFungibleTokenInfoLoader.load({
+ tokenId: parent.tokenId,
+ chainId: parent.chainId,
+ version: parent.version,
+ });
+
+ return tokenInfo;
} catch (error) {
throw normalizeError(error);
}
diff --git a/packages/apps/graph/src/graph/objects/non-fungible-token.ts b/packages/apps/graph/src/graph/objects/non-fungible-token-info.ts
similarity index 54%
rename from packages/apps/graph/src/graph/objects/non-fungible-token.ts
rename to packages/apps/graph/src/graph/objects/non-fungible-token-info.ts
index 45ebb7b242..79e4db6a83 100644
--- a/packages/apps/graph/src/graph/objects/non-fungible-token.ts
+++ b/packages/apps/graph/src/graph/objects/non-fungible-token-info.ts
@@ -1,10 +1,15 @@
import { builder } from '../builder';
+import Policy from './policy';
-export default builder.objectType('NonFungibleToken', {
+export default builder.objectType('NonFungibleTokenInfo', {
description: 'Information related to a token.',
fields: (t) => ({
supply: t.exposeInt('supply'),
precision: t.exposeInt('precision'),
uri: t.exposeString('uri'),
+ policies: t.field({
+ type: [Policy],
+ resolve: (parent) => parent.policies,
+ }),
}),
});
diff --git a/packages/apps/graph/src/graph/objects/policy.ts b/packages/apps/graph/src/graph/objects/policy.ts
new file mode 100644
index 0000000000..0dfb0daa7c
--- /dev/null
+++ b/packages/apps/graph/src/graph/objects/policy.ts
@@ -0,0 +1,16 @@
+import { builder } from '../builder';
+import ModuleReference from './module-reference';
+
+export default builder.objectType('Policy', {
+ description: 'A policy that defines the rules for a non-fungible token.',
+ fields: (t) => ({
+ refSpec: t.field({
+ type: [ModuleReference],
+ resolve: (parent) => parent.refSpec,
+ }),
+ refName: t.field({
+ type: ModuleReference,
+ resolve: (parent) => parent.refName,
+ }),
+ }),
+});
diff --git a/packages/apps/graph/src/graph/types/graphql-types.ts b/packages/apps/graph/src/graph/types/graphql-types.ts
index 7f42f095c5..fdf2422b03 100644
--- a/packages/apps/graph/src/graph/types/graphql-types.ts
+++ b/packages/apps/graph/src/graph/types/graphql-types.ts
@@ -1,6 +1,6 @@
import type { Signer, Transaction, Transfer } from '@prisma/client';
-export interface IGuard {
+export interface IKeyset {
keys: string[];
predicate: 'keys-all' | 'keys-any' | 'keys-two';
}
@@ -22,17 +22,26 @@ export interface INonFungibleTokenBalance {
balance: number;
accountName: string;
chainId: string;
- guard: IGuard;
- info?: INonFungibleToken;
+ guard: IKeyset | IJsonString;
+ info?: INonFungibleTokenInfo;
version: string;
}
-export interface INonFungibleToken {
+export interface IModuleReference {
+ name: string;
+ namespace: string;
+}
+
+export interface IPolicy {
+ refSpec: IModuleReference[];
+ refName: IModuleReference;
+}
+
+export interface INonFungibleTokenInfo {
supply: number;
precision: number;
uri: string;
- // TODO: figure out what to do with weird pact-arrays
- // policies: string[];
+ policies: [IPolicy];
}
export const FungibleChainAccountName: 'FungibleChainAccount' =
@@ -43,7 +52,7 @@ export interface IFungibleChainAccount {
chainId: string;
fungibleName: string;
accountName: string;
- guard: IGuard;
+ guard: IKeyset;
balance: number;
transactions: Transaction[];
transfers: Transfer[];
@@ -161,6 +170,10 @@ export interface IPactQueryResponse {
code: string;
}
+export interface IJsonString {
+ type: string;
+ value: string;
+}
export interface INetworkInfo {
networkHost: string;
networkId: string;
diff --git a/packages/apps/graph/src/services/account-service.ts b/packages/apps/graph/src/services/account-service.ts
index fe02aeb3cc..890541edcc 100644
--- a/packages/apps/graph/src/services/account-service.ts
+++ b/packages/apps/graph/src/services/account-service.ts
@@ -1,5 +1,5 @@
import { fungibleAccountDetailsLoader } from '../graph/data-loaders/fungible-account-details';
-import { tokenDetailsLoader } from '../graph/data-loaders/token-details';
+import { nonFungibleTokenBalancesLoader } from '../graph/data-loaders/non-fungible-token-balances';
import type {
IFungibleChainAccount,
INonFungibleChainAccount,
@@ -24,21 +24,23 @@ export async function getFungibleChainAccount({
chainId,
});
- return accountDetails !== null
- ? {
- __typename: FungibleChainAccountName,
- chainId,
- accountName,
- fungibleName,
- guard: {
- keys: accountDetails.guard.keys,
- predicate: accountDetails.guard.pred,
- },
- balance: accountDetails.balance,
- transactions: [],
- transfers: [],
- }
- : null;
+ if (!accountDetails || accountDetails === null) return null;
+
+ const guard = {
+ predicate: accountDetails.guard.pred,
+ keys: accountDetails.guard.keys,
+ };
+
+ return {
+ __typename: FungibleChainAccountName,
+ chainId,
+ accountName,
+ fungibleName,
+ guard,
+ balance: accountDetails.balance,
+ transactions: [],
+ transfers: [],
+ };
}
export async function getNonFungibleChainAccount({
@@ -48,7 +50,10 @@ export async function getNonFungibleChainAccount({
chainId: string;
accountName: string;
}): Promise {
- const tokenDetails = await tokenDetailsLoader.load({ accountName, chainId });
+ const tokenDetails = await nonFungibleTokenBalancesLoader.load({
+ accountName,
+ chainId,
+ });
return tokenDetails !== null && tokenDetails.length !== 0
? {
diff --git a/packages/apps/graph/src/services/chainweb-node/fungible-account-details.ts b/packages/apps/graph/src/services/chainweb-node/fungible-account-details.ts
index 056e9ceb2c..ca4ba6f572 100644
--- a/packages/apps/graph/src/services/chainweb-node/fungible-account-details.ts
+++ b/packages/apps/graph/src/services/chainweb-node/fungible-account-details.ts
@@ -2,7 +2,8 @@ import { details } from '@kadena/client-utils/coin';
import type { ChainId } from '@kadena/types';
import { dotenv } from '@utils/dotenv';
import { networkData } from '@utils/network';
-import type { IGuard } from '../../graph/types/graphql-types';
+import { withRetry } from '@utils/withRetry';
+import type { IKeyset } from '../../graph/types/graphql-types';
import { PactCommandError } from './utils';
export interface IFungibleChainAccountDetails {
@@ -10,7 +11,7 @@ export interface IFungibleChainAccountDetails {
balance: number;
guard: {
keys: string[];
- pred: IGuard['predicate'];
+ pred: IKeyset['predicate'];
};
}
@@ -18,8 +19,6 @@ export async function getFungibleAccountDetails(
fungibleName: string,
accountName: string,
chainId: string,
- retries = dotenv.CHAINWEB_NODE_RETRY_ATTEMPTS,
- delay = dotenv.CHAINWEB_NODE_RETRY_DELAY,
): Promise {
let result;
@@ -45,17 +44,13 @@ export async function getFungibleAccountDetails(
) {
return null;
} else {
- if (retries > 0) {
- await new Promise((resolve) => setTimeout(resolve, delay));
- return getFungibleAccountDetails(
- fungibleName,
- accountName,
- chainId,
- retries - 1,
- );
- } else {
- throw new PactCommandError('Pact Command failed with error', result);
- }
+ throw new PactCommandError('Pact Command failed with error', error);
}
}
}
+
+export const getFungibleAccountDetailsWithRetry = withRetry(
+ getFungibleAccountDetails,
+ dotenv.CHAINWEB_NODE_RETRY_ATTEMPTS,
+ dotenv.CHAINWEB_NODE_RETRY_DELAY,
+);
diff --git a/packages/apps/graph/src/services/chainweb-node/non-fungible-account-details.ts b/packages/apps/graph/src/services/chainweb-node/non-fungible-account-details.ts
index 78db0be052..adaa01d8ce 100644
--- a/packages/apps/graph/src/services/chainweb-node/non-fungible-account-details.ts
+++ b/packages/apps/graph/src/services/chainweb-node/non-fungible-account-details.ts
@@ -1,71 +1,57 @@
-import type { IClient } from '@kadena/client';
-import { Pact, createClient } from '@kadena/client';
-import type { ChainId } from '@kadena/types';
+import { getNonFungibleTokenDetails } from '@services/token-service';
import { dotenv } from '@utils/dotenv';
-import { networkData } from '@utils/network';
-import type { IGuard } from '../../graph/types/graphql-types';
+import { withRetry } from '@utils/withRetry';
+import type { IJsonString, IKeyset } from '../../graph/types/graphql-types';
import { PactCommandError } from './utils';
export interface INonFungibleChainAccountDetails {
id: string;
account: string;
balance: number;
- guard: {
- keys: string[];
- pred: IGuard['predicate'];
- };
-}
-
-function getClient(chainId: string): IClient {
- return createClient(
- `${dotenv.NETWORK_HOST}/chainweb/${networkData.apiVersion}/${networkData.networkId}/chain/${chainId}/pact`,
- );
+ guard: IKeyset | IJsonString;
}
export async function getNonFungibleAccountDetails(
tokenId: string,
accountName: string,
chainId: string,
+ version?: string,
): Promise {
- let result;
-
try {
- let commandResult;
-
- commandResult = await getClient(chainId).dirtyRead(
- Pact.builder
- .execution(
- Pact.modules['marmalade.ledger'].details(tokenId, accountName),
- )
- .setMeta({
- chainId: chainId as ChainId,
- })
- .setNetworkId(networkData.networkId)
- .createTransaction(),
+ const commandResult = await getNonFungibleTokenDetails(
+ tokenId,
+ accountName,
+ chainId,
+ version,
);
- if (commandResult.result.status === 'failure') {
- commandResult = await getClient(chainId).dirtyRead(
- Pact.builder
- .execution(
- Pact.modules['marmalade-v2.ledger'].details(tokenId, accountName),
- )
- .setMeta({
- chainId: chainId as ChainId,
- })
- .setNetworkId(networkData.networkId)
- .createTransaction(),
- );
- }
-
const result =
// eslint-disable-next-line @typescript-eslint/no-explicit-any
- (commandResult.result as unknown as any).data as unknown as any;
+ commandResult as unknown as any as unknown as any;
if (typeof result.balance === 'object') {
result.balance = parseFloat(result.balance.decimal);
}
+ if ('guard' in result) {
+ if (
+ 'keys' in result.guard &&
+ typeof result.guard.keys === 'object' &&
+ 'pred' in result.guard &&
+ typeof result.guard.pred === 'string'
+ ) {
+ result.guard = {
+ keys: result.guard.keys,
+ predicate: result.guard.pred,
+ };
+ } else {
+ result.guard = {
+ type: 'Guard',
+ value: JSON.stringify(result.guard),
+ };
+ }
+ }
+
return result as INonFungibleChainAccountDetails;
} catch (error) {
if (
@@ -73,7 +59,13 @@ export async function getNonFungibleAccountDetails(
) {
return null;
} else {
- throw new PactCommandError('Pact Command failed with error', result);
+ throw new PactCommandError('Pact Command failed with error', error);
}
}
}
+
+export const getNonFungibleAccountDetailsWithRetry = withRetry(
+ getNonFungibleAccountDetails,
+ dotenv.CHAINWEB_NODE_RETRY_ATTEMPTS,
+ dotenv.CHAINWEB_NODE_RETRY_DELAY,
+);
diff --git a/packages/apps/graph/src/services/token-service.ts b/packages/apps/graph/src/services/token-service.ts
index ec899a72f0..56eface059 100644
--- a/packages/apps/graph/src/services/token-service.ts
+++ b/packages/apps/graph/src/services/token-service.ts
@@ -1,11 +1,19 @@
import { prismaClient } from '@db/prisma-client';
+import type { ChainId } from '@kadena/client';
+import { Pact } from '@kadena/client';
+import { dirtyReadClient } from '@kadena/client-utils/core';
+import { composePactCommand, execution, setMeta } from '@kadena/client/fp';
import type { Prisma } from '@prisma/client';
import { dotenv } from '@utils/dotenv';
+import { withRetry } from '@utils/withRetry';
import { nonFungibleAccountDetailsLoader } from '../graph/data-loaders/non-fungible-account-details';
-import type { INonFungibleTokenBalance } from '../graph/types/graphql-types';
+import type {
+ INonFungibleTokenBalance,
+ INonFungibleTokenInfo,
+} from '../graph/types/graphql-types';
import { NonFungibleTokenBalanceName } from '../graph/types/graphql-types';
-export async function getTokenDetails(
+export async function getNonFungibleTokenBalances(
accountName: string,
chainId?: string,
): Promise {
@@ -63,18 +71,22 @@ export async function getTokenDetails(
tokenId: event.token,
accountName: accountName,
chainId: finalChainId,
+ version: event.version!,
});
+ if (!accountDetails) {
+ throw new Error(
+ `Account details not found for token ${event.token} and account ${accountName}`,
+ );
+ }
+
result.push({
__typename: NonFungibleTokenBalanceName,
balance: Number(balance),
accountName,
tokenId: event.token,
chainId: finalChainId,
- guard: {
- keys: accountDetails!.guard.keys,
- predicate: accountDetails!.guard.pred,
- },
+ guard: accountDetails.guard,
version: event.version!,
});
processedTokens.add(tokenChainIdKey);
@@ -110,3 +122,166 @@ export async function checkAccountChains(
return Array.from(chainIds);
}
+
+export async function getNonFungibleTokenInfo(
+ tokenId: string,
+ chainId: string,
+ version: string,
+): Promise {
+ if (version !== 'v1' && version !== 'v2') {
+ throw new Error(
+ `Invalid version found for token ${tokenId}. Got ${version} but expected v1 or v2.`,
+ );
+ }
+
+ let executionCmd;
+ let tokenInfo;
+
+ if (version === 'v1') {
+ executionCmd = execution(
+ Pact.modules['marmalade.ledger']['get-policy-info'](tokenId),
+ );
+ // Note: Alternative approach left for reference
+ // executionCmd = execution(`(bind
+ // (marmalade.ledger.get-policy-info "${tokenId}")
+ // {"token" := token }
+ // (bind
+ // token
+ // { "id" := id, "precision":= precision, "supply":= supply, "manifest":= manifest }
+ // { "id": id, "precision": precision, "supply": supply, "uri":
+ // (format
+ // "data:{},{}"
+ // [
+ // (at 'scheme (at 'uri manifest))
+ // (at 'data (at 'uri manifest))
+ // ]
+ // )
+ // }
+ // )
+ // )`);
+ } else {
+ executionCmd = execution(
+ Pact.modules['marmalade-v2.ledger']['get-token-info'](tokenId),
+ );
+ // Note: Alternative approach left for reference
+ // executionCmd = execution(`(bind
+ // (marmalade-v2.ledger.get-token-info "${tokenId}")
+ // { "id" := id, "precision":= precision, "supply" := supply, "uri" := uri }
+ // { "id" : id, "precision": precision, "supply" : supply, "uri" : uri }
+ // )`);
+ }
+
+ const command = composePactCommand(
+ executionCmd,
+
+ setMeta({
+ chainId: chainId as ChainId,
+ }),
+ );
+
+ const config = {
+ host: dotenv.NETWORK_HOST,
+ defaults: {
+ networkId: dotenv.NETWORK_ID,
+ },
+ };
+
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const tokenInfoResult = await dirtyReadClient(config)(command).execute();
+
+ if (!tokenInfoResult) {
+ return null;
+ }
+
+ // Note: Alternative approach left for reference
+ if (version === 'v1') {
+ if ('token' in tokenInfoResult) {
+ tokenInfo = tokenInfoResult.token;
+ }
+
+ // if ('policy' in tokenInfoResult) {
+ // policies = Array(tokenInfoResult.policy);
+ // }
+
+ if ('manifest' in tokenInfo) {
+ tokenInfo.uri = `data:${tokenInfo.manifest.uri.scheme},${tokenInfo.manifest.uri.data}`;
+ }
+ } else {
+ tokenInfo = tokenInfoResult;
+ // if ('policies' in tokenInfoResult) {
+ // policies = tokenInfoResult.policies;
+ // }
+ }
+
+ if ('precision' in tokenInfo) {
+ if (
+ typeof tokenInfo.precision === 'object' &&
+ tokenInfo.precision !== null
+ ) {
+ tokenInfo.precision = (tokenInfo.precision as { int: number }).int;
+ }
+ }
+
+ if ('policy' in tokenInfoResult || 'policies' in tokenInfoResult) {
+ if (version === 'v1') {
+ tokenInfo.policies = Array(tokenInfoResult.policy);
+ } else {
+ tokenInfo.policies = tokenInfoResult.policies;
+ }
+ }
+
+ return tokenInfo as INonFungibleTokenInfo;
+}
+
+export const getNonFungibleTokenInfoWithRetry = withRetry(
+ getNonFungibleTokenInfo,
+ dotenv.CHAINWEB_NODE_RETRY_ATTEMPTS,
+ dotenv.CHAINWEB_NODE_RETRY_DELAY,
+);
+
+// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
+export async function getNonFungibleTokenDetails(
+ tokenId: string,
+ accountName: string,
+ chainId: string,
+ version?: string,
+) {
+ const config = {
+ host: dotenv.NETWORK_HOST,
+ defaults: {
+ networkId: dotenv.NETWORK_ID,
+ },
+ };
+
+ const commandV2 = composePactCommand(
+ execution(
+ Pact.modules['marmalade-v2.ledger'].details(tokenId, accountName),
+ ),
+ setMeta({
+ chainId: chainId as ChainId,
+ }),
+ );
+
+ const commandV1 = composePactCommand(
+ execution(Pact.modules['marmalade.ledger'].details(tokenId, accountName)),
+ setMeta({
+ chainId: chainId as ChainId,
+ }),
+ );
+
+ if (version === 'v2') {
+ return dirtyReadClient(config)(commandV2).execute();
+ } else if (version === 'v1') {
+ return dirtyReadClient(config)(commandV1).execute();
+ } else {
+ try {
+ return await dirtyReadClient(config)(commandV1).execute();
+ } catch (error) {
+ // As safety measure, we're doing a timeout before retrying the command
+ await new Promise((resolve) =>
+ setTimeout(resolve, dotenv.CHAINWEB_NODE_RETRY_DELAY),
+ );
+ return await dirtyReadClient(config)(commandV2).execute();
+ }
+ }
+}
diff --git a/packages/apps/graph/src/utils/errors.ts b/packages/apps/graph/src/utils/errors.ts
index 3373c088ac..791d1b5435 100644
--- a/packages/apps/graph/src/utils/errors.ts
+++ b/packages/apps/graph/src/utils/errors.ts
@@ -191,6 +191,23 @@ export function normalizeError(error: unknown): GraphQLError {
});
}
+ if (
+ error instanceof Error &&
+ 'code' in error &&
+ 'type' in error &&
+ error.code === 'ECONNRESET'
+ ) {
+ return new GraphQLError('Chainweb Node Connection Reset', {
+ extensions: {
+ type: error.type,
+ message: error.message,
+ description:
+ 'Chainweb Node connection reset. Check if you have reached any rate limits, or if the Chainweb Node is overloaded.',
+ data: error.stack,
+ },
+ });
+ }
+
return new GraphQLError('Unknown error occured.', {
extensions: {
type: 'UnknownError',
diff --git a/packages/apps/graph/src/utils/validate-object.ts b/packages/apps/graph/src/utils/validate-object.ts
index 0159ce2552..c6e2516465 100644
--- a/packages/apps/graph/src/utils/validate-object.ts
+++ b/packages/apps/graph/src/utils/validate-object.ts
@@ -4,11 +4,13 @@ import { nullishOrEmpty } from './nullish-or-empty';
export function validateObjectProperties>(
obj: T,
typeName: string,
-): void {
+): boolean {
Object.entries(obj).forEach(([key, value]) => {
console.log(key, value);
if (nullishOrEmpty(value)) {
throw new Error(`${typeName} ${key} not specified`);
}
});
+
+ return true;
}
diff --git a/packages/apps/graph/src/utils/withRetry.ts b/packages/apps/graph/src/utils/withRetry.ts
new file mode 100644
index 0000000000..f63bb16ccb
--- /dev/null
+++ b/packages/apps/graph/src/utils/withRetry.ts
@@ -0,0 +1,24 @@
+import { dotenv } from './dotenv';
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export function withRetry(
+ func: (...args: T) => Promise,
+ retries: number = dotenv.CHAINWEB_NODE_RETRY_ATTEMPTS,
+ delay: number = dotenv.CHAINWEB_NODE_RETRY_DELAY,
+): (...funcArgs: T) => Promise {
+ return async (...funcArgs: T) => {
+ for (let i = 1; i <= retries; i++) {
+ try {
+ return await func(...funcArgs);
+ } catch (error) {
+ if (i < retries) {
+ await new Promise((resolve) => setTimeout(resolve, delay));
+ } else {
+ throw error;
+ }
+ }
+ }
+ // This will never be reached, but TypeScript doesn't know that
+ return await func(...funcArgs);
+ };
+}
diff --git a/packages/e2e/e2e-graph/queries/getAccount.ts b/packages/e2e/e2e-graph/queries/getAccount.ts
index 6bbefab91e..4d5554e7cf 100644
--- a/packages/e2e/e2e-graph/queries/getAccount.ts
+++ b/packages/e2e/e2e-graph/queries/getAccount.ts
@@ -6,8 +6,10 @@ export function getAccountQuery(accountName: string) {
chainAccounts {
...CoreChainAccountFields
guard {
- keys
- predicate
+ ... on Keyset {
+ keys
+ predicate
+ }
}
}
transactions {