Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: update claim command #73

Merged
merged 5 commits into from
Aug 23, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,11 +282,11 @@ cargo run --bin saturn-contracts -- -U $RPC_URL --retries=10 cancel-all --actor-
#### Payout Factory Deployment
```bash
cd ./cli
cargo run --bin saturn-contracts -- -S secrets/.secret -U https://api.hyperspace.node.glif.io/rpc/v1 --retries=10 deploy
cargo run --bin saturn-contracts -- -S secrets/.secret -U https://api.calibration.node.glif.io/rpc/v1 --retries=10 deploy
AmeanAsad marked this conversation as resolved.
Show resolved Hide resolved

```

> **Note:** The `--retries` parameter sets a number of times to poll a pending transaction before considering it as having failed. Because of the differences in block times between Filecoin / Hyperspace and Ethereum, `ethers-rs` can sometimes timeout prematurely _before_ a transaction has truly failed or succeeded (`ethers-rs` has been built with Ethereum in mind). `--retries` has a default value of 10, which empirically we have found to result in successful transactions.
> **Note:** The `--retries` parameter sets a number of times to poll a pending transaction before considering it as having failed. Because of the differences in block times between Filecoin / calibration and Ethereum, `ethers-rs` can sometimes timeout prematurely _before_ a transaction has truly failed or succeeded (`ethers-rs` has been built with Ethereum in mind). `--retries` has a default value of 10, which empirically we have found to result in successful transactions.


#### Payment Splitter Deployments
Expand All @@ -295,7 +295,7 @@ Make sure your deployed factory has sufficient funds for the subsequent payouts.

```bash
cd ./cli
cargo run --bin saturn-contracts -- -S secrets/.secret -U https://api.hyperspace.node.glif.io/rpc/v1 --retries=10 fund -F $FACTORY_ADDRESS -A $PAYOUT_AMOUNT
cargo run --bin saturn-contracts -- -S secrets/.secret -U https://api.calibration.node.glif.io/rpc/v1 --retries=10 fund -F $FACTORY_ADDRESS -A $PAYOUT_AMOUNT
```

##### Using a CSV file:
Expand All @@ -314,7 +314,7 @@ t410f4bmm756u5kft2czgqll4oybvtch3jj5v64yjeya,1
Now run:
```bash
cd ./cli
cargo run --bin saturn-contracts -- -S secrets/.secret -U https://api.hyperspace.node.glif.io/rpc/v1 --retries=10 new-payout -F $FACTORY_ADDRESS -P ./secrets/payouts.csv
cargo run --bin saturn-contracts -- -S secrets/.secret -U https://api.calibration.node.glif.io/rpc/v1 --retries=10 new-payout -F $FACTORY_ADDRESS -P ./secrets/payouts.csv
```

##### Using a Database:
Expand All @@ -333,20 +333,20 @@ To deploy a new `PaymentSplitter` from a deployed `PayoutFactory` contract using
Run:
```bash
cd ./cli
cargo run --bin saturn-contracts -- -S secrets/.secret -U https://api.hyperspace.node.glif.io/rpc/v1 --retries=10 new-payout -F $FACTORY_ADDRESS --db-deploy
cargo run --bin saturn-contracts -- -S secrets/.secret -U https://api.calibration.node.glif.io/rpc/v1 --retries=10 new-payout -F $FACTORY_ADDRESS --db-deploy
```
#### Claiming Earnings
You can then claim funds for a specific payee using the cli:
```bash
cd ./cli
cargo run --bin saturn-contracts -- -S secrets/.secret -U https://api.hyperspace.node.glif.io/rpc/v1 --retries=10 claim -F $FACTORY_ADDRESS -A $CLAIM_ADDRESS
cargo run --bin saturn-contracts -- -S secrets/.secret -U https://api.calibration.node.glif.io/rpc/v1 --retries=10 claim -F $FACTORY_ADDRESS -A $CLAIM_ADDRESS
```
#### Write PayoutFactory Abi
To write the `PayoutFactory` abi to a JSON file, you can use the `write-abi` command as such:

```bash
cd ./cli
cargo run --bin saturn-contracts -- -S secrets/.secret -U https://api.hyperspace.node.glif.io/rpc/v1 --retries=10 write-abi -P $ABI_PATH
cargo run --bin saturn-contracts -- -S secrets/.secret -U https://api.calibration.node.glif.io/rpc/v1 --retries=10 write-abi -P $ABI_PATH
```

#### Write Payout CSVs:
Expand All @@ -360,7 +360,7 @@ To run the command:

