-
Notifications
You must be signed in to change notification settings - Fork 25
Flatten AxelarGatewayAdapter contract #202
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
Merged
+202
−277
Merged
Changes from 2 commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
6018095
Implement requestRelay in Axelar gateway
Amxx a161c94
Merge axelar source and destination
Amxx 28f7ab9
Merge AxelarGatewayBase into AxelarGatewayAdapter
Amxx bb5de72
add documentation and removed unused file
Amxx 98d96a6
removed unused file
Amxx ce22703
removed unused file
Amxx 85448fe
remove import
Amxx cab901e
Merge branch 'master' into interop/refactor-axelar-gateway
Amxx 45eb1fa
try fix silent slither locked-ether
Amxx 006ee73
document limitation to EVM chains
Amxx aab1f49
codespell
Amxx ca2f4bc
Merge branch 'master' into interop/refactor-axelar-gateway
Amxx File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.27; | ||
|
||
import {IAxelarGateway} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol"; | ||
import {IAxelarGasService} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGasService.sol"; | ||
import {AxelarExecutable} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/executable/AxelarExecutable.sol"; | ||
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; | ||
import {InteroperableAddress} from "@openzeppelin/contracts/utils/draft-InteroperableAddress.sol"; | ||
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; | ||
import {IERC7786GatewaySource} from "../../interfaces/IERC7786.sol"; | ||
import {IERC7786Attributes} from "../../interfaces/IERC7786Attributes.sol"; | ||
import {IERC7786Receiver} from "../../interfaces/IERC7786.sol"; | ||
import {ERC7786Attributes} from "../utils/ERC7786Attributes.sol"; | ||
import {AxelarGatewayBase} from "./AxelarGatewayBase.sol"; | ||
|
||
/** | ||
* @dev Implementation of an ERC-7786 gateway destination adapter for the Axelar Network in dual mode. | ||
* | ||
* The contract implements AxelarExecutable's {_execute} function to execute the message, converting Axelar's native | ||
* workflow into the standard ERC-7786. | ||
*/ | ||
contract AxelarGatewayAdaptor is IERC7786GatewaySource, AxelarGatewayBase, AxelarExecutable { | ||
using InteroperableAddress for bytes; | ||
using Strings for *; | ||
|
||
struct MessageDetails { | ||
string destination; | ||
string target; | ||
bytes payload; | ||
} | ||
|
||
uint256 private _sendId; | ||
mapping(bytes32 => MessageDetails) private _details; | ||
|
||
error UnsupportedNativeTransfer(); | ||
error InvalidOriginGateway(string axelarSourceChain, string axelarSourceAddress); | ||
error ReceiverExecutionFailed(); | ||
|
||
/// @dev Initializes the contract with the Axelar gateway and the initial owner. | ||
constructor( | ||
IAxelarGateway gateway, | ||
IAxelarGasService gasService, | ||
address initialOwner | ||
) Ownable(initialOwner) AxelarGatewayBase(gateway, gasService) AxelarExecutable(address(gateway)) {} | ||
|
||
/// @inheritdoc IERC7786GatewaySource | ||
function supportsAttribute(bytes4 selector) public pure returns (bool) { | ||
return selector == IERC7786Attributes.requestRelay.selector; | ||
} | ||
|
||
/// @inheritdoc IERC7786GatewaySource | ||
function sendMessage( | ||
bytes calldata recipient, // Binary Interoperable Address | ||
bytes calldata payload, | ||
bytes[] calldata attributes | ||
) external payable returns (bytes32 sendId) { | ||
// Process attributes (relay) | ||
bool withRelay = false; | ||
uint256 value = 0; | ||
address refundRecipient = address(0); | ||
|
||
for (uint256 i = 0; i < attributes.length; ++i) { | ||
(withRelay, value, , refundRecipient) = ERC7786Attributes.tryDecodeRequestRelayCalldata(attributes[i]); | ||
require(withRelay, UnsupportedAttribute(attributes[i].length < 0x04 ? bytes4(0) : bytes4(attributes[i]))); | ||
} | ||
if (!withRelay) { | ||
sendId = bytes32(++_sendId); | ||
} | ||
require(msg.value == value, UnsupportedNativeTransfer()); | ||
|
||
// Create the package | ||
bytes memory sender = InteroperableAddress.formatEvmV1(block.chainid, msg.sender); | ||
bytes memory adapterPayload = abi.encode(sender, recipient, payload); | ||
|
||
// Emit event early (stack too deep) | ||
emit MessageSent(sendId, sender, recipient, payload, 0, attributes); | ||
|
||
// Send the message | ||
(bytes2 chainType, bytes calldata chainReference, ) = recipient.parseV1Calldata(); | ||
bytes memory remoteGateway = getRemoteGateway(chainType, chainReference); | ||
string memory axelarDestination = getAxelarChain(InteroperableAddress.formatV1(chainType, chainReference, "")); | ||
string memory axelarTarget = address(bytes20(remoteGateway)).toChecksumHexString(); // TODO non-evm chains? | ||
|
||
_axelarGateway.callContract(axelarDestination, axelarTarget, adapterPayload); | ||
|
||
if (withRelay) { | ||
_axelarGasService.payNativeGasForContractCall{value: msg.value}( | ||
address(this), | ||
axelarDestination, | ||
axelarTarget, | ||
adapterPayload, | ||
refundRecipient | ||
); | ||
} else { | ||
_details[sendId] = MessageDetails(axelarDestination, axelarTarget, adapterPayload); | ||
} | ||
} | ||
|
||
// TODO inheritdoc from interface when that is standardized | ||
function requestRelay(bytes32 sendId, uint256 /*gasLimit*/, address refundRecipient) external payable { | ||
frangio marked this conversation as resolved.
Show resolved
Hide resolved
|
||
MessageDetails storage details = _details[sendId]; | ||
require(details.payload.length > 0); | ||
|
||
_axelarGasService.payNativeGasForContractCall{value: msg.value}( | ||
address(this), | ||
details.destination, | ||
details.target, | ||
details.payload, | ||
refundRecipient | ||
); | ||
} | ||
|
||
/** | ||
* @dev Execution of a cross-chain message. | ||
* | ||
* In this function: | ||
* | ||
* - `axelarSourceChain` is in the Axelar format. It should not be expected to be a proper ERC-7930 format | ||
* - `axelarSourceAddress` is the sender of the Axelar message. That should be the remote gateway on the chain | ||
* which the message originates from. It is NOT the sender of the ERC-7786 crosschain message. | ||
* | ||
* Proper ERC-7930 encoding of the crosschain message sender can be found in the message | ||
*/ | ||
function _execute( | ||
bytes32 commandId, | ||
string calldata axelarSourceChain, // chain of the remote gateway - axelar format | ||
string calldata axelarSourceAddress, // address of the remote gateway | ||
bytes calldata adapterPayload | ||
) internal override { | ||
// Parse the package | ||
(bytes memory sender, bytes memory recipient, bytes memory payload) = abi.decode( | ||
adapterPayload, | ||
(bytes, bytes, bytes) | ||
); | ||
|
||
// Axelar to ERC-7930 translation | ||
bytes memory addr = getRemoteGateway(getErc7930Chain(axelarSourceChain)); | ||
|
||
// check message validity | ||
// - `axelarSourceAddress` is the remote gateway on the origin chain. | ||
require( | ||
address(bytes20(addr)).toChecksumHexString().equal(axelarSourceAddress), // TODO non-evm chains? | ||
InvalidOriginGateway(axelarSourceChain, axelarSourceAddress) | ||
); | ||
|
||
(, address target) = recipient.parseEvmV1(); | ||
bytes4 result = IERC7786Receiver(target).receiveMessage(commandId, sender, payload); | ||
require(result == IERC7786Receiver.receiveMessage.selector, ReceiverExecutionFailed()); | ||
} | ||
} |
frangio marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.27; | ||
|
||
import {IERC7786Attributes} from "../../interfaces/IERC7786Attributes.sol"; | ||
|
||
library ERC7786Attributes { | ||
function tryDecodeRequestRelay( | ||
bytes memory attribute | ||
) internal pure returns (bool success, uint256 value, uint256 gasLimit, address refundRecipient) { | ||
success = bytes4(attribute) == IERC7786Attributes.requestRelay.selector && attribute.length >= 0x64; | ||
if (success) { | ||
assembly ("memory-safe") { | ||
value := mload(add(attribute, 0x24)) | ||
gasLimit := mload(add(attribute, 0x44)) | ||
refundRecipient := mload(add(attribute, 0x64)) | ||
} | ||
} | ||
} | ||
|
||
function tryDecodeRequestRelayCalldata( | ||
bytes calldata attribute | ||
) internal pure returns (bool success, uint256 value, uint256 gasLimit, address refundRecipient) { | ||
success = bytes4(attribute) == IERC7786Attributes.requestRelay.selector && attribute.length >= 0x64; | ||
if (success) { | ||
assembly ("memory-safe") { | ||
value := calldataload(add(attribute.offset, 0x04)) | ||
gasLimit := calldataload(add(attribute.offset, 0x24)) | ||
refundRecipient := calldataload(add(attribute.offset, 0x44)) | ||
} | ||
} | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.4; | ||
|
||
/** | ||
* @dev Standard attributes for ERC-7786. These attributes may be standardized in different ERC. | ||
*/ | ||
interface IERC7786Attributes { | ||
function requestRelay(uint256 value, uint256 gasLimit, address refundRecipient) external; | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.