Solidity – TransferFrom Not Working in Contract Development

contract-developmentsoliditytruffle

I'm trying to create a simple airDrop contract. But whatever I do nothing seems to work.

I based my custom contract and token of the zeppelin-solidity contracts.

This is the code I'm using to airdrop funds:

function distribute(
    uint _airDropID
) public
{
    for (uint i = 0; i < airDrops[_airDropID].participantCount; i++) {
        TokenDrop(msg.sender, airDrops[_airDropID].participants[i], airDrops[_airDropID].amount);

        ERC20(token).transfer(airDrops[_airDropID].participants[i], airDrops[_airDropID].amount);

        //token.mint(airDrops[_airDropID].participants[i], airDrops[_airDropID].amount);
    }
}

I also tried to mint the funds but both functions result in a VM Exception while processing transaction: revert

Which tells me a require() is failing. I tried further debugging my code and it seemed that the msg.sender address changed during the transfer() or mint() function.

This is the test I'm currently executing.

let AirDrop = artifacts.require("./AirDrop.sol");
let GameToken = artifacts.require("./GameToken.sol");

//TestData
let gameTokenMarketCap = 1*1000*1000*1000;
let amountToMint = 1*1000*1000;
let amountToApprove = 1*1000*1000*1000*1000;
let amountToDrop = 1000;
let airDropParticipants = 2;

//Expected Test Results
let expectedAirDropID = 0;

contract('Airdrop', (accounts) => {
    let tokenContract;
    let airDropContract;
    let participantID;
    let dropper = accounts[0];
    let receiver = accounts[1];
    let receiver2 = accounts[2];

    /**
     * Initial setup. 
     * Mint tokens to dropper address and check it.
     */
    before(() => {
        return GameToken.new(gameTokenMarketCap, dropper, {from: dropper}).then((instance) => {
            tokenContract = instance;

            return AirDrop.new(tokenContract.address, dropper, {from: dropper});
        }).then((instance) => {
            airDropContract = instance;

            return tokenContract.mint(dropper, amountToMint).then(() => {
                return tokenContract.balanceOf.call(dropper);
            }).then((balance) => {
                console.log(balance.valueOf());
                assert.equal(balance.valueOf(), amountToMint, "Token dropper balance should equal minted balance!");
            });
        });
    });

    describe('Creation and Execution', () => {
        let airDropID;

        it('Allows creating an airDrop and increments the ID', async () => {
            //Firstly test with .call
            airDropContract.createAirDrop.call(airDropParticipants, amountToDrop).then((airDrop) => {
                airDropID = airDrop;

                assert.equal(airDropID.valueOf(), expectedAirDropID, "AirDrop ID not correctly created!");
            });

            //Execute on blockchain
            await airDropContract.createAirDrop(airDropParticipants, amountToDrop);

            return true;
        });

        //Add an participant to the created airDrop
        it('Allows adding a participant to the AirDrop', () => {
            airDropContract.addParticipantToAirDrop.call(airDropID, receiver).then(participantID => {
                assert.equal(participantID.valueOf(), expectedAirDropID, "Participant ID not correctly created!");
            });

            airDropContract.addParticipantToAirDrop(airDropID, receiver);
            airDropContract.addParticipantToAirDrop(airDropID, receiver2);

            return true;
        });

        it('Allows distributing an airDrop and sends the right amount of credits to both participants', async () => {
            //Distribute and check the airDrop
            return airDropContract.distribute(airDropID, {from: dropper}).then(success => {
                return tokenContract.balanceOf.call(receiver);
            }).then(balance => {
                assert.equal(balance.valueOf(), amountToDrop, "airDrop was not executed correctly.");

                return tokenContract.balanceOf.call(receiver2);
            }).then((balance) => {
                assert.equal(balance.valueOf(), amountToDrop, "airDrop was not executed correctly.");

                return tokenContract.balanceOf.call(dropper);
            }).then((balance) => {
                assert.equal(balance.valueOf(), amountToMint - amountToDrop * 2);

                return true;
            });
        });
    });
});

This is the failing function: airDropContract.distribute(airDropID, {from: dropper})

Does anyone know why the value of msg.sender is changing and why the airdrop isn't working?

Thanks in advance.

Best Answer

Solved, I had to approve the contract to send tokens on my behalf.

tokenContract.approve(airDropContract.address, amountToApprove);

Related Topic