hardhat-ignition icon indicating copy to clipboard operation
hardhat-ignition copied to clipboard

Compute stuff using futures

Open kevincharm opened this issue 1 year ago • 6 comments

Apologies if this is covered somewhere in docs, but I couldn't find it. Is there a way to do more complex stuff with futures? e.g. I need to construct some function call using a future address:

const Rocket = buildModule('Rocket', (m) => {
    const rocketImpl = m.contract('Rocket', [])
    const factoryImpl = m.contract('RocketFactory', [])

    const factoryInitData = RocketFactory__factory.createInterface().encodeFunctionData('init', [
        m.readEventArgument(rocketImpl, 'Deployed', 'deployedAddress'),
    ]) // Obviously this fails

    const factoryProxy = m.contract('ERC1967Proxy', [factoryImpl, factoryInitData])

    return {
        factoryProxy,
    }
})

kevincharm avatar Mar 25 '24 00:03 kevincharm

You can't compute over the value of a future.

We do want to support complex use cases though, to help me understand, the intent here is to allow calldata to be formed from a future (i.e. the deployed address) and then passed into a subsequent call?

So maybe some API enhancements like:

  const deployedAddress = m.readEventArgument(
    rocketImpl,
    "Deployed",
    "deployedAddress"
  );

  const callData = m.encodeCallData([deployedAddress])

  m.call(myContract, "someFunction", [callData]);

kanej avatar Mar 26 '24 16:03 kanej

Yes, almost. I'd like to encode a function call, so it would be convenient to also be able to compute the function selector using the function signature - or something along those lines. I haven't looked into the futures internals properly yet, but is it possible to just expose a lower-level future builder that we could use to build custom stuff? Then common usecases could be added to the module builder api?

kevincharm avatar Mar 26 '24 23:03 kevincharm

const initCall = m.encodeFunctionCall('init(address)', deployedAddress)
// or
const initCall = m.encodeFunctionCall('init', ['address'], [deployedAddress])

or if the ABI is available in the module builder at some point, could even infer the function signature types?

kevincharm avatar Mar 26 '24 23:03 kevincharm

Hey, this is really interesting, @kevincharm! Can you tell us a bit more about the need to encode the function call? Are you using some sort of factory that deploys and inits in the same call? Or something like that? We'd like to understand what's the right capability to expose that would enable your usecase.

alcuadrado avatar Mar 27 '24 15:03 alcuadrado

Not exactly - I'm just trying to deploy and initialise a standard ERC1967 proxy (see second argument in constructor here: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/ERC1967/ERC1967Proxy.sol#L26). While the proxy's implementation points to a factory, it's irrelevant in this case.

kevincharm avatar Mar 27 '24 15:03 kevincharm

Having the same problem. I tried initialization in a separate call like this

    const rocketImpl = m.contract('Rocket', [])
    const factoryImpl = m.contract('RocketFactory', [])

    const factoryInitData = RocketFactory__factory.createInterface().encodeFunctionData('init', [
        m.readEventArgument(rocketImpl, 'Deployed', 'deployedAddress'),
    ]) // Obviously this fails

    const factoryProxy = m.contract('ERC1967Proxy', [factoryImpl])
    
    m.call(factoryProxy, 'init', [m.readEventArgument(rocketImpl, 'Deployed', 'deployedAddress')]

But this also doesn't work due to validation (method 'init' does not exist on a proxy contract)

kiseln avatar Apr 12 '24 07:04 kiseln