From 4c13c294b239afc71577cdd8b4f26e4ebdbca784 Mon Sep 17 00:00:00 2001 From: Jeffrey Doyle Date: Wed, 25 Aug 2021 12:01:15 -0700 Subject: [PATCH 01/16] FLIP: FCL Tx Payer Service --- flips/20210824-fcl-tx-payer-service.md | 277 +++++++++++++++++++++++++ 1 file changed, 277 insertions(+) create mode 100644 flips/20210824-fcl-tx-payer-service.md diff --git a/flips/20210824-fcl-tx-payer-service.md b/flips/20210824-fcl-tx-payer-service.md new file mode 100644 index 000000000..68ac6337c --- /dev/null +++ b/flips/20210824-fcl-tx-payer-service.md @@ -0,0 +1,277 @@ +# FCL Transaction Payer Service + +| Status | Proposed | +:-------------- |:---------------------------------------------------- | +| **FLIP #** | [NNN](https://github.com/onflow/flow/pull/NNN) (update when you have PR #)| +| **Author(s)** | Jeffrey Doyle (jeffrey.doyle@dapperlabs.com), | +| **Sponsor** | | +| **Updated** | 2020-08-24 | + +## Objective + +FCL provides a mechanism for a transactions fees to be payed by an account +other than the proposer or any of the authorizers of a transaction. + +A transaction payer service would be a service that facilitates signing as the +payer for transactions that it receives. + +## Motivation + +One of the features of Flow is that transaction fees can be payed by an account +seperate from the authorizers of that transaction. Applications that wish to +provide "free" transactions for their users could opt to pay for their users +transactions themselves. They would do this by produdcing a signature for the +transaction as the payer for that transaction. Upon receiving this transaction, +the Flow network would then deduct transaction fees from the payer, not any of +the authorizers. + +Some FCL compatible Wallets choose to "swap out" the payer of a transaction they're +requested to sign with a Flow account that the wallet controls (in the case that the +user's Flow account is specified as the transactions payer) to provide "free" transactions +for their users. While this makes for a desireable user experience for those that use +Wallets with this feature, applications do not get the guarantee that _all_ of their +users will get this user experience. This is because users are able to use whichever FCL +compatible wallet they desire to the FCL compatible applications they use, and not +all FCL compatible wallets will strictly choose to provide this feature. + +Applications that wish to guarantee that their users are able to submit transactions +without having to pay for them will then have to always pay for them themselves. These +applications would then have to build a "Transaction Fee Payer Service" that is able +to receive a transaction signable and then produce a signature for it. + +We imagine that there is a potential business model here. A transaction fee payer +service (or: "TPaaS", "Transaction Payer as a Service") could be a webservice that +works with applications to provide payer signatures for the users of those applications. +Applications, instead of having to each individually build their own transaction +fee payer service, could depend on a this businesses implementation. The business could charge +a fee to the applications that use it for their ability to use this service. + +## User Benefit + +Most consumer applications that we interact with on a daily basis charge us nothing. +As internet users, we're freely able to interact for our favourite social media apps, +interact with our favourite websites and simply do as we please without paying for anything. +However, with blockchain applications, each one of those interactions would likely be +transaction, and with that, transaction fees would have to be paid. + +Users have been conditioned to expect things for free. As such, applications that wish +to provide the best user experiences will likely choose to allow their users to execute +transactions without them having to pay. + +This Transaction Fee Payer Service will enable more applications to be able to provide +this higher level of user experience. Creating value for the application, the users of +that application, and the potential for the tranaction fee payer service to capture some of that +value as revenue. + +## Design Proposal + +> Disclaimer: The Following is an example implementation for what _might_ work. All possible solutions are not limited to just the following. You're completely free to approach the problem however you choose, even if it differs from the thoughts below. + +When creating a transasction to be sent to the blockchain, FCL expects authorization functions +for each of the roles for that transaction to be specified. + +For example, when specifying to FCL that the "Current User" of the application +should be an Authorizer, the Proposer and the Payer of a transaction, it might +look something like: + +```javascript +// IN CLIENT APPLICATIONS FRONTEND + +import * as fcl from "@onflow/fcl" + +const tx = await fcl.send([ + fcl.transaction`... Cadence Code ...`, + fcl.authorizers([fcl.currentUser().authorization]), + fcl.proposer(fcl.currentUser().authorization), + fcl.payer(fcl.currentUser().authorization) +]).then(fcl.decode) +``` + +The line `fcl.currentUser().authorization` is an "Authorization Function". + +(Read More about Authorization Functions: https://github.com/onflow/fcl-js/blob/master/packages/fcl/src/wallet-provider-spec/authorization-function.md) + +The implementation of this Authorization Function is what FCL uses to gather a +signature from the Current User of the applcation for each of the roles of the +transaction (Proposer, Payer and Authorizer). + +A Transaction Fee Payer Service would need to make available an Authorization Function +that applications could use in place of `fcl.currentUser().authorization` (or whatever other +authorization function they use). + +The applicationss transaction code might then look something like: + +```javascript +// IN CLIENT APPLICATIONS FRONTEND + +import * as fcl from "@onflow/fcl" +import { TPaaSAuthorizationFunction } from "@YourTPaaS/TPaaS-client" + +const configuredTPasSAuthzFn = TPaaSAuthorizationFunction({ + ... TPaaS Configuration ... + appResolveAccountURL: "https://api.myawesomedapp.com/tpaas/resolveaccount" // Example + appSigningURL: "https://api.myawesomedapp.com/tpaas/sign" // Example +}) + +const tx = await fcl.send([ + fcl.transaction`... Cadence Code ...`, + fcl.authorizers([fcl.currentUser().authorization]), + fcl.proposer(fcl.currentUser().authorization), + fcl.payer(configuredTPasSAuthzFn) +]).then(fcl.decode) +``` + +The implementation of `TPaaSAuthorizationFunction` will need to conform to how FCL expects +Authorization Functions to work. (Read More about Authorization Functions: https://github.com/onflow/fcl-js/blob/master/packages/fcl/src/wallet-provider-spec/authorization-function.md) + +The implementation of the `TPaaSAuthorizationFunction` might look something like: + +```javascript +// IN "@YourTPaaS/TPaaS-client" + +export const TPaaSAuthorizationFunction = ({ appResolveAccountURL, appSigningURL }) => async (account) => { + + /** + Perform a network call to resolve the FCL "account" data structure. This network call will likely be to the client applcation's backend, which would likely attach some secret authentication information (API Key etc) to the request before sending it off to the TPaaS API. + + The call graph might look something like: + + Client App TPaaSAuthorizationFunction =POST=> Client Backend =POST=> TPaaS API =POST RESPONSE=> Client Backend =POST RESPONSE=> TPaaSAuthorizationFunction + + The TPaaS API would need to return back an account data structure containing: + (See: https://github.com/onflow/fcl-js/blob/master/packages/fcl/src/wallet-provider-spec/authorization-function.md#how-to-create-an-authorization-function for more information on the purpose of each field) + { + ...account, + tempId: tpaasSessionID + addr: tpaasSignerAddress + keyId: tpaasSignerKeyID + } + **/ + const resolvedAccount = await fetch(appResolveAccountURL, { + method: "POST", + headers: { "Content-Type": "application/json" } + body: JSON.stringify(account) + }).then(res => res.json()) + + // The authorization function returns an FCL account data structure with more fields filled in. + return { + ...resolvedAccount, + signingFunction: async signable => { + /** + Perform a network call to resolve the transaction signature. This network call will likely be to the client applcation's backend, which would likely attach some secret authentication information (API Key etc) to the request before sending it off to the TPaaS API. + + The call graph might look something like: + + Client App TPaaSAuthorizationFunction =POST=> Client Backend =POST=> TPaaS API =POST RESPONSE=> Client Backend =POST RESPONSE=> TPaaSAuthorizationFunction + + The TPaaS API would need to return back a composite signature data structure containing: + (See: https://github.com/onflow/fcl-js/blob/master/packages/fcl/src/wallet-provider-spec/authorization-function.md#how-to-create-an-authorization-function for more information on the purpose of each field) + { + addr: tpaasSignerAddress + keyId: tpaasSignerKeyID + signature: tpaasSignature // + } + **/ + return await fetch(appSigningURL, { + method: "POST", + headers: { "Content-Type": "application/json" } + body: JSON.stringify(signable) + }).then(res => res.json()) + } + } +} +``` + +The application would need to expose a API/webservice with POST routes for the +`appResolveAccountURL` and `appSigningURL` URLs the `TPaaSAuthorizationFunction` +would use. The application would need to attach an API Key / Secret to the request +it would then forward to your `TPaaS` + +The Client Application's backend might look something like: + +```javascript +// IN CLIENT APPLICATION BACKEND + +var express = require('express') +var app = express() + +app.post("/tpaas/resolveaccount", (req, res) => { + const account = req.body + const secretAPIKey = process.env.TPAAS_SECRET_API_KEY + + const resolvedAccount = await fetch("https://api.yourtpaas.com/resolveaccount", { + method: "POST", + headers: { + "Content-Type": "application/json" + "API_KEY": secretAPIKey + } + body: JSON.stringify(account) + }) + + res.send(resolvedAccount) +}) + +app.post("/tpaas/sign", (req, res) => { + const signable = req.body + const secretAPIKey = process.env.TPAAS_SECRET_API_KEY + + const compositeSignature = await fetch("https://api.yourtpaas.com/sign", { + method: "POST", + headers: { + "Content-Type": "application/json" + "API_KEY": secretAPIKey + } + body: JSON.stringify(signable) + }) + + res.send(compositeSignature) +}) +``` + +Your webservice would be responsible for implementing both pieces of functionality, +the ability to resolve an account and produce a composite signature. + +> To re-iterate, this is just an example implementation of what _might_ work. Your +implementation may vary extensively. + +Your TPaaS might choose to charge the client application a fee per transaction it signs. Because +in this example implementation it attaches a secret api key to each request to your TPaaS, +the TPaaS could record how many times each time it has done work for each client and bill accordingly. + +### Drawbacks + +Since the TPaaS would be responsible for paying for transactions, it should have suffient +security mechanisms in place to safeguard itself. Rate limiting, requiring +API keys to be sent with each request and other security mechanisms ought to be explored. + +### Dependencies + +The Transaction Fee Payer Service would depend on FCL. Should there be changes to any aspect +of FCL that may impact the TPaaS, the service would have to be updated accordingly. + +### Engineering Impact + +This is a large project. While a MVP could be built by an engineering team, a full solution +would likely require multiple participants and skillsets from product, engineering and design. + +### Tutorials and Examples + +* If design changes existing API or creates new ones, the design owner should create +end-to-end examples (ideally, a tutorial) which reflects how new feature will be used. +Some things to consider related to the tutorial: + - It should show the usage of the new feature in an end to end example + (i.e. from the browser to the execution node). + Many new features have unexpected effects in parts far away from the place of + change that can be found by running through an end-to-end example. + - This should be written as if it is documentation of the new feature, + i.e., consumable by a user, not a Flow contributor. + - The code does not need to work (since the feature is not implemented yet) + but the expectation is that the code does work before the feature can be merged. + +### Compatibility + +This project would need to maintain compatible with latest versions of FCL. + +## Questions and Discussion Topics + +- Are the other approches to desiging a FPaaS that differ from the example provided in this FLIP? \ No newline at end of file From 4628f212610f0afc8fe1099924c75ba47cdd39bf Mon Sep 17 00:00:00 2001 From: Jeffrey Doyle Date: Wed, 25 Aug 2021 12:02:49 -0700 Subject: [PATCH 02/16] FLIP: FCL Tx Payer Service --- flips/20210824-fcl-tx-payer-service.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flips/20210824-fcl-tx-payer-service.md b/flips/20210824-fcl-tx-payer-service.md index 68ac6337c..59b293963 100644 --- a/flips/20210824-fcl-tx-payer-service.md +++ b/flips/20210824-fcl-tx-payer-service.md @@ -2,7 +2,7 @@ | Status | Proposed | :-------------- |:---------------------------------------------------- | -| **FLIP #** | [NNN](https://github.com/onflow/flow/pull/NNN) (update when you have PR #)| +| **FLIP #** | [612](https://github.com/onflow/flow/pull/612) | | **Author(s)** | Jeffrey Doyle (jeffrey.doyle@dapperlabs.com), | | **Sponsor** | | | **Updated** | 2020-08-24 | From 233506ae295e72f3f2f28c6e28269accce57a8ce Mon Sep 17 00:00:00 2001 From: Jeffrey Doyle Date: Wed, 25 Aug 2021 12:15:38 -0700 Subject: [PATCH 03/16] FLIP: FCL Tx Payer Service --- flips/20210824-fcl-tx-payer-service.md | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/flips/20210824-fcl-tx-payer-service.md b/flips/20210824-fcl-tx-payer-service.md index 59b293963..57c0ad66e 100644 --- a/flips/20210824-fcl-tx-payer-service.md +++ b/flips/20210824-fcl-tx-payer-service.md @@ -20,14 +20,14 @@ payer for transactions that it receives. One of the features of Flow is that transaction fees can be payed by an account seperate from the authorizers of that transaction. Applications that wish to provide "free" transactions for their users could opt to pay for their users -transactions themselves. They would do this by produdcing a signature for the +transactions themselves. They would do this by producing a signature for the transaction as the payer for that transaction. Upon receiving this transaction, the Flow network would then deduct transaction fees from the payer, not any of the authorizers. Some FCL compatible Wallets choose to "swap out" the payer of a transaction they're requested to sign with a Flow account that the wallet controls (in the case that the -user's Flow account is specified as the transactions payer) to provide "free" transactions +users Flow account is specified as the transactions payer) to provide "free" transactions for their users. While this makes for a desireable user experience for those that use Wallets with this feature, applications do not get the guarantee that _all_ of their users will get this user experience. This is because users are able to use whichever FCL @@ -99,7 +99,7 @@ A Transaction Fee Payer Service would need to make available an Authorization Fu that applications could use in place of `fcl.currentUser().authorization` (or whatever other authorization function they use). -The applicationss transaction code might then look something like: +The applications transaction code might then look something like: ```javascript // IN CLIENT APPLICATIONS FRONTEND @@ -107,7 +107,7 @@ The applicationss transaction code might then look something like: import * as fcl from "@onflow/fcl" import { TPaaSAuthorizationFunction } from "@YourTPaaS/TPaaS-client" -const configuredTPasSAuthzFn = TPaaSAuthorizationFunction({ +const configuredTPaaSAuthzFn = TPaaSAuthorizationFunction({ ... TPaaS Configuration ... appResolveAccountURL: "https://api.myawesomedapp.com/tpaas/resolveaccount" // Example appSigningURL: "https://api.myawesomedapp.com/tpaas/sign" // Example @@ -117,7 +117,7 @@ const tx = await fcl.send([ fcl.transaction`... Cadence Code ...`, fcl.authorizers([fcl.currentUser().authorization]), fcl.proposer(fcl.currentUser().authorization), - fcl.payer(configuredTPasSAuthzFn) + fcl.payer(configuredTPaaSAuthzFn) ]).then(fcl.decode) ``` @@ -254,20 +254,6 @@ of FCL that may impact the TPaaS, the service would have to be updated according This is a large project. While a MVP could be built by an engineering team, a full solution would likely require multiple participants and skillsets from product, engineering and design. -### Tutorials and Examples - -* If design changes existing API or creates new ones, the design owner should create -end-to-end examples (ideally, a tutorial) which reflects how new feature will be used. -Some things to consider related to the tutorial: - - It should show the usage of the new feature in an end to end example - (i.e. from the browser to the execution node). - Many new features have unexpected effects in parts far away from the place of - change that can be found by running through an end-to-end example. - - This should be written as if it is documentation of the new feature, - i.e., consumable by a user, not a Flow contributor. - - The code does not need to work (since the feature is not implemented yet) - but the expectation is that the code does work before the feature can be merged. - ### Compatibility This project would need to maintain compatible with latest versions of FCL. From d8dc80bcfa1edbf2307f38f611ec6b13752cf675 Mon Sep 17 00:00:00 2001 From: Jeffrey Doyle Date: Wed, 25 Aug 2021 12:25:13 -0700 Subject: [PATCH 04/16] FLIP: FCL Tx Payer Service --- flips/20210824-fcl-tx-payer-service.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flips/20210824-fcl-tx-payer-service.md b/flips/20210824-fcl-tx-payer-service.md index 57c0ad66e..c60bc3c1d 100644 --- a/flips/20210824-fcl-tx-payer-service.md +++ b/flips/20210824-fcl-tx-payer-service.md @@ -13,7 +13,7 @@ FCL provides a mechanism for a transactions fees to be payed by an account other than the proposer or any of the authorizers of a transaction. A transaction payer service would be a service that facilitates signing as the -payer for transactions that it receives. +payer for a given transaction. ## Motivation From 7e49ce9af55cb70ea423e229f60c085bfdfe4bb0 Mon Sep 17 00:00:00 2001 From: Jeffrey Doyle Date: Wed, 25 Aug 2021 12:34:18 -0700 Subject: [PATCH 05/16] FLIP: FCL Tx Payer Service --- flips/20210824-fcl-tx-payer-service.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/flips/20210824-fcl-tx-payer-service.md b/flips/20210824-fcl-tx-payer-service.md index c60bc3c1d..538df7709 100644 --- a/flips/20210824-fcl-tx-payer-service.md +++ b/flips/20210824-fcl-tx-payer-service.md @@ -244,6 +244,10 @@ Since the TPaaS would be responsible for paying for transactions, it should have security mechanisms in place to safeguard itself. Rate limiting, requiring API keys to be sent with each request and other security mechanisms ought to be explored. +Since tranaction fees can be dynamic, increasing depending on the amount of work the network +would need to perform for each transaction, billing for the service must be smart enough +to take this into consideration. + ### Dependencies The Transaction Fee Payer Service would depend on FCL. Should there be changes to any aspect From 12c1102007bb813ac85f9afe6ca84fded39dd99f Mon Sep 17 00:00:00 2001 From: Jeffrey Doyle Date: Thu, 26 Aug 2021 10:05:12 -0700 Subject: [PATCH 06/16] FLIP: FCL Tx Payer Service --- flips/20210824-fcl-tx-payer-service.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flips/20210824-fcl-tx-payer-service.md b/flips/20210824-fcl-tx-payer-service.md index 538df7709..d3521b9c3 100644 --- a/flips/20210824-fcl-tx-payer-service.md +++ b/flips/20210824-fcl-tx-payer-service.md @@ -31,7 +31,7 @@ users Flow account is specified as the transactions payer) to provide "free" tra for their users. While this makes for a desireable user experience for those that use Wallets with this feature, applications do not get the guarantee that _all_ of their users will get this user experience. This is because users are able to use whichever FCL -compatible wallet they desire to the FCL compatible applications they use, and not +compatible wallet they desire with the FCL compatible applications they use, and not all FCL compatible wallets will strictly choose to provide this feature. Applications that wish to guarantee that their users are able to submit transactions @@ -92,7 +92,7 @@ The line `fcl.currentUser().authorization` is an "Authorization Function". (Read More about Authorization Functions: https://github.com/onflow/fcl-js/blob/master/packages/fcl/src/wallet-provider-spec/authorization-function.md) The implementation of this Authorization Function is what FCL uses to gather a -signature from the Current User of the applcation for each of the roles of the +signature from the Current User of the application for each of the roles of the transaction (Proposer, Payer and Authorizer). A Transaction Fee Payer Service would need to make available an Authorization Function From 9213fbd2c37a0e0ce8cbe7959e7f658c9a12670c Mon Sep 17 00:00:00 2001 From: Jeffrey Doyle Date: Thu, 26 Aug 2021 10:12:50 -0700 Subject: [PATCH 07/16] FLIP: FCL Tx Payer Service --- flips/20210824-fcl-tx-payer-service.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/flips/20210824-fcl-tx-payer-service.md b/flips/20210824-fcl-tx-payer-service.md index d3521b9c3..a4ffc8145 100644 --- a/flips/20210824-fcl-tx-payer-service.md +++ b/flips/20210824-fcl-tx-payer-service.md @@ -185,7 +185,7 @@ export const TPaaSAuthorizationFunction = ({ appResolveAccountURL, appSigningURL The application would need to expose a API/webservice with POST routes for the `appResolveAccountURL` and `appSigningURL` URLs the `TPaaSAuthorizationFunction` would use. The application would need to attach an API Key / Secret to the request -it would then forward to your `TPaaS` +it would then forward to your `TPaaS`. The Client Application's backend might look something like: @@ -234,6 +234,11 @@ the ability to resolve an account and produce a composite signature. > To re-iterate, this is just an example implementation of what _might_ work. Your implementation may vary extensively. +The TPaaS would be responsible for maintaining a collection of Flow Accounts that it will +use to pay for each of the transactions it signs for. It should monitor that each of the Flow +Accounts it controlls have a suitible FLOW balance such that they can cover the transaction fees +they needs to pay. + Your TPaaS might choose to charge the client application a fee per transaction it signs. Because in this example implementation it attaches a secret api key to each request to your TPaaS, the TPaaS could record how many times each time it has done work for each client and bill accordingly. From 044627a23c1a0927e54973eafb0be9269ddffc2a Mon Sep 17 00:00:00 2001 From: Jeffrey Doyle Date: Thu, 26 Aug 2021 10:13:45 -0700 Subject: [PATCH 08/16] FLIP: FCL Tx Payer Service --- flips/20210824-fcl-tx-payer-service.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flips/20210824-fcl-tx-payer-service.md b/flips/20210824-fcl-tx-payer-service.md index a4ffc8145..dd88f717b 100644 --- a/flips/20210824-fcl-tx-payer-service.md +++ b/flips/20210824-fcl-tx-payer-service.md @@ -65,7 +65,7 @@ value as revenue. ## Design Proposal -> Disclaimer: The Following is an example implementation for what _might_ work. All possible solutions are not limited to just the following. You're completely free to approach the problem however you choose, even if it differs from the thoughts below. +> Disclaimer: The following is an example implementation for what _might_ work. All possible solutions are not limited to just the following. You're completely free to approach the problem however you choose, even if it differs from the thoughts below. When creating a transasction to be sent to the blockchain, FCL expects authorization functions for each of the roles for that transaction to be specified. From ef3db354c0be3c9848b8568b7d20d06533196553 Mon Sep 17 00:00:00 2001 From: Jeffrey Doyle Date: Thu, 26 Aug 2021 11:14:39 -0700 Subject: [PATCH 09/16] FLIP: FCL Tx Payer Service --- flips/20210824-fcl-tx-payer-service.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flips/20210824-fcl-tx-payer-service.md b/flips/20210824-fcl-tx-payer-service.md index dd88f717b..b47161ab4 100644 --- a/flips/20210824-fcl-tx-payer-service.md +++ b/flips/20210824-fcl-tx-payer-service.md @@ -260,12 +260,12 @@ of FCL that may impact the TPaaS, the service would have to be updated according ### Engineering Impact -This is a large project. While a MVP could be built by an engineering team, a full solution +This is a large project. While an MVP could be built by an engineering team, a full solution would likely require multiple participants and skillsets from product, engineering and design. ### Compatibility -This project would need to maintain compatible with latest versions of FCL. +This project would need to maintain compatibility with latest versions of FCL. ## Questions and Discussion Topics From 774d4bb4ef356203d80500bcecc14be5a74f00c8 Mon Sep 17 00:00:00 2001 From: Layne Lafrance Date: Wed, 1 Sep 2021 14:40:07 -0700 Subject: [PATCH 10/16] Update 20210824-fcl-tx-payer-service.md Small spelling changes and comments on how to think about business model/ who would run this --- flips/20210824-fcl-tx-payer-service.md | 37 +++++++++++++------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/flips/20210824-fcl-tx-payer-service.md b/flips/20210824-fcl-tx-payer-service.md index b47161ab4..c60512925 100644 --- a/flips/20210824-fcl-tx-payer-service.md +++ b/flips/20210824-fcl-tx-payer-service.md @@ -9,7 +9,7 @@ ## Objective -FCL provides a mechanism for a transactions fees to be payed by an account +FCL provides a mechanism for a transactions fees to be paid by an account other than the proposer or any of the authorizers of a transaction. A transaction payer service would be a service that facilitates signing as the @@ -17,34 +17,33 @@ payer for a given transaction. ## Motivation -One of the features of Flow is that transaction fees can be payed by an account -seperate from the authorizers of that transaction. Applications that wish to -provide "free" transactions for their users could opt to pay for their users +One of the features of Flow is that transaction fees can be paid by an account +separate from the authorizers of that transaction. Applications that wish to +provide "free" transactions for their users could opt to pay for their users' transactions themselves. They would do this by producing a signature for the transaction as the payer for that transaction. Upon receiving this transaction, the Flow network would then deduct transaction fees from the payer, not any of the authorizers. -Some FCL compatible Wallets choose to "swap out" the payer of a transaction they're +Some FCL compatible wallets choose to "swap out" the payer of a transaction they're requested to sign with a Flow account that the wallet controls (in the case that the users Flow account is specified as the transactions payer) to provide "free" transactions -for their users. While this makes for a desireable user experience for those that use -Wallets with this feature, applications do not get the guarantee that _all_ of their +for their users.[<-clarity needed] While this makes for a desirable user experience for those that use +wallets with this feature, applications do not get the guarantee that _all_ of their users will get this user experience. This is because users are able to use whichever FCL compatible wallet they desire with the FCL compatible applications they use, and not all FCL compatible wallets will strictly choose to provide this feature. -Applications that wish to guarantee that their users are able to submit transactions -without having to pay for them will then have to always pay for them themselves. These -applications would then have to build a "Transaction Fee Payer Service" that is able +Applications that wish to guarantee their users can submit transactions +without having to pay for them will pay those fees themselves. To pay these fees, it's expected that applications would need to build a "Transaction Fee Payer Service" that is able to receive a transaction signable and then produce a signature for it. -We imagine that there is a potential business model here. A transaction fee payer + ## User Benefit @@ -59,9 +58,9 @@ to provide the best user experiences will likely choose to allow their users to transactions without them having to pay. This Transaction Fee Payer Service will enable more applications to be able to provide -this higher level of user experience. Creating value for the application, the users of -that application, and the potential for the tranaction fee payer service to capture some of that -value as revenue. +this higher level of user experience. ## Design Proposal @@ -235,13 +234,13 @@ the ability to resolve an account and produce a composite signature. implementation may vary extensively. The TPaaS would be responsible for maintaining a collection of Flow Accounts that it will -use to pay for each of the transactions it signs for. It should monitor that each of the Flow +use to pay for each of the transactions it signs for. It should monitor that each of the Flow Accounts it controlls have a suitible FLOW balance such that they can cover the transaction fees they needs to pay. Your TPaaS might choose to charge the client application a fee per transaction it signs. Because in this example implementation it attaches a secret api key to each request to your TPaaS, -the TPaaS could record how many times each time it has done work for each client and bill accordingly. +the TPaaS could record how many times each time it has done work for each client and bill accordingly. ### Drawbacks @@ -269,4 +268,4 @@ This project would need to maintain compatibility with latest versions of FCL. ## Questions and Discussion Topics -- Are the other approches to desiging a FPaaS that differ from the example provided in this FLIP? \ No newline at end of file +- Are the other approches to desiging a FPaaS that differ from the example provided in this FLIP? From 7413ff0848cfd9e6df2b98d138d8c951a79d69f0 Mon Sep 17 00:00:00 2001 From: Jeffrey Doyle Date: Fri, 3 Sep 2021 17:21:40 -0700 Subject: [PATCH 11/16] FLIP: FCL Tx Payer Service updates according to feedback --- flips/20210824-fcl-tx-payer-service.md | 234 ++++++++++++++++--------- 1 file changed, 147 insertions(+), 87 deletions(-) diff --git a/flips/20210824-fcl-tx-payer-service.md b/flips/20210824-fcl-tx-payer-service.md index c60512925..97ec52e4b 100644 --- a/flips/20210824-fcl-tx-payer-service.md +++ b/flips/20210824-fcl-tx-payer-service.md @@ -5,15 +5,16 @@ | **FLIP #** | [612](https://github.com/onflow/flow/pull/612) | | **Author(s)** | Jeffrey Doyle (jeffrey.doyle@dapperlabs.com), | | **Sponsor** | | -| **Updated** | 2020-08-24 | +| **Updated** | 2020-09-03 | ## Objective FCL provides a mechanism for a transactions fees to be paid by an account other than the proposer or any of the authorizers of a transaction. -A transaction payer service would be a service that facilitates signing as the -payer for a given transaction. +A Transaction Payer Service would be a service that facilitates signing as the +payer for a given transaction. It would exist as a resusable codebase / set of +packages & tools that could be used by the applications that desire it. ## Motivation @@ -25,25 +26,26 @@ transaction as the payer for that transaction. Upon receiving this transaction, the Flow network would then deduct transaction fees from the payer, not any of the authorizers. -Some FCL compatible wallets choose to "swap out" the payer of a transaction they're -requested to sign with a Flow account that the wallet controls (in the case that the -users Flow account is specified as the transactions payer) to provide "free" transactions -for their users.[<-clarity needed] While this makes for a desirable user experience for those that use +Some FCL compatible wallets choose to act as the payer of the transactions their users +sign. They do this by swapping out their users account with an account of their own, +if their users are specified as the payer of a transaction. This allows wallets with +this feature to provide "free" transactions for their users. + +While this makes for a desirable user experience for those that use wallets with this feature, applications do not get the guarantee that _all_ of their users will get this user experience. This is because users are able to use whichever FCL compatible wallet they desire with the FCL compatible applications they use, and not all FCL compatible wallets will strictly choose to provide this feature. Applications that wish to guarantee their users can submit transactions -without having to pay for them will pay those fees themselves. To pay these fees, it's expected that applications would need to build a "Transaction Fee Payer Service" that is able +without having to pay transaction fees for them will then have to pay those fees themselves. +To pay trans action fees, it's expected that applications would need to build a Transaction Payer Service that is able to receive a transaction signable and then produce a signature for it. - +A re-usable Transaction Payer Service would exist as project / set of packages and tools that applications could use +instead of having to build an implementation of them themselves. These re-usable components +would lower the overall complexity and effort required of a developer who wishes that +their application provide "free" transactions for their users. ## User Benefit @@ -57,14 +59,12 @@ Users have been conditioned to expect things for free. As such, applications tha to provide the best user experiences will likely choose to allow their users to execute transactions without them having to pay. -This Transaction Fee Payer Service will enable more applications to be able to provide -this higher level of user experience. +This Transaction Fee Payer Service will enable more applications to be more easily able to provide +a higher level of user experience. ## Design Proposal -> Disclaimer: The following is an example implementation for what _might_ work. All possible solutions are not limited to just the following. You're completely free to approach the problem however you choose, even if it differs from the thoughts below. +> Disclaimer: The following is an example implementation for what _might_ work. All possible solutions are not limited to just the following. You're completely free to approach the problem however you choose, even if it differs extensively from the thoughts below. When creating a transasction to be sent to the blockchain, FCL expects authorization functions for each of the roles for that transaction to be specified. @@ -103,50 +103,52 @@ The applications transaction code might then look something like: ```javascript // IN CLIENT APPLICATIONS FRONTEND +// Note: "TPS" <=> "Transaction Payer Service" + import * as fcl from "@onflow/fcl" -import { TPaaSAuthorizationFunction } from "@YourTPaaS/TPaaS-client" +import { TPSAuthorizationFunction } from "@TPS/TPS-client" -const configuredTPaaSAuthzFn = TPaaSAuthorizationFunction({ - ... TPaaS Configuration ... - appResolveAccountURL: "https://api.myawesomedapp.com/tpaas/resolveaccount" // Example - appSigningURL: "https://api.myawesomedapp.com/tpaas/sign" // Example +const configuredTPSAuthzFn = TPSAuthorizationFunction({ + ... TPS Configuration ... + resolveAccountURL: "https://api.myawesomedapp.com/tps/resolveaccount" // Example + signingURL: "https://api.myawesomedapp.com/tps/sign" // Example }) const tx = await fcl.send([ fcl.transaction`... Cadence Code ...`, fcl.authorizers([fcl.currentUser().authorization]), fcl.proposer(fcl.currentUser().authorization), - fcl.payer(configuredTPaaSAuthzFn) + fcl.payer(configuredTPSAuthzFn) ]).then(fcl.decode) ``` -The implementation of `TPaaSAuthorizationFunction` will need to conform to how FCL expects +The implementation of `TPSAuthorizationFunction` will need to conform to how FCL expects Authorization Functions to work. (Read More about Authorization Functions: https://github.com/onflow/fcl-js/blob/master/packages/fcl/src/wallet-provider-spec/authorization-function.md) -The implementation of the `TPaaSAuthorizationFunction` might look something like: +The implementation of the `TPSAuthorizationFunction` might look something like: ```javascript -// IN "@YourTPaaS/TPaaS-client" +// IN "@TPS/TPS-client" -export const TPaaSAuthorizationFunction = ({ appResolveAccountURL, appSigningURL }) => async (account) => { +export const TPSAuthorizationFunction = ({ appResolveAccountURL, appSigningURL }) => async (account) => { /** - Perform a network call to resolve the FCL "account" data structure. This network call will likely be to the client applcation's backend, which would likely attach some secret authentication information (API Key etc) to the request before sending it off to the TPaaS API. + Perform a network call to resolve the FCL "account" data structure. This network call will likely be to the client applcation's backend. The call graph might look something like: - Client App TPaaSAuthorizationFunction =POST=> Client Backend =POST=> TPaaS API =POST RESPONSE=> Client Backend =POST RESPONSE=> TPaaSAuthorizationFunction + Client App TPSAuthorizationFunction =POST=> Client Backend =POST=> TPS API =POST RESPONSE=> Client Backend =POST RESPONSE=> TPSAuthorizationFunction - The TPaaS API would need to return back an account data structure containing: + The TPS API would need to return back an account data structure containing: (See: https://github.com/onflow/fcl-js/blob/master/packages/fcl/src/wallet-provider-spec/authorization-function.md#how-to-create-an-authorization-function for more information on the purpose of each field) { ...account, - tempId: tpaasSessionID - addr: tpaasSignerAddress - keyId: tpaasSignerKeyID + tempId: tpsTempID + addr: tpsPayerAddress + keyId: tpsPayerKeyID } **/ - const resolvedAccount = await fetch(appResolveAccountURL, { + const resolvedAccount = await fetch(resolveAccountURL, { method: "POST", headers: { "Content-Type": "application/json" } body: JSON.stringify(account) @@ -157,21 +159,21 @@ export const TPaaSAuthorizationFunction = ({ appResolveAccountURL, appSigningURL ...resolvedAccount, signingFunction: async signable => { /** - Perform a network call to resolve the transaction signature. This network call will likely be to the client applcation's backend, which would likely attach some secret authentication information (API Key etc) to the request before sending it off to the TPaaS API. + Perform a network call to resolve the transaction signature. This network call will likely be to the client applcation's backend. The call graph might look something like: - Client App TPaaSAuthorizationFunction =POST=> Client Backend =POST=> TPaaS API =POST RESPONSE=> Client Backend =POST RESPONSE=> TPaaSAuthorizationFunction + Client App TPSAuthorizationFunction =POST=> Client Backend =POST Response=> Client App TPSAuthorizationFunction - The TPaaS API would need to return back a composite signature data structure containing: + The TPS API would need to return back a composite signature data structure containing: (See: https://github.com/onflow/fcl-js/blob/master/packages/fcl/src/wallet-provider-spec/authorization-function.md#how-to-create-an-authorization-function for more information on the purpose of each field) { - addr: tpaasSignerAddress - keyId: tpaasSignerKeyID - signature: tpaasSignature // + addr: tpsSignerAddress + keyId: tpsSignerKeyID + signature: TPSSignature } **/ - return await fetch(appSigningURL, { + return await fetch(signingURL, { method: "POST", headers: { "Content-Type": "application/json" } body: JSON.stringify(signable) @@ -181,86 +183,144 @@ export const TPaaSAuthorizationFunction = ({ appResolveAccountURL, appSigningURL } ``` -The application would need to expose a API/webservice with POST routes for the -`appResolveAccountURL` and `appSigningURL` URLs the `TPaaSAuthorizationFunction` -would use. The application would need to attach an API Key / Secret to the request -it would then forward to your `TPaaS`. +The application would need to expose an API/webservice with POST routes for the +`resolveAccountURL` and `signingURL` URLs the `TPSAuthorizationFunction` +would use. The application would need to perform the actions of resolving the account, +or signing the signable for those routes respectively. The Client Application's backend might look something like: ```javascript // IN CLIENT APPLICATION BACKEND +import { TPSAccountResolver, TPSSigner } from "@TPS/TPS-server" var express = require('express') var app = express() -app.post("/tpaas/resolveaccount", (req, res) => { +const TPSConfiguration = { + accounts: [ + { + address: "0xABC123", + privateKey: process.env.ACCOUNT_ABC123_PRIVATE_KEY_HEX, + keyIndex: 0 + }, + { + address: "0x456DEF", + privateKey: process.env.ACCOUNT_456DEF_PRIVATE_KEY_HEX, + keyIndex: 0 + } + ], +} + +const configuredTPSAccountResolver = await TPSAccountResolver(TPSConfiguration) +const configuredTPSSigner = await TPSSigner(TPSConfiguration) + +app.post("/tps/resolveaccount", (req, res) => { const account = req.body - const secretAPIKey = process.env.TPAAS_SECRET_API_KEY - const resolvedAccount = await fetch("https://api.yourtpaas.com/resolveaccount", { - method: "POST", - headers: { - "Content-Type": "application/json" - "API_KEY": secretAPIKey - } - body: JSON.stringify(account) - }) + const resolvedAccount = await configuredTPSAccountResolver(account) res.send(resolvedAccount) }) -app.post("/tpaas/sign", (req, res) => { +app.post("/tps/sign", (req, res) => { const signable = req.body - const secretAPIKey = process.env.TPAAS_SECRET_API_KEY - const compositeSignature = await fetch("https://api.yourtpaas.com/sign", { - method: "POST", - headers: { - "Content-Type": "application/json" - "API_KEY": secretAPIKey - } - body: JSON.stringify(signable) - }) + const compositeSignature = await configuredTPSSigner(signable) res.send(compositeSignature) }) ``` -Your webservice would be responsible for implementing both pieces of functionality, -the ability to resolve an account and produce a composite signature. +The applications backend might consume a package, `"@TPS/TPS-server"`, which would ideally expose some helpful utilities +for handling the logic behind resolving an account and producing a payer signature. The above code snippet uses these +potential utilities. -> To re-iterate, this is just an example implementation of what _might_ work. Your -implementation may vary extensively. +The implementation of these utilities, `TPSAccountResolver` and `TPSSigner` might look like: + +```javascript +// IN @TPS/TPS-server + +import {WalletUtils} from "@onflow/fcl" + +export const TPSAccountResolver = ({ accounts }) => (account) => { -The TPaaS would be responsible for maintaining a collection of Flow Accounts that it will -use to pay for each of the transactions it signs for. It should monitor that each of the Flow -Accounts it controlls have a suitible FLOW balance such that they can cover the transaction fees -they needs to pay. + // Select an available account address from those provided in the configuration. + const tpsPayerAddress = selectAvailableAddress(accounts) -Your TPaaS might choose to charge the client application a fee per transaction it signs. Because -in this example implementation it attaches a secret api key to each request to your TPaaS, -the TPaaS could record how many times each time it has done work for each client and bill accordingly. + // Select the Key ID of thekey on the Payer account that will be used during signing. + const tpsPayerKeyID = selectKeyID(tpsPayerAddress, accounts) + + // Produce a unique identifier for this payer address and key id combination. + const tpsTempID = `${tpsPayerAddress}-${tpsPayerKeyID}` + + return ({ + ...account, + tempId: tpsTempID + addr: tpsPayerAddress + keyId: tpsPayerKeyID + }) +} + +export const TPSSigner = ({ accounts }) => (signable) => { + // Get the address specified in the signable as the payer. + const signablePayerAddress = signable.voucher.payer + + // Get the account specified in the TPSSigner configuration that corresponds + // to the signablePayerAdress. + const account = accounts.find(a => a.address === signablePayerAddress) + + // Throw an error if the signablePayerAddress is not found in the TPSSigner configuration + if (!account) { + throw new Error(`TPSSigner Error: Could not find account for signablePayerAddress=${signablePayerAddress}`) + } + + // For security, ensure that `signablePayerAddress` is not specified as a transaction authorizer or proposer. + if (signable.voucher.authorizers.includes(signablePayerAddress)) { + throw new Error( + `TPSSigner Error: signablePayerAddress=${signablePayerAddress} specified as a transaction authorizer in transaction signable.` + ) + } + + if (signable.voucher.proposalKey.address === signablePayerAddress) { + throw new Error( + `TPSSigner Error: signablePayerAddress=${signablePayerAddress} specified as the transaction proposer in transaction signable.` + ) + } + + // Encode the signable using WalletUtils + const encodedMessage = WalletUtils.encodeMessageFromSignable(signable, signablePayerAddress) + + // Prodce a signature, as a hex string + const signature = toHexString(YourFavouriteCryptoUtility.sign(encodedMessage, account.privateKey)) + + return signature +} +``` + +The user of the TPS utility would be responsible for maintaining that the accounts they specify into `TPSAccountResolver` and `TPSSigner` as configuration have a sufficient FLOW balance at all times to pay for transactions they sign for. + +> To re-iterate, this is just an example implementation of what _might_ work. Your +implementation may vary extensively. ### Drawbacks -Since the TPaaS would be responsible for paying for transactions, it should have suffient -security mechanisms in place to safeguard itself. Rate limiting, requiring -API keys to be sent with each request and other security mechanisms ought to be explored. +Since the TPS would be responsible for paying for transactions, it should have suffient +security mechanisms in place to safeguard itself. Since tranaction fees can be dynamic, increasing depending on the amount of work the network -would need to perform for each transaction, billing for the service must be smart enough -to take this into consideration. +would need to perform for each transaction, the payer service should be smart enough to take +this into consideration as needed. ### Dependencies The Transaction Fee Payer Service would depend on FCL. Should there be changes to any aspect -of FCL that may impact the TPaaS, the service would have to be updated accordingly. +of FCL that may impact the TPS, the service would have to be updated accordingly. ### Engineering Impact -This is a large project. While an MVP could be built by an engineering team, a full solution -would likely require multiple participants and skillsets from product, engineering and design. +This is a rather large project. While an MVP could be built by an engineering team, a full solution +would likely require multiple participants and skillsets. ### Compatibility @@ -268,4 +328,4 @@ This project would need to maintain compatibility with latest versions of FCL. ## Questions and Discussion Topics -- Are the other approches to desiging a FPaaS that differ from the example provided in this FLIP? +- Are the other approches to desiging a TPS that differ from the example provided in this FLIP? From 18883cbbf0627a24bdc917d9dfab62b465261bbe Mon Sep 17 00:00:00 2001 From: Jeffrey Doyle Date: Tue, 7 Sep 2021 08:44:34 -0700 Subject: [PATCH 12/16] FLIP: FCL Tx Payer Service updates according to feedback --- flips/20210824-fcl-tx-payer-service.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/flips/20210824-fcl-tx-payer-service.md b/flips/20210824-fcl-tx-payer-service.md index 97ec52e4b..574796f42 100644 --- a/flips/20210824-fcl-tx-payer-service.md +++ b/flips/20210824-fcl-tx-payer-service.md @@ -39,7 +39,7 @@ all FCL compatible wallets will strictly choose to provide this feature. Applications that wish to guarantee their users can submit transactions without having to pay transaction fees for them will then have to pay those fees themselves. -To pay trans action fees, it's expected that applications would need to build a Transaction Payer Service that is able +To pay transaction fees, it's expected that applications would need to build a Transaction Payer Service that is able to receive a transaction signable and then produce a signature for it. A re-usable Transaction Payer Service would exist as project / set of packages and tools that applications could use @@ -64,7 +64,7 @@ a higher level of user experience. ## Design Proposal -> Disclaimer: The following is an example implementation for what _might_ work. All possible solutions are not limited to just the following. You're completely free to approach the problem however you choose, even if it differs extensively from the thoughts below. +> Disclaimer: The following is an example implementation for what _might_ work. All possible solutions are not limited to just the following. You're completely free and encouraged to approach the problem however you choose, even if it differs extensively from the thoughts below. When creating a transasction to be sent to the blockchain, FCL expects authorization functions for each of the roles for that transaction to be specified. @@ -130,14 +130,14 @@ The implementation of the `TPSAuthorizationFunction` might look something like: ```javascript // IN "@TPS/TPS-client" -export const TPSAuthorizationFunction = ({ appResolveAccountURL, appSigningURL }) => async (account) => { +export const TPSAuthorizationFunction = ({ resolveAccountURL, signingURL }) => async (account) => { /** Perform a network call to resolve the FCL "account" data structure. This network call will likely be to the client applcation's backend. The call graph might look something like: - Client App TPSAuthorizationFunction =POST=> Client Backend =POST=> TPS API =POST RESPONSE=> Client Backend =POST RESPONSE=> TPSAuthorizationFunction + Client App TPSAuthorizationFunction =POST=> Client Backend =POST Response=> Client App TPSAuthorizationFunction The TPS API would need to return back an account data structure containing: (See: https://github.com/onflow/fcl-js/blob/master/packages/fcl/src/wallet-provider-spec/authorization-function.md#how-to-create-an-authorization-function for more information on the purpose of each field) @@ -170,7 +170,7 @@ export const TPSAuthorizationFunction = ({ appResolveAccountURL, appSigningURL } { addr: tpsSignerAddress keyId: tpsSignerKeyID - signature: TPSSignature + signature: tpsSignature } **/ return await fetch(signingURL, { From 72e3b814fbf5ad7313c740fafba5691f68b3c40b Mon Sep 17 00:00:00 2001 From: Jeffrey Doyle Date: Thu, 9 Sep 2021 07:22:15 -0700 Subject: [PATCH 13/16] FLIP: FCL Tx Payer Service updates according to feedback --- flips/20210824-fcl-tx-payer-service.md | 52 +++++++++++++++++--------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/flips/20210824-fcl-tx-payer-service.md b/flips/20210824-fcl-tx-payer-service.md index 574796f42..1a16c51ca 100644 --- a/flips/20210824-fcl-tx-payer-service.md +++ b/flips/20210824-fcl-tx-payer-service.md @@ -5,37 +5,53 @@ | **FLIP #** | [612](https://github.com/onflow/flow/pull/612) | | **Author(s)** | Jeffrey Doyle (jeffrey.doyle@dapperlabs.com), | | **Sponsor** | | -| **Updated** | 2020-09-03 | +| **Updated** | 2020-09-09 | ## Objective -FCL provides a mechanism for a transactions fees to be paid by an account -other than the proposer or any of the authorizers of a transaction. +There is a need for the developement of a suite of tools that will collectively +act as a Transaction Fee Payer Service for use by applications built on Flow. -A Transaction Payer Service would be a service that facilitates signing as the -payer for a given transaction. It would exist as a resusable codebase / set of +Technically, this suite of tools will facilitate signing as the payer of transactions initiated +by application's users with an account controlled by the application itself. + +From a functional perspective, the service would enable users of applications that employ +these tools to be able to execute transactions, and thereby interact with Flow, without +encurrign the burden of having to pay for their transaction fees themselves. This user experience +is valuable for many reasons, including enabling users to engague with applications while not +having _any_ FLOW in their account, or having to worry about having _enough_ FLOW to execute the +transactions they wish to execute. + +A Transaction Fee Payer Service would exist as a resusable codebase / set of packages & tools that could be used by the applications that desire it. ## Motivation -One of the features of Flow is that transaction fees can be paid by an account -separate from the authorizers of that transaction. Applications that wish to +One of the many features of Flow is that transaction fees can be paid by an account +separate from the authorizers of that transaction. With this feature, applications that wish to provide "free" transactions for their users could opt to pay for their users' transactions themselves. They would do this by producing a signature for the -transaction as the payer for that transaction. Upon receiving this transaction, +transaction as the payer of that transaction. Upon receiving this transaction, the Flow network would then deduct transaction fees from the payer, not any of the authorizers. +For a user to pay their transaction fees themselves, that user would have to have FLOW +in their account. This means that should a first time Flow user attempt to engague with an application +that does not employ a Transaction Payer Service, they would first need to acquire FLOW before +engaguing with that application. This is an undesireable user experience, as it significantly raises +the friction required of first time Flow users to engague with these applications. Many users who are unfamiliar +with blockchain applications will not understand or sympathize with this requirement. Thus it is crucial +that applications who wish to provide a low friction experience for their users to pay transaction fees on +their behalf. + Some FCL compatible wallets choose to act as the payer of the transactions their users sign. They do this by swapping out their users account with an account of their own, if their users are specified as the payer of a transaction. This allows wallets with -this feature to provide "free" transactions for their users. - -While this makes for a desirable user experience for those that use -wallets with this feature, applications do not get the guarantee that _all_ of their -users will get this user experience. This is because users are able to use whichever FCL -compatible wallet they desire with the FCL compatible applications they use, and not -all FCL compatible wallets will strictly choose to provide this feature. +this feature to provide "free" transactions for their users. While this makes for a +desirable user experience for those that use wallets with this feature, applications do not +get the guarantee that _all_ of their users will get this user experience. +This is because users are able to use whichever FCL compatible wallet they desire with the FCL +compatible applications they use, and not all FCL compatible wallets will strictly choose to provide this feature. Applications that wish to guarantee their users can submit transactions without having to pay transaction fees for them will then have to pay those fees themselves. @@ -59,8 +75,8 @@ Users have been conditioned to expect things for free. As such, applications tha to provide the best user experiences will likely choose to allow their users to execute transactions without them having to pay. -This Transaction Fee Payer Service will enable more applications to be more easily able to provide -a higher level of user experience. +This Transaction Fee Payer Service will enable more applications to offer a top of the line user experience, where users can easily and seamlessly engage in any action without the friction of having to pay +for any transaction fees. ## Design Proposal @@ -317,6 +333,8 @@ this into consideration as needed. The Transaction Fee Payer Service would depend on FCL. Should there be changes to any aspect of FCL that may impact the TPS, the service would have to be updated accordingly. +The Flow team communicates out all breaking changes to the community consistently and with sufficient time to make any updates. + ### Engineering Impact This is a rather large project. While an MVP could be built by an engineering team, a full solution From a05c0f9bde8859cf5f500a28184900d7712de0dd Mon Sep 17 00:00:00 2001 From: Jeffrey Doyle Date: Mon, 13 Sep 2021 06:12:34 -0700 Subject: [PATCH 14/16] FLIP: FCL Tx Payer Service updates according to feedback --- flips/20210824-fcl-tx-payer-service.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flips/20210824-fcl-tx-payer-service.md b/flips/20210824-fcl-tx-payer-service.md index 1a16c51ca..c18d2498d 100644 --- a/flips/20210824-fcl-tx-payer-service.md +++ b/flips/20210824-fcl-tx-payer-service.md @@ -17,8 +17,8 @@ by application's users with an account controlled by the application itself. From a functional perspective, the service would enable users of applications that employ these tools to be able to execute transactions, and thereby interact with Flow, without -encurrign the burden of having to pay for their transaction fees themselves. This user experience -is valuable for many reasons, including enabling users to engague with applications while not +encurring the burden of having to pay for their transaction fees themselves. This user experience +is valuable for many reasons, including enabling users to engauge with applications while not having _any_ FLOW in their account, or having to worry about having _enough_ FLOW to execute the transactions they wish to execute. @@ -36,10 +36,10 @@ the Flow network would then deduct transaction fees from the payer, not any of the authorizers. For a user to pay their transaction fees themselves, that user would have to have FLOW -in their account. This means that should a first time Flow user attempt to engague with an application -that does not employ a Transaction Payer Service, they would first need to acquire FLOW before -engaguing with that application. This is an undesireable user experience, as it significantly raises -the friction required of first time Flow users to engague with these applications. Many users who are unfamiliar +in their account. This can lead to a situation where a first-time Flow user attempts to engage with an application +that does not employ a Transaction Payer Service, and they would first need to acquire FLOW before engaging with +that application. This is an undesireable user experience, as it significantly raises +the friction required of first time Flow users to engauge with these applications. Many users who are unfamiliar with blockchain applications will not understand or sympathize with this requirement. Thus it is crucial that applications who wish to provide a low friction experience for their users to pay transaction fees on their behalf. From e7d67effea7ddbcbb22924f1f6b2c1ac9d6eef5b Mon Sep 17 00:00:00 2001 From: Layne Lafrance Date: Mon, 13 Sep 2021 07:55:24 -0700 Subject: [PATCH 15/16] Update 20210824-fcl-tx-payer-service.md --- flips/20210824-fcl-tx-payer-service.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/flips/20210824-fcl-tx-payer-service.md b/flips/20210824-fcl-tx-payer-service.md index c18d2498d..a8019d1f5 100644 --- a/flips/20210824-fcl-tx-payer-service.md +++ b/flips/20210824-fcl-tx-payer-service.md @@ -39,9 +39,9 @@ For a user to pay their transaction fees themselves, that user would have to hav in their account. This can lead to a situation where a first-time Flow user attempts to engage with an application that does not employ a Transaction Payer Service, and they would first need to acquire FLOW before engaging with that application. This is an undesireable user experience, as it significantly raises -the friction required of first time Flow users to engauge with these applications. Many users who are unfamiliar -with blockchain applications will not understand or sympathize with this requirement. Thus it is crucial -that applications who wish to provide a low friction experience for their users to pay transaction fees on +the friction required of first time Flow users to engage with these applications. Many users who are unfamiliar +with blockchain applications will not understand or empathize with this requirement. Thus it is crucial +that applications who wish to provide a low friction experience for their users pay transaction fees on their behalf. Some FCL compatible wallets choose to act as the payer of the transactions their users @@ -317,11 +317,11 @@ export const TPSSigner = ({ accounts }) => (signable) => { The user of the TPS utility would be responsible for maintaining that the accounts they specify into `TPSAccountResolver` and `TPSSigner` as configuration have a sufficient FLOW balance at all times to pay for transactions they sign for. > To re-iterate, this is just an example implementation of what _might_ work. Your -implementation may vary extensively. +implementation may vary significantly. ### Drawbacks -Since the TPS would be responsible for paying for transactions, it should have suffient +Since the TPS (Transaction Payer Service) would be responsible for paying for transactions, it should have suffient security mechanisms in place to safeguard itself. Since tranaction fees can be dynamic, increasing depending on the amount of work the network From 96b30dad330b7414818d17f7842f2f21abb16cfc Mon Sep 17 00:00:00 2001 From: Jeffrey Doyle Date: Fri, 17 Sep 2021 07:10:50 -0700 Subject: [PATCH 16/16] FLIP: FCL Tx Payer Service updates according to feedback --- flips/20210824-fcl-tx-payer-service.md | 202 ++++++++++++++----------- 1 file changed, 114 insertions(+), 88 deletions(-) diff --git a/flips/20210824-fcl-tx-payer-service.md b/flips/20210824-fcl-tx-payer-service.md index c18d2498d..76dff15ac 100644 --- a/flips/20210824-fcl-tx-payer-service.md +++ b/flips/20210824-fcl-tx-payer-service.md @@ -5,85 +5,98 @@ | **FLIP #** | [612](https://github.com/onflow/flow/pull/612) | | **Author(s)** | Jeffrey Doyle (jeffrey.doyle@dapperlabs.com), | | **Sponsor** | | -| **Updated** | 2020-09-09 | +| **Updated** | 2020-09-17 | ## Objective -There is a need for the developement of a suite of tools that will collectively +There is a need for the development of a suite of tools that will collectively act as a Transaction Fee Payer Service for use by applications built on Flow. -Technically, this suite of tools will facilitate signing as the payer of transactions initiated -by application's users with an account controlled by the application itself. +Technically, this suite of tools will facilitate signing as the payer of +transactions initiated by application's users with an account controlled by the +application itself. -From a functional perspective, the service would enable users of applications that employ -these tools to be able to execute transactions, and thereby interact with Flow, without -encurring the burden of having to pay for their transaction fees themselves. This user experience -is valuable for many reasons, including enabling users to engauge with applications while not -having _any_ FLOW in their account, or having to worry about having _enough_ FLOW to execute the +From a functional perspective, the service would enable users of applications +that employ these tools to be able to execute transactions, and thereby interact +with Flow, without incurring the burden of having to pay for their transaction +fees themselves. This user experience is valuable for many reasons, including +enabling users to engage with applications while not having _any_ FLOW in their +account, or having to worry about having _enough_ FLOW to execute the transactions they wish to execute. -A Transaction Fee Payer Service would exist as a resusable codebase / set of +A Transaction Fee Payer Service would exist as a reusable codebase / set of packages & tools that could be used by the applications that desire it. ## Motivation -One of the many features of Flow is that transaction fees can be paid by an account -separate from the authorizers of that transaction. With this feature, applications that wish to -provide "free" transactions for their users could opt to pay for their users' -transactions themselves. They would do this by producing a signature for the -transaction as the payer of that transaction. Upon receiving this transaction, -the Flow network would then deduct transaction fees from the payer, not any of -the authorizers. - -For a user to pay their transaction fees themselves, that user would have to have FLOW -in their account. This can lead to a situation where a first-time Flow user attempts to engage with an application -that does not employ a Transaction Payer Service, and they would first need to acquire FLOW before engaging with -that application. This is an undesireable user experience, as it significantly raises -the friction required of first time Flow users to engauge with these applications. Many users who are unfamiliar -with blockchain applications will not understand or sympathize with this requirement. Thus it is crucial -that applications who wish to provide a low friction experience for their users to pay transaction fees on -their behalf. - -Some FCL compatible wallets choose to act as the payer of the transactions their users -sign. They do this by swapping out their users account with an account of their own, -if their users are specified as the payer of a transaction. This allows wallets with -this feature to provide "free" transactions for their users. While this makes for a -desirable user experience for those that use wallets with this feature, applications do not -get the guarantee that _all_ of their users will get this user experience. -This is because users are able to use whichever FCL compatible wallet they desire with the FCL -compatible applications they use, and not all FCL compatible wallets will strictly choose to provide this feature. - -Applications that wish to guarantee their users can submit transactions -without having to pay transaction fees for them will then have to pay those fees themselves. -To pay transaction fees, it's expected that applications would need to build a Transaction Payer Service that is able -to receive a transaction signable and then produce a signature for it. - -A re-usable Transaction Payer Service would exist as project / set of packages and tools that applications could use -instead of having to build an implementation of them themselves. These re-usable components -would lower the overall complexity and effort required of a developer who wishes that -their application provide "free" transactions for their users. +One of the many features of Flow is that transaction fees can be paid by an +account separate from the authorizers of that transaction. With this feature, +applications that wish to provide "free" transactions for their users could opt +to pay for their users' transactions themselves. They would do this by producing +a signature for the transaction as the payer of that transaction. Upon receiving +this transaction, the Flow network would then deduct transaction fees from the +payer, not any of the authorizers. + +For a user to pay their transaction fees themselves, that user would have to +have FLOW in their account. This can lead to a situation where a first-time Flow +user attempts to engage with an application that does not employ a Transaction +Payer Service, and they would first need to acquire FLOW before engaging with +that application. This is an undesirable user experience, as it significantly +raises the friction required of first time Flow users to engage with these +applications. Many users who are unfamiliar with blockchain applications will +not understand or sympathize with this requirement. Thus it is crucial that +applications who wish to provide a low friction experience for their users to +pay transaction fees on their behalf. + +Some FCL compatible wallets choose to act as the payer of the transactions their +users sign. They do this by swapping out their users account with an account of +their own, if their users are specified as the payer of a transaction. This +allows wallets with this feature to provide "free" transactions for their users. +While this makes for a desirable user experience for those that use wallets with +this feature, applications do not get the guarantee that _all_ of their users +will get this user experience. This is because users are able to use whichever +FCL compatible wallet they desire with the FCL compatible applications they use, +and not all FCL compatible wallets will strictly choose to provide this feature. + +Applications that wish to guarantee their users can submit transactions without +having to pay transaction fees for them will then have to pay those fees +themselves. To pay transaction fees, it's expected that applications would need +to build a Transaction Payer Service that is able to receive a transaction +signable and then produce a signature for it. + +A re-usable Transaction Payer Service would exist as project / set of packages +and tools that applications could use instead of having to build an +implementation of them themselves. These re-usable components would lower the +overall complexity and effort required of a developer who wishes that their +application provide "free" transactions for their users. ## User Benefit -Most consumer applications that we interact with on a daily basis charge us nothing. -As internet users, we're freely able to interact for our favourite social media apps, -interact with our favourite websites and simply do as we please without paying for anything. -However, with blockchain applications, each one of those interactions would likely be -transaction, and with that, transaction fees would have to be paid. +Most consumer applications that we interact with on a daily basis charge us +nothing. As internet users, we're freely able to interact for our favorite +social media apps, interact with our favorite websites and simply do as we +please without paying for anything. However, with blockchain applications, each +one of those interactions would likely be transaction, and with that, +transaction fees would have to be paid. -Users have been conditioned to expect things for free. As such, applications that wish -to provide the best user experiences will likely choose to allow their users to execute -transactions without them having to pay. +Users have been conditioned to expect things for free. As such, applications +that wish to provide the best user experiences will likely choose to allow their +users to execute transactions without them having to pay. -This Transaction Fee Payer Service will enable more applications to offer a top of the line user experience, where users can easily and seamlessly engage in any action without the friction of having to pay -for any transaction fees. +This Transaction Fee Payer Service will enable more applications to offer a top +of the line user experience, where users can easily and seamlessly engage in any +action without the friction of having to pay for any transaction fees. ## Design Proposal -> Disclaimer: The following is an example implementation for what _might_ work. All possible solutions are not limited to just the following. You're completely free and encouraged to approach the problem however you choose, even if it differs extensively from the thoughts below. +> Disclaimer: The following is an example implementation for what _might_ work. +> All possible solutions are not limited to just the following. You're +> completely free and encouraged to approach the problem however you choose, +> even if it differs extensively from the thoughts below. -When creating a transasction to be sent to the blockchain, FCL expects authorization functions -for each of the roles for that transaction to be specified. +When creating a transaction to be sent to the blockchain, FCL expects +authorization functions for each of the roles for that transaction to be +specified. For example, when specifying to FCL that the "Current User" of the application should be an Authorizer, the Proposer and the Payer of a transaction, it might @@ -104,15 +117,17 @@ const tx = await fcl.send([ The line `fcl.currentUser().authorization` is an "Authorization Function". -(Read More about Authorization Functions: https://github.com/onflow/fcl-js/blob/master/packages/fcl/src/wallet-provider-spec/authorization-function.md) +(Read More about Authorization Functions: +https://github.com/onflow/fcl-js/blob/master/packages/fcl/src/wallet-provider-spec/authorization-function.md) The implementation of this Authorization Function is what FCL uses to gather a signature from the Current User of the application for each of the roles of the transaction (Proposer, Payer and Authorizer). -A Transaction Fee Payer Service would need to make available an Authorization Function -that applications could use in place of `fcl.currentUser().authorization` (or whatever other -authorization function they use). +A Transaction Fee Payer Service would need to make available an Authorization +Function that applications could use in place of +`fcl.currentUser().authorization` (or whatever other authorization function they +use). The applications transaction code might then look something like: @@ -138,8 +153,11 @@ const tx = await fcl.send([ ]).then(fcl.decode) ``` -The implementation of `TPSAuthorizationFunction` will need to conform to how FCL expects -Authorization Functions to work. (Read More about Authorization Functions: https://github.com/onflow/fcl-js/blob/master/packages/fcl/src/wallet-provider-spec/authorization-function.md) +The implementation of `TPSAuthorizationFunction` will need to conform to how FCL +expects Authorization Functions to work. (Read More about Authorization +Functions: +https://github.com/onflow/fcl-js/blob/master/packages/fcl/src/wallet-provider-spec/authorization-function.md) + The implementation of the `TPSAuthorizationFunction` might look something like: @@ -149,7 +167,7 @@ The implementation of the `TPSAuthorizationFunction` might look something like: export const TPSAuthorizationFunction = ({ resolveAccountURL, signingURL }) => async (account) => { /** - Perform a network call to resolve the FCL "account" data structure. This network call will likely be to the client applcation's backend. + Perform a network call to resolve the FCL "account" data structure. This network call will likely be to the client application's backend. The call graph might look something like: @@ -175,7 +193,7 @@ export const TPSAuthorizationFunction = ({ resolveAccountURL, signingURL }) => a ...resolvedAccount, signingFunction: async signable => { /** - Perform a network call to resolve the transaction signature. This network call will likely be to the client applcation's backend. + Perform a network call to resolve the transaction signature. This network call will likely be to the client application's backend. The call graph might look something like: @@ -199,9 +217,9 @@ export const TPSAuthorizationFunction = ({ resolveAccountURL, signingURL }) => a } ``` -The application would need to expose an API/webservice with POST routes for the -`resolveAccountURL` and `signingURL` URLs the `TPSAuthorizationFunction` -would use. The application would need to perform the actions of resolving the account, +The application would need to expose an API/webservice with POST routes for the +`resolveAccountURL` and `signingURL` URLs the `TPSAuthorizationFunction` would +use. The application would need to perform the actions of resolving the account, or signing the signable for those routes respectively. The Client Application's backend might look something like: @@ -248,11 +266,13 @@ app.post("/tps/sign", (req, res) => { }) ``` -The applications backend might consume a package, `"@TPS/TPS-server"`, which would ideally expose some helpful utilities -for handling the logic behind resolving an account and producing a payer signature. The above code snippet uses these -potential utilities. +The applications backend might consume a package, `"@TPS/TPS-server"`, which +would ideally expose some helpful utilities for handling the logic behind +resolving an account and producing a payer signature. The above code snippet +uses these potential utilities. -The implementation of these utilities, `TPSAccountResolver` and `TPSSigner` might look like: +The implementation of these utilities, `TPSAccountResolver` and `TPSSigner` +might look like: ```javascript // IN @TPS/TPS-server @@ -283,7 +303,7 @@ export const TPSSigner = ({ accounts }) => (signable) => { const signablePayerAddress = signable.voucher.payer // Get the account specified in the TPSSigner configuration that corresponds - // to the signablePayerAdress. + // to the signablePayerAddress. const account = accounts.find(a => a.address === signablePayerAddress) // Throw an error if the signablePayerAddress is not found in the TPSSigner configuration @@ -314,31 +334,36 @@ export const TPSSigner = ({ accounts }) => (signable) => { } ``` -The user of the TPS utility would be responsible for maintaining that the accounts they specify into `TPSAccountResolver` and `TPSSigner` as configuration have a sufficient FLOW balance at all times to pay for transactions they sign for. +The user of the TPS utility would be responsible for maintaining that the +accounts they specify into `TPSAccountResolver` and `TPSSigner` as configuration +have a sufficient FLOW balance at all times to pay for transactions they sign +for. -> To re-iterate, this is just an example implementation of what _might_ work. Your -implementation may vary extensively. +> To re-iterate, this is just an example implementation of what _might_ work. +Your implementation may vary extensively. ### Drawbacks -Since the TPS would be responsible for paying for transactions, it should have suffient -security mechanisms in place to safeguard itself. +Since the TPS would be responsible for paying for transactions, it should have +sufficient security mechanisms in place to safeguard itself. -Since tranaction fees can be dynamic, increasing depending on the amount of work the network -would need to perform for each transaction, the payer service should be smart enough to take -this into consideration as needed. +Since transaction fees can be dynamic, increasing depending on the amount of work +the network would need to perform for each transaction, the payer service should +be smart enough to take this into consideration as needed. ### Dependencies -The Transaction Fee Payer Service would depend on FCL. Should there be changes to any aspect -of FCL that may impact the TPS, the service would have to be updated accordingly. +The Transaction Fee Payer Service would depend on FCL. Should there be changes +to any aspect of FCL that may impact the TPS, the service would have to be +updated accordingly. -The Flow team communicates out all breaking changes to the community consistently and with sufficient time to make any updates. +The Flow team communicates out all breaking changes to the community +consistently and with sufficient time to make any updates. ### Engineering Impact -This is a rather large project. While an MVP could be built by an engineering team, a full solution -would likely require multiple participants and skillsets. +This is a rather large project. While an MVP could be built by an engineering +team, a full solution would likely require multiple participants and skillsets. ### Compatibility @@ -346,4 +371,5 @@ This project would need to maintain compatibility with latest versions of FCL. ## Questions and Discussion Topics -- Are the other approches to desiging a TPS that differ from the example provided in this FLIP? +- Are the other approaches to designing a TPS that differ from the example + provided in this FLIP?