@nosana/kit 0.1.6 → 1.0.2

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 (86) hide show
  1. package/.gitlab-ci.yml +41 -9
  2. package/.prettierignore +17 -0
  3. package/README.md +729 -59
  4. package/dist/config/defaultConfigs.d.ts +1 -1
  5. package/dist/config/defaultConfigs.js +2 -2
  6. package/dist/config/types.d.ts +3 -1
  7. package/dist/config/utils.d.ts +1 -1
  8. package/dist/config/utils.js +14 -5
  9. package/dist/errors/NosanaError.d.ts +1 -1
  10. package/dist/generated_clients/jobs/instructions/assign.d.ts +79 -0
  11. package/dist/generated_clients/jobs/instructions/assign.js +120 -0
  12. package/dist/generated_clients/jobs/instructions/extend.d.ts +9 -6
  13. package/dist/generated_clients/jobs/instructions/extend.js +4 -1
  14. package/dist/generated_clients/jobs/instructions/finish.d.ts +10 -10
  15. package/dist/generated_clients/jobs/instructions/finish.js +6 -6
  16. package/dist/generated_clients/jobs/instructions/index.d.ts +1 -0
  17. package/dist/generated_clients/jobs/instructions/index.js +1 -0
  18. package/dist/generated_clients/jobs/programs/nosanaJobs.d.ts +18 -15
  19. package/dist/generated_clients/jobs/programs/nosanaJobs.js +18 -14
  20. package/dist/generated_clients/staking/accounts/index.d.ts +9 -0
  21. package/dist/generated_clients/staking/accounts/index.js +9 -0
  22. package/dist/generated_clients/staking/accounts/settingsAccount.d.ts +29 -0
  23. package/dist/generated_clients/staking/accounts/settingsAccount.js +55 -0
  24. package/dist/generated_clients/staking/accounts/stakeAccount.d.ts +39 -0
  25. package/dist/generated_clients/staking/accounts/stakeAccount.js +65 -0
  26. package/dist/generated_clients/staking/errors/index.d.ts +8 -0
  27. package/dist/generated_clients/staking/errors/index.js +8 -0
  28. package/dist/generated_clients/staking/errors/nosanaStaking.d.ts +45 -0
  29. package/dist/generated_clients/staking/errors/nosanaStaking.js +62 -0
  30. package/dist/generated_clients/staking/index.d.ts +11 -0
  31. package/dist/generated_clients/staking/index.js +11 -0
  32. package/dist/generated_clients/staking/instructions/close.d.ts +48 -0
  33. package/dist/generated_clients/staking/instructions/close.js +81 -0
  34. package/dist/generated_clients/staking/instructions/extend.d.ts +43 -0
  35. package/dist/generated_clients/staking/instructions/extend.js +73 -0
  36. package/dist/generated_clients/staking/instructions/index.d.ts +17 -0
  37. package/dist/generated_clients/staking/instructions/index.js +17 -0
  38. package/dist/generated_clients/staking/instructions/init.d.ts +45 -0
  39. package/dist/generated_clients/staking/instructions/init.js +82 -0
  40. package/dist/generated_clients/staking/instructions/restake.d.ts +42 -0
  41. package/dist/generated_clients/staking/instructions/restake.js +70 -0
  42. package/dist/generated_clients/staking/instructions/slash.d.ts +55 -0
  43. package/dist/generated_clients/staking/instructions/slash.js +90 -0
  44. package/dist/generated_clients/staking/instructions/stake.d.ts +64 -0
  45. package/dist/generated_clients/staking/instructions/stake.js +106 -0
  46. package/dist/generated_clients/staking/instructions/topup.d.ts +52 -0
  47. package/dist/generated_clients/staking/instructions/topup.js +87 -0
  48. package/dist/generated_clients/staking/instructions/unstake.d.ts +42 -0
  49. package/dist/generated_clients/staking/instructions/unstake.js +70 -0
  50. package/dist/generated_clients/staking/instructions/updateSettings.d.ts +45 -0
  51. package/dist/generated_clients/staking/instructions/updateSettings.js +73 -0
  52. package/dist/generated_clients/staking/instructions/withdraw.d.ts +48 -0
  53. package/dist/generated_clients/staking/instructions/withdraw.js +81 -0
  54. package/dist/generated_clients/staking/programs/index.d.ts +8 -0
  55. package/dist/generated_clients/staking/programs/index.js +8 -0
  56. package/dist/generated_clients/staking/programs/nosanaStaking.d.ts +53 -0
  57. package/dist/generated_clients/staking/programs/nosanaStaking.js +71 -0
  58. package/dist/generated_clients/staking/shared/index.d.ts +49 -0
  59. package/dist/generated_clients/staking/shared/index.js +86 -0
  60. package/dist/index.d.ts +14 -4
  61. package/dist/index.js +20 -136
  62. package/dist/ipfs/IPFS.d.ts +33 -2
  63. package/dist/ipfs/IPFS.js +110 -5
  64. package/dist/programs/BaseProgram.d.ts +1 -1
  65. package/dist/programs/BaseProgram.js +3 -7
  66. package/dist/programs/JobsProgram.d.ts +37 -37
  67. package/dist/programs/JobsProgram.js +63 -67
  68. package/dist/programs/StakeProgram.d.ts +29 -0
  69. package/dist/programs/StakeProgram.js +91 -0
  70. package/dist/services/NosService.d.ts +48 -0
  71. package/dist/services/NosService.js +134 -0
  72. package/dist/{solana/SolanaUtils.d.ts → services/SolanaService.d.ts} +5 -5
  73. package/dist/{solana/SolanaUtils.js → services/SolanaService.js} +17 -7
  74. package/dist/utils/index.d.ts +2 -1
  75. package/dist/utils/index.js +2 -0
  76. package/dist/utils/walletConverter.d.ts +9 -0
  77. package/dist/utils/walletConverter.js +141 -0
  78. package/eslint.config.js +49 -0
  79. package/examples/node/README.md +115 -0
  80. package/examples/node/nos-service.ts +117 -0
  81. package/examples/node/package.json +3 -1
  82. package/examples/node/retrieve.ts +2 -2
  83. package/examples/node/stake-program.ts +84 -0
  84. package/package.json +13 -8
  85. package/scripts/generate-clients.ts +20 -7
  86. package/vitest.config.ts +31 -0
