@toju.network/sol 0.1.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.
package/README.md ADDED
@@ -0,0 +1,232 @@
1
+ ## Storacha SOL
2
+
3
+ Crypto-native payments for storage on Storacha with SOL. No credit card needed.
4
+
5
+ Here are a couple of things you can do with this package:
6
+
7
+ - Estimate upload fees based on the file size and duration
8
+ - Make SOL payments for Storage on Storacha
9
+ - Multiple file (directory) uploads
10
+ - Show how much time is left to expiry
11
+ - View your upload history (all files you've stored with their details)
12
+ - Get expiration warnings via email before your files expire
13
+ - Automatic deletion of expired files from Storacha
14
+ - Renew/extend storage duration for existing uploads
15
+
16
+ Stuffs we hope to cover or extend:
17
+
18
+ - Allow payments from other chains. (Filecoin next)
19
+ - Include implementations for other libs. Right now, we only have React. Hoping to cover, Vue, Svelte etc.
20
+
21
+ ## Usage
22
+
23
+ First, install the package with your preferred package manager
24
+
25
+ ```shell
26
+ pnpm add storacha-sol
27
+ ```
28
+
29
+ The package exposes a react hook `useDeposit` which you can access the `client` with. Because this runs on Solana for now, you'll need to install any of your preferred solana wallet-adapter libs tailored for React or JS in general
30
+
31
+ We recommend this one: [@solana/wallet-adpater-react](https://www.npmjs.com/package/@solana/wallet-adapter-react). It'll come in handy when you'll need it to sign the transaction from `client.createDeposit(args)`
32
+
33
+ In your component, import the `useDeposit` hook like so:
34
+
35
+ ```tsx
36
+ import { useDeposit } from 'storacha-sol';
37
+
38
+ const UploadComponent = () => {
39
+ const client = useDeposit('testnet');
40
+ return <>// some markup</>;
41
+ };
42
+ ```
43
+
44
+ From the snippet above, you'll see that the hook takes an environment argument `"testnet"`. If you leave it as is, Typescript would start crying. So, to appease it, you should import the `Environment` type from `storacha-sol` and infer it.
45
+
46
+ ```ts
47
+ import { useDeposit, Environment } from "storacha-sol"
48
+ ...
49
+
50
+ const client = useDeposit("testnet" as Environment)
51
+ ```
52
+
53
+ In your component, we'll assume you already have file and duration state variables
54
+
55
+ ```tsx
56
+ import { useDeposit, Environment } from 'storacha-sol';
57
+ import { useSolanaWallet } from '@solana/wallet-adapter-react';
58
+
59
+ const UploadComponent = () => {
60
+ const [selectedFiles, setSelectedFiles] = useState<File>();
61
+ const [storageDuration, setStorageDuration] = useState(30);
62
+ const client = useDeposit('testnet' as Environment);
63
+ const { publicKey, signTransaction } = useSolanaWallet();
64
+
65
+ return <>// some markup</>;
66
+ };
67
+ ```
68
+
69
+ `createDeposit` expects the following args: `payer` (which is the publicKey, imported from wallet-adapter), `file`, `duration`, and the callback to sign a transaction.
70
+
71
+ See a sample of how you can achieve this below:
72
+
73
+ ```ts
74
+ const result = await client.createDeposit({
75
+ file,
76
+ durationDays: storageDuration,
77
+ payer: publicKey,
78
+ signTransaction: async (tx) => {
79
+ toast.loading('Please sign the transaction in your wallet...', {
80
+ id: 'upload-progress',
81
+ });
82
+
83
+ try {
84
+ const signed = await signTransaction(tx);
85
+ toast.loading('Transaction sent to network...', {
86
+ id: 'upload-progress',
87
+ });
88
+
89
+ return signed;
90
+ } catch (signError) {
91
+ console.error('❌ Transaction signing failed:', signError);
92
+ throw new Error(
93
+ `Transaction signing failed: ${signError instanceof Error ? signError.message : 'Unknown error'}`
94
+ );
95
+ }
96
+ },
97
+ });
98
+ ```
99
+
100
+ `storacha-sol` is type-safe, so you can always explore the content of the declaration file to see the structure. You could take a look at `UploadResult`, for starters.
101
+
102
+ and when `result` is successful, you can proceed with any other action you choose to carry out in your app.
103
+
104
+ ```ts
105
+ if (result.success) {
106
+ // do more stuff
107
+ }
108
+ ```
109
+
110
+ An edge-case you may want to consider before calling `createDeposit` is to check if the estimated storage cost is more than the wallet balance of the user, as this would fail to create the transaction.
111
+
112
+ You can use `client.estimateStorageCost` to get the values in SOL and compare if the balance is less than what was estimated before paying and provide a reasonable error message for your end-users.
113
+
114
+ ### View Upload History
115
+
116
+ You can fetch all uploads associated with a wallet address:
117
+
118
+ ```ts
119
+ const client = useDeposit('testnet' as Environment);
120
+ const history = await client.getUserUploadHistory(publicKey.toString());
121
+
122
+ console.log(history.userHistory); // Array of all deposits
123
+ ```
124
+
125
+ Each deposit in the history includes:
126
+
127
+ - File details (name, size, type, CID)
128
+ - Expiration date
129
+ - Deletion status (`active`, `warned`, `deleted`)
130
+ - Transaction hash
131
+ - Duration and cost information
132
+
133
+ ### Email Expiration Warnings
134
+
135
+ When creating a deposit, you can optionally provide an email address to receive expiration warnings:
136
+
137
+ ```ts
138
+ const result = await client.createDeposit({
139
+ file,
140
+ durationDays: storageDuration,
141
+ payer: publicKey,
142
+ userEmail: 'user@example.com', // Optional email for expiration warnings
143
+ signTransaction: async (tx) => {
144
+ const signed = await signTransaction(tx);
145
+ return signed;
146
+ },
147
+ });
148
+ ```
149
+
150
+ If provided, you'll receive an email notification **7 days before your file expires**, giving you time to extend the storage duration (feature coming soon!).
151
+
152
+ The warning email includes:
153
+
154
+ - File name and CID
155
+ - Exact expiration date
156
+ - Number of days remaining
157
+ - Direct link to view your file on IPFS
158
+
159
+ ### Automatic Cleanup
160
+
161
+ Files that have expired are automatically deleted from Storacha storage. This happens through a scheduled job that:
162
+
163
+ 1. Identifies expired deposits
164
+ 2. Removes the data from Storacha (including shards)
165
+ 3. Updates the deletion status in the database
166
+
167
+ Your upload history will show the deletion status, so you can track which files are still active, warned, or deleted.
168
+
169
+ ### Storage Renewal
170
+
171
+ Extend the storage duration for your existing uploads before they expire:
172
+
173
+ ```ts
174
+ const client = useDeposit('testnet' as Environment);
175
+ const { publicKey, signTransaction } = useSolanaWallet();
176
+
177
+ // First, get a quote to see what it'll cost
178
+ const quote = await client.getStorageRenewalCost(cid, 30); // 30 additional days
179
+
180
+ console.log(`Current expiration: ${quote.currentExpirationDate}`);
181
+ console.log(`New expiration: ${quote.newExpirationDate}`);
182
+ console.log(`Cost: ${quote.costInSOL} SOL`);
183
+
184
+ const result = await client.renewStorageDuration({
185
+ cid,
186
+ additionalDays: 30,
187
+ payer: publicKey,
188
+ signTransaction: async (tx) => {
189
+ const signed = await signTransaction(tx);
190
+ return signed;
191
+ },
192
+ });
193
+
194
+ if (result.success) {
195
+ console.log('Storage renewed! Transaction:', result.signature);
196
+ }
197
+ ```
198
+
199
+ **How it works:**
200
+
201
+ - `getStorageRenewalCost()` shows you the cost and new expiration date before committing
202
+ - `renewStorageDuration()` creates a payment transaction (same flow as initial upload)
203
+ - After payment confirms, your file's expiration date gets updated
204
+
205
+ ## Usage with Vite
206
+
207
+ When using this SDK in a project built with Vite, you may encounter a `ReferenceError: process is not defined`. This is because Vite does not automatically polyfill the Node.js `process` global, which this library may use.
208
+
209
+ To resolve this, you can define `process.env` to safely access `NODE_ENV` and additionally install `vite-plugin-node-polyfills` as a dev dependency and edit your `vite.config.ts` file:
210
+
211
+ ```ts
212
+ // vite.config.ts
213
+ import { defineConfig } from 'vite';
214
+ import react from '@vitejs/plugin-react';
215
+ import { nodePolyfills } from 'vite-plugin-node-polyfills';
216
+
217
+ export default defineConfig({
218
+ plugins: [react(), nodePolyfills()],
219
+ // ... other config
220
+ define: {
221
+ 'process.env': {
222
+ NODE_ENV: JSON.stringify(process.env.NODE_ENV || 'production'),
223
+ },
224
+ },
225
+ });
226
+ ```
227
+
228
+ This will prevent the runtime error and allow the SDK to function correctly.
229
+
230
+ ## Want to contribute?
231
+
232
+ Read the [Contributing guide](CONTRIBUTING.md)
package/dist/index.cjs ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";var D=Object.defineProperty;var _=Object.getOwnPropertyDescriptor;var H=Object.getOwnPropertyNames;var q=Object.prototype.hasOwnProperty;var z=(r,e)=>{for(var t in e)D(r,t,{get:e[t],enumerable:!0})},M=(r,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of H(e))!q.call(r,o)&&o!==t&&D(r,o,{get:()=>e[o],enumerable:!(n=_(e,o))||n.enumerable});return r};var K=r=>M(D({},"__esModule",{value:!0}),r);var W={};z(W,{Client:()=>S,Environment:()=>A,createDepositTxn:()=>C,fetchUserDepositHistory:()=>j,fetchUserUploadHistory:()=>j,getRpcUrl:()=>N,getStorageRenewalCost:()=>I,getUserUploadHistory:()=>j,renewStorageTxn:()=>x,useDeposit:()=>J,useUpload:()=>J});module.exports=K(W);var O=require("@solana/web3.js");var y={local:"http://localhost:5040",staging:"https://staging-api.toju.network",production:"https://api.toju.network"};function $(r){if(typeof window<"u"){let t=window.location.hostname;if(t==="localhost"||t==="127.0.0.1")return y.local}let e=r.toLowerCase();return e.includes("localhost")||e.includes("127.0.0.1")?y.local:e.includes("mainnet")?y.production:e.includes("testnet")||e.includes("devnet")?y.staging:y.production}var v=86400,F=1e9;var d=require("@solana/web3.js");async function C(r,e){let{file:t,duration:n,payer:o,connection:i,signTransaction:c,userEmail:l}=r;try{let a=new FormData;t.forEach(s=>a.append("file",s)),a.append("duration",n.toString()),a.append("publicKey",o.toBase58()),l&&a.append("userEmail",l);let m=t.length>1,g,b=await fetch(`${e}/upload/deposit`,{method:"POST",body:a});if(!b.ok)throw new Error("Failed to get deposit instructions");let p=await b.json();if(!p.instructions||!p.instructions.length)throw new Error("No instructions from deposit API");let E=await i.getLatestBlockhash("confirmed"),u=p.instructions[0],w=new d.TransactionInstruction({programId:new d.PublicKey(u.programId),keys:u.keys.map(s=>({pubkey:new d.PublicKey(s.pubkey),isSigner:s.isSigner,isWritable:s.isWritable})),data:Buffer.from(u.data,"base64")}),R=new d.Transaction;R.recentBlockhash=E.blockhash,R.feePayer=o,R.add(w);let B=await c(R),U;try{U=await i.sendRawTransaction(B.serialize(),{skipPreflight:!1,preflightCommitment:"confirmed"})}catch(s){throw s instanceof d.SendTransactionError?(s.logs??[]).some(L=>L.includes("already in use"))?new Error("This file has already been uploaded. You can find it in your dashboard."):new Error("Transaction failed during simulation. Please try again."):s}let T=await i.confirmTransaction({signature:U,blockhash:E.blockhash,lastValidBlockHeight:E.lastValidBlockHeight},"confirmed");if(T.value.err)throw console.error("Failed to confirm this transaction:",T.value.err),new Error(`Transaction failed: ${JSON.stringify(T.value.err)}`);try{await fetch(`${e}/upload/confirm`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({cid:p.cid,transactionHash:U})})}catch(s){console.warn("Failed to update transaction hash:",s)}p.error&&(g=p.error);let P=new FormData;t.forEach(s=>P.append("file",s));let h;if(m?h=await fetch(`${e}/upload/files?cid=${encodeURIComponent(p.cid)}`,{method:"POST",body:P}):h=await fetch(`${e}/upload/file?cid=${encodeURIComponent(p.cid)}`,{method:"POST",body:P}),!h.ok){let s="Unknown error";try{let k=await h.json();s=k.message||k.error||s}catch{}throw new Error("Deposit API error: "+s)}let f=await h?.json();return{signature:U,success:!0,cid:p.cid,url:f.object.url,message:f.object.message,fileInfo:f.object?{filename:f.object.fileInfo?.filename||"",size:f?.object?.fileInfo?.size||0,uploadedAt:f?.object?.fileInfo?.uploadedAt||"",type:f?.object?.fileInfo?.type||""}:void 0}}catch(a){return console.error(a),a instanceof Error&&(console.error("Error name:",a.name),console.error("Error message:",a.message),console.error("Error stack:",a.stack)),{signature:"",success:!1,cid:"",url:"",message:"",fileInfo:void 0,error:a instanceof Error?a.message:"Unknown error occurred"}}}async function I(r,e,t){try{let n=await fetch(`${t}/storage/renewal-cost?cid=${encodeURIComponent(r)}&duration=${e}`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!n.ok){let o=await n.json();throw new Error(o.message||"Failed to get storage renewal cost")}return await n.json()}catch(n){return console.error("Failed to get storage renewal cost",n),null}}async function x(r,e){let{cid:t,duration:n,payer:o,connection:i,signTransaction:c}=r,l=await fetch(`${e}/storage/renew`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({cid:t,duration:n,publicKey:o.toString()})});if(!l.ok){let u=await l.json().catch(()=>({}));throw new Error(u.message||"Failed to create renewal transaction")}let a=await l.json(),m=new d.Transaction;a.instructions.forEach(u=>{m.add({programId:new d.PublicKey(u.programId),keys:u.keys.map(w=>({pubkey:new d.PublicKey(w.pubkey),isSigner:w.isSigner,isWritable:w.isWritable})),data:Buffer.from(u.data,"base64")})});let{blockhash:g}=await i.getLatestBlockhash();m.recentBlockhash=g,m.feePayer=o;let b=await c(m),p=await i.sendRawTransaction(b.serialize());return await i.confirmTransaction(p,"confirmed"),(await fetch(`${e}/storage/confirm-renewal`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({cid:t,duration:n,transactionHash:p})})).ok||console.error("Failed to confirm renewal"),{success:!0,cid:t,signature:p,url:`https://w3s.link/ipfs/${t}`,message:"Storage renewed successfully"}}async function j(r,e,t={}){if(!r||typeof r!="string")throw new Error("User address is required and must be a string");let n=t.url||e,o=t.page??1,i=t.limit??20;try{let c=await fetch(`${n}/upload/history?userAddress=${encodeURIComponent(r)}?userAddress=${encodeURIComponent(r)}&page=${o}&limit=${i}`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!c.ok){let a=await c.json().catch(()=>({}));throw new Error(a.message||`Failed to fetch upload history: ${c.status} ${c.statusText}`)}let l=await c.json();if(typeof l!="object"||l===null||!Array.isArray(l.data)||typeof l!="object")throw new Error("Invalid response format from server");return l}catch(c){throw c instanceof Error?c:new Error("Unknown error occurred while fetching upload history")}}var A=(o=>(o.mainnet="mainnet-beta",o.testnet="testnet",o.devnet="devnet",o.local="localnet",o))(A||{});function N(r){switch(r){case"mainnet-beta":return"https://api.mainnet-beta.solana.com";case"testnet":return"https://api.testnet.solana.com";case"devnet":return"https://api.devnet.solana.com";case"localnet":return"http://localhost:5040";default:throw new Error(`Unsupported environment: ${r}`)}}var S=class{constructor(e){this.rpcUrl=N(e.environment),this.apiEndpoint=$(this.rpcUrl)}async createDeposit({payer:e,file:t,durationDays:n,signTransaction:o,userEmail:i}){console.log("Creating deposit transaction with environment:",this.rpcUrl);let c=new O.Connection(this.rpcUrl,"confirmed");return await C({file:t,duration:n*v,payer:e,connection:c,signTransaction:o,userEmail:i},this.apiEndpoint)}async estimateStorageCost(e,t){let n=e.reduce((m,g)=>m+g.size,0),o=Math.floor(t/86400),i=await fetch(`${this.apiEndpoint}/user/get-quote?size=${n}&duration=${o}`);if(!i.ok)throw new Error("Failed to get storage cost estimate");let{quote:c}=await i.json(),l=c.totalCost;return{sol:l/F,lamports:l}}async getUserUploadHistory(e,t,n){return await j(e,this.apiEndpoint,{page:t,limit:n})}async getStorageRenewalCost(e,t){return await I(e,t,this.apiEndpoint)}async renewStorageDuration({cid:e,duration:t,payer:n,signTransaction:o}){let i=new O.Connection(this.rpcUrl,"confirmed");return await x({cid:e,duration:t,payer:n,connection:i,signTransaction:o},this.apiEndpoint)}async getSolPrice(){let e=await fetch(`${this.apiEndpoint}/user/sol-price`);if(!e.ok)throw new Error("Couldn't fetch SOL price");return(await e.json()).price}};var J=r=>new S({environment:r||"testnet"});0&&(module.exports={Client,Environment,createDepositTxn,fetchUserDepositHistory,fetchUserUploadHistory,getRpcUrl,getStorageRenewalCost,getUserUploadHistory,renewStorageTxn,useDeposit,useUpload});
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/client.ts","../src/constants.ts","../src/payment.ts","../src/upload-history.ts","../src/hooks.ts"],"sourcesContent":["export * from './client';\nexport * from './hooks';\nexport * from './payment';\nexport * from './types';\nexport * from './upload-history';\n","import { Connection, PublicKey } from '@solana/web3.js';\nimport {\n DAY_TIME_IN_SECONDS,\n getEndpointForRpc,\n ONE_BILLION_LAMPORTS,\n} from './constants';\nimport {\n createDepositTxn,\n getStorageRenewalCost,\n renewStorageTxn,\n} from './payment';\nimport {\n CreateDepositArgs,\n StorageRenewalCost,\n StorageRenewalParams,\n UploadResult,\n} from './types';\nimport { getUserUploadHistory } from './upload-history';\n\nexport enum Environment {\n mainnet = 'mainnet-beta',\n testnet = 'testnet',\n devnet = 'devnet',\n local = 'localnet',\n}\n\nexport function getRpcUrl(env: Environment): string {\n switch (env) {\n case Environment.mainnet:\n return 'https://api.mainnet-beta.solana.com';\n case Environment.testnet:\n return 'https://api.testnet.solana.com';\n case Environment.devnet:\n return 'https://api.devnet.solana.com';\n case Environment.local:\n return 'http://localhost:5040';\n default:\n throw new Error(`Unsupported environment: ${env}`);\n }\n}\n\nexport interface ClientOptions {\n /** Solana RPC endpoint to use for chain interactions */\n environment: Environment;\n}\n\nexport interface UploadParams extends Pick<\n CreateDepositArgs,\n 'signTransaction' | 'userEmail'\n> {\n /** Wallet public key of the payer */\n payer: PublicKey;\n /** File(s) to be stored */\n file: File[];\n /** Duration in days to store the data */\n durationDays: number;\n}\n\n/**\n * @deprecated Use {@link UploadParams} instead.\n */\nexport type DepositParams = UploadParams;\n\n/**\n * Solana Storage Client — simplified (no fee estimation)\n */\nexport class Client {\n private rpcUrl: string;\n private apiEndpoint: string;\n\n constructor(options: ClientOptions) {\n this.rpcUrl = getRpcUrl(options.environment);\n this.apiEndpoint = getEndpointForRpc(this.rpcUrl);\n }\n\n /**\n * Creates a deposit transaction ready to be signed & sent by user's wallet.\n *\n * @param {Object} params\n * @param {PublicKey} params.payer - The public key (wallet address) of the connected wallet.\n * @param {File} params.file - The file to be uploaded.\n * @param {number} params.durationDays - How long (in days) the file should be stored.\n * @param {(tx: Transaction) => Promise<Transaction>} params.signTransaction -\n * A callback function to authorize the transaction via the Solana wallet library.\n *\n * @example\n * const { publicKey, signTransaction } = useSolanaWallet();\n * const result = await createDeposit({\n * payer: publicKey,\n * file,\n * durationDays: 30,\n * signTransaction,\n * });\n *\n * @returns {Promise<UploadResult>} The upload result after transaction is processed.\n */\n async createDeposit({\n payer,\n file,\n durationDays,\n signTransaction,\n userEmail,\n }: UploadParams): Promise<UploadResult> {\n console.log('Creating deposit transaction with environment:', this.rpcUrl);\n const connection = new Connection(this.rpcUrl, 'confirmed');\n\n return await createDepositTxn(\n {\n file,\n duration: durationDays * DAY_TIME_IN_SECONDS,\n payer,\n connection,\n signTransaction,\n userEmail,\n },\n this.apiEndpoint\n );\n }\n\n /**\n * estimates the cost for a file based on the amount of days it should be stored for\n * @param {File} file - a file to be uploaded\n * @param {number} duration - how long (in seconds) the file should be stored for\n */\n async estimateStorageCost(file: File[], duration: number) {\n const fileSizeInBytes = file.reduce((acc, f) => acc + f.size, 0);\n const durationInDays = Math.floor(duration / 86400); // convert seconds to day\n\n const response = await fetch(\n `${this.apiEndpoint}/user/get-quote?size=${fileSizeInBytes}&duration=${durationInDays}`\n );\n\n if (!response.ok) throw new Error('Failed to get storage cost estimate');\n\n const { quote } = await response.json();\n const totalLamports = quote.totalCost;\n const totalSOL = totalLamports / ONE_BILLION_LAMPORTS;\n\n return {\n sol: totalSOL,\n lamports: totalLamports,\n };\n }\n\n async getUserUploadHistory(userAddress: string, page: number, limit: number) {\n const response = await getUserUploadHistory(userAddress, this.apiEndpoint, {\n page,\n limit,\n });\n return response;\n }\n\n /**\n * Get cost estimate for renewing storage duration\n *\n * @param {string} cid - Content identifier of the file to renew\n * @param {number} duration - Number of additional days to extend storage\n *\n * @example\n * const quote = await client.getRenewalQuote('bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi', 30);\n * console.log(`Renewal cost: ${quote.costInSOL} SOL`);\n *\n * @returns {Promise<StorageRenewalCost | null>} Cost breakdown and expiration details\n */\n async getStorageRenewalCost(\n cid: string,\n duration: number\n ): Promise<StorageRenewalCost | null> {\n return await getStorageRenewalCost(cid, duration, this.apiEndpoint);\n }\n\n /**\n * Renew storage for an existing deposit\n *\n * @param {Object} params\n * @param {string} params.cid - Content identifier of the file to renew\n * @param {number} params.duration - Number of additional days to extend storage\n * @param {PublicKey} params.payer - Wallet public key paying for the renewal\n * @param {(tx: Transaction) => Promise<Transaction>} params.signTransaction - Transaction signing callback\n *\n * @example\n * const { publicKey, signTransaction } = useSolanaWallet();\n * const result = await client.renewStorage({\n * cid: 'bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi',\n * duration: 30,\n * payer: publicKey,\n * signTransaction,\n * });\n *\n * @returns {Promise<UploadResult>} Result of the renewal transaction\n */\n async renewStorageDuration({\n cid,\n duration,\n payer,\n signTransaction,\n }: StorageRenewalParams): Promise<UploadResult> {\n const connection = new Connection(this.rpcUrl, 'confirmed');\n\n return await renewStorageTxn(\n {\n cid,\n duration,\n payer,\n connection,\n signTransaction,\n },\n this.apiEndpoint\n );\n }\n\n /**\n * Gets the current SOL/USD price\n *\n * @example\n * const { price } = await client.getSolPrice();\n * console.log(`SOL price: $${price}`);\n *\n * @returns {Promise<{ price: number }>} Current SOL price in USD\n */\n async getSolPrice(): Promise<number> {\n const request = await fetch(`${this.apiEndpoint}/user/sol-price`);\n if (!request.ok) throw new Error(\"Couldn't fetch SOL price\");\n const data = await request.json();\n return data.price;\n }\n}\n","const ENDPOINTS = {\n local: 'http://localhost:5040',\n staging: 'https://staging-api.toju.network',\n production: 'https://api.toju.network',\n} as const;\n\n/**\n * Determines the appropriate backend endpoint based on Solana RPC URL\n */\nexport function getEndpointForRpc(rpcUrl: string): string {\n if (typeof window !== 'undefined') {\n const hostname = window.location.hostname;\n if (hostname === 'localhost' || hostname === '127.0.0.1')\n return ENDPOINTS.local;\n }\n\n const url = rpcUrl.toLowerCase();\n\n if (url.includes('localhost') || url.includes('127.0.0.1')) {\n return ENDPOINTS.local;\n }\n\n if (url.includes('mainnet')) {\n return ENDPOINTS.production;\n }\n\n if (url.includes('testnet') || url.includes('devnet')) {\n return ENDPOINTS.staging;\n }\n\n return ENDPOINTS.production;\n}\n\nexport const DAY_TIME_IN_SECONDS = 86400;\n\n/** 1 SOL in Lamports */\nexport const ONE_BILLION_LAMPORTS = 1_000_000_000;\n","import { Signature } from '@solana/kit';\nimport {\n PublicKey,\n SendTransactionError,\n Transaction,\n TransactionInstruction,\n} from '@solana/web3.js';\nimport {\n CreateDepositArgs,\n DepositResult,\n RenewStorageDurationArgs,\n StorageRenewalCost,\n StorageRenewalResult,\n UploadResult,\n} from './types';\n\n/**\n * Calls the deposit API for on-chain storage and returns a Transaction\n * which must be signed and sent externally by the user.\n *\n * @param params - {\n * cid: string;\n * file: File;\n * duration: number;\n * payer: PublicKey;\n * connection: Connection;\n * }\n * @returns Transaction\n */\nexport async function createDepositTxn(\n args: CreateDepositArgs,\n apiEndpoint: string\n): Promise<UploadResult> {\n const { file, duration, payer, connection, signTransaction, userEmail } =\n args;\n try {\n const formData = new FormData();\n file.forEach((f) => formData.append('file', f));\n formData.append('duration', duration.toString());\n formData.append('publicKey', payer.toBase58());\n if (userEmail) {\n formData.append('userEmail', userEmail);\n }\n\n const isMultipleFiles = file.length > 1;\n\n let uploadErr;\n\n const depositReq = await fetch(`${apiEndpoint}/upload/deposit`, {\n method: 'POST',\n body: formData,\n });\n if (!depositReq.ok) throw new Error('Failed to get deposit instructions');\n\n const depositRes: DepositResult = await depositReq.json();\n if (!depositRes.instructions || !depositRes.instructions.length)\n throw new Error('No instructions from deposit API');\n\n const latestBlockhash = await connection.getLatestBlockhash('confirmed');\n const instructions = depositRes.instructions[0];\n\n const depositInstruction = new TransactionInstruction({\n programId: new PublicKey(instructions.programId),\n keys: instructions.keys.map((k) => ({\n pubkey: new PublicKey(k.pubkey),\n isSigner: k.isSigner,\n isWritable: k.isWritable,\n })),\n data: Buffer.from(instructions.data, 'base64'),\n });\n\n const tx = new Transaction();\n tx.recentBlockhash = latestBlockhash.blockhash;\n tx.feePayer = payer;\n tx.add(depositInstruction);\n\n const signedTx = await signTransaction(tx);\n let signature: string;\n\n try {\n signature = await connection.sendRawTransaction(signedTx.serialize(), {\n skipPreflight: false, // not sure we should be disabling this verification step\n preflightCommitment: 'confirmed',\n });\n } catch (err) {\n if (err instanceof SendTransactionError) {\n const logs = err.logs ?? [];\n\n const isDuplicateUpload = logs.some((log) =>\n log.includes('already in use')\n );\n\n if (isDuplicateUpload) {\n throw new Error(\n 'This file has already been uploaded. You can find it in your dashboard.'\n );\n }\n\n throw new Error(\n 'Transaction failed during simulation. Please try again.'\n );\n }\n\n throw err;\n }\n const confirmation = await connection.confirmTransaction(\n {\n signature,\n blockhash: latestBlockhash.blockhash,\n lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,\n },\n 'confirmed'\n );\n\n if (confirmation.value.err) {\n console.error(\n 'Failed to confirm this transaction:',\n confirmation.value.err\n );\n throw new Error(\n `Transaction failed: ${JSON.stringify(confirmation.value.err)}`\n );\n }\n\n try {\n await fetch(`${apiEndpoint}/upload/confirm`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n cid: depositRes.cid,\n transactionHash: signature,\n }),\n });\n } catch (err) {\n console.warn('Failed to update transaction hash:', err);\n }\n\n if (depositRes.error) {\n uploadErr = depositRes.error;\n }\n\n const uploadForm = new FormData();\n file.forEach((f) => uploadForm.append('file', f));\n\n let fileUploadReq;\n // calls the upload functionality on our server with the file when deposit is successful\n if (isMultipleFiles) {\n fileUploadReq = await fetch(\n `${apiEndpoint}/upload/files?cid=${encodeURIComponent(depositRes.cid)}`,\n {\n method: 'POST',\n body: uploadForm,\n }\n );\n } else {\n fileUploadReq = await fetch(\n `${apiEndpoint}/upload/file?cid=${encodeURIComponent(depositRes.cid)}`,\n {\n method: 'POST',\n body: uploadForm,\n }\n );\n }\n\n if (!fileUploadReq.ok) {\n let err = 'Unknown error';\n try {\n const data: DepositResult = await fileUploadReq.json();\n err = data.message || data.error || err;\n } catch {}\n throw new Error('Deposit API error: ' + err);\n }\n\n const fileUploadRes: Pick<DepositResult, 'object' | 'cid' | 'message'> =\n await fileUploadReq?.json();\n\n return {\n signature: signature as Signature,\n success: true,\n cid: depositRes.cid,\n url: fileUploadRes.object.url,\n message: fileUploadRes.object.message,\n fileInfo: fileUploadRes.object\n ? {\n filename: fileUploadRes.object.fileInfo?.filename || '',\n size: fileUploadRes?.object?.fileInfo?.size || 0,\n uploadedAt: fileUploadRes?.object?.fileInfo?.uploadedAt || '',\n type: fileUploadRes?.object?.fileInfo?.type || '',\n }\n : undefined,\n };\n } catch (error) {\n console.error(error);\n if (error instanceof Error) {\n console.error('Error name:', error.name);\n console.error('Error message:', error.message);\n console.error('Error stack:', error.stack);\n }\n\n return {\n signature: '' as Signature,\n success: false,\n cid: '',\n url: '',\n message: '',\n fileInfo: undefined,\n error: error instanceof Error ? error.message : 'Unknown error occurred',\n };\n }\n}\n\n/**\n * Get cost estimate for renewing storage duration\n *\n * @param {string} cid - Content identifier of the uploaded data to renew\n * @param {number} duration - Number of additional days to extend storage\n *\n * @example\n * const quote = await client.getRenewalQuote('bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi', 30);\n * console.log(`Renewal cost: ${quote.costInSOL} SOL`);\n *\n * @returns {Promise<StorageRenewalCost | null>} Cost breakdown and expiration details\n */\nexport async function getStorageRenewalCost(\n cid: string,\n duration: number,\n apiEndpoint: string\n): Promise<StorageRenewalCost | null> {\n try {\n const request = await fetch(\n `${apiEndpoint}/storage/renewal-cost?cid=${encodeURIComponent(\n cid\n )}&duration=${duration}`,\n {\n method: 'GET',\n headers: { 'Content-Type': 'application/json' },\n }\n );\n\n if (!request.ok) {\n const response = await request.json();\n throw new Error(response.message || 'Failed to get storage renewal cost');\n }\n\n return await request.json();\n } catch (error) {\n console.error('Failed to get storage renewal cost', error);\n return null;\n }\n}\n\n/**\n * Renew storage for an existing deposit\n *\n * @param {Object} params\n * @param {string} params.cid - Content identifier of the uploaded data to renew\n * @param {number} params.duration - Number of additional days to extend storage\n * @param {PublicKey} params.payer - Wallet public key paying for the renewal\n * @param {(tx: Transaction) => Promise<Transaction>} params.signTransaction - Transaction signing callback\n *\n * @example\n * const { publicKey, signTransaction } = useSolanaWallet();\n * const result = await client.renewStorage({\n * cid: 'bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi',\n * additionalDays: 30,\n * payer: publicKey,\n * signTransaction,\n * });\n *\n * @returns {Promise<UploadResult>} Result of the renewal transaction\n */\nexport async function renewStorageTxn(\n args: RenewStorageDurationArgs,\n apiEndpoint: string\n): Promise<UploadResult> {\n const { cid, duration, payer, connection, signTransaction } = args;\n const renewalTransactionIx = await fetch(`${apiEndpoint}/storage/renew`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n cid,\n duration,\n publicKey: payer.toString(),\n }),\n });\n\n if (!renewalTransactionIx.ok) {\n const errorData = await renewalTransactionIx.json().catch(() => ({}));\n throw new Error(\n errorData.message || 'Failed to create renewal transaction'\n );\n }\n\n const renewalData: StorageRenewalResult = await renewalTransactionIx.json();\n const transaction = new Transaction();\n\n renewalData.instructions.forEach((ix) => {\n transaction.add({\n programId: new PublicKey(ix.programId),\n keys: ix.keys.map((key) => ({\n pubkey: new PublicKey(key.pubkey),\n isSigner: key.isSigner,\n isWritable: key.isWritable,\n })),\n data: Buffer.from(ix.data, 'base64'),\n });\n });\n\n const { blockhash } = await connection.getLatestBlockhash();\n transaction.recentBlockhash = blockhash;\n transaction.feePayer = payer;\n\n const signed = await signTransaction(transaction);\n const signature = await connection.sendRawTransaction(signed.serialize());\n await connection.confirmTransaction(signature, 'confirmed');\n\n const confirmRenewalTx = await fetch(\n `${apiEndpoint}/storage/confirm-renewal`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n cid,\n duration,\n transactionHash: signature,\n }),\n }\n );\n\n if (!confirmRenewalTx.ok) {\n console.error('Failed to confirm renewal');\n }\n\n return {\n success: true,\n cid,\n signature: signature as Signature,\n url: `https://w3s.link/ipfs/${cid}`,\n message: 'Storage renewed successfully',\n };\n}\n","import { ServerOptions, UploadHistoryResponse } from './types';\n\n/**\n * Get the upload history for a given user address from the server\n *\n * @param userAddress - The wallet address of the user\n * @param apiEndpoint - Base API URL\n * @param options - Optional server configuration and pagination options\n * @returns Promise<UploadHistoryResponse>\n *\n * @example\n * ```ts\n * const history = await getUserUploadHistory(\n * '9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM',\n * API_BASE_URL,\n * { page: 1, limit: 20 }\n * )\n *\n * console.log(history.data)\n * console.log(history.next)\n * ```\n *\n * @throws {Error} When the user address is invalid or the request fails\n */\nexport async function getUserUploadHistory(\n userAddress: string,\n apiEndpoint: string,\n options: ServerOptions & {\n page?: number;\n limit?: number;\n } = {}\n): Promise<UploadHistoryResponse> {\n if (!userAddress || typeof userAddress !== 'string') {\n throw new Error('User address is required and must be a string');\n }\n\n const baseUrl = options.url || apiEndpoint;\n const page = options.page ?? 1;\n const limit = options.limit ?? 20;\n\n try {\n const response = await fetch(\n `${baseUrl}/upload/history?userAddress=${encodeURIComponent(userAddress)}?userAddress=${encodeURIComponent(userAddress)}&page=${page}&limit=${limit}`,\n {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n },\n }\n );\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n throw new Error(\n errorData.message ||\n `Failed to fetch upload history: ${response.status} ${response.statusText}`\n );\n }\n\n const data: UploadHistoryResponse = await response.json();\n\n if (\n typeof data !== 'object' ||\n data === null ||\n !Array.isArray(data.data) ||\n typeof data !== 'object'\n ) {\n throw new Error('Invalid response format from server');\n }\n\n return data;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error('Unknown error occurred while fetching upload history');\n }\n}\n\n/**\n * @deprecated Use getUserUploadHistory instead\n */\nexport { getUserUploadHistory as fetchUserUploadHistory };\n\n/**\n * @deprecated Use getUserUploadHistory instead\n */\nexport { getUserUploadHistory as fetchUserDepositHistory };\n","import { Client, Environment } from './client';\n\nexport const useUpload = (environment: Environment) => {\n const client = new Client({ environment: environment || 'testnet' });\n return client;\n};\n\n/**\n * @deprecated Use {@link useUpload} instead.\n */\nexport { useUpload as useDeposit };\n"],"mappings":"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","names":["index_exports","__export","Client","Environment","createDepositTxn","getUserUploadHistory","getRpcUrl","getStorageRenewalCost","renewStorageTxn","useUpload","__toCommonJS","import_web3","ENDPOINTS","getEndpointForRpc","rpcUrl","hostname","url","DAY_TIME_IN_SECONDS","ONE_BILLION_LAMPORTS","import_web3","createDepositTxn","args","apiEndpoint","file","duration","payer","connection","signTransaction","userEmail","formData","f","isMultipleFiles","uploadErr","depositReq","depositRes","latestBlockhash","instructions","depositInstruction","k","tx","signedTx","signature","err","log","confirmation","uploadForm","fileUploadReq","data","fileUploadRes","error","getStorageRenewalCost","cid","request","response","renewStorageTxn","renewalTransactionIx","errorData","renewalData","transaction","ix","key","blockhash","signed","getUserUploadHistory","userAddress","apiEndpoint","options","baseUrl","page","limit","response","errorData","data","error","Environment","getRpcUrl","env","Client","options","getEndpointForRpc","payer","file","durationDays","signTransaction","userEmail","connection","createDepositTxn","DAY_TIME_IN_SECONDS","duration","fileSizeInBytes","acc","f","durationInDays","response","quote","totalLamports","ONE_BILLION_LAMPORTS","userAddress","page","limit","getUserUploadHistory","cid","getStorageRenewalCost","renewStorageTxn","request","useUpload","environment","Client"]}
@@ -0,0 +1,467 @@
1
+ import { Address, Signature } from '@solana/kit';
2
+ import { PublicKey, Connection, Transaction as Transaction$1 } from '@solana/web3.js';
3
+
4
+ interface ServerOptions {
5
+ /** URL pointing to the server (mostly Storacha's) */
6
+ url?: string;
7
+ }
8
+ /**
9
+ * Options needed to create an on-chain deposit for storage
10
+ */
11
+ interface UploadOptions {
12
+ /** content identifier for the data to be uploaded */
13
+ cid: string;
14
+ /** file/upload size in bytes */
15
+ size: number;
16
+ /** duration in days for how long the data should be retained */
17
+ duration: number;
18
+ /** wallet responsible for paying the deposit */
19
+ payer: Address;
20
+ /** optional Solana connection override (for testing or custom RPC) */
21
+ connection?: any;
22
+ /** Signature or transaction hash as proof of on-chain deposit */
23
+ signature?: string;
24
+ }
25
+ /**
26
+ * Result returned after a successful file upload
27
+ */
28
+ interface UploadResult {
29
+ /** message from the deposit transaction. could be an error or success message */
30
+ message?: string;
31
+ /** similar to message above but for error cases. can extrapoloate this later */
32
+ error?: string;
33
+ /** signature of the succesful transaction */
34
+ signature: Signature;
35
+ /** status of the request. successful or not. */
36
+ success: boolean;
37
+ /** CID of the uploaded content */
38
+ cid: string;
39
+ /** full URL where the content was uploaded to (on IPFS) */
40
+ url: string;
41
+ /** information of the file that was uploaded */
42
+ fileInfo?: {
43
+ /** file type */
44
+ type: string;
45
+ /** size of the uploaded content (in bytes) */
46
+ size: number;
47
+ /** UNIX timestamp (in seconds) of the time the file was uploaded */
48
+ uploadedAt: string;
49
+ /** name of the file uploaded */
50
+ filename: string;
51
+ };
52
+ }
53
+ /**
54
+ * Stored item entry returned when listing wallet space
55
+ */
56
+ interface WalletItem {
57
+ /** CID of the stored item */
58
+ cid: string;
59
+ /** file size in bytes */
60
+ size: number;
61
+ /** expiration timestamp in seconds */
62
+ expiresAt: number;
63
+ }
64
+ /**
65
+ * Config values fetched from the on-chain ConfigAccount
66
+ */
67
+ interface OnChainConfig {
68
+ /** current rate in lamports per byte per day */
69
+ ratePerBytePerDay: bigint;
70
+ /** minimum required duration in days */
71
+ minDurationDays: number;
72
+ /** wallet where provider can withdraw claimed funds */
73
+ withdrawalWallet: Address;
74
+ }
75
+ /**
76
+ * Deposit details stored on-chain for each user upload
77
+ */
78
+ interface OnChainDeposit {
79
+ /** public key of the depositor */
80
+ depositor: Address;
81
+ /** file object containing metadata about the upload */
82
+ file: File[];
83
+ /** storage duration (days) */
84
+ duration: number;
85
+ /** amount deposited in lamports */
86
+ depositAmount: bigint;
87
+ /** slot when deposit was made */
88
+ depositSlot: number;
89
+ /** last claimed slot for reward release */
90
+ lastClaimedSlot: number;
91
+ }
92
+ interface DepositResult extends Pick<UploadResult, 'message' | 'error'> {
93
+ /** CID of the stored item */
94
+ cid: string;
95
+ /** transaction instruction */
96
+ instructions: Array<{
97
+ programId: string;
98
+ keys: Array<{
99
+ pubkey: string;
100
+ isSigner: boolean;
101
+ isWritable: boolean;
102
+ }>;
103
+ data: string;
104
+ }>;
105
+ /** result of a successful upload */
106
+ object: UploadResult;
107
+ }
108
+ interface CreateDepositArgs extends Omit<OnChainDeposit, 'depositAmount' | 'depositor' | 'depositSlot' | 'lastClaimedSlot'> {
109
+ /** Public key of the user paying for the upload */
110
+ payer: PublicKey;
111
+ /** Wallet connection used to query chain state or recent blockhash */
112
+ connection: Connection;
113
+ /**
114
+ * a callback function to authorize the transaction via the solana wallet lib
115
+ * @example
116
+ * const {publickKey, signTransaction} = useSolanaWallet()
117
+ * const signTransaction = await signTransaction(tx)
118
+ * */
119
+ signTransaction: (tx: Transaction$1) => Promise<Transaction$1>;
120
+ /** Optional user email for expiration notifications */
121
+ userEmail?: string;
122
+ }
123
+ /** Arguments for renewing storage duration */
124
+ interface StorageRenewalParams extends Pick<CreateDepositArgs, 'payer' | 'signTransaction'> {
125
+ /** Content identifier of the uploaded data to be renewed */
126
+ cid: string;
127
+ /** Duration in days to extend storage */
128
+ duration: number;
129
+ }
130
+ /** Internal arguments for renewStorageTxn */
131
+ interface RenewStorageDurationArgs extends Pick<CreateDepositArgs, 'payer' | 'signTransaction' | 'connection'> {
132
+ /** Content identifier of the uploaded data to be renewed */
133
+ cid: string;
134
+ /** Duration in days to extend storage */
135
+ duration: number;
136
+ }
137
+ /**
138
+ * Transaction record for an upload (initial deposit or renewal)
139
+ */
140
+ interface Transaction {
141
+ /** Unique identifier for the transaction */
142
+ id: number;
143
+ /** ID of the associated deposit */
144
+ depositId: number;
145
+ /** Content identifier of the upload */
146
+ contentCid: string;
147
+ /** Solana transaction hash */
148
+ transactionHash: string;
149
+ /** Type of transaction: 'initial_deposit' | 'renewal' */
150
+ transactionType: string;
151
+ /** Amount paid in lamports */
152
+ amountInLamports: number;
153
+ /** Duration in days purchased */
154
+ durationDays: number;
155
+ /** Timestamp when the transaction was created */
156
+ createdAt: string;
157
+ }
158
+ /**
159
+ * Individual upload history entry from the server
160
+ */
161
+ interface UploadHistory {
162
+ /** Unique identifier for the deposit */
163
+ id: number;
164
+ /** User's wallet address (deposit key) */
165
+ depositKey: string;
166
+ /** Content identifier of the uploaded file */
167
+ contentCid: string;
168
+ /** Duration in days the file is stored for */
169
+ durationDays: number;
170
+ /** Amount deposited in lamports */
171
+ depositAmount: number;
172
+ /** Slot when the deposit was made */
173
+ depositSlot: number;
174
+ /** Last slot when rewards were claimed */
175
+ lastClaimedSlot: number;
176
+ /** Timestamp when the deposit was created */
177
+ createdAt: string;
178
+ /** Expiration date of the upload */
179
+ expiresAt?: string;
180
+ /** User email for notifications */
181
+ userEmail?: string;
182
+ /** Name of the uploaded file */
183
+ fileName?: string;
184
+ /** MIME type of the file */
185
+ fileType?: string;
186
+ /** Size of the file in bytes */
187
+ fileSize?: number;
188
+ /** Solana transaction hash */
189
+ transactionHash?: string;
190
+ /** Deletion status: 'active' | 'warned' | 'deleted' */
191
+ deletionStatus?: string;
192
+ /** Timestamp when warning email was sent */
193
+ warningSentAt?: string;
194
+ /** Optional array of all transactions for this upload */
195
+ transactions?: Transaction[];
196
+ }
197
+ /**
198
+ * @deprecated Use UploadHistory instead
199
+ */
200
+ type DepositHistoryEntry = UploadHistory;
201
+ interface PaginationMeta {
202
+ /** Total number of records */
203
+ total: number;
204
+ /** Current page (1-indexed) */
205
+ page: number;
206
+ /** Page size */
207
+ pageSize: number;
208
+ /** Total number of pages */
209
+ totalPages: number;
210
+ /** URL to fetch next page */
211
+ next: string | null;
212
+ /** URL to fetch previous page */
213
+ prev: string | null;
214
+ }
215
+ /**
216
+ * Response from the getUserUploadHistory endpoint
217
+ */
218
+ interface UploadHistoryResponse extends PaginationMeta {
219
+ /** Array of upload history entries */
220
+ data: UploadHistory[] | null;
221
+ /** The user address that was queried */
222
+ userAddress: string;
223
+ }
224
+ /**
225
+ * @deprecated Use UploadHistoryResponse instead
226
+ */
227
+ type DepositHistoryResponse = UploadHistoryResponse;
228
+ /**
229
+ * Storage renewal cost estimation
230
+ */
231
+ type StorageRenewalCost = {
232
+ /** New expiration date after renewal */
233
+ newExpirationDate: string;
234
+ /** Current expiration date before renewal */
235
+ currentExpirationDate: string;
236
+ /** Number of additional days being added */
237
+ additionalDays: string;
238
+ /** Cost of renewal in lamports */
239
+ costInLamports: number;
240
+ /** Cost of renewal in SOL */
241
+ costInSOL: number;
242
+ /** Details about the file being renewed */
243
+ fileDetails: {
244
+ /** Content identifier */
245
+ cid: string;
246
+ /** Name of the file */
247
+ fileName: string;
248
+ /** Size of the file in bytes */
249
+ fileSize: number;
250
+ };
251
+ };
252
+ /**
253
+ * Storage renewal transaction result
254
+ */
255
+ type StorageRenewalResult = {
256
+ /** Content identifier of the uploaded data to be renewed */
257
+ cid: string;
258
+ /** Status message about the renewal */
259
+ message: string;
260
+ /** Transaction instructions for the user to sign */
261
+ instructions: Array<{
262
+ programId: string;
263
+ keys: Array<{
264
+ pubkey: string;
265
+ isSigner: boolean;
266
+ isWritable: boolean;
267
+ }>;
268
+ data: string;
269
+ }>;
270
+ /** Number of additional days being added */
271
+ duration: number;
272
+ /** Cost breakdown for the renewal */
273
+ cost: {
274
+ /** Cost in lamports */
275
+ lamports: number;
276
+ /** Cost in SOL */
277
+ sol: number;
278
+ };
279
+ };
280
+
281
+ declare enum Environment {
282
+ mainnet = "mainnet-beta",
283
+ testnet = "testnet",
284
+ devnet = "devnet",
285
+ local = "localnet"
286
+ }
287
+ declare function getRpcUrl(env: Environment): string;
288
+ interface ClientOptions {
289
+ /** Solana RPC endpoint to use for chain interactions */
290
+ environment: Environment;
291
+ }
292
+ interface UploadParams extends Pick<CreateDepositArgs, 'signTransaction' | 'userEmail'> {
293
+ /** Wallet public key of the payer */
294
+ payer: PublicKey;
295
+ /** File(s) to be stored */
296
+ file: File[];
297
+ /** Duration in days to store the data */
298
+ durationDays: number;
299
+ }
300
+ /**
301
+ * @deprecated Use {@link UploadParams} instead.
302
+ */
303
+ type DepositParams = UploadParams;
304
+ /**
305
+ * Solana Storage Client — simplified (no fee estimation)
306
+ */
307
+ declare class Client {
308
+ private rpcUrl;
309
+ private apiEndpoint;
310
+ constructor(options: ClientOptions);
311
+ /**
312
+ * Creates a deposit transaction ready to be signed & sent by user's wallet.
313
+ *
314
+ * @param {Object} params
315
+ * @param {PublicKey} params.payer - The public key (wallet address) of the connected wallet.
316
+ * @param {File} params.file - The file to be uploaded.
317
+ * @param {number} params.durationDays - How long (in days) the file should be stored.
318
+ * @param {(tx: Transaction) => Promise<Transaction>} params.signTransaction -
319
+ * A callback function to authorize the transaction via the Solana wallet library.
320
+ *
321
+ * @example
322
+ * const { publicKey, signTransaction } = useSolanaWallet();
323
+ * const result = await createDeposit({
324
+ * payer: publicKey,
325
+ * file,
326
+ * durationDays: 30,
327
+ * signTransaction,
328
+ * });
329
+ *
330
+ * @returns {Promise<UploadResult>} The upload result after transaction is processed.
331
+ */
332
+ createDeposit({ payer, file, durationDays, signTransaction, userEmail, }: UploadParams): Promise<UploadResult>;
333
+ /**
334
+ * estimates the cost for a file based on the amount of days it should be stored for
335
+ * @param {File} file - a file to be uploaded
336
+ * @param {number} duration - how long (in seconds) the file should be stored for
337
+ */
338
+ estimateStorageCost(file: File[], duration: number): Promise<{
339
+ sol: number;
340
+ lamports: any;
341
+ }>;
342
+ getUserUploadHistory(userAddress: string, page: number, limit: number): Promise<UploadHistoryResponse>;
343
+ /**
344
+ * Get cost estimate for renewing storage duration
345
+ *
346
+ * @param {string} cid - Content identifier of the file to renew
347
+ * @param {number} duration - Number of additional days to extend storage
348
+ *
349
+ * @example
350
+ * const quote = await client.getRenewalQuote('bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi', 30);
351
+ * console.log(`Renewal cost: ${quote.costInSOL} SOL`);
352
+ *
353
+ * @returns {Promise<StorageRenewalCost | null>} Cost breakdown and expiration details
354
+ */
355
+ getStorageRenewalCost(cid: string, duration: number): Promise<StorageRenewalCost | null>;
356
+ /**
357
+ * Renew storage for an existing deposit
358
+ *
359
+ * @param {Object} params
360
+ * @param {string} params.cid - Content identifier of the file to renew
361
+ * @param {number} params.duration - Number of additional days to extend storage
362
+ * @param {PublicKey} params.payer - Wallet public key paying for the renewal
363
+ * @param {(tx: Transaction) => Promise<Transaction>} params.signTransaction - Transaction signing callback
364
+ *
365
+ * @example
366
+ * const { publicKey, signTransaction } = useSolanaWallet();
367
+ * const result = await client.renewStorage({
368
+ * cid: 'bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi',
369
+ * duration: 30,
370
+ * payer: publicKey,
371
+ * signTransaction,
372
+ * });
373
+ *
374
+ * @returns {Promise<UploadResult>} Result of the renewal transaction
375
+ */
376
+ renewStorageDuration({ cid, duration, payer, signTransaction, }: StorageRenewalParams): Promise<UploadResult>;
377
+ /**
378
+ * Gets the current SOL/USD price
379
+ *
380
+ * @example
381
+ * const { price } = await client.getSolPrice();
382
+ * console.log(`SOL price: $${price}`);
383
+ *
384
+ * @returns {Promise<{ price: number }>} Current SOL price in USD
385
+ */
386
+ getSolPrice(): Promise<number>;
387
+ }
388
+
389
+ declare const useUpload: (environment: Environment) => Client;
390
+
391
+ /**
392
+ * Calls the deposit API for on-chain storage and returns a Transaction
393
+ * which must be signed and sent externally by the user.
394
+ *
395
+ * @param params - {
396
+ * cid: string;
397
+ * file: File;
398
+ * duration: number;
399
+ * payer: PublicKey;
400
+ * connection: Connection;
401
+ * }
402
+ * @returns Transaction
403
+ */
404
+ declare function createDepositTxn(args: CreateDepositArgs, apiEndpoint: string): Promise<UploadResult>;
405
+ /**
406
+ * Get cost estimate for renewing storage duration
407
+ *
408
+ * @param {string} cid - Content identifier of the uploaded data to renew
409
+ * @param {number} duration - Number of additional days to extend storage
410
+ *
411
+ * @example
412
+ * const quote = await client.getRenewalQuote('bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi', 30);
413
+ * console.log(`Renewal cost: ${quote.costInSOL} SOL`);
414
+ *
415
+ * @returns {Promise<StorageRenewalCost | null>} Cost breakdown and expiration details
416
+ */
417
+ declare function getStorageRenewalCost(cid: string, duration: number, apiEndpoint: string): Promise<StorageRenewalCost | null>;
418
+ /**
419
+ * Renew storage for an existing deposit
420
+ *
421
+ * @param {Object} params
422
+ * @param {string} params.cid - Content identifier of the uploaded data to renew
423
+ * @param {number} params.duration - Number of additional days to extend storage
424
+ * @param {PublicKey} params.payer - Wallet public key paying for the renewal
425
+ * @param {(tx: Transaction) => Promise<Transaction>} params.signTransaction - Transaction signing callback
426
+ *
427
+ * @example
428
+ * const { publicKey, signTransaction } = useSolanaWallet();
429
+ * const result = await client.renewStorage({
430
+ * cid: 'bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi',
431
+ * additionalDays: 30,
432
+ * payer: publicKey,
433
+ * signTransaction,
434
+ * });
435
+ *
436
+ * @returns {Promise<UploadResult>} Result of the renewal transaction
437
+ */
438
+ declare function renewStorageTxn(args: RenewStorageDurationArgs, apiEndpoint: string): Promise<UploadResult>;
439
+
440
+ /**
441
+ * Get the upload history for a given user address from the server
442
+ *
443
+ * @param userAddress - The wallet address of the user
444
+ * @param apiEndpoint - Base API URL
445
+ * @param options - Optional server configuration and pagination options
446
+ * @returns Promise<UploadHistoryResponse>
447
+ *
448
+ * @example
449
+ * ```ts
450
+ * const history = await getUserUploadHistory(
451
+ * '9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM',
452
+ * API_BASE_URL,
453
+ * { page: 1, limit: 20 }
454
+ * )
455
+ *
456
+ * console.log(history.data)
457
+ * console.log(history.next)
458
+ * ```
459
+ *
460
+ * @throws {Error} When the user address is invalid or the request fails
461
+ */
462
+ declare function getUserUploadHistory(userAddress: string, apiEndpoint: string, options?: ServerOptions & {
463
+ page?: number;
464
+ limit?: number;
465
+ }): Promise<UploadHistoryResponse>;
466
+
467
+ export { Client, type ClientOptions, type CreateDepositArgs, type DepositHistoryEntry, type DepositHistoryResponse, type DepositParams, type DepositResult, Environment, type OnChainConfig, type OnChainDeposit, type PaginationMeta, type RenewStorageDurationArgs, type ServerOptions, type StorageRenewalCost, type StorageRenewalParams, type StorageRenewalResult, type Transaction, type UploadHistory, type UploadHistoryResponse, type UploadOptions, type UploadParams, type UploadResult, type WalletItem, createDepositTxn, getUserUploadHistory as fetchUserDepositHistory, getUserUploadHistory as fetchUserUploadHistory, getRpcUrl, getStorageRenewalCost, getUserUploadHistory, renewStorageTxn, useUpload as useDeposit, useUpload };
@@ -0,0 +1,467 @@
1
+ import { Address, Signature } from '@solana/kit';
2
+ import { PublicKey, Connection, Transaction as Transaction$1 } from '@solana/web3.js';
3
+
4
+ interface ServerOptions {
5
+ /** URL pointing to the server (mostly Storacha's) */
6
+ url?: string;
7
+ }
8
+ /**
9
+ * Options needed to create an on-chain deposit for storage
10
+ */
11
+ interface UploadOptions {
12
+ /** content identifier for the data to be uploaded */
13
+ cid: string;
14
+ /** file/upload size in bytes */
15
+ size: number;
16
+ /** duration in days for how long the data should be retained */
17
+ duration: number;
18
+ /** wallet responsible for paying the deposit */
19
+ payer: Address;
20
+ /** optional Solana connection override (for testing or custom RPC) */
21
+ connection?: any;
22
+ /** Signature or transaction hash as proof of on-chain deposit */
23
+ signature?: string;
24
+ }
25
+ /**
26
+ * Result returned after a successful file upload
27
+ */
28
+ interface UploadResult {
29
+ /** message from the deposit transaction. could be an error or success message */
30
+ message?: string;
31
+ /** similar to message above but for error cases. can extrapoloate this later */
32
+ error?: string;
33
+ /** signature of the succesful transaction */
34
+ signature: Signature;
35
+ /** status of the request. successful or not. */
36
+ success: boolean;
37
+ /** CID of the uploaded content */
38
+ cid: string;
39
+ /** full URL where the content was uploaded to (on IPFS) */
40
+ url: string;
41
+ /** information of the file that was uploaded */
42
+ fileInfo?: {
43
+ /** file type */
44
+ type: string;
45
+ /** size of the uploaded content (in bytes) */
46
+ size: number;
47
+ /** UNIX timestamp (in seconds) of the time the file was uploaded */
48
+ uploadedAt: string;
49
+ /** name of the file uploaded */
50
+ filename: string;
51
+ };
52
+ }
53
+ /**
54
+ * Stored item entry returned when listing wallet space
55
+ */
56
+ interface WalletItem {
57
+ /** CID of the stored item */
58
+ cid: string;
59
+ /** file size in bytes */
60
+ size: number;
61
+ /** expiration timestamp in seconds */
62
+ expiresAt: number;
63
+ }
64
+ /**
65
+ * Config values fetched from the on-chain ConfigAccount
66
+ */
67
+ interface OnChainConfig {
68
+ /** current rate in lamports per byte per day */
69
+ ratePerBytePerDay: bigint;
70
+ /** minimum required duration in days */
71
+ minDurationDays: number;
72
+ /** wallet where provider can withdraw claimed funds */
73
+ withdrawalWallet: Address;
74
+ }
75
+ /**
76
+ * Deposit details stored on-chain for each user upload
77
+ */
78
+ interface OnChainDeposit {
79
+ /** public key of the depositor */
80
+ depositor: Address;
81
+ /** file object containing metadata about the upload */
82
+ file: File[];
83
+ /** storage duration (days) */
84
+ duration: number;
85
+ /** amount deposited in lamports */
86
+ depositAmount: bigint;
87
+ /** slot when deposit was made */
88
+ depositSlot: number;
89
+ /** last claimed slot for reward release */
90
+ lastClaimedSlot: number;
91
+ }
92
+ interface DepositResult extends Pick<UploadResult, 'message' | 'error'> {
93
+ /** CID of the stored item */
94
+ cid: string;
95
+ /** transaction instruction */
96
+ instructions: Array<{
97
+ programId: string;
98
+ keys: Array<{
99
+ pubkey: string;
100
+ isSigner: boolean;
101
+ isWritable: boolean;
102
+ }>;
103
+ data: string;
104
+ }>;
105
+ /** result of a successful upload */
106
+ object: UploadResult;
107
+ }
108
+ interface CreateDepositArgs extends Omit<OnChainDeposit, 'depositAmount' | 'depositor' | 'depositSlot' | 'lastClaimedSlot'> {
109
+ /** Public key of the user paying for the upload */
110
+ payer: PublicKey;
111
+ /** Wallet connection used to query chain state or recent blockhash */
112
+ connection: Connection;
113
+ /**
114
+ * a callback function to authorize the transaction via the solana wallet lib
115
+ * @example
116
+ * const {publickKey, signTransaction} = useSolanaWallet()
117
+ * const signTransaction = await signTransaction(tx)
118
+ * */
119
+ signTransaction: (tx: Transaction$1) => Promise<Transaction$1>;
120
+ /** Optional user email for expiration notifications */
121
+ userEmail?: string;
122
+ }
123
+ /** Arguments for renewing storage duration */
124
+ interface StorageRenewalParams extends Pick<CreateDepositArgs, 'payer' | 'signTransaction'> {
125
+ /** Content identifier of the uploaded data to be renewed */
126
+ cid: string;
127
+ /** Duration in days to extend storage */
128
+ duration: number;
129
+ }
130
+ /** Internal arguments for renewStorageTxn */
131
+ interface RenewStorageDurationArgs extends Pick<CreateDepositArgs, 'payer' | 'signTransaction' | 'connection'> {
132
+ /** Content identifier of the uploaded data to be renewed */
133
+ cid: string;
134
+ /** Duration in days to extend storage */
135
+ duration: number;
136
+ }
137
+ /**
138
+ * Transaction record for an upload (initial deposit or renewal)
139
+ */
140
+ interface Transaction {
141
+ /** Unique identifier for the transaction */
142
+ id: number;
143
+ /** ID of the associated deposit */
144
+ depositId: number;
145
+ /** Content identifier of the upload */
146
+ contentCid: string;
147
+ /** Solana transaction hash */
148
+ transactionHash: string;
149
+ /** Type of transaction: 'initial_deposit' | 'renewal' */
150
+ transactionType: string;
151
+ /** Amount paid in lamports */
152
+ amountInLamports: number;
153
+ /** Duration in days purchased */
154
+ durationDays: number;
155
+ /** Timestamp when the transaction was created */
156
+ createdAt: string;
157
+ }
158
+ /**
159
+ * Individual upload history entry from the server
160
+ */
161
+ interface UploadHistory {
162
+ /** Unique identifier for the deposit */
163
+ id: number;
164
+ /** User's wallet address (deposit key) */
165
+ depositKey: string;
166
+ /** Content identifier of the uploaded file */
167
+ contentCid: string;
168
+ /** Duration in days the file is stored for */
169
+ durationDays: number;
170
+ /** Amount deposited in lamports */
171
+ depositAmount: number;
172
+ /** Slot when the deposit was made */
173
+ depositSlot: number;
174
+ /** Last slot when rewards were claimed */
175
+ lastClaimedSlot: number;
176
+ /** Timestamp when the deposit was created */
177
+ createdAt: string;
178
+ /** Expiration date of the upload */
179
+ expiresAt?: string;
180
+ /** User email for notifications */
181
+ userEmail?: string;
182
+ /** Name of the uploaded file */
183
+ fileName?: string;
184
+ /** MIME type of the file */
185
+ fileType?: string;
186
+ /** Size of the file in bytes */
187
+ fileSize?: number;
188
+ /** Solana transaction hash */
189
+ transactionHash?: string;
190
+ /** Deletion status: 'active' | 'warned' | 'deleted' */
191
+ deletionStatus?: string;
192
+ /** Timestamp when warning email was sent */
193
+ warningSentAt?: string;
194
+ /** Optional array of all transactions for this upload */
195
+ transactions?: Transaction[];
196
+ }
197
+ /**
198
+ * @deprecated Use UploadHistory instead
199
+ */
200
+ type DepositHistoryEntry = UploadHistory;
201
+ interface PaginationMeta {
202
+ /** Total number of records */
203
+ total: number;
204
+ /** Current page (1-indexed) */
205
+ page: number;
206
+ /** Page size */
207
+ pageSize: number;
208
+ /** Total number of pages */
209
+ totalPages: number;
210
+ /** URL to fetch next page */
211
+ next: string | null;
212
+ /** URL to fetch previous page */
213
+ prev: string | null;
214
+ }
215
+ /**
216
+ * Response from the getUserUploadHistory endpoint
217
+ */
218
+ interface UploadHistoryResponse extends PaginationMeta {
219
+ /** Array of upload history entries */
220
+ data: UploadHistory[] | null;
221
+ /** The user address that was queried */
222
+ userAddress: string;
223
+ }
224
+ /**
225
+ * @deprecated Use UploadHistoryResponse instead
226
+ */
227
+ type DepositHistoryResponse = UploadHistoryResponse;
228
+ /**
229
+ * Storage renewal cost estimation
230
+ */
231
+ type StorageRenewalCost = {
232
+ /** New expiration date after renewal */
233
+ newExpirationDate: string;
234
+ /** Current expiration date before renewal */
235
+ currentExpirationDate: string;
236
+ /** Number of additional days being added */
237
+ additionalDays: string;
238
+ /** Cost of renewal in lamports */
239
+ costInLamports: number;
240
+ /** Cost of renewal in SOL */
241
+ costInSOL: number;
242
+ /** Details about the file being renewed */
243
+ fileDetails: {
244
+ /** Content identifier */
245
+ cid: string;
246
+ /** Name of the file */
247
+ fileName: string;
248
+ /** Size of the file in bytes */
249
+ fileSize: number;
250
+ };
251
+ };
252
+ /**
253
+ * Storage renewal transaction result
254
+ */
255
+ type StorageRenewalResult = {
256
+ /** Content identifier of the uploaded data to be renewed */
257
+ cid: string;
258
+ /** Status message about the renewal */
259
+ message: string;
260
+ /** Transaction instructions for the user to sign */
261
+ instructions: Array<{
262
+ programId: string;
263
+ keys: Array<{
264
+ pubkey: string;
265
+ isSigner: boolean;
266
+ isWritable: boolean;
267
+ }>;
268
+ data: string;
269
+ }>;
270
+ /** Number of additional days being added */
271
+ duration: number;
272
+ /** Cost breakdown for the renewal */
273
+ cost: {
274
+ /** Cost in lamports */
275
+ lamports: number;
276
+ /** Cost in SOL */
277
+ sol: number;
278
+ };
279
+ };
280
+
281
+ declare enum Environment {
282
+ mainnet = "mainnet-beta",
283
+ testnet = "testnet",
284
+ devnet = "devnet",
285
+ local = "localnet"
286
+ }
287
+ declare function getRpcUrl(env: Environment): string;
288
+ interface ClientOptions {
289
+ /** Solana RPC endpoint to use for chain interactions */
290
+ environment: Environment;
291
+ }
292
+ interface UploadParams extends Pick<CreateDepositArgs, 'signTransaction' | 'userEmail'> {
293
+ /** Wallet public key of the payer */
294
+ payer: PublicKey;
295
+ /** File(s) to be stored */
296
+ file: File[];
297
+ /** Duration in days to store the data */
298
+ durationDays: number;
299
+ }
300
+ /**
301
+ * @deprecated Use {@link UploadParams} instead.
302
+ */
303
+ type DepositParams = UploadParams;
304
+ /**
305
+ * Solana Storage Client — simplified (no fee estimation)
306
+ */
307
+ declare class Client {
308
+ private rpcUrl;
309
+ private apiEndpoint;
310
+ constructor(options: ClientOptions);
311
+ /**
312
+ * Creates a deposit transaction ready to be signed & sent by user's wallet.
313
+ *
314
+ * @param {Object} params
315
+ * @param {PublicKey} params.payer - The public key (wallet address) of the connected wallet.
316
+ * @param {File} params.file - The file to be uploaded.
317
+ * @param {number} params.durationDays - How long (in days) the file should be stored.
318
+ * @param {(tx: Transaction) => Promise<Transaction>} params.signTransaction -
319
+ * A callback function to authorize the transaction via the Solana wallet library.
320
+ *
321
+ * @example
322
+ * const { publicKey, signTransaction } = useSolanaWallet();
323
+ * const result = await createDeposit({
324
+ * payer: publicKey,
325
+ * file,
326
+ * durationDays: 30,
327
+ * signTransaction,
328
+ * });
329
+ *
330
+ * @returns {Promise<UploadResult>} The upload result after transaction is processed.
331
+ */
332
+ createDeposit({ payer, file, durationDays, signTransaction, userEmail, }: UploadParams): Promise<UploadResult>;
333
+ /**
334
+ * estimates the cost for a file based on the amount of days it should be stored for
335
+ * @param {File} file - a file to be uploaded
336
+ * @param {number} duration - how long (in seconds) the file should be stored for
337
+ */
338
+ estimateStorageCost(file: File[], duration: number): Promise<{
339
+ sol: number;
340
+ lamports: any;
341
+ }>;
342
+ getUserUploadHistory(userAddress: string, page: number, limit: number): Promise<UploadHistoryResponse>;
343
+ /**
344
+ * Get cost estimate for renewing storage duration
345
+ *
346
+ * @param {string} cid - Content identifier of the file to renew
347
+ * @param {number} duration - Number of additional days to extend storage
348
+ *
349
+ * @example
350
+ * const quote = await client.getRenewalQuote('bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi', 30);
351
+ * console.log(`Renewal cost: ${quote.costInSOL} SOL`);
352
+ *
353
+ * @returns {Promise<StorageRenewalCost | null>} Cost breakdown and expiration details
354
+ */
355
+ getStorageRenewalCost(cid: string, duration: number): Promise<StorageRenewalCost | null>;
356
+ /**
357
+ * Renew storage for an existing deposit
358
+ *
359
+ * @param {Object} params
360
+ * @param {string} params.cid - Content identifier of the file to renew
361
+ * @param {number} params.duration - Number of additional days to extend storage
362
+ * @param {PublicKey} params.payer - Wallet public key paying for the renewal
363
+ * @param {(tx: Transaction) => Promise<Transaction>} params.signTransaction - Transaction signing callback
364
+ *
365
+ * @example
366
+ * const { publicKey, signTransaction } = useSolanaWallet();
367
+ * const result = await client.renewStorage({
368
+ * cid: 'bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi',
369
+ * duration: 30,
370
+ * payer: publicKey,
371
+ * signTransaction,
372
+ * });
373
+ *
374
+ * @returns {Promise<UploadResult>} Result of the renewal transaction
375
+ */
376
+ renewStorageDuration({ cid, duration, payer, signTransaction, }: StorageRenewalParams): Promise<UploadResult>;
377
+ /**
378
+ * Gets the current SOL/USD price
379
+ *
380
+ * @example
381
+ * const { price } = await client.getSolPrice();
382
+ * console.log(`SOL price: $${price}`);
383
+ *
384
+ * @returns {Promise<{ price: number }>} Current SOL price in USD
385
+ */
386
+ getSolPrice(): Promise<number>;
387
+ }
388
+
389
+ declare const useUpload: (environment: Environment) => Client;
390
+
391
+ /**
392
+ * Calls the deposit API for on-chain storage and returns a Transaction
393
+ * which must be signed and sent externally by the user.
394
+ *
395
+ * @param params - {
396
+ * cid: string;
397
+ * file: File;
398
+ * duration: number;
399
+ * payer: PublicKey;
400
+ * connection: Connection;
401
+ * }
402
+ * @returns Transaction
403
+ */
404
+ declare function createDepositTxn(args: CreateDepositArgs, apiEndpoint: string): Promise<UploadResult>;
405
+ /**
406
+ * Get cost estimate for renewing storage duration
407
+ *
408
+ * @param {string} cid - Content identifier of the uploaded data to renew
409
+ * @param {number} duration - Number of additional days to extend storage
410
+ *
411
+ * @example
412
+ * const quote = await client.getRenewalQuote('bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi', 30);
413
+ * console.log(`Renewal cost: ${quote.costInSOL} SOL`);
414
+ *
415
+ * @returns {Promise<StorageRenewalCost | null>} Cost breakdown and expiration details
416
+ */
417
+ declare function getStorageRenewalCost(cid: string, duration: number, apiEndpoint: string): Promise<StorageRenewalCost | null>;
418
+ /**
419
+ * Renew storage for an existing deposit
420
+ *
421
+ * @param {Object} params
422
+ * @param {string} params.cid - Content identifier of the uploaded data to renew
423
+ * @param {number} params.duration - Number of additional days to extend storage
424
+ * @param {PublicKey} params.payer - Wallet public key paying for the renewal
425
+ * @param {(tx: Transaction) => Promise<Transaction>} params.signTransaction - Transaction signing callback
426
+ *
427
+ * @example
428
+ * const { publicKey, signTransaction } = useSolanaWallet();
429
+ * const result = await client.renewStorage({
430
+ * cid: 'bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi',
431
+ * additionalDays: 30,
432
+ * payer: publicKey,
433
+ * signTransaction,
434
+ * });
435
+ *
436
+ * @returns {Promise<UploadResult>} Result of the renewal transaction
437
+ */
438
+ declare function renewStorageTxn(args: RenewStorageDurationArgs, apiEndpoint: string): Promise<UploadResult>;
439
+
440
+ /**
441
+ * Get the upload history for a given user address from the server
442
+ *
443
+ * @param userAddress - The wallet address of the user
444
+ * @param apiEndpoint - Base API URL
445
+ * @param options - Optional server configuration and pagination options
446
+ * @returns Promise<UploadHistoryResponse>
447
+ *
448
+ * @example
449
+ * ```ts
450
+ * const history = await getUserUploadHistory(
451
+ * '9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM',
452
+ * API_BASE_URL,
453
+ * { page: 1, limit: 20 }
454
+ * )
455
+ *
456
+ * console.log(history.data)
457
+ * console.log(history.next)
458
+ * ```
459
+ *
460
+ * @throws {Error} When the user address is invalid or the request fails
461
+ */
462
+ declare function getUserUploadHistory(userAddress: string, apiEndpoint: string, options?: ServerOptions & {
463
+ page?: number;
464
+ limit?: number;
465
+ }): Promise<UploadHistoryResponse>;
466
+
467
+ export { Client, type ClientOptions, type CreateDepositArgs, type DepositHistoryEntry, type DepositHistoryResponse, type DepositParams, type DepositResult, Environment, type OnChainConfig, type OnChainDeposit, type PaginationMeta, type RenewStorageDurationArgs, type ServerOptions, type StorageRenewalCost, type StorageRenewalParams, type StorageRenewalResult, type Transaction, type UploadHistory, type UploadHistoryResponse, type UploadOptions, type UploadParams, type UploadResult, type WalletItem, createDepositTxn, getUserUploadHistory as fetchUserDepositHistory, getUserUploadHistory as fetchUserUploadHistory, getRpcUrl, getStorageRenewalCost, getUserUploadHistory, renewStorageTxn, useUpload as useDeposit, useUpload };
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ import{Connection as F}from"@solana/web3.js";var h={local:"http://localhost:5040",staging:"https://staging-api.toju.network",production:"https://api.toju.network"};function D(n){if(typeof window<"u"){let t=window.location.hostname;if(t==="localhost"||t==="127.0.0.1")return h.local}let e=n.toLowerCase();return e.includes("localhost")||e.includes("127.0.0.1")?h.local:e.includes("mainnet")?h.production:e.includes("testnet")||e.includes("devnet")?h.staging:h.production}var C=86400,I=1e9;import{PublicKey as R,SendTransactionError as B,Transaction as x,TransactionInstruction as L}from"@solana/web3.js";async function j(n,e){let{file:t,duration:r,payer:o,connection:i,signTransaction:c,userEmail:l}=n;try{let a=new FormData;t.forEach(s=>a.append("file",s)),a.append("duration",r.toString()),a.append("publicKey",o.toBase58()),l&&a.append("userEmail",l);let u=t.length>1,f,y=await fetch(`${e}/upload/deposit`,{method:"POST",body:a});if(!y.ok)throw new Error("Failed to get deposit instructions");let p=await y.json();if(!p.instructions||!p.instructions.length)throw new Error("No instructions from deposit API");let S=await i.getLatestBlockhash("confirmed"),d=p.instructions[0],g=new L({programId:new R(d.programId),keys:d.keys.map(s=>({pubkey:new R(s.pubkey),isSigner:s.isSigner,isWritable:s.isWritable})),data:Buffer.from(d.data,"base64")}),b=new x;b.recentBlockhash=S.blockhash,b.feePayer=o,b.add(g);let A=await c(b),E;try{E=await i.sendRawTransaction(A.serialize(),{skipPreflight:!1,preflightCommitment:"confirmed"})}catch(s){throw s instanceof B?(s.logs??[]).some(N=>N.includes("already in use"))?new Error("This file has already been uploaded. You can find it in your dashboard."):new Error("Transaction failed during simulation. Please try again."):s}let T=await i.confirmTransaction({signature:E,blockhash:S.blockhash,lastValidBlockHeight:S.lastValidBlockHeight},"confirmed");if(T.value.err)throw console.error("Failed to confirm this transaction:",T.value.err),new Error(`Transaction failed: ${JSON.stringify(T.value.err)}`);try{await fetch(`${e}/upload/confirm`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({cid:p.cid,transactionHash:E})})}catch(s){console.warn("Failed to update transaction hash:",s)}p.error&&(f=p.error);let P=new FormData;t.forEach(s=>P.append("file",s));let w;if(u?w=await fetch(`${e}/upload/files?cid=${encodeURIComponent(p.cid)}`,{method:"POST",body:P}):w=await fetch(`${e}/upload/file?cid=${encodeURIComponent(p.cid)}`,{method:"POST",body:P}),!w.ok){let s="Unknown error";try{let k=await w.json();s=k.message||k.error||s}catch{}throw new Error("Deposit API error: "+s)}let m=await w?.json();return{signature:E,success:!0,cid:p.cid,url:m.object.url,message:m.object.message,fileInfo:m.object?{filename:m.object.fileInfo?.filename||"",size:m?.object?.fileInfo?.size||0,uploadedAt:m?.object?.fileInfo?.uploadedAt||"",type:m?.object?.fileInfo?.type||""}:void 0}}catch(a){return console.error(a),a instanceof Error&&(console.error("Error name:",a.name),console.error("Error message:",a.message),console.error("Error stack:",a.stack)),{signature:"",success:!1,cid:"",url:"",message:"",fileInfo:void 0,error:a instanceof Error?a.message:"Unknown error occurred"}}}async function O(n,e,t){try{let r=await fetch(`${t}/storage/renewal-cost?cid=${encodeURIComponent(n)}&duration=${e}`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!r.ok){let o=await r.json();throw new Error(o.message||"Failed to get storage renewal cost")}return await r.json()}catch(r){return console.error("Failed to get storage renewal cost",r),null}}async function $(n,e){let{cid:t,duration:r,payer:o,connection:i,signTransaction:c}=n,l=await fetch(`${e}/storage/renew`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({cid:t,duration:r,publicKey:o.toString()})});if(!l.ok){let d=await l.json().catch(()=>({}));throw new Error(d.message||"Failed to create renewal transaction")}let a=await l.json(),u=new x;a.instructions.forEach(d=>{u.add({programId:new R(d.programId),keys:d.keys.map(g=>({pubkey:new R(g.pubkey),isSigner:g.isSigner,isWritable:g.isWritable})),data:Buffer.from(d.data,"base64")})});let{blockhash:f}=await i.getLatestBlockhash();u.recentBlockhash=f,u.feePayer=o;let y=await c(u),p=await i.sendRawTransaction(y.serialize());return await i.confirmTransaction(p,"confirmed"),(await fetch(`${e}/storage/confirm-renewal`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({cid:t,duration:r,transactionHash:p})})).ok||console.error("Failed to confirm renewal"),{success:!0,cid:t,signature:p,url:`https://w3s.link/ipfs/${t}`,message:"Storage renewed successfully"}}async function v(n,e,t={}){if(!n||typeof n!="string")throw new Error("User address is required and must be a string");let r=t.url||e,o=t.page??1,i=t.limit??20;try{let c=await fetch(`${r}/upload/history?userAddress=${encodeURIComponent(n)}?userAddress=${encodeURIComponent(n)}&page=${o}&limit=${i}`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!c.ok){let a=await c.json().catch(()=>({}));throw new Error(a.message||`Failed to fetch upload history: ${c.status} ${c.statusText}`)}let l=await c.json();if(typeof l!="object"||l===null||!Array.isArray(l.data)||typeof l!="object")throw new Error("Invalid response format from server");return l}catch(c){throw c instanceof Error?c:new Error("Unknown error occurred while fetching upload history")}}var _=(o=>(o.mainnet="mainnet-beta",o.testnet="testnet",o.devnet="devnet",o.local="localnet",o))(_||{});function H(n){switch(n){case"mainnet-beta":return"https://api.mainnet-beta.solana.com";case"testnet":return"https://api.testnet.solana.com";case"devnet":return"https://api.devnet.solana.com";case"localnet":return"http://localhost:5040";default:throw new Error(`Unsupported environment: ${n}`)}}var U=class{constructor(e){this.rpcUrl=H(e.environment),this.apiEndpoint=D(this.rpcUrl)}async createDeposit({payer:e,file:t,durationDays:r,signTransaction:o,userEmail:i}){console.log("Creating deposit transaction with environment:",this.rpcUrl);let c=new F(this.rpcUrl,"confirmed");return await j({file:t,duration:r*C,payer:e,connection:c,signTransaction:o,userEmail:i},this.apiEndpoint)}async estimateStorageCost(e,t){let r=e.reduce((u,f)=>u+f.size,0),o=Math.floor(t/86400),i=await fetch(`${this.apiEndpoint}/user/get-quote?size=${r}&duration=${o}`);if(!i.ok)throw new Error("Failed to get storage cost estimate");let{quote:c}=await i.json(),l=c.totalCost;return{sol:l/I,lamports:l}}async getUserUploadHistory(e,t,r){return await v(e,this.apiEndpoint,{page:t,limit:r})}async getStorageRenewalCost(e,t){return await O(e,t,this.apiEndpoint)}async renewStorageDuration({cid:e,duration:t,payer:r,signTransaction:o}){let i=new F(this.rpcUrl,"confirmed");return await $({cid:e,duration:t,payer:r,connection:i,signTransaction:o},this.apiEndpoint)}async getSolPrice(){let e=await fetch(`${this.apiEndpoint}/user/sol-price`);if(!e.ok)throw new Error("Couldn't fetch SOL price");return(await e.json()).price}};var te=n=>new U({environment:n||"testnet"});export{U as Client,_ as Environment,j as createDepositTxn,v as fetchUserDepositHistory,v as fetchUserUploadHistory,H as getRpcUrl,O as getStorageRenewalCost,v as getUserUploadHistory,$ as renewStorageTxn,te as useDeposit,te as useUpload};
2
+ //# sourceMappingURL=index.js.map
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@toju.network/sol",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "module": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js",
11
+ "require": "./dist/index.cjs"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "!dist/**/*.js.map"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsup src/index.ts --minify",
20
+ "dev": "pnpm build --watch",
21
+ "pack:local": "pnpm build && pnpm pack",
22
+ "knip": "knip"
23
+ },
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/seetadev/storacha-solana-sdk.git"
27
+ },
28
+ "keywords": [
29
+ "solana",
30
+ "storacha",
31
+ "typescript",
32
+ "web3",
33
+ "ipfs",
34
+ "filecoin",
35
+ "decentralized-storage",
36
+ "blockchain"
37
+ ],
38
+ "author": "toju.network",
39
+ "license": "ISC",
40
+ "description": "Decentralized storage SDK for Solana via Storacha - pay with SOL, store on IPFS",
41
+ "packageManager": "pnpm@10.11.0",
42
+ "dependencies": {
43
+ "@solana/kit": "^2.3.0",
44
+ "@solana/web3.js": "^1.98.2",
45
+ "standard-version": "^9.5.0"
46
+ },
47
+ "devDependencies": {
48
+ "knip": "^5.72.0",
49
+ "tsup": "^8.5.0",
50
+ "typescript": "^5.8.3"
51
+ }
52
+ }