feat[venom]: support max_outsize=INF in raw_call - #5271
Merged
harkal merged 25 commits intoSep 17, 2026
Merged
Conversation
…nto a (bool, Bytes[INF]) frame
…igned to a bounded target
…w-call-bytes-inf-return
charles-cooper
approved these changes
Sep 16, 2026
charles-cooper
left a comment
Member
There was a problem hiding this comment.
surprisingly simple! lgtm pending clanker + @Sporarum @HodanPlodky review
Gas ChangesNo changes detected. Summary
|
📊 Bytecode Size Changes (venom)No changes detected. Full bytecode sizes
|
max_outsize=INF in raw_call
harkal
marked this pull request as ready for review
September 16, 2026 18:02
HodanPlodky
reviewed
Sep 16, 2026
Sporarum
approved these changes
Sep 17, 2026
Collaborator
|
Oh, and should close #5246 (add |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What I did
Closes: #5246
raw_call(..., max_outsize=INF)returns the whole return data asBytes[INF], or(bool, Bytes[INF])withrevert_on_failure=False, sized byreturndatasizeat runtime.max_outsizewas a compile-time cap: proxies, forwarders and multicall wrappers had to guess a bound or truncate silently. Venom already hasBytes[INF]locals and unboundedextcallreturns;raw_callwas the last return-data ingress without an unbounded form. Venom only; the legacy pipeline rejects it.How I did it
Semantics (
vyper/builtins/functions.py,vyper/builtins/_signatures.py).RawCall._is_unbounded_outsizerecognises theINFname onmax_outsizeand nothing else, sogas=INFandvalue=INFstill fail theuint256check.BuiltinFunctionT._validate_arg_typeshad its per-kwarg loop body extracted into_validate_kwargsoRawCallcanoverride it for that one kwarg without duplicating the loop; no behaviour change for other builtins.RawCall.infer_kwarg_typesannotates the kwarg withINF's own type because local analysis re-visits every kwarg value against those types.fetch_call_returnreturnsBytesT(INF)/TupleT([BoolT(), BytesT(INF)]).Which type drives codegen. The unbounded branch in
lower_raw_callis keyed on the call's own return type, not on the kwarg and not on the node annotation. TheCallnode's_metadata["type"]is the consumer-widened type:x: Bytes[INF] = raw_call(t, d, max_outsize=32)annotates the call asBytes[INF], and branching on it would drop the cap. Codegen therefore recomputesnode.func._metadata["type"].fetch_call_return(node), which is pure. There is no stored accessor for a builtin call's own return type:call_return_typeis only written for externalContractFunctionTcalls, and the other widenable builtins (concat,slice,abi_encode, ...) legitimately produce the widened buffer type, so extending the metadata would add a second writer for a single consumer. Two runtime tests pin the bounded cap through aBytes[INF]consumer; they fail if the annotation is used.Lowering (
vyper/codegen_venom/builtins/system.py,context.py). On the unbounded path the call is emitted withretsz=0(the existing zero-output path). Theokblock then callsmaterialize_returndata_bytes:returndatasize-> checked32 + ceil32(len)-> scratch allocation -> length store -> padding zero ->returndatacopy. Withrevert_on_failure=Falsethe(bool, Bytes[INF])result is a dynamic tuple frame around that value; the bytes member isthe return data on both success and failure, as on the bounded path. The bounded branches are textually unchanged apart from an assertion that the semantic type matches the literal, so bounded output is byte-identical (verified below).Cost. The return data is copied raw, no decode: the caller pays memory expansion for the actual
returndatasizeonly, and the callee can only produce what the caller's gas allowed.x: Bytes[INF] = raw_call(...)copies the value twice (scratch, then the local), the same as unboundedextcallreturns today.Legacy. The existing unbounded-sequence gate (
_validate_legacy_function_body_no_unbounded_sequences) already rejects every position, declaration and expression-only (len(...),keccak256(...),[...][0]), withunbounded sequence types require --experimental-codegen. Tests pin that message and that the same code compiles under Venom.Docs.
raw_callentry indocs/built-in-functions.rst, plus atypes-unboundedlabel indocs/types.rstfor the cross-reference.How to verify it
tests/functional/codegen/features/test_inf_raw_call.py(Venom only, 17 tests): empty and 10 KB return data; assignment to aBytes[INF]local; external return; forwarding through an internal function (dret, inlining disabled) and into anextcallargument;(bool, Bytes[INF])unpack, tuple return and direct subscript on success and on failure (revert data returned, not raised);is_static_callandis_delegate_call;msg.dataforwarding proxy, including return data shorter than the forwarded calldata and not word-aligned; boundedmax_outsizereturned through aBytes[INF]consumer keeps its cap.tests/unit/semantics/types/test_inf.py: resolved return type forrevert_on_failurex plain/static/delegate; ABIbytes/(bool, bytes).tests/functional/syntax/test_raw_call.py:gas=INF/value=INFrejected; narrowing intoBytes[100]/(bool, Bytes[100])rejected with the exact message; legacy rejection in four positions; bounded forms unchanged.Commit message
Description for the changelog
raw_callacceptsmax_outsize=INFunder the experimental code generator and returns the whole return data asBytes[INF](or(bool, Bytes[INF])withrevert_on_failure=False).Cute Animal Picture