@@ -0,0 +1,86 @@
1
+ /**
2
+ * This code was AUTOGENERATED using the codama library.
3
+ * Please DO NOT EDIT THIS FILE, instead use visitors
4
+ * to add features, then rerun codama to update it.
5
+ *
6
+ * @see https://github.com/codama-idl/codama
7
+ */
8
+ import { AccountRole, isProgramDerivedAddress, isTransactionSigner as kitIsTransactionSigner, upgradeRoleToSigner, } from '@solana/kit';
9
+ /**
10
+ * Asserts that the given value is not null or undefined.
11
+ * @internal
12
+ */
13
+ export function expectSome(value) {
14
+ if (value == null) {
15
+ throw new Error('Expected a value but received null or undefined.');
16
+ }
17
+ return value;
18
+ }
19
+ /**
20
+ * Asserts that the given value is a PublicKey.
21
+ * @internal
22
+ */
23
+ export function expectAddress(value) {
24
+ if (!value) {
25
+ throw new Error('Expected a Address.');
26
+ }
27
+ if (typeof value === 'object' && 'address' in value) {
28
+ return value.address;
29
+ }
30
+ if (Array.isArray(value)) {
31
+ return value[0];
32
+ }
33
+ return value;
34
+ }
35
+ /**
36
+ * Asserts that the given value is a PDA.
37
+ * @internal
38
+ */
39
+ export function expectProgramDerivedAddress(value) {
40
+ if (!value || !Array.isArray(value) || !isProgramDerivedAddress(value)) {
41
+ throw new Error('Expected a ProgramDerivedAddress.');
42
+ }
43
+ return value;
44
+ }
45
+ /**
46
+ * Asserts that the given value is a TransactionSigner.
47
+ * @internal
48
+ */
49
+ export function expectTransactionSigner(value) {
50
+ if (!value || !isTransactionSigner(value)) {
51
+ throw new Error('Expected a TransactionSigner.');
52
+ }
53
+ return value;
54
+ }
55
+ /**
56
+ * Get account metas and signers from resolved accounts.
57
+ * @internal
58
+ */
59
+ export function getAccountMetaFactory(programAddress, optionalAccountStrategy) {
60
+ return (account) => {
61
+ if (!account.value) {
62
+ if (optionalAccountStrategy === 'omitted')
63
+ return;
64
+ return Object.freeze({
65
+ address: programAddress,
66
+ role: AccountRole.READONLY,
67
+ });
68
+ }
69
+ const writableRole = account.isWritable
70
+ ? AccountRole.WRITABLE
71
+ : AccountRole.READONLY;
72
+ return Object.freeze({
73
+ address: expectAddress(account.value),
74
+ role: isTransactionSigner(account.value)
75
+ ? upgradeRoleToSigner(writableRole)
76
+ : writableRole,
77
+ ...(isTransactionSigner(account.value) ? { signer: account.value } : {}),
78
+ });
79
+ };
80
+ }
81
+ export function isTransactionSigner(value) {
82
+ return (!!value &&
83
+ typeof value === 'object' &&
84
+ 'address' in value &&
85
+ kitIsTransactionSigner(value));
86
+ }
package/dist/index.d.ts CHANGED
@@ -1,23 +1,33 @@
1
1
  import { ClientConfig, NosanaNetwork, PartialClientConfig, WalletConfig } from './config/index.js';
