Merge remote-tracking branch 'origin/main' into feature/m

This commit is contained in:
bartosz-lipinski 2021-04-28 17:31:01 -05:00
commit 45ce75fc1b
13 changed files with 4 additions and 3164 deletions

View File

@ -1,6 +1,8 @@
<html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charSet="utf-8"/>
<title>Metaplex NFT Marketplace</title>
<meta content="width=device-width, initial-scale=1" name="viewport" />
@ -44,4 +46,4 @@
</div>
</body>
</html>
</html>

File diff suppressed because it is too large Load Diff

View File

@ -1,29 +0,0 @@
[package]
name = "metavinci"
version = "0.1.0"
description = "Fractional Auction NFT"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/oyster"
license = "Apache-2.0"
edition = "2018"
[features]
no-entrypoint = []
production = []
[dependencies]
arrayref = "0.3.6"
enum_dispatch = "0.3.4"
num-derive = "0.3"
num-traits = "0.2"
solana-program = "1.6.1"
spl-token = { version = "3.0", features = [ "no-entrypoint" ] }
thiserror = "1.0"
arbitrary = { version = "0.4", features = ["derive"], optional = true }
[dev-dependencies]
solana-sdk = "1.6.1"
proptest = "0.10"
[lib]
crate-type = ["cdylib", "lib"]

View File

@ -1,2 +0,0 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []

View File

@ -1,21 +0,0 @@
//! Program entrypoint definitions
use crate::{error::AuctionError, processor::Processor};
use solana_program::{
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult,
program_error::PrintProgramError, pubkey::Pubkey,
};
entrypoint!(process_instruction);
fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
) -> ProgramResult {
if let Err(error) = Processor::process(program_id, accounts, instruction_data) {
// catch the error so we can print it
error.print::<AuctionError>();
return Err(error);
}
Ok(())
}

View File

@ -1,59 +0,0 @@
//! Error types
use num_derive::FromPrimitive;
use solana_program::{decode_error::DecodeError, program_error::ProgramError};
use thiserror::Error;
/// Errors that may be returned by the TokenSwap program.
#[derive(Clone, Debug, Eq, Error, FromPrimitive, PartialEq)]
pub enum AuctionError {
/// Invalid instruction data passed in.
#[error("Failed to unpack instruction data")]
InstructionUnpackError,
/// The account cannot be initialized because it is already being used.
#[error("Auction account already in use")]
AlreadyInUse,
/// The program address provided doesn't match the value generated by the program.
#[error("Invalid program address generated from nonce and key")]
InvalidProgramAddress,
/// The owner of the input isn't set to the program address generated by the program.
#[error("Input account owner is not the program address")]
InvalidOwner,
/// The owner of the pool token output is set to the program address generated by the program.
#[error("Output pool account owner cannot be the program address")]
InvalidOutputOwner,
/// The deserialization of the account returned something besides State::Mint.
#[error("Deserialized account is not an SPL Token mint")]
ExpectedMint,
/// The deserialization of the account returned something besides State::Account.
#[error("Deserialized account is not an SPL Token account")]
ExpectedAccount,
/// The input token account is empty.
#[error("Input token account empty")]
EmptySupply,
/// The pool token mint has a non-zero supply.
#[error("Pool token mint has a non-zero supply")]
InvalidSupply,
/// The provided token account has a delegate.
#[error("Token account has a delegate")]
InvalidDelegate,
/// The input token is invalid for swap.
#[error("InvalidInput")]
InvalidInput,
/// The provided token program does not match the token program expected by the swap
#[error("The provided token program does not match the token program expected by the swap")]
IncorrectTokenProgramId,
/// Invalid instruction number passed in.
#[error("Invalid instruction")]
InvalidInstruction,
}
impl From<AuctionError> for ProgramError {
fn from(e: AuctionError) -> Self {
ProgramError::Custom(e as u32)
}
}
impl<T> DecodeError<T> for AuctionError {
fn type_of() -> &'static str {
"Fractional Auction Error"
}
}

View File

