Skip to content

Commit

Permalink
[TAS-880] ✨ Support storing NFT Book in API wallet (#85)
Browse files Browse the repository at this point in the history
  • Loading branch information
WeiJun0827 authored and williamchong committed Jan 22, 2024
1 parent 3414f08 commit b2c438d
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 35 deletions.
4 changes: 4 additions & 0 deletions constant/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export const LIKER_NFT_FEE_WALLET = IS_TESTNET
? 'like1yney2cqn5qdrlc50yr5l53898ufdhxafqz9gxp'
: 'like10ywsmztkxjl55xarxnhlxwc83z9v2hkxtsajwl'

export const LIKER_NFT_TARGET_ADDRESS = IS_TESTNET
? 'like1yney2cqn5qdrlc50yr5l53898ufdhxafqz9gxp'
: 'like10ywsmztkxjl55xarxnhlxwc83z9v2hkxtsajwl'

export const LIKE_CO_API = `https://api.${IS_TESTNET ? 'rinkeby.' : ''}like.co`
export const APP_LIKE_CO_URL = `https://app.${IS_TESTNET ? 'rinkeby.' : ''}like.co`
export const LIKER_LAND_URL = `https://${LIKER_LAND_HOST}`
Expand Down
53 changes: 47 additions & 6 deletions pages/nft-book-store/new.vue
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,20 @@
</UFormGroup>

<UFormGroup :label="`Total number of NFT for sale of this ${priceItemLabel}`">
<UInput :value="p.stock" type="number" step="0.01" :min="0" @input="e => updatePrice(e, 'stock', index)" />
<UInput :value="p.stock" type="number" step="1" :min="0" @input="e => updatePrice(e, 'stock', index)" />
</UFormGroup>

<URadioGroup
v-model="p.deliverMethod"
:legend="`Deliver method of this ${priceItemLabel}`"
:options="deliverMethodOptions"
/>

<UFormGroup
v-if="p.deliverMethod === 'auto'"
:label="`Memo of this ${priceItemLabel}`"
>
<UInput placeholder="Thank you! 謝謝你的支持!" :value="p.autoMemo" @input="e => updatePrice(e, 'autoMemo', index)" />
</UFormGroup>

<UFormGroup
Expand Down Expand Up @@ -381,12 +394,13 @@ import { v4 as uuidv4 } from 'uuid'
import { LCD_URL, LIKE_CO_API } from '~/constant'
import { useBookStoreApiStore } from '~/stores/book-store-api'
import { useWalletStore } from '~/stores/wallet'
import { getPortfolioURL } from '~/utils'
import { getNFTAuthzGrants } from '~/utils/cosmos'
import { getPortfolioURL, deliverMethodOptions } from '~/utils'
import { getNFTAuthzGrants, sendNFTsToAPIWallet } from '~/utils/cosmos'
const walletStore = useWalletStore()
const bookStoreApiStore = useBookStoreApiStore()
const { wallet } = storeToRefs(walletStore)
const { connect } = walletStore
const { wallet, signer } = storeToRefs(walletStore)
const { token } = storeToRefs(bookStoreApiStore)
const { newBookListing, updateEditionPrice } = bookStoreApiStore
Expand Down Expand Up @@ -415,6 +429,8 @@ const mustClaimToView = ref(false)
const hideDownload = ref(false)
const prices = ref<any[]>([{
price: MINIMAL_PRICE,
deliverMethod: 'auto',
autoMemo: '',
stock: Number(route.query.count as string || 1),
nameEn: 'Standard Edition',
nameZh: '標準版',
Expand Down Expand Up @@ -455,7 +471,7 @@ const toolbarOptions = ref<string[]>([
'preview'
])
const isEditMode = computed(() => route.params.editingClassId && editionIndex.value)
const isEditMode = computed(() => Boolean(route.params.editingClassId && editionIndex.value))
const pageTitle = computed(() => isEditMode.value ? 'Edit Current Edition' : 'New NFT Book Listing')
const submitButtonText = computed(() => isEditMode.value ? 'Save Changes' : 'Submit')
const editionInfo = ref<any>({})
Expand Down Expand Up @@ -590,6 +606,8 @@ function addMorePrice () {
prices.value.push({
index: uuidv4(),
price: MINIMAL_PRICE,
deliverMethod: 'auto',
autoMemo: '',
stock: 1,
nameEn: `Tier ${nextPriceIndex.value}`,
nameZh: `級別 ${nextPriceIndex.value}`,
Expand Down Expand Up @@ -655,6 +673,8 @@ function mapPrices (prices:any) {
priceInDecimal: Math.round(Number(p.price) * 100),
price: Number(p.price),
stock: Number(p.stock),
isAutoDeliver: p.deliverMethod === 'auto',
autoMemo: p.deliverMethod === 'auto' ? (p.autoMemo || '') : '',
hasShipping: p.hasShipping || false
}))
}
Expand Down Expand Up @@ -712,6 +732,26 @@ async function submitNewClass () {
}))
: undefined
const autoDeliverCount = p
.filter(price => price.isAutoDeliver)
.reduce((acc, price) => acc + price.stock, 0)
let autoDeliverNFTsTxHash = ''
if (autoDeliverCount > 0) {
if (!wallet.value || !signer.value) {
await connect()
}
if (!wallet.value || !signer.value) {
throw new Error('Unable to connect to wallet')
}
autoDeliverNFTsTxHash = await sendNFTsToAPIWallet(
classIdInput.value as string,
autoDeliverCount,
signer.value,
wallet.value
)
}
await newBookListing(classIdInput.value as string, {
defaultPaymentCurrency,
connectedWallets,
Expand All @@ -720,7 +760,8 @@ async function submitNewClass () {
prices: p,
shippingRates: s,
mustClaimToView,
hideDownload
hideDownload,
autoDeliverNFTsTxHash
})
router.push({ name: 'nft-book-store' })
} catch (err) {
Expand Down
8 changes: 4 additions & 4 deletions pages/nft-book-store/send/[classId].vue
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ import { useBookStoreApiStore } from '~/stores/book-store-api'
import { useWalletStore } from '~/stores/wallet'
import { useNftStore } from '~/stores/nft'
import { parseImageURLFromMetadata } from '~/utils'
import { signExecNFTSendAuthz, signSendNFT } from '~/utils/cosmos'
import { signExecNFTSendAuthz, signSendNFTs } from '~/utils/cosmos'
const store = useWalletStore()
const { wallet, signer } = storeToRefs(store)
Expand Down Expand Up @@ -304,10 +304,10 @@ async function onSendNFTStart () {
let res: DeliverTxResponse | undefined
if (userIsOwner.value) {
res = await signSendNFT(
res = await signSendNFTs(
orderInfo.value.wallet,
classId.value,
targetNftId,
[classId.value],
[targetNftId],
signer.value,
wallet.value,
memo.value
Expand Down
24 changes: 23 additions & 1 deletion pages/nft-book-store/status/[classId]/edit/[editionIndex].vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,21 @@
</UFormGroup>

<UFormGroup :label="`Total number of NFT for sale of this ${priceItemLabel}`">
<UInput v-model="stock" type="number" step="0.01" :min="0" />
<UInput v-model="stock" type="number" step="1" :min="0" :disabled="isAutoDeliver" />
</UFormGroup>

<URadioGroup
v-model="deliverMethod"
disabled
:legend="`Deliver method of this ${priceItemLabel}`"
:options="deliverMethodOptions"
/>

<UFormGroup
v-if="isAutoDeliver"
:label="`Memo of this ${priceItemLabel}`"
>
<UInput v-model="autoMemo" placeholder="Thank you! 謝謝你的支持!" />
</UFormGroup>

<UFormGroup
Expand Down Expand Up @@ -191,6 +205,7 @@ import { LIKE_CO_API } from '~/constant'
import { useBookStoreApiStore } from '~/stores/book-store-api'
import { useWalletStore } from '~/stores/wallet'
import { deliverMethodOptions } from '~/utils'
const walletStore = useWalletStore()
const bookStoreApiStore = useBookStoreApiStore()
Expand All @@ -213,6 +228,8 @@ const hasMultiplePrices = computed(() => classData?.value?.prices?.length > 1)
const price = ref(MINIMAL_PRICE)
const stock = ref(1)
const deliverMethod = ref('auto')
const autoMemo = ref('')
const nameEn = ref('Standard Edition')
const nameZh = ref('標準版')
const descriptionEn = ref('')
Expand All @@ -226,6 +243,7 @@ const shippingRates = ref<any[]>([{
const hasMultipleShippingRates = computed(() => shippingRates.value.length > 1)
const priceItemLabel = computed(() => hasMultiplePrices.value ? 'edition' : 'book')
const isAutoDeliver = computed(() => deliverMethod.value === 'auto')
const toolbarOptions = ref<string[]>([
'bold',
Expand Down Expand Up @@ -276,6 +294,8 @@ onMounted(async () => {
if (currentEdition) {
price.value = currentEdition.price || 0
stock.value = currentEdition.stock || 0
deliverMethod.value = currentEdition.isAutoDeliver ? 'auto' : 'manual'
autoMemo.value = currentEdition.autoMemo || ''
nameEn.value = currentEdition.name?.en || currentEdition.name || ''
nameZh.value = currentEdition.name?.zh || currentEdition.name || ''
const legacyDescription = typeof currentEdition.description === 'string' ? currentEdition.description : undefined
Expand Down Expand Up @@ -351,6 +371,8 @@ async function handleSubmit () {
priceInDecimal: Math.round(Number(price.value) * 100),
price: Number(price.value),
stock: Number(stock.value),
isAutoDeliver: isAutoDeliver.value,
autoMemo: autoMemo.value || '',
hasShipping: hasShipping.value || false
}
Expand Down
43 changes: 40 additions & 3 deletions pages/nft-book-store/status/[classId]/edit/new.vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,20 @@
</UFormGroup>

<UFormGroup :label="`Total number of NFT for sale of this ${priceItemLabel}`">
<UInput v-model="stock" type="number" step="0.01" :min="0" />
<UInput v-model="stock" type="number" step="1" :min="0" />
</UFormGroup>

<URadioGroup
v-model="deliverMethod"
:legend="`Deliver method of this ${priceItemLabel}`"
:options="deliverMethodOptions"
/>

<UFormGroup
v-if="deliverMethod === 'auto'"
:label="`Memo of this ${priceItemLabel}`"
>
<UInput v-model="autoMemo" placeholder="Thank you! 謝謝你的支持!" />
</UFormGroup>

<UFormGroup
Expand Down Expand Up @@ -191,10 +204,13 @@ import { LIKE_CO_API } from '~/constant'
import { useBookStoreApiStore } from '~/stores/book-store-api'
import { useWalletStore } from '~/stores/wallet'
import { deliverMethodOptions } from '~/utils'
import { sendNFTsToAPIWallet } from '~/utils/cosmos'
const walletStore = useWalletStore()
const bookStoreApiStore = useBookStoreApiStore()
const { wallet } = storeToRefs(walletStore)
const { connect } = walletStore
const { wallet, signer } = storeToRefs(walletStore)
const { token } = storeToRefs(bookStoreApiStore)
const router = useRouter()
Expand All @@ -213,6 +229,8 @@ const hasMultiplePrices = computed(() => classData?.value?.prices?.length > 1)
const price = ref(MINIMAL_PRICE)
const stock = ref(1)
const deliverMethod = ref('auto')
const autoMemo = ref('')
const nameEn = ref('Standard Edition')
const nameZh = ref('標準版')
const descriptionEn = ref('')
Expand Down Expand Up @@ -338,6 +356,8 @@ async function handleSubmit () {
priceInDecimal: Math.round(Number(price.value) * 100),
price: Number(price.value),
stock: Number(stock.value),
isAutoDeliver: deliverMethod.value === 'auto',
autoMemo: deliverMethod.value === 'auto' ? (autoMemo.value || '') : '',
hasShipping: hasShipping.value || false
}
Expand All @@ -362,8 +382,25 @@ async function handleSubmit () {
isLoading.value = true
let autoDeliverNFTsTxHash = ''
if (editedPrice.isAutoDeliver && editedPrice.stock > 0) {
if (!wallet.value || !signer.value) {
await connect()
}
if (!wallet.value || !signer.value) {
throw new Error('Unable to connect to wallet')
}
autoDeliverNFTsTxHash = await sendNFTsToAPIWallet(
classId.value as string,
editedPrice.stock,
signer.value,
wallet.value
)
}
await bookStoreApiStore.addEditionPrice(classId.value as string, priceIndex.value, {
price: editedPrice
price: editedPrice,
autoDeliverNFTsTxHash
})
router.push({
Expand Down
61 changes: 40 additions & 21 deletions utils/cosmos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { parseAuthzGrant } from '@likecoin/iscn-js/dist/messages/parsing'
import { GenericAuthorization } from 'cosmjs-types/cosmos/authz/v1beta1/authz'
import { formatMsgSend } from '@likecoin/iscn-js/dist/messages/likenft'
import { addParamToUrl } from '.'
import { RPC_URL, LIKER_NFT_FEE_WALLET } from '~/constant'
import { RPC_URL, LIKER_NFT_FEE_WALLET, LIKER_NFT_TARGET_ADDRESS } from '~/constant'
import network from '~/constant/network'

const DEFAULT_GAS_AMOUNT = 200000
Expand Down Expand Up @@ -233,26 +233,6 @@ export async function signMintNFT (
return res
}

export async function signSendNFT (
targetAddress: string,
classId: string,
nftId: string,
signer: OfflineSigner,
address: string,
memo?: string
) {
const signingClient = await getSigningClient()
await signingClient.connectWithSigner(RPC_URL, signer)
const res = await signingClient.sendNFTs(
address,
targetAddress,
classId,
[nftId],
{ memo }
) as DeliverTxResponse
return res
}

export async function signSendNFTs (
targetAddress: string,
classIds: string[],
Expand Down Expand Up @@ -342,3 +322,42 @@ export function shortenWalletAddress (address: string) {
if (!address) { return '-' }
return `${address.slice(0, 10)}...${address.slice(-6)}`
}

export async function sendNFTsToAPIWallet (
classId: string,
nftCount: number,
signer: OfflineSigner,
ownerAddress: string
) {
if (nftCount <= 0) { return '' }

if (!ownerAddress) {
throw new Error('Missing owner address')
}

if (!signer) {
throw new Error('Missing signer')
}

const { nfts } = await getNFTs({
classId,
owner: ownerAddress,
needCount: nftCount
})
const nftIds = nfts.map(nft => nft.id).slice(0, nftCount)
const classIds = nftIds.map(_ => classId)

const { transactionHash, code } = await signSendNFTs(
LIKER_NFT_TARGET_ADDRESS,
classIds,
nftIds,
signer,
ownerAddress,
'Send auto delivering NFT Book to API wallet'
)

if (!transactionHash || code !== 0) {
throw new Error('Failed to sign and send NFTs')
}
return transactionHash
}
11 changes: 11 additions & 0 deletions utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,14 @@ function convertArrayOfObjectsToCSV (data: Record<string, any>[]): string {
export function getPortfolioURL (wallet: string) {
return `https://${LIKER_LAND_HOST}/${wallet}`
}

export const deliverMethodOptions = [
{
value: 'auto',
label: 'Automatic deliver NFT'
},
{
value: 'manual',
label: 'Sign memo and manually deliver each NFT'
}
]

0 comments on commit b2c438d

Please sign in to comment.