From afa564460a5614ccb557533800e3a4cc95e89958 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Wed, 18 Feb 2026 17:33:25 +0400 Subject: [PATCH 01/10] imp: add validation --- .../utils/CosmosIFTSendCallConstructor.sol | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/contracts/utils/CosmosIFTSendCallConstructor.sol b/contracts/utils/CosmosIFTSendCallConstructor.sol index 5740d372d..0595e24d6 100644 --- a/contracts/utils/CosmosIFTSendCallConstructor.sol +++ b/contracts/utils/CosmosIFTSendCallConstructor.sol @@ -11,6 +11,10 @@ import { IERC165 } from "@openzeppelin-contracts/utils/introspection/IERC165.sol /// @notice Constructs ICS27-GMP call data for minting IFT tokens on Cosmos SDK-based counterparty chains /// @dev This implementation encodes mint calls in protojson format for Cosmos SDK IFT module contract CosmosIFTSendCallConstructor is IIFTSendCallConstructor, ERC165 { + /// @notice Error thrown when the receiver address is invalid + /// @param receiver The invalid receiver string + error CosmosIFTInvalidReceiver(string receiver); + /// @notice The type URL for the MsgIFTMint message in the TokenFactory module // natlint-disable-next-line MissingInheritdoc string public bridgeReceiveTypeUrl; @@ -37,6 +41,10 @@ contract CosmosIFTSendCallConstructor is IIFTSendCallConstructor, ERC165 { /// @inheritdoc IIFTSendCallConstructor /// @dev Encodes the mint call as protojson CosmosTx for Cosmos SDK ICS27-GMP module function constructMintCall(string calldata receiver, uint256 amount) external view returns (bytes memory) { + if (!_validateReceiver(receiver)) { + revert CosmosIFTInvalidReceiver(receiver); + } + return abi.encodePacked( "{\"messages\":[{\"@type\":\"", bridgeReceiveTypeUrl, @@ -56,4 +64,49 @@ contract CosmosIFTSendCallConstructor is IIFTSendCallConstructor, ERC165 { function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { return interfaceId == type(IIFTSendCallConstructor).interfaceId || super.supportsInterface(interfaceId); } + + function _validateReceiver(string calldata receiver) internal pure returns (bool) { + if (bytes(receiver).length == 0) { + return false; + } + + // We allow eth addresses as receivers due to cosmos/evm + (bool isAddress,) = Strings.tryParseAddress(receiver); + if (isAddress) { + return true; + } + + // We also allow bech32 addresses for cosmos chains + // We will only allow HRPs which consist of [a-z0-9] and do not contain "1". If the HRP contains "1", then we will reject the address. + // All known Cosmos SDK chains have HRPs that do not contain "1", so this is a reasonable heuristic. + // We only allow lowercase alphanumeric characters excluding "1", "b", "i", and "o" as per bech32 specification, but we do not enforce the full bech32 checksum as it is not critical for our use case. + bool isHrp = true; + for (uint256 i = 0; i < bytes(receiver).length; i++) { + uint256 c = uint256(uint8(bytes(receiver)[i])); + if (isHrp) { + if (c == 0x31) { // "1" + isHrp = false; + continue; + } + if ((c >= 0x61 && c <= 0x7A) || (c >= 0x30 && c <= 0x39)) { // a-z, 0-9 + continue; + } + return false; + } else { + if ((c == 0x31) || (c == 0x62) || (c == 0x69) || (c == 0x6F)) { // "1", "b", "i", "o" + return false; + } + if ((c >= 0x61 && c <= 0x7A) || (c >= 0x30 && c <= 0x39)) { // a-z, 0-9 + continue; + } + return false; + } + } + + if (isHrp) { + return false; // must contain "1" separator + } + + return true; + } } From d1aa3da448e2742a2c7c2a790e0c573f841df71b Mon Sep 17 00:00:00 2001 From: srdtrk Date: Wed, 18 Feb 2026 18:02:11 +0400 Subject: [PATCH 02/10] test: added tests --- test/solidity-ibc/IFTTest.t.sol | 249 ++++++++++++++++++-------------- 1 file changed, 144 insertions(+), 105 deletions(-) diff --git a/test/solidity-ibc/IFTTest.t.sol b/test/solidity-ibc/IFTTest.t.sol index a67893ffa..1edf18ab5 100644 --- a/test/solidity-ibc/IFTTest.t.sol +++ b/test/solidity-ibc/IFTTest.t.sol @@ -909,13 +909,20 @@ contract IFTTest is Test { } struct UpgradeTestCase { - string name; - address caller; - bool ownable; - bytes expectedRevert; - } - - // CosmosIFTSendCallConstructor Tests + string name; + address caller; + bool ownable; + bytes expectedRevert; + } + + struct CosmosMintCallTestCase { + string name; + string receiver; + uint256 amount; + bytes expectedRevert; + } + + // CosmosIFTSendCallConstructor Tests function test_cosmosCallConstructor_constructor() public { string memory typeUrl = "/wfchain.ift.MsgIFTMint"; @@ -929,104 +936,136 @@ contract IFTTest is Test { assertEq(constructor_.icaAddress(), ica); } - function test_cosmosCallConstructor_constructMintCall() public { - string memory typeUrl = "/wfchain.ift.MsgIFTMint"; - string memory tokenDenom = "testift"; - string memory ica = "wf1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq5a9p63"; - - CosmosIFTSendCallConstructor constructor_ = new CosmosIFTSendCallConstructor(typeUrl, tokenDenom, ica); - - string memory receiver = "wf1receiver123"; - uint256 amount = 1_000_000; - - bytes memory callData = constructor_.constructMintCall(receiver, amount); - - bytes memory expected = abi.encodePacked( - "{\"messages\":[{\"@type\":\"", - typeUrl, - "\",\"signer\":\"", - ica, - "\",\"denom\":\"", - tokenDenom, - "\",\"receiver\":\"", - receiver, - "\",\"amount\":\"", - Strings.toString(amount), - "\"}]}" - ); - - assertEq(callData, expected); - } - - function testFuzz_cosmosCallConstructor_constructMintCall(uint256 amount) public { - string memory typeUrl = "/custom.module.MsgMint"; - string memory tokenDenom = "ibc/ABC123"; - string memory ica = "cosmos1ica456"; - - CosmosIFTSendCallConstructor constructor_ = new CosmosIFTSendCallConstructor(typeUrl, tokenDenom, ica); - - string memory receiver = "cosmos1receiver789"; - - bytes memory callData = constructor_.constructMintCall(receiver, amount); - - bytes memory expected = abi.encodePacked( - "{\"messages\":[{\"@type\":\"", - typeUrl, - "\",\"signer\":\"", - ica, - "\",\"denom\":\"", - tokenDenom, - "\",\"receiver\":\"", - receiver, - "\",\"amount\":\"", - Strings.toString(amount), - "\"}]}" - ); - - assertEq(callData, expected); - } - - function test_cosmosCallConstructor_constructMintCall_zeroAmount() public { - CosmosIFTSendCallConstructor constructor_ = - new CosmosIFTSendCallConstructor("/wfchain.ift.MsgIFTMint", "testift", "wf1ica"); - - bytes memory callData = constructor_.constructMintCall("wf1receiver", 0); - - assertTrue(callData.length > 0); - bytes memory expected = abi.encodePacked( - "{\"messages\":[{\"@type\":\"/wfchain.ift.MsgIFTMint\",\"signer\":\"wf1ica\",\"denom\":\"testift\",\"receiver\":\"wf1receiver\",\"amount\":\"0\"}]}" - ); - assertEq(callData, expected); - } - - function test_cosmosCallConstructor_constructMintCall_largeAmount() public { - CosmosIFTSendCallConstructor constructor_ = - new CosmosIFTSendCallConstructor("/wfchain.ift.MsgIFTMint", "testift", "wf1ica"); - - uint256 largeAmount = type(uint256).max; - bytes memory callData = constructor_.constructMintCall("wf1receiver", largeAmount); - - bytes memory expected = abi.encodePacked( - "{\"messages\":[{\"@type\":\"/wfchain.ift.MsgIFTMint\",\"signer\":\"wf1ica\",\"denom\":\"testift\",\"receiver\":\"wf1receiver\",\"amount\":\"", - Strings.toString(largeAmount), - "\"}]}" - ); - assertEq(callData, expected); - } - - function test_cosmosCallConstructor_emptyStrings() public { - CosmosIFTSendCallConstructor constructor_ = new CosmosIFTSendCallConstructor("", "", ""); - - assertEq(constructor_.bridgeReceiveTypeUrl(), ""); - assertEq(constructor_.denom(), ""); - assertEq(constructor_.icaAddress(), ""); - - bytes memory callData = constructor_.constructMintCall("receiver", 100); - bytes memory expected = abi.encodePacked( - "{\"messages\":[{\"@type\":\"\",\"signer\":\"\",\"denom\":\"\",\"receiver\":\"receiver\",\"amount\":\"100\"}]}" - ); - assertEq(callData, expected); - } + function fixtureCosmosMintCallTC() public returns (CosmosMintCallTestCase[] memory) { + CosmosMintCallTestCase[] memory testCases = new CosmosMintCallTestCase[](11); + + testCases[0] = CosmosMintCallTestCase({ + name: "success: bech32 receiver", + receiver: "wf1address234", + amount: 1_000_000, + expectedRevert: "" + }); + testCases[1] = CosmosMintCallTestCase({ + name: "success: hex address receiver", + receiver: Strings.toHexString(makeAddr("receiver")), + amount: 2, + expectedRevert: "" + }); + testCases[2] = CosmosMintCallTestCase({ + name: "revert: empty receiver", + receiver: "", + amount: 10, + expectedRevert: abi.encodeWithSelector( + CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "" + ) + }); + testCases[3] = CosmosMintCallTestCase({ + name: "revert: missing separator", + receiver: "wf2345a", + amount: 10, + expectedRevert: abi.encodeWithSelector( + CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "wf2345a" + ) + }); + testCases[4] = CosmosMintCallTestCase({ + name: "revert: invalid hrp capital", + receiver: "WF12345a", + amount: 10, + expectedRevert: abi.encodeWithSelector( + CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "WF12345a" + ) + }); + testCases[5] = CosmosMintCallTestCase({ + name: "revert: invalid data char 1", + receiver: "wf1rece1ver", + amount: 10, + expectedRevert: abi.encodeWithSelector( + CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "wf1rece1ver" + ) + }); + testCases[6] = CosmosMintCallTestCase({ + name: "revert: invalid data char i", + receiver: "wf1receiver", + amount: 10, + expectedRevert: abi.encodeWithSelector( + CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "wf1receiver" + ) + }); + testCases[7] = CosmosMintCallTestCase({ + name: "revert: invalid data char o", + receiver: "wf1receiver", + amount: 10, + expectedRevert: abi.encodeWithSelector( + CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "wf1receiver" + ) + }); + testCases[8] = CosmosMintCallTestCase({ + name: "revert: invalid data char b", + receiver: "wf1recebr", + amount: 10, + expectedRevert: abi.encodeWithSelector( + CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "wf1recebr" + ) + }); + testCases[9] = CosmosMintCallTestCase({ + name: "revert: invalid data char {", + receiver: "wf1address{", + amount: 10, + expectedRevert: abi.encodeWithSelector( + CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "wf1address{" + ) + }); + testCases[10] = CosmosMintCallTestCase({ + name: "revert: invalid data char \"", + receiver: "wf1address\"", + amount: 10, + expectedRevert: abi.encodeWithSelector( + CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "wf1address\"" + ) + }); + + return testCases; + } + + function tableCosmosMintCallTest(CosmosMintCallTestCase memory cosmosMintCallTC) public { + string memory typeUrl = "/wfchain.ift.MsgIFTMint"; + string memory tokenDenom = "testift"; + string memory ica = "wf1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq5a9p63"; + + CosmosIFTSendCallConstructor constructor_ = new CosmosIFTSendCallConstructor(typeUrl, tokenDenom, ica); + + if (cosmosMintCallTC.expectedRevert.length != 0) { + vm.expectRevert(cosmosMintCallTC.expectedRevert); + constructor_.constructMintCall(cosmosMintCallTC.receiver, cosmosMintCallTC.amount); + return; + } + + bytes memory callData = constructor_.constructMintCall(cosmosMintCallTC.receiver, cosmosMintCallTC.amount); + + bytes memory expected = abi.encodePacked( + "{\"messages\":[{\"@type\":\"", + typeUrl, + "\",\"signer\":\"", + ica, + "\",\"denom\":\"", + tokenDenom, + "\",\"receiver\":\"", + cosmosMintCallTC.receiver, + "\",\"amount\":\"", + Strings.toString(cosmosMintCallTC.amount), + "\"}]}" + ); + + assertEq(callData, expected); + } + + function test_cosmosCallConstructor_constructMintCall() public { + CosmosMintCallTestCase[] memory testCases = fixtureCosmosMintCallTC(); + for (uint256 i = 0; i < testCases.length; i++) { + CosmosMintCallTestCase memory testCase = testCases[i]; + tableCosmosMintCallTest(testCase); + } + } function test_cosmosCallConstructor_supportsInterface() public { CosmosIFTSendCallConstructor constructor_ = From 46ea8eea594653afbcf5964efb57a71e6e46ce46 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Wed, 18 Feb 2026 18:14:07 +0400 Subject: [PATCH 03/10] test: more cases --- test/solidity-ibc/IFTTest.t.sol | 212 +++++++++++++++++++------------- 1 file changed, 125 insertions(+), 87 deletions(-) diff --git a/test/solidity-ibc/IFTTest.t.sol b/test/solidity-ibc/IFTTest.t.sol index 1edf18ab5..6d07176e6 100644 --- a/test/solidity-ibc/IFTTest.t.sol +++ b/test/solidity-ibc/IFTTest.t.sol @@ -853,77 +853,7 @@ contract IFTTest is Test { bytes4 erc165Id = 0x01ffc9a7; assertTrue(IERC165(address(ift)).supportsInterface(erc165Id)); } - - struct IFTMintTestCase { - string name; - bool ownable; - address caller; - IICS27GMPMsgs.AccountIdentifier accountId; - address receiver; - uint256 amount; - bytes expectedRevert; - } - - struct OnAckPacketTestCase { - string name; - address caller; - bool success; - IIBCAppCallbacks.OnAcknowledgementPacketCallback callback; - bytes expectedRevert; - } - - struct OnTimeoutPacketTestCase { - string name; - address caller; - IIBCAppCallbacks.OnTimeoutPacketCallback callback; - bytes expectedRevert; - } - - struct IFTTransferTestCase { - string name; - address caller; - bool ownable; - string clientId; - string receiver; - uint256 amount; - uint64 timeoutTimestamp; - bytes expectedRevert; - } - - struct RemoveIFTBridgeTestCase { - string name; - address caller; - bool ownable; - string clientId; - bytes expectedRevert; - } - - struct RegisterIFTBridgeTestCase { - string name; - address caller; - bool ownable; - string clientId; - address iftSendCallConstructor; - string counterpartyIFT; - bytes expectedRevert; - } - - struct UpgradeTestCase { - string name; - address caller; - bool ownable; - bytes expectedRevert; - } - struct CosmosMintCallTestCase { - string name; - string receiver; - uint256 amount; - bytes expectedRevert; - } - - // CosmosIFTSendCallConstructor Tests - function test_cosmosCallConstructor_constructor() public { string memory typeUrl = "/wfchain.ift.MsgIFTMint"; string memory tokenDenom = "uatom"; @@ -937,7 +867,7 @@ contract IFTTest is Test { } function fixtureCosmosMintCallTC() public returns (CosmosMintCallTestCase[] memory) { - CosmosMintCallTestCase[] memory testCases = new CosmosMintCallTestCase[](11); + CosmosMintCallTestCase[] memory testCases = new CosmosMintCallTestCase[](19); testCases[0] = CosmosMintCallTestCase({ name: "success: bech32 receiver", @@ -952,6 +882,54 @@ contract IFTTest is Test { expectedRevert: "" }); testCases[2] = CosmosMintCallTestCase({ + name: "success: cosmos16...", + receiver: "cosmos16a87dpmkutsfx7cddxt749urv62nkcajmr8nw2", + amount: 3, + expectedRevert: "" + }); + testCases[3] = CosmosMintCallTestCase({ + name: "success: cosmos1xmx...", + receiver: "cosmos1xmxsh7td3ga53afs2qpzskq4dl4g9a4x3uck7j", + amount: 4, + expectedRevert: "" + }); + testCases[4] = CosmosMintCallTestCase({ + name: "success: cosmos1t5u...", + receiver: "cosmos1t5u0jfg3ljsjrh2m9e47d4ny2hea7eehxrzdgd", + amount: 5, + expectedRevert: "" + }); + testCases[5] = CosmosMintCallTestCase({ + name: "success: cosmos1hs6...", + receiver: "cosmos1hs6sl3u2lmrwr58khexm4v9yjk333c7ts0rcuz", + amount: 6, + expectedRevert: "" + }); + testCases[6] = CosmosMintCallTestCase({ + name: "success: osmo10c3...", + receiver: "osmo10c3r27nekd9w7s3swwg3t9sf5hzrvqjftf2djk", + amount: 7, + expectedRevert: "" + }); + testCases[7] = CosmosMintCallTestCase({ + name: "success: osmo14dy...", + receiver: "osmo14dy272jhqz3gqduujt0gngqm76sg3e0fqhvzxa", + amount: 8, + expectedRevert: "" + }); + testCases[8] = CosmosMintCallTestCase({ + name: "success: osmo1hd97...", + receiver: "osmo1hd97pvymu9xx63xg3y4qn2sy4cfywtzv0527jg", + amount: 9, + expectedRevert: "" + }); + testCases[9] = CosmosMintCallTestCase({ + name: "success: osmo1txtz...", + receiver: "osmo1txtzjqtmgwjhgkdvwtdgfwhsda77ewhxtsclce", + amount: 10, + expectedRevert: "" + }); + testCases[10] = CosmosMintCallTestCase({ name: "revert: empty receiver", receiver: "", amount: 10, @@ -959,7 +937,7 @@ contract IFTTest is Test { CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "" ) }); - testCases[3] = CosmosMintCallTestCase({ + testCases[11] = CosmosMintCallTestCase({ name: "revert: missing separator", receiver: "wf2345a", amount: 10, @@ -967,7 +945,7 @@ contract IFTTest is Test { CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "wf2345a" ) }); - testCases[4] = CosmosMintCallTestCase({ + testCases[12] = CosmosMintCallTestCase({ name: "revert: invalid hrp capital", receiver: "WF12345a", amount: 10, @@ -975,7 +953,7 @@ contract IFTTest is Test { CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "WF12345a" ) }); - testCases[5] = CosmosMintCallTestCase({ + testCases[13] = CosmosMintCallTestCase({ name: "revert: invalid data char 1", receiver: "wf1rece1ver", amount: 10, @@ -983,7 +961,7 @@ contract IFTTest is Test { CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "wf1rece1ver" ) }); - testCases[6] = CosmosMintCallTestCase({ + testCases[14] = CosmosMintCallTestCase({ name: "revert: invalid data char i", receiver: "wf1receiver", amount: 10, @@ -991,7 +969,7 @@ contract IFTTest is Test { CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "wf1receiver" ) }); - testCases[7] = CosmosMintCallTestCase({ + testCases[15] = CosmosMintCallTestCase({ name: "revert: invalid data char o", receiver: "wf1receiver", amount: 10, @@ -999,7 +977,7 @@ contract IFTTest is Test { CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "wf1receiver" ) }); - testCases[8] = CosmosMintCallTestCase({ + testCases[16] = CosmosMintCallTestCase({ name: "revert: invalid data char b", receiver: "wf1recebr", amount: 10, @@ -1007,7 +985,7 @@ contract IFTTest is Test { CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "wf1recebr" ) }); - testCases[9] = CosmosMintCallTestCase({ + testCases[17] = CosmosMintCallTestCase({ name: "revert: invalid data char {", receiver: "wf1address{", amount: 10, @@ -1015,7 +993,7 @@ contract IFTTest is Test { CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "wf1address{" ) }); - testCases[10] = CosmosMintCallTestCase({ + testCases[18] = CosmosMintCallTestCase({ name: "revert: invalid data char \"", receiver: "wf1address\"", amount: 10, @@ -1059,14 +1037,6 @@ contract IFTTest is Test { assertEq(callData, expected); } - function test_cosmosCallConstructor_constructMintCall() public { - CosmosMintCallTestCase[] memory testCases = fixtureCosmosMintCallTC(); - for (uint256 i = 0; i < testCases.length; i++) { - CosmosMintCallTestCase memory testCase = testCases[i]; - tableCosmosMintCallTest(testCase); - } - } - function test_cosmosCallConstructor_supportsInterface() public { CosmosIFTSendCallConstructor constructor_ = new CosmosIFTSendCallConstructor("/wfchain.ift.MsgIFTMint", "testift", "wf1ica"); @@ -1080,3 +1050,71 @@ contract IFTTest is Test { assertFalse(constructor_.supportsInterface(randomId)); } } + + struct IFTMintTestCase { + string name; + bool ownable; + address caller; + IICS27GMPMsgs.AccountIdentifier accountId; + address receiver; + uint256 amount; + bytes expectedRevert; + } + + struct OnAckPacketTestCase { + string name; + address caller; + bool success; + IIBCAppCallbacks.OnAcknowledgementPacketCallback callback; + bytes expectedRevert; + } + + struct OnTimeoutPacketTestCase { + string name; + address caller; + IIBCAppCallbacks.OnTimeoutPacketCallback callback; + bytes expectedRevert; + } + + struct IFTTransferTestCase { + string name; + address caller; + bool ownable; + string clientId; + string receiver; + uint256 amount; + uint64 timeoutTimestamp; + bytes expectedRevert; + } + + struct RemoveIFTBridgeTestCase { + string name; + address caller; + bool ownable; + string clientId; + bytes expectedRevert; + } + + struct RegisterIFTBridgeTestCase { + string name; + address caller; + bool ownable; + string clientId; + address iftSendCallConstructor; + string counterpartyIFT; + bytes expectedRevert; + } + + struct UpgradeTestCase { + string name; + address caller; + bool ownable; + bytes expectedRevert; + } + + struct CosmosMintCallTestCase { + string name; + string receiver; + uint256 amount; + bytes expectedRevert; + } From 5a5636124029b9ab38fa7facc02f9d52eaae2c9a Mon Sep 17 00:00:00 2001 From: srdtrk Date: Wed, 18 Feb 2026 23:01:50 +0400 Subject: [PATCH 04/10] lint --- .../utils/CosmosIFTSendCallConstructor.sol | 20 +- test/solidity-ibc/IFTTest.t.sol | 459 +++++++++--------- 2 files changed, 240 insertions(+), 239 deletions(-) diff --git a/contracts/utils/CosmosIFTSendCallConstructor.sol b/contracts/utils/CosmosIFTSendCallConstructor.sol index 0595e24d6..26a380c50 100644 --- a/contracts/utils/CosmosIFTSendCallConstructor.sol +++ b/contracts/utils/CosmosIFTSendCallConstructor.sol @@ -77,26 +77,32 @@ contract CosmosIFTSendCallConstructor is IIFTSendCallConstructor, ERC165 { } // We also allow bech32 addresses for cosmos chains - // We will only allow HRPs which consist of [a-z0-9] and do not contain "1". If the HRP contains "1", then we will reject the address. - // All known Cosmos SDK chains have HRPs that do not contain "1", so this is a reasonable heuristic. - // We only allow lowercase alphanumeric characters excluding "1", "b", "i", and "o" as per bech32 specification, but we do not enforce the full bech32 checksum as it is not critical for our use case. + // We will only allow HRPs which consist of [a-z0-9] and do not contain "1". If the HRP contains "1", then we + // will reject the address. All known Cosmos SDK chains have HRPs that do not contain "1", so this is a + // reasonable heuristic. + // We only allow lowercase alphanumeric characters excluding "1", "b", "i", and "o" as per bech32 specification, + // but we do not enforce the full bech32 checksum as it is not critical for our use case. bool isHrp = true; for (uint256 i = 0; i < bytes(receiver).length; i++) { uint256 c = uint256(uint8(bytes(receiver)[i])); if (isHrp) { - if (c == 0x31) { // "1" + if (c == 0x31) { + // "1" isHrp = false; continue; } - if ((c >= 0x61 && c <= 0x7A) || (c >= 0x30 && c <= 0x39)) { // a-z, 0-9 + if ((c >= 0x61 && c <= 0x7A) || (c >= 0x30 && c <= 0x39)) { + // a-z, 0-9 continue; } return false; } else { - if ((c == 0x31) || (c == 0x62) || (c == 0x69) || (c == 0x6F)) { // "1", "b", "i", "o" + if ((c == 0x31) || (c == 0x62) || (c == 0x69) || (c == 0x6F)) { + // "1", "b", "i", "o" return false; } - if ((c >= 0x61 && c <= 0x7A) || (c >= 0x30 && c <= 0x39)) { // a-z, 0-9 + if ((c >= 0x61 && c <= 0x7A) || (c >= 0x30 && c <= 0x39)) { + // a-z, 0-9 continue; } return false; diff --git a/test/solidity-ibc/IFTTest.t.sol b/test/solidity-ibc/IFTTest.t.sol index 6d07176e6..2ac1fb659 100644 --- a/test/solidity-ibc/IFTTest.t.sol +++ b/test/solidity-ibc/IFTTest.t.sol @@ -853,7 +853,7 @@ contract IFTTest is Test { bytes4 erc165Id = 0x01ffc9a7; assertTrue(IERC165(address(ift)).supportsInterface(erc165Id)); } - + function test_cosmosCallConstructor_constructor() public { string memory typeUrl = "/wfchain.ift.MsgIFTMint"; string memory tokenDenom = "uatom"; @@ -867,176 +867,171 @@ contract IFTTest is Test { } function fixtureCosmosMintCallTC() public returns (CosmosMintCallTestCase[] memory) { - CosmosMintCallTestCase[] memory testCases = new CosmosMintCallTestCase[](19); - - testCases[0] = CosmosMintCallTestCase({ - name: "success: bech32 receiver", - receiver: "wf1address234", - amount: 1_000_000, - expectedRevert: "" - }); - testCases[1] = CosmosMintCallTestCase({ - name: "success: hex address receiver", - receiver: Strings.toHexString(makeAddr("receiver")), - amount: 2, - expectedRevert: "" - }); - testCases[2] = CosmosMintCallTestCase({ - name: "success: cosmos16...", - receiver: "cosmos16a87dpmkutsfx7cddxt749urv62nkcajmr8nw2", - amount: 3, - expectedRevert: "" - }); - testCases[3] = CosmosMintCallTestCase({ - name: "success: cosmos1xmx...", - receiver: "cosmos1xmxsh7td3ga53afs2qpzskq4dl4g9a4x3uck7j", - amount: 4, - expectedRevert: "" - }); - testCases[4] = CosmosMintCallTestCase({ - name: "success: cosmos1t5u...", - receiver: "cosmos1t5u0jfg3ljsjrh2m9e47d4ny2hea7eehxrzdgd", - amount: 5, - expectedRevert: "" - }); - testCases[5] = CosmosMintCallTestCase({ - name: "success: cosmos1hs6...", - receiver: "cosmos1hs6sl3u2lmrwr58khexm4v9yjk333c7ts0rcuz", - amount: 6, - expectedRevert: "" - }); - testCases[6] = CosmosMintCallTestCase({ - name: "success: osmo10c3...", - receiver: "osmo10c3r27nekd9w7s3swwg3t9sf5hzrvqjftf2djk", - amount: 7, - expectedRevert: "" - }); - testCases[7] = CosmosMintCallTestCase({ - name: "success: osmo14dy...", - receiver: "osmo14dy272jhqz3gqduujt0gngqm76sg3e0fqhvzxa", - amount: 8, - expectedRevert: "" - }); - testCases[8] = CosmosMintCallTestCase({ - name: "success: osmo1hd97...", - receiver: "osmo1hd97pvymu9xx63xg3y4qn2sy4cfywtzv0527jg", - amount: 9, - expectedRevert: "" - }); - testCases[9] = CosmosMintCallTestCase({ - name: "success: osmo1txtz...", - receiver: "osmo1txtzjqtmgwjhgkdvwtdgfwhsda77ewhxtsclce", - amount: 10, - expectedRevert: "" - }); - testCases[10] = CosmosMintCallTestCase({ - name: "revert: empty receiver", - receiver: "", - amount: 10, - expectedRevert: abi.encodeWithSelector( - CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "" - ) - }); - testCases[11] = CosmosMintCallTestCase({ - name: "revert: missing separator", - receiver: "wf2345a", - amount: 10, - expectedRevert: abi.encodeWithSelector( - CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "wf2345a" - ) - }); - testCases[12] = CosmosMintCallTestCase({ - name: "revert: invalid hrp capital", - receiver: "WF12345a", - amount: 10, - expectedRevert: abi.encodeWithSelector( - CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "WF12345a" - ) - }); - testCases[13] = CosmosMintCallTestCase({ - name: "revert: invalid data char 1", - receiver: "wf1rece1ver", - amount: 10, - expectedRevert: abi.encodeWithSelector( - CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "wf1rece1ver" - ) - }); - testCases[14] = CosmosMintCallTestCase({ - name: "revert: invalid data char i", - receiver: "wf1receiver", - amount: 10, - expectedRevert: abi.encodeWithSelector( - CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "wf1receiver" - ) - }); - testCases[15] = CosmosMintCallTestCase({ - name: "revert: invalid data char o", - receiver: "wf1receiver", - amount: 10, - expectedRevert: abi.encodeWithSelector( - CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "wf1receiver" - ) - }); - testCases[16] = CosmosMintCallTestCase({ - name: "revert: invalid data char b", - receiver: "wf1recebr", - amount: 10, - expectedRevert: abi.encodeWithSelector( - CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "wf1recebr" - ) - }); - testCases[17] = CosmosMintCallTestCase({ - name: "revert: invalid data char {", - receiver: "wf1address{", - amount: 10, - expectedRevert: abi.encodeWithSelector( - CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "wf1address{" - ) - }); - testCases[18] = CosmosMintCallTestCase({ - name: "revert: invalid data char \"", - receiver: "wf1address\"", - amount: 10, - expectedRevert: abi.encodeWithSelector( - CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "wf1address\"" - ) - }); - - return testCases; - } - - function tableCosmosMintCallTest(CosmosMintCallTestCase memory cosmosMintCallTC) public { - string memory typeUrl = "/wfchain.ift.MsgIFTMint"; - string memory tokenDenom = "testift"; - string memory ica = "wf1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq5a9p63"; - - CosmosIFTSendCallConstructor constructor_ = new CosmosIFTSendCallConstructor(typeUrl, tokenDenom, ica); - - if (cosmosMintCallTC.expectedRevert.length != 0) { - vm.expectRevert(cosmosMintCallTC.expectedRevert); - constructor_.constructMintCall(cosmosMintCallTC.receiver, cosmosMintCallTC.amount); - return; - } - - bytes memory callData = constructor_.constructMintCall(cosmosMintCallTC.receiver, cosmosMintCallTC.amount); - - bytes memory expected = abi.encodePacked( - "{\"messages\":[{\"@type\":\"", - typeUrl, - "\",\"signer\":\"", - ica, - "\",\"denom\":\"", - tokenDenom, - "\",\"receiver\":\"", - cosmosMintCallTC.receiver, - "\",\"amount\":\"", - Strings.toString(cosmosMintCallTC.amount), - "\"}]}" - ); - - assertEq(callData, expected); - } - + CosmosMintCallTestCase[] memory testCases = new CosmosMintCallTestCase[](19); + + testCases[0] = CosmosMintCallTestCase({ + name: "success: bech32 receiver", receiver: "wf1address234", amount: 1_000_000, expectedRevert: "" + }); + testCases[1] = CosmosMintCallTestCase({ + name: "success: hex address receiver", + receiver: Strings.toHexString(makeAddr("receiver")), + amount: 2, + expectedRevert: "" + }); + testCases[2] = CosmosMintCallTestCase({ + name: "success: cosmos16...", + receiver: "cosmos16a87dpmkutsfx7cddxt749urv62nkcajmr8nw2", + amount: 3, + expectedRevert: "" + }); + testCases[3] = CosmosMintCallTestCase({ + name: "success: cosmos1xmx...", + receiver: "cosmos1xmxsh7td3ga53afs2qpzskq4dl4g9a4x3uck7j", + amount: 4, + expectedRevert: "" + }); + testCases[4] = CosmosMintCallTestCase({ + name: "success: cosmos1t5u...", + receiver: "cosmos1t5u0jfg3ljsjrh2m9e47d4ny2hea7eehxrzdgd", + amount: 5, + expectedRevert: "" + }); + testCases[5] = CosmosMintCallTestCase({ + name: "success: cosmos1hs6...", + receiver: "cosmos1hs6sl3u2lmrwr58khexm4v9yjk333c7ts0rcuz", + amount: 6, + expectedRevert: "" + }); + testCases[6] = CosmosMintCallTestCase({ + name: "success: osmo10c3...", + receiver: "osmo10c3r27nekd9w7s3swwg3t9sf5hzrvqjftf2djk", + amount: 7, + expectedRevert: "" + }); + testCases[7] = CosmosMintCallTestCase({ + name: "success: osmo14dy...", + receiver: "osmo14dy272jhqz3gqduujt0gngqm76sg3e0fqhvzxa", + amount: 8, + expectedRevert: "" + }); + testCases[8] = CosmosMintCallTestCase({ + name: "success: osmo1hd97...", + receiver: "osmo1hd97pvymu9xx63xg3y4qn2sy4cfywtzv0527jg", + amount: 9, + expectedRevert: "" + }); + testCases[9] = CosmosMintCallTestCase({ + name: "success: osmo1txtz...", + receiver: "osmo1txtzjqtmgwjhgkdvwtdgfwhsda77ewhxtsclce", + amount: 10, + expectedRevert: "" + }); + testCases[10] = CosmosMintCallTestCase({ + name: "revert: empty receiver", + receiver: "", + amount: 10, + expectedRevert: abi.encodeWithSelector(CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "") + }); + testCases[11] = CosmosMintCallTestCase({ + name: "revert: missing separator", + receiver: "wf2345a", + amount: 10, + expectedRevert: abi.encodeWithSelector( + CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "wf2345a" + ) + }); + testCases[12] = CosmosMintCallTestCase({ + name: "revert: invalid hrp capital", + receiver: "WF12345a", + amount: 10, + expectedRevert: abi.encodeWithSelector( + CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "WF12345a" + ) + }); + testCases[13] = CosmosMintCallTestCase({ + name: "revert: invalid data char 1", + receiver: "wf1rece1ver", + amount: 10, + expectedRevert: abi.encodeWithSelector( + CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "wf1rece1ver" + ) + }); + testCases[14] = CosmosMintCallTestCase({ + name: "revert: invalid data char i", + receiver: "wf1receiver", + amount: 10, + expectedRevert: abi.encodeWithSelector( + CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "wf1receiver" + ) + }); + testCases[15] = CosmosMintCallTestCase({ + name: "revert: invalid data char o", + receiver: "wf1receiver", + amount: 10, + expectedRevert: abi.encodeWithSelector( + CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "wf1receiver" + ) + }); + testCases[16] = CosmosMintCallTestCase({ + name: "revert: invalid data char b", + receiver: "wf1recebr", + amount: 10, + expectedRevert: abi.encodeWithSelector( + CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "wf1recebr" + ) + }); + testCases[17] = CosmosMintCallTestCase({ + name: "revert: invalid data char {", + receiver: "wf1address{", + amount: 10, + expectedRevert: abi.encodeWithSelector( + CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "wf1address{" + ) + }); + testCases[18] = CosmosMintCallTestCase({ + name: "revert: invalid data char \"", + receiver: "wf1address\"", + amount: 10, + expectedRevert: abi.encodeWithSelector( + CosmosIFTSendCallConstructor.CosmosIFTInvalidReceiver.selector, "wf1address\"" + ) + }); + + return testCases; + } + + function tableCosmosMintCallTest(CosmosMintCallTestCase memory cosmosMintCallTC) public { + string memory typeUrl = "/wfchain.ift.MsgIFTMint"; + string memory tokenDenom = "testift"; + string memory ica = "wf1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq5a9p63"; + + CosmosIFTSendCallConstructor constructor_ = new CosmosIFTSendCallConstructor(typeUrl, tokenDenom, ica); + + if (cosmosMintCallTC.expectedRevert.length != 0) { + vm.expectRevert(cosmosMintCallTC.expectedRevert); + constructor_.constructMintCall(cosmosMintCallTC.receiver, cosmosMintCallTC.amount); + return; + } + + bytes memory callData = constructor_.constructMintCall(cosmosMintCallTC.receiver, cosmosMintCallTC.amount); + + bytes memory expected = abi.encodePacked( + "{\"messages\":[{\"@type\":\"", + typeUrl, + "\",\"signer\":\"", + ica, + "\",\"denom\":\"", + tokenDenom, + "\",\"receiver\":\"", + cosmosMintCallTC.receiver, + "\",\"amount\":\"", + Strings.toString(cosmosMintCallTC.amount), + "\"}]}" + ); + + assertEq(callData, expected); + } + function test_cosmosCallConstructor_supportsInterface() public { CosmosIFTSendCallConstructor constructor_ = new CosmosIFTSendCallConstructor("/wfchain.ift.MsgIFTMint", "testift", "wf1ica"); @@ -1051,70 +1046,70 @@ contract IFTTest is Test { } } - struct IFTMintTestCase { - string name; - bool ownable; - address caller; - IICS27GMPMsgs.AccountIdentifier accountId; - address receiver; - uint256 amount; - bytes expectedRevert; - } +struct IFTMintTestCase { + string name; + bool ownable; + address caller; + IICS27GMPMsgs.AccountIdentifier accountId; + address receiver; + uint256 amount; + bytes expectedRevert; +} - struct OnAckPacketTestCase { - string name; - address caller; - bool success; - IIBCAppCallbacks.OnAcknowledgementPacketCallback callback; - bytes expectedRevert; - } +struct OnAckPacketTestCase { + string name; + address caller; + bool success; + IIBCAppCallbacks.OnAcknowledgementPacketCallback callback; + bytes expectedRevert; +} - struct OnTimeoutPacketTestCase { - string name; - address caller; - IIBCAppCallbacks.OnTimeoutPacketCallback callback; - bytes expectedRevert; - } +struct OnTimeoutPacketTestCase { + string name; + address caller; + IIBCAppCallbacks.OnTimeoutPacketCallback callback; + bytes expectedRevert; +} - struct IFTTransferTestCase { - string name; - address caller; - bool ownable; - string clientId; - string receiver; - uint256 amount; - uint64 timeoutTimestamp; - bytes expectedRevert; - } +struct IFTTransferTestCase { + string name; + address caller; + bool ownable; + string clientId; + string receiver; + uint256 amount; + uint64 timeoutTimestamp; + bytes expectedRevert; +} - struct RemoveIFTBridgeTestCase { - string name; - address caller; - bool ownable; - string clientId; - bytes expectedRevert; - } +struct RemoveIFTBridgeTestCase { + string name; + address caller; + bool ownable; + string clientId; + bytes expectedRevert; +} - struct RegisterIFTBridgeTestCase { - string name; - address caller; - bool ownable; - string clientId; - address iftSendCallConstructor; - string counterpartyIFT; - bytes expectedRevert; - } +struct RegisterIFTBridgeTestCase { + string name; + address caller; + bool ownable; + string clientId; + address iftSendCallConstructor; + string counterpartyIFT; + bytes expectedRevert; +} + +struct UpgradeTestCase { + string name; + address caller; + bool ownable; + bytes expectedRevert; +} - struct UpgradeTestCase { - string name; - address caller; - bool ownable; - bytes expectedRevert; - } - - struct CosmosMintCallTestCase { - string name; - string receiver; - uint256 amount; - bytes expectedRevert; - } +struct CosmosMintCallTestCase { + string name; + string receiver; + uint256 amount; + bytes expectedRevert; +} From b816f403fccc1a999f22bc6965b2808051ebf785 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Wed, 18 Feb 2026 23:14:32 +0400 Subject: [PATCH 05/10] lint: more --- contracts/utils/CosmosIFTSendCallConstructor.sol | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/contracts/utils/CosmosIFTSendCallConstructor.sol b/contracts/utils/CosmosIFTSendCallConstructor.sol index 26a380c50..5819c1459 100644 --- a/contracts/utils/CosmosIFTSendCallConstructor.sol +++ b/contracts/utils/CosmosIFTSendCallConstructor.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.28; +// solhint-disable gas-strict-inequalities,code-complexity + import { IIFTSendCallConstructor } from "../interfaces/IIFTSendCallConstructor.sol"; import { Strings } from "@openzeppelin-contracts/utils/Strings.sol"; @@ -65,6 +67,9 @@ contract CosmosIFTSendCallConstructor is IIFTSendCallConstructor, ERC165 { return interfaceId == type(IIFTSendCallConstructor).interfaceId || super.supportsInterface(interfaceId); } + /// @notice Validates the receiver string to ensure it is either a valid Ethereum address or a valid bech32 address + /// with an appropriate HRP @param receiver The receiver string to validate + /// @return True if the receiver is valid, false otherwise function _validateReceiver(string calldata receiver) internal pure returns (bool) { if (bytes(receiver).length == 0) { return false; @@ -83,7 +88,7 @@ contract CosmosIFTSendCallConstructor is IIFTSendCallConstructor, ERC165 { // We only allow lowercase alphanumeric characters excluding "1", "b", "i", and "o" as per bech32 specification, // but we do not enforce the full bech32 checksum as it is not critical for our use case. bool isHrp = true; - for (uint256 i = 0; i < bytes(receiver).length; i++) { + for (uint256 i = 0; i < bytes(receiver).length; ++i) { uint256 c = uint256(uint8(bytes(receiver)[i])); if (isHrp) { if (c == 0x31) { From 494cc5fc2928dbed6d36f034eda0434694c19228 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Wed, 18 Feb 2026 23:28:10 +0400 Subject: [PATCH 06/10] lint: slither --- contracts/utils/CosmosIFTSendCallConstructor.sol | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contracts/utils/CosmosIFTSendCallConstructor.sol b/contracts/utils/CosmosIFTSendCallConstructor.sol index 5819c1459..0981b40b4 100644 --- a/contracts/utils/CosmosIFTSendCallConstructor.sol +++ b/contracts/utils/CosmosIFTSendCallConstructor.sol @@ -76,6 +76,8 @@ contract CosmosIFTSendCallConstructor is IIFTSendCallConstructor, ERC165 { } // We allow eth addresses as receivers due to cosmos/evm + // NOTE: We ignore the return value of tryParseAddress because we just want to check if it is a valid address format + // slither-disable-next-line unused-return (bool isAddress,) = Strings.tryParseAddress(receiver); if (isAddress) { return true; From ba5927c2c088a0b7bd55d0a3b697aa9bdb9828d7 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Wed, 18 Feb 2026 23:50:27 +0400 Subject: [PATCH 07/10] lint --- contracts/utils/CosmosIFTSendCallConstructor.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contracts/utils/CosmosIFTSendCallConstructor.sol b/contracts/utils/CosmosIFTSendCallConstructor.sol index 0981b40b4..105e773f0 100644 --- a/contracts/utils/CosmosIFTSendCallConstructor.sol +++ b/contracts/utils/CosmosIFTSendCallConstructor.sol @@ -76,7 +76,8 @@ contract CosmosIFTSendCallConstructor is IIFTSendCallConstructor, ERC165 { } // We allow eth addresses as receivers due to cosmos/evm - // NOTE: We ignore the return value of tryParseAddress because we just want to check if it is a valid address format + // NOTE: We ignore the return value of tryParseAddress because + // we only want to check if it is a valid address format // slither-disable-next-line unused-return (bool isAddress,) = Strings.tryParseAddress(receiver); if (isAddress) { From 6d5cebde81699adfff2200a82cacf0a52ae60a21 Mon Sep 17 00:00:00 2001 From: srdtrk Date: Wed, 18 Feb 2026 23:57:02 +0400 Subject: [PATCH 08/10] docs: fix natspec --- contracts/utils/CosmosIFTSendCallConstructor.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contracts/utils/CosmosIFTSendCallConstructor.sol b/contracts/utils/CosmosIFTSendCallConstructor.sol index 105e773f0..81f62b21b 100644 --- a/contracts/utils/CosmosIFTSendCallConstructor.sol +++ b/contracts/utils/CosmosIFTSendCallConstructor.sol @@ -68,7 +68,8 @@ contract CosmosIFTSendCallConstructor is IIFTSendCallConstructor, ERC165 { } /// @notice Validates the receiver string to ensure it is either a valid Ethereum address or a valid bech32 address - /// with an appropriate HRP @param receiver The receiver string to validate + /// with an appropriate HRP + /// @param receiver The receiver string to validate /// @return True if the receiver is valid, false otherwise function _validateReceiver(string calldata receiver) internal pure returns (bool) { if (bytes(receiver).length == 0) { From 70528d82b1a5796f1a6de3a3a07477d74154cad0 Mon Sep 17 00:00:00 2001 From: Mariusz Zak Date: Thu, 19 Feb 2026 10:11:54 +0100 Subject: [PATCH 09/10] fix(e2e): increase Anvil block gas limit to 50M to prevent forge broadcast deadlock --- e2e/interchaintestv8/e2esuite/setup_ethereum.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/e2e/interchaintestv8/e2esuite/setup_ethereum.go b/e2e/interchaintestv8/e2esuite/setup_ethereum.go index be79eadd2..2628e63f7 100644 --- a/e2e/interchaintestv8/e2esuite/setup_ethereum.go +++ b/e2e/interchaintestv8/e2esuite/setup_ethereum.go @@ -18,7 +18,8 @@ import ( ) const ( - baseAnvilChainID = 31337 + baseAnvilChainID = 31337 + anvilBlockGasLimit = "50000000" ) func (s *TestSuite) buildChainSpecs(cfg setupConfig) []*interchaintest.ChainSpec { @@ -33,9 +34,9 @@ func (s *TestSuite) buildChainSpecs(cfg setupConfig) []*interchaintest.ChainSpec // Single anvil: Cosmos chains + one Anvil specs := chainconfig.DefaultChainSpecs - specs = append(specs, &interchaintest.ChainSpec{ - ChainConfig: icfoundry.DefaultEthereumAnvilChainConfig("ethereum"), - }) + chainConfig := icfoundry.DefaultEthereumAnvilChainConfig("ethereum") + chainConfig.AdditionalStartArgs = append(chainConfig.AdditionalStartArgs, "--gas-limit", anvilBlockGasLimit) + specs = append(specs, &interchaintest.ChainSpec{ChainConfig: chainConfig}) return specs } @@ -46,7 +47,7 @@ func (s *TestSuite) buildAnvilSpecs(count int) []*interchaintest.ChainSpec { name := fmt.Sprintf("ethereum-%d", i) chainConfig := icfoundry.DefaultEthereumAnvilChainConfig(name) chainConfig.ChainID = chainID - chainConfig.AdditionalStartArgs = append(chainConfig.AdditionalStartArgs, "--chain-id", chainID) + chainConfig.AdditionalStartArgs = append(chainConfig.AdditionalStartArgs, "--chain-id", chainID, "--gas-limit", anvilBlockGasLimit) specs = append(specs, &interchaintest.ChainSpec{ChainConfig: chainConfig}) } return specs From fcd995cc0453898d38aaac45e958ebf4d8360d39 Mon Sep 17 00:00:00 2001 From: Mariusz Zak Date: Thu, 19 Feb 2026 11:13:09 +0100 Subject: [PATCH 10/10] fix(e2e): use bech32-formatted invalid addr - New _validateReceiver rejects hyphens in receiver - Use valid bech32 format with wrong checksum instead --- e2e/interchaintestv8/cosmos_ethereum_ift_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/interchaintestv8/cosmos_ethereum_ift_test.go b/e2e/interchaintestv8/cosmos_ethereum_ift_test.go index 5f893ba43..54ed90e27 100644 --- a/e2e/interchaintestv8/cosmos_ethereum_ift_test.go +++ b/e2e/interchaintestv8/cosmos_ethereum_ift_test.go @@ -878,7 +878,7 @@ func (s *CosmosEthereumIFTTestSuite) Test_IFTTransfer_FailedReceiveOnCosmos() { tc := s.setupIFTInfrastructure(ctx, s.prover, s.proofType) ethUserAddr := crypto.PubkeyToAddress(s.ethUser.PublicKey) - invalidCosmosAddr := "invalid-cosmos-address" + invalidCosmosAddr := "wf1aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" s.Require().True(s.Run("Mint tokens on Ethereum", func() { iftContract, err := evmift.NewContract(tc.ethIFTAddress, eth.RPCClient)