The Automated Portfolio Manager automatically rebalances an investment portfolio using real-time data from on-chain and off-chain 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.
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, investments in ERC-4626 vaults, etc.
-
This tutorial represents an educational example to use a Chainlink system, product, or service and is provided to demonstrate how to interact with Chainlink’s systems, products, and services to integrate them into your own. This template is provided “AS IS” and “AS AVAILABLE” without warranties of any kind, it has not been audited, and it may be missing key checks or error handling to make the usage of the system, product or service more clear. Do not use the code in this example in a production environment without completing your own audits and application of best practices. Neither Chainlink Labs, the Chainlink Foundation, nor Chainlink node operators are responsible for unintended outputs that are generated due to errors in code.
-
The rebalancing strategy and logic outlined in this tutorial are solely for educational purposes and have not been backtested. This content is not meant to be financial advice and should not be interpreted as such. The example is intended to demonstrate the potential of Chainlink products. Users are strongly advised to perform their own due diligence before making any financial decisions.
- Requirements
- Project setup
OffchainDataFetcher
deployment and setupAutomatedPortfolioManager
deployment and setup- Run the dApp locally
-
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.
- 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.
-
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 UI.
-
Clone and navigate to the repository:
git clone https://github.com/smartcontractkit/quickstarts-automated-portfolio-manager.git cd quickstarts-automated-portfolio-manager
-
Install the front-end and back-end dependencies:
make install
-
Copy the
.env.example
file to.env
:cp .env.example .env
-
Open your
.env
file and fill in the required environment variables. -
Run
source .env
to make your environment variables available in your terminal session.source .env
-
Run
forge compile
to update dependencies in thelib
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!
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
-
Open the
functions/updateRequestDon.js
script and update thesubscriptionId
variable with your Chainlink Functions subscription ID. -
Fund your Functions subscription with at least 5 testnet LINK (Chainlink Functions UI).
-
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 theupdateRequestGists.js
script instead ofupdateRequestDon.js
. -
Add your
OffChainDataFetcher
contract as a consumer in your Functions subscription (Chainlink Functions UI). Note: You can find your deployedOffchainDataFetcher
contract address inoutput/deployedOffchainDataFetcher.json
.
-
Register an upkeep to call the
sendRequestCBOR
function on yourOffchainDataFetcher
contract daily:- Go to the Chainlink Automation UI.
- 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 inoutput/deployedOffchainDataFetcher.json
. -
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 timessendRequestCBOR
and draining your Functions subscription balance.- Go to Etherscan.
- Enter your
OffchainDataFetcher
contract address in the search bar. Note: This address is available inoutput/deployedOffchainDataFetcher.json
. - Select the
Contract
section, then click on theWrite Contract
tab. - Connect your admin account, which is the wallet you used to deploy the contract.
- Call the
setAutomationCronContract
function with theUpkeep address
as the input parameter. Note: You can find theUpkeep address
in the Details section of your upkeep in the Automation UI.
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
-
Go to the Chainlink Automation UI.
-
Create a new Custom logic upkeep with a starting balance of 5 testnet LINK. Note: You can find your deployed
AutomatedPortfolioManager
contract address inoutput/deployedPortfolioManager.json
. -
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 timesperformUpkeep
and draining your Automation balance.- Go to Etherscan.
- Enter your
AutomatedPortfolioManager
contract address in the search bar. Note: This address is available inoutput/deployedPortfolioManager.json
. - Select the
Contract
section, then click on theWrite 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 thesetAutomationUpkeepForwarderContract
function. Note: You can find theForwarder address
in the Details section of your upkeep in the Automation UI.
-
Navigate to the
app
folder:cd app
-
Create a
.env.local
file in theapp
directory:cp .env.example .env.local
-
Open your
.env.local
file and add your Sepolia RPC URL:NEXT_PUBLIC_SEPOLIA_RPC_URL=your_sepolia_rpc_url
You 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:3000
with 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.