Skip to main content

Ethereum Interactions

One of the most exciting features of Aztec Connect is the ability to interact directly with Ethereum from Aztec. Interactions with Ethereum are facilitated by bridge contracts. You can read more about bridge contracts in the Aztec Connect bridges repo on Github.

The DefiController makes it easy to interact directly with deployed Ethereum bridge contracts. Bridge contracts make the connection between the Aztec rollup processor contract and various Ethereum contracts.

Setup

AztecSdk.createDefiController(
userId: GrumpkinAddress,
userSigner: Signer,
bridgeCallData: BridgeCallData,
value: AssetValue,
fee: AssetValue)
: Promise<DefiController>

Inputs

ArgumentsTypeDescription
userIdGrumpkinAddressOwner of the value note to be sent.
userSignerSignerA signer associated with the userId.
bridgeCallDataBridgeCallDataA unique id corresponding to the bridge that the value is sent to.
valueAssetValueAsset type and amount to send.
feeAssetValueAsset type and amount to pay for the Aztec transaction fee.

Returns

Return TypeDescription
DefiControllerA user instance with apis bound to the user's account id.

BridgeCallData Setup

A bridge addressId is required to setup BridgeCallData. The addressId is a number associated with the bridge. It increments by 1 as new bridges are deployed to Ethereum and added to the rollup processor contract.

You can get the bridge addressIds from the published Deployed Bridge Info table.

You can also query the bridge ids on the rollup processor contracts directly. Here is the link to read the contract Etherscan. You can get the bridge contract addresses from the getSupportedBridge or getSupportedBridges functions. The bridge addressId corresponds to it's index in the supported bridges array returned by getSupportedBridges.

Once you have the bridge addressId, you can initialize a new bridge instance. The BridgeCallData contstructor looks like this:

const bridge = new BridgeCallData(
addressId: number,
inputAssetIdA: number,
outputAssetIdA: number,
inputAssetIdB?: number | undefined,
outputAssetIdB?: number | undefined,
auxData?: number);
ArgumentsTypeDescription
addressIdnumberThe id of the bridge in the rollup processor contract.
inputAssetIdAnumberThe asset id of the first input asset.
outputAssetIdAnumberThe asset id of the first output asset.
inputAssetIdBnumberOptional. The asset id of the second input asset.
outputAssetIdBnumberOptional. The asset id of the second output asset.
auxDatanumberCustom auxiliary data for bridge-specific logic.

The BridgeCallData is passed to the DefiController to specify how to construct the interaction.

For example, you can create the BridgeCallData for the ETH to wstETH Lido bridge like this:

const ethToWstEthBridge = new BridgeCallData(2, 0, 2); // IN: ETH (0), OUT: wstETH (2)

The Lido bridge contract is addressId 2, takes 1 input asset (ETH, assetId = 0) and returns 1 output asset (wstETH, assetId = 2). This bridge doesn't take any auxData since it is just a simple exchange of ETH to wstETH.

Advanced usage

The Element bridge is a bit more complicated than the Lido bridge, so it's worth going through. The Element bridge allows users to deposit funds into Element directly from Aztec to earn yield.

The Element bridge is asynchronous, meaning there is a user action to deposit funds and then another action later to withdraw. The bridge also uses auxData to track which tranche a user is interacting with.

Typescript Bridge Connectors

The bridges repo contains a client directory that contains useful helper functions for interacting with deployed bridge contracts. You can review the full Element bridge file here. This file includes functions to get the expected auxData values, get expected yields and historical interaction information.

For this Element connector, we need to pass in an Ethereum provider, the rollup contract address, the bridge contract address and a mainnet flag (useful for testing). You can view the ElementBridgeData class interface here.

For example:

const elementAdaptor = createElementAdaptor(
ethereumProvider,
"0xFF1F2B4ADb9dF6FC8eAFecDcbF96A2B351680455", // rollup contract
"0xaeD181779A8AAbD8Ce996949853FEA442C2CDB47", // bridge contract
false // mainnet flag
);

Once it's set up, we can ask it for the expected auxData values with:

await elementAdaptor.getAuxData?(
inputAssetA: AztecAsset,
inputAssetB: AztecAsset,
outputAssetA: AztecAsset,
outputAssetB: AztecAsset): Promise<bigint[]>;

This function returns an index of numbers corresponding to various tranche expiry times that correspond to the inputAsset. This will determine which tranche the user interacts with. So you could set up BridgeCallData for a user to interact with the latest tranche by passing the last index of the array of returned expiry times.

Looking at the Deployed Bridge Info table, the Element bridge is id 1, it takes DAI (asset id 1) and returns DAI and you can pass an element from the auxData array from the connector and pass this to the DefiController.

const elementBridge = new BridgeCallData(1, 1, 1, undefined, undefined, Number(elementAuxData[0])); // IN: DAI (1), OUT: DAI (1)

Async finalise

Since the Element bridge is asynchronous, the finalise function must be called on the bridge contract after the Element tranche has matured to get the deposit + interest back in the Aztec account.

In the case of the Element bridge, this is handled automatically by users entering new Element positions. The bridge contract checks if there are any positions available to finalise and if there are, it will process them. You can view the relevant code in the bridge here.

If you want to finalise a defi interaction without having to rely on interactions from others or do it independently of other bridge contract interactions, you can. There is a processAsyncDefiInteraction function on the rollup processor contract that takes an interactionNonce. You can call this from any Ethereum account. You can fetch defi transaction interactionNonces from an Aztec account using the getUserTxs method in the SDK.

Fees

Similarly to other controllers, the SDK comes with a helper method to calculate fees. It requires the BridgeCallData since different bridges cost different amounts of gas. settlementTime is type DefiSettlementTime.

DefiSettlementTime is an enum with members DEADLINE, NEXT_ROLLUP and INSTANT. DEADLINE is the slowest and cheapest. It provides a longer opportunity for your transaction to be batched with other similar interactions. NEXT_ROLLUP is the next most expensive and will process in the next regardless of the number of similar interactions. INSTANT is the most expensive and will pay the cost to settle the rollup and process the interaction immediately.

const fee = (await sdk.getDefiFees(bridgeId))[settlementTime];

The settlement time is inferred from the fee a user pays, it is not explicitly sent to the controller.

Bridge Address Ids

You can find the latest bridge contract addressIds and Ethereum contract addresses in the Aztec Connect Bridges repo.