A 2016 wrapper that gave Golem Network Token the ERC-20 approve and transferFrom functions its own contract never implemented.
Historical Significance
GNTW is one of the earliest examples on Ethereum of a wrapper deployed to retrofit a standard onto a token that shipped without it. The pattern it demonstrates, leaving the original contract untouched and issuing a compliant claim on it instead, became the standard answer to a non-conforming token and is the same idea later applied to CryptoPunks and to Etheria.
Context
ERC-20 was proposed in November 2015 and was still settling into practice through 2016. Golem's crowdsale in November 2016 was one of the largest of the period, so a very widely held token was suddenly in circulation with an interface that predated common agreement on what a token contract had to provide. Wrapping was the only route to compatibility that did not require asking every holder to migrate.
Token Information
Key Facts
Description
Golem Network Token Wrapped (GNTW) was deployed on 14 November 2016, three days after the Golem Network Token itself. GNT is a transfer-only token: its interface provides transfer and balanceOf but no approve, no transferFrom and no allowance. That omission is not cosmetic. Without an allowance mechanism, no other contract can move GNT on a holder's behalf, which rules out exchange escrow, automated market makers and any form of delegated spending.
GNTW exists to close that gap. A holder deposits GNT and receives an equivalent balance of a fully compliant ERC-20 token that can be approved and pulled by other contracts, and the process reverses to recover the original.
Heuristic Analysis
The following characteristics were detected through bytecode analysis and may not be accurate.
Tangerine Whistle Era
Emergency fork to address DoS attacks. Repriced IO-heavy opcodes.
Bytecode Overview
Verified Source Available
This contract has verified source code on Etherscan.
Show source code (Solidity)
pragma solidity ^0.4.4;
// ERC20-compliant wrapper token for GNT
// adapted from code provided by u/JonnyLatte
contract TokenInterface {
mapping (address => uint256) balances;
mapping (address => mapping (address => uint256)) allowed;
uint256 public totalSupply;
function balanceOf(address _owner) constant returns (uint256 balance);
function transfer(address _to, uint256 _amount) returns (bool success);
function transferFrom(
address _from, address _to, uint256 _amount) returns (bool success);
function approve(address _spender, uint256 _amount) returns (bool success);
function allowance(
address _owner, address _spender) constant returns (uint256 remaining);
event Transfer(address indexed _from, address indexed _to, uint256 _amount);
event Approval(
address indexed _owner, address indexed _spender, uint256 _amount);
}
contract Token is TokenInterface {
function balanceOf(address _owner) constant returns (uint256 balance) {
return balances[_owner];
}
function _transfer(address _to,
uint256 _amount) internal returns (bool success) {
if (balances[msg.sender] >= _amount && _amount > 0) {
balances[msg.sender] -= _amount;
balances[_to] += _amount;
Transfer(msg.sender, _to, _amount);
return true;
} else {
return false;
}
}
function _transferFrom(address _from,
address _to,
uint256 _amount) internal returns (bool success) {
if (balances[_from] >= _amount
&& allowed[_from][msg.sender] >= _amount
&& _amount > 0) {
balances[_to] += _amount;
balances[_from] -= _amount;
allowed[_from][msg.sender] -= _amount;
Transfer(_from, _to, _amount);
return true;
} else {
return false;
}
}
function approve(address _spender, uint256 _amount) returns (bool success) {
allowed[msg.sender][_spender] = _amount;
Approval(msg.sender, _spender, _amount);
return true;
}
function allowance(address _owner,
address _spender) constant returns (uint256 remaining) {
return allowed[_owner][_spender];
}
}
contract DepositSlot {
address public constant GNT = 0xa74476443119A942dE498590Fe1f2454d7D4aC0d;
address public wrapper;
modifier onlyWrapper {
if (msg.sender != wrapper) throw;
_;
}
function DepositSlot(address _wrapper) {
wrapper = _wrapper;
}
function collect() onlyWrapper {
uint amount = TokenInterface(GNT).balanceOf(this);
if (amount == 0) throw;
TokenInterface(GNT).transfer(wrapper, amount);
}
}
contract GolemNetworkTokenWrapped is Token {
string public constant standard = "Token 0.1";
string public constant name = "Golem Network Token Wrapped";
string public constant symbol = "GNTW";
uint8 public constant decimals = 18; // same as GNT
address public constant GNT = 0xa74476443119A942dE498590Fe1f2454d7D4aC0d;
mapping (address => address) depositSlots;
function createPersonalDepositAddress() returns (address depositAddress) {
if (depositSlots[msg.sender] == 0) {
depositSlots[msg.sender] = new DepositSlot(this);
}
return depositSlots[msg.sender];
}
function getPersonalDepositAddress(
address depositer) constant returns (address depositAddress) {
return depositSlots[depositer];
}
function processDeposit() {
address depositSlot = depositSlots[msg.sender];
if (depositSlot == 0) throw;
DepositSlot(depositSlot).collect();
uint balance = TokenInterface(GNT).balanceOf(this);
if (balance <= totalSupply) throw;
uint freshGNTW = balance - totalSupply;
totalSupply += freshGNTW;
balances[msg.sender] += freshGNTW;
Transfer(address(this), msg.sender, freshGNTW);
}
function transfer(address _to,
uint256 _amount) returns (bool success) {
if (_to == address(this)) {
withdrawGNT(_amount); // convert back to GNT
return true;
} else {
return _transfer(_to, _amount); // standard transfer
}
}
function transferFrom(address _from,
address _to,
uint256 _amount) returns (bool success) {
if (_to == address(this)) throw; // not supported
return _transferFrom(_from, _to, _amount);
}
function withdrawGNT(uint amount) internal {
if (balances[msg.sender] < amount) throw;
balances[msg.sender] -= amount;
totalSupply -= amount;
Transfer(msg.sender, address(this), amount);
TokenInterface(GNT).transfer(msg.sender, amount);
}
}External Links
Related contracts
My DAO Shares
Same eraA fixed-supply token with direct transfers only, deployed October 2016.
0xcbee2e...36ee4fOctober 18, 2016Alien
Same eraA fixed-supply token with transfers and third-party allowances, deployed October 2016.
0x00e0a4...9e52a0October 18, 2016Alien
Same eraA token with transfers and third-party allowances, and an owner who can mint new units, deployed October 2016.
0x9dbb48...affbb8October 19, 2016AiraEtherFunds
Same eraAn Ethereum fund token from the AIRA (Autonomous Intelligent Robot Agency) project, deployed in October 2016 as part of an early robot economy DAO ecosystem.
0xdcf691...3826c5October 19, 2016GavCoin
Same eraToken created by Gavin Wood (Ethereum co-founder), with a buyin/refund Dutch auction mechanism. ABI matches the official gavofyork/gavcoin GitHub repository.
0x0b8d56...74de06October 19, 2016ICONOMI
Same eraThe ICN token for ICONOMI, a Slovenian decentralized crypto fund management platform that raised $10.5M in its 2016 ICO — the third-largest crowdsale in Ethereu
0x888666...87d698October 24, 2016