Skip to content

Commit

Permalink
refactor: fetch activity for collection as opposed to individual nft (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
crnkovic authored Oct 13, 2023
1 parent 26b0bb1 commit bab3ce2
Show file tree
Hide file tree
Showing 35 changed files with 1,022 additions and 512 deletions.
53 changes: 53 additions & 0 deletions app/Console/Commands/FetchCollectionActivity.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

declare(strict_types=1);

namespace App\Console\Commands;

use App\Jobs\FetchCollectionActivity as Job;
use App\Models\Collection;
use Illuminate\Console\Command;
use Illuminate\Database\Eloquent\Builder;

class FetchCollectionActivity extends Command
{
use InteractsWithCollections;

/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'collections:fetch-activity {--collection-id=}';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Fetch collection activity for all collections';

/**
* Execute the console command.
*/
public function handle(): int
{
if (! config('dashbrd.features.activities')) {
return Command::SUCCESS;
}

// Modify the query to only fetch activities for collections that we index NFTs for...
$queryCallback = function (Builder $query) {
/** @var Builder<Collection> */
return $query->where('is_fetching_activity', false)
->whereNotNull('supply')
->where('supply', '<=', config('dashbrd.collections_max_cap'));
};

$this->forEachCollection(function ($collection) {
Job::dispatch($collection);
}, $queryCallback);

return Command::SUCCESS;
}
}
67 changes: 0 additions & 67 deletions app/Console/Commands/FetchNftActivity.php

This file was deleted.

6 changes: 6 additions & 0 deletions app/Console/Kernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace App\Console;

use App\Console\Commands\FetchCoingeckoTokens;
use App\Console\Commands\FetchCollectionActivity;
use App\Console\Commands\FetchCollectionBannerBatch;
use App\Console\Commands\FetchCollectionFloorPrice;
use App\Console\Commands\FetchCollectionMetadata;
Expand Down Expand Up @@ -105,6 +106,11 @@ private function scheduleJobsForCollectionsOrGalleries(Schedule $schedule): void
->withoutOverlapping()
->hourly();

$schedule
->command(FetchCollectionActivity::class)
->withoutOverlapping()
->weeklyOn(Schedule::MONDAY);

$schedule
->command(FetchCollectionFloorPrice::class)
->withoutOverlapping()
Expand Down
2 changes: 1 addition & 1 deletion app/Data/Gallery/GalleryNftData.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public static function fromModel(Nft $nft, bool $ownedByCurrentUser = false): se
floorPrice: $collection->floor_price,
floorPriceCurrency: $collection->floorPriceToken?->symbol,
floorPriceDecimals: $collection->floorPriceToken?->decimals,
lastActivityFetchedAt: $nft->last_activity_fetched_at,
lastActivityFetchedAt: $collection->activity_updated_at,
lastViewedAt: $nft->last_viewed_at,
ownedByCurrentUser: $ownedByCurrentUser,
);
Expand Down
2 changes: 1 addition & 1 deletion app/Data/Nfts/NftData.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public static function fromModel(Nft $nft): self
images: NftImagesData::from($nft->images()),
wallet: $nft->wallet_id ? NftWalletData::fromModel($nft->wallet) : null,
lastViewedAt: $nft->last_viewed_at,
lastActivityFetchedAt: $nft->last_activity_fetched_at,
lastActivityFetchedAt: $nft->collection->activity_updated_at,
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use Carbon\Carbon;
use Spatie\LaravelData\Data;

