Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PLA-1872] Beam packs #92

Merged
merged 29 commits into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
6fa7581
support create beam packs
enjinabner Aug 1, 2024
467d081
Update beam pack
enjinabner Aug 2, 2024
b50dc3b
fix
enjinabner Aug 2, 2024
befef64
Add tokens pack
enjinabner Aug 5, 2024
0fd5c15
Add remove beam pack tokens
enjinabner Aug 5, 2024
1949224
Update single use codes
enjinabner Aug 5, 2024
952be3d
Remove test
enjinabner Aug 5, 2024
069a53e
Update claims beam pack
enjinabner Aug 5, 2024
82f8af2
Add more tests
enjinabner Aug 5, 2024
6116c51
Fix tests
enjinabner Aug 5, 2024
2726b28
Add packs relation
enjinabner Aug 5, 2024
c63896a
Update translation
enjinabner Aug 15, 2024
fc64333
Fix max token count validation
enjinabner Aug 19, 2024
9a88a0f
Merge branch 'master' into feature/pla-1872/beam-pack-feature
enjinabner Aug 20, 2024
a1aac1c
PR comments
enjinabner Aug 20, 2024
b21e04e
PR comments
enjinabner Aug 20, 2024
733748b
Fix test
enjinabner Aug 21, 2024
7d3d327
Refactor max token supply rule
enjinabner Aug 21, 2024
de2ab5b
Fix tests
enjinabner Aug 21, 2024
c4233d1
Remove unused data
enjinabner Aug 21, 2024
4ee7a84
Make sure tokens are unique
enjinabner Aug 21, 2024
20abb1b
Merge branch 'feature/pla-1939/max-token-count-validation' into featu…
enjinabner Aug 21, 2024
f7fa47b
Merge branch 'master' into feature/pla-1872/beam-pack-feature
enjinabner Aug 28, 2024
a348380
Fix validation
enjinabner Sep 4, 2024
023739b
Fix unit test
enjinabner Sep 9, 2024
f9f3d89
lint
enjinabner Sep 9, 2024
43129a0
Fix validation
enjinabner Sep 9, 2024
e74d0ee
Merge branch 'master' into feature/pla-1872/beam-pack-feature
enjinabner Oct 15, 2024
3b92a48
Merge branch 'master' into feature/pla-1872/beam-pack-feature
enjinabner Oct 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading