-
Notifications
You must be signed in to change notification settings - Fork 253
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
Feat/payout #778
Feat/payout #778
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ use super::resolver::NonFungibleTokenResolver; | |
use crate::non_fungible_token::core::NonFungibleTokenCore; | ||
use crate::non_fungible_token::events::{NftMint, NftTransfer}; | ||
use crate::non_fungible_token::metadata::TokenMetadata; | ||
use crate::non_fungible_token::payout::Royalties; | ||
use crate::non_fungible_token::token::{Token, TokenId}; | ||
use crate::non_fungible_token::utils::{ | ||
hash_account_id, refund_approved_account_ids, refund_deposit_to_account, | ||
|
@@ -72,6 +73,7 @@ pub struct NonFungibleToken { | |
// required by approval extension | ||
pub approvals_by_id: Option<LookupMap<TokenId, HashMap<AccountId, u64>>>, | ||
pub next_approval_id_by_id: Option<LookupMap<TokenId, u64>>, | ||
pub royalties: Option<Royalties>, | ||
} | ||
|
||
#[derive(BorshStorageKey, BorshSerialize)] | ||
|
@@ -81,18 +83,20 @@ pub enum StorageKey { | |
} | ||
|
||
impl NonFungibleToken { | ||
pub fn new<Q, R, S, T>( | ||
pub fn new<Q, R, S, T, Y>( | ||
owner_by_id_prefix: Q, | ||
owner_id: AccountId, | ||
token_metadata_prefix: Option<R>, | ||
enumeration_prefix: Option<S>, | ||
approval_prefix: Option<T>, | ||
royalties_prefix: Option<Y>, | ||
) -> Self | ||
where | ||
Q: IntoStorageKey, | ||
R: IntoStorageKey, | ||
S: IntoStorageKey, | ||
T: IntoStorageKey, | ||
Y: IntoStorageKey, | ||
{ | ||
let (approvals_by_id, next_approval_id_by_id) = if let Some(prefix) = approval_prefix { | ||
let prefix: Vec<u8> = prefix.into_storage_key(); | ||
|
@@ -112,6 +116,7 @@ impl NonFungibleToken { | |
tokens_per_owner: enumeration_prefix.map(LookupMap::new), | ||
approvals_by_id, | ||
next_approval_id_by_id, | ||
royalties: royalties_prefix.map(Royalties::new), | ||
}; | ||
this.measure_min_token_storage_cost(); | ||
this | ||
|
@@ -426,7 +431,10 @@ impl NonFungibleTokenCore for NonFungibleToken { | |
msg: String, | ||
) -> PromiseOrValue<bool> { | ||
assert_one_yocto(); | ||
require!(env::prepaid_gas() > GAS_FOR_NFT_TRANSFER_CALL, "More gas is required"); | ||
require!( | ||
env::prepaid_gas() > GAS_FOR_NFT_TRANSFER_CALL + GAS_FOR_RESOLVE_TRANSFER, | ||
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. revert this change please |
||
"More gas is required" | ||
); | ||
let sender_id = env::predecessor_account_id(); | ||
let (old_owner, old_approvals) = | ||
self.internal_transfer(&sender_id, &receiver_id, &token_id, approval_id, memo); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,9 @@ mod macros; | |
/// Metadata traits and implementation according to the [NFT enumeration standard](https://nomicon.io/Standards/NonFungibleToken/Metadata.html). | ||
/// This covers both the contract metadata and the individual token metadata. | ||
pub mod metadata; | ||
|
||
pub mod payout; | ||
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. Module isn't documented here or in can you describe this and link to the spec there using |
||
|
||
/// The Token struct for the non-fungible token. | ||
mod token; | ||
pub use self::token::{Token, TokenId}; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
mod payout_impl; | ||
|
||
use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize}; | ||
use near_sdk::json_types::U128; | ||
use near_sdk::AccountId; | ||
use serde::{Deserialize, Serialize}; | ||
use std::collections::HashMap; | ||
#[derive(Default, BorshDeserialize, BorshSerialize, Serialize, Deserialize)] | ||
#[serde(crate = "near_sdk::serde")] | ||
|
||
pub struct Payout { | ||
pub payout: HashMap<AccountId, U128>, | ||
} | ||
#[derive(Deserialize, Serialize, BorshDeserialize, BorshSerialize, Default, Debug)] | ||
#[serde(crate = "near_sdk::serde")] | ||
pub struct Royalties { | ||
key_prefix: Vec<u8>, | ||
pub accounts: HashMap<AccountId, u8>, | ||
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. Also should be basis points. 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. No Problem |
||
pub percent: u8, | ||
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. Should use basis points: see https://github.com/TENK-DAO/tenk/blob/main/contracts/tenk/src/payout.rs |
||
} | ||
/// Offers methods helpful in determining account ownership of NFTs and provides a way to page through NFTs per owner, determine total supply, etc. | ||
pub trait NonFungibleTokenPayout { | ||
fn nft_payout(&self, token_id: String, balance: U128, max_len_payout: Option<u32>) -> Payout; | ||
/// Given a `token_id` and NEAR-denominated balance, transfer the token | ||
/// and return the `Payout` struct for the given token. Panic if the | ||
/// length of the payout exceeds `max_len_payout.` | ||
fn nft_transfer_payout( | ||
&mut self, | ||
receiver_id: AccountId, | ||
token_id: String, | ||
approval_id: Option<u64>, | ||
memo: Option<String>, | ||
balance: U128, | ||
max_len_payout: Option<u32>, | ||
) -> Payout; | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,80 @@ | ||||||||||||
use super::NonFungibleTokenPayout; | ||||||||||||
use crate::non_fungible_token::core::NonFungibleTokenCore; | ||||||||||||
use crate::non_fungible_token::payout::*; | ||||||||||||
use crate::non_fungible_token::NonFungibleToken; | ||||||||||||
use near_sdk::assert_one_yocto; | ||||||||||||
use near_sdk::{require, AccountId, Balance, IntoStorageKey}; | ||||||||||||
use std::collections::HashMap; | ||||||||||||
impl Royalties { | ||||||||||||
Comment on lines
+7
to
+8
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.
Suggested change
|
||||||||||||
/* pub fn new(accounts: HashMap<AccountId, u8>, percent: u8) -> Self { | ||||||||||||
let this = Self { accounts, percent }; | ||||||||||||
this.validate(); | ||||||||||||
this | ||||||||||||
} */ | ||||||||||||
Comment on lines
+9
to
+13
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.
Suggested change
|
||||||||||||
pub fn new<S>(key_prefix: S) -> Self | ||||||||||||
where | ||||||||||||
S: IntoStorageKey, | ||||||||||||
{ | ||||||||||||
let temp_accounts: HashMap<AccountId, u8> = HashMap::new(); | ||||||||||||
let this = | ||||||||||||
Self { key_prefix: key_prefix.into_storage_key(), accounts: temp_accounts, percent: 0 }; | ||||||||||||
this.validate(); | ||||||||||||
this | ||||||||||||
} | ||||||||||||
pub(crate) fn validate(&self) { | ||||||||||||
require!(self.percent <= 100, "royalty percent must be between 0 - 100"); | ||||||||||||
require!( | ||||||||||||
self.accounts.len() <= 10, | ||||||||||||
"can only have a maximum of 10 accounts spliting royalties" | ||||||||||||
); | ||||||||||||
let mut total: u8 = 0; | ||||||||||||
self.accounts.iter().for_each(|(_, percent)| { | ||||||||||||
require!(*percent <= 100, "each royalty should be less than 100"); | ||||||||||||
total += percent; | ||||||||||||
}); | ||||||||||||
require!(total <= 100, "total percent of each royalty split must be less than 100") | ||||||||||||
} | ||||||||||||
pub fn create_payout(&self, balance: Balance, owner_id: &AccountId) -> Payout { | ||||||||||||
let royalty_payment = apply_percent(self.percent, balance); | ||||||||||||
let mut payout = Payout { | ||||||||||||
payout: self | ||||||||||||
.accounts | ||||||||||||
.iter() | ||||||||||||
.map(|(account, percent)| { | ||||||||||||
(account.clone(), apply_percent(*percent, royalty_payment).into()) | ||||||||||||
}) | ||||||||||||
.collect(), | ||||||||||||
}; | ||||||||||||
let rest = balance - royalty_payment; | ||||||||||||
let owner_payout: u128 = payout.payout.get(owner_id).map_or(0, |x| x.0) + rest; | ||||||||||||
payout.payout.insert(owner_id.clone(), owner_payout.into()); | ||||||||||||
payout | ||||||||||||
} | ||||||||||||
} | ||||||||||||
|
||||||||||||
fn apply_percent(percent: u8, int: u128) -> u128 { | ||||||||||||
int * percent as u128 / 100u128 | ||||||||||||
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. Seems like this can overflow, are there any guarantees around this? Probably want to do a checked multiplication with an |
||||||||||||
} | ||||||||||||
|
||||||||||||
impl NonFungibleTokenPayout for NonFungibleToken { | ||||||||||||
#[allow(unused_variables)] | ||||||||||||
fn nft_payout(&self, token_id: String, balance: U128, max_len_payout: Option<u32>) -> Payout { | ||||||||||||
let owner_id = self.owner_by_id.get(&token_id).expect("No such token_id"); | ||||||||||||
self.royalties.as_ref().map_or(Payout::default(), |r| r.create_payout(balance.0, &owner_id)) | ||||||||||||
} | ||||||||||||
|
||||||||||||
fn nft_transfer_payout( | ||||||||||||
&mut self, | ||||||||||||
receiver_id: AccountId, | ||||||||||||
token_id: String, | ||||||||||||
approval_id: Option<u64>, | ||||||||||||
memo: Option<String>, | ||||||||||||
balance: U128, | ||||||||||||
max_len_payout: Option<u32>, | ||||||||||||
) -> Payout { | ||||||||||||
assert_one_yocto(); | ||||||||||||
let payout = self.nft_payout(token_id.clone(), balance, max_len_payout); | ||||||||||||
self.nft_transfer(receiver_id, token_id, approval_id, memo); | ||||||||||||
payout | ||||||||||||
} | ||||||||||||
} |
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.
can you revert this spacing?
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.
No Problem