Skip to content

Commit

Permalink
merge: branch 'main' into serdar/219-send-refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
srdtrk committed Feb 6, 2025
2 parents 15d1250 + e0db3bd commit 648a9f1
Show file tree
Hide file tree
Showing 13 changed files with 713 additions and 31 deletions.
99 changes: 99 additions & 0 deletions abi/ICS20Transfer.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,19 @@
],
"stateMutability": "view"
},
{
"type": "function",
"name": "getPauser",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "ibcERC20Contract",
Expand Down Expand Up @@ -57,6 +70,11 @@
"name": "ics26Router",
"type": "address",
"internalType": "address"
},
{
"name": "pauser",
"type": "address",
"internalType": "address"
}
],
"outputs": [],
Expand Down Expand Up @@ -293,6 +311,26 @@
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "pause",
"inputs": [],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "paused",
"inputs": [],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "proxiableUUID",
Expand Down Expand Up @@ -362,6 +400,26 @@
],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "setPauser",
"inputs": [
{
"name": "pauser",
"type": "address",
"internalType": "address"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "unpause",
"inputs": [],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "upgradeToAndCall",
Expand Down Expand Up @@ -393,6 +451,32 @@
],
"anonymous": false
},
{
"type": "event",
"name": "Paused",
"inputs": [
{
"name": "account",
"type": "address",
"indexed": false,
"internalType": "address"
}
],
"anonymous": false
},
{
"type": "event",
"name": "Unpaused",
"inputs": [
{
"name": "account",
"type": "address",
"indexed": false,
"internalType": "address"
}
],
"anonymous": false
},
{
"type": "event",
"name": "Upgraded",
Expand Down Expand Up @@ -433,6 +517,16 @@
"name": "ERC1967NonPayable",
"inputs": []
},
{
"type": "error",
"name": "EnforcedPause",
"inputs": []
},
{
"type": "error",
"name": "ExpectedPause",
"inputs": []
},
{
"type": "error",
"name": "FailedCall",
Expand Down Expand Up @@ -582,5 +676,10 @@
"internalType": "bytes32"
}
]
},
{
"type": "error",
"name": "Unauthorized",
"inputs": []
}
]
419 changes: 406 additions & 13 deletions abigen/ics20transfer/contract.go

Large diffs are not rendered by default.

37 changes: 29 additions & 8 deletions contracts/ICS20Transfer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { IIBCApp } from "./interfaces/IIBCApp.sol";
import { IERC20 } from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import { IICS20Transfer } from "./interfaces/IICS20Transfer.sol";
import { IICS26Router } from "./interfaces/IICS26Router.sol";
import { IIBCUUPSUpgradeable } from "./interfaces/IIBCUUPSUpgradeable.sol";

import { ReentrancyGuardTransientUpgradeable } from
"@openzeppelin-upgradeable/utils/ReentrancyGuardTransientUpgradeable.sol";
Expand All @@ -21,7 +22,7 @@ import { Escrow } from "./utils/Escrow.sol";
import { Strings } from "@openzeppelin-contracts/utils/Strings.sol";
import { Bytes } from "@openzeppelin-contracts/utils/Bytes.sol";
import { UUPSUpgradeable } from "@openzeppelin-contracts/proxy/utils/UUPSUpgradeable.sol";
import { IIBCUUPSUpgradeable } from "./interfaces/IIBCUUPSUpgradeable.sol";
import { IBCPausableUpgradeable } from "./utils/IBCPausableUpgradeable.sol";

using SafeERC20 for IERC20;

