Back to QuickStarts

Automated Portfolio Manager

Build a portfolio that automatically rebalances itself based on real-time data from on-chain and off-chain sources.

Overview

The Automated Portfolio Manager automatically rebalances an investment portfolio using real-time data from onchain and offchain sources. These contracts enable adjustments in asset allocations within a portfolio based on dynamic market conditions, sentiment scores, and volatility indicators such as the Gold Volatility Index (GVZ). This approach provides a modern way to manage digital assets.

A live demo is available at https://automated-portfolio-manager.vercel.app.

Objectives

The example contract supports dynamic asset management using tokens that adhere to the ERC20 standard. For this demonstration, Mimic Tokens representing gold (mXAU), Wrapped Bitcoin (mWBTC), and Ethereum (mETH) are used as the underlying assets. These tokens are automatically bought or sold to mirror changes in crypto market sentiment and gold volatility (GVZ) to ensure the portfolio adjusts to evolving market conditions. This functionality demonstrates how blockchain and Chainlink technologies can be used to create a dynamic investment strategy that adapts to global financial movements in real time.

Note: In a real-world use case, you can integrate the portfolio rebalancing mechanism with any DeFi protocol, such as a swapping mechanism or investments in ERC-4626 vaults.

Table of Content

Requirements

  • Git: Make sure you have Git installed. You can check your current version by running git --version in your terminal and download the latest version from the official Git website if necessary.

  • Foundry: This guide requires Foundry. Follow the instructions in the Foundry documentation to install it.

  • Nodejs and npm: Install the latest release of Node.js 20. Note: To ensure you are running the correct version in a terminal, type node -v.

    node -v
    v20.11.0
    
  • RPC URL: You need a Remote Procedure Call (RPC) URL for the Ethereum Sepolia network. You can obtain one by creating an account on Alchemy or Infura and setting up an Ethereum Sepolia project.

  • Private key: You need the private key of the account that will deploy and interact with the contracts. If you use MetaMask, follow the instructions to Export a Private Key.

  • Testnet funds: This guide requires testnet ETH, LINK, and USDC on Ethereum Sepolia.

  • Etherscan API Key: An API key to verify your deployed contracts on the Etherscan block explorer.

  • CryptoCompare API key: You need an API key to fetch BTC and ETH trading signals from CryptoCompare. Create one for free at CryptoCompare. Select the Poll Live and Historical Data permission level within the Read All Price Streaming and Polling Endpoints dropdown menu.

  • MarketData token: You need a valid token from MarketData to fetch the GVZ index value. Create a free account and generate a token. The token will be emailed to you.

  • Chainlink Functions subscription: A Chainlink Functions subscription is required for this guide. If you do not already have a subscription, you can create one using the Chainlink Functions Subscription Manager.

Setup

  1. Clone and navigate to the repository:

    git clone https://github.com/smartcontractkit/quickstarts-automated-portfolio-manager.git
    cd quickstarts-automated-portfolio-manager
    
  2. Install the frontend and backend dependencies:

    make install
    
  3. Copy the .env.example file to .env:

    cp .env.example .env
    
  4. Open your .env file and fill in the required environment variables.

  5. Run source .env to make your environment variables available in your terminal session.

    source .env
    
  6. Run forge compile to update dependencies in the lib folder.

    forge compile
    

    Expect an output similar to the following in your terminal:

    [] Compiling...
    [] Compiling 54 files with 0.8.19
    [] Solc 0.8.19 finished in 1.82s
    Compiler run successful!
    

OffchainDataFetcher deployment and setup

1 Deploy the contract

Run the following script to deploy your OffchainDataFetcher contract on Ethereum Sepolia:

