-
Notifications
You must be signed in to change notification settings - Fork 6
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
fix(unmerged): contract caller
as a chain extension origin
#301
base: main
Are you sure you want to change the base?
Conversation
# This is the 1st commit message: refactor: general # This is the commit message #2: init # This is the commit message #3: begin refactor # This is the commit message #4: refactor: error handling # This is the commit message #5: tests: add error handling tests # This is the commit message #6: WIP # This is the commit message #7: finalise error handling # This is the commit message #8: refactor: easier review
Co-authored-by: Frank Bell <frank@r0gue.io>
caller
as a chain extension origin
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 this is definitely something that we need to consider. Both serve a different use case. When we use the contract address to make the calls, the contract serves somewhat as a different entity (e.g. a DAO). When we use the caller's address, the contract serves somewhat as a wrapper (e.g. a wrapper to make something standard compliant). Not sure how we can provide this different use case for each api. Creating a different function id sounds a bit like an overkill to differ between calling with the contract's address or the caller to the contract.
Providing the account which to call the pallet from was another idea but then we would have to filter out these bytes before decoding into the dispatch call or runtime read so that is not ideal either.
@@ -168,14 +171,14 @@ pub trait Ext { | |||
type AccountId; | |||
|
|||
/// Returns a reference to the account id of the current contract. | |||
fn address(&self) -> &Self::AccountId; | |||
fn address(&self) -> Self::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.
Why are we changing this?
} | ||
|
||
impl Ext for () { | ||
type AccountId = (); | ||
|
||
fn address(&self) -> &Self::AccountId { | ||
&() | ||
fn address(&self) -> Self::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.
Again, why are we changing this?
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.
This was changed just to test if it works (will be reverted later when we decide the next step with the extension origin). Reason why I don't add the method caller()
because it will bring back stuffs that @evilrobot-01 removed before #265 that removed the Config
associated type. The caller()
method returns OriginFor<Config>
so if we want that method, we need to revert the changed Frank made.
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.
To be clear, I'm not against adding stuff that is needed, but a useful thing to consider is that taking a trait bound on the whole Config trait makes mocking overly complex. An example is needing to add a mock pallet-contracts impl just to do basic things.
Only expose what is required, nothing more. It may be easier, but has consequences downstream for implementors.
@@ -185,12 +188,16 @@ pub(crate) struct ExternalEnvironment<'a, T: pallet_contracts::chain_extension:: | |||
impl<'a, E: pallet_contracts::chain_extension::Ext> Ext for ExternalEnvironment<'a, E> { | |||
type AccountId = AccountIdOf<E::T>; | |||
|
|||
fn address(&self) -> &Self::AccountId { | |||
self.0.address() | |||
fn address(&self) -> Self::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.
Can we not add a function caller
instead? This function is to get the address and should not be changed tmo
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.
Explained here: #301 (comment)
caller
as a chain extension origincaller
as a chain extension origin
411ff66
to
8c72e2c
Compare
In the extension code, we have set the
contract
itself as the default origin for all calls. However, when it comes to methods likeapprove
andtransfer_from
, there's a potential problem. Typically, the approve function allows a user (likeALICE
) to approve another entity (like a contract) to spend their funds. But since we've set the contract as the origin for all calls, transfer_from would only be callable by the contract itself.Shouldn't it be
self.ext().caller()
instead so the caller of the extrinsic is the caller of the contract, not the contract by itself. Would love to have some view on this!Proposed changes
Payload sent to extension must have an extra flag to distinguish between which method should be called with
contract
as origin orcaller
as origin. Maybe the last byte of the encoding scheme?Guideline for the reviewers
transfer_from_works()
inpop-api/examples/fungibles
howALICE
can be the dispatch origin fortransfer_from
instead of a hard-coded contract address. We did not cover this in the contract integration test because it we use the dispatch functions frompallet-assets
directly to handle these logics. But as a smart contract developer, everything must be possibly handled on the contract side.