Skip to content

Commit

Permalink
Merge branch 'bridge-next-gen' into ron/transact-from-eth-to-sub
Browse files Browse the repository at this point in the history
  • Loading branch information
yrong committed Apr 16, 2024
2 parents a364298 + 76258e5 commit bdf4c71
Show file tree
Hide file tree
Showing 73 changed files with 3,616 additions and 1,294 deletions.
2 changes: 1 addition & 1 deletion codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ ignore:
- "contracts/src/utils"
- "contracts/src/ScaleCodec.sol"
- "contracts/src/SubstrateTypes.sol"
- "contracts/src/DeployScript.sol"
- "contracts/scripts"
flags:
solidity:
paths:
Expand Down
1 change: 1 addition & 0 deletions contracts/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ optimizer = true
optimizer_runs = 20_000
via_ir = true
test = 'test'
script = 'scripts'
fs_permissions = [{ access = "read-write", path = "test/data"}, { access = "read", path = "./"}]

ignored_error_codes = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
// SPDX-FileCopyrightText: 2023 Snowfork <[email protected]>
pragma solidity 0.8.23;

import {AgentExecutor} from "./AgentExecutor.sol";
import {Gateway} from "./Gateway.sol";
import {ParaID} from "./Types.sol";
import {AgentExecutor} from "../src/AgentExecutor.sol";
import {Gateway} from "../src//Gateway.sol";
import {ParaID} from "../src//Types.sol";
import {Script} from "forge-std/Script.sol";
import {stdJson} from "forge-std/StdJson.sol";

