Skip to content

Commit

Permalink
[Multi-Chain] Enable payments on other chains (#425)
Browse files Browse the repository at this point in the history
This is a PR for the current design of payments on other chains.

The current design allows for creation of csv files on Gnosis and
Arbitrum One. The script needs to be invoked once for each chain with a
different `.env` file.
fhenneke authored Dec 5, 2024
1 parent 613dda2 commit b4727c4
Showing 12 changed files with 180 additions and 71 deletions.
10 changes: 6 additions & 4 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
FILE_OUT_PATH=./out

# Network setup
# NETWORK needs to be one of mainnet, gnosis, arbitrum, base
NETWORK=mainnet
NODE_URL=

# Dune Analytics Credentials
DUNE_API_KEY=

# Safe Transaction Service Requirements.
SAFE_ADDRESS=0xA03be496e67Ec29bC62F01a428683D7F9c204930
NETWORK=mainnet
PAYOUTS_SAFE_ADDRESS=0xA03be496e67Ec29bC62F01a428683D7F9c204930
PROPOSER_PK=

NODE_URL=

# Slack Bot Credentials
SLACK_TOKEN=
SLACK_CHANNEL=
1 change: 1 addition & 0 deletions .github/workflows/postgres.yaml
Original file line number Diff line number Diff line change
@@ -32,6 +32,7 @@ jobs:
run: python -m pytest tests/queries
# Environment variables used by the `pg_client.py`
env:
NETWORK: 'mainnet'
POSTGRES_HOST: localhost
POSTGRES_PORT: 5432
DUNE_API_KEY: ${{ secrets.DUNE_API_KEY }}
2 changes: 2 additions & 0 deletions .github/workflows/pull-request.yaml
Original file line number Diff line number Diff line change
@@ -27,4 +27,6 @@ jobs:
run:
python -m pytest tests/unit
env:
NETWORK: 'mainnet'
NODE_URL: https://rpc.ankr.com/eth
PAYOUTS_SAFE_ADDRESS: '0x0000000000000000000000000000000000000000'
177 changes: 124 additions & 53 deletions src/config.py
Original file line number Diff line number Diff line change
@@ -41,6 +41,8 @@ class RewardConfig:
@staticmethod
def from_network(network: Network) -> RewardConfig:
"""Initialize reward config for a given network."""
cow_bonding_pool = Address("0x5d4020b9261f01b6f8a45db929704b0ad6f5e9e6")
service_fee_factor = Fraction(15, 100)
match network:
case Network.MAINNET:
return RewardConfig(
@@ -51,10 +53,32 @@ def from_network(network: Network) -> RewardConfig:
batch_reward_cap_lower=10 * 10**15,
quote_reward_cow=6 * 10**18,
quote_reward_cap_native=6 * 10**14,
service_fee_factor=Fraction(15, 100),
cow_bonding_pool=Address(
"0x5d4020b9261f01b6f8a45db929704b0ad6f5e9e6"
service_fee_factor=service_fee_factor,
cow_bonding_pool=cow_bonding_pool,
)
case Network.GNOSIS:
return RewardConfig(
reward_token_address=Address(
"0x177127622c4a00f3d409b75571e12cb3c8973d3c"
),
batch_reward_cap_upper=30 * 10**18,
batch_reward_cap_lower=5 * 10**18,
quote_reward_cow=6 * 10**18,
quote_reward_cap_native=1 * 10**18,
service_fee_factor=service_fee_factor,
cow_bonding_pool=cow_bonding_pool,
)
case Network.ARBITRUM_ONE:
return RewardConfig(
reward_token_address=Address(
"0xcb8b5cd20bdcaea9a010ac1f8d835824f5c87a04"
),
batch_reward_cap_upper=12 * 10**15,
batch_reward_cap_lower=10 * 10**15,
quote_reward_cow=6 * 10**18,
quote_reward_cap_native=6 * 10**14,
service_fee_factor=service_fee_factor,
cow_bonding_pool=cow_bonding_pool,
)
case _:
raise ValueError(f"No reward config set up for network {network}.")
@@ -81,18 +105,27 @@ def from_network(network: Network) -> ProtocolFeeConfig:
"""Initialize protocol fee config for a given network."""
match network:
case Network.MAINNET:
return ProtocolFeeConfig(
protocol_fee_safe=Address(
"0xB64963f95215FDe6510657e719bd832BB8bb941B"
),
partner_fee_cut=0.15,
partner_fee_reduced_cut=0.10,
reduced_cut_address="0x63695Eee2c3141BDE314C5a6f89B98E62808d716",
protocol_fee_safe = Address(
"0xB64963f95215FDe6510657e719bd832BB8bb941B"
)
case Network.GNOSIS:
protocol_fee_safe = Address(
"0x6b3214fD11dc91De14718DeE98Ef59bCbFcfB432"
)
case Network.ARBITRUM_ONE:
protocol_fee_safe = Address(
"0x451100Ffc88884bde4ce87adC8bB6c7Df7fACccd"
)
case _:
raise ValueError(
f"No protocol fee config set up for network {network}."
)
return ProtocolFeeConfig(
protocol_fee_safe=protocol_fee_safe,
partner_fee_cut=0.15,
partner_fee_reduced_cut=0.10,
reduced_cut_address="0x63695Eee2c3141BDE314C5a6f89B98E62808d716",
)


@dataclass(frozen=True)
@@ -106,12 +139,18 @@ def from_network(network: Network) -> BufferAccountingConfig:
"""Initialize buffer accounting config for a given network."""
match network:
case Network.MAINNET:
return BufferAccountingConfig(include_slippage=True)
include_slippage = True
case Network.GNOSIS | Network.ARBITRUM_ONE:
include_slippage = False
case _:
raise ValueError(
f"No buffer accounting config set up for network {network}."
)

return BufferAccountingConfig(
include_slippage=include_slippage,
)


@dataclass(frozen=True)
class OrderbookConfig:
@@ -142,10 +181,16 @@ def from_network(network: Network) -> DuneConfig:
dune_api_key = os.environ.get("DUNE_API_KEY", "")
match network:
case Network.MAINNET:
return DuneConfig(dune_api_key=dune_api_key, dune_blockchain="ethereum")
dune_blockchain = "ethereum"
case Network.GNOSIS:
dune_blockchain = "gnosis"
case Network.ARBITRUM_ONE:
dune_blockchain = "arbitrum"
case _:
raise ValueError(f"No dune config set up for network {network}.")

return DuneConfig(dune_api_key=dune_api_key, dune_blockchain=dune_blockchain)


@dataclass(frozen=True)
class NodeConfig:
@@ -154,14 +199,9 @@ class NodeConfig:
node_url: str

@staticmethod
def from_network(network: Network) -> NodeConfig:
"""Initialize node config for a given network."""
match network:
case Network.MAINNET:
node_url = os.environ.get("NODE_URL", "")
case _:
raise ValueError(f"No node config set up for network {network}.")

def from_env() -> NodeConfig:
"""Initialize node config from environment variables."""
node_url = os.environ.get("NODE_URL", "")
return NodeConfig(node_url=node_url)


@@ -177,7 +217,8 @@ class PaymentConfig:
signing_key: str | None
safe_queue_url: str
verification_docs_url: str
weth_address: ChecksumAddress
wrapped_native_token_address: ChecksumAddress
wrapped_eth_address: Address

@staticmethod
def from_network(network: Network) -> PaymentConfig:
@@ -188,47 +229,79 @@ def from_network(network: Network) -> PaymentConfig:

docs_url = "https://www.notion.so/cownation/Solver-Payouts-3dfee64eb3d449ed8157a652cc817a8c"

network_short_name = {
Network.MAINNET: "eth",
Network.GNOSIS: "gno",
}
payment_safe_address = Web3.to_checksum_address(
os.environ.get(
"PAYOUTS_SAFE_ADDRESS",
"",
)
)

match network:
case Network.MAINNET:
payment_safe_address = Web3.to_checksum_address(
os.environ.get(
"SAFE_ADDRESS", "0xA03be496e67Ec29bC62F01a428683D7F9c204930"
)
payment_network = EthereumNetwork.MAINNET
short_name = "eth"

cow_token_address = Address(
"0xDEf1CA1fb7FBcDC777520aa7f396b4E015F497aB"
)
wrapped_native_token_address = Web3.to_checksum_address(
"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
)
short_name = network_short_name[network]
safe_url = (
f"https://app.safe.global/{short_name}:{payment_safe_address}"
wrapped_eth_address = Address(
"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
)
safe_queue_url = f"{safe_url}/transactions/queue"

return PaymentConfig(
network=EthereumNetwork.MAINNET,
cow_token_address=Address(
"0xDEf1CA1fb7FBcDC777520aa7f396b4E015F497aB"
),
payment_safe_address=Web3.to_checksum_address(
"0xA03be496e67Ec29bC62F01a428683D7F9c204930"
),
signing_key=signing_key,
safe_queue_url=safe_queue_url,
verification_docs_url=docs_url,
weth_address=Web3.to_checksum_address(
"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
),
case Network.GNOSIS:
payment_network = EthereumNetwork.GNOSIS
short_name = "gno"

cow_token_address = Address(
"0x177127622c4a00f3d409b75571e12cb3c8973d3c"
)
wrapped_native_token_address = Web3.to_checksum_address(
"0xe91d153e0b41518a2ce8dd3d7944fa863463a97d"
)
wrapped_eth_address = Address(
"0x6a023ccd1ff6f2045c3309768ead9e68f978f6e1"
)
case Network.ARBITRUM_ONE:
payment_network = EthereumNetwork.ARBITRUM_ONE
short_name = "arb1"

cow_token_address = Address(
"0xcb8b5cd20bdcaea9a010ac1f8d835824f5c87a04"
)
wrapped_native_token_address = Web3.to_checksum_address(
"0x82af49447d8a07e3bd95bd0d56f35241523fbab1"
)
wrapped_eth_address = Address(
"0x82af49447d8a07e3bd95bd0d56f35241523fbab1"
)
case _:
raise ValueError(f"No payment config set up for network {network}.")

safe_url = f"https://app.safe.global/{short_name}:{payment_safe_address}"
safe_queue_url = f"{safe_url}/transactions/queue"

return PaymentConfig(
network=payment_network,
cow_token_address=cow_token_address,
payment_safe_address=payment_safe_address,
signing_key=signing_key,
safe_queue_url=safe_queue_url,
verification_docs_url=docs_url,
wrapped_native_token_address=wrapped_native_token_address,
wrapped_eth_address=wrapped_eth_address,
)


@dataclass(frozen=True)
class IOConfig:
"""Configuration of input and output."""

# pylint: disable=too-many-instance-attributes

network: Network
log_config_file: Path
project_root_dir: Path
query_dir: Path
@@ -240,6 +313,7 @@ class IOConfig:
@staticmethod
def from_env() -> IOConfig:
"""Initialize io config from environment variables."""
network = Network(os.getenv("NETWORK"))
slack_channel = os.getenv("SLACK_CHANNEL", None)
slack_token = os.getenv("SLACK_TOKEN", None)

@@ -250,6 +324,7 @@ def from_env() -> IOConfig:
dashboard_dir = project_root_dir / Path("dashboards/solver-rewards-accounting")

return IOConfig(
network=network,
project_root_dir=project_root_dir,
log_config_file=log_config_file,
query_dir=query_dir,
@@ -283,16 +358,12 @@ def from_network(network: Network) -> AccountingConfig:
payment_config=PaymentConfig.from_network(network),
orderbook_config=OrderbookConfig.from_env(),
dune_config=DuneConfig.from_network(network),
node_config=NodeConfig.from_network(network),
node_config=NodeConfig.from_env(),
reward_config=RewardConfig.from_network(network),
protocol_fee_config=ProtocolFeeConfig.from_network(network),
buffer_accounting_config=BufferAccountingConfig.from_network(network),
io_config=IOConfig.from_env(),
)


web3 = Web3(
Web3.HTTPProvider(
NodeConfig.from_network(Network(os.environ.get("NETWORK", "mainnet"))).node_url
)
)
web3 = Web3(Web3.HTTPProvider(NodeConfig.from_env().node_url))
13 changes: 12 additions & 1 deletion src/fetch/dune.py
Original file line number Diff line number Diff line change
@@ -22,13 +22,16 @@ class DuneFetcher:

dune: DuneClient
period: AccountingPeriod
blockchain: str

def __init__(
self,
dune: DuneClient,
blockchain: str,
period: AccountingPeriod,
):
self.dune = dune
self.blockchain = blockchain
self.period = period
# Already have period set, so we might as well store this upon construction.
# This may become an issue when we make the fetchers async;
@@ -39,6 +42,13 @@ def _period_params(self) -> list[QueryParameter]:
"""Easier access to these parameters."""
return self.period.as_query_params()

def _network_and_period_params(self) -> list[QueryParameter]:
"""Easier access to parameters for network and accounting period."""
network_param = QueryParameter.text_type("blockchain", self.blockchain)
period_params = self._period_params()

return period_params + [network_param]

@staticmethod
def _parameterized_query(
query_data: QueryData, params: list[QueryParameter]
@@ -71,7 +81,7 @@ def get_block_interval(self) -> tuple[str, str]:
"""Returns block numbers corresponding to date interval"""
results = self._get_query_results(
self._parameterized_query(
QUERIES["PERIOD_BLOCK_INTERVAL"], self._period_params()
QUERIES["PERIOD_BLOCK_INTERVAL"], self._network_and_period_params()
)
)
assert len(results) == 1, "Block Interval Query should return only 1 result!"
@@ -87,6 +97,7 @@ def get_vouches(self) -> list[DuneRecord]:
params=[
QueryParameter.date_type("end_time", self.period.end),
QueryParameter.enum_type("vouch_cte_name", "named_results"),
QueryParameter.text_type("blockchain", self.blockchain),
],
)
)
Loading

0 comments on commit b4727c4

Please sign in to comment.