Skip to content

Commit

Permalink
update executor options for solana client controller (#120)
Browse files Browse the repository at this point in the history
* update executor options

* update executor options

* add test for solana client

* add _isSolana(.) function

* simplifying the generateUID function

* simplifying the _buildOptions function
  • Loading branch information
magj2006 authored Oct 30, 2024
1 parent d21c7d6 commit 54cb293
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 13 deletions.
50 changes: 40 additions & 10 deletions src/core/ExocoreGateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,11 @@ contract ExocoreGateway is
}
emit LSTTransfer(isDeposit, success, bytes32(token), bytes32(staker), amount);

response = isDeposit ? bytes("") : abi.encodePacked(lzNonce, success);
if (_isSolana(srcChainId)) {
response = isDeposit ? bytes("") : abi.encodePacked(lzNonce, success, bytes32(token), bytes32(staker));
} else {
response = isDeposit ? bytes("") : abi.encodePacked(lzNonce, success);
}
}

/// @notice Handles NST transfer from a client chain.
Expand Down Expand Up @@ -419,7 +423,13 @@ contract ExocoreGateway is
}
emit RewardOperation(isSubmitReward, success, bytes32(token), bytes32(avsOrWithdrawer), amount);

response = isSubmitReward ? bytes("") : abi.encodePacked(lzNonce, success);
if (_isSolana(srcChainId)) {
response = isSubmitReward
? bytes("")
: abi.encodePacked(lzNonce, success, bytes32(token), bytes32(avsOrWithdrawer));
} else {
response = isSubmitReward ? bytes("") : abi.encodePacked(lzNonce, success);
}
}

/// @notice Handles delegation request from a client chain.
Expand Down Expand Up @@ -515,9 +525,9 @@ contract ExocoreGateway is
whenNotPaused
{
bytes memory payload = abi.encodePacked(act, actionArgs);
bytes memory options = OptionsBuilder.newOptions().addExecutorLzReceiveOption(
DESTINATION_GAS_LIMIT, DESTINATION_MSG_VALUE
).addExecutorOrderedExecutionOption();

bytes memory options = _buildOptions(srcChainId, act);

MessagingFee memory fee = _quote(srcChainId, payload, options, false);

address refundAddress = payByApp ? address(this) : msg.sender;
Expand All @@ -527,14 +537,34 @@ contract ExocoreGateway is
}

