├── .env.example
├── .eslintignore
├── .eslintrc.js
├── .github
├── CODEOWNERS
├── issue_template.md
├── pull_request_template.md
└── workflows
│ └── lint.yml
├── .gitignore
├── .prettierrc
├── .vscode
└── settings.json
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── docs
├── img
│ ├── api-key.png
│ └── legacy-id.png
├── onboarding.md
└── public-interfaces.md
├── package-lock.json
├── package.json
├── src
├── config
│ ├── client.ts
│ └── logging.ts
├── libs
│ ├── minting.ts
│ └── utils.ts
├── onboarding
│ ├── 1-user-registration.ts
│ ├── 2-create-project.ts
│ ├── 3-create-collection.ts
│ ├── 4-add-metadata-schema.ts
│ └── 5-mint-nfts.ts
└── public
│ ├── get-assets-info.ts
│ └── get-collection-info.ts
├── tsconfig.eslint.json
└── tsconfig.json
/.env.example:
--------------------------------------------------------------------------------
1 | #=============================================
2 | # General
3 | #=============================================
4 | ALCHEMY_API_KEY=
5 | ETH_NETWORK=sepolia
6 |
7 |
8 | #=============================================
9 | # Onboarding
10 | #=============================================
11 | # Your environment's api key generated from https://hub.immutable.com/
12 | API_KEY=
13 | OWNER_ACCOUNT_PRIVATE_KEY=
14 | COLLECTION_PROJECT_ID=
15 | COLLECTION_CONTRACT_ADDRESS=
16 |
17 |
18 | #=============================================
19 | # Logger
20 | #=============================================
21 | LOG_LEVEL=DEBUG
22 | # Only log from the following components
23 | LOG_COMPONENT_FILTER=
24 | # Comma separated list of log handlers eg. CONSOLE_HUMAN,CONSOLE_JSON,FILE_JSON
25 | LOG_HANDLERS=CONSOLE_HUMAN
26 | LOG_HANDLER_CONSOLE_HUMAN_TYPE=console
27 | LOG_HANDLER_CONSOLE_HUMAN_ENCODER=human
28 | LOG_HANDLER_CONSOLE_JSON_TYPE=console
29 | LOG_HANDLER_CONSOLE_JSON_ENCODER=json
30 | LOG_HANDLER_FILE_JSON_TYPE=file
31 | LOG_HANDLER_FILE_JSON_ENCODER=json
32 | LOG_HANDLER_FILE_JSON_FILENAME=wallet.log
33 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # config files
7 | .eslintrc.js
8 |
9 | # Build folder
10 | dist/
11 |
12 | # Diagnostic reports (https://nodejs.org/api/report.html)
13 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
14 |
15 | # Runtime data
16 | pids
17 | *.pid
18 | *.seed
19 | *.pid.lock
20 |
21 | # Directory for instrumented libs generated by jscoverage/JSCover
22 | lib-cov
23 |
24 | # Coverage directory used by tools like istanbul
25 | coverage
26 | *.lcov
27 |
28 | # nyc test coverage
29 | .nyc_output
30 |
31 | # Package managers:
32 | .grunt
33 | bower_components
34 | node_modules/
35 | jspm_packages/
36 |
37 | # TypeScript cache
38 | *.tsbuildinfo
39 |
40 | # Optional npm cache directory
41 | .npm
42 |
43 | # Optional eslint cache
44 | .eslintcache
45 |
46 | # Output of 'npm pack'
47 | *.tgz
48 |
49 | # dotenv environment variables file
50 | .env
51 | .env.test
52 |
53 | # parcel-bundler cache (https://parceljs.org/)
54 | .cache
55 |
56 | packages/
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | es2021: true,
5 | },
6 | parser: '@typescript-eslint/parser', // Specifies the ESLint parser
7 | parserOptions: {
8 | ecmaVersion: 12,
9 | sourceType: 'module',
10 | project: './tsconfig.eslint.json',
11 | },
12 | plugins: ['@typescript-eslint', 'simple-import-sort', 'import'],
13 | extends: [
14 | 'plugin:@typescript-eslint/recommended',
15 | 'airbnb-typescript/base',
16 | // @NOTE: Make sure this is always the last element in the array.
17 | 'plugin:prettier/recommended',
18 | ],
19 | rules: {
20 | '@typescript-eslint/no-unused-vars': [
21 | 'warn',
22 | {
23 | vars: 'all',
24 | varsIgnorePattern: '^_',
25 | args: 'after-used',
26 | argsIgnorePattern: '^_',
27 | },
28 | ],
29 | '@typescript-eslint/no-namespace': 'off',
30 | '@typescript-eslint/no-explicit-any': 'off',
31 | '@typescript-eslint/explicit-module-boundary-types': 'off',
32 | '@typescript-eslint/ban-ts-comment': 'off',
33 | 'simple-import-sort/imports': 'warn',
34 | 'simple-import-sort/exports': 'error',
35 | 'no-nested-ternary': 'off',
36 | 'no-console': 'off',
37 | 'arrow-body-style': 'off',
38 | 'import/prefer-default-export': 'off',
39 | '@typescript-eslint/no-redeclare': 'off',
40 | '@typescript-eslint/no-use-before-define': 'off',
41 | '@typescript-eslint/no-unused-expressions': 'off',
42 | 'class-methods-use-this': 'off',
43 | 'no-async-promise-executor': 'off',
44 | 'no-restricted-properties': 'off',
45 | 'import/no-cycle': 'off',
46 | },
47 | };
48 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @immutable/sdk
2 |
--------------------------------------------------------------------------------
/.github/issue_template.md:
--------------------------------------------------------------------------------
1 | Issue tracker is **ONLY** used for reporting bugs.
2 |
3 |
4 |
5 | ## Expected Behavior
6 |
7 |
8 | ## Current Behavior
9 |
10 |
11 | ## Possible Solution
12 |
13 |
14 | ## Steps to Reproduce
15 |
16 |
17 | 1.
18 | 2.
19 | 3.
20 | 4.
21 |
22 | ## Context (Environment)
23 |
24 |
25 |
26 |
27 |
28 | ## Detailed Description
29 |
30 |
31 | ## Possible Implementation
32 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | # Summary
2 |
3 |
4 |
5 | # Why the changes
6 |
7 |
8 |
9 | # Things worth calling out
10 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
1 | name: Lint
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 |
9 | jobs:
10 | lint:
11 | name: Lint
12 | runs-on: ubuntu-latest-4-cores
13 | steps:
14 | - name: Checkout
15 | uses: actions/checkout@v3
16 |
17 | - name: Use Node.js
18 | uses: actions/setup-node@v3
19 | with:
20 | node-version: 18
21 |
22 | - name: Install dependencies
23 | run: npm ci
24 |
25 | - name: Lint
26 | run: npm run lint
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | /.env
3 | /.yalc
4 | yalc.lock
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 80,
3 | "tabWidth": 2,
4 | "useTabs": false,
5 | "semi": true,
6 | "singleQuote": true,
7 | "bracketSpacing": true,
8 | "jsxBracketSameLine": false,
9 | "trailingComma": "all",
10 | "arrowParens": "avoid"
11 | }
12 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "typescript.tsdk": "node_modules/typescript/lib"
3 | }
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Immutable X Examples
2 |
3 | Welcome to Immutable X! This repository is designed to empower developers to integrate with Immutable X quickly and effectively.
4 |
5 | This contributing guide will help you get started with the repository and start contributing to the code.
6 |
7 | ---
8 |
9 | ## Table Of Contents
10 |
11 | * [What should you contribute?](#what-should-you-contribute)
12 | * [Prerequisites](#prerequisites)
13 | * [Bootstrap](#bootstrap)
14 | * [Linting](#linting)
15 | * [Building](#building)
16 | * [Publish your changes](#publish-your-changes)
17 | * [Commit Conventions](#commit-conventions)
18 | * [Make a Pull Request](#make-a-pull-request)
19 | * [Merge your Pull Request](#merge-your-pull-request)
20 |
21 | ---
22 |
23 | ## What should you contribute?
24 |
25 | You are welcome to contribute any fix or feature you can think of!. If you would like ideas, however, please refer to `TODO.md`.
26 |
27 | ## Prerequisites
28 |
29 | The following dependencies are required _before_ running the bootstrap procedure outlined below.
30 |
31 | * Node.js >= 12
32 | * NPM >= 7
33 |
34 | ## Bootstrap
35 |
36 | To get started, clone this repo and install all dependencies.
37 |
38 | ```sh
39 | clone git@github.com:immutable/imx-examples.git
40 | cd imx-examples
41 | npm install
42 | ```
43 |
44 | ## Linting
45 |
46 | The linting procedure checks the entire package source code using (in order):
47 | * typescript typecheck
48 | * eslint
49 |
50 | ```sh
51 | npm run lint:eslint:fix
52 | ```
53 |
54 |
55 | ## Building
56 |
57 | The following will build the package for production to the `dist` folder using [tsc](https://www.typescriptlang.org/docs/handbook/compiler-options.html):
58 |
59 | ```sh
60 | npm run build
61 | ```
62 |
63 | ## Publish your changes
64 |
65 | After you have verified your changes locally, you are ready to push your changes to the `remote` repo!
66 |
67 | ### Commit Conventions
68 |
69 | Please follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/#summary) guidelines when creating commits.
70 |
71 | ### Make a Pull Request
72 |
73 | The first step is to create a branch that will be the basis of a pull request. It is recommended you start your branch from an up-to-date version of `main`. However, you can run the following from any branch you may have been developing on:
74 |
75 | ```sh
76 | git checkout -b feat/magic
77 | git pull --rebase origin main
78 | git push origin feat/magic
79 | ```
80 |
81 | Your branches should be named in the following manner:
82 | * `feat/xyz` for new features
83 | * `fix/xyz` for bug fixes
84 |
85 | After pushing your branch, visit https://github.com/immutable/imx-examples and use the GitHub UI to create a pull request.
86 |
87 | ### Pull Request Review
88 |
89 | The maintainers of this repo will attempt to review your pull request when they are available.
90 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
8 |
9 | ---
10 |
11 | # Immutable X Examples
12 |
13 | ## Introduction
14 |
15 | This repository contains example scripts for interacting with Immutable X APIs.
16 |
17 | ## Guides
18 |
19 | This repository is intended to be used alongside the Immutable X [NFT minting tutorial](https://docs.immutable.com/docs/x/zero-to-hero-nft-minting).
20 |
21 | ## Docs
22 |
23 | * [Onboarding (Self Service)](docs/onboarding.md)
24 | * [Public Interfaces](docs/public-interfaces.md)
25 |
26 | ## License
27 |
28 | Immutable Examples repository is distributed under the terms of the [Apache License (Version 2.0)](LICENSE).
29 |
30 | ## Contributing
31 |
32 | Please check [contributing guidelines](CONTRIBUTING.md) to learn how to help this project.
33 |
--------------------------------------------------------------------------------
/docs/img/api-key.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/immutable/imx-examples/3a6480f7094e97a00e66ab6c1548ccea9859733f/docs/img/api-key.png
--------------------------------------------------------------------------------
/docs/img/legacy-id.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/immutable/imx-examples/3a6480f7094e97a00e66ab6c1548ccea9859733f/docs/img/legacy-id.png
--------------------------------------------------------------------------------
/docs/onboarding.md:
--------------------------------------------------------------------------------
1 | # Onboarding (Self-service)
2 |
3 | ## Pre-requisites
4 |
5 | - Node.js v18 (LTS) or later.
6 | - An Alchemy API key. You can create an account and get an API key following this [quickstart guide](https://docs.alchemy.com/docs/alchemy-quickstart-guide).
7 | - A MetaMask wallet with some Sepolia testnet ETH. You can get testnet ETH from the [Etherem Sepolia faucet](https://www.alchemy.com/faucets/ethereum-sepolia).
8 | - Alternatively, you can use the [Sepolia PoW faucet](https://sepolia-faucet.pk910.de/).
9 | - You will need to export the private key of the wallet you want to use for onboarding.
10 | - A collection contract deployed on Immutable X. You can deploy a collection contract following this [guide](https://github.com/immutable/contracts/blob/main/deploy/x/README.md)
11 |
12 | ### Immutable Developer Hub
13 |
14 | Register with your email address at the [Immutable Developer Hub](https://hub.immutable.com) to get access to customized documentation in the hub as well as the ability to create Immutable X and Immutable zkEVM projects on Immutable.
15 |
16 | You must first have a project in order to create collections that you can mint assets from on Immutable (L2).
17 |
18 | For this onboarding guide, we will need to create an **Immutable X** project. Make sure you note down the Legacy ID of the project from the project overview page.
19 |
20 | If you have already created an account, and have a Immutable X project, you can skip this step.
21 |
22 | ## Getting started
23 |
24 | To begin, clone this repository:
25 |
26 | ```sh
27 | git clone https://github.com/immutable/imx-examples.git
28 |
29 | cd imx-examples
30 | ```
31 |
32 | Copy environment file
33 |
34 | ```sh
35 | cp .env.example .env
36 | ```
37 |
38 | Set the environment variables in the `.env` file:
39 |
40 | | Field Name | Description |
41 | | ----------------------------- | ----------- |
42 | | `ALCHEMY_API_KEY` | The Alchemy API key. |
43 | | `API_KEY` | The API key generated from your environment's API Key page in the Immutable Hub. |
44 | | `OWNER_ACCOUNT_PRIVATE_KEY` | Metamask private key. |
45 | | `COLLECTION_PROJECT_ID` | The Legacy ID from your environment's overview page in the Immutable Hub. |
46 | | `COLLECTION_CONTRACT_ADDRESS` | Deployed contract address. |
47 |
48 | Install project dependencies
49 |
50 | ```sh
51 | npm install
52 | ```
53 |
54 | ## 1. Register as a user with Immutable X
55 |
56 | We provide an authentication service to protect your administrative level assets from being accessed or updated by someone else. This is done using a simliar technique as described [here](https://link.medium.com/CVTcj7YfQkb).
57 |
58 | In order to use services like creating a project or collection, you will first need to register as an Immutable X user. This is done by setting up an account using the private key from the wallet account you would like to specify as the owner of the project.
59 |
60 | Run the following script:
61 |
62 | _Requires environment variables `OWNER_ACCOUNT_PRIVATE_KEY` to be set._
63 |
64 | ```sh
65 | npm run onboarding:user-registration
66 | ```
67 |
68 | ## 2. Create project
69 |
70 | Follow the guide at [here](https://docs.immutable.com/docs/x/launch-collection/register-project) to create a project in the [Immutable Hub](https://hub.immutable.com).
71 |
72 | - Ensure you create a project for the **`Immutable X`** rollup.
73 |
74 | ## 3. Create a collection
75 |
76 | A collection refers to a smart contract you have deployed. Minted assets belong to a collection. In order to mint assets on L2
77 | you must first register your collection (smart contract) with Immutable X.
78 |
79 | If you have not deployed a collection contract, you can follow the guide [here](https://github.com/immutable/contracts/blob/main/deploy/x/README.md) to deploy a Asset collection contract.
80 |
81 | Add the collection contract address to the environment variable `COLLECTION_CONTRACT_ADDRESS`.
82 |
83 | Set `COLLECTION_PROJECT_ID` to the legacy ID in your created environment's overview page from step 2.
84 |
85 | 
86 |
87 | Generate an API key in Hub and set the `API_KEY` to the generated value.
88 |
89 | 
90 |
91 | Once updated, run the following script to create your collection:
92 |
93 | _Requires environment variables `OWNER_ACCOUNT_PRIVATE_KEY`, `COLLECTION_PROJECT_ID`, `API_KEY` and `COLLECTION_CONTRACT_ADDRESS` to be set._
94 |
95 | ```sh
96 | npm run onboarding:create-collection
97 | ```
98 |
99 | If you see a `replacement transaction underpriced` error message when trying to run `create-collection` please try again in 5 minutes.
100 |
101 | There is a convenience script to get the info of the newly created collection:
102 |
103 | ```sh
104 | npm run public:get-collection-info
105 | ```
106 |
107 | The collection should also be visible in the [Immutable Hub](https://hub.immutable.com).
108 |
109 | ## 4. Add metadata schema to your collection
110 |
111 | Update the `4-add-metadata-schema.ts` file with the metadata schema values you want to define. The metadata schema is used to define the structure of the metadata that will be associated with the NFTs in your collection.
112 |
113 | Descriptions of the metadata schema fields can be found [here](https://docs.immutable.com/docs/x/launch-collection/register-metadata-schema#metadata-schema).
114 |
115 | Once updated, run the following script to create your collection:
116 |
117 | _Requires environment variables `OWNER_ACCOUNT_PRIVATE_KEY` and `COLLECTION_CONTRACT_ADDRESS` to be set._
118 |
119 | ```sh
120 | npm run onboarding:add-metadata-schema
121 | ```
122 |
123 | If you want to add additional metadata schemas, you can do so by adding more objects to the `metadata` array, and remove any existing metadata schemas from the `metadata` array, and then run the script again.
124 |
125 | If you want to change the properties of an existing field, you can use the `updateMetadataSchemaByName` function instead. There is an example of this in the `4-add-metadata-schema.ts` file.
126 |
127 | ## 5. Mint NFTs
128 |
129 | There is an minting example script, `5-mint-nfts.ts`, which is used to mint example NFTs to a wallet.
130 |
131 | The default behaviour is to mint the NFTs to the wallet that owns the `OWNER_ACCOUNT_PRIVATE_KEY`. If you want to mint the NFTs to a different wallet, you can uncomment and update the `mintRecipient` value passed to the `mint` function in the script.
132 |
133 | Also take a look at the example token objects in `tokens` array and update the blueprint values to your liking.
134 |
135 | For a deeper explanation of the blueprint string, see the [Deep dive into metadata section] in the Immutable X documentation(https://docs.immutable.com/docs/x/deep-dive-metadata#providing-a-blueprint-string-when-token-is-minted-on-l2).
136 |
137 | _Requires environment variables `OWNER_ACCOUNT_PRIVATE_KEY` and `COLLECTION_CONTRACT_ADDRESS` to be set._
138 |
139 | ```sh
140 | npm run onboarding:mint
141 | ```
142 |
143 | Once the NFTs have been minted, you can run the following script to get the info of the minted NFTs in the collection:
144 |
145 | ```sh
146 | npm run public:get-assets-info
147 | ```
148 |
--------------------------------------------------------------------------------
/docs/public-interfaces.md:
--------------------------------------------------------------------------------
1 | # Public interfaces
2 |
3 | These are the requests that can be made by anyone without authentication.
4 |
5 | ## Collections
6 |
7 | Returns the collection information and associated metadata schema.
8 |
9 | _Requires environment variables `COLLECTION_CONTRACT_ADDRESS` to be set._
10 |
11 | ```sh
12 | npm run public:get-collection-info
13 | ```
14 |
15 | ## Assets
16 |
17 | Returns a list of assets in a collection.
18 |
19 | _Requires environment variables `COLLECTION_CONTRACT_ADDRESS` to be set._
20 |
21 | ```sh
22 | npm run public:get-assets-info
23 | ```
24 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@imtbl/imx-example",
3 | "version": "2.0.0",
4 | "repository": "https://github.com/immutable/imx-examples.git",
5 | "description": "Immutable X SDK examples and onboarding scripts",
6 | "author": "Immutable",
7 | "license": "Apache-2.0",
8 | "private": false,
9 | "main": "dist/index.js",
10 | "types": "dist/index.d.ts",
11 | "files": [
12 | "dist/**/*"
13 | ],
14 | "scripts": {
15 | "lint": "npm run typecheck && npm run lint:eslint",
16 | "lint:eslint": "eslint --ext .ts,.js .",
17 | "lint:eslint:fix": "eslint --ext .ts,.js --fix .",
18 | "typecheck": "tsc --noEmit",
19 | "onboarding:user-registration": "ts-node -r dotenv/config ./src/onboarding/1-user-registration.ts",
20 | "onboarding:create-collection": "ts-node -r dotenv/config ./src/onboarding/3-create-collection.ts",
21 | "onboarding:add-metadata-schema": "ts-node -r dotenv/config ./src/onboarding/4-add-metadata-schema.ts",
22 | "onboarding:mint": "ts-node -r dotenv/config ./src/onboarding/5-mint-nfts.ts",
23 | "public:get-collection-info": "ts-node -r dotenv/config ./src/public/get-collection-info.ts",
24 | "public:get-assets-info": "ts-node -r dotenv/config ./src/public/get-assets-info.ts"
25 | },
26 | "engines": {
27 | "npm": ">=9.x.x",
28 | "node": ">=18.x.x"
29 | },
30 | "keywords": [
31 | "Immutable",
32 | "Immutable X",
33 | "IMX"
34 | ],
35 | "dependencies": {
36 | "@ethersproject/providers": "^5.0.7",
37 | "@ethersproject/wallet": "^5.0.7",
38 | "@imtbl/imlogging": "^1.0.33",
39 | "@imtbl/sdk": "1.18.2",
40 | "@types/node": "^20.11.22",
41 | "@typescript-eslint/eslint-plugin": "^7.5.0",
42 | "@typescript-eslint/parser": "^7.5.0",
43 | "axios": "^1.6.7",
44 | "dotenv": "^8.2.0",
45 | "eslint": "^8.57.0",
46 | "eslint-config-airbnb": "^19.0.4",
47 | "eslint-config-airbnb-typescript": "^18.0.0",
48 | "eslint-config-prettier": "^9.1.0",
49 | "eslint-plugin-import": "^2.29.1",
50 | "eslint-plugin-prettier": "^5.1.3",
51 | "eslint-plugin-simple-import-sort": "^12.0.0",
52 | "ts-node": "^10.8.1",
53 | "typescript": "^5.4.3"
54 | },
55 | "publishConfig": {
56 | "access": "public"
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/config/client.ts:
--------------------------------------------------------------------------------
1 | import { getEnv } from '../libs/utils';
2 |
3 | export default {
4 | // general
5 | alchemyApiKey: getEnv('ALCHEMY_API_KEY'),
6 | ethNetwork: getEnv('ETH_NETWORK'),
7 | // Onboarding
8 | ownerAccountPrivateKey: getEnv('OWNER_ACCOUNT_PRIVATE_KEY'),
9 | collectionContractAddress: getEnv('COLLECTION_CONTRACT_ADDRESS'),
10 | collectionProjectId: getEnv('COLLECTION_PROJECT_ID'),
11 | };
12 |
--------------------------------------------------------------------------------
/src/config/logging.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Encoder,
3 | ENCODER_TYPES,
4 | Handler,
5 | ImLoggerConfig,
6 | LOG_LEVELS,
7 | SINK_TYPES,
8 | SinkType,
9 | } from '@imtbl/imlogging';
10 | import * as dotenv from 'dotenv';
11 |
12 | import { getEnv, validateString } from '../libs/utils';
13 |
14 | dotenv.config();
15 |
16 | function getHandlers(): Handler[] {
17 | const names = getEnv('LOG_HANDLERS');
18 |
19 | const handlers: Handler[] = names.split(',').map(name => {
20 | const type = validateString(
21 | getEnv(`LOG_HANDLER_${name}_TYPE`),
22 | SINK_TYPES,
23 | );
24 | const encoder = validateString(
25 | getEnv(`LOG_HANDLER_${name}_ENCODER`),
26 | ENCODER_TYPES,
27 | );
28 | if (type === 'console') {
29 | return {
30 | type,
31 | encoder,
32 | };
33 | }
34 | if (type === 'file') {
35 | const filename = getEnv(`LOG_HANDLER_${name}_FILENAME`);
36 | return {
37 | type,
38 | encoder,
39 | filename,
40 | };
41 | }
42 | throw new Error('Unknown log handler type');
43 | });
44 |
45 | return handlers;
46 | }
47 |
48 | export const loggerConfig: ImLoggerConfig = {
49 | appName: 'examples',
50 | appVersion: 'v1.0.0',
51 | level: validateString(getEnv('LOG_LEVEL'), LOG_LEVELS),
52 | componentFilter: getEnv('LOG_COMPONENT_FILTER', '')
53 | .split(',')
54 | .filter(i => i),
55 | handlers: getHandlers(),
56 | };
57 |
--------------------------------------------------------------------------------
/src/libs/minting.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-param-reassign */
2 | /* eslint-disable no-restricted-syntax */
3 | import { Wallet } from '@ethersproject/wallet';
4 | import { config, x } from '@imtbl/sdk';
5 |
6 | export interface Nft {
7 | id: string;
8 | blueprint: string;
9 | }
10 |
11 | export interface MintParams {
12 | ethSigner: Wallet;
13 | collectionAddress: string;
14 | nfts: Nft[];
15 | mintRecipient?: string;
16 | royaltyRecipient?: string;
17 | royaltyPercentage?: number;
18 | }
19 |
20 | /**
21 | * Helper function to get the next token id for a collection
22 | */
23 | export const nextTokenId = async (
24 | collectionAddress: string,
25 | imxClient: x.IMXClient,
26 | ) => {
27 | let remaining = 0;
28 | let cursor: string | undefined;
29 | let tokenId = 0;
30 |
31 | do {
32 | // eslint-disable-next-line no-await-in-loop
33 | const assets = await imxClient.listAssets({
34 | collection: collectionAddress,
35 | cursor,
36 | });
37 | remaining = assets.remaining;
38 | cursor = assets.cursor;
39 |
40 | for (const asset of assets.result) {
41 | const id = parseInt(asset.token_id, 10);
42 | if (id > tokenId) {
43 | tokenId = id;
44 | }
45 | }
46 | } while (remaining > 0);
47 |
48 | return tokenId + 1;
49 | };
50 |
51 | /**
52 | * Helper function to mint NFTs
53 | */
54 | export const mint = async ({
55 | ethSigner,
56 | collectionAddress,
57 | nfts,
58 | mintRecipient,
59 | royaltyRecipient,
60 | royaltyPercentage = 2,
61 | }: MintParams): Promise => {
62 | const { Environment } = config;
63 | const { IMXClient, imxClientConfig } = x;
64 | const environment = Environment.SANDBOX;
65 | const imxClient = new IMXClient(imxClientConfig({ environment }));
66 |
67 | // handle optional arguments
68 | if (!mintRecipient) {
69 | mintRecipient = ethSigner.address;
70 | }
71 | if (!royaltyRecipient) {
72 | royaltyRecipient = ethSigner.address;
73 | }
74 |
75 | // get next token id
76 | const nextId = await nextTokenId(collectionAddress, imxClient);
77 |
78 | // assign token ids to NFTs
79 | let tempId = nextId;
80 | for (const nft of nfts) {
81 | nft.id = tempId.toString();
82 | tempId += 1;
83 | }
84 |
85 | // form mint request
86 | const unsignedMintRequest: x.UnsignedMintRequest = {
87 | contract_address: collectionAddress,
88 | users: [
89 | {
90 | tokens: nfts,
91 | user: mintRecipient,
92 | },
93 | ],
94 | royalties: [
95 | {
96 | recipient: royaltyRecipient,
97 | percentage: royaltyPercentage,
98 | },
99 | ],
100 | };
101 |
102 | // send mint request
103 | const mintResponse = await imxClient.mint(ethSigner, unsignedMintRequest);
104 | return mintResponse;
105 | };
106 |
--------------------------------------------------------------------------------
/src/libs/utils.ts:
--------------------------------------------------------------------------------
1 | import { AlchemyProvider, JsonRpcProvider } from '@ethersproject/providers';
2 |
3 | export function getEnv(
4 | name: string,
5 | defaultValue: string | undefined = undefined,
6 | ): string {
7 | const value = process.env[name];
8 |
9 | if (value !== undefined) {
10 | return value;
11 | }
12 | if (defaultValue !== undefined) {
13 | return defaultValue;
14 | }
15 | throw new Error(`Environment variable '${name}' not set`);
16 | }
17 |
18 | export function requireEnvironmentVariable(key: string): string {
19 | const value = getEnv(key);
20 | if (!value) {
21 | throw new Error(`Please ensure a value exists for ${key}`);
22 | }
23 | return value;
24 | }
25 |
26 | export function validateString(
27 | val: string,
28 | validValues: readonly string[],
29 | ): T {
30 | const res = validValues.indexOf(val);
31 | if (res < 0) {
32 | throw Error(`${val} is not one of ${validValues}`);
33 | }
34 | return val as T;
35 | }
36 |
37 | export function getProvider(
38 | network: string,
39 | alchemyKey: string,
40 | ): JsonRpcProvider {
41 | if (network !== 'sepolia') {
42 | return new AlchemyProvider(network, alchemyKey);
43 | }
44 |
45 | return new JsonRpcProvider(
46 | `https://eth-sepolia.g.alchemy.com/v2/${alchemyKey}`,
47 | {
48 | name: 'sepolia',
49 | chainId: 11155111,
50 | },
51 | );
52 | }
53 |
--------------------------------------------------------------------------------
/src/onboarding/1-user-registration.ts:
--------------------------------------------------------------------------------
1 | import { Wallet } from '@ethersproject/wallet';
2 | import { ImLogger, WinstonLogger } from '@imtbl/imlogging';
3 | import { config, x } from '@imtbl/sdk';
4 |
5 | import env from '../config/client';
6 | import { loggerConfig } from '../config/logging';
7 | import { getProvider, requireEnvironmentVariable } from '../libs/utils';
8 |
9 | const provider = getProvider(env.ethNetwork, env.alchemyApiKey);
10 | const log: ImLogger = new WinstonLogger(loggerConfig);
11 |
12 | const component = '[IMX-USER-REGISTRATION]';
13 |
14 | (async (): Promise => {
15 | const privateKey = requireEnvironmentVariable('OWNER_ACCOUNT_PRIVATE_KEY');
16 |
17 | const { Environment } = config;
18 | const {
19 | createStarkSigner,
20 | GenericIMXProvider,
21 | IMXClient,
22 | imxClientConfig,
23 | ProviderConfiguration,
24 | generateLegacyStarkPrivateKey,
25 | } = x;
26 |
27 | log.info(component, 'Registering user...');
28 |
29 | const environment = Environment.SANDBOX;
30 | const ethSigner = new Wallet(privateKey).connect(provider);
31 | const starkPrivateKey = await generateLegacyStarkPrivateKey(ethSigner);
32 | const starkSigner = createStarkSigner(starkPrivateKey);
33 |
34 | const imxProviderConfig = new ProviderConfiguration({
35 | baseConfig: {
36 | environment,
37 | },
38 | });
39 | const imxProvider = new GenericIMXProvider(
40 | imxProviderConfig,
41 | ethSigner,
42 | starkSigner,
43 | );
44 | const userAddress = await imxProvider.getAddress();
45 |
46 | const imxClient = new IMXClient(imxClientConfig({ environment }));
47 |
48 | const isRegisteredOffChain = await imxProvider.isRegisteredOffchain();
49 |
50 | if (isRegisteredOffChain) {
51 | const { accounts } = await imxClient.getUser(userAddress);
52 | log.info(component, `User already exists off chain (L2): ${accounts}`);
53 | } else {
54 | await imxProvider.registerOffchain();
55 | const { accounts } = await imxClient.getUser(userAddress);
56 | log.info(component, `User has been created off chain (L2): ${accounts}`);
57 | }
58 |
59 | process.exit(0);
60 | })().catch(e => {
61 | log.error(component, e);
62 | process.exit(1);
63 | });
64 |
--------------------------------------------------------------------------------
/src/onboarding/2-create-project.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This is the second step of the onboarding process.
3 | * Follow the guide at https://docs.immutable.com/docs/x/launch-collection/register-project
4 | * to create a project in the Immutable Hub(https://hub.immutable.com).
5 | *
6 | * Make sure to note down the Legacy ID from your environment's overview page
7 | * in the Immutable Hub.
8 | */
9 |
--------------------------------------------------------------------------------
/src/onboarding/3-create-collection.ts:
--------------------------------------------------------------------------------
1 | import { Wallet } from '@ethersproject/wallet';
2 | import { ImLogger, WinstonLogger } from '@imtbl/imlogging';
3 | import { config, x } from '@imtbl/sdk';
4 |
5 | import env from '../config/client';
6 | import { loggerConfig } from '../config/logging';
7 | import { getProvider, requireEnvironmentVariable } from '../libs/utils';
8 |
9 | const provider = getProvider(env.ethNetwork, env.alchemyApiKey);
10 | const log: ImLogger = new WinstonLogger(loggerConfig);
11 |
12 | const component = '[IMX-CREATE-COLLECTION]';
13 |
14 | (async (): Promise => {
15 | const privateKey = requireEnvironmentVariable('OWNER_ACCOUNT_PRIVATE_KEY');
16 | const collectionContractAddress = requireEnvironmentVariable(
17 | 'COLLECTION_CONTRACT_ADDRESS',
18 | );
19 | const projectId = requireEnvironmentVariable('COLLECTION_PROJECT_ID');
20 | const apiKey = requireEnvironmentVariable('API_KEY');
21 |
22 | const { Environment } = config;
23 | const { IMXClient, imxClientConfig } = x;
24 |
25 | log.info(component, 'Creating collection...', collectionContractAddress);
26 |
27 | const environment = Environment.SANDBOX;
28 | const ethSigner = new Wallet(privateKey).connect(provider);
29 | const imxConfig = imxClientConfig({
30 | environment,
31 | apiKey,
32 | });
33 | const imxClient = new IMXClient(imxConfig);
34 |
35 | const createCollectionRequest: x.CreateCollectionRequest = {
36 | /**
37 | * Edit your values here
38 | */
39 | name: 'ENTER_COLLECTION_NAME',
40 | // description: 'ENTER_COLLECTION_DESCRIPTION (OPTIONAL)',
41 | contract_address: collectionContractAddress.toLowerCase(),
42 | owner_public_key: ethSigner.publicKey,
43 | // icon_url: '',
44 | // metadata_api_url: '',
45 | // collection_image_url: '',
46 | project_id: parseInt(projectId, 10),
47 | };
48 |
49 | const createCollectionResponse = await imxClient.createCollection(
50 | ethSigner,
51 | createCollectionRequest,
52 | );
53 |
54 | log.info(component, 'Created collection');
55 | console.log(JSON.stringify(createCollectionResponse, null, 2));
56 |
57 | process.exit(0);
58 | })().catch(e => {
59 | log.error(component, e);
60 | process.exit(1);
61 | });
62 |
--------------------------------------------------------------------------------
/src/onboarding/4-add-metadata-schema.ts:
--------------------------------------------------------------------------------
1 | import { Wallet } from '@ethersproject/wallet';
2 | import { ImLogger, WinstonLogger } from '@imtbl/imlogging';
3 | import { config, x } from '@imtbl/sdk';
4 |
5 | import env from '../config/client';
6 | import { loggerConfig } from '../config/logging';
7 | import { getProvider, requireEnvironmentVariable } from '../libs/utils';
8 |
9 | const provider = getProvider(env.ethNetwork, env.alchemyApiKey);
10 | const log: ImLogger = new WinstonLogger(loggerConfig);
11 |
12 | const component = '[IMX-ADD-COLLECTION-METADATA-SCHEMA]';
13 |
14 | (async (): Promise => {
15 | const privateKey = requireEnvironmentVariable('OWNER_ACCOUNT_PRIVATE_KEY');
16 | const collectionContractAddress = requireEnvironmentVariable(
17 | 'COLLECTION_CONTRACT_ADDRESS',
18 | );
19 |
20 | const { Environment } = config;
21 | const { IMXClient, imxClientConfig } = x;
22 |
23 | log.info(
24 | component,
25 | `Adding metadata schema to collection ${collectionContractAddress}...`,
26 | collectionContractAddress,
27 | );
28 |
29 | const environment = Environment.SANDBOX;
30 | const ethSigner = new Wallet(privateKey).connect(provider);
31 | const imxConfig = imxClientConfig({ environment });
32 | const imxClient = new IMXClient(imxConfig);
33 |
34 | /**
35 | * Edit your metadata schema values below
36 | * Info about the metadata schema types can be found here:
37 | * https://docs.immutable.com/docs/x/launch-collection/register-metadata-schema#metadata-schema
38 | */
39 | const metadata: x.MetadataSchemaRequest[] = [
40 | {
41 | name: 'name',
42 | type: x.MetadataSchemaRequestTypeEnum.Text,
43 | },
44 | {
45 | name: 'description',
46 | type: x.MetadataSchemaRequestTypeEnum.Text,
47 | },
48 | {
49 | name: 'image_url',
50 | type: x.MetadataSchemaRequestTypeEnum.Text,
51 | },
52 | {
53 | name: 'attack',
54 | type: x.MetadataSchemaRequestTypeEnum.Continuous,
55 | filterable: true,
56 | },
57 | {
58 | name: 'collectable',
59 | type: x.MetadataSchemaRequestTypeEnum.Boolean,
60 | filterable: true,
61 | },
62 | {
63 | name: 'class',
64 | type: x.MetadataSchemaRequestTypeEnum.Enum,
65 | filterable: true,
66 | },
67 | ];
68 |
69 | const params: x.AddMetadataSchemaToCollectionRequest = { metadata };
70 |
71 | const metadataResponse = await imxClient.addMetadataSchemaToCollection(
72 | ethSigner,
73 | collectionContractAddress,
74 | params,
75 | );
76 |
77 | // /**
78 | // * If you want to update a metadata schema by name, use this instead of the method above
79 | // * This will update the metadata schema with the name 'attack' to have the name 'power' instead
80 | // */
81 | // const metadataResponse = await imxClient.updateMetadataSchemaByName(
82 | // ethSigner,
83 | // collectionContractAddress,
84 | // 'attack',
85 | // {
86 | // name: 'power',
87 | // type: x.MetadataSchemaRequestTypeEnum.Discrete,
88 | // filterable: false,
89 | // },
90 | // );
91 |
92 | log.info(component, 'Updated metadata schema', collectionContractAddress);
93 | console.log(JSON.stringify(metadataResponse, null, 2));
94 |
95 | process.exit(0);
96 | })().catch(e => {
97 | log.error(component, e);
98 | process.exit(1);
99 | });
100 |
--------------------------------------------------------------------------------
/src/onboarding/5-mint-nfts.ts:
--------------------------------------------------------------------------------
1 | import { Wallet } from '@ethersproject/wallet';
2 | import { ImLogger, WinstonLogger } from '@imtbl/imlogging';
3 | import { isAxiosError } from 'axios';
4 |
5 | import env from '../config/client';
6 | import { loggerConfig } from '../config/logging';
7 | import { mint, Nft } from '../libs/minting';
8 | import { getProvider, requireEnvironmentVariable } from '../libs/utils';
9 |
10 | const provider = getProvider(env.ethNetwork, env.alchemyApiKey);
11 | const log: ImLogger = new WinstonLogger(loggerConfig);
12 |
13 | const component = '[IMX-MINT-NFT]';
14 |
15 | (async () => {
16 | /**
17 | * Example NFTs to mint - replace with your own
18 | * Note: the `id` value is optional and will be assigned by the mint function
19 | */
20 | const tokens: Nft[] = [
21 | {
22 | id: '0',
23 | blueprint: 'onchain-metadata',
24 | },
25 | {
26 | id: '0',
27 | blueprint: 'onchain-metadata',
28 | },
29 | {
30 | id: '0',
31 | blueprint: 'onchain-metadata',
32 | },
33 | ];
34 |
35 | const nfts: Nft[] = tokens;
36 |
37 | log.info(component, 'Minting NFTs...');
38 |
39 | const privateKey = requireEnvironmentVariable('OWNER_ACCOUNT_PRIVATE_KEY');
40 | const collectionContractAddress = requireEnvironmentVariable(
41 | 'COLLECTION_CONTRACT_ADDRESS',
42 | );
43 |
44 | const ethSigner = new Wallet(privateKey).connect(provider);
45 | const mintResponse = await mint({
46 | ethSigner,
47 | collectionAddress: collectionContractAddress,
48 | nfts,
49 | // mintRecipient: '0x', // Optional: address to mint NFTs to a different address
50 | });
51 |
52 | if (mintResponse.results) {
53 | log.info(
54 | component,
55 | `Successfully minted ${mintResponse.results.length} NFTs`,
56 | );
57 | console.log(mintResponse);
58 | }
59 |
60 | process.exit(0);
61 | })().catch(e => {
62 | if (isAxiosError(e)) {
63 | log.error(component, e.response?.data);
64 | } else {
65 | log.error(component, e);
66 | }
67 | process.exit(1);
68 | });
69 |
--------------------------------------------------------------------------------
/src/public/get-assets-info.ts:
--------------------------------------------------------------------------------
1 | import { ImLogger, WinstonLogger } from '@imtbl/imlogging';
2 | import { config, x } from '@imtbl/sdk';
3 |
4 | import { loggerConfig } from '../config/logging';
5 | import { requireEnvironmentVariable } from '../libs/utils';
6 |
7 | const log: ImLogger = new WinstonLogger(loggerConfig);
8 |
9 | const component = '[IMX-GET-COLLECTION-INFO]';
10 |
11 | (async (): Promise => {
12 | const collectionContractAddress = requireEnvironmentVariable(
13 | 'COLLECTION_CONTRACT_ADDRESS',
14 | );
15 |
16 | const { Environment } = config;
17 | const { IMXClient, imxClientConfig } = x;
18 |
19 | log.info(component, `Fetching asset info...`, collectionContractAddress);
20 |
21 | const environment = Environment.SANDBOX;
22 | const imxConfig = imxClientConfig({ environment });
23 | const imxClient = new IMXClient(imxConfig);
24 |
25 | const assets = await imxClient.listAssets({
26 | collection: collectionContractAddress,
27 | orderBy: 'updated_at',
28 | direction: 'desc',
29 | pageSize: 3,
30 | });
31 |
32 | log.info(
33 | component,
34 | `Fetched assets info with address ${collectionContractAddress}`,
35 | );
36 | console.log(JSON.stringify(assets.result, null, 2));
37 |
38 | process.exit(0);
39 | })().catch(e => {
40 | log.error(component, e);
41 | process.exit(1);
42 | });
43 |
--------------------------------------------------------------------------------
/src/public/get-collection-info.ts:
--------------------------------------------------------------------------------
1 | import { ImLogger, WinstonLogger } from '@imtbl/imlogging';
2 | import { config, x } from '@imtbl/sdk';
3 |
4 | import { loggerConfig } from '../config/logging';
5 | import { requireEnvironmentVariable } from '../libs/utils';
6 |
7 | const log: ImLogger = new WinstonLogger(loggerConfig);
8 |
9 | const component = '[IMX-GET-COLLECTION-INFO]';
10 |
11 | (async (): Promise => {
12 | const collectionContractAddress = requireEnvironmentVariable(
13 | 'COLLECTION_CONTRACT_ADDRESS',
14 | );
15 |
16 | const { Environment } = config;
17 | const { IMXClient, imxClientConfig } = x;
18 |
19 | log.info(component, `Fetching collection info...`, collectionContractAddress);
20 |
21 | const environment = Environment.SANDBOX;
22 | const imxConfig = imxClientConfig({ environment });
23 | const imxClient = new IMXClient(imxConfig);
24 |
25 | const collection = await imxClient.getCollection({
26 | address: collectionContractAddress,
27 | });
28 | const metadataSchema = await imxClient.getMetadataSchema({
29 | address: collectionContractAddress,
30 | });
31 |
32 | log.info(
33 | component,
34 | `Fetched collection info with address ${collectionContractAddress}`,
35 | );
36 | console.group('Collection');
37 | console.log(JSON.stringify(collection, null, 2));
38 | console.groupEnd();
39 | console.group('Metadata Schema');
40 | console.log(JSON.stringify(metadataSchema, null, 2));
41 | console.groupEnd();
42 |
43 | process.exit(0);
44 | })().catch(e => {
45 | log.error(component, e);
46 | process.exit(1);
47 | });
48 |
--------------------------------------------------------------------------------
/tsconfig.eslint.json:
--------------------------------------------------------------------------------
1 | {
2 | // extend your base config so you don't have to redefine your compilerOptions
3 | "extends": "./tsconfig.json",
4 | }
5 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */
4 | /* Basic Options */
5 | // "incremental": true, /* Enable incremental compilation */
6 | "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
7 | "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
8 | // "lib": [], /* Specify library files to be included in the compilation. */
9 | // "allowJs": true, /* Allow javascript files to be compiled. */
10 | // "checkJs": true, /* Report errors in .js files. */
11 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
12 | "declaration": true /* Generates corresponding '.d.ts' file. */,
13 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
14 | // "sourceMap": true, /* Generates corresponding '.map' file. */
15 | // "outFile": "./", /* Concatenate and emit output to single file. */
16 | "outDir": "./dist" /* Redirect output structure to the directory. */,
17 | "rootDir": "./src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */,
18 | // "composite": true, /* Enable project compilation */
19 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
20 | // "removeComments": true, /* Do not emit comments to output. */
21 | // "noEmit": true, /* Do not emit outputs. */
22 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */
23 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
24 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
25 | /* Strict Type-Checking Options */
26 | "strict": true /* Enable all strict type-checking options. */,
27 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
28 | // "strictNullChecks": true, /* Enable strict null checks. */
29 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */
30 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
31 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
32 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
33 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
34 | /* Additional Checks */
35 | // "noUnusedLocals": true, /* Report errors on unused locals. */
36 | // "noUnusedParameters": true, /* Report errors on unused parameters. */
37 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
38 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
39 | /* Module Resolution Options */
40 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
41 | "baseUrl": "src" /* Base directory to resolve non-absolute module names. */,
42 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
43 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
44 | // "typeRoots": [], /* List of folders to include type definitions from. */
45 | // "types": [], /* Type declaration files to be included in compilation. */
46 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
47 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
48 | "resolveJsonModule": true,
49 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
50 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
51 | /* Source Map Options */
52 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
53 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
54 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
55 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
56 | /* Experimental Options */
57 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
58 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
59 | /* Advanced Options */
60 | "skipLibCheck": true /* Skip type checking of declaration files. */,
61 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
62 | }
63 | }
64 |
--------------------------------------------------------------------------------