-
Notifications
You must be signed in to change notification settings - Fork 12
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
Make all logic implemented inside PSP22Data struct #2
Conversation
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.
Delegation seems to be clean solution (although might be a bit onerous for rich traits), so approving with a few minor comments.
There is one hidden thing that we'd better be careful about (especially when adapting this approach for other PSPs). The default implementation on the PSP22Data
type should meet the assumption that no storage change is done if the method returns Err(_)
. A situation when this might lead to a big problem is such a custom delegation:
fn method(args) {
if let Err(Event::MinorProblem) = self.data.method(args) {
self.do_some_custom_fallback()
}
}
In other words, we can't assume that a failed 'template' method will lead to a failed call. As far as I can see, your code meets this requirement, so we are ok.
@@ -24,6 +25,23 @@ mod token { | |||
data: PSP22Data::new(supply, Self::env().caller()), | |||
} | |||
} | |||
|
|||
fn emit_events(&self, events: ink::prelude::vec::Vec<PSP22Event>) { |
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.
sad thing is that this method has to be copied for every psp22 contract... maybe we could extract it from here and expose somehow for reuse?
if you go with the other suggestion, you might end up with a very generic, convenient method for emitting arbitrary event iterable from a contract, which sounds nice :)
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.
Unfortunately it's probably not possible to extract a method which uses self.env()
outside the contract. Or maybe technically it's possible, but results in some templating nightmare.
This method is here temporary, after ink! allows defining events outside contracts, it will be gone and all self.emit_events(events)
will be replaces with for e in events { self.env().emit_event(e); }
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.
I think you could extract (or rather implement in the PSP22Data
) something along these lines:
fn emit_event<C, E, EE>(emitter: EE, event: E)
where C: ContractEventBase,
E: Into<<C as ContractEventBase>::Type>,
EE: EventEmitter<C> {
emitter.emit_event(event);
}
Yes. In the previous PR I've suggested creating a "contract (not Smart Contract) level integration tests" - #1 (review) . It would cover the cases you're describing here. |
data.rs
Outdated
pub enum PSP22Event { | ||
Transfer(Option<AccountId>, Option<AccountId>, u128), | ||
Approval(AccountId, AccountId, u128), | ||
} | ||
|
||
#[ink::storage_item] | ||
#[derive(Debug, Default)] | ||
pub struct PSP22Data { | ||
total_supply: u128, | ||
balances: Mapping<AccountId, u128>, | ||
allowances: Mapping<(AccountId, AccountId), u128>, | ||
} |
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.
It'd be good to have comments (///
) on pub elements. Especially this is supposed to be an exemplary usage.
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.
I'll add nice comments and extensive readme in a different PR
@@ -0,0 +1,170 @@ | |||
use crate::PSP22Error; |
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.
We might think about adding a mod-level documentation here, explaining why this representation and how it's supposed to be integrated into other contracts.
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 be added soon (tm) ;)
|
||
pub fn transfer( | ||
&mut self, | ||
caller: AccountId, |
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.
caller: AccountId, | |
from: AccountId, |
At this level of abstraction there's no notion of "caller" anymore and in this particular method, from == caller
.
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.
These internal methods of PSP22Data
differ in signature form the ones defined by the PSP22
trait. My idea was to make it explicit, that this argument is meant to always be self.env().caller()
when someone is implementing the trait.
Moves all PSP22 token logic from the example implementation (
lib.rs
) inside thePSP22Data
struct. Thanks to that the logic is now fixed and a contract developer can use our implementation by simply implementingPSP22
trait in a "pass-through" way:This approach:
a) greatly reduces the amount of boilerplate code that needs to be copied into user's contract
b) avoids using complicated macro on top of
ink
macroc) (not sure if this is good, but OpenBrush had it) gives the user control over emitting events