-
Notifications
You must be signed in to change notification settings - Fork 11
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
[MplCore] Deposit sell #93
Changes from all commits
0ca98ac
f9d2fe2
8100622
56fed32
80d8baf
771ee63
6906bd7
755fc22
c53c0a9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
#![allow(missing_docs)] | ||
|
||
pub mod mpl_core_deposit_sell; | ||
pub mod mpl_core_wrap; | ||
|
||
pub use mpl_core_deposit_sell::*; | ||
pub use mpl_core_wrap::*; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
use anchor_lang::prelude::*; | ||
use mpl_core::instructions::TransferV1Builder; | ||
use mpl_core::types::UpdateAuthority; | ||
use solana_program::program::invoke; | ||
|
||
use crate::{ | ||
constants::*, | ||
errors::MMMErrorCode, | ||
state::{Pool, SellState}, | ||
util::{check_allowlists_for_mpl_core, log_pool}, | ||
AssetInterface, IndexableAsset, | ||
}; | ||
|
||
#[derive(AnchorSerialize, AnchorDeserialize)] | ||
pub struct MplCoreDepositSellArgs { | ||
pub allowlist_aux: Option<String>, | ||
} | ||
|
||
#[derive(Accounts)] | ||
#[instruction(args:MplCoreDepositSellArgs)] | ||
pub struct MplCoreDepositSell<'info> { | ||
#[account(mut)] | ||
pub owner: Signer<'info>, | ||
pub cosigner: Signer<'info>, | ||
#[account( | ||
mut, | ||
seeds = [POOL_PREFIX.as_bytes(), owner.key().as_ref(), pool.uuid.as_ref()], | ||
has_one = owner @ MMMErrorCode::InvalidOwner, | ||
has_one = cosigner @ MMMErrorCode::InvalidCosigner, | ||
bump | ||
)] | ||
pub pool: Box<Account<'info, Pool>>, | ||
#[account( | ||
mut, | ||
constraint = asset.to_account_info().owner == asset_program.key, | ||
)] | ||
pub asset: Box<Account<'info, IndexableAsset>>, | ||
#[account( | ||
init_if_needed, | ||
payer = owner, | ||
seeds = [ | ||
SELL_STATE_PREFIX.as_bytes(), | ||
pool.key().as_ref(), | ||
asset.key().as_ref(), | ||
], | ||
space = SellState::LEN, | ||
bump | ||
)] | ||
pub sell_state: Account<'info, SellState>, | ||
/// CHECK: check collection later | ||
collection: UncheckedAccount<'info>, | ||
|
||
pub system_program: Program<'info, System>, | ||
pub asset_program: Interface<'info, AssetInterface>, | ||
} | ||
|
||
pub fn handler(ctx: Context<MplCoreDepositSell>, args: MplCoreDepositSellArgs) -> Result<()> { | ||
let owner = &ctx.accounts.owner; | ||
let asset = &ctx.accounts.asset; | ||
let pool = &mut ctx.accounts.pool; | ||
let sell_state = &mut ctx.accounts.sell_state; | ||
let collection = &ctx.accounts.collection; | ||
|
||
if pool.using_shared_escrow() { | ||
return Err(MMMErrorCode::InvalidAccountState.into()); | ||
} | ||
|
||
let _ = check_allowlists_for_mpl_core(&pool.allowlists, asset, args.allowlist_aux)?; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: don't need There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. compiler seems will show some warning if I remove them |
||
|
||
let transfer_asset_builder = TransferV1Builder::new() | ||
.asset(asset.key()) | ||
.payer(owner.key()) | ||
.collection( | ||
if let UpdateAuthority::Collection(collection_address) = asset.update_authority { | ||
Some(collection_address) | ||
} else { | ||
None | ||
}, | ||
) | ||
.new_owner(pool.key()) | ||
.instruction(); | ||
|
||
let mut account_infos = vec![ | ||
asset.to_account_info(), | ||
owner.to_account_info(), | ||
pool.to_account_info(), | ||
]; | ||
if collection.key != &Pubkey::default() { | ||
if UpdateAuthority::Collection(collection.key()) != asset.update_authority { | ||
return Err(MMMErrorCode::InvalidAssetCollection.into()); | ||
} | ||
account_infos.push(collection.to_account_info()); | ||
} | ||
|
||
invoke(&transfer_asset_builder, account_infos.as_slice())?; | ||
|
||
pool.sellside_asset_amount = pool | ||
.sellside_asset_amount | ||
.checked_add(1) | ||
.ok_or(MMMErrorCode::NumericOverflow)?; | ||
|
||
sell_state.pool = pool.key(); | ||
sell_state.pool_owner = owner.key(); | ||
sell_state.asset_mint = asset.key(); | ||
sell_state.cosigner_annotation = pool.cosigner_annotation; | ||
sell_state.asset_amount = sell_state | ||
.asset_amount | ||
.checked_add(1) | ||
.ok_or(MMMErrorCode::NumericOverflow)?; | ||
log_pool("post_mpl_core_deposit_sell", pool)?; | ||
|
||
Ok(()) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
use mpl_core::ID; | ||
use solana_program::pubkey::Pubkey; | ||
use std::ops::Deref; | ||
|
||
#[derive(Clone)] | ||
pub struct AssetInterface; | ||
|
||
impl anchor_lang::Ids for AssetInterface { | ||
fn ids() -> &'static [Pubkey] { | ||
&[ID] | ||
} | ||
} | ||
|
||
#[derive(Clone, Debug, Eq, PartialEq)] | ||
pub struct IndexableAsset(mpl_core::IndexableAsset); | ||
|
||
impl anchor_lang::AccountDeserialize for IndexableAsset { | ||
fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result<Self> { | ||
mpl_core::IndexableAsset::fetch(mpl_core::types::Key::AssetV1, buf) | ||
.map(IndexableAsset) | ||
.map_err(Into::into) | ||
} | ||
} | ||
|
||
impl anchor_lang::AccountSerialize for IndexableAsset {} | ||
|
||
impl anchor_lang::Owner for IndexableAsset { | ||
fn owner() -> Pubkey { | ||
ID | ||
} | ||
} | ||
|
||
impl Deref for IndexableAsset { | ||
type Target = mpl_core::IndexableAsset; | ||
|
||
fn deref(&self) -> &Self::Target { | ||
&self.0 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,12 +6,14 @@ use crate::{ | |
}, | ||
errors::MMMErrorCode, | ||
state::*, | ||
IndexableAsset, | ||
}; | ||
use anchor_lang::{prelude::*, solana_program::log::sol_log_data}; | ||
use anchor_spl::token_interface::Mint; | ||
use m2_interface::{ | ||
withdraw_by_mmm_ix_with_program_id, WithdrawByMMMArgs, WithdrawByMmmIxArgs, WithdrawByMmmKeys, | ||
}; | ||
use mpl_core::types::UpdateAuthority; | ||
use mpl_token_metadata::{ | ||
accounts::{MasterEdition, Metadata}, | ||
types::TokenStandard, | ||
|
@@ -783,6 +785,63 @@ pub fn check_allowlists_for_mint_ext( | |
Err(MMMErrorCode::InvalidAllowLists.into()) | ||
} | ||
|
||
pub fn check_allowlists_for_mpl_core( | ||
allowlists: &[Allowlist], | ||
asset: &IndexableAsset, | ||
allowlist_aux: Option<String>, | ||
) -> Result<()> { | ||
if allowlists | ||
.iter() | ||
.any(|&val| val.kind == ALLOWLIST_KIND_METADATA) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. explore if we can make Core's onchain traits verifiable. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will be address in the future when we support onchain attribute offer for mpl core |
||
{ | ||
// If allowlist_aux is not passed in, do not validate URI. | ||
if let Some(ref aux_key) = allowlist_aux { | ||
// Handle URI padding. | ||
if !asset.uri.trim().starts_with(aux_key) { | ||
msg!( | ||
"Failed metadata validation. Expected URI: |{}| but got |{}|", | ||
*aux_key, | ||
asset.uri | ||
); | ||
return Err(MMMErrorCode::UnexpectedMetadataUri.into()); | ||
} | ||
} | ||
} | ||
|
||
for allowlist_val in allowlists.iter() { | ||
match allowlist_val.kind { | ||
ALLOWLIST_KIND_EMPTY => { | ||
continue; | ||
} | ||
ALLOWLIST_KIND_ANY => { | ||
// any is a special case, we don't need to check anything else | ||
return Ok(()); | ||
} | ||
ALLOWLIST_KIND_MPL_CORE_COLLECTION => { | ||
if let UpdateAuthority::Collection(collection_address) = asset.update_authority { | ||
if collection_address != allowlist_val.value { | ||
return Err(MMMErrorCode::InvalidAllowLists.into()); | ||
} | ||
return Ok(()); | ||
} else { | ||
return Err(MMMErrorCode::InvalidAllowLists.into()); | ||
} | ||
} | ||
ALLOWLIST_KIND_METADATA => { | ||
// Do not validate URI here, as we already did it above. | ||
// These checks are separate since allowlist values are unioned together. | ||
continue; | ||
} | ||
_ => { | ||
return Err(MMMErrorCode::InvalidAllowLists.into()); | ||
} | ||
} | ||
} | ||
|
||
// at the end, we didn't find a match, thus return err | ||
Err(MMMErrorCode::InvalidAllowLists.into()) | ||
} | ||
|
||
pub fn assert_and_get_valid_group(mint: &AccountInfo) -> Result<Option<Pubkey>> { | ||
let borrowed_data = mint.data.borrow(); | ||
let mint_deserialized = StateWithExtensions::<Token22Mint>::unpack(&borrowed_data)?; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,7 @@ export enum AllowlistKind { | |
mcc = 3, | ||
metadata = 4, | ||
group = 5, | ||
mpl_core_collection = 6, | ||
any = 255, | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we check the ownership here? maybe cpi of transfer does this, good
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will reply on cpi check for now