diff --git a/.gitignore b/.gitignore index 4f9cbbe5..c7e231e6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ dist node_modules -.DS_Store \ No newline at end of file +.DS_Store +.idea diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4654f0bf..c3f62862 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,54 +1,64 @@ -lockfileVersion: '6.1' +lockfileVersion: '9.0' settings: autoInstallPeers: true excludeLinksFromLockfile: false -dependencies: - kolorist: - specifier: ^1.8.0 - version: 1.8.0 - prompts: - specifier: ^2.4.2 - version: 2.4.2 - typescript: - specifier: ^4.7.4 - version: 4.7.4 - -devDependencies: - '@types/node': - specifier: ^20.5.0 - version: 20.5.0 +importers: + + .: + dependencies: + kolorist: + specifier: ^1.8.0 + version: 1.8.0 + prompts: + specifier: ^2.4.2 + version: 2.4.2 + typescript: + specifier: ^4.7.4 + version: 4.7.4 + devDependencies: + '@types/node': + specifier: ^20.5.0 + version: 20.5.0 packages: - /@types/node@20.5.0: + '@types/node@20.5.0': resolution: {integrity: sha512-Mgq7eCtoTjT89FqNoTzzXg2XvCi5VMhRV6+I2aYanc6kQCBImeNaAYRs/DyoVqk1YEUJK5gN9VO7HRIdz4Wo3Q==} - dev: true - /kleur@3.0.3: + kleur@3.0.3: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} - dev: false - /kolorist@1.8.0: + kolorist@1.8.0: resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} - dev: false - /prompts@2.4.2: + prompts@2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} - dependencies: - kleur: 3.0.3 - sisteransi: 1.0.5 - dev: false - /sisteransi@1.0.5: + sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - dev: false - /typescript@4.7.4: + typescript@4.7.4: resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==} engines: {node: '>=4.2.0'} hasBin: true - dev: false + +snapshots: + + '@types/node@20.5.0': {} + + kleur@3.0.3: {} + + kolorist@1.8.0: {} + + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + + sisteransi@1.0.5: {} + + typescript@4.7.4: {} diff --git a/templates/nft-template/.gitignore b/templates/nft-template/.gitignore new file mode 100644 index 00000000..f030ad0c --- /dev/null +++ b/templates/nft-template/.gitignore @@ -0,0 +1,28 @@ +# Aptos related files +.aptos +.env + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/templates/nft-template/README.md b/templates/nft-template/README.md new file mode 100644 index 00000000..e69de29b diff --git a/templates/nft-template/contract_address.txt b/templates/nft-template/contract_address.txt new file mode 100644 index 00000000..6f15fa43 --- /dev/null +++ b/templates/nft-template/contract_address.txt @@ -0,0 +1 @@ +0xc9d6f25bb6d465476ebfed667fa465975aedabb9664a26c261637d0d7e2b443a diff --git a/templates/nft-template/frontend/.eslintrc.cjs b/templates/nft-template/frontend/.eslintrc.cjs new file mode 100644 index 00000000..d6c95379 --- /dev/null +++ b/templates/nft-template/frontend/.eslintrc.cjs @@ -0,0 +1,18 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react-hooks/recommended', + ], + ignorePatterns: ['dist', '.eslintrc.cjs'], + parser: '@typescript-eslint/parser', + plugins: ['react-refresh'], + rules: { + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, +} diff --git a/templates/nft-template/frontend/.gitignore b/templates/nft-template/frontend/.gitignore new file mode 100644 index 00000000..a547bf36 --- /dev/null +++ b/templates/nft-template/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/templates/nft-template/frontend/index.html b/templates/nft-template/frontend/index.html new file mode 100644 index 00000000..8c09dfa1 --- /dev/null +++ b/templates/nft-template/frontend/index.html @@ -0,0 +1,13 @@ + + + + + + + Aptos dapp boilerplate + + +
+ + + diff --git a/templates/nft-template/frontend/package.json b/templates/nft-template/frontend/package.json new file mode 100644 index 00000000..64c47c59 --- /dev/null +++ b/templates/nft-template/frontend/package.json @@ -0,0 +1,30 @@ +{ + "name": "frontend", + "version": "0.0.0", + "license": "Apache-2.0", + "dependencies": { + "@aptos-labs/wallet-adapter-ant-design": "^2.3.0", + "@aptos-labs/wallet-adapter-react": "^2.4.0", + "@pontem/wallet-adapter-plugin": "^0.2.0", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview" + }, + "devDependencies": { + "@types/react": "^18.2.15", + "@types/react-dom": "^18.2.7", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "@vitejs/plugin-react": "^4.0.3", + "eslint": "^8.45.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.3", + "typescript": "^5.0.2", + "vite": "^4.4.5" + } +} diff --git a/templates/nft-template/frontend/public/aptos.png b/templates/nft-template/frontend/public/aptos.png new file mode 100644 index 00000000..5aa7e657 Binary files /dev/null and b/templates/nft-template/frontend/public/aptos.png differ diff --git a/templates/nft-template/frontend/src/App.tsx b/templates/nft-template/frontend/src/App.tsx new file mode 100644 index 00000000..2dc50b05 --- /dev/null +++ b/templates/nft-template/frontend/src/App.tsx @@ -0,0 +1,21 @@ +import { WalletSelector } from "@aptos-labs/wallet-adapter-ant-design"; +import "@aptos-labs/wallet-adapter-ant-design/dist/index.css"; +import logo from "./assets/aptos.png"; + +function App() { + return ( + <> +
+
Create Aptos Dapp
+
+ +
+
+
+ aptos +
+ + ); +} + +export default App; diff --git a/templates/nft-template/frontend/src/abi.ts b/templates/nft-template/frontend/src/abi.ts new file mode 100644 index 00000000..2593fa5d --- /dev/null +++ b/templates/nft-template/frontend/src/abi.ts @@ -0,0 +1 @@ +export const ABI = {"address":"0x8f838ee41b0cc142e46dfeac5a68e50be1488594b91ca8eefd17682e8e86c3f2","name":"todolist","friends":[],"exposed_functions":[{"name":"complete_task","visibility":"public","is_entry":true,"is_view":false,"generic_type_params":[],"params":["&signer","u64"],"return":[]},{"name":"create_list","visibility":"public","is_entry":true,"is_view":false,"generic_type_params":[],"params":["&signer"],"return":[]},{"name":"create_task","visibility":"public","is_entry":true,"is_view":false,"generic_type_params":[],"params":["&signer","0x1::string::String"],"return":[]}],"structs":[{"name":"Task","is_native":false,"abilities":["copy","drop","store"],"generic_type_params":[],"fields":[{"name":"address","type":"address"},{"name":"content","type":"0x1::string::String"},{"name":"completed","type":"bool"}]},{"name":"TodoList","is_native":false,"abilities":["key"],"generic_type_params":[],"fields":[{"name":"tasks","type":"vector<0x8f838ee41b0cc142e46dfeac5a68e50be1488594b91ca8eefd17682e8e86c3f2::todolist::Task>"},{"name":"set_task_event","type":"0x1::event::EventHandle<0x8f838ee41b0cc142e46dfeac5a68e50be1488594b91ca8eefd17682e8e86c3f2::todolist::Task>"}]}]} as const diff --git a/templates/nft-template/frontend/src/assets/aptos.png b/templates/nft-template/frontend/src/assets/aptos.png new file mode 100644 index 00000000..5aa7e657 Binary files /dev/null and b/templates/nft-template/frontend/src/assets/aptos.png differ diff --git a/templates/nft-template/frontend/src/index.css b/templates/nft-template/frontend/src/index.css new file mode 100644 index 00000000..bb0acd06 --- /dev/null +++ b/templates/nft-template/frontend/src/index.css @@ -0,0 +1,38 @@ +:root { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} +body { + margin: 0; +} +.navbar { + display: flex; + justify-content: space-between; + align-items: center; + background-color: #333; + color: #fff; + padding: 10px 20px; +} + +.navbar-text { + font-size: 20px; +} + +.center-container { + display: flex; + justify-content: center; + align-items: center; + height: 100vh; /* Adjust this to control vertical centering */ +} + +.center-image { + max-width: 100%; + max-height: 100%; +} diff --git a/templates/nft-template/frontend/src/main.tsx b/templates/nft-template/frontend/src/main.tsx new file mode 100644 index 00000000..aaa14862 --- /dev/null +++ b/templates/nft-template/frontend/src/main.tsx @@ -0,0 +1,28 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; +// wallet adapter +import { AptosWalletAdapterProvider } from "@aptos-labs/wallet-adapter-react"; +// wallets +import { PontemWallet } from "@pontem/wallet-adapter-plugin"; + +import App from "./App"; +import "./index.css"; + +const wallets = [new PontemWallet()]; + +const root = ReactDOM.createRoot( + document.getElementById("root") as HTMLElement +); +root.render( + + { + console.log("Custom error handling", error); + }} + > + + + +); diff --git a/templates/nft-template/frontend/src/vite-env.d.ts b/templates/nft-template/frontend/src/vite-env.d.ts new file mode 100644 index 00000000..11f02fe2 --- /dev/null +++ b/templates/nft-template/frontend/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/templates/nft-template/frontend/tsconfig.json b/templates/nft-template/frontend/tsconfig.json new file mode 100644 index 00000000..a7fc6fbf --- /dev/null +++ b/templates/nft-template/frontend/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/templates/nft-template/frontend/tsconfig.node.json b/templates/nft-template/frontend/tsconfig.node.json new file mode 100644 index 00000000..42872c59 --- /dev/null +++ b/templates/nft-template/frontend/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/templates/nft-template/frontend/vite.config.ts b/templates/nft-template/frontend/vite.config.ts new file mode 100644 index 00000000..fa25f4fe --- /dev/null +++ b/templates/nft-template/frontend/vite.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; + +// https://vitejs.dev/config/ +export default defineConfig({ + build: { + outDir: "build", + }, + server: { + open: true, + }, + plugins: [react()], +}); diff --git a/templates/nft-template/move/.gitignore b/templates/nft-template/move/.gitignore new file mode 100644 index 00000000..f930c403 --- /dev/null +++ b/templates/nft-template/move/.gitignore @@ -0,0 +1,2 @@ +build/ +.aptos \ No newline at end of file diff --git a/templates/nft-template/move/Move.toml b/templates/nft-template/move/Move.toml new file mode 100644 index 00000000..7f7a0870 --- /dev/null +++ b/templates/nft-template/move/Move.toml @@ -0,0 +1,19 @@ +[package] +name = "launchpad" +version = "1.0.0" +authors = [] + +[addresses] +launchpad_addr = "_" +minter = "_" + +[dev-addresses] +launchpad_addr = "0x100" +minter = "0x101" + +[dependencies] +AptosFramework = { git = "https://github.com/aptos-labs/aptos-core.git", rev = "mainnet", subdir = "aptos-move/framework/aptos-framework"} +AptosTokenObjects = { git = "https://github.com/aptos-labs/aptos-core.git", rev = "mainnet", subdir = "aptos-move/framework/aptos-token-objects"} +TokenMinter = { git = "https://github.com/aptos-labs/token-minter.git", rev = "main", subdir = "token-minter"} + +[dev-dependencies] diff --git a/templates/nft-template/move/sources/nft_launchpad.move b/templates/nft-template/move/sources/nft_launchpad.move new file mode 100644 index 00000000..9adc05cb --- /dev/null +++ b/templates/nft-template/move/sources/nft_launchpad.move @@ -0,0 +1,433 @@ +module launchpad_addr::nft_launchpad { + use std::option::{Self, Option}; + use std::signer; + use std::string::{Self, String}; + use std::vector; + + use aptos_std::simple_map::{Self, SimpleMap}; + use aptos_std::string_utils; + + use aptos_framework::aptos_account; + use aptos_framework::event; + use aptos_framework::object::{Self, Object}; + + use aptos_token_objects::collection::{Self, Collection}; + use aptos_token_objects::royalty::{Self, Royalty}; + use aptos_token_objects::token::{Self, Token}; + + use minter::token_components; + use minter::mint_stage; + use minter::collection_components; + + /// Sender is not admin + const ENOT_ADMIN: u64 = 1; + /// Sender is not pending admin + const ENOT_PENDING_ADMIN: u64 = 2; + /// Only admin can update mint fee collector + const EONLY_ADMIN_CAN_UPDATE_MINT_FEE_COLLECTOR: u64 = 3; + /// Only admin can create collection + const EONLY_ADMIN_CAN_CREATE_COLLECTION: u64 = 4; + /// No active mint stages + const E_NO_ACTIVE_STAGES: u64 = 5; + + const ALLOWLIST_MINT_STAGE_CATEGORY: vector = b"Allowlist mint stage"; + const PUBLIC_MINT_MINT_STAGE_CATEGORY: vector = b"Public mint mint stage"; + + #[event] + struct CreateCollectionEvent has store, drop { + creator_addr: address, + collection_owner_obj: Object, + collection_obj: Object, + max_supply: Option, + name: String, + uri: String, + pre_mint_amount: u64, + allowlist: Option>, + allowlist_start_time: Option, + allowlist_end_time: Option, + allowlist_mint_limit_per_addr: Option, + allowlist_mint_fee_per_nft: Option, + public_mint_start_time: Option, + public_mint_end_time: Option, + public_mint_limit_per_addr: Option, + public_mint_fee_per_nft: Option, + } + + #[event] + struct MintNftEvent has store, drop { + collection_obj: Object, + nft_obj: Object, + recipient_addr: address, + mint_fee: u64, + } + + /// Unique per collection + /// We need this object to own the collection object instead of contract directly owns the collection object + /// This helps us avoid address collision when we create multiple collections with same name + struct CollectionOwnerObjConfig has key { + /// Only thing it stores is the link to collection object + collection_obj: Object, + extend_ref: object::ExtendRef, + } + + /// Unique per collection + struct CollectionConfig has key { + /// Key is stage, value is mint fee denomination + mint_fee_per_nft_by_stages: SimpleMap, + collection_owner_obj: Object, + } + + /// Global per contract + struct Registry has key { + collection_objects: vector> + } + + /// Global per contract + struct Config has key { + admin_addr: address, + pending_admin_addr: Option
, + mint_fee_collector_addr: address, + } + + // If you deploy the module under an object, sender is the object's signer + // If you deploy the module under your own account, sender is your account's signer + fun init_module(sender: &signer) { + move_to(sender, Registry { + collection_objects: vector::empty() + }); + move_to(sender, Config { + admin_addr: signer::address_of(sender), + pending_admin_addr: option::none(), + mint_fee_collector_addr: signer::address_of(sender), + }); + } + + // ================================= Entry Functions ================================= // + + public entry fun set_pending_admin(sender: &signer, new_admin: address) acquires Config { + let sender_addr = signer::address_of(sender); + let config = borrow_global_mut(@launchpad_addr); + assert!(is_admin(config, sender_addr), ENOT_ADMIN); + config.pending_admin_addr = option::some(new_admin); + } + + public entry fun accept_admin(sender: &signer) acquires Config { + let sender_addr = signer::address_of(sender); + let config = borrow_global_mut(@launchpad_addr); + assert!(is_pending_admin(config, sender_addr), ENOT_PENDING_ADMIN); + config.admin_addr = sender_addr; + config.pending_admin_addr = option::none(); + } + + public entry fun update_mint_fee_collector(sender: &signer, new_mint_fee_collector: address) acquires Config { + let sender_addr = signer::address_of(sender); + let config = borrow_global_mut(@launchpad_addr); + assert!(is_admin(config, sender_addr), EONLY_ADMIN_CAN_UPDATE_MINT_FEE_COLLECTOR); + config.mint_fee_collector_addr = new_mint_fee_collector; + } + + public entry fun create_collection( + sender: &signer, + description: String, + name: String, + uri: String, + max_supply: Option, + royalty_percentage: Option, + pre_mint_amount: u64, + allowlist: Option>, + allowlist_start_time: Option, + allowlist_end_time: Option, + allowlist_mint_limit_per_addr: Option, + allowlist_mint_fee_per_nft: Option, + public_mint_start_time: Option, + public_mint_end_time: Option, + public_mint_limit_per_addr: Option, + public_mint_fee_per_nft: Option, + ) acquires Registry, Config, CollectionConfig, CollectionOwnerObjConfig { + let sender_addr = signer::address_of(sender); + let config = borrow_global(@launchpad_addr); + assert!(is_admin(config, sender_addr), EONLY_ADMIN_CAN_CREATE_COLLECTION); + + let royalty = royalty(&mut royalty_percentage, sender_addr); + + let collection_owner_obj_constructor_ref = &object::create_object(@launchpad_addr); + let collection_owner_obj_signer = &object::generate_signer(collection_owner_obj_constructor_ref); + + let collection_obj_constructor_ref = if (option::is_some(&max_supply)) { + &collection::create_fixed_collection( + collection_owner_obj_signer, + description, + option::extract(&mut max_supply), + name, + royalty, + uri, + ) + } else { + &collection::create_unlimited_collection( + collection_owner_obj_signer, + description, + name, + royalty, + uri, + ) + }; + let collection_obj_signer = &object::generate_signer(collection_obj_constructor_ref); + let collection_obj_addr = signer::address_of(collection_obj_signer); + let collection_obj = object::object_from_constructor_ref(collection_obj_constructor_ref); + + collection_components::create_refs_and_properties(collection_obj_constructor_ref); + + move_to(collection_owner_obj_signer, CollectionOwnerObjConfig { + extend_ref: object::generate_extend_ref(collection_owner_obj_constructor_ref), + collection_obj, + }); + let collection_owner_obj = object::object_from_constructor_ref(collection_owner_obj_constructor_ref); + move_to(collection_obj_signer, CollectionConfig { + mint_fee_per_nft_by_stages: simple_map::new(), + collection_owner_obj, + }); + + if (option::is_some(&allowlist)) { + let allowlist = *option::borrow(&allowlist); + let stage = string::utf8(ALLOWLIST_MINT_STAGE_CATEGORY); + mint_stage::create( + collection_obj_signer, + *option::borrow(&allowlist_start_time), + *option::borrow(&allowlist_end_time), + stage, + option::none(), + ); + + for (i in 0..vector::length(&allowlist)) { + mint_stage::add_to_allowlist( + collection_obj_signer, + collection_obj, + stage, + *vector::borrow(&allowlist, i), + *option::borrow(&allowlist_mint_limit_per_addr) + ); + }; + + let collection_config = borrow_global_mut(collection_obj_addr); + simple_map::upsert(&mut collection_config.mint_fee_per_nft_by_stages, stage, *option::borrow(&allowlist_mint_fee_per_nft)); + }; + + if (option::is_some(&public_mint_end_time)) { + let stage = string::utf8(PUBLIC_MINT_MINT_STAGE_CATEGORY); + mint_stage::create( + collection_obj_signer, + *option::borrow(&public_mint_start_time), + *option::borrow(&public_mint_end_time), + stage, + option::some(*option::borrow(&public_mint_limit_per_addr)) + ); + + let collection_config = borrow_global_mut(collection_obj_addr); + simple_map::upsert(&mut collection_config.mint_fee_per_nft_by_stages, stage, *option::borrow(&public_mint_fee_per_nft)); + }; + + let registry = borrow_global_mut(@launchpad_addr); + vector::push_back(&mut registry.collection_objects, collection_obj); + + event::emit(CreateCollectionEvent { + creator_addr: sender_addr, + collection_owner_obj, + collection_obj, + max_supply, + name, + uri, + pre_mint_amount, + allowlist, + allowlist_start_time, + allowlist_end_time, + allowlist_mint_limit_per_addr, + allowlist_mint_fee_per_nft, + public_mint_start_time, + public_mint_end_time, + public_mint_limit_per_addr, + public_mint_fee_per_nft, + }); + + for (i in 0..pre_mint_amount) { + mint_nft_internal(sender_addr, collection_obj, 0); + }; + } + + public entry fun mint_nft( + sender: &signer, + collection_obj: Object + ) acquires CollectionConfig, CollectionOwnerObjConfig, Config { + let sender_addr = signer::address_of(sender); + + let stage = &mint_stage::execute_earliest_stage(sender, collection_obj, 1); + assert!(option::is_some(stage), E_NO_ACTIVE_STAGES); + + let mint_fee = get_mint_fee(collection_obj, *option::borrow(stage)); + pay_for_mint(sender, mint_fee); + + mint_nft_internal(sender_addr, collection_obj, mint_fee); + } + + // ================================= View ================================= // + + #[view] + public fun get_admin(): address acquires Config { + let config = borrow_global(@launchpad_addr); + config.admin_addr + } + + #[view] + public fun get_mint_fee_collector(): address acquires Config { + let config = borrow_global(@launchpad_addr); + config.mint_fee_collector_addr + } + + #[view] + public fun get_registry(): vector> acquires Registry { + let registry = borrow_global(@launchpad_addr); + registry.collection_objects + } + + #[view] + public fun get_mint_fee( + collection_obj: Object, + stage: String, + ): u64 acquires CollectionConfig { + let collection_config = borrow_global(object::object_address(&collection_obj)); + let fee = *simple_map::borrow(&collection_config.mint_fee_per_nft_by_stages, &stage); + fee + } + + // ================================= Helpers ================================= // + + fun is_admin(config: &Config, sender: address): bool { + if (sender == config.admin_addr) { + true + } else { + if (object::is_object(@launchpad_addr)) { + let obj = object::address_to_object(@launchpad_addr); + object::is_owner(obj, sender) + } else { + false + } + } + } + + fun is_pending_admin(config: &Config, sender: address): bool { + config.pending_admin_addr == option::some(sender) + } + + fun pay_for_mint(sender: &signer, mint_fee: u64) acquires Config { + if (mint_fee > 0) { + aptos_account::transfer(sender, get_mint_fee_collector(), mint_fee) + } + } + + fun royalty( + royalty_numerator: &mut Option, + admin_addr: address, + ): Option { + if (option::is_some(royalty_numerator)) { + let num = option::extract(royalty_numerator); + option::some(royalty::create(num, 100, admin_addr)) + } else { + option::none() + } + } + + fun mint_nft_internal( + sender_addr: address, + collection_obj: Object, + mint_fee: u64, + ) acquires CollectionConfig, CollectionOwnerObjConfig { + let collection_config = borrow_global(object::object_address(&collection_obj)); + + let collection_owner_obj = collection_config.collection_owner_obj; + let collection_owner_config = borrow_global( + object::object_address(&collection_owner_obj) + ); + let collection_owner_obj_signer = &object::generate_signer_for_extending(&collection_owner_config.extend_ref); + + let next_nft_id = *option::borrow(&collection::count(collection_obj)); + let nft_obj_constructor_ref = &token::create( + collection_owner_obj_signer, + collection::name(collection_obj), + // placeholder value, please read description from json metadata in storage + string_utils::to_string(&next_nft_id), + // placeholder value, please read name from json metadata in storage + string_utils::to_string(&next_nft_id), + royalty::get(collection_obj), + // TODO: does petra support this? image url is in the json, wallet or any UI should fetch json first then fetch image + string_utils::format2(&b"{}/{}.json", collection::uri(collection_obj), next_nft_id), + ); + token_components::create_refs(nft_obj_constructor_ref); + let nft_obj = object::object_from_constructor_ref(nft_obj_constructor_ref); + object::transfer(collection_owner_obj_signer, nft_obj, sender_addr); + + event::emit(MintNftEvent { + recipient_addr: sender_addr, + mint_fee, + collection_obj, + nft_obj, + }); + } + + #[test_only] + use aptos_framework::aptos_coin::{Self, AptosCoin}; + #[test_only] + use aptos_framework::coin; + #[test_only] + use aptos_framework::timestamp; + #[test_only] + use aptos_framework::account; + + #[test(aptos_framework = @0x1, sender = @launchpad_addr, user1 = @0x200)] + fun test_happy_path( + aptos_framework: &signer, + sender: &signer, + user1: &signer, + ) acquires Registry, Config, CollectionConfig, CollectionOwnerObjConfig { + let (burn_cap, mint_cap) = aptos_coin::initialize_for_test(aptos_framework); + + let user1_addr = signer::address_of(user1); + + timestamp::set_time_has_started_for_testing(aptos_framework); + + init_module(sender); + + // create first collection + + create_collection( + sender, + string::utf8(b"description"), + string::utf8(b"name"), + string::utf8(b"hello.com"), + option::some(10), + option::some(10), + 3, + option::none(), + option::none(), + option::none(), + option::none(), + option::none(), + option::some(timestamp::now_seconds()), + option::some(timestamp::now_seconds() + 100), + option::some(2), + option::some(10), + ); + let registry = get_registry(); + let collection_1 = *vector::borrow(®istry, vector::length(®istry) - 1); + assert!(collection::count(collection_1) == option::some(3), 1); + + account::create_account_for_test(user1_addr); + coin::register(user1); + + let mint_fee = get_mint_fee(collection_1, string::utf8(PUBLIC_MINT_MINT_STAGE_CATEGORY)); + aptos_coin::mint(aptos_framework, user1_addr, mint_fee); + + mint_nft(user1, collection_1); + + coin::destroy_burn_cap(burn_cap); + coin::destroy_mint_cap(mint_cap); + } +} diff --git a/templates/nft-template/package.json b/templates/nft-template/package.json new file mode 100644 index 00000000..4ae73148 --- /dev/null +++ b/templates/nft-template/package.json @@ -0,0 +1,19 @@ +{ + "name": "fungible-asset-template", + "license": "Apache-2.0", + "version": "0.0.0", + "scripts": { + "move:init": "node ./scripts/init && node ./scripts/update_env", + "move:compile": "node ./scripts/move/compile", + "move:test": "node ./scripts/move/test", + "move:publish": "node ./scripts/move/publish" + }, + "dependencies": { + "@aptos-labs/ts-sdk": "1.16.0" + }, + "devDependencies": { + "js-yaml": "4.1.0", + "dotenv": "16.3.1", + "tree-kill": "1.2.2" + } +} diff --git a/templates/nft-template/pnpm-lock.yaml b/templates/nft-template/pnpm-lock.yaml new file mode 100644 index 00000000..742b2d32 --- /dev/null +++ b/templates/nft-template/pnpm-lock.yaml @@ -0,0 +1,419 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@aptos-labs/ts-sdk': + specifier: 1.16.0 + version: 1.16.0 + devDependencies: + dotenv: + specifier: 16.3.1 + version: 16.3.1 + js-yaml: + specifier: 4.1.0 + version: 4.1.0 + tree-kill: + specifier: 1.2.2 + version: 1.2.2 + +packages: + + '@aptos-labs/aptos-cli@0.1.8': + resolution: {integrity: sha512-xSWDchqoDR4aR74xNoJZgOzIFtn+EKFGGFLG0vOb+6Ce8Jgg1Ui0Pqhvwbx6Z36dDxfKv0F4M7bnurvWpwAjXA==} + hasBin: true + + '@aptos-labs/aptos-client@0.1.0': + resolution: {integrity: sha512-q3s6pPq8H2buGp+tPuIRInWsYOuhSEwuNJPwd2YnsiID3YSLihn2ug39ktDJAcSOprUcp7Nid8WK7hKqnUmSdA==} + engines: {node: '>=15.10.0'} + + '@aptos-labs/ts-sdk@1.16.0': + resolution: {integrity: sha512-NqJDx39sHN0o7BpxpQzishoZjGBzvXqSVxO+bRm6OPP/Oe+Kb51R5x8dWvW9i3amO3QNdocg/p4jFRCwzqy2Gg==} + engines: {node: '>=11.0.0'} + + '@noble/curves@1.4.0': + resolution: {integrity: sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg==} + + '@noble/hashes@1.4.0': + resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} + engines: {node: '>= 16'} + + '@scure/base@1.1.6': + resolution: {integrity: sha512-ok9AWwhcgYuGG3Zfhyqg+zwl+Wn5uE+dwC0NV/2qQkx4dABbb/bx96vWu8NSj+BNjjSjno+JRYRjle1jV08k3g==} + + '@scure/bip32@1.4.0': + resolution: {integrity: sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==} + + '@scure/bip39@1.3.0': + resolution: {integrity: sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==} + + '@sindresorhus/is@4.6.0': + resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} + engines: {node: '>=10'} + + '@szmarczak/http-timer@4.0.6': + resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} + engines: {node: '>=10'} + + '@types/cacheable-request@6.0.3': + resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} + + '@types/http-cache-semantics@4.0.4': + resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} + + '@types/keyv@3.1.4': + resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} + + '@types/node@20.12.12': + resolution: {integrity: sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==} + + '@types/responselike@1.0.3': + resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + axios@1.6.2: + resolution: {integrity: sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==} + + cacheable-lookup@5.0.4: + resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==} + engines: {node: '>=10.6.0'} + + cacheable-request@7.0.4: + resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==} + engines: {node: '>=8'} + + clone-response@1.0.3: + resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + + defer-to-connect@2.0.1: + resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} + engines: {node: '>=10'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + dotenv@16.3.1: + resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==} + engines: {node: '>=12'} + + end-of-stream@1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + follow-redirects@1.15.6: + resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + form-data@4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + + get-stream@5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + + got@11.8.6: + resolution: {integrity: sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==} + engines: {node: '>=10.19.0'} + + http-cache-semantics@4.1.1: + resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} + + http2-wrapper@1.0.3: + resolution: {integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==} + engines: {node: '>=10.19.0'} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + lowercase-keys@2.0.0: + resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} + engines: {node: '>=8'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mimic-response@1.0.1: + resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} + engines: {node: '>=4'} + + mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + + normalize-url@6.1.0: + resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} + engines: {node: '>=10'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + p-cancelable@2.1.1: + resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} + engines: {node: '>=8'} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + pump@3.0.0: + resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + + quick-lru@5.1.1: + resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} + engines: {node: '>=10'} + + resolve-alpn@1.2.1: + resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} + + responselike@2.0.1: + resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==} + + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + + undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + +snapshots: + + '@aptos-labs/aptos-cli@0.1.8': {} + + '@aptos-labs/aptos-client@0.1.0': + dependencies: + axios: 1.6.2 + got: 11.8.6 + transitivePeerDependencies: + - debug + + '@aptos-labs/ts-sdk@1.16.0': + dependencies: + '@aptos-labs/aptos-cli': 0.1.8 + '@aptos-labs/aptos-client': 0.1.0 + '@noble/curves': 1.4.0 + '@noble/hashes': 1.4.0 + '@scure/bip32': 1.4.0 + '@scure/bip39': 1.3.0 + eventemitter3: 5.0.1 + form-data: 4.0.0 + transitivePeerDependencies: + - debug + + '@noble/curves@1.4.0': + dependencies: + '@noble/hashes': 1.4.0 + + '@noble/hashes@1.4.0': {} + + '@scure/base@1.1.6': {} + + '@scure/bip32@1.4.0': + dependencies: + '@noble/curves': 1.4.0 + '@noble/hashes': 1.4.0 + '@scure/base': 1.1.6 + + '@scure/bip39@1.3.0': + dependencies: + '@noble/hashes': 1.4.0 + '@scure/base': 1.1.6 + + '@sindresorhus/is@4.6.0': {} + + '@szmarczak/http-timer@4.0.6': + dependencies: + defer-to-connect: 2.0.1 + + '@types/cacheable-request@6.0.3': + dependencies: + '@types/http-cache-semantics': 4.0.4 + '@types/keyv': 3.1.4 + '@types/node': 20.12.12 + '@types/responselike': 1.0.3 + + '@types/http-cache-semantics@4.0.4': {} + + '@types/keyv@3.1.4': + dependencies: + '@types/node': 20.12.12 + + '@types/node@20.12.12': + dependencies: + undici-types: 5.26.5 + + '@types/responselike@1.0.3': + dependencies: + '@types/node': 20.12.12 + + argparse@2.0.1: {} + + asynckit@0.4.0: {} + + axios@1.6.2: + dependencies: + follow-redirects: 1.15.6 + form-data: 4.0.0 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + cacheable-lookup@5.0.4: {} + + cacheable-request@7.0.4: + dependencies: + clone-response: 1.0.3 + get-stream: 5.2.0 + http-cache-semantics: 4.1.1 + keyv: 4.5.4 + lowercase-keys: 2.0.0 + normalize-url: 6.1.0 + responselike: 2.0.1 + + clone-response@1.0.3: + dependencies: + mimic-response: 1.0.1 + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + decompress-response@6.0.0: + dependencies: + mimic-response: 3.1.0 + + defer-to-connect@2.0.1: {} + + delayed-stream@1.0.0: {} + + dotenv@16.3.1: {} + + end-of-stream@1.4.4: + dependencies: + once: 1.4.0 + + eventemitter3@5.0.1: {} + + follow-redirects@1.15.6: {} + + form-data@4.0.0: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + + get-stream@5.2.0: + dependencies: + pump: 3.0.0 + + got@11.8.6: + dependencies: + '@sindresorhus/is': 4.6.0 + '@szmarczak/http-timer': 4.0.6 + '@types/cacheable-request': 6.0.3 + '@types/responselike': 1.0.3 + cacheable-lookup: 5.0.4 + cacheable-request: 7.0.4 + decompress-response: 6.0.0 + http2-wrapper: 1.0.3 + lowercase-keys: 2.0.0 + p-cancelable: 2.1.1 + responselike: 2.0.1 + + http-cache-semantics@4.1.1: {} + + http2-wrapper@1.0.3: + dependencies: + quick-lru: 5.1.1 + resolve-alpn: 1.2.1 + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + json-buffer@3.0.1: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + lowercase-keys@2.0.0: {} + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mimic-response@1.0.1: {} + + mimic-response@3.1.0: {} + + normalize-url@6.1.0: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + p-cancelable@2.1.1: {} + + proxy-from-env@1.1.0: {} + + pump@3.0.0: + dependencies: + end-of-stream: 1.4.4 + once: 1.4.0 + + quick-lru@5.1.1: {} + + resolve-alpn@1.2.1: {} + + responselike@2.0.1: + dependencies: + lowercase-keys: 2.0.0 + + tree-kill@1.2.2: {} + + undici-types@5.26.5: {} + + wrappy@1.0.2: {} diff --git a/templates/nft-template/scripts/init.js b/templates/nft-template/scripts/init.js new file mode 100644 index 00000000..a792eb33 --- /dev/null +++ b/templates/nft-template/scripts/init.js @@ -0,0 +1,13 @@ +require("dotenv").config(); + +const cli = require("@aptos-labs/ts-sdk/dist/common/cli/index.js"); + +async function init() { + const move = new cli.Move(); + + await move.init({ + network: process.env.VITE_APP_NETWORK, + profile: process.env.VITE_APP_NETWORK, + }); +} +init(); diff --git a/templates/nft-template/scripts/move/compile.js b/templates/nft-template/scripts/move/compile.js new file mode 100644 index 00000000..63c4ce6b --- /dev/null +++ b/templates/nft-template/scripts/move/compile.js @@ -0,0 +1,20 @@ +require("dotenv").config(); +const fs = require("node:fs"); +const yaml = require("js-yaml"); +const cli = require("@aptos-labs/ts-sdk/dist/common/cli/index.js"); + +const config = yaml.load(fs.readFileSync("./.aptos/config.yaml", "utf8")); +const accountAddress = + config["profiles"][process.env.VITE_APP_NETWORK]["account"]; + +async function compile() { + const move = new cli.Move(); + + await move.compile({ + packageDirectoryPath: "move", + namedAddresses: { + module_addr: accountAddress, // make module_addr generic and fetch from Move.toml file + }, + }); +} +compile(); diff --git a/templates/nft-template/scripts/move/publish.js b/templates/nft-template/scripts/move/publish.js new file mode 100644 index 00000000..62a2e849 --- /dev/null +++ b/templates/nft-template/scripts/move/publish.js @@ -0,0 +1,21 @@ +require("dotenv").config(); +const fs = require("node:fs"); +const yaml = require("js-yaml"); +const cli = require("@aptos-labs/ts-sdk/dist/common/cli/index.js"); + +const config = yaml.load(fs.readFileSync("./.aptos/config.yaml", "utf8")); +const accountAddress = + config["profiles"][process.env.VITE_APP_NETWORK]["account"]; + +async function publish() { + const move = new cli.Move(); + + await move.publish({ + packageDirectoryPath: "move", + namedAddresses: { + module_addr: accountAddress, // make module_addr generic and fetch from Move.toml file + }, + profile: process.env.VITE_APP_NETWORK, + }); +} +publish(); diff --git a/templates/nft-template/scripts/move/test.js b/templates/nft-template/scripts/move/test.js new file mode 100644 index 00000000..302e8e46 --- /dev/null +++ b/templates/nft-template/scripts/move/test.js @@ -0,0 +1,19 @@ +require("dotenv").config(); +const fs = require("node:fs"); +const yaml = require("js-yaml"); +const cli = require("@aptos-labs/ts-sdk/dist/common/cli/index.js"); + +const config = yaml.load(fs.readFileSync("./.aptos/config.yaml", "utf8")); +const accountAddress = + config["profiles"][process.env.VITE_APP_NETWORK]["account"]; +async function test() { + const move = new cli.Move(); + + await move.test({ + packageDirectoryPath: "move", + namedAddresses: { + launchpad_addr: accountAddress, // make module_addr generic and fetch from Move.toml file + }, + }); +} +test(); diff --git a/templates/nft-template/scripts/update_env.js b/templates/nft-template/scripts/update_env.js new file mode 100644 index 00000000..db342845 --- /dev/null +++ b/templates/nft-template/scripts/update_env.js @@ -0,0 +1,10 @@ +const fs = require("node:fs"); +const yaml = require("js-yaml"); +require("dotenv").config(); + +const config = yaml.load(fs.readFileSync("./.aptos/config.yaml", "utf8")); +const accountAddress = + config["profiles"][process.env.VITE_APP_NETWORK]["account"]; + +fs.appendFileSync(".env", `\nVITE_MODULE_ADDRESS=0x${accountAddress}`); +fs.appendFileSync("frontend/.env", `\nVITE_MODULE_ADDRESS=0x${accountAddress}`); diff --git a/templates/nft-template/sh_scripts/create_and_mint_some_fas.sh b/templates/nft-template/sh_scripts/create_and_mint_some_fas.sh new file mode 100755 index 00000000..83a9523f --- /dev/null +++ b/templates/nft-template/sh_scripts/create_and_mint_some_fas.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +set -e + +echo "##### Running move script to create and mint some FAs in 1 tx #####" + +CONTRACT_ADDRESS=$(cat contract_address.txt) + +# Need to compile the package first +aptos move compile \ + --named-addresses launchpad_addr=$CONTRACT_ADDRESS + +# Profile is the account you used to execute transaction +# Run "aptos init" to create the profile, then get the profile name from .aptos/config.yaml +SENDER_PROFILE=testnet-profile-1 + +# Run the script +aptos move run-script \ + --package-dir move \ + --assume-yes \ + --profile $SENDER_PROFILE \ + --compiled-script-path build/launchpad/bytecode_scripts/create_and_mint_some_fas.mv diff --git a/templates/nft-template/sh_scripts/deploy.sh b/templates/nft-template/sh_scripts/deploy.sh new file mode 100755 index 00000000..85014601 --- /dev/null +++ b/templates/nft-template/sh_scripts/deploy.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +set -e + +echo "##### Deploy module under a new object #####" + +# Profile is the account you used to execute transaction +# Run "aptos init" to create the profile, then get the profile name from .aptos/config.yaml +PUBLISHER_PROFILE=testnet-profile-1 + +PUBLISHER_ADDR=0x$(aptos config show-profiles --profile=$PUBLISHER_PROFILE | grep 'account' | sed -n 's/.*"account": \"\(.*\)\".*/\1/p') + +MINTER_ADDR="0x9d7365d7a09ee3a5610a2131d6ee395531d581e7a7c42582de51a3f111534bbd" + +OUTPUT=$(aptos move create-object-and-publish-package \ + --skip-fetch-latest-git-deps \ + --package-dir move \ + --address-name launchpad_addr \ + --named-addresses "launchpad_addr=$PUBLISHER_ADDR,minter=$MINTER_ADDR"\ + --profile $PUBLISHER_PROFILE \ + --assume-yes) + +# Extract the deployed contract address and save it to a file +echo "$OUTPUT" | grep "Code was successfully deployed to object address" | awk '{print $NF}' | sed 's/\.$//' > contract_address.txt +echo "Contract deployed to address: $(cat contract_address.txt)" +echo "Contract address saved to contract_address.txt" + diff --git a/templates/nft-template/sh_scripts/test.sh b/templates/nft-template/sh_scripts/test.sh new file mode 100755 index 00000000..fbcdd156 --- /dev/null +++ b/templates/nft-template/sh_scripts/test.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +set -e + +echo "##### Running tests #####" + +aptos move test \ + --package-dir move \ + --skip-fetch-latest-git-deps \ + --dev diff --git a/templates/nft-template/sh_scripts/upgrade.sh b/templates/nft-template/sh_scripts/upgrade.sh new file mode 100755 index 00000000..343d698a --- /dev/null +++ b/templates/nft-template/sh_scripts/upgrade.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +set -e + +echo "##### Upgrade module #####" + +# Profile is the account you used to execute transaction +# Run "aptos init" to create the profile, then get the profile name from .aptos/config.yaml +PUBLISHER_PROFILE=testnet-profile-1 + +CONTRACT_ADDRESS=$(cat contract_address.txt) +MINTER_ADDR="0x9d7365d7a09ee3a5610a2131d6ee395531d581e7a7c42582de51a3f111534bbd" + +aptos move upgrade-object-package \ + --skip-fetch-latest-git-deps \ + --package-dir move \ + --object-address $CONTRACT_ADDRESS \ + --named-addresses "launchpad_addr=$CONTRACT_ADDRESS,minter=$MINTER_ADDR"\ + --profile $PUBLISHER_PROFILE \ + --assume-yes