Skip to content

Commit

Permalink
Add bucket extension (#71)
Browse files Browse the repository at this point in the history
* Use nitrate entrypoint

* Add resize instruction

* Add properties extension

* Remove unused trait

* Fix properties getter

* Add bpf test

* Update program interface

* Add resize wrapper

* Rename resize argument

* Update kinobi config

* Add bucket extension

* Update generated code

* Add test

* Add boolean property type

* Add test

* Fix linting

* Fix bridge test

* Fix extension type

* Fix bucket comments
  • Loading branch information
febo authored May 14, 2024
1 parent 065bd06 commit e999bda
Show file tree
Hide file tree
Showing 14 changed files with 226 additions and 2 deletions.
7 changes: 7 additions & 0 deletions clients/js/asset/src/extensions/bucket.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { TypedExtension } from '.';
import { ExtensionType } from '../generated';

export const bucket = (data: number[] | Uint8Array): TypedExtension => ({
type: ExtensionType.Bucket,
data: data instanceof Uint8Array ? Array.from(data) : data,
});
8 changes: 7 additions & 1 deletion clients/js/asset/src/extensions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
Asset,
Attributes,
Blob,
Bucket,
Creators,
ExtensionType,
Grouping,
Expand All @@ -12,6 +13,7 @@ import {
Proxy,
getAttributesSerializer,
getBlobSerializer,
getBucketSerializer,
getCreatorsSerializer,
getGroupingSerializer,
getLinksSerializer,
Expand All @@ -24,6 +26,7 @@ import { Properties, getPropertiesSerializer } from './properties';

export * from './attributes';
export * from './blob';
export * from './bucket';
export * from './creators';
export * from './grouping';
export * from './links';
Expand All @@ -42,7 +45,8 @@ export type TypedExtension =
| ({ type: ExtensionType.Royalties } & Royalties)
| ({ type: ExtensionType.Manager } & Manager)
| ({ type: ExtensionType.Proxy } & Proxy)
| ({ type: ExtensionType.Properties } & Properties);
| ({ type: ExtensionType.Properties } & Properties)
| ({ type: ExtensionType.Bucket } & Bucket);

export const getExtensionSerializerFromType = <T extends TypedExtension>(
type: ExtensionType
Expand All @@ -69,6 +73,8 @@ export const getExtensionSerializerFromType = <T extends TypedExtension>(
return getProxySerializer();
case ExtensionType.Properties:
return getPropertiesSerializer();
case ExtensionType.Bucket:
return getBucketSerializer();
default:
throw new Error(`Unknown extension type: ${type}`);
}
Expand Down
24 changes: 24 additions & 0 deletions clients/js/asset/src/generated/types/bucket.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* This code was AUTOGENERATED using the kinobi library.
* Please DO NOT EDIT THIS FILE, instead use visitors
* to add features, then rerun kinobi to update it.
*
* @see https://github.com/metaplex-foundation/kinobi
*/

import {
Serializer,
array,
struct,
u8,
} from '@metaplex-foundation/umi/serializers';

export type Bucket = { data: Array<number> };

export type BucketArgs = Bucket;

export function getBucketSerializer(): Serializer<BucketArgs, Bucket> {
return struct<Bucket>([['data', array(u8(), { size: 'remainder' })]], {
description: 'Bucket',
}) as Serializer<BucketArgs, Bucket>;
}
1 change: 1 addition & 0 deletions clients/js/asset/src/generated/types/extensionType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export enum ExtensionType {
Manager,
Proxy,
Properties,
Bucket,
}

export type ExtensionTypeArgs = ExtensionType;
Expand Down
1 change: 1 addition & 0 deletions clients/js/asset/src/generated/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

export * from './attributes';
export * from './blob';
export * from './bucket';
export * from './creator';
export * from './creators';
export * from './delegate';
Expand Down
65 changes: 65 additions & 0 deletions clients/js/asset/test/extensions/bucket.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { generateSigner } from '@metaplex-foundation/umi';
import test from 'ava';
import {
Asset,
Discriminator,
ExtensionType,
Standard,
State,
bucket,
create,
fetchAsset,
initialize,
} from '../../src';
import { createUmi } from '../_setup';

test('it can create a new asset with a bucket', async (t) => {
// Given a Umi instance and a new signer.
const umi = await createUmi();
const asset = generateSigner(umi);
const owner = generateSigner(umi);

// And we initialize an asset with a bucket extension.
const response = await fetch(
'https://arweave.net/Y8MBS8tqo9XJ_Z1l9V6BIMvhknWxhzP0UxSNBk1OXSs'
);
const data = new Uint8Array(await response.arrayBuffer());

// And we initialize a bucket extension.
await initialize(umi, {
asset,
payer: umi.identity,
extension: bucket(data),
}).sendAndConfirm(umi);

t.true(await umi.rpc.accountExists(asset.publicKey), 'asset exists');

// When we create the asset.
await create(umi, {
asset,
owner: owner.publicKey,
name: 'Bucket Asset',
}).sendAndConfirm(umi);

// Then an asset was created with the correct data.
const assetAccount = await fetchAsset(umi, asset.publicKey);
t.like(assetAccount, <Asset>{
discriminator: Discriminator.Asset,
state: State.Unlocked,
standard: Standard.NonFungible,
owner: owner.publicKey,
authority: umi.identity.publicKey,
});

// And the bucket extension was added.
const extension = assetAccount.extensions[0];
t.true(extension.type === ExtensionType.Bucket);

// And the bucket has the correct data.
if (extension.type === ExtensionType.Bucket) {
t.is(extension.data.length, data.length);
t.deepEqual(extension.data, Array.from(data));
} else {
t.fail('extension is not a bucket');
}
});
16 changes: 16 additions & 0 deletions clients/rust/asset/src/generated/types/bucket.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//! This code was AUTOGENERATED using the kinobi library.
//! Please DO NOT EDIT THIS FILE, instead use visitors
//! to add features, then rerun kinobi to update it.
//!
//! [https://github.com/metaplex-foundation/kinobi]
//!
use borsh::BorshDeserialize;
use borsh::BorshSerialize;
use kaigan::types::RemainderVec;

#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Bucket {
pub data: RemainderVec<u8>,
}
1 change: 1 addition & 0 deletions clients/rust/asset/src/generated/types/extension_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ pub enum ExtensionType {
Manager,
Proxy,
Properties,
Bucket,
}
2 changes: 2 additions & 0 deletions clients/rust/asset/src/generated/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
pub(crate) mod r#attributes;
pub(crate) mod r#blob;
pub(crate) mod r#bucket;
pub(crate) mod r#creator;
pub(crate) mod r#creators;
pub(crate) mod r#delegate;
Expand All @@ -30,6 +31,7 @@ pub(crate) mod r#type;

pub use self::r#attributes::*;
pub use self::r#blob::*;
pub use self::r#bucket::*;
pub use self::r#creator::*;
pub use self::r#creators::*;
pub use self::r#delegate::*;
Expand Down
14 changes: 14 additions & 0 deletions configs/kinobi-asset.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ kinobi.update(
{ name: "Manager" },
{ name: "Proxy" },
{ name: "Properties" },
{ name: "Bucket" },
],
},
}),
Expand Down Expand Up @@ -359,6 +360,19 @@ kinobi.update(
}),
]),
}),
// bucket
k.definedTypeNode({
name: "bucket",
type: k.structTypeNode([
k.structFieldTypeNode({
name: "data",
type: k.arrayTypeNode(
k.numberTypeNode("u8"),
k.remainderSizeNode()
),
}),
]),
}),
// type (for properties extension)
k.definedTypeNodeFromIdl({
name: "type",
Expand Down
1 change: 1 addition & 0 deletions configs/kinobi-interface.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ kinobi.update(
{ name: "Manager" },
{ name: "Proxy" },
{ name: "Properties" },
{ name: "Bucket" },
],
},
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ pub enum ExtensionType {
Manager,
Proxy,
Properties,
Bucket,
}
79 changes: 79 additions & 0 deletions programs/asset/types/src/extensions/bucket.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use std::ops::Deref;

use super::{ExtensionBuilder, ExtensionData, ExtensionDataMut, ExtensionType, Lifecycle};

/// Extension to add binary data to an asset.
#[repr(C)]
pub struct Bucket<'a> {
/// The raw data of the extension.
pub data: &'a [u8],
}

