Skip to content

Commit

Permalink
feat(pallet): use unit mUSD for setting service contracts prices (#746
Browse files Browse the repository at this point in the history
)
  • Loading branch information
renauter authored Jun 16, 2023
1 parent d0fd8e1 commit fadf4df
Show file tree
Hide file tree
Showing 5 changed files with 31 additions and 34 deletions.
18 changes: 18 additions & 0 deletions docs/architecture/0003-service-consumer-contract.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# 3. Service consumer contract

Date: 2022-10-17

## Status

Accepted

## Context

See [here](https://github.com/threefoldtech/tfchain/issues/445) for more details.

## Decision

It is now possible to create generic contract between two `TFChain` users (without restriction of account type) for some "away from the grid" service and bill for it.

The service consumer contract specs are described [here](../../substrate-node/pallets/pallet-smart-contract/service_consumer_contract_specs.md).
See [here](../../substrate-node/pallets/pallet-smart-contract/service_consumer_contract_flow.md) how to use this feature.
25 changes: 0 additions & 25 deletions docs/architecture/0003-third-party-service-contract.md

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Service/Consumer contracts

It is now possible to create generic contract between two TFChain users (without restriction of account type) for some service and bill for it.
It is now possible to create generic contract between two TFChain users (without restriction of account type) for some "away from the grid" service and bill for it.


## How does it work?
Expand Down Expand Up @@ -38,7 +38,7 @@ Be aware that calling the extrinsic a second time will create a new contract wit

## Step 2: Fill contract

Once created, the contract must be filled with its relative `per hour` fees (only service can set fees):
Once created, the contract must be filled with its relative fees in `mUSD / hour` (only service can set fees):

~~~rust
service_contract_set_fees(
Expand Down Expand Up @@ -112,7 +112,7 @@ where `T` is the elapsed time, in seconds and bounded by 3600 (see above), since
Note that if `variable_amount` is too high (i.e `variable_amount > variable_fee * T / 3600`) the billing extrinsic will fail.
The `variable_fee` value in the contract is interpreted as being "per hour" and acts as a protection mechanism to avoid consumer draining.
Indeed, as it is technically possible for the service to send a bill every second, there would be no gain for that (unless overloading the chain uselessly).
So it is also the service responsability to set a suitable `variable_amount` according to the billing frequency!
So it is also the service responsability to set a suitable `variable_amount` (in mUSD) according to the billing frequency!

Also be aware that if the consumer is out of funds the billing will fail AND the contract will be canceled.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ A contract will work simple client - server principle (i.e. the "buyer" and the

- consumer twin id
- service twin id
- base fee, this is the fixed amount which will be billed hourly
- variable fee, this is the maximum amount which can be billed on top of the base fee (for variable consumption metrics, to be defined by the service)
- base fee, this is the fixed amount (in mUSD) which will be billed hourly
- variable fee, this is the maximum amount (in mUSD) which can be billed on top of the base fee (for variable consumption metrics, to be defined by the service)
- metadata, a field which just holds some bytes. The service can use this any way it likes (including having stuff set by the user). We limit this field to some size now, suggested 64 bytes (2 public keys generally)

Additionally, we also keep track of some metadata, which will be:
Expand All @@ -22,7 +22,7 @@ Additionally, we also keep track of some metadata, which will be:

Once a contract is accepted by both the consumer and the service, the chain can start accepting "bill reports" from the service for the contract. Only the twin of the service can send these, as specified in the contract. The bill contains the following:

- Variable amount which is billed. The chain checks that this is less than or equal to the variable amount as specified in the contract, if it is higher the bill is rejected for overcharging. Technically the service could send a report every second to drain the user. To protect against this, the max variable amount in the contract is interpreted as being "per hour", and the value set in the contract is divided by 3600, multiplied by window size, to find the actual maximum which can be billed by the contract.
- Variable amount (in mUSD) which is billed. The chain checks that this is less than or equal to the variable amount as specified in the contract, if it is higher the bill is rejected for overcharging. Technically the service could send a report every second to drain the user. To protect against this, the max variable amount in the contract is interpreted as being "per hour", and the value set in the contract is divided by 3600, multiplied by window size, to find the actual maximum which can be billed by the contract.
- Window, this is the amount of time (in seconds) covered since last contract. The chain verifies that `current time - window >= contract.last_bill`, such that no bills overlap to avoid overcharging. Combined with the previous limit to variable amount this prevents the service from overcharging the user.
- Some optional metadata, this will again just be some bytes (the service decides how this can be interpreted). For now we'll limit this to 50 bytes or so.

Expand All @@ -38,7 +38,7 @@ Once a contract is accepted by both the consumer and the service, the chain can

### Callable by service

- `set_fees(base, variable)`: Sets the base fee and variable fee on the contract
- `set_fees(base, variable)`: Sets the base fee and variable fee on the contract (both in mUSD)
- `reject_by_service()`: Rejects the contract, deleting it.
- `approve_by_service()`: Sets the `service_accepted` flag on the contract. After this, no more modifications to fees or metadata can be done

Expand Down
8 changes: 6 additions & 2 deletions substrate-node/pallets/pallet-smart-contract/src/cost.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,14 @@ impl ServiceContract {
&self,
service_bill: ServiceContractBill,
) -> u64 {
// Convert fees from mUSD to units USD
let base_fee_units_usd = self.base_fee * 10000;
let variable_amount_units_usd = service_bill.variable_amount * 10000;
// bill user for service usage for elpased usage (window) in seconds
let contract_cost = U64F64::from_num(self.base_fee) * U64F64::from_num(service_bill.window)
let contract_cost = U64F64::from_num(base_fee_units_usd)
* U64F64::from_num(service_bill.window)
/ U64F64::from_num(T::BillingReferencePeriod::get())
+ U64F64::from_num(service_bill.variable_amount);
+ U64F64::from_num(variable_amount_units_usd);
contract_cost.round().to_num::<u64>()
}
}
Expand Down

0 comments on commit fadf4df

Please sign in to comment.