@ -1,187 +0,0 @@
//! Instruction types
#![allow(clippy::too_many_arguments)]
use num_derive::{FromPrimitive, ToPrimitive};
use num_traits::{FromPrimitive, ToPrimitive};
use solana_program::{
instruction::{AccountMeta, Instruction},
program_error::ProgramError,
program_pack::Pack,
pubkey::Pubkey,
};
use std::convert::TryInto;
use std::mem::size_of;
use crate::error::AuctionError;
/// Describe how the borrow input amount should be treated
#[derive(Clone, Copy, Debug, PartialEq, FromPrimitive, ToPrimitive)]
pub enum AuctionStartType {
/// Treat amount as reservation price that needs to be reached before auction starts
/// This is not a hidden reservation price for english auction but a reservation price for
/// AMM phase
ReservationPrice,
/// Treat amount as relative number of slots that needs to elapsed before auction starts
RelativeSlot,
}
/// Instructions supported by the token swap program.
#[derive(Clone, Debug, PartialEq)]
pub enum AuctionInstruction {
/// Create Auction
///
///
InitAuction {
/// Owner authority which can add new reserves
owner: Pubkey,
/// number of shards, NFT will be fractionalized into
shards: u64,
/// number of shards retained by the creator of the pool (expressed as percent)
retained_shards: u64,
/// amount of quote token used to initialize AMM
amount: u64,
/// Wait time before bid is settled
bid_timelock: u8,
/// Type of wait before auction starts
auction_start_type: AuctionStartType, // switch to enum
/// Amount that defines when auction starts
auction_start_amount: u64,
},
/// Places bid by the user during
Bid {
/// Bid amount
amount: u64, // this amount should be locked and can be refunded to the bidder
},
/// Settles auction after successful bid
Settle {},
/// Allows shard holder withdraw tokens from vault after auction is settled
Withdraw {
/// amount of shards that users is exchanging for quote token
amount: u64,
},
/// Allows shard holder withdraw tokens from vault after auction is settled
CloseMarket {},
}
impl AuctionInstruction {
/// Unpacks a byte buffer into a [AuctionInstruction](enum.AuctionInstruction.html).
pub fn unpack(input: &[u8]) -> Result<Self, ProgramError> {
let (&tag, rest) = input
.split_first()
.ok_or(AuctionError::InstructionUnpackError)?;
Ok(match tag {
0 => {
let (owner, _rest) = Self::unpack_pubkey(rest)?;
let (shards, rest) = Self::unpack_u64(rest)?;
let (retained_shards, rest) = Self::unpack_u64(rest)?;
let (amount, rest) = Self::unpack_u64(rest)?;
let (bid_timelock, rest) = Self::unpack_u8(rest)?;
let (auction_start_type, rest) = Self::unpack_u8(rest)?;
let auction_start_type = AuctionStartType::from_u8(auction_start_type)
.ok_or(AuctionError::InstructionUnpackError)?;
let (auction_start_amount, rest) = Self::unpack_u64(rest)?;
Self::InitAuction {
owner,
shards,
retained_shards,
amount,
bid_timelock,
auction_start_type,
auction_start_amount,
}
}
_ => return Err(AuctionError::InstructionUnpackError.into()),
})
}
/// Packs a [AuctionInstruction](enum.AuctionInstruction.html) into a byte buffer.
pub fn pack(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(size_of::<Self>());
match *self {
Self::InitAuction {
owner,
shards,
retained_shards,
amount,
bid_timelock,
auction_start_type,
auction_start_amount,
} => {
buf.push(0);
buf.extend_from_slice(&owner.as_ref());
buf.extend_from_slice(&shards.to_le_bytes());
buf.extend_from_slice(&retained_shards.to_le_bytes());
buf.extend_from_slice(&amount.to_le_bytes());
buf.extend_from_slice(&bid_timelock.to_le_bytes());
buf.extend_from_slice(&auction_start_type.to_u8().unwrap().to_le_bytes());
buf.extend_from_slice(&auction_start_amount.to_le_bytes());
}
Self::Bid { amount } => {
buf.push(1);
}
Self::Settle {} => {
buf.push(2);
}
Self::Settle {} => {
buf.push(3);
}
Self::Withdraw { amount } => {
buf.push(4);
}
Self::CloseMarket {} => {
buf.push(5);
}
}
buf
}
fn unpack_pubkey(input: &[u8]) -> Result<(Pubkey, &[u8]), ProgramError> {
if input.len() >= 32 {
let (key, rest) = input.split_at(32);
let pk = Pubkey::new(key);
Ok((pk, rest))
} else {
Err(AuctionError::InvalidInstruction.into())
}
}
fn unpack_u64(input: &[u8]) -> Result<(u64, &[u8]), ProgramError> {
if input.len() >= 8 {
let (amount, rest) = input.split_at(8);
let amount = amount
.get(..8)
.and_then(|slice| slice.try_into().ok())
.map(u64::from_le_bytes)
.ok_or(AuctionError::InstructionUnpackError)?;
Ok((amount, rest))
} else {
Err(AuctionError::InstructionUnpackError.into())
}
}
fn unpack_u8(input: &[u8]) -> Result<(u8, &[u8]), ProgramError> {
if !input.is_empty() {
let (amount, rest) = input.split_at(1);
let amount = amount
.get(..1)
.and_then(|slice| slice.try_into().ok())
.map(u8::from_le_bytes)
.ok_or(AuctionError::InstructionUnpackError)?;
Ok((amount, rest))
} else {
Err(AuctionError::InstructionUnpackError.into())
}
}
}