impl<'a> ExtensionData<'a> for Bucket<'a> {
const TYPE: ExtensionType = ExtensionType::Bucket;

fn from_bytes(bytes: &'a [u8]) -> Self {
Self { data: bytes }
}

fn length(&self) -> usize {
self.data.len()
}
}

pub struct BucketMut<'a> {
/// The raw data of the extension.
pub data: &'a mut [u8],
}

impl<'a> ExtensionDataMut<'a> for BucketMut<'a> {
const TYPE: ExtensionType = ExtensionType::Bucket;

fn from_bytes_mut(bytes: &'a mut [u8]) -> Self {
Self { data: bytes }
}
}

impl Lifecycle for BucketMut<'_> {}

/// Builder for a `Bucket` extension.
#[derive(Default)]
pub struct BucketBuilder(Vec<u8>);

impl BucketBuilder {
pub fn with_capacity(capacity: usize) -> Self {
Self(Vec::with_capacity(capacity))
}

pub fn with_buffer(buffer: Vec<u8>) -> Self {
Self(buffer)
}

/// Set the data of the bucket.
pub fn set_data(&mut self, data: &[u8]) -> &mut Self {
// setting the data replaces any existing data
self.0.clear();
// add the data to the buffer
self.0.extend_from_slice(data);

self
}
}

