Skip to main content

Typescript SDK


Installation

Prerequisites

Node Version 20 or later
Immutable's Typescript SDK requires **Node v20** (Active LTS version) or **higher**. Node v20 can be installed via `nvm`.

To install nvm follow these instructions. Once installed, run:

nvm install --lts
  • (Optional) To enable Code Splitting (importing only the SDK modules you need) there are additional prerequisites.

Install the Immutable SDK

Run the following command in your project root directory.

npm install -D @imtbl/sdk
# if necessary, install dependencies
npm install -D typescript ts-node
Troubleshooting

The Immutable SDK is still in early development. If experiencing complications, use the following commands to ensure the most recent release of the SDK is correctly installed:

rm -Rf node_modules
npm cache clean --force
npm i

Initialization

Each module of the Immutable SDK must be initialised with an instance of an ImmutableConfiguration. The ImmutableConfiguration defines configuration that is shared across modules, such as the current environment.
An instance of an ImmutableConfiguration can be initialised as follows:

💡Environments

The environment argument can be one of the following:

Environment ConfigurationDescription
Environment.SANDBOXThe default test network (currently, it is Sepolia)
Environment.PRODUCTIONThe Ethereum mainnet network

SDK modules can then be initialised as follows:

import { passport, x } from '@imtbl/sdk';

const passport = new passport.Passport({
baseConfig,
// Passport specific configuration
});

const provider = new x.GenericIMXProvider({
baseConfig,
// Provider specific configuration
});

Using the Secret API Key

What is the Secret API Key for?
Secret API Keys are used to authenticate and authorise your backend integrations with Immutable. Don’t expose this key on a website or embed it in a game client or mobile application.
import { config, blockchainData } from '@imtbl/sdk';

const API_KEY = 'YOUR_API_KEY';
const PUBLISHABLE_KEY = 'YOUR_PUBLISHABLE_KEY';

const client = new blockchainData.BlockchainData({
baseConfig: {
environment: config.Environment.PRODUCTION,
apiKey: API_KEY,
publishableKey: PUBLISHABLE_KEY,
},
});

curl --request POST \
--url https://api.sandbox.immutable.com/v1/chains/imtbl-zkevm-testnet/collections/0x8a90cab2b38dba80c64b7734e58ee1db38b8992e/refresh-metadata \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'x-immutable-api-key: [YOUR_SECRET_API_KEY]' \
--data '{
"collection_metadata": {
"name": "Gigantic Lizards",
"symbol": "GLZ",
"description": "This is the Gigantic Lizards collection",
"image": "https://some-url",
"external_link": "https://some-url",
"contract_uri": "https://some-url",
"base_uri": "https://some-url"
}
}'

List of endpoints that require Secret API Key authorization

NameEndpointMethod
Create collection/v1/collectionsPOST

Learn more about API Keys here.

Browser Bundle

Our SDK is available publicly, and therefore there are a few services that you can use to serve the browser bundle to your webpage:

Both services allow specifying versions of the SDK to use, so if you want a specific version, with levels of specificity. For example:

  • If you want a specific version (e.g. 1.73.0), specify it fully.
  • If you want a minor version:
    • https://cdn.jsdelivr.net/npm/@imtbl/sdk@1.73
  • If you want to support a major version:
    • https://cdn.jsdelivr.net/npm/@imtbl/sdk@1

You can load the script directly in your webpage, like any other script tag, for example:

<script src="https://cdn.jsdelivr.net/npm/@imtbl/sdk@1.73.0

Once loaded, you can access the immutable SDK via the window.immutable object.

If you plan to specify a version, you can check the latest release version on our NPM page.

Browser bundle in console

From there, please look at each product's documentation on how to use each of the available modules.

Code Splitting

The Immutable SDK is designed to be used in a code-splitting environment, such as a web browser.

Your project will need some specific configuration to enable code splitting:

  • Typescript version 5 or higher
  • TS Config needs to have compilerOptions.moduleResolution set to bundler
  • Your code editor and project should have the same version of Typescript
// old import method
import { config, blockchainData } from '@imtbl/sdk';
const env = config.Environment.SANDBOX;
const client = new blockchainData.BlockchainData();

