Skip to content
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(NFT): add events to standard #627

Merged
merged 48 commits into from
Jan 25, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
a6e16e8
feat(NFT): add events to standard
willemneal Nov 4, 2021
3238e4a
Merge remote-tracking branch 'origin/master' into feat/event_logs
willemneal Nov 8, 2021
b65081a
feat: add last two events and add tests
willemneal Nov 8, 2021
19151a1
fix: fmt
willemneal Nov 8, 2021
4b8f0aa
fix: use near indexer's types
willemneal Nov 10, 2021
d27974f
Merge remote-tracking branch 'origin/master' into feat/event_logs
willemneal Nov 30, 2021
c32de7a
feat: add new methods; address feedback and move file up a directory
willemneal Dec 1, 2021
a32852b
chore: update wasm binaries
willemneal Dec 1, 2021
1ddc497
fix: fmt
willemneal Dec 1, 2021
d4a4c8b
Merge remote-tracking branch 'origin/master' into feat/event_logs
willemneal Dec 8, 2021
344d483
Merge remote-tracking branch 'origin/master' into feat/event_logs
willemneal Dec 13, 2021
30a2be5
feat: use Cow and generics to avoid copying strings & remove Display
willemneal Dec 13, 2021
00e0bf5
feat: rename to emit
willemneal Dec 13, 2021
bc403d5
feat: refactor emit functions to use &str instead of generic
willemneal Dec 13, 2021
b6bc5f3
fix: fmt
willemneal Dec 13, 2021
8fc2c80
feat: remove emit_* and add must_use attribute
willemneal Dec 14, 2021
62694c9
add the annoying wasm files
willemneal Dec 14, 2021
0726e8e
Merge remote-tracking branch 'origin/master' into feat/event_logs
willemneal Dec 14, 2021
f9fb90f
Merge remote-tracking branch 'origin/master' into feat/event_logs
willemneal Dec 16, 2021
2f2c0a2
feat: make nft data constructors generic & add from method to AccountId
willemneal Dec 16, 2021
7202200
feat: use event logs in standard
willemneal Dec 16, 2021
0917ab5
fix: fmt
willemneal Dec 16, 2021
62112ad
fix: duplicate import
willemneal Dec 16, 2021
4d494e8
fix: emit event for internal_mint
willemneal Dec 17, 2021
a01075c
Merge remote-tracking branch 'origin/master' into feat/event_logs
willemneal Dec 17, 2021
63beb49
Merge remote-tracking branch 'origin/master' into feat/event_logs
willemneal Dec 20, 2021
c184d9e
Merge branch 'master' into feat/event_logs
willemneal Dec 22, 2021
79a319e
Merge remote-tracking branch 'origin/master' into feat/event_logs
willemneal Dec 23, 2021
3524ffd
feat: ensure log when `nft_transfer_call`s receiver fails
willemneal Jan 12, 2022
c0ef525
Merge remote-tracking branch 'origin/master' into feat/event_logs
willemneal Jan 12, 2022
db7d5d4
fix: fmt
willemneal Jan 12, 2022
83a3b75
fix:wasm
willemneal Jan 12, 2022
38656b0
Merge branch 'master' into feat/event_logs
willemneal Jan 13, 2022
740a594
feat: add test to show that gas check is sufficient
willemneal Jan 13, 2022
9d7474c
fix: test correctly failed; fixed to actually not have enough gas
willemneal Jan 13, 2022
9c561e0
Merge branch 'master' into feat/event_logs
willemneal Jan 13, 2022
7187af3
Merge branch 'master' into feat/event_logs
willemneal Jan 18, 2022
3459d27
Merge branch 'master' into feat/event_logs
willemneal Jan 18, 2022
1404f55
Update near-contract-standards/src/event.rs
willemneal Jan 20, 2022
1746487
chore: add more documentation and edit CHANGELOG
willemneal Jan 20, 2022
9d0d28d
feat: switch to `&'a str` since the event will have shorter lifetime
willemneal Jan 20, 2022
1ce258b
fix: update wasm
willemneal Jan 20, 2022
64c1c6f
chore: add more documentation to core_impl
willemneal Jan 20, 2022
d004203
Update near-contract-standards/src/event.rs
willemneal Jan 20, 2022
179faf1
fix: annoying wasm files
willemneal Jan 20, 2022
bce9abe
Merge branch 'master' into feat/event_logs
willemneal Jan 20, 2022
ae405da
Merge remote-tracking branch 'origin/master' into feat/event_logs
willemneal Jan 21, 2022
173f6f7
Merge branch 'feat/event_logs'
austinabell Jan 25, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 71 additions & 105 deletions near-contract-standards/src/event.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,4 @@

/// This file makes heavy use of Copy-on-write references to strings.
/// Since the logs should be light weight they should not clone any string.
/// Using generic arguments bound to `Into<Cow<'a, str>>` any reference passed that uses
/// can provide a reference to a string can be used. E.g. `&String, String, &str, &AccountId`
/// This also makes it more ergonomic since not only one type can be passed.
///
/// The unfortunate aspect to this, however, is that the lifetime `'a` is introduced.
/// This is so that if the `Cow` value has a longer lifetime than the reference it is pointing to it will
/// copy the string to ensure it still has access to it. Hence, copy-on-write, where write here is
/// dropping the reference.
///
/// While this makes the internal types more complicated the external API will automatically provide the lifetime.
///
/// See discussion in this PR for more details: https://github.com/near/near-sdk-rs/pull/627
use std::borrow::Cow;

use near_sdk::AccountId;
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;

Expand Down Expand Up @@ -51,64 +35,52 @@ pub enum Nep171EventKind<'a> {
#[derive(Serialize, Deserialize, Debug)]
pub struct NftMintData<'a> {
#[serde(borrow)]
pub owner_id: Cow<'a, str>,
pub owner_id: &'a str,
#[serde(borrow)]
pub token_ids: Vec<Cow<'a, str>>,
pub token_ids: Vec<&'a str>,
#[serde(borrow)]
pub memo: Option<Cow<'a, str>>,
pub memo: Option<&'a str>,
}

impl<'a> NftMintData<'a> {
pub fn new<O, T, M>(owner_id: O, token_ids: Vec<T>, memo: Option<M>) -> NftMintData<'a>
where
O: Into<Cow<'a, str>>,
T: Into<Cow<'a, str>>,
M: Into<Cow<'a, str>>,
{
Self {
owner_id: owner_id.into(),
token_ids: token_ids.into_iter().map(Into::into).collect(),
memo: memo.map(Into::into),
}
pub fn new(
owner_id: &'a AccountId,
token_ids: Vec<&'a str>,
memo: Option<&'a str>,
) -> NftMintData<'a> {
Self { owner_id: owner_id.as_str(), token_ids, memo }
}
}

#[skip_serializing_none]
#[derive(Serialize, Deserialize, Debug)]
pub struct NftTransferData<'a> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice if there were docs on some of these, but that can always be added in follow up PR

#[serde(borrow)]
pub old_owner_id: Cow<'a, str>,
pub old_owner_id: &'a str,
#[serde(borrow)]
pub new_owner_id: Cow<'a, str>,
pub new_owner_id: &'a str,
#[serde(borrow)]
pub token_ids: Vec<Cow<'a, str>>,
pub token_ids: Vec<&'a str>,
#[serde(borrow)]
pub authorized_id: Option<Cow<'a, str>>,
pub authorized_id: Option<&'a str>,
#[serde(borrow)]
pub memo: Option<Cow<'a, str>>,
pub memo: Option<&'a str>,
}

impl<'a> NftTransferData<'a> {
pub fn new<O, N, T, A, M>(
old_owner_id: O,
new_owner_id: N,
token_ids: Vec<T>,
authorized_id: Option<A>,
memo: Option<M>,
) -> NftTransferData<'a>
where
O: Into<Cow<'a, str>>,
N: Into<Cow<'a, str>>,
T: Into<Cow<'a, str>>,
A: Into<Cow<'a, str>>,
M: Into<Cow<'a, str>>,
{
pub fn new(
old_owner_id: &'a AccountId,
new_owner_id: &'a AccountId,
token_ids: Vec<&'a str>,
austinabell marked this conversation as resolved.
Show resolved Hide resolved
authorized_id: Option<&'a AccountId>,
memo: Option<&'a str>,
) -> NftTransferData<'a> {
Self {
authorized_id: authorized_id.map(|t| t.into()),
old_owner_id: old_owner_id.into(),
new_owner_id: new_owner_id.into(),
token_ids: token_ids.into_iter().map(|s| s.into()).collect(),
memo: memo.map(|t| t.into()),
authorized_id: authorized_id.map(|id| id.as_str()),
old_owner_id: old_owner_id.as_str(),
new_owner_id: new_owner_id.as_str(),
token_ids,
memo,
}
}
}
Expand All @@ -117,33 +89,27 @@ impl<'a> NftTransferData<'a> {
#[derive(Serialize, Deserialize, Debug)]
pub struct NftBurnData<'a> {
#[serde(borrow)]
pub owner_id: Cow<'a, str>,
pub owner_id: &'a str,
#[serde(borrow)]
pub token_ids: Vec<Cow<'a, str>>,
pub token_ids: Vec<&'a str>,
#[serde(borrow)]
pub authorized_id: Option<Cow<'a, str>>,
pub authorized_id: Option<&'a str>,
#[serde(borrow)]
pub memo: Option<Cow<'a, str>>,
pub memo: Option<&'a str>,
}

