Skip to content

Commit

Permalink
Feat/basic tests (#4)
Browse files Browse the repository at this point in the history
* test coverage on local vault bridge requests and attestation flow

* e2e permissioned vault test coverage with fork-test

* add setup for fork-testing eigenlayer bridge
  • Loading branch information
idatsy authored Jun 16, 2024
1 parent df235b9 commit 0528cb1
Show file tree
Hide file tree
Showing 5 changed files with 223 additions and 12 deletions.
9 changes: 6 additions & 3 deletions src/PermissionedBridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import "./ECDSAUtils.sol";
import "./Vault.sol";


contract PermissionedVault is Vault {
contract PermissionedBridge is Vault {
using Structs for Structs.BridgeRequestData;

/// @notice Tracks bridge requests that this operator has responded to once to avoid duplications
Expand Down Expand Up @@ -67,12 +67,11 @@ contract PermissionedVault is Vault {

/// @notice Convenience function for releasing funds to the destination address with typed data for ABI construction
/// @dev NOTE: this function is only callable by the owner and always releases the funds to the destination address
function releaseFunds(bytes[] memory signatures, Structs.BridgeRequestData memory data) public nonReentrant onlyOwner {
function releaseFunds(bytes[] memory signatures, Structs.BridgeRequestData memory data) public nonReentrant {
// Verify each signature and sum the operator weights
uint256 totalWeight = 0;
for (uint256 i = 0; i < signatures.length; i++) {
address signer = getSigner(data, signatures[i]);
require(operatorResponses[signer][data.transferIndex], "Invalid signature");
totalWeight += getOperatorWeight(signer);
}

Expand All @@ -97,5 +96,9 @@ contract PermissionedVault is Vault {
return operatorWeights[operator];
}

function setOperatorWeight(address operator, uint256 weight) public onlyOwner {
operatorWeights[operator] = weight;
}

receive() external payable {}
}
9 changes: 9 additions & 0 deletions src/Vault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,21 @@ abstract contract Vault is ECDSAUtils, Events, ReentrancyGuard, OwnableUpgradeab
// Estimated gas cost for calling release funds, used to calculate rebate and incentivise users to call
uint256 public crankGasCost;

address deployer;

constructor(
uint256 _crankGasCost, uint256 _AVSReward, uint256 _bridgeFee, string memory _name, string memory _version
) ECDSAUtils(_name, _version) {
crankGasCost = _crankGasCost;
AVSReward = _AVSReward;
bridgeFee = _bridgeFee;

deployer = msg.sender;
}

function initialize() public initializer {
__Ownable_init();
transferOwnership(deployer);
}

/* Access control functions and fee setters */
Expand Down
55 changes: 55 additions & 0 deletions test/EigenLayerBridge.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Test, console} from "forge-std/Test.sol";

import {BridgeServiceManager} from "../src/EigenLayerBridge.sol";
import "../src/Events.sol";
import {Structs} from "../src/Structs.sol";

import "openzeppelin/contracts/utils/cryptography/EIP712.sol";

contract BridgeServiceManagerTest is Test, EIP712("BridgeServiceManager", "1") {
BridgeServiceManager public localVault;
BridgeServiceManager public remoteVault;

address usdc = address(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);

uint256 bridgeFee = 0.005 ether;
uint256 crankGasCost = 100_000;

address bob = address(0x123);
address alice = address(0x456);
address operator;
uint256 operatorPrivateKey;

// MAINNET EIGENLAYER CONTRACTS
// https://github.com/Layr-Labs/eigenlayer-contracts?tab=readme-ov-file#deployments
address delegationManager = address(0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A);
address aVSDirectory = address(0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A);
///https://github.com/Layr-Labs/eigenlayer-middleware/tree/mainnet
address rewardsCoordinator = address(0x0BAAc79acD45A023E19345c352d8a7a83C4e5656);
address stakeRegistry = address(0x006124Ae7976137266feeBFb3F4D2BE4C073139D);

function setUp() public {
(operator, operatorPrivateKey) = makeAddrAndKey("operator");

localVault = new BridgeServiceManager(
aVSDirectory, stakeRegistry, rewardsCoordinator, delegationManager,
crankGasCost, 0, bridgeFee, "PermissionedBridge", "1"
);
localVault.initialize();

remoteVault = new BridgeServiceManager(
aVSDirectory, stakeRegistry, rewardsCoordinator, delegationManager,
crankGasCost, 0, bridgeFee, "PermissionedBridge", "1"
);
remoteVault.initialize();

deal(bob, 1 ether);
deal(usdc, bob, 1000 * 10**6);
deal(usdc, address(remoteVault), 1000 * 10**6);

deal(operator, 1 ether);
}
}
153 changes: 153 additions & 0 deletions test/PermissionedBridge.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Test, console} from "forge-std/Test.sol";

import "../src/PermissionedBridge.sol";
import "../src/Events.sol";
import {Structs} from "../src/Structs.sol";

import "openzeppelin/contracts/utils/cryptography/EIP712.sol";

contract PermissionedBridgeTest is Test, EIP712("PermissionedBridge", "1") {
PermissionedBridge public localVault;
PermissionedBridge public remoteVault;

address usdc = address(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);

uint256 bridgeFee = 0.005 ether;
uint256 crankGasCost = 100_000;

address bob = address(0x123);
address alice = address(0x456);
address operator;
uint256 operatorPrivateKey;

function setUp() public {
(operator, operatorPrivateKey) = makeAddrAndKey("operator");

localVault = new PermissionedBridge(crankGasCost, 0, bridgeFee, "PermissionedBridge", "1");
localVault.initialize();
remoteVault = new PermissionedBridge(crankGasCost, 0, bridgeFee, "PermissionedBridge", "1");
remoteVault.initialize();

deal(bob, 1 ether);
deal(usdc, bob, 1000 * 10**6);
deal(usdc, address(remoteVault), 1000 * 10**6);

deal(operator, 1 ether);
localVault.setOperatorWeight(operator, 1000 ether);
remoteVault.setOperatorWeight(operator, 1000 ether);
}

/// @dev Util function for signing bridge request data. This should be mirrored on frontends and operator implementations.
function signBridgeRequestData(
PermissionedBridge forVault, Structs.BridgeRequestData memory data, uint256 pkey
) public returns (bytes memory) {
bytes32 digest = forVault.getDigest(data);

(uint8 v1, bytes32 r1, bytes32 s1) = vm.sign(
pkey,
digest
);

return abi.encodePacked(r1, s1, v1);
}

function testBridgeRequestEmitEvents() public returns (Structs.BridgeRequestData memory) {
vm.startPrank(bob);

IERC20(usdc).approve(address(localVault), 1000 * 10**6);

vm.expectEmit(true, true, true, true);
emit Events.BridgeRequest(bob, usdc, 0, 1000 * 10**6, 1000 * 10**6, address(remoteVault), alice, 0);

localVault.bridge{value: bridgeFee}(usdc, 1000 * 10**6, 1000 * 10**6, address(remoteVault), alice);

vm.stopPrank();

return Structs.BridgeRequestData(
bob,
usdc,
1000 * 10**6,
1000 * 10**6,
address(remoteVault),
alice,
0
);
}

function testOperatorCanSubmitAttestation() public {
testBridgeRequestEmitEvents();

(
address user,
address tokenAddress,
uint256 amountIn,
uint256 amountOut,
address destinationVault,
address destinationAddress,
uint256 transferIndex
) = localVault.bridgeRequests(0);

Structs.BridgeRequestData memory bridgeRequest = Structs.BridgeRequestData(
user,
tokenAddress,
amountIn,
amountOut,
destinationVault,
destinationAddress,
transferIndex
);

// NOTE: This is signed against the local vault in this example, but for cross-chain swaps this would need to
// be signed against the remoteVault as the chainId is part of the EIP712 signing domain!!
bytes memory attestation = signBridgeRequestData(localVault, bridgeRequest, operatorPrivateKey);


vm.prank(operator);
vm.expectEmit(true, true, true, true);
emit Events.AVSAttestation(attestation, 0);

localVault.publishAttestation(attestation, 0);
}

function testBridgeCompletesReleaseFunds() public {
testBridgeRequestEmitEvents();

(
address user,
address tokenAddress,
uint256 amountIn,
uint256 amountOut,
address destinationVault,
address destinationAddress,
uint256 transferIndex
) = localVault.bridgeRequests(0);

Structs.BridgeRequestData memory bridgeRequest = Structs.BridgeRequestData(
user,
tokenAddress,
amountIn,
amountOut,
destinationVault,
destinationAddress,
transferIndex
);

// NOTE: This is signed against the local vault in this example, but for cross-chain swaps this would need to
// be signed against the remoteVault as the chainId is part of the EIP712 signing domain!!
bytes memory attestation = signBridgeRequestData(remoteVault, bridgeRequest, operatorPrivateKey);
bytes[] memory bridgeRequestSignatures = new bytes[](1);
bridgeRequestSignatures[0] = attestation;

assertEq(IERC20(usdc).balanceOf(address(remoteVault)), 1000 * 10**6);
assertEq(IERC20(usdc).balanceOf(alice), 0);

vm.prank(alice);
remoteVault.releaseFunds(bridgeRequestSignatures, bridgeRequest);

assertEq(IERC20(usdc).balanceOf(address(remoteVault)), 0);
assertEq(IERC20(usdc).balanceOf(alice), 1000 * 10**6);
}
}
9 changes: 0 additions & 9 deletions test/VaultAVS.t.sol

This file was deleted.

0 comments on commit 0528cb1

Please sign in to comment.