Files
smom-dbis-138/contracts/ops/CWMirrorMeshBatch.sol

131 lines
4.0 KiB
Solidity
Raw Normal View History

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
interface IMintable {
function mint(address to, uint256 amount) external;
}
interface ICWMultiTokenBridgeL1 {
function lockAndSend(
address canonicalToken,
uint64 destinationChainSelector,
address recipient,
uint256 amount
) external payable returns (bytes32);
function calculateFee(
address canonicalToken,
uint64 destinationChainSelector,
address recipient,
uint256 amount
) external view returns (uint256);
}
/**
* @notice Batch mint + bridge for c* mirror mesh prep on Chain 138.
* @dev Called by the operator EOA. Grant IMintable.MINTER_ROLE to this contract on each c* token.
* Multicall3 cannot replace this: subcalls would see msg.sender = Multicall3, not the operator.
*/
contract CWMirrorMeshBatch {
using SafeERC20 for IERC20;
address public immutable bridge;
address public immutable link;
event MintMany(address indexed operator, uint256 callCount);
event BridgeMany(address indexed operator, address indexed token, uint256 chunkCount, uint256 totalAmount);
constructor(address bridge_, address link_) {
require(bridge_ != address(0), "zero bridge");
require(link_ != address(0), "zero link");
bridge = bridge_;
link = link_;
}
/// @dev Mint each amount to msg.sender (operator) in one transaction.
function mintMany(address[] calldata tokens, uint256[] calldata amounts) external {
uint256 n = tokens.length;
require(n == amounts.length, "length mismatch");
for (uint256 i; i < n; ) {
IMintable(tokens[i]).mint(msg.sender, amounts[i]);
unchecked {
++i;
}
}
emit MintMany(msg.sender, n);
}
/// @dev Mint one token repeatedly to msg.sender (same token, many chunks) in one transaction.
function mintChunks(address token, uint256[] calldata amounts) external {
uint256 n = amounts.length;
for (uint256 i; i < n; ) {
IMintable(token).mint(msg.sender, amounts[i]);
unchecked {
++i;
}
}
emit MintMany(msg.sender, n);
}
/**
* @dev Bridge one canonical token in multiple lockAndSend chunks from msg.sender.
* Pulls total token + total LINK fee upfront, then loops lockAndSend.
*/
function bridgeTokenChunks(
address canonicalToken,
uint64 destinationChainSelector,
address recipient,
uint256[] calldata amounts
) external payable {
uint256 n = amounts.length;
require(n > 0, "empty amounts");
uint256 totalAmount;
uint256 totalFee;
for (uint256 i; i < n; ) {
totalAmount += amounts[i];
totalFee += ICWMultiTokenBridgeL1(bridge).calculateFee(
canonicalToken,
destinationChainSelector,
recipient,
amounts[i]
);
unchecked {
++i;
}
}
IERC20(canonicalToken).safeTransferFrom(msg.sender, address(this), totalAmount);
IERC20(link).safeTransferFrom(msg.sender, address(this), totalFee);
for (uint256 i; i < n; ) {
uint256 amount = amounts[i];
uint256 fee = ICWMultiTokenBridgeL1(bridge).calculateFee(
canonicalToken,
destinationChainSelector,
recipient,
amount
);
IERC20(canonicalToken).forceApprove(bridge, amount);
IERC20(link).forceApprove(bridge, fee);
ICWMultiTokenBridgeL1(bridge).lockAndSend(
canonicalToken,
destinationChainSelector,
recipient,
amount
);
unchecked {
++i;
}
}
emit BridgeMany(msg.sender, canonicalToken, n, totalAmount);
}
}