Typescript SDK
This SDK provides access to a wide range of features allowing you to integrate your game with key blockchain functionality.
- Installation
- Initialization
- List of endpoints that require Secret API Key authorization
- Browser Bundle
- Code Splitting
- Get data (on assets, orders, past transactions, etc.)
- Operations requiring user signatures
- Contract requests
- Smart contract autogeneration
- Metadata refresh
- Further documentation
- Troubleshooting
Installation
Prerequisites
Node Version 20 or later
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
- yarn
npm install -D @imtbl/sdk
# if necessary, install dependencies
npm install -D typescript ts-node
yarn add --dev @imtbl/sdk
# if necessary, install dependencies
yarn add --dev typescript ts-node
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:
- npm
- yarn
rm -Rf node_modules
npm cache clean --force
npm i
rm -Rf node_modules
yarn cache clean
yarn install
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:
The environment
argument can be one of the following:
Environment Configuration | Description |
---|---|
Environment.SANDBOX | The default test network (currently, it is Sepolia) |
Environment.PRODUCTION | The 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?
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
Name | Endpoint | Method |
---|---|---|
Create collection | /v1/collections | POST |
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:
- jsdelivr (https://cdn.jsdelivr.net/npm/@imtbl/sdk)
- unpkg (https://unpkg.com/@imtbl/sdk)
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.66.0), specify it fully.
- If you want a minor version:
https://cdn.jsdelivr.net/npm/@imtbl/sdk@1.66
- 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.66.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.
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 tobundler
- 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:
- Transactions that update blockchain state
- 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
- Installed the Typescript SDK
- Initialized the Typescript SDK
- 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
};
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 }
}
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
}
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
- See the Developer homepage for general information on building on Immutable.
- Build on Immutable zkEVM:
- Build on Immutable X: