The Golem Network Token (GNT) is not a fully ERC20-compliant token. The ERC20 standard requires the following methods and events to be implemented for full compliance:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
contract ERCTokenInterface { // Methods function totalSupply() constant returns (uint256 totalSupply); function balanceOf(address _owner) constant returns (uint256 balance); function transfer(address _to, uint256 _value) returns (bool success); function transferFrom(address _from, address _to, uint256 _value) returns (bool success); function approve(address _spender, uint256 _value) returns (bool success); // Events event Transfer(address indexed _from, address indexed _to, uint256 _value); event Approval(address indexed _owner, address indexed _spender, uint256 _value); } |
Check out the Golem Network Token source code at Golem Crowdfunding Funded To 820,000 ETH Cap, and you will notice that the transferFrom(...)
and approve(...)
methods are not implemented, and the Approval(...)
event is not implemented.
It is for this reason that there is only a Trustless Golem Network Token (GNT) Selling Contract. There is no equivalent buying contract or a buy/sell contract.
User /u/vnovak has deployed an ERC20-compliant wrapper token for GNT to 0x936f78b9852d12f5cb93177c1f84fb8513d06263 – source ERC20-compliant wrapper token for GNT – will enable trading of wrapped GNT on Etherdelta.
Good news! This will also allow the trading of buy contracts or buy/sell contracts on the decentralised trusted platform https://cryptoderivatives.market/. This post will look into more details on GNTW smart contract that wraps the GNT.
See The Original Augur REP TokenTraderFactory Contract (Work In Progress) for more details on the REP TokenTraderFactory contract that will allow the creation of a buy/sell market on GNTW.
Table of contents
- 1. How The GNTW Wrapper Works
- 2. How To Wrap Your GNT In GNTW
- 3. How To Unwrap Your GNT From GNTW
- 4. Watch The GNTW Contract And Token In Ethereum Wallet / Mist
- 5. GNTW Source Code
- 6. GNTW Application Binary Interface
1. How The GNTW Wrapper Works
1. You transfer your GNT tokens to the GNTW contract. The GNTW contract now owns your GNT tokens, and issues you with the equivalent number of GNTW tokens.
2. You can transfer your GNTW tokens between addresses like any ERC20-compliant tokens.
3. You can approve the transfer of GNTWs to another account using the GNTW approve(...)
. The other account can then use the GNTW transferFrom(...)
to transfer up to the approved amount from your account to itself.
4. At any time, you can transfer your GNTWs to the GNTW contract and your GNTs will be unwrapped and deposited to your account.
2. How To Wrap Your GNT In GNTW
1. Call createPersonalDepositAddress()
. This creates a new DepositSlot
contract and associates it with your account.
2. Call getPersonalDepositAddress(address depositer)
to find out your personal deposit address. This is the address of your newly created DepositSlot
contract. The depositer
is your Ethereum wallet address.
3. Use the GNT contract to transfer(address _to, uint256 _value)
your GNTs from your account to your personal deposit address. The _to
is your personal deposit address from 2. above. _value
is the amount of tokens you want to transfer, in the native units. 1 GNT = 1e18 or 1,000,000,000,000,000,000 (enter without commas).
4. Call processDeposit()
so that the GNTW contract wraps your newly deposited GNTs. Your will receive the same number of GNTWs as amount of GNTs you deposited.
5. You can now use the ERC20-compliant GNTW contract methods transfer(address _to, uint256 _amount)
, approve(address _spender, uint256 _amount)
and transferFrom(address _from, address _to, uint256 _amount)
to move the GNTWs to different accounts.
6. Test it yourself. Before you wrap up significant amounts of your GNTs, check out the source code below, wrap up a small amount of your GNTs, transfer a portion of your GNTWs to another of your accounts, check that you can unwrap the GNTW into GNTs in both accounts and check that the numbers are correct.
3. How To Unwrap Your GNT From GNTW
1. To extract GNTs from your GNTW contract, use the GNT contract transfer(...)
to transfer the GNTW tokens from your account to the GNTW contract. Your GNTs will be unwrapped and transferred from the GNTW contract to your account.
4. Watch The GNTW Contract And Token In Ethereum Wallet / Mist
4.1 Watch The GNTW Contract
To watch the GNTW contract in Ethereum Wallet or Mist, select the Contracts tab on the top bar. Click on Watch Contract.
In the following screen:
- Set CONTRACT NAME to
GNTW
. - Set CONTRACT ADDRESS to
0x936f78b9852d12f5cb93177c1f84fb8513d06263
. - Set JSON INTERFACE to the blob of text in 6. GNTW Application Binary Interface.
4.2 Watch The GNTW Token
To watch the GNTW token in Ethereum Wallet or Mist, select the Contracts tab on the top bar. Click on Watch Token.
In the following screen, set the TOKEN CONTRACT ADDRESS to 0x936f78b9852d12f5cb93177c1f84fb8513d06263. The TOKEN NAME, TOKEN SYMBOL and DECIMAL PLACES OF SMALLEST UNIT will be automatically filled.
5. GNTW Source Code
The verified GNTW source code at the address 0x936f78b9852d12f5cb93177c1f84fb8513d06263 is shown below.
You can see that it is a fully ERC20-compliant wrapper token.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
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); } } |
See also the Maker Market GNT wrapper at makerdao/token-wrapper for comparison.
6. GNTW Application Binary Interface
[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_amount","type":"uint256"}],"name":"approve","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"depositer","type":"address"}],"name":"getPersonalDepositAddress","outputs":[{"name":"depositAddress","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"standard","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"createPersonalDepositAddress","outputs":[{"name":"depositAddress","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"GNT","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"processDeposit","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":false,"name":"_amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_spender","type":"address"},{"indexed":false,"name":"_amount","type":"uint256"}],"name":"Approval","type":"event"}]
Here is the corresponding method and event listing:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
contract TokenInterface { 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); function _transfer(address _to, uint256 _amount) internal returns (bool success); function _transferFrom(address _from, address _to, uint256 _amount) internal returns (bool success); function approve(address _spender, uint256 _amount) returns (bool success); function allowance(address _owner, address _spender) constant returns (uint256 remaining); } contract DepositSlot { address public constant GNT = 0xa74476443119A942dE498590Fe1f2454d7D4aC0d; address public wrapper; function collect() onlyWrapper; } 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; function createPersonalDepositAddress() returns (address depositAddress); function getPersonalDepositAddress(address depositer) constant returns (address depositAddress); function processDeposit(); function transfer(address _to, uint256 _amount) returns (bool success); function transferFrom(address _from, address _to, uint256 _amount) returns (bool success); function withdrawGNT(uint amount) internal } |