From e63389066e69c26b60cc4e0e99d761251e2ea426 Mon Sep 17 00:00:00 2001 From: JesseTheRobot Date: Mon, 3 Mar 2025 00:06:01 +0000 Subject: [PATCH 1/4] wip: isomorphic --- packages/umi-uploader-irys/package.json | 29 +++ packages/umi-uploader-irys/rollup.config.js | 1 + .../src/createIrysUploader.ts | 166 ++++-------------- packages/umi-uploader-irys/src/index.ts | 2 +- packages/umi-uploader-irys/src/nodePlugin.ts | 38 ++++ packages/umi-uploader-irys/src/plugin.ts | 8 - packages/umi-uploader-irys/src/webIndex.ts | 3 + packages/umi-uploader-irys/src/webPlugin.ts | 121 +++++++++++++ 8 files changed, 231 insertions(+), 137 deletions(-) create mode 100644 packages/umi-uploader-irys/src/nodePlugin.ts delete mode 100644 packages/umi-uploader-irys/src/plugin.ts create mode 100644 packages/umi-uploader-irys/src/webIndex.ts create mode 100644 packages/umi-uploader-irys/src/webPlugin.ts diff --git a/packages/umi-uploader-irys/package.json b/packages/umi-uploader-irys/package.json index d17aecc1..340bc680 100644 --- a/packages/umi-uploader-irys/package.json +++ b/packages/umi-uploader-irys/package.json @@ -4,14 +4,43 @@ "description": "An uploader implementation relying on Irys", "license": "MIT", "sideEffects": false, + "browser": "dist/esm/webIndex.mjs", "module": "dist/esm/index.mjs", "main": "dist/cjs/index.cjs", "types": "dist/types/index.d.ts", "exports": { ".": { + "types": "./dist/types/index.d.ts", + "import": "./dist/esm/index.mjs", + "require": "./dist/cjs/index.cjs", + "browser": "./dist/esm/webIndex.mjs" + }, + "./web": { + "types": "./dist/types/webIndex.d.ts", + "browser": "./dist/esm/webIndex.mjs", + "import": "./dist/esm/webIndex.mjs", + "require": "./dist/cjs/webIndex.cjs" + }, + "./node": { "types": "./dist/types/index.d.ts", "import": "./dist/esm/index.mjs", "require": "./dist/cjs/index.cjs" + }, + "./plugin": { + "types": "./dist/types/index.d.ts", + "browser": "./dist/esm/pluginWeb.mjs", + "import": "./dist/esm/node.mjs", + "require": "./dist/cjs/node.cjs" + }, + "./plugin/node": { + "types": "./dist/types/index.d.ts", + "import": "./dist/esm/node.mjs", + "require": "./dist/cjs/node.cjs" + }, + "./plugin/web": { + "types": "./dist/types/index.d.ts", + "import": "./dist/esm/web.mjs", + "require": "./dist/cjs/web.cjs" } }, "files": [ diff --git a/packages/umi-uploader-irys/rollup.config.js b/packages/umi-uploader-irys/rollup.config.js index ba38fd10..d0068aba 100644 --- a/packages/umi-uploader-irys/rollup.config.js +++ b/packages/umi-uploader-irys/rollup.config.js @@ -13,4 +13,5 @@ export default createConfigs({ format: 'cjs', }, ], + input: ["src/index.ts", "src/webIndex.ts"] }); diff --git a/packages/umi-uploader-irys/src/createIrysUploader.ts b/packages/umi-uploader-irys/src/createIrysUploader.ts index efc45325..ec98aee5 100644 --- a/packages/umi-uploader-irys/src/createIrysUploader.ts +++ b/packages/umi-uploader-irys/src/createIrysUploader.ts @@ -1,32 +1,20 @@ import type { BaseWebIrys } from '@irys/web-upload/dist/types/base'; import type { BaseNodeIrys } from '@irys/upload/dist/types/base'; import { - Commitment, Context, GenericFile, GenericFileTag, - Keypair, Signer, SolAmount, UploaderInterface, UploaderUploadOptions, - base58, createGenericFileFromJson, - createSignerFromKeypair, - isKeypairSigner, lamports, publicKey, - signTransaction, } from '@metaplex-foundation/umi'; -import { - fromWeb3JsKeypair, - fromWeb3JsLegacyTransaction, - toWeb3JsLegacyTransaction, - toWeb3JsPublicKey, -} from '@metaplex-foundation/umi-web3js-adapters'; + import { Connection as Web3JsConnection, - Keypair as Web3JsKeypair, PublicKey as Web3JsPublicKey, SendOptions as Web3JsSendOptions, Signer as Web3JsSigner, @@ -40,31 +28,31 @@ import { AssetUploadFailedError, IrysWithdrawError, FailedToConnectToIrysAddressError, - FailedToInitializeIrysError, IrysAbortError, } from './errors'; -// PromisePool is a dependency the Irys client already requires, so using it here has no extra cost. -/** - * This method is necessary to import the Irys package on both ESM and CJS modules. - * Without this, we get a different structure on each module: - * - CJS: { default: [Getter], WebIrys: [Getter] } - * - ESM: { default: { default: [Getter], WebIrys: [Getter] } } - * This method fixes this by ensure there is not double default in the imported package. - */ -// eslint-disable-next-line @typescript-eslint/naming-convention -function _removeDoubleDefault(pkg: T): T { - if ( - pkg && - typeof pkg === 'object' && - 'default' in pkg && - 'default' in (pkg as any).default - ) { - return (pkg as any).default; - } +// PromisePool is a dependency the Irys client already requires, so using it here has no extra cost. - return pkg; -} +// /** +// * This method is necessary to import the Irys package on both ESM and CJS modules. +// * Without this, we get a different structure on each module: +// * - CJS: { default: [Getter], WebIrys: [Getter] } +// * - ESM: { default: { default: [Getter], WebIrys: [Getter] } } +// * This method fixes this by ensure there is not double default in the imported package. +// */ +// // eslint-disable-next-line @typescript-eslint/naming-convention +// function _removeDoubleDefault(pkg: T): T { +// if ( +// pkg && +// typeof pkg === 'object' && +// 'default' in pkg && +// 'default' in (pkg as any).default +// ) { +// return (pkg as any).default; +// } + +// return pkg; +// } export type IrysUploader = UploaderInterface & { irys: () => Promise; @@ -108,7 +96,11 @@ const MINIMUM_SIZE = 80_000; const gatewayUrl = (id: string) => `https://gateway.irys.xyz/${id}`; -export function createIrysUploader( +export function createBaseIrysUploader( + initFn: ( address: string, + payer: Signer, + options: any, + context: any) => Promise, context: Pick, uploaderOptions: IrysUploaderOptions = {} ): IrysUploader { @@ -279,18 +271,19 @@ export function createIrysUploader( const payer: Signer = uploaderOptions.payer ?? context.payer; - // If in node use node irys, else use web irys. - const isNode = - // eslint-disable-next-line no-prototype-builtins - typeof window === 'undefined' || window.process?.hasOwnProperty('type'); + // // If in node use node irys, else use web irys. + // const isNode = + // // eslint-disable-next-line no-prototype-builtins + // typeof window === 'undefined' || window.process?.hasOwnProperty('type'); - let irys; - if (isNode && isKeypairSigner(payer)) - irys = await initNodeIrys(address, payer, irysOptions); - else { - irys = await initWebIrys(address, payer, irysOptions); - } + // let irys; + // if (isNode && isKeypairSigner(payer)) + // irys = await initNodeIrys(address, payer, irysOptions); + // else { + // irys = await initWebIrys(address, payer, irysOptions, context); + // } + const irys = await initFn(address, payer, irysOptions, context); try { // Check for valid irys node. await irys.utils.getBundlerAddress(token); @@ -301,92 +294,9 @@ export function createIrysUploader( return irys; }; - const initNodeIrys = async ( - address: string, - keypair: Keypair, - options: any - ): Promise => { - const bPackage = _removeDoubleDefault(await import('@irys/upload')); - const cPackage = _removeDoubleDefault(await import('@irys/upload-solana')); - return bPackage - .Uploader(cPackage.Solana) - .bundlerUrl(address) - .withWallet(keypair.secretKey) - .withIrysConfig(options) - .build(); - }; - const initWebIrys = async ( - address: string, - payer: Signer, - options: any - ): Promise => { - const wallet: IrysWalletAdapter = { - publicKey: toWeb3JsPublicKey(payer.publicKey), - signMessage: (message: Uint8Array) => payer.signMessage(message), - signTransaction: async (web3JsTransaction: Web3JsTransaction) => - toWeb3JsLegacyTransaction( - await payer.signTransaction( - fromWeb3JsLegacyTransaction(web3JsTransaction) - ) - ), - signAllTransactions: async (web3JsTransactions: Web3JsTransaction[]) => { - const transactions = web3JsTransactions.map( - fromWeb3JsLegacyTransaction - ); - const signedTransactions = await payer.signAllTransactions( - transactions - ); - return signedTransactions.map(toWeb3JsLegacyTransaction); - }, - sendTransaction: async ( - web3JsTransaction: Web3JsTransaction, - connection: Web3JsConnection, - options: Web3JsSendOptions & { signers?: Web3JsSigner[] } = {} - ): Promise => { - const { signers: web3JsSigners = [], ...sendOptions } = options; - const signers = web3JsSigners.map((web3JsSigner) => - createSignerFromKeypair( - context, - fromWeb3JsKeypair( - Web3JsKeypair.fromSecretKey(web3JsSigner.secretKey) - ) - ) - ); - - let transaction = fromWeb3JsLegacyTransaction(web3JsTransaction); - transaction = await signTransaction(transaction, [payer, ...signers]); - - const signature = await context.rpc.sendTransaction(transaction, { - ...sendOptions, - preflightCommitment: sendOptions.preflightCommitment as Commitment, - }); - return base58.deserialize(signature)[0]; - }, - }; - - const bPackage = _removeDoubleDefault(await import('@irys/web-upload')); - const cPackage = _removeDoubleDefault( - await import('@irys/web-upload-solana') - ); - - const irys = await bPackage - .WebUploader(cPackage.WebSolana) - .withProvider(wallet) - .bundlerUrl(address) - .withIrysConfig(options) - .build(); - - try { - // Try to initiate irys. - await irys.ready(); - } catch (error) { - throw new FailedToInitializeIrysError(error as Error); - } - return irys; - }; return { getUploadPriceFromBytes, diff --git a/packages/umi-uploader-irys/src/index.ts b/packages/umi-uploader-irys/src/index.ts index 0f1fca39..4e78b856 100644 --- a/packages/umi-uploader-irys/src/index.ts +++ b/packages/umi-uploader-irys/src/index.ts @@ -1,3 +1,3 @@ export * from './createIrysUploader'; export * from './errors'; -export * from './plugin'; +export * from './nodePlugin'; diff --git a/packages/umi-uploader-irys/src/nodePlugin.ts b/packages/umi-uploader-irys/src/nodePlugin.ts new file mode 100644 index 00000000..50657b3e --- /dev/null +++ b/packages/umi-uploader-irys/src/nodePlugin.ts @@ -0,0 +1,38 @@ +import { Uploader } from '@irys/upload'; +import Solana from '@irys/upload-solana'; +import BaseNodeIrys from '@irys/upload/dist/types/base'; +import { isKeypairSigner } from '@metaplex-foundation/umi'; + +import type { Context, Signer, UmiPlugin } from '@metaplex-foundation/umi'; +import { IrysUploaderOptions, createBaseIrysUploader as CIU, IrysUploader } from './createIrysUploader'; +import { FailedToInitializeIrysError } from './errors'; + +export const irysUploader = (options?: IrysUploaderOptions): UmiPlugin => ({ + install(umi) { + umi.uploader = createIrysUploader(umi, options); + }, +}); + +export function createIrysUploader( + context: Pick, + uploaderOptions: IrysUploaderOptions = {} +): IrysUploader { + return CIU(initNodeIrys, context, uploaderOptions) +} + + + export const initNodeIrys = async ( + address: string, + payer: Signer, + options: any + ): Promise => { + if (isKeypairSigner(payer)) { + return Uploader(Solana) + .bundlerUrl(address) + .withWallet(payer.secretKey) + .withIrysConfig(options) + .build() + } + throw new FailedToInitializeIrysError(new Error("expected signer to key a keypair")) + } + \ No newline at end of file diff --git a/packages/umi-uploader-irys/src/plugin.ts b/packages/umi-uploader-irys/src/plugin.ts deleted file mode 100644 index c9980d74..00000000 --- a/packages/umi-uploader-irys/src/plugin.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { UmiPlugin } from '@metaplex-foundation/umi'; -import { IrysUploaderOptions, createIrysUploader } from './createIrysUploader'; - -export const irysUploader = (options?: IrysUploaderOptions): UmiPlugin => ({ - install(umi) { - umi.uploader = createIrysUploader(umi, options); - }, -}); diff --git a/packages/umi-uploader-irys/src/webIndex.ts b/packages/umi-uploader-irys/src/webIndex.ts new file mode 100644 index 00000000..ca63b2d1 --- /dev/null +++ b/packages/umi-uploader-irys/src/webIndex.ts @@ -0,0 +1,3 @@ +export * from './createIrysUploader'; +export * from './errors'; +export * from './webPlugin'; \ No newline at end of file diff --git a/packages/umi-uploader-irys/src/webPlugin.ts b/packages/umi-uploader-irys/src/webPlugin.ts new file mode 100644 index 00000000..488734e0 --- /dev/null +++ b/packages/umi-uploader-irys/src/webPlugin.ts @@ -0,0 +1,121 @@ +import { + Commitment, + Signer, + base58, + createSignerFromKeypair, + signTransaction, + Context, + +} from '@metaplex-foundation/umi'; +import { + fromWeb3JsKeypair, + fromWeb3JsLegacyTransaction, + toWeb3JsLegacyTransaction, + toWeb3JsPublicKey, +} from '@metaplex-foundation/umi-web3js-adapters'; +import { + Connection as Web3JsConnection, + Keypair as Web3JsKeypair, + SendOptions as Web3JsSendOptions, + Signer as Web3JsSigner, + Transaction as Web3JsTransaction, + TransactionSignature as Web3JsTransactionSignature, +} from '@solana/web3.js'; + +import {WebUploader} from "@irys/web-upload" +import {WebSolana} from "@irys/web-upload-solana" +import BaseWebIrys from '@irys/web-upload/dist/types/base'; +import type { UmiPlugin } from '@metaplex-foundation/umi'; +import { IrysWalletAdapter , createBaseIrysUploader as CIU, IrysUploaderOptions, IrysUploader } from './createIrysUploader'; +import { + FailedToInitializeIrysError, +} from './errors'; + + +export const irysUploader = (options?: IrysUploaderOptions): UmiPlugin => ({ + install(umi) { + umi.uploader = createIrysUploader(umi, options); + }, +}); + +export function createIrysUploader( + context: Pick, + uploaderOptions: IrysUploaderOptions = {} +): IrysUploader { + return CIU(initWebIrys, context, uploaderOptions) +} + + +export const initWebIrys = async ( + address: string, + payer: Signer, + options: any, + context: any + ): Promise => { + const wallet: IrysWalletAdapter = { + publicKey: toWeb3JsPublicKey(payer.publicKey), + signMessage: (message: Uint8Array) => payer.signMessage(message), + signTransaction: async (web3JsTransaction: Web3JsTransaction) => + toWeb3JsLegacyTransaction( + await payer.signTransaction( + fromWeb3JsLegacyTransaction(web3JsTransaction) + ) + ), + signAllTransactions: async (web3JsTransactions: Web3JsTransaction[]) => { + const transactions = web3JsTransactions.map( + fromWeb3JsLegacyTransaction + ); + const signedTransactions = await payer.signAllTransactions( + transactions + ); + return signedTransactions.map(toWeb3JsLegacyTransaction); + }, + sendTransaction: async ( + web3JsTransaction: Web3JsTransaction, + connection: Web3JsConnection, + options: Web3JsSendOptions & { signers?: Web3JsSigner[] } = {} + ): Promise => { + const { signers: web3JsSigners = [], ...sendOptions } = options; + const signers = web3JsSigners.map((web3JsSigner) => + createSignerFromKeypair( + context, + fromWeb3JsKeypair( + Web3JsKeypair.fromSecretKey(web3JsSigner.secretKey) + ) + ) + ); + + let transaction = fromWeb3JsLegacyTransaction(web3JsTransaction); + transaction = await signTransaction(transaction, [payer, ...signers]); + + const signature = await context.rpc.sendTransaction(transaction, { + ...sendOptions, + preflightCommitment: sendOptions.preflightCommitment as Commitment, + }); + + return base58.deserialize(signature)[0]; + }, + }; + + // const bPackage = _removeDoubleDefault(await import('@irys/web-upload')); + // const cPackage = _removeDoubleDefault( + // await import('@irys/web-upload-solana') + // ); + + try { + const irys = await WebUploader(WebSolana) + // .WebUploader(cPackage.WebSolana) + .withProvider(wallet) + .bundlerUrl(address) + .withIrysConfig(options) + .build(); + + + // Try to initiate irys. + await irys.ready(); + return irys + } catch (error) { + throw new FailedToInitializeIrysError(error as Error); + } + + }; \ No newline at end of file From 77f971f61d7bf877a2a7d6abe097ed6d249cc1fd Mon Sep 17 00:00:00 2001 From: JesseTheRobot Date: Mon, 3 Mar 2025 09:30:19 +0000 Subject: [PATCH 2/4] chore: tidy up --- .../src/createIrysUploader.ts | 32 ------------------- packages/umi-uploader-irys/src/webPlugin.ts | 13 +------- 2 files changed, 1 insertion(+), 44 deletions(-) diff --git a/packages/umi-uploader-irys/src/createIrysUploader.ts b/packages/umi-uploader-irys/src/createIrysUploader.ts index ec98aee5..c7c84be1 100644 --- a/packages/umi-uploader-irys/src/createIrysUploader.ts +++ b/packages/umi-uploader-irys/src/createIrysUploader.ts @@ -33,26 +33,6 @@ import { // PromisePool is a dependency the Irys client already requires, so using it here has no extra cost. -// /** -// * This method is necessary to import the Irys package on both ESM and CJS modules. -// * Without this, we get a different structure on each module: -// * - CJS: { default: [Getter], WebIrys: [Getter] } -// * - ESM: { default: { default: [Getter], WebIrys: [Getter] } } -// * This method fixes this by ensure there is not double default in the imported package. -// */ -// // eslint-disable-next-line @typescript-eslint/naming-convention -// function _removeDoubleDefault(pkg: T): T { -// if ( -// pkg && -// typeof pkg === 'object' && -// 'default' in pkg && -// 'default' in (pkg as any).default -// ) { -// return (pkg as any).default; -// } - -// return pkg; -// } export type IrysUploader = UploaderInterface & { irys: () => Promise; @@ -271,18 +251,6 @@ export function createBaseIrysUploader( const payer: Signer = uploaderOptions.payer ?? context.payer; - // // If in node use node irys, else use web irys. - // const isNode = - // // eslint-disable-next-line no-prototype-builtins - // typeof window === 'undefined' || window.process?.hasOwnProperty('type'); - - // let irys; - // if (isNode && isKeypairSigner(payer)) - // irys = await initNodeIrys(address, payer, irysOptions); - // else { - // irys = await initWebIrys(address, payer, irysOptions, context); - // } - const irys = await initFn(address, payer, irysOptions, context); try { // Check for valid irys node. diff --git a/packages/umi-uploader-irys/src/webPlugin.ts b/packages/umi-uploader-irys/src/webPlugin.ts index 488734e0..5ebfc91f 100644 --- a/packages/umi-uploader-irys/src/webPlugin.ts +++ b/packages/umi-uploader-irys/src/webPlugin.ts @@ -97,23 +97,12 @@ export const initWebIrys = async ( }, }; - // const bPackage = _removeDoubleDefault(await import('@irys/web-upload')); - // const cPackage = _removeDoubleDefault( - // await import('@irys/web-upload-solana') - // ); - try { - const irys = await WebUploader(WebSolana) - // .WebUploader(cPackage.WebSolana) + return await WebUploader(WebSolana) .withProvider(wallet) .bundlerUrl(address) .withIrysConfig(options) .build(); - - - // Try to initiate irys. - await irys.ready(); - return irys } catch (error) { throw new FailedToInitializeIrysError(error as Error); } From 90f108ef1f667554e6c80a182a7b455e3968716d Mon Sep 17 00:00:00 2001 From: JesseTheRobot Date: Mon, 3 Mar 2025 09:32:18 +0000 Subject: [PATCH 3/4] chore: create changeset --- .changeset/fuzzy-ways-relate.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fuzzy-ways-relate.md diff --git a/.changeset/fuzzy-ways-relate.md b/.changeset/fuzzy-ways-relate.md new file mode 100644 index 00000000..6d2cf4e0 --- /dev/null +++ b/.changeset/fuzzy-ways-relate.md @@ -0,0 +1,5 @@ +--- +'@metaplex-foundation/umi-uploader-irys': minor +--- + +Create isomorphic exports From 07b6009e8a13dcc0885e12c2f196e784b510bf9e Mon Sep 17 00:00:00 2001 From: JesseTheRobot Date: Mon, 3 Mar 2025 09:41:40 +0000 Subject: [PATCH 4/4] feat: update readme & error message --- packages/umi-uploader-irys/README.md | 15 +++++++++++++++ packages/umi-uploader-irys/src/nodePlugin.ts | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/umi-uploader-irys/README.md b/packages/umi-uploader-irys/README.md index c0616c6a..d08a362f 100644 --- a/packages/umi-uploader-irys/README.md +++ b/packages/umi-uploader-irys/README.md @@ -7,3 +7,18 @@ An uploader implementation relying on Irys. ```sh npm install @metaplex-foundation/umi-uploader-irys ``` + +## Fixes +`Module not found: Can't resolve 'fs'` +This is due to the node plugin being imported, instead of the correct web plugin, try: +```diff +- import { irysUploader } from "@metaplex-foundation/umi-uploader-irys" ++ import { irysUploader } from "@metaplex-foundation/umi-uploader-irys/web" +``` + +`Expected signer to key a keypair` +This is due to importing the browser plugin instead of the node plugin, try: +```diff +- import { irysUploader } from "@metaplex-foundation/umi-uploader-irys" ++ import { irysUploader } from "@metaplex-foundation/umi-uploader-irys/node" +``` diff --git a/packages/umi-uploader-irys/src/nodePlugin.ts b/packages/umi-uploader-irys/src/nodePlugin.ts index 50657b3e..df44ad9d 100644 --- a/packages/umi-uploader-irys/src/nodePlugin.ts +++ b/packages/umi-uploader-irys/src/nodePlugin.ts @@ -33,6 +33,6 @@ export function createIrysUploader( .withIrysConfig(options) .build() } - throw new FailedToInitializeIrysError(new Error("expected signer to key a keypair")) + throw new FailedToInitializeIrysError(new Error("Expected signer to be a keypair (try importing @metaplex-foundation/umi-uploader-irys/web to use a browser/external wallet?)")) } \ No newline at end of file