```bash
cd ./cli
cargo run --bin saturn-contracts -- -S secrets/.secret -U https://api.hyperspace.node.glif.io/rpc/v1 --retries=10 generate-monthly-payout -D 2023-01 -F $FILECOIN_FACTORY_ADRESS
cargo run --bin saturn-contracts -- -S secrets/.secret -U https://api.calibration.node.glif.io/rpc/v1 --retries=10 generate-monthly-payout -D 2023-01 -F $FILECOIN_FACTORY_ADRESS
```
### Hardhat Integration

Expand Down
106 changes: 57 additions & 49 deletions cli/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ use std::path::PathBuf;
use std::sync::Arc;

use crate::utils::{
approve_payout, cancel_payout, claim_earnings, claim_earnings_filecoin_signing,
deploy_factory_contract, fund_factory_contract, generate_monthly_payout,
get_pending_transaction_multisig, get_signing_method_and_address, grant_admin,
inspect_earnings, inspect_multisig, new_payout, propose_payout, SigningOptions,
approve_payout, cancel_payout, deploy_factory_contract, fund_factory_contract,
generate_monthly_payout, get_pending_transaction_multisig, get_signing_method_and_address,
get_unreleased_payout_contracts, grant_admin, inspect_earnings, inspect_multisig, new_payout,
propose_payout, release_selected_payouts, release_selected_payouts_filecoin_signing,
SigningOptions,
};