2
2
  import { Logger } from './logger/Logger.js';
3
3
  import { JobsProgram } from './programs/JobsProgram.js';
4
- import { SolanaUtils } from './solana/SolanaUtils.js';
4
+ import { StakeProgram } from './programs/StakeProgram.js';
5
+ import { SolanaService } from './services/SolanaService.js';
6
+ import { NosService } from './services/NosService.js';
7
+ import { IPFS } from './ipfs/IPFS.js';
5
8
  import { KeyPairSigner } from 'gill';
6
9
  export declare class NosanaClient {
7
10
  readonly config: ClientConfig;
8
11
  readonly jobs: JobsProgram;
9
- readonly solana: SolanaUtils;
12
+ readonly stake: StakeProgram;
13
+ readonly solana: SolanaService;
14
+ readonly nos: NosService;
15
+ readonly ipfs: IPFS;
10
16
  readonly logger: Logger;
11
17
  wallet: KeyPairSigner | undefined;
12
18
  constructor(network?: NosanaNetwork, customConfig?: PartialClientConfig);
13
19
  setWallet(wallet: WalletConfig): Promise<KeyPairSigner | undefined>;
14
- private isValidFilePath;
15
20
  }
16
21
  export * from './config/index.js';
17
22
  export * from './errors/NosanaError.js';
18
23
  export * from './logger/Logger.js';
19
24
  export { JobsProgram, JobState, MarketQueueType } from './programs/JobsProgram.js';
20
25
  export type { Job, Market, Run } from './programs/JobsProgram.js';
26
+ export { StakeProgram } from './programs/StakeProgram.js';
27
+ export type { Stake } from './programs/StakeProgram.js';
21
28
  export * from './ipfs/IPFS.js';
22
- export * from './generated_clients/jobs/index.js';
29
+ export { NosService } from './services/NosService.js';
30
+ export type { TokenAccount, TokenAccountWithBalance } from './services/NosService.js';
31
+ export * as JobsClient from './generated_clients/jobs/index.js';
32
+ export * as StakingClient from './generated_clients/staking/index.js';
23
33
  export * from 'gill';
package/dist/index.js CHANGED
@@ -1,14 +1,11 @@
1
- import { Buffer } from 'buffer';
2
- if (typeof window !== 'undefined' && typeof window.Buffer === 'undefined') {
3
- window.Buffer = Buffer;
4
- }
5
- import { getNosanaConfig, NosanaNetwork } from './config/index.js';
1
+ import { getNosanaConfig, NosanaNetwork, } from './config/index.js';
6
2
  import { Logger } from './logger/Logger.js';
7
3
  import { JobsProgram } from './programs/JobsProgram.js';
