-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2594 from build-5/master
Merge latest
- Loading branch information
Showing
8 changed files
with
245 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,7 +28,9 @@ | |
"joi": "^17.10.1", | ||
"lodash": "^4.17.21", | ||
"rxjs": "^7.8.1", | ||
"ws": "^8.13.0" | ||
"ws": "^8.13.0", | ||
"@iota/iota.js-next": "npm:@iota/[email protected]", | ||
"@iota/util.js-next": "npm:@iota/[email protected]" | ||
}, | ||
"devDependencies": { | ||
"@types/express": "^4.17.17", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { GetNftIds, Network, WenError } from '@build-5/interfaces'; | ||
import { | ||
Bech32Helper, | ||
INftOutput, | ||
IndexerPluginClient, | ||
NFT_ADDRESS_TYPE, | ||
TransactionHelper, | ||
} from '@iota/iota.js-next'; | ||
import { Converter, HexHelper } from '@iota/util.js-next'; | ||
import Joi from 'joi'; | ||
import { of } from 'rxjs'; | ||
import { CommonJoi, getQueryParams } from '../common'; | ||
import { EMPTY_NFT_ID, getShimmerClient } from './wallet'; | ||
|
||
const getNftIdsSchema = Joi.object({ | ||
network: Joi.string().valid(Network.SMR, Network.RMS), | ||
collectionId: CommonJoi.uid(), | ||
}); | ||
|
||
export const getNftIds = async (url: string) => { | ||
const body = getQueryParams<GetNftIds>(url, getNftIdsSchema); | ||
try { | ||
const client = await getShimmerClient(body.network); | ||
const indexer = new IndexerPluginClient(client); | ||
|
||
const collectionOutputId = (await indexer.nft(body.collectionId)).items[0]; | ||
const { nftId: collectionId } = (await client.output(collectionOutputId)).output as INftOutput; | ||
const issuerAddress = Bech32Helper.toBech32( | ||
NFT_ADDRESS_TYPE, | ||
Converter.hexToBytes(HexHelper.stripPrefix(collectionId)), | ||
body.network || Network.SMR, | ||
); | ||
|
||
const nftOutputIds = (await indexer.nfts({ issuerBech32: issuerAddress })).items; | ||
const promises = nftOutputIds.map(async (outputId) => { | ||
const output = (await client.output(outputId)).output as INftOutput; | ||
if (output.nftId === EMPTY_NFT_ID) { | ||
return TransactionHelper.resolveIdFromOutputId(outputId); | ||
} | ||
return output.nftId; | ||
}); | ||
return of(await Promise.all(promises)); | ||
} catch (error) { | ||
throw { code: 400, message: WenError.invalid_collection_id.key }; | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { GetNftMutableData, Network, WenError } from '@build-5/interfaces'; | ||
import { INftOutput, IndexerPluginClient } from '@iota/iota.js-next'; | ||
import Joi from 'joi'; | ||
import { of } from 'rxjs'; | ||
import { CommonJoi, getQueryParams } from '../common'; | ||
import { getMutableMetadata, getShimmerClient } from './wallet'; | ||
|
||
const getNftMutableDataSchema = Joi.object({ | ||
network: Joi.string().valid(Network.SMR, Network.RMS), | ||
nftId: CommonJoi.uid(), | ||
}); | ||
|
||
export const getNftMutableMetadata = async (url: string) => { | ||
const body = getQueryParams<GetNftMutableData>(url, getNftMutableDataSchema); | ||
try { | ||
const client = await getShimmerClient(body.network); | ||
const indexer = new IndexerPluginClient(client); | ||
const outputId = (await indexer.nft(body.nftId)).items[0]; | ||
const output = (await client.output(outputId)).output as INftOutput; | ||
return of(getMutableMetadata(output)); | ||
} catch { | ||
throw { code: 400, message: WenError.invalid_nft_id.key }; | ||
} | ||
}; |
75 changes: 75 additions & 0 deletions
75
packages/api/src/metadataNft/getNftMutableMetadataHistory.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import { GetNftMutableMetadatHistory, Network, WenError } from '@build-5/interfaces'; | ||
import { | ||
INftOutput, | ||
IOutputResponse, | ||
ITransactionPayload, | ||
IndexerPluginClient, | ||
NFT_OUTPUT_TYPE, | ||
SingleNodeClient, | ||
TransactionHelper, | ||
} from '@iota/iota.js-next'; | ||
import Joi from 'joi'; | ||
import { isEqual, last } from 'lodash'; | ||
import { of } from 'rxjs'; | ||
import { CommonJoi, getQueryParams } from '../common'; | ||
import { EMPTY_NFT_ID, getMutableMetadata, getShimmerClient } from './wallet'; | ||
|
||
const getNftMutableMetadataHistorySchema = Joi.object({ | ||
network: Joi.string().valid(Network.SMR, Network.RMS), | ||
nftId: CommonJoi.uid(), | ||
}); | ||
|
||
export const getNftMutableMetadataHistory = async (url: string) => { | ||
const body = getQueryParams<GetNftMutableMetadatHistory>(url, getNftMutableMetadataHistorySchema); | ||
const history: any[] = []; | ||
try { | ||
const client = await getShimmerClient(body.network); | ||
const indexer = new IndexerPluginClient(client); | ||
const outputId = (await indexer.nft(body.nftId)).items[0]; | ||
let outputResponse: IOutputResponse | undefined = await client.output(outputId); | ||
do { | ||
const metadata = getMutableMetadata(outputResponse.output as INftOutput); | ||
if (!isEqual(metadata, last(history))) { | ||
history.push(metadata); | ||
} | ||
outputResponse = await getPrevNftOutput(client, outputResponse); | ||
} while (outputResponse !== undefined); | ||
|
||
history.reverse(); | ||
const response = history.reduce((acc, act, i) => ({ ...acc, [i]: act }), {}); | ||
return of(response); | ||
} catch { | ||
throw { code: 400, message: WenError.invalid_nft_id.key }; | ||
} | ||
}; | ||
|
||
const getPrevNftOutput = async (client: SingleNodeClient, output: IOutputResponse) => { | ||
if ((output.output as INftOutput).nftId === EMPTY_NFT_ID) { | ||
return undefined; | ||
} | ||
const block = await client.block(output.metadata.blockId); | ||
const inputs = (block.payload as ITransactionPayload).essence.inputs; | ||
const prevOutputIds = inputs.map(({ transactionId, transactionOutputIndex }) => | ||
TransactionHelper.outputIdFromTransactionData(transactionId, transactionOutputIndex), | ||
); | ||
for (const prevOutputId of prevOutputIds) { | ||
const prevOutputResponse = await client.output(prevOutputId); | ||
const prevOutput = prevOutputResponse.output; | ||
if (prevOutput.type !== NFT_OUTPUT_TYPE) { | ||
continue; | ||
} | ||
const prevNftId = getNftId(prevOutputId, prevOutput); | ||
if (prevNftId === (output.output as INftOutput).nftId) { | ||
return prevOutputResponse; | ||
} | ||
} | ||
|
||
return undefined; | ||
}; | ||
|
||
const getNftId = (outputId: string, output: INftOutput) => { | ||
if (output.nftId === EMPTY_NFT_ID) { | ||
return TransactionHelper.resolveIdFromOutputId(outputId); | ||
} | ||
return output.nftId; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import { Network } from '@build-5/interfaces'; | ||
import { | ||
IMetadataFeature, | ||
INftOutput, | ||
METADATA_FEATURE_TYPE, | ||
SingleNodeClient, | ||
} from '@iota/iota.js-next'; | ||
import { Converter } from '@iota/util.js-next'; | ||
|
||
const RMS_API_ENDPOINTS = ['https://rms1.svrs.io/']; | ||
|
||
const SMR_API_ENDPOINTS = ['https://smr1.svrs.io/', 'https://smr3.svrs.io/']; | ||
|
||
export const getShimmerClient = async (network = Network.SMR) => { | ||
let url = ''; | ||
for (let i = 0; i < 5; ++i) { | ||
url = getEndpointUrl(network); | ||
try { | ||
const client = new SingleNodeClient(url); | ||
const healty = await client.health(); | ||
if (healty) { | ||
return client; | ||
} | ||
} catch (error) { | ||
console.warn(`Could not connect to client ${network}`, url, error); | ||
} | ||
await new Promise((resolve) => setTimeout(resolve, Math.floor(Math.random() * 1000 + 500))); | ||
} | ||
console.error(`Could not connect to client ${network}`, url); | ||
throw Error(`Could not connect to any client ${network}`); | ||
}; | ||
|
||
const getEndpointUrl = (network: Network) => { | ||
const urls = network === Network.SMR ? SMR_API_ENDPOINTS : RMS_API_ENDPOINTS; | ||
return getRandomElement(urls); | ||
}; | ||
|
||
const getRandomElement = <T>(array: T[]) => array[Math.floor(Math.random() * array.length)]; | ||
|
||
export const EMPTY_NFT_ID = '0x0000000000000000000000000000000000000000000000000000000000000000'; | ||
|
||
export const getMutableMetadata = (output: INftOutput) => { | ||
const hexMetadata = <IMetadataFeature | undefined>( | ||
output?.features?.find((f) => f.type === METADATA_FEATURE_TYPE) | ||
); | ||
if (!hexMetadata?.data) { | ||
return {}; | ||
} | ||
const mutableMetadata = JSON.parse(Converter.hexToUtf8(hexMetadata.data) || '{}'); | ||
return mutableMetadata; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters