@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.
- package/.gitlab-ci.yml +41 -9
- package/.prettierignore +17 -0
- package/README.md +729 -59
- package/dist/config/defaultConfigs.d.ts +1 -1
- package/dist/config/defaultConfigs.js +2 -2
- package/dist/config/types.d.ts +3 -1
- package/dist/config/utils.d.ts +1 -1
- package/dist/config/utils.js +14 -5
- package/dist/errors/NosanaError.d.ts +1 -1
- package/dist/generated_clients/jobs/instructions/assign.d.ts +79 -0
- package/dist/generated_clients/jobs/instructions/assign.js +120 -0
- package/dist/generated_clients/jobs/instructions/extend.d.ts +9 -6
- package/dist/generated_clients/jobs/instructions/extend.js +4 -1
- package/dist/generated_clients/jobs/instructions/finish.d.ts +10 -10
- package/dist/generated_clients/jobs/instructions/finish.js +6 -6
- package/dist/generated_clients/jobs/instructions/index.d.ts +1 -0
- package/dist/generated_clients/jobs/instructions/index.js +1 -0
- package/dist/generated_clients/jobs/programs/nosanaJobs.d.ts +18 -15
- package/dist/generated_clients/jobs/programs/nosanaJobs.js +18 -14
- package/dist/generated_clients/staking/accounts/index.d.ts +9 -0
- package/dist/generated_clients/staking/accounts/index.js +9 -0
- package/dist/generated_clients/staking/accounts/settingsAccount.d.ts +29 -0
- package/dist/generated_clients/staking/accounts/settingsAccount.js +55 -0
- package/dist/generated_clients/staking/accounts/stakeAccount.d.ts +39 -0
- package/dist/generated_clients/staking/accounts/stakeAccount.js +65 -0
- package/dist/generated_clients/staking/errors/index.d.ts +8 -0
- package/dist/generated_clients/staking/errors/index.js +8 -0
- package/dist/generated_clients/staking/errors/nosanaStaking.d.ts +45 -0
- package/dist/generated_clients/staking/errors/nosanaStaking.js +62 -0
- package/dist/generated_clients/staking/index.d.ts +11 -0
- package/dist/generated_clients/staking/index.js +11 -0
- package/dist/generated_clients/staking/instructions/close.d.ts +48 -0
- package/dist/generated_clients/staking/instructions/close.js +81 -0
- package/dist/generated_clients/staking/instructions/extend.d.ts +43 -0
- package/dist/generated_clients/staking/instructions/extend.js +73 -0
- package/dist/generated_clients/staking/instructions/index.d.ts +17 -0
- package/dist/generated_clients/staking/instructions/index.js +17 -0
- package/dist/generated_clients/staking/instructions/init.d.ts +45 -0
- package/dist/generated_clients/staking/instructions/init.js +82 -0
- package/dist/generated_clients/staking/instructions/restake.d.ts +42 -0
- package/dist/generated_clients/staking/instructions/restake.js +70 -0
- package/dist/generated_clients/staking/instructions/slash.d.ts +55 -0
- package/dist/generated_clients/staking/instructions/slash.js +90 -0
- package/dist/generated_clients/staking/instructions/stake.d.ts +64 -0
- package/dist/generated_clients/staking/instructions/stake.js +106 -0
- package/dist/generated_clients/staking/instructions/topup.d.ts +52 -0
- package/dist/generated_clients/staking/instructions/topup.js +87 -0
- package/dist/generated_clients/staking/instructions/unstake.d.ts +42 -0
- package/dist/generated_clients/staking/instructions/unstake.js +70 -0
- package/dist/generated_clients/staking/instructions/updateSettings.d.ts +45 -0
- package/dist/generated_clients/staking/instructions/updateSettings.js +73 -0
- package/dist/generated_clients/staking/instructions/withdraw.d.ts +48 -0
- package/dist/generated_clients/staking/instructions/withdraw.js +81 -0
- package/dist/generated_clients/staking/programs/index.d.ts +8 -0
- package/dist/generated_clients/staking/programs/index.js +8 -0
- package/dist/generated_clients/staking/programs/nosanaStaking.d.ts +53 -0
- package/dist/generated_clients/staking/programs/nosanaStaking.js +71 -0
- package/dist/generated_clients/staking/shared/index.d.ts +49 -0
- package/dist/generated_clients/staking/shared/index.js +86 -0
- package/dist/index.d.ts +14 -4
- package/dist/index.js +20 -136
- package/dist/ipfs/IPFS.d.ts +33 -2
- package/dist/ipfs/IPFS.js +110 -5
- package/dist/programs/BaseProgram.d.ts +1 -1
- package/dist/programs/BaseProgram.js +3 -7
- package/dist/programs/JobsProgram.d.ts +37 -37
- package/dist/programs/JobsProgram.js +63 -67
- package/dist/programs/StakeProgram.d.ts +29 -0
- package/dist/programs/StakeProgram.js +91 -0
- package/dist/services/NosService.d.ts +48 -0
- package/dist/services/NosService.js +134 -0
- package/dist/{solana/SolanaUtils.d.ts → services/SolanaService.d.ts} +5 -5
- package/dist/{solana/SolanaUtils.js → services/SolanaService.js} +17 -7
- package/dist/utils/index.d.ts +2 -1
- package/dist/utils/index.js +2 -0
- package/dist/utils/walletConverter.d.ts +9 -0
- package/dist/utils/walletConverter.js +141 -0
- package/eslint.config.js +49 -0
- package/examples/node/README.md +115 -0
- package/examples/node/nos-service.ts +117 -0
- package/examples/node/package.json +3 -1
- package/examples/node/retrieve.ts +2 -2
- package/examples/node/stake-program.ts +84 -0
- package/package.json +13 -8
- package/scripts/generate-clients.ts +20 -7
- 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 {
|
|
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
|
|
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
|
|
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 {
|
|
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 {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import
|
|
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
|
|
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
|
-
|
|
24
|
-
|
|
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
|
|
157
|
-
export
|
|
158
|
-
// Export
|
|
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';
|
package/dist/ipfs/IPFS.d.ts
CHANGED
|
@@ -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):
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
|
73
|
+
return [...bs58.decode(hash).subarray(2)];
|
|
39
74
|
}
|
|
40
75
|
else {
|
|
41
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
41
|
-
|
|
40
|
+
* Fetch a run account by address
|
|
41
|
+
*/
|
|
42
42
|
market(addr: Address): Promise<Market>;
|
|
43
43
|
/**
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
* Fetch multiple job accounts by address
|
|
45
|
+
*/
|
|
46
46
|
multiple(addresses: Address[], checkRuns?: boolean): Promise<Job[]>;
|
|
47
47
|
/**
|
|
48
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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;
|