Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
432 changes: 216 additions & 216 deletions espresso/bindings/batch_authenticator.go

Large diffs are not rendered by default.

35 changes: 33 additions & 2 deletions espresso/bindings/opsuccinct_fault_dispute_game.go

Large diffs are not rendered by default.

18 changes: 9 additions & 9 deletions espresso/devnet-tests/batcher_active_publish_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ func TestBatcherActivePublishOnly(t *testing.T) {
batchAuthenticator, err := bindings.NewBatchAuthenticator(config.BatchAuthenticatorAddress, d.L1)
require.NoError(t, err)

teeBatcherAddr, err := batchAuthenticator.TeeBatcher(&bind.CallOpts{})
espressoBatcherAddr, err := batchAuthenticator.EspressoBatcher(&bind.CallOpts{})
require.NoError(t, err)
nonTeeBatcherAddr := config.Genesis.SystemConfig.BatcherAddr
fallbackBatcherAddr := config.Genesis.SystemConfig.BatcherAddr

activeIsTee, err := batchAuthenticator.ActiveIsTee(&bind.CallOpts{})
require.NoError(t, err)
Expand Down Expand Up @@ -100,19 +100,19 @@ func TestBatcherActivePublishOnly(t *testing.T) {
require.NoError(t, err)
t.Logf("Checking blocks %d-%d", startBlock, endBlock)

teePublished, err := hasBatchTransactions(ctx, d.L1, config.BatchInboxAddress, teeBatcherAddr, startBlock, endBlock)
espressoPublished, err := hasBatchTransactions(ctx, d.L1, config.BatchInboxAddress, espressoBatcherAddr, startBlock, endBlock)
require.NoError(t, err)
nonTeePublished, err := hasBatchTransactions(ctx, d.L1, config.BatchInboxAddress, nonTeeBatcherAddr, startBlock, endBlock)
fallbackPublished, err := hasBatchTransactions(ctx, d.L1, config.BatchInboxAddress, fallbackBatcherAddr, startBlock, endBlock)
require.NoError(t, err)

t.Logf("TEE batcher published: %v, non-TEE batcher published: %v", teePublished, nonTeePublished)
t.Logf("Espresso batcher published: %v, non-Espresso batcher published: %v", espressoPublished, fallbackPublished)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Same inconsistency here — the log message says "non-Espresso batcher" while the variable is named fallbackPublished. Consider aligning:

Suggested change
t.Logf("Espresso batcher published: %v, non-Espresso batcher published: %v", espressoPublished, fallbackPublished)
t.Logf("Espresso batcher published: %v, fallback batcher published: %v", espressoPublished, fallbackPublished)


if expectTeeActive {
require.True(t, teePublished, "TEE batcher should publish when active")
require.False(t, nonTeePublished, "non-TEE batcher should NOT publish when inactive")
require.True(t, espressoPublished, "Espresso batcher should publish when active")
require.False(t, fallbackPublished, "fallback batcher should NOT publish when inactive")
} else {
require.True(t, nonTeePublished, "non-TEE batcher should publish when active")
require.False(t, teePublished, "TEE batcher should NOT publish when inactive")
require.True(t, fallbackPublished, "fallback batcher should publish when active")
require.False(t, espressoPublished, "Espresso batcher should NOT publish when inactive")
}
}

Expand Down
2 changes: 1 addition & 1 deletion espresso/devnet-tests/batcher_switching_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
)

// TestBatcherSwitching tests that the batcher can be switched from the TEE-enabled
// batcher to a fallback non-TEE batcher using the BatchAuthenticator contract.
// batcher to a fallback non-Espresso batcher using the BatchAuthenticator contract.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: The rest of the PR uses "fallback batcher" consistently, but this comment says "non-Espresso batcher". Consider updating for consistency:

Suggested change
// batcher to a fallback non-Espresso batcher using the BatchAuthenticator contract.
// batcher to a fallback batcher using the BatchAuthenticator contract.

