diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index a629f2d..b420e06 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -12,18 +12,23 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + - name: Setup Node.js uses: actions/setup-node@v2 with: node-version: '18' cache: 'npm' + - name: Install dependencies run: npm install + - name: Copy config.json run: | cp ./resources/js/config.json.example ./resources/js/config.json + - name: Build static project run: npm run prod-laravel + - name: Archive build artifacts uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..a5f0f12 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,30 @@ +name: eslint +on: + push: + branches: + - master + pull_request: + +jobs: + tests: + name: Eslint & Prettier + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v2 + with: + node-version: '20' + cache: 'npm' + + - name: Install dependencies + run: npm install + + - name: Run Pretty + run: npm run pretty:check + + - name: Run Eslint + run: npm run lint \ No newline at end of file diff --git a/resources/js/api/collection.ts b/resources/js/api/collection.ts index b9ba928..8f88ae2 100644 --- a/resources/js/api/collection.ts +++ b/resources/js/api/collection.ts @@ -15,11 +15,12 @@ export class CollectionApi { return ApiService.sendPlatformRequest(data); } - static async getCollectionsIds(first: number = 20) { + static async getCollectionsIds(first: number = 20, after?: string) { const data = { query: queries.GetCollectionIds, variables: { first, + after, }, }; diff --git a/resources/js/api/index.ts b/resources/js/api/index.ts index a1bd7a3..c15b185 100644 --- a/resources/js/api/index.ts +++ b/resources/js/api/index.ts @@ -27,6 +27,7 @@ export class ApiService { credentials = 'omit', mode, nest = true, + auth = true, }: { url: string; data?: Record; @@ -35,6 +36,7 @@ export class ApiService { credentials?: 'omit' | 'same-origin' | 'include'; mode?: 'cors' | 'no-cors' | 'same-origin' | 'navigate'; nest?: boolean; + auth?: boolean; }): Promise { let body: string | null = null; const fullUrl = url; @@ -44,10 +46,10 @@ export class ApiService { body = JSON.stringify(data); } - if (mode) { + if (auth) { if (!useAppStore().isMultiTenant) { headers.Authorization = useAppStore().authorization_token; - } else if (mode) { + } else { headers['X-CSRF-TOKEN'] = csrf; } } @@ -160,6 +162,7 @@ export class ApiService { method: HttpMethods.GET, credentials: undefined, mode: undefined, + auth: false, }); } diff --git a/resources/js/components/FormInput.vue b/resources/js/components/FormInput.vue index 7961816..26d4993 100644 --- a/resources/js/components/FormInput.vue +++ b/resources/js/components/FormInput.vue @@ -71,6 +71,7 @@ const props = withDefaults( type?: string; min?: number; modelValue?: number | string | null; + value?: number | string | null; disabled?: boolean; prefix?: string; name: string; @@ -109,10 +110,10 @@ const inputChange = (e: Event) => { const localModelValue = computed({ get() { - return props.modelValue; + return props.modelValue ?? props.value; }, set(value) { - if (localModelValue.value !== value) { + if (value && localModelValue.value !== value) { emit('update:modelValue', value); } }, diff --git a/resources/js/components/WalletConnectButton.vue b/resources/js/components/WalletConnectButton.vue index bcffc79..826927f 100644 --- a/resources/js/components/WalletConnectButton.vue +++ b/resources/js/components/WalletConnectButton.vue @@ -112,10 +112,11 @@ const connectWallet = async (provider: string) => { }); await connectionStore.getAccounts(); const localAccounts = connectionStore.accounts.map((account) => publicKeyToAddress(account.address)); - const walletAccounts = useAppStore().user?.walletAccounts?.map((account) => publicKeyToAddress(account)); + const walletAccounts = useAppStore().user?.walletAccounts?.map((account) => publicKeyToAddress(account)) ?? []; const uniqueAccounts = [...new Set([...walletAccounts, ...localAccounts])]; - - AuthApi.setUserAccounts(uniqueAccounts); + if (useAppStore().isMultiTenant) { + AuthApi.setUserAccounts(uniqueAccounts); + } } catch { snackbar.error({ title: 'Failed to connect the wallet' }); } finally { diff --git a/resources/js/components/pages/Collections.vue b/resources/js/components/pages/Collections.vue index 5d85456..e103a87 100644 --- a/resources/js/components/pages/Collections.vue +++ b/resources/js/components/pages/Collections.vue @@ -371,7 +371,7 @@ const fetchUri = async (uri) => { } return '-'; - } catch (e) { + } catch { return '-'; } }; diff --git a/resources/js/components/pages/Tokens.vue b/resources/js/components/pages/Tokens.vue index 4f28ea1..508a827 100644 --- a/resources/js/components/pages/Tokens.vue +++ b/resources/js/components/pages/Tokens.vue @@ -98,7 +98,7 @@ " > {{ tokenNames[`${token.collection.collectionId}-${token.tokenId}`] }} diff --git a/resources/js/components/pages/create/CreateCollection.vue b/resources/js/components/pages/create/CreateCollection.vue index f5c99dc..ac17a56 100644 --- a/resources/js/components/pages/create/CreateCollection.vue +++ b/resources/js/components/pages/create/CreateCollection.vue @@ -114,11 +114,10 @@ description="With this option selected tokens in this collection will have an infinite supply." /> @@ -377,12 +376,14 @@ const maxTokenCount = ref(); const maxTokenSupply = ref(); const isInfiniteSupply = ref(true); const isInfiniteCount = ref(true); -const forceSingleMint = ref(false); +const forceCollapsingSupply = ref(false); const beneficiaryAddress = ref(''); const beneficiaryPercentage = ref(0); const idempotencyKey = ref(''); const skipValidation = ref(false); +const hasMintPolicy = computed(() => !isInfiniteCount.value || !isInfiniteSupply.value); + const attributes = ref([ { key: '', @@ -413,7 +414,7 @@ const validation = yup.object({ bannerUrl: stringNotRequiredSchema, maxTokenCount: numberNotRequiredSchema.typeError('Max token count must be a number'), maxTokenSupply: numberNotRequiredSchema.typeError('Max token supply must be a number'), - forceSingleMint: booleanNotRequiredSchema, + forceCollapsingSupply: booleanNotRequiredSchema, beneficiaryAddress: addressNotRequiredSchema, beneficiaryPercentage: yup.number().when('beneficiaryAddress', { is: (val) => val !== '' && val !== null, @@ -500,11 +501,13 @@ const createCollection = async () => { const res = await CollectionApi.createCollection( formatData({ - mintPolicy: { - maxTokenCount: isInfiniteCount.value ? null : maxTokenCount.value, - maxTokenSupply: isInfiniteSupply.value ? null : maxTokenSupply.value, - forceSingleMint: forceSingleMint.value, - }, + mintPolicy: hasMintPolicy.value + ? { + maxTokenCount: isInfiniteCount.value ? null : maxTokenCount.value, + maxTokenSupply: isInfiniteSupply.value ? null : maxTokenSupply.value, + forceCollapsingSupply: forceCollapsingSupply.value, + } + : null, marketPolicy: beneficiaryAddress.value ? { beneficiary: addressToPublicKey(beneficiaryAddress.value) ?? null, diff --git a/resources/js/components/pages/create/CreateToken.vue b/resources/js/components/pages/create/CreateToken.vue index afa3dd4..752ed2d 100644 --- a/resources/js/components/pages/create/CreateToken.vue +++ b/resources/js/components/pages/create/CreateToken.vue @@ -81,6 +81,13 @@ label="Description" description="The description of the token." /> +
+ +
+ + + +
appStore.collections); const isAdvanced = computed(() => mode.value === 'advanced'); +const totalInfuseAmountComputed = computed(() => { + return initialSupply.value * infuseAmount.value; +}); + const validation = yup.object({ imageUrl: stringNotRequiredSchema, name: stringRequiredSchema, description: stringNotRequiredSchema, + symbol: stringRequiredSchema, collectionId: collectionIdRequiredSchema, tokenId: stringRequiredSchema, recipient: addressRequiredSchema, - initialSupply: numberRequiredSchema.typeError('Initial Supply must be a number').min(1), + initialSupply: yup.number().when({ + is: () => tokenType.value === 'ft', + then: () => numberRequiredSchema.typeError('Initial Supply must be a number').min(1), + otherwise: () => numberNotRequiredSchema, + }), capAmount: numberNotRequiredSchema, beneficiaryAddress: addressNotRequiredSchema, beneficiaryPercentage: yup.number().when('beneficiaryAddress', { @@ -487,10 +547,20 @@ const createToken = async () => { isCurrency: isCurrency.value, }, listingForbidden: listingForbidden.value, + ...(infuseEnj.value + ? { + infusion: totalInfuseAmountComputed.value, + anyoneCanInfuse: infuseAccess.value === 'Everyone', + } + : {}), attributes: [ ...simpleAttributes(), ...attributes.value.filter((a) => a.key !== '' && a.value !== ''), ], + metadata: { + name: name.value, + symbol: symbol.value, + }, }, idempotencyKey: idempotencyKey.value, skipValidation: skipValidation.value, diff --git a/resources/js/components/slideovers/collection/DetailsCollectionSlideover.vue b/resources/js/components/slideovers/collection/DetailsCollectionSlideover.vue index f6fe5f8..dd1773a 100644 --- a/resources/js/components/slideovers/collection/DetailsCollectionSlideover.vue +++ b/resources/js/components/slideovers/collection/DetailsCollectionSlideover.vue @@ -39,6 +39,20 @@
+
+
Total Infusion
+
+ {{ formatPriceFromENJ(item.totalInfusion) }} +
+
+ +
+
Total Deposit
+
+ {{ formatPriceFromENJ(item.totalDeposit) }} +
+
+
Total tokens
@@ -102,6 +116,7 @@ diff --git a/resources/js/components/slideovers/token/MintTokenSlideover.vue b/resources/js/components/slideovers/token/MintTokenSlideover.vue index c15f0a2..c7aa4be 100644 --- a/resources/js/components/slideovers/token/MintTokenSlideover.vue +++ b/resources/js/components/slideovers/token/MintTokenSlideover.vue @@ -92,7 +92,6 @@ import { addressRequiredSchema, booleanNotRequiredSchema, collectionIdRequiredSchema, - numberNotRequiredSchema, numberRequiredSchema, stringNotRequiredSchema, stringRequiredSchema, @@ -130,7 +129,6 @@ const validation = yup.object({ tokenId: stringRequiredSchema, recipient: addressRequiredSchema, amount: numberRequiredSchema.typeError('Amount must be a number'), - unitPrice: numberNotRequiredSchema.min(0.01).typeError('Unit price must be a number'), idempotencyKey: stringNotRequiredSchema, skipValidation: booleanNotRequiredSchema, }); diff --git a/resources/js/graphql/mutation/TransferBalance.ts b/resources/js/graphql/mutation/TransferBalance.ts index 0fc8dee..7dfcbe6 100644 --- a/resources/js/graphql/mutation/TransferBalance.ts +++ b/resources/js/graphql/mutation/TransferBalance.ts @@ -1,9 +1,8 @@ -export default `mutation TransferBalance($recipient: String!, $amount: BigInt!, $signingAccount: String, $keepAlive: Boolean = false, $idempotencyKey: String, $skipValidation: Boolean = false) { - TransferBalance( +export default `mutation TransferAllowDeath($recipient: String!, $amount: BigInt!, $signingAccount: String, $idempotencyKey: String, $skipValidation: Boolean = false) { + TransferAllowDeath( recipient: $recipient amount: $amount signingAccount: $signingAccount - keepAlive: $keepAlive idempotencyKey: $idempotencyKey skipValidation: $skipValidation ) { diff --git a/resources/js/graphql/query/GetCollection.ts b/resources/js/graphql/query/GetCollection.ts index e630e15..3f4271d 100644 --- a/resources/js/graphql/query/GetCollection.ts +++ b/resources/js/graphql/query/GetCollection.ts @@ -20,6 +20,19 @@ export default `query GetCollection($collectionId: BigInt!) { value } network + maxTokenCount + maxTokenSupply + forceCollapsingSupply + totalInfusion + totalDeposit + creationDeposit { + depositor { + account { + publicKey + } + } + amount + } tokens { totalCount } diff --git a/resources/js/graphql/query/GetCollections.ts b/resources/js/graphql/query/GetCollections.ts index faa8321..5190ac5 100644 --- a/resources/js/graphql/query/GetCollections.ts +++ b/resources/js/graphql/query/GetCollections.ts @@ -22,6 +22,19 @@ export default `query GetCollections($after: String, $first: Int) { value } network + maxTokenCount + maxTokenSupply + forceCollapsingSupply + totalInfusion + totalDeposit + creationDeposit { + depositor { + account { + publicKey + } + } + amount + } tokens { totalCount } diff --git a/resources/js/graphql/query/GetTokens.ts b/resources/js/graphql/query/GetTokens.ts index 98bead8..6f4ce29 100644 --- a/resources/js/graphql/query/GetTokens.ts +++ b/resources/js/graphql/query/GetTokens.ts @@ -11,8 +11,6 @@ export default `query GetTokens($collectionId: BigInt, $tokenIds: [EncodableToke isFrozen supply capSupply - minimumBalance - unitPrice attributeCount attributes { key diff --git a/resources/js/store/index.ts b/resources/js/store/index.ts index 493c7bf..1e63a37 100644 --- a/resources/js/store/index.ts +++ b/resources/js/store/index.ts @@ -102,7 +102,6 @@ export const useAppStore = defineStore('app', { } await useConnectionStore().getSession(); - await this.fetchCollectionIds(); } catch (error: any) { snackbar.error({ title: error }); @@ -173,16 +172,18 @@ export const useAppStore = defineStore('app', { this.tokensCount = res.data.User?.apiTokens.length; } }, - async fetchCollectionIds(totalCount?: number) { + async fetchCollectionIds(totalCount?: number, after?: string) { if (!this.loggedIn) return false; - try { this.newCollection = false; - const res = await CollectionApi.getCollectionsIds(totalCount); + const res = await CollectionApi.getCollectionsIds(totalCount, after); const collectionsData = res.data.GetCollections; if (collectionsData.pageInfo.hasNextPage) { - await this.fetchCollectionIds(collectionsData.totalCount > 500 ? 500 : collectionsData.totalCount); + await this.fetchCollectionIds( + collectionsData.totalCount > 500 ? 500 : collectionsData.totalCount, + collectionsData.pageInfo.endCursor + ); } else { const accounts = useConnectionStore().getTrackableAccounts(); this.collections = [