Skip to content

Commit

Permalink
feat: transaction allocate subcommand
Browse files Browse the repository at this point in the history
  • Loading branch information
hopeyen committed Jan 8, 2024
1 parent 3c0ba2a commit 5d3dd82
Show file tree
Hide file tree
Showing 7 changed files with 291 additions and 587 deletions.
384 changes: 0 additions & 384 deletions docs/cli_usage.md

This file was deleted.

103 changes: 68 additions & 35 deletions subfile-exchange/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use clap::{arg, ValueEnum};
use clap::{command, Args, Parser, Subcommand};
use ethers_core::types::U256;
use serde::{Deserialize, Serialize};
use std::fmt;

Expand Down Expand Up @@ -56,58 +57,45 @@ pub enum Role {
Wallet(WalletArgs),
}

/// Server enable payments through the staking contract,
/// assume indexer is already registered on the staking registry contract
///1. `allocate` - indexer address, Qm hash in bytes32, token amount, allocation_id, metadata: utils.hexlify(Array(32).fill(0)), allocation_id_proof
///2. `close_allocate` -allocationID: String, poi: BytesLike (0x0 32bytes)
///3. `close_allocate` and then `allocate`
/// receipt validation and storage is handled by the indexer-service framework
/// receipt redemption is handled by indexer-agent
///
/// Client payments - assume client signer is valid (should work without gateways)
///1. `deposit` - to a sender address and an amount
///2. `depositMany` - to Vec<sender address, an amount>
#[derive(Clone, Debug, Subcommand, Serialize, Deserialize)]
#[group(required = false, multiple = true)]
pub enum OnchainAction {
Allocate(AllocateArgs),
Unallocate(UnallocateArgs),
}

#[derive(Clone, Debug, Args, Serialize, Deserialize, Default)]
#[group(required = false, multiple = true)]
pub struct WalletArgs {
#[arg(
long,
value_name = "HOST",
default_value = "127.0.0.1",
env = "HOST",
help = "Subfile server host"
)]
pub host: Option<String>,
#[arg(
long,
value_name = "PORT",
default_value = "5678",
env = "PORT",
help = "Subfile server port"
)]
pub port: Option<usize>,
#[clap(
long,
value_name = "KEY",
value_parser = parse_key,
env = "PRIVATE_KEY",
hide_env_values = true,
help = "Private key to the Graphcast ID wallet (Precendence over mnemonics)",
)]
pub private_key: Option<String>,
#[clap(subcommand)]
pub action: Option<OnchainAction>,
#[clap(
long,
value_name = "KEY",
value_parser = parse_key,
env = "MNEMONIC",
hide_env_values = true,
help = "Mnemonic to the Graphcast ID wallet (first address of the wallet is used; Only one of private key or mnemonic is needed)",
help = "Mnemonic to the Indexer operator wallet (first address of the wallet is used",
)]
pub mnemonic: Option<String>,
pub mnemonic: String,
#[clap(
long,
value_name = "provider_url",
env = "PROVIDER",
help = "Blockchain provider endpoint"
)]
pub provider: String,
//TODO: chain id should be resolvable through provider
// #[clap(
// long,
// value_name = "chain_id",
// env = "CHAIN_ID",
// help = "Protocol network's Chain ID"
// )]
// pub chain_id: u64,
#[clap(
long,
value_name = "verifier",
Expand Down Expand Up @@ -318,6 +306,51 @@ pub struct PublisherArgs {
pub chain_id: String,
}

#[derive(Clone, Debug, Args, Serialize, Deserialize, Default)]
#[group(required = false, multiple = true)]
pub struct AllocateArgs {
#[clap(
long,
value_name = "tokens",
env = "TOKENS",
help = "Token amount to allocate"
)]
pub tokens: U256,
#[clap(
long,
value_name = "deployment_ipfs",
env = "DEPLOYMENT_IPFS",
help = "Deployment IPFS hash to allocate"
)]
pub deployment_ipfs: String,
#[clap(
long,
value_name = "epoch",
env = "EPOCH",
help = "Epoch field to generate unique allocation id (Should be auto-resolve through network query)"
)]
pub epoch: u64,
}

#[derive(Clone, Debug, Args, Serialize, Deserialize, Default)]
#[group(required = false, multiple = true)]
pub struct UnallocateArgs {
#[clap(
long,
value_name = "deployment_ipfs",
env = "DEPLOYMENT_IPFS",
help = "Deployment IPFS hash to unallocate"
)]
pub deployment_ipfs: String,
#[clap(
long,
value_name = "allocation_id",
env = "ALLOCATION_ID",
help = "Deployment IPFS hash to unallocate"
)]
pub allocation_id: String,
}

