From 2fdcee26db4eab60e05bf86646e0b83cbee0fd46 Mon Sep 17 00:00:00 2001 From: Preschian Febryantara Date: Mon, 18 Mar 2024 00:50:55 +0700 Subject: [PATCH 01/93] poc: drops with uid --- components/collection/drop/PaidGenerative.vue | 5 +++++ .../mintToken/constructDirectoryMeta.ts | 19 +++-------------- composables/useExperiments.ts | 8 +++++++ tests/crypto.spec.ts | 21 +++++++++++++++++++ utils/randomize.ts | 18 ++++++++++++++++ 5 files changed, 55 insertions(+), 16 deletions(-) create mode 100644 tests/crypto.spec.ts create mode 100644 utils/randomize.ts diff --git a/components/collection/drop/PaidGenerative.vue b/components/collection/drop/PaidGenerative.vue index d1cb099ace..9805a086a4 100644 --- a/components/collection/drop/PaidGenerative.vue +++ b/components/collection/drop/PaidGenerative.vue @@ -42,6 +42,7 @@ import useCursorDropEvents from '@/composables/party/useCursorDropEvents' import useDropMassMint from '@/composables/drop/massmint/useDropMassMint' import useDropMassMintListing from '@/composables/drop/massmint/useDropMassMintListing' +const { uid } = useExperiments() const { drop } = useDrop() const { fetchDropStatus } = useDropStatus() const instance = getCurrentInstance() @@ -96,6 +97,10 @@ const mintNft = async () => { initTransactionLoader() + if (uid.value) { + console.log('uid', uid.value) + } + const cb = api.tx.utility.batchAll const args = allocatedNFTs.value.map((allocatedNFT) => api.tx.nfts.mint( diff --git a/composables/transaction/mintToken/constructDirectoryMeta.ts b/composables/transaction/mintToken/constructDirectoryMeta.ts index 86f6affc88..db25296e76 100644 --- a/composables/transaction/mintToken/constructDirectoryMeta.ts +++ b/composables/transaction/mintToken/constructDirectoryMeta.ts @@ -35,10 +35,8 @@ export const uploadMediaAndMetadataDirectories = async ( const metaDirectoryCid = await pinDirectory(metadataFiles) // Preheat and upload files - await Promise.all( - mediaFiles.map((media, index) => - preheatAndDirectUpload(media, imageHashes[index]), - ), + mediaFiles.map((_, index) => + preheatFileFromIPFS(extractCid(imageHashes[index])), ) return metadataFiles.map((_, index) => @@ -125,18 +123,7 @@ const createTokenMetadata = ( return makeJsonFile(meta, String(index)) } -export const makeJsonFile = (data: any, name: string) => +export const makeJsonFile = (data: unknown, name: string) => new File([JSON.stringify(data, null, 2)], `${name}.json`, { type: 'application/json', }) - -const preheatAndDirectUpload = async ( - media: File, - primaryHash: string, -): Promise => { - const { $consola } = useNuxtApp() - preheatFileFromIPFS(primaryHash) - const filePair: [File, File | null] = [media, null] - const hashPair: [string, string | undefined] = [primaryHash, undefined] - await uploadDirectWhenMultiple(filePair, hashPair).catch($consola.warn) -} diff --git a/composables/useExperiments.ts b/composables/useExperiments.ts index d9bee2470f..bfa37b8c28 100644 --- a/composables/useExperiments.ts +++ b/composables/useExperiments.ts @@ -4,7 +4,15 @@ export default function () { const has = (key: string) => searchParams.get(key) === 'true' || localStorage.getItem(key) === 'true' + // poc by using unique id. ref: https://github.com/kodadot/private-workers/issues/87#issuecomment-1997444473 + const route = useRoute() + const pocDrops = ['/ahk/drops/pills'] + const uid = computed(() => { + return has('uid') || pocDrops.includes(route.path) + }) + return { redesign: computed(() => has('redesign')), + uid, } } diff --git a/tests/crypto.spec.ts b/tests/crypto.spec.ts new file mode 100644 index 0000000000..09cf9c0584 --- /dev/null +++ b/tests/crypto.spec.ts @@ -0,0 +1,21 @@ +import { uidCrypto, uidMath, uidMathDate } from '../utils/randomize' + +const attemps = 100 +// const attemps = 10_000 + +// uidMathDate() has less randomness in its format because it has a date prefix (yyMM). However, it has a slightly higher chance than the others of generating a non-unique ID because of the date prefix. + +it.each([uidCrypto, uidMath, uidMathDate])('generate unique id', (random) => { + const ids = new Set() + const length = new Set() + + // eslint-disable-next-line no-restricted-syntax + for (let index = 0; index < attemps; index++) { + const id = random() + ids.add(id) + length.add(id.length) + } + + expect(ids.size).toBe(attemps) + expect(length.has(9)).toEqual(true) +}) diff --git a/utils/randomize.ts b/utils/randomize.ts new file mode 100644 index 0000000000..643a22c049 --- /dev/null +++ b/utils/randomize.ts @@ -0,0 +1,18 @@ +import format from 'date-fns/format' + +export function uidMath(length = 9) { + const random = Math.floor(Math.random() * 900000000) + 100000000 + return random.toString().slice(0, length) +} + +export function uidCrypto(length = 9) { + const array = new Uint32Array(1) + const randomNumber = window.crypto.getRandomValues(array)[0] + const paddedNumber = randomNumber.toString().padStart(length, '0') + return paddedNumber.slice(0, length) +} + +export function uidMathDate(length = 5) { + const date = format(new Date(), 'yyMM') + return date + uidMath(length) +} From e5fccd7e55190a7054a178eb455c1f5dc325ee97 Mon Sep 17 00:00:00 2001 From: Preschian Febryantara Date: Wed, 20 Mar 2024 17:00:22 +0700 Subject: [PATCH 02/93] test: metadata --- components/collection/drop/PaidGenerative.vue | 18 ++++++++++++++++-- composables/useExperiments.ts | 2 +- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/components/collection/drop/PaidGenerative.vue b/components/collection/drop/PaidGenerative.vue index 9805a086a4..667e0777c2 100644 --- a/components/collection/drop/PaidGenerative.vue +++ b/components/collection/drop/PaidGenerative.vue @@ -101,7 +101,7 @@ const mintNft = async () => { console.log('uid', uid.value) } - const cb = api.tx.utility.batchAll + const cb = api.tx.utility.batch const args = allocatedNFTs.value.map((allocatedNFT) => api.tx.nfts.mint( drop.value?.collection, @@ -114,7 +114,21 @@ const mintNft = async () => { ), ) - howAboutToExecute(accountId.value, cb, [args], { + const url = new URL( + 'https://nft-metadata.preschian-cdn.workers.dev/generate', + ) + const args2 = allocatedNFTs.value.map((nft) => { + url.searchParams.set('collection', drop.value?.collection) + url.searchParams.set('nft', nft.id.toString()) + url.searchParams.set('image', previewItem.value?.image as string) + return api.tx.nfts.setMetadata( + drop.value?.collection, + nft.id, + url.toString(), + ) + }) + + howAboutToExecute(accountId.value, cb, [[args, args2]], { onResult: ({ txHash }) => { mintingSession.value.txHash = txHash }, diff --git a/composables/useExperiments.ts b/composables/useExperiments.ts index bfa37b8c28..1a743cc25c 100644 --- a/composables/useExperiments.ts +++ b/composables/useExperiments.ts @@ -6,7 +6,7 @@ export default function () { // poc by using unique id. ref: https://github.com/kodadot/private-workers/issues/87#issuecomment-1997444473 const route = useRoute() - const pocDrops = ['/ahk/drops/pills'] + const pocDrops = ['/ahk/drops/pills', '/ahk/drops/monotifs'] const uid = computed(() => { return has('uid') || pocDrops.includes(route.path) }) From 1d43d4a3b0b5c3267a9b40e7e481775ea7a7f0d1 Mon Sep 17 00:00:00 2001 From: Preschian Febryantara Date: Wed, 20 Mar 2024 20:09:06 +0700 Subject: [PATCH 03/93] feat(PaidGenerative.vue): replace batch with batchAll in mintNft function refactor(PaidGenerative.vue): remove unused code and simplify mintNft function refactor(useDropMassMint.ts): comment out unused code and refactor allocateItems function feat(fxart.ts): add updateMetadata function fix(randomize.ts): increase length parameter in uidMathDate function --- components/collection/drop/PaidGenerative.vue | 25 +----- composables/drop/massmint/useDropMassMint.ts | 81 ++++++++++--------- services/fxart.ts | 27 +++++++ utils/randomize.ts | 2 +- 4 files changed, 76 insertions(+), 59 deletions(-) diff --git a/components/collection/drop/PaidGenerative.vue b/components/collection/drop/PaidGenerative.vue index 667e0777c2..264a544972 100644 --- a/components/collection/drop/PaidGenerative.vue +++ b/components/collection/drop/PaidGenerative.vue @@ -42,7 +42,6 @@ import useCursorDropEvents from '@/composables/party/useCursorDropEvents' import useDropMassMint from '@/composables/drop/massmint/useDropMassMint' import useDropMassMintListing from '@/composables/drop/massmint/useDropMassMintListing' -const { uid } = useExperiments() const { drop } = useDrop() const { fetchDropStatus } = useDropStatus() const instance = getCurrentInstance() @@ -97,11 +96,7 @@ const mintNft = async () => { initTransactionLoader() - if (uid.value) { - console.log('uid', uid.value) - } - - const cb = api.tx.utility.batch + const cb = api.tx.utility.batchAll const args = allocatedNFTs.value.map((allocatedNFT) => api.tx.nfts.mint( drop.value?.collection, @@ -114,21 +109,7 @@ const mintNft = async () => { ), ) - const url = new URL( - 'https://nft-metadata.preschian-cdn.workers.dev/generate', - ) - const args2 = allocatedNFTs.value.map((nft) => { - url.searchParams.set('collection', drop.value?.collection) - url.searchParams.set('nft', nft.id.toString()) - url.searchParams.set('image', previewItem.value?.image as string) - return api.tx.nfts.setMetadata( - drop.value?.collection, - nft.id, - url.toString(), - ) - }) - - howAboutToExecute(accountId.value, cb, [[args, args2]], { + howAboutToExecute(accountId.value, cb, [args], { onResult: ({ txHash }) => { mintingSession.value.txHash = txHash }, @@ -196,7 +177,7 @@ const submitMints = async () => { const response = await Promise.all(toMintNFTs.value.map(submitMint)) const mintedNfts = response.map((item) => ({ - id: `${drop.value.collection}-${item.sn}`, + id: drop.value.collection, collection: item.collection, chain: item.chain, name: item.name, diff --git a/composables/drop/massmint/useDropMassMint.ts b/composables/drop/massmint/useDropMassMint.ts index 7571b4589d..8df1fd074b 100644 --- a/composables/drop/massmint/useDropMassMint.ts +++ b/composables/drop/massmint/useDropMassMint.ts @@ -1,13 +1,13 @@ import { - AllocatedNFT, + // AllocatedNFT, DoResult, - allocateCollection, + // allocateCollection, allocateClaim as claimAllocation, } from '@/services/fxart' import { ImageDataPayload } from '../useGenerativeIframeData' import { ToMintNft } from '@/components/collection/drop/types' -import { getFakeEmail } from '@/components/collection/drop/utils' +// import { getFakeEmail } from '@/components/collection/drop/utils' import useDropMassMintPreview from './useDropMassMintPreview' import useDropMassMintUploader from './useDropMassMintUploader' import { useCollectionEntity } from '../useGenerativeDropMint' @@ -133,8 +133,8 @@ export default () => { try { loading.value = true - const email = raffleEmail.value || getFakeEmail() - const address = accountId.value + // const email = raffleEmail.value || getFakeEmail() + // const address = accountId.value const items = mintNfts.map(({ image, hash, metadata }) => ({ image, @@ -142,20 +142,29 @@ export default () => { metadata, })) - allocatedNFTs.value = await allocateItems({ items, email, address }) + // allocatedNFTs.value = await allocateItems({ items, email, address }) + allocatedNFTs.value = items.map((item) => { + return { + id: parseInt(uidMathDate()), + name: '', + image: item.image, + } + }) + console.log('[MASSMINT::ALLOCATE] Allocating', allocatedNFTs.value) // even thought the user might want x amount of items the worker can return a different amount - const allocatedNFTsToMint = toMintNFTs.value.slice( - 0, - allocatedNFTs.value.length, - ) + // const allocatedNFTsToMint = toMintNFTs.value.slice( + // 0, + // allocatedNFTs.value.length, + // ) - toMintNFTs.value = allocatedNFTsToMint.map((toMint, index) => { + toMintNFTs.value = toMintNFTs.value.map((toMint, index) => { const allocated = allocatedNFTs.value[index] return allocated - ? { ...toMint, name: allocated.name, sn: Number(allocated.id) } + ? { ...toMint, name: toMint.collectionName, sn: Number(allocated.id) } : toMint }) + console.log('[MASSMINT::ALLOCATE] Allocated', toMintNFTs.value) } catch (error) { console.log('[MASSMINT::ALLOCATE] Failed', error) } finally { @@ -163,30 +172,30 @@ export default () => { } } - const allocateItems = async ({ - items, - email, - address, - }): Promise => { - const results = [] as Array - - // @see https://github.com/kodadot/private-workers/pull/86#issuecomment-1975842570 for context - for (const item of items) { - const { result } = await allocateCollection( - { - email, - address, - image: item.image, - hash: item.hash, - metadata: item.metadata, - }, - drop.value.id, - ) - results.push(result) - } - - return results - } + // const allocateItems = async ({ + // items, + // email, + // address, + // }): Promise => { + // const results = [] as Array + + // // @see https://github.com/kodadot/private-workers/pull/86#issuecomment-1975842570 for context + // for (const item of items) { + // const { result } = await allocateCollection( + // { + // email, + // address, + // image: item.image, + // hash: item.hash, + // metadata: item.metadata, + // }, + // drop.value.id, + // ) + // results.push(result) + // } + + // return results + // } const submitMint = async (nft: MassMintNFT): Promise => { return new Promise((resolve, reject) => { diff --git a/services/fxart.ts b/services/fxart.ts index 5c7c239c3e..9412d7ebee 100644 --- a/services/fxart.ts +++ b/services/fxart.ts @@ -144,3 +144,30 @@ export const allocateClaim = async (body, id) => { ) } } + +export const updateMetadata = async ({ + nft, + collection, + chain, + image, + animationUrl, +}) => { + try { + const response = await api<{ result: DoResult }>('/drops/update-metadata', { + method: 'get', + query: { + nft, + collection, + chain, + image, + animationUrl, + }, + }) + + return response + } catch (error) { + throw new Error( + `[FXART::UPDATE_METADATA] ERROR: ${(error as FetchError).data}`, + ) + } +} diff --git a/utils/randomize.ts b/utils/randomize.ts index 643a22c049..7db19e99a7 100644 --- a/utils/randomize.ts +++ b/utils/randomize.ts @@ -12,7 +12,7 @@ export function uidCrypto(length = 9) { return paddedNumber.slice(0, length) } -export function uidMathDate(length = 5) { +export function uidMathDate(length = 6) { const date = format(new Date(), 'yyMM') return date + uidMath(length) } From f792c96ae4ae9d6c77ab44f48ab6f854bc853817 Mon Sep 17 00:00:00 2001 From: Preschian Febryantara Date: Wed, 20 Mar 2024 21:27:16 +0700 Subject: [PATCH 04/93] feat(useDropMassMint.ts): replace claimAllocation with updateMetadata function call --- composables/drop/massmint/useDropMassMint.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/composables/drop/massmint/useDropMassMint.ts b/composables/drop/massmint/useDropMassMint.ts index 8df1fd074b..a99544bdc6 100644 --- a/composables/drop/massmint/useDropMassMint.ts +++ b/composables/drop/massmint/useDropMassMint.ts @@ -2,7 +2,8 @@ import { // AllocatedNFT, DoResult, // allocateCollection, - allocateClaim as claimAllocation, + // allocateClaim as claimAllocation, + updateMetadata, } from '@/services/fxart' import { ImageDataPayload } from '../useGenerativeIframeData' @@ -36,7 +37,7 @@ export default () => { previewItem, allocatedNFTs, toMintNFTs, - mintingSession, + // mintingSession, loading, } = storeToRefs(dropStore) @@ -200,14 +201,13 @@ export default () => { const submitMint = async (nft: MassMintNFT): Promise => { return new Promise((resolve, reject) => { try { - claimAllocation( - { - sn: nft.sn, - txHash: mintingSession.value.txHash, - address: accountId.value, - }, - drop.value.id, - ).then(({ result }) => resolve(result)) + updateMetadata({ + nft: nft.sn, + collection: drop.value.id, + chain: drop.value.chain, + image: nft.image, + animationUrl: nft.image, + }).then(({ result }) => resolve(result)) } catch (e) { reject(e) } From ce2df2478210943e5924c9b992c941422436f6c6 Mon Sep 17 00:00:00 2001 From: Preschian Febryantara Date: Wed, 27 Mar 2024 12:50:04 +0700 Subject: [PATCH 05/93] feat: add experiment flagUid to control display of serial number in ItemsGrid refactor: update minting process in PaidGenerative.vue to store NFT metadata on-chain fix: adjust useNft to accept displaySn parameter for controlling display of serial number refactor: update useDropMassMint to include NFT metadata in updateMetadata call fix: adjust useExperiments to rename uid to flagUid refactor: update useNftMetadata to accept displaySn parameter for controlling display of serial number fix: change BASE_URL in fxart service to localhost for local testing refactor: update updateMetadata in fxart service to accept new parameters for updating metadata --- components/collection/drop/ItemsGrid.vue | 4 +- components/collection/drop/PaidGenerative.vue | 75 ++++++++++++------- components/items/ItemsGrid/ItemsGridImage.vue | 2 +- composables/drop/massmint/useDropMassMint.ts | 7 +- composables/useExperiments.ts | 4 +- composables/useNft.ts | 23 ++++-- services/fxart.ts | 22 ++---- 7 files changed, 81 insertions(+), 56 deletions(-) diff --git a/components/collection/drop/ItemsGrid.vue b/components/collection/drop/ItemsGrid.vue index 30e4afc629..b12be59d2e 100644 --- a/components/collection/drop/ItemsGrid.vue +++ b/components/collection/drop/ItemsGrid.vue @@ -18,7 +18,7 @@ @@ -28,6 +28,8 @@ diff --git a/components/gallery/useGalleryItem.ts b/components/gallery/useGalleryItem.ts index 7acf0ace83..fd4ad27591 100644 --- a/components/gallery/useGalleryItem.ts +++ b/components/gallery/useGalleryItem.ts @@ -99,7 +99,7 @@ export const useGalleryItem = (nftId?: string): GalleryItem => { nftMetadata.value = metadata nftResources.value = resources - nftImage.value = metadata.image || '' + nftImage.value = sanitizeIpfsUrl(metadata.image) || '' nftMimeType.value = metadata.imageMimeType || '' nftAnimation.value = metadata.animationUrl || '' nftAnimationMimeType.value = metadata.animationUrlMimeType || '' From 65f864402b97b2c473e38269cf041ef937b86ce6 Mon Sep 17 00:00:00 2001 From: iMac7 Date: Thu, 28 Mar 2024 10:11:42 +0000 Subject: [PATCH 08/93] modal-width to tailwind --- components/collection/drop/AddFundsModal.vue | 8 +------- .../collection/drop/modal/DropConfirmModal.vue | 2 +- .../drop/modal/newsletter/EmailSignup.vue | 4 ---- .../common/autoTeleport/AutoTeleportModal.vue | 12 +----------- .../autoTeleport/AutoTeleportWelcomeModal.vue | 10 +--------- .../ConfirmPurchaseModal.vue | 2 +- .../common/itemTransfer/ItemTransferModal.vue | 16 +++++----------- components/create/Confirm/MintConfirmModal.vue | 2 +- components/shared/modals/ModalBody.vue | 13 +++---------- libs/ui/src/components/NeoModal/NeoModal.scss | 5 ----- 10 files changed, 14 insertions(+), 60 deletions(-) diff --git a/components/collection/drop/AddFundsModal.vue b/components/collection/drop/AddFundsModal.vue index 197e3539e6..ace2e042cc 100644 --- a/components/collection/drop/AddFundsModal.vue +++ b/components/collection/drop/AddFundsModal.vue @@ -2,7 +2,7 @@ { height: initial; } } - -@include mobile() { - .modal-width { - width: unset; - } -} diff --git a/components/collection/drop/modal/DropConfirmModal.vue b/components/collection/drop/modal/DropConfirmModal.vue index 03891e116f..108098d3c5 100644 --- a/components/collection/drop/modal/DropConfirmModal.vue +++ b/components/collection/drop/modal/DropConfirmModal.vue @@ -3,7 +3,7 @@ :value="isModalActive" :can-cancel="isClaimingDropStep ? false : ['outside', 'escape']" class="top" - content-class="modal-width" + content-class="!w-[unset]" @close="onClose"> { @import '@/assets/styles/abstracts/variables'; @import '@/assets/styles/abstracts/animations'; -.modal-width { - width: 25rem; -} - .shine:not(:hover):not(:disabled) { @include shineEffect(var(--k-accent-light-3), lightgrey, false); diff --git a/components/common/autoTeleport/AutoTeleportModal.vue b/components/common/autoTeleport/AutoTeleportModal.vue index d22555a96f..a85d8037b8 100644 --- a/components/common/autoTeleport/AutoTeleportModal.vue +++ b/components/common/autoTeleport/AutoTeleportModal.vue @@ -4,7 +4,7 @@ :can-cancel="['outside', 'escape']" class="z-[1000]" @close="onClose"> -