Skip to content

Commit

Permalink
docs: fix lint
Browse files Browse the repository at this point in the history
  • Loading branch information
kaymomin committed Nov 25, 2024
1 parent ceaabae commit 98de704
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 72 deletions.
117 changes: 45 additions & 72 deletions docs/src/specs/interop/interoptransactions.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,30 @@
# Interop Transactions

## Basics

The **InteropTransaction** sits at the top of our interop stack, acting as the “delivery” mechanism for **Interop
Bundles**.
The **InteropTransaction** sits at the top of our interop stack, acting as the “delivery” mechanism for **Interop Bundles**.

Think of it like a car that picks up our "hitchhiker" bundles and carries them to their destination.

![interoptx.png](../img/interoptx.png)

**Note:** Interop Transactions aren’t the only way to execute a bundle. Once an interop bundle is created on the source
chain, users can simply send a regular transaction on the destination chain to execute it.
**Note:** Interop Transactions aren’t the only way to execute a bundle. Once an interop bundle is created on the source chain, users can simply send a regular transaction on the destination chain to execute it.

However, this approach can be inconvenient as it requires users to have funds on the destination chain to cover gas fees
and to configure the necessary network settings (like the RPC address).
However, this approach can be inconvenient as it requires users to have funds on the destination chain to cover gas fees and to configure the necessary network settings (like the RPC address).

**InteropTransactions** simplify this process by handling everything from the source chain. They allow you to select
which **interopBundle** to execute, specify gas details (such as gas amount and gas price), and determine who will cover
the gas costs. This can be achieved using tokens on the source chain or through a paymaster.
**InteropTransactions** simplify this process by handling everything from the source chain. They allow you to select which **interopBundle** to execute, specify gas details (such as gas amount and gas price), and determine who will cover the gas costs. This can be achieved using tokens on the source chain or through a paymaster.

Once configured, the transaction will automatically execute, either by the chain operator, the gateway, or off-chain
tools.
Once configured, the transaction will automatically execute, either by the chain operator, the gateway, or off-chain tools.

An **InteropTransaction** contains two pointers to bundles:

- **feesBundle**: Holds interop calls to cover fees.
- **bundleHash**: Contains the main execution.
- **feesBundle**: Holds interop calls to cover fees.
- **bundleHash**: Contains the main execution.

![ipointers.png](../img/ipointers.png)

## Interface

The function `sendInteropTransaction` provides all the options. For simpler use cases, refer to the helper methods
defined later in the article.
The function `sendInteropTransaction` provides all the options. For simpler use cases, refer to the helper methods defined later in the article.

```solidity
contract InteropCenter {
Expand All @@ -41,15 +33,15 @@ contract InteropCenter {
/// This function covers all the cases - we expect most users to use the helper
/// functions defined later.
function sendInteropTransaction(
destinationChain,
destinationChain,
bundleHash, // the main bundle that you want to execute on destination chain
gasLimit, // gasLimit & price for execution
gasPrice,
gasPrice,
feesBundleHash, // this is the bundle that contains the calls to pay for gas
destinationPaymaster, // optionally - you can use a paymaster on destination chain
destinationPaymasterInput); // with specific params
struct InteropTransaction {
address sourceChainSender
uint256 destinationChain
Expand All @@ -64,70 +56,59 @@ contract InteropCenter {
}
```

After creating the **InteropBundle**, you can simply call `sendInteropTransaction` to create the complete transaction
that will execute the bundle.
After creating the **InteropBundle**, you can simply call `sendInteropTransaction` to create the complete transaction that will execute the bundle.

## Retries

If your transaction fails to execute the bundle (e.g., due to a low gas limit) or isn’t included at all (e.g., due to
too low gasPrice), you can send another transaction to **attempt to execute the same bundle again**.
If your transaction fails to execute the bundle (e.g., due to a low gas limit) or isn’t included at all (e.g., due to too low gasPrice), you can send another transaction to **attempt to execute the same bundle again**.

Simply call `sendInteropTransaction` again with updated gas settings.


### Example of Retrying

Here’s a concrete example: Suppose you created a bundle to perform a swap that includes transferring 100 ETH, executing
the swap, and transferring some tokens back.
Here’s a concrete example: Suppose you created a bundle to perform a swap that includes transferring 100 ETH, executing the swap, and transferring some tokens back.

You attempted to send the interop transaction with a low gas limit (e.g., 100). Since you didn’t have any base tokens on
the destination chain, you created a separate bundle to transfer a small fee (e.g., 0.0001) to cover the gas.
You attempted to send the interop transaction with a low gas limit (e.g., 100). Since you didn’t have any base tokens on the destination chain, you created a separate bundle to transfer a small fee (e.g., 0.0001) to cover the gas.

You sent your first interop transaction to the destination chain, but it failed due to insufficient gas. However, your
“fee bundle” was successfully executed, as it covered the gas cost for the failed attempt.
You sent your first interop transaction to the destination chain, but it failed due to insufficient gas. However, your “fee bundle” was successfully executed, as it covered the gas cost for the failed attempt.

