Using oracles

Use onchain price oracles to get accurate point-in-time USD prices of tokens, without needing to use any offchain price APIs.

Without Shadow, augmenting blockchain data with point-in-time accurate prices requires complex data pipelines and integrations with third-party price feed APIs. Shadow drastically simplifies this by allowing you to leverage existing oracle contracts, in <100 lines of code.

In this example, we’ll be updating the 1inch Aggregator contract to emit a shadow event called OrderFilledDetails that contains additional metadata of a trade, including the point-in-time USD price of the trade. We’ll be fetching the point-in-time prices via a Chainlink oracle.

Step 1 – Edit the contract

Open the example on the Shadow Playground: https://app.shadow.xyz/demo?example=one_inch_oracle This will open the playground editor at the 1inch Aggregation Router v5 contract on Ethereum at 0x1111111254eeb25477b68fb85ed929f73a960582 (as of Jul 2024).

At L4302, we’ve defined the OrderFilledDetails event and a helper OrderDetails struct:

struct OrderDetails {
    address maker;
    address taker;
    address makerToken;
    address takerToken;
    uint256 takerTokenFilledAmount;
    uint256 makerTokenFilledAmount;
    uint256 orderAmountUsd;
}

event OrderFilledDetails(OrderDetails orderDetails);

At L4567, we call a new function called getOrderAmountUsd and emit the OrderFilledDetails event:

uint256 orderAmountUsd = getOrderAmountUsd(order.makerAsset, order.takerAsset, actualMakingAmount, actualTakingAmount);

OrderDetails memory orderDetails = OrderDetails({
    maker: order.maker,
    taker: order.receiver,
    makerToken: order.makerAsset,
    takerToken: order.takerAsset,
    takerTokenFilledAmount: actualTakingAmount,
    makerTokenFilledAmount: actualMakingAmount,
    orderAmountUsd: orderAmountUsd
});

emit OrderFilledDetails(orderDetails);

At the bottom of the contract at L4667, we define the getOrderAmountUsd function and two other helper functions:

function getOrderAmountUsd(address makerToken, address takerToken, uint256 makerTokenAmount, uint256 takerTokenAmount) internal returns (uint256 orderAmountUsd) {
    if (takerToken == address(_WETH)) {
        orderAmountUsd = takerTokenAmount * uint256(getPriceETH()) * 1e6 / 1e18 / 1e8;
    } else if (makerToken == address(_WETH)) {
        orderAmountUsd = makerTokenAmount * uint256(getPriceETH()) * 1e6 / 1e18 / 1e8;
    } else if (isStable(makerToken)) {
        orderAmountUsd = makerTokenAmount * 1e6;
    } else if (isStable(takerToken)) {
        orderAmountUsd = takerTokenAmount * 1e6;
    }

    bool isStable = isStable(makerToken) || isStable(takerToken);
}

function isStable(address token) internal returns (bool) {
    if (
        token == 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 || // USDC
        token == 0xdAC17F958D2ee523a2206206994597C13D831ec7 || // USDT
        token == 0x6B175474E89094C44Da98b954EedeAC495271d0F    // DAI
    ) {
        return true;
    }
    return false;
}

// Get the current price of ETH from Chainlink's ETH/USD oracle (8 decimals)
function getPriceETH() internal view returns (int256) {
    AggregatorV3Interface dataFeed = AggregatorV3Interface(
        0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419
    );
    (, int256 answer, , , ) = dataFeed.latestRoundData();
    return answer;
}

Notice that we also pasted Chainlink’s AggregatorV3Interface into this file. This allows us to call the Chainlink oracle contract via its interface. You’ll have to do this any time you introduce shadow changes that interact with a contract interface that it doesn’t currently interact with.

Step 2 – Test run your changes

Click “Compile” > “Test Run” in the top right corner, and paste in this transaction hash 0x045bd6741a5ea38dc6da6b324c2446552bc0e38f6b53b79ada7fc53604135f75 (or simulate any other order filled transaction).

You should see your custom OrderFilledDetails event in the output!

Step 3 – Deploy your changes

If you want to apply these changes on your shadow fork, click the button on the top right corner that says “Apply to your shadow fork”.

This will take you to the editor for your shadow fork, where you can deploy the changes by hitting “Compile > Deploy”

Last updated