@glamsystems/glam-sdk 1.0.10 → 1.0.12-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/index.cjs.js +11052 -7904
  2. package/index.esm.js +11046 -7904
  3. package/package.json +1 -1
  4. package/src/client/base.d.ts +5 -1
  5. package/src/client/fees.d.ts +3 -0
  6. package/src/client/mint.d.ts +5 -1
  7. package/src/client/price.d.ts +3 -2
  8. package/src/clientConfig.d.ts +1 -0
  9. package/src/constants.d.ts +3 -3
  10. package/src/deser/integrationPolicies.d.ts +29 -1
  11. package/src/glamExports.d.ts +527 -47
  12. package/src/models/state.d.ts +3 -1
  13. package/src/react/glam.d.ts +1 -0
  14. package/src/react/index.d.ts +2 -0
  15. package/src/react/query-keys.d.ts +39 -0
  16. package/src/react/useVaultBalanceSubscription.d.ts +17 -0
  17. package/src/utils/bitmask.d.ts +4 -3
  18. package/src/utils/index.d.ts +0 -1
  19. package/src/utils/lookupTables.d.ts +45 -1
  20. package/src/utils/timelock.d.ts +11 -4
  21. package/src/utils/transaction.d.ts +1 -1
  22. package/target/idl/ext_drift-staging.json +4 -0
  23. package/target/idl/ext_drift.json +4 -0
  24. package/target/idl/glam_config.json +12 -2
  25. package/target/idl/glam_mint-staging.json +16 -186
  26. package/target/idl/glam_mint.json +16 -186
  27. package/target/idl/glam_policies.json +6 -1
  28. package/target/idl/glam_protocol-staging.json +536 -7
  29. package/target/idl/glam_protocol.json +536 -7
  30. package/target/types/ext_drift-staging.ts +4 -0
  31. package/target/types/ext_drift.d.ts +4 -0
  32. package/target/types/ext_drift.ts +4 -0
  33. package/target/types/ext_offchain.d.ts +1080 -0
  34. package/target/types/glam_config.d.ts +12 -2
  35. package/target/types/glam_config.ts +12 -2
  36. package/target/types/glam_mint-staging.ts +16 -186
  37. package/target/types/glam_mint.d.ts +16 -186
  38. package/target/types/glam_mint.ts +16 -186
  39. package/target/types/glam_policies.ts +6 -1
  40. package/target/types/glam_protocol-staging.ts +536 -7
  41. package/target/types/glam_protocol.d.ts +536 -7
  42. package/target/types/glam_protocol.ts +536 -7
  43. package/src/utils/glamApi.d.ts +0 -12
@@ -4,6 +4,7 @@ import { PublicKey } from "@solana/web3.js";
4
4
  import { Mint } from "@solana/spl-token";
5
5
  import { BN } from "@coral-xyz/anchor";
6
6
  import { MintModel } from "./mint";
7
+ import type { FeeStructure } from "./mint";
7
8
  import type { RequestQueue } from "./types";
8
9
  import type { IntegrationAcl, DelegateAcl } from "./acl";
9
10
  export type StateAccount = IdlAccounts<GlamProtocol>["stateAccount"];
@@ -26,6 +27,7 @@ export declare class StateIdlModel implements StateModelType {
26
27
  timelockDuration: number | null;
27
28
  integrationAcls: IntegrationAcl[] | null;
28
29
  delegateAcls: DelegateAcl[] | null;
30
+ feeStructure: FeeStructure | null;
29
31
  constructor(data: Partial<StateModelType>);
30
32
  }
31
33
  /**
@@ -69,7 +71,7 @@ export declare class StateModel extends StateIdlModel {
69
71
  * @param openfundsMetadataAccount includes fund rawOpenfunds data and share class rawOpenfunds data
70
72
  * @param glamMint
71
73
  */
72
- static fromOnchainAccounts(statePda: PublicKey, stateAccount: StateAccount, glamMint?: Mint, requestQueue?: RequestQueue): StateModel;
74
+ static fromOnchainAccounts(statePda: PublicKey, stateAccount: StateAccount, staging: boolean, glamMint?: Mint, requestQueue?: RequestQueue): StateModel;
73
75
  }
74
76
  export type CreatedModelType = IdlTypes<GlamProtocol>["createdModel"];
