Skip to content

Commit

Permalink
support multiple networks for ARPA User CLI
Browse files Browse the repository at this point in the history
  • Loading branch information
kafeikui committed Sep 12, 2023
1 parent ac55a39 commit e634d99
Show file tree
Hide file tree
Showing 4 changed files with 699 additions and 297 deletions.
82 changes: 55 additions & 27 deletions crates/user-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ It consists of:

# Dependencies

Install `anvil` and `cast` from [foundry](https://github.com/foundry-rs/foundry#installation), then add them to `PATH` for `randcast estimate-callback-gas <consumer> <request-sender> <request-signature> <request-params>` command.
Install `anvil` and `cast` from [foundry](https://github.com/foundry-rs/foundry#installation), then add them to `PATH`.

(This is Optional, mainly for running `randcast estimate-callback-gas <consumer> <request-sender> <request-signature> <request-params>` command.)

# Usage

Expand All @@ -42,6 +44,8 @@ cargo run --bin user-shell -- -H user-shell.history

# Config

Note: Contract addresses on ETH Mainnet, Sepolia Testnet and Optimism can be found [here](https://docs.arpanetwork.io/randcast/supported-networks-and-parameters).

Configuration items in [`conf/user_config.yml`](conf/user_config.yml) are listed here:

- provider_endpoint: Config endpoint to interact with chain provider. (example: "http://127.0.0.1:8545")
Expand All @@ -54,9 +58,7 @@ Configuration items in [`conf/user_config.yml`](conf/user_config.yml) are listed

- arpa_address: Config on-chain ARPA token contract address. (example: "0x9fe46736679d2d9a65f0992f2272de9f3c7fa6e0")

```
Contract addresses on ETH Mainnet and Sepolia Testnet can be found [here](https://docs.arpanetwork.io/).
```
- adapter_deployed_block_height: Config the block height when adapter contract is deployed to accelerate the query of events. (example: 100000)

- account: Config the identity of the subscriptions you owned. There are three available account types.

Expand Down Expand Up @@ -86,7 +88,7 @@ Contract addresses on ETH Mainnet and Sepolia Testnet can be found [here](https:
- $ARPA_ACCOUNT_KEYSTORE_PASSWORD (account, keystore, password)
- $ARPA_HD_ACCOUNT_MNEMONIC (account, hdwallet, mnemonic)

- contract_transaction_retry_descriptor(Optional): Config retry strategy for contract transactions. All the time limits are in milliseconds.
- contract_transaction_retry_descriptor: Config retry strategy for contract transactions. All the time limits are in milliseconds.

- example:

Expand All @@ -98,7 +100,7 @@ Contract addresses on ETH Mainnet and Sepolia Testnet can be found [here](https:
use_jitter: true
```

- contract_view_retry_descriptor(Optional): Config retry strategy for contract views. All the time limits are in milliseconds.
- contract_view_retry_descriptor: Config retry strategy for contract views. All the time limits are in milliseconds.

- example:

Expand All @@ -110,14 +112,39 @@ Contract addresses on ETH Mainnet and Sepolia Testnet can be found [here](https:
use_jitter: true
```

```
We use exponential backoff to retry when an interaction fails. The interval will be an exponent of base multiplied by factor every time. The interval will be reset when the interaction succeeds.
```
We use exponential backoff to retry when an interaction fails. The interval will be an exponent of base multiplied by factor every time. The interval will be reset when the interaction succeeds.
A jitter is added to the interval to avoid the situation that all the tasks are polling at the same time. It will multiply a random number between 0.5 and 1.0 to the interval.
A jitter is added to the interval to avoid the situation that all the tasks are polling at the same time. It will multiply a random number between 0.5 and 1.0 to the interval.
contract_transaction_retry_descriptor: (interval sequence without jitter: 2s, 4s, 8s)
contract_view_retry_descriptor: (interval sequence without jitter: 1s, 2s, 4s, 8s, 16s)
```
contract_transaction_retry_descriptor: (interval sequence without jitter: 2s, 4s, 8s)
contract_view_retry_descriptor: (interval sequence without jitter: 1s, 2s, 4s, 8s, 16s)
```

- relayed_chains: Config chain_id, account, contract addresses, provider endpoint, adapter_deployed_block_height, contract_transaction_retry_descriptor and contract_view_retry_descriptor for all relayed chains we support.

- example:

```
relayed_chains:
- chain_id: 901
provider_endpoint: "http://127.0.0.1:9545"
adapter_address: "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707"
adapter_deployed_block_height: 0
arpa_address: "0x9fe46736679d2d9a65f0992f2272de9f3c7fa6e0"
account:
private_key: $OP_ACCOUNT_PRIVATE_KEY
contract_transaction_retry_descriptor:
base: 2
factor: 1000
max_attempts: 3
use_jitter: true
contract_view_retry_descriptor:
base: 2
factor: 500
max_attempts: 5
use_jitter: true
```

# REPL Commands

Expand Down Expand Up @@ -176,20 +203,21 @@ Send trxs to on-chain contracts
Usage: send [COMMAND]
Commands:
approve-arpa-to-staking Approve arpa to staking contract [aliases: aats]
stake Stake arpa to staking contract [aliases: s]
unstake Unstake(then freeze) arpa from staking contract and claim delegation rewards instantly after exit [aliases: u]
claim-frozen-principal Claim frozen principal from staking after unstake [aliases: cfp]
claim Claim rewards as well as frozen principal(if any) from staking [aliases: c]
claim-reward Claim rewards from staking [aliases: cr]
create-subscription Create a new subscription as owner [aliases: cs]
add-consumer Add consumer contract to subscription [aliases: ac]
fund-subscription Fund subscription with ETH [aliases: fs]
set-referral Set referral subscription id for your subscription to get referral rewards [aliases: sr]
cancel-subscription Cancel subscription and redeem ETH left to receiver address [aliases: ccs]
remove-consumer Remove consumer contract from subscription [aliases: rc]
set-callback-gas-config Set callback gas config for consumer contract [aliases: scgc]
help Print this message or the help of the given subcommand(s)
approve-arpa-to-staking Approve arpa to staking contract [aliases: aats]
stake Stake arpa to staking contract [aliases: s]
unstake Unstake(then freeze) arpa from staking contract and claim delegation rewards instantly after exit [aliases: u]
claim-frozen-principal Claim frozen principal from staking after unstake [aliases: cfp]
claim Claim rewards as well as frozen principal(if any) from staking [aliases: c]
claim-reward Claim rewards from staking [aliases: cr]
create-subscription Create a new subscription as owner [aliases: cs]
add-consumer Add consumer contract to subscription [aliases: ac]
fund-subscription Fund subscription with ETH [aliases: fs]
set-referral Set referral subscription id for your subscription to get referral rewards [aliases: sr]
cancel-subscription Cancel subscription and redeem ETH left to receiver address [aliases: ccs]
remove-consumer Remove consumer contract from subscription [aliases: rc]
set-callback-gas-config Set callback gas config for consumer contract [aliases: scgc]
set-request-confirmations Set request confirmations for consumer contract [aliases: src]
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help
Expand All @@ -209,7 +237,7 @@ Commands:
estimate-payment-amount Estimate the amount of gas used for a fulfillment of randomness in 3 times of current gas price, for calculating how much eth is needed for subscription funding [aliases: epa]
callback-gas-limit Get callback gas limit of consumer contract [aliases: cgl]
callback-max-gas-fee Get callback max gas fee of consumer contract. 0 means auto-estimating CallbackMaxGasFee as 3 times tx.gasprice of the request call, also user can set it manually by calling set-callback-gas-config [aliases: cmgf]
nonce Get nonce(counting from 1, as there was no request) of consumer contract [aliases: n]
nonces Get nonce(counting from 1, as there was no request) for a specific subscription id and consumer address [aliases: n]
last-randomness Get last randomness [aliases: lr]
pending-request-commitment Get pending commitment by request id [aliases: prc]
adapter-config Get adapter config [aliases: ac]
Expand Down
25 changes: 24 additions & 1 deletion crates/user-cli/conf/user_config.yml.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ provider_endpoint: "http://127.0.0.1:8545"
chain_id: 31337

adapter_address: "0xa513e6e4b8f2a923d98304ec87f64353c4d5c853"

adapter_deployed_block_height: 0

staking_address: "0xcf7ed3acca5a467e9e704c703e8d87f634fb0fc9"

arpa_address: "0x9fe46736679d2d9a65f0992f2272de9f3c7fa6e0"

account:
private_key: $ARPA_ACCOUNT_PRIVATE_KEY
private_key: $ACCOUNT_PRIVATE_KEY

contract_transaction_retry_descriptor:
base: 2
Expand All @@ -19,3 +23,22 @@ contract_view_retry_descriptor:
factor: 500
max_attempts: 5
use_jitter: true

relayed_chains:
- chain_id: 901
provider_endpoint: "http://127.0.0.1:9545"
adapter_address: "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707"
adapter_deployed_block_height: 0
arpa_address: "0x9fe46736679d2d9a65f0992f2272de9f3c7fa6e0"
account:
private_key: $OP_ACCOUNT_PRIVATE_KEY
contract_transaction_retry_descriptor:
base: 2
factor: 1000
max_attempts: 3
use_jitter: true
contract_view_retry_descriptor:
base: 2
factor: 500
max_attempts: 5
use_jitter: true
136 changes: 122 additions & 14 deletions crates/user-cli/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@ use thiserror::Error;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
pub provider_endpoint: String,
pub chain_id: usize,
pub adapter_address: String,
pub staking_address: String,
pub arpa_address: String,
pub account: Account,
pub contract_transaction_retry_descriptor: ExponentialBackoffRetryDescriptor,
pub contract_view_retry_descriptor: ExponentialBackoffRetryDescriptor,
provider_endpoint: String,
chain_id: u32,
adapter_address: String,
adapter_deployed_block_height: u64,
staking_address: String,
arpa_address: String,
account: Account,
contract_transaction_retry_descriptor: ExponentialBackoffRetryDescriptor,
contract_view_retry_descriptor: ExponentialBackoffRetryDescriptor,
relayed_chains: Vec<RelayedChain>,
}

impl Default for Config {
Expand All @@ -32,6 +34,7 @@ impl Default for Config {
provider_endpoint: "localhost:8545".to_string(),
chain_id: 0,
adapter_address: "0xa513e6e4b8f2a923d98304ec87f64353c4d5c853".to_string(),
adapter_deployed_block_height: 0,
staking_address: "0x5f3b5dfeb7b28cdbd7faba78963ee202a494e2a2".to_string(),
arpa_address: "0x6f769e65c14ebd1f68817f5f1dcdbd5c5d5f6e6a".to_string(),
account: Default::default(),
Expand All @@ -47,10 +50,23 @@ impl Default for Config {
max_attempts: DEFAULT_CONTRACT_VIEW_RETRY_MAX_ATTEMPTS,
use_jitter: DEFAULT_CONTRACT_VIEW_RETRY_USE_JITTER,
},
relayed_chains: vec![],
}
}
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RelayedChain {
chain_id: u32,
provider_endpoint: String,
adapter_address: String,
adapter_deployed_block_height: u64,
arpa_address: String,
account: Account,
contract_transaction_retry_descriptor: ExponentialBackoffRetryDescriptor,
contract_view_retry_descriptor: ExponentialBackoffRetryDescriptor,
}

impl Config {
pub fn load(config_path: PathBuf) -> Config {
let config_str = &read_to_string(config_path).unwrap_or_else(|e| {
Expand All @@ -63,16 +79,106 @@ impl Config {
serde_yaml::from_str(config_str).expect("Error loading configuration file")
}

pub fn adapter_address(&self) -> Address {
self.adapter_address.parse().unwrap()
pub fn main_chain_id(&self) -> u32 {
self.chain_id
}

pub fn relayed_chain_ids(&self) -> Vec<u32> {
self.relayed_chains.iter().map(|c| c.chain_id).collect()
}

pub fn provider_endpoint(&self, chain_id: u32) -> anyhow::Result<String> {
if chain_id == self.chain_id {
Ok(self.provider_endpoint.clone())
} else {
self.relayed_chains
.iter()
.find(|c| c.chain_id == chain_id)
.map(|c| c.provider_endpoint.clone())
.ok_or_else(|| ConfigError::InvalidChainId(chain_id).into())
}
}

pub fn account(&self, chain_id: u32) -> anyhow::Result<Wallet<SigningKey>> {
if chain_id == self.chain_id {
build_wallet_from_config(&self.account)
} else {
self.relayed_chains
.iter()
.find(|c| c.chain_id == chain_id)
.map(|c| build_wallet_from_config(&c.account))
.ok_or_else(|| ConfigError::InvalidChainId(chain_id))?
}
}

pub fn adapter_address(&self, chain_id: u32) -> anyhow::Result<Address> {
if chain_id == self.chain_id {
Ok(self.adapter_address.parse().unwrap())
} else {
self.relayed_chains
.iter()
.find(|c| c.chain_id == chain_id)
.map(|c| c.adapter_address.parse().unwrap())
.ok_or_else(|| ConfigError::InvalidChainId(chain_id).into())
}
}

pub fn adapter_deployed_block_height(&self, chain_id: u32) -> anyhow::Result<u64> {
if chain_id == self.chain_id {
Ok(self.adapter_deployed_block_height)
} else {
self.relayed_chains
.iter()
.find(|c| c.chain_id == chain_id)
.map(|c| c.adapter_deployed_block_height)
.ok_or_else(|| ConfigError::InvalidChainId(chain_id).into())
}
}

pub fn staking_address(&self) -> Address {
self.staking_address.parse().unwrap()
}

pub fn arpa_address(&self) -> Address {
self.arpa_address.parse().unwrap()
pub fn arpa_address(&self, chain_id: u32) -> anyhow::Result<Address> {
if chain_id == self.chain_id {
Ok(self.arpa_address.parse().unwrap())
} else {
self.relayed_chains
.iter()
.find(|c| c.chain_id == chain_id)
.map(|c| c.arpa_address.parse().unwrap())
.ok_or_else(|| ConfigError::InvalidChainId(chain_id).into())
}
}

pub fn contract_transaction_retry_descriptor(
&self,
chain_id: u32,
) -> anyhow::Result<ExponentialBackoffRetryDescriptor> {
if chain_id == self.chain_id {
Ok(self.contract_transaction_retry_descriptor)
} else {
self.relayed_chains
.iter()
.find(|c| c.chain_id == chain_id)
.map(|c| c.contract_transaction_retry_descriptor)
.ok_or_else(|| ConfigError::InvalidChainId(chain_id).into())
}
}

pub fn contract_view_retry_descriptor(
&self,
chain_id: u32,
) -> anyhow::Result<ExponentialBackoffRetryDescriptor> {
if chain_id == self.chain_id {
Ok(self.contract_view_retry_descriptor)
} else {
self.relayed_chains
.iter()
.find(|c| c.chain_id == chain_id)
.map(|c| c.contract_view_retry_descriptor)
.ok_or_else(|| ConfigError::InvalidChainId(chain_id).into())
}
}
}

Expand All @@ -97,7 +203,7 @@ pub struct HDWallet {
pub index: u32,
pub passphrase: Option<String>,
}
pub fn build_wallet_from_config(account: &Account) -> Result<Wallet<SigningKey>, ConfigError> {
pub fn build_wallet_from_config(account: &Account) -> anyhow::Result<Wallet<SigningKey>> {
if account.hdwallet.is_some() {
let mut hd = account.hdwallet.clone().unwrap();
if hd.mnemonic.starts_with('$') {
Expand Down Expand Up @@ -129,7 +235,7 @@ pub fn build_wallet_from_config(account: &Account) -> Result<Wallet<SigningKey>,
return Ok(private_key.parse::<Wallet<SigningKey>>()?);
}

Err(ConfigError::LackOfAccount)
Err(ConfigError::LackOfAccount.into())
}

#[derive(Debug, Error)]
Expand All @@ -142,4 +248,6 @@ pub enum ConfigError {
EnvVarNotExisted(#[from] VarError),
#[error(transparent)]
BuildingAccountError(#[from] WalletError),
#[error("the chain id: {0} is not supported")]
InvalidChainId(u32),
}
Loading

0 comments on commit e634d99

Please sign in to comment.