impl<'a> NftBurnData<'a> {
pub fn new<O, T, A, M>(
owner_id: O,
token_ids: Vec<T>,
authorized_id: Option<A>,
memo: Option<M>,
) -> NftBurnData<'a>
where
O: Into<Cow<'a, str>>,
T: Into<Cow<'a, str>>,
A: Into<Cow<'a, str>>,
M: Into<Cow<'a, str>>,
{
pub fn new(
owner_id: &'a AccountId,
token_ids: Vec<&'a str>,
authorized_id: Option<&'a AccountId>,
memo: Option<&'a str>,
) -> NftBurnData<'a> {
Self {
owner_id: owner_id.into(),
token_ids: token_ids.into_iter().map(|s| s.into()).collect(),
authorized_id: authorized_id.map(|t| t.into()),
memo: memo.map(|t| t.into()),
owner_id: owner_id.as_str(),
token_ids,
authorized_id: authorized_id.map(|id| id.as_str()),
memo,
}
}
}
Expand Down Expand Up @@ -180,25 +146,31 @@ impl<'a> NearEvent<'a> {
format!("EVENT_JSON:{}", self.to_json_string())
}

pub fn emit(&self) {
/// Logs the event to the host. This is required to ensure that the event is triggered.
pub fn emit(self) {
near_sdk::env::log_str(&self.to_json_event_string());
}
}

