@@ -14,6 +14,7 @@ use anchor_spl::token_interface::Mint;
1414use m2_interface:: {
1515 withdraw_by_mmm_ix_with_program_id, WithdrawByMMMArgs , WithdrawByMmmIxArgs , WithdrawByMmmKeys ,
1616} ;
17+ use mpl_bubblegum:: hash:: hash_creators;
1718use mpl_core:: types:: { Royalties , UpdateAuthority } ;
1819use mpl_token_metadata:: {
1920 accounts:: { MasterEdition , Metadata } ,
@@ -30,7 +31,7 @@ use spl_token_2022::{
3031} ;
3132use spl_token_group_interface:: state:: TokenGroupMember ;
3233use spl_token_metadata_interface:: state:: TokenMetadata ;
33- use std:: { convert:: TryFrom , str:: FromStr } ;
34+ use std:: { convert:: TryFrom , slice :: Iter , str:: FromStr } ;
3435
3536#[ macro_export]
3637macro_rules! index_ra {
@@ -597,6 +598,83 @@ pub fn pay_creator_fees_in_sol<'info>(
597598 Ok ( total_royalty)
598599}
599600
601+ #[ allow( clippy:: too_many_arguments) ]
602+ pub fn pay_creator_fees_in_sol_cnft < ' info > (
603+ buyside_creator_royalty_bp : u16 ,
604+ total_price : u64 ,
605+ metadata_args : & MetadataArgs ,
606+ creator_accounts : & [ AccountInfo < ' info > ] ,
607+ payer : AccountInfo < ' info > ,
608+ payer_seeds : & [ & [ & [ u8 ] ] ] ,
609+ system_program : AccountInfo < ' info > ,
610+ ) -> Result < u64 > {
611+ // Calculate the total royalty to be paid
612+ let royalty = ( ( total_price as u128 )
613+ . checked_mul ( metadata_args. seller_fee_basis_points as u128 )
614+ . ok_or ( MMMErrorCode :: NumericOverflow ) ?
615+ . checked_div ( 10000 )
616+ . ok_or ( MMMErrorCode :: NumericOverflow ) ?
617+ . checked_mul ( buyside_creator_royalty_bp as u128 )
618+ . ok_or ( MMMErrorCode :: NumericOverflow ) ?
619+ . checked_div ( 10000 )
620+ . ok_or ( MMMErrorCode :: NumericOverflow ) ?) as u64 ;
621+
622+ if royalty == 0 {
623+ return Ok ( 0 ) ;
624+ }
625+
626+ if payer. lamports ( ) < royalty {
627+ return Err ( MMMErrorCode :: NotEnoughBalance . into ( ) ) ;
628+ }
629+
630+ let min_rent = Rent :: get ( ) ?. minimum_balance ( 0 ) ;
631+ let mut total_royalty: u64 = 0 ;
632+
633+ let creator_accounts_iter = & mut creator_accounts. iter ( ) ;
634+ for ( index, creator) in metadata_args. creators . iter ( ) . enumerate ( ) {
635+ let creator_fee = if index == metadata_args. creators . len ( ) - 1 {
636+ royalty
637+ . checked_sub ( total_royalty)
638+ . ok_or ( MMMErrorCode :: NumericOverflow ) ?
639+ } else {
640+ ( royalty as u128 )
641+ . checked_mul ( creator. share as u128 )
642+ . ok_or ( MMMErrorCode :: NumericOverflow ) ?
643+ . checked_div ( 100 )
644+ . ok_or ( MMMErrorCode :: NumericOverflow ) ? as u64
645+ } ;
646+ let current_creator_info = next_account_info ( creator_accounts_iter) ?;
647+ if creator. address . ne ( current_creator_info. key ) {
648+ return Err ( MMMErrorCode :: InvalidCreatorAddress . into ( ) ) ;
649+ }
650+ let current_creator_lamports = current_creator_info. lamports ( ) ;
651+ if creator_fee > 0
652+ && current_creator_lamports
653+ . checked_add ( creator_fee)
654+ . ok_or ( MMMErrorCode :: NumericOverflow ) ?
655+ > min_rent
656+ {
657+ anchor_lang:: solana_program:: program:: invoke_signed (
658+ & anchor_lang:: solana_program:: system_instruction:: transfer (
659+ payer. key ,
660+ current_creator_info. key ,
661+ creator_fee,
662+ ) ,
663+ & [
664+ payer. to_account_info ( ) ,
665+ current_creator_info. to_account_info ( ) ,
666+ system_program. to_account_info ( ) ,
667+ ] ,
668+ payer_seeds,
669+ ) ?;
670+ total_royalty = total_royalty
671+ . checked_add ( creator_fee)
672+ . ok_or ( MMMErrorCode :: NumericOverflow ) ?;
673+ }
674+ }
675+ Ok ( total_royalty)
676+ }
677+
600678pub fn log_pool ( prefix : & str , pool : & Pool ) -> Result < ( ) > {
601679 msg ! ( prefix) ;
602680 sol_log_data ( & [ & pool. try_to_vec ( ) ?] ) ;
@@ -1228,6 +1306,48 @@ pub fn hash_metadata(metadata: &MetadataArgs) -> Result<[u8; 32]> {
12281306 . to_bytes ( ) )
12291307}
12301308
1309+ pub fn verify_creators (
1310+ creator_accounts : Iter < AccountInfo > ,
1311+ creator_shares : Vec < u16 > ,
1312+ creator_verified : Vec < bool > ,
1313+ creator_hash : [ u8 ; 32 ] ,
1314+ ) -> Result < ( ) > {
1315+ // Check that all input arrays/vectors are of the same length
1316+ if creator_accounts. len ( ) != creator_shares. len ( )
1317+ || creator_accounts. len ( ) != creator_verified. len ( )
1318+ {
1319+ return Err ( MMMErrorCode :: MismatchedCreatorDataLengths . into ( ) ) ;
1320+ }
1321+
1322+ // Convert input data to a vector of Creator structs
1323+ let creators: Vec < mpl_bubblegum:: types:: Creator > = creator_accounts
1324+ . zip ( creator_shares. iter ( ) )
1325+ . zip ( creator_verified. iter ( ) )
1326+ . map (
1327+ |( ( account, & share) , & verified) | mpl_bubblegum:: types:: Creator {
1328+ address : * account. key ,
1329+ verified,
1330+ share : share as u8 , // Assuming the share is never more than 255. If it can be, this needs additional checks.
1331+ } ,
1332+ )
1333+ . collect ( ) ;
1334+
1335+ // Compute the hash from the Creator vector
1336+ let computed_hash = hash_creators ( & creators) ;
1337+
1338+ // Compare the computed hash with the provided hash
1339+ if computed_hash != creator_hash {
1340+ msg ! (
1341+ "Computed hash does not match provided hash: {{\" computed\" :{:?},\" provided\" :{:?}}}" ,
1342+ computed_hash,
1343+ creator_hash
1344+ ) ;
1345+ return Err ( MMMErrorCode :: InvalidCreators . into ( ) ) ;
1346+ }
1347+
1348+ Ok ( ( ) )
1349+ }
1350+
12311351#[ cfg( test) ]
12321352mod tests {
12331353 use anchor_spl:: token_2022;
0 commit comments