@cityofzion/bs-neo3 0.9.3 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/BSNeo3.d.ts +8 -4
  2. package/dist/BSNeo3.js +38 -13
  3. package/dist/DoraBDSNeo3.d.ts +0 -1
  4. package/dist/DoraBDSNeo3.js +11 -12
  5. package/dist/DoraESNeo3.d.ts +1 -1
  6. package/dist/DoraESNeo3.js +19 -5
  7. package/dist/FlamingoEDSNeo3.d.ts +1 -2
  8. package/dist/FlamingoEDSNeo3.js +20 -5
  9. package/dist/GhostMarketNDSNeo3.d.ts +1 -1
  10. package/dist/GhostMarketNDSNeo3.js +17 -3
  11. package/dist/LedgerServiceNeo3.d.ts +14 -0
  12. package/dist/LedgerServiceNeo3.js +96 -0
  13. package/dist/RpcBDSNeo3.d.ts +4 -4
  14. package/dist/RpcBDSNeo3.js +23 -19
  15. package/package.json +12 -9
  16. package/.eslintignore +0 -13
  17. package/.eslintrc.cjs +0 -22
  18. package/.rush/temp/operation/build/all.log +0 -1
  19. package/.rush/temp/operation/build/state.json +0 -3
  20. package/.rush/temp/package-deps_build.json +0 -31
  21. package/.rush/temp/shrinkwrap-deps.json +0 -439
  22. package/CHANGELOG.json +0 -66
  23. package/CHANGELOG.md +0 -33
  24. package/bs-neo3.build.log +0 -1
  25. package/jest.config.ts +0 -13
  26. package/jest.setup.ts +0 -1
  27. package/src/BSNeo3.ts +0 -232
  28. package/src/DoraBDSNeo3.ts +0 -188
  29. package/src/DoraESNeo3.ts +0 -19
  30. package/src/FlamingoEDSNeo3.ts +0 -41
  31. package/src/GhostMarketNDSNeo3.ts +0 -121
  32. package/src/RpcBDSNeo3.ts +0 -159
  33. package/src/__tests__/BDSNeo3.spec.ts +0 -124
  34. package/src/__tests__/BSNeo3.spec.ts +0 -149
  35. package/src/__tests__/DoraESNeo3.spec.ts +0 -23
  36. package/src/__tests__/FlamingoEDSNeo3.spec.ts +0 -48
  37. package/src/__tests__/GhostMarketNDSNeo3.spec.ts +0 -48
  38. package/src/__tests__/utils/sleep.ts +0 -1
  39. package/src/assets/tokens/common.json +0 -14
  40. package/src/assets/tokens/mainnet.json +0 -116
  41. package/src/constants.ts +0 -29
  42. package/src/index.ts +0 -6
  43. package/tsconfig.build.json +0 -4
  44. package/tsconfig.json +0 -14
