Closed AlexAnarcho closed 7 months ago
Looks like it's trying to evaluate this dynamic dependency statically, which doesn't apply in your code anyway:
Maybe there is a way to configure excluding web-worker? In webpack, there is the externals
field.
Alternatively maybe the dynamic import could be removed somehow.
Hey woodser, thanks so much for the quick reply!
According to the NextJS documentation webpack can be customized.
So I tried this in my next.config.js
:
webpack: (config, { isServer }) => {
if (!isServer) {
config.resolve.fallback.child_process = false;
config.resolve.fallback.fs = false;
config.externals = {
bufferutil: "bufferutil",
"utf-8-validate": "utf-8-validate",
};
} else {
config.externals = {
"web-worker": "webWorker",
};
}
and this works! In the sense that the errors don't show during development, but the app still hangs. Also with this the entire application just won't build anymore. This is the error on build with the externals
:
> Export encountered errors on following paths:
/_error: /404
/_error: /500
/_not-found
/dashboard/page: /dashboard
/dashboard/tx-history/page: /dashboard/tx-history
/login/page: /login
/page: /
/registration/existing/page: /registration/existing
/registration/new/page: /registration/new
/registration/page: /registration
/registration/username/page: /registration/username
/registration/view-only/page: /registration/view-only
/setup/page: /setup
/streams/page: /streams
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
Could you explain what you mean with your two possible solutions? I think we may be on a good path here, but I just lack the understanding of what is causing the issue.
BTW, here is the entire codebase if you want to check it out https://github.com/tipxmr/tipxmr/commit/8f4dc832839bc90fe83b1be9f1aadeb5bbf74b0e
Option 1 is configure NextJS to work with monero-ts by not statically analyzing this dynamic import.
Option 2 is changing monero-ts somehow to remove the dynamic import so it's not an issue for any build environment.
I'd first start with simple configuration to exclude the import though.
Wondering if
config.externals = {
"web-worker": "webWorker",
};
should be
config.externals = {
"web-worker": "web-worker",
};
?
Not sure without installing the NextJS environment and playing with it.
I created a minimal sample application with NextJS to recreate the problem in the dev server: https://github.com/woodser/xmr-next-app
I can confirm I see the same issue connecting to wallet RPC with monero-ts 0.9.5.
This issue appears to be fixed in monero-ts 0.9.6, so it can connect to wallet RPC. Can you try that version?
However, there's still an error loading the wasm modules: "Failed to parse URL from /Users/woodser/git/xmr-next-app/node_modules/monero-ts/dist/dist/monero_wallet_full.wasm".
So native capabilities aren't available.
Heyo, thank you so much for the support and helping out!
I am also using 0.9.6 now and I think the issue is actually with tRPC, which acts as a api between front-end and back-end. I have the following next.config.js
now:
...
webpack: (config, { isServer }) => {
if (!isServer) {
config.resolve.fallback.child_process = false;
config.resolve.fallback.fs = false;
config.externals = {
bufferutil: "bufferutil",
"utf-8-validate": "utf-8-validate",
};
} else {
config.externals = {
"web-worker": "web-worker",
};
}
return config;
},
...
and the import trace error is gone.
I can also fetch new subaddresses in server-components like so:
export default async function DashboardPage() {
const session = await getServerAuthSession();
if (!session?.user) return <>No session</>;
const mostRecentInvoice = await api.invoice.mostRecentInvoice.query({
streamerId: session.user?.id,
});
const dashboardInfo = await api.streamer.dashboard.query();
const subaddress = (await xmrWallet.createSubaddress(0)).getAddress();
console.log({ subaddress });
return (
...)}
I think that putting the Monero code inside a tRPC procedure is actually causing the issue, especially with the development server. tRPC with monero-ts code (accessing the walletRpc) works when building the application (💡 btw, you have to have the monero-wallet-rpc
running while building).
Pretty much one of the only reasons why I use an external monero-wallet-rpc
in the first place is because I have never really gotten the native monero stuff to work. Buut, it would be much cooler, since as far as I can tell, opening a walletFull in typescript allows for more granular wallet listeners.
Omg, I got it! 🥳
So, in my last post I mentioned that I believe the issue is actually with tRPC. Turns out, that's right! I looked a little closer and welp, in T3 stack projects we have the server/api/trpc.ts
file were we can generate a context for trpc on the server-side. Like, database, session... and moneroWallet! So, here is how it works:
In the file where I write my monero-ts code, at the end I do the following export to create a global variable to work with during development. This is necessary for hot reloading and such.
export const xmrWallet = globalForXmrWallet.xmrWallet ?? (await initWallet());
if (env.NODE_ENV !== "production") globalForXmrWallet.xmrWallet = xmrWallet;
export default xmrWallet;
Then, I can import the xmrWallet
in my server/api/trpc.ts
and pass it in the context:
import xmrWallet from "~/server/xmrWallet";
export const createTRPCContext = async (opts: { headers: Headers }) => {
const session = await getServerAuthSession();
const serverWallet = xmrWallet;
return {
db,
serverWallet,
session,
...opts,
};
};
Now, I can access the serverWallet in my tRPC procedures 😍:
export const invoiceRouter = createTRPCRouter({
create: publicProcedure
.input(
z.object({
streamerId: z.string(),
planType: z.enum(["basic", "premium"]),
}),
)
.mutation(async ({ ctx, input }) => {
const subaddress = (
await ctx.serverWallet.createSubaddress(0)
).getAddress();
console.log({ subaddress });
const data = {
...input,
subaddress,
};
return ctx.db.invoice.create({
data,
});
}),
});
This is sooo dope. Thank you @woodser you are a rockstar!
ah man, i think i celebrated too early... dev server still hangs even with the server wallet in the context..
Heyo! Now I really got it! At least the dev server is working and I can use the serverWallet in trpc.
Here is what I did: I removed the stuff I had adopted earlier from the prisma file with the global export and stuff and just have a initWallet()
function that returns the opened wallet with a listener attached. Then in the trpc
context creation I call that function, get the wallet and store it in the context. This means that I can only use it in trpc procedures, but thats fine since they can be used on the server side as well.
So, just this:
import { initWallet } from "~/server/xmrWallet";
export const createTRPCContext = async (opts: { headers: Headers }) => {
const session = await getServerAuthSession();
// Here we create the wallet file for our server
// The wallet is only an RPC (service runs externally)
// and informs us about new payments
// as well as provides us with new subaddresses for invoices
const serverWallet = await initWallet();
return {
db,
serverWallet,
session,
...opts,
};
};
and
export async function initWallet() {
const walletRpc = await connectToWalletRpc({
uri: env.MONERO_RPC_URI,
rejectUnauthorized: false,
});
const wallet = await walletRpc.openWallet({
path: env.MONERO_WALLET_PATH,
password: env.MONERO_WALLET_PW,
});
await wallet.addListener(
new (class extends MoneroWalletListener {
async onOutputReceived(output: MoneroOutputWallet) {
// --- Parse the transaction
const targetAddressIndex = output.getSubaddressIndex();
const subaddress = (
await wallet.getSubaddress(0, targetAddressIndex)
).getAddress();
const amount = convertBigIntToXmrFloat(output.getAmount());
const transactionKey = output.getKeyImage().getHex();
const isConfirmed = output.getTx().getIsConfirmed();
const isLocked = output.getTx().getIsLocked();
// --- Existing Tx or new?
const existingTx = await db.transaction.findFirst({
where: { transactionKey },
});
const relatedFeedItem = await db.invoice.findFirst({
where: { subaddress },
});
await db.transaction.upsert({
where: { transactionKey: transactionKey },
update: { isConfirmed, isUnlocked: !isLocked },
create: {
transactionKey: transactionKey,
isConfirmed,
isUnlocked: !isLocked,
amount: Number(amount),
},
});
if (existingTx) return;
await updateInvoiceWithTx(relatedFeedItem, amount);
// --- Finishing up
await wallet.save();
}
})(),
);
return wallet;
}
aaaand that works! So yeah, I think I overcomplicated things, but maybe somebody else finds this helpful.
this is the way, and dont use --turbo
next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
webpack: (config, { isServer }) => {
if (isServer) config.externals.push("monero-ts");
return config;
},
};
Heyo, Running
monero-ts v0.9.5
andnextjs v14
leads to a hangup in the dev server. I have amonero-wallet-rpc
running in a separate terminal and connect to it on my NextJS web server. Now, running the dev server and using a server-side function to access the wallet leads to a hangup of the application. No error, the browser window just remains in a perpetual loop state. In the server terminal I see the following output:Now, curious, when building the server with
yarn build
and starting the application withyarn start
works just fine. The server can handle the wallet and does not hang.So my hunch is that it has to do with the way a NextJS dev server works under the hood. Here is the code for my monero-wallet-rpc on the server:
As you can see in the last part, I am doing something that I just copied from the prisma pattern. The
xmrWallet
should kinda be a global on the server, only one instance that holds the open wallet with its functionality.