Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[AIP-39][Discussion] Separate gas payer #173

Closed
movekevin opened this issue Jun 14, 2023 · 11 comments
Closed

[AIP-39][Discussion] Separate gas payer #173

movekevin opened this issue Jun 14, 2023 · 11 comments

Comments

@movekevin
Copy link
Contributor

movekevin commented Jun 14, 2023

Summary

This AIP proposes a mechanism for specifying a different gas payer account from the transaction sender. The gas for the transaction is deducted from the gas payer account while not giving access to its signer for any other purposes - the transaction would be executed exactly the same regardless of who pays for gas.

Motivation

It’s a common use case where an app can pay for gas on behalf of their users. Currently, transactions on Aptos always deduct gas from the sender from normal single-sender transactions or primary sender for multi-agent transactions. There is no direct support for specifying a different account where gas should be deducted from. Users can currently construct multi-agent transactions and call a custom proxy Move function so that:

  1. The primary sender is the gas payer and gas is deducted from this account.
  2. The secondary sender is the actual signer that would be used in the transaction execution. The proxy call would discard the first signer (from the gas payer) and pass the second signer to the destination function call.

Although this could achieve the desired effect of a separate gas payer, this approach is cumbersome and requires both custom on-chain and off-chain code. Furthermore, this would use the gas payer’s nonce, which creates a bottleneck for scaling gas paying operations where the gas payer account could be paying gas for many transactions from many different users.

Proposal

We can properly support separating the gas payer account from the sender while preserving the same effects as if the sender sends the transaction themselves. This means:

  • The nonce of the sender account should be used, not the gas payer’s. This allows for easier scaling of gas paying operations
  • Transactions with a separate gas payer should use the same payload as a normal transaction (both entry function and script calls) and should not require intermediate proxy code to deal with multiple signers.

The cleanest solution proposed here is to extend the multi-agent transaction semantics to allow appending an extra gas payer address to the list of sender addresses. Gas can be deducted from this address without a signer being constructed when the destination code (entry function call or script) is invoked. In the future we can also extend the Authenticator part of a transaction to add more metadata if we want to further enrich gas paying functionalities.

Extending multi-agent transactions

The main benefit of this approach is minimal code changes required both in the Aptos VM (where gas is charged) and in SDK. This approach is more of an extension to multi-agent transactions than a new transaction type. The developer flow works as below:

  1. To send a transfer of USDC from 0xsender to 0xreceiver where a separate account 0xpayer pays for gas, the app can first construct a multi-agent transaction with the standard payload (entry function 0x1::aptos_account::transfer_coins where the sender is 0xsender). The address 0xpayer needs to be inserted to the list of sender addresses to indicate that 0xpayer is the gas payer. Note that 0x1::aptos_account::transfer_coins takes a single signer, which would be 0xsender.
  2. The app can prompt the user to sign the transaction payload with their account 0xsender
  3. The payload and the signature can then be passed to the server side where 0xpayer will review and sign the transaction.
  4. The transaction is now complete. 0xpayer can send the transaction themselves or passes back to the client side for the user (0xsender) to submit it themselves. Either way, gas will be deducted from 0xpayer and the transaction will be executed in the context of 0xsender, using their account’s nonce and signer.

The implementation is relatively straightforward:

  1. Create new multi_agent prologue and epilogue functions where nonce/gas validation and final gas charge should be applied on the separate gas payer account instead of the sender.
  2. Update aptos_vm to correctly detect a separate gas payer (extra address at the end means there are more senders than the number of signers the invoked function takes) and call the new prologue/epilogue flow instead
  3. Update the SDK to support adding a separate gas payer at the end of the sender address list.
  4. Changes to the coin_model used in the default coin processor in Indexer to correctly attribute the gas burn activity to the gas payer, instead of the sender

Impact on indexing

In either approach, the coin processor/model would need to be updated to correctly attribute gas burn events (currently hardcoded to be the sender). Indexing service providers and ecosystem projects who run their own indexing would need to upgrade their code to get this change.

Reference Implementation

WIP

Risks and Drawbacks

The primary risk is smart contract risk where there can be bugs or vulnerabilities in the VM (gas charging) and Move (transaction prologue/epilogue) changes

Future Potential

