Skip to content

feat: meta-pn#1405

Merged
leoslr merged 23 commits intomasterfrom
feat/meta-pn
Jul 1, 2024
Merged

feat: meta-pn#1405
leoslr merged 23 commits intomasterfrom
feat/meta-pn

Conversation

@leoslr
Copy link
Contributor

@leoslr leoslr commented Jun 4, 2024

Description of the changes

Context:

In the Request object, the extensions field is of type Record<PN_ID, PN_STATE>.
As a result, the same request can't contain twice the same payment network. This is a blocker if we want a USD request to be payable in both USDC-matic and USDC-mainnet as we would need twice the pn-any-to-erc20.

Meta payment network

This PR Introduces a new payment network: META.
Its purpose is to store information related to other payment networks.
This way we do not introduce breaking changes either at the request level, or at any of the existing payment network level.

Scope

For now the sub-payment network supported are only:

  • any-to-erc20-proxy
  • any-to-eth-proxy
  • any-to-declarative

Supporting the erc20-fee-proxy pn would require to add a network field to it as it currently relies on the request.currency to determine the chain where the payment should happen.
I didn't want to introduce the associated changes in this PR.
Furthermore, when there are several ways of paying a request, it's most likely denominated in FIAT.

It can always be added later on if needed.

Design

State of the Meta pn:

{
  id: meta-pn,
  events: [...],
  values: {
    [key: string]: sub-pn-state // usual pn state
  } 
}

The key used to index each PN is the salt of the associated PN. This data already exists, is unique and can easily be used to identify a PN within the meta-pn extension.
Each sub-pn state contains the same data as it does when it is used as a standalone pn.

Actions of the Meta pn

This new PN has two actions:

  • create
  • apply-action-to-pn

The create action perform basic validation regarding the meta-pn input data (e.g. uniqueness of each salt). Then it uses existing payment network to create individual sub-pn-state and index them using their associated salt.

The apply-action-to-pn action applies an action to one of the sub-pn. This is required, because when we create a pn, we allow for missing information (e.g. payment address) that can be added at a later time.
If we don't have this action, it's not possible to add this information afterwards and the request will be unusable.
Same as the create action, it reuses all the capacities of existing payment network

Payment detection

To get the balance for the meta-pn extension, we get the balance events of each sub-pn, then aggregate them. Reuse all of the existing logic.

However, reusing everything means that there's a different paymentReference for each sub-pn, as the salt of each sub-pn are different AND the paymentAddress as well.
Imo this is acceptable given the fact that the protocol already planned to have several paymentReference per request (it's not the same whether we proceed to a payment or a refund see here)

Payment processor

To build the transaction for the meta-pn extension, we pass the same properties as we currently do AND the key (salt) of the pn we wish to build the transaction for. It reuses all of the existing logic.

Comment on lines -161 to +167
): AnyToNative | undefined {
):
| ExtensionTypes.IExtension<ExtensionTypes.PnAnyToAnyConversion.ICreationParameters>
| undefined {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated but align with the method above.

Comment on lines +70 to +75
copiedExtensionState[extensionAction.id] = this.applyCreation(extensionAction, timestamp, {
extensionsState,
extensionAction,
requestState,
actionSigner,
});
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Necessary so we can call applyActionToExtension from the meta-pn context to create sub-pn states (see here).

@@ -0,0 +1,243 @@
import { ICurrencyManager } from '@requestnetwork/currency';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

File of interest

@@ -0,0 +1,190 @@
import {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

File of interest

@@ -55,6 +55,7 @@ const approvalSettings = {
const mnemonic = 'candy maple cake sugar pudding cream honey rich smooth crumble sweet treat';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a bit of duplication in the payment-processor tests file, but I didn't want to refactor them in this PR

network: ChainName,
) => Extension.IExtension<Extension.PnAnyToEth.ICreationParameters> | undefined;
network?: ChainName,
) => Extension.IExtension<Extension.PnAnyToAnyConversion.ICreationParameters> | undefined;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated. There was a type mismatch that wasn't raised by typescript before

actionSigner: Identity.IIdentity,
timestamp: number,
) => RequestLogic.IExtensionStates;
createCreationAction: (parameters: TCreationParameters) => IAction<TCreationParameters>;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is public on all PN, but it wasn't exposed in this interface.
Exposing it allows to call it here

@leoslr leoslr marked this pull request as ready for review June 5, 2024 15:40
@leoslr leoslr removed the request for review from kevindavee June 5, 2024 15:41
Copy link
Contributor

@yomarion yomarion left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(partial review, to be continued) Solid work and huge improvement to the lib 🚀

@leoslr leoslr requested a review from yomarion June 11, 2024 10:23
Copy link
Contributor

@alexandre-abrioux alexandre-abrioux left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solid work, well done!

Copy link
Member

@MantisClone MantisClone left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR @leoslr!

Here's a few comments and questions.

I've got to set it aside for today.I will continue my review tomorrow. I've only reviewed the payment-processor, types, and part of the advanced-logic. I haven't looked at payment-detection or any of the tests yet. And i'm still trying to wrap my head around the omission of the erc20-fee-proxy from the scope - especially whether the current design would prevent us from implementing it in the future.

sentRefundAmount: '0',
});
// actions
export const actionCreationFull = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you try to rename the fixtures in clear way so we can understand why adding a bitcoin payment is expected to throw, for instance? Example in this case createAnyToErc20Mainnet.

if (!pnIdentifier) throw new Error('Missing pn identifier');

const extensionOfInterest: ExtensionTypes.IState | undefined = pn.values[pnIdentifier];
if (!extensionOfInterest) throw new Error('Invalid pn identifier');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invalid is wrong, the ID might be valid but missing from the advanced logic state.

Copy link
Contributor Author

@leoslr leoslr Jun 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree, if the pnIdentifier is valid then it is in the advanced logic state.

Given a request, in state X. we should consider that the request is correct and does not miss data.
And the validity on pnIdentifier is based on X.

Otherwise, I can pass any pnIdentifier with any request, and it will never be considered invalid.

},
};

export const validMetaRequest: ClientTypes.IRequestData = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't you re-use the same fixture for encoder-payment.test.ts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The data from both test are not the same.
All the mocks should be shared in a common file, but this is already a big PR and I didn't want to include this mock refactoring as well.
Sharing the data between both file would require to update the associated tests as the expected recipient / currencies as well as

expect(() =>
encodeRequestPayment(validMetaRequest, provider, {
conversion: alphaConversionSettings,
pnIdentifier: 'unknown',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test makes it look like it throws because "unknown" is an invalid pnId, but it would also through with "eth-input" for instance, which is valid but missing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eth-input is not valid the pnIdentifier within the meta-pn is a unique identifier to identify each sub-pn, it's not the payment network identifiers like any-to-erc20 eth-input ...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The payment-detection package provides a getPaymentReference() utility function.
We need a new utility function to return all the sub-paymentReferences.

@MantisClone
Copy link
Member

MantisClone commented Jun 23, 2024

In the Payment Detection section of the description, can you clarify which paymentReference is written on-chain when a "meta" request is paid? is it the meta paymentReference or one of the sub-pn paymentReferences?

Also, will this feature require updates to the payment-subgraph?

@leoslr
Copy link
Contributor Author

leoslr commented Jul 1, 2024

@MantisClone The paymentReference used during a payment is the one associated to the sub-pn that is used to make the payment - I've updated the comment.
The payment subgraphs don't need to be updated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants