Was ist die beste Art der Token-Einlösung durch den Benutzer?

Hintergrund

RETN Deals bietet Rabattgutscheine von globalen Marken und bietet darüber hinaus großzügige Cashbacks in Kryptowährung (RETN). Die Webanwendung verfolgt automatisch die Einkäufe der Verbraucher in diesen Geschäften und belohnt sie in Krypto. Sobald der Benutzer das in der Web-App festgelegte Mindestlimit für Auszahlungen erreicht, ist er berechtigt, RETN einzulösen.

Auf der Suche nach dem besten Ansatz für den Rücknahmeprozess.

Wege der Annäherung

Ansatz 1.

Immer wenn der Benutzer auf „Einlösen“ klickt, ruft die Web-App die token.transfer-Methode auf und überträgt das Token von der Adresse von RETN Deal an die ERC20-Adresse des Benutzers.

Vorteile

  • Die Validierung erfolgt auf der Serverseite, der Benutzer kann nicht mehr Wert manipulieren, als er berechtigt ist.

Probleme

  • Gaspreis: Der Besitzer der Web-App muss den Gaspreis für jede einzelne Einlösung tragen. Die Kosten für die Übertragung der Token an die Endbenutzer sind sehr hoch
  • Warteschlange: Die Transaktionen jedes Benutzers werden blockiert, bis die vorherigen Transaktionen gelöscht werden. Dies liegt daran, dass die NONCE für jede Transaktion inkrementiert wird, da sie von derselben Adresse stammen.

Ansatz 2

Erstellen Sie einen Smart Contract, der (über Metamaske) vom Benutzer aufgerufen werden kann

Vorteile

  • Kostengünstig: Der Verbraucher ist Träger des Gaspreises

  • Warteschlange: Die Transaktionsentstehung ist für jeden Kunden unterschiedlich und daher ist die Transaktion schnell

  • Wahl des Kunden: Der Kunde kann wählen, wie viel er abheben möchte und wann er abheben möchte. Er/sie kann auch den Gaspreis kontrollieren.

Probleme

  • Das Auszahlungslimit muss jedes Mal im Vertrag festgelegt werden, damit der Benutzer nicht mehr abheben kann, als er verdient. Das verbraucht Benzin. Selbst wenn ein Array von Elementen festgelegt ist, kann die Anzahl der Elemente in einem Array aufgrund des Block-/Transaktionsgaslimits nicht mehr als 180 (ungefähr) betragen, sodass eine größere Anzahl von Transaktionen erforderlich ist, um das Limit selbst festzulegen, das wiederum verbraucht wird Das Gas vom Absender und die Transaktionen bleiben in der Warteschlange, bis die Vorfahrentransaktionen abgebaut werden.

  • Wenn das Auszahlungslimit des Benutzers nicht festgelegt ist, kann er nicht einlösen

  • Wenn wir den Benutzern die Einlösung erlauben, ohne das Limit festzulegen, kann der Benutzer mehr Wert abheben, als er/sie berechtigt ist.

Was ist ein besserer Ansatz, um es den Verbrauchern zu ermöglichen, RETN einzulösen, was einen Mehrwert für den Verbraucher schaffen, die Vertragssicherheit aufrechterhalten, den Gasverbrauch durch RETN reduzieren und die Blockchain nicht verstopfen kann?

Sollte oraclize bei dieser Art von Problemen hilfreich sein? Denke das wird noch Sprit verbrauchen.

Antworten (1)

Der beste Weg ist, den Empfänger für die Transaktion bezahlen zu lassen. Folgen Sie Steve Marx in diesem Artikel , um den folgenden Code zu erhalten. Der Fluss des Codes wird wie unten sein.

  • Benutzer aktualisieren ihre bevorzugte Wallet-Adresse in der App
  • Benutzer stellen sicher, dass sie ETH für Gas und die in Metamask geladene Brieftaschenadresse haben
  • Berechnen Sie auf der Serverseite, wie viele Token sie erhalten sollen, und signieren Sie dann die Transaktion

Node-Server-Code

const recipient = receipientaddress;
const amount =  web3.toWei(tokensToSend);
const nonce = req.body.nonce; //calucated nonce of the wallet address (of metamask)
const tmpContractAddress = "0x...";//contract address that will send the token (address of the deployed contract below) 
var hash = "0x" + ethereumjs.soliditySHA3(
    ["address", "uint256", "uint256", "address"],
    [recipient,amount, nonce, tmpContractAddress]
  ).toString("hex");
var signed = web3.personal.sign(hash, senderAddr, senderAddrPass);

intelligenter Vertragscode

pragma solidity ^0.4.20;

/**
 * @title Ownable
 * @dev The Ownable contract has an owner address, and provides basic authorization control
 * functions, this simplifies the implementation of "user permissions".
 */
