Skip to content

Commit

Permalink
Merge branch 'main' into gjermund/201-add-tests-for-batch-tx-and-ack-…
Browse files Browse the repository at this point in the history
…relaying-from-eth-to-cosmos
  • Loading branch information
srdtrk committed Jan 10, 2025
2 parents 7e18d10 + f8ed823 commit c04f85d
Show file tree
Hide file tree
Showing 8 changed files with 16 additions and 145 deletions.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,10 @@ The following benchmarks are for a single packet transfer without aggregation.
| **Contract** | **Method** | **Description** | **Gas (groth16)** | **Gas (plonk)** |
|:---:|:---:|:---:|:---:|:---:|
| `ICS26Router.sol` | `sendPacket` | Initiating an IBC transfer with an `ERC20`. | ~186,808 | ~186,808 |
| `ICS26Router.sol` | `recvPacket` | Receiving _back_ an `ERC20` token. | ~541,800 | ~626,140 |
| `ICS26Router.sol` | `recvPacket` | Receiving a _new_ Cosmos token for the first time. (Deploying an `ERC20` contract) | ~1,436,748 | ~1,520,226 |
| `ICS26Router.sol` | `ackPacket` | Acknowledging an ICS20 packet. | ~419,121 | ~502,895 |
| `ICS26Router.sol` | `timeoutPacket` | Timing out an ICS20 packet | ~471,313 | ~554,696 |
| `ICS26Router.sol` | `recvPacket` | Receiving _back_ an `ERC20` token. | ~542,281 | ~626,502 |
| `ICS26Router.sol` | `recvPacket` | Receiving a _new_ Cosmos token for the first time. (Deploying an `ERC20` contract) | ~1,436,540 | ~1,520,035 |
| `ICS26Router.sol` | `ackPacket` | Acknowledging an ICS20 packet. | ~419,062 | ~502,836 |
| `ICS26Router.sol` | `timeoutPacket` | Timing out an ICS20 packet | ~471,947 | ~555,364 |
### Aggregated Packet Benchmarks
Expand All @@ -175,8 +175,8 @@ Since there is no meaningful difference in gas costs between plonk and groth16 i
| **ICS26Router Method** | **Description** | **Avg Gas (25 packets)** | **Avg Gas (50 packets)** |
|:---:|:---:|:---:|:---:|
| `multicall/recvPacket` | Receiving _back_ an `ERC20` token. | ~198,341 | ~191,618 |
| `multicall/ackPacket` | Acknowledging an ICS20 packet. | ~105,336 | ~99,365 |
| `multicall/recvPacket` | Receiving _back_ an `ERC20` token. | ~198,822 | ~192,116 |
| `multicall/ackPacket` | Acknowledging an ICS20 packet. | ~105,277 | ~99,306 |
Note: These gas benchmarks are with Groth16.
Expand Down
32 changes: 0 additions & 32 deletions abi/ICS20Transfer.json
Original file line number Diff line number Diff line change
Expand Up @@ -594,38 +594,6 @@
"name": "ICS20AbiEncodingFailure",
"inputs": []
},
{
"type": "error",
"name": "ICS20BytesSliceOutOfBounds",
"inputs": [
{
"name": "length",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "start",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "end",
"type": "uint256",
"internalType": "uint256"
}
]
},
{
"type": "error",
"name": "ICS20BytesSliceOverflow",
"inputs": [
{
"name": "length",
"type": "uint256",
"internalType": "uint256"
}
]
},
{
"type": "error",
"name": "ICS20DenomNotFound",
Expand Down
2 changes: 1 addition & 1 deletion abigen/ics20transfer/contract.go

Large diffs are not rendered by default.

Binary file modified bun.lockb
Binary file not shown.
4 changes: 2 additions & 2 deletions contracts/ICS20Transfer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { IICS26Router } from "./interfaces/IICS26Router.sol";
import { IICS26RouterMsgs } from "./msgs/IICS26RouterMsgs.sol";
import { IBCERC20 } from "./utils/IBCERC20.sol";
import { Escrow } from "./utils/Escrow.sol";
import { Bytes } from "@openzeppelin/utils/Bytes.sol";

using SafeERC20 for IERC20;

Expand Down Expand Up @@ -275,8 +276,7 @@ contract ICS20Transfer is
erc20Address = findOrCreateERC20Address(fullDenomPath, baseDenom);
} else {
// we are the source of this token: we remove the source prefix and expect the denom to be an erc20 address
string memory erc20AddressStr =
string(ICS20Lib.slice(denomBz, sourceDenomPrefix.length, denomBz.length - sourceDenomPrefix.length));
string memory erc20AddressStr = string(Bytes.slice(denomBz, sourceDenomPrefix.length));
erc20Address = ICS20Lib.mustHexStringToAddress(erc20AddressStr);
}

Expand Down
10 changes: 0 additions & 10 deletions contracts/errors/IICS20Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,4 @@ interface IICS20Errors {

/// @notice Abi encoding/decoding failure
error ICS20AbiEncodingFailure();

/// @notice Bytes slice overflow
/// @param length length of the slice
error ICS20BytesSliceOverflow(uint256 length);

/// @notice Bytes slice out of bounds
/// @param length length of the bytes
/// @param start start index
/// @param end end index
error ICS20BytesSliceOutOfBounds(uint256 length, uint256 start, uint256 end);
}
97 changes: 5 additions & 92 deletions contracts/utils/ICS20Lib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity ^0.8.28;
// solhint-disable no-inline-assembly

