Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: isomorphic Irys uploader #169

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fuzzy-ways-relate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@metaplex-foundation/umi-uploader-irys': minor
---

Create isomorphic exports
15 changes: 15 additions & 0 deletions packages/umi-uploader-irys/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"
```
29 changes: 29 additions & 0 deletions packages/umi-uploader-irys/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand Down
1 change: 1 addition & 0 deletions packages/umi-uploader-irys/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ export default createConfigs({
format: 'cjs',
},
],
input: ["src/index.ts", "src/webIndex.ts"]
});
138 changes: 8 additions & 130 deletions packages/umi-uploader-irys/src/createIrysUploader.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -40,31 +28,11 @@ 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<T>(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;
}

export type IrysUploader = UploaderInterface & {
irys: () => Promise<BaseNodeIrys | BaseWebIrys>;
Expand Down Expand Up @@ -108,7 +76,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<BaseWebIrys | BaseNodeIrys>,
context: Pick<Context, 'rpc' | 'payer' | 'eddsa'>,
uploaderOptions: IrysUploaderOptions = {}
): IrysUploader {
Expand Down Expand Up @@ -279,18 +251,7 @@ 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');

let irys;
if (isNode && isKeypairSigner(payer))
irys = await initNodeIrys(address, payer, irysOptions);
else {
irys = await initWebIrys(address, payer, irysOptions);
}

const irys = await initFn(address, payer, irysOptions, context);
try {
// Check for valid irys node.
await irys.utils.getBundlerAddress(token);
Expand All @@ -301,92 +262,9 @@ export function createIrysUploader(
return irys;
};

const initNodeIrys = async (
address: string,
keypair: Keypair,
options: any
): Promise<BaseNodeIrys> => {
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<BaseWebIrys> => {
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<Web3JsTransactionSignature> => {
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,
Expand Down
2 changes: 1 addition & 1 deletion packages/umi-uploader-irys/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export * from './createIrysUploader';
export * from './errors';
export * from './plugin';
export * from './nodePlugin';
38 changes: 38 additions & 0 deletions packages/umi-uploader-irys/src/nodePlugin.ts
Original file line number Diff line number Diff line change
@@ -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<Context, 'rpc' | 'payer' | 'eddsa'>,
uploaderOptions: IrysUploaderOptions = {}
): IrysUploader {
return CIU(initNodeIrys, context, uploaderOptions)
}


export const initNodeIrys = async (
address: string,
payer: Signer,
options: any
): Promise<BaseNodeIrys> => {
if (isKeypairSigner(payer)) {
return Uploader(Solana)
.bundlerUrl(address)
.withWallet(payer.secretKey)
.withIrysConfig(options)
.build()
}
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?)"))
}

8 changes: 0 additions & 8 deletions packages/umi-uploader-irys/src/plugin.ts

This file was deleted.

3 changes: 3 additions & 0 deletions packages/umi-uploader-irys/src/webIndex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './createIrysUploader';
export * from './errors';
export * from './webPlugin';
Loading
Loading