Skip to content

Commit

Permalink
anchor: add more metadata fields per the spec (#36)
Browse files Browse the repository at this point in the history
1. Added to metadata fields per
https://docs.google.com/spreadsheets/d/1sNKoqr48AIslZlYjDG3LZYfW4qMGb3Ak-x18mvfVLYg/edit#gid=959668688
2. Update share class mint decimals from 0 to 9
  • Loading branch information
yurushao authored Apr 3, 2024
1 parent 6fe5375 commit 93f40ab
Show file tree
Hide file tree
Showing 6 changed files with 326 additions and 18 deletions.
126 changes: 125 additions & 1 deletion anchor/programs/glam/src/instructions/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ pub fn initialize_fund_handler<'c: 'info, 'info>(
mint: share_mint.clone(),
},
);
token_2022::initialize_mint2(mint_cpi_ix, 0, &share_mint_authority.key(), None).unwrap();
token_2022::initialize_mint2(mint_cpi_ix, 9, &share_mint_authority.key(), None).unwrap();

// Init the metadata account
let init_token_metadata_ix = spl_token_metadata_interface::instruction::initialize(
Expand All @@ -173,7 +173,10 @@ pub fn initialize_fund_handler<'c: 'info, 'info>(
],
signer_seeds,
)?;

//
// Add additional metadata fields
//
solana_program::program::invoke_signed(
&spl_token_metadata_interface::instruction::update_field(
&spl_token_2022::id(),
Expand All @@ -185,6 +188,127 @@ pub fn initialize_fund_handler<'c: 'info, 'info>(
&[share_mint.clone(), share_mint_authority.clone()],
signer_seeds,
)?;
solana_program::program::invoke_signed(
&spl_token_metadata_interface::instruction::update_field(
&spl_token_2022::id(),
&share_metadata.key(),
&share_metadata_authority.key(),
spl_token_metadata_interface::state::Field::Key("share_class_asset".to_string()),
share_class_metadata.share_class_asset,
),
&[share_mint.clone(), share_mint_authority.clone()],
signer_seeds,
)?;
solana_program::program::invoke_signed(
&spl_token_metadata_interface::instruction::update_field(
&spl_token_2022::id(),
&share_metadata.key(),
&share_metadata_authority.key(),
spl_token_metadata_interface::state::Field::Key("share_class_asset_id".to_string()),
share_class_metadata.share_class_asset_id.to_string(),
),
&[share_mint.clone(), share_mint_authority.clone()],
signer_seeds,
)?;
solana_program::program::invoke_signed(
&spl_token_metadata_interface::instruction::update_field(
&spl_token_2022::id(),
&share_metadata.key(),
&share_metadata_authority.key(),
spl_token_metadata_interface::state::Field::Key("isin".to_string()),
share_class_metadata.isin,
),
&[share_mint.clone(), share_mint_authority.clone()],
signer_seeds,
)?;
solana_program::program::invoke_signed(
&spl_token_metadata_interface::instruction::update_field(
&spl_token_2022::id(),
&share_metadata.key(),
&share_metadata_authority.key(),
spl_token_metadata_interface::state::Field::Key("status".to_string()),
share_class_metadata.status,
),
&[share_mint.clone(), share_mint_authority.clone()],
signer_seeds,
)?;
solana_program::program::invoke_signed(
&spl_token_metadata_interface::instruction::update_field(
&spl_token_2022::id(),
&share_metadata.key(),
&share_metadata_authority.key(),
spl_token_metadata_interface::state::Field::Key("fee_management".to_string()),
share_class_metadata.fee_management.to_string(),
),
&[share_mint.clone(), share_mint_authority.clone()],
signer_seeds,
)?;
solana_program::program::invoke_signed(
&spl_token_metadata_interface::instruction::update_field(
&spl_token_2022::id(),
&share_metadata.key(),
&share_metadata_authority.key(),
spl_token_metadata_interface::state::Field::Key("fee_performance".to_string()),
share_class_metadata.fee_performance.to_string(),
),
&[share_mint.clone(), share_mint_authority.clone()],
signer_seeds,
)?;
solana_program::program::invoke_signed(
&spl_token_metadata_interface::instruction::update_field(
&spl_token_2022::id(),
&share_metadata.key(),
&share_metadata_authority.key(),
spl_token_metadata_interface::state::Field::Key("policy_distribution".to_string()),
share_class_metadata.policy_distribution,
),
&[share_mint.clone(), share_mint_authority.clone()],
signer_seeds,
)?;
solana_program::program::invoke_signed(
&spl_token_metadata_interface::instruction::update_field(
&spl_token_2022::id(),
&share_metadata.key(),
&share_metadata_authority.key(),
spl_token_metadata_interface::state::Field::Key("extension".to_string()),
share_class_metadata.extension,
),
&[share_mint.clone(), share_mint_authority.clone()],
signer_seeds,
)?;
solana_program::program::invoke_signed(
&spl_token_metadata_interface::instruction::update_field(
&spl_token_2022::id(),
&share_metadata.key(),
&share_metadata_authority.key(),
spl_token_metadata_interface::state::Field::Key("launch_date".to_string()),
share_class_metadata.launch_date,
),
&[share_mint.clone(), share_mint_authority.clone()],
signer_seeds,
)?;
solana_program::program::invoke_signed(
&spl_token_metadata_interface::instruction::update_field(
&spl_token_2022::id(),
&share_metadata.key(),
&share_metadata_authority.key(),
spl_token_metadata_interface::state::Field::Key("lifecycle".to_string()),
share_class_metadata.lifecycle,
),
&[share_mint.clone(), share_mint_authority.clone()],
signer_seeds,
)?;
solana_program::program::invoke_signed(
&spl_token_metadata_interface::instruction::update_field(
&spl_token_2022::id(),
&share_metadata.key(),
&share_metadata_authority.key(),
spl_token_metadata_interface::state::Field::Key("image_uri".to_string()),
share_class_metadata.image_uri,
),
&[share_mint.clone(), share_mint_authority.clone()],
signer_seeds,
)?;

msg!("Fund created: {}", ctx.accounts.fund.key());
Ok(())
Expand Down
19 changes: 15 additions & 4 deletions anchor/programs/glam/src/state/fund.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,25 @@ impl Treasury {

#[derive(AnchorSerialize, AnchorDeserialize)]
pub struct ShareClassMetadata {
pub name: String,
pub symbol: String,
pub uri: String,
pub name: String, // MAX_FUND_NAME
pub symbol: String, // MAX_FUND_SYMBOL
pub uri: String, // MAX_FUND_URI
pub share_class_asset: String, // 20
pub share_class_asset_id: Pubkey, // 32
pub isin: String, // 20
pub status: String, // 20
pub fee_management: i32, // 4, 1_000_000 == 1%
pub fee_performance: i32, // 4, 1_000_000 == 1%
pub policy_distribution: String, // 20
pub extension: String, // 20
pub launch_date: String, // 20
pub lifecycle: String, // 20
pub image_uri: String, // 100
}
impl ShareClassMetadata {
// use the same max sizes as Fund
// more space needed for two reasons:
// 1. we need to support additional metadata
// 2. for each KV pair in metadata, keys ("name" etc) also take up space
pub const INIT_SIZE: usize = MAX_FUND_NAME + MAX_FUND_SYMBOL + MAX_FUND_URI + 100;
pub const INIT_SIZE: usize = MAX_FUND_NAME + MAX_FUND_SYMBOL + MAX_FUND_URI + 500;
}
44 changes: 44 additions & 0 deletions anchor/target/idl/glam.json
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,50 @@
{
"name": "uri",
"type": "string"
},
{
"name": "shareClassAsset",
"type": "string"
},
{
"name": "shareClassAssetId",
"type": "publicKey"
},
{
"name": "isin",
"type": "string"
},
{
"name": "status",
"type": "string"
},
{
"name": "feeManagement",
"type": "i32"
},
{
"name": "feePerformance",
"type": "i32"
},
{
"name": "policyDistribution",
"type": "string"
},
{
"name": "extension",
"type": "string"
},
{
"name": "launchDate",
"type": "string"
},
{
"name": "lifecycle",
"type": "string"
},
{
"name": "imageUri",
"type": "string"
}
]
}
Expand Down
88 changes: 88 additions & 0 deletions anchor/target/types/glam.ts
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,50 @@ export type Glam = {
{
"name": "uri",
"type": "string"
},
{
"name": "shareClassAsset",
"type": "string"
},
{
"name": "shareClassAssetId",
"type": "publicKey"
},
{
"name": "isin",
"type": "string"
},
{
"name": "status",
"type": "string"
},
{
"name": "feeManagement",
"type": "i32"
},
{
"name": "feePerformance",
"type": "i32"
},
{
"name": "policyDistribution",
"type": "string"
},
{
"name": "extension",
"type": "string"
},
{
"name": "launchDate",
"type": "string"
},
{
"name": "lifecycle",
"type": "string"
},
{
"name": "imageUri",
"type": "string"
}
]
}
Expand Down Expand Up @@ -1364,6 +1408,50 @@ export const IDL: Glam = {
{
"name": "uri",
"type": "string"
},
{
"name": "shareClassAsset",
"type": "string"
},
{
"name": "shareClassAssetId",
"type": "publicKey"
},
{
"name": "isin",
"type": "string"
},
{
"name": "status",
"type": "string"
},
{
"name": "feeManagement",
"type": "i32"
},
{
"name": "feePerformance",
"type": "i32"
},
{
"name": "policyDistribution",
"type": "string"
},
{
"name": "extension",
"type": "string"
},
{
"name": "launchDate",
"type": "string"
},
{
"name": "lifecycle",
"type": "string"
},
{
"name": "imageUri",
"type": "string"
}
]
}
Expand Down
33 changes: 26 additions & 7 deletions anchor/tests/glam_crud.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { Keypair, PublicKey } from "@solana/web3.js";
import {
Keypair,
PublicKey,
ComputeBudgetProgram,
ComputeBudgetInstruction
} from "@solana/web3.js";
import {
createMint,
createAssociatedTokenAccount,
Expand Down Expand Up @@ -46,15 +51,26 @@ describe("glam_crud", () => {
program.programId
);

const shareClass = {
name: "Class A share",
symbol: "CLASS-A",
uri: "https://glam.systems/fund/XYZ/share/A"
};
const [sharePDA, shareBump] = PublicKey.findProgramAddressSync(
[anchor.utils.bytes.utf8.encode("share-0"), fundPDA.toBuffer()],
program.programId
);
const shareClassMetadata = {
name: "Class A share",
symbol: "CLASS-A",
uri: `https://api.glam.systems/metadata/${sharePDA.toBase58()}`,
shareClassAsset: "USDC",
shareClassAssetId: usdc,
isin: "XS1082172823",
status: "open",
feeManagement: 15000, // 1_000_000 * 0.015,
feePerformance: 100000, // 1_000_000 * 0.1,
policyDistribution: "accumulating",
extension: "",
launchDate: "2024-04-01",
lifecycle: "active",
imageUri: `https://api.glam.systems/image/${sharePDA.toBase58()}.png`
};

beforeAll(async () => {}, 15_000);

Expand All @@ -67,7 +83,7 @@ describe("glam_crud", () => {
fundUri,
[0, 60, 40],
true,
shareClass
shareClassMetadata
)
.accounts({
fund: fundPDA,
Expand All @@ -81,6 +97,9 @@ describe("glam_crud", () => {
{ pubkey: btc, isSigner: false, isWritable: false },
{ pubkey: eth, isSigner: false, isWritable: false }
])
.preInstructions([
ComputeBudgetProgram.setComputeUnitLimit({ units: 500_000 })
])
.rpc({ commitment }); // await 'confirmed'
console.log(`Fund ${fundPDA} initialized, txId: ${txId}`);
} catch (e) {
Expand Down
Loading

0 comments on commit 93f40ab

Please sign in to comment.