Security Disclosure: Native Token Transfers Incorrectly Include ERC-20 Call
Repository: pushchain/push-chain-sdk
Severity: Medium
Status: Unfixed as of 2026-03-28
Summary
In the multicall payload builder, the isNative flag is computed and logged correctly but is not used in the actual branching condition. When a native token (ETH/SOL bridged as native PC) is used in a non-array multicall, the code incorrectly encodes an ERC-20 transfer() call to the PRC-20 token address, which will either fail or produce unexpected behavior.
Affected Code
File: packages/core/src/lib/orchestrator/payload-builders.ts, lines 54-67
const isArrayMulticall = Array.isArray(execute.data);
const isNative = token.mechanism === 'native';
log('buildExecuteMulticall — Branch 2 (funds): ' + JSON.stringify({
// ...
willAddErc20Transfer: !isNative && !isArrayMulticall, // ← Correct logic in log
skippedReason: isNative ? 'native token — no PRC-20 transfer needed' : ...,
}, null, 2));
// Only add ERC-20 transfer for non-native tokens AND when NOT in array multicall mode
// - Native tokens (ETH/SOL) are bridged as native PC on Push Chain, not as PRC-20
// - When execute.data is an array (explicit multicall), user handles fund transfers
if (!isArrayMulticall) { // ← BUG: should be `if (!isNative && !isArrayMulticall)`
const erc20Transfer = encodeFunctionData({
abi: ERC20_EVM,
functionName: 'transfer',
args: [execute.to, execute.funds?.amount],
});
The comment on line 64 and the logging on line 61 both describe the correct condition (!isNative && !isArrayMulticall), but the actual if statement on line 67 only checks !isArrayMulticall.
Impact
When token.mechanism === 'native' and execute.data is not an array:
- An ERC-20
transfer() call is encoded for a PRC-20 address
- This address likely doesn't have a valid ERC-20
transfer implementation for native tokens
- Result: transaction reverts (wasted gas) or, worse, if the PRC-20 address has a fallback that accepts the call, funds could be misrouted
Suggested Fix
--- a/packages/core/src/lib/orchestrator/payload-builders.ts
+++ b/packages/core/src/lib/orchestrator/payload-builders.ts
@@ -64,7 +64,7 @@
// Only add ERC-20 transfer for non-native tokens AND when NOT in array multicall mode
// - Native tokens (ETH/SOL) are bridged as native PC on Push Chain, not as PRC-20
// - When execute.data is an array (explicit multicall), user handles fund transfers in their calls
- if (!isArrayMulticall) {
+ if (!isNative && !isArrayMulticall) {
const erc20Transfer = encodeFunctionData({
One character change — adds the !isNative && check that the comment and logging already describe.
Security Disclosure: Native Token Transfers Incorrectly Include ERC-20 Call
Repository: pushchain/push-chain-sdk
Severity: Medium
Status: Unfixed as of 2026-03-28
Summary
In the multicall payload builder, the
isNativeflag is computed and logged correctly but is not used in the actual branching condition. When a native token (ETH/SOL bridged as native PC) is used in a non-array multicall, the code incorrectly encodes an ERC-20transfer()call to the PRC-20 token address, which will either fail or produce unexpected behavior.Affected Code
File:
packages/core/src/lib/orchestrator/payload-builders.ts, lines 54-67The comment on line 64 and the logging on line 61 both describe the correct condition (
!isNative && !isArrayMulticall), but the actualifstatement on line 67 only checks!isArrayMulticall.Impact
When
token.mechanism === 'native'andexecute.datais not an array:transfer()call is encoded for a PRC-20 addresstransferimplementation for native tokensSuggested Fix
One character change — adds the
!isNative &&check that the comment and logging already describe.