Skip to content

Commit

Permalink
[PLA-1872] Beam packs (#92)
Browse files Browse the repository at this point in the history
  • Loading branch information
enjinabner authored Nov 6, 2024
1 parent 395ec42 commit 1f52d52
Show file tree
Hide file tree
Showing 49 changed files with 1,899 additions and 426 deletions.
30 changes: 30 additions & 0 deletions database/factories/BeamPackFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace Enjin\Platform\Beam\Database\Factories;

use Enjin\Platform\Beam\Models\BeamPack;
use Illuminate\Database\Eloquent\Factories\Factory;

class BeamPackFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var BeamPack
*/
protected $model = BeamPack::class;

/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition()
{
return [
'is_claimed' => fake()->boolean(),
'code' => fake()->text(),
'nonce' => 1,
];
}
}
48 changes: 48 additions & 0 deletions database/migrations/2024_08_01_041409_beam_packs.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class () extends Migration {
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('beams', function (Blueprint $table) {
$table->boolean('is_pack')->default(false)->index();
});

Schema::create('beam_packs', function (Blueprint $table) {
$table->id();
$table->foreignId('beam_id')->constrained()->cascadeOnDelete();
$table->string('code')->index()->nullable();
$table->unsignedInteger('nonce')->nullable();
$table->boolean('is_claimed')->default(false)->index();
$table->softDeletes();
$table->timestamps();
});

Schema::table('beam_claims', function (Blueprint $table) {
$table->foreignId('beam_pack_id')->index()->nullable()->constrained()->cascadeOnDelete();
$table->dropUnique(['idempotency_key']);
$table->index(['idempotency_key']);
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('beams', function (Blueprint $table) {
$table->dropColumn('is_pack');
});
Schema::dropIfExists('beam_packs');
Schema::table('beam_claims', function (Blueprint $table) {
$table->dropColumn('beam_pack_id');
$table->unique(['idempotency_key']);
});
}
};
3 changes: 3 additions & 0 deletions lang/en/input_type.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@
'claim_token.field.tokenIdDataUpload' => 'You can use this to upload a txt file that contains a list of token ID ranges, one per line.',
'claim_token.field.claimQuantity' => 'The total amount of times each token ID can be claimed. This is mainly relevant for fungible tokens, where you can specify that there are a certain amount of claims for a token ID, e.g. 10 individual claims to receive 1 token with ID 123 per claim.',
'claim_token.field.tokenQuantityPerClaim' => 'The quantity of token that can be received per claim.',
'beam_pack.description' => 'The beam pack.',
'beam_pack.field.id' => 'The beam pack database ID, which can be null when creating a new beam pack.',
'beam_pack.field.beam_pack' => 'The number of times this pack can be claimed.',
];
3 changes: 3 additions & 0 deletions lang/en/type.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,7 @@
'beam_scan.field.walletPublicKey' => 'The wallet public key.',
'integer_range.description' => "A string value that can be used to represent a range of integer numbers. Use a double full stop to supply a range between 2 integers. \n\nExample \[\"1\",\"2\",\"3..8\"\]",
'attribute.description' => 'An initial attribute to set for the token when minting on demand.',
'beam_pack.description' => 'The beam pack.',
'beam_pack.field.id' => 'The beam pack internal ID.',
'beam_pack.field.isClaimed' => 'The flag that determines if the beam pack is claimed.',
];
5 changes: 5 additions & 0 deletions lang/en/union.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

return [
'claim_union.description' => 'The beam claim can be of either type: BeamPack or BeamClaim',
];
2 changes: 2 additions & 0 deletions lang/en/validation.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,6 @@
'tokens_doesnt_exist_in_beam' => 'The :attribute already exist in beam.',
'tokens_exist_in_beam' => 'The :attribute doesn\'t exist in beam.',
'not_owner' => 'The :attribute should not be the owner of the collection.',
'beam_pack_exist_in_beam' => 'The :attribute doesn\'t exist in beam.',
'tokens_exist_in_beam_pack' => 'The :attribute doesn\'t exist in beam pack.',
];
1 change: 1 addition & 0 deletions src/BeamServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public function configurePackage(Package $package): void
->hasMigration('update_beams_table')
->hasMigration('add_collection_chain_id_to_beam_batches_table')
->hasMigration('add_idempotency_key_to_beam_claims_table')
->hasMigration('beam_packs')
->hasMigration('add_source_to_beams_table')
->hasRoute('enjin-platform-beam')
->hasTranslations();
Expand Down
88 changes: 19 additions & 69 deletions src/GraphQL/Mutations/AddTokensMutation.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,21 @@
namespace Enjin\Platform\Beam\GraphQL\Mutations;

