From 77bf96cae62c1de7d3f0b1b6bb22259c5829a6fd Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Fri, 13 Oct 2023 11:17:46 -0500 Subject: [PATCH 01/41] test: initial fixtures for nft arrays --- tests/fixtures/alchemy/nfts_array_valid.json | 49 +++++++++++++++++ .../alchemy/nfts_array_with_error.json | 53 +++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 tests/fixtures/alchemy/nfts_array_valid.json create mode 100644 tests/fixtures/alchemy/nfts_array_with_error.json diff --git a/tests/fixtures/alchemy/nfts_array_valid.json b/tests/fixtures/alchemy/nfts_array_valid.json new file mode 100644 index 000000000..c234761ed --- /dev/null +++ b/tests/fixtures/alchemy/nfts_array_valid.json @@ -0,0 +1,49 @@ +{ + "ownedNfts": [ + { + "contract": { + "address": "0x54e10253619e8790e162b5517aec518ad0676ef1" + }, + "id": { + "tokenId": "0x0000000000000000000000000000000000000000000000000000000000000051", + "tokenMetadata": { + "tokenType": "ERC721" + } + }, + "balance": "1", + "title": "", + "description": "", + "tokenUri": { + "gateway": "https://metadataapi.codename883.repl.co/apiA4C/81", + "raw": "https://metadataapi.codename883.repl.co/apiA4C/81" + }, + "media": [ + { + "gateway": "", + "raw": "" + } + ], + "metadata": {}, + "timeLastUpdated": "2023-10-10T10:07:01.300Z", + "contractMetadata": { + "name": "Apes For Change", + "symbol": "A4C", + "totalSupply": "3480", + "tokenType": "ERC721", + "contractDeployer": "0xb817b0137bb1a838aebcc63a231d152c6cb03b41", + "deployedBlockNumber": 15319472, + "openSea": { + "floorPrice": 0.001, + "collectionName": "Apes For Change: A CODENAMEhamm Joint", + "collectionSlug": "apesforchange", + "safelistRequestStatus": "not_requested", + "imageUrl": "https://i.seadn.io/gcs/files/e64de844a9f282b896da7507f8ad31f8.png?w=500&auto=format", + "description": "Apes For Change was created to pay tribute to one of the greatest communities in the NFT space. The artwork was reimagined, the traits were reshuffled, and the collection was minted exclusively with $APE. A CODENAME:hamm Joint.", + "externalUrl": "https://apes-for-change.webflow.io/", + "bannerImageUrl": "https://i.seadn.io/gcs/files/686d50de759a7acb244655596962b457.png?w=500&auto=format", + "lastIngestedAt": "2023-10-07T04:53:52.000Z" + } + } + } + ] +} diff --git a/tests/fixtures/alchemy/nfts_array_with_error.json b/tests/fixtures/alchemy/nfts_array_with_error.json new file mode 100644 index 000000000..4c6d68715 --- /dev/null +++ b/tests/fixtures/alchemy/nfts_array_with_error.json @@ -0,0 +1,53 @@ +{ + "ownedNfts": [ + { + "contract": { + "address": "0x54e10253619e8790e162b5517aec518ad0676ef1" + }, + "id": { + "tokenId": "0x0000000000000000000000000000000000000000000000000000000000000051", + "tokenMetadata": { + "tokenType": "ERC721" + } + }, + "balance": "1", + "title": "", + "description": "", + "tokenUri": { + "gateway": "https://metadataapi.codename883.repl.co/apiA4C/81", + "raw": "https://metadataapi.codename883.repl.co/apiA4C/81" + }, + "media": [ + { + "gateway": "", + "raw": "" + } + ], + "metadata": { + "metadata": [], + "attributes": [] + }, + "timeLastUpdated": "2023-10-10T10:18:02.421Z", + "error": "Contract returned a broken token uri", + "contractMetadata": { + "name": "Apes For Change", + "symbol": "A4C", + "totalSupply": "3480", + "tokenType": "ERC721", + "contractDeployer": "0xb817b0137bb1a838aebcc63a231d152c6cb03b41", + "deployedBlockNumber": 15319472, + "openSea": { + "floorPrice": 0.001, + "collectionName": "Apes For Change: A CODENAMEhamm Joint", + "collectionSlug": "apesforchange", + "safelistRequestStatus": "not_requested", + "imageUrl": "https://i.seadn.io/gcs/files/e64de844a9f282b896da7507f8ad31f8.png?w=500&auto=format", + "description": "Apes For Change was created to pay tribute to one of the greatest communities in the NFT space. The artwork was reimagined, the traits were reshuffled, and the collection was minted exclusively with $APE. A CODENAME:hamm Joint.", + "externalUrl": "https://apes-for-change.webflow.io/", + "bannerImageUrl": "https://i.seadn.io/gcs/files/686d50de759a7acb244655596962b457.png?w=500&auto=format", + "lastIngestedAt": "2023-10-07T04:53:52.000Z" + } + } + } + ] +} From edbd6ee1cd0161709940ae8372f453f780bfcfc9 Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Fri, 13 Oct 2023 11:18:55 -0500 Subject: [PATCH 02/41] feat: log and add flag in case of error --- .../Client/Alchemy/AlchemyPendingRequest.php | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/app/Http/Client/Alchemy/AlchemyPendingRequest.php b/app/Http/Client/Alchemy/AlchemyPendingRequest.php index 73b5c7a9d..b3d897102 100644 --- a/app/Http/Client/Alchemy/AlchemyPendingRequest.php +++ b/app/Http/Client/Alchemy/AlchemyPendingRequest.php @@ -33,6 +33,7 @@ use Illuminate\Http\Client\Response; use Illuminate\Support\Arr; use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Log; use Illuminate\Support\Str; use stdClass; use Throwable; @@ -208,7 +209,7 @@ public function getWalletNfts(Wallet $wallet, Network $network, string $cursor = $ownedNfts = Arr::get($data, 'ownedNfts', []); $nfts = collect($ownedNfts) - ->filter(fn ($nft) => $this->filterNft($nft)) + ->filter(fn ($nft) => $this->filterNft($nft, false)) ->map(fn ($nft) => $this->parseNft($nft, $network->id)) ->values(); @@ -364,7 +365,7 @@ public function getContractMetadataBatch(array $contactAddresses, Network $netwo public function parseNft(array $nft, int $networkId, bool $convertTokenNumber = true): Web3NftData { $extractedFloorPrice = $this->tryExtractFloorPrice($nft); - $collectionName = $this->collectionName($nft); + $collectionName = $this->collectionName($nft) ?? Arr::get($nft, 'contractMetadata.symbol'); $description = $nft['description'] ?? null; $supply = Arr::get($nft, 'contractMetadata.totalSupply'); if (is_numeric($supply)) { @@ -395,11 +396,20 @@ public function parseNft(array $nft, int $networkId, bool $convertTokenNumber = $tokenNumber = $convertTokenNumber === true ? CryptoUtils::hexToBigIntStr($nft['id']['tokenId']) : $nft['id']['tokenId']; + $error = Arr::get($nft, 'error'); + if(! empty($error)) { + Log::info('AlchemyPendingRequest: Filter NFT', [ + 'error' => $error, + 'collection' => $collectionName, + 'nft_id' => $tokenNumber, + ]); + } + return new Web3NftData( tokenAddress: $nft['contract']['address'], tokenNumber: $tokenNumber, networkId: $networkId, - collectionName: $collectionName ?? Arr::get($nft, 'contractMetadata.symbol'), + collectionName: $collectionName, collectionSymbol: Arr::get($nft, 'contractMetadata.symbol') ?? $collectionName, collectionImage: Arr::get($nft, 'contractMetadata.openSea.imageUrl') ?? Arr::get($nft, 'media.0.thumbnail') ?? Arr::get($nft, 'media.0.gateway'), collectionWebsite: Arr::get($nft, 'contractMetadata.openSea.externalUrl') ?? Arr::get($nft, 'metadata.external_url'), @@ -421,6 +431,7 @@ public function parseNft(array $nft, int $networkId, bool $convertTokenNumber = traits: $this->extractTraits($nft), mintedBlock: $nft['contractMetadata']['deployedBlockNumber'], mintedAt: $mintTimestamp !== null ? Carbon::createFromTimestampMs($mintTimestamp) : null, + hasError: ! empty($error), ); } @@ -681,13 +692,13 @@ private function getNftV2ApiUrl(): string return 'https://'.self::$apiUrlPlaceholder.'.g.alchemy.com/nft/v2/'; } - private function filterNft(mixed $nft): bool + private function filterNft(mixed $nft, bool $filterError = true): bool { if (Arr::get($nft, 'spamInfo.isSpam', false)) { return false; } - if (Arr::has($nft, 'error')) { + if (Arr::has($nft, 'error') && $filterError) { return false; } From b60d99072e0ea752ee272c1e80787da9ef56d749 Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Fri, 13 Oct 2023 11:19:37 -0500 Subject: [PATCH 03/41] test: AlchemyPendingRequest --- .../Alchemy/AlchemyPendingRequestTest.php | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php b/tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php index 864ecee66..5baa1d401 100644 --- a/tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php +++ b/tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php @@ -94,3 +94,29 @@ expect($fetchedNfts->nfts)->toHaveCount(1); }); + +it('should add a flag in case of responses with errors', function () { + Alchemy::fake([ + 'https://polygon-mainnet.g.alchemy.com/nft/v2/*' => Http::sequence() + ->push(fixtureData('alchemy.nfts_array_with_error'), 200), + ]); + + $wallet = Wallet::factory()->create(); + $network = Network::polygon(); + + $collection = Alchemy::getWalletNfts($wallet, $network); + expect($collection->nfts[0]->hasError)->toBetrue(); +}); + +it('should not add a flag in case of valid responses', function () { + Alchemy::fake([ + 'https://polygon-mainnet.g.alchemy.com/nft/v2/*' => Http::sequence() + ->push(fixtureData('alchemy.nfts_array_valid'), 200), + ]); + + $wallet = Wallet::factory()->create(); + $network = Network::polygon(); + + $collection = Alchemy::getWalletNfts($wallet, $network); + expect($collection->nfts[0]->hasError)->not->toBeTrue(); +}); From f89a9c2402dd6bf98a7bd286c8bed49ff341d63b Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Fri, 13 Oct 2023 11:20:05 -0500 Subject: [PATCH 04/41] fix: update the use of hasError in other implementations --- .../Client/Moralis/MoralisPendingRequest.php | 1 + .../Web3/Fake/FakeWeb3DataProvider.php | 1 + app/Support/Web3NftHandler.php | 39 ++++-- database/seeders/LiveUserSeeder.php | 1 + .../DetermineCollectionMintingDateTest.php | 4 + tests/App/Support/Web3NftHandlerTest.php | 123 ++++++++++++++++++ 6 files changed, 157 insertions(+), 12 deletions(-) diff --git a/app/Http/Client/Moralis/MoralisPendingRequest.php b/app/Http/Client/Moralis/MoralisPendingRequest.php index 5c531e1c0..d49f0085d 100644 --- a/app/Http/Client/Moralis/MoralisPendingRequest.php +++ b/app/Http/Client/Moralis/MoralisPendingRequest.php @@ -170,6 +170,7 @@ public function getWalletNfts(Wallet $wallet, Network $network, ?string $cursor) traits: [], mintedBlock: (int) $nft['block_number_minted'], mintedAt: null, + hasError: false, ); })->values(); diff --git a/app/Services/Web3/Fake/FakeWeb3DataProvider.php b/app/Services/Web3/Fake/FakeWeb3DataProvider.php index 4d5bce89f..9378e00cb 100644 --- a/app/Services/Web3/Fake/FakeWeb3DataProvider.php +++ b/app/Services/Web3/Fake/FakeWeb3DataProvider.php @@ -86,6 +86,7 @@ public function getWalletNfts(Wallet $wallet, Network $network, string $cursor = traits: [], mintedBlock: random_int(1, 10000), mintedAt: now(), + hasError: false, ); }); diff --git a/app/Support/Web3NftHandler.php b/app/Support/Web3NftHandler.php index c37885c25..7b4cb3dfb 100644 --- a/app/Support/Web3NftHandler.php +++ b/app/Support/Web3NftHandler.php @@ -65,6 +65,12 @@ public function store(Collection $nfts, bool $dispatchJobs = false): void $attributes['opensea_slug'] = $nftData->collectionOpenSeaSlug; } + if ($nftData->hasError) { + $attributes = array_filter($attributes, function ($value) { + return $value !== null; + }); + } + return [ $nftData->tokenAddress, $nftData->networkId, @@ -124,22 +130,31 @@ public function store(Collection $nfts, bool $dispatchJobs = false): void return $this->shouldKeepNft($collection); }); - $valuesToUpsert = $nfts->map(fn ($nft) => [ - 'wallet_id' => $this->wallet?->id, - 'collection_id' => $groupedByAddress->get(Str::lower($nft->tokenAddress))->id, - 'token_number' => $nft->tokenNumber, - 'name' => $nft->name, - 'extra_attributes' => json_encode($nft->extraAttributes), - 'deleted_at' => null, - 'metadata_fetched_at' => $now, - ])->toArray(); + $valuesToUpsert = $nfts->map(function ($nft) use ($groupedByAddress, $now) { + $collection = $groupedByAddress->get(Str::lower($nft->tokenAddress)); + + $values = [ + 'wallet_id' => $this->wallet?->id, + 'collection_id' => $collection->id, + 'token_number' => $nft->tokenNumber, + 'name' => $nft->name, + 'extra_attributes' => json_encode($nft->extraAttributes), + 'deleted_at' => null, + 'metadata_fetched_at' => $now, + ]; + + return $values; + })->toArray(); $uniqueBy = ['collection_id', 'token_number']; - $valuesToUpdateIfExists = ['name', 'extra_attributes', 'deleted_at', 'metadata_fetched_at']; + $valuesToUpdateIfExists = ['deleted_at']; + $valuesToCheck = ['name', 'extra_attributes', 'metadata_fetched_at', 'wallet_id']; - if ($this->wallet !== null) { - $valuesToUpdateIfExists[] = 'wallet_id'; + foreach ($valuesToCheck as $value) { + if (array_filter($valuesToUpsert, fn ($v) => $v[$value] !== null)) { + $valuesToUpdateIfExists[] = $value; + } } Nft::upsert($valuesToUpsert, $uniqueBy, $valuesToUpdateIfExists); diff --git a/database/seeders/LiveUserSeeder.php b/database/seeders/LiveUserSeeder.php index fa780a904..28bf3a296 100644 --- a/database/seeders/LiveUserSeeder.php +++ b/database/seeders/LiveUserSeeder.php @@ -173,6 +173,7 @@ traits: array_map(fn ($trait) => [ ], $nft['traits']), mintedAt: Carbon::parse($nft['mintedAt']), mintedBlock: $nft['mintedBlock'], + hasError: false, )); } diff --git a/tests/App/Jobs/DetermineCollectionMintingDateTest.php b/tests/App/Jobs/DetermineCollectionMintingDateTest.php index cfc39d540..582a47f9b 100644 --- a/tests/App/Jobs/DetermineCollectionMintingDateTest.php +++ b/tests/App/Jobs/DetermineCollectionMintingDateTest.php @@ -53,6 +53,7 @@ traits: [], mintedBlock: 1000, mintedAt: null, + hasError: false, ); $job = new DetermineCollectionMintingDate($nft); @@ -103,6 +104,7 @@ traits: [], traits: [], mintedBlock: 1000, mintedAt: null, + hasError: false, ); $job = new DetermineCollectionMintingDate($nft); @@ -136,6 +138,7 @@ traits: [], traits: [], mintedBlock: 1000, mintedAt: null, + hasError: false, ); $job = new DetermineCollectionMintingDate($nft); @@ -167,6 +170,7 @@ traits: [], traits: [], mintedBlock: 1000, mintedAt: null, + hasError: false, ); $job = new DetermineCollectionMintingDate($nft); diff --git a/tests/App/Support/Web3NftHandlerTest.php b/tests/App/Support/Web3NftHandlerTest.php index d49757f3a..7f5bd14f6 100644 --- a/tests/App/Support/Web3NftHandlerTest.php +++ b/tests/App/Support/Web3NftHandlerTest.php @@ -48,6 +48,7 @@ traits: [], mintedBlock: 1000, mintedAt: null, + hasError: false, ); $handler->store(collect([$data])); @@ -88,6 +89,7 @@ traits: [], traits: [], mintedBlock: 1000, mintedAt: null, + hasError: false, ); $handler->store(nfts: collect([$data]), dispatchJobs: true); @@ -135,6 +137,7 @@ traits: [ ], mintedBlock: 1000, mintedAt: null, + hasError: false, ); $collection = Collection::query()->create([ @@ -203,6 +206,7 @@ traits: [ traits: [], mintedBlock: 1000, mintedAt: null, + hasError: false, ); $collection = Collection::query()->create([ @@ -269,6 +273,7 @@ traits: [], traits: [], mintedBlock: 1000, mintedAt: null, + hasError: false, ); $collection = Collection::query()->create([ @@ -333,6 +338,7 @@ traits: [], traits: [], mintedBlock: 1000, mintedAt: null, + hasError: false, ); $collection = Collection::query()->create([ @@ -409,6 +415,7 @@ traits: [], traits: [], mintedBlock: 1000, mintedAt: null, + hasError: false, ); $collection = Collection::query()->create([ @@ -471,6 +478,7 @@ traits: [], traits: [], mintedBlock: 1000, mintedAt: null, + hasError: false, ); $collection = Collection::query()->create([ @@ -493,3 +501,118 @@ traits: [], expect(Collection::count())->toBe(1); expect($collection->extra_attributes->opensea_slug)->toBe('test456'); }); + +it('should not update any already filled fields in DB with empty values if hasError is true', function () { + Bus::fake(); + + $handler = new Web3NftHandler(); + + $network = Network::firstWhere('chain_id', 1); + + $token = Token::factory()->create([ + 'network_id' => $network->id, + ]); + + $wallet = Wallet::factory()->create(); + + $handler = new Web3NftHandler( + network: $network, + wallet: $wallet, + ); + + // originalData is the data that is already in the DB + $now = Carbon::now(); + $originalData = new Web3NftData( + tokenAddress: '0x1234', + tokenNumber: '123', + networkId: $token->network_id, + collectionName: 'Collection Name', + collectionSymbol: 'AME', + collectionImage: 'test_image', + collectionWebsite: 'www.test_website.com', + collectionDescription: 'Collection Description', + collectionSocials: [ + [ + 'name' => 'twitter', + 'url' => 'https://twitter.com/test', + ], + ], + collectionSupply: 3000, + collectionBannerImageUrl: 'test_banner', + collectionBannerUpdatedAt: $now, + collectionOpenSeaSlug: 'test456', + name: 'NFT Name', + description: 'NFT Description', + extraAttributes: [ + 'image' => 'test_image', + 'website' => 'www.test_website.com', + 'banner' => 'test_banner', + 'banner_updated_at' => $now, + 'opensea_slug' => 'test456', + ], + floorPrice: null, + traits: [], + mintedBlock: 1000, + mintedAt: null, + hasError: false, + ); + + $collection = Collection::query()->create([ + 'address' => '0x1234', + 'network_id' => $network->id, + 'name' => $originalData->collectionName, + 'slug' => Str::slug($originalData->collectionName), + 'symbol' => $originalData->collectionSymbol, + 'minted_block' => $originalData->mintedBlock, + 'extra_attributes' => null, + ]); + + $handler->store(collect([$originalData])); + $collection->refresh(); + + expect(Collection::count())->toBe(1); + + $dataWithError = new Web3NftData( + tokenAddress: '0x1234', + tokenNumber: '123', + networkId: $token->network_id, + collectionName: 'Collection Name', + collectionSymbol: 'AME', + collectionImage: null, + collectionWebsite: null, + collectionDescription: null, + collectionSocials: null, + collectionSupply: 3000, + collectionBannerImageUrl: null, + collectionBannerUpdatedAt: $now, + collectionOpenSeaSlug: null, + name: null, + description: null, + extraAttributes: [ + 'image' => null, + 'website' => null, + 'banner' => null, + 'banner_updated_at' => $now, + 'opensea_slug' => null, + ], + floorPrice: null, + traits: [], + mintedBlock: 1000, + mintedAt: null, + hasError: true, + ); + + $handler->store(collect([$dataWithError])); + $collection->refresh(); + + # Expect all fields to be the same as before + expect(Collection::count())->toBe(1); + expect($collection->name)->toBe($originalData->collectionName); + expect($collection->slug)->toBe(Str::slug($originalData->collectionName)); + expect($collection->symbol)->toBe($originalData->collectionSymbol); + expect($collection->extra_attributes[0]['image'])->toBe($originalData->collectionImage); + expect($collection->extra_attributes[0]['website'])->toBe($originalData->collectionWebsite); + expect($collection->extra_attributes[0]['banner'])->toBe($originalData->collectionBannerImageUrl); + expect($collection->extra_attributes[0]['opensea_slug'])->toBe($originalData->collectionOpenSeaSlug); + expect($collection->nfts->first()->name)->toBe($originalData->name); +}); From f035503c99fc2550c83ab9c6cb3fede25213d8dc Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Fri, 13 Oct 2023 11:20:18 -0500 Subject: [PATCH 05/41] chore: update types --- resources/types/generated.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/types/generated.d.ts b/resources/types/generated.d.ts index 34c795e15..d1942ff27 100644 --- a/resources/types/generated.d.ts +++ b/resources/types/generated.d.ts @@ -491,6 +491,7 @@ declare namespace App.Data.Web3 { traits: Array; mintedBlock: number; mintedAt: string | null; + hasError: boolean | null; }; export type Web3NftTransfer = { contractAddress: string; From f08b598afc4617aab6aadaf4ec02bbb1ac028bf2 Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Fri, 13 Oct 2023 11:20:38 -0500 Subject: [PATCH 06/41] feat: support for hasError in Web3NftData --- app/Data/Web3/Web3NftData.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Data/Web3/Web3NftData.php b/app/Data/Web3/Web3NftData.php index 21ddcf7a1..1345a8683 100644 --- a/app/Data/Web3/Web3NftData.php +++ b/app/Data/Web3/Web3NftData.php @@ -4,7 +4,6 @@ namespace App\Data\Web3; -use App\Enums\TraitDisplayType; use App\Models\Token; use Carbon\Carbon; use Spatie\LaravelData\Data; @@ -40,6 +39,7 @@ public function __construct( public array $traits, public int $mintedBlock, public ?Carbon $mintedAt, + public ?bool $hasError, ) { } From fdaff51fbe279291c147764f0bc75b7de2bc5b6e Mon Sep 17 00:00:00 2001 From: patricio0312rev Date: Fri, 13 Oct 2023 16:28:40 +0000 Subject: [PATCH 07/41] style: resolve style guide violations --- app/Http/Client/Alchemy/AlchemyPendingRequest.php | 2 +- tests/App/Support/Web3NftHandlerTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Http/Client/Alchemy/AlchemyPendingRequest.php b/app/Http/Client/Alchemy/AlchemyPendingRequest.php index b3d897102..57356ff58 100644 --- a/app/Http/Client/Alchemy/AlchemyPendingRequest.php +++ b/app/Http/Client/Alchemy/AlchemyPendingRequest.php @@ -397,7 +397,7 @@ public function parseNft(array $nft, int $networkId, bool $convertTokenNumber = $tokenNumber = $convertTokenNumber === true ? CryptoUtils::hexToBigIntStr($nft['id']['tokenId']) : $nft['id']['tokenId']; $error = Arr::get($nft, 'error'); - if(! empty($error)) { + if (! empty($error)) { Log::info('AlchemyPendingRequest: Filter NFT', [ 'error' => $error, 'collection' => $collectionName, diff --git a/tests/App/Support/Web3NftHandlerTest.php b/tests/App/Support/Web3NftHandlerTest.php index 7f5bd14f6..35d47d117 100644 --- a/tests/App/Support/Web3NftHandlerTest.php +++ b/tests/App/Support/Web3NftHandlerTest.php @@ -605,7 +605,7 @@ traits: [], $handler->store(collect([$dataWithError])); $collection->refresh(); - # Expect all fields to be the same as before + // Expect all fields to be the same as before expect(Collection::count())->toBe(1); expect($collection->name)->toBe($originalData->collectionName); expect($collection->slug)->toBe(Str::slug($originalData->collectionName)); From 651ab55d641c2a5fa191a740006b6995d4b18893 Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Fri, 13 Oct 2023 11:34:33 -0500 Subject: [PATCH 08/41] fix: Web3NftData --- app/Data/Web3/Web3NftData.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Data/Web3/Web3NftData.php b/app/Data/Web3/Web3NftData.php index 1345a8683..d0ddcdf6e 100644 --- a/app/Data/Web3/Web3NftData.php +++ b/app/Data/Web3/Web3NftData.php @@ -4,6 +4,7 @@ namespace App\Data\Web3; +use App\Enums\TraitDisplayType; use App\Models\Token; use Carbon\Carbon; use Spatie\LaravelData\Data; From ab756ef73a4092ca53470e5ab1764a1e5624466b Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Fri, 13 Oct 2023 11:52:23 -0500 Subject: [PATCH 09/41] fix: tests count --- phpunit.xml | 5 +++-- tests/App/Jobs/FetchWalletNftsTest.php | 10 +++++----- .../Web3/Alchemy/AlchemyWeb3DataProviderTest.php | 6 +++--- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/phpunit.xml b/phpunit.xml index 3af49fe2a..0ff1d4e8b 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -15,9 +15,10 @@ - + - + + diff --git a/tests/App/Jobs/FetchWalletNftsTest.php b/tests/App/Jobs/FetchWalletNftsTest.php index ee48750f5..8485970d1 100644 --- a/tests/App/Jobs/FetchWalletNftsTest.php +++ b/tests/App/Jobs/FetchWalletNftsTest.php @@ -39,7 +39,7 @@ (new FetchWalletNfts($wallet, $network))->handle(); - $this->assertDatabaseCount('collections', 39); + $this->assertDatabaseCount('collections', 42); $this->assertDatabaseCount('nfts', 90); expect(Collection::whereNotNull('last_indexed_token_number')->count())->toBe(0); @@ -77,10 +77,10 @@ (new FetchWalletNfts($wallet, $network))->handle(); - $this->assertDatabaseCount('collections', 39); + $this->assertDatabaseCount('collections', 42); $this->assertDatabaseCount('nfts', 90); - Bus::assertDispatchedTimes(FetchCollectionFloorPrice::class, 39); + Bus::assertDispatchedTimes(FetchCollectionFloorPrice::class, 42); }); it('should fetch nfts for wallet and skip floor price job if already present', function () { @@ -707,7 +707,7 @@ function (GalleryNftsChanged $notification) use ($wallet) { // Fetch same NFTs with a different wallet (new FetchWalletNfts($wallet1, $network))->handle(); - $this->assertDatabaseCount('collections', 39); + $this->assertDatabaseCount('collections', 42); $this->assertDatabaseCount('nfts', 90); $this->assertDatabaseCount('galleries_dirty', 0); @@ -1224,7 +1224,7 @@ function getTestNfts($length = 3, $offset = 0, $withCursor = false): array (new FetchWalletNfts($wallet, $network))->handle(); - $this->assertDatabaseCount('collections', 39); + $this->assertDatabaseCount('collections', 42); $this->assertDatabaseCount('nfts', 90); expect(Collection::whereNotNull('last_indexed_token_number')->count())->toBe(1); diff --git a/tests/App/Services/Web3/Alchemy/AlchemyWeb3DataProviderTest.php b/tests/App/Services/Web3/Alchemy/AlchemyWeb3DataProviderTest.php index 117b2b58e..00b87b885 100644 --- a/tests/App/Services/Web3/Alchemy/AlchemyWeb3DataProviderTest.php +++ b/tests/App/Services/Web3/Alchemy/AlchemyWeb3DataProviderTest.php @@ -42,7 +42,7 @@ $tokens = $provider->getWalletTokens($wallet, $network); expect($tokens)->toBeInstanceOf(Collection::class) - ->and($tokens)->toHaveCount(5) + ->and($tokens)->toHaveCount(6) ->and($tokens[0])->toBeInstanceOf(Web3Erc20TokenData::class) ->and($tokens[0]->tokenAddress)->toBe('0x01e849040c418c3b7f130351a6e4630c08a7d98e'); }); @@ -149,7 +149,7 @@ $tokens = $provider->getWalletNfts($wallet, $network)->nfts; expect($tokens)->toBeInstanceOf(Collection::class) - ->and($tokens)->toHaveCount(90) + ->and($tokens)->toHaveCount(94) ->and($tokens[0])->toBeInstanceOf(Web3NftData::class) ->and($tokens[0]->tokenAddress)->toBe('0x0b7600ca77fc257fe7eb432f87825cccc4590037') ->and($tokens[2]->floorPrice) @@ -463,7 +463,7 @@ $provider = new AlchemyWeb3DataProvider(); $nfts = $provider->getWalletNfts($wallet, $network)->nfts; - expect($nfts)->toHaveCount(5) + expect($nfts)->toHaveCount(6) ->and($nfts->first()->name)->toBeNull() ->and($nfts->last()->name)->toEqual('OK OpenSea fallback'); }); From 889fdfd8e7d2cd2f2032f1480730a9751d05794e Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Fri, 13 Oct 2023 12:02:03 -0500 Subject: [PATCH 10/41] fix: nft count for updated tests --- tests/App/Jobs/FetchWalletNftsTest.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/App/Jobs/FetchWalletNftsTest.php b/tests/App/Jobs/FetchWalletNftsTest.php index 8485970d1..f251b5d19 100644 --- a/tests/App/Jobs/FetchWalletNftsTest.php +++ b/tests/App/Jobs/FetchWalletNftsTest.php @@ -40,7 +40,7 @@ (new FetchWalletNfts($wallet, $network))->handle(); $this->assertDatabaseCount('collections', 42); - $this->assertDatabaseCount('nfts', 90); + $this->assertDatabaseCount('nfts', 94); expect(Collection::whereNotNull('last_indexed_token_number')->count())->toBe(0); }); @@ -78,7 +78,7 @@ (new FetchWalletNfts($wallet, $network))->handle(); $this->assertDatabaseCount('collections', 42); - $this->assertDatabaseCount('nfts', 90); + $this->assertDatabaseCount('nfts', 94); Bus::assertDispatchedTimes(FetchCollectionFloorPrice::class, 42); }); @@ -708,11 +708,11 @@ function (GalleryNftsChanged $notification) use ($wallet) { (new FetchWalletNfts($wallet1, $network))->handle(); $this->assertDatabaseCount('collections', 42); - $this->assertDatabaseCount('nfts', 90); + $this->assertDatabaseCount('nfts', 94); $this->assertDatabaseCount('galleries_dirty', 0); expect($galleryCache->nftsCount())->toBe(0) - ->and($userCache->nftsCount())->toBe(90); + ->and($userCache->nftsCount())->toBe(94); $nfts = Nft::query()->get(); $nfts->each(function ($nft) use ($gallery) { @@ -721,12 +721,12 @@ function (GalleryNftsChanged $notification) use ($wallet) { $this->assertDatabaseCount('galleries_dirty', 1); expect($galleryCache->nftsCount())->toBe(0) - ->and($userCache->nftsCount())->toBe(90); + ->and($userCache->nftsCount())->toBe(94); GalleryCache::clearAllDirty(); - expect($galleryCache->nftsCount())->toBe(90) - ->and($userCache->nftsCount())->toBe(90); + expect($galleryCache->nftsCount())->toBe(94) + ->and($userCache->nftsCount())->toBe(94); (new FetchWalletNfts($wallet2, $network))->handle(); @@ -1225,7 +1225,7 @@ function getTestNfts($length = 3, $offset = 0, $withCursor = false): array (new FetchWalletNfts($wallet, $network))->handle(); $this->assertDatabaseCount('collections', 42); - $this->assertDatabaseCount('nfts', 90); + $this->assertDatabaseCount('nfts', 94); expect(Collection::whereNotNull('last_indexed_token_number')->count())->toBe(1); From ff145454bcb25c143218b10d2e79bdc3effd0de1 Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Fri, 13 Oct 2023 12:02:17 -0500 Subject: [PATCH 11/41] fix: restore phpunit --- phpunit.xml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/phpunit.xml b/phpunit.xml index 0ff1d4e8b..3af49fe2a 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -15,10 +15,9 @@ - + - - + From c75ade02bd63d5bbd32d8d618b5445ddacce4ca7 Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Fri, 13 Oct 2023 12:10:04 -0500 Subject: [PATCH 12/41] fix: AlchemyWeb3DataProvider --- .../App/Services/Web3/Alchemy/AlchemyWeb3DataProviderTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/App/Services/Web3/Alchemy/AlchemyWeb3DataProviderTest.php b/tests/App/Services/Web3/Alchemy/AlchemyWeb3DataProviderTest.php index 00b87b885..9ac8dc6c2 100644 --- a/tests/App/Services/Web3/Alchemy/AlchemyWeb3DataProviderTest.php +++ b/tests/App/Services/Web3/Alchemy/AlchemyWeb3DataProviderTest.php @@ -42,7 +42,7 @@ $tokens = $provider->getWalletTokens($wallet, $network); expect($tokens)->toBeInstanceOf(Collection::class) - ->and($tokens)->toHaveCount(6) + ->and($tokens)->toHaveCount(5) ->and($tokens[0])->toBeInstanceOf(Web3Erc20TokenData::class) ->and($tokens[0]->tokenAddress)->toBe('0x01e849040c418c3b7f130351a6e4630c08a7d98e'); }); @@ -464,7 +464,7 @@ $nfts = $provider->getWalletNfts($wallet, $network)->nfts; expect($nfts)->toHaveCount(6) - ->and($nfts->first()->name)->toBeNull() + ->and($nfts->first()->name)->not->toBeNull() ->and($nfts->last()->name)->toEqual('OK OpenSea fallback'); }); From 16333b9afad24f37e7c5e2c584af87ac4478e54b Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Fri, 13 Oct 2023 12:17:54 -0500 Subject: [PATCH 13/41] fix: getWalletNfts test in AlchemyWeb3DataProvider --- tests/App/Services/Web3/Alchemy/AlchemyWeb3DataProviderTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/App/Services/Web3/Alchemy/AlchemyWeb3DataProviderTest.php b/tests/App/Services/Web3/Alchemy/AlchemyWeb3DataProviderTest.php index 9ac8dc6c2..6cda06fb6 100644 --- a/tests/App/Services/Web3/Alchemy/AlchemyWeb3DataProviderTest.php +++ b/tests/App/Services/Web3/Alchemy/AlchemyWeb3DataProviderTest.php @@ -151,7 +151,7 @@ expect($tokens)->toBeInstanceOf(Collection::class) ->and($tokens)->toHaveCount(94) ->and($tokens[0])->toBeInstanceOf(Web3NftData::class) - ->and($tokens[0]->tokenAddress)->toBe('0x0b7600ca77fc257fe7eb432f87825cccc4590037') + ->and($tokens[0]->tokenAddress)->toBe('0x0631cc561618ee4fa142e502c5f5ab9fcc2aa90c') ->and($tokens[2]->floorPrice) ->toBeInstanceOf(Web3NftCollectionFloorPrice::class) ->and($tokens[2]->floorPrice->price)->toEqual('3000000000000000') From 24f2013833ff5f7b61fad6cdda09223c373d52c8 Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Mon, 16 Oct 2023 07:48:01 -0500 Subject: [PATCH 14/41] test: coverage for line 702 in AlchemyPendingRequest --- .../Alchemy/AlchemyPendingRequestTest.php | 37 +++++++ .../nft_batch_metadata_with_error.json | 98 +++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 tests/fixtures/alchemy/nft_batch_metadata_with_error.json diff --git a/tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php b/tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php index 5baa1d401..e5afb28bb 100644 --- a/tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php +++ b/tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php @@ -120,3 +120,40 @@ $collection = Alchemy::getWalletNfts($wallet, $network); expect($collection->nfts[0]->hasError)->not->toBeTrue(); }); + +it('should not filter nfts with errors if the flag is set as true', function () { + Alchemy::fake([ + 'https://polygon-mainnet.g.alchemy.com/nft/v2/*' => Http::sequence() + ->push(fixtureData('alchemy.nfts_array_with_error'), 200), + ]); + + $wallet = Wallet::factory()->create(); + $network = Network::polygon(); + + $collection = Alchemy::getWalletNfts($wallet, $network); + expect($collection->nfts)->toHaveCount(1); +}); + + +it('should filter nfts with errors by default', function() { + $user = createUser(); + + $network = Network::polygon(); + $collection = Collection::factory()->create(['network_id' => $network->id]); + + $nfts = collect(); + $nft = Nft::factory()->create([ + 'wallet_id' => $user->wallet, + 'collection_id' => $collection, + ]); + + $nfts->push($nft); + + Alchemy::fake([ + 'https://polygon-mainnet.g.alchemy.com/nft/v2/*' => Http::response(fixtureData('alchemy.nft_batch_metadata_with_error'), 200), + ]); + + $fetchedNfts = Alchemy::nftMetadataBatch($nfts, $network); + + expect($fetchedNfts->nfts)->toHaveCount(0); +}); diff --git a/tests/fixtures/alchemy/nft_batch_metadata_with_error.json b/tests/fixtures/alchemy/nft_batch_metadata_with_error.json new file mode 100644 index 000000000..b856e0ae0 --- /dev/null +++ b/tests/fixtures/alchemy/nft_batch_metadata_with_error.json @@ -0,0 +1,98 @@ +[ + { + "contract": { "address": "0x0e33fd2db4f140dca8f65671c40e36f8fd648fff" }, + "id": { "tokenId": "7148", "tokenMetadata": { "tokenType": "ERC721" } }, + "title": "100 Meals 7148", + "description": "Each NFT represents a 100 meal donation to Feeding America via their network of food banks.\\n\\n0.0066 ETH = ~$10.50 USD on 11\\/07\\/2022.\\n\\nFunds routed directly to Feeding America via The Giving Block.\\n\\nIf you prefer to donate ETH directly: 0x0b4290922c4bD6660c542451e98c69b208dfbb4d", + "tokenUri": { + "gateway": "", + "raw": "data:application\\/json;base64,eyJuYW1lIjogIjEwMCBNZWFscyA3MTQ4IiwgImRlc2NyaXB0aW9uIjogIkVhY2ggTkZUIHJlcHJlc2VudHMgYSAxMDAgbWVhbCBkb25hdGlvbiB0byBGZWVkaW5nIEFtZXJpY2EgdmlhIHRoZWlyIG5ldHdvcmsgb2YgZm9vZCBiYW5rcy5cblxuMC4wMDY2IEVUSCA9IH4kMTAuNTAgVVNEIG9uIDExLzA3LzIwMjIuXG5cbkZ1bmRzIHJvdXRlZCBkaXJlY3RseSB0byBGZWVkaW5nIEFtZXJpY2EgdmlhIFRoZSBHaXZpbmcgQmxvY2suXG5cbklmIHlvdSBwcmVmZXIgdG8gZG9uYXRlIEVUSCBkaXJlY3RseTogMHgwYjQyOTA5MjJjNGJENjY2MGM1NDI0NTFlOThjNjliMjA4ZGZiYjRkIiwgImltYWdlIjogImlwZnM6Ly9iYWZrcmVpYnNsYnNucHZjdG9yZzJlc2NqbTJrbWlieHR2NTJnaWJ3NXdvbW5vbG5sc2ZlcW1rN2l4ND9pZD03MTQ4IiwgInByb3BlcnRpZXMiOiB7Im51bWJlciI6IDcxNDgsICJuYW1lIjogIjEwMCBNZWFscyJ9fQ==" + }, + "media": [ + { + "gateway": "https:\\/\\/nft-cdn.alchemy.com\\/eth-mainnet\\/38f70907e4aaa3758c20b3f27ab791a1", + "thumbnail": "https:\\/\\/res.cloudinary.com\\/alchemyapi\\/image\\/upload\\/thumbnailv2\\/eth-mainnet\\/38f70907e4aaa3758c20b3f27ab791a1", + "raw": "ipfs:\\/\\/bafkreibslbsnpvctorg2escjm2kmibxtv52gibw5womnolnlsfeqmk7ix4?id=7148", + "format": "png", + "bytes": 231775 + } + ], + "metadata": { + "name": "100 Meals 7148", + "description": "Each NFT represents a 100 meal donation to Feeding America via their network of food banks.\\n\\n0.0066 ETH = ~$10.50 USD on 11\\/07\\/2022.\\n\\nFunds routed directly to Feeding America via The Giving Block.\\n\\nIf you prefer to donate ETH directly: 0x0b4290922c4bD6660c542451e98c69b208dfbb4d", + "image": "ipfs:\\/\\/bafkreibslbsnpvctorg2escjm2kmibxtv52gibw5womnolnlsfeqmk7ix4?id=7148", + "properties": { "name": "100 Meals", "number": 7148 } + }, + "timeLastUpdated": "2023-09-02T17:30:15.571Z", + "error": "Contract returned a broken token uri", + "contractMetadata": { + "name": "100 Meals", + "symbol": "FEED", + "totalSupply": "24757", + "tokenType": "ERC721", + "contractDeployer": "0xc8f8e2f59dd95ff67c3d39109eca2e2a017d4c8a", + "deployedBlockNumber": 15920868, + "openSea": { + "floorPrice": 0.0005, + "collectionName": "100 Meals", + "collectionSlug": "100-meals", + "safelistRequestStatus": "approved", + "imageUrl": "https:\\/\\/i.seadn.io\\/gae\\/3JiXwIAdOJK19jchqvrHe0OHX8A5mZgKbQ_ccUy6Ku0ttH2P1TEB4IjKVilR1AeGBF-2s9442i2DaTzQ7QyYCmLWSwiGFDdOOWYHBQ?w=500&auto=format", + "description": "Each NFT represents a 100 meal donation to Feeding America via their network of food banks.\\n\\n0.0066 ETH = ~$10.50 USD on 11\\/07\\/2022.\\n\\nFunds routed directly to Feeding America via The Giving Block.\\n\\nIf you prefer to donate ETH directly: 0x0b4290922c4bD6660c542451e98c69b208dfbb4d\\n\\nRoyalties on this collection go directly to the address above.", + "externalUrl": "http:\\/\\/visualizevalue.com", + "twitterUsername": "jackbutcher", + "bannerImageUrl": "https:\\/\\/openseauserdata.com\\/files\\/55a8c3b070b98d3429eb57ab644cf087.svg", + "lastIngestedAt": "2023-09-29T00:15:42.000Z" + } + } + }, + { + "contract": { + "address": "0x54e10253619e8790e162b5517aec518ad0676ef1" + }, + "id": { + "tokenId": "0x0000000000000000000000000000000000000000000000000000000000000051", + "tokenMetadata": { + "tokenType": "ERC721" + } + }, + "balance": "1", + "title": "", + "description": "", + "tokenUri": { + "gateway": "https://metadataapi.codename883.repl.co/apiA4C/81", + "raw": "https://metadataapi.codename883.repl.co/apiA4C/81" + }, + "media": [ + { + "gateway": "", + "raw": "" + } + ], + "metadata": { + "metadata": [], + "attributes": [] + }, + "timeLastUpdated": "2023-10-10T10:18:02.421Z", + "error": "Contract returned a broken token uri", + "contractMetadata": { + "name": "Apes For Change", + "symbol": "A4C", + "totalSupply": "3480", + "tokenType": "ERC721", + "contractDeployer": "0xb817b0137bb1a838aebcc63a231d152c6cb03b41", + "deployedBlockNumber": 15319472, + "openSea": { + "floorPrice": 0.001, + "collectionName": "Apes For Change: A CODENAMEhamm Joint", + "collectionSlug": "apesforchange", + "safelistRequestStatus": "not_requested", + "imageUrl": "https://i.seadn.io/gcs/files/e64de844a9f282b896da7507f8ad31f8.png?w=500&auto=format", + "description": "Apes For Change was created to pay tribute to one of the greatest communities in the NFT space. The artwork was reimagined, the traits were reshuffled, and the collection was minted exclusively with $APE. A CODENAME:hamm Joint.", + "externalUrl": "https://apes-for-change.webflow.io/", + "bannerImageUrl": "https://i.seadn.io/gcs/files/686d50de759a7acb244655596962b457.png?w=500&auto=format", + "lastIngestedAt": "2023-10-07T04:53:52.000Z" + } + } + } +] From bb6a9d69b717e9b6bb97a8dae6a4c6e4a6b3225f Mon Sep 17 00:00:00 2001 From: patricio0312rev Date: Mon, 16 Oct 2023 12:49:50 +0000 Subject: [PATCH 15/41] style: resolve style guide violations --- tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php b/tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php index e5afb28bb..b23116085 100644 --- a/tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php +++ b/tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php @@ -134,8 +134,7 @@ expect($collection->nfts)->toHaveCount(1); }); - -it('should filter nfts with errors by default', function() { +it('should filter nfts with errors by default', function () { $user = createUser(); $network = Network::polygon(); From 45cf769853003a856d454bf1df9a7cfeabf5fe16 Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Mon, 16 Oct 2023 07:50:42 -0500 Subject: [PATCH 16/41] feat: add collection address to log --- app/Http/Client/Alchemy/AlchemyPendingRequest.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/Http/Client/Alchemy/AlchemyPendingRequest.php b/app/Http/Client/Alchemy/AlchemyPendingRequest.php index 57356ff58..3376d32db 100644 --- a/app/Http/Client/Alchemy/AlchemyPendingRequest.php +++ b/app/Http/Client/Alchemy/AlchemyPendingRequest.php @@ -397,10 +397,13 @@ public function parseNft(array $nft, int $networkId, bool $convertTokenNumber = $tokenNumber = $convertTokenNumber === true ? CryptoUtils::hexToBigIntStr($nft['id']['tokenId']) : $nft['id']['tokenId']; $error = Arr::get($nft, 'error'); + $collectionAddress = Arr::get($nft, 'contract.address'); + if (! empty($error)) { Log::info('AlchemyPendingRequest: Filter NFT', [ 'error' => $error, - 'collection' => $collectionName, + 'collection_name' => $collectionName, + 'collection_address' => $collectionAddress, 'nft_id' => $tokenNumber, ]); } From d6d9dee6af260423a350ce01acb543908a2f1d61 Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Tue, 17 Oct 2023 04:06:12 -0500 Subject: [PATCH 17/41] feat: add error column to nfts table --- ..._084040_add_error_column_to_nfts_table.php | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 database/migrations/2023_10_17_084040_add_error_column_to_nfts_table.php diff --git a/database/migrations/2023_10_17_084040_add_error_column_to_nfts_table.php b/database/migrations/2023_10_17_084040_add_error_column_to_nfts_table.php new file mode 100644 index 000000000..5b3c042ce --- /dev/null +++ b/database/migrations/2023_10_17_084040_add_error_column_to_nfts_table.php @@ -0,0 +1,28 @@ +string('error')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('nfts', function (Blueprint $table) { + $table->dropColumn('error'); + }); + } +}; From 189dded95da335184e4e87ebfbe85aba2a87fb0f Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Tue, 17 Oct 2023 06:13:54 -0500 Subject: [PATCH 18/41] feat: error field for Web3NftData --- app/Data/Web3/Web3NftData.php | 1 + resources/types/generated.d.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/app/Data/Web3/Web3NftData.php b/app/Data/Web3/Web3NftData.php index d0ddcdf6e..c78126a65 100644 --- a/app/Data/Web3/Web3NftData.php +++ b/app/Data/Web3/Web3NftData.php @@ -41,6 +41,7 @@ public function __construct( public int $mintedBlock, public ?Carbon $mintedAt, public ?bool $hasError, + public ?string $error, ) { } diff --git a/resources/types/generated.d.ts b/resources/types/generated.d.ts index d1942ff27..40ce03408 100644 --- a/resources/types/generated.d.ts +++ b/resources/types/generated.d.ts @@ -492,6 +492,7 @@ declare namespace App.Data.Web3 { mintedBlock: number; mintedAt: string | null; hasError: boolean | null; + error: string | null; }; export type Web3NftTransfer = { contractAddress: string; From d8dd2af35e7b9663e5e812684eb4015439a789b6 Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Tue, 17 Oct 2023 06:14:08 -0500 Subject: [PATCH 19/41] feat: enum for nft errors --- app/Enums/NftErrors.php | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 app/Enums/NftErrors.php diff --git a/app/Enums/NftErrors.php b/app/Enums/NftErrors.php new file mode 100644 index 000000000..a95cd45a6 --- /dev/null +++ b/app/Enums/NftErrors.php @@ -0,0 +1,10 @@ + Date: Tue, 17 Oct 2023 06:16:03 -0500 Subject: [PATCH 20/41] feat: set metadata outdated error if nft has no metadata --- app/Http/Client/Alchemy/AlchemyPendingRequest.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/Http/Client/Alchemy/AlchemyPendingRequest.php b/app/Http/Client/Alchemy/AlchemyPendingRequest.php index 3376d32db..8892ab2d9 100644 --- a/app/Http/Client/Alchemy/AlchemyPendingRequest.php +++ b/app/Http/Client/Alchemy/AlchemyPendingRequest.php @@ -13,6 +13,7 @@ use App\Enums\Chains; use App\Enums\CryptoCurrencyDecimals; use App\Enums\ImageSize; +use App\Enums\NftErrors; use App\Enums\TokenType; use App\Enums\TraitDisplayType; use App\Exceptions\ConnectionException; @@ -397,6 +398,7 @@ public function parseNft(array $nft, int $networkId, bool $convertTokenNumber = $tokenNumber = $convertTokenNumber === true ? CryptoUtils::hexToBigIntStr($nft['id']['tokenId']) : $nft['id']['tokenId']; $error = Arr::get($nft, 'error'); + $nftError = null; $collectionAddress = Arr::get($nft, 'contract.address'); if (! empty($error)) { @@ -406,6 +408,11 @@ public function parseNft(array $nft, int $networkId, bool $convertTokenNumber = 'collection_address' => $collectionAddress, 'nft_id' => $tokenNumber, ]); + + // if metadata stuff is empty, is empty object or empty array + if (empty($nft['metadata']) || empty($nft['metadata']['metadata']) || is_object($nft['metadata']['metadata']) && count((array)$nft['metadata']['metadata'])) { + $nftError = NftErrors::MetadataOutdated->value; + } } return new Web3NftData( @@ -435,6 +442,7 @@ traits: $this->extractTraits($nft), mintedBlock: $nft['contractMetadata']['deployedBlockNumber'], mintedAt: $mintTimestamp !== null ? Carbon::createFromTimestampMs($mintTimestamp) : null, hasError: ! empty($error), + error: $nftError, ); } From fe21157b16dc14d3c964a3698b5b9e362671d973 Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Tue, 17 Oct 2023 06:16:33 -0500 Subject: [PATCH 21/41] fix: implementations of Web3NftData --- app/Http/Client/Moralis/MoralisPendingRequest.php | 1 + database/seeders/LiveUserSeeder.php | 1 + tests/App/Jobs/DetermineCollectionMintingDateTest.php | 4 ++++ tests/App/Support/Web3NftHandlerTest.php | 10 ++++++++++ 4 files changed, 16 insertions(+) diff --git a/app/Http/Client/Moralis/MoralisPendingRequest.php b/app/Http/Client/Moralis/MoralisPendingRequest.php index d49f0085d..95cab07ef 100644 --- a/app/Http/Client/Moralis/MoralisPendingRequest.php +++ b/app/Http/Client/Moralis/MoralisPendingRequest.php @@ -171,6 +171,7 @@ traits: [], mintedBlock: (int) $nft['block_number_minted'], mintedAt: null, hasError: false, + error: null, ); })->values(); diff --git a/database/seeders/LiveUserSeeder.php b/database/seeders/LiveUserSeeder.php index 28bf3a296..38532eb3c 100644 --- a/database/seeders/LiveUserSeeder.php +++ b/database/seeders/LiveUserSeeder.php @@ -174,6 +174,7 @@ traits: array_map(fn ($trait) => [ mintedAt: Carbon::parse($nft['mintedAt']), mintedBlock: $nft['mintedBlock'], hasError: false, + error: null, )); } diff --git a/tests/App/Jobs/DetermineCollectionMintingDateTest.php b/tests/App/Jobs/DetermineCollectionMintingDateTest.php index 582a47f9b..960911852 100644 --- a/tests/App/Jobs/DetermineCollectionMintingDateTest.php +++ b/tests/App/Jobs/DetermineCollectionMintingDateTest.php @@ -54,6 +54,7 @@ traits: [], mintedBlock: 1000, mintedAt: null, hasError: false, + error: null, ); $job = new DetermineCollectionMintingDate($nft); @@ -105,6 +106,7 @@ traits: [], mintedBlock: 1000, mintedAt: null, hasError: false, + error: null, ); $job = new DetermineCollectionMintingDate($nft); @@ -139,6 +141,7 @@ traits: [], mintedBlock: 1000, mintedAt: null, hasError: false, + error: null, ); $job = new DetermineCollectionMintingDate($nft); @@ -171,6 +174,7 @@ traits: [], mintedBlock: 1000, mintedAt: null, hasError: false, + error: null, ); $job = new DetermineCollectionMintingDate($nft); diff --git a/tests/App/Support/Web3NftHandlerTest.php b/tests/App/Support/Web3NftHandlerTest.php index 35d47d117..b574e2716 100644 --- a/tests/App/Support/Web3NftHandlerTest.php +++ b/tests/App/Support/Web3NftHandlerTest.php @@ -49,6 +49,7 @@ traits: [], mintedBlock: 1000, mintedAt: null, hasError: false, + error: null, ); $handler->store(collect([$data])); @@ -90,6 +91,7 @@ traits: [], mintedBlock: 1000, mintedAt: null, hasError: false, + error: null, ); $handler->store(nfts: collect([$data]), dispatchJobs: true); @@ -138,6 +140,7 @@ traits: [ mintedBlock: 1000, mintedAt: null, hasError: false, + error: null, ); $collection = Collection::query()->create([ @@ -207,6 +210,7 @@ traits: [], mintedBlock: 1000, mintedAt: null, hasError: false, + error: null, ); $collection = Collection::query()->create([ @@ -274,6 +278,7 @@ traits: [], mintedBlock: 1000, mintedAt: null, hasError: false, + error: null, ); $collection = Collection::query()->create([ @@ -339,6 +344,7 @@ traits: [], mintedBlock: 1000, mintedAt: null, hasError: false, + error: null, ); $collection = Collection::query()->create([ @@ -416,6 +422,7 @@ traits: [], mintedBlock: 1000, mintedAt: null, hasError: false, + error: null, ); $collection = Collection::query()->create([ @@ -479,6 +486,7 @@ traits: [], mintedBlock: 1000, mintedAt: null, hasError: false, + error: null, ); $collection = Collection::query()->create([ @@ -555,6 +563,7 @@ traits: [], mintedBlock: 1000, mintedAt: null, hasError: false, + error: null, ); $collection = Collection::query()->create([ @@ -600,6 +609,7 @@ traits: [], mintedBlock: 1000, mintedAt: null, hasError: true, + error: null, ); $handler->store(collect([$dataWithError])); From 3f8918bf4b8e12be6036c27e4824a9cc9ebdf833 Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Tue, 17 Oct 2023 06:16:53 -0500 Subject: [PATCH 22/41] test: nft array with errors and empty metadata object --- .../Alchemy/AlchemyPendingRequestTest.php | 15 ++++++ ..._with_error_and_empty_metadata_object.json | 53 +++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 tests/fixtures/alchemy/nfts_array_with_error_and_empty_metadata_object.json diff --git a/tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php b/tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php index b23116085..e6f997509 100644 --- a/tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php +++ b/tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php @@ -156,3 +156,18 @@ expect($fetchedNfts->nfts)->toHaveCount(0); }); + +it('should return error field with METADATA_OUTATED if metadata object is empty', function () { + Alchemy::fake([ + 'https://polygon-mainnet.g.alchemy.com/nft/v2/*' => Http::sequence() + ->push(fixtureData('alchemy.nfts_array_with_error_and_empty_metadata_object'), 200), + ]); + + $wallet = Wallet::factory()->create(); + $network = Network::polygon(); + + $collection = Alchemy::getWalletNfts($wallet, $network); + print_r($collection); + expect($collection->nfts[0]->hasError)->toBetrue(); + expect($collection->nfts[0]->error)->toBe('METADATA_OUTDATED'); +}); diff --git a/tests/fixtures/alchemy/nfts_array_with_error_and_empty_metadata_object.json b/tests/fixtures/alchemy/nfts_array_with_error_and_empty_metadata_object.json new file mode 100644 index 000000000..160107f48 --- /dev/null +++ b/tests/fixtures/alchemy/nfts_array_with_error_and_empty_metadata_object.json @@ -0,0 +1,53 @@ +{ + "ownedNfts": [ + { + "contract": { + "address": "0x54e10253619e8790e162b5517aec518ad0676ef1" + }, + "id": { + "tokenId": "0x0000000000000000000000000000000000000000000000000000000000000051", + "tokenMetadata": { + "tokenType": "ERC721" + } + }, + "balance": "1", + "title": "", + "description": "", + "tokenUri": { + "gateway": "https://metadataapi.codename883.repl.co/apiA4C/81", + "raw": "https://metadataapi.codename883.repl.co/apiA4C/81" + }, + "media": [ + { + "gateway": "", + "raw": "" + } + ], + "metadata": { + "metadata": {}, + "attributes": [] + }, + "timeLastUpdated": "2023-10-10T10:18:02.421Z", + "error": "Contract returned a broken token uri", + "contractMetadata": { + "name": "Apes For Change", + "symbol": "A4C", + "totalSupply": "3480", + "tokenType": "ERC721", + "contractDeployer": "0xb817b0137bb1a838aebcc63a231d152c6cb03b41", + "deployedBlockNumber": 15319472, + "openSea": { + "floorPrice": 0.001, + "collectionName": "Apes For Change: A CODENAMEhamm Joint", + "collectionSlug": "apesforchange", + "safelistRequestStatus": "not_requested", + "imageUrl": "https://i.seadn.io/gcs/files/e64de844a9f282b896da7507f8ad31f8.png?w=500&auto=format", + "description": "Apes For Change was created to pay tribute to one of the greatest communities in the NFT space. The artwork was reimagined, the traits were reshuffled, and the collection was minted exclusively with $APE. A CODENAME:hamm Joint.", + "externalUrl": "https://apes-for-change.webflow.io/", + "bannerImageUrl": "https://i.seadn.io/gcs/files/686d50de759a7acb244655596962b457.png?w=500&auto=format", + "lastIngestedAt": "2023-10-07T04:53:52.000Z" + } + } + } + ] +} From 7507980ad22ac266daeb1d9aeb19ee647fab89a8 Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Tue, 17 Oct 2023 06:17:00 -0500 Subject: [PATCH 23/41] fix: implementations of Web3NftData --- app/Services/Web3/Fake/FakeWeb3DataProvider.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Services/Web3/Fake/FakeWeb3DataProvider.php b/app/Services/Web3/Fake/FakeWeb3DataProvider.php index 9378e00cb..fcd80cc51 100644 --- a/app/Services/Web3/Fake/FakeWeb3DataProvider.php +++ b/app/Services/Web3/Fake/FakeWeb3DataProvider.php @@ -87,6 +87,7 @@ traits: [], mintedBlock: random_int(1, 10000), mintedAt: now(), hasError: false, + error: null, ); }); From 0219c9d7c77c60f26a20809b4835a663c344031e Mon Sep 17 00:00:00 2001 From: patricio0312rev Date: Tue, 17 Oct 2023 11:19:04 +0000 Subject: [PATCH 24/41] style: resolve style guide violations --- app/Http/Client/Alchemy/AlchemyPendingRequest.php | 2 +- .../2023_10_17_084040_add_error_column_to_nfts_table.php | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/Http/Client/Alchemy/AlchemyPendingRequest.php b/app/Http/Client/Alchemy/AlchemyPendingRequest.php index 8892ab2d9..ffe6e55be 100644 --- a/app/Http/Client/Alchemy/AlchemyPendingRequest.php +++ b/app/Http/Client/Alchemy/AlchemyPendingRequest.php @@ -410,7 +410,7 @@ public function parseNft(array $nft, int $networkId, bool $convertTokenNumber = ]); // if metadata stuff is empty, is empty object or empty array - if (empty($nft['metadata']) || empty($nft['metadata']['metadata']) || is_object($nft['metadata']['metadata']) && count((array)$nft['metadata']['metadata'])) { + if (empty($nft['metadata']) || empty($nft['metadata']['metadata']) || is_object($nft['metadata']['metadata']) && count((array) $nft['metadata']['metadata'])) { $nftError = NftErrors::MetadataOutdated->value; } } diff --git a/database/migrations/2023_10_17_084040_add_error_column_to_nfts_table.php b/database/migrations/2023_10_17_084040_add_error_column_to_nfts_table.php index 5b3c042ce..437a186bc 100644 --- a/database/migrations/2023_10_17_084040_add_error_column_to_nfts_table.php +++ b/database/migrations/2023_10_17_084040_add_error_column_to_nfts_table.php @@ -1,5 +1,7 @@ Date: Tue, 17 Oct 2023 06:49:02 -0500 Subject: [PATCH 25/41] feat: store error column for nft in Web3NftHandler --- app/Support/Web3NftHandler.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Support/Web3NftHandler.php b/app/Support/Web3NftHandler.php index 7b4cb3dfb..e7b4a4bbb 100644 --- a/app/Support/Web3NftHandler.php +++ b/app/Support/Web3NftHandler.php @@ -141,6 +141,7 @@ public function store(Collection $nfts, bool $dispatchJobs = false): void 'extra_attributes' => json_encode($nft->extraAttributes), 'deleted_at' => null, 'metadata_fetched_at' => $now, + 'error' => $nft->hasError ? $nft->error : null, ]; return $values; @@ -148,7 +149,7 @@ public function store(Collection $nfts, bool $dispatchJobs = false): void $uniqueBy = ['collection_id', 'token_number']; - $valuesToUpdateIfExists = ['deleted_at']; + $valuesToUpdateIfExists = ['deleted_at', 'error']; $valuesToCheck = ['name', 'extra_attributes', 'metadata_fetched_at', 'wallet_id']; foreach ($valuesToCheck as $value) { From 733edeffb1417b3b0172bdcae7595aa1e1e0835a Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Tue, 17 Oct 2023 06:49:17 -0500 Subject: [PATCH 26/41] fix: use of NftErrors enum --- tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php b/tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php index e6f997509..95231873a 100644 --- a/tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php +++ b/tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php @@ -2,6 +2,7 @@ declare(strict_types=1); +use App\Enums\NftErrors; use App\Exceptions\ConnectionException; use App\Exceptions\RateLimitException; use App\Models\Collection; @@ -169,5 +170,5 @@ $collection = Alchemy::getWalletNfts($wallet, $network); print_r($collection); expect($collection->nfts[0]->hasError)->toBetrue(); - expect($collection->nfts[0]->error)->toBe('METADATA_OUTDATED'); + expect($collection->nfts[0]->error)->toBe(NftErrors::MetadataOutdated->value); }); From 11ee6c9dfca92922f36abe398d9b667630aaa283 Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Tue, 17 Oct 2023 06:49:33 -0500 Subject: [PATCH 27/41] test: Web3NftHandler --- tests/App/Support/Web3NftHandlerTest.php | 169 +++++++++++++++++++++++ 1 file changed, 169 insertions(+) diff --git a/tests/App/Support/Web3NftHandlerTest.php b/tests/App/Support/Web3NftHandlerTest.php index b574e2716..f4b72d835 100644 --- a/tests/App/Support/Web3NftHandlerTest.php +++ b/tests/App/Support/Web3NftHandlerTest.php @@ -3,6 +3,7 @@ declare(strict_types=1); use App\Data\Web3\Web3NftData; +use App\Enums\NftErrors; use App\Enums\TraitDisplayType; use App\Models\Collection; use App\Models\Network; @@ -626,3 +627,171 @@ traits: [], expect($collection->extra_attributes[0]['opensea_slug'])->toBe($originalData->collectionOpenSeaSlug); expect($collection->nfts->first()->name)->toBe($originalData->name); }); + +it('should set save the error for the nft if set', function () { + Bus::fake(); + + $handler = new Web3NftHandler(); + + $network = Network::firstWhere('chain_id', 1); + + $token = Token::factory()->create([ + 'network_id' => $network->id, + ]); + + $wallet = Wallet::factory()->create(); + + $handler = new Web3NftHandler( + network: $network, + wallet: $wallet, + ); + + $data = new Web3NftData( + tokenAddress: '0x1234', + tokenNumber: '123', + networkId: $token->network_id, + collectionName: 'Collection Name', + collectionSymbol: 'AME', + collectionImage: null, + collectionWebsite: null, + collectionDescription: null, + collectionSocials: null, + collectionSupply: null, + collectionBannerImageUrl: null, + collectionBannerUpdatedAt: null, + collectionOpenSeaSlug: 'test456', + name: null, + description: null, + extraAttributes: [], + floorPrice: null, + traits: [], + mintedBlock: 1000, + mintedAt: null, + hasError: true, + error: NftErrors::MetadataOutdated->value, + ); + + $collection = Collection::query()->create([ + 'address' => '0x1234', + 'network_id' => $network->id, + 'name' => $data->collectionName, + 'slug' => Str::slug($data->collectionName), + 'symbol' => $data->collectionSymbol, + 'minted_block' => $data->mintedBlock, + 'extra_attributes' => null, + ]); + + $handler->store(collect([$data])); + + expect($collection->nfts->first()->error)->toBe(NftErrors::MetadataOutdated->value); +}); + +it('should update the error field for nft', function () { + Bus::fake(); + + $handler = new Web3NftHandler(); + + $network = Network::firstWhere('chain_id', 1); + + $token = Token::factory()->create([ + 'network_id' => $network->id, + ]); + + $wallet = Wallet::factory()->create(); + + $handler = new Web3NftHandler( + network: $network, + wallet: $wallet, + ); + + // originalData is the data that is already in the DB + $now = Carbon::now(); + $dataWithError = new Web3NftData( + tokenAddress: '0x1234', + tokenNumber: '123', + networkId: $token->network_id, + collectionName: 'Collection Name', + collectionSymbol: 'AME', + collectionImage: 'test_image', + collectionWebsite: 'www.test_website.com', + collectionDescription: 'Collection Description', + collectionSocials: [ + [ + 'name' => 'twitter', + 'url' => 'https://twitter.com/test', + ], + ], + collectionSupply: 3000, + collectionBannerImageUrl: 'test_banner', + collectionBannerUpdatedAt: $now, + collectionOpenSeaSlug: 'test456', + name: 'NFT Name', + description: 'NFT Description', + extraAttributes: [ + 'image' => 'test_image', + 'website' => 'www.test_website.com', + 'banner' => 'test_banner', + 'banner_updated_at' => $now, + 'opensea_slug' => 'test456', + ], + floorPrice: null, + traits: [], + mintedBlock: 1000, + mintedAt: null, + hasError: true, + error: NftErrors::MetadataOutdated->value, + ); + + $collection = Collection::query()->create([ + 'address' => '0x1234', + 'network_id' => $network->id, + 'name' => $dataWithError->collectionName, + 'slug' => Str::slug($dataWithError->collectionName), + 'symbol' => $dataWithError->collectionSymbol, + 'minted_block' => $dataWithError->mintedBlock, + 'extra_attributes' => null, + ]); + + $handler->store(collect([$dataWithError])); + $collection->refresh(); + + expect(Collection::count())->toBe(1); + expect($collection->nfts->first()->error)->toBe(NftErrors::MetadataOutdated->value); + + $dataWithNoError = new Web3NftData( + tokenAddress: '0x1234', + tokenNumber: '123', + networkId: $token->network_id, + collectionName: 'Collection Name', + collectionSymbol: 'AME', + collectionImage: null, + collectionWebsite: null, + collectionDescription: null, + collectionSocials: null, + collectionSupply: 3000, + collectionBannerImageUrl: null, + collectionBannerUpdatedAt: $now, + collectionOpenSeaSlug: null, + name: null, + description: null, + extraAttributes: [ + 'image' => null, + 'website' => null, + 'banner' => null, + 'banner_updated_at' => $now, + 'opensea_slug' => null, + ], + floorPrice: null, + traits: [], + mintedBlock: 1000, + mintedAt: null, + hasError: true, + error: null, + ); + + $handler->store(collect([$dataWithNoError])); + $collection->refresh(); + + expect(Collection::count())->toBe(1); + expect($collection->nfts->first()->error)->toBe(null); +}); From b18a74fa15a679fc3c615f31747e5cdb1143f012 Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Wed, 18 Oct 2023 11:08:34 -0500 Subject: [PATCH 28/41] fix: remove reverse migration --- ...023_10_17_084040_add_error_column_to_nfts_table.php | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/database/migrations/2023_10_17_084040_add_error_column_to_nfts_table.php b/database/migrations/2023_10_17_084040_add_error_column_to_nfts_table.php index 437a186bc..9831c3389 100644 --- a/database/migrations/2023_10_17_084040_add_error_column_to_nfts_table.php +++ b/database/migrations/2023_10_17_084040_add_error_column_to_nfts_table.php @@ -17,14 +17,4 @@ public function up(): void $table->string('error')->nullable(); }); } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::table('nfts', function (Blueprint $table) { - $table->dropColumn('error'); - }); - } }; From 53f3fae18682e4ac271a241f0f1a78c1d05713e2 Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Wed, 18 Oct 2023 11:17:01 -0500 Subject: [PATCH 29/41] feat: rename NftErrors to NftInfo --- app/Enums/{NftErrors.php => NftInfo.php} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename app/Enums/{NftErrors.php => NftInfo.php} (82%) diff --git a/app/Enums/NftErrors.php b/app/Enums/NftInfo.php similarity index 82% rename from app/Enums/NftErrors.php rename to app/Enums/NftInfo.php index a95cd45a6..d39c838bd 100644 --- a/app/Enums/NftErrors.php +++ b/app/Enums/NftInfo.php @@ -4,7 +4,7 @@ namespace App\Enums; -enum NftErrors: string +enum NftInfo: string { case MetadataOutdated = 'METADATA_OUTDATED'; } From 031f018faa7f3e9ac1d7427f4f88b87f7e182139 Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Wed, 18 Oct 2023 11:17:42 -0500 Subject: [PATCH 30/41] feat: rename migration to create info column --- ....php => 2023_10_17_084040_add_info_column_to_nfts_table.php} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename database/migrations/{2023_10_17_084040_add_error_column_to_nfts_table.php => 2023_10_17_084040_add_info_column_to_nfts_table.php} (87%) diff --git a/database/migrations/2023_10_17_084040_add_error_column_to_nfts_table.php b/database/migrations/2023_10_17_084040_add_info_column_to_nfts_table.php similarity index 87% rename from database/migrations/2023_10_17_084040_add_error_column_to_nfts_table.php rename to database/migrations/2023_10_17_084040_add_info_column_to_nfts_table.php index 9831c3389..7bf14f7a9 100644 --- a/database/migrations/2023_10_17_084040_add_error_column_to_nfts_table.php +++ b/database/migrations/2023_10_17_084040_add_info_column_to_nfts_table.php @@ -14,7 +14,7 @@ public function up(): void { Schema::table('nfts', function (Blueprint $table) { - $table->string('error')->nullable(); + $table->string('info')->nullable(); }); } }; From 9a2dd91cb4a90058f61f53e44892d3f8b6ad2d6e Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Wed, 18 Oct 2023 11:18:02 -0500 Subject: [PATCH 31/41] feat: update Web3NftData structure --- app/Data/Web3/Web3NftData.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Data/Web3/Web3NftData.php b/app/Data/Web3/Web3NftData.php index c78126a65..403e28514 100644 --- a/app/Data/Web3/Web3NftData.php +++ b/app/Data/Web3/Web3NftData.php @@ -41,7 +41,7 @@ public function __construct( public int $mintedBlock, public ?Carbon $mintedAt, public ?bool $hasError, - public ?string $error, + public ?string $info, ) { } From ce8595ef1770c923ba7efc3b718a47a60b173e35 Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Wed, 18 Oct 2023 11:18:16 -0500 Subject: [PATCH 32/41] chore: update typing --- resources/types/generated.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/types/generated.d.ts b/resources/types/generated.d.ts index 40ce03408..ffb69ee12 100644 --- a/resources/types/generated.d.ts +++ b/resources/types/generated.d.ts @@ -492,7 +492,7 @@ declare namespace App.Data.Web3 { mintedBlock: number; mintedAt: string | null; hasError: boolean | null; - error: string | null; + info: string | null; }; export type Web3NftTransfer = { contractAddress: string; From 69abde4b236877947bb87b2b5aeefcbf9f08e26b Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Wed, 18 Oct 2023 11:18:38 -0500 Subject: [PATCH 33/41] fix: update AlchemyPendingRequest imports --- app/Http/Client/Alchemy/AlchemyPendingRequest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/Http/Client/Alchemy/AlchemyPendingRequest.php b/app/Http/Client/Alchemy/AlchemyPendingRequest.php index ffe6e55be..b38d8af59 100644 --- a/app/Http/Client/Alchemy/AlchemyPendingRequest.php +++ b/app/Http/Client/Alchemy/AlchemyPendingRequest.php @@ -13,7 +13,7 @@ use App\Enums\Chains; use App\Enums\CryptoCurrencyDecimals; use App\Enums\ImageSize; -use App\Enums\NftErrors; +use App\Enums\NftInfo; use App\Enums\TokenType; use App\Enums\TraitDisplayType; use App\Exceptions\ConnectionException; @@ -398,7 +398,7 @@ public function parseNft(array $nft, int $networkId, bool $convertTokenNumber = $tokenNumber = $convertTokenNumber === true ? CryptoUtils::hexToBigIntStr($nft['id']['tokenId']) : $nft['id']['tokenId']; $error = Arr::get($nft, 'error'); - $nftError = null; + $nftInfo = null; $collectionAddress = Arr::get($nft, 'contract.address'); if (! empty($error)) { @@ -411,7 +411,7 @@ public function parseNft(array $nft, int $networkId, bool $convertTokenNumber = // if metadata stuff is empty, is empty object or empty array if (empty($nft['metadata']) || empty($nft['metadata']['metadata']) || is_object($nft['metadata']['metadata']) && count((array) $nft['metadata']['metadata'])) { - $nftError = NftErrors::MetadataOutdated->value; + $nftInfo = NftInfo::MetadataOutdated->value; } } @@ -442,7 +442,7 @@ traits: $this->extractTraits($nft), mintedBlock: $nft['contractMetadata']['deployedBlockNumber'], mintedAt: $mintTimestamp !== null ? Carbon::createFromTimestampMs($mintTimestamp) : null, hasError: ! empty($error), - error: $nftError, + info: $nftInfo, ); } From 85de6ee143177049f84aef2a74cefb8e21b3b2a4 Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Wed, 18 Oct 2023 11:18:55 -0500 Subject: [PATCH 34/41] fix: Web3NftHandlerTest --- tests/App/Support/Web3NftHandlerTest.php | 34 ++++++++++++------------ 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/tests/App/Support/Web3NftHandlerTest.php b/tests/App/Support/Web3NftHandlerTest.php index f4b72d835..44db4d418 100644 --- a/tests/App/Support/Web3NftHandlerTest.php +++ b/tests/App/Support/Web3NftHandlerTest.php @@ -3,7 +3,7 @@ declare(strict_types=1); use App\Data\Web3\Web3NftData; -use App\Enums\NftErrors; +use App\Enums\NftInfo; use App\Enums\TraitDisplayType; use App\Models\Collection; use App\Models\Network; @@ -50,7 +50,7 @@ traits: [], mintedBlock: 1000, mintedAt: null, hasError: false, - error: null, + info: null, ); $handler->store(collect([$data])); @@ -92,7 +92,7 @@ traits: [], mintedBlock: 1000, mintedAt: null, hasError: false, - error: null, + info: null, ); $handler->store(nfts: collect([$data]), dispatchJobs: true); @@ -141,7 +141,7 @@ traits: [ mintedBlock: 1000, mintedAt: null, hasError: false, - error: null, + info: null, ); $collection = Collection::query()->create([ @@ -211,7 +211,7 @@ traits: [], mintedBlock: 1000, mintedAt: null, hasError: false, - error: null, + info: null, ); $collection = Collection::query()->create([ @@ -279,7 +279,7 @@ traits: [], mintedBlock: 1000, mintedAt: null, hasError: false, - error: null, + info: null, ); $collection = Collection::query()->create([ @@ -345,7 +345,7 @@ traits: [], mintedBlock: 1000, mintedAt: null, hasError: false, - error: null, + info: null, ); $collection = Collection::query()->create([ @@ -423,7 +423,7 @@ traits: [], mintedBlock: 1000, mintedAt: null, hasError: false, - error: null, + info: null, ); $collection = Collection::query()->create([ @@ -487,7 +487,7 @@ traits: [], mintedBlock: 1000, mintedAt: null, hasError: false, - error: null, + info: null, ); $collection = Collection::query()->create([ @@ -564,7 +564,7 @@ traits: [], mintedBlock: 1000, mintedAt: null, hasError: false, - error: null, + info: null, ); $collection = Collection::query()->create([ @@ -610,7 +610,7 @@ traits: [], mintedBlock: 1000, mintedAt: null, hasError: true, - error: null, + info: null, ); $handler->store(collect([$dataWithError])); @@ -668,7 +668,7 @@ traits: [], mintedBlock: 1000, mintedAt: null, hasError: true, - error: NftErrors::MetadataOutdated->value, + info: NftInfo::MetadataOutdated->value, ); $collection = Collection::query()->create([ @@ -683,7 +683,7 @@ traits: [], $handler->store(collect([$data])); - expect($collection->nfts->first()->error)->toBe(NftErrors::MetadataOutdated->value); + expect($collection->nfts->first()->info)->toBe(NftInfo::MetadataOutdated->value); }); it('should update the error field for nft', function () { @@ -739,7 +739,7 @@ traits: [], mintedBlock: 1000, mintedAt: null, hasError: true, - error: NftErrors::MetadataOutdated->value, + info: NftInfo::MetadataOutdated->value, ); $collection = Collection::query()->create([ @@ -756,7 +756,7 @@ traits: [], $collection->refresh(); expect(Collection::count())->toBe(1); - expect($collection->nfts->first()->error)->toBe(NftErrors::MetadataOutdated->value); + expect($collection->nfts->first()->info)->toBe(NftInfo::MetadataOutdated->value); $dataWithNoError = new Web3NftData( tokenAddress: '0x1234', @@ -786,12 +786,12 @@ traits: [], mintedBlock: 1000, mintedAt: null, hasError: true, - error: null, + info: null, ); $handler->store(collect([$dataWithNoError])); $collection->refresh(); expect(Collection::count())->toBe(1); - expect($collection->nfts->first()->error)->toBe(null); + expect($collection->nfts->first()->info)->toBe(null); }); From 3485da3a2304060104a3b7cb5f211ad64054b67f Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Wed, 18 Oct 2023 11:19:19 -0500 Subject: [PATCH 35/41] fix: AlchemyPendingRequestTest --- tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php b/tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php index 95231873a..45f67d752 100644 --- a/tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php +++ b/tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -use App\Enums\NftErrors; +use App\Enums\NftInfo; use App\Exceptions\ConnectionException; use App\Exceptions\RateLimitException; use App\Models\Collection; @@ -170,5 +170,5 @@ $collection = Alchemy::getWalletNfts($wallet, $network); print_r($collection); expect($collection->nfts[0]->hasError)->toBetrue(); - expect($collection->nfts[0]->error)->toBe(NftErrors::MetadataOutdated->value); + expect($collection->nfts[0]->info)->toBe(NftInfo::MetadataOutdated->value); }); From 39d57816fcc07a0e954befb61b382636c5663f3a Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Wed, 18 Oct 2023 11:19:56 -0500 Subject: [PATCH 36/41] chore: rename error atribute to info --- app/Http/Client/Moralis/MoralisPendingRequest.php | 2 +- app/Services/Web3/Fake/FakeWeb3DataProvider.php | 2 +- database/seeders/LiveUserSeeder.php | 2 +- tests/App/Jobs/DetermineCollectionMintingDateTest.php | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/Http/Client/Moralis/MoralisPendingRequest.php b/app/Http/Client/Moralis/MoralisPendingRequest.php index 95cab07ef..7e16a5949 100644 --- a/app/Http/Client/Moralis/MoralisPendingRequest.php +++ b/app/Http/Client/Moralis/MoralisPendingRequest.php @@ -171,7 +171,7 @@ traits: [], mintedBlock: (int) $nft['block_number_minted'], mintedAt: null, hasError: false, - error: null, + info: null, ); })->values(); diff --git a/app/Services/Web3/Fake/FakeWeb3DataProvider.php b/app/Services/Web3/Fake/FakeWeb3DataProvider.php index fcd80cc51..6fc20fb4b 100644 --- a/app/Services/Web3/Fake/FakeWeb3DataProvider.php +++ b/app/Services/Web3/Fake/FakeWeb3DataProvider.php @@ -87,7 +87,7 @@ traits: [], mintedBlock: random_int(1, 10000), mintedAt: now(), hasError: false, - error: null, + info: null, ); }); diff --git a/database/seeders/LiveUserSeeder.php b/database/seeders/LiveUserSeeder.php index 38532eb3c..597c245aa 100644 --- a/database/seeders/LiveUserSeeder.php +++ b/database/seeders/LiveUserSeeder.php @@ -174,7 +174,7 @@ traits: array_map(fn ($trait) => [ mintedAt: Carbon::parse($nft['mintedAt']), mintedBlock: $nft['mintedBlock'], hasError: false, - error: null, + info: null, )); } diff --git a/tests/App/Jobs/DetermineCollectionMintingDateTest.php b/tests/App/Jobs/DetermineCollectionMintingDateTest.php index 960911852..24eb9ef97 100644 --- a/tests/App/Jobs/DetermineCollectionMintingDateTest.php +++ b/tests/App/Jobs/DetermineCollectionMintingDateTest.php @@ -54,7 +54,7 @@ traits: [], mintedBlock: 1000, mintedAt: null, hasError: false, - error: null, + info: null, ); $job = new DetermineCollectionMintingDate($nft); @@ -106,7 +106,7 @@ traits: [], mintedBlock: 1000, mintedAt: null, hasError: false, - error: null, + info: null, ); $job = new DetermineCollectionMintingDate($nft); @@ -141,7 +141,7 @@ traits: [], mintedBlock: 1000, mintedAt: null, hasError: false, - error: null, + info: null, ); $job = new DetermineCollectionMintingDate($nft); @@ -174,7 +174,7 @@ traits: [], mintedBlock: 1000, mintedAt: null, hasError: false, - error: null, + info: null, ); $job = new DetermineCollectionMintingDate($nft); From 980c3bf37d33724ead839576aca21c8e6a31fe4e Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Wed, 18 Oct 2023 11:20:11 -0500 Subject: [PATCH 37/41] feat: update logic to store info field in db --- app/Support/Web3NftHandler.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Support/Web3NftHandler.php b/app/Support/Web3NftHandler.php index e7b4a4bbb..8f6c6c431 100644 --- a/app/Support/Web3NftHandler.php +++ b/app/Support/Web3NftHandler.php @@ -141,7 +141,7 @@ public function store(Collection $nfts, bool $dispatchJobs = false): void 'extra_attributes' => json_encode($nft->extraAttributes), 'deleted_at' => null, 'metadata_fetched_at' => $now, - 'error' => $nft->hasError ? $nft->error : null, + 'info' => $nft->hasError ? $nft->info : null, ]; return $values; @@ -149,7 +149,7 @@ public function store(Collection $nfts, bool $dispatchJobs = false): void $uniqueBy = ['collection_id', 'token_number']; - $valuesToUpdateIfExists = ['deleted_at', 'error']; + $valuesToUpdateIfExists = ['deleted_at', 'info']; $valuesToCheck = ['name', 'extra_attributes', 'metadata_fetched_at', 'wallet_id']; foreach ($valuesToCheck as $value) { From b7f1a6401e2a018304fe57c5472422e9d68dc20c Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Wed, 18 Oct 2023 11:25:43 -0500 Subject: [PATCH 38/41] fix: rector issues --- app/Support/Web3NftHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Support/Web3NftHandler.php b/app/Support/Web3NftHandler.php index 4e2ccce5a..5c151ccaf 100644 --- a/app/Support/Web3NftHandler.php +++ b/app/Support/Web3NftHandler.php @@ -137,7 +137,7 @@ public function store(Collection $nfts, bool $dispatchJobs = false): void 'wallet_id' => $this->wallet?->id, 'collection_id' => $collection->id, 'token_number' => $nft->tokenNumber, - 'description' => $nft->description + 'description' => $nft->description, 'name' => $nft->name, 'extra_attributes' => json_encode($nft->extraAttributes), 'deleted_at' => null, From 1a5510dabca53846248ebb8f78c385b4fb22802d Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Thu, 19 Oct 2023 04:15:42 -0500 Subject: [PATCH 39/41] fix: update collectionName in parseNft --- app/Http/Client/Alchemy/AlchemyPendingRequest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Client/Alchemy/AlchemyPendingRequest.php b/app/Http/Client/Alchemy/AlchemyPendingRequest.php index fbc84f298..4a328e2b5 100644 --- a/app/Http/Client/Alchemy/AlchemyPendingRequest.php +++ b/app/Http/Client/Alchemy/AlchemyPendingRequest.php @@ -366,7 +366,7 @@ public function getContractMetadataBatch(array $contactAddresses, Network $netwo public function parseNft(array $nft, int $networkId, bool $convertTokenNumber = true): Web3NftData { $extractedFloorPrice = $this->tryExtractFloorPrice($nft); - $collectionName = $this->collectionName($nft) ?? Arr::get($nft, 'contractMetadata.symbol'); + $collectionName = $this->collectionName($nft); $description = $nft['description'] ?? null; $supply = Arr::get($nft, 'contractMetadata.totalSupply'); if (is_numeric($supply)) { From a40c8c0bf4518095d9b24e0001714af718966e4d Mon Sep 17 00:00:00 2001 From: Patricio Marroquin <55117912+patricio0312rev@users.noreply.github.com> Date: Thu, 19 Oct 2023 05:15:02 -0500 Subject: [PATCH 40/41] Update tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php Co-authored-by: George Mamoulasvili --- tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php b/tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php index 45f67d752..efda08fec 100644 --- a/tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php +++ b/tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php @@ -158,7 +158,7 @@ expect($fetchedNfts->nfts)->toHaveCount(0); }); -it('should return error field with METADATA_OUTATED if metadata object is empty', function () { +it('should return error field with METADATA_OUTDATED if metadata object is empty', function () { Alchemy::fake([ 'https://polygon-mainnet.g.alchemy.com/nft/v2/*' => Http::sequence() ->push(fixtureData('alchemy.nfts_array_with_error_and_empty_metadata_object'), 200), From 2249c6341e5e7c9f771888cdc6fd16b7347bf1ff Mon Sep 17 00:00:00 2001 From: Juan Patricio Marroquin Date: Thu, 19 Oct 2023 06:21:20 -0500 Subject: [PATCH 41/41] fix: update conditions for setting nft info --- .../Client/Alchemy/AlchemyPendingRequest.php | 2 +- .../Alchemy/AlchemyPendingRequestTest.php | 19 ++++++- ...or_and_empty_metadata_metadata_object.json | 53 +++++++++++++++++++ ..._with_error_and_empty_metadata_object.json | 5 +- 4 files changed, 72 insertions(+), 7 deletions(-) create mode 100644 tests/fixtures/alchemy/nfts_array_with_error_and_empty_metadata_metadata_object.json diff --git a/app/Http/Client/Alchemy/AlchemyPendingRequest.php b/app/Http/Client/Alchemy/AlchemyPendingRequest.php index 4a328e2b5..ccaec44a6 100644 --- a/app/Http/Client/Alchemy/AlchemyPendingRequest.php +++ b/app/Http/Client/Alchemy/AlchemyPendingRequest.php @@ -414,7 +414,7 @@ public function parseNft(array $nft, int $networkId, bool $convertTokenNumber = ]); // if metadata stuff is empty, is empty object or empty array - if (empty($nft['metadata']) || empty($nft['metadata']['metadata']) || is_object($nft['metadata']['metadata']) && count((array) $nft['metadata']['metadata'])) { + if (empty($nft['metadata']) || empty($nft['metadata']['metadata'])) { $nftInfo = NftInfo::MetadataOutdated->value; } } diff --git a/tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php b/tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php index efda08fec..61aeff444 100644 --- a/tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php +++ b/tests/App/Http/Client/Alchemy/AlchemyPendingRequestTest.php @@ -158,7 +158,7 @@ expect($fetchedNfts->nfts)->toHaveCount(0); }); -it('should return error field with METADATA_OUTDATED if metadata object is empty', function () { +it('should return error field with METADATA_OUTDATED if parent metadata object is empty', function () { Alchemy::fake([ 'https://polygon-mainnet.g.alchemy.com/nft/v2/*' => Http::sequence() ->push(fixtureData('alchemy.nfts_array_with_error_and_empty_metadata_object'), 200), @@ -168,7 +168,22 @@ $network = Network::polygon(); $collection = Alchemy::getWalletNfts($wallet, $network); - print_r($collection); + + expect($collection->nfts[0]->hasError)->toBetrue(); + expect($collection->nfts[0]->info)->toBe(NftInfo::MetadataOutdated->value); +}); + +it('should return error field with METADATA_OUTDATED if metadata.metadata object is empty', function () { + Alchemy::fake([ + 'https://polygon-mainnet.g.alchemy.com/nft/v2/*' => Http::sequence() + ->push(fixtureData('alchemy.nfts_array_with_error_and_empty_metadata_metadata_object'), 200), + ]); + + $wallet = Wallet::factory()->create(); + $network = Network::polygon(); + + $collection = Alchemy::getWalletNfts($wallet, $network); + expect($collection->nfts[0]->hasError)->toBetrue(); expect($collection->nfts[0]->info)->toBe(NftInfo::MetadataOutdated->value); }); diff --git a/tests/fixtures/alchemy/nfts_array_with_error_and_empty_metadata_metadata_object.json b/tests/fixtures/alchemy/nfts_array_with_error_and_empty_metadata_metadata_object.json new file mode 100644 index 000000000..160107f48 --- /dev/null +++ b/tests/fixtures/alchemy/nfts_array_with_error_and_empty_metadata_metadata_object.json @@ -0,0 +1,53 @@ +{ + "ownedNfts": [ + { + "contract": { + "address": "0x54e10253619e8790e162b5517aec518ad0676ef1" + }, + "id": { + "tokenId": "0x0000000000000000000000000000000000000000000000000000000000000051", + "tokenMetadata": { + "tokenType": "ERC721" + } + }, + "balance": "1", + "title": "", + "description": "", + "tokenUri": { + "gateway": "https://metadataapi.codename883.repl.co/apiA4C/81", + "raw": "https://metadataapi.codename883.repl.co/apiA4C/81" + }, + "media": [ + { + "gateway": "", + "raw": "" + } + ], + "metadata": { + "metadata": {}, + "attributes": [] + }, + "timeLastUpdated": "2023-10-10T10:18:02.421Z", + "error": "Contract returned a broken token uri", + "contractMetadata": { + "name": "Apes For Change", + "symbol": "A4C", + "totalSupply": "3480", + "tokenType": "ERC721", + "contractDeployer": "0xb817b0137bb1a838aebcc63a231d152c6cb03b41", + "deployedBlockNumber": 15319472, + "openSea": { + "floorPrice": 0.001, + "collectionName": "Apes For Change: A CODENAMEhamm Joint", + "collectionSlug": "apesforchange", + "safelistRequestStatus": "not_requested", + "imageUrl": "https://i.seadn.io/gcs/files/e64de844a9f282b896da7507f8ad31f8.png?w=500&auto=format", + "description": "Apes For Change was created to pay tribute to one of the greatest communities in the NFT space. The artwork was reimagined, the traits were reshuffled, and the collection was minted exclusively with $APE. A CODENAME:hamm Joint.", + "externalUrl": "https://apes-for-change.webflow.io/", + "bannerImageUrl": "https://i.seadn.io/gcs/files/686d50de759a7acb244655596962b457.png?w=500&auto=format", + "lastIngestedAt": "2023-10-07T04:53:52.000Z" + } + } + } + ] +} diff --git a/tests/fixtures/alchemy/nfts_array_with_error_and_empty_metadata_object.json b/tests/fixtures/alchemy/nfts_array_with_error_and_empty_metadata_object.json index 160107f48..4c0474678 100644 --- a/tests/fixtures/alchemy/nfts_array_with_error_and_empty_metadata_object.json +++ b/tests/fixtures/alchemy/nfts_array_with_error_and_empty_metadata_object.json @@ -23,10 +23,7 @@ "raw": "" } ], - "metadata": { - "metadata": {}, - "attributes": [] - }, + "metadata": {}, "timeLastUpdated": "2023-10-10T10:18:02.421Z", "error": "Contract returned a broken token uri", "contractMetadata": {