Author SHA1 Message Date
Serkan Reis e319a4306c Update config for fang-1 testnet 2024-05-05 11:41:01 +03:00
Serkan Reis 5e963cf615 Merge pull request #375 from public-awesome/token-factory-update
Address token factory issues
2024-05-01 17:46:18 +03:00
Serkan Reis 41d71a6765 Address token factory issues 2024-05-01 17:38:34 +03:00
Serkan Reis 22d58dbe45 Merge pull request #374 from public-awesome/main
Sync main > dev
2024-04-30 13:37:16 +03:00
Serkan Reis 471ff43fcf Merge pull request #373 from public-awesome/enable-oe-wl-compatibility-on-mainnet
Enable WL use for Open/Limited Edition collections on mainnet
2024-04-30 13:36:02 +03:00
Serkan Reis 027c4c6821 Enable WL use for Open/Limited Edition collections on mainnet 2024-04-26 13:27:48 +03:00
Serkan Reis f0b94422b1 Merge pull request #372 from public-awesome/skip-whitelist-checks
Skip whitelist checks on mainnet for OE collections
2024-04-16 19:24:16 +03:00
Serkan Reis bb8b3e1791 Skip wl checks on mainnet for OE collections 2024-04-16 19:23:02 +03:00
Serkan Reis 42707adc0b Merge pull request #371 from public-awesome/disable-oe-whitelists
Disable whitelist compatibility for Open Edition collections on mainnet
2024-04-16 19:03:41 +03:00
Serkan Reis 35bed9d6aa Disable whitelist compatibility for OEs on mainnet 2024-04-16 18:51:24 +03:00
Serkan Reis 93f98bcec6 Merge pull request #370 from public-awesome/main
Sync main > dev
2024-04-16 18:36:44 +03:00
Serkan Reis 83835dfba2 Merge branch 'develop' into main 2024-04-16 18:34:30 +03:00
Serkan Reis d85e19b770 Merge pull request #369 from public-awesome/update-mint-price-denom-list
Update mint price denom list
2024-04-16 18:29:41 +03:00
Serkan Reis 1abb2f82df Update default sg721 code ID for base minter creation 2024-04-16 18:22:53 +03:00
Serkan Reis b44e4512a5 Remove HUAHUA from mint price denom list 2024-04-16 18:21:26 +03:00
Serkan Reis a226d8b341 Merge pull request #368 from public-awesome/oe-whitelist-compatibility
Whitelist compatibility for OE collections
2024-04-15 13:55:05 +03:00
Serkan Reis 8a02a7f80d Enable setting both time & token count limit for OE collections 2024-04-15 13:48:44 +03:00
Serkan Reis 122c61055f Select OE factory wrt existing whitelist type 2024-04-15 09:39:19 +03:00
Serkan Reis 81880aecdd Display WL address upon OE collection creation 2024-04-14 21:08:31 +03:00
Serkan Reis b38562d9fd Improve factory selection logic for OE collections 2024-04-14 19:53:44 +03:00
Serkan Reis 0eb94e4ee8 Add OE whitelist compatibility 2024-04-14 17:08:49 +03:00
Serkan Reis 3a150a50f3 Merge pull request #367 from public-awesome/develop
Sync dev > main
2024-04-10 22:24:14 +03:00
Serkan Reis 03c008d0fa Add discount price update warning 2024-04-10 22:22:30 +03:00
Serkan Reis 6487646f2c Merge pull request #365 from public-awesome/develop
Sync dev > main
2024-04-10 19:00:42 +03:00
Serkan Reis b1094f5231 Merge pull request #364 from public-awesome/wl-merkletree-dashboard-update
Add whitelist merkle tree support to whitelist contract dashboard
2024-04-10 18:59:47 +03:00
Serkan Reis 97bb60b3ff Add option to instantiate whitelist merkletree 2024-04-10 18:48:56 +03:00
Serkan Reis 9654ec845e Update execute action list for whitelist merkle tree 2024-04-10 16:06:56 +03:00
Serkan Reis 41e8a3961a Fetch proof hashes for hasMember query 2024-04-10 14:39:00 +03:00
Serkan Reis 43fd0d7848 Update queries for whitelist merkle tree 2024-04-10 13:25:29 +03:00
Serkan Reis cc16f7ceb1 Merge pull request #363 from public-awesome/develop
Sync dev > main
2024-04-08 09:29:00 +03:00
Serkan Reis cc58de90a4 Update interface for CollectionInfo 2024-04-08 09:18:22 +03:00
Serkan Reis 27f8510335 Merge pull request #362 from public-awesome/develop
Sync dev > main
2024-04-08 09:01:25 +03:00
Serkan Reis 0fc60f6c05 Merge pull request #361 from public-awesome/transfer-ownership
Allow designating a new creator address when updating collection info
2024-04-08 09:00:39 +03:00
Serkan Reis c59531f87e Allow designating a new creator address when updating collection info 2024-04-08 08:59:38 +03:00
Serkan Reis 6ddd780338 Merge pull request #360 from public-awesome/develop
Sync dev > main
2024-04-04 13:14:07 +03:00
Serkan Reis 57e36b6dbd Merge pull request #359 from public-awesome/featured-vending-minter-wl-merkletree
Add featured vending-minter-wl-merkletree option
2024-04-04 13:13:36 +03:00
Serkan Reis 1a866db888 Add featured vending-minter-wl-merkletree option 2024-04-04 13:12:19 +03:00
Serkan Reis 7e97f5d393 Merge pull request #358 from public-awesome/develop
Sync dev > main
2024-04-04 12:10:55 +03:00
Serkan Reis e26068085d Merge pull request #357 from public-awesome/wl-merkletree-updates
Display spinner during merkle root hash acquisiton
2024-04-04 12:10:24 +03:00
Serkan Reis 1ccc55a3b2 Temporarily disable importing members for whitelist-merkletree 2024-04-04 12:07:59 +03:00
Serkan Reis 99f7b25f10 Add merkle root fetch spinner 2024-04-04 11:56:09 +03:00
Serkan Reis 3308cfdcf2 Merge pull request #356 from public-awesome/develop
Sync dev > main
2024-04-03 22:43:22 +03:00
Serkan Reis 5e9fdc1cf1 Merge pull request #355 from public-awesome/creation-wl-merkletree-update
Include creation fee for whitelist-merkletree
2024-04-03 22:42:51 +03:00
Serkan Reis 39847d1ab9 Include creation fee for whitelist-merkletree 2024-04-03 22:41:28 +03:00
Serkan Reis 29cd89f6a5 Merge pull request #354 from public-awesome/develop
Sync dev > main
2024-04-02 23:24:14 +03:00
Serkan Reis 6466945e28 Merge pull request #353 from public-awesome/merkle-wl-update
Add whitelist-merkletree option during collection creation
2024-04-02 23:22:58 +03:00
Serkan Reis db9ffb0899 Revert initial OE whitelist compatibility changes 2024-04-02 23:15:17 +03:00
Serkan Reis a295dd5d4a Update collection creation logic 2024-04-02 22:58:51 +03:00
Serkan Reis 85ac7a4f71 Add env variables for whitelist-merkletree 2024-04-02 17:02:07 +03:00
Serkan Reis ee46fa64d3 Include whitelist-merkletree option in whitelist details 2024-04-02 14:18:37 +03:00
Serkan Reis f5f14fb330 Add whitelist-merkletree contract helpers 2024-04-02 13:04:25 +03:00
Serkan Reis fd65316d1f Add merkleTree generation helpers 2024-04-02 12:59:43 +03:00
Serkan Reis 004b102540 Test init WL compatible open-edition collection 2024-04-01 13:52:10 +03:00
Serkan Reis 9095c5c06c Merge pull request #352 from public-awesome/main
Sync main > dev
2024-03-21 16:05:41 +02:00
Serkan Reis 55e46cc830 Merge pull request #351 from public-awesome/update-creation-summary
Update collection creation summary logic
2024-03-21 16:04:43 +02:00
Serkan Reis d07ad7db04 Update collection creation summary logic 2024-03-21 16:58:31 +03:00
Serkan Reis 8f0a0e84aa Merge pull request #350 from public-awesome/develop
Sync dev > main
2024-03-19 21:40:51 +02:00
Serkan Reis 4e6db44105 Merge pull request #349 from public-awesome/update-mint-price-denoms
Include TIA among mint price denom options
2024-03-19 21:40:09 +02:00
Serkan Reis 7b0f1f6176 Update denom list to match the right collection code id during creation 2024-03-19 11:52:39 +03:00
Serkan Reis fe8bfd14bc Update minter list 2024-03-19 07:50:56 +03:00
Serkan Reis 1c7c0a682d Update env variables 2024-03-19 07:36:00 +03:00
Serkan Reis b58549b1a2 Add TIA to token list 2024-03-19 06:12:12 +03:00
Serkan Reis 6cdd2a24ac Merge pull request #348 from public-awesome/develop
Sync dev> main
2024-03-17 16:45:12 +02:00
Serkan Reis 437ac5b6f5 Merge pull request #347 from public-awesome/include-infinity-pools-for-snapshots
Add option to include tokens in Infinity Pools for snapshots
2024-03-17 16:44:24 +02:00
Serkan Reis afef36375b Add option to include tokens in Infinity Pools for snapshots 2024-03-17 17:42:11 +03:00
Serkan Reis 0231692eb1 Merge pull request #346 from public-awesome/develop
Sync dev > main
2024-03-01 10:25:27 +02:00
Serkan Reis e0d6aaee4d Merge pull request #345 from public-awesome/remove-collection-offer
Add remove collection offer page
2024-03-01 10:24:37 +02:00
Serkan Reis c4027859c0 Update testnet marketplace contract address 2024-03-01 11:23:25 +03:00
Serkan Reis c12fd51525 Add remove collection offer page 2024-03-01 11:18:38 +03:00
Serkan Reis f1aeeb2167 Merge pull request #344 from public-awesome/develop
Sync dev > main
2024-02-20 22:06:59 +02:00
Serkan Reis 103921d946 Merge pull request #343 from public-awesome/update-collection-info-update
Add update collection info disclaimer
2024-02-20 22:06:04 +02:00
Serkan Reis b4fcc63de6 Add update collection info disclaimer 2024-02-20 23:05:06 +03:00
Serkan Reis b3dea5e757 Merge pull request #342 from public-awesome/limited-edition-update
Limited edition collection update
2024-02-14 22:37:02 +02:00
Serkan Reis e43ec78acc Revert creation summary changes for mainnet 2024-02-14 23:27:36 +03:00
Serkan Reis 4c312d0ad8 Merge pull request #339 from public-awesome/main
Sync main > dev
2024-02-12 22:16:23 +02:00
Serkan Reis 89836ad84e Merge pull request #338 from public-awesome/sample-wl-flex-airdrop-file
Include sample files for whitelist creation
2024-02-12 22:15:54 +02:00
Serkan Reis 51f38f12d7 Include sample files for whitelist creation 2024-02-12 23:10:18 +03:00
Serkan Reis 609745ce11 Merge pull request #337 from public-awesome/main
Sync main > dev
2024-02-11 18:57:37 +02:00
Serkan Reis 3b9778270b Merge pull request #332 from public-awesome/add-default-nft-storage-api-key-option
Add default NFT.Storage API key option
2024-02-11 18:57:01 +02:00
Serkan Reis c66b21792c Add default API key selection option 2024-02-11 19:41:05 +03:00
Serkan Reis 35a9c0eba8 Set up env variable for default API key 2024-02-11 19:35:27 +03:00
Serkan Reis 07152745e0 Merge pull request #336 from public-awesome/main
Sync main > dev
2024-02-08 18:36:06 +02:00
Serkan Reis 7a455b60bc Merge pull request #335 from public-awesome/remove-wl-flex-allocation-check
Disable whitelist-flex total allocation check
2024-02-08 18:35:35 +02:00
Serkan Reis 67694d4c5d Disable whitelist-flex total allocation check 2024-02-08 19:34:48 +03:00
Serkan Reis 4ec704f588 Merge pull request #334 from public-awesome/main
Sync main > dev
2024-02-07 10:28:13 +02:00
Serkan Reis 3f44e342e8 Merge pull request #333 from public-awesome/fix-factory-validation
Fix factory validation on init issue
2024-02-07 10:27:41 +02:00
Serkan Reis 245fd2253b Add API Key input method 2024-02-06 23:48:13 +03:00
Serkan Reis ed1844fdf0 Merge pull request #331 from public-awesome/main
Sync main > dev
2024-02-05 21:29:45 +02:00
Serkan Reis 6316a68408 Merge pull request #329 from public-awesome/main
Sync main > dev
2024-02-03 03:48:44 +02:00
Serkan Reis a949a1e103 Merge pull request #326 from public-awesome/time-input-updates
Time input updates
2024-02-02 15:45:10 +02:00
Serkan Reis 39385ee82e Address lint issues 2024-02-02 16:34:17 +03:00
Serkan Reis 440ee78122 Merge branch 'develop' into time-input-updates 2024-02-02 15:27:50 +02:00
Serkan Reis 1f31cdbe29 Merge pull request #324 from public-awesome/factory-validation
Check if a valid factory exists for the selected parameters prior to collection creation
2024-02-02 09:02:45 +02:00
Serkan Reis b66e6d0920 Merge pull request #322 from public-awesome/featured-check
Add option to create featured collections
2024-01-31 20:00:17 +02:00
Serkan Reis ab4d4fa31d Merge pull request #321 from public-awesome/creation-summary-update
Update collection creation summary logic
2024-01-31 09:24:14 +02:00
Serkan Reis 92ce752b15 Update collection creation summary logic 2024-01-31 10:17:53 +03:00
Serkan Reis 01936d8f5e Merge pull request #320 from public-awesome/main
Sync main > dev
2024-01-25 11:09:08 +02:00
Serkan Reis 02df116e09 Merge pull request #318 from public-awesome/main
Sync main > dev
2024-01-24 11:46:53 +02:00
Serkan Reis ab724afb9c Merge pull request #316 from public-awesome/main
Sync main > dev
2024-01-22 22:37:52 +02:00
Serkan Reis 3c3b60ebe8 Merge pull request #314 from public-awesome/main
Sync main > dev
2024-01-18 16:02:27 +02:00
Serkan Reis de5de84873 Merge pull request #312 from public-awesome/open-edition-updates
Token Count limited OE collections
2024-01-18 12:29:56 +02:00
Serkan Reis 7951669ed2 Update checks for OE minting details 2023-12-20 23:36:32 +03:00
Serkan Reis 20944cf5de Update config import logic for OE 2023-12-20 23:35:41 +03:00
Serkan Reis 0f1c4a027b Add limit type selection to OE MintingDetails 2023-12-19 13:53:41 +03:00
42 changed files with 3344 additions and 323 deletions
+26 -5
View File
@@ -16,8 +16,10 @@ NEXT_PUBLIC_VENDING_FACTORY_ADDRESS="stars18h7ugh8eaug7wr0w4yjw0ls5s937z35pnkg93
NEXT_PUBLIC_FEATURED_VENDING_FACTORY_ADDRESS="stars14pd96yk3t6gq9l6uyrkg0n5dr09n8rt5y9v3at8x4wl4lrkxhlzq4trqmh"
NEXT_PUBLIC_VENDING_FACTORY_UPDATABLE_ADDRESS="stars1h65nms9gwg4vdktyqj84tu50gwlm34e0eczl5w2ezllxuzfxy9esa9qlt0"
NEXT_PUBLIC_VENDING_FACTORY_FLEX_ADDRESS="stars1hvu2ghqkcnvhtj2fc6wuazxt4dqcftslp2rwkkkcxy269a35a9pq60ug2q"
NEXT_PUBLIC_VENDING_FACTORY_MERKLE_TREE_ADDRESS="stars167tudcsr9n2y9ljgk4cwxhs0cvkfkk0hh6c3dzngsz7m5s9jmqnsdgr3jy"
NEXT_PUBLIC_FEATURED_VENDING_FACTORY_MERKLE_TREE_ADDRESS="stars167tudcsr9n2y9ljgk4cwxhs0cvkfkk0hh6c3dzngsz7m5s9jmqnsdgr3jy"
NEXT_PUBLIC_FEATURED_VENDING_FACTORY_FLEX_ADDRESS="stars1udlmmnmmnnqamh36hy6d7azn3ycv23yymkmg6558ntalvyt2pz7s8lhgcd"
NEXT_PUBLIC_VENDING_FACTORY_UPDATABLE_FLEX_ADDRESS=
# NEXT_PUBLIC_VENDING_FACTORY_UPDATABLE_FLEX_ADDRESS=
# NEXT_PUBLIC_VENDING_IBC_ATOM_FACTORY_ADDRESS=
# NEXT_PUBLIC_VENDING_IBC_ATOM_UPDATABLE_FACTORY_ADDRESS=
@@ -25,8 +27,10 @@ NEXT_PUBLIC_VENDING_FACTORY_UPDATABLE_FLEX_ADDRESS=
# NEXT_PUBLIC_VENDING_IBC_ATOM_UPDATABLE_FACTORY_FLEX_ADDRESS=
# NEXT_PUBLIC_VENDING_IBC_USDC_FACTORY_ADDRESS=
# NEXT_PUBLIC_FEATURED_VENDING_IBC_USDC_FACTORY_ADDRESS=
# NEXT_PUBLIC_VENDING_IBC_USDC_UPDATABLE_FACTORY_ADDRESS=
# NEXT_PUBLIC_VENDING_IBC_USDC_FACTORY_FLEX_ADDRESS=
# NEXT_PUBLIC_FEATURED_VENDING_IBC_USDC_FACTORY_FLEX_ADDRESS=
# NEXT_PUBLIC_VENDING_IBC_USDC_UPDATABLE_FACTORY_FLEX_ADDRESS=
# NEXT_PUBLIC_VENDING_IBC_USK_FACTORY_ADDRESS=
@@ -34,6 +38,15 @@ NEXT_PUBLIC_VENDING_FACTORY_UPDATABLE_FLEX_ADDRESS=
# NEXT_PUBLIC_VENDING_IBC_USK_FACTORY_FLEX_ADDRESS=
# NEXT_PUBLIC_VENDING_IBC_USK_UPDATABLE_FACTORY_FLEX_ADDRESS=
# NEXT_PUBLIC_VENDING_IBC_TIA_FACTORY_ADDRESS=
# NEXT_PUBLIC_FEATURED_VENDING_IBC_TIA_FACTORY_ADDRESS=
# NEXT_PUBLIC_VENDING_IBC_TIA_UPDATABLE_FACTORY_ADDRESS=
# NEXT_PUBLIC_VENDING_IBC_TIA_FACTORY_FLEX_ADDRESS=
# NEXT_PUBLIC_VENDING_IBC_TIA_FACTORY_MERKLE_TREE_ADDRESS=
# NEXT_PUBLIC_FEATURED_VENDING_IBC_TIA_FACTORY_MERKLE_TREE_ADDRESS=
# NEXT_PUBLIC_FEATURED_VENDING_IBC_TIA_FACTORY_FLEX_ADDRESS=
# NEXT_PUBLIC_VENDING_IBC_TIA_UPDATABLE_FACTORY_FLEX_ADDRESS=
NEXT_PUBLIC_VENDING_NATIVE_STARDUST_FACTORY_ADDRESS="stars1mxwf2hjcjvqnlw0v3j7m0u34975qesp325wzrgz0ht7vr8ys2zmsenjutf"
NEXT_PUBLIC_VENDING_NATIVE_STARDUST_UPDATABLE_FACTORY_ADDRESS="stars18gjczf88jd4z3a3megwj9g5c9famu654csxfnnq59mkqeszuzy4ssdgr46"
NEXT_PUBLIC_VENDING_NATIVE_STRDST_FLEX_FACTORY_ADDRESS="stars1eluqmr6x78ehl4plrln6khxc0qrspfhc7rt3whmr59escpve0r4swcacjh"
@@ -46,6 +59,7 @@ NEXT_PUBLIC_BASE_FACTORY_ADDRESS="stars1a45hcxty3spnmm2f0papl8v4dk5ew29s4syhn4ef
NEXT_PUBLIC_BASE_FACTORY_UPDATABLE_ADDRESS="stars100xegx2syry4tclkmejjwxk4nfqahvcqhm9qxut5wxuzhj5d9qfsh5nmym"
NEXT_PUBLIC_OPEN_EDITION_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e"
NEXT_PUBLIC_OPEN_EDITION_FACTORY_FLEX_ADDRESS="stars1nc59ddaa8xcx9mu8jladza82dznhxrta3njal3xylkqlsfqa7g4s9s5q02"
NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_FACTORY_ADDRESS="stars1fk5dkzcylam8mcpqrn8y9spauvc3d4navtaqurcc49dc3p9f8d3qdkvymx"
NEXT_PUBLIC_VENDING_IBC_KUJI_FACTORY_ADDRESS="stars1yyje87e0h9mqg34kp3x75yesa78ve4glc3dstdrn6nscw3zjfanqkj95f0"
@@ -59,8 +73,12 @@ NEXT_PUBLIC_VENDING_IBC_CRBRUS_FACTORY_FLEX_ADDRESS="stars1halhp674yxwgn3p4gpkl8
# NEXT_PUBLIC_OPEN_EDITION_IBC_ATOM_FACTORY_ADDRESS=
# NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_ATOM_FACTORY_ADDRESS=
# NEXT_PUBLIC_OPEN_EDITION_IBC_USDC_FACTORY_ADDRESS=
# NEXT_PUBLIC_OPEN_EDITION_IBC_USDC_FACTORY_ADDRESS="stars152a40mmd3k2kk90add606vrqxcvzdp29qrjx4pjv33cjl6svksfscrrtuk"
# NEXT_PUBLIC_OPEN_EDITION_IBC_USDC_FACTORY_FLEX_ADDRESS="stars10sz9mup3a548l34k83q5w59nrklrnvv2gdsdkr2xref4zl5j3d4q0efamx"
# NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_USDC_FACTORY_ADDRESS=
# NEXT_PUBLIC_OPEN_EDITION_IBC_TIA_FACTORY_ADDRESS="stars1vza7k890fkejxz3mqwau0u2m89k9y76w94vvxe4d42ya9862ryfq0damns"
# NEXT_PUBLIC_OPEN_EDITION_IBC_TIA_FACTORY_FLEX_ADDRESS="stars1jgn0ntt5tut93yn756rrqa60794qdsrn6dwhl8vhfx0yxgpr44qsfzhmrt"
# NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_TIA_FACTORY_ADDRESS=
NEXT_PUBLIC_OPEN_EDITION_IBC_FRNZ_FACTORY_ADDRESS="stars1vzffawsjhvspstu5lvtzz2x5n7zh07hnw09c9dfxcj78un05rcms5n3q3e"
NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_FRNZ_FACTORY_ADDRESS="stars1tc09vlgdg8rqyapcxwm9qdq8naj4gym9px4ntue9cs0kse5rvess0nee3a"
@@ -87,13 +105,14 @@ NEXT_PUBLIC_VENDING_IBC_NBTC_UPDATABLE_FACTORY_ADDRESS="stars1k6ee8qgwvumguqnqqr
NEXT_PUBLIC_SG721_NAME_ADDRESS="stars1fx74nkqkw2748av8j7ew7r3xt9cgjqduwn8m0ur5lhe49uhlsasszc5fhr"
NEXT_PUBLIC_ROYALTY_REGISTRY_ADDRESS="stars1crgx0f70fzksa57hq87wtl8f04h0qyk5la0hk0fu8dyhl67ju80qaxzr5z"
NEXT_PUBLIC_INFINITY_SWAP_PROTOCOL_ADDRESS="stars136yp6fl9h66m0cwv8weu4w4aawveuz40992ty0atj5ecjd8z0thqv9xpy5"
NEXT_PUBLIC_WHITELIST_CODE_ID=3131
NEXT_PUBLIC_WHITELIST_FLEX_CODE_ID=3130
NEXT_PUBLIC_WHITELIST_CODE_ID=4008
NEXT_PUBLIC_WHITELIST_FLEX_CODE_ID=4009
NEXT_PUBLIC_WHITELIST_MERKLE_TREE_CODE_ID=3911
NEXT_PUBLIC_BADGE_HUB_CODE_ID=1336
NEXT_PUBLIC_BADGE_HUB_ADDRESS="stars1dacun0xn7z73qzdcmq27q3xn6xuprg8e2ugj364784al2v27tklqynhuqa"
NEXT_PUBLIC_BADGE_NFT_CODE_ID=1337
NEXT_PUBLIC_BADGE_NFT_ADDRESS="stars1vlw4y54dyzt3zg7phj8yey9fg4zj49czknssngwmgrnwymyktztstalg7t"
NEXT_PUBLIC_SPLITS_CODE_ID=1905
NEXT_PUBLIC_SPLITS_CODE_ID=4010
NEXT_PUBLIC_CW4_GROUP_CODE_ID=1904
NEXT_PUBLIC_API_URL=https://nft-api.elgafar-1.stargaze-apis.com
@@ -103,6 +122,8 @@ NEXT_PUBLIC_STARGAZE_WEBSITE_URL=https://testnet.publicawesome.dev
NEXT_PUBLIC_BADGES_URL=https://badges.publicawesome.dev
NEXT_PUBLIC_WEBSITE_URL=https://
NEXT_PUBLIC_SYNC_COLLECTIONS_API_URL="https://..."
NEXT_PUBLIC_WHITELIST_MERKLE_TREE_API_URL="https://..."
NEXT_PUBLIC_NFT_STORAGE_DEFAULT_API_KEY="..."
NEXT_PUBLIC_MEILISEARCH_HOST="https://search.publicawesome.dev"
NEXT_PUBLIC_MEILISEARCH_API_KEY= "..."
+10 -3
View File
@@ -1,3 +1,5 @@
/* eslint-disable eslint-comments/disable-enable-pair */
// Styles required for @cosmos-kit/react modal
import '@interchain-ui/react/styles'
@@ -7,6 +9,7 @@ import { wallets as leapExtensionWallets } from '@cosmos-kit/leap-extension'
import { ChainProvider } from '@cosmos-kit/react'
import { assets, chains } from 'chain-registry'
import { getConfig } from 'config'
import { fangAssetList, fangTestnet } from 'config/fangTestnet'
import type { ReactNode } from 'react'
import { NETWORK } from 'utils/constants'
@@ -14,8 +17,8 @@ export const WalletProvider = ({ children }: { children: ReactNode }) => {
const { gasPrice, feeToken } = getConfig(NETWORK)
return (
<ChainProvider
assetLists={assets}
chains={chains}
assetLists={[...assets, fangAssetList]}
chains={[...chains, fangTestnet]}
endpointOptions={{
endpoints: {
stargaze: {
@@ -26,6 +29,10 @@ export const WalletProvider = ({ children }: { children: ReactNode }) => {
rpc: ['https://rpc.elgafar-1.stargaze-apis.com/'],
rest: ['https://rest.elgafar-1.stargaze-apis.com/'],
},
stargazefangtestnet: {
rpc: ['https://rpc.fang-1.stargaze-apis.com/'],
rest: ['https://rest.fang-1.stargaze-apis.com/'],
},
},
isLazy: true,
}}
@@ -37,7 +44,7 @@ export const WalletProvider = ({ children }: { children: ReactNode }) => {
gasPrice: GasPrice.fromString(`${gasPrice}${feeToken}`),
}),
signingStargate: () => ({
gasPrice: GasPrice.fromString(`${gasPrice}${feeToken}`),
gasPrice: GasPrice.fromString(`${0.01}${feeToken}`),
}),
}}
wallets={[...keplrExtensionWallets, ...leapExtensionWallets]}
+1 -1
View File
@@ -78,7 +78,7 @@ export const WhitelistUpload = ({ onChange }: WhitelistUploadProps) => {
const printableData = data?.map((item) => item.replace(regex, ''))
const names = printableData?.filter((address) => address !== '' && address.endsWith('.stars'))
const strippedNames = names?.map((name) => name.split('.')[0])
console.log(names)
console.log('names: ', names)
if (strippedNames?.length) {
await toast
.promise(resolveAddresses(strippedNames), {
@@ -13,6 +13,7 @@ import type { ChangeEvent } from 'react'
import { useEffect, useMemo, useRef, useState } from 'react'
import { toast } from 'react-hot-toast'
import type { UploadServiceType } from 'services/upload'
import { NFT_STORAGE_DEFAULT_API_KEY } from 'utils/constants'
import { getAssetType } from 'utils/getAssetType'
export type UploadMethod = 'new' | 'existing'
@@ -37,6 +38,7 @@ export const ImageUploadDetails = ({ onChange, mintRule }: ImageUploadDetailsPro
const [assetFile, setAssetFile] = useState<File>()
const [uploadMethod, setUploadMethod] = useState<UploadMethod>('new')
const [uploadService, setUploadService] = useState<UploadServiceType>('nft-storage')
const [useDefaultApiKey, setUseDefaultApiKey] = useState(false)
const assetFileRef = useRef<HTMLInputElement | null>(null)
@@ -130,6 +132,14 @@ export const ImageUploadDetails = ({ onChange, mintRule }: ImageUploadDetailsPro
imageUrlState.onChange('')
}, [uploadMethod, mintRule])
useEffect(() => {
if (useDefaultApiKey) {
nftStorageApiKeyState.onChange(NFT_STORAGE_DEFAULT_API_KEY || '')
} else {
nftStorageApiKeyState.onChange('')
}
}, [useDefaultApiKey])
const videoPreview = useMemo(
() => (
<video
@@ -269,7 +279,22 @@ export const ImageUploadDetails = ({ onChange, mintRule }: ImageUploadDetailsPro
<div className="flex w-full">
<Conditional test={uploadService === 'nft-storage'}>
<TextInput {...nftStorageApiKeyState} className="w-full" />
<div className="flex-col w-full">
<TextInput {...nftStorageApiKeyState} className="w-full" disabled={useDefaultApiKey} />
<div className="flex-row mt-2 w-full form-control">
<label className="cursor-pointer label">
<span className="mr-2 font-bold">Use Default API Key</span>
<input
checked={useDefaultApiKey}
className={`${useDefaultApiKey ? `bg-stargaze` : `bg-gray-600`} checkbox`}
onClick={() => {
setUseDefaultApiKey(!useDefaultApiKey)
}}
type="checkbox"
/>
</label>
</div>
</div>
</Conditional>
<Conditional test={uploadService === 'pinata'}>
<TextInput {...pinataApiKeyState} className="w-full" />
+54
View File
@@ -3,6 +3,7 @@
import { toUtf8 } from '@cosmjs/encoding'
import clsx from 'clsx'
import { AirdropUpload } from 'components/AirdropUpload'
import { Alert } from 'components/Alert'
import { Button } from 'components/Button'
import type { DispatchExecuteArgs } from 'components/collections/actions/actions'
import { dispatchExecute, isEitherType, previewExecutePayload } from 'components/collections/actions/actions'
@@ -115,6 +116,13 @@ export const CollectionActions = ({
subtitle: 'Address of the recipient',
})
const creatorState = useInputState({
id: 'creator-address',
name: 'creator',
title: 'Creator Address',
subtitle: 'Address of the creator',
})
const tokenURIState = useInputState({
id: 'token-uri',
name: 'tokenURI',
@@ -217,6 +225,7 @@ export const CollectionActions = ({
])
const showPriceField = isEitherType(type, ['update_mint_price', 'update_discount_price'])
const showDescriptionField = type === 'update_collection_info'
const showCreatorField = type === 'update_collection_info'
const showImageField = type === 'update_collection_info'
const showExternalLinkField = type === 'update_collection_info'
const showRoyaltyRelatedFields =
@@ -289,6 +298,16 @@ export const CollectionActions = ({
void resolveRoyaltyPaymentAddress()
}, [royaltyPaymentAddressState.value])
const resolveCreatorAddress = async () => {
await resolveAddress(creatorState.value.trim(), wallet).then((resolvedAddress) => {
creatorState.onChange(resolvedAddress)
})
}
useEffect(() => {
void resolveCreatorAddress()
}, [creatorState.value])
useEffect(() => {
setCollectionInfo({
description: descriptionState.value.replaceAll('\\n', '\n') || undefined,
@@ -302,6 +321,7 @@ export const CollectionActions = ({
share: (Number(royaltyShareState.value) / 100).toString(),
}
: undefined,
creator: creatorState.value || undefined,
})
}, [
descriptionState.value,
@@ -310,6 +330,7 @@ export const CollectionActions = ({
externalLinkState.value,
royaltyPaymentAddressState.value,
royaltyShareState.value,
creatorState.value,
])
useEffect(() => {
@@ -438,6 +459,27 @@ export const CollectionActions = ({
}
}
if (type === 'update_collection_info' && creatorState.value) {
const resolvedCreatorAddress = await resolveAddress(creatorState.value.trim(), wallet)
const contractInfoResponse = await (await wallet.getCosmWasmClient())
.queryContractRaw(
resolvedCreatorAddress,
toUtf8(Buffer.from(Buffer.from('contract_info').toString('hex'), 'hex').toString()),
)
.catch((e) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
if (e.message.includes('bech32')) throw new Error('Invalid creator address.')
console.log(e.message)
})
if (contractInfoResponse !== undefined) {
const contractInfo = JSON.parse(new TextDecoder().decode(contractInfoResponse as Uint8Array))
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
if (contractInfo && !contractInfo.contract.includes('dao'))
throw new Error('The provided creator address does not belong to a compatible contract.')
else console.log(contractInfo)
}
}
const txHash = await toast.promise(dispatchExecute(payload), {
error: `${type.charAt(0).toUpperCase() + type.slice(1)} execute failed!`,
loading: 'Executing message...',
@@ -494,6 +536,7 @@ export const CollectionActions = ({
{showBaseUriField && <TextInput className="mt-2" {...baseURIState} />}
{showNumberOfTokensField && <NumberInput className="mt-2" {...batchNumberState} />}
{showPriceField && <NumberInput className="mt-2" {...priceState} />}
{showCreatorField && <AddressInput className="mt-2" {...creatorState} />}
{showDescriptionField && <TextInput className="my-2" {...descriptionState} />}
{showImageField && <TextInput className="mb-2" {...imageState} />}
{showExternalLinkField && <TextInput className="mb-2" {...externalLinkState} />}
@@ -664,6 +707,17 @@ export const CollectionActions = ({
</div>
</Tooltip>
</Conditional>
<Conditional test={type === 'update_collection_info'}>
<Alert className="mt-2 text-sm" type="info">
Please note that you are only required to fill in the fields you want to update.
</Alert>
</Conditional>
<Conditional test={type === 'update_discount_price'}>
<Alert className="mt-2 text-sm" type="warning">
Please note that discount price can only be updated every 24 hours and be removed 12 hours after its last
update.
</Alert>
</Conditional>
</div>
<div className="-mt-6">
<div className="relative mb-2">
@@ -20,6 +20,7 @@ import type { ChangeEvent } from 'react'
import { useEffect, useRef, useState } from 'react'
import { toast } from 'react-hot-toast'
import type { UploadServiceType } from 'services/upload'
import { NFT_STORAGE_DEFAULT_API_KEY } from 'utils/constants'
import type { AssetType } from 'utils/getAssetType'
import { getAssetType } from 'utils/getAssetType'
import { uid } from 'utils/random'
@@ -66,6 +67,7 @@ export const UploadDetails = ({
const [uploadService, setUploadService] = useState<UploadServiceType>('nft-storage')
const [metadataFileArrayIndex, setMetadataFileArrayIndex] = useState(0)
const [refreshMetadata, setRefreshMetadata] = useState(false)
const [useDefaultApiKey, setUseDefaultApiKey] = useState(false)
const [baseMinterMetadataFile, setBaseMinterMetadataFile] = useState<File | undefined>()
@@ -461,6 +463,14 @@ export const UploadDetails = ({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [importedUploadDetails])
useEffect(() => {
if (useDefaultApiKey) {
nftStorageApiKeyState.onChange(NFT_STORAGE_DEFAULT_API_KEY || '')
} else {
nftStorageApiKeyState.onChange('')
}
}, [useDefaultApiKey])
return (
<div className="justify-items-start mb-3 rounded border-2 border-white/20 flex-column">
<div className="flex justify-center">
@@ -616,7 +626,22 @@ export const UploadDetails = ({
<div className="flex w-full">
<Conditional test={uploadService === 'nft-storage'}>
<TextInput {...nftStorageApiKeyState} className="w-full" />
<div className="flex-col w-full">
<TextInput {...nftStorageApiKeyState} className="w-full" disabled={useDefaultApiKey} />
<div className="flex-row mt-2 w-full form-control">
<label className="cursor-pointer label">
<span className="mr-2 font-bold">Use Default API Key</span>
<input
checked={useDefaultApiKey}
className={`${useDefaultApiKey ? `bg-stargaze` : `bg-gray-600`} checkbox`}
onClick={() => {
setUseDefaultApiKey(!useDefaultApiKey)
}}
type="checkbox"
/>
</label>
</div>
</div>
</Conditional>
<Conditional test={uploadService === 'pinata'}>
<TextInput {...pinataApiKeyState} className="w-full" />
@@ -1,6 +1,7 @@
/* eslint-disable eslint-comments/disable-enable-pair */
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
/* eslint-disable no-nested-ternary */
import { Button } from 'components/Button'
import { FormControl } from 'components/FormControl'
import { FormGroup } from 'components/FormGroup'
import { AddressList } from 'components/forms/AddressList'
@@ -42,7 +43,7 @@ export interface WhitelistDetailsDataProps {
type WhitelistState = 'none' | 'existing' | 'new'
type WhitelistType = 'standard' | 'flex'
export type WhitelistType = 'standard' | 'flex' | 'merkletree'
export const WhitelistDetails = ({
onChange,
@@ -58,6 +59,7 @@ export const WhitelistDetails = ({
const [endDate, setEndDate] = useState<Date | undefined>(undefined)
const [whitelistStandardArray, setWhitelistStandardArray] = useState<string[]>([])
const [whitelistFlexArray, setWhitelistFlexArray] = useState<WhitelistFlexMember[]>([])
const [whitelistMerkleTreeArray, setWhitelistMerkleTreeArray] = useState<string[]>([])
const [adminsMutable, setAdminsMutable] = useState<boolean>(true)
const whitelistAddressState = useInputState({
@@ -96,17 +98,41 @@ export const WhitelistDetails = ({
const addressListState = useAddressListState()
const whitelistFileOnChange = (data: string[]) => {
setWhitelistStandardArray(data)
if (whitelistType === 'standard') setWhitelistStandardArray(data)
if (whitelistType === 'merkletree') setWhitelistMerkleTreeArray(data)
}
const whitelistFlexFileOnChange = (whitelistData: WhitelistFlexMember[]) => {
setWhitelistFlexArray(whitelistData)
}
const downloadSampleWhitelistFlexFile = () => {
const csvData =
'address,mint_count\nstars153w5xhuqu3et29lgqk4dsynj6gjn96lr33wx4e,3\nstars1xkes5r2k8u3m3ayfpverlkcrq3k4jhdk8ws0uz,1\nstars1s8qx0zvz8yd6e4x0mqmqf7fr9vvfn622wtp3g3,2'
const blob = new Blob([csvData], { type: 'text/csv' })
const url = window.URL.createObjectURL(blob)
const a = document.createElement('a')
a.setAttribute('href', url)
a.setAttribute('download', 'sample_whitelist_flex.csv')
a.click()
}
const downloadSampleWhitelistFile = () => {
const txtData =
'stars153w5xhuqu3et29lgqk4dsynj6gjn96lr33wx4e\nstars1xkes5r2k8u3m3ayfpverlkcrq3k4jhdk8ws0uz\nstars1s8qx0zvz8yd6e4x0mqmqf7fr9vvfn622wtp3g3'
const blob = new Blob([txtData], { type: 'text/txt' })
const url = window.URL.createObjectURL(blob)
const a = document.createElement('a')
a.setAttribute('href', url)
a.setAttribute('download', 'sample_whitelist.txt')
a.click()
}
useEffect(() => {
if (!importedWhitelistDetails) {
setWhitelistStandardArray([])
setWhitelistFlexArray([])
setWhitelistMerkleTreeArray([])
}
}, [whitelistType])
@@ -120,7 +146,12 @@ export const WhitelistDetails = ({
.replace(/"/g, '')
.replace(/'/g, '')
.replace(/ /g, ''),
members: whitelistType === 'standard' ? whitelistStandardArray : whitelistFlexArray,
members:
whitelistType === 'standard'
? whitelistStandardArray
: whitelistType === 'merkletree'
? whitelistMerkleTreeArray
: whitelistFlexArray,
unitPrice: unitPriceState.value
? (Number(unitPriceState.value) * 1_000_000).toString()
: unitPriceState.value === 0
@@ -150,7 +181,9 @@ export const WhitelistDetails = ({
endDate,
whitelistStandardArray,
whitelistFlexArray,
whitelistMerkleTreeArray,
whitelistState,
whitelistType,
addressListState.values,
adminsMutable,
])
@@ -188,7 +221,12 @@ export const WhitelistDetails = ({
importedWhitelistDetails.members?.forEach((member) => {
setWhitelistStandardArray((standardArray) => [...standardArray, member as string])
})
} else {
} else if (importedWhitelistDetails.whitelistType === 'merkletree') {
setWhitelistMerkleTreeArray([])
// importedWhitelistDetails.members?.forEach((member) => {
// setWhitelistMerkleTreeArray((merkleTreeArray) => [...merkleTreeArray, member as string])
// })
} else if (importedWhitelistDetails.whitelistType === 'flex') {
setWhitelistFlexArray([])
importedWhitelistDetails.members?.forEach((member) => {
setWhitelistFlexArray((flexArray) => [
@@ -280,7 +318,7 @@ export const WhitelistDetails = ({
</Conditional>
<Conditional test={whitelistState === 'new'}>
<div className="flex justify-between mb-5 ml-6 max-w-[300px] text-lg font-bold">
<div className="flex justify-between mb-5 ml-6 max-w-[500px] text-lg font-bold">
<div className="form-check form-check-inline">
<input
checked={whitelistType === 'standard'}
@@ -291,7 +329,7 @@ export const WhitelistDetails = ({
setWhitelistType('standard')
}}
type="radio"
value="nft-storage"
value="standard"
/>
<label
className="inline-block py-1 px-2 text-gray peer-checked:text-white hover:text-white peer-checked:bg-black hover:rounded-sm peer-checked:border-b-2 hover:border-b-2 peer-checked:border-plumbus hover:border-plumbus cursor-pointer form-check-label"
@@ -320,12 +358,33 @@ export const WhitelistDetails = ({
Whitelist Flex
</label>
</div>
<div className="form-check form-check-inline">
<input
checked={whitelistType === 'merkletree'}
className="peer sr-only"
id="inlineRadio9"
name="inlineRadioOptions9"
onClick={() => {
setWhitelistType('merkletree')
}}
type="radio"
value="merkletree"
/>
<label
className="inline-block py-1 px-2 text-gray peer-checked:text-white hover:text-white peer-checked:bg-black hover:rounded-sm peer-checked:border-b-2 hover:border-b-2 peer-checked:border-plumbus hover:border-plumbus cursor-pointer form-check-label"
htmlFor="inlineRadio9"
>
Whitelist Merkle Tree
</label>
</div>
</div>
<div className="grid grid-cols-2">
<FormGroup subtitle="Information about your minting settings" title="Whitelist Minting Details">
<NumberInput isRequired {...unitPriceState} />
<NumberInput isRequired {...memberLimitState} />
<Conditional test={whitelistType === 'standard'}>
<Conditional test={whitelistType !== 'merkletree'}>
<NumberInput isRequired {...memberLimitState} />
</Conditional>
<Conditional test={whitelistType === 'standard' || whitelistType === 'merkletree'}>
<NumberInput isRequired {...perAddressLimitState} />
</Conditional>
<FormControl
@@ -408,7 +467,17 @@ export const WhitelistDetails = ({
/>
</div>
<Conditional test={whitelistType === 'standard'}>
<FormGroup subtitle="TXT file that contains the whitelisted addresses" title="Whitelist File">
<FormGroup
subtitle={
<div>
<span>TXT file that contains the whitelisted addresses</span>
<Button className="mt-2 text-sm text-white" onClick={downloadSampleWhitelistFile}>
Download Sample File
</Button>
</div>
}
title="Whitelist File"
>
<WhitelistUpload onChange={whitelistFileOnChange} />
</FormGroup>
<Conditional test={whitelistStandardArray.length > 0}>
@@ -417,7 +486,14 @@ export const WhitelistDetails = ({
</Conditional>
<Conditional test={whitelistType === 'flex'}>
<FormGroup
subtitle="CSV file that contains the whitelisted addresses and their corresponding mint counts"
subtitle={
<div>
<span>CSV file that contains the whitelisted addresses and corresponding mint counts</span>
<Button className="mt-2 text-sm text-white" onClick={downloadSampleWhitelistFlexFile}>
Download Sample File
</Button>
</div>
}
title="Whitelist File"
>
<WhitelistFlexUpload onChange={whitelistFlexFileOnChange} />
@@ -426,6 +502,24 @@ export const WhitelistDetails = ({
<JsonPreview content={whitelistFlexArray} initialState={false} title="File Contents" />
</Conditional>
</Conditional>
<Conditional test={whitelistType === 'merkletree'}>
<FormGroup
subtitle={
<div>
<span>TXT file that contains the whitelisted addresses</span>
<Button className="mt-2 text-sm text-white" onClick={downloadSampleWhitelistFile}>
Download Sample File
</Button>
</div>
}
title="Whitelist File"
>
<WhitelistUpload onChange={whitelistFileOnChange} />
</FormGroup>
<Conditional test={whitelistStandardArray.length > 0}>
<JsonPreview content={whitelistStandardArray} initialState title="File Contents" />
</Conditional>
</Conditional>
</div>
</div>
</Conditional>
@@ -1,8 +1,11 @@
/* eslint-disable eslint-comments/disable-enable-pair */
/* eslint-disable no-nested-ternary */
import { Combobox, Transition } from '@headlessui/react'
import clsx from 'clsx'
import { FormControl } from 'components/FormControl'
import type { ExecuteListItem } from 'contracts/whitelist/messages/execute'
import { EXECUTE_LIST } from 'contracts/whitelist/messages/execute'
import { EXECUTE_LIST as WL_MERKLE_TREE_EXECUTE_LIST } from 'contracts/whitelistMerkleTree/messages/execute'
import { matchSorter } from 'match-sorter'
import { Fragment, useState } from 'react'
import { FaChevronDown, FaInfoCircle } from 'react-icons/fa'
@@ -10,13 +13,20 @@ import { FaChevronDown, FaInfoCircle } from 'react-icons/fa'
export interface ExecuteComboboxProps {
value: ExecuteListItem | null
onChange: (item: ExecuteListItem) => void
whitelistType?: 'standard' | 'flex' | 'merkletree'
}
export const ExecuteCombobox = ({ value, onChange }: ExecuteComboboxProps) => {
export const ExecuteCombobox = ({ value, onChange, whitelistType }: ExecuteComboboxProps) => {
const [search, setSearch] = useState('')
const filtered =
search === '' ? EXECUTE_LIST : matchSorter(EXECUTE_LIST, search, { keys: ['id', 'name', 'description'] })
whitelistType !== 'merkletree'
? search === ''
? EXECUTE_LIST
: matchSorter(EXECUTE_LIST, search, { keys: ['id', 'name', 'description'] })
: search === ''
? WL_MERKLE_TREE_EXECUTE_LIST
: matchSorter(WL_MERKLE_TREE_EXECUTE_LIST, search, { keys: ['id', 'name', 'description'] })
return (
<Combobox
+26 -1
View File
@@ -14,6 +14,7 @@ import type { ChangeEvent } from 'react'
import { useEffect, useMemo, useRef, useState } from 'react'
import { toast } from 'react-hot-toast'
import type { UploadServiceType } from 'services/upload'
import { NFT_STORAGE_DEFAULT_API_KEY } from 'utils/constants'
import type { AssetType } from 'utils/getAssetType'
import { getAssetType } from 'utils/getAssetType'
@@ -43,6 +44,7 @@ export const ImageUploadDetails = ({ onChange, importedImageUploadDetails }: Ima
const [isThumbnailCompatible, setIsThumbnailCompatible] = useState<boolean>(false)
const [uploadMethod, setUploadMethod] = useState<UploadMethod>('new')
const [uploadService, setUploadService] = useState<UploadServiceType>('nft-storage')
const [useDefaultApiKey, setUseDefaultApiKey] = useState(false)
const assetFileRef = useRef<HTMLInputElement | null>(null)
const thumbnailFileRef = useRef<HTMLInputElement | null>(null)
@@ -192,6 +194,14 @@ export const ImageUploadDetails = ({ onChange, importedImageUploadDetails }: Ima
}
}, [importedImageUploadDetails])
useEffect(() => {
if (useDefaultApiKey) {
nftStorageApiKeyState.onChange(NFT_STORAGE_DEFAULT_API_KEY || '')
} else {
nftStorageApiKeyState.onChange('')
}
}, [useDefaultApiKey])
const previewUrl = imageUrlState.value.toLowerCase().trim().startsWith('ipfs://')
? `https://ipfs-gw.stargaze-apis.com/ipfs/${imageUrlState.value.substring(7)}`
: imageUrlState.value
@@ -343,7 +353,22 @@ export const ImageUploadDetails = ({ onChange, importedImageUploadDetails }: Ima
<div className="flex w-full">
<Conditional test={uploadService === 'nft-storage'}>
<TextInput {...nftStorageApiKeyState} className="w-full" />
<div className="flex-col w-full">
<TextInput {...nftStorageApiKeyState} className="w-full" disabled={useDefaultApiKey} />
<div className="flex-row mt-2 w-full form-control">
<label className="cursor-pointer label">
<span className="mr-2 font-bold">Use Default API Key</span>
<input
checked={useDefaultApiKey}
className={`${useDefaultApiKey ? `bg-stargaze` : `bg-gray-600`} checkbox`}
onClick={() => {
setUseDefaultApiKey(!useDefaultApiKey)
}}
type="checkbox"
/>
</label>
</div>
</div>
</Conditional>
<Conditional test={uploadService === 'pinata'}>
<TextInput {...pinataApiKeyState} className="w-full" />
+104 -28
View File
@@ -1,6 +1,7 @@
/* eslint-disable eslint-comments/disable-enable-pair */
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
/* eslint-disable no-nested-ternary */
import { Conditional } from 'components/Conditional'
import { FormControl } from 'components/FormControl'
import { FormGroup } from 'components/FormGroup'
import { useInputState, useNumberInputState } from 'components/forms/FormInput.hooks'
@@ -16,21 +17,27 @@ import { useWallet } from 'utils/wallet'
import { NumberInput, TextInput } from '../forms/FormInput'
import type { UploadMethod } from './OffChainMetadataUploadDetails'
export type LimitType = 'count_limited' | 'time_limited' | 'time_and_count_limited'
interface MintingDetailsProps {
onChange: (data: MintingDetailsDataProps) => void
uploadMethod: UploadMethod
minimumMintPrice: number
mintTokenFromFactory?: TokenInfo | undefined
importedMintingDetails?: MintingDetailsDataProps
isPresale: boolean
whitelistStartDate?: string
}
export interface MintingDetailsDataProps {
unitPrice: string
perAddressLimit: number
startTime: string
endTime: string
endTime?: string
tokenCountLimit?: number
paymentAddress?: string
selectedMintToken?: TokenInfo
limitType: LimitType
}
export const MintingDetails = ({
@@ -39,6 +46,8 @@ export const MintingDetails = ({
minimumMintPrice,
mintTokenFromFactory,
importedMintingDetails,
isPresale,
whitelistStartDate,
}: MintingDetailsProps) => {
const wallet = useWallet()
@@ -46,6 +55,7 @@ export const MintingDetails = ({
const [endTimestamp, setEndTimestamp] = useState<Date | undefined>()
const [selectedMintToken, setSelectedMintToken] = useState<TokenInfo | undefined>(stars)
const [mintingDetailsImported, setMintingDetailsImported] = useState(false)
const [limitType, setLimitType] = useState<LimitType>('time_limited')
const { timezone } = useGlobalSettings()
const unitPriceState = useNumberInputState({
@@ -66,6 +76,14 @@ export const MintingDetails = ({
placeholder: '1',
})
const tokenCountLimitState = useNumberInputState({
id: 'tokencountlimit',
name: 'tokencountlimit',
title: 'Maximum Token Count',
subtitle: 'Total number of mintable tokens',
placeholder: '100',
})
const paymentAddressState = useInputState({
id: 'payment-address',
name: 'paymentAddress',
@@ -95,9 +113,19 @@ export const MintingDetails = ({
: '',
perAddressLimit: perAddressLimitState.value,
startTime: timestamp ? (timestamp.getTime() * 1_000_000).toString() : '',
endTime: endTimestamp ? (endTimestamp.getTime() * 1_000_000).toString() : '',
endTime:
limitType === 'time_limited' || limitType === 'time_and_count_limited'
? endTimestamp
? (endTimestamp.getTime() * 1_000_000).toString()
: ''
: undefined,
paymentAddress: paymentAddressState.value.trim(),
selectedMintToken,
limitType,
tokenCountLimit:
limitType === 'count_limited' || limitType === 'time_and_count_limited'
? tokenCountLimitState.value
: undefined,
}
onChange(data)
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -108,6 +136,8 @@ export const MintingDetails = ({
endTimestamp,
paymentAddressState.value,
selectedMintToken,
tokenCountLimitState.value,
limitType,
])
useEffect(() => {
@@ -115,6 +145,8 @@ export const MintingDetails = ({
console.log('Selected Token ID: ', importedMintingDetails.selectedMintToken?.id)
unitPriceState.onChange(Number(importedMintingDetails.unitPrice) / 1000000)
perAddressLimitState.onChange(importedMintingDetails.perAddressLimit)
setLimitType(importedMintingDetails.limitType)
tokenCountLimitState.onChange(importedMintingDetails.tokenCountLimit ? importedMintingDetails.tokenCountLimit : 0)
setTimestamp(new Date(Number(importedMintingDetails.startTime) / 1_000_000))
setEndTimestamp(new Date(Number(importedMintingDetails.endTime) / 1_000_000))
paymentAddressState.onChange(importedMintingDetails.paymentAddress ? importedMintingDetails.paymentAddress : '')
@@ -124,6 +156,12 @@ export const MintingDetails = ({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [importedMintingDetails])
useEffect(() => {
if (isPresale) {
setTimestamp(whitelistStartDate ? new Date(Number(whitelistStartDate) / 1_000_000) : undefined)
}
}, [whitelistStartDate, isPresale])
return (
<div className="border-l-[1px] border-gray-500 border-opacity-20">
<FormGroup subtitle="Information about your minting settings" title="Minting Details">
@@ -152,6 +190,7 @@ export const MintingDetails = ({
title="Start Time"
>
<InputDateTime
disabled={isPresale}
minDate={
timezone === 'Local' ? new Date() : new Date(Date.now() + new Date().getTimezoneOffset() * 60 * 1000)
}
@@ -171,32 +210,69 @@ export const MintingDetails = ({
}
/>
</FormControl>
<FormControl
htmlId="endTimestamp"
isRequired
subtitle={`Minting end time ${timezone === 'Local' ? '(local)' : '(UTC)'}`}
title="End Time"
>
<InputDateTime
minDate={
timezone === 'Local' ? new Date() : new Date(Date.now() + new Date().getTimezoneOffset() * 60 * 1000)
}
onChange={(date) =>
date
? setEndTimestamp(
timezone === 'Local' ? date : new Date(date.getTime() - new Date().getTimezoneOffset() * 60 * 1000),
)
: setEndTimestamp(undefined)
}
value={
timezone === 'Local'
? endTimestamp
: endTimestamp
? new Date(endTimestamp.getTime() + new Date().getTimezoneOffset() * 60 * 1000)
: undefined
}
/>
</FormControl>
<div className="flex-row mt-2 w-full form-control">
<h1 className="mt-2 font-bold text-md">Limit Type: </h1>
<label className="justify-start ml-6 cursor-pointer label">
<span className="mr-2">Time</span>
<input
checked={limitType === 'time_limited' || limitType === 'time_and_count_limited'}
className={`${limitType === 'time_limited' ? `bg-stargaze` : `bg-gray-600`} checkbox`}
onClick={() => {
if (limitType === 'time_and_count_limited') setLimitType('count_limited' as LimitType)
else if (limitType === 'count_limited') setLimitType('time_and_count_limited' as LimitType)
else setLimitType('count_limited' as LimitType)
}}
type="checkbox"
/>
</label>
<label className="justify-start ml-4 cursor-pointer label">
<span className="mr-2">Token Count</span>
<input
checked={limitType === 'count_limited' || limitType === 'time_and_count_limited'}
className={`${limitType === 'count_limited' ? `bg-stargaze` : `bg-gray-600`} checkbox`}
onClick={() => {
if (limitType === 'time_and_count_limited') setLimitType('time_limited' as LimitType)
else if (limitType === 'time_limited') setLimitType('time_and_count_limited' as LimitType)
else setLimitType('time_limited' as LimitType)
}}
type="checkbox"
/>
</label>
</div>
<Conditional test={limitType === 'time_limited' || limitType === 'time_and_count_limited'}>
<FormControl
htmlId="endTimestamp"
isRequired
subtitle={`Minting end time ${timezone === 'Local' ? '(local)' : '(UTC)'}`}
title="End Time"
>
<InputDateTime
minDate={
timezone === 'Local' ? new Date() : new Date(Date.now() + new Date().getTimezoneOffset() * 60 * 1000)
}
onChange={(date) =>
date
? setEndTimestamp(
timezone === 'Local'
? date
: new Date(date.getTime() - new Date().getTimezoneOffset() * 60 * 1000),
)
: setEndTimestamp(undefined)
}
value={
timezone === 'Local'
? endTimestamp
: endTimestamp
? new Date(endTimestamp.getTime() + new Date().getTimezoneOffset() * 60 * 1000)
: undefined
}
/>
</FormControl>
</Conditional>
<Conditional test={limitType === 'count_limited' || limitType === 'time_and_count_limited'}>
<NumberInput {...tokenCountLimitState} isRequired />
</Conditional>
</FormGroup>
<TextInput className="pr-4 pl-4 mt-3" {...paymentAddressState} />
</div>
@@ -18,6 +18,7 @@ import type { ChangeEvent } from 'react'
import { useEffect, useRef, useState } from 'react'
import { toast } from 'react-hot-toast'
import type { UploadServiceType } from 'services/upload'
import { NFT_STORAGE_DEFAULT_API_KEY } from 'utils/constants'
import type { AssetType } from 'utils/getAssetType'
import { getAssetType } from 'utils/getAssetType'
import { uid } from 'utils/random'
@@ -64,6 +65,7 @@ export const OffChainMetadataUploadDetails = ({
const [refreshMetadata, setRefreshMetadata] = useState(false)
const [exportedMetadata, setExportedMetadata] = useState(undefined)
const [openEditionMinterMetadataFile, setOpenEditionMinterMetadataFile] = useState<File | undefined>()
const [useDefaultApiKey, setUseDefaultApiKey] = useState(false)
const thumbnailCompatibleAssetTypes: AssetType[] = ['video', 'audio', 'html', 'document']
@@ -297,6 +299,14 @@ export const OffChainMetadataUploadDetails = ({
}
}, [importedOffChainMetadataUploadDetails])
useEffect(() => {
if (useDefaultApiKey) {
nftStorageApiKeyState.onChange(NFT_STORAGE_DEFAULT_API_KEY || '')
} else {
nftStorageApiKeyState.onChange('')
}
}, [useDefaultApiKey])
return (
<div className="justify-items-start mb-3 rounded border-2 border-white/20 flex-column">
<div className="flex justify-center">
@@ -415,7 +425,22 @@ export const OffChainMetadataUploadDetails = ({
<div className="flex w-full">
<Conditional test={uploadService === 'nft-storage'}>
<TextInput {...nftStorageApiKeyState} className="w-full" />
<div className="flex-col w-full">
<TextInput {...nftStorageApiKeyState} className="w-full" disabled={useDefaultApiKey} />
<div className="flex-row mt-2 w-full form-control">
<label className="cursor-pointer label">
<span className="mr-2 font-bold">Use Default API Key</span>
<input
checked={useDefaultApiKey}
className={`${useDefaultApiKey ? `bg-stargaze` : `bg-gray-600`} checkbox`}
onClick={() => {
setUseDefaultApiKey(!useDefaultApiKey)
}}
type="checkbox"
/>
</label>
</div>
</div>
</Conditional>
<Conditional test={uploadService === 'pinata'}>
<TextInput {...pinataApiKeyState} className="w-full" />
@@ -6,13 +6,13 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { toUtf8 } from '@cosmjs/encoding'
import { coin } from '@cosmjs/proto-signing'
import axios from 'axios'
import clsx from 'clsx'
import { Button } from 'components/Button'
import type { MinterType } from 'components/collections/actions/Combobox'
import { Conditional } from 'components/Conditional'
import { ConfirmationModal } from 'components/ConfirmationModal'
import { LoadingModal } from 'components/LoadingModal'
import { openEditionMinterList } from 'config/minter'
import { type TokenInfo } from 'config/token'
import { useContracts } from 'contexts/contracts'
import { addLogItem } from 'contexts/log'
@@ -22,12 +22,15 @@ import React, { useEffect, useMemo, useState } from 'react'
import { toast } from 'react-hot-toast'
import { upload } from 'services/upload'
import {
OPEN_EDITION_FACTORY_ADDRESS,
OPEN_EDITION_UPDATABLE_FACTORY_ADDRESS,
SG721_OPEN_EDITION_CODE_ID,
SG721_OPEN_EDITION_UPDATABLE_CODE_ID,
STRDST_SG721_CODE_ID,
WHITELIST_CODE_ID,
WHITELIST_FLEX_CODE_ID,
WHITELIST_MERKLE_TREE_API_URL,
WHITELIST_MERKLE_TREE_CODE_ID,
} from 'utils/constants'
import { useDebounce } from 'utils/debounce'
import type { AssetType } from 'utils/getAssetType'
import { isValidAddress } from 'utils/isValidAddress'
import { checkTokenUri } from 'utils/isValidTokenUri'
@@ -37,7 +40,7 @@ import { useWallet } from 'utils/wallet'
import { type CollectionDetailsDataProps, CollectionDetails } from './CollectionDetails'
import type { ImageUploadDetailsDataProps } from './ImageUploadDetails'
import { ImageUploadDetails } from './ImageUploadDetails'
import type { MintingDetailsDataProps } from './MintingDetails'
import type { LimitType, MintingDetailsDataProps } from './MintingDetails'
import { MintingDetails } from './MintingDetails'
import type { UploadMethod } from './OffChainMetadataUploadDetails'
import {
@@ -47,12 +50,14 @@ import {
import type { OnChainMetadataInputDetailsDataProps } from './OnChainMetadataInputDetails'
import { OnChainMetadataInputDetails } from './OnChainMetadataInputDetails'
import { type RoyaltyDetailsDataProps, RoyaltyDetails } from './RoyaltyDetails'
import { type WhitelistDetailsDataProps, WhitelistDetails } from './WhitelistDetails'
export type MetadataStorageMethod = 'off-chain' | 'on-chain'
export interface OpenEditionMinterDetailsDataProps {
imageUploadDetails?: ImageUploadDetailsDataProps
collectionDetails?: CollectionDetailsDataProps
whitelistDetails?: WhitelistDetailsDataProps
royaltyDetails?: RoyaltyDetailsDataProps
onChainMetadataInputDetails?: OnChainMetadataInputDetailsDataProps
offChainMetadataUploadDetails?: OffChainMetadataUploadDetailsDataProps
@@ -62,24 +67,26 @@ export interface OpenEditionMinterDetailsDataProps {
coverImageUrl?: string | null
tokenUri?: string | null
tokenImageUri?: string | null
isRefreshed?: boolean
}
interface OpenEditionMinterCreatorProps {
onChange: (data: OpenEditionMinterCreatorDataProps) => void
onDetailsChange: (data: OpenEditionMinterDetailsDataProps) => void
openEditionMinterUpdatableCreationFee?: string
openEditionMinterCreationFee?: string
minimumMintPrice?: string
minimumUpdatableMintPrice?: string
minterType?: MinterType
mintTokenFromFactory?: TokenInfo | undefined
importedOpenEditionMinterDetails?: OpenEditionMinterDetailsDataProps
isMatchingFactoryPresent?: boolean
openEditionFactoryAddress?: string
}
export interface OpenEditionMinterCreatorDataProps {
metadataStorageMethod: MetadataStorageMethod
openEditionMinterContractAddress: string | null
sg721ContractAddress: string | null
whitelistContractAddress: string | null
transactionHash: string | null
}
@@ -87,21 +94,27 @@ export const OpenEditionMinterCreator = ({
onChange,
onDetailsChange,
openEditionMinterCreationFee,
openEditionMinterUpdatableCreationFee,
minimumMintPrice,
minimumUpdatableMintPrice,
minterType,
mintTokenFromFactory,
importedOpenEditionMinterDetails,
isMatchingFactoryPresent,
openEditionFactoryAddress,
}: OpenEditionMinterCreatorProps) => {
const wallet = useWallet()
const { openEditionMinter: openEditionMinterContract, openEditionFactory: openEditionFactoryContract } =
useContracts()
const {
openEditionMinter: openEditionMinterContract,
openEditionFactory: openEditionFactoryContract,
whitelist: whitelistContract,
whitelistMerkleTree: whitelistMerkleTreeContract,
} = useContracts()
const [metadataStorageMethod, setMetadataStorageMethod] = useState<MetadataStorageMethod>('off-chain')
const [imageUploadDetails, setImageUploadDetails] = useState<ImageUploadDetailsDataProps | null>(null)
const [collectionDetails, setCollectionDetails] = useState<CollectionDetailsDataProps | null>(null)
const [whitelistDetails, setWhitelistDetails] = useState<WhitelistDetailsDataProps | null>(null)
const [royaltyDetails, setRoyaltyDetails] = useState<RoyaltyDetailsDataProps | null>(null)
const [isRefreshed, setIsRefreshed] = useState(false)
const [onChainMetadataInputDetails, setOnChainMetadataInputDetails] =
useState<OnChainMetadataInputDetailsDataProps | null>(null)
const [offChainMetadataUploadDetails, setOffChainMetadataUploadDetails] =
@@ -116,29 +129,19 @@ export const OpenEditionMinterCreator = ({
const [coverImageUrl, setCoverImageUrl] = useState<string | null>(null)
const [openEditionMinterContractAddress, setOpenEditionMinterContractAddress] = useState<string | null>(null)
const [sg721ContractAddress, setSg721ContractAddress] = useState<string | null>(null)
const [whitelistContractAddress, setWhitelistContractAddress] = useState<string | null>(null)
const [transactionHash, setTransactionHash] = useState<string | null>(null)
const [thumbnailImageUri, setThumbnailImageUri] = useState<string | undefined>(undefined)
const thumbnailCompatibleAssetTypes: AssetType[] = ['video', 'audio', 'html']
const factoryAddressForSelectedDenom =
openEditionMinterList.find((minter) => minter.supportedToken === mintTokenFromFactory && minter.updatable === false)
?.factoryAddress || OPEN_EDITION_FACTORY_ADDRESS
const updatableFactoryAddressForSelectedDenom =
openEditionMinterList.find((minter) => minter.supportedToken === mintTokenFromFactory && minter.updatable === true)
?.factoryAddress || OPEN_EDITION_UPDATABLE_FACTORY_ADDRESS
const openEditionFactoryMessages = useMemo(
() =>
openEditionFactoryContract?.use(
collectionDetails?.updatable ? updatableFactoryAddressForSelectedDenom : factoryAddressForSelectedDenom,
),
() => openEditionFactoryContract?.use(openEditionFactoryAddress as string),
[
openEditionFactoryContract,
wallet.address,
collectionDetails?.updatable,
factoryAddressForSelectedDenom,
updatableFactoryAddressForSelectedDenom,
openEditionFactoryAddress,
wallet.isWalletConnected,
],
)
@@ -152,13 +155,26 @@ export const OpenEditionMinterCreator = ({
.then(() => {
void checkRoyaltyDetails()
.then(() => {
void checkwalletBalance()
checkWhitelistDetails()
.then(() => {
setReadyToCreate(true)
void checkwalletBalance()
.then(() => {
setReadyToCreate(true)
})
.catch((error: any) => {
toast.error(`Error in Wallet Balance: ${error.message}`, { style: { maxWidth: 'none' } })
addLogItem({ id: uid(), message: error.message, type: 'Error', timestamp: new Date() })
setReadyToCreate(false)
})
})
.catch((error: any) => {
toast.error(`Error in Wallet Balance: ${error.message}`, { style: { maxWidth: 'none' } })
addLogItem({ id: uid(), message: error.message, type: 'Error', timestamp: new Date() })
.catch((error) => {
if (String(error.message).includes('Insufficient wallet balance')) {
toast.error(`${error.message}`, { style: { maxWidth: 'none' } })
addLogItem({ id: uid(), message: error.message, type: 'Error', timestamp: new Date() })
} else {
toast.error(`Error in Whitelist Configuration: ${error.message}`, { style: { maxWidth: 'none' } })
addLogItem({ id: uid(), message: error.message, type: 'Error', timestamp: new Date() })
}
setReadyToCreate(false)
})
})
@@ -299,9 +315,9 @@ export const OpenEditionMinterCreator = ({
if (!mintingDetails) throw new Error('Please fill out the minting details')
if (mintingDetails.unitPrice === '') throw new Error('Mint price is required')
if (collectionDetails?.updatable) {
if (Number(mintingDetails.unitPrice) < Number(minimumUpdatableMintPrice))
if (Number(mintingDetails.unitPrice) < Number(minimumMintPrice))
throw new Error(
`Invalid mint price: The minimum mint price is ${Number(minimumUpdatableMintPrice) / 1000000} ${
`Invalid mint price: The minimum mint price is ${Number(minimumMintPrice) / 1000000} ${
mintTokenFromFactory?.displayName
}`,
)
@@ -314,11 +330,27 @@ export const OpenEditionMinterCreator = ({
if (!mintingDetails.perAddressLimit || mintingDetails.perAddressLimit < 1 || mintingDetails.perAddressLimit > 50)
throw new Error('Invalid limit for tokens per address')
if (mintingDetails.startTime === '') throw new Error('Start time is required')
if (mintingDetails.endTime === '') throw new Error('End time is required')
if (mintingDetails.limitType === 'time_limited' && mintingDetails.endTime === '')
throw new Error('End time is required')
if (mintingDetails.limitType === 'count_limited' && mintingDetails.tokenCountLimit === undefined)
throw new Error('Token count limit is required')
if (
mintingDetails.limitType === 'count_limited' &&
mintingDetails.perAddressLimit > (mintingDetails.tokenCountLimit as number)
)
throw new Error('Per address limit cannot exceed maximum token count limit')
if (mintingDetails.limitType === 'count_limited' && (mintingDetails.tokenCountLimit as number) > 10000)
throw new Error('Maximum token count cannot exceed 10000')
if (Number(mintingDetails.startTime) < new Date().getTime() * 1000000) throw new Error('Invalid start time')
if (Number(mintingDetails.endTime) < Number(mintingDetails.startTime))
if (
mintingDetails.limitType === 'time_limited' &&
Number(mintingDetails.endTime) < Number(mintingDetails.startTime)
)
throw new Error('End time cannot be earlier than start time')
if (Number(mintingDetails.endTime) === Number(mintingDetails.startTime))
if (
mintingDetails.limitType === 'time_limited' &&
Number(mintingDetails.endTime) === Number(mintingDetails.startTime)
)
throw new Error('End time cannot be equal to the start time')
if (
@@ -326,6 +358,92 @@ export const OpenEditionMinterCreator = ({
(!isValidAddress(mintingDetails.paymentAddress) || !mintingDetails.paymentAddress.startsWith('stars1'))
)
throw new Error('Invalid payment address')
if (!isMatchingFactoryPresent)
throw new Error(
`No matching open edition factory contract found for the selected parameters (Mint Price Denom: ${mintingDetails.selectedMintToken?.displayName}, Whitelist Type: ${whitelistDetails?.whitelistType})`,
)
}
const checkWhitelistDetails = async () => {
if (!whitelistDetails) throw new Error('Please fill out the whitelist details')
if (whitelistDetails.whitelistState === 'existing') {
if (whitelistDetails.contractAddress === '') throw new Error('Whitelist contract address is required')
else {
const contract = whitelistContract?.use(whitelistDetails.contractAddress)
//check if the address belongs to a whitelist contract (see performChecks())
const config = await contract?.config()
if (JSON.stringify(config).includes('whale_cap')) whitelistDetails.whitelistType = 'flex'
else if (!JSON.stringify(config).includes('member_limit') || config?.member_limit === 0) {
// whitelistDetails.whitelistType = 'merkletree'
throw new Error(
'Whitelist Merkle Tree is not supported yet. Please use a standard or flexible whitelist contract.',
)
} else whitelistDetails.whitelistType = 'standard'
if (Number(config?.start_time) !== Number(mintingDetails?.startTime)) {
const whitelistStartDate = new Date(Number(config?.start_time) / 1000000)
throw Error(`Whitelist start time (${whitelistStartDate.toLocaleString()}) does not match minting start time`)
}
if (mintingDetails?.tokenCountLimit && config?.per_address_limit) {
if (mintingDetails.tokenCountLimit >= 100 && Number(config.per_address_limit) > 50) {
throw Error(
`Invalid limit for tokens per address (${config.per_address_limit} tokens). Tokens per address limit cannot exceed 50 regardless of the total number of tokens.`,
)
} else if (
mintingDetails.tokenCountLimit >= 100 &&
Number(config.per_address_limit) > Math.ceil((mintingDetails.tokenCountLimit / 100) * 3)
) {
throw Error(
`Invalid limit for tokens per address (${config.per_address_limit} tokens). Tokens per address limit cannot exceed 3% of the total number of tokens in the collection.`,
)
} else if (mintingDetails.tokenCountLimit < 100 && Number(config.per_address_limit) > 3) {
throw Error(
`Invalid limit for tokens per address (${config.per_address_limit} tokens). Tokens per address limit cannot exceed 3 for collections with a token count limit smaller than 100 tokens.`,
)
}
}
}
} else if (whitelistDetails.whitelistState === 'new') {
if (whitelistDetails.members?.length === 0) throw new Error('Whitelist member list cannot be empty')
if (whitelistDetails.unitPrice === undefined) throw new Error('Whitelist unit price is required')
if (Number(whitelistDetails.unitPrice) < 0)
throw new Error('Invalid unit price: The unit price cannot be negative')
if (whitelistDetails.startTime === '') throw new Error('Start time is required')
if (whitelistDetails.endTime === '') throw new Error('End time is required')
if (
whitelistDetails.whitelistType === 'standard' &&
(!whitelistDetails.perAddressLimit || whitelistDetails.perAddressLimit === 0)
)
throw new Error('Per address limit is required')
if (
whitelistDetails.whitelistType !== 'merkletree' &&
(!whitelistDetails.memberLimit || whitelistDetails.memberLimit === 0)
)
throw new Error('Member limit is required')
if (Number(whitelistDetails.startTime) >= Number(whitelistDetails.endTime))
throw new Error('Whitelist start time cannot be equal to or later than the whitelist end time')
if (Number(whitelistDetails.startTime) !== Number(mintingDetails?.startTime))
throw new Error('Whitelist start time must be the same as the minting start time')
if (whitelistDetails.perAddressLimit && mintingDetails?.tokenCountLimit) {
if (mintingDetails.tokenCountLimit >= 100 && whitelistDetails.perAddressLimit > 50) {
throw Error(
`Invalid limit for tokens per address. Tokens per address limit cannot exceed 50 regardless of the total number of tokens.`,
)
} else if (
mintingDetails.tokenCountLimit >= 100 &&
whitelistDetails.perAddressLimit > Math.ceil((mintingDetails.tokenCountLimit / 100) * 3)
) {
throw Error(
`Invalid limit for tokens per address. Tokens per address limit cannot exceed 3% of the total number of tokens in the collection.`,
)
} else if (mintingDetails.tokenCountLimit < 100 && whitelistDetails.perAddressLimit > 3) {
throw Error(
`Invalid limit for tokens per address. Tokens per address limit cannot exceed 3 for collections with a token count limit smaller than 100 tokens.`,
)
}
}
}
}
const checkRoyaltyDetails = async () => {
@@ -362,9 +480,13 @@ export const OpenEditionMinterCreator = ({
const checkwalletBalance = async () => {
if (!wallet.isWalletConnected) throw new Error('Wallet not connected.')
const amountNeeded = collectionDetails?.updatable
? Number(openEditionMinterUpdatableCreationFee)
: Number(openEditionMinterCreationFee)
let amountNeeded = 0
if (whitelistDetails?.whitelistState === 'new' && whitelistDetails.memberLimit) {
amountNeeded =
Math.ceil(Number(whitelistDetails.memberLimit) / 1000) * 100000000 + Number(openEditionMinterCreationFee)
} else {
amountNeeded = openEditionMinterCreationFee ? Number(openEditionMinterCreationFee) : 0
}
await (await wallet.getCosmWasmClient()).getBalance(wallet.address || '', 'ustars').then((balance) => {
if (amountNeeded >= Number(balance.amount))
throw new Error(
@@ -383,6 +505,7 @@ export const OpenEditionMinterCreator = ({
setTokenImageUri(null)
setOpenEditionMinterContractAddress(null)
setSg721ContractAddress(null)
setWhitelistContractAddress(null)
setTransactionHash(null)
if (metadataStorageMethod === 'off-chain') {
if (offChainMetadataUploadDetails?.uploadMethod === 'new') {
@@ -407,13 +530,27 @@ export const OpenEditionMinterCreator = ({
setTokenUri(metadataUriWithBase)
setCoverImageUrl(coverImageUriWithBase)
setUploading(false)
await instantiateOpenEditionMinter(metadataUriWithBase, coverImageUriWithBase)
let whitelist: string | undefined
if (whitelistDetails?.whitelistState === 'existing') whitelist = whitelistDetails.contractAddress
else if (whitelistDetails?.whitelistState === 'new') whitelist = await instantiateWhitelist()
setWhitelistContractAddress(whitelist as string)
await instantiateOpenEditionMinter(metadataUriWithBase, coverImageUriWithBase, undefined, whitelist)
} else {
setTokenUri(offChainMetadataUploadDetails?.tokenURI as string)
setCoverImageUrl(offChainMetadataUploadDetails?.imageUrl as string)
let whitelist: string | undefined
if (whitelistDetails?.whitelistState === 'existing') whitelist = whitelistDetails.contractAddress
else if (whitelistDetails?.whitelistState === 'new') whitelist = await instantiateWhitelist()
setWhitelistContractAddress(whitelist as string)
await instantiateOpenEditionMinter(
offChainMetadataUploadDetails?.tokenURI as string,
offChainMetadataUploadDetails?.imageUrl as string,
undefined,
whitelist,
)
}
} else if (metadataStorageMethod === 'on-chain') {
@@ -455,15 +592,27 @@ export const OpenEditionMinterCreator = ({
? `ipfs://${thumbnailUri}/${(imageUploadDetails.thumbnailFile as File).name}`
: undefined
setThumbnailImageUri(thumbnailUriWithBase)
setUploading(false)
await instantiateOpenEditionMinter(imageUriWithBase, coverImageUriWithBase, thumbnailUriWithBase)
let whitelist: string | undefined
if (whitelistDetails?.whitelistState === 'existing') whitelist = whitelistDetails.contractAddress
else if (whitelistDetails?.whitelistState === 'new') whitelist = await instantiateWhitelist()
setWhitelistContractAddress(whitelist as string)
await instantiateOpenEditionMinter(imageUriWithBase, coverImageUriWithBase, thumbnailUriWithBase, whitelist)
} else if (imageUploadDetails?.uploadMethod === 'existing') {
setTokenImageUri(imageUploadDetails.imageUrl as string)
setCoverImageUrl(imageUploadDetails.coverImageUrl as string)
let whitelist: string | undefined
if (whitelistDetails?.whitelistState === 'existing') whitelist = whitelistDetails.contractAddress
else if (whitelistDetails?.whitelistState === 'new') whitelist = await instantiateWhitelist()
setWhitelistContractAddress(whitelist as string)
await instantiateOpenEditionMinter(
imageUploadDetails.imageUrl as string,
imageUploadDetails.coverImageUrl as string,
whitelist,
)
}
}
@@ -562,7 +711,104 @@ export const OpenEditionMinterCreator = ({
})
}
const instantiateOpenEditionMinter = async (uri: string, coverImageUri: string, thumbnailUri?: string) => {
const instantiateWhitelist = async () => {
if (!wallet.isWalletConnected) throw new Error('Wallet not connected')
if (!whitelistContract) throw new Error('Contract not found')
if (whitelistDetails?.whitelistType === 'standard' || whitelistDetails?.whitelistType === 'flex') {
const standardMsg = {
members: whitelistDetails.members,
start_time: whitelistDetails.startTime,
end_time: whitelistDetails.endTime,
mint_price: coin(
String(Number(whitelistDetails.unitPrice)),
mintTokenFromFactory ? mintTokenFromFactory.denom : 'ustars',
),
per_address_limit: whitelistDetails.perAddressLimit,
member_limit: whitelistDetails.memberLimit,
admins: whitelistDetails.admins || [wallet.address],
admins_mutable: whitelistDetails.adminsMutable,
}
const flexMsg = {
members: whitelistDetails.members,
start_time: whitelistDetails.startTime,
end_time: whitelistDetails.endTime,
mint_price: coin(
String(Number(whitelistDetails.unitPrice)),
mintTokenFromFactory ? mintTokenFromFactory.denom : 'ustars',
),
member_limit: whitelistDetails.memberLimit,
admins: whitelistDetails.admins || [wallet.address],
admins_mutable: whitelistDetails.adminsMutable,
}
const data = await whitelistContract.instantiate(
whitelistDetails.whitelistType === 'standard' ? WHITELIST_CODE_ID : WHITELIST_FLEX_CODE_ID,
whitelistDetails.whitelistType === 'standard' ? standardMsg : flexMsg,
'Stargaze Whitelist Contract',
wallet.address,
)
return data.contractAddress
} else if (whitelistDetails?.whitelistType === 'merkletree') {
const members = whitelistDetails.members as string[]
const membersCsv = members.join('\n')
const membersBlob = new Blob([membersCsv], { type: 'text/csv' })
const membersFile = new File([membersBlob], 'members.csv', { type: 'text/csv' })
const formData = new FormData()
formData.append('whitelist', membersFile)
const response = await toast
.promise(
axios.post(`${WHITELIST_MERKLE_TREE_API_URL}/create_whitelist`, formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
}),
{
loading: 'Fetching merkle root hash...',
success: 'Merkle root fetched successfully.',
error: 'Error fetching root hash from Whitelist Merkle Tree API.',
},
)
.catch((error) => {
console.log('error', error)
throw new Error('Whitelist instantiation failed.')
})
const rootHash = response.data.root_hash
console.log('rootHash', rootHash)
const merkleTreeMsg = {
merkle_root: rootHash,
merkle_tree_uri: null,
start_time: whitelistDetails.startTime,
end_time: whitelistDetails.endTime,
mint_price: coin(
String(Number(whitelistDetails.unitPrice)),
mintTokenFromFactory ? mintTokenFromFactory.denom : 'ustars',
),
per_address_limit: whitelistDetails.perAddressLimit,
admins: whitelistDetails.admins || [wallet.address],
admins_mutable: whitelistDetails.adminsMutable,
}
const data = await whitelistMerkleTreeContract?.instantiate(
WHITELIST_MERKLE_TREE_CODE_ID,
merkleTreeMsg,
'Stargaze Whitelist Merkle Tree Contract',
wallet.address,
)
return data?.contractAddress
}
}
const instantiateOpenEditionMinter = async (
uri: string,
coverImageUri: string,
thumbnailUri?: string,
whitelist?: string,
) => {
if (!wallet.isWalletConnected) throw new Error('Wallet not connected')
if (!openEditionFactoryContract) throw new Error('Contract not found')
if (!openEditionMinterContract) throw new Error('Contract not found')
@@ -603,19 +849,30 @@ export const OpenEditionMinterCreator = ({
: null,
},
start_time: mintingDetails?.startTime,
end_time: mintingDetails?.endTime,
end_time:
mintingDetails?.limitType === ('time_limited' as LimitType) ||
mintingDetails?.limitType === ('time_and_count_limited' as LimitType)
? mintingDetails.endTime
: null,
mint_price: {
amount: Number(mintingDetails?.unitPrice).toString(),
denom: (mintTokenFromFactory?.denom as string) || 'ustars',
},
per_address_limit: mintingDetails?.perAddressLimit,
num_tokens:
mintingDetails?.limitType === ('count_limited' as LimitType) ||
mintingDetails?.limitType === ('time_and_count_limited' as LimitType)
? mintingDetails.tokenCountLimit
: null,
payment_address: mintingDetails?.paymentAddress || null,
whitelist,
},
collection_params: {
code_id: collectionDetails?.updatable
? SG721_OPEN_EDITION_UPDATABLE_CODE_ID
: mintingDetails?.selectedMintToken?.displayName === 'USK' ||
mintingDetails?.selectedMintToken?.displayName === 'USDC' ||
mintingDetails?.selectedMintToken?.displayName === 'TIA' ||
mintingDetails?.selectedMintToken?.displayName === 'STRDST' ||
mintingDetails?.selectedMintToken?.displayName === 'KUJI' ||
mintingDetails?.selectedMintToken?.displayName === 'HUAHUA' ||
@@ -638,18 +895,11 @@ export const OpenEditionMinterCreator = ({
}
const payload: OpenEditionFactoryDispatchExecuteArgs = {
contract: collectionDetails?.updatable ? updatableFactoryAddressForSelectedDenom : factoryAddressForSelectedDenom,
contract: openEditionFactoryAddress as string,
messages: openEditionFactoryMessages,
txSigner: wallet.address || '',
msg,
funds: [
coin(
collectionDetails?.updatable
? (openEditionMinterUpdatableCreationFee as string)
: (openEditionMinterCreationFee as string),
'ustars',
),
],
funds: [coin(openEditionMinterCreationFee as string, 'ustars')],
updatable: collectionDetails?.updatable,
}
await openEditionFactoryDispatchExecute(payload)
@@ -680,16 +930,24 @@ export const OpenEditionMinterCreator = ({
metadataStorageMethod,
openEditionMinterContractAddress,
sg721ContractAddress,
whitelistContractAddress,
transactionHash,
}
onChange(data)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [metadataStorageMethod, openEditionMinterContractAddress, sg721ContractAddress, transactionHash])
}, [
metadataStorageMethod,
openEditionMinterContractAddress,
sg721ContractAddress,
whitelistContractAddress,
transactionHash,
])
useEffect(() => {
const data: OpenEditionMinterDetailsDataProps = {
imageUploadDetails: imageUploadDetails ? imageUploadDetails : undefined,
collectionDetails: collectionDetails ? collectionDetails : undefined,
whitelistDetails: whitelistDetails ? whitelistDetails : undefined,
royaltyDetails: royaltyDetails ? royaltyDetails : undefined,
onChainMetadataInputDetails: onChainMetadataInputDetails ? onChainMetadataInputDetails : undefined,
offChainMetadataUploadDetails: offChainMetadataUploadDetails ? offChainMetadataUploadDetails : undefined,
@@ -699,12 +957,14 @@ export const OpenEditionMinterCreator = ({
coverImageUrl,
tokenUri,
tokenImageUri,
isRefreshed,
}
onDetailsChange(data)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
imageUploadDetails,
collectionDetails,
whitelistDetails,
royaltyDetails,
onChainMetadataInputDetails,
offChainMetadataUploadDetails,
@@ -714,6 +974,7 @@ export const OpenEditionMinterCreator = ({
coverImageUrl,
tokenUri,
tokenImageUri,
isRefreshed,
])
useEffect(() => {
@@ -722,6 +983,40 @@ export const OpenEditionMinterCreator = ({
}
}, [importedOpenEditionMinterDetails])
const fetchWhitelistConfig = async (contractAddress: string | undefined) => {
if (contractAddress === '' || !whitelistDetails) return
const contract = whitelistContract?.use(contractAddress)
await contract
?.config()
.then((config) => {
if (!config) {
whitelistDetails.whitelistType = 'standard'
return
}
if (JSON.stringify(config).includes('whale_cap')) whitelistDetails.whitelistType = 'flex'
else if (!JSON.stringify(config).includes('member_limit') || config.member_limit === 0) {
// whitelistDetails.whitelistType = 'merkletree'
toast.error(
'Whitelist Merkle Tree is not supported yet for open edition collections. Please use a standard or flexible whitelist contract.',
)
} else whitelistDetails.whitelistType = 'standard'
setIsRefreshed(!isRefreshed)
})
.catch((error) => {
console.log('error', error)
})
}
const debouncedWhitelistContractAddress = useDebounce(whitelistDetails?.contractAddress, 300)
useEffect(() => {
if (whitelistDetails?.whitelistState === 'existing' && debouncedWhitelistContractAddress !== '') {
void fetchWhitelistConfig(debouncedWhitelistContractAddress)
}
}, [whitelistDetails?.whitelistState, debouncedWhitelistContractAddress])
return (
<div>
{/* TODO: Cancel once we're able to index on-chain metadata */}
@@ -810,16 +1105,23 @@ export const OpenEditionMinterCreator = ({
/>
<MintingDetails
importedMintingDetails={importedOpenEditionMinterDetails?.mintingDetails}
minimumMintPrice={
collectionDetails?.updatable
? Number(minimumUpdatableMintPrice) / 1000000
: Number(minimumMintPrice) / 1000000
}
isPresale={whitelistDetails?.whitelistState === 'new'}
minimumMintPrice={Number(minimumMintPrice) / 1000000}
mintTokenFromFactory={mintTokenFromFactory}
onChange={setMintingDetails}
uploadMethod={offChainMetadataUploadDetails?.uploadMethod as UploadMethod}
whitelistStartDate={whitelistDetails?.startTime}
/>
</div>
<div className="my-6 mx-10">
<WhitelistDetails
importedWhitelistDetails={importedOpenEditionMinterDetails?.whitelistDetails}
mintingTokenFromFactory={mintTokenFromFactory}
onChange={setWhitelistDetails}
/>
</div>
<div className="my-6">
<RoyaltyDetails
importedRoyaltyDetails={importedOpenEditionMinterDetails?.royaltyDetails}
+528
View File
@@ -0,0 +1,528 @@
/* eslint-disable eslint-comments/disable-enable-pair */
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
/* eslint-disable no-nested-ternary */
import { Button } from 'components/Button'
import { FormControl } from 'components/FormControl'
import { FormGroup } from 'components/FormGroup'
import { AddressList } from 'components/forms/AddressList'
import { useAddressListState } from 'components/forms/AddressList.hooks'
import { useInputState, useNumberInputState } from 'components/forms/FormInput.hooks'
import { InputDateTime } from 'components/InputDateTime'
import type { WhitelistFlexMember } from 'components/WhitelistFlexUpload'
import { WhitelistFlexUpload } from 'components/WhitelistFlexUpload'
import type { TokenInfo } from 'config/token'
import { useGlobalSettings } from 'contexts/globalSettings'
import React, { useEffect, useState } from 'react'
import { isValidAddress } from 'utils/isValidAddress'
import { useWallet } from 'utils/wallet'
import { Conditional } from '../Conditional'
import { AddressInput, NumberInput } from '../forms/FormInput'
import { JsonPreview } from '../JsonPreview'
import { WhitelistUpload } from '../WhitelistUpload'
interface WhitelistDetailsProps {
onChange: (data: WhitelistDetailsDataProps) => void
mintingTokenFromFactory?: TokenInfo
importedWhitelistDetails?: WhitelistDetailsDataProps
}
export interface WhitelistDetailsDataProps {
whitelistState: WhitelistState
whitelistType: WhitelistType
contractAddress?: string
members?: string[] | WhitelistFlexMember[]
unitPrice?: string
startTime?: string
endTime?: string
perAddressLimit?: number
memberLimit?: number
admins?: string[]
adminsMutable?: boolean
}
type WhitelistState = 'none' | 'existing' | 'new'
export type WhitelistType = 'standard' | 'flex' | 'merkletree'
export const WhitelistDetails = ({
onChange,
mintingTokenFromFactory,
importedWhitelistDetails,
}: WhitelistDetailsProps) => {
const wallet = useWallet()
const { timezone } = useGlobalSettings()
const [whitelistState, setWhitelistState] = useState<WhitelistState>('none')
const [whitelistType, setWhitelistType] = useState<WhitelistType>('standard')
const [startDate, setStartDate] = useState<Date | undefined>(undefined)
const [endDate, setEndDate] = useState<Date | undefined>(undefined)
const [whitelistStandardArray, setWhitelistStandardArray] = useState<string[]>([])
const [whitelistFlexArray, setWhitelistFlexArray] = useState<WhitelistFlexMember[]>([])
const [whitelistMerkleTreeArray, setWhitelistMerkleTreeArray] = useState<string[]>([])
const [adminsMutable, setAdminsMutable] = useState<boolean>(true)
const whitelistAddressState = useInputState({
id: 'whitelist-address',
name: 'whitelistAddress',
title: 'Whitelist Address',
defaultValue: '',
})
const unitPriceState = useNumberInputState({
id: 'unit-price',
name: 'unitPrice',
title: 'Unit Price',
subtitle: `Token price for whitelisted addresses \n (min. 0 ${
mintingTokenFromFactory ? mintingTokenFromFactory.displayName : 'STARS'
})`,
placeholder: '25',
})
const memberLimitState = useNumberInputState({
id: 'member-limit',
name: 'memberLimit',
title: 'Member Limit',
subtitle: 'Maximum number of whitelisted addresses',
placeholder: '1000',
})
const perAddressLimitState = useNumberInputState({
id: 'per-address-limit',
name: 'perAddressLimit',
title: 'Per Address Limit',
subtitle: 'Maximum number of tokens per whitelisted address',
placeholder: '5',
})
const addressListState = useAddressListState()
const whitelistFileOnChange = (data: string[]) => {
if (whitelistType === 'standard') setWhitelistStandardArray(data)
if (whitelistType === 'merkletree') setWhitelistMerkleTreeArray(data)
}
const whitelistFlexFileOnChange = (whitelistData: WhitelistFlexMember[]) => {
setWhitelistFlexArray(whitelistData)
}
const downloadSampleWhitelistFlexFile = () => {
const csvData =
'address,mint_count\nstars153w5xhuqu3et29lgqk4dsynj6gjn96lr33wx4e,3\nstars1xkes5r2k8u3m3ayfpverlkcrq3k4jhdk8ws0uz,1\nstars1s8qx0zvz8yd6e4x0mqmqf7fr9vvfn622wtp3g3,2'
const blob = new Blob([csvData], { type: 'text/csv' })
const url = window.URL.createObjectURL(blob)
const a = document.createElement('a')
a.setAttribute('href', url)
a.setAttribute('download', 'sample_whitelist_flex.csv')
a.click()
}
const downloadSampleWhitelistFile = () => {
const txtData =
'stars153w5xhuqu3et29lgqk4dsynj6gjn96lr33wx4e\nstars1xkes5r2k8u3m3ayfpverlkcrq3k4jhdk8ws0uz\nstars1s8qx0zvz8yd6e4x0mqmqf7fr9vvfn622wtp3g3'
const blob = new Blob([txtData], { type: 'text/txt' })
const url = window.URL.createObjectURL(blob)
const a = document.createElement('a')
a.setAttribute('href', url)
a.setAttribute('download', 'sample_whitelist.txt')
a.click()
}
useEffect(() => {
if (!importedWhitelistDetails) {
setWhitelistStandardArray([])
setWhitelistFlexArray([])
setWhitelistMerkleTreeArray([])
}
}, [whitelistType])
useEffect(() => {
const data: WhitelistDetailsDataProps = {
whitelistState,
whitelistType,
contractAddress: whitelistAddressState.value
.toLowerCase()
.replace(/,/g, '')
.replace(/"/g, '')
.replace(/'/g, '')
.replace(/ /g, ''),
members:
whitelistType === 'standard'
? whitelistStandardArray
: whitelistType === 'merkletree'
? whitelistMerkleTreeArray
: whitelistFlexArray,
unitPrice: unitPriceState.value
? (Number(unitPriceState.value) * 1_000_000).toString()
: unitPriceState.value === 0
? '0'
: undefined,
startTime: startDate ? (startDate.getTime() * 1_000_000).toString() : '',
endTime: endDate ? (endDate.getTime() * 1_000_000).toString() : '',
perAddressLimit: perAddressLimitState.value,
memberLimit: memberLimitState.value,
admins: [
...new Set(
addressListState.values
.map((a) => a.address.trim())
.filter((address) => address !== '' && isValidAddress(address.trim()) && address.startsWith('stars')),
),
],
adminsMutable,
}
onChange(data)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
whitelistAddressState.value,
unitPriceState.value,
memberLimitState.value,
perAddressLimitState.value,
startDate,
endDate,
whitelistStandardArray,
whitelistFlexArray,
whitelistMerkleTreeArray,
whitelistState,
whitelistType,
addressListState.values,
adminsMutable,
])
// make the necessary changes with respect to imported whitelist details
useEffect(() => {
if (importedWhitelistDetails) {
setWhitelistState(importedWhitelistDetails.whitelistState)
setWhitelistType(importedWhitelistDetails.whitelistType)
whitelistAddressState.onChange(
importedWhitelistDetails.contractAddress ? importedWhitelistDetails.contractAddress : '',
)
unitPriceState.onChange(
importedWhitelistDetails.unitPrice ? Number(importedWhitelistDetails.unitPrice) / 1000000 : 0,
)
memberLimitState.onChange(importedWhitelistDetails.memberLimit ? importedWhitelistDetails.memberLimit : 0)
perAddressLimitState.onChange(
importedWhitelistDetails.perAddressLimit ? importedWhitelistDetails.perAddressLimit : 0,
)
setStartDate(
importedWhitelistDetails.startTime
? new Date(Number(importedWhitelistDetails.startTime) / 1_000_000)
: undefined,
)
setEndDate(
importedWhitelistDetails.endTime ? new Date(Number(importedWhitelistDetails.endTime) / 1_000_000) : undefined,
)
setAdminsMutable(importedWhitelistDetails.adminsMutable ? importedWhitelistDetails.adminsMutable : true)
importedWhitelistDetails.admins?.forEach((admin) => {
addressListState.reset()
addressListState.add({ address: admin })
})
if (importedWhitelistDetails.whitelistType === 'standard') {
setWhitelistStandardArray([])
importedWhitelistDetails.members?.forEach((member) => {
setWhitelistStandardArray((standardArray) => [...standardArray, member as string])
})
} else if (importedWhitelistDetails.whitelistType === 'merkletree') {
setWhitelistMerkleTreeArray([])
// importedWhitelistDetails.members?.forEach((member) => {
// setWhitelistMerkleTreeArray((merkleTreeArray) => [...merkleTreeArray, member as string])
// })
} else if (importedWhitelistDetails.whitelistType === 'flex') {
setWhitelistFlexArray([])
importedWhitelistDetails.members?.forEach((member) => {
setWhitelistFlexArray((flexArray) => [
...flexArray,
{
address: (member as WhitelistFlexMember).address,
mint_count: (member as WhitelistFlexMember).mint_count,
},
])
})
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [importedWhitelistDetails])
useEffect(() => {
if (whitelistState === 'new' && wallet.address) {
addressListState.reset()
addressListState.add({ address: wallet.address })
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [whitelistState, wallet.address])
return (
<div className="py-3 px-8 rounded border-2 border-white/20">
<div className="flex justify-center">
<div className="ml-4 font-bold form-check form-check-inline">
<input
checked={whitelistState === 'none'}
className="peer sr-only"
id="whitelistRadio1"
name="whitelistRadioOptions1"
onClick={() => {
setWhitelistState('none')
setWhitelistType('standard')
}}
type="radio"
value="None"
/>
<label
className="inline-block py-1 px-2 text-gray peer-checked:text-white hover:text-white peer-checked:bg-black hover:rounded-sm peer-checked:border-b-2 hover:border-b-2 peer-checked:border-plumbus hover:border-plumbus cursor-pointer form-check-label"
htmlFor="whitelistRadio1"
>
No whitelist
</label>
</div>
<div className="ml-4 font-bold form-check form-check-inline">
<input
checked={whitelistState === 'existing'}
className="peer sr-only"
id="whitelistRadio2"
name="whitelistRadioOptions2"
onClick={() => {
setWhitelistState('existing')
}}
type="radio"
value="Existing"
/>
<label
className="inline-block py-1 px-2 text-gray peer-checked:text-white hover:text-white peer-checked:bg-black hover:rounded-sm peer-checked:border-b-2 hover:border-b-2 peer-checked:border-plumbus hover:border-plumbus cursor-pointer form-check-label"
htmlFor="whitelistRadio2"
>
Existing whitelist
</label>
</div>
<div className="ml-4 font-bold form-check form-check-inline">
<input
checked={whitelistState === 'new'}
className="peer sr-only"
id="whitelistRadio3"
name="whitelistRadioOptions3"
onClick={() => {
setWhitelistState('new')
}}
type="radio"
value="New"
/>
<label
className="inline-block py-1 px-2 text-gray peer-checked:text-white hover:text-white peer-checked:bg-black hover:rounded-sm peer-checked:border-b-2 hover:border-b-2 peer-checked:border-plumbus hover:border-plumbus cursor-pointer form-check-label"
htmlFor="whitelistRadio3"
>
New whitelist
</label>
</div>
</div>
<Conditional test={whitelistState === 'existing'}>
<AddressInput {...whitelistAddressState} className="pb-5" isRequired />
</Conditional>
<Conditional test={whitelistState === 'new'}>
<div className="flex justify-between mb-5 ml-6 max-w-[300px] text-lg font-bold">
<div className="form-check form-check-inline">
<input
checked={whitelistType === 'standard'}
className="peer sr-only"
id="inlineRadio7"
name="inlineRadioOptions7"
onClick={() => {
setWhitelistType('standard')
}}
type="radio"
value="standard"
/>
<label
className="inline-block py-1 px-2 text-gray peer-checked:text-white hover:text-white peer-checked:bg-black hover:rounded-sm peer-checked:border-b-2 hover:border-b-2 peer-checked:border-plumbus hover:border-plumbus cursor-pointer form-check-label"
htmlFor="inlineRadio7"
>
Standard Whitelist
</label>
</div>
<div className="form-check form-check-inline">
<input
checked={whitelistType === 'flex'}
className="peer sr-only"
id="inlineRadio8"
name="inlineRadioOptions8"
onClick={() => {
setWhitelistType('flex')
}}
type="radio"
value="flex"
/>
<label
className="inline-block py-1 px-2 text-gray peer-checked:text-white hover:text-white peer-checked:bg-black hover:rounded-sm peer-checked:border-b-2 hover:border-b-2 peer-checked:border-plumbus hover:border-plumbus cursor-pointer form-check-label"
htmlFor="inlineRadio8"
>
Whitelist Flex
</label>
</div>
{/* <div className="form-check form-check-inline">
<input
checked={whitelistType === 'merkletree'}
className="peer sr-only"
id="inlineRadio9"
name="inlineRadioOptions9"
onClick={() => {
setWhitelistType('merkletree')
}}
type="radio"
value="merkletree"
/>
<label
className="inline-block py-1 px-2 text-gray peer-checked:text-white hover:text-white peer-checked:bg-black hover:rounded-sm peer-checked:border-b-2 hover:border-b-2 peer-checked:border-plumbus hover:border-plumbus cursor-pointer form-check-label"
htmlFor="inlineRadio9"
>
Whitelist Merkle Tree
</label>
</div> */}
</div>
<div className="grid grid-cols-2">
<FormGroup subtitle="Information about your minting settings" title="Whitelist Minting Details">
<NumberInput isRequired {...unitPriceState} />
<Conditional test={whitelistType !== 'merkletree'}>
<NumberInput isRequired {...memberLimitState} />
</Conditional>
<Conditional test={whitelistType === 'standard' || whitelistType === 'merkletree'}>
<NumberInput isRequired {...perAddressLimitState} />
</Conditional>
<FormControl
htmlId="start-date"
isRequired
subtitle="Start time for minting tokens to whitelisted addresses"
title={`Whitelist Start Time ${timezone === 'Local' ? '(local)' : '(UTC)'}`}
>
<InputDateTime
minDate={
timezone === 'Local' ? new Date() : new Date(Date.now() + new Date().getTimezoneOffset() * 60 * 1000)
}
onChange={(date) =>
date
? setStartDate(
timezone === 'Local'
? date
: new Date(date.getTime() - new Date().getTimezoneOffset() * 60 * 1000),
)
: setStartDate(undefined)
}
value={
timezone === 'Local'
? startDate
: startDate
? new Date(startDate.getTime() + new Date().getTimezoneOffset() * 60 * 1000)
: undefined
}
/>
</FormControl>
<FormControl
htmlId="end-date"
isRequired
subtitle="Whitelist End Time dictates when public sales will start"
title={`Whitelist End Time ${timezone === 'Local' ? '(local)' : '(UTC)'}`}
>
<InputDateTime
minDate={
timezone === 'Local' ? new Date() : new Date(Date.now() + new Date().getTimezoneOffset() * 60 * 1000)
}
onChange={(date) =>
date
? setEndDate(
timezone === 'Local'
? date
: new Date(date.getTime() - new Date().getTimezoneOffset() * 60 * 1000),
)
: setEndDate(undefined)
}
value={
timezone === 'Local'
? endDate
: endDate
? new Date(endDate.getTime() + new Date().getTimezoneOffset() * 60 * 1000)
: undefined
}
/>
</FormControl>
</FormGroup>
<div>
<div className="mt-2 ml-3 w-[65%] form-control">
<label className="justify-start cursor-pointer label">
<span className="mr-4 font-bold">Mutable Administrator Addresses</span>
<input
checked={adminsMutable}
className={`toggle ${adminsMutable ? `bg-stargaze` : `bg-gray-600`}`}
onClick={() => setAdminsMutable(!adminsMutable)}
type="checkbox"
/>
</label>
</div>
<div className="my-4 ml-4">
<AddressList
entries={addressListState.entries}
onAdd={addressListState.add}
onChange={addressListState.update}
onRemove={addressListState.remove}
subtitle="The list of administrator addresses"
title="Administrator Addresses"
/>
</div>
<Conditional test={whitelistType === 'standard'}>
<FormGroup
subtitle={
<div>
<span>TXT file that contains the whitelisted addresses</span>
<Button className="mt-2 text-sm text-white" onClick={downloadSampleWhitelistFile}>
Download Sample File
</Button>
</div>
}
title="Whitelist File"
>
<WhitelistUpload onChange={whitelistFileOnChange} />
</FormGroup>
<Conditional test={whitelistStandardArray.length > 0}>
<JsonPreview content={whitelistStandardArray} initialState title="File Contents" />
</Conditional>
</Conditional>
<Conditional test={whitelistType === 'flex'}>
<FormGroup
subtitle={
<div>
<span>CSV file that contains the whitelisted addresses and corresponding mint counts</span>
<Button className="mt-2 text-sm text-white" onClick={downloadSampleWhitelistFlexFile}>
Download Sample File
</Button>
</div>
}
title="Whitelist File"
>
<WhitelistFlexUpload onChange={whitelistFlexFileOnChange} />
</FormGroup>
<Conditional test={whitelistFlexArray.length > 0}>
<JsonPreview content={whitelistFlexArray} initialState={false} title="File Contents" />
</Conditional>
</Conditional>
<Conditional test={whitelistType === 'merkletree'}>
<FormGroup
subtitle={
<div>
<span>TXT file that contains the whitelisted addresses</span>
<Button className="mt-2 text-sm text-white" onClick={downloadSampleWhitelistFile}>
Download Sample File
</Button>
</div>
}
title="Whitelist File"
>
<WhitelistUpload onChange={whitelistFileOnChange} />
</FormGroup>
<Conditional test={whitelistStandardArray.length > 0}>
<JsonPreview content={whitelistStandardArray} initialState title="File Contents" />
</Conditional>
</Conditional>
</div>
</div>
</Conditional>
</div>
)
}
+127
View File
@@ -0,0 +1,127 @@
import type { AssetList, Chain } from '@chain-registry/types'
export const fangTestnet: Chain = {
$schema: '../../chain.schema.json',
chain_name: 'stargazefangtestnet',
status: 'live',
network_type: 'testnet',
website: 'https://stargaze.zone/',
pretty_name: 'Fang Testnet',
chain_id: 'fang-1',
bech32_prefix: 'stars',
daemon_name: 'starsd',
node_home: '$HOME/.starsd',
slip44: 118,
fees: {
fee_tokens: [
{
denom: 'ustars',
low_gas_price: 0.3,
average_gas_price: 0.4,
high_gas_price: 0.5,
},
],
},
codebase: {
git_repo: 'https://github.com/public-awesome/stargaze',
recommended_version: 'v7.0.0',
compatible_versions: ['v7.0.0'],
cosmos_sdk_version: '0.45',
consensus: {
type: 'tendermint',
version: '0.34',
},
cosmwasm_version: '0.28',
cosmwasm_enabled: true,
genesis: {
genesis_url: 'https://github.com/public-awesome/testnets/blob/main/elgafar-1/genesis/genesis.tar.gz?raw=true',
},
versions: [
{
name: 'v7.0.0',
recommended_version: 'v7.0.0',
compatible_versions: ['v7.0.0'],
cosmos_sdk_version: '0.45',
consensus: {
type: 'tendermint',
version: '0.34',
},
cosmwasm_version: '0.28',
cosmwasm_enabled: true,
},
],
},
peers: {
seeds: [],
persistent_peers: [
{
id: 'e31886cba90a06e165b0df18cc5c8ae015ecd23e',
address: '209.159.152.82:26656',
provider: 'stargaze',
},
{
id: 'de00d2d65594b672469ecd65826a94ec1be80b9f',
address: '208.73.205.226:26656',
provider: 'stargaze',
},
{
id: '496ac0ba20188f70f41e0a814dfd4d9a617338f8',
address: 'stargaze-testnet-seed.ibs.team:16652',
provider: 'Inter Blockchain Services',
},
],
},
apis: {
rpc: [
{
address: 'https://rpc.fang-1.stargaze-apis.com',
provider: 'Stargaze Foundation',
},
],
rest: [
{
address: 'https://rest.fang-1.stargaze-apis.com',
provider: 'Stargaze Foundation',
},
],
grpc: [
{
address: 'http://grpc-1.fang-1.stargaze-apis.com:26660',
provider: 'Stargaze Foundation',
},
],
},
}
export const fangAssetList: AssetList = {
$schema: '../../assetlist.schema.json',
chain_name: 'stargazefangtestnet',
assets: [
{
description: 'The native token of Stargaze',
denom_units: [
{
denom: 'ustars',
exponent: 0,
},
{
denom: 'stars',
exponent: 6,
},
],
base: 'ustars',
name: 'Stargaze',
display: 'stars',
symbol: 'STARS',
logo_URIs: {
png: 'https://raw.githubusercontent.com/cosmos/chain-registry/master/stargaze/images/stars.png',
},
coingecko_id: 'stargaze',
images: [
{
png: 'https://raw.githubusercontent.com/cosmos/chain-registry/master/stargaze/images/stars.png',
},
],
},
],
}
+295 -30
View File
@@ -1,14 +1,24 @@
import {
FEATURED_IBC_TIA_FACTORY_ADDRESS,
FEATURED_IBC_USDC_FACTORY_ADDRESS,
FEATURED_VENDING_FACTORY_ADDRESS,
FEATURED_VENDING_FACTORY_FLEX_ADDRESS,
FEATURED_VENDING_FACTORY_MERKLE_TREE_ADDRESS,
FEATURED_VENDING_IBC_TIA_FACTORY_FLEX_ADDRESS,
FEATURED_VENDING_IBC_TIA_FACTORY_MERKLE_TREE_ADDRESS,
FEATURED_VENDING_IBC_USDC_FACTORY_FLEX_ADDRESS,
OPEN_EDITION_FACTORY_ADDRESS,
OPEN_EDITION_FACTORY_FLEX_ADDRESS,
OPEN_EDITION_IBC_ATOM_FACTORY_ADDRESS,
OPEN_EDITION_IBC_ATOM_FACTORY_FLEX_ADDRESS,
OPEN_EDITION_IBC_CRBRUS_FACTORY_ADDRESS,
OPEN_EDITION_IBC_FRNZ_FACTORY_ADDRESS,
OPEN_EDITION_IBC_HUAHUA_FACTORY_ADDRESS,
OPEN_EDITION_IBC_KUJI_FACTORY_ADDRESS,
OPEN_EDITION_IBC_NBTC_FACTORY_ADDRESS,
OPEN_EDITION_IBC_TIA_FACTORY_ADDRESS,
OPEN_EDITION_IBC_TIA_FACTORY_FLEX_ADDRESS,
OPEN_EDITION_IBC_USDC_FACTORY_ADDRESS,
OPEN_EDITION_IBC_USDC_FACTORY_FLEX_ADDRESS,
OPEN_EDITION_IBC_USK_FACTORY_ADDRESS,
OPEN_EDITION_NATIVE_BRNCH_FACTORY_ADDRESS,
OPEN_EDITION_NATIVE_STRDST_FACTORY_ADDRESS,
@@ -16,10 +26,12 @@ import {
OPEN_EDITION_UPDATABLE_IBC_ATOM_FACTORY_ADDRESS,
OPEN_EDITION_UPDATABLE_IBC_FRNZ_FACTORY_ADDRESS,
OPEN_EDITION_UPDATABLE_IBC_NBTC_FACTORY_ADDRESS,
OPEN_EDITION_UPDATABLE_IBC_TIA_FACTORY_ADDRESS,
OPEN_EDITION_UPDATABLE_IBC_USDC_FACTORY_ADDRESS,
OPEN_EDITION_UPDATABLE_IBC_USK_FACTORY_ADDRESS,
VENDING_FACTORY_ADDRESS,
VENDING_FACTORY_FLEX_ADDRESS,
VENDING_FACTORY_MERKLE_TREE_ADDRESS,
VENDING_FACTORY_UPDATABLE_ADDRESS,
VENDING_FACTORY_UPDATABLE_FLEX_ADDRESS,
VENDING_IBC_ATOM_FACTORY_ADDRESS,
@@ -28,14 +40,17 @@ import {
VENDING_IBC_ATOM_UPDATABLE_FACTORY_FLEX_ADDRESS,
VENDING_IBC_CRBRUS_FACTORY_ADDRESS,
VENDING_IBC_CRBRUS_FACTORY_FLEX_ADDRESS,
VENDING_IBC_HUAHUA_FACTORY_ADDRESS,
VENDING_IBC_HUAHUA_FACTORY_FLEX_ADDRESS,
VENDING_IBC_KUJI_FACTORY_ADDRESS,
VENDING_IBC_KUJI_FACTORY_FLEX_ADDRESS,
VENDING_IBC_NBTC_FACTORY_ADDRESS,
VENDING_IBC_NBTC_FACTORY_FLEX_ADDRESS,
VENDING_IBC_NBTC_UPDATABLE_FACTORY_ADDRESS,
VENDING_IBC_NBTC_UPDATABLE_FACTORY_FLEX_ADDRESS,
VENDING_IBC_TIA_FACTORY_ADDRESS,
VENDING_IBC_TIA_FACTORY_FLEX_ADDRESS,
VENDING_IBC_TIA_FACTORY_MERKLE_TREE_ADDRESS,
VENDING_IBC_TIA_UPDATABLE_FACTORY_ADDRESS,
VENDING_IBC_TIA_UPDATABLE_FACTORY_FLEX_ADDRESS,
VENDING_IBC_USDC_FACTORY_ADDRESS,
VENDING_IBC_USDC_FACTORY_FLEX_ADDRESS,
VENDING_IBC_USDC_UPDATABLE_FACTORY_ADDRESS,
@@ -57,9 +72,10 @@ import {
ibcAtom,
ibcCrbrus,
ibcFrnz,
ibcHuahua,
// ibcHuahua,
ibcKuji,
ibcNbtc,
ibcTia,
ibcUsdc,
ibcUsk,
nativeBrnch,
@@ -73,6 +89,7 @@ export interface MinterInfo {
supportedToken: TokenInfo
updatable?: boolean
flexible?: boolean
merkleTree?: boolean
featured?: boolean
}
@@ -82,6 +99,7 @@ export const openEditionStarsMinter: MinterInfo = {
supportedToken: stars,
updatable: false,
featured: false,
flexible: false,
}
export const openEditionUpdatableStarsMinter: MinterInfo = {
@@ -90,6 +108,7 @@ export const openEditionUpdatableStarsMinter: MinterInfo = {
supportedToken: stars,
updatable: true,
featured: false,
flexible: false,
}
export const openEditionIbcAtomMinter: MinterInfo = {
@@ -98,6 +117,7 @@ export const openEditionIbcAtomMinter: MinterInfo = {
supportedToken: ibcAtom,
updatable: false,
featured: false,
flexible: false,
}
export const openEditionUpdatableIbcAtomMinter: MinterInfo = {
@@ -106,6 +126,7 @@ export const openEditionUpdatableIbcAtomMinter: MinterInfo = {
supportedToken: ibcAtom,
updatable: true,
featured: false,
flexible: false,
}
export const openEditionIbcUsdcMinter: MinterInfo = {
@@ -114,6 +135,16 @@ export const openEditionIbcUsdcMinter: MinterInfo = {
supportedToken: ibcUsdc,
updatable: false,
featured: false,
flexible: false,
}
export const openEditionIbcTiaMinter: MinterInfo = {
id: 'open-edition-ibc-tia-minter',
factoryAddress: OPEN_EDITION_IBC_TIA_FACTORY_ADDRESS,
supportedToken: ibcTia,
updatable: false,
featured: false,
flexible: false,
}
export const openEditionIbcNbtcMinter: MinterInfo = {
@@ -122,6 +153,7 @@ export const openEditionIbcNbtcMinter: MinterInfo = {
supportedToken: ibcNbtc,
updatable: false,
featured: false,
flexible: false,
}
export const openEditionUpdatableIbcUsdcMinter: MinterInfo = {
@@ -130,6 +162,16 @@ export const openEditionUpdatableIbcUsdcMinter: MinterInfo = {
supportedToken: ibcUsdc,
updatable: true,
featured: false,
flexible: false,
}
export const openEditionUpdatableIbcTiaMinter: MinterInfo = {
id: 'open-edition-updatable-ibc-tia-minter',
factoryAddress: OPEN_EDITION_UPDATABLE_IBC_TIA_FACTORY_ADDRESS,
supportedToken: ibcTia,
updatable: true,
featured: false,
flexible: false,
}
export const openEditionUpdatableIbcNbtcMinter: MinterInfo = {
@@ -138,6 +180,7 @@ export const openEditionUpdatableIbcNbtcMinter: MinterInfo = {
supportedToken: ibcNbtc,
updatable: true,
featured: false,
flexible: false,
}
export const openEditionIbcFrnzMinter: MinterInfo = {
@@ -146,6 +189,7 @@ export const openEditionIbcFrnzMinter: MinterInfo = {
supportedToken: ibcFrnz,
updatable: false,
featured: false,
flexible: false,
}
export const openEditionUpdatableIbcFrnzMinter: MinterInfo = {
@@ -154,6 +198,7 @@ export const openEditionUpdatableIbcFrnzMinter: MinterInfo = {
supportedToken: ibcFrnz,
updatable: true,
featured: false,
flexible: false,
}
export const openEditionIbcUskMinter: MinterInfo = {
@@ -162,6 +207,7 @@ export const openEditionIbcUskMinter: MinterInfo = {
supportedToken: ibcUsk,
updatable: false,
featured: false,
flexible: false,
}
export const openEditionUpdatableIbcUskMinter: MinterInfo = {
@@ -170,6 +216,7 @@ export const openEditionUpdatableIbcUskMinter: MinterInfo = {
supportedToken: ibcUsk,
updatable: true,
featured: false,
flexible: false,
}
export const openEditionIbcKujiMinter: MinterInfo = {
@@ -178,15 +225,16 @@ export const openEditionIbcKujiMinter: MinterInfo = {
supportedToken: ibcKuji,
updatable: false,
featured: false,
flexible: false,
}
export const openEditionIbcHuahuaMinter: MinterInfo = {
id: 'open-edition-ibc-huahua-minter',
factoryAddress: OPEN_EDITION_IBC_HUAHUA_FACTORY_ADDRESS,
supportedToken: ibcHuahua,
updatable: false,
featured: false,
}
// export const openEditionIbcHuahuaMinter: MinterInfo = {
// id: 'open-edition-ibc-huahua-minter',
// factoryAddress: OPEN_EDITION_IBC_HUAHUA_FACTORY_ADDRESS,
// supportedToken: ibcHuahua,
// updatable: false,
// featured: false,
// }
export const openEditionIbcCrbrusMinter: MinterInfo = {
id: 'open-edition-ibc-crbrus-minter',
@@ -194,6 +242,7 @@ export const openEditionIbcCrbrusMinter: MinterInfo = {
supportedToken: ibcCrbrus,
updatable: false,
featured: false,
flexible: false,
}
export const openEditionNativeStrdstMinter: MinterInfo = {
@@ -202,6 +251,7 @@ export const openEditionNativeStrdstMinter: MinterInfo = {
supportedToken: nativeStardust,
updatable: false,
featured: false,
flexible: false,
}
export const openEditionNativeBrnchMinter: MinterInfo = {
@@ -210,6 +260,7 @@ export const openEditionNativeBrnchMinter: MinterInfo = {
supportedToken: nativeBrnch,
updatable: false,
featured: false,
flexible: false,
}
export const openEditionMinterList = [
@@ -221,23 +272,69 @@ export const openEditionMinterList = [
openEditionUpdatableIbcFrnzMinter,
openEditionIbcUsdcMinter,
openEditionUpdatableIbcUsdcMinter,
openEditionIbcTiaMinter,
openEditionUpdatableIbcTiaMinter,
openEditionIbcNbtcMinter,
openEditionUpdatableIbcNbtcMinter,
openEditionIbcUskMinter,
openEditionUpdatableIbcUskMinter,
openEditionIbcKujiMinter,
openEditionIbcHuahuaMinter,
// openEditionIbcHuahuaMinter,
openEditionIbcCrbrusMinter,
openEditionNativeStrdstMinter,
openEditionNativeBrnchMinter,
]
export const flexibleOpenEditionStarsMinter: MinterInfo = {
id: 'flexible-open-edition-stars-minter',
factoryAddress: OPEN_EDITION_FACTORY_FLEX_ADDRESS,
supportedToken: stars,
updatable: false,
featured: false,
flexible: true,
}
export const flexibleOpenEditionIbcAtomMinter: MinterInfo = {
id: 'flexible-open-edition-ibc-atom-minter',
factoryAddress: OPEN_EDITION_IBC_ATOM_FACTORY_FLEX_ADDRESS,
supportedToken: ibcAtom,
updatable: false,
featured: false,
flexible: true,
}
export const flexibleOpenEditionIbcUsdcMinter: MinterInfo = {
id: 'flexible-open-edition-ibc-usdc-minter',
factoryAddress: OPEN_EDITION_IBC_USDC_FACTORY_FLEX_ADDRESS,
supportedToken: ibcUsdc,
updatable: false,
featured: false,
flexible: true,
}
export const flexibleOpenEditionIbcTiaMinter: MinterInfo = {
id: 'flexible-open-edition-ibc-tia-minter',
factoryAddress: OPEN_EDITION_IBC_TIA_FACTORY_FLEX_ADDRESS,
supportedToken: ibcTia,
updatable: false,
featured: false,
flexible: true,
}
export const flexibleOpenEditionMinterList = [
flexibleOpenEditionStarsMinter,
flexibleOpenEditionIbcAtomMinter,
flexibleOpenEditionIbcUsdcMinter,
flexibleOpenEditionIbcTiaMinter,
]
export const vendingStarsMinter: MinterInfo = {
id: 'vending-stars-minter',
factoryAddress: VENDING_FACTORY_ADDRESS,
supportedToken: stars,
updatable: false,
flexible: false,
merkleTree: false,
featured: false,
}
@@ -247,6 +344,7 @@ export const vendingFeaturedStarsMinter: MinterInfo = {
supportedToken: stars,
updatable: false,
flexible: false,
merkleTree: false,
featured: true,
}
@@ -256,6 +354,7 @@ export const vendingUpdatableStarsMinter: MinterInfo = {
supportedToken: stars,
updatable: true,
flexible: false,
merkleTree: false,
featured: false,
}
@@ -265,6 +364,7 @@ export const vendingIbcAtomMinter: MinterInfo = {
supportedToken: ibcAtom,
updatable: false,
flexible: false,
merkleTree: false,
featured: false,
}
@@ -274,6 +374,7 @@ export const vendingUpdatableIbcAtomMinter: MinterInfo = {
supportedToken: ibcAtom,
updatable: true,
flexible: false,
merkleTree: false,
featured: false,
}
@@ -283,15 +384,47 @@ export const vendingIbcUsdcMinter: MinterInfo = {
supportedToken: ibcUsdc,
updatable: false,
flexible: false,
merkleTree: false,
featured: false,
}
export const vendingFeaturedIbcUsdcMinter: MinterInfo = {
id: 'vending-featured-ibc-usdc-minter',
factoryAddress: FEATURED_IBC_USDC_FACTORY_ADDRESS,
supportedToken: ibcUsdc,
updatable: false,
flexible: false,
merkleTree: false,
featured: true,
}
export const vendingIbcTiaMinter: MinterInfo = {
id: 'vending-ibc-tia-minter',
factoryAddress: VENDING_IBC_TIA_FACTORY_ADDRESS,
supportedToken: ibcTia,
updatable: false,
flexible: false,
merkleTree: false,
featured: false,
}
export const vendingFeaturedIbcTiaMinter: MinterInfo = {
id: 'vending-featured-ibc-tia-minter',
factoryAddress: FEATURED_IBC_TIA_FACTORY_ADDRESS,
supportedToken: ibcTia,
updatable: false,
flexible: false,
merkleTree: false,
featured: true,
}
export const vendingIbcNbtcMinter: MinterInfo = {
id: 'vending-ibc-nbtc-minter',
factoryAddress: VENDING_IBC_NBTC_FACTORY_ADDRESS,
supportedToken: ibcNbtc,
updatable: false,
flexible: false,
merkleTree: false,
featured: false,
}
@@ -301,6 +434,17 @@ export const vendingUpdatableIbcUsdcMinter: MinterInfo = {
supportedToken: ibcUsdc,
updatable: true,
flexible: false,
merkleTree: false,
featured: false,
}
export const vendingUpdatableIbcTiaMinter: MinterInfo = {
id: 'vending-updatable-ibc-tia-minter',
factoryAddress: VENDING_IBC_TIA_UPDATABLE_FACTORY_ADDRESS,
supportedToken: ibcTia,
updatable: true,
flexible: false,
merkleTree: false,
featured: false,
}
@@ -310,6 +454,7 @@ export const vendingUpdatableIbcNbtcMinter: MinterInfo = {
supportedToken: ibcNbtc,
updatable: true,
flexible: false,
merkleTree: false,
featured: false,
}
@@ -319,6 +464,7 @@ export const vendingIbcUskMinter: MinterInfo = {
supportedToken: ibcUsk,
updatable: false,
flexible: false,
merkleTree: false,
featured: false,
}
@@ -328,6 +474,7 @@ export const vendingUpdatableIbcUskMinter: MinterInfo = {
supportedToken: ibcUsk,
updatable: true,
flexible: false,
merkleTree: false,
featured: false,
}
@@ -337,17 +484,19 @@ export const vendingIbcKujiMinter: MinterInfo = {
supportedToken: ibcKuji,
updatable: false,
flexible: false,
merkleTree: false,
featured: false,
}
export const vendingIbcHuahuaMinter: MinterInfo = {
id: 'vending-ibc-huahua-minter',
factoryAddress: VENDING_IBC_HUAHUA_FACTORY_ADDRESS,
supportedToken: ibcHuahua,
updatable: false,
flexible: false,
featured: false,
}
// export const vendingIbcHuahuaMinter: MinterInfo = {
// id: 'vending-ibc-huahua-minter',
// factoryAddress: VENDING_IBC_HUAHUA_FACTORY_ADDRESS,
// supportedToken: ibcHuahua,
// updatable: false,
// flexible: false,
// merkleTree: false,
// featured: false,
// }
export const vendingIbcCrbrusMinter: MinterInfo = {
id: 'vending-ibc-crbrus-minter',
@@ -355,6 +504,7 @@ export const vendingIbcCrbrusMinter: MinterInfo = {
supportedToken: ibcCrbrus,
updatable: false,
flexible: false,
merkleTree: false,
featured: false,
}
@@ -364,6 +514,7 @@ export const vendingNativeStardustMinter: MinterInfo = {
supportedToken: nativeStardust,
updatable: false,
flexible: false,
merkleTree: false,
featured: false,
}
@@ -373,6 +524,7 @@ export const vendingUpdatableNativeStardustMinter: MinterInfo = {
supportedToken: nativeStardust,
updatable: true,
flexible: false,
merkleTree: false,
featured: false,
}
@@ -382,6 +534,7 @@ export const vendingNativeBrnchMinter: MinterInfo = {
supportedToken: nativeBrnch,
updatable: false,
flexible: false,
merkleTree: false,
featured: false,
}
@@ -391,6 +544,7 @@ export const vendingUpdatableNativeBrnchMinter: MinterInfo = {
supportedToken: nativeBrnch,
updatable: true,
flexible: false,
merkleTree: false,
featured: false,
}
@@ -401,13 +555,17 @@ export const vendingMinterList = [
vendingIbcAtomMinter,
vendingUpdatableIbcAtomMinter,
vendingIbcUsdcMinter,
vendingFeaturedIbcUsdcMinter,
vendingUpdatableIbcUsdcMinter,
vendingIbcTiaMinter,
vendingFeaturedIbcTiaMinter,
vendingUpdatableIbcTiaMinter,
vendingIbcNbtcMinter,
vendingUpdatableIbcNbtcMinter,
vendingIbcUskMinter,
vendingUpdatableIbcUskMinter,
vendingIbcKujiMinter,
vendingIbcHuahuaMinter,
// vendingIbcHuahuaMinter,
vendingIbcCrbrusMinter,
vendingNativeStardustMinter,
vendingUpdatableNativeStardustMinter,
@@ -421,6 +579,7 @@ export const flexibleVendingStarsMinter: MinterInfo = {
supportedToken: stars,
updatable: false,
flexible: true,
merkleTree: false,
featured: false,
}
@@ -430,6 +589,7 @@ export const flexibleFeaturedVendingStarsMinter: MinterInfo = {
supportedToken: stars,
updatable: false,
flexible: true,
merkleTree: false,
featured: true,
}
@@ -439,6 +599,7 @@ export const flexibleVendingUpdatableStarsMinter: MinterInfo = {
supportedToken: stars,
updatable: true,
flexible: true,
merkleTree: false,
featured: false,
}
@@ -448,6 +609,7 @@ export const flexibleVendingIbcAtomMinter: MinterInfo = {
supportedToken: ibcAtom,
updatable: false,
flexible: true,
merkleTree: false,
featured: false,
}
@@ -457,6 +619,7 @@ export const flexibleVendingUpdatableIbcAtomMinter: MinterInfo = {
supportedToken: ibcAtom,
updatable: true,
flexible: true,
merkleTree: false,
featured: false,
}
@@ -466,15 +629,47 @@ export const flexibleVendingIbcUsdcMinter: MinterInfo = {
supportedToken: ibcUsdc,
updatable: false,
flexible: true,
merkleTree: false,
featured: false,
}
export const flexibleFeaturedVendingIbcUsdcMinter: MinterInfo = {
id: 'flexible-featured-vending-ibc-usdc-minter',
factoryAddress: FEATURED_VENDING_IBC_USDC_FACTORY_FLEX_ADDRESS,
supportedToken: ibcUsdc,
updatable: false,
flexible: true,
merkleTree: false,
featured: true,
}
export const flexibleVendingIbcTiaMinter: MinterInfo = {
id: 'flexible-vending-ibc-tia-minter',
factoryAddress: VENDING_IBC_TIA_FACTORY_FLEX_ADDRESS,
supportedToken: ibcTia,
updatable: false,
flexible: true,
merkleTree: false,
featured: false,
}
export const flexibleFeaturedVendingIbcTiaMinter: MinterInfo = {
id: 'flexible-featured-vending-ibc-tia-minter',
factoryAddress: FEATURED_VENDING_IBC_TIA_FACTORY_FLEX_ADDRESS,
supportedToken: ibcTia,
updatable: false,
flexible: true,
merkleTree: false,
featured: true,
}
export const flexibleVendingIbcNbtcMinter: MinterInfo = {
id: 'flexible-vending-ibc-nbtc-minter',
factoryAddress: VENDING_IBC_NBTC_FACTORY_FLEX_ADDRESS,
supportedToken: ibcNbtc,
updatable: false,
flexible: true,
merkleTree: false,
featured: false,
}
@@ -484,6 +679,17 @@ export const flexibleVendingUpdatableIbcUsdcMinter: MinterInfo = {
supportedToken: ibcUsdc,
updatable: true,
flexible: true,
merkleTree: false,
featured: false,
}
export const flexibleVendingUpdatableIbcTiaMinter: MinterInfo = {
id: 'flexible-vending-updatable-ibc-tia-minter',
factoryAddress: VENDING_IBC_TIA_UPDATABLE_FACTORY_FLEX_ADDRESS,
supportedToken: ibcTia,
updatable: true,
flexible: true,
merkleTree: false,
featured: false,
}
@@ -493,6 +699,7 @@ export const flexibleVendingUpdatableIbcNbtcMinter: MinterInfo = {
supportedToken: ibcNbtc,
updatable: true,
flexible: true,
merkleTree: false,
featured: false,
}
@@ -502,6 +709,7 @@ export const flexibleVendingIbcUskMinter: MinterInfo = {
supportedToken: ibcUsk,
updatable: false,
flexible: true,
merkleTree: false,
featured: false,
}
@@ -511,6 +719,7 @@ export const flexibleVendingUpdatableIbcUskMinter: MinterInfo = {
supportedToken: ibcUsk,
updatable: true,
flexible: true,
merkleTree: false,
featured: false,
}
@@ -520,17 +729,19 @@ export const flexibleVendingIbcKujiMinter: MinterInfo = {
supportedToken: ibcKuji,
updatable: false,
flexible: true,
merkleTree: false,
featured: false,
}
export const flexibleVendingIbcHuahuaMinter: MinterInfo = {
id: 'flexible-vending-ibc-huahua-minter',
factoryAddress: VENDING_IBC_HUAHUA_FACTORY_FLEX_ADDRESS,
supportedToken: ibcHuahua,
updatable: false,
flexible: true,
featured: false,
}
// export const flexibleVendingIbcHuahuaMinter: MinterInfo = {
// id: 'flexible-vending-ibc-huahua-minter',
// factoryAddress: VENDING_IBC_HUAHUA_FACTORY_FLEX_ADDRESS,
// supportedToken: ibcHuahua,
// updatable: false,
// flexible: true,
// merkleTree: false,
// featured: false,
// }
export const flexibleVendingIbcCrbrusMinter: MinterInfo = {
id: 'flexible-vending-ibc-crbrus-minter',
@@ -538,6 +749,7 @@ export const flexibleVendingIbcCrbrusMinter: MinterInfo = {
supportedToken: ibcCrbrus,
updatable: false,
flexible: true,
merkleTree: false,
featured: false,
}
@@ -547,6 +759,7 @@ export const flexibleVendingStrdstMinter: MinterInfo = {
supportedToken: nativeStardust,
updatable: false,
flexible: true,
merkleTree: false,
featured: false,
}
@@ -556,6 +769,7 @@ export const flexibleVendingBrnchMinter: MinterInfo = {
supportedToken: nativeBrnch,
updatable: false,
flexible: true,
merkleTree: false,
featured: false,
}
@@ -566,14 +780,65 @@ export const flexibleVendingMinterList = [
flexibleVendingIbcAtomMinter,
flexibleVendingUpdatableIbcAtomMinter,
flexibleVendingIbcUsdcMinter,
flexibleFeaturedVendingIbcUsdcMinter,
flexibleVendingUpdatableIbcUsdcMinter,
flexibleVendingIbcTiaMinter,
flexibleFeaturedVendingIbcTiaMinter,
flexibleVendingUpdatableIbcTiaMinter,
flexibleVendingIbcNbtcMinter,
flexibleVendingUpdatableIbcNbtcMinter,
flexibleVendingIbcUskMinter,
flexibleVendingUpdatableIbcUskMinter,
flexibleVendingIbcKujiMinter,
flexibleVendingIbcHuahuaMinter,
// flexibleVendingIbcHuahuaMinter,
flexibleVendingIbcCrbrusMinter,
flexibleVendingStrdstMinter,
flexibleVendingBrnchMinter,
]
export const merkleTreeVendingStarsMinter: MinterInfo = {
id: 'merkletree-vending-stars-minter',
factoryAddress: VENDING_FACTORY_MERKLE_TREE_ADDRESS,
supportedToken: stars,
updatable: false,
flexible: false,
merkleTree: true,
featured: false,
}
export const merkleTreeVendingFeaturedStarsMinter: MinterInfo = {
id: 'merkletree-vending-featured-stars-minter',
factoryAddress: FEATURED_VENDING_FACTORY_MERKLE_TREE_ADDRESS,
supportedToken: stars,
updatable: false,
flexible: false,
merkleTree: true,
featured: true,
}
export const merkleTreeVendingIbcTiaMinter: MinterInfo = {
id: 'merkletree-vending-ibc-tia-minter',
factoryAddress: VENDING_IBC_TIA_FACTORY_MERKLE_TREE_ADDRESS,
supportedToken: ibcTia,
updatable: false,
flexible: false,
merkleTree: true,
featured: false,
}
export const merkleTreeVendingFeaturedIbcTiaMinter: MinterInfo = {
id: 'merkletree-vending-featured-ibc-tia-minter',
factoryAddress: FEATURED_VENDING_IBC_TIA_FACTORY_MERKLE_TREE_ADDRESS,
supportedToken: ibcTia,
updatable: false,
flexible: false,
merkleTree: true,
featured: true,
}
export const merkleTreeVendingMinterList = [
merkleTreeVendingStarsMinter,
merkleTreeVendingIbcTiaMinter,
merkleTreeVendingFeaturedStarsMinter,
merkleTreeVendingFeaturedIbcTiaMinter,
]
+20
View File
@@ -19,6 +19,25 @@ export const mainnetConfig: AppConfig = {
},
}
export const fangTestnetConfig: AppConfig = {
chainId: 'fang-1',
chainName: 'fang-1',
addressPrefix: 'stars',
rpcUrl: 'https://rpc.fang-1.stargaze-apis.com/',
httpUrl: 'https://rest.fang-1.stargaze-apis.com/',
feeToken: 'ustars',
stakingToken: 'ustars',
coinMap: {
ustars: { denom: 'STARS', fractionalDigits: 6 },
},
gasPrice: 0.025,
fees: {
upload: 1500000,
init: 500000,
exec: 200000,
},
}
export const testnetConfig: AppConfig = {
chainId: 'elgafar-1',
chainName: 'elgafar-1',
@@ -40,5 +59,6 @@ export const testnetConfig: AppConfig = {
export const getConfig = (network: string): AppConfig => {
if (network === 'mainnet') return mainnetConfig
else if (network === 'fang-testnet') return fangTestnetConfig
return testnetConfig
}
+21 -10
View File
@@ -69,15 +69,15 @@ export const ibcNbtc: TokenInfo = {
decimalPlaces: 6,
}
export const ibcHuahua: TokenInfo = {
id: 'ibc-huahua',
denom:
NETWORK === 'mainnet'
? 'ibc/CAD8A9F306CAAC55731C66930D6BEE539856DD12E59061C965E44D82AA26A0E7'
: 'factory/stars153w5xhuqu3et29lgqk4dsynj6gjn96lr33wx4e/uhuahua',
displayName: 'HUAHUA',
decimalPlaces: 6,
}
// export const ibcHuahua: TokenInfo = {
// id: 'ibc-huahua',
// denom:
// NETWORK === 'mainnet'
// ? 'ibc/CAD8A9F306CAAC55731C66930D6BEE539856DD12E59061C965E44D82AA26A0E7'
// : 'factory/stars153w5xhuqu3et29lgqk4dsynj6gjn96lr33wx4e/uhuahua',
// displayName: 'HUAHUA',
// decimalPlaces: 6,
// }
export const ibcCrbrus: TokenInfo = {
id: 'ibc-crbrus',
@@ -89,6 +89,16 @@ export const ibcCrbrus: TokenInfo = {
decimalPlaces: 6,
}
export const ibcTia: TokenInfo = {
id: 'ibc-tia',
denom:
NETWORK === 'mainnet'
? 'ibc/14D1406D84227FDF4B055EA5CB2298095BBCA3F3BC3EF583AE6DF36F0FB179C8'
: 'factory/stars153w5xhuqu3et29lgqk4dsynj6gjn96lr33wx4e/utia',
displayName: 'TIA',
decimalPlaces: 6,
}
export const nativeStardust: TokenInfo = {
id: 'native-strdst',
denom:
@@ -117,8 +127,9 @@ export const tokensList = [
ibcFrnz,
ibcNbtc,
ibcKuji,
ibcHuahua,
// ibcHuahua,
ibcCrbrus,
ibcTia,
nativeStardust,
nativeBrnch,
]
+19 -1
View File
@@ -16,6 +16,7 @@ import type { UseVendingMinterContractProps } from 'contracts/vendingMinter'
import { useVendingMinterContract } from 'contracts/vendingMinter'
import type { UseWhiteListContractProps } from 'contracts/whitelist'
import { useWhiteListContract } from 'contracts/whitelist'
import { type UseWhiteListMerkleTreeContractProps, useWhiteListMerkleTreeContract } from 'contracts/whitelistMerkleTree'
import type { ReactNode, VFC } from 'react'
import { Fragment, useEffect } from 'react'
import { create } from 'zustand'
@@ -32,6 +33,7 @@ export interface ContractsStore {
baseMinter: UseBaseMinterContractProps | null
openEditionMinter: UseOpenEditionMinterContractProps | null
whitelist: UseWhiteListContractProps | null
whitelistMerkleTree: UseWhiteListMerkleTreeContractProps | null
vendingFactory: UseVendingFactoryContractProps | null
baseFactory: UseBaseFactoryContractProps | null
openEditionFactory: UseOpenEditionFactoryContractProps | null
@@ -49,6 +51,7 @@ export const defaultValues: ContractsStore = {
baseMinter: null,
openEditionMinter: null,
whitelist: null,
whitelistMerkleTree: null,
vendingFactory: null,
baseFactory: null,
openEditionFactory: null,
@@ -83,6 +86,7 @@ const ContractsSubscription: VFC = () => {
const baseMinter = useBaseMinterContract()
const openEditionMinter = useOpenEditionMinterContract()
const whitelist = useWhiteListContract()
const whitelistMerkleTree = useWhiteListMerkleTreeContract()
const vendingFactory = useVendingFactoryContract()
const baseFactory = useBaseFactoryContract()
const openEditionFactory = useOpenEditionFactoryContract()
@@ -97,6 +101,7 @@ const ContractsSubscription: VFC = () => {
baseMinter,
openEditionMinter,
whitelist,
whitelistMerkleTree,
vendingFactory,
baseFactory,
openEditionFactory,
@@ -104,7 +109,20 @@ const ContractsSubscription: VFC = () => {
splits,
royaltyRegistry,
})
}, [sg721, vendingMinter, baseMinter, whitelist, vendingFactory, baseFactory, badgeHub, splits, royaltyRegistry])
}, [
sg721,
vendingMinter,
baseMinter,
whitelist,
whitelistMerkleTree,
vendingFactory,
baseFactory,
badgeHub,
splits,
royaltyRegistry,
openEditionMinter,
openEditionFactory,
])
return null
}
+2 -2
View File
@@ -70,8 +70,8 @@ export const baseFactory = (client: SigningCosmWasmClient, txSigner: string): Ba
)
return {
baseMinterAddress: result.logs[0].events[5].attributes[0].value,
sg721Address: result.logs[0].events[5].attributes[2].value,
baseMinterAddress: result.logs[0].events[16].attributes[0].value,
sg721Address: result.logs[0].events[18].attributes[0].value,
transactionHash: result.transactionHash,
logs: result.logs,
}
+2 -2
View File
@@ -61,8 +61,8 @@ export const openEditionFactory = (client: SigningCosmWasmClient, txSigner: stri
const result = await client.execute(senderAddress, contractAddress, msg, 'auto', '', funds)
return {
openEditionMinterAddress: result.logs[0].events[5].attributes[0].value,
sg721Address: result.logs[0].events[5].attributes[2].value,
openEditionMinterAddress: result.logs[0].events[16].attributes[0].value,
sg721Address: result.logs[0].events[18].attributes[0].value,
transactionHash: result.transactionHash,
logs: result.logs,
}
+1
View File
@@ -25,6 +25,7 @@ export interface CollectionInfo {
external_link?: string
explicit_content?: boolean
royalty_info?: RoyaltyInfo | undefined
creator?: string
}
export interface SG721Instance {
+9 -3
View File
@@ -1,7 +1,8 @@
/* eslint-disable eslint-comments/disable-enable-pair */
import type { StdFee } from '@cosmjs/amino'
import type { SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate'
import type { Coin } from '@cosmjs/proto-signing'
import { type Coin, coins } from '@cosmjs/proto-signing'
import type { logs } from '@cosmjs/stargate'
export interface CreateVendingMinterResponse {
@@ -52,6 +53,11 @@ export const vendingFactory = (client: SigningCosmWasmClient, txSigner: string):
const use = (contractAddress: string): VendingFactoryInstance => {
//Query
const defaultFee: StdFee = {
amount: coins('100000', 'ustars'),
gas: '100000',
}
//Execute
const createVendingMinter = async (
senderAddress: string,
@@ -63,8 +69,8 @@ export const vendingFactory = (client: SigningCosmWasmClient, txSigner: string):
const result = await client.execute(senderAddress, contractAddress, msg, 'auto', '', funds)
return {
vendingMinterAddress: result.logs[0].events[5].attributes[0].value,
sg721Address: result.logs[0].events[5].attributes[2].value,
vendingMinterAddress: result.logs[0].events[16].attributes[0].value,
sg721Address: result.logs[0].events[18].attributes[0].value,
transactionHash: result.transactionHash,
logs: result.logs,
}
+375
View File
@@ -0,0 +1,375 @@
import type { SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate'
import { type Coin, coin } from '@cosmjs/proto-signing'
import type { WhitelistFlexMember } from 'components/WhitelistFlexUpload'
export interface InstantiateResponse {
readonly contractAddress: string
readonly transactionHash: string
}
export interface ConfigResponse {
readonly per_address_limit: number
readonly start_time: string
readonly end_time: string
readonly mint_price: Coin
readonly is_active: boolean
}
export interface WhiteListMerkleTreeInstance {
readonly contractAddress: string
//Query
hasStarted: () => Promise<boolean>
hasEnded: () => Promise<boolean>
isActive: () => Promise<boolean>
hasMember: (member: string, proof_hashes: string[]) => Promise<boolean>
adminList: () => Promise<string[]>
config: () => Promise<ConfigResponse>
canExecute: (sender: string, msg: string) => Promise<boolean>
merkleRoot: () => Promise<string>
merkleTreeUri: () => Promise<string>
//Execute
updateStartTime: (startTime: string) => Promise<string>
updateEndTime: (endTime: string) => Promise<string>
addMembers: (memberList: string[] | WhitelistFlexMember[]) => Promise<string>
removeMembers: (memberList: string[]) => Promise<string>
// updatePerAddressLimit: (limit: number) => Promise<string>
updateAdmins: (admins: string[]) => Promise<string>
freeze: () => Promise<string>
}
export interface WhiteListMerkleTreeMessages {
updateStartTime: (startTime: string) => UpdateStartTimeMessage
updateEndTime: (endTime: string) => UpdateEndTimeMessage
addMembers: (memberList: string[] | WhitelistFlexMember[]) => AddMembersMessage
removeMembers: (memberList: string[]) => RemoveMembersMessage
// updatePerAddressLimit: (limit: number) => UpdatePerAddressLimitMessage
updateAdmins: (admins: string[]) => UpdateAdminsMessage
freeze: () => FreezeMessage
}
export interface UpdateStartTimeMessage {
sender: string
contract: string
msg: {
update_start_time: string
}
funds: Coin[]
}
export interface UpdateEndTimeMessage {
sender: string
contract: string
msg: {
update_end_time: string
}
funds: Coin[]
}
export interface UpdateAdminsMessage {
sender: string
contract: string
msg: {
update_admins: { admins: string[] }
}
funds: Coin[]
}
export interface FreezeMessage {
sender: string
contract: string
msg: { freeze: Record<string, never> }
funds: Coin[]
}
export interface AddMembersMessage {
sender: string
contract: string
msg: {
add_members: { to_add: string[] | WhitelistFlexMember[] }
}
funds: Coin[]
}
export interface RemoveMembersMessage {
sender: string
contract: string
msg: {
remove_members: { to_remove: string[] }
}
funds: Coin[]
}
// export interface UpdatePerAddressLimitMessage {
// sender: string
// contract: string
// msg: {
// update_per_address_limit: number
// }
// funds: Coin[]
// }
export interface WhiteListMerkleTreeContract {
instantiate: (
codeId: number,
initMsg: Record<string, unknown>,
label: string,
admin?: string,
) => Promise<InstantiateResponse>
use: (contractAddress: string) => WhiteListMerkleTreeInstance
messages: (contractAddress: string) => WhiteListMerkleTreeMessages
}
export const WhiteListMerkleTree = (client: SigningCosmWasmClient, txSigner: string): WhiteListMerkleTreeContract => {
const use = (contractAddress: string): WhiteListMerkleTreeInstance => {
///QUERY START
const hasStarted = async (): Promise<boolean> => {
return client.queryContractSmart(contractAddress, { has_started: {} })
}
const hasEnded = async (): Promise<boolean> => {
return client.queryContractSmart(contractAddress, { has_ended: {} })
}
const isActive = async (): Promise<boolean> => {
return client.queryContractSmart(contractAddress, { is_active: {} })
}
const hasMember = async (member: string, proofHashes: string[]): Promise<boolean> => {
return client.queryContractSmart(contractAddress, {
has_member: { member, proof_hashes: proofHashes },
})
}
const adminList = async (): Promise<string[]> => {
return client.queryContractSmart(contractAddress, {
admin_list: {},
})
}
const config = async (): Promise<ConfigResponse> => {
return client.queryContractSmart(contractAddress, {
config: {},
})
}
const merkleRoot = async (): Promise<string> => {
return client.queryContractSmart(contractAddress, {
merkle_root: {},
})
}
const merkleTreeUri = async (): Promise<string> => {
return client.queryContractSmart(contractAddress, {
merkle_tree_u_r_i: {},
})
}
const canExecute = async (sender: string, msg: string): Promise<boolean> => {
return client.queryContractSmart(contractAddress, {
can_execute: { sender, msg },
})
}
/// QUERY END
/// EXECUTE START
const updateStartTime = async (startTime: string): Promise<string> => {
const res = await client.execute(txSigner, contractAddress, { update_start_time: startTime }, 'auto')
return res.transactionHash
}
const updateEndTime = async (endTime: string): Promise<string> => {
const res = await client.execute(txSigner, contractAddress, { update_end_time: endTime }, 'auto')
return res.transactionHash
}
const addMembers = async (memberList: string[] | WhitelistFlexMember[]): Promise<string> => {
const res = await client.execute(
txSigner,
contractAddress,
{
add_members: {
to_add: memberList,
},
},
'auto',
)
return res.transactionHash
}
const updateAdmins = async (admins: string[]): Promise<string> => {
const res = await client.execute(
txSigner,
contractAddress,
{
update_admins: {
admins,
},
},
'auto',
)
return res.transactionHash
}
const freeze = async (): Promise<string> => {
const res = await client.execute(
txSigner,
contractAddress,
{
freeze: {},
},
'auto',
)
return res.transactionHash
}
const removeMembers = async (memberList: string[]): Promise<string> => {
const res = await client.execute(
txSigner,
contractAddress,
{
remove_members: {
to_remove: memberList,
},
},
'auto',
)
return res.transactionHash
}
// const updatePerAddressLimit = async (limit: number): Promise<string> => {
// const res = await client.execute(txSigner, contractAddress, { update_per_address_limit: limit }, 'auto')
// return res.transactionHash
// }
/// EXECUTE END
return {
contractAddress,
updateStartTime,
updateEndTime,
updateAdmins,
freeze,
addMembers,
removeMembers,
// updatePerAddressLimit,
hasStarted,
hasEnded,
isActive,
hasMember,
adminList,
config,
merkleRoot,
merkleTreeUri,
canExecute,
}
}
const instantiate = async (
codeId: number,
initMsg: Record<string, unknown>,
label: string,
admin?: string,
): Promise<InstantiateResponse> => {
const result = await client.instantiate(txSigner, codeId, initMsg, label, 'auto', {
admin,
funds: [coin(1000000000, 'ustars')],
})
return {
contractAddress: result.contractAddress,
transactionHash: result.transactionHash,
}
}
const messages = (contractAddress: string) => {
const updateStartTime = (startTime: string) => {
return {
sender: txSigner,
contract: contractAddress,
msg: {
update_start_time: startTime,
},
funds: [],
}
}
const updateEndTime = (endTime: string) => {
return {
sender: txSigner,
contract: contractAddress,
msg: {
update_end_time: endTime,
},
funds: [],
}
}
const addMembers = (memberList: string[] | WhitelistFlexMember[]) => {
return {
sender: txSigner,
contract: contractAddress,
msg: {
add_members: { to_add: memberList },
},
funds: [],
}
}
const updateAdmins = (admins: string[]) => {
return {
sender: txSigner,
contract: contractAddress,
msg: {
update_admins: { admins },
},
funds: [],
}
}
const freeze = () => {
return {
sender: txSigner,
contract: contractAddress,
msg: {
freeze: {},
},
funds: [],
}
}
const removeMembers = (memberList: string[]) => {
return {
sender: txSigner,
contract: contractAddress,
msg: {
remove_members: { to_remove: memberList },
},
funds: [],
}
}
// const updatePerAddressLimit = (limit: number) => {
// return {
// sender: txSigner,
// contract: contractAddress,
// msg: {
// update_per_address_limit: limit,
// },
// funds: [],
// }
// }
return {
updateStartTime,
updateEndTime,
updateAdmins,
addMembers,
removeMembers,
// updatePerAddressLimit,
freeze,
}
}
return { use, instantiate, messages }
}
+2
View File
@@ -0,0 +1,2 @@
export * from './contract'
export * from './useContract'
@@ -0,0 +1,144 @@
import type { WhitelistFlexMember } from '../../../components/WhitelistFlexUpload'
import type { WhiteListMerkleTreeInstance } from '../index'
import { useWhiteListMerkleTreeContract } from '../index'
export type ExecuteType = typeof EXECUTE_TYPES[number]
export const EXECUTE_TYPES = [
'update_start_time',
'update_end_time',
'update_admins',
'add_members',
'remove_members',
// 'update_per_address_limit',
'freeze',
] as const
export interface ExecuteListItem {
id: ExecuteType
name: string
description?: string
}
export const EXECUTE_LIST: ExecuteListItem[] = [
{
id: 'update_start_time',
name: 'Update Start Time',
description: `Update the start time of the whitelist`,
},
{
id: 'update_end_time',
name: 'Update End Time',
description: `Update the end time of the whitelist`,
},
{
id: 'update_admins',
name: 'Update Admins',
description: `Update the list of administrators for the whitelist`,
},
// {
// id: 'add_members',
// name: 'Add Members',
// description: `Add members to the whitelist`,
// },
// {
// id: 'remove_members',
// name: 'Remove Members',
// description: `Remove members from the whitelist`,
// },
// {
// id: 'update_per_address_limit',
// name: 'Update Per Address Limit',
// description: `Update tokens per address limit`,
// },
{
id: 'freeze',
name: 'Freeze',
description: `Freeze the current state of the contract admin list`,
},
]
export interface DispatchExecuteProps {
type: ExecuteType
[k: string]: unknown
}
/** @see {@link WhiteListMerkleTreeInstance} */
export interface DispatchExecuteArgs {
contract: string
messages?: WhiteListMerkleTreeInstance
type: string | undefined
timestamp: string
members: string[] | WhitelistFlexMember[]
limit: number
admins: string[]
}
export const dispatchExecute = async (args: DispatchExecuteArgs) => {
const { messages } = args
if (!messages) {
throw new Error('cannot dispatch execute, messages is not defined')
}
switch (args.type) {
case 'update_start_time': {
return messages.updateStartTime(args.timestamp)
}
case 'update_end_time': {
return messages.updateEndTime(args.timestamp)
}
case 'update_admins': {
return messages.updateAdmins(args.admins)
}
case 'add_members': {
return messages.addMembers(args.members)
}
case 'remove_members': {
return messages.removeMembers(args.members as string[])
}
// case 'update_per_address_limit': {
// return messages.updatePerAddressLimit(args.limit)
// }
case 'freeze': {
return messages.freeze()
}
default: {
throw new Error('unknown execute type')
}
}
}
export const previewExecutePayload = (args: DispatchExecuteArgs) => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const { messages } = useWhiteListMerkleTreeContract()
const { contract } = args
switch (args.type) {
case 'update_start_time': {
return messages(contract)?.updateStartTime(args.timestamp)
}
case 'update_end_time': {
return messages(contract)?.updateEndTime(args.timestamp)
}
case 'update_admins': {
return messages(contract)?.updateAdmins(args.admins)
}
case 'add_members': {
return messages(contract)?.addMembers(args.members)
}
case 'remove_members': {
return messages(contract)?.removeMembers(args.members as string[])
}
// case 'update_per_address_limit': {
// return messages(contract)?.updatePerAddressLimit(args.limit)
// }
case 'freeze': {
return messages(contract)?.freeze()
}
default: {
return {}
}
}
}
export const isEitherType = <T extends ExecuteType>(type: unknown, arr: T[]): type is T => {
return arr.some((val) => type === val)
}
@@ -0,0 +1,66 @@
import type { WhiteListMerkleTreeInstance } from '../contract'
export type WhitelistMerkleTreeQueryType = typeof WHITELIST_MERKLE_TREE_QUERY_TYPES[number]
export const WHITELIST_MERKLE_TREE_QUERY_TYPES = [
'has_started',
'has_ended',
'is_active',
'admin_list',
'has_member',
'config',
'merkle_root',
'merkle_tree_uri',
] as const
export interface QueryListItem {
id: WhitelistMerkleTreeQueryType
name: string
description?: string
}
export const WHITELIST_MERKLE_TREE_QUERY_LIST: QueryListItem[] = [
{ id: 'has_started', name: 'Has Started', description: 'Check if the whitelist minting has started' },
{ id: 'has_ended', name: 'Has Ended', description: 'Check if the whitelist minting has ended' },
{ id: 'is_active', name: 'Is Active', description: 'Check if the whitelist minting is active' },
{ id: 'admin_list', name: 'Admin List', description: 'View the whitelist admin list' },
{ id: 'has_member', name: 'Has Member', description: 'Check if a member is in the whitelist' },
{ id: 'config', name: 'Config', description: 'View the whitelist configuration' },
{ id: 'merkle_root', name: 'Merkle Root', description: 'View the whitelist merkle root' },
{ id: 'merkle_tree_uri', name: 'Merkle Tree URI', description: 'View the whitelist merkle tree URI' },
]
export interface DispatchQueryProps {
messages: WhiteListMerkleTreeInstance | undefined
type: WhitelistMerkleTreeQueryType
address: string
startAfter?: string
limit?: number
proofHashes?: string[]
}
export const dispatchQuery = (props: DispatchQueryProps) => {
const { messages, type, address, proofHashes } = props
switch (type) {
case 'has_started':
return messages?.hasStarted()
case 'has_ended':
return messages?.hasEnded()
case 'is_active':
return messages?.isActive()
case 'admin_list':
return messages?.adminList()
case 'has_member':
return messages?.hasMember(address, proofHashes || [])
case 'config':
return messages?.config()
case 'merkle_root':
return messages?.merkleRoot()
case 'merkle_tree_uri':
return messages?.merkleTreeUri()
default: {
throw new Error('unknown query type')
}
}
}
@@ -0,0 +1,89 @@
import { useCallback, useEffect, useState } from 'react'
import { useWallet } from 'utils/wallet'
import type {
InstantiateResponse,
WhiteListMerkleTreeContract,
WhiteListMerkleTreeInstance,
WhiteListMerkleTreeMessages,
} from './contract'
import { WhiteListMerkleTree as initContract } from './contract'
export interface UseWhiteListMerkleTreeContractProps {
instantiate: (
codeId: number,
initMsg: Record<string, unknown>,
label: string,
admin?: string,
) => Promise<InstantiateResponse>
use: (customAddress?: string) => WhiteListMerkleTreeInstance | undefined
updateContractAddress: (contractAddress: string) => void
messages: (contractAddress: string) => WhiteListMerkleTreeMessages | undefined
}
export function useWhiteListMerkleTreeContract(): UseWhiteListMerkleTreeContractProps {
const wallet = useWallet()
const [address, setAddress] = useState<string>('')
const [whiteListMerkleTree, setWhiteListMerkleTree] = useState<WhiteListMerkleTreeContract>()
useEffect(() => {
setAddress(localStorage.getItem('contract_address') || '')
}, [])
useEffect(() => {
if (!wallet.isWalletConnected) {
return
}
const load = async () => {
const client = await wallet.getSigningCosmWasmClient()
const contract = initContract(client, wallet.address || '')
setWhiteListMerkleTree(contract)
}
load().catch(console.error)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [wallet.isWalletConnected, wallet.address])
const updateContractAddress = (contractAddress: string) => {
setAddress(contractAddress)
}
const instantiate = useCallback(
(codeId: number, initMsg: Record<string, unknown>, label: string, admin?: string): Promise<InstantiateResponse> => {
return new Promise((resolve, reject) => {
if (!whiteListMerkleTree) {
reject(new Error('Contract is not initialized.'))
return
}
whiteListMerkleTree.instantiate(codeId, initMsg, label, admin).then(resolve).catch(reject)
})
},
[whiteListMerkleTree],
)
const use = useCallback(
(customAddress = ''): WhiteListMerkleTreeInstance | undefined => {
return whiteListMerkleTree?.use(address || customAddress)
},
[whiteListMerkleTree, address],
)
const messages = useCallback(
(customAddress = ''): WhiteListMerkleTreeMessages | undefined => {
return whiteListMerkleTree?.messages(address || customAddress)
},
[whiteListMerkleTree, address],
)
return {
instantiate,
use,
updateContractAddress,
messages,
}
}
Vendored
+21
View File
@@ -22,18 +22,25 @@ declare namespace NodeJS {
readonly NEXT_PUBLIC_OPEN_EDITION_SG721_UPDATABLE_CODE_ID: string
readonly NEXT_PUBLIC_WHITELIST_CODE_ID: string
readonly NEXT_PUBLIC_WHITELIST_FLEX_CODE_ID: string
readonly NEXT_PUBLIC_WHITELIST_MERKLE_TREE_CODE_ID: string
readonly NEXT_PUBLIC_VENDING_MINTER_CODE_ID: string
readonly NEXT_PUBLIC_VENDING_MINTER_FLEX_CODE_ID: string
readonly NEXT_PUBLIC_VENDING_FACTORY_ADDRESS: string
readonly NEXT_PUBLIC_FEATURED_VENDING_FACTORY_ADDRESS: string
readonly NEXT_PUBLIC_VENDING_FACTORY_UPDATABLE_ADDRESS: string
readonly NEXT_PUBLIC_VENDING_FACTORY_FLEX_ADDRESS: string
readonly NEXT_PUBLIC_VENDING_FACTORY_MERKLE_TREE_ADDRESS: string
readonly NEXT_PUBLIC_FEATURED_VENDING_FACTORY_MERKLE_TREE_ADDRESS: string
readonly NEXT_PUBLIC_FEATURED_VENDING_FACTORY_FLEX_ADDRESS: string
readonly NEXT_PUBLIC_VENDING_FACTORY_UPDATABLE_FLEX_ADDRESS: string
readonly NEXT_PUBLIC_VENDING_IBC_ATOM_FACTORY_ADDRESS: string
readonly NEXT_PUBLIC_VENDING_IBC_ATOM_UPDATABLE_FACTORY_ADDRESS: string
readonly NEXT_PUBLIC_VENDING_IBC_USDC_FACTORY_ADDRESS: string
readonly NEXT_PUBLIC_FEATURED_VENDING_IBC_USDC_FACTORY_ADDRESS: string
readonly NEXT_PUBLIC_VENDING_IBC_USDC_UPDATABLE_FACTORY_ADDRESS: string
readonly NEXT_PUBLIC_VENDING_IBC_TIA_FACTORY_ADDRESS: string
readonly NEXT_PUBLIC_FEATURED_VENDING_IBC_TIA_FACTORY_ADDRESS: string
readonly NEXT_PUBLIC_VENDING_IBC_TIA_UPDATABLE_FACTORY_ADDRESS: string
readonly NEXT_PUBLIC_VENDING_IBC_NBTC_FACTORY_ADDRESS: string
readonly NEXT_PUBLIC_VENDING_IBC_NBTC_UPDATABLE_FACTORY_ADDRESS: string
readonly NEXT_PUBLIC_VENDING_IBC_USK_FACTORY_ADDRESS: string
@@ -47,7 +54,13 @@ declare namespace NodeJS {
readonly NEXT_PUBLIC_VENDING_IBC_ATOM_FACTORY_FLEX_ADDRESS: string
readonly NEXT_PUBLIC_VENDING_IBC_ATOM_UPDATABLE_FACTORY_FLEX_ADDRESS: string
readonly NEXT_PUBLIC_VENDING_IBC_USDC_FACTORY_FLEX_ADDRESS: string
readonly NEXT_PUBLIC_FEATURED_VENDING_IBC_USDC_FACTORY_FLEX_ADDRESS: string
readonly NEXT_PUBLIC_VENDING_IBC_USDC_UPDATABLE_FACTORY_FLEX_ADDRESS: string
readonly NEXT_PUBLIC_VENDING_IBC_TIA_FACTORY_FLEX_ADDRESS: string
readonly NEXT_PUBLIC_VENDING_IBC_TIA_FACTORY_MERKLE_TREE_ADDRESS: string
readonly NEXT_PUBLIC_FEATURED_VENDING_IBC_TIA_FACTORY_MERKLE_TREE_ADDRESS: string
readonly NEXT_PUBLIC_FEATURED_VENDING_IBC_TIA_FACTORY_FLEX_ADDRESS: string
readonly NEXT_PUBLIC_VENDING_IBC_TIA_UPDATABLE_FACTORY_FLEX_ADDRESS: string
readonly NEXT_PUBLIC_VENDING_IBC_NBTC_FACTORY_FLEX_ADDRESS: string
readonly NEXT_PUBLIC_VENDING_IBC_NBTC_UPDATABLE_FACTORY_FLEX_ADDRESS: string
readonly NEXT_PUBLIC_VENDING_IBC_USK_FACTORY_FLEX_ADDRESS: string
@@ -65,11 +78,17 @@ declare namespace NodeJS {
readonly NEXT_PUBLIC_VENDING_NATIVE_BRNCH_UPDATABLE_FACTORY_ADDRESS: string
readonly NEXT_PUBLIC_VENDING_NATIVE_BRNCH_FLEX_FACTORY_ADDRESS: string
readonly NEXT_PUBLIC_OPEN_EDITION_FACTORY_ADDRESS: string
readonly NEXT_PUBLIC_OPEN_EDITION_FACTORY_FLEX_ADDRESS: string
readonly NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_FACTORY_ADDRESS: string
readonly NEXT_PUBLIC_OPEN_EDITION_IBC_ATOM_FACTORY_ADDRESS: string
readonly NEXT_PUBLIC_OPEN_EDITION_IBC_ATOM_FACTORY_FLEX_ADDRESS: string
readonly NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_ATOM_FACTORY_ADDRESS: string
readonly NEXT_PUBLIC_OPEN_EDITION_IBC_USDC_FACTORY_ADDRESS: string
readonly NEXT_PUBLIC_OPEN_EDITION_IBC_USDC_FACTORY_FLEX_ADDRESS: string
readonly NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_USDC_FACTORY_ADDRESS: string
readonly NEXT_PUBLIC_OPEN_EDITION_IBC_TIA_FACTORY_ADDRESS: string
readonly NEXT_PUBLIC_OPEN_EDITION_IBC_TIA_FACTORY_FLEX_ADDRESS: string
readonly NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_TIA_FACTORY_ADDRESS: string
readonly NEXT_PUBLIC_OPEN_EDITION_IBC_NBTC_FACTORY_ADDRESS: string
readonly NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_NBTC_FACTORY_ADDRESS: string
readonly NEXT_PUBLIC_OPEN_EDITION_IBC_FRNZ_FACTORY_ADDRESS: string
@@ -104,6 +123,8 @@ declare namespace NodeJS {
readonly NEXT_PUBLIC_STARGAZE_WEBSITE_URL: string
readonly NEXT_PUBLIC_WEBSITE_URL: string
readonly NEXT_PUBLIC_SYNC_COLLECTIONS_API_URL: string
readonly NEXT_PUBLIC_WHITELIST_MERKLE_TREE_API_URL: string
readonly NEXT_PUBLIC_NFT_STORAGE_DEFAULT_API_KEY: string
readonly NEXT_PUBLIC_MEILISEARCH_HOST: string
readonly NEXT_PUBLIC_MEILISEARCH_API_KEY: string
+3
View File
@@ -23,8 +23,11 @@
"@cosmos-kit/leap": "^2.4.3",
"@cosmos-kit/leap-metamask-cosmos-snap": "^0.3.3",
"@cosmos-kit/react": "^2.9.3",
"crypto-js": "4.1.1",
"@types/crypto-js": "4.2.1",
"@fontsource/jetbrains-mono": "^4",
"@fontsource/roboto": "^4",
"merkletreejs": "0.3.11",
"@leapwallet/cosmos-snap-provider": "0.1.24",
"@pinata/sdk": "^1.1.26",
"@popperjs/core": "^2",
+179 -100
View File
@@ -34,7 +34,13 @@ import { FormControl } from 'components/FormControl'
import { LoadingModal } from 'components/LoadingModal'
import type { OpenEditionMinterCreatorDataProps } from 'components/openEdition/OpenEditionMinterCreator'
import { OpenEditionMinterCreator } from 'components/openEdition/OpenEditionMinterCreator'
import { flexibleVendingMinterList, openEditionMinterList, vendingMinterList } from 'config/minter'
import {
flexibleOpenEditionMinterList,
flexibleVendingMinterList,
merkleTreeVendingMinterList,
openEditionMinterList,
vendingMinterList,
} from 'config/minter'
import type { TokenInfo } from 'config/token'
import { useContracts } from 'contexts/contracts'
import { addLogItem } from 'contexts/log'
@@ -56,7 +62,6 @@ import {
BLOCK_EXPLORER_URL,
NETWORK,
OPEN_EDITION_FACTORY_ADDRESS,
OPEN_EDITION_UPDATABLE_FACTORY_ADDRESS,
SG721_CODE_ID,
SG721_UPDATABLE_CODE_ID,
STARGAZE_URL,
@@ -67,6 +72,8 @@ import {
VENDING_FACTORY_UPDATABLE_ADDRESS,
WHITELIST_CODE_ID,
WHITELIST_FLEX_CODE_ID,
WHITELIST_MERKLE_TREE_API_URL,
WHITELIST_MERKLE_TREE_CODE_ID,
} from 'utils/constants'
import { checkTokenUri } from 'utils/isValidTokenUri'
import { withMetadata } from 'utils/layout'
@@ -88,6 +95,7 @@ const CollectionCreationPage: NextPage = () => {
baseMinter: baseMinterContract,
vendingMinter: vendingMinterContract,
whitelist: whitelistContract,
whitelistMerkleTree: whitelistMerkleTreeContract,
vendingFactory: vendingFactoryContract,
baseFactory: baseFactoryContract,
} = useContracts()
@@ -122,20 +130,19 @@ const CollectionCreationPage: NextPage = () => {
const [baseMinterCreationFee, setBaseMinterCreationFee] = useState<string | null>(null)
const [vendingMinterUpdatableCreationFee, setVendingMinterUpdatableCreationFee] = useState<string | null>(null)
const [openEditionMinterCreationFee, setOpenEditionMinterCreationFee] = useState<string | null>(null)
const [openEditionMinterUpdatableCreationFee, setOpenEditionMinterUpdatableCreationFee] = useState<string | null>(
null,
)
const [vendingMinterFlexCreationFee, setVendingMinterFlexCreationFee] = useState<string | null>(null)
const [baseMinterUpdatableCreationFee, setBaseMinterUpdatableCreationFee] = useState<string | null>(null)
const [minimumMintPrice, setMinimumMintPrice] = useState<string | null>('0')
const [minimumUpdatableMintPrice, setMinimumUpdatableMintPrice] = useState<string | null>('0')
const [minimumOpenEditionMintPrice, setMinimumOpenEditionMintPrice] = useState<string | null>('0')
const [minimumOpenEditionUpdatableMintPrice, setMinimumOpenEditionUpdatableMintPrice] = useState<string | null>('0')
const [minimumFlexMintPrice, setMinimumFlexMintPrice] = useState<string | null>('0')
const [mintTokenFromOpenEditionFactory, setMintTokenFromOpenEditionFactory] = useState<TokenInfo | undefined>(stars)
const [mintTokenFromVendingFactory, setMintTokenFromVendingFactory] = useState<TokenInfo | undefined>(stars)
const [vendingFactoryAddress, setVendingFactoryAddress] = useState<string | null>(VENDING_FACTORY_ADDRESS)
const [openEditionFactoryAddress, setOpenEditionFactoryAddress] = useState<string | undefined>(
OPEN_EDITION_FACTORY_ADDRESS,
)
const vendingFactoryMessages = useMemo(
() => vendingFactoryContract?.use(vendingFactoryAddress as string),
@@ -162,6 +169,7 @@ const CollectionCreationPage: NextPage = () => {
const [coverImageUrl, setCoverImageUrl] = useState<string | null>(null)
const [transactionHash, setTransactionHash] = useState<string | null>(null)
const [isMatchingVendingFactoryPresent, setIsMatchingVendingFactoryPresent] = useState<boolean>(true)
const [isMatchingOpenEditionFactoryPresent, setIsMatchingOpenEditionFactoryPresent] = useState<boolean>(true)
const performVendingMinterChecks = () => {
try {
@@ -513,41 +521,92 @@ const CollectionCreationPage: NextPage = () => {
if (!wallet.isWalletConnected) throw new Error('Wallet not connected')
if (!whitelistContract) throw new Error('Contract not found')
const standardMsg = {
members: whitelistDetails?.members,
start_time: whitelistDetails?.startTime,
end_time: whitelistDetails?.endTime,
mint_price: coin(
String(Number(whitelistDetails?.unitPrice)),
mintTokenFromVendingFactory ? mintTokenFromVendingFactory.denom : 'ustars',
),
per_address_limit: whitelistDetails?.perAddressLimit,
member_limit: whitelistDetails?.memberLimit,
admins: whitelistDetails?.admins || [wallet.address],
admins_mutable: whitelistDetails?.adminsMutable,
if (whitelistDetails?.whitelistType === 'standard' || whitelistDetails?.whitelistType === 'flex') {
const standardMsg = {
members: whitelistDetails.members,
start_time: whitelistDetails.startTime,
end_time: whitelistDetails.endTime,
mint_price: coin(
String(Number(whitelistDetails.unitPrice)),
mintTokenFromVendingFactory ? mintTokenFromVendingFactory.denom : 'ustars',
),
per_address_limit: whitelistDetails.perAddressLimit,
member_limit: whitelistDetails.memberLimit,
admins: whitelistDetails.admins || [wallet.address],
admins_mutable: whitelistDetails.adminsMutable,
}
const flexMsg = {
members: whitelistDetails.members,
start_time: whitelistDetails.startTime,
end_time: whitelistDetails.endTime,
mint_price: coin(
String(Number(whitelistDetails.unitPrice)),
mintTokenFromVendingFactory ? mintTokenFromVendingFactory.denom : 'ustars',
),
member_limit: whitelistDetails.memberLimit,
admins: whitelistDetails.admins || [wallet.address],
admins_mutable: whitelistDetails.adminsMutable,
}
const data = await whitelistContract.instantiate(
whitelistDetails.whitelistType === 'standard' ? WHITELIST_CODE_ID : WHITELIST_FLEX_CODE_ID,
whitelistDetails.whitelistType === 'standard' ? standardMsg : flexMsg,
'Stargaze Whitelist Contract',
wallet.address,
)
return data.contractAddress
} else if (whitelistDetails?.whitelistType === 'merkletree') {
const members = whitelistDetails.members as string[]
const membersCsv = members.join('\n')
const membersBlob = new Blob([membersCsv], { type: 'text/csv' })
const membersFile = new File([membersBlob], 'members.csv', { type: 'text/csv' })
const formData = new FormData()
formData.append('whitelist', membersFile)
const response = await toast
.promise(
axios.post(`${WHITELIST_MERKLE_TREE_API_URL}/create_whitelist`, formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
}),
{
loading: 'Fetching merkle root hash...',
success: 'Merkle root fetched successfully.',
error: 'Error fetching root hash from Whitelist Merkle Tree API.',
},
)
.catch((error) => {
console.log('error', error)
throw new Error('Whitelist instantiation failed.')
})
const rootHash = response.data.root_hash
console.log('rootHash', rootHash)
const merkleTreeMsg = {
merkle_root: rootHash,
merkle_tree_uri: null,
start_time: whitelistDetails.startTime,
end_time: whitelistDetails.endTime,
mint_price: coin(
String(Number(whitelistDetails.unitPrice)),
mintTokenFromVendingFactory ? mintTokenFromVendingFactory.denom : 'ustars',
),
per_address_limit: whitelistDetails.perAddressLimit,
admins: whitelistDetails.admins || [wallet.address],
admins_mutable: whitelistDetails.adminsMutable,
}
const data = await whitelistMerkleTreeContract?.instantiate(
WHITELIST_MERKLE_TREE_CODE_ID,
merkleTreeMsg,
'Stargaze Whitelist Merkle Tree Contract',
wallet.address,
)
return data?.contractAddress
}
const flexMsg = {
members: whitelistDetails?.members,
start_time: whitelistDetails?.startTime,
end_time: whitelistDetails?.endTime,
mint_price: coin(
String(Number(whitelistDetails?.unitPrice)),
mintTokenFromVendingFactory ? mintTokenFromVendingFactory.denom : 'ustars',
),
member_limit: whitelistDetails?.memberLimit,
admins: whitelistDetails?.admins || [wallet.address],
admins_mutable: whitelistDetails?.adminsMutable,
}
const data = await whitelistContract.instantiate(
whitelistDetails?.whitelistType === 'standard' ? WHITELIST_CODE_ID : WHITELIST_FLEX_CODE_ID,
whitelistDetails?.whitelistType === 'standard' ? standardMsg : flexMsg,
'Stargaze Whitelist Contract',
wallet.address,
)
return data.contractAddress
}
const instantiateVendingMinter = async (baseUri: string, coverImageUri: string, whitelist?: string) => {
@@ -582,6 +641,7 @@ const CollectionCreationPage: NextPage = () => {
: mintingDetails?.selectedMintToken?.displayName === 'STRDST' ||
mintingDetails?.selectedMintToken?.displayName === 'USK' ||
mintingDetails?.selectedMintToken?.displayName === 'USDC' ||
mintingDetails?.selectedMintToken?.displayName === 'TIA' ||
mintingDetails?.selectedMintToken?.displayName === 'nBTC' ||
mintingDetails?.selectedMintToken?.displayName === 'KUJI' ||
mintingDetails?.selectedMintToken?.displayName === 'HUAHUA' ||
@@ -1046,6 +1106,8 @@ const CollectionCreationPage: NextPage = () => {
//check if the address belongs to a whitelist contract (see performChecks())
const config = await contract?.config()
if (JSON.stringify(config).includes('whale_cap')) whitelistDetails.whitelistType = 'flex'
else if (!JSON.stringify(config).includes('member_limit') || config?.member_limit === 0)
whitelistDetails.whitelistType = 'merkletree'
else whitelistDetails.whitelistType = 'standard'
if (Number(config?.start_time) !== Number(mintingDetails?.startTime)) {
const whitelistStartDate = new Date(Number(config?.start_time) / 1000000)
@@ -1083,7 +1145,10 @@ const CollectionCreationPage: NextPage = () => {
(!whitelistDetails.perAddressLimit || whitelistDetails.perAddressLimit === 0)
)
throw new Error('Per address limit is required')
if (!whitelistDetails.memberLimit || whitelistDetails.memberLimit === 0)
if (
whitelistDetails.whitelistType !== 'merkletree' &&
(!whitelistDetails.memberLimit || whitelistDetails.memberLimit === 0)
)
throw new Error('Member limit is required')
if (Number(whitelistDetails.startTime) >= Number(whitelistDetails.endTime))
throw new Error('Whitelist start time cannot be equal to or later than the whitelist end time')
@@ -1208,32 +1273,27 @@ const CollectionCreationPage: NextPage = () => {
setOpenEditionMinterCreationFee(openEditionFactoryParameters?.params?.creation_fee?.amount)
setMinimumOpenEditionMintPrice(openEditionFactoryParameters?.params?.min_mint_price?.amount)
}
if (OPEN_EDITION_UPDATABLE_FACTORY_ADDRESS) {
const openEditionUpdatableFactoryParameters = await client
.queryContractSmart(OPEN_EDITION_UPDATABLE_FACTORY_ADDRESS, { params: {} })
.catch((error) => {
toast.error(`${error.message}`, { style: { maxWidth: 'none' } })
addLogItem({ id: uid(), message: error.message, type: 'Error', timestamp: new Date() })
})
setOpenEditionMinterUpdatableCreationFee(openEditionUpdatableFactoryParameters?.params?.creation_fee?.amount)
setMinimumOpenEditionUpdatableMintPrice(openEditionUpdatableFactoryParameters?.params?.min_mint_price?.amount)
}
setInitialParametersFetched(true)
}
const fetchOpenEditionFactoryParameters = useCallback(async () => {
const client = await wallet.getCosmWasmClient()
const factoryForSelectedDenom = openEditionMinterList.find(
(minter) =>
minter.supportedToken === openEditionMinterDetails?.mintingDetails?.selectedMintToken &&
minter.updatable === false,
)
const updatableFactoryForSelectedDenom = openEditionMinterList.find(
(minter) =>
minter.supportedToken === openEditionMinterDetails?.mintingDetails?.selectedMintToken &&
minter.updatable === true,
)
const factoryForSelectedDenom = openEditionMinterList
.concat(flexibleOpenEditionMinterList)
.find(
(minter) =>
minter.supportedToken === openEditionMinterDetails?.mintingDetails?.selectedMintToken &&
minter.updatable === openEditionMinterDetails.collectionDetails?.updatable &&
minter.flexible ===
(openEditionMinterDetails.whitelistDetails?.whitelistState !== 'none' &&
openEditionMinterDetails.whitelistDetails?.whitelistType === 'flex'),
)
console.log('OE Factory: ', factoryForSelectedDenom?.factoryAddress)
if (factoryForSelectedDenom?.factoryAddress) {
setIsMatchingOpenEditionFactoryPresent(true)
setOpenEditionFactoryAddress(factoryForSelectedDenom.factoryAddress)
const openEditionFactoryParameters = await client
.queryContractSmart(factoryForSelectedDenom.factoryAddress, { params: {} })
.catch((error) => {
@@ -1241,48 +1301,40 @@ const CollectionCreationPage: NextPage = () => {
addLogItem({ id: uid(), message: error.message, type: 'Error', timestamp: new Date() })
})
setOpenEditionMinterCreationFee(openEditionFactoryParameters?.params?.creation_fee?.amount)
if (!openEditionMinterDetails?.collectionDetails?.updatable) {
setMinimumOpenEditionMintPrice(openEditionFactoryParameters?.params?.min_mint_price?.amount)
setMintTokenFromOpenEditionFactory(
tokensList.find((token) => token.denom === openEditionFactoryParameters?.params?.min_mint_price?.denom),
)
}
}
if (updatableFactoryForSelectedDenom?.factoryAddress) {
const openEditionUpdatableFactoryParameters = await client
.queryContractSmart(updatableFactoryForSelectedDenom.factoryAddress, { params: {} })
.catch((error) => {
toast.error(`${error.message}`, { style: { maxWidth: 'none' } })
addLogItem({ id: uid(), message: error.message, type: 'Error', timestamp: new Date() })
})
setOpenEditionMinterUpdatableCreationFee(openEditionUpdatableFactoryParameters?.params?.creation_fee?.amount)
if (openEditionMinterDetails?.collectionDetails?.updatable) {
setMinimumOpenEditionUpdatableMintPrice(openEditionUpdatableFactoryParameters?.params?.min_mint_price?.amount)
setMintTokenFromOpenEditionFactory(
tokensList.find(
(token) => token.denom === openEditionUpdatableFactoryParameters?.params?.min_mint_price?.denom,
),
)
}
setMinimumOpenEditionMintPrice(openEditionFactoryParameters?.params?.min_mint_price?.amount)
setMintTokenFromOpenEditionFactory(
tokensList.find((token) => token.denom === openEditionFactoryParameters?.params?.min_mint_price?.denom),
)
} else if (
openEditionMinterDetails?.mintingDetails?.selectedMintToken &&
openEditionMinterDetails.whitelistDetails?.whitelistState
) {
setIsMatchingOpenEditionFactoryPresent(false)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
openEditionMinterDetails?.mintingDetails?.selectedMintToken,
openEditionMinterDetails?.collectionDetails?.updatable,
openEditionMinterDetails?.whitelistDetails?.whitelistType,
openEditionMinterDetails?.whitelistDetails?.whitelistState,
wallet.isWalletConnected,
openEditionMinterDetails?.isRefreshed,
])
const fetchVendingFactoryParameters = useCallback(async () => {
const client = await wallet.getCosmWasmClient()
const vendingFactoryForSelectedDenom = vendingMinterList
.concat(flexibleVendingMinterList)
.concat(merkleTreeVendingMinterList)
.find(
(minter) =>
minter.supportedToken === mintingDetails?.selectedMintToken &&
minter.updatable === collectionDetails?.updatable &&
minter.flexible === (whitelistDetails?.whitelistType === 'flex') &&
minter.merkleTree === (whitelistDetails?.whitelistType === 'merkletree') &&
minter.featured === isFeaturedCollection,
)?.factoryAddress
console.log('Vending Factory: ', vendingFactoryForSelectedDenom)
if (vendingFactoryForSelectedDenom) {
setIsMatchingVendingFactoryPresent(true)
@@ -1321,20 +1373,34 @@ const CollectionCreationPage: NextPage = () => {
const checkwalletBalance = async () => {
await (await wallet.getCosmWasmClient()).getBalance(wallet.address || '', 'ustars').then((balance) => {
if (minterType === 'vending' && whitelistDetails?.whitelistState === 'new' && whitelistDetails.memberLimit) {
const amountNeeded =
Math.ceil(Number(whitelistDetails.memberLimit) / 1000) * 100000000 +
(whitelistDetails.whitelistType === 'flex'
? Number(vendingMinterFlexCreationFee)
: collectionDetails?.updatable
? Number(vendingMinterUpdatableCreationFee)
: Number(vendingMinterCreationFee))
if (amountNeeded >= Number(balance.amount))
throw new Error(
`Insufficient wallet balance to instantiate the required contracts. Needed amount: ${(
amountNeeded / 1000000
).toString()} STARS`,
)
if (minterType === 'vending' && whitelistDetails?.whitelistState === 'new') {
if (whitelistDetails.whitelistType !== 'merkletree' && whitelistDetails.memberLimit) {
const amountNeeded =
Math.ceil(Number(whitelistDetails.memberLimit) / 1000) * 100000000 +
(whitelistDetails.whitelistType === 'flex'
? Number(vendingMinterFlexCreationFee)
: collectionDetails?.updatable
? Number(vendingMinterUpdatableCreationFee)
: Number(vendingMinterCreationFee))
if (amountNeeded >= Number(balance.amount))
throw new Error(
`Insufficient wallet balance to instantiate the required contracts. Needed amount: ${(
amountNeeded / 1000000
).toString()} STARS`,
)
} else if (whitelistDetails.whitelistType === 'merkletree') {
const amountNeeded =
1000000000 +
(collectionDetails?.updatable
? Number(vendingMinterUpdatableCreationFee)
: Number(vendingMinterCreationFee))
if (amountNeeded >= Number(balance.amount))
throw new Error(
`Insufficient wallet balance to instantiate the required contracts. Needed amount: ${(
amountNeeded / 1000000
).toString()} STARS`,
)
}
} else {
const amountNeeded =
minterType === 'vending'
@@ -1540,6 +1606,19 @@ const CollectionCreationPage: NextPage = () => {
>
{openEditionMinterCreatorData?.sg721ContractAddress as string}
</Anchor>
<Conditional test={openEditionMinterCreatorData?.whitelistContractAddress !== null}>
<br />
Whitelist Contract Address:{' '}
<Anchor
className="text-stargaze hover:underline"
external
href={`/contracts/whitelist/query/?contractAddress=${
openEditionMinterCreatorData?.whitelistContractAddress as string
}`}
>
{openEditionMinterCreatorData?.whitelistContractAddress as string}
</Anchor>
</Conditional>
<br />
Transaction Hash: {' '}
<Conditional test={NETWORK === 'testnet'}>
@@ -1848,14 +1927,14 @@ const CollectionCreationPage: NextPage = () => {
<Conditional test={minterType === 'openEdition'}>
<OpenEditionMinterCreator
importedOpenEditionMinterDetails={importedDetails?.openEditionMinterDetails}
isMatchingFactoryPresent={isMatchingOpenEditionFactoryPresent}
minimumMintPrice={minimumOpenEditionMintPrice as string}
minimumUpdatableMintPrice={minimumOpenEditionUpdatableMintPrice as string}
mintTokenFromFactory={mintTokenFromOpenEditionFactory}
minterType={minterType}
onChange={setOpenEditionMinterCreatorData}
onDetailsChange={setOpenEditionMinterDetails}
openEditionFactoryAddress={openEditionFactoryAddress}
openEditionMinterCreationFee={openEditionMinterCreationFee as string}
openEditionMinterUpdatableCreationFee={openEditionMinterUpdatableCreationFee as string}
/>
</Conditional>
<div className="mx-10">
+122
View File
@@ -0,0 +1,122 @@
/* eslint-disable eslint-comments/disable-enable-pair */
import { Alert } from 'components/Alert'
import { Button } from 'components/Button'
import { Conditional } from 'components/Conditional'
import { ContractPageHeader } from 'components/ContractPageHeader'
import { AddressInput } from 'components/forms/FormInput'
import { useInputState } from 'components/forms/FormInput.hooks'
import type { NextPage } from 'next'
import { useRouter } from 'next/router'
import { NextSeo } from 'next-seo'
import { useEffect, useState } from 'react'
import { toast } from 'react-hot-toast'
import { NETWORK } from 'utils/constants'
import { useDebounce } from 'utils/debounce'
import { withMetadata } from 'utils/layout'
import { links } from 'utils/links'
import { resolveAddress } from 'utils/resolveAddress'
import { useWallet } from 'utils/wallet'
const RemoveOfferPage: NextPage = () => {
const wallet = useWallet()
const [isLoading, setIsLoading] = useState(false)
const [txHash, setTxHash] = useState<string | undefined>(undefined)
const collectionAddressState = useInputState({
id: 'collection-address',
name: 'collectionAddress',
title: 'Collection Contract Address',
defaultValue: '',
placeholder: 'stars1...',
})
const collectionAddress = useDebounce(collectionAddressState.value, 300)
const router = useRouter()
useEffect(() => {
if (collectionAddress.length > 0) {
void router.replace({ query: { contractAddress: collectionAddress } })
}
if (collectionAddress.length === 0) {
void router.replace({ query: {} })
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [collectionAddress])
useEffect(() => {
const initial = new URL(document.URL).searchParams.get('contractAddress')
if (initial && initial.length > 0) collectionAddressState.onChange(initial)
}, [])
const resolveCollectionAddress = async () => {
await resolveAddress(collectionAddressState.value.trim(), wallet).then((resolvedAddress) => {
if (resolvedAddress) {
collectionAddressState.onChange(resolvedAddress)
}
})
}
useEffect(() => {
void resolveCollectionAddress()
}, [collectionAddressState.value])
const handleRemoveOffer = async () => {
if (!wallet.isWalletConnected) return toast.error('Please connect your wallet.')
if (!collectionAddressState.value) return toast.error('Please enter a collection address.')
const client = await wallet.getSigningCosmWasmClient()
setTxHash(undefined)
setIsLoading(true)
try {
const result = await client.execute(
wallet.address as string,
NETWORK === 'mainnet'
? 'stars1fvhcnyddukcqfnt7nlwv3thm5we22lyxyxylr9h77cvgkcn43xfsvgv0pl'
: 'stars18cszlvm6pze0x9sz32qnjq4vtd45xehqs8dq7cwy8yhq35wfnn3qgzs5gu',
{
remove_collection_bid: {
collection: collectionAddressState.value,
},
},
'auto',
)
toast.success('Offer successfully removed.')
setTxHash(result.transactionHash)
} catch (error: any) {
toast.error(error.message, { style: { maxWidth: 'none' } })
setTxHash(undefined)
} finally {
setIsLoading(false)
}
}
return (
<section className="py-6 px-12 space-y-4">
<NextSeo title="Remove Collection offer" />
<ContractPageHeader link={links.Documentation} title="Remove Collection Offer" />
<div className="space-y-8">
<AddressInput {...collectionAddressState} />
</div>
<div className="flex flex-row content-center">
<Button
isDisabled={collectionAddressState.value === ''}
isLoading={isLoading}
onClick={() => {
void handleRemoveOffer()
}}
>
Remove Collection Offer
</Button>
</div>
<Conditional test={txHash !== undefined}>
<Alert type="info">
<b>Transaction Hash:</b> {txHash}
</Alert>
</Conditional>
</section>
)
}
export default withMetadata(RemoveOfferPage, { center: false })
+2 -3
View File
@@ -23,13 +23,12 @@ import { useEffect, useState } from 'react'
import { toast } from 'react-hot-toast'
import { FaAsterisk } from 'react-icons/fa'
import { useMutation } from 'react-query'
import { BASE_FACTORY_ADDRESS } from 'utils/constants'
import { BASE_FACTORY_ADDRESS, BASE_FACTORY_SG721_CODE_ID } from 'utils/constants'
import { withMetadata } from 'utils/layout'
import { links } from 'utils/links'
import { useWallet } from 'utils/wallet'
import type { CreateBaseMinterResponse } from '../../../contracts/baseFactory/contract'
import { SG721_CODE_ID } from '../../../utils/constants'
import { resolveAddress } from '../../../utils/resolveAddress'
const BaseMinterInstantiatePage: NextPage = () => {
@@ -62,7 +61,7 @@ const BaseMinterInstantiatePage: NextPage = () => {
title: 'Code ID',
subtitle: 'Code ID for the sg721 contract',
placeholder: '1',
defaultValue: SG721_CODE_ID,
defaultValue: BASE_FACTORY_SG721_CODE_ID,
})
const creatorState = useInputState({
+4 -2
View File
@@ -48,7 +48,7 @@ const WhitelistExecutePage: NextPage = () => {
const [lastTx, setLastTx] = useState('')
const [memberList, setMemberList] = useState<string[]>([])
const [flexMemberList, setFlexMemberList] = useState<WhitelistFlexMember[]>([])
const [whitelistType, setWhitelistType] = useState<'standard' | 'flex'>('standard')
const [whitelistType, setWhitelistType] = useState<'standard' | 'flex' | 'merkletree'>('standard')
const comboboxState = useExecuteComboboxState()
const type = comboboxState.value?.id
@@ -211,6 +211,8 @@ const WhitelistExecutePage: NextPage = () => {
.then((contractType) => {
if (contractType?.includes('flex')) {
setWhitelistType('flex')
} else if (contractType?.includes('merkle')) {
setWhitelistType('merkletree')
} else {
setWhitelistType('standard')
}
@@ -236,7 +238,7 @@ const WhitelistExecutePage: NextPage = () => {
<form className="grid grid-cols-2 p-4 space-x-8" onSubmit={mutate}>
<div className="space-y-8">
<AddressInput {...contractState} />
<ExecuteCombobox {...comboboxState} />
<ExecuteCombobox whitelistType={whitelistType} {...comboboxState} />
<Conditional test={showLimitState}>
<NumberInput {...limitState} />
</Conditional>
+113 -21
View File
@@ -1,6 +1,7 @@
/* eslint-disable eslint-comments/disable-enable-pair */
/* eslint-disable no-nested-ternary */
import { coin } from '@cosmjs/proto-signing'
import axios from 'axios'
import { Alert } from 'components/Alert'
import { Button } from 'components/Button'
import { Conditional } from 'components/Conditional'
@@ -34,17 +35,22 @@ import { withMetadata } from 'utils/layout'
import { links } from 'utils/links'
import { useWallet } from 'utils/wallet'
import { WHITELIST_CODE_ID, WHITELIST_FLEX_CODE_ID } from '../../../utils/constants'
import {
WHITELIST_CODE_ID,
WHITELIST_FLEX_CODE_ID,
WHITELIST_MERKLE_TREE_API_URL,
WHITELIST_MERKLE_TREE_CODE_ID,
} from '../../../utils/constants'
const WhitelistInstantiatePage: NextPage = () => {
const wallet = useWallet()
const { whitelist: contract } = useContracts()
const { whitelist: contract, whitelistMerkleTree: whitelistMerkleTreeContract } = useContracts()
const { timezone } = useGlobalSettings()
const [startDate, setStartDate] = useState<Date | undefined>(undefined)
const [endDate, setEndDate] = useState<Date | undefined>(undefined)
const [adminsMutable, setAdminsMutable] = useState<boolean>(true)
const [whitelistType, setWhitelistType] = useState<'standard' | 'flex'>('standard')
const [whitelistType, setWhitelistType] = useState<'standard' | 'flex' | 'merkletree'>('standard')
const [whitelistStandardArray, setWhitelistStandardArray] = useState<string[]>([])
const [whitelistFlexArray, setWhitelistFlexArray] = useState<WhitelistFlexMember[]>([])
@@ -85,12 +91,16 @@ const WhitelistInstantiatePage: NextPage = () => {
const addressListState = useAddressListState()
const { data, isLoading, mutate } = useMutation(
async (event: FormEvent): Promise<InstantiateResponse | null> => {
async (event: FormEvent): Promise<InstantiateResponse | undefined | null> => {
event.preventDefault()
if (!contract) {
throw new Error('Smart contract connection failed')
}
if (!whitelistMerkleTreeContract && whitelistType === 'merkletree') {
throw new Error('Smart contract connection failed')
}
if (!startDate) {
throw new Error('Start date is required')
}
@@ -132,19 +142,79 @@ const WhitelistInstantiatePage: NextPage = () => {
admins_mutable: adminsMutable,
}
return toast.promise(
contract.instantiate(
whitelistType === 'standard' ? WHITELIST_CODE_ID : WHITELIST_FLEX_CODE_ID,
whitelistType === 'standard' ? standardMsg : flexMsg,
whitelistType === 'standard' ? 'Stargaze Whitelist Contract' : 'Stargaze Whitelist Flex Contract',
wallet.address,
),
{
loading: 'Instantiating contract...',
error: 'Instantiation failed!',
success: 'Instantiation success!',
},
)
if (whitelistType !== 'merkletree') {
return toast.promise(
contract.instantiate(
whitelistType === 'standard' ? WHITELIST_CODE_ID : WHITELIST_FLEX_CODE_ID,
whitelistType === 'standard' ? standardMsg : flexMsg,
whitelistType === 'standard' ? 'Stargaze Whitelist Contract' : 'Stargaze Whitelist Flex Contract',
wallet.address,
),
{
loading: 'Instantiating contract...',
error: 'Instantiation failed!',
success: 'Instantiation success!',
},
)
} else if (whitelistType === 'merkletree') {
const members = whitelistStandardArray
const membersCsv = members.join('\n')
const membersBlob = new Blob([membersCsv], { type: 'text/csv' })
const membersFile = new File([membersBlob], 'members.csv', { type: 'text/csv' })
const formData = new FormData()
formData.append('whitelist', membersFile)
const response = await toast
.promise(
axios.post(`${WHITELIST_MERKLE_TREE_API_URL}/create_whitelist`, formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
}),
{
loading: 'Fetching merkle root hash...',
success: 'Merkle root fetched successfully.',
error: 'Error fetching root hash from Whitelist Merkle Tree API.',
},
)
.catch((error) => {
console.log('error', error)
throw new Error('Whitelist instantiation failed.')
})
const rootHash = response.data.root_hash
console.log('rootHash', rootHash)
const merkleTreeMsg = {
merkle_root: rootHash,
merkle_tree_uri: null,
start_time: (startDate.getTime() * 1_000_000).toString(),
end_time: (endDate.getTime() * 1_000_000).toString(),
mint_price: coin(String(Number(unitPriceState.value) * 1000000), selectedMintToken?.denom || 'ustars'),
per_address_limit: perAddressLimitState.value,
admins: [
...new Set(
addressListState.values
.map((a) => a.address.trim())
.filter((address) => address !== '' && isValidAddress(address.trim()) && address.startsWith('stars')),
),
] || [wallet.address],
admins_mutable: adminsMutable,
}
return toast.promise(
whitelistMerkleTreeContract?.instantiate(
WHITELIST_MERKLE_TREE_CODE_ID,
merkleTreeMsg,
'Stargaze Whitelist Merkle Tree Contract',
wallet.address,
) as Promise<InstantiateResponse>,
{
loading: 'Instantiating contract...',
error: 'Instantiation failed!',
success: 'Instantiation success!',
},
)
}
},
{
onError: (error) => {
@@ -176,7 +246,7 @@ const WhitelistInstantiatePage: NextPage = () => {
/>
<LinkTabs activeIndex={0} data={whitelistLinkTabs} />
<div className="flex justify-between mb-5 ml-6 max-w-[300px] text-lg font-bold">
<div className="flex justify-between mb-5 ml-6 max-w-[520px] text-lg font-bold">
<div className="form-check form-check-inline">
<input
checked={whitelistType === 'standard'}
@@ -216,6 +286,26 @@ const WhitelistInstantiatePage: NextPage = () => {
Whitelist Flex
</label>
</div>
<div className="form-check form-check-inline">
<input
checked={whitelistType === 'merkletree'}
className="peer sr-only"
id="inlineRadio3"
name="inlineRadioOptions3"
onClick={() => {
setWhitelistType('merkletree')
}}
type="radio"
value="merkletree"
/>
<label
className="inline-block py-1 px-2 text-gray peer-checked:text-white hover:text-white peer-checked:bg-black hover:rounded-sm peer-checked:border-b-2 hover:border-b-2 peer-checked:border-plumbus hover:border-plumbus cursor-pointer form-check-label"
htmlFor="inlineRadio3"
>
Whitelist Merkle Tree
</label>
</div>
</div>
<Conditional test={Boolean(data)}>
@@ -251,7 +341,7 @@ const WhitelistInstantiatePage: NextPage = () => {
</div>
<FormGroup subtitle="Your whitelisted addresses" title="Whitelist File">
<Conditional test={whitelistType === 'standard'}>
<Conditional test={whitelistType === 'standard' || whitelistType === 'merkletree'}>
<WhitelistUpload onChange={whitelistFileOnChange} />
<Conditional test={whitelistStandardArray.length > 0}>
<JsonPreview content={whitelistStandardArray} initialState={false} title="File Contents" />
@@ -283,8 +373,10 @@ const WhitelistInstantiatePage: NextPage = () => {
</select>
</div>
<NumberInput isRequired {...memberLimitState} />
<Conditional test={whitelistType === 'standard'}>
<Conditional test={whitelistType !== 'merkletree'}>
<NumberInput isRequired {...memberLimitState} />
</Conditional>
<Conditional test={whitelistType === 'standard' || whitelistType === 'merkletree'}>
<NumberInput isRequired {...perAddressLimitState} />
</Conditional>
<Conditional test={whitelistType === 'flex'}>
+157 -14
View File
@@ -1,4 +1,6 @@
/* eslint-disable eslint-comments/disable-enable-pair */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable no-nested-ternary */
/* eslint-disable no-await-in-loop */
/* eslint-disable @typescript-eslint/no-unsafe-return */
@@ -8,6 +10,7 @@
import { toUtf8 } from '@cosmjs/encoding'
import clsx from 'clsx'
import { Button } from 'components/Button'
import type { WhitelistType } from 'components/collections/creation/WhitelistDetails'
import { Conditional } from 'components/Conditional'
import { ContractPageHeader } from 'components/ContractPageHeader'
import { FormControl } from 'components/FormControl'
@@ -19,12 +22,18 @@ import { whitelistLinkTabs } from 'components/LinkTabs.data'
import { useContracts } from 'contexts/contracts'
import type { QueryType } from 'contracts/whitelist/messages/query'
import { dispatchQuery, QUERY_LIST } from 'contracts/whitelist/messages/query'
import type { WhitelistMerkleTreeQueryType } from 'contracts/whitelistMerkleTree/messages/query'
import {
dispatchQuery as disptachWhitelistMerkleTreeQuery,
WHITELIST_MERKLE_TREE_QUERY_LIST,
} from 'contracts/whitelistMerkleTree/messages/query'
import type { NextPage } from 'next'
import { useRouter } from 'next/router'
import { NextSeo } from 'next-seo'
import { useEffect, useState } from 'react'
import { toast } from 'react-hot-toast'
import { useQuery } from 'react-query'
import { WHITELIST_MERKLE_TREE_API_URL } from 'utils/constants'
import { useDebounce } from 'utils/debounce'
import { withMetadata } from 'utils/layout'
import { links } from 'utils/links'
@@ -33,8 +42,11 @@ import { useWallet } from 'utils/wallet'
const WhitelistQueryPage: NextPage = () => {
const { whitelist: contract } = useContracts()
const { whitelistMerkleTree: contractWhitelistMerkleTree } = useContracts()
const wallet = useWallet()
const [exporting, setExporting] = useState(false)
const [whitelistType, setWhitelistType] = useState<WhitelistType>('standard')
const [proofHashes, setProofHashes] = useState<string[]>([])
const contractState = useInputState({
id: 'contract-address',
@@ -44,6 +56,46 @@ const WhitelistQueryPage: NextPage = () => {
})
const contractAddress = contractState.value
const debouncedWhitelistContractState = useDebounce(contractAddress, 300)
useEffect(() => {
async function getWhitelistContractType() {
if (debouncedWhitelistContractState.length > 0) {
const client = await wallet.getCosmWasmClient()
const data = await toast.promise(
client.queryContractRaw(
debouncedWhitelistContractState,
toUtf8(Buffer.from(Buffer.from('contract_info').toString('hex'), 'hex').toString()),
),
{
loading: 'Retrieving whitelist type...',
error: 'Whitelist type retrieval failed.',
success: 'Whitelist type retrieved.',
},
)
const whitelistContract: string = JSON.parse(new TextDecoder().decode(data as Uint8Array)).contract
console.log(contract)
return whitelistContract
}
}
void getWhitelistContractType()
.then((whitelistContract) => {
if (whitelistContract?.includes('merkletree')) {
setWhitelistType('merkletree')
} else if (whitelistContract?.includes('flex')) {
setWhitelistType('flex')
} else {
setWhitelistType('standard')
}
})
.catch((err) => {
console.log(err)
setWhitelistType('standard')
console.log('Unable to retrieve contract type. Defaulting to "standard".')
})
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [debouncedWhitelistContractState, wallet.isWalletConnected])
const addressState = useInputState({
id: 'address',
name: 'address',
@@ -52,6 +104,47 @@ const WhitelistQueryPage: NextPage = () => {
})
const address = addressState.value
const debouncedAddress = useDebounce(address, 300)
const fetchProofHashes = async (whitelistContractAddress: string, memberAddress: string): Promise<string[]> => {
if (whitelistContractAddress.length === 0 || memberAddress.length === 0)
throw new Error('Contract or member address is empty.')
const resolvedAddress = await resolveAddress(memberAddress, wallet)
const merkleRootResponse = await (
await wallet.getCosmWasmClient()
).queryContractSmart(contractAddress, { merkle_root: {} })
const proofs = await toast.promise(
fetch(`${WHITELIST_MERKLE_TREE_API_URL}/whitelist/${merkleRootResponse.merkle_root}/${resolvedAddress}`)
.then((res) => res.json())
.then((data) => data.proofs)
.catch((e) => {
console.log(e)
setProofHashes([])
}),
{
loading: 'Fetching proof hashes...',
error: 'Error fetching proof hashes from Whitelist Merkle Tree API.',
success: 'Proof hashes fetched.',
},
)
return proofs as string[] | []
}
useEffect(() => {
if (
whitelistType === 'merkletree' &&
whitelistMerkleTreeQueryType === 'has_member' &&
debouncedAddress.length > 0
) {
void fetchProofHashes(contractAddress, debouncedAddress)
.then((proofs) => setProofHashes(proofs))
.catch((e) => {
console.log(e)
setProofHashes([])
})
}
}, [debouncedAddress])
const limit = useNumberInputState({
id: 'limit',
name: 'limit',
@@ -80,22 +173,59 @@ const WhitelistQueryPage: NextPage = () => {
}, [debouncedLimit])
const [type, setType] = useState<QueryType>('config')
const [whitelistMerkleTreeQueryType, setWhitelistMerkleTreeQueryType] =
useState<WhitelistMerkleTreeQueryType>('config')
const addressVisible = type === 'has_member'
const addressVisible = type === 'has_member' || whitelistMerkleTreeQueryType === 'has_member'
const { data: response } = useQuery(
[contractAddress, type, contract, wallet.address, address, startAfter.value, limit.value] as const,
[
contractAddress,
type,
whitelistMerkleTreeQueryType,
contract,
contractWhitelistMerkleTree,
wallet.address,
address,
startAfter.value,
limit.value,
proofHashes,
whitelistType,
] as const,
async ({ queryKey }) => {
const [_contractAddress, _type, _contract, _wallet, _address, _startAfter, _limit] = queryKey
const [
_contractAddress,
_type,
_whitelistMerkleTreeQueryType,
_contract,
_contractWhitelistMerkleTree,
_wallet,
_address,
_startAfter,
_limit,
_proofHashes,
_whitelistType,
] = queryKey
const messages = contract?.use(contractAddress)
const whitelistMerkleTreeMessages = contractWhitelistMerkleTree?.use(contractAddress)
const res = await resolveAddress(_address, wallet).then(async (resolvedAddress) => {
const result = await dispatchQuery({
messages,
type,
address: resolvedAddress,
startAfter: _startAfter || undefined,
limit: _limit,
})
const result =
whitelistType === 'merkletree'
? await disptachWhitelistMerkleTreeQuery({
messages: whitelistMerkleTreeMessages,
address: resolvedAddress,
type: whitelistMerkleTreeQueryType,
limit: _limit,
proofHashes: _proofHashes,
})
: await dispatchQuery({
messages,
type,
address: resolvedAddress,
startAfter: _startAfter || undefined,
limit: _limit,
})
return result
})
return res
@@ -105,7 +235,7 @@ const WhitelistQueryPage: NextPage = () => {
onError: (error: any) => {
toast.error(error.message, { style: { maxWidth: 'none' } })
},
enabled: Boolean(contractAddress && contract),
enabled: Boolean(contractAddress && (contract || contractWhitelistMerkleTree)),
},
)
@@ -230,9 +360,13 @@ const WhitelistQueryPage: NextPage = () => {
defaultValue="config"
id="contract-query-type"
name="query-type"
onChange={(e) => setType(e.target.value as QueryType)}
onChange={(e) =>
whitelistType === 'merkletree'
? setWhitelistMerkleTreeQueryType(e.target.value as WhitelistMerkleTreeQueryType)
: setType(e.target.value as QueryType)
}
>
{QUERY_LIST.map(({ id, name }) => (
{(whitelistType === 'merkletree' ? WHITELIST_MERKLE_TREE_QUERY_LIST : QUERY_LIST).map(({ id, name }) => (
<option key={`query-${id}`} className="mt-2 text-lg bg-[#1A1A1A]" value={id}>
{name}
</option>
@@ -255,7 +389,16 @@ const WhitelistQueryPage: NextPage = () => {
</Button>
</Conditional>
</div>
<JsonPreview content={contractAddress ? { type, response } : null} title="Query Response" />
<JsonPreview
content={
contractAddress
? whitelistType === 'merkletree'
? { type: whitelistMerkleTreeQueryType, response }
: { type, response }
: null
}
title="Query Response"
/>
</div>
</section>
)
+16
View File
@@ -30,6 +30,7 @@ const Holders: NextPage = () => {
const [includeStaked, setIncludeStaked] = useState<boolean>(true)
const [includeListed, setIncludeListed] = useState<boolean>(true)
const [includeInPool, setIncludeInPool] = useState<boolean>(true)
const [exportIndividualTokens, setExportIndividualTokens] = useState<boolean>(false)
const [isLoading, setIsLoading] = useState(false)
@@ -83,6 +84,17 @@ const Holders: NextPage = () => {
type="checkbox"
/>
</label>
<label className="justify-start cursor-pointer label w-2/5">
<span className="mr-2 font-bold">Include tokens in Infinity Pools</span>
<input
checked={includeInPool}
className={`${includeInPool ? `bg-stargaze` : `bg-gray-600`} checkbox`}
onClick={() => {
setIncludeInPool(!includeInPool)
}}
type="checkbox"
/>
</label>
<label className="justify-start cursor-pointer label w-2/5">
<span className="mr-2 font-bold">Export by Token ID</span>
<input
@@ -122,6 +134,8 @@ const Holders: NextPage = () => {
?.map((row: any) => {
if (!includeListed && row.is_listed) return ''
if (!includeStaked && row.is_staked) return ''
if (!includeInPool && row.is_in_pool) return ''
if (row.owner_addr === null) return ''
return `${row.owner_addr},${row.token_id}\n`
})
.join('')}`
@@ -133,6 +147,8 @@ const Holders: NextPage = () => {
data.forEach((row: any) => {
if (!includeListed && row.is_listed) return
if (!includeStaked && row.is_staked) return
if (!includeInPool && row.is_in_pool) return
if (row.owner_addr === null) return
const existingRow = aggregatedData.find((r) => r.address === row.owner_addr)
if (existingRow) {
existingRow.amount += 1
+20 -14
View File
@@ -1,11 +1,12 @@
/* eslint-disable eslint-comments/disable-enable-pair */
/* eslint-disable no-nested-ternary */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable tailwindcss/classnames-order */
/* eslint-disable react/button-has-type */
import type { EncodeObject } from '@cosmjs/proto-signing'
import { Registry } from '@cosmjs/proto-signing'
import { GasPrice, SigningStargateClient } from '@cosmjs/stargate'
import { Button } from 'components/Button'
import { Conditional } from 'components/Conditional'
import { ContractPageHeader } from 'components/ContractPageHeader'
import { DenomUnits } from 'components/forms/DenomUnits'
@@ -17,6 +18,7 @@ import { NextSeo } from 'next-seo'
import { Field, Type } from 'protobufjs'
import { useEffect, useState } from 'react'
import toast from 'react-hot-toast'
import { NETWORK } from 'utils/constants'
import { withMetadata } from 'utils/layout'
import { useWallet } from 'utils/wallet'
@@ -52,16 +54,13 @@ const MsgMint = new Type('MsgMint')
.add(new Field('sender', 1, 'string', 'required'))
.add(new Field('amount', 2, 'Coin', 'required'))
.add(new Field('mintToAddress', 3, 'string', 'required'))
const CoinType = new Type('Coin').add(new Field('denom', 1, 'string')).add(new Field('amount', 2, 'string'))
MsgMint.add(CoinType)
.add(new Type('Coin').add(new Field('denom', 1, 'string')).add(new Field('amount', 2, 'string')))
const MsgSend = new Type('MsgSend')
.add(new Field('fromAddress', 1, 'string'))
.add(new Field('toAddress', 2, 'string'))
.add(new Field('amount', 3, 'Coin', 'repeated'))
MsgSend.add(CoinType)
.add(new Type('Coin').add(new Field('denom', 1, 'string')).add(new Field('amount', 2, 'string')))
const MsgChangeAdmin = new Type('MsgChangeAdmin')
.add(new Field('sender', 1, 'string', 'required'))
@@ -88,6 +87,7 @@ const Tokenfactory: NextPage = () => {
const wallet = useWallet()
const [messageType, setMessageType] = useState<MessageType>('MsgCreateDenom')
const [loading, setLoading] = useState(false)
const denomState = useInputState({
id: 'denom',
@@ -118,8 +118,8 @@ const Tokenfactory: NextPage = () => {
id: 'mintToAddress',
name: 'mintToAddress',
title: 'Mint To Address',
//placeholder: `${wallet.isWalletConnected && wallet.address ? wallet.address : 'stars1...'}`,
placeholder: 'The tokens can only be minted to the creator address currently.',
placeholder: 'stars1...',
defaultValue: wallet.address ?? '',
subtitle: 'The address to mint tokens to',
})
@@ -209,10 +209,14 @@ const Tokenfactory: NextPage = () => {
const handleSendMessage = async () => {
try {
if (!wallet.isWalletConnected) return toast.error('Please connect your wallet.')
setLoading(true)
const offlineSigner = wallet.getOfflineSignerDirect()
const stargateClient = await SigningStargateClient.connectWithSigner(
'https://rpc.elgafar-1.stargaze-apis.com/',
NETWORK === 'fang-testnet'
? 'https://rpc.fang-1.stargaze-apis.com/'
: NETWORK === 'testnet'
? 'https://rpc.fang-1.stargaze-apis.com/'
: 'https://rpc.stargaze-apis.com/',
offlineSigner,
{
gasPrice: GasPrice.fromString('0.025ustars'),
@@ -302,9 +306,10 @@ const Tokenfactory: NextPage = () => {
'auto',
)
console.log('response: ', response)
setLoading(false)
toast.success(`${messageType} success.`, { style: { maxWidth: 'none' } })
} catch (error: any) {
setLoading(false)
toast.error(error.message, { style: { maxWidth: 'none' } })
console.error('Error: ', error)
}
@@ -349,7 +354,7 @@ const Tokenfactory: NextPage = () => {
<Conditional test={messageType === 'MsgMint'}>
<TextInput className="w-3/5" {...denomState} />
<NumberInput className="w-1/4" {...amountState} />
<AddressInput className="w-3/5" disabled {...mintToAddressState} />
<AddressInput className="w-3/5" {...mintToAddressState} />
</Conditional>
<Conditional test={messageType === 'MsgSend'}>
<TextInput className="w-3/5" {...denomState} />
@@ -375,15 +380,16 @@ const Tokenfactory: NextPage = () => {
<TextInput className="w-1/2" {...denomState} />
<AddressInput className="w-1/2" {...newAdminAddressState} />
</Conditional>
<button
<Button
className="px-4 py-2 font-bold text-white bg-stargaze rounded-md"
isLoading={loading}
onClick={() => {
void handleSendMessage()
}}
>
{' '}
{getButtonName()}
</button>
</Button>
</section>
)
}
+32
View File
@@ -11,18 +11,27 @@ export const WHITELIST_CODE_ID = parseInt(process.env.NEXT_PUBLIC_WHITELIST_CODE
export const WHITELIST_FLEX_CODE_ID = parseInt(process.env.NEXT_PUBLIC_WHITELIST_FLEX_CODE_ID, 10)
export const VENDING_MINTER_CODE_ID = parseInt(process.env.NEXT_PUBLIC_VENDING_MINTER_CODE_ID, 10)
export const VENDING_MINTER_FLEX_CODE_ID = parseInt(process.env.NEXT_PUBLIC_VENDING_MINTER_FLEX_CODE_ID, 10)
export const WHITELIST_MERKLE_TREE_CODE_ID = parseInt(process.env.NEXT_PUBLIC_WHITELIST_MERKLE_TREE_CODE_ID, 10)
export const VENDING_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_VENDING_FACTORY_ADDRESS
export const FEATURED_VENDING_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_FEATURED_VENDING_FACTORY_ADDRESS
export const VENDING_FACTORY_UPDATABLE_ADDRESS = process.env.NEXT_PUBLIC_VENDING_FACTORY_UPDATABLE_ADDRESS
export const VENDING_FACTORY_FLEX_ADDRESS = process.env.NEXT_PUBLIC_VENDING_FACTORY_FLEX_ADDRESS
export const VENDING_FACTORY_MERKLE_TREE_ADDRESS = process.env.NEXT_PUBLIC_VENDING_FACTORY_MERKLE_TREE_ADDRESS
export const FEATURED_VENDING_FACTORY_MERKLE_TREE_ADDRESS =
process.env.NEXT_PUBLIC_FEATURED_VENDING_FACTORY_MERKLE_TREE_ADDRESS
export const FEATURED_VENDING_FACTORY_FLEX_ADDRESS = process.env.NEXT_PUBLIC_FEATURED_VENDING_FACTORY_FLEX_ADDRESS
export const VENDING_FACTORY_UPDATABLE_FLEX_ADDRESS = process.env.NEXT_PUBLIC_VENDING_FACTORY_UPDATABLE_FLEX_ADDRESS
export const VENDING_IBC_ATOM_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_VENDING_IBC_ATOM_FACTORY_ADDRESS
export const VENDING_IBC_ATOM_UPDATABLE_FACTORY_ADDRESS =
process.env.NEXT_PUBLIC_VENDING_IBC_ATOM_UPDATABLE_FACTORY_ADDRESS
export const VENDING_IBC_USDC_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_VENDING_IBC_USDC_FACTORY_ADDRESS
export const FEATURED_IBC_USDC_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_FEATURED_VENDING_IBC_USDC_FACTORY_ADDRESS
export const VENDING_IBC_USDC_UPDATABLE_FACTORY_ADDRESS =
process.env.NEXT_PUBLIC_VENDING_IBC_USDC_UPDATABLE_FACTORY_ADDRESS
export const VENDING_IBC_TIA_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_VENDING_IBC_TIA_FACTORY_ADDRESS
export const FEATURED_IBC_TIA_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_FEATURED_VENDING_IBC_TIA_FACTORY_ADDRESS
export const VENDING_IBC_TIA_UPDATABLE_FACTORY_ADDRESS =
process.env.NEXT_PUBLIC_VENDING_IBC_TIA_UPDATABLE_FACTORY_ADDRESS
export const VENDING_IBC_NBTC_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_VENDING_IBC_NBTC_FACTORY_ADDRESS
export const VENDING_IBC_NBTC_UPDATABLE_FACTORY_ADDRESS =
process.env.NEXT_PUBLIC_VENDING_IBC_NBTC_UPDATABLE_FACTORY_ADDRESS
@@ -36,8 +45,19 @@ export const VENDING_IBC_ATOM_FACTORY_FLEX_ADDRESS = process.env.NEXT_PUBLIC_VEN
export const VENDING_IBC_ATOM_UPDATABLE_FACTORY_FLEX_ADDRESS =
process.env.NEXT_PUBLIC_VENDING_IBC_ATOM_UPDATABLE_FACTORY_FLEX_ADDRESS
export const VENDING_IBC_USDC_FACTORY_FLEX_ADDRESS = process.env.NEXT_PUBLIC_VENDING_IBC_USDC_FACTORY_FLEX_ADDRESS
export const FEATURED_VENDING_IBC_USDC_FACTORY_FLEX_ADDRESS =
process.env.NEXT_PUBLIC_FEATURED_VENDING_IBC_USDC_FACTORY_FLEX_ADDRESS
export const VENDING_IBC_USDC_UPDATABLE_FACTORY_FLEX_ADDRESS =
process.env.NEXT_PUBLIC_VENDING_IBC_USDC_UPDATABLE_FACTORY_FLEX_ADDRESS
export const VENDING_IBC_TIA_FACTORY_FLEX_ADDRESS = process.env.NEXT_PUBLIC_VENDING_IBC_TIA_FACTORY_FLEX_ADDRESS
export const VENDING_IBC_TIA_FACTORY_MERKLE_TREE_ADDRESS =
process.env.NEXT_PUBLIC_VENDING_IBC_TIA_FACTORY_MERKLE_TREE_ADDRESS
export const FEATURED_VENDING_IBC_TIA_FACTORY_MERKLE_TREE_ADDRESS =
process.env.NEXT_PUBLIC_FEATURED_VENDING_IBC_TIA_FACTORY_MERKLE_TREE_ADDRESS
export const FEATURED_VENDING_IBC_TIA_FACTORY_FLEX_ADDRESS =
process.env.NEXT_PUBLIC_FEATURED_VENDING_IBC_TIA_FACTORY_FLEX_ADDRESS
export const VENDING_IBC_TIA_UPDATABLE_FACTORY_FLEX_ADDRESS =
process.env.NEXT_PUBLIC_VENDING_IBC_TIA_UPDATABLE_FACTORY_FLEX_ADDRESS
export const VENDING_IBC_NBTC_FACTORY_FLEX_ADDRESS = process.env.NEXT_PUBLIC_VENDING_IBC_NBTC_FACTORY_FLEX_ADDRESS
export const VENDING_IBC_NBTC_UPDATABLE_FACTORY_FLEX_ADDRESS =
process.env.NEXT_PUBLIC_VENDING_IBC_NBTC_UPDATABLE_FACTORY_FLEX_ADDRESS
@@ -60,13 +80,23 @@ export const VENDING_NATIVE_BRNCH_FLEX_FACTORY_ADDRESS =
export const BASE_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_BASE_FACTORY_ADDRESS
export const BASE_FACTORY_UPDATABLE_ADDRESS = process.env.NEXT_PUBLIC_BASE_FACTORY_UPDATABLE_ADDRESS
export const OPEN_EDITION_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_OPEN_EDITION_FACTORY_ADDRESS
export const OPEN_EDITION_FACTORY_FLEX_ADDRESS = process.env.NEXT_PUBLIC_OPEN_EDITION_FACTORY_FLEX_ADDRESS
export const OPEN_EDITION_UPDATABLE_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_FACTORY_ADDRESS
export const OPEN_EDITION_IBC_ATOM_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_OPEN_EDITION_IBC_ATOM_FACTORY_ADDRESS
export const OPEN_EDITION_IBC_ATOM_FACTORY_FLEX_ADDRESS =
process.env.NEXT_PUBLIC_OPEN_EDITION_IBC_ATOM_FACTORY_FLEX_ADDRESS
export const OPEN_EDITION_UPDATABLE_IBC_ATOM_FACTORY_ADDRESS =
process.env.NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_ATOM_FACTORY_ADDRESS
export const OPEN_EDITION_IBC_USDC_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_OPEN_EDITION_IBC_USDC_FACTORY_ADDRESS
export const OPEN_EDITION_IBC_USDC_FACTORY_FLEX_ADDRESS =
process.env.NEXT_PUBLIC_OPEN_EDITION_IBC_USDC_FACTORY_FLEX_ADDRESS
export const OPEN_EDITION_UPDATABLE_IBC_USDC_FACTORY_ADDRESS =
process.env.NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_USDC_FACTORY_ADDRESS
export const OPEN_EDITION_IBC_TIA_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_OPEN_EDITION_IBC_TIA_FACTORY_ADDRESS
export const OPEN_EDITION_IBC_TIA_FACTORY_FLEX_ADDRESS =
process.env.NEXT_PUBLIC_OPEN_EDITION_IBC_TIA_FACTORY_FLEX_ADDRESS
export const OPEN_EDITION_UPDATABLE_IBC_TIA_FACTORY_ADDRESS =
process.env.NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_TIA_FACTORY_ADDRESS
export const OPEN_EDITION_IBC_NBTC_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_OPEN_EDITION_IBC_NBTC_FACTORY_ADDRESS
export const OPEN_EDITION_UPDATABLE_IBC_NBTC_FACTORY_ADDRESS =
process.env.NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_NBTC_FACTORY_ADDRESS
@@ -102,6 +132,8 @@ export const STARGAZE_URL = process.env.NEXT_PUBLIC_STARGAZE_WEBSITE_URL
export const BLOCK_EXPLORER_URL = process.env.NEXT_PUBLIC_BLOCK_EXPLORER_URL
export const WEBSITE_URL = process.env.NEXT_PUBLIC_WEBSITE_URL
export const SYNC_COLLECTIONS_API_URL = process.env.NEXT_PUBLIC_SYNC_COLLECTIONS_API_URL
export const WHITELIST_MERKLE_TREE_API_URL = process.env.NEXT_PUBLIC_WHITELIST_MERKLE_TREE_API_URL
export const NFT_STORAGE_DEFAULT_API_KEY = process.env.NEXT_PUBLIC_NFT_STORAGE_DEFAULT_API_KEY
export const MEILISEARCH_HOST = process.env.NEXT_PUBLIC_MEILISEARCH_HOST
export const MEILISEARCH_API_KEY = process.env.NEXT_PUBLIC_MEILISEARCH_API_KEY
+8 -8
View File
@@ -4,14 +4,14 @@ import { toast } from 'react-hot-toast'
import { isValidAddress } from './isValidAddress'
export const isValidFlexListFile = (file: WhitelistFlexMember[]) => {
let sumOfAmounts = 0
file.forEach((allocation) => {
sumOfAmounts += Number(allocation.mint_count)
})
if (sumOfAmounts > 10000) {
toast.error(`Total mint count should be less than 10000 tokens (current count: ${sumOfAmounts}))`)
return false
}
// let sumOfAmounts = 0
// file.forEach((allocation) => {
// sumOfAmounts += Number(allocation.mint_count)
// })
// if (sumOfAmounts > 10000) {
// toast.error(`Total mint count should be less than 10000 tokens (current count: ${sumOfAmounts}))`)
// return false
// }
const checks = file.map((account) => {
// Check if address is valid bech32 address
+31
View File
@@ -0,0 +1,31 @@
import sha256 from 'crypto-js/sha256'
import { MerkleTree } from 'merkletreejs'
export class WhitelistMerkleTree {
tree: MerkleTree
constructor(members: string[]) {
this.tree = new MerkleTree(
members.map((member) => sha256(member)),
sha256,
{
// sort: true,
// hashLeaves: false,
// sortLeaves: true,
sortPairs: true,
},
)
}
getMerkleRoot() {
return this.tree.getRoot().toString('hex')
}
getMerkleProof(member: string) {
console.log('this.tree.getProof(sha256(member).toString()): ', this.tree.getProof(sha256(member).toString()))
return this.tree.getProof(sha256(member).toString()).map((item) => item.data.toString('hex'))
}
verify(proof: string[], member: string) {
return this.tree.verify(proof, sha256(member).toString(), this.tree.getRoot())
}
}
+2 -1
View File
@@ -1,6 +1,7 @@
import { useChain as useCosmosKitChain } from '@cosmos-kit/react'
import { chains } from 'chain-registry'
import { getConfig } from 'config'
import { fangTestnet } from 'config/fangTestnet'
import { NETWORK } from './constants'
@@ -9,7 +10,7 @@ import { NETWORK } from './constants'
*/
export const useWallet = () => {
const { chainId } = getConfig(NETWORK)
const chain = chains.find((c) => c.chain_id === chainId)
const chain = chains.concat([fangTestnet]).find((c) => c.chain_id === chainId)
if (!chain) {
throw new Error('Chain not found')
}
+161 -3
View File
@@ -2295,6 +2295,20 @@
minimatch "^3.0.4"
strip-json-comments "^3.1.1"
"@ethereumjs/rlp@^4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@ethereumjs/rlp/-/rlp-4.0.1.tgz#626fabfd9081baab3d0a3074b0c7ecaf674aaa41"
integrity sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==
"@ethereumjs/util@^8.1.0":
version "8.1.0"
resolved "https://registry.yarnpkg.com/@ethereumjs/util/-/util-8.1.0.tgz#299df97fb6b034e0577ce9f94c7d9d1004409ed4"
integrity sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==
dependencies:
"@ethereumjs/rlp" "^4.0.1"
ethereum-cryptography "^2.0.0"
micro-ftch "^0.3.1"
"@ethersproject/address@^5.6.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37"
@@ -2803,11 +2817,28 @@
resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.5.tgz#02f377e4d41eaaacf265e34bab9bacd8efc4a351"
integrity sha512-/SoXW1Ntpmpw3AXAzfDRaQidnd8kbZ2oSni8u5z0yw6t4RwJvmdZy1eOaAADRThWKV+2oU90++LSnXJIwBRWYQ==
"@noble/curves@1.3.0", "@noble/curves@~1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.3.0.tgz#01be46da4fd195822dab821e72f71bf4aeec635e"
integrity sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==
dependencies:
"@noble/hashes" "1.3.3"
"@noble/hashes@1.3.3", "@noble/hashes@~1.3.2":
version "1.3.3"
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699"
integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==
"@noble/hashes@^1", "@noble/hashes@^1.0.0":
version "1.0.0"
resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.0.0.tgz"
integrity sha512-DZVbtY62kc3kkBtMHqwCOfXrT/hnoORy5BJ4+HU1IR59X0KWAOqsfzQPcUl/lQLlG7qXbe/fZ3r/emxtAl+sqg==
"@noble/hashes@^1.4.0":
version "1.4.0"
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426"
integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz"
@@ -2908,6 +2939,28 @@
resolved "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.1.tgz"
integrity sha512-BUyKJGdDWqvWC5GEhyOiUrGNi9iJUr4CU0O2WxJL6QJhHeeA/NVBalH+FeK0r/x/W0rPymXt5s78TDS7d6lCwg==
"@scure/base@~1.1.4":
version "1.1.6"
resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.6.tgz#8ce5d304b436e4c84f896e0550c83e4d88cb917d"
integrity sha512-ok9AWwhcgYuGG3Zfhyqg+zwl+Wn5uE+dwC0NV/2qQkx4dABbb/bx96vWu8NSj+BNjjSjno+JRYRjle1jV08k3g==
"@scure/bip32@1.3.3":
version "1.3.3"
resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.3.tgz#a9624991dc8767087c57999a5d79488f48eae6c8"
integrity sha512-LJaN3HwRbfQK0X1xFSi0Q9amqOgzQnnDngIt+ZlsBC3Bm7/nE7K0kwshZHyaru79yIVRv/e1mQAjZyuZG6jOFQ==
dependencies:
"@noble/curves" "~1.3.0"
"@noble/hashes" "~1.3.2"
"@scure/base" "~1.1.4"
"@scure/bip39@1.2.2":
version "1.2.2"
resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.2.tgz#f3426813f4ced11a47489cbcf7294aa963966527"
integrity sha512-HYf9TUXG80beW+hGAt3TRM8wU6pQoYur9iNypTROm42dorCGmLnFe3eWjz3gOq6G62H2WRh0FCzAR1PI+29zIA==
dependencies:
"@noble/hashes" "~1.3.2"
"@scure/base" "~1.1.4"
"@stablelib/aead@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@stablelib/aead/-/aead-1.0.1.tgz#c4b1106df9c23d1b867eb9b276d8f42d5fc4c0c3"
@@ -3184,6 +3237,11 @@
resolved "https://registry.yarnpkg.com/@typeform/embed/-/embed-2.11.0.tgz#97ab13d8c010a87bd2586b95b4d44c27859ba671"
integrity sha512-MEckCfqsqj4z00w5b3D4nK1ix4Elm15L4xsgAkFX7NuCLETa9TZD/vqARlBb63yqR5tPD4LW1mw/ftBBoeIQgA==
"@types/crypto-js@4.2.1":
version "4.2.1"
resolved "https://registry.yarnpkg.com/@types/crypto-js/-/crypto-js-4.2.1.tgz#480edd76991a63050cb88db1a8840758c55a7135"
integrity sha512-FSPGd9+OcSok3RsM0UZ/9fcvMOXJ1ENE/ZbLfOPlBWj7BgXtEAM8VYfTtT760GiLbQIMoVozwVuisjvsVwqYWw==
"@types/glob@*":
version "7.2.0"
resolved "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz"
@@ -4230,7 +4288,7 @@ big-integer@^1.6.16, big-integer@^1.6.48:
resolved "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz"
integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==
bignumber.js@^9.1.1, bignumber.js@^9.1.2:
bignumber.js@^9.0.1, bignumber.js@^9.1.1, bignumber.js@^9.1.2:
version "9.1.2"
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.2.tgz#b7c4242259c008903b13707983b5f4bbd31eda0c"
integrity sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==
@@ -4300,6 +4358,11 @@ blockstore-core@^1.0.2:
it-take "^1.0.1"
multiformats "^9.4.7"
bn.js@4.11.6:
version "4.11.6"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215"
integrity sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==
bn.js@^4.11.8, bn.js@^4.11.9:
version "4.12.0"
resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz"
@@ -4391,6 +4454,11 @@ bs58check@<3.0.0, bs58check@^2.1.1, bs58check@^2.1.2:
create-hash "^1.1.0"
safe-buffer "^5.1.2"
buffer-reverse@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/buffer-reverse/-/buffer-reverse-1.0.1.tgz#49283c8efa6f901bc01fa3304d06027971ae2f60"
integrity sha512-M87YIUBsZ6N924W57vDwT/aOu8hw7ZgdByz6ijksLjmHJELBASmYTTlNHRgjE+pTsT9oJXGaDSgqqwfdHotDUg==
buffer@^5.5.0, buffer@^5.6.0:
version "5.7.1"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
@@ -4770,11 +4838,16 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3:
shebang-command "^2.0.0"
which "^2.0.1"
crypto-js@^4.0.0:
crypto-js@4.1.1, crypto-js@^4.0.0:
version "4.1.1"
resolved "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz"
integrity sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==
crypto-js@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631"
integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==
css-select@^4.1.3:
version "4.3.0"
resolved "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz"
@@ -5542,6 +5615,31 @@ eth-rpc-errors@^4.0.2:
dependencies:
fast-safe-stringify "^2.0.6"
ethereum-bloom-filters@^1.0.6:
version "1.1.0"
resolved "https://registry.yarnpkg.com/ethereum-bloom-filters/-/ethereum-bloom-filters-1.1.0.tgz#b3fc1eb789509ee30db0bf99a2988ccacb8d0397"
integrity sha512-J1gDRkLpuGNvWYzWslBQR9cDV4nd4kfvVTE/Wy4Kkm4yb3EYRSlyi0eB/inTsSTTVyA0+HyzHgbr95Fn/Z1fSw==
dependencies:
"@noble/hashes" "^1.4.0"
ethereum-cryptography@^2.0.0, ethereum-cryptography@^2.1.2:
version "2.1.3"
resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-2.1.3.tgz#1352270ed3b339fe25af5ceeadcf1b9c8e30768a"
integrity sha512-BlwbIL7/P45W8FGW2r7LGuvoEZ+7PWsniMvQ4p5s2xCyw9tmaDlpfsN9HjAucbF+t/qpVHwZUisgfK24TCW8aA==
dependencies:
"@noble/curves" "1.3.0"
"@noble/hashes" "1.3.3"
"@scure/bip32" "1.3.3"
"@scure/bip39" "1.2.2"
ethjs-unit@0.1.6:
version "0.1.6"
resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699"
integrity sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw==
dependencies:
bn.js "4.11.6"
number-to-bn "1.7.0"
event-emitter@^0.3.5:
version "0.3.5"
resolved "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz"
@@ -6358,6 +6456,11 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
dependencies:
is-extglob "^2.1.1"
is-hex-prefixed@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554"
integrity sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==
is-ip@^3.1.0:
version "3.1.0"
resolved "https://registry.npmjs.org/is-ip/-/is-ip-3.1.0.tgz"
@@ -6967,6 +7070,22 @@ merge2@^1.3.0, merge2@^1.4.1:
resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz"
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
merkletreejs@0.3.11:
version "0.3.11"
resolved "https://registry.yarnpkg.com/merkletreejs/-/merkletreejs-0.3.11.tgz#e0de05c3ca1fd368de05a12cb8efb954ef6fc04f"
integrity sha512-LJKTl4iVNTndhL+3Uz/tfkjD0klIWsHlUzgtuNnNrsf7bAlXR30m+xYB7lHr5Z/l6e/yAIsr26Dabx6Buo4VGQ==
dependencies:
bignumber.js "^9.0.1"
buffer-reverse "^1.0.1"
crypto-js "^4.2.0"
treeify "^1.1.0"
web3-utils "^1.3.4"
micro-ftch@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/micro-ftch/-/micro-ftch-0.3.1.tgz#6cb83388de4c1f279a034fb0cf96dfc050853c5f"
integrity sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==
micromatch@^4.0.4:
version "4.0.5"
resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz"
@@ -7333,6 +7452,14 @@ nth-check@^2.0.1:
dependencies:
boolbase "^1.0.0"
number-to-bn@1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/number-to-bn/-/number-to-bn-1.7.0.tgz#bb3623592f7e5f9e0030b1977bd41a0c53fe1ea0"
integrity sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig==
dependencies:
bn.js "4.11.6"
strip-hex-prefix "1.0.0"
object-assign@^4.0.1, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz"
@@ -7823,7 +7950,7 @@ rainbow-sprinkles@^0.17.0:
resolved "https://registry.yarnpkg.com/rainbow-sprinkles/-/rainbow-sprinkles-0.17.0.tgz#6ee06c6decc767b89bf01fc5fe87ecf859fd4c2f"
integrity sha512-ok3NrylQ0szvJtuBYaB/w09L9zOvvqcSQrvycT2A5XJxQNvwvkeADvTqQWGOQ3b6MkO8UmYccBPt8g8vVvxM9A==
randombytes@^2.0.1:
randombytes@^2.0.1, randombytes@^2.1.0:
version "2.1.0"
resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz"
integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
@@ -8584,6 +8711,13 @@ strip-final-newline@^2.0.0:
resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz"
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
strip-hex-prefix@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz#0c5f155fef1151373377de9dbb588da05500e36f"
integrity sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==
dependencies:
is-hex-prefixed "1.0.0"
strip-indent@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz"
@@ -8775,6 +8909,11 @@ toggle-selection@^1.0.6:
resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32"
integrity sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==
treeify@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/treeify/-/treeify-1.1.0.tgz#4e31c6a463accd0943879f30667c4fdaff411bb8"
integrity sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==
trim-newlines@^3.0.0:
version "3.0.1"
resolved "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz"
@@ -8936,6 +9075,11 @@ use-sync-external-store@1.2.0:
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==
utf8@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1"
integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==
util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
@@ -9012,6 +9156,20 @@ web-streams-polyfill@^3.1.1:
resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz"
integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==
web3-utils@^1.3.4:
version "1.10.4"
resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.10.4.tgz#0daee7d6841641655d8b3726baf33b08eda1cbec"
integrity sha512-tsu8FiKJLk2PzhDl9fXbGUWTkkVXYhtTA+SmEFkKft+9BgwLxfCRpU96sWv7ICC8zixBNd3JURVoiR3dUXgP8A==
dependencies:
"@ethereumjs/util" "^8.1.0"
bn.js "^5.2.1"
ethereum-bloom-filters "^1.0.6"
ethereum-cryptography "^2.1.2"
ethjs-unit "0.1.6"
number-to-bn "1.7.0"
randombytes "^2.1.0"
utf8 "3.0.0"
"webextension-polyfill@>=0.10.0 <1.0", webextension-polyfill@^0.10.0:
version "0.10.0"
resolved "https://registry.yarnpkg.com/webextension-polyfill/-/webextension-polyfill-0.10.0.tgz#ccb28101c910ba8cf955f7e6a263e662d744dbb8"