From a5e12bc5053eecdc3b3de6a8fea4e0ba0d226b40 Mon Sep 17 00:00:00 2001 From: Philip Diaz Date: Mon, 10 May 2021 13:53:26 -0400 Subject: [PATCH 1/2] wip: Add 'mintType' to AssetContract decoder/query --- src/lib/nfts/decoders.ts | 39 ++++++++++++++++++++++++++++++++ src/lib/nfts/queries.ts | 49 +++++++++++++++++++++++++++++++++++----- src/lib/service/tzkt.ts | 33 +++++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 6 deletions(-) diff --git a/src/lib/nfts/decoders.ts b/src/lib/nfts/decoders.ts index 58e9e7f2..53d7d1fb 100644 --- a/src/lib/nfts/decoders.ts +++ b/src/lib/nfts/decoders.ts @@ -26,6 +26,42 @@ export const ContractRow = (storage: S) => storage: storage }); +const MintEntrypointBaseSchema = t.type({ + 'token_metadata:object': t.type({ + 'token_id:nat': t.literal('nat'), + 'token_info:map_flat:string:bytes': t.type({ + string: t.literal('bytes') + }) + }), + 'owner:address': t.literal('address') +}); + +const MintEntrypointEditionSchema = t.intersection([ + MintEntrypointBaseSchema, + t.type({ + 'amount:nat': t.literal('nat') + }) +]); + +const MintEntrypointGeneric = (schema: S) => + t.type({ + name: t.literal('mint'), + jsonParameters: t.type({ 'schema:list:object': t.array(schema) }), + unused: t.literal(false) + }); + +export const MintEntrypointBase = MintEntrypointGeneric( + MintEntrypointBaseSchema +); +export const MintEntrypointEdition = MintEntrypointGeneric( + MintEntrypointEditionSchema +); + +export const MintEntrypoint = t.union([ + MintEntrypointEdition, + MintEntrypointBase +]); + // Generic BigMaps export const BigMapRow = (props: { @@ -195,5 +231,8 @@ export const AssetContract = t.intersection([ ContractRow(t.unknown), t.type({ metadata: AssetContractMetadata + }), + t.partial({ + mintType: t.union([t.literal('basic'), t.literal('editions')]) }) ]); diff --git a/src/lib/nfts/queries.ts b/src/lib/nfts/queries.ts index a34ed203..9899c558 100644 --- a/src/lib/nfts/queries.ts +++ b/src/lib/nfts/queries.ts @@ -81,7 +81,9 @@ async function getFixedPriceSalesBigMap( if (isLeft(t.number.decode(fixedPriceBigMapId))) { throw Error('Failed to decode `getFixedPriceSales` bigMap ID'); } - const fixedPriceSales = transformFixedPriceSales(await tzkt.getBigMapKeys(fixedPriceBigMapId)); + const fixedPriceSales = transformFixedPriceSales( + await tzkt.getBigMapKeys(fixedPriceBigMapId) + ); const decoded = D.FixedPriceSaleBigMap.decode(fixedPriceSales); if (isLeft(decoded)) { throw Error('Failed to decode `getFixedPriceSales` response'); @@ -131,6 +133,36 @@ async function getContract( return decoded.right; } +async function getContractEntrypoint( + tzkt: TzKt, + address: string, + entrypoint: string, + params: Params, + decoder: S +) { + const contract = await tzkt.getContractEntrypoint( + address, + entrypoint, + params + ); + const decoded = decoder.decode(contract); + if (isLeft(decoded)) { + throw Error('Failed to decode `getContracts` response'); + } + return decoded.right; +} + +async function getMintEntrypointType(tzkt: TzKt, address: string) { + const mintEntrypoint = await getContractEntrypoint( + tzkt, + address, + 'mint', + {}, + D.MintEntrypoint + ); + return D.MintEntrypointEdition.is(mintEntrypoint) ? 'editions' : 'basic'; +} + //// Main query functions export async function getContractNfts( @@ -189,6 +221,8 @@ export async function getNftAssetContract( address: string ): Promise { const contract = await getContract(system.tzkt, address, {}, t.unknown); + + const mintType = await getMintEntrypointType(system.tzkt, contract.address); const metaBigMap = await getAssetMetadataBigMap(system.tzkt, address); const metaUri = metaBigMap.find(v => v.key === '')?.value; if (!metaUri) { @@ -204,7 +238,7 @@ export async function getNftAssetContract( if (isLeft(decoded)) { throw Error('Metadata validation failed'); } - return { ...contract, metadata: decoded.right }; + return { ...contract, metadata: decoded.right, mintType }; } export async function getWalletNftAssetContracts( @@ -252,6 +286,9 @@ export async function getWalletNftAssetContracts( if (!contract) { continue; } + + const mintType = await getMintEntrypointType(system.tzkt, contract.address); + try { const metaUri = row.content.value; const { metadata } = await system.resolveMetadata( @@ -260,7 +297,7 @@ export async function getWalletNftAssetContracts( ); const decoded = D.AssetContractMetadata.decode(metadata); if (!isLeft(decoded)) { - results.push({ ...contract, metadata: decoded.right }); + results.push({ ...contract, metadata: decoded.right, mintType }); } } catch (e) { console.log(e); @@ -333,10 +370,10 @@ export async function getMarketplaceNfts( return salesWithTokenMetadata; } -export const loadMarketplaceNft = async ( +export async function loadMarketplaceNft( system: SystemWithToolkit | SystemWithWallet, tokenLoadData: MarketplaceNftLoadingData -): Promise => { +): Promise { const { token, loaded, tokenSale, tokenMetadata } = tokenLoadData; const result = { ...tokenLoadData }; @@ -389,4 +426,4 @@ export const loadMarketplaceNft = async ( console.error("Couldn't load token", { tokenSale, err }); return result; } -}; +} diff --git a/src/lib/service/tzkt.ts b/src/lib/service/tzkt.ts index 55e1f699..e23a3eb9 100644 --- a/src/lib/service/tzkt.ts +++ b/src/lib/service/tzkt.ts @@ -44,6 +44,31 @@ export async function getContract( return response.data; } +export async function getContractEntrypoints( + config: Config, + address: string, + params?: Params +) { + const uri = `${ + config.tzkt.api + }/v1/contracts/${address}/entrypoints?${mkQueryParams(params)}`; + const response = await axios.get(uri); + return response.data; +} + +export async function getContractEntrypoint( + config: Config, + address: string, + entrypoint: string, + params?: Params +) { + const uri = `${ + config.tzkt.api + }/v1/contracts/${address}/entrypoints/${entrypoint}?${mkQueryParams(params)}`; + const response = await axios.get(uri); + return response.data; +} + export async function getContractBigMapKeys( config: Config, address: string, @@ -92,6 +117,14 @@ export class TzKt { return getContract(this.config, address, params); } + getContractEntrypoints(address: string, params?: Params) { + return getContractEntrypoints(this.config, address, params); + } + + getContractEntrypoint(address: string, entrypoint: string, params?: Params) { + return getContractEntrypoint(this.config, address, entrypoint, params); + } + getContractBigMapKeys(address: string, name: string, params?: Params) { return getContractBigMapKeys(this.config, address, name, params); } From 4087ac84060b03c397409893b1e458bfe2af8d54 Mon Sep 17 00:00:00 2001 From: Philip Diaz Date: Mon, 10 May 2021 14:44:30 -0400 Subject: [PATCH 2/2] fix: Handle divergent mint entrypoint schemas --- src/lib/nfts/queries.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/lib/nfts/queries.ts b/src/lib/nfts/queries.ts index 9899c558..7aacf342 100644 --- a/src/lib/nfts/queries.ts +++ b/src/lib/nfts/queries.ts @@ -128,7 +128,7 @@ async function getContract( const contract = await tzkt.getContract(address, params); const decoded = D.ContractRow(storage).decode(contract); if (isLeft(decoded)) { - throw Error('Failed to decode `getContracts` response'); + throw Error('Failed to decode `getContract` response'); } return decoded.right; } @@ -147,7 +147,7 @@ async function getContractEntrypoint( ); const decoded = decoder.decode(contract); if (isLeft(decoded)) { - throw Error('Failed to decode `getContracts` response'); + throw Error('Failed to decode `getContractEntrypoint` response'); } return decoded.right; } @@ -287,9 +287,11 @@ export async function getWalletNftAssetContracts( continue; } - const mintType = await getMintEntrypointType(system.tzkt, contract.address); - try { + const mintType: 'editions' | 'basic' = await getMintEntrypointType( + system.tzkt, + contract.address + ).catch(() => 'basic'); const metaUri = row.content.value; const { metadata } = await system.resolveMetadata( fromHexString(metaUri),