ERC-20 – How to Exchange Between ERC20 Token and ETH Automatically

contract-designerc-20

I've created an ERC20 token. Now I want to use this token. Is it available to swap between ETH and the token automatically like the followings?

What I want:

  1. UserA send 1 ETH to the token owner's address.
  2. UserA receive 10 Token automatically.

To do that, should I deploy another contract?

I read the definition of ERC20 on this page. Is "approve" what I should use…?

What are the key criteria for meeting the ERC20 token standard?

I've used zeppelin-solidity to create the token.


Update 1

Thanks to travis-jacobs. I confirmed after I sent the created contract address 1 ETH then transfer the 1 ETH to transfer address.

Now, I'm trying to create call "getBalance" transaction.

deploy the contract

$ mkdir /tmp/zonotoken-infra
$ cd /tmp/zonotoken-infra
$ truffle unbox tutorialtoken
$ npm install ethereumjs-wallet bip39 web3-provider-engine@8.6.1 web3@0.18.4 zeppelin-solidity --save
$ vi ./truffle.js
$ vi ./contracts/ZonoToken.sol
$ vi ./migrations/2_deploy_contracts.js
$ truffle compile
$ truffle deploy —network ropsten

truffle.js

var bip39 = require("bip39");
var hdkey = require('ethereumjs-wallet/hdkey');
var ProviderEngine = require("web3-provider-engine");
var WalletSubprovider = require('web3-provider-engine/subproviders/wallet.js');
var Web3Subprovider = require("web3-provider-engine/subproviders/web3.js");
var Web3 = require("web3");
var FilterSubprovider = require('web3-provider-engine/subproviders/filters.js');

// Get our mnemonic and create an hdwallet
var mnemonic = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
var hdwallet = hdkey.fromMasterSeed(bip39.mnemonicToSeed(mnemonic));

// Get the first account using the standard hd path.
var wallet_hdpath = "m/44'/60'/0'/0/";
var wallet = hdwallet.derivePath(wallet_hdpath + "0").getWallet();
var address = "0x" + wallet.getAddress().toString("hex");

var providerUrl = "https://testnet.infura.io";
var engine = new ProviderEngine();
engine.addProvider(new FilterSubprovider());
engine.addProvider(new WalletSubprovider(wallet, {}));
engine.addProvider(new Web3Subprovider(new Web3.providers.HttpProvider(providerUrl)));
engine.start(); // Required by the provider engine.

module.exports = {
  networks: {
    "ropsten": {
      network_id: 3,    // Official ropsten network id
      provider: engine, // Use our custom provider
      from: address,    // Use the address we derived
      gas: 4600000
    }
  },
  rpc: {
    // Use the default host and port when not using ropsten
    host: "localhost",
    port: 8545
  }
};

contracts/ZonoToken.sol

pragma solidity ^0.4.4;
import 'zeppelin-solidity/contracts/token/StandardToken.sol';
import 'zeppelin-solidity/contracts/ownership/Ownable.sol';

contract ZonoToken is StandardToken {
  string public name = 'ZONO';
  string public symbol = 'ZONO';
  address public owner = 0x683821afb3f4f4fafffdb259254ae870a091e3b4;
  uint public decimals = 0;
  uint public INITIAL_SUPPLY = 100;

  function ZonoToken() {
    totalSupply = INITIAL_SUPPLY;
    balances[msg.sender] = INITIAL_SUPPLY;
  }

  function() payable { deposit(); }

  function deposit() payable {
    owner.transfer(msg.value);
    uint newTokens = (msg.value * 10) / 1 ether;
    balances[msg.sender] = balances[msg.sender] + newTokens;
  }
}

migrations/2_deploy_contracts.js

var ZonoToken = artifacts.require("./ZonoToken.sol");

module.exports = function(deployer) {
  deployer.deploy(ZonoToken);
};

send ETH to the contract address and transfer automatically

from:0x5dbb9793537515398a1176d365b636a5321d9e39
to:0x5a65585ce8213d1c9433e63d53d4e468540a5019 (Contract)
transfer to:0x683821afb3f4f4fafffdb259254ae870a091e3b4

https://ropsten.etherscan.io/tx/0xea44ce75c9c8a27df1a570b73bfda32f86444d715acb14c7cdb20401d8a49a9f


Update 2

Now, I'm trying to create call "getBalance" transaction.

I found the way.

npm install

npm install truffle-contract --save

src/js/app.js

var Web3 = require("web3");
var TruffleContract = require("truffle-contract");
var TutorialTokenArtifact = require("../../build/contracts/ZonoToken.json");

App = {
  web3Provider: null,
  contracts: {},

  init: function() {
    return App.initWeb3();
  },

  initWeb3: function() {
    if (typeof web3 !== 'undefined') {
      App.web3Provider = web3.currentProvider;
      web3 = new Web3(web3.currentProvider);
    } else {
      App.web3Provider = new Web3.providers.HttpProvider('https://testnet.infura.io');
      web3 = new Web3(App.web3Provider);
    }
    return App.initContract();
  },

  initContract: function() {
    App.contracts.TutorialToken = TruffleContract(TutorialTokenArtifact);
    App.contracts.TutorialToken.setProvider(App.web3Provider);
    return App.getBalances();
  },

  getBalances: function(adopters, account) {
    console.log('Getting balances...');
    var tutorialTokenInstance;
    web3.eth.getAccounts(function(error, accounts) {
      if (error) {
        console.log(error);
      }
      var account = "0x5dbb9793537515398a1176d365b636a5321d9e39";
      App.contracts.TutorialToken.deployed().then(function(instance) {
        tutorialTokenInstance = instance;
        return tutorialTokenInstance.balanceOf(account);
      }).then(function(result) {
        balance = result.c[0];
        console.log(balance);
      }).catch(function(err) {
        console.log(err.message);
      });
    });
  }

};

App.init();

execute

$ node src/js/app.js 
Getting balances...
10

I'm still not sure my contract is the best way to exchange between ETH and Token. I'm looking into it.

Best Answer

You could add a function similar to this, either in a new contract, or by extending the token contract you are using.

A contract implementing this might be:

import "token/BasicToken.sol";
import "ownership/Ownable.sol";  // link to these contracts below

contract ZonoToken is BasicToken { 

    function deposit() payable
    {
        owner.transfer(msg.value); // see Note 2
        uint newTokens = (msg.value * 10) / 1 ether;
        balances[msg.sender] = balances[msg.sender] + newTokens;
    }

    // fallback function
    function() payable { deposit(); }
}

Notes:

  1. The math of "1 ETH for 10 Tokens" depends on how many decimal places your token has. Above, I assumed that the tokens are indivisible (0 decimals), but with decimals places you would need to adjust the math on the line setting newTokens.
  2. Security consideration: we are assuming that owner is not a contract here. This link explains why we may want to use another pattern for sending ether around (e.g. the withdrawal pattern)

Links:

Related Topic