Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat add support for v3 transactions #125

Merged
merged 19 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 18 additions & 7 deletions pragma/core/contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from starknet_py.contract import Contract as StarknetContract
from starknet_py.contract import ContractFunction, InvokeResult
from starknet_py.net.client_models import SentTransactionResponse
from starknet_py.net.client_models import SentTransactionResponse, ResourceBounds


class Contract(StarknetContract):
Expand All @@ -21,6 +21,8 @@
*args,
max_fee: Optional[int] = None,
auto_estimate: bool = False,
enable_strk_fees: Optional[bool] = False,
l1_resource_bounds: Optional[ResourceBounds] = None,
callback: Optional[Callable[[SentTransactionResponse], None]] = None,
**kwargs,
) -> InvokeResult:
Expand All @@ -29,18 +31,27 @@
This is useful for tracking the nonce changes
"""

prepared_call = self.prepare_invoke_v1(*args, **kwargs)
prepared_call = (

Check warning on line 34 in pragma/core/contract.py

View check run for this annotation

Codecov / codecov/patch

pragma/core/contract.py#L34

Added line #L34 was not covered by tests
self.prepare_invoke_v3(*args, **kwargs)
if enable_strk_fees
else self.prepare_invoke_v1(*args, **kwargs)
)

# transfer ownership to the prepared call
self = prepared_call
if max_fee is not None:
self.max_fee = max_fee

transaction = await self.get_account.sign_invoke_v1(
calls=self,
max_fee=self.max_fee,
auto_estimate=auto_estimate,
transaction = (

Check warning on line 45 in pragma/core/contract.py

View check run for this annotation

Codecov / codecov/patch

pragma/core/contract.py#L45

Added line #L45 was not covered by tests
await self.get_account.sign_invoke_v3(
calls=self,
l1_resource_bounds=l1_resource_bounds,
auto_estimate=auto_estimate,
)
if enable_strk_fees
else await self.get_account.sign_invoke_v1(calls=self, max_fee=max_fee)
)

response = await self._client.send_transaction(transaction)
if callback:
await callback(transaction.nonce, response.transaction_hash)
Expand All @@ -59,4 +70,4 @@


# patch contract function to use new invoke function
ContractFunction.invoke_v1 = invoke_
ContractFunction.invoke = invoke_
51 changes: 35 additions & 16 deletions pragma/core/mixins/oracle.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
from starknet_py.contract import InvokeResult
from starknet_py.net.account.account import Account
from starknet_py.net.client import Client
from starknet_py.net.client_models import ResourceBounds


from pragma.core.contract import Contract
from pragma.core.entry import Entry, FutureEntry, SpotEntry
Expand Down Expand Up @@ -43,14 +45,17 @@
publisher: int,
volume: int = 0,
max_fee: int = int(1e18),
enable_strk_fees: Optional[bool] = False,
l1_resource_bounds: Optional[ResourceBounds] = None,
auto_estimate: Optional[bool] = False,
) -> InvokeResult:
if not self.is_user_client:
raise AttributeError(
"Must set account. "
"You may do this by invoking "
"self._setup_account_client(private_key, account_contract_address)"
)
invocation = await self.oracle.functions["publish_data"].invoke_v1(
invocation = await self.oracle.functions["publish_data"].invoke(

Check warning on line 58 in pragma/core/mixins/oracle.py

View check run for this annotation

Codecov / codecov/patch

pragma/core/mixins/oracle.py#L58

Added line #L58 was not covered by tests
new_entry={
"Spot": {
"base": {
Expand All @@ -64,6 +69,9 @@
}
},
max_fee=max_fee,
l1_resource_bounds=l1_resource_bounds,
auto_estimate=auto_estimate,
enable_strk_fees=enable_strk_fees,
)
return invocation

Expand All @@ -72,6 +80,9 @@
entries: List[Entry],
pagination: Optional[int] = 40,
max_fee=int(1e18),
enable_strk_fees: Optional[bool] = False,
l1_resource_bounds: Optional[ResourceBounds] = None,
auto_estimate: Optional[bool] = False,
) -> List[InvokeResult]:
if len(entries) == 0:
logger.warning("Skipping publishing as entries array is empty")
Expand All @@ -84,11 +95,12 @@
index = 0
while index < len(serialized_spot_entries):
entries_subset = serialized_spot_entries[index : index + pagination]
invocation = await self.oracle.functions[
"publish_data_entries"
].invoke_v1(
invocation = await self.oracle.functions["publish_data_entries"].invoke(

Check warning on line 98 in pragma/core/mixins/oracle.py

View check run for this annotation

Codecov / codecov/patch

pragma/core/mixins/oracle.py#L98

Added line #L98 was not covered by tests
new_entries=[{"Spot": entry} for entry in entries_subset],
enable_strk_fees=enable_strk_fees,
max_fee=max_fee,
l1_resource_bounds=l1_resource_bounds,
auto_estimate=auto_estimate,
callback=self.track_nonce,
)
index += pagination
Expand All @@ -100,9 +112,12 @@
hex(invocation.hash),
)
elif len(serialized_spot_entries) > 0:
invocation = await self.oracle.functions["publish_data_entries"].invoke_v1(
invocation = await self.oracle.functions["publish_data_entries"].invoke(

Check warning on line 115 in pragma/core/mixins/oracle.py

View check run for this annotation

Codecov / codecov/patch

pragma/core/mixins/oracle.py#L115

Added line #L115 was not covered by tests
new_entries=[{"Spot": entry} for entry in serialized_spot_entries],
max_fee=max_fee,
enable_strk_fees=enable_strk_fees,
l1_resource_bounds=l1_resource_bounds,
auto_estimate=auto_estimate,
callback=self.track_nonce,
)
invocations.append(invocation)
Expand All @@ -119,11 +134,12 @@
index = 0
while index < len(serialized_future_entries):
entries_subset = serialized_future_entries[index : index + pagination]
invocation = await self.oracle.functions[
"publish_data_entries"
].invoke_v1(
invocation = await self.oracle.functions["publish_data_entries"].invoke(

Check warning on line 137 in pragma/core/mixins/oracle.py

View check run for this annotation

Codecov / codecov/patch

pragma/core/mixins/oracle.py#L137

Added line #L137 was not covered by tests
new_entries=[{"Future": entry} for entry in entries_subset],
max_fee=max_fee,
enable_strk_fees=enable_strk_fees,
l1_resource_bounds=l1_resource_bounds,
auto_estimate=auto_estimate,
callback=self.track_nonce,
)
index += pagination
Expand All @@ -135,9 +151,12 @@
hex(invocation.hash),
)
elif len(serialized_future_entries) > 0:
invocation = await self.oracle.functions["publish_data_entries"].invoke_v1(
invocation = await self.oracle.functions["publish_data_entries"].invoke(

Check warning on line 154 in pragma/core/mixins/oracle.py

View check run for this annotation

Codecov / codecov/patch

pragma/core/mixins/oracle.py#L154

Added line #L154 was not covered by tests
new_entries=[{"Future": entry} for entry in serialized_future_entries],
max_fee=max_fee,
enable_strk_fees=enable_strk_fees,
l1_resource_bounds=l1_resource_bounds,
auto_estimate=auto_estimate,
callback=self.track_nonce,
)
invocations.append(invocation)
Expand Down Expand Up @@ -296,7 +315,7 @@
"You may do this by invoking "
"self._setup_account_client(private_key, account_contract_address)"
)
invocation = await self.oracle.functions["set_checkpoint"].invoke_v1(
invocation = await self.oracle.functions["set_checkpoint"].invoke(

Check warning on line 318 in pragma/core/mixins/oracle.py

View check run for this annotation

Codecov / codecov/patch

pragma/core/mixins/oracle.py#L318

Added line #L318 was not covered by tests
DataType(DataTypes.SPOT, pair_id, None).serialize(),
aggregation_mode.serialize(),
max_fee=max_fee,
Expand All @@ -317,7 +336,7 @@
"You may do this by invoking "
"self._setup_account_client(private_key, account_contract_address)"
)
invocation = await self.oracle.functions["set_checkpoint"].invoke_v1(
invocation = await self.oracle.functions["set_checkpoint"].invoke(

Check warning on line 339 in pragma/core/mixins/oracle.py

View check run for this annotation

Codecov / codecov/patch

pragma/core/mixins/oracle.py#L339

Added line #L339 was not covered by tests
DataType(DataTypes.FUTURE, pair_id, expiry_timestamp).serialize(),
aggregation_mode.serialize(),
max_fee=max_fee,
Expand Down Expand Up @@ -345,7 +364,7 @@
index = 0
while index < len(pair_ids):
pair_ids_subset = pair_ids[index : index + pagination]
invocation = await self.oracle.functions["set_checkpoints"].invoke_v1(
invocation = await self.oracle.functions["set_checkpoints"].invoke(

Check warning on line 367 in pragma/core/mixins/oracle.py

View check run for this annotation

Codecov / codecov/patch

pragma/core/mixins/oracle.py#L367

Added line #L367 was not covered by tests
pair_ids_subset,
expiry_timestamps,
aggregation_mode.serialize(),
Expand All @@ -359,7 +378,7 @@
hex(invocation.hash),
)
else:
invocation = await self.oracle.functions["set_checkpoints"].invoke_v1(
invocation = await self.oracle.functions["set_checkpoints"].invoke(

Check warning on line 381 in pragma/core/mixins/oracle.py

View check run for this annotation

Codecov / codecov/patch

pragma/core/mixins/oracle.py#L381

Added line #L381 was not covered by tests
pair_ids,
expiry_timestamps,
aggregation_mode.serialize(),
Expand Down Expand Up @@ -387,7 +406,7 @@
index = 0
while index < len(pair_ids):
pair_ids_subset = pair_ids[index : index + pagination]
invocation = await self.oracle.set_checkpoints.invoke_v1(
invocation = await self.oracle.set_checkpoints.invoke(

Check warning on line 409 in pragma/core/mixins/oracle.py

View check run for this annotation

Codecov / codecov/patch

pragma/core/mixins/oracle.py#L409

Added line #L409 was not covered by tests
[
DataType(DataTypes.SPOT, pair_id, None).serialize()
for pair_id in pair_ids_subset
Expand All @@ -403,7 +422,7 @@
hex(invocation.hash),
)
else:
invocation = await self.oracle.set_checkpoints.invoke_v1(
invocation = await self.oracle.set_checkpoints.invoke(

Check warning on line 425 in pragma/core/mixins/oracle.py

View check run for this annotation

Codecov / codecov/patch

pragma/core/mixins/oracle.py#L425

Added line #L425 was not covered by tests
[
DataType(DataTypes.SPOT, pair_id, None).serialize()
for pair_id in pair_ids
Expand All @@ -423,7 +442,7 @@
implementation_hash: int,
max_fee=int(1e18),
) -> InvokeResult:
invocation = await self.oracle.functions["upgrade"].invoke_v1(
invocation = await self.oracle.functions["upgrade"].invoke(

Check warning on line 445 in pragma/core/mixins/oracle.py

View check run for this annotation

Codecov / codecov/patch

pragma/core/mixins/oracle.py#L445

Added line #L445 was not covered by tests
implementation_hash,
max_fee=max_fee,
)
Expand Down
4 changes: 2 additions & 2 deletions pragma/tests/api_client_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
@pytest.fixture(scope="module")
def forked_client(request, module_mocker, pytestconfig) -> Client:
"""
This module-scope fixture prepares a forked katana
This module-scope fixture prepares a forked starknet
client for e2e testing.