Gas payer paves the way for many exciting extensions in the future, such as:

  1. Paying for gas using an object instead of account
  2. More generic account authentication with fully customized control over which account/object can be used for gas, or whether the account’s signer can be used for specific purposes. This is related to the gas change here as this is the first non-trivial modification of the transaction prologue/epilogue flow with rigid single sender mechanism that has existed since the Diem time
@neoul
Copy link

neoul commented Jun 15, 2023

Before this proposal, I had built the following wrapping functions that pays the transaction fee instread of our user. After this is implemented, we do not need this kinds of functions anymore, right?

    public entry fun transfer_p<CoinType>(
        _payer: &signer, from: &signer, to: address, amount: u64
    ) acquires CoinTransferEvents {
        transfer<CoinType>(from, to, amount);
    }

@styner32
Copy link

Awesome work!

I have some questions.

  1. There will be a new error code for the case that payer does not have enough funds to pay the fee?
  2. There will be a new event for paying fee? i.e. 0x1::coin::FeePaidByPayer
  3. Multi agent transactions also can be signed by a payer?

@null-ref-ex
Copy link

This makes a lot of sense. As I was reading through I had the idea that it might be useful to consider supporting multiple gas providers on a single transaction. It's not uncommon for a lot of wallets to end up with dust in them which can't basically be used for anything. It might potentially be (I don't have any quantitative data on this) a way for people or groups of people that have tiny amount of dust (stagnant economic power) to utilize it for something?

@ghost
Copy link

ghost commented Jun 15, 2023

I didn't read the AIP to be deep, Question, do the AIP will affect to Gas Payer of account sequence number? if so, it sounds not useful when you are going to send a bunch of transactions at once time.

@macfly-base
Copy link

This makes a lot of sensé

@thepomeranian thepomeranian added this to the aptos-node-v1.6 milestone Jun 16, 2023
@kslee8224
Copy link

Sounds great!
At last, we don't need to worry about the sequence number of the payer as the the sender will care the nonce!

BTW, there would be typos.

In Extending multi-agent transactions

  1. To send a transfer of USDC from 0xsender to 0xreceiver where a separate account 0xpayer pays for gas, the app can first construct a multi-agent transaction with the standard payload (entry function 0x1::aptos_account::transfer_coins where the sender is 0xsender). The address 0xpayer needs to be inserted to the list of sender addresses to indicate that 0xpayer is the gas payer. Note that 0x1::aptos_account::transfer_coins takes a singer signer -> a single signer, which would be 0xsender.

  2. The transaction is now complete. 0xsender (maybe --> 0xpayer in server side) can send the transaction themselves or passes back to the client side for the user to submit it themselves. Either way, gas will be deducted from 0xpayer and the transaction will be executed in the context of 0xsender, using their account’s nonce and signer.

Thanks!

@kslee8224
Copy link

I have a question.

As I understand, the normal transaction (from one account) has the rate limit in terms of TPS as 100.
Does the payer has the same limit? or, only the sender would have the limitation as previous.

Thanks!

@movekevin
Copy link
Contributor Author

Before this proposal, I had built the following wrapping functions that pays the transaction fee instread of our user. After this is implemented, we do not need this kinds of functions anymore, right?

    public entry fun transfer_p<CoinType>(
        _payer: &signer, from: &signer, to: address, amount: u64
    ) acquires CoinTransferEvents {
        transfer<CoinType>(from, to, amount);
    }

Yep that's correct. You'd be able to call coin::transfer directly with just the signer of the main sender. The signer for the gas payer account will not be instantiated at all

@movekevin
Copy link
Contributor Author

Awesome work!

I have some questions.

  1. There will be a new error code for the case that payer does not have enough funds to pay the fee?
  2. There will be a new event for paying fee? i.e. 0x1::coin::FeePaidByPayer
  3. Multi agent transactions also can be signed by a payer?
  1. No it'd still be the same error codes that you'd see for a normal tx that the sender also pays for gas
  2. No as this would need the gas payer's signer to create the event handle for their account. Gas payments coming from a separate accounts should be easily identifiable as part of indexing.
  3. Yes for accounts with multi-ed25519 auth key as it's still a single signature sender-wise

@movekevin
Copy link
Contributor Author

movekevin commented Jun 20, 2023

As I understand, the normal transaction (from one account) has the rate limit in terms of TPS as 100.
Does the payer has the same limit? or, only the sender would have the limitation as previous.

No the limit will still be enforced on a per-sender basis and not gas payer

@dharam99971
Copy link

oyee

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

8 participants