Skip to content

Commit

Permalink
Add AgentExecuteCommand back for compatibility
Browse files Browse the repository at this point in the history
  • Loading branch information
yrong committed Apr 11, 2024
1 parent 4075c11 commit fcd7539
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 6 deletions.
20 changes: 18 additions & 2 deletions contracts/src/AgentExecutor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-FileCopyrightText: 2023 Snowfork <[email protected]>
pragma solidity 0.8.23;

import {ParaID} from "./Types.sol";
import {AgentExecuteCommand, ParaID} from "./Types.sol";
import {SubstrateTypes} from "./SubstrateTypes.sol";

import {IERC20} from "./interfaces/IERC20.sol";
Expand All @@ -16,6 +16,17 @@ contract AgentExecutor {
using SafeTokenTransfer for IERC20;
using SafeNativeTransfer for address payable;

/// @dev Execute a message which originated from the Polkadot side of the bridge. In other terms,
/// the `data` parameter is constructed by the BridgeHub parachain.
///
function execute(bytes memory data) external {
(AgentExecuteCommand command, bytes memory params) = abi.decode(data, (AgentExecuteCommand, bytes));
if (command == AgentExecuteCommand.TransferToken) {
(address token, address recipient, uint128 amount) = abi.decode(params, (address, address, uint128));
_transferToken(token, recipient, amount);
}
}

/// @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.
///
Expand All @@ -24,10 +35,15 @@ contract AgentExecutor {
}

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

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

/// @dev Mint ERC20 token to `recipient`.
function mintToken(address token, address recipient, uint256 amount) external {
ERC20(token).mint(recipient, amount);
Expand Down
32 changes: 29 additions & 3 deletions contracts/src/Gateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import {
MultiAddress,
Ticket,
Costs,
TokenInfo
TokenInfo,
AgentExecuteCommand
} from "./Types.sol";
import {IGateway} from "./interfaces/IGateway.sol";
import {IInitializable} from "./interfaces/IInitializable.sol";
Expand All @@ -30,6 +31,7 @@ import {Math} from "./utils/Math.sol";
import {ScaleCodec} from "./utils/ScaleCodec.sol";

import {
AgentExecuteParams,
UpgradeParams,
CreateAgentParams,
CreateChannelParams,
Expand Down Expand Up @@ -85,6 +87,7 @@ contract Gateway is IGateway, IInitializable {
error ChannelAlreadyCreated();
error ChannelDoesNotExist();
error InvalidChannelUpdate();
error AgentExecutionFailed(bytes returndata);
error InvalidAgentExecutionPayload();
error InvalidCodeHash();
error InvalidConstructorParams();
Expand Down Expand Up @@ -169,8 +172,8 @@ contract Gateway is IGateway, IInitializable {
bool success = true;

// Dispatch message to a handler
if (message.command == Command.TransferToken) {
try Gateway(this).transferToken{gas: maxDispatchGas}(message.params) {}
if (message.command == Command.AgentExecute) {
try Gateway(this).agentExecute{gas: maxDispatchGas}(message.params) {}
catch {
success = false;
}
Expand Down Expand Up @@ -214,6 +217,11 @@ contract Gateway is IGateway, IInitializable {
catch {
success = false;
}
} else if (message.command == Command.TransferToken) {
try Gateway(this).transferToken{gas: maxDispatchGas}(message.params) {}
catch {
success = false;
}
} else if (message.command == Command.RegisterForeignToken) {
try Gateway(this).registerForeignToken{gas: maxDispatchGas}(message.params) {}
catch {
Expand Down Expand Up @@ -277,6 +285,24 @@ contract Gateway is IGateway, IInitializable {
* Handlers
*/

// Execute code within an agent
function agentExecute(bytes calldata data) external onlySelf {
AgentExecuteParams memory params = abi.decode(data, (AgentExecuteParams));

address agent = _ensureAgent(params.agentID);

if (params.payload.length == 0) {
revert InvalidAgentExecutionPayload();
}

bytes memory call = abi.encodeCall(AgentExecutor.execute, params.payload);

(bool success, bytes memory returndata) = Agent(payable(agent)).invoke(AGENT_EXECUTOR, call);
if (!success) {
revert AgentExecutionFailed(returndata);
}
}

/// @dev Create an agent for a consensus system on Polkadot
function createAgent(bytes calldata data) external onlySelf {
CoreStorage.Layout storage $ = CoreStorage.layout();
Expand Down
6 changes: 6 additions & 0 deletions contracts/src/Params.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ pragma solidity 0.8.23;
import {ChannelID, OperatingMode} from "./Types.sol";
import {UD60x18} from "prb/math/src/UD60x18.sol";

// Payload for AgentExecute
struct AgentExecuteParams {
bytes32 agentID;
bytes payload;
}

// Payload for CreateAgent
struct CreateAgentParams {
/// @dev The agent ID of the consensus system
Expand Down
8 changes: 7 additions & 1 deletion contracts/src/Types.sol
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ enum OperatingMode {

/// @dev Messages from Polkadot take the form of these commands.
enum Command {
TransferToken,
AgentExecute,
Upgrade,
CreateAgent,
CreateChannel,
Expand All @@ -85,10 +85,16 @@ enum Command {
TransferNativeFromAgent,
SetTokenTransferFees,
SetPricingParameters,
TransferToken,
RegisterForeignToken,
MintForeignToken
}

enum AgentExecuteCommand {
TransferToken,
MintToken
}

/// @dev Application-level costs for a message
struct Costs {
/// @dev Costs in foreign currency
Expand Down
14 changes: 14 additions & 0 deletions contracts/test/Gateway.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {ERC20Lib} from "../src/ERC20Lib.sol";
import {
UpgradeParams,
CreateAgentParams,
AgentExecuteParams,
CreateChannelParams,
UpdateChannelParams,
SetOperatingModeParams,
Expand All @@ -39,6 +40,7 @@ import {
} from "../src/Params.sol";

import {
AgentExecuteCommand,
InboundMessage,
OperatingMode,
ParaID,
Expand Down Expand Up @@ -1033,4 +1035,16 @@ contract GatewayTest is Test {

IGateway(address(gateway)).sendToken{value: 0.1 ether}(address(dotToken), destPara, recipientAddress32, 1, 1);
}

function testLegacyAgentExecutionForCompatibility() public {
token.transfer(address(assetHubAgent), 200);

AgentExecuteParams memory params = AgentExecuteParams({
agentID: assetHubAgentID,
payload: abi.encode(AgentExecuteCommand.TransferToken, abi.encode(address(token), address(account2), 10))
});

bytes memory encodedParams = abi.encode(params);
GatewayMock(address(gateway)).agentExecutePublic(encodedParams);
}
}
4 changes: 4 additions & 0 deletions contracts/test/mocks/GatewayMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ contract GatewayMock is Gateway {
uint8 foreignTokenDecimals
) Gateway(beefyClient, agentExecutor, bridgeHubParaID, bridgeHubHubAgentID, foreignTokenDecimals) {}

function agentExecutePublic(bytes calldata params) external {
this.agentExecute(params);
}

function createAgentPublic(bytes calldata params) external {
this.createAgent(params);
}
Expand Down

0 comments on commit fcd7539

Please sign in to comment.