Now, you have two options: either cancel the execution bundle (the one with 100 ETH) or retry.

To retry, you decide to set a higher gas limit (e.g., 10,000) and create another fee transfer (e.g., 0.01) but use **the
same execution bundle** as before.
To retry, you decide to set a higher gas limit (e.g., 10,000) and create another fee transfer (e.g., 0.01) but use **the same execution bundle** as before.

This time, the transaction succeeds — the swap completes on the destination chain, and the resulting tokens are
successfully transferred back to the source chain.
This time, the transaction succeeds — the swap completes on the destination chain, and the resulting tokens are successfully transferred back to the source chain.

![retryexample.png](../img/retryexample.png)

## Fees & Restrictions

Using an **InteropBundle** for fee payments offers flexibility, allowing users to transfer a small amount to cover the
fees while keeping the main assets in the execution bundle itself.
Using an **InteropBundle** for fee payments offers flexibility, allowing users to transfer a small amount to cover the fees while keeping the main assets in the execution bundle itself.

### Restrictions

This flexibility comes with trade-offs, similar to the validation phases in **Account Abstraction** or **ERC4337**,
primarily designed to prevent DoS attacks. Key restrictions include:
This flexibility comes with trade-offs, similar to the validation phases in **Account Abstraction** or **ERC4337**, primarily designed to prevent DoS attacks. Key restrictions include:

- **Lower gas limits**
- **Lower gas limits**
- **Limited access to specific slots**

Additionally, when the `INTEROP_CENTER` constructs an **InteropTransaction**, it enforces extra restrictions on
**feePaymentBundles**:
Additionally, when the `INTEROP_CENTER` constructs an **InteropTransaction**, it enforces extra restrictions on **feePaymentBundles**:

- **Restricted Executors**:
Only your **AliasedAccount** on the receiving side can execute the `feePaymentBundle`.
Only your **AliasedAccount** on the receiving side can execute the `feePaymentBundle`.

This restriction is crucial for security, preventing others from executing your **fee bundle**, which could cause your transaction to fail and prevent the **execution bundle** from processing.


This restriction is crucial for security, preventing others from executing your **fee bundle**, which could cause your
transaction to fail and prevent the **execution bundle** from processing.

### **Types of Fees**

#### Using the Destination Chain’s Base Token

The simplest scenario is when you (as the sender) already have the destination chain’s base token available on the
source chain.
The simplest scenario is when you (as the sender) already have the destination chain’s base token available on the source chain.

For example:

- If you are sending a transaction from **Era** (base token: ETH) to **Sophon** (base token: SOPH) and already have SOPH
on ERA, you can use it for the fee.
- If you are sending a transaction from **Era** (base token: ETH) to **Sophon** (base token: SOPH) and already have SOPH on ERA, you can use it for the fee.

To make this easier, we’ll provide a helper function:

