Skip to content

Commit

Permalink
Added distributor contract for more robust testing
Browse files Browse the repository at this point in the history
  • Loading branch information
scx1332 authored Mar 29, 2024
1 parent 6ebed67 commit 058d89d
Show file tree
Hide file tree
Showing 13 changed files with 417 additions and 20 deletions.
53 changes: 53 additions & 0 deletions .github/workflows/distribute.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: Distribute tests

on:
push:
workflow_dispatch:
schedule:
- cron: "42 3 * * *"

jobs:
test_deposit:
name: Test Distribute
timeout-minutes: 20

runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v3

- name: Cache dependencies
uses: Swatinem/rust-cache@v2
with:
shared-key: "dev-build-cache"

- name: Build
run: |
cargo build
cp target/debug/erc20_processor /usr/local/bin/erc20_processor
[ $(which erc20_processor) == "/usr/local/bin/erc20_processor" ]
- name: Generate ethereum accounts
run: |
erc20_processor generate-key -n 5 > .env
cat .env | grep ETH_ADDRESS | sed "s/#\s//g" | sed "s/:\s/=/g" > $GITHUB_ENV
- name: Distribute ETH
run: |
erc20_processor distribute --amounts "0.0001;0.0001;0.0001;0.0001;0.0001" --recipients "$ETH_ADDRESS_0;$ETH_ADDRESS_1;$ETH_ADDRESS_2;$ETH_ADDRESS_3;$ETH_ADDRESS_4"
erc20_processor run
env:
ETH_PRIVATE_KEYS: ${{ secrets.HOLESKY_FUND_ENV }}

- name: Transfer all left ETH tokens
run: |
set -x
erc20_processor show-config > config.toml.tmp
sed 's/^max-fee-per-gas = "20"$/max-fee-per-gas = "1.1"/' config.toml.tmp > config-payments.toml
erc20_processor transfer --account-no 0 --recipient 0x0079dce233830c7b0cd41116214e17b93c64e030 --token eth --all
erc20_processor transfer --account-no 1 --recipient 0x0079dce233830c7b0cd41116214e17b93c64e030 --token eth --all
erc20_processor transfer --account-no 2 --recipient 0x0079dce233830c7b0cd41116214e17b93c64e030 --token eth --all
erc20_processor transfer --account-no 3 --recipient 0x0079dce233830c7b0cd41116214e17b93c64e030 --token eth --all
erc20_processor transfer --account-no 4 --recipient 0x0079dce233830c7b0cd41116214e17b93c64e030 --token eth --all
erc20_processor run
1 change: 1 addition & 0 deletions crates/erc20_payment_lib/config-payments.toml
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ token = { address = "0x8888888815bf4DB87e57B609A50f938311EEd068", symbol = "tGLM
multi-contract = { address = "0xAaAAAaA00E1841A63342db7188abA84BDeE236c7", max-at-once = 10 }
mint-contract = { address = "0xFACe100969FF47EB58d2CF603321B581A84bcEaC", max-glm-allowed = 400 }
lock-contract = { address = "0xfe1B27Bac0e3Ad39d55C9459ae59894De847dcbf" }
distributor-contract = { address = "0xb7Fb99e86f93dc3047A12932052236d853065173" }
faucet-client = { max-eth-allowed = 0.009, faucet-srv = "_holesky-faucet._tcp", faucet-host = "faucet.testnet.golem.network", faucet-lookup-domain = "dev.golem.network", faucet-srv-port = 4002 }
confirmation-blocks = 0
block-explorer-url = "https://holesky.etherscan.io"
Expand Down
69 changes: 69 additions & 0 deletions crates/erc20_payment_lib/contracts/distributor.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
[
{
"inputs": [
{
"internalType": "bytes",
"name": "addrs",
"type": "bytes"
},
{
"internalType": "uint256[]",
"name": "values",
"type": "uint256[]"
}
],
"name": "distribute",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes",
"name": "addrs",
"type": "bytes"
}
],
"name": "distributeEqual",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes",
"name": "addrs",
"type": "bytes"
},
{
"internalType": "uint32[]",
"name": "values",
"type": "uint32[]"
}
],
"name": "distributeEther",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes",
"name": "addrs",
"type": "bytes"
},
{
"internalType": "uint64[]",
"name": "values",
"type": "uint64[]"
}
],
"name": "distributeGwei",
"outputs": [],
"stateMutability": "payable",
"type": "function"
}
]
27 changes: 17 additions & 10 deletions crates/erc20_payment_lib/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use serde::Deserialize;
use serde::{Deserialize, Serialize};
use std::collections::btree_map::BTreeMap as Map;

use rust_decimal::Decimal;
Expand Down Expand Up @@ -46,7 +46,7 @@ impl AdditionalOptions {
}
}

#[derive(Deserialize, Debug, Clone)]
#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct Engine {
pub process_interval: u64,
Expand All @@ -63,33 +63,39 @@ pub struct Engine {
pub ignore_deadlines: bool,
}

#[derive(Deserialize, Debug, Clone)]
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct Config {
pub chain: Map<String, Chain>,
pub engine: Engine,
}

#[derive(Deserialize, Debug, Clone)]
#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct MultiContractSettings {
pub address: Address,
pub max_at_once: usize,
}

#[derive(Deserialize, Debug, Clone)]
#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct MintContractSettings {
pub address: Address,
pub max_glm_allowed: Decimal,
}

#[derive(Deserialize, Debug, Clone)]
#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct LockContractSettings {
pub address: Address,
}

#[derive(Deserialize, Debug, Clone)]
#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct DistributorContractSettings {
pub address: Address,
}

#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct FaucetClientSettings {
pub max_eth_allowed: Decimal,
Expand All @@ -99,7 +105,7 @@ pub struct FaucetClientSettings {
pub faucet_lookup_domain: String,
}

#[derive(Deserialize, Debug, Clone)]
#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct RpcSettings {
pub names: Option<String>,
Expand All @@ -115,7 +121,7 @@ pub struct RpcSettings {
pub max_consecutive_errors: Option<u64>,
}