/// @inheritdoc IExocoreGateway
function quote(uint32 srcChainid, bytes calldata _message) public view returns (uint256 nativeFee) {
bytes memory options = OptionsBuilder.newOptions().addExecutorLzReceiveOption(
DESTINATION_GAS_LIMIT, DESTINATION_MSG_VALUE
).addExecutorOrderedExecutionOption();
MessagingFee memory fee = _quote(srcChainid, _message, options, false);
function quote(uint32 srcChainId, bytes calldata _message) public view returns (uint256 nativeFee) {
Action act = Action(uint8(_message[0]));

bytes memory options = _buildOptions(srcChainId, act);

MessagingFee memory fee = _quote(srcChainId, _message, options, false);
return fee.nativeFee;
}

/// @dev Builds options for interchain messages based on chain and action
/// @param srcChainId The source chain ID
/// @param act The action being performed
/// @return options The built options
function _buildOptions(uint32 srcChainId, Action act) private pure returns (bytes memory) {
bytes memory options = OptionsBuilder.newOptions();
uint128 value = DESTINATION_MSG_VALUE;

if (!_isSolana(srcChainId)) {
options = options.addExecutorOrderedExecutionOption();
} else if (act == Action.REQUEST_ADD_WHITELIST_TOKEN) {
value = SOLANA_WHITELIST_TOKEN_MSG_VALUE;
}

options = options.addExecutorLzReceiveOption(DESTINATION_GAS_LIMIT, value);

return options;
}

/// @inheritdoc OAppReceiverUpgradeable
function nextNonce(uint32 srcEid, bytes32 sender)
public
Expand Down
19 changes: 19 additions & 0 deletions src/storage/ExocoreGatewayStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,17 @@ contract ExocoreGatewayStorage is GatewayStorage {
/// @dev The msg.value for all the destination chains.
uint128 internal constant DESTINATION_MSG_VALUE = 0;

/// constants used for solana mainnet chainId
/// @dev the solana mainnet chain id
uint32 internal constant SOLANA_MAINNET_CHAIN_ID = 30_168;

/// constants used for solana devnet chainId
/// @dev the solana devnet chain id
uint32 internal constant SOLANA_DEVNET_CHAIN_ID = 40_168;

/// @dev the msg.value for send addTokenWhiteList message
uint128 internal constant SOLANA_WHITELIST_TOKEN_MSG_VALUE = 3_000_000;

/// @notice Emitted when a precompile call fails.
/// @param precompile Address of the precompile contract.
/// @param nonce The LayerZero nonce
Expand Down Expand Up @@ -128,4 +139,12 @@ contract ExocoreGatewayStorage is GatewayStorage {
}
}

/**
* @dev return true if chain is either Solana devnet or Solana mainnet
* @param srcChainId remote Chain Id
*/
function _isSolana(uint32 srcChainId) internal pure returns (bool) {
return srcChainId == SOLANA_DEVNET_CHAIN_ID || srcChainId == SOLANA_MAINNET_CHAIN_ID;
}

}
56 changes: 53 additions & 3 deletions test/foundry/unit/ExocoreGateway.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,17 @@ contract SetUp is Test {

ExocoreGateway exocoreGateway;
ClientChainGateway clientGateway;
ClientChainGateway solanaClientGateway;

NonShortCircuitEndpointV2Mock exocoreLzEndpoint;
NonShortCircuitEndpointV2Mock clientLzEndpoint;
NonShortCircuitEndpointV2Mock solanaClientLzEndpoint;

ERC20 restakeToken;

uint16 exocoreChainId = 1;
uint16 clientChainId = 2;
uint16 solanaClientChainId = 40_168;

struct Player {
uint256 privateKey;
Expand All @@ -64,6 +69,7 @@ contract SetUp is Test {
deployer = Player({privateKey: uint256(0xb), addr: vm.addr(uint256(0xb))});
withdrawer = Player({privateKey: uint256(0xc), addr: vm.addr(uint256(0xb))});
clientGateway = ClientChainGateway(payable(address(0xd)));
solanaClientGateway = ClientChainGateway(payable(address(0xe)));

// bind precompile mock contracts code to constant precompile address
bytes memory AssetsMockCode = vm.getDeployedCode("AssetsMock.sol");
Expand All @@ -88,6 +94,7 @@ contract SetUp is Test {

exocoreLzEndpoint = new NonShortCircuitEndpointV2Mock(exocoreChainId, exocoreValidatorSet.addr);
clientLzEndpoint = new NonShortCircuitEndpointV2Mock(clientChainId, exocoreValidatorSet.addr);
solanaClientLzEndpoint = new NonShortCircuitEndpointV2Mock(solanaClientChainId, exocoreValidatorSet.addr);

ProxyAdmin proxyAdmin = new ProxyAdmin();
ExocoreGateway exocoreGatewayLogic = new ExocoreGateway(address(exocoreLzEndpoint));
Expand All @@ -108,6 +115,17 @@ contract SetUp is Test {
"EVM compatible client chain",
"secp256k1"
);

exocoreLzEndpoint.setDestLzEndpoint(address(solanaClientGateway), address(clientLzEndpoint));
exocoreGateway.registerOrUpdateClientChain(
solanaClientChainId,
address(solanaClientGateway).toBytes32(),
20,
"solanaClientChain",
"Non-EVM compatible client chain",
"ed25519"
);

vm.stopPrank();

// transfer some gas fee to exocore gateway as it has to pay for the relay fee to layerzero endpoint when
Expand All @@ -116,14 +134,24 @@ contract SetUp is Test {
}

function generateUID(uint64 nonce, bool fromClientChainToExocore) internal view returns (bytes32 uid) {
uid = generateUID(nonce, fromClientChainToExocore, false);
}

function generateUID(uint64 nonce, bool fromClientChainToExocore, bool isSolanaClient)
internal
view
returns (bytes32 uid)
{
if (fromClientChainToExocore) {
uid = GUID.generate(
nonce, clientChainId, address(clientGateway), exocoreChainId, address(exocoreGateway).toBytes32()
);
} else {
uid = GUID.generate(
nonce, exocoreChainId, address(exocoreGateway), clientChainId, address(clientGateway).toBytes32()
);
uint16 targetChainId = isSolanaClient ? solanaClientChainId : clientChainId;
bytes32 targetGateway =
isSolanaClient ? address(solanaClientGateway).toBytes32() : address(clientGateway).toBytes32();

return GUID.generate(nonce, exocoreChainId, address(exocoreGateway), targetChainId, targetGateway);
}
}

Expand Down Expand Up @@ -526,6 +554,7 @@ contract AddWhitelistTokens is SetUp {

uint256 MESSAGE_LENGTH = 1 + 32 + 16; // action + token address as bytes32 + uint128 tvl limit
uint256 nativeFee;
uint256 nativeFeeForSolana;

error IncorrectNativeFee(uint256 amount);

Expand All @@ -534,6 +563,9 @@ contract AddWhitelistTokens is SetUp {
function setUp() public virtual override {
super.setUp();
nativeFee = exocoreGateway.quote(clientChainId, new bytes(MESSAGE_LENGTH));
bytes memory message = new bytes(MESSAGE_LENGTH);
message[0] = bytes1(abi.encodePacked(Action.REQUEST_ADD_WHITELIST_TOKEN));
nativeFeeForSolana = exocoreGateway.quote(solanaClientChainId, message);
}

function test_RevertWhen_CallerNotOwner() public {
Expand Down Expand Up @@ -587,6 +619,24 @@ contract AddWhitelistTokens is SetUp {
vm.stopPrank();
}

function test_Success_AddWhiteListTokenOnSolana() public {
vm.startPrank(exocoreValidatorSet.addr);
vm.expectEmit(address(exocoreGateway));
emit WhitelistTokenAdded(solanaClientChainId, bytes32(bytes20(address(restakeToken))));
vm.expectEmit(address(exocoreGateway));
emit MessageSent(Action.REQUEST_ADD_WHITELIST_TOKEN, generateUID(1, false, true), 1, nativeFeeForSolana);
exocoreGateway.addWhitelistToken{value: nativeFeeForSolana}(
solanaClientChainId,
bytes32(bytes20(address(restakeToken))),
9,
"RestakeToken",
"Spl LST token",
"oracleInfo",
5000 * 1e9
);
vm.stopPrank();
}

}

contract UpdateWhitelistTokens is SetUp {
Expand Down

0 comments on commit 54cb293

Please sign in to comment.