To help you better understand how a Dapp - ADCS works, let’s dive into the following example together Meme Coin Trend Application.
The Meme Coin Trend application utilizes cutting-edge AI and blockchain technology to monitor trending meme coins and analyze market data in real time. With an integrated AI agent, the platform autonomously trades meme coins using insights gathered from the ADCS network, ensuring efficient and data-driven trading decisions.
Step-by-step guide on how to approach building the application
You need to define an adapter that outlines the necessary parameters.
Provider: Meme Coin Trend
Network: Rivalz
Category : Meme Coin
Output Type : StringAndBool
Adaptor Name (String): Meme Coin Trend.
Description(String): Get a trending meme coin and make decisions which meme coin should buy.
Prompt: Retrieve the current trending meme coins and analyze market conditions to recommend which meme coin should be bought.
Once an adaptor is created, the system will generate a unique JobID.
Oxd7....2352 is the JobID for your Meme Coin Trend Adaptor.
2. Setup Your Consumer Contract:
Depending on the type of outputData you have defined in the adaptor, your consumer contract must inherit from the appropriate ADCS fulfillment contract. Here the outputData is StringandBool so the consumer contract will inherit the ADCSConsumerFulfillStringAndBool.
ADCSConsumerBase is an abstract contract that serves as the base for consumer contracts interacting with the ADCS coordinator. It initializes data requests and verifies the fulfillment.
The constructor takes the Coordinator contract's address (_coordinator) as input and passes it to the base contract. The Coordinator manages the interaction between oracles and consumers, facilitating the flow of data requests and responses.
_weth: the address of the WETH (Wrapped Ether) token contract.
_swapRouter: the address of the Uniswap V3 swap router contract, used to interact with the Uniswap decentralized exchange (DEX) to swap tokens
Different Coordinator contract addresses are provided, each tailored to handle a specific output type, such as uint256, bool, or bytes. This allows consumers to interact with oracles in a way that matches the data type of the responses they expect.
setWethAmountForTrade Function: Sets the amount of WETH to use for trading.
function setWethAmountForTrade(uint256 amount) external onlyOwner {
wethAmountForTrade = amount;
}
3. Deploy the consumer contract
After defining all necessary functions, the global state, and the Coordinator contract for your consumer contract, the next step is to deploy the contract to the blockchain. Learn more here.
4. How the Meme Coin Trend Application works
When receiving a request from the consumer contract, the system listens for the request and then identifies an appropriate AI to process the data.
The AI system processes the request, identifying the meme coin (e.g., "Shiba Inu") and making a recommendation (e.g., buy or sell).
The Coordinator contract receives the result from the AI system and calls the fulfillDataRequest() function in the consumer contract, passing the processed result (token name and recommendation).
The consumer contract processes the result in the fulfillDataRequest() function, which identifies the meme coin (via the name) and calls the tradeMemeCoin() function to execute the buy or sell action.
The contract interacts with the Uniswap V3 router to perform the trade, and once completed, the TradeSuccess event is emitted.
FullCode Example
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "../ADCSConsumerFulfill.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
import "@uniswap/v3-periphery/contracts/interfaces/IPeripheryPayments.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract MockTradeMemeCoin is ADCSConsumerFulfillStringAndBool, Ownable {
using ADCS for ADCS.Request;
// Store the last received response for testing
bytes public lastResponse;
uint256 public lastRequestId;
uint256 public wethAmountForTrade = 1000000000000000; // 0.001 WETH
uint256 public memeCoinAmount = 100; // 100 memecoin
struct MemeCoin {
string name;
address addr;
uint8 decimals;
}
MemeCoin[] public memeCoins;
event DataRequested(uint256 indexed requestId);
event DataFulfilled(uint256 indexed requestId, bytes response);
event MemecoinNotFound(string tokenName);
event TradeSuccess(uint256 indexed requestId, uint256 amountIn, bool isBuy);
address public immutable WETH;
ISwapRouter public immutable swapRouter;
constructor(
address _coordinator,
address _weth,
address _swapRouter
) ADCSConsumerBase(_coordinator) Ownable(msg.sender) {
WETH = _weth;
swapRouter = ISwapRouter(_swapRouter);
}
function setWethAmountForTrade(uint256 amount) external onlyOwner {
wethAmountForTrade = amount;
}
/**
* @notice Add a new memecoin to the list
* @param name The name of the memecoin
* @param addr The contract address of the memecoin
* @param decimals The decimals of the memecoin
*/
function addMemeCoin(string memory name, address addr, uint8 decimals) external onlyOwner {
memeCoins.push(MemeCoin({name: name, addr: addr, decimals: decimals}));
}
/**
* @notice Get the total number of memecoins in the list
* @return The length of the memecoins array
*/
function getMemeCoinCount() external view returns (uint256) {
return memeCoins.length;
}
/**
* @notice Get a memecoin by index
* @param index The index in the memecoins array
* @return name The memecoin name
* @return addr The memecoin contract address
* @return decimals The decimals of the memecoin
*/
function getMemeCoin(
uint256 index
) external view returns (string memory name, address addr, uint8 decimals) {
require(index < memeCoins.length, "Index out of bounds");
MemeCoin memory coin = memeCoins[index];
return (coin.name, coin.addr, coin.decimals);
}
// Function to request bytes data
function requestTradeMemeCoin(
bytes32 jobId,
uint256 callbackGasLimit
) external returns (uint256 requestId) {
bytes32 typeId = keccak256(abi.encodePacked("stringAndbool"));
ADCS.Request memory req = buildRequest(jobId, typeId);
requestId = COORDINATOR.requestData(callbackGasLimit, req);
emit DataRequested(requestId);
return requestId;
}
function fulfillDataRequest(
uint256 requestId,
StringAndBool memory response
) internal virtual override {
string memory tokenName = response.name;
bool result = response.response;
// Find memecoin address and decimals by name
tradeMemeCoin(requestId, tokenName, result);
}
function tradeMemeCoin(uint256 requestId, string memory tokenName, bool result) internal {
// Find memecoin address and decimals by name
address memeTokenAddress;
uint8 tokenDecimals;
for (uint i = 0; i < memeCoins.length; i++) {
if (keccak256(bytes(memeCoins[i].name)) == keccak256(bytes(tokenName))) {
memeTokenAddress = memeCoins[i].addr;
tokenDecimals = memeCoins[i].decimals;
break;
}
}
if (memeTokenAddress == address(0)) {
emit MemecoinNotFound(tokenName);
return;
}
// Execute trade through Uniswap V3
if (result) {
// buy memecoin with eth
IERC20(WETH).approve(address(swapRouter), wethAmountForTrade);
swapRouter.exactInputSingle(
ISwapRouter.ExactInputSingleParams({
tokenIn: WETH,
tokenOut: memeTokenAddress,
fee: 3000,
recipient: address(this),
deadline: block.timestamp + 15 minutes,
amountIn: wethAmountForTrade,
amountOutMinimum: 0,
sqrtPriceLimitX96: 0
})
);
emit TradeSuccess(requestId, wethAmountForTrade, true);
} else {
// sell memecoin for eth
// First approve router to spend our tokens
uint256 memeCoinAmountInWei = memeCoinAmount * (10 ** tokenDecimals);
IERC20(memeTokenAddress).approve(address(swapRouter), memeCoinAmountInWei);
swapRouter.exactInputSingle(
ISwapRouter.ExactInputSingleParams({
tokenIn: memeTokenAddress, // memecoin token
tokenOut: WETH, // eth
fee: 3000, // 0.3% fee tier
recipient: address(this),
deadline: block.timestamp + 15 minutes,
amountIn: memeCoinAmountInWei,
amountOutMinimum: 0, // Set minimum amount out to 0 (should use proper slippage in production)
sqrtPriceLimitX96: 0
})
);
emit TradeSuccess(requestId, memeCoinAmountInWei, false);
}
}
receive() external payable {}
function withdraw() external onlyOwner {
payable(owner()).transfer(address(this).balance);
}
function withdrawToken(address token) external onlyOwner {
IERC20(token).transfer(owner(), IERC20(token).balanceOf(address(this)));
}
}
We've also built some examples to help you easily understand what you can do with ADCS network here