Skip to content

Commit

Permalink
WIP multichain
Browse files Browse the repository at this point in the history
  • Loading branch information
dmax10 committed Jan 30, 2025
1 parent 233f187 commit 48ee48c
Show file tree
Hide file tree
Showing 11 changed files with 237 additions and 50 deletions.
8 changes: 5 additions & 3 deletions contracts/data/Keys.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2098,13 +2098,15 @@ library Keys {
}

// @dev key for user's balance for a source chain, recorded under the user's virtual account
// @param virtualAccount the virtual account for which to retreive the user balance key
// @param chianId the chain id for the source chain
// @param account the account for which to retreive the user balance key
// @param token the token for which to retreive the user balance key
// @return key for a source chain balance for a given user and token
function sourceChainBalanceKey(address virtualAccount, address token) internal pure returns (bytes32) {
function sourceChainBalanceKey(uint256 chainId, address account, address token) internal pure returns (bytes32) {
return keccak256(abi.encode(
SOURCE_CHAIN_BALANCE,
virtualAccount,
chainId,
account,
token
));
}
Expand Down
5 changes: 3 additions & 2 deletions contracts/multichain/LayerZeroProvider.sol
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,12 @@ contract LayerZeroProvider is IMultichainProvider, ILayerZeroComposer {

_transferToVault(token, address(multichainVault));

address virtualAccount = multichainHandler.recordDeposit(account, token, sourceChainId);
multichainHandler.recordDeposit(account, token, sourceChainId);

LayerZeroProviderEventUtils.emitComposedMessageReceived(
eventEmitter,
virtualAccount,
sourceChainId,
account,
from,
guid,
message,
Expand Down
18 changes: 12 additions & 6 deletions contracts/multichain/LayerZeroProviderEventUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ library LayerZeroProviderEventUtils {

function emitComposedMessageReceived(
EventEmitter eventEmitter,
address virtualAccount,
uint256 sourceChainId,
address account,
address from,
bytes32 guid,
bytes calldata message,
Expand All @@ -27,9 +28,12 @@ library LayerZeroProviderEventUtils {
EventUtils.EventLogData memory eventData;

eventData.addressItems.initItems(3);
eventData.addressItems.setItem(0, "virtualAccount", virtualAccount);
eventData.addressItems.setItem(0, "account", account);
eventData.addressItems.setItem(1, "from", from);
eventData.addressItems.setItem(2, "executor", executor);

eventData.uintItems.initItems(1);
eventData.uintItems.setItem(0, "sourceChainId", sourceChainId);

eventData.bytes32Items.initItems(1);
eventData.bytes32Items.setItem(0, "guid", guid);
Expand All @@ -38,12 +42,13 @@ library LayerZeroProviderEventUtils {
eventData.bytesItems.setItem(0, "message", message);
eventData.bytesItems.setItem(1, "extraData", extraData);

eventEmitter.emitEventLog1("MessageComposedReceived", Cast.toBytes32(virtualAccount), eventData);
eventEmitter.emitEventLog2("MessageComposedReceived", bytes32(sourceChainId), Cast.toBytes32(account), eventData);
}

function emitWithdrawalReceipt(
EventEmitter eventEmitter,
address virtualAccount,
uint256 sourceChainId,
address account,
bytes32 guid,
uint64 nonce,
uint256 nativeFee,
Expand All @@ -53,16 +58,17 @@ library LayerZeroProviderEventUtils {
) internal {
EventUtils.EventLogData memory eventData;

eventData.uintItems.initItems(5);
eventData.uintItems.initItems(6);
eventData.uintItems.setItem(0, "nonce", uint256(nonce));
eventData.uintItems.setItem(1, "nativeFee", nativeFee);
eventData.uintItems.setItem(2, "lzTokenFee", lzTokenFee);
eventData.uintItems.setItem(3, "amountSentLD", amountSentLD);
eventData.uintItems.setItem(4, "amountReceivedLD", amountReceivedLD);
eventData.uintItems.setItem(5, "sourceChainId", sourceChainId);

eventData.bytes32Items.initItems(1);
eventData.bytes32Items.setItem(0, "guid", guid);

eventEmitter.emitEventLog1("WithdrawalReceipt", Cast.toBytes32(virtualAccount), eventData);
eventEmitter.emitEventLog2("WithdrawalReceipt", bytes32(sourceChainId), Cast.toBytes32(account), eventData);
}
}
18 changes: 9 additions & 9 deletions contracts/multichain/MultichainEventUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,56 +16,56 @@ library MultichainEventUtils {
function emitMultichainDeposit(
EventEmitter eventEmitter,
address token,
address virtualAccount,
address account,
uint256 amount,
uint256 sourceChainId
) internal {
EventUtils.EventLogData memory eventData;

eventData.addressItems.initItems(2);
eventData.addressItems.setItem(0, "token", token);
eventData.addressItems.setItem(1, "virtualAccount", virtualAccount);
eventData.addressItems.setItem(1, "account", account);

eventData.uintItems.initItems(2);
eventData.uintItems.setItem(0, "amount", amount);
eventData.uintItems.setItem(1, "sourceChainId", sourceChainId);

eventEmitter.emitEventLog1("MultichainDeposit", Cast.toBytes32(virtualAccount), eventData);
eventEmitter.emitEventLog2("MultichainDeposit", bytes32(sourceChainId), Cast.toBytes32(account), eventData);
}

function emitMultichainMessage(
EventEmitter eventEmitter,
address virtualAccount,
address account,
uint256 sourceChainId
) internal {
EventUtils.EventLogData memory eventData;

eventData.addressItems.initItems(1);
eventData.addressItems.setItem(0, "virtualAccount", virtualAccount);
eventData.addressItems.setItem(0, "account", account);

eventData.uintItems.initItems(1);
eventData.uintItems.setItem(0, "sourceChainId", sourceChainId);

eventEmitter.emitEventLog1("MultichainMessage", Cast.toBytes32(virtualAccount), eventData);
eventEmitter.emitEventLog1("MultichainMessage", Cast.toBytes32(account), eventData);
}

function emitMultichainWithdrawal(
EventEmitter eventEmitter,
address token,
address virtualAccount,
address account,
uint256 amount,
uint256 sourceChainId
) internal {
EventUtils.EventLogData memory eventData;

eventData.addressItems.initItems(2);
eventData.addressItems.setItem(0, "token", token);
eventData.addressItems.setItem(1, "virtualAccount", virtualAccount);
eventData.addressItems.setItem(1, "account", account);

eventData.uintItems.initItems(2);
eventData.uintItems.setItem(0, "amount", amount);
eventData.uintItems.setItem(1, "sourceChainId", sourceChainId);

eventEmitter.emitEventLog1("MultichainWithdrawal", Cast.toBytes32(virtualAccount), eventData);
eventEmitter.emitEventLog2("MultichainWithdrawal", bytes32(sourceChainId), Cast.toBytes32(account), eventData);
}
}
37 changes: 26 additions & 11 deletions contracts/multichain/MultichainHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import { RoleStore } from "../role/RoleStore.sol";
import { RoleModule } from "../role/RoleModule.sol";
import { DataStore } from "../data/DataStore.sol";
Expand All @@ -21,6 +24,8 @@ import { MultichainEventUtils } from "./MultichainEventUtils.sol";
* @title MultichainHandler
*/
contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule {
using SafeERC20 for IERC20;

MultichainVault public multichainVault;
EventEmitter public eventEmitter;
ExchangeRouter public exchangeRouter;
Expand All @@ -39,7 +44,7 @@ contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule {
}

/**
* Records a deposit from another chain. IMultichainProvider has MULTICHAIN_CONTROLLER role
* Records a deposit from another chain. IMultichainProvider has CONTROLLER role
* @param account user address on the source chain
* @param token address of the token being deposited
* @param sourceChainId chain id of the source chain
Expand All @@ -48,18 +53,30 @@ contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule {
address account,
address token,
uint256 sourceChainId
) external onlyController returns (address virtualAccount) {
) external onlyController {
// token should have been transferred to multichainVault by IMultichainProvider
uint256 amount = multichainVault.recordTransferIn(token);
if (amount == 0) {
revert Errors.EmptyMultichainDepositAmount();
}

virtualAccount = MultichainUtils.getVirtualAccount(account, sourceChainId);
dataStore.incrementUint(Keys.sourceChainBalanceKey(sourceChainId, account, token), amount);

dataStore.incrementUint(Keys.sourceChainBalanceKey(virtualAccount, token), amount);
MultichainEventUtils.emitMultichainDeposit(eventEmitter, token, account, amount, sourceChainId);
}

/**
* @dev transfer the specified amount of tokens from account to receiver
* @param account the account for which the tokens are subtracted
* @param token the token to transfer
* @param receiver the account to transfer to
* @param amount the amount of tokens to transfer
*/
function pluginTransfer(address token, address account, address receiver, uint256 amount) external onlyRouterPlugin { // TODO: confirm access control
// TODO: tokens should come from MultichainVault and the user's multichain balance should be decreased
// dataStore.decrementUint(Keys.sourceChainBalanceKey(sourceChainId, account, token), amount);

MultichainEventUtils.emitMultichainDeposit(eventEmitter, token, virtualAccount, amount, sourceChainId);
IERC20(token).safeTransferFrom(account, receiver, amount);
}

/**
Expand All @@ -77,12 +94,11 @@ contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule {
// execute multicall
exchangeRouter.multicall(multicallArgs);

address virtualAccount = MultichainUtils.getVirtualAccount(account, sourceChainId);
MultichainEventUtils.emitMultichainMessage(eventEmitter, virtualAccount, sourceChainId);
MultichainEventUtils.emitMultichainMessage(eventEmitter, account, sourceChainId);
}

/**
* Record a withdrawal to another chain. IMultichainProvider has MULTICHAIN_CONTROLLER role
* Record a withdrawal to another chain. IMultichainProvider has CONTROLLER role
* @param account user address on the source chain
* @param token address of the token being withdrawn
* @param amount amount of token being withdrawn
Expand All @@ -98,8 +114,7 @@ contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule {
revert Errors.EmptyMultichainWithdrawalAmount();
}

address virtualAccount = MultichainUtils.getVirtualAccount(account, sourceChainId);
bytes32 balanceKey = Keys.sourceChainBalanceKey(virtualAccount, token);
bytes32 balanceKey = Keys.sourceChainBalanceKey(sourceChainId, account, token);

uint256 balance = dataStore.getUint(balanceKey);
if (balance < amount) {
Expand All @@ -112,6 +127,6 @@ contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule {
// transfer tokens to IMultichainProvider
multichainVault.transferOut(token, msg.sender, amount);

MultichainEventUtils.emitMultichainWithdrawal(eventEmitter, token, virtualAccount, amount, sourceChainId);
MultichainEventUtils.emitMultichainWithdrawal(eventEmitter, token, account, amount, sourceChainId);
}
}
Loading

0 comments on commit 48ee48c

Please sign in to comment.