#[allow(unused)]
#[derive(ValueEnum, Clone, Debug, Serialize, Deserialize, Default)]
pub enum FileType {
Expand Down
37 changes: 26 additions & 11 deletions subfile-exchange/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use dotenv::dotenv;

use subfile_exchange::{
config::{Cli, Role},
config::{Cli, OnchainAction, Role},
publisher::SubfilePublisher,
subfile::ipfs::IpfsClient,
subfile_client::SubfileDownloader,
transaction_manager::TransactionManager,
};

#[tokio::main]
Expand Down Expand Up @@ -59,17 +60,31 @@ async fn main() {
"Use the provided wallet to send transactions"
);

// Server enable payments through the staking contract,
// assume indexer is already registered on the staking registry contract
//1. `allocate` - indexer address, Qm hash in bytes32, token amount, allocation_id, metadata: utils.hexlify(Array(32).fill(0)), allocation_id_proof
//2. `close_allocate` -allocationID: String, poi: BytesLike (0x0 32bytes)
//3. `close_allocate` and then `allocate`
// receipt validation and storage is handled by the indexer-service framework
// receipt redemption is handled by indexer-agent
let transaction_manager = TransactionManager::new(wallet_args)
.await
.expect("Cannot initate transaction manager");

// Client payments - assume client signer is valid (should work without gateways)
//1. `deposit` - to a sender address and an amount
//2. `depositMany` - to Vec<sender address, an amount>
match transaction_manager.args.action.clone() {
Some(OnchainAction::Allocate(allocate_args)) => {
let (allocation_id, tx_receipt) = transaction_manager
.allocate(
&allocate_args.deployment_ipfs,
allocate_args.tokens,
allocate_args.epoch,
)
.await
.unwrap();
tracing::info!(
allocation_id = tracing::field::debug(&allocation_id),
tx_receipt = tracing::field::debug(&tx_receipt),
"Allocation transaction finished"
);
}
Some(OnchainAction::Unallocate(_unallocate_args)) => {
todo!()
}
None => {}
}
}
}
}
7 changes: 1 addition & 6 deletions subfile-exchange/src/subfile_client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use crate::subfile::{
Subfile,
};
use crate::subfile_finder::{IndexerEndpoint, SubfileFinder};
use crate::transaction_manager::TransactionManager;

use crate::util::build_wallet;

use self::signer::{ReceiptSigner, TapReceipt};
Expand Down Expand Up @@ -58,11 +58,6 @@ impl SubfileDownloader {

let wallet = build_wallet(&args.mnemonic).expect("Mnemonic build wallet");
//TODO: Factor away from client, Transactions could be a separate entity
let transaction_manager = TransactionManager::new(&args.provider, wallet.clone()).await;
tracing::info!(
transaction_manager = tracing::field::debug(&transaction_manager),
"transaction_manager"
);
let signing_key = wallet.signer().to_bytes();
let secp256k1_private_key =
SecretKey::from_slice(&signing_key).expect("Private key from wallet");
Expand Down
48 changes: 30 additions & 18 deletions subfile-exchange/src/transaction_manager/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ use std::fs;
use std::str::FromStr;
use std::sync::Arc;

use crate::config::WalletArgs;
use crate::transaction_manager::staking::L2Staking;
use crate::util::build_wallet;

pub mod staking;

/// Contracts: (contract name, contract object)
pub type NetworkContracts = HashMap<String, ContractClient>;
/// Contracts: (contract name, contract address)
pub type ContractAddresses = HashMap<String, H160>;
/// Client with provider endpoint and a wallet
Expand All @@ -20,36 +22,46 @@ pub type ContractClient = SignerMiddleware<Provider<Http>, Wallet<SigningKey>>;
#[allow(dead_code)]
pub struct TransactionManager {
client: Arc<ContractClient>,
contracts: NetworkContracts,
staking_contract: L2Staking<Arc<SignerMiddleware<Provider<Http>, Wallet<SigningKey>>>>,
pub args: WalletArgs,
}

impl TransactionManager {
// Constructor to create a new instance
pub async fn new(
provider_url: &str,
wallet: Wallet<SigningKey>,
) -> Result<Self, anyhow::Error> {
let provider = Provider::<Http>::try_from(provider_url)?;
pub async fn new(args: WalletArgs) -> Result<Self, anyhow::Error> {
tracing::info!("Initialize transaction manager");
let provider = Provider::<Http>::try_from(&args.provider)?;
let chain_id = provider.get_chainid().await?;
let wallet = wallet.with_chain_id(provider.get_chainid().await.unwrap().as_u64());
let client = Arc::new(SignerMiddleware::new(provider, wallet));
let wallet = build_wallet(&args.mnemonic)
.expect("Mnemonic build wallet")
.with_chain_id(provider.get_chainid().await.unwrap().as_u64());
let client = Arc::new(SignerMiddleware::new(provider, wallet.clone()));

// Access contracts for the specified chain_id
let contract_addresses =
network_contract_addresses("addresses.json", &chain_id.to_string())?;

// Initiate contract instances
let contracts = NetworkContracts::new();
// Test reading the function
let value =
staking::controller(&client, *contract_addresses.get("L2Staking").unwrap()).await?;
tracing::debug!("test read - controller value: {:#?}", value);
Ok(TransactionManager { client, contracts })
// // Test reading the function
// let tokens = U256::from(100000);
// let _value = staking::allocate(&client, *contract_addresses.get("L2Staking").unwrap(), "QmeaPp764FjQjPB66M9ijmQKmLhwBpHQhA7dEbH2FA1j3v", token).await?;

// let value =
// staking::controller(&client, *contract_addresses.get("L2Staking").unwrap()).await?;
// tracing::debug!("test read - controller value: {:#?}", value);

let staking_addr = contract_addresses.get("L2Staking").unwrap();
let staking_contract = L2Staking::new(*staking_addr, Arc::new(client.clone()));

Ok(TransactionManager {
client,
staking_contract,
args,
})
}
}

/// Track network contract addresses given an address book in json
fn network_contract_addresses(
pub fn network_contract_addresses(
file_path: &str,
chain_id: &str,
) -> Result<ContractAddresses, anyhow::Error> {
Expand Down
Loading

0 comments on commit 5d3dd82

Please sign in to comment.