Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Native ETH in v1 #1354

Merged
merged 35 commits into from
Jan 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
2c5e273
initial support
alistair-singh Dec 12, 2024
3f178ef
add error
alistair-singh Dec 17, 2024
5887da7
fixed tests
alistair-singh Dec 17, 2024
4677fe7
test legacy and new unlock messages
alistair-singh Dec 17, 2024
13b59fb
fix spelling
alistair-singh Dec 17, 2024
6d6f3fc
test registration is blocked
alistair-singh Dec 17, 2024
299c197
fix inbound messages
alistair-singh Dec 17, 2024
5aad78d
allow gateway proxy to receive funds
alistair-singh Dec 17, 2024
dfcfb6c
final tests and fixes
alistair-singh Dec 17, 2024
3c0d826
warnings
alistair-singh Dec 17, 2024
1212459
PR feedback
alistair-singh Dec 18, 2024
e75906a
migrate ether on upgrade
alistair-singh Dec 18, 2024
182d6ed
fix scripts
alistair-singh Dec 18, 2024
61c52b0
update bindings
alistair-singh Dec 18, 2024
db45375
e2e tests
alistair-singh Dec 19, 2024
a44d093
fix assert
alistair-singh Dec 19, 2024
9b67398
duplicated error removal
alistair-singh Dec 19, 2024
2b9173e
fix test fixture generator
alistair-singh Dec 20, 2024
e209869
template fixes
alistair-singh Dec 20, 2024
bb55b2a
contract address changes
alistair-singh Dec 21, 2024
7359dba
contract address change
alistair-singh Dec 24, 2024
366c457
Ignore AgentTransferFromNative bridge command (#1359)
alistair-singh Jan 7, 2025
3ac9646
fmt
alistair-singh Jan 10, 2025
aaa8139
merge conflicts
alistair-singh Jan 10, 2025
a12492b
Rename and remove parameter
alistair-singh Jan 16, 2025
a149663
added sendEth sanity check
alistair-singh Jan 16, 2025
566f2fb
register ether on contruction and migration
alistair-singh Jan 16, 2025
6ed7140
remove extra branches
alistair-singh Jan 16, 2025
f86a6d8
simplify math
alistair-singh Jan 16, 2025
bd1548e
separate branches for ether and native tokens
alistair-singh Jan 16, 2025
e45c9ee
fix comments
alistair-singh Jan 16, 2025
2699ebe
factor out emitting of sendToken
alistair-singh Jan 17, 2025
ddad89f
comment
alistair-singh Jan 22, 2025
b972591
Refactor
vgeddes Jan 23, 2025
eed9eb1
Improve validation of passed ether
vgeddes Jan 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 3 additions & 7 deletions contracts/scripts/DeployLocal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -96,15 +96,11 @@ contract DeployLocal is Script {
// Deploy WETH for testing
new WETH9();

// Fund the sovereign account for the BridgeHub parachain. Used to reward relayers
// Fund the gateway proxy contract. Used to reward relayers
// of messages originating from BridgeHub
uint256 initialDeposit = vm.envUint("BRIDGE_HUB_INITIAL_DEPOSIT");
uint256 initialDeposit = vm.envUint("GATEWAY_PROXY_INITIAL_DEPOSIT");

address bridgeHubAgent = IGateway(address(gateway)).agentOf(bridgeHubAgentID);
address assetHubAgent = IGateway(address(gateway)).agentOf(assetHubAgentID);

payable(bridgeHubAgent).safeNativeTransfer(initialDeposit);
payable(assetHubAgent).safeNativeTransfer(initialDeposit);
IGateway(address(gateway)).depositEther{value: initialDeposit}();

// Deploy MockGatewayV2 for testing
new MockGatewayV2();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {ParaID} from "../src/Types.sol";
import {SafeNativeTransfer} from "../src/utils/SafeTransfer.sol";
import {stdJson} from "forge-std/StdJson.sol";

contract FundAgent is Script {
contract FundGateway is Script {
using SafeNativeTransfer for address payable;
using stdJson for string;

Expand All @@ -26,17 +26,10 @@ contract FundAgent is Script {
address deployer = vm.rememberKey(privateKey);
vm.startBroadcast(deployer);

uint256 initialDeposit = vm.envUint("BRIDGE_HUB_INITIAL_DEPOSIT");
uint256 initialDeposit = vm.envUint("GATEWAY_PROXY_INITIAL_DEPOSIT");
address gatewayAddress = vm.envAddress("GATEWAY_PROXY_CONTRACT");

bytes32 bridgeHubAgentID = vm.envBytes32("BRIDGE_HUB_AGENT_ID");
bytes32 assetHubAgentID = vm.envBytes32("ASSET_HUB_AGENT_ID");

address bridgeHubAgent = IGateway(gatewayAddress).agentOf(bridgeHubAgentID);
address assetHubAgent = IGateway(gatewayAddress).agentOf(assetHubAgentID);

payable(bridgeHubAgent).safeNativeTransfer(initialDeposit);
payable(assetHubAgent).safeNativeTransfer(initialDeposit);
IGateway(address(gatewayAddress)).depositEther{value: initialDeposit}();

vm.stopBroadcast();
}
Expand Down
33 changes: 33 additions & 0 deletions contracts/scripts/upgrades/polkadot/DeployGateway202502.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023 Snowfork <[email protected]>
pragma solidity 0.8.28;

import {WETH9} from "canonical-weth/WETH9.sol";
import {Script} from "forge-std/Script.sol";
import {BeefyClient} from "../../../src/BeefyClient.sol";
import {AgentExecutor} from "../../../src/AgentExecutor.sol";
import {ParaID} from "../../../src/Types.sol";
import {Gateway202502} from "../../../src/upgrades/Gateway202502.sol";
import {SafeNativeTransfer} from "../../../src/utils/SafeTransfer.sol";
import {stdJson} from "forge-std/StdJson.sol";
import {UD60x18, ud60x18} from "prb/math/src/UD60x18.sol";

contract DeployGateway202502 is Script {
address public constant BEEFY_CLIENT_ADDRESS = 0x6eD05bAa904df3DE117EcFa638d4CB84e1B8A00C;

function run() public {
vm.startBroadcast();

AgentExecutor executor = new AgentExecutor();
Gateway202502 gatewayLogic = new Gateway202502(
BEEFY_CLIENT_ADDRESS,
address(executor),
ParaID.wrap(1002),
0x03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314,
10,
20_000_000_000 // 2 DOT
);

vm.stopBroadcast();
}
}
33 changes: 33 additions & 0 deletions contracts/scripts/upgrades/westend/DeployGateway202502.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023 Snowfork <[email protected]>
pragma solidity 0.8.28;

import {WETH9} from "canonical-weth/WETH9.sol";
import {Script} from "forge-std/Script.sol";
import {BeefyClient} from "../../../src/BeefyClient.sol";
import {AgentExecutor} from "../../../src/AgentExecutor.sol";
import {ParaID} from "../../../src/Types.sol";
import {Gateway202502} from "../../../src/upgrades/Gateway202502.sol";
import {SafeNativeTransfer} from "../../../src/utils/SafeTransfer.sol";
import {stdJson} from "forge-std/StdJson.sol";
import {UD60x18, ud60x18} from "prb/math/src/UD60x18.sol";

contract DeployGateway202502 is Script {
address public constant BEEFY_CLIENT_ADDRESS = 0x6DFaD3D73A28c48E4F4c616ECda80885b415283a;

function run() public {
vm.startBroadcast();

AgentExecutor executor = new AgentExecutor();
Gateway202502 gatewayLogic = new Gateway202502(
BEEFY_CLIENT_ADDRESS,
address(executor),
ParaID.wrap(1002),
0x03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314,
12,
2_000_000_000_000 // 2 DOT
);

vm.stopBroadcast();
}
}
3 changes: 1 addition & 2 deletions contracts/src/Agent.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ contract Agent {
}

/// @dev Agents can receive ether permissionlessly.
/// This is important, as agents for top-level parachains also act as sovereign accounts from which message relayers
/// are rewarded.
/// This is important, as agents are used to lock ether.
receive() external payable {}

/// @dev Allow the gateway to invoke some code within the context of this agent
Expand Down
14 changes: 4 additions & 10 deletions contracts/src/AgentExecutor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {AgentExecuteCommand, ParaID} from "./Types.sol";
import {SubstrateTypes} from "./SubstrateTypes.sol";

import {IERC20} from "./interfaces/IERC20.sol";
import {IGateway} from "./interfaces/IGateway.sol";
import {SafeTokenTransfer, SafeNativeTransfer} from "./utils/SafeTransfer.sol";

/// @title Code which will run within an `Agent` using `delegatecall`.
Expand All @@ -14,20 +15,13 @@ contract AgentExecutor {
using SafeTokenTransfer for IERC20;
using SafeNativeTransfer for address payable;

/// @dev Transfer ether to `recipient`. Unlike `_transferToken` This logic is not nested within `execute`,
/// as the gateway needs to control an agent's ether balance directly.
///
function transferNative(address payable recipient, uint256 amount) external {
/// @dev Transfer ether to `recipient`.
function transferEther(address payable recipient, uint256 amount) external {
recipient.safeNativeTransfer(amount);
}

/// @dev Transfer ERC20 to `recipient`. Only callable via `execute`.
/// @dev Transfer ERC20 to `recipient`.
function transferToken(address token, address recipient, uint128 amount) external {
_transferToken(token, recipient, amount);
}

/// @dev Transfer ERC20 to `recipient`. Only callable via `execute`.
function _transferToken(address token, address recipient, uint128 amount) internal {
IERC20(token).safeTransfer(recipient, amount);
}
}
36 changes: 30 additions & 6 deletions contracts/src/Assets.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {CoreStorage} from "./storage/CoreStorage.sol";
import {SubstrateTypes} from "./SubstrateTypes.sol";
import {ChannelID, ParaID, MultiAddress, Ticket, Costs} from "./Types.sol";
import {Address} from "./utils/Address.sol";
import {SafeNativeTransfer} from "./utils/SafeTransfer.sol";
import {AgentExecutor} from "./AgentExecutor.sol";
import {Agent} from "./Agent.sol";
import {Call} from "./utils/Call.sol";
Expand All @@ -21,6 +22,7 @@ import {Token} from "./Token.sol";
/// @title Library for implementing Ethereum->Polkadot ERC20 transfers.
library Assets {
using Address for address;
using SafeNativeTransfer for address payable;
using SafeTokenTransferFrom for IERC20;

/* Errors */
Expand Down Expand Up @@ -108,14 +110,18 @@ library Assets {
) external returns (Ticket memory ticket) {
AssetsStorage.Layout storage $ = AssetsStorage.layout();

if (amount == 0) {
revert InvalidAmount();
}

TokenInfo storage info = $.tokenRegistry[token];

if (!info.isRegistered) {
revert TokenNotRegistered();
}

if (info.foreignID == bytes32(0)) {
return _sendNativeToken(
if (info.isNativeToken()) {
return _sendNativeTokenOrEther(
token, sender, destinationChain, destinationAddress, destinationChainFee, maxDestinationChainFee, amount
);
} else {
Expand All @@ -132,7 +138,8 @@ library Assets {
}
}

function _sendNativeToken(
// @dev Transfer ERC20(Ethereum-native) tokens to Polkadot
function _sendNativeTokenOrEther(
address token,
address sender,
ParaID destinationChain,
Expand All @@ -143,8 +150,15 @@ library Assets {
) internal returns (Ticket memory ticket) {
AssetsStorage.Layout storage $ = AssetsStorage.layout();

// Lock the funds into AssetHub's agent contract
_transferToAgent($.assetHubAgent, token, sender, amount);
if (token != address(0)) {
vgeddes marked this conversation as resolved.
Show resolved Hide resolved
// Lock ERC20
_transferToAgent($.assetHubAgent, token, sender, amount);
ticket.value = 0;
} else {
// Track the ether to bridge to Polkadot. This will be handled
// in `Gateway._submitOutbound`.
ticket.value = amount;
}

ticket.dest = $.assetHubParaID;
ticket.costs = _sendTokenCosts(destinationChain, destinationChainFee, maxDestinationChainFee);
Expand Down Expand Up @@ -191,6 +205,7 @@ library Assets {
revert Unsupported();
}
}

emit IGateway.TokenSent(token, sender, destinationChain, destinationAddress, amount);
}

Expand All @@ -211,6 +226,7 @@ library Assets {

ticket.dest = $.assetHubParaID;
ticket.costs = _sendTokenCosts(destinationChain, destinationChainFee, maxDestinationChainFee);
ticket.value = 0;
claravanstaden marked this conversation as resolved.
Show resolved Hide resolved

// Construct a message payload
if (destinationChain == $.assetHubParaID && destinationAddress.isAddress32()) {
Expand Down Expand Up @@ -262,6 +278,7 @@ library Assets {
ticket.dest = $.assetHubParaID;
ticket.costs = _registerTokenCosts();
ticket.payload = SubstrateTypes.RegisterToken(token, $.assetHubCreateAssetFee);
ticket.value = 0;

emit IGateway.TokenRegistrationSent(token);
}
Expand Down Expand Up @@ -299,7 +316,14 @@ library Assets {
function transferNativeToken(address executor, address agent, address token, address recipient, uint128 amount)
external
{
bytes memory call = abi.encodeCall(AgentExecutor.transferToken, (token, recipient, amount));
bytes memory call;
if (token != address(0)) {
// ERC20
call = abi.encodeCall(AgentExecutor.transferToken, (token, recipient, amount));
} else {
// Native ETH
call = abi.encodeCall(AgentExecutor.transferEther, (payable(recipient), amount));
}
(bool success,) = Agent(payable(agent)).invoke(executor, call);
if (!success) {
revert TokenTransferFailed();
Expand Down
Loading
Loading