Skip to main content
ClaudeWave
Skill55 estrellas del repoactualizado 4mo ago

Chainlink Integration Patterns

>

Instalar en Claude Code
Copiar
git clone --depth 1 https://github.com/PurpleAILAB/Vigilo /tmp/chainlink-integration-patterns && cp -r /tmp/chainlink-integration-patterns/packages/claude/skills/integration-patterns/chainlink ~/.claude/skills/chainlink-integration-patterns
Después abre una sesión nueva de Claude Code; el skill carga automáticamente.

SKILL.md

# Chainlink Integration Patterns

This skill provides comprehensive knowledge for auditing Chainlink service integrations.

## Chainlink Services Overview

| Service | Purpose | Key Risks |
|---------|---------|-----------|
| Price Feeds | Oracle data | Stale prices, manipulation |
| VRF | Random numbers | Front-running, reorg attacks |
| CCIP | Cross-chain | Message validation |
| Automation | Keepers | Timing assumptions |
| Functions | Off-chain compute | Trust assumptions |

---

## Price Feed Integration

### Standard Integration Pattern

```solidity
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";

contract PriceFeedConsumer {
    AggregatorV3Interface internal priceFeed;
    uint256 public constant HEARTBEAT = 3600; // 1 hour

    constructor(address feedAddress) {
        priceFeed = AggregatorV3Interface(feedAddress);
    }

    function getLatestPrice() public view returns (int256) {
        (
            uint80 roundId,
            int256 price,
            uint256 startedAt,
            uint256 updatedAt,
            uint80 answeredInRound
        ) = priceFeed.latestRoundData();

        // Validation checks
        require(price > 0, "Invalid price");
        require(updatedAt > 0, "Round not complete");
        require(updatedAt >= block.timestamp - HEARTBEAT, "Stale price");
        require(answeredInRound >= roundId, "Stale round");

        return price;
    }
}
```

### Required Validations

| Check | Code | Risk if Missing |
|-------|------|-----------------|
| Zero price | `require(price > 0)` | Division by zero, wrong valuations |
| Freshness | `require(updatedAt >= block.timestamp - HEARTBEAT)` | Stale price exploitation |
| Round complete | `require(answeredInRound >= roundId)` | Incomplete data |
| Negative price | `require(price > 0)` | Wrap-around issues |

### L2 Sequencer Uptime Feed

```solidity
// REQUIRED for Arbitrum, Optimism, Base deployments
AggregatorV3Interface internal sequencerUptimeFeed;
uint256 public constant GRACE_PERIOD = 3600; // 1 hour

function getLatestPrice() public view returns (int256) {
    // Check sequencer first
    (, int256 answer,, uint256 startedAt,) =
        sequencerUptimeFeed.latestRoundData();

    // answer == 0: Sequencer is up
    // answer == 1: Sequencer is down
    bool isSequencerUp = answer == 0;
    require(isSequencerUp, "Sequencer down");

    // Grace period after sequencer comes back up
    uint256 timeSinceUp = block.timestamp - startedAt;
    require(timeSinceUp > GRACE_PERIOD, "Grace period active");

    // Now get price
    return _getPrice();
}
```

### Heartbeat Reference

| Feed | Heartbeat | Deviation |
|------|-----------|-----------|
| ETH/USD | 3600s | 0.5% |
| BTC/USD | 3600s | 0.5% |
| USDC/USD | 86400s | 0.1% |
| DAI/USD | 3600s | 0.25% |

---

## VRF V2 Integration

### Standard Pattern

```solidity
import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";
import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";

contract VRFConsumer is VRFConsumerBaseV2 {
    VRFCoordinatorV2Interface COORDINATOR;

    uint64 s_subscriptionId;
    bytes32 keyHash;
    uint32 callbackGasLimit = 100000;
    uint16 requestConfirmations = 3; // Minimum for mainnet
    uint32 numWords = 1;

    mapping(uint256 => address) public requestIdToSender;
    mapping(uint256 => uint256) public requestIdToRandomWord;

    function requestRandomWords() external returns (uint256 requestId) {
        requestId = COORDINATOR.requestRandomWords(
            keyHash,
            s_subscriptionId,
            requestConfirmations,
            callbackGasLimit,
            numWords
        );
        requestIdToSender[requestId] = msg.sender;
        return requestId;
    }

    function fulfillRandomWords(
        uint256 requestId,
        uint256[] memory randomWords
    ) internal override {
        // Use randomWords[0] for result
        requestIdToRandomWord[requestId] = randomWords[0];
        // Process result...
    }
}
```

### VRF Security Checklist

| Check | Risk if Missing |
|-------|-----------------|
| Sufficient confirmations | Reorg attacks |
| requestId to user mapping | Wrong user gets result |
| Callback gas limit | DoS if too low |
| No state prediction | Front-running |
| Subscription funded | Failed requests |

### VRF Vulnerabilities

**1. Request Front-Running**
```solidity
// DANGEROUS: User can see result and decide
function play() external {
    uint256 requestId = requestRandomWords();
    // User can front-run fulfillment if they see result
}

// SECURE: Commit-reveal pattern
function commit(bytes32 commitment) external {
    commitments[msg.sender] = commitment;
}

function reveal(bytes32 secret) external {
    require(keccak256(abi.encode(secret)) == commitments[msg.sender]);
    // Request VRF
}
```

**2. Insufficient Confirmations**
```solidity
// DANGEROUS on chains with reorgs
uint16 requestConfirmations = 1; // Too low!

// RECOMMENDED
uint16 requestConfirmations = 3; // Mainnet minimum
// Higher for chains with frequent reorgs
```

---

## CCIP Integration

### Message Receiving

```solidity
import {CCIPReceiver} from "@chainlink/contracts-ccip/src/v0.8/ccip/applications/CCIPReceiver.sol";
import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";

contract CCIPReceiverExample is CCIPReceiver {
    mapping(uint64 => bool) public allowlistedSourceChains;
    mapping(address => bool) public allowlistedSenders;

    function _ccipReceive(
        Client.Any2EVMMessage memory message
    ) internal override {
        // CRITICAL: Validate source chain
        require(
            allowlistedSourceChains[message.sourceChainSelector],
            "Source chain not allowlisted"
        );

        // CRITICAL: Validate sender
        address sender = abi.decode(message.sender, (address));
        require(
            allowlistedSenders[sender],
            "Sender not allowlisted"
        );

        // Process message