はじめに
Ethereumの支払い処理は、payableを使えば良いだけだが、意外と困るのが、
自作のtokenを支払い手段にする場合に、コントラクトが受け取ってから、アイテムなどの排出までの一連の流れを制御する時。
社内の勉強会用に自作tokenを自作サービスの通貨として使うような話をしたので覚書。
ETHの場合は、payable。自作tokenの場合はどうする?
ETHをやり取りする場合、payableという修飾子が用意されていて、
これを使うと下記のような形で、やり取りできる。
重要なのは、購入した時に、payloadをひっつけて、商品IDなどの情報を指定できることが必要条件。
(つまりただ送金するだけでは不十分)
function storeData(uint256 payload) public payable {
require(msg.value == 1 ether);
info[msg.sender] = payload;
}
自作tokenの場合は、下記のような方法で書きたくなるが、不可能。
function storeData(uint256 payload) public payable {
require(msg.value == 1 HOGECOIN);
info[msg.sender] = payload;
}
どのようにするかというと、
approve + trasactionFrom または transactionのどちらかを使う方法。
例えば購入処理などで使う場合は、サービス側のコントラクトから、
coinを呼び出して
service.coin.approve()
service.coin.transferFrom()
とやりたくなるが、セキュリティ上これはできない。
そのため、coin側からapproveAndCallという形で
coin.approve()
coin.service.transerfer()
という形にcallする必要がある。
pragma solidity ^ 0.4 .24;
//version:0.4.24+commit.e67f0147.Emscripten.clang
import "./ERC20.sol";
import "./ERC20Detailed.sol";
import "./ERC20Mintable.sol";
import "./Strings.sol";
/**
* @title PlanetCoin
*/
interface tokenRecipient {
function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) public;
}
contract PlanetCoin is ERC20, ERC20Mintable, ERC20Detailed {
string private _name = "PlanetCoin";
string private _symbol = "PTC";
uint8 private _decimals = 0;
address account = msg.sender;
uint value = 1;
constructor() ERC20Detailed(_name, _symbol, _decimals) public {
_mint(account, value);
}
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;
}
}
}
contract ApproveAndCall {
function receiveApproval(address _from, uint256 _amount, address _tokenContract, bytes _data) public returns(bool);
}
contract CoinTransferDemo {
address public owner;
PlanetCoin public planetCoinContract;
mapping(address => uint) public addressToPlanetCoinDeposit;
constructor() CoinTransferDemo() payable public {
owner = msg.sender;
}
mapping(address => uint256) public addressToCoinamount;
function sendMint() public {
planetCoinContract.mint(msg.sender, 100);
addressToPlanetCoinDeposit[msg.sender] += 100;
}
function setPlanetCoinContract(address _contractAddress) public {
planetCoinContract = PlanetCoin(_contractAddress);
}
function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) public {
//Token t = Token(_token);
//require(t.transferFrom(_from, this, _value));
addressToCoinamount[_from] = _value;
//receivedTokens(_from, _value, _token, _extraData);
}
event receivedTokens(address _from, uint256 _value, address _token, bytes _extraData);
}
(参考)
Ethereum smart service payment with tokens
https://medium.com/@jgm.orinoco/ethereum-smart-service-payment-with-tokens-60894a79f75c
Paying for Services with ERC20 Tokens
https://medium.com/official-amulet/paying-for-services-with-erc20-tokens-6c4313114128
function approveAndCall error
https://ethereum.stackexchange.com/questions/52712/function-approveandcall-error