- CCIP/trustless bridge contracts, GRU tokens, DEX/PMM tests, reserve vault. - Token-aggregation service routes, planner, chain config, relay env templates. - Config snapshots and multi-chain deployment markdown updates. - gitignore services/btc-intake/dist/ (tsc output); do not track dist. Run forge build && forge test before deploy (large solc graph). Made-with: Cursor
55 lines
2.0 KiB
Solidity
55 lines
2.0 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.20;
|
|
|
|
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
import {IRouterClient} from "../ccip/IRouterClient.sol";
|
|
|
|
/**
|
|
* @title CrossChainFlashVaultCreditReceiver
|
|
* @notice **Source-chain** (or same-chain) CCIP receiver: forwards the first ERC-20 leg to a vault address encoded in `message.data`.
|
|
* @dev Use to **restore flash vault balance** after off-chain or destination-chain settlement. This is not an ERC-3156 repayment
|
|
* (that must still happen in the flash callback on the borrow chain). `data` = `abi.encode(address vault)`.
|
|
*/
|
|
contract CrossChainFlashVaultCreditReceiver {
|
|
using SafeERC20 for IERC20;
|
|
|
|
IRouterClient public immutable ccipRouter;
|
|
|
|
event LiquidityCredited(
|
|
bytes32 indexed messageId,
|
|
uint64 indexed sourceChainSelector,
|
|
address indexed vault,
|
|
address token,
|
|
uint256 amount
|
|
);
|
|
|
|
error OnlyRouter();
|
|
error NoTokens();
|
|
error BadData();
|
|
|
|
constructor(address ccipRouter_) {
|
|
require(ccipRouter_ != address(0), "CrossChainFlashVaultCreditReceiver: zero router");
|
|
ccipRouter = IRouterClient(ccipRouter_);
|
|
}
|
|
|
|
modifier onlyRouter() {
|
|
if (msg.sender != address(ccipRouter)) revert OnlyRouter();
|
|
_;
|
|
}
|
|
|
|
function ccipReceive(IRouterClient.Any2EVMMessage calldata message) external onlyRouter {
|
|
_credit(message);
|
|
}
|
|
|
|
function _credit(IRouterClient.Any2EVMMessage calldata m) private {
|
|
if (m.tokenAmounts.length == 0) revert NoTokens();
|
|
IRouterClient.TokenAmount calldata ta = m.tokenAmounts[0];
|
|
if (ta.token == address(0) || ta.amount == 0) revert NoTokens();
|
|
address vault = abi.decode(m.data, (address));
|
|
if (vault == address(0)) revert BadData();
|
|
IERC20(ta.token).safeTransfer(vault, ta.amount);
|
|
emit LiquidityCredited(m.messageId, m.sourceChainSelector, vault, ta.token, ta.amount);
|
|
}
|
|
}
|