:return: a starknet Client
Expand All @@ -54,7 +54,7 @@ def forked_client(request, module_mocker, pytestconfig) -> Client:
str(1),
]
if block_number is not None:
print(f"forking katana at block {block_number}")
print(f"forking starknet at block {block_number}")
command.extend(["--fork-block-number", str(block_number)])
subprocess.Popen(command) # pylint: disable=consider-using-with
time.sleep(10)
Expand Down
32 changes: 26 additions & 6 deletions pragma/tests/client_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
from pragma.core.utils import str_to_felt
from pragma.tests.constants import CURRENCIES, PAIRS
from pragma.tests.utils import read_contract, wait_for_acceptance
from starknet_py.net.client_models import ResourceBounds


PUBLISHER_NAME = "PRAGMA"

Expand Down Expand Up @@ -216,7 +218,9 @@ async def test_client_oracle_mixin_spot(pragma_client: PragmaClient):
ETH_PAIR, 200, timestamp + 10, SOURCE_1, publisher_name, volume=20
)

invocations = await pragma_client.publish_many([spot_entry_1, spot_entry_2])
invocations = await pragma_client.publish_many(
[spot_entry_1, spot_entry_2], auto_estimate=True
)
await invocations[len(invocations) - 1].wait_for_acceptance()
# Fails for UNKNOWN source
unknown_source = "UNKNOWN"
Expand Down Expand Up @@ -246,7 +250,12 @@ async def test_client_oracle_mixin_spot(pragma_client: PragmaClient):
ETH_PAIR, 100, timestamp + 450, SOURCE_1, publisher_name, volume=10
)
try:
invocations = await pragma_client.publish_many([spot_entry_future])
invocations = await pragma_client.publish_many(
[spot_entry_future],
l1_resource_bounds=ResourceBounds(
max_price_per_unit=500 * 10**9, max_amount=10**7
EvolveArt marked this conversation as resolved.
Show resolved Hide resolved
),
)
except TransactionRevertedError as err:
# err_msg = "Execution was reverted; failure reason: [0x54696d657374616d7020697320696e2074686520667574757265]"
err_msg = "Unknown Starknet error"
Expand All @@ -264,7 +273,9 @@ async def test_client_oracle_mixin_spot(pragma_client: PragmaClient):
ETH_PAIR, 200, timestamp + 30, SOURCE_2, publisher_name, volume=20
)