8
- import { SolanaUtils } from './solana/SolanaUtils.js';
9
- import { createKeyPairSignerFromBytes } from 'gill';
10
- import { NosanaError, ErrorCodes } from './errors/NosanaError.js';
11
- import bs58 from 'bs58';
4
+ import { StakeProgram } from './programs/StakeProgram.js';
5
+ import { SolanaService } from './services/SolanaService.js';
6
+ import { NosService } from './services/NosService.js';
7
+ import { IPFS } from './ipfs/IPFS.js';
8
+ import { convertWalletConfigToKeyPairSigner } from './utils/walletConverter.js';
12
9
  export class NosanaClient {
13
10
  constructor(network = NosanaNetwork.MAINNET, customConfig) {
14
11
  this.config = getNosanaConfig(network, customConfig);
@@ -16,133 +13,15 @@ export class NosanaClient {
16
13
  this.setWallet(this.config.wallet);
17
14
  }
18
15
  this.jobs = new JobsProgram(this);
16
+ this.stake = new StakeProgram(this);
19
17
  this.logger = Logger.getInstance();
20
- this.solana = new SolanaUtils(this);
18
+ this.solana = new SolanaService(this);
19
+ this.nos = new NosService(this);
20
+ this.ipfs = new IPFS(this.config.ipfs);
21
21
  }
22
22
  async setWallet(wallet) {
23
- try {
24
- // Check if we already have a KeyPairSigner type
25
- if (wallet && typeof wallet === 'object' && 'address' in wallet && 'signMessages' in wallet) {
26
- this.wallet = wallet;
27
- return this.wallet;
28
- }
29
- // Check if it's a browser wallet adapter (has publicKey and signTransaction/signMessage)
30
- if (wallet && typeof wallet === 'object' && 'publicKey' in wallet && ('signTransaction' in wallet || 'signMessage' in wallet)) {
31
- // Convert browser wallet adapter to KeyPairSigner-like interface
32
- const browserWallet = wallet;
33
- this.wallet = {
34
- address: browserWallet.publicKey.toString(),
35
- signMessages: async (messages) => {
36
- if (browserWallet.signMessage) {
37
- return Promise.all(messages.map(msg => browserWallet.signMessage(msg)));
38
- }
39
- throw new Error('Browser wallet does not support message signing');
40
- },
41
- signTransactions: async (transactions) => {
42
- if (browserWallet.signTransaction) {
43
- return Promise.all(transactions.map(tx => browserWallet.signTransaction(tx)));
44
- }
45
- throw new Error('Browser wallet does not support transaction signing');
46
- }
47
- };
48
- return this.wallet;
49
- }
50
- // If it's a string, try multiple conversion methods
51
- if (typeof wallet === 'string') {
52
- // Only try file/environment loading in Node.js environment
53
- if (typeof window === 'undefined') {
54
- try {
55
- // Use string concatenation to avoid bundler resolving this import at build time
56
- const nodeModule = 'gill' + '/node';
57
- const { loadKeypairSignerFromFile, loadKeypairSignerFromEnvironment, loadKeypairSignerFromEnvironmentBase58 } = await import(nodeModule);
58
- // Try to load from file path
59
- if (await this.isValidFilePath(wallet)) {
60
- try {
61
- this.wallet = await loadKeypairSignerFromFile(wallet);
62
- return this.wallet;
63
- }
64
- catch (error) {
65
- this.logger.debug(`Failed to load keypair from file: ${error}`);
66
- }
67
- }
68
- // Try to load from environment variable
69
- try {
70
- this.wallet = await loadKeypairSignerFromEnvironment(wallet);
71
- return this.wallet;
72
- }
73
- catch (error) {
74
- this.logger.debug(`Failed to load keypair from environment: ${error}`);
75
- }
76
- // Try to load from environment variable as base58
77
- try {
78
- this.wallet = await loadKeypairSignerFromEnvironmentBase58(wallet);
79
- return this.wallet;
80
- }
81
- catch (error) {
82
- this.logger.debug(`Failed to load keypair from environment base58: ${error}`);
83
- }
84
- }
85
- catch (error) {
86
- this.logger.debug(`Node.js modules not available: ${error}`);
87
- }
88
- }
89
- // Try to parse as JSON array
90
- if (wallet.startsWith('[')) {
91
- try {
92
- const key = JSON.parse(wallet);
93
- this.wallet = await createKeyPairSignerFromBytes(new Uint8Array(key));
94
- return this.wallet;
95
- }
96
- catch (error) {
97
- this.logger.debug(`Failed to parse as JSON array: ${error}`);
98
- }
99
- }
100
- // Try to decode as base58
101
- try {
102
- const key = Buffer.from(bs58.decode(wallet)).toJSON().data;
103
- this.wallet = await createKeyPairSignerFromBytes(new Uint8Array(key));
104
- return this.wallet;
105
- }
106
- catch (error) {
107
- this.logger.debug(`Failed to decode as base58: ${error}`);
108
- }
109
- }
110
- // If it's an array, try to create from bytes
111
- if (Array.isArray(wallet)) {
112
- try {
113
- this.wallet = await createKeyPairSignerFromBytes(new Uint8Array(wallet));
114
- return this.wallet;
115
- }
116
- catch (error) {
117
- this.logger.debug(`Failed to create from byte array: ${error}`);
118
- }
119
- }
120
- // If we get here, none of the conversion methods worked
121
- throw new Error('Unable to convert wallet to KeyPairSigner using any available method');
122
- }
123
- catch (error) {
124
- throw new NosanaError(`Failed to convert wallet to KeyPairSigner: ${error instanceof Error ? error.message : 'Unknown error'}`, ErrorCodes.WALLET_CONVERSION_ERROR, error);
125
- }
126
- }
127
- async isValidFilePath(filePath) {
128
- // Only validate file paths in Node.js environment
129
- if (typeof window !== 'undefined') {
130
- return false; // Browser environment, no file system access
131
- }
132
- try {
133
- const [fs, path] = await Promise.all([
134
- import('fs'),
135
- import('path')
136
- ]);
137
- if (!path.isAbsolute(filePath) && !filePath.startsWith('./') && !filePath.startsWith('../')) {
138
- return false;
139
- }
140
- const stats = await fs.promises.stat(filePath);
141
- return stats.isFile();
142
- }
143
- catch {
144
- return false;
145
- }
23
+ this.wallet = await convertWalletConfigToKeyPairSigner(wallet);
24
+ return this.wallet;
146
25
  }
147
26
  }