Expand All @@ -36,7 +37,8 @@ contract ICS20Transfer is
IIBCApp,
ReentrancyGuardTransientUpgradeable,
MulticallUpgradeable,
UUPSUpgradeable
UUPSUpgradeable,
IBCPausableUpgradeable
{
/// @notice Storage of the ICS20Transfer contract
/// @dev It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions when using with
Expand Down Expand Up @@ -64,9 +66,11 @@ contract ICS20Transfer is
/// @notice Initializes the contract instead of a constructor
/// @dev Meant to be called only once from the proxy
/// @param ics26Router The ICS26Router contract address
function initialize(address ics26Router) public initializer {
/// @param pauser The address that can pause and unpause the contract
function initialize(address ics26Router, address pauser) public initializer {
__ReentrancyGuardTransient_init();
__Multicall_init();
__IBCPausable_init(pauser);

ICS20TransferStorage storage $ = _getICS20TransferStorage();

Expand All @@ -87,7 +91,7 @@ contract ICS20Transfer is
}

/// @inheritdoc IICS20Transfer
function sendTransfer(IICS20TransferMsgs.SendTransferMsg calldata msg_) external override returns (uint32) {
function sendTransfer(IICS20TransferMsgs.SendTransferMsg calldata msg_) external whenNotPaused returns (uint32) {
IICS20TransferMsgs.FungibleTokenPacketData memory packetData =
ICS20Lib.newFungibleTokenPacketDataV1(_msgSender(), msg_);

Expand Down Expand Up @@ -118,7 +122,13 @@ contract ICS20Transfer is
}

/// @inheritdoc IIBCApp
function onRecvPacket(OnRecvPacketCallback calldata msg_) external onlyRouter nonReentrant returns (bytes memory) {
function onRecvPacket(OnRecvPacketCallback calldata msg_)
external
onlyRouter
nonReentrant
whenNotPaused
returns (bytes memory)
{
// TODO: Figure out if should actually error out, or if just error acking is enough (#112)

// Since this function mostly returns acks, also when it fails, the ics26router (the caller) will log the ack
Expand Down Expand Up @@ -179,7 +189,12 @@ contract ICS20Transfer is
}

/// @inheritdoc IIBCApp
function onAcknowledgementPacket(OnAcknowledgementPacketCallback calldata msg_) external onlyRouter nonReentrant {
function onAcknowledgementPacket(OnAcknowledgementPacketCallback calldata msg_)
external
onlyRouter
nonReentrant
whenNotPaused
{
if (keccak256(msg_.acknowledgement) != ICS20Lib.KECCAK256_SUCCESSFUL_ACKNOWLEDGEMENT_JSON) {
IICS20TransferMsgs.FungibleTokenPacketData memory packetData =
abi.decode(msg_.payload.value, (IICS20TransferMsgs.FungibleTokenPacketData));
Expand All @@ -189,7 +204,7 @@ contract ICS20Transfer is
}

/// @inheritdoc IIBCApp
function onTimeoutPacket(OnTimeoutPacketCallback calldata msg_) external onlyRouter nonReentrant {
function onTimeoutPacket(OnTimeoutPacketCallback calldata msg_) external onlyRouter nonReentrant whenNotPaused {
IICS20TransferMsgs.FungibleTokenPacketData memory packetData =
abi.decode(msg_.payload.value, (IICS20TransferMsgs.FungibleTokenPacketData));
_refundTokens(msg_.payload.sourcePort, msg_.sourceClient, packetData);
Expand Down Expand Up @@ -319,7 +334,13 @@ contract ICS20Transfer is
}

/// @inheritdoc UUPSUpgradeable
function _authorizeUpgrade(address) internal virtual override {
function _authorizeUpgrade(address) internal view override {
address ics26Router = address(_getICS26Router());
require(IIBCUUPSUpgradeable(ics26Router).isAdmin(_msgSender()), ICS20Unauthorized(_msgSender()));
}

/// @inheritdoc IBCPausableUpgradeable
function _authorizeSetPauser(address) internal view override {
address ics26Router = address(_getICS26Router());
require(IIBCUUPSUpgradeable(ics26Router).isAdmin(_msgSender()), ICS20Unauthorized(_msgSender()));
}
Expand Down
7 changes: 7 additions & 0 deletions contracts/errors/IIBCPausableUpgradeableErrors.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

interface IIBCPausableUpgradeableErrors {
/// @notice Error code returned when caller is not the pauser
error Unauthorized();
}
22 changes: 22 additions & 0 deletions contracts/interfaces/IIBCPausableUpgradeable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

interface IIBCPausableUpgradeable {
/// @notice Returns the pauser address
/// @return The pauser address
function getPauser() external view returns (address);

/// @notice Pauses the contract
/// @dev The caller must be the pauser
function pause() external;

/// @notice Unpauses the contract
/// @dev The caller must be the pauser
function unpause() external;

/// @notice Sets the pauser address
/// @dev Must be authorized by this contract
/// @dev This operation cannot be paused
/// @param pauser The new pauser address
function setPauser(address pauser) external;
}
79 changes: 79 additions & 0 deletions contracts/utils/IBCPausableUpgradeable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import { IIBCPausableUpgradeableErrors } from "../errors/IIBCPausableUpgradeableErrors.sol";
import { IIBCPausableUpgradeable } from "../interfaces/IIBCPausableUpgradeable.sol";
import { ContextUpgradeable } from "@openzeppelin-upgradeable/utils/ContextUpgradeable.sol";
import { PausableUpgradeable } from "@openzeppelin-upgradeable/utils/PausableUpgradeable.sol";

/// @title IBC Pausable Upgradeable contract
/// @notice This contract is an abstract contract for adding pausability to IBC contracts.
abstract contract IBCPausableUpgradeable is
IIBCPausableUpgradeableErrors,
IIBCPausableUpgradeable,
ContextUpgradeable,
PausableUpgradeable
{
/// @notice Storage of the IBCPausableUpgradeable contract
/// @dev It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions when using with
/// upgradeable contracts.
/// @param _pauser The address that can pause and unpause the contract
struct IBCPausableUpgradeableStorage {
address _pauser;
}

/// @notice ERC-7201 slot for the IBCPausableUpgradeable storage
/// @dev keccak256(abi.encode(uint256(keccak256("ibc.storage.IBCPausableUpgradeable")) - 1)) &
/// ~bytes32(uint256(0xff))
bytes32 private constant IBCPAUSABLEUPGRADEABLE_STORAGE_SLOT =
0x3cb0d659d6ec9ab9509297c9cf14e29ed0165d10590ef43eb31ba393e648af00;

/**
* @dev Initializes the contract in unpaused state.
*/
function __IBCPausable_init(address pauser) internal onlyInitializing {
__Pausable_init();

_getIBCPausableUpgradeableStorage()._pauser = pauser;
}

/// @inheritdoc IIBCPausableUpgradeable
function getPauser() public view returns (address) {
return _getIBCPausableUpgradeableStorage()._pauser;
}

/// @inheritdoc IIBCPausableUpgradeable
function pause() external onlyPauser {
_pause();
}

/// @inheritdoc IIBCPausableUpgradeable
function unpause() external onlyPauser {
_unpause();
}

/// @inheritdoc IIBCPausableUpgradeable
function setPauser(address pauser) public {
_authorizeSetPauser(pauser);
_getIBCPausableUpgradeableStorage()._pauser = pauser;
}

/// @notice Authorizes the setting of a new pauser
/// @param pauser The new address that can pause and unpause the contract
/// @dev This function must be overridden to add authorization logic
function _authorizeSetPauser(address pauser) internal virtual;

/// @notice Returns the storage of the IBCPausableUpgradeable contract
function _getIBCPausableUpgradeableStorage() internal pure returns (IBCPausableUpgradeableStorage storage $) {
// solhint-disable-next-line no-inline-assembly
assembly {
$.slot := IBCPAUSABLEUPGRADEABLE_STORAGE_SLOT
}
}

/// @notice Modifier to make a function callable only by the pauser
modifier onlyPauser() {
require(_msgSender() == getPauser(), Unauthorized());
_;
}
}
2 changes: 1 addition & 1 deletion contracts/utils/IBCUUPSUpgradeable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ abstract contract IBCUUPSUpgradeable is
function _authorizeUpgrade(address) internal view virtual override onlyAdmin { }
// solhint-disable-previous-line no-empty-blocks

/// @notice Returns the storage of the IBCUpgradeable contract
/// @notice Returns the storage of the IBCUUPSUpgradeable contract
function _getIBCUUPSUpgradeableStorage() internal pure returns (IBCUUPSUpgradeableStorage storage $) {
// solhint-disable-next-line no-inline-assembly
assembly {
Expand Down
3 changes: 2 additions & 1 deletion scripts/E2ETestDeploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ contract E2ETestDeploy is Script, IICS07TendermintMsgs {
address(ics20TransferLogic),
abi.encodeWithSelector(
ICS20Transfer.initialize.selector,
address(routerProxy)
address(routerProxy),
address(0)
)
);

Expand Down
3 changes: 2 additions & 1 deletion test/solidity-ibc/FixtureTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ abstract contract FixtureTest is Test, IICS07TendermintMsgs {
);

ERC1967Proxy transferProxy = new ERC1967Proxy(
address(ics20TransferLogic), abi.encodeWithSelector(ICS20Transfer.initialize.selector, address(routerProxy))
address(ics20TransferLogic),
abi.encodeWithSelector(ICS20Transfer.initialize.selector, address(routerProxy), address(0))
);

// ============== Step 3: Wire up the contracts ==============
Expand Down
Loading

0 comments on commit 648a9f1

Please sign in to comment.