75
77
  export declare class CreatedModel implements CreatedModelType {
@@ -24,6 +24,7 @@ interface GlamProviderContext {
24
24
  allGlamStates: StateModel[];
25
25
  jupTokenList?: JupTokenList;
26
26
  driftMarketConfigs?: DriftMarketConfigs;
27
+ wsConnected: boolean;
27
28
  setActiveGlamState: (f: GlamStateCache) => void;
28
29
  refresh: () => Promise<void>;
29
30
  refetchGlamStates: () => Promise<void>;
@@ -1,3 +1,5 @@
1
1
  export * from "../index";
2
2
  export * from "./glam";
3
3
  export * from "./cluster-provider";
4
+ export * from "./query-keys";
5
+ export * from "./useVaultBalanceSubscription";
@@ -0,0 +1,39 @@
1
+ export declare const queryKeys: {
2
+ readonly vault: {
3
+ readonly all: (pk: string, cluster: string) => readonly ["vault", string, string];
4
+ readonly balances: (pk: string, cluster: string) => readonly ["vault", string, string, "balances"];
5
+ readonly holdings: (pk: string, cluster: string) => readonly ["vault", string, string, "holdings"];
6
+ readonly acls: (pk: string, cluster: string) => readonly ["vault", string, string, "acls"];
7
+ readonly config: (pk: string, cluster: string) => readonly ["vault", string, string, "config"];
8
+ readonly driftUsers: (pk: string, cluster: string) => readonly ["vault", string, string, "drift-users"];
9
+ readonly stakes: (pk: string, cluster: string) => readonly ["vault", string, string, "stakes"];
10
+ readonly holders: (mint: string, cluster: string) => readonly ["vault", "holders", string, string];
11
+ };
12
+ readonly wallet: {
13
+ readonly balances: (pk: string, cluster: string) => readonly ["wallet", string, "balances", string];
14
+ };
15
+ readonly global: {
16
+ readonly allStates: (cluster: string) => readonly ["global", "all-states", string];
17
+ readonly jupTokens: (cluster: string) => readonly ["global", "jup-tokens", string];
18
+ readonly jupDexes: (cluster: string) => readonly ["global", "jup-dexes", string];
19
+ readonly driftMarkets: (cluster: string) => readonly ["global", "drift-markets", string];
20
+ readonly kaminoMarkets: (cluster: string) => readonly ["global", "kamino-markets", string];
21
+ readonly priorityFee: (cluster: string) => readonly ["global", "priority-fee", string];
22
+ };
23
+ readonly vaultGraph: {
24
+ readonly vault: (vaultId: string, cluster: string) => readonly ["vault-graph", "vault", string, string];
25
+ readonly tokenData: (mint: string, cluster: string) => readonly ["vault-graph", "token-data", string, string];
26
+ readonly availableIntegrations: (cluster: string) => readonly ["vault-graph", "available-integrations", string];
27
+ };
28
+ readonly tx: {
29
+ readonly status: (sig: string) => readonly ["tx", string];
30
+ };
31
+ };
32
+ export type InvalidationScope = "balance" | "drift" | "acls" | "wallet" | "graph" | "stakes" | "all";
33
+ export interface InvalidationContext {
34
+ vaultPk: string;
35
+ walletPk: string;
36
+ cluster: string;
37
+ }
38
+ /** Maps invalidation scopes to the query keys that need to be invalidated. */
39
+ export declare function getInvalidationKeys(scopes: InvalidationScope[], ctx: InvalidationContext): readonly (readonly string[])[];
@@ -0,0 +1,17 @@
1
+ import { Connection, PublicKey } from "@solana/web3.js";
2
+ import { QueryClient } from "@tanstack/react-query";
3
+ import { TokenAccount } from "../client/base";
4
+ export interface UseVaultBalanceSubscriptionOptions {
5
+ connection: Connection;
6
+ queryClient: QueryClient;
7
+ vaultPda: PublicKey | undefined;
8
+ /** Base58 string of the active vault state pubkey (used for query key scoping) */
9
+ pk: string;
10
+ /** Cluster network string for query key scoping */
11
+ cluster: string;
12
+ tokenAccounts: TokenAccount[];
13
+ enabled: boolean;
14
+ /** Called when WebSocket connection status changes (for polling fallback) */
15
+ onWsStatusChange: (connected: boolean) => void;
16
+ }
17
+ export declare function useVaultBalanceSubscription({ connection, queryClient, vaultPda, pk, cluster, tokenAccounts, enabled, onWsStatusChange, }: UseVaultBalanceSubscriptionOptions): void;
@@ -15,7 +15,7 @@ export declare function formatBits(bitmask: number | BN, padding?: number): stri
15
15
  * @param protocolsBitmask - The bitmask to parse.
16
16
  * @returns An object containing the bitmask string and a list of protocol names.
17
17
  */
18
- export declare function parseProtocolsBitmask(integrationProgram: PublicKey, protocolsBitmask: number | BN): {
18
+ export declare function parseProtocolsBitmask(integrationProgram: PublicKey, protocolsBitmask: number | BN, staging: boolean): {
19
19
  protocols: {
20
20
  bitflag: number | BN;
21
21
  name: string;
@@ -29,7 +29,7 @@ export declare function parseProtocolsBitmask(integrationProgram: PublicKey, pro
29
29
  * @param permissionsBitmask - The permissions bitmask to parse.
30
30
  * @returns An object containing the bitmask string and a list of permission names.
31
31
  */
32
- export declare function parseProtocolPermissionsBitmask(integrationProgram: PublicKey, protocolBitflag: number | BN, permissionsBitmask: number | BN): {
32
+ export declare function parseProtocolPermissionsBitmask(integrationProgram: PublicKey, protocolBitflag: number | BN, permissionsBitmask: number | BN, staging: boolean): {
33
33
  protocol: string;
34
34
  permissions: {
35
35
  bitflag: number | BN;
@@ -39,9 +39,10 @@ export declare function parseProtocolPermissionsBitmask(integrationProgram: Publ
39
39
  /**
40
40
  * Given the protocol name and a list of permission names, returns the permissions bitmask.
41
41
  */
42
- export declare function parsePermissionNames({ protocolName, permissionNames, }: {
42
+ export declare function parsePermissionNames({ protocolName, permissionNames, staging, }: {
43
43
  protocolName: string;
44
44
  permissionNames: string[];
45
+ staging: boolean;
45
46
  }): {
46
47
  integrationProgram: PublicKey;
47
48
  protocolBitflag: number;
@@ -4,7 +4,6 @@ export * from "./blockhash";
4
4
  export * from "./computeBudget";
5
5
  export * from "./evm";
6
6
  export * from "./common";
7
- export * from "./glamApi";
8
7
  export * from "./glamPDAs";
9
8
  export * from "./lookupTables";
10
9
  export * from "./rpc";
@@ -1,4 +1,4 @@
1
- import { AddressLookupTableAccount, Connection, PublicKey } from "@solana/web3.js";
1
+ import { AddressLookupTableAccount, Connection, PublicKey, TransactionInstruction } from "@solana/web3.js";
2
2
  /**
3
3
  * Fetches multiple address lookup table accounts
4
4
  *
@@ -7,3 +7,47 @@ import { AddressLookupTableAccount, Connection, PublicKey } from "@solana/web3.j
7
7
  * @returns Array of address lookup table accounts
8
8
  */
9
9
  export declare function fetchAddressLookupTableAccounts(connection: Connection, pubkeys?: string[] | PublicKey[]): Promise<AddressLookupTableAccount[]>;
10
+ export interface VaultAccountsInfo {
11
+ statePda: PublicKey;
12
+ vaultPda: PublicKey;
13
+ mintPda: PublicKey;
14
+ escrowPda: PublicKey;
15
+ requestQueuePda: PublicKey;
16
+ extraMetasPda: PublicKey;
17
+ protocolProgramId: PublicKey;
18
+ mintProgramId: PublicKey;
19
+ connection: Connection;
20
+ stateAccount: {
21
+ mint: PublicKey;
22
+ baseAssetMint: PublicKey;
23
+ assets: PublicKey[];
24
+ externalPositions: PublicKey[];
25
+ integrationAcls: {
26
+ integrationProgram: PublicKey;
27
+ }[];
28
+ };
29
+ borrowable?: PublicKey[];
30
+ }
31
+ /**
32
+ * Collects all pubkeys that should be included in a vault's Address Lookup Table.
33
+ *
34
+ * The first 3 entries are fixed (statePda, vaultPda, GLAM_CONFIG_PROGRAM) to
35
+ * match the ALT discovery filter offsets used by findGlamLookupTables().
36
+ */
37
+ export declare function collectVaultLookupTableAddresses(info: VaultAccountsInfo): Promise<PublicKey[]>;
38
+ export interface CreateAltResult {
39
+ createIx: TransactionInstruction;
40
+ lookupTableAddress: PublicKey;
41
+ extendIxBatches: TransactionInstruction[];
42
+ }
43
+ /**
44
+ * Builds instructions to create an ALT and extend it with the given addresses.
45
+ * Returns the create instruction, the derived lookup table address, and batched
46
+ * extend instructions (each batch fits within a single transaction).
47
+ */
48
+ export declare function buildCreateAltInstructions(addresses: PublicKey[], authority: PublicKey, payer: PublicKey, recentSlot: number): CreateAltResult;
49
+ /**
50
+ * Builds batched extend instructions for an existing ALT.
51
+ * Each instruction extends the table with up to 20 addresses.
52
+ */
53
+ export declare function buildExtendAltInstructions(lookupTableAddress: PublicKey, newAddresses: PublicKey[], authority: PublicKey, payer: PublicKey): TransactionInstruction[];
@@ -5,16 +5,23 @@ import { IntegrationAcl, DelegateAcl } from "../models";
5
5
  * Get protocol names from a bitmask for a given integration program
6
6
  * Uses the SDK's parseProtocolsBitmask function
7
7
  */
8
- export declare function getProtocolNamesFromBitmask(integrationProgram: PublicKey, bitmask: number): string[];
8
+ export declare function getProtocolNamesFromBitmask(integrationProgram: PublicKey, bitmask: number, staging: boolean): string[];
9
9
  /**
10
10
  * Get permission names from a permissions bitmask for a protocol
11
11
  * Uses the SDK's parseProtocolPermissionsBitmask function
12
12
  */
13
- export declare function getPermissionNamesFromBitmask(integrationProgram: PublicKey, protocolBitflag: number, permissionsBitmask: BN): string[];
13
+ export declare function getPermissionNamesFromBitmask(integrationProgram: PublicKey, protocolBitflag: number, permissionsBitmask: BN, staging: boolean): string[];
14
+ /**
15
+ * Compare current and staged PublicKey arrays and return added/removed keys
16
+ */
17
+ export declare function comparePublicKeyArrays(current: PublicKey[] | null, staged: PublicKey[]): {
18
+ added: PublicKey[];
19
+ removed: PublicKey[];
20
+ };
14
21
  /**
15
22
  * Compare current and staged integrationAcls and return the differences
16
23
  */
17
- export declare function compareIntegrationAcls(current: IntegrationAcl[] | null, staged: IntegrationAcl[] | null): {
24
+ export declare function compareIntegrationAcls(current: IntegrationAcl[] | null, staged: IntegrationAcl[] | null, staging: boolean): {
18
25
  added: IntegrationAcl[];
19
26
  removed: IntegrationAcl[];
20
27
  modified: Array<{
@@ -28,7 +35,7 @@ export declare function compareIntegrationAcls(current: IntegrationAcl[] | null,
28
35
  /**
29
36
  * Compare current and staged delegateAcls and return the differences
30
37
  */
31
- export declare function compareDelegateAcls(current: DelegateAcl[] | null, staged: DelegateAcl[] | null): {
38
+ export declare function compareDelegateAcls(current: DelegateAcl[] | null, staged: DelegateAcl[] | null, staging: boolean): {
32
39
  added: DelegateAcl[];
33
40
  removed: DelegateAcl[];
34
41
  modified: Array<{
@@ -3,7 +3,7 @@ import { Connection, PublicKey, TransactionInstruction, AddressLookupTableAccoun
3
3
  * Parses program logs to extract error message
4
4
  */
5
5
  export declare function parseProgramLogs(logs?: null | string[]): string;
6
- export declare const getSimulationResult: (connection: Connection, instructions: Array<TransactionInstruction>, payer: PublicKey, lookupTables?: Array<AddressLookupTableAccount>) => Promise<{
6
+ export declare const getSimulationResult: (connection: Connection, instructions: Array<TransactionInstruction>, payer: PublicKey, lookupTables?: Array<AddressLookupTableAccount>, staging?: boolean) => Promise<{
7
7
  unitsConsumed?: number;
8
8
  error?: Error;
9
9
  serializedTx?: String;
@@ -2705,6 +2705,10 @@
2705
2705
  "type": {
2706
2706
  "vec": "pubkey"
2707
2707
  }
2708
+ },
2709
+ {
2710
+ "name": "order_price_tolerance_bps",
2711
+ "type": "u16"
2708
2712
  }
2709
2713
  ]
2710
2714
  }
@@ -2705,6 +2705,10 @@
2705
2705
  "type": {
2706
2706
  "vec": "pubkey"
2707
2707
  }
2708
+ },
2709
+ {
2710
+ "name": "order_price_tolerance_bps",
2711
+ "type": "u16"
2708
2712
  }
2709
2713
  ]
2710
2714
  }
@@ -480,7 +480,7 @@
480
480
  {
481
481
  "code": 6002,
482
482
  "name": "InvalidParameters",
483
- "msg": "Invalid parameters"
483
+ "msg": "Invalid fee parameters or insufficient account space"
484
484
  },
485
485
  {
486
486
  "code": 6003,
@@ -490,7 +490,17 @@
490
490
  {
491
491
  "code": 6004,
492
492
  "name": "InvalidGlobalConfig",
493
- "msg": "Invalid global config"
493
+ "msg": "Invalid or corrupted global config account"
494
+ },
495
+ {
496
+ "code": 6005,
497
+ "name": "InvalidFeeAuthority",
498
+ "msg": "Invalid fee authority"
499
+ },
500
+ {
501
+ "code": 6006,
502
+ "name": "AssetMetaNotFound",
503
+ "msg": "Asset meta not found"
494
504
  }
495
505
  ],
496
506
  "types": [
@@ -4637,19 +4637,6 @@
4637
4637
  }
4638
4638
  ],
4639
4639
  "accounts": [
4640
- {
4641
- "name": "GlobalConfig",
4642
- "discriminator": [
4643
- 149,
4644
- 8,
4645
- 156,
4646
- 202,
4647
- 160,
4648
- 252,
4649
- 176,
4650
- 217
4651
- ]
4652
- },
4653
4640
  {
4654
4641
  "name": "RequestQueue",
4655
4642
  "discriminator": [
@@ -4724,7 +4711,7 @@
4724
4711
  {
4725
4712
  "code": 6003,
4726
4713
  "name": "InvalidAsset",
4727
- "msg": "Asset not allowed to subscribe"
4714
+ "msg": "Invalid asset"
4728
4715
  },
4729
4716
  {
4730
4717
  "code": 6004,
@@ -4775,6 +4762,21 @@
4775
4762
  "code": 6013,
4776
4763
  "name": "ProtocolFeesNotCrystallized",
4777
4764
  "msg": "Protocol fees should be crystallized before updating"
4765
+ },
4766
+ {
4767
+ "code": 6014,
4768
+ "name": "ManagerFeesNotCrystallized",
4769
+ "msg": "Manager fees should be crystallized before updating"
4770
+ },
4771
+ {
4772
+ "code": 6015,
4773
+ "name": "InsufficientEscrowBalance",
4774
+ "msg": "Insufficient escrow balance for fee burn"
4775
+ },
4776
+ {
4777
+ "code": 6016,
4778
+ "name": "AmountBelowMinimum",
4779
+ "msg": "Amount below minimum threshold"
4778
4780
  }
4779
4781
  ],
4780
4782
  "types": [
@@ -4838,51 +4840,6 @@
4838
4840
  ]
4839
4841
  }
4840
4842
  },
4841
- {
4842
- "name": "AssetMeta",
4843
- "type": {
4844
- "kind": "struct",
4845
- "fields": [
4846
- {
4847
- "name": "asset",
4848
- "type": "pubkey"
4849
- },
4850
- {
4851
- "name": "decimals",
4852
- "type": "u8"
4853
- },
4854
- {
4855
- "name": "oracle",
4856
- "type": "pubkey"
4857
- },
4858
- {
4859
- "name": "oracle_source",
4860
- "type": {
4861
- "defined": {
4862
- "name": "OracleSource"
4863
- }
4864
- }
4865
- },
4866
- {
4867
- "name": "max_age_seconds",
4868
- "type": "u16"
4869
- },
4870
- {
4871
- "name": "priority",
4872
- "type": "i8"
4873
- },
4874
- {
4875
- "name": "padding",
4876
- "type": {
4877
- "array": [
4878
- "u8",
4879
- 3
4880
- ]
4881
- }
4882
- }
4883
- ]
4884
- }
4885
- },
4886
4843
  {
4887
4844
  "name": "AumRecord",
4888
4845
  "type": {
@@ -5343,62 +5300,6 @@
5343
5300
  ]
5344
5301
  }
5345
5302
  },
5346
- {
5347
- "name": "GlobalConfig",
5348
- "type": {
5349
- "kind": "struct",
5350
- "fields": [
5351
- {
5352
- "name": "admin",
5353
- "docs": [
5354
- "The authority that can modify the config"
5355
- ],
5356
- "type": "pubkey"
5357
- },
5358
- {
5359
- "name": "fee_authority",
5360
- "docs": [
5361
- "The authority that can modify fee structure of individual glam state and claim protocol fees"
5362
- ],
5363
- "type": "pubkey"
5364
- },
5365
- {
5366
- "name": "referrer",
5367
- "docs": [
5368
- "Default GLAM referrer"
5369
- ],
5370
- "type": "pubkey"
5371
- },
5372
- {
5373
- "name": "base_fee_bps",
5374
- "docs": [
5375
- "Default protocol base fee applied to all vaults"
5376
- ],
5377
- "type": "u16"
5378
- },
5379
- {
5380
- "name": "flow_fee_bps",
5381
- "docs": [
5382
- "Default protocol flow fee applied to all vaults"
5383
- ],
5384
- "type": "u16"
5385
- },
5386
- {
5387
- "name": "asset_metas",
5388
- "docs": [
5389
- "List of assets and their oracle configs supported by the protocol"
5390
- ],
5391
- "type": {
5392
- "vec": {
5393
- "defined": {
5394
- "name": "AssetMeta"
5395
- }
5396
- }
5397
- }
5398
- }
5399
- ]
5400
- }
5401
- },
5402
5303
  {
5403
5304
  "name": "HurdleType",
5404
5305
  "type": {
@@ -5755,77 +5656,6 @@
5755
5656
  ]
5756
5657
  }
5757
5658
  },
5758
- {
5759
- "name": "OracleSource",
5760
- "type": {
5761
- "kind": "enum",
5762
- "variants": [
5763
- {
5764
- "name": "Pyth"
5765
- },
5766
- {
5767
- "name": "Switchboard"
5768
- },
5769
- {
5770
- "name": "QuoteAsset"
5771
- },
5772
- {
5773
- "name": "Pyth1K"
5774
- },
5775
- {
5776
- "name": "Pyth1M"
5777
- },
5778
- {
5779
- "name": "PythStableCoin"
5780
- },
5781
- {
5782
- "name": "Prelaunch"
5783
- },
5784
- {
5785
- "name": "PythPull"
5786
- },
5787
- {
5788
- "name": "Pyth1KPull"
5789
- },
5790
- {
5791
- "name": "Pyth1MPull"
5792
- },
5793
- {
5794
- "name": "PythStableCoinPull"
5795
- },
5796
- {
5797
- "name": "SwitchboardOnDemand"
5798
- },
5799
- {
5800
- "name": "PythLazer"
5801
- },
5802
- {
5803
- "name": "PythLazer1K"
5804
- },
5805
- {
5806
- "name": "PythLazer1M"
5807
- },
5808
- {
5809
- "name": "PythLazerStableCoin"
5810
- },
5811
- {
5812
- "name": "NotSet"
5813
- },
5814
- {
5815
- "name": "LstPoolState"
5816
- },
5817
- {
5818
- "name": "MarinadeState"
5819
- },
5820
- {
5821
- "name": "BaseAsset"
5822
- },
5823
- {
5824
- "name": "ChainlinkRWA"
5825
- }
5826
- ]
5827
- }
5828
- },
5829
5659
  {
5830
5660
  "name": "PendingRequest",
5831
5661
  "type": {