//tips
//smart contract
Dexコントラクトを作成し、truffle compileしてコンパイルファイルを生成。
//SPDX-License-Identifier:MIT
pragma solidity 0.8.4;
import"./ERC20.sol";
contract Dex{
event buy(address account,address _tokenAddr,uint256 _cost,uint256 _amount);
event sell(address account,address _tokenAddr,uint256 _cost,uint256 _amount);
mapping(address=>bool)public supportedTokenAddr;
modifier supportsToken(address _tokenAddr){
require(supportedTokenAddr[_tokenAddr]==true,"this token is not supported");
_;
}
//対応可能トークンを定義
constructor(address[] memory _tokenAddr){
for(uint i=0;i<_tokenAddr.length;i++){
supportedTokenAddr[_tokenAddr[i]]=true;
}
}
//userのethを受け取り、他のトークンを返す
//ethを受け取るのでpayable
//ERC20を通じてcontructorで生成される各トークンにオリジナルのアドレスが設けられることになるので_tokenAddr
function buyToken(address _tokenAddr,uint256 _cost,uint256 _amount)external payable supportsToken(_tokenAddr){
//この時点ではERC20contructorでトークンの生成は完了している
//コントラクト内のアカウント取得。要確認。
ERC20 token=ERC20(_tokenAddr);
//この際のmsg.valueとは?
//msg.value is a member of the msg (message) object when sending (state transitioning) transactions on the Ethereum network.
//msg.value contains the amount of wei (ether / 1e18) sent in the transaction.
//トランザクション発生時の送付値。これはユーザーベース
require(msg.value == _cost,"insuficient fund");
require(token.balanceOf(address(this))>=_amount,"Token sold out");
//tokenを購入者へ送付
token.transfer(msg.sender,_amount);
emit buy(msg.sender,_tokenAddr,_cost,_amount);
}
function sellToken(address _tokenAddr,uint256 _cost,uint256 _amount)external supportsToken(_tokenAddr){
ERC20 token=ERC20(_tokenAddr);
//売り手が売り分を保有しているかの確認
require(token.balanceOf(address(msg.sender))>=_cost,"insufficient token balance");
require(address(this).balance >= _amount,"dex does not have enough funds");
//tokenを売主からdexへ送付。ERCには戻す必要がないため
token.transferFrom(msg.sender,address(this),_cost);
//call関数を呼ぶ
//これで上記のトランザクションをチェックできる
(bool success,)=payable(msg.sender).call{value:_amount}("");
require(success,"eth transfer failed");
emit sell(msg.sender,_tokenAddr,_cost,_amount);
}
}
ERC20とDexコントラクトを作成する中で、理解の辻褄が合わなくなってきたので整理。
ERC20コントラクトは複数トークンの生成と送付、各ユーザーの残高記録を行う部分で、各ユーザーのwalletそのものではない点に注意。
Dexを通して、各ユーザーが通貨の取引を行う際には、DexがERC20の保有残高などを確認しつつ、ユーザーのトランザクションを実行する。また、ERC20とのパスが必要なのでimportにより接続している。
この接続の時点ではすでにERCはチェーン状にデプロイされている状態なので、
ERC20 token=ERC20(_tokenAddr);
で取得するのはweb3接続しなくても大丈夫なのかは疑問。truffleだから大丈夫なのか。
また、transferfromで第三者間の送金メソッドを実行した際の検証を
(bool success,)=payable(msg.sender).call{value:_amount}("");
で行っていたが、このcall関数の仕組みがいまいちわからなかったので確認。
これを読んでいくと、やはりtruffleとweb3接続とでは若干やり方が違うよう。違うという点を認識しておく。
The regular way to interact with other contracts is to call a function on a contract object (x.f()).
All three functions call, delegatecall and staticcall are very low-level functions and should only be used as a last resort as they break the type-safety of Solidity.
ただ、これは今では送金の際にgasを記載する影響で.sendから.call{}()の形で記述が一般的に行われる形に移ってきているので注意。call()にも
All contracts can be converted to address type, so it is possible to query the balance of the current contract using address(this).balance.
(bool success,)=payable(msg.sender).call{value:_amount}("");
require(success,"eth transfer failed");
ここの箇所の由来は、
(bool success, bytes memory data) = callee.call(abi.encodeWithSignature("add(uint256,uint256)", _x, _y));
require(success);
にあり、calleeは別のコントラクトのaddressなので、そこのバイトコードの中身をabi.encodeWithSignatureで呼び出している。
実際に呼び出しだけでなく、送金もできるものなので注意が必要。
the abi object that contains encodeWithSignature() method is he standard way to interact with contracts in the Ethereum ecosystem.
When it is a function in the called contract, that function is called when the abi.encodeWithSignature() method is called. So in the code example in the question, the called contract has a function:function foo(string var1, uint256 var2){} that is called every time abi.encodeWithSignature("foo(string,uint256)", "call foo", 123) method is called in the caller contract.
また,callの後に(””)をつけるのは必ずstringを返さなければいけないからとのこと。
.call() needs 1 string argument, the function being called. The empty string is the way to call the fallback function. I
参考になる例が下記にあったので一部抜粋。
contract SendToFallback {
function transferToFallback(address payable _to) public payable {
_to.transfer(msg.value);
}
function callFallback(address payable _to) public payable {
(bool sent, ) = _to.call{value: msg.value}("");
require(sent, "Failed to send Ether");
}
}
このようにtransferがきちんと実行されているかを確認できる。