// new import method with code-splitting
import { Environment } from '@imtbl/sdk/config';
// module name is now snake_case
import { BlockchainData } from '@imtbl/sdk/blockchain_data';

Importing modules this way has the added benefit of allowing you to see all the available types within the module root so they can be imported individually.

Get data (on assets, orders, past transactions, etc.)

These methods allow you to read data about events, transactions or current state on Immutable X (layer 2). They do not require any user authentication because no state is being changed.

Examples of the types of data that are typically retrieved include:

  • Assets or details of a particular asset
  • Token balances for a particular user
  • Orders or details about a particular order
  • Historical trades and transfers

Get all collections and get assets from a particular collection:

const listCollectionsResponse = await client.listCollections({
pageSize: 2,
});

const collection = listCollectionsResponse.result[0];

const collectionAssetsResponse = await client.listAssets({
collection: collection.address,
pageSize: 10,
});

Operations requiring user signatures

There are two types of operations requiring user signatures:

  1. Transactions that update blockchain state
  2. Operations that Immutable X require authorization for

In order to get user signatures, applications need to generate "signers".

What are transactions that update blockchain state?

A common transaction type is the transfer of asset ownership from one user to another (ie. asset sale). These operations require users to sign (approve) them to prove that they are valid.

What are operations that require authorization?

These operations add to or update data in Immutable's databases and typically require the authorization of an object's owner (ie. a user creating a project on ImmutableX).

How do applications generate and use signers?

Signers are abstractions of user accounts that can be used to sign transactions. A user's private key is required to generate them.

You can generate your own by obtaining and using the user's private keys

Warning, applications that persist or have access to a user's private key are an exploit target. Design with security in mind.

As Immutable X enables applications to execute signed transactions on both Ethereum (layer 1) and StarkEx (layer 2), signers are required for both these layers.

Generate your own signers

The Typescript SDK provides functionality for applications to generate STARK (L2) private keys.

🚨🚨🚨 Warning 🚨🚨🚨

If you generate your own STARK private key, you will have to persist it. The key is randomly generated so cannot be deterministically re-generated.

import { x, config } from '@imtbl/sdk';
import { Wallet } from '@ethersproject/wallet';
import { AlchemyProvider } from '@ethersproject/providers';

const { Environment, ImmutableConfiguration } = config;

const {
ProviderConfiguration,
createStarkSigner,
GenericIMXProvider,
generateStarkPrivateKey,
} = x;

const ethNetwork = 'sepolia'; // Or 'mainnet'
const alchemyAPIKey = 'YOUR_ALCHEMY_API_KEY';
const ethPrivateKey = 'YOUR_PRIVATE_ETH_KEY';

// Create Ethereum signer
const provider = new AlchemyProvider(ethNetwork, alchemyAPIKey);
const ethSigner = new Wallet(ethPrivateKey).connect(provider);

// Create STARK signer
const starkPrivateKey = generateStarkPrivateKey(); // Or retrieve previously generated key
const starkSigner = createStarkSigner(starkPrivateKey);

// Initialize provider
const environment = Environment.SANDBOX; // or Environment.PRODUCTION
const providerConfig = new ProviderConfiguration({
baseConfig: new ImmutableConfiguration({ environment }),
});
const imxProvider = new GenericIMXProvider(
providerConfig,
ethSigner,
starkSigner
);

Examples

Create a project

To create a project, please visit The Immutable Hub.

Deposit tokens from L1 to L2 (requires an Ethereum layer 1 signer)

const depositResponse = await imxProvider.deposit({
type: 'ETH',
amount: '500000000000000000', // Amount in wei
});

Create an order (requires an Ethereum layer 1 and StarkEx layer 2 signer)

const signers = { ethSigner, starkSigner };

const orderResponse = await imxProvider.createOrder({
buy: {
type: 'ETH',
amount: '1230000000000000000', // Sale price in wei
},
sell: {
type: 'ERC721',
tokenAddress: '0x0fb969a08c7c39ba99c1628b59c0b7e5611bd396',
tokenId: '5',
},
});

Contract requests

See all smart contracts available in the Typescript SDK.

import { x, config } from '@imtbl/sdk';
import { Wallet } from '@ethersproject/wallet';

const { Environment } = config;