Expand All @@ -137,7 +118,7 @@ contract InteropCenter {
// Before calling, you have to 'approve' InteropCenter to the ERC20/Bridge that holds the destination chain's base tokens.
// or if the destination chain's tokens are the same as yours, just attach value to this call.
function sendInteropTxMinimal(
destinationChain,
destinationChain,
bundleHash, // the main bundle that you want to execute on destination chain
gasLimit, // gasLimit & price for execution
gasPrice,
Expand All @@ -147,54 +128,46 @@ contract InteropCenter {

#### Using paymaster on the destination chain

If you don’t have the base token from the destination chain (e.g., SOPH in our example) on your source chain, you’ll
need to use a paymaster on the destination chain instead.
If you don’t have the base token from the destination chain (e.g., SOPH in our example) on your source chain, you’ll need to use a paymaster on the destination chain instead.

In this case, you’ll send the token you do have (e.g., USDC) to the destination chain as part of the **feeBundleHash**.
Once there, you’ll use it to pay the paymaster on the destination chain to cover your gas fees.
In this case, you’ll send the token you do have (e.g., USDC) to the destination chain as part of the **feeBundleHash**. Once there, you’ll use it to pay the paymaster on the destination chain to cover your gas fees.

Your **InteropTransaction** would look like this:

![paymastertx.png](../img/paymastertx.png)

## **Automatic Execution**

One of the main advantages of **InteropTransactions** is that they execute automatically. As the sender on the source
chain, you don’t need to worry about technical details like RPC addresses or obtaining proofs — it’s all handled for
you.
One of the main advantages of **InteropTransactions** is that they execute automatically. As the sender on the source chain, you don’t need to worry about technical details like RPC addresses or obtaining proofs — it’s all handled for you.

After creating an **InteropTransaction**, it can be relayed to the destination chain by anyone. The transaction already
includes a signature (also known as an interop message proof), making it fully self-contained and ready to send without
requiring additional permissions.
After creating an **InteropTransaction**, it can be relayed to the destination chain by anyone. The transaction already includes a signature (also known as an interop message proof), making it fully self-contained and ready to send without requiring additional permissions.

Typically, the destination chain’s operator will handle and include incoming **InteropTransactions**. However, if they
don’t, the **Gateway** or other participants can step in to prepare and send them.
Typically, the destination chain’s operator will handle and include incoming **InteropTransactions**. However, if they don’t, the **Gateway** or other participants can step in to prepare and send them.

You can also use the available tools to create and send the destination transaction yourself. Since the transaction is
self-contained, it doesn’t require additional funds or signatures to execute.
You can also use the available tools to create and send the destination transaction yourself. Since the transaction is self-contained, it doesn’t require additional funds or signatures to execute.

![Usually destination chain operator will keep querying gateway to see if there are any messages for their chain.](../img/autoexecution.png)

Once they see the message, they can request the proof from the **Gateway** and also fetch the **InteropBundles**
contained within the message (along with their respective proofs).
Once they see the message, they can request the proof from the **Gateway** and also fetch the **InteropBundles** contained within the message (along with their respective proofs).

![Operator getting necessary data from Gateway.](../img/chainop.png)

As the final step, the operator can use the received data to create a regular transaction, which can then be sent to
their chain.
As the final step, the operator can use the received data to create a regular transaction, which can then be sent to their chain.


![Creating the final transaction to send to the destination chain](../img/finaltx.png)

The steps above don’t require any special permissions and can be executed by anyone.

While the **Gateway** was used above for tasks like providing proofs, if the Gateway becomes malicious, all this
information can still be constructed off-chain using data available on L1.
While the **Gateway** was used above for tasks like providing proofs, if the Gateway becomes malicious, all this information can still be constructed off-chain using data available on L1.


### How it Works Under the Hood
### How it Works Under the hood

We’ll modify the default account to accept interop proofs as signatures, seamlessly integrating with the existing ZKSync native **Account Abstraction** model.

We’ll modify the default account to accept interop proofs as signatures, seamlessly integrating with the existing ZKSync
native **Account Abstraction** model.

### Comparison with Superchain

Superchain doesn’t offer anything that is comparable to **InteropTransactions** on this level.

64 changes: 64 additions & 0 deletions install
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#!/usr/bin/env bash
set -eo pipefail

echo "Installing foundryup-zksync..."

BASE_DIR="${XDG_CONFIG_HOME:-$HOME}"
FOUNDRY_DIR="${FOUNDRY_DIR-"$BASE_DIR/.foundry"}"
FOUNDRY_BIN_DIR="$FOUNDRY_DIR/bin"
FOUNDRY_MAN_DIR="$FOUNDRY_DIR/share/man/man1"

BIN_URL="https://raw.githubusercontent.com/matter-labs/foundry-zksync/main/foundryup-zksync/foundryup-zksync"
BIN_PATH="$FOUNDRY_BIN_DIR/foundryup-zksync"

# Create the .foundry bin directory and foundryup binary if it doesn't exist.
mkdir -p "$FOUNDRY_BIN_DIR"
curl -sSf -L "$BIN_URL" -o "$BIN_PATH"
chmod +x "$BIN_PATH"

# Create the man directory for future man files if it doesn't exist.
mkdir -p "$FOUNDRY_MAN_DIR"

# Store the correct profile file (i.e. .profile for bash or .zshenv for ZSH).
case $SHELL in
*/zsh)
PROFILE="${ZDOTDIR-"$HOME"}/.zshenv"
PREF_SHELL=zsh
;;
*/bash)
PROFILE=$HOME/.bashrc
PREF_SHELL=bash
;;
*/fish)
PROFILE=$HOME/.config/fish/config.fish
PREF_SHELL=fish
;;
*/ash)
PROFILE=$HOME/.profile
PREF_SHELL=ash
;;
*)
echo "foundryup-zksync: could not detect shell, manually add ${FOUNDRY_BIN_DIR} to your PATH."
exit 1
esac

# Only add foundryup-zksync if it isn't already in PATH.
if [[ ":$PATH:" != *":${FOUNDRY_BIN_DIR}:"* ]]; then
# Add the foundryup directory to the path and ensure the old PATH variables remain.
# If the shell is fish, echo fish_add_path instead of export.
if [[ "$PREF_SHELL" == "fish" ]]; then
echo >> "$PROFILE" && echo "fish_add_path -a $FOUNDRY_BIN_DIR" >> "$PROFILE"
else
echo >> "$PROFILE" && echo "export PATH=\"\$PATH:$FOUNDRY_BIN_DIR\"" >> "$PROFILE"
fi
fi

# Warn MacOS users that they may need to manually install libusb via Homebrew:
if [[ "$OSTYPE" =~ ^darwin ]] && [[ ! -f /usr/local/opt/libusb/lib/libusb-1.0.0.dylib && ! -f /opt/homebrew/opt/libusb/lib/libusb-1.0.0.dylib ]]; then
echo && echo "warning: libusb not found. You may need to install it manually on MacOS via Homebrew (brew install libusb)."
fi

echo
echo "Detected your preferred shell is $PREF_SHELL and added foundryup-zksync to PATH."
echo "Run 'source $PROFILE' or start a new terminal session to use foundryup-zksync."
echo "Then, simply run 'foundryup-zksync' to install foundry-zksync."

0 comments on commit 98de704

Please sign in to comment.