#[derive(Deserialize, Debug, Clone)]
#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct Chain {
pub chain_name: String,
Expand All @@ -128,6 +134,7 @@ pub struct Chain {
pub multi_contract: Option<MultiContractSettings>,
pub mint_contract: Option<MintContractSettings>,
pub lock_contract: Option<LockContractSettings>,
pub distributor_contract: Option<DistributorContractSettings>,
pub faucet_client: Option<FaucetClientSettings>,
pub transaction_timeout: u64,
pub confirmation_blocks: u64,
Expand All @@ -138,7 +145,7 @@ pub struct Chain {
pub external_source_check_interval: Option<u64>,
}

#[derive(Deserialize, Debug, Clone)]
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct Token {
pub symbol: String,
pub address: Address,
Expand Down
25 changes: 24 additions & 1 deletion crates/erc20_payment_lib/src/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use web3::contract::tokens::Tokenize;
use web3::contract::Contract;
use web3::transports::Http;
use web3::types::{Address, U256};
use web3::{Transport, Web3};
use web3::{ethabi, Transport, Web3};

// todo remove DUMMY_RPC_PROVIDER and use ABI instead
// todo change to once_cell
Expand All @@ -26,6 +26,8 @@ lazy_static! {
};
pub static ref LOCK_CONTRACT_TEMPLATE: Contract<Http> =
prepare_contract_template(include_bytes!("../contracts/lock_payments.json")).unwrap();
pub static ref DISTRIBUTOR_CONTRACT_TEMPLATE: Contract<Http> =
prepare_contract_template(include_bytes!("../contracts/distributor.json")).unwrap();
}

pub fn prepare_contract_template(json_abi: &[u8]) -> Result<Contract<Http>, PaymentError> {
Expand Down Expand Up @@ -72,6 +74,27 @@ pub fn encode_erc20_allowance(
contract_encode(&ERC20_CONTRACT_TEMPLATE, "allowance", (owner, spender))
}

pub fn encode_distribute(
recipients: &[Address],
amounts: &[U256],
) -> Result<Vec<u8>, web3::ethabi::Error> {
if recipients.len() != amounts.len() {
return Err(web3::ethabi::Error::InvalidData);
}
let mut bytes = Vec::with_capacity(recipients.len() * 20);
for recipient in recipients {
bytes.extend_from_slice(recipient.as_bytes());
}
// convert to abi encoded bytes
let bytes = ethabi::Bytes::from(bytes);

contract_encode(
&DISTRIBUTOR_CONTRACT_TEMPLATE,
"distribute",
(bytes, amounts.to_vec()),
)
}

pub fn encode_faucet_create() -> Result<Vec<u8>, web3::ethabi::Error> {
contract_encode(&FAUCET_CONTRACT_TEMPLATE, "create", ())
}
Expand Down
103 changes: 101 additions & 2 deletions crates/erc20_payment_lib/src/runtime.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::signer::{Signer, SignerAccount};
use crate::transaction::{
create_create_deposit, create_faucet_mint, create_terminate_deposit, create_token_transfer,
find_receipt_extended, FindReceiptParseResult,
create_create_deposit, create_distribute_transaction, create_faucet_mint,
create_terminate_deposit, create_token_transfer, find_receipt_extended, FindReceiptParseResult,
};
use crate::{err_custom_create, err_from};
use erc20_payment_lib_common::create_sqlite_connection;
Expand Down Expand Up @@ -951,6 +951,32 @@ impl PaymentRuntime {
Ok(())
}

pub async fn distribute_gas(
&self,
chain_name: &str,
from: Address,
) -> Result<(), PaymentError> {
let chain_cfg = self.config.chain.get(chain_name).ok_or(err_custom_create!(
"Chain {} not found in config file",
chain_name
))?;
let golem_address = chain_cfg.token.address;
let web3 = self.setup.get_provider(chain_cfg.chain_id)?;

let res = mint_golem_token(
web3,
&self.conn,
chain_cfg.chain_id as u64,
from,
golem_address,
chain_cfg.mint_contract.clone().map(|c| c.address),
false,
)
.await;
self.wake.notify_one();
res
}

pub async fn mint_golem_token(
&self,
chain_name: &str,
Expand Down Expand Up @@ -1038,6 +1064,79 @@ impl VerifyTransactionResult {
}
}

#[allow(clippy::too_many_arguments)]
pub async fn distribute_gas(
web3: Arc<Web3RpcPool>,
conn: &SqlitePool,
chain_id: u64,
from: Address,
distribute_contract_address: Option<Address>,
skip_balance_check: bool,
recipients: &[Address],
amounts: &[rust_decimal::Decimal],
) -> Result<(), PaymentError> {
let distribute_contract_address =
if let Some(distribute_contract_address) = distribute_contract_address {
distribute_contract_address
} else {
return Err(err_custom_create!(
"Distribute contract address unknown. If not sure try on holesky network"
));
};

if recipients.len() != amounts.len() {
return Err(err_custom_create!(
"recipients and amounts must have the same length"
));
}

let mut amounts_u256: Vec<U256> = Vec::with_capacity(amounts.len());

let mut sum_u256 = U256::zero();
for amount in amounts {
let amount = amount
.to_u256_from_eth()
.map_err(|err| err_custom_create!("Invalid amount: {} - {}", amount, err))?;
amounts_u256.push(amount);
sum_u256 += amount;
}

if !skip_balance_check {
//todo check if we have enough gas + token to distribute
let get_eth_balance = web3
.clone()
.eth_balance(from, None)
.await
.map_err(err_from!())?
.to_eth_saturate();

if get_eth_balance < Decimal::from_f64(0.000001).unwrap() {
return Err(err_custom_create!(
"You need at least 0.000001 ETH to continue. You have {} ETH on network with chain id: {} and account {:#x} ",
get_eth_balance,
chain_id,
from
));
}
}

let distribute_tx = create_distribute_transaction(
from,
distribute_contract_address,
chain_id,
None,
recipients,
&amounts_u256,
)?;
let distribute_tx = insert_tx(conn, &distribute_tx).await.map_err(err_from!())?;

log::info!(
"Distribute transaction added to queue: {}",
distribute_tx.id
);
Ok(())
}

pub async fn mint_golem_token(
web3: Arc<Web3RpcPool>,
conn: &SqlitePool,
Expand Down
Loading

0 comments on commit 058d89d

Please sign in to comment.