const {
Contracts,
IMXClient,
imxClientConfig, // helper method to create a client config
} = x;

// Create Ethereum Signer
const signer = new Wallet('YOUR_PRIVATE_ETH_KEY');

// Initialize client
const environment = Environment.SANDBOX; // or Environment.PRODUCTION
const client = new IMXClient(imxClientConfig({ environment }));
const coreContractAddress =
client.imxConfig.immutableXConfig.ethConfiguration.coreContractAddress;

// Get instance of core contract
const contract = Contracts.Core.connect(coreContractAddress, signer);

// Obtain necessary parameters...

// Populate and send transaction
const populatedTransaction = await contract.populateTransaction.depositNft(
starkPublicKey,
assetType,
vaultId,
tokenId
);

const transactionResponse = await signer.sendTransaction(populatedTransaction);

Smart contract autogeneration

The Immutable Solidity contracts can be found in the contracts folder. Contract bindings in TypeScript are generated using hardhat.

Metadata refresh

Immutable allows developers to request asset metadata refreshes on-demand. This introductory guide shows you how to use the Typescript SDK to request and check for updates on your metadata refreshes.

Pre-requisites

  1. Installed the Typescript SDK
  2. Initialized the Typescript SDK
  3. Have satisified all the metadata refresh requirements

Create Ethereum Signer

First, we need to create an Ethereum Signer to tell Immutable that you are the project_owner of the project containing the collection of your assets:

import { config, x } from '@imtbl/sdk';
import { Wallet } from '@ethersproject/wallet';

const { Environment } = config;

const {
IMXClient,
imxClientConfig, // helper method to create a client config
} = x;

// Create Ethereum Signer
const ethSigner = new Wallet('YOUR_PRIVATE_ETH_KEY');

// Initialize client
const environment = Environment.SANDBOX; // or Environment.PRODUCTION
const client = new IMXClient(imxClientConfig({ environment }));

// Address of the collection the asset was minted under.
// This is the address that was generated when you registered
// your collection with Immutable e.g 0x6e7eaac66499b8733964f24ae4a9d36bf8118dff

const collectionAddress = 'YOUR_COLLECTION_ADDRESS';

Construct the refresh request

In order to construct the refresh request, we need to grab the token ids for the assets that require a metadata refresh:

// Fetch token ids for refresh
const listAssetsResponse = await client.listAssets({
pageSize: 5,
collection: collectionAddress,
});

// Example response
{
result: [
{
token_address: '0x94742ebb6279a3ddb70a1bec53ecd75',
token_id: '5',
id: '0x1111114971ed8cf199c019028dea827bd5f05735111111111',
user: '0xa257d2c65c91d1e111181da9fbafac8a3111111',
status: 'imx',
uri: null,
name: 'Sample NFT',
description: null,
image_url: 'https://www.example.com/some-image/5',
metadata: {
name: 'Some image',
image_url: 'https://www.example.com/some-image/5'
},
collection: {
name: '0x111111bb6279a3bc3e44da9ddb70a1bec111111',
icon_url: 'https://www.example.com/some-icon/5'
},
created_at: '2022-09-30T10:58:32.04664Z',
updated_at: '2022-09-30T11:58:13.85627Z'
},
......
],
cursor: 'eyJpZCI6IjB4NjczZWY3MDI2NDk0NzAzNjA4OTFiZDZiZTdlN2FiZTdkYTgyNzY0MTIyYzVjNTczMTllNTUyMWVkMGRjN2E5YSIsIm5hbWUiOiJMaW5hIiwidXBkYXRlWUiOiJMaW5hIiwidXBkYXR',
remaining: 0
}

const tokenIds: string[] = listAssetsResponse.result.map((asset) => asset.token_id);

const createRefreshRequestParams = {
collection_address: collectionAddress,
token_ids: tokenIds // Token ids for metadata refresh, limit to 1000 per request
};
Customising List Assets

You can narrow down the results returned by listAssets. Please refer to the listAssets request SDK reference.

For more information regarding limits on metadata refreshes, please refer to metadata refresh limits.

Request the refresh

const createMetadataRefreshResponse = await client.createMetadataRefresh(
ethSigner,
createRefreshRequestParams
);
// Example response

{
refresh_id: '8cc5552-6276-4af7-9099-ce4135350e2d';
}

