ERC20トークンぽいコントラクトをデプロイする@Unichain Sepolia by foundry(solidity)
今回はOpenZeppelinのERC20.solを参考に、少しオリジナリティを加えてERC20トークンぽいコントラクトをUnichain Sepoliaにデプロイします。
前提は、Solidityの開発ツールキットであるfoundryのインストールと正常動作の確認を終えていることです(以下の記事)。
コード(Solidity)
// SPDX-License-Identifier: MIT
//PNZ20.sol
pragma solidity ^0.8.20;
contract PNZ20 {
uint256 private _totalSupply; //総発行枚数
string private _name; //トークン名
string private _symbol; //トークンシンボル
uint8 private _decimals; //デシマル
mapping(address account => uint256) private _balances; //各アドレスの所持数
//mapping(address acount => mapping(address spender => uint256)) private _allowances; //トークン所持者がspenderに操作を許可している数量
address private _owner; //このコントラクトのオーナー
constructor(string memory name_, string memory symbol_, uint8 decimals_){
_name = name_;
_symbol = symbol_;
_decimals = decimals_;
_owner = msg.sender;
}
function name() public view returns(string memory){
return _name;
}
function symbol() public view returns(string memory){
return _symbol;
}
function decimals() public view returns(uint8){
return _decimals;
}
function totalSupply() public view returns(uint256){
return _totalSupply;
}
function balanceOf(address account) public view returns(uint256){
return _balances[account];
}
function owner() public view returns(address){
return _owner;
}
function transfer(address to, uint256 value) public returns(bool){
address from = msg.sender;
require(from != address(0), "zero address is unavailable(from)");
require(to != address(0), "zero address is unavailable(to)");
require(_balances[from] >= value, "insufficient balance");
_balances[to] += value;
_balances[from] -= value;
return true;
}
function mint(address account, uint256 value) external{
require(account != address(0), "Mint to zero address is unavailable");
//require(msg.sender == owner, "Mint is only available to contract owner");
_balances[account] += value;
_totalSupply += value;
}
function burn(uint256 value) external{
_balances[msg.sender] -= value;
_totalSupply -= value;
}
function hello() external pure returns(string memory){
return "This contract is created Ponzing Man! Wooohooo!";
}
}説明
Contractのname,symbol,decimals,owner
これらは、コントラクトがデプロイされたときに決まるようにしました(constructorはコントラクトデプロイ時に1度だけ実行され、そこで決まります)。
コントラクトのデプロイ時にトークンの名前、シンボル、デシマルを引数として指定します。
また、コントラクトのオーナーは、コントラクト作成のトランザクションを展開したウォレットアドレスになります。
private修飾子
状態変数にはすべてprivate修飾子を付けていますが、これにより状態変数はこのコントラクト内部からしかアクセスできません。
なので、このコントラクトの関数からアクセスすることになります。
public(修飾子が付いた)関数
public関数はコントラクトの外部からでも内部からでも呼び出せます。
name(), symbol(), decimals(), totalSupply(), balanceOf(), owner()がこれに当たります。また、偶然ですが今回はこれらの関数はコントラクト内部の値を読み取りますが書き換えませんのでveiw修飾子も付けています。
external修飾子
コントラクト外部からのみアクセス可能、という意味です。
なので、mint(), burn(), hello()はコントラクト外部からのみ呼び出せます。
transfer関数
送り元はトランザクションの送信者としています。
そして、送り元と送り先のアドレスがゼロアドレスでないことを確認しています。
そのうえで、送り元アドレスの残高が送ろうとしている数量(value)以上かどうかを確認しています。
送り先の残高にvalueを加え、送り元の残高からvalueを引いてtrueを返します。
mint関数
トークンを発行します。
誰でも発行できるようにしています(トランザクションを発行できるのをコントラクトのオーナーに限るのが通常なのかもしれませんが今回はこうしました)。
account:発行したトークンの送り先
value:発行数量
accountの残高をvalueだけ増やして、totalSupplyにvalueだけ加えています。
burn関数
さすがにこれはトークン所有者が自分のトークンをburn(焼却)する前提で書きました。
他人のトークンをburnするのはさすがに、、、ということで。
なので、トランザクションの発行者の残高からvalueだけ引かれて、totalSupplyもその分減少する、という感じです。
hello関数
参考にしたERC20トークンのコントラクトを写しただけでは面白くないので、文字列を返す関数を加えてみました。
こちらは、コントラクト内部の状態変数にアクセスすることもなければ変更することもないのでpure修飾子を付けています。
デプロイ@Unichain Sepolia
チェーンは何でもよいですが、今回はUnichain Sepoliaを選びました。
まずはビルドします。
forge build下の画像のように、コンパイルが通りました。

次にデプロイします。
※下記項目を任意に変更することができます。
・https://sepolia.unichain.org(別のチェーンにデプロイしたい場合)
・PonziSchemeToken(トークン名を別の名前にしたい場合)
・PNZ(トークンのシンボルを別のものにしたい場合)
・18(デシマルを変えたい場合)
forge create --broadcast --rpc-url https://sepolia.unichain.org --private-key <my_private_key> src/PNZ20.sol:PNZ20 --constructor-args "PonziSchemeToken" "PNZ" 18下の画像のように、デプロイできました。

トランザクションは下記で見られます。
コントラクト自体は下記で見られますが、デプロイ当初はContractタブに関数が表示されません。

Verify & publish
Contractタブを出現させるには、ブロックエクスプローラにソースコードをアップロードする必要があります。
上の画像の「Verify&publish」をクリックし、下画像のように項目を埋めます。(Compilerの項目については上のコンパイル時の画像を見ていただくと、Compiling 1 files with Solc 0.8.28とあります。)
そして、Verify&publishをクリック。

Successになると、いつもの見た目になります。

次の記事では、デプロイしたコントラクトにpythonからアクセスしてみます。
