-
Notifications
You must be signed in to change notification settings - Fork 432
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
Improve E2E interface #1644
Comments
There should be some way to invoke this as an associated function e.g. May not be entirely straightforward since |
Not a fun of using different method names since it harms readability of the tests. |
Proposed designWe keep the contract instantiation. Let's take let constructor = MappingsRef::new();
let contract_id = client
.instantiate(
"mapping-integration-tests",
&ink_e2e::charlie(),
constructor,
0,
None,
)
.await
.expect("instantiate failed")
.account_id; Instead of manually building a message: let first_insert = ink_e2e::build_message::<MappingsRef>(contract_id)
.call(|contract| contract.insert_balance(1_000)); and making an explicit call: let _ = client
.call(&ink_e2e::charlie(), first_insert, 0, None)
.await
.expect("Calling `insert_balance` failed")
.return_value(); Let's unify the process under the automatically-generated associated message function. i.e we have So we can do something like: // get the contracts' constructor
let constructor = MappingsRef::new();
// generate the associated trait for E2E
let contract = client
.instantiate(
"mapping-integration-tests",
&ink_e2e::charlie(),
constructor,
0,
None,
)
.await
.expect("instantiate failed")
.account_id;
// we call a contract, await the result, unwrap `LangError` and return the value automatically
let _ = contract.insert_balance(1_000)
.call()
.await
.expect("Calling `insert_balance` failed")
.return_value() In case the message is payable and/or we want to set a deposit limit, we can use Builder pattern and chain arguments into the final call: let _ = contract.insert_balance(1_000)
.with_storage_deposit(20_123)
.with_value(30_000)
// specify the account to sign a transaction
.call(&ink_e2e::alice())
.await
.expect("Calling `insert_balance` failed")
.return_value() @ascjones @cmichi and @HCastano Looking forward your thoughts :) |
Yes this looks like a nice API, please go ahead and produce a prototype. Keep in mind that we should attempt to minimize changes to the core codegen to facilitate this API. Where there are changes they should be non breaking to the main purpose of the |
UpdateI managed to come up with the design that simplifies the instantiation of the contract: let contract_acc_id = FlipperCaller::new(true)
.instantiate("flipper", &ink_e2e::dave(), &mut client)
.await
.expect("instantiation failed")
.account_id; However, it is problematic to instantiate a contract and create an instance of After a little investigation I came up with two ways how to move forward: EasyWe have two structures:
The downside of this approach is that it is not very idiomatic as we now have two different structures for calls. ProperIdeally, we need to generate associated structure in The reason why it is not possible to do in However, this approach is more difficult to implement, will require significantly more time than the first one and introduces breaking changes to the API whereas the first one can be used alongside the existing API. @cmichi @ascjones please let me know how I should move forward. |
Out of the two I would go for the simpler approach, I don't think it is necessary to introduce a whole new layer of codegen. After all this idea of utilizing the existing My feeling is there should be a way to coerce existing codegen and types to achieve a decent API, let me have a look and see whether I can see a possible third solution. |
I've made an experimental PR #1669 (for illustration and discussion purposes only) which is another approach to improve this call builder API. TLDR: .call(|flipper| flipper.get()); becomes flipper.call().get(); Let me know what you think. |
After a quick call with Michi, he also pointed out that With regard to #1669 this can be an intermediate improvement, but not what I had in mind. I don't see the reason why having a codegen in e2e would be bad. It doesn't bloat the WASM binary but provides better DevEx experience.
It won't be duplicated, it will be extended. Even if you look at my changes you'll see that the codegen for constructor simply wraps the method into |
It's simply a solution to the problem of removing callback for generating calls, which was the original scope of the idea when I flagged it up.
Nothing inherently wrong, in some places it would most likely duplicate some boilerplate of generating and binding methods (although not complicated). My thinking was simply to minimize time and effort spent on it and extra code to write and maintain.
Yes this would be great, although the way I was thinking about it was to mimic the cross-contract calling API since we have access already to all those builders and types.
I've not seen the code yet, do you have a link? Anyway happy for you to proceed as you see fit, I am only providing some ideas, they are not necessarily correct. |
I haven't looked in detail at the code, but much of the codegen does look similar to that of the existing contract ref. Again not so bad per se if there is a big benefit. It would be good if one of the examples (flipper?) was updated to use the new API so we could see the finished product (even if it doesn't compile). From my side I prefer small incremental API improvements, which was what I had in mind with the original suggestion to remove the callback from the call builder syntax. This looks so far like a much larger scoped undertaking, which may produce nice results, but we need to sure that it will be worth the effort. |
@SkymanOne I agree with Andrew, this approach would become too involved right now. As we have bigger construction sites currently, let's go for a simpler approach. What are your thoughts on Andrew's #1669 ? |
I actually did not remove a significant portion of code, as I am still working on the calling behaviour. The only method that is actually used right now is
There is an example test with instantiation:
While I agree with this approach, the problem is that each incremental step with break an API which we can not afford as we rolling into main nets and our developer base grows. Ideally, I would like to get this breaking change into the next major release and do not change API after. Therefore, it sounds more reasonable to me to do introduce the major change I originally proposed and stick with it. Introducing module wide attribute and codegen for e2e tests will have more benefits in future. It will be easier to introduce new changes without breaking API. |
Yes, although that does still use
E2E tests are brand new and we can afford to iterate and break stuff to get to a nice API. IMO we are not that far away from having a nice API already.
What benefits do you imagine? We only need to be able to construct contract messages in an ergonomic fashion, which we are already a long way towards. Also doing a lot of work up front for possible future benefits usually not worth it.
As you say this requires significantly more time from you in the short term, but there are also other costs to consider:
So, having considered those additional costs, only if we still think it is worth it for the benefits we should go ahead. |
As mentioned earlier with top level attribute macro we can pass
I'll have a PoC for the messages too. For now, it'll be a separate associated structure as described earlier. In this PoC it will still use
|
A thing that would be nice is to simplify the e2e API that at the moment requires sth like this:
The text was updated successfully, but these errors were encountered: