-
Notifications
You must be signed in to change notification settings - Fork 19
feat: imt_mint/imt_burn intents
#193
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
Changes from 38 commits
142726e
475e5a7
00dc35b
8f56a19
47cb36c
552904f
b940a6f
60524fd
1e277ae
3347af8
fdf1353
6ba4a39
e5c3096
3e6ca0b
a82384f
99e38ee
8146632
974ab4f
6602cee
2aa54eb
174654e
416ad99
18f4268
b2b2e13
1f0037c
a1e0f37
e5f8b68
38344f4
a352ecd
709aeac
deeace5
6cac2b2
d75da27
37a1953
bf617a7
ea03a31
60c1c4b
6a3b585
9cc44df
f54ae8b
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 |
|---|---|---|
|
|
@@ -17,6 +17,15 @@ use crate::{ | |
|
|
||
| use super::{ExecutableIntent, IntentEvent}; | ||
|
|
||
| pub const MAX_TOKEN_ID_LEN: usize = 127; | ||
|
|
||
| #[derive(thiserror::Error, Debug)] | ||
| #[error("token_id is too long: max length is {MAX_TOKEN_ID_LEN}, got {0}")] | ||
| pub struct TokenIdTooLarge(pub usize); | ||
mitinarseny marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| const MT_ON_TRANSFER_GAS_MIN: Gas = Gas::from_tgas(5); | ||
| const MT_ON_TRANSFER_GAS_DEFAULT: Gas = Gas::from_tgas(30); | ||
|
|
||
| #[must_use] | ||
| #[near(serializers = [borsh, json])] | ||
| #[derive(Debug, Clone)] | ||
|
|
@@ -73,17 +82,12 @@ pub struct Transfer { | |
| /// Optionally notify receiver_id via `mt_on_transfer()` | ||
| /// | ||
| /// NOTE: `min_gas` is adjusted with following values: | ||
| /// * default: 30TGas | ||
| /// * minimum: 5TGas | ||
| /// * default: 30TGas | ||
| #[serde(flatten, default, skip_serializing_if = "Option::is_none")] | ||
| pub notification: Option<NotifyOnTransfer>, | ||
| } | ||
|
|
||
| impl Transfer { | ||
| pub const MT_ON_TRANSFER_GAS_MIN: Gas = Gas::from_tgas(5); | ||
| pub const MT_ON_TRANSFER_GAS_DEFAULT: Gas = Gas::from_tgas(30); | ||
| } | ||
|
|
||
| impl ExecutableIntent for Transfer { | ||
| fn execute_intent<S, I>( | ||
| self, | ||
|
|
@@ -127,9 +131,10 @@ impl ExecutableIntent for Transfer { | |
| notification.min_gas = Some( | ||
| notification | ||
| .min_gas | ||
| .unwrap_or(Self::MT_ON_TRANSFER_GAS_DEFAULT) | ||
| .max(Self::MT_ON_TRANSFER_GAS_MIN), | ||
| .unwrap_or(MT_ON_TRANSFER_GAS_DEFAULT) | ||
| .max(MT_ON_TRANSFER_GAS_MIN), | ||
| ); | ||
|
|
||
| engine | ||
| .state | ||
| .notify_on_transfer(sender_id, self.receiver_id, self.tokens, notification); | ||
|
|
@@ -516,3 +521,171 @@ impl ExecutableIntent for StorageDeposit { | |
| engine.state.storage_deposit(owner_id, self) | ||
| } | ||
| } | ||
|
|
||
| #[cfg(feature = "imt")] | ||
| pub mod imt { | ||
|
Collaborator
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. consider moving to separate module? |
||
| use super::{MT_ON_TRANSFER_GAS_DEFAULT, MT_ON_TRANSFER_GAS_MIN}; | ||
| use crate::{Result, intents::tokens::MAX_TOKEN_ID_LEN}; | ||
| use defuse_token_id::TokenId; | ||
| use near_sdk::{AccountId, AccountIdRef, CryptoHash, near}; | ||
| use serde_with::{DisplayFromStr, serde_as}; | ||
|
|
||
| use std::{borrow::Cow, collections::BTreeMap}; | ||
|
|
||
| use crate::{ | ||
| DefuseError, | ||
| accounts::AccountEvent, | ||
| amounts::Amounts, | ||
| engine::{Engine, Inspector, State}, | ||
| events::DefuseEvent, | ||
| intents::{ExecutableIntent, IntentEvent, tokens::NotifyOnTransfer}, | ||
| }; | ||
|
|
||
| pub type ImtTokens = Amounts<BTreeMap<defuse_nep245::TokenId, u128>>; | ||
|
|
||
| impl ImtTokens { | ||
| #[inline] | ||
| fn into_generic_tokens( | ||
| self, | ||
| minter_id: &AccountIdRef, | ||
| ) -> Result<Amounts<BTreeMap<TokenId, u128>>> { | ||
| let tokens = self | ||
| .into_iter() | ||
| .map(|(token_id, amount)| { | ||
| if token_id.len() > MAX_TOKEN_ID_LEN { | ||
| return Err(DefuseError::TokenIdTooLarge(token_id.len())); | ||
| } | ||
|
|
||
| let token = defuse_token_id::imt::ImtTokenId::new(minter_id, token_id).into(); | ||
|
|
||
| Ok((token, amount)) | ||
| }) | ||
|
Comment on lines
+551
to
+558
Collaborator
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. maybe move into
Collaborator
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. token_id length limits are verifier-specific, since TokenId is reused in other crates, i.e. escrow-swap |
||
| .collect::<Result<_, _>>()?; | ||
|
|
||
| Ok(Amounts::new(tokens)) | ||
| } | ||
| } | ||
|
|
||
| #[near(serializers = [borsh, json])] | ||
| #[derive(Debug, Clone)] | ||
| /// Mint a set of tokens from the signer to a specified account id, within the intents contract. | ||
| pub struct ImtMint { | ||
| pub receiver_id: AccountId, | ||
mitinarseny marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // The tokens transferred in this call will be wrapped | ||
| // in such a way as to bind the token ID to the minter authority. | ||
| // The final string representation of the token | ||
| // will be as follows: `imt:<minter_id>:<token_id>` | ||
mitinarseny marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| #[serde_as(as = "Amounts<BTreeMap<_, DisplayFromStr>>")] | ||
| pub tokens: ImtTokens, | ||
|
|
||
| #[serde(default, skip_serializing_if = "Option::is_none")] | ||
| pub memo: Option<String>, | ||
|
|
||
| /// Optionally notify receiver_id via `mt_on_transfer()` | ||
| /// | ||
| /// NOTE: `min_gas` is adjusted with following values: | ||
| /// * minimum: 5TGas | ||
| /// * default: 30TGas | ||
| #[serde(flatten, default, skip_serializing_if = "Option::is_none")] | ||
| pub notification: Option<NotifyOnTransfer>, | ||
| } | ||
|
|
||
| impl ExecutableIntent for ImtMint { | ||
| #[inline] | ||
| fn execute_intent<S, I>( | ||
| self, | ||
| signer_id: &AccountIdRef, | ||
| engine: &mut Engine<S, I>, | ||
| intent_hash: CryptoHash, | ||
| ) -> Result<()> | ||
| where | ||
| S: State, | ||
| I: Inspector, | ||
| { | ||
| if self.tokens.is_empty() { | ||
| return Err(DefuseError::InvalidIntent); | ||
| } | ||
|
|
||
| engine | ||
| .inspector | ||
| .on_event(DefuseEvent::ImtMint(Cow::Borrowed( | ||
| [IntentEvent::new( | ||
| AccountEvent::new(signer_id, Cow::Borrowed(&self)), | ||
| intent_hash, | ||
| )] | ||
| .as_slice(), | ||
| ))); | ||
|
|
||
| let tokens = self.tokens.into_generic_tokens(signer_id)?; | ||
hlgltvnnk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| engine | ||
| .state | ||
| .mint(self.receiver_id.clone(), tokens.clone(), self.memo)?; | ||
|
|
||
| if let Some(mut notification) = self.notification { | ||
| notification.min_gas = Some( | ||
| notification | ||
| .min_gas | ||
| .unwrap_or(MT_ON_TRANSFER_GAS_DEFAULT) | ||
| .max(MT_ON_TRANSFER_GAS_MIN), | ||
| ); | ||
|
|
||
| engine | ||
| .state | ||
| .notify_on_transfer(signer_id, self.receiver_id, tokens, notification); | ||
| } | ||
|
|
||
| Ok(()) | ||
| } | ||
| } | ||
|
|
||
| #[near(serializers = [borsh, json])] | ||
| #[derive(Debug, Clone)] | ||
| /// Burn a set of imt tokens, within the intents contract. | ||
| pub struct ImtBurn { | ||
| // The minter authority of the imt tokens | ||
| pub minter_id: AccountId, | ||
|
|
||
| // The tokens transferred in this call will be wrapped | ||
mitinarseny marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| // in such a way as to bind the token ID to the minter authority. | ||
| // The final string representation of the token | ||
| // will be as follows: `imt:<minter_id>:<token_id>` | ||
| #[serde_as(as = "Amounts<BTreeMap<_, DisplayFromStr>>")] | ||
| pub tokens: ImtTokens, | ||
|
|
||
| #[serde(default, skip_serializing_if = "Option::is_none")] | ||
| pub memo: Option<String>, | ||
| } | ||
|
|
||
| impl ExecutableIntent for ImtBurn { | ||
| #[inline] | ||
| fn execute_intent<S, I>( | ||
| self, | ||
| signer_id: &AccountIdRef, | ||
| engine: &mut Engine<S, I>, | ||
| intent_hash: CryptoHash, | ||
| ) -> Result<()> | ||
| where | ||
| S: State, | ||
| I: Inspector, | ||
| { | ||
| if self.tokens.is_empty() { | ||
| return Err(DefuseError::InvalidIntent); | ||
| } | ||
|
|
||
| engine | ||
| .inspector | ||
| .on_event(DefuseEvent::ImtBurn(Cow::Borrowed( | ||
| [IntentEvent::new( | ||
| AccountEvent::new(signer_id, Cow::Borrowed(&self)), | ||
| intent_hash, | ||
| )] | ||
| .as_slice(), | ||
| ))); | ||
|
|
||
| let tokens = self.tokens.into_generic_tokens(&self.minter_id)?; | ||
|
|
||
| engine.state.burn(signer_id, tokens, self.memo) | ||
| } | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.