forge script script/DeployOffChainDataFetcher.s.sol --rpc-url $RPC_URL_SEPOLIA --private-key $PRIVATE_KEY --broadcast --verify -vvvv
2 Add the contract to your Functions subscription and configure it
  1. Open the functions/updateRequestDon.js script and update the subscriptionId variable with your Chainlink Functions subscription ID.

  2. Fund your Functions subscription with at least 5 testnet LINK using the Chainlink Functions Subscription Manager.

  3. Run the following script to update the OffchainDataFetcher Functions request with DON-hosted Secrets:

    node functions/updateRequestDon.js
    

    Expect output similar to the following in your terminal:

    Make request...
    Upload encrypted secret to gateways https://01.functions-gateway.testnet.chain.link/,https://02.functions-gateway.testnet.chain.link/. slotId 1. Expiration in minutes: 4320
    
    ✅ Secrets uploaded properly to gateways https://01.functions-gateway.testnet.chain.link/,https://02.functions-gateway.testnet.chain.link/! Gateways response:  { version: 1717010963, success: true }
    
    ✅ Automated Functions request settings updated! Transaction hash 0xfaafcfcbb42163679681d189af6e777b36df4ec7a6d2cf14aea6fe220be0899e - Check the explorer https://sepolia.etherscan.io/tx/0xfaafcfcbb42163679681d189af6e777b36df4ec7a6d2cf14aea6fe220be0899e
    

    Note: On testnets, DON-hosted secrets have a maximum Time to Live (TTL) of 72 hours. If you need to extend the TTL beyond this period, consider using secrets hosted in your own GitHub gists for your requests. For this approach, include your GITHUB_API_TOKEN in your .env file and use the updateRequestGists.js script instead of updateRequestDon.js.

  4. Add your OffChainDataFetcher contract as a consumer in your Functions subscription (Chainlink Functions Subscription Manager). Note: You can find your deployed OffchainDataFetcher contract address in output/deployedOffchainDataFetcher.json.

3 Create a Time-based upkeep
  1. Register an upkeep to call the sendRequestCBOR function on your OffchainDataFetcher contract daily:

    • Go to the Chainlink Automation App.
    • Create a time-based upkeep that targets the sendRequestCBOR function.
    • To run the upkeep daily at 3 AM UTC, use the following CRON expression: 0 3 * * *.
    • Set the gas limit to 1000000.
    • Fund your upkeep with 5 testnet LINK.

    Note: You can find your deployed OffchainDataFetcher contract address in output/deployedOffchainDataFetcher.json.

  2. Configure your contract so only the upkeep contract (and your admin account, which deployed the contract) can call the sendRequestCBOR function. This security measure is important to prevent anyone from calling several times sendRequestCBOR and draining your Functions subscription balance.

    • Go to Etherscan.
    • Enter your OffchainDataFetcher contract address in the search bar. Note: This address is available in output/deployedOffchainDataFetcher.json.
    • Select the Contract section, then click on the Write Contract tab.
    • Connect your admin account, which is the wallet you used to deploy the contract.
    • Call the setAutomationCronContract function with the Upkeep address as the input parameter. Note: You can find the Upkeep address in the Details section of your upkeep in the Chainlink Automation App.

AutomatedPortfolioManager deployment and setup

1 Deploy the contract

Run the following script to deploy your AutomatedPortfolioManager contract on Ethereum Sepolia:

forge script script/DeployAutomatedPortfolioManager.s.sol --rpc-url $RPC_URL_SEPOLIA --private-key $PRIVATE_KEY --broadcast --verify -vvvv
2 Create a custom upkeep
  1. Go to the Chainlink Automation App.

  2. Create a new Custom logic upkeep with a starting balance of 5 testnet LINK. Note: You can find your deployed AutomatedPortfolioManager contract address in output/deployedPortfolioManager.json.

  3. Configure your contract so only the upkeep contract (and your admin account, which deployed the contract) can call the performUpkeep function. This security measure is important to prevent anyone from calling several times performUpkeep and draining your Automation balance.

    • Go to Etherscan.
    • Enter your AutomatedPortfolioManager contract address in the search bar. Note: This address is available in output/deployedPortfolioManager.json.
    • Select the Contract section, then click on the Write Contract tab.
    • Connect your admin account, which is the wallet you used to deploy the contract.
    • In the function inputs, enter the Forwarder address into the setAutomationUpkeepForwarderContract function. Note: You can find the Forwarder address in the Details section of your upkeep in the Automation App.

Run the dApp locally

  1. Navigate to the app folder:

    cd app
    
  2. Run the dApp locally:

    npm run dev
    
  3. Navigate to http://localhost:3000 with your favorite browser.

  4. Change tab to My Investment.

  5. Connect your wallet.

  6. Enter the amount of testnet USDC you want to invest and click Invest.

Note: The initial assets allocation is 40% mXAU, 30% mWBTC, and 30% mETH. You can wait until the next scheduled time-based upkeep at 3 AM UTC to fetch the latest data and make it available in your OffchainDataFetcherContract. Your custom upkeep will then automatically rebalance the portfolio. Alternatively, you can call the sendRequestCBOR function on your OffchainDataFetcherContract on Etherscan with your admin account to initiate the first rebalance.

Get the latest Chainlink content straight to your inbox.