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 initial scenario is when two parties, a service provider and a consumer of the service, want to use TFChain to automatically handle the billing/payment process for an agreement they want to make. Multiple use cases can benefit from this feature.
Initial requirements:
- Both service and consumer need to have their respective twin created
- Consumer account needs to be funded (lack of funds will simply result in the contract cancelation)
NB: A twin is automatically created when user first register to TFGrid via TFConnect app or Dashboard. For devnet purpose, see here how to create a twin on TFChain.
In the following steps we detail the sequence of extrinsic that need to be called for executing such contract.
The contract creation can be initiated by both service or consumer using the following extrinsic:
service_contract_create(
service_account: AccountId32,
consumer_account: AccountId32,
)
Once executed the service contract is Created
between the two parties.
ServiceContractCreated(service_contract)
is triggered with the contract object.
This object contain a unique ID (service_contract_id
) which is essential to extract for being able to continue the flow
Be aware that calling the extrinsic a second time will create a new contract with a new ID.
Once created, the contract must be filled with its relative fees in mUSD / hour
(only service can set fees):
service_contract_set_fees(
service_contract_id: u64,
base_fee: u64,
variable_fee: u64,
)
and also filled with some metadata with the description of the service for example (only service or consumer can set metadata):
service_contract_set_metadata(
service_contract_id: u64,
metadata: Bytes,
)
The agreement will be automatically considered Ready
when both metadata and fees are set (metadata
not empty and base_fee
greater than zero).
Note that whenever this condition is not reached both extrinsics can still be called to modify agreement
Now having the agreement ready the contract can be submited for approval. One can approve the agreement using this extrinsic:
service_contract_approve(
service_contract_id: u64,
)
and reject it using this following one:
service_contract_reject(
service_contract_id: u64,
)
The contract need to be explicitly Approved
by both service and consumer to be ready for billing.
Before reaching this state, if one of the parties decides to call the rejection extrinsic, it will instantly lead to the cancelation of the contract (and its permanent removal).
Once the contract is accepted by both it can be billed. Only the service can bill the consumer using the following extrinsic:
service_contract_bill(
service_contract_id: u64,
variable_amount: u64,
metadata: Bytes,
)
When the bill is received, the chain calculates the bill amount based on the agreement values as follows:
amount = base_fee * T / 3600 + variable_amount
where T
is the elapsed time, in seconds and bounded by 3600 (see above), since last effective billing operation occured.
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
(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.
Then, if all goes well the consumer pays for the due amount calculated from the bill (see detail above). In practice the amount is transferred from the consumer twin account to the service twin account.
At every moment of the flow since the contract is created it can be canceled (and definitively removed). Only the service or the consumer can do it by calling the following:
service_contract_cancel(
service_contract_id: u64,
)