-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* 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 * Add boolean property type * Add test * Fix linting
- Loading branch information
Showing
14 changed files
with
943 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
import { | ||
Serializer, | ||
array, | ||
bool, | ||
string, | ||
struct, | ||
u64, | ||
u8, | ||
} from '@metaplex-foundation/umi/serializers'; | ||
import { TypedExtension, getExtension } from '.'; | ||
import { Asset, ExtensionType, Type, getTypeSerializer } from '../generated'; | ||
|
||
type Property = | ||
| Omit<Text, 'type'> | ||
| Omit<Number, 'type'> | ||
| Omit<Boolean, 'type'>; | ||
|
||
export const properties = (values: Property[]): TypedExtension => ({ | ||
type: ExtensionType.Properties, | ||
values: values.map( | ||
(property) => | ||
({ | ||
type: getTypeFromString(typeof property.value), | ||
...property, | ||
} as TypedProperty) | ||
), | ||
}); | ||
|
||
type TypedPropertyfromEnum<T extends Type> = Extract< | ||
TypedProperty, | ||
{ type: T } | ||
>; | ||
|
||
export function getProperty<T extends Type>( | ||
asset: Asset, | ||
name: string, | ||
type: T | ||
): TypedPropertyfromEnum<T> | undefined { | ||
const extension = getExtension(asset, ExtensionType.Properties); | ||
|
||
if (!extension) { | ||
return undefined; | ||
} | ||
|
||
const property = extension.values.find( | ||
(p) => 'type' in p && p.type === type && p.name === name | ||
); | ||
|
||
return property ? (property as TypedPropertyfromEnum<T>) : undefined; | ||
} | ||
|
||
// Properties. | ||
|
||
export type Properties = { values: Array<TypedProperty> }; | ||
|
||
export type PropertiesArgs = { values: Array<TypedProperty> }; | ||
|
||
export function getPropertiesSerializer(): Serializer< | ||
PropertiesArgs, | ||
Properties | ||
> { | ||
return struct<Properties>( | ||
[['values', array(getPropertySerializer(), { size: 'remainder' })]], | ||
{ description: 'Properties' } | ||
) as Serializer<PropertiesArgs, Properties>; | ||
} | ||
|
||
export const getPropertySerializer = (): Serializer<TypedProperty> => ({ | ||
description: 'TypedProperty', | ||
fixedSize: null, | ||
maxSize: null, | ||
serialize: (property: TypedProperty) => | ||
getPropertySerializerFromType(property.type).serialize(property), | ||
deserialize: (buffer, offset = 0) => { | ||
const [, nameOffset] = string({ size: u8() }).deserialize(buffer, offset); | ||
const type = buffer[nameOffset] as Type; | ||
return getPropertySerializerFromType(type).deserialize(buffer, offset); | ||
}, | ||
}); | ||
|
||
export type TypedProperty = | ||
| ({ type: Type.Text } & Omit<Text, 'type'>) | ||
| ({ type: Type.Number } & Omit<Number, 'type'>) | ||
| ({ type: Type.Boolean } & Omit<Boolean, 'type'>); | ||
|
||
export const getPropertySerializerFromType = <T extends TypedProperty>( | ||
type: Type | ||
): Serializer<T> => | ||
((): Serializer<any> => { | ||
switch (type) { | ||
case Type.Text: | ||
return getTextSerializer(); | ||
case Type.Number: | ||
return getNumberSerializer(); | ||
case Type.Boolean: | ||
return getBooleanSerializer(); | ||
default: | ||
throw new Error(`Unknown property type: ${type}`); | ||
} | ||
})() as Serializer<T>; | ||
|
||
export const getTypeFromString = (type: String): Type => { | ||
switch (type) { | ||
case 'string': | ||
return Type.Text; | ||
case 'boolean': | ||
return Type.Boolean; | ||
default: | ||
return Type.Number; | ||
} | ||
}; | ||
|
||
// Text property. | ||
|
||
export type Text = { name: string; type: Type; value: string }; | ||
|
||
export type TextArgs = Text; | ||
|
||
export function getTextSerializer(): Serializer<TextArgs, Text> { | ||
return struct<Text>( | ||
[ | ||
['name', string({ size: u8() })], | ||
['type', getTypeSerializer()], | ||
['value', string({ size: u8() })], | ||
], | ||
{ description: 'Text' } | ||
) as Serializer<TextArgs, Text>; | ||
} | ||
|
||
// Number property. | ||
|
||
export type Number = { name: string; type: Type; value: bigint }; | ||
|
||
export type NumberArgs = Number; | ||
|
||
export function getNumberSerializer(): Serializer<NumberArgs, Number> { | ||
return struct<Number>( | ||
[ | ||
['name', string({ size: u8() })], | ||
['type', getTypeSerializer()], | ||
['value', u64()], | ||
], | ||
{ description: 'Number' } | ||
) as Serializer<NumberArgs, Number>; | ||
} | ||
|
||
// Boolean property. | ||
|
||
export type Boolean = { name: string; type: Type; value: boolean }; | ||
|
||
export type BooleanArgs = Boolean; | ||
|
||
export function getBooleanSerializer(): Serializer<BooleanArgs, Boolean> { | ||
return struct<Boolean>( | ||
[ | ||
['name', string({ size: u8() })], | ||
['type', getTypeSerializer()], | ||
['value', bool()], | ||
], | ||
{ description: 'Boolean' } | ||
) as Serializer<BooleanArgs, Boolean>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, scalarEnum } from '@metaplex-foundation/umi/serializers'; | ||
|
||
export enum Type { | ||
Text, | ||
Number, | ||
Boolean, | ||
} | ||
|
||
export type TypeArgs = Type; | ||
|
||
export function getTypeSerializer(): Serializer<TypeArgs, Type> { | ||
return scalarEnum<Type>(Type, { description: 'Type' }) as Serializer< | ||
TypeArgs, | ||
Type | ||
>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import { generateSigner } from '@metaplex-foundation/umi'; | ||
import test from 'ava'; | ||
import { | ||
Asset, | ||
Discriminator, | ||
ExtensionType, | ||
Standard, | ||
State, | ||
Type, | ||
create, | ||
fetchAsset, | ||
getProperty, | ||
initialize, | ||
properties, | ||
} from '../../src'; | ||
import { createUmi } from '../_setup'; | ||
|
||
test('it can create a new asset with properties', 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 properties. | ||
await initialize(umi, { | ||
asset, | ||
payer: umi.identity, | ||
extension: properties([ | ||
{ name: 'name', value: 'nifty' }, | ||
{ name: 'version', value: 1n }, | ||
{ name: 'alpha', value: false }, | ||
]), | ||
}).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: 'Asset with creators', | ||
}).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, | ||
extensions: [ | ||
{ | ||
type: ExtensionType.Properties, | ||
values: [ | ||
{ | ||
type: Type.Text, | ||
name: 'name', | ||
value: 'nifty', | ||
}, | ||
{ | ||
type: Type.Number, | ||
name: 'version', | ||
value: 1n, | ||
}, | ||
{ | ||
type: Type.Boolean, | ||
name: 'alpha', | ||
value: false, | ||
}, | ||
], | ||
}, | ||
], | ||
}); | ||
|
||
const name = getProperty(assetAccount, 'name', Type.Text); | ||
if (name) { | ||
t.deepEqual(name.value, 'nifty'); | ||
} | ||
|
||
const version = getProperty(assetAccount, 'version', Type.Number); | ||
if (version) { | ||
t.deepEqual(version.value, 1n); | ||
} | ||
|
||
const alpha = getProperty(assetAccount, 'alpha', Type.Boolean); | ||
if (alpha) { | ||
t.deepEqual(alpha.value, false); | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,4 +21,5 @@ pub enum ExtensionType { | |
Royalties, | ||
Manager, | ||
Proxy, | ||
Properties, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
//! 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; | ||
|
||
#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)] | ||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] | ||
pub enum Type { | ||
Text, | ||
Number, | ||
Boolean, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
#![cfg(feature = "test-sbf")] | ||
pub mod properties; |
Oops, something went wrong.