From d0df5b9139ae1f915621c1d873e5a68bc8807214 Mon Sep 17 00:00:00 2001 From: MantisClone Date: Thu, 19 Dec 2024 18:30:46 -0500 Subject: [PATCH 1/4] feat: add delegate declarative payment example --- README.md | 1 + package.json | 3 +- src/delegateDeclarePaymentSentAndReceived.js | 249 +++++++++++++++++++ 3 files changed, 252 insertions(+), 1 deletion(-) create mode 100644 src/delegateDeclarePaymentSentAndReceived.js diff --git a/README.md b/README.md index 7c8f174..043561d 100644 --- a/README.md +++ b/README.md @@ -40,4 +40,5 @@ npm run retrieve npm run create npm run pay npm run declare +npm run delegate ``` diff --git a/package.json b/package.json index 86b4cf6..0b77323 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "retrieve": "node src/retrieveRequest.js", "create": "node src/createRequest.js", "pay": "node src/payRequest.js", - "declare": "node src/declarePaymentSentAndReceived.js" + "declare": "node src/declarePaymentSentAndReceived.js", + "delegate": "node src/delegateDeclarePaymentSentAndReceived.js" }, "author": "", "license": "ISC", diff --git a/src/delegateDeclarePaymentSentAndReceived.js b/src/delegateDeclarePaymentSentAndReceived.js new file mode 100644 index 0000000..641b50b --- /dev/null +++ b/src/delegateDeclarePaymentSentAndReceived.js @@ -0,0 +1,249 @@ +const waitForConfirmation = async (dataOrPromise) => { + const data = await dataOrPromise; + return new Promise((resolve, reject) => { + data.on("confirmed", resolve); + data.on("error", reject); + }); +}; + +(async () => { + const { + RequestNetwork, + Types, + Utils, + } = require("@requestnetwork/request-client.js"); + const { + EthereumPrivateKeySignatureProvider, + } = require("@requestnetwork/epk-signature"); + const { config } = require("dotenv"); + const { Wallet } = require("ethers"); + + // Load environment variables from .env file + config(); + + const payeeEpkSignatureProvider = new EthereumPrivateKeySignatureProvider({ + method: Types.Signature.METHOD.ECDSA, + privateKey: process.env.PAYEE_PRIVATE_KEY, // Must include 0x prefix + }); + + const payerEpkSignatureProvider = new EthereumPrivateKeySignatureProvider({ + method: Types.Signature.METHOD.ECDSA, + privateKey: process.env.PAYER_PRIVATE_KEY, // Must include 0x prefix + }); + + const payeeDelegateEpkSignatureProvider = + new EthereumPrivateKeySignatureProvider({ + method: Types.Signature.METHOD.ECDSA, + privateKey: process.env.PAYEE_DELEGATE_PRIVATE_KEY, // Must include 0x prefix + }); + + const payerDelegateEpkSignatureProvider = + new EthereumPrivateKeySignatureProvider({ + method: Types.Signature.METHOD.ECDSA, + privateKey: process.env.PAYER_DELEGATE_PRIVATE_KEY, // Must include 0x prefix + }); + + const payeeRequestClient = new RequestNetwork({ + nodeConnectionConfig: { + baseURL: "https://sepolia.gateway.request.network/", + }, + signatureProvider: payeeEpkSignatureProvider, + }); + + const payerRequestClient = new RequestNetwork({ + nodeConnectionConfig: { + baseURL: "https://sepolia.gateway.request.network/", + }, + signatureProvider: payerEpkSignatureProvider, + }); + + const payeeDelegateRequestClient = new RequestNetwork({ + nodeConnectionConfig: { + baseURL: "https://sepolia.gateway.request.network/", + }, + signatureProvider: payeeDelegateEpkSignatureProvider, + }); + + const payerDelegateRequestClient = new RequestNetwork({ + nodeConnectionConfig: { + baseURL: "https://sepolia.gateway.request.network/", + }, + signatureProvider: payerDelegateEpkSignatureProvider, + }); + + const payeeIdentityAddress = new Wallet(process.env.PAYEE_PRIVATE_KEY) + .address; + const payerIdentityAddress = new Wallet(process.env.PAYER_PRIVATE_KEY) + .address; + const payeeDelegateIdentityAddress = new Wallet( + process.env.PAYEE_DELEGATE_PRIVATE_KEY, + ).address; + const payerDelegateIdentityAddress = new Wallet( + process.env.PAYER_DELEGATE_PRIVATE_KEY, + ).address; + + const payeeIdentity = { + type: Types.Identity.TYPE.ETHEREUM_ADDRESS, + value: payeeIdentityAddress, + }; + + const payerIdentity = { + type: Types.Identity.TYPE.ETHEREUM_ADDRESS, + value: payerIdentityAddress, + }; + + const payeeDelegateIdentity = { + type: Types.Identity.TYPE.ETHEREUM_ADDRESS, + value: payeeDelegateIdentityAddress, + }; + + const payerDelegateIdentity = { + type: Types.Identity.TYPE.ETHEREUM_ADDRESS, + value: payerDelegateIdentityAddress, + }; + + // In this example, the payee is also the payment recipient. + const paymentRecipient = payeeIdentityAddress; + const feeRecipient = "0x0000000000000000000000000000000000000000"; + + const requestCreateParameters = { + requestInfo: { + currency: { + type: Types.RequestLogic.CURRENCY.ERC20, + value: "0x370DE27fdb7D1Ff1e1BaA7D11c5820a324Cf623C", // FAU token address + network: "sepolia", + }, + expectedAmount: "1000000000000000000", // 1.0 + payee: payeeIdentity, + payer: payerIdentity, + timestamp: Utils.getCurrentTimestampInSecond(), + }, + paymentNetwork: { + // We can declare payments because ERC20 fee proxy payment network inherits from declarative payment network + id: Types.Extension.PAYMENT_NETWORK_ID.ERC20_FEE_PROXY_CONTRACT, + parameters: { + paymentNetworkName: "sepolia", + paymentAddress: paymentRecipient, + feeAddress: feeRecipient, + feeAmount: "0", + }, + }, + contentData: { + reason: "🍕", + dueDate: "2023.06.16", + builderId: "request-network", + createdWith: "quickstart", + }, + signer: payeeIdentity, + }; + + const payeeRequest = await payeeRequestClient.createRequest( + requestCreateParameters, + ); + const payeeRequestData = await payeeRequest.waitForConfirmation(); + + const payeeRequestDataAfterDelegate = + await payeeRequest.addDeclarativeDelegate( + payeeDelegateIdentity, + payeeIdentity, + ); + console.log( + "payeeRequestDataAfterDelegate: " + + JSON.stringify(payeeRequestDataAfterDelegate, null, 2), + ); + + const payeeRequestDataAfterDelegateConfirmed = await waitForConfirmation( + payeeRequestDataAfterDelegate, + ); + console.log( + "payeeRequestDataAfterDelegateConfirmed: " + + JSON.stringify(payeeRequestDataAfterDelegateConfirmed, null, 2), + ); + + const payerRequest = await payerRequestClient.fromRequestId( + payeeRequestData.requestId, + ); + const payerRequestData = payerRequest.getData(); + + const payerRequestDataAfterDelegate = + await payerRequest.addDeclarativeDelegate( + payerDelegateIdentity, + payerIdentity, + ); + console.log( + "payerRequestDataAfterDelegate: " + + JSON.stringify(payerRequestDataAfterDelegate, null, 2), + ); + + const payerRequestDataAfterDelegateConfirmed = await waitForConfirmation( + payerRequestDataAfterDelegate, + ); + console.log( + "payerRequestDataAfterDelegateConfirmed: " + + JSON.stringify(payerRequestDataAfterDelegateConfirmed, null, 2), + ); + + const payerDelegateRequest = await payerDelegateRequestClient.fromRequestId( + payeeRequestData.requestId, + ); + + const payerDelegateRequestData = payerDelegateRequest.getData(); + + const payerDelegateRequestDataAfterSent = + await payerDelegateRequest.declareSentPayment( + payerDelegateRequestData.expectedAmount, + "payment initiated from the bank", + payerDelegateIdentity, + ); + console.log( + "payerDelegateRequestDataAfterSent: " + + JSON.stringify(payerDelegateRequestDataAfterSent, null, 2), + ); + + const payerDelegateRequestDataAfterSentConfirmed = await waitForConfirmation( + payerDelegateRequestDataAfterSent, + ); + console.log( + "payerDelegateRequestDataAfterSentConfirmed: " + + JSON.stringify(payerDelegateRequestDataAfterSentConfirmed, null, 2), + ); + console.log( + "Observe extensionsData contains 5 events: paymentNetwork 'create', contentData 'create', paymentNetwork 'delegate' x2, and paymentNetwork 'declareSentPayment'", + ); + + const payeeDelegateRequest = await payeeDelegateRequestClient.fromRequestId( + payeeRequestData.requestId, + ); + + const payeeDelegateRequestData = payeeDelegateRequest.getData(); + + const payeeDelegateRequestDataAfterReceived = + await payeeDelegateRequest.declareReceivedPayment( + payeeDelegateRequestData.expectedAmount, + "payment received from the bank", + payeeDelegateIdentity, + ); + + const payeeDelegateRequestDataAfterReceivedConfirmed = + await waitForConfirmation(payeeDelegateRequestDataAfterReceived); + console.log( + "payeeDelegateRequestDataAfterReceivedConfirmed: " + + JSON.stringify(payeeDelegateRequestDataAfterReceivedConfirmed, null, 2), + ); + console.log( + "Observe extensionsData contains 6 events: paymentNetwork 'create', contentData 'create', paymentNetwork 'delegate' x2, paymentNetwork 'declareSentPayment', and paymentNetwork 'declareReceivedPayment'", + ); + + console.log( + "Request balance: " + + payeeDelegateRequestDataAfterReceivedConfirmed.balance.balance, + ); + console.log( + "Request balance events: " + + JSON.stringify( + payeeDelegateRequestDataAfterReceivedConfirmed.balance.events, + null, + 2, + ), + ); +})(); From 11968140920fa3b035dc790ded07753fe16db315 Mon Sep 17 00:00:00 2001 From: MantisClone Date: Fri, 20 Dec 2024 00:14:22 -0500 Subject: [PATCH 2/4] Add PAYER_DELEGATE and PAYEE_DELEGATE PRIVATE_KEY to the .env.example --- .env.example | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.env.example b/.env.example index 15498cc..dea342e 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,8 @@ # Must include 0x prefix PAYEE_PRIVATE_KEY='0x4025da5692759add08f98f4b056c41c71916a671cedc7584a80d73adc7fb43c0' PAYER_PRIVATE_KEY='0x4025da5692759add08f98f4b056c41c71916a671cedc7584a80d73adc7fb43c0' +PAYEE_DELEGATE_PRIVATE_KEY='0x4025da5692759add08f98f4b056c41c71916a671cedc7584a80d73adc7fb43c0' +PAYER_DELEGATE_PRIVATE_KEY='0x4025da5692759add08f98f4b056c41c71916a671cedc7584a80d73adc7fb43c0' # Infura, Alchemy, etc. JSON_RPC_PROVIDER_URL='https://eth-sepolia.g.alchemy.com/v2/demo' \ No newline at end of file From 993ea99fe930eed2926366973e6870986f187319 Mon Sep 17 00:00:00 2001 From: MantisClone Date: Fri, 20 Dec 2024 00:15:17 -0500 Subject: [PATCH 3/4] Clean up --- src/delegateDeclarePaymentSentAndReceived.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/delegateDeclarePaymentSentAndReceived.js b/src/delegateDeclarePaymentSentAndReceived.js index 641b50b..4d9cfbb 100644 --- a/src/delegateDeclarePaymentSentAndReceived.js +++ b/src/delegateDeclarePaymentSentAndReceived.js @@ -159,11 +159,13 @@ const waitForConfirmation = async (dataOrPromise) => { "payeeRequestDataAfterDelegateConfirmed: " + JSON.stringify(payeeRequestDataAfterDelegateConfirmed, null, 2), ); + console.log( + "Observe that extensions.pn-erc20-fee-proxy-contract.values.payeeDelegate is set to the payee delegate identity", + ); const payerRequest = await payerRequestClient.fromRequestId( payeeRequestData.requestId, ); - const payerRequestData = payerRequest.getData(); const payerRequestDataAfterDelegate = await payerRequest.addDeclarativeDelegate( @@ -182,6 +184,9 @@ const waitForConfirmation = async (dataOrPromise) => { "payerRequestDataAfterDelegateConfirmed: " + JSON.stringify(payerRequestDataAfterDelegateConfirmed, null, 2), ); + console.log( + "Observe that extensions.pn-erc20-fee-proxy-contract.values.payerDelegate is set to the payer delegate identity", + ); const payerDelegateRequest = await payerDelegateRequestClient.fromRequestId( payeeRequestData.requestId, @@ -208,7 +213,7 @@ const waitForConfirmation = async (dataOrPromise) => { JSON.stringify(payerDelegateRequestDataAfterSentConfirmed, null, 2), ); console.log( - "Observe extensionsData contains 5 events: paymentNetwork 'create', contentData 'create', paymentNetwork 'delegate' x2, and paymentNetwork 'declareSentPayment'", + "Observe extensionsData contains 5 events: paymentNetwork 'create', contentData 'create', paymentNetwork 'addDelegate' x2, and paymentNetwork 'declareSentPayment'", ); const payeeDelegateRequest = await payeeDelegateRequestClient.fromRequestId( @@ -231,13 +236,16 @@ const waitForConfirmation = async (dataOrPromise) => { JSON.stringify(payeeDelegateRequestDataAfterReceivedConfirmed, null, 2), ); console.log( - "Observe extensionsData contains 6 events: paymentNetwork 'create', contentData 'create', paymentNetwork 'delegate' x2, paymentNetwork 'declareSentPayment', and paymentNetwork 'declareReceivedPayment'", + "Observe extensionsData contains 6 events: paymentNetwork 'create', contentData 'create', paymentNetwork 'addDelegate' x2, paymentNetwork 'declareSentPayment', and paymentNetwork 'declareReceivedPayment'", ); console.log( "Request balance: " + payeeDelegateRequestDataAfterReceivedConfirmed.balance.balance, ); + console.log( + `Observe that the balance is ${requestCreateParameters.requestInfo.expectedAmount}`, + ); console.log( "Request balance events: " + JSON.stringify( @@ -246,4 +254,7 @@ const waitForConfirmation = async (dataOrPromise) => { 2, ), ); + console.log( + `Observe that the balance event note is "payment received from the bank"`, + ); })(); From 5233f928ac017f87fb63c5761329abb04b616f39 Mon Sep 17 00:00:00 2001 From: MantisClone Date: Fri, 20 Dec 2024 00:18:07 -0500 Subject: [PATCH 4/4] Add warnings about Private Keys --- .env.example | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.env.example b/.env.example index dea342e..fdd30fb 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,6 @@ # Must include 0x prefix +# WARNING: These are example keys. DO NOT use them to store real funds! +# Generate unique private keys for each role using a secure method PAYEE_PRIVATE_KEY='0x4025da5692759add08f98f4b056c41c71916a671cedc7584a80d73adc7fb43c0' PAYER_PRIVATE_KEY='0x4025da5692759add08f98f4b056c41c71916a671cedc7584a80d73adc7fb43c0' PAYEE_DELEGATE_PRIVATE_KEY='0x4025da5692759add08f98f4b056c41c71916a671cedc7584a80d73adc7fb43c0'