Skip to content

Commit 31d82b2

Browse files
authored
Merge pull request #98 from singnet/development
Free calls update
2 parents 00d73f3 + 7db2761 commit 31d82b2

20 files changed

Lines changed: 425 additions & 267 deletions

README.md

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -147,29 +147,53 @@ visit the [Payment](#payment) section.
147147

148148
## Payment
149149

150-
### Free call
150+
When creating a service client, you can select a payment strategy using the `payment_strategy_type` parameter:
151151

152-
If you want to use the free calls you will need to pass these arguments to the `create_service_client()` method:
152+
```python
153+
from snet.sdk import PaymentStrategyType
153154

154-
```python
155-
free_call_auth_token_bin = "f2548d27ffd319b9c05918eeac15ebab934e5cfcd68e1ec3db2b92765",
156-
free_call_token_expiry_block = 172800,
157-
email = "test@test.com" # which using in AI marketplace account
155+
payment_strategy_type = PaymentStrategyType.<NAME>
158156
```
159157

160-
You can receive these for a given service from the [Dapp](https://beta.singularitynet.io/)
158+
These are four payment strategies:
159+
160+
- `PaymentStrategyType.DEFAULT`
161+
- `PaymentStrategyType.FREE_CALL`
162+
- `PaymentStrategyType.PAID_CALL`
163+
- `PaymentStrategyType.PREPAID_CALL`
164+
165+
The default payment strategy selects one of the other three each time the service is called, depending on the
166+
availability of free calls, as well as the presence of parameters required for concurrent calls. While choosing
167+
a specific payment strategy will not allow you to switch to another. This is especially convenient when you want
168+
to use free calls without accidentally spending money.
161169

170+
> Note: If you don't specify еру `payment_strategy_type` parameter, the default payment strategy will be used.
171+
172+
### Free call
173+
174+
If you want to use the free calls you will need to choose `PaymentStrategyType.FREE_CALL` as the payment strategy type.
162175
Creating a service client with free calls included would look like this:
176+
163177
```python
164178
service_client = snet_sdk.create_service_client(org_id="26072b8b6a0e448180f8c0e702ab6d2f",
165-
service_id="Exampleservice"
166-
free_call_auth_token_bin="f2548d27ffd319b9c05918eeac15ebab934e5cfcd68e1ec3db2b92765",
167-
free_call_token_expiry_block=172800,
168-
email="test@mail.com")
179+
service_id="Exampleservice",
180+
payment_strategy_type = PaymentStrategyType.FREE_CALL)
169181
```
170182

171183
### Paid call
172184

185+
If you want to use regular paid calls you will need to choose `PaymentStrategyType.PAID_CALL` as the payment strategy type.
186+
Creating a service client with paid calls would look like this:
187+
188+
```python
189+
service_client = snet_sdk.create_service_client(org_id="26072b8b6a0e448180f8c0e702ab6d2f",
190+
service_id="Exampleservice",
191+
payment_strategy_type = PaymentStrategyType.PAID_CALL)
192+
```
193+
194+
There is no need to call functions for interacting with payment channels, because they are automatically
195+
managed by the SDK. But anyway you can use them if you want.
196+
173197
#### Open channel with the specified amount of funds and expiration
174198

175199
`open_channel()`[[1]](#1-this-method-uses-a-call-to-a-paid-smart-contract-function) opens a payment channel with the specified amount of AGIX tokens in cogs and expiration time.
@@ -209,15 +233,20 @@ payment_channel.extend_and_add_funds(amount=123456, expiration=33333)
209233

210234
### Concurrent (Prepaid) call
211235

212-
Concurrent (prepaid) calls allow you to prepay for a batch of service calls in advance. This off-chain strategy is ideal for scenarios requiring high throughput and low latency. Unlike regular paid calls, the payment is done once upfront, and the SDK automatically manages the channel during usage.
236+
Concurrent (prepaid) calls allow you to prepay for a batch of service calls in advance. This off-chain strategy
237+
is ideal for scenarios requiring high throughput and low latency. Unlike regular paid calls, the payment is done
238+
once upfront, and the SDK automatically manages the channel during usage.
213239

214-
To use concurrent prepaid calls, specify the `concurrent_calls` parameter when creating a service client:
240+
If you want to use prepaid calls you will need to choose `PaymentStrategyType.PREPAID_CALL` as the payment strategy type
241+
as well as pass the number of concurrent calls as the `concurrent_calls` parameter. Creating a service client with
242+
prepaid calls would look like this:
215243

216244
```python
217245
service_client = snet_sdk.create_service_client(
218246
org_id="26072b8b6a0e448180f8c0e702ab6d2f",
219247
service_id="Exampleservice",
220248
group_name="default_group",
249+
payment_strategy_type=PaymentStrategyType.PREPAID_CALL,
221250
concurrent_calls=5 # Number of prepaid calls to allocate
222251
)
223252
```

docs/main/init.md

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
[Link](https://github.com/singnet/snet-sdk-python/blob/master/snet/sdk/__init__.py) to GitHub
44

55
Entities:
6-
1. [SnetSDK](#class-snetsdk)
6+
1. [PaymentStrategyType](#class-paymentstrategytype)
7+
2. [SnetSDK](#class-snetsdk)
78
- [\_\_init\_\_](#__init__)
89
- [create_service_client](#create_service_client)
910
- [get_service_stub](#get_service_stub)
@@ -15,6 +16,24 @@ Entities:
1516
- [get_organization_list](#get_organization_list)
1617
- [get_services_list](#get_services_list)
1718

19+
### Class `PaymentStrategyType`
20+
21+
extends: `Enum`
22+
23+
is extended by: -
24+
25+
#### description
26+
27+
This is an `enum` that represents the available payment strategies. It is used to determine which payment
28+
strategy to use when initializing the SDK.
29+
30+
#### members
31+
32+
- `DEFAULT`
33+
- `FREE_CALL`
34+
- `PAID_CALL`
35+
- `PREPAID_CALL`
36+
1837
### Class `SnetSDK`
1938

2039
extends: -
@@ -73,8 +92,8 @@ of the `ServiceClient` class with all the required parameters, which is then ret
7392
- `service_id` (str): The ID of the service.
7493
- `group_name` (str): The name of the payment group. Defaults to _None_.
7594
- `payment_strategy` (PaymentStrategy): The payment channel management strategy. Defaults to _None_.
76-
- `free_call_auth_token_bin` (str): The free call authentication token in binary format. Defaults to _None_.
77-
- `free_call_token_expiry_block` (int): The block number when the free call token expires. Defaults to _None_.
95+
- `payment_strategy_type` (PaymentStrategyType): The type of payment management strategy.
96+
Defaults to `PaymentStrategyType.DEFAULT`.
7897
- `options` (dict): Additional options for the service client. Defaults to _None_.
7998
- `concurrent_calls` (int): The number of concurrent calls allowed. Defaults to 1.
8099

docs/payment_strategies/freecall_payment_strategy.md

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44

55
Entities:
66
1. [FreeCallPaymentStrategy](#class-freecallpaymentstrategy)
7-
- [is_free_call_available](#is_free_call_available)
7+
- [get_free_calls_available](#get_free_calls_available)
88
- [get_payment_metadata](#get_payment_metadata)
99
- [generate_signature](#generate_signature)
10+
- [get_free_call_token_details](#get_free_call_token_details)
1011

1112
### Class `FreeCallPaymentStrategy`
1213

@@ -17,28 +18,29 @@ is extended by: -
1718
#### description
1819

1920
The `FreeCallPaymentStrategy` class is a concrete implementation of the `PaymentStrategy` interface.
20-
It allows you to use free calls (which can be received from the [Dapp](https://beta.singularitynet.io/)) to
21+
It allows you to use free calls (which can be received from the daemon) to
2122
call services.
2223

2324
#### methods
2425

25-
#### `is_free_call_available`
26+
#### `get_free_calls_available`
2627

27-
Checks if a free call is available for a given service client.
28+
Using grpc calls to the daemon, it gets a free call token, and also gets and returns the number of free calls
29+
available.
2830

2931
###### args:
3032

3133
- `service_client` (ServiceClient): The service client instance.
3234

3335
###### returns:
3436

35-
- True if a free call is available, False otherwise. (bool)
37+
- Amount of free calls available. (int)
3638

3739
###### raises:
3840

3941
- Exception: If an error occurs while checking the free call availability.
4042

41-
_Note_: If any exception occurs during the process, it returns False.
43+
_Note_: If an error occurs specifically during the grpc call to `GetFreeCallsAvailable`, 0 will be returned.
4244

4345
#### `get_payment_metadata`
4446

@@ -60,6 +62,8 @@ Generates a signature for the given service client using the provided free call
6062
###### args:
6163

6264
- `service_client` (ServiceClient): The service client instance.
65+
- `current_block_number` (int, optional): The current block number. Defaults to _None_.
66+
- `with_token` (bool, optional): Whether to include the free call token in the signature. Defaults to _True_.
6367

6468
###### returns:
6569

@@ -68,3 +72,20 @@ Generates a signature for the given service client using the provided free call
6872
###### raises:
6973

7074
- Exception: If any of the required parameters for the free call strategy are missing.
75+
76+
#### `get_free_call_token_details`
77+
78+
Sends a request to the daemon and receives a free call token and its details.
79+
80+
###### args:
81+
82+
- `service_client` (ServiceClient): The service client instance.
83+
- `current_block_number` (int, optional): The current block number. Defaults to _None_.
84+
85+
###### returns:
86+
87+
- A tuple containing the free call token and the token expiration block number. (tuple[str, int])
88+
89+
###### raises:
90+
91+
- Exception: If an error occurred while receiving the token.

requirements.txt

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,12 @@
1-
protobuf==4.21.6
2-
grpcio-tools==1.59.0
3-
wheel==0.41.2
4-
jsonrpcclient==4.0.3
5-
eth-hash==0.5.2
6-
rlp==3.0.0
7-
eth-rlp==0.3.0
8-
web3==6.11.1
9-
mnemonic==0.20
10-
pycoin==0.92.20230326
11-
pyyaml==6.0.1
12-
ipfshttpclient==0.4.13.2
13-
rfc3986==2.0.0
14-
pymultihash==0.8.2
15-
base58==2.1.1
16-
argcomplete==3.1.2
17-
grpcio-health-checking==1.59.0
18-
jsonschema==4.0.0
19-
eth-account==0.9.0
20-
snet-contracts==1.0.0
21-
lighthouseweb3==0.1.4
22-
zipp>=3.19.1
1+
protobuf==5.*
2+
grpcio-tools>=1.71.0
3+
wheel>=0.45.0
4+
rlp>=4.1.0
5+
web3==7.*
6+
ipfshttpclient==0.4.13
7+
rfc3986>=2.0.0
8+
base58>=2.1.1
9+
grpcio-health-checking>=1.71.0
10+
snet-contracts==1.0.1
11+
lighthouseweb3>=0.1.4
12+
pymultihash==0.*

snet/sdk/__init__.py

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import os
33
import sys
44
import warnings
5+
from enum import Enum
56

67
import google.protobuf.internal.api_implementation
78

@@ -26,18 +27,28 @@
2627
from snet.sdk.client_lib_generator import ClientLibGenerator
2728
from snet.sdk.mpe.mpe_contract import MPEContract
2829
from snet.sdk.mpe.payment_channel_provider import PaymentChannelProvider
29-
from snet.sdk.payment_strategies.default_payment_strategy import DefaultPaymentStrategy as PaymentStrategy
30+
from snet.sdk.payment_strategies.default_payment_strategy import *
3031
from snet.sdk.service_client import ServiceClient
3132
from snet.sdk.storage_provider.storage_provider import StorageProvider
3233
from snet.sdk.custom_typing import ModuleName, ServiceStub
33-
from snet.sdk.utils.utils import (bytes32_to_str, find_file_by_keyword,
34-
type_converter)
34+
from snet.sdk.utils.utils import (
35+
bytes32_to_str,
36+
find_file_by_keyword,
37+
type_converter
38+
)
3539

3640
google.protobuf.internal.api_implementation.Type = lambda: 'python'
3741
_sym_db = _symbol_database.Default()
3842
_sym_db.RegisterMessage = lambda x: None
3943

4044

45+
class PaymentStrategyType(Enum):
46+
PAID_CALL = PaidCallPaymentStrategy
47+
FREE_CALL = FreeCallPaymentStrategy
48+
PREPAID_CALL = PrePaidPaymentStrategy
49+
DEFAULT = DefaultPaymentStrategy
50+
51+
4152
class SnetSDK:
4253
"""Base Snet SDK"""
4354

@@ -91,11 +102,9 @@ def __init__(self, sdk_config: Config, metadata_provider=None):
91102
def create_service_client(self,
92103
org_id: str,
93104
service_id: str,
94-
group_name=None,
95-
payment_strategy=None,
96-
free_call_auth_token_bin=None,
97-
free_call_token_expiry_block=None,
98-
email=None,
105+
group_name: str=None,
106+
payment_strategy: PaymentStrategy = None,
107+
payment_strategy_type: PaymentStrategyType=PaymentStrategyType.DEFAULT,
99108
options=None,
100109
concurrent_calls: int = 1):
101110

@@ -120,22 +129,13 @@ def create_service_client(self,
120129
print("Generating client library...")
121130
self.lib_generator.generate_client_library()
122131

123-
if payment_strategy is None:
124-
payment_strategy = PaymentStrategy(
125-
concurrent_calls=concurrent_calls
126-
)
127-
128132
if options is None:
129133
options = dict()
130-
options['free_call_auth_token-bin'] = (
131-
bytes.fromhex(free_call_auth_token_bin) if
132-
free_call_token_expiry_block else ""
133-
)
134-
options['free-call-token-expiry-block'] = (
135-
free_call_token_expiry_block if free_call_token_expiry_block else 0
136-
)
137-
options['email'] = email if email else ""
138134
options['concurrency'] = self._sdk_config.get("concurrency", True)
135+
options['concurrent_calls'] = concurrent_calls
136+
137+
if payment_strategy is None:
138+
payment_strategy = payment_strategy_type.value()
139139

140140
service_metadata = self._metadata_provider.enhance_service_metadata(
141141
org_id, service_id
@@ -146,7 +146,8 @@ def create_service_client(self,
146146

147147
pb2_module = self.get_module_by_keyword(keyword="pb2.py")
148148
_service_client = ServiceClient(org_id, service_id, service_metadata,
149-
group, service_stubs, payment_strategy,
149+
group, service_stubs,
150+
payment_strategy,
150151
options, self.mpe_contract,
151152
self.account, self.web3, pb2_module,
152153
self.payment_channel_provider,

snet/sdk/account.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def _send_signed_transaction(self, contract_fn, *args):
8484
signed_txn = self.web3.eth.account.sign_transaction(
8585
transaction, private_key=self.private_key)
8686
return self.web3.to_hex(
87-
self.web3.eth.send_raw_transaction(signed_txn.rawTransaction)
87+
self.web3.eth.send_raw_transaction(signed_txn.raw_transaction)
8888
)
8989

9090
def send_transaction(self, contract_fn, *args):

snet/sdk/concurrency_manager.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@
33
import grpc
44
import web3
55

6-
from snet.sdk.service_client import ServiceClient
76
from snet.sdk.utils.utils import RESOURCES_PATH, add_to_path
87

98

109
class ConcurrencyManager:
11-
def __init__(self, concurrent_calls: int):
10+
def __init__(self, concurrent_calls: int=1):
1211
self.__concurrent_calls: int = concurrent_calls
1312
self.__token: str = ''
1413
self.__planned_amount: int = 0
@@ -18,14 +17,18 @@ def __init__(self, concurrent_calls: int):
1817
def concurrent_calls(self) -> int:
1918
return self.__concurrent_calls
2019

20+
@concurrent_calls.setter
21+
def concurrent_calls(self, concurrent_calls: int):
22+
self.__concurrent_calls = concurrent_calls
23+
2124
def get_token(self, service_client, channel, service_call_price):
2225
if len(self.__token) == 0:
2326
self.__token = self.__get_token(service_client, channel, service_call_price)
2427
elif self.__used_amount >= self.__planned_amount:
2528
self.__token = self.__get_token(service_client, channel, service_call_price, new_token=True)
2629
return self.__token
2730

28-
def __get_token(self, service_client: ServiceClient, channel, service_call_price, new_token=False):
31+
def __get_token(self, service_client, channel, service_call_price, new_token=False):
2932
if not new_token:
3033
amount = channel.state["last_signed_amount"]
3134
if amount != 0:
@@ -47,13 +50,13 @@ def __get_token(self, service_client: ServiceClient, channel, service_call_price
4750
self.__planned_amount = token_reply.planned_amount
4851
return token_reply.token
4952

50-
def __get_stub_for_get_token(self, service_client: ServiceClient):
53+
def __get_stub_for_get_token(self, service_client):
5154
grpc_channel = service_client.get_grpc_base_channel()
5255
with add_to_path(str(RESOURCES_PATH.joinpath("proto"))):
5356
token_service_pb2_grpc = importlib.import_module("token_service_pb2_grpc")
5457
return token_service_pb2_grpc.TokenServiceStub(grpc_channel)
5558

56-
def __get_token_for_amount(self, service_client: ServiceClient, channel, amount):
59+
def __get_token_for_amount(self, service_client, channel, amount):
5760
nonce = channel.state["nonce"]
5861
stub = self.__get_stub_for_get_token(service_client)
5962
with add_to_path(str(RESOURCES_PATH.joinpath("proto"))):

0 commit comments

Comments
 (0)