use Closure;
use Enjin\Platform\Beam\Enums\BeamType;
use Enjin\Platform\Beam\GraphQL\Traits\HasBeamCommonFields;
use Enjin\Platform\Beam\GraphQL\Traits\HasTokenInputRules;
use Enjin\Platform\Beam\Models\Beam;
use Enjin\Platform\Beam\Rules\BeamExists;
use Enjin\Platform\Beam\Rules\MaxTokenCount;
use Enjin\Platform\Beam\Rules\MaxTokenSupply;
use Enjin\Platform\Beam\Rules\TokensDoNotExistInBeam;
use Enjin\Platform\Beam\Rules\TokensDoNotExistInCollection;
use Enjin\Platform\Beam\Rules\TokensExistInCollection;
use Enjin\Platform\Beam\Rules\TokenUploadExistInCollection;
use Enjin\Platform\Beam\Rules\TokenUploadNotExistInBeam;
use Enjin\Platform\Beam\Rules\TokenUploadNotExistInCollection;
use Enjin\Platform\Beam\Rules\UniqueTokenIds;
use Enjin\Platform\Beam\Services\BeamService;
use Enjin\Platform\Rules\DistinctAttributes;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
use Rebing\GraphQL\Support\Facades\GraphQL;

class AddTokensMutation extends Mutation
{
use HasBeamCommonFields;
use HasTokenInputRules;

/**
* Get the mutation's attributes.
Expand Down Expand Up @@ -59,9 +49,13 @@ public function args(): array
'description' => __('enjin-platform-beam::mutation.claim_beam.args.code'),
],
'tokens' => [
'type' => GraphQL::type('[ClaimToken!]!'),
'type' => GraphQL::type('[ClaimToken!]'),
'description' => __('enjin-platform-beam::input_type.claim_token.description'),
],
'packs' => [
'type' => GraphQL::type('[BeamPackInput!]'),
'description' => __('enjin-platform-beam::input_type.beam_pack.description'),
],
];
}

Expand All @@ -76,7 +70,13 @@ public function resolve(
Closure $getSelectFields,
BeamService $beam
) {
return DB::transaction(fn () => $beam->addTokens($args['code'], $args['tokens']));
return DB::transaction(
fn () => $beam->addTokens(
$args['code'],
Arr::get($args, 'tokens', []),
Arr::get($args, 'packs', [])
)
);
}

/**
Expand All @@ -92,61 +92,11 @@ protected function rules(array $args = []): array
'max:1024',
new BeamExists(),
],
'tokens' => ['bail', 'array', 'min:1', new UniqueTokenIds()],
'tokens.*.attributes' => Rule::forEach(function ($value, $attribute) use ($args) {
if (empty($value)) {
return [];
}

return [
'nullable',
'bail',
'array',
'min:1',
'max:10',
new DistinctAttributes(),
Rule::prohibitedIf(BeamType::getEnumCase(Arr::get($args, str_replace('attributes', 'type', $attribute))) == BeamType::TRANSFER_TOKEN),
];
}),
'tokens.*.attributes.*.key' => 'max:255',
'tokens.*.attributes.*.value' => 'max:1000',
'tokens.*.tokenIds' => Rule::forEach(function ($value, $attribute) use ($args, $beam) {
return [
'bail',
'required_without:tokens.*.tokenIdDataUpload',
'prohibits:tokens.*.tokenIdDataUpload',
'distinct',
BeamType::getEnumCase(Arr::get($args, str_replace('tokenIds', 'type', $attribute))) == BeamType::TRANSFER_TOKEN
? new TokensExistInCollection($beam?->collection_chain_id)
: new TokensDoNotExistInCollection($beam?->collection_chain_id),
new TokensDoNotExistInBeam($beam),
];
}),
'tokens.*.tokenIdDataUpload' => Rule::forEach(function ($value, $attribute) use ($args, $beam) {
return [
'bail',
'required_without:tokens.*.tokenIds',
'prohibits:tokens.*.tokenIds',
BeamType::getEnumCase(Arr::get($args, str_replace('tokenIdDataUpload', 'type', $attribute))) == BeamType::TRANSFER_TOKEN
? new TokenUploadExistInCollection($beam?->collection_chain_id)
: new TokenUploadNotExistInCollection($beam?->collection_chain_id),
new TokenUploadNotExistInBeam($beam),
];
}),
'tokens.*.tokenQuantityPerClaim' => [
'bail',
'filled',
'integer',
'min:1',
new MaxTokenSupply($beam?->collection_chain_id),
],
'tokens.*.claimQuantity' => [
'bail',
'filled',
'integer',
'min:1',
new MaxTokenCount($beam?->collection_chain_id),
],
...match (true) {
!$beam => [],
!$beam?->is_pack => $this->tokenRules($args, $beam?->collection_chain_id),
default => $this->packTokenRules($args, $beam?->collection_chain_id),
},
];
}
}
80 changes: 13 additions & 67 deletions src/GraphQL/Mutations/CreateBeamMutation.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,22 @@
namespace Enjin\Platform\Beam\GraphQL\Mutations;

use Closure;
use Enjin\Platform\Beam\Enums\BeamType;
use Enjin\Platform\Beam\GraphQL\Traits\HasBeamCommonFields;
use Enjin\Platform\Beam\Rules\MaxTokenCount;
use Enjin\Platform\Beam\Rules\MaxTokenSupply;
use Enjin\Platform\Beam\Rules\TokensDoNotExistInBeam;
use Enjin\Platform\Beam\Rules\TokensExistInCollection;
use Enjin\Platform\Beam\Rules\TokenUploadExistInCollection;
use Enjin\Platform\Beam\Rules\TokenUploadNotExistInBeam;
use Enjin\Platform\Beam\Rules\UniqueTokenIds;
use Enjin\Platform\Beam\GraphQL\Traits\HasTokenInputRules;
use Enjin\Platform\Beam\Services\BeamService;
use Enjin\Platform\Models\Collection;
use Enjin\Platform\Rules\DistinctAttributes;
use Enjin\Platform\Rules\IsCollectionOwnerOrApproved;
use Enjin\Platform\Rules\ValidSubstrateAddress;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
use Rebing\GraphQL\Support\Facades\GraphQL;

class CreateBeamMutation extends Mutation
{
use HasBeamCommonFields;
use HasTokenInputRules;

/**
* Get the mutation's attributes.
Expand Down Expand Up @@ -64,9 +56,13 @@ public function args(): array
'description' => __('enjin-platform-beam::mutation.create_beam.args.collectionId'),
],
'tokens' => [
'type' => GraphQL::type('[ClaimToken!]!'),
'type' => GraphQL::type('[ClaimToken!]'),
'description' => __('enjin-platform-beam::input_type.claim_token.description'),
],
'packs' => [
'type' => GraphQL::type('[BeamPackInput!]'),
'description' => __('enjin-platform-beam::input_type.beam_pack.description'),
],
];
}

Expand All @@ -81,7 +77,10 @@ public function resolve(
Closure $getSelectFields,
BeamService $beam
) {
return DB::transaction(fn () => $beam->create($args)->code, 3);
return DB::transaction(
fn () => $beam->create($args, (bool) Arr::get($args, 'packs'))->code,
3
);
}

/**
Expand All @@ -105,62 +104,9 @@ function (string $attribute, mixed $value, Closure $fail) {
},
new IsCollectionOwnerOrApproved(),
],
'tokens' => ['bail', 'array', 'min:1', 'max:1000', new UniqueTokenIds()],
'tokens.*.attributes' => Rule::forEach(function ($value, $attribute) use ($args) {
if (empty($value)) {
return [];
}

return [
'nullable',
'bail',
'array',
'min:1',
'max:10',
new DistinctAttributes(),
Rule::prohibitedIf(BeamType::getEnumCase(Arr::get($args, str_replace('attributes', 'type', $attribute))) == BeamType::TRANSFER_TOKEN),
];
}),
'tokens.*.attributes.*.key' => 'max:255',
'tokens.*.attributes.*.value' => 'max:1000',
'tokens.*.tokenIds' => Rule::forEach(function ($value, $attribute) use ($args) {
return [
'bail',
'required_without:tokens.*.tokenIdDataUpload',
'prohibits:tokens.*.tokenIdDataUpload',
'distinct',
BeamType::getEnumCase(Arr::get($args, str_replace('tokenIds', 'type', $attribute))) == BeamType::TRANSFER_TOKEN
? new TokensExistInCollection($args['collectionId'])
: '',
new TokensDoNotExistInBeam(),
];
}),
'tokens.*.tokenIdDataUpload' => Rule::forEach(function ($value, $attribute) use ($args) {
return [
'bail',
'required_without:tokens.*.tokenIds',
'prohibits:tokens.*.tokenIds',
BeamType::getEnumCase(Arr::get($args, str_replace('tokenIdDataUpload', 'type', $attribute))) == BeamType::TRANSFER_TOKEN
? new TokenUploadExistInCollection($args['collectionId'])
: '',
new TokenUploadNotExistInBeam(),
];
}),
'tokens.*.tokenQuantityPerClaim' => [
'bail',
'filled',
'integer',
'min:1',
new MaxTokenSupply($args['collectionId']),
],
'tokens.*.claimQuantity' => [
'bail',
'filled',
'integer',
'min:1',
new MaxTokenCount($args['collectionId']),
],
'flags.*.flag' => ['required', 'distinct'],
...$this->tokenRules($args, $args['collectionId'], true),
...$this->packTokenRules($args, $args['collectionId'], true),
'source' => [
'nullable',
new ValidSubstrateAddress(),
Expand Down
Loading

0 comments on commit 1f52d52

Please sign in to comment.