├── package.json ├── config └── default.json ├── LICENSE ├── .gitignore ├── mint.js └── README.md /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xrpl-bulk-minting", 3 | "version": "1.0.0", 4 | "description": "bulk mint nfts on the xrpl", 5 | "main": "mint.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "xrp.cafe", 10 | "license": "ISC", 11 | "dependencies": { 12 | "config": "^3.3.8", 13 | "xrpl": "^2.7.0-beta.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /config/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "XRPL_Server": "wss://s.altnet.rippletest.net:51233/", 3 | "Account" : "rUfQJWAfzMHgtjUrYYEzKTyzZHALhEF1Av", 4 | "Secret_Key" : "", 5 | "Mint": 6 | { 7 | "Base_URI": "ipfs://bafybeifwagnkgmgqnss6uotfrs3qbjdr7weznxds3q7pqorz3k4wrr3cnq/", 8 | "Start_Index": 1, 9 | "Number_Of_Items": 100, 10 | "Royalties": 5, 11 | "Token_Taxon": 0, 12 | "Burnable": false, 13 | "Only_XRP": false, 14 | "Transferable": true, 15 | "Transaction_Memo": "Minted by X" 16 | } 17 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 xrp.cafe 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port -------------------------------------------------------------------------------- /mint.js: -------------------------------------------------------------------------------- 1 | const xrpl = require("xrpl"); 2 | const config = require('config'); 3 | const xrplServer = config.get('XRPL_Server'); 4 | const xrp_account = config.get('Account'); 5 | const xrp_account_secret = config.get('Secret_Key'); 6 | const mint_base_uri = config.get('Mint.Base_URI'); 7 | const mint_number_of_items = config.get('Mint.Number_Of_Items'); 8 | const mint_start_index = config.get('Mint.Start_Index'); 9 | const mint_burnable = config.get('Mint.Burnable'); 10 | const mint_only_xrp = config.get('Mint.Only_XRP'); 11 | const mint_transferable = config.get('Mint.Transferable'); 12 | const flagTotal = (mint_burnable ? 1 : 0) + (mint_only_xrp ? 2 : 0) + (mint_transferable ? 8 : 0) 13 | const royalties = config.get('Mint.Royalties') * 1000; 14 | const token_taxon = config.get('Mint.Token_Taxon'); 15 | const transaction_memo = config.get('Mint.Transaction_Memo'); 16 | 17 | function MintRequest(uri) { 18 | return { 19 | TransactionType: "NFTokenMint", 20 | Account: xrp_account, 21 | Flags: flagTotal, 22 | URI: xrpl.convertStringToHex(uri), 23 | NFTokenTaxon: token_taxon, 24 | TransferFee: royalties, 25 | Memos: [ 26 | { 27 | Memo: { 28 | MemoData: xrpl.convertStringToHex(transaction_memo), 29 | }, 30 | }, 31 | ], 32 | }; 33 | } 34 | 35 | async function main() { 36 | const client = new xrpl.Client(xrplServer) 37 | await client.connect() 38 | const hot_wallet = xrpl.Wallet.fromSeed(xrp_account_secret); 39 | for(let i = mint_start_index; i <= mint_number_of_items; i++) 40 | { 41 | console.log('Currently Minting: '+ mint_base_uri + i.toString() + '.json') 42 | let mintPayload = MintRequest(mint_base_uri + i.toString() + '.json') 43 | 44 | const cst_prepared_offer = await client.autofill(mintPayload) 45 | const ts_signed_offer = hot_wallet.sign(cst_prepared_offer) 46 | const ts_result_offer = await client.submitAndWait(ts_signed_offer.tx_blob) 47 | if (ts_result_offer.result.meta.TransactionResult == "tesSUCCESS") { 48 | console.log('mint success ' + mint_base_uri + i.toString()) 49 | } 50 | } 51 | 52 | await client.disconnect() 53 | console.log('Finished minting!') 54 | return; 55 | } 56 | 57 | main() -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # How to Use 2 | 3 | ## NFT Upload Guide 4 | 5 | Here is how to prepare and upload an NFT collection to IPFS using https://nft.storage 6 | 7 | ## Preparation 8 | Before you start, make sure you have the following: 9 | * A folder with all the images/media. 10 | * A folder with all the metadata files. Each metadata file must be named according to the corresponding media file (e.g. 1.json for 1.png). If you are using GIF, MP3, or MP4, it is recommended to include image thumbnails in the metadata so marketplaces can best support the collection. Thumbnails would require an additional upload of a thumbnails folder. 11 | 12 | We recommend using one of these metadata standards: 13 | * XLS-24: https://github.com/XRPLF/XRPL-Standards/discussions/69 14 | * Opensea: https://docs.opensea.io/docs/metadata-standards 15 | 16 | ## Uploading Your Collection 17 | Before you start, make a backup of your images and metadata folders. Then follow these steps: 18 | 19 | 1. Go to https://nft.storage and create an account. 20 | 2. In the API Keys section, click "New Key" and enter a name for your collection. Click "Create". 21 | 3. Click "Actions" and select "Copy" to copy the API key. 22 | 4. In the "Files" section, click "Upload directories easily with NFTUp". 23 | 5. Download and install NFTUp for your operating system (Windows or Mac). 24 | 6. Open NFTUp and drag and drop the images folder into the upload window. Do not select individual files or subfolders - make sure you are uploading the entire folder. 25 | 7. Paste the API key into the designated field and click "Continue". 26 | 8. Once the upload is complete, copy the CID (Content ID). 27 | 9. Add the CID to all the metadata files. 28 | 10. Test the IPFS links to make sure everything is working, you can test here: https://nftstorage.link 29 | 11. Drag and drop the updated metadata folder into the NFTUp upload window. 30 | 12. Once the metadata upload is complete, copy the IPFS URL. 31 | 32 | ## To mint your NFTs, you will need the following: 33 | * The metadata IPFS URL you copied. 34 | * The Issuer wallet address you want to use for royalties (and secret) (make sure the wallet is on the XUMM app). 35 | * The number of NFTs in your collection (e.g. 100, 1000, 10000). 36 | * The percentage of royalties you want to collect for each sale (suggested ranges: 10k NFTs 2%-8%; below 1k up to 10%; below 100 up to 20%). 37 | * Note: If you are bulk minting a large number of NFTs, make sure you have enough XRP in your issuing/royalties wallet to cover the required reserves. For example, if you are minting 10k NFTs, have at least 550 XRP in the wallet. 38 | 39 | # Minting 40 | 41 | ### Requirements 42 | 43 | + [NodeJs](https://nodejs.org/en/) 44 | + [Git](https://git-scm.com/downloads) 45 | 46 | ## Getting Started 47 | 48 | ### Open a command prompt or Powershell prompt and issue the following commands 49 | 50 | ``` 51 | git clone https://github.com/xrpcafe/xrpl-bulk-minting 52 | ``` 53 | 54 | ### in the ./config directory edit the default.json file with the information about your mint. 55 | ``` 56 | { 57 | "XRPL_Server": "wss://s.altnet.rippletest.net:51233/", 58 | "Account" : "rUfQJWAfzMHgtjUrYYEzKTyzZHALhEF1Av", 59 | "Secret_Key" : "", 60 | "Mint": 61 | { 62 | "Base_URI": "ipfs://bafybeifwagnkgmgqnss6uotfrs3qbjdr7weznxds3q7pqorz3k4wrr3cnq/", 63 | "Start_Index": 1, 64 | "Number_Of_Items": 100, 65 | "Royalties": 5, 66 | "Token_Taxon": 0, 67 | "Burnable": false, 68 | "Only_XRP": false, 69 | "Transferable": true, 70 | "Transaction_Memo": "Minted by X" 71 | } 72 | } 73 | ``` 74 | 1. XRPL_Server: the wss server of the public XRPL endpoint. Testnet: wss://s.altnet.rippletest.net:51233/ Main Net: wss://s1.ripple.com/ wss://xrplcluster.com/ 75 | - Public Main Net servers: [Servers](https://xrpl.org/public-servers.html) 76 | 2. Account: Your xrp account address. [XRPL Faucet](https://xrpl.org/xrp-testnet-faucet.html) 77 | 3. Secret_Key: the family seed for your xrp address. If you activated from XUMM you'll need to convert your secret numbers to a Family seed [https://github.com/WietseWind/secret-numbers-to-family-seed](https://github.com/WietseWind/secret-numbers-to-family-seed) 78 | ```diff 79 | - KEEP THIS SAFE, DO NOT SHARE!!! 80 | ``` 81 | 4. Base_URI: the base url of the location you uploaded your metadata to in IPFS/Arweave or a centralized API. 82 | 5. Start_Index: The number of your first .json file in the IPFS folder you uploaded to. 83 | 6. Number_Of_Items: total number of items you uploaded. If Start_Index = 1 and Number_Of_Items = 100, then the app will iterate by making NFTs from 1.json up to 100.json 84 | 7. Royalties: % of royalties to set each NFT at. ie 5 = 5% 85 | 8. Token_Taxon: the taxon to use to mint this batch of NFTS. Must be an integer. 86 | 9. Burnable: can this NFT be burnt by it's creator. Default is false. 87 | 10. Only_XRP: should this NFT only be transferable/tradeable with XRP only. Default is false. 88 | 11. Transferable: is this NFT transferable. Default is true. 89 | ```diff 90 | - This value must be set to true if you want this NFT to be tradeable on any marketplace 91 | ``` 92 | 12. Transaction_Memo: customizable text to add to the memo field of the transaction. 93 | 94 | ### Install 95 | ``` npm install ``` 96 | 97 | ### Run mint 98 | ``` node mint.js ``` 99 | 100 | 101 | --------------------------------------------------------------------------------