148
27
  // Export types and configuration
@@ -151,9 +30,14 @@ export * from './errors/NosanaError.js';
151
30
  export * from './logger/Logger.js';
152
31
  // Export JobsProgram and related types
153
32
  export { JobsProgram, JobState, MarketQueueType } from './programs/JobsProgram.js';
33
+ // Export StakeProgram and related types
34
+ export { StakeProgram } from './programs/StakeProgram.js';
154
35
  // Export IPFS utilities
155
36
  export * from './ipfs/IPFS.js';
156
- // Export all generated client types and functions
157
- export * from './generated_clients/jobs/index.js';
158
- // Export dependencies
37
+ // Export NOS token service
38
+ export { NosService } from './services/NosService.js';
39
+ // Export generated clients under namespaces to avoid naming conflicts
40
+ export * as JobsClient from './generated_clients/jobs/index.js';
41
+ export * as StakingClient from './generated_clients/staking/index.js';
42
+ // Export dependencies
159
43
  export * from 'gill';
@@ -1,19 +1,50 @@
1
+ import { AxiosRequestConfig } from 'axios';
1
2
  import { ReadonlyUint8Array } from 'gill';
3
+ import type { IpfsConfig } from '../config/types.js';
2
4
  /**
3
5
  * Class to interact with Pinata Cloud
4
6
  * https://www.pinata.cloud/
5
7
  */
6
8
  export declare class IPFS {
9
+ private api;
10
+ config: IpfsConfig;
11
+ constructor(config: IpfsConfig);
7
12
  /**
8
13
  * Convert the ipfs bytes from a solana job to a CID
9
14
  * It prepends the 0x1220 (18,32) to make it 34 bytes and Base58 encodes it.
10
15
  * This result is IPFS addressable.
11
16
  */
12
- static solHashToIpfsHash(hashArray: ReadonlyUint8Array): string | null;
17
+ static solHashToIpfsHash(hashArray: ReadonlyUint8Array | Array<number>): string | null;
13
18
  /**
14
19
  * Converts IPFS hash to byte array needed to submit results
15
20
  * @param hash IPFS hash
16
21
  * @returns Array<number>
17
22
  */
18
- static IpfsHashToByteArray(hash: string): Uint8Array;
23
+ static IpfsHashToByteArray(hash: string): Array<number>;
24
+ /**
25
+ * Retrieve data from IPFS using the configured gateway
26
+ * @param hash IPFS hash string or byte array
27
+ * @param options Additional axios request options
28
+ * @returns The retrieved data
29
+ */
30
+ retrieve(hash: string | Array<number>, options?: AxiosRequestConfig): Promise<any>;
31
+ /**
32
+ * Function to pin data into Pinata Cloud
33
+ * @param data Object to pin into IPFS as JSON
34
+ * @returns The IPFS hash of the pinned data
35
+ */
36
+ pin(data: object): Promise<string>;
37
+ /**
38
+ * Function to pin a file into Pinata Cloud
39
+ * @param filePath Path to the file to pin
40
+ * @returns The IPFS hash of the pinned file
41
+ */
42
+ pinFile(filePath: string): Promise<string>;
43
+ /**
44
+ * Function to pin a file from buffer/blob into Pinata Cloud
45
+ * @param fileBuffer Buffer or Blob containing the file data
46
+ * @param fileName Name of the file
47
+ * @returns The IPFS hash of the pinned file
48
+ */
49
+ pinFileFromBuffer(fileBuffer: Buffer | Blob, fileName: string): Promise<string>;
19
50
  }