package/src/BSNeo3.ts DELETED
@@ -1,232 +0,0 @@
1
- import {
2
- BlockchainDataService,
3
- BlockchainService,
4
- BSClaimable,
5
- Account,
6
- ExchangeDataService,
7
- BDSClaimable,
8
- Token,
9
- BSWithNameService,
10
- Network,
11
- PartialBy,
12
- TransferParam,
13
- BSCalculableFee,
14
- NftDataService,
15
- BSWithNft,
16
- AccountWithDerivationPath,
17
- BSWithExplorerService,
18
- ExplorerService,
19
- } from '@cityofzion/blockchain-service'
20
- import { api, u, wallet } from '@cityofzion/neon-js'
21
- import Neon from '@cityofzion/neon-core'
22
- import { NeonInvoker } from '@cityofzion/neon-invoker'
23
- import { NeonParser } from '@cityofzion/neon-parser'
24
- import { ABI_TYPES } from '@cityofzion/neo3-parser'
25
- import { ContractInvocation } from '@cityofzion/neo3-invoker'
26
-
27
- import { RPCBDSNeo3 } from './RpcBDSNeo3'
28
- import { DoraBDSNeo3 } from './DoraBDSNeo3'
29
- import { DEFAULT_URL_BY_NETWORK_TYPE, DERIVATION_PATH, NEO_NS_HASH, TOKENS } from './constants'
30
- import { FlamingoEDSNeo3 } from './FlamingoEDSNeo3'
31
- import { GhostMarketNDSNeo3 } from './GhostMarketNDSNeo3'
32
- import { keychain } from '@cityofzion/bs-asteroid-sdk'
33
- import { DoraESNeo3 } from './DoraESNeo3'
34
-
35
- export class BSNeo3<BSCustomName extends string = string>
36
- implements BlockchainService, BSClaimable, BSWithNameService, BSCalculableFee, BSWithNft, BSWithExplorerService
37
- {
38
- readonly blockchainName: BSCustomName
39
- readonly feeToken: Token
40
- readonly claimToken: Token
41
- readonly burnToken: Token
42
- readonly derivationPath: string
43
-
44
- blockchainDataService!: BlockchainDataService & BDSClaimable
45
- nftDataService!: NftDataService
46
- exchangeDataService!: ExchangeDataService
47
- explorerService!: ExplorerService
48
- tokens: Token[]
49
- network!: Network
50
-
51
- constructor(blockchainName: BSCustomName, network: PartialBy<Network, 'url'>) {
52
- this.blockchainName = blockchainName
53
- this.tokens = TOKENS[network.type]
54
-
55
- this.derivationPath = DERIVATION_PATH
56
- this.feeToken = this.tokens.find(token => token.symbol === 'GAS')!
57
- this.burnToken = this.tokens.find(token => token.symbol === 'NEO')!
58
- this.claimToken = this.tokens.find(token => token.symbol === 'GAS')!
59
- this.setNetwork(network)
60
- }
61
-
62
- setNetwork(param: PartialBy<Network, 'url'>) {
63
- const network = {
64
- type: param.type,
65
- url: param.url ?? DEFAULT_URL_BY_NETWORK_TYPE[param.type],
66
- }
67
- this.network = network
68
-
69
- if (network.type === 'custom') {
70
- this.blockchainDataService = new RPCBDSNeo3(network, this.feeToken, this.claimToken)
71
- } else {
72
- this.blockchainDataService = new DoraBDSNeo3(network, this.feeToken, this.claimToken)
73
- }
74
-
75
- this.exchangeDataService = new FlamingoEDSNeo3(network.type)
76
- this.nftDataService = new GhostMarketNDSNeo3(network.type)
77
- this.explorerService = new DoraESNeo3(network.type)
78
- }
79
-
80
- validateAddress(address: string): boolean {
81
- return wallet.isAddress(address, 53)
82
- }
83
-
84
- validateEncrypted(encryptedKey: string): boolean {
85
- return wallet.isNEP2(encryptedKey)
86
- }
87
-
88
- validateKey(key: string): boolean {
89
- return wallet.isWIF(key) || wallet.isPrivateKey(key)
90
- }
91
-
92
- validateNameServiceDomainFormat(domainName: string): boolean {
93
- if (!domainName.endsWith('.neo')) return false
94
- return true
95
- }
96
-
97
- generateAccountFromMnemonic(mnemonic: string[] | string, index: number): AccountWithDerivationPath {
98
- keychain.importMnemonic(Array.isArray(mnemonic) ? mnemonic.join(' ') : mnemonic)
99
- const path = this.derivationPath.replace('?', index.toString())
100
- const childKey = keychain.generateChildKey('neo', path)
101
- const key = childKey.getWIF()
102
- const { address } = new wallet.Account(key)
103
- return { address, key, type: 'wif', derivationPath: path }
104
- }
105
-
106
- generateAccountFromKey(key: string): Account {
107
- const type = wallet.isWIF(key) ? 'wif' : wallet.isPrivateKey(key) ? 'privateKey' : undefined
108
- if (!type) throw new Error('Invalid key')
109
-
110
- const { address } = new wallet.Account(key)
111
- return { address, key, type }
112
- }
113
-
114
- async decrypt(encryptedKey: string, password: string): Promise<Account> {
115
- let BsReactNativeDecrypt: any
116
-
117
- try {
118
- const { NativeModules } = require('react-native')
119
- BsReactNativeDecrypt = NativeModules.BsReactNativeDecrypt
120
-
121
- if (!BsReactNativeDecrypt) {
122
- throw new Error('@CityOfZion/bs-react-native-decrypt is not installed')
123
- }
124
- } catch {
125
- const key = await wallet.decrypt(encryptedKey, password)
126
- return this.generateAccountFromKey(key)
127
- }
128
-
129
- const privateKey = await BsReactNativeDecrypt.decryptNeo3(encryptedKey, password)
130
- return this.generateAccountFromKey(privateKey)
131
- }
132
-
133
- encrypt(key: string, password: string): Promise<string> {
134
- return wallet.encrypt(key, password)
135
- }
136
-
137
- async calculateTransferFee(param: TransferParam): Promise<string> {
138
- const account = new wallet.Account(param.senderAccount.key)
139
- const invoker = await NeonInvoker.init({
140
- rpcAddress: this.network.url,
141
- account,
142
- })
143
-
144
- const invocations = this.buildTransferInvocation(param, account)
145
-
146
- const { networkFee, systemFee } = await invoker.calculateFee({
147
- invocations,
148
- signers: [],
149
- })
150
-
151
- return networkFee.add(systemFee).toDecimal(this.feeToken.decimals)
152
- }
153
-
154
- async transfer(param: TransferParam): Promise<string> {
155
- const account = new wallet.Account(param.senderAccount.key)
156
- const invoker = await NeonInvoker.init({
157
- rpcAddress: this.network.url,
158
- account,
159
- })
160
-
161
- const invocations = this.buildTransferInvocation(param, account)
162
-
163
- const transactionHash = await invoker.invokeFunction({
164
- invocations,
165
- signers: [],
166
- })
167
-
168
- return transactionHash
169
- }
170
-
171
- async claim(account: Account): Promise<string> {
172
- const neoAccount = new wallet.Account(account.key)
173
- const facade = await api.NetworkFacade.fromConfig({ node: this.network.url })
174
-
175
- const transactionHash = await facade.claimGas(neoAccount, {
176
- signingCallback: api.signWithAccount(neoAccount),
177
- })
178
-
179
- return transactionHash
180
- }
181
-
182
- async resolveNameServiceDomain(domainName: string): Promise<any> {
183
- const parser = NeonParser
184
- const invoker = await NeonInvoker.init({ rpcAddress: this.network.url })
185
- const response = await invoker.testInvoke({
186
- invocations: [
187
- {
188
- scriptHash: NEO_NS_HASH,
189
- operation: 'ownerOf',
190
- args: [{ type: 'String', value: domainName }],
191
- },
192
- ],
193
- })
194
-
195
- if (response.stack.length === 0) {
196
- throw new Error(response.exception ?? 'unrecognized response')
197
- }
198
-
199
- const parsed = parser.parseRpcResponse(response.stack[0] as any, {
200
- type: ABI_TYPES.HASH160.name,
201
- })
202
- const address = parser.accountInputToAddress(parsed.replace('0x', ''))
203
- return address
204
- }
205
-
206
- private buildTransferInvocation(
207
- { intent, tipIntent }: TransferParam,
208
- account: Neon.wallet.Account
209
- ): ContractInvocation[] {
210
- const intents = [intent, ...(tipIntent ? [tipIntent] : [])]
211
-
212
- const invocations: ContractInvocation[] = intents.map(intent => {
213
- return {
214
- operation: 'transfer',
215
- scriptHash: intent.tokenHash,
216
- args: [
217
- { type: 'Hash160', value: account.address },
218
- { type: 'Hash160', value: intent.receiverAddress },
219
- {
220
- type: 'Integer',
221
- value: intent.tokenDecimals
222
- ? u.BigInteger.fromDecimal(intent.amount, intent.tokenDecimals).toString()
223
- : intent.amount,
224
- },
225
- { type: 'Any', value: '' },
226
- ],
227
- }
228
- })
229
-
230
- return invocations
231
- }
232
- }
@@ -1,188 +0,0 @@
1
- import {
2
- BalanceResponse,
3
- ContractResponse,
4
- TransactionsByAddressParams,
5
- TransactionsByAddressResponse,
6
- TransactionResponse,
7
- TransactionNotifications,
8
- Network,
9
- Token,
10
- TransactionTransferNft,
11
- TransactionTransferAsset,
12
- } from '@cityofzion/blockchain-service'
13
- import { wallet, u } from '@cityofzion/neon-js'
14
- import { NeoRESTApi } from '@cityofzion/dora-ts/dist/api'
15
- import { RPCBDSNeo3 } from './RpcBDSNeo3'
16
- import { TOKENS } from './constants'
17
-
18
- const NeoRest = new NeoRESTApi({
19
- doraUrl: 'https://dora.coz.io',
20
- endpoint: '/api/v2/neo3',
21
- })
22
-
23
- export class DoraBDSNeo3 extends RPCBDSNeo3 {
24
- readonly network: Network
25
-
26
- constructor(network: Network, feeToken: Token, claimToken: Token) {
27
- if (network.type === 'custom') {
28
- throw new Error('DoraBDSNeo3 does not support custom networks')
29
- }
30
-
31
- super(network, feeToken, claimToken)
32
- this.network = network
33
- }
34
-
35
- async getTransaction(hash: string): Promise<TransactionResponse> {
36
- try {
37
- const data = await NeoRest.transaction(hash, this.network.type)
38
- return {
39
- block: data.block,
40
- time: Number(data.time),
41
- hash: data.hash,
42
- fee: u.BigInteger.fromNumber(data.netfee ?? 0)
43
- .add(u.BigInteger.fromNumber(data.sysfee ?? 0))
44
- .toDecimal(this.feeToken.decimals),
45
- notifications: [],
46
- transfers: [],
47
- }
48
- } catch {
49
- throw new Error(`Transaction not found: ${hash}`)
50
- }
51
- }
52
-
53
- async getTransactionsByAddress({
54
- address,
55
- page = 1,
56
- }: TransactionsByAddressParams): Promise<TransactionsByAddressResponse> {
57
- const data = await NeoRest.addressTXFull(address, page, this.network.type)
58
-
59
- const promises = data.items.map(async (item): Promise<TransactionResponse> => {
60
- const transferPromises: Promise<TransactionTransferAsset | TransactionTransferNft>[] = []
61
-
62
- item.notifications.forEach(({ contract: contractHash, state, event_name: eventName }) => {
63
- const properties = Array.isArray(state) ? state : state.value
64
- if (eventName !== 'Transfer' || (properties.length !== 3 && properties.length !== 4)) return
65
-
66
- const promise = async (): Promise<TransactionTransferAsset | TransactionTransferNft> => {
67
- const isAsset = properties.length === 3
68
-
69
- const from = properties[0].value
70
- const to = properties[1].value
71
- const convertedFrom = from ? this.convertByteStringToAddress(from) : 'Mint'
72
- const convertedTo = to ? this.convertByteStringToAddress(to) : 'Burn'
73
-
74
- if (isAsset) {
75
- const token = await this.getTokenInfo(contractHash)
76
- const [, , { value: amount }] = properties
77
- return {
78
- amount: u.BigInteger.fromNumber(amount).toDecimal(token.decimals ?? 0),
79
- from: convertedFrom,
80
- to: convertedTo,
81
- contractHash,
82
- type: 'token',
83
- token,
84
- }
85
- }
86
-
87
- return {
88
- from: convertedFrom,
89
- to: convertedTo,
90
- tokenId: properties[3].value,
91
- contractHash,
92
- type: 'nft',
93
- }
94
- }
95
-
96
- transferPromises.push(promise())
97
- })
98
-
99
- const transfers = await Promise.all(transferPromises)
100
-
101
- const notifications = item.notifications.map<TransactionNotifications>(notification => ({
102
- eventName: notification.event_name,
103
- state: notification.state as any,
104
- }))
105
-
106
- return {
107
- block: item.block,
108
- time: Number(item.time),
109
- hash: item.hash,
110
- fee: u.BigInteger.fromNumber(item.netfee ?? 0)
111
- .add(u.BigInteger.fromNumber(item.sysfee ?? 0))
112
- .toDecimal(this.feeToken.decimals),
113
- transfers,
114
- notifications,
115
- }
116
- })
117
-
118
- const transactions = await Promise.all(promises)
119
-
120
- return {
121
- totalCount: data.totalCount,
122
- transactions,
123
- limit: 15,
124
- }
125
- }
126
-
127
- async getContract(contractHash: string): Promise<ContractResponse> {
128
- try {
129
- const data = await NeoRest.contract(contractHash, this.network.type)
130
- return {
131
- hash: data.hash,
132
- methods: data.manifest.abi?.methods ?? [],
133
- name: data.manifest.name,
134
- }
135
- } catch {
136
- throw new Error(`Contract not found: ${contractHash}`)
137
- }
138
- }
139
-
140
- async getTokenInfo(tokenHash: string): Promise<Token> {
141
- const localToken = TOKENS[this.network.type].find(token => token.hash === tokenHash)
142
- if (localToken) return localToken
143
-
144
- if (this.tokenCache.has(tokenHash)) {
145
- return this.tokenCache.get(tokenHash)!
146
- }
147
-
148
- try {
149
- const { decimals, symbol, name, scripthash } = await NeoRest.asset(tokenHash, this.network.type)
150
- const token = {
151
- decimals: Number(decimals),
152
- symbol,
153
- name,
154
- hash: scripthash,
155
- }
156
- this.tokenCache.set(tokenHash, token)
157
-
158
- return token
159
- } catch {
160
- throw new Error(`Token not found: ${tokenHash}`)
161
- }
162
- }
163
-
164
- async getBalance(address: string): Promise<BalanceResponse[]> {
165
- const response = await NeoRest.balance(address, this.network.type)
166
-
167
- const promises = response.map<Promise<BalanceResponse | undefined>>(async balance => {
168
- try {
169
- const token = await this.getTokenInfo(balance.asset)
170
- return {
171
- amount: balance.balance.toString(),
172
- token,
173
- }
174
- } catch {
175
- // Empty block
176
- }
177
- })
178
- const balances = await Promise.all(promises)
179
- const filteredBalances = balances.filter(balance => balance !== undefined) as BalanceResponse[]
180
- return filteredBalances
181
- }
182
-
183
- private convertByteStringToAddress(byteString: string): string {
184
- const account = new wallet.Account(u.reverseHex(u.HexString.fromBase64(byteString).toString()))
185
-
186
- return account.address
187
- }
188
- }
package/src/DoraESNeo3.ts DELETED
@@ -1,19 +0,0 @@
1
- import { ExplorerService, NetworkType, BuildNftUrlParams } from '@cityofzion/blockchain-service'
2
-
3
- export class DoraESNeo3 implements ExplorerService {
4
- private networkType: NetworkType
5
-
6
- constructor(networkType: NetworkType) {
7
- this.networkType = networkType
8
- }
9
-
10
- buildTransactionUrl(hash: string): string {
11
- if (this.networkType === 'custom') throw new Error('DoraESNeo3 does not support custom network')
12
- return `https://dora.coz.io/transaction/neo3/${this.networkType}/${hash}`
13
- }
14
-
15
- buildNftUrl({ contractHash, tokenId }: BuildNftUrlParams): string {
16
- if (this.networkType === 'custom') throw new Error('DoraESNeo3 does not support custom network')
17
- return `https://dora.coz.io/nft/neo3/${this.networkType}/${contractHash}/${tokenId}`
18
- }
19
- }
@@ -1,41 +0,0 @@
1
- import { Currency, ExchangeDataService, NetworkType, TokenPricesResponse } from '@cityofzion/blockchain-service'
2
- import axios, { AxiosInstance } from 'axios'
3
-
4
- type FlamingoTokenInfoPricesResponse = {
5
- symbol: string
6
- usd_price: number
7
- hash: string
8
- }[]
9
-
10
- export class FlamingoEDSNeo3 implements ExchangeDataService {
11
- readonly networkType: NetworkType
12
- private axiosInstance: AxiosInstance
13
-
14
- constructor(networkType: NetworkType) {
15
- this.networkType = networkType
16
- this.axiosInstance = axios.create({ baseURL: 'https://api.flamingo.finance' })
17
- }
18
-
19
- async getTokenPrices(currency: Currency): Promise<TokenPricesResponse[]> {
20
- if (this.networkType !== 'mainnet') throw new Error('Exchange is only available on mainnet')
21
-
22
- const { data: prices } = await this.axiosInstance.get<FlamingoTokenInfoPricesResponse>('/token-info/prices')
23
-
24
- let currencyRatio: number = 1
25
-
26
- if (currency !== 'USD') {
27
- currencyRatio = await this.getCurrencyRatio(currency)
28
- }
29
-
30
- return prices.map(price => ({
31
- price: price.usd_price * currencyRatio,
32
- symbol: price.symbol,
33
- hash: price.hash,
34
- }))
35
- }
36
-
37
- private async getCurrencyRatio(currency: Currency): Promise<number> {
38
- const { data } = await this.axiosInstance.get<number>(`/fiat/exchange-rate?pair=USD_${currency}`)
39
- return data
40
- }
41
- }
@@ -1,121 +0,0 @@
1
- import {
2
- NftResponse,
3
- NftsResponse,
4
- NetworkType,
5
- NftDataService,
6
- GetNftParam,
7
- GetNftsByAddressParams,
8
- } from '@cityofzion/blockchain-service'
9
- import qs from 'query-string'
10
- import axios from 'axios'
11
-
12
- import { GHOSTMARKET_CHAIN_BY_NETWORK_TYPE, GHOSTMARKET_URL_BY_NETWORK_TYPE } from './constants'
13
-
14
- type GhostMarketNFT = {
15
- tokenId: string
16
- contract: {
17
- chain?: string
18
- hash: string
19
- symbol: string
20
- }
21
- creator: {
22
- address: string
23
- offchainName?: string
24
- }
25
- apiUrl?: string
26
- ownerships: {
27
- owner: {
28
- address?: string
29
- }
30
- }[]
31
- collection: {
32
- name?: string
33
- logoUrl?: string
34
- }
35
- metadata: {
36
- description: string
37
- mediaType: string
38
- mediaUri: string
39
- mintDate: number
40
- mintNumber: number
41
- name: string
42
- }
43
- }
44
-
45
- type GhostMarketAssets = {
46
- assets: GhostMarketNFT[]
47
- next: string
48
- }
49
-
50
- export class GhostMarketNDSNeo3 implements NftDataService {
51
- private networkType: NetworkType
52
-
53
- constructor(networkType: NetworkType) {
54
- this.networkType = networkType
55
- }
56
-
57
- async getNftsByAddress({ address, size = 18, cursor }: GetNftsByAddressParams): Promise<NftsResponse> {
58
- const url = this.getUrlWithParams({
59
- size,
60
- owners: [address],
61
- cursor: cursor,
62
- })
63
- const { data } = await axios.get<GhostMarketAssets>(url)
64
- const nfts = data.assets ?? []
65
-
66
- return { nextCursor: data.next, items: nfts.map(this.parse.bind(this)) }
67
- }
68
-
69
- async getNft({ contractHash, tokenId }: GetNftParam): Promise<NftResponse> {
70
- const url = this.getUrlWithParams({
71
- contract: contractHash,
72
- tokenIds: [tokenId],
73
- })
74
- const { data } = await axios.get<GhostMarketAssets>(url)
75
- return this.parse(data.assets[0])
76
- }
77
-
78
- private treatGhostMarketImage(srcImage?: string) {
79
- if (!srcImage) {
80
- return
81
- }
82
-
83
- if (srcImage.startsWith('ipfs://')) {
84
- const [, imageId] = srcImage.split('://')
85
-
86
- return `https://ghostmarket.mypinata.cloud/ipfs/${imageId}`
87
- }
88
-
89
- return srcImage
90
- }
91
-
92
- private getUrlWithParams(params: Record<string, any>) {
93
- const parameters = qs.stringify(
94
- {
95
- chain: GHOSTMARKET_CHAIN_BY_NETWORK_TYPE[this.networkType],
96
- ...params,
97
- },
98
- { arrayFormat: 'bracket' }
99
- )
100
- return `${GHOSTMARKET_URL_BY_NETWORK_TYPE[this.networkType]}/assets?${parameters}`
101
- }
102
-
103
- private parse(data: GhostMarketNFT) {
104
- const nftResponse: NftResponse = {
105
- collectionImage: this.treatGhostMarketImage(data.collection?.logoUrl),
106
- id: data.tokenId,
107
- contractHash: data.contract.hash,
108
- symbol: data.contract.symbol,
109
- collectionName: data.collection?.name,
110
- image: this.treatGhostMarketImage(data.metadata.mediaUri),
111
- isSVG: String(data.metadata.mediaType).includes('svg+xml'),
112
- name: data.metadata.name,
113
- creator: {
114
- address: data.creator.address,
115
- name: data.creator.offchainName,
116
- },
117
- }
118
-
119
- return nftResponse
120
- }
121
- }