Я пытаюсь сделать простой тест в 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
. Но по какой-то причине мне не удается получить его экземпляр. Есть идеи, почему?
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);
});
хорошая вибрация
await VaultFactory.deploy()
наawait VaultFactory.new()
.хорошая вибрация
CreateVaultContract
функция не является константой (т.е. она изменяет состояние), поэтому вы не можете получитьcall
ее и получить возвращаемое значение напрямую (вам нужно сделатьsend
это как транзакцию и получить возвращаемое значение из события, которое эта функция испускает) .