contract Ownable {

  address public owner;
  event OwnershipTransferred (address indexed _from, address indexed _to);

  /**
   * @dev The Ownable constructor sets the original `owner` of the contract to the sender
   * account.
   */
  function Ownable() public{
    owner = msg.sender;
    emit OwnershipTransferred(address(0), owner);
  }

  /**
   * @dev Throws if called by any account other than the owner.
   */
  modifier onlyOwner() {
    require(msg.sender == owner);
    _;
  }

  /**
   * @dev Allows the current owner to transfer control of the contract to a newOwner.
   * @param newOwner The address to transfer ownership to.
   */
  function transferOwnership(address newOwner) public onlyOwner {
    require(newOwner != address(0));
    owner = newOwner;
    emit OwnershipTransferred(owner,newOwner);
  }
}

/**
 * @title Token
 * @dev API interface for interacting with the Token contract 
 */
interface Token {
  function transfer(address _to, uint256 _value) external returns (bool);
  function balanceOf(address _owner) constant external returns (uint256 balance);
}

contract RetnWithdraw10 is Ownable{
    address owner;
    Token token;
    mapping(address => mapping(uint256 => bool)) usedNonces;
    event Redeemed(address indexed beneficiary, uint256 value);

    function RetnWithdraw10() public {
        address _tokenAddr = 0x815CfC2701C1d072F2fb7E8bDBe692dEEefFfe41;//0x2ADd07C4d319a1211Ed6362D8D0fBE5EF56b65F6; 
      // test token 0x815CfC2701C1d072F2fb7E8bDBe692dEEefFfe41;
      token = Token(_tokenAddr);
        owner = msg.sender;
    }

    function claimPayment(uint256 amount, uint256 nonce, bytes sig) public {


        require (token.balanceOf(this) >= amount);
        require (!usedNonces[msg.sender][nonce]);

        // This recreates the message that was signed on the client.
        bytes32 message = prefixed(keccak256(msg.sender, amount, nonce, this));

        require (recoverSigner(message, sig) == owner);


        usedNonces[msg.sender][nonce] = true;

        //msg.sender.transfer(amount);
        if(token.transfer(msg.sender,amount)){
           emit Redeemed(msg.sender,amount);
      }
      else
      usedNonces[msg.sender][nonce] = false;
    }

    // Destroy contract and reclaim leftover funds.
    function kill() public onlyOwner{
        uint256 remaining = token.balanceOf(this);
        if(remaining>0)
            token.transfer(owner,remaining);
        selfdestruct(msg.sender);
    }


    // Signature methods

    function splitSignature(bytes sig)
        internal
        pure
        returns (uint8, bytes32, bytes32)
    {
        require(sig.length == 65);

        bytes32 r;
        bytes32 s;
        uint8 v;

        assembly {
            // first 32 bytes, after the length prefix
            r := mload(add(sig, 32))
            // second 32 bytes
            s := mload(add(sig, 64))
            // final byte (first byte of the next 32 bytes)
            v := byte(0, mload(add(sig, 96)))
        }

        return (v, r, s);
    }

    function recoverSigner(bytes32 message, bytes sig)
        internal
        pure
        returns (address)
    {
        uint8 v;
        bytes32 r;
        bytes32 s;

        (v, r, s) = splitSignature(sig);

        return ecrecover(message, v, r, s);
    }

    // Builds a prefixed hash to mimic the behavior of eth_sign.
    function prefixed(bytes32 hash) internal pure returns (bytes32) {
        return keccak256("\x19Ethereum Signed Message:\n32", hash);
    }
}

und finall auf der HTML-Seite (Front-End).

tokensToRedeem = 2; //should be same as what is passed to the server
        var abi = [ { "constant": false, "inputs": [ { "name": "amount", "type": "uint256" }, { "name": "nonce", "type": "uint256" }, { "name":  "sig", "type": "bytes" } ], "name": "claimPayment", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" } ];
        var MyContract = web3.eth.contract(abi);
        var contrctAddress = "0x...";//deployed contract address
        var myContractInstance = MyContract.at(contrctAddress);
        web3.eth.getAccounts(function(error, result) { 
            if(!error)
            {
                if(result.length == 0)
                {
                    toastr.error('Please ensure you logged in metamask.');
                    return false; 
                }
                myContractInstance.claimPayment(web3.toWei(tokensToRedeem),2,hash,{ from:result[0],
                            to:contrctAddress,
                            value:  0, 
                        }, function(err, transactionHash) {
                            if (!err)
                            {
                            $('#btnMetaMask').css('display','none');
                                $('#notesMeta').html("Transaction Submitted. Track <a target='_blank' href='<?php echo $etherscanurl;?>/tx/"+transactionHash +"'>here</a>");
                                console.log(transactionHash); 
                            }
                            else
                            {
                                console.log('Error While Redeem');
                                console.log(error);
                            }; 
                    }) 
                } 
                else
                { console.log('error');
                    console.log(err);
                }
            });