Skip to content

Commit 6e56ccf

Browse files
authored
Add claim_USDC command and update docs (#1758)
* Update claim command to replace OCEAN with USDC and mark OCEAN as deprecated * Add USDC payout functionality to CLI * Add unit test for USDC claim functionality * Implement USDC payout functionality in the payout module * Update payout instructions to include USDC and mark OCEAN as deprecated * Add unit test for USDC payout functionality * Refactor payout module to use constants for contract addresses and add tests for payout contract addresses * un-deprecate
1 parent 18319b6 commit 6e56ccf

8 files changed

Lines changed: 208 additions & 15 deletions

File tree

READMEs/payout.md

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,29 @@ This README describes how you can claim the $ based on running a predictoor bot.
99

1010
First, congratulations on your participation and progress in making predictions! Whether you've made accurate or erroneous predictions, it's now time to claim your earnings from correct predictions.
1111

12-
There are two sections below: to claim OCEAN payout, and ROSE payout, respectively.
12+
There are three sections below: to claim USDC payout, OCEAN payout, and ROSE payout, respectively.
13+
14+
## Steps to Request USDC Rewards
15+
16+
### 1. Preparation
17+
18+
Ensure you pause or stop any ongoing prediction submissions. You can use `Ctrl-C` to stop the predictions. This is crucial as active submissions can interfere with the payout process.
19+
20+
### 2. Execute Payout
21+
22+
From console:
23+
24+
```console
25+
pdr claim_USDC ppss.yaml
26+
```
27+
28+
### 3. Completion
29+
30+
Once the payout module concludes its operation, your balance will reflect the updated amount.
31+
32+
### 4. Verification
33+
34+
It's good practice to run the payout module again. This ensures any failed blockchain calls from the previous attempt are addressed and verifies that all eligible payouts have been claimed.
1335

1436
## Steps to Request OCEAN Rewards
1537

@@ -33,7 +55,7 @@ Once the payout module concludes its operation, your balance will reflect the up
3355

3456
It's good practice to run the payout module again. This ensures any failed blockchain calls from the previous attempt are addressed and verifies that all eligible payouts have been claimed.
3557

36-
### Steps to Request ROSE Rewards
58+
## Steps to Request ROSE Rewards
3759

3860
- 1. Preparation
3961

pdr_backend/cli/cli_arguments.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
pdr predictoor PPSS_FILE NETWORK
2525
pdr dashboard PPSS_FILE NETWORK
2626
pdr trader APPROACH PPSS_FILE NETWORK
27+
pdr claim_USDC PPSS_FILE
2728
pdr claim_OCEAN PPSS_FILE
2829
pdr claim_ROSE PPSS_FILE
2930
"""
@@ -556,6 +557,7 @@ def print_args(arguments: Namespace, nested_args: dict):
556557
TraderArgParser = _ArgParser_APPROACH_PPSS_NETWORK
557558
ClaimOceanArgParser = _ArgParser_PPSS
558559
ClaimRoseArgParser = _ArgParser_PPSS
560+
ClaimUsdcArgParser = _ArgParser_PPSS
559561

560562
# power tools
561563
MultisimArgParser = _ArgParser_PPSS
@@ -647,6 +649,7 @@ def __init__(self, description: str, command_name: str):
647649
"do_sim": SimArgParser("Run simulation", "sim"),
648650
"do_predictoor": PredictoorArgParser("Run a predictoor bot", "predictoor"),
649651
"do_trader": TraderArgParser("Run a trader bot", "trader"),
652+
"do_claim_USDC": ClaimUsdcArgParser("Claim USDC", "claim_USDC"),
650653
"do_claim_OCEAN": ClaimOceanArgParser("Claim OCEAN", "claim_OCEAN"),
651654
"do_claim_ROSE": ClaimRoseArgParser("Claim ROSE", "claim_ROSE"),
652655
# power tools

pdr_backend/cli/cli_module.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from pdr_backend.cli.cli_arguments_lake import LAKE_SUBCOMMANDS
2121
from pdr_backend.cli.cli_module_lake import do_lake_subcommand
2222
from pdr_backend.lake.ohlcv_data_factory import OhlcvDataFactory
23-
from pdr_backend.payout.payout import do_ocean_payout, do_rose_payout
23+
from pdr_backend.payout.payout import do_ocean_payout, do_rose_payout, do_usdc_payout
2424
from pdr_backend.ppss.ppss import PPSS
2525
from pdr_backend.pred_submitter.deploy import deploy_pred_submitter_mgr_contract
2626
from pdr_backend.predictoor.predictoor_agent import PredictoorAgent
@@ -142,6 +142,16 @@ def do_claim_ROSE(args, nested_args=None):
142142
do_rose_payout(ppss)
143143

144144

145+
@enforce_types
146+
def do_claim_USDC(args, nested_args=None):
147+
ppss = PPSS(
148+
yaml_filename=args.PPSS_FILE,
149+
network="sapphire-mainnet",
150+
nested_override_args=nested_args,
151+
)
152+
do_usdc_payout(ppss)
153+
154+
145155
@enforce_types
146156
def do_multisim(args, nested_args=None):
147157
d = PPSS.constructor_dict(

pdr_backend/cli/test/test_cli_module.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
do_sim,
1212
do_predictoor,
1313
do_trader,
14+
do_claim_USDC,
1415
do_claim_OCEAN,
1516
do_claim_ROSE,
1617
# power tools
@@ -276,6 +277,15 @@ def test_do_claim_ROSE(monkeypatch):
276277
mock_f.assert_called()
277278

278279

280+
@enforce_types
281+
def test_do_claim_USDC(monkeypatch):
282+
mock_f = Mock()
283+
monkeypatch.setattr(f"{_CLI_PATH}.do_usdc_payout", mock_f)
284+
285+
do_claim_USDC(MockArgParser_PPSS().parse_args())
286+
mock_f.assert_called()
287+
288+
279289
# ---------------------------------------------------------------
280290
# test: Power tools
281291

pdr_backend/payout/payout.py

Lines changed: 81 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@
1616
from pdr_backend.ppss.ppss import PPSS
1717
from pdr_backend.subgraph.subgraph_pending_payouts import query_pending_payouts
1818
from pdr_backend.subgraph.subgraph_sync import wait_until_subgraph_syncs
19-
from pdr_backend.util.constants import SAPPHIRE_MAINNET_CHAINID
19+
from pdr_backend.util.constants import (
20+
DFREWARDS_ADDR,
21+
SAPPHIRE_MAINNET_CHAINID,
22+
USDC_TOKEN_ADDR,
23+
WROSE_TOKEN_ADDR,
24+
)
2025
from pdr_backend.util.currency_types import Eth, Wei
2126

2227
logger = logging.getLogger("payout")
@@ -114,21 +119,23 @@ def do_rose_payout(ppss: PPSS, check_network: bool = True):
114119
up_addr = pred_submitter_mgr.pred_submitter_up_address()
115120
down_addr = pred_submitter_mgr.pred_submitter_down_address()
116121

117-
dfrewards_addr = "0xc37F8341Ac6e4a94538302bCd4d49Cf0852D30C0"
118-
wROSE_addr = "0x8Bc2B030b299964eEfb5e1e0b36991352E56D2D3"
119-
wROSE = WrappedToken(ppss.web3_pp, wROSE_addr)
122+
wROSE = WrappedToken(ppss.web3_pp, WROSE_TOKEN_ADDR)
120123

121-
dfrewards_contract = DFRewards(ppss.web3_pp, dfrewards_addr)
122-
claimable_rewards_up = dfrewards_contract.get_claimable_rewards(up_addr, wROSE_addr)
124+
dfrewards_contract = DFRewards(ppss.web3_pp, DFREWARDS_ADDR)
125+
claimable_rewards_up = dfrewards_contract.get_claimable_rewards(
126+
up_addr, WROSE_TOKEN_ADDR
127+
)
123128
claimable_rewards_down = dfrewards_contract.get_claimable_rewards(
124-
down_addr, wROSE_addr
129+
down_addr, WROSE_TOKEN_ADDR
125130
)
126131
total_claimable = claimable_rewards_up + claimable_rewards_down
127132
logger.info("Found %s wROSE available to claim", total_claimable.to_eth().amt_eth)
128133

129134
if total_claimable > Eth(0):
130135
logger.info("Claiming wROSE rewards from the manager contract...")
131-
receipt = pred_submitter_mgr.claim_dfrewards(wROSE_addr, dfrewards_addr, True)
136+
receipt = pred_submitter_mgr.claim_dfrewards(
137+
WROSE_TOKEN_ADDR, DFREWARDS_ADDR, True
138+
)
132139
if receipt["status"] != 1:
133140
logger.warning(
134141
"Failed to claim wROSE rewards from the contract, tx: %s",
@@ -144,7 +151,7 @@ def _transfer_wrose(instance_address, instance_name):
144151
if balance > 0:
145152
instance = PredSubmitterMgr(ppss.web3_pp, instance_address)
146153
receipt = instance.transfer_erc20(
147-
wROSE_addr, web3_config.owner, balance, True
154+
WROSE_TOKEN_ADDR, web3_config.owner, balance, True
148155
)
149156
if receipt["status"] != 1:
150157
logger.warning(
@@ -169,3 +176,68 @@ def _transfer_wrose(instance_address, instance_name):
169176
wROSE.withdraw(wROSE_bal)
170177

171178
logger.info("ROSE reward claim done")
179+
180+
181+
@enforce_types
182+
def do_usdc_payout(ppss: PPSS, check_network: bool = True):
183+
web3_config = ppss.web3_pp.web3_config
184+
185+
if check_network:
186+
assert ppss.web3_pp.network == "sapphire-mainnet"
187+
assert web3_config.w3.eth.chain_id == SAPPHIRE_MAINNET_CHAINID
188+
189+
web3_config = ppss.web3_pp.web3_config
190+
pred_submitter_mgr_addr = ppss.predictoor_ss.pred_submitter_mgr
191+
pred_submitter_mgr = PredSubmitterMgr(ppss.web3_pp, pred_submitter_mgr_addr)
192+
up_addr = pred_submitter_mgr.pred_submitter_up_address()
193+
down_addr = pred_submitter_mgr.pred_submitter_down_address()
194+
195+
usdc = WrappedToken(ppss.web3_pp, USDC_TOKEN_ADDR)
196+
197+
dfrewards_contract = DFRewards(ppss.web3_pp, DFREWARDS_ADDR)
198+
claimable_rewards_up = dfrewards_contract.get_claimable_rewards(
199+
up_addr, USDC_TOKEN_ADDR
200+
)
201+
claimable_rewards_down = dfrewards_contract.get_claimable_rewards(
202+
down_addr, USDC_TOKEN_ADDR
203+
)
204+
total_claimable = claimable_rewards_up + claimable_rewards_down
205+
logger.info("Found %s USDC available to claim", total_claimable.to_eth().amt_eth)
206+
207+
if total_claimable > Eth(0):
208+
logger.info("Claiming USDC rewards from the manager contract...")
209+
receipt = pred_submitter_mgr.claim_dfrewards(
210+
USDC_TOKEN_ADDR, DFREWARDS_ADDR, True
211+
)
212+
if receipt["status"] != 1:
213+
logger.warning(
214+
"Failed to claim USDC rewards from the contract, tx: %s",
215+
receipt["transactionHash"],
216+
)
217+
return
218+
time.sleep(4)
219+
else:
220+
logger.warning("No rewards available to claim")
221+
222+
def _transfer_usdc(instance_address, instance_name):
223+
balance = usdc.balanceOf(instance_address)
224+
if balance > 0:
225+
instance = PredSubmitterMgr(ppss.web3_pp, instance_address)
226+
receipt = instance.transfer_erc20(
227+
USDC_TOKEN_ADDR, web3_config.owner, balance, True
228+
)
229+
if receipt["status"] != 1:
230+
logger.warning(
231+
"Failed to transfer USDC tokens to the owner from %s, tx: %s",
232+
instance_name,
233+
receipt["transactionHash"],
234+
)
235+
time.sleep(4)
236+
237+
logger.info("Transferring USDC to owner")
238+
239+
_transfer_usdc(up_addr, "up predictoor")
240+
_transfer_usdc(down_addr, "down predictoor")
241+
_transfer_usdc(pred_submitter_mgr.contract_address, "manager")
242+
243+
logger.info("USDC reward claim done")

pdr_backend/payout/test/test_payout.py

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,15 @@
1010
batchify,
1111
do_ocean_payout,
1212
do_rose_payout,
13+
do_usdc_payout,
1314
request_payout_batches,
1415
)
1516
from pdr_backend.ppss.ppss import PPSS, fast_test_yaml_str
17+
from pdr_backend.util.constants import (
18+
DFREWARDS_ADDR,
19+
USDC_TOKEN_ADDR,
20+
WROSE_TOKEN_ADDR,
21+
)
1622
from pdr_backend.util.currency_types import Eth
1723

1824

@@ -109,12 +115,12 @@ def test_do_rose_payout(tmpdir):
109115
):
110116
do_rose_payout(ppss, check_network=False)
111117
mock_contract.claim_dfrewards.assert_called_with(
112-
"0x8Bc2B030b299964eEfb5e1e0b36991352E56D2D3",
113-
"0xc37F8341Ac6e4a94538302bCd4d49Cf0852D30C0",
118+
WROSE_TOKEN_ADDR,
119+
DFREWARDS_ADDR,
114120
True,
115121
)
116122
mock_contract.transfer_erc20.assert_called_with(
117-
"0x8Bc2B030b299964eEfb5e1e0b36991352E56D2D3",
123+
WROSE_TOKEN_ADDR,
118124
web3_config.owner,
119125
Eth(100).to_wei(),
120126
True,
@@ -123,6 +129,49 @@ def test_do_rose_payout(tmpdir):
123129
mock_wrose.withdraw.assert_called_with(Eth(100).to_wei())
124130

125131

132+
@enforce_types
133+
def test_do_usdc_payout(tmpdir):
134+
ppss = _ppss(tmpdir)
135+
web3_config = ppss.web3_pp.web3_config
136+
137+
mock_contract = Mock(spec=PredSubmitterMgr)
138+
mock_contract.get_claimable_rewards = Mock()
139+
mock_contract.get_claimable_rewards.return_value = Eth(100)
140+
mock_contract.contract_address = "0x0"
141+
mock_contract.claim_dfrewards = Mock()
142+
mock_contract.claim_dfrewards.return_value = {
143+
"transactionHash": b"0x1",
144+
"status": 1,
145+
}
146+
mock_contract.transfer_erc20 = Mock()
147+
mock_contract.transfer_erc20.return_value = {"transactionHash": b"0x1", "status": 1}
148+
mock_contract.pred_submitter_up_address.return_value = "0x1"
149+
mock_contract.pred_submitter_down_address.return_value = "0x2"
150+
151+
mock_usdc = Mock(spec=WrappedToken)
152+
mock_usdc.balanceOf = Mock()
153+
mock_usdc.balanceOf.return_value = Eth(100).to_wei()
154+
155+
with patch("pdr_backend.payout.payout.time"), patch(
156+
"pdr_backend.payout.payout.WrappedToken", return_value=mock_usdc
157+
), patch("pdr_backend.payout.payout.DFRewards", return_value=mock_contract), patch(
158+
"pdr_backend.payout.payout.PredSubmitterMgr", return_value=mock_contract
159+
):
160+
do_usdc_payout(ppss, check_network=False)
161+
mock_contract.claim_dfrewards.assert_called_with(
162+
USDC_TOKEN_ADDR,
163+
DFREWARDS_ADDR,
164+
True,
165+
)
166+
mock_contract.transfer_erc20.assert_called_with(
167+
USDC_TOKEN_ADDR,
168+
web3_config.owner,
169+
Eth(100).to_wei(),
170+
True,
171+
)
172+
mock_usdc.balanceOf.assert_called()
173+
174+
126175
@enforce_types
127176
def _ppss(tmpdir):
128177
s = fast_test_yaml_str(tmpdir)

pdr_backend/util/constants.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@
1414
SAPPHIRE_TESTNET_CHAINID: "0x973e69303259B0c2543a38665122b773D28405fB",
1515
}
1616

17+
# Payout contract addresses (Sapphire Mainnet)
18+
DFREWARDS_ADDR = "0xc37F8341Ac6e4a94538302bCd4d49Cf0852D30C0"
19+
WROSE_TOKEN_ADDR = "0x8Bc2B030b299964eEfb5e1e0b36991352E56D2D3"
20+
USDC_TOKEN_ADDR = "0x2c2E3812742Ab2DA53a728A09F5DE670Aba584b6"
21+
1722
S_PER_MIN = 60
1823
S_PER_DAY = 86400
1924
S_PER_WEEK = S_PER_DAY * 7

pdr_backend/util/test_noganache/test_util_constants.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,17 @@
55
CAND_TIMEFRAMES,
66
CAND_USDCOINS,
77
CHAR_TO_SIGNAL,
8+
DFREWARDS_ADDR,
89
S_PER_DAY,
910
S_PER_MIN,
1011
SAPPHIRE_MAINNET_CHAINID,
1112
SAPPHIRE_MAINNET_RPC,
1213
SAPPHIRE_TESTNET_CHAINID,
1314
SAPPHIRE_TESTNET_RPC,
1415
SUBGRAPH_MAX_TRIES,
16+
USDC_TOKEN_ADDR,
1517
WEB3_MAX_TRIES,
18+
WROSE_TOKEN_ADDR,
1619
ZERO_ADDRESS,
1720
)
1821

@@ -39,3 +42,22 @@ def test_util_constants():
3942
assert "close" in CHAR_TO_SIGNAL.values()
4043
assert len(CHAR_TO_SIGNAL) == 5
4144
assert CHAR_TO_SIGNAL["c"] == "close"
45+
46+
47+
@enforce_types
48+
def test_payout_contract_addresses():
49+
"""Test that payout contract addresses are valid checksummed Ethereum addresses."""
50+
# All addresses should start with 0x
51+
assert DFREWARDS_ADDR.startswith("0x")
52+
assert WROSE_TOKEN_ADDR.startswith("0x")
53+
assert USDC_TOKEN_ADDR.startswith("0x")
54+
55+
# All addresses should be 42 characters (0x + 40 hex chars)
56+
assert len(DFREWARDS_ADDR) == 42
57+
assert len(WROSE_TOKEN_ADDR) == 42
58+
assert len(USDC_TOKEN_ADDR) == 42
59+
60+
# Verify specific expected addresses on Sapphire Mainnet
61+
assert DFREWARDS_ADDR == "0xc37F8341Ac6e4a94538302bCd4d49Cf0852D30C0"
62+
assert WROSE_TOKEN_ADDR == "0x8Bc2B030b299964eEfb5e1e0b36991352E56D2D3"
63+
assert USDC_TOKEN_ADDR == "0x2c2E3812742Ab2DA53a728A09F5DE670Aba584b6"

0 commit comments

Comments
 (0)