Expand All @@ -24,9 +24,18 @@ contract DeployGatewayLogic is Script {
bytes32 bridgeHubAgentID = vm.envBytes32("BRIDGE_HUB_AGENT_ID");

uint8 foreignTokenDecimals = uint8(vm.envUint("FOREIGN_TOKEN_DECIMALS"));
uint128 maxDestinationFee = uint128(vm.envUint("RESERVE_TRANSFER_MAX_DESTINATION_FEE"));

AgentExecutor executor = new AgentExecutor();
new Gateway(address(beefyClient), address(executor), bridgeHubParaID, bridgeHubAgentID, foreignTokenDecimals);

new Gateway(
address(beefyClient),
address(executor),
bridgeHubParaID,
bridgeHubAgentID,
foreignTokenDecimals,
maxDestinationFee
);

vm.stopBroadcast();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,15 @@ pragma solidity 0.8.23;

import {WETH9} from "canonical-weth/WETH9.sol";
import {Script} from "forge-std/Script.sol";
import {BeefyClient} from "./BeefyClient.sol";

import {IGateway} from "./interfaces/IGateway.sol";
import {GatewayProxy} from "./GatewayProxy.sol";
import {Gateway} from "./Gateway.sol";
import {GatewayUpgradeMock} from "../test/mocks/GatewayUpgradeMock.sol";
import {Agent} from "./Agent.sol";
import {AgentExecutor} from "./AgentExecutor.sol";
import {ChannelID, ParaID, OperatingMode} from "./Types.sol";
import {SafeNativeTransfer} from "./utils/SafeTransfer.sol";
import {BeefyClient} from "../src/BeefyClient.sol";

import {IGateway} from "../src/interfaces/IGateway.sol";
import {GatewayProxy} from "../src/GatewayProxy.sol";
import {Gateway} from "../src/Gateway.sol";
import {Agent} from "../src/Agent.sol";
import {AgentExecutor} from "../src/AgentExecutor.sol";
import {ChannelID, ParaID, OperatingMode} from "../src/Types.sol";
import {SafeNativeTransfer} from "../src/utils/SafeTransfer.sol";
import {stdJson} from "forge-std/StdJson.sol";
import {UD60x18, ud60x18} from "prb/math/src/UD60x18.sol";

Expand Down Expand Up @@ -58,10 +57,16 @@ contract DeployScript is Script {
bytes32 assetHubAgentID = vm.envBytes32("ASSET_HUB_AGENT_ID");

uint8 foreignTokenDecimals = uint8(vm.envUint("FOREIGN_TOKEN_DECIMALS"));
uint128 maxDestinationFee = uint128(vm.envUint("RESERVE_TRANSFER_MAX_DESTINATION_FEE"));

AgentExecutor executor = new AgentExecutor();
Gateway gatewayLogic = new Gateway(
address(beefyClient), address(executor), bridgeHubParaID, bridgeHubAgentID, foreignTokenDecimals
address(beefyClient),
address(executor),
bridgeHubParaID,
bridgeHubAgentID,
foreignTokenDecimals,
maxDestinationFee
);

bool rejectOutboundMessages = vm.envBool("REJECT_OUTBOUND_MESSAGES");
Expand Down Expand Up @@ -99,8 +104,6 @@ contract DeployScript is Script {
payable(bridgeHubAgent).safeNativeTransfer(initialDeposit);
payable(assetHubAgent).safeNativeTransfer(initialDeposit);

new GatewayUpgradeMock();

vm.stopBroadcast();
}
}
19 changes: 9 additions & 10 deletions contracts/src/FundAgent.sol → contracts/scripts/FundAgent.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,15 @@ pragma solidity 0.8.23;

import {WETH9} from "canonical-weth/WETH9.sol";
import {Script} from "forge-std/Script.sol";
import {BeefyClient} from "./BeefyClient.sol";

import {IGateway} from "./interfaces/IGateway.sol";
import {GatewayProxy} from "./GatewayProxy.sol";
import {Gateway} from "./Gateway.sol";
import {GatewayUpgradeMock} from "../test/mocks/GatewayUpgradeMock.sol";
import {Agent} from "./Agent.sol";
import {AgentExecutor} from "./AgentExecutor.sol";
import {ParaID} from "./Types.sol";
import {SafeNativeTransfer} from "./utils/SafeTransfer.sol";
import {BeefyClient} from "../src/BeefyClient.sol";

import {IGateway} from "../src/interfaces/IGateway.sol";
import {GatewayProxy} from "../src/GatewayProxy.sol";
import {Gateway} from "../src/Gateway.sol";
import {Agent} from "../src/Agent.sol";
import {AgentExecutor} from "../src/AgentExecutor.sol";
import {ParaID} from "../src/Types.sol";
import {SafeNativeTransfer} from "../src/utils/SafeTransfer.sol";
import {stdJson} from "forge-std/StdJson.sol";

contract FundAgent is Script {
Expand Down
31 changes: 23 additions & 8 deletions contracts/src/Assets.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,21 +42,22 @@ library Assets {
IERC20(token).safeTransferFrom(sender, agent, amount);
}

function sendTokenCosts(address token, ParaID destinationChain, uint128 destinationChainFee)
external
view
returns (Costs memory costs)
{
function sendTokenCosts(
address token,
ParaID destinationChain,
uint128 destinationChainFee,
uint128 maxDestinationChainFee
) external view returns (Costs memory costs) {
AssetsStorage.Layout storage $ = AssetsStorage.layout();
TokenInfo storage info = $.tokenRegistry[token];
if (!info.isRegistered) {
revert TokenNotRegistered();
}

return _sendTokenCosts(destinationChain, destinationChainFee);
return _sendTokenCosts(destinationChain, destinationChainFee, maxDestinationChainFee);
}

function _sendTokenCosts(ParaID destinationChain, uint128 destinationChainFee)
function _sendTokenCosts(ParaID destinationChain, uint128 destinationChainFee, uint128 maxDestinationChainFee)
internal
view
returns (Costs memory costs)
Expand All @@ -65,6 +66,19 @@ library Assets {
if ($.assetHubParaID == destinationChain) {
costs.foreign = $.assetHubReserveTransferFee;
} else {
// Reduce the ability for users to perform arbitrage by exploiting a
// favourable exchange rate. For example supplying Ether
// and gaining a more valuable amount of DOT on the destination chain.
//
// Also prevents users from mistakenly sending more fees than would be required
// which has negative effects like draining AssetHub's sovereign account.
//
// For safety, `maxDestinationChainFee` should be less valuable
// than the gas cost to send tokens.
if (destinationChainFee > maxDestinationChainFee) {
revert InvalidDestinationFee();
}

// If the final destination chain is not AssetHub, then the fee needs to additionally
// include the cost of executing an XCM on the final destination parachain.
costs.foreign = $.assetHubReserveTransferFee + destinationChainFee;
Expand All @@ -79,6 +93,7 @@ library Assets {
ParaID destinationChain,
MultiAddress calldata destinationAddress,
uint128 destinationChainFee,
uint128 maxDestinationChainFee,
uint128 amount
) external returns (Ticket memory ticket) {
AssetsStorage.Layout storage $ = AssetsStorage.layout();
Expand All @@ -92,7 +107,7 @@ library Assets {
_transferToAgent($.assetHubAgent, token, sender, amount);

ticket.dest = $.assetHubParaID;
ticket.costs = _sendTokenCosts(destinationChain, destinationChainFee);
ticket.costs = _sendTokenCosts(destinationChain, destinationChainFee, maxDestinationChainFee);

// Construct a message payload
if (destinationChain == $.assetHubParaID) {
Expand Down
9 changes: 9 additions & 0 deletions contracts/src/BeefyClient.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ contract BeefyClient {
*/
event NewMMRRoot(bytes32 mmrRoot, uint64 blockNumber);

/**
* @dev Emitted when a new ticket has been created
* @param relayer The relayer who created the ticket
* @param blockNumber the parent block number of the candidate MMR root
*/
event NewTicket(address relayer, uint64 blockNumber);

/* Types */

/**
Expand Down Expand Up @@ -289,6 +296,8 @@ contract BeefyClient {
prevRandao: 0,
bitfieldHash: keccak256(abi.encodePacked(bitfield))
});

emit NewTicket(msg.sender, commitment.blockNumber);
}

/**
Expand Down
52 changes: 19 additions & 33 deletions contracts/src/Gateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ import {
OriginKind,
Weight
} from "./Types.sol";
import {Upgrade} from "./Upgrade.sol";
import {IGateway} from "./interfaces/IGateway.sol";
import {IInitializable} from "./interfaces/IInitializable.sol";
import {IUpgradable} from "./interfaces/IUpgradable.sol";
import {ERC1967} from "./utils/ERC1967.sol";
import {Address} from "./utils/Address.sol";
import {SafeNativeTransfer} from "./utils/SafeTransfer.sol";
Expand All @@ -49,7 +51,7 @@ import {AssetsStorage} from "./storage/AssetsStorage.sol";

import {UD60x18, ud60x18, convert} from "prb/math/src/UD60x18.sol";

contract Gateway is IGateway, IInitializable {
contract Gateway is IGateway, IInitializable, IUpgradable {
using Address for address;
using SafeNativeTransfer for address payable;

Expand All @@ -72,6 +74,13 @@ contract Gateway is IGateway, IInitializable {
// 2. Calling implementation function
uint256 DISPATCH_OVERHEAD_GAS = 10_000;

// The maximum fee that can be sent to a destination parachain to pay for execution (DOT).
// Has two functions:
// * Reduces the ability of users to perform arbitrage using a favourable exchange rate
// * Prevents users from mistakenly providing too much fees, which would drain AssetHub's
// sovereign account here on Ethereum.
uint128 internal immutable MAX_DESTINATION_FEE;

uint8 internal immutable FOREIGN_TOKEN_DECIMALS;

error InvalidProof();
Expand All @@ -87,12 +96,11 @@ contract Gateway is IGateway, IInitializable {
error InvalidChannelUpdate();
error AgentExecutionFailed(bytes returndata);
error InvalidAgentExecutionPayload();
error InvalidCodeHash();
error InvalidConstructorParams();
error AlreadyInitialized();
error InvalidTransact();

// handler functions are privileged
// Message handlers can only be dispatched by the gateway itself
modifier onlySelf() {
if (msg.sender != address(this)) {
revert Unauthorized();
Expand All @@ -105,7 +113,8 @@ contract Gateway is IGateway, IInitializable {
address agentExecutor,
ParaID bridgeHubParaID,
bytes32 bridgeHubAgentID,
uint8 foreignTokenDecimals
uint8 foreignTokenDecimals,
uint128 maxDestinationFee
) {
if (bridgeHubParaID == ParaID.wrap(0) || bridgeHubAgentID == 0) {
revert InvalidConstructorParams();
Expand All @@ -117,6 +126,7 @@ contract Gateway is IGateway, IInitializable {
BRIDGE_HUB_PARA_ID = bridgeHubParaID;
BRIDGE_HUB_AGENT_ID = bridgeHubAgentID;
FOREIGN_TOKEN_DECIMALS = foreignTokenDecimals;
MAX_DESTINATION_FEE = maxDestinationFee;
}

/// @dev Submit a message from Polkadot for verification and dispatch
Expand Down Expand Up @@ -335,29 +345,7 @@ contract Gateway is IGateway, IInitializable {
/// @dev Perform an upgrade of the gateway
function upgrade(bytes calldata data) external onlySelf {
UpgradeParams memory params = abi.decode(data, (UpgradeParams));

// Verify that the implementation is actually a contract
if (!params.impl.isContract()) {
revert InvalidCodeHash();
}

// As a sanity check, ensure that the codehash of implementation contract
// matches the codehash in the upgrade proposal
if (params.impl.codehash != params.implCodeHash) {
revert InvalidCodeHash();
}

// Update the proxy with the address of the new implementation
ERC1967.store(params.impl);

// Apply the initialization function of the implementation only if params were provided
if (params.initParams.length > 0) {
(bool success, bytes memory returndata) =
params.impl.delegatecall(abi.encodeCall(IInitializable.initialize, params.initParams));
Call.verifyResult(success, returndata);
}

emit Upgraded(params.impl);
Upgrade.upgrade(params.impl, params.implCodeHash, params.initParams);
}

// @dev Set the operating mode of the gateway
Expand Down Expand Up @@ -421,7 +409,7 @@ contract Gateway is IGateway, IInitializable {
view
returns (uint256)
{
return _calculateFee(Assets.sendTokenCosts(token, destinationChain, destinationFee));
return _calculateFee(Assets.sendTokenCosts(token, destinationChain, destinationFee, MAX_DESTINATION_FEE));
}

// Transfer ERC20 tokens to a Polkadot parachain
Expand All @@ -433,7 +421,9 @@ contract Gateway is IGateway, IInitializable {
uint128 amount
) external payable {
_submitOutbound(
Assets.sendToken(token, msg.sender, destinationChain, destinationAddress, destinationFee, amount)
Assets.sendToken(
token, msg.sender, destinationChain, destinationAddress, destinationFee, MAX_DESTINATION_FEE, amount
)
);
}

Expand Down Expand Up @@ -591,10 +581,6 @@ contract Gateway is IGateway, IInitializable {

CoreStorage.Layout storage core = CoreStorage.layout();

if (core.channels[PRIMARY_GOVERNANCE_CHANNEL_ID].agent != address(0)) {
revert AlreadyInitialized();
}

Config memory config = abi.decode(data, (Config));

core.mode = config.mode;
Expand Down
29 changes: 29 additions & 0 deletions contracts/src/Shell.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023 Snowfork <[email protected]>
pragma solidity 0.8.23;

import {Upgrade} from "./Upgrade.sol";
import {IInitializable} from "./interfaces/IInitializable.sol";
import {IUpgradable} from "./interfaces/IUpgradable.sol";
import {IShell} from "./interfaces/IShell.sol";

// address recoveryOperator = vm.envOr("RECOVERY_OPERATOR", address(0));

contract Shell is IShell, IUpgradable, IInitializable {
address public immutable operator;

error Unauthorised();

constructor(address _operator) {
operator = _operator;
}

function upgrade(address impl, bytes32 implCodeHash, bytes calldata initializerParams) external {
if (msg.sender != operator) {
revert Unauthorised();
}
Upgrade.upgrade(impl, implCodeHash, initializerParams);
}

function initialize(bytes memory params) external {}
}
Loading

0 comments on commit bdf4c71

Please sign in to comment.