Skip to content

Commit

Permalink
Multi-network support in ERC20 driver
Browse files Browse the repository at this point in the history
  • Loading branch information
Wiezzel committed Jan 22, 2021
1 parent eb63d2f commit 1d8733e
Show file tree
Hide file tree
Showing 15 changed files with 388 additions and 220 deletions.
12 changes: 5 additions & 7 deletions .env-template
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,13 @@ YAGNA_DATADIR="."

#ACCOUNT_LIST="${YAGNA_DATADIR}/accounts.json"

## NGNT Driver.
## ERC20 Driver.

# Ethereum chain: rinkeby or mainnet
#CHAIN=rinkeby
#ETH_FAUCET_ADDRESS=http://faucet.testnet.golem.network:4000/donate
#GETH_ADDRESS=http://1.geth.testnet.golem.network:55555
#NGNT_CONTRACT_ADDRESS=0xd94e3DC39d4Cad1DAd634e7eb585A57A19dC7EFE
#NGNT_FAUCET_CONTRACT_ADDRESS=0x59259943616265A03d775145a2eC371732E2B06C
#REQUIRED_CONFIRMATIONS=5
#ERC20_RINKEBY_GETH_ADDR=http://1.geth.testnet.golem.network:55555
#ERC20_MAINNET_GETH_ADDR=https://geth.golem.network:55555
#ERC20_RINKEBY_REQUIRED_CONFIRMATIONS=1
#ERC20_MAINNET_REQUIRED_CONFIRMATIONS=5

## ZkSync driver

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
-- HACK: All this code below is just to drop column network from table gnt_driver_payment

PRAGMA foreign_keys=off;

CREATE TABLE gnt_driver_payment_tmp
(
order_id VARCHAR(50) NOT NULL PRIMARY KEY,
-- U256 in big endian hex
amount VARCHAR(64) NOT NULL,
-- U256 in big endian hex
gas VARCHAR(64) NOT NULL,
sender VARCHAR(40) NOT NULL,
recipient VARCHAR(40) NOT NULL,
payment_due_date DATETIME NOT NULL,
status INTEGER NOT NULL,
tx_id VARCHAR(128),
FOREIGN KEY(tx_id) REFERENCES gnt_driver_transaction (tx_id),
FOREIGN KEY(status) REFERENCES gnt_driver_payment_status (status_id)
);

INSERT INTO gnt_driver_payment_tmp(order_id, amount, gas, sender, recipient, payment_due_date, status, tx_id)
SELECT order_id, amount, gas, sender, recipient, payment_due_date, status, tx_id FROM gnt_driver_payment;


DROP TABLE gnt_driver_payment;

ALTER TABLE gnt_driver_payment_tmp RENAME TO gnt_driver_payment;

PRAGMA foreign_keys=on;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE gnt_driver_payment ADD COLUMN network INTEGER NOT NULL DEFAULT 4; -- 4 is rinkeby's network ID
8 changes: 7 additions & 1 deletion core/payment-driver/gnt/src/dao/payment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::utils::PAYMENT_STATUS_OK;
use ya_persistence::executor::{do_with_transaction, readonly_transaction, AsDao, PoolType};

use crate::dao::DbResult;
use crate::networks::Network;

#[allow(unused)]
pub struct PaymentDao<'c> {
Expand All @@ -20,10 +21,15 @@ impl<'c> AsDao<'c> for PaymentDao<'c> {
}

