Introducing golem-js 2.0

The new golem-js beta release has been finalized and released as latest on NPM. In this post, we want to highlight just some of the features and improvements released in this version.

The update of related tooling like the React SDK, our CLI, and create-golem-app will follow shortly after this release.

We also prepared a few additional reference application implementations that will be based on golem-js@2.0, stay tuned for the next updates.

New features

List and accept invoices for work

In some cases, Requestors could fail to accept the invoices from the Providers - especially if they set their budget too low and request too much work on the network.

While the root cause is being dealt with by the Golem Team, we delivered an interim solution within the SDK.

With the SDK You can now programmatically access the list of invoices from your Yagna node and accept them to process the payment:

import { InvoiceProcessor } from "@golem-sdk/golem-js";

const applyAdditionalFilter = (i: any) => i;

const processor = await InvoiceProcessor.create({
  apiKey: "your-app-key-from-Yagna",
});

const invoices = await processor.collectInvoices({
  limit: 10,
  minAmount: 0.001,
  statuses: ["RECEIVED"],
  maxAmount: 10,
  paymentPlatforms: ["erc20-polygon-glm"],
});

const selectedInvoices = applyAdditionalFilter(invoices);

console.log("Matched invoices", selectedInvoices);

const acceptResults = await processor.acceptManyInvoices({
  invoices: selectedInvoices,
});

console.log("Results of accepting invoices", acceptResults);

At the same time, Providers can use this API to browse through the invoices that their node has issued.

Implementing this feature opened the way for a CLI command that helps in browsing and accepting invoices from Providers.

Catch and handle Golem-specific errors

Operating in a decentralized distributed computing space inherently brings a lot of challenges related to error handling for developers.
Widely distributed infrastructure resources with non-deterministic availability characteristics introduce enormous complexity levels that have to be considered.

That's why with this release we introduced a set of custom error types that aim to:

  • give you more information about "What?" and "Where?" went wrong
  • give you the possibility to handle the error in a more clean manner

And here's one way you can make use of the provided error types:

import { GolemPlatformError, TaskExecutor } from "@golem-sdk/golem-js";

const executor = await TaskExecutor.create({
  package: "golem/node:20",
});

try {
  console.log("Scheduling work on Golem");

  const nodeVersion = await executor.run(async (ctx) => {
    // ๐Ÿ†• ๐Ÿคฉ NEW: You can reach provider information (id, name, walletAddress) like that
    console.log("Reading node version on provider", ctx.provider.name);
    const result = await ctx.run("node -v");

    return result.stdout?.toString();
  });

  console.log("Node version on provider", nodeVersion);
} catch (err) {
  // ๐Ÿ†• ๐Ÿคฉ NEW: You can now capture errors from the SDK and handle it as you wish
  if (err instanceof GolemPlatformError) {
    console.log("It seems that there's an internal error", err);
  } else {
    console.error("A generic error happened", err);
  }
} finally {
  // ๐Ÿง  REMEMBER: Always do a clean shutdown :)
  await executor.shutdown();
}

The possible errors include: GolemError, GolemUserError, GolemAbortError, GolemConfigError, GolemInternalError, GolemPlatformError, GolemTimeoutError, GolemModuleError.

Please refer to the API reference to learn more about the semantics of these error types.

Bug-fixes

This release includes numerous bug fixes to improve your experience with Golem Network and the SDK itself. Adopting some of the fixes will require some work at your end, but the changes are worth it.

Deduplication of proposals on the market

That's probably one of the most important changes in this release. Due to the decentralized nature of Golem, each Yagna node can contain multiple offers from the same Provider for the same hardware setup. Previously the SDK attempted to negotiate with the same Provider all the offers that were available from that Provider.

With this release, that's no longer the case. Before the proposal is passed to your proposalFilter, the SDK de-duplicates the proposals from the same provider and provides you with the most attractive one (lowest in price, with the highest validity time of the proposal).

This way we expect to reduce the overall amount of excessive negotiations on the network and supply the Requestors with the best proposals available.

Market proposal filters

  • The market proposal filter function passed to the TaskExecutor should not be an asynchronous one. We expect users to provide filter functions that will return boolean values instead of functions with complex async logic to ensure faster processing of the market proposals.
  • The previous naming scheme and behavior of the proposal filters built into the SDK have been confusing for the users, and we addressed that by rewriting and renaming them.

TaskExecutor stability

  • We fixed the issue that was allowing TaskExecutor to continue operation when there was a bug in the user-provided debit note or invoice filters. This was causing activities to be silently terminated due to failing to accept debit notes on time or producing unpaid invoices. After the change, if any of these filters throws an error when called, the whole TaskExecutor will be canceled to prevent further malfunctioning operations.
  • We fixed the issue when the number of retries for a task sent to the TaskExecutor was not respected due to timeouts. After the change the task is retried the right amount of times, each time having the configured timeout available to finish - timeouts are applied per attempt to execute the task.

These are just a few of the improvements we provided. Feel free to check the release notes to learn more.

Breaking changes

Unfortunately, making things better sometimes means that we need to break compatibility with the old code. Here's the list of the Backward-Compatibility breaking changes made in this release:

General

  • The SDK now requires Yagna at least in version 0.13.2 and will throw an error if you're using a version that's too old.
  • The SDK won't log anything to the console by default and will use debug package to implement logging.
  • The events accessible via EventTarget passed to TaskExecutor are now going to contain a provider: ProviderInfo property that will be enhanced with the walletAddress property. Old providerId and providerName fields are removed from the event.

Task Executor

  • The TaskExecutor will now be canceled when an issue is raised with processing the payments - including errors in the invoice and debit note filters.
  • Deprecated TaskExecutor.isSubprocess option is now removed.
  • Deprecated TaskExecutor.end is now removed (use TaskExecutor.shutdown instead)
  • Deprecated TaskExecutor.beforeEach is now removed (use TaskExecutor.onActivityReady instead)
  • Deprecated WorkContext.rejectResult method is removed.

Market

The following filter re-names were introduced:

  • ProposalFilters was renamed to ProposalFilterFactory.
  • acceptAllProposalFilter was renamed to acceptAll and no longer async.
  • blackListProposalIdsFilter was renamed to disallowProvidersById and no longer async.
  • blackListProposalNamesFilter was renamed disallowProvidersByName and no longer async.
  • blackListProposalRegexpFilter was renamed to disallowProvidersByNameRegex and no longer async.
  • whiteListProposalIdsFilter was renamed to allowProvidersById and no longer async.
  • whiteListProposalNamesFilter was renamed to allowProvidersByName and no longer async.
  • whiteListProposalRegexpFilter was renamed to allowProvidersByNameRegex and no longer async.

Upgrade instructions

In your existing project run:

npm i --save @golem-sdk/golem-js@^2

After the SDK gets updated, take a look at the list of breaking changes mentioned above and in the release notes. Update your code accordingly, by:

  • following the renames of classes and methods
  • stop using removed methods and switch to their appointed replacements