Skip to content

Replace COA.call with COA.callWithSigAndArgs for reduced computation cost#207

Merged
joshuahannan merged 4 commits into
mainfrom
mpeter/optimize-evm-call-usage
Apr 9, 2026
Merged

Replace COA.call with COA.callWithSigAndArgs for reduced computation cost#207
joshuahannan merged 4 commits into
mainfrom
mpeter/optimize-evm-call-usage

Conversation

@m-Peter

@m-Peter m-Peter commented Mar 18, 2026

Copy link
Copy Markdown
Collaborator

Related: onflow/flow-go#8401
Closes: #206

Description

Previously, to perform contract calls using a COA, it was necessary to produce the callData on Cadence side:

let calldata = EVM.encodeABIWithSignature(signature, args)

To be able to ABI-decode the returned data from the COA call, another step was needed:

let decoded = EVM.decodeABI(types: [Type<[UInt256]>()], data: res.data)
let amountsOut = decoded[0] as! [UInt256]

After doing some profiling on Cadence transactions, the computation cost of ABI encoding/decoding, amounted to about 15%-20% of the total computation, which is not trivial.

To optimize this case, we have a new wrapper function, callWithSigAndArgs, which does all this more efficiently:

coa.callWithSigAndArgs(
    to: to,
    signature: "approve(address,uint256)",
    args: [self.routerAddress, evmAmountIn],
    gasLimit: gasLimit,
    value: valueBalance.attoflow,
    resultTypes: [Type<[UInt256]>()]
)

We do the same for dryCall, by replacing it with dryCallWithSigAndArgs .

NOTE: CI is failing because we still need to deploy the EVM system contract on testnet/mainnet, for these new functions to be available.


For contributor use:

  • Targeted PR against main branch
  • Linked to Github issue with discussion and accepted design OR link to spec that describes this work.
  • Code follows the standards mentioned here.
  • Updated relevant documentation
  • Re-reviewed Files changed in the Github PR explorer
  • Added appropriate labels

@m-Peter m-Peter self-assigned this Mar 18, 2026
@github-project-automation github-project-automation Bot moved this to 👀 In Review in 🌊 Flow 4D Mar 18, 2026
@m-Peter m-Peter force-pushed the mpeter/optimize-evm-call-usage branch 2 times, most recently from 0480739 to c058e91 Compare March 19, 2026 07:57
@m-Peter m-Peter force-pushed the mpeter/optimize-evm-call-usage branch 4 times, most recently from 0b195c1 to 6dc3a89 Compare March 19, 2026 14:14
@m-Peter m-Peter marked this pull request as ready for review March 19, 2026 17:04
@m-Peter m-Peter requested a review from a team as a code owner March 19, 2026 17:04
@m-Peter m-Peter force-pushed the mpeter/optimize-evm-call-usage branch from 6dc3a89 to ae1fbc4 Compare March 23, 2026 10:05
@turbolent

Copy link
Copy Markdown
Member

Nice! Thank you for optimizing this 👍

It looks like some of the function signatures changed. We should probably make sure this doesn't break existing contracts and transactions. From what I could see doing a quick search for protectedTransferCall: (xsv search 'protectedTransferCall:' contracts.csv | xsv select location), only the EVM bridge contracts themselves need to be adjusted and no other contracts use it. Not sure about transactions

@m-Peter

m-Peter commented Mar 24, 2026

Copy link
Copy Markdown
Collaborator Author

Nice! Thank you for optimizing this 👍

It looks like some of the function signatures changed. We should probably make sure this doesn't break existing contracts and transactions. From what I could see doing a quick search for protectedTransferCall: (xsv search 'protectedTransferCall:' contracts.csv | xsv select location), only the EVM bridge contracts themselves need to be adjusted and no other contracts use it. Not sure about transactions

@turbolent I have already checked that the function signature changes affect only "internal" functions. Their access modifiers are either access(self) or access(account). I have also updated all the call-sites of these functions, so I believe we're not introducing any breaking changes to existing contracts / transactions.

@joshuahannan joshuahannan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good!