View File

@ -1,16 +0,0 @@
#![deny(missing_docs)]
//! Fractional non-fungible-token program for the Solana blockchain that exposes AMM and Auction mechanics for NFTs.
pub mod error;
pub mod instruction;
pub mod processor;
pub mod state;
#[cfg(not(feature = "no-entrypoint"))]
mod entrypoint;
// Export current sdk types for downstream users building with a different sdk version
pub use solana_program;
solana_program::declare_id!("vinRJ4LvgnNjgeS9aDEw7YvE7UywgTMA3F1GbjRmVxW");

View File

@ -1,188 +0,0 @@
//! Program state processor
use num_traits::FromPrimitive;
use solana_program::{
account_info::AccountInfo,
decode_error::DecodeError,
entrypoint::ProgramResult,
msg,
program::invoke_signed,
program_error::{PrintProgramError, ProgramError},
program_pack::Pack,
pubkey::Pubkey,
};
use crate::{error::AuctionError, instruction::AuctionInstruction};
/// Program state handler.
pub struct Processor {}
impl Processor {
/// Unpacks a spl_token `Account`.
pub fn unpack_token_account(
account_info: &AccountInfo,
token_program_id: &Pubkey,
) -> Result<spl_token::state::Account, AuctionError> {
if account_info.owner != token_program_id {
Err(AuctionError::IncorrectTokenProgramId)
} else {
spl_token::state::Account::unpack(&account_info.data.borrow())
.map_err(|_| AuctionError::ExpectedAccount)
}
}
/// Unpacks a spl_token `Mint`.
pub fn unpack_mint(
account_info: &AccountInfo,
token_program_id: &Pubkey,
) -> Result<spl_token::state::Mint, AuctionError> {
if account_info.owner != token_program_id {
Err(AuctionError::IncorrectTokenProgramId)
} else {
spl_token::state::Mint::unpack(&account_info.data.borrow())
.map_err(|_| AuctionError::ExpectedMint)
}
}
/// Calculates the authority id by generating a program address.
pub fn authority_id(
program_id: &Pubkey,
my_info: &Pubkey,
nonce: u8,
) -> Result<Pubkey, AuctionError> {
Pubkey::create_program_address(&[&my_info.to_bytes()[..32], &[nonce]], program_id)
.or(Err(AuctionError::InvalidProgramAddress))
}
/// Issue a spl_token `Burn` instruction.
pub fn token_burn<'a>(
swap: &Pubkey,
token_program: AccountInfo<'a>,
burn_account: AccountInfo<'a>,
mint: AccountInfo<'a>,
authority: AccountInfo<'a>,
nonce: u8,
amount: u64,
) -> Result<(), ProgramError> {
let swap_bytes = swap.to_bytes();
let authority_signature_seeds = [&swap_bytes[..32], &[nonce]];
let signers = &[&authority_signature_seeds[..]];
let ix = spl_token::instruction::burn(
token_program.key,
burn_account.key,
mint.key,
authority.key,
&[],
amount,
)?;
invoke_signed(
&ix,
&[burn_account, mint, authority, token_program],
signers,
)
}
/// Issue a spl_token `MintTo` instruction.
pub fn token_mint_to<'a>(
swap: &Pubkey,
token_program: AccountInfo<'a>,
mint: AccountInfo<'a>,
destination: AccountInfo<'a>,
authority: AccountInfo<'a>,
nonce: u8,
amount: u64,
) -> Result<(), ProgramError> {
let swap_bytes = swap.to_bytes();
let authority_signature_seeds = [&swap_bytes[..32], &[nonce]];
let signers = &[&authority_signature_seeds[..]];
let ix = spl_token::instruction::mint_to(
token_program.key,
mint.key,
destination.key,
authority.key,
&[],
amount,
)?;
invoke_signed(&ix, &[mint, destination, authority, token_program], signers)
}
/// Issue a spl_token `Transfer` instruction.
pub fn token_transfer<'a>(
swap: &Pubkey,
token_program: AccountInfo<'a>,
source: AccountInfo<'a>,
destination: AccountInfo<'a>,
authority: AccountInfo<'a>,
nonce: u8,
amount: u64,
) -> Result<(), ProgramError> {
let swap_bytes = swap.to_bytes();
let authority_signature_seeds = [&swap_bytes[..32], &[nonce]];
let signers = &[&authority_signature_seeds[..]];
let ix = spl_token::instruction::transfer(
token_program.key,
source.key,
destination.key,
authority.key,
&[],
amount,
)?;
invoke_signed(
&ix,
&[source, destination, authority, token_program],
signers,
)
}
/// Processes an [Instruction](enum.Instruction.html).
pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult {
let instruction = AuctionInstruction::unpack(input)?;
Ok(match instruction {
AuctionInstruction::InitAuction {
owner,
shards,
retained_shards,
amount,
bid_timelock,
auction_start_type,
auction_start_amount,
} => {
msg!("Instruction: Init Auction");
// process_init_market(
// program_id,
// owner,
// shards,
// retained_shards,
// amount,
// bid_timelock,
// auction_start_type,
// auction_start_amount,
// accounts,
// );
}
AuctionInstruction::Bid { amount } => {
msg!("Instruction: Auction Bid");
}
AuctionInstruction::Settle {} => {
msg!("Instruction: Auction Settle");
}
AuctionInstruction::Withdraw { amount } => {
msg!("Instruction: Auction Withdraw");
}
AuctionInstruction::CloseMarket {} => {
msg!("Instruction: Close Market");
}
})
}
}
impl PrintProgramError for AuctionError {
fn print<E>(&self)
where
E: 'static + std::error::Error + DecodeError<E> + PrintProgramError + FromPrimitive,
{
msg!(&self.to_string());
}
}

View File

@ -1,35 +0,0 @@
//! State transition types
use arrayref::{array_mut_ref, array_ref, array_refs, mut_array_refs};
use enum_dispatch::enum_dispatch;
use solana_program::{
program_error::ProgramError,
program_pack::{IsInitialized, Pack, Sealed},
pubkey::Pubkey,
};
/// Trait representing access to program state across all versions
#[enum_dispatch]
pub trait AuctionState {
/// Is the auction initialized, with data written to it
fn is_initialized(&self) -> bool;
/// Bump seed used to generate the program address / authority
fn nonce(&self) -> u8;
/// Token program ID associated with the swap
fn token_program_id(&self) -> &Pubkey;
/// Address of NFT vault account
fn vault_account(&self) -> &Pubkey;
/// Address of quote treasury token account
fn treasury_quote_account(&self) -> &Pubkey;
/// Address of pool token mint
fn token_swap_pool(&self) -> &Pubkey;
/// Address of lp treasury token account - stores lp tokens deposited to AMM
fn treasury_lp_account(&self) -> &Pubkey;
/// Address of quote token mint
fn quote_mint(&self) -> &Pubkey;
/// Address of NFT token mint
fn vault_mint(&self) -> &Pubkey;
/// Address of LP token mint
fn lp_mint(&self) -> &Pubkey;
}

View File

@ -1,22 +0,0 @@
//! State transition types
use arrayref::{array_mut_ref, array_ref, array_refs, mut_array_refs};
use enum_dispatch::enum_dispatch;
use solana_program::{
program_error::ProgramError,
program_pack::{IsInitialized, Pack, Sealed},
pubkey::Pubkey,
};
/// Trait representing access to program state across all versions
#[enum_dispatch]
pub trait BidState {
/// Is the auction initialized, with data written to it
fn is_initialized(&self) -> bool;
/// Token program ID associated with the swap
fn amount(&self) -> &Pubkey;
/// Address of quote vault account
fn vault_account(&self) -> &Pubkey;
/// Address of NFT vault account
fn vote_slot(&self) -> &u64;
}

View File

@ -1,35 +0,0 @@
//! State transition types
use arrayref::{array_mut_ref, array_ref, array_refs, mut_array_refs};
use enum_dispatch::enum_dispatch;
use solana_program::{
program_error::ProgramError,
program_pack::{IsInitialized, Pack, Sealed},
pubkey::Pubkey,
};
/// Trait representing access to program state across all versions
#[enum_dispatch]
pub trait AuctionState {
/// Is the auction initialized, with data written to it
fn is_initialized(&self) -> bool;
/// Bump seed used to generate the program address / authority
fn nonce(&self) -> u8;
/// Token program ID associated with the swap
fn token_program_id(&self) -> &Pubkey;
/// Address of NFT vault account
fn vault_account(&self) -> &Pubkey;
/// Address of quote treasury token account
fn treasury_quote_account(&self) -> &Pubkey;
/// Address of pool token mint
fn token_swap_pool(&self) -> &Pubkey;
/// Address of lp treasury token account - stores lp tokens deposited to AMM
fn treasury_lp_account(&self) -> &Pubkey;
/// Address of quote token mint
fn quote_mint(&self) -> &Pubkey;
/// Address of NFT token mint
fn vault_mint(&self) -> &Pubkey;
/// Address of LP token mint
fn lp_mint(&self) -> &Pubkey;
}