#[cfg(test)]
mod tests {
use super::*;

use near_sdk::AccountId;

const AUTHORIZED_ID_NONE: Option<String> = None;
const MEMO_NONE: Option<&String> = None;
fn bob() -> AccountId {
AccountId::new_unchecked("bob".to_string())
}

fn alice() -> AccountId {
AccountId::new_unchecked("alice".to_string())
}
// const MEMO_NONE: Option<String> = None;
willemneal marked this conversation as resolved.
Show resolved Hide resolved

#[test]
fn nft_mint() {
let owner_id = "bob";
let owner_id = &bob();
let token_ids = vec!["0", "1"];
let mint_log = NftMintData::new(owner_id, token_ids, MEMO_NONE);
let mint_log = NftMintData::new(owner_id, token_ids, None);
let event_log = NearEvent::nft_mint(vec![mint_log]);
assert_eq!(
serde_json::to_string(&event_log).unwrap(),
Expand All @@ -208,9 +180,9 @@ mod tests {

#[test]
fn nft_mints() {
let owner_id = "bob";
let owner_id = &bob();
let token_ids = vec!["0", "1"];
let mint_log = NftMintData::new(owner_id, token_ids, MEMO_NONE);
let mint_log = NftMintData::new(owner_id, token_ids, None);
let alice = AccountId::new_unchecked("alice".to_string());
let event_log = NearEvent::nft_mint(vec![
mint_log,
Expand All @@ -224,9 +196,9 @@ mod tests {

#[test]
fn nft_burn() {
let owner_id = "bob";
let owner_id = &bob();
let token_ids = vec!["0", "1"];
let burn_data = NftBurnData::new(owner_id, token_ids, AUTHORIZED_ID_NONE, MEMO_NONE);
let burn_data = NftBurnData::new(owner_id, token_ids, None, None);
let log = NearEvent::nft_burn(vec![burn_data]).to_json_string();
assert_eq!(
log,
Expand All @@ -236,30 +208,30 @@ mod tests {

#[test]
fn nft_burns() {
let owner_id = "bob";
let owner_id = &bob();
let token_ids = vec!["0", "1"];
let log = NearEvent::nft_burn(vec![
NftBurnData::new("alice", vec!["2", "3"], Some("4"), Some("has memo")),
NftBurnData::new(owner_id, token_ids, AUTHORIZED_ID_NONE, MEMO_NONE),
NftBurnData::new(&alice(), vec!["2", "3"], Some(&bob()), Some(&"has memo".to_string())),
NftBurnData::new(owner_id, token_ids, None, None),
])
.to_json_string();
assert_eq!(
log,
r#"{"standard":"nep171","version":"1.0.0","event":"nft_burn","data":[{"owner_id":"alice","token_ids":["2","3"],"authorized_id":"4","memo":"has memo"},{"owner_id":"bob","token_ids":["0","1"]}]}"#
r#"{"standard":"nep171","version":"1.0.0","event":"nft_burn","data":[{"owner_id":"alice","token_ids":["2","3"],"authorized_id":"bob","memo":"has memo"},{"owner_id":"bob","token_ids":["0","1"]}]}"#
);
}

#[test]
fn nft_transfer() {
willemneal marked this conversation as resolved.
Show resolved Hide resolved
let old_owner_id = "bob".to_string();
let new_owner_id = "alice";
let old_owner_id = &bob();
let new_owner_id = &alice();
let token_ids = vec!["0", "1"];
let log = NearEvent::nft_transfer(vec![NftTransferData::new(
&old_owner_id,
old_owner_id,
new_owner_id,
token_ids,
AUTHORIZED_ID_NONE,
MEMO_NONE,
None,
None,
)])
.to_json_string();
assert_eq!(
Expand All @@ -270,29 +242,23 @@ mod tests {

#[test]
fn nft_transfers() {
let old_owner_id = "bob";
let new_owner_id = "alice";
let old_owner_id = &bob();
let new_owner_id = &alice();
let token_ids = vec!["0", "1"];
let log = NearEvent::nft_transfer(vec![
NftTransferData::new(
new_owner_id,
old_owner_id,
vec!["2", "3"],
Some("4".to_string()),
Some(&bob()),
Some("has memo"),
),
NftTransferData::new(
old_owner_id,
new_owner_id,
token_ids,
AUTHORIZED_ID_NONE,
MEMO_NONE,
),
NftTransferData::new(old_owner_id, new_owner_id, token_ids, None, None),
])
.to_json_string();
assert_eq!(
log,
r#"{"standard":"nep171","version":"1.0.0","event":"nft_transfer","data":[{"old_owner_id":"alice","new_owner_id":"bob","token_ids":["2","3"],"authorized_id":"4","memo":"has memo"},{"old_owner_id":"bob","new_owner_id":"alice","token_ids":["0","1"]}]}"#
r#"{"standard":"nep171","version":"1.0.0","event":"nft_transfer","data":[{"old_owner_id":"alice","new_owner_id":"bob","token_ids":["2","3"],"authorized_id":"bob","memo":"has memo"},{"old_owner_id":"bob","new_owner_id":"alice","token_ids":["0","1"]}]}"#
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,13 @@ impl NonFungibleToken {

self.internal_transfer_unguarded(token_id, &owner_id, receiver_id);

NonFungibleToken::emit_transfer(&owner_id, receiver_id, token_id, sender_id, memo.as_ref());
NonFungibleToken::emit_transfer(
&owner_id,
receiver_id,
token_id,
sender_id,
memo,
);

// return previous owner & approvals
(owner_id, approved_account_ids)
Expand All @@ -287,14 +293,14 @@ impl NonFungibleToken {
receiver_id: &AccountId,
token_id: &str,
sender_id: Option<&AccountId>,
memo: Option<&String>,
memo: Option<String>,
) {
NearEvent::nft_transfer(vec![NftTransferData::new(
owner_id,
receiver_id,
vec![token_id],
sender_id.filter(|sender_id| *sender_id == owner_id),
memo,
memo.as_deref(),
)])
.emit();
}
Expand Down
7 changes: 0 additions & 7 deletions near-sdk/src/types/account_id.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use borsh::{maybestd::io, BorshDeserialize, BorshSchema, BorshSerialize};
use serde::{de, Deserialize, Serialize};
use std::borrow::Cow;
use std::convert::TryFrom;
use std::fmt;

Expand Down Expand Up @@ -69,12 +68,6 @@ impl From<AccountId> for String {
}
}

impl<'a> From<&'a AccountId> for Cow<'a, str> {
fn from(id: &'a AccountId) -> Self {
Cow::Borrowed(id.as_str())
}
}

impl AsRef<str> for AccountId {
fn as_ref(&self) -> &str {
self.0.as_str()
Expand Down