A holding account driven entirely by tx.origin: one owner key can move ether, move tokens, or grant an allowance, each with a single call.
Historical Significance
tx.origin authorisation was already understood to be dangerous when this was deployed; the Solidity documentation had warned against it for years. Seeing it at the centre of a widely replicated agent account is a reminder that the pattern persisted because it solved a real ergonomic problem, letting an operator route calls through whatever helper contract they liked without re-plumbing permissions, and that the phishing risk it carries was treated as acceptable when the account holds working balances rather than reserves.
Key Facts
Description
Storage holds one address. Every state-changing function checks tx.origin against it and does nothing else by way of access control. transferEth makes a raw call with caller-supplied value and gas limit, so the account can pay a contract that needs more than the transfer stipend. transferToken and approveToken are the ERC-20 counterparts, forwarding to transfer and approve on a token the caller names. blockVersion returns the literal string AgentAccount.1.0, which is how a fleet operator tells deployments apart when none of them are verified.
The fallback is payable and empty, so the account can be funded like a wallet.
Authorising on tx.origin rather than msg.sender is the decision that shapes everything else. It means the owner can reach this account through any intermediary contract, which is convenient for a relayer or batching front end, and it means any contract the owner is ever induced to call can turn round and drain this one.
Source Verified
Byzantium Era
First Metropolis hard fork. Added zk-SNARK precompiles, REVERT opcode, and staticcall.
Bytecode Overview
Verified Source Available
This contract has verified source code.
View Verification ProofShow source code (Solidity)
// Submitted by EthereumHistory (ethereumhistory.com)
pragma solidity ^0.4.23;
contract ERC20 {
function approve(address _spender, uint256 _value) public returns (bool);
function transfer(address _to, uint256 _value) public returns (bool);
}
contract AgentAccount {
address public owner;
function () public payable {
}
function transferEth(address _to, uint256 _value, uint256 _gas) public payable returns (bool) {
require(tx.origin == owner);
return _to.call.gas(_gas).value(_value)();
}
function approveToken(address _token, uint256 _value, address _spender) public returns (bool) {
require(tx.origin == owner);
return ERC20(_token).approve(_spender, _value);
}
function blockVersion() public pure returns (string) {
return "AgentAccount.1.0";
}
function transferToken(address _token, address _to, uint256 _value) public returns (bool) {
require(tx.origin == owner);
return ERC20(_token).transfer(_to, _value);
}
}