package/dist/ipfs/IPFS.js CHANGED
@@ -1,9 +1,43 @@
1
1
  import bs58 from 'bs58';
2
+ import axios, { AxiosHeaders } from 'axios';
3
+ // Import form-data dynamically for Node.js environments
4
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5
+ let FormData;
6
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
7
+ let fs;
8
+ // Dynamically import Node.js-specific modules
9
+ const loadNodeModules = async () => {
10
+ if (typeof window === 'undefined') {
11
+ try {
12
+ const formDataModule = await import('form-data');
13
+ FormData = formDataModule.default;
14
+ fs = await import('fs');
15
+ }
16
+ catch (error) {
17
+ console.warn('Node.js modules not available for file operations');
18
+ }
19
+ }
20
+ else {
21
+ // Use browser FormData
22
+ FormData = window.FormData;
23
+ }
24
+ };
2
25
  /**
3
26
  * Class to interact with Pinata Cloud
4
27
  * https://www.pinata.cloud/
5
28
  */
6
29
  export class IPFS {
30
+ constructor(config) {
31
+ this.config = config;
32
+ const headers = new AxiosHeaders();
33
+ if (this.config.jwt) {
34
+ headers.set('Authorization', `Bearer ${this.config.jwt}`);
35
+ }
36
+ this.api = axios.create({
37
+ baseURL: this.config.api,
38
+ headers,
39
+ });
40
+ }
7
41
  /**
8
42
  * Convert the ipfs bytes from a solana job to a CID
9
43
  * It prepends the 0x1220 (18,32) to make it 34 bytes and Base58 encodes it.
@@ -11,16 +45,17 @@ export class IPFS {
11
45
  */
12
46
  static solHashToIpfsHash(hashArray) {
13
47
  let finalArray;
14
- if (hashArray.length === 32) {
48
+ const inputArray = Array.isArray(hashArray) ? hashArray : Array.from(hashArray);
49
+ if (inputArray.length === 32) {
15
50
  // Create a new array with the prepended bytes [18, 32] + original array
16
51
  finalArray = new Uint8Array(34);
17
52
  finalArray[0] = 18;
18
53
  finalArray[1] = 32;
19
- finalArray.set(hashArray, 2);
54
+ finalArray.set(inputArray, 2);
20
55
  }
21
56
  else {
22
57
  // Use the array as-is if it's not 32 bytes
23
- finalArray = new Uint8Array(hashArray);
58
+ finalArray = new Uint8Array(inputArray);
24
59
  }
25
60
  const hash = bs58.encode(Buffer.from(finalArray));
26
61
  if (hash === 'QmNLei78zWmzUdbeRB3CiUfAizWUrbeeZh5K1rhAQKCh51') {
@@ -35,10 +70,80 @@ export class IPFS {
35
70
  */
36
71
  static IpfsHashToByteArray(hash) {
37
72
  if (hash.length === 34) {
38
- return new Uint8Array([...bs58.decode(hash).subarray(2)]);
73
+ return [...bs58.decode(hash).subarray(2)];
39
74
  }
40
75
  else {
41
- return new Uint8Array([...bs58.decode(hash)]);
76
+ return [...bs58.decode(hash)];
77
+ }
78
+ }
79
+ /**
80
+ * Retrieve data from IPFS using the configured gateway
81
+ * @param hash IPFS hash string or byte array
82
+ * @param options Additional axios request options
83
+ * @returns The retrieved data
84
+ */
85
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
86
+ async retrieve(hash, options = {}) {
87
+ if (typeof hash !== 'string') {
88
+ const convertedHash = IPFS.solHashToIpfsHash(hash);
89
+ if (!convertedHash) {
90
+ throw new Error('Invalid hash provided');
91
+ }
92
+ hash = convertedHash;
93
+ }
94
+ const response = await axios.get(this.config.gateway + hash, options);
95
+ return response.data;
96
+ }
97
+ /**
98
+ * Function to pin data into Pinata Cloud
99
+ * @param data Object to pin into IPFS as JSON
100
+ * @returns The IPFS hash of the pinned data
101
+ */
102
+ async pin(data) {
103
+ const response = await this.api.post('/pinning/pinJSONToIPFS', data);
104
+ return response.data.IpfsHash;
105
+ }
106
+ /**
107
+ * Function to pin a file into Pinata Cloud
108
+ * @param filePath Path to the file to pin
109
+ * @returns The IPFS hash of the pinned file
110
+ */
111
+ async pinFile(filePath) {
112
+ // Ensure Node.js modules are loaded
113
+ await loadNodeModules();
114
+ if (!FormData || !fs) {
115
+ throw new Error('File operations are not supported in this environment');
116
+ }
117
+ const data = new FormData();
118
+ data.append('file', fs.createReadStream(filePath));
119
+ const response = await this.api.post('/pinning/pinFileToIPFS', data, {
120
+ headers: {
121
+ 'Content-Type': `multipart/form-data; boundary=${data.getBoundary()}`,
122
+ Authorization: `Bearer ${this.config.jwt}`,
123
+ },
124
+ });
125
+ return response.data.IpfsHash;
126
+ }
127
+ /**
128
+ * Function to pin a file from buffer/blob into Pinata Cloud
129
+ * @param fileBuffer Buffer or Blob containing the file data
130
+ * @param fileName Name of the file
131
+ * @returns The IPFS hash of the pinned file
132
+ */
133
+ async pinFileFromBuffer(fileBuffer, fileName) {
134
+ // Ensure FormData is available
135
+ await loadNodeModules();
136
+ if (!FormData) {
137
+ throw new Error('FormData is not available in this environment');
42
138
  }
139
+ const data = new FormData();
140
+ data.append('file', fileBuffer, fileName);
141
+ const response = await this.api.post('/pinning/pinFileToIPFS', data, {
142
+ headers: {
143
+ 'Content-Type': `multipart/form-data${data.getBoundary ? `; boundary=${data.getBoundary()}` : ''}`,
144
+ Authorization: `Bearer ${this.config.jwt}`,
145
+ },
146
+ });
147
+ return response.data.IpfsHash;
43
148
  }
44
149
  }
@@ -12,7 +12,7 @@ export declare abstract class BaseProgram {
12
12
  /**
13
13
  * Gets the static accounts, initializing them if needed.
14
14
  */
15
- protected getStaticAccounts(): Promise<staticAccounts>;
15
+ getStaticAccounts(): Promise<staticAccounts>;
16
16
  private initializeStaticAccounts;
17
17
  protected readonly sdk: NosanaClient;
18
18
  constructor(sdk: NosanaClient);
@@ -19,14 +19,10 @@ export class BaseProgram {
19
19
  }
20
20
  async initializeStaticAccounts() {
21
21
  return {
22
- rewardsReflection: await this.sdk.solana.pda([
23
- 'reflection',
24
- ], this.sdk.config.programs.rewardsAddress),
25
- rewardsVault: await this.sdk.solana.pda([
26
- this.sdk.config.programs.nosTokenAddress,
27
- ], this.sdk.config.programs.rewardsAddress),
22
+ rewardsReflection: await this.sdk.solana.pda(['reflection'], this.sdk.config.programs.rewardsAddress),
23
+ rewardsVault: await this.sdk.solana.pda([this.sdk.config.programs.nosTokenAddress], this.sdk.config.programs.rewardsAddress),
28
24
  rewardsProgram: this.sdk.config.programs.rewardsAddress,
29
- jobsProgram: this.sdk.config.programs.jobsAddress
25
+ jobsProgram: this.sdk.config.programs.jobsAddress,
30
26
  };
31
27
  }
32
28
  constructor(sdk) {
@@ -1,7 +1,7 @@
1
1
  import { BaseProgram } from './BaseProgram.js';
2
2
  import { Address, Account } from 'gill';
3
3
  import { NosanaClient } from '../index.js';
4
- import * as programClient from "../generated_clients/jobs/index.js";
4
+ import * as programClient from '../generated_clients/jobs/index.js';
5
5
  import { ConvertTypesForDb } from '../utils/index.js';
6
6
  export declare enum JobState {
7
7
  QUEUED = 0,
@@ -37,16 +37,16 @@ export declare class JobsProgram extends BaseProgram {
37
37
  */
38
38
  run(addr: Address): Promise<Run>;
39
39
  /**
40
- * Fetch a run account by address
41
- */
40
+ * Fetch a run account by address
41
+ */
42
42
  market(addr: Address): Promise<Market>;
43
43
  /**
44
- * Fetch multiple job accounts by address
45
- */
44
+ * Fetch multiple job accounts by address
45
+ */
46
46
  multiple(addresses: Address[], checkRuns?: boolean): Promise<Job[]>;
47
47
  /**
48
- * Fetch all job accounts
49
- */
48
+ * Fetch all job accounts
49
+ */
50
50
  all(filters?: {
51
51
  state?: JobState;
52
52
  market?: Address;
@@ -54,15 +54,15 @@ export declare class JobsProgram extends BaseProgram {
54
54
  project?: Address;
55
55
  }, checkRuns?: boolean): Promise<Job[]>;
56
56
  /**
57
- * Fetch all run accounts
58
- */
57
+ * Fetch all run accounts
58
+ */
59
59
  runs(filters?: {
60
60
  node?: Address;
61
61
  job?: Address;
62
62
  }): Promise<Run[]>;
63
63
  /**
64
- * Fetch all market accounts
65
- */
64
+ * Fetch all market accounts
65
+ */
66
66
  markets(): Promise<Market[]>;
67
67
  /**
68
68
  * Post a new job to the marketplace
@@ -76,32 +76,32 @@ export declare class JobsProgram extends BaseProgram {
76
76
  node?: Address;
77
77
  }): Promise<ReturnType<typeof this.client.getListInstruction>>;
78
78
  /**
79
- * Monitor program account updates using callback functions
80
- * Uses WebSocket subscriptions with automatic restart on failure
81
- *
82
- * @example
83
- * ```typescript
84
- * // Example: Monitor job accounts and save to file
85
- * const stopMonitoring = await jobsProgram.monitor({
86
- * onJobAccount: async (jobAccount) => {
87
- * console.log('Job updated:', jobAccount.address.toString());
88
- * // Save to database, file, or process as needed
89
- * },
90
- * onRunAccount: async (runAccount) => {
91
- * console.log('Run updated:', runAccount.address.toString());
92
- * },
93
- * onError: async (error, accountType) => {
94
- * console.error('Error processing account:', error, accountType);
95
- * }
96
- * });
97
- *
98
- * // Stop monitoring when done
99
- * stopMonitoring();
100
- * ```
101
- *
102
- * @param options Configuration options for monitoring
103
- * @returns A function to stop monitoring
104
- */
79
+ * Monitor program account updates using callback functions
80
+ * Uses WebSocket subscriptions with automatic restart on failure
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * // Example: Monitor job accounts and save to file
85
+ * const stopMonitoring = await jobsProgram.monitor({
86
+ * onJobAccount: async (jobAccount) => {
87
+ * console.log('Job updated:', jobAccount.address.toString());
88
+ * // Save to database, file, or process as needed
89
+ * },
90
+ * onRunAccount: async (runAccount) => {
91
+ * console.log('Run updated:', runAccount.address.toString());
92
+ * },
93
+ * onError: async (error, accountType) => {
94
+ * console.error('Error processing account:', error, accountType);
95
+ * }
96
+ * });
97
+ *
98
+ * // Stop monitoring when done
99
+ * stopMonitoring();
100
+ * ```
101
+ *
102
+ * @param options Configuration options for monitoring
103
+ * @returns A function to stop monitoring
104
+ */
105
105
  monitor(options?: {
106
106
  onJobAccount?: (jobAccount: Job) => Promise<void> | void;
107
107
  onMarketAccount?: (marketAccount: Market) => Promise<void> | void;