diff --git a/nuxt.config.ts b/nuxt.config.ts
index e6ef06e9..679a9bbe 100644
--- a/nuxt.config.ts
+++ b/nuxt.config.ts
@@ -36,7 +36,7 @@ export default defineNuxtConfig({
},
tailwindcss: {
config: {
- presets: [require('@lukso/web-components/tailwind.config')],
+ presets: [require('./tailwind.config')],
content: [], // it already merges with nuxt default config https://tailwindcss.nuxt.dev/tailwind/config#merging-strategy
},
cssPath: '~/assets/styles/main.scss',
@@ -103,6 +103,12 @@ export default defineNuxtConfig({
},
imports: {
dirs: ['stores/**', 'shared/**'],
+ presets: [
+ {
+ from: 'pinia',
+ imports: ['storeToRefs'],
+ },
+ ],
},
runtimeConfig: {
public: {},
diff --git a/package.json b/package.json
index 79d8ea5b..824fcd2a 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,7 @@
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
- "postinstall": "nuxt prepare",
+ "postinstall": "npm-run-all nuxt:prepare typechain",
"lint:fix": "npm-run-all --aggregate-output --continue-on-error --parallel \"lint:*:fix\"",
"lint": "npm-run-all --aggregate-output --continue-on-error --parallel \"lint:{eslint,css,fmt,types}\"",
"lint:eslint": "eslint --ext '.js,.vue,.ts' .",
@@ -23,15 +23,17 @@
"test": "npx playwright test",
"test:ui": "npx playwright test --ui",
"test:debug": "npx playwright test --debug",
- "pages:build": "cross-env NODE_ENV=production NITRO_PRESET=cloudflare_pages nuxt build --standalone"
+ "pages:build": "cross-env NODE_ENV=production NITRO_PRESET=cloudflare_pages nuxt build --standalone",
+ "nuxt:prepare": "nuxt prepare",
+ "typechain": "typechain --target web3-v1 --out-dir types/contracts/ './node_modules/@lukso/lsp-smart-contracts/artifacts/*.json'"
},
"devDependencies": {
- "@erc725/erc725.js": "0.18.0",
+ "@erc725/erc725.js": "0.19.0",
"@esbuild-plugins/node-globals-polyfill": "0.2.3",
"@formatjs/intl": "^2.9.0",
"@lukso/lsp-factory.js": "3.1.1",
"@lukso/lsp-smart-contracts": "0.11.1",
- "@lukso/web-components": "1.46.0",
+ "@lukso/web-components": "1.47.1",
"@nuxt/devtools": "^0.7.4",
"@nuxtjs/algolia": "^1.9.0",
"@nuxtjs/device": "^3.1.0",
@@ -39,6 +41,7 @@
"@nuxtjs/tailwindcss": "^6.8.0",
"@pinia/nuxt": "^0.4.11",
"@playwright/test": "1.36.2",
+ "@typechain/web3-v1": "^6.0.7",
"@types/fs-extra": "11.0.1",
"@types/node": "^20.4.7",
"@types/randombytes": "2.0.0",
@@ -75,6 +78,7 @@
"stylelint-config-standard-scss": "10.0.0",
"stylelint-prettier": "4.0.2",
"stylelint-scss": "5.0.1",
+ "typechain": "^8.3.2",
"typescript": "5.1.6",
"util": "0.12.5",
"web3": "~1.10.0",
diff --git a/pages/[profileAddress]/index.vue b/pages/[profileAddress]/index.vue
index d878e39c..2358038b 100644
--- a/pages/[profileAddress]/index.vue
+++ b/pages/[profileAddress]/index.vue
@@ -1,8 +1,5 @@
@@ -27,40 +41,20 @@ watchEffect(() => {
'opacity-0': profileStatus.isAssetLoading,
'opacity-100': !profileStatus.isAssetLoading,
}"
- class="max-w-[835px] py-20 px-4 mx-auto relative grid grid-cols-[1fr,2fr] gap-12 transition-opacity duration-300"
+ class="max-w-content py-20 px-4 mx-auto relative grid grid-cols-[1fr,2fr] gap-12 transition-opacity duration-300"
>
-
-
-
-
- {{ $formatMessage('asset_created_by') }}
-
-
-
-
+
- {{ nft?.data.collectionName }}
+ {{ nft?.name }}
@@ -74,43 +68,36 @@ watchEffect(() => {
>
-
{{
+ {{
$formatMessage('token_details_send', {
- token: nft?.data.collectionSymbol || '',
+ token: nft?.symbol || '',
})
}}
- {{ nft?.data.collectionName }}
+ {{ nft?.name }}
-
+
-
+
-
+
-import { storeToRefs } from 'pinia'
-import { toWei } from 'web3-utils'
+import { AbiItem, toWei } from 'web3-utils'
import { TransactionConfig } from 'web3-core'
+import LSP7Mintable from '@lukso/lsp-smart-contracts/artifacts/LSP7Mintable.json'
+import LSP8Mintable from '@lukso/lsp-smart-contracts/artifacts/LSP8Mintable.json'
-import { PROVIDERS } from '@/types/enums'
+import {
+ LSP7DigitalAsset,
+ LSP8IdentifiableDigitalAsset,
+} from '@/types/contracts'
const { profile: connectedProfile, status } = useConnectedProfileStore()
-const { profile: viewedProfile } = useViewedProfileStore()
+const {
+ profile: viewedProfile,
+ setBalance,
+ removeNft,
+} = useViewedProfileStore()
+const { ownedAssets } = storeToRefs(useViewedProfileStore())
const { currentNetwork } = useAppStore()
const { asset, onSend, amount, receiver } = storeToRefs(useSendStore())
const { setStatus, clearSend } = useSendStore()
const { showModal } = useModal()
const { formatMessage } = useIntl()
-const { sendTransaction, getBalance } = useWeb3(PROVIDERS.INJECTED)
+const { sendTransaction, getBalance, contract } = useWeb3(PROVIDERS.INJECTED)
onMounted(() => {
setStatus('draft')
- clearSend()
- asset.value = {
- name: 'LUKSO',
- symbol: currentNetwork.token.symbol,
- icon: ASSET_LYX_ICON_URL,
+ onSend.value = handleSend
+
+ // for nft's we prefill amount
+ if (isNft(asset.value)) {
+ amount.value = '1'
}
+})
- onSend.value = handleSend
+onUnmounted(() => {
+ setStatus('draft')
+ clearSend()
})
watchEffect(() => {
- if (!asset.value) {
+ // until everything is loaded we skip this effect
+ if (!status.isProfileLoaded) {
return
}
+ try {
+ amount.value = undefined
+ const assetAddress = useRouter().currentRoute.value.query.asset
+ assertAddress(assetAddress, 'asset')
+ asset.value = ownedAssets.value?.find(
+ asset => asset.address === assetAddress
+ )
+ assertAddress(asset.value?.address, 'asset')
+ } catch (error) {
+ // fallback to native token
+ asset.value = {
+ name: currentNetwork.token.name,
+ symbol: currentNetwork.token.symbol,
+ icon: ASSET_LYX_ICON_URL,
+ isNativeToken: true,
+ }
+ }
+
// since balance is not avail in onMounted hook
asset.value = {
...asset.value,
- amount: connectedProfile.balance,
+ amount: isLyx(asset.value) ? connectedProfile.balance : asset.value.amount,
}
+ // when logout
if (!status.isConnected) {
navigateTo(homeRoute())
}
@@ -47,16 +79,69 @@ const handleSend = async () => {
try {
setStatus('pending')
- let transaction = {
- from: connectedProfile.address,
- to: receiver.value?.address as unknown as string,
- value: toWei(amount.value || '0'),
- gas: DEFAULT_GAS,
- gasPrice: DEFAULT_GAS_PRICE,
- } as TransactionConfig
+ // native token transfer
+ if (isLyx(asset.value)) {
+ const transaction = {
+ from: connectedProfile.address,
+ to: receiver.value?.address as unknown as string,
+ value: toWei(amount.value || '0'),
+ } as TransactionConfig
+
+ await sendTransaction(transaction)
+ await updateLyxBalance()
+ } else {
+ // custom token transfer
+ switch (asset.value?.standard) {
+ case 'LSP7DigitalAsset':
+ const tokenContract = contract(
+ LSP7Mintable.abi as AbiItem[],
+ asset.value?.address
+ )
+
+ assertAddress(connectedProfile.address)
+ assertAddress(receiver.value?.address)
+ await tokenContract.methods
+ .transfer(
+ connectedProfile.address,
+ receiver.value?.address,
+ toWei(amount.value || '0'),
+ false,
+ '0x'
+ )
+ .send({ from: connectedProfile.address })
+ const balance = (await tokenContract.methods
+ .balanceOf(connectedProfile.address)
+ .call()) as string
+ assertAddress(asset.value?.address, 'asset')
+ setBalance(asset.value.address, balance)
+ break
+ case 'LSP8IdentifiableDigitalAsset':
+ const nftContract = contract(
+ LSP8Mintable.abi as AbiItem[],
+ asset.value?.address
+ )
+
+ assertAddress(connectedProfile.address)
+ assertAddress(receiver.value?.address)
+ assertString(asset.value.tokenId)
+ await nftContract.methods
+ .transfer(
+ connectedProfile.address,
+ receiver.value?.address,
+ asset.value.tokenId,
+ false,
+ '0x'
+ )
+ .send({ from: connectedProfile.address })
+ assertNotUndefined(asset.value.address, 'asset')
+ removeNft(asset.value.address, asset.value.tokenId)
+ break
+ default:
+ console.error('Unknown token type')
+ break
+ }
+ }
- await sendTransaction(transaction)
- await updateBalance()
setStatus('success')
} catch (error: any) {
console.error(error)
@@ -71,9 +156,9 @@ const handleSend = async () => {
message: formatMessage('send_error_rejected_request'),
})
default:
- return showModal({
+ showModal({
title: formatMessage('send_error_title'),
- message: error.message,
+ message: formatMessage('send_error_message'),
})
}
}
@@ -86,7 +171,7 @@ const handleSend = async () => {
}
}
-const updateBalance = async () => {
+const updateLyxBalance = async () => {
assertString(connectedProfile.address)
connectedProfile.balance = await getBalance(connectedProfile.address)
diff --git a/pages/[profileAddress]/token/[tokenAddress].vue b/pages/[profileAddress]/token/[tokenAddress].vue
index 0f03d778..4252e9fe 100644
--- a/pages/[profileAddress]/token/[tokenAddress].vue
+++ b/pages/[profileAddress]/token/[tokenAddress].vue
@@ -1,7 +1,5 @@
@@ -29,7 +39,7 @@ const tokenSupply = computed(() => {
'opacity-0': profileStatus.isAssetLoading,
'opacity-100': !profileStatus.isAssetLoading,
}"
- class="max-w-[835px] py-20 px-4 mx-auto relative grid grid-cols-[1fr,2fr] gap-12 transition-opacity duration-300"
+ class="max-w-content py-20 px-4 mx-auto relative grid grid-cols-[1fr,2fr] gap-12 transition-opacity duration-300"
>
@@ -40,7 +50,7 @@ const tokenSupply = computed(() => {
@@ -53,37 +63,42 @@ const tokenSupply = computed(() => {
>
- {{
+ {{
$formatMessage('token_details_send', {
- token: token?.data.symbol || '',
+ token: token?.symbol || '',
})
}}