The BTC-20 token standard on Bitcoin is similar to ERC-20 on Ethereum. It can represent virtually anything in Bitcoin:
Extending the OpenZeppelin ERC-20 contract, we can create a governance token (BIT) for a hypothetical DEX (Bitswap) on Bitcoin.
Copy // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
β
import "@openzeppelin/contracts/token/ERC20/ERC20.sol" ;
β
contract Bitswap is ERC20 {
constructor ( uint256 initialSupply) ERC20 ("Bitswap", "BIT") {
_mint (msg.sender , initialSupply);
}
}
β
We've prepared a few different examples for you to get started.
To compile your contracts, use the built-in hardhat compile task.
Review config file hardhat.config.ts. The network configs should look like this.
Copy networks: {
mynw: {
url: "http://localhost:10002",
accounts: {
mnemonic: "<your mnemonic with funds>"
},
timeout: 100_000,
},
blockscoutVerify: {
blockscoutURL: "http://localhost:4000", // your explorer URL
...
}
}
Run the deploy scripts using hardhat-deploy.
Make sure the accounts in hardhat.config.ts have some BTC.
Once the contracts are deployed, you can interact with them. We've prepared a few hardhat tasks to make it easy for you to interact with the contracts.
Copy
//// SPDX-License-Identifier: MIT
// Contract by Bitcoin EVM.
pragma solidity ^0.4.23;
contract owned {
address public owner;
constructor () public {
owner = msg.sender;
}
modifier onlyOwner {
require (msg.sender == owner);
_;
}
function transferOwnership ( address newOwner) onlyOwner public {
owner = newOwner;
}
}
interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) external; }
contract TokenERC20 {
string public name;
string public symbol;
uint8 public decimals = 18 ;
uint256 public totalSupply;
mapping ( address => uint256 ) public balanceOf;
mapping ( address => mapping ( address => uint256 )) public allowance;
event Transfer ( address indexed from, address indexed to, uint256 value);
event Approval ( address indexed _owner, address indexed _spender, uint256 _value);
event Burn ( address indexed from, uint256 value);
constructor (
uint256 initialSupply ,
string tokenName ,
string tokenSymbol
) public {
totalSupply = initialSupply * 10 ** uint256 (decimals);
balanceOf[msg.sender] = totalSupply;
name = tokenName;
symbol = tokenSymbol;
}
function _transfer ( address _from , address _to , uint _value ) internal {
require (_to != 0x0 );
require (balanceOf[_from] >= _value);
require (balanceOf[_to] + _value > balanceOf[_to]);
uint previousBalances = balanceOf[_from] + balanceOf[_to];
balanceOf[_from] -= _value;
balanceOf[_to] += _value;
emit Transfer (_from , _to , _value);
assert (balanceOf[_from] + balanceOf[_to] == previousBalances);
}
function transfer ( address _to , uint256 _value ) public returns ( bool success) {
_transfer (msg.sender , _to , _value);
return true ;
}
function transferFrom ( address _from , address _to , uint256 _value ) public returns ( bool success) {
require (_value <= allowance[_from][msg.sender]);
allowance[_from][msg.sender] -= _value;
_transfer (_from , _to , _value);
return true ;
}
function approve ( address _spender , uint256 _value ) public
returns ( bool success) {
allowance[msg.sender][_spender] = _value;
emit Approval (msg.sender , _spender , _value);
return true ;
}
function approveAndCall ( address _spender , uint256 _value , bytes _extraData )
public
returns ( bool success) {
tokenRecipient spender = tokenRecipient (_spender);
if ( approve (_spender , _value)) {
spender. receiveApproval (msg.sender , _value , this , _extraData);
return true ;
}
}
function burn ( uint256 _value ) public returns ( bool success) {
require (balanceOf[msg.sender] >= _value);
balanceOf[msg.sender] -= _value;
totalSupply -= _value;
emit Burn (msg.sender , _value);
return true ;
}
function burnFrom ( address _from , uint256 _value ) public returns ( bool success) {
require (balanceOf[_from] >= _value);
require (_value <= allowance[_from][msg.sender]);
balanceOf[_from] -= _value;
allowance[_from][msg.sender] -= _value;
totalSupply -= _value;
emit Burn (_from , _value);
return true ;
}
}
contract ERC20Token is owned , TokenERC20 {
uint256 public sellPrice;
uint256 public buyPrice;
mapping ( address => bool ) public frozenAccount;
event FrozenFunds ( address target, bool frozen);
constructor (
uint256 initialSupply ,
string tokenName ,
string tokenSymbol
) TokenERC20 (initialSupply, tokenName, tokenSymbol) public {}
function _transfer ( address _from , address _to , uint _value ) internal {
require (_to != 0x0 );
require (balanceOf[_from] >= _value);
require (balanceOf[_to] + _value >= balanceOf[_to]);
require ( ! frozenAccount[_from]);
require ( ! frozenAccount[_to]);
balanceOf[_from] -= _value;
balanceOf[_to] += _value;
emit Transfer (_from , _to , _value);
}
function mintToken ( address target , uint256 mintedAmount) onlyOwner public {
balanceOf[target] += mintedAmount;
totalSupply += mintedAmount;
emit Transfer ( 0 , this , mintedAmount);
emit Transfer ( this , target , mintedAmount);
}
function freezeAccount ( address target , bool freeze) onlyOwner public {
frozenAccount[target] = freeze;
emit FrozenFunds (target , freeze);
}
function setPrices ( uint256 newSellPrice , uint256 newBuyPrice) onlyOwner public {
sellPrice = newSellPrice;
buyPrice = newBuyPrice;
}
function buy () payable public {
uint amount = msg.value / buyPrice;
_transfer ( this , msg.sender , amount);
}
function sell ( uint256 amount) public {
address myAddress = this ;
require (myAddress.balance >= amount * sellPrice);
_transfer (msg.sender , this , amount);
msg.sender. transfer (amount * sellPrice);
}
}