LoginSignup
2
0

More than 5 years have passed since last update.

ApproveAndCall

Posted at

はじめに

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

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0