Checking the status of your request

The duration of the refresh depends on the amount of tokens in the request. You can check on the status of your request using the following code:

const refresh_id = createMetadataRefreshResponse.refresh_id;
const getMetadataRefreshResultsResponse = await client.getMetadataRefreshResults(ethSigner, refresh_id);
// Example response
{
refresh_id: '8cc5552-6276-4af7-9099-ce4135350e2d',
status: 'in_progress',
collection_address: '0x6e7eaa111499b876b964f24ae4a9d36bf8228dff',
started_at: '2022-10-20T04:17:51.351675Z',
completed_at: null,
summary: { succeeded: 3, failed: 0, pending: 2 }
}

// Once the metadata refresh request has been completed
{
refresh_id: '8cc5552-6276-4af7-9099-ce4135350e2d',
status: 'completed',
collection_address: '0x6e7eaa111499b876b964f24ae4a9d36bf8228dff',
started_at: '2022-10-20T04:17:51.351675Z',
completed_at: '2022-10-20T04:19:23.240863Z',
summary: { succeeded: 5, failed: 0, pending: 0 }
}
Metadata Refresh Statuses

For more information regarding refresh and summary statuses, please refer to viewing status of a metadata refresh.

Please note, currently there's no webhook or push notification that can notify you about the status of your request, hence, it's up to the developers to keep polling for any updates. The expected maximum time (estimate) to complete a refresh is approximately 48 hours. Updated metadata values will only be reflected in downstream systems e.g. marketplaces once the refresh for that asset is complete.

Checking the status of failed requests

If your requests fails for some reason, you should see the following response:

{
refresh_id: '2a5796a1-2f2f-4443-8142-bfcf0ffcdfcb',
status: 'completed',
collection_address: '0x6e7eaac66111b876b964f24ae4a9d36bf8228dff',
started_at: '2022-10-20T04:23:52.316555Z',
completed_at: '2022-10-20T04:25:26.359759Z',
summary: { succeeded: 0, failed: 5, pending: 0 }
}

To check why your requests failed, you can use the following snippet:

const getMetadataRefreshErrors = await client.getMetadataRefreshErrors(ethSigner, refresh_id);
// Example output
{
result: [
{
token_id: '12',
collection_address: '0x6e7eaa111119b876b964f24ae4a9d36bf8228dff',
client_token_metadata_url: 'https://your-metadata-url.com/12',
client_response_status_code: 400,
client_response_body: "{message: 'Bad request'}",
error_code: 'unable_to_retrieve_metadata',
created_at: '2022-10-20T04:25:26.354327Z'
},
......
],
cursor: 'eyJjcmVhdGVkX2F0IjoiMjAyMi0xMC0yMFQwNDoyNDo1Ny4xMjYxODZaIiwiaWQiOiI2MTZkMTg4MC0zOTZiLTRmMGUtOGZmaaa',
remaining: 0
}
Metadata Refresh Errors

For more information regarding metadata refresh errors and various error codes, please refer to viewing metadata refresh errors.

Viewing all your current and previous refreshes

const listMetadataRefreshesRespose = await client.listMetadataRefreshes(ethSigner, collectionAddress);

// Example response
{
result: [
{
refresh_id: '2a5796a1-1f1f-4443-8142-bfcf0ffcdfcb',
status: 'completed',
collection_address: '0x6e7e111199b876b964f24ae4a9d36bf8228dff',
started_at: '2022-10-20T04:23:52.316555Z',
completed_at: '2022-10-20T04:25:26.359759Z'
},
{
refresh_id: '8cc2c472-6276-4af7-9099-ce4135350e2d',
status: 'completed',
collection_address: '0x6e7e111199b876b964f24ae4a9d36bf8228dff',
started_at: '2022-10-20T04:17:51.351675Z',
completed_at: '2022-10-20T04:19:23.240863Z'
}
],
cursor: 'eyJjcmVhdGVkX2F0IjoiMjAyMi0xMC0yMFQwNDoxNzo1MS4zNTE2NzVaIiwiaWQiOiI4Y2MyYzQ3Mi02Mjc2LTRhZjctOTA5OS1jZ111111',
remaining: 0
}

Further documentation

Troubleshooting