let decodedSymbolData = EVM.decodeABI(types: [Type<String>()], data: symbolRes.data)
assert(decodedNameData.length == 1, message: "Unexpected name() return length of ".concat(decodedNameData.length.toString()))
assert(decodedSymbolData.length == 1, message: "Unexpected symbol() return length of ".concat(decodedSymbolData.length.toString()))
assert(nameRes.results.length == 1, message: "Unexpected name() return length of ".concat(nameRes.results.length.toString()))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you use string interpolation everywhere instead of concat?

Suggested change
assert(nameRes.results.length == 1, message: "Unexpected name() return length of ".concat(nameRes.results.length.toString()))
assert(nameRes.results.length == 1, message: "Unexpected name() return length of \(nameRes.results.length.toString()))

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Updated all such occurrences in f931ba9 .

@fxamacker fxamacker left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Nice! 👍 Thanks @m-Peter for updating this repo to use optimized EVM functions.

My only suggestion is for code that wasn't modified by this PR.

Maybe we can special case value 0.0 to avoid creating EVM.Balance in several places.

Comment on lines 1315 to 1316
let valueBalance = EVM.Balance(attoflow: 0)
valueBalance.setFLOW(flow: value)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

These lines are not modified by PR.

Maybe we can special case value 0.0 to avoid creating EVM.Balance.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Updated in e9246a2 .

Comment on lines 1339 to 1340
let valueBalance = EVM.Balance(attoflow: 0)
valueBalance.setFLOW(flow: value)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Same as above. Maybe we can special case value 0.0 to avoid creating EVM.Balance.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Updated in e9246a2 .

Comment on lines 106 to 107
let valueBalance = EVM.Balance(attoflow: 0)
valueBalance.setFLOW(flow: value)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Same as above. Maybe we can special case value 0.0 to avoid creating EVM.Balance.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Updated in e9246a2 .

resultTypes: [Type]?
): EVM.ResultDecoded {
let valueBalance = EVM.Balance(attoflow: 0)
valueBalance.setFLOW(flow: value)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Same as above. Maybe we can special case value 0.0 to avoid creating EVM.Balance.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Updated in e9246a2 .

@joshuahannan

Copy link
Copy Markdown
Contributor

@m-Peter I'd like to start upgrading these contracts on testnet soon. Are the tests not passing because this function is not available in the emulator yet?

@m-Peter

m-Peter commented Apr 8, 2026

Copy link
Copy Markdown
Collaborator Author

@joshuahannan The tests are not passing, because the EVM contract has not been updated on mainnet. The CI is built by fetching dependencies from mainnet, and the current EVM contract on mainnet doesn't have the desired methods.

@joshuahannan

Copy link
Copy Markdown
Contributor

good to know! I'll schedule a multisig to upgrade the EVM contract on mainnet

@m-Peter

m-Peter commented Apr 9, 2026

Copy link
Copy Markdown
Collaborator Author

good to know! I'll schedule a multisig to upgrade the EVM contract on mainnet

@joshuahannan It would be good to first upgrade the EVM contract on testnet, and see that everything's working well.

@joshuahannan

Copy link
Copy Markdown
Contributor

Yes, that was the plan, just trying to get this update to the EVM contract merged first: onflow/flow-go#8529

Switches the EVM contract dependency from mainnet://e467b9dd11fa00df.EVM
to testnet://8c5303eaa26202d6.EVM and updates the hash and block height
to match the testnet version. All tests pass.
@joshuahannan

Copy link
Copy Markdown
Contributor

@m-Peter I just updated the dependency to use testnet instead of mainnet for EVM so we could make sure these tests pass and merge it. Is that okay?

@m-Peter

m-Peter commented Apr 9, 2026

Copy link
Copy Markdown
Collaborator Author

@m-Peter I just updated the dependency to use testnet instead of mainnet for EVM so we could make sure these tests pass and merge it. Is that okay?

@joshuahannan That's awesome 🎉 I wasn't sure if that was possible, I thought it had to be from mainnet only. CI now seems to be ✔️

@joshuahannan joshuahannan merged commit fe30b25 into main Apr 9, 2026
2 checks passed
@joshuahannan joshuahannan deleted the mpeter/optimize-evm-call-usage branch April 9, 2026 18:06
@github-project-automation github-project-automation Bot moved this from 👀 In Review to ✅ Done in 🌊 Flow 4D Apr 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done
Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

[Optimization] Replace call & dryCall with their optimized versions

4 participants