Fondo
RETN Deals ofrece cupones de descuento de marcas globales y, además, ofrece un generoso reembolso en Criptomoneda (RETN). La aplicación web rastrea automáticamente las compras de los consumidores en estas tiendas y las recompensa en criptografía. Una vez que el usuario alcanza el límite mínimo de retiro que se establece dentro de la aplicación web, se vuelve elegible para canjear RETN.
Buscando el mejor enfoque para manejar el proceso de redención.
Formas de acercamiento
Enfoque 1.
Cada vez que el usuario hace clic en Canjear, la aplicación web llama al método token.transfer y transfiere el token de la dirección de RETN Deal a la dirección ERC20 del usuario.
Beneficios
Problemas
Enfoque 2
Cree un contrato inteligente que el usuario pueda llamar (a través de metamask)
Beneficios
Rentable: El consumidor es el portador del precio del gas
Cola: el origen de la transacción será diferente para cada cliente y, por lo tanto, la transacción será rápida
Elección del cliente: El cliente puede elegir cuánto retirar y cuándo retirarlo. Él / ella también puede controlar el precio del gas.
Problemas
El límite de retiro debe establecerse cada vez en el contrato para que el usuario no pueda retirar más de lo que merece. Esto consume gasolina. Incluso si se establece una matriz de elementos, la cantidad de elementos en una matriz no puede ser superior a 180 (aprox.) debido al límite de gas de bloque/transacción, por lo tanto, se necesitará una mayor cantidad de transacciones para establecer el límite, lo que nuevamente consumirá el gas del remitente y las transacciones permanecen pendientes en la cola hasta que se extraen las transacciones de su antepasado.
Si el límite de retiro del usuario no está establecido, no podrá canjear
¿Cuál es un mejor enfoque para permitir que los consumidores canjeen RETN, que puede crear valor para el consumidor, mantener la seguridad del contrato, reducir el consumo de gas por RETN y no obstruir la cadena de bloques?
¿Oraclize debería ser de alguna ayuda para este tipo de problemas? Supongo que seguirá consumiendo gasolina.
Encontró que la mejor manera es hacer que el receptor pague por la transacción. Siguió a Steve Marx en este artículo para llegar al siguiente código. El flujo del código será como a continuación.
código de servidor de nodo
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);
código de contrato inteligente
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);
}
}
y finall en el lado html (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);
}
});