Organize docs directory: move 25 files to appropriate locations
- Created docs/00-meta/ for documentation meta files (11 files) - Created docs/archive/reports/ for reports (5 files) - Created docs/archive/issues/ for issue tracking (2 files) - Created docs/bridge/contracts/ for Solidity contracts (3 files) - Created docs/04-configuration/metamask/ for Metamask configs (3 files) - Created docs/scripts/ for documentation scripts (2 files) - Root directory now contains only 3 essential files (89.3% reduction) All recommended actions from docs directory review complete.
This commit is contained in:
396
docs/bridge/contracts/CCIPWETH9Bridge_flattened.sol
Normal file
396
docs/bridge/contracts/CCIPWETH9Bridge_flattened.sol
Normal file
@@ -0,0 +1,396 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.19;
|
||||
|
||||
// contracts/ccip/IRouterClient.sol
|
||||
|
||||
/**
|
||||
* @title Chainlink CCIP Router Client Interface
|
||||
* @notice Interface for Chainlink CCIP Router Client
|
||||
* @dev This interface is based on Chainlink CCIP Router Client specification
|
||||
*/
|
||||
interface IRouterClient {
|
||||
/// @notice Represents the router's fee token
|
||||
enum TokenAmountType {
|
||||
Fiat,
|
||||
Native
|
||||
}
|
||||
|
||||
/// @notice Represents a token amount and its type
|
||||
struct TokenAmount {
|
||||
address token;
|
||||
uint256 amount;
|
||||
TokenAmountType amountType;
|
||||
}
|
||||
|
||||
/// @notice Represents a CCIP message
|
||||
struct EVM2AnyMessage {
|
||||
bytes receiver;
|
||||
bytes data;
|
||||
TokenAmount[] tokenAmounts;
|
||||
address feeToken;
|
||||
bytes extraArgs;
|
||||
}
|
||||
|
||||
/// @notice Represents a CCIP message with source chain information
|
||||
struct Any2EVMMessage {
|
||||
bytes32 messageId;
|
||||
uint64 sourceChainSelector;
|
||||
bytes sender;
|
||||
bytes data;
|
||||
TokenAmount[] tokenAmounts;
|
||||
}
|
||||
|
||||
/// @notice Emitted when a message is sent
|
||||
event MessageSent(
|
||||
bytes32 indexed messageId,
|
||||
uint64 indexed destinationChainSelector,
|
||||
address indexed sender,
|
||||
bytes receiver,
|
||||
bytes data,
|
||||
TokenAmount[] tokenAmounts,
|
||||
address feeToken,
|
||||
bytes extraArgs
|
||||
);
|
||||
|
||||
/// @notice Emitted when a message is received
|
||||
event MessageReceived(
|
||||
bytes32 indexed messageId,
|
||||
uint64 indexed sourceChainSelector,
|
||||
address indexed sender,
|
||||
bytes data,
|
||||
TokenAmount[] tokenAmounts
|
||||
);
|
||||
|
||||
/// @notice Sends a message to a destination chain
|
||||
/// @param destinationChainSelector The chain selector of the destination chain
|
||||
/// @param message The message to send
|
||||
/// @return messageId The ID of the sent message
|
||||
/// @return fees The fees required for the message
|
||||
/// @dev If feeToken is zero address, fees are paid in native token (ETH) via msg.value
|
||||
function ccipSend(
|
||||
uint64 destinationChainSelector,
|
||||
EVM2AnyMessage memory message
|
||||
) external payable returns (bytes32 messageId, uint256 fees);
|
||||
|
||||
/// @notice Gets the fee for sending a message
|
||||
/// @param destinationChainSelector The chain selector of the destination chain
|
||||
/// @param message The message to send
|
||||
/// @return fee The fee required for the message
|
||||
function getFee(
|
||||
uint64 destinationChainSelector,
|
||||
EVM2AnyMessage memory message
|
||||
) external view returns (uint256 fee);
|
||||
|
||||
/// @notice Gets the supported tokens for a destination chain
|
||||
/// @param destinationChainSelector The chain selector of the destination chain
|
||||
/// @return tokens The list of supported tokens
|
||||
function getSupportedTokens(
|
||||
uint64 destinationChainSelector
|
||||
) external view returns (address[] memory tokens);
|
||||
}
|
||||
|
||||
// contracts/ccip/CCIPWETH9Bridge.sol
|
||||
|
||||
// Minimal IERC20 interface for ERC20 tokens (WETH9 and LINK)
|
||||
interface IERC20 {
|
||||
function transferFrom(address from, address to, uint256 amount) external returns (bool);
|
||||
function transfer(address to, uint256 amount) external returns (bool);
|
||||
function approve(address spender, uint256 amount) external returns (bool);
|
||||
function balanceOf(address account) external view returns (uint256);
|
||||
}
|
||||
|
||||
/**
|
||||
* @title CCIP WETH9 Bridge
|
||||
* @notice Cross-chain WETH9 transfer bridge using Chainlink CCIP
|
||||
* @dev Enables users to send WETH9 tokens across chains via CCIP
|
||||
*/
|
||||
contract CCIPWETH9Bridge {
|
||||
|
||||
IRouterClient public immutable ccipRouter;
|
||||
address public immutable weth9; // WETH9 contract address
|
||||
address public feeToken; // LINK token address
|
||||
address public admin;
|
||||
|
||||
// Destination chain configurations
|
||||
struct DestinationChain {
|
||||
uint64 chainSelector;
|
||||
address receiverBridge; // Address of corresponding bridge on destination chain
|
||||
bool enabled;
|
||||
}
|
||||
|
||||
mapping(uint64 => DestinationChain) public destinations;
|
||||
uint64[] public destinationChains;
|
||||
|
||||
// Track cross-chain transfers for replay protection
|
||||
mapping(bytes32 => bool) public processedTransfers;
|
||||
mapping(address => uint256) public nonces;
|
||||
|
||||
event CrossChainTransferInitiated(
|
||||
bytes32 indexed messageId,
|
||||
address indexed sender,
|
||||
uint64 indexed destinationChainSelector,
|
||||
address recipient,
|
||||
uint256 amount,
|
||||
uint256 nonce
|
||||
);
|
||||
|
||||
event CrossChainTransferCompleted(
|
||||
bytes32 indexed messageId,
|
||||
uint64 indexed sourceChainSelector,
|
||||
address indexed recipient,
|
||||
uint256 amount
|
||||
);
|
||||
|
||||
event DestinationAdded(uint64 chainSelector, address receiverBridge);
|
||||
event DestinationRemoved(uint64 chainSelector);
|
||||
event DestinationUpdated(uint64 chainSelector, address receiverBridge);
|
||||
|
||||
modifier onlyAdmin() {
|
||||
require(msg.sender == admin, "CCIPWETH9Bridge: only admin");
|
||||
_;
|
||||
}
|
||||
|
||||
modifier onlyRouter() {
|
||||
require(msg.sender == address(ccipRouter), "CCIPWETH9Bridge: only router");
|
||||
_;
|
||||
}
|
||||
|
||||
constructor(address _ccipRouter, address _weth9, address _feeToken) {
|
||||
require(_ccipRouter != address(0), "CCIPWETH9Bridge: zero router");
|
||||
require(_weth9 != address(0), "CCIPWETH9Bridge: zero WETH9");
|
||||
require(_feeToken != address(0), "CCIPWETH9Bridge: zero fee token");
|
||||
|
||||
ccipRouter = IRouterClient(_ccipRouter);
|
||||
weth9 = _weth9;
|
||||
feeToken = _feeToken;
|
||||
admin = msg.sender;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Send WETH9 tokens to another chain via CCIP
|
||||
* @param destinationChainSelector The chain selector of the destination chain
|
||||
* @param recipient The recipient address on the destination chain
|
||||
* @param amount The amount of WETH9 to send
|
||||
* @return messageId The CCIP message ID
|
||||
*/
|
||||
function sendCrossChain(
|
||||
uint64 destinationChainSelector,
|
||||
address recipient,
|
||||
uint256 amount
|
||||
) external returns (bytes32 messageId) {
|
||||
require(amount > 0, "CCIPWETH9Bridge: invalid amount");
|
||||
require(recipient != address(0), "CCIPWETH9Bridge: zero recipient");
|
||||
|
||||
DestinationChain memory dest = destinations[destinationChainSelector];
|
||||
require(dest.enabled, "CCIPWETH9Bridge: destination not enabled");
|
||||
|
||||
// Transfer WETH9 from user
|
||||
require(IERC20(weth9).transferFrom(msg.sender, address(this), amount), "CCIPWETH9Bridge: transfer failed");
|
||||
|
||||
// Increment nonce for replay protection
|
||||
nonces[msg.sender]++;
|
||||
uint256 currentNonce = nonces[msg.sender];
|
||||
|
||||
// Encode transfer data (recipient, amount, sender, nonce)
|
||||
bytes memory data = abi.encode(
|
||||
recipient,
|
||||
amount,
|
||||
msg.sender,
|
||||
currentNonce
|
||||
);
|
||||
|
||||
// Prepare CCIP message with WETH9 tokens
|
||||
IRouterClient.EVM2AnyMessage memory message = IRouterClient.EVM2AnyMessage({
|
||||
receiver: abi.encode(dest.receiverBridge),
|
||||
data: data,
|
||||
tokenAmounts: new IRouterClient.TokenAmount[](1),
|
||||
feeToken: feeToken,
|
||||
extraArgs: ""
|
||||
});
|
||||
|
||||
// Set token amount (WETH9)
|
||||
message.tokenAmounts[0] = IRouterClient.TokenAmount({
|
||||
token: weth9,
|
||||
amount: amount,
|
||||
amountType: IRouterClient.TokenAmountType.Fiat
|
||||
});
|
||||
|
||||
// Calculate fee
|
||||
uint256 fee = ccipRouter.getFee(destinationChainSelector, message);
|
||||
|
||||
// Approve and pay fee
|
||||
if (fee > 0) {
|
||||
require(IERC20(feeToken).transferFrom(msg.sender, address(this), fee), "CCIPWETH9Bridge: fee transfer failed");
|
||||
require(IERC20(feeToken).approve(address(ccipRouter), fee), "CCIPWETH9Bridge: fee approval failed");
|
||||
}
|
||||
|
||||
// Send via CCIP
|
||||
(messageId, ) = ccipRouter.ccipSend(destinationChainSelector, message);
|
||||
|
||||
emit CrossChainTransferInitiated(
|
||||
messageId,
|
||||
msg.sender,
|
||||
destinationChainSelector,
|
||||
recipient,
|
||||
amount,
|
||||
currentNonce
|
||||
);
|
||||
|
||||
return messageId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Receive WETH9 tokens from another chain via CCIP
|
||||
* @param message The CCIP message
|
||||
*/
|
||||
function ccipReceive(
|
||||
IRouterClient.Any2EVMMessage calldata message
|
||||
) external onlyRouter {
|
||||
// Replay protection: check if message already processed
|
||||
require(!processedTransfers[message.messageId], "CCIPWETH9Bridge: transfer already processed");
|
||||
|
||||
// Mark as processed
|
||||
processedTransfers[message.messageId] = true;
|
||||
|
||||
// Validate token amounts
|
||||
require(message.tokenAmounts.length > 0, "CCIPWETH9Bridge: no tokens");
|
||||
require(message.tokenAmounts[0].token == weth9, "CCIPWETH9Bridge: invalid token");
|
||||
|
||||
uint256 amount = message.tokenAmounts[0].amount;
|
||||
require(amount > 0, "CCIPWETH9Bridge: invalid amount");
|
||||
|
||||
// Decode transfer data (recipient, amount, sender, nonce)
|
||||
(address recipient, , , ) = abi.decode(
|
||||
message.data,
|
||||
(address, uint256, address, uint256)
|
||||
);
|
||||
|
||||
require(recipient != address(0), "CCIPWETH9Bridge: zero recipient");
|
||||
|
||||
// Transfer WETH9 to recipient
|
||||
require(IERC20(weth9).transfer(recipient, amount), "CCIPWETH9Bridge: transfer failed");
|
||||
|
||||
emit CrossChainTransferCompleted(
|
||||
message.messageId,
|
||||
message.sourceChainSelector,
|
||||
recipient,
|
||||
amount
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Calculate fee for cross-chain transfer
|
||||
* @param destinationChainSelector The chain selector of the destination chain
|
||||
* @param amount The amount of WETH9 to send
|
||||
* @return fee The fee required for the transfer
|
||||
*/
|
||||
function calculateFee(
|
||||
uint64 destinationChainSelector,
|
||||
uint256 amount
|
||||
) external view returns (uint256 fee) {
|
||||
DestinationChain memory dest = destinations[destinationChainSelector];
|
||||
require(dest.enabled, "CCIPWETH9Bridge: destination not enabled");
|
||||
|
||||
bytes memory data = abi.encode(address(0), amount, address(0), 0);
|
||||
|
||||
IRouterClient.EVM2AnyMessage memory message = IRouterClient.EVM2AnyMessage({
|
||||
receiver: abi.encode(dest.receiverBridge),
|
||||
data: data,
|
||||
tokenAmounts: new IRouterClient.TokenAmount[](1),
|
||||
feeToken: feeToken,
|
||||
extraArgs: ""
|
||||
});
|
||||
|
||||
message.tokenAmounts[0] = IRouterClient.TokenAmount({
|
||||
token: weth9,
|
||||
amount: amount,
|
||||
amountType: IRouterClient.TokenAmountType.Fiat
|
||||
});
|
||||
|
||||
return ccipRouter.getFee(destinationChainSelector, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Add destination chain
|
||||
*/
|
||||
function addDestination(
|
||||
uint64 chainSelector,
|
||||
address receiverBridge
|
||||
) external onlyAdmin {
|
||||
require(receiverBridge != address(0), "CCIPWETH9Bridge: zero address");
|
||||
require(!destinations[chainSelector].enabled, "CCIPWETH9Bridge: destination already exists");
|
||||
|
||||
destinations[chainSelector] = DestinationChain({
|
||||
chainSelector: chainSelector,
|
||||
receiverBridge: receiverBridge,
|
||||
enabled: true
|
||||
});
|
||||
destinationChains.push(chainSelector);
|
||||
|
||||
emit DestinationAdded(chainSelector, receiverBridge);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Remove destination chain
|
||||
*/
|
||||
function removeDestination(uint64 chainSelector) external onlyAdmin {
|
||||
require(destinations[chainSelector].enabled, "CCIPWETH9Bridge: destination not found");
|
||||
destinations[chainSelector].enabled = false;
|
||||
|
||||
// Remove from array
|
||||
for (uint256 i = 0; i < destinationChains.length; i++) {
|
||||
if (destinationChains[i] == chainSelector) {
|
||||
destinationChains[i] = destinationChains[destinationChains.length - 1];
|
||||
destinationChains.pop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
emit DestinationRemoved(chainSelector);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Update destination receiver bridge
|
||||
*/
|
||||
function updateDestination(
|
||||
uint64 chainSelector,
|
||||
address receiverBridge
|
||||
) external onlyAdmin {
|
||||
require(destinations[chainSelector].enabled, "CCIPWETH9Bridge: destination not found");
|
||||
require(receiverBridge != address(0), "CCIPWETH9Bridge: zero address");
|
||||
|
||||
destinations[chainSelector].receiverBridge = receiverBridge;
|
||||
emit DestinationUpdated(chainSelector, receiverBridge);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Update fee token
|
||||
*/
|
||||
function updateFeeToken(address newFeeToken) external onlyAdmin {
|
||||
require(newFeeToken != address(0), "CCIPWETH9Bridge: zero address");
|
||||
feeToken = newFeeToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Change admin
|
||||
*/
|
||||
function changeAdmin(address newAdmin) external onlyAdmin {
|
||||
require(newAdmin != address(0), "CCIPWETH9Bridge: zero address");
|
||||
admin = newAdmin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Get destination chains
|
||||
*/
|
||||
function getDestinationChains() external view returns (uint64[] memory) {
|
||||
return destinationChains;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Get user nonce
|
||||
*/
|
||||
function getUserNonce(address user) external view returns (uint256) {
|
||||
return nonces[user];
|
||||
}
|
||||
}
|
||||
|
||||
26
docs/bridge/contracts/CCIPWETH9Bridge_standard_json.json
Normal file
26
docs/bridge/contracts/CCIPWETH9Bridge_standard_json.json
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user