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(world): add CallBatchSystem to core module #1500

Merged
merged 25 commits into from
Sep 21, 2023
Merged

feat(world): add CallBatchSystem to core module #1500

merged 25 commits into from
Sep 21, 2023

Conversation

yonadaa
Copy link
Contributor

@yonadaa yonadaa commented Sep 15, 2023

closes #1160
closes #1470

The BatchCallSystem enables calling many systems in a World in a single transaction.

It leverages callFrom to execute each system call with the user as msgSender(). Users delegate to the BatchCallSystem system, then call batchCall with the resource selectors and calldata for the systems they wish to call.

@changeset-bot
Copy link

changeset-bot bot commented Sep 15, 2023

🦋 Changeset detected

Latest commit: 4ce27d2

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 29 packages
Name Type
@latticexyz/world Major
@latticexyz/cli Major
@latticexyz/dev-tools Major
@latticexyz/store-sync Major
@latticexyz/store-indexer Major
@latticexyz/abi-ts Major
@latticexyz/block-logs-stream Major
@latticexyz/common Major
@latticexyz/config Major
create-mud Major
@latticexyz/ecs-browser Major
@latticexyz/faucet Major
@latticexyz/gas-report Major
@latticexyz/network Major
@latticexyz/noise Major
@latticexyz/phaserx Major
@latticexyz/protocol-parser Major
@latticexyz/react Major
@latticexyz/recs Major
@latticexyz/schema-type Major
@latticexyz/services Major
@latticexyz/solecs Major
solhint-config-mud Major
solhint-plugin-mud Major
@latticexyz/std-client Major
@latticexyz/std-contracts Major
@latticexyz/store-cache Major
@latticexyz/store Major
@latticexyz/utils Major

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

IBaseWorld world = IBaseWorld(_world());

for (uint256 i; i < resourceSelectors.length; i++) {
world.callFrom(_msgSender(), resourceSelectors[i], funcSelectorAndArgss[i]);
Copy link
Member

Choose a reason for hiding this comment

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

should we make this a root system and then do this so the users don't need to create a delegation?

(bool success, bytes memory returnData) = address(world).delegatecall(abi.encodeCall(
  world.call,
  (
    _msgSender(),
    resourceSelector[i],
    funcSelectorAndArgs[i]
  )
));
if(!success) revertWithBytes(returnData);

We can actually do both, have a BatchCallSystem and a BatchCallRootSystem, and install then in installModule and installRootModule respectively

Copy link
Member

@holic holic Sep 15, 2023

Choose a reason for hiding this comment

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

do we need this to be a module? if we intend to use this for deploys anyway, would it make sense to just have this under core/implementations as a plain root system? I also can't imagine you'd wanna install this more than once anyway?

Copy link
Member

Choose a reason for hiding this comment

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

yeah it can also be part of the core module

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I like this approach without the delegation. I'm going to add the root version to the core module.

I'll also remove the non-root version, because the root version will be installed on worlds by default.

import { IBaseWorld } from "../../interfaces/IBaseWorld.sol";

contract BatchCallSystem is System {
function batchCall(bytes32[] memory resourceSelectors, bytes[] memory funcSelectorAndArgss) public {
Copy link
Member

@holic holic Sep 17, 2023

Choose a reason for hiding this comment

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

would it be worse on calldata length if this was something like

struct SystemCall {
  bytes32 systemId;
  bytes callData;
}

function batchCall(SystemCall[] memory systemCalls) public {

(this feels cleaner but I would prob optimize for calldata/gas here, since these calls will be abstracted away anyway)

@yonadaa yonadaa changed the title feat(world): add BatchCallModule feat(world): add CallBatchSystem to core module Sep 18, 2023
@yonadaa yonadaa marked this pull request as ready for review September 18, 2023 21:31
@yonadaa yonadaa requested a review from dk1a as a code owner September 18, 2023 21:31
*/
function callBatch(
bytes32[] memory resourceSelectors,
bytes[] memory callDatas
Copy link
Member

@holic holic Sep 19, 2023

Choose a reason for hiding this comment

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

same question as before: would it be better or worse gas-wise to have an array of SystemCall structs instead of two arrays? I assume a little worse on calldata but we wouldn't have to do a length check on the arrays to make sure they match (though I guess the whole thing would revert with an out of bounds error in that case?)

Copy link
Member

Choose a reason for hiding this comment

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

my feeling is it would be a little better calldata wise because the structs are encoded like tuples, so there shouldn't be overhead on the encoding of the individual elements, but we might save one word because we only have one array (and therefore only one length encoding)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Going to implement the struct for the calldata improvements and to improve usability (only dealing with a single array)


assertEq(abi.decode(datas[0], (address)), caller, "wrong address returned");
assertEq(abi.decode(datas[1], (address)), address(world), "wrong store returned");
}
Copy link
Member

Choose a reason for hiding this comment

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

should we add some tests to make sure this goes through access control checks properly and fails on system calls we don't have access to?

Co-authored-by: alvarius <[email protected]>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wasn't sure where to put this file specifically for the struct.

Copy link
Member

@holic holic Sep 19, 2023

Choose a reason for hiding this comment

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

Realizing now we have two things named SystemCall

@alvrs should we keep this one named that way and the other lib named as SystemCaller or SystemCallLib?

Copy link
Member

Choose a reason for hiding this comment

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

i'd propose calling this one SystemCallData (aligned with how the tablegen structs are called, and also alllgins with "callData")

@alvrs alvrs merged commit 95c59b2 into main Sep 21, 2023
@alvrs alvrs deleted the batch-call branch September 21, 2023 19:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
Development

Successfully merging this pull request may close these issues.

Support batch-calling world methods
3 participants