feat: bridges, PMM, flash workflow, token-aggregation, and deployment docs
- 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
This commit is contained in:
57
contracts/flash/CrossChainFlashRepayReceiver.sol
Normal file
57
contracts/flash/CrossChainFlashRepayReceiver.sol
Normal file
@@ -0,0 +1,57 @@
|
||||
// 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 CrossChainFlashRepayReceiver
|
||||
* @notice **Destination-chain** CCIP receiver: forwards received ERC-20s to a recipient encoded in `message.data`.
|
||||
* @dev Intended pairing with `CrossChainFlashBorrower` / bridge senders. `msg.sender` must be the CCIP router.
|
||||
* `data` encoding: `abi.encode(address recipient, bytes32 obligationId)` where `obligationId` is opaque (indexing / ops).
|
||||
* **Repaying the source-chain flash vault** requires a **separate** flow (second tx / bridge back); this contract does not pull debt on source.
|
||||
*/
|
||||
contract CrossChainFlashRepayReceiver {
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
IRouterClient public immutable ccipRouter;
|
||||
|
||||
event CrossChainDelivered(
|
||||
bytes32 indexed messageId,
|
||||
uint64 indexed sourceChainSelector,
|
||||
address indexed recipient,
|
||||
address token,
|
||||
uint256 amount,
|
||||
bytes32 obligationId
|
||||
);
|
||||
|
||||
error OnlyRouter();
|
||||
error NoTokens();
|
||||
error BadData();
|
||||
|
||||
constructor(address ccipRouter_) {
|
||||
require(ccipRouter_ != address(0), "CrossChainFlashRepayReceiver: zero router");
|
||||
ccipRouter = IRouterClient(ccipRouter_);
|
||||
}
|
||||
|
||||
modifier onlyRouter() {
|
||||
if (msg.sender != address(ccipRouter)) revert OnlyRouter();
|
||||
_;
|
||||
}
|
||||
|
||||
function ccipReceive(IRouterClient.Any2EVMMessage calldata message) external onlyRouter {
|
||||
_deliver(message);
|
||||
}
|
||||
|
||||
function _deliver(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 recipient, bytes32 obligationId) = abi.decode(m.data, (address, bytes32));
|
||||
if (recipient == address(0)) revert BadData();
|
||||
IERC20(ta.token).safeTransfer(recipient, ta.amount);
|
||||
emit CrossChainDelivered(m.messageId, m.sourceChainSelector, recipient, ta.token, ta.amount, obligationId);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user