ERC-2771: Secure Protocol for Trusted Forwarders
Meta-transactions: a relayer pays gas on behalf of users who don't have ETH. The user signs an off-chain message, the relayer wraps it in a real transaction and calls a "Forwarder" contract, which calls the target appending the original signer to calldata. Targets that opt in extract the real _msgSender() from the appended data instead of msg.sender.
Required Convention
function isTrustedForwarder(address forwarder) external view returns (bool);
function _msgSender() internal view returns (address) {
if (msg.sender == trustedForwarder && msg.data.length >= 20) {
return address(bytes20(msg.data[msg.data.length - 20:]));
}
return msg.sender;
}Neo Equivalent: Witness Scopes (Solved at Protocol)
This entire ceremony is unnecessary on Neo. A relayer-paid transaction lists the relayer as Sender (paying the fee) and the user as a Signer with appropriate scopes. The target contract's Runtime.CheckWitness(user) succeeds because the user genuinely signed the transaction; the relayer is a separate witness paying gas. No forwarder, no _msgSender() wrapper, no calldata munging.
Live on Neo TestNet
Both implementations deployed on Neo N3 TestNet.
| Implementation | TestNet Address | Contract Hash |
|---|---|---|
Solidity (neo-solc) | NfiyLrGXijAAHiwq1LUGu69K92ybMu4ZS4 | 0x6653a8da…2c124ed9 |
Neo C# (nccs) | NLq6WUsRv4FAtxrqhwtTNxTeSgxAWYhG8P | 0x1463ad54…a6280c0a |
Checked-in snapshot verifies getNonce(deployer) == 0; the manifest now also exercises bumpNonce on both implementations. The C# port exposes named-method forwarding through Neo's witness model. docs/standards-mirror/deployments/erc-2771/.
