Cannot access functions of VestingWallet from a derived contract

Hi there, I am trying to create a derived version of the VestingWallet, however I am running into "TypeError: xxx is not a function" errors when running my test case in hardhat.

This is my derived VestingWallet contract:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/finance/VestingWallet.sol";
import "hardhat/console.sol";

contract MyVestingWallet is VestingWallet {

    constructor(
        address beneficiaryAddress,
        uint64 startTimestamp,
        uint64 durationSeconds
    ) VestingWallet(beneficiaryAddress, startTimestamp, durationSeconds) {}

    // overriding the underlying method in VestingWallet here
    function _vestingSchedule(uint256 totalAllocation, uint64 timestamp)
        internal
        view
        override
        returns (uint256)
    {
        console.log("Calling override _vestingSchedule() method...");

        if (timestamp < start()) {
            return 0;
        } else if (timestamp > start() + duration()) {
            return totalAllocation;
        } else {
            return (totalAllocation * (timestamp - start())) / duration();
        }
    }
}

And the following is the code for my test case:


const { expect } = require("chai");

const { ethers } = require("hardhat");

describe("Token vesting contract", function () {

    let Token;
    let Vesting;
    let tokenContract;
    let owner;
    let addr1;

    beforeEach(async function () {

        Token = await ethers.getContractFactory("MyTestToken");
        Vesting = await ethers.getContractFactory("MyVestingWallet");

        tokenContract = await Token.deploy();

        [owner, addr1] = await ethers.getSigners();
    });

    describe("Vesting", function () {

        it("Should allow a beneficiary to receive the vested amount", async function () {

            const blockNumBefore = await ethers.provider.getBlockNumber();
            const blockBefore = await ethers.provider.getBlock(blockNumBefore);
            const timestampBefore = blockBefore.timestamp;

            const beneficiary = addr1.address;
            const fullyVestedAmount = 50000;
            const startDate = timestampBefore + (60 * 60);
            const cliffSec = 0;
            const durationSec = 60000;

            let vestingContract = await Vesting.deploy(beneficiary, startDate, durationSec);

            // fund the contract with the vested amount

            await expect(tokenContract.transfer(vestingContract.address, fullyVestedAmount))
                .to.emit(tokenContract, 'Transfer')
                .withArgs(owner.address, vestingContract.address, fullyVestedAmount);

            // advance time to end of vesting duration
            await network.provider.send("evm_setNextBlockTimestamp", [startDate + cliffSec + durationSec])
            await ethers.provider.send('evm_mine');

            console.log("beneficiary: ", await vestingContract.beneficiary());                  // prints out fine
            console.log("released: ", await vestingContract.released(tokenContract.address));   // gives "TypeError: vestingContract.released is not a function"

            await vestingContract.connect(addr1).release(tokenContract.address);    // gives "TypeError: vestingContract.connect(...).release is not a function"

            beneficiaryBalance = await tokenContract.balanceOf(beneficiary);
            expect(beneficiaryBalance).to.equal(fullyVestedAmount);
        });
    });
});

What I do not understand here is that why calling start(), duration() and beneficiary() methods are fine in this case but calling released() and release() gives an error, even though they are all of the same function visibility(i.e. public)?

Its because VestingWallet has two functions named released. One that takes no arguments and one that takes a single argument. The contract handle doesn't disambiguate automatically for you, so you have to tell ethers explicitly which one you want, by specifying the name of the function you are calling and its argument list, like this:

await vestingContract["released(address)"](tokenContract.address)
await vestingContract.connect(addr1)["release(address)"](tokenContract.address); 
2 Likes

You are such a lifesaver sir, that had not come across my mind at all so thanks a lot for that!

For whoever facing a similar issue, I would also like to highlight that in the event that there is more than one input argument there shouldn't be any spaces between the specified arguments.

await vestingContract["vestedAmount(address, uint64)"](tokenContract.address, startDate)    // gives TypeError: vestingContract.vestedAmount(address, uint64) is not a function
await vestingContract["vestedAmount(address,uint64)"](tokenContract.address, startDate)    // works
1 Like