//
// This is the devnet equivalent of TestBatcherSwitching from the E2E tests.
// The test runs two batchers in parallel:
Expand Down
2 changes: 1 addition & 1 deletion espresso/devnet-tests/devnet_tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ func (d *Devnet) Up(profile ComposeProfile) (err error) {
"docker", "compose", "up", "-d",
)
cmd.Env = append(os.Environ(), "COMPOSE_PROFILES="+string(profile))
// TEE batcher uses HD index 6 (distinct from the SystemConfig/fallback batcher at index 2)
// Espresso batcher uses HD index 6 (distinct from the SystemConfig/fallback batcher at index 2)
cmd.Env = append(
cmd.Env,
fmt.Sprintf("OP_BATCHER_PRIVATE_KEY=%s", hex.EncodeToString(crypto.FromECDSA(d.secrets.AccountAtIdx(6)))),
Expand Down
4 changes: 2 additions & 2 deletions espresso/environment/14_batcher_fallback_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ func waitForRollupToMovePastL1Block(ctx context.Context, rollupCli *sources.Roll
// fallback batcher, ensuring seamless transitions in both directions.
//
// In this scenario the test starts with the batcher running in Espresso
// mode and verifies transactions work correctly. It then stops the TEE batcher,
// mode and verifies transactions work correctly. It then stops the Espresso batcher,
// sends switch action to the Batch Authenticator contract and switches to the
// fallback batcher, verifies transactions continue to go through. Next, it switches
// back to the TEE batcher by restarting it with proper caffeination heights
// back to the Espresso batcher by restarting it with proper caffeination heights
// (both Espresso and L2 heights set to ensure correct sync points). Finally, it
// launches a Caff node with the same caffeination heights and verifies it
// derives the same chain state as the verifier by comparing block hashes at the
Expand Down
2 changes: 1 addition & 1 deletion espresso/environment/6_batch_inbox_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func TestE2eDevnetWithoutAuthenticatingBatches(t *testing.T) {
// Substitute batcher's transaction manager with one that always sends transactions, even
// if they won't succeed. Otherwise batcher wouldn't submit transactions that would revert to
// batch inbox.
// Use the TEE batcher key (HD index 6) — the same key the primary batcher signs with.
// Use the Espresso batcher key (HD index 6) — the same key the primary batcher signs with.
// This ensures the tx comes from an address that is NOT the SystemConfig batcher, so the
// derivation pipeline's fallback authorization won't accept it either.
txMgrCliConfig := setuputils.NewTxMgrConfig(system.NodeEndpoint(e2esys.RoleL1), system.Cfg.Secrets.AccountAtIdx(6))
Expand Down
10 changes: 5 additions & 5 deletions espresso/ethclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,18 @@ func (c *AdaptL1BlockRefClient) CallContract(ctx context.Context, call ethereum.
return c.L1Client.CallContract(ctx, call, blockNumber)
}

// FetchTeeBatcherAddress reads the TEE batcher address from the BatchAuthenticator
// FetchEspressoBatcherAddress reads the Espresso batcher address from the BatchAuthenticator
// contract on L1. This is used by the caff node to determine which address signed
// Espresso batches, since the TEE batcher may use a different key than the
// Espresso batches, since the Espresso batcher may use a different key than the
// SystemConfig batcher (fallback batcher).
func FetchTeeBatcherAddress(ctx context.Context, l1Client *ethclient.Client, batchAuthenticatorAddr common.Address) (common.Address, error) {
func FetchEspressoBatcherAddress(ctx context.Context, l1Client *ethclient.Client, batchAuthenticatorAddr common.Address) (common.Address, error) {
caller, err := bindings.NewBatchAuthenticatorCaller(batchAuthenticatorAddr, l1Client)
if err != nil {
return common.Address{}, fmt.Errorf("failed to bind BatchAuthenticator at %s: %w", batchAuthenticatorAddr, err)
}
addr, err := caller.TeeBatcher(&bind.CallOpts{Context: ctx})
addr, err := caller.EspressoBatcher(&bind.CallOpts{Context: ctx})
if err != nil {
return common.Address{}, fmt.Errorf("failed to call BatchAuthenticator.teeBatcher(): %w", err)
return common.Address{}, fmt.Errorf("failed to call BatchAuthenticator.espressoBatcher(): %w", err)
}
return addr, nil
}
16 changes: 8 additions & 8 deletions espresso/streamer.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ func GetFinalizedL1(header *espressoCommon.HeaderImpl) espressoCommon.L1BlockInf
type l1State struct {
// Block hash
hash common.Hash
// TEE batchers addresses for signature verification
teeBatchers []common.Address
// Espresso batcher addresses for signature verification
espressoBatchers []common.Address
}

type BatchStreamer[B Batch] struct {
Expand Down Expand Up @@ -244,22 +244,22 @@ func (s *BatchStreamer[B]) CheckBatch(ctx context.Context, batch B) BatchValidit
return BatchUndecided
}

teeBatcher, err := s.BatchAuthenticatorCaller.TeeBatcher(&bind.CallOpts{BlockNumber: blockNumber})
espressoBatcher, err := s.BatchAuthenticatorCaller.EspressoBatcher(&bind.CallOpts{BlockNumber: blockNumber})
if err != nil {
s.Log.Warn("Failed to fetch the TEE batcher address, pending resync", "error", err)
s.Log.Warn("Failed to fetch the Espresso batcher address, pending resync", "error", err)
return BatchUndecided
}

state = l1State{
hash: hash,
teeBatchers: []common.Address{teeBatcher},
hash: hash,
espressoBatchers: []common.Address{espressoBatcher},
}

s.finalizedL1StateCache.Add(origin.Number, state)
}

if !slices.Contains(state.teeBatchers, batch.Signer()) {
s.Log.Info("Dropping batch with invalid TEE batcher", "batch", batch.Hash(), "signer", batch.Signer())
if !slices.Contains(state.espressoBatchers, batch.Signer()) {
s.Log.Info("Dropping batch with invalid Espresso batcher", "batch", batch.Hash(), "signer", batch.Signer())
return BatchDrop
}

Expand Down
22 changes: 11 additions & 11 deletions espresso/streamer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,9 @@ type MockStreamerSource struct {
LatestEspHeight uint64
finalizedHeightHistory map[uint64]uint64

// TeeBatcherAddr is the address returned by the mock BatchAuthenticator contract
// for teeBatcher() calls. Can be changed per-test to simulate TEE batcher rotation.
TeeBatcherAddr common.Address
// EspressoBatcherAddr is the address returned by the mock BatchAuthenticator contract
// for espressoBatcher() calls. Can be changed per-test to simulate Espresso batcher rotation.
EspressoBatcherAddr common.Address
}

// FetchNamespaceTransactionsInRange implements espresso.EspressoClient.
Expand Down Expand Up @@ -203,19 +203,19 @@ func (m *MockStreamerSource) HeaderHashByNumber(ctx context.Context, number *big
return l1Ref.Hash, nil
}

// teeBatcherSelector is the 4-byte function selector for teeBatcher() — 0xd909ba7c
var teeBatcherSelector = []byte{0xd9, 0x09, 0xba, 0x7c}
// espressoBatcherSelector is the 4-byte function selector for espressoBatcher() — 0x030650d2
var espressoBatcherSelector = []byte{0x03, 0x06, 0x50, 0xd2}

func (m *MockStreamerSource) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
// Return non-empty bytes so the bindings consider the contract deployed
return []byte{0x01}, nil
}

func (m *MockStreamerSource) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
if len(call.Data) >= 4 && common.Bytes2Hex(call.Data[:4]) == common.Bytes2Hex(teeBatcherSelector) {
// ABI-encode the TEE batcher address as a 32-byte left-padded word
if len(call.Data) >= 4 && common.Bytes2Hex(call.Data[:4]) == common.Bytes2Hex(espressoBatcherSelector) {
// ABI-encode the Espresso batcher address as a 32-byte left-padded word
var result [32]byte
copy(result[12:], m.TeeBatcherAddr.Bytes())
copy(result[12:], m.EspressoBatcherAddr.Bytes())
return result[:], nil
}
return nil, fmt.Errorf("unexpected contract call: %x", call.Data)
Expand Down Expand Up @@ -425,7 +425,7 @@ var batchAuthenticatorAddr = common.HexToAddress("0x0000000000000000000000000000
// and returns both the MockStreamerSource and the EspressoStreamer.
func setupStreamerTesting(namespace uint64, batcherAddress common.Address) (*MockStreamerSource, *espresso.BatchStreamer[derive.EspressoBatch]) {
state := NewMockStreamerSource()
state.TeeBatcherAddr = batcherAddress
state.EspressoBatcherAddr = batcherAddress

logger := new(NoOpLogger)
streamer, err := espresso.NewEspressoStreamer(
Expand Down Expand Up @@ -1070,7 +1070,7 @@ func TestStreamerBufferCapacityAndSkipPos(t *testing.T) {
defer cancel()

state := NewMockStreamerSource()
state.TeeBatcherAddr = signerAddress
state.EspressoBatcherAddr = signerAddress
logger := new(NoOpLogger)

streamer, err := espresso.NewEspressoStreamer(
Expand Down Expand Up @@ -1149,7 +1149,7 @@ func TestStreamerBufferCapacityAndSkipPos(t *testing.T) {
defer cancel()

state := NewMockStreamerSource()
state.TeeBatcherAddr = signerAddress
state.EspressoBatcherAddr = signerAddress
logger := new(NoOpLogger)

// Create streamer - after Refresh with SafeL2.Number=0, BatchPos becomes 1
Expand Down
6 changes: 3 additions & 3 deletions op-batcher/batcher/espresso_active.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ import (
// be publishing batches, false if it should stay idle.
//
// The active batcher is determined by the contract's activeIsTee flag:
// - If activeIsTee is true, the TEE batcher address is active
// - If activeIsTee is false, the non-TEE (fallback) batcher address is active
// - If activeIsTee is true, the Espresso batcher address is active
// - If activeIsTee is false, the fallback batcher address is active
//
// This method compares the batcher's own address (from TxMgr) against the
// contract's registered TEE batcher address and the SystemConfig batcher address.
// contract's registered Espresso batcher address and the SystemConfig batcher address.
func (l *BatchSubmitter) isBatcherActive(ctx context.Context) (bool, error) {
// Check if contract code exists at the address
code, err := l.L1Client.CodeAt(ctx, l.RollupConfig.BatchAuthenticatorAddress, nil)
Expand Down
2 changes: 1 addition & 1 deletion op-deployer/pkg/deployer/opcm/espresso.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ type DeployAWSNitroVerifierOutput struct {
type DeployEspressoInput struct {
Salt common.Hash
NitroTEEVerifier common.Address
TeeBatcher common.Address
EspressoBatcher common.Address
SystemConfig common.Address
ProxyAdminOwner common.Address
UseMockTEEVerifier bool
Expand Down
2 changes: 1 addition & 1 deletion op-deployer/pkg/deployer/pipeline/espresso.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func DeployEspresso(env *Env, intent *state.Intent, st *state.State, chainID com
eo, err = opcm.DeployEspresso(env.L1ScriptHost, opcm.DeployEspressoInput{
Salt: st.Create2Salt,
NitroTEEVerifier: nvo.NitroTEEVerifierProxy,
TeeBatcher: chainIntent.TeeBatcher,
EspressoBatcher: chainIntent.EspressoBatcher,
SystemConfig: chainState.SystemConfigProxy,
ProxyAdminOwner: batchAuthenticatorOwnwerAddress,
UseMockTEEVerifier: nitroEnclaveVerifierAddress == common.Address{},
Expand Down
2 changes: 1 addition & 1 deletion op-deployer/pkg/deployer/state/chain_intent.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ type ChainIntent struct {

// Espresso-specific fields
EspressoEnabled bool `json:"espressoEnabled,omitzero" toml:"espressoEnabled,omitzero"`
TeeBatcher common.Address `json:"teeBatcher,omitzero" toml:"teeBatcher,omitzero"`
EspressoBatcher common.Address `json:"espressoBatcher,omitzero" toml:"espressoBatcher,omitzero"`
}

type ChainRoles struct {
Expand Down
6 changes: 3 additions & 3 deletions op-e2e/config/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,13 +278,13 @@ func initAllocType(root string, allocType AllocType) {
}

// Configure Espresso allocation types.
// The TEE batcher uses a separate key (HD index 6) from the standard
// The Espresso batcher uses a separate key (HD index 6) from the standard
// OP stack batcher (Roles.Batcher, HD index 2). The fallback batcher
// uses the SystemConfig batcher address (Roles.Batcher).
if allocType.IsEspresso() {
intent.Chains[0].EspressoEnabled = true
teeBatcherKey := secrets.DefaultSecrets.AccountAtIdx(6)
intent.Chains[0].TeeBatcher = crypto.PubkeyToAddress(teeBatcherKey.PublicKey)
espressoBatcherKey := secrets.DefaultSecrets.AccountAtIdx(6)
intent.Chains[0].EspressoBatcher = crypto.PubkeyToAddress(espressoBatcherKey.PublicKey)
}

baseUpgradeSchedule := map[string]any{
Expand Down
2 changes: 1 addition & 1 deletion op-e2e/system/e2esys/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -1022,7 +1022,7 @@ func (cfg SystemConfig) Start(t *testing.T, startOpts ...StartOption) (*System,
TestingBatcherPrivateKey: testingBatcherPk,
}

// When Espresso is enabled, the primary batcher is the TEE batcher which uses
// When Espresso is enabled, the primary batcher is the Espresso batcher which uses
// a dedicated key (HD index 6) distinct from the SystemConfig batcher (HD index 2).
batcherKey := cfg.Secrets.Batcher
if cfg.AllocType.IsEspresso() {
Expand Down
2 changes: 1 addition & 1 deletion op-node/rollup/derive/calldata_source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ func TestDataFromEVMTransactionsEventAuth(t *testing.T) {

t.Run("mixed: TEE authenticated and fallback sender", func(t *testing.T) {
l1F := &testutils.MockL1Source{}
// tx1: TEE batcher with auth event
// tx1: Espresso batcher with auth event
txData1 := testutils.RandomData(rng, 100)
tx1, err := types.SignNewTx(batcherPriv, signer, &types.DynamicFeeTx{
ChainID: big.NewInt(100), Nonce: 0, Gas: 100_000,
Expand Down
6 changes: 3 additions & 3 deletions op-node/rollup/derive/data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,13 +143,13 @@ func isAuthorizedBatchSender(tx *types.Transaction, l1Signer types.Signer, batch
// once per L1 block via CollectAuthenticatedBatches.
//
// When batch auth is enabled, there are two authorization paths:
// 1. TEE batcher: must have a matching BatchInfoAuthenticated event (event-based auth)
// 1. Espresso batcher: must have a matching BatchInfoAuthenticated event (event-based auth)
// 2. Fallback batcher: authorized via sender verification against batcherAddr, which is
// the standard OP stack batcher address from SystemConfig.batcherHash. This allows
// the fallback batcher address to be changed dynamically via SystemConfig.setBatcherHash().
//
// This dual-mode approach allows the fallback (non-TEE) batcher to post batches without
// calling authenticateBatchInfo on L1, while still requiring the TEE batcher to authenticate
// calling authenticateBatchInfo on L1, while still requiring the Espresso batcher to authenticate
// its batches via on-chain events.
func isBatchTxAuthorized(
tx *types.Transaction,
Expand All @@ -160,7 +160,7 @@ func isBatchTxAuthorized(
logger log.Logger,
) bool {
if dsCfg.BatchAuthEnabled() {
// Event-based authentication: TEE batcher must have an auth event
// Event-based authentication: Espresso batcher must have an auth event
if authenticatedHashes[batchHash] {
return true
}
Expand Down
14 changes: 7 additions & 7 deletions packages/contracts-bedrock/interfaces/L1/IBatchAuthenticator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ interface IBatchAuthenticator {
/// @notice Emitted when a signer registration is initiated through this contract.
event SignerRegistrationInitiated(address indexed caller);

/// @notice Emitted when the TEE batcher address is updated.
event TeeBatcherUpdated(
address indexed oldTeeBatcher,
address indexed newTeeBatcher
/// @notice Emitted when the Espresso batcher address is updated.
event EspressoBatcherUpdated(
address indexed oldEspressoBatcher,
address indexed newEspressoBatcher
);

/// @notice Emitted when the active batcher is switched.
Expand All @@ -34,9 +34,9 @@ interface IBatchAuthenticator {

function owner() external view returns (address);

function teeBatcher() external view returns (address);
function espressoBatcher() external view returns (address);

function registerSigner(bytes memory attestationTbs, bytes memory signature) external;
function registerSigner(bytes memory verificationData, bytes memory data) external;

function activeIsTee() external view returns (bool);

Expand All @@ -46,5 +46,5 @@ interface IBatchAuthenticator {

function switchBatcher() external;

function setTeeBatcher(address _newTeeBatcher) external;
function setEspressoBatcher(address _newEspressoBatcher) external;
}
12 changes: 6 additions & 6 deletions packages/contracts-bedrock/scripts/deploy/DeployEspresso.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { MockEspressoTEEVerifier } from "test/mocks/MockEspressoTEEVerifiers.sol
contract DeployEspressoInput is BaseDeployIO {
bytes32 internal _salt;
address internal _nitroTEEVerifier;
address internal _teeBatcher;
address internal _espressoBatcher;
address internal _systemConfig;
address internal _proxyAdminOwner;
bool internal _useMockTEEVerifier;
Expand All @@ -37,8 +37,8 @@ contract DeployEspressoInput is BaseDeployIO {
function set(bytes4 _sel, address _val) public {
if (_sel == this.nitroTEEVerifier.selector) {
_nitroTEEVerifier = _val;
} else if (_sel == this.teeBatcher.selector) {
_teeBatcher = _val;
} else if (_sel == this.espressoBatcher.selector) {
_espressoBatcher = _val;
} else if (_sel == this.systemConfig.selector) {
_systemConfig = _val;
} else if (_sel == this.proxyAdminOwner.selector) {
Expand All @@ -58,8 +58,8 @@ contract DeployEspressoInput is BaseDeployIO {
return _nitroTEEVerifier;
}

function teeBatcher() public view returns (address) {
return _teeBatcher;
function espressoBatcher() public view returns (address) {
return _espressoBatcher;
}

function systemConfig() public view returns (address) {
Expand Down Expand Up @@ -164,7 +164,7 @@ contract DeployEspresso is Script {
// Initialize the proxy with explicit owner parameter
bytes memory initData = abi.encodeCall(
BatchAuthenticator.initialize,
(teeVerifier, input.teeBatcher(), ISystemConfig(input.systemConfig()), batchAuthenticatorOwner)
(teeVerifier, input.espressoBatcher(), ISystemConfig(input.systemConfig()), batchAuthenticatorOwner)
);
vm.broadcast(msg.sender);
proxyAdmin.upgradeAndCall(payable(address(proxy)), address(impl), initData);
Expand Down
Loading
Loading