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
- Project setup
OffchainDataFetcherdeployment and setupAutomatedPortfolioManagerdeployment and setup- Run the dApp locally
Requirements
-
Git: Make sure you have Git installed. You can check your current version by running
git --versionin 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.
- Get Sepolia testnet ETH and LINK at faucets.chain.link.
- Get Sepolia testnet USDC at faucet.circle.com.
-
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
-
Clone and navigate to the repository:
git clone https://github.com/smartcontractkit/quickstarts-automated-portfolio-manager.git cd quickstarts-automated-portfolio-manager -
Install the frontend and backend dependencies:
make install -
Copy the
.env.examplefile to.env:cp .env.example .env -
Open your
.envfile and fill in the required environment variables. -
Run
source .envto make your environment variables available in your terminal session.source .env -
Run
forge compileto update dependencies in thelibfolder.forge compileExpect 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
-
Open the
functions/updateRequestDon.jsscript and update thesubscriptionIdvariable with your Chainlink Functions subscription ID. -
Fund your Functions subscription with at least 5 testnet LINK using the Chainlink Functions Subscription Manager.
-
Run the following script to update the
OffchainDataFetcherFunctions request with DON-hosted Secrets:node functions/updateRequestDon.jsExpect 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/0xfaafcfcbb42163679681d189af6e777b36df4ec7a6d2cf14aea6fe220be0899eNote: 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_TOKENin your.envfile and use theupdateRequestGists.jsscript instead ofupdateRequestDon.js. -
Add your
OffChainDataFetchercontract as a consumer in your Functions subscription (Chainlink Functions Subscription Manager). Note: You can find your deployedOffchainDataFetchercontract address inoutput/deployedOffchainDataFetcher.json.
3 Create a Time-based upkeep
-
Register an upkeep to call the
sendRequestCBORfunction on yourOffchainDataFetchercontract daily:- Go to the Chainlink Automation App.
- Create a time-based upkeep that targets the
sendRequestCBORfunction. - 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
OffchainDataFetchercontract address inoutput/deployedOffchainDataFetcher.json. -
Configure your contract so only the upkeep contract (and your admin account, which deployed the contract) can call the
sendRequestCBORfunction. This security measure is important to prevent anyone from calling several timessendRequestCBORand draining your Functions subscription balance.- Go to Etherscan.
- Enter your
OffchainDataFetchercontract address in the search bar. Note: This address is available inoutput/deployedOffchainDataFetcher.json. - Select the
Contractsection, then click on theWrite Contracttab. - Connect your admin account, which is the wallet you used to deploy the contract.
- Call the
setAutomationCronContractfunction with theUpkeep addressas the input parameter. Note: You can find theUpkeep addressin 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
-
Go to the Chainlink Automation App.
-
Create a new Custom logic upkeep with a starting balance of 5 testnet LINK. Note: You can find your deployed
AutomatedPortfolioManagercontract address inoutput/deployedPortfolioManager.json. -
Configure your contract so only the upkeep contract (and your admin account, which deployed the contract) can call the
performUpkeepfunction. This security measure is important to prevent anyone from calling several timesperformUpkeepand draining your Automation balance.- Go to Etherscan.
- Enter your
AutomatedPortfolioManagercontract address in the search bar. Note: This address is available inoutput/deployedPortfolioManager.json. - Select the
Contractsection, then click on theWrite Contracttab. - Connect your admin account, which is the wallet you used to deploy the contract.
- In the function inputs, enter the
Forwarder addressinto thesetAutomationUpkeepForwarderContractfunction. Note: You can find theForwarder addressin the Details section of your upkeep in the Automation App.
Run the dApp locally
-
Navigate to the
appfolder:cd app -
Create a
.env.localfile in theappdirectory:cp .env.example .env.local -
Open your
.env.localfile and add your Sepolia RPC URL:NEXT_PUBLIC_SEPOLIA_RPC_URL=your_sepolia_rpc_urlYou can use the same RPC URL you used for contract deployment or get one from providers like Alchemy or Infura.
-
Run the dApp locally:
npm run dev -
Navigate to
http://localhost:3000with your favorite browser. -
Change tab to
My Investment. -
Connect your wallet.
-
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.