Hi, has anyone successfully simulated a DELEGATE CALL operation with Hardhat Mainnet forking in tests? I'm doing this so reduce the total number of transactions I need to do from the governance contract (basically like a multisend). I'm failing and I don't really understand why. I'm mostly following along with docs for Gnosis SDK from here. My code looks like this...
const tx = {
to: migrator.address,
value: "0",
data: ethersMigrator.interface.encodeFunctionData("upgradeImplementations", [config, newImplementations]),
}
await delegateSafeTransaction(tx)
async function delegateSafeTransaction(tx) {
const safeAddress = MAINNET_MULTISIG
const blakesGovAddress = "0xf13eFa505444D09E176d83A4dfd50d10E399cFd5"
const mikesGovAddress = "0x5E7b1B5d5B03558BA57410e5dc4DbFCA71C92B84"
await impersonateAccount(hre, blakesGovAddress)
await impersonateAccount(hre, mikesGovAddress)
let ownerSigner = await ethers.getSigner(blakesGovAddress)
let ownerSigner2 = await ethers.getSigner(mikesGovAddress)
const ethAdapterOwner1 = new EthersAdapter({ethers, signer: ownerSigner})
const ethAdapterOwner2 = new EthersAdapter({ethers, signer: ownerSigner2})
const safeSdk = await Safe.create({ethAdapter: ethAdapterOwner1, safeAddress})
const owner2 = await safeSdk.connect({ethAdapter: ethAdapterOwner2, safeAddress})
tx.operation = 1
const transactions = [tx]
const safeTx = await safeSdk.createTransaction(...transactions)
console.log("safe tx is:", safeTx)
const txHash = await owner2.getTransactionHash(safeTx)
const approveTx = await owner2.approveTransactionHash(txHash)
await approveTx.transactionResponse.wait()
const executeTxResponse = await safeSdk.executeTransaction(safeTx)
return await executeTxResponse.transactionResponse.wait()
}
The relevant code from the migrator contract...
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import "../core/BaseUpgradeablePausable.sol";
import "../core/ConfigHelper.sol";
import "../core/CreditLine.sol";
import "../core/GoldfinchConfig.sol";
import "../../interfaces/IBase.sol";
import "hardhat/console.sol";
contract V2Migrator is BaseUpgradeablePausable {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant GO_LISTER_ROLE = keccak256("GO_LISTER_ROLE");
using SafeMath for uint256;
GoldfinchConfig public config;
using ConfigHelper for GoldfinchConfig;
mapping(address => address) public borrowerContracts;
function initialize(address owner, address _config) external initializer {
require(owner != address(0) && _config != address(0), "Owner and config addresses cannot be empty");
__BaseUpgradeablePausable__init(owner);
config = GoldfinchConfig(_config);
}
function upgradeImplementations(GoldfinchConfig _config, address[] calldata newDeployments) public onlyAdmin {
console.log("msg sender", msg.sender);
console.log("address this", address(this));
address poolAddress = newDeployments[0];
address creditDeskAddress = newDeployments[1];
address fiduAddress = newDeployments[2];
address goldfinchFactoryAddress = newDeployments[3];
bytes calldata data;
console.log("proxy address is:", _config.poolAddress());
console.log("pool implementation address is", poolAddress);
IBase(_config.poolAddress()).changeImplementation(poolAddress, data);
console.log("It appears to have worked", poolAddress);
IBase(_config.creditDeskAddress()).changeImplementation(creditDeskAddress, data);
IBase(_config.fiduAddress()).changeImplementation(fiduAddress, data);
IBase(_config.goldfinchFactoryAddress()).changeImplementation(goldfinchFactoryAddress, data);
}
}
And here are some useful logs...
governance contract is: 0xBEb28978B2c755155f20fd3d09Cb37e300A6981f
new implementation is: 0x0f5D1ef48f12b6f691401bfe88c2037c690a6afe
msg sender 0xbeb28978b2c755155f20fd3d09cb37e300a6981f
address this 0x59f2f1fcfe2474fd5f0b9ba1e73ca90b143eb8d0
proxy address is: 0xb01b315e32d1d9b5ce93e296d483e1f0aad39e75
pool implementation address is 0x0f5d1ef48f12b6f691401bfe88c2037c690a6afe
safe tx is: EthSafeTransaction {
signatures: Map {},
data: {
to: '0x59F2f1fCfE2474fD5F0b9BA1E73ca90b143Eb8d0',
value: '0',
data: '0x1b7e0e8a0000000000000000000000007daa6477194b784d384e79333230daec3b32a65e000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000f5d1ef48f12b6f691401bfe88c2037c690a6afe00000000000000000000000090118d110b07abb82ba8980d1c5cc96eea810d2c000000000000000000000000ca03dc4665a8c3603cb4fd5ce71af9649dc00d440000000000000000000000002de080e97b0cae9825375d31f5d0ed5751fdf16d',
operation: 1,
baseGas: 0,
gasPrice: 0,
gasToken: '0x0000000000000000000000000000000000000000',
refundReceiver: '0x0000000000000000000000000000000000000000',
nonce: 40,
safeTxGas: 0
}
}
execution result is: {
to: '0xBEb28978B2c755155f20fd3d09Cb37e300A6981f',
from: '0xf13eFa505444D09E176d83A4dfd50d10E399cFd5',
contractAddress: null,
transactionIndex: 0,
gasUsed: BigNumber { _hex: '0xbfb1', _isBigNumber: true },
logsBloom: '0x00000000400000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000800000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000',
blockHash: '0x374e8c16d52567f2d93b729d6c72a10f3f9a92f96ea53d461bcc794f86205459',
transactionHash: '0xac122c6077df323a0769a669ea6333c7e2c0ed67b00621bc44d83028d6aa93f9',
logs: [
{
transactionIndex: 0,
blockNumber: 12896337,
transactionHash: '0xac122c6077df323a0769a669ea6333c7e2c0ed67b00621bc44d83028d6aa93f9',
address: '0xBEb28978B2c755155f20fd3d09Cb37e300A6981f',
topics: [Array],
data: '0x62d25ac1a3d8db126aa75594cca3e3fa60929f815c302d48293853d767887d3d0000000000000000000000000000000000000000000000000000000000000000',
logIndex: 0,
blockHash: '0x374e8c16d52567f2d93b729d6c72a10f3f9a92f96ea53d461bcc794f86205459'
}
],
blockNumber: 12896337,
confirmations: 1,
cumulativeGasUsed: BigNumber { _hex: '0xbfb1', _isBigNumber: true },
status: 1,
byzantium: true,
events: [
{
transactionIndex: 0,
blockNumber: 12896337,
transactionHash: '0xac122c6077df323a0769a669ea6333c7e2c0ed67b00621bc44d83028d6aa93f9',
address: '0xBEb28978B2c755155f20fd3d09Cb37e300A6981f',
topics: [Array],
data: '0x62d25ac1a3d8db126aa75594cca3e3fa60929f815c302d48293853d767887d3d0000000000000000000000000000000000000000000000000000000000000000',
logIndex: 0,
blockHash: '0x374e8c16d52567f2d93b729d6c72a10f3f9a92f96ea53d461bcc794f86205459',
args: [Array],
decode: [Function],
event: 'ExecutionSuccess',
eventSignature: 'ExecutionSuccess(bytes32,uint256)',
removeListener: [Function],
getBlock: [Function],
getTransaction: [Function],
getTransactionReceipt: [Function]
}
]
}
So you can see a few things here.
- The only event is "ExecutionSuccess" from the multisig
- The console logs show that we do not even make it past the actual attempt to change the implementation (the log for "it appears to have worked never shows up")
- The msg.sender while in the "upgradeImplementations" method is in fact the governance contract. But "address(this)" is still the original contract. If it was actually a delegate call, I would expect "address(this)" to be the governance contract.
Also worth noting, if I change the "operation" on the multisig TX to be 0, then the 'msg.sender' and 'address(this)' values remain identical, and I receive an expected error of "NOT_AUTHORIZED". So my best guess right now is that DELEGATE_CALL is not really working, but the multisig is somehow swallowing the error when I do it? Any ideas on this? Any help would be greatly appreciated! Thanks!
Environment
I'm using a mac with latest Hardhat and Ethers/Truffle.