HRC20 Token
In this chapter we will deploy an HRC20 token on HYDRA. All HRC20 compliant token contracts support a common set of methods:
contract HRC20 {
function totalSupply() constant returns (uint totalSupply);
function balanceOf(address _owner) constant returns (uint balance);
function transfer(address _to, uint _value) returns (bool success);
function transferFrom(address _from, address _to, uint _value) returns (bool success);
function approve(address _spender, uint _value) returns (bool success);
function allowance(address _owner, address _spender) constant returns (uint remaining);
event Transfer(address indexed _from, address indexed _to, uint _value);
event Approval(address indexed _owner, address indexed _spender, uint _value); }
}
Because all tokens share the same interface, it is much easier for wallets and exchanges to support all the different tokens out there in the wild.
In what follows, we will deploy the CappedToken, implemented by OpenZeppelin. We won't need to modify the contract in any way to make it work on HYDRA.
The
CappedToken
is an HRC20 compliant token, inheriting the basic functionalities from both StandardToken and MintableToken.In particular,
StandardToken
implements the HRC20 interface.MintableToken
adds themint(address _to, uint256 _amount)
method, to create new tokens out of thin air.CappedToken
adds limit to the max supply of tokens that could be minted.
mkdir mytoken && cd mytoken
git clone https://github.com/OpenZeppelin/openzeppelin-solidity.git
For this exercise, we'll start
hydrad
from scratch, in regtest mode:docker run -it --rm \
--name myapp \
-v `pwd`:/dapp \
-p 9899:9899 \
-p 9888:9888 \
hayeah/hydraportal
Enter into the container:
docker exec -it myapp sh
Generate some initial balance:
./hydra-cli generate 600
The HRC20 token we deploy will be "owned" by a particular UTXO address. A few administrative methods are protected, such that only the owner of the contract may use them.
These methods are protected by the
onlyOwner
modifier, which checks whether the msg.sender
is the contract's owner
:modifier onlyOwner() {
require(msg.sender == owner);
_;
}
For example, the method
mint
makes sure that only the owner can use it:function mint(address _to, uint256 _amount) onlyOwner canMint public returns (bool)
Let's generate an address to act as the owner.
./hydra-cli getnewaddress
hdgznat81MfTHZUrQrLZDZteAx212X4Wjj
There's nothing special about this address. You could use the address of any UTXO in your wallet.
Let's fund the owner address with 10 HYDRA, to pay for gas when we deploy our contract later:
./hydra-cli sendtoaddress hdgznat81MfTHZUrQrLZDZteAx212X4Wjj 10
cf652f54e6a6dde3e60fa4e38eee1c529bf4ecf3f8424c7ac7ef9717850cc984
After the payment confirms, you should that there is one UTXO for this owner address:
./hydra-cli listunspent 1 99999 '["hdgznat81MfTHZUrQrLZDZteAx212X4Wjj"]'
[
{
"txid": "cf652f54e6a6dde3e60fa4e38eee1c529bf4ecf3f8424c7ac7ef9717850cc984",
"vout": 1,
"address": "hdgznat81MfTHZUrQrLZDZteAx212X4Wjj",
"account": "",
"scriptPubKey": "76a91437158152a9768477770ecb7a9e55a5875b9f35b088ac",
"amount": 10.00000000,
"confirmations": 1,
"spendable": true,
"solvable": true
}
]
Finally, we'll need to configure the deployment tool
solar
to use this particular address as the owner:export HYDRA_SENDER=hdgznat81MfTHZUrQrLZDZteAx212X4Wjj
We are now ready to deploy our token contract.
The
CappedToken
constructor requires the _capacity
parameter, to specify the maximum number of tokens we can mint:function CappedToken(uint256 _capacity)
It takes quite a few steps to deploy a contract:
- 1.
- 2.
- 3.Concatenate 1 and 2 together, then make a
createcontract
RPC call to hydrad. - 4.Wait for transaction to confirm.
- 5.Record the address of the contract, and owner of the contract, for later uses.
To deploy the CappedToken contract, specifying 21 million as the capacity by passing in the constructor parameters as a JSON array (remember to set
HYDRA_SENDER
):solar deploy openzeppelin-solidity/contracts/token/HRC20/CappedToken.sol \
'[21000000]'
Then solar waits for confirmation:
🚀 All contracts confirmed
deployed openzeppelin-solidity/contracts/token/HRC20/CappedToken.sol =>
a778c05f1d0f70f1133f4bbf78c1a9a7bf84aed3
The contract had been deployed to
a778c05f1d0f70f1133f4bbf78c1a9a7bf84aed3
. (You'd get a different contract address).The
solar status
command lists all contracts that had been deployed with solar:solar status
✅ openzeppelin-solidity/contracts/token/HRC20/CappedToken.sol
txid: 457a5afe15686c0bd596635aeb78d4ff7d2bf6a75df66c7251e89ce4d9c8f6d3
address: 3db297ee4c225b45219d2a7aa68afea7f4e68832
confirmed: true
owner: hdgznat81MfTHZUrQrLZDZteAx212X4Wjj
Note that the contract's owner should be set to the
HYDRA_SENDER
value we have specified earlier. If you did not set HYDRA_SENDER
to anything, a random UTXO from the wallet is selected, and that become the owner.You can find more information about the deployed contracts in
solar.development.json
.The main difference between HYDRA and Ethereum is that HYDRA is built on Bitcoin's UTXO model, and Ethereum has its own account model, as we've seen in the UTXO chapter.
In Ethereum, the cost of a transaction is paid by an account. The amount paid is decremented from the account, but the account is still there.
UTXO, however, may be spent only once. So earlier we used an UTXO with the address
hdgznat81MfTHZUrQrLZDZteAx212X4Wjj
to deploy the contract. That UTXO is now gone, with its entire value spent.So every time you act as the owner of a contract, you destroy the owner UTXO. And next time you want to act as the owner of the contract, you'd need a new UTXO with the same address. This could be very annoying if you had to do it manually.
Fortunately, when interacting with a contract, HYDRA always create a new UTXO with the same address to replace UTXO that was used up.
Listing the UTXO for the owner address
hdgznat81MfTHZUrQrLZDZteAx212X4Wjj
, we see that, hey, there's still one UTXO, even though we already spent one:./hydra-cli listunspent 1 99999 '["hdgznat81MfTHZUrQrLZDZteAx212X4Wjj"]'
[
{
"txid": "457a5afe15686c0bd596635aeb78d4ff7d2bf6a75df66c7251e89ce4d9c8f6d3",
"vout": 1,
"address": "hdgznat81MfTHZUrQrLZDZteAx212X4Wjj",
"account": "",
"scriptPubKey": "76a91437158152a9768477770ecb7a9e55a5875b9f35b088ac",
"amount": 8.78777200,
"confirmations": 61,
"spendable": true,
"solvable": true
}
]
Note, however, that the transaction id
457a...f6d3
is different from the UTXO we had cf65...c984
. This is a different UTXO, and its amount is 10 minus the fee we paid to deploy the contract.The amount
8.78777200
is the "change". For a transaction that interacts with a contract, the "change" is paid to the sender. Whereas in a typical payment transaction, the change is paid to a newly generated address controlled by the wallet.The upshot is that despite the vastly different accounting model between Bitcoin UTXO and Ethereum account, in HYDRA they behave in very similar ways.
The
solar.development.json
file stores information about the deployed CappedToken contract. You can load this file into the ABIPlayer to interact with any contracts deployed with solar.Load the file, and you should see a list of available contracts and methods:
The gray buttons are readonly methods (call only). The blue buttons support both send and call.
Let's click on the
owner
button, to get the owner
of this contract:And we see the owner address returned as a hexadecimal address:
We can convert it back to the base58 UTXO address:
./hydra-cli fromhexaddress dcd32b87270aeb980333213da2549c9907e09e94
hdgznat81MfTHZUrQrLZDZteAx212X4Wjj
Let's mint some coins for the contract owner! Because we are sending the receiver address into the smart contract, we'll need to format the address in hexadecimal instead of base58.
To mint 1000 tokens:
Click send, and you'll see that the transaction is waiting for authorization:
This request requires your authorization because it costs HYDRA. Visit the authorization UI (http://localhost:9899/) to approve it:
Wait for confirmation, and you should see information about the transaction:
You may now call
balanceOf
and totalSupply
to check if the owner had received the minted tokens, and that the supply had increased accordingly:In this chapter we have deployed a basic HRC20 token, and encounter a few tools along the way:
solar deploy
to compile & create a contract.
Last modified 2yr ago