import { Strings } from "@openzeppelin/utils/Strings.sol";
import { Bytes } from "@openzeppelin/utils/Bytes.sol";
import { IICS20Errors } from "../errors/IICS20Errors.sol";
import { IICS26RouterMsgs } from "../msgs/IICS26RouterMsgs.sol";
import { IICS20TransferMsgs } from "../msgs/IICS20TransferMsgs.sol";
Expand Down Expand Up @@ -112,107 +113,19 @@ library ICS20Lib {
/// @return address value
/// @return true if the conversion was successful
function hexStringToAddress(string memory addrHexString) internal pure returns (address, bool) {
bytes memory addrBytes = bytes(addrHexString);
if (addrBytes.length != 42) {
return (address(0), false);
} else if (addrBytes[0] != "0" || addrBytes[1] != "x") {
return (address(0), false);
}
uint256 addr = 0;
unchecked {
for (uint256 i = 2; i < 42; i++) {
uint256 c = uint256(uint8(addrBytes[i]));
if (c >= 48 && c <= 57) {
addr = addr * 16 + (c - 48);
} else if (c >= 97 && c <= 102) {
addr = addr * 16 + (c - 87);
} else if (c >= 65 && c <= 70) {
addr = addr * 16 + (c - 55);
} else {
return (address(0), false);
}
}
}
return (address(uint160(addr)), true);
(bool success, address addr) = Strings.tryParseAddress(addrHexString);
return (addr, success);
}

/// @notice mustHexStringToAddress converts a hex string to an address and reverts on failure.
/// @param addrHexString hex address string
/// @return address the converted address
function mustHexStringToAddress(string memory addrHexString) internal pure returns (address) {
(address addr, bool success) = hexStringToAddress(addrHexString);
(bool success, address addr) = Strings.tryParseAddress(addrHexString);
require(success, IICS20Errors.ICS20InvalidAddress(addrHexString));
return addr;
}

/// @notice slice returns a slice of the original bytes from `start` to `start + length`.
/// @dev This is a copy from https://github.com/GNSPS/solidity-bytes-utils/blob/v0.8.0/contracts/BytesLib.sol
/// @param _bytes bytes
/// @param _start start index
/// @param _length length
/// @return sliced bytes
function slice(bytes memory _bytes, uint256 _start, uint256 _length) internal pure returns (bytes memory) {
if (_length + 31 < _length) {
revert IICS20Errors.ICS20BytesSliceOverflow(_length);
} else if (_start + _length > _bytes.length) {
revert IICS20Errors.ICS20BytesSliceOutOfBounds(_bytes.length, _start, _start + _length);
}

bytes memory tempBytes;

assembly {
switch iszero(_length)
case 0 {
// Get a location of some free memory and store it in tempBytes as
// Solidity does for memory variables.
tempBytes := mload(0x40)

// The first word of the slice result is potentially a partial
// word read from the original array. To read it, we calculate
// the length of that partial word and start copying that many
// bytes into the array. The first word we copy will start with
// data we don't care about, but the last `lengthmod` bytes will
// land at the beginning of the contents of the new array. When
// we're done copying, we overwrite the full first word with
// the actual length of the slice.
let lengthmod := and(_length, 31)

// The multiplication in the next line is necessary
// because when slicing multiples of 32 bytes (lengthmod == 0)
// the following copy loop was copying the origin's length
// and then ending prematurely not copying everything it should.
let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
let end := add(mc, _length)

for {
// The multiplication in the next line has the same exact purpose
// as the one above.
let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
} lt(mc, end) {
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} { mstore(mc, mload(cc)) }

mstore(tempBytes, _length)

//update free-memory pointer
//allocating the array padded to 32 bytes like the compiler does now
mstore(0x40, and(add(mc, 31), not(31)))
}
//if we want a zero-length slice let's just return a zero-length array
default {
tempBytes := mload(0x40)
//zero out the 32 bytes slice we are about to return
//we need to do it because Solidity does not garbage collect
mstore(tempBytes, 0)

mstore(0x40, add(tempBytes, 0x20))
}
}

return tempBytes;
}

/// @notice equal returns true if two byte arrays are equal.
/// @param a bytes
/// @param b bytes
Expand All @@ -229,7 +142,7 @@ library ICS20Lib {
if (denomBz.length < prefix.length) {
return false;
}
return equal(slice(denomBz, 0, prefix.length), prefix);
return equal(Bytes.slice(denomBz, 0, prefix.length), prefix);
}

/// @notice errorAck returns an error acknowledgement.
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
"url": "https://github.com/srdtrk"
},
"dependencies": {
"@openzeppelin/contracts": "^5.1.0"
"@openzeppelin/contracts": "^5.2.0"
},
"devDependencies": {
"sp1-contracts": "github:succinctlabs/sp1-contracts#275691af9bfaf67158f6df1f4c3c1646eb03eed0",
"forge-std": "github:foundry-rs/forge-std#v1.9.4",
"solhint": "^5.0.3",
"solhint": "^5.0.4",
"@defi-wonderland/natspec-smells": "^1.1.5"
},
"keywords": [
Expand Down

0 comments on commit c04f85d

Please sign in to comment.