invocations = await pragma_client.publish_many([spot_entry_1, spot_entry_2])
invocations = await pragma_client.publish_many(
[spot_entry_1, spot_entry_2], auto_estimate=True
)
await invocations[len(invocations) - 1].wait_for_acceptance()
res = await pragma_client.get_spot(ETH_PAIR)
assert res.price == 150
Expand Down Expand Up @@ -301,7 +312,9 @@ async def test_client_oracle_mixin_future(pragma_client: PragmaClient):
volume=20000,
)

invocations = await pragma_client.publish_many([future_entry_1, future_entry_2])
invocations = await pragma_client.publish_many(
[future_entry_1, future_entry_2], auto_estimate=True
)
await invocations[len(invocations) - 1].wait_for_acceptance()
# Check entries
entries = await pragma_client.get_future_entries(
Expand Down Expand Up @@ -332,7 +345,9 @@ async def test_client_oracle_mixin_future(pragma_client: PragmaClient):
volume=20,
)

invocations = await pragma_client.publish_many([future_entry_1, future_entry_2])
invocations = await pragma_client.publish_many(
[future_entry_1, future_entry_2], auto_estimate=True
)
await invocations[len(invocations) - 1].wait_for_acceptance()
res = await pragma_client.get_future(ETH_PAIR, expiry_timestamp)
assert res.price == 150
Expand All @@ -351,7 +366,12 @@ async def test_client_oracle_mixin_future(pragma_client: PragmaClient):
volume=10,
)
try:
await pragma_client.publish_many([future_entry_future])
await pragma_client.publish_many(
[future_entry_future],
l1_resource_bounds=ResourceBounds(
max_price_per_unit=500 * 10**9, max_amount=10**7
),
)
except TransactionRevertedError as err:
# err_msg = "Execution was reverted; failure reason: [0x54696d657374616d7020697320696e2074686520667574757265]"
err_msg = "Unknown Starknet error"
Expand Down
6 changes: 3 additions & 3 deletions pragma/tests/publisher_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ async def test_publisher_client_spot(pragma_client: PragmaClient):