impl<'c> PaymentDao<'c> {
pub async fn get_pending_payments(&self, address: String) -> DbResult<Vec<PaymentEntity>> {
pub async fn get_pending_payments(
&self,
address: String,
network: Network,
) -> DbResult<Vec<PaymentEntity>> {
readonly_transaction(self.pool, move |conn| {
let payments: Vec<PaymentEntity> = dsl::gnt_driver_payment
.filter(dsl::sender.eq(address))
.filter(dsl::network.eq(network))
.filter(dsl::status.eq(crate::utils::PAYMENT_STATUS_NOT_YET))
.order(dsl::payment_due_date.asc())
.load(conn)?;
Expand Down
18 changes: 11 additions & 7 deletions core/payment-driver/gnt/src/gnt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ pub mod ethereum;
pub mod faucet;
pub mod sender;

use crate::{
GNTDriverError, GNTDriverResult, DEFAULT_NETWORK, DEFAULT_PLATFORM, DEFAULT_TOKEN, DRIVER_NAME,
};
use crate::{GNTDriverError, GNTDriverResult, DRIVER_NAME};
use bigdecimal::BigDecimal;
use std::future::Future;
use std::pin::Pin;
Expand All @@ -20,12 +18,13 @@ pub(crate) async fn notify_payment(
amount: BigDecimal,
sender: String,
recipient: String,
platform: String,
order_ids: Vec<String>,
confirmation: PaymentConfirmation,
) -> GNTDriverResult<()> {
let msg = payment::NotifyPayment {
driver: DRIVER_NAME.to_string(),
platform: DEFAULT_PLATFORM.to_string(), // TODO: Implement multi-network support
platform,
amount,
sender,
recipient,
Expand All @@ -41,13 +40,18 @@ pub(crate) async fn notify_payment(
.map_err(|e| GNTDriverError::LibraryError(e.to_string()))
}

pub(crate) async fn register_account(address: String, mode: AccountMode) -> GNTDriverResult<()> {
pub(crate) async fn register_account(
address: String,
network: String,
token: String,
mode: AccountMode,
) -> GNTDriverResult<()> {
log::info!("Register account: {}, mode: {:?}", address, mode);
let msg = payment::RegisterAccount {
address,
driver: DRIVER_NAME.to_string(),
network: DEFAULT_NETWORK.to_string(), // TODO: Implement multi-network support
token: DEFAULT_TOKEN.to_string(), // TODO: Implement multi-network support
network,
token,
mode,
};
bus::service(payment::BUS_ID)
Expand Down
94 changes: 28 additions & 66 deletions core/payment-driver/gnt/src/gnt/config.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use crate::gnt::ethereum::Chain;
use crate::{utils, GNTDriverError, GNTDriverResult};
use ethereum_types::{Address, H160};

use ethereum_types::Address;
use lazy_static::lazy_static;
use std::env;

use crate::utils;

pub(crate) const MAX_TESTNET_BALANCE: &str = "1000";

pub(crate) const ETH_TX_SUCCESS: u64 = 1;
Expand All @@ -13,74 +13,36 @@ pub(crate) const TX_LOG_TOPICS_LENGTH: usize = 3;
pub(crate) const TRANSFER_CANONICAL_SIGNATURE: &str =
"ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";

pub(crate) const NGNT_CONTRACT_ADDRESS_ENV_VAR: &str = "NGNT_CONTRACT_ADDRESS";
pub(crate) const NGNT_FAUCET_CONTRACT_ADDRESS_ENV_VAR: &str = "NGNT_FAUCET_CONTRACT_ADDRESS";
pub(crate) const REQUIRED_CONFIRMATIONS_ENV_VAR: &str = "REQUIRED_CONFIRMATIONS";

#[derive(Clone, Copy, Debug)]
pub struct EnvConfiguration {
pub gnt_contract_address: Address,
pub gnt_faucet_address: Option<Address>,
pub required_confirmations: u64,
}

pub const CFG_TESTNET: EnvConfiguration = EnvConfiguration {
gnt_contract_address: H160([
0xd9, 0x4e, 0x3D, 0xC3, 0x9d, 0x4C, 0xad, 0x1D, 0xAd, 0x63, 0x4e, 0x7e, 0xb5, 0x85, 0xA5,
0x7A, 0x19, 0xdC, 0x7E, 0xFE,
]),
gnt_faucet_address: Some(H160([
0x59, 0x25, 0x99, 0x43, 0x61, 0x62, 0x65, 0xA0, 0x3d, 0x77, 0x51, 0x45, 0xa2, 0xeC, 0x37,
0x17, 0x32, 0xE2, 0xB0, 0x6C,
])),
required_confirmations: 1,
};

pub const CFG_MAINNET: EnvConfiguration = EnvConfiguration {
gnt_contract_address: H160([
0x7D, 0xD9, 0xc5, 0xCb, 0xa0, 0x5E, 0x15, 0x1C, 0x89, 0x5F, 0xDe, 0x1C, 0xF3, 0x55, 0xC9,
0xA1, 0xD5, 0xDA, 0x64, 0x29,
]),
gnt_faucet_address: None,
required_confirmations: 5,
};

impl EnvConfiguration {
pub fn from_env(chain: Chain) -> GNTDriverResult<Self> {
let mut base = match chain {
Chain::Rinkeby => CFG_TESTNET,
Chain::Mainnet => CFG_MAINNET,
};
if let Some(gnt_contract_address) = env::var(NGNT_CONTRACT_ADDRESS_ENV_VAR).ok() {
base.gnt_contract_address = utils::str_to_addr(&gnt_contract_address)?;
}
if let Some(gnt_faucet_address) = env::var(NGNT_FAUCET_CONTRACT_ADDRESS_ENV_VAR).ok() {
base.gnt_faucet_address = Some(utils::str_to_addr(&gnt_faucet_address)?);
lazy_static! {
pub static ref RINKEBY_CONFIG: EnvConfiguration = EnvConfiguration {
gnt_contract_address: utils::str_to_addr("0xd94e3DC39d4Cad1DAd634e7eb585A57A19dC7EFE")
.unwrap(),
gnt_faucet_address: Some(
utils::str_to_addr("0x59259943616265A03d775145a2eC371732E2B06C").unwrap()
),
required_confirmations: {
match env::var("ERC20_RINKEBY_REQUIRED_CONFIRMATIONS").map(|s| s.parse()) {
Ok(Ok(x)) => x,
_ => 1,
}
}
if let Some(required_confirmations) = env::var(REQUIRED_CONFIRMATIONS_ENV_VAR).ok() {
base.required_confirmations = required_confirmations.parse().map_err(|_| {
GNTDriverError::library_err_msg(format!(
"invalid {} value: {}",
REQUIRED_CONFIRMATIONS_ENV_VAR, required_confirmations
))
})?;
};
pub static ref MAINNET_CONFIG: EnvConfiguration = EnvConfiguration {
gnt_contract_address: utils::str_to_addr("0x7DD9c5Cba05E151C895FDe1CF355C9A1D5DA6429")
.unwrap(),
gnt_faucet_address: None,
required_confirmations: {
match env::var("ERC20_MAINNET_REQUIRED_CONFIRMATIONS").map(|s| s.parse()) {
Ok(Ok(x)) => x,
_ => 5,
}
}
Ok(base)
}
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn test_address() {
assert_eq!(
CFG_TESTNET.gnt_contract_address,
utils::str_to_addr("0xd94e3DC39d4Cad1DAd634e7eb585A57A19dC7EFE").unwrap()
);
assert_eq!(
CFG_TESTNET.gnt_faucet_address.unwrap(),
utils::str_to_addr("0x59259943616265A03d775145a2eC371732E2B06C").unwrap()
)
}
};
}
77 changes: 12 additions & 65 deletions core/payment-driver/gnt/src/gnt/ethereum.rs
Original file line number Diff line number Diff line change
@@ -1,61 +1,22 @@
use ethereum_types::{Address, H256, U256, U64};
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use std::env;

use crate::networks::Network;
use crate::GNTDriverError;
use std::time::Duration;
use web3::contract::Contract;
use web3::transports::Http;
use web3::types::{Bytes, TransactionId, TransactionReceipt};
use web3::Web3;

const MAINNET_ID: u64 = 1;
const RINKEBY_ID: u64 = 4;

const MAINNET_NAME: &str = "mainnet";
const RINKEBY_NAME: &str = "rinkeby";

const CHAIN_ENV_VAR: &str = "CHAIN";
const GETH_ADDRESS_ENV_VAR: &str = "GETH_ADDRESS";

fn default_geth_address(chain: Chain) -> &'static str {
match chain {
Chain::Rinkeby => "http://1.geth.testnet.golem.network:55555",
Chain::Mainnet => "https://geth.golem.network:55555",
}
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum Chain {
Mainnet,
Rinkeby,
}

impl Default for Chain {
fn default() -> Self {
Chain::Rinkeby
}
}

impl Chain {
pub fn from_env() -> Result<Chain, GNTDriverError> {
if let Some(chain_name) = env::var(CHAIN_ENV_VAR).ok() {
match chain_name.as_str() {
MAINNET_NAME => Ok(Chain::Mainnet),
RINKEBY_NAME => Ok(Chain::Rinkeby),
_chain => Err(GNTDriverError::UnknownChain(_chain.into())),
}
} else {
Ok(Default::default())
}
}

pub fn id(&self) -> u64 {
match &self {
Chain::Mainnet => MAINNET_ID,
Chain::Rinkeby => RINKEBY_ID,
}
fn geth_address(network: Network) -> Cow<'static, str> {
match network {
Network::Rinkeby => std::env::var("ERC20_RINKEBY_GETH_ADDR")
.map(Cow::Owned)
.unwrap_or(Cow::Borrowed("http://1.geth.testnet.golem.network:55555")),
Network::Mainnet => std::env::var("ERC20_MAINNET_GETH_ADDR")
.map(Cow::Owned)
.unwrap_or(Cow::Borrowed("https://geth.golem.network:55555")),
}
}

Expand All @@ -66,30 +27,20 @@ pub struct EthereumClientBuilder {
}

impl EthereumClientBuilder {
pub fn from_env() -> EthereumClientResult<Self> {
let chain = Chain::from_env()?;
Self::with_chain(chain)
}

pub fn with_chain(chain: Chain) -> EthereumClientResult<Self> {
let geth_address = env::var(GETH_ADDRESS_ENV_VAR)
.ok()
.map(Cow::Owned)
.unwrap_or_else(|| Cow::Borrowed(default_geth_address(chain)));
Ok(Self { geth_address })
pub fn with_network(network: Network) -> Self {
let geth_address = geth_address(network);
Self { geth_address }
}

pub fn build(self) -> EthereumClientResult<EthereumClient> {
let transport = web3::transports::Http::new(self.geth_address.as_ref())?;
Ok(EthereumClient {
chain: Chain::from_env()?,
web3: Web3::new(transport),
})
}
}

pub struct EthereumClient {
chain: Chain,
web3: Web3<Http>,
}

Expand Down Expand Up @@ -146,10 +97,6 @@ impl EthereumClient {
}
}

pub fn chain_id(&self) -> u64 {
self.chain.id()
}

pub async fn get_next_nonce(&self, eth_address: Address) -> EthereumClientResult<U256> {
let nonce = self.web3.eth().transaction_count(eth_address, None).await?;
Ok(nonce)
Expand Down
Loading

0 comments on commit 1d8733e

Please sign in to comment.