Skip to content

Conversation

@devbugging
Copy link
Contributor

Issue: #348

Copy link
Member

@joshuahannan joshuahannan left a comment

Choose a reason for hiding this comment

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

Well thought out and well written. I just have a few questions and comments


```go
type EVMScheduler interface {

Copy link
Member

Choose a reason for hiding this comment

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

Is getStatus included here or is that just querying the status on the EVM side?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can just use the EVM side to query. This way we could even introduce failed status.

Scheduling involves coordination between all the above components and should be initiated from EOA using the EVM Scheduler proxy Solidity contract.

1. **EOA**
1. Submits an EVM transaction that calls the EVM Scheduler Proxy with all required arguments as well as the amount needed to pay for execution.
Copy link
Member

Choose a reason for hiding this comment

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

Maybe add a first step here for EOA having to deploy a contract that contains the logic to execute for the scheduled transaction

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea

1. Submits an EVM transaction that calls the EVM Scheduler Proxy with all required arguments as well as the amount needed to pay for execution.
2. **Solidity EVM Scheduler Proxy**
1. The EVM Scheduler Proxy receives a scheduling request in Solidity with all arguments (handler address, data, priority, timestamp, gas limit). It stores the arguments internally and it also records the sender as the owner of the transaction data.
2. If provided funds are sufficient, it forwards them to the EVM Scheduler COA.
Copy link
Member

Choose a reason for hiding this comment

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

How are we determining if the provided funds are sufficient? Is there a standard transaction fee calculation for a provided execution effort for Solidity? Are we taking the data storage into account?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It checks in the contract, so not standard fee, but additional check for the msg.value

1. The schedule function is invoked, which internally stores the gas limit.
2. It withdraws the funds from EVM to a vault it will use to schedule with Flow transaction scheduler.
3. If the funds were successfully withdrawn, it calls the Transaction Scheduler schedule function and provides the funds, priority, effort limit (converted from gas limit), and timestamp. For the handler, it provides its own function. It doesn’t forward any other EVM data.
4. It records the scheduled transaction ID it received from the Transaction scheduler contract.
Copy link
Member

Choose a reason for hiding this comment

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

What does it do with the ScheduledTransaction resource? does it store it or destroy it? It might be good to keep it until the transaction is executed. The storage would need to be paid for though

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hm, I was thinking to destroy it since there's no use of keeping it, unless you think of something.


# Alternatives

There were multiple alternatives considered, some of which proved to be unviable, most elegant ones due to limitation of Cadence Arch authorization mechanism. The Cadence Arch is a precompile that must follow the [EVM precompile interface](https://github.com/ethereum/go-ethereum/blob/1487a8577d1566497e161a04f8cee3204d4b3d36/core/vm/contracts.go#L52) and unless we fork the EVM we can not introduce more arguments to it. Forking of EVM is not acceptable at this point.
Copy link
Collaborator

Choose a reason for hiding this comment

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

ah this makes sense. I think it may be possible without forking EVM (as Call will precede with transfer from msg.sender only issue maybe delegatecall there but I don't think it is too important in this context )

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What do you mean? can you elaborate

Copy link
Collaborator

Choose a reason for hiding this comment

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

Now every call in EVM side is basically Transfer + Call ( Transfer is the part sending the msg.value ) [0]

This Transfer part is something we can hook into [1] with TransferFunc func(StateDB, common.Address, common.Address, *uint256.Int)

Here we can find out caller ( which is msg.sender )

Now imagine let's say we saved the last caller in somewhere ( blockview, config etc ) then when we are injecting precompiles [2] we can inject that into precompile function. [3]

something like:

return MultiFunctionPrecompiledContract(
   	address,
   	[]Function{
   		&flowBlockHeight{heightProvider},
   		&proofVerifier{proofVer},
   		&randomnessSource{randomSourceProvider},
   		&revertibleRandom{revertibleRandomGenerator},
                       &scheduleThingy{lastSenderGetter}.
   	},
   )

So technically precompiles can access the caller without forking EVM probably.

[0] https://github.com/ethereum/go-ethereum/blob/5e6f7374deaae104e243703f5aea25c68f4d6df6/core/vm/evm.go#L283-L286

[1] https://github.com/onflow/flow-go/blob/3cb966eabeafcfc6b90e40cfe2bcb9e5c773dada/fvm/evm/emulator/config.go#L141-L146

[2] https://github.com/onflow/flow-go/blob/af145645dab91fa9424cc16e8b3d773a561150e8/fvm/evm/emulator/emulator.go#L345-L349

[3] https://github.com/onflow/flow-go/blob/af145645dab91fa9424cc16e8b3d773a561150e8/fvm/evm/emulator/emulator.go#L345-L349

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 see what you mean. This approach can work. The only concern I have with it is I guess we would have to copy and modify the TransferFunc which is very critical function that should always be kept exactly in sync with the latest implementation.
I have to think a bit about this approach, but it could potentially unlock more context on the cadence arch. But I need to think about security of it more, since it's quite hacky.

Copy link
Collaborator

@bluesign bluesign Nov 24, 2025

Choose a reason for hiding this comment

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

you can still call from gethCall, basically it will be something like this:

func(sb StateDB, caller common.Address, to common.Address, amount *uint256.Int){
    // save caller 
    xxx.lastCaller = caller
    // call original impl.
    gethCore.Transfer(sb, caller, to, amount)
}

yeah it is kinda hacky, I was using this to hook into transfers when I was developing tinyAN, just recalled from there.

@bluesign
Copy link
Collaborator

very nice work, I think this will be very useful.

devbugging and others added 2 commits November 18, 2025 16:40
Co-authored-by: Joshua Hannan <joshua.hannan@flowfoundation.org>
Co-authored-by: Dieter Shirley <dete@dapperlabs.com>
@devbugging
Copy link
Contributor Author

very nice work, I think this will be very useful.

Thank you, great feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants