Ошибка теста Truffle: невозможно создать экземпляр Vault; нет кода по адресу

Я пытаюсь сделать простой тест в Truffle, используя Ganache, где я вызываю функцию из экземпляра смарт-контракта. Этот компонент создаст Хранилище и вернет мне его адрес.

Мой трюфельный тест делает следующее:

const VaultFactory = artifacts.require("./VaultFactory");
const Vault = artifacts.require("./Vault");

contract('VaultFactory', async () => {
    it("Should create a new Vault", async () => {
        let VaultFactoryInstance = await VaultFactory.deploy();
        let VaultAddress = await VaultFactoryInstance.CreateVaultContract.call();
        let VaultInstance = await Vault.at(VaultAddress);
    });
});

Код VaultFactory.sol, создающий Vault, делает следующее (конструктор Vault пуст):

function CreateVaultContract ()
    public
    returns(address)
{
    Vault newVault = new Vault();
    emit VaultCreation(msg.sender, newVault, VaultState.Created);
    return address(newVault);
}

То VaultAdress, что я получаю, не пусто, однако, когда я пытаюсь получить VaultInstance, это дает мне следующую ошибку:

Uncaught Error: Cannot create instance of Vault; no code at address 0xed41bb9a56dad9b9d0901400f0a8de72d3cf1854

Я попытался сделать, web3.eth.getCode(VaultAddress);чтобы увидеть, есть ли у меня какой-либо код, но он возвращает мне 0x0.

Итак, мой VaultFactoryконтракт, кажется, развернут и выглядит так, как будто он создал файл Vault. Но по какой-то причине мне не удается получить его экземпляр. Есть идеи, почему?

Попробуйте заменить await VaultFactory.deploy()на await VaultFactory.new().
Кроме того, CreateVaultContractфункция не является константой (т.е. она изменяет состояние), поэтому вы не можете получить callее и получить возвращаемое значение напрямую (вам нужно сделать sendэто как транзакцию и получить возвращаемое значение из события, которое эта функция испускает) .

Ответы (3)

let VaultAddress = await VaultFactoryInstance.CreateVaultContract.call();

должно быть:

let VaultAddress = await VaultFactoryInstance.CreateVaultContract();

Наконец-то мне удалось получить рабочее решение (хотя я не уверен, что оно лучшее).

const VaultFactory = artifacts.require("./VaultFactory");
const Vault = artifacts.require("./Vault");

contract('VaultFactory', async () => {
    it("Should create a new Vault", async () => {
        let VaultFactoryInstance = await VaultFactory.deploy();
        let VaultReceipt = await VaultFactoryInstance.CreateVaultContract();

        //This returns the address of the newly created Vault
        let VaultAddress = VaultReceipt.logs[0].address;

        let VaultInstance = await Vault.at(VaultAddress);
    });
});

Таким образом, я могу получить адрес своего хранилища и получить его экземпляр. Затем я попытался вызвать функцию, и она отлично работает!

у меня было две проблемы

Использование .call() не сохраняет данные

Factory factory = await Factory.deployed()
factory.createChild.call()

Это не работает, потому что вызов вернет код, который будет работать в цепочке, но на самом деле ничего не сохранит в цепочке. Как будто вы вообще никогда не вызывали функцию.

Динамические массивы не нуждаются в инициализации

constructor() {
   children = new Child[](1)
}

Не делай этого. Если вы это сделаете, он инициирует 1 элемент как нулевой.
Если вы затем вызовете factory.children.call(0)этот элемент, будет 0x00000...
Очевидно, что не будет никакого кода с нулевым значением.

Работающий

Солидность

contract Factory {
  event CreatedChild(address child);
  Child[] public children;

  function createChild() {
    Child child = new Child();
    children.push(child);
    emit CreatedChild(address(child);
  }
}

contract Child {}

Трюфель

 it("should deploy child", async () => {
  const factory = await Factory.deployed();
  const tx = await factory.createChild();

  const childAddress = tx.logs[0].address;
  const firstChild = await factory.children.call(0);

  // console.log("tx return", childAddress);
  // console.log("array call", firstChild);

  assert.ok(childAddress !== 0);
  assert.equal(childAddress, firstChild);
});