Skip to content

Commit 80f8bb8

Browse files
nptynik-suri
andauthored
feat: add axelar endpoint (#1)
* feat: add AxelarEndpoint * forge install: axelar-gmp-sdk-solidity v5.6.4 * feat: add axelar-gmp-sdk-solidity lib * chore: move internal function down * chore: cleanup naming * chore: fix setEmitter function * feat: add function to set axelar chain id * chore: update modifier for setEmitter * chore: update interfaces * chore: remove unnecessary function and update bytes32 to string conversion * feat: add way to setEmitter * feat: fix bytes32 conversion * chore: add basic tests * forge install: wormhole ntt * chore: imports from wormhole-foundation * chore: fix comment * chore: remove old endpoint contract * forge install: wormhole ntt * chore: update git modules * forge install: wormhole ntt * forge install: wormhole ntt * forge install: wormhole 81e7d50330c39f0f045058d40e232813b863ab18 * forge install: openzeppelin-contracts v5.0.1 * chore: update contract to latest interface * fix compiler errors * forge install: example-native-token-transfers * update dependency * forge install: wormhole-solidity-sdk 374a016685715f6aa8cb05f079f22f471e5f48fc --------- Co-authored-by: Nikhil Suri <[email protected]>
1 parent 6086388 commit 80f8bb8

15 files changed

+310
-50
lines changed

.gitmodules

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
11
[submodule "lib/forge-std"]
22
path = lib/forge-std
33
url = https://github.com/foundry-rs/forge-std
4+
[submodule "lib/axelar-gmp-sdk-solidity"]
5+
path = lib/axelar-gmp-sdk-solidity
6+
url = https://github.com/axelarnetwork/axelar-gmp-sdk-solidity
7+
[submodule "lib/wormhole"]
8+
path = lib/wormhole
9+
url = https://github.com/wormhole-foundation/wormhole
10+
[submodule "lib/openzeppelin-contracts"]
11+
path = lib/openzeppelin-contracts
12+
url = https://github.com/OpenZeppelin/openzeppelin-contracts
13+
[submodule "lib/example-native-token-transfers"]
14+
path = lib/example-native-token-transfers
15+
url = https://github.com/wormhole-foundation/example-native-token-transfers
16+
[submodule "lib/wormhole-solidity-sdk"]
17+
path = lib/wormhole-solidity-sdk
18+
url = https://github.com/wormhole-foundation/wormhole-solidity-sdk

lib/axelar-gmp-sdk-solidity

Submodule axelar-gmp-sdk-solidity added at 971cd50

lib/example-native-token-transfers

lib/openzeppelin-contracts

Submodule openzeppelin-contracts added at 01ef448

lib/wormhole

Submodule wormhole added at 81e7d50

lib/wormhole-solidity-sdk

Submodule wormhole-solidity-sdk added at 374a016

remappings.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
@axelar-network/axelar-gmp-sdk-solidity=lib/axelar-gmp-sdk-solidity/
2+
@wormhole-foundation/native_token_transfer=lib/example-native-token-transfers/src

script/Counter.s.sol

Lines changed: 0 additions & 12 deletions
This file was deleted.

src/Counter.sol

Lines changed: 0 additions & 14 deletions
This file was deleted.

src/axelar/AxelarEndpoint.sol

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.19;
3+
4+
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
5+
import {AxelarExecutable} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/executable/AxelarExecutable.sol";
6+
import {IAxelarGasService} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGasService.sol";
7+
import {
8+
StringToAddress, AddressToString
9+
} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/AddressString.sol";
10+
import {IEndpointManagerStandalone} from
11+
"@wormhole-foundation/native_token_transfer/interfaces/IEndpointManagerStandalone.sol";
12+
import {EndpointStandalone} from "@wormhole-foundation/native_token_transfer/EndpointStandalone.sol";
13+
import {EndpointStructs} from "@wormhole-foundation/native_token_transfer/libraries/EndpointStructs.sol";
14+
import {SetEmitterMessage} from "./Structs.sol";
15+
16+
contract AxelarEndpoint is EndpointStandalone, AxelarExecutable, Ownable {
17+
IAxelarGasService public immutable gasService;
18+
19+
// These mappings are used to convert between chainId and chainName as Axelar accept chainName as string format
20+
mapping(uint16 => string) public idToAxelarChainIds;
21+
mapping(string => uint16) public axelarChainIdToId;
22+
23+
error UnsupportedMessageType();
24+
error InvalidSibling(uint16 chainId, bytes32 siblingAddress);
25+
error NotImplemented();
26+
27+
modifier onlySibling(string calldata sourceChain, string calldata sourceAddress) {
28+
uint16 chainId = axelarChainIdToId[sourceChain];
29+
address _sourceAddress = StringToAddress.toAddress(sourceAddress);
30+
if (getSibling(chainId) != bytes32(uint256(uint160(_sourceAddress)))) {
31+
revert InvalidSibling(chainId, getSibling(chainId));
32+
}
33+
_;
34+
}
35+
36+
constructor(address _gateway, address _gasService, address _manager, address _owner)
37+
AxelarExecutable(_gateway)
38+
EndpointStandalone(_manager)
39+
Ownable(_owner)
40+
{
41+
gasService = IAxelarGasService(_gasService);
42+
}
43+
44+
/**
45+
* Set the bridge manager contract address
46+
* @param chainId The chainId of the chain. This is used to identify the chain in the EndpointManager.
47+
* @param chainName The chainName of the chain. This is used to identify the chain in the AxelarGateway.
48+
*/
49+
function setAxelarChainId(uint16 chainId, string calldata chainName) external onlyOwner {
50+
idToAxelarChainIds[chainId] = chainName;
51+
axelarChainIdToId[chainName] = chainId;
52+
}
53+
54+
/**
55+
* Revert if the message type is not supported
56+
*/
57+
function _handleMessage(bytes memory payload) internal returns (bool) {
58+
// Decode the payload as a EndpointManagerMessage
59+
EndpointStructs.EndpointManagerMessage memory message = EndpointStructs.parseEndpointManagerMessage(payload);
60+
61+
// msgType 1: Send Token
62+
// msgType 2: Set Emitter (destination contract address)
63+
if (message.msgType == 1) {
64+
return false;
65+
} else {
66+
revert UnsupportedMessageType();
67+
}
68+
}
69+
70+
/**
71+
* Send message to Axelar Gateway
72+
* @param recipientChain The chainId of the chain. This is used to identify the chain in the EndpointManager.
73+
* @param payload The payload of the message which is a NativeTokenTransfer
74+
*/
75+
function _sendMessage(uint16 recipientChain, bytes memory payload) internal virtual override {
76+
bool isInternalCall = _handleMessage(payload);
77+
78+
if (isInternalCall) {
79+
return;
80+
}
81+
82+
bytes32 destEmitter = getSibling(recipientChain);
83+
string memory destinationContract = AddressToString.toString(address(uint160(uint256(destEmitter))));
84+
string memory destinationChain = idToAxelarChainIds[recipientChain];
85+
86+
gasService.payNativeGasForContractCall{value: msg.value}(
87+
address(this), destinationChain, destinationContract, payload, msg.sender
88+
);
89+
90+
gateway.callContract(destinationChain, destinationContract, payload);
91+
}
92+
93+
/**
94+
* Receive message from Axelar Gateway
95+
*/
96+
function _execute(string calldata sourceChain, string calldata sourceAddress, bytes calldata payload)
97+
internal
98+
override
99+
onlySibling(sourceChain, sourceAddress)
100+
{
101+
EndpointStructs.EndpointManagerMessage memory message = EndpointStructs.parseEndpointManagerMessage(payload);
102+
IEndpointManagerStandalone(_manager).attestationReceived(message);
103+
}
104+
105+
function _verifyMessage(bytes memory encodedMessage) internal override returns (bytes memory) {
106+
revert NotImplemented();
107+
}
108+
109+
function _quoteDeliveryPrice(uint16 targetChain)
110+
internal
111+
view
112+
virtual
113+
override
114+
returns (uint256 nativePriceQuote)
115+
{
116+
// Axelar doesn't support on-chain gas fee.
117+
return 0;
118+
}
119+
}

0 commit comments

Comments
 (0)