impl<'a> ExtensionBuilder<'a, Bucket<'a>> for BucketBuilder {
fn build(&'a self) -> Bucket<'a> {
Bucket::from_bytes(&self.0)
}

fn data(&mut self) -> Vec<u8> {
std::mem::take(&mut self.0)
}
}

impl Deref for BucketBuilder {
type Target = Vec<u8>;

fn deref(&self) -> &Self::Target {
&self.0
}
}
8 changes: 7 additions & 1 deletion programs/asset/types/src/extensions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
mod attributes;
mod blob;
mod bucket;
mod creators;
mod grouping;
mod links;
Expand All @@ -26,6 +27,7 @@ mod royalties;

pub use attributes::*;
pub use blob::*;
pub use bucket::*;
pub use creators::*;
pub use grouping::*;
pub use links::*;
Expand Down Expand Up @@ -208,6 +210,7 @@ pub enum ExtensionType {
Manager,
Proxy,
Properties,
Bucket,
}

impl TryFrom<u32> for ExtensionType {
Expand All @@ -226,6 +229,7 @@ impl TryFrom<u32> for ExtensionType {
8 => Ok(ExtensionType::Manager),
9 => Ok(ExtensionType::Proxy),
10 => Ok(ExtensionType::Properties),
11 => Ok(ExtensionType::Bucket),
_ => Err(Error::InvalidExtensionType(value)),
}
}
Expand All @@ -245,6 +249,7 @@ impl From<ExtensionType> for u32 {
ExtensionType::Manager => 8,
ExtensionType::Proxy => 9,
ExtensionType::Properties => 10,
ExtensionType::Bucket => 11,
}
}
}
Expand Down Expand Up @@ -333,5 +338,6 @@ validate_extension_type!(
(Royalties, RoyaltiesMut),
(Manager, ManagerMut),
(Proxy, ProxyMut),
(Properties, PropertiesMut)
(Properties, PropertiesMut),
(Bucket, BucketMut)
);

0 comments on commit e999bda

Please sign in to comment.