Skip to content

Commit

Permalink
fix: issue with paging account history (#144)
Browse files Browse the repository at this point in the history
  • Loading branch information
antazoey authored Jul 5, 2024
1 parent 94c4c38 commit a6d03aa
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 9 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,15 @@ from ape import project

spork_contract_type = project.dependencies["Spork"]["etherscan"].Spork
```

# Querying Accounts

Etherscan offers a query-provider plugin for account data.
Query account transactions from Etherscan using the following syntax:

```python
from ape import chain

history = chain.history["vitalik.eth"]
result = history.query("*", engine_to_use="etherscan")
```
16 changes: 10 additions & 6 deletions ape_etherscan/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,12 +429,15 @@ def get_all_normal_transactions(
page = self._get_page_of_normal_transactions(
page_num, start_block, end_block, offset=offset, sort=sort
)

if len(page):
new_items = len(page)
if new_items:
yield from page

last_page_results = len(page)
last_page_results = new_items
page_num += 1
if new_items <= 0:
# No more items. Break now to avoid 500 errors.
break

def _get_page_of_normal_transactions(
self,
Expand All @@ -456,10 +459,11 @@ def _get_page_of_normal_transactions(
}
result = self._get(params=params)

if not isinstance(result.value, list):
raise UnhandledResultError(result, result.value)
value = result.value or []
if not isinstance(value, list):
raise UnhandledResultError(result, value)

return result.value
return value


class ClientFactory:
Expand Down
2 changes: 1 addition & 1 deletion ape_etherscan/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,6 @@
],
"scroll": [
"mainnet",
"sepolia",
"testnet",
],
}
4 changes: 4 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,10 @@ def get_url_f(testnet: bool = False, tld: str = "io"):
"gnosis": {
"mainnet": url("gnosisscan"),
},
"scroll": {
"mainnet": com_url("scrollscan"),
"testnet": com_testnet_url("testnet", "scrollscan"),
},
}

def set_network(self, ecosystem: str, network: str):
Expand Down
52 changes: 52 additions & 0 deletions tests/test_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import pytest
from ape.utils import ManagerAccessMixin

from ape_etherscan.client import AccountClient
from ape_etherscan.types import EtherscanInstance


class TestAccountClient(ManagerAccessMixin):
@pytest.fixture
def instance(self) -> EtherscanInstance:
return EtherscanInstance(
ecosystem_name="mye-cosystem",
network_name="my-network",
uri="https://explorer.example.com",
api_uri="https://explorer.example.com/api",
)

@pytest.fixture
def address(self):
return self.account_manager.test_accounts[0]

@pytest.fixture
def mock_session(self, mocker):
return mocker.MagicMock()

@pytest.fixture
def account_client(self, mock_session, instance, address):
client = AccountClient(instance, address)
client.session = mock_session
return client

def test_get_all_normal_transactions(self, mocker, account_client):
start_block = 6
end_block = 8
end_page = 3

# Setup session.
def get_txns(*args, **kwargs):
# Make it page a bit.
page = kwargs.get("params").get("page")
result = [] if page == end_page else [{"page": page}]
resp = mocker.MagicMock()
resp.json.return_value = {"result": result}
return resp

account_client.session.request.side_effect = get_txns

fn = account_client.get_all_normal_transactions
iterator = fn(start_block=start_block, end_block=end_block, offset=1, sort="desc")
actual = [x for x in iterator]
expected = [{"page": 1}, {"page": 2}]
assert actual == expected
4 changes: 2 additions & 2 deletions tests/test_etherscan.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@
("bsc", "testnet-fork", "testnet.bscscan.com"),
("gnosis", "mainnet", "gnosisscan.io"),
("gnosis", "mainnet-fork", "gnosisscan.io"),
("scroll", "mainnet", "scrollscan.io"),
("scroll", "sepolia", "sepolia.scrollscan.io"),
("scroll", "mainnet", "scrollscan.com"),
("scroll", "testnet", "testnet.scrollscan.com"),
],
)

Expand Down
15 changes: 15 additions & 0 deletions tests/test_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import pytest

from ape_etherscan.types import EtherscanResponse


class TestEtherscanResponse:
@pytest.fixture
def response(self, mocker):
return mocker.MagicMock()

@pytest.mark.parametrize("value", ([], [{"foo": "bar"}], None))
def test_value(self, response, value):
response.json.return_value = {"result": value}
resp = EtherscanResponse(response, "my-ecosystem", raise_on_exceptions=False)
assert resp.value == value

0 comments on commit a6d03aa

Please sign in to comment.