csx (Chainsight command-line execution envirionment) depends on several tools. Therefore, developers must have these tools installed and available in advance.
You can confirm that it has been installed and is in the path by running the following in a terminal.
% rustc --version
rustc 1.76.0 (07dca489a 2024-02-04)
% dfx --version
dfx 0.15.1
Before you can deploy on your local machine, you need to deploy chainsight-management-canisters to your local dfx environment.
git clone https://github.com/horizonx-tech/chainsight-management-canisters.git
cd chainsight-management-canisters/artifacts
make all port=${YOUR_DFX_PORT}
You can choose between the following two methods of installation.
- Download the prebuild CLI binaries
- Build the CLI from source code
Once you have followed either of these steps to install, you can confirm the installation with the following command.
csx --version
# -> csx version: x.y.z
Warning
Currently only macos and linux environments are supported.
This is a limitation of the dfx specification on which it depends.
Install and path the binary file according to your terminal from the following
Releases · horizonx-tech/chainsight-cli
Clone this repository and run cargo build.
cd horizonx-tech/chainsight-cli && cargo build --release && cargo install --path .
A small modification to the Manifest, which is placed when the project is initially generated, is all that is required to create a deployable component.
Let's actually create a project/component with the project name 'initial_project'.
The 'new' command can be used to create a project with a specified project name.
# Create Chainsight project
csx new initial_project
Then modify the manifest. Specifically, modify the rpc_url key to match your project.
Once the manifest is corrected to the correct one, the 'build' command can be executed to generate the module and execution commands to run canister.
# Build project
csx build --path initial_project
If the build command succeeds, deploy with the 'deploy' command.
Remember to have a dfx network in your local area when deploying locally.
# Deploy project
# NOTE: If you deploy in local, dfx network must be started (ex: 'dfx start')
dfx start
csx deploy
When 'deploy' is complete, the 'exec' command sends the actual initialization and start of periodic execution instructions to the component.
# Initialize Components / Start processing
csx exec --path initial_project
Use Manifest to actually tell Chainsight what the developer wants to customize.
Specifically, this is a YAML file, and the developer's intent can be reflected by having the values set according to the format specified on the Chainsight side.
Currently, the following types of Manifest are available.
- Component Manifest
- Project Manifest
A Canister that contains a Module specialized for the data processes managed within the Chainsight Platform.
Chainsight defines several types of canisters specialized for certain applications, allowing users to select the type that best suits their needs and to freely customize items within that type.
The following Components are currently available on the CLI.
- Snapshot Indexer
- Select the following types depending on the location of the data you wish to collect
- Snapshot Indexer EVM: For EVM-based Other chains
- Snapshot Indexer ICP: Other canisters on Internet Computer
- Snapshot Indexer HTTPS: Resources available on the general web
- Select the following types depending on the location of the data you wish to collect
- Event Indexer
- Algorithm Indexer
- Algorithm Lens
- Relayer
Note
See following for all supported Component Types and their purpose.
Data Processing Components - Chainsight Network
Manage one or more Components together.
For example, at least Snapshot Indexer and Relayer are required to take a snapshot of ERC20's totalSupply obtained from Ethereum and flow it to other Chains.
Most objectives require combining multiple Components, and Project is provided to make them easier to manage.
Project consists of the following folders.
However, these are not usually necessary to be aware of.
(project root)
|- artifacts # Artifacts generated by 'build' process
|- components # Place Component Manifest
|- interfaces # Place dependent interfaces (abi etc.)
|- project.yaml # Project Manifest
.chainsight
These are the Internet Computer concepts needed to better understand Chainsight
HTTPS outcalls in the Internet Computer refer to making HTTP requests from a canister (smart contract) running on the Internet Computer to external HTTPS endpoints. It allows canisters to interact with external services, such as APIs or web servers, over secure HTTPS connections.
HTTPS outcalls are subject to certain limitations and security considerations imposed by the Internet Computer platform. For example, canisters have a set of allowed domains and endpoints they can make requests to, and the response size and request duration may be limited. These restrictions are in place to ensure the security, scalability, and resource efficiency of the Internet Computer network.
For more information, please check here.
HTTPS outcalls: technology overview | Internet Computer
Unlike other blockchains, the Internet Computer can automatically execute canister smart contracts after a specified delay or periodically.
For more information, please check here.
Periodic tasks and timers | Internet Computer
Several subcommands of csx can be used to advance the development of the component.
The basic syntax is as follows
csx [subcommand] [flag]
You can also check the list of executable commands by specifying the --help
flag.
Create a new project for Chainsight Platform.
This generated project contains several Manifests as templates.
Specify the name of the project to be created, and then generate the project.
csx new --help
Generates Chainsight project with built-in templates
Usage: csx new [OPTIONS] <PROJECT_NAME>
Arguments:
<PROJECT_NAME> Specifies the name of the project to create
Options:
--no-samples Skip generation of sample component manifests [short aliases: n]
-v, --verbose... Displays detailed information about operations. -vv will generate a very large number of messages and can
affect performance
-q, --quiet... Suppresses informational messages. -qq limits to errors only; -qqqq disables them all
-h, --help Print help
The 'add' command is used to add a new type of component to your project.
This command will add a Component Manifest of the specified Type and its management settings to the Project Manifest.
If you are familiar with it, you can do manually what this command does.
--type
: Specify the Component Type- A Template Manifest will be generated for the specified Component Type.
--path
: Select the path of the project to which you want to add the Component.- The folder containing the
.chainsight
file will be recognized as the project.
- The folder containing the
% csx add --help
Generates component manifest of specified type and adds to your project
Usage: csx add [OPTIONS] --type <TYPE> <COMPONENT_NAME>
Arguments:
<COMPONENT_NAME>
Specifies the name of the component to add
Options:
--type <TYPE>
Specifies type of the component to add
Possible values:
- event-indexer: To synchronize event data
- algorithm-indexer: To get events from other indexer and convert it into another format
- snapshot-indexer-icp: To periodically take and store snapshots from other Canisters
- snapshot-indexer-evm: To periodically take and store snapshots from Contract
- snapshot-indexer-https: To periodically take and store snapshots using HTTPS Outcall
- relayer: To relay data to other blockchains
- algorithm-lens: To calculate using data obtained from the specified Source and process into an arbitrary format
-v, --verbose...
Displays detailed information about operations. -vv will generate a very large number of messages and can affect performance
-p, --path <PATH>
Specify the path of the project to which the component is to be added. If not specified, the current directory is targeted
-q, --quiet...
Suppresses informational messages. -qq limits to errors only; -qqqq disables them all
-h, --help
Print help (see a summary with '-h')
Generate component code from the manifest in your Chainsight project.
% csx generate --help (or csx gen ...)
Generate codes according to project/component manifests
Usage: csx generate [OPTIONS]
Options:
-p, --path <PATH> Specify the path of the project. If not specified, the current directory is targeted
-v, --verbose... Displays detailed information about operations. -vv will generate a very large number of messages and can affect performance
-q, --quiet... Suppresses informational messages. -qq limits to errors only; -qqqq disables them all
-h, --help Print help
Generate a canister module that runs on the Chainsight Platform from your Chainsight project code.
By default, it also includes csx generate
, use the -only-build
option if you want to generate modules only.
% csx build --help
Builds your project to generate canisters' modules for Chainsight
Usage: csx build [OPTIONS]
Options:
-p, --path <PATH> Specify the path of the project to build. If not specified, the current directory is targeted
-v, --verbose... Displays detailed information about operations. -vv will generate a very large number of messages and can affect performance
--only-build Only perform build. Perform this steps with code already generated
-q, --quiet... Suppresses informational messages. -qq limits to errors only; -qqqq disables them all
-h, --help Print help
This command is used to deploy a built module of your own Project to a specified network (local or IC).
It is built by wrapping the operations performed by the dfx deploy command, plus Identity and Wallet checks.
--path
: Specify the path of the project to deploy. If not specified, the current directory is targeted--network
: Specify the network to deploy to.- Currently, you can choose between the following options.
- local ... localhost
- ic ... mainnet of Internet Computer
- Currently, you can choose between the following options.
--component
: Only specified components can be targeted.- Without this option, all components declared in the project are executed as targets.
% csx deploy --help
Deploy the components of your project. If you want to operate on a local network, you need to build a local dfx network in advance
Usage: csx deploy [OPTIONS]
Options:
-p, --path <PATH> Specify the path of the project to deploy. If not specified, the current directory is targeted
-v, --verbose... Displays detailed information about operations. -vv will generate a very large number of messages and can affect performance
-c, --component <COMPONENT> Specify the component to deploy. If this option is not specified, the command will be given to all components managed by the project
-q, --quiet... Suppresses informational messages. -qq limits to errors only; -qqqq disables them all
--network <NETWORK> Specify the network to execute on [default: local] [possible values: local, ic]
--port <PORT> Specifies the port to call. This option is used only if the target is localhost
--with-cycles <WITH_CYCLES> Specify the initial number of cycles for canister. Used as a parameter for `dfx canister create`
-h, --help Print help
Executes commands that prepare the deployed project's Components for correct operation. The commands executed here are built from the information in the Component Manifest.
It is currently implemented to perform the following commands.
- Setup: Set parameters for the Component to operate as intended.
- Start timer task: kick periodic execution of data acquisition/processing/storage.
% csx exec --help
Calls for component processing. Currently supports initialization and task start instructions
Usage: csx exec [OPTIONS]
Options:
-p, --path <PATH> Specify the path of the project that manages the component to be called. Refer to the manifest of this project to build the commands that should be executed
-v, --verbose... Displays detailed information about operations. -vv will generate a very large number of messages and can affect performance
-c, --component <COMPONENT> Specify the name of the component you want to execute. If this option is not specified, the command will be given to all components managed by the project
-q, --quiet... Suppresses informational messages. -qq limits to errors only; -qqqq disables them all
--network <NETWORK> Specify the network to execute on [default: local] [possible values: local, ic]
--only-generate-cmds Only generate commands
--only-execute-cmds Only execute commands. Perform this steps with commands already generated
-h, --help Print help
Remove already deployed components included in the project.
% csx delete --help
Delete your Chainsight component. This command deletes the component with sidecars and allows you to recover the remaining cycles
Usage: csx delete [OPTIONS] --component <COMPONENT>
Options:
-p, --path <PATH> Specify the path of the project to be deleted. If not specified, the current directory is targeted
-v, --verbose... Displays detailed information about operations. -vv will generate a very large number of messages and can affect performance
-c, --component <COMPONENT> Specify the component name or canister id to delete
-q, --quiet... Suppresses informational messages. -qq limits to errors only; -qqqq disables them all
--network <NETWORK> Specify the network to execute on [default: local] [possible values: local, ic]
--port <PORT> Specifies the port to call. This option is used only if the target is localhost
-h, --help Print help
Used to remove resources related to a specified component from your project.
Warning
Stopping a deployed Component (Canister) is currently not part of the process, so you will need to stop/delete the Component, canister, manually.
dfx remove --path sample_project
The developer must update the manifest to meet his/her own objectives and communicate his/her intentions to the component.
This section describes how to define and modify Manifest.
This section describes the manifest of the component managed by your project.
The version is currently fixed at "v1" only.
example)
version: v1
label: sample
components:
- component_path: components/sample_algorithm_indexer.yaml
- component_path: components/sample_algorithm_lens.yaml
- component_path: components/sample_event_indexer.yaml
- component_path: components/sample_relayer.yaml
- component_path: components/sample_snapshot_indexer_evm.yaml
- component_path: components/sample_snapshot_indexer_icp.yaml
- component_path: components/sample_snapshot_indexer_https.yaml
The description of each component type is different, but the following is a description of the common parts.
version
: Like the project manifest, currently fixed at “v1”.
metadata
: These are the places where the meta-information of the Component, which can be any Type, is stored, and are set in the custom attributes of WASM according to the specification of Internet Computer.
Of particular importance is the type, which determines the Component Type, so be sure to check and set it.
label
: String / Component nametype
: Enum / Component Type (ex: snapshot_indexer_icp)description
: String / Component description fieldtags
: Array(String) / Tags for component elements
example)
version: v1
metadata:
label: sample_snapshot_indexer_evm
type: snapshot_indexer_evm
description: ''
tags: ...
...
The differences between components are presented in the "Components" section below.
If you want to use environment variables or secret values in the manifest, you can use the following syntax.
version: v1
metadata:
label: sample_snapshot_indexer_evm
type: snapshot_indexer_evm
description: ''
tags: ...
datasource:
type: contract
location:
id: "${CHAIN_CONTRACT_ID}"
args:
network_id: ${CHAIN_NETWORK_ID}
rpc_url: https://alchemy.com/v2/${CHAIN_ALCHEMY_KEY}
...
And then, you can set the environment variables in the .env
file on the project root.
CHAIN_CONTRACT_ID=0x1234567890
CHAIN_NETWORK_ID=1
CHAIN_ALCHEMY_KEY=abcde12345
As mentioned earlier, there are multiple types of Snapshot Indexer, and each datasource has different logic and external connection methods, so there are differences in the way manifest is written.
These Snapshot Indexer types are also specified in metadata.type
.
- snapshot_indexer_evm
- snapshot_indexer_icp
- snapshot_indexer_https
This section also briefly introduces the manifest items that commonly exist in Snapshot Indexer.
datasource
specifies the data source to be collected- The method of description varies slightly from component to component. See individual component sections for details.
interval
specifies the interval between acquiring and storing data at the destination specified bydatasource
- Number / Interval between data acquisition and storage.
- Set in seconds.
datasource: ...
...
interval: 3600
Select this type if you want to build a data snapshot using HTTPS outcalls from an EVM-compliant blockchain.
In datasource
, specify the destination network, contract address, and contract function.
datasource.location
... Specify destination network, contractid
: String / Contract addressargs.network_id
: Number / chain_id of the target networkargs.rpc_url
: String / rpc endpoint url to connect to the target network
datasource.method
... Specify the function of the contract to be called.identifier
: String / Interface of the function to be called.- Please refer to the interfaces mentioned in the repository below.
interface
: Name of the ABI file containing the interface to the above function.- Placed under
(project folder)/interfaces
- ERC20.json is buildin to the CLI.
- Placed under
args
: String, Number / If the function has arguments, set their values.- The value entered is always used in HTTPS outcalls as a fixed value.
- Snapshot stores function calls with the same conditions as a Snapshot, so it is not possible to calculate the input value each time.
- The value entered is always used in HTTPS outcalls as a fixed value.
example)
version: v1
metadata:
label: sample_snapshot_indexer_evm
type: snapshot_indexer_evm
description: ''
tags: ...
datasource:
type: contract
location:
id: 6b175474e89094c44da98b954eedeac495271d0f
args:
network_id: 1
rpc_url: https://eth-mainnet.g.alchemy.com/v2/<YOUR_KEY>
method:
identifier: totalSupply():(uint256)
interface: ERC20.json
args: []
interval: 3600
cycles: null
Select this type if you want to use cross canister calls within Internet Computer to build a snapshot of data that can be retrieved from other canisters.
Set the function for the canister to be acquired at datasource
.
datasource.location
... Specify the canister to retrieve from.id
: String / Target canister name or id
datasource.method
... Specify the Canister function to call.method.identifier
: String / interface of the function to be called.- Refer to the interface definition in the candid file and enter.
- NOTE: Exclude "query" in the query call.
- Refer to the interface definition in the candid file and enter.
method.interface
: Current status "null" fixed.method.args
: String, Number / If the function has arguments, set their values- The value entered is always used in cross contract calls as a fixed value.
is_target_component
specifies whether the data source is a canister in the Chainsight Platform.
is_target_component
: Whether the data source is a Chainsight Component or not- If this is not set, the target will act as if it were a Chainsight Component.
example)
version: v1
metadata:
label: sample_snapshot_indexer_icp
type: snapshot_indexer_icp
description: ''
tags: ...
datasource:
type: canister
location:
id: sample_snapshot_indexer_evm
method:
identifier: 'get_last_snapshot : () -> (record { value : text; timestamp : nat64 })'
interface: null
args: []
is_target_component: null
interval: 3600
Select this type if you want to collect data from a general web, such as a public API server.
The datasource
specifies the data location to be collected.
datasource.url
: String / Specify the URL from which to retrieve datadatasource.headers
: Object / Specifies the request headerdatasource.queries
: Object / Specify request datatype
: "static" or "dynamic"- "static" will set it statically in the subsequent
value
field in the manifest - "dynamic", where the user writes the logic to calculate parameters in the generated component code
- "static" will set it statically in the subsequent
value
: Object / Set the request body in key-value format
example)
version: v1
metadata:
label: Sample Snapshot Indexer Https
type: snapshot_indexer_https
description: ''
tags: ...
datasource:
url: https://api.coingecko.com/api/v3/simple/price
headers:
Content-Type: application/json
queries:
type: static
value:
ids: dai
vs_currencies: usd
interval: 3600
cycles: null
Event Indexer subscribes to Ethereum Contract events and stores them as data in the component.
Specify the contract to be subscribed to and its events in datasource
. The specification of chains and contracts is the same as in the Snapshot Indexer EVM.
datasource.id
: String / Contract addressdatasource.event
: Specify the target Eventidentifier
: event name / identifierinterface
: Name of the ABI file containing the interface to the above event.
datasource.network
: Network to which the contract for the specified event belongschain_id
: Number / chain_id of the target networkrpc_url
: String / rpc endpoint url to connect to the target
datasource.from
: Number / Specifies the block number to start subscribing to- Block numbers before the specified number are skipped.
datasource.contract_type
: String / Specify the contract typedatasource.batch_size
: Optional(Number) / Specify the size of the block to be subscribed at a time.
example)
version: v1
metadata:
label: Event Indexer
type: event_indexer
description: ''
tags: ...
datasource:
id: 0x6B175474E89094C44Da98b954EedeAC495271d0F
event:
identifier: Transfer
interface: ERC20.json
network:
rpc_url: https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_KEY}
chain_id: 1
from: 17660942
contract_type: ERC-20
batch_size: null
interval: 3600
cycles: null
The Algorithm Indexer subscribes to data from the Event Indexer and other Algorithm Indexers and allows them to be processed with arbitrary logic.
The datasource
determines what data source is used as input, and the output
determines the format of the data produced by the logic.
datasource
: Identification of computation source informationdatasource.principal
: Component to calldatasource.input
: Type of data to be collecteddatasource.input.name
: String / Name of typedatasource.input.fields
: Optional(Object) / Specify the field information of the type, any key name and value type.- If it is not set up, code it yourself.
datasource.method
: String / Function name to calldatasource.source_type
: String / Select the data source formatevent_indexer
... Event Indexer Componentkey_value
,key_values
... Algorithm Indexer Component, More details to follow.
- The following are similar to Event Indexer
datasource.from
,datasource.batch_size
output
: Array(Object) / Specify the format of calculation resultsoutput[i].output_type
: Specify the type of storage where the calculation results are to be storedkey_value
... Store one data in one key.key_values
... Store multiple data in one key. That is, it stores an array of data.
- The following is the same as for
datasource.input
output[i].name
,output[i].field
version: v1
metadata:
label: Algorithm Indexer
type: algorithm_indexer
description: ''
tags: ...
datasource:
principal: event_indexer_component
input:
name: Transfer
fields:
from: String
to: String
value: chainsight_cdk::core::U256
from: 17660942
method: proxy_call
source_type: event_indexer
batch_size: null
output:
- name: Account
fields:
address: String
output_type: key_value
- name: AccountBalance
fields:
address: String
balance: u64
output_type: key_values
interval: 3600
cycles: null
Relayer is a component for propagating data computed and maintained by Chainsight Platform to other blockchains.
Therefore, datasource
and interval
are the same as Snapshot Indexer ICP, and a new destination
must be specified to specify the propagation destination.
destination.network_id
: Number / chain_id of the target networkdestination.type
: Enum / Oracle Type- To send data to the destination chain, Chainsight provides several Oracle types according to a defined standard, and the developer specifies the type
destination.oracle_address
: String / Oracle addressdestination.rpc_url
: String / rpc endpoint url to call the target network
example)
version: v1
metadata:
label: sample_relayer
type: relayer
description: ''
tags: ...
datasource:
type: canister
location:
id: sample_snapshot_indexer_evm
method:
identifier: 'get_last_snapshot_value : () -> (text)'
interface: null
args: []
destination:
network_id: 80001
type: uint256
oracle_address: 0539a0EF8e5E60891fFf0958A059E049e43020d9
rpc_url: https://polygon-mumbai.g.alchemy.com/v2/<YOUR_KEY>
interval: 3600
The above description is only a minimal setup. Further customization is possible.
- To change the caller's contract interface...
destination.method_name
: identifier of the function to calldestination.interface
: ABI of the contract to be called
conversion_parameter
allows simple processing of the data to be propagatedextracted_field
: String / Specify the field to propagatedestination_type_to_convert
: String / Specify the type you want to convertexponent_of_power10
: Number / Use for digit up
example)
destination:
...
interface: IProposalSynchronizer.json
method_name: batchSynchronize
conversion_parameter:
extracted_field: value.dai.usd
destination_type_to_convert: U256
exponent_of_power10: 2
What is Oracle?
When Relayer propagates data to other blockchains, Chainsight provides Oracle Contract as the target.
It has only a state that stores the specified type in key/value format, where the key is an address constructed from the sender's Canister secret information, allowing the sender to be identified.
There is a specific code below.
horizonx-tech/chainsight-management-oracle: Oracles supported by Chainsight
Check the README in the above repository for the addresses of available oracle contracts that have been deployed. If you have a network you would like to add, please use that repository or send us an add request.
Users can define arbitrary logic using Algorithm Lens. All data sources on Chainsight can be used as inputs for the calculations.
datasource
specifies the source data source for the calculation. You can specify data sources by canister in Chainsight and their endpoints (e.g. functions). Multiple data sources can be specified.
datasource.methods
: Array / Specifies the components from which the data originates.id
: String / Specify the Component to be the data source- If in the same project, specify the "component name"
- Otherwise, specify the canister ID.
- ex:
thiw2-paaaa-aaaag-qc53a-cai
- ex:
identifier
: String / Candid format interface to call to for data source acquisitioncandid_file_path
: Optional(String) / Path to the .did file that contains the function interface for the canister that will be the data sourcefunc_name_alias
: Optional(String) / Function name generated in the logic file generated from this data source definition
example)
version: v1
metadata:
label: sample_algorithm_lens
type: algorithm_lens
description: ''
tags: ...
datasource:
methods:
- id: sample_snapshot_indexer_evm
identifier: 'get_last_snapshot_value : () -> (record { nat; text })'
candid_file_path: interfaces/sample_snapshot_indexer_evm.did
- id: sample_snapshot_indexer_icp
identifier: 'get_last_snapshot_value : () -> (nat)'
candid_file_path: interfaces/sample_snapshot_indexer_icp.did
cycles: null
"csx generate" can be run to generate code for a component module from your project manifest. The codes is generated in the following configuration.
pj_root/src
|- __interface // ABI and Candid used in the codes
|- accessors // External communication logic
|- bindings // Types for external communication
|- canisters // Core canister codes
|- logics // User-specific customization
|- Cargo.lock
L Cargo.toml
Some components can be customized by the user by editing the component-specific lib.rs
in the logics
folder.
logics
|- (component_name)
| |- src
| | L lib.rs
| L Cargo.toml
...
Snapshot Indexer HTTPS allows filtering on response data that can be retrieved from a specified destination. It can be reflected by updating the field in the rust structure that represents the response data type.
example)
use candid::{Decode, Encode};
#[derive(Debug, Clone, candid::CandidType, candid::Deserialize, serde::Serialize, chainsight_cdk_macros::StableMemoryStorable)]
pub struct SnapshotValue {
pub dai: Dai,
}
#[derive(Debug, Clone, candid::CandidType, candid::Deserialize, serde::Serialize, chainsight_cdk_macros::StableMemoryStorable)]
pub struct Dai {
pub usd: f64,
- pub usd_market_cap: f64,
- pub usd_24h_vol: f64,
- pub usd_24h_change: f64,
- pub last_updated_at: f64
}
With respect to Algorithm Lens, it is up to the user to decide how to process data from the specified data source. This logic must be implemented here.
Codes is automatically generated to retrieve data from the specified data source.
example)
#[derive(Clone, Debug, Default, candid :: CandidType, serde :: Deserialize, serde :: Serialize)]
pub struct LensValue {
pub dummy: f64,
}
pub async fn calculate(targets: Vec<String>) -> LensValue {
let _result = get_eth_1(targets.get(0usize).unwrap().clone()).await;
let _result = get_eth_2(targets.get(1usize).unwrap().clone()).await;
let _result = get_eth_3(targets.get(2usize).unwrap().clone()).await;
todo!();
}
From the template code as above, update the code as follows, taking into account your own logic. This is an example of calculating an average.
example)
#[derive(Clone, Debug, Default, candid :: CandidType, serde :: Deserialize, serde :: Serialize)]
pub struct LensValue {
pub value: f64,
}
pub async fn calculate(targets: Vec<String>) -> LensValue {
let eth_1 = get_eth_1(targets.get(0usize).unwrap().clone()).await.unwrap();
let eth_2 = get_eth_2(targets.get(1usize).unwrap().clone()).await.unwrap();
let eth_3 = get_eth_3(targets.get(2usize).unwrap().clone()).await.unwrap();
let prices = vec![eth_1, eth_2, eth_3];
LensValue { value: (prices.iter().sum::<f64>() / prices.len() as f64) }
}
Medium's official account introduces various implementation examples and the technology behind them.
The Showcase repository also publishes actual code for a variety of use case examples.