ERC20.sol
のdecimals()
機能の理解
この関数の目的は、コントラクトで実行されている測定単位Wei
とプログラマーによって表示された測定単位とのEthers
間の桁違いの関係を他の人に伝えることです。これは私たちの電子モールに相当し、内部で使用される単位はセントですが、外部で使用される単位は人民元なので、基本差として2を返すことができます。
イーサリアムネットワークでは、内部で使用されるWei
単位はであり、外部で使用される単位はEther
であるため、基本差として18が返されます。
view
識別子について
そのview
目的は、状態を変更することではなく、機能を制御することです。最も一般的なのは、次の3つの状況です。
最初のものは間違いなく状態変数を変更しています。2番目のものはイベントを送信できない関数として簡単に分類できます。view
イベントが送信されると、Ethereumログが変更されるため、カウントされませんview
。他のコントラクト、Ethereumのデータを作成します。また変更されます。
つまり、イーサリアムのデータを変更できるコンテンツview
は関数に表示されません。
virtual
識別子について
virtual
IDは子コントラクトに継承でき、override
IDは親コントラクトをオーバーライドします。
returns(uint8)
識別子によって返される内容はuint8型でありuint8
、符号なし8ビット整数、つまり0〜255を表すことができる数値を識別します。これは完全に十分です。
function decimals() public view virtual override returns (uint8) {
return 18;
}
balanceOf
ユーザーバランスをとる機能について
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
この指定されたアカウントの残高はWei
、単位で識別されることに注意してください。これは外部関数であることを示していますexternal
が、なぜpublic
ですか?
public
関数または変数が外側と内側の両方に表示されることを示します。external
関数または変数が外側にのみ表示され、内側には表示されないことを示します。
- 次の関数での転送のロジック
transfer
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
パラメータ内のアドレスのみが送金の受け取りに使用され、送金uint256
の金額を示していることに注意してください。これexternal
は、外の世界にのみ表示されることを意味します。
上記は関数のインターフェースであり、以下は実装です
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `recipient` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
_transfer(_msgSender(), recipient, amount);
return true;
}
このドキュメントは2行しかなく、デフォルトでtrueを返します。関数にエラーがある場合、例外が直接スローされ、対応するロジックは実行されません。
この関数の興味深い点の1つは、_msgSender()
実際、自分の考えに従って考えれば、完全に使用できると思うことmsg.sender()
です。なぜ関数を使用する必要があるのでしょうか。Context.sol;
これは、統一された調整を行うことができるように、すべての一般的なものとコンテキスト的なものをそれに統合したいという作者の願いであるべきだと思います。
/**
* @dev Moves `amount` of tokens from `sender` to `recipient`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `sender` cannot be the zero address.
* - `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
*/
function _transfer(
address sender,
address recipient,
uint256 amount
) internal virtual {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(sender, recipient, amount);
uint256 senderBalance = _balances[sender];
require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[sender] = senderBalance - amount;
}
_balances[recipient] += amount;
emit Transfer(sender, recipient, amount);
_afterTokenTransfer(sender, recipient, amount);
}
上記internal
は、関数が外部ではなく内部で表示されることを意味し、上記についてこれ以上言うことはありません。
これaddress(0)
は、送信者と受信者が0アドレスになることはできないことを意味します。ここでの表示変換は、数値をアドレスに変換することです。これは、アドレスの欠落による誤った転送を防ぐためです。
_beforeTokenTransfer
転送前に何をする必要があるかを示します。もちろん、例外を直接スローして伝達関数を中断することもできます。これは、すべての非メインラインロジックに例外を使用するのと同じです。同様に、下部に_afterTokenTransfer
あるものは、転送が成功した後に何をすべきかを示しています。
_balances
マッピングタイプであり、その定義はmapping(address => uint256) private _balances;
非常に明確で、内部的に見え、アドレスに応じてバランスをとることができます。したがって、次の2つの文は理解しやすいです。
uint256 senderBalance = _balances[sender];
...
_balances[recipient] += amount;
さて、次のことが非常に興味深いです。まず、作成者はrequire
、送信された量が転送された量を超えてはならないことを確認します。ケースは、転送が成功する可能性があると述べています。なぜunchecked
この領域を追加する必要があるのですか?
require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[sender] = senderBalance - amount;
}
ここでは、証明書がオーバーフローすることはないので、心理的な快適さの方が多いと思います。
要素がオーバーフローするか、追加せずにオーバーフローするunchecked
と、例外がスローされます。さらにunchecked
、たとえば、値をループさせます
0-1=>2^256-1
internal
memory
メモリが空にならないため、関数内で参照の受け渡しを効率的に行うことができます。したがって、再帰を使用してスマートコントラクトの配列の内容を処理することは完全に可能ですが、再帰によって占有されるスタックスロットは1024を超えることはできないことに注意する必要があります。これは、各再帰呼び出しが少なくとも1つのスタックスロットを使用するためです。EVM
スタックスロットの数を1024に制限します。
認可額を取得するための手当機能について
銀行のクレジットラインとして理解できると思います。ここexternal view
に1つありますが、上記のことは何も言えません。
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
主に実装を見てください:
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* Requirements:
*
* - `sender` and `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
* - the caller must have allowance for ``sender``'s tokens of at least
* `amount`.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) public virtual override returns (bool) {
_transfer(sender, recipient, amount);
uint256 currentAllowance = _allowances[sender][_msgSender()];
require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
unchecked {
_approve(sender, _msgSender(), currentAllowance - amount);
}
return true;
}
最初のステップは送金で、2番目の部分はクレジットラインが送金に十分かどうかをチェックします。十分でない場合は、例外を直接報告します。十分な場合は、クレジットラインを更新します。
ご覧のとおり、この関数はyesvirtual
です。関数を継承して更新できます。
承認機能について
以下はERC20
クレジットの機能であり、この機能は外部機能であり、言うまでもありません。
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
以下は、この関数の実装です
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(
address owner,
address spender,
uint256 amount
) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
とてもシンプルですが、この機能に問題があります。2つのクレジットが追加されます。最初のクレジットは大きく、2番目のクレジットは小さいです。それは問題があるということですか?
これまでのところ、ERC20のすべての機能は基本的に実装されており、以下に実装されている機能は、コインの送受信プロセス全体を補うためのものです。
increaseAllowance
そしてdecreaseAllowance
、量を増減するために使用され、これは補うことapprove
です。
_mint
_burn
健全な通貨価値を維持するために、指定されたアカウントにコインを発行して燃やすために使用されます。
_beforeTokenTransfer
これらの2つの_afterTokenTransfer
メソッドは、転送を含むすべてのトランザクションに表示されます。これは、Javaでのラッピングに相当します。これらの場所で、ログ関連またはセキュリティ関連の処理を実行できます。