#[allow(missing_docs)]
Expand Down Expand Up @@ -135,55 +136,65 @@ impl Cli {
Commands::Claim {
factory_addr,
addr_to_claim,
offset,
method,
} => match method {
Some(option) => {
let (signing_method, signer_address) =
get_signing_method_and_address(option, self.ledger_account.clone())
.await
.unwrap();
} => {
let releasable_contract_indices = get_unreleased_payout_contracts(
&factory_addr,
&addr_to_claim,
&self.rpc_url,
&provider.clone(),
)
.await
.unwrap();
match method {
Some(option) => {
let (signing_method, signer_address) =
get_signing_method_and_address(option, self.ledger_account.clone())
.await
.unwrap();

claim_earnings_filecoin_signing(
&provider.clone(),
factory_addr,
addr_to_claim,
&signing_method,
&signer_address,
&self.rpc_url,
)
.await?
}
None => {
let factory_eth_addr =
filecoin_to_eth_address(&factory_addr, &self.rpc_url).await?;
if self.secret.is_some() {
let client = get_wallet(self.secret.unwrap(), provider).await?;
claim_earnings(
client.clone(),
self.retries,
gas_price,
ethers::types::U256::from(*offset),
&factory_eth_addr,
addr_to_claim,
)
.await?;
} else {
let client =
get_ledger_signing_provider(provider, chain_id.as_u64()).await?;
let client = Arc::new(client);
claim_earnings(
client.clone(),
self.retries,
gas_price,
ethers::types::U256::from(*offset),
release_selected_payouts_filecoin_signing(
&provider.clone(),
factory_addr,
addr_to_claim,
releasable_contract_indices.clone(),
&signing_method,
&signer_address,
&self.rpc_url,
)
.await?;
.await?
}
None => {
let factory_eth_addr =
filecoin_to_eth_address(&factory_addr, &self.rpc_url).await?;
if self.secret.is_some() {
let client = get_wallet(self.secret.unwrap(), provider).await?;
release_selected_payouts(
client.clone(),
self.retries,
gas_price,
&factory_eth_addr,
addr_to_claim,
releasable_contract_indices.clone(),
)
.await?;
} else {
let client =
get_ledger_signing_provider(provider, chain_id.as_u64()).await?;
let client = Arc::new(client);
release_selected_payouts(
client.clone(),
self.retries,
gas_price,
&factory_eth_addr,
addr_to_claim,
releasable_contract_indices.clone(),
)
.await?;
}
}
}
},
}
Commands::Fund {
factory_addr,
amount,
Expand Down Expand Up @@ -385,9 +396,6 @@ pub enum Commands {
// Address to claim for
#[arg(short = 'A', long)]
addr_to_claim: String,
// Index from which to start claiming
#[arg(short = 'O', long, default_value = "0")]
offset: usize,
#[arg(short = 'M', long, required = false)]
method: Option<SigningOptions>,
},
Expand Down
5 changes: 3 additions & 2 deletions cli/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,12 @@ pub async fn get_payment_records(date: &str) -> Result<PayoutRecords, Error> {
"
SELECT
fil_wallet_address, sum(fil_earned)
FROM payment_aggregation
FROM payments
AmeanAsad marked this conversation as resolved.
Show resolved Hide resolved
INNER JOIN
nodes on payment_aggregation.node_id = nodes.id
nodes on payments.node_id = nodes.id
AND core = false
AND banned_at is NULL
AND status = 'paid'
WHERE
date_trunc('month',time_stamp)::date =
date_trunc('month', $1::TIMESTAMP WITH TIME ZONE)::date
Expand Down
131 changes: 130 additions & 1 deletion cli/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ use crate::db::{get_payment_records, PayoutRecords};

pub static ATTO_FIL: Lazy<f64> = Lazy::new(|| 10_f64.powf(18.0));

pub const MAX_PAYEES_PER_PAYOUT: usize = 700;
pub const MAX_PAYEES_PER_PAYOUT: usize = 305;

// MaxFee is set to zero when using MpoolPush
const MAX_FEE: &str = "0";
Expand Down Expand Up @@ -462,6 +462,135 @@ pub async fn cancel_payout(
Ok(())
}

pub async fn get_unreleased_payout_contracts(
factory_address: &str,
release_address: &str,
rpc_url: &str,
provider: &Provider<Http>,
) -> Result<Vec<U256>, Box<dyn std::error::Error>> {
let factory_eth_address = filecoin_to_eth_address(factory_address, rpc_url)
.await
.unwrap();
let contract_addr = Address::from_str(factory_eth_address.as_str()).unwrap();

let client = Arc::new(provider.clone());
let factory = PayoutFactory::new(contract_addr, client);

let release_addr = FilAddress {
data: check_address_string(release_address).unwrap().bytes.into(),
};

let contract_call_result = factory.releasable_per_contract(release_addr).call().await;

let res = match contract_call_result {
Ok(result) => {
let contracts = result.0;
let releasable_payouts = result.1;
assert!(contracts.len() == releasable_payouts.len());

releasable_payouts
.iter()
.enumerate()
.filter(|&(_, &value)| value != 0.into())
.map(|(index, _)| index.into())
.collect()
}
Err(error) => {
panic!(
"Error extracting payout info for {}: {}",
release_address, error
)
}
};

Ok(res)
}

pub async fn release_selected_payouts<S: ::ethers::providers::Middleware + 'static>(
client: Arc<S>,
retries: usize,
gas_price: U256,
factory_addr: &str,
addr_to_claim: &str,
selected_contract_indices: Vec<U256>,
) -> Result<(), Box<dyn std::error::Error>> {
let addr = Address::from_str(factory_addr)?;
let factory = PayoutFactory::new(addr, client.clone());
let addr_to_claim = check_address_string(addr_to_claim)?;
let claim_addr = FilAddress {
data: addr_to_claim.bytes.into(),
};
let mut claim_tx = factory.release_select(claim_addr, selected_contract_indices);
let tx = claim_tx.tx.clone();
set_tx_gas(
&mut claim_tx.tx,
client.estimate_gas(&tx, None).await?,
gas_price,
);

info!("estimated claim gas cost {:#?}", claim_tx.tx.gas().unwrap());

send_tx(&claim_tx.tx, client, retries).await?;
Ok(())
}

pub async fn release_selected_payouts_filecoin_signing(
provider: &Provider<Http>,
factory_addr: &str,
release_address: &str,
selected_contract_indices: Vec<U256>,
signing_method: &SignatureMethod,
signing_address: &str,
rpc_url: &str,
) -> Result<(), Box<dyn Error>> {
let factory_eth_addr = filecoin_to_eth_address(factory_addr, rpc_url)
.await
.unwrap();

let addr = Address::from_str(factory_eth_addr.as_str()).unwrap();
let release_addr = FilAddress {
data: check_address_string(release_address).unwrap().bytes.into(),
};

let client = Arc::new(provider.clone());
let factory = PayoutFactory::new(addr, client);

let call_bytes = factory
.release_select(release_addr, selected_contract_indices)
.calldata()
.unwrap()
.to_vec();

let num_bytes = call_bytes.len().to_be_bytes();
let num_bytes = num_bytes
.iter()
.filter(|x| **x != 0)
.map(|x| x.clone())
.collect::<Vec<u8>>();
let mut params = hex::decode(PARAMS_CBOR_HEADER[num_bytes.len() - 1])?;
params.extend(num_bytes);
params.extend(call_bytes.clone());

let nonce = get_nonce(&signing_address, provider.clone()).await;

let mut message = Message {
version: 0,
to: FilecoinAddress::from_str(&factory_addr)?,
from: FilecoinAddress::from_str(&signing_address)?,
sequence: nonce,
value: TokenAmount::from_atto(BigInt::from_str("0")?),
gas_limit: 0,
gas_fee_cap: TokenAmount::from_atto(BigInt::from_str("0")?),
gas_premium: TokenAmount::from_atto(BigInt::from_str("0")?),
method_num: 3844450837, // InvokeContract is method no 3844450837
params: RawBytes::new(params),
};
let signed_message: MessageTxAPI = sign_message(provider, signing_method, &mut message).await?;

push_mpool_message(provider, signed_message).await?;
Ok(())
}

pub async fn claim_earnings_filecoin_signing(
provider: &Provider<Http>,
factory_addr: &str,
Expand Down
Loading