# Publish SPOT data
print(data)
await publisher.publish_many(data, pagination=PAGINATION)
await publisher.publish_many(data, pagination=PAGINATION, auto_estimate=True)


@pytest.mark.asyncio
Expand All @@ -118,7 +118,7 @@ async def test_publisher_client_future(pragma_client: PragmaClient):
d for d in data_async if isinstance(d, FutureEntry)
]
print(data_list)
await publisher.publish_many(data_list, pagination=PAGINATION)
await publisher.publish_many(data_list, pagination=PAGINATION, auto_estimate=True)


@pytest.mark.asyncio
Expand Down Expand Up @@ -149,7 +149,7 @@ async def test_publisher_client_all_assets(pragma_client: PragmaClient):

data = [d for d in data if isinstance(d, Entry)]
print(data)
await publisher.publish_many(data, pagination=PAGINATION)
await publisher.publish_many(data, pagination=PAGINATION, auto_estimate=True)


def asset_valid_data_type(data: Sequence[Entry], data_type: Entry):
Expand Down
4 changes: 3 additions & 1 deletion stagecoach/jobs/publishers/custom/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ async def publish_all(assets):

# Use your own custom logic
_entries = fetch_entries(assets)
await publisher_client.publish_many(_entries, max_fee=int(max_fee))
await publisher_client.publish_many(
_entries, max_fee=int(max_fee), auto_estimate=True
)

logger.info("Publishing the following entries:")
for entry in _entries:
Expand Down
7 changes: 7 additions & 0 deletions stagecoach/jobs/publishers/starknet_publisher/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
PAGINATION = os.environ.get("PAGINATION")
RPC_URL = os.environ.get("RPC_URL")
MAX_FEE = int(os.getenv("MAX_FEE", int(1e17)))
ENABLE_STRK_FEES = os.environ.get("ENABLE_STRK_FEES", "False")

DEVIATION_THRESHOLD = float(os.getenv("DEVIATION_THRESHOLD", 0.01))
FREQUENCY_SECONDS = int(os.getenv("FREQUENCY_SECONDS", 60))
Expand Down Expand Up @@ -164,10 +165,16 @@ async def _handler(assets):

_entries = await publisher_client.fetch()
print("entries", _entries)

enable_strk_fees = ENABLE_STRK_FEES.lower() in ("true", "1", "yes")

print(f"ENABLE_STRK_FEES is set to: {enable_strk_fees}")
response = await publisher_client.publish_many(
_entries,
pagination=PAGINATION,
max_fee=MAX_FEE,
enable_strk_fees=enable_strk_fees,
auto_estimate=True, # For safety or should we switch to l1_resource_bounds for cost ?
)

print(
Expand Down
Loading