Compare commits
129 Commits
fix-factor
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
| 7490e24a1d | |||
| da62b2fbf3 | |||
| 8a08acf250 | |||
| 6c6ee08024 | |||
| e6f56c0509 | |||
| 50489c00cd | |||
| ac5a7f2cb6 | |||
| 04397853d9 | |||
|
|
2ab4cb9349 | ||
|
|
ea7dea6a2a | ||
|
|
ed9105684c | ||
|
|
3511a57eb9 | ||
|
|
28741049ee | ||
|
|
1003fcf4ae | ||
|
|
a96c80462d | ||
|
|
d889e7e04b | ||
|
|
be93200f53 | ||
|
|
3bbed658a7 | ||
|
|
fa1475f109 | ||
|
|
d1b5041312 | ||
|
|
0958b0db94 | ||
|
|
0354131ad1 | ||
|
|
f7880540ad | ||
|
|
123e07362d | ||
|
|
aaf7b82b43 | ||
|
|
467c7a4cfb | ||
|
|
5e963cf615 | ||
|
|
41d71a6765 | ||
|
|
22d58dbe45 | ||
|
|
471ff43fcf | ||
|
|
027c4c6821 | ||
|
|
f0b94422b1 | ||
|
|
bb8b3e1791 | ||
|
|
42707adc0b | ||
|
|
35bed9d6aa | ||
|
|
93f98bcec6 | ||
|
|
83835dfba2 | ||
|
|
d85e19b770 | ||
|
|
1abb2f82df | ||
|
|
b44e4512a5 | ||
|
|
a226d8b341 | ||
|
|
8a02a7f80d | ||
|
|
122c61055f | ||
|
|
81880aecdd | ||
|
|
b38562d9fd | ||
|
|
0eb94e4ee8 | ||
|
|
3a150a50f3 | ||
|
|
03c008d0fa | ||
|
|
6487646f2c | ||
|
|
b1094f5231 | ||
|
|
97bb60b3ff | ||
|
|
9654ec845e | ||
|
|
41e8a3961a | ||
|
|
43fd0d7848 | ||
|
|
cc16f7ceb1 | ||
|
|
cc58de90a4 | ||
|
|
27f8510335 | ||
|
|
0fc60f6c05 | ||
|
|
c59531f87e | ||
|
|
6ddd780338 | ||
|
|
57e36b6dbd | ||
|
|
1a866db888 | ||
|
|
7e97f5d393 | ||
|
|
e26068085d | ||
|
|
1ccc55a3b2 | ||
|
|
99f7b25f10 | ||
|
|
3308cfdcf2 | ||
|
|
5e9fdc1cf1 | ||
|
|
39847d1ab9 | ||
|
|
29cd89f6a5 | ||
|
|
6466945e28 | ||
|
|
db9ffb0899 | ||
|
|
a295dd5d4a | ||
|
|
85ac7a4f71 | ||
|
|
ee46fa64d3 | ||
|
|
f5f14fb330 | ||
|
|
fd65316d1f | ||
|
|
004b102540 | ||
|
|
9095c5c06c | ||
|
|
55e46cc830 | ||
|
|
d07ad7db04 | ||
|
|
8f0a0e84aa | ||
|
|
4e6db44105 | ||
|
|
7b0f1f6176 | ||
|
|
fe8bfd14bc | ||
|
|
1c7c0a682d | ||
|
|
b58549b1a2 | ||
|
|
6cdd2a24ac | ||
|
|
437ac5b6f5 | ||
|
|
afef36375b | ||
|
|
0231692eb1 | ||
|
|
e0d6aaee4d | ||
|
|
c4027859c0 | ||
|
|
c12fd51525 | ||
|
|
f1aeeb2167 | ||
|
|
103921d946 | ||
|
|
b4fcc63de6 | ||
|
|
b3dea5e757 | ||
|
|
e43ec78acc | ||
|
|
4c312d0ad8 | ||
|
|
89836ad84e | ||
|
|
51f38f12d7 | ||
|
|
609745ce11 | ||
|
|
3b9778270b | ||
|
|
c66b21792c | ||
|
|
35a9c0eba8 | ||
|
|
07152745e0 | ||
|
|
7a455b60bc | ||
|
|
67694d4c5d | ||
|
|
4ec704f588 | ||
|
|
3f44e342e8 | ||
|
|
245fd2253b | ||
|
|
ed1844fdf0 | ||
|
|
6316a68408 | ||
|
|
a949a1e103 | ||
|
|
39385ee82e | ||
|
|
440ee78122 | ||
|
|
1f31cdbe29 | ||
|
|
b66e6d0920 | ||
|
|
ab4d4fa31d | ||
|
|
92ce752b15 | ||
|
|
01936d8f5e | ||
|
|
02df116e09 | ||
|
|
ab724afb9c | ||
|
|
3c3b60ebe8 | ||
|
|
de5de84873 | ||
|
|
7951669ed2 | ||
|
|
20944cf5de | ||
|
|
0f1c4a027b |
31
.env.example
31
.env.example
@ -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= "..."
|
||||
|
||||
45
.github/workflows/publish.yaml
vendored
Normal file
45
.github/workflows/publish.yaml
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
name: Publish ApplicationRecord to Registry
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- '*'
|
||||
|
||||
env:
|
||||
CERC_REGISTRY_USER_KEY: ${{ secrets.CICD_LACONIC_USER_KEY }}
|
||||
CERC_REGISTRY_BOND_ID: ${{ secrets.CICD_LACONIC_BOND_ID }}
|
||||
|
||||
jobs:
|
||||
cns_publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: "Clone project repository"
|
||||
uses: actions/checkout@v3
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18 # though you need version 14 with geojson
|
||||
# - name: "Install exiftool"
|
||||
# run: |
|
||||
# apt-get update -y
|
||||
# apt-get upgrade -y
|
||||
# apt-get install exiftool -y
|
||||
#- name: "Exiftool Version"
|
||||
# run: |
|
||||
# exiftool -ver
|
||||
- name: "Install Yarn"
|
||||
run: npm install -g yarn
|
||||
- name: "Install registry CLI"
|
||||
run: |
|
||||
npm config set @cerc-io:registry https://git.vdb.to/api/packages/cerc-io/npm/
|
||||
yarn global add @cerc-io/laconic-registry-cli
|
||||
- name: "Install jq"
|
||||
uses: dcarbone/install-jq-action@v2.1.0
|
||||
- name: "Publish App Record"
|
||||
run: scripts/publish-app-record.sh
|
||||
#- name: "Create Metadata Record"
|
||||
# run: scripts/create-metadata-record.sh
|
||||
- name: "Request Deployment"
|
||||
run: scripts/request-app-deployment.sh
|
||||
@ -298,17 +298,16 @@ export const Sidebar = () => {
|
||||
>
|
||||
<Link href="/contracts/royaltyRegistry/">Royalty Registry</Link>
|
||||
</li>
|
||||
<Conditional test={NETWORK === 'testnet'}>
|
||||
<li
|
||||
className={clsx(
|
||||
'text-lg font-bold hover:text-white hover:bg-stargaze-80 rounded',
|
||||
router.asPath.includes('/contracts/upload/') ? 'text-white' : 'text-gray',
|
||||
)}
|
||||
tabIndex={-1}
|
||||
>
|
||||
<Link href="/contracts/upload/">Upload Contract</Link>
|
||||
</li>
|
||||
</Conditional>
|
||||
|
||||
<li
|
||||
className={clsx(
|
||||
'text-lg font-bold hover:text-white hover:bg-stargaze-80 rounded',
|
||||
router.asPath.includes('/contracts/upload/') ? 'text-white' : 'text-gray',
|
||||
)}
|
||||
tabIndex={-1}
|
||||
>
|
||||
<Link href="/contracts/upload/">Upload Contract</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@ -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" />
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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" />
|
||||
|
||||
@ -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" />
|
||||
|
||||
@ -5,15 +5,16 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
import { toUtf8 } from '@cosmjs/encoding'
|
||||
import type { Coin } from '@cosmjs/proto-signing'
|
||||
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 { type TokenInfo, tokensList } from 'config/token'
|
||||
import { useContracts } from 'contexts/contracts'
|
||||
import { addLogItem } from 'contexts/log'
|
||||
import type { DispatchExecuteArgs as OpenEditionFactoryDispatchExecuteArgs } from 'contracts/openEditionFactory/messages/execute'
|
||||
@ -22,12 +23,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 +41,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 +51,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 +68,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
|
||||
openEditionMinterCreationFee?: Coin
|
||||
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 +95,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 +130,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 +156,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 +316,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 +331,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 +359,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,16 +481,45 @@ export const OpenEditionMinterCreator = ({
|
||||
|
||||
const checkwalletBalance = async () => {
|
||||
if (!wallet.isWalletConnected) throw new Error('Wallet not connected.')
|
||||
const amountNeeded = collectionDetails?.updatable
|
||||
? Number(openEditionMinterUpdatableCreationFee)
|
||||
: Number(openEditionMinterCreationFee)
|
||||
await (await wallet.getCosmWasmClient()).getBalance(wallet.address || '', 'ustars').then((balance) => {
|
||||
if (amountNeeded >= Number(balance.amount))
|
||||
throw new Error(
|
||||
`Insufficient wallet balance to instantiate the required contracts. Needed amount: ${(
|
||||
amountNeeded / 1000000
|
||||
).toString()} STARS`,
|
||||
)
|
||||
const queryClient = await wallet.getCosmWasmClient()
|
||||
const creationFeeDenom = tokensList.find((token) => token.denom === openEditionMinterCreationFee?.denom)
|
||||
await queryClient.getBalance(wallet.address || '', 'ustars').then(async (starsBalance) => {
|
||||
await queryClient
|
||||
.getBalance(wallet.address || '', openEditionMinterCreationFee?.denom as string)
|
||||
.then((creationFeeDenomBalance) => {
|
||||
if (whitelistDetails?.whitelistState === 'new' && whitelistDetails.memberLimit) {
|
||||
const whitelistCreationFee = Math.ceil(Number(whitelistDetails.memberLimit) / 1000) * 100000000
|
||||
if (openEditionMinterCreationFee?.denom === 'ustars') {
|
||||
const amountNeeded = whitelistCreationFee + Number(openEditionMinterCreationFee.amount)
|
||||
if (amountNeeded >= Number(starsBalance.amount))
|
||||
throw new Error(
|
||||
`Insufficient wallet balance to instantiate the required contracts. Needed amount: ${(
|
||||
amountNeeded / 1000000
|
||||
).toString()} STARS`,
|
||||
)
|
||||
} else {
|
||||
if (whitelistCreationFee >= Number(starsBalance.amount))
|
||||
throw new Error(
|
||||
`Insufficient wallet balance to instantiate the whitelist. Needed amount: ${(
|
||||
whitelistCreationFee / 1000000
|
||||
).toString()} STARS`,
|
||||
)
|
||||
if (Number(openEditionMinterCreationFee?.amount) > Number(creationFeeDenomBalance.amount))
|
||||
throw new Error(
|
||||
`Insufficient wallet balance to instantiate the required contracts. Needed amount: ${(
|
||||
Number(openEditionMinterCreationFee?.amount) / 1000000
|
||||
).toString()} ${
|
||||
creationFeeDenom ? creationFeeDenom.displayName : openEditionMinterCreationFee?.denom
|
||||
}`,
|
||||
)
|
||||
}
|
||||
} else if (Number(openEditionMinterCreationFee?.amount) > Number(creationFeeDenomBalance.amount))
|
||||
throw new Error(
|
||||
`Insufficient wallet balance to instantiate the required contracts. Needed amount: ${(
|
||||
Number(openEditionMinterCreationFee?.amount) / 1000000
|
||||
).toString()} ${creationFeeDenom ? creationFeeDenom.displayName : openEditionMinterCreationFee?.denom}`,
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@ -383,6 +531,7 @@ export const OpenEditionMinterCreator = ({
|
||||
setTokenImageUri(null)
|
||||
setOpenEditionMinterContractAddress(null)
|
||||
setSg721ContractAddress(null)
|
||||
setWhitelistContractAddress(null)
|
||||
setTransactionHash(null)
|
||||
if (metadataStorageMethod === 'off-chain') {
|
||||
if (offChainMetadataUploadDetails?.uploadMethod === 'new') {
|
||||
@ -407,13 +556,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 +618,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 +737,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 +875,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 +921,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: [openEditionMinterCreationFee as Coin],
|
||||
updatable: collectionDetails?.updatable,
|
||||
}
|
||||
await openEditionFactoryDispatchExecute(payload)
|
||||
@ -680,16 +956,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 +983,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 +1000,7 @@ export const OpenEditionMinterCreator = ({
|
||||
coverImageUrl,
|
||||
tokenUri,
|
||||
tokenImageUri,
|
||||
isRefreshed,
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
@ -722,6 +1009,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 +1131,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
components/openEdition/WhitelistDetails.tsx
Normal file
528
components/openEdition/WhitelistDetails.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
325
config/minter.ts
325
config/minter.ts
@ -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,
|
||||
]
|
||||
|
||||
@ -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,
|
||||
]
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -342,7 +342,8 @@ export const badgeHub = (client: SigningCosmWasmClient, txSigner: string): Badge
|
||||
},
|
||||
'auto',
|
||||
'',
|
||||
editFee ? [coin(editFee, 'ustars')] : [],
|
||||
[coin(200000000, 'ustars')],
|
||||
// editFee ? [coin(editFee, 'ustars')] : [],
|
||||
)
|
||||
|
||||
return res.transactionHash
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
@ -25,6 +25,7 @@ export interface CollectionInfo {
|
||||
external_link?: string
|
||||
explicit_content?: boolean
|
||||
royalty_info?: RoyaltyInfo | undefined
|
||||
creator?: string
|
||||
}
|
||||
|
||||
export interface SG721Instance {
|
||||
|
||||
@ -63,8 +63,10 @@ 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.filter((e) => e.type === 'instantiate')[0].attributes[0].value,
|
||||
sg721Address: result.logs[0].events
|
||||
.filter((e) => e.type === 'wasm')
|
||||
.filter((e) => e.attributes[2]?.key === 'sg721_address')[0].attributes[2].value,
|
||||
transactionHash: result.transactionHash,
|
||||
logs: result.logs,
|
||||
}
|
||||
|
||||
375
contracts/whitelistMerkleTree/contract.ts
Normal file
375
contracts/whitelistMerkleTree/contract.ts
Normal 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
contracts/whitelistMerkleTree/index.ts
Normal file
2
contracts/whitelistMerkleTree/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './contract'
|
||||
export * from './useContract'
|
||||
144
contracts/whitelistMerkleTree/messages/execute.ts
Normal file
144
contracts/whitelistMerkleTree/messages/execute.ts
Normal file
@ -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)
|
||||
}
|
||||
66
contracts/whitelistMerkleTree/messages/query.ts
Normal file
66
contracts/whitelistMerkleTree/messages/query.ts
Normal file
@ -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')
|
||||
}
|
||||
}
|
||||
}
|
||||
89
contracts/whitelistMerkleTree/useContract.ts
Normal file
89
contracts/whitelistMerkleTree/useContract.ts
Normal file
@ -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,
|
||||
}
|
||||
}
|
||||
21
env.d.ts
vendored
21
env.d.ts
vendored
@ -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
|
||||
|
||||
18257
package-lock.json
generated
Normal file
18257
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
38
package.json
38
package.json
@ -1,5 +1,7 @@
|
||||
{
|
||||
"name": "stargaze-studio",
|
||||
"private": true,
|
||||
"name": "@mito/stargaze-studio",
|
||||
"repository": "https://git.vdb.to/LaconicNetwork/stargaze-studio",
|
||||
"version": "0.8.7",
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
@ -13,18 +15,21 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3",
|
||||
"@cosmjs/cosmwasm-stargate": "0.32.2",
|
||||
"@cosmjs/encoding": "0.32.2",
|
||||
"@cosmjs/math": "0.32.2",
|
||||
"@cosmjs/proto-signing": "0.32.2",
|
||||
"@cosmjs/stargate": "0.32.2",
|
||||
"cosmjs-types": "0.9.0",
|
||||
"@cosmos-kit/keplr": "^2.4.4",
|
||||
"@cosmos-kit/leap": "^2.4.3",
|
||||
"@cosmos-kit/leap-metamask-cosmos-snap": "^0.3.3",
|
||||
"@cosmos-kit/react": "^2.9.3",
|
||||
"@cosmjs/cosmwasm-stargate": "0.32.3",
|
||||
"@cosmjs/encoding": "0.32.3",
|
||||
"@cosmjs/math": "0.32.3",
|
||||
"@cosmjs/proto-signing": "0.32.3",
|
||||
"@cosmjs/stargate": "0.32.3",
|
||||
"@cosmos-kit/keplr": "2.8.0",
|
||||
"@cosmos-kit/leap": "2.8.0",
|
||||
"@cosmos-kit/leap-metamask-cosmos-snap": "0.8.0",
|
||||
"@cosmos-kit/react": "2.12.0",
|
||||
"@fontsource/jetbrains-mono": "^4",
|
||||
"@fontsource/roboto": "^4",
|
||||
"@headlessui/react": "1.6.0",
|
||||
"@headlessui/tailwindcss": "0.2.0",
|
||||
"@heroicons/react": "2.0.18",
|
||||
"@interchain-ui/react": "1.23.11",
|
||||
"@leapwallet/cosmos-snap-provider": "0.1.24",
|
||||
"@pinata/sdk": "^1.1.26",
|
||||
"@popperjs/core": "^2",
|
||||
@ -32,20 +37,23 @@
|
||||
"@tailwindcss/forms": "^0",
|
||||
"@tailwindcss/line-clamp": "^0",
|
||||
"@typeform/embed-react": "2.21.0",
|
||||
"@types/crypto-js": "4.2.1",
|
||||
"@types/pako": "^2.0.3",
|
||||
"axios": "^0",
|
||||
"chain-registry": "^1.20.0",
|
||||
"clsx": "^1",
|
||||
"compare-versions": "^4",
|
||||
"cosmjs-types": "0.9.0",
|
||||
"crypto-js": "4.1.1",
|
||||
"daisyui": "^2.19.0",
|
||||
"html-to-image": "1.11.11",
|
||||
"@headlessui/react": "1.6.0",
|
||||
"@headlessui/tailwindcss": "0.2.0",
|
||||
"@heroicons/react": "2.0.18",
|
||||
"jscrypto": "^1.0.3",
|
||||
"match-sorter": "^6",
|
||||
"merkletreejs": "0.3.11",
|
||||
"next": "^12",
|
||||
"next-seo": "^4",
|
||||
"nft.storage": "^6.3.0",
|
||||
"pako": "^2.0.2",
|
||||
"qrcode.react": "3.1.0",
|
||||
"react": "^18",
|
||||
"react-datetime-picker": "^3",
|
||||
@ -72,8 +80,8 @@
|
||||
"lint-staged": "^12",
|
||||
"object-sizeof": "^1.6.0",
|
||||
"postcss": "^8",
|
||||
"tailwindcss": "^3",
|
||||
"tailwind-merge": "1.14.0",
|
||||
"tailwindcss": "^3",
|
||||
"typescript": "^4"
|
||||
},
|
||||
"eslintConfig": {
|
||||
|
||||
132
pages/collections/cancelAuction.tsx
Normal file
132
pages/collections/cancelAuction.tsx
Normal file
@ -0,0 +1,132 @@
|
||||
/* 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, TextInput } 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 CancelAuctionPage: 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 tokenIdState = useInputState({
|
||||
id: 'token-id',
|
||||
name: 'tokenId',
|
||||
title: 'Token ID',
|
||||
defaultValue: '',
|
||||
placeholder: '1',
|
||||
})
|
||||
|
||||
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 handleCancelAuction = 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'
|
||||
? 'stars1vvdkcn393ddyd47v9g3qv6mvne59d0ykzy9wre3ga0c58dtdg4ksm776jg'
|
||||
: 'stars1dnadsd7tx0dmnpp26ms7d66zsp7tduygwjgfjzueh0lg9t5lq5vq9kn47c',
|
||||
{
|
||||
cancel_auction: {
|
||||
collection: collectionAddressState.value,
|
||||
token_id: tokenIdState.value,
|
||||
},
|
||||
},
|
||||
'auto',
|
||||
)
|
||||
toast.success('Auction successfully cancelled.')
|
||||
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="Cancel Auction" />
|
||||
<ContractPageHeader link={links.Documentation} title="Cancel Auction" />
|
||||
<div className="space-y-2">
|
||||
<AddressInput {...collectionAddressState} />
|
||||
<TextInput className="w-1/4" {...tokenIdState} />
|
||||
</div>
|
||||
|
||||
<div className="flex flex-row content-center mt-4">
|
||||
<Button
|
||||
isDisabled={collectionAddressState.value === ''}
|
||||
isLoading={isLoading}
|
||||
onClick={() => {
|
||||
void handleCancelAuction()
|
||||
}}
|
||||
>
|
||||
Cancel Auction
|
||||
</Button>
|
||||
</div>
|
||||
<Conditional test={txHash !== undefined}>
|
||||
<Alert type="info">
|
||||
<b>Transaction Hash:</b> {txHash}
|
||||
</Alert>
|
||||
</Conditional>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default withMetadata(CancelAuctionPage, { center: false })
|
||||
@ -7,6 +7,7 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
|
||||
import { toUtf8 } from '@cosmjs/encoding'
|
||||
import type { Coin } from '@cosmjs/proto-signing'
|
||||
import { coin } from '@cosmjs/proto-signing'
|
||||
import { Sidetab } from '@typeform/embed-react'
|
||||
import axios from 'axios'
|
||||
@ -34,7 +35,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 +63,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 +73,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 +96,7 @@ const CollectionCreationPage: NextPage = () => {
|
||||
baseMinter: baseMinterContract,
|
||||
vendingMinter: vendingMinterContract,
|
||||
whitelist: whitelistContract,
|
||||
whitelistMerkleTree: whitelistMerkleTreeContract,
|
||||
vendingFactory: vendingFactoryContract,
|
||||
baseFactory: baseFactoryContract,
|
||||
} = useContracts()
|
||||
@ -118,24 +127,23 @@ const CollectionCreationPage: NextPage = () => {
|
||||
const [royaltyDetails, setRoyaltyDetails] = useState<RoyaltyDetailsDataProps | null>(null)
|
||||
const [minterType, setMinterType] = useState<MinterType>('vending')
|
||||
|
||||
const [vendingMinterCreationFee, setVendingMinterCreationFee] = useState<string | null>(null)
|
||||
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 [vendingMinterCreationFee, setVendingMinterCreationFee] = useState<Coin | null>(null)
|
||||
const [baseMinterCreationFee, setBaseMinterCreationFee] = useState<Coin | null>(null)
|
||||
const [vendingMinterUpdatableCreationFee, setVendingMinterUpdatableCreationFee] = useState<Coin | null>(null)
|
||||
const [openEditionMinterCreationFee, setOpenEditionMinterCreationFee] = useState<Coin | undefined>(undefined)
|
||||
const [vendingMinterFlexCreationFee, setVendingMinterFlexCreationFee] = useState<Coin | null>(null)
|
||||
const [baseMinterUpdatableCreationFee, setBaseMinterUpdatableCreationFee] = useState<Coin | 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 +170,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 +522,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 +642,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' ||
|
||||
@ -615,14 +676,11 @@ const CollectionCreationPage: NextPage = () => {
|
||||
txSigner: wallet.address || '',
|
||||
msg,
|
||||
funds: [
|
||||
coin(
|
||||
whitelistDetails?.whitelistState !== 'none' && whitelistDetails?.whitelistType === 'flex'
|
||||
? (vendingMinterFlexCreationFee as string)
|
||||
: collectionDetails?.updatable
|
||||
? (vendingMinterUpdatableCreationFee as string)
|
||||
: (vendingMinterCreationFee as string),
|
||||
'ustars',
|
||||
),
|
||||
whitelistDetails?.whitelistState !== 'none' && whitelistDetails?.whitelistType === 'flex'
|
||||
? (vendingMinterFlexCreationFee as Coin)
|
||||
: collectionDetails?.updatable
|
||||
? (vendingMinterUpdatableCreationFee as Coin)
|
||||
: (vendingMinterCreationFee as Coin),
|
||||
],
|
||||
updatable: collectionDetails?.updatable,
|
||||
flex: whitelistDetails?.whitelistState !== 'none' && whitelistDetails?.whitelistType === 'flex',
|
||||
@ -676,10 +734,7 @@ const CollectionCreationPage: NextPage = () => {
|
||||
txSigner: wallet.address || '',
|
||||
msg,
|
||||
funds: [
|
||||
coin(
|
||||
collectionDetails?.updatable ? (baseMinterUpdatableCreationFee as string) : (baseMinterCreationFee as string),
|
||||
'ustars',
|
||||
),
|
||||
collectionDetails?.updatable ? (baseMinterUpdatableCreationFee as Coin) : (baseMinterCreationFee as Coin),
|
||||
],
|
||||
updatable: collectionDetails?.updatable,
|
||||
}
|
||||
@ -1046,6 +1101,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 +1140,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')
|
||||
@ -1151,7 +1211,7 @@ const CollectionCreationPage: NextPage = () => {
|
||||
toast.error(`${error.message}`, { style: { maxWidth: 'none' } })
|
||||
addLogItem({ id: uid(), message: error.message, type: 'Error', timestamp: new Date() })
|
||||
})
|
||||
setBaseMinterCreationFee(baseFactoryParameters?.params?.creation_fee?.amount)
|
||||
setBaseMinterCreationFee(baseFactoryParameters?.params?.creation_fee)
|
||||
}
|
||||
if (BASE_FACTORY_UPDATABLE_ADDRESS) {
|
||||
const baseFactoryUpdatableParameters = await client
|
||||
@ -1162,7 +1222,7 @@ const CollectionCreationPage: NextPage = () => {
|
||||
toast.error(`${error.message}`, { style: { maxWidth: 'none' } })
|
||||
addLogItem({ id: uid(), message: error.message, type: 'Error', timestamp: new Date() })
|
||||
})
|
||||
setBaseMinterUpdatableCreationFee(baseFactoryUpdatableParameters?.params?.creation_fee?.amount)
|
||||
setBaseMinterUpdatableCreationFee(baseFactoryUpdatableParameters?.params?.creation_fee)
|
||||
}
|
||||
if (VENDING_FACTORY_ADDRESS) {
|
||||
const vendingFactoryParameters = await client
|
||||
@ -1171,7 +1231,7 @@ const CollectionCreationPage: NextPage = () => {
|
||||
toast.error(`${error.message}`, { style: { maxWidth: 'none' } })
|
||||
addLogItem({ id: uid(), message: error.message, type: 'Error', timestamp: new Date() })
|
||||
})
|
||||
setVendingMinterCreationFee(vendingFactoryParameters?.params?.creation_fee?.amount)
|
||||
setVendingMinterCreationFee(vendingFactoryParameters?.params?.creation_fee)
|
||||
setMinimumMintPrice(vendingFactoryParameters?.params?.min_mint_price?.amount)
|
||||
}
|
||||
if (VENDING_FACTORY_UPDATABLE_ADDRESS) {
|
||||
@ -1183,7 +1243,7 @@ const CollectionCreationPage: NextPage = () => {
|
||||
toast.error(`${error.message}`, { style: { maxWidth: 'none' } })
|
||||
addLogItem({ id: uid(), message: error.message, type: 'Error', timestamp: new Date() })
|
||||
})
|
||||
setVendingMinterUpdatableCreationFee(vendingFactoryUpdatableParameters?.params?.creation_fee?.amount)
|
||||
setVendingMinterUpdatableCreationFee(vendingFactoryUpdatableParameters?.params?.creation_fee)
|
||||
setMinimumUpdatableMintPrice(vendingFactoryUpdatableParameters?.params?.min_mint_price?.amount)
|
||||
}
|
||||
if (VENDING_FACTORY_FLEX_ADDRESS) {
|
||||
@ -1195,7 +1255,7 @@ const CollectionCreationPage: NextPage = () => {
|
||||
toast.error(`${error.message}`, { style: { maxWidth: 'none' } })
|
||||
addLogItem({ id: uid(), message: error.message, type: 'Error', timestamp: new Date() })
|
||||
})
|
||||
setVendingMinterFlexCreationFee(vendingFactoryFlexParameters?.params?.creation_fee?.amount)
|
||||
setVendingMinterFlexCreationFee(vendingFactoryFlexParameters?.params?.creation_fee)
|
||||
setMinimumFlexMintPrice(vendingFactoryFlexParameters?.params?.min_mint_price?.amount)
|
||||
}
|
||||
if (OPEN_EDITION_FACTORY_ADDRESS) {
|
||||
@ -1205,84 +1265,71 @@ const CollectionCreationPage: NextPage = () => {
|
||||
toast.error(`${error.message}`, { style: { maxWidth: 'none' } })
|
||||
addLogItem({ id: uid(), message: error.message, type: 'Error', timestamp: new Date() })
|
||||
})
|
||||
setOpenEditionMinterCreationFee(openEditionFactoryParameters?.params?.creation_fee?.amount)
|
||||
setOpenEditionMinterCreationFee(openEditionFactoryParameters?.params?.creation_fee)
|
||||
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) => {
|
||||
toast.error(`${error.message}`, { style: { maxWidth: 'none' } })
|
||||
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,
|
||||
),
|
||||
)
|
||||
}
|
||||
setOpenEditionMinterCreationFee(openEditionFactoryParameters?.params?.creation_fee)
|
||||
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)
|
||||
@ -1295,13 +1342,13 @@ const CollectionCreationPage: NextPage = () => {
|
||||
})
|
||||
|
||||
if (whitelistDetails?.whitelistState !== 'none' && whitelistDetails?.whitelistType === 'flex') {
|
||||
setVendingMinterFlexCreationFee(vendingFactoryParameters?.params?.creation_fee?.amount)
|
||||
setVendingMinterFlexCreationFee(vendingFactoryParameters?.params?.creation_fee)
|
||||
setMinimumFlexMintPrice(vendingFactoryParameters?.params?.min_mint_price?.amount)
|
||||
} else if (collectionDetails?.updatable) {
|
||||
setVendingMinterUpdatableCreationFee(vendingFactoryParameters?.params?.creation_fee?.amount)
|
||||
setVendingMinterUpdatableCreationFee(vendingFactoryParameters?.params?.creation_fee)
|
||||
setMinimumUpdatableMintPrice(vendingFactoryParameters?.params?.min_mint_price?.amount)
|
||||
} else {
|
||||
setVendingMinterCreationFee(vendingFactoryParameters?.params?.creation_fee?.amount)
|
||||
setVendingMinterCreationFee(vendingFactoryParameters?.params?.creation_fee)
|
||||
setMinimumMintPrice(vendingFactoryParameters?.params?.min_mint_price?.amount)
|
||||
}
|
||||
setMintTokenFromVendingFactory(
|
||||
@ -1320,39 +1367,82 @@ 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`,
|
||||
)
|
||||
} else {
|
||||
const amountNeeded =
|
||||
minterType === 'vending'
|
||||
? whitelistDetails?.whitelistState === 'existing' && whitelistDetails.whitelistType === 'flex'
|
||||
? Number(vendingMinterFlexCreationFee)
|
||||
: collectionDetails?.updatable
|
||||
? Number(vendingMinterUpdatableCreationFee)
|
||||
: Number(vendingMinterCreationFee)
|
||||
: collectionDetails?.updatable
|
||||
? Number(baseMinterUpdatableCreationFee)
|
||||
: Number(baseMinterCreationFee)
|
||||
if (amountNeeded >= Number(balance.amount))
|
||||
throw new Error(
|
||||
`Insufficient wallet balance to instantiate the required contracts. Needed amount: ${(
|
||||
amountNeeded / 1000000
|
||||
).toString()} STARS`,
|
||||
)
|
||||
}
|
||||
const queryClient = await wallet.getCosmWasmClient()
|
||||
|
||||
const creationFee: Coin | null =
|
||||
minterType === 'vending'
|
||||
? whitelistDetails?.whitelistType === 'flex'
|
||||
? vendingMinterFlexCreationFee
|
||||
: collectionDetails?.updatable
|
||||
? vendingMinterUpdatableCreationFee
|
||||
: vendingMinterCreationFee
|
||||
: collectionDetails?.updatable
|
||||
? baseMinterUpdatableCreationFee
|
||||
: baseMinterCreationFee
|
||||
|
||||
const creationFeeDenom = tokensList.find((token) => token.denom === creationFee?.denom)
|
||||
|
||||
await queryClient.getBalance(wallet.address || '', 'ustars').then(async (starsBalance) => {
|
||||
await queryClient
|
||||
.getBalance(wallet.address || '', creationFee?.denom as string)
|
||||
.then((creationFeeDenomBalance) => {
|
||||
if (minterType === 'vending' && whitelistDetails?.whitelistState === 'new') {
|
||||
if (whitelistDetails.whitelistType !== 'merkletree' && whitelistDetails.memberLimit) {
|
||||
const whitelistCreationFee = Math.ceil(Number(whitelistDetails.memberLimit) / 1000) * 100000000
|
||||
if (creationFee?.denom === 'ustars') {
|
||||
const amountNeeded = whitelistCreationFee + Number(creationFee.amount)
|
||||
if (amountNeeded >= Number(starsBalance.amount))
|
||||
throw new Error(
|
||||
`Insufficient wallet balance to instantiate the required contracts. Needed amount: ${(
|
||||
amountNeeded / 1000000
|
||||
).toString()} STARS`,
|
||||
)
|
||||
} else {
|
||||
if (whitelistCreationFee >= Number(starsBalance.amount))
|
||||
throw new Error(
|
||||
`Insufficient wallet balance to instantiate the whitelist. Needed amount: ${(
|
||||
whitelistCreationFee / 1000000
|
||||
).toString()} STARS`,
|
||||
)
|
||||
if (Number(creationFee?.amount) > Number(creationFeeDenomBalance.amount))
|
||||
throw new Error(
|
||||
`Insufficient wallet balance to instantiate the required contracts. Needed amount: ${(
|
||||
Number(creationFee?.amount) / 1000000
|
||||
).toString()} ${creationFeeDenom ? creationFeeDenom.displayName : creationFee?.denom}`,
|
||||
)
|
||||
}
|
||||
} else if (whitelistDetails.whitelistType === 'merkletree') {
|
||||
const merkleWhitelistCreationFee = 1000000000
|
||||
if (creationFee?.denom === 'ustars') {
|
||||
const amountNeeded = merkleWhitelistCreationFee + Number(creationFee.amount)
|
||||
if (amountNeeded >= Number(starsBalance.amount))
|
||||
throw new Error(
|
||||
`Insufficient wallet balance to instantiate the required contracts. Needed amount: ${(
|
||||
amountNeeded / 1000000
|
||||
).toString()} STARS`,
|
||||
)
|
||||
} else {
|
||||
if (merkleWhitelistCreationFee >= Number(starsBalance.amount))
|
||||
throw new Error(
|
||||
`Insufficient wallet balance to instantiate the whitelist. Needed amount: ${(
|
||||
merkleWhitelistCreationFee / 1000000
|
||||
).toString()} STARS`,
|
||||
)
|
||||
if (Number(creationFee?.amount) > Number(creationFeeDenomBalance.amount))
|
||||
throw new Error(
|
||||
`Insufficient wallet balance to instantiate the required contracts. Needed amount: ${(
|
||||
Number(creationFee?.amount) / 1000000
|
||||
).toString()} ${creationFeeDenom ? creationFeeDenom.displayName : creationFee?.denom}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
} else if (Number(creationFee?.amount) > Number(creationFeeDenomBalance.amount))
|
||||
throw new Error(
|
||||
`Insufficient wallet balance to instantiate the required contracts. Needed amount: ${(
|
||||
Number(creationFee?.amount) / 1000000
|
||||
).toString()} ${creationFeeDenom ? creationFeeDenom.displayName : creationFee?.denom}`,
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@ -1540,6 +1630,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 +1951,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}
|
||||
openEditionMinterCreationFee={openEditionMinterCreationFee as string}
|
||||
openEditionMinterUpdatableCreationFee={openEditionMinterUpdatableCreationFee as string}
|
||||
openEditionFactoryAddress={openEditionFactoryAddress}
|
||||
openEditionMinterCreationFee={openEditionMinterCreationFee}
|
||||
/>
|
||||
</Conditional>
|
||||
<div className="mx-10">
|
||||
|
||||
@ -60,6 +60,12 @@ const CollectionList: NextPage = () => {
|
||||
if (minterConfig?.whitelist) collection.whitelist = minterConfig.whitelist
|
||||
setMyStandardCollections((prevState) => [...prevState, collection])
|
||||
} else if (contractType?.includes('open-edition')) {
|
||||
const minterConfig = await (await wallet.getCosmWasmClient())
|
||||
.queryContractSmart(collection.minter, { config: {} })
|
||||
.catch(() => {
|
||||
console.log('Unable to retrieve minter config')
|
||||
})
|
||||
if (minterConfig?.whitelist) collection.whitelist = minterConfig.whitelist
|
||||
setMyOpenEditionCollections((prevState) => [...prevState, collection])
|
||||
}
|
||||
})
|
||||
@ -429,6 +435,31 @@ const CollectionList: NextPage = () => {
|
||||
</Tooltip>
|
||||
</span>
|
||||
</div>
|
||||
<Conditional test={collection.whitelist}>
|
||||
<div className="flex flex-row items-center space-x-3">
|
||||
Whitelist:
|
||||
<span className="ml-2">
|
||||
<Tooltip
|
||||
backgroundColor="bg-blue-500"
|
||||
label="Click to copy the whitelist contract address"
|
||||
>
|
||||
<button
|
||||
className="group flex space-x-2 font-mono text-base text-white/80 hover:underline"
|
||||
onClick={() => void copy(collection.whitelist as string)}
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
{truncateMiddle(
|
||||
collection.whitelist ? (collection.whitelist as string) : '',
|
||||
36,
|
||||
)}
|
||||
</span>
|
||||
<FaCopy className="opacity-0 group-hover:opacity-100" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
</span>
|
||||
</div>
|
||||
</Conditional>
|
||||
</td>
|
||||
<th className="bg-black">
|
||||
<div className="flex items-center space-x-8">
|
||||
@ -445,6 +476,14 @@ const CollectionList: NextPage = () => {
|
||||
>
|
||||
<FaRocket />
|
||||
</Anchor>
|
||||
<Conditional test={collection.whitelist}>
|
||||
<Anchor
|
||||
className="text-xl text-white"
|
||||
href={`/contracts/whitelist/execute/?contractAddress=${collection.whitelist}`}
|
||||
>
|
||||
<FaList />
|
||||
</Anchor>
|
||||
</Conditional>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
122
pages/collections/removeOffer.tsx
Normal file
122
pages/collections/removeOffer.tsx
Normal 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 })
|
||||
@ -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({
|
||||
|
||||
@ -1,14 +1,28 @@
|
||||
/* eslint-disable eslint-comments/disable-enable-pair */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
|
||||
import type { EncodeObject } from '@cosmjs/proto-signing'
|
||||
import { GasPrice, SigningStargateClient } from '@cosmjs/stargate'
|
||||
import clsx from 'clsx'
|
||||
import { Alert } from 'components/Alert'
|
||||
import { Button } from 'components/Button'
|
||||
import { Conditional } from 'components/Conditional'
|
||||
import { ContractPageHeader } from 'components/ContractPageHeader'
|
||||
import { AddressList } from 'components/forms/AddressList'
|
||||
import { useAddressListState } from 'components/forms/AddressList.hooks'
|
||||
import { TextInput } from 'components/forms/FormInput'
|
||||
import { useInputState } from 'components/forms/FormInput.hooks'
|
||||
import { JsonPreview } from 'components/JsonPreview'
|
||||
import { getConfig } from 'config'
|
||||
import { MsgExec } from 'cosmjs-types/cosmos/authz/v1beta1/tx'
|
||||
import { MsgStoreCode } from 'cosmjs-types/cosmwasm/wasm/v1/tx'
|
||||
import { AccessConfig, AccessType } from 'cosmjs-types/cosmwasm/wasm/v1/types'
|
||||
import type { NextPage } from 'next'
|
||||
import { NextSeo } from 'next-seo'
|
||||
import pako from 'pako'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { toast } from 'react-hot-toast'
|
||||
import { FaAsterisk } from 'react-icons/fa'
|
||||
@ -23,9 +37,38 @@ const UploadContract: NextPage = () => {
|
||||
const [transactionResult, setTransactionResult] = useState<any>()
|
||||
const [wasmFile, setWasmFile] = useState<File | null>(null)
|
||||
const [wasmByteArray, setWasmByteArray] = useState<Uint8Array | null>(null)
|
||||
const [accessType, setAccessType] = useState<
|
||||
'ACCESS_TYPE_UNSPECIFIED' | 'ACCESS_TYPE_EVERYBODY' | 'ACCESS_TYPE_ANY_OF_ADDRESSES' | 'ACCESS_TYPE_NOBODY'
|
||||
>('ACCESS_TYPE_UNSPECIFIED')
|
||||
const [accessConfig, setAccessConfig] = useState<AccessConfig | undefined>(undefined)
|
||||
const [isAuthzUpload, setIsAuthzUpload] = useState(false)
|
||||
|
||||
const granterAddressState = useInputState({
|
||||
id: 'address',
|
||||
name: 'Granter Address',
|
||||
title: 'Granter Address',
|
||||
subtitle: 'The address that granted the authorization for contract upload',
|
||||
defaultValue: '',
|
||||
placeholder: 'stars1...',
|
||||
})
|
||||
|
||||
const memoState = useInputState({
|
||||
id: 'memo',
|
||||
name: 'Memo',
|
||||
title: 'Transaction Memo',
|
||||
defaultValue: '',
|
||||
placeholder: 'My contract',
|
||||
})
|
||||
|
||||
const permittedAddressListState = useAddressListState()
|
||||
|
||||
const inputFile = useRef<HTMLInputElement>(null)
|
||||
|
||||
interface MsgExecAllowanceEncodeObject extends EncodeObject {
|
||||
readonly typeUrl: '/cosmos.authz.v1beta1.MsgExec'
|
||||
readonly value: Partial<MsgExec>
|
||||
}
|
||||
|
||||
const onFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (!e.target.files) return
|
||||
setWasmFile(e.target.files[0])
|
||||
@ -51,20 +94,70 @@ const UploadContract: NextPage = () => {
|
||||
try {
|
||||
if (!wallet.isWalletConnected) return toast.error('Please connect your wallet.')
|
||||
if (!wasmFile || !wasmByteArray) return toast.error('No file selected.')
|
||||
if (accessType === 'ACCESS_TYPE_UNSPECIFIED')
|
||||
return toast.error('Please select an instantiation permission type.', { style: { maxWidth: 'none' } })
|
||||
|
||||
setLoading(true)
|
||||
|
||||
const client = await wallet.getSigningCosmWasmClient()
|
||||
|
||||
const result = await client.upload(wallet.address as string, wasmByteArray, 'auto')
|
||||
if (!isAuthzUpload) {
|
||||
const result = await client.upload(
|
||||
wallet.address as string,
|
||||
wasmByteArray,
|
||||
'auto',
|
||||
memoState.value ?? undefined,
|
||||
accessConfig,
|
||||
)
|
||||
setTransactionResult({
|
||||
transactionHash: result.transactionHash,
|
||||
codeId: result.codeId,
|
||||
originalSize: result.originalSize,
|
||||
compressedSize: result.compressedSize,
|
||||
originalChecksum: result.checksum,
|
||||
})
|
||||
} else {
|
||||
if (!granterAddressState.value) {
|
||||
setLoading(false)
|
||||
return toast.error('Please enter the authorization granter address.', { style: { maxWidth: 'none' } })
|
||||
}
|
||||
const compressed = pako.gzip(wasmByteArray, { level: 9 })
|
||||
|
||||
setTransactionResult({
|
||||
transactionHash: result.transactionHash,
|
||||
codeId: result.codeId,
|
||||
originalSize: result.originalSize,
|
||||
compressedSize: result.compressedSize,
|
||||
originalChecksum: result.checksum,
|
||||
})
|
||||
const authzExecuteContractMsg: MsgExecAllowanceEncodeObject = {
|
||||
typeUrl: '/cosmos.authz.v1beta1.MsgExec',
|
||||
value: MsgExec.fromPartial({
|
||||
grantee: wallet.address as string,
|
||||
msgs: [
|
||||
{
|
||||
typeUrl: '/cosmwasm.wasm.v1.MsgStoreCode',
|
||||
value: MsgStoreCode.encode({
|
||||
sender: granterAddressState.value,
|
||||
wasmByteCode: compressed,
|
||||
instantiatePermission: accessConfig,
|
||||
}).finish(),
|
||||
},
|
||||
],
|
||||
}),
|
||||
}
|
||||
|
||||
const offlineSigner = wallet.getOfflineSignerDirect()
|
||||
const stargateClient = await SigningStargateClient.connectWithSigner(getConfig(NETWORK).rpcUrl, offlineSigner, {
|
||||
gasPrice: GasPrice.fromString('0.025ustars'),
|
||||
})
|
||||
|
||||
const result = await stargateClient.signAndBroadcast(
|
||||
wallet.address || '',
|
||||
[authzExecuteContractMsg],
|
||||
'auto',
|
||||
memoState.value ?? undefined,
|
||||
)
|
||||
|
||||
setTransactionResult({
|
||||
transactionHash: result.transactionHash,
|
||||
codeId: result.events.filter((event) => event.type === 'store_code')[0].attributes[1].value,
|
||||
originalChecksum: result.events.filter((event) => event.type === 'store_code')[0].attributes[0].value,
|
||||
})
|
||||
}
|
||||
|
||||
setLoading(false)
|
||||
} catch (err: any) {
|
||||
@ -73,55 +166,120 @@ const UploadContract: NextPage = () => {
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
if (accessType === 'ACCESS_TYPE_ANY_OF_ADDRESSES') {
|
||||
setAccessConfig(
|
||||
AccessConfig.fromPartial({
|
||||
permission: AccessType.ACCESS_TYPE_ANY_OF_ADDRESSES,
|
||||
addresses: permittedAddressListState.entries.map((entry) => entry[1].address).filter(Boolean),
|
||||
}),
|
||||
)
|
||||
} else if (accessType === 'ACCESS_TYPE_NOBODY') {
|
||||
setAccessConfig(AccessConfig.fromPartial({ permission: AccessType.ACCESS_TYPE_NOBODY }))
|
||||
} else if (accessType === 'ACCESS_TYPE_EVERYBODY') {
|
||||
setAccessConfig(AccessConfig.fromPartial({ permission: AccessType.ACCESS_TYPE_EVERYBODY }))
|
||||
} else if (accessType === 'ACCESS_TYPE_UNSPECIFIED') {
|
||||
setAccessConfig(undefined)
|
||||
}
|
||||
} catch (error: any) {
|
||||
toast.error(error.message, { style: { maxWidth: 'none' } })
|
||||
}
|
||||
}, [accessType, permittedAddressListState.entries])
|
||||
|
||||
return (
|
||||
<section className="py-6 px-12 space-y-4">
|
||||
<Conditional test={NETWORK === 'testnet'}>
|
||||
<NextSeo title="Upload Contract" />
|
||||
<ContractPageHeader
|
||||
description="Here you can upload a contract on Stargaze Testnet."
|
||||
link=""
|
||||
title="Upload Contract"
|
||||
/>
|
||||
<div className="inset-x-0 bottom-0 border-b-2 border-white/25" />
|
||||
<NextSeo title="Upload Contract" />
|
||||
<ContractPageHeader
|
||||
description="Here you can upload a contract on Stargaze Testnet."
|
||||
link=""
|
||||
title="Upload Contract"
|
||||
/>
|
||||
<div className="inset-x-0 bottom-0 border-b-2 border-white/25" />
|
||||
|
||||
<Conditional test={Boolean(transactionResult)}>
|
||||
<Alert type="info">
|
||||
<b>Upload success!</b> Here is the transaction result containing the code ID, transaction hash and other
|
||||
data.
|
||||
</Alert>
|
||||
<JsonPreview content={transactionResult} title="Transaction Result" />
|
||||
<br />
|
||||
</Conditional>
|
||||
|
||||
<div
|
||||
className={clsx(
|
||||
'flex relative justify-center items-center space-y-4 h-32',
|
||||
'rounded border-2 border-white/20 border-dashed',
|
||||
)}
|
||||
<div className="flex flex-col w-1/2">
|
||||
<span className="text-xl font-bold text-white">Authorization Type for Contract Instantiation</span>
|
||||
<select
|
||||
className="px-4 pt-2 pb-2 mt-2 w-1/2 placeholder:text-white/50 bg-white/10 rounded border-2 border-white/20 focus:ring focus:ring-plumbus-20"
|
||||
onChange={(e) => setAccessType(e.target.value as any)}
|
||||
value={accessType}
|
||||
>
|
||||
<option disabled value="ACCESS_TYPE_UNSPECIFIED">
|
||||
Select Authorization Type
|
||||
</option>
|
||||
<option value="ACCESS_TYPE_EVERYBODY">Everybody</option>
|
||||
<option value="ACCESS_TYPE_ANY_OF_ADDRESSES">Any of Addresses</option>
|
||||
<option value="ACCESS_TYPE_NOBODY">Nobody</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="my-2 w-1/2">
|
||||
<TextInput {...memoState} />
|
||||
</div>
|
||||
|
||||
<div className="flex flex-row justify-start">
|
||||
<h1 className="mt-2 font-bold text-md">Authz Upload?</h1>
|
||||
<label className="justify-start ml-6 cursor-pointer label">
|
||||
<input
|
||||
accept=".wasm"
|
||||
className={clsx(
|
||||
'file:py-2 file:px-4 file:mr-4 file:bg-plumbus-light file:rounded file:border-0 cursor-pointer',
|
||||
'before:absolute before:inset-0 before:hover:bg-white/5 before:transition',
|
||||
)}
|
||||
onChange={onFileChange}
|
||||
ref={inputFile}
|
||||
type="file"
|
||||
checked={isAuthzUpload}
|
||||
className={`${isAuthzUpload ? `bg-stargaze` : `bg-gray-600`} checkbox`}
|
||||
onClick={() => {
|
||||
setIsAuthzUpload(!isAuthzUpload)
|
||||
}}
|
||||
type="checkbox"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<Conditional test={isAuthzUpload}>
|
||||
<div className="my-2 w-3/4">
|
||||
<TextInput {...granterAddressState} />
|
||||
</div>
|
||||
</Conditional>
|
||||
|
||||
<Conditional test={accessType === 'ACCESS_TYPE_ANY_OF_ADDRESSES'}>
|
||||
<div className="my-2 w-3/4">
|
||||
<AddressList
|
||||
entries={permittedAddressListState.entries}
|
||||
onAdd={permittedAddressListState.add}
|
||||
onChange={permittedAddressListState.update}
|
||||
onRemove={permittedAddressListState.remove}
|
||||
subtitle="The list of addresses permitted to instantiate the contract"
|
||||
title="Permitted Addresses"
|
||||
/>
|
||||
</div>
|
||||
</Conditional>
|
||||
|
||||
<div className="flex justify-end pb-6">
|
||||
<Button isDisabled={!wasmFile} isLoading={loading} isWide leftIcon={<FaAsterisk />} onClick={upload}>
|
||||
Upload Contract
|
||||
</Button>
|
||||
</div>
|
||||
</Conditional>
|
||||
<Conditional test={NETWORK === 'mainnet'}>
|
||||
<NextSeo title="Upload Contract" />
|
||||
<ContractPageHeader description="" link="" title="Upload Contract" />
|
||||
<Alert type="info">Permissionless upload of contracts is only supported for testnet currently.</Alert>
|
||||
<Conditional test={Boolean(transactionResult)}>
|
||||
<Alert type="info">
|
||||
<b>Upload success!</b> Here is the transaction result containing the code ID, transaction hash and other data.
|
||||
</Alert>
|
||||
<JsonPreview content={transactionResult} title="Transaction Result" />
|
||||
<br />
|
||||
</Conditional>
|
||||
|
||||
<div
|
||||
className={clsx(
|
||||
'flex relative justify-center items-center space-y-4 h-32',
|
||||
'rounded border-2 border-white/20 border-dashed',
|
||||
)}
|
||||
>
|
||||
<input
|
||||
accept=".wasm"
|
||||
className={clsx(
|
||||
'file:py-2 file:px-4 file:mr-4 file:bg-plumbus-light file:rounded file:border-0 cursor-pointer',
|
||||
'before:absolute before:inset-0 before:hover:bg-white/5 before:transition',
|
||||
)}
|
||||
onChange={onFileChange}
|
||||
ref={inputFile}
|
||||
type="file"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end pb-6">
|
||||
<Button isDisabled={!wasmFile} isLoading={loading} isWide leftIcon={<FaAsterisk />} onClick={upload}>
|
||||
Upload Contract
|
||||
</Button>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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'}>
|
||||
|
||||
@ -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>
|
||||
)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
/* eslint-disable eslint-comments/disable-enable-pair */
|
||||
/* 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 +17,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 +53,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 +86,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 +117,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 +208,10 @@ 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 === 'testnet' ? 'https://rpc.elgafar-1.stargaze-apis.com/' : 'https://rpc.stargaze-apis.com/',
|
||||
offlineSigner,
|
||||
{
|
||||
gasPrice: GasPrice.fromString('0.025ustars'),
|
||||
@ -302,9 +301,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 +349,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 +375,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>
|
||||
)
|
||||
}
|
||||
|
||||
69
scripts/publish-app-record.sh
Executable file
69
scripts/publish-app-record.sh
Executable file
@ -0,0 +1,69 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
RECORD_FILE=tmp.rf.$$
|
||||
CONFIG_FILE=`mktemp`
|
||||
|
||||
CERC_APP_TYPE=${CERC_APP_TYPE:-"webapp"}
|
||||
CERC_REPO_REF=${CERC_REPO_REF:-${GITHUB_SHA:-`git log -1 --format="%H"`}}
|
||||
CERC_IS_LATEST_RELEASE=${CERC_IS_LATEST_RELEASE:-"true"}
|
||||
|
||||
rcd_name=$(jq -r '.name' package.json | sed 's/null//')
|
||||
rcd_desc=$(jq -r '.description' package.json | sed 's/null//')
|
||||
rcd_repository=$(jq -r '.repository' package.json | sed 's/null//')
|
||||
rcd_homepage=$(jq -r '.homepage' package.json | sed 's/null//')
|
||||
rcd_license=$(jq -r '.license' package.json | sed 's/null//')
|
||||
rcd_author=$(jq -r '.author' package.json | sed 's/null//')
|
||||
rcd_app_version=$(jq -r '.version' package.json | sed 's/null//')
|
||||
|
||||
cat <<EOF > "$CONFIG_FILE"
|
||||
services:
|
||||
cns:
|
||||
restEndpoint: '${CERC_REGISTRY_REST_ENDPOINT:-http://23.111.69.218:1317}'
|
||||
gqlEndpoint: '${CERC_REGISTRY_GQL_ENDPOINT:-https://lx-daemon.audubon.app/api}'
|
||||
chainId: ${CERC_REGISTRY_CHAIN_ID:-laconic_9000-1}
|
||||
gas: 9550000
|
||||
fees: 500000aphoton
|
||||
EOF
|
||||
|
||||
next_ver=$(laconic -c $CONFIG_FILE cns record list --type ApplicationRecord --all --name "$rcd_name" 2>/dev/null | jq -r -s ".[] | sort_by(.createTime) | reverse | [ .[] | select(.bondId == \"$CERC_REGISTRY_BOND_ID\") ] | .[0].attributes.version" | awk -F. -v OFS=. '{$NF += 1 ; print}')
|
||||
|
||||
if [ -z "$next_ver" ] || [ "1" == "$next_ver" ]; then
|
||||
next_ver=0.0.1
|
||||
fi
|
||||
|
||||
cat <<EOF | sed '/.*: ""$/d' > "$RECORD_FILE"
|
||||
record:
|
||||
type: ApplicationRecord
|
||||
version: ${next_ver}
|
||||
name: "$rcd_name"
|
||||
description: "$rcd_desc"
|
||||
homepage: "$rcd_homepage"
|
||||
license: "$rcd_license"
|
||||
author: "$rcd_author"
|
||||
repository:
|
||||
- "$rcd_repository"
|
||||
repository_ref: "$CERC_REPO_REF"
|
||||
app_version: "$rcd_app_version"
|
||||
app_type: "$CERC_APP_TYPE"
|
||||
EOF
|
||||
|
||||
|
||||
cat $RECORD_FILE
|
||||
RECORD_ID=$(laconic -c $CONFIG_FILE cns record publish --filename $RECORD_FILE --user-key "${CERC_REGISTRY_USER_KEY}" --bond-id ${CERC_REGISTRY_BOND_ID} | jq -r '.id')
|
||||
echo $RECORD_ID
|
||||
|
||||
if [ -z "$CERC_REGISTRY_APP_CRN" ]; then
|
||||
authority=$(echo "$rcd_name" | cut -d'/' -f1 | sed 's/@//')
|
||||
app=$(echo "$rcd_name" | cut -d'/' -f2-)
|
||||
CERC_REGISTRY_APP_CRN="crn://$authority/applications/$app"
|
||||
fi
|
||||
|
||||
laconic -c $CONFIG_FILE cns name set --user-key "${CERC_REGISTRY_USER_KEY}" --bond-id ${CERC_REGISTRY_BOND_ID} "$CERC_REGISTRY_APP_CRN@${rcd_app_version}" "$RECORD_ID"
|
||||
laconic -c $CONFIG_FILE cns name set --user-key "${CERC_REGISTRY_USER_KEY}" --bond-id ${CERC_REGISTRY_BOND_ID} "$CERC_REGISTRY_APP_CRN@${CERC_REPO_REF}" "$RECORD_ID"
|
||||
if [ "true" == "$CERC_IS_LATEST_RELEASE" ]; then
|
||||
laconic -c $CONFIG_FILE cns name set --user-key "${CERC_REGISTRY_USER_KEY}" --bond-id ${CERC_REGISTRY_BOND_ID} "$CERC_REGISTRY_APP_CRN" "$RECORD_ID"
|
||||
fi
|
||||
|
||||
rm -f $RECORD_FILE $CONFIG_FILE
|
||||
56
scripts/request-app-deployment.sh
Executable file
56
scripts/request-app-deployment.sh
Executable file
@ -0,0 +1,56 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
RECORD_FILE=tmp.rf.$$
|
||||
CONFIG_FILE=`mktemp`
|
||||
|
||||
rcd_name=$(jq -r '.name' package.json | sed 's/null//' | sed 's/^@//')
|
||||
rcd_app_version=$(jq -r '.version' package.json | sed 's/null//')
|
||||
|
||||
cat <<EOF > "$CONFIG_FILE"
|
||||
services:
|
||||
cns:
|
||||
restEndpoint: '${CERC_REGISTRY_REST_ENDPOINT:-http://23.111.69.218:1317}'
|
||||
gqlEndpoint: '${CERC_REGISTRY_GQL_ENDPOINT:-https://lx-daemon.audubon.app/api}'
|
||||
chainId: ${CERC_REGISTRY_CHAIN_ID:-laconic_9000-1}
|
||||
gas: 550000
|
||||
fees: 200000aphoton
|
||||
EOF
|
||||
|
||||
if [ -z "$CERC_REGISTRY_APP_CRN" ]; then
|
||||
authority=$(echo "$rcd_name" | cut -d'/' -f1 | sed 's/@//')
|
||||
app=$(echo "$rcd_name" | cut -d'/' -f2-)
|
||||
CERC_REGISTRY_APP_CRN="crn://$authority/applications/$app"
|
||||
fi
|
||||
|
||||
APP_RECORD=$(laconic -c $CONFIG_FILE cns name resolve "$CERC_REGISTRY_APP_CRN" | jq '.[0]')
|
||||
if [ -z "$APP_RECORD" ] || [ "null" == "$APP_RECORD" ]; then
|
||||
echo "No record found for $CERC_REGISTRY_APP_CRN."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cat <<EOF | sed '/.*: ""$/d' > "$RECORD_FILE"
|
||||
record:
|
||||
type: ApplicationDeploymentRequest
|
||||
version: 1.0.0
|
||||
name: "$rcd_name@$rcd_app_version"
|
||||
application: "$CERC_REGISTRY_APP_CRN@$rcd_app_version"
|
||||
dns: "$CERC_REGISTRY_DEPLOYMENT_SHORT_HOSTNAME"
|
||||
deployment: "$CERC_REGISTRY_DEPLOYMENT_CRN"
|
||||
config:
|
||||
env:
|
||||
# this overrides the setting in `.env`
|
||||
CERC_WEBAPP_DEBUG: "$rcd_app_version"
|
||||
CERC_MAX_GENERATE_TIME: "600"
|
||||
meta:
|
||||
note: "Added by CI @ `date`"
|
||||
repository: "`git remote get-url origin`"
|
||||
repository_ref: "${GITHUB_SHA:-`git log -1 --format="%H"`}"
|
||||
EOF
|
||||
|
||||
cat $RECORD_FILE
|
||||
RECORD_ID=$(laconic -c $CONFIG_FILE cns record publish --filename $RECORD_FILE --user-key "${CERC_REGISTRY_USER_KEY}" --bond-id ${CERC_REGISTRY_BOND_ID} | jq -r '.id')
|
||||
echo $RECORD_ID
|
||||
|
||||
rm -f $RECORD_FILE $CONFIG_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
|
||||
|
||||
@ -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
utils/merkleTree.ts
Normal file
31
utils/merkleTree.ts
Normal 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())
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user