¿Cómo invocar una función de contrato desde el código y firmar la transacción?

Mi caso de uso es que tengo que invocar una función de contrato cuando se llama a una API.

Ahora he intentado varias formas, pero tengo problemas con ambas. El nombre de mi función es "transferir" .

Método - yo

En este método primero desbloqueo la cuenta del usuario. Luego invoque la función de contrato por contractInstance.methodName(). En el método de devolución de llamada, vuelvo a bloquear la cuenta.

Problema: el problema que veo aquí es que cuando recibo varias llamadas a mi API al mismo tiempo, causará un problema. Como una llamada puede bloquear la cuenta justo cuando un subproceso más está intentando invocar la función.

/ * Getting the contract Instance */

import Web3 from 'web3';
  const tokenABI = require('../ContractABI.json');

  if (typeof web3 !== 'undefined') {
    var web3 = new Web3(web3.currentProvider)
} else {
    var web3 = new Web3(new Web3.providers.HttpProvider('http://127.0.0.1:8545'))
}

var TokenContract = web3.eth.contract(tokenABI.abi);
var tokenInstance = TokenContract.at(contractConfig.id);

/ * Getting the contract Instance */

router.post('/transferTokens',async (req,res,next)=>{
    let senderAddress = req.param("sender");
    let receiverAddress = req.param("receiver");
    let transferAmount = req.param("amount");

    let searchCriteria = {
        address:senderAddress
    }

    try{
        // Getting the private key of the user from DB.

        let senderDetails = await MongoHelper.findOneByCriteria('accounts',searchCriteria);
        console.log('senderDetails:',senderDetails);

        let chk = web3.personal.unlockAccount(senderDetails.address, senderDetails.pwd, 95000);

        // Unlocked the account and then invoke the transfer function with the necessary parameters.

        tokenInstance.transfer(receiverAddress, transferAmount, { from: senderDetails.address })
        .then(function (msg) {
            res.data = msg;
            let chk = web3.personal.lockAccount(senderDetails.address);
            next();

        }).catch(function (e) {
            console.log('Error %%%%%%%%%%%%%%%%%',e);
            let chk = web3.personal.lockAccount(senderDetails.address);
            next(e)

        });

    }catch(err){
        console.log(' Error err:',err);
        next(err);
    }

});

Método - II

**En este método, obtengo los datos de llamada para mi función de contrato. Estoy creando un objeto de transacción sin procesar. Lo estoy firmando con la clave privada del usuario. Luego estoy usando web3.eth.sendRawTransaction para enviar la transacción **

Problema: El problema al que me enfrento es: Veo que se crea una transacción. Pero no está haciendo lo que debería hacer idealmente. Es solo una transacción ficticia. El método de contrato no se invoca. Supongo que los datos de la llamada no son correctos o algo así.

Veo los datos de llamada algo similar a lo siguiente: 0xb7184b680000000000000000000000000000000000000000000000000000000000000578

router.post('/transferTokens2',async (req,res,next)=>{
    let senderAddress = req.param("sender");
    let receiverAddress = req.param("receiver");
    let transferAmount = req.param("amount");

    let searchCriteria = {
        address:senderAddress
    }

    try{
        let senderDetails = await MongoHelper.findOneByCriteria('accounts',searchCriteria);
        let callData = tokenInstance.transfer.getData(receiverAddress,transferAmount);

        let rawTx = {
            from:senderAddress,
            nonce: 93,
            gasPrice: '0x4a817c800', // eth_estimateGas rpc result
            gasLimit: '0x2fd618',
            to:receiverAddress,
            data:callData 
        }
        var privateKey = new Buffer(senderDetails.key, 'hex')

        var tx = new Tx(rawTx);
        tx.sign(privateKey);

        var serializedTx = tx.serialize();

        web3.eth.sendRawTransaction('0x'+ serializedTx.toString('hex'), function(err, hash) {
            if (!err) {
                console.log('$$$$$:',hash); 
                res.data = hash;
                next();
            }else{
                next(err);
            }   
        });

    }catch(err){
        next(err);
    }

});

Respuestas (1)

Para el método 1) debe serializar el acceso al desbloqueo/bloqueo de la cuenta. Puede usar el semáforo para limitar a una sola operación en cualquier momento.

Para el método 2) ha codificado el nonce. Tienes que calcular antes de cada transacción var nonce = web3.eth.getTransactionCount(<address>). Ojo que un nonce son números consecutivos sin repeticiones. También debe garantizar que cada nonce se use solo una vez.