class Web3NftTransfer extends Data
class CollectionActivity extends Data
{
/**
* @param array<string, mixed> $extraAttributes
Expand All @@ -19,11 +19,22 @@ public function __construct(
public string $sender,
public string $recipient,
public string $txHash,
public NftTransferType $type,
public string $logIndex,
public ?NftTransferType $type,
public Carbon $timestamp,
public ?float $totalNative,
public ?float $totalUsd,
public array $extraAttributes,
) {
}

public function key(): string
{
return implode(':', [
$this->txHash,
$this->logIndex,
$this->tokenId,
$this->type->value,
]);
}
}
72 changes: 33 additions & 39 deletions app/Http/Client/Mnemonic/MnemonicPendingRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

namespace App\Http\Client\Mnemonic;

use App\Data\Web3\CollectionActivity;
use App\Data\Web3\Web3NftCollectionFloorPrice;
use App\Data\Web3\Web3NftCollectionTrait;
use App\Data\Web3\Web3NftTransfer;
use App\Enums\Chains;
use App\Enums\CryptoCurrencyDecimals;
use App\Enums\CurrencyCode;
Expand Down Expand Up @@ -346,24 +346,23 @@ private function fetchCollectionTraits(Chains $chain, string $contractAddress, s
/**
* @see https://docs.mnemonichq.com/reference/foundationalservice_getnfttransfers
*
* @return Collection<int, Web3NftTransfer>
* @return Collection<int, CollectionActivity>
*/
public function getNftActivity(Chains $chain, string $contractAddress, string $tokenId, int $limit, Carbon $from = null): Collection
public function getCollectionActivity(Chains $chain, string $contractAddress, int $limit, Carbon $from = null): Collection
{
$this->chain = MnemonicChain::fromChain($chain);

// grab the ETH token regardless of the chain, because always want to report prices in ETH
$ethToken = Token::whereHas('network', fn ($query) => $query
->where('chain_id', Chains::ETH->value)
->where('is_mainnet', true)
)->firstOrFail();
// Grab the ETH token regardless of the chain, because always want to report prices in ETH...
$ethToken = Token::query()
->whereHas('network', fn ($query) => $query->where('chain_id', Chains::ETH->value))
->where('is_native_token', true)
->firstOrFail();

$query = [
'limit' => $limit,
// Oldest first
'sortDirection' => 'SORT_DIRECTION_ASC',
'contractAddress' => $contractAddress,
'tokenId' => $tokenId,
];

// I cant pass the `labelsAny` filter directly to the query array because
Expand All @@ -382,36 +381,31 @@ public function getNftActivity(Chains $chain, string $contractAddress, string $t
/** @var array<string, mixed> $data */
$data = self::get(sprintf('/foundational/v1beta2/transfers/nft?%s', $labelsQuery), $query)->json('nftTransfers');

return collect($data)
// Sometimes the request is returning transfers that are not labeled
// as any of the values we expect, I was, for example getting
// `LABEL_BURN` transfers, so I am filtering them out here.
// In the future we may want to add support for them.
->filter(fn ($transfer) => $this->extractNftTransferType($transfer['labels']) !== null)
->map(function ($transfer) use ($chain, $contractAddress, $tokenId, $ethToken) {
$currency = CurrencyCode::USD;

$blockchainTimestamp = Carbon::parse($transfer['blockchainEvent']['blockTimestamp']);
$prices = $this->extractActivityPrices($chain, $transfer, $currency, $ethToken, $blockchainTimestamp);

return new Web3NftTransfer(
contractAddress: $contractAddress,
tokenId: $tokenId,
sender: $transfer['sender']['address'],
recipient: $transfer['recipient']['address'],
txHash: $transfer['blockchainEvent']['txHash'],
type: $this->extractNftTransferType($transfer['labels']),
timestamp: $blockchainTimestamp,
totalNative: $prices['native'],
totalUsd: $prices['usd'],
extraAttributes: [
'recipient' => Arr::get($transfer, 'recipient'),
'recipientPaid' => Arr::get($transfer, 'recipientPaid'),
'sender' => Arr::get($transfer, 'sender'),
'senderReceived' => Arr::get($transfer, 'senderReceived'),
]
);
})->values();
return collect($data)->map(function ($transfer) use ($chain, $contractAddress, $ethToken) {
$currency = CurrencyCode::USD;

$blockchainTimestamp = Carbon::parse($transfer['blockchainEvent']['blockTimestamp']);
$prices = $this->extractActivityPrices($chain, $transfer, $currency, $ethToken, $blockchainTimestamp);

return new CollectionActivity(
contractAddress: $contractAddress,
tokenId: $transfer['tokenId'],
sender: $transfer['sender']['address'],
recipient: $transfer['recipient']['address'],
txHash: $transfer['blockchainEvent']['txHash'],
logIndex: $transfer['blockchainEvent']['logIndex'],
type: $this->extractNftTransferType($transfer['labels']),
timestamp: $blockchainTimestamp,
totalNative: $prices['native'],
totalUsd: $prices['usd'],
extraAttributes: [
'recipient' => Arr::get($transfer, 'recipient'),
'recipientPaid' => Arr::get($transfer, 'recipientPaid'),
'sender' => Arr::get($transfer, 'sender'),
'senderReceived' => Arr::get($transfer, 'senderReceived'),
]
);
})->values();
}

/**
Expand Down
30 changes: 18 additions & 12 deletions app/Http/Controllers/CollectionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -202,17 +202,21 @@ public function show(Request $request, Collection $collection): Response

$tab = $request->get('tab') === 'activity' ? 'activity' : 'collection';

$activities = $collection->activities()
->latest('timestamp')
->where('type', '!=', NftTransferType::Transfer)
->paginate($activityPageLimit)
->appends([
'tab' => 'activity',
'activityPageLimit' => $activityPageLimit,
]);

/** @var PaginatedDataCollection<int, NftActivityData> */
$paginated = NftActivityData::collection($activities);
// TODO: enable when we enable the "Activity" tab (https://app.clickup.com/t/862kftp7w)...
// $activities = $collection->activities()
// ->latest('timestamp')
// ->with(['nft' => fn ($q) => $q->where('collection_id', $collection->id)])
// ->whereHas('nft', fn ($q) => $q->where('collection_id', $collection->id))
// ->where('type', '!=', NftTransferType::Transfer)
// ->paginate($activityPageLimit)
// ->appends([
// 'tab' => 'activity',
// 'activityPageLimit' => $activityPageLimit,
// ]);

// TODO: enable when we enable the "Activity" tab (https://app.clickup.com/t/862kftp7w)...
// /** @var PaginatedDataCollection<int, NftActivityData> */
// $paginated = NftActivityData::collection($activities);

$ownedNftIds = $user
? $collection->nfts()->ownedBy($user)->pluck('id')
Expand All @@ -230,7 +234,9 @@ public function show(Request $request, Collection $collection): Response
$currency = $user ? $user->currency() : CurrencyCode::USD;

return Inertia::render('Collections/View', [
'activities' => new NftActivitiesData($paginated),
// TODO: enable when we enable the "Activity" tab (https://app.clickup.com/t/862kftp7w)...
// 'activities' => new NftActivitiesData($paginated),
'activities' => null,
'collection' => CollectionDetailData::fromModel($collection, $currency, $user),
'isHidden' => $user && $user->hiddenCollections()->where('id', $collection->id)->exists(),
'previousUrl' => url()->previous() === url()->current()
Expand Down
6 changes: 0 additions & 6 deletions app/Http/Controllers/NftController.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
use App\Data\Nfts\NftActivityData;
use App\Data\Nfts\NftData;
use App\Data\Token\TokenData;
use App\Jobs\FetchNftActivity;
use App\Models\Collection;
use App\Models\Nft;
use App\Models\User;
Expand All @@ -28,11 +27,6 @@ public function show(Request $request, Collection $collection, Nft $nft): Respon

$nativeToken = $collection->network->tokens()->nativeToken()->defaultToken()->first();

// Dispatch every 3 days...
if (! $nft->last_activity_fetched_at || ! $nft->last_viewed_at || now() > $nft->last_viewed_at->addDays(3)) {
FetchNftActivity::dispatch($nft);
}

$nft->touch('last_viewed_at');

return Inertia::render('Collections/Nfts/View', [
Expand Down
4 changes: 2 additions & 2 deletions app/Http/Controllers/RefreshedNftController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace App\Http\Controllers;

use App\Jobs\FetchNftActivity;
use App\Jobs\FetchCollectionActivity;
use App\Jobs\RefreshNftMetadata;
use App\Models\Collection;
use App\Models\Nft;
Expand All @@ -21,7 +21,7 @@ public function __invoke(Collection $collection, Nft $nft): JsonResponse
// It's supposed to be completely opaque to the user what the "refresh" is doing.
RefreshNftMetadata::dispatch($collection, $nft)->onQueue(Queues::NFTS);

FetchNftActivity::dispatch($nft);
FetchCollectionActivity::dispatch($collection)->onQueue(Queues::NFTS);

return response()->json([
'success' => true,
Expand Down
Loading

0 comments on commit bab3ce2

Please sign in to comment.