├── .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 |
2 |

3 | 4 | 5 | 6 |

7 |
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 | ![Environment legacy ID](./img/legacy-id.png) 86 | 87 | Generate an API key in Hub and set the `API_KEY` to the generated value. 88 | 89 | ![Api Key](./img/api-key.png) 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 | --------------------------------------------------------------------------------