diff --git a/src/Support/ClaimProbabilities.php b/src/Support/ClaimProbabilities.php index 61e5d8e..0fd7bb6 100644 --- a/src/Support/ClaimProbabilities.php +++ b/src/Support/ClaimProbabilities.php @@ -2,7 +2,9 @@ namespace Enjin\Platform\Beam\Support; +use Closure; use Enjin\Platform\Beam\Enums\PlatformBeamCache; +use Enjin\Platform\Beam\Models\Laravel\BeamClaim; use Enjin\Platform\Beam\Rules\Traits\IntegerRange; use Illuminate\Support\Arr; use Illuminate\Support\Collection; @@ -12,6 +14,8 @@ class ClaimProbabilities { use IntegerRange; + public const FORMAT_VERSION = 'v1'; + /** * Create or update the probabilities for a claim. */ @@ -31,7 +35,15 @@ public function createOrUpdateProbabilities(string $code, array $claims): void */ public static function hasProbabilities(string $code): bool { - return Cache::has(PlatformBeamCache::CLAIM_PROBABILITIES->key($code)); + return Cache::has(static::getCacheKey($code)); + } + + /** + * Get the cache key for the code. + */ + public static function getCacheKey(string $code): string + { + return PlatformBeamCache::CLAIM_PROBABILITIES->key($code, static::FORMAT_VERSION); } /** @@ -39,7 +51,41 @@ public static function hasProbabilities(string $code): bool */ public static function getProbabilities(string $code): array { - return Cache::get(PlatformBeamCache::CLAIM_PROBABILITIES->key($code), []); + return Cache::get( + static::getCacheKey($code), + static::getProbabilitiesFromDB($code) + ); + } + + /** + * Get the probabilities from the database. + */ + public static function getProbabilitiesFromDB(string $code): Closure + { + return function () use ($code) { + $claims = BeamClaim::selectRaw(' + token_chain_id, + quantity as tokenQuantityPerClaim, + count(*) as claimQuantity + ')->hasCode($code) + ->groupBy('token_chain_id', 'quantity') + ->get() + ->map(function ($claim) { + $claim->tokenIds = [$claim->token_chain_id]; + + return $claim; + })->toArray(); + + $instance = resolve(static::class); + $formatted = $instance->filterClaims($claims); + $instance->computeProbabilities( + $code, + $formatted['ft'], + $formatted['nft'], + ); + + return Cache::get(static::getCacheKey($code), []); + }; } /** @@ -68,6 +114,34 @@ public function removeTokens(string $code, array $tokenIds): void } } + /** + * Compute the probabilities for the items. + */ + public function computeProbabilities(string $code, array $fts, array $nfts): void + { + $total = collect($fts)->sum() + ($totalNft = collect($nfts)->sum()); + if (!$total) { + return; + } + + $probabilities = [ + 'ft' => collect($fts)->mapWithKeys(fn ($quantity, $key) => [$key => ($quantity / $total) * 100])->toArray(), + 'nft' => ($totalNft / $total) * 100, + 'ftTokenIds' => $this->extractTokenIds($fts, $total), + 'nftTokenIds' => $this->extractTokenIds($nfts, $total), + ]; + + $data = [ + 'tokens' => ['ft' => $fts, 'nft' => $nfts], + 'probabilities' => $probabilities, + ]; + + Cache::forever( + static::getCacheKey($code), + $data + ); + } + /** * Merge the tokens into the current tokens. */ @@ -116,28 +190,22 @@ protected function filterClaims(array $claims): array } /** - * Compute the probabilities for the items. + * Extract the token ids from the array. */ - protected function computeProbabilities(string $code, array $fts, array $nfts): void + protected function extractTokenIds(array $tokenIds, int $total): array { - $totalNft = collect($nfts)->sum(); - $total = collect($fts)->sum() + $totalNft; - $probabilities = []; - if ($total > 0) { - foreach ($fts as $key => $quantity) { - $probabilities['ft'][$key] = ($quantity / $total) * 100; + $tokens = []; + foreach ($tokenIds as $key => $quantity) { + if (($range = $this->integerRange($key)) !== false) { + $count = $quantity / (($range[1] - $range[0]) + 1); + for ($i = $range[0]; $i <= $range[1]; $i++) { + $tokens[$i] = ($count / $total) * 100; + } + } else { + $tokens[$key] = ($quantity / $total) * 100; } - $probabilities['nft'] = ($totalNft / $total) * 100; } - $data = [ - 'tokens' => ['ft' => $fts, 'nft' => $nfts], - 'probabilities' => $probabilities, - ]; - - Cache::forever( - PlatformBeamCache::CLAIM_PROBABILITIES->key($code), - $data - ); + return $tokens; } } diff --git a/tests/Unit/ClaimProbabilityTest.php b/tests/Unit/ClaimProbabilityTest.php index 423b74f..2afcbef 100644 --- a/tests/Unit/ClaimProbabilityTest.php +++ b/tests/Unit/ClaimProbabilityTest.php @@ -30,11 +30,24 @@ public function test_it_can_create_probabilities() $this->assertEquals( [ 'ft' => [ - '41' => 10, - '42' => 20, - '43..45' => 30, + '7' => 10.0, + '8..10' => 60.0, + ], + 'nft' => 30.0, + 'ftTokenIds' => [ + '7' => 10.0, + '8' => 20.0, + '9' => 20.0, + '10' => 20.0, + ], + 'nftTokenIds' => [ + '1' => 5.0, + '2' => 5.0, + '3' => 5.0, + '4' => 5.0, + '5' => 5.0, + '6' => 5.0, ], - 'nft' => 40, ], ClaimProbabilities::getProbabilities($this->beam->code)['probabilities'] ); @@ -46,20 +59,46 @@ public function test_it_can_remove_tokens() $this->assertEquals( [ 'ft' => [ - '41' => 10, - '42' => 20, - '43..45' => 30, + '7' => 10.0, + '8..10' => 60.0, + ], + 'nft' => 30.0, + 'ftTokenIds' => [ + '7' => 10.0, + '8' => 20.0, + '9' => 20.0, + '10' => 20.0, + ], + 'nftTokenIds' => [ + '1' => 5.0, + '2' => 5.0, + '3' => 5.0, + '4' => 5.0, + '5' => 5.0, + '6' => 5.0, ], - 'nft' => 40, ], ClaimProbabilities::getProbabilities($this->beam->code)['probabilities'] ); - $this->probabilities->removeTokens($this->beam->code, ['42', '43..45']); + $this->probabilities->removeTokens($this->beam->code, ['8..10']); $this->assertEquals( [ - 'ft' => ['41' => 20], - 'nft' => 80, + 'ft' => [ + '7' => 25.0, + ], + 'nft' => 75.0, + 'ftTokenIds' => [ + '7' => 25.0, + ], + 'nftTokenIds' => [ + '1' => 12.5, + '2' => 12.5, + '3' => 12.5, + '4' => 12.5, + '5' => 12.5, + '6' => 12.5, + ], ], ClaimProbabilities::getProbabilities($this->beam->code)['probabilities'] ); @@ -70,26 +109,26 @@ protected function generateTokens(): array return [ [ 'type' => BeamType::MINT_ON_DEMAND->name, - 'tokenIds' => ['1..40'], + 'tokenIds' => ['1..5'], 'claimQuantity' => 1, 'tokenQuantityPerClaim' => 1, ], [ 'type' => BeamType::MINT_ON_DEMAND->name, - 'tokenIds' => ['41'], - 'claimQuantity' => 10, + 'tokenIds' => ['6'], + 'claimQuantity' => 1, 'tokenQuantityPerClaim' => 1, ], [ 'type' => BeamType::MINT_ON_DEMAND->name, - 'tokenIds' => ['42'], - 'claimQuantity' => 20, + 'tokenIds' => ['7'], + 'claimQuantity' => 2, 'tokenQuantityPerClaim' => 1, ], [ 'type' => BeamType::MINT_ON_DEMAND->name, - 'tokenIds' => ['43..45'], - 'claimQuantity' => 10, + 'tokenIds' => ['8..10'], + 'claimQuantity' => 4, 'tokenQuantityPerClaim' => 1, ], ];