Retrieve current price data off-chain with AMB Access Polygon and Chainlink Data Feeds
Learn how to retrieve asset price data using Amazon Managed Blockchain (AMB) Access and Chainlink Price Feeds
Introduction
In this tutorial, you will learn how to retrieve current price data off-chain for various cryptocurrencies and real-world assets by using Amazon Managed Blockchain (AMB) Access Polygon and Chainlink Data Feeds. You will retrieve data from Chainlink's deployed Verified Price Feed smart contracts on Polygon Mainnet using AMB Access and web3.js, a widely-used JavaScript library for Ethereum. Chainlink Data Feeds gather and publish aggregated data from a variety of sources on-chain, utilizing Chainlink's decentralized oracle network.
Prerequisites
- Installation of Node.js (version 18 or above)
- A text editor (such as Visual Studio Code)
- Basic knowledge of Ethereum and smart contracts
- Installation of the AWS Command Line Interface (CLI).
- Run the command
aws configure
to set the variables for your IAM User’sAccess Key ID
,Secret Access Key
, andRegion
.- Ensure that this User has the appropriate IAM permissions for AMB Access Polygon. For this tutorial, you can use the
AmazonManagedBlockchainFullAccess
managed policy.
- Ensure that this User has the appropriate IAM permissions for AMB Access Polygon. For this tutorial, you can use the
- Run the command
Step 1: Setting up the project
- Create a new directory for your project and change into it by running the following command:
mkdir chainlinkPriceFeeds && cd chainlinkPriceFeeds
- Run the following command to install the necessary dependencies:
npm install web3 @aws-sdk/client-managedblockchain dotenv
Web3
enables blockchain interactions, dotenv
handles environment variables, and the Managed Blockchain client
is used to initialize your AMB Access Polygon Mainnet endpoint.
Step 2: AMB Access Polygon Endpoint Configuration
The following script automates the following tasks:
- Retrieval or creation of an AMB (Amazon Managed Blockchain) Polygon Mainnet Accessor token.
- The script first checks for any existing Polygon Mainnet Accessor tokens in your AWS account. If an available token is found, it will be reused. Otherwise, a new Accessor token will be created.
- Configuration of an AMB Access Polygon Mainnet endpoint with your Accessor token.
- Your configured endpoint will allow you to make JSON-RPC calls to Polygon Mainnet with
web3.js
.
- Your configured endpoint will allow you to make JSON-RPC calls to Polygon Mainnet with
Warning: Never share or expose your Accessor token publicly. It is important to note that this code should not be used in production environments, as it poses a security risk.
Create a new file (e.g init.js
) in the root of your directory and copy the following code:
const fs = require('fs').promises;
const { Web3 } = require('web3');
const { ManagedBlockchainClient, CreateAccessorCommand, ListAccessorsCommand, GetAccessorCommand } = require("@aws-sdk/client-managedblockchain");
const network = "POLYGON_MAINNET";
async function getExistingAccessor(client) {
try {
const response = await client.send(new ListAccessorsCommand({ NetworkType: network }));
for (const accessor of response.Accessors) {
if (accessor.Status === "AVAILABLE") {
const accessorResponse = await client.send(new GetAccessorCommand({ AccessorId: accessor.Id }));
return accessorResponse.Accessor;
}
}
} catch (error) {
console.error('Error retrieving existing Accessor:', error);
throw error;
}
return null;
}
async function createAccessor() {
const client = new ManagedBlockchainClient();
const existingAccessor = await getExistingAccessor(client);
if (existingAccessor) {
console.log('Using existing Accessor token.');
return {
billingToken: existingAccessor.BillingToken
};
}
else {
console.log('Creating a new Accessor token.');
}
try {
const input = {
AccessorType: "BILLING_TOKEN",
NetworkType: network,
};
const command = new CreateAccessorCommand(input);
const response = await client.send(command);
return {
billingToken: response.BillingToken,
};
} catch (error) {
console.error('Error creating Accessor token:', error);
throw error;
}
}
async function main() {
try {
console.log('Creating or retrieving AMB Access Polygon Accessor token...');
const accessor = await createAccessor();
const accessEndpoint = `https://mainnet.polygon.managedblockchain.us-east-1.amazonaws.com?billingtoken=${accessor.billingToken}`;
const dataToSave = `AMB_ACCESS_POLYGON_MAINNET=${accessEndpoint}`;
await fs.writeFile('.env', dataToSave);
console.log('Accessor token created or retrieved. Details saved to .env file.');
} catch (error) {
console.error('Setup failed:', error);
}
}
main();
Execute the script by running:
node init.js
Your endpoint for AMB Access Polygon Mainnet has been securely stored in a generated .env
file.
Step 3: Write the Chainlink Price Feeds Script
Create a new file (e.g priceFeeds.js
) and copy the following code:
const { Web3 } = require('web3')
require('dotenv').config();
const web3 = new Web3(process.env.AMB_ACCESS_POLYGON_MAINNET);
const abi = [{ "inputs": [{ "internalType": "address", "name": "_aggregator", "type": "address" }, { "internalType": "address", "name": "_accessController", "type": "address" }], "stateMutability": "nonpayable", "type": "constructor" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "int256", "name": "current", "type": "int256" }, { "indexed": true, "internalType": "uint256", "name": "roundId", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "updatedAt", "type": "uint256" }], "name": "AnswerUpdated", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "uint256", "name": "roundId", "type": "uint256" }, { "indexed": true, "internalType": "address", "name": "startedBy", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "startedAt", "type": "uint256" }], "name": "NewRound", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "from", "type": "address" }, { "indexed": true, "internalType": "address", "name": "to", "type": "address" }], "name": "OwnershipTransferRequested", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "from", "type": "address" }, { "indexed": true, "internalType": "address", "name": "to", "type": "address" }], "name": "OwnershipTransferred", "type": "event" }, { "inputs": [], "name": "acceptOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "accessController", "outputs": [{ "internalType": "contract AccessControllerInterface", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "aggregator", "outputs": [{ "internalType": "address", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_aggregator", "type": "address" }], "name": "confirmAggregator", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "decimals", "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "description", "outputs": [{ "internalType": "string", "name": "", "type": "string" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "uint256", "name": "_roundId", "type": "uint256" }], "name": "getAnswer", "outputs": [{ "internalType": "int256", "name": "", "type": "int256" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "uint80", "name": "_roundId", "type": "uint80" }], "name": "getRoundData", "outputs": [{ "internalType": "uint80", "name": "roundId", "type": "uint80" }, { "internalType": "int256", "name": "answer", "type": "int256" }, { "internalType": "uint256", "name": "startedAt", "type": "uint256" }, { "internalType": "uint256", "name": "updatedAt", "type": "uint256" }, { "internalType": "uint80", "name": "answeredInRound", "type": "uint80" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "uint256", "name": "_roundId", "type": "uint256" }], "name": "getTimestamp", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "latestAnswer", "outputs": [{ "internalType": "int256", "name": "", "type": "int256" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "latestRound", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "latestRoundData", "outputs": [{ "internalType": "uint80", "name": "roundId", "type": "uint80" }, { "internalType": "int256", "name": "answer", "type": "int256" }, { "internalType": "uint256", "name": "startedAt", "type": "uint256" }, { "internalType": "uint256", "name": "updatedAt", "type": "uint256" }, { "internalType": "uint80", "name": "answeredInRound", "type": "uint80" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "latestTimestamp", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "owner", "outputs": [{ "internalType": "address", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "uint16", "name": "", "type": "uint16" }], "name": "phaseAggregators", "outputs": [{ "internalType": "contract AggregatorV2V3Interface", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "phaseId", "outputs": [{ "internalType": "uint16", "name": "", "type": "uint16" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_aggregator", "type": "address" }], "name": "proposeAggregator", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "proposedAggregator", "outputs": [{ "internalType": "contract AggregatorV2V3Interface", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "uint80", "name": "_roundId", "type": "uint80" }], "name": "proposedGetRoundData", "outputs": [{ "internalType": "uint80", "name": "roundId", "type": "uint80" }, { "internalType": "int256", "name": "answer", "type": "int256" }, { "internalType": "uint256", "name": "startedAt", "type": "uint256" }, { "internalType": "uint256", "name": "updatedAt", "type": "uint256" }, { "internalType": "uint80", "name": "answeredInRound", "type": "uint80" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "proposedLatestRoundData", "outputs": [{ "internalType": "uint80", "name": "roundId", "type": "uint80" }, { "internalType": "int256", "name": "answer", "type": "int256" }, { "internalType": "uint256", "name": "startedAt", "type": "uint256" }, { "internalType": "uint256", "name": "updatedAt", "type": "uint256" }, { "internalType": "uint80", "name": "answeredInRound", "type": "uint80" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_accessController", "type": "address" }], "name": "setController", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_to", "type": "address" }], "name": "transferOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "version", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }]
//Contract addresses for Chainlink Data Feeds on Polygon Mainnet
const contracts = [
{ name: 'Bitcoin', address: '0xc907E116054Ad103354f2D350FD2514433D57F6f' },
{ name: 'Ethereum', address: '0xF9680D99D6C9589e2a93a78A04A279e509205945' },
{ name: 'Polygon', address: '0xAB594600376Ec9fD91F8e885dADF0CE036862dE0' },
{ name: 'XAU (Gold)', address: '0x0C466540B2ee1a31b441671eac0ca886e051E410' },
{ name: 'GBP/USD', address: '0x099a2540848573e94fb1Ca0Fa420b00acbBc845a' }
];
const getDecimals = async (priceFeed) => {
return await priceFeed.methods.decimals().call();
}
const getLatestRoundData = async (priceFeed) => {
return await priceFeed.methods.latestRoundData().call();
}
async function main() {
for (const contract of contracts) {
try {
const priceFeed = new web3.eth.Contract(abi, contract.address);
const decimals = await getDecimals(priceFeed);
const roundData = await getLatestRoundData(priceFeed);
const balanceNumber = Number(roundData.answer) / (10 ** Number(decimals));
const formattedBalanceString = balanceNumber.toLocaleString('en-US', {
style: 'currency',
currency: 'USD'
});
console.log(`${contract.name} Price: ${formattedBalanceString}`);
} catch (error) {
console.error(`An error occurred with ${contract.name}:`, error);
}
}
}
main();
Run the script with the following command:
node priceFeeds.js
You will see the current prices in USD for Bitcoin, Ethereum, Polygon, gold (in ounces), and the Great Britain Pound. This data was saved in a decentralized manner to Chainlink’s on-chain Verified Price Feeds. If you would like to incorporate price feed data for other assets, you can refer to Chainlink’s extensive list of Price Feed Contract Addresses.
Conclusion
You have successfully utilized AMB Access and Chainlink Price Feeds to fetch real time prices of various assets from off-chain using web3.js
. If you would like to learn more about Chainlink Price Feeds, you can refer to the official documentation. Remember to prioritize security, especially with sensitive data such as your Amazon Managed Blockchain Accessor token.
Please leave a comment below if you have any questions. For further information on AMB Access, you can refer to the documentation.
Relevant content
- asked 5 years agolg...
- Accepted Answerasked 2 years agolg...
- AWS OFFICIALUpdated 7 days ago
- AWS OFFICIALUpdated a year ago
- AWS OFFICIALUpdated 2 years ago
- AWS OFFICIALUpdated 10 months ago