Skip to content

Commit cf347aa

Browse files
Neuronemgarbs
authored andcommitted
Updates
Signed-off-by: Giuseppe Bertone <giuseppe.bertone@hashgraph.com>
1 parent 53cc5bb commit cf347aa

1 file changed

Lines changed: 177 additions & 0 deletions

File tree

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
---
2+
hip: 0000 # Assigned by HIP editor.
3+
title: Ignore Trailing Calldata for System Contract
4+
author: Giuseppe Bertone (@neurone)
5+
working-group: Giuseppe Bertone (@neurone), Ian Holsman (@web3-nomad), Glib Kozyryatskyy (@gkozyryatskyy)
6+
requested-by: Squid <https://www.squidrouter.com>; Axelar <https://www.axelar.network>; Hedera Foundation <https://hedera.foundation>
7+
discussions-to: <URL of the GitHub Pull Request for this HIP> # This will be filled by the HIP editor upon PR creation.
8+
type: Standards Track
9+
category: Service
10+
needs-hiero-approval: Yes
11+
needs-hedera-review: Yes
12+
status: Draft
13+
created: 2025-11-14
14+
updated: 2025-11-14
15+
release: TBD
16+
---
17+
18+
## Abstract
19+
20+
This HIP proposes enabling system contract to accept and ignore trailing calldata beyond the ABI-defined parameters of a function. This restores EVM-parity behavior where contracts typically ignore additional bytes in calldata, allowing applications to append small "memo" or order identifiers to standard ERC-20-like calls (e.g., `transfer`, `approve`) without causing transaction failure. The change is implemented by updating the parameter decoding used by system contract so that decoding succeeds when extra bytes remain after all expected parameters are parsed. The extra bytes are ignored by the system contract and preserved in the underlying Ethereum transaction/record for off-chain consumption.
21+
22+
## Motivation
23+
24+
Several cross-chain and attribution workflows append a compact memo (e.g., 32-byte order ID) to calldata so downstream systems can correlate external processes with on-chain actions. On most EVM chains, contracts either tolerate or ignore extra calldata beyond expected arguments, so these transactions succeed and the memo is retrievable from the transaction input data.
25+
26+
System contracts currently fail when extra bytes are present in calldata, preventing ERC-20-style calls with a trailing memo from succeeding. This breaks compatibility for integrations relying on the memo pattern and requires workarounds that degrade developer experience. Enabling system contracts to accept and ignore trailing calldata resolves this incompatibility while preserving consensus integrity and transaction provenance.
27+
28+
## Rationale
29+
30+
- EVM compatibility: Typical Solidity contracts do not revert solely due to trailing calldata; the decoder consumes expected parameters and disregards the remainder. Bringing system contract behavior in line with this expectation reduces surprise and increases portability of EVM-based tooling.
31+
- Minimal scope: The change is localized to ABI parameter decoding within system contracts (and possibly shared libraries), with no changes to the system contract semantics or state transitions.
32+
- Data availability: The full calldata remains in the Ethereum transaction input; off-chain systems can continue to read memos via standard RPCs (e.g., `eth_getTransactionByHash`). HTS, account or transaction memo fields remain unrelated and are still only visible via native APIs.
33+
- Operational safety: Transaction size limits and fee/size checks already constrain worst-case payloads.
34+
35+
## User stories
36+
37+
- As a cross-chain router, I want to append an order ID to ERC-20 `transfer` calls routed through HTS system contract, so I can correlate off-chain orders with on-chain settlements.
38+
- As an analytics provider, I want to reliably extract app-defined identifiers from transaction calldata without modifying token contracts or system contracts.
39+
- As a dApp developer, I want my integration that works on other EVM networks to work on Hiero without special-case logic when using system contracts.
40+
41+
## Specification
42+
43+
This section defines the required behavior changes for system contracts.
44+
45+
- Scope
46+
- HTS, HAS, HSS system contracts, including paths invoked via `redirectForToken` (see [HIP-218](https://hips.hedera.com/hip/hip-218)) that emulate ERC-20 functions (`transfer`, `transferFrom`, `approve`, etc.).
47+
- Apply the same decoding rule to all future system contracts to ensure consistent behavior across the precompile/system contract surface.
48+
49+
- Decoding Rule
50+
- When decoding calldata for a system contract function, the decoder MUST:
51+
1. Parse the function selector and decode the exact set of ABI-defined parameters for the matched function.
52+
2. If additional bytes remain after successfully decoding all expected parameters, decoding MUST NOT fail solely due to the presence of these extra bytes.
53+
3. The system contract MUST ignore (i.e., not read/use) any trailing bytes beyond the expected parameters.
54+
- If decoding of required parameters fails (e.g., insufficient bytes or invalid types), the call MUST revert as today.
55+
56+
- Limits and Validation
57+
- Existing transaction size limits (including jumbo Ethereum transaction limits where applicable) remain in force.
58+
- No changes to fees or throttles are required.
59+
60+
- Semantics
61+
- State transitions and events emitted by system contracts are unchanged.
62+
- The extra trailing bytes have no semantic effect and are not persisted in system contract-specific fields, but remain visible in the Ethereum transaction input data.
63+
64+
- Observability
65+
- Mirror Node and RPC layers continue to return full transaction input data. No schema changes are required.
66+
- SDKs need no API changes; developers may continue using standard Ethereum JSON-RPC to read input data if desired.
67+
68+
### Example Behavior
69+
70+
- A call to an HTS-redirected ERC-20 `transfer(address,uint256)` with 20 extra bytes appended for a memo MUST succeed (assuming other checks pass) and perform an ordinary transfer, ignoring the memo bytes. The full calldata, including the memo trailer, is visible in the transaction input data via `eth_getTransactionByHash`.
71+
72+
### Impact on Mirror Node
73+
74+
- No schema changes. Transactions that previously failed due to strict decoding may now succeed; mirrors will observe corresponding status codes and logs.
75+
- The mirror node APIs are already compliant because they don't examine the calldata but just offer that information to clients.
76+
77+
### Impact on the Mirror Node Explorer
78+
79+
The mirror node explorer should show trailing data correctly:
80+
81+
- In case the ABI is unknown, the raw input is visible as-is, including any trailing bytes.
82+
- In case the ABI is known, call arguments should be parsed and an extra field should be dedicated in the UI for the trailing data.
83+
84+
### Impact on SDK
85+
86+
- SDKs should adapt the Ethereum decoding functions so that they can safely ignore the additional info in the calldata.
87+
- No API changes required: the "memo-in-calldata" technique can be considered an EVM workaround, and there's no intention to offer this ability via SDK, where developers can leverage the dedicated memo field present in every Hiero transaction.
88+
89+
## Backwards Compatibility
90+
91+
This is a consensus rule change: transactions that previously failed when extra trailing calldata was present will now succeed.
92+
93+
Mitigations:
94+
95+
- Analyze all the mainnet and testnet Ethereum transactions calling the system contracts and failing because of malformed calldata.
96+
- In case transactions like that are present, put in place actions to contact the developers/users for those transactions:
97+
- Known actors: contact them directly via email, Slack, GitHub
98+
- Unknown actors: contact them over the network (void transfers with memo attached).
99+
- Document the change in release notes to alert any systems that may have (unusually) relied on reversion when extra bytes were present.
100+
101+
Overall, the change aligns Hiero behavior with common EVM expectations and improves portability.
102+
103+
## Security Implications
104+
105+
- Misinterpretation risk: System contracts must explicitly ignore the trailing bytes and never act on them, avoiding semantic ambiguity.
106+
- Input size abuse: Extra bytes could be used to inflate input size. Existing transaction size limits and fees already constrain this. A configurable cap on trailing bytes further reduces risk.
107+
- Audit: Treat the decoder change as security-sensitive; apply standard code review and fuzz testing for malformed calldata.
108+
109+
## How to Teach This
110+
111+
- Concept: "Memo via calldata" is an EVM pattern where applications append memo or metadata to calls. System contracts will accept and ignore these bytes, while preserving them in the transaction input for off-chain reading.
112+
- Guidance: Continue using standard Ethereum JSON-RPC to obtain input data. Do not expect any `memo` field to reflect the appended calldata.
113+
- Examples: Provide docs showing `transfer(to, amount)` with an appended 32-byte order ID and how to retrieve it via RPC.
114+
115+
### Examples
116+
117+
Retrieve the transaction input via JSON-RPC and parse the trailing memo with Ethers.js.
118+
119+
```js
120+
const { ethers } = require("ethers");
121+
122+
async function main() {
123+
const provider = new ethers.JsonRpcProvider("https://mainnet.hashio.io/api");
124+
const txHash = "0x976965d65ee09f20fb152ff5e1de9490720a8e4e4a0707ae6093a6d0341f6bb1";
125+
126+
const input = (await provider.getTransaction(txHash)).data;
127+
const iface = new ethers.Interface(["function transfer(address to, uint256 amount)"]);
128+
const decoded = iface.decodeFunctionData("transfer", input);
129+
const trailingData = iface.encodeFunctionData("transfer", [decoded[0], decoded[1]]);
130+
const memoHex = "0x" + input.slice(trailingData.length);
131+
132+
console.log("Calldata:", input);
133+
console.log("Decoded transfer:", decoded[1].toString(), "units to", decoded[0]);
134+
console.log("Unencoded memo:", memoHex);
135+
}
136+
137+
main().catch(console.error);
138+
139+
// Result
140+
// Calldata: 0xa9059cbb000000000000000000000000136c1cb3257dcf2615e25c914fd1767dcf08457700000000000000000000000000000000000000000000000000000000000000643372348f476a87cf942167a35e78e40f00c91c99072b0c515ba8118cbd927fb7
141+
// Decoded transfer: 100 units to 0x136c1cb3257DCF2615E25c914fd1767dcf084577
142+
// Unencoded memo: 0x3372348f476a87cf942167a35e78e40f00c91c99072b0c515ba8118cbd927fb7
143+
```
144+
145+
## Reference Implementation
146+
147+
The implementation depends on the current state of the code base, but here is an overall potential flow that satisfies the HIP:
148+
149+
- Update the ABI decoding library used by system contracts (and shared system contract decoding utilities if present) to:
150+
- Succeed when extra bytes remain after expected parameters are decoded.
151+
- Ignore the trailing bytes without attempting to deserialize them.
152+
- Add unit and property/fuzz tests covering:
153+
- Exact-length calldata (baseline)
154+
- Calldata with small trailing bytes (success)
155+
- Malformed inputs (revert)
156+
157+
## Rejected Ideas
158+
159+
- Map trailing calldata to HTS `memo` automatically: Rejected to avoid conflating EVM calldata with native memo semantics and to keep behavior consistent with EVM norms.
160+
- Introduce new system contracts methods with explicit `bytes memo` parameters: Heavier API surface change with less compatibility benefit; ignores off-chain tools already reading calldata.
161+
- Limit to only `transfer`: Applying uniformly across HTS and all system contracts is simpler, less surprising for developers and users, and fully EVM-compatible.
162+
- Limit this HIP to HTS system contract only: Applying the change to all present and future system contracts ensures consistent behavior and avoids confusion.
163+
164+
## Open Issues
165+
166+
- Decide whether there should be a maximum tolerated trailing bytes cap and, if so, determine its value.
167+
168+
## References
169+
170+
- Ethereum StackExchange: "Is there a way to attach a memo to an ERC-20 approve transaction" (<https://ethereum.stackexchange.com/questions/138779/is-there-a-way-to-attach-a-memo-to-an-erc-20-approve-transaction>)
171+
- Example success on Arbitrum with 3rd argument ignored: <https://arbiscan.io/tx/0x4d17711d22420314c7da40df8a7262944fe41ac394d508e79e48bc71add39e51>
172+
- Example failure on Hedera (strict decoding): <https://hashscan.io/mainnet/transaction/1762783522.983266000/result>
173+
174+
## Copyright/license
175+
176+
This document is licensed under the Apache License, Version 2.0 —
177+
see [LICENSE](../LICENSE) or <https://www.apache.org/licenses/LICENSE-2.0>.

0 commit comments

Comments
 (0)