Closed xmonader closed 1 year ago
I'm not a fan of this. Erc20 is specifically meant for general purpose assets on top of ethereum, while TFT is already a native currency on tfchain. Furthermore, in this context, we use a contract
to govern the payment between 2 parties. To this extend, I prefer the design and implementation of a GenericContract
, which handles billing. This gives more control than the erc20 interface, and fits better in the current architecture.
Specifically, I'm thinking off something like this:
The above follows the trend of using contracts, which means after creation a user does not need to care anymore. Furthermore you can track easily per contract how much you pay over time, how much you can pay maximum, and you don't have to continuously increase your allowance for some 3rd party.
@xmonader @LeeSmet Here is another approach to be considered. Zos deployments work in a similar fashion I believe.
We can have a contract with pin
method which takes a content hash and TFT amounts.
pinning service would listen for that contract on-chain events, pins the hashes, and unpinned it when received another event that states TFT amount consumed.
This approach won't expose public pinning API. pinning and unpinning will mainly be done by listening to chain events.
We could also have a hybrid approach to make the API follow the specs of IPFS pinning service. like after sending the content hash to the pin
contract we could return a unique token, which can be used by a pinning client to pin the same hash. the service will validate the token and the hash from the chain and then pin the content. the pinning period will depend on the content size and the amount of TFT deposited.
@xmonader please put an assignee if already on 'accepted '
@LeeSmet please prepare the specs for this and have consensus with @DylanVerstraete to move forward
Since we don't want to do work on the chain for every possible 3rd party service, we will keep this as generic as possible. While custom implementations for services might offer small advantages in the flow, the extra effort to develop and most importantly maintain these implementations is not worth it compared to a proper generic flow which would technically also be reusable.
A contract will work simple client - server principle (i.e. the "buyer" and the "seller", a "consumer" of a service and one who "offers" the service). Both parties are identified by a twin id to fit in the current flow (we could use a generic address as well here). Contract is laid out as such
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:
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.create_contract(consumer twin ID, service twin ID)
: Creates the contract and sets the id's. Base fee and variable fee are left at 0set_metadata(data)
: Sets the custom metadata on the contract. This can be done by either the client of the service, depending on how it is interpreted (as specified by the service). For now, we will assume that setting metadata is a one off operation. As a result, if metadata is already present when this is called, an error is thrown (i.e. only the first call of this function can succeed).set_fees(base, variable)
: Sets the base fee and variable fee on the contractreject_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 donereject_by_consumer()
: Rejects the contract, deleting it.approve_by_consumer()
: Sets the consumer_accepted
flag on the contract. After this, no more modifications to fees or metadata can be doneWe start of by creating a contract. This can technically be done by anyone, but in practice will likely end up being done by either the service or the consumer (depending on what the service expects). This will be followed by the service or consumer setting the metadata (again depending on how the service expects things to be), and the service setting a base fee + variable fee. Note that part of the communication can and should be off chain, the contract is only the finalized agreement. When the fees and metadata are set, both the consumer and service need to explicitly approve the contract, setting the appropriate flag on the contract. Note that as soon as either party accepted (i.e. either flag is set), the fees and metadata cannot be changed anymore. It is technically possible for consumers to accept a contract as soon as it is created, thereby not giving the service a chance to set the fees. Though this basically means the contract is invalid and the service should just outright reject it.
Once the contract is accepted by both the consumer and the service, it can be billed (i.e. bills send before both flags are set must be rejected). Because a service should not charge the user if it doesn't work, we will require that bills be send every hour, by limiting the window size to 3600. Anything with a bigger window is rejected. This way if the service is down (for some longer period), it for sure can't bill for the time it was down. When the bill is received, the chain calculates contract.base_fee * bill.window / 3600 + variable fee
(keeping in mind the constraint for variable fee as outlined above), and this amount is transferred from the consumer twin account to the service twin account.
We will not implement a grace period for this right now, as the service should define on an individual basis how this is handled. If needed in the future this can of course change.
Progress on tfchain: https://github.com/threefoldtech/tfchain/pull/495
Things left to do here:
Need a status update here, as this was rebased on the work of the power mgmt and the capacity planning? @renauter @DylanVerstraete ?
Need a status update here, as this was rebased on the work of the power mgmt and the capacity planning? @renauter @DylanVerstraete ?
Power mgmt and the capacity planning was reverted 3rd party service contract is now independent from it and has been already merged and tagged https://github.com/threefoldtech/tfchain/releases/tag/2.2.0-rc7 Substrate go client is also already synchronized Tomorrow we handle graphql And will be able to deploy on devnet
First version of 3rd party service contracts is deployed on Devnet! See https://github.com/threefoldtech/tf_operations/issues/1340
@xmonader, all related issues are tagged in Verification
for 3.8.0
Verified.
serviceContractCreate
serviceContractSetMetadata (by service)
serviceContractsSetFees (by service)
serviceContractApprove (by service)
serviceContractReject (by service)
serviceContractApprove (by user)
serviceContractReject (by user)
according to the erc20 interface
these functions support third party billing, where